Poke-Controllerを使った「3DS自動化」の方法

Poke-Controllerとは、(剣盾以降の)ポケモン向けの画像認識を使ったNintendoSwitchの操作を自動化するためのソフトです。
Switch用なので「HDMIキャプチャによる映像取り込み」と「Arduinoによる疑似プロコン操作」によってそれを可能にしています。
tangential-star.hatenablog.jp
これをVC版ポケモンで使うための課題は
3DSの画面をカメラ映像として取り込む方法」
「疑似プロコン向けの操作コマンドを3DSに送る方法」
の2つがあります。

前者は「Webカメラで直撮りという方法」もありますが画像認識(キャプ画像と照らし合わせる方式)と相性が悪いので「偽トロでPCに取り込む方法」をとることにしました。
ですが偽トロは専用ソフトで取り込む為、そのままではPoke-Controllerが取り込んでくれません。なのでゲーム配信に使われるOBS+VirtualCamを経由させてカメラ映像として取り込む方法を使い解決ました。
(偽トロについては公式サイトを参照。オプティマイズ(電子工作・ゲームハード解析)
(Poke-ControllerはOBSの仮想カメラ機能に対応していないのでVirtualCam導入が必要。参考:【BDSP ID調整】Player BlinkとPoke-Controllerを共存させる|こちゃてす|note])

後者はController Modを組み込んでCGコンに対応させる方法(+Arduinoの疑似GCコン化も必要)もあるようですが日本国内では気軽に入手できるものではないようなのでここでは無しとします。(再現困難な方法はあまり使いたくないので)
例によって「3DSからボタンの電極を抜き出し、フォトカプラ(≒リレー)を使用してボタンを押す方法」を使う事にしました。(この方法にはFC/SFC/N64どころかどのハードにも転用が利く利点があります)
そこで問題となるのはどうやってボタン信号を取り出すかです。
基本的にどこもシリアル通信(PC-シリアル変換アダプタ間、シリアル変換アダプタ-Arduino間、Arduino-Switch間)なのでパラレル通信(各ボタンへの信号)にどうにかして変える必要があります。
そこで目が付くのがArduino部分。Arduinoにはデジタル出力(2~13の12個)があるのでそこから各ボタンへ信号を送ることができます。
問題はArduinoでシリアル通信を解読するプログラムをどうするのかです。0から書くというのはなかなか難しいです。
そこでArduinoに書き込む疑似プロコンのプログラム("Poke-ControllerForLeonardo.inoと同_Func.ino")に追加で「本来のボタン入力に連動してデジタル出力を行うプログラム」を書き加えました。
これで得たデジタル出力をフォトカプラに入力して、出力を3DSのボタンの電極に接続。それをボタンの数だけ用意する。
これで操作コマンドを3DSに送る方法が解決しました。

・ブレッドボードでの試作機

・専用基板での実用機
シリアル変換アダプタの「5V」をLeonardoの「VIN」に繋いで電源供給しています。

あとは通常のPoke-Controller同様にプログラムを制作するだけです。
注意点:十字キーを使うのでHatのimportが必要です。~.pyの5行目「from Commands.Keys import KeyPress, Button, Direction, Stick, Hat

・書き加えたプラグラム

"Poke-ControllerForLeonardo.ino"への追加プログラム

void setup() {
Serial1_Init();//RX←0でのシリアル通信
Controller_Init();//コントローラーの準備
※斜体は元々のプログラム
pinMode(13,OUTPUT); // A
pinMode(12,OUTPUT); // B
pinMode(11,OUTPUT); // X(3DSのSTARTへ)
pinMode(10,OUTPUT); // Y(3DSのSELECTへ)
pinMode(9,OUTPUT); // 未定
pinMode(8,OUTPUT); // 未定
pinMode(7,OUTPUT); // 未定
pinMode(6,OUTPUT); // 未定
pinMode(5,OUTPUT); // 上
pinMode(4,OUTPUT); // 下
pinMode(3,OUTPUT); // 左
pinMode(2,OUTPUT); // 右
digitalWrite(13,LOW); // 初期化
digitalWrite(12,LOW);
digitalWrite(11,LOW);
digitalWrite(10,LOW);
digitalWrite(9,LOW);
digitalWrite(8,LOW);
digitalWrite(7,LOW);
digitalWrite(6,LOW);
digitalWrite(5,LOW);
digitalWrite(4,LOW);
digitalWrite(3,LOW);
digitalWrite(2,LOW); // 追加ここまで
}

"Poke-ControllerForLeonardo_Func.ino"への追加プログラム

pc_report.Hat = hat;
switch (hat) // 追加部分ここから
{
case 0x08: // 押してない時リセット
for (int i=2; i <= 5; i++){
digitalWrite(i,LOW);
}
break;

case 0x06:
digitalWrite(3,HIGH);// 左
break;

case 0x04:
digitalWrite(4,HIGH);// 下
break;

case 0x02:
digitalWrite(2,HIGH);// 右
break;

case 0x00:
digitalWrite(5,HIGH);// 上
break;

default:
break;
} // 追加部分ここまで

"Poke-ControllerForLeonardo_Func.ino"への追加プログラム(2)

pc_report.Button |= p_btns;
switch (p_btns) // 追加部分ここから
{
case 0x00: // 押してない時リセット
for (int i=10; i <= 13; i++){
digitalWrite(i,LOW);
}
break;

case 0x01:
digitalWrite(10,HIGH);// Y = SELECT扱い
break;

case 0x02:
digitalWrite(12,HIGH);// B
break;

case 0x04:
digitalWrite(13,HIGH);// A
break;

case 0x08:
digitalWrite(11,HIGH);// X = START扱い
break;

case 0x0F: // 15=1+2+4+8同時押し
digitalWrite(13,HIGH);// A
digitalWrite(12,HIGH);// B
digitalWrite(11,HIGH);// X = START
digitalWrite(10,HIGH);// Y = SELECT
break;

default:
break;
} // 追加部分ここまで