StackChanの頭上タッチが触っていないのに反応し続けたので物理キーに戻した
目次
2026-09-27 追記: 部屋の空気情報(CO2・温湿度)を音声チャットに渡す新APIの検証と、頭上タッチ撫で誤反応の調査、感情に合わせた首振り動作の続編 → StackChanにCO2センサーをつないで部屋の空気情報を音声チャットに反映させた
前回は、StackChanの音声チャットをESP-IDFでビルドし直し、Tailscaleで音声サーバーへ直結した。
話しかけるきっかけは頭上タッチの叩きで、起動直後は手を触れていないのに叩きと判定されるため、起動後しばらく叩きを受け付けない「準備中」の状態を足していた。
翌日、起動から約22時間たったStackChanが、勝手に録音して返事をしゃべった。
誰も触っておらず、ログも取っていなかった。
誤判定は起動直後だけではなかったので、どこで発生しているのか切り分けてみた。
検証環境
| 項目 | 使用した環境 |
|---|---|
| 実機 | M5Stack CoreS3(ESP32-S3)、StackChan Body(CoreS3を取り付けるロボット筐体) |
| 頭上タッチ | Body上面の3ゾーン静電タッチ(静電容量式タッチセンサICのSi12T、CoreS3の内部I2Cに接続) |
| 音声チャットのファーム | ESP-IDF 5.5.4、arduino-esp32 3.3.10、M5Unified 0.2.17、StackChan-BSP 1.1.0 |
| 比較した公式ファーム | m5stack/StackChan の firmware(ESP-IDF 5.5.4) |
| 置き場所 | 机の上。周りに触れるものはない |
録音が始まる経路は頭上タッチしかない
音声チャットのファームで録音を始めるのは、頭上タッチの叩きか、シリアルから送るテスト用のコマンドだけだった。
このときシリアルはつないでいない。
持ち上げて動かしても反応しないので、外から何かが触れている様子もなかった。
頭上タッチのチップが、触れていないのに「触れた」とCoreS3へ返していた。
叩きの判定は、3つのパッドの強さ(0〜3)の最大が3に届いたら、という条件にしていた。
接触ごとの状況を残す
強さ3の接触が出るたびに、時刻、起動からの経過、最後に顔を描き直してからの時間、首のサーボ・スピーカー・録音の状態、Wi-Fiの電波強度をログに出した。
| 項目 | 準備中の17回 | 準備中明けの14回 |
|---|---|---|
| サーボが動いている | 11回 | 5回 |
| スピーカー・録音 | すべて止まっている | すべて止まっている |
| 描き直しからの時間 | 42ms〜66秒でばらばら | ばらばら |
| 電波強度 | -55前後 | -55前後 |
起動後に首を動かすのは、ホーム位置へ戻す1回だけのはずで、サーボが「動いている」と返す理由は分からなかった。
ほかの項目とも、時間の重なりは確認できなかった。
2回叩きでも見分けられない
誤判定と本人の入力を分けるため、1.5秒以内に2回叩いたら録音を始めるようにした。
1回目のあとは画面に「もう1回」と出す。
準備中が明けてから1分半で、「2回叩き」が4回成立して、4回とも録音まで進んだ。
誤判定は1回ずつ離れて来るのではなく、短い間隔でまとまって来る。
触れていないのに画面に「もう1回」が出続け、実用にならない。
頭上タッチでは録音を始めず、記録だけにすると、約10分で接触504回、うち強さ3が257回だった。
公式ファームは叩きを使っていない
公式ファームの頭上タッチの処理(hal_head_touch.cpp、head_pet.h)を確認した。
| 項目 | 公式ファーム | 音声チャットのファーム |
|---|---|---|
| 反応する操作 | 撫でだけ。叩き1回には何も割り当てていない | 叩きで録音開始 |
| センサーの省電力スキャン | 有効 | 無効(軽い叩きを取りこぼすので切っていた) |
| 基準の取り直し | しない | Wi-Fi接続後、会話のあとなどで頻繁に |
| 読み取りの間隔 | 専用タスクで50msおき | メインのループで約10msおき |
撫では、3つのパッドの強さから重み付きの位置(-100〜100)を出し、触れ始めから40を超えて動いたら、という判定だった。
公式ドキュメントにも、上部のタッチ部分を縦にスワイプすると喜ぶとある。
感度(LOWのレベル3)と検出の設定は、こちらと同じだった。
Keyユニットで話しかける
叩く必要はないので、話しかけは物理スイッチに戻した。
以前使っていた1ボタンのボタンスイッチであるKeyユニット(U144)を、Body頭上のGroveポートに挿した。
StackChanのGroveポートは3つあり、公式の端子割り当て(PinMap)では PORT.A がG2/G1、PORT.B がG9/G8、PORT.C がG17/G18になっている。
本体横のPORT.AはCO2センサー用に空けておき、頭上のポートを使う。
以前のファームと同じく、Port B/Cの4ピンを見張って、最初に押されたピンをキーにする処理を戻すと、Port C(G18)と判定された。
| 区間 | 実測 |
|---|---|
| 録音(話し始めまで1.3秒) | 4,132ms |
| 送信(64,187バイト) | 295ms |
| 返事の感情が届くまで | 4,035ms |
| 1文目の音声のダウンロード(39,724バイト) | 186ms |
| 1文目の再生開始 | 7,869ms |
| 会話の終わりまで | 11,726ms |
頭上タッチの「2回叩き」の判定はこの間も出ていたが、記録だけにしていたため録音は始まらなかった。
ただ、頭上に挿したケーブルがまっすぐ上に出て、頭を撫でようとすると指に当たる。
L字のGroveケーブルは既製品で見つからず、「Grove L型」として売られているのは基板に付ける差し込み口だった(秋月電子、Seeed Studio)。
足元のUSB-CはCoreS3本体の給電とデータの端子なので、スイッチには回せなかった。
撫でだけ残しても誤判定が出る
叩きはやめて、撫でたときだけ喜ぶ顔を2.5秒出すようにした。
撫で判定でも誤判定が出るなら、タッチセンサー自体を使えない。
誰も撫でていないのに、約20分で撫での判定が40回以上出た。
この検証中、キーを押しても反応せず、顔が半目のまま固まる現象が起きた。
ログ収集用のPC側プログラムがシリアルポートを不正に閉じたことによるDTR/RTS信号の影響で、タッチセンサーの異常ではなかった。PC側のプログラムを直して検証を続けた。
公式と同じ設定と判定にする
省電力スキャンを有効に戻し、基準の取り直しをやめ、読み取りを50msおきにした。
それでも、会話のあとの約17分で、撫での判定26回、接触1,034回が出た。
撫での判定そのものも、公式のコードを移した。
すると、起動から約75秒で撫での判定が42回出て、ずっと喜んだ顔になった。
公式の判定は強さ1以上を接触とみなすので、触っていないのに各パッドが1〜2の値を出し続けると、重み付きの位置計算が変動し、そのまま撫での移動判定になっていた。
顔の反応は止めて、記録だけにした。
読み取るレジスタ、値の解釈、初期化の順序と値(感度、検出、省電力スキャンの設定)は、公式とすべて同じになっている。
公式ファームのタッチ処理だけを動かす
公式ファームをESP-IDF 5.5.4でビルドして書き込むと、「ID:(本体のMACアドレス)」「Look for me in the app to start setup.」の画面から進まなかった。
スマホの公式アプリでWi-Fiを設定するまで、撫でに反応する顔の画面は出なかった。
手元のコピーの main.cpp だけを書き換え、電源やI2C、頭上タッチの読み取りタスクまでは公式のまま起動し、撫でと接触の回数を1分ごとにログへ出すようにした。
書き込む前に、Tailscaleの鍵が入っている本体の設定領域(NVS: 不揮発性ストレージ)をバックアップした。
| 時間 | 接触 | 撫で | |
|---|---|---|---|
| 音声チャットのファーム(公式と同じ設定) | 約17分 | 1,034回 | 26回 |
| 公式のコード | 20分 | 0回 | 0回 |
0回が読み取れていないだけでないことを確かめるため、撫でてもらうと3回とも取れた。
叩きは7回中5回で、1回は撫でと判定された。
省電力スキャンでは読み取りが約200msおきになり、短い叩きは取りこぼしていた。
叩いた指がパッドの上でずれると、位置が動いて撫でになっていた。
公式が撫でにしか反応しないのも、このためかもしれない。
何を止めても消えない
音声チャットのファームのほうで、動いているものを1つずつ止め、1分ごとの接触回数を比べた。
| 止めたもの | 1分ごとの接触 |
|---|---|
| なし | 62、77 |
本体の更新処理(M5.update) | 53、27、48、11 |
| サーボの力 | 54、26、60 |
| Wi-Fiの送信出力(20dBm→2dBm) | 63、28、63 |
| スピーカー | 69、69、70 |
| 顔の描き直し | 75、68、64 |
I2Cの読み取りの失敗は0回で、チップが本当に「触れた」と返していた。
どれを止めても、その場では消えなかった。
誤判定には波があり、起動によって1分に55〜80回出るときも、最初から0回のときもあった。
ある起動では、約7分後から急に0回になり、止めたもの(バックライト)を戻しても0回のままだった。
公式ファームを一晩動かす
公式の20分で0回も、たまたま静かな時間に当たっただけかもしれない。
公式のタッチ処理だけを動かす版で、20:39から0:31まで、約3時間50分記録した。
| 時間 | 接触 | 撫で | |
|---|---|---|---|
| 公式のコード | 約3時間50分 | 4回 | 3回 |
| 音声チャットのファーム(多い時間帯) | 1分 | 数十回 | 十数回 |
公式でも、誰も触っていないのに1時間に1回ほど撫でが出た。
センサーは完全に静かになるわけではなく、音声チャットのファームでは、それが桁違いに増える時間帯があった。
何が増やしているのかは、まだ分かっていない。
話しかけはKeyユニットに任せ、頭上タッチは叩きも撫でも記録だけにしている。