/*
 * iron.c
 *
 *  Created on: Jan 12, 2021
 *      Author: David    Original work by Jose (PTDreamer), 2017
 */

#include "iron.h"
#include "buzzer.h"
#include "settings.h"
#include "main.h"
#include "tempsensors.h"
#include "voltagesensors.h"
#include "ssd1306.h"

volatile iron_t Iron;
typedef struct setTemperatureReachedCallbackStruct_t setTemperatureReachedCallbackStruct_t;

struct setTemperatureReachedCallbackStruct_t {
  setTemperatureReachedCallback callback;
  setTemperatureReachedCallbackStruct_t *next;
};

typedef struct currentModeChangedCallbackStruct_t currentModeChangedCallbackStruct_t;
struct currentModeChangedCallbackStruct_t {
  currentModeChanged callback;
  currentModeChangedCallbackStruct_t *next;
};
static currentModeChangedCallbackStruct_t *currentModeChangedCallbacks = NULL;
static setTemperatureReachedCallbackStruct_t *temperatureReachedCallbacks = NULL;



static void temperatureReached(uint16_t temp) {
  setTemperatureReachedCallbackStruct_t *s = temperatureReachedCallbacks;
  while(s) {
    if(s->callback) {
      s->callback(temp);
    }
    s = s->next;
  }
}

static void modeChanged(uint8_t newMode) {
  currentModeChangedCallbackStruct_t *s = currentModeChangedCallbacks;
  while(s) {
    s->callback(newMode);
    s = s->next;
  }
}


void ironInit(TIM_HandleTypeDef *delaytimer, TIM_HandleTypeDef *pwmtimer, uint32_t pwmchannel) {
  Iron.Pwm_Timer      = pwmtimer;
  Iron.Delay_Timer    = delaytimer;
  Iron.Pwm_Channel     = pwmchannel;
  Iron.Error.Flags    = noError;                                                              // Clear all errors

  if(systemSettings.settings.WakeInputMode==wakeInputmode_shake){                             // If in shake mode, apply init mode
    setCurrentMode(systemSettings.settings.initMode);
  }
  else{                                                                                       // If in stand mode, read WAKE status
    if(WAKE_input()){
      setCurrentMode(mode_run);                                                               // Set run mode
    }
    else{
      setCurrentMode(systemSettings.settings.StandMode);                                      // Set stand idle mode
    }
  }
  initTimers();                                                                               // Initialize timers
                                                                                              // Now the PWM and ADC are working in the background.
}

void handleIron(void) {
  uint32_t CurrentTime = HAL_GetTick();
  uint16_t tipTemp = readTipTemperatureCompensated(update_reading,read_Avg);                  // Update Tip temperature in human readable format

  if(!Iron.Error.failState){
    if( (systemSettings.setupMode==setup_On) ||                                               // Ensure not being in setup mode,
      (systemSettings.settings.NotInitialized!=initialized) ||                                // settings and profile are initialized
      (systemSettings.Profile.NotInitialized!=initialized) ||
      (systemSettings.Profile.ID != systemSettings.settings.currentProfile) ||                // Profile ID is the same as the system profile
      (systemSettings.settings.currentProfile>profile_C210)){                                 // And it's a known value

      SetFailState(setError);
    }
  }

  checkIronError();                                                                           // Error detection.
  if( Iron.Error.globalFlag ){                                                                // If any error flag active, stop here
    return;                                                                                   // Do nothing else (PWM already disabled)
  }

  // Controls external mode changes (from stand mode changes), this acts as a debouncing timer
  if(Iron.updateMode==needs_update){                                                          // If pending mode change
    if((CurrentTime-Iron.LastModeChangeTime)>100){                                            // Wait 100mS with steady mode (de-bouncing)
      Iron.updateMode=no_update;                                                              // reset flag
      setCurrentMode(Iron.changeMode);                                                        // Apply stand mode
    }
  }
  
  // If sleeping, stop here
  if(Iron.CurrentMode==mode_sleep) {                                                          // For safety, update PWM out everytime
    if(systemSettings.settings.activeDetection){
      Iron.Pwm_Out = PWMminOutput;
    }
    else{
      Iron.Pwm_Out = 0;
    }
    __HAL_TIM_SET_COMPARE(Iron.Pwm_Timer, Iron.Pwm_Channel, Iron.Pwm_Out);                    // Load new calculated PWM Duty
    return;
  }
  
  // Controls inactivity timer and enters low power modes
  uint32_t mode_time = CurrentTime - Iron.CurrentModeTimer;
  uint32_t sleep_time = (uint32_t)systemSettings.Profile.sleepTimeout*60000;
  uint32_t standby_time = (uint32_t)systemSettings.Profile.standbyTimeout*60000;

  if(Iron.calibrating==calibration_Off){                                                      // Don't enter low power states while calibrating
    if(Iron.CurrentMode==mode_run) {                                                          // If running
      if(systemSettings.Profile.standbyTimeout>0 && mode_time>standby_time) {                 // If standbyTimeout not zero, check time
        setCurrentMode(mode_standby);
      }
      else if(systemSettings.Profile.standbyTimeout==0 && mode_time>sleep_time) {             // If standbyTimeout zero, check sleep time
        setCurrentMode(mode_sleep);
      }
    }
    else if(Iron.CurrentMode==mode_standby){                                                  // If in standby
      if(systemSettings.Profile.standbyTimeout>0 && mode_time>sleep_time) {                   // Check sleep time
        setCurrentMode(mode_sleep);                                                           // Only enter sleep if not zero
      }
    }
  }

  if(Iron.updatePwm==needs_update){
    Iron.updatePwm=no_update;
    __HAL_TIM_SET_AUTORELOAD(Iron.Pwm_Timer,systemSettings.Profile.pwmPeriod);
    __HAL_TIM_SET_AUTORELOAD(Iron.Delay_Timer,systemSettings.Profile.pwmDelay);
    Iron.Pwm_Limit = (systemSettings.Profile.pwmPeriod-1) - (systemSettings.Profile.pwmDelay + 1 + (uint16_t)ADC_MEASURE_TIME/10);
  }

  #ifdef USE_VIN
  updatePowerLimit();                                                                         // Update power limit values
  #endif

  // Update PID
  volatile uint16_t PID_temp;
  if(Iron.DebugMode==debug_On){                                                               // If in debug mode, use debug setpoint value
    Iron.Pwm_Out = calculatePID(Iron.Debug_SetTemperature, TIP.last_avg, Iron.Pwm_Max);
  }
  else{                                                                                       // Else, use current setpoint value
    PID_temp = human2adc(Iron.CurrentSetTemperature);
    Iron.Pwm_Out = calculatePID(PID_temp, TIP.last_avg, Iron.Pwm_Max);
  }

  if(systemSettings.settings.activeDetection && Iron.Pwm_Out<=PWMminOutput){
    Iron.CurrentIronPower = 0;
    Iron.Pwm_Out = PWMminOutput;                                                              // Maintain iron detection
  }
  else if(Iron.Pwm_Out == Iron.Pwm_Max){
    Iron.CurrentIronPower = 100;
  }
  else{
    Iron.CurrentIronPower = ((uint32_t)Iron.Pwm_Out*100)/Iron.Pwm_Max;                        // Compute new %
  }
  __HAL_TIM_SET_COMPARE(Iron.Pwm_Timer, Iron.Pwm_Channel, Iron.Pwm_Out);                      // Load new calculated PWM Duty

  // For calibration process. Add +-2ºC detection margin
  if(  (tipTemp>=(Iron.CurrentSetTemperature-2)) && (tipTemp<=(Iron.CurrentSetTemperature+2)) && !Iron.Cal_TemperatureReachedFlag) {
    temperatureReached( Iron.CurrentSetTemperature);
    Iron.Cal_TemperatureReachedFlag = 1;
  }
}

// Round to closest 10
uint16_t round_10(uint16_t input){
  if((input%10)>5){
    input+=(10-input%10);                                                                     // ex. 640°F=337°C->340°C)
  }
  else{
    input-=input%10;                                                                          // ex. 300°C=572°F->570°F
  }
  return input;
}

// Changes the system temperature unit
void setSystemTempUnit(bool unit){

  if(systemSettings.Profile.tempUnit != unit){
    systemSettings.Profile.tempUnit = unit;
    systemSettings.Profile.UserSetTemperature = round_10(TempConversion(systemSettings.Profile.UserSetTemperature,unit,0));
    systemSettings.Profile.standbyTemperature = round_10(TempConversion(systemSettings.Profile.standbyTemperature,unit,0));
    systemSettings.Profile.MaxSetTemperature = round_10(TempConversion(systemSettings.Profile.MaxSetTemperature,unit,0));
    systemSettings.Profile.MinSetTemperature = round_10(TempConversion(systemSettings.Profile.MinSetTemperature,unit,0));
  }

  systemSettings.settings.tempUnit = unit;
  setCurrentMode(Iron.CurrentMode);     // Reload temps
}

