ユーザ用ツール

サイト用ツール


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

差分

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

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
ハイハイスクールアドベンチャー_m5stack_m5cardputer版 [2024/10/27 15:00] – [M5ATOM + 外部ディスプレイ] 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ターゲット}}
行 265: 行 266:
 手抜きの極みのキー処理である。 手抜きの極みのキー処理である。
 キーボードからの通知が来るたびに、これが呼び出される。 キーボードからの通知が来るたびに、これが呼び出される。
 +Handle == 41は modifiers + 10文字分のキーバッファだが Handle == 29は modifiers + res + 6文字分のキーバッファとなっている。
 +そもそも Handle で見分けるのが正しいのかさえよくわかっていないがとにかく試して動いたキーボードはこのどちらかだった。
  
-データは keyboardReport に格納され、あとで、BTKeyBoard::update()が呼び出されるときに処理される。+動作は単純で、前回渡されたデータと、今回渡ってきたデータとでキーバッファ部分を比べて、新しいのがあったらそれが今回入力されたキーだとみなして、keybufに積んでいく。 
 +これは、例えば、A, B, Cのキーを押しっぱなしにして、新しく D を押したときに、バッファには A, B, C, Dの四文字が入ってくるからである。古いバッファは A, B, Cのみだったので Dが追加で押されたことがわかる。 
 + 
 +データは keybufに格納され、あとで、BTKeyBoard::update()が呼び出されるときに処理される。
  
  
 <code> <code>
-typedef struct __attribute__((__packed__)) +typedef union { 
-+    struct __attribute__((__packed__)) 
-    uint8_t modifiers; +    
-    uint8_t keys[10];+        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; } keyboard_t;
 +
  
 static keyboard_t keyboardReport; static keyboard_t keyboardReport;
-static bool newKeyData = false;+static std::queue<uint16_t> keybuf;
  
 static void static void
行 283: 行 300:
 { {
     // handle: 41 -- key / 51 -- media key     // handle: 41 -- key / 51 -- media key
-    if (pRemoteCharacteristic->getHandle() == 41)+    switch (pRemoteCharacteristic->getHandle())
     {     {
-        keyboard_t *newKeyReport = (keyboard_t*)pData; +        case 22: 
-        for (int i = 0 ; i < 10 ; i++)+        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))
         {         {
-            uint8_t c = newKeyReport->keys[i]+            NimBLEDevice::deleteClient(pClient)
-            if (c == 0break+            Serial.println("Failed to connect."); 
-            if (memchr(&keyboardReport, c, 10) == NULL) keybuf.push((((uint16_t)newKeyReport->modifiers) << 8)|c);+            return false;
         }         }
-        memcpy(&keyboardReportpDatasizeof(keyboardReport)); +        if (!pClient->secureConnection()) 
-    }+        { 
 +            Serial.println("Failed to establish secure connection."); 
 +            return false; 
 +        } 
 +</code> 
 + 
 +そうそう。 
 +初期化もちょいと変えてやります。 
 + 
 +<code> 
 +    NimBLEDevice::init(""); 
 +    NimBLEDevice::setSecurityAuth(truetruetrue); // 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> </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なら飛ばすように変更しました。
 +
 +
 +
  
 ==== 画面のスケーリング ==== ==== 画面のスケーリング ====
ハイハイスクールアドベンチャー_m5stack_m5cardputer版.1730041259.txt.gz · 最終更新: 2024/10/27 15:00 by araki