タップセンサとBLEを使った電子書籍ページめくり機
ふと気付くと今年初めてのブログ投稿です。今年もよろしくお願いします。
さて、2022年の夏にESP32-WROOM-32EのBLEを使って無線化したAndroid用の電子書籍ページめくり機を作りました。その後、もっと小さくて省電力の無線モジュールが、最初に作った有線ページめくり機で使ったのと同じSeeed社から同サイズでシリーズ化されていたことに気が付きました。特にnRF52840 Senseは、定評のあるNORDIC社のBLE SoCとSTマイクロエレクトロニクス社の6軸MEMSセンサを組み合わせた上にTI社のLi-PoバッテリーマネージャICを組み合わせた意欲作で、とても惹かれました。なお、主要な資料はSeeed社のWikiにまとめられています。
ESP32などを調べていたときには既に秋月電子さんなどで併売されていたのを見つけられなかったことにちょっと落ち込んだりもしましたが、気を取り直して早速入手して遊び始めました。私の環境(macOS Catalina on MacBook Air 2017)ではArduinoのコンパイルや転送が一筋縄でいかなかったものの、CircuitPythonを試してみたところ各種の作例とともに開発環境のMuも素直に動きました。(ただ、人によってはMacでマウントされなくなることがたびたび起こるそうです。) 味見もできたところで何を作るかを考えました。5年ほどRaspberry Pi Zero W/WHのWi-Fiで運用している宅内環境センサをBLEに差し換えようかと思ったのですが、実験したところ距離が遠くて接続が安定せず諦めて、とりあえずページめくり機を作ってみました。まずは記念撮影。

左から、330円のリモートシャッター、最初の有線版、今回のもの、前回のESP32版です。

今回作ったものも中身はシンプルで、小さい方のMINTIAにちょうど収まりました。また、タクトスイッチの軸上に、べつやく れいさんのDPZ記事で知ったセメダイン社のBBXでキートップを貼るなどして、使い勝手がすこぶる良くなりました。

回路図も簡単です。なお、次の点は少々考えました:
・CircuitPythonで割り込みキーと値を読み取るキーを1つのピンに重ねて定義することができないため、2ピンをパラに繋いでそれぞれにキー定義しています
・DeepSleepにすると内部プルアップが維持できなくなるので、起動ピンは外部プルアップにしています
・Reader(& Kindle)とKinoppyのモード切替SWの場所が取れず、論理が以前と逆でプルアップにしています
・モード切替SWはKinoppy時にプルアップを常時Lowに引くため、内部プルアップ抵抗typ.13kではリークが0.25mAと大きいと思い、100kで外部プルアップしました(0.03mA)
・入力待ちのLEDを付けました。暗くていいので0.1mAしか流していません。
・回路図上のLi-Poは110mAhですが、その後ケースに合わせて600mAhに置き換えています
コーディングは2段階で行いました。
まず、従来Arduino言語で書いていたBLE版のコードをCircuitPython用に組み直してみて、そこからPythonらしく(よりPythonicに)書くことを心がけてみました。ごちゃごちゃっと手続きが並んでいたのが整理されていくのがPythonの楽しいところだと思います。また、CircuitPythonのスリープ割り込み手順が分かったので、従来キー読み取りループを常に回していたところがスマートにできました。また、BLEでConsumerControl-Powerのキーコード0x30をAndroid端末へ送るとスリープに落とせることが分かったので、BACKキーの長押しで端末とページめくり機が両方スリープするようにできました。
一方、前回は自前で組んでいたLi-Po電圧モニタのところが、XIAO nRF52840で組み込まれているものの、正しく電圧をモニタする手続きをしないとVBAT入力などのピンがnRF52840の絶対最大定格VDD+0.3Vを超えてしまうことに気が付きました。

