ユーザ用ツール

サイト用ツール


ハイハイスクールアドベンチャー_m5stack_m5cardputer版

差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
ハイハイスクールアドベンチャー_m5stack_m5cardputer版 [2024/10/18 04:52] – [BTキーボードをつなごう] arakiハイハイスクールアドベンチャー_m5stack_m5cardputer版 [2024/12/09 02:30] (現在) – [BTキーボードをつなごう] araki
行 34: 行 34:
 作っている最中に M5Cardputerもリリースされたため、これにも対応を行った。 作っている最中に M5Cardputerもリリースされたため、これにも対応を行った。
  
-M5Stack Core (BASIC, Grey, Fire)および Core2、また M5Cardputerに対応しているが、M5Stack Core/Core2については FACESのキーボードモジュールが存在していることが前提である。 +M5Stack Core (BASIC, Grey, Fire)および Core2、また M5Cardputerに対応しているが、M5Stack Core/Core2については FACESのキーボードモジュールが存在している<del>ことが前提である</del>か、あるいはBLEキーボードが必要である。 
-入手困難なようなので、見かけたらゲットされたい。+FACESに関しては入手困難なようなので、見かけたらゲットされたい。
  
 また、データをmicroSDカードにストアして利用するためmicroSDカードも必須である。 また、データをmicroSDカードにストアして利用するためmicroSDカードも必須である。
行 57: 行 57:
 M5Stack Core2であっても M5Stack-grey でビルドしたバイナリを使用可能である。 M5Stack Core2であっても M5Stack-grey でビルドしたバイナリを使用可能である。
 M5 Cardputer用は M5Cardputerをターゲットとしてビルドしたものを使用する。 M5 Cardputer用は M5Cardputerをターゲットとしてビルドしたものを使用する。
 +Ver 1.6より m5atomExtDisplay というターゲットが追加されており、M5Atom + 240x240 SPI液晶モジュール + BLEキーボードでのプレイが可能になっている。M5Atom + SPI液晶に関してはしかるのちさん (@shikarunoci)からコードの提供をいただいた。
  
 {{::hhsadv:platformio_targets.png?400|PlatformIOターゲット}} {{::hhsadv:platformio_targets.png?400|PlatformIOターゲット}}
