//Sterilization Chamber => Tank 2, used for sterilizing the water
//    -> Handle the Sterilization Chamber and Post UV on storage
//    -> Dependency: water bank's float switch Low and High
//                   chamber's float switch Low and High
//                   storage's float switch Low and High

#define CHAMBER_STATE_NONE 0
#define CHAMBER_STATE_IDLE 1
#define CHAMBER_STATE_START_FILLING 2
#define CHAMBER_STATE_CHECK_FILLING_TO_LOWER 3
#define CHAMBER_STATE_CHECK_FILLING_TO_UPPER 4
#define CHAMBER_STATE_STERILIZING 5
#define CHAMBER_STATE_START_UNLOAD 6
#define CHAMBER_STATE_START_UNLOAD_TO_LOWER 7
#define CHAMBER_STATE_START_UNLOAD_TO_UPPER 8

//uint32_t chamberSterilizationOzoneDuration = 1*60*1000;
uint32_t chamberSterilizationUvDuration = 5*60*1000;
//uint32_t chamberSterilizationPostUvDuration = 5*60*1000;

uint32_t chamberExpectedTimeToFilledLower = 1*60*1000;//in mulliseconds
uint32_t chamberExpectedTimeToFilledUpper = 3*60*1000;//in mulliseconds
uint32_t chamberExpectedTimeToUnloadFilledLower = 1*60*1000;//in mulliseconds
uint32_t chamberExpectedTimeToUnloadFilledUpper = 3*60*1000;//in mulliseconds
uint32_t chamberTick = 0;
bool chamberPostUvEnable = true;
uint32_t chamberPostUvDuration = 1*60*1000;
uint32_t chamberPostUvTick = 0;
uint32_t chamberInverterStartupTick = 0;

int chamberState = WATER_BANK_STATE_NONE;
bool chamberAutomatic = false;

#define CHAMBER_ERROR_NONE 0
#define CHAMBER_ERROR_LOWER_FLOAT_SWITCH 1 //lower switch error
#define CHAMBER_ERROR_UPPER_FLOAT_SWITCH 2 //upper switch error
#define CHAMBER_ERROR_UPPER_FLOAT_SWITCH_OR_NO_WATER 3
#define CHAMBER_ERROR_LOWER_FLOAT_SWITCH_OR_NO_WATER 4
#define CHAMBER_ERROR_STORAGE_LOWER_FLOAT_SWITCH 5 //lower switch error
#define CHAMBER_ERROR_STORAGE_UPPER_FLOAT_SWITCH 6 //upper switch error
#define CHAMBER_ERROR_STORAGE_UPPER_FLOAT_SWITCH_OR_NO_WATER 7
#define CHAMBER_ERROR_STORAGE_LOWER_FLOAT_SWITCH_OR_NO_WATER 8
#define CHAMBER_ERROR_TANK_LOWER_FLOAT_SWITCH 9 //lower switch error
#define CHAMBER_ERROR_TANK_UPPER_FLOAT_SWITCH 10 //upper switch error
#define CHAMBER_ERROR_TANK_UPPER_FLOAT_SWITCH_OR_NO_WATER 11
#define CHAMBER_ERROR_TANK_LOWER_FLOAT_SWITCH_OR_NO_WATER 12
#define CHAMBER_ERROR_LOWER_FLOAT_SWITCH_OR_LEAKED 13

bool chamberError = false;
byte chamberErrorCode = CHAMBER_ERROR_NONE;
String chamberErrorString = "";

//penampungan hasil sterilisasi (tank 3)
bool chamber_SterilizedLowerFloatSwitchStateCurrent = false;
bool chamber_SterilizedLowerFloatSwitchStatePrevious = false;
bool chamber_SterilizedUpperFloatSwitchStateCurrent = false;
bool chamber_SterilizedUpperFloatSwitchStatePrevious = false;
//ruang sterilisasi
bool chamber_SterilizationLowerFloatSwitchStateCurrent = false;
bool chamber_SterilizationLowerFloatSwitchStatePrevious = false;
bool chamber_SterilizationUpperFloatSwitchStateCurrent = false;
bool chamber_SterilizationUpperFloatSwitchStatePrevious = false;
//water tank sebelum sterilisasi
bool chamber_TankLowerFloatSwitchStateCurrent = false;
bool chamber_TankLowerFloatSwitchStatePrevious = false;
bool chamber_TankUpperFloatSwitchStateCurrent = false;
bool chamber_TankUpperFloatSwitchStatePrevious = false;

bool chamberSterilizationSuccess = false;

uint32_t GetSterilizationRemainingMillis(){
  uint32_t t = 0;
  if (chamberState==CHAMBER_STATE_STERILIZING){
    t = (chamberSterilizationOzoneDuration - (millis()-chamberTick));
    if (t>chamberSterilizationOzoneDuration) t = 0;
    return t;
  }

  if (GetSwitchState(STERILIZED_UV_SWITCH)){
    t = chamberPostUvDuration - (millis()-chamberPostUvTick);
    if (t>chamberPostUvDuration) t = 0;
  }
  return t;
}

bool OnOzoneStrelization(){
  if (GetSwitchState(STERILIZATION_OZONE_AND_UV_SWITCH)) return true;
  return (chamberState==CHAMBER_STATE_STERILIZING);
}

bool OnPostUV(){
  if (GetSwitchState(STERILIZED_UV_SWITCH)){
    return true;
  }
  return false;
}

bool IsChamberFilling(){
  return (chamberState>=CHAMBER_STATE_START_FILLING) && (chamberState<=CHAMBER_STATE_CHECK_FILLING_TO_UPPER);
}

bool IsChamberUnload(){
  return (chamberState>=CHAMBER_STATE_START_UNLOAD) && (chamberState<=CHAMBER_STATE_START_UNLOAD_TO_UPPER);
}

bool ChamberIsSuccess(){
  return chamberSterilizationSuccess;
}

bool ChamberIsError(){
  return chamberError;
}

String ChamberErrorString(){
  if (chamberError) return chamberErrorString;
  return "";
}

bool ChamberIsIdle(){
  return (chamberState==CHAMBER_STATE_IDLE) || (chamberState==WATER_BANK_STATE_NONE);
}

bool IsChamberSystemRunning(){
  if (GetSwitchState(STERILIZATION_OZONE_AND_UV_SWITCH) || GetSwitchState(INVERTER_SWITCH)) return true;
  if (!chamberAutomatic) return (chamberState!=WATER_BANK_STATE_NONE);
  return (chamberState>CHAMBER_STATE_IDLE);
}

void ClearChamberError(){
  chamberError = false;
  chamberErrorCode = CHAMBER_ERROR_NONE;
  chamberErrorString = "";        
}

void ChamberTurnOffAllSwitch(bool exceptInverterAndPostUv=true){
  //make sure ozone, cycling pump, fill in pump, fill out pump, uv1, uv2 is off
  //inverter
  if (!exceptInverterAndPostUv){
    if (GetSwitchState(INVERTER_SWITCH)){
      Serial.println("ChamberSystemRoutine: INVERTER_SWITCH OFF");
      SetSwitchState(INVERTER_SWITCH, false);
    }
  }
  //ozone
  if (GetSwitchState(STERILIZATION_OZONE_AND_UV_SWITCH)){
    Serial.println("ChamberSystemRoutine: STERILIZATION_OZONE_AND_UV_SWITCH OFF");
    SetSwitchState(STERILIZATION_OZONE_AND_UV_SWITCH, false);
  }
  //post uv
  if (!exceptInverterAndPostUv){
    if (GetSwitchState(STERILIZED_UV_SWITCH)){
      Serial.println("ChamberSystemRoutine: ");
      SetSwitchState(STERILIZED_UV_SWITCH, false);
    }
  }
  //fill in pump
  if (GetSwitchState(STERILIZATION_CHAMBER_FILL_PUMP_SWITCH)){
    Serial.println("ChamberSystemRoutine: STERILIZATION_CHAMBER_FILL_PUMP_SWITCH OFF");
    SetSwitchState(STERILIZATION_CHAMBER_FILL_PUMP_SWITCH, false);
  }
  //fill out pump
  if (GetSwitchState(STERILIZED_CHAMBER_FILL_PUMP_SWITCH)){
    Serial.println("ChamberSystemRoutine: STERILIZED_CHAMBER_FILL_PUMP_SWITCH OFF");
    SetSwitchState(STERILIZED_CHAMBER_FILL_PUMP_SWITCH, false);
  }
  //circulation pump
  if (GetSwitchState(STERILIZATION_CHAMBER_CIRCULATION_PUMP_SWITCH)){
    Serial.println("ChamberSystemRoutine: STERILIZATION_CHAMBER_CIRCULATION_PUMP_SWITCH OFF");
    SetSwitchState(STERILIZATION_CHAMBER_CIRCULATION_PUMP_SWITCH, false);
  }
}

//system running state =====================================================
void ChamberSystemRoutine(){

  //handle the post uv timer independently ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  bool ctrlLowerSwitchState = GetInSwitchState(STERILIZED_CHAMBER_FLOAT_SWITCH_LOWER);
  if ((!ctrlLowerSwitchState) || ((GetSwitchState(STERILIZED_UV_SWITCH) && (millis()-chamberPostUvTick)>=chamberPostUvDuration)) || (!chamberPostUvEnable)){
    if (GetSwitchState(STERILIZED_UV_SWITCH)){
      Serial.println("ChamberSystemRoutine: ");
      SetSwitchState(STERILIZED_UV_SWITCH, false);
    }
    //if ((chamberState==CHAMBER_STATE_NONE) || (chamberState==CHAMBER_STATE_IDLE) || ((chamberState>=CHAMBER_STATE_START_FILLING) && (chamberState<=CHAMBER_STATE_START_FILLING_TO_UPPER))){
    if (chamberState==CHAMBER_STATE_NONE){
      //ChamberTurnOffAllSwitch(false);//turn off all
    }else if ((chamberState!=CHAMBER_STATE_NONE) && (chamberState!=CHAMBER_STATE_STERILIZING)){
      SetSwitchState(STERILIZED_UV_SWITCH, false);//turn off post uv
      SetSwitchState(INVERTER_SWITCH, false);//turn off inverter
      //ChamberTurnOffAllSwitch(false);//turn off all
    }
  }
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  switch(chamberState){
    case CHAMBER_STATE_NONE:{
      ChamberTurnOffAllSwitch(true);
    };
    break;
    case CHAMBER_STATE_IDLE:{
      //this is the state when use automatic mode
      ChamberTurnOffAllSwitch(true); //except inverter if post uv if running

      //in case mismatch, prevent auto monitoring
      if (!chamberAutomatic){
          chamberState = CHAMBER_STATE_NONE;
          Serial.println("CHAMBER_STATE_IDLE -> CHAMBER_STATE_NONE");
      }
      
      //trigger condition: 
      // state => STORAGE_STATE_CHECK_IF_EMPTY: sterilized bank is empty
      chamber_SterilizedLowerFloatSwitchStateCurrent = GetInSwitchState(STERILIZED_CHAMBER_FLOAT_SWITCH_LOWER);
      bool intoFilling = false;
      if ((chamber_SterilizedLowerFloatSwitchStateCurrent!=chamber_SterilizedLowerFloatSwitchStatePrevious) && (!chamber_SterilizedLowerFloatSwitchStateCurrent)){
        intoFilling = true;
        Serial.println("CHAMBER_STATE_IDLE -> Trigger reason: Storage empty");
      }
      //storage kosong dan water tank menjadi penuh
      chamber_TankLowerFloatSwitchStateCurrent = GetInSwitchState(WATER_BANK_FLOAT_SWITCH_LOWER);
      chamber_TankUpperFloatSwitchStateCurrent = GetInSwitchState(WATER_BANK_FLOAT_SWITCH_UPPER);
      if (!chamber_SterilizedLowerFloatSwitchStateCurrent && (chamber_TankUpperFloatSwitchStateCurrent!=chamber_TankUpperFloatSwitchStatePrevious) && (chamber_TankUpperFloatSwitchStateCurrent && chamber_TankLowerFloatSwitchStateCurrent)){
        intoFilling = true;
        //Serial.println("Tank baru penuh dan pemampungan sterilisasi kosong.");
        Serial.println("CHAMBER_STATE_IDLE -> Trigger reason: Storage empty and Water Tank just full");
      }
      //update state
      chamber_SterilizedLowerFloatSwitchStatePrevious = chamber_SterilizedLowerFloatSwitchStateCurrent;
      chamber_TankLowerFloatSwitchStatePrevious = chamber_TankLowerFloatSwitchStateCurrent;
      chamber_TankUpperFloatSwitchStatePrevious = chamber_TankUpperFloatSwitchStateCurrent;
      if (intoFilling){
        chamberSterilizationSuccess = false;
        ClearChamberError();
        chamberState = CHAMBER_STATE_START_FILLING;
        Serial.println("CHAMBER_STATE_IDLE -> CHAMBER_STATE_START_FILLING");
      }
    };
    break;
    case CHAMBER_STATE_START_FILLING:{
      chamberSterilizationSuccess = false;
      chamber_SterilizedLowerFloatSwitchStateCurrent = GetInSwitchState(STERILIZED_CHAMBER_FLOAT_SWITCH_LOWER);
      chamber_SterilizedUpperFloatSwitchStateCurrent = GetInSwitchState(STERILIZED_CHAMBER_FLOAT_SWITCH_UPPER);
      //cek error lain dari float switch:
      // -> storage switch upper lower harus benar
      // -> tank switch upper lower harus benar (water tank penuh)

      //cek penampungan harus kosong
      if (chamber_SterilizedLowerFloatSwitchStateCurrent){
        //switch back to idle
        chamberError = true;
        chamberErrorString = "Penampungan harus kosong dahulu untuk memulai sterilisasi.";
        chamberState = CHAMBER_STATE_IDLE;
        Serial.print("Error: ");
        Serial.println(chamberErrorString);
        Serial.println("CHAMBER_STATE_START_FILLING -> CHAMBER_STATE_IDLE");
        return;
      }
      //check if: full -> CHAMBER_STATE_STERILIZING
      //          empty -> CHAMBER_STATE_CHECK_FILLING_TO_LOWER
      //          lower -> CHAMBER_STATE_CHECK_FILLING_TO_UPPER
      //jika chamber penuh langsung ke sterilisasi
      chamber_SterilizationLowerFloatSwitchStateCurrent = GetInSwitchState(STERILIZATION_CHAMBER_FLOAT_SWITCH_LOWER);
      chamber_SterilizationUpperFloatSwitchStateCurrent = GetInSwitchState(STERILIZATION_CHAMBER_FLOAT_SWITCH_UPPER);
      if (chamber_SterilizationLowerFloatSwitchStateCurrent && chamber_SterilizationUpperFloatSwitchStateCurrent){
        chamberTick = millis();
        chamberState = CHAMBER_STATE_STERILIZING;
        Serial.println("Sterilization chamber already full.");
        Serial.println("CHAMBER_STATE_START_FILLING -> CHAMBER_STATE_STERILIZING");
        return;
      }
      chamber_TankLowerFloatSwitchStateCurrent = GetInSwitchState(WATER_BANK_FLOAT_SWITCH_LOWER);
      chamber_TankUpperFloatSwitchStateCurrent = GetInSwitchState(WATER_BANK_FLOAT_SWITCH_UPPER);
      Serial.print("Water Tank lower float switch: "); Serial.println(chamber_TankLowerFloatSwitchStateCurrent);
      Serial.print("Water Tank upper float switch: "); Serial.println(chamber_TankUpperFloatSwitchStateCurrent);
      //tank penampung harus penuh dulu baru mau dong
      if (chamber_TankLowerFloatSwitchStateCurrent && chamber_TankUpperFloatSwitchStateCurrent){
        //cek jika chamber kosong total
        if (!chamber_SterilizationLowerFloatSwitchStateCurrent && !chamber_SterilizationUpperFloatSwitchStateCurrent){
          //kosong total
          chamberTick = millis();
          chamberState = CHAMBER_STATE_CHECK_FILLING_TO_LOWER;
          Serial.println("CHAMBER_STATE_START_FILLING -> CHAMBER_STATE_CHECK_FILLING_TO_LOWER");
        }else if (chamber_SterilizationLowerFloatSwitchStateCurrent && !chamber_SterilizationUpperFloatSwitchStateCurrent){
          //ada sedikit, mayan
          chamberTick = millis();
          chamberState = CHAMBER_STATE_CHECK_FILLING_TO_UPPER;
          Serial.println("CHAMBER_STATE_START_FILLING -> CHAMBER_STATE_CHECK_FILLING_TO_UPPER");
        }else{
          //float switch error ini hadeh
          chamberError = true;
          chamberErrorString = "Float switch sterilisasi error.";
          chamberState = CHAMBER_STATE_IDLE;
          Serial.print("Error: ");
          Serial.println(chamberErrorString);
          Serial.println("CHAMBER_STATE_START_FILLING -> CHAMBER_STATE_IDLE");
          return;
        }
      }else{
        chamberError = true;
        chamberErrorString = "Tangki penampung air harus penuh dahulu untuk dapat memulai pengisian tangki sterilisasi.";
        Serial.print("Error: ");
        Serial.println(chamberErrorString);
        if (chamberAutomatic){
          chamberState = CHAMBER_STATE_IDLE;
          Serial.println("CHAMBER_STATE_START_FILLING -> CHAMBER_STATE_IDLE");
        }else{
          chamberState = CHAMBER_STATE_NONE;
          Serial.println("CHAMBER_STATE_START_FILLING -> CHAMBER_STATE_NONE");
        }
        return;
      }
    };
    break;
    case CHAMBER_STATE_CHECK_FILLING_TO_LOWER:{
      //waiting for filling into lower level, or stop when timeout
      //switch on the pump
      if (!GetSwitchState(STERILIZATION_CHAMBER_FILL_PUMP_SWITCH)){
        Serial.println("ChamberSystemRoutine: TURN ON FILL PUMP SWITCH");
        SetSwitchState(STERILIZATION_CHAMBER_FILL_PUMP_SWITCH, true);
      }
      //monitoring water level or timed out
      bool lowerSwitchState = GetInSwitchState(STERILIZATION_CHAMBER_FLOAT_SWITCH_LOWER);
      bool upperSwitchState = GetInSwitchState(STERILIZATION_CHAMBER_FLOAT_SWITCH_UPPER);
      //chamber_TankLowerFloatSwitchStateCurrent = GetInSwitchState(WATER_BANK_FLOAT_SWITCH_LOWER);
      //if (lowerSwitchState || upperSwitchState || !chamber_TankLowerFloatSwitchStateCurrent){
      if (lowerSwitchState || upperSwitchState){
        //check if lower float switch on
        //try to fill till full
        ClearChamberError();
        chamberTick = millis();
        chamberState = CHAMBER_STATE_CHECK_FILLING_TO_UPPER;
        Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_LOWER -> CHAMBER_STATE_CHECK_FILLING_TO_UPPER");
      }else{
        //check if timed out
        if (millis()-chamberTick>=chamberExpectedTimeToFilledLower){
          chamberError = true;
          chamberErrorCode = CHAMBER_ERROR_LOWER_FLOAT_SWITCH;
          chamberErrorString = "Tank 2 insufficient input water, or lower float switch error, or input pump error.";
          Serial.println("Chamber Error -> CHAMBER_ERROR_LOWER_FLOAT_SWITCH");
          if (chamberAutomatic){
            chamberState = CHAMBER_STATE_IDLE;
            Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_LOWER -> CHAMBER_STATE_IDLE");
          }else{
            chamberState = CHAMBER_STATE_NONE;
            Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_LOWER -> CHAMBER_STATE_NONE");
          }
        }
      }
    };
    break;
    case CHAMBER_STATE_CHECK_FILLING_TO_UPPER:{
      //waiting for filling into lower level, or stop when timeout
      //switch on the pump
      if (!GetSwitchState(STERILIZATION_CHAMBER_FILL_PUMP_SWITCH)){
        Serial.println("ChamberSystemRoutine: TURN ON FILL PUMP SWITCH");
        SetSwitchState(STERILIZATION_CHAMBER_FILL_PUMP_SWITCH, true);
      }
      //monitoring water level or timed out
      bool upperSwitchState = GetInSwitchState(STERILIZATION_CHAMBER_FLOAT_SWITCH_UPPER);
      chamber_TankLowerFloatSwitchStateCurrent = GetInSwitchState(WATER_BANK_FLOAT_SWITCH_LOWER);
      if (upperSwitchState || (!chamber_TankLowerFloatSwitchStateCurrent)){
        //check if upper float switch on or chamber's water below switch
        SetSwitchState(STERILIZATION_CHAMBER_FILL_PUMP_SWITCH, false); //matiin pompa
        ClearChamberError();
        chamberTick = millis();
        chamberState = CHAMBER_STATE_STERILIZING;
        Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_UPPER -> CHAMBER_STATE_STERILIZING");
      }else{
        //check if timed out
        if (millis()-chamberTick>=chamberExpectedTimeToFilledUpper){
          SetSwitchState(STERILIZATION_CHAMBER_FILL_PUMP_SWITCH, false); //matiin pompa
          chamberError = true;
          chamberErrorCode = CHAMBER_ERROR_UPPER_FLOAT_SWITCH;
          chamberErrorString = "Tank 2 insufficient input water, or upper float switch error, or input pump error.";
          Serial.println("Chamber Error -> CHAMBER_ERROR_UPPER_FLOAT_SWITCH");
          if (chamberAutomatic){
            chamberState = CHAMBER_STATE_IDLE;
            Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_UPPER -> CHAMBER_STATE_IDLE");
          }else{
            chamberState = CHAMBER_STATE_NONE;
            Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_UPPER -> CHAMBER_STATE_NONE");
          }
        }
      }
    };
    break;
    case CHAMBER_STATE_STERILIZING:{
      //make sure turn off filling in and filling out pump
      if (GetSwitchState(STERILIZATION_CHAMBER_FILL_PUMP_SWITCH)) SetSwitchState(STERILIZATION_CHAMBER_FILL_PUMP_SWITCH, false);
      if (GetSwitchState(STERILIZED_CHAMBER_FILL_PUMP_SWITCH)) SetSwitchState(STERILIZED_CHAMBER_FILL_PUMP_SWITCH, false);
      //if inverter not turned -> switch it first, but wait for 2 seconds
      if (!GetSwitchState(INVERTER_SWITCH)){
        //make sure turn off ozone and circulation
        if (GetSwitchState(STERILIZATION_OZONE_AND_UV_SWITCH)) SetSwitchState(STERILIZATION_OZONE_AND_UV_SWITCH, false);
        if (GetSwitchState(STERILIZATION_CHAMBER_CIRCULATION_PUMP_SWITCH)) SetSwitchState(STERILIZATION_CHAMBER_CIRCULATION_PUMP_SWITCH, false);      
        chamberInverterStartupTick = millis();
        SetSwitchState(INVERTER_SWITCH, true);
        Serial.println("Starting up the inveter.");
        return;
      }else{
        if ((millis()-chamberInverterStartupTick)<2000){
          chamberTick = millis(); //keep reset it
          return;
        }
      }
      //turn on ozone and circulation pump
      if (!GetSwitchState(STERILIZATION_OZONE_AND_UV_SWITCH)) SetSwitchState(STERILIZATION_OZONE_AND_UV_SWITCH, true);
      if (!GetSwitchState(STERILIZATION_CHAMBER_CIRCULATION_PUMP_SWITCH)) SetSwitchState(STERILIZATION_CHAMBER_CIRCULATION_PUMP_SWITCH, true);
      
      //wait till done, or if water is empty by accident (leaking) => back to idle with error state
      bool lowerSwitchState = GetInSwitchState(STERILIZATION_CHAMBER_FLOAT_SWITCH_LOWER);
      if (!lowerSwitchState){
        //wah error atau bocor nih
        ChamberTurnOffAllSwitch(false); //turn off all switch including inverter
        chamberError = true;
        chamberErrorCode = CHAMBER_ERROR_LOWER_FLOAT_SWITCH_OR_LEAKED;
        chamberErrorString = "Tank 2 leaked, or lower float switch error";
        Serial.print("Error: ");
        Serial.println(chamberErrorString);;
        chamberState = CHAMBER_STATE_NONE;
        Serial.println("CHAMBER_STATE_STERILIZING -> CHAMBER_STATE_NONE");
        return;
      }
      //tunggu hingga selesai sterilisasi
      if ((millis()-chamberTick)>=chamberSterilizationOzoneDuration){
        chamberPostUvTick = millis();//reset uv post tick too
        bool keepInverter = chamberPostUvEnable && (chamberPostUvDuration>0);
        ChamberTurnOffAllSwitch(keepInverter);
        chamberTick = millis(); //keep reset it
        chamberState = CHAMBER_STATE_START_UNLOAD;
        Serial.println("CHAMBER_STATE_STERILIZING -> CHAMBER_STATE_START_UNLOAD");
      }
    };
    break;
    case CHAMBER_STATE_START_UNLOAD:{
      bool lowerSwitchState = GetInSwitchState(STERILIZED_CHAMBER_FLOAT_SWITCH_LOWER);
      bool upperSwitchState = GetInSwitchState(STERILIZED_CHAMBER_FLOAT_SWITCH_LOWER);
      chamberTick = millis(); //reset it
      if (!lowerSwitchState && !upperSwitchState){
        //fill to lower first
        chamberState = CHAMBER_STATE_START_UNLOAD_TO_LOWER;
        Serial.println("CHAMBER_STATE_START_UNLOAD -> CHAMBER_STATE_START_UNLOAD_TO_LOWER");
      }else if (lowerSwitchState && !upperSwitchState){
        //fill to upper
        chamberState = CHAMBER_STATE_START_UNLOAD_TO_UPPER;
        Serial.println("CHAMBER_STATE_START_UNLOAD -> CHAMBER_STATE_START_UNLOAD_TO_UPPER");
      }else if (lowerSwitchState && upperSwitchState){
        //already full
        ChamberTurnOffAllSwitch(false); //turn off all switch including inverter
        if (chamberAutomatic){
          chamberState = CHAMBER_STATE_IDLE;
          Serial.println("CHAMBER_STATE_START_UNLOAD -> CHAMBER_STATE_IDLE");
        }else{
          chamberState = CHAMBER_STATE_NONE;
          Serial.println("CHAMBER_STATE_START_UNLOAD -> CHAMBER_STATE_NONE");
        }
        chamberSterilizationSuccess = true;
      }else{
        //float switch error
        ChamberTurnOffAllSwitch(false); //turn off all switch including inverter
        chamberError = true;
        chamberErrorCode = CHAMBER_ERROR_STORAGE_LOWER_FLOAT_SWITCH;
        chamberErrorString = "Tank 3 float switch error";
        Serial.print("Error: ");
        Serial.println(chamberErrorString);;
        chamberState = CHAMBER_STATE_NONE;
        Serial.println("CHAMBER_STATE_START_UNLOAD -> CHAMBER_STATE_NONE");
        return;
      }
      //switch post uv when it reach at least lowest level and used
      if (chamberPostUvEnable && lowerSwitchState){
        chamberPostUvTick = millis();//reset uv post tick too
        if (chamberPostUvEnable && (chamberPostUvDuration>0)){
          //turn on uv
          SetSwitchState(STERILIZED_UV_SWITCH, true);
        }
      }
    };
    break;
    case CHAMBER_STATE_START_UNLOAD_TO_LOWER:{
      //switch on the pump
      if (!GetSwitchState(STERILIZED_CHAMBER_FILL_PUMP_SWITCH)){
        Serial.println("ChamberSystemRoutine: TURN ON OUT PUMP SWITCH");
        SetSwitchState(STERILIZED_CHAMBER_FILL_PUMP_SWITCH, true);
      }
      bool lowerSwitchState = GetInSwitchState(STERILIZED_CHAMBER_FLOAT_SWITCH_LOWER);
      bool upperSwitchState = GetInSwitchState(STERILIZED_CHAMBER_FLOAT_SWITCH_UPPER);
      if (lowerSwitchState || upperSwitchState){
        //check if lower float switch on
        //try to fill till full
        ClearChamberError();
        chamberTick = millis();
        chamberState = CHAMBER_STATE_START_UNLOAD_TO_UPPER;
        Serial.println("CHAMBER_STATE_START_UNLOAD_TO_LOWER -> CHAMBER_STATE_START_UNLOAD_TO_UPPER");
      }else{
        //check if timed out
        if (millis()-chamberTick>=chamberExpectedTimeToUnloadFilledLower){
          ChamberTurnOffAllSwitch(false); //turn off all switch including inverter
          chamberError = true;
          chamberErrorCode = CHAMBER_ERROR_STORAGE_LOWER_FLOAT_SWITCH_OR_NO_WATER;
          chamberErrorString = "Tank 2 leaked, or Tank 3 lower float switch error, or output pump error.";
          Serial.println("Chamber Error -> CHAMBER_ERROR_STORAGE_LOWER_FLOAT_SWITCH_OR_NO_WATER");
          if (chamberAutomatic){
            chamberState = CHAMBER_STATE_IDLE;
            Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_LOWER -> CHAMBER_STATE_IDLE");
          }else{
            chamberState = CHAMBER_STATE_NONE;
            Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_LOWER -> CHAMBER_STATE_NONE");
          }
          return;
        }
        //check if tank 2 empty. indicate leaking
        chamber_SterilizationLowerFloatSwitchStateCurrent = GetInSwitchState(STERILIZATION_CHAMBER_FLOAT_SWITCH_LOWER);
        if (!chamber_SterilizationLowerFloatSwitchStateCurrent){
          ChamberTurnOffAllSwitch(false); //turn off all switch including inverter
          chamberError = true;
          chamberErrorCode = CHAMBER_ERROR_LOWER_FLOAT_SWITCH;
          chamberErrorString = "Tank 2 leaked or lower float switch error";
          Serial.println("Chamber Error -> CHAMBER_ERROR_LOWER_FLOAT_SWITCH");
          if (chamberAutomatic){
            chamberState = CHAMBER_STATE_IDLE;
            Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_LOWER -> CHAMBER_STATE_IDLE");
          }else{
            chamberState = CHAMBER_STATE_NONE;
            Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_LOWER -> CHAMBER_STATE_NONE");
          }
          return;
        }
      }
      //manage post uv
      if (chamberPostUvEnable && lowerSwitchState){
        chamberPostUvTick = millis();//reset uv post tick too
        if (chamberPostUvEnable && (chamberPostUvDuration>0)){
          //turn on uv
          SetSwitchState(STERILIZED_UV_SWITCH, true);
        }
      }
    };
    break;
    case CHAMBER_STATE_START_UNLOAD_TO_UPPER:{
      //switch on the pump
      if (!GetSwitchState(STERILIZED_CHAMBER_FILL_PUMP_SWITCH)){
        Serial.println("ChamberSystemRoutine: TURN ON OUT PUMP SWITCH");
        SetSwitchState(STERILIZED_CHAMBER_FILL_PUMP_SWITCH, true);
      }
      //manage post uv
      bool lowerSwitchState = GetInSwitchState(STERILIZED_CHAMBER_FLOAT_SWITCH_LOWER);
      if (chamberPostUvEnable && lowerSwitchState){
        if (chamberPostUvEnable && (chamberPostUvDuration>0) && (!GetSwitchState(STERILIZED_UV_SWITCH) && (millis()-chamberPostUvTick<chamberPostUvDuration))){
          //turn on uv but do not update the timer
          SetSwitchState(STERILIZED_UV_SWITCH, true);
        }
      }
      //bool lowerSwitchState = GetInSwitchState(STERILIZED_CHAMBER_FLOAT_SWITCH_LOWER);
      bool upperSwitchState = GetInSwitchState(STERILIZED_CHAMBER_FLOAT_SWITCH_UPPER);
      chamber_SterilizationLowerFloatSwitchStateCurrent = GetInSwitchState(STERILIZATION_CHAMBER_FLOAT_SWITCH_LOWER);
      if (upperSwitchState || !chamber_SterilizationLowerFloatSwitchStateCurrent){
        chamberSterilizationSuccess = false; //done!
        ChamberTurnOffAllSwitch(true); //turn off all switch excluding inverter
        ClearChamberError();
        chamberTick = millis();
        if (chamberAutomatic){
          chamberState = CHAMBER_STATE_IDLE;
          Serial.println("CHAMBER_STATE_START_UNLOAD_TO_UPPER -> CHAMBER_STATE_IDLE");
        }else{
          chamberState = CHAMBER_STATE_NONE;
          Serial.println("CHAMBER_STATE_START_UNLOAD_TO_UPPER -> CHAMBER_STATE_NONE");
        }
      }else{
        //check if timed out
        if (millis()-chamberTick>=chamberExpectedTimeToUnloadFilledUpper){
          ChamberTurnOffAllSwitch(false); //turn off all switch including inverter
          chamberError = true;
          chamberErrorCode = CHAMBER_ERROR_STORAGE_UPPER_FLOAT_SWITCH_OR_NO_WATER;
          chamberErrorString = "Tank 2 leaked, or Tank 1 upper float switch error, or output pump error.";
          Serial.println("Chamber Error -> CHAMBER_ERROR_STORAGE_UPPER_FLOAT_SWITCH_OR_NO_WATER");
          if (chamberAutomatic){
            chamberState = CHAMBER_STATE_IDLE;
            Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_UPPER -> CHAMBER_STATE_IDLE");
          }else{
            chamberState = CHAMBER_STATE_NONE;
            Serial.println("CHAMBER_STATE_CHECK_FILLING_TO_UPPER -> CHAMBER_STATE_NONE");
          }
          return;
        }
      }
      
    };
    break;
  }
}
//==========================================================================

void StartChamberSystem(bool automatic, bool forceRunIfAutomatic = false, bool usePostUv=false){
  ClearChamberError();
  chamberAutomatic = automatic;
  chamberPostUvEnable = usePostUv;
  chamberSterilizationSuccess = false;
  
  bool runNow = false;
  
  if (chamberAutomatic){
    RefreshInSwitchState();
    chamberState = CHAMBER_STATE_IDLE;
    runNow = forceRunIfAutomatic;
    if (!runNow) Serial.println("StartChamberSystem -> CHAMBER_STATE_IDLE");
  }else{
    runNow = true;
  }

  if (runNow){
    RefreshInSwitchState();
    chamberState = CHAMBER_STATE_START_FILLING;
    Serial.println("StartChamberSystem -> CHAMBER_STATE_START_FILLING");
  }
}

void StopChamberSystem(){
  ClearChamberError();
  chamberState = CHAMBER_STATE_NONE;
  ChamberSystemRoutine(); //call it once
  ChamberTurnOffAllSwitch(false);//make sure to turn off all
}

void ChamberSetPostUvDuration(uint32_t v){
  chamberPostUvDuration = v;
}

void ChamberSetSterlizationDuration(uint32_t v){
  chamberSterilizationOzoneDuration = v;
}
