Compatibility for new displays - if you upgrade you will be asked if the screen shows blue or red. Improved the temperature measurement, the 100k pulldown altered the measurement, so the temperature was 30-50°C hotter than measured. Based on this measurement error, you can now officially set the max temperature of 450°C. Removed misleading offset temperature, if colder than 50°C it only shows cold. Added direct heating on boot and option to disable this and auto shutdown. Just press the power button when booting (led blinking). Powering the tip will no longer heat to standby, even if it was in standby before. Added installation routine, do not use if you don’t know what you’re doin’. Intended for external voltage divider (temperature element fake) and LED attached to heater instead of using a weller tip. Reduced prescaler to 128 for less noise. Testing is needed to reduce it further to something between 1 and 32 to eliminate sound from switching.
784 lines
21 KiB
C++
784 lines
21 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
|
|
*/
|
|
//#define HARDWARE_DEFINED_TFT 2
|
|
/*
|
|
* Only used for testing, do not use.
|
|
*/
|
|
//#define INSTALL
|
|
|
|
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;
|
|
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;
|
|
|
|
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);
|
|
Serial.begin(115200);
|
|
|
|
boolean force_menu = false;
|
|
if (EEPROM.read(0) != EEPROM_CHECK) {
|
|
EEPROM.update(0, EEPROM_CHECK);
|
|
updateEEPROM();
|
|
force_menu = true;
|
|
}
|
|
#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);
|
|
delay(1000);
|
|
#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();
|
|
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));
|
|
}
|
|
/*
|
|
* lower frequency = noisier tip
|
|
* higher frequency = needs higher pwm
|
|
*/
|
|
//PWM Prescaler = 1024
|
|
//TCCR2B = TCCR2B & (0b11111000 | 7);
|
|
//PWM Prescaler = 128
|
|
TCCR2B = TCCR2B & (0b11111000 | 5);
|
|
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);
|
|
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);
|
|
}
|
|
|
|
int getTemperature(void) {
|
|
analogRead(TEMP_SENSE);//Switch ADC MUX
|
|
uint16_t adc = median(TEMP_SENSE);
|
|
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 < 210 ? (((float)adc) * 0.530805 + 38.9298) : (((float)adc) * 0.415375 + 64.6123));
|
|
//return round(((float) adc)*ADC_TO_TEMP_GAIN+ADC_TO_TEMP_OFFSET); //old conversion
|
|
}
|
|
|
|
void measureVoltage(void) {
|
|
analogRead(BAT_C1); //Switch analog MUX before measuring
|
|
v_c1 = v_c1*.9+(analogRead(BAT_C1)*5/1024.0)*.1;
|
|
analogRead(BAT_C2);
|
|
v_c2 = v_c2*.9+(analogRead(BAT_C2)*5/512.0)*.1;
|
|
analogRead(BAT_C3);
|
|
v_c3 = v_c3*.9+(analogRead(BAT_C3)*(5.0*1510.0)/(510.0*1024.0))*.1; //maximum measurable is 14.79V
|
|
}
|
|
|
|
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) {
|
|
if (v_c3 < 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) {
|
|
tft.setTextSize(2);
|
|
tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
|
|
tft.setCursor(0,5);
|
|
tft.print(v_c3);
|
|
tft.print("V ");
|
|
} 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) {
|
|
Serial.print(pwm);
|
|
Serial.print(" > ");
|
|
Serial.println(max(20, (cur_t-150)/50*round(25-v_c3))+5);
|
|
Serial.println(round(25-v_c3));
|
|
if (pwm > max(20, (cur_t-150)/50*round(25-v_c3))+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;
|
|
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_c3);
|
|
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;
|
|
}
|