Raspberry Pi と micro:bit の BLE 通信
micro:bit は実は BLE が使えることを知ったので、試しに使ってみました。
今回のゴールは以下の実現です
- Raspberry Pi から micro:bit のセンサーデータを読み出す
- bluetoothctl で接続してみる
- Go言語で実装する
micro:bit の準備
MakeCodeで、Bluetoothを有効にして[1]、以下のスケッチを書き込みます。
バージョン
今回、以下の環境で試しました
$ cat /proc/device-tree/model
Raspberry Pi 3 Model A Plus Rev 1.0
$ bluetoothctl --version
bluetoothctl: 5.66
$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
akihiro@raspberrypi:~ $ go version
go version go1.19.8 linux/arm64
$ go version
go version go1.19.8 linux/arm64
まずは bluetoothctl で触ってみる
BLEなので、Scanして、Connectして、Readする流れになります
Scan
まずは Scan します。数秒後に scan off して止めます
[bluetooth]# scan on
Discovery started
[CHG] Controller B8:27:EB:89:F5:01 Discovering: yes
[NEW] Device DF:09:4E:6C:38:40 BBC micro:bit [zetot]
...
[bluetooth]# scan off
scan に成功すると devices
でデバイスが見えるようになります。
[bluetooth]# devices
Device DF:09:4E:6C:38:40 BBC micro:bit [zetot]
...
Connect
デバイスが見つかったので、connect
してみみます。
[bluetooth]# connect DF:09:4E:6C:38:40
Attempting to connect to DF:09:4E:6C:38:40
[CHG] Device DF:09:4E:6C:38:40 Connected: yes
Connection successful
Connection successful
が出れば成功です。
ただし、何度か試していると、失敗することがありました。よくわからないけど、とりあえずOS再起動すると直りました。
Read
connect できたら gatt モード?に入って、characteristic の一覧を眺めます
[BBC micro:bit [zetot]]# menu gatt
[BBC micro:bit [zetot]]# list-attributes
(snip)
Characteristic (Handle 0x0000)
/org/bluez/hci0/dev_DF_09_4E_6C_38_40/service002e/char0032
e95dda91-251d-470a-a062-fa1922dfa9a8
MicroBit Button B State
(snip)
それっぽいのがありますね。
read してみましょう。read するためには select-attribute
が必要です。
[BBC micro:bit [zetot]]# select-attribute e95dda91-251d-470a-a062-fa1922dfa9a8
[BBC micro:bit [zetot]:/service002e/char0032]# read
Attempting to read /org/bluez/hci0/dev_DF_09_4E_6C_38_40/service002e/char0032
[CHG] Attribute /org/bluez/hci0/dev_DF_09_4E_6C_38_40/service002e/char0032 Value:
00 .
00 .
それっぽい値が出てきました。
B ボタンを押しながらもう一度 read すると 01
になりました
[BBC micro:bit [zetot]:/service002e/char0032]# read
Attempting to read /org/bluez/hci0/dev_DF_09_4E_6C_38_40/service002e/char0032
[CHG] Attribute /org/bluez/hci0/dev_DF_09_4E_6C_38_40/service002e/char0032 Value:
01 .
01 .
さらに押し続けていると 02, 離すと 00 に戻る、という挙動しています。
おおよその挙動がわかったので、次は Go で同様のことをやってみましょう
Go言語で触ってみる
Go言語には BLE 関連のライブラリがいくつかあります。今回は2024.11現時点で開発が活発に続いている、tinygo-org/blueooth を使うことにしました
サンプルコード をベースに実装してみます。
package main
import (
"fmt"
"strings"
"time"
"tinygo.org/x/bluetooth"
)
func main() {
var adapter = bluetooth.DefaultAdapter
must("enable BLE stack", adapter.Enable())
ch := make(chan bluetooth.ScanResult, 1)
// Scan for devices.
println("scanning...")
err := adapter.Scan(func(adapter *bluetooth.Adapter, result bluetooth.ScanResult) {
// println("found device:", result.Address.String(), result.RSSI, result.LocalName())
if strings.HasPrefix(result.LocalName(), "BBC micro:bit") {
fmt.Printf("FOUND: %s %s\n", result.Address.String(), result.LocalName())
adapter.StopScan()
ch <- result
}
})
must("scan", err)
// Connect to the device.
println("connecting...")
var device bluetooth.Device
select {
case result := <-ch:
device, err = adapter.Connect(result.Address, bluetooth.ConnectionParams{})
must("connect to device", err)
println("connected to ", result.Address.String())
}
// Discover services and characteristics.
println("discovering services...")
services, err := device.DiscoverServices(nil)
if len(services) == 0 {
must("discover services", fmt.Errorf("no services found"))
}
var targetCharacteristic bluetooth.DeviceCharacteristic
for _, service := range services {
println("service:", service.UUID().String())
characteristics, err := service.DiscoverCharacteristics(nil)
must("discover characteristics", err)
found := false
for _, characteristic := range characteristics {
println(" characteristic:", characteristic.UUID().String())
// e95dda91-251d-470a-a062 == B button
if strings.HasPrefix(characteristic.UUID().String(), "e95dda91-251d-470a-a062") {
println(" FOUND ", characteristic.UUID().String())
targetCharacteristic = characteristic
found = true
break
}
}
if found {
break
}
}
// Read data from the device.
println("reading data")
for {
buffer := make([]byte, 1)
_, err := targetCharacteristic.Read(buffer)
must("read data", err)
println("data:", buffer[0])
time.Sleep(2 * time.Second)
}
}
func must(action string, err error) {
if err != nil {
panic("failed to " + action + ": " + err.Error())
}
}
ではビルドして実行してみましょう。sudo (かそれに類する権限) が必要です
FOUND: DF:09:4E:6C:38:40 BBC micro:bit [zetot]
scanning...
connecting...
connected to DF:09:4E:6C:38:40
(snip)
reading data
data: 0
data: 0
data: 2 << ボタンを長押しした
data: 0
^C
めでたくボタンの状態を取得できました。
-
Bluetoothを有効にする方法はこちらのページを参考にしました。(ありがとうございます)
https://monomonotech.jp/kurage/webbluetooth/microbit_ble_setting.html ↩︎
Discussion