MENU

つなぐだけでトースターが自動リフローオーブンになるコントローラーを作ろう

このエントリーをはてなブックマークに追加

Arduino互換機を使って、オーブントースター自動リフローができるようになるコントローラーを作りました。
トースターの電源につなぐだけで、トースターが自動リフローオーブンになっちゃいます。
トースター本体の改造は一切不要だよ。

作り方を詳しく説明してるので、自己責任でもいいからリフローオーブン欲しいんじゃーって方はぜひ一緒に作りましょう。

目指せ、一家に一台リフローオーブン。

作ったもの

ソリッドステートリレー付きの延長コードが付いてるコントローラーで、トースターの電源につないで使います。

コントローラーのスタートボタンを押すとあとは勝手に基板をリフローしてくれます。

このリフローコントローラーは下記製品の量産にも使用しており、実用レベルといえると思います。
geekyfab.com

なんで作ったか

電子工作をやっていると「リフローしたい!」と思う時が来ますよね。
部品点数が増えてきてリフローのほうが楽かもと思ったとか、ピンがない部品(QFNとか)を使いたくなったとか、作品を量産したくなったとか。

僕も作品の量産を効率的にやりたいなと思って自動リフローオーブンが欲しくなりました。
で、作ってみようかなと。

ただ、Web上にはトースターを改造してリフローオーブンを作るプロジェクトは結構あるんですけど、どれもトースター本体を改造してるんですよね。
僕はAC電源の機器を改造して運用するのはちょっと怖いなと思ってるんで、トースター本体を改造しない方法を考えて作ってみたっていうのがこのプロジェクトです。

では早速作っていきましょう。

部品とか

主要部品はこんな感じ

部品 説明 商品リンク
Arduino互換機 (UniversALDUINO) ユニバーサル基板付きArduino互換機 SwitchSience
オーブントースター コンベクション機能アリのものを購入したけど機能OFFにして使ってる。電力さえあれば安いのでいい。 amazon
ソリッドステートリレー 安全安心のPanasonic製。amazonでも似たようなの売ってる。 モノタロウ
AC延長コード ダイソーで適当に買った ダイソー
MAX31855 熱電対温度センサモジュール SwitchSienceで買ったけど、今は品切れなので"MAX31855"で検索して適当なもの買ってください。秋月とか。熱電対別売りなら熱電対も必要。 SwitchSience
LCDモジュール 16×2行バックライト付 秋月電子
タクトスイッチ たぶん秋月で買ったやつ。どれか忘れたから適当に。 秋月電子
可変抵抗 たぶん秋月で買ったやつ。どれか忘れたから適当に。 秋月電子
抵抗 Aliexpressでいっぱい買ったやつ。秋月でもいい。 Aliexpress
NPNトランジスタ 2SC1815 秋月電子
リード線 なんでもいい。 秋月電子
半田 amazon

回路図とはんだ付けと組み立て

回路図

今回は作る人が読みやすいかなと思ってFritzingで書いてみました。
ヨミヤスイネ!

オーブントースターの電源部分にソリッドステートリレー(SSR)が挿入してるのがポイントです。
このSSRをON-OFF制御することで、オーブントースターをON-OFF制御して、温度をいい感じに制御します。

ちなみに上の回路図だとArduino UNOになってますけど、実際には互換機(UniversALDUINO)使って作りました。
作品の完成形が決まってるときにUniversALDUINO使うと便利なんです。

ソリッドステートリレーと電源コード部分の作成

今回の肝となる、ソリッドステートリレー(SSR)を使った電源制御の部分を作っていきましょう。

使うのはSSRとダイソーで買った適当な短めの延長コード

まずは延長コードの中間あたりにカッターで切り、

二線に分割します。

そしてどちらか片方を切って、被覆を剥ぎ、

SSRにねじ止めして完成です。

試しに半田ごてをつないでLチカしてみました。

問題なく動いてそうです。

そのほかの部品のはんだ付け

他の部品をUniversALDUINOにはんだ付けしていきます。
作ったのがこれ。

表は結構キレイ。

裏は汚い。

LCD部分のはんだ付けは2回間違えて付け直してます。(+1時間)
こういうの一発でできたことないや。

コーディング

温度測定部分はSwitch Sienceさんが公開していた熱電対温度センサモジュールキットのサンプルスケッチを参考にしてます。
MAX31855Sketch – スイッチサイエンス

スケッチ中のPID制御のパラメーター(Kp, Ki, Kd)の値はトースターの温度制御の様子を見ながら調整しました。
他のトースターでやるときは調整が必要かも。

#include <SPI.h>
#include <LiquidCrystal.h>
#include <PID_v1.h>

//#include SERIAL_MON

#define VCC  8
#define GND  9

#define SLAVE 10

// LCD
const int rs = 2, en = 3, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

// LED
bool state_led_buitlin = false;

// 温度閾値の設定
double thermo_preheat = 90;
double thermo_preheat_done = 130;
double thermo_max = 180;

// 熱電対
float thermo_now=0; // 熱電対温度

// PID変数
double setpoint = 0;
double input, output;

PID myPID(&input, &output, &setpoint,15,0,0, DIRECT);

// タイマー
unsigned int time_start = 0, time_now = 0, time_start_for_serial = 0;

// SSR
const int SSR = A5;

// SW
const int SW = A3;

// PWM
unsigned long pwm_period = 1000;  // PWM period in milliseconds
unsigned long last_switch_time;

// Phase
unsigned int phase=0;
#define PHASE0 0  //開始前
#define PHASE1 1  // 開始 - preheat
#define PHASE2 2  // preheat
#define PHASE3 3  // preheat - max
#define PHASE4 4  // 終了

int duration_phase1 = 90;
int duration_phase2 = 90;
int duration_phase3 = 60;

void setup() {
#ifdef GND
  pinMode(GND, OUTPUT);
  digitalWrite(GND, LOW); 
#endif
#ifdef VCC
  pinMode(VCC, OUTPUT);
  digitalWrite(VCC, HIGH);
#endif
  pinMode(SLAVE, OUTPUT);
  digitalWrite(SLAVE, HIGH);

  Serial.begin(9600);
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV4);
  SPI.setDataMode(SPI_MODE0);

  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);


  // LED pin settings
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  // SW
  pinMode(SW, INPUT);

  // PID settings
  myPID.SetMode(AUTOMATIC);
  myPID.SetOutputLimits(0, 255);

  // SSR
  pinMode(SSR, OUTPUT);

  time_start = millis() / 1000;
  last_switch_time = millis();

  phase = PHASE0;

}

void loop() {
  unsigned int thermocouple; // 14-Bit Thermocouple Temperature Data + 2-Bit
  unsigned int internal; // 12-Bit Internal Temperature Data + 4-Bit

  time_now = millis() / 1000;

  // State machine
  switch(phase){
    case PHASE0:
      if(digitalRead(SW)){
        delay(10);   // チャタリング対策。値は適当。
        if(digitalRead(SW)){
          while(digitalRead(SW)){
            lcd.clear();
            lcd.setCursor(0,0);
            lcd.print("START!");
            delay(1000);
          }          
          phase = PHASE1;
          time_start = millis() / 1000;   //タイマーリセット
          setpoint = thermo_preheat;
          myPID.SetTunings(15,0,0);         // Kp, Ki, Kd を調整
          lcd.clear();
          delay(10);
        }
      }
      break;

    case PHASE1:
      if(thermo_now > thermo_preheat-2 ){
        if((time_now - time_start)> duration_phase1){
          phase = PHASE2;
          time_start = millis() / 1000;   //タイマーリセット
          setpoint = thermo_preheat_done;
          myPID.SetTunings(5,0,0);         // Kp, Ki, Kd を調整
          lcd.clear();
        }else{
          setpoint = 0;     // 早く温度が上がりすぎた場合は待機          
        }
      }
      break;

    case PHASE2:
      if(thermo_now > thermo_preheat_done-2){
        if((time_now - time_start)>duration_phase2){
          phase = PHASE3;
          setpoint = thermo_max;                 // プロファイル上は165だけど、マージンを見て180とする。
          myPID.SetTunings(15,0,0);         // Kp, Ki, Kd を調整
          time_start = millis() / 1000;   // タイマーリセット
          lcd.clear();
        } else{
          setpoint = 0;     // 早く温度が上がりすぎた場合は待機
        }
      }
      break;

    case PHASE3:
      if(thermo_now>thermo_max-2){
        if((time_now - time_start)>duration_phase3){
          phase = PHASE4;
          setpoint = 0;
          time_start = millis() / 1000;   // タイマーリセット
          lcd.clear();
        } else{
          setpoint = 0;
        }
      }        
      break;

    case PHASE4:
      state_led_buitlin = !state_led_buitlin;
      digitalWrite(LED_BUILTIN, state_led_buitlin);
      setpoint = 0;
      if(digitalRead(SW)){
        delay(10);   // チャタリング対策。値は適当。
        if(digitalRead(SW)){
          phase = PHASE0;
          while(digitalRead(SW)){
            lcd.clear();
            lcd.setCursor(0,0);
            lcd.print("RESET!");
            delay(1000);
          }
          lcd.clear();
          time_start = millis() / 1000;   // タイマーリセット
        }
      }        
  }


  digitalWrite(SLAVE, LOW);                             //  Enable the chip
  thermocouple = (unsigned int)SPI.transfer(0x00) << 8;   //  Read high byte thermocouple
  thermocouple |= (unsigned int)SPI.transfer(0x00);       //  Read low byte thermocouple 
  internal = (unsigned int)SPI.transfer(0x00) << 8;       //  Read high byte internal
  internal |= (unsigned int)SPI.transfer(0x00);           //  Read low byte internal 
  digitalWrite(SLAVE, HIGH);                            //  Disable the chip

  if((thermocouple & 0x0001) != 0) {
//    Serial.print("ERROR: ");
//    if ((internal & 0x0004) !=0) {
//      Serial.print("Short to Vcc, ");
//    }
//    if ((internal & 0x0002) !=0) {
//      Serial.print("Short to GND, ");
//    }
//    if ((internal & 0x0001) !=0) {
//      Serial.print("Open Circuit, ");
//    }    
//    Serial.println();
  } else {
    if((thermocouple & 0x8000) == 0){ // 0℃以上   above 0 Degrees Celsius 
      thermo_now = (thermocouple >> 2) * 0.25;
    } else {                          // 0℃未満   below zero
      thermo_now = (0x3fff - (thermocouple >> 2) + 1)  * -0.25;
    }
      

    
    // PWM
    unsigned long current_time = millis();
    unsigned long time_since_last_switch = current_time - last_switch_time;
    double duty_cycle = output/255;
    unsigned long on_time = pwm_period * duty_cycle;
    if(time_since_last_switch >= pwm_period && duty_cycle > 0){
      // Start a new PWM cycle
      digitalWrite(SSR, HIGH);
      last_switch_time = current_time;
    } else if (time_since_last_switch >= on_time){
        digitalWrite(SSR, LOW);
    }
    
    // PID controll
    input = thermo_now;
    myPID.Compute();

    // serial plot
    if(time_start_for_serial-time_now >= 1){
        Serial.print("0,250,");     //縦軸固定
        Serial.print(thermo_now);   
        Serial.print(",");
        Serial.println(setpoint);        
        
        #ifdef SERIAL_MON
        Serial.print(thermocouple, HEX);
        Serial.print(" : ");
        Serial.print(thermo_now);

        Serial.print(" // ");
        #endif

        time_start_for_serial = millis() / 1000;   // タイマーリセット

    }

    // display LCD
    switch(phase){
      case PHASE0:
        lcd.setCursor(0, 0);
        lcd.print("Themo:");
        lcd.setCursor(7, 0);
        lcd.print(thermo_now);
        lcd.setCursor(13,0);
        lcd.print("degC");
              
        lcd.setCursor(0, 1);
        lcd.print("PUSH SW TO START");
        break;

      case PHASE1:
      case PHASE2:
      case PHASE3:
      case PHASE4:      
        lcd.setCursor(0, 0);
        lcd.print("Themo:");
        lcd.setCursor(7, 0);
        lcd.print(thermo_now);

        lcd.setCursor(13,0);
        lcd.print("degC");


        lcd.setCursor(0, 1);
        lcd.print("Phase");

        lcd.setCursor(5, 1);
        lcd.print(phase);

        
        lcd.setCursor(11, 1);
        lcd.print(time_now-time_start);

        lcd.setCursor(15,1);
        lcd.print("s");

        break;
    }

  }
}

