chakokuのブログ(rev4)

日々のごった煮ブログです

nRF52840 + CircuitPyton + BLEによるKindleページめくりボタン試作

iOS とペアリングできたので、次はiPadとペアリングさせた。iPadをどこかに立てかけて、Kindleアプリで本を読んでるとページめくりが面倒なので、BLE接続のページめくりボタンを作ってみた。ソースは以下

iOS上でKindleが動いている前提での) Kindleページめくり用ボタンの例

# original source:
# https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/main/examples/ble_hid_periph.py
#

# SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

"""
This example acts as a keyboard to peer devices.
"""

import sys
import time
import board

from digitalio import DigitalInOut, Direction, Pull
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard import Keycode

import adafruit_ble
from adafruit_ble.advertising import Advertisement
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.standard.hid import HIDService
from adafruit_ble.services.standard.device_info import DeviceInfoService


#setup User button on board
btn = DigitalInOut(board.SWITCH)
btn.direction = Direction.INPUT
btn.pull = Pull.UP

# Use default HID descriptor
hid = HIDService()
device_info = DeviceInfoService(
    software_revision=adafruit_ble.__version__, manufacturer="Adafruit Industries"
)
advertisement = ProvideServicesAdvertisement(hid)
advertisement.appearance = 961
scan_response = Advertisement()

ble = adafruit_ble.BLERadio()
if ble.connected:
    for c in ble.connections:
        c.disconnect()

print("advertising")
ble.start_advertising(advertisement, scan_response)

kbd = Keyboard(hid.devices)

while True:
    while not ble.connected:
        pass
    print("Start typing:")
    while ble.connected:
        if not btn.value:          # if pushed
           kbd.send(Keycode.LEFT_ARROW)
           print("<-")
           time.sleep(0.5)     # to avoid chattering
        time.sleep(0.1)
    ble.start_advertising(advertisement)

Git上のAdafruit_CircuitPython_BLEのble_hid_periph.pyをベースに、nRF52840上のユーザボタンが押されたら、ARROW_LEFTのコードを送信するよう改修した。何が悪いのか分からないが、時々ボタン操作したのに反応しない。ポーリング方式だから?

上記コードをファイル名: code.py としてFlashに書き込むと、nRF52840の電源をOnした際に、自動的に実行される。

■追記
目的と手段がワケ分からずと言いながら、AdafruitのnRF52840を買いましたが、、いざ買ってみると、BLEデバイスがさくっと作れるのでこれはこれで面白い。CircuitPythonを使うことで、スマフォとペアリングできない!!といった悩みもなくなった。自力ではBLEペアリングの問題が解決できず他力なのだが。。

■ご参考
参考にしたソースは以下
https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/main/examples/ble_hid_periph.py

BLEを動かすためにnRF52840のボードを買った->CircuitPythonによるBLE Keyboardが動いた

本来の目的が何なのか分からなくなっているが、道楽だからいいだろうということで、、nRF52840のボードを買った。外観は以下

目の前の目的はBLEでiOSと接続できるKeyboard風SWを実現するため。CircuitPythonを焼いて、modulesで使えるモジュールを表示させた。どうもBLEのライブラリが無いようなのだが。。なぜか!?

Adafruit CircuitPython 7.0.0 on 2021-09-20; Adafruit Feather nRF52840 Express with nRF52840
>>> help('modules')
__main__          bitmaptools       math              sharpdisplay
_bleio            board             microcontroller   storage
adafruit_bus_device                 builtins          micropython       struct
adafruit_pixelbuf busio             msgpack           supervisor
aesio             collections       neopixel_write    synthio
alarm             countio           onewireio         sys
analogio          digitalio         os                terminalio
array             displayio         pulseio           time
atexit            errno             pwmio             touchio
audiobusio        fontio            rainbowio         traceback
audiocore         framebufferio     random            ulab
audiomixer        gc                re                usb_cdc
audiomp3          getpass           rgbmatrix         usb_hid
audiopwmio        io                rotaryio          usb_midi
binascii          json              rtc               vectorio
bitbangio         keypad            sdcardio          watchdog
Plus any modules on the filesystem

