概要
演奏中に手を使わずiPadの電子楽譜をめくりたいので、いままで市販のBLE接続譜めくりペダルを使っていました。が、どうにも不満が多いので、譜めくりペダルを自作してしまいました。具体的には以下のようなものです。
- ペダルを踏んで離した瞬間(リリースエッジ)に右矢印キーを1回送信
- 誤操作防止のため送信後10秒はクールダウン
なお、今回は要件書作成、HW選定、実装などほぼ Claude Code にやらせています。この記事自体も7割くらいは Claude Code が書いています。
要件書・ドキュメント・コードはすべて Github で公開しています → tmurakam/ble-music-pedal
なぜ市販の譜めくりペダルではだめなのか
市販の譜めくりペダルも2つほど持っているのですが、以下のような不満がありました。
第一に、市販のペダルは踏んだ瞬間にページをめくるようになっています。手順としては 1) 足をペダルの上にもっていく(予備動作)、2) 踏む、という2つの動作が必要になります。
これが非常にやりにくい。なぜなら 1) の手順でうっかり踏んでしまうリスクがあるので気を使うからです。ピアノのダンパーペダルと違って譜めくりペダルは非常に軽いので、先に足だけペダルに置いておくということは基本できません。
私が欲しいのは、ペダルを離した瞬間にページをめくるというものです。この動作であれば、1) 一旦ペダルを踏んでホールドする、2) めくりたい瞬間に離す、という動作になります。これだと、1) は割と雑にペダルを踏んでしまえばいいので楽ですし、2) の離す動作はそもそも簡単なので気を使いません。
また、市販のペダルだと間違って2回踏んでしまって2ページ進んでしまうことがよくあります。そもそもページめくりは通常連続しては行わないので、1回踏んだら10秒程度は反応しないようにしてほしいのですが、そういうことができる製品は見当たりません。
要件
要件としては以下のようなものになります。詳細な要件書は こちら
- iPadのBluetooth設定から「キーボード」として認識・ペアリングできる
- ペダルを1回踏んで離すと、iPad側で右矢印キーが押される(次ページ送り)
- 踏みっぱなしにしても追加送信されない
- 送信後10秒間は再送信されない(クールダウン)
- 10分間操作がないと自動でBLE切断+スリープに入り、消費電力を抑える
- スリープ中でもペダルを踏めば自動で復帰・再アドバタイズを開始
ハードウェア構成
ハードウェア構成は、以下の通りとしました。このあたりは AI (Claude Code) と相談しつつです。
なお、配線については 配線図 にあります。
| 項目 | 内容 |
|---|---|
| マイコン | Seeed Studio XIAO nRF52840 (Nordic nRF52840、BLE 5.0内蔵) |
| ペダル | YAMAHA FC-5 フットペダル (単純な機械式スイッチ。常閉接点=NC) |
| 電源 | LiPoバッテリー(3.7V) 320mAh。XIAO裏面のBAT+/BAT-パッドに接続し、USB-C経由で自動充電 |
| ケース | SW-75B |
| ユニバーサル基盤 | TNF44-69 |
| ペダル端子 | 6.3mmステレオジャック |
| スイッチ | スライドスイッチ |
合計金額は 8000円ほどです。
マイコンは、iPad と BLE 接続するので、BLE 内蔵のものを選ぶ必要があります。
今回は Seeed Studio XIAO nRF52840 を選びました。
最初は Raspberry Pico 2W あたりにしようと思ったのですが、以下の理由から Seeed Studio にしています。
- バッテリー制御機能があるので、LiPo バッテリーを容易に活用できる
- Raspberry Pico 2W より低消費電力、かつ小さい
配線はシンプルで、フットペダルの2本の線をD0とGNDに繋ぐだけです(極性なし)。LiPoバッテリーは基板裏面のBAT+/BAT-パッドにはんだ付けします。
以下が完成品です。右下に Seeed Studio があり、USB-C で PC と接続できるようになっています。左下のステレオジャックは FC-5 フットペダルと接続するのに使用します。
ソフトウェア構成
-
ファームウェア: CircuitPython(ビルド不要、
code.pyを保存すると即座にリロードされる) -
使用ライブラリ:
adafruit_ble、adafruit_hid -
入力処理: 標準ライブラリの
keypadモジュールでデバウンス・エッジ検出をハードウェア/バックグラウンド側に任せている(追加ライブラリ不要) - 通信方式: BLE HID over GATT(HOG)、Appearance = Generic Keyboard(0x03C1)
-
送信キーコード:
Keycode.RIGHT_ARROWのみ
アーキテクチャ
code.pyは単一ファイルのイベントループで構成されています。
-
初期化: ペダル用の
keypad.Keysを作成、HIDService・BLERadioを初期化してアドバタイズ開始 -
メインループ(20ms間隔):
- アイドルタイムアウトをチェックし、超えていればスリープへ移行
-
keypadが検出済みのペダルイベント(press/release)を処理し、リリースエッジで送信条件を判定 - 接続していてクールダウン中でなければ
RIGHT_ARROWを送信 - 未接続時はBLEアドバタイズを維持
- LEDの点滅パターンを状態に応じて更新
- 一定間隔でバッテリー電圧を読んでログ出力
スリープ移行時はkeypad.Keysをdeinit()してピンを解放し、alarm.pin.PinAlarmにピンを渡してlight_sleep_until_alarms()で待機、ペダルが踏まれたら復帰
してkeypad.Keysを再生成します。
実装で工夫したところ
-
デバウンスをアプリコードで書かない:
keypad.Keysのバックグラウンドスキャン(intervalとdebounce_threshold)に任せることで、メインループ側は既に
確定したイベントを読むだけで済むようにした -
NCペダル特有のプル方向の罠:
keypad.Keysはvalue_when_pressed=Falseのときだけ内部プルアップを選択する仕様のため、GNDに落ちるNC(常閉)ペダルではそのままだと意図通りに動かない。value_when_pressedを反転して渡し、イベント判定側でも意味を読み替えることで対応した - クールダウンは送信成功時のみ発生: BLE送信が例外を投げた場合(切断など)はクールダウンを設定せず、次の操作ですぐ再送信を試みられるようにした
-
LEDでペアリング状態を可視化: アドバタイズ中は2回/秒、接続中は3秒に1回、スリープ中は消灯、というシンプルな点滅パターンだけで状態が一目でわかるよう
にした
今後の課題
- 実機でのバッテリー持続時間の実測、およびスリープ移行時間(現在10分)の調整
- Bluetooth の強制ペアリング機能がない
- 長期使用時の安定性検証(ボンディング情報の永続性、再接続の安定性)
まとめ
CircuitPythonとkeypadモジュール、adafruit_ble/adafruit_hidを使うことで、ビルド不要かつ少ないコード量でBLE HIDデバイスを自作できました。
比較的コンパクトで、期待どおり動作しているのでいまのところ満足しています。

