LoginSignup
1
0

6キー片手キーボードをカスタマイズする(調査編)

Last updated at Posted at 2023-06-14

はじめに

この手のデバイスは自作しているので1 2、改めてこの製品が欲しいとは思いませんでしたが、たまたまAmazonで安く売られていて、レビューにある「怪しい設定ソフト」に興味を持ち、Mac用の設定ソフト3でも自作しようかと考えて購入した次第。
購入時の価格;税込¥3,481.-送料無料

sikaicase6key.jpg amazon.png

・ 前置き

他者のレビューやAmazonの商品説明によると「設定ソフト」は、Googleドライブからダウンロードすることになっているが、届いた商品に同梱されていた「ユーザーマニュアル」には、QRコードがあり、『スキャンして設定ソフトをダウンロードする』と記載されていた。実際にスキャンすると説明ページ(設定ソフトのダウンロードおよび使い方)が用意されていた。日本語ページはこちら

sikicase_site.png

しかし、Edgeでダウンロードすると、セキュリティではじかれ「未確認.download」となるので、自己責任で元々のファイル名にリネーム。
また、ダウンロードファイルは拡張子が.rarのため、Windows標準機能では解凍できない。

分解

4本のネジを外し、キーキャップ・キースイッチ・つまみを抜くと、基板が取り出せる。

おもて IMG_1971.png
Layer切替プッシュスイッチ、Bluetoothオン/オフスライドスイッチ、ロータリーエンコーダ、RGBLEDx10
うら IMG_1972.png
USB-C2.0ソケット、キーソケットx6、リチウムイオン電池、電子部品、基板右上にBluetoothのアンテナパターン

元々、赤軸のキースイッチが付いていたが、ソケット式のため自分で好みのキースイッチに交換も可能。

マイコン.png ch573.png

マイコンと思われるICは、表面が削られており型番は不明であるが、『32ピン、4ミリ四方、USB、Bluetooth』の情報検索から、CH573Xであると思われる。このマイコンは、クリスタル(水晶発振器)こそ外付けだが、4ミリ四方の1チップに、USB・BLE・PWN・UART・ADC・GPIOx22本を有し、5V→3.3Vレギュレータも内蔵しており、USB電源から直接3.3V MCUを駆動できる優れものである。詳しくは「データシート(CH573DS1.PDF)」参照。

IMG_1974.png

3.7V 200mAh仕様のリチウムイオンポリマー電池は、USBケーブルでPCに接続すると充電されるようだ。Bluetooth接続時はワイヤレスで使用できるが、バッテリ駆動時はLEDが一切点灯しない仕様。(モバイルバッテリー接続時はLEDは点灯する)

crystal.png transistor.png

抵抗とコンデンサを除くと、残りの電子部品は、クリスタルとトランジスタが2個だけ。

トランジスタは表面の文字から、Pch-MOSFET(A19T)とNPN型トランジスタ(1AM)で、使われてないスルーホールがすぐ横に見えるので、外部電源か何かの拡張用か?


カスタマイズ方針

購入当初は、キースイッチ、ロータリーエンコーダとRGBLEDの各信号が接続されているマイコンのGPIO端子を調べ、マイコンのプログラムを直接書き換えてカスタマイズする方法を考えていた。しかし、分解してみると自分が扱ったことが無いマイコンであり、かつ、このマイコン用の開発環境を準備することが簡単ではないため、方針を変更する。

設定ソフトがこのデバイスとやりとりしている内容を解析し、このデバイスの設定インタフェースを模すことで、Macから設定できるアプリを作る方針とする。

メーカーが提供している設定ソフトはWindowsでしか動かないため、WindowsでUSBの通信データをキャプチャするソフトを使い解析する。


2024.2.22追記
先日日本語サイトを見てみたら、Mac用設定アプリが追加されていた。

設定インタフェースの確認

準備

  1. USB通信をキャプチャするため、Windows10にWiresharkUSBPcapをインストールする。Wiresharkを管理者権限で起動しないと、USBがインタフェースに現れません(インストールや使い方の説明は、他の記事に譲る)

  2. PC環境に依存すると思われるが、自分のPCはUSBPcap3が当デバイスの接続先(これを探すのに苦労した)

  3. 一瞬にして何十万ものキャプチャデータが出てくるため、フィルタにusb.bInterfaceClass == 0x03を指定する。0x03はHIDデバイスを示す

