2017-11-23

自製MÚSECA控制器

MÚSECA是KONAMI比較新的機台,想在家裡玩所以需要一個控制器
不過我還沒有實際摸過實際的機台,只能先看網路上的照片參考,猜比例並縮小體積


最早的原型是用光碟片黏磁鐵跟家具轉盤組合,用49E線性霍爾IC做感應
因為同時需要按壓與轉動的訊號,並沒有耐操的現成便宜零件可用
或者都需要從軸心開始作,所以選擇非接觸式的磁力感應會比較方便
但實做到一半才發現49E的單軸感應只適合做180度以內的旋轉偵測

修正後,用兩顆49E以90度角擺放(但中心位置會變得不準)或換感應器

讓旋轉方向連動到兩個按鈕輸出

然後結構大概會像這樣,轉盤是用雷射切割的5mm壓克力圓盤,直徑12cm


3吋家具轉盤四角鎖橡膠柱,塞進拔掉上蓋的電玩按鍵(全都是中國製便宜貨)
測試的時候只有用防滑墊塞著,最後組合時會用螺絲從底下鎖緊


底下感應器後來決定改用MLX90333 3D霍爾感測器
轉盤的磁鐵在模組上方移動,提供非接觸式的類比信號
使用Arduino類比腳位讀取訊號並平滑化,轉換數值為角度輸出


拿紙箱試裝,然後手工磨邊緣的倒角,鑽平頭螺絲的沉孔




一組做完確定可以用之後,複製5組出來,
磁鐵規格是5x5mm圓柱形,從背面用膠帶跟白膠固定




後來剛好有機會接觸機台,帶個半成品轉盤合照,順便試試按壓手感



回來接著做框體,材料是12mm木合板




然後做面板圖,參考官方機台然後一直改來改去


第一次輸出,招牌店的大圖輸出設備解析度不夠,成品不夠美所以未採用



另找廠商重印面板圖,壓克力面板先拿回來試裝框體跟轉盤



貼上新面板圖,割掉多餘部分,新廠商輸出的解析度很好
但可惜貼圖時有黏到一點毛屑跟小氣泡,過程滿手都是洗碗精水,沒空拍照 XD



然後製作連接線,微動開關是松下AM50039A331F1 ,OF 0.49N



還要幫框體加上鎖壓克力板的螺絲孔,我拿塑膠用的M3嵌入式螺母磨尖之後敲進木板

裝上壓克力板跟鋁殼腳墊的樣子,此時小氣泡已經消退不少了




右側有收線槽,內部是Type-B的可分離線設計,拉門邊裝有M5不鏽鋼定位珠


正面外觀完成
轉盤按鍵全裝上,我喜歡讓螺絲跟磁鐵露在外面


初步配線,接出來剛好變卍字形



把5個轉盤同時接上測試電路


電路板使用Arduino Leonardo ,設計一塊Shield方便未來拆裝


所有接線


收線槽特寫,


程式的部分總共需要讀取10個類比+7個數位訊號 (磁力X/Y軸,按壓開關等)
轉換成5個類比旋轉信號加5個轉盤的按壓,START與踏板等7個按壓訊號


感應器回傳的X/Y座標用三角函數atan2換算成角度值 (藍色那個夾角)
把5個類比輸出一次做好,再把轉盤連動按鍵輸出,相容現有的操作方式
最終模擬一個17鍵的六軸手把,再用JoyToKey轉鍵盤訊號
(如果直接送出鍵盤訊號的話,測試時會很麻煩)

踏板的部分我直接延用現成的Drummania控制器踏板
Shield上有做一組接點但並未使用,未來有需要的話可以從收線槽那邊拉出來用


2018-04-09 Update: SpiceTools追加類比輸入功能,轉盤訊號變按鈕功能移除
2018-01-23 Update: spicecfg更新減速按鍵功能

//Arduino Joystick Library
//https://github.com/MHeironimus/ArduinoJoystickLibrary/
#include <Joystick.h> //模擬8鍵遊戲手把,6個類比軸
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_GAMEPAD, 8, 0,
true, false, true, true, true, true, false, false, false, false, false);

