Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <LiquidCrystal.h>
- #include "TimerOne.h"
- #include "Button.h"
- #include "PID_v1.h"
- #include "PID_AutoTune_v0.h"
- #include "EEPROMex.h"
- #include <avr/wdt.h>
- #define OFF 0
- #define ON 1
- // Pin definitions
- #define HEATER_PIN 5
- #define POT_PIN A1
- #define TEMP_PIN A0
- #define BUTTON_PIN 2
- #define AUTOTUNE_PIN 3
- #define LCD_RS 13
- #define LCD_EN 12
- #define LCD_D4 11
- #define LCD_D5 10
- #define LCD_D6 9
- #define LCD_D7 8
- // LCD Update Interval
- #define UPDATE_INTERVAL 100 // Time im ms between LCD updates
- /*****************************************************************************************
- * Temperature equation definition
- *
- * Y = a*X + b, where Y is the temperature and X is the analog value read from the sensor.
- * You'll need two temperatures and it's analog values to calculate a and b.
- *
- * To find a, subtract your higher value temperature from your lower value temperature and
- * divide by the subtraction of their correspondent analog values.
- *
- * For example: (30C, 228) and (450C, 590) where 30C is 30 degree celsius and 228 is the
- * analog value.
- *
- * a = (450-30)/(590-228) = 1.16
- *
- * To find b, use Y = a*X + b where Y is one of the values you found and X is it's analog
- * value. The value of a is the one found just before.
- *
- * For example:
- *
- * 30 = 1.16*228 + b -> b = 30 - 1.16*228 = -234.48
- *
- * To use Fahrenheit instead of Celsius, the you should do the same as written above but
- * measure the values is Fahrenheit instead of Celsius, like (30F, 228) and (450F, 590).
- *
- * You'll need to change the "C" prints to "F" too.
- *****************************************************************************************/
- #define EQUATION_A 0.441162791
- #define EQUATION_B 10.153255677
- // Temperature control definitions
- #define MIN_TEMP 100 // Minimum setpoint temperature
- #define MAX_TEMP 500 // Maximum setpoint temperature
- #define PWM_MAX 1023 // PWM limit, max 1023 (my power supply was shutting down at 1023)
- // PID VALUES
- #define KP_VAL 26.67
- #define KI_VAL 3.56
- #define KD_VAL 50.01
- // PID Autotune Variables
- #define AUTOTUNE_SETPOINT 150 // Temperature around PID autotune will tune
- #define AUTOTUNE_START_VALUE (PWM_MAX/2) // Do not change
- #define AUTOTUNE_STEP_VALUE AUTOTUNE_START_VALUE // Do not change
- #define AUTOTUNE_NOISEBAND 3
- #define AUTOTUNE_LOOKBACK 10
- // Auto turn-off time (in milliseconds)
- #define TURN_OFF_TIME 2700000
- // Global variables
- uint32_t update_last = 0;
- uint8_t heater_mode = OFF;
- uint32_t on_time = 0;
- // PID Variables
- double temperature;
- double setpoint = 0.0;
- double duty;
- double kp;
- double ki;
- double kd;
- uint16_t autotune_address;
- uint16_t kp_address;
- uint16_t ki_address;
- uint16_t kd_address;
- PID heaterPid(&temperature, &duty, &setpoint, 2, 5, 1, DIRECT);
- PID_ATune aTune(&temperature, &duty);
- LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
- Button setButton = Button(BUTTON_PIN, LOW);
- // Read the Iron temperature
- uint16_t readTemp();
- // Read Setpoint value from the potentiometer
- uint16_t readPot();
- // Check if heater start button was pressed
- void checkButton();
- // Display PID Constants
- void displayPid();
- // Create degree character
- void charSetup();
- void setup() {
- // Autotune
- uint8_t autotune = OFF;
- uint8_t autotune_pid = 0;
- // Enable Watchdog Timer, 1 second
- wdt_enable(WDTO_1S);
- // Define the EEPROM address for Kp, Ki and Kd
- autotune_address = EEPROM.getAddress(sizeof(uint8_t));
- kp_address = EEPROM.getAddress(sizeof(double));
- ki_address = EEPROM.getAddress(sizeof(double));
- kd_address = EEPROM.getAddress(sizeof(double));
- // Set up pins
- pinMode(HEATER_PIN, OUTPUT);
- pinMode(AUTOTUNE_PIN, INPUT);
- digitalWrite(BUTTON_PIN, HIGH);
- digitalWrite(AUTOTUNE_PIN, HIGH);
- // Set up Heater PWM
- Timer1.pwm(HEATER_PIN, 0, 30);
- // Set up PID
- heaterPid.SetOutputLimits(0, PWM_MAX);
- heaterPid.SetSampleTime(50); // Update PID every 50ms
- heaterPid.SetMode(AUTOMATIC);
- // Set up LCD
- lcd.begin(16, 2);
- charSetup();
- // Check if PID autotune button is pressed at startup
- if(digitalRead(AUTOTUNE_PIN) == 0) {
- autotune = ON;
- EEPROM.writeByte(autotune_address, 1);
- }
- // Start PID autotune procedure if button was pressed
- if(autotune == ON) {
- // Configure PID autotune
- setpoint = AUTOTUNE_SETPOINT;
- duty = AUTOTUNE_START_VALUE;
- aTune.SetNoiseBand(AUTOTUNE_NOISEBAND);
- aTune.SetOutputStep(AUTOTUNE_STEP_VALUE);
- aTune.SetLookbackSec(AUTOTUNE_LOOKBACK);
- aTune.SetControlType(1);
- // Print some autotune info
- lcd.clear();
- lcd.print("Autotuning at:");
- lcd.setCursor(0, 1);
- lcd.print((uint16_t)setpoint);
- lcd.print("C");
- // Heat to the setpoint, temperature will rise above it
- Timer1.setPwmDuty(HEATER_PIN, duty);
- while(readTemp() < setpoint) {
- // Reset the watchdog timer to prevent rebooting
- wdt_reset();
- }
- // Wait for temperature to drop to setpoint
- Timer1.setPwmDuty(HEATER_PIN, 0);
- while(readTemp() > setpoint) {
- // Reset the watchdog timer to prevent rebooting
- wdt_reset();
- }
- // Start the autotune
- while(autotune == ON) {
- // Get the current temperature
- temperature = readTemp();
- // Check if the autotune is finished
- if(aTune.Runtime() != 0) {
- autotune = OFF;
- }
- else {
- Timer1.setPwmDuty(HEATER_PIN, duty);
- }
- // If finished, set up the PID and EEPROM values
- if(autotune == OFF) {
- //char string[17];
- // Turn off the heater
- Timer1.setPwmDuty(HEATER_PIN, 0);
- // Get the values from autotune
- kp = aTune.GetKp();
- ki = aTune.GetKi();
- kd = aTune.GetKd();
- // Write them to the EEPROM
- EEPROM.writeDouble(kp_address, kp);
- EEPROM.writeDouble(ki_address, ki);
- EEPROM.writeDouble(kd_address, kd);
- // Reset the setpoint
- setpoint = 0.0;
- }
- // Reset the watchdog timer to prevent rebooting
- wdt_reset();
- }
- }
- // Get the PID constant values from EEPROM if autotune was run
- autotune_pid = EEPROM.readByte(autotune_address);
- if(autotune_pid == 1) {
- kp = EEPROM.readDouble(kp_address);
- ki = EEPROM.readDouble(ki_address);
- kd = EEPROM.readDouble(kd_address);
- } else {
- kp = KP_VAL;
- ki = KI_VAL;
- kd = KD_VAL;
- }
- // Set the to the PID
- heaterPid.SetTunings(kp,ki,kd);
- // Clear the display
- lcd.clear();
- }
- void loop() {
- // Get the current time
- uint32_t update_time = millis();
- // Reset the watchdog timer to prevent rebooting
- wdt_reset();
- // Check the Heater button
- checkButton();
- // Automatic turn-off
- if(heater_mode == ON) {
- if( (millis() - on_time) >= TURN_OFF_TIME) {
- heater_mode = OFF;
- }
- }
- // Read Iron temperature and Setpoint
- temperature = readTemp();
- setpoint = readPot();
- // Calculate PID value
- heaterPid.Compute();
- // Adjust PWM Duty based on the PID
- if(heater_mode == ON) {
- Timer1.setPwmDuty(HEATER_PIN, duty);
- } else {
- Timer1.setPwmDuty(HEATER_PIN, 0);
- }
- //Check if it's time to update the LCD
- if(update_time - update_last > UPDATE_INTERVAL) {
- //char string[17];
- // Check if autotune button is pressed, if yes display the PID constant values
- if(digitalRead(AUTOTUNE_PIN) == 1) {
- lcd.setCursor(0, 0);
- lcd.print("T: ");
- if(temperature < 100) {
- lcd.print(' ');
- }
- lcd.print(temperature, 0);
- lcd.write(byte(0));
- lcd.print("C / ");
- lcd.print(setpoint, 0);
- lcd.write(byte(0));
- lcd.print('C');
- lcd.setCursor(0, 1);
- if(heater_mode == ON) {
- lcd.print("Heater ON ");
- } else {
- lcd.print("Heater OFF");
- }
- //Uncomment to show analog values (used to calibrate)
- //lcd.setCursor(13, 1);
- //uint16_t temp_analog = 0;
- //temp_analog += analogRead(TEMP_PIN);
- //temp_analog += analogRead(TEMP_PIN);
- //temp_analog += analogRead(TEMP_PIN);
- //lcd.print(temp_analog/3);
- } else {
- displayPid();
- }
- // Update the time variable
- update_last = update_time;
- }
- }
- // Read Iron temperature
- uint16_t readTemp() {
- uint16_t temp = 0;
- // Discard first reading
- analogRead(TEMP_PIN);
- // Read temperature four times
- temp += analogRead(TEMP_PIN);
- temp += analogRead(TEMP_PIN);
- temp += analogRead(TEMP_PIN);
- temp += analogRead(TEMP_PIN);
- // Four readings average
- temp = (temp >> 2);
- // Get the value in degrees celsius (or fahrenheit)
- temp = (EQUATION_A*temp) + EQUATION_B;
- return temp;
- }
- // Read Setpoint value from the potentiometer
- uint16_t readPot() {
- uint16_t pot = 0;
- // Discard first reading
- analogRead(POT_PIN);
- // Read the potentiometer four times
- pot += analogRead(POT_PIN);
- pot += analogRead(POT_PIN);
- pot += analogRead(POT_PIN);
- pot += analogRead(POT_PIN);
- // Map the value read to the temperature range
- pot = map(pot, 0, 4092, 0, (MAX_TEMP-MIN_TEMP)) + MIN_TEMP;
- return pot;
- }
- // Check if heater start button was pressed
- void checkButton() {
- setButton.listen();
- if(setButton.onPress()) {
- if(heater_mode == ON) {
- heater_mode = OFF;
- } else {
- heater_mode = ON;
- on_time = millis();
- }
- }
- }
- // Display PID Constants
- void displayPid() {
- lcd.clear();
- lcd.print("P ");
- lcd.print(kp, 2);
- lcd.print(" I ");
- lcd.print(ki, 2);
- lcd.setCursor(0, 1);
- lcd.print("D ");
- lcd.print(kd, 2);
- }
- // Create degree character
- void charSetup() {
- byte degree[8] = {
- B00010,
- B00101,
- B00010,
- B00000,
- B00000,
- B00000,
- B00000,
- };
- lcd.createChar(0, degree) ;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement