Advertisement
simas1017

Soldering iron

Oct 12th, 2016
552
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.52 KB | None | 0 0
  1. #include <LiquidCrystal.h>
  2. #include "TimerOne.h"
  3. #include "Button.h"
  4. #include "PID_v1.h"
  5. #include "PID_AutoTune_v0.h"
  6. #include "EEPROMex.h"
  7. #include <avr/wdt.h>
  8.  
  9. #define OFF             0
  10. #define ON              1
  11.  
  12. // Pin definitions
  13. #define HEATER_PIN      5
  14. #define POT_PIN         A1
  15. #define TEMP_PIN        A0
  16. #define BUTTON_PIN      2
  17. #define AUTOTUNE_PIN    3
  18.  
  19. #define LCD_RS          13
  20. #define LCD_EN          12
  21. #define LCD_D4          11
  22. #define LCD_D5          10
  23. #define LCD_D6          9
  24. #define LCD_D7          8
  25.  
  26. // LCD Update Interval
  27. #define UPDATE_INTERVAL 100 // Time im ms between LCD updates
  28.  
  29. /*****************************************************************************************
  30. * Temperature equation definition
  31. *
  32. * Y = a*X + b, where Y is the temperature and X is the analog value read from the sensor.
  33. * You'll need two temperatures and it's analog values to calculate a and b.
  34. *
  35. * To find a, subtract your higher value temperature from your lower value temperature and
  36. * divide by the subtraction of their correspondent analog values.
  37. *
  38. * For example: (30C, 228) and (450C, 590) where 30C is 30 degree celsius and 228 is the
  39. * analog value.
  40. *
  41. * a = (450-30)/(590-228) = 1.16
  42. *
  43. * To find b, use Y = a*X + b where Y is one of the values you found and X is it's analog
  44. * value. The value of a is the one found just before.
  45. *
  46. * For example:
  47. *
  48. * 30 = 1.16*228 + b -> b = 30 - 1.16*228 = -234.48
  49. *
  50. * To use Fahrenheit instead of Celsius, the you should do the same as written above but
  51. * measure the values is Fahrenheit instead of Celsius, like (30F, 228) and (450F, 590).
  52. *
  53. * You'll need to change the "C" prints to "F" too.
  54. *****************************************************************************************/
  55. #define EQUATION_A  0.441162791
  56. #define EQUATION_B  10.153255677
  57.  
  58. // Temperature control definitions
  59. #define MIN_TEMP    100 // Minimum setpoint temperature
  60. #define MAX_TEMP    500 // Maximum setpoint temperature
  61. #define PWM_MAX     1023    // PWM limit, max 1023 (my power supply was shutting down at 1023)
  62.  
  63. // PID VALUES
  64. #define KP_VAL      26.67
  65. #define KI_VAL      3.56
  66. #define KD_VAL      50.01
  67.  
  68. // PID Autotune Variables
  69. #define AUTOTUNE_SETPOINT       150 // Temperature around PID autotune will tune
  70. #define AUTOTUNE_START_VALUE    (PWM_MAX/2) // Do not change
  71. #define AUTOTUNE_STEP_VALUE     AUTOTUNE_START_VALUE // Do not change
  72. #define AUTOTUNE_NOISEBAND      3
  73. #define AUTOTUNE_LOOKBACK       10
  74.  
  75. // Auto turn-off time (in milliseconds)
  76. #define TURN_OFF_TIME 2700000
  77.  
  78. // Global variables
  79. uint32_t update_last = 0;
  80. uint8_t heater_mode = OFF;
  81. uint32_t on_time = 0;
  82.  
  83. // PID Variables
  84. double temperature;
  85. double setpoint = 0.0;
  86. double duty;
  87. double kp;
  88. double ki;
  89. double kd;
  90. uint16_t autotune_address;
  91. uint16_t kp_address;
  92. uint16_t ki_address;
  93. uint16_t kd_address;
  94.  
  95. PID heaterPid(&temperature, &duty, &setpoint, 2, 5, 1, DIRECT);
  96. PID_ATune aTune(&temperature, &duty);
  97. LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
  98. Button setButton = Button(BUTTON_PIN, LOW);
  99.  
  100. // Read the Iron temperature
  101. uint16_t readTemp();
  102.  
  103. // Read Setpoint value from the potentiometer
  104. uint16_t readPot();
  105.  
  106. // Check if heater start button was pressed
  107. void checkButton();
  108.  
  109. // Display PID Constants
  110. void displayPid();
  111.  
  112. // Create degree character
  113. void charSetup();
  114.  
  115. void setup() {
  116.     // Autotune
  117.     uint8_t autotune = OFF;
  118.     uint8_t autotune_pid = 0;
  119.    
  120.     // Enable Watchdog Timer, 1 second
  121.     wdt_enable(WDTO_1S);
  122.  
  123.     // Define the EEPROM address for Kp, Ki and Kd
  124.     autotune_address = EEPROM.getAddress(sizeof(uint8_t));
  125.     kp_address = EEPROM.getAddress(sizeof(double));
  126.     ki_address = EEPROM.getAddress(sizeof(double));
  127.     kd_address = EEPROM.getAddress(sizeof(double));
  128.    
  129.     // Set up pins
  130.     pinMode(HEATER_PIN, OUTPUT);
  131.     pinMode(AUTOTUNE_PIN, INPUT);
  132.     digitalWrite(BUTTON_PIN, HIGH);
  133.     digitalWrite(AUTOTUNE_PIN, HIGH);
  134.    
  135.     // Set up Heater PWM
  136.     Timer1.pwm(HEATER_PIN, 0, 30);
  137.    
  138.     // Set up PID
  139.     heaterPid.SetOutputLimits(0, PWM_MAX);
  140.     heaterPid.SetSampleTime(50);            // Update PID every 50ms
  141.     heaterPid.SetMode(AUTOMATIC);
  142.    
  143.     // Set up LCD
  144.     lcd.begin(16, 2);
  145.     charSetup();
  146.    
  147.     // Check if PID autotune button is pressed at startup
  148.     if(digitalRead(AUTOTUNE_PIN) == 0) {
  149.         autotune = ON;
  150.         EEPROM.writeByte(autotune_address, 1);
  151.     }
  152.    
  153.     // Start PID autotune procedure if button was pressed
  154.     if(autotune == ON) {
  155.         // Configure PID autotune
  156.         setpoint = AUTOTUNE_SETPOINT;
  157.         duty = AUTOTUNE_START_VALUE;
  158.         aTune.SetNoiseBand(AUTOTUNE_NOISEBAND);
  159.         aTune.SetOutputStep(AUTOTUNE_STEP_VALUE);
  160.         aTune.SetLookbackSec(AUTOTUNE_LOOKBACK);
  161.         aTune.SetControlType(1);
  162.        
  163.         // Print some autotune info
  164.         lcd.clear();
  165.         lcd.print("Autotuning at:");
  166.         lcd.setCursor(0, 1);
  167.         lcd.print((uint16_t)setpoint);
  168.         lcd.print("C");
  169.        
  170.         // Heat to the setpoint, temperature will rise above it
  171.         Timer1.setPwmDuty(HEATER_PIN, duty);
  172.         while(readTemp() < setpoint) {
  173.             // Reset the watchdog timer to prevent rebooting
  174.             wdt_reset();
  175.         }
  176.        
  177.         // Wait for temperature to drop to setpoint
  178.         Timer1.setPwmDuty(HEATER_PIN, 0);
  179.         while(readTemp() > setpoint) {
  180.             // Reset the watchdog timer to prevent rebooting
  181.             wdt_reset();
  182.         }
  183.        
  184.         // Start the autotune
  185.         while(autotune == ON) {
  186.             // Get the current temperature
  187.             temperature = readTemp();
  188.            
  189.             // Check if the autotune is finished
  190.             if(aTune.Runtime() != 0) {
  191.                 autotune = OFF;
  192.             }
  193.             else {
  194.                 Timer1.setPwmDuty(HEATER_PIN, duty);
  195.             }
  196.            
  197.             // If finished, set up the PID and EEPROM values
  198.             if(autotune == OFF) {
  199.                 //char string[17];
  200.                
  201.                 // Turn off the heater
  202.                 Timer1.setPwmDuty(HEATER_PIN, 0);
  203.                
  204.                 // Get the values from autotune
  205.                 kp = aTune.GetKp();
  206.                 ki = aTune.GetKi();
  207.                 kd = aTune.GetKd();
  208.                
  209.                 // Write them to the EEPROM
  210.                 EEPROM.writeDouble(kp_address, kp);
  211.                 EEPROM.writeDouble(ki_address, ki);
  212.                 EEPROM.writeDouble(kd_address, kd);
  213.                
  214.                 // Reset the setpoint
  215.                 setpoint = 0.0;
  216.             }
  217.            
  218.             // Reset the watchdog timer to prevent rebooting
  219.             wdt_reset();
  220.         }
  221.     }
  222.    
  223.     // Get the PID constant values from EEPROM if autotune was run
  224.     autotune_pid = EEPROM.readByte(autotune_address);
  225.    
  226.     if(autotune_pid == 1) {
  227.         kp = EEPROM.readDouble(kp_address);
  228.         ki = EEPROM.readDouble(ki_address);
  229.         kd = EEPROM.readDouble(kd_address);
  230.     } else {
  231.         kp = KP_VAL;
  232.         ki = KI_VAL;
  233.         kd = KD_VAL;
  234.     }
  235.    
  236.     // Set the to the PID
  237.     heaterPid.SetTunings(kp,ki,kd);
  238.    
  239.     // Clear the display
  240.     lcd.clear();
  241. }
  242.  
  243. void loop() {
  244.     // Get the current time
  245.     uint32_t update_time = millis();
  246.    
  247.     // Reset the watchdog timer to prevent rebooting
  248.     wdt_reset();
  249.    
  250.     // Check the Heater button
  251.     checkButton();
  252.    
  253.     // Automatic turn-off
  254.     if(heater_mode == ON) {
  255.         if( (millis() - on_time) >= TURN_OFF_TIME) {
  256.             heater_mode = OFF;
  257.         }
  258.     }
  259.    
  260.     // Read Iron temperature and Setpoint
  261.     temperature = readTemp();
  262.     setpoint = readPot();
  263.    
  264.     // Calculate PID value
  265.     heaterPid.Compute();
  266.    
  267.     // Adjust PWM Duty based on the PID
  268.     if(heater_mode == ON) {
  269.         Timer1.setPwmDuty(HEATER_PIN, duty);
  270.     } else {
  271.         Timer1.setPwmDuty(HEATER_PIN, 0);
  272.     }
  273.  
  274.     //Check if it's time to update the LCD
  275.     if(update_time - update_last > UPDATE_INTERVAL) {
  276.         //char string[17];
  277.    
  278.         // Check if autotune button is pressed, if yes display the PID constant values
  279.         if(digitalRead(AUTOTUNE_PIN) == 1) {
  280.             lcd.setCursor(0, 0);
  281.             lcd.print("T: ");
  282.             if(temperature < 100) {
  283.                 lcd.print(' ');
  284.             }
  285.             lcd.print(temperature, 0);
  286.             lcd.write(byte(0));
  287.             lcd.print("C / ");
  288.             lcd.print(setpoint, 0);
  289.             lcd.write(byte(0));
  290.             lcd.print('C');
  291.            
  292.             lcd.setCursor(0, 1);
  293.             if(heater_mode == ON) {
  294.                 lcd.print("Heater ON ");
  295.             } else {
  296.                 lcd.print("Heater OFF");
  297.             }
  298.            
  299.             //Uncomment to show analog values (used to calibrate)
  300.             //lcd.setCursor(13, 1);
  301.             //uint16_t temp_analog = 0;
  302.             //temp_analog += analogRead(TEMP_PIN);
  303.             //temp_analog += analogRead(TEMP_PIN);
  304.             //temp_analog += analogRead(TEMP_PIN);
  305.             //lcd.print(temp_analog/3);
  306.         } else {
  307.             displayPid();
  308.         }
  309.        
  310.         // Update the time variable
  311.         update_last = update_time;
  312.     }
  313. }
  314.  
  315. // Read Iron temperature
  316. uint16_t readTemp() {
  317.     uint16_t temp = 0;
  318.    
  319.     // Discard first reading
  320.     analogRead(TEMP_PIN);
  321.    
  322.     // Read temperature four times
  323.     temp += analogRead(TEMP_PIN);
  324.     temp += analogRead(TEMP_PIN);
  325.     temp += analogRead(TEMP_PIN);
  326.     temp += analogRead(TEMP_PIN);
  327.    
  328.     // Four readings average
  329.     temp = (temp >> 2);
  330.    
  331.     // Get the value in degrees celsius (or fahrenheit)
  332.     temp = (EQUATION_A*temp) + EQUATION_B;
  333.    
  334.     return temp;
  335. }
  336.  
  337. // Read Setpoint value from the potentiometer
  338. uint16_t readPot() {
  339.     uint16_t pot = 0;
  340.    
  341.     // Discard first reading
  342.     analogRead(POT_PIN);
  343.    
  344.     // Read the potentiometer four times
  345.     pot += analogRead(POT_PIN);
  346.     pot += analogRead(POT_PIN);
  347.     pot += analogRead(POT_PIN);
  348.     pot += analogRead(POT_PIN);
  349.    
  350.     // Map the value read to the temperature range
  351.     pot = map(pot, 0, 4092, 0, (MAX_TEMP-MIN_TEMP)) + MIN_TEMP;
  352.    
  353.     return pot;
  354. }
  355.  
  356. // Check if heater start button was pressed
  357. void checkButton() {
  358.     setButton.listen();
  359.  
  360.     if(setButton.onPress()) {
  361.         if(heater_mode == ON) {
  362.             heater_mode = OFF;
  363.         } else {
  364.             heater_mode = ON;
  365.             on_time = millis();
  366.         }
  367.     }
  368. }
  369.  
  370. // Display PID Constants
  371. void displayPid() {
  372.     lcd.clear();
  373.     lcd.print("P ");
  374.     lcd.print(kp, 2);
  375.     lcd.print(" I ");
  376.     lcd.print(ki, 2);
  377.     lcd.setCursor(0, 1);
  378.     lcd.print("D ");
  379.     lcd.print(kd, 2);
  380. }
  381.  
  382. // Create degree character
  383. void charSetup() {
  384.     byte degree[8] = {
  385.         B00010,
  386.         B00101,
  387.         B00010,
  388.         B00000,
  389.         B00000,
  390.         B00000,
  391.         B00000,
  392.     };
  393.    
  394.     lcd.createChar(0, degree) ;
  395. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement