diff --git a/Maiskolben.ino b/Maiskolben.ino new file mode 100644 index 0000000..5858f07 --- /dev/null +++ b/Maiskolben.ino @@ -0,0 +1,434 @@ +#include +#include +#include +#include +#include "TimerOne.h" + +#define VERSION 100 +#define EEPROM_CHECK 42 + +#define STBY_TEMP 150 +//SOFTWARE CAN'T MEASURE MORE THAN 422, IF SET TO >= 422 IT'S LIKELY TO KILL YOUR TIP! +//If read 1024 on Analog in, the tip is turned off. +#define MAX_TEMP 400 +#define MIN_TEMP 100 + +//Temperature in degree to rise at least in given time +#define TEMP_MIN_RISE 10 +//Time in that the temperature must rise by the set temperature +#define TEMP_RISE_TIME 1000 + +#define HEATER_PWM 3 +#define SW_T1 5 +#define SW_T2 6 +#define SW_T3 7 +#define TEMP_SENSE A0 +#define STBY_NO A1 +#define SW_STBY A2 +#define HEAT_LED A3 +#define SW_DOWN A4 +#define SW_UP A5 +#define BATTERY_IN A6 + +#define TIME_DISP_REFRESH_IN_MS 300 +#define TIME_SW_POLL_IN_MS 10 +#define DELAY_BEFORE_MEASURE 10 +#define DELAY_MAIN_LOOP 10 + +#define ADC_TO_TEMP_GAIN 0.39 +#define ADC_TO_TEMP_OFFSET 23.9 +#define CTRL_GAIN 10 + +#define NUM_DIFFS 8 //Dividable by 8 + +boolean off = true, stby = true, stby_layoff = true, sw_stby_old = false, sw_up_old = false, sw_down_old = false, clear_display = true, store_invalid = true, error = false, menu = false; +uint16_t stored[3] = {250, 300, 350}, set_t = 150, cur_t, set_t_old, cur_t_old; +uint8_t pwm, store_to = 255, contrast = 50; +uint16_t cnt_disp_refresh = TIME_DISP_REFRESH_IN_MS, cnt_sw_poll, cnt_but_press, cnt_off_press, cnt_but_store; +float battery_voltage; +uint16_t last_measured; +int16_t last_diffs[NUM_DIFFS]; +uint8_t array_index, array_count; + +Adafruit_PCD8544 display = Adafruit_PCD8544(10, 9, -1); + +void setup() { + digitalWrite(HEATER_PWM, LOW); + pinMode(HEATER_PWM, OUTPUT); + pinMode(HEAT_LED, OUTPUT); + pinMode(TEMP_SENSE, INPUT); + pinMode(SW_T1, INPUT_PULLUP); + pinMode(SW_T2, INPUT_PULLUP); + pinMode(SW_T3, INPUT_PULLUP); + pinMode(SW_UP, INPUT_PULLUP); + pinMode(SW_DOWN, INPUT_PULLUP); + pinMode(STBY_NO, INPUT_PULLUP); + pinMode(SW_STBY, INPUT_PULLUP); + + for (uint8_t i = 0; i < NUM_DIFFS; i++) { + last_diffs[i] = 0; + } + + //PWM Prescaler = 1024 + TCCR2B = TCCR2B & 0xFF; + + display.begin(); + display.clearDisplay(); + if (EEPROM.read(0) != EEPROM_CHECK) { + EEPROM.update(0, EEPROM_CHECK); + updateEEPROM(); + } + stby = EEPROM.read(1); + for (uint8_t i = 0; i < 3; i++) { + stored[i] = EEPROM.read(2+i*2) << 8; + stored[i] |= EEPROM.read(3+i*2); + } + set_t = EEPROM.read(8) << 8; + set_t |= EEPROM.read(9); + setContrast(EEPROM.read(10)); + delay(500); + + + Serial.begin(115200); + Serial.println("DIFF SUM;SET TEMPERATURE;IS TEMPERATURE;DUTY CYCLE;VOLTAGE"); + last_measured = getTemperature(); + Timer1.initialize(1000); + Timer1.attachInterrupt(timer_isr); +} + +void updateEEPROM() { + EEPROM.update(1, stby); + for (uint8_t i = 0; i < 3; i++) { + EEPROM.update(2+i*2, stored[i] >> 8); + EEPROM.update(3+i*2, stored[i] & 0xFF); + } + EEPROM.update(8, set_t >> 8); + EEPROM.update(9, set_t & 0xFF); + EEPROM.update(10, contrast); +} + +void setContrast(uint8_t value) { + if (value == 255) value = 0; + contrast = min(100, value); + EEPROM.update(10, contrast); + display.setContrast(contrast); +} + +int getTemperature() { + analogWrite(HEATER_PWM, 0); //switch off heater + delay(DELAY_BEFORE_MEASURE); //wait for some time (to get low pass filter in steady state) + uint16_t adcValue = analogRead(TEMP_SENSE); // read the input + if (adcValue >= 1015) { //Illegal value + analogWrite(HEATER_PWM, 0); + return 999; + } else { + analogWrite(HEATER_PWM, pwm); //switch heater back to last value + } + //return 124; + return round(((float) adcValue)*ADC_TO_TEMP_GAIN+ADC_TO_TEMP_OFFSET); //apply linear conversion to actual temperature +} + +void timer_sw_poll() { + stby_layoff = !digitalRead(STBY_NO); + if (!digitalRead(SW_STBY)) { + if (cnt_off_press == 100) { + setOff(!off); + } + cnt_off_press = min(101, cnt_off_press+1); + } else { + if (cnt_off_press > 0 && cnt_off_press != 101) { + setStandby(!stby); + } + cnt_off_press = 0; + } + boolean t1 = !digitalRead(SW_T1); + boolean t2 = !digitalRead(SW_T2); + boolean t3 = !digitalRead(SW_T3); + + //simultanious push of multiple buttons + if (t1 + t2 + t3 > 1) { + if (store_to != 255) cnt_but_store = 0; + cnt_but_store = min(cnt_but_store+1, 255); + store_to = 255; + store_invalid = true; + if (cnt_but_store > 100) { + menu = true; + clear_display = true; + } + } else if (menu) { + if (t1 + t2 + t3 > 1) { + store_invalid = true; + } else if (!(t1 | t2 | t3)) { + store_invalid = false; + } else if (!store_invalid) { + if (t3) { + store_invalid = true; + menu = false; + clear_display = true; + } + } + } else if (error) { + if (!(t1 | t2 | t3)) { + store_invalid = false; + } else if (!store_invalid && t3) { + error = false; //dismiss + store_invalid = true; //wait for release + clear_display = true; + } + } else if (!off) { + //all buttons released + if (!(t1 | t2 | t3)) { + if (store_to != 255) { + if (cnt_but_store <= 100) { + set_t = stored[store_to]; + setStandby(false); + updateEEPROM(); + } + } + store_to = 255; + store_invalid = false; + cnt_but_store = 0; + } else + //one button pressed + if (!store_invalid) { + store_to = t2 + 2*t3; + if (cnt_but_store > 100) { + if (set_t != stored[store_to] && !stby) { + stored[store_to] = set_t; + cnt_but_store = 100; + clear_display = true; + updateEEPROM(); + } + } + cnt_but_store++; + } + } + boolean sw_up = !digitalRead(SW_UP); + boolean sw_down = !digitalRead(SW_DOWN); + boolean sw_changed = (sw_up != sw_up_old) || (sw_down !=sw_down_old); + sw_up_old = sw_up; + sw_down_old = sw_down; + if(sw_up && sw_down || !(sw_up || sw_down)) { + cnt_but_press = 0; + return; + } + if(sw_up || sw_down) { + cnt_but_press++; + if((cnt_but_press >= 100) || sw_changed) { + if (menu) { + if(sw_up) setContrast(contrast+1); + else setContrast(contrast-1); + if(!sw_changed) cnt_but_press = 70; + } else if (!off) { + setStandby(false); + if(sw_up && set_t < MAX_TEMP) set_t++; + else if (sw_down && set_t > MIN_TEMP) set_t--; + if(!sw_changed) cnt_but_press = 97; + updateEEPROM(); + } + } + } +} + +void setStandby(boolean state) { + if (state == stby) return; + stby = state; + //clear_display = true; + last_measured = cur_t; + EEPROM.update(1, stby); +} + +void setOff(boolean state) { + if (state == off) return; + if (!state) + analogWrite(HEATER_PWM, 0); + off = state; + clear_display = true; + last_measured = cur_t; +} + +void timer_disp_refresh() { + boolean disp = false, clr = false; + if (menu) { + if (clear_display) { + clear_display = false; + display.clearDisplay(); + display.setTextSize(1); + display.setCursor(16,0); + display.print("Contrast"); + display.setTextSize(1); + display.setCursor(58,40); + display.print("Exit"); + } + display.setTextSize(3); + display.fillRect(16, 10, 64, 24, WHITE); + display.setCursor(16+8*(contrast<100),10); + display.setTextSize(3); + display.print(contrast); + disp = true; + } else if (error) { + if (clear_display) { + display.clearDisplay(); + display.setCursor(16,0); + display.setTextSize(3); + display.print("ERR"); + display.setTextSize(1); + display.setCursor(41,40); + display.print("Dismiss"); + disp = true; + } + } else { + if (clear_display) { + clear_display = false; + clr = true; + display.clearDisplay(); + display.setTextSize(1); + display.setCursor(0,40); + display.print(stored[0]); + display.setCursor(32,40); + display.print(stored[1]); + display.setCursor(64,40); + display.print(stored[2]); + disp = true; + } + if (off) { + if (clr) { + display.fillRect(16, 0, 64, 24, WHITE); + display.setCursor(16,0); + display.setTextSize(3); + display.print("OFF"); + disp = true; + } + } else if (set_t_old != set_t || clr) { + display.fillRect(16, 0, 64, 24, WHITE); + display.setCursor(16,0); + display.setTextSize(3); + display.print(set_t); + set_t_old = set_t; + disp = true; + } + if (cur_t_old != cur_t || clr) { + display.fillRect(24, 24, 60, 16, WHITE); + display.setCursor(24+8*(cur_t<100),24); + display.setTextSize(2); + if (cur_t == 999) + display.print("ERR"); + else + display.print(cur_t); + if (stby_layoff || stby) { + display.setCursor(58,28); + display.setTextSize(1); + display.print("STBY"); + } + cur_t_old = cur_t; + disp = true; + } + } + if (battery_voltage > 1) { + display.fillRect(display.width()-11,0,10,6,BLACK); + display.drawRect(display.width()-10,1,8,4,WHITE); + uint8_t bars = min(6,max(0,48-4*battery_voltage)); + display.fillRect(display.width()-2-bars,2,bars,2,WHITE); + display.drawFastVLine(display.width()-1,2,2,BLACK); + if (battery_voltage < 10.5) { + setOff(true); + } + } + if (disp) + display.display(); +} + +void timer_isr() { + if(cnt_disp_refresh >= TIME_DISP_REFRESH_IN_MS) { + timer_disp_refresh(); + cnt_disp_refresh=0; + } + cnt_disp_refresh++; + if(cnt_sw_poll >= TIME_SW_POLL_IN_MS){ + timer_sw_poll(); + cnt_sw_poll=0; + } + cnt_sw_poll++; +} + +void setError() { + error = true; + setOff(true); +} + +uint8_t cnt_temp_rise; +int16_t diff_old; +void loop() { + cur_t = getTemperature(); + uint16_t target; + if (off) { + target = 0; + } else if (stby_layoff || stby) { + target = STBY_TEMP; + } else { + target = set_t; + } + int16_t diff = target-cur_t; + cnt_temp_rise++; + //check the temperature rise is high enough + int16_t delta = cur_t-last_measured; + if (delta <= -20) { + setError(); + } + if (diff >= 15) { + if (delta < -5 && array_count > 2) { + setError(); + } else { + last_diffs[array_index%NUM_DIFFS] = delta; + array_count = min(array_count+1, NUM_DIFFS); + if (array_count >= NUM_DIFFS) { + int16_t delta_t = 0; + for (uint8_t i = 0; i < array_count; i++) { + delta_t += last_diffs[(array_index+NUM_DIFFS-i)%NUM_DIFFS]; + } + if (delta_t < NUM_DIFFS/8*2) { + setError(); + } + } + array_index++; + } + } else { + array_count = 0; + } + last_measured = cur_t; + /* + if (cnt_temp_rise >= TEMP_RISE_TIME/DELAY_MAIN_LOOP) { + //if risen less then 20deg then it's a sensor failure (tip not plugged in?) + if (diff_old-diff < TEMP_MIN_RISE) { + error = true; + stby = true; + clear_display = true; + } else { + diff_old = diff; + cnt_temp_rise = 0; + } + } + */ + if (error) + pwm = 0; + else + pwm = max(0, min(255, diff*CTRL_GAIN)); + //reset counter if not heating that much + if (pwm < min(TEMP_MIN_RISE*CTRL_GAIN, 200)) cnt_temp_rise = 0; + + analogWrite(HEATER_PWM, pwm); + digitalWrite(HEAT_LED, cur_t <= target && (cur_t < target-5 || (millis()/1000)%2)); + battery_voltage = (analogRead(BATTERY_IN)*3*5/1024.0); + Serial.print(delta); + Serial.print(";"); + Serial.print(set_t); + Serial.print(";"); + Serial.print(cur_t); + Serial.print(";"); + Serial.print(pwm); + Serial.print(";"); + Serial.print(battery_voltage); + Serial.println("V"); + Serial.flush(); + delay(DELAY_MAIN_LOOP); +}