2016年1月26日 星期二

用Raspberry 來玩 blue tooth programming PyBluez



先安裝好blue-tooth 的套件

sudo apt-get update

sudo apt-get install bluetooth bluez-utils blueman bluez python-gobject python-gobject-2

sudo apt-get install python-bluez


sudo hciconfig hci0 piscan [make your device discover-able]
sudo hciconfig hci0 name 'Device Name' 
接下來這個動作很重要....因為bluetooth有bug... 需要用一個workaround的方式
 Disable bluetooth pnat support as there seems to be a bug which stops proper operation with pnat enabled. Full details can be found here: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=6907492A workaround is to add the following to /etc/bluetooth/main.conf:
   DisablePlugins = pnat
    如果上面的事情沒有做...就會產生下面的error message


(111, 'Connection refused')

接著開始啟動藍牙:

確認作業系統是否有抓到藍牙裝置。
lsusb




確認藍牙裝置接收器已被啟動。
hciconfig


可以看得出來目前的藍芽裝置只有一個, 就是 hci0....

dev_addr 為 00:1A:7D:DA:71:04


一些小工具....hcitool scan...可以掃描附近的藍芽裝置




接下來就是重頭戲...開始來寫programming...


請參考ref 1.. Chapter 3

It is written for the Windows XP (Microsoft Bluetooth stack) and GNU/Linux (BlueZ stack)

Bluetooth programming with Python - PyBluez

chapter 3.1

 findmyphone.py

import bluetooth

target_name = "chifc iphone"
target_address = None

nearby_devices = bluetooth.discover_devices()

for bdaddr in nearby_devices:
    print bluetooth.lookup_name( bdaddr ) # for debug used
    if target_name == bluetooth.lookup_name( bdaddr ):
        target_address = bdaddr
        break

if target_address is not None:
    print "found target bluetooth device with address ", target_address
else:
    print "could not find target bluetooth device nearby"

從上面的例子來看....他使用了 bluetooth 的library...

呼叫  bluetooth.discover_devices() 來找到最近的  devices....

接著用一個for loop.....來找名字是 "My Phone" 的target...

最後判斷  target_address是不是NULL....如果不是...代表找到了


我用我的手機打開藍芽... 手機名稱為 chifc iphone.....掃到第二個裝置就找到了 ....

同時印出target address

至於iphone怎麼修改名稱   .... 設定>一般>關於本機>名稱





chapter 3.2 Communicating with RFCOMM

Example 3-2. rfcomm-server.py
import bluetooth

server_sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )

port = 1
server_sock.bind(("",port))
server_sock.listen(1)

client_sock,address = server_sock.accept()
print "Accepted connection from ",address

data = client_sock.recv(1024)
print "received [%s]" % data

client_sock.close()
server_sock.close()




Example 3-3. rfcomm-client.py
import bluetooth

bd_addr = "01:23:45:67:89:AB"  # here need to modify. use hciconfig to get address

port = 1

sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )
sock.connect((bd_addr, port))

sock.send("hello!!")

sock.close()

這邊有兩個 code...一個是server...一個是client..

在server 端呼叫 server_sock.accept()

來得到client_socket 和 address

然後再由client_socket來得到傳送的data   

data = client_sock.recv(1024)

在client端就是寫一個傳送的test code...

首先先指定對方的address ..."01:23:45:67:89:AB"

然後呼叫下面的function 來建立連線
sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )
sock.connect((bd_addr, port))

接下來就是傳送資料
sock.send("hello!!")



3.3. Communicating with L2CAP

Example 3-4. l2cap-server.py
import bluetooth

server_sock=bluetooth.BluetoothSocket( bluetooth.L2CAP )

port = 0x1001
server_sock.bind(("",port))
server_sock.listen(1)

client_sock,address = server_sock.accept()
print "Accepted connection from ",address

data = client_sock.recv(1024)
print "received [%s]" % data

client_sock.close()
server_sock.close()

Example 3-5. l2cap-client.py
import bluetooth

sock=bluetooth.BluetoothSocket(bluetooth.L2CAP)

bd_addr = "01:23:45:67:89:AB"
port = 0x1001

sock.connect((bd_addr, port))

sock.send("hello!!")

sock.close()
===============================================================

sample code 和 chapter 3.2差不多...只差在指定傳輸的方式



3.4. Service Discovery Protocol

Dynamically allocating port numbers and using the Service Discovery Protocol (SDP) to search for and advertise services is a simple process in PyBluez

動態地配置port number 和 使用 service Dsicovery Protocol 去尋找和配置服務在PyBluez 是一個很簡單的過程