BLEは組み込まれておらず、外部モジュールとして手動で加えるようであった。。
https://github.com/adafruit/Adafruit_CircuitPython_BLE
adafruitのbundleを落としてきて、ZIPを解凍して、BLEライブラリをCopy&Pasteするようであった。

iOSと接続している実装例がAdafruit社から出ていて、コードの冒頭は以下である。これらのライブラリを入手してセットアップする必要がある。(
https://learn.adafruit.com/ble-hid-keyboard-buttons-with-circuitpython/ble-keyboard-buttons-libraries-and-code
)

import adafruit_ble
from adafruit_ble.advertising import Advertisement
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.standard.hid import HIDService
from adafruit_ble.services.standard.device_info import DeviceInfoService
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode

V7のbundleを展開して、adafruit_ble , adafruit_hidを nRF52840にコピーした。これにより上記のライブラリ importはエラーなく実行できたが、ソースコード本体の実行でエラーが出た。特に、、
adafruit_bleモジュール内に BLERadioクラスがある前提で書かれているが、このクラスがどこか別のモジュールに移っているようである。あるいは、、使い方が変わったのか??

ble = adafruit_ble.BLERadio()

BLE Keyboard Buttons Libraries and Code | BLE HID Keyboard Buttons with CircuitPython | Adafruit Learning System

# PyLint can't find BLERadio for some reason so special case it here.
ble = adafruit_ble.BLERadio()  # pylint: disable=no-member

https://www.digikey.com/en/maker/projects/circuitpython-ble-multi-temperature-monitoring/5c208beeafa74418a59e1fafde853ae9

再起動してdir()関数で確認するとBLERadioが存在した。先ほどエラーになったのは、adafruit_bleを何かで上書きしてしまったせいだろうか。

Adafruit CircuitPython 7.0.0 on 2021-09-20; Adafruit Feather nRF52840 Express with nRF52840
>>> import adafruit_ble
>>> dir(adafruit_ble)
['__class__', '__name__', '__file__', '__path__', '__version__', 'Service', '_bleio', 'advertising', 'characteristics', 'sys', 'services', 'Advertisement', '__repo__', 'BLEConnection', 'BLERadio', 'attributes']

Git上にあったBLE_HIDサンプルを動かしてみた。
https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/main/examples/ble_hid_periph.py

エラーなく動作して、iOSではペアリングまで行えた。以下はCircutPython側のログ表示

advertising
Start typing:
a1
i1
u1
e1
o1

Basicで言うところのinkey$関数のような動作をしているので、nRF52840と接続しているシリアルコンソール(REPL画面)からaiueoと打ち込んだ。以下はiOSの画面。BLEキーボードからキー入力されたと認識され、「あいうえお」と表示された。

まとめ
MicroPython版のESP32用BLEライブラリではiOSと接続できてもペアリングまで進められなかった。CircuitPythonの場合、ペアリングも可能でキーボード入力まで行えた。なお、、MicroPython版のSTM32用BLEライブラリだと、NimBLEが使われており、ペアリングも可能かもしれない。が、、適当なSTM32ボードが見つけられなかった(たしか。なぜCircuitPythonに切り替えたのだったか、少し忘れかけているが。) 自分に実力があったらESP32 + MicroPythonでBLEライブラリをデバッグしてなんとかペアリングも実装してしまえるのだろうけど、、そんな余力も時間も知力もない。ということで、、今後BLEのアプリを作る場合は、動作が安定しているCircuitPythonを使うことにする。夜の8時過ぎにボードが届いて、CiruitPythonのファームをボードに焼いて、適当に調べながら2時間ぐらいでBLE Keyboardの試作が動いた。CircuitPythonのBLEは作りこまれているなーと感心いたします。

