From: jweigele Date: Thu, 13 Jul 2023 17:30:02 +0000 (-0700) Subject: Report both pm25 *and* pm10, and average temperature across time X-Git-Url: http://git.hexthepla.net/?a=commitdiff_plain;h=525c1abb630d67e10b23dba7e140bfcc6e7780e3;p=esp32projects Report both pm25 *and* pm10, and average temperature across time * add pm10 as a discrete value, and create an overall struct to hold both data types (pmbuffer) and average * make the temp list an actual ring buffer too, for use in flattening/averaging out temperature values like is currently done on pico * memmem for searching a value worked better, and the strstr variant was buggy for whatever reason --- diff --git a/aqi/main/aqi.c b/aqi/main/aqi.c index 6c28780..27d0cf6 100644 --- a/aqi/main/aqi.c +++ b/aqi/main/aqi.c @@ -34,14 +34,26 @@ typedef struct zdo_info_ctx_s { esp_zb_ieee_addr_t long_addr; } zdo_info_user_ctx_t; -static short temp_list[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +typedef struct aqi_data_s { + float pm10; + float pm25; +} aqi_data_t; + +static int tempbuffer[TEMPBUFFERSIZE]; +static int tempbufferindex = 0; + +//= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static const char *TAG = "aqi"; static OW ow; static bool zig_connected = false; -static uint16_t cur_pm25 = 0; +static aqi_data_t cur_pm = { + .pm10 = 0, + .pm25 = 0 +}; -static float pm25buffer[PM25BUFFERSIZE]; -static int pm25bufferindex = 0; +static aqi_data_t pmbuffer[PMBUFFERSIZE]; +static int pmbufferindex = 0; /********************* Define functions **************************/ static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) @@ -64,35 +76,93 @@ bool init_temp(){ return err; } +void print_pmdata(aqi_data_t data){ + ESP_LOGI(TAG, "pmdata pm10: %f, pm25: %f", data.pm10, data.pm25); +} // add new value to ringbuffer and return average of all, downcast to uint16_t -uint16_t new_pm25_average(float new_val){ - pm25buffer[pm25bufferindex++] = new_val; +aqi_data_t new_pm_average(aqi_data_t new_val){ + pmbuffer[pmbufferindex++] = new_val; + aqi_data_t retval = { + .pm10 = 0, + .pm25 = 0, + }; // rollover - if (pm25bufferindex == PM25BUFFERSIZE){ - pm25bufferindex = 0; + if (pmbufferindex == PMBUFFERSIZE){ + pmbufferindex = 0; } - float total = 0; + float pm10_total = 0; + float pm25_total = 0; int count = 0; - for (int i = 0; i < PM25BUFFERSIZE; i++ ){ + for (int i = 0; i < PMBUFFERSIZE; i++ ){ // ignore initial values - if ( pm25buffer[i] != -1.0 ){ - total += pm25buffer[i]; + if ( pmbuffer[i].pm10 != -1.0 ){ + pm10_total += pmbuffer[i].pm10; + } + if ( pmbuffer[i].pm25 != -1.0 ){ + pm25_total += pmbuffer[i].pm25; + // assuming both fields have the same "valid" count + count++; + } + + + } + // multiply * 100 for greater precision once sent + if (count != 0 ){ + retval.pm10 = (pm10_total*100/count); + retval.pm25 = (pm25_total*100/count); + } + + ESP_LOGI(TAG, "returning average pmdata to send: pm10 %f pm25 %f", retval.pm10, retval.pm25); + return retval; + +} + +int new_temp_average(short new_val){ + tempbuffer[tempbufferindex++] = new_val; + // rollover + if (tempbufferindex == TEMPBUFFERSIZE){ + tempbufferindex = 0; + } + + int total = 0; + int count = 0; + for (int i = 0; i < TEMPBUFFERSIZE; i++ ){ + // ignore invalid + if ( tempbuffer[i] != 0x8000 ){ + total += tempbuffer[i]; count++; } } - uint16_t retval = (uint16_t)(total*100/count); - ESP_LOGI(TAG, "returning average pm25 to send: %d", retval); + int retval; + // we should only get here if all values are added are 0x8000 (aka no read) + // but it's still important to fill out just so we don't crash + // mainly, when ds18b20 is not connected or sensing properly + if (count == 0){ + retval = 0x8000; + } else { + retval = (int)(total/count); + } + ESP_LOGI(TAG, "returning average temp to send: %d", retval); return retval; } -void init_pm25buffer(){ - for (int i = 0; i < PM25BUFFERSIZE; i++){ - pm25buffer[i] = -1.0; + +void init_pmbuffer(){ + for (int i = 0; i < PMBUFFERSIZE; i++){ + pmbuffer[i].pm10 = -1.0; + pmbuffer[i].pm25 = -1.0; } } + +void init_tempbuffer(){ + for (int i = 0; i < TEMPBUFFERSIZE; i++){ + tempbuffer[i] = 0x8000; + } +} + void init_uart(void) { const uart_config_t uart_config = { .baud_rate = 9600, @@ -154,7 +224,7 @@ static short get_temp(){ } } -static void send_report(uint8_t report_type){ +static void send_report(int report_cluster, int report_attribute){ ESP_LOGI(TAG, "sending report here"); esp_zb_zcl_report_attr_cmd_t des; @@ -169,23 +239,11 @@ static void send_report(uint8_t report_type){ //ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; //des.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; - if (report_type == TEMP_REPORT){ - des.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; - des.attributeID = ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID; - } else if (report_type == PM25_REPORT){ - des.clusterID = PM25CLUSTER; - des.attributeID = PM25MEASURED; - } else { - ESP_LOGW(TAG, "undefined report type, just returning"); - return; - } + des.clusterID = report_cluster; + des.attributeID = report_attribute; des.cluster_role=ESP_ZB_ZCL_CLUSTER_SERVER_ROLE; ESP_ERROR_CHECK(esp_zb_zcl_report_attr_cmd_req(&des)); - if (report_type == TEMP_REPORT){ - ESP_LOGI(TAG, "sent temp report"); - } else if (report_type == PM25_REPORT){ - ESP_LOGI(TAG, "sent pm25 report"); - } + ESP_LOGI(TAG, "sent report"); } @@ -201,83 +259,114 @@ bool verify_checksum(uint8_t* data){ return data[30] == check_high && data[31] == check_low; } -float get_average_pm25(uint8_t* data, int data_size){ - int total = 0; +aqi_data_t get_average_pm25(uint8_t* data, int data_size){ + int pm10_total = 0; + int pm25_total = 0; int count = 0; + aqi_data_t retval = { + .pm10 = -1.0, + .pm25 = -1.0, + }; + //ESP_LOG_BUFFER_HEXDUMP(TAG, data, data_size, ESP_LOG_INFO); char search_prefix[2] = {0x42, 0x4d}; - uint8_t* first_entry = (uint8_t*)strstr((char*)data, search_prefix); + uint8_t* first_entry = (uint8_t*)memmem((void*)data, data_size, search_prefix, 2); int offset = first_entry - data; + if (first_entry == NULL){ + ESP_LOGW(TAG, "invalid first entry, returning invalid data"); + return retval; + } ESP_LOGI(TAG, "first entry found at %p (offset %d)", (uint8_t*)first_entry, offset); uint8_t* cur_entry = first_entry; - int pm25; + int pm10, pm25; while (cur_entry + 32 < data + data_size){ //printf("doing one iteration\n"); //ESP_LOG_BUFFER_HEXDUMP(TAG, cur_entry, 32, ESP_LOG_INFO); if (verify_checksum(cur_entry)){ pm25 = (data[12] << 8) + (data[13]); + pm10 = (data[10] << 8) + (data[11]); //ESP_LOGI(TAG, "pm25 val: %d", pm25); count++; - total += pm25; + pm25_total += pm25; + pm10_total += pm10; } else { ESP_LOGW(TAG, "checksum failed for uart, skipping entry"); } cur_entry += 32; } - if (count == 0){ - return 0.0; - } else { - return ((float)total)/count; + if (count != 0){ + retval.pm10 = ((float)pm10_total)/count; + retval.pm25 = ((float)pm25_total)/count; } + print_pmdata(retval); + return retval; } + static void monitoring_task(void* discard) { bool tempsetup = init_temp(); //bool tempsetup = true; uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE+1); + short temp_avg = 0x8000; int length = 0; for(;;){ ESP_LOGI(TAG, "free heap: %"PRIu32, esp_get_free_heap_size()); //temp_list[0] += 100; - if (tempsetup){ - temp_list[0] = get_temp(); - } else { - ESP_LOGI(TAG, "temp not setup, skipping update"); - } - ESP_LOGI(TAG, "current: %d", temp_list[0]); if (zig_connected == true){ + + // 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, 100); 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); - float cur_avg = get_average_pm25(data, rxBytes); - ESP_LOGI(TAG, "average from last 10 seconds pm25: %f", cur_avg); + aqi_data_t cur_avg = get_average_pm25(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); + ESP_LOGI(TAG, "setting attrib value and sending, I guess"); - cur_pm25 = new_pm25_average(cur_avg); + cur_pm = new_pm_average(cur_avg); + uint16_t set_pm10 = (uint16_t)cur_pm.pm10; + uint16_t set_pm25 = (uint16_t)cur_pm.pm25; + esp_zb_zcl_set_attribute_val(HA_ESP_TEMP_ENDPOINT, PM25CLUSTER, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, + PM10MEASURED, &set_pm10, false); esp_zb_zcl_set_attribute_val(HA_ESP_TEMP_ENDPOINT, PM25CLUSTER, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, - PM25MEASURED, &cur_pm25, false); - send_report(PM25_REPORT); + PM25MEASURED, &set_pm25, false); + + send_report(PM10CLUSTER, PM10MEASURED); + send_report(PM25CLUSTER, PM25MEASURED); } else { ESP_LOGI(TAG, "Nothing seen from uart"); } - ESP_LOGI(TAG, "setting attrib value and sending, I guess"); - esp_zb_zcl_set_attribute_val(HA_ESP_TEMP_ENDPOINT, ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, - ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, &temp_list[0], false); - send_report(TEMP_REPORT); - // reg sleep for 1 second to clear out backlog, then light sleep - //ESP_LOGI(TAG, "light sleep starts in a second"); - vTaskDelay( pdMS_TO_TICKS(10000) ); - //ESP_ERROR_CHECK(esp_sleep_enable_timer_wakeup(9000000)); - //ESP_ERROR_CHECK(esp_light_sleep_start()); + // Temperature fetch/calculation/report + if (tempsetup){ + temp_avg = new_temp_average(get_temp()); + } else { + ESP_LOGI(TAG, "temp not setup, skipping update"); + } + ESP_LOGI(TAG, "current: %d", temp_avg); + + ESP_LOGI(TAG, "setting attrib value and sending, I guess"); + esp_zb_zcl_set_attribute_val(HA_ESP_TEMP_ENDPOINT, ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, + ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, &temp_avg, false); + send_report(ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID); - // now we've woken up I guess? - ESP_LOGI(TAG, "I have worken up after sleep, looping around"); + + // loop around and sleep + // + // reg sleep for 1 second to clear out backlog, then light sleep + //ESP_LOGI(TAG, "light sleep starts in a second"); + vTaskDelay( pdMS_TO_TICKS(10000) ); + //ESP_ERROR_CHECK(esp_sleep_enable_timer_wakeup(9000000)); + //ESP_ERROR_CHECK(esp_light_sleep_start()); + + // now we've woken up I guess? + ESP_LOGI(TAG, "I have worken up after sleep, looping around"); } else { ESP_LOGI(TAG, "zig not connected, skipping temp report (and staying awake)"); @@ -291,11 +380,6 @@ static void monitoring_task(void* discard) //esp_zb_scheduler_alarm((esp_zb_callback_t)monitoring_task, parm, 10000); } -static void esp_zb_read_resp_cb(esp_zb_zcl_status_t status, uint16_t cluster_id, uint16_t attr_id, esp_zb_zcl_attr_type_t attr_type, void *value) -{ - ESP_LOGI(TAG, "Switch got read attribute response with status:%d,cluster_id:0x%x,attr_id:0x%x,value:%d,attr_type:0x%x", status, cluster_id, attr_id, *(uint8_t *)value, attr_type); -} - void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) { uint32_t *p_sg_p = signal_struct->p_app_signal; @@ -372,9 +456,6 @@ static void esp_zb_task(void *pvParameters) uint8_t power_source[] = {0x01}; - for (int i= 0; i < 10; i++ ){ - temp_list[i] = 0x8000; - } /* basic cluster create with fully customized */ esp_zb_attribute_list_t *esp_zb_basic_cluster = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_BASIC); esp_zb_basic_cluster_add_attr(esp_zb_basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_POWER_SOURCE_ID, power_source); @@ -400,22 +481,23 @@ static void esp_zb_task(void *pvParameters) short min_temp = -5500; short max_temp = 12500; + uint16_t initial_pm10 = 0; + uint16_t initial_pm25 = 0; // temperature cluster esp_zb_attribute_list_t *esp_zb_temp_cluster = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT); - esp_zb_temperature_meas_cluster_add_attr(esp_zb_temp_cluster, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, &temp_list[0]); + esp_zb_temperature_meas_cluster_add_attr(esp_zb_temp_cluster, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, &tempbuffer[0]); esp_zb_temperature_meas_cluster_add_attr(esp_zb_temp_cluster, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MIN_VALUE_ID, &min_temp); esp_zb_temperature_meas_cluster_add_attr(esp_zb_temp_cluster, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MAX_VALUE_ID, &max_temp); esp_zb_attribute_list_t *esp_zb_pm25_cluster = esp_zb_zcl_attr_list_create(PM25CLUSTER); - ESP_ERROR_CHECK(esp_zb_custom_cluster_add_custom_attr(esp_zb_pm25_cluster, PM25MEASURED, ESP_ZB_ZCL_ATTR_TYPE_U16, ESP_ZB_ZCL_ATTR_ACCESS_READ_ONLY | ESP_ZB_ZCL_ATTR_ACCESS_REPORTING, &cur_pm25)); + ESP_ERROR_CHECK(esp_zb_custom_cluster_add_custom_attr(esp_zb_pm25_cluster, PM25MEASURED, ESP_ZB_ZCL_ATTR_TYPE_U16, ESP_ZB_ZCL_ATTR_ACCESS_READ_ONLY | ESP_ZB_ZCL_ATTR_ACCESS_REPORTING, &initial_pm25)); + ESP_ERROR_CHECK(esp_zb_custom_cluster_add_custom_attr(esp_zb_pm25_cluster, PM10MEASURED, ESP_ZB_ZCL_ATTR_TYPE_U16, ESP_ZB_ZCL_ATTR_ACCESS_READ_ONLY | ESP_ZB_ZCL_ATTR_ACCESS_REPORTING, &initial_pm10)); + /* create cluster lists for this endpoint */ esp_zb_cluster_list_t *esp_zb_cluster_list = esp_zb_zcl_cluster_list_create(); esp_zb_cluster_list_add_basic_cluster(esp_zb_cluster_list, esp_zb_basic_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); - - // add all the clusters to our main cluster list - //esp_zb_cluster_list_update_basic_cluster(esp_zb_cluster_list, esp_zb_basic_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_identify_cluster(esp_zb_cluster_list, esp_zb_identify_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_temperature_meas_cluster(esp_zb_cluster_list, esp_zb_temp_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); ESP_ERROR_CHECK(esp_zb_cluster_list_add_custom_cluster(esp_zb_cluster_list, esp_zb_pm25_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE)); @@ -426,16 +508,6 @@ static void esp_zb_task(void *pvParameters) esp_zb_ep_list_add_ep(esp_zb_ep_list, esp_zb_cluster_list, HA_ESP_TEMP_ENDPOINT, ESP_ZB_AF_HA_PROFILE_ID, ESP_ZB_HA_ON_OFF_OUTPUT_DEVICE_ID); esp_zb_device_register(esp_zb_ep_list); - // set first pm25 val - /*esp_zb_zcl_set_attribute_val(HA_ESP_TEMP_ENDPOINT, PM25CLUSTER, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, - PM25MEASURED, &cur_pm25, false);*/ - - // set first temp value - esp_zb_zcl_set_attribute_val(HA_ESP_TEMP_ENDPOINT, ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, - ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, &temp_list[0], false); - - //esp_zb_device_add_set_attr_value_cb(attr_cb); - esp_zb_add_read_attr_resp_cb(HA_ESP_TEMP_ENDPOINT, esp_zb_read_resp_cb); ESP_ERROR_CHECK(esp_zb_start(false)); //ESP_ERROR_CHECK(esp_zb_secur_ic_set(ESP_ZB_IC_TYPE_128, (uint8_t*)curic)); @@ -452,7 +524,8 @@ void app_main(void) }; ESP_ERROR_CHECK(nvs_flash_init()); init_uart(); - init_pm25buffer(); + init_pmbuffer(); + init_tempbuffer(); /* load Zigbee light_bulb platform config to initialization */ ESP_ERROR_CHECK(esp_zb_platform_config(&config)); /* hardware related and device init */ diff --git a/aqi/main/aqi.h b/aqi/main/aqi.h index 62b336d..6c04ac7 100644 --- a/aqi/main/aqi.h +++ b/aqi/main/aqi.h @@ -27,14 +27,17 @@ #define UART_RX_GPIO (GPIO_NUM_26) #define UART_TX_GPIO (GPIO_NUM_27) #define PM25CLUSTER 0xFFFE +#define PM10CLUSTER 0xFFFE //0x042A -#define PM25MEASURED 0x0000 +#define PM10MEASURED 0x0000 +#define PM25MEASURED 0x0001 #define TEMP_REPORT 0x1 #define PM25_REPORT 0x2 -#define PM25BUFFERSIZE 6 +#define PMBUFFERSIZE 6 +#define TEMPBUFFERSIZE 10 static const int RX_BUF_SIZE = 1024;