🕹️

SDVX筐体のコンパネからコントローラーを作る(2)

2024/12/17に公開

※この記事は Life is Tech!School秋葉原のメンバー・メンターが執筆する akihabara-school Advent Calendar 2024 17日目の記事になります。

はじめに

こんにちは!SDVX筐体のコンパネからコントローラーを作る記事の続きです!
今回は、前回で製作途中だったコンパネボルテコンのボディを作り、実際に動作できるようにします。

前回まで

✅ コンパネを入手する
✅ 大まかな仕様を決める
✅ 基板を設計する ←いまここ
4. 必要な部品を購入する
5. 筐体と基板を組み立てる
6. Pro Microにスケッチを書き込む
7. 完成!

3-ex.回路修正

部品を購入して組み立て...をしようとしたところで、もっといい回路があったので回路図を修正します。

変更点

  1. 基板を二枚に分けるメリットがないので一体化
  2. 浮きピン対策でボタンのピンを内部プルアップしたときに抵抗値が10MΩぐらいになってLEDが点かないので分離
  3. エンコーダの読み取りに割り込みを使えたら便利なのでピンを変更

他のところの回路はほとんど変更ありません。再度基盤を設計して発注します。今回もJLCPCBに発注しました。

4.必要な部品を購入する

組み立てに必要な部品を説明します。

4-1.基板

基板組み立てに必要なものは以下の通りです。前回で一部説明したので表のみ載せます。

部品名 個数 用途 リンク
基板(自作) 1枚 部品の接続 JLCPCB
ProMicro(互換機) 1枚 メイン基板 遊舎工房
B34B-XADSS-N 1ヶ ハーネスの接続 JST
B02B-XASK-1 1ヶ エンコーダの電源 JST
68Ω1/4W抵抗器 7本 LED電流制限 秋月電子
BZX55C5V1 7本 LED保護 秋月電子
ピンソケット 1set ProMicroの接続 秋月電子
貼り付けスペーサー 1set 基板の取り付け 秋月電子

4-2.つまみユニット

今回使うコンパネにはつまみの部品がついていないので自作します。ロータリエンコーダを取り付けるためのブラケットを自作します。
コンパネのつまみ部分には取り付け用のボルトがあるので、これに合わせて作ります。

ロータリーエンコーダを直付けしても良いのですが、横向きの力がかかったときに壊れそうだったので間に軸受けを入れて直接力がかからないようにします。
ロータリエンコーダの取り付け金具は、千石電商にタカチ製の良さそうなものを見つけたのでこれを使います。穴径が大きいのでワッシャを間に噛ませます。

軸受けとシャフトも千石電商で購入します。エンコーダの軸径は6mmなので軸受けとシャフトもφ6にします。

これらを取り付けるプレートが必要ですが、ぴったりな部品が無いので自作します。
部品のネジ穴の位置を参考に、3Dデータを作成します。今回はFreeCadを使いました。

3Dプリンターを所有していれば印刷できますが、無いのでJLCPCBに発注します。基板と同時に発注できます。
組み立てに必要な部品は以下の通りです。

部品名 個数 用途 リンク
取付プレート(自作) 2枚 部品の取り付け JLC3DP
RES20D-50-201-1 2ヶ ロータリエンコーダ RSコンポーネンツ
SH-12-2B 2ヶ エンコーダ取り付け 千石電商
φ6ワッシャ 2ヶ エンコーダ取り付け 千石電商
φ6延長シャフト50mm 2ヶ シャフト延長 千石電商
φ6軸受け金具 2ヶ シャフト支持 千石電商
SVK30 2ヶ ノブ BTOS
M4皿ボルト15mm 4本 金具取付 モノタロウ
M4ナット 16ヶ 取付 モノタロウ
XAP-04V-1 2ヶ エンコーダのハーネス JST

4-3.接続ケーブル

ボタンやつまみの接続のためのケーブルは前回から変更がないので部品一覧だけ載せます。
(SXAとSXAMはエンコーダ側コネクタ用も含む)