解析結果

キャプチャしたHIDレポートディスクリプタを解析すると、1つのキーの設定につき、03fdから始まる65バイトの固定長データが2組、デバイスに送信されることが分かった。

下記は、Layer3のKey2に2つのキーコードを割り当てた例である。
cap1.png

1stブロック

  • 1,2バイト目:03fd固定
  • 3バイト目:Key番号(1〜6、0x10〜0x12)(例では2)
  • 4バイト目:Layer番号(1〜3)(例では3)
  • 5バイト目:データ種別(例では0x01)
  • 11バイト目:後ろにつづくデータの個数(0〜)(例では2)
  • 12バイト目以降:2バイトで1組のキーコード情報
    上位バイト:キー モディファイア マスク(後述)
    下位バイト:キーコード(後述)

2ndブロック

  • 1,2バイト目:03fd固定
  • 3,4バイト目:feff固定

1) Key番号

当デバイスには、6個のキーと、1個のノブ(ロータリエンコーダ)があり、Key番号は以下の通り。

キー Key番号 ノブ Key番号
キー1 0x01 左回転 0x10
キー2 0x02 押し込み 0x11
右回転 0x12
キー6 0x06

2) キーコードおよびキー モディファイア マスク

  • キーコード(スキャンコード)
    A:0x4, B:0x5,・・・のコードで『HID Usage Tables 1.4 - §10 Keyboard/Keypad Page (0x07)』にUsage IDとして定義されている。

  • キー モディファイア マスク
    複数のキーで修飾する場合は、下記マスク値の論理和を指定する。

キー モディファイア マスク値
left-Control 0x01
left-Shift 0x02
left-Alt/option 0x04
left-Win/command 0x08
right-Control 0x10
right-Shift 0x20
right-Alt/option 0x40
right-Win/command 0x80

例 Alt+Ctrl+Del:054c(モディファイアマスク:0x05、キーコード:0x4c)

ここで指定するキーコードは、OSに設定しているキーボード種別と密接に関係する。
例えば、日本語キーボードは、数字2キーのシフト時は"(ダブルクォーテーション)だが、英語キーボードだと数字2キーのシフト時は@(アットマーク)だ。
このように、日本語と英語キーボードでは、記号文字のキー配列が異なるため、どのOSでもPCに接続しているキーボード種別を設定するようになっている。4 5

この設定アプリ英語キーボード基準で作成されているため、OSに日本語キーボードが設定されていると 期待通りの文字がタイプされない現象が発生する。具体的には、@をタイプしたいため、設定ソフトでShift+2@を割り当てたとする。しかし。前述の理由により"がタイプされる。
対応策としては、『打ちたい文字がある日本語キーボードのキートップの位置にあたる英語キーボードのキートップの文字』を指定する必要がある。

Macで設定アプリを作成する場合は、こういった面倒な変換は不要な仕様にするつもりである。6

3) 1つのキーに何個のキーコードを指定できるか?

例えば、あるキーを押した時に、"Hello World!"とタイプしたい場合は、
Shift+H E L L O space Shift+W O R L D Shift+1 の12個のデータを順にセットする。
データ構造上は、最大27個のデータを指定できるはずだが、実際に設定アプリでAZ,09の36文字をセットすると、下記のデータとなった。

x36.png

個数は、36(0x24)であるが、データがATまでの20個しかない。当初は、26個を超える場合は、複数ブロックに分割されるのかと想定したが、まさか20個で打ち切られるとはまったくの想定外。個数とデータ数に矛盾があり、設定アプリのバグも疑われる。
Mac用設定アプリが作成できたら、20個を超えるデータをセットしてみて、実際のデバイスの動作を検証する予定。 検証結果はこちら

4) RGBLED設定時のデータ

Layerごとに、Mode0Mode5の6パターンと7色の組み合わせでRGBLEDの光らせ方を指定できる。
RGBLED.png

