これまでのあらすじ
以前こちらの私の記事で ESP32-DevKitC と AE-TYBLE16 (with マイコン)の通信をしたが、最近導入したラズパイ3B でもしてみむとてするなり。python で。
おことわり
TAIYO YUDEN Original Service UUID、TAIYO YUDEN Characteristic UUID については、公開されている brief data report には記載がなく、購入者向けの data report には載っているので、このサイトの記事では記載しないこととします。Source Code of "Terminal Application for Android" の Terminal.java に書いてありますので、そちらをご参照下さい。
(公開されているサンプルコードに書いてあるとはいえ今後公開停止されることもあろうかと言う事で)
また、これらUUIDに対応する handle の値も固定であるように思われますが、UUIDを知らないと調べられない情報なので、そのものズバリの数値は使わず仮の数字で書いています。
解りにくくてすみませんが、権利侵害とかしたくないので。
2019/3/19 更新: CCCD(Client Characteristic Configuration Descriptor) は 「Bluetooth SIGという団体にて標準で定義されているuuid」なので伏せるのをやめました。
AE-TYBLE16側
こちらの私の記事で書いたような「AE-TYBLE16 + マイコン(Arduino Nanoや互換回路)」という構成です。ピンアサインとかその辺は前の記事の通りです。
Raspberyy Pi3 への pybluez の入れ方(なお使わなかった模様)
ラズパイ3B にbluez は既に入ってたが、入ってなければ
$ sudo apt-get install bluez
んで、参考記事1を参考に
$ sudo apt-get install python-dev libbluetooth3-dev libglib2.0 libboost-python-dev libboost-thread-dev
$ sudo pip install pybluez
$ sudo pip install gattlib
これだけでコマンド叩くとエラーが出たのでまた調べて参考記事2 を見て、
$ sudo systemctl daemon-reload
$ sudo service bluetooth restart
とかして確認コマンド下記で対象デバイスが見つかればOK。
$ sudo hcitool lescan
やっぱ pybluez やめた、bluepy にする!
サンプルプログラムで丁度いいのが、参考記事3で、bluepy って奴を使ってるので、そっちにした!(笑)
$ sudo pip install bluepy
pybluez の準備で入れた奴のうちどれが必要なのかわからないので、こんな酷い記事で本当にごめんなさい。
ハンドルって何だ?調べないと!
前述参考記事3のとおりにプログラムを…… writeCharacteristic のパラメータの handle ってなんじゃらほい?UUIDとかそういう奴はおらんの???
(2019/3/19 追記: もうちょい詳しく記事にしました。 https://qiita.com/ajtajta_j/items/ab0b09edf45976af0094 。
また、本記事も曖昧だった部分をupdateして、ちゃんと特定できるような書き方にしました。)
調べ方は参考記事4を参考に、gatttoolでとりあえずInteractiveモード起動して connect して char-descで handle - UUID 対応リストを出す。(下記は仮の数字で実際の出力とは違います)
pi@raspberrypi:~/TYBLE $ gatttool -b XX:XX:XX:XX:XX:XX -t random -I
[XX:XX:XX:XX:XX:XX][LE]> connect
Attempting to connect to XX:XX:XX:XX:XX:XX
Connection successful
[XX:XX:XX:XX:XX:XX][LE]> char-desc
handle: 0x0001, uuid: <UUID1>
handle: 0x0002, uuid: <UUID2>
handle: 0x0003, uuid: <UUID3>
handle: 0x0004, uuid: <UUID4>
handle: 0x0005, uuid: <UUID5>
handle: 0x0006, uuid: <UUID6>
handle: 0x0007, uuid: <RX_CHARACTERISTIC_UUID>
handle: 0x0008, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0009, uuid: <UUID7>
handle: 0x000a, uuid: <TX_CHARACTERISTIC_UUID>
handle: 0x000b, uuid: <UUID8>
handle: 0x000c, uuid: <UUID9>
handle: 0x000d, uuid: <UUID10>
handle: 0x000e, uuid: <UUID11>
handle: 0x000f, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0010, uuid: <UUID12>
handle: 0x0011, uuid: <UUID13>
handle: 0x0012, uuid: <UUID14>
handle: 0x0013, uuid: 00002902-0000-1000-8000-00805f9b34fb
[XX:XX:XX:XX:XX:XX][LE]> exit
(gatttool:18268): GLib-WARNING **: Invalid file descriptor.
pi@raspberrypi:~/TYBLE $
よく見ると太陽誘電サービスの UUID に対応するハンドルがわかる。
以前の私の記事 にも書きました(いや書いてないけど)が、UUID はサンプルコード (Android Central用なら Terminal.java )に書いてある4つ
private static final UUID CCCD = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
public static final UUID TY_SERVICE_UUID = UUID.fromString("太陽誘電のサイトのサンプルファイルでご確認ください"); // Taiyo Yuden Service UUID
public static final UUID RX_CHARACTERISTIC_UUID = UUID.fromString("太陽誘電のサイトのサンプルファイルでご確認ください"); // Taiyo Yuden Rx Characteristic
public static final UUID TX_CHARACTERISTIC_UUID = UUID.fromString("太陽誘電のサイトのサンプルファイルでご確認ください"); // Taiyo Yuden Tx Characteristic
これら4つのうち、UUID CCCD ってのが、ペリフェラルからの notify を enable するための descriptor で、char-desc の結果には同じのが3つあったので仮にhandleを 0x0008, 0x000f, 0x0013 とします(実際は違うので各自確認してください)
RX_CHARACTERISTIC_UUID ってのがペリフェラルからの read のための奴で、これがhandle 0x0007 、TX_CHARACTERISTIC_UUID ってのがペリフェラルへの write のための奴で、0x000a とします(これらも実際は違うので以下同文)
CCCD が3つありますが、RX_CHARACTERISTIC_UUID の後にある一番近い奴が RX_CHARACTERISTIC の通知を有効にするための CCCD です。上記例だと0x0008 です。Write のほうはそのまま 0x000a ですので、
> char-write-req 0x0008 0100
> char-write req 0x000a test_string
ってやって送信先で受信できたり、相手方からのnotify が届けば「ハンドルこれだった!」ってなります。(上記例は実際と違う数値にしてあるので、しつこいけど自分で確認してください。)
やっとプログラム.py
# -*- coding: utf-8 -*-
from bluepy.btle import Peripheral
import bluepy.btle as btle
import binascii
import sys
devaddr = "xx:xx:xx:xx:xx:xx" # BLE ペリフェラルのアドレス
class MyDelegate(btle.DefaultDelegate):
def __init__(self, params):
btle.DefaultDelegate.__init__(self)
def handleNotification(self, cHandle, data): # notify callback
sys.stdout.write( data ) #受信文字列を表示するだけ
class MyPeripheral(Peripheral):
def __init__(self, addr):
Peripheral.__init__(self, addr, addrType="random")
def main():
# 初期設定
perip = MyPeripheral(devaddr)
perip.setDelegate(MyDelegate(btle.DefaultDelegate))
# notifyを有効にする
perip.writeCharacteristic(0x0008, "\x01\x00", True) # 0x0008 は自分で調べたCCCD ハンドルで
#文字列の送信例
perip.writeCharacteristic(0x000a, "2001/01/01 00:00:01\r\n") # 0x000a は自分で調べたハンドルで
# データを受信し続ける
while True:
perip.waitForNotifications(1.0)
if __name__ == "__main__":
main()
めっちゃシンプルやん……UUID4つのうち2つの handle 調べて書いとけばUUIDだなんだやらなくて済むやん……
これ、自作時計の時刻設定用プログラムの素なので、日付と時刻を通知しています。ペリフェラルは受領したら返事よこすようになっています。2つのAE-TYBLE16 で試しましたが、devaddr の Bluetooth address のみ変えて handle その他全部一緒で使えています。
実用プログラムとしては datetime.now() などで現在時刻取ったり、時刻設定以外の命令飛ばしたり、パラメータで2つのアドレス切り替えたりもしてますがこの記事には関係無いので割愛。キモの部分はすぐ忘れそうな自分のために qiita に投稿して完了。