部品名 個数 用途 リンク
0.3sq線 10mぐらい ケーブル モノタロウ
B34B-XADSS-N 1ヶ コネクタ JST
B02B-XASK-1 1ヶ コネクタ JST
XADRP-34V 2ヶ コネクタ JST
XAP-02 1ヶ コネクタ JST
XARR-04V 2ヶ コネクタ JST
SXA-01T-P0.6 100~150ヶぐらい コネクタ JST
SXAM-01T-P0.6 20~50ヶぐらい コネクタ JST

4-4.ケース

基板やケーブルをしまうケース(コントローラー筐体)を作ります。今回は安価で使いやすいアルミフレームを用います。
コンパネの取り付けネジの穴の位置は次の通りです。M6ボタンボルトが適合します。


製作中にいろいろ考えていたときの図なので雑ですが参考にどうぞ。

NICオートテックがオーダーカットに対応していたのでこれを使います。ネジ穴に対応するように長さを指定します。うち2本は端部にねじ止めするのでタップ付きを選びます。

フレームを組み立てるためにブラケットが必要です。ケースにするための表面パネルを取り付けるのでネジ穴付きのを選びます。24個中4つはコンパネにつくのでネジ穴不要です。

重量が結構あるので机等の傷つき防止のためにゴム足をつけます。M6ネジ対応にしておくとナットが他と共通になるので便利です。6ヶぐらいがちょうどよさそうです。
コンパネやゴム足をつけたりブラケットを固定するためにはナットが必要です。ナットは今回すべてM6なのでまとめて注文します。このうちブラケットに必要な分はブラケットとセット販売しているので、残りの分を注文します。
必要なネジもブラケットの分はセットなので、残りの分を注文します。パネル取り付け用にトラスボルトを、コンパネ・ゴム足取り付け用に六角穴付きボタンボルトを用います。
パネルは強度が重要でなく、中身が見えなければ十分なので、ダイソーのスチレンボードを使います。

まとめると以下の通りです

部品名 個数 用途 リンク
AFS-3030F-6-520 4本 フレーム NICダイレクト
AFS-3030F-6-245 4本 フレーム NICダイレクト
AFS-3030F-6-140 2本 フレーム NICダイレクト
AFS-3030F-6-140-T 2本 タップ付フレーム NICダイレクト
ABLD-30-6-N-BNH 4set ブラケットセット NICダイレクト
ABLD-30-6-N-T-BNH 20set タップ付ブラケットセット NICダイレクト
NHG-06 15set ナットセット NICダイレクト
TSB-06-12 20本 トラスボルト NICダイレクト
TM-TK-3617 6ヶ ゴム足 モノタロウ
M6x20六角穴付ボタンボルト 2本 ボタンボルト モノタロウ
カラーボード 1枚 パネル ダイソー

4-5.その他

必要な工具など

品名 用途
六角レンチセット ボルト締め
ドライバーセット ネジ締め
はんだごて 部品のはんだ付け
はんだ 部品のはんだ付け
端子に適合する圧着ペンチ 端子圧着
ワイヤーストリッパ 被覆剥き
カッターナイフ パネル切断
デジタルマルチメータ 測定/導通チェック

5.筐体と基板を組み立てる

いろいろ発注したのものが届いたら、組み立てていきます。

5-1.基板

基板を組み立てます。部品を配置し、はんだ付けしていきます。コネクタとダイオードは向きに注意
はんだ付けしたらテスターを用いて導通とショートをチェックしてください。

5-2.つまみ

エンコーダに端子を圧着します。線が本来適合する線より細いので千切らないように注意

つまみを組み立てます。プレートに軸受けを取り付けます。

エンコーダをスイッチボックスに取り付けます。付属のナットとワッシャで固定します。次に軸に延長シャフトをねじ止めし、プレートの軸受けにシャフトを通し、ボックスを固定します。

コンパネに取り付けたら完了です。

5-3.接続ケーブル

ビニル線にコネクタを圧着します。線の長さは割と適当で問題ないです。100ヶ所ぐらいあるのでつけ間違いに注意してください。

基板やエンコーダなどに繋いで完了です。

5-4.ケース

