Help us understand the problem. What is going on with this article?

Pythonライブラリ bleakでWindows10/macOS/Linux上でtoioコア キューブを動かしてみる

身近にあるパソコンでtoioコア キューブを動かしてみたい

 toioコア キューブはBLE通信で制御できる「とてもかわいい」二輪ロボットです。
https://toio.github.io/toio-spec/

 先日、スマホを使ってBLE通信を直につかってtoioコアキューブを動かしてみる話を書いてみましたが今回はパソコンを使ってtoioコアキューブを動かしてみます。

 オフィシャルにはjavascriptおよびビジュアルプログラミングの環境が用意されています。

 が、この記事ではpythonを使います。
 ビジュアルプログラミング環境は~~ (2019年12月10日時点で)macOS用しかリリースされていませんし、~~ Windows10 64bit あるいは macOS 10.13以降が必要です。 JavaScriptライブラリもnode.js、nobleのインストールが必要です。この記事の趣旨としては、身近に使えるパソコンやRaspberry Piなどをつかってサクッとtoioコアキューブを動かしたい、というものなので、インストールが比較的簡単なpythonを使う方法を紹介してみます。

 なお、bleakのサンプルコードがasyncioを使っているので、対応するPythonのバージョンはPython3.5以降になります。

bleakって?

 Windows10/Linux/macOSで動くBLE通信Python用ライブラリです。

 BLE通信用pythonライブラリはいろいろなものがありますが、Windows10、Linux(raspbian、ubuntu)、macOSの3種のOSで(ほぼ)同じように動かせるものとしてbleakを選んでみました。

その他のBLE通信pythonライブラリ

BLE直でというのはちょっとなーという場合は偉大な先人たちが作成されたtoio コアキューブ用ライブラリを使うのが良いでしょう。動作するOSなどはそれぞれの動作要件を確認してください。

bleakのしくみ

 bleakは各OSのBLE通信APIをwrapする形で実装されています。

  • Windows10だと共通言語ランタイム(CLR)を使ってdllを呼び出しその中でUWP APIを呼び出して呼び出しています。
  • Linuxだとbluez スタックを呼び出しています。
  • macOSだとpyobjcを使ってObjective-CのCore Bluetooth APIを呼び出しています

 それぞれのOSのBLE通信部を隠蔽する形でpythonライブラリ化されているので、どのOS上でも(ほぼ)同じpythonスクリプトでBLE通信をすることができるようになっています。

まー、なにはともあれやってみましょう

Windows10とmacOSにpythonをインストール

 Linuxでは(だいたいは)最初からpythonが入っていますが、Windows10には入っていないので、インストールする必要があります。また、macOSはpython2.7は入っていますが、python3は入っていないのでこちらもインストールする必要があります。以下の記事が参考になります。

 以下の説明ではプロンプトが「$」になっていますが、Windows10、macOS、Linuxそれぞれの環境でのプロンプトになっているものとします。動作自体はどの環境でも同じです。

bleakをインストール

  • コマンドプロンプト(Windows10ならPowerShellかコマンドプロンプト(cmd.exe))、LinuxならLXterminal等、macOSならターミナルを起動)を使える状態にします。
  • pipコマンドでbleakをインストールします。
$ pip install bleak

しばらく待つとbleakライブラリのインストールが終わり、利用可能になります。

toioコアキューブのBLEデバイスアドレスを調べる

 これはbleakのサンプルコードdiscover.pyそのまんまです。

discover.py
import asyncio

from bleak import discover

async def run():
    devices = await discover()
    for d in devices:
        print(d)

loop = asyncio.get_event_loop()
loop.run_until_complete(run())

これを動かすと以下のように出力されます。

$ python3 discover.py
D0:8B:7F:12:34:56: toio Core Cube
B0:52:22:33:44:5A: Unknown
6E:E4:DA:12:34:55: bluetooth deviceY
63:87:F5:55:22:33: bluetooth deviceX

 toio Core Cubeとあるところの6バイト16進数がデバイスアドレスになります。この値をメモしておきます。この値を後のスクリプトの中でセットします。上の実行例だと「D0:8B:7F:12:34:56」。
 macOSの場合、6バイト16進数ではなく、243E23AE-4A99-406C-B317-18F1BD7B4CBEのような長いUUID文字列になります。