// This function inits the timers and sets the prescaler settings depending on the system core clock
// The final PWM settings are applied by LoadProfile
void initTimers(void){
  // Delay timer config
  #ifdef DELAY_TIMER_HALFCLOCK
  Iron.Delay_Timer->Init.Prescaler = (SystemCoreClock/50000)-1;                               // 10uS input clock
  #else
  Iron.Delay_Timer->Init.Prescaler = (SystemCoreClock/100000)-1;
  #endif

  Iron.Delay_Timer->Init.Period = 1999;                                                         // 20mS by default
  if (HAL_TIM_Base_Init(Iron.Delay_Timer) != HAL_OK){
    Error_Handler();
  }
  // PWM timer config
  #ifdef PWM_TIMER_HALFCLOCK
  Iron.Pwm_Timer->Init.Prescaler = (SystemCoreClock/50000)-1;                                 // 10uS input clock
  #else
  Iron.Pwm_Timer->Init.Prescaler = (SystemCoreClock/100000)-1;
  #endif

  Iron.Pwm_Timer->Init.Period = 19999;                                                          // 200mS by default
  if (HAL_TIM_Base_Init(Iron.Pwm_Timer) != HAL_OK){
    Error_Handler();
  }

  __HAL_TIM_CLEAR_FLAG(Iron.Delay_Timer,TIM_FLAG_UPDATE | TIM_FLAG_COM | TIM_FLAG_CC1 | TIM_FLAG_CC2 | TIM_FLAG_CC3 | TIM_FLAG_CC4 ); // Clear all flags

  __HAL_TIM_ENABLE_IT(Iron.Delay_Timer,TIM_IT_UPDATE);                                        // Enable Delay timer interrupt

  __HAL_TIM_CLEAR_FLAG(Iron.Pwm_Timer,TIM_FLAG_UPDATE | TIM_FLAG_COM | TIM_FLAG_CC1 | TIM_FLAG_CC2 | TIM_FLAG_CC3 | TIM_FLAG_CC4 );   // Clear all flags

  #ifdef DEBUG_PWM
  __HAL_TIM_ENABLE_IT(Iron.Pwm_Timer, TIM_IT_UPDATE);                                         // For debugging PWM (TEST pin)
  #endif

  #ifdef  PWM_CHx
  HAL_TIM_PWM_Start_IT(Iron.Pwm_Timer, Iron.Pwm_Channel);                                     // Start PWM, output uses CHx channel
  #elif defined PWM_CHxN
  HAL_TIMEx_PWMN_Start_IT(Iron.Pwm_Timer, Iron.Pwm_Channel);                                  // Start PWM, output uses CHxN channel
  #else
  #error No PWM ouput set (See PWM_CHx / PWM_CHxN in board.h)
  #endif
  if(systemSettings.settings.activeDetection){
    Iron.Pwm_Out = PWMminOutput;
  }
  else{
    Iron.Pwm_Out = 0;
  }
  Iron.Pwm_Limit = 17999 - (uint16_t)(ADC_MEASURE_TIME/10);
  __HAL_TIM_SET_COMPARE(Iron.Pwm_Timer, Iron.Pwm_Channel, Iron.Pwm_Out);                      // Set min value into PWM
}


// Check iron runaway
void runAwayCheck(void){
  uint16_t TempStep,TempLimit;
  uint32_t CurrentTime = HAL_GetTick();
  uint16_t tipTemp = readTipTemperatureCompensated(stored_reading, read_Avg);

  // If by any means the PWM output is higher than max calculated, generate error
  if((Iron.Pwm_Out > Iron.Pwm_Limit) || (Iron.Pwm_Out != __HAL_TIM_GET_COMPARE(Iron.Pwm_Timer,Iron.Pwm_Channel))){
    Error_Handler();
  }
  if(systemSettings.settings.tempUnit==mode_Celsius){
    TempStep = 25;
    TempLimit = 500;
  }else{
    TempStep = 45;
    TempLimit = 950;
  }

  if((Iron.Pwm_Out>PWMminOutput) && (Iron.RunawayStatus==runaway_ok)  && (Iron.DebugMode==debug_Off) &&(tipTemp > Iron.CurrentSetTemperature)){

    if(tipTemp>TempLimit){ Iron.RunawayLevel=runaway_500; }
    else{
      for(int8_t c=runaway_100; c>=runaway_ok; c--){                                        // Check temperature diff
        Iron.RunawayLevel=c;
        if(tipTemp > (Iron.CurrentSetTemperature + (TempStep*Iron.RunawayLevel)) ){         // 25ºC steps
          break;                                                                            // Stop at the highest overrun condition
        }
      }
    }
    if(Iron.RunawayLevel!=runaway_ok){                                                      // Runaway detected?
      if(Iron.prevRunawayLevel==runaway_ok){                                                // First overrun detection?
        Iron.prevRunawayLevel=Iron.RunawayLevel;                                            // Yes, store in prev level
        Iron.RunawayTimer = CurrentTime;                                                    // Store time
      }
      else{                                                                                 // Was already triggered
        switch(Iron.RunawayLevel){
          case runaway_ok:                                                                  // No problem (<25ºC difference)
            break;                                                                          // (Never used here)
          case runaway_25:                                                                  // Temp >25°C over setpoint
            if((CurrentTime-Iron.RunawayTimer)>20000){                                      // 20 second limit
              Iron.RunawayStatus=runaway_triggered;
              FatalError(error_RUNAWAY25);
            }
            break;
          case runaway_50:                                                                  // Temp >50°C over setpoint
            if((CurrentTime-Iron.RunawayTimer)>10000){                                      // 10 second limit
              Iron.RunawayStatus=runaway_triggered;
              FatalError(error_RUNAWAY50);
            }
            break;
          case runaway_75:                                                                  // Temp >75°C over setpoint
            if((CurrentTime-Iron.RunawayTimer)>3000){                                       // 3 second limit
              Iron.RunawayStatus=runaway_triggered;
              FatalError(error_RUNAWAY75);
            }
            break;
          case runaway_100:                                                                 // Temp >100°C over setpoint
            if((CurrentTime-Iron.RunawayTimer)>1000){                                       // 1 second limit
              Iron.RunawayStatus=runaway_triggered;
              FatalError(error_RUNAWAY100);
            }
            break;
          case runaway_500:                                                                 // Exceed 500ºC!
            if((CurrentTime-Iron.RunawayTimer)>500){                                        // 0.5 second limit
              Iron.RunawayStatus=runaway_triggered;
              FatalError(error_RUNAWAY500);
            }
            break;
          default:                                                                          // Unknown overrun state
            Iron.RunawayStatus=runaway_triggered;
            FatalError(error_RUNAWAY_UNKNOWN);
            break;
        }
      }
    }
    return;                                                                                 // Runaway active, return
  }
  Iron.RunawayTimer = CurrentTime;                                                          // If no runaway detected, reset values
  Iron.prevRunawayLevel=runaway_ok;
}

// Update PWM max value based on current supply voltage, heater resistance and power limit setting
#ifdef USE_VIN
void updatePowerLimit(void){
  uint32_t volts = getSupplyVoltage_v_x10();                                                // Get last voltage reading x10
  volts = (volts*volts)/10;                                                                 // (Vx10 * Vx10)/10 = (V*V)*10 (x10 for fixed point precision)
  if(volts==0){
    volts=1;                                                                                // set minimum value to avoid division by 0
  }
  uint32_t PwmPeriod=systemSettings.Profile.pwmPeriod;                                      // Read complete PWM period
  uint32_t maxPower = volts/systemSettings.Profile.impedance;                                        // Compute max power with current voltage and impedance(Impedance stored in x10)
  if(systemSettings.Profile.power >= maxPower){                                             // If set power is already higher than the max possible power given the voltage and heater resistance
    Iron.Pwm_Max = Iron.Pwm_Limit;                                                          // Set max PWM
  }
  else{                                                                                     // Else,
    Iron.Pwm_Max = (PwmPeriod*systemSettings.Profile.power)/maxPower;                       // Compute max PWM output for current power limit
    if(Iron.Pwm_Max > Iron.Pwm_Limit){                                                      // Ensure it doesn't exceed the limits
      Iron.Pwm_Max = Iron.Pwm_Limit;
    }
    else if(Iron.Pwm_Max==0){                                                               // Ensure it doesn't exceed the PWM limits
      Iron.Pwm_Max = 1;
    }
  }
}
#endif

// Loads the PWM delay
void setPwmDelay(uint16_t delay){
 systemSettings.Profile.pwmDelay=delay;
 Iron.updatePwm=needs_update;
}

// Loads the PWM period
void setPwmPeriod(uint16_t period){
  systemSettings.Profile.pwmPeriod=period;
  Iron.updatePwm=needs_update;
}

// Sets no Iron detection threshold
void setNoIronValue(uint16_t noiron){
  systemSettings.Profile.noIronValue=noiron;
}

// Change the iron operating mode in stand mode
void setModefromStand(uint8_t mode){
  if( GetIronError() ||
      ((Iron.changeMode==mode) && (Iron.CurrentMode==mode)) ||
      ((Iron.CurrentMode==mode_sleep) && (mode==mode_standby))){                            // Ignore if error present, same mode, or setting standby when already in sleep mode
    return;
  }
  if(Iron.changeMode!=mode){
    Iron.changeMode = mode;                                                                 // Update mode
    Iron.LastModeChangeTime = HAL_GetTick();                                                // Reset debounce timer
  }
  Iron.updateMode = needs_update;                                                           // Set flag
}

// Set the iron operating mode
void setCurrentMode(uint8_t mode){
  Iron.CurrentModeTimer = HAL_GetTick();                                                    // Refresh current mode timer
  if(mode==mode_standby){
    Iron.CurrentSetTemperature = systemSettings.Profile.standbyTemperature;                 // Set standby temp
  }
  else{
    Iron.CurrentSetTemperature = systemSettings.Profile.UserSetTemperature;                 // Set user temp (sleep mode ignores this)
  }
  if(Iron.CurrentMode != mode){                                                             // If current mode is different
    resetPID();
    buzzer_long_beep();
    Iron.CurrentMode = mode;
    modeChanged(mode);
    if(Iron.CurrentMode == mode_run){
      Iron.Cal_TemperatureReachedFlag = 0;
    }
  }
}

// Called from program timer if WAKE change is detected
void IronWake(bool source){                                                                 // source: handle shake, encoder push button
  if(GetIronError()){ return; }                                                             // Ignore if error present
  if(Iron.CurrentMode!=mode_run){
    // If in sleep mode, ignore if wake source disabled
    if( (source==source_wakeButton && (!systemSettings.settings.wakeOnButton || (systemSettings.settings.WakeInputMode==wakeInputmode_stand) )) || (source==source_wakeInput && !systemSettings.settings.wakeOnShake)){
      return;
    }
  }
  if(source==source_wakeInput){                                                             // Else, if source wake input
    Iron.newActivity=1;                                                                     // Enable flag for oled activity icon
    Iron.lastActivityTime = HAL_GetTick();                                                  // Store time for keeping the image on
  }
  setCurrentMode(mode_run);                                                                 // Set run mode. If already in run mode, this will reset the run timer.
}