NICオートテックの説明を参考に組み立てていきます。
必要なナットをあらかじめ入れておきます。ゴム足やコンパネとつなぐナットを入れ忘れないようにしましょう。
ブラケットの向きと140mmタップ付きのフレームの位置に注意しながら組み立てます。

組み立てたフレームをコンパネに取り付けます。コンパネのMDF部分をM6ボタンボルトでねじ止めします。その後、コンパネの残りの部品を取り付けます。
ダイソーのカラーボードをフレームの内側に合わせて切り、ブラケットのネジ穴の位置にφ6の穴をあけネジ止めします。

取り付け部品を使い、基板をねじ止めします。

最後に、ゴム足をねじ止めして完了です。

6.ProMicroにスケッチを書き込む

ProMicroにスケッチを書き込み、実際に使用できるようにします。
まずは全体から

#include <Joystick.h>
#include <Encoder.h>


Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_GAMEPAD, 7, 0, true, true, false, false, false, false, false, false, false, false, false);
const int Abtn = 4;
const int Bbtn = 5;
const int Cbtn = 6;
const int Dbtn = 7;
const int Lbtn = 8;
const int Rbtn = 9;
const int Sbtn = 10;
const int Aled = 20;
const int Bled = 19;
const int Cled = 18;
const int Dled = 15;
const int Lled = 14;
const int Rled = 16;
const int Sled = 21;
Encoder LeftEncoder(2, 3);
Encoder RightEncoder(0, 1);
void setup(){
  pinMode(Abtn, INPUT_PULLUP);
  pinMode(Bbtn, INPUT_PULLUP);
  pinMode(Cbtn, INPUT_PULLUP);
  pinMode(Dbtn, INPUT_PULLUP);
  pinMode(Lbtn, INPUT_PULLUP);
  pinMode(Rbtn, INPUT_PULLUP);
  pinMode(Sbtn, INPUT_PULLUP);
  pinMode(Aled, OUTPUT);
  pinMode(Bled, OUTPUT);
  pinMode(Cled, OUTPUT);
  pinMode(Dled, OUTPUT);
  pinMode(Lled, OUTPUT);
  pinMode(Rled, OUTPUT);
  pinMode(Sled, OUTPUT);
  Joystick.begin();
  Joystick.setButton(1, 0);
  Joystick.setButton(2, 0);
  Joystick.setButton(3, 0);
  Joystick.setButton(4, 0);
  Joystick.setButton(5, 0);
  Joystick.setButton(6, 0);
  Joystick.setButton(0, 0);
  Joystick.setXAxisRange(0,1023);
  Joystick.setYAxisRange(0,1023);
} 
long LeftPos = 0;
long RightPos = 0;
int deltaLeft = 0;
int deltaRight = 0;
int XAxisValue = 0;
int YAxisValue = 0;
void loop(){
  long newLeft, newRight;
  newLeft = LeftEncoder.read();
  newRight = RightEncoder.read();
  if (newLeft != LeftPos || newRight != RightPos) {

    deltaLeft = (int)(newLeft - LeftPos);
    deltaRight = (int)(newRight - RightPos);

    if (deltaLeft != 0) {
      XAxisValue += deltaLeft;
    }
    if(deltaRight != 0) {
      YAxisValue += deltaRight;
    }

    LeftPos = newLeft;
    RightPos = newRight;

    if (XAxisValue < 0){
      XAxisValue += 1024;
    }
    else if (XAxisValue > 1023) {
      XAxisValue -= 1024;
    }

    if (YAxisValue < 0){
      YAxisValue += 1024;
    }
    else if (YAxisValue > 1023) {
      YAxisValue -= 1024;
    }

    Joystick.setXAxis(XAxisValue);
    Joystick.setYAxis(YAxisValue);
  }

  if (digitalRead(Abtn) == LOW) {
    Joystick.pressButton(1);
    digitalWrite(Aled, HIGH);
  }
  else {
    Joystick.releaseButton(1);
    digitalWrite(Aled, LOW);
  }

  if (digitalRead(Bbtn) == LOW) {
    Joystick.pressButton(2);
    digitalWrite(Bled, HIGH);
  }
  else {
    Joystick.releaseButton(2);
    digitalWrite(Bled, LOW);
  }

  if (digitalRead(Cbtn) == LOW) {
    Joystick.pressButton(3);
    digitalWrite(Cled, HIGH);
  }
  else {
    Joystick.releaseButton(3);
    digitalWrite(Cled, LOW);
  }

  if (digitalRead(Dbtn) == LOW) {
    Joystick.pressButton(4);
    digitalWrite(Dled, HIGH);
  }
  else {
    Joystick.releaseButton(4);
    digitalWrite(Dled, LOW);
  }

  if (digitalRead(Lbtn) == LOW) {
    Joystick.pressButton(5);
    digitalWrite(Lled, HIGH);
  }
  else {
    Joystick.releaseButton(5);
    digitalWrite(Lled, LOW);
  }

  if (digitalRead(Rbtn) == LOW) {
    Joystick.pressButton(6);
    digitalWrite(Rled, HIGH);
  }
  else {
    Joystick.releaseButton(6);
    digitalWrite(Rled, LOW);
  }

  if (digitalRead(Sbtn) == LOW) {
    Joystick.pressButton(0);
    digitalWrite(Sled, HIGH);
  }
  else {
    Joystick.releaseButton(0);
    digitalWrite(Sled, LOW);
  }
}

コードは、JoystickライブラリEncoderライブラリを用いて、ゲームパッドとして動作させています。詳しくはそれぞれのライブラリのページを参考にしてください。
特徴的な部分は以下の通りです。ほかの部分はほとんどライブラリのexampleにあるものと同じです。

const int Abtn = 4;
const int Bbtn = 5;
const int Cbtn = 6;
const int Dbtn = 7;
const int Lbtn = 8;
const int Rbtn = 9;
const int Sbtn = 10;
const int Aled = 20;
const int Bled = 19;
const int Cled = 18;
const int Dled = 15;
const int Lled = 14;
const int Rled = 16;
const int Sled = 21;
Encoder LeftEncoder(2, 3);
Encoder RightEncoder(0, 1);

まず、使用するピンをはじめに設定します、こうすることで、ピン配置を変えても簡単に対応させることができます。

long newLeft, newRight;
  newLeft = LeftEncoder.read();
  newRight = RightEncoder.read();
  if (newLeft != LeftPos || newRight != RightPos) {

    deltaLeft = (int)(newLeft - LeftPos);
    deltaRight = (int)(newRight - RightPos);

    if (deltaLeft != 0) {
      XAxisValue += deltaLeft;
    }
    if(deltaRight != 0) {
      YAxisValue += deltaRight;
    }

    LeftPos = newLeft;
    RightPos = newRight;

    if (XAxisValue < 0){
      XAxisValue += 1024;
    }
    else if (XAxisValue > 1023) {
      XAxisValue -= 1024;
    }

    if (YAxisValue < 0){
      YAxisValue += 1024;
    }
    else if (YAxisValue > 1023) {
      YAxisValue -= 1024;
    }

    Joystick.setXAxis(XAxisValue);
    Joystick.setYAxis(YAxisValue);
  }

この部分で、ロータリエンコーダの回転を検出し、0~1023の値の変化としてゲームパッドのX,Y軸に反映させています。

if (digitalRead(Abtn) == LOW) {
    Joystick.pressButton(1);
    digitalWrite(Aled, HIGH);
  }
  else {
    Joystick.releaseButton(1);
    digitalWrite(Aled, LOW);
  }

ボタンは、スイッチが接続されているピンの状態に応じて、対応するゲームパッドのボタンとLEDに反映させています。

これをProMicroに書き込み、動作を確認します。

動作を確認出来たら無事完成です!

7.完成

このコントローラーをSDVXコナステ版で使うために、Joy2Keyでキーボード動作に変換します。K-Shoot-MANIAではそのまま使用できます。
基板・3Dモデル・スケッチは下の場所にまとめてあります。
https://github.com/SUNORAPI/SVAC1
筐体や回路、ファームウェアなどに改善できる箇所がたくさんあると思うので、ぜひいろいろカスタマイズしてみてくだい。閲覧ありがとうございました!

AkihabaraSchool

Discussion