■追記
adafruit_bleのソースを読んでみたが、ハードレイヤーを制御するコードが全くなかった。よく見ると、import _bleioを実行しており、そっちに任せているようであった。改めて組み込みモジュールを再確認すると、、_bleioが存在していた。(見落としていた)

>>> import _bleio
>>> dir(_bleio)
['__class__', '__init__', '__name__', 'Adapter', 'Address', 'Attribute', 'BluetoothError', 'Characteristic', 'CharacteristicBuffer', 'Connection', 'Descriptor', 'PacketBuffer', 'RoleError', 'ScanEntry', 'ScanResults', 'SecurityError', 'Service', 'UUID', 'adapter', 'set_adapter']

_bleioのソースは確認していないけど、BLEコントローラを制御していると思われる。(といっても、ミドルウエアを介してハードを制御しているのだろうと推測しますが)

ご参考
最新版のBundle
https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20211207/adafruit-circuitpython-bundle-7.x-mpy-20211207.zip

手作り餃子(改訂版)

前回の反省を踏まえ、再度餃子を作る。材料は以下(4人前、24個ぐらい)

    • 強力粉 160g
    • 水 80cc
  • あん
    • 豚ミンチ 120g
    • キャベツ 120g
    • ショウガみじん切り
    • 醤油 小1
    • 酒 小1~1.5
    • ごま油 小1

皮を作る。はじめは菜箸で、小麦と水をグルグルと混ぜる。だんだんと水が回ってゆく

一回目のこね状態。このような状態だといくらこねても均一にならないので、水が足りない。この状態に叱らない場合は水をたすべき。この後、こねて休むを2回行う

今回は豚ミンチを使用。味付けはミンチに対して行う。まずはよく練ってから、調味料を加える。

塩もみした野菜とみじん切りショウガを加える。また練る。

練り終わった所。この時点で、ごま油、醤油、酒によるまったりした香りがしている。

皮を延ばしたところ。餃子としての一体感のためには、皮はあまり大きくしない方がいいと思う(延ばし過ぎない。延ばし過ぎると、具を包んでいる所が少しで、包みしろの部分が大半になってしまう)。

包み終わり。(全員揃わないので、半分だけ包んだ)

包んだ餃子を茹でる

浮き上がってから1分程度茹でるとのこと。

できあがり。見て分かるけど、包みしろが多い、皮を延ばし過ぎると包みしろが大きくなる。もうちょっとバランスを考えたい。


TVでは皮を作るとき、粉っぽいからといって水を足さないようにと説明されていた。最初の段階からかなり混ざり方が不十分だったのだが、結局混ざり切らず最後は少し水を足した。一回目にこねる段階で、生地が均一にならない場合はやはり水が足りないので、均一になるぐらいには最低加えるべき

あんの作り方の注意

  1. ミンチだけ練る。白っぽく粘り気がでるまで良くねる
  2. 練ったミンチに、醤油、ごま油、酒を加えてさらに練る(ミンチに味をつけることで野菜の味を活かす)
  3. キャベツは塩もみしない方法もあるらしいが、自分は少し塩を振って水分を抜いた
  4. ミンチに、野菜、ショウガを混ぜてさらに練る(野菜を入れてからはあまり練らないという説明をする料理人もおられます)

あとは、延ばした皮でアンを包むのだが、包みしろを大きくとると、味のバランスがイマイチになる気がして、包みしろはなるべく少ない方がいいように思う。だから、皮を延ばす時も調子にのって大きく延ばさず、アンを包めるだけの最低限の大きさにしておく方がいいように思える

今回の気づきと反省点

  • アンに、酒とゴマ油を足したので、味がまったりした(一体感のある味)、塩加減もちょうどいい。一方で肉の旨味はあまりない。
  • 皮をこねる時、一回目のこねで均一にならない場合はやっぱり水が足りない。その場合は水を足すべき
  • 皮とアンの包み方のバランスをさらに改良すべし。餃子、具、皮(つつみしろ)の3点セットになっている。餃子としての一体感に欠ける。もうちょっとどうにかできる気がする

