diff --git a/esp32/cloudsmets/main/cs_app.c b/esp32/cloudsmets/main/cs_app.c index e33208b..4bc3efd 100644 --- a/esp32/cloudsmets/main/cs_app.c +++ b/esp32/cloudsmets/main/cs_app.c @@ -20,6 +20,7 @@ #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #include "esp_log.h" #include "esp_event.h" +#include "esp_timer.h" #include "driver/gpio.h" // #include "ft_err.h" @@ -34,16 +35,32 @@ #include "cs_time.h" #include "cs_zigbee.h" #include "cs_flash.h" +#include "cs_app.h" /* Task Configuration */ #define CS_APP_TASK_QUEUE_SIZE CONFIG_CS_TASK_QUEUE_SIZE #define CS_APP_TASK_PRIORITY_DEFAULT CONFIG_CS_TASK_PRIORITY_DEFAULT #define CS_APP_TASK_STACK_SIZE CONFIG_CS_TASK_STACK_SIZE -#define TLSR8258_POWER GPIO_NUM_0 +// TODO: Should be common. +#define BLUE_LED GPIO_NUM_3 +#define TLSR8258_POWER GPIO_NUM_0 +#define CLEAR_KEY GPIO_NUM_2 -static const char *cs_app_task = "App"; -#define TAG cs_app_task +/* Times. */ +#define FIRST_JAN_2023 1672531200 +#define FACTORY_RESET_PRESS 10 +#define ZIGBEE_RESET_PRESS 4 +#define FACTORY_RESET_TIMER_WAIT ((uint64_t)1000 * 1000 * 2) + +#define TAG cs_app_task_name + +esp_timer_handle_t factory_reset_timer_handle = NULL; + +ESP_EVENT_DEFINE_BASE(CS_APP_EVENT); + +static esp_event_loop_handle_t app_event_loop_handle = NULL; +static esp_event_loop_handle_t zigbee_event_loop_handle = NULL; union cs_create_parms_t { cs_flash_create_parms_t flash; @@ -88,6 +105,133 @@ static void esp32_info() "Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size()); } +/** + * Note that the line is pull DOWN by a key press. + * We send ourselves an event to avoid any processing that might not be + * possible from inside an interrupt handler. +*/ +static void intr_handler(void *arg) +{ + time_t pressed = 0; + time_t released = 0; + int level; + + level = gpio_get_level(CLEAR_KEY); + if (level) + { + /* Rising line; key has been released. */ + released = time(NULL); + released = released - pressed; + if (released > FIRST_JAN_2023) + { + /** + * Time has been set whilst we were pressing the key so we cannot + * tell how long it was held for; have to just ignore it. + */ + } + else if (released > FACTORY_RESET_PRESS) + { + /** + * Factory resetting the ESP32c3 will happen on a timer to allow + * the tlsr8258 to be reset first. + */ + ESP_ERROR_CHECK(esp_event_isr_post_to( + app_event_loop_handle, + CS_APP_EVENT, + CS_APP_EVENT_FACTORY_RESET, + NULL, + 0, + NULL)); + } + else if (released > ZIGBEE_RESET_PRESS) + { + ESP_ERROR_CHECK(esp_event_isr_post_to( + app_event_loop_handle, + CS_APP_EVENT, + CS_APP_EVENT_FACTORY_RESET, + NULL, + 0, + NULL)); + } + } + else + { + /* Falling line; key has been pressed. */ + pressed = time(NULL); + released = pressed; + } +} + +static void init_gpio(void) +{ + /* Configure the LED GPIO lines used by T-ZigBee. */ + gpio_config_t gpio_config_data = { + .pin_bit_mask = (1 << BLUE_LED) | (1 << TLSR8258_POWER), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE + }; + ESP_ERROR_CHECK(gpio_config(&gpio_config_data)); + + /* Configure the input key GPIO line used by T-ZigBee. */ + gpio_config_data.pin_bit_mask = (1 << CLEAR_KEY); + gpio_config_data.mode = GPIO_MODE_INPUT; + gpio_config_data.pull_up_en = GPIO_PULLUP_ENABLE; + gpio_config_data.pull_down_en = GPIO_PULLDOWN_DISABLE; + gpio_config_data.intr_type = GPIO_INTR_ANYEDGE; + ESP_ERROR_CHECK(gpio_config(&gpio_config_data)); + ESP_ERROR_CHECK(gpio_isr_register(intr_handler, NULL, ESP_INTR_FLAG_LEVEL1, NULL)); + ESP_ERROR_CHECK(gpio_intr_enable(CLEAR_KEY)); +} + +static void factory_reset_timer_cb(void *arg) +{ + cs_cfg_factory_reset(); + esp_restart(); +} + +static void event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == CS_APP_EVENT) + { + switch (event_id) + { + case CS_APP_EVENT_FACTORY_RESET: + ESP_ERROR_CHECK(esp_event_post_to( + zigbee_event_loop_handle, + CS_ZIGBEE_EVENT, + CS_ZIGBEE_EVENT_FACTORY_RESET, + NULL, + 0, + 10)); + + /* Delay the ESP32c3 reset to allow ZigBee time to reset first. */ + ESP_ERROR_CHECK(esp_timer_start_once(factory_reset_timer_handle, FACTORY_RESET_TIMER_WAIT)); + break; + + case CS_APP_EVENT_ZIGBEE_RESET: + ESP_ERROR_CHECK(esp_event_post_to( + zigbee_event_loop_handle, + CS_ZIGBEE_EVENT, + CS_ZIGBEE_EVENT_FACTORY_RESET, + NULL, + 0, + 10)); + break; + + default: + ESP_LOGE(TAG, "Unexpected flash event: %d", event_id); + break; + } + } + else + { + ESP_LOGE(TAG, "Unexpected event: %s, %d", event_base, event_id); + } +} + static void start_of_day() { /* @@ -115,7 +259,6 @@ void app_main(void) static esp_event_loop_handle_t web_event_loop_handle = NULL; static esp_event_loop_handle_t ota_event_loop_handle = NULL; static esp_event_loop_handle_t mqtt_event_loop_handle = NULL; - static esp_event_loop_handle_t zigbee_event_loop_handle = NULL; // TODO: Remove this. esp_log_level_set(TAG, ESP_LOG_VERBOSE); @@ -123,10 +266,21 @@ void app_main(void) /* * Perform start-of-day checking. */ - // TODO: Should be cs_cfg_init(); + // TODO: Should be cs_cfg_init(); Also very messy having lots of stuff here! cs_cfg_init(); + init_gpio(); start_of_day(); + /** + * Create various timers. + */ + esp_timer_create_args_t timer_create_args = + { + .callback = factory_reset_timer_cb, + .name = "F.Reset" + }; + ESP_ERROR_CHECK(esp_timer_create(&timer_create_args, &factory_reset_timer_handle)); + /** * Instead of using FreeRTOS tasks and queues, CloudSMETS uses the ESP-IDF * Event Loop Library. Each event loop is instantiated as a FreeRTOS task @@ -198,6 +352,17 @@ void app_main(void) create_parms.wifi.flash_event_loop_handle = flash_event_loop_handle; cs_wifi_task(&create_parms.wifi); + /** + * Listen for events targetted to this app. + */ + ESP_ERROR_CHECK(esp_event_handler_instance_register_with( + app_event_loop_handle, + CS_APP_EVENT, + ESP_EVENT_ANY_ID, + event_handler, + NULL, + NULL)); + // TODO: remove this which was added for testing. #define NOW 1703592595 struct timeval now = { .tv_sec = NOW }; diff --git a/esp32/cloudsmets/main/cs_app.h b/esp32/cloudsmets/main/cs_app.h new file mode 100644 index 0000000..7846e89 --- /dev/null +++ b/esp32/cloudsmets/main/cs_app.h @@ -0,0 +1,6 @@ +typedef enum { + CS_APP_EVENT_FACTORY_RESET, /*!< Reset everything! */ + CS_APP_EVENT_ZIGBEE_RESET, /*!< Reset ZigBee (new join). */ +} cs_app_event_t; + +ESP_EVENT_DECLARE_BASE(CS_APP_EVENT); diff --git a/esp32/cloudsmets/main/cs_cfg.c b/esp32/cloudsmets/main/cs_cfg.c index a1eff0b..96a9929 100644 --- a/esp32/cloudsmets/main/cs_cfg.c +++ b/esp32/cloudsmets/main/cs_cfg.c @@ -370,6 +370,15 @@ static void cs_cfg_default(void) #endif } +/** + * Wipe all configuration from the NVS. We expect an immediate reboot after + * this happens. +*/ +void cs_cfg_factory_reset(void) +{ + nvs_flash_erase(); +} + void cs_cfg_init(void) { // TODO: Remove this. diff --git a/esp32/cloudsmets/main/cs_cfg.h b/esp32/cloudsmets/main/cs_cfg.h index 1e5d6e8..4798f33 100644 --- a/esp32/cloudsmets/main/cs_cfg.h +++ b/esp32/cloudsmets/main/cs_cfg.h @@ -129,3 +129,4 @@ extern void cs_cfg_write_uint8(const char *ns, const char *key, uint8_t value); extern void cs_cfg_write_uint16(const char *ns, const char *key, uint16_t value); extern void cs_cfg_write_uint32(const char *ns, const char *key, uint32_t value); extern void cs_cfg_write_str(const char *ns, const char *key, const char *value); +extern void cs_cfg_factory_reset(void); \ No newline at end of file diff --git a/esp32/cloudsmets/main/cs_flash.c b/esp32/cloudsmets/main/cs_flash.c index 7f58bbb..25b67d6 100644 --- a/esp32/cloudsmets/main/cs_flash.c +++ b/esp32/cloudsmets/main/cs_flash.c @@ -141,7 +141,6 @@ void update_flash_status(uint8_t status) * - Triple flash, also connected to Azure IotHub * - Solid blue, also receiving and relaying ZigBee messages. */ - static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { diff --git a/esp32/cloudsmets/main/cs_mqtt.c b/esp32/cloudsmets/main/cs_mqtt.c index dfc3c45..7f77a76 100644 --- a/esp32/cloudsmets/main/cs_mqtt.c +++ b/esp32/cloudsmets/main/cs_mqtt.c @@ -119,7 +119,7 @@ static void quote_plus(cs_string *in_str, cs_string *out_str) out_str->length = out_len; if (out_str->length > 0) { - CS_STRING_MALLOC(out_str->value, (out_len + 1)); + CS_STRING_MALLOC(out_str, (out_len + 1)); } ESP_LOGV(TAG, "out_str len: %u, %p", out_str->length, out_str->value); @@ -231,14 +231,14 @@ static void read_config() unsigned char *sptr; /* Free existing values and read new ones. */ - CS_STRING_FREE(iothub_url); - CS_STRING_FREE(quoted_device_url); - CS_STRING_FREE(sign_key); - CS_STRING_FREE(device); - CS_STRING_FREE(username); - CS_STRING_FREE(access_keys[0]); - CS_STRING_FREE(access_keys[1]); - CS_STRING_FREE(sas_token); + CS_STRING_FREE(&iothub_url); + CS_STRING_FREE("ed_device_url); + CS_STRING_FREE(&sign_key); + CS_STRING_FREE(&device); + CS_STRING_FREE(&username); + CS_STRING_FREE(&access_keys[0]); + CS_STRING_FREE(&access_keys[1]); + CS_STRING_FREE(&sas_token); /** * Note that the length of read strings includes the NULL terminator so @@ -285,7 +285,7 @@ static void read_config() * Username is "/devicename" */ username.length = iothub_url.length - PROTOCOL_MQTTS_LENGTH + 1+ device.length + 1; - CS_STRING_MALLOC(username.value, username.length); + CS_STRING_MALLOC(&username, username.length); sptr = username.value; memcpy(sptr, &iothub_url.value[PROTOCOL_MQTTS_LENGTH], (iothub_url.length - PROTOCOL_MQTTS_LENGTH)); sptr += iothub_url.length - PROTOCOL_MQTTS_LENGTH; @@ -301,7 +301,7 @@ static void read_config() #define DEVICES "/Devices/" #define DEVICES_LEN (sizeof(DEVICES) - 1) temp_url.length = iothub_url.length - PROTOCOL_MQTTS_LENGTH + DEVICES_LEN + device.length + 1; - CS_STRING_MALLOC(temp_url.value, temp_url.length); + CS_STRING_MALLOC(&temp_url, temp_url.length); sptr = temp_url.value; memcpy(sptr, &iothub_url.value[PROTOCOL_MQTTS_LENGTH], (iothub_url.length - PROTOCOL_MQTTS_LENGTH)); sptr += iothub_url.length - PROTOCOL_MQTTS_LENGTH; @@ -332,7 +332,7 @@ static void read_config() */ sas_token.length = SAS_TOKEN_START_LEN + quoted_device_url.length + SAS_TOKEN_AND_SIG_LEN + URLENCODED_BASE64_SHA256_LEN + 10 + 1; ESP_LOGV(TAG, "ST: %u", sas_token.length); - CS_STRING_MALLOC(sas_token.value, sas_token.length); + CS_STRING_MALLOC(&sas_token, sas_token.length); /* Now we preload as much as we can into the token. */ sptr = sas_token.value; @@ -353,7 +353,7 @@ static void read_config() * plus a stringified time. */ sign_key.length = quoted_device_url.length + 1 + 11; - CS_STRING_MALLOC(sign_key.value, sign_key.length); + CS_STRING_MALLOC(&sign_key, sign_key.length); sptr = sign_key.value; memcpy(sptr, quoted_device_url.value, quoted_device_url.length); sptr += quoted_device_url.length; diff --git a/esp32/cloudsmets/main/cs_string.h b/esp32/cloudsmets/main/cs_string.h index 28733e3..852b1ab 100644 --- a/esp32/cloudsmets/main/cs_string.h +++ b/esp32/cloudsmets/main/cs_string.h @@ -10,16 +10,16 @@ typedef struct } cs_string; #define CS_STRING_FREE(X) \ - if ((X.value) != NULL) \ + if ((X)->value != NULL) \ { \ - free((X).value); \ - (X).value = NULL; \ - (X).length = 0; \ + free((X)->value); \ + (X)->value = NULL; \ + (X)->length = 0; \ } #define CS_STRING_MALLOC(DST, LEN) \ - (DST) = malloc(LEN); \ - if ((DST) == NULL) \ + (DST)->value = malloc(LEN); \ + if ((DST)->value == NULL) \ { \ ESP_LOGE(TAG, "malloc failed for length: %d", (LEN)); \ ESP_ERROR_CHECK(ESP_FAIL); \ diff --git a/esp32/cloudsmets/main/cs_zigbee.c b/esp32/cloudsmets/main/cs_zigbee.c index 22b601d..40ebe5b 100644 --- a/esp32/cloudsmets/main/cs_zigbee.c +++ b/esp32/cloudsmets/main/cs_zigbee.c @@ -73,6 +73,7 @@ static bool receiving_events = false; // TODO: This will require checksum, destination etc etc before we can send it. // TODO: Do received messages contain this too? Assume not? static cs_string query_time = {NULL, 0}; +static cs_string factory_reset = {NULL, 0}; /** * Calculate the CRC for the received frame. @@ -364,6 +365,11 @@ static void event_handler(void *arg, esp_event_base_t event_base, zbhci_request_time(); break; + case CS_ZIGBEE_EVENT_FACTORY_RESET: + /* Factory reset the tlsr8258. */ + uart_write_bytes(UART_TO_TLSR8258, factory_reset.value, factory_reset.length); + break; + default: ESP_LOGE(TAG, "Unknown ZigBee event: %d", event_id); break; @@ -400,6 +406,22 @@ static void connected_timer_cb(void *arg) receiving_events = false; } +static void build_commands(void) +{ + zbhci_msg_t *msg; + + /* Factory reset. */ + CS_STRING_MALLOC(&factory_reset, 7); + msg = (zbhci_msg_t *)factory_reset.value; + msg->startFlag = ZBHCI_MSG_START_FLAG; + msg->msgType16H = U16_BYTE1(ZBHCI_CMD_BDB_FACTORY_RESET); + msg->msgType16L = U16_BYTE0(ZBHCI_CMD_BDB_FACTORY_RESET); + msg->msgLen16H = 0; + msg->msgLen16L = 0; + msg->checkSum = crc8_calculate(ZBHCI_CMD_BDB_FACTORY_RESET, 0, NULL); + msg->pData[0] = ZBHCI_MSG_END_FLAG; +} + void cs_zigbee_task(cs_zigbee_create_parms_t *create_parms) { uint32_t bytes_to_read = RX_BUFFER_SIZE; @@ -411,14 +433,19 @@ void cs_zigbee_task(cs_zigbee_create_parms_t *create_parms) // TODO: Remove this or perhaps replace with config? esp_log_level_set(TAG, ESP_LOG_VERBOSE); - /* Allocate a buffer for receipt. */ - buffer = (uint8_t *)malloc(RX_BUFFER_SIZE); - ESP_LOGI(TAG, "Init. MQTT task"); + ESP_LOGI(TAG, "Init. ZigBee task"); zigbee_event_loop_handle = create_parms->zigbee_event_loop_handle; mqtt_event_loop_handle = create_parms->mqtt_event_loop_handle; flash_event_loop_handle = create_parms->flash_event_loop_handle; + /* Allocate a buffer for receipt. */ + buffer = (uint8_t *)malloc(RX_BUFFER_SIZE); + + // TODO: Perhaps do this when we manage to connect? Need to build others? + /* Build fixed commands. */ + build_commands(); + ESP_LOGI(TAG, "Register event handlers"); /* Register Event handler */ diff --git a/esp32/cloudsmets/main/cs_zigbee.h b/esp32/cloudsmets/main/cs_zigbee.h index bd4101e..8e55a5c 100644 --- a/esp32/cloudsmets/main/cs_zigbee.h +++ b/esp32/cloudsmets/main/cs_zigbee.h @@ -7,7 +7,8 @@ typedef enum { CS_ZIGBEE_EVENT_ATTRS = 0, /**< Attributes. */ CS_ZIGBEE_EVENT_TIME, /**< Query time. */ CS_ZIGBEE_EVENT_CONNECTED, /**< Receiving msgs over ZigBee. */ - CS_ZIGBEE_EVENT_DISCONNECTED /**< No messages seen over ZigBee. */ + CS_ZIGBEE_EVENT_DISCONNECTED, /**< No messages seen over ZigBee. */ + CS_ZIGBEE_EVENT_FACTORY_RESET /**< No messages seen over ZigBee. */ } zigbee_event_t; typedef struct diff --git a/esp32/cloudsmets/main/telink/utility.h b/esp32/cloudsmets/main/telink/utility.h index 5b852e7..ee4d812 100644 --- a/esp32/cloudsmets/main/telink/utility.h +++ b/esp32/cloudsmets/main/telink/utility.h @@ -106,6 +106,7 @@ #define everyN(i, n) ++(i); (i)=((i) < N ? (i) : 0); if(0 == (i)) +#endif #define HI_UINT16(a) (((a) >> 8) & 0xFF) #define LO_UINT16(a) ((a) & 0xFF) @@ -122,7 +123,6 @@ #define U32_BYTE2(a) (((a) >> 16) & 0xFF) #define U32_BYTE3(a) (((a) >> 24) & 0xFF) -#endif #define BUILD_U16(lo, hi) ( (unsigned short)((((hi) & 0x00FF) << 8) + ((lo) & 0x00FF)) ) #define BUILD_U24(b0, b1, b2) ( (unsigned int)((((b2) & 0x000000FF) << 16) + (((b1) & 0x000000FF) << 8) + ((b0) & 0x000000FF)) )