下記は、Layer3に、Mode3Greenを割り当てた例である。
led_data.png

  • 1〜3バイト目:03 fe b0固定
  • 4バイト目:Layer番号(1〜3)(例では3)
  • 5バイト目:08固定(データ種別)
  • 11バイト目:01固定(後ろにつづくデータの個数=1)
  • 13バイト目:上4ビット色情報+下4ビットMode情報
    上4ビット:Red〜Purpleが1〜7に対応
    下4ビット:Mode0〜Mode5が0〜5に対応(Mode0は消灯指定でColorは無視)

5) Mouse設定時のデータ

キー入力と同様にマウスのボタン操作も設定できる。
mouse_spec.png

以下は、Layer1、Key1にCtrl+Mouse Downを割り当てたときのデータ。
mouse.png

  • 1〜2バイト目:03fd固定
  • 3バイト目:Key番号(1〜6、0x10〜0x12)(例では1)
  • 4バイト目:Layer番号(1〜3)(例では1)
  • 5バイト目:03固定(データ種別)
  • 11バイト目:01固定(後ろにつづくデータの個数=1)
  • 12、13、16バイト目:下表に示すデータ(空欄は0x00)
設定値 12 13 16
Mouse LetfKey 0x01 マウス左ボタンクリック
Mouse Middle 0x04 マウス中央ボタンクリック
Mouse Right 0x02 マウス右ボタンクリック
Mouse Wheel+ 0x01 マウスホイール下回転
Mouse Wheel- 0xFF マウスホイール上回転
Ctrl+Mouse Up 0x01 0x01 Ctrl+マウス左ボタンup
Ctrl+Mouse Down 0x01 0xFF Ctrl+マウス左ボタンdown
Shift+Mouse Up 0x02 0x01 Shift+マウス左ボタンup
Shift+Mouse Down 0x02 0xFF Shift+マウス左ボタンdown
Alt+Mouse Up 0x04 0x01 Shift+マウス左ボタンup
Alt+Mouse Down 0x04 0xFF Shift+マウス左ボタンdown

12バイト目はキー モディファイア マスクと思われるので、論理和により複数のキーによる修飾も可能か? また、マウスボタンクリックにも修飾できるのか不明。
整然としたパターンになっておらず、マウスホイールのデータを識別する方法に疑問の残る。
また、マウス操作においては、1つのキーに複数の操作を設定できないようである。

6) Multimedia設定時のデータ

multimedia.png

以下は、Layer1、Key1にPlay/Pauseを割り当てたときのデータ。
media_data.png

  • 1〜2バイト目:03fd固定
  • 3バイト目:Key番号(1〜6、0x10〜0x12)(例では1)
  • 4バイト目:Layer番号(1〜3)(例では1)
  • 5バイト目:02固定(データ種別)
  • 11バイト目:02固定(後ろにつづくデータの個数=2)
  • 12、13バイト目:下表に示すデータ(空欄は0x00)
設定値 12 13 備考
Play/Pause 0xcd
Stop 0xb7
Previous track 0xb6
Next track 0xb5
My Computer 0x94 0x01
Screen britness+ 0x6f
Screen britness- 0x70
Multimedia 0x83 0x01 Windows Media Playerが開いた
Mute 0xe2
Volume+ 0xe9
Volume- 0xea
Calculator 0x92 0x01 電卓が開いた
WWW home 0x23 0x02 Edgeが開いた
E-mail 0x8a 0x01
Bass+ 0x52 0x01
Bass- 0x53 0x01
Treble+ 0x54 0x01
Treble- 0x55 0x01
WWW Pagerefresh 0x27 0x02
WWW Pageforward 0x25 0x02
WWW Pageback 0x24 0x02

MacAppコマンドライン版インタフェース確認のみ

コマンドライン版MacAPPを作成したが、インタフェース確認が目的のため、Layer、Key、キーコードなどは、ハードコーディングしている。XcodeでCommand Line Toolを選択し、 C言語インタフェースのlibUSBを使用するため、Project-Bridging-Headerが必要で、Appの実行にはスーパーユーザ権限が必要である。

Project-Bridging-Headerのコードを表示
Project-Bridging-Header.h
#include <libusb.h>
#include <string.h>
#include <errno.h>

//SwiftからC言語の可変長引数の関数が呼び出せないため、デバックログレベル設定専用のブリッジ関数を用意
int libusb_set_option_debug(libusb_context *ctx, enum libusb_option option1, enum libusb_log_level option2) {
    return libusb_set_option(ctx, option1, option2);
}

