main/co2meter.c

changeset 21
043ae27633f8
parent 20
7c1dacafed03
child 22
cceb36fd3a2a
equal deleted inserted replaced
20:7c1dacafed03 21:043ae27633f8
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);
588 } else if (ret == ESP_ERR_NOT_FOUND) { 108 } else if (ret == ESP_ERR_NOT_FOUND) {
589 ESP_LOGE(TAG, "Failed to find SPIFFS partition"); 109 ESP_LOGE(TAG, "Failed to find SPIFFS partition");
590 } else { 110 } else {
591 ESP_LOGE(TAG, "Failed to initialize SPIFFS (%d)", ret); 111 ESP_LOGE(TAG, "Failed to initialize SPIFFS (%d)", ret);
592 } 112 }
593 screen_fatal("SPIFFS:", "init error");
594 return; // Stop application. 113 return; // Stop application.
595 } 114 }
596 115
597 size_t total = 0, used = 0; 116 size_t total = 0, used = 0;
598 ret = esp_spiffs_info(NULL, &total, &used); 117 ret = esp_spiffs_info(NULL, &total, &used);
599 if (ret != ESP_OK) { 118 if (ret != ESP_OK) {
600 ESP_LOGE(TAG, "Failed to get SPIFFS partition information"); 119 ESP_LOGE(TAG, "Failed to get SPIFFS partition information");
601 screen_fatal("SPIFFS:", "partition error");
602 return; // Stop application. 120 return; // Stop application.
603 } else { 121 } else {
604 ESP_LOGI(TAG, "Partition size: %d, used: %d - %d%%", total, used, (used * 100) / total); 122 ESP_LOGI(TAG, "Partition size: %d, used: %d - %d%%", total, used, (used * 100) / total);
605 } 123 }
606 124
635 */ 153 */
636 xSemaphoreDS18B20 = xSemaphoreCreateMutex(); 154 xSemaphoreDS18B20 = xSemaphoreCreateMutex();
637 xSemaphoreADC = xSemaphoreCreateMutex(); 155 xSemaphoreADC = xSemaphoreCreateMutex();
638 xSemaphoreUnits = xSemaphoreCreateMutex(); 156 xSemaphoreUnits = xSemaphoreCreateMutex();
639 157
158 xTaskCreate(&task_user, "task_user", 2048, NULL,10, &xTaskUser);
640 xTaskCreate(&task_ds18b20, "task_ds18b20", 2560, NULL, 8, &xTaskDS18B20); 159 xTaskCreate(&task_ds18b20, "task_ds18b20", 2560, NULL, 8, &xTaskDS18B20);
641 xTaskCreate(&task_adc, "task_adc", 2560, NULL, 8, &xTaskADC); 160 xTaskCreate(&task_adc, "task_adc", 2560, NULL, 8, &xTaskADC);
642 esp_log_level_set("wifi", ESP_LOG_ERROR); 161 esp_log_level_set("wifi", ESP_LOG_ERROR);
643 xTaskCreate(&task_wifi, "task_wifi", 4096, NULL, 3, &xTaskWifi); 162 xTaskCreate(&task_wifi, "task_wifi", 4096, NULL, 3, &xTaskWifi);
644 vTaskDelay(10 / portTICK_PERIOD_MS); 163 vTaskDelay(10 / portTICK_PERIOD_MS);
645 esp_log_level_set("MQTT_CLIENT", ESP_LOG_ERROR); 164 esp_log_level_set("MQTT_CLIENT", ESP_LOG_ERROR);
646 xTaskCreate(&task_mqtt, "task_mqtt", 4096, NULL, 5, &xTaskMQTT); 165 xTaskCreate(&task_mqtt, "task_mqtt", 4096, NULL, 5, &xTaskMQTT);
647 166
648 /* 167 /*
649 * Setup the Rotary Encoder.
650 * esp32-rotary-encoder requires that the GPIO ISR service is
651 * installed before calling rotary_encoder_register()
652 */
653 ESP_ERROR_CHECK(gpio_install_isr_service(0));
654 ESP_ERROR_CHECK(rotary_encoder_init(&rinfo, ROT_ENC_A_GPIO, ROT_ENC_B_GPIO));
655 ESP_ERROR_CHECK(rotary_encoder_enable_half_steps(&rinfo, false));
656
657 gpio_config_t io_conf;
658 io_conf.intr_type = GPIO_PIN_INTR_ANYEDGE;
659 io_conf.pin_bit_mask = (1ULL << ROT_ENC_SW_GPIO);
660 io_conf.mode = GPIO_MODE_INPUT;
661 gpio_config(&io_conf);
662
663 gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
664 xTaskCreate(gpio_task, "gpio_task", 2048, NULL, 10, NULL);
665
666 gpio_isr_handler_add(ROT_ENC_SW_GPIO, gpio_isr_handler, (void*) ROT_ENC_SW_GPIO);
667
668 // Create a queue for events from the rotary encoder driver.
669 // Tasks can read from this queue to receive up to date position information.
670 event_queue = rotary_encoder_create_queue();
671 ESP_ERROR_CHECK(rotary_encoder_set_queue(&rinfo, event_queue));
672
673 /*
674 * Main application loop. 168 * Main application loop.
675 */ 169 */
676 while (1) { 170 while (1) {
677 171
678 ESP_LOGI(TAG, "Entered app loop"); 172 ESP_LOGI(TAG, "Entered app loop");
679 //event = { 0 }; 173
680 int sub = 0; 174 /* Measure process */
681 u8g2_SetPowerSave(&u8g2, 1);
682
683 /* Measure process or user input via rotary switch */
684 while (1) { 175 while (1) {
685 switch (Main_Loop1) { 176 switch (Main_Loop1) {
686 case ML1_INIT: 177 case ML1_INIT:
687 ESP_LOGI(TAG, "Loop timer: Init"); 178 ESP_LOGI(TAG, "Loop timer: Init");
688 // If configured do ML1_CONNECT 179 // If configured do ML1_CONNECT
693 break; 184 break;
694 185
695 case ML1_CONNECT: 186 case ML1_CONNECT:
696 if (ready_WiFi()) { 187 if (ready_WiFi()) {
697 Main_Loop1 = ML1_MQTT_CONNECT; 188 Main_Loop1 = ML1_MQTT_CONNECT;
698 if (Main_Loop2 == ML2_WIFI) 189 // if (Main_Loop2 == ML2_WIFI)
699 screen_wifi(); 190 // screen_wifi();
700 } 191 }
701 break; 192 break;
702 193
703 case ML1_MQTT_CONNECT: 194 case ML1_MQTT_CONNECT:
704 if (ready_ds18b20() && ready_adc()) { 195 if (ready_ds18b20() && ready_adc()) {
745 xSemaphoreGive(xSemaphoreADC); 236 xSemaphoreGive(xSemaphoreADC);
746 } 237 }
747 } 238 }
748 write_units(); 239 write_units();
749 xSemaphoreGive(xSemaphoreUnits); 240 xSemaphoreGive(xSemaphoreUnits);
750 switch (Main_Loop2) { 241 user_refresh();
751 case ML2_USER: screen_main(); break; 242 // switch (Main_Loop2) {
752 case ML2_UNIT1: screen_unit(0); break; 243 // case ML2_USER: screen_main(); break;
753 case ML2_UNIT2: screen_unit(1); break; 244 // case ML2_UNIT1: screen_unit(0); break;
754 case ML2_UNIT3: screen_unit(2); break; 245 // case ML2_UNIT2: screen_unit(1); break;
755 } 246 // case ML2_UNIT3: screen_unit(2); break;
247 // }
756 } 248 }
757 } 249 }
758 break; 250 break;
759 251
760 case ML1_WAITCON: 252 case ML1_WAITCON:
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

mercurial