const int BT_1 = 7;
const int BT_2 = 0;
const int BT_3 = 3;
const int BT_4 = 1;
const int BT_5 = 2;
const int BT_6 = 13;
const int BT_7 = 18; //A0
const int numReadings = 2; //平滑陣列取樣數
int readings[11][numReadings] = {0}; //類比讀值暫存陣列
int Smoothing[11][3] = {0}; //平滑化變數暫存陣列 {readIndex,total,average}
int Disc[6][5] = {0}; //轉盤狀態暫存陣列 {主轉盤值,這次讀值,前次讀值,X軸,Y軸}
int DeadZone1 = 1; //變動下限

void setup() {
Joystick.begin();
Joystick.setXAxisRange(-180,180);
Joystick.setZAxisRange(-180,180);
Joystick.setRxAxisRange(-180,180);
Joystick.setRyAxisRange(-180,180);
Joystick.setRzAxisRange(-180,180);
pinMode(BT_1, INPUT_PULLUP);
pinMode(BT_2, INPUT_PULLUP);
pinMode(BT_3, INPUT_PULLUP);
pinMode(BT_4, INPUT_PULLUP);
pinMode(BT_5, INPUT_PULLUP);
pinMode(BT_6, INPUT_PULLUP);
pinMode(BT_7, INPUT_PULLUP);
}

void loop() {
//輪詢按鍵們
if(digitalRead(BT_1) == LOW)
{ Joystick.setButton (0,1); }
else
{ Joystick.setButton (0,0); }
if(digitalRead(BT_2) == LOW)
{ Joystick.setButton (1,1); }
else
{ Joystick.setButton (1,0); }
if(digitalRead(BT_3) == LOW)
{ Joystick.setButton (2,1); }
else
{ Joystick.setButton (2,0); }
if(digitalRead(BT_4) == LOW)
{ Joystick.setButton (3,1); }
else
{ Joystick.setButton (3,0); }
if(digitalRead(BT_5) == LOW)
{ Joystick.setButton (4,1); }
else
{ Joystick.setButton (4,0); }
if(digitalRead(BT_6) == LOW)
{ Joystick.setButton (5,1); }
else
{ Joystick.setButton (5,0); }

/*未使用踏板按鍵(7),註銷以加速
if(digitalRead(BT_7) == LOW)
{ Joystick.setButton (6,1); }
else
{ Joystick.setButton (6,0); }
*/

//類比讀取數值平滑化,程式參考 Examples>Analog>Smoothing 轉為陣列迴圈
int APins[] = { 0 ,A1, A2, A3, A4, A5, A6, A7, A8, A9, A10 }; //從A1開始運算
for (int a = 1; a <= 10; a++)
{Smoothing[a][1] = Smoothing[a][1] - readings[a][Smoothing[a][0]];
readings[a][Smoothing[a][0]] = analogRead(APins[a]);
Smoothing[a][1] = Smoothing[a][1] + readings[a][Smoothing[a][0]];
Smoothing[a][0] = Smoothing[a][0] + 1;
if (Smoothing[a][0] >= numReadings) {Smoothing[a][0] = 0; }
Smoothing[a][2] = Smoothing[a][1] / numReadings; }

//插槽與接線對照
Disc[1][3] = map(Smoothing[9][2], 0, 1023, -512, 512); //Disc1X軸
Disc[1][4] = map(Smoothing[10][2],0, 1023, -512, 512); //Disc1Y軸
Disc[2][3] = map(Smoothing[1][2], 0, 1023, -512, 512); //Disc2X軸
Disc[2][4] = map(Smoothing[2][2], 0, 1023, -512, 512); //Disc2Y軸
Disc[3][3] = map(Smoothing[7][2], 0, 1023, -512, 512); //Disc3X軸
Disc[3][4] = map(Smoothing[8][2], 0, 1023, -512, 512); //Disc3Y軸
Disc[4][3] = map(Smoothing[3][2], 0, 1023, -512, 512); //Disc4X軸
Disc[4][4] = map(Smoothing[4][2], 0, 1023, -512, 512); //Disc4Y軸
Disc[5][3] = map(Smoothing[5][2], 0, 1023, -512, 512); //Disc5X軸
Disc[5][4] = map(Smoothing[6][2], 0, 1023, -512, 512); //Disc5Y軸

//運算atan2夾角,轉為正負180度數值
for (int a = 1; a <= 5; a++) { //atan2(X,Y)*180/M_PI)
Disc[a][1] = (atan2(Disc[a][3],Disc[a][4])*180/M_PI); }

//DeadZone計算
for (int d = 1; d <= 5; d++) {
if(abs(Disc[d][1] - Disc[d][2]) > DeadZone1 )
Disc[d][0] = Disc[d][1]; }

//更新類比數值
Joystick.setXAxis (Disc[1][0]);
Joystick.setZAxis (Disc[2][0]);
Joystick.setRxAxis(Disc[3][0]);
Joystick.setRyAxis(Disc[4][0]);
Joystick.setRzAxis(Disc[5][0]);

//回存所有轉盤數值
for (int o = 1; o <= 5; o++) {
Disc[o][2] = Disc[o][1];}

} //end loop