■追記
ずっと水餃子にして食べてきたけど、一度焼き餃子にしてみた。結果、まったく旨くなかった。やっぱり難しい

M5 Stick CがBLE Advertisingしている時のパケット

MicroPythonを使ってM5 Stick Cを BLE Keyboard風にしたてようとしたものの、成功せずに原因調査中。Arduino版のNimBLEだと動作したので、パケットに違いがあるのかを見るため、BLE Snifferでキャプチャしてみた。その時の結果は以下
(もう面倒なので、固有アドレスとか消さずに掲示します)

iPADから使う場合も、M5からペアリング要求して、iPAD画面でペアリングを許可する処理をしており、キーボードとして使うには、ペアリングが必須なのかもしれない。詳細わかっていませんが。

ESP32版のMicroPythonではParingメソッドが実装されていない。NimBLE版で実装しているSTM32とかなら実装されているらしい。

iOS/iPadにしても最低ペアリングしてくれないとキーボード入力なんて危なくて受け付けられないと設計されていても納得できる。多分ペアリングがないのが入力できない原因だろう。。と安直に決めつけてみる。 STM32でBLEが動かせるボードがあれば切り分けもできそうですが。。

■追記
CircuitPythonのBLEだったら動くのでは?と思い、マイコンボード「Adafruit Feather nRF52840 Express」を発注した。iOSとも接続できまーすと宣伝しているので。。MicroPython版のBLEではずっと手こずっていて、目的と手段が入れ替わっている。目の前の事にこだわり、目的地がつど変わる・・・。

■追記
NimBLE Keyboard Sample で、SecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND)を
実行しているとの説明
https://github.com/palette-system/az-macro/wiki/NimBLE-Keyboard-Sample


https://github.com/h2zero/NimBLE-Arduino
https://github.com/wakwak-koba/ESP32-NimBLE-Keyboard/blob/master/BleKeyboard.cpp

イシューからはじめよ

「イシューからはじめよ」を買った(Kindleで約1000円)。
気になった点をメモする(今後順次追加)

  • 答えの出ない悩みには時間を使わず、考えて答えが出る問に時間をかける
  • 10分考えてらちが明かない場合は、悩みに入っている
  • Issueの定義
    • 2つ以上の集団の間で決着のついていない問題
    • 根本にかかわる、もしくは白黒がはっきりしていない問題
  • Issueのマトリクス
    • 横軸:Issueの度合い:答えを出す必要性の高さ
    • 縦軸:解の質:どこまで明確に答えを出せているか
  • 縦軸横軸を上位に進めることのできる確率は1%、がむしゃらにやっても、まず高いIssueには到達できない
  • いたずらに労力をかけて縦軸方向に伸ばしてから横軸方向に伸ばすアプローチは犬の道、失敗する。体力勝負のやり方はやめろ。
  • 問題を厳選して横軸に進んでから縦軸に進むべき。1%の本質のIssueをどのように選ぶのかが重要
  • Issueを見極めること
    • まず最初に「最終的に何を伝えようとしているのか」「何に答えを出すためのものか」を明確にする。
    • Issueから仮設を作る。仮説を立てることで解くことができる。まず言葉にする。
    • 人には、ビジュアル思考型と、言語思考型がいる
    • 仮説は、主語と動詞を入れる、WHYより、WHERE/WHAT/HOWで表現
  • 構造的理解の4パターン
    • 1.共通性の発見(類推できる)
    • 2. 関係性の発見
    • 3. グルーピングの発見(まとまりを見つける)
    • 4. ルールの発見(普遍的な仕組み、数量的な関係性)
  • Issueを特定する5つのアプローチ
    • 1. 変数を絞る
    • 2. 視覚化する
    • 3. 最終形から逆に考える
    • 4. So What?を繰り返して、表層的な問いから本質的な問いに掘り下げる
    • 5. 極端な事例を考える