toioコアキューブのバッテリー残量を読み取り、LEDを点灯し、モーターを回す

 toioコアキューブのバッテリー残量を読み取るには、oioコアキューブに接続したあと、バッテリー(Battery Information)のキャラクタリスティック(10B20108-5B3B-4571-9508-CF3EFCD7BBAE)を読み取ります。
 LEDを点灯するには、ランプ(Light Control)のキャラクタリスティック(10B20103-5B3B-4571-9508-CF3EFCD7BBAE)に書き込みます。
 モーターを動かすにはモーター(Motor Control)のキャラクタリスティク(10B20102-5B3B-4571-9508-CF3EFCD7BBAE)に値を書き込みます。

 toioコアキューブ技術仕様書の通信仕様の「ランプ」「モーター」の例を参考に値を書き込んでみます。

read_write_sample.py
import asyncio
import platform

from bleak import BleakClient

# バッテリーとLEDとモーターのキャラクタリスティック 
BATTERY_CHARACTERISTIC_UUID = ("10b20108-5b3b-4571-9508-cf3efcd7bbae")
LAMP_CHARACTERISTIC_UUID = ("10b20103-5b3b-4571-9508-cf3efcd7bbae")
MOTOR_CHARACTERISTIC_UUID = ("10b20102-5b3b-4571-9508-cf3efcd7bbae")  
# bleakではキャラクタリスティックのUUIDは小文字で書かないとダメみたい

async def run(address, loop):
    async with BleakClient(address, loop=loop) as client:
        x = await client.is_connected()
        #logger.info("Connected: {0}".format(x))
        print("Connected: {0}".format(x))
        # バッテリー残量読み取り
        battery = await client.read_gatt_char(BATTERY_CHARACTERISTIC_UUID)
        print("battery: {0}".format(int(battery[0])))
        # LEDを160ミリ秒、赤に点灯
        write_value = bytearray(b'\x03\x10\x01\x01\xff\x00\x00')
        await client.write_gatt_char(LAMP_CHARACTERISTIC_UUID, write_value)
        # モーター 左を前に100の速度、右を後ろに20の速度
        write_value = bytearray(b'\x01\x01\x01\x64\x02\x02\x14')
        await client.write_gatt_char(MOTOR_CHARACTERISTIC_UUID, write_value)
        # 5秒後に終了
        await asyncio.sleep(5.0, loop=loop)

if __name__ == "__main__":
    address = (
        # discovery.pyでみつけたtoio Core Cubeのデバイスアドレスをここにセットする
        "D0:8B:7F:12:34:56"  # Windows か Linux のときは16進6バイトのデバイスアドレスを指定
        if platform.system() != "Darwin"
        else "243E23AE-4A99-406C-B317-18F1BD7B4CBE"  # macOSのときはmacOSのつけるUUID
    )
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run(address, loop))
$ python read_write_sample.py
Connected: True
battery: 100

動かすとバッテリー残量を読み取って表示した後、LEDが赤く一瞬点灯します。
P_20191209_221300.jpg
その後、toioコアキューブのやや右側を中心に時計回りに回転します。
P_20191209_221458.jpg
5秒後に終了します。

toioコアキューブのボタン状態や読み取りセンサーの読み取るIDを通知させる

toioコアキューブのボタン状態や読み取りセンサーの読み取るIDを通知(notify)させるにはボタン(Button Information)のキャラクタリスティック(10B20107-5B3B-4571-9508-CF3EFCD7BBAE)と、読み取りセンサー(ID Information)ののキャラクタリスティック(10B20101-5B3B-4571-9508-CF3EFCD7BBAE)の通知をうけるようにします。

 これはbleakのサンプルコードenable_notifications.pyを参考に、以下のようなスクリプトを書きます。

enable_button_and_id_info_notification.py
import asyncio
import platform

from bleak import BleakClient

