├── BOM_SolderingStation_2021-04-13.csv ├── Gerber_SolderingStation.zip ├── PickAndPlace_SolderingStation_2021-04-13.csv ├── Readme.md ├── Schematic_t12dev_2021-04-16.pdf ├── SolderStaionShortNob.stl ├── SolderingStationCase.stl └── T12SoldingStation.ino /BOM_SolderingStation_2021-04-13.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drzzzf/T12SoldingStation/31edcbac3a0bbfa1a47944d8b4a6aecc9d18bc61/BOM_SolderingStation_2021-04-13.csv -------------------------------------------------------------------------------- /Gerber_SolderingStation.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drzzzf/T12SoldingStation/31edcbac3a0bbfa1a47944d8b4a6aecc9d18bc61/Gerber_SolderingStation.zip -------------------------------------------------------------------------------- /PickAndPlace_SolderingStation_2021-04-13.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drzzzf/T12SoldingStation/31edcbac3a0bbfa1a47944d8b4a6aecc9d18bc61/PickAndPlace_SolderingStation_2021-04-13.csv -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 我是羊羹爸爸。 2 | 本烙铁的原始设计完全来自以下这位大神的开源项目。 3 | 其中有完整的解说文档,如果你看了我的视频也想制作这个烙铁,可以先访以下地址 4 | https://github.com/wagiminator/ATmega-Soldering-Station 5 | 6 | 我的代码是基于原版代码1.7版本修改的,所以使用的类库和编译环境请参考原始网站的说明。 7 | pcb是基于原版2.7修改的,只是加入了一个传感器,对原始电路并没有改动,所以应该可以兼容原始项目的程序。 8 | 我的这把烙铁的diy视频,等发布以后,会把链接更新到这里。 9 | -------------------------------------------------------------------------------- /Schematic_t12dev_2021-04-16.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drzzzf/T12SoldingStation/31edcbac3a0bbfa1a47944d8b4a6aecc9d18bc61/Schematic_t12dev_2021-04-16.pdf -------------------------------------------------------------------------------- /SolderStaionShortNob.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drzzzf/T12SoldingStation/31edcbac3a0bbfa1a47944d8b4a6aecc9d18bc61/SolderStaionShortNob.stl -------------------------------------------------------------------------------- /SolderingStationCase.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drzzzf/T12SoldingStation/31edcbac3a0bbfa1a47944d8b4a6aecc9d18bc61/SolderingStationCase.stl -------------------------------------------------------------------------------- /T12SoldingStation.ino: -------------------------------------------------------------------------------- 1 | // SolderingStation2 2 | // 3 | // ATmega328-controlled Soldering Station for Hakko T12 Tips. 4 | // 5 | // This version of the code implements: 6 | // - Temperature measurement of the tip 7 | // - Direct or PID control of the heater 8 | // - Temperature control via rotary encoder 9 | // - Boost mode by short pressing rotary encoder switch 10 | // - Setup menu by long pressing rotary encoder switch 11 | // - Handle movement detection (by checking ball switch) 12 | // - Iron unconnected detection (by idenfying invalid temperature readings) 13 | // - Time driven sleep/power off mode if iron is unused (movement detection) 14 | // - Measurement of input voltage, Vcc and ATmega's internal temperature 15 | // - Information display on OLED 16 | // - Buzzer 17 | // - Calibrating and managing different soldering tips 18 | // - Storing user settings into the EEPROM 19 | // - Tip change detection 20 | // 21 | // Power supply should be in the range of 16V/2A to 24V/3A and well 22 | // stabilized. 23 | // 24 | // For calibration you need a soldering iron tips thermometer. For best results 25 | // wait at least three minutes after switching on the soldering station before 26 | // you start the calibration process. 27 | // 28 | // Controller: ATmega328p 29 | // Core: Barebones ATmega (https://github.com/carlosefr/atmega) 30 | // Clockspeed: 16 MHz external 31 | // 32 | // It is recommended not to use a bootloader! 33 | // 34 | // 2019/2020 by Stefan Wagner with great support from John Glavinos 35 | // Project Page: https://easyeda.com/wagiminator 36 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 37 | 38 | 39 | 40 | // Libraries 41 | #include // https://github.com/olikraus/u8glib 42 | #include // https://github.com/mblythe86/C-PID-Library/tree/master/PID_v1 43 | #include // for storing user settings into EEPROM 44 | #include // for sleeping during ADC sampling 45 | 46 | // Firmware version 47 | #define VERSION "v1.71" 48 | 49 | // Type of rotary encoder 50 | #define ROTARY_TYPE 1 // 0: 2 increments/step; 1: 4 increments/step (default) 51 | 52 | // Pins 53 | #define SENSOR_PIN A0 // tip temperature sense 54 | #define VIN_PIN A1 // input voltage sense 55 | #define ONSTAND_PIN A6 // handle on stand sensor 56 | #define BUZZER_PIN 5 // buzzer 57 | #define BUTTON_PIN 6 // rotary encoder switch 58 | #define ROTARY_1_PIN 7 // rotary encoder 1 59 | #define ROTARY_2_PIN 8 // rotary encoder 2 60 | #define CONTROL_PIN 9 // heater MOSFET PWM control 61 | #define SWITCH_PIN 10 // handle vibration switch 62 | 63 | // Default temperature control values (recommended soldering temperature: 300-380°C) 64 | #define TEMP_MIN 150 // min selectable temperature 65 | #define TEMP_MAX 400 // max selectable temperature 66 | #define TEMP_DEFAULT 320 // default start setpoint 67 | #define TEMP_SLEEP 150 // temperature in sleep mode 68 | #define TEMP_BOOST 50 // temperature increase in boost mode 69 | #define TEMP_STEP 10 // rotary encoder temp change steps 70 | 71 | // Default tip temperature calibration values 72 | #define TEMP200 258 // temperature at ADC = 200 73 | #define TEMP280 357 // temperature at ADC = 280 74 | #define TEMP360 470 // temperature at ADC = 360 75 | #define TEMPCHP 30 // chip temperature while calibration 76 | #define TIPMAX 8 // max number of tips 77 | #define TIPNAMELENGTH 6 // max length of tip names (including termination) 78 | #define TIPNAME "HAKKO" // default tip name 79 | 80 | // Default timer values (0 = disabled) 81 | #define TIME2SLEEP 5 // time to enter sleep mode in minutes 82 | #define TIME2OFF 15 // time to shut off heater in minutes 83 | #define TIMEOFBOOST 40 // time to stay in boost mode in seconds 84 | 85 | // Control values 86 | #define TIME2SETTLE 950 // time in microseconds to allow OpAmp output to settle 87 | #define SMOOTHIE 0.05 // OpAmp output smooth factor (1=no smoothing; 0.05 default) 88 | #define PID_ENABLE true // enable PID control 89 | #define BEEP_ENABLE true // enable/disable buzzer 90 | #define MAINSCREEN 0 // type of main screen (0: big numbers; 1: more infos) 91 | #define DEFAULT_DISTANCE 10 92 | 93 | // EEPROM identifier 94 | #define EEPROM_IDENT 0xE76C // to identify if EEPROM was written by this program 95 | 96 | // Define the aggressive and conservative PID tuning parameters 97 | double aggKp=11, aggKi=0.5, aggKd=1; 98 | double consKp=11, consKi=3, consKd=5; 99 | 100 | // Default values that can be changed by the user and stored in the EEPROM 101 | uint16_t DefaultTemp = TEMP_DEFAULT; 102 | uint16_t SleepTemp = TEMP_SLEEP; 103 | uint8_t BoostTemp = TEMP_BOOST; 104 | uint8_t time2sleep = TIME2SLEEP; 105 | uint8_t time2off = TIME2OFF; 106 | uint8_t timeOfBoost = TIMEOFBOOST; 107 | uint8_t MainScrType = MAINSCREEN; 108 | bool PIDenable = PID_ENABLE; 109 | bool beepEnable = BEEP_ENABLE; 110 | uint16_t DefaultDistance = DEFAULT_DISTANCE; 111 | 112 | // Default values for tips 113 | uint16_t CalTemp[TIPMAX][4] = {TEMP200, TEMP280, TEMP360, TEMPCHP}; 114 | char TipName[TIPMAX][TIPNAMELENGTH] = {TIPNAME}; 115 | uint8_t CurrentTip = 0; 116 | uint8_t NumberOfTips = 1; 117 | 118 | // Menu items 119 | const char *SetupItems[] = { "Setup Menu", "Tip Settings", "Temp Settings", 120 | "Timer Settings", "Control Type", "Main Screen", 121 | "Buzzer", "Information", "Sleep Senor Setting", "Return" }; 122 | const char *TipItems[] = { "Tip:", "Change Tip", "Calibrate Tip", 123 | "Rename Tip", "Delete Tip", "Add new Tip", "Return" }; 124 | const char *TempItems[] = { "Temp Settings", "Default Temp", "Sleep Temp", 125 | "Boost Temp", "Return" }; 126 | const char *TimerItems[] = { "Timer Settings", "Sleep Timer", "Off Timer", 127 | "Boost Timer", "Return" }; 128 | const char *ControlTypeItems[] = { "Control Type", "Direct", "PID" }; 129 | const char *MainScreenItems[] = { "Main Screen", "Big Numbers", "More Infos" }; 130 | const char *StoreItems[] = { "Store Settings ?", "No", "Yes" }; 131 | const char *SureItems[] = { "Are you sure ?", "No", "Yes" }; 132 | const char *BuzzerItems[] = { "Buzzer", "Disable", "Enable" }; 133 | const char *DefaultTempItems[] = { "Default Temp", "deg C" }; 134 | const char *SleepTempItems[] = { "Sleep Temp", "deg C" }; 135 | const char *BoostTempItems[] = { "Boost Temp", "deg C" }; 136 | const char *SleepTimerItems[] = { "Sleep Timer", "Minutes" }; 137 | const char *OffTimerItems[] = { "Off Timer", "Minutes" }; 138 | const char *BoostTimerItems[] = { "Boost Timer", "Seconds" }; 139 | const char *DeleteMessage[] = { "Warning", "You cannot", "delete your", "last tip!" }; 140 | const char *MaxTipMessage[] = { "Warning", "You reached", "maximum number", "of tips!" }; 141 | const char *DistanceItems[] = { "Set Distance", "dist" }; 142 | 143 | // Variables for pin change interrupt 144 | volatile uint8_t a0, b0, c0, d0; 145 | volatile bool ab0; 146 | volatile int count, countMin, countMax, countStep; 147 | volatile bool handleMoved; 148 | 149 | // Variables for temperature control 150 | uint16_t SetTemp, ShowTemp, gap, Step, HandleDistance; 151 | double Input, Output, Setpoint, RawTemp, CurrentTemp, ChipTemp; 152 | 153 | // Variables for voltage readings 154 | uint16_t Vcc, Vin; 155 | 156 | // State variables 157 | bool inSleepMode = false; 158 | bool inOffMode = false; 159 | bool inBoostMode = false; 160 | bool inCalibMode = false; 161 | bool isWorky = true; 162 | bool beepIfWorky = true; 163 | bool TipIsPresent= true; 164 | bool isOnStand = true; 165 | 166 | // Timing variables 167 | uint32_t sleepmillis; 168 | uint32_t boostmillis; 169 | uint32_t buttonmillis; 170 | uint8_t goneMinutes; 171 | uint8_t goneSeconds; 172 | uint8_t SensorCounter = 255; 173 | 174 | // Specify variable pointers and initial PID tuning parameters 175 | PID ctrl(&Input, &Output, &Setpoint, aggKp, aggKi, aggKd, REVERSE); 176 | 177 | // Setup u8g object: uncomment according to the OLED used 178 | U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST); 179 | //U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_FAST|U8G_I2C_OPT_NO_ACK); 180 | 181 | 182 | 183 | void setup() { 184 | // set the pin modes 185 | pinMode(SENSOR_PIN, INPUT); 186 | pinMode(VIN_PIN, INPUT); 187 | pinMode(ONSTAND_PIN, INPUT); 188 | pinMode(BUZZER_PIN, OUTPUT); 189 | pinMode(CONTROL_PIN, OUTPUT); 190 | pinMode(ROTARY_1_PIN, INPUT_PULLUP); 191 | pinMode(ROTARY_2_PIN, INPUT_PULLUP); 192 | pinMode(BUTTON_PIN, INPUT_PULLUP); 193 | pinMode(SWITCH_PIN, INPUT_PULLUP); 194 | 195 | analogWrite(CONTROL_PIN, 255); // this shuts off the heater 196 | digitalWrite(BUZZER_PIN, LOW); // must be LOW when buzzer not in use 197 | 198 | // setup ADC 199 | ADCSRA |= bit (ADPS0) | bit (ADPS1) | bit (ADPS2); // set ADC prescaler to 128 200 | ADCSRA |= bit (ADIE); // enable ADC interrupt 201 | interrupts (); // enable global interrupts 202 | 203 | // setup pin change interrupt for rotary encoder 204 | PCMSK0 = bit (PCINT0); // Configure pin change interrupt on Pin8 205 | PCICR = bit (PCIE0); // Enable pin change interrupt 206 | PCIFR = bit (PCIF0); // Clear interrupt flag 207 | 208 | // prepare and start OLED 209 | if ( u8g.getMode() == U8G_MODE_R3G3B2 ) u8g.setColorIndex(255); 210 | else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) u8g.setColorIndex(3); 211 | else if ( u8g.getMode() == U8G_MODE_BW ) u8g.setColorIndex(1); 212 | else if ( u8g.getMode() == U8G_MODE_HICOLOR ) u8g.setHiColorByRGB(255,255,255); 213 | 214 | // Rotate 180 degrees by drz 215 | u8g.setRot180(); 216 | 217 | // get default values from EEPROM 218 | getEEPROM(); 219 | 220 | // read supply voltages in mV 221 | Vcc = getVCC(); Vin = getVIN(); 222 | 223 | // read and set current iron temperature 224 | SetTemp = DefaultTemp; 225 | RawTemp = denoiseAnalog(SENSOR_PIN); 226 | ChipTemp = getChipTemp(); 227 | calculateTemp(); 228 | 229 | // turn on heater if iron temperature is well below setpoint 230 | if ((CurrentTemp + 20) < DefaultTemp) analogWrite(CONTROL_PIN, 0); 231 | 232 | // set PID output range and start the PID 233 | ctrl.SetOutputLimits(0, 255); 234 | ctrl.SetMode(AUTOMATIC); 235 | 236 | // set initial rotary encoder values 237 | a0 = PINB & 1; b0 = PIND>>7 & 1; ab0 = (a0 == b0); 238 | setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, DefaultTemp); 239 | 240 | // reset sleep timer 241 | sleepmillis = millis(); 242 | 243 | // long beep for setup completion 244 | beep(); beep(); 245 | } 246 | 247 | 248 | void loop() { 249 | ROTARYCheck(); // check rotary encoder (temp/boost setting, enter setup menu) 250 | SLEEPCheck(); // check and activate/deactivate sleep modes 251 | SENSORCheck(); // reads temperature and vibration switch of the iron 252 | Thermostat(); // heater control 253 | MainScreen(); // updates the main page on the OLED 254 | } 255 | 256 | 257 | 258 | 259 | // check rotary encoder; set temperature, toggle boost mode, enter setup menu accordingly 260 | void ROTARYCheck() { 261 | // set working temperature according to rotary encoder value 262 | SetTemp = getRotary(); 263 | 264 | // check rotary encoder switch 265 | uint8_t c = digitalRead(BUTTON_PIN); 266 | if ( !c && c0 ) { 267 | beep(); 268 | buttonmillis = millis(); 269 | while( (!digitalRead(BUTTON_PIN)) && ((millis() - buttonmillis) < 500) ); 270 | if ((millis() - buttonmillis) >= 500) SetupScreen(); 271 | else { 272 | inBoostMode = !inBoostMode; 273 | if (inBoostMode) boostmillis = millis(); 274 | handleMoved = true; 275 | } 276 | } 277 | c0 = c; 278 | 279 | // check timer when in boost mode 280 | if (inBoostMode && timeOfBoost) { 281 | goneSeconds = (millis() - boostmillis) / 1000; 282 | if (goneSeconds >= timeOfBoost) { 283 | inBoostMode = false; // stop boost mode 284 | beep(); // beep if boost mode is over 285 | beepIfWorky = true; // beep again when working temperature is reached 286 | } 287 | } 288 | } 289 | 290 | 291 | // check and activate/deactivate sleep modes 292 | void SLEEPCheck() { 293 | if (handleMoved && !isOnStand) { // if handle was moved 294 | if (inSleepMode) { // in sleep or off mode? 295 | if ((CurrentTemp + 20) < SetTemp) // if temp is well below setpoint 296 | analogWrite(CONTROL_PIN, 0); // then start the heater right now 297 | beep(); // beep on wake-up 298 | beepIfWorky = true; // beep again when working temperature is reached 299 | } 300 | handleMoved = false; // reset handleMoved flag 301 | inSleepMode = false; // reset sleep flag 302 | inOffMode = false; // reset off flag 303 | sleepmillis = millis(); // reset sleep timer 304 | } 305 | 306 | // check time passed since the handle was moved 307 | goneMinutes = (millis() - sleepmillis) / 60000; 308 | if ( (!inSleepMode) && (((time2sleep > 0) && (goneMinutes >= time2sleep)) || isOnStand)) {inSleepMode = true; beep();} 309 | if ( (!inOffMode) && (time2off > 0) && (goneMinutes >= time2off ) ) {inOffMode = true; beep();} 310 | } 311 | 312 | 313 | // reads temperature, vibration switch and supply voltages 314 | void SENSORCheck() { 315 | analogWrite(CONTROL_PIN, 255); // shut off heater in order to measure temperature 316 | delayMicroseconds(TIME2SETTLE); // wait for voltage to settle 317 | 318 | double temp = denoiseAnalog(SENSOR_PIN); // read ADC value for temperature 319 | uint8_t d = digitalRead(SWITCH_PIN); // check handle vibration switch 320 | if (d != d0) {handleMoved = true; d0 = d;} // set flag if handle was moved 321 | if (! SensorCounter--) Vin = getVIN(); // get Vin every now and then 322 | HandleDistance = getHandleDistance() ;// get handle distance value 323 | 324 | bool oldIsOnStand = isOnStand; 325 | isOnStand = HandleDistance >= DefaultDistance ? true : false; //check is handle on stand 326 | if (!oldIsOnStand&isOnStand){ 327 | inBoostMode=false; 328 | } 329 | 330 | // isOnStand = HandleDistance >= 25 ? false : false; //check is handle on stand 331 | 332 | analogWrite(CONTROL_PIN, Output); // turn on again heater 333 | 334 | RawTemp += (temp - RawTemp) * SMOOTHIE; // stabilize ADC temperature reading 335 | calculateTemp(); // calculate real temperature value 336 | 337 | // stabilize displayed temperature when around setpoint 338 | if ((ShowTemp != Setpoint) || (abs(ShowTemp - CurrentTemp) > 5)) ShowTemp = CurrentTemp; 339 | if (abs(ShowTemp - Setpoint) <= 1) ShowTemp = Setpoint; 340 | 341 | // set state variable if temperature is in working range; beep if working temperature was just reached 342 | gap = abs(SetTemp - CurrentTemp); 343 | if (gap < 5) { 344 | if (!isWorky && beepIfWorky) beep(); 345 | isWorky = true; 346 | beepIfWorky = false; 347 | } 348 | else isWorky = false; 349 | 350 | // checks if tip is present or currently inserted 351 | if (ShowTemp > 500) TipIsPresent = false; // tip removed ? 352 | if (!TipIsPresent && (ShowTemp < 500)) { // new tip inserted ? 353 | analogWrite(CONTROL_PIN, 255); // shut off heater 354 | beep(); // beep for info 355 | TipIsPresent = true; // tip is present now 356 | ChangeTipScreen(); // show tip selection screen 357 | updateEEPROM(); // update setting in EEPROM 358 | handleMoved = true; // reset all timers 359 | RawTemp = denoiseAnalog(SENSOR_PIN); // restart temp smooth algorithm 360 | c0 = LOW; // switch must be released 361 | setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, SetTemp); // reset rotary encoder 362 | } 363 | } 364 | 365 | 366 | // calculates real temperature value according to ADC reading and calibration values 367 | void calculateTemp() { 368 | if (RawTemp < 200) CurrentTemp = map (RawTemp, 0, 200, 21, CalTemp[CurrentTip][0]); 369 | else if (RawTemp < 280) CurrentTemp = map (RawTemp, 200, 280, CalTemp[CurrentTip][0], CalTemp[CurrentTip][1]); 370 | else CurrentTemp = map (RawTemp, 280, 360, CalTemp[CurrentTip][1], CalTemp[CurrentTip][2]); 371 | } 372 | 373 | 374 | // controls the heater 375 | void Thermostat() { 376 | // define Setpoint acoording to current working mode 377 | if (inOffMode) Setpoint = 0; 378 | else if (inSleepMode) Setpoint = SleepTemp; 379 | else if (inBoostMode) Setpoint = SetTemp + BoostTemp; 380 | else Setpoint = SetTemp; 381 | 382 | // control the heater (PID or direct) 383 | gap = abs(Setpoint - CurrentTemp); 384 | if (PIDenable) { 385 | Input = CurrentTemp; 386 | if (gap < 30) ctrl.SetTunings(consKp, consKi, consKd); 387 | else ctrl.SetTunings(aggKp, aggKi, aggKd); 388 | ctrl.Compute(); 389 | } else { 390 | // turn on heater if current temperature is below setpoint 391 | if ((CurrentTemp + 0.5) < Setpoint) Output = 0; else Output = 255; 392 | } 393 | analogWrite(CONTROL_PIN, Output); // set heater PWM 394 | } 395 | 396 | 397 | // creates a short beep on the buzzer 398 | void beep(){ 399 | if (beepEnable) { 400 | for (uint8_t i=0; i<255; i++) { 401 | digitalWrite(BUZZER_PIN, HIGH); 402 | delayMicroseconds(125); 403 | digitalWrite(BUZZER_PIN, LOW); 404 | delayMicroseconds(125); 405 | } 406 | } 407 | } 408 | 409 | 410 | // sets start values for rotary encoder 411 | void setRotary(int rmin, int rmax, int rstep, int rvalue) { 412 | countMin = rmin << ROTARY_TYPE; 413 | countMax = rmax << ROTARY_TYPE; 414 | countStep = rstep; 415 | count = rvalue << ROTARY_TYPE; 416 | } 417 | 418 | 419 | // reads current rotary encoder value 420 | int getRotary() { 421 | return (count >> ROTARY_TYPE); 422 | } 423 | 424 | 425 | // reads user settings from EEPROM; if EEPROM values are invalid, write defaults 426 | void getEEPROM() { 427 | uint16_t identifier = (EEPROM.read(0) << 8) | EEPROM.read(1); 428 | if (identifier == EEPROM_IDENT) { 429 | DefaultTemp = (EEPROM.read(2) << 8) | EEPROM.read(3); 430 | SleepTemp = (EEPROM.read(4) << 8) | EEPROM.read(5); 431 | BoostTemp = EEPROM.read(6); 432 | time2sleep = EEPROM.read(7); 433 | time2off = EEPROM.read(8); 434 | timeOfBoost = EEPROM.read(9); 435 | MainScrType = EEPROM.read(10); 436 | PIDenable = EEPROM.read(11); 437 | beepEnable = EEPROM.read(12); 438 | CurrentTip = EEPROM.read(13); 439 | NumberOfTips = EEPROM.read(14); 440 | DefaultDistance = EEPROM.read(15); 441 | uint8_t i, j; 442 | uint16_t counter = 16; 443 | for (i = 0; i < NumberOfTips; i++) { 444 | for (j = 0; j < TIPNAMELENGTH; j++) { 445 | TipName[i][j] = EEPROM.read(counter++); 446 | } 447 | for (j = 0; j < 4; j++) { 448 | CalTemp[i][j] = EEPROM.read(counter++) << 8; 449 | CalTemp[i][j] |= EEPROM.read(counter++); 450 | } 451 | } 452 | } 453 | else { 454 | EEPROM.update(0, EEPROM_IDENT >> 8); EEPROM.update(1, EEPROM_IDENT & 0xFF); 455 | updateEEPROM(); 456 | } 457 | } 458 | 459 | 460 | // writes user settings to EEPROM using updade function to minimize write cycles 461 | void updateEEPROM() { 462 | EEPROM.update( 2, DefaultTemp >> 8); 463 | EEPROM.update( 3, DefaultTemp & 0xFF); 464 | EEPROM.update( 4, SleepTemp >> 8); 465 | EEPROM.update( 5, SleepTemp & 0xFF); 466 | EEPROM.update( 6, BoostTemp); 467 | EEPROM.update( 7, time2sleep); 468 | EEPROM.update( 8, time2off); 469 | EEPROM.update( 9, timeOfBoost); 470 | EEPROM.update(10, MainScrType); 471 | EEPROM.update(11, PIDenable); 472 | EEPROM.update(12, beepEnable); 473 | EEPROM.update(13, CurrentTip); 474 | EEPROM.update(14, NumberOfTips); 475 | EEPROM.update(15, DefaultDistance); 476 | uint8_t i, j; 477 | uint16_t counter = 16; 478 | for (i = 0; i < NumberOfTips; i++) { 479 | for (j = 0; j < TIPNAMELENGTH; j++) EEPROM.update(counter++, TipName[i][j]); 480 | for (j = 0; j < 4; j++) { 481 | EEPROM.update(counter++, CalTemp[i][j] >> 8); 482 | EEPROM.update(counter++, CalTemp[i][j] & 0xFF); 483 | } 484 | } 485 | } 486 | 487 | 488 | // draws the main screen 489 | void MainScreen() { 490 | u8g.firstPage(); 491 | do { 492 | // draw setpoint temperature 493 | u8g.setFont(u8g_font_9x15); 494 | u8g.setFontPosTop(); 495 | if (!inBoostMode) { 496 | u8g.drawStr( 0, 0, "SET:"); 497 | u8g.setPrintPos(40,0); 498 | //u8g.print(Setpoint, 0); 499 | u8g.print(SetTemp); 500 | } else { 501 | u8g.drawStr( 0, 0, "BST:"); 502 | u8g.setPrintPos(40,0); 503 | //u8g.print(Setpoint, 0); 504 | u8g.print(SetTemp+BoostTemp); 505 | } 506 | 507 | // draw status of heater 508 | u8g.setPrintPos(83,0); 509 | if (ShowTemp > 500) u8g.print(F("ERROR")); 510 | else if (inOffMode) u8g.print(F(" OFF")); 511 | else if (inSleepMode) u8g.print(F("SLEEP")); 512 | else if (inBoostMode) u8g.print(F("BOOST")); 513 | else if (isWorky) u8g.print(F("WORKY")); 514 | else if (Output < 180) u8g.print(F(" HEAT")); 515 | else u8g.print(F(" HOLD")); 516 | // u8g.print(HandleDistance); 517 | // u8g.print(isOnStand); 518 | // rest depending on main screen type 519 | if (MainScrType) { 520 | // draw current tip and input voltage 521 | float fVin = (float)Vin / 1000; // convert mv in V 522 | u8g.setPrintPos( 0,52); u8g.print(TipName[CurrentTip]); 523 | u8g.setPrintPos(83,52); u8g.print(fVin, 1); u8g.print(F("V")); 524 | // draw current temperature 525 | u8g.setFont(u8g_font_freedoomr25n); 526 | u8g.setFontPosTop(); 527 | u8g.setPrintPos(37,22); 528 | if (ShowTemp > 500) u8g.print(F("000")); else u8g.print(ShowTemp); 529 | } else { 530 | // draw current temperature in big figures 531 | u8g.setFont(u8g_font_fub42n); 532 | u8g.setFontPosTop(); 533 | u8g.setPrintPos(15,20); 534 | if (ShowTemp > 500) u8g.print(F("000")); else u8g.print(ShowTemp); 535 | } 536 | } while(u8g.nextPage()); 537 | } 538 | 539 | 540 | // setup screen 541 | void SetupScreen() { 542 | analogWrite(CONTROL_PIN, 255); // shut off heater 543 | beep(); 544 | uint16_t SaveSetTemp = SetTemp; 545 | uint8_t selection = 0; 546 | bool repeat = true; 547 | 548 | while (repeat) { 549 | selection = MenuScreen(SetupItems, sizeof(SetupItems), selection); 550 | switch (selection) { 551 | case 0: TipScreen(); repeat = false; break; 552 | case 1: TempScreen(); break; 553 | case 2: TimerScreen(); break; 554 | case 3: PIDenable = MenuScreen(ControlTypeItems, sizeof(ControlTypeItems), PIDenable); break; 555 | case 4: MainScrType = MenuScreen(MainScreenItems, sizeof(MainScreenItems), MainScrType); break; 556 | case 5: beepEnable = MenuScreen(BuzzerItems, sizeof(BuzzerItems), beepEnable); break; 557 | case 6: InfoScreen(); break; 558 | case 7: DistanceScreen(); break; 559 | default: repeat = false; break; 560 | } 561 | } 562 | updateEEPROM(); 563 | handleMoved = true; 564 | SetTemp = SaveSetTemp; 565 | setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, SetTemp); 566 | } 567 | 568 | 569 | // tip settings screen 570 | void TipScreen() { 571 | uint8_t selection = 0; 572 | bool repeat = true; 573 | while (repeat) { 574 | selection = MenuScreen(TipItems, sizeof(TipItems), selection); 575 | switch (selection) { 576 | case 0: ChangeTipScreen(); break; 577 | case 1: CalibrationScreen(); break; 578 | case 2: InputNameScreen(); break; 579 | case 3: DeleteTipScreen(); break; 580 | case 4: AddTipScreen(); break; 581 | default: repeat = false; break; 582 | } 583 | } 584 | } 585 | 586 | 587 | // temperature settings screen 588 | void TempScreen() { 589 | uint8_t selection = 0; 590 | bool repeat = true; 591 | while (repeat) { 592 | selection = MenuScreen(TempItems, sizeof(TempItems), selection); 593 | switch (selection) { 594 | case 0: setRotary(TEMP_MIN, TEMP_MAX, TEMP_STEP, DefaultTemp); 595 | DefaultTemp = InputScreen(DefaultTempItems); break; 596 | case 1: setRotary(20, 200, TEMP_STEP, SleepTemp); 597 | SleepTemp = InputScreen(SleepTempItems); break; 598 | case 2: setRotary(10, 100, TEMP_STEP, BoostTemp); 599 | BoostTemp = InputScreen(BoostTempItems); break; 600 | default: repeat = false; break; 601 | } 602 | } 603 | } 604 | 605 | 606 | // timer settings screen 607 | void TimerScreen() { 608 | uint8_t selection = 0; 609 | bool repeat = true; 610 | while (repeat) { 611 | selection = MenuScreen(TimerItems, sizeof(TimerItems), selection); 612 | switch (selection) { 613 | case 0: setRotary(0, 30, 1, time2sleep); 614 | time2sleep = InputScreen(SleepTimerItems); break; 615 | case 1: setRotary(0, 60, 5, time2off); 616 | time2off = InputScreen(OffTimerItems); break; 617 | case 2: setRotary(0, 180, 10, timeOfBoost); 618 | timeOfBoost = InputScreen(BoostTimerItems); break; 619 | default: repeat = false; break; 620 | } 621 | } 622 | } 623 | 624 | 625 | // menu screen 626 | uint8_t MenuScreen(const char *Items[], uint8_t numberOfItems, uint8_t selected) { 627 | bool isTipScreen = (Items[0] == "Tip:"); 628 | uint8_t lastselected = selected; 629 | int8_t arrow = 0; 630 | if (selected) arrow = 1; 631 | numberOfItems >>= 1; 632 | setRotary(0, numberOfItems - 2, 1, selected); 633 | bool lastbutton = (!digitalRead(BUTTON_PIN)); 634 | 635 | do { 636 | selected = getRotary(); 637 | arrow = constrain(arrow + selected - lastselected, 0, 2); 638 | lastselected = selected; 639 | u8g.firstPage(); 640 | do { 641 | u8g.setFont(u8g_font_9x15); 642 | u8g.setFontPosTop(); 643 | u8g.drawStr( 0, 0, Items[0]); 644 | if (isTipScreen) u8g.drawStr( 54, 0, TipName[CurrentTip]); 645 | u8g.drawStr( 0, 16 * (arrow + 1), ">"); 646 | for (uint8_t i=0; i<3; i++) { 647 | uint8_t drawnumber = selected + i + 1 - arrow; 648 | if (drawnumber < numberOfItems) 649 | u8g.drawStr( 12, 16 * (i + 1), Items[selected + i + 1 - arrow]); 650 | } 651 | } while(u8g.nextPage()); 652 | if (lastbutton && digitalRead(BUTTON_PIN)) {delay(10); lastbutton = false;} 653 | } while (digitalRead(BUTTON_PIN) || lastbutton); 654 | 655 | beep(); 656 | return selected; 657 | } 658 | 659 | 660 | void MessageScreen(const char *Items[], uint8_t numberOfItems) { 661 | bool lastbutton = (!digitalRead(BUTTON_PIN)); 662 | u8g.firstPage(); 663 | do { 664 | u8g.setFont(u8g_font_9x15); 665 | u8g.setFontPosTop(); 666 | for (uint8_t i = 0; i < numberOfItems; i++) u8g.drawStr( 0, i * 16, Items[i]); 667 | } while(u8g.nextPage()); 668 | do { 669 | if (lastbutton && digitalRead(BUTTON_PIN)) {delay(10); lastbutton = false;} 670 | } while (digitalRead(BUTTON_PIN) || lastbutton); 671 | beep(); 672 | } 673 | 674 | 675 | // input value screen 676 | uint16_t InputScreen(const char *Items[]) { 677 | uint16_t value; 678 | bool lastbutton = (!digitalRead(BUTTON_PIN)); 679 | 680 | do { 681 | value = getRotary(); 682 | u8g.firstPage(); 683 | do { 684 | u8g.setFont(u8g_font_9x15); 685 | u8g.setFontPosTop(); 686 | u8g.drawStr( 0, 0, Items[0]); 687 | u8g.setPrintPos(0, 32); u8g.print(">"); u8g.setPrintPos(10, 32); 688 | if (value == 0) u8g.print(F("Deactivated")); 689 | else {u8g.print(value);u8g.print(" ");u8g.print(Items[1]);} 690 | } while(u8g.nextPage()); 691 | if (lastbutton && digitalRead(BUTTON_PIN)) {delay(10); lastbutton = false;} 692 | } while (digitalRead(BUTTON_PIN) || lastbutton); 693 | 694 | beep(); 695 | return value; 696 | } 697 | 698 | 699 | // information display screen 700 | void InfoScreen() { 701 | bool lastbutton = (!digitalRead(BUTTON_PIN)); 702 | 703 | do { 704 | Vcc = getVCC(); // read input voltage 705 | float fVcc = (float)Vcc / 1000; // convert mV in V 706 | Vin = getVIN(); // read supply voltage 707 | float fVin = (float)Vin / 1000; // convert mv in V 708 | float fTmp = getChipTemp(); // read cold junction temperature 709 | uint16_t iHandleDistance = getHandleDistance(); 710 | u8g.firstPage(); 711 | do { 712 | u8g.setFont(u8g_font_9x15); 713 | u8g.setFontPosTop(); 714 | u8g.setPrintPos(0, 0); u8g.print(F("Firmware: ")); u8g.print(VERSION); 715 | u8g.setPrintPos(0, 16); u8g.print(F("Tmp: ")); u8g.print(fTmp, 1); u8g.print(F(" C")); 716 | u8g.setPrintPos(0, 32); u8g.print(F("Vin: ")); u8g.print(fVin, 1); u8g.print(F(" V")); 717 | //u8g.setPrintPos(0, 48); u8g.print(F("Vcc: ")); u8g.print(fVcc, 1); u8g.print(F(" V")); 718 | u8g.setPrintPos(0, 48); u8g.print(F("Handle: ")); u8g.print(iHandleDistance); 719 | } while(u8g.nextPage()); 720 | if (lastbutton && digitalRead(BUTTON_PIN)) {delay(10); lastbutton = false;} 721 | } while (digitalRead(BUTTON_PIN) || lastbutton); 722 | 723 | beep(); 724 | } 725 | 726 | // distance display screen 727 | void DistanceScreen(){ 728 | setRotary(0, 1024, 1, DefaultDistance); 729 | DefaultDistance = InputScreen(DistanceItems); 730 | beep(); 731 | } 732 | 733 | // change tip screen 734 | void ChangeTipScreen() { 735 | uint8_t selected = CurrentTip; 736 | uint8_t lastselected = selected; 737 | int8_t arrow = 0; 738 | if (selected) arrow = 1; 739 | setRotary(0, NumberOfTips - 1, 1, selected); 740 | bool lastbutton = (!digitalRead(BUTTON_PIN)); 741 | 742 | do { 743 | selected = getRotary(); 744 | arrow = constrain(arrow + selected - lastselected, 0, 2); 745 | lastselected = selected; 746 | u8g.firstPage(); 747 | do { 748 | u8g.setFont(u8g_font_9x15); 749 | u8g.setFontPosTop(); 750 | u8g.drawStr( 0, 0, F("Select Tip")); 751 | u8g.drawStr( 0, 16 * (arrow + 1), ">"); 752 | for (uint8_t i=0; i<3; i++) { 753 | uint8_t drawnumber = selected + i - arrow; 754 | if (drawnumber < NumberOfTips) 755 | u8g.drawStr( 12, 16 * (i + 1), TipName[selected + i - arrow]); 756 | } 757 | } while(u8g.nextPage()); 758 | if (lastbutton && digitalRead(BUTTON_PIN)) {delay(10); lastbutton = false;} 759 | } while (digitalRead(BUTTON_PIN) || lastbutton); 760 | 761 | beep(); 762 | CurrentTip = selected; 763 | } 764 | 765 | 766 | // temperature calibration screen 767 | void CalibrationScreen() { 768 | if (inSleepMode) { // in sleep or off mode? 769 | inSleepMode = false; 770 | beep(); // beep 771 | } 772 | uint16_t CalTempNew[4]; 773 | for (uint8_t CalStep = 0; CalStep < 3; CalStep++) { 774 | SetTemp = CalTemp[CurrentTip][CalStep]; 775 | setRotary(100, 600, 1, SetTemp); 776 | beepIfWorky = true; 777 | bool lastbutton = (!digitalRead(BUTTON_PIN)); 778 | 779 | do { 780 | SENSORCheck(); // reads temperature and vibration switch of the iron 781 | Thermostat(); // heater control 782 | 783 | u8g.firstPage(); 784 | do { 785 | u8g.setFont(u8g_font_9x15); 786 | u8g.setFontPosTop(); 787 | u8g.drawStr( 0, 0, F("Calibration")); 788 | u8g.setPrintPos(0, 16); u8g.print(F("Step: ")); u8g.print(CalStep + 1); u8g.print(" of 3"); 789 | if (isWorky) { 790 | u8g.setPrintPos(0, 32); u8g.print(F("Set measured")); 791 | u8g.setPrintPos(0, 48); u8g.print(F("temp: ")); u8g.print(getRotary()); 792 | } else { 793 | u8g.setPrintPos(0, 32); u8g.print(F("ADC: ")); u8g.print(uint16_t(RawTemp)); 794 | u8g.setPrintPos(0, 48); u8g.print(F("Please wait...")); 795 | } 796 | } while(u8g.nextPage()); 797 | if (lastbutton && digitalRead(BUTTON_PIN)) {delay(10); lastbutton = false;} 798 | } while (digitalRead(BUTTON_PIN) || lastbutton); 799 | 800 | CalTempNew[CalStep] = getRotary(); 801 | beep(); delay (10); 802 | } 803 | 804 | analogWrite(CONTROL_PIN, 255); // shut off heater 805 | delayMicroseconds(TIME2SETTLE); // wait for voltage to settle 806 | CalTempNew[3] = getChipTemp(); // read chip temperature 807 | if ((CalTempNew[0] + 10 < CalTempNew[1]) && (CalTempNew[1] + 10 < CalTempNew[2])) { 808 | if (MenuScreen(StoreItems, sizeof(StoreItems), 0)) { 809 | for (uint8_t i = 0; i < 4; i++) CalTemp[CurrentTip][i] = CalTempNew[i]; 810 | } 811 | } 812 | } 813 | 814 | 815 | // input tip name screen 816 | void InputNameScreen() { 817 | uint8_t value; 818 | 819 | for (uint8_t digit = 0; digit < (TIPNAMELENGTH - 1); digit++) { 820 | bool lastbutton = (!digitalRead(BUTTON_PIN)); 821 | setRotary(31, 96, 1, 65); 822 | do { 823 | value = getRotary(); 824 | if (value == 31) {value = 95; setRotary(31, 96, 1, 95);} 825 | if (value == 96) {value = 32; setRotary(31, 96, 1, 32);} 826 | u8g.firstPage(); 827 | do { 828 | u8g.setFont(u8g_font_9x15); 829 | u8g.setFontPosTop(); 830 | u8g.drawStr( 0, 0, F("Enter Tip Name")); 831 | u8g.setPrintPos(9 * digit, 48); u8g.print(char(94)); 832 | u8g.setPrintPos(0, 32); 833 | for (uint8_t i = 0; i < digit; i++) u8g.print(TipName[CurrentTip][i]); 834 | u8g.setPrintPos(9 * digit, 32); u8g.print(char(value)); 835 | } while(u8g.nextPage()); 836 | if (lastbutton && digitalRead(BUTTON_PIN)) {delay(10); lastbutton = false;} 837 | } while (digitalRead(BUTTON_PIN) || lastbutton); 838 | TipName[CurrentTip][digit] = value; 839 | beep(); delay (10); 840 | } 841 | TipName[CurrentTip][TIPNAMELENGTH - 1] = 0; 842 | return value; 843 | } 844 | 845 | 846 | // delete tip screen 847 | void DeleteTipScreen() { 848 | if (NumberOfTips == 1) {MessageScreen(DeleteMessage, sizeof(DeleteMessage));} 849 | else if (MenuScreen(SureItems, sizeof(SureItems), 0)) { 850 | if (CurrentTip == (NumberOfTips - 1)) {CurrentTip--;} 851 | else { 852 | for (uint8_t i = CurrentTip; i < (NumberOfTips - 1); i++) { 853 | for (uint8_t j = 0; j < TIPNAMELENGTH; j++) TipName[i][j] = TipName[i+1][j]; 854 | for (uint8_t j = 0; j < 4; j++) CalTemp[i][j] = CalTemp[i+1][j]; 855 | } 856 | } 857 | NumberOfTips--; 858 | } 859 | } 860 | 861 | 862 | // add new tip screen 863 | void AddTipScreen() { 864 | if (NumberOfTips < TIPMAX) { 865 | CurrentTip = NumberOfTips++; InputNameScreen(); 866 | CalTemp[CurrentTip][0] = TEMP200; CalTemp[CurrentTip][1] = TEMP280; 867 | CalTemp[CurrentTip][2] = TEMP360; CalTemp[CurrentTip][3] = TEMPCHP; 868 | } else MessageScreen(MaxTipMessage, sizeof(MaxTipMessage)); 869 | } 870 | 871 | 872 | // average several ADC readings in sleep mode to denoise 873 | uint16_t denoiseAnalog (byte port) { 874 | uint16_t result = 0; 875 | ADCSRA |= bit (ADEN) | bit (ADIF); // enable ADC, turn off any pending interrupt 876 | if (port >= A0) port -= A0; // set port and 877 | ADMUX = (0x0F & port) | bit(REFS0); // reference to AVcc 878 | set_sleep_mode (SLEEP_MODE_ADC); // sleep during sample for noise reduction 879 | for (uint8_t i=0; i<32; i++) { // get 32 readings 880 | sleep_mode(); // go to sleep while taking ADC sample 881 | while (bitRead(ADCSRA, ADSC)); // make sure sampling is completed 882 | result += ADC; // add them up 883 | } 884 | bitClear (ADCSRA, ADEN); // disable ADC 885 | return (result >> 5); // devide by 32 and return value 886 | } 887 | 888 | 889 | // get internal temperature by reading ADC channel 8 against 1.1V reference 890 | double getChipTemp() { 891 | uint16_t result = 0; 892 | ADCSRA |= bit (ADEN) | bit (ADIF); // enable ADC, turn off any pending interrupt 893 | ADMUX = bit (REFS1) | bit (REFS0) | bit (MUX3); // set reference and mux 894 | delay(20); // wait for voltages to settle 895 | set_sleep_mode (SLEEP_MODE_ADC); // sleep during sample for noise reduction 896 | for (uint8_t i=0; i<32; i++) { // get 32 readings 897 | sleep_mode(); // go to sleep while taking ADC sample 898 | while (bitRead(ADCSRA, ADSC)); // make sure sampling is completed 899 | result += ADC; // add them up 900 | } 901 | bitClear (ADCSRA, ADEN); // disable ADC 902 | result >>= 2; // devide by 4 903 | return ((result - 2594) / 9.76); // calculate internal temperature in degrees C 904 | } 905 | 906 | 907 | // get input voltage in mV by reading 1.1V reference against AVcc 908 | uint16_t getVCC() { 909 | uint16_t result = 0; 910 | ADCSRA |= bit (ADEN) | bit (ADIF); // enable ADC, turn off any pending interrupt 911 | // set Vcc measurement against 1.1V reference 912 | ADMUX = bit (REFS0) | bit (MUX3) | bit (MUX2) | bit (MUX1); 913 | delay(1); // wait for voltages to settle 914 | set_sleep_mode (SLEEP_MODE_ADC); // sleep during sample for noise reduction 915 | for (uint8_t i=0; i<16; i++) { // get 16 readings 916 | sleep_mode(); // go to sleep while taking ADC sample 917 | while (bitRead(ADCSRA, ADSC)); // make sure sampling is completed 918 | result += ADC; // add them up 919 | } 920 | bitClear (ADCSRA, ADEN); // disable ADC 921 | result >>= 4; // devide by 16 922 | return (1125300L / result); // 1125300 = 1.1 * 1023 * 1000 923 | } 924 | 925 | 926 | // get supply voltage in mV 927 | uint16_t getVIN() { 928 | long result; 929 | result = denoiseAnalog (VIN_PIN); // read supply voltage via voltage divider 930 | return (result * Vcc / 179.474); // 179.474 = 1023 * R13 / (R12 + R13) 931 | } 932 | 933 | // get Handle Distanse 934 | uint16_t getHandleDistance() { 935 | uint16_t result; 936 | result = denoiseAnalog (ONSTAND_PIN); 937 | return result; 938 | } 939 | 940 | 941 | // ADC interrupt service routine 942 | EMPTY_INTERRUPT (ADC_vect); // nothing to be done here 943 | 944 | 945 | // Pin change interrupt service routine for rotary encoder 946 | ISR (PCINT0_vect) { 947 | uint8_t a = PINB & 1; 948 | uint8_t b = PIND>>7 & 1; 949 | 950 | if (a != a0) { // A changed 951 | a0 = a; 952 | if (b != b0) { // B changed 953 | b0 = b; 954 | count = constrain(count + ((a == b) ? -countStep : countStep), countMin, countMax); 955 | if (ROTARY_TYPE && ((a == b) != ab0)) { 956 | count = constrain(count + ((a == b) ? -countStep : countStep), countMin, countMax);; 957 | } 958 | ab0 = (a == b); 959 | handleMoved = true; 960 | inBoostMode = false; 961 | } 962 | } 963 | } 964 | --------------------------------------------------------------------------------