前回、Raspberry PiとWindows PCの間で、ターミナルのコマンドを使ってBluetooth シリアル通信を行う方法についてまとめました。↓
今回はRaspberry Pi同士でのluetoothシリアル通信についてまとめようかと思います。前回のようなコマンドではなく、一応pythonで通信させる仕組みになっています。
下記のサイトを参考にさせていただきました。↓
上記サイトでは、ペアリング後にpythonプログラムを走らせて任意の文字列をサーバー側からクライアント側に送り続けるといった内容だったので、そこを少し下記のように変更致しました。
ブラウザ→Raspberry pi(html/js/python CGI)→bluetooth→Raspberry Pi(GPIOでLチカ)
Raspberry PiにWEBサーバーを立てて、そこにhtmlで作った簡単なボタンを設置し、ブラウザからそのボタンを押せばもう片方のRaspberry Piに任意の文字列がbluetoothシリアル通信で転送され、文字列に応じてGPIOピンの制御を行いLチカさせるといった流れです。
ブラウザで操作するのがとても無駄に感じますが、とりあえずやってみたかったのでやってみました。
目次
初めに
作業環境
Raspberry Piの操作にはVNCのリモートデスクトップを使います。SSHだと何故かペアリング時にエラーが発生しうまくいきませんでした。SSHもしくはVNCを使います。
PCからRaspberry Pi2つに接続し、ターミナルを開いておきます。
必要なライブラリのインストール
Raspberry Piにbluezがインストールされていなければ、bluezをインストールします。bluetooth 本体であるpybluezは必須。
1 2 3 |
sudo apt-get update sudo apt-get install bluetooth bluez blueman sudo pip install pybluez |
サーバー側のRaspberry Piのシリアルポート設定
今回Raspberry Pi ZERO W を2つ使うので(以後デバイスA,B)、片方をBluetooth接続時のサーバーとします。
デバイスAをサーバーにするとすれば、デバイスAの方のSDPサーバーの仮想シリアルポートの設定が必要になります。設定は以下の通り。
1 |
sudo sdptool add --channel=1 SP |
チャンネルは1とします。これでサーバー側にrfcomm0のチャンネル1のシリアルポートが設定されます。
ペアリング
どちらか片方をアドバタイズに(ペリフェラル側)
デバイスA、Bの両方で下記コマンドを入力し、Bluetooth設定モードに入ります。
1 |
bluetoothctl |
デバイスA、Bどちらでも構わないので、片方で下記コマンドを入力し、ペアリングの接続待ち(アドバタイズ)にさせます。この時、接続待ちしている側のMACアドレスが表示されます。
1 |
discoverable on |
もう片方のデバイスからペアリング要求(セントラル側)
もう片方のデバイスから以下のコマンドでスキャンをかけます。スキャン後、周囲のbluetoothデバイスがぱらぱらと表示されていきますので、その中に接続待ちのアドレスが出てくれば、スキャンをOFFして接続のコマンドを入力します。
1 2 3 |
scan on //スキャン開始 scan off //スキャン終了 pair xx:xx:xx:xx:xx:xx //ペリフェラルのMACアドレス |
Pairing successfulと表示されればOKです。なお、ペリフェラル側で「ペアリング許可しますか」の表示がでるので、OKを選択します。
これでペアリングまでは終了です。
シリアル接続
サーバー側プログラム
server.py
シリアル接続からはpythonプログラムで制御します。デバイスA、Bのどちらでも構わないので、サーバー側のプログラムを書き込みます。server.pyとします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# -*- coding: utf-8 -*- import time import bluetooth import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) MOTOR_A1 = 13 MOTOR_A2 = 19 MOTOR_A3 = 26 GPIO.setup(MOTOR_A1, GPIO.OUT) GPIO.setup(MOTOR_A2, GPIO.OUT) GPIO.setup(MOTOR_A3, GPIO.OUT) while(True): PORT = 1 server_socket = bluetooth.BluetoothSocket(bluetooth.RFCOMM) print("connect...") server_socket.bind(("",PORT )) server_socket.listen(1) client_socket,address = server_socket.accept() print("connection success!!") while 1: try: data = client_socket.recv(1024) data = data.decode() print(data) print('\n') #except KeyboardInterrupt: #client_socket.close() #server_socket.close() #break except: print("error") print('\n') client_socket.close() server_socket.close() break if data == "hhhhh": GPIO.output(MOTOR_A1, GPIO.HIGH) elif data == "kkkk": GPIO.output(MOTOR_A2, GPIO.HIGH) elif data == "jjjj": GPIO.output(MOTOR_A3, GPIO.HIGH) elif data == "ssss": GPIO.output(MOTOR_A1, GPIO.LOW) GPIO.output(MOTOR_A2, GPIO.LOW) GPIO.output(MOTOR_A3, GPIO.LOW) |
python3 server.py でプログラムを開始させておきます。
rfcomm0 のポート1で接続を待ち受けます。recieveした文字列によって、GPIOの出力を制御しています。
接続が切れても、再度 rfcomm0 のポート1で接続を待ち続けます。
クライアント側プログラム
もう片方のデバイスに、クライアント側のプログラムを書き込みます。
なお、今回はブラウザからbluetoothを操作しているので、過去に作成したラジコンコントローラーのpython CGIスクリプトのプログラムの構成(html/js/python cgi)を応用して、python cgiの中にbluetooth接続の記述を埋め込みました。こちらの過去記事の応用になります。↓
プログラムはrecieve.pyと、通信部をまとめたRN42.pyになります。
recieve.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#!/usr/bin/python3 # -*- coding: utf-8 -*- import cgi import RPi.GPIO as GPIO import time from RN42 import RN42 import sys form = cgi.FieldStorage() recieve = form.getvalue('name') ras = RN42("ras", "B8:27:EB:84:F0:1E", 1) ras.connectBluetooth(ras.bdAddr,ras.port) print("Entering main loop now") if recieve == 'hhhhh': ras.sock.send("hhhhh") print("data send") ras.disconnect(ras.sock) print('Content-type: text/html\n') print(recieve) elif recieve == "kkkk": ras.sock.send("kkkk") print("data send") ras.disconnect(ras.sock) print('Content-type: text/html\n') print(recieve) elif recieve == "jjjj": ras.sock.send("jjjj") print("data send") ras.disconnect(ras.sock) print('Content-type: text/html\n') print(recieve) elif recieve == "ssss": ras.sock.send("ssss") print("data send") ras.disconnect(ras.sock) print('Content-type: text/html\n') print(recieve) |
RN42の変数に、サーバーのMACアドレスと待ち受けポートを指定しています。
jsからpostされた文字列をそのままsendでサーバー側へ転送しています。
その後、cgiファイルの設計上やむなく接続を切断しています。
RN42.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# -*- coding: utf-8 -*- # RN42.py from time import sleep import sys import bluetooth class RN42: def __init__(self, name, addr, num): #""" Arduino with RN42""" self.__name = name #Set Name self.__bdAddr = addr #the address form the Arduino RN42 self.__port = num #Connect Port (RaspberryPi to Arduino) self.__sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM) #config @property def name(self): pass @name.getter def name(self): return self.__name @name.setter def name(self,name): self.__name = name @property def bdAddr(self): pass @bdAddr.getter def bdAddr(self): return self.__bdAddr @bdAddr.setter def bdAddr(self,value): self.__bdAddr = value return @property def port(self): pass @port.getter def port(self): return self.__port @port.setter def port(self,value): self.__port = value return @property def sock(self): pass @sock.getter def sock(self): return self.__sock @sock.setter def sock(self, value): self.__sock = value def reConnect(self, addr, num): self.__bdAddr = addr #the address form the Arduino RN42 self.__port = num #Connect Port (RaspberryPi to Arduino) self.__sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM) #config def connectBluetooth(self, bdAddr, port): while(1): try: print(self.__name + " : Connect......") self.__sock.connect((bdAddr,port)) sleep(2) print(self.__name + " : Successful Connected!!!") break except bluetooth.BluetoothError: print(self.name + " : connecting failed") print("try connecting") self.reConnect(bdAddr, port) sleep(0.5) except KeyboardInterrupt: break def disConnect(self,sock): sock.close() |
動作確認
CGIスクリプトを呼び出す度に接続、切断を繰り返しているので遅延がえげつないですが、冒頭の流れの通りに動いています。↓