luetooth.get_available_port( protocol )

 The get_available_port method finds available L2CAP and RFCOMM ports

bluetooth.advertise_service( sock, name, uuid )
bluetooth.stop_advertising( sock )
bluetooth.find_service( name = None, uuid = None, bdaddr = None )

advertise_service advertises a service with the local SDP server, and find_service searches Bluetooth devices for a specific service.

The UUID must always be a string of the form ``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" or ``xxxxxxxx" or ``xxxx", where each 'x' is a hexadecimal digit

find_service looks for a service with name and UUID that match name and uuid, at least one of which must be specified

 In the special case that ``localhost" is used for bdaddr


直接看範例吧...

Example 3-6. rfcomm-server-sdp.py
import bluetooth

server_sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )

port = bluetooth.get_available_port( bluetooth.RFCOMM )
server_sock.bind(("",port))
server_sock.listen(1)
print "listening on port %d" % port

uuid = "1e0ca4ea-299d-4335-93eb-27fcfe7fa848"
bluetooth.advertise_service( server_sock, "FooBar Service", uuid )

client_sock,address = server_sock.accept()
print "Accepted connection from ",address

data = client_sock.recv(1024)
print "received [%s]" % data

client_sock.close()
server_sock.close()

Example 3-7. rfcomm-client-sdp.py
import sys
import bluetooth

uuid = "1e0ca4ea-299d-4335-93eb-27fcfe7fa848"
service_matches = bluetooth.find_service( uuid = uuid )

if len(service_matches) == 0:
    print "couldn't find the FooBar service"
    sys.exit(0)

first_match = service_matches[0]
port = first_match["port"]
name = first_match["name"]
host = first_match["host"]

print "connecting to \"%s\" on %s" % (name, host)

sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )
sock.connect((host, port))
sock.send("hello!!")
sock.close()
==========================================================
在執行這個範例... 需要先找到你的UUID...
從Ref 5可以得知
vi get_uuid.py
get_uuid.py 的內容如下:
#!/usr/bin/python
import sys,uuid
print uuid.uuid4().hex
執行後的結果如下
所以要把內文中的uuid 改成你執行後所得到的值.....
在執行/rfcomm-server-sdp.py 會遇到下面的問題
查了一下 google...從ref 6 可以得知...
原來 
get_available_port(protocol)
deprecated. bind to port zero instead.
get_available_port 函式已經被放棄了
請改用 port 0來代替...
所以修改你的code
 #port = bluetooth.get_available_port( bluetooth.RFCOMM )
port = 0
在client 端使用  find_service...來尋找 service...他指定了uuid ..
藉由找到的service_match[0]來得到 port 和 name 和 host...
然後再用 
sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )
sock.connect((host, port))

來產生連線....執行結果如下:






3.5. Advanced usage

 They don't return until the requests are complete, which can often taken a long time . During this time, the controlling thread blocks and can't do anything else....
PyBluez provides the DeviceDiscoverer class for asynchronous device discovery and name lookup.
Example 3-8. asynchronous-inquiry.py
import bluetooth
import select

class MyDiscoverer(bluetooth.DeviceDiscoverer):
    
    def pre_inquiry(self):
        self.done = False
    
    def device_discovered(self, address, device_class, name):
        print "%s - %s" % (address, name)

    def inquiry_complete(self):
        self.done = True

d = MyDiscoverer()
d.find_devices(lookup_names = True)

readfiles = [ d, ]

while True:
    rfds = select.select( readfiles, [], [] )[0]

    if d in rfds:
        d.process_event()

    if d.done: break

To asynchronously detect nearby bluetooth devices, create a subclass of DeviceDiscoverer and override the pre_inquirydevice_discovered, and inquiry_complete methods.


Reference  1: https://people.csail.mit.edu/albert/bluez-intro/

Reference  2 : http://cheng-min-i-taiwan.blogspot.tw/2015/03/raspberry-pi-40ibeacon.html

Reference  3 : https://github.com/karulis/pybluez

Reference  4 : http://forum.erlerobotics.com/t/working-with-bluetooth/453

Reference  5 : http://www.wadewegner.com/2014/05/create-an-ibeacon-transmitter-with-the-raspberry-pi/
Reference 6 : https://www.raspberrypi.org/forums/viewtopic.php?f=32&t=113987

2 則留言:

  1. 你好,請問你有做過藍芽一對多的連線嗎?用ACL形成一個微網

    回覆刪除
  2. Hi Sir and Madam
    我的作業系統是Raspbian 我想連 PS3 搖桿
    可以指導方向嗎?
    Kenny

    回覆刪除