ハイハイスクールアドベンチャー_raspberry_pico_lcd版
差分
このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン前のリビジョン次のリビジョン | 前のリビジョン | ||
ハイハイスクールアドベンチャー_raspberry_pico_lcd版 [2025/04/14 01:02] – araki | ハイハイスクールアドベンチャー_raspberry_pico_lcd版 [2025/04/17 23:26] (現在) – [USBキーボード] araki | ||
---|---|---|---|
行 19: | 行 19: | ||
STT7789 という、よくあるコントローラで 240x320の液晶(タッチパネル/ | STT7789 という、よくあるコントローラで 240x320の液晶(タッチパネル/ | ||
[[https:// | [[https:// | ||
+ | |||
+ | ちなみにピン配置は以下の通り。 | ||
+ | |||
+ | ^名前^番号^ | ||
+ | |LCD_SLCK/ | ||
+ | |LCD_MOSI/ | ||
+ | |LCD_MISO/ | ||
+ | |LCD_BL|13| | ||
+ | |LCD_DC|14| | ||
+ | |LCD_RST|15| | ||
+ | |SD_CS|22| | ||
+ | |||
+ | ちなみに、液晶パネルとSDカードはSPI1を共有しています((なので一部ピンが共用されている。)) | ||
原因はバックライトのコントロールにありました。 | 原因はバックライトのコントロールにありました。 | ||
行 27: | 行 40: | ||
まあ、描画はされていたけれど、バックライトが真っ暗で何も見えていなかっただけというのが実際だったわけです。 | まあ、描画はされていたけれど、バックライトが真っ暗で何も見えていなかっただけというのが実際だったわけです。 | ||
- | < | + | < |
namespace lgfx | namespace lgfx | ||
{ | { | ||
行 60: | 行 73: | ||
</ | </ | ||
- | < | + | < |
namespace lgfx | namespace lgfx | ||
{ | { | ||
行 94: | 行 107: | ||
</ | </ | ||
+ | SPI1を共用しているので、LCD/ | ||
+ | LCDを先に初期化している前提で話しますが、この場合SDカードインターフェイスの初期化時に SPI1.setCS(22)のように、CSピンをセットしに行くと固まってしまいます。 | ||
+ | SDカードのCSピンは SD.begin(22, | ||
==== USBキーボード ==== | ==== USBキーボード ==== | ||
行 110: | 行 126: | ||
=== 間違ったWEAK宣言の呪縛 === | === 間違ったWEAK宣言の呪縛 === | ||
- | コンパイラというかリンカーへの指示として、WEAKシンボルというのが太鼓の昔から存在しています。 | + | コンパイラというかリンカーへの指示として、WEAKシンボルというのが太古の昔から存在しています。 |
何かというと、そのシンボルはリンク時に他にWEAKではない同名のシンボルがあればそれで置き換えるというものです。 | 何かというと、そのシンボルはリンク時に他にWEAKではない同名のシンボルがあればそれで置き換えるというものです。 | ||
普通同じシンボルが別々に実体をもっていたらリンク時に重複エラーになるんですが、WEAKがついていれば、それが起きません。 | 普通同じシンボルが別々に実体をもっていたらリンク時に重複エラーになるんですが、WEAKがついていれば、それが起きません。 | ||
行 137: | 行 153: | ||
ちょっとWeb界隈を漁ると、同じ現象で困っている人もいらっしゃる。 | ちょっとWeb界隈を漁ると、同じ現象で困っている人もいらっしゃる。 | ||
- | で、さらに調べていったら、**ヘッダファイルの中でコールバックのテンプレート宣言に__attribute((weak))__つけてる**じゃありませんか! | + | で、さらに調べていったら、**ヘッダファイルの中でコールバックのテンプレート宣言に**<code cpp> |
ダメ、絶対! | ダメ、絶対! | ||
WEAK属性は実装の方にだけつけて宣言につけちゃダメ! | WEAK属性は実装の方にだけつけて宣言につけちゃダメ! | ||
行 152: | 行 168: | ||
それが usbkbd.cppなのです。 | それが usbkbd.cppなのです。 | ||
- | < | + | < |
// Tiny USB HID callback entry points to avoid wrong weak attribute usabe in Adafruit TinyUSB | // Tiny USB HID callback entry points to avoid wrong weak attribute usabe in Adafruit TinyUSB | ||
行 206: | 行 222: | ||
tuh_hid_report_received_cb()の最後に次のコールバックを要求する処理が入っているのです。 | tuh_hid_report_received_cb()の最後に次のコールバックを要求する処理が入っているのです。 | ||
- | < | + | < |
if (!tuh_hid_received_report(dev_addr, | if (!tuh_hid_received_report(dev_addr, | ||
{ | { | ||
行 217: | 行 233: | ||
されどこれだけ。 | されどこれだけ。 | ||
+ | === キーマッピング === | ||
+ | |||
+ | US配列のキーボードがターゲットなのは、Keyboard.cpp内の keycode2asciiという配列がそうなっているからです。 | ||
+ | HID_KEYCODE_TO_ASCIIというTinyUSB由来のマッピングを横着して使っているのでこうなっています。 | ||
+ | |||
+ | これを、JISキーボードにあうように直せばJISキーボードでも使えます。 | ||
+ | |||
+ | <code cpp> | ||
+ | static uint8_t const keycode2ascii[128][2] = { HID_KEYCODE_TO_ASCII }; | ||
+ | </ | ||
==== BLEキーボード ==== | ==== BLEキーボード ==== | ||
行 270: | 行 296: | ||
わたしの手元にあるバッファローのBLEキーボードとM5 CardputerのおまけでついてくるBLEキーボードだけがセキュア接続でなくてもつながるキーボードなので、つまりは、大体の場合セキュアじゃないとダメなのです。((ちなみにCardputerのそれはセキュアでは接続できません。バッファローはセキュアでもOK)) | わたしの手元にあるバッファローのBLEキーボードとM5 CardputerのおまけでついてくるBLEキーボードだけがセキュア接続でなくてもつながるキーボードなので、つまりは、大体の場合セキュアじゃないとダメなのです。((ちなみにCardputerのそれはセキュアでは接続できません。バッファローはセキュアでもOK)) | ||
- | さて、世間には、実は Pico W + btstack | + | とりあえず、ボンディングは行わないで単純にコネクションだけをセキュアにできればいいので、そのように設定をします。 |
+ | <code cpp> | ||
+ | sm_init(); | ||
+ | sm_set_request_security(true); | ||
+ | sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); | ||
+ | sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION); | ||
+ | sm_set_encryption_key_size_range(7, | ||
- | まあ、セキュアじゃなくてもつながるキーボードなら多分つながるのでそれでよしとするというのもありでしょうけれど、なんかあとちょっとの工夫で動くんじゃないかっていう気がするので、あきらめがつきません。 | + | sm_event_callback_registration.callback = & |
+ | sm_add_event_handler(& | ||
+ | </ | ||
+ | |||
+ | IO_CAPABILITY_NO_INPUT_NO_OUTPUTとSM_AUtHREQ_SECURE_CONNECTIONのみがセットされている場合、Just in Workでの接続が行われるようです。なので、sm_event_handler側でそれを処理すればいいようです。 | ||
+ | |||
+ | <code cpp> | ||
+ | static void | ||
+ | sm_event_handler(uint8_t packet_type, | ||
+ | if (packet_type != HCI_EVENT_PACKET) return; | ||
+ | |||
+ | switch (hci_event_packet_get_type(packet)) | ||
+ | { | ||
+ | case SM_EVENT_JUST_WORKS_REQUEST: | ||
+ | { | ||
+ | // Just Works Confirmを自動的に承認 | ||
+ | bd_addr_t addr; | ||
+ | sm_event_just_works_request_get_address(packet, | ||
+ | Serial.printf(" | ||
+ | sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); | ||
+ | break; | ||
+ | } | ||
+ | case SM_EVENT_PAIRING_STARTED: | ||
+ | Serial.println(" | ||
+ | break; | ||
+ | case SM_EVENT_PAIRING_COMPLETE: | ||
+ | { | ||
+ | uint8_t status = sm_event_pairing_complete_get_status(packet); | ||
+ | if (status == ERROR_CODE_SUCCESS) | ||
+ | { | ||
+ | Serial.println(" | ||
+ | hci_con_handle_t connection_handle = sm_event_pairing_complete_get_handle(packet); | ||
+ | uint16_t hids_cid; | ||
+ | イスに接続 | ||
+ | gatt_client_discover_primary_services_by_uuid16(& | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | Serial.printf(" | ||
+ | } | ||
+ | break; | ||
+ | } | ||
+ | case SM_EVENT_PASSKEY_DISPLAY_NUMBER: | ||
+ | // パスキー表示が必要な場合の処理 (今回は No Input No Output | ||
+ | display.printf(" | ||
+ | break; | ||
+ | case SM_EVENT_REENCRYPTION_STARTED: | ||
+ | { | ||
+ | // 再暗号化が開始された場合の処理 | ||
+ | bd_addr_t addr; | ||
+ | sm_event_reencryption_started_get_address(packet, | ||
+ | uint8_t addr_type = sm_event_reencryption_started_get_addr_type(packet); | ||
+ | Serial.printf(" | ||
+ | break; | ||
+ | } | ||
+ | case SM_EVENT_REENCRYPTION_COMPLETE: | ||
+ | { | ||
+ | // 再暗号化が完了した場合の処理 | ||
+ | uint8_t status = sm_event_reencryption_complete_get_status(packet); | ||
+ | if(status == ERROR_CODE_SUCCESS) { | ||
+ | Serial.println(" | ||
+ | } else { | ||
+ | Serial.printf(" | ||
+ | } | ||
+ | break; | ||
+ | } | ||
+ | default: | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | あとは接続時にセキュア接続を要求するだけです。 | ||
+ | |||
+ | <code cpp> | ||
+ | case HCI_EVENT_LE_META: | ||
+ | if (hci_event_le_meta_get_subevent_code(packet) == HCI_SUBEVENT_LE_CONNECTION_COMPLETE) | ||
+ | { | ||
+ | // 暗号化を有効化 | ||
+ | hci_con_handle_t connection_handle = gap_subevent_le_connection_complete_get_connection_handle(packet); | ||
+ | gap_request_security_level(connection_handle, | ||
+ | sm_request_pairing(connection_handle); | ||
+ | // | ||
+ | Serial.printf(" | ||
+ | } | ||
+ | break; | ||
+ | </ | ||
- | 識者の見解を待ちます! | + | これでコネクションはセキュアになりました。 |
+ | < | ||
+ | ble/ | ||
+ | ついに、文字が拾えるようになりました。 | ||
+ | < | ||
+ | ポイントは、Notificationを受け取りたいCharacteristicに対して、ディスクリプタを検索してCCCDを見つけたら、Notifyを送信するように要求する((0x0001 -- little endian なら {0x01, 0x00}を送信する。))のと、最後に gatt_client_listen_for_charcteristic_value_updates()でGATT_EVENT_NOTIFICATIONを受け取るようにすることの両方。 | ||
+ | 更に gatt_client_listen_for_characteristic_value_updates()のcharacteristicにはnullptrを渡して一括でやる方がいいということですかね。((サンプルに従って notification_listenerを一つだけ作っているので、個別の characteristicに対してやりたいなら、多分これもcharacteristicごとにわけないといけないんだと思いますが、一括で動いているのでよしとします。)) | ||
ハイハイスクールアドベンチャー_raspberry_pico_lcd版.1744592563.txt.gz · 最終更新: 2025/04/14 01:02 by araki