スーパーファミコン コントローラーの無線化プロジェクト:ESPからの制御
前回(https://funasover.blogspot.com/2025/03/blog-post.html)、SFCのコントローラーの仕様というのを確認したので、次のステップとしてEPSのマイコンを使って、信号をSFCに送り込む事を目指す。
P/SのシグナルをGPIO5に入力し、パルスの立ち上がえりを検出。割り込みを入れ、コントローターのボタンの押されている状態にあわせて、DATを出力する。
ESPを使っているので、3.3V系とSFCの5Vの電圧違いを吸収するために、レベルコンバーターを間に挟んでいる。
#define PS_READ_PIN 5
#define DAT_OUTPIN 4
unsigned int button_status;
void onRising() {
//B Y SELECT START 上 下 左 右 A X L R HHH
button_status = 0b0101010101010000; //ボタンが押されていたら1
for (int i = 0; i < 16; i++) {
if (bitRead(button_status, 15 - i) == 0B00) {
digitalWrite(DAT_OUTPIN, HIGH);
} else {
digitalWrite(DAT_OUTPIN, LOW);
}
if (i == 0) {
delayMicroseconds(17);
for (int i = 0; i < 2; i++) { asm("nop \n"); }; // i<10 0.65usec
} else {
delayMicroseconds(10);
for (int i = 0; i < 3; i++) { asm("nop \n"); }; // i<10 0.65usec
}
}
digitalWrite(DAT_OUTPIN, LOW);
}
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(DAT_OUTPIN, OUTPUT);
pinMode(PS_READ_PIN, INPUT);
digitalWrite(DAT_OUTPIN, LOW);
attachInterrupt(digitalPinToInterrupt(PS_READ_PIN), onRising, RISING);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, LOW); // Turn the LED on (Note that LOW is the voltage level
// but actually the LED is on; this is because
// it is active low on the ESP-01)
delay(1000); // Wait for a second
digitalWrite(LED_BUILTIN, HIGH); // Turn the LED off by making the voltage HIGH
delay(2000); // Wait for two seconds (to demonstrate the active low LED)
}
button_statusとして16ビットのintを用意し目的ボタンが押されていたら1という事にする。デバッグしやすいように、B→Y→SELECT→START→上→下→左→A→X→L→Rで交互に押す、押さないとして、0b0101010101010000;とゼロと1を交互にテストのため並べた。
まず、PSが立ち上がりをattachInterruptで検出するが、これには5usec程度かかる。こればかりは仕方がない。
delayMicroseconds(17);
for (int i = 0; i < 2; i++) { asm("nop \n"); }; // i<10 0.65usec
ここがその調整の努力の跡で、17msec+ちょっとHIGHの状態を続けている。1msec以下の時間調整のためにfor (int i = 0; i < 2; i++) { asm("nop \n"); };を使っている。
asm("nop \n");一回で0.065usecの調整がおよそできていた。
delayMicroseconds(10);
for (int i = 0; i < 3; i++) { asm("nop \n"); }; // i<10 0.65usec
それ以外の所は、12usecの周期でクロックが変化が、12usecの待ち時間を入れるとif文の処理時間なのでそれより長くなるので、実際の所は、10usecの遅延+ちょっと調整でちょうど12usecに調整。
いざ、SFCと接続してみる。先ほど接続してなかったDATをSFCのDATとレベルコンバーターを介して接続。
ソースも、少し編集して、1秒おきに、STARTボタンと、Aボタンを交互に押すようにESPマイコンからコントローターの信号を出してやる。
#define PS_READ_PIN 5
#define DAT_OUTPIN 4
unsigned int button_status;
void onRising() {
//B Y SELECT START 上 下 左 右 A X L R HHH
// button_status = 0b0101010101010000; //ボタンが押されていたら1
for (int i = 0; i < 16; i++) {
if (bitRead(button_status, 15 - i) == 0B00) {
digitalWrite(DAT_OUTPIN, HIGH);
} else {
digitalWrite(DAT_OUTPIN, LOW);
}
if (i == 0) {
delayMicroseconds(17);
for (int i = 0; i < 2; i++) { asm("nop \n"); }; // i<10 0.65usec
} else {
delayMicroseconds(10);
for (int i = 0; i < 3; i++) { asm("nop \n"); }; // i<10 0.65usec
}
}
digitalWrite(DAT_OUTPIN, LOW);
}
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output
pinMode(DAT_OUTPIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output
pinMode(PS_READ_PIN, INPUT);
digitalWrite(DAT_OUTPIN, LOW);
attachInterrupt(digitalPinToInterrupt(PS_READ_PIN), onRising, RISING);
button_status = 0b0000000000000000;
}
// the loop function runs over and over again forever
void loop() {
//B Y SELECT START 上 下 左 右 A X L R HHH
button_status = 0b0001000000000000;//start
digitalWrite(LED_BUILTIN, LOW); // Turn the LED on (Note that LOW is the voltage level
// but actually the LED is on; this is because
// it is active low on the ESP-01)
delay(1000); // Wait for a second
button_status = 0b0000000001000000;//A
digitalWrite(LED_BUILTIN, HIGH); // Turn the LED off by making the voltage HIGH
delay(1000); // Wait for two seconds (to demonstrate the active low LED)
}
実際、動かした様子。ドンキーコングだが電源を入れてから、STARTとAを交互に押しているのでゲームがスタートするところまで、自動で進んでいく!
これだけでも、ESPマイコンによるRPAツールができてRPGとかのゲームで自動レベルアップとかできぞう・・・。https://pokemonit.com/splatoon3-auto-nawabattler/
ロードマップ上、マイコンで自動制御できるところまではできた。button_statusにあたる2バイト(16ビット)の信号を無線で飛ばしてくれば、無線化ができぞう・・・もう少しかかるが・・・
コメント
コメントを投稿