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の一部のピンは半田ブリッジしてしまってましたが、これはどちらかというと半田ペーストを塗るときの技量の問題です。

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

いいもの作れたなぁ。

最後に

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

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

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

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