ユーザ用ツール

差分

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

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
bleキーボードをつなごう_btstack編 [2025/04/22 02:06] – [Notificationの処理準備] arakibleキーボードをつなごう_btstack編 [2026/05/26 23:45] (現在) – [処理の流れ] araki
行 26: 行 26:
 言語は C++を使用する。 言語は C++を使用する。
  
 +なお、この文書やプログラムの作成に当たっては[[https://vanhunteradams.com/Pico/BLE/GATT_Client.html|Building a Bluetooth GATT Client on the Pi Pico W]]および[[https://github.com/bluekitchen/btstack/tree/501e6d2b86e6c92bfb9c390bcf55709938e25ac1|btstack-1.6.2]]のサンプルプログラムを参考にした。
 +
 +また、Copilot、Gemini、およびLM Studio上の Gemma3 12BなどAIによる支援も利用した。
 +
 +完全なコードは[[https://github.com/wildtree/HHSAdvPico.git|ハイハイスクールアドベンチャー PicoCalc版]]に含まれている。
 ===== 処理の流れ ===== ===== 処理の流れ =====
  
行 40: 行 45:
 この時、デバイスによっては接続を暗号化してセキュアにする必要がある場合がある。 この時、デバイスによっては接続を暗号化してセキュアにする必要がある場合がある。
 キーボードの場合は、その性質上打鍵情報を傍受される危険性があるためか、セキュアであることを要求しているものが少なくない。 キーボードの場合は、その性質上打鍵情報を傍受される危険性があるためか、セキュアであることを要求しているものが少なくない。
-手元にあるキーボードでは、バッファロー製のものと、Capdputerを購入したときに入っているサンプルのBLEキーボード化アプリがセキュア接続を要求しない((前者はセキュアでもOKだが後者はセキュア接続がそもそもできない))が、他はすべてセキュア接続を要求する。+手元にあるキーボードでは、バッファロー製のものと、Cardputerを購入したときに入っているサンプルのBLEキーボード化アプリがセキュア接続を要求しない((前者はセキュアでもOKだが後者はセキュア接続がそもそもできない))が、他はすべてセキュア接続を要求する。
 この要件は、キー入力を通知する Characteristic の属性に記されており、セキュア接続を必須とする場合に、セキュアでない接続上でキー入力のデータを要求しても要求が失敗する。 この要件は、キー入力を通知する Characteristic の属性に記されており、セキュア接続を必須とする場合に、セキュアでない接続上でキー入力のデータを要求しても要求が失敗する。
  
行 710: 行 715:
 ==== Notification handler ==== ==== Notification handler ====
  
 +ほぼ蛇足であるが、Notification handlerについても少しだけ記しておく。
 +Notificationについては、キーボードからの入力は 0x0040 のハンドルで渡されるようである。
 +メディアキーなどの特殊キーはさらに別のハンドルとなるがここでは割愛する。
 +
 +ほとんどのキーボードの場合はデータ長は 8bytes で先頭が modifierで、1byteのパディングがあって、6bytesのキーコードがわたされる。
 +
 +但し、バッファローのキーボードで11bytes (modifier + 10bytesのキーコード)というものがあったのでどちらでも扱えるようにデザインしてある。
 +
 +キーコードについては、キーボードごとに違いはないので、キーコードを文字にマッピングしてやって、keybuf というキューにデータを登録している。
 +
 +キーの状態に変化がないと Notificationは来ない。
 +キーが押しっぱなしでは状態が変化したわけではないので、押しっぱなしの通知は来ない。
 +通知がなければ、押しっぱなしとみることで、キーリピートやゲームでのキー処理にも対応できるが、ここではそのあたりの処理はしない。
 +
 +キーを押し続けているときに新しくキーが押されたらその分が新しく押されたキーコードとして渡されるが、その時、押しっぱなしになっているキーも渡されるので、差分を見なければ、新しく押されたキーを見つけることができない。
 +
 +そのため前回渡されたキーバッファの内容を保存している。
 +
 +押しっぱなしのキーが離されたときにも、通知が来るが、この時他に押しっぱなしのキーがあればそれも渡されるため、同様に、新しく押されたキーがあるかどうか、離されただけなのか、というチェックを前回のデータと比較して行う。
 +
 +キーボードについては概ねこのような処理を行っている。
 +
 +
 +<code cpp>
 +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<uint8_t> keybuf;
 +static const int MAX_KEYCODE = 96;
 +const uint8_t keymap[][MAX_KEYCODE] = {
 +  {    0,   0,   0,   0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
 +     'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
 +     '3', '4', '5', '6', '7', '8', '9', '0',  13,  27,   8,   9, ' ', '-', '=', '[',
 +     ']','\\',   0, ';','\'', '`', ',', '.', '/',   0,   0,   0,   0,   0,   0,   0,
 +       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 127,   0,   0,   0,
 +       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
 +  },
 +  {
 +       0,   0,   0,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,
 +      13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,   0,   0,
 +       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
 +       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
 +       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
 +       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
 +  },
 +  {
 +       0,   0,   0,   0, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
 +     'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
 +     '#', '$', '%', '^', '&', '*', '(', ')',  13,  27,   8,   9, ' ', '_', '+', '{',
 +     '}', '|',   0, ':', '"', '~', '<', '>', '?',   0,   0,   0,   0,   0,   0,   0,
 +       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 127,   0,   0,   0,
 +       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
 +  },
 +};
 +// 通知を受け取るハンドラ
 +static void 
 +notification_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) 
 +{
 +  if (packet_type != HCI_EVENT_PACKET) return;
 +  switch (hci_event_packet_get_type(packet)) 
 +  {
 +    case GATT_EVENT_NOTIFICATION:
 +      {
 +        // 通知を受信した場合の処理
 +        uint16_t attribute_handle = gatt_event_notification_get_handle(packet);
 +        uint16_t value_length = gatt_event_notification_get_value_length(packet);
 +        const uint8_t *value = gatt_event_notification_get_value(packet);
 +        uint16_t value_handle = gatt_event_notification_get_value_handle(packet);
 +        uint16_t service_id = gatt_event_notification_get_service_id(packet);
 +        if (attribute_handle == 0x0040)
 +        {
 +          if (value_length == 0) return; // データがない場合は無視
 +          if (value_length == 8 || value_length == 11)
 +          {
 +            keyboard_t *newKeyReport = (keyboard_t*)value;
 +            int buflen = 6;
 +            uint8_t *buf = keyboardReport.k2.keys;
 +            uint8_t *input = newKeyReport->k2.keys;
 +            uint8_t mod = newKeyReport->k2.modifiers;
 +            if (value_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 (mod == 3) mod = 1;
 +              uint8_t ch = keymap[mod][c];
 +              if (ch == 0) continue;
 +              if (memchr(buf, c, buflen) == NULL) keybuf.push(ch);
 +            }
 +            memcpy(&keyboardReport, value, value_length);
 +          }
 +        }
 +      }
 +      break;
 +    default:
 +      break;
 +  }
 +}
 +</code>
  
  

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also, you acknowledge that you have read and understand our Privacy Policy. If you do not agree, please leave the website.

More information