From: jweigele Date: Tue, 19 Mar 2024 18:56:11 +0000 (-0700) Subject: rearrange uart code and add co2 monitoring (on same uart as aqi) plus cleanup more... X-Git-Url: http://git.hexthepla.net/?a=commitdiff_plain;h=e697fb26cd64bc83a50babf63d00f439ff33581c;p=esp32projects rearrange uart code and add co2 monitoring (on same uart as aqi) plus cleanup more zigbee --- diff --git a/aqi/main/Kconfig b/aqi/main/Kconfig index f75c64e..e4fbdd3 100644 --- a/aqi/main/Kconfig +++ b/aqi/main/Kconfig @@ -21,16 +21,45 @@ config LIGHT_SLEEP_ENABLED help Not very well tested yet -config UART_ENABLED - bool "Actually fetch and report the AQI over UART" - default y +config AQI_ENABLED + bool "Fetch and report AQI from UART?" + default n + help + Gets passed and used later for includes + +config AQI_TX_PIN + int "UART TX pin for AQI" + default 22 help Gets passed and used later for includes -config ZIG_ENABLED - bool "Are we using zigbee pairing and reporting" + +config AQI_RX_PIN + int "UART RX pin for AQI" + default 25 + help + Gets passed and used later for includes + +config CO2_ENABLED + bool "Fetch and report CO2 from UART?" default n help Gets passed and used later for includes + +config CO2_TX_PIN + int "UART TX pin for CO2" + default 22 + help + Gets passed and used later for includes + +config CO2_RX_PIN + int "UART RX pin for CO2" + default 25 + help + Gets passed and used later for includes + + + + config WIFI_ENABLED bool "Are we using wifi association and MQTT" default n diff --git a/aqi/main/aqi.c b/aqi/main/aqi.c index 22949ae..4cead69 100644 --- a/aqi/main/aqi.c +++ b/aqi/main/aqi.c @@ -41,11 +41,14 @@ typedef struct pwm_data_s { // overall report data structure, contains all the variables we'll parse and send to mqtt typedef struct report_data_s { -#ifdef CONFIG_UART_ENABLED +#ifdef CONFIG_AQI_ENABLED uint16_t pm10; uint16_t pm25; float aqi; #endif +#ifdef CONFIG_CO2_ENABLED + int16_t co2_ppm; +#endif #ifdef CONFIG_TEMP_ENABLED uint16_t temperature; #endif @@ -129,7 +132,7 @@ static uint8_t ot_mac[6]; static uint8_t otExt[8]; #endif #endif -#ifdef CONFIG_UART_ENABLED +#ifdef CONFIG_AQI_ENABLED static aqi_data_t cur_pm = { .pm10 = 0, .pm25 = 0 @@ -599,6 +602,8 @@ aqi_data_t new_pm_average(aqi_data_t new_val){ } +#ifdef CONFIG_AQI_ENABLED + void init_pmbuffer(){ for (int i = 0; i < PMBUFFERSIZE; i++){ pmbuffer[i].pm10 = -1.0; @@ -607,7 +612,25 @@ void init_pmbuffer(){ } // hardcoded given the aqi device we expect to find -void init_uart(void) { +void init_uart_aqi(void) { + const uart_config_t uart_config = { + .baud_rate = 9600, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_DEFAULT, + }; + // We won't use a buffer for sending data. + uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0); + uart_param_config(UART_NUM_1, &uart_config); + uart_set_pin(UART_NUM_1, CONFIG_AQI_TX_PIN, CONFIG_AQI_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); +} +#endif + +#ifdef CONFIG_CO2_ENABLED +// hardcoded given the co2 device we expect to find +void init_uart_co2(void) { const uart_config_t uart_config = { .baud_rate = 9600, .data_bits = UART_DATA_8_BITS, @@ -619,9 +642,78 @@ void init_uart(void) { // We won't use a buffer for sending data. uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0); uart_param_config(UART_NUM_1, &uart_config); - uart_set_pin(UART_NUM_1, UART_TX_GPIO, UART_RX_GPIO, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + uart_set_pin(UART_NUM_1, CONFIG_CO2_TX_PIN, CONFIG_CO2_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); +} + +bool co2_validate_checksum(uint8_t* data){ + uint8_t checksum = 0; + // yeah, this starts at 1 and skips the first byte + for (int i = 1; i < 8; i++ ){ + checksum += data[i]; + } + checksum = 0xff - checksum; + checksum += 1; + if (checksum == data[8] ){ + ESP_LOGI(TAG, "validated CO2 data checksum!\n"); + return true; + } else { + ESP_LOGE(TAG, "incorrect CO2 data checksum %02x != %02x\n", checksum, data[8]); + return false; + } +} + + +// returns a signed 16 bit int, which should be fine because the detector +// can never go above 10k anyway so we have like 20k headroom for the sign bit +int16_t co2_parse_sensor_data(uint8_t* data){ + // first validate checksum + if (co2_validate_checksum(data) ){ + // need to parse according to manufacturer formula + uint8_t high = data[2]; + uint8_t low = data[3]; + int16_t retval = high * 256 + low; + return retval; + } else { + return -1; + } + + +} + +void co2_send_fetch(void){ + // Write fetch command to co2 sensor + uint8_t fetch_cmd[] = CO2_FETCH; + uart_write_bytes(UART_NUM_1, (void*)fetch_cmd, 9); +} + +int16_t co2_read_sensor(void){ + // we should only get back 9 bytes, but give a bigger buffer for error detection of garbage + uint8_t* co2_data = (uint8_t*) malloc(128); + size_t length = 0; + co2_send_fetch(); + + ESP_ERROR_CHECK(uart_get_buffered_data_len(UART_NUM_1, (size_t*)&length)); + + // assuming we have queried the sensor, at this point we should expect 9 bytes + const int rxBytes = uart_read_bytes(UART_NUM_1, co2_data, length, 128); + if (rxBytes != 9 ){ + ESP_LOGE(TAG, "read wrong number of bytes back from UART %d, ignoring!\n", length); + free(co2_data); + return -1; + } + + + int16_t retval = co2_parse_sensor_data(co2_data); + free(co2_data); + return retval; + // we read the correct number of bytes in response, now need to parse and return + return co2_parse_sensor_data(co2_data); } + +#endif + + // this task sends identifying information over mqtt, sleeps an hour, then repeats // // if it doesn't go out for some reason it's just stored in a queue until it is fetched @@ -746,11 +838,17 @@ static void send_report_summary(report_data_t report_data){ cJSON *root; root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "location", CONFIG_LOCATION); -#ifdef CONFIG_UART_ENABLED +#ifdef CONFIG_AQI_ENABLED cJSON_AddNumberToObject(root, "pm10", ((float)report_data.pm10/100)); cJSON_AddNumberToObject(root, "pm25", ((float)report_data.pm25/100)); cJSON_AddNumberToObject(root, "aqi", report_data.aqi); #endif +#ifdef CONFIG_CO2_ENABLED + // -1 is what we use for errors, 0 seems to be the device just isn't yet init + if (report_data.co2_ppm != -1 && report_data.co2_ppm != 0){ + cJSON_AddNumberToObject(root, "co2", report_data.co2_ppm); + } +#endif // both set as we go through the normal loop, "nice to have" information on device operation cJSON_AddNumberToObject(root, "uptime", report_data.uptime); cJSON_AddNumberToObject(root, "internal", report_data.internal_temperature); @@ -1251,13 +1349,13 @@ short temp_avg = 0; init_temp(); #endif -#ifdef CONFIG_UART_ENABLED +#ifdef CONFIG_AQI_ENABLED uint16_t set_pm10; uint16_t set_pm25; float cur_aqi_pm25 = 0.0; float cur_aqi_pm10 = 0.0; int length = 0; - uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE+1); + uint8_t* aqi_data = (uint8_t*) malloc(RX_BUF_SIZE+1); #endif int64_t cur_uptime = 0; int64_t next_sleep_uptime = 0; @@ -1290,15 +1388,15 @@ short temp_avg = 0; xSemaphoreGive(report_data.mutex); -#ifdef CONFIG_UART_ENABLED +#ifdef CONFIG_AQI_ENABLED // aqi fetch/calculation/report ESP_ERROR_CHECK(uart_get_buffered_data_len(UART_NUM_1, (size_t*)&length)); - const int rxBytes = uart_read_bytes(UART_NUM_1, data, length, 1000); + const int rxBytes = uart_read_bytes(UART_NUM_1, aqi_data, length, 1000); if (rxBytes > 0) { - data[rxBytes] = 0; - //ESP_LOGI(TAG, "Read %d bytes: '%s'", rxBytes, data); - //ESP_LOG_BUFFER_HEXDUMP(TAG, data, rxBytes, ESP_LOG_INFO); - aqi_data_t cur_avg = get_average_pm25(data, rxBytes); + aqi_data[rxBytes] = 0; + //ESP_LOGI(TAG, "Read %d bytes: '%s'", rxBytes, aqi_data); + //ESP_LOG_BUFFER_HEXDUMP(TAG, aqi_data, rxBytes, ESP_LOG_INFO); + aqi_data_t cur_avg = get_average_pm25(aqi_data, rxBytes); ESP_LOGI(TAG, "average from last 10 seconds pm10: %f", cur_avg.pm10); ESP_LOGI(TAG, "average from last 10 seconds pm25: %f", cur_avg.pm25); @@ -1340,8 +1438,17 @@ short temp_avg = 0; light_driver_set_color_RGB(current_colors[0], current_colors[1], current_colors[2]); #endif -#endif // end UART section - +#endif // end AQI UART section +#ifdef CONFIG_CO2_ENABLED + int16_t co2_ppm = co2_read_sensor(); + if (co2_ppm != -1 ){ + xSemaphoreTake(report_data.mutex, portMAX_DELAY); + report_data.co2_ppm = co2_ppm; + xSemaphoreGive(report_data.mutex); + } + +#endif // end CO2 UART section + temp_avg = 0; #ifdef CONFIG_TEMP_ENABLED // Temperature fetch/calculation/report @@ -1405,8 +1512,8 @@ short temp_avg = 0; } // only called if we break out of the loop somehow (right now, never) -#ifdef CONFIG_UART_ENABLED - free(data); +#ifdef CONFIG_AQI_ENABLED + free(aqi_data); #endif } @@ -1640,10 +1747,13 @@ void app_main(void) light_driver_init(LIGHT_DEFAULT_OFF); adjust_color_lookup_brightness(AQI_INDICATOR_BRIGHTNESS); #endif -#ifdef CONFIG_UART_ENABLED - init_uart(); +#ifdef CONFIG_AQI_ENABLED + init_uart_aqi(); init_pmbuffer(); #endif +#ifdef CONFIG_CO2_ENABLED + init_uart_co2(); +#endif #ifdef CONFIG_TEMP_ENABLED init_tempbuffer(); #endif diff --git a/aqi/main/aqi.h b/aqi/main/aqi.h index 2070309..0f2c25d 100644 --- a/aqi/main/aqi.h +++ b/aqi/main/aqi.h @@ -1,10 +1,5 @@ #include "esp_timer.h" -#ifdef CONFIG_ZIG_ENABLED -#include "esp_zigbee_core.h" -#include "ha/esp_zigbee_ha_standard.h" -#endif - // just used for erasing partition, so we want // to include it regardless of whether zb // is enabled now. @@ -111,6 +106,13 @@ static const char *TAG = "aqi"; #define MAX_TEMP_VALUE 12500 #endif +#ifdef CONFIG_CO2_ENABLED +// 8 bytes + 1 checksum, defined by manufacturer +// we should expect 9 bytes in return +#define CO2_FETCH {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79} +#endif + + #define MAX_PM10 500 #define MAX_PM25 1000 @@ -122,30 +124,11 @@ static const char *TAG = "aqi"; #endif #define MAXDEVS 1 -#define UART_RX_GPIO (GPIO_NUM_22) -#define UART_TX_GPIO (GPIO_NUM_25) // ten second loop #define SLEEP_MS 10000 #define SQUELCH_MS 5000 - - -// zigbee cluster reporting stuff -#ifdef CONFIG_ZIG_ENABLED -#define PM25CLUSTER 0xFFFE -#define PM10CLUSTER 0xFFFE -//0x042A -#define PM10MEASURED 0x0000 -#define PM25MEASURED 0x0001 - -#define UPTIMECLUSTER 0xFFFD -#define UPTIMEID 0x0000 - -#endif - - - #define PMBUFFERSIZE 60 #define TEMPBUFFERSIZE 10 @@ -180,56 +163,3 @@ static void mqtt_reinit(); #define DS18B20_COPY_SCRATCHPAD 0x48 #define DS18B20_RECALL_EE 0xb8 #define DS18B20_READ_POWER_SUPPLY 0xb4 - - - -#ifdef CONFIG_ZIG_ENABLED -/* Zigbee configuration */ -#define INSTALLCODE_POLICY_ENABLE false /* enable the install code policy for security */ -#define ED_AGING_TIMEOUT ESP_ZB_ED_AGING_TIMEOUT_64MIN -// router or endpoint - - -// auto chooses the correct configuration based upon your menuconfig selection -#ifdef CONFIG_ZB_ZCZR -#define ZB_TYPE ESP_ZB_DEVICE_TYPE_ROUTER -#define MAX_CHILDREN 10 -#define ESP_ZB_ZED_CONFIG() \ - { \ - .esp_zb_role = ZB_TYPE, \ - .install_code_policy = INSTALLCODE_POLICY_ENABLE, \ - .nwk_cfg.zczr_cfg = { \ - .max_children = MAX_CHILDREN, \ - }, \ - } - -#elif CONFIG_ZB_ZED -#define ZB_TYPE ESP_ZB_DEVICE_TYPE_ED -#define ESP_ZB_ZED_CONFIG() \ - { \ - .esp_zb_role = ZB_TYPE, \ - .install_code_policy = INSTALLCODE_POLICY_ENABLE, \ - .nwk_cfg.zed_cfg = { \ - .ed_timeout = ED_AGING_TIMEOUT, \ - .keep_alive = ED_KEEP_ALIVE, \ - }, \ - } - -#endif - -#define ED_KEEP_ALIVE 3000 /* 3000 millisecond */ -#define HA_ESP_ENDPOINT 1 -#define ESP_ZB_PRIMARY_CHANNEL_MASK ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK /* Zigbee primary channel mask use in the example */ -//specifically search channel 15, which is what I'm on -//#define ESP_ZB_PRIMARY_CHANNEL_MASK 1l<<15 - -#define ESP_ZB_DEFAULT_RADIO_CONFIG() \ - { \ - .radio_mode = RADIO_MODE_NATIVE, \ - } - -#define ESP_ZB_DEFAULT_HOST_CONFIG() \ - { \ - .host_connection_mode = HOST_CONNECTION_MODE_NONE, \ - } -#endif