完成



轉盤按壓下沉狀況



控制台的訊號測試

遊玩影片


Instructions:
Controller CAD File and Panel Art Template Download
My design is based on magnetic, set a magnet on the off-center of the disc, rotating disk and magnet move around the center of the sensor, measuring X & Y axis and convert coordinates to angle with atan2 function.
Because I need to filter out magnetic signal glitches, there is some man-made input lag, it can be adjusted, but it does exist.

Parts list: (most parts bought from taobao)
1pc. Arduino Leonardo clone
1pc. panel mount USB cable - B female to micro-B male
1pc. USB 2.0 type-B cable (any length)
1set. parts for fixing Arduino (M3x10mm copper pillar and bolt)

1pc. 300x550x5mm chamfered laser cut clear acrylic panel
5pcs. 120x5mm chamfered laser cut acrylic discs with 4 countersunk holes
5pcs. 71x71x3mm laser cut acrylic squares
5pcs. 3 inch hollow furniture turntables
5pcs. CJMCU-93 MLX90333 magnetic sensor modules
5pcs. 5x5mm cylinder neodymium magnets
10pcs. M3x2mm Nylon washers
10pcs. M3 bolts
10pcs. M3 nuts

1pc. foot paddle switch
1pc. 39mm triangle push button
20pcs. 33mm push round buttons
21pcs. microswitches with 50g strength

20pcs. M4x8mm hexagon socket countersunk head stainless steel screws
20pcs. M4 flat nut
20pcs. M4 bolts
20pcs. self-tapping screws
20pcs. M4x15mm washers
20pcs. D15xH20mm DE type rubber shock absorber (with M4 screw hole)

9pcs. M3x15mm hexagon socket button head stainless steel screws
9pcs. M3 brass insert nut (chamfer it video1, video2 )

1pc. box, put everything inside
1set. parts for box making (glue or screw)
1set. parts for bottom door fixing (magnet or screw)
4pcs. hifi style aluminium feet

Optional Parts: (depends on your habits)
2.54 XH connector
4.8mm female crimp connector
jumper wire

Assembling:


furniture turntable has good durability,
sand the edges before build up, cover ball bearings with tape to avoid dust during sanding.

I didn't find proper size of DD type(both female) shock absorber,
so you need drill a hole before screw in into rubber, and don't tight it up, loose self-tapping screw little bit.

Wiring:


Or make an Arduino Shield PCB


Pin layout:
Button 1 = 0
Button 2 = 1
Button 3 = 2
Button 4 = 3
Button 5 = 7
Foot paddle = 13
Button Start = A0(18)
*Connect switches in quad parallel.

Magnet sensor A OUT1 =A1
Magnet sensor A OUT2 =A2
Magnet sensor B OUT1 =A3
Magnet sensor B OUT2 =A4
Magnet sensor C OUT1 =A5
Magnet sensor C OUT2 =4(A6)
Magnet sensor D OUT1 =6(A7)
Magnet sensor D OUT2 =8(A8)
Magnet sensor E OUT1 =9(A9)
Magnet sensor E OUT2 =10(A10)
*Connect all GND to GND, VCC to 5V

Code: (Upload code to Arduino using the Arduino IDE.)
variable "numReadings" is for magnetic reading stabilizes,a lower value makes response quicker and unstable, recommended value between 3 and 5.

Bind joystick button with SpiceTools, enjoy the game.