以下は、Layer1、Key1に、"aB"を割り当てる確認プログラム。

mainのコードを表示
main.swift
//
//  main.swift
//  6key_setting_test
//

import Foundation

func asciiCStringToString(_ cString: [CChar]) -> String {
    var result = ""
    for char in cString {
        if char == 0 { return result }
        result += String(Character(UnicodeScalar(UInt8(char))))
    }
    return result
}

func hexDump(_ cString: [UInt8], length: Int) {
    var buffer = ""
    for offset in stride(from: 0, to: length, by: 1) {
        buffer += String(format: "%@%02x ", offset > 0 && offset.isMultiple(of: 16) ? "\n" : "", cString[offset])
    }
    print(buffer)
}

let VENDOR_ID: UInt16  = 0x1189
let PRODUCT_ID: UInt16 = 0x8840
let INTERFACE: Int32  = 0
let ENABLE: Int32  = 1
let DISABLE: Int32 = 0
let ENDPOINT: UInt8   = 0x04

let main: () = {

    var ctx: OpaquePointer? = nil
    var ret: Int32 = 0

    libusb_init(&ctx)
    ret = libusb_set_option2(nil, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG)
    if (ret != 0) {
        print(String(format: "libusb_set_option2() failed. %s", libusb_error_name(ret)))
    }
    
    let handle = libusb_open_device_with_vid_pid(ctx, VENDOR_ID, PRODUCT_ID)
    if (handle == nil) {
        print(String(format: "libusb_open_device_with_vid_pid() failed. errno:%d %s\n", errno, strerror(errno)))
        return
    }
    
    ret = libusb_set_auto_detach_kernel_driver(handle, ENABLE)
    if (ret != 0) {
        print(String(format: "libusb_set_auto_detach_kernel_driver() failed. %s", libusb_error_name(ret)))
        return
    }
    
    var text = [CChar](repeating: 0, count: 256)
    libusb_get_string_descriptor_ascii(handle, 2, &text[0], 256);
    print(String(format: "opened device \"%@\"", asciiCStringToString(text)))
    
    ret = libusb_claim_interface(handle, INTERFACE)
    if (ret != 0) {
        print(String(format: "libusb_claim_interface() failed. %s", libusb_error_name(ret)))
        return
    }
    
    let layer = 1, key = 1
    let modifiers1 = 0, code1 = 4
    let modifiers2 = 0x2, code2 = 5
    var xferd: Int32 = 0
    
    var data1 = [UInt8](repeating: 0, count: 65)
    [0x03, 0xfd, key, layer, 0x01].enumerated().forEach { data1[$0.offset] = UInt8($0.element) }
    zip([0x02, modifiers1, code1, modifiers2, code2], Array(10...14)).forEach { data1[$1] = UInt8($0) }
    hexDump(data1, length: 65)
    ret = libusb_interrupt_transfer(handle, ENDPOINT, &data1, 65, &xferd, 1000);
    if (ret != 0) {
        print(String(format: "1st libusb_interrupt_transfer() failed. %s", libusb_error_name(ret)))
        return
    }
    
    var data2 = [UInt8](repeating: 0, count: 65)
    [0x03, 0xfd, 0xfe, 0xff].enumerated().forEach { data2[$0.offset] = UInt8($0.element) }
    hexDump(data2, length: 65)
    ret = libusb_interrupt_transfer(handle, ENDPOINT, &data2, 65, &xferd, 1000);
    if (ret != 0) {
        print(String(format: "2nd libusb_interrupt_transfer() failed. %s", libusb_error_name(ret)))
        return
    }
    
    libusb_close(handle)
    libusb_exit(ctx)

    print("Completed!")
}()

デバッグログ出力した実行結果を示す。

デバッグログ出力結果を表示
% sudo ./6key_setting_test
Password:
[timestamp] [threadID] facility level [function call] <message>
--------------------------------------------------------------------------------
[ 0.005980] [001d7d56] libusb: debug [libusb_get_device_list]  
[ 0.005997] [001d7d56] libusb: debug [libusb_get_device_descriptor]  
[ 0.005999] [001d7d56] libusb: debug [libusb_open] open 0.7
[ 0.006034] [001d7d56] libusb: debug [darwin_open] device open for access
[ 0.006137] [001d7d56] libusb: debug [libusb_submit_transfer] transfer 0x129f04470
[ 0.006171] [001d7d56] libusb: debug [libusb_get_next_timeout] no URB with timeout or all handled by OS; no timeout!
[ 0.006174] [001d7d56] libusb: debug [libusb_handle_events_timeout_completed] doing our own event handling
[ 0.006176] [001d7d56] libusb: debug [handle_events] event sources modified, reallocating event data
[ 0.006179] [001d7d56] libusb: debug [usbi_wait_for_events] poll() 1 fds with timeout in 60000ms
[ 0.008358] [001d7d5f] libusb: debug [darwin_async_io_callback] an async io operation has completed
[ 0.008374] [001d7d56] libusb: debug [usbi_wait_for_events] poll() returned 1
[ 0.008379] [001d7d56] libusb: debug [handle_event_trigger] event triggered
[ 0.008381] [001d7d56] libusb: debug [darwin_handle_transfer_completion] handling transfer completion type control with kernel status 0
[ 0.008383] [001d7d56] libusb: debug [usbi_handle_transfer_completion] transfer 0x129f04470 has callback 0x10035c99c
[ 0.008385] [001d7d56] libusb: debug [sync_transfer_cb] actual_length=4
[ 0.008388] [001d7d56] libusb: debug [libusb_free_transfer] transfer 0x129f04470
[ 0.008397] [001d7d56] libusb: debug [libusb_submit_transfer] transfer 0x12b804150
[ 0.008414] [001d7d56] libusb: debug [libusb_get_next_timeout] no URB with timeout or all handled by OS; no timeout!
[ 0.008416] [001d7d56] libusb: debug [libusb_handle_events_timeout_completed] doing our own event handling
[ 0.008416] [001d7d5f] libusb: debug [darwin_async_io_callback] an async io operation has completed
[ 0.008418] [001d7d56] libusb: debug [usbi_wait_for_events] poll() 1 fds with timeout in 60000ms
[ 0.008428] [001d7d56] libusb: debug [usbi_wait_for_events] poll() returned 1
[ 0.008430] [001d7d56] libusb: debug [handle_event_trigger] event triggered
[ 0.008431] [001d7d56] libusb: debug [darwin_handle_transfer_completion] handling transfer completion type control with kernel status 0
[ 0.008432] [001d7d56] libusb: debug [usbi_handle_transfer_completion] transfer 0x12b804150 has callback 0x10035c99c
[ 0.008433] [001d7d56] libusb: debug [sync_transfer_cb] actual_length=42
[ 0.008435] [001d7d56] libusb: debug [libusb_free_transfer] transfer 0x12b804150
opened device "USB Composite Device"
[ 0.008628] [001d7d56] libusb: debug [libusb_claim_interface] interface 0
[ 0.008682] [001d7d56] libusb: debug [darwin_detach_kernel_driver] attempting to detach kernel driver from device
[ 0.008718] [001d7d56] libusb: info [darwin_detach_kernel_driver] no capture entitlements. may not be able to detach the kernel driver for this device
[ 0.010616] [001d7d56] libusb: debug [darwin_reenumerate_device] darwin/reenumerate_device: restoring state...
[ 0.010640] [001d7d56] libusb: debug [darwin_open] device open for access
[ 0.010643] [001d7d56] libusb: debug [darwin_restore_state] darwin/restore_state: reclaiming interfaces
[ 0.010646] [001d7d56] libusb: debug [darwin_restore_state] darwin/restore_state: device state restored
[ 0.011127] [001d7d56] libusb: debug [get_endpoints] building table of endpoints.
[ 0.011134] [001d7d56] libusb: debug [get_endpoints] interface: 0 pipe 1: dir: 1 number: 4
[ 0.011138] [001d7d56] libusb: debug [get_endpoints] interface: 0 pipe 2: dir: 0 number: 4
[ 0.011152] [001d7d56] libusb: debug [darwin_claim_interface] interface opened
03 fd 01 01 01 00 00 00 00 00 02 00 04 02 05 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00  
[ 0.011350] [001d7d56] libusb: debug [libusb_submit_transfer] transfer 0x12b804700
[ 0.011351] [001d7d56] libusb: debug [ep_to_pipeRef] converting ep address 0x04 to pipeRef and interface
[ 0.011353] [001d7d56] libusb: debug [ep_to_pipeRef] pipe 2 on interface 0 matches
[ 0.011385] [001d7d56] libusb: debug [libusb_get_next_timeout] next timeout in 0.999966s
[ 0.011388] [001d7d56] libusb: debug [libusb_handle_events_timeout_completed] doing our own event handling
[ 0.011389] [001d7d56] libusb: debug [usbi_wait_for_events] poll() 1 fds with timeout in 1000ms
[ 0.013647] [001d7d5f] libusb: debug [darwin_async_io_callback] an async io operation has completed
[ 0.013671] [001d7d56] libusb: debug [usbi_wait_for_events] poll() returned 1
[ 0.013679] [001d7d56] libusb: debug [handle_event_trigger] event triggered
[ 0.013682] [001d7d56] libusb: debug [darwin_handle_transfer_completion] handling transfer completion type interrupt with kernel status 0
[ 0.013687] [001d7d56] libusb: debug [usbi_handle_transfer_completion] transfer 0x12b804700 has callback 0x10035c99c
[ 0.013690] [001d7d56] libusb: debug [sync_transfer_cb] actual_length=65
[ 0.013695] [001d7d56] libusb: debug [libusb_free_transfer] transfer 0x12b804700
03 fd fe ff 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 
[ 0.013849] [001d7d56] libusb: debug [libusb_submit_transfer] transfer 0x12b904150
[ 0.013851] [001d7d56] libusb: debug [ep_to_pipeRef] converting ep address 0x04 to pipeRef and interface
[ 0.013852] [001d7d56] libusb: debug [ep_to_pipeRef] pipe 2 on interface 0 matches
[ 0.013879] [001d7d56] libusb: debug [libusb_get_next_timeout] next timeout in 0.999972s
[ 0.013882] [001d7d56] libusb: debug [libusb_handle_events_timeout_completed] doing our own event handling
[ 0.013883] [001d7d56] libusb: debug [usbi_wait_for_events] poll() 1 fds with timeout in 1000ms
[ 0.017607] [001d7d5f] libusb: debug [darwin_async_io_callback] an async io operation has completed
[ 0.017631] [001d7d56] libusb: debug [usbi_wait_for_events] poll() returned 1
[ 0.017637] [001d7d56] libusb: debug [handle_event_trigger] event triggered
[ 0.017640] [001d7d56] libusb: debug [darwin_handle_transfer_completion] handling transfer completion type interrupt with kernel status 0
[ 0.017644] [001d7d56] libusb: debug [usbi_handle_transfer_completion] transfer 0x12b904150 has callback 0x10035c99c
[ 0.017647] [001d7d56] libusb: debug [sync_transfer_cb] actual_length=65
[ 0.017652] [001d7d56] libusb: debug [libusb_free_transfer] transfer 0x12b904150
[ 0.017665] [001d7d56] libusb: debug [libusb_close]  
[ 0.017670] [001d7d56] libusb: debug [libusb_release_interface] interface 0
[ 0.017873] [001d7d56] libusb: debug [darwin_attach_kernel_driver] reenumerating device for kernel driver attach
[ 0.126452] [001d7d56] libusb: debug [darwin_reenumerate_device] darwin/reenumerate_device: waiting for re-enumeration to complete...
[ 0.128381] [001d7d5f] libusb: debug [darwin_devices_detached] detected device detached due to re-enumeration. sessionID: 0x4bed2678b29, locationID: 0x120000
[ 0.162517] [001d7d5f] libusb: debug [darwin_get_cached_device] finding cached device for sessionID 0x4bf81dbca23
[ 0.162554] [001d7d5f] libusb: debug [darwin_get_cached_device] parent sessionID: 0xa18e0b5
[ 0.162557] [001d7d5f] libusb: debug [darwin_get_cached_device] matching sessionID/locationID 0x4bf81dbca23/0x120000 against cached device with sessionID/locationID 0x4bed2678b29/0x120000
[ 0.162561] [001d7d5f] libusb: debug [darwin_get_cached_device] found cached device with matching location that is being re-enumerated
[ 0.162563] [001d7d5f] libusb: debug [darwin_get_cached_device] caching new device with sessionID 0x4bf81dbca23
[ 0.162816] [001d7d5f] libusb: debug [darwin_cache_device_descriptor] cached device descriptor:
[ 0.162820] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   bDescriptorType:    0x01
[ 0.162822] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   bcdUSB:             0x0110
[ 0.162824] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   bDeviceClass:       0x00
[ 0.162826] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   bDeviceSubClass:    0x00
[ 0.162828] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   bDeviceProtocol:    0x00
[ 0.162829] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   bMaxPacketSize0:    0x40
[ 0.162831] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   idVendor:           0x1189
[ 0.162833] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   idProduct:          0x8840
[ 0.162835] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   bcdDevice:          0x0100
[ 0.162837] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   iManufacturer:      0x01
[ 0.162839] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   iProduct:           0x02
[ 0.162840] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   iSerialNumber:      0x03
[ 0.162842] [001d7d5f] libusb: debug [darwin_cache_device_descriptor]   bNumConfigurations: 0x01
[ 0.213163] [001d7d5f] libusb: debug [darwin_check_configuration] active config: 1, first config: 1
[ 0.213176] [001d7d5f] libusb: debug [process_new_device] re-using existing device from context 0x129e04890 for with session 0x4bed2678b29 new session 0x4bf81dbca23
[ 0.213181] [001d7d5f] libusb: debug [process_new_device] found device with address 7 port = 2 parent = 0x600003384240 at 0x600003984bba
[ 0.213183] [001d7d5f] libusb: debug [darwin_devices_attached] cached device in reset state. reset complete...
[ 0.213184] [001d7d56] libusb: debug [darwin_reenumerate_device] darwin/reenumerate_device: checking whether descriptors changed
[ 0.213189] [001d7d56] libusb: debug [darwin_reenumerate_device] darwin/reenumerate_device: device reset complete. restoring state...
[ 0.213419] [001d7d56] libusb: debug [darwin_open] device open for access
[ 0.213422] [001d7d56] libusb: debug [darwin_restore_state] darwin/restore_state: reclaiming interfaces
[ 0.213424] [001d7d56] libusb: debug [darwin_restore_state] darwin/restore_state: re-claiming interface 0
[ 0.214969] [001d7d56] libusb: info [darwin_claim_interface] USBInterfaceOpen: another process has device opened for exclusive access
[ 0.214973] [001d7d56] libusb: debug [darwin_restore_state] darwin/restore_state: could not claim interface 0
[ 0.214974] [001d7d56] libusb: info [darwin_capture_release_interface] on attempt to reattach the kernel driver got ret=-5
[ 0.214991] [001d7d56] libusb: debug [libusb_exit]  
[ 0.214999] [001d7d5f] libusb: debug [darwin_event_thread_main] darwin event thread exiting
[ 0.215220] [001d7d56] libusb: debug [libusb_unref_device] destroy device 0.7
[ 0.215579] [001d7d56] libusb: debug [libusb_unref_device] destroy device 0.6
[ 0.215631] [001d7d56] libusb: debug [libusb_unref_device] destroy device 0.5
[ 0.215673] [001d7d56] libusb: debug [libusb_unref_device] destroy device 0.4
[ 0.215707] [001d7d56] libusb: debug [libusb_unref_device] destroy device 0.3
[ 0.215865] [001d7d56] libusb: debug [libusb_unref_device] destroy device 0.2
[ 0.215917] [001d7d56] libusb: debug [libusb_unref_device] destroy device 0.1
[ 0.215955] [001d7d56] libusb: debug [libusb_unref_device] destroy device 2.3
[ 0.215995] [001d7d56] libusb: debug [libusb_unref_device] destroy device 2.2
[ 0.216033] [001d7d56] libusb: debug [libusb_unref_device] destroy device 2.1
[ 0.216070] [001d7d56] libusb: debug [usbi_remove_event_source] remove fd 3
Completed!
% aB

実際にLayer1、キー1を押すと、"aB"がタイプされ、正しく設定したことが確認できた。

20個を超えるキーコード設定の検証

36個のキーコードを指定してもデバイスに20個のデータしか渡していないのは、設定ソフトの仕様(バグ?)であった。上記で作成した検証アプリにて 26個のデータを設定して、実際のデバイスの動作を確認した。

送信したデータは、以下の通り。Layer1、Key1にAZの26個(0x1a)のキーコード。

03 fd 01 01 01 00 00 00 00 00 1a 00 04 00 05 00 
06 00 07 00 08 00 09 00 0a 00 0b 00 0c 00 0d 00 
0e 00 0f 00 10 00 11 00 12 00 13 00 14 00 15 00 
16 00 17 00 18 00 19 00 1a 00 1b 00 1c 00 1d 00 
00

送信は正常に完了した。実際にLayer1、Key1を押してみると、abcdefghijklmnopqrがタイプされた。先頭の18文字だけ?!。このデバイスは、1つのキーに付き18個までしかメモリされない仕様と推察した。

デバイスから設定情報を読み出す

設定アプリには、「Reading Device」というボタンが用意されていて、デバイスに設定されている情報を読み出すことができる。

データの呼び出しは、Layerごとに以下のデータで指定する。

  • 5バイト目::Layer番号(1〜3)(例では3)

