main/co2meter.c

changeset 0
88d965579617
child 2
c0184362d48c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/co2meter.c	Tue Oct 08 12:00:31 2019 +0200
@@ -0,0 +1,421 @@
+/*
+ * co2meter project.
+ */
+
+#include "config.h"
+
+static const char *TAG = "co2meter";
+
+#define PIN_SDA 		(CONFIG_I2C_MASTER_SDA)
+#define PIN_SCL			(CONFIG_I2C_MASTER_SCL)
+#define ROT_ENC_A_GPIO		(CONFIG_ROT_ENC_A_GPIO)
+#define ROT_ENC_B_GPIO		(CONFIG_ROT_ENC_B_GPIO)
+#define ROT_ENC_SW_GPIO		(CONFIG_ROT_ENC_SW_GPIO)
+
+
+#define ENABLE_HALF_STEPS	false		///< Set to true to enable tracking of rotary encoder at half step resolution
+#define RESET_AT		0		///< Set to a positive non-zero number to reset the position if this value is exceeded
+#define FLIP_DIRECTION		false		///< Set to true to reverse the clockwise/counterclockwise sense
+
+
+int					Main_Loop1 = MAIN_LOOP1_INIT;	///< Loop 1 init
+int					Main_Loop2 = -1;		///< Loop 2 invalid
+bool                            	System_TimeOk = false;          ///< System time status
+time_t                          	now;                            ///< Current time
+struct tm                       	timeinfo;                       ///< Current time structure
+char                            	strftime_buf[64];               ///< Time buffer
+static RTC_DATA_ATTR struct timeval	sleep_enter_time;
+static TaskHandle_t			xTaskDS18B20 = NULL;
+static TaskHandle_t			xTaskADC = NULL;
+static TaskHandle_t			xTaskWifi = NULL;
+static TaskHandle_t			xTaskMQTT = NULL;
+const esp_app_desc_t			*app_desc = NULL;
+unit_t					units[3];			///< Pressure test units
+
+
+extern DS18B20_State            	*ds18b20_state;         	///< DS18B20 state
+extern SemaphoreHandle_t        	xSemaphoreDS18B20;      	///< DS18B20 lock semaphore
+extern ADC_State                	*adc_state;             	///< ADC state
+extern SemaphoreHandle_t        	xSemaphoreADC;          	///< ADC lock semaphore
+
+
+void app_main()
+{
+    struct timeval	now;
+    gettimeofday(&now, NULL);
+    int			sleep_time_ms = (now.tv_sec - sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - sleep_enter_time.tv_usec) / 1000;
+    int			New_Loop2 = MAIN_LOOP2_INIT;
+    esp_err_t           ret;
+
+    Main_Loop1 = MAIN_LOOP1_INIT;
+    Main_Loop2 = -1;
+
+    switch (esp_sleep_get_wakeup_cause()) {
+        case ESP_SLEEP_WAKEUP_EXT1: {
+            uint64_t wakeup_pin_mask = esp_sleep_get_ext1_wakeup_status();
+            if (wakeup_pin_mask != 0) {
+                int pin = __builtin_ffsll(wakeup_pin_mask) - 1;
+                printf("Wake up from GPIO %d\n", pin);
+            } else {
+                printf("Wake up from GPIO\n");
+            }
+            break;
+        }
+        case ESP_SLEEP_WAKEUP_TIMER: {
+	    ESP_LOGI(TAG, "Starting from deep sleep, timer wakeup after %dms", sleep_time_ms);
+            break;
+        }
+        case ESP_SLEEP_WAKEUP_UNDEFINED:
+        default:
+            ESP_LOGI(TAG, "Starting from hard reset");
+    }
+
+    const int wakeup_time_sec = 20;
+    ESP_LOGI(TAG, "Enabling timer wakeup, %ds", wakeup_time_sec);
+    esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000);
+
+//    const int ext_wakeup_pin_1 = ROT_ENC_SW_GPIO; // 25 in example, redefine to rotary name.
+//    const uint64_t ext_wakeup_pin_1_mask = 1ULL << ext_wakeup_pin_1;
+
+//    printf("Enabling EXT1 wakeup on pins GPIO%d\n", ext_wakeup_pin_1);
+//    esp_sleep_enable_ext1_wakeup(ext_wakeup_pin_1_mask, ESP_EXT1_WAKEUP_ANY_HIGH); // TODO: what is the logic of the rotary button.
+
+    // Isolate GPIO12 pin from external circuits. This is needed for modules
+    // which have an external pull-up resistor on GPIO12 (such as ESP32-WROVER)
+    // to minimize current consumption.
+//    rtc_gpio_isolate(GPIO_NUM_12);
+
+    app_desc = esp_ota_get_app_description();
+
+    /*
+     * Initialize NVS
+     */
+    ret = nvs_flash_init();
+    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
+        ESP_ERROR_CHECK(nvs_flash_erase());
+        ret = nvs_flash_init();
+    }
+    ESP_ERROR_CHECK(ret);
+
+    /*
+     * Setup the OLED display.
+     * See: https://github.com/nkolban/esp32-snippets/blob/master/hardware/displays/U8G2/
+     */
+//    u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;
+//    u8g2_esp32_hal.sda = PIN_SDA;
+//    u8g2_esp32_hal.scl = PIN_SCL;
+//    u8g2_esp32_hal_init(u8g2_esp32_hal);
+
+//    u8g2_t u8g2; // a structure which will contain all the data for one display
+////    u8g2_Setup_ssd1306_i2c_128x32_univision_f(
+//    u8g2_Setup_sh1106_i2c_128x64_noname_f(
+//		&u8g2,
+//		U8G2_R0,
+//		//u8x8_byte_sw_i2c,
+//		u8g2_esp32_i2c_byte_cb,
+//		u8g2_esp32_gpio_and_delay_cb);  // init u8g2 structure
+//    u8x8_SetI2CAddress(&u8g2.u8x8,0x78);
+
+//    ESP_LOGI(TAG, "u8g2_InitDisplay");
+//    u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
+
+//    ESP_LOGI(TAG, "u8g2_SetPowerSave");
+//    u8g2_SetPowerSave(&u8g2, 0); // wake up display
+//    ESP_LOGI(TAG, "u8g2_ClearBuffer");
+//    u8g2_ClearBuffer(&u8g2);
+//    ESP_LOGI(TAG, "u8g2_DrawBox");
+//    u8g2_DrawBox(&u8g2, 0, 26, 80,6);
+//    u8g2_DrawFrame(&u8g2, 0,26,100,6);
+
+//    ESP_LOGI(TAG, "u8g2_SetFont");
+//    u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr);
+//    ESP_LOGI(TAG, "u8g2_DrawStr");
+//    u8g2_DrawStr(&u8g2, 2,17,"Hi nkolban!");
+//    ESP_LOGI(TAG, "u8g2_SendBuffer");
+//    u8g2_SendBuffer(&u8g2);
+
+
+    ESP_LOGI(TAG, "Initializing SPIFFS");
+
+    esp_vfs_spiffs_conf_t conf = {
+        .base_path = "/spiffs",
+        .partition_label = NULL,
+        .max_files = 5,
+        .format_if_mount_failed = true
+    };
+
+    // Use settings defined above to initialize and mount SPIFFS filesystem.
+    // Note: esp_vfs_spiffs_register is an all-in-one convenience function.
+    ret = esp_vfs_spiffs_register(&conf);
+
+    if (ret != ESP_OK) {
+        if (ret == ESP_FAIL) {
+            ESP_LOGE(TAG, "Failed to mount or format filesystem");
+        } else if (ret == ESP_ERR_NOT_FOUND) {
+            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
+        } else {
+            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%d)", ret);
+        }
+//        _fg = TFT_RED;
+//        TFT_print((char *)"error\r\n", LASTX, LASTY);
+        return; // Stop application.
+    }
+
+    size_t total = 0, used = 0;
+    ret = esp_spiffs_info(NULL, &total, &used);
+    if (ret != ESP_OK) {
+        ESP_LOGE(TAG, "Failed to get SPIFFS partition information");
+//        _fg = TFT_RED;
+//        TFT_print((char *)"error\r\n", LASTX, LASTY);
+        return; // Stop application.
+    } else {
+        ESP_LOGI(TAG, "Partition size: %d, used: %d - %d%%", total, used, (used * 100) / total);
+    }
+
+    // Just to debug, list the /spiffs filesystem.
+#if 1
+    DIR *dir = opendir("/spiffs");
+    struct dirent* de = readdir(dir);
+    while (de) {
+        if (de->d_type == DT_REG) {
+            printf("F ");
+        }
+        if (de->d_type == DT_DIR) {
+            printf("D ");
+        }
+        printf("%s\n", de->d_name);
+        de = readdir(dir);
+    }
+    closedir(dir);
+#endif
+
+    /*
+     * Read or create configuration
+     */
+//    TFT_print((char *)"Ophalen configuratie ", LASTX, LASTY);
+    read_config();
+    read_units();
+
+//add_station((uint8_t *)"MBSE_WLR", (uint8_t *)"abcjkltuv");
+//remove_station((uint8_t *)"MBSE_WLP");
+//sprintf(config.lastSSID, "%s", "BREWER");
+//write_config();
+
+    xSemaphoreDS18B20 = xSemaphoreCreateMutex();
+    xSemaphoreADC = xSemaphoreCreateMutex();
+
+    xTaskCreate(&task_ds18b20, "task_ds18b20",  2560, NULL, 8, &xTaskDS18B20);
+    xTaskCreate(&task_adc,     "task_adc",      2560, NULL, 8, &xTaskADC);
+    esp_log_level_set("wifi", ESP_LOG_ERROR);
+    xTaskCreate(&task_wifi,    "task_wifi",     4096, NULL, 3, &xTaskWifi);
+    vTaskDelay( (TickType_t)10);
+    xTaskCreate(&task_mqtt,    "task_mqtt",     4096, NULL, 5, &xTaskMQTT);
+
+    // esp32-rotary-encoder requires that the GPIO ISR service is installed before calling rotary_encoder_register()
+//    ESP_ERROR_CHECK(gpio_install_isr_service(0));
+
+    // Initialise the rotary encoder device with the GPIOs for A and B signals
+//    rotary_encoder_info_t info = { 0 };
+//    ESP_ERROR_CHECK(rotary_encoder_init(&info, ROT_ENC_A_GPIO, ROT_ENC_B_GPIO));
+//    ESP_ERROR_CHECK(rotary_encoder_enable_half_steps(&info, ENABLE_HALF_STEPS));
+#ifdef FLIP_DIRECTION
+//    ESP_ERROR_CHECK(rotary_encoder_flip_direction(&info));
+#endif
+
+    // Create a queue for events from the rotary encoder driver.
+    // Tasks can read from this queue to receive up to date position information.
+//    QueueHandle_t event_queue = rotary_encoder_create_queue();
+//    ESP_ERROR_CHECK(rotary_encoder_set_queue(&info, event_queue));
+
+
+    /* Print chip information */
+//    esp_chip_info_t chip_info;
+//    esp_chip_info(&chip_info);
+//    printf("This is ESP32 chip with %d CPU cores, WiFi%s%s, ",
+//            chip_info.cores,
+//            (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
+//            (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
+
+//    printf("silicon revision %d, ", chip_info.revision);
+
+//    printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
+//            (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
+
+//    esp_err_t status = adc2_vref_to_gpio(GPIO_NUM_26);
+//    if (status == ESP_OK) {
+//        printf("v_ref routed to GPIO\n");
+//    } else {
+//        printf("failed to route v_ref\n");
+//    }
+//    vTaskDelay(1000 * wakeup_time_sec / portTICK_PERIOD_MS);
+
+    /*
+     * Main application loop.
+     */
+    while (1) {
+
+	ESP_LOGI(TAG, "Entered app loop");
+
+	/* Measure process or user input via rotary switch */
+	while (1) {
+	    switch (Main_Loop1) {
+		case MAIN_LOOP1_INIT:
+		    ESP_LOGI(TAG, "Loop timer: Init");
+		    // If configured do MAIN_LOOP1_CONNECT
+		    Main_Loop1 = MAIN_LOOP1_CONNECT;
+		    requestWiFi_system(true);
+		    request_ds18b20();
+		    request_adc();
+		    break;
+
+		case MAIN_LOOP1_CONNECT:
+                    if (ready_WiFi())
+                        Main_Loop1 = MAIN_LOOP1_MQTT_CONNECT;
+                    break;
+
+		case MAIN_LOOP1_MQTT_CONNECT:
+		    if (ready_ds18b20() && ready_adc()) {
+			connect_mqtt(true);
+			Main_Loop1 = MAIN_LOOP1_WAITCON;
+			ESP_LOGI(TAG, "Loop timer: Wait MQTT");
+
+			/* Get global temperature, use for all units. */
+			uint32_t temp = 0;
+			int state = 0;
+			if (xSemaphoreTake(xSemaphoreDS18B20, 10) == pdTRUE) {
+			    temp = (ds18b20_state->bottle_temperature * 1000);
+			    state = (ds18b20_state->bottle_error == 0) ? 0:1;
+        		    xSemaphoreGive(xSemaphoreDS18B20);
+    			}
+
+			/* Copy measured data and calculate results */
+			for (int i = 0; i < 3; i++) {
+			    units[i].temperature = temp;
+			    units[i].temperature_state = state;
+			    if (xSemaphoreTake(xSemaphoreADC, 10) == pdTRUE) {
+				units[i].pressure_state = adc_state->Pressure[i].error;
+				units[i].pressure_channel = adc_state->Pressure[i].channel;
+				units[i].pressure_voltage = adc_state->Pressure[i].voltage;
+				units[i].pressure_zero = 110;
+				int P = (units[i].pressure_voltage / (adc_state->Batt_voltage / 1000) - units[i].pressure_zero) * 14; // in bar
+				if (P < 0)
+				    P = 0;
+				units[i].pressure = P;
+printf("%d volt: %d batt: %d scale: %d  bar: %d\n", i, units[i].pressure_voltage, adc_state->Batt_voltage, 
+		units[i].pressure_voltage / (adc_state->Batt_voltage / 1000) - units[i].pressure_zero, P);
+// Moet die echt op 5 volt?
+// Verbruik 10 mA
+// Setup tijd max 2 mS
+				xSemaphoreGive(xSemaphoreADC);
+			    }
+			}
+			write_units();
+		    }
+		    break;
+
+		// calculate stap en data copy
+
+		case MAIN_LOOP1_WAITCON:
+		    if (ready_mqtt())
+			Main_Loop1 = MAIN_LOOP1_SEND;
+		    break;
+
+		case MAIN_LOOP1_SEND:
+		    ESP_LOGI(TAG, "Loop timer: Send MQTT");
+		    publishNode();
+		    publishUnits();
+
+Main_Loop1 = MAIN_LOOP1_MQTT_DISCONNECT;
+		    break;
+
+		case MAIN_LOOP1_WAITACK:
+		    break;
+
+		case MAIN_LOOP1_MQTT_DISCONNECT:
+		    ESP_LOGI(TAG, "Loop timer: Disconnect MQTT");
+		    connect_mqtt(false);
+                    Main_Loop1 = MAIN_LOOP1_DISCONNECT;
+		    break;
+
+		case MAIN_LOOP1_DISCONNECT:
+		    if (! ready_mqtt()) {
+			ESP_LOGI(TAG, "Loop timer: WiFi off");
+		    	requestWiFi_system(false);
+		    	Main_Loop1 = MAIN_LOOP1_WIFI_OFF;
+		    }
+		    break;
+
+		case MAIN_LOOP1_WIFI_OFF:
+		    if (! ready_WiFi()) {
+			ESP_LOGI(TAG, "Loop timer: Done");
+			Main_Loop1 = MAIN_LOOP1_DONE;
+		    }
+		    break;
+
+		case MAIN_LOOP1_DONE:
+		    break;
+	    }
+
+	    /*
+	     * One time actions
+	     */
+	    if (New_Loop2 != Main_Loop2) {
+
+		Main_Loop2 = New_Loop2;
+
+		switch (Main_Loop2) {
+		    case MAIN_LOOP2_INIT:
+			ESP_LOGI(TAG, "Loop user: Init");
+//			u8g2_SetPowerSave(&u8g2, 0); // wake up display
+//			u8g2_ClearBuffer(&u8g2);
+//			New_Loop2 = MAIN_LOOP2_INACTIVE;
+			New_Loop2 = MAIN_LOOP2_DONE;
+			break;
+
+		    case MAIN_LOOP2_INACTIVE:
+//			u8g2_SetPowerSave(&u8g2, 1); // powersave display
+			New_Loop2 = MAIN_LOOP2_DONE;
+			break;
+
+		    default:
+			break;
+		}
+	    }
+
+	    /*
+	     * Action process.
+	     */
+	    switch (Main_Loop2) {
+	    // If wakeup from GPIO -- state machine 2
+		// Init OLED
+		// If not configured, start configure
+		// If configured select first unit
+		// New rotate position, set screen, reset waittimer
+		// Handle screen (first is show measured values)
+		// Count inactivity
+		// flag if inactive and OLED lowpower.
+
+	    // Break if all done and inactive.
+		default:
+		    break;
+	    }
+
+	    if (Main_Loop1 == MAIN_LOOP1_DONE && Main_Loop2 == MAIN_LOOP2_DONE) 
+	    	break;
+
+	    vTaskDelay(10 / portTICK_PERIOD_MS);
+	}
+ 
+//	printf("Simulate deep sleep\n");
+// 	vTaskDelay(1000 * wakeup_time_sec / portTICK_PERIOD_MS);
+  
+	printf("Entering deep sleep\n");
+    	gettimeofday(&sleep_enter_time, NULL);
+	esp_deep_sleep_start();
+
+	Main_Loop1 = MAIN_LOOP1_INIT;
+	New_Loop2 = MAIN_LOOP2_INIT;
+    }
+
+}
+

mercurial