|  | @@ -39,29 +39,32 @@ LiquidCrystal g_lcd(PIN_LCD_RS, PIN_LCD_ENABLE, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_
 | 
		
	
		
			
			| 39 | 39 |  UiStateEnum g_modeSequence[3] = {WaterSetting, HeaterSetting, Lighting};
 | 
		
	
		
			
			| 40 | 40 |  #define MODE_SEQUENCE_COUNT (sizeof(g_modeSequence) / sizeof(*g_modeSequence))
 | 
		
	
		
			
			| 41 | 41 |  
 | 
		
	
		
			
			| 42 |  | -BoilerState g_boilerState = {
 | 
		
	
		
			
			| 43 |  | -        .water = {
 | 
		
	
		
			
			| 44 |  | -              .current = 44,
 | 
		
	
		
			
			| 45 |  | -              .setting = 44,
 | 
		
	
		
			
			| 46 |  | -              .isActive = false
 | 
		
	
		
			
			| 47 |  | -        },
 | 
		
	
		
			
			| 48 |  | -        .heater = {
 | 
		
	
		
			
			| 49 |  | -              .current = 44,
 | 
		
	
		
			
			| 50 |  | -              .setting = 44,
 | 
		
	
		
			
			| 51 |  | -              .isActive = false
 | 
		
	
		
			
			|  | 42 | +GlobalState g_globalState = {
 | 
		
	
		
			
			|  | 43 | +        .appState = {
 | 
		
	
		
			
			|  | 44 | +                .water = {
 | 
		
	
		
			
			|  | 45 | +                        .current = 44,
 | 
		
	
		
			
			|  | 46 | +                        .setting = 44,
 | 
		
	
		
			
			|  | 47 | +                        .isActive = false
 | 
		
	
		
			
			|  | 48 | +                },
 | 
		
	
		
			
			|  | 49 | +                .heater = {
 | 
		
	
		
			
			|  | 50 | +                        .current = 44,
 | 
		
	
		
			
			|  | 51 | +                        .setting = 44,
 | 
		
	
		
			
			|  | 52 | +                        .isActive = false
 | 
		
	
		
			
			|  | 53 | +                }
 | 
		
	
		
			
			| 52 | 54 |          },
 | 
		
	
		
			
			| 53 | 55 |          .uiState = {
 | 
		
	
		
			
			| 54 | 56 |                  .state = Lighting,
 | 
		
	
		
			
			| 55 | 57 |                  .lastOpMs = 0,
 | 
		
	
		
			
			| 56 |  | -                .modeSequenceIndex = MODE_SEQUENCE_COUNT - 1
 | 
		
	
		
			
			|  | 58 | +                .modeSequenceIndex = MODE_SEQUENCE_COUNT - 1,
 | 
		
	
		
			
			|  | 59 | +                .isUpdateNeeded = true
 | 
		
	
		
			
			| 57 | 60 |          }
 | 
		
	
		
			
			| 58 | 61 |  };
 | 
		
	
		
			
			| 59 | 62 |  
 | 
		
	
		
			
			| 60 |  | -void printStateLine(char prefix, const BoilerLineState& boilerLineState, bool isModifying, bool isActive, LiquidCrystal lcd)
 | 
		
	
		
			
			|  | 63 | +void printStateLine(char prefix, const BoilerItemState& boilerItemState, bool isModifying, bool isActive, LiquidCrystal lcd)
 | 
		
	
		
			
			| 61 | 64 |  {
 | 
		
	
		
			
			| 62 | 65 |      char curTmp[5], setTmp[5], tmp[17];
 | 
		
	
		
			
			| 63 |  | -    dtostrf(boilerLineState.current / 2.0f, 2, 1, curTmp);
 | 
		
	
		
			
			| 64 |  | -    dtostrf(boilerLineState.setting / 2.0f, 2, 1, setTmp);
 | 
		
	
		
			
			|  | 66 | +    dtostrf(boilerItemState.current / 2.0f, 2, 1, curTmp);
 | 
		
	
		
			
			|  | 67 | +    dtostrf(boilerItemState.setting / 2.0f, 2, 1, setTmp);
 | 
		
	
		
			
			| 65 | 68 |      int count = snprintf(tmp, sizeof(tmp), "%c: %s [%s]%c%c", prefix, curTmp, setTmp, isModifying ? '<' : ' ', isActive ? LCD_CHAR_SENSOR : ' ');
 | 
		
	
		
			
			| 66 | 69 |      for (; count < 17; ++count)
 | 
		
	
		
			
			| 67 | 70 |      {
 | 
		
	
	
		
			
			|  | @@ -71,12 +74,47 @@ void printStateLine(char prefix, const BoilerLineState& boilerLineState, bool is
 | 
		
	
		
			
			| 71 | 74 |      lcd.print(tmp);
 | 
		
	
		
			
			| 72 | 75 |  }
 | 
		
	
		
			
			| 73 | 76 |  
 | 
		
	
		
			
			| 74 |  | -void printState(const BoilerState& boilerState, LiquidCrystal lcd)
 | 
		
	
		
			
			|  | 77 | +void printState(const GlobalState& globalState, LiquidCrystal lcd)
 | 
		
	
		
			
			| 75 | 78 |  {
 | 
		
	
		
			
			|  | 79 | +    Serial.println("Updating display");
 | 
		
	
		
			
			|  | 80 | +
 | 
		
	
		
			
			| 76 | 81 |      lcd.setCursor(0, 0);
 | 
		
	
		
			
			| 77 |  | -    printStateLine('S', boilerState.water, boilerState.uiState.state == WaterSetting, boilerState.water.isActive, lcd);
 | 
		
	
		
			
			|  | 82 | +    printStateLine('S', globalState.appState.water, globalState.uiState.state == WaterSetting, globalState.appState.water.isActive, lcd);
 | 
		
	
		
			
			| 78 | 83 |      lcd.setCursor(0, 1);
 | 
		
	
		
			
			| 79 |  | -    printStateLine('C', boilerState.heater, boilerState.uiState.state == HeaterSetting, boilerState.heater.isActive, lcd);
 | 
		
	
		
			
			|  | 84 | +    printStateLine('C', globalState.appState.heater, globalState.uiState.state == HeaterSetting, globalState.appState.heater.isActive, lcd);
 | 
		
	
		
			
			|  | 85 | +}
 | 
		
	
		
			
			|  | 86 | +
 | 
		
	
		
			
			|  | 87 | +void setState(GlobalState& boilerState, UiStateEnum state)
 | 
		
	
		
			
			|  | 88 | +{
 | 
		
	
		
			
			|  | 89 | +    char tmp[50];
 | 
		
	
		
			
			|  | 90 | +    snprintf(tmp, sizeof(tmp), "Changing state %i => %i", g_globalState.uiState.state, state);
 | 
		
	
		
			
			|  | 91 | +    Serial.println(tmp);
 | 
		
	
		
			
			|  | 92 | +    
 | 
		
	
		
			
			|  | 93 | +    if (state == Lighting)
 | 
		
	
		
			
			|  | 94 | +    {
 | 
		
	
		
			
			|  | 95 | +        digitalWrite(PIN_LCD_LED, 1);
 | 
		
	
		
			
			|  | 96 | +    }
 | 
		
	
		
			
			|  | 97 | +    else if (state == Hibernate)
 | 
		
	
		
			
			|  | 98 | +    {
 | 
		
	
		
			
			|  | 99 | +        digitalWrite(PIN_LCD_LED, 0);
 | 
		
	
		
			
			|  | 100 | +        g_globalState.uiState.modeSequenceIndex = MODE_SEQUENCE_COUNT - 1;
 | 
		
	
		
			
			|  | 101 | +    }
 | 
		
	
		
			
			|  | 102 | +    g_globalState.uiState.state = state;
 | 
		
	
		
			
			|  | 103 | +    g_globalState.uiState.isUpdateNeeded = true;
 | 
		
	
		
			
			|  | 104 | +}
 | 
		
	
		
			
			|  | 105 | +
 | 
		
	
		
			
			|  | 106 | +void checkBoilerItem(BoilerItemState& boilerItemState, UiState uiState)
 | 
		
	
		
			
			|  | 107 | +{
 | 
		
	
		
			
			|  | 108 | +    if (!boilerItemState.isActive && (boilerItemState.current <= boilerItemState.setting - TEMP_TRIGGER))
 | 
		
	
		
			
			|  | 109 | +    {
 | 
		
	
		
			
			|  | 110 | +        boilerItemState.isActive = true;
 | 
		
	
		
			
			|  | 111 | +        uiState.isUpdateNeeded = true;
 | 
		
	
		
			
			|  | 112 | +    }
 | 
		
	
		
			
			|  | 113 | +    else if (boilerItemState.isActive && (boilerItemState.current >= boilerItemState.setting))
 | 
		
	
		
			
			|  | 114 | +    {
 | 
		
	
		
			
			|  | 115 | +        boilerItemState.isActive = false;
 | 
		
	
		
			
			|  | 116 | +        uiState.isUpdateNeeded = true;
 | 
		
	
		
			
			|  | 117 | +    }
 | 
		
	
		
			
			| 80 | 118 |  }
 | 
		
	
		
			
			| 81 | 119 |  
 | 
		
	
		
			
			| 82 | 120 |  void setup()
 | 
		
	
	
		
			
			|  | @@ -91,98 +129,83 @@ void setup()
 | 
		
	
		
			
			| 91 | 129 |  
 | 
		
	
		
			
			| 92 | 130 |      g_lcd.begin(16, 2);
 | 
		
	
		
			
			| 93 | 131 |      g_lcd.createChar(LCD_CHAR_SENSOR, g_sensorChar);
 | 
		
	
		
			
			| 94 |  | -    printState(g_boilerState, g_lcd);
 | 
		
	
		
			
			| 95 | 132 |  
 | 
		
	
		
			
			| 96 | 133 |      Serial.begin(9600);
 | 
		
	
		
			
			| 97 | 134 |  }
 | 
		
	
		
			
			| 98 | 135 |  
 | 
		
	
		
			
			| 99 | 136 |  void loop()
 | 
		
	
		
			
			| 100 | 137 |  {
 | 
		
	
		
			
			| 101 |  | -    bool needLcdUpdate = false;
 | 
		
	
		
			
			| 102 |  | -    unsigned long currentMs = millis();
 | 
		
	
		
			
			|  | 138 | +    const auto& currentMs = millis();
 | 
		
	
		
			
			| 103 | 139 |  
 | 
		
	
		
			
			| 104 | 140 |      for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
 | 
		
	
		
			
			| 105 | 141 |      {
 | 
		
	
		
			
			| 106 | 142 |          g_buttons[i]->read();
 | 
		
	
		
			
			| 107 | 143 |          if (g_buttons[i]->isPressed())
 | 
		
	
		
			
			| 108 | 144 |          {
 | 
		
	
		
			
			| 109 |  | -            g_boilerState.uiState.lastOpMs = currentMs;
 | 
		
	
		
			
			|  | 145 | +            g_globalState.uiState.lastOpMs = currentMs;
 | 
		
	
		
			
			|  | 146 | +            break;
 | 
		
	
		
			
			| 110 | 147 |          }
 | 
		
	
		
			
			| 111 | 148 |      }
 | 
		
	
		
			
			| 112 | 149 |  
 | 
		
	
		
			
			| 113 | 150 |  
 | 
		
	
		
			
			| 114 |  | -    if (g_boilerState.uiState.state == Hibernate)
 | 
		
	
		
			
			|  | 151 | +    if (g_globalState.uiState.state == Hibernate)
 | 
		
	
		
			
			| 115 | 152 |      {
 | 
		
	
		
			
			| 116 | 153 |          for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
 | 
		
	
		
			
			| 117 | 154 |          {
 | 
		
	
		
			
			| 118 | 155 |              if (g_buttons[i]->wasReleased())
 | 
		
	
		
			
			| 119 | 156 |              {
 | 
		
	
		
			
			| 120 |  | -                g_boilerState.uiState.state = Lighting;
 | 
		
	
		
			
			| 121 |  | -                digitalWrite(PIN_LCD_LED, 1);
 | 
		
	
		
			
			|  | 157 | +                setState(g_globalState, Lighting);
 | 
		
	
		
			
			| 122 | 158 |                  break;
 | 
		
	
		
			
			| 123 | 159 |              }
 | 
		
	
		
			
			| 124 | 160 |          }
 | 
		
	
		
			
			| 125 |  | -        needLcdUpdate = true;
 | 
		
	
		
			
			| 126 | 161 |      }
 | 
		
	
		
			
			| 127 | 162 |      else
 | 
		
	
		
			
			| 128 | 163 |      {
 | 
		
	
		
			
			| 129 |  | -        if (currentMs - g_boilerState.uiState.lastOpMs >= HIBERNATE_DELAY)
 | 
		
	
		
			
			|  | 164 | +        if (currentMs - g_globalState.uiState.lastOpMs >= HIBERNATE_DELAY)
 | 
		
	
		
			
			| 130 | 165 |          {
 | 
		
	
		
			
			| 131 |  | -            g_boilerState.uiState.state = Hibernate;
 | 
		
	
		
			
			| 132 |  | -            digitalWrite(PIN_LCD_LED, 0);
 | 
		
	
		
			
			| 133 |  | -            g_boilerState.uiState.modeSequenceIndex = MODE_SEQUENCE_COUNT - 1;
 | 
		
	
		
			
			| 134 |  | -            needLcdUpdate = true;
 | 
		
	
		
			
			|  | 166 | +            setState(g_globalState, Hibernate);
 | 
		
	
		
			
			| 135 | 167 |          }
 | 
		
	
		
			
			| 136 | 168 |          else
 | 
		
	
		
			
			| 137 | 169 |          {
 | 
		
	
		
			
			| 138 |  | -            if (g_btnMode.wasPressed())
 | 
		
	
		
			
			|  | 170 | +            if (g_btnMode.wasReleased())
 | 
		
	
		
			
			| 139 | 171 |              {
 | 
		
	
		
			
			| 140 |  | -                g_boilerState.uiState.modeSequenceIndex =
 | 
		
	
		
			
			| 141 |  | -                        (g_boilerState.uiState.modeSequenceIndex + 1) % MODE_SEQUENCE_COUNT;
 | 
		
	
		
			
			| 142 |  | -                g_boilerState.uiState.state = g_modeSequence[g_boilerState.uiState.modeSequenceIndex];
 | 
		
	
		
			
			| 143 |  | -                needLcdUpdate = true;
 | 
		
	
		
			
			|  | 172 | +                g_globalState.uiState.modeSequenceIndex = (g_globalState.uiState.modeSequenceIndex + 1) % MODE_SEQUENCE_COUNT;
 | 
		
	
		
			
			|  | 173 | +                setState(g_globalState, g_modeSequence[g_globalState.uiState.modeSequenceIndex]);
 | 
		
	
		
			
			| 144 | 174 |              }
 | 
		
	
		
			
			| 145 |  | -            else
 | 
		
	
		
			
			|  | 175 | +            else if (g_btnMinus.wasReleased() || g_btnPlus.wasReleased())
 | 
		
	
		
			
			| 146 | 176 |              {
 | 
		
	
		
			
			| 147 |  | -                temp_t* setting = NULL;
 | 
		
	
		
			
			| 148 |  | -                if (g_boilerState.uiState.state == WaterSetting)
 | 
		
	
		
			
			|  | 177 | +                BoilerItemState* itemState = NULL;
 | 
		
	
		
			
			|  | 178 | +                if (g_globalState.uiState.state == WaterSetting)
 | 
		
	
		
			
			| 149 | 179 |                  {
 | 
		
	
		
			
			| 150 |  | -                    setting = &g_boilerState.water.setting;
 | 
		
	
		
			
			|  | 180 | +                    itemState = &g_globalState.appState.water;
 | 
		
	
		
			
			| 151 | 181 |                  }
 | 
		
	
		
			
			| 152 |  | -                else if (g_boilerState.uiState.state == HeaterSetting)
 | 
		
	
		
			
			|  | 182 | +                else if (g_globalState.uiState.state == HeaterSetting)
 | 
		
	
		
			
			| 153 | 183 |                  {
 | 
		
	
		
			
			| 154 |  | -                    setting = &g_boilerState.heater.setting;
 | 
		
	
		
			
			|  | 184 | +                    itemState = &g_globalState.appState.heater;
 | 
		
	
		
			
			| 155 | 185 |                  }
 | 
		
	
		
			
			| 156 | 186 |  
 | 
		
	
		
			
			| 157 |  | -                if (setting)
 | 
		
	
		
			
			|  | 187 | +                if (itemState)
 | 
		
	
		
			
			| 158 | 188 |                  {
 | 
		
	
		
			
			| 159 | 189 |                      if (g_btnMinus.wasReleased())
 | 
		
	
		
			
			| 160 | 190 |                      {
 | 
		
	
		
			
			| 161 |  | -                        --(*setting);
 | 
		
	
		
			
			|  | 191 | +                        --itemState->setting;
 | 
		
	
		
			
			| 162 | 192 |                      }
 | 
		
	
		
			
			| 163 | 193 |                      else if (g_btnPlus.wasReleased())
 | 
		
	
		
			
			| 164 | 194 |                      {
 | 
		
	
		
			
			| 165 |  | -                        ++(*setting);
 | 
		
	
		
			
			|  | 195 | +                        ++itemState->setting;
 | 
		
	
		
			
			| 166 | 196 |                      }
 | 
		
	
		
			
			| 167 |  | -                    needLcdUpdate = true;
 | 
		
	
		
			
			|  | 197 | +                    g_globalState.uiState.isUpdateNeeded = true;
 | 
		
	
		
			
			| 168 | 198 |                  }
 | 
		
	
		
			
			| 169 | 199 |              }
 | 
		
	
		
			
			| 170 | 200 |          }
 | 
		
	
		
			
			| 171 | 201 |      }
 | 
		
	
		
			
			| 172 | 202 |  
 | 
		
	
		
			
			| 173 |  | -    if (!g_boilerState.water.isActive && (g_boilerState.water.current <= g_boilerState.water.setting - TEMP_TRIGGER))
 | 
		
	
		
			
			| 174 |  | -    {
 | 
		
	
		
			
			| 175 |  | -        g_boilerState.water.isActive = true;
 | 
		
	
		
			
			| 176 |  | -        needLcdUpdate = true;
 | 
		
	
		
			
			| 177 |  | -    }
 | 
		
	
		
			
			| 178 |  | -    else if (g_boilerState.water.isActive && (g_boilerState.water.current >= g_boilerState.water.setting))
 | 
		
	
		
			
			| 179 |  | -    {
 | 
		
	
		
			
			| 180 |  | -        g_boilerState.water.isActive = false;
 | 
		
	
		
			
			| 181 |  | -        needLcdUpdate = true;
 | 
		
	
		
			
			| 182 |  | -    }
 | 
		
	
		
			
			|  | 203 | +    checkBoilerItem(g_globalState.appState.water, g_globalState.uiState);
 | 
		
	
		
			
			|  | 204 | +    checkBoilerItem(g_globalState.appState.heater, g_globalState.uiState);
 | 
		
	
		
			
			| 183 | 205 |  
 | 
		
	
		
			
			| 184 |  | -    if (needLcdUpdate)
 | 
		
	
		
			
			|  | 206 | +    if (g_globalState.uiState.isUpdateNeeded)
 | 
		
	
		
			
			| 185 | 207 |      {
 | 
		
	
		
			
			| 186 |  | -        printState(g_boilerState, g_lcd);
 | 
		
	
		
			
			|  | 208 | +        printState(g_globalState, g_lcd);
 | 
		
	
		
			
			|  | 209 | +        g_globalState.uiState.isUpdateNeeded = false;
 | 
		
	
		
			
			| 187 | 210 |      }
 | 
		
	
		
			
			| 188 | 211 |  }
 |