Browse Source

Moved to Activities and Services

master
Robin Thoni 2 months ago
parent
commit
544fd898e7
Signed by: Robin THONI <robin@rthoni.com> GPG Key ID: 4E09DEF46B99E61E

+ 96
- 430
AppCore.cpp View File

@@ -1,477 +1,143 @@
1 1
 #include "AppCore.h"
2 2
 #include "Logs.h"
3 3
 
4
+#include "defines.h"
5
+#include "Boiler.h"
6
+#include "globals.h"
7
+#include "HomeActivity.h"
8
+#include "MenuItemActivity.h"
9
+#include "LoaderActivity.h"
10
+
4 11
 #define xstr(s) str(s)
5 12
 #define str(s) #s
6 13
 
7
-#define HIBERNATE_DELAY 5000
8
-#define SENSORS_CHECK_INTERVAL 2000
9
-#define SENSORS_REQUEST_DELAY 750
10
-#define TEMP_TRIGGER ((temp_t)(5 * 10.0f))
11
-#define TEMP_INTERVAL ((temp_t)(0.5 * 10.0f))
12
-#define LCD_CHAR_SENSOR 1
13
-
14
-#define PIN_ONEWIRE A0
15
-#define PIN_RELAY2 A1
16
-#define PIN_RELAY1 A2
17
-#define PIN_BTN_CANCEL 9
18
-#define PIN_BTN_OK 10
19
-#define PIN_BTN_MINUS 11
20
-#define PIN_BTN_PLUS 12
21
-#define PIN_LCD_LED 2
22
-#define PIN_LCD_RS 8
23
-#define PIN_LCD_ENABLE 7
24
-#define PIN_LCD_D4 6
25
-#define PIN_LCD_D5 5
26
-#define PIN_LCD_D6 4
27
-#define PIN_LCD_D7 3
28
-
29
-UiStateEnum AppCore::s_menuSequence[4]{MenuWaterSetting, MenuHeaterSetting, MenuTempTrigger, MenuVersion};
30
-
31
-AppCore::AppCore()
32
-        : m_appCoreState(new AppCoreState{
33
-        .appState = {
34
-                .lastSensorRequestMs = 0,
35
-                .hasReadSensors = true,
36
-                .water = {
37
-                        .current = TEMP_T_INVALID,
14
+Button g_btnCancel(PIN_BTN_CANCEL);
15
+Button g_btnOk(PIN_BTN_OK);
16
+Button g_btnMinus(PIN_BTN_MINUS);
17
+Button g_btnPlus(PIN_BTN_PLUS);
18
+HibernateService g_hibernateService;
19
+LiquidCrystal g_lcd{PIN_LCD_RS, PIN_LCD_ENABLE, PIN_LCD_D4, PIN_LCD_D5, PIN_LCD_D6, PIN_LCD_D7};
20
+DigitalOutput g_lcdLed{PIN_LCD_LED};
21
+DigitalOutput g_relay1{PIN_RELAY1};
22
+DigitalOutput g_relay2{PIN_RELAY2};
23
+OneWire g_oneWire{PIN_ONEWIRE};
24
+DallasTemperature g_dallasTemperature{&g_oneWire};
25
+TempInput g_tempInput1{0};
26
+TempInput g_tempInput2{1};
27
+BoilerTankService g_tankService1{&g_appState.tanks[0]};
28
+BoilerTankService g_tankService2{&g_appState.tanks[1]};
29
+
30
+AppCoreState g_appState{
31
+        .tanks = {
32
+                {
33
+                        .mode = BoilerTankState::Auto,
38 34
                         .setting = 0,
39
-                        .isActive = false,
40
-                        .pinNo = PIN_RELAY1
35
+                        .tempTrigger = TEMP_TRIGGER,
36
+                        .input = &g_tempInput1,
37
+                        .relay = &g_relay1
41 38
                 },
42
-                .heater = {
43
-                        .current = TEMP_T_INVALID,
39
+                {
40
+                        .mode = BoilerTankState::Auto,
44 41
                         .setting = 0,
45
-                        .isActive = false,
46
-                        .pinNo = PIN_RELAY2
47
-                },
48
-                .tempTrigger = TEMP_TRIGGER,
49
-                .pCurrentSettingEdit = nullptr,
50
-                .currentSettingEditTmp = 0
51
-        },
52
-        .uiState = {
53
-                .state = HomeLighting,
54
-                .lastOpMs = 0,
55
-                .isUpdateNeeded = true
42
+                        .tempTrigger = TEMP_TRIGGER,
43
+                        .input = &g_tempInput2,
44
+                        .relay = &g_relay2
45
+                }
56 46
         }
57
-})
58
-          , m_pBtnCancel(new Button{PIN_BTN_CANCEL})
59
-          , m_pBtnOk(new Button{PIN_BTN_OK})
60
-          , m_pBtnMinus(new Button{PIN_BTN_MINUS})
61
-          , m_pBtnPlus(new Button{PIN_BTN_PLUS})
62
-          , m_pButtons{m_pBtnCancel, m_pBtnOk, m_pBtnMinus, m_pBtnPlus}
63
-          , m_pLcd(new LiquidCrystal{PIN_LCD_RS, PIN_LCD_ENABLE, PIN_LCD_D4, PIN_LCD_D5, PIN_LCD_D6, PIN_LCD_D7})
64
-          , m_pOneWire(new OneWire{PIN_ONEWIRE})
65
-          , m_pSensors(new DallasTemperature{m_pOneWire})
66
-          , m_sensor1{0}
67
-          , m_sensor2{0}
47
+};
48
+Storage g_storage;
49
+
50
+LoaderActivity g_loaderActivity;
51
+HomeActivity g_homeActivity;
52
+BoilerTankMenuActivity g_menuWaterActivity(&g_homeActivity, &g_menuVersionActivity, &g_menuHeaterActivity, "Sanitaire",
53
+                                           "", &g_appState.tanks[0]);
54
+BoilerTankMenuActivity g_menuHeaterActivity(&g_homeActivity, &g_menuWaterActivity, &g_menuVersionActivity, "Chauffage",
55
+                                            "", &g_appState.tanks[1]);
56
+MenuItemActivity g_menuVersionActivity(&g_homeActivity, nullptr, &g_menuHeaterActivity, &g_menuWaterActivity, "Version",
57
+                                       xstr(APP_CORE_VERSION) " - " xstr(APP_CORE_COMMIT));
58
+IActivity* g_currentActivity = nullptr;
59
+
60
+
61
+int freeRam()
68 62
 {
63
+    extern int __heap_start, * __brkval;
64
+    int v;
65
+    return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
69 66
 }
70 67
 
71
-void AppCore::setup()
68
+void AppCore::begin()
72 69
 {
73 70
     Serial.begin(9600);
74 71
     LOG_FN_BEGIN(1);
75 72
 
76
-    m_pSensors->begin();
77
-    LOG(5, "Found %i sensors", m_pSensors->getDeviceCount());
78
-    m_pSensors->getAddress(m_sensor1, 0);
79
-    m_pSensors->getAddress(m_sensor2, 1);
80
-    m_pSensors->setWaitForConversion(false);
81
-
82
-    for (auto& g_button : m_pButtons)
83
-    {
84
-        g_button->begin();
85
-    }
73
+    LOG(1, "Free RAM: %i bytes", freeRam());
86 74
 
87
-    pinMode(PIN_RELAY1, OUTPUT);
88
-    digitalWrite(PIN_RELAY1, LOW);
75
+    g_btnCancel.begin();
76
+    g_btnOk.begin();
77
+    g_btnMinus.begin();
78
+    g_btnPlus.begin();
89 79
 
90
-    pinMode(PIN_RELAY2, OUTPUT);
91
-    digitalWrite(PIN_RELAY2, LOW);
80
+    g_hibernateService.begin();
92 81
 
93
-    pinMode(PIN_LCD_LED, OUTPUT);
94
-    digitalWrite(PIN_LCD_LED, 1);
95
-
96
-    m_pLcd->begin(16, 2);
82
+    g_lcd.begin(16, 2);
97 83
     byte sensorChar[8] = {
98 84
             B00100,
99
-            B01110,
100
-            B01110,
101
-            B01110,
102
-            B01110,
103
-            B11111,
85
+            B01010,
86
+            B01010,
87
+            B01010,
88
+            B01010,
89
+            B10001,
104 90
             B11111,
105 91
             B01110,
106 92
     };
107
-    m_pLcd->createChar(LCD_CHAR_SENSOR, sensorChar);
93
+    g_lcd.createChar(LCD_CHAR_SENSOR, sensorChar);
108 94
 
95
+    g_lcdLed.begin();
96
+    g_relay1.begin();
97
+    g_relay2.begin();
109 98
 
110
-    bool allButtonsPressed = true;
111
-    for (auto& g_button : m_pButtons)
112
-    {
113
-        g_button->read();
114
-        allButtonsPressed = allButtonsPressed && g_button->isPressed();
115
-    }
116
-    if (!allButtonsPressed)
117
-    {
118
-        LOG(5, "%s: Loading settings", __FUNCTION__);
119
-        m_storage.load(*m_appCoreState);
120
-    }
121
-    else
122
-    {
123
-        LOG(5, "%s: Resetting settings", __FUNCTION__);
124
-        m_storage.save(*m_appCoreState);
125
-        m_pLcd->clear();
126
-        m_pLcd->setCursor(6, 0);
127
-        m_pLcd->print("Reset");
128
-        bool allButtonsPressed = true;
129
-        while (allButtonsPressed)
130
-        {
131
-            for (auto& g_button : m_pButtons)
132
-            {
133
-                g_button->read();
134
-                allButtonsPressed = allButtonsPressed && g_button->isPressed();
135
-            }
136
-        }
137
-    }
99
+    g_dallasTemperature.begin();
100
+    g_dallasTemperature.setWaitForConversion(false);
101
+    LOG(1, "Found %i sensors", g_dallasTemperature.getDeviceCount());
138 102
 
139
-    LOG_FN_END(1);
140
-}
103
+    g_tempInput1.begin();
104
+    g_tempInput2.begin();
141 105
 
142
-void AppCore::loop()
143
-{
144
-    LOG_FN_BEGIN(50);
106
+    g_tankService1.begin();
107
+    g_tankService2.begin();
145 108
 
146
-    const auto& currentMs = millis();
147
-
148
-    if (currentMs - m_appCoreState->appState.lastSensorRequestMs >= SENSORS_CHECK_INTERVAL)
149
-    {
150
-        m_appCoreState->appState.lastSensorRequestMs = currentMs;
151
-        m_appCoreState->appState.hasReadSensors = false;
152
-        m_pSensors->requestTemperaturesByAddress(m_sensor1);
153
-        m_pSensors->requestTemperaturesByAddress(m_sensor2);
154
-    }
155
-    if (currentMs - m_appCoreState->appState.lastSensorRequestMs >= SENSORS_REQUEST_DELAY &&
156
-        !m_appCoreState->appState.hasReadSensors)
157
-    {
158
-        m_appCoreState->appState.hasReadSensors = true;
159
-        readAndUpdateSensors(&m_appCoreState->appState.water, m_sensor1);
160
-        readAndUpdateSensors(&m_appCoreState->appState.heater, m_sensor2);
161
-        checkBoilerItem(&m_appCoreState->appState.water);
162
-        checkBoilerItem(&m_appCoreState->appState.heater);
163
-    }
164
-
165
-
166
-    for (auto& pButton : m_pButtons)
167
-    {
168
-        pButton->read();
169
-        if (pButton->isPressed())
170
-        {
171
-            m_appCoreState->uiState.lastOpMs = currentMs;
172
-            break;
173
-        }
174
-    }
175
-
176
-
177
-    if (m_appCoreState->uiState.state == HomeHibernate)
178
-    {
179
-        for (auto& pButton : m_pButtons)
180
-        {
181
-            if (pButton->wasReleased())
182
-            {
183
-                setState(HomeLighting);
184
-                break;
185
-            }
186
-        }
187
-    }
188
-    else
189
-    {
190
-        if (currentMs - m_appCoreState->uiState.lastOpMs >= HIBERNATE_DELAY)
191
-        {
192
-            setState(HomeHibernate);
193
-        }
194
-        else
195
-        {
196
-            if (m_appCoreState->uiState.state == HomeLighting)
197
-            {
198
-                if (m_pBtnCancel->wasReleased())
199
-                {
200
-                    setState(HomeHibernate);
201
-                }
202
-                else if (m_pBtnOk->wasReleased())
203
-                {
204
-                    setState(s_menuSequence[0]);
205
-                }
206
-            }
207
-            else if (m_appCoreState->uiState.state == MenuWaterSetting || m_appCoreState->uiState.state == MenuHeaterSetting || m_appCoreState->uiState.state == MenuTempTrigger || m_appCoreState->uiState.state == MenuVersion)
208
-            {
209
-                if (m_pBtnCancel->wasReleased())
210
-                {
211
-                    setState(HomeLighting);
212
-                }
213
-                else if (m_pBtnOk->wasReleased())
214
-                {
215
-                    UiStateEnum nextState = m_appCoreState->uiState.state;
216
-                    if (m_appCoreState->uiState.state == MenuWaterSetting)
217
-                    {
218
-                        nextState = MenuWaterSettingEdit;
219
-                    }
220
-                    else if (m_appCoreState->uiState.state == MenuHeaterSetting)
221
-                    {
222
-                        nextState = MenuHeaterSettingEdit;
223
-                    }
224
-                    else if (m_appCoreState->uiState.state == MenuTempTrigger)
225
-                    {
226
-                        nextState = MenuTempTriggerEdit;
227
-                    }
228
-                    setState(nextState);
229
-                }
230
-                else if (m_pBtnMinus->wasReleased() || m_pBtnPlus->wasReleased())
231
-                {
232
-                    int idx = 0;
233
-                    while (m_appCoreState->uiState.state != s_menuSequence[idx])
234
-                    {
235
-                        ++idx;
236
-                    }
237
-                    auto shift = m_pBtnMinus->wasReleased() ? (-1) : 1;
238
-                    setState(s_menuSequence[(idx + shift) % ((sizeof(s_menuSequence) / sizeof(*s_menuSequence)))]);
239
-                }
240
-            }
241
-            else if (m_appCoreState->uiState.state == MenuWaterSettingEdit || m_appCoreState->uiState.state == MenuHeaterSettingEdit || m_appCoreState->uiState.state == MenuTempTriggerEdit)
242
-            {
243
-                if (m_pBtnCancel->wasReleased() || m_pBtnOk->wasReleased())
244
-                {
245
-                    UiStateEnum nextState = m_appCoreState->uiState.state;
246
-                    if (m_appCoreState->uiState.state == MenuWaterSettingEdit)
247
-                    {
248
-                        nextState = MenuWaterSetting;
249
-                    }
250
-                    else if (m_appCoreState->uiState.state == MenuHeaterSettingEdit)
251
-                    {
252
-                        nextState = MenuHeaterSetting;
253
-                    }
254
-                    else if (m_appCoreState->uiState.state == MenuTempTriggerEdit)
255
-                    {
256
-                        nextState = MenuTempTrigger;
257
-                    }
258
-
259
-                    if (m_pBtnOk->wasReleased())
260
-                    {
261
-                        *m_appCoreState->appState.pCurrentSettingEdit = m_appCoreState->appState.currentSettingEditTmp;
262
-                    }
263
-
264
-                    m_appCoreState->appState.currentSettingEditTmp = 0;
265
-                    m_appCoreState->appState.pCurrentSettingEdit = nullptr;
266
-                    setState(nextState);
267
-                }
268
-                else if (m_pBtnMinus->wasReleased() || m_pBtnPlus->wasReleased())
269
-                {
270
-                    m_appCoreState->appState.currentSettingEditTmp += (m_pBtnMinus->wasReleased() ? -1 : 1) * TEMP_INTERVAL;
271
-                    m_appCoreState->uiState.isUpdateNeeded = true;
272
-                }
273
-            }
274
-        }
275
-    }
276
-
277
-    if (m_appCoreState->uiState.isUpdateNeeded)
278
-    {
279
-        printState();
280
-    }
281
-
282
-    LOG_FN_END(50);
283
-}
284
-
285
-void AppCore::setState(
286
-        UiStateEnum state
287
-)
288
-{
289
-    LOG_FN_BEGIN(1);
290
-    LOG(5, "Changing state %i => %i", m_appCoreState->uiState.state, state);
291
-
292
-    if (state == HomeLighting)
293
-    {
294
-        digitalWrite(PIN_LCD_LED, 1);
295
-    }
296
-    else if (state == HomeHibernate)
297
-    {
298
-        digitalWrite(PIN_LCD_LED, 0);
299
-        m_storage.save(*m_appCoreState);
300
-    }
301
-    else if (state == MenuWaterSettingEdit)
302
-    {
303
-        m_appCoreState->appState.currentSettingEditTmp = m_appCoreState->appState.water.setting;
304
-        m_appCoreState->appState.pCurrentSettingEdit = &m_appCoreState->appState.water.setting;
305
-    }
306
-    else if (state == MenuHeaterSettingEdit)
307
-    {
308
-        m_appCoreState->appState.currentSettingEditTmp = m_appCoreState->appState.heater.setting;
309
-        m_appCoreState->appState.pCurrentSettingEdit = &m_appCoreState->appState.heater.setting;
310
-    }
311
-    else if (state == MenuTempTriggerEdit)
312
-    {
313
-        m_appCoreState->appState.currentSettingEditTmp = m_appCoreState->appState.tempTrigger;
314
-        m_appCoreState->appState.pCurrentSettingEdit = &m_appCoreState->appState.tempTrigger;
315
-    }
316
-    m_appCoreState->uiState.state = state;
317
-    m_appCoreState->uiState.isUpdateNeeded = true;
109
+    setActivity(&g_loaderActivity);
318 110
 
319 111
     LOG_FN_END(1);
320 112
 }
321 113
 
322
-void AppCore::checkBoilerItem(
323
-        BoilerItemState* boilerItemState
324
-)
325
-{
326
-    LOG_FN_BEGIN(2);
327
-
328
-    if (!boilerItemState->isActive && boilerItemState->current != TEMP_T_INVALID &&
329
-        boilerItemState->current <= boilerItemState->setting - m_appCoreState->appState.tempTrigger)
330
-    {
331
-        boilerItemState->isActive = true;
332
-        digitalWrite(boilerItemState->pinNo, HIGH);
333
-        m_appCoreState->uiState.isUpdateNeeded = true;
334
-    }
335
-    else if (boilerItemState->isActive &&
336
-             (boilerItemState->current == TEMP_T_INVALID || boilerItemState->current >= boilerItemState->setting))
337
-    {
338
-        boilerItemState->isActive = false;
339
-        digitalWrite(boilerItemState->pinNo, LOW);
340
-        m_appCoreState->uiState.isUpdateNeeded = true;
341
-    }
342
-
343
-    LOG_FN_END(2);
344
-}
345
-
346
-void AppCore::readAndUpdateSensors(
347
-        BoilerItemState* boilerItemState
348
-        , const uint8_t* sensor
349
-)
114
+void AppCore::loop()
350 115
 {
351
-    LOG_FN_BEGIN(2);
116
+    LOG_FN_BEGIN(50);
352 117
 
353
-    auto raw = m_pSensors->getTempC(sensor);
354
-    temp_t temp = TEMP_T_INVALID;
355
-    if (raw != DEVICE_DISCONNECTED_C)
356
-    {
357
-        temp = (temp_t) (raw * 10);
358
-    }
118
+    g_btnCancel.read();
119
+    g_btnOk.read();
120
+    g_btnMinus.read();
121
+    g_btnPlus.read();
359 122
 
360
-    if (temp != boilerItemState->current)
361
-    {
362
-        boilerItemState->current = temp;
363
-        m_appCoreState->uiState.isUpdateNeeded = true;
364
-    }
123
+    g_tempInput1.loop();
124
+    g_tempInput2.loop();
365 125
 
366
-    LOG_FN_END(2);
367
-}
126
+    g_tankService1.loop();
127
+    g_tankService2.loop();
368 128
 
369
-void AppCore::printState()
370
-{
371
-    LOG_FN_BEGIN(2);
129
+    g_hibernateService.loop();
372 130
 
373
-    if (m_appCoreState->uiState.state == HomeLighting || m_appCoreState->uiState.state == HomeHibernate)
374
-    {
375
-        m_pLcd->setCursor(0, 0);
376
-        printStateLine('S', &m_appCoreState->appState.water);
377
-        m_pLcd->setCursor(0, 1);
378
-        printStateLine('C', &m_appCoreState->appState.heater);
379
-    }
380
-    else if (m_appCoreState->uiState.state == MenuWaterSetting || m_appCoreState->uiState.state == MenuHeaterSetting || m_appCoreState->uiState.state == MenuTempTrigger ||
381
-            m_appCoreState->uiState.state == MenuWaterSettingEdit || m_appCoreState->uiState.state == MenuHeaterSettingEdit || m_appCoreState->uiState.state == MenuTempTriggerEdit)
382
-    {
383
-        const auto isEdit = m_appCoreState->uiState.state == MenuWaterSettingEdit || m_appCoreState->uiState.state == MenuHeaterSettingEdit || m_appCoreState->uiState.state == MenuTempTriggerEdit;
384
-        const char* pTitle = nullptr;
385
-        temp_t* pTemp = nullptr;
386
-        if (isEdit)
387
-        {
388
-            pTemp = &m_appCoreState->appState.currentSettingEditTmp;
389
-        }
390
-        else
391
-        {
392
-            if (m_appCoreState->uiState.state == MenuWaterSetting ||
393
-                m_appCoreState->uiState.state == MenuWaterSettingEdit)
394
-            {
395
-                pTitle = "Sanitaire";
396
-                pTemp = &m_appCoreState->appState.water.setting;
397
-            }
398
-            else if (m_appCoreState->uiState.state == MenuHeaterSetting ||
399
-                     m_appCoreState->uiState.state == MenuHeaterSettingEdit)
400
-            {
401
-                pTitle = "Chauffage";
402
-                pTemp = &m_appCoreState->appState.heater.setting;
403
-            }
404
-            else if (m_appCoreState->uiState.state == MenuTempTrigger ||
405
-                     m_appCoreState->uiState.state == MenuTempTriggerEdit)
406
-            {
407
-                pTitle = "Delta";
408
-                pTemp = &m_appCoreState->appState.tempTrigger;
409
-            }
410
-        }
411
-        char tmpStr[17], tmpTemp[7];
412
-        m_pLcd->setCursor(0, 0);
413
-        snprintf(tmpStr, sizeof(tmpStr), "%s :                ", pTitle);
414
-        m_pLcd->print(tmpStr);
415
-        m_pLcd->setCursor(0, 1);
416
-        tempToStr(tmpTemp, *pTemp, 4);
417
-        snprintf(tmpStr, sizeof(tmpStr), "%s%s C                ", isEdit ? "> " : "  ", tmpTemp);
418
-        m_pLcd->print(tmpStr);
419
-    }
420
-    else if (m_appCoreState->uiState.state == MenuVersion)
131
+    if (g_currentActivity)
421 132
     {
422
-        char tmpStr[17];
423
-        m_pLcd->setCursor(0, 0);
424
-        snprintf(tmpStr, sizeof(tmpStr), "%s :                ", "Version");
425
-        m_pLcd->print(tmpStr);
426
-        m_pLcd->setCursor(0, 1);
427
-        snprintf(tmpStr, sizeof(tmpStr), "%s - %s                ", xstr(APP_CORE_VERSION), xstr(APP_CORE_COMMIT));
428
-        m_pLcd->print(tmpStr);
133
+        g_currentActivity->loop();
429 134
     }
430
-    m_appCoreState->uiState.isUpdateNeeded = false;
431 135
 
432
-    LOG_FN_END(2);
433
-}
434
-
435
-void
436
-AppCore::printStateLine(
437
-        char prefix
438
-        , const BoilerItemState* boilerItemState
439
-)
440
-{
441
-    LOG_FN_BEGIN(2);
442
-
443
-    char curTmp[7], setTmp[7], tmp[17];
444
-    tempToStr(curTmp, boilerItemState->current, 5);
445
-    tempToStr(setTmp, boilerItemState->setting, 4);
446
-    int count = snprintf(tmp, sizeof(tmp), "%c:%s [%s] %c", prefix, curTmp, setTmp, boilerItemState->isActive ? LCD_CHAR_SENSOR : ' ');
447
-    for (; count < 17; ++count)
448
-    {
449
-        tmp[count] = ' ';
450
-    }
451
-    tmp[count] = 0;
452
-    m_pLcd->print(tmp);
453
-
454
-    LOG_FN_END(2);
136
+    LOG_FN_END(50);
455 137
 }
456 138
 
457
-void AppCore::tempToStr(
458
-        char* out
459
-        , temp_t temp
460
-        , signed char width
461
-)
139
+void AppCore::setActivity(IActivity* activity)
462 140
 {
463
-    LOG_FN_BEGIN(2);
464
-
465
-    LOG(5, "%s: temp=%i", __FUNCTION__, (int)temp);
466
-
467
-    if (temp == TEMP_T_INVALID)
468
-    {
469
-        strcpy(out, " --.-");
470
-    }
471
-    else
472
-    {
473
-        dtostrf(temp / 10.0f, width, 1, out);
474
-    }
475
-
476
-    LOG_FN_END(2);
141
+    g_currentActivity = activity;
142
+    g_currentActivity->begin();
477 143
 }

+ 7
- 42
AppCore.h View File

@@ -6,52 +6,17 @@
6 6
 #include <DallasTemperature.h>
7 7
 #include "Boiler.h"
8 8
 #include "Storage.h"
9
+#include "IInput.h"
10
+#include "ILifeCycle.h"
11
+#include "IActivity.h"
9 12
 
10 13
 class AppCore
14
+        : public ILifeCycle
11 15
 {
12 16
 public:
13
-    static UiStateEnum s_menuSequence[4];
17
+    void begin() override;
14 18
 
15
-    AppCore();
19
+    void loop() override;
16 20
 
17
-    void setup();
18
-
19
-    void loop();
20
-
21
-protected:
22
-    void setState(UiStateEnum state);
23
-
24
-    void checkBoilerItem(BoilerItemState* boilerItemState);
25
-
26
-    void readAndUpdateSensors(BoilerItemState* boilerItemState, const uint8_t* sensor);
27
-
28
-    void printState();
29
-
30
-    void printStateLine(
31
-            char prefix
32
-            , const BoilerItemState* boilerItemState
33
-    );
34
-
35
-    void tempToStr(
36
-            char* out
37
-            , temp_t temp
38
-            , signed char width
39
-    );
40
-
41
-private:
42
-    AppCoreState* m_appCoreState;
43
-    Storage m_storage;
44
-
45
-    Button* m_pBtnCancel;
46
-    Button* m_pBtnOk;
47
-    Button* m_pBtnMinus;
48
-    Button* m_pBtnPlus;
49
-    Button* m_pButtons[4];
50
-
51
-    LiquidCrystal* m_pLcd;
52
-
53
-    OneWire* m_pOneWire;
54
-    DallasTemperature* m_pSensors;
55
-    DeviceAddress m_sensor1;
56
-    DeviceAddress m_sensor2;
21
+    void setActivity(IActivity* activity);
57 22
 };

+ 98
- 0
BaseActivity.cpp View File

@@ -0,0 +1,98 @@
1
+#include "BaseActivity.h"
2
+#include "globals.h"
3
+#include "Helpers.h"
4
+
5
+BaseActivity::BaseActivity(
6
+        IActivity* mParentActivity
7
+        , IActivity* mChildActivity
8
+        , IActivity* mPreviousActivity
9
+        , IActivity* mNextActivity
10
+)
11
+        : m_lcdUpdateNeeded(true)
12
+          , m_parentActivity(mParentActivity), m_childActivity(mChildActivity), m_previousActivity(mPreviousActivity)
13
+          , m_nextActivity(mNextActivity)
14
+{
15
+}
16
+
17
+void BaseActivity::begin()
18
+{
19
+    m_lcdUpdateNeeded = true;
20
+}
21
+
22
+void BaseActivity::loop()
23
+{
24
+    if (!g_hibernateService.getValue() && !g_hibernateService.hasChanged())
25
+    {
26
+        if (g_btnCancel.wasReleased())
27
+        {
28
+            onButtonReleased(Cancel);
29
+        }
30
+        else if (g_btnOk.wasReleased())
31
+        {
32
+            onButtonReleased(Ok);
33
+        }
34
+        else if (g_btnMinus.wasReleased())
35
+        {
36
+            onButtonReleased(Minus);
37
+        }
38
+        else if (g_btnPlus.wasReleased())
39
+        {
40
+            onButtonReleased(Plus);
41
+        }
42
+    }
43
+
44
+    if (m_lcdUpdateNeeded)
45
+    {
46
+        updateLcd();
47
+    }
48
+}
49
+
50
+void BaseActivity::updateLcd()
51
+{
52
+    char lines[2][17];
53
+    lines[0][0] = 0;
54
+    lines[1][0] = 0;
55
+    char* lines_[2] = {lines[0], lines[1]};
56
+    getLcdText(lines_);
57
+    for (int i = 0; i < 2; ++i)
58
+    {
59
+        g_lcd.setCursor(0, i);
60
+        Helpers::fillLine(lines[i], 16, ' ');
61
+        g_lcd.print(lines[i]);
62
+    }
63
+    m_lcdUpdateNeeded = false;
64
+}
65
+
66
+void BaseActivity::onButtonReleased(BaseActivity::Button button)
67
+{
68
+    if (button == Cancel)
69
+    {
70
+        if (m_parentActivity)
71
+        {
72
+            g_appCore.setActivity(m_parentActivity);
73
+        }
74
+    }
75
+    else if (button == Ok)
76
+    {
77
+        if (m_childActivity)
78
+        {
79
+            g_appCore.setActivity(m_childActivity);
80
+        }
81
+    }
82
+    else if (button == Minus)
83
+    {
84
+        if (m_previousActivity)
85
+        {
86
+            g_appCore.setActivity(m_previousActivity);
87
+        }
88
+    }
89
+    else if (button == Plus)
90
+    {
91
+        if (m_nextActivity)
92
+        {
93
+            g_appCore.setActivity(m_nextActivity);
94
+        }
95
+    }
96
+}
97
+
98
+

+ 41
- 0
BaseActivity.h View File

@@ -0,0 +1,41 @@
1
+#pragma once
2
+
3
+#include "IActivity.h"
4
+
5
+class BaseActivity
6
+        : public IActivity
7
+{
8
+public:
9
+    enum Button
10
+    {
11
+        Cancel = 1,
12
+        Ok = 2,
13
+        Minus = 4,
14
+        Plus = 8
15
+    };
16
+
17
+    explicit BaseActivity(
18
+            IActivity* mParentActivity = nullptr
19
+            , IActivity* mChildActivity = nullptr
20
+            , IActivity* mPreviousActivity = nullptr
21
+            , IActivity* mNextActivity = nullptr
22
+    );
23
+
24
+    void begin() override;
25
+
26
+    void loop() override;
27
+
28
+protected:
29
+    void updateLcd();
30
+
31
+    virtual void getLcdText(char** lines) = 0;
32
+
33
+    virtual void onButtonReleased(Button button);
34
+
35
+    bool m_lcdUpdateNeeded;
36
+
37
+    IActivity* m_parentActivity;
38
+    IActivity* m_childActivity;
39
+    IActivity* m_previousActivity;
40
+    IActivity* m_nextActivity;
41
+};

+ 20
- 37
Boiler.h View File

@@ -1,50 +1,33 @@
1 1
 #pragma once
2 2
 
3
-typedef short temp_t;
3
+#include "stdint.h"
4
+
5
+using temp_t = short;
4 6
 #define TEMP_T_INVALID -127
5
-typedef unsigned long timestamp_t;
7
+using timestamp_t = unsigned long;
6 8
 
7
-enum UiStateEnum
8
-{
9
-    HomeHibernate,
10
-    HomeLighting,
11
-    MenuWaterSetting,
12
-    MenuWaterSettingEdit,
13
-    MenuHeaterSetting,
14
-    MenuHeaterSettingEdit,
15
-    MenuTempTrigger,
16
-    MenuTempTriggerEdit,
17
-    MenuVersion
18
-};
9
+class TempInput;
19 10
 
20
-struct BoilerItemState
21
-{
22
-    temp_t current;
23
-    temp_t setting;
24
-    bool isActive;
25
-    int pinNo;
26
-};
11
+class DigitalOutput;
27 12
 
28
-struct AppState
13
+struct BoilerTankState
29 14
 {
30
-    timestamp_t lastSensorRequestMs;
31
-    bool hasReadSensors;
32
-    BoilerItemState water;
33
-    BoilerItemState heater;
34
-    temp_t tempTrigger;
35
-    temp_t* pCurrentSettingEdit;
36
-    temp_t currentSettingEditTmp;
37
-};
15
+    enum Mode
16
+    {
17
+        Auto,
18
+        On,
19
+        Off
20
+    };
38 21
 
39
-struct UiState
40
-{
41
-    UiStateEnum state;
42
-    timestamp_t lastOpMs;
43
-    bool isUpdateNeeded;
22
+    Mode mode;
23
+    temp_t setting;
24
+    temp_t tempTrigger;
25
+//    uint8_t sensorAddress[8];
26
+    TempInput* input;
27
+    DigitalOutput* relay;
44 28
 };
45 29
 
46 30
 struct AppCoreState
47 31
 {
48
-    AppState appState;
49
-    UiState uiState;
32
+    BoilerTankState tanks[2];
50 33
 };

+ 18
- 0
BoilerTankMenuActivity.cpp View File

@@ -0,0 +1,18 @@
1
+#include "BoilerTankMenuActivity.h"
2
+
3
+BoilerTankMenuActivity::BoilerTankMenuActivity(
4
+        IActivity* mParentActivity
5
+        , IActivity* mPreviousActivity
6
+        , IActivity* mNextActivity
7
+        , const char* line1
8
+        , const char* line2
9
+        , BoilerTankState* state
10
+)
11
+        : MenuItemActivity(mParentActivity, &m_tempSettingActivity, mPreviousActivity, mNextActivity, line1, line2)
12
+          , m_tempSettingActivity(this, &m_modeSettingActivity, &m_triggerSettingActivity, "Temperature",
13
+                                  &state->setting)
14
+          , m_triggerSettingActivity(this, &m_tempSettingActivity, &m_modeSettingActivity, "Delta", &state->tempTrigger)
15
+          , m_modeSettingActivity(this, &m_triggerSettingActivity, &m_tempSettingActivity, "Mode", &state->mode)
16
+          , m_state(state)
17
+{
18
+}

+ 26
- 0
BoilerTankMenuActivity.h View File

@@ -0,0 +1,26 @@
1
+#pragma once
2
+
3
+#include "MenuItemActivity.h"
4
+#include "Boiler.h"
5
+#include "TempEditorActivity.h"
6
+#include "BoilerTankModeEditorActivity.h"
7
+
8
+class BoilerTankMenuActivity
9
+        : public MenuItemActivity
10
+{
11
+public:
12
+    BoilerTankMenuActivity(
13
+            IActivity* mParentActivity
14
+            , IActivity* mPreviousActivity
15
+            , IActivity* mNextActivity
16
+            , const char* line1
17
+            , const char* line2
18
+            , BoilerTankState* state
19
+    );
20
+
21
+protected:
22
+    TempEditorActivity m_tempSettingActivity;
23
+    TempEditorActivity m_triggerSettingActivity;
24
+    BoilerTankModeEditorActivity m_modeSettingActivity;
25
+    BoilerTankState* m_state;
26
+};

+ 108
- 0
BoilerTankModeEditorActivity.cpp View File

@@ -0,0 +1,108 @@
1
+#include <stdio.h>
2
+#include "BoilerTankModeEditorActivity.h"
3
+#include "Helpers.h"
4
+#include "globals.h"
5
+
6
+BoilerTankModeEditorActivity::BoilerTankModeEditorActivity(
7
+        IActivity* mParentActivity
8
+        , IActivity* mPreviousActivity
9
+        , IActivity* mNextActivity
10
+        , const char* line1
11
+        , BoilerTankState::Mode* value
12
+)
13
+        : BaseActivity(mParentActivity, nullptr, mPreviousActivity, mNextActivity)
14
+          , m_line1(line1)
15
+          , m_value(value)
16
+          , m_tmpValue(*value)
17
+          , m_isEditMode(false)
18
+{
19
+}
20
+
21
+void BoilerTankModeEditorActivity::begin()
22
+{
23
+    m_tmpValue = *m_value;
24
+    BaseActivity::begin();
25
+}
26
+
27
+void BoilerTankModeEditorActivity::getLcdText(char** lines)
28
+{
29
+    Helpers::center(lines[0], m_line1, 16, ' ');
30
+
31
+    const char* str = "??";
32
+    if (m_tmpValue == BoilerTankState::Auto)
33
+    {
34
+        str = "Auto";
35
+    }
36
+    else if (m_tmpValue == BoilerTankState::On)
37
+    {
38
+        str = "On  ";
39
+    }
40
+    else if (m_tmpValue == BoilerTankState::Off)
41
+    {
42
+        str = "Off ";
43
+    }
44
+    snprintf(lines[1], 17, "%c %s %c", m_isEditMode ? '>' : ' ', str, *m_value != m_tmpValue ? '*' : ' ');
45
+}
46
+
47
+void BoilerTankModeEditorActivity::onButtonReleased(BaseActivity::Button button)
48
+{
49
+    if (m_isEditMode)
50
+    {
51
+        if (button == Cancel)
52
+        {
53
+            m_tmpValue = *m_value;
54
+            m_isEditMode = false;
55
+        }
56
+        else if (button == Ok)
57
+        {
58
+            *m_value = m_tmpValue;
59
+            g_storage.save();
60
+            m_isEditMode = false;
61
+        }
62
+        else if (button == Minus)
63
+        {
64
+            switch (m_tmpValue)
65
+            {
66
+                case BoilerTankState::Auto:
67
+                    m_tmpValue = BoilerTankState::Off;
68
+                    break;
69
+                case BoilerTankState::On:
70
+                    m_tmpValue = BoilerTankState::Auto;
71
+                    break;
72
+                case BoilerTankState::Off:
73
+                default:
74
+                    m_tmpValue = BoilerTankState::On;
75
+                    break;
76
+            }
77
+        }
78
+        else if (button == Plus)
79
+        {
80
+            switch (m_tmpValue)
81
+            {
82
+                case BoilerTankState::Auto:
83
+                    m_tmpValue = BoilerTankState::On;
84
+                    break;
85
+                case BoilerTankState::On:
86
+                    m_tmpValue = BoilerTankState::Off;
87
+                    break;
88
+                case BoilerTankState::Off:
89
+                default:
90
+                    m_tmpValue = BoilerTankState::Auto;
91
+                    break;
92
+            }
93
+        }
94
+        m_lcdUpdateNeeded = true;
95
+    }
96
+    else
97
+    {
98
+        if (button == Ok)
99
+        {
100
+            m_isEditMode = true;
101
+            m_lcdUpdateNeeded = true;
102
+        }
103
+        else
104
+        {
105
+            BaseActivity::onButtonReleased(button);
106
+        }
107
+    }
108
+}

+ 30
- 0
BoilerTankModeEditorActivity.h View File

@@ -0,0 +1,30 @@
1
+#pragma once
2
+
3
+#include <stdint.h>
4
+#include "BaseActivity.h"
5
+#include "Boiler.h"
6
+
7
+class BoilerTankModeEditorActivity
8
+        : public BaseActivity
9
+{
10
+public:
11
+    explicit BoilerTankModeEditorActivity(
12
+            IActivity* mParentActivity
13
+            , IActivity* mPreviousActivity
14
+            , IActivity* mNextActivity
15
+            , const char* line1
16
+            , BoilerTankState::Mode* value
17
+    );
18
+
19
+    void begin() override;
20
+
21
+protected:
22
+    void getLcdText(char** lines) override;
23
+
24
+    void onButtonReleased(Button button) override;
25
+
26
+    const char* m_line1;
27
+    BoilerTankState::Mode* m_value;
28
+    BoilerTankState::Mode m_tmpValue;
29
+    bool m_isEditMode;
30
+};

+ 44
- 0
BoilerTankService.cpp View File

@@ -0,0 +1,44 @@
1
+#include "BoilerTankService.h"
2
+#include "DigitalOutput.h"
3
+#include "TempInput.h"
4
+
5
+BoilerTankService::BoilerTankService(BoilerTankState* mBoilerTank)
6
+        : m_boilerTank(mBoilerTank)
7
+{
8
+}
9
+
10
+void BoilerTankService::loop()
11
+{
12
+    if (m_boilerTank->mode == BoilerTankState::Auto)
13
+    {
14
+        if (!m_boilerTank->relay->isEnabled() &&
15
+            m_boilerTank->input->getValue() != TEMP_T_INVALID &&
16
+            m_boilerTank->setting != TEMP_T_INVALID &&
17
+            m_boilerTank->input->getValue() <= m_boilerTank->setting - m_boilerTank->tempTrigger)
18
+        {
19
+            m_boilerTank->relay->setEnabled(true);
20
+        }
21
+        else if (m_boilerTank->relay->isEnabled() && (
22
+                m_boilerTank->input->getValue() == TEMP_T_INVALID ||
23
+                m_boilerTank->setting == TEMP_T_INVALID ||
24
+                m_boilerTank->input->getValue() >= m_boilerTank->setting)
25
+                )
26
+        {
27
+            m_boilerTank->relay->setEnabled(false);
28
+        }
29
+    }
30
+    else if (m_boilerTank->mode == BoilerTankState::On)
31
+    {
32
+        if (!m_boilerTank->relay->isEnabled())
33
+        {
34
+            m_boilerTank->relay->setEnabled(true);
35
+        }
36
+    }
37
+    else if (m_boilerTank->mode == BoilerTankState::Off)
38
+    {
39
+        if (m_boilerTank->relay->isEnabled())
40
+        {
41
+            m_boilerTank->relay->setEnabled(false);
42
+        }
43
+    }
44
+}

+ 18
- 0
BoilerTankService.h View File

@@ -0,0 +1,18 @@
1
+#pragma once
2
+
3
+#include "ILifeCycle.h"
4
+#include "Boiler.h"
5
+
6
+class BoilerTankService
7
+        : public ILifeCycle
8
+{
9
+public:
10
+    BoilerTankService(BoilerTankState* mBoilerTank);
11
+
12
+    void loop() override;
13
+
14
+protected:
15
+    BoilerTankState* m_boilerTank;
16
+};
17
+
18
+

+ 4
- 6
CMakeLists.txt View File

@@ -19,7 +19,7 @@ set(${CMAKE_PROJECT_NAME}_PORT /dev/ttyUSB3)
19 19
 
20 20
 
21 21
 enable_language(ASM)
22
-set(${CMAKE_PROJECT_NAME}_ALL_SRCS main.ino Storage.cpp AppCore.cpp)
22
+set(${CMAKE_PROJECT_NAME}_ALL_SRCS main.ino TempInput.cpp BoilerTankService.cpp HibernateService.cpp DigitalOutput.cpp BaseActivity.cpp LoaderActivity.cpp HomeActivity.cpp MenuItemActivity.cpp BoilerTankMenuActivity.cpp TempEditorActivity.cpp BoilerTankModeEditorActivity.cpp Storage.cpp AppCore.cpp Helpers.cpp)
23 23
 set(${CMAKE_PROJECT_NAME}_SKETCH main.ino)
24 24
 generate_arduino_firmware(${CMAKE_PROJECT_NAME})
25 25
 
@@ -28,10 +28,8 @@ add_definitions(-DAPP_CORE_VERSION=1.0.0)
28 28
 execute_process(
29 29
         COMMAND
30 30
         git rev-parse --short HEAD
31
-        RESULT_VARIABLE
32
-        SHORT_HASH_RESULT
33
-        OUTPUT_VARIABLE
34
-        SHORT_HASH
31
+        RESULT_VARIABLE SHORT_HASH_RESULT
32
+        OUTPUT_VARIABLE SHORT_HASH
35 33
 )
36 34
 string(REGEX REPLACE "\n$" "" SHORT_HASH "${SHORT_HASH}")
37 35
 message(STATUS "SHORT_HASH=${SHORT_HASH}")
@@ -40,4 +38,4 @@ add_definitions(-DAPP_CORE_COMMIT=${SHORT_HASH})
40 38
 if (CMAKE_BUILD_TYPE MATCHES "Debug")
41 39
     message(STATUS "Debug build detected. Enabling logs.")
42 40
     add_definitions(-DAPP_CORE_LOGS=1)
43
-endif()
41
+endif ()

+ 22
- 0
DigitalOutput.cpp View File

@@ -0,0 +1,22 @@
1
+#include "DigitalOutput.h"
2
+#include <Arduino.h>
3
+
4
+DigitalOutput::DigitalOutput(uint8_t mPin)
5
+        : m_pin(mPin)
6
+{
7
+}
8
+
9
+void DigitalOutput::begin()
10
+{
11
+    pinMode(m_pin, OUTPUT);
12
+}
13
+
14
+void DigitalOutput::setEnabled(bool enabled)
15
+{
16
+    digitalWrite(m_pin, enabled ? HIGH : LOW);
17
+}
18
+
19
+bool DigitalOutput::isEnabled() const
20
+{
21
+    return digitalRead(m_pin);
22
+}

+ 23
- 0
DigitalOutput.h View File

@@ -0,0 +1,23 @@
1
+#pragma once
2
+
3
+
4
+#include <stdint.h>
5
+#include "ILifeCycle.h"
6
+
7
+class DigitalOutput
8
+        : public ILifeCycle
9
+{
10
+public:
11
+    explicit DigitalOutput(uint8_t mPin);
12
+
13
+    void begin() override;
14
+
15
+    void setEnabled(bool enabled);
16
+
17
+    bool isEnabled() const;
18
+
19
+protected:
20
+    const uint8_t m_pin;
21
+};
22
+
23
+

+ 65
- 0
Helpers.cpp View File

@@ -0,0 +1,65 @@
1
+#include <string.h>
2
+#include <stdlib.h>
3
+#include "Helpers.h"
4
+#include "Logs.h"
5
+
6
+void Helpers::tempToStr(
7
+        char* out
8
+        , temp_t temp
9
+        , signed char width
10
+)
11
+{
12
+    LOG_FN_BEGIN(2);
13
+
14
+    LOG(5, "%s: temp=%i", __FUNCTION__, (int) temp);
15
+
16
+    if (temp == TEMP_T_INVALID)
17
+    {
18
+        for (int i = 0; i < width - 4; ++i)
19
+        {
20
+            out[i] = ' ';
21
+        }
22
+        strcpy(&out[width - 4], "--.-");
23
+    }
24
+    else
25
+    {
26
+        dtostrf((float) temp / 10.0f, width, 1, out);
27
+    }
28
+
29
+    LOG_FN_END(2);
30
+}
31
+
32
+void Helpers::fillLine(
33
+        char* line
34
+        , int length
35
+        , char c
36
+)
37
+{
38
+    int i = 0;
39
+    while (line[i] && i < length)
40
+    {
41
+        ++i;
42
+    }
43
+    while (i < length)
44
+    {
45
+        line[i] = c;
46
+        ++i;
47
+    }
48
+    line[length] = 0;
49
+}
50
+
51
+void Helpers::center(
52
+        char* line
53
+        , const char* text
54
+        , int length
55
+        , char c
56
+)
57
+{
58
+    auto cCount = (length - strlen(text)) / 2;
59
+    for (int i = 0; i < cCount; ++i)
60
+    {
61
+        line[i] = c;
62
+    }
63
+    strcpy(&line[cCount], text);
64
+    fillLine(line, length, c);
65
+}

+ 29
- 0
Helpers.h View File

@@ -0,0 +1,29 @@
1
+#pragma once
2
+
3
+
4
+#include "Boiler.h"
5
+
6
+class Helpers
7
+{
8
+public:
9
+    static void tempToStr(
10
+            char* out
11
+            , temp_t temp
12
+            , signed char width
13
+    );
14
+
15
+    static void fillLine(
16
+            char* line
17
+            , int length
18
+            , char c
19
+    );
20
+
21
+    static void center(
22
+            char* line
23
+            , const char* text
24
+            , int length
25
+            , char c
26
+    );
27
+};
28
+
29
+

+ 52
- 0
HibernateService.cpp View File

@@ -0,0 +1,52 @@
1
+#include "HibernateService.h"
2
+#include "defines.h"
3
+#include "Logs.h"
4
+#include "globals.h"
5
+#include <Arduino.h>
6
+
7
+HibernateService::HibernateService()
8
+        : m_buttons{
9
+        &g_btnCancel,
10
+        &g_btnOk,
11
+        &g_btnMinus,
12
+        &g_btnPlus,
13
+        nullptr
14
+}
15
+          , m_lastEventMs(0)
16
+{
17
+}
18
+
19
+void HibernateService::begin()
20
+{
21
+    m_lastEventMs = millis();
22
+    setValue(false);
23
+    g_lcdLed.setEnabled(!m_lastValue);
24
+}
25
+
26
+void HibernateService::loop()
27
+{
28
+    auto currentMs = millis();
29
+    bool shouldHibernate = m_lastValue;
30
+
31
+    for (auto i = 0; m_buttons[i]; ++i)
32
+    {
33
+        if (m_buttons[i]->wasReleased())
34
+        {
35
+            m_lastEventMs = currentMs;
36
+            shouldHibernate = false;
37
+            break;
38
+        }
39
+    }
40
+    if (!m_lastValue && currentMs - m_lastEventMs > HIBERNATE_DELAY)
41
+    {
42
+        shouldHibernate = true;
43
+    }
44
+
45
+    setValue(shouldHibernate);
46
+
47
+    if (hasChanged())
48
+    {
49
+        LOG(1, "Hibernate event");
50
+        g_lcdLed.setEnabled(!m_lastValue);
51
+    }
52
+}

+ 22
- 0
HibernateService.h View File

@@ -0,0 +1,22 @@
1
+#pragma once
2
+
3
+
4
+#include "IInput.h"
5
+#include "Boiler.h"
6
+#include "JC_Button.h"
7
+
8
+class HibernateService
9
+        : public IInput<bool>
10
+{
11
+public:
12
+    HibernateService();
13
+
14
+    void begin() override;
15
+
16
+    void loop() override;
17
+
18
+protected:
19
+    Button* m_buttons[5];
20
+
21
+    timestamp_t m_lastEventMs;
22
+};

+ 41
- 0
HomeActivity.cpp View File

@@ -0,0 +1,41 @@
1
+#include "HomeActivity.h"
2
+#include "Boiler.h"
3
+#include "defines.h"
4
+#include "Helpers.h"
5
+#include "Logs.h"
6
+#include "globals.h"
7
+#include "BaseActivity.h"
8
+
9
+HomeActivity::HomeActivity()
10
+        : BaseActivity(nullptr, &g_menuWaterActivity)
11
+{
12
+}
13
+
14
+void HomeActivity::getLcdText(char** lines)
15
+{
16
+    getLineText(lines[0], 'S', &g_appState.tanks[0]);
17
+    getLineText(lines[1], 'C', &g_appState.tanks[1]);
18
+}
19
+
20
+void HomeActivity::getLineText(
21
+        char* line
22
+        , char prefix
23
+        , const BoilerTankState* boilerItemState
24
+)
25
+{
26
+    char curTmp[7], setTmp[7];
27
+    Helpers::tempToStr(curTmp, boilerItemState->input->getValue(), 5);
28
+    Helpers::tempToStr(setTmp,
29
+                       boilerItemState->mode == BoilerTankState::Auto ? boilerItemState->setting : TEMP_T_INVALID, 4);
30
+    snprintf(line, 17, "%c:%s [%s] %c", prefix, curTmp, setTmp,
31
+             boilerItemState->relay->isEnabled() ? LCD_CHAR_SENSOR : ' ');
32
+}
33
+
34
+void HomeActivity::loop()
35
+{
36
+    if (g_appState.tanks[0].input->hasChanged() || g_appState.tanks[1].input->hasChanged())
37
+    {
38
+        m_lcdUpdateNeeded = true;
39
+    }
40
+    BaseActivity::loop();
41
+}

+ 25
- 0
HomeActivity.h View File

@@ -0,0 +1,25 @@
1
+#pragma once
2
+
3
+#include <LiquidCrystal.h>
4
+#include <JC_Button.h>
5
+#include "Boiler.h"
6
+#include "BaseActivity.h"
7
+
8
+class HomeActivity
9
+        : public BaseActivity
10
+{
11
+public:
12
+    explicit HomeActivity();
13
+
14
+    void loop() override;
15
+
16
+protected:
17
+    void getLcdText(char** lines) override;
18
+
19
+    void getLineText(
20
+            char* line
21
+            , char prefix
22
+            , const BoilerTankState* boilerItemState
23
+    );
24
+};
25
+

+ 10
- 0
IActivity.h View File

@@ -0,0 +1,10 @@
1
+#pragma once
2
+
3
+#include "ILifeCycle.h"
4
+
5
+class IActivity
6
+        : public ILifeCycle
7
+{
8
+public:
9
+    IActivity() = default;
10
+};

+ 37
- 0
IInput.h View File

@@ -0,0 +1,37 @@
1
+#pragma once
2
+
3
+
4
+#include "ILifeCycle.h"
5
+
6
+template<class T>
7
+class IInput
8
+        : public ILifeCycle
9
+{
10
+public:
11
+    const T& getValue() const
12
+    {
13
+        return m_lastValue;
14
+    }
15
+
16
+    bool hasChanged() const
17
+    {
18
+        return m_hasChanged;
19
+    }
20
+
21
+protected:
22
+    void setValue(const T& value)
23
+    {
24
+        if (value != m_lastValue)
25
+        {
26
+            m_hasChanged = true;
27
+            m_lastValue = value;
28
+        }
29
+        else if (m_hasChanged)
30
+        {
31
+            m_hasChanged = false;
32
+        }
33
+    }
34
+
35
+    T m_lastValue;
36
+    bool m_hasChanged;
37
+};

+ 12
- 0
ILifeCycle.h View File

@@ -0,0 +1,12 @@
1
+#pragma once
2
+
3
+
4
+class ILifeCycle
5
+{
6
+public:
7
+    virtual void begin()
8
+    {}
9
+
10
+    virtual void loop()
11
+    {}
12
+};

+ 47
- 0
LoaderActivity.cpp View File

@@ -0,0 +1,47 @@
1
+#include "LoaderActivity.h"
2
+#include "globals.h"
3
+#include "Helpers.h"
4
+#include "Logs.h"
5
+
6
+void LoaderActivity::begin()
7
+{
8
+    if (!g_btnMinus.read() || !g_btnPlus.read())
9
+    {
10
+        load();
11
+    }
12
+    else
13
+    {
14
+        BaseActivity::begin();
15
+    }
16
+}
17
+
18
+void LoaderActivity::load()
19
+{
20
+    LOG(1, "%s: Loading settings", __FUNCTION__);
21
+    g_storage.load();
22
+    g_appCore.setActivity(&g_homeActivity);
23
+}
24
+
25
+void LoaderActivity::reset()
26
+{
27
+    LOG(1, "%s: Resetting settings", __FUNCTION__);
28
+    g_storage.save();
29
+    g_appCore.setActivity(&g_homeActivity);
30
+}
31
+
32
+void LoaderActivity::getLcdText(char** lines)
33
+{
34
+    Helpers::center(lines[0], "Reset?", 16, ' ');
35
+}
36
+
37
+void LoaderActivity::onButtonReleased(BaseActivity::Button button)
38
+{
39
+    if (button == Cancel)
40
+    {
41
+        load();
42
+    }
43
+    else if (button == Ok)
44
+    {
45
+        reset();
46
+    }
47
+}

+ 22
- 0
LoaderActivity.h View File

@@ -0,0 +1,22 @@
1
+#pragma once
2
+
3
+
4
+#include "BaseActivity.h"
5
+
6
+class LoaderActivity
7
+        : public BaseActivity
8
+{
9
+public:
10
+    void begin() override;
11
+
12
+    void load();
13
+
14
+    void reset();
15
+
16
+protected:
17
+    void getLcdText(char** lines) override;
18
+
19
+    void onButtonReleased(Button button) override;
20
+};
21
+
22
+

+ 3
- 0
Logs.h View File

@@ -2,6 +2,9 @@
2 2
 
3 3
 #if APP_CORE_LOGS
4 4
 
5
+#include <stdio.h>
6
+#include <Arduino.h>
7
+
5 8
 #define APP_CORE_LOGS_LEVEL 1
6 9
 
7 10
 #define LOG(level_, ...) do {                           \

+ 23
- 0
MenuItemActivity.cpp View File

@@ -0,0 +1,23 @@
1
+#include "MenuItemActivity.h"
2
+#include "globals.h"
3
+#include "Helpers.h"
4
+
5
+MenuItemActivity::MenuItemActivity(
6
+        IActivity* mParentActivity
7
+        , IActivity* mChildActivity
8
+        , IActivity* mPreviousActivity
9
+        , IActivity* mNextActivity
10
+        , const char* line1
11
+        , const char* line2
12
+)
13
+        : BaseActivity(mParentActivity, mChildActivity, mPreviousActivity, mNextActivity)
14
+          , m_line1(line1)
15
+          , m_line2(line2)
16
+{
17
+}
18
+
19
+void MenuItemActivity::getLcdText(char** lines)
20
+{
21
+    Helpers::center(lines[0], m_line1, 16, ' ');
22
+    strncpy(lines[1], m_line2, 16);
23
+}

+ 26
- 0
MenuItemActivity.h View File

@@ -0,0 +1,26 @@
1
+#pragma once
2
+
3
+
4
+#include "BaseActivity.h"
5
+
6
+class MenuItemActivity
7
+        : public BaseActivity
8
+{
9
+public:
10
+    MenuItemActivity(
11
+            IActivity* mParentActivity
12
+            , IActivity* mChildActivity
13
+            , IActivity* mPreviousActivity
14
+            , IActivity* mNextActivity
15
+            , const char* line1
16
+            , const char* line2
17
+    );
18
+
19
+protected:
20
+    void getLcdText(char** lines) override;
21
+
22
+    const char* m_line1;
23
+    const char* m_line2;
24
+};
25
+
26
+

+ 48
- 13
Storage.cpp View File

@@ -1,43 +1,78 @@
1 1
 #include "Storage.h"
2
+#include "globals.h"
2 3
 #include <EEPROM.h>
3 4
 #include <Arduino.h>
4 5
 
5
-template <class T> int EEPROM_writeAnything(int ee, const T& value)
6
+template<class T>
7
+int EEPROM_writeAnything(
8
+        int ee
9
+        , const T& value
10
+)
6 11
 {
7
-    const byte* p = (const byte*)(const void*)&value;
12
+    const byte* p = (const byte*) (const void*) &value;
8 13
     unsigned int i;
9 14
     for (i = 0; i < sizeof(value); i++)
10 15
         EEPROM.write(ee++, *p++);
11 16
     return i;
12 17
 }
13 18
 
14
-template <class T> int EEPROM_readAnything(int ee, T& value)
19
+template<class T>
20
+int EEPROM_readAnything(
21
+        int ee
22
+        , T& value
23
+)
15 24
 {
16
-    byte* p = (byte*)(void*)&value;
25
+    byte* p = (byte*) (void*) &value;
17 26
     unsigned int i;
18 27
     for (i = 0; i < sizeof(value); i++)
19 28
         *p++ = EEPROM.read(ee++);
20 29
     return i;
21 30
 }
22 31
 
23
-void Storage::load(AppCoreState& data)
32
+void Storage::load()
24 33
 {
25 34
     uint8_t version;
26 35
     uint32_t index = 0;
27 36
     index += EEPROM_readAnything(index, version);
28 37
     if (version == 1)
29 38
     {
30
-        index += EEPROM_readAnything(index, data.appState.water.setting);
31
-        index += EEPROM_readAnything(index, data.appState.heater.setting);
32
-        index += EEPROM_readAnything(index, data.appState.tempTrigger);
39
+        index += load(index, g_appState.tanks[0]);
40
+        index += load(index, g_appState.tanks[1]);
33 41
     }
34 42
 }
35 43
 
36
-void Storage::save(const AppCoreState& data)
44
+uint32_t Storage::load(
45
+        uint32_t index
46
+        , BoilerTankState& tankState
47
+)
48
+{
49
+    uint32_t delta = 0;
50
+
51
+    delta += EEPROM_readAnything(index + delta, tankState.setting);
52
+    delta += EEPROM_readAnything(index + delta, tankState.tempTrigger);
53
+    delta += EEPROM_readAnything(index + delta, tankState.mode);
54
+
55
+    return delta;
56
+}
57
+
58
+void Storage::save()
37 59
 {
38 60
     uint32_t index = 0;
39
-    index += EEPROM_writeAnything(index, (uint8_t)1);
40
-    index += EEPROM_writeAnything(index, data.appState.water.setting);
41
-    index += EEPROM_writeAnything(index, data.appState.heater.setting);
42
-    index += EEPROM_writeAnything(index, data.appState.tempTrigger);
61
+    index += EEPROM_writeAnything(index, (uint8_t) 1);
62
+    index += save(index, g_appState.tanks[0]);
63
+    index += save(index, g_appState.tanks[1]);
64
+}
65
+
66
+uint32_t Storage::save(
67
+        uint32_t index
68
+        , const BoilerTankState& tankState
69
+)
70
+{
71
+    uint32_t delta = 0;
72
+
73
+    delta += EEPROM_writeAnything(index + delta, tankState.setting);
74
+    delta += EEPROM_writeAnything(index + delta, tankState.tempTrigger);
75
+    delta += EEPROM_writeAnything(index + delta, tankState.mode);
76
+
77
+    return delta;
43 78
 }

+ 13
- 2
Storage.h View File

@@ -5,7 +5,18 @@
5 5
 class Storage
6 6
 {
7 7
 public:
8
-    void load(AppCoreState& data);
8
+    void load();
9 9
 
10
-    void save(const AppCoreState& data);
10
+    void save();
11
+
12
+protected:
13
+    uint32_t load(
14
+            uint32_t index
15
+            , BoilerTankState& tankState
16
+    );
17
+
18
+    uint32_t save(
19
+            uint32_t index
20
+            , const BoilerTankState& tankState
21
+    );
11 22
 };

+ 74
- 0
TempEditorActivity.cpp View File

@@ -0,0 +1,74 @@
1
+#include <stdio.h>
2
+#include "TempEditorActivity.h"
3
+#include "Helpers.h"
4
+#include "defines.h"
5
+#include "globals.h"
6
+
7
+TempEditorActivity::TempEditorActivity(
8
+        IActivity* mParentActivity
9
+        , IActivity* mPreviousActivity
10
+        , IActivity* mNextActivity
11
+        , const char* line1
12
+        , temp_t* value
13
+)
14
+        : BaseActivity(mParentActivity, nullptr, mPreviousActivity, mNextActivity)
15
+          , m_line1(line1)
16
+          , m_value(value)
17
+          , m_tmpValue(*value)
18
+          , m_isEditMode(false)
19
+{
20
+}
21
+
22
+void TempEditorActivity::begin()
23
+{
24
+    m_tmpValue = *m_value;
25
+    BaseActivity::begin();
26
+}
27
+
28
+void TempEditorActivity::getLcdText(char** lines)
29
+{
30
+    Helpers::center(lines[0], m_line1, 16, ' ');
31
+
32
+    char temp[7];
33
+    Helpers::tempToStr(temp, m_tmpValue, 5);
34
+    snprintf(lines[1], 17, "%c %s C %c", m_isEditMode ? '>' : ' ', temp, *m_value != m_tmpValue ? '*' : ' ');
35
+}
36
+
37
+void TempEditorActivity::onButtonReleased(BaseActivity::Button button)
38
+{
39
+    if (m_isEditMode)
40
+    {
41
+        if (button == Cancel)
42
+        {
43
+            m_tmpValue = *m_value;
44
+            m_isEditMode = false;
45
+        }
46
+        else if (button == Ok)
47
+        {
48
+            *m_value = m_tmpValue;
49
+            g_storage.save();
50
+            m_isEditMode = false;
51
+        }
52
+        else if (button == Minus)
53
+        {
54
+            m_tmpValue -= TEMP_INTERVAL;
55
+        }
56
+        else if (button == Plus)
57
+        {
58
+            m_tmpValue += TEMP_INTERVAL;
59
+        }
60
+        m_lcdUpdateNeeded = true;
61
+    }
62
+    else
63
+    {
64
+        if (button == Ok)
65
+        {
66
+            m_isEditMode = true;
67
+            m_lcdUpdateNeeded = true;
68
+        }
69
+        else
70
+        {
71
+            BaseActivity::onButtonReleased(button);
72
+        }
73
+    }
74
+}

+ 32
- 0
TempEditorActivity.h View File

@@ -0,0 +1,32 @@
1
+#pragma once
2
+
3
+
4
+#include "BaseActivity.h"
5
+#include "Boiler.h"
6
+
7
+class TempEditorActivity
8
+        : public BaseActivity
9
+{
10
+public:
11
+    TempEditorActivity(
12
+            IActivity* mParentActivity
13
+            , IActivity* mPreviousActivity
14
+            , IActivity* mNextActivity
15
+            , const char* line1
16
+            , temp_t* value
17
+    );
18
+
19
+    void begin() override;
20
+
21
+protected:
22
+    void getLcdText(char** lines) override;
23
+
24
+    void onButtonReleased(Button button) override;
25
+
26
+    const char* m_line1;
27
+    temp_t* m_value;
28
+    temp_t m_tmpValue;
29
+    bool m_isEditMode;
30
+};
31
+
32
+

+ 41
- 0
TempInput.cpp View File

@@ -0,0 +1,41 @@
1
+#include "TempInput.h"
2
+#include "globals.h"
3
+#include "defines.h"
4
+
5
+TempInput::TempInput(uint8_t index)
6
+        : m_address{0}
7
+          , m_index(index)
8
+          , m_lastSensorRequestMs(0)
9
+          , m_hasReadSensor(false)
10
+{
11
+}
12
+
13
+void TempInput::begin()
14
+{
15
+    setValue(TEMP_T_INVALID);
16
+    g_dallasTemperature.getAddress(m_address, m_index);
17
+}
18
+
19
+void TempInput::loop()
20
+{
21
+    const auto& currentMs = millis();
22
+
23
+    if (currentMs - m_lastSensorRequestMs >= SENSORS_CHECK_INTERVAL)
24
+    {
25
+        m_lastSensorRequestMs = currentMs;
26
+        m_hasReadSensor = false;
27
+        g_dallasTemperature.requestTemperaturesByAddress(m_address);
28
+    }
29
+    if (currentMs - m_lastSensorRequestMs >= SENSORS_REQUEST_DELAY &&
30
+        !m_hasReadSensor)
31
+    {
32
+        m_hasReadSensor = true;
33
+        auto raw = g_dallasTemperature.getTempC(m_address);
34
+        temp_t temp = TEMP_T_INVALID;
35
+        if (raw != DEVICE_DISCONNECTED_C)
36
+        {
37
+            temp = (temp_t) (raw * 10);
38
+        }
39
+        setValue(temp);
40
+    }
41
+}

+ 23
- 0
TempInput.h View File

@@ -0,0 +1,23 @@
1
+#pragma once
2
+
3
+#include "Boiler.h"
4
+#include "IInput.h"
5
+
6
+class TempInput
7
+        : public IInput<temp_t>
8
+{
9
+public:
10
+    explicit TempInput(uint8_t index);
11
+
12
+    void begin() override;
13
+
14
+    void loop() override;
15
+
16
+protected:
17
+    uint8_t m_address[8];
18
+    uint8_t m_index;
19
+    timestamp_t m_lastSensorRequestMs;
20
+    bool m_hasReadSensor;
21
+};
22
+
23
+

+ 23
- 0
defines.h View File

@@ -0,0 +1,23 @@
1
+#pragma once
2
+
3
+#define HIBERNATE_DELAY 5000
4
+#define SENSORS_CHECK_INTERVAL 2000
5
+#define SENSORS_REQUEST_DELAY 750
6
+#define TEMP_TRIGGER ((temp_t)(5 * 10.0f))
7
+#define TEMP_INTERVAL ((temp_t)(0.5 * 10.0f))
8
+#define LCD_CHAR_SENSOR 1
9
+
10
+#define PIN_ONEWIRE 14 //A0
11
+#define PIN_RELAY2 15 //A1
12
+#define PIN_RELAY1 16 //A2
13
+#define PIN_BTN_CANCEL 9
14
+#define PIN_BTN_OK 10
15
+#define PIN_BTN_MINUS 11
16
+#define PIN_BTN_PLUS 12
17
+#define PIN_LCD_LED 2
18
+#define PIN_LCD_RS 8
19
+#define PIN_LCD_ENABLE 7
20
+#define PIN_LCD_D4 6
21
+#define PIN_LCD_D5 5
22
+#define PIN_LCD_D6 4
23
+#define PIN_LCD_D7 3

+ 35
- 0
globals.h View File

@@ -0,0 +1,35 @@
1
+#pragma once
2
+
3
+#include <JC_Button.h>
4
+#include <LiquidCrystal.h>
5
+#include <OneWire.h>
6
+#include <DallasTemperature.h>
7
+#include "AppCore.h"
8
+#include "HibernateService.h"
9
+#include "DigitalOutput.h"
10
+#include "Storage.h"
11
+#include "HomeActivity.h"
12
+#include "MenuItemActivity.h"
13
+#include "BoilerTankMenuActivity.h"
14
+#include "TempInput.h"
15
+#include "BoilerTankService.h"
16
+
17
+extern AppCore g_appCore;
18
+extern AppCoreState g_appState;
19
+extern Storage g_storage;
20
+extern Button g_btnCancel;
21
+extern Button g_btnOk;
22
+extern Button g_btnMinus;
23
+extern Button g_btnPlus;
24
+extern HibernateService g_hibernateService;
25
+extern LiquidCrystal g_lcd;
26
+extern DigitalOutput g_lcdLed;
27
+extern OneWire g_oneWire;
28
+extern DallasTemperature g_dallasTemperature;
29
+extern BoilerTankService g_tankService1;
30
+extern BoilerTankService g_tankService2;
31
+
32
+extern HomeActivity g_homeActivity;
33
+extern BoilerTankMenuActivity g_menuWaterActivity;
34
+extern BoilerTankMenuActivity g_menuHeaterActivity;
35
+extern MenuItemActivity g_menuVersionActivity;

+ 3
- 3
main.ino View File

@@ -1,14 +1,14 @@
1 1
 #include "AppCore.h"
2 2
 #include <OneWire.h>
3 3
 
4
-auto g_pAppCore = new AppCore();
4
+AppCore g_appCore;
5 5
 
6 6
 void setup()
7 7
 {
8
-    g_pAppCore->setup();
8
+    g_appCore.begin();
9 9
 }
10 10
 
11 11
 void loop()
12 12
 {
13
-    g_pAppCore->loop();
13
+    g_appCore.loop();
14 14
 }

Loading…
Cancel
Save