Uptime reporting, bugfixes in zigbee erase, and optional router caps
authorjweigele <jweigele@local>
Mon, 17 Jul 2023 18:57:21 +0000 (11:57 -0700)
committerjweigele <jweigele@local>
Mon, 17 Jul 2023 18:57:21 +0000 (11:57 -0700)
aqi/main/aqi.c
aqi/main/aqi.h
aqi/sdkconfig

index 3a71a89b9b7b586242fac32ec4ba8c87bd5b5700..43c4514c5762fdd8cc00175593e3d1709fbbcbd3 100644 (file)
 #include "ha/esp_zigbee_ha_standard.h"
 #include "aqi.h"
 
-/**
- * @note Make sure set idf.py menuconfig in zigbee component as zigbee end device!
-*/
-#if !defined ZB_ED_ROLE
-#error Define ZB_ED_ROLE in idf.py menuconfig to compile light (End Device) source code.
-#endif
-
 typedef struct aqi_data_s {
     float pm10;
     float pm25;
@@ -33,6 +26,9 @@ typedef struct aqi_data_s {
 
 static QueueHandle_t event_queue;
 
+// for tracking and reporting device uptime
+static uint32_t device_uptime_seconds = 0;
+
 #ifdef CONFIG_TEMP_ENABLED
 
 static int tempbuffer[TEMPBUFFERSIZE];
@@ -78,6 +74,46 @@ static aqi_data_t pmbuffer[PMBUFFERSIZE];
 static int pmbufferindex = 0;
 
 
+// this function checks for signal on a designated "reset" pin,
+// then erases both data partitions, sets the LED to indicate success,
+// and reboots after a delay
+void erase_data_hook(void){
+    ESP_LOGI(TAG, "checking pin %d for erase status", CONFIG_GPIO_ERASE_PIN);
+    gpio_set_pull_mode(CONFIG_GPIO_ERASE_PIN, GPIO_PULLDOWN_ONLY);
+    gpio_set_direction(CONFIG_GPIO_ERASE_PIN, GPIO_MODE_INPUT);
+    int erase_status = gpio_get_level(CONFIG_GPIO_ERASE_PIN);
+    if (erase_status == 1){
+        // we gotta have high priority for our blinking
+#ifdef CONFIG_INDICATOR_ENABLED
+        xTaskCreate(blink_that_shit, "blink_that_shit", 2048, NULL, 9, NULL);
+#endif
+        ESP_LOGW(TAG, "would erase flash here!!!");
+        // we don't want to erase a factory partition since it also contains the apps
+        if (strcmp(CONFIG_NVS_WIFI_PARTITION, "factory") != 0){
+            ESP_LOGW(TAG, "erasing wifi partition at %s", CONFIG_NVS_WIFI_PARTITION);
+            ESP_ERROR_CHECK(nvs_flash_erase_partition(CONFIG_NVS_WIFI_PARTITION));
+        } else {
+            ESP_LOGW(TAG, "cannot erase wifi partition because it's the same as the app partition :(");
+            // make it blink worse
+            way_too_funky = true;
+        }
+
+#ifdef CONFIG_ZIG_ENABLED
+        // we call the zigbee code directly so this needs no check
+        ESP_LOGW(TAG, "erasing zigbee settings by calling function");
+        esp_zb_factory_reset();
+#endif
+        ESP_LOGI(TAG, "waiting 10 seconds then restarting");
+        // if you didn't have zigbee running, this takes care of the restart
+        vTaskDelay( pdMS_TO_TICKS(10000) );
+        esp_restart();
+    } else {
+        ESP_LOGI(TAG, "erase pin not set, continuing boot");
+    }
+}
+
+
+
 #ifdef CONFIG_ZIG_ENABLED
 typedef struct zdo_info_ctx_s {
     uint8_t endpoint;
@@ -276,6 +312,7 @@ static void send_report_wifi(uint16_t pm10, uint16_t pm25, float aqi, uint16_t t
     cJSON_AddNumberToObject(root, "pm10", ((float)pm10/100));
     cJSON_AddNumberToObject(root, "pm25", ((float)pm25/100));
     cJSON_AddNumberToObject(root, "aqi", aqi);
+    cJSON_AddNumberToObject(root, "uptime", device_uptime_seconds);
 #ifdef CONFIG_TEMP_ENABLED
     cJSON_AddNumberToObject(root, "temperature", ((float)temperature/100));
 #endif
@@ -391,7 +428,7 @@ static void monitoring_task(void* discard)
     int length = 0;
 #ifdef CONFIG_WIFI_ENABLED
     esp_mqtt_client_handle_t queue_item;
-#endif    
+#endif
     for(;;){
 
 #ifdef CONFIG_WIFI_ENABLED
@@ -404,6 +441,10 @@ static void monitoring_task(void* discard)
         ESP_LOGI(TAG, "free heap: %"PRIu32, esp_get_free_heap_size());
 
 
+        // first things first (function gives microseconds, just convert to seconds)
+        // 32 bits is enough for >50 years and zigbee stack gets mad if we use more bits
+        device_uptime_seconds = (uint32_t)(esp_timer_get_time()/1000000);
+
         // 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);
@@ -429,6 +470,10 @@ static void monitoring_task(void* discard)
                                 PM25MEASURED, &set_pm25, false);
                 send_report_zig(PM10CLUSTER, PM10MEASURED);
                 send_report_zig(PM25CLUSTER, PM25MEASURED);
+                // reasonable place to do it since this is the one universal measurement block for zigbee
+                esp_zb_zcl_set_attribute_val(HA_ESP_ENDPOINT, UPTIMECLUSTER, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,
+                                UPTIMEID, &device_uptime_seconds, false);                
+                send_report_zig(UPTIMECLUSTER, UPTIMEID);
             }
 #endif
         } else {
@@ -548,6 +593,12 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
 
 static void esp_zb_task(void *pvParameters)
 {
+    if (ZB_TYPE == ESP_ZB_DEVICE_TYPE_ROUTER){
+        ESP_LOGI(TAG, "entered esp_zb_task, zb_type is router");
+    } else {
+        ESP_LOGI(TAG, "entered esp_zb_task, zb_type is end device");
+    }
+
     /* initialize Zigbee stack with Zigbee end-device config */
     esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG();
     esp_zb_init(&zb_nwk_cfg);
@@ -568,6 +619,10 @@ static void esp_zb_task(void *pvParameters)
     /* identify cluster create with fully customized */
     esp_zb_attribute_list_t *esp_zb_identify_cluster = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY);
 
+
+    esp_zb_attribute_list_t *esp_zb_uptime_cluster = esp_zb_zcl_attr_list_create(UPTIMECLUSTER);
+    ESP_ERROR_CHECK(esp_zb_custom_cluster_add_custom_attr(esp_zb_uptime_cluster, UPTIMEID, ESP_ZB_ZCL_ATTR_TYPE_U32, ESP_ZB_ZCL_ATTR_ACCESS_READ_ONLY | ESP_ZB_ZCL_ATTR_ACCESS_REPORTING, &device_uptime_seconds));
+
     uint16_t initial_pm10 = 0;
     uint16_t initial_pm25 = 0;
 
@@ -575,12 +630,12 @@ static void esp_zb_task(void *pvParameters)
     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, &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);
     esp_zb_cluster_list_add_identify_cluster(esp_zb_cluster_list, esp_zb_identify_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
+
 #ifdef CONFIG_TEMP_ENABLED
     short min_temp = MIN_TEMP_VALUE;
     short max_temp = MAX_TEMP_VALUE;
@@ -593,7 +648,8 @@ static void esp_zb_task(void *pvParameters)
     
     esp_zb_cluster_list_add_temperature_meas_cluster(esp_zb_cluster_list, esp_zb_temp_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
 #endif
-    
+
+    ESP_ERROR_CHECK(esp_zb_cluster_list_add_custom_cluster(esp_zb_cluster_list, esp_zb_uptime_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));
 
 
@@ -603,6 +659,7 @@ static void esp_zb_task(void *pvParameters)
     esp_zb_device_register(esp_zb_ep_list);
 
     ESP_ERROR_CHECK(esp_zb_start(false));
+    erase_data_hook();
 
     //ESP_ERROR_CHECK(esp_zb_secur_ic_set(ESP_ZB_IC_TYPE_128, (uint8_t*)curic));
     ESP_LOGI(TAG, "main loop begin");
@@ -774,40 +831,6 @@ void blink_that_shit_funkay(void* discard){
     }    
 }
 
-// this function checks for signal on a designated "reset" pin,
-// then erases both data partitions, sets the LED to indicate success,
-// and reboots after a delay
-void erase_data_hook(void){
-    gpio_set_pull_mode(CONFIG_GPIO_ERASE_PIN, GPIO_PULLDOWN_ONLY);
-    gpio_set_direction(CONFIG_GPIO_ERASE_PIN, GPIO_MODE_INPUT);
-    int erase_status = gpio_get_level(CONFIG_GPIO_ERASE_PIN);
-    if (erase_status == 1){
-        // we gotta have high priority for our blinking
-        xTaskCreate(blink_that_shit, "blink_that_shit", 2048, NULL, 9, NULL);
-        ESP_LOGW(TAG, "would erase flash here!!!");
-        // we don't want to erase a factory partition since it also contains the apps
-        if (strcmp(CONFIG_NVS_WIFI_PARTITION, "factory") != 0){
-            ESP_LOGW(TAG, "erasing wifi partition at %s", CONFIG_NVS_WIFI_PARTITION);
-            ESP_ERROR_CHECK(nvs_flash_erase_partition(CONFIG_NVS_WIFI_PARTITION));
-        } else {
-            ESP_LOGE(TAG, "cannot erase wifi partition because it's the same as the app partition :(");
-            // make it blink worse
-            way_too_funky = true;
-        }
-
-#ifdef CONFIG_ZIG_ENABLED
-        // we call the zigbee code directly so this needs no check
-        esp_zb_factory_reset();
-#endif
-        // if you didn't have zigbee running, this takes care of the restart
-        vTaskDelay( pdMS_TO_TICKS(10000) );
-        esp_restart();
-    } else {
-        ESP_LOGI(TAG, "erase pin not set, continuing boot");
-    }
-}
-
-
 #ifdef CONFIG_INDICATOR_ENABLED
 // simple and cheap, just nerfs the total color values in the lookup table once at boot
 void adjust_color_lookup_brightness(float multiplier){
@@ -829,9 +852,13 @@ void app_main(void)
     light_driver_init(LIGHT_DEFAULT_OFF);
 #endif
 
-
+// if it's zigbee enabled, we need to actually start zb
+// before calling this (added directly in task later)
+#ifndef CONFIG_ZIG_ENABLED
+    ESP_LOGI(TAG, "no zigbee, calling erase hook early");
     // check for erasing data partitions
     erase_data_hook();
+#endif
 
 #ifdef CONFIG_INDICATOR_ENABLED
     adjust_color_lookup_brightness(AQI_INDICATOR_BRIGHTNESS);
index 44c1db8d9457e69555871f1c84c072369bb70c46..9f062322758332037fed7ba0c5fc280c25d66433 100644 (file)
@@ -1,16 +1,4 @@
-/*
- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: CC0-1.0
- *
- * Zigbee HA_color_dimmable_light Example
- *
- * This example code is in the Public Domain (or CC0 licensed, at your option.)
- *
- * Unless required by applicable law or agreed to in writing, this
- * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- * CONDITIONS OF ANY KIND, either express or implied.
- */
+#include "esp_timer.h"
 
 #ifdef CONFIG_ZIG_ENABLED
 #include "esp_zigbee_core.h"
@@ -84,8 +72,9 @@ static const char *TAG = "aqi";
 #define PM10MEASURED 0x0000
 #define PM25MEASURED 0x0001
 
-#define TEMP_REPORT 0x1
-#define PM25_REPORT 0x2
+#define UPTIMECLUSTER 0xFFFD
+#define UPTIMEID    0x0000
+
 #endif
 
 
@@ -131,15 +120,27 @@ static void mqtt_reinit();
 /* Zigbee configuration */
 #define INSTALLCODE_POLICY_ENABLE       false    /* enable the install code policy for security */
 #define ED_AGING_TIMEOUT                ESP_ZB_ED_AGING_TIMEOUT_64MIN
-#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
+// 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_ED
+#define ZB_TYPE                         ESP_ZB_DEVICE_TYPE_ED
 #define ESP_ZB_ZED_CONFIG()                                         \
     {                                                               \
-        .esp_zb_role = ESP_ZB_DEVICE_TYPE_ED,                       \
+        .esp_zb_role = ZB_TYPE,                                     \
         .install_code_policy = INSTALLCODE_POLICY_ENABLE,           \
         .nwk_cfg.zed_cfg = {                                        \
             .ed_timeout = ED_AGING_TIMEOUT,                         \
@@ -147,6 +148,14 @@ static void mqtt_reinit();
         },                                                          \
     }
 
+#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,                        \
index 789b7468a1ba681b96a34c61b87e52ea3d4ac822..c66ebfdce13ff049a07b24cb131206ee8e07125f 100644 (file)
@@ -1637,7 +1637,7 @@ CONFIG_ZIG_ENABLED=y
 # CONFIG_INDICATOR_ENABLED is not set
 CONFIG_BROKER_URL="mqtts://esp32:sensorauth@rabbitmq.hexthepla.net"
 CONFIG_LOCATION="ESP32 babyyy"
-CONFIG_GPIO_ERASE_PIN=14
+CONFIG_GPIO_ERASE_PIN=25
 # end of AQI Program Configuration
 
 #
@@ -1669,8 +1669,8 @@ CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
 # Zigbee
 #
 CONFIG_ZB_ENABLED=y
-# CONFIG_ZB_ZCZR is not set
-CONFIG_ZB_ZED=y
+CONFIG_ZB_ZCZR=y
+# CONFIG_ZB_ZED is not set
 # CONFIG_ZB_RCP is not set
 CONFIG_ZB_RADIO_NATIVE=y
 # CONFIG_ZB_RADIO_MACSPLIT_UART is not set