readingDevice.png

このデータを送信後、デバイスから9組のデータが連続で送信されてくる。ボタンのキー6個とノブのキー3個の情報で、形式は設定時と同一。(RGBLEDの設定情報は読み出しできない)

読み出しも、確認プログラムにて問題なく実行できた。

デバイスから設定情報を読み出す(その2)

設定アプリは起動時に、下記フォーマットのデータをデバイスに送信している。すると、デバイスから回答データを受信する。
startup.png

このデータのやり取りは、デバイスの接続確認と、接続したデバイスが持つキーとノブの個数を確認しているものと想定する。キー9/12個+ノブ3個など、このデバイスには兄弟デバイスが何種類かある。上記はキー6個+ノブ1個で本デバイスと一致するが、他にデバイスを持っていないため、真偽は不明である。

調査編のまとめ

  • 設定インタフェースを真似る方針に変更
  • Windows10にWinsharkとUSBPcapをインストールして、USBデータをキャプチャできた
  • キャプチャデータを解析して、各種設定データのフォーマットが判明した
  • MacのコマンドラインAppにて、解析したデータ形式で実際に設定できることを確認した
  • デバイスからの設定情報の読み出しも、確認アプリで動作確認した

以上により、調査としてはほぼ目的を達成できた。これで、元々の購入目的である、MacAppによる設定アプリの作成に着手できるが、そもそもこのデバイスが次の点でイマイチであるので、最終的には、このマイコンのプログラムを自分好みに書き換えるつもりである。

