Changes between Initial Version and Version 1 of HomeReflow


Ignore:
Timestamp:
Nov 24, 2009 3:37:41 PM (14 years ago)
Author:
sgk
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • HomeReflow

    v1 v1  
     1= 自作リフロー =
     2
     3とりあえず、いきなりスケッチを置いておきます。
     4回路図とかはそのうち。
     5
     6== 残り作業 ==
     7 * ブザーが欲しい。開始・終了で鳴らす。
     8 * 冷めてきて動かしても大丈夫になったことを知らせたい。
     9 * オーブンの筐体の隙間にグラスウールまたはロックウールを詰め込んで試してみる。
     10 * それでだめなら、もう少し強力なオーブンを探す。
     11 * PCをコンソールにするアプリが欲しい。
     12
     13== 外部ライブラリ ==
     14 * [http://www.arduino.cc/playground/Code/PIDLibrary PIDライブラリ]
     15
     16== `ReflowOven.pde` ==
     17{{{
     18#!c
     19#include <LiquidCrystal.h>
     20#include "PID_Beta6.h"
     21
     22/*
     23 *
     24 * Config
     25 *
     26 */
     27#define BUTTON 3
     28#define HEATER 9
     29#define SENSOR 10
     30LiquidCrystal lcd(2, 8, 4, 5, 6, 7);
     31
     32
     33/*
     34 *
     35 * Temperature sensor
     36 *
     37 */
     38#include "SPI.h"
     39const static double TEMP_ERROR = 10000;
     40
     41void
     42sensorSetup()
     43{
     44  SPI_Master.begin(SENSOR);
     45}
     46
     47double
     48sensorValue()
     49{
     50  SPI_Master.enable(SENSOR);
     51  int value;
     52  value = SPI_Master.read() << 8;
     53  value |= SPI_Master.read();
     54  SPI_Master.disable();
     55
     56  if ((value & 0x0004) != 0)
     57    return TEMP_ERROR;
     58  return (value >> 3) * 0.25;
     59}
     60
     61
     62/*
     63 *
     64 * Main
     65 *
     66 */
     67
     68
     69/* PID */
     70double temperature, output, target;
     71PID pid(&temperature, &output, &target, 75, 50, 0);
     72
     73/* Profile */
     74const double Rstart = 3.0; // max ramp-up rate to Ts_min
     75const double Rup = 3.0; // max ramp-up rate from Ts_max to Tpeak
     76const double Rdown = 6.0; // max ramp-down rate from Tpeak to Ts_max
     77const double Ts_min = 150.0;
     78const double Ts_max = 190.0;
     79const int ts = 120; // pre-heat duration
     80const double Tpeak = 232.0;
     81const double TL = 220.0;
     82const int tL = 50; // keep above TL for tL
     83const double Tend = 80.0;
     84
     85/* State Machine */
     86int state;
     87unsigned long nextOff, nextCheck, meltCount;
     88double slope, destination;
     89
     90void
     91setup()
     92{
     93  digitalWrite(HEATER, false);
     94  pinMode(HEATER, OUTPUT);
     95  digitalWrite(BUTTON, true); // pull-up
     96  pinMode(BUTTON, INPUT);
     97
     98  Serial.begin(9600);
     99  sensorSetup();
     100  pid.SetOutputLimits(0, 1000); // 1000 milliseconds
     101  pid.SetMode(AUTO);
     102
     103  lcd.begin(16, 2); // cols, rows
     104  lcd.clear();
     105  lcd.print("Reflow Oven");
     106  delay(2000);
     107 
     108  state = nextOff = nextCheck = 0;
     109}
     110
     111/*
     112 * 0: waiting for button press.
     113 * 1: ramp-up to 150, slope rate between 1.0/sec and 3.0/sec.
     114 * 2: preheat to 190, for 60 and 120 seconds.
     115 * 3: heat to 232 and keep 232, over 220 for 30 to 60 seconds.
     116 * 5: cool down to under 50
     117 * 6: fail
     118 */
     119void
     120loop()
     121{
     122  unsigned long now;
     123  now = millis();
     124 
     125  /* state 0: wait for button presss. */
     126  if (digitalRead(BUTTON) == LOW) {
     127    while (digitalRead(BUTTON) == LOW)
     128      delay(100);
     129    delay(100);
     130    if (state == 0)
     131      state = 9;
     132    else
     133      state = 0;
     134    nextOff = nextCheck = now;
     135  }
     136
     137  /* Heater */
     138  if (now < nextCheck) {
     139    /* PWM on 1Hz */
     140    if (now < nextOff)
     141      digitalWrite(HEATER, true);
     142    else
     143      digitalWrite(HEATER, false);
     144  } else {
     145    /*
     146     * Check
     147     */
     148    nextCheck += 1000; // 1 second
     149    temperature = sensorValue();
     150 
     151    if (temperature == TEMP_ERROR)
     152      state = 6;
     153   
     154    /* Check if the state changes. */
     155    switch (state) {
     156    case 9:
     157      state = 1;
     158      pid.Reset();
     159      target = temperature;
     160      slope = Rstart;
     161      destination = Ts_min;
     162      break;
     163    case 1:
     164      if (temperature >= Ts_min) {
     165        state = 2;
     166        slope = (Ts_max - Ts_min) / ts;
     167        destination = Ts_max;
     168      }
     169      break;
     170    case 2:
     171      if (temperature >= Ts_max) {
     172        state = 3;
     173        slope = Rup;
     174        destination = Tpeak;
     175        meltCount = 0;
     176      }
     177      break;
     178    case 3:
     179      if (temperature >= TL) {
     180        state = 4;
     181        meltCount = 0;
     182      }
     183      break;
     184    case 4:
     185      if (++meltCount > tL) {
     186        state = 5;
     187        slope = Rdown;
     188        destination = -273.0;
     189      }
     190      break;
     191    case 5:
     192      if (temperature <= Tend)
     193        state = 0;
     194      break;
     195    }
     196 
     197    /* Next target */
     198    switch (state) {
     199    case 1:
     200    case 2:
     201    case 3:
     202      target += slope;
     203      if (target > destination)
     204        target = destination;
     205      break;
     206    case 5:
     207      target -= slope;
     208      if (target < destination)
     209        target = destination;
     210      break;
     211    }
     212   
     213    /* Heater control value */
     214    switch (state) {
     215    case 1:
     216    case 2:
     217    case 3:
     218    case 4:
     219    case 5:
     220      pid.Compute();
     221      nextOff = now + output;
     222      break;
     223     
     224    case 0:
     225    case 6:
     226    default:
     227      nextOff = 0;
     228    }
     229
     230    /* LCD display */
     231    lcd.clear();
     232    switch (state) {
     233    case 0:
     234      lcd.print("Press to start");
     235      break;
     236    case 1:
     237      lcd.print("Ramp up");
     238      break;
     239    case 2:
     240      lcd.print("Pre-heat");
     241      break;
     242    case 3:
     243      lcd.print("Heat up");
     244      break;
     245    case 4:
     246      lcd.print("Melted");
     247      break;
     248    case 5:
     249      lcd.print("Cool down");
     250      break;
     251    case 6:
     252      lcd.print("Fail");
     253      break;
     254    }
     255    lcd.setCursor(0, 1);
     256    lcd.print(output);
     257    lcd.print(' ');
     258    if (temperature != TEMP_ERROR)
     259      lcd.print(temperature);
     260
     261    SerialReceive();
     262    SerialSend();
     263  }
     264}
     265
     266
     267
     268/********************************************
     269 * Serial Communication functions / helpers
     270 ********************************************/
     271union {                // This Data structure lets
     272  byte asBytes[24];    // us take the byte array
     273  float asFloat[6];    // sent from processing and
     274}                      // easily convert it to a
     275foo;                   // float array
     276
     277// getting float values from processing into the arduino
     278// was no small task.  the way this program does it is
     279// as follows:
     280//  * a float takes up 4 bytes.  in processing, convert
     281//    the array of floats we want to send, into an array
     282//    of bytes.
     283//  * send the bytes to the arduino
     284//  * use a data structure known as a union to convert
     285//    the array of bytes back into an array of floats
     286
     287//  the bytes coming from the arduino follow the following
     288//  format:
     289//  0: 0=Manual, 1=Auto, else = ? error ?
     290//  1-4: float setpoint
     291//  5-8: float input
     292//  9-12: float output 
     293//  13-16: float P_Param
     294//  17-20: float I_Param
     295//  21-24: float D_Param
     296void SerialReceive()
     297{
     298  // read the bytes sent from Processing
     299  int index = 0;
     300  byte Auto_Man = -1;
     301  while (Serial.available() && index < 25) {
     302    if (index == 0)
     303      Auto_Man = Serial.read();
     304    else
     305      foo.asBytes[index-1] = Serial.read();
     306    index++;
     307  }
     308
     309  // if the information we got was in the correct format,
     310  // read it into the system
     311  if (index == 25 && (Auto_Man == 0 || Auto_Man == 1)) {
     312    target = double(foo.asFloat[0]);
     313    if (Auto_Man == 0)                       // * only change the output if we are in
     314    {                                     //   manual mode.  otherwise we'll get an
     315      output = double(foo.asFloat[2]);      //   output blip, then the controller will
     316    }                                     //   overwrite.
     317
     318    double p, i, d;                       // * read in and set the controller tunings
     319    p = double(foo.asFloat[3]);           //
     320    i = double(foo.asFloat[4]);           //
     321    d = double(foo.asFloat[5]);           //
     322    pid.SetTunings(p, i, d);            //
     323
     324    if(Auto_Man==0)
     325      pid.SetMode(MANUAL);// * set the controller mode
     326    else
     327      pid.SetMode(AUTO);             //
     328  }
     329  Serial.flush();                         // * clear any random data from the serial buffer
     330}
     331
     332// unlike our tiny microprocessor, the processing ap
     333// has no problem converting strings into floats, so
     334// we can just send strings.  much easier than getting
     335// floats from processing to here no?
     336void SerialSend()
     337{
     338  Serial.print("PID ");
     339  Serial.print(target);   
     340  Serial.print(" ");
     341  Serial.print(temperature);   
     342  Serial.print(" ");
     343  Serial.print(output);
     344  Serial.print(" ");
     345  Serial.print(pid.GetP_Param());   
     346  Serial.print(" ");
     347  Serial.print(pid.GetI_Param());   
     348  Serial.print(" ");
     349  Serial.print(pid.GetD_Param());   
     350  Serial.print(" ");
     351  if (pid.GetMode() == AUTO)
     352    Serial.println("Automatic");
     353  else
     354    Serial.println("Manual"); 
     355}
     356}}}
     357
     358== `SPI.h` ==
     359{{{
     360#!c
     361#ifndef __SPI_H__
     362#define __SPI_H__
     363
     364#include "WProgram.h"
     365
     366class SPI_Master_Class {
     367public:
     368  static void begin(int slaveselecter);
     369  void enable(int slaveselecter);
     370  void disable();
     371  byte write_and_read(byte data) const;
     372  void write(byte data) const;
     373  byte read() const;
     374
     375private:
     376  static boolean initialized_;
     377  static const int SS = 10;
     378  static const int MOSI = 11;
     379  static const int MISO = 12;
     380  static const int SCK = 13;
     381  static int enabled_;
     382};
     383
     384extern SPI_Master_Class SPI_Master;
     385
     386#endif //__SPI_H__
     387}}}
     388
     389== `SPI.cpp` ==
     390{{{
     391#!c
     392#include "SPI.h"
     393
     394boolean SPI_Master_Class::initialized_ = false;
     395int SPI_Master_Class::enabled_ = -1;
     396
     397void
     398SPI_Master_Class::begin(int slaveselecter) {
     399  if (!initialized_) {
     400    initialized_ = true;
     401    enabled_ = -1;
     402    pinMode(SS, OUTPUT);  // Must be set as OUTPUT before SPE is asserted.
     403    pinMode(MOSI, OUTPUT);
     404    pinMode(MISO, INPUT);
     405    digitalWrite(MISO, HIGH);  // Pull-up
     406    pinMode(SCK, OUTPUT);
     407    SPCR = (1<<SPE)|(1<<MSTR);  // SPE: SPI Enable; MSTR: Master
     408    byte garbage;
     409    garbage = SPSR;
     410    garbage = SPDR;
     411  }
     412
     413  if (slaveselecter != SS)
     414    pinMode(slaveselecter, OUTPUT);
     415  digitalWrite(slaveselecter, HIGH);  // Disable
     416}
     417
     418void
     419SPI_Master_Class::enable(int slaveselecter) {
     420  disable();
     421  digitalWrite(slaveselecter, LOW);
     422  enabled_ = slaveselecter;
     423}
     424
     425void
     426SPI_Master_Class::disable() {
     427  if (enabled_ >= 0) {
     428    digitalWrite(enabled_, HIGH);
     429    enabled_ = -1;
     430  }
     431}
     432
     433byte
     434SPI_Master_Class::write_and_read(byte data) const {
     435  SPDR = data;
     436  while (!(SPSR & (1<<SPIF)))
     437    ;
     438  return SPDR;
     439}
     440
     441void
     442SPI_Master_Class::write(byte data) const {
     443  write_and_read(data);
     444}
     445
     446byte
     447SPI_Master_Class::read() const {
     448  return write_and_read(0x00);
     449}
     450
     451SPI_Master_Class SPI_Master;
     452}}}
     453