From e1d34c9aee961fd5ca77125d08758411b0dda88e Mon Sep 17 00:00:00 2001 From: Luca Zimmermann Date: Mon, 30 May 2016 16:09:47 +0200 Subject: [PATCH] Version 2.0 --- Maiskolben_GUI/Maiskolben_GUI.pde | 89 +++ .../Maiskolben_LCD.ino | 139 ++--- Maiskolben_TFT/Maiskolben_TFT.ino | 505 ++++++++++++++++++ Maiskolben_TFT/definitions.h | 149 ++++++ README.md | 122 ++++- 5 files changed, 940 insertions(+), 64 deletions(-) create mode 100644 Maiskolben_GUI/Maiskolben_GUI.pde rename Maiskolben.ino => Maiskolben_LCD/Maiskolben_LCD.ino (80%) create mode 100644 Maiskolben_TFT/Maiskolben_TFT.ino create mode 100644 Maiskolben_TFT/definitions.h diff --git a/Maiskolben_GUI/Maiskolben_GUI.pde b/Maiskolben_GUI/Maiskolben_GUI.pde new file mode 100644 index 0000000..7a97fbc --- /dev/null +++ b/Maiskolben_GUI/Maiskolben_GUI.pde @@ -0,0 +1,89 @@ +import processing.serial.*; +import java.lang.reflect.Array; +import javax.swing.*; + +static int gWidth = 400; +static int gHeight = 450; +static int frequency = 10; // receive data every 1/f seconds + +Serial Maiskolben; +java.util.LinkedList temperature = new java.util.LinkedList(); +float pidValue; +int setPoint; +int secs; +boolean off, standby, standby_layoff; +int[] stored = new int[3]; +PGraphics history; + +JToggleButton on = new JToggleButton(); +JToggleButton stby = new JToggleButton(); + + + +void setup() { + size(50,1); + surface.setResizable(true); + surface.setSize(gWidth+200,gHeight); + Object[] list = Serial.list(); + JComboBox serialPort = new JComboBox(list); + Object[] params = {"Select serial Port:",serialPort}; + Object[] opts = {"Select","Exit"}; + int opt = JOptionPane.showOptionDialog(null, params, "Select Maiskolben", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, opts, opts[0]); + if (opt == JOptionPane.CANCEL_OPTION) System.exit(0); + else { + Maiskolben = new Serial(this, (String)serialPort.getSelectedItem(), 115200); + Maiskolben.bufferUntil(10); + } + history = createGraphics(gWidth,gHeight); +} + +void draw() { + if (Maiskolben != null && Maiskolben.available() > 0) { + //println("Received something!"); + String line = Maiskolben.readStringUntil(10); + if (line != null) { + String values[] = line.split(";"); + if (values.length < 9) println("zu wenig!"); + else { + try { + secs = (secs+1)%frequency; + for (int i = 0; i < 3; stored[i] = Integer.parseInt(values[i++])); + off = values[3].equals("1"); + standby = values[4].equals("1"); + standby_layoff = values[5].equals("1"); + setPoint = Integer.parseInt(values[6]); + temperature.add(Integer.parseInt(values[7])); + if (temperature.size() > gWidth) temperature.remove(0); + pidValue = Float.parseFloat(values[8]); + history.beginDraw(); + history.background(50); + history.stroke(150); + for (int i = 50; i < gHeight; i+=50) { + history.line(0,i,gWidth,i); + } + for (int i = (temperature.size() == gWidth)?secs:0; i < gWidth; i+=frequency) { + history.line(gWidth-i,0,gWidth-i,gHeight); + } + history.stroke(255); + int lastV = temperature.getFirst(); + int x = 0; + for (int temp : temperature) { + history.line(x,gHeight-lastV,++x,gHeight-(lastV = temp)); + } + history.endDraw(); + } catch (Exception e) { + + } + } + println(line); + } + } + background(0); + image(history,0,0); + text(off?"Off":"On",gWidth+10,10); + text(standby?"Standby":"Active",gWidth+10,30); + text(standby_layoff?"In Holder":"",gWidth+10,50); + text("Set Temperature "+setPoint,gWidth+10,70); + if (temperature.size() != 0) + text("Current Temperature "+temperature.getLast(),gWidth+10,90); +} \ No newline at end of file diff --git a/Maiskolben.ino b/Maiskolben_LCD/Maiskolben_LCD.ino similarity index 80% rename from Maiskolben.ino rename to Maiskolben_LCD/Maiskolben_LCD.ino index 5858f07..a41aab8 100644 --- a/Maiskolben.ino +++ b/Maiskolben_LCD/Maiskolben_LCD.ino @@ -1,15 +1,17 @@ #include #include #include +#include #include #include "TimerOne.h" #define VERSION 100 #define EEPROM_CHECK 42 +//#define HAS_BATTERY #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. +//SOFTWARE CAN'T MEASURE MORE THAN 422 DUE TO RESISTOR CONFIGURATION, 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 @@ -30,10 +32,15 @@ #define SW_UP A5 #define BATTERY_IN A6 +#define kp 0.1 +#define ki 0.0001 +#define kd 0.0 + #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 PID_SAMPLE_TIME 10 #define ADC_TO_TEMP_GAIN 0.39 #define ADC_TO_TEMP_OFFSET 23.9 @@ -41,17 +48,21 @@ #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; +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, error = false, menu = false; uint16_t stored[3] = {250, 300, 350}, set_t = 150, cur_t, set_t_old, cur_t_old; +double pid_val, cur_td, set_td; 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; +uint32_t sendNext; Adafruit_PCD8544 display = Adafruit_PCD8544(10, 9, -1); +PID heaterPID(&cur_td, &pid_val, &set_td, kp, ki, kd, DIRECT); + void setup() { digitalWrite(HEATER_PWM, LOW); pinMode(HEATER_PWM, OUTPUT); @@ -70,9 +81,10 @@ void setup() { } //PWM Prescaler = 1024 - TCCR2B = TCCR2B & 0xFF; - + TCCR2B = TCCR2B & 0b11111000 | 7; + delay(500); display.begin(); + delay(500); display.clearDisplay(); if (EEPROM.read(0) != EEPROM_CHECK) { EEPROM.update(0, EEPROM_CHECK); @@ -86,14 +98,15 @@ void setup() { 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"); + //Serial.println("DIFF SUM;SET TEMPERATURE;IS TEMPERATURE;DUTY CYCLE;VOLTAGE"); last_measured = getTemperature(); Timer1.initialize(1000); Timer1.attachInterrupt(timer_isr); + heaterPID.SetMode(AUTOMATIC); + sendNext = millis(); } void updateEEPROM() { @@ -120,6 +133,7 @@ int getTemperature() { uint16_t adcValue = analogRead(TEMP_SENSE); // read the input if (adcValue >= 1015) { //Illegal value analogWrite(HEATER_PWM, 0); + setOff(true); return 999; } else { analogWrite(HEATER_PWM, pwm); //switch heater back to last value @@ -292,11 +306,18 @@ void timer_disp_refresh() { disp = true; } if (off) { - if (clr) { - display.fillRect(16, 0, 64, 24, WHITE); - display.setCursor(16,0); - display.setTextSize(3); - display.print("OFF"); + if (clr || ((cur_t_old != cur_t) && ((cur_t_old == 999) || (cur_t == 999)))) { + if (cur_t == 999) { + display.fillRect(0,0,84,24, WHITE); + display.setCursor(6,4); + display.setTextSize(2); + display.print("NO TIP"); + } else { + display.fillRect(0, 0, 84, 24, WHITE); + display.setCursor(16,0); + display.setTextSize(3); + display.print("OFF"); + } disp = true; } } else if (set_t_old != set_t || clr) { @@ -324,6 +345,7 @@ void timer_disp_refresh() { disp = true; } } + #ifdef HAS_BATTERY if (battery_voltage > 1) { display.fillRect(display.width()-11,0,10,6,BLACK); display.drawRect(display.width()-10,1,8,4,WHITE); @@ -334,6 +356,7 @@ void timer_disp_refresh() { setOff(true); } } + #endif if (disp) display.display(); } @@ -353,6 +376,7 @@ void timer_isr() { void setError() { error = true; + clear_display = true; setOff(true); } @@ -368,67 +392,56 @@ void loop() { } 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) { + if (!off && delta <= -20 && cur_td != 999) { 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; - } + + set_td = target; + cur_td = cur_t; + int16_t diff = target-cur_t; + cnt_temp_rise++; 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) + + heaterPID.Compute(); + if (error || off) pwm = 0; else - pwm = max(0, min(255, diff*CTRL_GAIN)); + pwm = min(255,pid_val*255); + //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)); + digitalWrite(HEAT_LED, cur_t+5 < target || (abs((int16_t)cur_t-(int16_t)target) <= 5 && (millis()/(stby?1000:500))%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(); + 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?1:0); + 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(battery_voltage); + Serial.print(";"); + Serial.print(battery_voltage); + Serial.print(";"); + Serial.println(battery_voltage); + Serial.flush(); + } delay(DELAY_MAIN_LOOP); } diff --git a/Maiskolben_TFT/Maiskolben_TFT.ino b/Maiskolben_TFT/Maiskolben_TFT.ino new file mode 100644 index 0000000..4cc4d17 --- /dev/null +++ b/Maiskolben_TFT/Maiskolben_TFT.ino @@ -0,0 +1,505 @@ +#include +#include +#include +#include +#include +#include "TimerOne.h" +#include "definitions.h" + +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; +boolean wasOff = true; + +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() { + 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); + delay(100); + tft.initR(INITR_BLACKTAB); + tft.fillScreen(ST7735_BLACK); + tft.setRotation(1); + 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); + + //PWM Prescaler = 1024 + TCCR2B = TCCR2B & (0b11111000 | 7); + 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); + + for (uint8_t i = 0; i < 50; i++) + measureVoltage(); //measure average 50 times to get realistic results + + Serial.begin(115200); + //Serial.println("DIFF SUM;SET TEMPERATURE;IS TEMPERATURE;DUTY CYCLE;VOLTAGE"); + delay(3000); + tft.fillScreen(ST7735_BLACK); + last_measured = getTemperature(); + Timer1.initialize(1000); + Timer1.attachInterrupt(timer_isr); + heaterPID.SetMode(AUTOMATIC); + sendNext = millis(); +} + +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); +} + +int getTemperature() { + 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(((float) adc)*ADC_TO_TEMP_GAIN+ADC_TO_TEMP_OFFSET); //apply linear conversion to actual temperature +} + +void measureVoltage() { + 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; +} + +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() { + 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(); + EEPROM.update(1, stby); +} + +void setOff(boolean state) { + if (state == off) return; + if (!state) + analogWrite(HEATER_PWM, 0); + if (power_source == POWER_USB && !state) { + state = true; //don't switch on, if powered via USB + setError(USB_ONLY); + } + off = state; + wasOff = true; + last_measured = cur_t; +} + +void display() { + 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(54,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) { + set_t_old = set_t; + tft.setTextColor(ST7735_WHITE, ST7735_BLACK); + tft.setCursor(54,26); + tft.setTextSize(3); + 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 && !stby) { + uint16_t tout = min(max(0,(last_temperature_drop + STANDBY_TIMEOUT - (millis()+500)/1000)), STANDBY_TIMEOUT); + tft.setTextColor(ST7735_YELLOW, ST7735_BLACK); + tft.setTextSize(2); + tft.setCursor(58,78); + tft.print(tout/60); + tft.write(':'); + if (tout%60 < 10) tft.write('0'); + tft.print(tout%60); + } else if (temperature != 999) { + tft.fillRect(54, 78, 60, 20, ST7735_BLACK); + } + } + if (cur_t_old != temperature) { + tft.setCursor(54,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_GREEN : 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 < 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); + } + } + } + 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); + } + blink = !blink; +} + +void compute() { + 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() { + 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() { + 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?1:0); + 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(); + } + delay(DELAY_MAIN_LOOP); +} diff --git a/Maiskolben_TFT/definitions.h b/Maiskolben_TFT/definitions.h new file mode 100644 index 0000000..17512b3 --- /dev/null +++ b/Maiskolben_TFT/definitions.h @@ -0,0 +1,149 @@ +#define VERSION 103 +#define EEPROM_CHECK 42 +//#define HAS_BATTERY + +#define STBY_TEMP 150 +//SOFTWARE CAN'T MEASURE MORE THAN 422 DUE TO RESISTOR CONFIGURATION, 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 + +#define STANDBY_TIMEOUT 240 // seconds without any significant temperature drop, if exceeded it will standby + +#define TEMP_THRESHOLD 50 //threshold voltage, that must be exceeded in given time: +#define TEMP_UNDER_THRESHOLD 150 //*10ms +#define THRES_MAX_DECEED 2 //max times the threshold temperature may be undercut by the current temperature + +//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 SW_STBY 2 +#define HEATER_PWM 3 +#define SW_DOWN 4 +#define HEAT_LED 5 +#define SW_UP 6 +#define SW_T3 7 +#define SW_T2 8 +#define SW_T1 9 +#define TFT_CS 10 +#define TEMP_SENSE A0 +#define STBY_NO A1 +#define BAT_C3 A2 +#define BAT_C2 A3 +#define BAT_C1 A4 +#define TFT_DC A5 + +#define kp 0.03 +#define ki 0.00001 +#define kd 0.0 + +#define TIME_COMPUTE_IN_MS 10 +#define TIME_MEASURE_VOLTAGE_IN_MS 200 +#define TIME_SW_POLL_IN_MS 10 +#define DELAY_BEFORE_MEASURE 10 +#define DELAY_MAIN_LOOP 10 +#define PID_SAMPLE_TIME 10 + +#define ADC_TO_TEMP_GAIN 0.39 +#define ADC_TO_TEMP_OFFSET 23.9 +#define CTRL_GAIN 10 + +typedef enum POWER_SOURCE { + NO_INIT, + POWER_USB, + POWER_CORD, + POWER_LIPO +} p_source; +typedef enum ERROR_TYPE { + NO_ERROR, + EXCESSIVE_FALL, + NOT_HEATING, + NO_TIP, + BATTERY_LOW, + USB_ONLY +} error_type; + +const unsigned char maiskolben [] PROGMEM = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xe0, 0x60, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xcf, 0xff, 0xa0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0xdc, 0xf7, +0x18, 0x47, 0x89, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, +0x26, 0x78, 0xc6, 0x33, 0x9c, 0x7f, 0x0c, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x08, 0x23, 0x49, 0xe6, 0x1b, 0xff, 0xf8, 0x39, 0xd0, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xc5, 0x31, 0xcf, 0xe7, 0x3f, 0xfe, 0x14, 0x21, 0x08, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x47, 0xf9, 0xff, 0xff, 0xcc, +0x40, 0x08, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x4e, +0x0f, 0xc1, 0x08, 0x04, 0x63, 0x00, 0x21, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x47, 0xe0, 0x0c, 0x01, 0x08, 0x07, 0xaf, 0x80, 0x60, 0x84, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x02, 0x0c, 0x01, 0x8c, 0xc7, 0xa4, 0x80, 0xe0, 0xf4, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x0c, 0x60, 0x9f, 0xc7, +0xf0, 0xf3, 0x33, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x83, +0x0e, 0xe0, 0x77, 0x69, 0x9a, 0xf7, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x0c, 0xfd, 0xb7, 0xb6, 0x67, 0x79, 0x9f, 0xfe, 0xee, 0x12, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xfc, 0xf3, 0xbe, 0xff, 0xfb, 0xff, 0xf8, 0x46, 0x12, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xcd, 0xcf, 0xbc, 0x63, 0x18, 0x6e, +0x21, 0x80, 0x42, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x8c, 0xfe, +0x08, 0x42, 0x08, 0x44, 0x21, 0x48, 0x42, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x06, 0x84, 0xe2, 0x1e, 0x4a, 0x18, 0x44, 0xe1, 0xc0, 0x63, 0x0f, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x63, 0x3c, 0x7b, 0x18, 0x6e, 0x60, 0x80, 0x31, 0x07, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x65, 0xec, 0x71, 0x08, 0x22, +0x10, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x39, +0x84, 0x21, 0x8c, 0x33, 0x10, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x08, 0x38, 0x84, 0x00, 0x08, 0x32, 0x10, 0x02, 0x20, 0x06, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x58, 0x10, 0x84, 0x00, 0x08, 0x20, 0x10, 0x03, 0xf1, 0x9f, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x70, 0x44, 0x60, 0x08, 0x23, +0x30, 0xc4, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, +0xc7, 0x1f, 0xff, 0xff, 0x1f, 0x84, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x08, 0xc0, 0x7f, 0x82, 0x10, 0x8c, 0x03, 0x08, 0x84, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x78, 0x82, 0x10, 0x88, 0x01, 0x08, 0x84, 0x10, 0x87, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7c, 0x28, 0x82, 0x18, 0xc8, 0x21, +0x08, 0x82, 0x10, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfc, 0x28, +0x42, 0x08, 0xc0, 0x21, 0x8c, 0x42, 0x10, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf0, 0x00, +0x00, 0x3f, 0xfc, 0x18, 0x42, 0x08, 0xc4, 0x11, 0x8c, 0x42, 0x10, 0x83, 0x80, 0x00, 0x00, 0x00, +0x7f, 0xff, 0xfe, 0x89, 0xfc, 0xff, 0x94, 0x0c, 0x42, 0x08, 0x44, 0x30, 0x88, 0x42, 0x10, 0x03, +0x18, 0x00, 0x00, 0x00, 0x06, 0xff, 0xff, 0xff, 0xff, 0x00, 0x04, 0x0c, 0x62, 0x00, 0x04, 0x00, +0x88, 0x42, 0x10, 0x03, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x04, 0x1c, +0x20, 0x00, 0x88, 0x00, 0x88, 0x42, 0x10, 0xc7, 0x06, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x07, 0x80, 0x1c, 0x23, 0x10, 0xc8, 0x00, 0x8c, 0x43, 0x30, 0xf9, 0x01, 0xf0, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x37, 0x9d, 0xbc, 0x30, 0xfe, 0xe7, 0x6f, 0x11, +0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0x04, 0x4c, 0xe7, 0x9e, 0x2f, +0x31, 0x8e, 0x64, 0x31, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xce, 0x23, +0x8f, 0xe7, 0x1d, 0xc7, 0x31, 0x8c, 0x64, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x02, 0x46, 0x30, 0xcf, 0xf6, 0x1c, 0xe6, 0x39, 0xcc, 0x64, 0x11, 0x00, 0x06, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x44, 0x38, 0xcc, 0x76, 0x18, 0xc2, 0x3d, 0x2c, 0x6c, 0x13, +0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x38, 0xcc, 0x66, 0x10, 0x86, +0x31, 0x08, 0x48, 0x13, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x38, +0xc0, 0x44, 0x10, 0x84, 0x21, 0x00, 0x48, 0x12, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x40, 0x30, 0x80, 0x44, 0x01, 0x04, 0x20, 0x00, 0x48, 0x1e, 0x00, 0x01, 0x80, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x84, 0x44, 0x21, 0x04, 0x20, 0x0c, 0xfc, 0x26, +0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x86, 0xcc, 0x21, 0x04, +0x23, 0x0f, 0x8e, 0xc2, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, +0x8e, 0xff, 0xff, 0x0f, 0xfb, 0xf9, 0x84, 0x46, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x80, 0x71, 0xf0, 0x6c, 0x31, 0xc4, 0x40, 0x19, 0x00, 0x46, 0x00, 0x00, 0x30, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb8, 0x30, 0x4c, 0x21, 0x04, 0x40, 0x11, 0x00, 0x46, +0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x20, 0x20, 0x48, 0x20, 0x00, +0x02, 0x11, 0x08, 0x44, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x28, +0x20, 0x08, 0x02, 0x00, 0x80, 0x11, 0x08, 0x4c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x22, 0x18, 0x20, 0x00, 0x02, 0x00, 0x80, 0x13, 0x1c, 0xd8, 0x00, 0x00, 0x03, 0x40, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x18, 0x20, 0x00, 0x42, 0x08, 0x84, 0x1d, 0xe6, 0x38, +0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x18, 0x21, 0x1c, 0x7e, 0x0e, +0x7f, 0x90, 0x44, 0x38, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x7c, +0x7c, 0xcc, 0x31, 0x88, 0x42, 0x10, 0x44, 0x38, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x1e, 0x42, 0x10, 0x08, 0x21, 0x08, 0x42, 0x10, 0x84, 0x78, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x40, 0x00, 0x08, 0x20, 0x08, 0x62, 0x10, 0x8c, 0x70, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x08, +0xc6, 0x21, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, +0x60, 0x08, 0x44, 0x3f, 0xff, 0xe7, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/README.md b/README.md index 64c3662..7cb0335 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,122 @@ # Maiskolben -SMD Solderingstation + +## SMD Lötstation, kompatibel zu Weller-Lötspitzen + +Weller baut seit Jahren *die* Lötstationen für den professionellen Bereich. +Die WMRP/WXRP-Stationen heben sich durch ihre aktiven Lötspitzen von anderen Produkten ab. +Mit einer außergewöhnlich kurzen Aufheizzeit im Sekundenbereich, einer direkt in der Spitze integrierten Temperaturmessung und dementsprechend schnelle Nachregulierung in Sekundenbruchteilen sind die Spitzen in der Industrie äußerst beliebt. +Die Spitzen selbst sind für 40€ im Handel erhältlich, die original Weller-Station kostet an die 600€. +Für den einfachen Maker oder Hobbyist auf keinen Fall ein Produkt, das beim Onlineshopping ohne weitere Überlegungen in den Warenkorb wandert. +Der *Maiskolben* hingegen ist eine erschwingliche Lösung für lediglich 40€, mit der die Spitzen ebenso angesteuert werden können. + +* Betrieb mit 12V Netzteil oder 3s LiPo (Modellbau-Akku) +* Spannungsüberwachung (bei Akku für alle 3 Zellen und automatische Abschaltung bei zu geringer Ladung) +* Einfaches Wechseln von Spitzen mittels Klinken-Buchse/-Kabel +* 3 individuelle Temperatur-Presets +* Temperaturbereich von 100-400°C +* Gut ablesbares Farbdisplay mit allen nötigen Informationen +* Auto-Standby (entweder sensor- oder zeitgesteuert auf 150°C) +* Auto-Wakeup (entweder sensorgesteuert oder durch berühren von Lötschwamm, Messingwolle oder Lötstelle) +* USB-Schnittstelle zur Überwachung oder Steuerung mittels PC +* Potentialausgleich-/ESD-Buchse (über 1MΩ an GND) +* Optisches Feedback der Heizleistung durch LED +* Open-Source-Software auf Basis von Arduino +* Software-Update kann per USB eingespielt werden + +## Stromzufuhr + +Die Stromzufuhr wird wahlweise über die Hohlstecker-Buchse (10-18V) *ODER* die JST-XH4 (11,1V) Buchse angeschlossen. +Niemals beide gleichzeitig anschließen! +Der Lötkolben (Weller RT-XX, z.B. RT-1 für feine Lötarbeiten) wird über die Klinkenbuchse mit einer Verlängerung angesteckt. + +## Display + +Das Display zeigt mittig in weißer Schrift die Soll-Temperatur, darunter, sofern eine Spitze eingesteckt ist, die aktuelle Ist-Temperatur. +Bei eingeschaltetem Lötkolben erscheint zusätzlich noch die Zeit, nach der sich der Lötkolben automatisch in den Standby versetzt. +Der Balken am oberen Bildschirmrand spiegelt die Ist-Temperatur wieder. +Oben Links wird die Spannung bzw. die Ladezustände der einzelnen Zellen angezeigt. +Links wird durch das Power-Symbol der aktuelle Zustand (aus = rot, Standby = gelb, an = grün) signalisiert. +Am unteren Rand sind die drei Presets zu sehen. + +## Bedienung + +Nach dem Anstecken von Netzteil und Lötspitze kann die Station durch halten der Power-Taste (unten rechts) eingeschaltet werden. +Basierend auf der letzten Einstellung wird die angezeigte Soll- (Power-Symbol grün) oder die Standby-Temperatur (Symbol gelb) angefahren. +Mittels der Tasten rechts vom Display (Pfeile) lässt sich die Soll-Temperatur nach oben und unten korrigieren. +Sollte einer der Pfeile auf dem Display ausgegraut sein, so lässt sich über diese Temperatur hinaus keine weitere Änderung mehr vornehmen (100 ≥ T ≥ 400). +Mittels der Preset-Tasten unter dem Bildschirm lassen sich voreingestellte Temperaturen als Soll-Temperatur setzen und die Station gleichzeitig aus dem Standby aufwecken. +Durch gedrückthalten einer der drei Tasten wird der dort gespeicherte Wert durch die aktuelle Soll-Temperatur überschrieben. +Wird die Power-Taste nur kurz gedrückt, so wird der Standby-Zustand umgeschaltet. + +## Fehlermeldungen + +#### USB power only/Connect power supply; Power too low/Connect power >5V +Die Lötstation wird entweder nur über USB oder eine Spannungsquelle mit knapp 5V versorgt. +Zum Betrieb wird ein adäquates Netzteil oder Akku benötigt (≥40W) + +#### NO TIP; No tip connected/Tip slipped out? +Es wurde keine Spitze eingesteckt oder nicht erkannt. +Es kann sein, dass die Spitze defekt ist oder nicht richtig in der Buchse steckt. + +#### Temperature dropped/Tip slipped out? +Es wurde ein zu großer Temperaturabfall bemerkt und die Wahrscheinlichkeit besteht, dass die Spitze leicht aus der Buchse herausgerutscht ist. +Durch das Herausrutschen kann eine Verbindung zwischen Masse und dem Sensor hergestellt werden, sodass die erkannte Temperatur beim Offset von 24°C liegt. + +#### Not heating/Weak power source or short +Entweder kann die Stromversorgung nicht genug Leistung bringen, um die Spitze in einem angemessenen Zeitraum aufzuheizen oder es liegt wie bei obigem Fehler ein Kurzschluss von Masse und Sensor vor. + +#### Battery low/Replace or charge +Der Akku hat auf mindestens einer Zelle eine zu geringe Spannung und sollte ersetzt oder geladen werden. + +Fehlermeldungen müssen durch OK bestätigt werden. +Die Fehlermeldungen können nicht unterdrückt werden, sollte allerdings mehrfach hintereinander eine Meldung bestätigt und die Station wieder eingeschaltet werden, kann dies zu übermäßigem Erwärmen der Lötspitze führen. + +## Software-Update + +Um die aktuellste Software auf den Maiskolbe zu übertragen, muss zunächst die [Arduino-IDE](https://www.arduino.cc/en/Main/Software) heruntergeladen werden. +In diesem Repository befinden sich zwei Programme, zum einen Maiskolben_LCD für die Hardware-Revision 1.0 und Maiskolben_TFT für Hardware-Revisionen ab 1.5. +Die .ino Datei kann mit Arduino geöffnet werden. +Der Serielle-Port muss nach Herstellen einer Verbindung zwischen Maiskolben und PC unter _Werkzeuge_ > _Port_ ausgewählt werden. +Unter umständen muss zuvor noch der [Treiber](http://www.wch.cn/download/CH341SER_ZIP.html) für den USB-Seriell-Wandler installiert werden. +Als _Board_ sollte _Arduino Nano_ mit _Prozessor_: _ATmega328_ ausgewählt werden. +Über _Sketch_ > _Hochladen_ bzw. den Pfeil nach Rechts in der oberen Leiste kann das Programm übertragen werden. + +Unter umständen müssen vorher folgende Libraries noch über _Sketch_ > _Bibliothek einbinden_ > _.ZIP Bibliothek hinzufügen..._ eingebunden werden: + +* [TimerOne](https://github.com/PaulStoffregen/TimerOne) +* [PID_v1](https://github.com/br3ttb/Arduino-PID-Library/) +* [Adafruit_ST7735](https://github.com/adafruit/Adafruit-ST7735-Library) +* [Adafruit_GFX](https://github.com/adafruit/Adafruit-GFX-Library) + +### Changelog + +* 2.0 Initiale Version für GitHub (TFT) + * Auto-Standby hinzugefügt + * Parameter angepasst + +### Achtung! + +Zur eigenen Sicherheit vor einem Software-Update die Lötspitze entfernen. +Zwar wird die Heizspannung beim Übertragen deaktiviert, dennoch kann es vereinzelt zu Fehlfunktionen und dadurch bedingtem unbeschränkten Aufheizen der Spitze kommen. + +Modifikationen an der Software sind auf eigene Gefahr des Nutzers. + +## Umbauten + +Nachfolgend werden Modifikationen beschrieben, die die Funktionen des Maiskolben erweitern. +Für eigens durchgeführte Änderungen kann keine Haftung übernommen werden. + +### Ablage-Sensor nachrüsten + +Statt des automatischen Standbys nach einer bestimmten Zeit kann auch der Standby durch einen Ablage-Sensor erzwungen werden. +Auf der Platine befinden sich Kontakte mit STBY, 5V und GND. +Um den Standby zu aktivieren muss STBY mit GND verbunden werden, um wieder aufzuwachen muss die Verbindung getrennt werden. +Das Schalten von STBY nach GND kann entweder durch einen Schalter erfolgen, der beim auflegen der Spitze auslöst, alternativ mit einem Hall- oder Reed-Sensor. +Für letzteres muss an dem Ende des Klinken-Kabels, an dem die Spitze eingesteckt wird, ein Magnet angebracht werden, der den Sensor dann auslöst. + +### Stromversorgungs-Schalter + +Ab Hardware-Revision 1.5 gibt es auf der Platinenunterseite eine Lötbrücke mit der Beschriftung ON/OFF. +Bis einschließlich Revision 1.9 wird beim Durchtrennen dieser Brücke die gesamte Stromversorgung unterbrochen. +Ab Revision 1.11 wird hingegen nur noch die Zuleitung des LiPos getrennt. +An den beiden Lötpunkten können nach dem Durchtrennen der Leiterbahn Kabel zu einem Schalter gelötet werden, mit dem sich die Stromversorgung unterbrechen lässt. \ No newline at end of file