├── README.txt ├── Heater.h ├── Thermistor.h ├── PID.h ├── Heater.cpp ├── PID.cpp ├── Thermistor.cpp └── ReflowController.pde /README.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaggo/ReflowController/HEAD/README.txt -------------------------------------------------------------------------------- /Heater.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 2011 Eberhard Rensch, Pleasant Software 3 | * 4 | * Based on parts of the Makerbot firmware 5 | * Copyright 2010 by Adam Mayer 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see 19 | */ 20 | 21 | #ifndef HEATER_H 22 | #define HEATER_H 23 | 24 | #include "Thermistor.h" 25 | #include "PID.h" 26 | 27 | class Heater 28 | { 29 | private: 30 | int current_temperature; 31 | PID pid; 32 | 33 | public: 34 | Heater(); 35 | 36 | void set_target_temperature(int temp); 37 | int get_target_temperature(); 38 | bool hasReachedTargetTemperature(); 39 | 40 | // Call once each temperature interval 41 | void manage_temperature(); 42 | 43 | 44 | // Reset to board-on state 45 | void reset(); 46 | }; 47 | 48 | #endif // HEATER_H 49 | -------------------------------------------------------------------------------- /Thermistor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 2011 Eberhard Rensch, Pleasant Software 3 | * 4 | * Based on parts of the Makerbot firmware 5 | * Copyright 2010 by Adam Mayer 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see 19 | */ 20 | 21 | #ifndef THERMISTOR_HH_ 22 | #define THERMISTOR_HH_ 23 | 24 | #include 25 | 26 | #define THERM_TABLE_SIZE 20 27 | #define SAMPLE_COUNT 4 28 | 29 | struct ThermTableEntry { 30 | int16_t adc; 31 | int16_t celsius; 32 | } __attribute__ ((packed)); 33 | 34 | class Thermistor { 35 | private: 36 | uint16_t current_temp; 37 | uint8_t analog_pin; // index of analog pin 38 | const static int ADC_RANGE = 1024; 39 | int16_t sample_buffer[SAMPLE_COUNT]; 40 | uint8_t next_sample; 41 | const uint8_t table_index; 42 | 43 | public: 44 | Thermistor(uint8_t analog_pin, uint8_t table_index); 45 | 46 | int16_t getTemperature() const { return current_temp; } 47 | 48 | // True if update initiated, false otherwise 49 | bool update(); 50 | }; 51 | 52 | #endif //THERMISTOR_H 53 | -------------------------------------------------------------------------------- /PID.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 2011 Eberhard Rensch, Pleasant Software 3 | * 4 | * Based on parts of the Makerbot firmware 5 | * Copyright 2010 by Adam Mayer 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see 19 | * 20 | * This simplified PID controller was written with reference to: 21 | * * The Heater.h implementation (lookup credits) 22 | * * Brett Beauregard's Arduino PID implementation 23 | * Created on: Feb 19, 2010 24 | * Author: phooky 25 | */ 26 | 27 | #ifndef PID_HH_ 28 | #define PID_HH_ 29 | 30 | #include 31 | 32 | #define DELTA_SAMPLES 4 33 | 34 | /// This simplified PID controller makes several assumptions: 35 | /// * The output range is limited to 0-255. 36 | class PID { 37 | private: 38 | // Data for approximating d (smoothing to handle discrete nature of sampling). 39 | // See PID.cc for a description of why we do this. 40 | int16_t delta_history[DELTA_SAMPLES]; 41 | float delta_summation; 42 | uint8_t delta_idx; 43 | int prev_error; // previous input for calculating next delta 44 | int error_acc; // accumulated error, for calculating integral 45 | 46 | int sp; // set point 47 | 48 | 49 | public: 50 | PID() { reset(); } 51 | 52 | void setTarget(const int target) { sp = target; } 53 | const int getTarget() const { return sp; } 54 | 55 | /// Reset the PID to board-on values 56 | void reset(); 57 | /// PV is the process value; that is, the measured value 58 | /// Returns the new value of the manipulated value; that is, the output 59 | int calculate(int pv); 60 | }; 61 | 62 | #endif /* PID_HH_ */ 63 | -------------------------------------------------------------------------------- /Heater.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 2011 Eberhard Rensch, Pleasant Software 3 | * 4 | * Based on parts of the Makerbot firmware 5 | * Copyright 2010 by Adam Mayer 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see 19 | */ 20 | 21 | #include "Heater.h" 22 | #include "Thermistor.h" 23 | #include "WProgram.h" 24 | 25 | 26 | // Offset to compensate for range clipping and bleed-off 27 | #define HEATER_OFFSET_ADJUSTMENT 0 28 | 29 | extern int heaterPin; 30 | extern Thermistor thermistor; 31 | 32 | Heater::Heater() 33 | { 34 | reset(); 35 | } 36 | 37 | void Heater::reset() { 38 | current_temperature = 0; 39 | } 40 | 41 | void Heater::set_target_temperature(int temp) 42 | { 43 | pid.setTarget(temp); 44 | } 45 | 46 | int Heater::get_target_temperature() 47 | { 48 | return pid.getTarget(); 49 | } 50 | 51 | // We now define target hysteresis in absolute degrees. The original 52 | // implementation (+/-5%) was giving us swings of 10% in either direction 53 | // *before* any artifacts of process instability came in. 54 | #define TARGET_HYSTERESIS 2 55 | 56 | bool Heater::hasReachedTargetTemperature() 57 | { 58 | return (current_temperature >= (pid.getTarget() - TARGET_HYSTERESIS)) && 59 | (current_temperature <= (pid.getTarget() + TARGET_HYSTERESIS)); 60 | } 61 | 62 | /** 63 | * Samples the temperature and converts it to degrees celsius. 64 | * Returns degrees celsius. 65 | */ 66 | 67 | /*! 68 | Manages motor and heater based on measured temperature: 69 | o If temp is too low, don't start the motor 70 | o Adjust the heater power to keep the temperature at the target 71 | */ 72 | void Heater::manage_temperature() 73 | { 74 | thermistor.update(); 75 | 76 | // update the temperature reading. 77 | current_temperature = thermistor.getTemperature(); 78 | 79 | int mv = pid.calculate(current_temperature); 80 | // offset value to compensate for heat bleed-off. 81 | // There are probably more elegant ways to do this, 82 | // but this works pretty well. 83 | mv += HEATER_OFFSET_ADJUSTMENT; 84 | // clamp value 85 | if (mv < 0) { mv = 0; } 86 | if (mv >255) { mv = 255; } 87 | digitalWrite(heaterPin,(mv>0)); 88 | } 89 | 90 | -------------------------------------------------------------------------------- /PID.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 2011 Eberhard Rensch, Pleasant Software 3 | * 4 | * Based on parts of the Makerbot firmware 5 | * Copyright 2010 by Adam Mayer 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see 19 | * 20 | * This simplified PID controller was written with reference to: 21 | * * The Heater.h implementation (lookup credits) 22 | * * Brett Beauregard's Arduino PID implementation 23 | * Created on: Feb 19, 2010 24 | * Author: phooky 25 | * 26 | */ 27 | 28 | #include "PID.h" 29 | 30 | #define ERR_ACC_MAX 256 31 | #define ERR_ACC_MIN -ERR_ACC_MAX 32 | 33 | #define P_GAIN (float)7.0 34 | #define I_GAIN (float)0.325 35 | #define D_GAIN (float)36.0 36 | 37 | // scale the output term to account for our fixed-point bounds 38 | #define OUTPUT_SCALE 2 39 | 40 | void PID::reset() { 41 | error_acc = 0; 42 | prev_error = 0; 43 | delta_idx = 0; 44 | sp = 0; 45 | for (delta_idx = 0; delta_idx < DELTA_SAMPLES; delta_idx++) { 46 | delta_history[delta_idx] = 0; 47 | } 48 | delta_idx = 0; 49 | delta_summation = 0; 50 | } 51 | 52 | // We're modifying the way we compute delta by averaging the deltas over a 53 | // series of samples. This helps us get a reasonable delta despite the discrete 54 | // nature of the samples; on average we will get a delta of maybe 1/deg/second, 55 | // which will give us a delta impulse for that one calculation round and then 56 | // the D term will immediately disappear. By averaging the last N deltas, we 57 | // allow changes to be registered rather than get subsumed in the sampling noise. 58 | int PID::calculate(const int pv) { 59 | int e = sp - pv; 60 | error_acc += e; 61 | // Clamp the error accumulator at accepted values. 62 | // This will help control overcorrection for accumulated error during the run-up 63 | // and allow the I term to be integrated away more quickly as we approach the 64 | // setpoint. 65 | if (error_acc > ERR_ACC_MAX) { 66 | error_acc = ERR_ACC_MAX; 67 | } 68 | if (error_acc < ERR_ACC_MIN) { 69 | error_acc = ERR_ACC_MIN; 70 | } 71 | float p_term = (float)e * P_GAIN; 72 | float i_term = (float)error_acc * I_GAIN; 73 | int delta = e - prev_error; 74 | // Add to delta history 75 | delta_summation -= delta_history[delta_idx]; 76 | delta_history[delta_idx] = delta; 77 | delta_summation += (float)delta; 78 | delta_idx = (delta_idx+1) % DELTA_SAMPLES; 79 | // Use the delta over the whole window 80 | float d_term = delta_summation * D_GAIN; 81 | 82 | prev_error = e; 83 | 84 | return ((int)(p_term + i_term + d_term))*OUTPUT_SCALE; 85 | } 86 | -------------------------------------------------------------------------------- /Thermistor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 2011 Eberhard Rensch, Pleasant Software 3 | * 4 | * Based on parts of the Makerbot firmware 5 | * Copyright 2010 by Adam Mayer 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see 19 | */ 20 | 21 | #include "Thermistor.h" 22 | #include "WProgram.h" 23 | 24 | // Default thermistor table. If no thermistor table is loaded into eeprom, 25 | // this will be copied in by the initTable() method. 26 | // 27 | // Thermistor lookup table for RepRap Temperature Sensor Boards (http://make.rrrf.org/ts) 28 | // Made with createTemperatureLookup.py (http://svn.reprap.org/trunk/reprap/firmware/Arduino/utilities/createTemperatureLookup.py) 29 | // ./createTemperatureLookup.py --r0=100000 --t0=25 --r1=0 --r2=4700 --beta=4066 --max-adc=1023 30 | // r0: 100000 31 | // t0: 25 32 | // r1: 0 33 | // r2: 4700 34 | // beta: 4066 35 | // max adc: 1023 36 | typedef int16_t TempTable[THERM_TABLE_SIZE][2]; 37 | TempTable default_table = { 38 | {1, 841}, 39 | {54, 255}, 40 | {107, 209}, 41 | {160, 184}, 42 | {213, 166}, 43 | {266, 153}, 44 | {319, 142}, 45 | {372, 132}, 46 | {425, 124}, 47 | {478, 116}, 48 | {531, 108}, 49 | {584, 101}, 50 | {637, 93}, 51 | {690, 86}, 52 | {743, 78}, 53 | {796, 70}, 54 | {849, 61}, 55 | {902, 50}, 56 | {955, 34}, 57 | {1008, 3} 58 | }; 59 | 60 | int16_t thermistorToCelsius(int16_t reading, int8_t table_idx) { 61 | int16_t celsius = 0; 62 | int8_t i; 63 | for (i=1; i reading) 66 | { 67 | celsius = default_table[i-1][1] + 68 | (reading - default_table[i-1][0]) * 69 | (default_table[i][1] - default_table[i-1][1]) / 70 | (default_table[i][0] - default_table[i-1][0]); 71 | if (celsius > 255) 72 | celsius = 255; 73 | break; 74 | } 75 | } 76 | // Overflow: We just clamp to 255 degrees celsius to ensure 77 | // that the heater gets shut down if something goes wrong. 78 | if (i == THERM_TABLE_SIZE) { 79 | celsius = 255; 80 | } 81 | return celsius; 82 | } 83 | 84 | Thermistor::Thermistor(uint8_t analog_pin_in, uint8_t table_index_in) : 85 | analog_pin(analog_pin_in), next_sample(0), table_index(table_index_in) { 86 | for (int i = 0; i < SAMPLE_COUNT; i++) { sample_buffer[i] = 0; } 87 | } 88 | 89 | bool Thermistor::update() { 90 | int16_t temp; 91 | temp = analogRead(analog_pin); 92 | 93 | sample_buffer[next_sample] = temp; 94 | next_sample = (next_sample+1) % SAMPLE_COUNT; 95 | 96 | // average 97 | int16_t cumulative = 0; 98 | for (int i = 0; i < SAMPLE_COUNT; i++) { 99 | cumulative += sample_buffer[i]; 100 | } 101 | int16_t avg = cumulative / SAMPLE_COUNT; 102 | 103 | //current_temp = thermistorToCelsius(avg,table_index); 104 | current_temp = thermistorToCelsius(temp,table_index); 105 | return true; 106 | } 107 | -------------------------------------------------------------------------------- /ReflowController.pde: -------------------------------------------------------------------------------- 1 | /* 2 | * Temperature control software for a Reflow Soldering Oven 3 | * This firmware can be used on a ATtiny 45 µController 4 | * For schematics of the hardware, please see 5 | * http://www.pleasantsoftware.com/developer/3d/reflow 6 | * 7 | * (c) 2011 Eberhard Rensch, Pleasant Software 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see 21 | */ 22 | 23 | 24 | #include 25 | #include "Thermistor.h" 26 | #include "Heater.h" 27 | 28 | // Use these Pins on a ATtiny 29 | uint8_t sensorPin = 3; 30 | uint8_t heaterPin = 0; 31 | uint8_t ledPin = 1; 32 | uint8_t buttonPin= 2; 33 | 34 | // These pins might be used for debugging with an Arduino instead 35 | // uint8_t sensorPin = 3; 36 | // uint8_t heaterPin = 2; 37 | // uint8_t ledPin = 3; 38 | // uint8_t buttonPin= 4; 39 | 40 | 41 | Thermistor thermistor(sensorPin, 0); 42 | Heater heater; 43 | Bounce button = Bounce(buttonPin, 10); 44 | 45 | void setup() { 46 | #if !defined(__AVR_ATtiny45__) 47 | Serial.begin(115200); 48 | Serial.println("Init"); 49 | #endif 50 | 51 | pinMode(heaterPin, OUTPUT); 52 | pinMode(ledPin, OUTPUT); 53 | pinMode(buttonPin, INPUT); 54 | digitalWrite(buttonPin, HIGH); 55 | digitalWrite(heaterPin, LOW); 56 | 57 | heater.set_target_temperature(0); 58 | 59 | // Signal ready for duty 60 | // for(uint8_t i=0;i<5;i++) { 61 | // delay(100); 62 | // digitalWrite(ledPin,HIGH); 63 | // delay(150); 64 | // digitalWrite(ledPin,LOW); 65 | // } 66 | 67 | } 68 | 69 | uint8_t phase = 0; 70 | uint8_t program = 0; 71 | 72 | boolean running = false; 73 | boolean programFinished = false; 74 | 75 | uint8_t ledState=0; 76 | uint8_t ledPhase = 0; 77 | 78 | uint32_t blinkTimer=0L; 79 | uint32_t tempManagerTimer=0L; 80 | uint32_t currentTime=0L; 81 | uint32_t nextPhaseChange=0; 82 | boolean buttonHandled=true; 83 | 84 | void loop() 85 | { 86 | boolean buttonChanged = button.update(); 87 | currentTime = millis(); 88 | if(running) 89 | { 90 | if(blinkTimer(phase-1)/2) { 97 | blinkTimer+=500UL; 98 | ledPhase = 0; 99 | } 100 | } 101 | if(tempManagerTimer 1000) { 197 | running = true; 198 | programFinished = false; 199 | switch(program) 200 | { 201 | case 0: phase=0; 202 | break; 203 | case 1: phase=20; 204 | break; 205 | case 2: phase=10; 206 | break; 207 | } 208 | //phase = (program==0)?0:10; // 10 -> Always on! 209 | nextPhaseChange = 0L; 210 | buttonHandled=true; 211 | #if !defined(__AVR_ATtiny45__) 212 | Serial.println("Start Program"); 213 | #endif 214 | } 215 | else if(buttonChanged && programFinished) 216 | { 217 | resetProgram(); 218 | buttonHandled=true; 219 | } 220 | } 221 | if(buttonChanged && button.read()==HIGH) 222 | { 223 | if(!buttonHandled) { 224 | program = (program+1)%3; 225 | for(uint8_t i=0;i<=program;i++) 226 | { 227 | delay(200); 228 | digitalWrite(ledPin,HIGH); 229 | delay(250); 230 | digitalWrite(ledPin,LOW); 231 | } 232 | 233 | #if !defined(__AVR_ATtiny45__) 234 | Serial.println("Change Program"); 235 | #endif 236 | } 237 | buttonHandled=false; 238 | } 239 | } 240 | } 241 | 242 | void resetProgram() 243 | { 244 | programFinished = false; 245 | digitalWrite(ledPin, LOW); 246 | #if !defined(__AVR_ATtiny45__) 247 | Serial.println("Reset Program"); 248 | #endif 249 | } 250 | --------------------------------------------------------------------------------