これをTwitterで呟いていたところSeeed社の中の人に拾われて検証され、現状はよろしくないという話になっています。個人的には、VBATとポートの間に1MΩが入っているので大きな問題では無いと思っていたのですが、実際に電池を繋いでディープスリープに落とすとリーク電流が約2mAも流れたので、Li-Poに限らず3.6V以上の電池で駆動する場合は、
・真っ先にP0.14(CircuitPythonではboard.READ_BATT_ENABLE)をLowに落とす
・スリープは端子状態を制御できるライトスリープしか使わない(ちなみにライトスリープ時は0.28mA)
・電池に直接ハードSWを付けて使わないときは切り離す
ようにしました。結果として、今回のコードでは
A. BLE接続を維持して暗いLEDを付けキー読み取りを待つ
B. BLE接続を切ってLEDも消灯し充電状態と電源キーだけを監視する
の2種類のライトスリープを使い分けることにして、後者Bをスードディープスリープ(pseudo deep sleep)と勝手に呼んでいます。 なお、本来のディープスリープで2mA流れた原因はいまのところ不明ですが、端子状態が不定になって何らかのリークパスができたのかも知れませんし、私のが既に壊れているのかも知れません。ちなみに、ディープスリープに入る前のBLE接続状態によっても電流値は変動するようでした。 続いて、コーディングの第2段階です。
従来のページめくり機と同じ機能でしばらく使っていて、たまにブックマークを付けるときなどに画面をタップしてメニューを出すのを、内蔵センサの機能でできないかと思い立ちました。
あらためてLSM6DS3TR-Cのドキュメントからデータシート(リンク先PDF)を見ると、タップ検知機能というのがあるようです。
そもそも加速度センサを使ったタップ検知とは何か、ということはアナログデバイセズ社の代理店であるマクニカ社のチュートリアルが分かりやすかったです。
これを踏まえてLSM6DS3TR-Cのアプリケーションノート(リンク先PDF)とSeeed社のフォーラムにおけるQ&A(Arduinoに沿って交わされたもの)を参考に、センサの割り込みレジスタにアクセスするクラスを作成して、ダブルタップを安定して取得できるようになりました。また、(A)通常のライトスリープの時はダブルタップで起きてダブルタップを認識して画面タップ・メニュー出しをし、(B)pseudo deep sleepの時はセンサをパワーダウンさせています。 以上の内容を組み込んだコードはGitHubにebook_turner_w2というリポジトリを作ってアップしておきました。
ebook_turner_w2.py がダブルタップを含むフルバージョンで、
ebook_turner_w2-woTap.py がセンサ機能を含まないものです。
もちろん開発は逆順にやったのですが、フルバージョンの開発途上で細かな見直しがいっぱい入って、センサ機能を含まない元のものが見るに堪えなくなってしまいました(汗
最終的な動作電流は、次の通りです:
起動時の電流はおそらくUSB動作のtyp.7.73mAで、約250msecで収まります。ライトスリープAでの差はセンサICのNormal mode消費電流(typ.0.085mA)が、ライトスリープBでの差は同ICのパワーダウンモード(typ.0.003mA)が、それぞれ見えているのだと思います。
また、BOMは次の通りで、合計3074円でした。タップ機能を使用せずSense無しのnRF52840にすると540円安く2534円になります。なお、電線・ハンダ・ラベル等の副材はカウントしていません:
小さくて低消費電力のBLEモジュールを使いたいけれど無い、というのが単なる見落としだったわけですが、その紆余曲折があったおかげでXIAOシリーズのありがたみを一層感じているところです。ただ、折角の低消費電力をより活かそうと思うとボタン電池で動かしたいところですが、現状では3V3端子に電池を繋ぐとシールドカバーの中に入っている3端子レギュレータの入力側がゼロVで出力側に電圧を印加することになり、レギュレータ側に電流が吸い込まれてしまいます。この辺がなんとかなればと思うのですが、既製品に細かなことを求めるのは贅沢かも知れませんね。
少々長くなりました。以上、何かの参考になれば幸いです。
パドラッパ from MacBook Air (2017)
ESP32などを調べていたときには既に秋月電子さんなどで併売されていたのを見つけられなかったことにちょっと落ち込んだりもしましたが、気を取り直して早速入手して遊び始めました。私の環境(macOS Catalina on MacBook Air 2017)ではArduinoのコンパイルや転送が一筋縄でいかなかったものの、CircuitPythonを試してみたところ各種の作例とともに開発環境のMuも素直に動きました。(ただ、人によってはMacでマウントされなくなることがたびたび起こるそうです。) 味見もできたところで何を作るかを考えました。5年ほどRaspberry Pi Zero W/WHのWi-Fiで運用している宅内環境センサをBLEに差し換えようかと思ったのですが、実験したところ距離が遠くて接続が安定せず諦めて、とりあえずページめくり機を作ってみました。まずは記念撮影。

左から、330円のリモートシャッター、最初の有線版、今回のもの、前回のESP32版です。

今回作ったものも中身はシンプルで、小さい方のMINTIAにちょうど収まりました。また、タクトスイッチの軸上に、べつやく れいさんのDPZ記事で知ったセメダイン社のBBXでキートップを貼るなどして、使い勝手がすこぶる良くなりました。

回路図も簡単です。なお、次の点は少々考えました:
・CircuitPythonで割り込みキーと値を読み取るキーを1つのピンに重ねて定義することができないため、2ピンをパラに繋いでそれぞれにキー定義しています
・DeepSleepにすると内部プルアップが維持できなくなるので、起動ピンは外部プルアップにしています
・Reader(& Kindle)とKinoppyのモード切替SWの場所が取れず、論理が以前と逆でプルアップにしています
・モード切替SWはKinoppy時にプルアップを常時Lowに引くため、内部プルアップ抵抗typ.13kではリークが0.25mAと大きいと思い、100kで外部プルアップしました(0.03mA)
・入力待ちのLEDを付けました。暗くていいので0.1mAしか流していません。
・回路図上のLi-Poは110mAhですが、その後ケースに合わせて600mAhに置き換えています
コーディングは2段階で行いました。
まず、従来Arduino言語で書いていたBLE版のコードをCircuitPython用に組み直してみて、そこからPythonらしく(よりPythonicに)書くことを心がけてみました。ごちゃごちゃっと手続きが並んでいたのが整理されていくのがPythonの楽しいところだと思います。また、CircuitPythonのスリープ割り込み手順が分かったので、従来キー読み取りループを常に回していたところがスマートにできました。また、BLEでConsumerControl-Powerのキーコード0x30をAndroid端末へ送るとスリープに落とせることが分かったので、BACKキーの長押しで端末とページめくり機が両方スリープするようにできました。
一方、前回は自前で組んでいたLi-Po電圧モニタのところが、XIAO nRF52840で組み込まれているものの、正しく電圧をモニタする手続きをしないとVBAT入力などのピンがnRF52840の絶対最大定格VDD+0.3Vを超えてしまうことに気が付きました。

これをTwitterで呟いていたところSeeed社の中の人に拾われて検証され、現状はよろしくないという話になっています。個人的には、VBATとポートの間に1MΩが入っているので大きな問題では無いと思っていたのですが、実際に電池を繋いでディープスリープに落とすとリーク電流が約2mAも流れたので、Li-Poに限らず3.6V以上の電池で駆動する場合は、
・真っ先にP0.14(CircuitPythonではboard.READ_BATT_ENABLE)をLowに落とす
・スリープは端子状態を制御できるライトスリープしか使わない(ちなみにライトスリープ時は0.28mA)
・電池に直接ハードSWを付けて使わないときは切り離す
ようにしました。結果として、今回のコードでは
A. BLE接続を維持して暗いLEDを付けキー読み取りを待つ
B. BLE接続を切ってLEDも消灯し充電状態と電源キーだけを監視する
の2種類のライトスリープを使い分けることにして、後者Bをスードディープスリープ(pseudo deep sleep)と勝手に呼んでいます。 なお、本来のディープスリープで2mA流れた原因はいまのところ不明ですが、端子状態が不定になって何らかのリークパスができたのかも知れませんし、私のが既に壊れているのかも知れません。ちなみに、ディープスリープに入る前のBLE接続状態によっても電流値は変動するようでした。 続いて、コーディングの第2段階です。
従来のページめくり機と同じ機能でしばらく使っていて、たまにブックマークを付けるときなどに画面をタップしてメニューを出すのを、内蔵センサの機能でできないかと思い立ちました。
あらためてLSM6DS3TR-Cのドキュメントからデータシート(リンク先PDF)を見ると、タップ検知機能というのがあるようです。
そもそも加速度センサを使ったタップ検知とは何か、ということはアナログデバイセズ社の代理店であるマクニカ社のチュートリアルが分かりやすかったです。
これを踏まえてLSM6DS3TR-Cのアプリケーションノート(リンク先PDF)とSeeed社のフォーラムにおけるQ&A(Arduinoに沿って交わされたもの)を参考に、センサの割り込みレジスタにアクセスするクラスを作成して、ダブルタップを安定して取得できるようになりました。また、(A)通常のライトスリープの時はダブルタップで起きてダブルタップを認識して画面タップ・メニュー出しをし、(B)pseudo deep sleepの時はセンサをパワーダウンさせています。 以上の内容を組み込んだコードはGitHubにebook_turner_w2というリポジトリを作ってアップしておきました。
ebook_turner_w2.py がダブルタップを含むフルバージョンで、
ebook_turner_w2-woTap.py がセンサ機能を含まないものです。
もちろん開発は逆順にやったのですが、フルバージョンの開発途上で細かな見直しがいっぱい入って、センサ機能を含まない元のものが見るに堪えなくなってしまいました(汗
最終的な動作電流は、次の通りです:
項目 | フル版 | 除センサ |
---|---|---|
起動時 | 約7mA | ← |
BLE送受信 | 約16mA | ← |
ライトスリープA | 0.46mA | 0.38mA |
ライトスリープB | 0.29mA | 0.28mA |
item | detail | price | pcs | subtotal |
---|---|---|---|---|
BLE module | Seeed Studio XIAO nRF52840 Sense | 2480 | 1 | 2480 |
Li-Po Batt. | 600mAh | 231 | 1 | 231 |
LED | OSG8HA3Z74A | 10 | 1 | 10 |
Substrate | D-type TH 0.3t | 60 | 1 | 60 |
Silent SW | TS8855SG-P2 | 22 | 3 | 66 |
Slide SW 1x2 | SS-12D00-G5 | 20 | 1 | 20 |
Slide SW 2x2 | IS-2235-G | 25 | 1 | 25 |
Resistor | 1/6W, 10kx1, 100kx2 | 1 | 3 | 3 |
Capacitor | MLCC 10μF | 50 | 1 | 50 |
Connector | PH-2 set | 21 | 1 | 21 |
Case | MINTIA | 108 | 1 | 108 |
| 固定リンク | 0
コメント