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