# ボタンと読み取りセンサーのキャラクタリスティック 
BUTTON_CHARACTERISTIC_UUID = ("10b20107-5b3b-4571-9508-cf3efcd7bbae") 
ID_READER_CHARACTERISTIC_UUID = ("10b20101-5b3b-4571-9508-cf3efcd7bbae") 
# bleakではキャラクタリスティックのUUIDは小文字で書かないとダメみたい

# ボタン状態の通知ハンドラ
def button_notification_handler(sender, data):
    print("BUTTON {0}: {1}".format(sender, data))

#読み取りセンサーの通知ハンドラ
def id_reader_notification_handler(sender, data):
    print("toio ID {0}: {1}".format(sender, data))

async def run(address, loop):
    async with BleakClient(address, loop=loop) as client:
        x = await client.is_connected()
        print("Connected: {0}".format(x))
        # ボタン状態の通知スタート
        await client.start_notify(BUTTON_CHARACTERISTIC_UUID, button_notification_handler)
        # 読み取りセンサーの通知スタート
        await client.start_notify(ID_READER_CHARACTERISTIC_UUID, id_reader_notification_handler)
        # 10秒後に終了
        await asyncio.sleep(10.0, loop=loop)
        # ボタン状態、読み取りセンサーの通知終了
        await client.stop_notify(BUTTON_CHARACTERISTIC_UUID)
        await client.stop_notify(ID_READER_CHARACTERISTIC_UUID)

if __name__ == "__main__":
    address = (
        # discovery.pyでみつけたtoio Core Cubeのデバイスアドレスをここにセットする
        "D0:8B:7F:12:34:56"  # Windows か Linux のときは16進6バイトのデバイスアドレスを指定
        if platform.system() != "Darwin"
        else "243E23AE-4A99-406C-B317-18F1BD7B4CBE"  # macOSのときはmacOSのつけるUUID
    )
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run(address, loop))

 動かしたらtoioコアキューブを持ち上げてボタンを押してみたり、トイオコレクションのマットやステッカーの上にのせてみます。

$ python enable_button_and_id_info_notification.py
Connected: True
toio ID 10b20101-5b3b-4571-9508-cf3efcd7bbae: bytearray(b'\x01\xa4\x01g\x01|\x00\xaf\x01e\x01|\x00')
toio ID 10b20101-5b3b-4571-9508-cf3efcd7bbae: bytearray(b'\x01\xa4\x01h\x01|\x00\xaf\x01f\x01|\x00')
toio ID 10b20101-5b3b-4571-9508-cf3efcd7bbae: bytearray(b'\x01\xa4\x01g\x01|\x00\xaf\x01e\x01|\x00')

逐一変わる値が通知されてくるのがわかります。

実は...

 実際に動作を確認できたのはWindows10(1909)とLinux(raspbian busterで、RasPi3Bと4Bで動作確認)だけです。
 すみません。自分の環境ではmacOSでbleakを動かすことはできませんでした。discovery.pyは動くのですが、それ以外がさっぱり動かなかったです。(環境はMacBookPro 13inch(early2015) macOS 10.14.6(mojave))
 このmacOS環境では、Linux/macOS対応をうたうAdafruitのBLEライブラリだと動くのですが、逆にこちらはraspbian busterでは動きませんでした。(bluezのバージョン違いが原因) bluezのbackendを修正すれば動くような気がしますが、原因がつかめてないのでなんとも。

さいごに

 小学校や中学校のパソコン部?にもありそうな身近にあるパソコンやRaspberry Piでpythonを使ってtoioコアキューブを動かして見る方法を紹介してみました。ノートPCのようにbluetoothインタフェースが内蔵されていないPCでも、USBドングル型のBluetoothインタフェースをつければ動かすことができると思います。
 node.jsとnoble、scratch-linkの導入がちょっとハードルが高いという方、またWindows10は使えるが、インストールできるアプリはストアアプリに制限されているような環境でもストアアプリのpython3はインストールできてbleakが使えるので、ぜひ試してみてください。

kenichih
デジタルおもちゃをつい買ってしまうそのへんによくいるおっさん
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした