Slide 1

Slide 1 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber Embedded Swiftで作る自作USBデバイス によるiOSデバイスの自動テスト iOSDC Japan 2026 2026/09/12 15:00 Track A glass ber(ピクシブ株式会社) fi 1

Slide 2

Slide 2 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber iOS Engineer @ pixiv glass ber • iPad用お絵描きアプリ Pastela の開発に従事 • 元ゲーム開発者 • iOSDC 2024 ルーキーズLT fi 2

Slide 3

Slide 3 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 発表内容 マイコンを使って、iPadを操作するUSBデバイスを作った話 ・HIDデバイスの実装でつまずいた部分とその解決 Embedded Swiftでアプリケーションロジックを書いた話 ・Cのアプリケーションロジック実装をEmbedded Swiftに移す 実機での自動テストへ発展する(ための準備段階)の話 ・テストを実用するには至らず 3

Slide 4

Slide 4 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber iPadを自動操作しようとした動機 動作確認やテストはSimulatorで行うことが多いが iPad実機でやりたいときもある 実機で動作させるときはXCTest経由で行うことが多い ・AppiumやMagicPodなど ・XCTestを使わない手段としては手動・タッチペンを動かす機械など XCTestを経由せずに操作することはできるのか? 4

Slide 5

Slide 5 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 目的 実機をXCTestを経由せず操作してみたい ・iPadはキーボードやマウスなどの外部デバイスを接続して操作できる ・外部からマウスのような操作を入力できるデバイスがあれば自動で操作できそう Embedded Swiftで実用品を作りたい ・最近Embedded Swiftが話題で、iOSエンジニアとして挑戦してみたい ・単なる学習用ではなく、実際の開発課題につながるものがやりたい →iPadに接続し、任意の位置をタップ・ドラッグする専用入力デバイスを作りたい 5

Slide 6

Slide 6 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber iPadに接続できる入力デバイス iPadはキーボードやマウスを接続して操作できる ・これらはHuman Interface Device(HID)と呼ばれる 自分のプログラムで制御可能なHIDを作り、iPadを外部から操作 ・HIDを自作するのにマイコンボードを使うことが多い ・開発にEmbedded Swiftが使えるもの 6

Slide 7

Slide 7 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber マイコンボードでUSB HIDデバイスを作る Embedded Swiftが対応しているメジャーなマイコンボード ・Raspberry Pi Picoシリーズ ・ESP32シリーズ 今回はRaspberry Pi Pico 2 Wを使用 ・USBとBluetoothを扱える Raspberry Pi Picoシリーズ ・安価かつ容易に入手できる ・ドキュメントや作例が豊富 ・すでに所有していた ESP32シリーズ 7

Slide 8

Slide 8 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 作成したものの全体構成 ・Raspberry Pi Pico 2 W(Pico) をiPadに接続 iPad HIDとして振る舞い、iPadを操作する ・PCからPicoにBluetooth(BLE)で操作スクリプトを送信 Picoのファームウェア記述はCとEmbedded Swiftの混合 iPad USB HID 画面操作 ・HIDとBLEの制御部分はCで記述 Cで書かれた既存のライブラリが充実しているためそのまま利用 ・Pico SDK - Pico向けの公式C/C++開発環境 ・TinyUSB - PicoをUSB機器として動かすライブラリ ・BTstack - PicoでBluetooth通信を行うライブラリ ・操作データの管理部分をEmbedded Swiftで記述 Swiftの配列やenumなどを使って書ける 8 BLE スクリプト送信

Slide 9

Slide 9 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber Report Descriptor └─ Usage Page (Generic Desktop) └─ Usage (Mouse) └─ Collection (Application) ├─ Usage Page(Button) └─ Usage Page(Generic Desktop) ├─ Usage (X) ├─ Usage (Y) └─ Input (Data, Variable, Relative) HID動作の概要 USB規格書にあるHIDの仕様を参照 https://www.usb.org/hid まず、デバイス側がReport Descriptorで データ構造を定義する マウスのReport Descriptorの例(抜粋) デバイスは定義した構造に従ってInput Reportを送信する ホスト側はReport Descriptorに基づいてInput Reportを解釈する マウスやキーボードなどよく使われるデバイスは Report Descriptor の構成がある程度定型化している Report Descriptorをどう解釈するかはホスト側次第 ・解釈できない場合は動作しない 9

Slide 10

Slide 10 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber マウスのReport Descriptorの例 ・デバイスの種類はマウス Usage Page (Generic Desktop) Usage (Mouse) ・ボタンは3つ Usage Mimum (1), Usage Maximum (3) : 番号1〜3 Report Count (3) : 個数3 ・XとYは-127〜127の範囲で 相対的(Relative)な値 Logical Minumum (-127), Logical Maximum (127) Input (Data, Variable, Relative) 10 Usage Page (Generic Desktop), Usage (Mouse), Collection (Application), Usage (Pointer), Collection (Physical), Usage Page (Buttons), Usage Minimum (1), Usage Maximum (3), Logical Minimum (0), Logical Maximum (1), Report Count (3), Report Size (1), Input (Data, Variable, Absolute), // 3 button bits Report Count (1), Report Size (5), Input (Constant), // 5 bit padding Usage Page (Generic Desktop), Usage (X), Usage (Y), Logical Minimum (-127), Logical Maximum (127), Report Size (8), Report Count (2), Input (Data, Variable, Relative), // X and Y End Collection, End Collection

Slide 11

Slide 11 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber さまざまなHID HIDの規格ではマウスやキーボード以外にも さまざまな機器が定義されている Touch Screen, Pen, Digitizer など、絶対座標を指定できるものがい くつかある →これらを使ったらiPadで画面の任意の位置を操作できるのでは… しかし検証してみたところ、iPad OSでは認識しなかった 11

Slide 12

Slide 12 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber Report Descriptorを調べる マウスとTouch ScreenのReport Descriptorを比較してみる ・デバイスの種類が異なる Generic Desktop/Mouse と Digitizers/Touch Screen ・座標の属性が異なる Relative と Absolute デバイスの種類をマウスに したまま、座標の属性を Absoluteにしたら絶対座標で 指定できないだろうか? Touch Screen Usage Page (Generic Desktop), Logical Maximum (4095), マウス Usage Page (Generic Desktop), Report Size (16), ... Usage (X), Usage (X), Usage (Y), ... Logical Minimum (-127), Input (Data, Variable, Absolute), Logical Maximum (127), Usage (Y), Report Size (8), ... Report Count (2), Input (Data, Variable, Relative), Input (Data, Variable, Absolute), 12

Slide 13

Slide 13 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber Absoluteで座標指定するマウス Descriptorで座標値の範囲を指定 今回は画面の範囲で指定できればよいため、 16bitの整数値で0…32767とする →絶対座標で指定できた! マウスカーソルが指定した位置に ジャンプする動き 13 Absolute Mouse Usage Page (Generic Desktop), Usage (X), Usage (Y), Logical Minimum (0), Logical Maximum (32767), Report Size (16), Report Count (2), Input (Data, Variable, Absolute),

Slide 14

Slide 14 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 14

Slide 15

Slide 15 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 15

Slide 16

Slide 16 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 16

Slide 17

Slide 17 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 17

Slide 18

Slide 18 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 18

Slide 19

Slide 19 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 19

Slide 20

Slide 20 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber マウスボタンの入力(TapやDrag) 指定座標への移動に成功したのでボタン入力に対応する Descriptionのボタンに関する部分は通常のマウスと同じ Button DownとButton Upを送るだけ …のはずがボタンのUpが伝わらない現象が発生 座標移動後、Button Downは反応するがButton Upはアプリへ伝わらず、 押されっぱなしの状態になる 20

Slide 21

Slide 21 text

左上でクリックした後、下の方に水平線を引く Mac iPad Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber マウスボタンの入力(Macとの比較) 21

Slide 22

Slide 22 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber どうにかしてボタン操作を可能にしたい 解決方法がないか試行錯誤を繰り返す ・Report Descriptor内の順番を変えてみる ・同じ座標でもう一度Button Upを送ってみる ・iPad側でAssistiveTouchの設定をいろいろいじってみる しかし解決しない 座標がRelativeならちゃんと動作するのに… 22

Slide 23

Slide 23 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 2匹のマウスでクリックする 座標がRelativeのマウスでは動作する →Relativeのマウスを追加して、ボタンはそちらから送ってみたらどうか? 1台のPicoを2種類のマウスに見えるようにして、役割を分ける ・座標がAbsoluteのマウス1 ・座標がRelativeのマウス2 1つのReport Descriptorに2つのMouse Application Collectionを定義した 複合デバイス ・マウス1で座標を送る(ボタン情報は常に空) ・マウス2でボタン操作を送る(移動量は常に0) 23

Slide 24

Slide 24 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber クリックもドラッグもできた ・マウス1で指定座標へ移動 ・マウス2でボタンをDown→Up →クリックすることができた! ドラッグについても動作できた ・マウス1で始点へ移動 ・マウス2でボタンDown ・マウス1で座標を移動 ・マウス2でボタンUp PicoでiPadを操作することができた! 24

Slide 25

Slide 25 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber Embedded Swiftのコードを書く ここまでは移動やクリックの処理 などをすべてCで書いていた→ HID動作の目処がたったので、当初 の目的であるEmbedded Swiftを 書きたい 25 #define DEMO_POINT(_delay, _buttons, _x, _y) \ {_delay, PICOTAB_EVENT_POINTER_REPORT, _buttons, _x, _y, 0, 0} static const picotab_script_event_t bootsel_demo_events[] = { DEMO_POINT(1500, 0, UI_NEW_CANVAS_BUTTON_X, UI_NEW_CANVAS_BUTTON_Y), DEMO_POINT(UI_CLICK_HOLD_MS, DEMO_PRIMARY_BUTTON, UI_NEW_CANVAS_BUTTON_X, UI_NEW_CANVAS_BUTTON_Y), DEMO_POINT(UI_OPEN_PRESET_WAIT_MS, 0, UI_NEW_CANVAS_BUTTON_X, UI_NEW_CANVAS_BUTTON_Y), DEMO_POINT(500, 0, UI_SQUARE_PRESET_X, UI_SQUARE_PRESET_Y), DEMO_POINT(UI_CLICK_HOLD_MS, DEMO_PRIMARY_BUTTON, UI_SQUARE_PRESET_X, UI_SQUARE_PRESET_Y), DEMO_POINT(UI_CREATE_CANVAS_WAIT_MS, 0, UI_SQUARE_PRESET_X, UI_SQUARE_PRESET_Y), : : DEMO_POINT(DRAW_INTERVAL_MS, DEMO_PRIMARY_BUTTON, DRAW_STAR_1_X, DRAW_STAR_1_Y), DEMO_POINT(0, 0, DRAW_STAR_1_X, DRAW_STAR_1_Y), }; #undef DEMO_POINT

Slide 26

Slide 26 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber Embedded Swiftのビルド環境をつくる Visual Studio Code + Pico SDK ・Embedded Swiftコンパイラをインストール "Installing Embedded Swift"*1に従い、swiftlyを使ってSwiftコンパ イラのDevelopment Snapshotをインストール ・Embedded SwiftコードのコンパイルができるCMake設定など →AIで解決 (CMakeわからない…) *1 https://docs.swift.org/latest/documentation/embeddedswift/installembeddedswift 26

Slide 27

Slide 27 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber Embedded Swift で書き直す enum ScriptEventKind: UInt8 { case mouse case keyboard case wait } 操作データとその配列 →Swiftのenumとstructで表現 [ScriptEvent]として保持 再生時にイベントを1件ずつHIDレ ポートへ変換 struct MouseEvent { let buttons: UInt8 let x: Int16 let y: Int16 } struct ScriptEvent { let delayAfterMs: UInt16 let kind: ScriptEventKind let mouse: MouseEvent let keyboard: KeyboardEvent (比較的) } Swiftらしく書けるようになった let events: [ScriptEvent] 27

Slide 28

Slide 28 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber [86/86] Linking CXX executable picotab.elf 開発中に詰まった問題 DemoScript.swift.o: undefined reference to `posix_memalign' events: [ScriptEvent] 配列のデータを増やして いったらリンクエラーが出るようになった DemoScript.swift.o: undefined reference to `posix_memalign' collect2: error: ld returned 1 exit status ninja: build stopped: subcommand failed. 原因はposix_memalign関数がないこと ・要素数が大きくなると動的メモリ確保が生成される → Embedded Swiftが posix_memalign を要求するが Pico SDK側に実装がなく、リンクエラー Embedded Swiftは大きなArrayの領域確保に posix_memalign を使用するが 今回 Pico SDKで使用している Newlib には posix_memalign がなかった ※Embedded Swiftドキュメントの必要な外部依存に posix_memalignとfree が書かれている https://docs.swift.org/latest/documentation/embeddedswift/externaldependencies/ 28

Slide 29

Slide 29 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber Newlibとは 組み込み向けのlibc実装 ・ISO Cの機能を提供する ・OSを前提とするPOSIX APIの posix_memalign は含まれない 対応 aligned_alloc を使い posix_memalign 互換実装を作成 (aligned_alloc はISO C11で規定された標準Cライブラリ関数) →無事ビルド可能に 29

Slide 30

Slide 30 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber Embedded Swiftで動作するようになった 現状の手順 1. Swiftコード上に操作スクリプトの配列を直接記述 2. ビルドしてPCに接続したPicoに転送 3. PicoをiPadに接続しなおして再生ボタンを押す 4. なんかおかしければ1.に戻る あまりにもめんどくさい… →操作スクリプトをビルド時でなく外から送り込みたい 30

Slide 31

Slide 31 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 操作スクリプトをBLEで転送 PicoはiPadにUSBで接続されていて、Bluetoothは未使用 iPad →Bluetooth(BLE)でPCと接続し、データを受信できる ※当初、画面操作をBluetooth HIDで行うことも検討したが、 USB HID 画面操作 Absolute指定がうまく動作しなかったため断念 1. PCで操作内容を編集 2. スクリプト全体をBLEでPicoに転送 3. Picoがスクリプトを再生、HIDでiPadを操作 31 BLE スクリプト送信

Slide 32

Slide 32 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 操作スクリプトをBLEで転送 ・PC側の送信ツールはPythonで作成 ・操作をリアルタイムで逐次送るわけではない ・通信遅延などによりドラッグなどのタイミングが不安定になる ・スクリプト全体を一括で転送することで回避 ・Pico側のBLE通信はBTstackに実装されている Nordic UART Serviceを使用 …しているらしい(AIに書いてもらった) 32

Slide 33

Slide 33 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber iPad操作デモ 33

Slide 34

Slide 34 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 実機自動テストに向けて 実機iPadを外部から操作する経路ができた ・PCから操作スクリプトを送信 ・PicoがiPadへHID入力を送る ・iPad上の座標を絶対座標で指定し、タップやドラッグができる 結果の検証が必要 ・とりあえずスクリーンショットを取得した ・その後の検証部分は検討が必要 34

Slide 35

Slide 35 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber まとめ ・Raspberry Pi Pico 2 WでUSBデバイスを作ってiPadを操作することができた ・座標値をAbsoluteとすることで絶対座標で操作の指定が可能 ・ボタンがうまく動作しない現象はもう1つマウスを追加することで回避できる ・Embedded SwiftとCを混在してプログラムの記述ができた ・Cで書いていたものをEmbedded Swiftに置き換えて書きやすく ・Embedded Swiftで必要な外部依存関数について理解 ・BLEでPCから操作スクリプトを送信し、PCからiPadを操作できるようになった ・実機自動テストへ発展させられる可能性 ・テスト環境をハードウェアごと作るのは楽しい 35

Slide 36

Slide 36 text

Embedd ed Swif tで作る自作USBデバイスによるiO Sデバイスの自動テスト|glassfiber 36