T12 Soldering Station

I tried to copy a tutorial of a soldering iron whose control was based on arduino. After purchasing all the parts and assembling everything, it was time to upload the provided script. But when trying to do it the following errors appear:

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.cpp:4:49: warning: default argument given for parameter 2 of 'BUTTON::BUTTON(uint8_t, uint16_t)' [-fpermissive]
 BUTTON::BUTTON(uint8_t b_pin, uint16_t to = 3000) {
                                                 ^
In file included from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.cpp:1:0:
C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.h:8:9: note: previous specification in 'BUTTON::BUTTON(uint8_t, uint16_t)' here
         BUTTON(uint8_t b_pin, uint16_t timeout_ms = 3000);
         ^~~~~~
C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino: In member function 'void IRON::init()':
C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:251:21: warning: passing 'volatile EMP_AVERAGE' as 'this' argument discards qualifiers [-fpermissive]
     amb_int.length(4);
                     ^
In file included from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.h:3:0,
                 from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:18:
C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\stat.h:9:25: note:   in call to 'void EMP_AVERAGE::length(uint8_t)'
         void            length(uint8_t h_length)        { emp_k = h_length; emp_data = 0; }
                         ^~~~~~
C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino: In member function 'void IRON::keepTemp()':
C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:325:27: warning: passing 'volatile EMP_AVERAGE' as 'this' argument discards qualifiers [-fpermissive]
     amb_int.update(ambient);
                           ^
In file included from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.h:3:0,
                 from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:18:
C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\stat.h:12:25: note:   in call to 'void EMP_AVERAGE::update(int32_t)'
         void            update(int32_t value);
                         ^~~~~~
C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino: In member function 'int16_t IRON::ambientTemp()':
C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:398:36: warning: passing 'volatile EMP_AVERAGE' as 'this' argument discards qualifiers [-fpermissive]
     uint16_t a_temp = amb_int.read();                       // Average value of ambient temperature
                                    ^
In file included from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.h:3:0,
                 from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:18:
C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\stat.h:13:25: note:   in call to 'int32_t EMP_AVERAGE::read()'
         int32_t         read(void);
                         ^~~~

``````````````````


**I know little or nothing about Arduino. Can anyone give me some help? I would really appreciate it.**

**Thanks !**

Your topic was MOVED to its current forum category as it is more suitable than the original as it has nothing whatsoever to do with Installation and Troubleshooting of the IDE

These are warnings as far as I can see, not errors.

But i cannot see anything in the serie monitor..

How about posting the sketch ?

Do you think that might help ?


/*
 * Soldering IRON controller for hakko t12 tips built on atmega328 microcontroller running 16 MHz
 * The controller is using interrupts from the Timer1 to generate high-frequence PWM signal on port D10
 * to silently heat the IRON and periodically check the IRON temperature on overflow interrupts
 * Timer1 runs with prescale 1 through 0 to 255 and back and its frequency is 31250 Hz.
 * The owerflow interrupt running as folows:
 * First, the current through the IRON is checked
 * then the power, supplien to the IRON interrupted and the controller waits for 32 timer interrupts (about 1 ms)
 * then the IRON temperature is checked and the power to the IRON restored
 * then the controller waits for check_period Timer1 interrupts to restart the all procedure over again
 *
 * Code modifications by Sasiskas (Youtube channel)
 */

// Edit the configuration file to select appropriate display type
#include <EEPROM.h>
#include "config.h"
#include "encoder.h"
#include "cfg.h"
#include "iron_tips.h"
#include "vars.h"

// Rotary encoder interface
const uint8_t R_MAIN_PIN = 2;					// Rotary Encoder main pin (right)
const uint8_t R_SECD_PIN = 4;                   // Rotary Encoder second pin (left)
const uint8_t R_BUTN_PIN = 3;                   // Rotary Encoder push button pin

const uint8_t probePIN  = A0;                   // Thermometer pin from soldering IRON
const uint8_t checkPIN  = A1;                   // Iron current check pin
const uint8_t termisPIN = A2;                   // The thermistor pin to check ambient temperature
const uint8_t tiltswPIN = A3;                   // The tilt/reed/vibro switch pin 
const uint8_t buzzerPIN = 11;                   // The simple buzzer to make a noise
const uint8_t heaterBIT = 0b00000100;           // The Heater pin, D10, is 2-nd bit on the PORTB register

// The variables for Timer1 operations
volatile uint16_t  tmr1_count;                  // The count to calculate the temperature and the current check periods
volatile bool      iron_off;                    // Whether the IRON is switched off to check the temperature
const uint32_t     temp_check_period = 20;      // The IRON temperature check period, ms

//------------------------------------------ class BUZZER ------------------------------------------------------
class BUZZER {
	public:
		BUZZER(uint8_t buzzerP)                 { buzzer_pin = buzzerP; }
        void    activate(bool on)               { this->on = on; }
		void    init(void)						{ pinMode(buzzer_pin, OUTPUT); noTone(buzzer_pin); }
		void    shortBeep(void)					{ if (on) tone(buzzer_pin, 3520, 160); }
		void    lowBeep(void)    				{ if (on) tone(buzzer_pin,  880, 160); }
		void    doubleBeep(void) 				{ if (on) { tone(buzzer_pin, 3520, 160); delay(300); tone(buzzer_pin, 3520, 160); } }
		void    failedBeep(void) 			    { if (on) { tone(buzzer_pin, 3520, 160); delay(170);
													tone(buzzer_pin, 880, 250); delay(260);
													tone(buzzer_pin, 3520, 160); }
												}
	private:
		uint8_t buzzer_pin;
        bool    on = true;
};

//------------------------------------------ class PID algoritm to keep the temperature -----------------------
/*  The PID algorithm 
 *  Un = Kp*(Xs - Xn) + Ki*summ{j=0; j<=n}(Xs - Xj) + Kd(Xn - Xn-1),
 *  Where Xs - is the setup temperature, Xn - the temperature on n-iteration step
 *  In this program the interactive formula is used:
 *    Un = Un-1 + Kp*(Xn-1 - Xn) + Ki*(Xs - Xn) + Kd*(Xn-2 + Xn - 2*Xn-1)
 *  With the first step:
 *  U0 = Kp*(Xs - X0) + Ki*(Xs - X0); Xn-1 = Xn;
 *  
 *  PID coefficients history:
 *  10/14/2017  [768, 32, 328]
 */
class PID {
	public:
		PID(void) {
			Kp = 2009;
			Ki =   16;
			Kd = 2048;
		}
		void	resetPID(int temp = -1);      	// reset PID algorithm history parameters
		// Calculate the power to be applied
		int32_t	reqPower(int temp_set, int temp_curr);
		int16_t	changePID(uint8_t p, int k);  	// set or get (if parameter < 0) PID parameter
	private:
		void  	debugPID(int t_set, int t_curr, int32_t kp, int32_t ki, int32_t kd, int32_t delta_p);
		int16_t	temp_h0, temp_h1;               // previously measured temperature
		bool  	pid_iterate;                    // Whether the iterative process is used
		int32_t i_summ;                        	// Ki summary multiplied by denominator
		int32_t power;                         	// The power iterative multiplied by denominator
		int32_t Kp, Ki, Kd;                    	// The PID algorithm coefficients multiplied by denominator
		const uint8_t denominator_p = 11;      	// The common coefficient denominator power of 2 (11 means divide by 2048)
};

void PID::resetPID(int temp) {
	temp_h0 = 0;
	power  = 0;
	i_summ = 0;
	pid_iterate = false;
	if ((temp > 0) && (temp < 1000))
		temp_h1 = temp;
	else
		temp_h1 = 0;
}

int16_t PID::changePID(uint8_t p, int k) {
	switch(p) {
		case 1:
			if (k >= 0) Kp = k;
			return Kp;
		case 2:
			if (k >= 0) Ki = k;
			return Ki;
		case 3:
			if (k >= 0) Kd = k;
			return Kd;
		default:
			break;
	}
	return 0;
}

int32_t PID::reqPower(int temp_set, int temp_curr) {
	if (temp_h0 == 0) {
		// When the temperature is near the preset one, reset the PID and prepare iterative formula                        
		if ((temp_set - temp_curr) < 30) {
			if (!pid_iterate) {
				pid_iterate = true;
				power = 0;
				i_summ = 0;
			}
		}
		i_summ += temp_set - temp_curr;       	// first, use the direct formula, not the iterate process
		power = Kp*(temp_set - temp_curr) + Ki*i_summ;
		// If the temperature is near, prepare the PID iteration process
	} else {
		int32_t kp = Kp * (temp_h1 - temp_curr);
		int32_t ki = Ki * (temp_set - temp_curr);
		int32_t kd = Kd * (temp_h0 + temp_curr - 2*temp_h1);
		int32_t delta_p = kp + ki + kd;
		power += delta_p;                     	// power kept multiplied by denominator!
	}
	if (pid_iterate) temp_h0 = temp_h1;
	temp_h1 = temp_curr;
	int32_t pwr = power + (1 << (denominator_p-1));  // prepare the power to delete by denominator, round the result
	pwr >>= denominator_p;                    	// delete by the denominator
	return pwr;
}

//------------------------- class FastPWM operations using Timer1 on pin D10 at 31250 Hz ----------------------
class FastPWM {
	public:
		FastPWM()                               { }
		void init(void);
		void duty(uint8_t d)                    { OCR1B = d; }
		void off(void)                        	{ OCR1B = 0; PORTB &= ~heaterBIT; }
};

void FastPWM::init(void) {
	pinMode(10, OUTPUT);                        // Use D10 pin for heating the IRON
	PORTB &= ~heaterBIT;                        // Switch-off the power
	tmr1_count = 0;
	iron_off = false;                           // The volatile global variable
	noInterrupts();
	TCNT1   = 0;
	TCCR1B  = _BV(WGM13);                       // Set mode as phase and frequency correct pwm, stop the timer
	TCCR1A  = 0;
	ICR1    = 256;
	TCCR1B  = _BV(WGM13) | _BV(CS10);           // Top value = ICR1, prescale = 1; 31250 Hz
	TCCR1A |= _BV(COM1B1);                      // XOR D10 on OC1B, detached from D09
	OCR1B   = 0;                                // Switch-off the signal on pin D10;
	TIMSK1  = _BV(TOIE1);                     	// Enable overflow interrupts @31250 Hz
	interrupts();
}

//------------------------------------------ class soldering IRON ---------------------------------------------
class IRON : protected PID {
	public:
		IRON(uint8_t sensor_pin, uint8_t check_pin, uint8_t ambient_pin, uint8_t tilt_pin) {
			sPIN = sensor_pin;
			cPIN = check_pin;
            aPIN = ambient_pin;
            tPIN = tilt_pin;
			h_counter = h_max_counter;
		}
        typedef enum { POWER_OFF, POWER_ON, POWER_FIXED, POWER_COOLING } PowerMode;
		void     	init(void);
		void     	switchPower(bool On);
        bool        isOn(void);
		uint16_t 	presetTemp(void)            { return temp_set;              }
		uint16_t 	currTemp(void)              { return h_temp.read();         }
		uint16_t 	tempAverage(void)           { return h_temp.average();      }
		uint16_t 	tempDispersion(void)        { return h_temp.dispersion();   }
		uint16_t 	powerDispersion(void)       { return h_power.dispersion();  }
		uint8_t		getMaxFixedPower(void)      { return max_fixed_power;       }
		int16_t		changePID(uint8_t p, int k)	{ return PID::changePID(p, k);  }
		bool     	checkIron(void);           	// Check the IRON, return true if the iron is not connected
		void     	keepTemp(void);            	// Keep the IRON temperature, called by Timer1 interrupt
		uint8_t     appliedPower(void);         // Power applied to the IRON in percents
		void     	setTemp(uint16_t t);        // Set the temperature to be kept (internal units)
        void        lowPowerMode(uint16_t t);   // Activate low power mode (setup temp. or 0 to return to standard mode)
		uint8_t     getAvgPower(void);          // Average applied power
		void     	fixPower(uint8_t Power);    // Set the specified power to the the soldering IRON
		void     	initTempHistory(void)       { h_counter = h_max_counter; h_temp.init(); mode = POWER_OFF; }
        bool        isCold(void)                { return (mode == POWER_OFF); }
        int16_t     ambientTemp(void);
        void        adjust(uint16_t t);
        bool        isIronTiltSwitch(bool reed);
        void        checkSWStatus(void);
  private:
		FastPWM  	fastPWM;                    // Power the iron using fast PWM through D10 pin using Timer1
		uint8_t     sPIN, cPIN, aPIN, tPIN;     // The sensor PIN, current check PIN, ambient temperature PIN, tilt switch PIN
        uint16_t    temp_set;                   // The temperature that should be kept
        uint16_t    temp_low;                   // Low power mode temperature
		uint8_t     fix_power = 0;              // Fixed power value of the IRON (or zero if off)
		uint32_t 	check_iron_ms = 0;          // The time in ms when check the IRON next time
        uint32_t    check_tilt_ms = 0;          // The time in ms when check the tilt switch next time
		bool     	disconnected;               // Whether no current through the IRON (the iron disconnected)
		int      	h_counter;                  // Put the temperature and power to the history, when the counter become 0 
        uint8_t     applied_power = 0;          // The applied power to the IRON, used in checkIron() 
		volatile    PowerMode mode = POWER_OFF; // Working mode of the IRON
		volatile 	bool chill;                 // Whether the IRON should be cooled (preset temp is lower than current)
		HISTORY     h_power;                    // The history data of power applied values
		HISTORY     h_temp;                     // The history data of the temperature
		EMP_AVERAGE current;                    // The average value for the current through the IRON
        EMP_AVERAGE tilt;                       // The average value of tilt port
        volatile    EMP_AVERAGE amb_int;        // The internal reading of ambient temperature                        
        bool        tilt_toggle = false;        // The tilt switch changed state
		const uint8_t     max_power       = 210; // maximum power to the IRON
		const uint8_t     max_fixed_power = 120; // Maximum power in fixed power mode
		const uint16_t min_curr        = 10;    // The minimum current value to check the IRON is connected
		const uint32_t check_period    = 503;   // Check the iron period in ms
		const uint16_t h_max_counter   = 500 / temp_check_period;     // Put the history data twice a second
		const uint8_t  emp_k = 2;           	// The exponential average coefficient
        const uint8_t  emp_tilt = 3;            // The exponential average coefficient for tilt pin
        const uint16_t iron_cold = 25;          // The internal temperature when the IRON is cold
};

void IRON::init(void) {
	pinMode(sPIN, INPUT);
    pinMode(aPIN, INPUT);
    pinMode(tPIN, INPUT);
	fastPWM.init();                           	// Initialization for 31.5 kHz PWM on D10 pin
	mode            = POWER_OFF;
	fix_power       = 0;
    applied_power   = 0;
	disconnected    = true;                     // Do not read the ambient temperature when the IRON disconnected
	check_iron_ms   = 0;
	resetPID();
	h_counter = h_max_counter;
	h_power.init();
	h_temp.init();
	current.length(emp_k);
    tilt.length(emp_tilt);
    amb_int.length(4);
}

void IRON::setTemp(uint16_t t) {
    if (mode == POWER_ON) resetPID();
    temp_set = t;
    uint16_t ta = h_temp.average();
    chill = (ta > t + 5);                       // The IRON must be cooled
    temp_low = 0;                               // disable low power mode
}

void IRON::lowPowerMode(uint16_t t) {
    if ((mode == POWER_ON && t < temp_set) || t == 0)
        temp_low = t;                           // Activate low power mode
}

uint8_t IRON::getAvgPower(void) {
    uint16_t p = h_power.average();
    return p & 0xff;  
}

uint8_t IRON::appliedPower(void) {
    uint8_t p = getAvgPower(); 
    return map(p, 0, max_power, 0, 100);  
}

void IRON::switchPower(bool On) {
	if (!On) {
		fastPWM.off();
		fix_power = 0;
        if (mode != POWER_OFF)
            mode = POWER_COOLING;
		return;
	}
	resetPID(analogRead(sPIN));
	h_power.init();
    mode = POWER_ON;
}

bool IRON::isOn(void) {
    return (mode == POWER_ON || mode == POWER_FIXED);
}

bool IRON::checkIron(void) {
	if (millis() < check_iron_ms)
		return disconnected;

	check_iron_ms = millis() + check_period;
	uint16_t curr = 0;
	if (applied_power == 0) {                   // The IRON is switched-off
		fastPWM.duty(127);                      // Quarter of maximum power
		for (uint8_t i = 0; i < 5; ++i) {       // Make sure we check the current in active phase of PWM signal
			delayMicroseconds(31);
			uint16_t c = analogRead(cPIN);      // Check the current through the IRON
			if (c > curr) curr = c;             // The maximum value
		}
		fastPWM.off();
		if (curr > min_curr * 2)                // Do not put big values in to the history 
			curr = min_curr * 2;                // This is enough to ensure the IRON is connected
		curr = current.average(curr);           // Calculate exponential average value of the current
	} else {
		curr = analogRead(cPIN);
	}
	disconnected = (curr < min_curr);

	if (mode == POWER_OFF || mode == POWER_COOLING) { // If the soldering IRON is set to be switched off
		fastPWM.off();                          // Surely power off the IRON
	}
	return disconnected;
}

// This routine is used to keep the IRON temperature near required value and is activated by the Timer1
void IRON::keepTemp(void) {
    uint16_t  ambient = analogRead(aPIN);       // Update ambient temperature
    amb_int.update(ambient);
    uint16_t t = analogRead(sPIN);              // Read the IRON temperature
    volatile uint16_t t_set = temp_set;         // The preset temperature depends on usual/low power mode
    if (temp_low) t_set = temp_low;
    
    if ((t >= temp_max + 20) || (t > (t_set + 100))) { // Prevent global over heating
        if (mode == POWER_ON) chill = true;     // Turn off the power in main working mode only;
    }
    if (t < temp_max) {                         // Do not save to the history readings when the IRON is disconnected
        if (--h_counter < 0) {
            h_temp.update(t);
            h_counter = h_max_counter;
        } 
    }

    int32_t p = 0;
    switch (mode) {
        case POWER_OFF:
            break;
        case POWER_COOLING:
            if (h_temp.average() < iron_cold)
                mode = POWER_OFF;
            break;
        case POWER_ON:
            if (chill) {
                if (t < (t_set - 2)) {
                    chill = false;
                    resetPID();
                } else {
                    break;
                }
            }
            p = PID::reqPower(t_set, t);
            p = constrain(p, 0, max_power);
            break;
        case POWER_FIXED:
            p = fix_power;
            break;
        default:
            break;
    }
    applied_power = p & 0xff;
    if (h_counter == 1) {
        h_power.update(applied_power);
    }
    fastPWM.duty(applied_power);
}

void IRON::fixPower(uint8_t Power) {
	if (Power == 0) {                         	// To switch off the IRON, set the Power to 0
		fix_power = 0;
		fastPWM.off();
        mode = POWER_COOLING;
		return;
	}

	if (Power > max_fixed_power)
		Power = max_fixed_power;
    fix_power = Power;
    mode = POWER_FIXED;
}

/*
 * Return ambient temperature in Celsius
 * Caches previous result to skip expensive calculations
 */
int16_t IRON::ambientTemp(void) {
static const uint16_t add_resistor  = 10030;                // The additional resistor value (10koHm)
static const float    normal_temp[2]= { 10000, 25 };        // nominal resistance and the nominal temperature
static const uint16_t beta          = 3950;                 // The beta coefficient of the thermistor (usually 3000-4000)
static int32_t  average             = -1;                   // Previous value of ambient temperature (readings on aPIN)
static int      cached_ambient      = ambient_tempC;        // Previous value of the temperature

    uint16_t a_temp = amb_int.read();                       // Average value of ambient temperature
    if (abs(a_temp - average) < 20)
        return cached_ambient;

    average = a_temp;
    if (average < 975) {                                    // prevent division by zero
        // convert the value to resistance
        float resistance = 1023.0 / (float)average - 1.0;
        resistance = (float)add_resistor / resistance;
        float steinhart = resistance / normal_temp[0];      // (R/Ro)
        steinhart = log(steinhart);                         // ln(R/Ro)
        steinhart /= beta;                                  // 1/B * ln(R/Ro)
        steinhart += 1.0 / (normal_temp[1] + 273.15);       // + (1/To)
        steinhart = 1.0 / steinhart;                        // Invert
        steinhart -= 273.15;                                // convert to Celsius
        cached_ambient = round(steinhart);
    } else {                                                // about -30 *C, perhaps, the IRON is disconnected
        cached_ambient  = ambient_tempC;
    }
    return cached_ambient;
}

void IRON::adjust(uint16_t t) {
    if (t > temp_max) t = temp_max;             // Do not allow over heating
    temp_set = t;
}

// If any switch is short, its status is 'true'
void IRON::checkSWStatus(void) {
    if (millis() > check_tilt_ms) {
        check_tilt_ms = millis() + 100;
        if (!disconnected) {                    // Current through the IRON is registered
            uint16_t avg = tilt.read();
            if (300 < avg && avg < 700) {       // Middle state
                avg = tilt.average(analogRead(tPIN));
                if (avg < 300 || avg > 700) {   // Toggle state
                    tilt_toggle = true;
                }
            } else {
                tilt.update(analogRead(tPIN));          
            }
        }
    }
}

bool IRON::isIronTiltSwitch(bool reed) {
    bool ret = tilt_toggle;
    tilt_toggle = false;
    if (reed) {
        return tilt.read() < 300;
    }
    return ret;
}

//------------------------------------------ class SCREEN ------------------------------------------------------
class SCREEN {
	public:
		SCREEN* next;                           // Pointer to the next screen
		SCREEN* nextL;                          // Pointer to the next Level screen, usually, setup
		SCREEN* main;                           // Pointer to the main screen
		SCREEN* no_iron;                       	// Pointer to the screen when the IRON was disconnected
		SCREEN() {
			next = nextL = main = no_iron = 0;
			update_screen  = 0;
			scr_timeout    = 0;
			time_to_return = 0;
		}
		virtual void init(void)                 { }
		virtual SCREEN* show(void)              { return this; }
		virtual SCREEN* menu(void)              { if (this->next != 0)  return this->next;  else return this; }
		virtual SCREEN* menu_long(void)      { if (this->nextL != 0) return this->nextL; else return this; }
		virtual void rotaryValue(int16_t value) { }
		bool    isSetup(void)                   { return (scr_timeout != 0); }
		void    forceRedraw(void)               { update_screen = 0; }
		virtual SCREEN* returnToMain(void) {
			if (main && scr_timeout && (millis() >= time_to_return)) {
				scr_timeout = 0;
				return main;
			}
			return this;
		}
		void resetTimeout(void) {
			if (scr_timeout > 0)
			time_to_return = millis() + scr_timeout*1000;
		}
		void setSCRtimeout(uint16_t t) {
			scr_timeout = t;
			resetTimeout(); 
		}
		bool wasRecentlyReset(void) {
			uint32_t to = (time_to_return - millis()) / 1000;
			return((scr_timeout - to) < 15);
		}
	protected:
		uint32_t update_screen;             	// Time in ms when the screen should be updated
		uint32_t scr_timeout;                   // Timeout is sec. to return to the main screen, canceling all changes
		uint32_t time_to_return;                // Time in ms to return to main screen
};

//---------------------------------------- class mainSCREEN [the soldering IRON is OFF] ------------------------
class mainSCREEN : public SCREEN {
	public:
		mainSCREEN(IRON* Iron, DSPL* DSP, RENC* ENC, BUZZER* Buzz, IRON_CFG* Cfg) {
			pIron = Iron;
			pD    = DSP;
			pEnc  = ENC;
			pBz   = Buzz;
			pCfg  = Cfg;
		}
		virtual void    init(void);
		virtual SCREEN* show(void);
		virtual void    rotaryValue(int16_t value); // Setup the preset temperature
	private:
		IRON*     pIron;                      	// Pointer to the IRON instance
		DSPL*     pD;                           // Pointer to the DSPLay instance
		RENC*     pEnc;                         // Pointer to the rotary encoder instance
		BUZZER*   pBz;                          // Pointer to the simple buzzer instance
		IRON_CFG* pCfg;                         // Pointer to the configuration instance
		uint32_t  clear_used_ms;                // Time in ms when used flag should be cleared (if > 0)
		uint32_t  change_display;               // Time in ms when to switch display between preset temperature and tip name                    
		bool      used;                         // Whether the IRON was used (was hot)
		bool      cool_notified;                // Whether there was cold notification played
		bool      show_tip;                     // Whether show the tip name instead of preset temperature
		const uint16_t period = 1000;           // The period to update the screen
		const uint32_t cool_notify_period = 120000; // The period to display 'cool' message (ms)
		const uint16_t show_temp = 20000;       // The period in ms to show the preset temperature
};

void mainSCREEN::init(void) {
	pIron->switchPower(false);
	uint16_t temp_set = pIron->presetTemp();
    int16_t  ambient  = pIron->ambientTemp();
	temp_set = pCfg->tempToHuman(temp_set, ambient); // The preset temperature in the human readable units
	if (pCfg->isCelsius())
		pEnc->reset(temp_set, temp_minC, temp_maxC, 1, 5);
	else
		pEnc->reset(temp_set, temp_minF, temp_maxF, 1, 5);
	used = !pIron->isCold();
	cool_notified = !used;
	if (used) {                                 // the IRON was used, we should save new data in EEPROM
		pCfg->savePresetTempHuman(temp_set);
	}
	clear_used_ms = 0;
	forceRedraw();
	pD->clear();
	pD->msgOff();
	show_tip = false;
	change_display = millis() + show_temp;
}

void mainSCREEN::rotaryValue(int16_t value) {
    int16_t ambient = pIron->ambientTemp();
	uint16_t temp = pCfg->humanToTemp(value, ambient);
	pIron->setTemp(temp);
	pD->tSet(value, pCfg->isCelsius());
	uint32_t ms = millis();
	update_screen  = ms + period;
	change_display = ms + show_temp;
	show_tip = false;
}

SCREEN* mainSCREEN::show(void) {
	SCREEN* nxt = this;
	if (no_iron && pIron->checkIron()) {      	// Check that the IRON is connected
		nxt = no_iron;
	}
	if (millis() < update_screen) return nxt;
	update_screen = millis() + period;

	if (clear_used_ms && (millis() > clear_used_ms)) {
		clear_used_ms = 0;
		used = false;
	}
/*
	if (millis() > change_display) {
		show_tip = !show_tip;
		change_display = millis() + 2000;
		if (!show_tip) change_display += show_temp;
	}
*/
    int16_t ambient = pIron->ambientTemp();
//	if (show_tip) {
//		pD->tip(pCfg->tipName(), true);
//	} else {
		uint16_t temp_set = pIron->presetTemp();
		temp_set = pCfg->tempToHuman(temp_set, ambient);	// The preset temperature in the human readable units
		pD->tSet(temp_set, pCfg->isCelsius());
		pD->tip(pCfg->tipName(), true);
//	}
	pD->msgOff();
 
	uint16_t temp   = pIron->tempAverage();
	uint16_t tempH  = pCfg->tempToHuman(temp, ambient);
	if (pIron->isCold()) {
		if (used)
			pD->msgCold();
		else
			pD->tCurr(tempH,pCfg->isCelsius());
		if (used && !cool_notified) {
			pBz->lowBeep();
			cool_notified = true;
			clear_used_ms = millis() + cool_notify_period;
		}
	} else {
		pD->tCurr(tempH,pCfg->isCelsius());
	}
	return nxt;
}

//---------------------------------------- class tipSCREEN [tip is disconnected, choose new tip] ---------------
class tipSCREEN : public SCREEN {
	public:
		tipSCREEN(IRON* Iron, DSPL* DSP, RENC* ENC, IRON_CFG* Cfg) {
			pIron = Iron;
			pD    = DSP;
			pEnc  = ENC;
			pCfg  = Cfg;
		}
		virtual void    init(void);
		virtual SCREEN* show(void);
		virtual void    rotaryValue(int16_t value); // Select the tip
	private:
        uint8_t     old_tip;                    // previous tip index
		IRON*       pIron;                      // Pointer to the IRON instance
		DSPL*       pD;                         // Pointer to the DSPLay instance
		RENC*       pEnc;                       // Pointer to the rotary encoder instance
		IRON_CFG*   pCfg;                       // Pointer to the configuration instance                      
		const uint16_t period = 1000;          	// The period to update the screen
};

void tipSCREEN::init(void) {
	pIron->switchPower(false);
	old_tip = pCfg->tipIndex();
	pEnc->reset(old_tip, 0, pCfg->tipsLoaded(), 1, 1, true);// Select the tip by the rotary encoder
	forceRedraw();
	pD->clear();
	pD->msgSelectTip();
}

void tipSCREEN::rotaryValue(int16_t value) {
    if (value == old_tip) return;
	update_screen   = millis() + period;
    uint8_t index   = pCfg->nextTip(old_tip, value > old_tip);
	uint16_t temp   = pIron->presetTemp();      // Preset temperature in internal units
    int16_t ambient = pIron->ambientTemp();
	temp = pCfg->tempToHuman(temp, ambient);    // The temperature in human readable units (Celsius o Fahrenheit)
	index = pCfg->selectTip(index);
    old_tip = index;
	pEnc->write(index);
	temp = pCfg->humanToTemp(temp, ambient);    // Translate previously set temperature in human readable units into internal value
	pIron->setTemp(temp);                       // Install previously set temperature into the IRON by new tip calibration
    forceRedraw();
}

SCREEN* tipSCREEN::show(void) {
	SCREEN* nxt = this;
	if (no_iron && !pIron->checkIron()) {       // Check that the IRON is disconnected
		nxt = no_iron;
		pIron->initTempHistory();               // The new tip is connected, reset the temp history 
	}
	if (millis() < update_screen) return nxt;
	update_screen = millis() + period;
    
	pD->tip(pCfg->tipName(), false);
	pD->markCalibrated('*', pCfg->isCalibrated());
	return nxt;
}

//---------------------------------------- class actSCREEN [Toggle tip activation ] ----------------------------
class actSCREEN : public SCREEN {
    public:
        actSCREEN(IRON* Iron, DSPL* DSP, RENC* ENC, IRON_CFG* Cfg) {
            pIron = Iron;
            pD    = DSP;
            pEnc  = ENC;
            pCfg  = Cfg;
        }
        virtual void    init(void);
        virtual SCREEN* show(void);
        virtual SCREEN* menu(void);
        virtual void    rotaryValue(int16_t value); // Select the tip
    private:
        IRON*       pIron;                      // Pointer to the IRON instance
        DSPL*       pD;                         // Pointer to the DSPLay instance
        RENC*       pEnc;                       // Pointer to the rotary encoder instance
        IRON_CFG*   pCfg;                       // Pointer to the configuration instance
        const uint16_t period = 10000;          // The period to update the screen                     
};

void actSCREEN::init(void) {
    pIron->switchPower(false);
    char *n = pCfg->tipName();
    int8_t global_index = pCfg->index(n);       // Find current tip in the global tip array by the name
    if (global_index < 0) global_index = 0;
    pEnc->reset(global_index, 0, pCfg->tipsLoaded(), 1, 1, true);// Select the tip by the rotary encoder
    pD->clear();
    pD->msgActivateTip();
    pD->tip(pCfg->tipName(), false);
}

void actSCREEN::rotaryValue(int16_t value) {
    forceRedraw();
}

SCREEN* actSCREEN::menu(void) {
    uint8_t tip = pEnc->read();
    bool active = pCfg->toggleTipActivation(tip);
    forceRedraw();
    return this;
}

SCREEN* actSCREEN::show(void) {
    if (millis() < update_screen) return this;
    update_screen = millis() + period;
    uint8_t tip = pEnc->read();
    bool active = pCfg->isTipActive(tip);
    char tip_name[tip_name_sz+1];
    pCfg->name(tip_name, tip);
    tip_name[tip_name_sz] = '\0';
    pD->clear();
    pD->tip(tip_name, false);
    pD->markActivated('x', active);
    return this;
}

//---------------------------------------- class workSCREEN [the soldering IRON is ON] -------------------------
class workSCREEN : public SCREEN {
	public:
		workSCREEN(IRON* Iron, DSPL* DSP, RENC* Enc, BUZZER* Buzz, IRON_CFG* Cfg) {
			update_screen = 0;
			pIron = Iron;
			pD    = DSP;
			pBz   = Buzz;
			pEnc  = Enc;
			pCfg  = Cfg;
		}
		virtual void    init(void);
		virtual SCREEN* show(void);
		virtual void    rotaryValue(int16_t value); // Change the preset temperature
		virtual SCREEN* returnToMain(void);    	// Automatic power-off
	private:
        void        adjustPresetTemp(void);
        void        hwTimeout(uint16_t low_temp, bool tilt_active);
		IRON*       pIron;                      // Pointer to the IRON instance
		DSPL*       pD;                         // Pointer to the DSPLay instance
		BUZZER*     pBz;                        // Pointer to the simple Buzzer instance
		RENC*       pEnc;                       // Pointer to the rotary encoder instance
		IRON_CFG*   pCfg;                       // Pointer to the configuration instance
		bool        ready;                      // Whether the IRON have reached the preset temperature
        bool        lowpower_mode   = false;    // Whether hardware low power mode using tilt switch
        uint32_t    lowpower_time   = 0;        // Time when switch to standby power mode
		uint32_t    auto_off_notified;          // The time (in ms) when the automatic power-off was notified
        uint16_t    tempH = 0;                  // The preset temperature in human readable units
		const uint16_t period = 1000;           // The period to update the screen (ms)
};

void workSCREEN::init(void) {
	uint16_t temp_set = pIron->presetTemp();
    int16_t ambient   = pIron->ambientTemp();
	bool is_celsius   = pCfg->isCelsius();
	tempH             = pCfg->tempToHuman(temp_set, ambient);
	if (is_celsius)
		pEnc->reset(tempH, temp_minC, temp_maxC, 1, 5);
	else
		pEnc->reset(tempH, temp_minF, temp_maxF, 1, 5);
	pIron->switchPower(true);
	ready           = false;
    lowpower_mode   = false;
    lowpower_time   = 0;
    time_to_return  = 0;
	pD->clear();
	pD->tSet(tempH, is_celsius);
	pD->msgOn();
	uint16_t to = pCfg->getOffTimeout() * 60;
	this->setSCRtimeout(to);
	auto_off_notified = 0;
	forceRedraw();
	pD->tip(pCfg->tipName(), true);
}

void workSCREEN::rotaryValue(int16_t value) {   // Setup new preset temperature by rotating the encoder
    tempH = value;
	ready = false;
    lowpower_mode = false;
	pD->msgOn();
    int16_t ambient = pIron->ambientTemp();
	uint16_t temp = pCfg->humanToTemp(value, ambient); // Translate human readable temperature into internal value
	pIron->setTemp(temp);
	pD->tSet(value, pCfg->isCelsius());
	SCREEN::resetTimeout();
	update_screen = millis() + period;
}

SCREEN* workSCREEN::show(void) {
	SCREEN* nxt = this;
	if (millis() < update_screen) return nxt;
	update_screen = millis() + period;

	int16_t temp        = pIron->tempAverage();
	int16_t temp_set    = pIron->presetTemp();
    int16_t ambient     = pIron->ambientTemp();
	int tempH           = pCfg->tempToHuman(temp, ambient);
	pD->tCurr(tempH,pCfg->isCelsius());
	uint8_t p = pIron->appliedPower();
	pD->percent(p);

	uint16_t td = pIron->tempDispersion();
	uint16_t pd = pIron->powerDispersion();
	int ap      = pIron->getAvgPower();
    uint16_t low_temp = pCfg->lowTemp();        // 'Standby temperature' setup in the main menu

	if ((abs(temp_set - temp) < 3) && (pIron->tempDispersion() <= 10) && (ap > 0))  {
		if (!ready) {
			pBz->shortBeep();
			ready = true;
			pD->msgReady();
            if (low_temp)
                lowpower_time = millis() + (uint32_t)pCfg->lowTimeout() * 1000;
			update_screen = millis() + (period << 2);
			return this;
		}
	}

    bool tilt_active = false;
    if (low_temp) {
        tilt_active = pIron->isIronTiltSwitch(pCfg->isReedType());
    }

    // If the automatic power-off feature is enabled, check the IRON status
    if (low_temp && ready && pCfg->getOffTimeout()) {       // The IRON has reaches the preset temperature                         
        hwTimeout(low_temp, tilt_active);       // Use hardware tilt switch to turn low power mode
    }

    if (!lowpower_mode && pCfg->isAmbientSensor())
        adjustPresetTemp();
        
	uint32_t to = (time_to_return - millis()) / 1000;
	if (ready) {
		if (scr_timeout > 0 && (to < 100)) {
			pD->timeToOff(to);
			if (!auto_off_notified) {
				pBz->shortBeep();
				auto_off_notified = millis();
			}
		} else if (lowpower_mode) {
            pD->msgStandby();
		} else if (SCREEN::wasRecentlyReset()) {
			pD->msgWorking();
		} else {
			pD->msgReady();
		}
	} else {
		pD->msgOn();
        resetTimeout();
	}
	return nxt;
}

SCREEN* workSCREEN::returnToMain(void) {
	if (main && scr_timeout && (millis() >= time_to_return)) {
		scr_timeout = 0;
		pBz->doubleBeep();
		return main;
	}
	return this;
}

void workSCREEN::adjustPresetTemp(void) {
    uint16_t presetTemp = pIron->presetTemp();              // Preset temperature (internal units)
    int16_t  ambient    = pIron->ambientTemp();
    uint16_t temp       = pCfg->humanToTemp(tempH, ambient); // Expected temperature of IRON in internal units
    if (temp != presetTemp) {                               // The ambient temperature have changed, we need to adjust preset temperature
        pIron->adjust(temp);
    }
}

void workSCREEN::hwTimeout(uint16_t low_temp, bool tilt_active) {
    uint32_t now_ms = millis();
    if (tilt_active) {                                      // If the IRON is used, Reset standby time
        lowpower_time = now_ms + (uint32_t)pCfg->lowTimeout() * 1000; // Convert timeout to milliseconds
        if (lowpower_mode) {                                // If the IRON is in low power mode, return to main working mode
            pIron->lowPowerMode(0);
            lowpower_time   = 0;
            lowpower_mode   = false;
            ready           = false;
            pD->msgOn();
        }
    } else if (!lowpower_mode) {
        if (lowpower_time) {
            if (now_ms >= lowpower_time) {
                int16_t  ambient    = pIron->ambientTemp();
                uint16_t temp_low   = pCfg->lowTemp();
                uint16_t temp       = pCfg->humanToTemp(temp_low, ambient);
                pIron->lowPowerMode(temp);
                auto_off_notified   = false;
                lowpower_mode       = true;
                resetTimeout();                             // Activate automatic power-off
                return;
            }
        } else {
            lowpower_time = now_ms + (uint32_t)pCfg->lowTimeout() * 1000;
        }
    }
}

//---------------------------------------- class powerSCREEN [fixed power to the IRON] -------------------------
class powerSCREEN : public SCREEN {
	public:
		powerSCREEN(IRON* Iron, DSPL* DSP, RENC* Enc, IRON_CFG* CFG) {
			pIron = Iron;
			pD    = DSP;
			pEnc  = Enc;
			pCfg  = CFG;
			on    = false;
		}
		virtual void    init(void);
		virtual SCREEN* show(void);
		virtual void    rotaryValue(int16_t value);
		virtual SCREEN* menu(void);
		virtual SCREEN* menu_long(void);
	private:
		IRON*     pIron;                        // Pointer to the IRON instance
		DSPL*     pD;                           // Pointer to the DSPLay instance
		RENC*     pEnc;                         // Pointer to the rotary encoder instance
		IRON_CFG* pCfg;                         // Pointer to the configuration instance
		uint32_t  update_screen;                // Time in ms to update the screen
		bool on;                                // Whether the power of soldering IRON is on
		const uint16_t period = 1000;          	// The period in ms to update the screen
};

void powerSCREEN::init(void) {
	uint8_t p = pIron->getAvgPower();
	uint8_t max_power = pIron->getMaxFixedPower();
	pEnc->reset(p, 0, max_power, 1);
	on = true;                                 	// Do start heating immediately
	pIron->switchPower(false);
	pIron->fixPower(p);
	pD->clear();
	pD->pSet(p);
}

SCREEN* powerSCREEN::show(void) {
	SCREEN* nxt = this;
	if (no_iron && pIron->checkIron()) {       	// Check that the IRON is connected
		nxt = no_iron;
	}
	if (millis() < update_screen) return nxt;
	update_screen = millis() + period;
	uint16_t temp = pIron->tempAverage();
    int16_t ambient = pIron->ambientTemp();
	temp = pCfg->tempToHuman(temp, ambient);
	pD->tCurr(temp,pCfg->isCelsius());
	uint8_t p = pIron->appliedPower();
	pD->percent(p);
	return nxt;
}

void powerSCREEN::rotaryValue(int16_t value) {
	pD->pSet(value);
	pIron->fixPower(value);
	on = true;
	update_screen = millis() + (period * 2);
}

SCREEN* powerSCREEN::menu(void) {
	on = !on;
	if (on) {
		uint16_t pos = pEnc->read();
		pIron->fixPower(pos);
		pD->clear();
		pD->pSet(pos);
	} else {
		pIron->fixPower(0);
		pD->clear();
		pD->pSet(0);
	}
	forceRedraw();
	return this;
}

SCREEN* powerSCREEN::menu_long(void) {
	pIron->fixPower(0);
	if (nextL) {
		pIron->switchPower(true);
		return nextL;
	}
	return this;
}

//---------------------------------------- class errorSCREEN [the soldering IRON error detected] ---------------
class errorSCREEN : public SCREEN {
	public:
		errorSCREEN(IRON* Iron, DSPL* DSP, BUZZER* Buzz) {
		pIron = Iron;
		pD    = DSP;
		pBz   = Buzz;
		}
		virtual void init(void) { pIron->switchPower(false); pD->clear(); pD->msgFail(); pBz->failedBeep(); }
	private:
		IRON*    pIron;                       	// Pointer to the IRON instance
		DSPL*    pD;                            // Pointer to the display instance
		BUZZER*  pBz;                           // Pointer to the simple Buzzer instance
};

//---------------------------------------- class configSCREEN [configuration menu] -----------------------------
class configSCREEN : public SCREEN {
	public:
		configSCREEN(IRON* Iron, DSPL* DSP, RENC* Enc, IRON_CFG* Cfg, BUZZER* Buzz) {
			pIron = Iron;
			pD    = DSP;
			pEnc  = Enc;
			pCfg  = Cfg;
            pBz   = Buzz;
		}
		virtual void    init(void);
		virtual SCREEN* show(void);
		virtual void    rotaryValue(int16_t value);
		virtual SCREEN* menu(void);
		virtual SCREEN* menu_long(void);
		SCREEN* calib;                          // Pointer to the calibration SCREEN
        SCREEN* activate;                       // Pointer to the tip activation SCREEN
	private:
		IRON*     	pIron;                      // Pointer to the IRON instance
		DSPL*     	pD;                         // Pointer to the DSPLay instance
		RENC*  	    pEnc;                       // Pointer to the rotary encoder instance
		IRON_CFG* 	pCfg;                       // Pointer to the config instance
        BUZZER*     pBz;                        // Pointer to the buzzer instance
		uint8_t 	mode;                       // Which parameter to change
		bool 		tune;                       // Whether the parameter is modifying
		bool 		changed;                    // Whether some configuration parameter has been changed
		bool 		cels;                       // Current Celsius/Fahrenheit;
        bool        buzzer;                     // Buzzer ON/OFF
        bool        reed;                       // The hardware switch type: reed/tilt
        bool        ambient;                    // The ambient temperature sensor enabled/disabled
		uint8_t		off_timeout;                // Automatic switch-off timeout in minutes
        uint16_t    low_temp;                   // Standby temperature
        uint8_t     low_timeout;                // Standby timeout
		const uint16_t period = 10000;          // The period in ms to update the screen
};

void configSCREEN::init(void) {
	mode = 0;
    // 0 - C/F, 1 - buzzer, 2 - switch type, 3 - ambient, 4 - standby temp, 5 - standby time,
    // 6 - off-timeout, 7 - tip calibrate, 8 - atcivate tip, 9 - tune, 10 - save, 11 - cancel
	pEnc->reset(mode, 0, 11, 1, 0, true);
	tune        = false;
	changed     = false;
	cels        = pCfg->isCelsius();
    buzzer      = pCfg->isBuzzer();
    reed        = pCfg->isReedType();
    ambient     = pCfg->isAmbientSensor();
	off_timeout = pCfg->getOffTimeout();
    low_temp    = pCfg->lowTemp();
    low_timeout = pCfg->lowTimeout();
	pD->clear();
	pD->setupMode(0, false, 0);
	this->setSCRtimeout(30);
}

SCREEN* configSCREEN::show(void) {
	if (millis() < update_screen) return this;
	update_screen = millis() + period;
	switch (mode) {
		case 0:                                 // C/F
			pD->setupMode(mode, false, cels);
			break;
		case 1:                                 // buzzer
            pD->setupMode(mode, tune, buzzer);
			break;
        case 2:                                 // switch type
            pD->setupMode(mode, tune, reed);
            break;
        case 3:                                 // ambient temperature sensor
            pD->setupMode(mode, tune, ambient);
            break;
		case 4:                                 // standby temp
            pD->setupMode(mode, tune, low_temp);
            break;
        case 5:                                 // standby timeout
            pD->setupMode(mode, tune, low_timeout);
            break;
        case 6:                                 // off-timeout
            pD->setupMode(mode, tune, off_timeout);
            break;
        default:
			pD->setupMode(mode, false, 0);
			break;
	}
	return this;
}

void configSCREEN::rotaryValue(int16_t value) {
	if (tune) {                               	// tune the temperature units
		changed = true;
		switch (mode) {
            case 1:                             // Buzzer
                buzzer = value; 
                break;
            case 2:                             // Switch type
                reed = value;
                break;
            case 3:                             // ambient temperature sensor
                ambient = value;
                break;
            case 4:                             // standby temperature
                if (value > 0)
                    value += 179;
                low_temp = value;
                break;
            case 5:                             // Standby Time
                low_timeout = value;
                break; 
			case 6:                            	// tuning the switch-off timeout
				if (value > 0) value += 2;      // The minimum timeout is 3 minutes
				off_timeout = value;
				break;
			default:
				break;
		}
	} else {
		mode = value;
	}
	forceRedraw();
}

SCREEN* configSCREEN::menu(void) {
	if (tune) {
		tune = false;
		pEnc->reset(mode, 0, 11, 1, 0, true);   // The value has been tuned, return to the menu list mode
	} else {
		switch (mode) {
            case 0:                             // C/F. In-line editing
                cels = !cels;
                changed = true;
                forceRedraw();
                return this;
            case 1:                             // Buzzer
                pEnc->reset(buzzer, 0, 1, 1, 0, true);
                break;
            case 2:                             // Switch type
                pEnc->reset(reed, 0, 1, 1, 0, true);
                break;
            case 3:                             // ambient temperature sensor
                pEnc->reset(ambient, 0, 1, 1, 0, true);
                break;
            case 4:                             // standby temperature
                {
                uint16_t v = low_temp;
                if (v > 0) v -= 179;
                pEnc->reset(v, 0, 120, 1, 5, false);
                }
                break;
            case 5:                             // standby timeout
                pEnc->reset(low_timeout, 10, 255, 1, 5, false);
                break;
            case 6:                             // off-timeout
                {
                int v = off_timeout;
                if (v > 0) v -= 2;
                pEnc->reset(v, 0, 28, 1, 5, false);
                }
                break;
			case 7:
				if (calib) return calib;
				break;
            case 8:                             // Activate tip
                if (activate) return activate;
                break;
			case 9:                           	// Tune potentiometer
				if (next) return next;
				break;
			case 10:                           	// Save configuration data
				menu_long();
			case 11:                            // Return to the main menu
				if (main) return main;
				return this;
		}
		tune = true;
	}
	forceRedraw();
	return this;
}

SCREEN* configSCREEN::menu_long(void) {
	if (nextL) {
		if (changed) {
            pCfg->setLowPower(low_temp, low_timeout, reed);
			pCfg->saveConfig(off_timeout, cels, buzzer, ambient);
            pBz->activate(buzzer);
		}
		return nextL;
	}
	return this;
}

//---------------------------------------- class calibSCREEN [ tip calibration ] -------------------------------
#define MCALIB_POINTS	8
class calibSCREEN : public SCREEN {
	public:
		calibSCREEN(IRON* Iron, DSPL* DSP, RENC* Enc, IRON_CFG* Cfg, BUZZER* Buzz) {
			pIron = Iron;
			pD    = DSP;
			pEnc  = Enc;
			pCfg  = Cfg;
			pBz   = Buzz;
		}
		virtual void    init(void);
		virtual SCREEN* show(void);
		virtual void    rotaryValue(int16_t value);
		virtual SCREEN* menu(void);
		virtual SCREEN* menu_long(void);
	private:
		bool		calibrationOLS(uint16_t* tip, uint16_t min_temp, uint16_t max_temp);
		uint8_t 	closestIndex(uint16_t temp);
        void        updateReference(uint8_t indx);
		IRON*		pIron;                      // Pointer to the IRON instance
		DSPL*		pD;                         // Pointer to the DSPLay instance
		RENC*	    pEnc;                       // Pointer to the rotary encoder instance
		IRON_CFG*	pCfg;                       // Pointer to the config instance
		BUZZER*		pBz;                        // Pointer to the buzzer instance
		uint16_t	calib_temp[2][MCALIB_POINTS];	// The calibration data: real temp. [0] and temp. in internal units [1]
		uint8_t		ref_temp_index	= 0;		// Which temperature reference to change: [0-MCALIB_POINTS]
		uint16_t	tip_temp_max	= 0;		// the maximum possible tip temperature in the internal units
		uint16_t	preset_temp;                // The preset temp in human readable units
		bool		cels;                       // Current Celsius/Fahrenheit;
		bool		ready;                      // Ready to enter real temperature
		const uint16_t t_diff = 60;             // The adjustment could be in the interval [t_ref-t_diff; t_ref+2*t_diff]
		const uint32_t period = 1000;          	// Update screen period
		const uint16_t start_int_temp = 200;	// Minimal temperature in internal units, about 100 degrees Celsius
};

void calibSCREEN::init(void) {
	pIron->switchPower(false);
	// Prepare to enter real temperature
	uint16_t min_t 		= 50;
	uint16_t max_t		= 600;
	if (!pCfg->isCelsius()) {
		min_t 	=  122;
		max_t 	= 1111;
	}
	pEnc->reset(0, min_t, max_t, 1, 5, false);
    tip_temp_max = temp_max / 2;
	for (uint8_t i = 0; i < MCALIB_POINTS; ++i) {
		calib_temp[0][i] = 0;					// Real temperature. 0 means not entered yet
		calib_temp[1][i] = map(i, 0, MCALIB_POINTS-1, start_int_temp, tip_temp_max); // Internal temperature
	}
	ready 			= false;                    // Not ready to enter real temperature
	ref_temp_index	= 0;
	pD->tRef(ref_temp_index);
	preset_temp = pIron->presetTemp();          // Save the preset temperature in human readable units
    int16_t ambient = pIron->ambientTemp();
	preset_temp = pCfg->tempToHuman(preset_temp, ambient);
    pD->clear();
    pD->msgOff();
	forceRedraw();
}

SCREEN* calibSCREEN::show(void) {
	if (millis() < update_screen) return this;
	update_screen = millis() + period;
	int16_t temp		= pIron->tempAverage(); // Actual IRON temperature
	int16_t temp_set	= pIron->presetTemp();
    int16_t ambient     = pIron->ambientTemp();
    uint16_t tempH      = pCfg->tempToHuman(temp, ambient);
	if (ready) {
		pD->tSet(tempH,pCfg->isCelsius());
	} else {                                  // Show the current Iron temperature
        pD->tCurr(tempH,pCfg->isCelsius());
	}
	uint8_t p = pIron->appliedPower();
	if (!pIron->isOn()) p = 0;
	pD->percent(p);
	if (! ready && abs(temp_set - temp) < 4 && pIron->tempDispersion() <= 20)  {
		pBz->shortBeep();
		pD->msgReady();
	    ready = true;
        pEnc->write(tempH);
	}
	if (ready && !pIron->isOn()) {          	// The IRON was switched off by error
		pD->msgOff();
		ready = false;
	}
	return this;
}

void calibSCREEN::rotaryValue(int16_t value) {	// The Encoder rotated
	update_screen = millis() + period;
	if (ready) {                               	// change the real value for the temperature
		pD->tCurr(value,pCfg->isCelsius());
	}
}

SCREEN* calibSCREEN::menu(void) {				// Rotary encoder pressed
	if (ready) {                                // The real temperature has been entered
		uint16_t r_temp = pEnc->read();
		uint16_t temp   = pIron->tempAverage();	// The temperature of the IRON in internal units
		pIron->switchPower(false);
		pD->msgOff();
		if (!cels)                                // Always save the human readable temperature in Celsius
			r_temp = map(r_temp, temp_minF, temp_maxF, temp_minC, temp_maxC);
		calib_temp[0][ref_temp_index] = r_temp;
		calib_temp[1][ref_temp_index] = temp;
		if (r_temp < temp_maxC - 20) {
			updateReference(ref_temp_index);	// Update reference temperature points
			++ref_temp_index;
			// Try to update the current tip calibration
			uint16_t tip[3];
			if (calibrationOLS(tip, 150, 600)) {
				pCfg->applyCalibration(tip);
			}
		}
		if ((r_temp >= temp_maxC - 20) || ref_temp_index >= MCALIB_POINTS) {
			return menu_long();                 // Finish calibration
		} else {								// Continue calibration
			uint16_t temp = calib_temp[1][ref_temp_index];
			pIron->setTemp(temp);
			pIron->switchPower(true);	
		}
	} else {									// Toggle the power
		if (pIron->isOn()) {
			pIron->switchPower(false);
			pD->msgOff();
		} else {
			pIron->switchPower(true);
			pD->msgOn();
		}
	}
	pD->tRef(ref_temp_index);
	ready = false;
	forceRedraw();
	return this;
}

SCREEN* calibSCREEN::menu_long(void) {    	// Save new tip calibration data
	pIron->switchPower(false);
	uint16_t tip[3];
	if (calibrationOLS(tip, 150, temp_maxC)) {
		uint8_t near_index	= closestIndex(temp_tip[2]);
		tip[2] = map(temp_tip[2], temp_tip[1], calib_temp[0][near_index],
				tip[1], calib_temp[1][near_index]);
		if (tip[2] > temp_max) tip[2] = temp_max;

		pCfg->applyCalibration(tip);
		pCfg->saveCalibrationData(tip, ambient_tempC);
	}
	pCfg->savePresetTempHuman(preset_temp);
    int16_t ambient = pIron->ambientTemp();
	uint16_t temp = pCfg->humanToTemp(preset_temp, ambient);
	pIron->setTemp(temp);
	if (nextL) return nextL;
	return this;
}

/*
 * Calculate tip calibration parameter using linear approximation by Ordinary Least Squares method
 * Y = a * X + b, where
 * Y - internal temperature, X - real temperature. a and b are double coefficients
 * a = (N * sum(Xi*Yi) - sum(Xi) * sum(Yi)) / ( N * sum(Xi^2) - (sum(Xi))^2)
 * b = 1/N * (sum(Yi) - a * sum(Xi))
 */
bool calibSCREEN::calibrationOLS(uint16_t* tip, uint16_t min_temp, uint16_t max_temp) {
	long sum_XY = 0;							// sum(Xi * Yi)
	long sum_X 	= 0;							// sum(Xi)
	long sum_Y  = 0;							// sum(Yi)
	long sum_X2 = 0;							// sum(Xi^2)
	long N		= 0;

	for (uint8_t i = 0; i < MCALIB_POINTS; ++i) {
		uint16_t X 	= calib_temp[0][i];
		uint16_t Y	= calib_temp[1][i];
		if (X >= min_temp && X <= max_temp) {
			sum_XY 	+= X * Y;
			sum_X	+= X;
			sum_Y   += Y;
			sum_X2  += X * X;
			++N;
		}
	}

	if (N <= 2)									// Not enough real temperatures have been entered
		return false;

	double	a  = (double)N * (double)sum_XY - (double)sum_X * (double)sum_Y;
			a /= (double)N * (double)sum_X2 - (double)sum_X * (double)sum_X;
	double 	b  = (double)sum_Y - a * (double)sum_X;
			b /= (double)N;

	for (uint8_t i = 0; i < 3; ++i) {
		double temp = a * (double)temp_tip[i] + b;
		tip[i] = round(temp);
	}
	if (tip[2] > temp_max) tip[2] = temp_max;
	return true;
}

// Find the index of the reference point with the closest temperature
uint8_t calibSCREEN::closestIndex(uint16_t temp) {
	uint16_t diff = 1000;
	uint8_t index = MCALIB_POINTS;
	for (uint8_t i = 0; i < MCALIB_POINTS; ++i) {
		uint16_t X = calib_temp[0][i];
		if (X > 0 && abs(X-temp) < diff) {
			diff = abs(X-temp);
			index = i;
		}
	}
	return index;
}

void calibSCREEN::updateReference(uint8_t indx) {                    // Update reference points
    uint16_t expected_temp  = map(indx, 0, MCALIB_POINTS, temp_minC, temp_maxC);
    uint16_t r_temp         = calib_temp[0][indx];
    if (indx < 5 && r_temp > (expected_temp + expected_temp/4)) {   // The real temperature is too high
        tip_temp_max -= tip_temp_max >> 2;                      // tip_temp_max *= 0.75;
        if (tip_temp_max < temp_max / 4)
            tip_temp_max = temp_max / 4;                        // Limit minimum possible value of the highest temperature
    } else if (r_temp > (expected_temp + expected_temp/8)) {    // The real temperature is biger than expected
        tip_temp_max += tip_temp_max >> 3;                      // tip_temp_max *= 1.125;
        if (tip_temp_max > temp_max)
            tip_temp_max = temp_max;
    } else if (indx < 5 && r_temp < (expected_temp - expected_temp/4)) { // The real temperature is too low
        tip_temp_max += tip_temp_max >> 2;                      // tip_temp_max *= 1.25;
        if (tip_temp_max > temp_max)
            tip_temp_max = temp_max;
    } else if (r_temp < (expected_temp - expected_temp/8)) {    // The real temperature is lower than expected
        tip_temp_max += tip_temp_max >> 3;                      // tip_temp_max *= 1.125;
        if (tip_temp_max > temp_max)
            tip_temp_max = temp_max;
    } else {
        return;
    }
    // rebuild the array of the reference temperatures
    for (uint8_t i = indx+1; i < MCALIB_POINTS; ++i) {
        calib_temp[1][i] = map(i, 0, MCALIB_POINTS-1, start_int_temp, tip_temp_max);
    }

}

//---------------------------------------- class tuneSCREEN [tune the potentiometer ] --------------------------
class tuneSCREEN : public SCREEN {
	public:
		//tuneSCREEN(IRON* Iron, DSPL* DSP, RENC* ENC, BUZZER* Buzz) {
	    tuneSCREEN(IRON* Iron, DSPL* DSP, RENC* ENC, BUZZER* Buzz, IRON_CFG* CFG) {
			pIron = Iron;
			pD    = DSP;
			pEnc  = ENC;
			pBz   = Buzz;
		    pCfg  = CFG;
		}
		virtual void    init(void);
		virtual SCREEN* menu(void);
		virtual SCREEN* menu_long(void);
		virtual SCREEN* show(void);
		virtual void    rotaryValue(int16_t value);
	private:
		IRON*    pIron;							// Pointer to the IRON instance
		DSPL*    pD;                            // Pointer to the display instance
		RENC*   pEnc;                           // Pointer to the rotary encoder instance
		BUZZER*  pBz;                           // Pointer to the simple Buzzer instance
	    IRON_CFG* pCfg;                         // Pointer to the configuration instance
		bool     arm_beep;                      // Whether beep is armed
		uint8_t  max_power;                     // Maximum possible power to be applied
		const uint16_t period = 1000;           // The period in ms to update the screen
};

void tuneSCREEN::init(void) {
	pIron->switchPower(false);
	max_power = pIron->getMaxFixedPower();
	pEnc->reset(15, 0, max_power, 1, 5);        // Rotate the encoder to change the power supplied
	arm_beep = false;
	pD->clear();
	pD->msgTune();
	forceRedraw();
}

void tuneSCREEN::rotaryValue(int16_t value) {
	pIron->fixPower(value);
	forceRedraw();
}

SCREEN* tuneSCREEN::show(void) {
	if (millis() < update_screen) return this;
	update_screen = millis() + period;
	int16_t temp = pIron->tempAverage();
	//
    int16_t ambient     = pIron->ambientTemp();
	int tempH           = pCfg->tempToHuman(temp, ambient);
	pD->tCurrTune(tempH,pCfg->isCelsius());
	//
	pD->tCurr(temp);
	uint8_t power = pEnc->read();				// applied power
	if (!pIron->isOn())
		power = 0;
	else
		power = map(power, 0, max_power, 0, 100);
	pD->percent(power);
	if (arm_beep && (pIron->tempDispersion() < 5)) {
		pBz->shortBeep();
		arm_beep = false;
	}
	return this;
}
  
SCREEN* tuneSCREEN::menu(void) {                // The rotary button pressed
	if (pIron->isOn()) {
		pIron->fixPower(0);
	} else {
		uint8_t power = pEnc->read();			// applied power
		pIron->fixPower(power);
	}
	return this;
}

SCREEN* tuneSCREEN::menu_long(void) {
	pIron->fixPower(0);							// switch off the power
	if (next) return next;
	return this;
}

//---------------------------------------- class pidSCREEN [tune the PID coefficients] -------------------------
class pidSCREEN : public SCREEN {
	public:
		pidSCREEN(IRON* Iron, RENC* ENC) {
			pIron = Iron;
			pEnc  = ENC;
		}
		virtual void    init(void);
		virtual SCREEN* menu(void);
		virtual SCREEN* menu_long(void);
		virtual SCREEN* show(void);
		virtual void    rotaryValue(int16_t value);
	private:
		void     showCfgInfo(void);				// show the main config information: Temp set and PID coefficients
		IRON*    pIron;							// Pointer to the IRON instance
		RENC*    pEnc;							// Pointer to the rotary encoder instance
		uint8_t  mode;							// Which temperature to tune [0-3]: select, Kp, Ki, Kd
		uint32_t update_screen;					// Time in ms when update the screen (print nre info)
		int      temp_set;
		const uint16_t period = 500;
};

void pidSCREEN::init(void) {
	temp_set = pIron->presetTemp();
	mode = 0;									// select the element from the list
	pEnc->reset(1, 1, 4, 1, 1, true);			// 1 - Kp, 2 - Ki, 3 - Kd, 4 - temp 
	showCfgInfo();
	Serial.println("");
}

void pidSCREEN::rotaryValue(int16_t value) {
	if (mode == 0) {							// No limit is selected, list the menu
		showCfgInfo();
		switch (value) {
			case 1:
				Serial.println("Kp");
				break;
			case 2:
				Serial.println("Ki");
				break;
			case 4:
				Serial.println(F("Temp"));
				break;
			case 3:
			default:
				Serial.println("Kd");
				break;
		}
	} else {
		switch (mode) {
			case 1:
				Serial.print(F("Kp = "));
				pIron->changePID(mode, value);
				break;
			case 2:
				Serial.print(F("Ki = "));
				pIron->changePID(mode, value);
				break;
			case 4:
				Serial.print(F("Temp = "));
				temp_set = value;
				pIron->setTemp(value);
				break;
			case 3:
			default:
				Serial.print(F("Kd = "));
				pIron->changePID(mode, value);
				break;
		}
		Serial.println(value);
	}
}

SCREEN* pidSCREEN::show(void) {
	if (millis() < update_screen) return this;
	update_screen = millis() + period;
	if (pIron->isOn()) {
		char buff[60];
		int temp     = pIron->currTemp();
		uint8_t  pwr = pIron->getAvgPower();
		uint16_t td  = pIron->tempDispersion();
		uint16_t pd  = pIron->powerDispersion();
		sprintf(buff, "%3d: power = %3d, td = %3d, pd = %3d --- ", temp_set - temp, pwr, td, pd);
		Serial.println(buff);
	}
	return this;
}

SCREEN* pidSCREEN::menu(void) {					// The rotary button pressed
	if (mode == 0) {							// select upper or lower temperature limit
		mode = pEnc->read();
		if (mode != 4) {
			int k = pIron->changePID(mode, -1);
			pEnc->reset(k, 0, 10000, 1, 10);
		} else {
			pEnc->reset(temp_set, 0, 970, 1, 5);
		}
	} else {									// upper or lower temperature limit just setup     
		mode = 0;
		pEnc->reset(1, 1, 4, 1, 1, true);		// 1 - Kp, 2 - Ki, 3 - Kd, 4 - temp
	}
	return this;
}

SCREEN* pidSCREEN::menu_long(void) {
	bool on = pIron->isOn();
	pIron->switchPower(!on);
	if (on)
		Serial.println("The iron is OFF");
	else
		Serial.println("The iron is ON");
	return this;
}

void pidSCREEN::showCfgInfo(void) {
	Serial.print(F("Temp set: "));
	Serial.print(temp_set, DEC);
	Serial.print(F(", PID: ["));
	for (uint8_t i = 1; i < 4; ++i) {
		int k = pIron->changePID(i, -1);
		Serial.print(k, DEC);
		if (i < 3) Serial.print(", ");
	}
	Serial.print("]; ");
}
//=================================== End of class declarations ================================================

DSPL		disp;
RENC        encoder(R_MAIN_PIN, R_SECD_PIN, R_BUTN_PIN);
IRON		iron(probePIN, checkPIN, termisPIN, tiltswPIN);
IRON_CFG	ironCfg(MAX_CUSTOM_TIPS);               // See config.h
BUZZER		simpleBuzzer(buzzerPIN);
TIPS        tips;

mainSCREEN		offScr(&iron, &disp, &encoder, &simpleBuzzer, &ironCfg);
tipSCREEN		selScr(&iron, &disp, &encoder, &ironCfg);
workSCREEN		wrkScr(&iron, &disp, &encoder, &simpleBuzzer, &ironCfg);
errorSCREEN		errScr(&iron, &disp, &simpleBuzzer);
powerSCREEN		pwrScr(&iron, &disp, &encoder, &ironCfg);
configSCREEN	cfgScr(&iron, &disp, &encoder, &ironCfg, &simpleBuzzer);
calibSCREEN		tipScr(&iron, &disp, &encoder, &ironCfg, &simpleBuzzer);
actSCREEN       actScr(&iron, &disp, &encoder, &ironCfg);
//tuneSCREEN		tuneScr(&iron, &disp, &encoder, &simpleBuzzer);
tuneSCREEN		tuneScr(&iron, &disp, &encoder, &simpleBuzzer, &ironCfg);
//pidSCREEN		pidScr(&iron, &encoder);

SCREEN *pCurrentScreen = &offScr;
//SCREEN *pCurrentScreen = &pidScr;

/*
 * The timer1 overflow interrupt handler.
 * Activates the procedure for IRON current check or for IRON temperature check
 * Interrupt routine on Timer1 overflow, @31250 Hz, 32 microseconds is a timer period
 * keepTemp() function takes 353 mks, about 12 ticks of TIMER1;
 * We should wait for 33 timer ticks before checking the temperature after iron was powered off
 */
const uint32_t period_ticks = (31250 * temp_check_period)/1000-33-12;
ISR(TIMER1_OVF_vect) {
	if (iron_off) {									// The IRON is switched off, we need to check the temperature
		if (++tmr1_count >= 33) {					// about 1 millisecond
			TIMSK1 &= ~_BV(TOIE1);					// disable the overflow interrupts
			iron.keepTemp();						// Check the temp. If on, keep the temperature
			tmr1_count = 0;
			iron_off = false;
			TIMSK1 |= _BV(TOIE1);					// enable the the overflow interrupts
		}
	} else {										// The IRON is on, check the current and switch-off the IRON
		if (++tmr1_count >= period_ticks) {
			TIMSK1 &= ~_BV(TOIE1);					// disable the overflow interrupts
			tmr1_count = 0;
			OCR1B      = 0;							// Switch-off the power to check the temperature
			PORTB     &= ~heaterBIT;
			iron_off   = true;
			TIMSK1    |= _BV(TOIE1);				// enable the overflow interrupts
		}
	}
}

// the setup routine runs once when you press reset:
void setup() {
//	Serial.begin(115200);
	disp.init();

	// Load configuration parameters
	ironCfg.init();
	iron.init();
	uint16_t temp   = ironCfg.tempPresetHuman();
    int16_t ambient = 0;
    for (uint8_t i = 0; i < 10; ++i) {
        int16_t amb = iron.ambientTemp();
        if (amb == ambient) break;
        delay(500);
        ambient = amb;
    }
    temp = ironCfg.humanToTemp(temp, ambient);
	iron.setTemp(temp);
    simpleBuzzer.activate(ironCfg.isBuzzer());

	// Initialize rotary encoder
	encoder.init();
	delay(500);
	attachInterrupt(digitalPinToInterrupt(R_MAIN_PIN), rotEncChange,   CHANGE);

	// Initialize SCREEN hierarchy
	offScr.next     = &wrkScr;
	offScr.nextL    = &cfgScr;
	offScr.no_iron  = &selScr;
	wrkScr.next     = &offScr;
	wrkScr.nextL    = &pwrScr;
	wrkScr.main     = &offScr;
	errScr.next     = &offScr;
	errScr.nextL    = &offScr;
	pwrScr.nextL    = &wrkScr;
	pwrScr.no_iron  = &errScr;
	cfgScr.next     = &tuneScr;
	cfgScr.nextL    = &offScr;
	cfgScr.main     = &offScr;
	cfgScr.calib    = &tipScr;
    cfgScr.activate = &actScr;
	tipScr.nextL    = &offScr;
	tuneScr.next    = &cfgScr;
	tuneScr.main    = &offScr;
	selScr.nextL    = &cfgScr;
	selScr.no_iron  = &offScr;
    actScr.main     = &offScr;
    actScr.nextL    = &offScr;
	pCurrentScreen->init();
}

// Encoder interrupt handler
static void rotEncChange(void) {
    encoder.encoderIntr();
}

// The main loop
void loop() {
	static int16_t  old_pos = encoder.read();
  
	SCREEN* nxt = pCurrentScreen->returnToMain();
	if (nxt != pCurrentScreen) {				// return to the main screen by timeout
		pCurrentScreen = nxt;
		pCurrentScreen->init();
	}

	int16_t pos = encoder.read();
	if (old_pos != pos) {
		pCurrentScreen->rotaryValue(pos);
		old_pos = pos;
		if (pCurrentScreen->isSetup())
			pCurrentScreen->resetTimeout();
	}

	uint8_t bStatus = encoder.buttonCheck();
	switch (bStatus) {
		case 2:									// int32_t press;
			nxt = pCurrentScreen->menu_long();
			if (nxt != pCurrentScreen) {
				pCurrentScreen = nxt;
				pCurrentScreen->init();
			} else {
				if (pCurrentScreen->isSetup())
					pCurrentScreen->resetTimeout();
			}
			break;
		case 1:									// short press
			nxt = pCurrentScreen->menu();
			if (nxt != pCurrentScreen) {
				pCurrentScreen = nxt;
				pCurrentScreen->init();
			} else {
				if (pCurrentScreen->isSetup())
					pCurrentScreen->resetTimeout();
			}
			break;
		case 0:									// Not pressed
		default:
			break;
	}

	nxt = pCurrentScreen->show();
	if (nxt && pCurrentScreen != nxt) {			// Be paranoiac, the returned value must not be null 
		pCurrentScreen = nxt;
		pCurrentScreen->init();
	}

    iron.checkSWStatus();
}

Please post a link to the project.

https://www.youtube.com/watch?v=5W3Ysm1ktg4&list=LL&index=10&t=880s&ab_channel=sasiskas

i already assemble all the parts, spend alot of money and now, the only thing that i cannot do is put the script running..

I got version 2 to compile after installing some standard libraries and the microLiquidCrystal_I2C library from GitHub.

[edit]

I could not find this microLiquidCrystal library in the package manager, so I created a folder in Arduino/libraries named microLiquidCrystal_I2C and placed the source and header file there.

Also, I noticed this in the sketch,

in case you expect output in the serial console once you manage to upload the sketch.

Bloco de Citação
I could not find this microLiquidCrystal library in the package manager, so I created a folder in Arduino/libraries named microLiquidCrystal_I2C and placed the source and header file there.

Thanks. Already done that.

Bloco de Citação
void setup() {
// Serial.begin(115200);

Where do i copy this to ? Btw, the errors/warning messages are still apearing. Is that normal ?

Uploaded the code. i've changed the "speed thing" in monitor serie to 115200 and pressed reset but still nothing..

You do not need to do anything, but if you want to see any messages that are written to the serial console, you should uncomment (remove the // from) the first line of the setup function. After this it will look like:

void setup() {
  Serial.begin(115200);

and set the speed of the serial monitor to 115200, but you already figured that out.

If you have configured the IDE to show warnings, this is normal (for this sketch). I see a lot of them too when I compile this particular sketch.

Sorry for leaving the conversation half way through, but the forum limited me to 20 posts because I'm a new user. Could you please check if anything appears on the serial monitor? Is it supposed to appear or only on the LCD display module ? From the video I've seen, letters are supposed to appear as soon as the soldering station is turned on. My question is, where am I going wrong because I don't see anything on the LCD display module or on the serial monitor. In advance let me thank you for the time and availability you are having with me.

You need trust level 1, you can get there by:

  • Entering at least 5 topics
  • Reading at least 30 posts
  • Spend a total of 10 minutes reading posts

Users at trust level 1 can…

  • Use all core Discourse functions; all new user restrictions are removed
  • Send PMs
  • Upload images and attachments

I do not see any activity either, here I put everything in an online simulator. My guess is that it is running out of memory.

What type of Arduino are you planning to use?

[edit]

Hmm, the sketch already seems to hang at the LCD initialisation, so it is probably not a memory issue.

I've tryed in a arduino mini and a arduino nano. the tutorial in youtube use a arduino mini. I've seen the coments of the video of some people that build one of those and no one talks about that.. strange..

From private communication I understand that the LCD module is plain, not an I2C.

With some modification (attached) and the LiquidCrystal library installed, the simulation now has a working display.

dspl_1602.cpp (9.6 KB)
dspl_1602.h (3.3 KB)

I replace the 2 files that you provide but now i have new error:

Arduino: 1.8.19 (Windows 10), Placa:"Arduino Nano, ATmega328P (Old Bootloader)"

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.cpp:4:49: warning: default argument given for parameter 2 of 'BUTTON::BUTTON(uint8_t, uint16_t)' [-fpermissive]

BUTTON::BUTTON(uint8_t b_pin, uint16_t to = 3000) {

                                             ^

In file included from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.cpp:1:0:

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.h:8:9: note: previous specification in 'BUTTON::BUTTON(uint8_t, uint16_t)' here

     BUTTON(uint8_t b_pin, uint16_t timeout_ms = 3000);

     ^~~~~~

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino: In member function 'void IRON::init()':

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:251:21: warning: passing 'volatile EMP_AVERAGE' as 'this' argument discards qualifiers [-fpermissive]

 amb_int.length(4);

                 ^

In file included from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.h:3:0,

             from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:18:

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\stat.h:9:25: note: in call to 'void EMP_AVERAGE::length(uint8_t)'

     void            length(uint8_t h_length)        { emp_k = h_length; emp_data = 0; }

                     ^~~~~~

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino: In member function 'void IRON::keepTemp()':

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:325:27: warning: passing 'volatile EMP_AVERAGE' as 'this' argument discards qualifiers [-fpermissive]

 amb_int.update(ambient);

                       ^

In file included from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.h:3:0,

             from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:18:

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\stat.h:12:25: note: in call to 'void EMP_AVERAGE::update(int32_t)'

     void            update(int32_t value);

                     ^~~~~~

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino: In member function 'int16_t IRON::ambientTemp()':

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:398:36: warning: passing 'volatile EMP_AVERAGE' as 'this' argument discards qualifiers [-fpermissive]

 uint16_t a_temp = amb_int.read();                       // Average value of ambient temperature

                                ^

In file included from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\encoder.h:3:0,

             from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:18:

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\stat.h:13:25: note: in call to 'int32_t EMP_AVERAGE::read()'

     int32_t         read(void);

                     ^~~~

C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino: In function 'void setup()':

T12-soldering-stationV2:1698:12: error: no matching function for call to 'DSPL::init()'

disp.init();

        ^

In file included from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\dspl_1602.h:8:0,

             from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\config.h:20,

             from C:\Users\ruial\Downloads\6. T12\Version 2\T12-soldering-stationV2\T12-soldering-stationV2\T12-soldering-stationV2.ino:17:

C:\Users\ruial\Documents\Arduino\libraries\LiquidCrystal\src/LiquidCrystal.h:58:8: note: candidate: void LiquidCrystal::init(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)

void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,

    ^~~~

C:\Users\ruial\Documents\Arduino\libraries\LiquidCrystal\src/LiquidCrystal.h:58:8: note: candidate expects 12 arguments, 0 provided

Foram encontradas múltiplas bibliotecas para «LiquidCrystal.h»

Utilizado: C:\Users\ruial\Documents\Arduino\libraries\LiquidCrystal

Não utilizado: C:\Program Files (x86)\Arduino\libraries\LiquidCrystal

exit status 1

no matching function for call to 'DSPL::init()'

Somehow your sketch differs from the one I downloaded. What happens when you replace the following line in the setup function,

with:

disp.beginN();