Arduino Leonardoで自作IIDXコンを作ってみる まとめ(無理だったのでSDVXにした)
まえがき
このブログは無料で読むことができます。わからないことがあれば、Google,Apple,Twitterアカウントを使ってログインができますので、ログインしてコメントを残していただければ、分かる範囲でお答えします。
製作の動機
公式のIIDXコントローラーが高かったり入手難
エントリーモデルはアーケードと仕様が異なる
そういった事を解決しつつ、単純に自作したいという欲があったので
自作をしてみました
結論
結論としては自作に至るまで加工技術的なハードルがあり、作成には至りませんでしたが、それまでの事柄を備忘録として残しておきます。
しかし、SDVXのコントローラーに活かすことができそうです。
こちらに記事を書き残しております。
2023/01/24現在執筆中です。完成までしばしお待ちを…
ロータリーエンコーダーが2月まで入荷しそうにありません。
2023/02/10入荷しました!
おもに使用したもの
・(試作品用に)丸ボタン(OBSF-30/24)
OBSA-45UK-W-LN(アーケード版のボタン)
JoyStickライブラリ
HID
flexitimer2
試作品
はじめの一歩として
スクラッチのない7鍵+START+SELECTボタンの試作品をつくりました。
家族が金属加工会社に勤めているので家族に頼んで、アルミ板を加工してもらいました。
アルミ版にボタンをはめ込み、土台になる木を固定したものが
こちらです。
試作品にコードを書いてみる
そしてArduino LeonardoにJoystickライブラリをつかってゲームパッドとしてPCに認識させ、Lunatic Rave 2にデフォルトで設定されているものをプログラムにして書き込みました。
とりあえず試作品なのでif文をつかって書いてみます。
//1鍵
if (!digitalRead(0) == HIGH) //0ピンがTRUE
{
Joystick.setButton(3, HIGH);
}
else if (!digitalRead(0) == LOW) //0ピンがFALSE
{
Joystick.setButton(3, LOW);
}
//--こんなコードが7鍵まで続きます....
1KEY:Button4
2KEY:Button7
3KEY:Button3
4KEY:Button8
5KEY:Button2
6KEY:Button5
7KEY:押すとX軸が0 離すとX軸が127
START:Button9
SELECT:Button10
スクラッチ時計回り:Y軸が0
スクラッチニュートラル:Y軸が127
スクラッチ反時計回り:Y軸が255
となっています。7KEYだけなんか特殊ですね。
#メモ
PS2のボタンアサイン
△1ボタン
○ 2ボタン 5鍵
✕3ボタン 3鍵
□4ボタン 1鍵
L2 5ボタン 6鍵
R2 6ボタン
L1 7ボタン 2鍵
R1 8ボタン 4鍵
スタート9ボタン
セレクト10ボタン
デジタル時
十字キーX 軸7鍵 押したとき0:離したとき127
y 軸 時計回り0 ニュートラル127 反時計回り255
アナログ時
十字キー ハットスイッチPOV
左 XY軸
右 z回転 z軸
L3 11ボタン
R3 12ボタン
試作品に違和感...
この自作コンで遊んでみたのですが、なんか...すごく扱いにくいんです。
丸ボタンを使っていて慣れないというのもありますがとても入力に遅延を感じます。
私が下手というのもありますが...とても黄グレが多いです。
キーボードでやってるときはそんなでもないのに...
調べているとこちらの動画内で同じようなことをしていて
Arduino Leonardoをつかっているのです!!!
この方の動画では2:04のところで遅延について解説されています。
同じようにプログラムを書き直してみました。
遅延が減ったか・・・あれれ?わからん。
遅延が減ったのかよくわかりません。
Hate氏の嘆きの樹8Bitのピカグレは220前後から260前後になったのですが
自分の慣れなのか遅延が減ったからなのかさっぱりわかりません。
IIDXをやっている仲間が少なく、チュウニズムやSDVXの比率が多い、地元のゲームセンターにIIDXがないなど
遅延はなくなったのか自分には断定できませんでした。
ピカグレが増えて黃グレが減っているので遅延は減ったと思います()
スクラッチの試作品を作ってみる
まず、IIDXのコントローラーのスクラッチには
「歯車」と「フォトインタラプタ」という部品がつかわれているようです。
それについてはこれらのサイトで詳しく解説されています。
読んでみました。
ふむふむ。なるほど、さっぱりわからん。
わかんないので調べました。
①2つのフォトインタラプタをつかって歯車を通して信号を受信する。
②2つの信号は90度のズレがある矩形波の信号になる
③信号をA相、B相として見たときに
どちらの信号を先に受信するかで回転方向を判断する。
という仕組みらしい。
はぁ...考えた人最強か。天才の考えることはわからん。
更に調べていくとこれは
ロータリーエンコーダーを構成している
ということが判明。(解釈あってるかな?)
ロータリーエンコーダーはこちらの画像のような仕組みになっています
これ使えばフォトインタラプタなんて使わなくてもいいんじゃないか?
と思ったので秋月電子でロータリーエンコーダーを買いました。
→後にインタラプタを使うのは設計上必要になってることが判明。
ロータリーエンコーダーには
・インクリメンタル式
・アブソリュート式
という二種類があります。
インクリメンタル式にも種類があり
・クリックタイプ
・ノンクリックタイプ
という2種類があります。
さらに仕組みとして
・光学式
・機械式
というものもあるようです。
今回秋月電子で購入したのは
インクリメンタル式、ノンクリックタイプ、機械式です
後に別のエンコーダーを買いました
Arduinoにロータリーエンコーダーを接続し、値を取得してみました。
値の取得はこちらのサイトが参考になります。
スクラッチの試作品にコードを書いてみる
まず上記のサイトに有るコードをもとにピン変化割り込みを使ってエンコーダーの状態を取得します。
const int ENC_A = 2;
const int ENC_B = 3;
void setup() {
pinMode(ENC_A, INPUT_PULLUP);//内部プルアップ使用
pinMode(ENC_B, INPUT_PULLUP);//内部プルアップ使用
attachInterrupt(0, ENC_READ, CHANGE);//ピン変化割込みの宣言
attachInterrupt(1, ENC_READ, CHANGE);//ピン変化割込みの宣言 詳しくはリファレンスへ
Serial.begin(38400);
}
void loop()
{
}
void ENC_READ() {
bool cur[2];
cur[0] = !digitalRead(ENC_A);
cur[1] = !digitalRead(ENC_B);
Serial.print("A:");
Serial.print(cur[0]);
Serial.print(" ");
Serial.print("B:");
Serial.print(cur[1]);
Serial.println();
}
このコードを書き込み、シリアルモニタで観察...
ちゃんとA相、B相がでています。
成功!
ところでなぜNOT演算子がはいっているのでしょうか。
cur[0] = !digitalRead(ENC_A);
cur[1] = !digitalRead(ENC_B);
否定を外してやってみるとわかりますよ。
内部プルアップをすると否定演算子が必要になります。
HIGHとLOWが逆になるからですね。
まぁ、ロータリーエンコーダーの場合は
否定を入れようが入れまいが回転方向はわかりますが...
そしてこちらがロータリーエンコーダーのテスト動画になります。
入力がされたときは感動しました。
先人の知恵がないと私には作れません...
本当にありがとうございます。
キーボード入力かコントローラーか
書いているうちにわからなくなってきて、
スミマセン、キーボード入力は断念しました。
どのキーボード入力ライブラリでも6つまでの同時押しが限界でIIDXは最大8キー同時押しするので、無理そうです。
しかしライブラリの一部を改変することで解決はできそうです。
でも私の環境では、改変するとキーが打てなくなったので、とりあえずコントローラーとして認識させるのが良さそうです。
6キーまでしか同時押しできないのはUSBの仕様らしいので、本当にコントローラーが最適解のようなんですね。
でも諦められないので調べてたらなんかありました
HID-Projectの中にNキーロールオーバーキーボードの関数がありました
こちらのサイトを参考にしました
参考にしつつコードをカキカキ...
#include <HID-Project.h>
#include <HID-Settings.h>
void setup() {
pinMode(7, INPUT_PULLUP);//内部プルアップ使用
Serial.begin(38400);
NKROKeyboard.begin();
}
void loop()
{
Serial.println(!digitalRead(7));
if (!digitalRead(7))
{
NKROKeyboard.add('a');
NKROKeyboard.add('b');
NKROKeyboard.add('c');
NKROKeyboard.add('d');
NKROKeyboard.add('e');
NKROKeyboard.add('f');
NKROKeyboard.add('g');
NKROKeyboard.add('h');
NKROKeyboard.add('i');
}
else if (digitalRead(7))
{
NKROKeyboard.remove('a');
NKROKeyboard.remove('b');
NKROKeyboard.remove('c');
NKROKeyboard.remove('d');
NKROKeyboard.remove('e');
NKROKeyboard.remove('f');
NKROKeyboard.remove('g');
NKROKeyboard.remove('h');
NKROKeyboard.remove('i');
}
NKROKeyboard.send();
}
このサイトでテストします
・・・できちゃいましたね、同時押し。
やったぁああああああああああああああああああ!!
(え、でも6キー同時押し制限って仕様じゃないの...?)
ということで、Nキーロールオーバーができることがわかり、キーボードとして認識させることにしました。
試作品がうまくいったので、本格的に作る
試作品のスクラッチやボタンがうまく動いたので、これらのプログラムを組み合わせ、実際に使われているボタンを使用して作ってみましょう。
スクラッチはさすがに売られていませんから、自作をすることになります。
→無理でした。
まずはスイッチですが
このサイトを参考に
アーケードにはOBSA-45UK-W-LNが使われているみたいです
黒鍵にはOBSA-45UK-K/W-LNが使われています
セレクト、スタートの正方形ボタンはOBSA-30UKです。
代わりに使えそうなのもあります。DJDAOの正方形ボタンです。
なお、ボタンの構成はOBSA-45UK-W-LNとスプリング、マイクロスイッチの三点が必要になります。
ボタンだけで1000円
マイクロスイッチ300円
スプリング1200円程度しますから、2300*7で18000円ほどかかってしまいます。
コスパを考えるならGAMO2 オンラインショップで妥協するべきでしょう。
噂程度なのですが、アリエクスプレスでもボタンあるみたいですよ。
Lightning筐体、つまり新筐体では
obsa-45uk-kn279(ホワイト)
obsa-45uk-kn280(スモーク)
が使われているそうですが、検索しても出てきません。
マイクロスイッチは内蔵されて届きますが、かなり硬いボタンです。
硬くてもいいよ!っていう剛力羅さんは追加購入はしなくていいです。
スタート、セレクトはアーケード仕様でなくても実際問題はそこまでないので、丸ボタンでもいいかもしれません。
こちらにアーケードと同じボタン、スプリング、マイクロスイッチを載せておきます。
マイクロスイッチ
D2MV-01-1C1 0.1N
D2MV-01-1C2 0.25N
D2MV-01-1C3 0.49N
VX-01-1C23 0.49N
VX-01-1A3 0.49N
V-10-1A4 0.98N
(N=ニュートン 1N=約102g)
スプリング
OBSA-SP-20 20g
OBSA-SP-40 40g
OBSA-SP-100 100g
これらを組み合わせてボタンが構成されています。
ゲームセンターの店員さんに聞けばホーム台の詳細を教えてくれると思います。
私はスプリングは60gマイクロスイッチは0.49N(50g)のものを使いました。
なお、D2MV-01-1C3は一番重要にも関わらず、生産終了となりました。
後継品はVX-01-1C23だと思われます。なんてこったい
本格的に作るために、またスイッチをはめ込む板が必要になるわけですが
寸法がわかりません....調べましょう
あぁ設計図も書いている人がいらっしゃる.....
先人の知恵がないと作れません…
ありがとうございます…
ボタン構造を調べる
本格的につくるにあたっても試作品は作るべきだと思います。
注文したボタンの寸法を測ります。
とりあえず、46×28くらいの穴を開けた板を用意すればよさそうですね。
こんな感じでしょうか。
先程設計図を載っけましたけど、横浜1ミリ程度誤差がありますね。
まずはダンボールから始めると良いです。
簡単に加工、入手ができるので失敗しても大丈夫だからです。
このときに測ったら28mmではなく29mmであることがわかりました。
次はマイクロスイッチを配線します。
マイクロスイッチには端子があり、平型端子というものを使います。
直接はんだ付けするとメンテナンスがとてもしづらいです。
平型端子は
187型というものを使います。
左が187型
右が250型です。
250型は間違えて購入しました。
マイクロスイッチに装置するとこんな感じになります。
とても硬く、怪我をしそうなので、気をつけましょう。
250型が上で
187型が下です。
一見、250型が正解に見えるのですが、187型が正解です。
設計図を書きました。
あとはこの設計図の通りに木材や鉄板を加工してはめることで鍵盤のほうは作成できます。
しかしスクラッチのほうはロータリーエンコーダーはともかく皿を作成しなくてはいけないため、そこまでの加工技術はなく、鍵盤だけの作成になってしまいました。もしつくるのならば、フォトインタラプタのためのギアを構成する板、皿部分を作る必要がありますね。
少しだけ設計をしてみましたが年単位でつくることになりそうなのでごめんなさい。。。
鍵盤とつまみが売られているSDVXコントローラーなら簡単に作れそうです。そっちに流用します。
スクラッチ皿の作成で思ったのがフォトインタラプタを採用している理由がそういった物を作成するときに面倒だからなのかもしれないと感じました。
さて加工ができないことが分かったし…
ソースコードを考えるほうが先にできそうなので書いてみましょう。
今手元にArduinoがないのでものがない状態で書いていますが、動くでしょうか。試したい...
Noteに結構ソースコードを書いていたので、実物がない状態でもある程度書くことができました。
こういう時にクラウドやインターネットの便利さを感じます。
後日Arduinoに書き込んだら案の定エラーが出ましたが、ほとんど構文エラーでした。
セミコロンとか。
直しておきました。かなり汚いコードですので、そのうち直していきたいです。
#include <FlexiTimer2.h>//FlexiTimer2をインクルード
#include <HID-Project.h>
#include <HID-Settings.h>//HIDProjectをインクルード
////#include <Joystick.h>
class ButtonAssainClass
{
private:
//char Button[11] = { 'z', 's', 'x', 'd', 'c', 'f', 'v', 'q', 'w', KEY_LEFT_SHIFT, KEY_LEFT_CTRL}; ;
public:
char Button[11] = { 'z', 's', 'x', 'd', 'c', 'f', 'v', 'q', 'w', KEY_LEFT_SHIFT, KEY_LEFT_CTRL};
//1,2,3,4,5,6,7,START,SELECT,反時計回り、時計回りの順
char* getButtonAssain() {
return Button;
};
};
ButtonAssainClass Assain;
volatile byte pos;//ここ配列にするとつまみにできると思う
volatile int right, left;//ここ配列にするとつまみにできると思う。
int Mode = 0;
int ModeCount[7] = { 0, 0, 0, 0, 0, 0, 0};
void setup() {
int i = 0;
for (i = 0; i <= 13; i++)
{
pinMode(i, INPUT_PULLUP);//pin0~13までの7つをプルアップで入力
}
NKROKeyboard.begin();//Nキーロールオーバーのキーボードを使用する
FlexiTimer2::set(5, 1.0 / 1000000, TimerIRQ); //タイマー割り込み 引数1:時間 引数2:時間の単位 引数3:割り込む関数
FlexiTimer2::start(); //タイマ割込み実行
Serial.begin(9600); //デバッグ用シリアル通信
}
void loop() {
ModeChange();
KEYStu();//キー
EncoderStu();//エンコーダー
//ロータリーエンコーダーの変数を減らし続ける。0が下限
right -= 1 ;
left -= 1;
if (right <= 0) {
right = 0;
}
if (left <= 0) {
left = 0;
}
NKROKeyboard.send();
}
void KEYStu() {
//7鍵とSTART,SELECTの状態を取得
for (int i = 0 ; i <= 8; i++) ///0,1,2,3,4,5,6,7,8で9個
{
if (!digitalRead(i) == HIGH)//ピンiが押されたら
{
NKROKeyboard.add(Assain.getButtonAssain()[i]);//Assain.Button[i]を押す
}
else if (!digitalRead(i) == LOW)//ピンiが離されたら
{
NKROKeyboard.remove(Assain.getButtonAssain()[i]);////Assain.Button[i]を離す
}
}
}
void EncoderStu() { //スクラッチの状態を取得
if (right > 1)//時計周り中
{
NKROKeyboard.add(KEY_LEFT_SHIFT);
}
else
{
NKROKeyboard.remove(KEY_LEFT_SHIFT);
}
if (left > 1)//反時計周り中
{
NKROKeyboard.add(KEY_LEFT_CTRL);
}
else
{
NKROKeyboard.remove(KEY_LEFT_CTRL);
}
//これで一つにできる??
/*
else if (left = < 0 && right = < 0) {
NKROKeyboard.remove(KEY_LEFT_SHIFT);
NKROKeyboard.remove(KEY_LEFT_CTRL);
NKROKeyboard.send();
}
*/
}
void TimerIRQ()
{
byte cur;//最新のロータリー信号
byte old;//ひとつ前の信号
cur = (!digitalRead(10) << 1) + !digitalRead(11);//A相とB相の信号を 000000ABとして変数に代入
if (cur == 3) cur = 2;
else if (cur == 2) cur = 3; //回転すると0 1 3 2となるので無理やり2を3、3を2にして0 1 2 3 にする
old = pos & B00000011; //pos(curとoldが0000AB A'B'として入ってる変数)と&演算して000000A'B'だけ代入する
pos = (old << 2) + cur;//回転信号を0000AB A'B'として代入
if (cur == 0 && old == 1) //反時計回りに回った時
right -= 50;
if (right < 0) {
right = 0;
left = 100;
}
else if (cur == 1 && old == 2) //反時計回りに回った時
right -= 50;
if (right < 0) {
right = 0;
left = 100;
}
else if (cur == 2 && old == 3) //反時計回りに回った時
right -= 50;
if (right < 0) {
right = 0;
left = 100;
}
else if (cur == 3 && old == 0)//反時計回りに回った時
{
right -= 50;
if (right < 0) {
right = 0;
left = 100;
}
}
else if (cur == 0 && old == 3) //時計回りに回った時
{
left -= 50;
if (left < 0) {
right = 100;
left = 0;
}
}
else if (cur > old)//時計回りに回った時条件2
{
left -= 50;
if (left < 0) {
right = 100;
left = 0;
}
}
/*
//これだと一つにできる???
else if ((cur == 0 && old == 0)&&(cur > old)) //時計回りに回った時
{
left -= 50;
if (left < 0) {
right = 100;
left = 0;
}
}
*/
}
void ModeChange()
{
if (!digitalRead(7) && !digitalRead(8))//セレクトスタート同時押し
{
for (int i = 0; i < sizeof(ModeCount) / sizeof(int); i++)
{
if (!digitalRead(i))//1~7鍵
{
ModeCount[i]++;
}
}
//ここバグの温床
if (ModeCount[0] >= 2500 )
{
//ここでモード切替
Mode = 1;
}
/*
else if (ModeCount[1] >= 2500)
{
//ここでモード切替
//Mode = 1;
}
else if (ModeCount[2] >= 2500)
{
//ここでモード切替
//Mode = 1;
}
else if (ModeCount[3] >= 2500)
{
//ここでモード切替
//Mode = 1;
}
else if (ModeCount[4] >= 2500)
{
//ここでモード切替
//Mode = 1;
}
else if (ModeCount[5] >= 2500)
{
//ここでモード切替
//Mode = 1;
}
*/
else if (ModeCount[6] >= 2500)
{
//ここでモード切替
Mode = 0;
}
}
else
{
for (int i = 0; i < sizeof(ModeCount) / sizeof(int); i++)
{
ModeCount[i] = 0;
}
}
//
}
void TESTFC() {
Serial.print("Mode:");
Serial.println(Mode);
//すべてのピンの状態を出力
for (int i = 0; i < 13; i++)
{
Serial.println((String)i + ":" + !digitalRead(i));
}
for (int i = 0; i < sizeof(ModeCount) / sizeof(int); i++)
{
Serial.print(i + 1);
Serial.print("is");
Serial.println(ModeCount[i]);
}
Serial.print((String)"R:" + right);
Serial.println((String)" L:" + left);
}
番外編 失敗したこと
一番最初はゲームパッドとして制作するはずだった。
作っているうちにゲームパッドよりキー入力のほうが便利ということで
ゲームパッド→キーボード→USBの仕様により6キー同時押し制限がある→ゲームパッド→Nキーロールオーバーのプロジェクトを発見→キーボード
となかなか遠回りになってしましました。
INFINITAS って公式専用コントローラーしか反応しないらしいんですね。だったらさいしょからキーボードとして認識させよう。と。
ただ工夫すればコントローラーでもいいみたい。コントローラーのベンダーID/プロダクトIDがなんとか…
INFINITAS公式コントローラーの場合
1鍵: 1
2鍵: 2
3鍵: 3
4鍵: 4
5鍵: 5
6鍵: 6
7鍵: 7
E1: 9
E2: 10
E3: 11
E4: 12
ターンテーブル: X軸循環
DJDAOで紹介されていますが、大丈夫なんでしょうか…
番外編 芝ボタンと三和ボタン
安く作るために芝商事のボタンを利用する方へ
三和電子のボタンと芝商事のボタンは似ているのですが仕様に違いがあります。こちらのサイトで詳しく解説されています。
芝ボタンを利用する方はご注意ください。
LEDをつける方法
ここからLEDを装着する手段について書きます。
まず三和ボタンや芝ボタンにLEDをつける方法はいくつか存在します。
①このようなウエッジベースというものにLEDを取り付けてArduinoの5v出力を利用して点灯させる方法
②GAMO2 オンラインショップのLEDを取り付けてArduinoの5v出力を利用して点灯させる方法
③専用LEDを使う
2022/10/13
加工不要なLEDを発見。
抵抗は組み込まれていなさそう?(要検証)
④自動車のウェッジ球を取り付けて対応する電圧をかけて点灯させる方法
この中では①と②と③が簡単です。とりあえず①の方法で行きましょう。
5vのLEDに直接5vを流しても…悪くはないですが、LEDの点灯には抵抗が必要です。
Arduinoと接続するときに抵抗を組み込んであげる必要があるんですが、抵抗を組み込むのが面倒だったので、抵抗入りLEDというものがあります。https://akizukidenshi.com/catalog/c/cregled/
これを使えば抵抗を使うことなく、簡単にLEDを装着できそうです。
装着した後に眩しい場合はこちらの拡散キャップを使うと軽減できます
この後の手順がモチベ保てそうにありません。
モチベがあるうちに文章で書き残しておきます。
LEDは
https://www.nicovideo.jp/watch/sm33839263の動画内にあるように
つなぎます。
これでLEDがつくようになります。
心配なら整流ダイオードを入れてもいいと思います。
https://wokwi.com/projects/345222008766726740
Arduinoのシミュレーターで書き起こしてみました。
わかりやすいかなと
今後の気をつける点まとめ
2022/10/11
D2MV-01-1C3生産終了について
0.49Nの定番マイクロスイッチです。
これは生産終了します。
VX-01-1C23が後継品と思われます
従来のスイッチと同じ#187のファストン端子がついています。
構造が変化したOBSA-LHS1F-LNというものが販売されています。
ファストン端子よりメンテナンスが簡単になるよう設計されています。
しかし専用ハーネスが必要であるようです(自作可能なものか検証します)
リードスイッチ版のOBSA-LHSDF-LNもあります
どちらも押圧0.98N(100g)なのですが
マイクロスイッチと比較してリードスイッチは価格が高く、
リードスイッチのほうがボタンが組み合わさったときには軽いようです。
従来のD2MV-01-1C3では
マイクロスイッチの0.49Nと
バネ20g,60g,100gなどを選択して調節する方法が主流でしたが、新製品は固定になります。
この記事を書いていて思ったのがマイクロスイッチ一体型のボタン
硬いんだけど???です非常に圧力が必要な気がします。
リードスイッチを試せるときがあれば試してみます。