■追記
かなり端折って読んでいるのだけど、この本の内容は、近々開催する予定のIoT勉強会に参加してもらう人にとって、参加する価値は何なのか?それをどのように表現し、伝えるか??にも当てはまると思った。開催する側と参加した人との間の課題、それをどう解いていくかを考えて、ストリーラインやサブイシューの考えに基づき講義を組み立てると、参加してもらった人に伝わる勉強会になるのでは?と思った。そもそもの教材の準備がまだまだなので、そこまで質を高められるかどうか?がかなり問題なのだが。


HID/HOGPの仕様書

HID/HOGPの仕様書や説明サイトがあったのでメモ

SiliconLab社のBLE HID Keyboard説明ページ
BLE HID Keyboard - v2.13 - Bluetooth API Documentation Silicon Labs

HOGPの仕様書DL用URL (bluetooth org)
https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=245141

BLE HID(HOGP)のオリジナルはUSBらしく、HIDの仕様はUSBサイトにあるそうだ
Human Interface Devices (HID) Information | USB-IF

iOSではEjectキーで本体キーボードと無線キーボードを切り替えるという記事(動かないのはこのせいか??分からん)
https://community.silabs.com/s/article/hid-over-gatt-keyboard-demo-for-hid-keyboard-and-consumer-page-report?language=en_US

さらに調べると、iOSapple社外のキーボードを使う場合は、管理画面で設定しないとだめらしい。知らなかった。このせいで使えないのか??
iPhoneで他社製キーボードを追加・設定する方法 | iPhone Wave
と思ったら、上記はダウンロードした別のソフトウエアキーボードに切り替えるための手順であった。外付けのBLEキーボードのことではない。(だからこのせいではない)

M5 Stick C PlusをBLE Keyboard風にしてみる->BLE KeyboardはiOSではまともに動かない

スマフォのシャッターは、BLE Keyboardの音量Up/Downで代用できることが分かっていて、M5 StickCPlusをBLE Keyboard風に仕立てたら、SitckCのボタン押下で、スマフォのシャッターが切れるはず。
KeyboardはHIDプロファイルと思っていたけど、BLEでは、HOGPと呼ばれるらしい。*1
MicroPythonのBLEサンプルでもKeyboardの実装例があるので、それを参考に音量Up/Downだけを実装したらいいのではと想像する。
Git上には、MicroPythonで実装したHIDの試作がいろいろ公開されているので、試して動くやつを選ぶ予定。
少し調べただけなのだが、HIDとして動かすには押下キーをプロファイルで伝えるだけではなく、バッテリー等のサービスも実装する必要があるようだ。公開されているサンプルにはその辺も実装されているのでよく読めばおおよそ分かるかと。
heerkog氏?のサンプルを動かしてみる。エラー無く起動できて、M5側はAdvertisingの状態
https://github.com/Heerkog/MicroPythonBLEHID/blob/master/examples/simple/keyboard_example.py

Server created
no secrets available
BLE on
Registering services
Writing service characteristics
Writing device information service characteristics
Writing battery service characteristics
Writing hid service characteristics
Advertiser created:  Keyboard  with services:  [UUID(0x1812)]
Server started
Started advertising

サンプルはエラー無く起動されるし、iOSでもデバイスを検出してconnectまでは進むのだが、、キー入力イベントを発生させてもiOS側ではキー入力したと判断されない。notifyのやり方が間違っているのだろうか。
BLEをスキャンして属性を表示させた時、バッテリーサービスは表示されるが、HIDサービスは表示されない。キー入力を扱うにはHIDサービスが見えるべきなのでは?と思うのだが、何が正しいのかよくわからない。
デバッグのためには、ArduinoとかBLEが正しく動くファームとアプリを走らせて、本来どう見えるべきかを把握しないと、何が問題なのか?分からない。

iOSとの接続ではうまくいかず、Androidで試すとキー入力ができた。Androidの場合、再接続が発生していて、これがうまくいくかどうかの違いなのだろうか。AndroidでBLEスキャナーで確認するとHIDサービスが正しく表示されている。やはり、iOSでHID
サービスが見えない状態になってるのがうまく動かない原因なんだろう。

