beatmania IIDX INFINITASの専用コントローラーの1つ、プレミアムモデルの中身をマイコンを使って自作します。
外装の自作はありません。
前提
近所に住む@KOZiROさんから「中身がない、ガワだけのがプレミアムモデルあるんだけど遊べるようにできないだろうか?」との打診。
コントローラー自作で一番大変な、皿などは既にある状態なので、基板・配線・プログラムだけ用意すれば動きそうということで取り組んでみることに。
ついでにBMSなどでも使えるように、皿の出力をPS2専コンのようなデジタル出力にモード切り替えできるようにもしてみる。
プレミアムモデルの仕様調査
KOZiROさんが純正のプレミアムモデルもお持ちだったので、お借りして仕様を調査。
ボタンの仕様
ボタン1~ボタン7:そのまま1鍵~7鍵
ボタン8:未接続?
ボタン9~ボタン12:E1~E4
LEDは6V駆動のものを使用。
ターンテーブルの仕様
内部のギアは100枚。3.6℃で反応する計算。
PCから見るとX軸をアナログに回した分だけ移動している。
約2.5周(歯車256回分)回すとオーバーフローして反対側に戻るので、おそらく解像度は「-128~+127」の256段階(8bit)。
時計回りで減算、反時計回りで加算する。
ターンテーブルのセンサー
SHINKOH ELECS.の刻印があるので新光電子株式会社製。
KI1300シリーズと思われる。
ユニテクのUI1900シリーズも同等に利用できそう。
5V駆動で端子はXHコネクタの3ピン。
5鍵やIIDXのACで利用しているセンサーはNH3ピンのもので、幅(ギャップ)が狭いためそのまま流用は難しそう。
USB デバイスID
こちらの記事によればUSBのVID/PIDが一致していれば公式コントローラーとして認識するらしい。
ArduinoのUSB IDは変更が可能なので以下の手順を参考に、公式コンに偽装する。
Arduino Micro (ATmega 32U4) のUSBデバイスIDを変更する
C:\Program Files (x86)\Arduino\hardware\arduino\avr\boards.txt
VID:0x1CCF / PID:0x8048
ちなみにエントリーコンはPID:0x1018だったけど、ボタン数少ないコントローラーに偽装する意味が無いので無視。
用意する物
種別 | 品目 | 値段 | 備考 |
---|---|---|---|
マイコン | Pro micro | 約700円 | Ali Expressの安物。 Arduino Leonardoなどの同等品。 |
皿センサー | KI1301-AALF | 484円 | 左右で2つ使用。 個人向けに売ってるところが見つからない。 |
基板 | ユニバーサル基板 | 約60円 | 大きさは秋月のC基板くらい。 |
皿用ケーブル | 6芯ケーブル | 約120円 | 5V/L信号/GND/5V/R信号/GNDで6本。 ほんとはAWG24くらいの方が圧着しやすい。 |
ボタン用ケーブル | 4芯ケーブル | 約500円 | 各ボタン毎に 信号線・GND・LED5V・LED制御線 の4本を配線。 |
中継ケーブル | 10芯ケーブル | 約100円 | マイコン基板と中継基板の接続用。 |
中継基板 | 自作 | 約200円 | こんなこともあろうかと昔設計して発注しておいた コントローラー配線用中継基板。 ボタンの信号とLEDをそれぞれ配線できる。 |
皿センサー用コネクタ | XH 3ピン | 約16円 | ハウジング・コンタクト・ポスト込みの概算。 |
皿センサー基板側 | NH 6ピン | 約100円 | 同上。 |
各ボタン用コネクタ | XH 4ピン | 約200円 | 同上。 |
中継用コネクタ | XH 10ピン | 約240円 | 同上。 |
皿LED用コネクタ | XH 2ピン | 約14円 | 同上。 |
ボタン用端子 | 平型端子#250 | 約300円 | いわゆるファストン端子。 信号分は#187でもいいが、 LEDと共用のため全て#250を採用。 |
合計 | 約3000円 | メンテナンス性を犠牲にしてコネクタなど 使わなければもっと安くなる。 |
基本的に家に転がってるものを使用したので実際の出費は少ない。
センサーだけはAC5鍵用のしかなかったので新たに購入した。
個人向けに販売しているサイトがどうしても見つからなかったのでKOZiROさんちの名義(自営業)で代理購入していただいた。
回路図
LEDの配線は、12VのT10LEDを使う場合も想定して逆流防止のダイオードを挿入。
ただ今回は6V用のLEDを5Vで駆動させてるので無くてもよかったかも…。
E1~E4の配線は丁度いいコネクタが手元になかったのでXH4ピンにGND以外同居。GNDは他の場所から伸ばして分岐させた。
ボタン・LEDの10ピンコネクタには、自作の中継基板を繋いでいる。
メンテナンス性向上のためにやっているだけなので直結でも問題ない。
中継基板
ボタン9個分の信号とLEDの配線を、各ボタンの4ピンに振り分けるだけのシンプルな基板。
PHOENIXWANと同じピンアサインにしてるので配線の流用が可能なはず。
コントローラー自作繰り返してたら、ユニバーサル基板で何度もこの部分を実装するのが辛くなったので発注したもの。(ただ結局コネクタの圧着がたくさんあるので負担は余り減らなかった…)
配線
ボタン部配線
どの作業よりも精神的に過酷な70回以上のカシメ作業…。
圧着警察に見られたら怒られます。
動けばいいのよ。動かなければ直せばいいのよ。
PC接続部
Pro MicroのUSBコネクタがビックリするくらいもげやすいのでハンダを盛って補強&延長ケーブルで着脱の負荷を軽減。
コントローラー自体のケーブルも着脱できるように基板側の延長ケーブルは短めにして、PCへは別の長いケーブルを接続。
配線全体像
メンテナンス時に天板をひっくり返しても届くよう、配線を長めにしましたがちょっと収まりが悪かったかも。
スケッチ(プログラム)
#include "HID-Project.h"
const int key[12] = {
2, 3, 4, 5, 6, 7, 8, // Key1-Key7
9, // NC
18, 19, 20, 21 // E1-E4
};
const int TT_R = 0; // TurnTable Sensor Right
const int TT_L = 1; // TurnTable Sensor Left
char TT = 0; // TurnTable Value
const unsigned long LONG_PRESS_TIME = 1000; // Long press duration (msec)
unsigned long MODE_end_time = 0; // Long press end time
bool mode_inf = true; // Infinitas Mode OR LR2 Mode
const unsigned long TT_DELAY = 133; // Turntable Duration (msec)
unsigned long TT_end_time = 0; // Turntable end time
void setup() {
// Keys Setup
for (int i = 0; i < 12; i++) {
pinMode(key[i], INPUT_PULLUP);
}
// TT Setup
pinMode(TT_R, INPUT_PULLUP);
pinMode(TT_L, INPUT_PULLUP);
// TT Interrupt Setup
// 0:pin3 1:pin2 2:pin0 3:pin1 4:pin7
attachInterrupt(2, TTcount, RISING); // Call TTcount when Rising 2=pin0=TT_R
// Sends a clean report to the host. This is important on any Arduino type.
Gamepad.begin();
}
void loop() {
// Read Keys
for (int i = 0; i < 12; i++) {
if (!digitalRead(key[i])) {
Gamepad.press(i + 1);
} else {
Gamepad.release(i + 1);
}
}
// Set TT value
if (mode_inf) {
// Infinitas Mode
Gamepad.xAxis(TT * 256); // IIDX value is 8bit, xAxis is 16bit
Gamepad.yAxis(0);
} else {
// LR2 Mode
Gamepad.xAxis(0);
if (TT_end_time > millis()) {
// The opening time has not yet been reached.
if (TT == 1) {
Gamepad.yAxis(32767);
} else if (TT == -1) {
Gamepad.yAxis(-32768);
}
} else {
// Reset
Gamepad.yAxis(0);
TT = 0;
TT_end_time = 0;
}
}
// Send Gamepad
Gamepad.write();
// Mode switch
if (!digitalRead(key[8]) && !digitalRead(key[9]) && !digitalRead(key[10]) && !digitalRead(key[11])) {
// E1-E4 are pressed.
if (MODE_end_time == 0) {
// Init
MODE_end_time = millis() + LONG_PRESS_TIME; // Set end time
} else if (MODE_end_time < millis()) {
// End time is exceeded, switch mode
mode_inf = !mode_inf;
// Reset end time
MODE_end_time = 0;
}
// Otherwise, pass.
} else {
// Keys released
// Reset end time
MODE_end_time = 0;
}
}
void TTcount() {
if (mode_inf) {
// Infinitas mode
if (digitalRead(TT_L)) {
TT--;
} else {
TT++;
}
} else {
// LR2 mode
if (digitalRead(TT_L)) {
TT = 1;
} else {
TT = -1;
}
// Set turntable end time
TT_end_time = millis() + TT_DELAY;
}
}
解説
ボタンの読み取りは配列でfor文に集約。
ターンテーブルは割り込み処理にし、片方の立ち上がりエッジを検出した際に関数を呼び出す。
関数内では、もう片方のセンサーがHIGHかLOWかで回転方向を判断し、加減算の処理を実施。
回転方向検出の仕組みについての詳細は、当ブログの記事を参照のこと。
E1~E4を1秒間同時押ししたらモード切替を行う。
切り替え後のターンテーブルは、Infinitas用の絶対値でのアナログ制御ではなく、PS2専コンのように回して一定の間上下どちらかに信号を送信するモードになる。
送信時間は押し押しがギリギリまではいる8F程度とし、133msに設定した。
完成
想定通り、本家と遜色なく動いてるように見受けられたので引き渡しを実施。
実際に動作テストをしている様子。(動画内ではE1~E4のアサインを間違えていたので1つズレている)
モード切替もバッチリ。
INFINITASモード(X軸のアナログ移動)と、BMSモード(Y軸のデジタル移動)を正しく切り替えられています。
(画面解像度の問題で軸の移動が見づらいです)
コメント
コメント失礼します。
専コン自作を考えていてこちらのサイトにたどり着きました。
自分もメンテナンス性向上の為に中継基板を発注しようかと思ったのですが、自分のスキルが足りずプリントデータの作成、発注まで行けてません。
もしよければ記事の途中にある「中継基板」の予備があればいくらかのお値段で譲っていただけませんか?
ありがとうございます。
予備はあるのですが人に渡すことを想定していなかったので頒布と実装の説明が準備でき次第またご連絡します。
ご返事ありがとうございます。
お時間のある時でゆっくりで大丈夫です。