行 261: 行 262:
  
  
 +== 通知処理 ==
  
 +手抜きの極みのキー処理である。
 +キーボードからの通知が来るたびに、これが呼び出される。
 +Handle == 41は modifiers + 10文字分のキーバッファだが Handle == 29は modifiers + res + 6文字分のキーバッファとなっている。
 +そもそも Handle で見分けるのが正しいのかさえよくわかっていないがとにかく試して動いたキーボードはこのどちらかだった。
 +
 +動作は単純で、前回渡されたデータと、今回渡ってきたデータとでキーバッファ部分を比べて、新しいのがあったらそれが今回入力されたキーだとみなして、keybufに積んでいく。
 +これは、例えば、A, B, Cのキーを押しっぱなしにして、新しく D を押したときに、バッファには A, B, C, Dの四文字が入ってくるからである。古いバッファは A, B, Cのみだったので Dが追加で押されたことがわかる。
 +
 +データは keybufに格納され、あとで、BTKeyBoard::update()が呼び出されるときに処理される。
 +
 +
 +<code>
 +typedef union {
 +    struct __attribute__((__packed__))
 +    {
 +        uint8_t modifiers;
 +        uint8_t keys[10];
 +    } k1;
 +    struct __attribute__((__packed__))
 +    {
 +        uint8_t modifiers;
 +        uint8_t reserved;
 +        uint8_t keys[6];
 +        uint8_t padding[3];
 +    } k2;
 +    uint8_t raw[11];
 +} keyboard_t;
 +
 +
 +static keyboard_t keyboardReport;
 +static std::queue<uint16_t> keybuf;
 +
 +static void
 +notifyCallback(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)
 +{
 +    // handle: 41 -- key / 51 -- media key
 +    switch (pRemoteCharacteristic->getHandle())
 +    {
 +        case 22:
 +        case 29:
 +        case 41:
 +            if (length != 8 && length != 11) return; // not key data interface (maybe)
 +            keyboard_t *newKeyReport = (keyboard_t*)pData;
 +            int buflen = 6;
 +            uint8_t *buf = keyboardReport.k2.keys;
 +            uint8_t *input = newKeyReport->k2.keys;
 +            uint8_t mod = newKeyReport->k2.modifiers;
 +            if (length == 11)
 +            {
 +                buflen = 10;
 +                buf = keyboardReport.k1.keys;
 +                input = newKeyReport->k1.keys;
 +                mod = newKeyReport->k1.modifiers;     
 +            }
 +            for (int i = 0 ; i < buflen ; i++)
 +            {
 +                uint8_t c = input[i];
 +                if (c == 0) continue;
 +                if (memchr(buf, c, buflen) == NULL) keybuf.push(((uint16_t)mod << 8)|c);
 +            }
 +            memcpy(&keyboardReport, pData, length);
 +            break;
 +    }
 +}
 +</code>
 +
 +== つながるキーボードつながらないキーボード ==
 +
 +BLEキーボードっていっているのにつながるやつとつながらないやつがありまして、なんでだろうと思っていたのです。
 +思えば、最初にテストに使った、バッファローのBSKBB335がつながる方のやつだったので、深く考えないで、かつさっさと実装を終えられたのですが。
 +
 +それでもつながらないのがあるというのが気になります。
 +BLEの実装に関しては、ペリフェラル側((サーバー側))に多く、セントラル側((クライアント側))については、ほぼ、NimBLEのサンプルとその亜種しかないのです。
 +
 +ハイハイスクールアドベンチャーについても、もちろん、亜種の一つです。
 +
 +通知可能のCharacteristicは端から notfy()をかけてみても、まったく一つも callbackを返してきません。
 +悶々とします。
 +
 +で、gatttool を使ってもう少し掘ってみることにしました。
 +
 +<code>
 +$ gatttool -I -b xx:xx:xx:xx:xx:xx
 +...
 +[xx:xx:xx:xx:xx:xx][LE]> char-read-uuid 2a4d
 +Error: Read characteristics by UUID failed: Encryption required before read/write
 +[xx:xx:xx:xx:xx:xx][LE]>
 +</code>
 +
 +え、これって、セキュアな接続をはらないとダメってことですか?
 +
 +なんで、コードにごそごそと追加を行います。
 +
 +<code>
 +        pClient = NimBLEDevice::createClient();
 +        Serial.println("A new client created.");
 +        pClient->setClientCallbacks(new ClientCallbacks(), false);
 +        pClient->setConnectionParams(12, 12, 0, 51);
 +        pClient->setConnectTimeout(5); // 5sec
 +        if (!pClient->connect(advDevice))
 +        {
 +            NimBLEDevice::deleteClient(pClient);
 +            Serial.println("Failed to connect.");
 +            return false;
 +        }
 +        if (!pClient->secureConnection())
 +        {
 +            Serial.println("Failed to establish secure connection.");
 +            return false;
 +        }
 +</code>
 +
 +そうそう。
 +初期化もちょいと変えてやります。
 +
 +<code>
 +    NimBLEDevice::init("");
 +    NimBLEDevice::setSecurityAuth(true, true, true); // all true if requires secure connection.
 +    NimBLEDevice::setPower(ESP_PWR_LVL_P9);
 +    NimBLEScan *pScan = NimBLEDevice::getScan();
 +    pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
 +    pScan->setInterval(45);
 +    pScan->setWindow(15);
 +    pScan->setActiveScan(true);
 +    pScan->start(scanTime);
 +</code>
 +
 +NimBLEDevice::setSecurityAuth()は全部trueにしてやります。
 +これで無事につながらなかったキーボードがつながるようになりました。
 +
 +== キーボードが再接続できない ==
 +
 +BLEキーボードを接続した状態でしばらく何もしないで放置していると、キーボード側がパワーセーブモードに入って接続が切れます。
 +クライアント側には onDisconnect() の通知が送られて、接続が切れたことが認識されます。
 +
 +ここで、デバイスの再検索を始めて、打鍵するなどして復帰したキーボードから Advertiseがやってくればめでたく再接続になります。
 +
 +NimBLE のサンプルやそれを参考にした多くのコードが、次のような実装をしていたので私もそうしていました。
 +
 +<code>
 +void
 +ClientCallbacks::onDisconnect(NimBLEClient* pClient)
 +{
 +    Serial.print(pClient->getPeerAddress().toString().c_str());
 +    Serial.println(" disconnected - Starting scan");
 +    NimBLEDevice::getScan()->start(BTKeyBoard::scanTime);
 +}
 +</code>
 +
 +一見すると何の問題もありません。
 +実際、シリアルコンソールにも  disconnected - Starting scan と表示されて、いかにも動いているように見えますが実は全然動いていません。
 +一向にデバイスが再接続されないのです。
 +困った。
 +これでは、一気通貫にハイハイスクールアドベンチャーを最後までプレイするのでなければ、ゲームの途中でにっちもさっちもいかなくなってしまいます。
 +
 +さて、原因はなんでしょう?
 +もしかすると、一度接続したデバイスからの Advertiseを無視しているのかもしれません。
 +ならば <code>NimBLEDevice::getScan()->setDuplicateFilter(true);</code>とかすればいいのかもしれないと思って試してみました。
 +再接続はうまくいかないし、そもそも、最初の接続が完了するまで何度も Advertiseのコールバックがかかってきてしまいました。
 +筋違いのようです。
 +
 +じゃあ、なんなんだろう?
 +
 +ふと、callbackの中から NimBLEDevice::getScan()->start(0); とか呼んじゃダメなんじゃないか?ということに気づきました。
 +これ、つまりコールバック処理が完了しなくなって、おかしなことになっている可能性があるんじゃないかと。
 +
 +で、次のようにしました。
 +
 +<code>
 +static volatile bool connected = false;
 +
 +void
 +ClientCallbacks::onDisconnect(NimBLEClient* pClient)
 +{
 +    Serial.print(pClient->getPeerAddress().toString().c_str());
 +    Serial.println(" disconnected");
 +    connected = false;
 +}
 +</code>
 +
 +で、メイン処理の中で、
 +
 +<code>
 +    if (!connected)
 +    {
 +        Serial.println("Start scan.");
 +        NimBLEDevice::getScan()->start(scanTime);
 +    }
 +</code>
 +
 +という処理を足してやりました。
 +
 +結論。
 +
 +あっさり再接続しました_ノ乙(、ン、)_
 +
 +<code>
 +Connect to BLE Keybord.
 +Advertised HID Device found: Name: Notepad8, Address: XX:XX:XX:XX:XX:XX, appearance: 961, manufacturer data: YYYYYYYYYYYYYYYYYYYYYY, serviceUUID: 0x1812
 +Start connecting to server...
 +A new client created.
 +BLE Device connected.
 +Connected to XX:XX:XX:XX:XX:XX
 +RSSI: -60
 +Done.
 +XX:XX:XX:XX:XX:XX disconnected
 +Start scan.
 +Advertised HID Device found: Name: Notepad8, Address: XX:XX:XX:XX:XX:XX, appearance: 961, manufacturer data: YYYYYYYYYYYYYYYYYYYYYY, serviceUUID: 0x1812
 +Start connecting to server...
 +BLE Device connected.
 +Reconnected.
 +Connected to XX:XX:XX:XX:XX:XX
 +RSSI: -62
 +Done.
 +</code>
 +
 +== キーバッファ ==
 +
 +キーバッファの仕様は何種類か存在しているようである。
 +
 +最も多い実装は8bytesで、modifiers + padding + key buffer (6bytes) という構成で、手元ではバッファローのBSKBB335だけが11bytes で modifiers + key buffer (10bytes)というものである。
 +
 +キーバッファのサイズだとか、多分アラインメントを考慮したであろうパディングの有無や全体長などの差異はあれど、渡ってくる情報(modifiersやキーコード)に違いはない。
 +
 +が、キーバッファーの部分の扱いには、解釈違いのものがあるようである。
 +
 +当初、試したキーボード((BSKBB335/NimBLE版 Cardputer BT キーボード))では、キーが押されたときには先頭からキーコードが入って送られてきた。
 +なので、常にキーが押されればキーバッファの先頭にはキーコードがあるという想定だった。
 +
 +他のキーボードでもそれで問題は特にはなかった。
 +
 +が、ちょっと色気(?)を出して、シフトキーを押したりコントロールキーを押したりしたときにそれは起きた。
 +
 +文字が入力されない。
 +
 +仕方がないので、デバッグです。
 +調べてみたら、キーは渡ってきてます。
 +但し、なぜか二文字目で先頭は0です。
 +どうやら、キーボードによっては、modifiers が押されたのもキーバッファを消費する((ただしよりにもよってキーコードは0))ようです。
 +なので、0ならそこで打ち切りとしていた処理を、0なら飛ばすように変更しました。
 +
 +
 +
 +
 +==== 画面のスケーリング ====
 +
 +Cardputerは別として、基本的に、M5シリーズ向けハイハイスクールアドベンチャーは320x240の表示装置を前提にデザインしている。
 +が、しかるのちさんは M5Atomに 240x240のパネルをつないでミニチュアをつくってらっしゃる。
 +
 +このため表示装置にあわせたスケーリングを行いたい。
 +
 +基本的に、M5GFXライブラリは、スプライトやバッファを使った画像データをアフィン変換して表示する機能があるので、これを使えばスケーリングできる。
 +
 +が、問題は、画像バッファはメモリを食うということだ。
 +
 +最初、ハイハイスクールアドベンチャーはRGB565の256x152のバッファをもって、ここに描画をして表示していた。ほかの部分についてはダイレクトに画面に書き込んで、画面のデータを使って、文字のスクロールなどを実装していた。利点はメモリを使わないで済むこと。
 +
 +ところがスケーリングをするためにはバッファを持たなければならない。
 +
 +単純に、文字表示領域を片っ端から createSprite()を使ってバッファリングしていくとあっという間にヒープを使い切ってしまい動作しなくなった。
 +
 +そこで、画像データをRGB332にしてバッファを半分にし、各スプライトも M5Canvas::setColorDepth(8)でRGB332にしてけちけち実装した。
 +
 +最後に、ダイアログを同じように8bitのバッファにしたら、ダイアログが表示されなくなってしまった。
 +どうやら、ヒープ不足でバッファをとれない様子。
 +
 +もともとダイアログは白黒((一部無効化しているボタンはグレー表示))なので、思い切ってM5Canvas::setColorDepth(1)にしたらあっさり動いた。グレーは白になってしまい全く表示されなくなったがまあご愛敬、ということで。
 +
 +=== 副作用 ===
 +
 +画像用のバッファをRGB332にしたために、幽霊先生のジャケットがMAROONからピンクになってしまった、まるで第二期から第三期に代わったときのルパン三世のように。
 +
 +これは、ペイント処理の関係で、RGB565-->RGB332-->RGB565と変換して、もとと同じ値に戻る組み合わせでない色はペイントに使えなくなってしまったためだ。この条件を満たしていないと、ペイント処理で境界色や塗った領域の検出ができなくなり移乗動作してしまうためである。
 +
 +==== M5ATOM + 外部ディスプレイ ====
 +
 +{{::hhsadv:m5atom_extlcddisplay.jpg?400|}}
 +
 +しかるのちさん(@shikarunoci)から M5ATOMに、240x240のSPI液晶モジュールを接続して遊ぶためのソースの変更分をいただき、これを本体にマージしました。
 +
 +液晶は、SPI接続で、env:m5atomExtDisplay でビルドする。
 +接続は次の通り。
 +
 +^液晶^M5ATOM^
 +|VCC|3V3|
 +|GND|GND|
 +|SLC|G23|
 +|SDA|G33|
 +|RES|G19|
 +|DC|G22|
 +|CS|-|
 +
 +データファイルはPlatformIOのUpload Filesystem Image メニューでSPIFFSに転送しておく。
 +この時、ビルド環境をARM64上に構築していると、mkspiffs コマンドがないため失敗する。
 +その場合自力でビルドして置き換えておく。
 +
 +<code>
 +$ git clone --recursive https://github.com/igrr/mkspiffs.git
 +$ cd mkspiffs
 +$ make clean
 +$ make dist BUILD_CONFIG_NAME="-arduino-esp32" CPPFLAGS="-DSPIFFS_OBJ_META_LEN=4"
 +$ cp mkspiffs ~/.platformio/packages/tool-mkspiffs/mkspiffs_espressif32_arduino
 +</code>
  
  
ハイハイスクールアドベンチャー_m5stack_m5cardputer版.1729227139.txt.gz · 最終更新: 2024/10/18 04:52 by araki