AndroidではHIDサービスが正しく見えている

iOSではバッテリーサービスしか見えない

あてずっぽだが、、Androidで読めて、iOSで読めないというのは、HIDのプロファイルで何か非互換な書き方になっているのではと推測してみる。HOGPのキーボードでプロファイルを掲載してくれている人がいるので、このデータを使ってパケットを作ってみる。
BLE HID Keyboard(Surface キーボード) の Report Map (Report Descriptor)

作ってみたが、HIDは表示されないままであった。だとすると、、過去にArduino等で成功しているので、ArduinoでBLE Keyboardを動かして、その時の素のパケットをスキャナでキャプチャして、それをそっくりそのままMicroPythonのBLEライブラリで作るしかないか。。 本筋としては、BLEのHOGPの仕様書を探して、仕様書を満たすパケットを作るべきなんだろうけど

めざす、シャッターボタンを解析した人がいて、その記事によると、シャッタボタンはBLEのHIDとして認識されているそうなのだが、AdvertisingにはHIDが入っていて、サービス一覧にはバッテリーしかない。だから、iOSとしてはHIDサービスが表示されないのが正しい動作のようである。うーんわからん。そういうものなのか。
https://note.com/tomorrow56/n/n83cd108e65d5
ThousanDIY (Masawo Yamazaki)氏の記事より

C言語版でもiOSの互換性が問題になっている。こちらの対応策を読めば分かるかも
IoS 13 compatibility? · Issue #3 · T-vK/ESP32-BLE-Mouse · GitHub
Works with iPad Air2, iPad Pro 2, iPhone SE. Does NOT work with iPad 2 · Issue #2 · T-vK/ESP32-BLE-Keyboard · GitHub
List of supported / unsupported Apple devices (iPhones, iPads, Macs etc.) · Issue #51 · T-vK/ESP32-BLE-Keyboard · GitHub

■追記
Arduinoの世界?でも、BLE KeyboardとiOSとの接続は難航しているようで、以下の記事では、NimBLEを使うことで制御できると書かれていた。自分も同じように公開されているソースで実装したら、確かに動いた。(Arduino版ですが)
M5StickCでiPhoneのスクリーンキャプチャボタンを作る | Lang-ship

NimBLEのソースを見たり、BLEのパケットキャプチャして、MicroPython版とどう違っているのかを掘り下げたら、MicroPython版のBLE Keyboardが動くようになるかもしれない。たんなるパケットのフラグの違い程度なら直せるかもだけど、プロトコルのレベルで違っていたりしたらもうお手上げかと
NimBLE-Arduino - Arduino Reference

Gitのソースは以下。確かに、全部自力でライブラリを書いているようである。
https://github.com/h2zero/NimBLE-Arduino



■ご参考URL
https://www.renesas.com/jp/ja/document/apn/bluetooth-low-energy-protocol-stack-hogp-ascii-text-keyboard-rev100?language=ja
GitHub - Heerkog/MicroPythonBLEHID: Human Interface Device (HID) over Bluetooth Low Energy (BLE) GATT library for MicroPython.
WIP: examples/bluetooth: Add BLE HID mouse and keyboard examples. by dpgeorge · Pull Request #6559 · micropython/micropython · GitHub
WIP: examples/bluetooth: Add BLE HID mouse and keyboard examples. by dpgeorge · Pull Request #6559 · micropython/micropython · GitHub
https://www.usb.org/sites/default/files/hid1_11.pdf
https://www.renesas.com/jp/ja/document/mat/bluetooth-low-energy-protocol-stack-api-reference-manual-hogp-rev104?language=ja&r=488876

WIP: examples/bluetooth: Add BLE HID mouse and keyboard examples. by dpgeorge · Pull Request #6559 · micropython/micropython · GitHub

*1: HID over GATT Profile(HOGP)