イマイチポイント

  • 1つのキーに設定できるキーコード数が少なすぎる
  • マウス操作がボタン操作に限定されていて、(ホイールの回転はあるのに、)マウス移動が使えない
  • また、マウス操作とキーボード操作の組み合わせが使えない

しかし、CH573マイコン用の開発環境を整備することに相当な時間を要すと考えられるため7、その間に、Mac版設定アプリも作成しておく。続編は別記事で投稿する予定。

今回の記事は以上です。



こちらのブログを参考にさせていただきましたが、データ形式がかなり異なっていました。見た感じは同じ仲間と思いましたが・・・



  1. プログラマブルテンキーを作る《https://qiita.com/nak435/items/71e8c6ac306a661f8be8》

  2. ロータリーエンコーダーを使った回転型デバイスの製作《https://qiita.com/nak435/items/44897e080ab51208ca3c》

  3. (2023.12.22追記)久々に商品ページを見たら、Macの設定ソフトが追加されていた。《https://sikaicase.com/blogs/news/sikai-case-mini-keyboard-software-setting-japanese》

  4. 【備忘録】Windows10 102/109キーボードの変更手順《https://qiita.com/nak435/items/25adbfb17285db7e35fc》

  5. Raspberry Pi Desktop「USキーボード」の設定方法《https://qiita.com/nak435/items/5ba4b1254b3b7d0c0265》

  6. Arduino HID-Projectを日本語キーボードに対応する《https://qiita.com/nak435/items/bbe04300c67c37febb7e》

  7. MounRiver Studio(MRS)という統合開発環境だが、サイトhttp://www.mounriver.com/が閉じられていて、新規での環境構築は不可能かも? にpingは通るので、中国在住の友人にダウンロードを依頼し入手した

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0