Browse Source

Moving to AppCore and Storage

master
Robin Thoni 4 years ago
parent
commit
8421f3d9a3
Signed by: Robin THONI <robin@rthoni.com> GPG Key ID: 4E09DEF46B99E61E
7 changed files with 341 additions and 204 deletions
  1. 287
    0
      AppCore.cpp
  2. 15
    0
      AppCore.h
  3. 4
    1
      Boiler.h
  4. 1
    1
      CMakeLists.txt
  5. 19
    0
      Storage.cpp
  6. 11
    0
      Storage.h
  7. 4
    202
      main.ino

+ 287
- 0
AppCore.cpp View File

@@ -0,0 +1,287 @@
1
+#include <LiquidCrystal.h>
2
+#include <JC_Button.h>
3
+#include <OneWire.h>
4
+#include <DallasTemperature.h>
5
+#include "Boiler.h"
6
+#include "Storage.h"
7
+#include "AppCore.h"
8
+
9
+#define HIBERNATE_DELAY 5000
10
+#define SENSORS_CHECK_INTERVAL 2000
11
+#define SENSORS_REQUEST_DELAY 750
12
+#define TEMP_TRIGGER 10
13
+#define LCD_CHAR_SENSOR 1
14
+
15
+#define PIN_ONEWIRE 12
16
+#define PIN_BTN_MODE 11
17
+#define PIN_BTN_MINUS 10
18
+#define PIN_BTN_PLUS 9
19
+#define PIN_LCD_LED 8
20
+#define PIN_LCD_RS 7
21
+#define PIN_LCD_ENABLE 6
22
+#define PIN_LCD_D0 5
23
+#define PIN_LCD_D1 4
24
+#define PIN_LCD_D2 3
25
+#define PIN_LCD_D3 2
26
+
27
+byte g_sensorChar[8] = {
28
+        B00100,
29
+        B01110,
30
+        B01110,
31
+        B01110,
32
+        B01110,
33
+        B11111,
34
+        B11111,
35
+        B01110,
36
+};
37
+
38
+Storage g_storage;
39
+
40
+OneWire g_oneWire(PIN_ONEWIRE);
41
+DallasTemperature g_sensors(&g_oneWire);
42
+DeviceAddress g_sensor1;
43
+
44
+Button g_btnMode(PIN_BTN_MODE);
45
+Button g_btnMinus(PIN_BTN_MINUS);
46
+Button g_btnPlus(PIN_BTN_PLUS);
47
+Button* g_buttons[3] = {&g_btnMode, &g_btnMinus, &g_btnPlus};
48
+#define BUTTONS_COUNT (sizeof(g_buttons) / sizeof(*g_buttons))
49
+
50
+LiquidCrystal g_lcd(PIN_LCD_RS, PIN_LCD_ENABLE, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
51
+
52
+UiStateEnum g_modeSequence[3] = {WaterSetting, HeaterSetting, Lighting};
53
+#define MODE_SEQUENCE_COUNT (sizeof(g_modeSequence) / sizeof(*g_modeSequence))
54
+
55
+AppCoreState g_appCoreState = {
56
+        .appState = {
57
+                .lastSensorRequestMs = 0,
58
+                .hasReadSensors = true,
59
+                .water = {
60
+                        .current = TEMP_T_INVALID,
61
+                        .setting = 0,
62
+                        .isActive = false
63
+                },
64
+                .heater = {
65
+                        .current = TEMP_T_INVALID,
66
+                        .setting = 0,
67
+                        .isActive = false
68
+                }
69
+        },
70
+        .uiState = {
71
+                .state = Lighting,
72
+                .lastOpMs = 0,
73
+                .modeSequenceIndex = MODE_SEQUENCE_COUNT - 1,
74
+                .isUpdateNeeded = true
75
+        }
76
+};
77
+
78
+void tmpToStr(char* out, temp_t temp)
79
+{
80
+    if (temp == TEMP_T_INVALID)
81
+    {
82
+        strcpy(out, " -- ");
83
+    }
84
+    else
85
+    {
86
+        char tmp[7];
87
+        dtostrf(temp / 2.0f, 2, 1, tmp);
88
+        auto len = 4 - strlen(tmp);
89
+        for (auto i = 0; i < len; ++i)
90
+        {
91
+            out[i] = ' ';
92
+        }
93
+        strcpy(&out[len], tmp);
94
+    }
95
+}
96
+
97
+void printStateLine(char prefix, const BoilerItemState& boilerItemState, bool isModifying, bool isActive, LiquidCrystal lcd)
98
+{
99
+    char curTmp[7], setTmp[7], tmp[17];
100
+    tmpToStr(curTmp, boilerItemState.current);
101
+    tmpToStr(setTmp, boilerItemState.setting);
102
+    int count = snprintf(tmp, sizeof(tmp), "%c: %s [%s]%c%c", prefix, curTmp, setTmp, isModifying ? '<' : ' ', isActive ? LCD_CHAR_SENSOR : ' ');
103
+    for (; count < 17; ++count)
104
+    {
105
+        tmp[count] = ' ';
106
+    }
107
+    tmp[count] = 0;
108
+    lcd.print(tmp);
109
+}
110
+
111
+void printState(const AppCoreState& appCoreState, LiquidCrystal lcd)
112
+{
113
+    Serial.println("Updating display");
114
+
115
+    lcd.setCursor(0, 0);
116
+    printStateLine('S', appCoreState.appState.water, appCoreState.uiState.state == WaterSetting, appCoreState.appState.water.isActive, lcd);
117
+    lcd.setCursor(0, 1);
118
+    printStateLine('C', appCoreState.appState.heater, appCoreState.uiState.state == HeaterSetting, appCoreState.appState.heater.isActive, lcd);
119
+}
120
+
121
+void setState(AppCoreState& boilerState, UiStateEnum state)
122
+{
123
+    char tmp[50];
124
+    snprintf(tmp, sizeof(tmp), "Changing state %i => %i", g_appCoreState.uiState.state, state);
125
+    Serial.println(tmp);
126
+
127
+    if (state == Lighting)
128
+    {
129
+        digitalWrite(PIN_LCD_LED, 1);
130
+    }
131
+    else if (state == Hibernate)
132
+    {
133
+        digitalWrite(PIN_LCD_LED, 0);
134
+        g_appCoreState.uiState.modeSequenceIndex = MODE_SEQUENCE_COUNT - 1;
135
+    }
136
+    g_appCoreState.uiState.state = state;
137
+    g_appCoreState.uiState.isUpdateNeeded = true;
138
+}
139
+
140
+void checkBoilerItem(BoilerItemState& boilerItemState, UiState uiState)
141
+{
142
+    if (!boilerItemState.isActive && boilerItemState.current != TEMP_T_INVALID && boilerItemState.current <= boilerItemState.setting - TEMP_TRIGGER)
143
+    {
144
+        boilerItemState.isActive = true;
145
+        uiState.isUpdateNeeded = true;
146
+    }
147
+    else if (boilerItemState.isActive && (boilerItemState.current == TEMP_T_INVALID || boilerItemState.current >= boilerItemState.setting))
148
+    {
149
+        boilerItemState.isActive = false;
150
+        uiState.isUpdateNeeded = true;
151
+    }
152
+}
153
+
154
+void AppCore::setup()
155
+{
156
+    g_sensors.begin();
157
+    g_sensors.getAddress(g_sensor1, 0);
158
+    g_sensors.setWaitForConversion(false);
159
+
160
+    for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
161
+    {
162
+        g_buttons[i]->begin();
163
+    }
164
+
165
+    pinMode(PIN_LCD_LED, OUTPUT);
166
+    digitalWrite(PIN_LCD_LED, 1);
167
+
168
+    g_lcd.begin(16, 2);
169
+    g_lcd.createChar(LCD_CHAR_SENSOR, g_sensorChar);
170
+
171
+    Serial.begin(9600);
172
+
173
+    bool allButtonsPressed = true;
174
+    for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
175
+    {
176
+        allButtonsPressed = allButtonsPressed && g_buttons[i]->isPressed();
177
+    }
178
+    if (!allButtonsPressed)
179
+    {
180
+        g_storage.load(g_appCoreState);
181
+    }
182
+    else
183
+    {
184
+        g_storage.save(g_appCoreState);
185
+    }
186
+}
187
+
188
+void AppCore::loop()
189
+{
190
+    const auto& currentMs = millis();
191
+
192
+    if (currentMs - g_appCoreState.appState.lastSensorRequestMs >= SENSORS_CHECK_INTERVAL)
193
+    {
194
+        g_appCoreState.appState.lastSensorRequestMs = currentMs;
195
+        g_appCoreState.appState.hasReadSensors = false;
196
+        g_sensors.requestTemperaturesByAddress(g_sensor1);
197
+    }
198
+    if (currentMs - g_appCoreState.appState.lastSensorRequestMs >= SENSORS_REQUEST_DELAY && !g_appCoreState.appState.hasReadSensors)
199
+    {
200
+        g_appCoreState.appState.hasReadSensors = true;
201
+        auto raw = g_sensors.getTempC(g_sensor1);
202
+        temp_t temp = TEMP_T_INVALID;
203
+        if (raw != DEVICE_DISCONNECTED_C)
204
+        {
205
+            temp = (temp_t) (raw * 2);
206
+        }
207
+
208
+        if (temp != g_appCoreState.appState.water.current)
209
+        {
210
+            g_appCoreState.appState.water.current = temp;
211
+            g_appCoreState.uiState.isUpdateNeeded = true;
212
+        }
213
+    }
214
+
215
+
216
+    for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
217
+    {
218
+        g_buttons[i]->read();
219
+        if (g_buttons[i]->isPressed())
220
+        {
221
+            g_appCoreState.uiState.lastOpMs = currentMs;
222
+            break;
223
+        }
224
+    }
225
+
226
+
227
+    if (g_appCoreState.uiState.state == Hibernate)
228
+    {
229
+        for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
230
+        {
231
+            if (g_buttons[i]->wasReleased())
232
+            {
233
+                setState(g_appCoreState, Lighting);
234
+                break;
235
+            }
236
+        }
237
+    }
238
+    else
239
+    {
240
+        if (currentMs - g_appCoreState.uiState.lastOpMs >= HIBERNATE_DELAY)
241
+        {
242
+            setState(g_appCoreState, Hibernate);
243
+        }
244
+        else
245
+        {
246
+            if (g_btnMode.wasReleased())
247
+            {
248
+                g_appCoreState.uiState.modeSequenceIndex = (g_appCoreState.uiState.modeSequenceIndex + 1) % MODE_SEQUENCE_COUNT;
249
+                setState(g_appCoreState, g_modeSequence[g_appCoreState.uiState.modeSequenceIndex]);
250
+            }
251
+            else if (g_btnMinus.wasReleased() || g_btnPlus.wasReleased())
252
+            {
253
+                BoilerItemState* itemState = NULL;
254
+                if (g_appCoreState.uiState.state == WaterSetting)
255
+                {
256
+                    itemState = &g_appCoreState.appState.water;
257
+                }
258
+                else if (g_appCoreState.uiState.state == HeaterSetting)
259
+                {
260
+                    itemState = &g_appCoreState.appState.heater;
261
+                }
262
+
263
+                if (itemState)
264
+                {
265
+                    if (g_btnMinus.wasReleased())
266
+                    {
267
+                        --itemState->setting;
268
+                    }
269
+                    else if (g_btnPlus.wasReleased())
270
+                    {
271
+                        ++itemState->setting;
272
+                    }
273
+                    g_appCoreState.uiState.isUpdateNeeded = true;
274
+                }
275
+            }
276
+        }
277
+    }
278
+
279
+    checkBoilerItem(g_appCoreState.appState.water, g_appCoreState.uiState);
280
+    checkBoilerItem(g_appCoreState.appState.heater, g_appCoreState.uiState);
281
+
282
+    if (g_appCoreState.uiState.isUpdateNeeded)
283
+    {
284
+        printState(g_appCoreState, g_lcd);
285
+        g_appCoreState.uiState.isUpdateNeeded = false;
286
+    }
287
+}

+ 15
- 0
AppCore.h View File

@@ -0,0 +1,15 @@
1
+#pragma once
2
+
3
+
4
+class AppCore
5
+{
6
+public:
7
+    void setup();
8
+
9
+    void loop();
10
+
11
+private:
12
+
13
+};
14
+
15
+

+ 4
- 1
Boiler.h View File

@@ -1,6 +1,7 @@
1 1
 #pragma once
2 2
 
3 3
 typedef char temp_t;
4
+#define TEMP_T_INVALID -127
4 5
 typedef unsigned long timestamp_t;
5 6
 
6 7
 enum UiStateEnum
@@ -20,6 +21,8 @@ struct BoilerItemState
20 21
 
21 22
 struct AppState
22 23
 {
24
+    timestamp_t lastSensorRequestMs;
25
+    bool hasReadSensors;
23 26
     BoilerItemState water;
24 27
     BoilerItemState heater;
25 28
 };
@@ -32,7 +35,7 @@ struct UiState
32 35
     bool isUpdateNeeded;
33 36
 };
34 37
 
35
-struct GlobalState
38
+struct AppCoreState
36 39
 {
37 40
     AppState appState;
38 41
     UiState uiState;

+ 1
- 1
CMakeLists.txt View File

@@ -19,6 +19,6 @@ 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 JC_Button.cpp)
22
+set(${CMAKE_PROJECT_NAME}_ALL_SRCS main.ino JC_Button.cpp Storage.cpp AppCore.cpp)
23 23
 set(${CMAKE_PROJECT_NAME}_SKETCH main.ino)
24 24
 generate_arduino_firmware(${CMAKE_PROJECT_NAME})

+ 19
- 0
Storage.cpp View File

@@ -0,0 +1,19 @@
1
+#include "Storage.h"
2
+#include <EEPROM.h>
3
+
4
+void Storage::load(AppCoreState& data)
5
+{
6
+    auto version = EEPROM.read(0);
7
+    if (version == 1)
8
+    {
9
+        data.appState.water.setting = EEPROM.read(1);
10
+        data.appState.heater.setting = EEPROM.read(1);
11
+    }
12
+}
13
+
14
+void Storage::save(const AppCoreState& data)
15
+{
16
+    EEPROM.write(0, 1);
17
+    EEPROM.write(1, data.appState.water.setting);
18
+    EEPROM.write(2, data.appState.heater.setting);
19
+}

+ 11
- 0
Storage.h View File

@@ -0,0 +1,11 @@
1
+#pragma once
2
+
3
+#include "Boiler.h"
4
+
5
+class Storage
6
+{
7
+public:
8
+    void load(AppCoreState& data);
9
+
10
+    void save(const AppCoreState& data);
11
+};

+ 4
- 202
main.ino View File

@@ -1,211 +1,13 @@
1
-#include <LiquidCrystal.h>
2
-#include <JC_Button.h>
3
-#include "Boiler.h"
1
+#include "AppCore.h"
4 2
 
5
-#define HIBERNATE_DELAY 5000
6
-#define TEMP_TRIGGER 10
7
-#define LCD_CHAR_SENSOR 1
8
-
9
-#define PIN_BTN_MODE 11
10
-#define PIN_BTN_MINUS 10
11
-#define PIN_BTN_PLUS 9
12
-#define PIN_LCD_LED 8
13
-#define PIN_LCD_RS 7
14
-#define PIN_LCD_ENABLE 6
15
-#define PIN_LCD_D0 5
16
-#define PIN_LCD_D1 4
17
-#define PIN_LCD_D2 3
18
-#define PIN_LCD_D3 2
19
-
20
-byte g_sensorChar[8] = {
21
-        B00100,
22
-        B01110,
23
-        B01110,
24
-        B01110,
25
-        B01110,
26
-        B11111,
27
-        B11111,
28
-        B01110,
29
-};
30
-
31
-Button g_btnMode(PIN_BTN_MODE);
32
-Button g_btnMinus(PIN_BTN_MINUS);
33
-Button g_btnPlus(PIN_BTN_PLUS);
34
-Button* g_buttons[3] = {&g_btnMode, &g_btnMinus, &g_btnPlus};
35
-#define BUTTONS_COUNT (sizeof(g_buttons) / sizeof(*g_buttons))
36
-
37
-LiquidCrystal g_lcd(PIN_LCD_RS, PIN_LCD_ENABLE, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
38
-
39
-UiStateEnum g_modeSequence[3] = {WaterSetting, HeaterSetting, Lighting};
40
-#define MODE_SEQUENCE_COUNT (sizeof(g_modeSequence) / sizeof(*g_modeSequence))
41
-
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
-                }
54
-        },
55
-        .uiState = {
56
-                .state = Lighting,
57
-                .lastOpMs = 0,
58
-                .modeSequenceIndex = MODE_SEQUENCE_COUNT - 1,
59
-                .isUpdateNeeded = true
60
-        }
61
-};
62
-
63
-void printStateLine(char prefix, const BoilerItemState& boilerItemState, bool isModifying, bool isActive, LiquidCrystal lcd)
64
-{
65
-    char curTmp[5], setTmp[5], tmp[17];
66
-    dtostrf(boilerItemState.current / 2.0f, 2, 1, curTmp);
67
-    dtostrf(boilerItemState.setting / 2.0f, 2, 1, setTmp);
68
-    int count = snprintf(tmp, sizeof(tmp), "%c: %s [%s]%c%c", prefix, curTmp, setTmp, isModifying ? '<' : ' ', isActive ? LCD_CHAR_SENSOR : ' ');
69
-    for (; count < 17; ++count)
70
-    {
71
-        tmp[count] = ' ';
72
-    }
73
-    tmp[count] = 0;
74
-    lcd.print(tmp);
75
-}
76
-
77
-void printState(const GlobalState& globalState, LiquidCrystal lcd)
78
-{
79
-    Serial.println("Updating display");
80
-
81
-    lcd.setCursor(0, 0);
82
-    printStateLine('S', globalState.appState.water, globalState.uiState.state == WaterSetting, globalState.appState.water.isActive, lcd);
83
-    lcd.setCursor(0, 1);
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
-    }
118
-}
3
+AppCore g_appCore;
119 4
 
120 5
 void setup()
121 6
 {
122
-    for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
123
-    {
124
-        g_buttons[i]->begin();
125
-    }
126
-
127
-    pinMode(PIN_LCD_LED, OUTPUT);
128
-    digitalWrite(PIN_LCD_LED, 1);
129
-
130
-    g_lcd.begin(16, 2);
131
-    g_lcd.createChar(LCD_CHAR_SENSOR, g_sensorChar);
132
-
133
-    Serial.begin(9600);
7
+    g_appCore.setup();
134 8
 }
135 9
 
136 10
 void loop()
137 11
 {
138
-    const auto& currentMs = millis();
139
-
140
-    for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
141
-    {
142
-        g_buttons[i]->read();
143
-        if (g_buttons[i]->isPressed())
144
-        {
145
-            g_globalState.uiState.lastOpMs = currentMs;
146
-            break;
147
-        }
148
-    }
149
-
150
-
151
-    if (g_globalState.uiState.state == Hibernate)
152
-    {
153
-        for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
154
-        {
155
-            if (g_buttons[i]->wasReleased())
156
-            {
157
-                setState(g_globalState, Lighting);
158
-                break;
159
-            }
160
-        }
161
-    }
162
-    else
163
-    {
164
-        if (currentMs - g_globalState.uiState.lastOpMs >= HIBERNATE_DELAY)
165
-        {
166
-            setState(g_globalState, Hibernate);
167
-        }
168
-        else
169
-        {
170
-            if (g_btnMode.wasReleased())
171
-            {
172
-                g_globalState.uiState.modeSequenceIndex = (g_globalState.uiState.modeSequenceIndex + 1) % MODE_SEQUENCE_COUNT;
173
-                setState(g_globalState, g_modeSequence[g_globalState.uiState.modeSequenceIndex]);
174
-            }
175
-            else if (g_btnMinus.wasReleased() || g_btnPlus.wasReleased())
176
-            {
177
-                BoilerItemState* itemState = NULL;
178
-                if (g_globalState.uiState.state == WaterSetting)
179
-                {
180
-                    itemState = &g_globalState.appState.water;
181
-                }
182
-                else if (g_globalState.uiState.state == HeaterSetting)
183
-                {
184
-                    itemState = &g_globalState.appState.heater;
185
-                }
186
-
187
-                if (itemState)
188
-                {
189
-                    if (g_btnMinus.wasReleased())
190
-                    {
191
-                        --itemState->setting;
192
-                    }
193
-                    else if (g_btnPlus.wasReleased())
194
-                    {
195
-                        ++itemState->setting;
196
-                    }
197
-                    g_globalState.uiState.isUpdateNeeded = true;
198
-                }
199
-            }
200
-        }
201
-    }
202
-
203
-    checkBoilerItem(g_globalState.appState.water, g_globalState.uiState);
204
-    checkBoilerItem(g_globalState.appState.heater, g_globalState.uiState);
205
-
206
-    if (g_globalState.uiState.isUpdateNeeded)
207
-    {
208
-        printState(g_globalState, g_lcd);
209
-        g_globalState.uiState.isUpdateNeeded = false;
210
-    }
12
+    g_appCore.loop();
211 13
 }

Loading…
Cancel
Save