#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;
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];
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;
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
int length = 0;
#ifdef CONFIG_WIFI_ENABLED
esp_mqtt_client_handle_t queue_item;
-#endif
+#endif
for(;;){
#ifdef CONFIG_WIFI_ENABLED
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);
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 {
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);
/* 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;
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;
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));
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");
}
}
-// 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){
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);
-/*
- * 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"
#define PM10MEASURED 0x0000
#define PM25MEASURED 0x0001
-#define TEMP_REPORT 0x1
-#define PM25_REPORT 0x2
+#define UPTIMECLUSTER 0xFFFD
+#define UPTIMEID 0x0000
+
#endif
/* 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, \
}, \
}
+#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, \