はじめに
きっかけは以下のツイートの内容まんまですが、Nintendo Switchのコントローラー制御を自動化したくなりました。
自動化にあたり行った調査、自動化で使用したライブラリとその使い方を紹介していきます。
調査
「Switch 自動化」でググる
Arduinoが基本のよう。
けどArduinoが手元にない…
M5StackデバイスはいっぱいあるのでESP32でいける方法がほしい…
生成AIに聞いてみる
ChatGPT 3.5、Claude3 Haiku、Bingに、
「ESP32というマイコンを使ってNintendo Switchのコントローラーを作りたいです。どのようなライブラリを駆使すれば実現できますか。」
と聞いてみた。
結果、プロンプトが適当すぎるからでしょうがChatGPTとClaude3はESP32のBLEライブラリを提示してきて結構な低レイヤーから実装させようとしてきました。
ただBingはESP32向けのSwitchコントローラーライブラリを提示してくれて、そこで教えてくれたsorasen2020/SwitchControllerESP32がサンプルコード見た感じ一番シンプルだったのでこれを使ってみました。
実装
サンプルコードをそのままAtomS3に書き込んでみたらSwitchに認識されなくて、コード冒頭の#ifndef
まわりを消したらいけました。
当初目的としてはA連打だけしたかったのでこれで実装完了。
簡単すぎる…
BingとSwitchControllerESP32の作者さんありがとう…
サンプルコードの#ifndef
まわりを削除しただけのコードがこちら。
/*
Copyright (c) 2023 sorasen2020
released under the MIT license
https://opensource.org/licenses/mit-license.php
*/
#include "SwitchControllerESP32.h"
#include "Arduino.h"
void setup() {
switchcontrolleresp32_init();
USB.begin();
switchcontrolleresp32_reset();
}
void loop() {
pushButton(Button::A, 100, 1);
}
簡単にコントローラー操作メソッドを紹介
pushButton
void pushButton(Button button, int delay_after_pushing_msec, int loop_num)
ボタンを押すメソッドです。
button
に後述するButton型を指定します。
ここでは方向ボタンは含まれません。
単にボタンを押すだけのケースでこのメソッドを使います。
delay_after_pushing_msec
は押したあとの待ち時間のミリ秒、loop_num
は繰り返し回数を指定します。
Button
型の定義は下表です。
値を足し合わせることで同時押しになります。
ボタン | 記述 | 値 |
---|---|---|
Y | Button::Y | 0x0001 |
B | Button::B | 0x0002 |
A | Button::A | 0x0004 |
X | Button::X | 0x0008 |
L | Button::L | 0x0010 |
R | Button::R | 0x0020 |
ZL | Button::ZL | 0x0040 |
ZR | Button::ZR | 0x0080 |
- | Button::MINUS | 0x0100 |
+ | Button::PLUS | 0x0200 |
左スティック押し込み | Button::LCLICK | 0x0400 |
右スティック押し込み | Button::RCLICK | 0x0800 |
HOME | Button::HOME | 0x1000 |
キャプチャー | Button::CAPTURE | 0x2000 |
pushButton2
void pushButton2(Button button, int pushing_time_msec, int delay_after_pushing_msec, int loop_num)
pushButton
の引数に加え、ボタンを押し込んでいる時間のミリ秒となるpushing_time_msec
が追加されたメソッドです。
ちなみにpushButton
でのデフォルトのボタン押し込み時間は40msecです。
pushHatButton
void pushHatButton(Hat button, int delay_after_pushing_msec, int loop_num)
pushButton
と同じようなメソッドですが、こちらは方向ボタンを操作するメソッドです。
Hat
型の定義は下表です。
ボタン | 記述 | 値 |
---|---|---|
上 | Hat::UP | 0x00 |
上右同時 | Hat::UP_RIGHT | 0x01 |
右 | Hat::RIGHT | 0x02 |
右下同時 | Hat::RIGHT_DOWN | 0x03 |
下 | Hat::DOWN | 0x04 |
下左同時 | Hat::DOWN_LEFT | 0x05 |
左 | Hat::LEFT | 0x06 |
左上同時 | Hat::LEFT_UP | 0x07 |
ニュートラル | Hat::CENTER | 0x08 |
pushHatButtonContinuous
void pushHatButtonContinuous(Hat button, int pushing_time_msec)
方向ボタンを押し込んでいる時間のミリ秒をpushing_time_msec
で指定します。
pushButton2
とは異なりdelay_after_pushing_msec
、loop_num
の引数は指定できません。
tiltJoystick
void tiltJoystick(int lx_per, int ly_per, int rx_per, int ry_per, int tilt_time_msec, int delay_after_tilt_msec)
lx_per
、ly_per
、rx_per
、ry_per
は左右スティックそれぞれのX/Y軸値を-100~100の範囲で指定します。
tilt_time_msec
はスティックを倒している時間のミリ秒、delay_after_tilt_msec
はメソッド実行後の待ち時間のミリ秒です。
細かなスティック操作を行いたい場合にこのメソッドを使用します。
UseLStick / UseRStick
void UseLStick(LS Lstick, int tilt_time_msec, int delay_after_tilt_msec)
void UseRStick(RS Rstick, int tilt_time_msec, int delay_after_tilt_msec)
左スティック / 右スティックを特定の方向に完全に倒すメソッドです。
Lstick / Rstick
は下表のLS / RS
型を指定します(割当られている値がHat
型と異なります)。
tilt_time_msec
はスティックを倒している時間のミリ秒、delay_after_tilt_msec
はメソッド実行後の待ち時間のミリ秒です。
スティック位置 | 記述 | 値 |
---|---|---|
中央(ニュートラル) | LS::LS_CENTER / RS::RS_CENTER | 0x0000 |
上 | LS::LS_UP / RS::RS_UP | 0x0001 |
右上 | LS::LS_UP_RIGHT / RS::RS_UP_RIGHT | 0x0002 |
右 | LS::LS_RIGHT / RS::RS_RIGHT | 0x0003 |
右下 | LS::LS_DOWN_RIGHT / RS::RS_DOWN_RIGHT | 0x0004 |
下 | LS::LS_DOWN / RS::RS_DOWN | 0x0005 |
左下 | LS::LS_DOWN_LEFT / RS::RS_DOWN_LEFT | 0x0006 |
左 | LS::LS_LEFT / RS::RS_LEFT | 0x0007 |
左上 | LS::LS_UP_LEFT / RS::RS_UP_LEFT | 0x0008 |
TiltLeftStick
void TiltLeftStick(int direction_deg, double power, int holdtime, int delaytime)
左スティックをdirection_deg
で指定した角度へ倒します。
power
で傾き具合を0.0~1.0の間で指定できます。
holdtime
はスティックを倒している時間のミリ秒、delaytime
はメソッド実行後の待ち時間のミリ秒です。
なお右スティックバージョンのメソッド(TiltRightStick
のようなメソッド)はありません。
おわりに
先達の方々による技術の積み重ねのおかげでめちゃくちゃあっさり自動化が実現できました。
なお今回やりたかったA連打のような操作はデバイスに書き込んですぐに実用できますが、複数操作のディレイタイム調整やスティック操作が絡んでくると微妙な試行錯誤が必要になってきます。
コードを書き換えるたびにデバイス書き込みもして…と繰り返すのはかなり億劫なので、opnizを使ってデバイス書き込み不要で処理をホットスワップする方法についても記事にしたいと思います。