main/co2meter.c

changeset 0
88d965579617
child 2
c0184362d48c
equal deleted inserted replaced
-1:000000000000 0:88d965579617
1 /*
2 * co2meter project.
3 */
4
5 #include "config.h"
6
7 static const char *TAG = "co2meter";
8
9 #define PIN_SDA (CONFIG_I2C_MASTER_SDA)
10 #define PIN_SCL (CONFIG_I2C_MASTER_SCL)
11 #define ROT_ENC_A_GPIO (CONFIG_ROT_ENC_A_GPIO)
12 #define ROT_ENC_B_GPIO (CONFIG_ROT_ENC_B_GPIO)
13 #define ROT_ENC_SW_GPIO (CONFIG_ROT_ENC_SW_GPIO)
14
15
16 #define ENABLE_HALF_STEPS false ///< Set to true to enable tracking of rotary encoder at half step resolution
17 #define RESET_AT 0 ///< Set to a positive non-zero number to reset the position if this value is exceeded
18 #define FLIP_DIRECTION false ///< Set to true to reverse the clockwise/counterclockwise sense
19
20
21 int Main_Loop1 = MAIN_LOOP1_INIT; ///< Loop 1 init
22 int Main_Loop2 = -1; ///< Loop 2 invalid
23 bool System_TimeOk = false; ///< System time status
24 time_t now; ///< Current time
25 struct tm timeinfo; ///< Current time structure
26 char strftime_buf[64]; ///< Time buffer
27 static RTC_DATA_ATTR struct timeval sleep_enter_time;
28 static TaskHandle_t xTaskDS18B20 = NULL;
29 static TaskHandle_t xTaskADC = NULL;
30 static TaskHandle_t xTaskWifi = NULL;
31 static TaskHandle_t xTaskMQTT = NULL;
32 const esp_app_desc_t *app_desc = NULL;
33 unit_t units[3]; ///< Pressure test units
34
35
36 extern DS18B20_State *ds18b20_state; ///< DS18B20 state
37 extern SemaphoreHandle_t xSemaphoreDS18B20; ///< DS18B20 lock semaphore
38 extern ADC_State *adc_state; ///< ADC state
39 extern SemaphoreHandle_t xSemaphoreADC; ///< ADC lock semaphore
40
41
42 void app_main()
43 {
44 struct timeval now;
45 gettimeofday(&now, NULL);
46 int sleep_time_ms = (now.tv_sec - sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - sleep_enter_time.tv_usec) / 1000;
47 int New_Loop2 = MAIN_LOOP2_INIT;
48 esp_err_t ret;
49
50 Main_Loop1 = MAIN_LOOP1_INIT;
51 Main_Loop2 = -1;
52
53 switch (esp_sleep_get_wakeup_cause()) {
54 case ESP_SLEEP_WAKEUP_EXT1: {
55 uint64_t wakeup_pin_mask = esp_sleep_get_ext1_wakeup_status();
56 if (wakeup_pin_mask != 0) {
57 int pin = __builtin_ffsll(wakeup_pin_mask) - 1;
58 printf("Wake up from GPIO %d\n", pin);
59 } else {
60 printf("Wake up from GPIO\n");
61 }
62 break;
63 }
64 case ESP_SLEEP_WAKEUP_TIMER: {
65 ESP_LOGI(TAG, "Starting from deep sleep, timer wakeup after %dms", sleep_time_ms);
66 break;
67 }
68 case ESP_SLEEP_WAKEUP_UNDEFINED:
69 default:
70 ESP_LOGI(TAG, "Starting from hard reset");
71 }
72
73 const int wakeup_time_sec = 20;
74 ESP_LOGI(TAG, "Enabling timer wakeup, %ds", wakeup_time_sec);
75 esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000);
76
77 // const int ext_wakeup_pin_1 = ROT_ENC_SW_GPIO; // 25 in example, redefine to rotary name.
78 // const uint64_t ext_wakeup_pin_1_mask = 1ULL << ext_wakeup_pin_1;
79
80 // printf("Enabling EXT1 wakeup on pins GPIO%d\n", ext_wakeup_pin_1);
81 // esp_sleep_enable_ext1_wakeup(ext_wakeup_pin_1_mask, ESP_EXT1_WAKEUP_ANY_HIGH); // TODO: what is the logic of the rotary button.
82
83 // Isolate GPIO12 pin from external circuits. This is needed for modules
84 // which have an external pull-up resistor on GPIO12 (such as ESP32-WROVER)
85 // to minimize current consumption.
86 // rtc_gpio_isolate(GPIO_NUM_12);
87
88 app_desc = esp_ota_get_app_description();
89
90 /*
91 * Initialize NVS
92 */
93 ret = nvs_flash_init();
94 if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
95 ESP_ERROR_CHECK(nvs_flash_erase());
96 ret = nvs_flash_init();
97 }
98 ESP_ERROR_CHECK(ret);
99
100 /*
101 * Setup the OLED display.
102 * See: https://github.com/nkolban/esp32-snippets/blob/master/hardware/displays/U8G2/
103 */
104 // u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;
105 // u8g2_esp32_hal.sda = PIN_SDA;
106 // u8g2_esp32_hal.scl = PIN_SCL;
107 // u8g2_esp32_hal_init(u8g2_esp32_hal);
108
109 // u8g2_t u8g2; // a structure which will contain all the data for one display
110 //// u8g2_Setup_ssd1306_i2c_128x32_univision_f(
111 // u8g2_Setup_sh1106_i2c_128x64_noname_f(
112 // &u8g2,
113 // U8G2_R0,
114 // //u8x8_byte_sw_i2c,
115 // u8g2_esp32_i2c_byte_cb,
116 // u8g2_esp32_gpio_and_delay_cb); // init u8g2 structure
117 // u8x8_SetI2CAddress(&u8g2.u8x8,0x78);
118
119 // ESP_LOGI(TAG, "u8g2_InitDisplay");
120 // u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
121
122 // ESP_LOGI(TAG, "u8g2_SetPowerSave");
123 // u8g2_SetPowerSave(&u8g2, 0); // wake up display
124 // ESP_LOGI(TAG, "u8g2_ClearBuffer");
125 // u8g2_ClearBuffer(&u8g2);
126 // ESP_LOGI(TAG, "u8g2_DrawBox");
127 // u8g2_DrawBox(&u8g2, 0, 26, 80,6);
128 // u8g2_DrawFrame(&u8g2, 0,26,100,6);
129
130 // ESP_LOGI(TAG, "u8g2_SetFont");
131 // u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr);
132 // ESP_LOGI(TAG, "u8g2_DrawStr");
133 // u8g2_DrawStr(&u8g2, 2,17,"Hi nkolban!");
134 // ESP_LOGI(TAG, "u8g2_SendBuffer");
135 // u8g2_SendBuffer(&u8g2);
136
137
138 ESP_LOGI(TAG, "Initializing SPIFFS");
139
140 esp_vfs_spiffs_conf_t conf = {
141 .base_path = "/spiffs",
142 .partition_label = NULL,
143 .max_files = 5,
144 .format_if_mount_failed = true
145 };
146
147 // Use settings defined above to initialize and mount SPIFFS filesystem.
148 // Note: esp_vfs_spiffs_register is an all-in-one convenience function.
149 ret = esp_vfs_spiffs_register(&conf);
150
151 if (ret != ESP_OK) {
152 if (ret == ESP_FAIL) {
153 ESP_LOGE(TAG, "Failed to mount or format filesystem");
154 } else if (ret == ESP_ERR_NOT_FOUND) {
155 ESP_LOGE(TAG, "Failed to find SPIFFS partition");
156 } else {
157 ESP_LOGE(TAG, "Failed to initialize SPIFFS (%d)", ret);
158 }
159 // _fg = TFT_RED;
160 // TFT_print((char *)"error\r\n", LASTX, LASTY);
161 return; // Stop application.
162 }
163
164 size_t total = 0, used = 0;
165 ret = esp_spiffs_info(NULL, &total, &used);
166 if (ret != ESP_OK) {
167 ESP_LOGE(TAG, "Failed to get SPIFFS partition information");
168 // _fg = TFT_RED;
169 // TFT_print((char *)"error\r\n", LASTX, LASTY);
170 return; // Stop application.
171 } else {
172 ESP_LOGI(TAG, "Partition size: %d, used: %d - %d%%", total, used, (used * 100) / total);
173 }
174
175 // Just to debug, list the /spiffs filesystem.
176 #if 1
177 DIR *dir = opendir("/spiffs");
178 struct dirent* de = readdir(dir);
179 while (de) {
180 if (de->d_type == DT_REG) {
181 printf("F ");
182 }
183 if (de->d_type == DT_DIR) {
184 printf("D ");
185 }
186 printf("%s\n", de->d_name);
187 de = readdir(dir);
188 }
189 closedir(dir);
190 #endif
191
192 /*
193 * Read or create configuration
194 */
195 // TFT_print((char *)"Ophalen configuratie ", LASTX, LASTY);
196 read_config();
197 read_units();
198
199 //add_station((uint8_t *)"MBSE_WLR", (uint8_t *)"abcjkltuv");
200 //remove_station((uint8_t *)"MBSE_WLP");
201 //sprintf(config.lastSSID, "%s", "BREWER");
202 //write_config();
203
204 xSemaphoreDS18B20 = xSemaphoreCreateMutex();
205 xSemaphoreADC = xSemaphoreCreateMutex();
206
207 xTaskCreate(&task_ds18b20, "task_ds18b20", 2560, NULL, 8, &xTaskDS18B20);
208 xTaskCreate(&task_adc, "task_adc", 2560, NULL, 8, &xTaskADC);
209 esp_log_level_set("wifi", ESP_LOG_ERROR);
210 xTaskCreate(&task_wifi, "task_wifi", 4096, NULL, 3, &xTaskWifi);
211 vTaskDelay( (TickType_t)10);
212 xTaskCreate(&task_mqtt, "task_mqtt", 4096, NULL, 5, &xTaskMQTT);
213
214 // esp32-rotary-encoder requires that the GPIO ISR service is installed before calling rotary_encoder_register()
215 // ESP_ERROR_CHECK(gpio_install_isr_service(0));
216
217 // Initialise the rotary encoder device with the GPIOs for A and B signals
218 // rotary_encoder_info_t info = { 0 };
219 // ESP_ERROR_CHECK(rotary_encoder_init(&info, ROT_ENC_A_GPIO, ROT_ENC_B_GPIO));
220 // ESP_ERROR_CHECK(rotary_encoder_enable_half_steps(&info, ENABLE_HALF_STEPS));
221 #ifdef FLIP_DIRECTION
222 // ESP_ERROR_CHECK(rotary_encoder_flip_direction(&info));
223 #endif
224
225 // Create a queue for events from the rotary encoder driver.
226 // Tasks can read from this queue to receive up to date position information.
227 // QueueHandle_t event_queue = rotary_encoder_create_queue();
228 // ESP_ERROR_CHECK(rotary_encoder_set_queue(&info, event_queue));
229
230
231 /* Print chip information */
232 // esp_chip_info_t chip_info;
233 // esp_chip_info(&chip_info);
234 // printf("This is ESP32 chip with %d CPU cores, WiFi%s%s, ",
235 // chip_info.cores,
236 // (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
237 // (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
238
239 // printf("silicon revision %d, ", chip_info.revision);
240
241 // printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
242 // (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
243
244 // esp_err_t status = adc2_vref_to_gpio(GPIO_NUM_26);
245 // if (status == ESP_OK) {
246 // printf("v_ref routed to GPIO\n");
247 // } else {
248 // printf("failed to route v_ref\n");
249 // }
250 // vTaskDelay(1000 * wakeup_time_sec / portTICK_PERIOD_MS);
251
252 /*
253 * Main application loop.
254 */
255 while (1) {
256
257 ESP_LOGI(TAG, "Entered app loop");
258
259 /* Measure process or user input via rotary switch */
260 while (1) {
261 switch (Main_Loop1) {
262 case MAIN_LOOP1_INIT:
263 ESP_LOGI(TAG, "Loop timer: Init");
264 // If configured do MAIN_LOOP1_CONNECT
265 Main_Loop1 = MAIN_LOOP1_CONNECT;
266 requestWiFi_system(true);
267 request_ds18b20();
268 request_adc();
269 break;
270
271 case MAIN_LOOP1_CONNECT:
272 if (ready_WiFi())
273 Main_Loop1 = MAIN_LOOP1_MQTT_CONNECT;
274 break;
275
276 case MAIN_LOOP1_MQTT_CONNECT:
277 if (ready_ds18b20() && ready_adc()) {
278 connect_mqtt(true);
279 Main_Loop1 = MAIN_LOOP1_WAITCON;
280 ESP_LOGI(TAG, "Loop timer: Wait MQTT");
281
282 /* Get global temperature, use for all units. */
283 uint32_t temp = 0;
284 int state = 0;
285 if (xSemaphoreTake(xSemaphoreDS18B20, 10) == pdTRUE) {
286 temp = (ds18b20_state->bottle_temperature * 1000);
287 state = (ds18b20_state->bottle_error == 0) ? 0:1;
288 xSemaphoreGive(xSemaphoreDS18B20);
289 }
290
291 /* Copy measured data and calculate results */
292 for (int i = 0; i < 3; i++) {
293 units[i].temperature = temp;
294 units[i].temperature_state = state;
295 if (xSemaphoreTake(xSemaphoreADC, 10) == pdTRUE) {
296 units[i].pressure_state = adc_state->Pressure[i].error;
297 units[i].pressure_channel = adc_state->Pressure[i].channel;
298 units[i].pressure_voltage = adc_state->Pressure[i].voltage;
299 units[i].pressure_zero = 110;
300 int P = (units[i].pressure_voltage / (adc_state->Batt_voltage / 1000) - units[i].pressure_zero) * 14; // in bar
301 if (P < 0)
302 P = 0;
303 units[i].pressure = P;
304 printf("%d volt: %d batt: %d scale: %d bar: %d\n", i, units[i].pressure_voltage, adc_state->Batt_voltage,
305 units[i].pressure_voltage / (adc_state->Batt_voltage / 1000) - units[i].pressure_zero, P);
306 // Moet die echt op 5 volt?
307 // Verbruik 10 mA
308 // Setup tijd max 2 mS
309 xSemaphoreGive(xSemaphoreADC);
310 }
311 }
312 write_units();
313 }
314 break;
315
316 // calculate stap en data copy
317
318 case MAIN_LOOP1_WAITCON:
319 if (ready_mqtt())
320 Main_Loop1 = MAIN_LOOP1_SEND;
321 break;
322
323 case MAIN_LOOP1_SEND:
324 ESP_LOGI(TAG, "Loop timer: Send MQTT");
325 publishNode();
326 publishUnits();
327
328 Main_Loop1 = MAIN_LOOP1_MQTT_DISCONNECT;
329 break;
330
331 case MAIN_LOOP1_WAITACK:
332 break;
333
334 case MAIN_LOOP1_MQTT_DISCONNECT:
335 ESP_LOGI(TAG, "Loop timer: Disconnect MQTT");
336 connect_mqtt(false);
337 Main_Loop1 = MAIN_LOOP1_DISCONNECT;
338 break;
339
340 case MAIN_LOOP1_DISCONNECT:
341 if (! ready_mqtt()) {
342 ESP_LOGI(TAG, "Loop timer: WiFi off");
343 requestWiFi_system(false);
344 Main_Loop1 = MAIN_LOOP1_WIFI_OFF;
345 }
346 break;
347
348 case MAIN_LOOP1_WIFI_OFF:
349 if (! ready_WiFi()) {
350 ESP_LOGI(TAG, "Loop timer: Done");
351 Main_Loop1 = MAIN_LOOP1_DONE;
352 }
353 break;
354
355 case MAIN_LOOP1_DONE:
356 break;
357 }
358
359 /*
360 * One time actions
361 */
362 if (New_Loop2 != Main_Loop2) {
363
364 Main_Loop2 = New_Loop2;
365
366 switch (Main_Loop2) {
367 case MAIN_LOOP2_INIT:
368 ESP_LOGI(TAG, "Loop user: Init");
369 // u8g2_SetPowerSave(&u8g2, 0); // wake up display
370 // u8g2_ClearBuffer(&u8g2);
371 // New_Loop2 = MAIN_LOOP2_INACTIVE;
372 New_Loop2 = MAIN_LOOP2_DONE;
373 break;
374
375 case MAIN_LOOP2_INACTIVE:
376 // u8g2_SetPowerSave(&u8g2, 1); // powersave display
377 New_Loop2 = MAIN_LOOP2_DONE;
378 break;
379
380 default:
381 break;
382 }
383 }
384
385 /*
386 * Action process.
387 */
388 switch (Main_Loop2) {
389 // If wakeup from GPIO -- state machine 2
390 // Init OLED
391 // If not configured, start configure
392 // If configured select first unit
393 // New rotate position, set screen, reset waittimer
394 // Handle screen (first is show measured values)
395 // Count inactivity
396 // flag if inactive and OLED lowpower.
397
398 // Break if all done and inactive.
399 default:
400 break;
401 }
402
403 if (Main_Loop1 == MAIN_LOOP1_DONE && Main_Loop2 == MAIN_LOOP2_DONE)
404 break;
405
406 vTaskDelay(10 / portTICK_PERIOD_MS);
407 }
408
409 // printf("Simulate deep sleep\n");
410 // vTaskDelay(1000 * wakeup_time_sec / portTICK_PERIOD_MS);
411
412 printf("Entering deep sleep\n");
413 gettimeofday(&sleep_enter_time, NULL);
414 esp_deep_sleep_start();
415
416 Main_Loop1 = MAIN_LOOP1_INIT;
417 New_Loop2 = MAIN_LOOP2_INIT;
418 }
419
420 }
421

mercurial