3DS外部操作拡張改造基板「Qingpi」の紹介

この記事は[Pokémon Past Generation Advent Calendar 2023](https://adventar.org/calendars/8746) 23日目の記事です。昨日はきゆうりさんによる「[RS乱数　ポケモンボックス使用　メソッド4 勇敢5VS0グラードン](https://kyuri24gtr.hatenablog.com/entry/2023/12/22/000000)」、明日はpoe/ぽえさんによる「ポケモンフェスタ2003ルールの構築記事」の予定です。

時間が足りずポケモン要素が消失しています、本当にすみません……　のちほど加筆します。

## はじめに

「[Qingpi](https://github.com/U-1F992/Qingpi)」は、3DSに外部操作機能を追加するRP2040搭載マイコンボード／フレキシブル基板／ファームウェア／付属アプリケーションを含むプロジェクトです。この記事の公開をもってリリースとし、実装済み基板の販売を開始します。

https://u-1f992.booth.pm/items/5365222

USB Type-Cコネクタを通じてPCとシリアル通信を行い、電源や無線切り替えを含む本体上の全ボタン／スライドパッド／タッチスクリーン操作を提供します。特にタッチスクリーン操作は先行プロジェクトにはない独自設計であり、優れた安定性とノンブロッキングの押下～開放操作を実現しています。通信内容は簡潔でアプリケーション開発が容易であるほか、既存のSwitch自動化アプリケーションとの互換性も考慮しています。

構想から丸2年、開発に手をつけてから約8か月の間、ハード・ソフトともにほとんどゼロから多くのことを学び、できる限りこのプロジェクトに活かしてきたつもりです。この記事では、そんなQingpiのこだわりポイントを紹介します。

## 開発の経緯

Qingpiは、[Controller Mod](https://3dscapture.com/controller/) by loopyおよび[dekuNukem/3xtDS](https://github.com/dekuNukem/3xtDS)を参考にフルスクラッチで開発されました。先達のプロジェクトを紹介し、Qingpiの開発に至った経緯を説明します。

### Controller Mod

Controller Modは、3DSにゲームキューブコントローラー等を接続する拡張改造基板です。私はまずController Mod＋偽トロ搭載3DSを入手し、GC自動化の要領でこれを外部操作していました。これは上手く働き、細密な画像認識と非常に長い繰り返し操作を必要とするORASのID調整を成功させることができました。

https://x.com/U_1F992/status/1567108757808889856

しかし、本格的にゲームを遊ぶ上でController Modはあまり実用的ではないと気づくのにそれほど時間はかかりませんでした。DSシリーズの主要な特徴は下画面のタッチスクリーンです。多くのゲームはタッチ操作を織り込んだインタラクションに趣向を凝らしており、ボタンとスライドパッド操作のみでは明らかに不足だったのです。

移植性の低さにも頭を悩ませました。ほとんどチップ一つで動作するPSoCは、たしかに既製品に寄生させるためには最適な選択のひとつです。ただし、ハード・ソフトの両面で替えが効かないものでもあります。折しも半導体不足のあおりを受けてか、Controller Modに採用されているPSoCはほとんど入手不能の状態が続いていました（現在はある程度在庫が回復しています）。

### 3xtDS

タッチ操作を実現しており、一般的なボードとファームウェアを用いているプロジェクトとして注目したのが3xtDSです。本体に組み込むことは想定されていないものの、Arduino（Teensy）を用いておりドキュメントもよくまとまっていたため、実際にRaspberry Pi Picoで再現して3DS外部操作の理解に大いに役に立ちました。

しかしやはり、タッチスクリーンの操作には不満が残りました。3xtDSのタッチスクリーン操作関数はポーリングを必要とするためブロッキング操作になっています（割り込みも試しましたがタイミングがシビアで上手くいきませんでした。RP2040であれば、2コア使ってノンブロッキングにすること自体は可能です）。信号の都合上、画面上端付近のタッチ入力継続は安定しないという課題もありました。3xtDSのドキュメントを参考にしつつタッチスクリーン操作の新規回路を考案し始めたことが、Qingpiの開発開始の直接のきっかけとなりました。

以上を経て、タッチスクリーンまで対応し、可能な限り移植性を考慮した一般的な部品とファームウェアを用いて、本体に組み込めるサイズまで凝縮することを目指したのが、Qingpiです。

## PCB

Qingpiの本体基板は、RP2040と最小限の必要なIC搭載したマイコンボードです。当初はプリント基板設計どころか電子回路の基礎も知りませんでしたが、LEDのみの基板からはじめて自作小型マイコンボードまで、都度改善しながら最善は尽くしました。

![](https://github.com/U-1F992/qingpi/blob/main/pcb/pcb.jpg?raw=true)

基本的なところでは、電源には太い配線を通し、GNDビアは多めに開けています。USB差動インピーダンスを考慮し、D+/D-は極太の配線になりました。デバッグに使用するピンとstdin/stdoutにあたるピンはスルーホールで引き出しています。こんな感じの[テストワイヤー](https://shop.sunhayato.co.jp/products/ttw-203)を使用して簡単にアクセスできます。

電源は実績のある[Raspberry Pi Debug Probe](https://www.raspberrypi.com/documentation/microcontrollers/debug-probe.html)を参考に部品選定しています。USB未接続時にもQingpiを起動しないと本体側のスライドパッド操作ができなくなってしまうので3DSから5Vを拝借していますが、3DSと繋がないと起動しないのでは開発中に困ります。USBからも供給できるように、双方にショットキーバリアダイオードを配置して、逆流しないように配慮しています。

些細なことですが、外部操作には不要のLEDもついていると基板の動作確認で安心できることを学びました。はじめはただの緑色LEDでしたが、抵抗値の決め方を理解したついでに調子にのってRGB LEDを搭載することにしました。特に実用性はありませんが、操作に応じて七色に光るのは楽しいです。

https://twitter.com/U_1F992/status/1737135066088935737

スライドパッド用に電流出力D/Aコンバータ、タッチスクリーン用にデジタルポテンショメータとCMOSアナログスイッチを搭載しています。D/AコンバータはController Modを参考に選定し、タッチスクリーンは独自設計です。これらの部品には特に代替品がないのですが、執筆時点では入手が容易なので許容としています。

スライドパッドは、Controller ModではPSoCのIDACを使用しています。内蔵のスライドパッドを取り外せば通常の電圧出力D/AコンバータやPWMでも制御できますが、残すためにはシンク・ソース両方に対応した電流出力のD/Aコンバータが必要[^1]です。

[^1]: https://3dscapture.com/forum/viewtopic.php?t=5400&start=50

タッチスクリーンは、回路的には2基のポテンショメータのワイパー同士をリレーでon/offしている状態と等価です。Qingpiでは、同様の回路をボードに構築しもとのタッチスクリーンと並列に接続することで、外部操作を実現しています。ワイパーの抵抗成分によって画面端ギリギリのタッチは要キャリブレーションであるものの、元のタッチと回路的には同じであるため安定している点と、押下～解放の間マイコン側の処理を必要としない点が有効です。

（後日画像追加予定）

製造面では、マイコンに必須の部品と各種IC、コネクタを備えながら2層の片面基板に収めたことも重要でした。PCBおよびPCBAが安価になりますし、自前で生産することも可能です。執筆時点では一部の部品をJLCPCB（LCSC）のPCBAで揃えられないので、PCBのみ注文して、残りは私が人力チップマウンタになって自宅で製造しています。手作業で製造するならソルダーレジストの色を選んでも割高にならないので、紫にしてみました。

## フレキシブル基板（FPC）

本体基板と3DS基板の間はフレキシブル基板によって接続します。Controller Modにせよ3xtDSにせよ、はんだ付け自体はさほど難しくはないものの、大量のケーブルをどのように取りまわすかが問題でした。フレキシブル基板を用いることで、この問題を一度に解決できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1550728/d1594af8-c76b-1237-88b6-f54459d85f7c.png)

偽トロのようにFPCと本体基板を直接はんだ付けするのではなく、本体基板にFPCコネクタを搭載したこともポイントです。これにより、1台の3DSに確実にはんだ付けできれば本体基板を取り換えて動作確認を行えます。執筆時点では3DS LL用のFPCしか用意できていませんが、接続部分さえ合わせてもらえれば、他の機種でも対応できます。

市販品には一般的なことですが、コネクタとの接続部にはポリイミドの補強板を付けています。PCBとは違うFPCの独特なところで、勉強になりました。他には、既存のフレキシブル基板を参考にすべての配線を曲線にして、基板の角も丸めています。これもFPCの独特な工夫かもしれません。

3DS LL用フレキシブル基板では、偽トロとの共存を想定して、原則として偽トロと干渉しないように配線しています。ただし、偽トロのフレキの下に隠れるTPはあります。従ってQingpiは偽トロより先に取り付ける必要があります。

## ファームウェア

ファームウェアの大枠はArduinoフレームワークで記述していますが、SOLID原則を意識した†オブジェクト指向完全に理解した†構成になっています。

大まかには、Arduinoや各種ICに依存しないコアライブラリ`qingpi`、同様に何物にも依存せずバイナリ通信の解析を担う`nx_automation_meta_firmware`ライブラリ、Arduino-APIに依存し各種ICに対応するライブラリ群、そしてメインルーチン部とアダプター群に分かれています。

### qingpi

Qingpiのコアライブラリで、3DSの外部操作を担います。

`QpiButton`、`QpiHat`、`QpiSlidePad`、`QpiTouchScreen`のコンクリートクラス（構造体）は、`QpiXxxInterface`と名付けられたインターフェイス（関数ポインタをメンバに持つ構造体）によって組み立てられており、利用側にこれらの実装を求めます。デジタルポテンショメータやD/Aコンバータはもちろんのこと、GPIOに至るまで抽象化することで、徹底してArduinoに依存させないようにしています。Controller Modのファームウェアを読むのに苦労したことに由来するこだわりです。

qingpiはC言語（C99）で書かれています。実行時コストをコードから把握しやすくなり、マイコン間の移植性も高まる想定です。以下の書籍が大いに参考になりました。

https://asciidwango.jp/post/183284754310/%E3%83%A2%E3%83%80%E3%83%B3c%E8%A8%80%E8%AA%9E%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0

ついでに単体テストも可能になりました。常時テストが通るようにしていたこの部分は完成版にファームウェアを書き込んだ際にも一発で正常に動作したので、品質にもよい影響があったと言ってよいでしょう。

### nx_automation_meta_firmware

元はRaspberry Pi Picoでゲームキューブコントローラーの機能を実現するファームウェア「[Jiangtun](https://github.com/U-1F992/Jiangtun)」と共に開発していたライブラリです。どこかから（今回はUSB UARTから）受け取ったバイト列をあるルールに従ってコントローラー操作としてパースする枠組みを提供しています。

ここでのルールとは`NxamfProtocolInterface`の実装のことを指します。現在主力の自動化ホストアプリケーション[Poke-Controller MODIFIED](https://github.com/Moi-poke/Poke-Controller-Modified)および[NX Macro Controller](https://blog.bzl-web.com/entry/2020/01/20/165719)用のルール実装、加えて複数のルールを束ねて先に成立したものから取り出すルール実装を同梱しています。

JiangtunはこのライブラリによってPokeCon/NXMCの両方に対応していますが、Qingpiも同様に両対応となっています。将来的にルールを追加実装することも容易です。

## Pythonライブラリ

PC側で利用するPythonライブラリ`qingpi-python`を同梱しています。NXMC互換のシリアル通信にタッチスクリーン操作を加えた簡単なものです。スタンドアロンの自動化スクリプトを記述するのに役立ちます。

既存のどのライブラリよりも簡潔な書き味を目指しています。`hold, release = qingpi.init(Writer)`によって入力用の`hold`関数、解放用の`release`関数を取得します。肝はこれだけです。

`Writer`には基本的に`Serial`のインスタンスを渡すわけですが、ただの`write`メソッドのみを要求するProtocolなのでテストも簡単になっています。PokeConではコマンドクラスの最奥から`Serial`のインスタンスを引っこ抜けば、`qingpi`ライブラリのタッチスクリーン操作を使えます。

インポートはよしなにしていただければよいですが、おすすめは

```
import qingpi
from qingpi import *
```

です。各種定数は`qingpi`に直下に置かれていますので、Aボタンを1秒間ホールドしたければ

```python
import serial

import qingpi
from qingpi import *


hold, release = qingpi.init(serial.Serial())

hold(A)
time.sleep(1000)
release(A)
```

としてください。

ハットやスライドパッド、タッチスクリーンも同様に`hold`で入力します。`release`にはクラス名を指定します。

```python
hold(UP)
release(Hat)

hold(SlidePad(x, y))
release(SlidePad)

hold(TouchScreen(x, y))
release(TouchScreen)
```

`release`に`Button`を指定すればすべてのボタンを離しますし、何も与えなければ全ての操作を取りやめます。

```python
release(Button)
release()
```

`hold`、`release`ともに可変長の引数を取ります。同時入力/解放も簡単です。

```python
hold(A, B, UP, SlidePad(x, y), TouchScreen(x, y))
release(A, UP, TouchScreen)
```

終了処理はとくに必要ありません。ただし終了時も操作のリセットはしません。

## GUIアプリケーション

先述の`qingpi-python`ライブラリにはGUIアプリケーションが付属しています。現状Windows専用です。exe版も用意しています。

https://github.com/U-1F992/qingpi/releases/tag/qingpi-python_v0.1.8

Tkinterで透明なウィンドウを描画するだけのシンプルなアプリケーションです。README.mdに記載しているボタンマッピングは[Citra](https://citra-emu.org/)準拠となっています。ウィンドウ内をクリックするとタッチ操作になります。+/-キーでサイズを調整し、偽トロビュアーに重ねるなどして使うとよいでしょう。

https://twitter.com/U_1F992/status/1737875644980351037

## サンプル：DPtポフィンづくりの自動化

偽トロ＋Qingpi改造を活用した一例として、『ポケットモンスター　ダイヤモンド・パール・プラチナ』におけるポフィンづくりの自動化を紹介します。ポケットモンスターシリーズのDSデビュー作であるDPtでは、タッチ操作を交えた象徴的なミニゲームがいくつか収録されています。中盤の街ヨスガシティのりょうりハウスでは、下画面に表示される鍋をかき混ぜてきのみを料理できます。

https://gist.github.com/U-1F992/aa3c8c31dbb1b0be82cbb4190e899509

かき混ぜと向きの切り替えを別プロセスで行っているだけです。スピードの調節を実装する前に力尽きました。ぼろぼろこぼしながら最高速度でかき混ぜ続けます。

https://twitter.com/U_1F992/status/1742490730353021282

## 未実装の機能

Controller Modでは赤外線通信をエミュレートして拡張スライドパッドを操作できます（[対応ソフト一覧](https://www.nintendo.co.jp/hardware/3dsseries/accessories/list_slidepad/index.html)）。しかし、Qingpiではまだこの機能を実装できていません。

通信の物理層は把握しており、RP2040のPIOを用いて実現できることを確認しているのですが、自分が必要としていないのでモチベーションがあまりないところです。

## おわりに

Qingpiには、ハードウェア／ソフトウェアともに数多くのこだわりが詰め込まれたオープンソースプロジェクトです。当面は私が手作業でチップ部品を載せて製造しているので、納期2週間の受注生産でこの価格設定ですが、最小ロット製造するよりは安く済むはずです。ぜひお手に取ってください。

https://u-1f992.booth.pm/items/5365222