// Checks for non critical iron errors (Errors that can be cleared)
void checkIronError(void){
  uint32_t CurrentTime = HAL_GetTick();                                                     // Get current time
  int16_t ambTemp = readColdJunctionSensorTemp_x10(mode_Celsius);                           // Read NTC in Celsius
  IronError_t Err = { 0 };
  Err.failState = Iron.Error.failState;                                                     // Get failState flag
  Err.NTC_high = ambTemp > 700 ? 1 : 0;                                                     // Check NTC too high (Wrong NTC wiring or overheating, >70ºC)
  Err.NTC_low = ambTemp < -500 ? 1 : 0;    
  #ifdef USE_VIN
  Err.V_low = getSupplyVoltage_v_x10() < systemSettings.settings.lvp   ? 1 : 0;             // Check supply voltage (Mosfet will not work ok <10V, it will heat up)
  #endif
  Err.noIron = TIP.last_raw>systemSettings.Profile.noIronValue ? 1 : 0;                     // Check tip temperature too high (Wrong connection or not connected)
  if(CurrentTime<1000 || systemSettings.setupMode==setup_On){                               // Don't check sensor errors during first second or in setup mode, wait for readings need to get stable
    Err.Flags &= 0x10;                                                                      // Only check failure state
  }
  if(Err.Flags){                                                                            // If there are errors
    Iron.Error.Flags |= Err.Flags;                                                          // Update flags
    Iron.LastErrorTime = CurrentTime;                                                       // Save time
    if(!Iron.Error.globalFlag){                                                             // If first detection
      if(Err.Flags==1 && Iron.CurrentMode == mode_sleep){                                   // If in sleep mode and only no iron flag is set
        return;                                                                             // return
      }
      Iron.Error.globalFlag = 1;                                                            // Set global flag
      setCurrentMode(mode_sleep);                                                           // Force sleep mode
      if(systemSettings.settings.activeDetection && !Err.failState){
        Iron.Pwm_Out = PWMminOutput;
      }
      else{
        Iron.Pwm_Out = 0;
      }
      __HAL_TIM_SET_COMPARE(Iron.Pwm_Timer, Iron.Pwm_Channel, Iron.Pwm_Out);                // Load now the value into the PWM hardware
      buzzer_alarm_start();                                                                 // Start alarm
    }
  }
  else if(!Err.Flags && Iron.Error.Flags==1){                                               // If no errors and only no iron flag was active (And no global flag, so it was detected while in sleep mode)
    Iron.Error.Flags=0;                                                                     // Clear
  }
  else if (Iron.Error.globalFlag && Err.Flags==noError){                                    // If global flag set, but there are no errors anymore
    if((CurrentTime-Iron.LastErrorTime)>systemSettings.settings.errorDelay){                // Check enough time has passed
      Iron.Error.Flags = noError;                                                           // Reset errors
      buzzer_alarm_stop();                                                                  // Stop alarm
      setCurrentMode(mode_run);                                                             // Restore run mode
    }
  }
}

// Returns the actual status of the iron error.
bool GetIronError(void){
  return Iron.Error.globalFlag;
}

// Sets Failure state
void SetFailState(bool FailState) {
  if(!FailState && Iron.Error.Flags==0x90){                                                 // If only failsafe was active? (This should only happen because it was on first init screen)
    Iron.Error.Flags = 0;                                                                   // Reset errors (Ignore timer)
    setCurrentMode(mode_run);                                                               // Resume run mode
  }
  else{
    Iron.Error.failState=FailState;
    checkIronError();                                                                       // Update Error
  }
}

// Gets Failure state
bool GetFailState() {
  return Iron.Error.failState;
}


// Sets the debug temperature
void setDebugTemp(uint16_t value) {
  Iron.Debug_SetTemperature = value;
}
// Handles the debug activation/deactivation
void setDebugMode(uint8_t value) {
  Iron.DebugMode = value;
}

// Sets the user temperature
void setUserTemperature(uint16_t temperature) {
  Iron.Cal_TemperatureReachedFlag = 0;
  if(systemSettings.Profile.UserSetTemperature != temperature){
    systemSettings.Profile.UserSetTemperature = temperature;
    if(Iron.CurrentMode==mode_run){
      Iron.CurrentSetTemperature=temperature;
      resetPID();
    }
  }
}

// Returns the actual set temperature
uint16_t getUserTemperature() {
  return systemSettings.Profile.UserSetTemperature;
}

// Returns the actual working mode of the iron
uint8_t getCurrentMode() {
  return Iron.CurrentMode;
}

// Returns the output power
int8_t getCurrentPower() {
  return Iron.CurrentIronPower;
}

// Adds a callback to be called when the set temperature is reached
void addSetTemperatureReachedCallback(setTemperatureReachedCallback callback) {
  setTemperatureReachedCallbackStruct_t *s = malloc(sizeof(setTemperatureReachedCallbackStruct_t));
  if(!s){
    Error_Handler();
  }
  s->callback = callback;
  s->next = NULL;
  setTemperatureReachedCallbackStruct_t *last = temperatureReachedCallbacks;
  if(!last) {
    temperatureReachedCallbacks = s;
    return;
  }
  while(last && last->next != NULL) {
    last = last->next;
  }
  last->next = s;
}

// Adds a callback to be called when the iron working mode is changed
void addModeChangedCallback(currentModeChanged callback) {
  currentModeChangedCallbackStruct_t *s = malloc(sizeof(currentModeChangedCallbackStruct_t));
  if(!s){
    Error_Handler();
  }
  s->callback = callback;
  s->next = NULL;
  currentModeChangedCallbackStruct_t *last = currentModeChangedCallbacks;
  while(last && last->next != NULL) {
    last = last->next;
  }
  if(last){
    last->next = s;
  }
  else{
    last = s;
  }
}
