5 |
5 |
6 #include "config.h" |
6 #include "config.h" |
7 |
7 |
8 static const char *TAG = "co2meter"; |
8 static const char *TAG = "co2meter"; |
9 |
9 |
10 #define PIN_SDA (CONFIG_I2C_MASTER_SDA) |
10 |
11 #define PIN_SCL (CONFIG_I2C_MASTER_SCL) |
11 const esp_app_desc_t *app_desc = NULL; ///< Application description |
12 #define ROT_ENC_A_GPIO (CONFIG_ROT_ENC_A_GPIO) |
|
13 #define ROT_ENC_B_GPIO (CONFIG_ROT_ENC_B_GPIO) |
|
14 #define ROT_ENC_SW_GPIO (CONFIG_ROT_ENC_SW_GPIO) |
|
15 #define INACTIVITY 480 ///< Time in 250 mSec units. |
|
16 |
|
17 #define EDIT_TYPE_TEXT 0 ///< Editor type is text |
|
18 #define EDIT_TYPE_INT 1 ///< Editor type is integer |
|
19 #define EDIT_TYPE_FLOAT 2 ///< Editor type is float |
|
20 |
|
21 |
|
22 int Main_Loop1 = ML1_INIT; ///< Loop 1 init |
12 int Main_Loop1 = ML1_INIT; ///< Loop 1 init |
23 int Main_Loop2 = -1; ///< Loop 2 invalid |
|
24 int New_Loop2 = ML2_DONE; ///< Loop 2 new state |
|
25 bool System_TimeOk = false; ///< System time status |
13 bool System_TimeOk = false; ///< System time status |
26 time_t now; ///< Current time |
14 time_t now; ///< Current time |
27 struct tm timeinfo; ///< Current time structure |
15 struct tm timeinfo; ///< Current time structure |
28 char strftime_buf[64]; ///< Time buffer |
16 char strftime_buf[64]; ///< Time buffer |
29 static RTC_DATA_ATTR struct timeval sleep_enter_time; |
17 static RTC_DATA_ATTR struct timeval sleep_enter_time; |
30 static TaskHandle_t xTaskDS18B20 = NULL; |
18 static TaskHandle_t xTaskDS18B20 = NULL; |
31 static TaskHandle_t xTaskADC = NULL; |
19 static TaskHandle_t xTaskADC = NULL; |
32 static TaskHandle_t xTaskWifi = NULL; |
20 static TaskHandle_t xTaskWifi = NULL; |
33 static TaskHandle_t xTaskMQTT = NULL; |
21 static TaskHandle_t xTaskMQTT = NULL; |
34 const esp_app_desc_t *app_desc = NULL; ///< Application description |
22 static TaskHandle_t xTaskUser = NULL; |
35 u8g2_t u8g2; ///< A structure which will contain all the data for one display |
|
36 rotary_encoder_info_t rinfo = { 0 }; ///< Rotary encoder record |
|
37 rotary_encoder_event_t event = { 0 }; |
|
38 QueueHandle_t event_queue; |
|
39 static int PushDuration = 0; ///< Duration of the pushed button |
|
40 |
23 |
41 extern unit_t units[3]; ///< Pressure test units |
24 extern unit_t units[3]; ///< Pressure test units |
42 extern SemaphoreHandle_t xSemaphoreUnits; ///< Units lock semaphore |
25 extern SemaphoreHandle_t xSemaphoreUnits; ///< Units lock semaphore |
43 extern DS18B20_State *ds18b20_state; ///< DS18B20 state |
26 extern DS18B20_State *ds18b20_state; ///< DS18B20 state |
44 extern SemaphoreHandle_t xSemaphoreDS18B20; ///< DS18B20 lock semaphore |
27 extern SemaphoreHandle_t xSemaphoreDS18B20; ///< DS18B20 lock semaphore |
45 extern ADC_State *adc_state; ///< ADC state |
28 extern ADC_State *adc_state; ///< ADC state |
46 extern SemaphoreHandle_t xSemaphoreADC; ///< ADC lock semaphore |
29 extern SemaphoreHandle_t xSemaphoreADC; ///< ADC lock semaphore |
47 extern WIFI_State *wifi_state; ///< WiFi state |
30 extern WIFI_State *wifi_state; ///< WiFi state |
|
31 extern EventGroupHandle_t xEventGroupUser; |
48 extern int count_pub; ///< Published MQTT messages in transit |
32 extern int count_pub; ///< Published MQTT messages in transit |
49 static xQueueHandle gpio_evt_queue = NULL; ///< Rotary pushbutton queue |
33 |
50 static int usertimer = 0; ///< User inactive timeout |
|
51 |
|
52 |
|
53 |
|
54 /** |
|
55 * @brief Get a keyboard character from the rotary encoder. |
|
56 * @param curkey The referenced value if the key being edited. NOTE, start at 0 for a new char?? |
|
57 * @param type The edittype, all values, integer or float. |
|
58 * @param x The x position on the screen. |
|
59 * @param y The y position on the screen. |
|
60 * @return 1 if short keypress, meaning enter key. 2 if long press, enter key and editing is ready. |
|
61 */ |
|
62 int getkey(int *curkey, int type, int x, int y) |
|
63 { |
|
64 int key = *curkey; |
|
65 int rc = 0; |
|
66 |
|
67 u8g2_DrawHLine(&u8g2, x, y+3, 12); |
|
68 u8g2_SendBuffer(&u8g2); |
|
69 |
|
70 for (;;) { |
|
71 if (xQueueReceive(event_queue, &event, 100 / portTICK_PERIOD_MS) == pdTRUE) { |
|
72 usertimer = INACTIVITY; |
|
73 if (event.state.position != 0) { |
|
74 |
|
75 u8g2_SetDrawColor(&u8g2, 0); |
|
76 u8g2_DrawGlyph(&u8g2, x, y, key); |
|
77 u8g2_SetDrawColor(&u8g2, 1); |
|
78 u8g2_SendBuffer(&u8g2); |
|
79 |
|
80 if (event.state.position > 0) { |
|
81 if (key == 126) |
|
82 key = 171; |
|
83 else if (key < 126) |
|
84 key++; |
|
85 } else if (event.state.position < 0) { |
|
86 if (key == 171) |
|
87 key = 126; |
|
88 else if (key > 32) |
|
89 key--; |
|
90 } |
|
91 |
|
92 ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo)); |
|
93 u8g2_DrawGlyph(&u8g2, x, y, key); |
|
94 u8g2_SendBuffer(&u8g2); |
|
95 } |
|
96 } else { |
|
97 if (PushDuration) { |
|
98 if (PushDuration > 500) |
|
99 rc = 2; |
|
100 else |
|
101 rc = 1; |
|
102 PushDuration = 0; |
|
103 break; |
|
104 } |
|
105 } |
|
106 } |
|
107 u8g2_SetDrawColor(&u8g2, 0); |
|
108 u8g2_DrawHLine(&u8g2, x, y+3, 12); |
|
109 u8g2_SetDrawColor(&u8g2, 1); |
|
110 u8g2_SendBuffer(&u8g2); |
|
111 |
|
112 *curkey = key; |
|
113 return rc; |
|
114 } |
|
115 |
|
116 |
|
117 |
|
118 /** |
|
119 * @brief Editor using the rotary switch. |
|
120 * @param label The label of the edit field. |
|
121 * @param txt The string to edit. |
|
122 * @param errmsg The error message if needed. |
|
123 * @param len The maximum length for the string. |
|
124 * @param type The edit type. |
|
125 */ |
|
126 void rotary_editer(char *label, char *txt, char *errmsg, int len, int type) |
|
127 { |
|
128 char buf[65]; |
|
129 int key, x, y, rc; |
|
130 |
|
131 u8g2_ClearBuffer(&u8g2); |
|
132 u8g2_DrawHLine(&u8g2, 0, 14, 128); |
|
133 u8g2_DrawHLine(&u8g2, 0, 49, 128); |
|
134 u8g2_SetFont(&u8g2, u8g2_font_t0_15_tf); |
|
135 sprintf(buf, "Edit %s", label); |
|
136 u8g2_DrawStr(&u8g2,0,12,buf); |
|
137 |
|
138 if (strlen(errmsg)) { |
|
139 u8g2_SetFont(&u8g2, u8g2_font_t0_12b_tf); |
|
140 u8g2_DrawStr(&u8g2, 0, 61, errmsg); |
|
141 } |
|
142 u8g2_SetFont(&u8g2, u8g2_font_t0_12_tf); |
|
143 y = 36; |
|
144 u8g2_DrawStr(&u8g2, 0, y, txt); |
|
145 u8g2_SendBuffer(&u8g2); |
|
146 |
|
147 for (;;) { |
|
148 x = u8g2_GetUTF8Width(&u8g2, txt); |
|
149 key = 'a'; |
|
150 rc = getkey(&key, type, x, y); |
|
151 if (rc == 1) { |
|
152 if (key >= 32 && key <= 126 && strlen(txt) < len) { |
|
153 txt[strlen(txt) + 1] = '\0'; |
|
154 txt[strlen(txt)] = key; |
|
155 } else if (key == 171 && strlen(txt)) { |
|
156 // delete key |
|
157 txt[strlen(txt) - 1] = '\0'; |
|
158 } |
|
159 printf("strlen %d x %d key %d\n", strlen(txt), x, key); |
|
160 } else if (rc == 2) { |
|
161 break; |
|
162 } |
|
163 } |
|
164 } |
|
165 |
|
166 |
|
167 |
|
168 /** |
|
169 * @brief Write a menu line on the display. |
|
170 * @param bright Display the line with a bold or normal font. |
|
171 * @param x The horizontal start position of the line. |
|
172 * @param y The vertical bottom of the line position. |
|
173 * @param format The formatted data to display. |
|
174 */ |
|
175 void menu_line(int bright, int x, int y, const char *format, ...) |
|
176 { |
|
177 char buf[65]; |
|
178 va_list va_ptr; |
|
179 |
|
180 if (bright) |
|
181 u8g2_SetFont(&u8g2, u8g2_font_t0_12b_tr); |
|
182 else |
|
183 u8g2_SetFont(&u8g2, u8g2_font_t0_12_tr); |
|
184 |
|
185 va_start(va_ptr, format); |
|
186 vsnprintf(buf, 65, format, va_ptr); |
|
187 va_end(va_ptr); |
|
188 |
|
189 u8g2_DrawStr(&u8g2, x, y, buf); |
|
190 } |
|
191 |
|
192 |
|
193 |
|
194 /** |
|
195 * @brief Clear the display and prepare the top of the display. |
|
196 * @format The formatted data to display at the top. |
|
197 */ |
|
198 void screen_top(const char *format, ...) |
|
199 { |
|
200 char buf[65]; |
|
201 va_list va_ptr; |
|
202 |
|
203 va_start(va_ptr, format); |
|
204 vsnprintf(buf, 65, format, va_ptr); |
|
205 va_end(va_ptr); |
|
206 |
|
207 u8g2_ClearBuffer(&u8g2); |
|
208 u8g2_DrawHLine(&u8g2, 0, 14, 128); |
|
209 |
|
210 u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr); |
|
211 u8g2_uint_t w = u8g2_GetStrWidth(&u8g2, buf); |
|
212 u8g2_DrawStr(&u8g2, (128 - w) / 2,12, buf); |
|
213 } |
|
214 |
|
215 |
|
216 |
|
217 /** |
|
218 * @brief The splash screen shown during cold boot or user wakeup. |
|
219 */ |
|
220 void screen_splash() |
|
221 { |
|
222 screen_top("CO2 meter %s", app_desc->version); |
|
223 |
|
224 u8g2_SetFont(&u8g2, u8g2_font_t0_22b_tf); |
|
225 u8g2_uint_t w = u8g2_GetUTF8Width(&u8g2, "START"); |
|
226 u8g2_DrawUTF8(&u8g2, (128 - w) / 2,50, "START"); |
|
227 |
|
228 u8g2_SendBuffer(&u8g2); |
|
229 u8g2_SetPowerSave(&u8g2, 0); // wake up display |
|
230 } |
|
231 |
|
232 |
|
233 |
|
234 /** |
|
235 * @brief The main overview screen. |
|
236 */ |
|
237 void screen_main() |
|
238 { |
|
239 char buf[65]; |
|
240 int i; |
|
241 |
|
242 screen_top("CO2 meter %s", app_desc->version); |
|
243 |
|
244 if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE) { |
|
245 |
|
246 u8g2_SetFont(&u8g2, u8g2_font_t0_22b_tf); |
|
247 sprintf(buf, "%.1f °C", units[0].temperature / 1000.0); |
|
248 u8g2_uint_t w = u8g2_GetUTF8Width(&u8g2, buf); |
|
249 u8g2_DrawUTF8(&u8g2, (128 - w) / 2,40, buf); |
|
250 u8g2_SetFont(&u8g2, u8g2_font_t0_18b_tf); |
|
251 |
|
252 for (i = 0; i < 3; i++) { |
|
253 sprintf(buf, "%.1f", units[i].pressure / 1000.0); |
|
254 w = u8g2_GetUTF8Width(&u8g2, buf); |
|
255 u8g2_DrawUTF8(&u8g2, ((42 - w) / 2) + i * 43,63, buf); |
|
256 } |
|
257 xSemaphoreGive(xSemaphoreUnits); |
|
258 } |
|
259 u8g2_SendBuffer(&u8g2); |
|
260 u8g2_SetPowerSave(&u8g2, 0); // wake up display |
|
261 } |
|
262 |
|
263 |
|
264 |
|
265 /** |
|
266 * @brief The unit display screen. |
|
267 * @param no The unit index number. |
|
268 */ |
|
269 void screen_unit(int no) |
|
270 { |
|
271 char buf[65]; |
|
272 |
|
273 if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE) { |
|
274 |
|
275 screen_top("Unit %d %s", no + 1, units[no].mode ? "On":"Off"); |
|
276 |
|
277 u8g2_SetFont(&u8g2, u8g2_font_t0_22b_tf); |
|
278 sprintf(buf, "%.1f °C", units[no].temperature / 1000.0); |
|
279 u8g2_uint_t w = u8g2_GetUTF8Width(&u8g2, buf); |
|
280 u8g2_DrawUTF8(&u8g2, (128 - w) / 2,40, buf); |
|
281 |
|
282 sprintf(buf, "%.2f bar", units[no].pressure / 1000.0); |
|
283 w = u8g2_GetUTF8Width(&u8g2, buf); |
|
284 u8g2_DrawUTF8(&u8g2, (128 - w) / 2,63, buf); |
|
285 |
|
286 xSemaphoreGive(xSemaphoreUnits); |
|
287 } |
|
288 u8g2_SendBuffer(&u8g2); |
|
289 u8g2_SetPowerSave(&u8g2, 0); // wake up display |
|
290 } |
|
291 |
|
292 |
|
293 |
|
294 /** |
|
295 * @brief The unit zero setup screen. |
|
296 * @param no The unit index number. |
|
297 * @param sub The submenu index number. |
|
298 */ |
|
299 void screen_unit_zero(int no, int sub) |
|
300 { |
|
301 screen_top("Unit %d zero mV", no + 1); |
|
302 menu_line( 0, 2, 25, "Current %d", units[no].pressure_zero); |
|
303 menu_line(sub == 0, 2, 37, "New value %d", units[no].pressure_voltage / (adc_state->Batt_voltage / 1000)); |
|
304 menu_line(sub == 1, 2, 49, "Return"); |
|
305 printf("current %d p_voltage %d batt %d\n", units[no].pressure_zero, units[no].pressure_voltage, adc_state->Batt_voltage); |
|
306 u8g2_SendBuffer(&u8g2); |
|
307 u8g2_SetPowerSave(&u8g2, 0); |
|
308 } |
|
309 |
|
310 |
|
311 |
|
312 /** |
|
313 * @brief The unit setup screen. |
|
314 * @param no The unit index number. |
|
315 * @param sub The submenu index number. |
|
316 */ |
|
317 void screen_unit_setup(int no, int sub) |
|
318 { |
|
319 screen_top("Unit %d setup", no + 1); |
|
320 menu_line(sub == 0, 2, 25, "Mode %s", units[no].mode ? "ON":"OFF"); |
|
321 menu_line(sub == 1, 2, 37, "Zero mV %d", units[no].pressure_zero); |
|
322 menu_line(sub == 2, 2, 49, "DS18B20 %s", units[no].temperature_rom_code); |
|
323 menu_line(sub == 3, 2, 61, "Return"); |
|
324 u8g2_SendBuffer(&u8g2); |
|
325 u8g2_SetPowerSave(&u8g2, 0); |
|
326 } |
|
327 |
|
328 |
|
329 |
|
330 void screen_wifi() |
|
331 { |
|
332 char buf[65]; |
|
333 |
|
334 screen_top("WiFi Status"); |
|
335 snprintf(buf, 65, "SSID %s", wifi_state->STA_ssid); |
|
336 u8g2_DrawStr(&u8g2, 1, 28, buf); |
|
337 snprintf(buf, 65, "Online %s", wifi_state->STA_online ? "Yes":"No"); |
|
338 u8g2_DrawStr(&u8g2, 1, 43, buf); |
|
339 snprintf(buf, 65, "RSSI %d", wifi_state->STA_rssi); |
|
340 u8g2_DrawStr(&u8g2, 1, 59, buf); |
|
341 u8g2_SendBuffer(&u8g2); |
|
342 u8g2_SetPowerSave(&u8g2, 0); |
|
343 } |
|
344 |
|
345 |
|
346 |
|
347 void screen_wifi_setup(int sub) |
|
348 { |
|
349 screen_top("WiFi Setup"); |
|
350 menu_line(sub == 0, 2, 25, "Connect"); |
|
351 menu_line(sub == 1, 2, 37, "New"); |
|
352 menu_line(sub == 2, 2, 49, "Delete"); |
|
353 menu_line(sub == 3, 2, 61, "Return"); |
|
354 u8g2_SendBuffer(&u8g2); |
|
355 u8g2_SetPowerSave(&u8g2, 0); |
|
356 } |
|
357 |
|
358 |
|
359 |
|
360 void screen_network() |
|
361 { |
|
362 screen_top("Network Status"); |
|
363 menu_line(0, 1, 25, "IP %s", wifi_state->STA_ip); |
|
364 menu_line(0, 1, 37, "Mask %s", wifi_state->STA_nm); |
|
365 menu_line(0, 1, 49, "GW %s", wifi_state->STA_gw); |
|
366 menu_line(0, 1, 61, "Name %s", config.hostname); |
|
367 u8g2_SendBuffer(&u8g2); |
|
368 u8g2_SetPowerSave(&u8g2, 0); |
|
369 } |
|
370 |
|
371 |
|
372 |
|
373 void screen_mqtt() |
|
374 { |
|
375 screen_top("MQTT Status"); |
|
376 menu_line(0, 1, 25, "serv %s", config.mqtt_server); |
|
377 menu_line(0, 1, 37, "port %d", config.mqtt_port); |
|
378 menu_line(0, 1, 49, "user %s", config.mqtt_user); |
|
379 u8g2_SendBuffer(&u8g2); |
|
380 u8g2_SetPowerSave(&u8g2, 0); |
|
381 } |
|
382 |
|
383 |
|
384 |
|
385 void screen_update() |
|
386 { |
|
387 screen_top("Update firmware"); |
|
388 menu_line(0, 1, 43, "Push to update"); |
|
389 u8g2_SendBuffer(&u8g2); |
|
390 u8g2_SetPowerSave(&u8g2, 0); |
|
391 } |
|
392 |
|
393 |
|
394 |
|
395 /** |
|
396 * @brief Fatal messages on the screen. |
|
397 * @param e1 The first line. |
|
398 * @param e2 The second line. |
|
399 */ |
|
400 void screen_fatal(char *e1, char *e2) |
|
401 { |
|
402 u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr); |
|
403 u8g2_DrawStr(&u8g2,2,12,e1); |
|
404 u8g2_DrawStr(&u8g2,2,24,e2); |
|
405 u8g2_SendBuffer(&u8g2); |
|
406 u8g2_SetPowerSave(&u8g2, 0); |
|
407 } |
|
408 |
|
409 |
|
410 |
|
411 /** |
|
412 * @brief Interrupt service routine for the rotary pushbutton. |
|
413 */ |
|
414 static void IRAM_ATTR gpio_isr_handler(void* arg) |
|
415 { |
|
416 uint32_t gpio_num = (uint32_t) arg; |
|
417 xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); |
|
418 } |
|
419 |
|
420 |
|
421 |
|
422 /** |
|
423 * @brief GPIO queue task. See if there is a rotary pushbutton event on the queue. |
|
424 */ |
|
425 static void gpio_task(void* arg) |
|
426 { |
|
427 uint32_t io_num; |
|
428 static int64_t pushed = 0; |
|
429 |
|
430 for(;;) { |
|
431 if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) { |
|
432 if (io_num == ROT_ENC_SW_GPIO) { |
|
433 if (gpio_get_level(io_num) == 0) { |
|
434 pushed = esp_timer_get_time(); |
|
435 PushDuration = 0; |
|
436 } else if (gpio_get_level(io_num) == 1) { |
|
437 PushDuration = (esp_timer_get_time() - pushed) / 1000; |
|
438 ESP_LOGI(TAG, "GPIO rotary button intr, val: %d time: %d", gpio_get_level(io_num), PushDuration); |
|
439 } |
|
440 } else { |
|
441 ESP_LOGE(TAG, "GPIO[%d] unknown intr, val: %d", io_num, gpio_get_level(io_num)); |
|
442 } |
|
443 usertimer = INACTIVITY; |
|
444 } |
|
445 } |
|
446 } |
|
447 |
|
448 |
|
449 |
|
450 /** |
|
451 * @brief Select new menu number on a postitive or negative rotary position. |
|
452 * @param pos The new position, positive, negative or zero. |
|
453 * @param next_menu The selected menu if rotated clockwise. |
|
454 * @param prev_menu The selected menu if rotated counter-clockwise. |
|
455 */ |
|
456 static void rotate_to_menu(rotary_encoder_position_t pos, int next_menu, int prev_menu) |
|
457 { |
|
458 if (pos > 0) |
|
459 New_Loop2 = next_menu; |
|
460 else if (pos < 0) |
|
461 New_Loop2 = prev_menu; |
|
462 ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo)); |
|
463 } |
|
464 |
|
465 |
|
466 |
|
467 /** |
|
468 * @brief Rotate subscreens numbers. |
|
469 * @param pos The new position, positive, negative or zero. |
|
470 * @param min The lowest number. If already at the lowest, select the highest. |
|
471 * @param max The highest number. If already at the highest, select the lowest. |
|
472 * @param cursub The subscreen number by reference. This is updated with the new number. |
|
473 * @return Returns true if a new number is selected, false if nothing changed. |
|
474 */ |
|
475 bool rotate_to_sub(rotary_encoder_position_t pos, int min, int max, int *cursub) |
|
476 { |
|
477 int sub = *cursub; |
|
478 bool rc = false; |
|
479 |
|
480 if (pos > 0) { |
|
481 if (sub < max) |
|
482 sub++; |
|
483 else |
|
484 sub = min; |
|
485 ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo)); |
|
486 rc = true; |
|
487 } else if (pos < 0) { |
|
488 if (sub > min) |
|
489 sub--; |
|
490 else |
|
491 sub = max; |
|
492 ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo)); |
|
493 rc = true; |
|
494 } |
|
495 |
|
496 *cursub = sub; |
|
497 return rc; |
|
498 } |
|
499 |
34 |
500 |
35 |
501 |
36 |
502 void app_main() |
37 void app_main() |
503 { |
38 { |
504 struct timeval now; |
39 struct timeval now; |
505 gettimeofday(&now, NULL); |
40 gettimeofday(&now, NULL); |
506 int sleep_time_ms = (now.tv_sec - sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - sleep_enter_time.tv_usec) / 1000; |
41 int sleep_time_ms = (now.tv_sec - sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - sleep_enter_time.tv_usec) / 1000; |
507 esp_err_t ret; |
42 esp_err_t ret; |
508 char txt[65]; |
|
509 |
43 |
510 Main_Loop1 = ML1_INIT; |
44 Main_Loop1 = ML1_INIT; |
511 Main_Loop2 = -1; |
|
512 |
|
513 /* |
|
514 * Setup the OLED display. |
|
515 * See: https://github.com/nkolban/esp32-snippets/blob/master/hardware/displays/U8G2/ |
|
516 */ |
|
517 u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT; |
|
518 u8g2_esp32_hal.sda = PIN_SDA; |
|
519 u8g2_esp32_hal.scl = PIN_SCL; |
|
520 u8g2_esp32_hal_init(u8g2_esp32_hal); |
|
521 |
|
522 u8g2_Setup_sh1106_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb); // init u8g2 structure |
|
523 u8x8_SetI2CAddress(&u8g2.u8x8, 0x78); |
|
524 u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this, |
|
525 |
|
526 app_desc = esp_ota_get_app_description(); |
45 app_desc = esp_ota_get_app_description(); |
|
46 /* event handler and event group for the user interface */ |
|
47 xEventGroupUser = xEventGroupCreate(); |
527 |
48 |
528 switch (esp_sleep_get_wakeup_cause()) { |
49 switch (esp_sleep_get_wakeup_cause()) { |
529 case ESP_SLEEP_WAKEUP_EXT1: { |
50 case ESP_SLEEP_WAKEUP_EXT1: { |
530 ESP_LOGI(TAG, "Starting from deep sleep, Rotary switch pressed"); |
51 ESP_LOGI(TAG, "Starting from deep sleep, Rotary switch pressed"); |
531 New_Loop2 = ML2_INIT; |
52 user_wakeup(); |
532 screen_splash(); |
|
533 break; |
53 break; |
534 } |
54 } |
535 case ESP_SLEEP_WAKEUP_TIMER: { |
55 case ESP_SLEEP_WAKEUP_TIMER: { |
536 ESP_LOGI(TAG, "Starting from deep sleep, timer wakeup after %dms", sleep_time_ms); |
56 ESP_LOGI(TAG, "Starting from deep sleep, timer wakeup after %dms", sleep_time_ms); |
537 break; |
57 break; |
538 } |
58 } |
539 case ESP_SLEEP_WAKEUP_UNDEFINED: |
59 case ESP_SLEEP_WAKEUP_UNDEFINED: |
540 default: |
60 default: |
541 ESP_LOGI(TAG, "Starting from hard reset"); |
61 ESP_LOGI(TAG, "Starting from hard reset"); |
542 screen_splash(); |
62 user_cold(); |
543 } |
63 } |
544 |
64 |
545 const int wakeup_time_sec = 55; |
65 const int wakeup_time_sec = 55; |
546 ESP_LOGI(TAG, "Enabling timer wakeup, %ds", wakeup_time_sec); |
66 ESP_LOGI(TAG, "Enabling timer wakeup, %ds", wakeup_time_sec); |
547 esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000); |
67 esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000); |
789 } |
281 } |
790 break; |
282 break; |
791 |
283 |
792 case ML1_WIFI_OFF: |
284 case ML1_WIFI_OFF: |
793 if (! ready_WiFi()) { |
285 if (! ready_WiFi()) { |
794 ESP_LOGI(TAG, "Loop timer: Done"); |
286 ESP_LOGI(TAG, "Loop timer: Done %s", user_busy() ? "true":"false"); |
795 Main_Loop1 = ML1_DONE; |
287 Main_Loop1 = ML1_DONE; |
796 if (Main_Loop2 == ML2_WIFI) |
288 // if (Main_Loop2 == ML2_WIFI) |
797 screen_wifi(); |
289 // screen_wifi(); |
798 } |
290 } |
799 break; |
291 break; |
800 |
292 |
801 case ML1_DONE: |
293 case ML1_DONE: |
802 break; |
294 break; |
803 } |
295 } |
804 |
296 |
805 /* |
297 if (Main_Loop1 == ML1_DONE && ! user_busy()) |
806 * One time actions |
|
807 */ |
|
808 if (New_Loop2 != Main_Loop2) { |
|
809 |
|
810 Main_Loop2 = New_Loop2; |
|
811 |
|
812 switch (Main_Loop2) { |
|
813 case ML2_INIT: |
|
814 ESP_LOGI(TAG, "Loop user: Init"); |
|
815 New_Loop2 = ML2_USER; |
|
816 usertimer = INACTIVITY; |
|
817 break; |
|
818 |
|
819 case ML2_USER: |
|
820 ESP_LOGI(TAG, "Loop user: User mainmenu"); |
|
821 screen_main(); |
|
822 break; |
|
823 |
|
824 case ML2_UNIT1: |
|
825 case ML2_UNIT2: |
|
826 case ML2_UNIT3: |
|
827 ESP_LOGI(TAG, "Loop user: Unit %d", Main_Loop2 - ML2_UNIT1); |
|
828 screen_unit(Main_Loop2 - ML2_UNIT1); |
|
829 break; |
|
830 |
|
831 case ML2_WIFI: |
|
832 ESP_LOGI(TAG, "Loop user: WiFi"); |
|
833 screen_wifi(); |
|
834 break; |
|
835 |
|
836 case ML2_NETWORK: |
|
837 ESP_LOGI(TAG, "Loop user: Network"); |
|
838 screen_network(); |
|
839 break; |
|
840 |
|
841 case ML2_MQTT: |
|
842 ESP_LOGI(TAG, "Loop user: MQTT"); |
|
843 screen_mqtt(); |
|
844 break; |
|
845 |
|
846 case ML2_SETUP_MQTT: |
|
847 ESP_LOGI(TAG, "Loop user: MQTT setup"); |
|
848 sprintf(txt, "EDtXt"); |
|
849 rotary_editer("MQTT demo", txt, "", 16, EDIT_TYPE_TEXT); |
|
850 New_Loop2 = ML2_MQTT; |
|
851 break; |
|
852 |
|
853 case ML2_UPDATE: |
|
854 ESP_LOGI(TAG, "Loop user: Update"); |
|
855 screen_update(); |
|
856 break; |
|
857 |
|
858 case ML2_SETUP_UNIT1: |
|
859 case ML2_SETUP_UNIT2: |
|
860 case ML2_SETUP_UNIT3: |
|
861 ESP_LOGI(TAG, "Loop user: Setup Unit %d", Main_Loop2 - ML2_SETUP_UNIT1); |
|
862 sub = 0; |
|
863 screen_unit_setup(Main_Loop2 - ML2_SETUP_UNIT1, sub); |
|
864 break; |
|
865 |
|
866 case ML2_ZERO_UNIT1: |
|
867 case ML2_ZERO_UNIT2: |
|
868 case ML2_ZERO_UNIT3: |
|
869 ESP_LOGI(TAG, "Loop user: Zero Unit %d", Main_Loop2 - ML2_ZERO_UNIT1); |
|
870 sub = 0; |
|
871 screen_unit_zero(Main_Loop2 - ML2_ZERO_UNIT1, sub); |
|
872 break; |
|
873 |
|
874 case ML2_INACTIVE: |
|
875 ESP_LOGI(TAG, "Loop user: Inactive"); |
|
876 u8g2_SetPowerSave(&u8g2, 1); // powersave display |
|
877 New_Loop2 = ML2_DONE; |
|
878 break; |
|
879 |
|
880 default: |
|
881 break; |
|
882 } |
|
883 } |
|
884 |
|
885 /* |
|
886 * Handle rotationg the rotary encoder. |
|
887 */ |
|
888 if (Main_Loop2 < ML2_INACTIVE) { |
|
889 // If not configured, start configure |
|
890 // If configured select first unit |
|
891 // Handle screen (first is show measured values) |
|
892 |
|
893 // Display per unit. Temp + Pressure + state. Press is setup this sensor. |
|
894 // Setup menu: Sensors |
|
895 // WiFi |
|
896 // MQTT |
|
897 // System (timers etc) |
|
898 // Update OTA |
|
899 // Return |
|
900 |
|
901 // Sensors menu: Assignments, turn to choose one. |
|
902 // Sensors setup menu: DS18B20 addr Press is assign |
|
903 // DS18B20 addr |
|
904 |
|
905 if (xQueueReceive(event_queue, &event, 250 / portTICK_PERIOD_MS) == pdTRUE) { |
|
906 usertimer = INACTIVITY; |
|
907 switch (Main_Loop2) { |
|
908 case ML2_USER: rotate_to_menu(event.state.position, ML2_UNIT1, ML2_USER); break; |
|
909 case ML2_UNIT1: rotate_to_menu(event.state.position, ML2_UNIT2, ML2_USER); break; |
|
910 case ML2_UNIT2: rotate_to_menu(event.state.position, ML2_UNIT3, ML2_UNIT1); break; |
|
911 case ML2_UNIT3: rotate_to_menu(event.state.position, ML2_WIFI, ML2_UNIT2); break; |
|
912 case ML2_WIFI: rotate_to_menu(event.state.position, ML2_NETWORK, ML2_UNIT3); break; |
|
913 case ML2_NETWORK: rotate_to_menu(event.state.position, ML2_MQTT, ML2_WIFI); break; |
|
914 case ML2_MQTT: rotate_to_menu(event.state.position, ML2_UPDATE, ML2_NETWORK); break; |
|
915 case ML2_UPDATE: rotate_to_menu(event.state.position, ML2_UPDATE, ML2_MQTT); break; |
|
916 case ML2_SETUP_UNIT1: |
|
917 case ML2_SETUP_UNIT2: |
|
918 case ML2_SETUP_UNIT3: if (rotate_to_sub(event.state.position, 0, 3, &sub)) |
|
919 screen_unit_setup(Main_Loop2 - ML2_SETUP_UNIT1, sub); |
|
920 break; |
|
921 case ML2_ZERO_UNIT1: |
|
922 case ML2_ZERO_UNIT2: |
|
923 case ML2_ZERO_UNIT3: if (rotate_to_sub(event.state.position, 0, 1, &sub)) |
|
924 screen_unit_zero(Main_Loop2 - ML2_ZERO_UNIT1, sub); |
|
925 break; |
|
926 default: |
|
927 ESP_LOGI(TAG, "Event: position %d, direction %s", event.state.position, |
|
928 event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW":"CCW"):"NOT_SET"); |
|
929 } |
|
930 } else { |
|
931 // Poll current position and direction |
|
932 // rotary_encoder_state_t state = { 0 }; |
|
933 // ESP_ERROR_CHECK(rotary_encoder_get_state(&rinfo, &state)); |
|
934 |
|
935 // ESP_LOGI(TAG, "Poll: position %d, direction %s timer %d", state.position, |
|
936 // state.direction ? (state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET", usertimer); |
|
937 if (usertimer) { |
|
938 usertimer--; |
|
939 if ((usertimer % 240) == 0) { // Each minute |
|
940 ESP_LOGI(TAG, "usertimer %d", usertimer); |
|
941 if (Main_Loop1 == ML1_DONE) |
|
942 Main_Loop1 = ML1_INIT; |
|
943 } |
|
944 } else |
|
945 New_Loop2 = ML2_INACTIVE; |
|
946 } |
|
947 } |
|
948 |
|
949 /* |
|
950 * Handle pressed rotary button. |
|
951 */ |
|
952 if (PushDuration) { |
|
953 int idx = 0; |
|
954 switch (Main_Loop2) { |
|
955 case ML2_UNIT1: |
|
956 case ML2_UNIT2: |
|
957 case ML2_UNIT3: |
|
958 New_Loop2 = ML2_SETUP_UNIT1 + (Main_Loop2 - ML2_UNIT1); |
|
959 break; |
|
960 |
|
961 case ML2_SETUP_UNIT1: |
|
962 case ML2_SETUP_UNIT2: |
|
963 case ML2_SETUP_UNIT3: |
|
964 idx = Main_Loop2 - ML2_SETUP_UNIT1; |
|
965 if (sub == 0) { |
|
966 if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE) { |
|
967 if (units[idx].mode) |
|
968 units[idx].mode = 0; |
|
969 else |
|
970 units[idx].mode = 1; |
|
971 write_units(); |
|
972 xSemaphoreGive(xSemaphoreUnits); |
|
973 } |
|
974 screen_unit_setup(idx, sub); |
|
975 if (Main_Loop1 == ML1_DONE) |
|
976 Main_Loop1 = ML1_INIT; |
|
977 } |
|
978 if (sub == 1) |
|
979 New_Loop2 = ML2_ZERO_UNIT1 + idx; |
|
980 if (sub == 3) |
|
981 New_Loop2 = ML2_UNIT1 + idx; |
|
982 printf("unit setup sub %d new %d idx %d\n", sub, New_Loop2, idx); |
|
983 break; |
|
984 |
|
985 case ML2_ZERO_UNIT1: |
|
986 case ML2_ZERO_UNIT2: |
|
987 case ML2_ZERO_UNIT3: |
|
988 idx = Main_Loop2 - ML2_ZERO_UNIT1; |
|
989 if (sub == 0) { |
|
990 if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE && |
|
991 xSemaphoreTake(xSemaphoreADC, 10) == pdTRUE && |
|
992 adc_state->Pressure[idx].voltage > 165 && |
|
993 adc_state->Pressure[idx].voltage < 660) { |
|
994 units[idx].pressure_zero = adc_state->Pressure[idx].voltage / (adc_state->Batt_voltage / 1000); |
|
995 write_units(); |
|
996 xSemaphoreGive(xSemaphoreADC); |
|
997 xSemaphoreGive(xSemaphoreUnits); |
|
998 screen_unit_zero(idx, sub); |
|
999 if (Main_Loop1 == ML1_DONE) |
|
1000 Main_Loop1 = ML1_INIT; |
|
1001 } |
|
1002 } |
|
1003 if (sub == 1) { |
|
1004 New_Loop2 = ML2_SETUP_UNIT1 + idx; |
|
1005 sub = 1; |
|
1006 } |
|
1007 printf("unit zero sub %d new %d idx %d\n", sub, New_Loop2, idx); |
|
1008 break; |
|
1009 |
|
1010 case ML2_MQTT: |
|
1011 New_Loop2 = ML2_SETUP_MQTT; |
|
1012 break; |
|
1013 |
|
1014 default: |
|
1015 break; |
|
1016 } |
|
1017 PushDuration = 0; |
|
1018 } |
|
1019 |
|
1020 if (Main_Loop1 == ML1_DONE && Main_Loop2 == ML2_DONE) |
|
1021 break; |
298 break; |
1022 |
299 |
1023 vTaskDelay(10 / portTICK_PERIOD_MS); |
300 vTaskDelay(10 / portTICK_PERIOD_MS); |
1024 } |
301 } |
1025 |
302 |
1026 ESP_LOGI(TAG, "Entering deep sleep"); |
303 ESP_LOGI(TAG, "Entering deep sleep"); |
1027 gettimeofday(&sleep_enter_time, NULL); |
304 gettimeofday(&sleep_enter_time, NULL); |
1028 esp_deep_sleep_start(); |
305 esp_deep_sleep_start(); |
1029 |
306 |
|
307 // ESP_LOGI(TAG, "Do nothing loop"); |
|
308 // vTaskDelay(55000 / portTICK_PERIOD_MS); |
|
309 |
1030 Main_Loop1 = ML1_INIT; |
310 Main_Loop1 = ML1_INIT; |
1031 New_Loop2 = ML2_INIT; |
311 } |
1032 } |
|
1033 |
|
1034 } |
312 } |
1035 |
313 |