🚀

Raspberry Pi と micro:bit の BLE 通信

2024/11/18に公開

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

めでたくボタンの状態を取得できました。

脚注
  1. Bluetoothを有効にする方法はこちらのページを参考にしました。(ありがとうございます)
    https://monomonotech.jp/kurage/webbluetooth/microbit_ble_setting.html ↩︎

Discussion