概要
Raspberry Pi Pico の USB 機能を利用し、JR 東日本トレインシミュレーター用の簡易コントローラーを作成します。
作成する機能
タクト スイッチを使用して、シンプルに次の 3 つの操作を行えるようにします。
- 加速強め/ブレーキ弱め (P)
- 加速弱め/ブレーキ強め (B)
- ニュートラル (N)
作成は、手軽に試すことのできるブレッドボードを使用します。
ハードウェア
使用パーツ
使用するパーツを以下の表にまとめます。これらは、秋月電子通商などで購入できます。表中のパーツ名は、秋月電子通商の商品ページにリンクしています。
パーツ名 | 型番 | 数量 | 備考 |
---|---|---|---|
Raspberry Pi Pico | 1 | ||
ブレッドボード | EIC-801 | 1 | |
タクト スイッチ | TS-0606 | 3 | 基板取付用タイプ |
細ピン ヘッダー | PHA-1x20SG | 2 | シングル 20 ピン |
ジャンパ ワイヤー | 165-012-000(EIC -J-L) | 適量 | 単線タイプ (型番は一例) |
USB ケーブル | 1 | A オス – マイクロ B オス |
回路
回路図は以下の通りです。

部品配置
部品配置は以下の写真の通りです。

ファームウェア
TinyUSB のサンプルを流用
ファームウェアは、Raspberry Pi Pico 公式 SDK に付属する TinyUSB のサンプル コード dev_hid_composite を改造して作成します。Raspberry Pi Pico を HID デバイスとして動作させ、Windows からはキーボード デバイスとして認識させます。
サンプルの主な改造ポイントを以降で説明します。
ボタン入力ピン割り当て
ボタン入力を受け付けるため、GPIO ピンを次のように割り当てます。GP25 は基板上の LED を点滅させるために使用します。
GPIO | 機能 |
---|---|
GP2 | 加速強め/ブレーキ弱め |
GP3 | ニュートラル |
GP4 | 加速弱め/ブレーキ強め |
// GPIO ピン兼ボタン ID の定義
enum
{
LED_PIN = 25, // LED
BUTTON_ACCEL = 2, // 加速強め/ブレーキ弱め
BUTTON_NEUTRAL = 3, // ニュートラル
BUTTON_BRAKE = 4, // 加速弱め/ブレーキ強め
};
GPIO の初期化
main 関数内で、ボタン用の GPIO を入力ピンとして設定し、さらにプルアップを有効にします。
int main()
{
board_init();
tusb_init();
stdio_init_all();
// GPIO を初期化します。
gpio_init(LED_PIN);
gpio_init(BUTTON_ACCEL);
gpio_init(BUTTON_NEUTRAL);
gpio_init(BUTTON_BRAKE);
// GPIO の入出力方向を設定します。
gpio_set_dir(LED_PIN, GPIO_OUT);
gpio_set_dir(BUTTON_ACCEL, GPIO_IN);
gpio_set_dir(BUTTON_NEUTRAL, GPIO_IN);
gpio_set_dir(BUTTON_BRAKE, GPIO_IN);
// 入力端子をプルアップします。
gpio_pull_up(BUTTON_ACCEL);
gpio_pull_up(BUTTON_NEUTRAL);
gpio_pull_up(BUTTON_BRAKE);
while (1)
{
tud_task(); // tinyusb device task
led_blinking_task();
hid_task();
}
return 0;
}
ボタン入力関数の作成
GetButtonStatus 関数は GPIO 入力をスキャンし、押されているボタンの ID を返します。
uint GetButtonStatus(void)
{
const uint buttons[] =
{
BUTTON_NEUTRAL,
BUTTON_BRAKE,
BUTTON_ACCEL
};
// 入力ポートをスキャンします。
for (int i = 0; i < count_of(buttons); i++)
{
if (!gpio_get(buttons[i]))
{
return buttons[i];
}
}
return 0;
}
hid_task 関数の改造
hid_task 関数内で、board_button_read 関数の代わりに GetButtonStatus 関数を呼び出すことにより、ボタンが押されたときにキー イベントを送信できるようにします。
void hid_task(void)
{
// Poll every 10ms
const uint32_t interval_ms = 10;
static uint32_t start_ms = 0;
if (board_millis() - start_ms < interval_ms)
return; // not enough time
start_ms += interval_ms;
//uint32_t const btn = board_button_read();
uint32_t const btn = GetButtonStatus();
// Remote wakeup
if (tud_suspended() && btn)
{
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
}
else
{
// Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
send_hid_report(REPORT_ID_KEYBOARD, btn);
}
}
tud_hid_report_complete_cb 関数の改造
サンプルでは、キーボード イベント以外にマウス イベント等も送信していますが、今回はキーボード イベントのみでよいため、不要なコードを削除します。
void tud_hid_report_complete_cb(uint8_t instance, uint8_t const *report, uint16_t len)
{
(void)instance;
(void)len;
(void)report;
}
send_hid_report 関数の改造
キーボード イベントのみ送信するため、その他のコードを削除します。また、それぞれのボタンに対応したキー コードを送信するようにコードを書き換えます。
ここで、JR 東日本トレインシミュレーターのキーボード ショートカットは以下の通りです。
操作 | ショートカット キー |
---|---|
加速強め/ブレーキ弱め | z |
ニュートラル | s |
加速弱め/ブレーキ強め | q |
static void send_hid_report(uint8_t report_id, uint32_t btn)
{
// skip if hid is not ready yet
if (!tud_hid_ready())
return;
switch (report_id)
{
case REPORT_ID_KEYBOARD:
{
// キーボードに対して複数の連続したゼロ レポートが送信されるのを避けるために使用します
static bool has_keyboard_key = false;
uint8_t keycode[6] = {0};
switch (btn)
{
case BUTTON_ACCEL:
keycode[0] = HID_KEY_Z;
break;
case BUTTON_NEUTRAL:
keycode[0] = HID_KEY_S;
break;
case BUTTON_BRAKE:
keycode[0] = HID_KEY_Q;
break;
default:
break;
}
if (btn)
{
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);
has_keyboard_key = true;
}
else
{
// 以前にキーが押された場合は空のキーのレポートを送信する
if (has_keyboard_key)
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
has_keyboard_key = false;
}
}
break;
default:
break;
}
}
デバッグ
ビルドしたファームウェア (.uf2) をインストールすると、Raspberry Pi Pico 上の LED が点滅を始め、PC にドライバーが自動的にインストールされます。そして、Raspberry Pi Pico がキーボード デバイスとして認識されるようになります。
メモ帳を立ち上げ、ウィンドウをアクティブにした状態でボタンを押します。押したボタンに応じて「z」、「s」、「q」という文字が入力されれば OK です。
うまくいったら、今度は JR 東日本トレインシミュレーターを立ち上げて、コントロールができることを確認します。
実行例
以下の動画は、作成したコントローラーで JR 東日本トレインシミュレーターをプレイしている様子です。
ソース全体について
ソース ファイル一式 (ZIP ファイル) は、下記リンクからダウンロードできます。
まとめ
Raspberry Pi Pico SDK には USB ライブラリが標準装備されているため、USB のプロトコルに関する専門的な知識がなくても、このようにオリジナルの USB 機器を簡単に作成することができました。便利な時代になりましたね。
皆さんも、オリジナル USB 機器の作成に挑戦してみてはいかがでしょうか?