Maiskolben/Maiskolben_TFT/Maiskolben_TFT.ino
Luca Zimmermann 17f415d323 Hooray, v2.5
New temperature measurement (again…), show the effective heat power in
watts, advanced features for Rev. 3.x (more to come), some private
features for testing
2016-12-05 18:27:19 +01:00

842 lines
23 KiB
C++

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <PID_v1.h>
#include <EEPROM.h>
#include "TimerOne.h"
#include "definitions.h"
/*
* If red is blue and blue is red change this (2.0 and above have new display types)
* If not sure, leave commented, you will be shown a setup screen
*/
//#define HARDWARE_DEFINED_TFT 2
/*
* Based on your Hardware-Revision there may be modifications to the PCB.
* In V3 and up is a second voltage measurement circuit.
* For THT this should be set to anything < 3
* Normally leave this commented as it is stored in EEPROM
*/
//#define HARDWARE_REVISON 3
//#ifndef PIN_A7
//#define HARDWARE_REVISON 1
//#endif
/*
* Only used for testing, do not use.
*/
//#define INSTALL
//#define TEST_ADC
volatile 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, menu = false;
volatile uint8_t pwm, threshold_counter;
volatile int16_t cur_t, last_measured;
volatile error_type error = NO_ERROR;
error_type error_old;
int16_t stored[3] = {250, 300, 350}, set_t = 150, set_t_old, cur_t_old, target_t;
double pid_val, cur_td, set_td;
uint8_t store_to = 255;
p_source power_source, power_source_old = NO_INIT;
boolean blink;
uint16_t cnt_measure_voltage, cnt_compute, cnt_sw_poll, cnt_but_press, cnt_off_press, cnt_but_store;
float v_c1, v_c2, v_c3, v_in, v;
uint8_t array_index, array_count;
uint32_t sendNext;
uint32_t last_temperature_drop;
uint32_t last_on_state;
boolean wasOff = true, old_stby = false;
boolean autopower = true, bootheat = false;
uint8_t revision = 1;
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, 0);
#define ST7735_GRAY 0x94B2
PID heaterPID(&cur_td, &pid_val, &set_td, kp, ki, kd, DIRECT);
void setup(void) {
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);
pinMode(TFT_CS, OUTPUT);
digitalWrite(TFT_CS, HIGH);
Serial.begin(115200);
boolean force_menu = false;
if (EEPROM.read(0) != EEPROM_CHECK) {
EEPROM.update(0, EEPROM_CHECK);
updateEEPROM();
force_menu = true;
}
//delay(1000);
#ifdef HARDWARE_DEFINED_TFT
#if HARDWARE_DEFINED_TFT == 1
tft.initR(INITR_BLACKTAB);
EEPROM.update(EEPROM_DISPLAY, INITR_BLACKTAB);
#else
tft.initR(INITR_GREENTAB);
EEPROM.update(EEPROM_DISPLAY, INITR_GREENTAB);
#endif
#else
if (force_menu || EEPROM.read(EEPROM_VERSION) < 23 || EEPROM.read(EEPROM_VERSION) == 255 || (EEPROM.read(EEPROM_DISPLAY) != INITR_GREENTAB && EEPROM.read(EEPROM_DISPLAY) != INITR_BLACKTAB)) {
tft.initR(INITR_GREENTAB);
tft.setRotation(1);
tft.fillScreen(ST7735_BLACK);
tft.setTextSize(2);
tft.setCursor(0,0);
tft.setTextColor(ST7735_WHITE);
tft.print(F("What color is displayed?"));
tft.setCursor(10,112);
tft.setTextColor(ST7735_RED);
tft.print("RED BLUE");
while (true) {
if (!digitalRead(SW_T1)) {
EEPROM.update(EEPROM_DISPLAY, INITR_GREENTAB);
break;
}
if (!digitalRead(SW_T3)) {
EEPROM.update(EEPROM_DISPLAY, INITR_BLACKTAB);
tft.initR(INITR_BLACKTAB);
tft.setRotation(1);
break;
}
}
tft.fillScreen(ST7735_BLACK);
tft.setTextColor(ST7735_YELLOW);
tft.drawBitmap(0, 20, maiskolben, 160, 64, ST7735_YELLOW);
tft.setCursor(20,86);
tft.setTextColor(ST7735_YELLOW);
tft.setTextSize(2);
tft.print("Maiskolben");
tft.setCursor(35,104);
tft.print("Welcome!");
delay(4000);
while (!digitalRead(SW_T3) || !digitalRead(SW_T1)) delay(100);
} else {
tft.initR(EEPROM.read(EEPROM_DISPLAY));
}
#endif
#ifdef INSTALL
//EEPROM.update(EEPROM_INSTALL, 0);
if (EEPROM.read(EEPROM_INSTALL) != EEPROM_CHECK) {
tft.setRotation(1);
tft.setTextColor(ST7735_WHITE, ST7735_BLACK);
tft.fillScreen(ST7735_BLACK);
tft.setCursor(0,0);
tft.setTextSize(2);
tft.println("Installation");
for (int16_t i = -255; i < 256; i++) {
analogWrite(HEAT_LED, 255-abs(i));
delay(1);
}
while (digitalRead(SW_STBY)) {
int t = getTemperature();
Serial.println(t);
digitalWrite(HEATER_PWM, !digitalRead(SW_T1) | !digitalRead(SW_T2) | !digitalRead(SW_T3) | !digitalRead(SW_UP) | !digitalRead(SW_DOWN));
tft.setCursor(0,18);
tft.print(t);
tft.println(" ");
delay(50);
}
EEPROM.update(EEPROM_OPTIONS, (bootheat << 1) | autopower);
EEPROM.update(EEPROM_VERSION, EE_VERSION);
EEPROM.update(EEPROM_INSTALL, EEPROM_CHECK);
tft.println("done.");
delay(1000);
asm volatile("jmp 0");
}
#endif
if (EEPROM.read(EEPROM_VERSION) != EE_VERSION) {
force_menu = true;
}
tft.fillScreen(ST7735_BLACK);
tft.setRotation(1);
uint8_t options = EEPROM.read(EEPROM_OPTIONS);
autopower = options & 1;
bootheat = (options >> 1) & 1;
if (force_menu) optionMenu();
else {
tft.drawBitmap(0, 20, maiskolben, 160, 64, ST7735_YELLOW);
tft.setCursor(20,86);
tft.setTextColor(ST7735_YELLOW);
tft.setTextSize(2);
tft.print("Maiskolben");
tft.setCursor(50,110);
tft.setTextSize(1);
tft.print("Version ");
tft.print(VERSION);
//Allow Options to be set at startup
attachInterrupt(digitalPinToInterrupt(SW_STBY), optionMenu, LOW);
for (int i = 1; i < 11; i++) {
digitalWrite(HEAT_LED, i % 2);
delay(250);
}
detachInterrupt(digitalPinToInterrupt(SW_STBY));
}
revision = EEPROM.read(EEPROM_REVISION);
/*
* lower frequency = noisier tip
* higher frequency = needs higher pwm
*/
//PWM Prescaler = 1024 31Hz
//TCCR2B = (TCCR2B & 0b11111000) | 7;
//PWM Prescaler = 256 122Hz
//TCCR2B = (TCCR2B & 0b11111000) | 6;
//PWM Prescaler = 128 245Hz
TCCR2B = (TCCR2B & 0b11111000) | 5;
//PWM Prescaler = 64 490Hz
//TCCR2B = (TCCR2B & 0b11111000) | 4
//PWM Prescaler = 32 980Hz
//TCCR2B = (TCCR2B & 0b11111000) | 3;
//PWM Prescaler = 8 3.9kHz
//TCCR2B = (TCCR2B & 0b11111000) | 2
//PWM Prescaler = 1 31kHz - no Noise
//TCCR2B = (TCCR2B & 0b11111000) | 1;
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(EEPROM_SET_T) << 8;
set_t |= EEPROM.read(EEPROM_SET_T+1);
for (uint8_t i = 0; i < 50; i++)
measureVoltage(); //measure average 50 times to get realistic results
tft.fillScreen(ST7735_BLACK);
last_measured = getTemperature();
Timer1.initialize(1000);
Timer1.attachInterrupt(timer_isr);
heaterPID.SetMode(AUTOMATIC);
sendNext = millis();
if (bootheat) {
threshold_counter = TEMP_UNDER_THRESHOLD;
setOff(false);
}
}
void optionMenu(void) {
tft.fillScreen(ST7735_BLACK);
digitalWrite(HEAT_LED, LOW);
tft.setTextSize(2);
tft.setCursor(0,0);
tft.setTextColor(ST7735_WHITE);
tft.println("Options\n");
tft.setTextColor(ST7735_WHITE);
tft.setCursor(10,112);
tft.print("ON OFF EXIT");
uint8_t options = 2;
uint8_t opt = 0;
boolean redraw = true;
while (true) {
if (redraw) {
tft.setCursor(0,36);
#ifdef SHUTOFF_ACTIVE
tft.setTextColor(autopower?ST7735_GREEN:ST7735_RED);
#else
tft.setTextColor(ST7735_GRAY);
#endif
tft.println(" Autoshutdown");
#ifdef BOOTHEAT_ACTIVE
tft.setTextColor(bootheat?ST7735_GREEN:ST7735_RED);
#else
tft.setTextColor(ST7735_GRAY);
#endif
tft.println(" Heat on boot");
tft.setCursor(0, (opt+2)*18);
tft.setTextColor(ST7735_WHITE);
tft.print(">");
redraw = false;
}
if (!digitalRead(SW_UP)) {
tft.setCursor(0, (opt+2)*18);
tft.setTextColor(ST7735_BLACK);
tft.print(">");
opt = (opt+options-1)%options;
while (!digitalRead(SW_UP)) delay(100);
redraw = true;
}
if (!digitalRead(SW_DOWN)) {
tft.setCursor(0, (opt+2)*18);
tft.setTextColor(ST7735_BLACK);
tft.print(">");
opt = (opt+1)%options;
while (!digitalRead(SW_DOWN)) delay(100);
redraw = true;
}
if (!digitalRead(SW_T1)) {
switch (opt) {
case 0: autopower = 1; break;
case 1: bootheat = 1; break;
}
redraw = true;
}
if (!digitalRead(SW_T2)) {
switch (opt) {
case 0: autopower = 0; break;
case 1: bootheat = 0; break;
}
redraw = true;
}
if (!digitalRead(SW_T3)) break;
}
EEPROM.update(EEPROM_OPTIONS, (bootheat << 1) | autopower);
#if HARDWARE_REVISION >= 3
EEPROM.update(EEPROM_REVISION, HARDWARE_REVISION);
#else
if (EEPROM.read(EEPROM_VERSION) < 25 || EEPROM.read(EEPROM_VERSION) > 100) {
EEPROM.update(EEPROM_REVISION, 2);
}
#endif
EEPROM.update(EEPROM_VERSION, EE_VERSION);
}
void updateEEPROM(void) {
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(EEPROM_OPTIONS, 1); //Defaults to auto power down
}
int getTemperature(void) {
analogRead(TEMP_SENSE);//Switch ADC MUX
uint16_t adc = median(TEMP_SENSE);
#ifdef TEST_ADC
Serial.println(adc);
#endif
if (adc >= 1020) { //Illegal value, tip not plugged in
analogWrite(HEATER_PWM, 0);
if (!off)
setError(NO_TIP);
return 999;
} else {
analogWrite(HEATER_PWM, pwm); //switch heater back to last value
}
return round(adc*0.574503+43.5); //nasty resistors...
//return round(adc < 210 ? (((float)adc) * 0.530805 + 38.9298) : (((float)adc) * 0.415375 + 64.6123)); //old conversion
//return round(((float) adc)*ADC_TO_TEMP_GAIN+ADC_TO_TEMP_OFFSET); //even older conversion
}
void measureVoltage(void) {
analogRead(BAT_C1); //Switch analog MUX before measuring
v_c1 = v_c1*.9+(analogRead(BAT_C1)*5/1024.0)*.1; //no divisor
analogRead(BAT_C2);
v_c2 = v_c2*.9+(analogRead(BAT_C2)*5/512.0)*.1; //divisor 1:1 -> /2
analogRead(BAT_C3);
v_c3 = v_c3*.9+(analogRead(BAT_C3)*(5.0*3.0)/1024.0)*.1; //maximum measurable is ~15V
v = v_c3;
if (revision < 3) return;
#ifdef VIN
analogRead(VIN);
v_in = v_in*.9+(analogRead(VIN)*25/1024.0)*.1; //maximum measurable is ~24.5V
Serial.println(v_in);
if (v_in > 1.0) {
v = v_in; //backwards compatibility
}
#endif
}
uint16_t median(uint8_t analogIn) {
uint16_t adcValue[3];
for (uint8_t i = 0; i < 3; i++) {
adcValue[i] = analogRead(analogIn); // read the input 3 times
}
uint16_t tmp;
if (adcValue[0] > adcValue[1]) {
tmp = adcValue[0];
adcValue[0] = adcValue[1];
adcValue[1] = tmp;
}
if (adcValue[1] > adcValue[2]) {
tmp = adcValue[1];
adcValue[1] = adcValue[2];
adcValue[2] = tmp;
}
if (adcValue[0] > adcValue[1]) {
tmp = adcValue[0];
adcValue[0] = adcValue[1];
adcValue[1] = tmp;
}
return adcValue[1];
}
void timer_sw_poll(void) {
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) {
store_to = 255;
store_invalid = true;
} else if (error != NO_ERROR) {
if (!(t1 | t2 | t3)) {
store_invalid = false;
} else if (!store_invalid && t3) {
error = NO_ERROR; //dismiss
set_t_old = 0; //refresh set_t display
store_invalid = true; //wait for release
}
} else {
//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;
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) {
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;
last_measured = cur_t;
last_temperature_drop = millis();
last_on_state = millis()/1000;
EEPROM.update(1, stby);
}
void setOff(boolean state) {
if (state == off) return;
if (!state)
analogWrite(HEATER_PWM, 0);
else
setStandby(false);
if (power_source == POWER_USB && !state) {
state = true; //don't switch on, if powered via USB
setError(USB_ONLY);
}
last_on_state = millis()/1000;
off = state;
wasOff = true;
last_measured = cur_t;
}
void display(void) {
int16_t temperature = cur_t; //buffer volatile value
boolean yell = stby || (stby_layoff && blink);
tft.drawCircle(20,63,8, off?ST7735_RED:yell?ST7735_YELLOW:ST7735_GREEN);
tft.drawCircle(20,63,7,off?ST7735_RED:yell?ST7735_YELLOW:ST7735_GREEN);
tft.fillRect(19,55,3,3,ST7735_BLACK);
tft.drawFastVLine(20,53,10, off?ST7735_RED:yell?ST7735_YELLOW:ST7735_GREEN);
if (error != NO_ERROR) {
if (error != error_old) {
error_old = error;
tft.setTextSize(1);
tft.setTextColor(ST7735_RED, ST7735_BLACK);
tft.setCursor(0,96);
switch (error) {
case EXCESSIVE_FALL:
tft.print("Error: Temperature dropped\nTip slipped out?");
break;
case NOT_HEATING:
tft.print("Error: Not heating\nWeak power source or short");
break;
case BATTERY_LOW:
tft.print("Error: Battery low\nReplace or charge");
break;
case USB_ONLY:
tft.print("Error: Power too low\nConnect power >5V");
break;
case NO_TIP:
tft.print("Error: No tip connected\nTip slipped out?");
break;
}
tft.setTextSize(2);
tft.setTextColor(ST7735_YELLOW, ST7735_BLACK);
tft.setCursor(10,112);
tft.print(" OK ");
tft.setTextColor(ST7735_RED, ST7735_BLACK);
tft.setCursor(36,26);
tft.setTextSize(3);
tft.print(" ERR");
}
} else {
if (error != error_old) {
tft.fillRect(0, 96, 160, 16, ST7735_BLACK);
error_old = NO_ERROR;
}
tft.setTextSize(2);
tft.setCursor(15,112);
tft.setTextColor(ST7735_WHITE, ST7735_BLACK);
tft.print(stored[0]);
tft.write(' ');
tft.print(stored[1]);
tft.write(' ');
tft.print(stored[2]);
if (set_t_old != set_t || old_stby != (stby || stby_layoff)) {
tft.setCursor(36,26);
tft.setTextSize(3);
if (stby || stby_layoff) {
old_stby = true;
tft.setTextColor(ST7735_YELLOW, ST7735_BLACK);
tft.print("STBY");
} else {
old_stby = false;
set_t_old = set_t;
tft.setTextColor(ST7735_WHITE, ST7735_BLACK);
tft.write(' ');
tft.print(set_t);
tft.fillTriangle(149, 50, 159, 50, 154, 38, (set_t < MAX_TEMP) ? ST7735_WHITE : ST7735_GRAY);
tft.fillTriangle(149, 77, 159, 77, 154, 90, (set_t > MIN_TEMP) ? ST7735_WHITE : ST7735_GRAY);
}
}
if (!off) {
#ifdef SHUTOFF_ACTIVE
if (autopower) {
int16_t tout;
if (stby) {
tout = min(max(0,(last_on_state + OFF_TIMEOUT - (millis())/1000)), OFF_TIMEOUT);
} else {
tout = min(max(0,(last_temperature_drop + STANDBY_TIMEOUT - (millis())/1000)), STANDBY_TIMEOUT);
}
tft.setTextColor(stby?ST7735_RED:ST7735_YELLOW, ST7735_BLACK);
tft.setTextSize(2);
tft.setCursor(46,78);
if (tout < 600) tft.write('0');
tft.print(tout/60);
tft.write(':');
if (tout%60 < 10) tft.write('0');
tft.print(tout%60);
}
#endif
} else if (temperature != 999) {
tft.fillRect(46, 78, 60, 20, ST7735_BLACK);
}
}
if (cur_t_old != temperature) {
tft.setCursor(36,52);
tft.setTextSize(3);
if (temperature == 999) {
tft.setTextColor(ST7735_RED, ST7735_BLACK);
tft.print(" ERR");
tft.setCursor(44,76);
tft.setTextSize(2);
tft.print("NO TIP");
} else {
if (cur_t_old == 999) {
tft.fillRect(44,76,72,16,ST7735_BLACK);
}
tft.setTextColor(off ? temperature < 50 ? ST7735_CYAN : ST7735_RED : tft.Color565(min(10,abs(temperature-target_t))*25, 250 - min(10,max(0,(abs(temperature-target_t)-10)))*25, 0), ST7735_BLACK);
if (temperature < 50) {
tft.print("COLD");
} else {
tft.write(' ');
if (temperature < 100) tft.write(' ');
tft.print(temperature);
}
}
if (temperature < cur_t_old)
tft.drawFastHLine((int)(temperature/2.6), 0, 160-(int)(temperature/2.6), ST7735_BLACK);
else if (cur_t != 999) {
for (int16_t i = 0; i < temperature/2.6; i++) {
tft.drawPixel(i, 0, tft.Color565(min(255, max(0, i*5)), min(255, max(0, 400-i*2.5)), 0));
}
}
cur_t_old = temperature;
}
if (v_c3 > 1.0) {
tft.setTextColor(ST7735_YELLOW, ST7735_BLACK);
tft.setCursor(122,5);
tft.setTextSize(2);
int power = min(15,v)*min(15,v)/4.8*pwm/255;
if (power < 10) tft.write(' ');
tft.print(power);
tft.write('W');
if (v < 5.0) {
power_source = POWER_USB;
} else if (v_c2 < 1.0) {
power_source = POWER_CORD;
} else {
power_source = POWER_LIPO;
}
if (power_source != power_source_old) {
tft.fillRect(0, 5, 128, 20, ST7735_BLACK);
tft.fillRect(11, 25, 21, 20, ST7735_BLACK);
switch (power_source) {
case POWER_LIPO:
for (uint8_t i = 0; i < 3; i++) {
tft.fillRect(11, 5+i*14, 20, 12, ST7735_WHITE);
tft.fillRect(12, 6+i*14, 18, 10, ST7735_BLACK);
tft.drawFastVLine(31,8+i*14,6,ST7735_WHITE);
}
break;
case POWER_USB:
tft.setTextSize(1);
tft.setTextColor(ST7735_RED, ST7735_BLACK);
tft.setCursor(0,5);
tft.print("USB power only\nConnect power supply.");
if (!off) setError(USB_ONLY);
break;
}
power_source_old = power_source;
}
if (power_source == POWER_CORD) {
if (v > v_c3) {
tft.setTextSize(2);
tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
tft.setCursor(0,5);
tft.print(v);
tft.print("V ");
} else {
tft.drawBitmap(0, 5, power_cord, 24, 9, tft.Color565(max(0, min(255, (14.5-v)*112)), max(0, min(255, (v-11)*112)), 0));
}
} else if (power_source == POWER_LIPO) {
float volt[] = {v_c1, v_c2-v_c1, v_c3-v_c2};
for (uint8_t i = 0; i < 3; i++) {
if (volt[i] < 3.20) {
setError(BATTERY_LOW);
tft.fillRect(13, 7+14*i, max(1,min(16,(volt[i]-3.0)*14.2)), 8, blink?ST7735_RED:ST7735_BLACK);
} else {
tft.fillRect(13, 7+14*i, max(1,min(16,(volt[i]-3.0)*14.2)), 8, tft.Color565(250-min(250, max(0, (volt[i]-3.4)*1000.0)), max(0,min(250, (volt[i]-3.15)*1000.0)), 0));
}
tft.fillRect(13+max(1,min(16,(volt[i]-3.0)*14.2)), 7+14*i, 17-max(1,min(16,(volt[i]-3.0)*14.2)), 8, ST7735_BLACK);
}
}
}
#ifdef SHUTOFF_ACTIVE
if (autopower) {
if (pwm > max(20, (cur_t-150)/50*round(25-min(15,v)))+5) {
//if (target_t-cur_t > 0.715*exp(0.0077*target_t)) {
//if (cur_t / (double)target_t < STANDBY_TEMPERATURE_DROP) {
if (stby && !wasOff) {
setStandby(false);
} else {
last_temperature_drop = millis()/1000;
}
} else if (wasOff) {
wasOff = false;
}
if (!off && !stby && millis()/1000 > (last_temperature_drop + STANDBY_TIMEOUT)) {
setStandby(true);
}
if (!off && stby && millis()/1000 > (last_on_state + OFF_TIMEOUT)) {
setOff(true);
}
}
#endif
blink = !blink;
}
void compute(void) {
cur_t = getTemperature();
if (off) {
target_t = 0;
if (cur_t < TEMP_THRESHOLD) {
threshold_counter = TEMP_UNDER_THRESHOLD; //reset counter
}
} else {
if (stby_layoff || stby) {
target_t = STBY_TEMP;
} else {
target_t = set_t;
}
if (cur_t-last_measured <= -30 && last_measured != 999) {
setError(EXCESSIVE_FALL); //decrease of more than 30 degree is not realistic, short of ring and gnd is common.
}
if (cur_t < TEMP_THRESHOLD) {
if (threshold_counter == 0) {
setError(NOT_HEATING); //temperature is not reached in desired time, short of sensor and gnd too?
} else {
threshold_counter--;
}
} else {
threshold_counter = THRES_MAX_DECEED; //reset counter to a smaller value to allow small oscillation of temperature
}
}
set_td = target_t;
cur_td = cur_t;
last_measured = cur_t;
heaterPID.Compute();
if (error != NO_ERROR || off)
pwm = 0;
else
pwm = min(255,pid_val*255);
analogWrite(HEATER_PWM, pwm);
}
void timer_isr(void) {
if (cnt_compute >= TIME_COMPUTE_IN_MS+DELAY_BEFORE_MEASURE) {
compute();
cnt_compute=0;
} else if(cnt_compute >= TIME_COMPUTE_IN_MS) {
analogWrite(HEATER_PWM, 0); //switch off heater to let the low pass settle
}
cnt_compute++;
if(cnt_sw_poll >= TIME_SW_POLL_IN_MS){
timer_sw_poll();
cnt_sw_poll=0;
}
cnt_sw_poll++;
if(cnt_measure_voltage >= TIME_MEASURE_VOLTAGE_IN_MS) {
measureVoltage();
cnt_measure_voltage=0;
}
cnt_measure_voltage++;
}
void setError(error_type e) {
error = e;
setOff(true);
}
void loop(void) {
analogWrite(HEAT_LED, pwm);
//Switch to following if the oscillation of the led bothers you
//digitalWrite(HEAT_LED, cur_t+5 < target || (abs((int16_t)cur_t-(int16_t)target) <= 5 && (millis()/(stby?1000:500))%2));
if (sendNext <= millis()) {
sendNext += 100;
#ifndef TEST_ADC
Serial.print(stored[0]);
Serial.print(";");
Serial.print(stored[1]);
Serial.print(";");
Serial.print(stored[2]);
Serial.print(";");
Serial.print(off?0:1);
Serial.print(";");
Serial.print(error);
Serial.print(";");
Serial.print(stby?1:0);
Serial.print(";");
Serial.print(stby_layoff?1:0);
Serial.print(";");
Serial.print(set_t);
Serial.print(";");
Serial.print(cur_t);
Serial.print(";");
Serial.print(pid_val);
Serial.print(";");
Serial.print(v_c2>1.0?v_c1:0.0);
Serial.print(";");
Serial.print(v_c2);
Serial.print(";");
Serial.println(v);
#endif
Serial.flush();
display();
}
if (Serial.available()) {
uint16_t t = 0;
switch (Serial.read()) {
//Set new Temperature (eg. S350 to set to 350C)
case 'T':
if (Serial.available() >= 3) {
t = serialReadTemp();
//Serial.println(t);
if (t <= MAX_TEMP && t >= MIN_TEMP) {
set_t = t;
updateEEPROM();
}
}
break;
//Store new Preset (eg. P1200 to store 200C to Preset 1, NOT 0 indexed)
case 'P':
if (Serial.available() >= 4) {
uint8_t slot = Serial.read()-'1';
if (slot < 3) {
t = serialReadTemp();
if (t <= MAX_TEMP && t >= MIN_TEMP) {
stored[slot] = t;
updateEEPROM();
}
}
}
break;
//Clear errors
case 'C':
error = NO_ERROR;
break;
//Set standby
case 'S':
setStandby(Serial.read() == '1');
break;
//Set on/off
case 'O':
setOff(Serial.read() == '0');
break;
}
}
delay(DELAY_MAIN_LOOP);
}
uint16_t serialReadTemp() {
uint16_t t;
uint8_t n;
n = Serial.read()-'0';
t = min(9, max(0, n))*100;
n = Serial.read()-'0';
t += min(9, max(0, n))*10;
n = Serial.read()-'0';
t += min(9, max(0, n))*1;
return t;
}