ちょっとだけスケッチの説明

基板をリフローするためには、はんだペーストの説明書などに書いてある温度プロファイルに沿って温度を制御する必要があります。

僕はCHIPQUIKのTS391LT50を使っているので、データシートに書いてある以下の温度プロファイル通りにオーブントースターの温度を制御することができればOKです。

温度プロファイル (出典:https://www.chipquik.com/datasheets/TS391LT50.pdf)

このプロファイル通りに温度を制御するために、ステートマシンっぽく段階的に設定目標温度を変更しながらPID制御している感じです。

スケッチを書き込んで動作確認

で、スケッチをUniversALDUINOに書き込めば、自動リフローコントローラーの完成です。

できたリフローコントローラーを使って、ちゃんとプロファイル通りに温度制御できてるかプロットしてみました。
すると、こんな感じに。

青線がトースターの温度測定値、赤丸が要求されてるプロファイル温度、灰色の線がスケッチ内でその時間での制御目標温度です。
要するに、結構いい感じに制御できてるってこと!!

リフローしてみた

では実際にリフローしてみましょう。
※リフローするときは換気をしっかりしましょう!

今回はマルコンUSB拡張ユニットの基板のリフローをしてみたいと思います。

まずは、基板を固定して

上からステンシルをかぶせます。

そして、はんだペーストを塗り、部品を置きます。

これを熱電対と一緒にオーブントースターの中に入れ、

蓋を閉めてリフローコントローラーのスタートボタンを押します。

あとは、LCDの表示が終了になるまでほっとけばOK。
出来上がったら、トースターの扉を手動で開けるのを忘れずに。
ここだけ手動。

あとは基板が冷めるまで待ちましょう。

そして、出来上がったのがこちら。

いっぱい作れた!

写真ではわかりにくいですが、全基板しっかりとリフローできてました。
ICの一部のピンは半田ブリッジしてしまってましたが、これはどちらかというと半田ペーストを塗るときの技量の問題です。

これで基板量産がかなり楽になりました。

いいもの作れたなぁ。

最後に

今回は、おうちのトースター改造なし自動リフローオーブン化するためのコントローラーを作りました。

思いのほか長い記事になってしまいました。
これだけ長々と書きましたが、このコントローラーがなくてもリフロー自体はできるので気軽にリフローを始めてもらえればと思います。
手動でトースターのダイヤル回して温度調整すればいいのです。
もし手動でやるのに疲れたら、今回紹介したコントローラーに挑戦してみてもらえると嬉しいです。

私は今まで量産のためにいろんな治具を作ってきましたが、このリフローコントローラーは量産効率化に最も貢献しているものかと思います。
作ったよという報告も待ってますので、敷居は若干高いかもしれませんが電子工作が趣味の人はぜひ作ってほしいと思います。

今回はここまで。
読んでくれてありがとうございました。

セガサターンのマルコンをUSB化する拡張ユニットを作りました

このエントリーをはてなブックマークに追加

セガサターンのマルチコントローラー(マルコン)をUSB化するための拡張ユニットを作りました。

マルコンの標準ユニットと付け替えることで、マルコンがUSBゲームパッド化するっていうイカしたやつです。

セガが「拡張ユニット発売予定」と言ってから20年以上の時を経て、やっと現実のものとなりました。

マルコンの説明書に拡張ユニット発売するって書いてた
出典: https://segaretro.org/images/9/9e/MultiControllerSaturnJPManual.pdf

作ったもの

短いデモ映像にまとめてみました。

別ページに頒布委託先とか機能とかまとめました。
geekyfab.com

なんで作ったか

昔遊んだけど今は遊ばなくなったゲーム機って持ってますか?
押し入れの奥にしまったままだったり、棚に置いたままホコリがかぶってたりしてもったいないなって感じることないですか?
使えるのに使う機会がなくてかわいそうというか、ちょっとした罪悪感を感じるというか、そういう気分になることありますよね。
だから、もう一回彼らで遊んであげるためにこういうものを作ろうと思いました。

あと、マルコンのアナログスティックってホールセンサーを使った非接触タイプなんで、摩耗による劣化がなくて現在でも全然使えるんです。
しかも、スイッチで起こるようなドリフト現象も起こらないんですよ。
もう一回遊んであげないと損ですよね。

じゃあ作りましょう。

こんな感じの仕様にします

まずはマルコンをUSB化するにあたってどういった作戦で行こうか考えました。

まず、思いついたのはマルコンの中の基板ごと入れ替えちゃう作戦でした。
サターンパッドとかをこの作戦でUSB化した実績があったので。

でも分解してみると基板ごと入れ替えるのは難しいなという印象でした。

マルコンを分解するとこんな感じ

スティックやLRトリガーにはホールセンサーが使われてる

ホールセンサーが使われているのが特徴的ですね。
物理的な接触がなくなるので、スティックやLRトリガーが劣化しにくくなり、ドリフトや故障に悩まされにくい構造です。

難易度が高そうだったので基板ごと入れ替える作戦はいったんあきらめ、他の作戦を考えてみることにしました。

次に考えたのが、標準ユニットのとこの基板を入れ替える作戦です。

標準ユニットを分解するとこんな感じ

ユニットのとこにマルコンからの信号をUSBに変換する基板を入れて、USB化するって方法です。

これの懸念点は変換回路とプログラムが組めるかってとこと、ちっちゃいユニットの中に変換基板が収まるかってとこですね。

これはちょっとやってみないとわからなかったんで、とりあえずはこれで作ってみることにしました。

マルコンの信号仕様調査

回路書くにしてもプログラム書くにしても信号の仕様がわかんないと始まりません。

幸運なことに、セガサターンは発売から時間が経っているハードなので、ネットを探せばあちこちに仕様が転がっています。

コントローラーのピンアサインはこちらとか。
あとは実機でテスターとか使いながら見た感じです。

信号の仕様とか細かい仕様はここが大変参考になります。なんだこのページ…

マルコンの仕様はこの資料のp97あたりでしょうか。
雑に言うとホスト側(セガサターン本体)からTHとTRをパタパタさせると順番に各ボタンのステートを返してくれるので、それらをホスト側で読むって感じですね。

ということで、ここら辺の情報からまずは回路を書いていきましょう。

ハードウェア編

回路図を書いてみる

早速全体見せるとこんな感じです。

基本的にはマルコンからの入力をPICマイコンに直で入れる感じで、できる限り部品が少なくなるように書いてます。
小型化が必要ですからね。

マルコン側のコネクタJ2のpin10をDET端子として使ってるのは一つのポイントかな。
マルコン側ではGNDにつながってるピンなんですが、これをマイコンにつないでマルコンの接続有無を監視できるようにする目論見です。

基板図を書いてみる

ユニットのシェルに合うように基板図を書いていきます。
で、書いたのがこれ

おもて

うら

ユニットに入る大きさまで小型化できるかがポイントでしたが、どうにか部品を置くことができました。

お次は製造です

基板を製造します

基板のガーバーデータをメーカーに出図して基板を製造してもらいます。
今回はJLCPCBに出しました。

発注時のオプションで気を付けたいのは表面処理です。
カードエッジなのでほんとは金メッキを選択するところですが、試作なのでいったん半田リベラで注文しました。

部品を実装してみる

今回はお試しなので、部品一つずつ手ではんだ付けしていきました。
こんな感じ

ケーブルつけるとこんな感じ。

ユニットにもぴったり入る。

シェルも作ることにする

いい感じにできたのでこれで進めようと思ったんですが、ここでひらめきました。
「3Dプリンター使ったら拡張ユニットのシェルも作れるんじゃないか?」と。

ということで拡張ユニットのシェルも設計してみました。

3Dプリンターを使いながら結構エラーアンドトライしてなんとかいい感じにできたと思います。
意外とできるもんですね。

最終的な製造は精度が必要なので、JLCPCBの3Dプリントサービスで光造形タイプを使って作りました。

いろいろ試したけど、黒レジンがいい感じでした。

回路と基板を設計しなおす

新シェルに合わせて回路と基板も設計しなおしました。
コネクタをUSB Type-Cにして、基板外形を調整した感じ。

回路図 rev.B

基板図 rev.B

シェルと基板を組み合わせる

新しくできた基板とシェルを組み合わせるとこんな感じになりました。

マルコンにもピッタリです

マルコンにもピッタリ

USB Type-Cを挿すとこんな感じ

これでハードウェアの方は設計完了です。
やったね。

ソフトウェア編

プログラムを書く

問題はマルコンから来る信号をどうやって受けて、USBに変換するかですね。
とはいえ、前述のとおり仕様はここに書いてますので、実装するだけといえばそれだけ。

ソースコードはgithubで公開してるので、そちらを参考にしてください。

github.com

あと今回もUSB Bootloader入れときました。
これを入れとくと、USB経由でファームウェアが書き換えられて便利です。
起動時にアダプターがマルコンに接続されているか確認し、接続されていなければBootloaderが立ち上がるというようなことをしてます。

あ、追加ファームウェアとしてXinputモード版も作りました。

動かしてみる

できたアダプターにプログラムを書き込んで動かしてみました。

ちゃんとアナログモードとデジタルモードの切り替えで動作が変わっていい感じです。

いろいろゲームをプレイしてみたのは冒頭のデモ映像の通りです。
見た目がごついんですが、手によくフィットしてくれます。

最後に

今回はセガサターンのマルチコントローラー 通称マルコンをUSB化する拡張ユニットを作ってみました。
20年以上前のコントローラーですが、アナログスティックもすごく感度よく動きました。
アナログスティックとLRトリガーが非接触なので、他のコントローラーでは味わえない感触でとってもいい感じです。

近日中に頒布もするんで、よろしくお願いします。

今回はここまで。
読んでくれてありがとうございました。

Arduino互換機でArduino Bootloader書き込み装置を作ろう

Arduino互換機をベースにArduino Bootloader書き込み装置を作ってみました。
Arduino互換機とソケットとちょっとの部品をはんだ付けすれば簡単に作れます。

これを使えばATMEGA328Pを簡単にArduino化できて、Arduino量産し放題です。
同じ書き込み装置でスケッチの書き込みもできます。

Arduino Bootloader書き込み装置の構成

こちらのArduino公式サイトにBootloaderの構成方法が載っています。

こんな感じのやつ。

Arduino二個の場合のBootloader書き込み用接続

この図だとArduinoが二個いるように見えますが、書き込まれる方はATMEGA328Pが動く最小構成でOKです。
で、今回作ろうとしているのはそういうやつです。

あとは、Bootloader書き込み対象のATMEGA328Pを挿抜できるソケットがあればOKです。

つまりこんな感じ。

Bootloader書き込み装置回路

接続も特に難しいところはなく、線を6本ArduinoからATMEGA328Pにつないでるだけです。
上の図ではATMEGA328Pを直接ユニバーサル基板に置いてるっぽくなってますが、実際はソケットを使います。

作ってみる

回路図書いたので、あとははんだ付けしていくだけです。

今回は純正Arduinoの代わりにUniversALDUINOというArduino互換機を使います。
ユニバーサル基板がついたArduinoなので、今回のように完成品を作る場合は便利な互換機です。

材料

というわけで、材料はこんな感じ。

材料 個数 購入先 備考
UniversALDUINO 1 SwitchScience
ゼロプレッシャーICソケット(28Pin) 1 秋月電子
100nF セラミックコンデンサ 1 秋月電子
22pF セラミックコンデンサ 2 秋月電子 未確認だけどなくてもいけるかも
16MHz 水晶 1 秋月電子
1MΩ抵抗 1 秋月電子 未確認だけどなくてもいけるかも
10kΩ抵抗 1 秋月電子
スズメッキ線 1 秋月電子 配線用
ワイヤー 1 秋月電子 配線用
半田 1 amazon
スペーサーとねじ 各4 秋月電子の適当なやつ なくてもいい。自分は家にあったので使った
ATMEGA328P 好きなだけ 秋月電子 書き込み対象

はんだ付け

基本はさっき書いた回路図に沿って配線とはんだ付けをしていきます。

できたのがこれ。

ソケットが思いのほか大きかったけど、UniversALDUINOに十分収まりました。

Bootloader書き込み装置用スケッチの書き込み

Bootloader書き込み装置用のスケッチはArduino IDEにあらかじめ入っています。

"ファイル"→"スケッチ例"→"11.ArduinoISP"→"ArduinoISP" がそれになります。

UniversALDUINOのUSB-C端子とPCを接続し、このスケッチを開いて"マイコンボードに書き込む"を押せばOKです。

これでArduino Bootloader書き込み装置は完成です。

動かしてみる

ATMEGA328PにBootloaderを書き込んでArduino化する

まずはATMEGA328Pをソケットに挿して準備します。

Bootloaderの書き込みにはPC側でArduino IDEの操作が必要です。
手順は以下の通り

  1. ”ツール”→”ボード”→”Arduino UNO” を選択
  2. ”ツール”→”書き込み装置”→”Arduino as ISP(ATmega32U4)”を選択
  3. ”ツール”→”ブートローダーを書き込む”をクリックし書き込み実行

これでATMEGA328PがArduinoになりました。

Arduino化したATMEGA328P

Arduino化したATMEGA328Pにスケッチを書き込む

Arduino化したATMEGA328Pへのスケッチの書き込みも、同じ書き込み装置を使ってできます。

やり方は簡単で、書き込み装置にATMEGA328Pをセットした状態で"スケッチ"→"書き込み装置を使って書き込む"を選択すればOKです。

Arduino化したATMEGA328Pを動かしてみる

試しにできたArduinoにBlink (Lチカ)のスケッチを書き込んで動かしてみましょう。

書き込み装置にATMEGA328Pをセットした状態でBlinkのスケッチを開き、"スケッチ"→"書き込み装置を使って書き込む"で書き込み完了です。

スケッチを書き込んだArduinoを使って、ブレッドボード上でLチカの回路を組むとこんな感じです。

電源をつないだだけでちゃんとLチカが走りました!
スケッチがちゃんと書き込めてるってことですね。いい感じ。

最後に

今回はArduino Bootloader書き込み装置を作りました。
これでArduinoが簡単に量産し放題なので、Arduinoベースの電子工作が捗りますね。

今回はここまで。
読んでくれてありがとうございました。

ユニバーサル基板付Arduino互換機(USB type-C)を作りました

このエントリーをはてなブックマークに追加

Arduino UNOにユニバーサル基板がくっついてて、直接部品をはんだ付けして遊べたらいいなと思ったんで作っちゃいました。

頒布中です。
製品ページはこちら。
geekyfab.com

回路を描いてみる

Arduinoはオープンソースなので回路が公開されてます。

https://www.arduino.cc/en/uploads/Main/Arduino_Uno_Rev3-schematic.pdf

これを参考に回路を描いていきます。

で、描いたのがこれです。

pdfも置いときます。
https://drive.google.com/file/d/1QOIp-zrMsw029SfpdKsc4x77Gn-aM0-h/view?usp=sharing

結構変えてますね。
いくつか抜粋してコメント

USB Type-Cコネクタにしました

せっかくなので USBはType-Cコネクタにしました。

いいですよねType-C。

電源回路をより安全にしました

↓の記事で書いたように、元の電源回路はちょっと危ないので対策を入れました。
geekyfab.com

VINにダイオードを入れました
前回記事で書いた通り、VINはノーガードになってるのでいまいちです。

ということで、自作回路の方はショットキーバリアダイオード(D304)入れました。

ショットキーバリアダイオードは普通のダイオードに比べて逆電流がちょっと多いですが、その中でも少なめの0.5mAとかのやつ選んでるんでまあ大丈夫でしょう。

USB VBUSに理想ダイオード回路入れました

前回記事で書いた通り、元回路のコンパレーターを使った電源ソース選択回路はイマイチで、意識して使わないとArduino本体かUSB接続の電源元(パソコンとか)が壊れる恐れがあります。

元回路のここイマイチ

で、自作回路のほうはコンパレーター自体やめて、理想ダイオード回路を採用しています。

理想ダイオード回路

この理想ダイオード回路はRaspberry Pi 3 Model B でも使われてるやつです。

https://datasheets.raspberrypi.com/rpi3/raspberry-pi-3-b-reduced-schematics.pdf

ちなみに初回試作では元回路に倣ってコンパレーター使ってたんですが、評価してみてあかんやんってなった結果、理想ダイオード回路に悔い改めたんですよね。

評価大事。

3.3V出力から電流をいっぱい取れるようにしました

元の回路ではLP2985-33DBVRが使われてますが、これ150mAしか流せないんですよね。

そんな出力で十分なのかい

ちょっと頼りないかなぁってことで、1Aまで流せるリニアレギュレーターに変えてます。

1Aもいるか?

1Aもいるかですって?
電流容量なんて多いに越したことはないでしょ。

基板を描いてみる

こんな感じにしました。

おもて

うら

Arduinoと同じ位置に信号ピンを配置して、その周りにユニバーサル基板エリアを配置してる感じです。

Arduinoの信号ピンはオリジナルと互換の位置に加えて外側にプラス2個ずつ用意してます。
(ブレークアウトピンってやつ)
シルクを置いて、つながってるぜ感を出してます。
伝われ。

大きさはとりあえず100x100。

このアートワークでJLCPCBさんに実装も含めて注文しました。

納品されました

納品されてきました。
実装まで爆速ですごいなあ。


黒レジスト金メッキカッコイイ!!
レジストにマット感があるのもカッコイイ!!
すごく高級感ある。


僕はレジスト抜きで文字とか入れるの好き。

動作確認

では早速動かしてみましょう。

Bootloader書き込み

まずはArduinoとして動かすためにBootloaderを書き込みます。
これは過去に書いた記事のやり方と同じ感じ。
geekyfab.com

接続するとこんなの。

ICSPの1~4と6ピン同士をつないで、自作ArduinoのICSP 5ピンはArduinoの10番端子につなぐ感じ。
上の写真では書き込み用にArduino MEGAを使ったけど、まあほぼ同じ。

あとはArduino MEGAにArduino ISPスケッチを書き込んで、 自作ArduinoにArduino UNOのBootloaderを書き込めばOK。

これで自作Arduino互換機が完成!

Lチカしてみる

Arduino IDEのサンプルスケッチから、Blinkを開いてLチカできるか確認してみましょう。

スケッチはこれ。

無事書き込めたみたいです。

基板上のLEDもチカチカしました!!

ということで、無事に自作Arduino互換機が動いてくれました!
やったね!

最後に

回路図があるし簡単にできるだろうって作る前は思ってたんですが、作り始めてみると変えたい部分がいっぱい出てきて意外と大変なプロジェクトでした。
回路図読みながら、なんでこんな回路になってんの?って何回思ったか…
主に電源回路で。

ともかく、これでArduinoを使った実験や作品作りが捗るはず!!

スイッチサイエンスさんとかで頒布も準備してるので、よろしくお願いします。

サポートページも作りました。
geekyfab.com

今回はこれで終わります。

本当は怖いArduino電源回路 ~ArduinoとPCを壊さないために知っておいてほしいこと~


Arduino UNO互換機を作ろうと思って回路を読んだところ、よく見るとちょっと危ない電源回路になっていることがわかりました。

少し使い方を誤ると、Arduino本体だけでなくUSB接続したPCやACアダプターを壊しそうです。

今回はArduino UNO 電源回路の怖いよポイントを三つと、どうすればArduino本体やUSB接続したPCを壊さずに済むかを紹介します。

Arduinoのデータシートを読んで使ってるから大丈夫だよ!という方も是非読んでほしいです。
データシート通りに使ってても、避けられないポイントもあるので。

【結論】ここが怖いよポイント三つ

まずは結論から。
ここが怖いよポイントは三つです。

  • VIN端子と一緒にACアダプターかUSBを使うとVIN側の電源が壊れるかも
  • USBと一緒にACアダプターかVIN端子を使うとUSB機器が壊れるかも
  • 11.4V以上のACアダプターを使用するとArduinoが壊れるかも

順番に解説していきましょう。

Arduino UNOの回路図

まずは全体の回路図を見ておきましょう。
Arduino UNO (R3)の回路図はこちらで公開されています。

https://www.arduino.cc/en/uploads/Main/Arduino_Uno_Rev3-schematic.pdf

そして問題の電源回路は上のほうのここら辺

Arduino UNO電源回路

この電源回路に怖いよポイントが三つ隠れているわけです。

Arduinoの電源の取り方三種類

怖いよポイントの前に、Arduinoの電源の取り方について書いておきます。

電源の取り方は次の三つの方法があります。

  • USBから取る
  • ACアダプターをDC JACK(回路図のX1)に挿して取る
  • VIN端子に電源をつないで取る

怖いよポイントは、主にACアダプターかVIN端子を使うときに発生します。
では、順番に説明していきます。

VIN端子と一緒にACアダプターかUSBを使うとVIN側の電源が壊れるかも

一つ目の怖いよポイントはDC JACKとVIN端子周辺回路にあります。

DC JACK(X1)とVIN端子間に注目するとダイオード(D1)が間に入っており、VINからDC JACKへの逆流を防いでくれています。

じゃあ逆方向はというと、何もないです。

なので、DC JACKとVIN端子を併用するときには注意が必要で、VINがOFFの時やDC JACKの電圧>VINのときはVINに電流が逆流してきます。
といことで、DC JACKとVINは基本的には併用しないほうがいいです。

加えて、VINとUSBの併用も危険です。
リニアレギュレータ(U1)の出力側に5Vがありますが、この5VはVINがOFFのときはUSBから供給されます。

VINがOFFでUSBから電源供給されてる状況だと、U1があるので一見5VからVINには電流が来なさそうですが、罠です。

U1にはOUTからIN方向に保護ダイオードが内蔵されているため、普通にVINに逆流してきます。

ONSEMI NCP1117データシート p.10 (https://www.onsemi.com/pdf/datasheet/ncp1117-d.pdf)

ということで、VINとUSBの併用もやめておきましょう。

USBと一緒にACアダプターかVIN端子を使うとUSB機器が壊れるかも

二つ目は一つ目と似てますが、これはUSB側が壊れるかもという問題なのでホント気を付けましょう。
USB電源としてPCを使ってると、PCが壊れる可能性があるので。

で、問題個所はここです。

これは、コンパレーター(U5)でDC JACK≒VINの電圧を見てUSB側の電源を使うかVIN側の電源を使うかFET(T1)で切り替えてる感じの回路ですね。

VIN/2と3.3Vを比較して、VIN/2>3.3V、つまりVIN>6.6VならFET(T1)がOFFになってUSB側に5Vが逆流しないようになります。

一方で、Arduinoのデータシートを見ると、VINは6V~20Vって書いてるっぽいんですよね。

Arduino UNO R3 データシート p.4 (https://docs.arduino.cc/resources/datasheets/A000066-datasheet.pdf)

6.6V以下ではFET(T1)はONなのに。

例えば6VのACアダプター使ったらFET(T1)が導通するわけです。
これだとUSB機器も一緒に使ってる場合はUSB機器に電流が逆流しかねない状態です。

特に電源OFFのUSB機器がつながった状態で6.6V以下のACアダプターとか使ったらかなりまずいですね。
USB機器壊れるかもです。

USB機器に電源入ってる状態なら問題じゃないかもですが、それでもUSB規格的にはミニマムで4.75V出力だからよくないです。

ということで、ACアダプターは8V以上くらいのものを使いましょう。
ギリギリ攻めて7Vとか使うとダイオード(D1)の電圧降下と部品のばらつきでFET(T1)がONになるかもなので。

まあ、できればUSBとACアダプターは併用しないでおきましょう。

11.4V以上のACアダプターを使用するとArduinoが壊れるかも

三つ目の問題個所もここです。

これは単純な話で、VINの電圧によってはコンパレーター(U5)の絶対最大定格超えます。

TI LMV358 データシート p.5

絶対最大定格が5.7Vなので、VIN/2 > 5.7V -> VIN>11.4Vでアウトです。

20V?

ご冗談でしょう、Arduinoさん

ちなみに、この件はこちらのスレッドでも議論されていましたのでご紹介。
Question Regarding Vin Pin of Arduino | All About Circuits

ということで、二つ目も考慮すると、ACアダプターは8V~11Vくらいのものを使いましょう。

じゃあどうすればいいか

改めて、Arduinoの電源回路の怖いよポイントは次の三つです。

  • VIN端子と一緒にACアダプターかUSBを使うとVIN側の電源が壊れるかも
  • USBと一緒にACアダプターかVIN端子を使うとUSB機器が壊れるかも
  • 11.4V以上のACアダプターを使用するとArduinoが壊れるかも

この三つの怖いよポイントを考えると、Arduino本体やUSB機器を壊さないには次の二つを守ればよいかと思います。

  • VINとACアダプターとUSBのどれか一つだけを使う
  • ACアダプターは8V~11Vくらいのものを使う

もしくは、

  • 電源回路に対策が入っているArduino互換機を使う

というのも一つの手です。
例えば、UniversALDUINOはArduino本体やUSB機器が壊れないように設計されているArduino互換機の一つです。
geekyfab.com

最後に

Arduinoは素晴らしいプロジェクトですが、電源回路はかなり危なっかしい作りになっていました。

電子工作は自己責任の部分が大きいとはいえ、ユーザーに厳しすぎる回路ではないかという感想です。

なんでこんな回路になってるんでしょうね。

まあ、実際にはICの実力だったりUSB電源側の保護回路だったりで、なかなか壊れることはないかもしれませんが、そこに期待するのは危険です。

Arduinoの回路をじっくりと読むきっかけとなった自作の互換機ですが、そちらには怖いよポイントへの対策を入れました。
↓とか

どういう対策したかの話は、作ったArduino互換機の紹介も併せて次回したいと思います。
geekyfab.com

今回はこれで終わります。


GEEKY Fab.では思い出のゲームで遊ぼうをテーマにハードウェアを作ってます。
よかったら↓から見ていってください。

ラズパイZero2WとGPi Caseでゲームボーイ風エミュレーターを作りました


Raspberry Pi Zero 2Wが手に入ったので、GPi Caseを使ってゲームボーイ風エミュレーターを作りました。
めちゃくちゃ簡単に携帯ゲーム機が作れてしまったので、ちょっと感動です。

この記事では、作り方と使い方、遊んでみた感想と不満点を書きました。

紹介編

GPi Caseって?

ゲームボーイを模したラズパイZero用のケースです。


Amazon.co.jp: レトロフラッグ RETROFLAG ゲームボーイ風ラズベリーパイケース GPi Case Raspberry Pi Zero Raspberry Pi Zero W 432219 : パソコン・周辺機器

これホントよくできてて、液晶もボタンも一通りついてるから、専用のOSを焼いたラズパイZeroを入れるだけですぐに遊べるんですよね。
しかも、専用OSは公式のRaspberry Pi Imagerから書き込めるという優遇っぷり。

Raspberry Pi Zero 2Wって?

ちっちゃいラズパイです。

Raspberry Pi Zero 2 W — スイッチサイエンス

めちゃくちゃざっくり説明すると、Raspberry Pi 3Bをちっちゃくして、メモリを少なくして、CPUのクロックを抑えたやつです。
じゃあRaspberry Pi 3より劣るかといえばそんなことはなくて、こちらのサイトによるとゲーミングがらみのベンチマークスコアはほぼ同等となっています。
まぁエミュレータの場合はCPUのスコアが影響してくるのかもしれんけど、よくわからんので各自調べてください。

あと今回の用途では大事な特徴として、先代のRaspberry Pi Zeroと形状・ピンアサインがすべて同じになっています。
そのため、もともとはZero用に作られたGpi CaseにZero 2Wを組み込むことが可能になってます。

作ってみた編

OSの準備

まずはOSの準備からしていきましょう。

GPi Case用の専用OSのうち、Raspberry Pi ImagerからインストールできるOSはRecalboxLakkaの2種類です。
RetroPieも使えますが、使いにくいので試してません。
初期設定とかムズいんで。

あと、LakkaのほうはインストールしてもGPi Caseが上手く動いてくれなかったので、今回はRecalboxだけ紹介します。

  1. Micro SDをPCに挿す。
    今回使用したMicroSDはBuffalo製の32GBのやつです。

  2. Raspberry Pi Imagerを起動して、「OSを選ぶ」→「Emulation and game OS」→「Recalbox」→「Recalbox - GPi Case + Raspberry Pi Zero 2」と選択。

  3. 「ストレージを選ぶ」を選択し、Micro SDのドライブを選択

  4. 「書き込む」を選択し、書き込み実行

これで完了です。

組み立て方

わかりやすい説明書が同梱されてますし、めちゃくちゃ簡単なので、ここではさらっと紹介しときます。
10分くらいでできるんじゃないかな。

ちなみに、内容物はこんな感じでした。

本体とねじとドライバーと電源ケーブル、あとはマニュアルですね。

では早速組み立てていきましょうか。

まずはカートリッジを外します。

写真では特に意味なく電池の蓋も外してます。

次にカートリッジを開けます。

中には基板が入ってます。
この丸っこいピン(ポゴピン)にラズパイの端子がフィットするんですね。
ばね性があるのでラズパイの端子としっかり導通するようになってます。
よくできてますね。

基板の裏はこんな感じ。

続いてケーブルをラズパイZero2WのUSBポートに挿します。

そしてポゴピンにラズパイの端子を合わせて折り畳み、

カートリッジに戻して

蓋を同梱されてるねじで締めます。

あとはカートリッジを本体に戻して完成です。

Safe Shutdown Scriptのインストール方法

マニュアルのほうにはSafe Shutdown Scriptあるよって書いてあるので、必要ならインストールしましょう。

やり方としては、別PCからGPi Caseにssh接続してインストール用スクリプトを実行するのがいいと思います。
難しそうであれば、この手順は飛ばしても遊べます。
気が向いたら戻ってきてください。

手順は次の通りです。
GPi Caseは電源ONの状態で行ってください。

まずはPCでwindows powershellを立ち上げます。

powershellで次のコマンドを入力して、GPi Caseにssh接続します。

ssh root@RECALBOXGPI

「Are you sure you want to continue connecting」とか聞かれた場合はyesと回答してください。
その後すぐにパスワードを聞かれると思うので、「recalboxroot」と入力してEnterしてください。
これでGPi Caseにsshで接続できました。

続いて次のコマンドを入力してSafe Shutdown Scriptをインストールします。

wget -O - "https://raw.githubusercontent.com/RetroFlag/retroflag-picase/master/recalbox_install_gpi.sh" | bash

インストールが終わるとGPi Caseが勝手に再起動するので、これを待ってSafe Shutdown Scriptのインストールは完了です。

ああ、GPi Caseの電池カバー内のSafe ShutdownスイッチをONにするのも忘れずに。

パッチのインストールについて

パッチをインストールしろって書いてあるんですけど、当てたら起動しなくなり、逆にアンインストールしたら起動するようになったので、Recalbox+Zero2Wの場合は当てなくていいです。
Recalbox以外とZero2W以外の場合はわかりません。

ゲームの入れ方

ゲームのromデータは別PCから無線LANを介してネットワーク上のGPi Caseに入れます。

ROMデータは、例えばCartReaderなどを使ってカセットから吸い出して用意してください。

参考記事↓
geekyfab.com

では早速入れ方ですが、別PCでエクスプローラーを開き、ネットワークをクリックすると、RECALBOXGPIという名前で見えます。

ネットワーク上にRECALBOXGPIがいる

「RECALBOXGPI」→「share」→「roms」とクリックすると各コンソールごとのフォルダがあります。

\RECALBOX\share\roms

この各コンソールごとのフォルダに用意したROMデータを置けばOKです。

例えば、ファミコンのロックマン3を遊ぶには、「nes」フォルダにROMデータを置けばOKです。
(nesはファミコンの国外版です。)

遊んでみた編

遊んでみた

準備が完了したので、さっそく遊んでみました。
ずっとやりたかったファミコンのロックマンシリーズ

ロックマン3

ロックマン4

スクリーンショットも取れます。

ロックマン3 なんとかワイリーステージまで来た…

むずい!でも楽しい!!
それにしてもファミコンからこんなむずかったんだ。
RTA動画だと簡単そうに見えたのに(※当たり前)
どこでもセーブ機能がなかったら完全に心折れてます。

ほかにもスーファミとか64とかもちょっとだけ触ってみました。

スーパーマリオワールド このボスこんなに強かったっけ…

スーパーファミコンのスーパーマリオワールドは問題なくプレイできました。
処理落ちもなく、これはいい感じ。

ゼルダの伝説 ムジュラの仮面 デクナッツのお面怖すぎ

NINTENDO64のムジュラの仮面はところどころムービーが映らない感じでした。
これはOSのせいかGPi Caseのせいか不明です。
プレイ自体は処理落ちもなく、かなり快適にプレイできました。
とはいえ、そもそもボタンの数が足りないんですけどね。
cボタン押せないので詰みます。

本体についての出来栄えはかなり満足です。
最近のゲーム機としては液晶の小ささが気になるかなと思ってましたが、プレイしているうちに全く気にならなくなります。
昔のゲームボーイとかも液晶めちゃくちゃ小さかったけど、遊んでるうちに液晶の大きさを忘れてるあの現象です。

電池持ちについて

GPi Caseは単三電池3本で駆動します。

満充電状態のエネループで遊んでみたところ、合計で約二時間半ほど遊べました。
amazonレビューとか見ると、そもそもエネループでは動作が安定しないとか書いてあったので不安なところではありましたが、問題ありませんでした。

不満な点

おおむね満足なんですが、何点か不満な点もあったので書いときます。

処理落ち

CPUなのかメモリなのかエミュレーターなのか、何が悪いのかよくわからないのですが、ファミコンのロックマン3で敵やオブジェクトがいっぱい出てくるところは処理落ちが見られました。

こいつが玉いっぱい打った瞬間とか処理落ちする

設定とかで何とかできそうな気もするけど、わかんない。
[追記]
ファミコン実機でも同等程度の処理落ちがありましたし、他エミュレーターでも処理落ちがありました。
ですので、Gpi Caseが特段処理落ちが激しいというわけではなさそうです。

Bluetoothイヤホン非対応?

オーディオ出力がスピーカーとイヤホンジャックしかなく、Bluetoothイヤホンはつながらないっぽいです。
Bluetoothコントローラーのペアリング設定はあるので、できそうな可能性は秘めてますが、うーん…

ボタンが少ない

上でも書いた通り、ボタンの数が足りないので64のゲームとかを遊ぶのはちょっと厳しいです。
スーファミまでのゲームを遊ぶのなら問題ないのですが、ボタンの数がスーファミと同じ数までしかなく、アナログスティックもありません。
一応コントローラーをペアリングすれば遊べるだろうけど、そこまでするのはちょっとなー

最後に

今回はGPi CaseとRaspberry Pi Zero 2 Wを使って、ゲームボーイ風エミュレーターを作成しました。
組み立ても分解も簡単ですし、ラズパイで何か作ってみたいという人や、レトロゲームを遊ぶための携帯エミュレーターが欲しいという人におすすめできます。

ちょっと不満な点はありましたが、総合的にはかなり満足してて、毎日のようにこれでファミコンのロックマンシリーズを遊んでます。
ロックマンが難しすぎるおかげで、しばらくはこれで遊び続けられそうです…

今回はこれで終わります。


GEEKY Fab.では思い出のゲームで遊ぼうをテーマにハードウェアを作ってます。
よかったら↓から見ていってください。

スーパーちっちゃいスーファミコントローラー基板を作りました(電子工作やコントローラー自作派向け)

このエントリーをはてなブックマークに追加

スーパーちっちゃいSFCコントローラー基板を作ってみました。
自作SFCコントローラーとかアケコンとか、あとArduinoやラズパイにつないで遊んだり電子工作したりするためとか。

サイズは約2cm x 5cm。
私調べによると世界最小です。

この記事はサターンパッドUSB化基板の製作日記のようなものです。
完成品の紹介ページはこっちです。
geekyfab.com

作るよ編

SFCコントローラーはどう動いてるの?

SFCコントローラの回路って次の写真のように、ICが一つのタイプと二つのタイプがあります。

どちらの回路もただの16bitのParralel-in Serial-out(PISO)のシフトレジスタとして動作するようです。
ICが二つ乗ってるほうは、8bitのシフトレジスタをつなげて16bitにしてる感じですね。

ただ、回路のどこにも抵抗が実装されていないところを見ると、普通のシフトレジスタとは違って、内部でプルアップしてるっぽいです。
Mouserとかで探した感じだと、そのようなシフトレジスタは見つからなかったので、結構特殊なICのようです。
低コスト化の気迫を感じる。

SFCコントローラーの回路を描いてみます

ということで、実物とネットで調べた情報をもとに作れる回路を描くと、こんな感じになりました。

8bitのシフトレジスタを二個接続して、各ラインにプルアップ抵抗をつけただけの簡単な回路です。
各ボタン端子とGND端子をショートさせると各ボタンが押される感じですねー

参考URL

SFCのコントローラを調べてみる(の続き) | 懐古主義者の暇つぶし
Arcade style controller for Snes, NES and PC DIY Super Nintendo Breadboard Controller

基板を描いてみます

基板の形状ですが、小型なのはもちろん、ブレッドボードにさして遊べるようにArduino Microと同形状にしました。
できた基板図はこんな感じ。

表面

裏面

裏面がまるまる空いたので、ロゴとメッセージを入れときました。

基板を製造してみます

基板のガーバーデータをメーカーに出図して基板を製造してもらいます。
今回はJLCPCBに依頼しました。

ここらへんのオーダーの仕方はググったら無限に出てくるので割愛します。
今回は特殊な設定もしてませんし。

部品実装もお願いしてみます

JLCPCBでは部品実装サービスもやってるので、今回はお願いしてみることにしました。
ここらへんの(以下略

一点、困ったことにKicadの実装用ファイルをそのままアップロードしただけだと実装位置めっちゃずれてたんですよね。
↓は違う基板だけど、こんな感じ。

ファイルを直接いじっていい感じに修正したんですが、結局JLCPCBでの最終チェック時に「ずれてるから直したよ」的なメッセージが来たんで別に修正する必要なかった気がします。
よくわからん。

できたよ編

基板がやってきました

で、待ってたら基板がやってきました。

めっちゃちっちゃいです。
だいたい2cm x 5cmですからねー。
これは組み込みやすそう。

動かしてみます

では早速動かしてみます。

まずはケーブルをはんだ付けします。

そこらへんに転がってるSFCコントローラー基板からケーブルをチョッキンして、ワイヤーストリッパーで被覆を剥いであげます。
ワイヤーストリッパーがなければニッパーでも剥げますし、100均でも売ってた気がします。
ちなみに私が使ってるワイヤーストリッパーはエンジニア製のこれ
エンジニア製の工具が好きなので。

はんだ付けしたのがこんな感じ。

ケーブルは1pin(GNDピン側)から茶、赤、オレンジ、黄色、白の順ではんだ付けです。
もとの並びと同じですね。

一緒にピンヘッダーもはんだ付けしてます。
ピンヘッダーは2.54mmピッチのよくあるやつがささるようにしてます。
どこでも売ってると思いますけど、うちではAliexpressで大量に買ってます。
多分これかな。
こんなんなんぼあってもいいですからね。

では早速動かしてみましょう。
ジャンパーケーブル使って簡単に動かしてる様子がこれです。

GND端子と各ボタン端子をショートさせることでちゃんとマリオが動いてますねー
動いたときはかなり感動しました
めっちゃはしゃぎながらやってました

ブレッドボード上でコントローラーを作ってみます

ブレッドボードにさせるようにしたので、ブレッドボード上で簡単に遊べるようになってます。
一部ボタンだけつないだだけですけど、こんな感じです。

一気にコントローラーぽさが出てきましたね

サターンパッドに組み込んでみます

もうここまででかなり満足しましたが、せっかくなんでサターンパッドに組み込んで、サターンパッドをSFCパッド化してみることにします。
なぜか我が家にいっぱいありますからね、サターンパッド。

作り方は簡単!
サターンパッドの中の基板を取り出してSFCコントローラー基板を貼り付け、

ワイヤーをはんだ付けし、

元通り組みなおして完成!

ちゃんと動きました!

いいですねー

この時のケーブルはAliexpressで買ったこのケーブル使ってます。
各内部ケーブルの色が純正のものと違い、どの色がどの信号か分からんかったのでテスターで調べました。
参考までですが、私が購入したものは茶=GND、赤=DATA、黄色=P/S、青=CLK、緑=5Vになってました。

最後に

今回はちっちゃいSFCコントローラー基板を作りました。
形はArduino Microと同じで、約2cm x 5cmです。
ちっちゃいですねー

それにしても、スーファミのコントローラー基板って意外と簡単に作れるもんなんですね
マリオが動いたときは本当に感動しました

準備ができたら配布もやっていこうと思うので、みなさんよろしければどうぞです。
自作コントローラーやアケコンにはもちろんですが、子どもの電子工作入門とかにもいいんじゃないかなーって気がしてます。
自分が作ったコントローラーでキャラが動くって最高ですし。
親子でSFCコントローラーを作ろうってワークショップとかどっかでやりたいですねー。

今回はこれで終わります。

サターンパッドをUSBゲームパッド化する基板を作りました

このエントリーをはてなブックマークに追加

セガサターンのコントローラー、通称サターンパッドをUSBゲームパッド化する基板を作ってみました。
この記事はサターンパッドUSB化基板の製作日記のようなものです。

完成品の紹介ページはこっちです。
geekyfab.com

以前にSFCコントローラーをUSBゲームパッド化する基板を作りましたが、今回はそれの亜種になります。
基本的な作りは同じで、基板の形状がサターンパッドになっている感じ。

設計データはgithubに公開してます。
github.com
基板データは公開準備できてないですが、そのうち公開します。

頒布もやる予定ですのでよろしくお願いします。

こんな感じの仕様にします

基本的にはSFCコントローラーUSBゲームパッド化基板の設計を踏襲しつつ、作っていく感じにします。
違うところといえば、基板形状とボタンくらいなので、そこをどうするか。

特にLRボタンをどうするかが一番の悩みどころ。
サターンパッドのLRボタンにはタクトスイッチが使われていますが、同じタクトスイッチは手に入らなさそうだったので、部品選定に悩みました。

あと、SFCコントローラーUSBゲームパッド化基板でも付けていた機能で、十字キーモード切替機能とボタン二重登録機能がありましたが、これも入れます。

回路図を書いてみます

こんな風になりました。

pdfはこちら。
usb-ss-gamepad/hardware/SS_Gamepad_revB at main · takusan213/usb-ss-gamepad · GitHub

メインマイコン

メインマイコンはPIC16F1459です。
秋月電子によるとEOLの予感があるマイコンです。困る。

LRボタンのタクトスイッチ

純正のサターンパッドのLRボタンのタクトスイッチはかなりやわらかめでストロークも長めのものが使われています。
同じ仕様のものは手に入らなさそうだったので、今回は近い仕様で、自分が気に入る押し心地のものを探しました。
で、最終的に選んだタクトスイッチがこちらです。
www.mouser.jp

個人的にはかなりいい感じの押し心地かなと思ってます。

純正のものとの比較表は以下の通りです。

作動力 移動量
本製品 1.6N 0.5mm
サターンパッド 約1N(実測) 約1mm(実測)

仕事 = 力 × 距離 なんで、計算上でも近い押し心地とも言えなくもない…かも…?

ちなみに、純正のサターンパッドのタクトスイッチに付け替えることもできるようになってます。

ドナー提供すれば付け替えも可能

基板図を書いてみます

これが結構大変な作業で、サターンパッドにピッタリ合うように基板外形や穴、ボタン用パッドを描く必要があります。

以前にSFCのやつ作ったときは基板を作っては微調整してまた作って…みたいなことをしてたんですが、今回は3Dプリンターを使うことでかなり高速に設計がすることができました。
具体的には
KiCADで基板外形を描く→dxfで出力→3DCADで3D化→3Dプリンターで出力
のサイクルを回すといった具合です。

これにより、一発でサターンパッドに合う基板を設計することができました。

こういうので基板外形とかを調整しました

最終の基板図は↓の感じです。

部品面

ボタン用パッド面

基板を製造します

基板のガーバーデータをメーカーに出図して基板を製造してもらいます。
今回はAll PCBに出しました。

発注時のオプションは基本的にはデフォルトですが、表面処理は金メッキを選択します。
これはボタン用パッドの酸化を防ぐためです。

そして、届いた基板がこちらです。

金メッキのロゴ付きでかっこいい。

部品を実装します

頒布のために大量に製造する必要があったので、クリームはんだを使って部品実装してます。
もちろん、手はんだでも十分実装可能です。私も試作一枚目は手はんだで実装しました。

部品実装時の写真は撮り忘れてたので、表面実装後の写真だけ…

表面実装後の写真

USBケーブルを作ってくっつけて完成

きれいに作るためにコネクタにUSBケーブルを挿す設計にしたんで、これ用のUSBケーブルを作る必要があります。
あと、コントローラーの根元についてるケーブル保護用のアレも作ります。

コレ

コントローラーの根元についてるケーブル保護用のアレを作る

ストレインレリーフ(Strain Relief)っていうらしいです。
サターンパッドのケーブルにくっついてるストレインレリーフを実測して、3D CADデータに落とし込んでいきます。

で、こんな感じになりました。

3D-CADで描いたストレインレリーフ

ケーブルとの固定はホットメルトボンドを流し込んで行うのですが、流し込んだホットメルトボンドが抜けにくいように内側をギザギザにしています。

根本(下のほう)の内側をギザギザさせてる

ギザギザがないと、ホットメルトボンドを流し込んでも結構簡単に抜けてしまいましたので、ちょっとした工夫です。

で、3Dプリンターで印刷している様子がこちら。

かなりいい感じの出来栄えかと思います。

まあ、きれいに出来上がるまでに死屍累々も生み出されたわけですが…

USBケーブルを作って引っ付ければ完成

あとは作ったストレインレリーフを使ってUSBケーブルを作り、基板と組み合わせてホットメルトボンドで固定すれば完成です。

プログラムを書く

ここらへんはSFCコントローラーUSBゲームパッド化基板とほぼおんなじなので、気になる方はそっちを読んでみてください。

ボタン切り替え機能はstart+L / start+R 長押しで切り替えられるように実装しています。

プログラムをpickit3などで書き込んだ後は基板上のパッド二か所(J101, J102)は半田でブリッジしておいてください。 これで基板がpickit書き込みモードから通常使用モードになります。
pickitでプログラムを書き込みたい場合は、逆にブリッジをなくします。

サターンパッドをUSBゲームパッド化してみる

では、基板にプログラムを書き込んだらサターンパッドに組み込んでUSB化してみましょう。

これを

こうして

こうじゃ

はいできあがり。

早速ゲームで遊んでみましょう。

遊んでみる

動作確認

遊んでみる前に、一応動作確認。

ボタンと十字キーが全部動きますね。

start+Lを3秒長押しで十字キーモード切り替え機能も動きます。

start+Rを3秒長押しでボタン二重割り当て機能も動きます。

ということで、動作確認OKです。

レトロフリークで遊んでみる

レトロフリークでマリオワールドをやってみました。

反応良好です。

Steamで遊んでみる

まずはコントローラーのキー割り当てをします。

十字キーモード切り替え機能とボタン二重割り当て機能を使いながらのキー割り当ては結構難しかったですが、なんとか登録OK。

キー割り当て 結構難しかった

とりあえず、cupheadやってみました。

いい感じ!

最後に

今回はサターンパッドをUSBゲームパッド化する基板を作ってみました。
意外と3Dプリンターが役に立ったプロジェクトでした。

出来上がりもかなり満足しています。

作った基板のガーバーデータとファームウェアのソースコードはGithub上で公開してるので、よろしければぜひ作ってみてください。
(基板データは公開準備中)
頒布も予定してます。

今回はこれで終わります。

オープンソースなゲームダンパー CartReader V4を作ろう

このエントリーをはてなブックマークに追加


CartReaderはたくさんのゲームハードに対応しているオープンソースのゲームダンパーです。

2021年末にメジャーバージョンアップがあり、Cart Reader Version 4が公開されたので作ってみました。
今回はその製作過程を公開したいと思います。

せっかくのオープンソースプロジェクトです。
この記事を読んだら作れるように書いたつもりなので、皆さんにもこの記事を読んで自作にチャレンジしてほしいです。

Cart Readerについて

改めてCart Readerについて簡単に説明します。
Cart Readerはgithub上で運営されているオープンソースのゲームソフトROM吸出し機(ダンパー)です。

github.com

大きな特徴は次の二つです。

  • ハードウェアもソフトウェアもオープンソース化されており、だれでも自由に作ることができる。
  • 対応しているゲームハードはFC, SFC, メガドライブ, N64,,, と多岐にわたる。

プロジェクトは誰でも簡単に作れることを目指して運営されてるっぽいです。
Wikiが充実していますし、設計は買ってきたモジュール同士をつなぎ合わせることがメインなので、難しい作業がほとんどありません。
簡単なはんだ付けができれば十分です。

では、さっそく作っていきます。

必要な材料

必要な材料はざっくりと大別すると、

  • 基板類(MAIN基板と各種カートリッジ基板)
  • モジュール類
  • こまごました部品類

という感じでしょうか。

それぞれの説明と入手の仕方を書いていきます。

基板類

基板類はgithub上の基板設計データ(ガーバーデータ)から作ります。

といっても、JLCPCBなどの基板メーカーサイトにガーバーデータをアップロードして注文するだけの簡単な作業です。

私は今回PCBGOGOを使用しましたが、どこのメーカーでも手順はだいたい一緒です。
ガーバーデータのダウンロードから基板の注文までの手順を簡単に書いておきます。

① 基板のガーバーデータをダウンロードします。

基板のガーバーデータはリポジトリのhardwareフォルダの各ゲームハードのフォルダにzip形式でアップされています。
https://github.com/sanni/cartreader/tree/master/hardware

例えば、MAIN基板のガーバーデータはmain_pcbフォルダのmain_pcb_gerber.zipです。
私はファミコン、スーファミ、N64のゲームカートリッジの吸出しを行いたかったので、次の4つのガーバーデータを使いました。

  • cartreader/hardware/main_pcb/main_pcb_gerber.zip
  • cartreader/hardware/famicom_adapter/famicom_adapter_gerber.zip
  • cartreader/hardware/snes_adapter/snes_adapter_gerber.zip
  • cartreader/hardware/n64_adapter/n64_adapter_gerber.zip

githubのリポジトリごとダウンロードかクローンしておくのがおすすめです。あとでほかのファイルも使うので。

② 基板メーカーサイトにzipファイルをアップロードします。

ダウンロードしたzipファイルを基板メーカーサイトにアップロードします。
例えばJLCPCBだと、まずトップページの"Instant Quote"をクリックし、

"Add gerber file"をクリックし、ダウンロードしたzipファイルを選択すればアップロード完了です。

うまくアップできればこんな感じの表示になるかと思います。

③ 基板の製造条件を選択します。

アップロードが完了したら、あとは基板の製造条件を選択してカートに追加し、清算すれば基板を製造してもらえます。

製造条件は初期設定だと下記の感じかと思います。

基本的には、この初期設定からいじる必要はありません。
好みで、"PCB Color"と"Silkscreen"を変更してもよいかと思います。

設定が終わったらSave To Cartを押してカートに追加しておきましょう。

これでMAIN基板が製造準備OK状態になりました。

あとは残りの基板についても同様の作業をしていけばOKです。

これで基板の準備は完了です。
深圳から基板が来るのを待ちましょう。

モジュール類とこまごました部品類

必要なモジュール類と部品類はほぼほぼAliexpressでそろえることができます。
一覧は次の表のとおりです。

材料名 備考 商品リンク
arduino mega 2560互換マイコンモジュール ※1 Aliexpress
SDカードスロット付きLCDモジュール(MKS MINI12864 V3) Aliexpress
SI5351クロックジェネレータモジュール N64/スーファミ/NP/SVでのみ必要 Aliexpress
18650バッテリー取り付け用モジュール なくてもよい。一応買ったけど使ってない。 Aliexpress
18650バッテリー なくてもよい。変なとこから買うと危ないのでAliexpressで買わないこと。
2.54mmピッチ 40pin ソケット カットして使用する Aliexpress
2.54mmピッチ 40pin ピンヘッダー カットして使用する Aliexpress
オス-メスのジャンパーワイヤー リード線でもよい Aliexpress
スライドスイッチ 1,2個でいいのに50個も入ってる Aliexpress
SDカード FAT32かexFATでフォーマットしておくこと。ラズパイでも使えるようにmicroSDカードが良いかも(→リンクはMicroSD to SD変換付き) Amazon
100nFコンデンサ(2012サイズ) スーファミの一部カセットの吸出しに必要。→はサンプルブック Aliexpress
PIC12F629T-I/SN スーファミの一部カセットの吸出しに必要。 秋月電子
1kΩ抵抗 N64スロットに必要 秋月電子
※1
旧版と新版のarduino mega 2560互換マイコンモジュールがあり、新版では動かないとの注記が元リポジトリに追記されていました。
見分け方の画像を元リポジトリから以下に転載させていただきます。

出典:https://github.com/sanni/cartreader/wiki/What-to-order

今回はバッテリーは無しで組み立てました。
わざわざバッテリーを搭載するメリットをそんなに感じなかったので。
USBから電源供給なので、モバイルバッテリーを使ってもいいですしね。

ひとまずPIC12F629もなしで組んでます。
ただ、実際に使った感じ、吸い出せないスーファミカセットがあったのでやっぱり必要かも。

あと、カートリッジスロットの商品リンク一覧は下の通りです。
遊びたいゲーム機に合わせて選んでください。

今回は上三つを購入しました。

必要な工具

私が実際に組み立て時に使った工具を書いときます。
基本的にははんだごて一式とニッパーがあればなんとかなると思います。

工具名 型番 備考 商品リンク
はんだごて FX600-02 二本あると便利。二本目は安いのでOK Amazon
こて台 633-01 Amazon
はんだ FS402-02 Amazon
ニッパー 不明 今回はダイソーのやつを使用 -
ワイヤーストリッパー PA-14 ニッパーでも代用可能。(ダイソーのでは厳しいかも) Amazon
ピンセット P-880 Amazon
のこぎり 4907052107489 SFCスロットの加工に使用 Amazon


必要なものがそろったら早速組み立てていきましょう。

組み立て手順

材料を並べるとこんな感じです。

では、さっそく組み立てていきましょう。

Mega 2560 Proにプログラムを書き込む

まずは、Mega 2560 Proにプログラムを書き込みます。
プログラムの書き込みにはArduino IDEを使います。
持ってない方はこちらからインストールしておきましょう。

プログラムの準備

プログラム本体はリポジトリのcartreader/Cart_Reader/Cart_Reader.ino です。
まずはこのファイルをArduino IDEから開きましょう。
こんな感じで開くかと思います。

上にずらーっとタブが出てると思うので、option.hタブに移動して、次の修正をします。

  1. HW4をアンコメント、HW1,HW2,HW3をコメントアウト

  2. clockgen_installedをアンコメント

必要なライブラリのインストール

次のライブラリが必要です。

Arduino IDEのスケッチ→ライブラリをインクルード→ライブラリを管理 からライブラリマネージャを開き、

必要なライブラリを各リポジトリ名で検索してインストールしてください。

IDEの書き込み設定

IDEのツールメニューからボード:Arduino Mega or Mega 2560に設定、プロセッサ: ATmega2560(Mega 2560)に設定します。

プログラムの書き込み

Mega2560をPCとUSB接続し、IDEのツール→シリアルポート からMega2560が接続されているポートを選択した後、左上の矢印アイコンを押して書き込みます。

以上で、Mega 2560にCartReaderのプログラムが書き込みできました。

SDカードの準備

FAT32かexFATでフォーマットしたSDカードを用意します。
SDカードをPCに接続し、リポジトリのsdフォルダ配下のファイルをすべてSDカードにコピペしておきます。

モジュールの改造

Mega2560とLCDモジュールにちょっとだけ改造が必要です。

Mega 2560の改造

まずは下の写真の赤で囲んだ部品(Fuse)を

外します。

このときはんだごてを二本使って、部品をつまむようにしてあてると簡単に取れます。
はんだごてが一本しかない場合ははんだを大量に盛って、部品の両端子のはんだをうまく溶かしながら外しましょう。

次に、USBコネクタ側のランドにジャンパーワイヤーをはんだ付けします。
(ジャンパーワイヤーはあらかじめ半分に切って被覆を剥いておきます。)

これでMega2560の改造は完了です。

LCDモジュールの改造

R4(下の写真の赤丸)を

はんだでショートさせます。

LCDモジュールの改造はこれだけです。

ピンヘッダーとソケットのはんだ付け

ここからひたすらピンヘッダーとソケットのはんだ付けです。
大量にはんだ付けするので、換気はしっかりとおこないましょう。
では頑張ってください。

まずはMega2560に、付属していたピンヘッダーのはんだ付け。

続いて、クロックジェネレーターモジュールのはんだ付け。

次にMAIN基板へソケットとピンヘッダー、ジャンパーワイヤーのはんだ付け。

ピンヘッダーとソケットは1x40のものを使っている場合は、ニッパーで必要な長さに切って使いましょう。

上の写真の右下のピンヘッダーはLCDモジュールのコネクタ接続用ですが、2列あるのでずれに注意しながらつけましょう。
↓のようにあらかじめコネクタにピンヘッダーを挿した状態ではんだ付けするとずれないですよ。


あとは、モジュールとMAIN基板を接続していけば、CartReader本体の完成です。
まずは、ジャンパーワイヤーをモジュールと接続して、

(写真上部のモジュールはバッテリーモジュールです。最終的には使ってません。)

すべてのモジュールをがっしゃーん。

(真ん中のスロット部については次で説明します。)

バッテリーモジュールが取りつかない場合の接続は下の写真のようになります。

バッテリーモジュール用のジャンパーワイヤーと写真左上端のソケットの一番左に接続してあります。

これでCart Reader本体は完成です。
ひとまずお疲れ様です。
続いて、各スロット部を組み立てていきましょう。

スロット部の組み立て

ファミコン用とN64用についてはひたすらはんだ付けするだけです。
N64用1Kと書いてるとこに1kΩ抵抗をつけます。
完成品はそれぞれこんな感じ。

ファミコン用アダプタ

ニンテンドー64用アダプタ

SFC用については、スロット部品がそのままだと写真のように基板と干渉して取り付けられないので、加工が必要です。

まずは下の写真の赤線部をのこぎりなどで切り落とします。

あとはちょっと凸部が残るので、赤線のようにニッパーで切り落とします。

これで基板に取り付けられるようになります。

あとは、スライドスイッチとスロット部品、ピンヘッダーをはんだ付けすれば完成です。

これでスロット部の組み立ても終わりです。



お疲れさまでした!!
これでCartReaderの組み立てはすべて完了です。

使い方

今回はバッテリーなしなので、USBケーブルから直接電源を供給してあげます。

ここでは一例としてN64ソフト「ゼルダの伝説 ムジュラの仮面」をダンプしてみます。

N64スロットをCartReader本体に差し込み、カートリッジを上から挿し、USBケーブルを接続して電源を入れます。

メニューからNintendo 64→Game Cartridge →カートリッジの情報の表示画面 → Read Rom と選択することで、カートリッジのダンプができます。

このダンプしたファイルを使えば、PCやラズパイでゲームを遊べるようになります。

SFCカートリッジの吸出しについて

SFCのソフトには吸い出せるゲームと吸い出せないゲームがありました。
例えば、上の写真の星のカービィ 3は吸い出せなかったです。
おそらく、PIC12F629を実装して特殊チップ(SA1)に対応させることができれば吸い出せるのかな。

FCカートリッジの吸出しについて

カートリッジの情報(PRG, CHR, Mapper, RAM)をこういったサイト調べてから、それをもとにCartReaderを設定し、吸出しを行う必要があります。
詳しくは本家wiklを見ていただくのがいいかと思います。

スロットの交換について

ほかのゲーム機のカートリッジをダンプしたいときは、スロットを交換する必要があるのですが、抜くのがめちゃくちゃ固いです。
抜くときにはこのようにドライバーなどを差し込んで、テコの要領でちょっとずつ抜くようにしてください。

遊んでみる

さっそく吸い出したゲームで遊んでみたいと思います。
エミュレーターはRecalboxを使用します。

https://www.recalbox.com/diy/1-discover/

RecalboxはRaspberry PIで動作するエミュレーターです。
これについてはまた別の記事で紹介できたらいいなと思います。

遊ぶ方法は簡単です。
Recalboxのイメージが入ったSDカードのそれぞれのゲームハード用のフォルダに、先ほど吸い出したファイルをコピペするだけです。
例えばSFCのソフトだと、"SNES"っていうフォルダに吸い出したファイルを親フォルダごとコピペすればOKです。

すると、Recalboxのメニュー上で先ほど吸い出したゲームが選択できます。

これを選ぶと、ちゃんとゲームが遊べました。

写ってるコントローラーは純正のSFCコントローラーをUSB化したものです。
思い出のコントローラーで思い出のゲームを遊べるのは最高でした。

USB化基板はこっちで紹介してるんで、よかったら見ていってください。

geekyfab.com

最後に

今回は、オープンソースなゲームダンパー CartReader Version 4を作ってみました。
製作難易度はそんなに高くなかったので、電子工作入門としてもいい題材かなと思います。
でもはんだ付けするピンの数がめちゃくちゃ多かったので、はんだ付け嫌いになるかもしれないなあ。  

オープンソースなだけあって、ネットで検索すると完成品を販売してる人や製作代行してる人なんかも見つかるかと思います。

とはいえ、繰り返しになりますが、せっかくのオープンソースプロジェクトです。
ぜひ皆さんにも一度、自作にチャレンジしてほしいところです。


今回はこれで終わります。


GEEKY Fab.では思い出のゲームで遊ぼうをテーマにハードウェアを作ってます。
よかったら↓から見ていってください。

SFCコントローラーをUSBゲームパッド化する基板を作りました

今回はスーパーファミコン(SFC)のコントローラーをUSB Gamepad化する基板を作りました。

こちらの記事では、SFCコントローラー USBゲームパッド化基板の制作過程を順を追って紹介します。
設計データはgithubに公開していますので、自作にもぜひ挑戦してみてください。

お知らせ

こちらの記事で紹介している基板は製品化して、以下で頒布しています。
geekyfab.booth.pm

www.kadenken.com

また、自作キットはスイッチサイエンスで頒布しています。

www.switch-science.com

興味を持っていただいた方はご覧ください。


では本編です。

こんな感じの仕様で作ります。

SFCコントローラーの筐体にピッタリ収まるUSBゲームパッドの基板を作ります。
SFCコントローラーはボタン数が少ないので、今どきのゲームも遊べるように、Start, Selectボタンを長押しすることでボタンの割り当てを変更できるようにします。

回路図を描いてみます

全体回路図はこういう感じにしました。

SFCコントローラー USBゲームパッド化基板 回路図
基本的にはメインマイコンであるPIC16F1459のIOポートにボタン用のパッドを直つなぎしている回路になっています。
PIC16F1459はIOポートによっては内蔵プルアップ抵抗がついてたりついてなかったりのようなので、念のためすべてのボタン用のパッドにはプルアップ抵抗用のランドを設けてあります。

メインマイコン PIC16F1459

メインマイコンにはPICマイコンのPIC16F1459を使用しています。
このPICマイコンは俗にいうUSBマイコンで、USBデバイスとしてふるまうことができます。

IOポート数も多いところもよいところです。
キーマトリックスなどの回路を組まなくても、SFCコントローラーのボタン数をカバーすることができるので、回路が単純になります。

アートワークを描いてみます

今回のプロジェクトの肝です。
SFCコントローラーの筐体と基板から寸法を実測し、筐体にぴったりとはまり、ボタン位置も合うようにアートワークを設計します。
とはいえ、一発でSFCの筐体にピッタリはまる基板を作るのは難しくて、基板を製造して微調整してまた作って…みたいなことをしました。

そして、最終的に出来上がったアートワークがこちらになります。

SFCコントローラー USBゲームパッド化基板 表面

SFCコントローラー USBゲームパッド化基板 裏面

左下にちょろっとついてるのはL,Rボタン用の基板になります。

基板を製造します

基板製造はAllpcbに注文をしました。
基本的にはデフォルトのオプションになりますが、表面処理については金メッキ処理を選択する必要があります。
これは、ボタン用のパッドの酸化を避けるためです。
半田レベラーや銅箔むき出しではボタン用のパッドが酸化して反応しなくなってしまうので、金メッキ処理をして酸化を防ぎます。

そして、届いた基板がこちらです。

届いたSFCコントローラー USB Gamepad化基板 表面

届いたSFCコントローラー USB Gamepad化基板 裏面

金メッキでピカピカのきれいな基板です。
ロゴをレジスト抜きで作ってみましたが、なかなかかっこいいです。

金メッキのロゴ

部品を実装します

早速部品を実装していきます。
そして、実装後の基板がこちらです。

実装後はこんな感じです

配布するならもっときれいに作りますが、自分用なのでこんなもんでしょう。

プログラムを書いてみます

USB Gamepad化基板用のソースコード全文はGithubに公開していますので、この記事ではいくつかのポイントだけ紹介します。

github.com

このプログラムはMicrochip社が提供するMicrochip Libraries for Applications (MLA) をベースに書いています。

MLA内にはPICマイコンごとにプロジェクトファイルのサンプルが用意されています。
例えば、今回使用するPIC16F1459のUSB Joystickのサンプルプロジェクトは下記階層にあるはずです。

mla\v2018_11_26\apps\usb\device\hid_joystick\firmware\low_pin_count_usb_development_kit_pic16f1459.x

PICのポート設定とタイマ初期化

PICのポート設定とタイマ初期化はmain.cのmainの初めのほうで行っています。

    /* set all ports input*/
    TRISA = 0x30;
    TRISB = 0xf0;
    TRISC = 0xff;
    
    /* enabling internal pull up*/
    OPTION_REGbits.nWPUEN = 0;
    WPUA = 0x30;
    WPUB = 0xf0;
    
    /* all ports used as degital ports.*/
    ANSELA = 0x00;
    ANSELB = 0x00;
    ANSELC = 0x00;
    
    /* initializing timer0 and interruption*/
    
    OPTION_REGbits.PS = 0b111;        // clock divided by 256
    OPTION_REGbits.PSA = 0;         // enabling prescaler
    OPTION_REGbits.TMR0SE = 0;      // edge select (rise)
    OPTION_REGbits.TMR0CS = 0;      // clock source select (internal)
    OPTION_REGbits.INTEDG = 1;      // interrupt edge select (rise)
//    INTCONbits.TMR0IF = 0;          // reset timer0 interrupt flag
//    INTCONbits.TMR0IE = 1;          // enabling peripheral interrupts
    INTCONbits.GIE = 1;             // enabling interrupts
    
    // setting initial value of timer 0.
    // 1 clock = 256/16MHz = 16us 
    // the timer interrupt happens every 16us x 0xff(255) = 4080us .
    // 
    // set the timer0 value so that timer interruption happens every 4000us.
    // 80us / 16us = 5 clocks
    TMR0bits.TMR0 = (uint8_t)5;
    

コメントの通りです。
IOポートをすべて入力にして、内部プルアップがついてるポートはすべて有効にして、デフォルトのポート設定はアナログなのですべてデジタルに設定しています。
次に、タイマー0のパラメーターを設定しています。

USB通信に使うデータ型

USB通信で送るpacketのデータの型はapp_device_joystick.h内で次のように宣言しています。

typedef union _INTPUT_CONTROLS_TYPEDEF
{
    struct
    {
        struct
        {
            uint8_t y:1;
            uint8_t b:1;
            uint8_t a:1;
            uint8_t x:1;
            uint8_t L1:1;
            uint8_t R1:1;
            uint8_t L2:1;
            uint8_t R2:1;//            
            uint8_t select:1;
            uint8_t start:1;
            uint8_t left_stick:1;
            uint8_t right_stick:1;
            uint8_t home:1;
            uint8_t :3;    //filler            
        } buttons;
        struct
        {
            uint8_t hat_switch:4;
            uint8_t :4;//filler
        } hat_switch;        
        struct
        {
            uint8_t X;
            uint8_t Y;
            uint8_t Z;
            uint8_t Rz;            
        } analog_stick;
    } members;
    uint8_t val[7];
} INPUT_CONTROLS;

ボタンの押下に合わせて、これらの変数に値を格納することでUSB Gamepadを動作させることができます。
例えば、"Y"ボタンが押されたら、変数yの値を1にしてUSBでPCに送信します。
これにより、USB Gamepadで"Y"ボタンが押されたんだなということがわかります。

ボタン処理部

ボタン処理部はmy_app_device_gamepad.cに書いています。
ここのコードでは、

  • ボタンの状態の監視
  • start/select長押し時の処理とステートの管理

を行っています。

ボタン状態の監視は例えば下記のような簡単なコードをボタン分用意しているだけです。

gamepad_input->members.buttons.select = BUTTON_IsPressed(BUTTON_SELECT);

start/select長押し時の処理にはPICマイコンの初期化したタイマー0を使っています。
以下はstartの長押し処理のための関数です。

void ChangeSWMode_Button_Start(void){
    uint16_t cnt_timer =0;
    
    if (BUTTON_IsPressed(BUTTON_START)){
        INTCONbits.TMR0IF = 0;          // reset timer0 interrupt flag
        TMR0bits.TMR0 = (uint8_t)5;
        cnt_timer = 0;
        while(BUTTON_IsPressed(BUTTON_START)){
            if(INTCONbits.TMR0IF){          // INTCONbits.TMR0IF happens every 4ms
                cnt_timer++;
                if(cnt_timer >=500){        // 2s
                    flags.sw_flag = ~(flags.sw_flag);
                    cnt_timer =0;
                    while(BUTTON_IsPressed(BUTTON_START));
                }
                INTCONbits.TMR0IF = 0;
                TMR0bits.TMR0 = (uint8_t)5;
            }
        }
    }
        
    return;
}

USB HID report descriptors

USB HID report descriptorsは資料が公式のドキュメント以外あまりなく、仕様もわかりにくく、かなり厄介でした。
内容的にはusb_descriptors.cに書かれている下記部分になります。

const struct{uint8_t report[HID_RPT01_SIZE];}hid_rpt01={{
  0x05,0x01,        //USAGE_PAGE (Generic Desktop)
  0x09,0x05,        //USAGE (Game Pad)
  0xA1,0x01,        //COLLECTION (Application)
  0x15,0x00,        //  LOGICAL_MINIMUM(0)
  0x25,0x01,        //  LOGICAL_MAXIMUM(1)
  0x35,0x00,        //  PHYSICAL_MINIMUM(0)
  0x45,0x01,        //  PHYSICAL_MAXIMUM(1)
  0x75,0x01,        //  REPORT_SIZE(1)
  0x95,0x0D,        //  REPORT_COUNT(13)
  0x05,0x09,        //  USAGE_PAGE(Button)
  0x19,0x01,        //  USAGE_MINIMUM(Button 1)
  0x29,0x0D,        //  USAGE_MAXIMUM(Button 13)
  0x81,0x02,        //  INPUT(Data,Var,Abs)
  0x95,0x03,        //  REPORT_COUNT(3)
  0x81,0x01,        //  INPUT(Cnst,Ary,Abs)
  0x05,0x01,        //  USAGE_PAGE(Generic Desktop)
  0x25,0x07,        //  LOGICAL_MAXIMUM(7)
  0x46,0x3B,0x01,   //  PHYSICAL_MAXIMUM(315)
  0x75,0x04,        //  REPORT_SIZE(4)
  0x95,0x01,        //  REPORT_COUNT(1)
  0x65,0x14,        //  UNIT(Eng Rot:Angular Pos)
  0x09,0x39,        //  USAGE(Hat Switch)
  0x81,0x42,        //  INPUT(Data,Var,Abs,Null)
  0x65,0x00,        //  UNIT(None)
  0x95,0x01,        //  REPORT_COUNT(1)
  0x81,0x01,        //  INPUT(Cnst,Ary,Abs)
  0x26,0xFF,0x00,   //  LOGICAL_MAXIMUM(255)
  0x46,0xFF,0x00,   //  PHYSICAL_MAXIMUM(255)
  0x09,0x30,        //  USAGE(X)
  0x09,0x31,        //  USAGE(Y)
  0x09,0x32,        //  USAGE(Z)
  0x09,0x35,        //  USAGE(Rz)          
  0x75,0x08,        //  REPORT_SIZE(8)
  0x95,0x04,        //  REPORT_COUNT(4)
  0x81,0x02,        //  INPUT(Data,Var,Abs)
  0xC0              //END_COLLECTION
}
};

ここの書き方は需要があれば別の記事で書きたいと思います。

プログラムのライセンスについて

Microchipが提供するMLAのソースコードのほとんどはApache License 2.0で提供されています。
私が追加したソースコードについてもApache License 2.0で提供することにしましたので、個人利用・商用利用かかわらずご自由にお使いください。
ただし、USB プロダクトID(PID)についてはMicrochip社とのサブライセンス契約により提供されているものですので、商用利用不可となります。
ファイルとしてはmy_usb_pid.hが商用利用不可です。

SFCコントローラーを改造してみます

出来上がったプログラムをpickit3で基板に書き込めば、基板は完成です。

では早速、出来上がった基板を使ってSFCコントローラーをUSBゲームパッド化してみましょう。

これを、

こうして、

こうして、

こうじゃ。

あとはふたを戻して出来上がりです。

SFCコントローラー USBゲームパッドver.

早速動かしてみましょう。

動かしてみます

Windowsで動作確認

まずは動作確認です。
PCに挿してみると、コントロールパネルにちゃんと"SFC Gamepad"が認識されました。

"デバイスとプリンター"にSFC Gamepadが表示されました

では、上のアイコンを右クリック→ "ゲームコントローラーの設定"→"プロパティ"と移動し、ゲームパッドの動作確認画面を表示させます。
で、動かすとこんな感じです。

動作確認(Select長押し機能の確認中)

動作確認 (Start長押し機能の確認中)

しっかり動いてくれてますね。
初めて動いたときはかなりうれしかったです。

Raspberry PIで動作確認

Raspberry PIでの動作確認もしておきました。

ラズパイでGamepadを使うには"Joystick"パッケージを使用します。
また、動作確認には"jstest-gtk"が使いやすいです。
まずはterminalから次のコマンドを実行してこれらのパッケージをインストールします。

sudo apt install joystick jstest-gtk

続いて、terminalから次のコマンドを実行してjstest-gtkを立ち上げます。

jstest-gtk

あとは、windowsと同じ感じです。

最終的に動作確認したOSは次の通りです。

  • Raspberry PI 3
    • Raspberry Pi OS
    • Recalbox for RASPBERRY PI 3
    • RetroPie 4.7.1 for RASPBERRY PI 2/3
  • Raspberry PI 4
    • Raspberry Pi OS
    • Recalbox for RASPBERRY PI 4/400
    • RetroPie 4.7.1 for RASPBERRY PI 4/400

その他は確認してませんが、特殊なドライバーは使っておらず、標準ドライバーで動いてるのでおそらく行けるでしょう。

Steamで遊んでみる

USBゲームパッド化したSFCコントローラーで、Steamのゲームを遊んでみました。

まずは子供の頃スーファミでよく遊んでたボンバーマンのバトロワゲーム、"スーパーボンバーマン R オンライン"。

スーパーボンバーマン R オンライン
最新のボンバーマンがSFCコントローラーで遊べるなんて、感動です…

大好きなゲーム"UNDERTALE"を手掛けたToby Fox氏が贈る新作、"DELTARUNE"。

DELTARUNE
最近のゲームなのにSFCコントローラーでプレイすることに全く違和感がない。

今時な3Dパーティーゲーム"Fall Guys"

Fall Guys: Ultimate Knockout
さすがに無理かと思いましたが、全然遊べました!

テイストが1930年代のカートゥーンっぽい"Cuphead"

Cuphead
SFCコントローラーにめちゃくちゃあう。

EPIC Gamesで遊んでみる…?

EPIC Gamesでは対応コントローラーが限られているようで、遊べませんでした…
FORTNITEも遊びたかったなぁ…

<後日談>
x360ceを使用することでできました。
詳しくは↓
geekyfab.com

最後に

今回は、スーパーファミコンのコントローラーをUSBゲームパッド化する基板を作り、実際にスーファミのコントローラーを改造してゲームを遊んでみました。

スーファミのコントローラーってすごく好きなんで、最近のゲームもこれで遊びたいなぁと思ったのが制作のきっかけです。
結果、大満足な仕上がりになりました。

作った基板のガーバーデータとファームウェアのソースコードはGithub上で公開してるので、よろしければぜひ作ってみてください。

github.com

今回はこれで終わります。