From f3ab4c339853f18a7897d4518fee27bd78f20cca Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sat, 12 Feb 2022 10:19:29 +0100 Subject: [PATCH 01/31] initial setup --- components/grownode/boards/gn_easypot1.c | 6 ++--- components/grownode/gn_commons.h | 5 ++++ components/grownode/grownode.c | 32 ++++++++++++++++++++++++ components/grownode/grownode.h | 4 +++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/components/grownode/boards/gn_easypot1.c b/components/grownode/boards/gn_easypot1.c index 380aa306..434fd16e 100644 --- a/components/grownode/boards/gn_easypot1.c +++ b/components/grownode/boards/gn_easypot1.c @@ -119,7 +119,7 @@ void gn_configure_easypot1(gn_node_handle_t node) { //creates the temperature sensor temp = gn_leaf_create(node, "temp", gn_ds18b20_config, 4096); //set GPIO - gn_leaf_param_init_double(temp, GN_DS18B20_PARAM_GPIO, 0); + gn_leaf_param_init_double(temp, GN_DS18B20_PARAM_GPIO, 13); //set update time gn_leaf_param_init_double(temp, GN_DS18B20_PARAM_UPDATE_TIME_SEC, 5); //set initial status to active (on) @@ -127,11 +127,11 @@ void gn_configure_easypot1(gn_node_handle_t node) { //create the temp led leaf led_temp = gn_leaf_create(node, "led_temp", gn_led_config, 4096); - gn_leaf_param_init_double(led_temp, GN_LED_PARAM_GPIO, 1); + gn_leaf_param_init_double(led_temp, GN_LED_PARAM_GPIO, 14); //create the moisture led leaf led_moist = gn_leaf_create(node, "led_moist", gn_led_config, 4096); - gn_leaf_param_init_double(led_moist, GN_LED_PARAM_GPIO, 2); + gn_leaf_param_init_double(led_moist, GN_LED_PARAM_GPIO, 27); //creates a timer that checks moisture every seconds, using esp_timer API esp_timer_handle_t timer_moisture_handler; diff --git a/components/grownode/gn_commons.h b/components/grownode/gn_commons.h index 4e5afd02..5b509ded 100755 --- a/components/grownode/gn_commons.h +++ b/components/grownode/gn_commons.h @@ -74,6 +74,11 @@ typedef enum { GN_NODE_STATUS_ERROR_MISSING_SERVER_DISCOVERY_PREFIX = 13 } gn_node_status_t; +typedef enum { + GN_SLEEP_MODE_LIGHT = 0, + GN_SLEEP_MODE_DEEP = 1 +} gn_sleep_mode_t; + const char *gn_config_status_descriptions [14]; typedef enum { diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index 931f2f3e..d3f22645 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -37,6 +37,7 @@ extern "C" { #include "esp_err.h" #include "esp_spiffs.h" #include "esp_vfs.h" +#include "esp_sleep.h" #if CONFIG_GROWNODE_WIFI_ENABLED @@ -728,6 +729,37 @@ gn_err_t gn_node_start(gn_node_handle_t node) { } +/** + * @brief execute the main grownode loop + * + * depending on the configuration it can just wait for the leaf to execute or set the board in low power mode + * + * @param node the node to be started + * + * @return GN_RET_ERR in case of errors. this function should never return in normal circumstances. + */ +gn_err_t gn_node_loop(gn_node_handle_t node) { + + while (true) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + ESP_LOGD(TAG, "looping. grownode startup status: %s", + gn_get_status_description(config)); + } + +} + + +gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, uint64_t msec) { + + ESP_LOGI(TAG, "Entering deep sleep for %d seconds", msec); + esp_deep_sleep(1000000LL * deep_sleep_sec); + + return GN_RET_OK; + +} + + + gn_leaf_config_handle_intl_t _gn_leaf_config_create() { gn_leaf_config_handle_intl_t _conf = (gn_leaf_config_handle_intl_t) malloc( diff --git a/components/grownode/grownode.h b/components/grownode/grownode.h index e155edc6..e4e6a653 100755 --- a/components/grownode/grownode.h +++ b/components/grownode/grownode.h @@ -45,6 +45,10 @@ gn_err_t gn_node_destroy(gn_node_handle_t node); gn_err_t gn_node_start(gn_node_handle_t node); +gn_err_t gn_node_loop(gn_node_handle_t node); + +gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, uint64_t msec) + size_t gn_node_get_size(gn_node_handle_t config); gn_config_handle_t gn_init(gn_config_init_param_t *config_init); From 73b3fb6b680358f5a027070362c78926c4cecf19 Mon Sep 17 00:00:00 2001 From: ogghst Date: Sat, 12 Feb 2022 18:28:13 +0100 Subject: [PATCH 02/31] test deep/light sleep management --- components/grownode/boards/gn_easypot1.c | 43 +++++---- components/grownode/gn_commons.h | 43 +++++---- components/grownode/gn_event_source.h | 5 +- components/grownode/grownode.c | 113 ++++++++++++++++++++--- components/grownode/grownode.h | 2 +- components/grownode/leaves/gn_ds18b20.c | 7 +- main/main.c | 23 ++--- 7 files changed, 172 insertions(+), 64 deletions(-) diff --git a/components/grownode/boards/gn_easypot1.c b/components/grownode/boards/gn_easypot1.c index 434fd16e..7c8f4079 100644 --- a/components/grownode/boards/gn_easypot1.c +++ b/components/grownode/boards/gn_easypot1.c @@ -30,8 +30,8 @@ gn_leaf_handle_t moist, temp, led_moist, led_temp; double moist_last, temp_last; //sets the tresholds -const double moist_min = 1; -const double moist_max = 3; +const double moist_min = 20; +const double moist_max = 70; const double temp_min = 15; const double temp_max = 28; @@ -39,9 +39,9 @@ const double temp_max = 28; const double blink_time_high = 300; const double blink_time_low = 2000; -void moisture_callback(const gn_leaf_handle_t moist) { +void moisture_callback() { - double moist_act; + double moist_act = 0; gn_leaf_param_get_double(moist, GN_CMS_PARAM_ACT_LEVEL, &moist_act); gn_log(TAG, GN_LOG_INFO, "easypot1 - measuring moisture: %f", moist_act); @@ -68,10 +68,10 @@ void moisture_callback(const gn_leaf_handle_t moist) { } -void temp_callback(const gn_leaf_handle_t temp) { +void temp_callback() { - double temp_act; - gn_leaf_param_get_double(temp, GN_CMS_PARAM_ACT_LEVEL, &temp_act); + double temp_act = 0; + gn_leaf_param_get_double(temp, GN_DS18B20_PARAM_SENSOR_NAMES[0], &temp_act); gn_log(TAG, GN_LOG_INFO, "easypot1 - measuring temp: %f", temp_act); //turn on the LED if low with a specific frequency @@ -104,7 +104,11 @@ void temp_callback(const gn_leaf_handle_t temp) { void gn_configure_easypot1(gn_node_handle_t node) { //leaves - //esp_log_level_set("gn_leaf_led", ESP_LOG_INFO); + esp_log_level_set("gn_leaf_led", ESP_LOG_INFO); + esp_log_level_set("gn_leaf_cms", ESP_LOG_INFO); + esp_log_level_set("gn_leaf_ds18b20", ESP_LOG_INFO); + + //creates the moisture sensor moist = gn_leaf_create(node, "moist", gn_capacitive_moisture_sensor_config, @@ -112,16 +116,23 @@ void gn_configure_easypot1(gn_node_handle_t node) { //set the channel 4 gn_leaf_param_init_double(moist, GN_CMS_PARAM_ADC_CHANNEL, 4); //GPIO12 //set update time - gn_leaf_param_init_double(moist, GN_CMS_PARAM_UPDATE_TIME_SEC, 5); + gn_leaf_param_init_double(moist, GN_CMS_PARAM_UPDATE_TIME_SEC, 15); //set initial status to active (on) gn_leaf_param_init_bool(moist, GN_CMS_PARAM_ACTIVE, true); + //creates a timer that checks moisture every seconds, using esp_timer API + esp_timer_handle_t timer_moisture_handler; + esp_timer_create_args_t timer_moisture_args = { .callback = + &moisture_callback, .name = "moist_timer" }; + esp_timer_create(&timer_moisture_args, &timer_moisture_handler); + esp_timer_start_periodic(timer_moisture_handler, 20 * 1000000); + //creates the temperature sensor temp = gn_leaf_create(node, "temp", gn_ds18b20_config, 4096); //set GPIO - gn_leaf_param_init_double(temp, GN_DS18B20_PARAM_GPIO, 13); + gn_leaf_param_init_double(temp, GN_DS18B20_PARAM_GPIO, 26); //set update time - gn_leaf_param_init_double(temp, GN_DS18B20_PARAM_UPDATE_TIME_SEC, 5); + gn_leaf_param_init_double(temp, GN_DS18B20_PARAM_UPDATE_TIME_SEC, 15); //set initial status to active (on) gn_leaf_param_init_bool(temp, GN_DS18B20_PARAM_ACTIVE, true); @@ -133,19 +144,13 @@ void gn_configure_easypot1(gn_node_handle_t node) { led_moist = gn_leaf_create(node, "led_moist", gn_led_config, 4096); gn_leaf_param_init_double(led_moist, GN_LED_PARAM_GPIO, 27); - //creates a timer that checks moisture every seconds, using esp_timer API - esp_timer_handle_t timer_moisture_handler; - esp_timer_create_args_t timer_moisture_args = { .callback = - &moisture_callback, .name = "moist_timer" }; - esp_timer_create(&timer_moisture_args, &timer_moisture_handler); - esp_timer_start_periodic(timer_moisture_handler, 1 * 1000000); - //creates a timer that checks temperature every seconds, using esp_timer API esp_timer_handle_t timer_temp_handler; esp_timer_create_args_t timer_temp_args = { .callback = &temp_callback, .name = "temp_timer" }; esp_timer_create(&timer_temp_args, &timer_temp_handler); - esp_timer_start_periodic(timer_temp_handler, 1 * 1000000); + esp_timer_start_periodic(timer_temp_handler, 20 * 1000000); + } diff --git a/components/grownode/gn_commons.h b/components/grownode/gn_commons.h index 5b509ded..57071ebe 100755 --- a/components/grownode/gn_commons.h +++ b/components/grownode/gn_commons.h @@ -41,22 +41,6 @@ extern "C" { */ static const int16_t GN_CONFIG_MAX_SERVER_KEEPALIVE_SEC = 3600; -typedef struct { - bool provisioning_security; - char provisioning_password[9]; - int16_t wifi_retries_before_reset_provisioning; /*!< -1 to never lose provisioning (warning: in case of SSID change, no way to reset!*/ - bool server_board_id_topic; - char server_base_topic[80]; - char server_url[255]; - uint32_t server_keepalive_timer_sec; - bool server_discovery; - char server_discovery_prefix[80]; - char firmware_url[255]; - char sntp_url[255]; -} gn_config_init_param_t; - -typedef struct gn_config_init_param_t *gn_config_init_param_handle_t; - typedef enum { GN_NODE_STATUS_NOT_INITIALIZED = 0, GN_NODE_STATUS_INITIALIZING = 1, @@ -75,8 +59,9 @@ typedef enum { } gn_node_status_t; typedef enum { - GN_SLEEP_MODE_LIGHT = 0, - GN_SLEEP_MODE_DEEP = 1 + GN_SLEEP_MODE_NONE = 0, + GN_SLEEP_MODE_LIGHT = 1, + GN_SLEEP_MODE_DEEP = 2 } gn_sleep_mode_t; const char *gn_config_status_descriptions [14]; @@ -123,6 +108,28 @@ typedef enum { GN_LOG_ERROR = ESP_LOG_ERROR, } gn_log_level_t; +typedef struct { + bool provisioning_security; + char provisioning_password[9]; + int16_t wifi_retries_before_reset_provisioning; /*!< -1 to never lose provisioning (warning: in case of SSID change, no way to reset!*/ + bool server_board_id_topic; + char server_base_topic[80]; + char server_url[255]; + uint32_t server_keepalive_timer_sec; + bool server_discovery; + char server_discovery_prefix[80]; + char firmware_url[255]; + char sntp_url[255]; + uint64_t wakeup_time_millisec; + uint64_t sleep_time_millisec; + uint64_t sleep_delay_millisec; + gn_sleep_mode_t sleep_mode; + +} gn_config_init_param_t; + +typedef struct gn_config_init_param_t *gn_config_init_param_handle_t; + + typedef void *gn_leaf_handle_t; typedef void *gn_node_handle_t; typedef void *gn_config_handle_t; diff --git a/components/grownode/gn_event_source.h b/components/grownode/gn_event_source.h index ee1cad24..437117bd 100755 --- a/components/grownode/gn_event_source.h +++ b/components/grownode/gn_event_source.h @@ -69,8 +69,11 @@ typedef enum { GN_SRV_KEEPALIVE_TRIGGERED_EVENT = 0x503, //node events - GN_NODE_STARTED_EVENT = 0x601 + GN_NODE_STARTED_EVENT = 0x601, + //power events + GN_NODE_DEEP_SLEEP_START_EVENT = 0x701, + GN_NODE_LIGHT_SLEEP_START_EVENT = 0x702 } gn_event_id_t; diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index d3f22645..1f49dd8e 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -85,6 +85,9 @@ esp_event_loop_handle_t gn_event_loop = NULL; gn_config_handle_intl_t _gn_default_conf = NULL; +//remember last sleep reason +RTC_DATA_ATTR static gn_sleep_mode_t wakeup_reason = GN_SLEEP_MODE_NONE; + //SemaphoreHandle_t _gn_xEvtSemaphore; ESP_EVENT_DEFINE_BASE(GN_BASE_EVENT); @@ -723,7 +726,9 @@ gn_err_t gn_node_start(gn_node_handle_t node) { return GN_RET_ERR_EVENT_LOOP_ERROR; } - ret = gn_send_node_leaf_param_status(node); + //if first boot, send parameter status + if (wakeup_reason == GN_SLEEP_MODE_NONE) + ret = gn_send_node_leaf_param_status(node); return ret; @@ -740,25 +745,111 @@ gn_err_t gn_node_start(gn_node_handle_t node) { */ gn_err_t gn_node_loop(gn_node_handle_t node) { - while (true) { - vTaskDelay(1000 / portTICK_PERIOD_MS); - ESP_LOGD(TAG, "looping. grownode startup status: %s", - gn_get_status_description(config)); - } + if (!node) + return GN_RET_ERR_INVALID_ARG; + gn_node_handle_intl_t _node = (gn_node_handle_intl_t) node; + + if (_node->config->config_init_params->sleep_mode == GN_SLEEP_MODE_NONE) { + + while (true) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + ESP_LOGD(TAG, "looping. grownode startup status: %s, sleep mode = %d", + gn_get_status_description( + ((gn_node_handle_intl_t )node)->config), + _node->config->config_init_params->sleep_mode + ); + } + + } else if (_node->config->config_init_params->sleep_mode == GN_SLEEP_MODE_DEEP) { + ESP_LOGD(TAG, "working. grownode startup status: %s, sleep mode = %d, sleep in %"PRIu64" millisec", + gn_get_status_description( + ((gn_node_handle_intl_t )node)->config), + _node->config->config_init_params->sleep_mode, + _node->config->config_init_params->wakeup_time_millisec + ); + vTaskDelay(_node->config->config_init_params->wakeup_time_millisec / portTICK_PERIOD_MS); + gn_node_sleep(node, GN_SLEEP_MODE_DEEP, 1000LL * _node->config->config_init_params->sleep_time_millisec); + } else if (_node->config->config_init_params->sleep_mode == GN_SLEEP_MODE_LIGHT) { + ESP_LOGD(TAG, "working. grownode startup status: %s, sleep mode = %d, sleep in %"PRIu64" millisec", + gn_get_status_description( + ((gn_node_handle_intl_t )node)->config), + _node->config->config_init_params->sleep_mode, + _node->config->config_init_params->wakeup_time_millisec + ); + vTaskDelay(_node->config->config_init_params->wakeup_time_millisec / portTICK_PERIOD_MS); + gn_node_sleep(node, GN_SLEEP_MODE_LIGHT, 1000LL * _node->config->config_init_params->sleep_time_millisec); + } + return GN_RET_OK; } +/** + * @brief enter in sleep mode + * + * @param node the node to sleep + * @sleep_mode the type of sleep + * @millisec for how long + */ +gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, + uint64_t millisec) { + + if (!node) + return GN_RET_ERR_INVALID_ARG; + + gn_node_handle_intl_t _node = (gn_node_handle_intl_t) node; -gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, uint64_t msec) { + if (sleep_mode == GN_SLEEP_MODE_DEEP) { - ESP_LOGI(TAG, "Entering deep sleep for %d seconds", msec); - esp_deep_sleep(1000000LL * deep_sleep_sec); + if (ESP_OK + != esp_event_post_to(_node->config->event_loop, GN_BASE_EVENT, + GN_NODE_DEEP_SLEEP_START_EVENT, + NULL, 0, portMAX_DELAY)) { + ESP_LOGD(TAG, + "failed to send GN_NODE_DEEP_SLEEP_START_EVENT event"); + return GN_RET_ERR_EVENT_LOOP_ERROR; + } - return GN_RET_OK; + ESP_LOGD(TAG, + "Entering deep sleep for %"PRIu64" millisec in %"PRIu64" millisec", + millisec, + _node->config->config_init_params->sleep_delay_millisec); + //gives some time to handle the event + vTaskDelay( + _node->config->config_init_params->sleep_delay_millisec + / portTICK_PERIOD_MS); -} + wakeup_reason = GN_SLEEP_MODE_DEEP; + esp_deep_sleep(millisec * 1000LL); + } + if (sleep_mode == GN_SLEEP_MODE_LIGHT) { + if (ESP_OK + != esp_event_post_to(_node->config->event_loop, GN_BASE_EVENT, + GN_NODE_LIGHT_SLEEP_START_EVENT, + NULL, 0, portMAX_DELAY)) { + ESP_LOGD(TAG, + "failed to send GN_NODE_LIGHT_SLEEP_START_EVENT event"); + return GN_RET_ERR_EVENT_LOOP_ERROR; + } + + ESP_LOGD(TAG, + "Entering light sleep for %"PRIu64" millisec in %"PRIu64" millisec", + millisec, + _node->config->config_init_params->sleep_delay_millisec); + //gives some time to handle the event + vTaskDelay( + _node->config->config_init_params->sleep_delay_millisec + / portTICK_PERIOD_MS); + + wakeup_reason = GN_SLEEP_MODE_LIGHT; + esp_sleep_enable_timer_wakeup(millisec * 1000LL); + esp_light_sleep_start(); + } + + return GN_RET_OK; + +} gn_leaf_config_handle_intl_t _gn_leaf_config_create() { diff --git a/components/grownode/grownode.h b/components/grownode/grownode.h index e4e6a653..b07c8fbe 100755 --- a/components/grownode/grownode.h +++ b/components/grownode/grownode.h @@ -47,7 +47,7 @@ gn_err_t gn_node_start(gn_node_handle_t node); gn_err_t gn_node_loop(gn_node_handle_t node); -gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, uint64_t msec) +gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, uint64_t millisec); size_t gn_node_get_size(gn_node_handle_t config); diff --git a/components/grownode/leaves/gn_ds18b20.c b/components/grownode/leaves/gn_ds18b20.c index d1ac5213..a7aa6c71 100755 --- a/components/grownode/leaves/gn_ds18b20.c +++ b/components/grownode/leaves/gn_ds18b20.c @@ -53,8 +53,8 @@ void _scan_sensors(int gpio, size_t *sensor_count, ds18x20_addr_t *addrs) { return; } - ESP_LOGD(TAG, "%d sensors detected. addr[0]=%llu, addr[1]=%llu", - *sensor_count, addrs[0], addrs[1]); + ESP_LOGD(TAG, "%d sensors detected. addr[0]=%llu, addr[1]=%llu, addr[2]=%llu, addr[3]=%llu", + *sensor_count, addrs[0], addrs[1], addrs[2], addrs[3]); // If there were more sensors found than we have space to handle, // just report the first MAX_SENSORS.. @@ -248,7 +248,7 @@ void gn_ds18b20_task(gn_leaf_handle_t leaf_config) { //init sensors //setup gpio - gpio_set_pull_mode(gpio, GPIO_PULLUP_ONLY); + //gpio_set_pull_mode(gpio, GPIO_PULLUP_ONLY); _scan_sensors(gpio, &data->sensor_count, &data->addrs[0]); @@ -264,6 +264,7 @@ void gn_ds18b20_task(gn_leaf_handle_t leaf_config) { } if (ret == ESP_OK && active == true) { + ret = esp_timer_start_periodic(data->sensor_timer, update_time_sec * 1000000); if (ret != ESP_OK) { diff --git a/main/main.c b/main/main.c index dafc9c7c..b22a0903 100644 --- a/main/main.c +++ b/main/main.c @@ -22,7 +22,7 @@ #include "grownode.h" //include the board you want to start here -#include "gn_blink.h" +#include "gn_easypot1.h" #define TASK_STACK_SIZE 8192*4 @@ -40,12 +40,12 @@ void app_main(void) { esp_log_level_set("grownode", ESP_LOG_INFO); esp_log_level_set("gn_commons", ESP_LOG_INFO); esp_log_level_set("gn_nvs", ESP_LOG_INFO); - esp_log_level_set("gn_mqtt_protocol", ESP_LOG_DEBUG); + esp_log_level_set("gn_mqtt_protocol", ESP_LOG_INFO); esp_log_level_set("gn_network", ESP_LOG_INFO); esp_log_level_set("gn_display", ESP_LOG_INFO); //boards - esp_log_level_set("gn_blink", ESP_LOG_INFO); + esp_log_level_set("gn_easypot1", ESP_LOG_INFO); gn_config_init_param_t config_init = { .provisioning_security = true, @@ -53,12 +53,15 @@ void app_main(void) { .wifi_retries_before_reset_provisioning = 5, .server_board_id_topic = false, .server_base_topic = "/grownode/test", - .server_url = "mqtt://192.168.1.170:1883", + .server_url = "mqtt://192.168.1.10:1883", .server_keepalive_timer_sec = 60, .server_discovery = false, .server_discovery_prefix = "homeassistant", .firmware_url = "http://myserver/myfirmware.bin", - .sntp_url = "pool.ntp.org" + .sntp_url = "pool.ntp.org", + .sleep_delay_millisec = 10000LL, + .wakeup_time_millisec = 30000LL, + .sleep_mode = GN_SLEEP_MODE_DEEP }; //creates the config handle @@ -75,16 +78,14 @@ void app_main(void) { gn_node_handle_t node = gn_node_create(config, "node"); //the board to start - gn_configure_blink(node); + gn_configure_easypot1(node); //finally, start node gn_node_start(node); - while (true) { - vTaskDelay(10000 / portTICK_PERIOD_MS); - ESP_LOGD(TAG, "grownode startup status: %s", - gn_get_status_description(config)); - } + //handles loop + gn_node_loop(node); + } From f8152dfbb19a83b81b25809d3ca93fa023895d75 Mon Sep 17 00:00:00 2001 From: ogghst Date: Mon, 14 Feb 2022 08:36:41 +0100 Subject: [PATCH 03/31] sleep now starting only when leaves are not working --- components/grownode/boards/gn_easypot1.c | 6 +- components/grownode/gn_mqtt_protocol.c | 32 ++-- components/grownode/grownode.c | 159 ++++++++++++------ components/grownode/grownode_intl.h | 1 + .../leaves/gn_capacitive_moisture_sensor.c | 20 ++- components/grownode/leaves/gn_ds18b20.c | 69 ++++---- main/main.c | 5 +- 7 files changed, 183 insertions(+), 109 deletions(-) diff --git a/components/grownode/boards/gn_easypot1.c b/components/grownode/boards/gn_easypot1.c index 7c8f4079..34e12b4f 100644 --- a/components/grownode/boards/gn_easypot1.c +++ b/components/grownode/boards/gn_easypot1.c @@ -105,10 +105,8 @@ void gn_configure_easypot1(gn_node_handle_t node) { //leaves esp_log_level_set("gn_leaf_led", ESP_LOG_INFO); - esp_log_level_set("gn_leaf_cms", ESP_LOG_INFO); - esp_log_level_set("gn_leaf_ds18b20", ESP_LOG_INFO); - - + esp_log_level_set("gn_leaf_cms", ESP_LOG_DEBUG); + esp_log_level_set("gn_leaf_ds18b20", ESP_LOG_DEBUG); //creates the moisture sensor moist = gn_leaf_create(node, "moist", gn_capacitive_moisture_sensor_config, diff --git a/components/grownode/gn_mqtt_protocol.c b/components/grownode/gn_mqtt_protocol.c index a1236b9e..5cae300e 100755 --- a/components/grownode/gn_mqtt_protocol.c +++ b/components/grownode/gn_mqtt_protocol.c @@ -74,8 +74,7 @@ inline char* _gn_mqtt_build_node_name(gn_config_handle_intl_t config) { } -void _gn_mqtt_build_leaf_command_topic(gn_leaf_handle_t _leaf_config, - char *buf) { +void _gn_mqtt_build_leaf_command_topic(gn_leaf_handle_t _leaf_config, char *buf) { gn_leaf_config_handle_intl_t leaf_config = (gn_leaf_config_handle_intl_t) _leaf_config; @@ -99,8 +98,7 @@ void _gn_mqtt_build_leaf_command_topic(gn_leaf_handle_t _leaf_config, } void _gn_mqtt_build_leaf_parameter_command_topic( - const gn_leaf_handle_t _leaf_config, const char *param_name, - char *buf) { + const gn_leaf_handle_t _leaf_config, const char *param_name, char *buf) { gn_leaf_config_handle_intl_t leaf_config = (gn_leaf_config_handle_intl_t) _leaf_config; @@ -125,8 +123,8 @@ void _gn_mqtt_build_leaf_parameter_command_topic( } -void _gn_mqtt_build_leaf_parameter_status_topic( - gn_leaf_handle_t _leaf_config, char *param_name, char *buf) { +void _gn_mqtt_build_leaf_parameter_status_topic(gn_leaf_handle_t _leaf_config, + char *param_name, char *buf) { gn_leaf_config_handle_intl_t leaf_config = (gn_leaf_config_handle_intl_t) _leaf_config; @@ -151,8 +149,7 @@ void _gn_mqtt_build_leaf_parameter_status_topic( } -void _gn_mqtt_build_leaf_status_topic(gn_leaf_handle_t _leaf_config, - char *buf) { +void _gn_mqtt_build_leaf_status_topic(gn_leaf_handle_t _leaf_config, char *buf) { gn_leaf_config_handle_intl_t leaf_config = (gn_leaf_config_handle_intl_t) _leaf_config; @@ -433,8 +430,7 @@ gn_err_t gn_mqtt_send_node_config(gn_node_handle_t _node_config) { if (!_node_config) return GN_RET_ERR_INVALID_ARG; - gn_node_handle_intl_t __node_config = - (gn_node_handle_intl_t) _node_config; + gn_node_handle_intl_t __node_config = (gn_node_handle_intl_t) _node_config; if (!__node_config->config) return GN_RET_ERR_INVALID_ARG; @@ -452,8 +448,7 @@ gn_err_t gn_mqtt_send_node_config(gn_node_handle_t _node_config) { msg->config = _node_config; strncpy(msg->topic, _gn_sts_topic, _GN_MQTT_MAX_TOPIC_LENGTH); - gn_node_handle_intl_t node_config = - (gn_node_handle_intl_t) _node_config; + gn_node_handle_intl_t node_config = (gn_node_handle_intl_t) _node_config; gn_config_handle_intl_t config = (gn_config_handle_intl_t) node_config->config; @@ -577,7 +572,7 @@ gn_err_t gn_mqtt_send_leaf_param(gn_leaf_param_handle_t _param) { if (_node_config->config->status != GN_NODE_STATUS_STARTED) return GN_RET_OK; - ESP_LOGD(TAG, "gn_mqtt_send_leaf_param %s", param->name); + ESP_LOGI(TAG, "gn_mqtt_send_leaf_param %s", param->name); int ret = GN_RET_OK; @@ -601,7 +596,9 @@ gn_err_t gn_mqtt_send_leaf_param(gn_leaf_param_handle_t _param) { break; case GN_VAL_TYPE_STRING: len = strlen(param->param_val->v.s); - strncpy(buf, param->param_val->v.s, len > _GN_MQTT_MAX_PAYLOAD_LENGTH? _GN_MQTT_MAX_PAYLOAD_LENGTH: len); + strncpy(buf, param->param_val->v.s, + len > _GN_MQTT_MAX_PAYLOAD_LENGTH ? + _GN_MQTT_MAX_PAYLOAD_LENGTH : len); break; case GN_VAL_TYPE_DOUBLE: snprintf(buf, 31, "%f", param->param_val->v.d); @@ -634,6 +631,9 @@ gn_err_t gn_mqtt_send_leaf_param(gn_leaf_param_handle_t _param) { return ((msg_id == -1) ? (GN_RET_ERR_MQTT_ERROR) : (GN_RET_OK)); } + //TODO remove + vTaskDelay(10 / portTICK_PERIOD_MS); + return ret; #else @@ -1037,8 +1037,7 @@ gn_err_t gn_mqtt_send_ota_message(gn_config_handle_t _config) { * @return GN_RET_ERR_INVALID_ARG if _config is null * @return GN_RET_ERR_MQTT_ERROR if not possible to send message */ -gn_err_t gn_mqtt_send_leaf_message(gn_leaf_handle_t _leaf, - const char *msg) { +gn_err_t gn_mqtt_send_leaf_message(gn_leaf_handle_t _leaf, const char *msg) { #ifdef CONFIG_GROWNODE_WIFI_ENABLED @@ -1385,7 +1384,6 @@ gn_err_t gn_mqtt_init(gn_config_handle_t config) { return GN_RET_ERR_INVALID_ARG; } - /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ ESP_ERROR_CHECK( esp_mqtt_client_register_event(client, (esp_mqtt_event_id_t) ESP_EVENT_ANY_ID, _gn_mqtt_event_handler, NULL)); diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index 1f49dd8e..716bc221 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -105,15 +105,19 @@ gn_err_t _gn_leaf_start(gn_leaf_config_handle_intl_t leaf_config) { int ret = GN_RET_OK; ESP_LOGI(TAG, "_gn_leaf_start %s", leaf_config->name); + TaskHandle_t task_handle; + if (xTaskCreate((void*) leaf_config->leaf_descriptor->callback, leaf_config->name, leaf_config->task_size, leaf_config, 1, //configMAX_PRIORITIES - 1, - NULL) != pdPASS) { + &task_handle) != pdPASS) { gn_log(TAG, GN_LOG_ERROR, "failed to create lef task for %s", leaf_config->name); goto fail; } + leaf_config->task_handle = task_handle; - vTaskDelay(pdMS_TO_TICKS(100)); + while (eTaskGetState(task_handle) != eBlocked) + vTaskDelay(pdMS_TO_TICKS(100)); //gn_display_leaf_start(leaf_config); @@ -543,6 +547,7 @@ gn_node_handle_intl_t _gn_node_config_create() { } + /** * @brief retrieves the configuration status * @@ -704,16 +709,6 @@ gn_err_t gn_node_start(gn_node_handle_t node) { ESP_LOGD(TAG, "gn_start_node: %s, leaves: %d", _node->name, _node->leaves.last); - //publish node - //if (gn_mqtt_send_node_config(node) != ESP_OK) - //return ESP_FAIL; - - //run leaves - for (int i = 0; i < _node->leaves.last; i++) { - //ESP_LOGD(TAG, "starting leaf: %d", i); - if (_gn_leaf_start(_node->leaves.at[i]) != GN_RET_OK) - return GN_RET_ERR_NODE_NOT_STARTED; - } _node->config->status = GN_NODE_STATUS_STARTED; @@ -726,6 +721,22 @@ gn_err_t gn_node_start(gn_node_handle_t node) { return GN_RET_ERR_EVENT_LOOP_ERROR; } + //publish node + //if (gn_mqtt_send_node_config(node) != ESP_OK) + //return ESP_FAIL; + + //run leaves + for (int i = 0; i < _node->leaves.last; i++) { + //ESP_LOGD(TAG, "starting leaf: %d", i); + if (_gn_leaf_start(_node->leaves.at[i]) != GN_RET_OK) { + gn_log(TAG, GN_LOG_ERROR, + "failed to start leaf: %s", _node->leaves.at[i]->name); + _node->config->status = GN_NODE_STATUS_ERROR; + return GN_RET_ERR_NODE_NOT_STARTED; + } + } + + //if first boot, send parameter status if (wakeup_reason == GN_SLEEP_MODE_NONE) ret = gn_send_node_leaf_param_status(node); @@ -754,35 +765,67 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { while (true) { vTaskDelay(1000 / portTICK_PERIOD_MS); - ESP_LOGD(TAG, "looping. grownode startup status: %s, sleep mode = %d", + ESP_LOGD(TAG, + "looping. grownode startup status: %s, sleep mode = %d", gn_get_status_description( ((gn_node_handle_intl_t )node)->config), - _node->config->config_init_params->sleep_mode - ); + _node->config->config_init_params->sleep_mode); } - } else if (_node->config->config_init_params->sleep_mode == GN_SLEEP_MODE_DEEP) { - ESP_LOGD(TAG, "working. grownode startup status: %s, sleep mode = %d, sleep in %"PRIu64" millisec", + } else if (_node->config->config_init_params->sleep_mode + == GN_SLEEP_MODE_DEEP) { + ESP_LOGD(TAG, + "working. grownode startup status: %s, sleep mode = %d, sleep in %"PRIu64" millisec", gn_get_status_description( ((gn_node_handle_intl_t )node)->config), - _node->config->config_init_params->sleep_mode, - _node->config->config_init_params->wakeup_time_millisec - ); - vTaskDelay(_node->config->config_init_params->wakeup_time_millisec / portTICK_PERIOD_MS); - gn_node_sleep(node, GN_SLEEP_MODE_DEEP, 1000LL * _node->config->config_init_params->sleep_time_millisec); - } else if (_node->config->config_init_params->sleep_mode == GN_SLEEP_MODE_LIGHT) { - ESP_LOGD(TAG, "working. grownode startup status: %s, sleep mode = %d, sleep in %"PRIu64" millisec", + _node->config->config_init_params->sleep_mode, + _node->config->config_init_params->wakeup_time_millisec); + vTaskDelay( + _node->config->config_init_params->wakeup_time_millisec + / portTICK_PERIOD_MS); + gn_node_sleep(node, GN_SLEEP_MODE_DEEP, + _node->config->config_init_params->sleep_time_millisec); + } else if (_node->config->config_init_params->sleep_mode + == GN_SLEEP_MODE_LIGHT) { + ESP_LOGD(TAG, + "working. grownode startup status: %s, sleep mode = %d, sleep in %"PRIu64" millisec", gn_get_status_description( ((gn_node_handle_intl_t )node)->config), - _node->config->config_init_params->sleep_mode, - _node->config->config_init_params->wakeup_time_millisec - ); - vTaskDelay(_node->config->config_init_params->wakeup_time_millisec / portTICK_PERIOD_MS); - gn_node_sleep(node, GN_SLEEP_MODE_LIGHT, 1000LL * _node->config->config_init_params->sleep_time_millisec); + _node->config->config_init_params->sleep_mode, + _node->config->config_init_params->wakeup_time_millisec); + vTaskDelay( + _node->config->config_init_params->wakeup_time_millisec + / portTICK_PERIOD_MS); + gn_node_sleep(node, GN_SLEEP_MODE_LIGHT, + _node->config->config_init_params->sleep_time_millisec); } return GN_RET_OK; } + +void _gn_wait_for_blocked_leaves(gn_node_handle_intl_t _node) { + + //waits until all leaves has reached blocked status + int leaves_count = _node->leaves.last; + + bool leaves_working = false; + bool this_leaf_working = false; + + while (!leaves_working) { + for (int i = 0; i < leaves_count; i++) { + this_leaf_working = eTaskGetState(_node->leaves.at[i]->task_handle) + != eBlocked; + leaves_working = this_leaf_working || leaves_working; + if (this_leaf_working) { + ESP_LOGD(TAG, "leaves working: %s", _node->leaves.at[i]->name); + } + } + + if (leaves_working) + vTaskDelay(pdMS_TO_TICKS(5)); + } +} + /** * @brief enter in sleep mode * @@ -810,19 +853,27 @@ gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, } ESP_LOGD(TAG, - "Entering deep sleep for %"PRIu64" millisec in %"PRIu64" millisec", - millisec, + "Preparing deep sleep in %"PRIu64" millisec", _node->config->config_init_params->sleep_delay_millisec); + //gives some time to handle the event + if (_node->config->config_init_params->sleep_delay_millisec > 0) { vTaskDelay( _node->config->config_init_params->sleep_delay_millisec / portTICK_PERIOD_MS); + } + + _gn_wait_for_blocked_leaves(_node); + + ESP_LOGD(TAG, + "Entering deep sleep for %"PRIu64" millisec", + millisec); wakeup_reason = GN_SLEEP_MODE_DEEP; esp_deep_sleep(millisec * 1000LL); } - if (sleep_mode == GN_SLEEP_MODE_LIGHT) { + else if (sleep_mode == GN_SLEEP_MODE_LIGHT) { if (ESP_OK != esp_event_post_to(_node->config->event_loop, GN_BASE_EVENT, @@ -834,13 +885,21 @@ gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, } ESP_LOGD(TAG, - "Entering light sleep for %"PRIu64" millisec in %"PRIu64" millisec", - millisec, + "Preparing light sleep in %"PRIu64" millisec", _node->config->config_init_params->sleep_delay_millisec); - //gives some time to handle the event + +//gives some time to handle the event + if (_node->config->config_init_params->sleep_delay_millisec > 0) { vTaskDelay( _node->config->config_init_params->sleep_delay_millisec / portTICK_PERIOD_MS); + } + + _gn_wait_for_blocked_leaves(_node); + + ESP_LOGD(TAG, + "Entering light sleep for %"PRIu64" millisec", + millisec); wakeup_reason = GN_SLEEP_MODE_LIGHT; esp_sleep_enable_timer_wakeup(millisec * 1000LL); @@ -1189,9 +1248,9 @@ gn_leaf_param_handle_t gn_leaf_param_create(gn_leaf_handle_t leaf_config, //ESP_LOGD(TAG, "building storage tag.."); if (storage == GN_LEAF_PARAM_STORAGE_PERSISTED) { - //check parameter stored +//check parameter stored int _len = (strlen(_leaf_config->name) + strlen(name) + 2); - //ESP_LOGD(TAG, "..len: %i", _len); +//ESP_LOGD(TAG, "..len: %i", _len); char *_buf = (char*) calloc(_len, sizeof(char)); memcpy(_buf, _leaf_config->name, @@ -1203,10 +1262,10 @@ gn_leaf_param_handle_t gn_leaf_param_create(gn_leaf_handle_t leaf_config, _buf[_len - 1] = '\0'; - //ESP_LOGD(TAG, ".. storage tag: %s", _buf); +//ESP_LOGD(TAG, ".. storage tag: %s", _buf); char *value = 0; - //check if existing +//check if existing ESP_LOGD(TAG, "check stored value for key %s", _buf); if (gn_storage_get(_buf, (void**) &value) == ESP_OK) { @@ -1455,7 +1514,7 @@ gn_err_t gn_leaf_param_write_string(const gn_leaf_handle_t leaf_config, sizeof(char) * (strlen(*validate) + 1)); strncpy(_param->param_val->v.s, *validate, strlen(*validate)); } - //ESP_LOGD(TAG, "processing validator - result: %d", (int )ret); +//ESP_LOGD(TAG, "processing validator - result: %d", (int )ret); } else { _param->param_val->v.s = (char*) realloc(_param->param_val->v.s, sizeof(char) * (strlen(val) + 1)); @@ -1670,7 +1729,7 @@ gn_err_t gn_leaf_param_write_bool(const gn_leaf_handle_t leaf_config, _buf[_len - 1] = '\0'; - //check if existing +//check if existing if (gn_storage_set(_buf, (void**) &val, sizeof(bool)) != ESP_OK) { ESP_LOGW(TAG, "not possible to store leaf parameter value - key %s value %i", @@ -1950,7 +2009,7 @@ gn_err_t gn_leaf_param_write_double(const gn_leaf_handle_t leaf_config, _buf[_len - 1] = '\0'; - //check if existing +//check if existing if (gn_storage_set(_buf, (void**) &val, sizeof(double)) != ESP_OK) { ESP_LOGW(TAG, "not possible to store leaf parameter value - key %s value %f", @@ -1971,7 +2030,7 @@ gn_err_t gn_leaf_param_write_double(const gn_leaf_handle_t leaf_config, if (val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_GENERIC && val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_NOT_ALLOWED) _param->param_val->v.d = **validate; - //ESP_LOGD(TAG, "processing validator - result: %d", (int )ret); +//ESP_LOGD(TAG, "processing validator - result: %d", (int )ret); } else { _param->param_val->v.d = val; } @@ -2087,7 +2146,7 @@ gn_err_t _gn_leaf_parameter_update(const gn_leaf_handle_t leaf_config, while (leaf_params != NULL) { - //check param name +//check param name if (strcmp(param, leaf_params->name) == 0) { //param is the one to update @@ -2417,10 +2476,10 @@ gn_leaf_param_handle_t gn_leaf_param_get_param_handle( ((gn_leaf_config_handle_intl_t) leaf_config)->params; while (param) { - //ESP_LOGD(TAG, - // "gn_leaf_param_get_param_handle - comparing %s (%d) and checking %s (%d)", - // param_name, strlen(param_name), param->name, - // strlen(param->name)); +//ESP_LOGD(TAG, +// "gn_leaf_param_get_param_handle - comparing %s (%d) and checking %s (%d)", +// param_name, strlen(param_name), param->name, +// strlen(param->name)); if (strncmp(param->name, param_name, strlen(param_name)) == 0) { //ESP_LOGD(TAG, "found!"); return param; @@ -2885,7 +2944,7 @@ gn_err_t gn_storage_get(const char *key, void **value) { gn_hash_str(key, _hashedkey, len); // Read the size of memory space required for blob - size_t required_size = 0; // value will default to 0, if not set yet in NVS + size_t required_size = 0;// value will default to 0, if not set yet in NVS err = nvs_get_blob(my_handle, _hashedkey, NULL, &required_size); if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) { ESP_LOGD(TAG_NVS, "nvs_get_blob(handle, %s, NULL, %d) - %d", key, @@ -2895,7 +2954,7 @@ gn_err_t gn_storage_get(const char *key, void **value) { if (required_size > 0) { - // Read previously saved blob if available +// Read previously saved blob if available *value = malloc(required_size + sizeof(uint32_t)); err = nvs_get_blob(my_handle, _hashedkey, *value, &required_size); diff --git a/components/grownode/grownode_intl.h b/components/grownode/grownode_intl.h index def279b0..87dc1b7a 100755 --- a/components/grownode/grownode_intl.h +++ b/components/grownode/grownode_intl.h @@ -75,6 +75,7 @@ struct gn_leaf_config_t { //gn_leaf_config_handle_t next; //gn_leaf_task_callback task_cb; QueueHandle_t event_queue; + TaskHandle_t task_handle; //esp_event_loop_handle_t event_loop; gn_leaf_param_handle_t params; //gn_display_handler_t display_handler; diff --git a/components/grownode/leaves/gn_capacitive_moisture_sensor.c b/components/grownode/leaves/gn_capacitive_moisture_sensor.c index 879f0bc8..0b458f57 100644 --- a/components/grownode/leaves/gn_capacitive_moisture_sensor.c +++ b/components/grownode/leaves/gn_capacitive_moisture_sensor.c @@ -146,7 +146,7 @@ gn_leaf_descriptor_handle_t gn_capacitive_moisture_sensor_config( gn_cms_data_t *data = malloc(sizeof(gn_cms_data_t)); data->active_param = gn_leaf_param_create(leaf_config, GN_CMS_PARAM_ACTIVE, - GN_VAL_TYPE_BOOLEAN, (gn_val_t ) { .b = false }, + GN_VAL_TYPE_BOOLEAN, (gn_val_t ) { .b = true }, GN_LEAF_PARAM_ACCESS_ALL, GN_LEAF_PARAM_STORAGE_PERSISTED, NULL); @@ -367,17 +367,25 @@ void gn_cms_task(gn_leaf_handle_t leaf_config) { ret = esp_timer_create(&water_sensor_timer_args, &data->sensor_timer); if (ret != ESP_OK) { gn_log(TAG, GN_LOG_ERROR, - "[%s] failed to init capacitive moisture sensor timer", leaf_name); + "[%s] failed to init capacitive moisture sensor timer", + leaf_name); } if (ret == ESP_OK && active == true) { - //start sensor callback - ret = esp_timer_start_periodic(data->sensor_timer, - update_time_sec * 1000000); + //first shot immediate + gn_cms_sensor_collect(leaf_config); + + //then start the periodic wakeup + if (ret == ESP_OK) { + ret = esp_timer_start_periodic(data->sensor_timer, + update_time_sec * 1000000); + } + if (ret != ESP_OK) { gn_log(TAG, GN_LOG_ERROR, - "[%s] failed to start capacitive moisture sensor timer", leaf_name); + "[%s] failed to start capacitive moisture sensor timer", + leaf_name); gn_leaf_get_descriptor(leaf_config)->status = GN_LEAF_STATUS_ERROR; gn_leaf_param_write_bool(leaf_config, GN_CMS_PARAM_ACTIVE, false); descriptor->status = GN_LEAF_STATUS_ERROR; diff --git a/components/grownode/leaves/gn_ds18b20.c b/components/grownode/leaves/gn_ds18b20.c index a7aa6c71..d9beb746 100755 --- a/components/grownode/leaves/gn_ds18b20.c +++ b/components/grownode/leaves/gn_ds18b20.c @@ -49,11 +49,13 @@ void _scan_sensors(int gpio, size_t *sensor_count, ds18x20_addr_t *addrs) { res = ds18x20_scan_devices(gpio, addrs, GN_DS18B20_MAX_SENSORS, sensor_count); if (res != ESP_OK) { - gn_log(TAG, GN_LOG_ERROR, "Sensors scan error %d (%s)", res, esp_err_to_name(res)); + gn_log(TAG, GN_LOG_ERROR, "Sensors scan error %d (%s)", res, + esp_err_to_name(res)); return; } - ESP_LOGD(TAG, "%d sensors detected. addr[0]=%llu, addr[1]=%llu, addr[2]=%llu, addr[3]=%llu", + ESP_LOGD(TAG, + "%d sensors detected. addr[0]=%llu, addr[1]=%llu, addr[2]=%llu, addr[3]=%llu", *sensor_count, addrs[0], addrs[1], addrs[2], addrs[3]); // If there were more sensors found than we have space to handle, @@ -126,7 +128,7 @@ void gn_ds18b20_temp_sensor_collect(gn_leaf_handle_t leaf_config) { if (active == true) { - ESP_LOGD(TAG, "[%s] reading from GPIO %d..", leaf_name, (int)gpio); + ESP_LOGD(TAG, "[%s] reading from GPIO %d..", leaf_name, (int )gpio); //read data from sensors using GPIO parameter res = ds18x20_measure_and_read_multi(gpio, data->addrs, @@ -134,9 +136,7 @@ void gn_ds18b20_temp_sensor_collect(gn_leaf_handle_t leaf_config) { if (res != ESP_OK) { gn_log(TAG, GN_LOG_ERROR, "[%s] sensors read error %d (%s)", - leaf_name, - res, - esp_err_to_name(res)); + leaf_name, res, esp_err_to_name(res)); gn_leaf_get_descriptor(leaf_config)->status = GN_LEAF_STATUS_ERROR; gn_leaf_param_write_bool(leaf_config, GN_DS18B20_PARAM_ACTIVE, false); @@ -147,9 +147,9 @@ void gn_ds18b20_temp_sensor_collect(gn_leaf_handle_t leaf_config) { for (int j = 0; j < data->sensor_count; j++) { float temp_c = data->temp[j]; float temp_f = (temp_c * 1.8) + 32; - ESP_LOGD(TAG, "[%s] sensor %08x%08x (%s) reports %.3f �C (%.3f �F)", - leaf_name, - (uint32_t )(data->addrs[j] >> 32), + ESP_LOGD(TAG, + "[%s] sensor %08x%08x (%s) reports %.3f �C (%.3f �F)", + leaf_name, (uint32_t )(data->addrs[j] >> 32), (uint32_t )data->addrs[j], (data->addrs[j] & 0xff) == DS18B20_FAMILY_ID ? "DS18B20" : "DS18S20", temp_c, temp_f); @@ -164,8 +164,7 @@ void gn_ds18b20_temp_sensor_collect(gn_leaf_handle_t leaf_config) { } } -gn_leaf_descriptor_handle_t gn_ds18b20_config( - gn_leaf_handle_t leaf_config) { +gn_leaf_descriptor_handle_t gn_ds18b20_config(gn_leaf_handle_t leaf_config) { char leaf_name[GN_LEAF_NAME_SIZE]; gn_leaf_get_name(leaf_config, leaf_name); @@ -201,7 +200,8 @@ gn_leaf_descriptor_handle_t gn_ds18b20_config( //get gpio from params. default 27 data->gpio_param = gn_leaf_param_create(leaf_config, GN_DS18B20_PARAM_GPIO, GN_VAL_TYPE_DOUBLE, (gn_val_t ) { .d = 27 }, - GN_LEAF_PARAM_ACCESS_NETWORK, GN_LEAF_PARAM_STORAGE_PERSISTED, NULL); + GN_LEAF_PARAM_ACCESS_NETWORK, GN_LEAF_PARAM_STORAGE_PERSISTED, + NULL); gn_leaf_param_add_to_leaf(leaf_config, data->gpio_param); //get params for temp. init to 0 @@ -231,7 +231,6 @@ void gn_ds18b20_task(gn_leaf_handle_t leaf_config) { char leaf_name[GN_LEAF_NAME_SIZE]; gn_leaf_get_name(leaf_config, leaf_name); - bool active; gn_leaf_param_get_bool(leaf_config, GN_DS18B20_PARAM_ACTIVE, &active); @@ -242,7 +241,6 @@ void gn_ds18b20_task(gn_leaf_handle_t leaf_config) { gn_leaf_param_get_double(leaf_config, GN_DS18B20_PARAM_UPDATE_TIME_SEC, &update_time_sec); - ESP_LOGD(TAG, "[%s] gn_ds18b20_task", leaf_name); //init sensors @@ -253,25 +251,35 @@ void gn_ds18b20_task(gn_leaf_handle_t leaf_config) { _scan_sensors(gpio, &data->sensor_count, &data->addrs[0]); //create a timer to update temps - esp_timer_create_args_t sensor_timer_args = - { .callback = &gn_ds18b20_temp_sensor_collect, .arg = leaf_config, .name = - "ds18b20_periodic" }; + esp_timer_create_args_t sensor_timer_args = { .callback = + &gn_ds18b20_temp_sensor_collect, .arg = leaf_config, .name = + "ds18b20_periodic" }; esp_err_t ret = esp_timer_create(&sensor_timer_args, &data->sensor_timer); if (ret != ESP_OK) { - gn_log(TAG, GN_LOG_ERROR, "[%s] failed to init ds18b20 leaf timer", leaf_name); + gn_log(TAG, GN_LOG_ERROR, "[%s] failed to init ds18b20 leaf timer", + leaf_name); descriptor->status = GN_LEAF_STATUS_ERROR; } if (ret == ESP_OK && active == true) { - ret = esp_timer_start_periodic(data->sensor_timer, - update_time_sec * 1000000); + //first shot immediate + gn_ds18b20_temp_sensor_collect(leaf_config); + + //then start the periodic wakeup + if (ret == ESP_OK) { + ret = esp_timer_start_periodic(data->sensor_timer, + update_time_sec * 1000000); + } + if (ret != ESP_OK) { - gn_log(TAG, GN_LOG_ERROR, "[%s] failed to start ds18b20 leaf timer", leaf_name); + gn_log(TAG, GN_LOG_ERROR, "[%s] failed to start ds18b20 leaf timer", + leaf_name); gn_leaf_get_descriptor(leaf_config)->status = GN_LEAF_STATUS_ERROR; - gn_leaf_param_write_bool(data->active_param, GN_DS18B20_PARAM_ACTIVE, - false); + gn_leaf_param_write_bool(data->active_param, + GN_DS18B20_PARAM_ACTIVE, + false); descriptor->status = GN_LEAF_STATUS_ERROR; } } @@ -425,8 +433,8 @@ void gn_ds18b20_task(gn_leaf_handle_t leaf_config) { leaf_name, evt.param_name, evt.data); //parameter is update time - if (gn_leaf_event_mask_param(&evt, - data->update_time_param) == 0) { + if (gn_leaf_event_mask_param(&evt, data->update_time_param) + == 0) { gn_leaf_param_write_double(leaf_config, GN_DS18B20_PARAM_UPDATE_TIME_SEC, (double) atof(evt.data)); @@ -434,14 +442,15 @@ void gn_ds18b20_task(gn_leaf_handle_t leaf_config) { esp_timer_start_periodic(data->sensor_timer, update_time_sec * 1000000); - } else if (gn_leaf_event_mask_param(&evt, - data->active_param) == 0) { + } else if (gn_leaf_event_mask_param(&evt, data->active_param) + == 0) { bool prev_active = active; int _active = atoi(evt.data); //execute change - gn_leaf_param_write_bool(leaf_config, GN_DS18B20_PARAM_ACTIVE, + gn_leaf_param_write_bool(leaf_config, + GN_DS18B20_PARAM_ACTIVE, _active == 0 ? false : true); active = _active; @@ -453,8 +462,8 @@ void gn_ds18b20_task(gn_leaf_handle_t leaf_config) { update_time_sec * 1000000); } - } else if (gn_leaf_event_mask_param(&evt, - data->gpio_param) == 0) { + } else if (gn_leaf_event_mask_param(&evt, data->gpio_param) + == 0) { //check limits int _gpio = atoi(evt.data); diff --git a/main/main.c b/main/main.c index b22a0903..b985c0c7 100644 --- a/main/main.c +++ b/main/main.c @@ -59,8 +59,9 @@ void app_main(void) { .server_discovery_prefix = "homeassistant", .firmware_url = "http://myserver/myfirmware.bin", .sntp_url = "pool.ntp.org", - .sleep_delay_millisec = 10000LL, - .wakeup_time_millisec = 30000LL, + .wakeup_time_millisec = 2000LL, + .sleep_delay_millisec = 50LL, + .sleep_time_millisec = 10000LL, .sleep_mode = GN_SLEEP_MODE_DEEP }; From 613dbab3944ca7095efa9e3c79fabcb48867fbeb Mon Sep 17 00:00:00 2001 From: ogghst Date: Thu, 17 Feb 2022 13:32:08 +0100 Subject: [PATCH 04/31] bug fixing in sleep modes --- components/grownode/boards/gn_blink.c | 7 +- components/grownode/gn_mqtt_protocol.c | 70 +++--- components/grownode/gn_mqtt_protocol.h | 2 +- components/grownode/gn_network.c | 7 +- components/grownode/grownode.c | 289 ++++++++++++++----------- components/grownode/leaves/gn_gpio.c | 39 ++-- main/main.c | 11 +- 7 files changed, 241 insertions(+), 184 deletions(-) diff --git a/components/grownode/boards/gn_blink.c b/components/grownode/boards/gn_blink.c index 34d49d83..8b569960 100644 --- a/components/grownode/boards/gn_blink.c +++ b/components/grownode/boards/gn_blink.c @@ -24,14 +24,15 @@ void led_blink_callback(const gn_leaf_handle_t blink) { - bool status; + bool status = false; //gets the previous parameter status gn_leaf_param_get_bool(blink, GN_GPIO_PARAM_TOGGLE, &status); + ESP_LOGD(TAG, "blinking - old status = %d", status); //invert the status status = !status; - ESP_LOGI(TAG, "blinking - %d", status); + ESP_LOGI(TAG, "blinking - new status = %d", status); //set the new parameter gn_leaf_param_set_bool(blink, GN_GPIO_PARAM_TOGGLE, status); @@ -48,6 +49,8 @@ void led_blink_callback(const gn_leaf_handle_t blink) { */ void gn_configure_blink(gn_node_handle_t node) { + esp_log_level_set("gn_leaf_gpio", esp_log_level_get(TAG)); + //fastcreate call gn_leaf_handle_t blink = gn_gpio_fastcreate(node, "blink", 2, false, false); diff --git a/components/grownode/gn_mqtt_protocol.c b/components/grownode/gn_mqtt_protocol.c index 5cae300e..3e842ec5 100755 --- a/components/grownode/gn_mqtt_protocol.c +++ b/components/grownode/gn_mqtt_protocol.c @@ -35,6 +35,8 @@ EventGroupHandle_t _gn_event_group_mqtt; const int _GN_MQTT_CONNECTED_OK_EVENT_BIT = BIT0; const int _GN_MQTT_CONNECTED_KO_EVENT_BIT = BIT1; +const int _GN_MQTT_DEBUG_WAIT_MS = 500; + //static gn_server_status_t status = GN_SERVER_DISCONNECTED; //gn_config_handle_intl_t _config; //TODO shared pointer, dangerous @@ -254,7 +256,7 @@ void _gn_mqtt_build_command_topic(gn_config_handle_intl_t config, char *buf) { * * @return status of the operation */ -gn_err_t gn_mqtt_publish_leaf(gn_leaf_handle_t _leaf_config) { +gn_err_t gn_mqtt_subscribe_leaf(gn_leaf_handle_t _leaf_config) { if (!_leaf_config) return GN_RET_ERR; @@ -279,20 +281,19 @@ gn_err_t gn_mqtt_publish_leaf(gn_leaf_handle_t _leaf_config) { if (!config) return GN_RET_ERR; - ESP_LOGD(TAG, "publishing leaf %s", leaf_config->name); - char topic[_GN_MQTT_MAX_TOPIC_LENGTH]; _gn_mqtt_build_leaf_command_topic(leaf_config, topic); - ESP_LOGD(TAG, "subscribing leaf. topic: %s", topic); + if(esp_log_level_get(TAG) == ESP_LOG_DEBUG) { + ESP_LOGD(TAG, "gn_mqtt_subscribe_leaf - topic = %s. now waiting %d ms", topic, _GN_MQTT_DEBUG_WAIT_MS); + vTaskDelay(_GN_MQTT_DEBUG_WAIT_MS / portTICK_PERIOD_MS); + } if (esp_mqtt_client_subscribe(config->mqtt_client, topic, 0) == -1) { ESP_LOGE(TAG, "subscribing error"); return GN_RET_ERR_MQTT_SUBSCRIBE; } - ESP_LOGD(TAG, "sent subscribe successful, topic = %s", topic); - //notify if (config->config_init_params->server_discovery) { @@ -302,7 +303,7 @@ gn_err_t gn_mqtt_publish_leaf(gn_leaf_handle_t _leaf_config) { char *_d_payload = calloc(_GN_MQTT_MAX_PAYLOAD_LENGTH + 1, sizeof(char)); - ESP_LOGD(TAG, "gn_mqtt_send_node_config - building node config: %s", + ESP_LOGD(TAG, "gn_mqtt_subscribe_leaf - building node config: %s", _node_config->name); gn_leaf_param_handle_intl_t _param = @@ -392,7 +393,7 @@ gn_err_t gn_mqtt_subscribe_leaf_param(gn_leaf_param_handle_t _param) { gn_config_handle_intl_t config = (gn_config_handle_intl_t) node_config->config; - ESP_LOGD(TAG, "subscribing param %s on %s", param->name, leaf_config->name); + //ESP_LOGD(TAG, "subscribing param %s on %s", param->name, leaf_config->name); char topic[_GN_MQTT_MAX_TOPIC_LENGTH]; _gn_mqtt_build_leaf_parameter_command_topic(leaf_config, param->name, @@ -401,10 +402,14 @@ gn_err_t gn_mqtt_subscribe_leaf_param(gn_leaf_param_handle_t _param) { ESP_LOGD(TAG, "gn_mqtt_subscribe_leaf_param. topic: %s", topic); int msg_id = esp_mqtt_client_subscribe(config->mqtt_client, topic, 0); - ESP_LOGD(TAG, "sent subscribe successful, topic = %s, msg_id=%d", topic, - msg_id); - return GN_RET_OK; + if(esp_log_level_get(TAG) == ESP_LOG_DEBUG) { + ESP_LOGD(TAG, "gn_mqtt_subscribe_leaf_param, topic = %s, msg_id=%d. now waiting %d ms", topic, + msg_id, _GN_MQTT_DEBUG_WAIT_MS); + vTaskDelay(_GN_MQTT_DEBUG_WAIT_MS / portTICK_PERIOD_MS); + } + + return msg_id == -1? GN_RET_ERR: GN_RET_OK; #else return GN_RET_OK; @@ -572,7 +577,7 @@ gn_err_t gn_mqtt_send_leaf_param(gn_leaf_param_handle_t _param) { if (_node_config->config->status != GN_NODE_STATUS_STARTED) return GN_RET_OK; - ESP_LOGI(TAG, "gn_mqtt_send_leaf_param %s", param->name); + ESP_LOGD(TAG, "gn_mqtt_send_leaf_param %s", param->name); int ret = GN_RET_OK; @@ -623,16 +628,20 @@ gn_err_t gn_mqtt_send_leaf_param(gn_leaf_param_handle_t _param) { if (msg_id == -1) goto fail; - ESP_LOGD(TAG, "sent publish successful, msg_id=%d, topic=%s, payload=%s", - msg_id, _topic, buf); + + if(esp_log_level_get(TAG) == ESP_LOG_DEBUG) { + ESP_LOGD(TAG, "sent publish successful, msg_id=%d, topic=%s, payload=%s. now waiting %d ms", + msg_id, _topic, buf, _GN_MQTT_DEBUG_WAIT_MS); + vTaskDelay(_GN_MQTT_DEBUG_WAIT_MS / portTICK_PERIOD_MS); + } fail: { free(buf); return ((msg_id == -1) ? (GN_RET_ERR_MQTT_ERROR) : (GN_RET_OK)); } - //TODO remove - vTaskDelay(10 / portTICK_PERIOD_MS); + + return ret; @@ -1058,11 +1067,17 @@ gn_err_t gn_mqtt_send_leaf_message(gn_leaf_handle_t _leaf, const char *msg) { char buf[_GN_MQTT_MAX_TOPIC_LENGTH]; _gn_mqtt_build_command_topic(node_config->config, buf); -//publish - ESP_LOGD(TAG, "publish topic %s, msg=%s", buf, msg); int msg_id = esp_mqtt_client_publish(config->mqtt_client, buf, msg, 0, 0, 0); +//publish + if(esp_log_level_get(TAG) == ESP_LOG_DEBUG) { + ESP_LOGD(TAG, "publish topic %s, msg=%s. now waiting %d ms" + , buf, msg, _GN_MQTT_DEBUG_WAIT_MS); + vTaskDelay(_GN_MQTT_DEBUG_WAIT_MS / portTICK_PERIOD_MS); + } + + return ((msg_id == -1) ? (GN_RET_ERR_MQTT_ERROR) : (GN_RET_OK)); #else @@ -1107,8 +1122,7 @@ esp_err_t _gn_mqtt_on_connected(gn_config_handle_t config) { goto fail; } - return xEventGroupSetBits(_gn_event_group_mqtt, - _GN_MQTT_CONNECTED_OK_EVENT_BIT); + return ESP_OK; fail: @@ -1140,9 +1154,6 @@ esp_err_t _gn_mqtt_on_disconnected(gn_config_handle_t config) { ESP_LOGE(TAG, "failed to send GN_SERVER_DISCONNECTED_EVENT event"); } - ESP_LOGD(TAG, "_GN_MQTT_CONNECTED_KO_EVENT_BIT"); - - xEventGroupSetBits(_gn_event_group_mqtt, _GN_MQTT_CONNECTED_KO_EVENT_BIT); return ESP_OK; #else @@ -1174,7 +1185,8 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, switch ((esp_mqtt_event_id_t) event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGD(TAG, "MQTT_EVENT_CONNECTED"); - _gn_mqtt_on_connected(config); + xEventGroupSetBits(_gn_event_group_mqtt, + _GN_MQTT_CONNECTED_OK_EVENT_BIT); /* msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, @@ -1193,9 +1205,13 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, break; case MQTT_EVENT_DISCONNECTED: ESP_LOGD(TAG, "MQTT_EVENT_DISCONNECTED"); + xEventGroupSetBits(_gn_event_group_mqtt, + _GN_MQTT_CONNECTED_KO_EVENT_BIT); _gn_mqtt_on_disconnected(config); + break; + /* case MQTT_EVENT_SUBSCRIBED: ESP_LOGD(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); break; @@ -1205,6 +1221,7 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, case MQTT_EVENT_PUBLISHED: ESP_LOGD(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; + */ case MQTT_EVENT_DATA: //TODO here the code to forward the call to appropriate node/leaf or system handler. start from remote OTA and RST ESP_LOGD(TAG, "MQTT_EVENT_DATA"); @@ -1343,7 +1360,7 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, } break; default: - ESP_LOGD(TAG, "Other event id:%d", event->event_id); + //ESP_LOGD(TAG, "Other event id:%d", event->event_id); break; } @@ -1412,6 +1429,7 @@ gn_err_t gn_mqtt_init(gn_config_handle_t config) { if ((uxBits & _GN_MQTT_CONNECTED_OK_EVENT_BIT) != 0) { ESP_LOGI(TAG, "MQTT connection successful"); + _gn_mqtt_on_connected(config); /* //publish server connected event if (ESP_OK @@ -1428,6 +1446,8 @@ gn_err_t gn_mqtt_init(gn_config_handle_t config) { else if ((uxBits & _GN_MQTT_CONNECTED_KO_EVENT_BIT) != 0) { ESP_LOGE(TAG, "MQTT connection error"); + + //publish server disconnected event /* if (ESP_OK diff --git a/components/grownode/gn_mqtt_protocol.h b/components/grownode/gn_mqtt_protocol.h index 65564d96..ae75874b 100755 --- a/components/grownode/gn_mqtt_protocol.h +++ b/components/grownode/gn_mqtt_protocol.h @@ -35,7 +35,7 @@ extern "C" { #define _GN_MQTT_DEFAULT_QOS 0 -gn_err_t gn_mqtt_publish_leaf(gn_leaf_handle_t leaf_config); +gn_err_t gn_mqtt_subscribe_leaf(gn_leaf_handle_t leaf_config); esp_err_t gn_mqtt_subscribe_leaf_param(gn_leaf_param_handle_t param); diff --git a/components/grownode/gn_network.c b/components/grownode/gn_network.c index 8d78c716..6c53a681 100755 --- a/components/grownode/gn_network.c +++ b/components/grownode/gn_network.c @@ -128,7 +128,7 @@ void _gn_wifi_event_handler(void *arg, esp_event_base_t event_base, memcpy(_conf->macAddress, eth_mac, 6); strcpy(_conf->deviceName, deviceName); - snprintf(log, 41, "%s-%d.%d.%d.%d", deviceName, + snprintf(log, 52, "board:%s -IP: %d.%d.%d.%d", deviceName, IP2STR(&event->ip_info.ip)); gn_log(TAG, GN_LOG_DEBUG, log); @@ -416,8 +416,9 @@ esp_err_t _gn_init_wifi(gn_config_handle_intl_t conf) { ESP_LOGD(TAG, "Wait for Wi-Fi connection"); /* Wait for Wi-Fi connection */ - //xEventGroupWaitBits(_gn_event_group_wifi, GN_WIFI_CONNECTED_EVENT, false, - //true, portMAX_DELAY); + xEventGroupWaitBits(_gn_event_group_wifi, GN_WIFI_CONNECTED_EVENT, false, + true, portMAX_DELAY); + fail: return ret; #else diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index 716bc221..adc46049 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -122,7 +122,7 @@ gn_err_t _gn_leaf_start(gn_leaf_config_handle_intl_t leaf_config) { //gn_display_leaf_start(leaf_config); //notice network of the leaf added - ret = gn_mqtt_publish_leaf(leaf_config); + ret = gn_mqtt_subscribe_leaf(leaf_config); ESP_LOGI(TAG, "_gn_start_leaf %s completed", leaf_config->name); return ret; @@ -293,7 +293,7 @@ void _gn_evt_handler(void *handler_args, esp_event_base_t base, int32_t id, break; case GN_SRV_CONNECTED_EVENT: - //start keepalive service + if (!_gn_default_conf) break; @@ -304,6 +304,7 @@ void _gn_evt_handler(void *handler_args, esp_event_base_t base, int32_t id, break; } + //start keepalive service _gn_keepalive_start(); break; @@ -381,7 +382,7 @@ esp_err_t _gn_init_keepalive_timer(gn_config_handle_intl_t conf) { ESP_LOGD(TAG, "_gn_init_keepalive_timer"); - if (conf->config_init_params->server_keepalive_timer_sec) + if (conf->config_init_params->server_keepalive_timer_sec == 0) return ESP_OK; timer_config_t config = { .divider = TIMER_DIVIDER, .counter_dir = @@ -547,7 +548,6 @@ gn_node_handle_intl_t _gn_node_config_create() { } - /** * @brief retrieves the configuration status * @@ -709,7 +709,6 @@ gn_err_t gn_node_start(gn_node_handle_t node) { ESP_LOGD(TAG, "gn_start_node: %s, leaves: %d", _node->name, _node->leaves.last); - _node->config->status = GN_NODE_STATUS_STARTED; if (ESP_OK @@ -729,14 +728,13 @@ gn_err_t gn_node_start(gn_node_handle_t node) { for (int i = 0; i < _node->leaves.last; i++) { //ESP_LOGD(TAG, "starting leaf: %d", i); if (_gn_leaf_start(_node->leaves.at[i]) != GN_RET_OK) { - gn_log(TAG, GN_LOG_ERROR, - "failed to start leaf: %s", _node->leaves.at[i]->name); + gn_log(TAG, GN_LOG_ERROR, "failed to start leaf: %s", + _node->leaves.at[i]->name); _node->config->status = GN_NODE_STATUS_ERROR; return GN_RET_ERR_NODE_NOT_STARTED; } } - //if first boot, send parameter status if (wakeup_reason == GN_SLEEP_MODE_NONE) ret = gn_send_node_leaf_param_status(node); @@ -765,7 +763,7 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { while (true) { vTaskDelay(1000 / portTICK_PERIOD_MS); - ESP_LOGD(TAG, + ESP_LOGI(TAG, "looping. grownode startup status: %s, sleep mode = %d", gn_get_status_description( ((gn_node_handle_intl_t )node)->config), @@ -774,35 +772,37 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { } else if (_node->config->config_init_params->sleep_mode == GN_SLEEP_MODE_DEEP) { - ESP_LOGD(TAG, - "working. grownode startup status: %s, sleep mode = %d, sleep in %"PRIu64" millisec", + ESP_LOGI(TAG, + "working. grownode startup status: %s, sleep mode = deep, sleep in %"PRIu64" millisec", gn_get_status_description( ((gn_node_handle_intl_t )node)->config), - _node->config->config_init_params->sleep_mode, _node->config->config_init_params->wakeup_time_millisec); vTaskDelay( _node->config->config_init_params->wakeup_time_millisec / portTICK_PERIOD_MS); gn_node_sleep(node, GN_SLEEP_MODE_DEEP, _node->config->config_init_params->sleep_time_millisec); + //not needed to cycle as the board will restart } else if (_node->config->config_init_params->sleep_mode == GN_SLEEP_MODE_LIGHT) { - ESP_LOGD(TAG, - "working. grownode startup status: %s, sleep mode = %d, sleep in %"PRIu64" millisec", - gn_get_status_description( - ((gn_node_handle_intl_t )node)->config), - _node->config->config_init_params->sleep_mode, - _node->config->config_init_params->wakeup_time_millisec); - vTaskDelay( - _node->config->config_init_params->wakeup_time_millisec - / portTICK_PERIOD_MS); - gn_node_sleep(node, GN_SLEEP_MODE_LIGHT, - _node->config->config_init_params->sleep_time_millisec); + while (true) { + ESP_LOGI(TAG, + "working. grownode startup status: %s, sleep mode = light, sleep in %"PRIu64" millisec", + gn_get_status_description( + ((gn_node_handle_intl_t )node)->config), + _node->config->config_init_params->wakeup_time_millisec); + vTaskDelay( + _node->config->config_init_params->wakeup_time_millisec + / portTICK_PERIOD_MS); + gn_node_sleep(node, GN_SLEEP_MODE_LIGHT, + _node->config->config_init_params->sleep_time_millisec); + ESP_LOGI(TAG, "waking up from light sleep"); + } + } return GN_RET_OK; } - void _gn_wait_for_blocked_leaves(gn_node_handle_intl_t _node) { //waits until all leaves has reached blocked status @@ -847,27 +847,24 @@ gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, != esp_event_post_to(_node->config->event_loop, GN_BASE_EVENT, GN_NODE_DEEP_SLEEP_START_EVENT, NULL, 0, portMAX_DELAY)) { - ESP_LOGD(TAG, + ESP_LOGE(TAG, "failed to send GN_NODE_DEEP_SLEEP_START_EVENT event"); return GN_RET_ERR_EVENT_LOOP_ERROR; } - ESP_LOGD(TAG, - "Preparing deep sleep in %"PRIu64" millisec", + ESP_LOGI(TAG, "Preparing deep sleep in %"PRIu64" millisec", _node->config->config_init_params->sleep_delay_millisec); //gives some time to handle the event if (_node->config->config_init_params->sleep_delay_millisec > 0) { - vTaskDelay( - _node->config->config_init_params->sleep_delay_millisec - / portTICK_PERIOD_MS); + vTaskDelay( + _node->config->config_init_params->sleep_delay_millisec + / portTICK_PERIOD_MS); } _gn_wait_for_blocked_leaves(_node); - ESP_LOGD(TAG, - "Entering deep sleep for %"PRIu64" millisec", - millisec); + ESP_LOGI(TAG, "Entering deep sleep for %"PRIu64" millisec", millisec); wakeup_reason = GN_SLEEP_MODE_DEEP; esp_deep_sleep(millisec * 1000LL); @@ -879,27 +876,24 @@ gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, != esp_event_post_to(_node->config->event_loop, GN_BASE_EVENT, GN_NODE_LIGHT_SLEEP_START_EVENT, NULL, 0, portMAX_DELAY)) { - ESP_LOGD(TAG, + ESP_LOGE(TAG, "failed to send GN_NODE_LIGHT_SLEEP_START_EVENT event"); return GN_RET_ERR_EVENT_LOOP_ERROR; } - ESP_LOGD(TAG, - "Preparing light sleep in %"PRIu64" millisec", + ESP_LOGI(TAG, "Preparing light sleep in %"PRIu64" millisec", _node->config->config_init_params->sleep_delay_millisec); //gives some time to handle the event if (_node->config->config_init_params->sleep_delay_millisec > 0) { - vTaskDelay( - _node->config->config_init_params->sleep_delay_millisec - / portTICK_PERIOD_MS); + vTaskDelay( + _node->config->config_init_params->sleep_delay_millisec + / portTICK_PERIOD_MS); } _gn_wait_for_blocked_leaves(_node); - ESP_LOGD(TAG, - "Entering light sleep for %"PRIu64" millisec", - millisec); + ESP_LOGI(TAG, "Entering light sleep for %"PRIu64" millisec", millisec); wakeup_reason = GN_SLEEP_MODE_LIGHT; esp_sleep_enable_timer_wakeup(millisec * 1000LL); @@ -1455,20 +1449,25 @@ gn_err_t gn_leaf_param_init_string(const gn_leaf_handle_t leaf_config, * @param val the value to set (null terminated) * * @return GN_RET_OK if the parameter is set - * @return GN_RET_ERR_INVALID_ARG in case of input errors + * @return GN_RET_ERR_INVALID_ARG in case of input errors or validation error * @return GN_RET_ERR in case of messaging error */ gn_err_t gn_leaf_param_write_string(const gn_leaf_handle_t leaf_config, const char *name, char *val) { - if (!leaf_config || !name || !val) + if (!leaf_config || !name) { + ESP_LOGD(TAG, "gn_leaf_param_write_string: GN_RET_ERR_INVALID_ARG"); return GN_RET_ERR_INVALID_ARG; + } gn_leaf_param_handle_intl_t _param = (gn_leaf_param_handle_intl_t) gn_leaf_param_get_param_handle( leaf_config, name); - if (!_param) + + if (!_param) { + ESP_LOGD(TAG, "gn_leaf_param_write_string: param not found"); return GN_RET_ERR_INVALID_ARG; + } //ESP_LOGD(TAG, "gn_leaf_param_set_string - param:%s value:%s", name, val); //ESP_LOGD(TAG, " old value %s", _param->param_val->v.s); @@ -1476,6 +1475,31 @@ gn_err_t gn_leaf_param_write_string(const gn_leaf_handle_t leaf_config, gn_leaf_config_handle_intl_t _leaf_config = (gn_leaf_config_handle_intl_t) leaf_config; + if (_param->validator) { + char **validate = &val; + gn_leaf_param_validator_result_t ret = _param->validator(_param, + (void**) validate); + if (ret != GN_LEAF_PARAM_VALIDATOR_ERROR_GENERIC + && ret != GN_LEAF_PARAM_VALIDATOR_ERROR_NOT_ALLOWED) { + _param->param_val->v.s = (char*) realloc(_param->param_val->v.s, + sizeof(char) * (strlen(*validate) + 1)); + memset(_param->param_val->v.s, 0, + sizeof(char) * (strlen(*validate) + 1)); + strncpy(_param->param_val->v.s, *validate, strlen(*validate)); + } else { + ESP_LOGD(TAG, + "gn_leaf_param_write_string: validation error. code %d", + (int )ret); + return GN_RET_ERR_INVALID_ARG; + } +//ESP_LOGD(TAG, "processing validator - result: %d", (int )ret); + } else { + _param->param_val->v.s = (char*) realloc(_param->param_val->v.s, + sizeof(char) * (strlen(val) + 1)); + memset(_param->param_val->v.s, 0, sizeof(char) * (strlen(val) + 1)); + strncpy(_param->param_val->v.s, val, strlen(val)); + } + if (_param->storage == GN_LEAF_PARAM_STORAGE_PERSISTED) { int _len = (strlen(_leaf_config->name) + strlen(name) + 2); @@ -1502,27 +1526,8 @@ gn_err_t gn_leaf_param_write_string(const gn_leaf_handle_t leaf_config, } - if (_param->validator) { - char **validate = &val; - gn_leaf_param_validator_result_t ret = _param->validator(_param, - (void**) validate); - if (ret != GN_LEAF_PARAM_VALIDATOR_ERROR_GENERIC - && ret != GN_LEAF_PARAM_VALIDATOR_ERROR_NOT_ALLOWED) { - _param->param_val->v.s = (char*) realloc(_param->param_val->v.s, - sizeof(char) * (strlen(*validate) + 1)); - memset(_param->param_val->v.s, 0, - sizeof(char) * (strlen(*validate) + 1)); - strncpy(_param->param_val->v.s, *validate, strlen(*validate)); - } -//ESP_LOGD(TAG, "processing validator - result: %d", (int )ret); - } else { - _param->param_val->v.s = (char*) realloc(_param->param_val->v.s, - sizeof(char) * (strlen(val) + 1)); - memset(_param->param_val->v.s, 0, sizeof(char) * (strlen(val) + 1)); - strncpy(_param->param_val->v.s, val, strlen(val)); - } - - //ESP_LOGD(TAG, "gn_leaf_param_set - result %s", _param->param_val->v.s); + ESP_LOGD(TAG, "gn_leaf_param_write_string - result %s", + _param->param_val->v.s); //notify event loop gn_leaf_parameter_event_t evt; @@ -1536,9 +1541,9 @@ gn_err_t gn_leaf_param_write_string(const gn_leaf_handle_t leaf_config, _leaf_config->node_config->config->event_loop, GN_BASE_EVENT, evt.id, &evt, sizeof(evt), portMAX_DELAY); if (ret != ESP_OK) { - gn_log(TAG, GN_LOG_ERROR, - "gn_leaf_param_set_string - not possible to send param message to event loop - id:%d, size:%d - result: %d", - evt.id, sizeof(evt), (int) ret); + ESP_LOGD(TAG, + "gn_leaf_param_write_string - not possible to send param message to event loop - id:%d, size:%d - result: %d", + evt.id, sizeof(evt), (int ) ret); return GN_RET_ERR; } @@ -1695,19 +1700,24 @@ gn_err_t gn_leaf_param_init_bool(const gn_leaf_handle_t leaf_config, * @param val the value to set (null terminated) * * @return GN_RET_OK if the parameter is set - * @return GN_RET_ERR_INVALID_ARG in case of input errors + * @return GN_RET_ERR_INVALID_ARG in case of input errors or validation error */ gn_err_t gn_leaf_param_write_bool(const gn_leaf_handle_t leaf_config, const char *name, bool val) { - if (!leaf_config || !name) + if (!leaf_config || !name) { + ESP_LOGD(TAG, "gn_leaf_param_write_bool: GN_RET_ERR_INVALID_ARG"); return GN_RET_ERR_INVALID_ARG; + } gn_leaf_param_handle_intl_t _param = (gn_leaf_param_handle_intl_t) gn_leaf_param_get_param_handle( leaf_config, name); - if (!_param) + + if (!_param) { + ESP_LOGD(TAG, "gn_leaf_param_write_bool: param not found"); return GN_RET_ERR_INVALID_ARG; + } //ESP_LOGD(TAG, "gn_leaf_param_set_bool %s %d", name, val); //ESP_LOGD(TAG, " old value %d", _param->param_val->v.b); @@ -1715,6 +1725,27 @@ gn_err_t gn_leaf_param_write_bool(const gn_leaf_handle_t leaf_config, gn_leaf_config_handle_intl_t _leaf_config = (gn_leaf_config_handle_intl_t) leaf_config; + if (_param->validator) { + bool *p = &val; + bool **validate = &p; + gn_leaf_param_validator_result_t val_ret = _param->validator(_param, + (void**) validate); + if (val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_GENERIC + && val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_NOT_ALLOWED) { + _param->param_val->v.b = **validate; + //ESP_LOGD(TAG, "processing validator - result: %d", (int )ret); + } else { + ESP_LOGD(TAG, "gn_leaf_param_write_bool: validation error. code %d", + val_ret); + return GN_RET_ERR_INVALID_ARG; + } + } else { + _param->param_val->v.b = val; + } + + ESP_LOGD(TAG, "gn_leaf_param_write_bool: after validator: %d", + _param->param_val->v.b); + if (_param->storage == GN_LEAF_PARAM_STORAGE_PERSISTED) { int _len = (strlen(_leaf_config->name) + strlen(name) + 2); @@ -1742,20 +1773,8 @@ gn_err_t gn_leaf_param_write_bool(const gn_leaf_handle_t leaf_config, } - if (_param->validator) { - bool *p = &val; - bool **validate = &p; - gn_leaf_param_validator_result_t val_ret = _param->validator(_param, - (void**) validate); - if (val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_GENERIC - && val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_NOT_ALLOWED) { - _param->param_val->v.b = **validate; - //ESP_LOGD(TAG, "processing validator - result: %d", (int )ret); - } else { - _param->param_val->v.b = val; - } - } - //ESP_LOGD(TAG, "gn_leaf_param_set - result %d", _param->param_val->v.b); + ESP_LOGD(TAG, "gn_leaf_param_write_bool - result %d", + _param->param_val->v.b); //notify event loop gn_leaf_parameter_event_t evt; @@ -1774,9 +1793,9 @@ gn_err_t gn_leaf_param_write_bool(const gn_leaf_handle_t leaf_config, evt.id, &evt, sizeof(evt), portMAX_DELAY); if (ret != ESP_OK) { - gn_log(TAG, GN_LOG_ERROR, - "gn_leaf_param_set_bool - not possible to send param message to event loop - id:%d, size:%d - result: %d", - evt.id, sizeof(evt), (int) ret); + ESP_LOGD(TAG, + "gn_leaf_param_write_bool - not possible to send param message to event loop - id:%d, size:%d - result: %d", + evt.id, sizeof(evt), (int ) ret); return GN_RET_ERR; } @@ -1974,19 +1993,23 @@ gn_err_t gn_leaf_param_init_double(const gn_leaf_handle_t leaf_config, * @param val the value to set * * @return GN_RET_OK if the parameter is set - * @return GN_RET_ERR_INVALID_ARG in case of input errors + * @return GN_RET_ERR_INVALID_ARG in case of input errors or validation errors */ gn_err_t gn_leaf_param_write_double(const gn_leaf_handle_t leaf_config, const char *name, double val) { - if (!leaf_config || !name) - return ESP_ERR_INVALID_ARG; + if (!leaf_config || !name) { + ESP_LOGD(TAG, "gn_leaf_param_write_double: GN_RET_ERR_INVALID_ARG"); + return GN_RET_ERR_INVALID_ARG; + } gn_leaf_param_handle_intl_t _param = (gn_leaf_param_handle_intl_t) gn_leaf_param_get_param_handle( leaf_config, name); + if (!_param) { - return ESP_ERR_INVALID_ARG; + ESP_LOGD(TAG, "gn_leaf_param_write_double: param not found"); + return GN_RET_ERR_INVALID_ARG; } //ESP_LOGD(TAG, "gn_leaf_param_set_double %s %g", name, val); @@ -1995,6 +2018,25 @@ gn_err_t gn_leaf_param_write_double(const gn_leaf_handle_t leaf_config, gn_leaf_config_handle_intl_t _leaf_config = (gn_leaf_config_handle_intl_t) leaf_config; + if (_param->validator) { + double *p = &val; + double **validate = &p; + gn_leaf_param_validator_result_t val_ret = _param->validator(_param, + (void**) validate); + if (val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_GENERIC + && val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_NOT_ALLOWED) { + _param->param_val->v.d = **validate; + } else { + ESP_LOGD(TAG, + "gn_leaf_param_write_double: validation error. code %d", + val_ret); + return GN_RET_ERR_INVALID_ARG; + } +//ESP_LOGD(TAG, "processing validator - result: %d", (int )ret); + } else { + _param->param_val->v.d = val; + } + if (_param->storage == GN_LEAF_PARAM_STORAGE_PERSISTED) { int _len = (strlen(_leaf_config->name) + strlen(name) + 2); @@ -2022,20 +2064,8 @@ gn_err_t gn_leaf_param_write_double(const gn_leaf_handle_t leaf_config, } - if (_param->validator) { - double *p = &val; - double **validate = &p; - gn_leaf_param_validator_result_t val_ret = _param->validator(_param, - (void**) validate); - if (val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_GENERIC - && val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_NOT_ALLOWED) - _param->param_val->v.d = **validate; -//ESP_LOGD(TAG, "processing validator - result: %d", (int )ret); - } else { - _param->param_val->v.d = val; - } - - //ESP_LOGD(TAG, "gn_leaf_param_set - result %g", _param->param_val->v.d); + ESP_LOGD(TAG, "gn_leaf_param_write_double - result %g", + _param->param_val->v.d); //notify event loop gn_leaf_parameter_event_t evt; @@ -2049,9 +2079,9 @@ gn_err_t gn_leaf_param_write_double(const gn_leaf_handle_t leaf_config, evt.id, &evt, sizeof(evt), portMAX_DELAY); if (ret != ESP_OK) { - gn_log(TAG, GN_LOG_ERROR, - "gn_leaf_param_set_double - not possible to send param message to event loop - id:%d, size:%d - result: %d", - evt.id, sizeof(evt), (int) ret); + ESP_LOGD(TAG, + "gn_leaf_param_write_double - not possible to send param message to event loop - id:%d, size:%d - result: %d", + evt.id, sizeof(evt), (int ) ret); return GN_RET_ERR; } @@ -2153,7 +2183,7 @@ gn_err_t _gn_leaf_parameter_update(const gn_leaf_handle_t leaf_config, //check if has write access if (leaf_params->access != GN_LEAF_PARAM_ACCESS_NETWORK && leaf_params->access != GN_LEAF_PARAM_ACCESS_ALL) { - gn_log(TAG, GN_LOG_ERROR, + ESP_LOGW(TAG, "gn_leaf_parameter_update - paramater has no WRITE access, change discarded"); return GN_RET_ERR_LEAF_PARAM_ACCESS_VIOLATION; } @@ -2367,7 +2397,7 @@ gn_err_t gn_leaf_param_set_bool(const gn_leaf_handle_t leaf_config, const char *name, bool val) { if (leaf_config == NULL || name == NULL) { - gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_send_bool - invalid args"); + gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_set_bool - invalid args"); return GN_RET_ERR_INVALID_ARG; } gn_leaf_config_handle_intl_t _leaf_config = @@ -2397,7 +2427,7 @@ gn_err_t gn_leaf_param_set_bool(const gn_leaf_handle_t leaf_config, gn_err_t gn_leaf_param_set_double(const gn_leaf_handle_t leaf_config, const char *name, double val) { if (leaf_config == NULL || name == NULL) { - gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_send_bool - invalid args"); + gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_set_double - invalid args"); return GN_RET_ERR_INVALID_ARG; } gn_leaf_config_handle_intl_t _leaf_config = @@ -2427,7 +2457,7 @@ gn_err_t gn_leaf_param_set_double(const gn_leaf_handle_t leaf_config, gn_err_t gn_leaf_param_set_string(const gn_leaf_handle_t leaf_config, const char *name, char *val) { if (leaf_config == NULL || name == NULL) { - gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_send_bool - invalid args"); + gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_set_string - invalid args"); return GN_RET_ERR_INVALID_ARG; } gn_leaf_config_handle_intl_t _leaf_config = @@ -2468,13 +2498,18 @@ gn_leaf_param_handle_t gn_leaf_get_params(gn_leaf_handle_t leaf_config) { gn_leaf_param_handle_t gn_leaf_param_get_param_handle( const gn_leaf_handle_t leaf_config, const char *param_name) { - if (leaf_config == NULL || param_name == NULL) { - gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_get incorrect parameters"); + if (!leaf_config || !param_name) { + ESP_LOGD(TAG, "gn_leaf_param_get_param_handle invaoid arguments"); return NULL; } gn_leaf_param_handle_intl_t param = ((gn_leaf_config_handle_intl_t) leaf_config)->params; + if (!param) { + ESP_LOGD(TAG, "gn_leaf_param_get_param_handle incorrect parameter"); + return NULL; + } + while (param) { //ESP_LOGD(TAG, // "gn_leaf_param_get_param_handle - comparing %s (%d) and checking %s (%d)", @@ -2799,10 +2834,6 @@ gn_config_handle_t gn_init(gn_config_init_param_t *config_init) { ESP_GOTO_ON_ERROR(_gn_evt_handlers_register(_gn_default_conf), err, TAG, "error _gn_register_event_handlers: %s", esp_err_to_name(ret)); -//heartbeat to check network comm and send periodical system watchdog to the network - ESP_GOTO_ON_ERROR(_gn_init_keepalive_timer(_gn_default_conf), err, TAG, - "error on timer init: %s", esp_err_to_name(ret)); - //init display #ifdef CONFIG_GROWNODE_DISPLAY_ENABLED ESP_GOTO_ON_ERROR(gn_init_display(_gn_default_conf), err, TAG, @@ -2810,18 +2841,24 @@ gn_config_handle_t gn_init(gn_config_init_param_t *config_init) { #endif #if CONFIG_GROWNODE_WIFI_ENABLED -//init wifi + + //heartbeat to check network comm and send periodical system watchdog to the network + ESP_GOTO_ON_ERROR(_gn_init_keepalive_timer(_gn_default_conf), err, TAG, + "error on timer init: %s", esp_err_to_name(ret)); + + //init wifi ESP_GOTO_ON_ERROR(_gn_init_wifi(_gn_default_conf), err_net, TAG, "error on wifi init: %s", esp_err_to_name(ret)); -//init time sync. note: if bad, continue + //init time sync. note: if bad, continue ESP_GOTO_ON_ERROR(_gn_init_time_sync(_gn_default_conf), err_timesync, TAG, "error on time sync init: %s", esp_err_to_name(ret)); err_timesync: -//init mqtt system + //init mqtt system ESP_GOTO_ON_ERROR(gn_mqtt_init(_gn_default_conf), err_srv, TAG, "error on server init: %s", esp_err_to_name(ret)); + #endif ESP_LOGI(TAG, "grownode startup sequence completed!"); @@ -2875,7 +2912,7 @@ gn_err_t gn_storage_set(const char *key, const void *value, // Open err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle); if (err != ESP_OK) { - ESP_LOGE(TAG_NVS, "gn_storage_set() failed -nvs_open() error: %d", err); + ESP_LOGD(TAG_NVS, "gn_storage_set() failed -nvs_open() error: %d", err); goto fail; } @@ -2886,7 +2923,7 @@ gn_err_t gn_storage_set(const char *key, const void *value, err = nvs_set_blob(my_handle, _hashedkey, value, required_size); if (err != ESP_OK) { - ESP_LOGE(TAG_NVS, "gn_storage_set() failed -nvs_set_blob() error: %d", + ESP_LOGD(TAG_NVS, "gn_storage_set() failed -nvs_set_blob() error: %d", err); goto fail; } @@ -2894,7 +2931,7 @@ gn_err_t gn_storage_set(const char *key, const void *value, // Commit err = nvs_commit(my_handle); if (err != ESP_OK) { - ESP_LOGE(TAG_NVS, "gn_storage_set() failed -nvs_commit() error: %d", + ESP_LOGD(TAG_NVS, "gn_storage_set() failed -nvs_commit() error: %d", err); goto fail; } diff --git a/components/grownode/leaves/gn_gpio.c b/components/grownode/leaves/gn_gpio.c index af6af737..40b787dd 100644 --- a/components/grownode/leaves/gn_gpio.c +++ b/components/grownode/leaves/gn_gpio.c @@ -72,8 +72,8 @@ gn_leaf_handle_t gn_gpio_fastcreate(gn_node_handle_t node, } //creates the blink leave - gn_leaf_handle_t leaf = gn_leaf_create(node, leaf_name, - gn_gpio_config, 4096); + gn_leaf_handle_t leaf = gn_leaf_create(node, leaf_name, gn_gpio_config, + 4096); if (leaf == NULL) { ESP_LOGE(TAG, "gn_gpio_fastcreate - cannot create leaf %s", leaf_name); @@ -91,7 +91,7 @@ gn_leaf_handle_t gn_gpio_fastcreate(gn_node_handle_t node, void gn_gpio_task(gn_leaf_handle_t leaf_config); typedef struct { - gn_leaf_param_handle_t gn_gpio_status_param; + gn_leaf_param_handle_t gn_gpio_toggle_param; gn_leaf_param_handle_t gn_gpio_inverted_param; gn_leaf_param_handle_t gn_gpio_gpio_param; } gn_gpio_data_t; @@ -106,7 +106,7 @@ gn_leaf_descriptor_handle_t gn_gpio_config(gn_leaf_handle_t leaf_config) { gn_gpio_data_t *data = malloc(sizeof(gn_gpio_data_t)); - data->gn_gpio_status_param = gn_leaf_param_create(leaf_config, + data->gn_gpio_toggle_param = gn_leaf_param_create(leaf_config, GN_GPIO_PARAM_TOGGLE, GN_VAL_TYPE_BOOLEAN, (gn_val_t ) { .b = false }, GN_LEAF_PARAM_ACCESS_NETWORK, GN_LEAF_PARAM_STORAGE_PERSISTED, NULL); @@ -121,7 +121,7 @@ gn_leaf_descriptor_handle_t gn_gpio_config(gn_leaf_handle_t leaf_config) { GN_LEAF_PARAM_ACCESS_NETWORK, GN_LEAF_PARAM_STORAGE_PERSISTED, NULL); - gn_leaf_param_add_to_leaf(leaf_config, data->gn_gpio_status_param); + gn_leaf_param_add_to_leaf(leaf_config, data->gn_gpio_toggle_param); gn_leaf_param_add_to_leaf(leaf_config, data->gn_gpio_inverted_param); gn_leaf_param_add_to_leaf(leaf_config, data->gn_gpio_gpio_param); @@ -231,12 +231,14 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { evt.param_name, evt.data); //parameter is status - if (gn_leaf_event_mask_param(&evt, data->gn_gpio_status_param) + if (gn_leaf_event_mask_param(&evt, data->gn_gpio_toggle_param) == 0) { int _active = atoi(evt.data); //notify change + ESP_LOGD(TAG, "written: %d", _active); + gn_leaf_param_write_bool(leaf_config, GN_GPIO_PARAM_TOGGLE, _active == 0 ? false : true); @@ -247,12 +249,8 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { inverted ? 1 : 0); //update sensor using the parameter values - if (gn_gpio_state == GN_GPIO_STATE_RUNNING) { - gpio_set_level((int) gpio, - status ? - (inverted ? 0 : 1) : - (inverted ? 1 : 0)); - } + gpio_set_level((int) gpio, + status ? (inverted ? 0 : 1) : (inverted ? 1 : 0)); #ifdef CONFIG_GROWNODE_DISPLAY_ENABLED if (pdTRUE == gn_display_leaf_refresh_start()) { @@ -271,7 +269,8 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { int _inverted = atoi(evt.data); //notify change - gn_leaf_param_write_bool(leaf_config, GN_GPIO_PARAM_INVERTED, + gn_leaf_param_write_bool(leaf_config, + GN_GPIO_PARAM_INVERTED, _inverted == 0 ? false : true); inverted = _inverted; @@ -281,12 +280,8 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { inverted ? 1 : 0); //update sensor using the parameter values - if (gn_gpio_state == GN_GPIO_STATE_RUNNING) { - gpio_set_level((int) gpio, - status ? - (inverted ? 0 : 1) : - (inverted ? 1 : 0)); - } + gpio_set_level((int) gpio, + status ? (inverted ? 0 : 1) : (inverted ? 1 : 0)); #ifdef CONFIG_GROWNODE_DISPLAY_ENABLED if (pdTRUE == gn_display_leaf_refresh_start()) { @@ -310,17 +305,17 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { //what to do when network is disconnected case GN_NET_DISCONNECTED_EVENT: - gn_gpio_state = GN_GPIO_STATE_STOP; + //gn_gpio_state = GN_GPIO_STATE_STOP; break; //what to do when server is connected case GN_SRV_CONNECTED_EVENT: - gn_gpio_state = GN_GPIO_STATE_RUNNING; + //gn_gpio_state = GN_GPIO_STATE_RUNNING; break; //what to do when server is disconnected case GN_SRV_DISCONNECTED_EVENT: - gn_gpio_state = GN_GPIO_STATE_STOP; + //gn_gpio_state = GN_GPIO_STATE_STOP; break; default: diff --git a/main/main.c b/main/main.c index b985c0c7..379b44ec 100644 --- a/main/main.c +++ b/main/main.c @@ -22,7 +22,7 @@ #include "grownode.h" //include the board you want to start here -#include "gn_easypot1.h" +#include "gn_blink.h" #define TASK_STACK_SIZE 8192*4 @@ -46,6 +46,7 @@ void app_main(void) { //boards esp_log_level_set("gn_easypot1", ESP_LOG_INFO); + esp_log_level_set("gn_blink", ESP_LOG_INFO); gn_config_init_param_t config_init = { .provisioning_security = true, @@ -59,10 +60,10 @@ void app_main(void) { .server_discovery_prefix = "homeassistant", .firmware_url = "http://myserver/myfirmware.bin", .sntp_url = "pool.ntp.org", - .wakeup_time_millisec = 2000LL, + .wakeup_time_millisec = 20000LL, .sleep_delay_millisec = 50LL, - .sleep_time_millisec = 10000LL, - .sleep_mode = GN_SLEEP_MODE_DEEP + .sleep_time_millisec = 20000LL, + .sleep_mode = GN_SLEEP_MODE_LIGHT }; //creates the config handle @@ -79,7 +80,7 @@ void app_main(void) { gn_node_handle_t node = gn_node_create(config, "node"); //the board to start - gn_configure_easypot1(node); + gn_configure_blink(node); //finally, start node gn_node_start(node); From aeba30e201e3a9c3bbfd7f1dff29c798f010b99e Mon Sep 17 00:00:00 2001 From: ogghst Date: Sat, 19 Feb 2022 16:22:03 +0100 Subject: [PATCH 05/31] deep and light sleep tested, wifi and mqtt disconnection implemented --- components/grownode/boards/gn_blink.c | 2 +- components/grownode/gn_commons.h | 3 +- components/grownode/gn_mqtt_protocol.c | 277 ++++++++++++++++++------- components/grownode/gn_mqtt_protocol.h | 8 +- components/grownode/gn_network.c | 157 +++++++++++--- components/grownode/gn_network.h | 9 +- components/grownode/grownode.c | 220 ++++++++++++-------- components/grownode/grownode_intl.h | 3 +- components/grownode/leaves/gn_gpio.c | 5 +- 9 files changed, 495 insertions(+), 189 deletions(-) diff --git a/components/grownode/boards/gn_blink.c b/components/grownode/boards/gn_blink.c index 8b569960..5bc9834f 100644 --- a/components/grownode/boards/gn_blink.c +++ b/components/grownode/boards/gn_blink.c @@ -60,7 +60,7 @@ void gn_configure_blink(gn_node_handle_t node) { esp_timer_create_args_t timer_args = { .callback = &led_blink_callback, .arg = blink, .name = "blink_timer" }; esp_timer_create(&timer_args, &timer_handler); - esp_timer_start_periodic(timer_handler, 5 * 1000000); + esp_timer_start_periodic(timer_handler, 2 * 1000000); } diff --git a/components/grownode/gn_commons.h b/components/grownode/gn_commons.h index 57071ebe..48f600bf 100755 --- a/components/grownode/gn_commons.h +++ b/components/grownode/gn_commons.h @@ -55,7 +55,8 @@ typedef enum { GN_NODE_STATUS_ERROR_MISSING_SERVER_URL = 10, GN_NODE_STATUS_ERROR_BAD_SERVER_KEEPALIVE_SEC = 11, GN_NODE_STATUS_ERROR_MISSING_SNTP_URL = 12, - GN_NODE_STATUS_ERROR_MISSING_SERVER_DISCOVERY_PREFIX = 13 + GN_NODE_STATUS_ERROR_MISSING_SERVER_DISCOVERY_PREFIX = 13, + GN_NODE_STATUS_SLEEPING = 14 } gn_node_status_t; typedef enum { diff --git a/components/grownode/gn_mqtt_protocol.c b/components/grownode/gn_mqtt_protocol.c index 3e842ec5..229f4234 100755 --- a/components/grownode/gn_mqtt_protocol.c +++ b/components/grownode/gn_mqtt_protocol.c @@ -32,8 +32,8 @@ extern "C" { #define TAG "gn_mqtt_protocol" EventGroupHandle_t _gn_event_group_mqtt; -const int _GN_MQTT_CONNECTED_OK_EVENT_BIT = BIT0; -const int _GN_MQTT_CONNECTED_KO_EVENT_BIT = BIT1; +const int _GN_MQTT_CONNECTED_EVENT_BIT = BIT0; +const int _GN_MQTT_DISCONNECT_EVENT_BIT = BIT1; const int _GN_MQTT_DEBUG_WAIT_MS = 500; @@ -284,8 +284,9 @@ gn_err_t gn_mqtt_subscribe_leaf(gn_leaf_handle_t _leaf_config) { char topic[_GN_MQTT_MAX_TOPIC_LENGTH]; _gn_mqtt_build_leaf_command_topic(leaf_config, topic); - if(esp_log_level_get(TAG) == ESP_LOG_DEBUG) { - ESP_LOGD(TAG, "gn_mqtt_subscribe_leaf - topic = %s. now waiting %d ms", topic, _GN_MQTT_DEBUG_WAIT_MS); + if (esp_log_level_get(TAG) == ESP_LOG_DEBUG) { + ESP_LOGD(TAG, "gn_mqtt_subscribe_leaf - topic = %s. now waiting %d ms", + topic, _GN_MQTT_DEBUG_WAIT_MS); vTaskDelay(_GN_MQTT_DEBUG_WAIT_MS / portTICK_PERIOD_MS); } @@ -403,13 +404,14 @@ gn_err_t gn_mqtt_subscribe_leaf_param(gn_leaf_param_handle_t _param) { int msg_id = esp_mqtt_client_subscribe(config->mqtt_client, topic, 0); - if(esp_log_level_get(TAG) == ESP_LOG_DEBUG) { - ESP_LOGD(TAG, "gn_mqtt_subscribe_leaf_param, topic = %s, msg_id=%d. now waiting %d ms", topic, - msg_id, _GN_MQTT_DEBUG_WAIT_MS); + if (esp_log_level_get(TAG) == ESP_LOG_DEBUG) { + ESP_LOGD(TAG, + "gn_mqtt_subscribe_leaf_param, topic = %s, msg_id=%d. now waiting %d ms", + topic, msg_id, _GN_MQTT_DEBUG_WAIT_MS); vTaskDelay(_GN_MQTT_DEBUG_WAIT_MS / portTICK_PERIOD_MS); } - return msg_id == -1? GN_RET_ERR: GN_RET_OK; + return msg_id == -1 ? GN_RET_ERR : GN_RET_OK; #else return GN_RET_OK; @@ -603,7 +605,8 @@ gn_err_t gn_mqtt_send_leaf_param(gn_leaf_param_handle_t _param) { len = strlen(param->param_val->v.s); strncpy(buf, param->param_val->v.s, len > _GN_MQTT_MAX_PAYLOAD_LENGTH ? - _GN_MQTT_MAX_PAYLOAD_LENGTH : len); + _GN_MQTT_MAX_PAYLOAD_LENGTH : + len); break; case GN_VAL_TYPE_DOUBLE: snprintf(buf, 31, "%f", param->param_val->v.d); @@ -628,9 +631,9 @@ gn_err_t gn_mqtt_send_leaf_param(gn_leaf_param_handle_t _param) { if (msg_id == -1) goto fail; - - if(esp_log_level_get(TAG) == ESP_LOG_DEBUG) { - ESP_LOGD(TAG, "sent publish successful, msg_id=%d, topic=%s, payload=%s. now waiting %d ms", + if (esp_log_level_get(TAG) == ESP_LOG_DEBUG) { + ESP_LOGD(TAG, + "sent publish successful, msg_id=%d, topic=%s, payload=%s. now waiting %d ms", msg_id, _topic, buf, _GN_MQTT_DEBUG_WAIT_MS); vTaskDelay(_GN_MQTT_DEBUG_WAIT_MS / portTICK_PERIOD_MS); } @@ -640,9 +643,6 @@ gn_err_t gn_mqtt_send_leaf_param(gn_leaf_param_handle_t _param) { return ((msg_id == -1) ? (GN_RET_ERR_MQTT_ERROR) : (GN_RET_OK)); } - - - return ret; #else @@ -772,7 +772,7 @@ gn_err_t gn_mqtt_send_startup_message(gn_config_handle_t _config) { gn_config_handle_intl_t config = (gn_config_handle_intl_t) _config; - //if (config->status != GN_CONFIG_STATUS_COMPLETED) + //if (config->status != GN_NODE_STATUS_STARTED) // return GN_RET_OK; //build @@ -1071,13 +1071,12 @@ gn_err_t gn_mqtt_send_leaf_message(gn_leaf_handle_t _leaf, const char *msg) { 0); //publish - if(esp_log_level_get(TAG) == ESP_LOG_DEBUG) { - ESP_LOGD(TAG, "publish topic %s, msg=%s. now waiting %d ms" - , buf, msg, _GN_MQTT_DEBUG_WAIT_MS); + if (esp_log_level_get(TAG) == ESP_LOG_DEBUG) { + ESP_LOGD(TAG, "publish topic %s, msg=%s. now waiting %d ms", buf, msg, + _GN_MQTT_DEBUG_WAIT_MS); vTaskDelay(_GN_MQTT_DEBUG_WAIT_MS / portTICK_PERIOD_MS); } - return ((msg_id == -1) ? (GN_RET_ERR_MQTT_ERROR) : (GN_RET_OK)); #else @@ -1186,7 +1185,7 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, case MQTT_EVENT_CONNECTED: ESP_LOGD(TAG, "MQTT_EVENT_CONNECTED"); xEventGroupSetBits(_gn_event_group_mqtt, - _GN_MQTT_CONNECTED_OK_EVENT_BIT); + _GN_MQTT_CONNECTED_EVENT_BIT); /* msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, @@ -1206,22 +1205,22 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, case MQTT_EVENT_DISCONNECTED: ESP_LOGD(TAG, "MQTT_EVENT_DISCONNECTED"); xEventGroupSetBits(_gn_event_group_mqtt, - _GN_MQTT_CONNECTED_KO_EVENT_BIT); + _GN_MQTT_DISCONNECT_EVENT_BIT); _gn_mqtt_on_disconnected(config); break; - /* - case MQTT_EVENT_SUBSCRIBED: - ESP_LOGD(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); - break; - case MQTT_EVENT_UNSUBSCRIBED: - ESP_LOGD(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); - break; - case MQTT_EVENT_PUBLISHED: - ESP_LOGD(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); - break; - */ + /* + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGD(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGD(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGD(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + */ case MQTT_EVENT_DATA: //TODO here the code to forward the call to appropriate node/leaf or system handler. start from remote OTA and RST ESP_LOGD(TAG, "MQTT_EVENT_DATA"); @@ -1260,16 +1259,16 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, char param_topic[_GN_MQTT_MAX_TOPIC_LENGTH]; gn_leaf_parameter_event_t evt; - for (int i = 0; i < config->node_config->leaves.last; i++) { + for (int i = 0; i < config->node_handle->leaves.last; i++) { //message is for this leaf _gn_mqtt_build_leaf_command_topic( - config->node_config->leaves.at[i], leaf_topic); + config->node_handle->leaves.at[i], leaf_topic); if (strncmp(leaf_topic, event->topic, event->topic_len) == 0) { evt.id = GN_LEAF_MESSAGE_RECEIVED_EVENT; strncpy(evt.leaf_name, - config->node_config->leaves.at[i]->name, + config->node_handle->leaves.at[i]->name, GN_LEAF_NAME_SIZE); //evt.data = event->data; memcpy(&evt.data[0], event->data, @@ -1282,35 +1281,35 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, if (esp_event_post_to(config->event_loop, GN_BASE_EVENT, evt.id, &evt, sizeof(evt), portMAX_DELAY) != ESP_OK) { ESP_LOGE(TAG, "not possible to send message to leaf %s", - config->node_config->leaves.at[i]->name); + config->node_handle->leaves.at[i]->name); } //send message to the interested leaf - _gn_send_event_to_leaf(config->node_config->leaves.at[i], + _gn_send_event_to_leaf(config->node_handle->leaves.at[i], &evt); break; } gn_leaf_param_handle_intl_t _param = - (gn_leaf_param_handle_intl_t) config->node_config->leaves.at[i]->params; + (gn_leaf_param_handle_intl_t) config->node_handle->leaves.at[i]->params; while (_param) { //message is for a parameter of this leaf _gn_mqtt_build_leaf_parameter_command_topic( - config->node_config->leaves.at[i], _param->name, + config->node_handle->leaves.at[i], _param->name, param_topic); if (strncmp(param_topic, event->topic, event->topic_len) == 0) { if (GN_RET_OK != _gn_leaf_parameter_update( - config->node_config->leaves.at[i], + config->node_handle->leaves.at[i], _param->name, event->data, event->data_len)) { ESP_LOGE(TAG, "error in updating parameter %s with value %s to leaf %s", _param->name, event->data, - config->node_config->leaves.at[i]->name); + config->node_handle->leaves.at[i]->name); break; } @@ -1377,7 +1376,7 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, * @return GN_RET_ERR_MQTT_ERROR in case of MQTT errors * @return GN_RET_ERR in case of general errors */ -gn_err_t gn_mqtt_init(gn_config_handle_t config) { +gn_err_t gn_mqtt_start(gn_config_handle_t config) { #ifdef CONFIG_GROWNODE_WIFI_ENABLED @@ -1385,6 +1384,9 @@ gn_err_t gn_mqtt_init(gn_config_handle_t config) { _gn_event_group_mqtt = xEventGroupCreate(); + xEventGroupClearBits(_gn_event_group_mqtt, _GN_MQTT_DISCONNECT_EVENT_BIT); + xEventGroupClearBits(_gn_event_group_mqtt, _GN_MQTT_CONNECTED_EVENT_BIT); + char _topic[_GN_MQTT_MAX_TOPIC_LENGTH]; _gn_mqtt_build_status_topic(_config, _topic); @@ -1420,47 +1422,29 @@ gn_err_t gn_mqtt_init(gn_config_handle_t config) { _gn_mqtt_build_status_topic(_config, _gn_sts_topic); _gn_mqtt_build_log_topic(_config, _gn_log_topic); + ESP_LOGI(TAG, "gn_mqtt_init waiting to connect"); + EventBits_t uxBits; uxBits = xEventGroupWaitBits(_gn_event_group_mqtt, - _GN_MQTT_CONNECTED_OK_EVENT_BIT | _GN_MQTT_CONNECTED_KO_EVENT_BIT, - pdTRUE, + _GN_MQTT_CONNECTED_EVENT_BIT | _GN_MQTT_DISCONNECT_EVENT_BIT, + pdFALSE, pdFALSE, portMAX_DELAY); - if ((uxBits & _GN_MQTT_CONNECTED_OK_EVENT_BIT) != 0) { - ESP_LOGI(TAG, "MQTT connection successful"); + if ((uxBits & _GN_MQTT_CONNECTED_EVENT_BIT) != 0) { + ESP_LOGD(TAG, "gn_mqtt_init connection successful. returning"); _gn_mqtt_on_connected(config); - /* - //publish server connected event - if (ESP_OK - != esp_event_post_to(_config->event_loop, GN_BASE_EVENT, - GN_SERVER_CONNECTED_EVENT, - NULL, 0, portMAX_DELAY)) { - ESP_LOGE(TAG, "failed to send GN_SERVER_CONNECTED_EVENT event"); - goto fail; - } - */ - return GN_RET_OK; } - else if ((uxBits & _GN_MQTT_CONNECTED_KO_EVENT_BIT) != 0) { - ESP_LOGE(TAG, "MQTT connection error"); - + else if ((uxBits & _GN_MQTT_DISCONNECT_EVENT_BIT) != 0) { - //publish server disconnected event - /* - if (ESP_OK - != esp_event_post_to(_config->event_loop, GN_BASE_EVENT, - GN_SERVER_DISCONNECTED_EVENT, - NULL, 0, portMAX_DELAY)) { - ESP_LOGE(TAG, "failed to send GN_SERVER_DISCONNECTED_EVENT event"); - return ESP_FAIL; - } - */ + ESP_LOGE(TAG, "gn_mqtt_init connection error. returning"); return GN_RET_ERR_MQTT_ERROR; + } else { //should never reach here + ESP_LOGE(TAG, "gn_mqtt_init control flow error. returning"); return GN_RET_ERR; } @@ -1470,6 +1454,157 @@ gn_err_t gn_mqtt_init(gn_config_handle_t config) { } +/** + * @brief stops the MQTT subsystem. this requires the client to be reinitialized + * + * @param config the configuration to use + * + * @return GN_RET_ERR_INVALID_ARG in case of null _conf + * @return GN_RET_ERR in case of general errors + */ + +gn_err_t gn_mqtt_stop(gn_config_handle_t config) { + +#ifdef CONFIG_GROWNODE_WIFI_ENABLED + + if (!config) + return GN_RET_ERR_INVALID_ARG; + + gn_config_handle_intl_t _config = (gn_config_handle_intl_t) config; + + if (!_config->mqtt_client) + return GN_RET_ERR_INVALID_ARG; + + esp_err_t esp_ret; + esp_ret = esp_mqtt_client_disconnect(_config->mqtt_client); + if (esp_ret != ESP_OK) { + ESP_LOGE(TAG, "Error on esp_mqtt_client_disconnect: %s", + esp_err_to_name(esp_ret)); + return GN_RET_ERR; + } + + esp_ret = esp_mqtt_client_stop(_config->mqtt_client); + if (esp_ret != ESP_OK) { + ESP_LOGE(TAG, "Error on esp_mqtt_client_stop: %s", + esp_err_to_name(esp_ret)); + return GN_RET_ERR; + } + + /* + ESP_LOGI(TAG, "gn_mqtt_stop waiting to disconnect"); + + xEventGroupWaitBits(_gn_event_group_mqtt, _GN_MQTT_DISCONNECT_EVENT_BIT, + pdFALSE, + pdFALSE, portMAX_DELAY); + + xEventGroupClearBits(_gn_event_group_mqtt, _GN_MQTT_DISCONNECT_EVENT_BIT); + xEventGroupClearBits(_gn_event_group_mqtt, _GN_MQTT_CONNECTED_EVENT_BIT); + + ESP_LOGI(TAG, "gn_mqtt_stop returning"); + */ + + return GN_RET_OK; + +#else + return GN_RET_OK; +#endif /* CONFIG_GROWNODE_WIFI_ENABLED */ + +} + +/** + * @brief disconnect the MQTT subsystem, this is useful if the reconnection using same client is planned. use gn_mqtt_reconnect to restart + * + * @param config the configuration to use + * + * @return GN_RET_ERR_INVALID_ARG in case of null _conf + * @return GN_RET_ERR in case of general errors + */ + +gn_err_t gn_mqtt_disconnect(gn_config_handle_t config) { + +#ifdef CONFIG_GROWNODE_WIFI_ENABLED + + if (!config) + return GN_RET_ERR_INVALID_ARG; + + gn_config_handle_intl_t _config = (gn_config_handle_intl_t) config; + + if (!_config->mqtt_client) + return GN_RET_ERR_INVALID_ARG; + + esp_err_t esp_ret; + esp_ret = esp_mqtt_client_disconnect(_config->mqtt_client); + if (esp_ret != ESP_OK) { + ESP_LOGE(TAG, "Error on esp_mqtt_client_disconnect: %s", + esp_err_to_name(esp_ret)); + return GN_RET_ERR; + } + + /* + ESP_LOGI(TAG, "gn_mqtt_disconnect waiting to disconnect"); + + EventBits_t uxBits; + uxBits = xEventGroupWaitBits(_gn_event_group_mqtt, + _GN_MQTT_DISCONNECT_EVENT_BIT, + pdFALSE, + pdFALSE, portMAX_DELAY); + + ESP_LOGI(TAG, "gn_mqtt_disconnect returning"); + */ + + return GN_RET_OK; + +#else + return GN_RET_OK; +#endif /* CONFIG_GROWNODE_WIFI_ENABLED */ + +} + +/** + * @brief reconnect the MQTT subsystem keeping the client configuration + * + * @param config the configuration to use + * + * @return GN_RET_ERR_INVALID_ARG in case of null _conf + * @return GN_RET_ERR in case of general errors + */ + +gn_err_t gn_mqtt_reconnect(gn_config_handle_t config) { + +#ifdef CONFIG_GROWNODE_WIFI_ENABLED + + if (!config) + return GN_RET_ERR_INVALID_ARG; + + gn_config_handle_intl_t _config = (gn_config_handle_intl_t) config; + + if (!_config->mqtt_client) + return GN_RET_ERR_INVALID_ARG; + + esp_err_t esp_ret; + esp_ret = esp_mqtt_client_reconnect(_config->mqtt_client); + if (esp_ret != ESP_OK) { + ESP_LOGE(TAG, "Error on esp_mqtt_client_disconnect: %s", + esp_err_to_name(esp_ret)); + return GN_RET_ERR; + } + + ESP_LOGD(TAG, "gn_mqtt_reconnect waiting to connect"); + + xEventGroupWaitBits(_gn_event_group_mqtt, + _GN_MQTT_CONNECTED_EVENT_BIT, + pdFALSE, + pdFALSE, portMAX_DELAY); + + ESP_LOGD(TAG, "gn_mqtt_reconnect returning"); + + return GN_RET_OK; + +#else + return GN_RET_OK; +#endif /* CONFIG_GROWNODE_WIFI_ENABLED */ +} + /* static void test() { diff --git a/components/grownode/gn_mqtt_protocol.h b/components/grownode/gn_mqtt_protocol.h index ae75874b..cdca3c8b 100755 --- a/components/grownode/gn_mqtt_protocol.h +++ b/components/grownode/gn_mqtt_protocol.h @@ -39,7 +39,13 @@ gn_err_t gn_mqtt_subscribe_leaf(gn_leaf_handle_t leaf_config); esp_err_t gn_mqtt_subscribe_leaf_param(gn_leaf_param_handle_t param); -gn_err_t gn_mqtt_init(gn_config_handle_t config); +gn_err_t gn_mqtt_start(gn_config_handle_t config); + +gn_err_t gn_mqtt_stop(gn_config_handle_t config); + +gn_err_t gn_mqtt_disconnect(gn_config_handle_t config); + +gn_err_t gn_mqtt_reconnect(gn_config_handle_t config); gn_err_t gn_mqtt_send_node_config(gn_node_handle_t conf); diff --git a/components/grownode/gn_network.c b/components/grownode/gn_network.c index 6c53a681..6f67aa9e 100755 --- a/components/grownode/gn_network.c +++ b/components/grownode/gn_network.c @@ -57,6 +57,8 @@ extern "C" { const int GN_WIFI_CONNECTED_EVENT = BIT0; const int GN_WIFI_FAIL_EVENT = BIT2; const int GN_PROV_END_EVENT = BIT1; +const int GN_WIFI_DISCONNECTED_EVENT = BIT3; +const int GN_WIFI_STOPPED_EVENT = BIT4; EventGroupHandle_t _gn_event_group_wifi; int s_retry_num = 0; @@ -72,6 +74,7 @@ void _gn_wifi_event_handler(void *arg, esp_event_base_t event_base, if (event_base == WIFI_PROV_EVENT) { switch (event_id) { + case WIFI_PROV_START: ESP_LOGD(TAG, "WIFI_PROV_START"); gn_log(TAG, GN_LOG_INFO, "Provisioning Started"); @@ -85,6 +88,7 @@ void _gn_wifi_event_handler(void *arg, esp_event_base_t event_base, (const char* ) wifi_sta_cfg->password); break; } + case WIFI_PROV_CRED_FAIL: { ESP_LOGD(TAG, "WIFI_PROV_CRED_FAIL"); wifi_prov_sta_fail_reason_t *reason = @@ -98,9 +102,11 @@ void _gn_wifi_event_handler(void *arg, esp_event_base_t event_base, gn_reboot(); break; } + case WIFI_PROV_CRED_SUCCESS: ESP_LOGD(TAG, "WIFI_PROV_CRED_SUCCESS"); break; + case WIFI_PROV_END: ESP_LOGD(TAG, "WIFI_PROV_END"); gn_log(TAG, GN_LOG_INFO, "Provisioning OK"); @@ -108,10 +114,13 @@ void _gn_wifi_event_handler(void *arg, esp_event_base_t event_base, default: break; } + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + ESP_LOGD(TAG, "WIFI_EVENT_STA_START"); esp_wifi_connect(); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ESP_LOGD(TAG, "IP_EVENT_STA_GOT_IP"); s_retry_num = 0; ip_event_got_ip_t *event = (ip_event_got_ip_t*) event_data; @@ -144,33 +153,50 @@ void _gn_wifi_event_handler(void *arg, esp_event_base_t event_base, } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { ESP_LOGD(TAG, "WIFI_EVENT_STA_DISCONNECTED"); - wifi_event_sta_disconnected_t *disconnected = (wifi_event_sta_disconnected_t *) event_data; - ESP_LOGE(TAG,"Wifi disconnected. reason: %d", disconnected->reason); + wifi_event_sta_disconnected_t *disconnected = + (wifi_event_sta_disconnected_t*) event_data; + ESP_LOGD(TAG, "wifi_event_sta_disconnected_t reason: %d", + disconnected->reason); //ESP_LOGI(TAG, "Disconnected. Connecting to the AP again."); esp_event_post_to(_conf->event_loop, GN_BASE_EVENT, GN_NET_DISCONNECTED_EVENT, NULL, 0, portMAX_DELAY); - if (_conf->config_init_params->wifi_retries_before_reset_provisioning - == -1 - || s_retry_num - < _conf->config_init_params->wifi_retries_before_reset_provisioning) { - ESP_LOGI(TAG, "Retry to connect - attempt %i of %i", s_retry_num, _conf->config_init_params->wifi_retries_before_reset_provisioning); - s_retry_num++; - esp_wifi_connect(); - } else { - xEventGroupSetBits(_gn_event_group_wifi, GN_WIFI_FAIL_EVENT); + xEventGroupSetBits(_gn_event_group_wifi, GN_WIFI_DISCONNECTED_EVENT); + + //WIFI_REASON_ASSOC_LEAVE means that is a voluntary disconnect, do not retry to reconnect + if (disconnected->reason != WIFI_REASON_ASSOC_LEAVE) { + + if (_conf->config_init_params->wifi_retries_before_reset_provisioning + == -1 + || s_retry_num + < _conf->config_init_params->wifi_retries_before_reset_provisioning) { + ESP_LOGI(TAG, "Retry to connect - attempt %i of %i", + s_retry_num, + _conf->config_init_params->wifi_retries_before_reset_provisioning); + s_retry_num++; + esp_wifi_connect(); + } else { + xEventGroupSetBits(_gn_event_group_wifi, GN_WIFI_FAIL_EVENT); + } + //ESP_LOGI(TAG,"connect to the AP fail"); } - //ESP_LOGI(TAG,"connect to the AP fail"); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_STOP) { + + xEventGroupSetBits(_gn_event_group_wifi, GN_WIFI_STOPPED_EVENT); + + } else { + ESP_LOGD(TAG, "other event received. event_base = %s, event_id = %d", + event_base, event_id); } #endif } -void _gn_wifi_init_sta(void) { +gn_err_t _gn_wifi_init_sta(void) { #ifdef CONFIG_GROWNODE_WIFI_ENABLED @@ -180,21 +206,28 @@ void _gn_wifi_init_sta(void) { EventBits_t bits = xEventGroupWaitBits(_gn_event_group_wifi, GN_WIFI_CONNECTED_EVENT | GN_WIFI_FAIL_EVENT, - pdFALSE, + pdTRUE, pdFALSE, portMAX_DELAY); if (bits & GN_WIFI_CONNECTED_EVENT) { - gn_log(TAG, GN_LOG_INFO, "Connected to AP"); + ESP_LOGI(TAG, "_gn_wifi_init_sta - connected"); } else if (bits & GN_WIFI_FAIL_EVENT) { - gn_log(TAG, GN_LOG_ERROR, "Failed to connect to AP. Resetting Provisioning Status"); + ESP_LOGE(TAG, + "_gn_wifi_init_sta - Failed to connect to AP. Resetting Provisioning Status"); wifi_prov_mgr_reset_provisioning(); gn_reboot(); + return GN_RET_ERR; } else { - gn_log(TAG, GN_LOG_ERROR, "UNEXPECTED EVENT"); + ESP_LOGE(TAG, "_gn_wifi_init_sta - UNEXPECTED EVENT"); + return GN_RET_ERR; } -#endif + return GN_RET_OK; + +#else + return GN_RET_OK; +#endif /* CONFIG_GROWNODE_WIFI_ENABLED */ } @@ -238,7 +271,7 @@ esp_err_t _gn_wifi_custom_prov_data_handler(uint32_t session_id, return ESP_OK; } -esp_err_t _gn_init_wifi(gn_config_handle_intl_t conf) { +esp_err_t gn_wifi_init(gn_config_handle_intl_t conf) { #ifdef CONFIG_GROWNODE_WIFI_ENABLED @@ -336,8 +369,6 @@ esp_err_t _gn_init_wifi(gn_config_handle_intl_t conf) { * for encryption/decryption of messages. */ - - wifi_prov_security_t security = WIFI_PROV_SECURITY_0; if (_conf->config_init_params->provisioning_security) { @@ -416,9 +447,8 @@ esp_err_t _gn_init_wifi(gn_config_handle_intl_t conf) { ESP_LOGD(TAG, "Wait for Wi-Fi connection"); /* Wait for Wi-Fi connection */ - xEventGroupWaitBits(_gn_event_group_wifi, GN_WIFI_CONNECTED_EVENT, false, - true, portMAX_DELAY); - + //xEventGroupWaitBits(_gn_event_group_wifi, GN_WIFI_CONNECTED_EVENT, false, + //true, portMAX_DELAY); fail: return ret; #else @@ -431,7 +461,7 @@ esp_err_t _gn_init_wifi(gn_config_handle_intl_t conf) { static bool time_sync_init_done = false; #endif -esp_err_t _gn_init_time_sync(gn_config_handle_t conf) { +esp_err_t gn_wifi_time_sync_init(gn_config_handle_t conf) { #ifdef CONFIG_GROWNODE_WIFI_ENABLED @@ -455,7 +485,7 @@ esp_err_t _gn_init_time_sync(gn_config_handle_t conf) { } -void _gn_ota_task(void *pvParameter) { +void gn_ota_task(void *pvParameter) { #ifdef CONFIG_GROWNODE_WIFI_ENABLED @@ -495,6 +525,81 @@ void _gn_ota_task(void *pvParameter) { } +/** + * @brief stops the wifi connection, this is usually done in power saving mode + * + * @param config the configuration to use + * + * @return GN_RET_ERR_INVALID_ARG in case of null _conf + * @return GN_RET_ERR in case of general errors + */ +gn_err_t gn_wifi_stop(gn_config_handle_t config) { + +#ifdef CONFIG_GROWNODE_WIFI_ENABLED + + if (!config) + return GN_RET_ERR_INVALID_ARG; + + //gn_config_handle_intl_t _config = (gn_config_handle_intl_t) config; + + esp_err_t esp_ret; + esp_ret = esp_wifi_disconnect(); + if (esp_ret != ESP_OK) { + ESP_LOGE(TAG, "Error on esp_wifi_disconnect: %s", + esp_err_to_name(esp_ret)); + return GN_RET_ERR; + } + + ESP_LOGD(TAG, "gn_wifi_stop - Start Wait for Wi-Fi disconnection"); + + xEventGroupWaitBits(_gn_event_group_wifi, GN_WIFI_DISCONNECTED_EVENT, + pdTRUE, + pdFALSE, portMAX_DELAY); + + ESP_LOGD(TAG, "gn_wifi_stop - End Wait for Wi-Fi disconnection"); + + esp_ret = esp_wifi_stop(); + if (esp_ret != ESP_OK) { + ESP_LOGE(TAG, "Error on esp_wifi_stop: %s", esp_err_to_name(esp_ret)); + return GN_RET_ERR; + } + + xEventGroupWaitBits(_gn_event_group_wifi, GN_WIFI_STOPPED_EVENT, + pdTRUE, + pdFALSE, portMAX_DELAY); + + ESP_LOGD(TAG, "gn_wifi_stop - returning"); + + return GN_RET_OK; + +#else + return GN_RET_OK; +#endif /* CONFIG_GROWNODE_WIFI_ENABLED */ + +} + +/** + * @brief reconnect the MQTT subsystem keeping the client configuration + * + * @param config the configuration to use + * + * @return GN_RET_ERR_INVALID_ARG in case of null _conf + * @return GN_RET_ERR in case of general errors + */ +gn_err_t gn_wifi_start(gn_config_handle_t conf) { + +#ifdef CONFIG_GROWNODE_WIFI_ENABLED + + _gn_wifi_init_sta(); + + return GN_RET_OK; + +#else + return GN_RET_OK; +#endif /* CONFIG_GROWNODE_WIFI_ENABLED */ + +} + #ifdef __cplusplus } #endif //__cplusplus diff --git a/components/grownode/gn_network.h b/components/grownode/gn_network.h index 9d463925..d6d1680d 100755 --- a/components/grownode/gn_network.h +++ b/components/grownode/gn_network.h @@ -19,9 +19,12 @@ extern "C" { #endif -esp_err_t _gn_init_wifi(gn_config_handle_t conf); -void _gn_ota_task(void *pvParameter); -esp_err_t _gn_init_time_sync(gn_config_handle_t conf); +esp_err_t gn_wifi_init(gn_config_handle_t conf); +void gn_ota_task(void *pvParameter); +esp_err_t gn_wifi_time_sync_init(gn_config_handle_t conf); + +gn_err_t gn_wifi_stop(gn_config_handle_t conf); +gn_err_t gn_wifi_start(gn_config_handle_t conf); #ifdef __cplusplus } diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index adc46049..33b353fd 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -110,8 +110,7 @@ gn_err_t _gn_leaf_start(gn_leaf_config_handle_intl_t leaf_config) { if (xTaskCreate((void*) leaf_config->leaf_descriptor->callback, leaf_config->name, leaf_config->task_size, leaf_config, 1, //configMAX_PRIORITIES - 1, &task_handle) != pdPASS) { - gn_log(TAG, GN_LOG_ERROR, "failed to create lef task for %s", - leaf_config->name); + ESP_LOGE(TAG, "failed to create lef task for %s", leaf_config->name); goto fail; } leaf_config->task_handle = task_handle; @@ -191,8 +190,7 @@ esp_err_t _gn_init_spiffs(gn_config_handle_intl_t conf) { size_t total = 0, used = 0; ret = esp_spiffs_info(NULL, &total, &used); if (ret != ESP_OK) { - gn_log(TAG, GN_LOG_ERROR, - "Failed to get SPIFFS partition information (%s)", + ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret)); } else { ESP_LOGD(TAG, "Partition size: total: %d, used: %d", total, used); @@ -202,6 +200,7 @@ esp_err_t _gn_init_spiffs(gn_config_handle_intl_t conf) { } +/* #define TIMER_DIVIDER (16) // Hardware timer clock divider #define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds @@ -213,20 +212,34 @@ static bool IRAM_ATTR _gn_timer_callback_isr(void *args) { GN_SRV_KEEPALIVE_TRIGGERED_EVENT, NULL, 0, &high_task_awoken); return high_task_awoken == pdTRUE; } +*/ + +void _gn_timer_callback(gn_config_handle_intl_t conf) { + esp_event_post_to(gn_event_loop, GN_BASE_EVENT, + GN_SRV_KEEPALIVE_TRIGGERED_EVENT, NULL, 0, portMAX_DELAY); +} -void _gn_keepalive_start() { +void _gn_keepalive_start(gn_config_handle_intl_t conf) { + + if(!conf->keepalive_timer_handler) return; + //timer_start(TIMER_GROUP_0, TIMER_0); + if (!esp_timer_is_active(conf->keepalive_timer_handler)) + esp_timer_start_periodic(conf->keepalive_timer_handler, conf->config_init_params->server_keepalive_timer_sec * 1000); ESP_LOGD(TAG, "timer started"); - timer_start(TIMER_GROUP_0, TIMER_0); } -void _gn_keepalive_stop() { +void _gn_keepalive_stop(gn_config_handle_intl_t conf) { + + if(!conf->keepalive_timer_handler) return; + //timer_pause(TIMER_GROUP_0, TIMER_0); + if (!esp_timer_is_active(conf->keepalive_timer_handler)) + esp_timer_stop(conf->keepalive_timer_handler); ESP_LOGD(TAG, "timer paused"); - timer_pause(TIMER_GROUP_0, TIMER_0); } -gn_leaf_config_handle_intl_t _gn_leaf_get_by_name(char *leaf_name) { +gn_leaf_config_handle_intl_t _gn_leaf_get_by_name(gn_config_handle_intl_t conf, char *leaf_name) { - gn_leaves_list leaves = _gn_default_conf->node_config->leaves; + gn_leaves_list leaves = conf->node_handle->leaves; for (int i = 0; i < leaves.last; i++) { if (strcmp(leaves.at[i]->name, leaf_name) == 0) { @@ -257,7 +270,7 @@ gn_err_t _gn_send_event_to_leaf(gn_leaf_config_handle_intl_t leaf_config, evt->data[evt->data_size] = '\0'; if (xQueueSend(leaf_config->event_queue, evt, portMAX_DELAY) != pdTRUE) { - gn_log(TAG, GN_LOG_ERROR, "not possible to send message to leaf %s", + ESP_LOGE(TAG, "not possible to send message to leaf %s", leaf_config->name); return GN_RET_ERR_EVENT_NOT_SENT; } @@ -265,8 +278,12 @@ gn_err_t _gn_send_event_to_leaf(gn_leaf_config_handle_intl_t leaf_config, return GN_RET_OK; } -void _gn_evt_handler(void *handler_args, esp_event_base_t base, int32_t id, - void *event_data) { +void _gn_evt_handler(void* handler_data, esp_event_base_t base, int32_t id, + void* event_data) { + + if (!handler_data) return; + + gn_config_handle_intl_t conf = (gn_config_handle_intl_t) handler_data; //if (pdTRUE == xSemaphoreTake(_gn_xEvtSemaphore, portMAX_DELAY)) { @@ -294,23 +311,23 @@ void _gn_evt_handler(void *handler_args, esp_event_base_t base, int32_t id, break; case GN_SRV_CONNECTED_EVENT: - if (!_gn_default_conf) + if (!conf) break; //from unknown status: reboot - to keep consistency - if (_gn_default_conf->status != GN_NODE_STATUS_READY_TO_START - && _gn_default_conf->status != GN_NODE_STATUS_STARTED) { + if (conf->status != GN_NODE_STATUS_READY_TO_START + && conf->status != GN_NODE_STATUS_STARTED) { gn_reboot(); break; } //start keepalive service - _gn_keepalive_start(); + _gn_keepalive_start(conf); break; case GN_SRV_DISCONNECTED_EVENT: //stop keepalive service - _gn_keepalive_stop(); + _gn_keepalive_stop(conf); break; /* @@ -335,7 +352,7 @@ void _gn_evt_handler(void *handler_args, esp_event_base_t base, int32_t id, case GN_SRV_KEEPALIVE_TRIGGERED_EVENT: //publish node - if (gn_mqtt_send_node_config(_gn_default_conf->node_config) + if (gn_mqtt_send_node_config(conf->node_handle) != GN_RET_OK) { ESP_LOGE(TAG, "Error in sending node config message"); } @@ -346,7 +363,7 @@ void _gn_evt_handler(void *handler_args, esp_event_base_t base, int32_t id, gn_leaf_parameter_event_handle_t evt = (gn_leaf_parameter_event_handle_t) event_data; - gn_leaf_config_handle_intl_t leaf_config = _gn_leaf_get_by_name( + gn_leaf_config_handle_intl_t leaf_config = _gn_leaf_get_by_name(conf, evt->leaf_name); if (leaf_config != NULL) { @@ -385,6 +402,7 @@ esp_err_t _gn_init_keepalive_timer(gn_config_handle_intl_t conf) { if (conf->config_init_params->server_keepalive_timer_sec == 0) return ESP_OK; + /* timer_config_t config = { .divider = TIMER_DIVIDER, .counter_dir = TIMER_COUNT_UP, .counter_en = TIMER_PAUSE, .alarm_en = TIMER_ALARM_EN, .auto_reload = 1, }; // default clock source is APB @@ -396,7 +414,13 @@ esp_err_t _gn_init_keepalive_timer(gn_config_handle_intl_t conf) { return timer_isr_callback_add(TIMER_GROUP_0, TIMER_0, _gn_timer_callback_isr, NULL, 0); + */ + //creates the blink timer + const esp_timer_create_args_t keepalive_timer_args = { .callback = &_gn_timer_callback, + .arg = conf, .name = "keepalive_timer" }; + + return esp_timer_create(&keepalive_timer_args, &conf->keepalive_timer_handler); } /** @@ -641,8 +665,7 @@ gn_node_handle_t gn_node_create(gn_config_handle_t config, const char *name) { if (config == NULL || ((gn_config_handle_intl_t) config)->mqtt_client == NULL || name == NULL) { - gn_log(TAG, GN_LOG_ERROR, - "gn_create_node failed. parameters not correct"); + ESP_LOGE(TAG, "gn_create_node failed. parameters not correct"); return NULL; } @@ -656,7 +679,7 @@ gn_node_handle_t gn_node_create(gn_config_handle_t config, const char *name) { gn_leaves_list leaves = { .size = GN_NODE_LEAVES_MAX_SIZE, .last = 0 }; n_c->leaves = leaves; - ((gn_config_handle_intl_t) config)->node_config = n_c; + ((gn_config_handle_intl_t) config)->node_handle = n_c; return n_c; } @@ -715,8 +738,7 @@ gn_err_t gn_node_start(gn_node_handle_t node) { != esp_event_post_to(_node->config->event_loop, GN_BASE_EVENT, GN_NODE_STARTED_EVENT, NULL, 0, portMAX_DELAY)) { - gn_log(TAG, GN_LOG_ERROR, - "failed to send GN_SERVER_CONNECTED_EVENT event"); + ESP_LOGE(TAG, "failed to send GN_SERVER_CONNECTED_EVENT event"); return GN_RET_ERR_EVENT_LOOP_ERROR; } @@ -728,7 +750,7 @@ gn_err_t gn_node_start(gn_node_handle_t node) { for (int i = 0; i < _node->leaves.last; i++) { //ESP_LOGD(TAG, "starting leaf: %d", i); if (_gn_leaf_start(_node->leaves.at[i]) != GN_RET_OK) { - gn_log(TAG, GN_LOG_ERROR, "failed to start leaf: %s", + ESP_LOGE(TAG, "failed to start leaf: %s", _node->leaves.at[i]->name); _node->config->status = GN_NODE_STATUS_ERROR; return GN_RET_ERR_NODE_NOT_STARTED; @@ -803,31 +825,43 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { return GN_RET_OK; } +/** + * TODO warning: this is not working, seems that eTaskGetState blocks the main task sometimes + */ void _gn_wait_for_blocked_leaves(gn_node_handle_intl_t _node) { + + if (!_node) return; + //waits until all leaves has reached blocked status int leaves_count = _node->leaves.last; + ESP_LOGI(TAG, "leaves_count %d", leaves_count); bool leaves_working = false; bool this_leaf_working = false; while (!leaves_working) { + for (int i = 0; i < leaves_count; i++) { - this_leaf_working = eTaskGetState(_node->leaves.at[i]->task_handle) - != eBlocked; - leaves_working = this_leaf_working || leaves_working; - if (this_leaf_working) { - ESP_LOGD(TAG, "leaves working: %s", _node->leaves.at[i]->name); + if (_node->leaves.at[i] && _node->leaves.at[i]->task_handle) { + this_leaf_working = eTaskGetState( + _node->leaves.at[i]->task_handle) == eRunning; + leaves_working = this_leaf_working || leaves_working; + if (this_leaf_working) { + ESP_LOGI(TAG, "leaves working: %s", + _node->leaves.at[i]->name); + } } } if (leaves_working) vTaskDelay(pdMS_TO_TICKS(5)); } + } /** - * @brief enter in sleep mode + * @brief enter in sleep mode, disabling networking and releasing resources. * * @param node the node to sleep * @sleep_mode the type of sleep @@ -862,12 +896,26 @@ gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, / portTICK_PERIOD_MS); } - _gn_wait_for_blocked_leaves(_node); + _node->config->status = GN_NODE_STATUS_SLEEPING; + //stop mqtt + gn_mqtt_stop(_node->config); + + //stop wifi + gn_wifi_stop(_node->config); ESP_LOGI(TAG, "Entering deep sleep for %"PRIu64" millisec", millisec); wakeup_reason = GN_SLEEP_MODE_DEEP; + + //_gn_wait_for_blocked_leaves(_node); esp_deep_sleep(millisec * 1000LL); + + //start wifi + gn_wifi_start(_node->config); + + //start mqtt + gn_mqtt_start(_node->config); + _node->config->status = GN_NODE_STATUS_STARTED; } else if (sleep_mode == GN_SLEEP_MODE_LIGHT) { @@ -891,13 +939,28 @@ gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, / portTICK_PERIOD_MS); } - _gn_wait_for_blocked_leaves(_node); + _node->config->status = GN_NODE_STATUS_SLEEPING; + //stop mqtt + gn_mqtt_stop(_node->config); + + //stop wifi + gn_wifi_stop(_node->config); ESP_LOGI(TAG, "Entering light sleep for %"PRIu64" millisec", millisec); wakeup_reason = GN_SLEEP_MODE_LIGHT; + + //_gn_wait_for_blocked_leaves(_node); esp_sleep_enable_timer_wakeup(millisec * 1000LL); esp_light_sleep_start(); + + //start wifi + gn_wifi_start(_node->config); + + //start mqtt + gn_mqtt_start(_node->config); + _node->config->status = GN_NODE_STATUS_STARTED; + } return GN_RET_OK; @@ -960,8 +1023,7 @@ gn_leaf_handle_t gn_leaf_create(gn_node_handle_t node_config, const char *name, if (node_cfg == NULL || node_cfg->config == NULL || name == NULL || node_cfg->config->mqtt_client == NULL) { - gn_log(TAG, GN_LOG_ERROR, - "gn_leaf_create failed. parameters not correct"); + ESP_LOGE(TAG, "gn_leaf_create failed. parameters not correct"); return NULL; } @@ -986,7 +1048,7 @@ gn_leaf_handle_t gn_leaf_create(gn_node_handle_t node_config, const char *name, //TODO add leaf to node. implement dynamic array if (n_c->leaves.last >= n_c->leaves.size - 1) { - gn_log(TAG, GN_LOG_ERROR, + ESP_LOGE(TAG, "gn_leaf_create failed. not possible to add more than %d leaves to a node", n_c->leaves.size); return NULL; @@ -1129,7 +1191,7 @@ void _gn_leaf_evt_handler(void *handler_args, esp_event_base_t base, int32_t id, if (_gn_send_event_to_leaf(leaf_config, evt) == GN_RET_OK) { //ESP_LOGD(TAG, "_gn_leaf_evt_handler OK"); } else { - gn_log(TAG, GN_LOG_ERROR, "_gn_leaf_evt_handler ERROR"); + ESP_LOGE(TAG, "_gn_leaf_evt_handler ERROR"); } } else { @@ -1150,7 +1212,7 @@ void _gn_leaf_evt_handler(void *handler_args, esp_event_base_t base, int32_t id, if (_gn_send_event_to_leaf(leaf_config, &evt) == GN_RET_OK) { //ESP_LOGD(TAG, "_gn_leaf_evt_handler OK"); } else { - gn_log(TAG, GN_LOG_ERROR, "_gn_leaf_evt_handler ERROR"); + ESP_LOGE(TAG, "_gn_leaf_evt_handler ERROR"); } } @@ -1231,7 +1293,7 @@ gn_leaf_param_handle_t gn_leaf_param_create(gn_leaf_handle_t leaf_config, gn_validator_callback_t validator) { if (!name) { - gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_create incorrect parameters"); + ESP_LOGE(TAG, "gn_leaf_param_create incorrect parameters"); return NULL; } @@ -1283,7 +1345,7 @@ gn_leaf_param_handle_t gn_leaf_param_create(gn_leaf_handle_t leaf_config, free(value); break; default: - gn_log(TAG, GN_LOG_ERROR, "param type not handled"); + ESP_LOGE(TAG, "param type not handled"); free(value); free(_buf); return NULL; @@ -1311,8 +1373,7 @@ gn_leaf_param_handle_t gn_leaf_param_create(gn_leaf_handle_t leaf_config, switch (type) { case GN_VAL_TYPE_STRING: if (!val.s) { - gn_log(TAG, GN_LOG_ERROR, - "gn_leaf_param_create incorrect string parameter"); + ESP_LOGE(TAG, "gn_leaf_param_create incorrect string parameter"); return NULL; } _val.s = strdup(val.s); @@ -1324,7 +1385,7 @@ gn_leaf_param_handle_t gn_leaf_param_create(gn_leaf_handle_t leaf_config, _val.d = val.d; break; default: - gn_log(TAG, GN_LOG_ERROR, "param type not handled"); + ESP_LOGE(TAG, "param type not handled"); return NULL; break; } @@ -1426,9 +1487,9 @@ gn_err_t gn_leaf_param_init_string(const gn_leaf_handle_t leaf_config, _leaf_config->node_config->config->event_loop, GN_BASE_EVENT, evt.id, &evt, sizeof(evt), portMAX_DELAY); if (ret != ESP_OK) { - gn_log(TAG, GN_LOG_ERROR, + ESP_LOGE(TAG, "gn_leaf_param_init_string - not possible to send param message to event loop - id:%d, size:%d - result: %d", - evt.id, sizeof(evt), (int) ret); + evt.id, sizeof(evt), (int ) ret); return GN_RET_ERR; } @@ -1677,9 +1738,9 @@ gn_err_t gn_leaf_param_init_bool(const gn_leaf_handle_t leaf_config, _leaf_config->node_config->config->event_loop, GN_BASE_EVENT, evt.id, &evt, sizeof(evt), portMAX_DELAY); if (ret != ESP_OK) { - gn_log(TAG, GN_LOG_ERROR, + ESP_LOGE(TAG, "gn_leaf_param_init_bool - not possible to send param message to event loop - id:%d, size:%d - result: %d", - evt.id, sizeof(evt), (int) ret); + evt.id, sizeof(evt), (int ) ret); return GN_RET_ERR; } @@ -1893,8 +1954,7 @@ gn_err_t gn_leaf_param_init_double(const gn_leaf_handle_t leaf_config, const char *name, double val) { if (!leaf_config || !name) { - gn_log(TAG, GN_LOG_ERROR, - "gn_leaf_param_init_double - wrong parameters"); + ESP_LOGE(TAG, "gn_leaf_param_init_double - wrong parameters"); return ESP_ERR_INVALID_ARG; } @@ -1902,8 +1962,8 @@ gn_err_t gn_leaf_param_init_double(const gn_leaf_handle_t leaf_config, (gn_leaf_param_handle_intl_t) gn_leaf_param_get_param_handle( leaf_config, name); if (!_param) { - gn_log(TAG, GN_LOG_ERROR, - "gn_leaf_param_init_double - cannot find parameter %s", name); + ESP_LOGE(TAG, "gn_leaf_param_init_double - cannot find parameter %s", + name); return ESP_ERR_INVALID_ARG; } @@ -1969,9 +2029,9 @@ gn_err_t gn_leaf_param_init_double(const gn_leaf_handle_t leaf_config, evt.id, &evt, sizeof(evt), portMAX_DELAY); if (ret != ESP_OK) { - gn_log(TAG, GN_LOG_ERROR, + ESP_LOGE(TAG, "gn_leaf_param_init_double - not possible to send param message to event loop - id:%d, size:%d - result: %d", - evt.id, sizeof(evt), (int) ret); + evt.id, sizeof(evt), (int ) ret); return GN_RET_ERR; } @@ -2244,7 +2304,7 @@ gn_err_t gn_leaf_param_add_to_leaf(const gn_leaf_handle_t leaf, gn_leaf_param_handle_intl_t new_param = (gn_leaf_param_handle_intl_t) param; if (!leaf || !new_param) { - gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_add incorrect parameters"); + ESP_LOGE(TAG, "gn_leaf_param_add incorrect parameters"); return GN_RET_ERR_INVALID_ARG; } @@ -2253,10 +2313,9 @@ gn_err_t gn_leaf_param_add_to_leaf(const gn_leaf_handle_t leaf, while (_param) { if (strcmp(_param->name, new_param->name) == 0) { - gn_log(TAG, GN_LOG_ERROR, - "Parameter with name %s already exists in Leaf %s", + ESP_LOGE(TAG, "Parameter with name %s already exists in Leaf %s", new_param->name, - ((gn_leaf_config_handle_intl_t) leaf)->name); + ((gn_leaf_config_handle_intl_t ) leaf)->name); return GN_RET_ERR_INVALID_ARG; } if (_param->next) { @@ -2277,17 +2336,17 @@ gn_err_t gn_leaf_param_add_to_leaf(const gn_leaf_handle_t leaf, ret = gn_mqtt_subscribe_leaf_param(new_param); if (ret != GN_RET_OK) { - gn_log(TAG, GN_LOG_ERROR, + ESP_LOGE(TAG, "gn_leaf_param_add failed to subscribe param %s of leaf %s", - new_param->name, ((gn_leaf_config_handle_intl_t) leaf)->name); + new_param->name, ((gn_leaf_config_handle_intl_t ) leaf)->name); return ret; } ret = gn_mqtt_send_leaf_param(new_param); if (ret != GN_RET_OK) { - gn_log(TAG, GN_LOG_ERROR, + ESP_LOGE(TAG, "gn_leaf_param_add failed to send param configuration %s of leaf %s", - new_param->name, ((gn_leaf_config_handle_intl_t) leaf)->name); + new_param->name, ((gn_leaf_config_handle_intl_t ) leaf)->name); return ret; } @@ -2322,7 +2381,7 @@ gn_err_t gn_send_node_leaf_param_status(const gn_node_handle_t _node_config) { gn_err_t ret = gn_mqtt_send_leaf_param(_param); if (ret != GN_RET_OK) { - gn_log(TAG, GN_LOG_ERROR, + ESP_LOGE(TAG, "gn_leaf_param_add failed to send param configuration %s of leaf %s", _param->name, leaf_config->name); return ret; @@ -2356,12 +2415,11 @@ gn_err_t gn_send_leaf_param_change_message(const char *leaf_name, const char *param_name, const void *message, size_t message_len) { if (leaf_name == NULL || param_name == NULL || message == NULL) { - gn_log(TAG, GN_LOG_ERROR, - "gn_send_leaf_param_change_message - invalid args"); + ESP_LOGE(TAG, "gn_send_leaf_param_change_message - invalid args"); return GN_RET_ERR_INVALID_ARG; } - gn_leaves_list leaves = _gn_default_conf->node_config->leaves; + gn_leaves_list leaves = _gn_default_conf->node_handle->leaves; for (int i = 0; i < leaves.last; i++) { @@ -2372,8 +2430,7 @@ gn_err_t gn_send_leaf_param_change_message(const char *leaf_name, } } - gn_log(TAG, GN_LOG_ERROR, - "gn_send_leaf_param_change_message(%s) - leaf not found", + ESP_LOGE(TAG, "gn_send_leaf_param_change_message(%s) - leaf not found", leaf_name); return GN_RET_ERR_LEAF_NOT_FOUND; } @@ -2397,7 +2454,7 @@ gn_err_t gn_leaf_param_set_bool(const gn_leaf_handle_t leaf_config, const char *name, bool val) { if (leaf_config == NULL || name == NULL) { - gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_set_bool - invalid args"); + ESP_LOGE(TAG, "gn_leaf_param_set_bool - invalid args"); return GN_RET_ERR_INVALID_ARG; } gn_leaf_config_handle_intl_t _leaf_config = @@ -2427,7 +2484,7 @@ gn_err_t gn_leaf_param_set_bool(const gn_leaf_handle_t leaf_config, gn_err_t gn_leaf_param_set_double(const gn_leaf_handle_t leaf_config, const char *name, double val) { if (leaf_config == NULL || name == NULL) { - gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_set_double - invalid args"); + ESP_LOGE(TAG, "gn_leaf_param_set_double - invalid args"); return GN_RET_ERR_INVALID_ARG; } gn_leaf_config_handle_intl_t _leaf_config = @@ -2457,7 +2514,7 @@ gn_err_t gn_leaf_param_set_double(const gn_leaf_handle_t leaf_config, gn_err_t gn_leaf_param_set_string(const gn_leaf_handle_t leaf_config, const char *name, char *val) { if (leaf_config == NULL || name == NULL) { - gn_log(TAG, GN_LOG_ERROR, "gn_leaf_param_set_string - invalid args"); + ESP_LOGE(TAG, "gn_leaf_param_set_string - invalid args"); return GN_RET_ERR_INVALID_ARG; } gn_leaf_config_handle_intl_t _leaf_config = @@ -2534,7 +2591,7 @@ void* _gn_leaf_context_add_to_leaf(const gn_leaf_handle_t leaf, char *key, void *value) { if (!leaf || !key || !value) { - gn_log(TAG, GN_LOG_ERROR, "gn_leaf_context_add incorrect parameters"); + ESP_LOGE(TAG, "gn_leaf_context_add incorrect parameters"); return NULL; } @@ -2549,8 +2606,7 @@ void* _gn_leaf_context_add_to_leaf(const gn_leaf_handle_t leaf, char *key, void* _gn_leaf_context_remove_to_leaf(const gn_leaf_handle_t leaf, char *key) { if (!leaf || !key) { - gn_log(TAG, GN_LOG_ERROR, - "gn_leaf_context_remove_to_leaf incorrect parameters"); + ESP_LOGE(TAG, "gn_leaf_context_remove_to_leaf incorrect parameters"); return NULL; } @@ -2565,8 +2621,7 @@ void* _gn_leaf_context_remove_to_leaf(const gn_leaf_handle_t leaf, char *key) { void* _gn_leaf_context_get_key_to_leaf(const gn_leaf_handle_t leaf, char *key) { if (!leaf || !key) { - gn_log(TAG, GN_LOG_ERROR, - "gn_leaf_context_remove_to_leaf incorrect parameters"); + ESP_LOGE(TAG, "gn_leaf_context_remove_to_leaf incorrect parameters"); return NULL; } @@ -2689,7 +2744,7 @@ gn_err_t gn_firmware_update() { ESP_LOGE(TAG, "OTA message not sent"); } vTaskDelay(1000 / portTICK_PERIOD_MS); - xTaskCreate(_gn_ota_task, "gn_ota_task", 8196, NULL, 10, + xTaskCreate(gn_ota_task, "gn_ota_task", 8196, NULL, 10, NULL); #endif return GN_RET_OK; @@ -2758,8 +2813,7 @@ gn_err_t gn_log(char *log_tag, gn_log_level_t level, const char *message, ...) { portMAX_DELAY); if (ret != GN_RET_OK) { - gn_log(TAG, GN_LOG_ERROR, "Not possible to post log event: %s", - formatted_message); + ESP_LOGE(TAG, "Not possible to post log event: %s", formatted_message); return ret; } @@ -2847,16 +2901,16 @@ gn_config_handle_t gn_init(gn_config_init_param_t *config_init) { "error on timer init: %s", esp_err_to_name(ret)); //init wifi - ESP_GOTO_ON_ERROR(_gn_init_wifi(_gn_default_conf), err_net, TAG, + ESP_GOTO_ON_ERROR(gn_wifi_init(_gn_default_conf), err_net, TAG, "error on wifi init: %s", esp_err_to_name(ret)); //init time sync. note: if bad, continue - ESP_GOTO_ON_ERROR(_gn_init_time_sync(_gn_default_conf), err_timesync, TAG, - "error on time sync init: %s", esp_err_to_name(ret)); + ESP_GOTO_ON_ERROR(gn_wifi_time_sync_init(_gn_default_conf), err_timesync, + TAG, "error on time sync init: %s", esp_err_to_name(ret)); err_timesync: //init mqtt system - ESP_GOTO_ON_ERROR(gn_mqtt_init(_gn_default_conf), err_srv, TAG, + ESP_GOTO_ON_ERROR(gn_mqtt_start(_gn_default_conf), err_srv, TAG, "error on server init: %s", esp_err_to_name(ret)); #endif diff --git a/components/grownode/grownode_intl.h b/components/grownode/grownode_intl.h index 87dc1b7a..3d2af7a7 100755 --- a/components/grownode/grownode_intl.h +++ b/components/grownode/grownode_intl.h @@ -52,10 +52,11 @@ struct gn_config_t { esp_event_loop_handle_t event_loop; wifi_init_config_t wifi_config; wifi_prov_mgr_config_t prov_config; + esp_timer_handle_t keepalive_timer_handler; char deviceName[17]; uint8_t macAddress[6]; gn_node_status_t status; - gn_node_handle_intl_t node_config; + gn_node_handle_intl_t node_handle; gn_config_init_param_t *config_init_params; }; diff --git a/components/grownode/leaves/gn_gpio.c b/components/grownode/leaves/gn_gpio.c index 40b787dd..07b4cdbe 100644 --- a/components/grownode/leaves/gn_gpio.c +++ b/components/grownode/leaves/gn_gpio.c @@ -217,7 +217,7 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { //check for messages and cycle every 100ms if (xQueueReceive(gn_leaf_get_event_queue(leaf_config), &evt, - pdMS_TO_TICKS(100)) == pdPASS) { + portMAX_DELAY) == pdPASS) { ESP_LOGD(TAG, "%s - received message: %d", leaf_name, evt.id); @@ -325,7 +325,8 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { } - vTaskDelay(1000 / portTICK_PERIOD_MS); + //ESP_LOGD(TAG, "sleeping.."); + //vTaskDelay(1000 / portTICK_PERIOD_MS); } } From 2dbb2589b63e0ff58f1f1cebf0d2358073d4ddac Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sat, 19 Feb 2022 18:26:06 +0100 Subject: [PATCH 06/31] bug fixes --- components/grownode/boards/gn_easypot1.c | 6 ++++-- components/grownode/gn_commons.h | 2 +- components/grownode/grownode.c | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/components/grownode/boards/gn_easypot1.c b/components/grownode/boards/gn_easypot1.c index 34e12b4f..2cb3ee69 100644 --- a/components/grownode/boards/gn_easypot1.c +++ b/components/grownode/boards/gn_easypot1.c @@ -118,12 +118,13 @@ void gn_configure_easypot1(gn_node_handle_t node) { //set initial status to active (on) gn_leaf_param_init_bool(moist, GN_CMS_PARAM_ACTIVE, true); + moisture_callback(); //creates a timer that checks moisture every seconds, using esp_timer API esp_timer_handle_t timer_moisture_handler; esp_timer_create_args_t timer_moisture_args = { .callback = &moisture_callback, .name = "moist_timer" }; esp_timer_create(&timer_moisture_args, &timer_moisture_handler); - esp_timer_start_periodic(timer_moisture_handler, 20 * 1000000); + esp_timer_start_periodic(timer_moisture_handler, 5 * 1000000); //creates the temperature sensor temp = gn_leaf_create(node, "temp", gn_ds18b20_config, 4096); @@ -142,12 +143,13 @@ void gn_configure_easypot1(gn_node_handle_t node) { led_moist = gn_leaf_create(node, "led_moist", gn_led_config, 4096); gn_leaf_param_init_double(led_moist, GN_LED_PARAM_GPIO, 27); + temp_callback(); //creates a timer that checks temperature every seconds, using esp_timer API esp_timer_handle_t timer_temp_handler; esp_timer_create_args_t timer_temp_args = { .callback = &temp_callback, .name = "temp_timer" }; esp_timer_create(&timer_temp_args, &timer_temp_handler); - esp_timer_start_periodic(timer_temp_handler, 20 * 1000000); + esp_timer_start_periodic(timer_temp_handler, 5 * 1000000); } diff --git a/components/grownode/gn_commons.h b/components/grownode/gn_commons.h index 48f600bf..e2e98109 100755 --- a/components/grownode/gn_commons.h +++ b/components/grownode/gn_commons.h @@ -116,7 +116,7 @@ typedef struct { bool server_board_id_topic; char server_base_topic[80]; char server_url[255]; - uint32_t server_keepalive_timer_sec; + uint16_t server_keepalive_timer_sec; bool server_discovery; char server_discovery_prefix[80]; char firmware_url[255]; diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index 33b353fd..4938e789 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -214,7 +214,8 @@ static bool IRAM_ATTR _gn_timer_callback_isr(void *args) { } */ -void _gn_timer_callback(gn_config_handle_intl_t conf) { +void _gn_keepalive_callback(gn_config_handle_intl_t conf) { + ESP_LOGI(TAG, "_gn_keepalive_callback"); esp_event_post_to(gn_event_loop, GN_BASE_EVENT, GN_SRV_KEEPALIVE_TRIGGERED_EVENT, NULL, 0, portMAX_DELAY); } @@ -224,7 +225,7 @@ void _gn_keepalive_start(gn_config_handle_intl_t conf) { if(!conf->keepalive_timer_handler) return; //timer_start(TIMER_GROUP_0, TIMER_0); if (!esp_timer_is_active(conf->keepalive_timer_handler)) - esp_timer_start_periodic(conf->keepalive_timer_handler, conf->config_init_params->server_keepalive_timer_sec * 1000); + esp_timer_start_periodic(conf->keepalive_timer_handler, conf->config_init_params->server_keepalive_timer_sec * 1000000); ESP_LOGD(TAG, "timer started"); } @@ -417,7 +418,7 @@ esp_err_t _gn_init_keepalive_timer(gn_config_handle_intl_t conf) { */ //creates the blink timer - const esp_timer_create_args_t keepalive_timer_args = { .callback = &_gn_timer_callback, + const esp_timer_create_args_t keepalive_timer_args = { .callback = &_gn_keepalive_callback, .arg = conf, .name = "keepalive_timer" }; return esp_timer_create(&keepalive_timer_args, &conf->keepalive_timer_handler); From b48bdb274712761fe81ec89417e820c956325b24 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sat, 19 Feb 2022 18:26:32 +0100 Subject: [PATCH 07/31] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6818f9d1..e1534eed 100755 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ sdkconfig sdkconfig sdkconfig +.project From 358f8b7667a6650d09951384ddcfbbd1ca300795 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sat, 19 Feb 2022 18:26:57 +0100 Subject: [PATCH 08/31] Update main.c --- main/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/main.c b/main/main.c index 379b44ec..7ff2473c 100644 --- a/main/main.c +++ b/main/main.c @@ -22,7 +22,7 @@ #include "grownode.h" //include the board you want to start here -#include "gn_blink.h" +#include "gn_easypot1.h" #define TASK_STACK_SIZE 8192*4 @@ -55,12 +55,12 @@ void app_main(void) { .server_board_id_topic = false, .server_base_topic = "/grownode/test", .server_url = "mqtt://192.168.1.10:1883", - .server_keepalive_timer_sec = 60, + .server_keepalive_timer_sec = 600, .server_discovery = false, .server_discovery_prefix = "homeassistant", .firmware_url = "http://myserver/myfirmware.bin", .sntp_url = "pool.ntp.org", - .wakeup_time_millisec = 20000LL, + .wakeup_time_millisec = 10000LL, .sleep_delay_millisec = 50LL, .sleep_time_millisec = 20000LL, .sleep_mode = GN_SLEEP_MODE_LIGHT @@ -80,7 +80,7 @@ void app_main(void) { gn_node_handle_t node = gn_node_create(config, "node"); //the board to start - gn_configure_blink(node); + gn_configure_easypot1(node); //finally, start node gn_node_start(node); From a41ae20d4271062bb3650b82803f398e8aeb4a36 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Tue, 22 Feb 2022 07:28:31 +0100 Subject: [PATCH 09/31] modified logging messages --- components/grownode/leaves/gn_gpio.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/components/grownode/leaves/gn_gpio.c b/components/grownode/leaves/gn_gpio.c index 07b4cdbe..b9243157 100644 --- a/components/grownode/leaves/gn_gpio.c +++ b/components/grownode/leaves/gn_gpio.c @@ -98,6 +98,11 @@ typedef struct { gn_leaf_descriptor_handle_t gn_gpio_config(gn_leaf_handle_t leaf_config) { + char leaf_name[GN_LEAF_NAME_SIZE]; + gn_leaf_get_name(leaf_config, leaf_name); + + ESP_LOGD(TAG, "[%s] gn_gpio_config", leaf_name); + gn_leaf_descriptor_handle_t descriptor = (gn_leaf_descriptor_handle_t) malloc(sizeof(gn_leaf_descriptor_t)); strncpy(descriptor->type, GN_LEAF_GPIO_TYPE, GN_LEAF_DESC_TYPE_SIZE); @@ -136,7 +141,7 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { char leaf_name[GN_LEAF_NAME_SIZE]; gn_leaf_get_name(leaf_config, leaf_name); - ESP_LOGD(TAG, "Initializing gpio leaf %s..", leaf_name); + ESP_LOGD(TAG, "[%s] gn_gpio_task", leaf_name); const size_t GN_GPIO_STATE_STOP = 0; const size_t GN_GPIO_STATE_RUNNING = 1; @@ -213,13 +218,13 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { //task cycle while (true) { - //ESP_LOGD(TAG, "task cycle.."); + ESP_LOGD(TAG, "[%s] task cycle..", leaf_name); //check for messages and cycle every 100ms if (xQueueReceive(gn_leaf_get_event_queue(leaf_config), &evt, portMAX_DELAY) == pdPASS) { - ESP_LOGD(TAG, "%s - received message: %d", leaf_name, evt.id); + ESP_LOGD(TAG, "[%s] received message: %d", leaf_name, evt.id); //event arrived for this node switch (evt.id) { @@ -227,8 +232,8 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { //parameter change case GN_LEAF_PARAM_CHANGE_REQUEST_EVENT: - ESP_LOGD(TAG, "request to update param %s, data = '%s'", - evt.param_name, evt.data); + ESP_LOGD(TAG, "[%s] request to update param %s, data = '%s'", + leaf_name, evt.param_name, evt.data); //parameter is status if (gn_leaf_event_mask_param(&evt, data->gn_gpio_toggle_param) @@ -244,7 +249,7 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { status = _active; - ESP_LOGD(TAG, "%s - gpio %d, toggle %d, inverted %d", + ESP_LOGD(TAG, "[%s] - gpio %d, toggle %d, inverted %d", leaf_name, (int )gpio, status ? 1 : 0, inverted ? 1 : 0); @@ -275,7 +280,7 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { inverted = _inverted; - ESP_LOGD(TAG, "%s - gpio %d, toggle %d, inverted %d", + ESP_LOGD(TAG, "[%s] - gpio %d, toggle %d, inverted %d", leaf_name, (int )gpio, status ? 1 : 0, inverted ? 1 : 0); From 3d19f2d0acbb4305397891e5bc1dfd621d102471 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Tue, 22 Feb 2022 07:28:42 +0100 Subject: [PATCH 10/31] added parasitic mode --- components/grownode/leaves/gn_ds18b20.c | 16 +++++++++++++++- components/grownode/leaves/gn_ds18b20.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/components/grownode/leaves/gn_ds18b20.c b/components/grownode/leaves/gn_ds18b20.c index d9beb746..1293f961 100755 --- a/components/grownode/leaves/gn_ds18b20.c +++ b/components/grownode/leaves/gn_ds18b20.c @@ -78,6 +78,7 @@ typedef struct { gn_leaf_param_handle_t update_time_param; gn_leaf_param_handle_t gpio_param; gn_leaf_param_handle_t active_param; + gn_leaf_param_handle_t parasitic_param; } gn_ds18b20_data_t; @@ -126,12 +127,19 @@ void gn_ds18b20_temp_sensor_collect(gn_leaf_handle_t leaf_config) { double gpio; gn_leaf_param_get_double(leaf_config, GN_DS18B20_PARAM_GPIO, &gpio); + bool parasitic; + gn_leaf_param_get_bool(leaf_config, GN_DS18B20_PARAM_PARASITIC, ¶sitic); + if (active == true) { ESP_LOGD(TAG, "[%s] reading from GPIO %d..", leaf_name, (int )gpio); + for (int i = 0; i < GN_DS18B20_MAX_SENSORS; i++) { + if (data->addrs[i]) + ds18x20_measure(gpio, data->addrs[i], parasitic); + } //read data from sensors using GPIO parameter - res = ds18x20_measure_and_read_multi(gpio, data->addrs, + res = ds18x20_read_temp_multi(gpio, data->addrs, data->sensor_count, data->temp); if (res != ESP_OK) { @@ -213,6 +221,12 @@ gn_leaf_descriptor_handle_t gn_ds18b20_config(gn_leaf_handle_t leaf_config) { gn_leaf_param_add_to_leaf(leaf_config, data->temp_param[i]); } + data->parasitic_param = gn_leaf_param_create(leaf_config, + GN_DS18B20_PARAM_PARASITIC, GN_VAL_TYPE_BOOLEAN, (gn_val_t ) { .b = + false }, GN_LEAF_PARAM_ACCESS_NODE, + GN_LEAF_PARAM_STORAGE_PERSISTED, NULL); + gn_leaf_param_add_to_leaf(leaf_config, data->parasitic_param); + descriptor->status = GN_LEAF_STATUS_INITIALIZED; descriptor->data = data; return descriptor; diff --git a/components/grownode/leaves/gn_ds18b20.h b/components/grownode/leaves/gn_ds18b20.h index 49b4d6d3..d622fa91 100755 --- a/components/grownode/leaves/gn_ds18b20.h +++ b/components/grownode/leaves/gn_ds18b20.h @@ -35,6 +35,7 @@ static const char GN_DS18B20_PARAM_UPDATE_TIME_SEC[16] = "upd_time_sec"; /*!< se static const char GN_DS18B20_PARAM_GPIO[5] = "gpio"; /*!< GPIO connected to the temp sensor */ static const char GN_DS18B20_PARAM_SENSOR_NAMES[GN_DS18B20_MAX_SENSORS][6] = { "temp1", "temp2", "temp3", "temp4" }; +static const char GN_DS18B20_PARAM_PARASITIC[] = "parasitic"; /*!< whether to use the parasitic mode. */ gn_leaf_descriptor_handle_t gn_ds18b20_config(gn_leaf_handle_t leaf_config); From 0f7132e8508b499c8d8011a6305de1a8358f62b7 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Tue, 22 Feb 2022 07:29:44 +0100 Subject: [PATCH 11/31] bugfix: leaves not collecting events may block the event loop --- components/grownode/grownode.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index 4938e789..1b361249 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -262,6 +262,12 @@ gn_leaf_config_handle_intl_t _gn_leaf_get_by_name(gn_config_handle_intl_t conf, gn_err_t _gn_send_event_to_leaf(gn_leaf_config_handle_intl_t leaf_config, gn_leaf_parameter_event_handle_t evt) { + if (!leaf_config || !evt) + return GN_RET_ERR_INVALID_ARG; + + if (leaf_config->node_config->config->status != GN_NODE_STATUS_STARTED) + return GN_RET_ERR_NODE_NOT_STARTED; + ESP_LOGD(TAG_EVENT, "_gn_send_event_to_leaf - id: %d, param %s, leaf %s, data %.*s", evt->id, evt->param_name, evt->leaf_name, evt->data_size, @@ -270,7 +276,7 @@ gn_err_t _gn_send_event_to_leaf(gn_leaf_config_handle_intl_t leaf_config, //make sure data will end with terminating char evt->data[evt->data_size] = '\0'; - if (xQueueSend(leaf_config->event_queue, evt, portMAX_DELAY) != pdTRUE) { + if (xQueueSend(leaf_config->event_queue, evt, pdMS_TO_TICKS(1000)) != pdTRUE) { ESP_LOGE(TAG, "not possible to send message to leaf %s", leaf_config->name); return GN_RET_ERR_EVENT_NOT_SENT; @@ -785,16 +791,17 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { if (_node->config->config_init_params->sleep_mode == GN_SLEEP_MODE_NONE) { while (true) { - vTaskDelay(1000 / portTICK_PERIOD_MS); ESP_LOGI(TAG, "looping. grownode startup status: %s, sleep mode = %d", gn_get_status_description( ((gn_node_handle_intl_t )node)->config), _node->config->config_init_params->sleep_mode); + vTaskDelay(1000 / portTICK_PERIOD_MS); } } else if (_node->config->config_init_params->sleep_mode == GN_SLEEP_MODE_DEEP) { + ESP_LOGI(TAG, "working. grownode startup status: %s, sleep mode = deep, sleep in %"PRIu64" millisec", gn_get_status_description( @@ -806,8 +813,10 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { gn_node_sleep(node, GN_SLEEP_MODE_DEEP, _node->config->config_init_params->sleep_time_millisec); //not needed to cycle as the board will restart + } else if (_node->config->config_init_params->sleep_mode == GN_SLEEP_MODE_LIGHT) { + while (true) { ESP_LOGI(TAG, "working. grownode startup status: %s, sleep mode = light, sleep in %"PRIu64" millisec", @@ -822,8 +831,22 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { ESP_LOGI(TAG, "waking up from light sleep"); } + } else { + ESP_LOGW(TAG, "sleep mode unrecognized: %d", (int)_node->config->config_init_params->sleep_mode); + + while (true) { + ESP_LOGI(TAG, + "looping. grownode startup status: %s, sleep mode = %d", + gn_get_status_description( + ((gn_node_handle_intl_t )node)->config), + _node->config->config_init_params->sleep_mode); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + } + return GN_RET_OK; + } /** From 9d9dc656e5a571170e1db2216a7639679b17ab2d Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Tue, 22 Feb 2022 07:30:11 +0100 Subject: [PATCH 12/31] better easypot1 algorithm --- components/grownode/boards/gn_easypot1.c | 91 +++++++++++------------- main/main.c | 12 ++-- 2 files changed, 47 insertions(+), 56 deletions(-) diff --git a/components/grownode/boards/gn_easypot1.c b/components/grownode/boards/gn_easypot1.c index 2cb3ee69..bf785e1b 100644 --- a/components/grownode/boards/gn_easypot1.c +++ b/components/grownode/boards/gn_easypot1.c @@ -20,6 +20,8 @@ #include "gn_capacitive_moisture_sensor.h" #include "gn_ds18b20.h" #include "gn_led.h" +#include "gn_gpio.h" +#include "soc/touch_sensor_channel.h" #include "gn_easypot1.h" @@ -39,61 +41,59 @@ const double temp_max = 28; const double blink_time_high = 300; const double blink_time_low = 2000; -void moisture_callback() { +void _gn_easypot1_callback() { - double moist_act = 0; - gn_leaf_param_get_double(moist, GN_CMS_PARAM_ACT_LEVEL, &moist_act); - gn_log(TAG, GN_LOG_INFO, "easypot1 - measuring moisture: %f", moist_act); + double temp_act = 0; + gn_leaf_param_get_double(temp, GN_DS18B20_PARAM_SENSOR_NAMES[0], &temp_act); + gn_log(TAG, GN_LOG_DEBUG, "easypot1 - measuring temp: %f", temp_act); //turn on the LED if low with a specific frequency - if (moist_act < moist_min && moist_last >= moist_min) { - gn_leaf_param_set_bool(led_moist, GN_LED_PARAM_TOGGLE, true); - gn_leaf_param_set_double(led_moist, GN_LED_PARAM_BLINK_TIME_MS, + if (temp_act < temp_min && temp_last >= temp_min) { + gn_leaf_param_set_bool(led_temp, GN_LED_PARAM_TOGGLE, true); + gn_leaf_param_set_double(led_temp, GN_LED_PARAM_BLINK_TIME_MS, blink_time_low); } //turn on the LED if high with a specific frequency - else if (moist_act > moist_max && moist_last <= moist_max) { - gn_leaf_param_set_bool(led_moist, GN_LED_PARAM_TOGGLE, true); - gn_leaf_param_set_double(led_moist, GN_LED_PARAM_BLINK_TIME_MS, + else if (temp_act > temp_max && temp_last <= temp_max) { + gn_leaf_param_set_bool(led_temp, GN_LED_PARAM_TOGGLE, true); + gn_leaf_param_set_double(led_temp, GN_LED_PARAM_BLINK_TIME_MS, blink_time_high); } //turn off the LED if under normal threshold - else if (moist_act <= moist_max && moist_act >= moist_min) { - gn_leaf_param_set_bool(led_moist, GN_LED_PARAM_TOGGLE, false); + else if (temp_act <= temp_max && temp_act >= temp_min) { + gn_leaf_param_set_bool(led_temp, GN_LED_PARAM_TOGGLE, false); } - moist_last = moist_act; - -} - -void temp_callback() { + temp_last = temp_act; - double temp_act = 0; - gn_leaf_param_get_double(temp, GN_DS18B20_PARAM_SENSOR_NAMES[0], &temp_act); - gn_log(TAG, GN_LOG_INFO, "easypot1 - measuring temp: %f", temp_act); + double moist_act = 0; + gn_leaf_param_get_double(moist, GN_CMS_PARAM_ACT_LEVEL, &moist_act); + gn_log(TAG, GN_LOG_DEBUG, "easypot1 - measuring moisture: %f", moist_act); //turn on the LED if low with a specific frequency - if (temp_act < temp_min && temp_last >= temp_min) { - gn_leaf_param_set_bool(led_temp, GN_LED_PARAM_TOGGLE, true); - gn_leaf_param_set_double(led_temp, GN_LED_PARAM_BLINK_TIME_MS, + if (moist_act < moist_min && moist_last >= moist_min) { + gn_leaf_param_set_bool(led_moist, GN_LED_PARAM_TOGGLE, true); + gn_leaf_param_set_double(led_moist, GN_LED_PARAM_BLINK_TIME_MS, blink_time_low); } //turn on the LED if high with a specific frequency - else if (temp_act > temp_max && temp_last <= temp_max) { - gn_leaf_param_set_bool(led_temp, GN_LED_PARAM_TOGGLE, true); - gn_leaf_param_set_double(led_temp, GN_LED_PARAM_BLINK_TIME_MS, + else if (moist_act > moist_max && moist_last <= moist_max) { + gn_leaf_param_set_bool(led_moist, GN_LED_PARAM_TOGGLE, true); + gn_leaf_param_set_double(led_moist, GN_LED_PARAM_BLINK_TIME_MS, blink_time_high); } //turn off the LED if under normal threshold - else if (temp_act <= temp_max && temp_act >= temp_min) { - gn_leaf_param_set_bool(led_temp, GN_LED_PARAM_TOGGLE, false); + else if (moist_act <= moist_max && moist_act >= moist_min) { + gn_leaf_param_set_bool(led_moist, GN_LED_PARAM_TOGGLE, false); } - temp_last = temp_act; + moist_last = moist_act; + + gn_log(TAG, GN_LOG_DEBUG, "easypot1 - callback ended"); } @@ -104,28 +104,21 @@ void temp_callback() { void gn_configure_easypot1(gn_node_handle_t node) { //leaves - esp_log_level_set("gn_leaf_led", ESP_LOG_INFO); - esp_log_level_set("gn_leaf_cms", ESP_LOG_DEBUG); - esp_log_level_set("gn_leaf_ds18b20", ESP_LOG_DEBUG); + esp_log_level_set("gn_leaf_led", esp_log_level_get(TAG)); + esp_log_level_set("gn_leaf_gpio", esp_log_level_get(TAG)); + esp_log_level_set("gn_leaf_cms", esp_log_level_get(TAG)); + esp_log_level_set("gn_leaf_ds18b20", esp_log_level_get(TAG)); //creates the moisture sensor moist = gn_leaf_create(node, "moist", gn_capacitive_moisture_sensor_config, 4096); - //set the channel 4 - gn_leaf_param_init_double(moist, GN_CMS_PARAM_ADC_CHANNEL, 4); //GPIO12 + //set the channel + gn_leaf_param_init_double(moist, GN_CMS_PARAM_ADC_CHANNEL, 5); //gpio33 //set update time gn_leaf_param_init_double(moist, GN_CMS_PARAM_UPDATE_TIME_SEC, 15); //set initial status to active (on) gn_leaf_param_init_bool(moist, GN_CMS_PARAM_ACTIVE, true); - moisture_callback(); - //creates a timer that checks moisture every seconds, using esp_timer API - esp_timer_handle_t timer_moisture_handler; - esp_timer_create_args_t timer_moisture_args = { .callback = - &moisture_callback, .name = "moist_timer" }; - esp_timer_create(&timer_moisture_args, &timer_moisture_handler); - esp_timer_start_periodic(timer_moisture_handler, 5 * 1000000); - //creates the temperature sensor temp = gn_leaf_create(node, "temp", gn_ds18b20_config, 4096); //set GPIO @@ -143,14 +136,12 @@ void gn_configure_easypot1(gn_node_handle_t node) { led_moist = gn_leaf_create(node, "led_moist", gn_led_config, 4096); gn_leaf_param_init_double(led_moist, GN_LED_PARAM_GPIO, 27); - temp_callback(); - //creates a timer that checks temperature every seconds, using esp_timer API - esp_timer_handle_t timer_temp_handler; - esp_timer_create_args_t timer_temp_args = { .callback = &temp_callback, - .name = "temp_timer" }; - esp_timer_create(&timer_temp_args, &timer_temp_handler); - esp_timer_start_periodic(timer_temp_handler, 5 * 1000000); - + //creates a timer that checks temperature and moisture, using esp_timer API + esp_timer_handle_t sensor_temp_handler; + esp_timer_create_args_t sensor_temp_args = { .callback = &_gn_easypot1_callback, + .name = "easypot1_timer" }; + esp_timer_create(&sensor_temp_args, &sensor_temp_handler); + esp_timer_start_periodic(sensor_temp_handler, 30 * 1000000); } diff --git a/main/main.c b/main/main.c index 7ff2473c..960b3c7a 100644 --- a/main/main.c +++ b/main/main.c @@ -53,17 +53,17 @@ void app_main(void) { .provisioning_password = "grownode", .wifi_retries_before_reset_provisioning = 5, .server_board_id_topic = false, - .server_base_topic = "/grownode/test", + .server_base_topic = "grownode/stella", .server_url = "mqtt://192.168.1.10:1883", - .server_keepalive_timer_sec = 600, + .server_keepalive_timer_sec = 3600, .server_discovery = false, .server_discovery_prefix = "homeassistant", - .firmware_url = "http://myserver/myfirmware.bin", + .firmware_url = "http://grownode.duckdns.org/grownode/stella/grownode.bin", .sntp_url = "pool.ntp.org", - .wakeup_time_millisec = 10000LL, + .wakeup_time_millisec = 5000LL, .sleep_delay_millisec = 50LL, - .sleep_time_millisec = 20000LL, - .sleep_mode = GN_SLEEP_MODE_LIGHT + .sleep_time_millisec = 120000LL, + .sleep_mode = GN_SLEEP_MODE_DEEP }; //creates the config handle From c020ece2e0ebcfa9bd3920aeb90fb5ac8b093eb3 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Tue, 22 Feb 2022 07:37:11 +0100 Subject: [PATCH 13/31] queue size parametrization --- components/grownode/grownode.c | 2 +- components/grownode/grownode.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index 1b361249..54f80538 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -1061,7 +1061,7 @@ gn_leaf_handle_t gn_leaf_create(gn_node_handle_t node_config, const char *name, l_c->leaf_context = gn_leaf_context_create(); l_c->display_container = NULL; //l_c->display_task = display_task; - l_c->event_queue = xQueueCreate(1, sizeof(gn_leaf_parameter_event_t)); + l_c->event_queue = xQueueCreate(GN_NODE_LEAF_QUEUE_SIZE, sizeof(gn_leaf_parameter_event_t)); if (l_c->event_queue == NULL) { return NULL; } diff --git a/components/grownode/grownode.h b/components/grownode/grownode.h index b07c8fbe..04ad08f6 100755 --- a/components/grownode/grownode.h +++ b/components/grownode/grownode.h @@ -36,6 +36,7 @@ extern "C" { #endif /* CONFIG_GROWNODE_PROV_TRANSPORT_BLE */ #define GN_NODE_LEAVES_MAX_SIZE 64 +#define GN_NODE_LEAF_QUEUE_SIZE 3 //functions gn_node_handle_t gn_node_create(gn_config_handle_t config, From effc1205669a050f1fb1dcb3ff459af551151d07 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Fri, 25 Feb 2022 07:27:01 +0100 Subject: [PATCH 14/31] Update main.c --- main/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/main.c b/main/main.c index 960b3c7a..fd2d6694 100644 --- a/main/main.c +++ b/main/main.c @@ -22,7 +22,7 @@ #include "grownode.h" //include the board you want to start here -#include "gn_easypot1.h" +#include "gn_blink.h" #define TASK_STACK_SIZE 8192*4 @@ -80,7 +80,7 @@ void app_main(void) { gn_node_handle_t node = gn_node_create(config, "node"); //the board to start - gn_configure_easypot1(node); + gn_configure_blink(node); //finally, start node gn_node_start(node); From 23a55fccad06500211ac32aa7e7ab2c484eb5f6e Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Fri, 25 Feb 2022 07:44:18 +0100 Subject: [PATCH 15/31] reconfiguring for new release --- components/grownode/gn_commons.h | 8 ++++---- components/grownode/grownode.c | 5 +++-- main/main.c | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/components/grownode/gn_commons.h b/components/grownode/gn_commons.h index e2e98109..990fd4f4 100755 --- a/components/grownode/gn_commons.h +++ b/components/grownode/gn_commons.h @@ -121,10 +121,10 @@ typedef struct { char server_discovery_prefix[80]; char firmware_url[255]; char sntp_url[255]; - uint64_t wakeup_time_millisec; - uint64_t sleep_time_millisec; - uint64_t sleep_delay_millisec; - gn_sleep_mode_t sleep_mode; + uint64_t wakeup_time_millisec; /*! if sleep mode is GN_SLEEP_MODE_LIGHT or GN_SLEEP_MODE_DEEP, sets for how long the board must stay on (counted from boot) !*/ + uint64_t sleep_time_millisec; /*! if sleep mode is GN_SLEEP_MODE_LIGHT or GN_SLEEP_MODE_DEEP, sets for how long the board must sleep !*/ + uint64_t sleep_delay_millisec; /*! if sleep mode is GN_SLEEP_MODE_LIGHT or GN_SLEEP_MODE_DEEP, sets for how long the board must stay on waiting for leaves to complete its job before sleeping!*/ + gn_sleep_mode_t sleep_mode; /*! define if and how the board must sleep !*/ } gn_config_init_param_t; diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index 54f80538..01631872 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -854,7 +854,7 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { */ void _gn_wait_for_blocked_leaves(gn_node_handle_intl_t _node) { - + /* if (!_node) return; //waits until all leaves has reached blocked status @@ -881,6 +881,7 @@ void _gn_wait_for_blocked_leaves(gn_node_handle_intl_t _node) { if (leaves_working) vTaskDelay(pdMS_TO_TICKS(5)); } + */ } @@ -974,7 +975,7 @@ gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, wakeup_reason = GN_SLEEP_MODE_LIGHT; - //_gn_wait_for_blocked_leaves(_node); + _gn_wait_for_blocked_leaves(_node); esp_sleep_enable_timer_wakeup(millisec * 1000LL); esp_light_sleep_start(); diff --git a/main/main.c b/main/main.c index fd2d6694..3252c71e 100644 --- a/main/main.c +++ b/main/main.c @@ -53,12 +53,12 @@ void app_main(void) { .provisioning_password = "grownode", .wifi_retries_before_reset_provisioning = 5, .server_board_id_topic = false, - .server_base_topic = "grownode/stella", + .server_base_topic = "grownode/blink", .server_url = "mqtt://192.168.1.10:1883", .server_keepalive_timer_sec = 3600, .server_discovery = false, .server_discovery_prefix = "homeassistant", - .firmware_url = "http://grownode.duckdns.org/grownode/stella/grownode.bin", + .firmware_url = "http://grownode.duckdns.org/grownode/blink/grownode.bin", .sntp_url = "pool.ntp.org", .wakeup_time_millisec = 5000LL, .sleep_delay_millisec = 50LL, From 05f5e7d686879f436cf26b06cd9f113126cafd12 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Fri, 25 Feb 2022 07:44:32 +0100 Subject: [PATCH 16/31] power management tutorial added --- docs/reference/power_management.md | 31 ++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 32 insertions(+) create mode 100644 docs/reference/power_management.md diff --git a/docs/reference/power_management.md b/docs/reference/power_management.md new file mode 100644 index 00000000..2addd70b --- /dev/null +++ b/docs/reference/power_management.md @@ -0,0 +1,31 @@ + +# Sleep Modes + +GrowNode implements Deep and Light sleep functionalities. These are useful to save energy when no active actions are needed and let the board be battery powered. + +In order to understand basic sleep mode functionalities, please refer to [ESP32 reference guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html) + +## Configuration + +Sleep management is configured in init parameters: + +``` + gn_config_init_param_t config_init = { + ... + .wakeup_time_millisec = 5000LL, + .sleep_delay_millisec = 50LL, + .sleep_time_millisec = 120000LL, + .sleep_mode = GN_SLEEP_MODE_DEEP + }; + +``` + +Main parameters are: + - `gn_sleep_mode_t sleep_mode` - define if and how the board must sleep. possible values are GN_SLEEP_MODE_NONE (never sleeps), GN_SLEEP_MODE_LIGHT (light sleep), GN_SLEEP_MODE_DEEP (deep sleep) + - `uint64_t wakeup_time_millisec` - if sleep mode is GN_SLEEP_MODE_LIGHT or GN_SLEEP_MODE_DEEP, sets for how long the board must stay on (counted from boot) + - `uint64_t sleep_time_millisec` - if sleep mode is GN_SLEEP_MODE_LIGHT or GN_SLEEP_MODE_DEEP, sets for how long the board must sleep + - `uint64_t sleep_delay_millisec` - if sleep mode is GN_SLEEP_MODE_LIGHT or GN_SLEEP_MODE_DEEP, sets for how long the board must stay on waiting for leaves to complete its job before sleeping + +## Sleep start cycle + +In light and deep sleep mode, a timer sets until when the board must stay on. Upon timeout, the board sends to its leaves a GN_NODE_LIGHT_SLEEP_START_EVENT o GN_NODE_DEEP_SLEEP_START_EVENT. This allows the leaves to perform housekeeping work in preparation to sleep. Then, waits for `sleep_delay_millisec`. After this time, its starts checking whether leaves are still working. Once all leaves are in blocking status if releases the MQTT and WIFI connection and start the sleep cycle for `sleep_time_millisec`. diff --git a/mkdocs.yml b/mkdocs.yml index a7b3f666..7f51b11f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -36,6 +36,7 @@ nav: - 'reference/events.md' - 'reference/networking.md' - 'reference/mqtt.md' + - 'reference/power_management.md' - 'reference/display.md' - 'reference/logging.md' - 'reference/error.md' From d2e0b45a77806fc7b3c9515898e056e6ff45dcea Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sat, 26 Feb 2022 10:48:14 +0100 Subject: [PATCH 17/31] Update power_management.md --- docs/reference/power_management.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/reference/power_management.md b/docs/reference/power_management.md index 2addd70b..fd421cc7 100644 --- a/docs/reference/power_management.md +++ b/docs/reference/power_management.md @@ -26,6 +26,11 @@ Main parameters are: - `uint64_t sleep_time_millisec` - if sleep mode is GN_SLEEP_MODE_LIGHT or GN_SLEEP_MODE_DEEP, sets for how long the board must sleep - `uint64_t sleep_delay_millisec` - if sleep mode is GN_SLEEP_MODE_LIGHT or GN_SLEEP_MODE_DEEP, sets for how long the board must stay on waiting for leaves to complete its job before sleeping -## Sleep start cycle +## Sleep start cycle (automatic) In light and deep sleep mode, a timer sets until when the board must stay on. Upon timeout, the board sends to its leaves a GN_NODE_LIGHT_SLEEP_START_EVENT o GN_NODE_DEEP_SLEEP_START_EVENT. This allows the leaves to perform housekeeping work in preparation to sleep. Then, waits for `sleep_delay_millisec`. After this time, its starts checking whether leaves are still working. Once all leaves are in blocking status if releases the MQTT and WIFI connection and start the sleep cycle for `sleep_time_millisec`. + +## Manual sleep cycle management + +If you want to sleep the board directly from your code, you can use the `gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, uint64_t delay_msec, uint64_t millisec)` function. It starts the same cycle called in automatic mode. + From 2b9547412628d6e2f7cef021ffa7a4a3ae5c37aa Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sat, 26 Feb 2022 10:48:38 +0100 Subject: [PATCH 18/31] added wait time for manual sleep cycle call --- components/grownode/grownode.c | 167 ++++++++++++++++++--------------- components/grownode/grownode.h | 2 +- 2 files changed, 90 insertions(+), 79 deletions(-) diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index 01631872..8bbc2103 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -201,18 +201,18 @@ esp_err_t _gn_init_spiffs(gn_config_handle_intl_t conf) { } /* -#define TIMER_DIVIDER (16) // Hardware timer clock divider -#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds - -static bool IRAM_ATTR _gn_timer_callback_isr(void *args) { - BaseType_t high_task_awoken = pdFALSE; - if (!gn_event_loop) - return high_task_awoken; - esp_event_isr_post_to(gn_event_loop, GN_BASE_EVENT, - GN_SRV_KEEPALIVE_TRIGGERED_EVENT, NULL, 0, &high_task_awoken); - return high_task_awoken == pdTRUE; -} -*/ + #define TIMER_DIVIDER (16) // Hardware timer clock divider + #define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds + + static bool IRAM_ATTR _gn_timer_callback_isr(void *args) { + BaseType_t high_task_awoken = pdFALSE; + if (!gn_event_loop) + return high_task_awoken; + esp_event_isr_post_to(gn_event_loop, GN_BASE_EVENT, + GN_SRV_KEEPALIVE_TRIGGERED_EVENT, NULL, 0, &high_task_awoken); + return high_task_awoken == pdTRUE; + } + */ void _gn_keepalive_callback(gn_config_handle_intl_t conf) { ESP_LOGI(TAG, "_gn_keepalive_callback"); @@ -222,23 +222,27 @@ void _gn_keepalive_callback(gn_config_handle_intl_t conf) { void _gn_keepalive_start(gn_config_handle_intl_t conf) { - if(!conf->keepalive_timer_handler) return; + if (!conf->keepalive_timer_handler) + return; //timer_start(TIMER_GROUP_0, TIMER_0); if (!esp_timer_is_active(conf->keepalive_timer_handler)) - esp_timer_start_periodic(conf->keepalive_timer_handler, conf->config_init_params->server_keepalive_timer_sec * 1000000); + esp_timer_start_periodic(conf->keepalive_timer_handler, + conf->config_init_params->server_keepalive_timer_sec * 1000000); ESP_LOGD(TAG, "timer started"); } void _gn_keepalive_stop(gn_config_handle_intl_t conf) { - if(!conf->keepalive_timer_handler) return; + if (!conf->keepalive_timer_handler) + return; //timer_pause(TIMER_GROUP_0, TIMER_0); if (!esp_timer_is_active(conf->keepalive_timer_handler)) esp_timer_stop(conf->keepalive_timer_handler); ESP_LOGD(TAG, "timer paused"); } -gn_leaf_config_handle_intl_t _gn_leaf_get_by_name(gn_config_handle_intl_t conf, char *leaf_name) { +gn_leaf_config_handle_intl_t _gn_leaf_get_by_name(gn_config_handle_intl_t conf, + char *leaf_name) { gn_leaves_list leaves = conf->node_handle->leaves; @@ -285,10 +289,11 @@ gn_err_t _gn_send_event_to_leaf(gn_leaf_config_handle_intl_t leaf_config, return GN_RET_OK; } -void _gn_evt_handler(void* handler_data, esp_event_base_t base, int32_t id, - void* event_data) { +void _gn_evt_handler(void *handler_data, esp_event_base_t base, int32_t id, + void *event_data) { - if (!handler_data) return; + if (!handler_data) + return; gn_config_handle_intl_t conf = (gn_config_handle_intl_t) handler_data; @@ -359,8 +364,7 @@ void _gn_evt_handler(void* handler_data, esp_event_base_t base, int32_t id, case GN_SRV_KEEPALIVE_TRIGGERED_EVENT: //publish node - if (gn_mqtt_send_node_config(conf->node_handle) - != GN_RET_OK) { + if (gn_mqtt_send_node_config(conf->node_handle) != GN_RET_OK) { ESP_LOGE(TAG, "Error in sending node config message"); } break; @@ -410,24 +414,25 @@ esp_err_t _gn_init_keepalive_timer(gn_config_handle_intl_t conf) { return ESP_OK; /* - timer_config_t config = { .divider = TIMER_DIVIDER, .counter_dir = - TIMER_COUNT_UP, .counter_en = TIMER_PAUSE, .alarm_en = - TIMER_ALARM_EN, .auto_reload = 1, }; // default clock source is APB - timer_init(TIMER_GROUP_0, TIMER_0, &config); - timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0); - timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, - conf->config_init_params->server_keepalive_timer_sec * TIMER_SCALE); - timer_enable_intr(TIMER_GROUP_0, TIMER_0); - return timer_isr_callback_add(TIMER_GROUP_0, TIMER_0, - _gn_timer_callback_isr, - NULL, 0); - */ + timer_config_t config = { .divider = TIMER_DIVIDER, .counter_dir = + TIMER_COUNT_UP, .counter_en = TIMER_PAUSE, .alarm_en = + TIMER_ALARM_EN, .auto_reload = 1, }; // default clock source is APB + timer_init(TIMER_GROUP_0, TIMER_0, &config); + timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0); + timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, + conf->config_init_params->server_keepalive_timer_sec * TIMER_SCALE); + timer_enable_intr(TIMER_GROUP_0, TIMER_0); + return timer_isr_callback_add(TIMER_GROUP_0, TIMER_0, + _gn_timer_callback_isr, + NULL, 0); + */ //creates the blink timer - const esp_timer_create_args_t keepalive_timer_args = { .callback = &_gn_keepalive_callback, - .arg = conf, .name = "keepalive_timer" }; + const esp_timer_create_args_t keepalive_timer_args = { .callback = + &_gn_keepalive_callback, .arg = conf, .name = "keepalive_timer" }; - return esp_timer_create(&keepalive_timer_args, &conf->keepalive_timer_handler); + return esp_timer_create(&keepalive_timer_args, + &conf->keepalive_timer_handler); } /** @@ -811,6 +816,7 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { _node->config->config_init_params->wakeup_time_millisec / portTICK_PERIOD_MS); gn_node_sleep(node, GN_SLEEP_MODE_DEEP, + _node->config->config_init_params->sleep_delay_millisec, _node->config->config_init_params->sleep_time_millisec); //not needed to cycle as the board will restart @@ -827,12 +833,14 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { _node->config->config_init_params->wakeup_time_millisec / portTICK_PERIOD_MS); gn_node_sleep(node, GN_SLEEP_MODE_LIGHT, + _node->config->config_init_params->sleep_delay_millisec, _node->config->config_init_params->sleep_time_millisec); ESP_LOGI(TAG, "waking up from light sleep"); } } else { - ESP_LOGW(TAG, "sleep mode unrecognized: %d", (int)_node->config->config_init_params->sleep_mode); + ESP_LOGW(TAG, "sleep mode unrecognized: %d", + (int )_node->config->config_init_params->sleep_mode); while (true) { ESP_LOGI(TAG, @@ -855,45 +863,49 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { void _gn_wait_for_blocked_leaves(gn_node_handle_intl_t _node) { /* - if (!_node) return; + if (!_node) return; - //waits until all leaves has reached blocked status - int leaves_count = _node->leaves.last; - ESP_LOGI(TAG, "leaves_count %d", leaves_count); + //waits until all leaves has reached blocked status + int leaves_count = _node->leaves.last; + ESP_LOGI(TAG, "leaves_count %d", leaves_count); - bool leaves_working = false; - bool this_leaf_working = false; + bool leaves_working = false; + bool this_leaf_working = false; - while (!leaves_working) { + while (!leaves_working) { - for (int i = 0; i < leaves_count; i++) { - if (_node->leaves.at[i] && _node->leaves.at[i]->task_handle) { - this_leaf_working = eTaskGetState( - _node->leaves.at[i]->task_handle) == eRunning; - leaves_working = this_leaf_working || leaves_working; - if (this_leaf_working) { - ESP_LOGI(TAG, "leaves working: %s", - _node->leaves.at[i]->name); - } - } - } + for (int i = 0; i < leaves_count; i++) { + if (_node->leaves.at[i] && _node->leaves.at[i]->task_handle) { + this_leaf_working = eTaskGetState( + _node->leaves.at[i]->task_handle) == eRunning; + leaves_working = this_leaf_working || leaves_working; + if (this_leaf_working) { + ESP_LOGI(TAG, "leaves working: %s", + _node->leaves.at[i]->name); + } + } + } - if (leaves_working) - vTaskDelay(pdMS_TO_TICKS(5)); - } - */ + if (leaves_working) + vTaskDelay(pdMS_TO_TICKS(5)); + } + */ } /** * @brief enter in sleep mode, disabling networking and releasing resources. * - * @param node the node to sleep - * @sleep_mode the type of sleep - * @millisec for how long + * @param node the node to sleep + * @param delay_msec the delay to wait before sleeping + * @param sleep_mode the type of sleep + * @param millisec for how long + * + * @return GN_RET_ERR_INVALID_ARG in case of node null + * @return GN_RET_OK if sleep cycle is completed successfully (only in light sleep, otherwise board restarts) */ gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, - uint64_t millisec) { + uint64_t delay_msec, uint64_t millisec) { if (!node) return GN_RET_ERR_INVALID_ARG; @@ -911,14 +923,13 @@ gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, return GN_RET_ERR_EVENT_LOOP_ERROR; } - ESP_LOGI(TAG, "Preparing deep sleep in %"PRIu64" millisec", - _node->config->config_init_params->sleep_delay_millisec); - //gives some time to handle the event - if (_node->config->config_init_params->sleep_delay_millisec > 0) { - vTaskDelay( - _node->config->config_init_params->sleep_delay_millisec - / portTICK_PERIOD_MS); + if (delay_msec > 0) { + + ESP_LOGI(TAG, "Preparing deep sleep in %"PRIu64" millisec", + delay_msec); + + vTaskDelay(delay_msec / portTICK_PERIOD_MS); } _node->config->status = GN_NODE_STATUS_SLEEPING; @@ -954,14 +965,13 @@ gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, return GN_RET_ERR_EVENT_LOOP_ERROR; } - ESP_LOGI(TAG, "Preparing light sleep in %"PRIu64" millisec", - _node->config->config_init_params->sleep_delay_millisec); + //gives some time to handle the event + if (delay_msec > 0) { + + ESP_LOGI(TAG, "Preparing deep sleep in %"PRIu64" millisec", + delay_msec); -//gives some time to handle the event - if (_node->config->config_init_params->sleep_delay_millisec > 0) { - vTaskDelay( - _node->config->config_init_params->sleep_delay_millisec - / portTICK_PERIOD_MS); + vTaskDelay(delay_msec / portTICK_PERIOD_MS); } _node->config->status = GN_NODE_STATUS_SLEEPING; @@ -1062,7 +1072,8 @@ gn_leaf_handle_t gn_leaf_create(gn_node_handle_t node_config, const char *name, l_c->leaf_context = gn_leaf_context_create(); l_c->display_container = NULL; //l_c->display_task = display_task; - l_c->event_queue = xQueueCreate(GN_NODE_LEAF_QUEUE_SIZE, sizeof(gn_leaf_parameter_event_t)); + l_c->event_queue = xQueueCreate(GN_NODE_LEAF_QUEUE_SIZE, + sizeof(gn_leaf_parameter_event_t)); if (l_c->event_queue == NULL) { return NULL; } diff --git a/components/grownode/grownode.h b/components/grownode/grownode.h index 04ad08f6..9526776b 100755 --- a/components/grownode/grownode.h +++ b/components/grownode/grownode.h @@ -48,7 +48,7 @@ gn_err_t gn_node_start(gn_node_handle_t node); gn_err_t gn_node_loop(gn_node_handle_t node); -gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, uint64_t millisec); +gn_err_t gn_node_sleep(gn_node_handle_t node, gn_sleep_mode_t sleep_mode, uint64_t delay_msec, uint64_t millisec); size_t gn_node_get_size(gn_node_handle_t config); From 0121929b08c9c2619289e797cae20ee930a60514 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sun, 27 Feb 2022 23:05:15 +0100 Subject: [PATCH 19/31] now reboot, reset and ota messages can be handled offline if retained --- components/grownode/gn_mqtt_protocol.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/components/grownode/gn_mqtt_protocol.c b/components/grownode/gn_mqtt_protocol.c index 229f4234..d788dbab 100755 --- a/components/grownode/gn_mqtt_protocol.c +++ b/components/grownode/gn_mqtt_protocol.c @@ -1234,6 +1234,11 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, == 0) { //ota message + if (event->retain) { + //clear retain message + esp_mqtt_client_publish(config->mqtt_client, _gn_cmd_topic, "", 0, 1, true); + } + esp_event_post_to(config->event_loop, GN_BASE_EVENT, GN_NET_OTA_START, NULL, 0, portMAX_DELAY); @@ -1241,6 +1246,11 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, event->data_len) == 0) { //rst message + if (event->retain) { + //clear retain message + esp_mqtt_client_publish(config->mqtt_client, _gn_cmd_topic, "", 0, 1, true); + } + esp_event_post_to(config->event_loop, GN_BASE_EVENT, GN_NET_RST_START, NULL, 0, portMAX_DELAY); @@ -1248,6 +1258,11 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, event->data_len) == 0) { //rst message + if (event->retain) { + //clear retain message + esp_mqtt_client_publish(config->mqtt_client, _gn_cmd_topic, "", 0, 1, true); + } + esp_event_post_to(config->event_loop, GN_BASE_EVENT, GN_NET_RBT_START, NULL, 0, portMAX_DELAY); From dcb16196e2333d39f1fceae62968e5f2e3f849f5 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sun, 27 Feb 2022 23:07:39 +0100 Subject: [PATCH 20/31] Update mqtt.md --- docs/reference/mqtt.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/mqtt.md b/docs/reference/mqtt.md index c418f70a..2e8133a5 100644 --- a/docs/reference/mqtt.md +++ b/docs/reference/mqtt.md @@ -150,7 +150,7 @@ Parameters involved in the configuration are described in `gn_config_init_param_ ### OTA message -This message informs the node to upload the firmware from the config specified URL. +This message informs the node to upload the firmware from the config specified URL. This message can be sent also with retained flag = true, and will be resetted by the board upon processing. This in order to call the functionality even if waking up from deep sleep | From | To | | ----------- | ----------- | @@ -176,7 +176,7 @@ This gives the confirmation the OTA message has been processed and the OTA is in ### Reboot message -This message tells the node to reboot +This message tells the node to reboot. This message can be sent also with retained flag = true, and will be resetted by the board upon processing. This in order to call the functionality even if waking up from deep sleep | From | To | | ----------- | ----------- | @@ -202,7 +202,7 @@ This gives the confirmation the reboot message has been processed and the board ### Reset message -This message tells the node to reset the NVS. This will bring to the initial configuration, including provisioning. +This message tells the node to reset the NVS. This will bring to the initial configuration, including provisioning. This message can be sent also with retained flag = true, and will be resetted by the board upon processing. This in order to call the functionality even if waking up from deep sleep | From | To | | ----------- | ----------- | From 31f529000b1e8f02bce237a499077bb09e37cd14 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Tue, 8 Mar 2022 07:47:52 +0100 Subject: [PATCH 21/31] oscilloscope board and ina219 leaf added --- .project | 4582 ++++++++++++++++++ Makefile | 2 +- components/grownode/CMakeLists.txt | 4 +- components/grownode/boards/gn_oscilloscope.c | 49 + components/grownode/boards/gn_oscilloscope.h | 30 + components/grownode/grownode.c | 13 +- components/grownode/leaves/gn_leaf_ina219.c | 387 ++ components/grownode/leaves/gn_leaf_ina219.h | 41 + ext/esp-idf-lib | 2 +- 9 files changed, 5101 insertions(+), 9 deletions(-) create mode 100644 components/grownode/boards/gn_oscilloscope.c create mode 100644 components/grownode/boards/gn_oscilloscope.h create mode 100644 components/grownode/leaves/gn_leaf_ina219.c create mode 100644 components/grownode/leaves/gn_leaf_ina219.h diff --git a/.project b/.project index 2b48ebfb..bd19dfcf 100755 --- a/.project +++ b/.project @@ -17,6 +17,4588 @@ org.eclipse.cdt.core.ccnature com.espressif.idf.core.idfNature + + + esp_idf_components/app_trace/app_trace.c + 1 + C:/Development/esp/esp-idf-44/components/app_trace/app_trace.c + + + esp_idf_components/app_trace/app_trace_util.c + 1 + C:/Development/esp/esp-idf-44/components/app_trace/app_trace_util.c + + + esp_idf_components/app_trace/host_file_io.c + 1 + C:/Development/esp/esp-idf-44/components/app_trace/host_file_io.c + + + esp_idf_components/app_update/esp_app_desc.c + 1 + C:/Development/esp/esp-idf-44/components/app_update/esp_app_desc.c + + + esp_idf_components/app_update/esp_ota_ops.c + 1 + C:/Development/esp/esp-idf-44/components/app_update/esp_ota_ops.c + + + esp_idf_components/console/commands.c + 1 + C:/Development/esp/esp-idf-44/components/console/commands.c + + + esp_idf_components/console/esp_console_repl.c + 1 + C:/Development/esp/esp-idf-44/components/console/esp_console_repl.c + + + esp_idf_components/console/split_argv.c + 1 + C:/Development/esp/esp-idf-44/components/console/split_argv.c + + + esp_idf_components/cxx/cxx_exception_stubs.cpp + 1 + C:/Development/esp/esp-idf-44/components/cxx/cxx_exception_stubs.cpp + + + esp_idf_components/cxx/cxx_guards.cpp + 1 + C:/Development/esp/esp-idf-44/components/cxx/cxx_guards.cpp + + + esp_idf_components/driver/adc.c + 1 + C:/Development/esp/esp-idf-44/components/driver/adc.c + + + esp_idf_components/driver/adc_common.c + 1 + C:/Development/esp/esp-idf-44/components/driver/adc_common.c + + + esp_idf_components/driver/adc_deprecated.c + 1 + C:/Development/esp/esp-idf-44/components/driver/adc_deprecated.c + + + esp_idf_components/driver/dac_common.c + 1 + C:/Development/esp/esp-idf-44/components/driver/dac_common.c + + + esp_idf_components/driver/gpio.c + 1 + C:/Development/esp/esp-idf-44/components/driver/gpio.c + + + esp_idf_components/driver/i2c.c + 1 + C:/Development/esp/esp-idf-44/components/driver/i2c.c + + + esp_idf_components/driver/i2s.c + 1 + C:/Development/esp/esp-idf-44/components/driver/i2s.c + + + esp_idf_components/driver/ledc.c + 1 + C:/Development/esp/esp-idf-44/components/driver/ledc.c + + + esp_idf_components/driver/mcpwm.c + 1 + C:/Development/esp/esp-idf-44/components/driver/mcpwm.c + + + esp_idf_components/driver/pcnt.c + 1 + C:/Development/esp/esp-idf-44/components/driver/pcnt.c + + + esp_idf_components/driver/periph_ctrl.c + 1 + C:/Development/esp/esp-idf-44/components/driver/periph_ctrl.c + + + esp_idf_components/driver/rmt.c + 1 + C:/Development/esp/esp-idf-44/components/driver/rmt.c + + + esp_idf_components/driver/rtc_io.c + 1 + C:/Development/esp/esp-idf-44/components/driver/rtc_io.c + + + esp_idf_components/driver/rtc_module.c + 1 + C:/Development/esp/esp-idf-44/components/driver/rtc_module.c + + + esp_idf_components/driver/sdio_slave.c + 1 + C:/Development/esp/esp-idf-44/components/driver/sdio_slave.c + + + esp_idf_components/driver/sdmmc_host.c + 1 + C:/Development/esp/esp-idf-44/components/driver/sdmmc_host.c + + + esp_idf_components/driver/sdmmc_transaction.c + 1 + C:/Development/esp/esp-idf-44/components/driver/sdmmc_transaction.c + + + esp_idf_components/driver/sdspi_crc.c + 1 + C:/Development/esp/esp-idf-44/components/driver/sdspi_crc.c + + + esp_idf_components/driver/sdspi_host.c + 1 + C:/Development/esp/esp-idf-44/components/driver/sdspi_host.c + + + esp_idf_components/driver/sdspi_transaction.c + 1 + C:/Development/esp/esp-idf-44/components/driver/sdspi_transaction.c + + + esp_idf_components/driver/sigmadelta.c + 1 + C:/Development/esp/esp-idf-44/components/driver/sigmadelta.c + + + esp_idf_components/driver/spi_bus_lock.c + 1 + C:/Development/esp/esp-idf-44/components/driver/spi_bus_lock.c + + + esp_idf_components/driver/spi_common.c + 1 + C:/Development/esp/esp-idf-44/components/driver/spi_common.c + + + esp_idf_components/driver/spi_master.c + 1 + C:/Development/esp/esp-idf-44/components/driver/spi_master.c + + + esp_idf_components/driver/spi_slave.c + 1 + C:/Development/esp/esp-idf-44/components/driver/spi_slave.c + + + esp_idf_components/driver/timer.c + 1 + C:/Development/esp/esp-idf-44/components/driver/timer.c + + + esp_idf_components/driver/touch_sensor_common.c + 1 + C:/Development/esp/esp-idf-44/components/driver/touch_sensor_common.c + + + esp_idf_components/driver/twai.c + 1 + C:/Development/esp/esp-idf-44/components/driver/twai.c + + + esp_idf_components/driver/uart.c + 1 + C:/Development/esp/esp-idf-44/components/driver/uart.c + + + esp_idf_components/esp-tls/esp_tls.c + 1 + C:/Development/esp/esp-idf-44/components/esp-tls/esp_tls.c + + + esp_idf_components/esp-tls/esp_tls_error_capture.c + 1 + C:/Development/esp/esp-idf-44/components/esp-tls/esp_tls_error_capture.c + + + esp_idf_components/esp-tls/esp_tls_mbedtls.c + 1 + C:/Development/esp/esp-idf-44/components/esp-tls/esp_tls_mbedtls.c + + + esp_idf_components/esp_adc_cal/esp_adc_cal_esp32.c + 1 + C:/Development/esp/esp-idf-44/components/esp_adc_cal/esp_adc_cal_esp32.c + + + esp_idf_components/esp_event/default_event_loop.c + 1 + C:/Development/esp/esp-idf-44/components/esp_event/default_event_loop.c + + + esp_idf_components/esp_event/esp_event.c + 1 + C:/Development/esp/esp-idf-44/components/esp_event/esp_event.c + + + esp_idf_components/esp_event/esp_event_private.c + 1 + C:/Development/esp/esp-idf-44/components/esp_event/esp_event_private.c + + + esp_idf_components/esp_event/event_loop_legacy.c + 1 + C:/Development/esp/esp-idf-44/components/esp_event/event_loop_legacy.c + + + esp_idf_components/esp_event/event_send.c + 1 + C:/Development/esp/esp-idf-44/components/esp_event/event_send.c + + + esp_idf_components/esp_http_client/esp_http_client.c + 1 + C:/Development/esp/esp-idf-44/components/esp_http_client/esp_http_client.c + + + esp_idf_components/esp_hw_support/clk_ctrl_os.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/clk_ctrl_os.c + + + esp_idf_components/esp_hw_support/compare_set.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/compare_set.c + + + esp_idf_components/esp_hw_support/cpu_util.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/cpu_util.c + + + esp_idf_components/esp_hw_support/esp_async_memcpy.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/esp_async_memcpy.c + + + esp_idf_components/esp_hw_support/esp_clk.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/esp_clk.c + + + esp_idf_components/esp_hw_support/hw_random.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/hw_random.c + + + esp_idf_components/esp_hw_support/intr_alloc.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/intr_alloc.c + + + esp_idf_components/esp_hw_support/mac_addr.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/mac_addr.c + + + esp_idf_components/esp_hw_support/regi2c_ctrl.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/regi2c_ctrl.c + + + esp_idf_components/esp_hw_support/sleep_gpio.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/sleep_gpio.c + + + esp_idf_components/esp_hw_support/sleep_mac_bb.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/sleep_mac_bb.c + + + esp_idf_components/esp_hw_support/sleep_modes.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/sleep_modes.c + + + esp_idf_components/esp_netif/esp_netif_defaults.c + 1 + C:/Development/esp/esp-idf-44/components/esp_netif/esp_netif_defaults.c + + + esp_idf_components/esp_netif/esp_netif_handlers.c + 1 + C:/Development/esp/esp-idf-44/components/esp_netif/esp_netif_handlers.c + + + esp_idf_components/esp_netif/esp_netif_objects.c + 1 + C:/Development/esp/esp-idf-44/components/esp_netif/esp_netif_objects.c + + + esp_idf_components/esp_pm/pm_impl.c + 1 + C:/Development/esp/esp-idf-44/components/esp_pm/pm_impl.c + + + esp_idf_components/esp_pm/pm_locks.c + 1 + C:/Development/esp/esp-idf-44/components/esp_pm/pm_locks.c + + + esp_idf_components/esp_pm/pm_trace.c + 1 + C:/Development/esp/esp-idf-44/components/esp_pm/pm_trace.c + + + esp_idf_components/esp_ringbuf/ringbuf.c + 1 + C:/Development/esp/esp-idf-44/components/esp_ringbuf/ringbuf.c + + + esp_idf_components/esp_serial_slave_link/essl.c + 1 + C:/Development/esp/esp-idf-44/components/esp_serial_slave_link/essl.c + + + esp_idf_components/esp_serial_slave_link/essl_sdio.c + 1 + C:/Development/esp/esp-idf-44/components/esp_serial_slave_link/essl_sdio.c + + + esp_idf_components/esp_serial_slave_link/essl_spi.c + 1 + C:/Development/esp/esp-idf-44/components/esp_serial_slave_link/essl_spi.c + + + esp_idf_components/esp_system/crosscore_int.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/crosscore_int.c + + + esp_idf_components/esp_system/dbg_stubs.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/dbg_stubs.c + + + esp_idf_components/esp_system/esp_err.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/esp_err.c + + + esp_idf_components/esp_system/esp_system.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/esp_system.c + + + esp_idf_components/esp_system/freertos_hooks.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/freertos_hooks.c + + + esp_idf_components/esp_system/int_wdt.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/int_wdt.c + + + esp_idf_components/esp_system/panic.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/panic.c + + + esp_idf_components/esp_system/stack_check.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/stack_check.c + + + esp_idf_components/esp_system/startup.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/startup.c + + + esp_idf_components/esp_system/system_time.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/system_time.c + + + esp_idf_components/esp_system/task_wdt.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/task_wdt.c + + + esp_idf_components/esp_system/ubsan.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/ubsan.c + + + esp_idf_components/esp_system/xt_wdt.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/xt_wdt.c + + + esp_idf_components/esp_websocket_client/esp_websocket_client.c + 1 + C:/Development/esp/esp-idf-44/components/esp_websocket_client/esp_websocket_client.c + + + esp_idf_components/freertos/FreeRTOS-openocd.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/FreeRTOS-openocd.c + + + esp_idf_components/freertos/croutine.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/croutine.c + + + esp_idf_components/freertos/event_groups.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/event_groups.c + + + esp_idf_components/freertos/freertos_v8_compat.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/freertos_v8_compat.c + + + esp_idf_components/freertos/list.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/list.c + + + esp_idf_components/freertos/queue.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/queue.c + + + esp_idf_components/freertos/stream_buffer.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/stream_buffer.c + + + esp_idf_components/freertos/tasks.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/tasks.c + + + esp_idf_components/freertos/timers.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/timers.c + + + esp_idf_components/hal/adc_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/adc_hal.c + + + esp_idf_components/hal/aes_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/aes_hal.c + + + esp_idf_components/hal/cpu_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/cpu_hal.c + + + esp_idf_components/hal/dac_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/dac_hal.c + + + esp_idf_components/hal/emac_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/emac_hal.c + + + esp_idf_components/hal/gpio_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/gpio_hal.c + + + esp_idf_components/hal/i2c_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/i2c_hal.c + + + esp_idf_components/hal/i2c_hal_iram.c + 1 + C:/Development/esp/esp-idf-44/components/hal/i2c_hal_iram.c + + + esp_idf_components/hal/i2s_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/i2s_hal.c + + + esp_idf_components/hal/interrupt_controller_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/interrupt_controller_hal.c + + + esp_idf_components/hal/ledc_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/ledc_hal.c + + + esp_idf_components/hal/ledc_hal_iram.c + 1 + C:/Development/esp/esp-idf-44/components/hal/ledc_hal_iram.c + + + esp_idf_components/hal/mcpwm_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/mcpwm_hal.c + + + esp_idf_components/hal/mpu_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/mpu_hal.c + + + esp_idf_components/hal/pcnt_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/pcnt_hal.c + + + esp_idf_components/hal/rmt_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/rmt_hal.c + + + esp_idf_components/hal/rtc_io_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/rtc_io_hal.c + + + esp_idf_components/hal/sdio_slave_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/sdio_slave_hal.c + + + esp_idf_components/hal/sha_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/sha_hal.c + + + esp_idf_components/hal/sigmadelta_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/sigmadelta_hal.c + + + esp_idf_components/hal/soc_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/soc_hal.c + + + esp_idf_components/hal/spi_flash_encrypt_hal_iram.c + 1 + C:/Development/esp/esp-idf-44/components/hal/spi_flash_encrypt_hal_iram.c + + + esp_idf_components/hal/spi_flash_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/spi_flash_hal.c + + + esp_idf_components/hal/spi_flash_hal_iram.c + 1 + C:/Development/esp/esp-idf-44/components/hal/spi_flash_hal_iram.c + + + esp_idf_components/hal/spi_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/spi_hal.c + + + esp_idf_components/hal/spi_hal_iram.c + 1 + C:/Development/esp/esp-idf-44/components/hal/spi_hal_iram.c + + + esp_idf_components/hal/spi_slave_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/spi_slave_hal.c + + + esp_idf_components/hal/spi_slave_hal_iram.c + 1 + C:/Development/esp/esp-idf-44/components/hal/spi_slave_hal_iram.c + + + esp_idf_components/hal/timer_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/timer_hal.c + + + esp_idf_components/hal/touch_sensor_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/touch_sensor_hal.c + + + esp_idf_components/hal/twai_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/twai_hal.c + + + esp_idf_components/hal/twai_hal_iram.c + 1 + C:/Development/esp/esp-idf-44/components/hal/twai_hal_iram.c + + + esp_idf_components/hal/uart_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/uart_hal.c + + + esp_idf_components/hal/uart_hal_iram.c + 1 + C:/Development/esp/esp-idf-44/components/hal/uart_hal_iram.c + + + esp_idf_components/hal/wdt_hal_iram.c + 1 + C:/Development/esp/esp-idf-44/components/hal/wdt_hal_iram.c + + + esp_idf_components/heap/heap_caps.c + 1 + C:/Development/esp/esp-idf-44/components/heap/heap_caps.c + + + esp_idf_components/heap/heap_caps_init.c + 1 + C:/Development/esp/esp-idf-44/components/heap/heap_caps_init.c + + + esp_idf_components/heap/heap_tlsf.c + 1 + C:/Development/esp/esp-idf-44/components/heap/heap_tlsf.c + + + esp_idf_components/heap/multi_heap.c + 1 + C:/Development/esp/esp-idf-44/components/heap/multi_heap.c + + + esp_idf_components/log/log.c + 1 + C:/Development/esp/esp-idf-44/components/log/log.c + + + esp_idf_components/log/log_buffers.c + 1 + C:/Development/esp/esp-idf-44/components/log/log_buffers.c + + + esp_idf_components/log/log_freertos.c + 1 + C:/Development/esp/esp-idf-44/components/log/log_freertos.c + + + esp_idf_components/mdns/mdns.c + 1 + C:/Development/esp/esp-idf-44/components/mdns/mdns.c + + + esp_idf_components/mdns/mdns_console.c + 1 + C:/Development/esp/esp-idf-44/components/mdns/mdns_console.c + + + esp_idf_components/mdns/mdns_networking_lwip.c + 1 + C:/Development/esp/esp-idf-44/components/mdns/mdns_networking_lwip.c + + + esp_idf_components/newlib/abort.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/abort.c + + + esp_idf_components/newlib/assert.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/assert.c + + + esp_idf_components/newlib/heap.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/heap.c + + + esp_idf_components/newlib/locks.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/locks.c + + + esp_idf_components/newlib/newlib_init.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/newlib_init.c + + + esp_idf_components/newlib/poll.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/poll.c + + + esp_idf_components/newlib/pthread.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/pthread.c + + + esp_idf_components/newlib/random.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/random.c + + + esp_idf_components/newlib/realpath.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/realpath.c + + + esp_idf_components/newlib/reent_init.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/reent_init.c + + + esp_idf_components/newlib/stdatomic.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/stdatomic.c + + + esp_idf_components/newlib/syscalls.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/syscalls.c + + + esp_idf_components/newlib/sysconf.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/sysconf.c + + + esp_idf_components/newlib/termios.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/termios.c + + + esp_idf_components/newlib/time.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/time.c + + + esp_idf_components/perfmon/xtensa_perfmon_access.c + 1 + C:/Development/esp/esp-idf-44/components/perfmon/xtensa_perfmon_access.c + + + esp_idf_components/perfmon/xtensa_perfmon_apis.c + 1 + C:/Development/esp/esp-idf-44/components/perfmon/xtensa_perfmon_apis.c + + + esp_idf_components/perfmon/xtensa_perfmon_masks.c + 1 + C:/Development/esp/esp-idf-44/components/perfmon/xtensa_perfmon_masks.c + + + esp_idf_components/pthread/pthread.c + 1 + C:/Development/esp/esp-idf-44/components/pthread/pthread.c + + + esp_idf_components/pthread/pthread_cond_var.c + 1 + C:/Development/esp/esp-idf-44/components/pthread/pthread_cond_var.c + + + esp_idf_components/pthread/pthread_local_storage.c + 1 + C:/Development/esp/esp-idf-44/components/pthread/pthread_local_storage.c + + + esp_idf_components/pthread/pthread_rwlock.c + 1 + C:/Development/esp/esp-idf-44/components/pthread/pthread_rwlock.c + + + esp_idf_components/sdmmc/sdmmc_cmd.c + 1 + C:/Development/esp/esp-idf-44/components/sdmmc/sdmmc_cmd.c + + + esp_idf_components/sdmmc/sdmmc_common.c + 1 + C:/Development/esp/esp-idf-44/components/sdmmc/sdmmc_common.c + + + esp_idf_components/sdmmc/sdmmc_init.c + 1 + C:/Development/esp/esp-idf-44/components/sdmmc/sdmmc_init.c + + + esp_idf_components/sdmmc/sdmmc_io.c + 1 + C:/Development/esp/esp-idf-44/components/sdmmc/sdmmc_io.c + + + esp_idf_components/sdmmc/sdmmc_mmc.c + 1 + C:/Development/esp/esp-idf-44/components/sdmmc/sdmmc_mmc.c + + + esp_idf_components/sdmmc/sdmmc_sd.c + 1 + C:/Development/esp/esp-idf-44/components/sdmmc/sdmmc_sd.c + + + esp_idf_components/soc/lldesc.c + 1 + C:/Development/esp/esp-idf-44/components/soc/lldesc.c + + + esp_idf_components/soc/soc_include_legacy_warn.c + 1 + C:/Development/esp/esp-idf-44/components/soc/soc_include_legacy_warn.c + + + esp_idf_components/spi_flash/cache_utils.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/cache_utils.c + + + esp_idf_components/spi_flash/esp_flash_api.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/esp_flash_api.c + + + esp_idf_components/spi_flash/esp_flash_spi_init.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/esp_flash_spi_init.c + + + esp_idf_components/spi_flash/flash_mmap.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/flash_mmap.c + + + esp_idf_components/spi_flash/flash_ops.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/flash_ops.c + + + esp_idf_components/spi_flash/memspi_host_driver.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/memspi_host_driver.c + + + esp_idf_components/spi_flash/partition.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/partition.c + + + esp_idf_components/spi_flash/spi_flash_chip_boya.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/spi_flash_chip_boya.c + + + esp_idf_components/spi_flash/spi_flash_chip_drivers.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/spi_flash_chip_drivers.c + + + esp_idf_components/spi_flash/spi_flash_chip_gd.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/spi_flash_chip_gd.c + + + esp_idf_components/spi_flash/spi_flash_chip_generic.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/spi_flash_chip_generic.c + + + esp_idf_components/spi_flash/spi_flash_chip_issi.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/spi_flash_chip_issi.c + + + esp_idf_components/spi_flash/spi_flash_chip_mxic.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/spi_flash_chip_mxic.c + + + esp_idf_components/spi_flash/spi_flash_chip_mxic_opi.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/spi_flash_chip_mxic_opi.c + + + esp_idf_components/spi_flash/spi_flash_chip_winbond.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/spi_flash_chip_winbond.c + + + esp_idf_components/spi_flash/spi_flash_os_func_app.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/spi_flash_os_func_app.c + + + esp_idf_components/spi_flash/spi_flash_os_func_noos.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/spi_flash_os_func_noos.c + + + esp_idf_components/spiffs/esp_spiffs.c + 1 + C:/Development/esp/esp-idf-44/components/spiffs/esp_spiffs.c + + + esp_idf_components/spiffs/spiffs_api.c + 1 + C:/Development/esp/esp-idf-44/components/spiffs/spiffs_api.c + + + esp_idf_components/tcp_transport/transport.c + 1 + C:/Development/esp/esp-idf-44/components/tcp_transport/transport.c + + + esp_idf_components/tcp_transport/transport_ssl.c + 1 + C:/Development/esp/esp-idf-44/components/tcp_transport/transport_ssl.c + + + esp_idf_components/tcp_transport/transport_utils.c + 1 + C:/Development/esp/esp-idf-44/components/tcp_transport/transport_utils.c + + + esp_idf_components/tcp_transport/transport_ws.c + 1 + C:/Development/esp/esp-idf-44/components/tcp_transport/transport_ws.c + + + esp_idf_components/tcpip_adapter/tcpip_adapter_compat.c + 1 + C:/Development/esp/esp-idf-44/components/tcpip_adapter/tcpip_adapter_compat.c + + + esp_idf_components/ulp/ulp.c + 1 + C:/Development/esp/esp-idf-44/components/ulp/ulp.c + + + esp_idf_components/ulp/ulp_macro.c + 1 + C:/Development/esp/esp-idf-44/components/ulp/ulp_macro.c + + + esp_idf_components/unity/unity_port_esp32.c + 1 + C:/Development/esp/esp-idf-44/components/unity/unity_port_esp32.c + + + esp_idf_components/unity/unity_runner.c + 1 + C:/Development/esp/esp-idf-44/components/unity/unity_runner.c + + + esp_idf_components/vfs/vfs.c + 1 + C:/Development/esp/esp-idf-44/components/vfs/vfs.c + + + esp_idf_components/vfs/vfs_console.c + 1 + C:/Development/esp/esp-idf-44/components/vfs/vfs_console.c + + + esp_idf_components/vfs/vfs_eventfd.c + 1 + C:/Development/esp/esp-idf-44/components/vfs/vfs_eventfd.c + + + esp_idf_components/vfs/vfs_semihost.c + 1 + C:/Development/esp/esp-idf-44/components/vfs/vfs_semihost.c + + + esp_idf_components/vfs/vfs_uart.c + 1 + C:/Development/esp/esp-idf-44/components/vfs/vfs_uart.c + + + esp_idf_components/wear_levelling/Partition.cpp + 1 + C:/Development/esp/esp-idf-44/components/wear_levelling/Partition.cpp + + + esp_idf_components/wear_levelling/SPI_Flash.cpp + 1 + C:/Development/esp/esp-idf-44/components/wear_levelling/SPI_Flash.cpp + + + esp_idf_components/wear_levelling/WL_Ext_Perf.cpp + 1 + C:/Development/esp/esp-idf-44/components/wear_levelling/WL_Ext_Perf.cpp + + + esp_idf_components/wear_levelling/WL_Ext_Safe.cpp + 1 + C:/Development/esp/esp-idf-44/components/wear_levelling/WL_Ext_Safe.cpp + + + esp_idf_components/wear_levelling/WL_Flash.cpp + 1 + C:/Development/esp/esp-idf-44/components/wear_levelling/WL_Flash.cpp + + + esp_idf_components/wear_levelling/crc32.cpp + 1 + C:/Development/esp/esp-idf-44/components/wear_levelling/crc32.cpp + + + esp_idf_components/wear_levelling/wear_levelling.cpp + 1 + C:/Development/esp/esp-idf-44/components/wear_levelling/wear_levelling.cpp + + + esp_idf_components/xtensa/eri.c + 1 + C:/Development/esp/esp-idf-44/components/xtensa/eri.c + + + esp_idf_components/xtensa/xt_trax.c + 1 + C:/Development/esp/esp-idf-44/components/xtensa/xt_trax.c + + + esp_idf_components/xtensa/xtensa_intr.c + 1 + C:/Development/esp/esp-idf-44/components/xtensa/xtensa_intr.c + + + esp_idf_components/xtensa/xtensa_intr_asm.S + 1 + C:/Development/esp/esp-idf-44/components/xtensa/xtensa_intr_asm.S + + + esp_idf_components/app_trace/gcov/gcov_rtio.c + 1 + C:/Development/esp/esp-idf-44/components/app_trace/gcov/gcov_rtio.c + + + esp_idf_components/bootloader_support/src/bootloader_clock_init.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/bootloader_clock_init.c + + + esp_idf_components/bootloader_support/src/bootloader_common.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/bootloader_common.c + + + esp_idf_components/bootloader_support/src/bootloader_common_loader.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/bootloader_common_loader.c + + + esp_idf_components/bootloader_support/src/bootloader_efuse_esp32.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/bootloader_efuse_esp32.c + + + esp_idf_components/bootloader_support/src/bootloader_flash.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/bootloader_flash.c + + + esp_idf_components/bootloader_support/src/bootloader_flash_config_esp32.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/bootloader_flash_config_esp32.c + + + esp_idf_components/bootloader_support/src/bootloader_mem.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/bootloader_mem.c + + + esp_idf_components/bootloader_support/src/bootloader_random.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/bootloader_random.c + + + esp_idf_components/bootloader_support/src/bootloader_random_esp32.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/bootloader_random_esp32.c + + + esp_idf_components/bootloader_support/src/bootloader_utility.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/bootloader_utility.c + + + esp_idf_components/bootloader_support/src/esp_image_format.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/esp_image_format.c + + + esp_idf_components/bootloader_support/src/flash_encrypt.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/flash_encrypt.c + + + esp_idf_components/bootloader_support/src/flash_partitions.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/flash_partitions.c + + + esp_idf_components/bootloader_support/src/flash_qio_mode.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/flash_qio_mode.c + + + esp_idf_components/bootloader_support/src/secure_boot.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/secure_boot.c + + + esp_idf_components/console/argtable3/argtable3.c + 1 + C:/Development/esp/esp-idf-44/components/console/argtable3/argtable3.c + + + esp_idf_components/console/linenoise/linenoise.c + 1 + C:/Development/esp/esp-idf-44/components/console/linenoise/linenoise.c + + + esp_idf_components/driver/esp32/adc.c + 1 + C:/Development/esp/esp-idf-44/components/driver/esp32/adc.c + + + esp_idf_components/driver/esp32/dac.c + 1 + C:/Development/esp/esp-idf-44/components/driver/esp32/dac.c + + + esp_idf_components/driver/esp32/touch_sensor.c + 1 + C:/Development/esp/esp-idf-44/components/driver/esp32/touch_sensor.c + + + esp_idf_components/efuse/esp32/esp_efuse_fields.c + 1 + C:/Development/esp/esp-idf-44/components/efuse/esp32/esp_efuse_fields.c + + + esp_idf_components/efuse/esp32/esp_efuse_table.c + 1 + C:/Development/esp/esp-idf-44/components/efuse/esp32/esp_efuse_table.c + + + esp_idf_components/efuse/esp32/esp_efuse_utility.c + 1 + C:/Development/esp/esp-idf-44/components/efuse/esp32/esp_efuse_utility.c + + + esp_idf_components/efuse/src/esp_efuse_api.c + 1 + C:/Development/esp/esp-idf-44/components/efuse/src/esp_efuse_api.c + + + esp_idf_components/efuse/src/esp_efuse_api_key_esp32.c + 1 + C:/Development/esp/esp-idf-44/components/efuse/src/esp_efuse_api_key_esp32.c + + + esp_idf_components/efuse/src/esp_efuse_fields.c + 1 + C:/Development/esp/esp-idf-44/components/efuse/src/esp_efuse_fields.c + + + esp_idf_components/efuse/src/esp_efuse_utility.c + 1 + C:/Development/esp/esp-idf-44/components/efuse/src/esp_efuse_utility.c + + + esp_idf_components/esp-tls/esp-tls-crypto/esp_tls_crypto.c + 1 + C:/Development/esp/esp-idf-44/components/esp-tls/esp-tls-crypto/esp_tls_crypto.c + + + esp_idf_components/esp_common/src/esp_err_to_name.c + 1 + C:/Development/esp/esp-idf-44/components/esp_common/src/esp_err_to_name.c + + + esp_idf_components/esp_eth/src/esp_eth.c + 1 + C:/Development/esp/esp-idf-44/components/esp_eth/src/esp_eth.c + + + esp_idf_components/esp_eth/src/esp_eth_mac_esp.c + 1 + C:/Development/esp/esp-idf-44/components/esp_eth/src/esp_eth_mac_esp.c + + + esp_idf_components/esp_eth/src/esp_eth_netif_glue.c + 1 + C:/Development/esp/esp-idf-44/components/esp_eth/src/esp_eth_netif_glue.c + + + esp_idf_components/esp_eth/src/esp_eth_phy.c + 1 + C:/Development/esp/esp-idf-44/components/esp_eth/src/esp_eth_phy.c + + + esp_idf_components/esp_eth/src/esp_eth_phy_dp83848.c + 1 + C:/Development/esp/esp-idf-44/components/esp_eth/src/esp_eth_phy_dp83848.c + + + esp_idf_components/esp_eth/src/esp_eth_phy_ip101.c + 1 + C:/Development/esp/esp-idf-44/components/esp_eth/src/esp_eth_phy_ip101.c + + + esp_idf_components/esp_eth/src/esp_eth_phy_ksz80xx.c + 1 + C:/Development/esp/esp-idf-44/components/esp_eth/src/esp_eth_phy_ksz80xx.c + + + esp_idf_components/esp_eth/src/esp_eth_phy_lan87xx.c + 1 + C:/Development/esp/esp-idf-44/components/esp_eth/src/esp_eth_phy_lan87xx.c + + + esp_idf_components/esp_eth/src/esp_eth_phy_rtl8201.c + 1 + C:/Development/esp/esp-idf-44/components/esp_eth/src/esp_eth_phy_rtl8201.c + + + esp_idf_components/esp_gdbstub/esp_common/gdbstub_common.c + 1 + C:/Development/esp/esp-idf-44/components/esp_gdbstub/esp_common/gdbstub_common.c + + + esp_idf_components/esp_gdbstub/src/gdbstub.c + 1 + C:/Development/esp/esp-idf-44/components/esp_gdbstub/src/gdbstub.c + + + esp_idf_components/esp_gdbstub/src/packet.c + 1 + C:/Development/esp/esp-idf-44/components/esp_gdbstub/src/packet.c + + + esp_idf_components/esp_gdbstub/xtensa/gdbstub-entry.S + 1 + C:/Development/esp/esp-idf-44/components/esp_gdbstub/xtensa/gdbstub-entry.S + + + esp_idf_components/esp_gdbstub/xtensa/gdbstub_xtensa.c + 1 + C:/Development/esp/esp-idf-44/components/esp_gdbstub/xtensa/gdbstub_xtensa.c + + + esp_idf_components/esp_hid/src/esp_hid_common.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hid/src/esp_hid_common.c + + + esp_idf_components/esp_hid/src/esp_hidd.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hid/src/esp_hidd.c + + + esp_idf_components/esp_hid/src/esp_hidh.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hid/src/esp_hidh.c + + + esp_idf_components/esp_http_client/lib/http_auth.c + 1 + C:/Development/esp/esp-idf-44/components/esp_http_client/lib/http_auth.c + + + esp_idf_components/esp_http_client/lib/http_header.c + 1 + C:/Development/esp/esp-idf-44/components/esp_http_client/lib/http_header.c + + + esp_idf_components/esp_http_client/lib/http_utils.c + 1 + C:/Development/esp/esp-idf-44/components/esp_http_client/lib/http_utils.c + + + esp_idf_components/esp_http_server/src/httpd_main.c + 1 + C:/Development/esp/esp-idf-44/components/esp_http_server/src/httpd_main.c + + + esp_idf_components/esp_http_server/src/httpd_parse.c + 1 + C:/Development/esp/esp-idf-44/components/esp_http_server/src/httpd_parse.c + + + esp_idf_components/esp_http_server/src/httpd_sess.c + 1 + C:/Development/esp/esp-idf-44/components/esp_http_server/src/httpd_sess.c + + + esp_idf_components/esp_http_server/src/httpd_txrx.c + 1 + C:/Development/esp/esp-idf-44/components/esp_http_server/src/httpd_txrx.c + + + esp_idf_components/esp_http_server/src/httpd_uri.c + 1 + C:/Development/esp/esp-idf-44/components/esp_http_server/src/httpd_uri.c + + + esp_idf_components/esp_http_server/src/httpd_ws.c + 1 + C:/Development/esp/esp-idf-44/components/esp_http_server/src/httpd_ws.c + + + esp_idf_components/esp_https_ota/src/esp_https_ota.c + 1 + C:/Development/esp/esp-idf-44/components/esp_https_ota/src/esp_https_ota.c + + + esp_idf_components/esp_ipc/src/esp_ipc.c + 1 + C:/Development/esp/esp-idf-44/components/esp_ipc/src/esp_ipc.c + + + esp_idf_components/esp_lcd/src/esp_lcd_common.c + 1 + C:/Development/esp/esp-idf-44/components/esp_lcd/src/esp_lcd_common.c + + + esp_idf_components/esp_lcd/src/esp_lcd_panel_io.c + 1 + C:/Development/esp/esp-idf-44/components/esp_lcd/src/esp_lcd_panel_io.c + + + esp_idf_components/esp_lcd/src/esp_lcd_panel_io_i2c.c + 1 + C:/Development/esp/esp-idf-44/components/esp_lcd/src/esp_lcd_panel_io_i2c.c + + + esp_idf_components/esp_lcd/src/esp_lcd_panel_io_i2s.c + 1 + C:/Development/esp/esp-idf-44/components/esp_lcd/src/esp_lcd_panel_io_i2s.c + + + esp_idf_components/esp_lcd/src/esp_lcd_panel_io_i80.c + 1 + C:/Development/esp/esp-idf-44/components/esp_lcd/src/esp_lcd_panel_io_i80.c + + + esp_idf_components/esp_lcd/src/esp_lcd_panel_io_spi.c + 1 + C:/Development/esp/esp-idf-44/components/esp_lcd/src/esp_lcd_panel_io_spi.c + + + esp_idf_components/esp_lcd/src/esp_lcd_panel_nt35510.c + 1 + C:/Development/esp/esp-idf-44/components/esp_lcd/src/esp_lcd_panel_nt35510.c + + + esp_idf_components/esp_lcd/src/esp_lcd_panel_ops.c + 1 + C:/Development/esp/esp-idf-44/components/esp_lcd/src/esp_lcd_panel_ops.c + + + esp_idf_components/esp_lcd/src/esp_lcd_panel_ssd1306.c + 1 + C:/Development/esp/esp-idf-44/components/esp_lcd/src/esp_lcd_panel_ssd1306.c + + + esp_idf_components/esp_lcd/src/esp_lcd_panel_st7789.c + 1 + C:/Development/esp/esp-idf-44/components/esp_lcd/src/esp_lcd_panel_st7789.c + + + esp_idf_components/esp_lcd/src/esp_lcd_rgb_panel.c + 1 + C:/Development/esp/esp-idf-44/components/esp_lcd/src/esp_lcd_rgb_panel.c + + + esp_idf_components/esp_local_ctrl/proto-c/esp_local_ctrl.pb-c.c + 1 + C:/Development/esp/esp-idf-44/components/esp_local_ctrl/proto-c/esp_local_ctrl.pb-c.c + + + esp_idf_components/esp_local_ctrl/src/esp_local_ctrl.c + 1 + C:/Development/esp/esp-idf-44/components/esp_local_ctrl/src/esp_local_ctrl.c + + + esp_idf_components/esp_local_ctrl/src/esp_local_ctrl_handler.c + 1 + C:/Development/esp/esp-idf-44/components/esp_local_ctrl/src/esp_local_ctrl_handler.c + + + esp_idf_components/esp_netif/loopback/esp_netif_loopback.c + 1 + C:/Development/esp/esp-idf-44/components/esp_netif/loopback/esp_netif_loopback.c + + + esp_idf_components/esp_netif/lwip/esp_netif_lwip.c + 1 + C:/Development/esp/esp-idf-44/components/esp_netif/lwip/esp_netif_lwip.c + + + esp_idf_components/esp_netif/lwip/esp_netif_lwip_defaults.c + 1 + C:/Development/esp/esp-idf-44/components/esp_netif/lwip/esp_netif_lwip_defaults.c + + + esp_idf_components/esp_netif/lwip/esp_netif_sta_list.c + 1 + C:/Development/esp/esp-idf-44/components/esp_netif/lwip/esp_netif_sta_list.c + + + esp_idf_components/esp_phy/src/phy_init.c + 1 + C:/Development/esp/esp-idf-44/components/esp_phy/src/phy_init.c + + + esp_idf_components/esp_rom/patches/esp_rom_crc.c + 1 + C:/Development/esp/esp-idf-44/components/esp_rom/patches/esp_rom_crc.c + + + esp_idf_components/esp_rom/patches/esp_rom_longjmp.S + 1 + C:/Development/esp/esp-idf-44/components/esp_rom/patches/esp_rom_longjmp.S + + + esp_idf_components/esp_rom/patches/esp_rom_sys.c + 1 + C:/Development/esp/esp-idf-44/components/esp_rom/patches/esp_rom_sys.c + + + esp_idf_components/esp_rom/patches/esp_rom_tjpgd.c + 1 + C:/Development/esp/esp-idf-44/components/esp_rom/patches/esp_rom_tjpgd.c + + + esp_idf_components/esp_rom/patches/esp_rom_uart.c + 1 + C:/Development/esp/esp-idf-44/components/esp_rom/patches/esp_rom_uart.c + + + esp_idf_components/esp_system/port/brownout.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/brownout.c + + + esp_idf_components/esp_system/port/cpu_start.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/cpu_start.c + + + esp_idf_components/esp_system/port/panic_handler.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/panic_handler.c + + + esp_idf_components/esp_timer/src/esp_timer.c + 1 + C:/Development/esp/esp-idf-44/components/esp_timer/src/esp_timer.c + + + esp_idf_components/esp_timer/src/esp_timer_impl_lac.c + 1 + C:/Development/esp/esp-idf-44/components/esp_timer/src/esp_timer_impl_lac.c + + + esp_idf_components/esp_timer/src/ets_timer_legacy.c + 1 + C:/Development/esp/esp-idf-44/components/esp_timer/src/ets_timer_legacy.c + + + esp_idf_components/esp_timer/src/system_time.c + 1 + C:/Development/esp/esp-idf-44/components/esp_timer/src/system_time.c + + + esp_idf_components/esp_wifi/esp32/esp_adapter.c + 1 + C:/Development/esp/esp-idf-44/components/esp_wifi/esp32/esp_adapter.c + + + esp_idf_components/esp_wifi/src/coexist.c + 1 + C:/Development/esp/esp-idf-44/components/esp_wifi/src/coexist.c + + + esp_idf_components/esp_wifi/src/lib_printf.c + 1 + C:/Development/esp/esp-idf-44/components/esp_wifi/src/lib_printf.c + + + esp_idf_components/esp_wifi/src/mesh_event.c + 1 + C:/Development/esp/esp-idf-44/components/esp_wifi/src/mesh_event.c + + + esp_idf_components/esp_wifi/src/smartconfig.c + 1 + C:/Development/esp/esp-idf-44/components/esp_wifi/src/smartconfig.c + + + esp_idf_components/esp_wifi/src/smartconfig_ack.c + 1 + C:/Development/esp/esp-idf-44/components/esp_wifi/src/smartconfig_ack.c + + + esp_idf_components/esp_wifi/src/wifi_default.c + 1 + C:/Development/esp/esp-idf-44/components/esp_wifi/src/wifi_default.c + + + esp_idf_components/esp_wifi/src/wifi_init.c + 1 + C:/Development/esp/esp-idf-44/components/esp_wifi/src/wifi_init.c + + + esp_idf_components/esp_wifi/src/wifi_netif.c + 1 + C:/Development/esp/esp-idf-44/components/esp_wifi/src/wifi_netif.c + + + esp_idf_components/espcoredump/src/core_dump_binary.c + 1 + C:/Development/esp/esp-idf-44/components/espcoredump/src/core_dump_binary.c + + + esp_idf_components/espcoredump/src/core_dump_checksum.c + 1 + C:/Development/esp/esp-idf-44/components/espcoredump/src/core_dump_checksum.c + + + esp_idf_components/espcoredump/src/core_dump_common.c + 1 + C:/Development/esp/esp-idf-44/components/espcoredump/src/core_dump_common.c + + + esp_idf_components/espcoredump/src/core_dump_elf.c + 1 + C:/Development/esp/esp-idf-44/components/espcoredump/src/core_dump_elf.c + + + esp_idf_components/espcoredump/src/core_dump_flash.c + 1 + C:/Development/esp/esp-idf-44/components/espcoredump/src/core_dump_flash.c + + + esp_idf_components/espcoredump/src/core_dump_uart.c + 1 + C:/Development/esp/esp-idf-44/components/espcoredump/src/core_dump_uart.c + + + esp_idf_components/fatfs/diskio/diskio.c + 1 + C:/Development/esp/esp-idf-44/components/fatfs/diskio/diskio.c + + + esp_idf_components/fatfs/diskio/diskio_rawflash.c + 1 + C:/Development/esp/esp-idf-44/components/fatfs/diskio/diskio_rawflash.c + + + esp_idf_components/fatfs/diskio/diskio_sdmmc.c + 1 + C:/Development/esp/esp-idf-44/components/fatfs/diskio/diskio_sdmmc.c + + + esp_idf_components/fatfs/diskio/diskio_wl.c + 1 + C:/Development/esp/esp-idf-44/components/fatfs/diskio/diskio_wl.c + + + esp_idf_components/fatfs/src/ff.c + 1 + C:/Development/esp/esp-idf-44/components/fatfs/src/ff.c + + + esp_idf_components/fatfs/src/ffunicode.c + 1 + C:/Development/esp/esp-idf-44/components/fatfs/src/ffunicode.c + + + esp_idf_components/fatfs/vfs/vfs_fat.c + 1 + C:/Development/esp/esp-idf-44/components/fatfs/vfs/vfs_fat.c + + + esp_idf_components/fatfs/vfs/vfs_fat_sdmmc.c + 1 + C:/Development/esp/esp-idf-44/components/fatfs/vfs/vfs_fat_sdmmc.c + + + esp_idf_components/fatfs/vfs/vfs_fat_spiflash.c + 1 + C:/Development/esp/esp-idf-44/components/fatfs/vfs/vfs_fat_spiflash.c + + + esp_idf_components/freemodbus/common/esp_modbus_master.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/common/esp_modbus_master.c + + + esp_idf_components/freemodbus/common/esp_modbus_master_serial.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/common/esp_modbus_master_serial.c + + + esp_idf_components/freemodbus/common/esp_modbus_master_tcp.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/common/esp_modbus_master_tcp.c + + + esp_idf_components/freemodbus/common/esp_modbus_slave.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/common/esp_modbus_slave.c + + + esp_idf_components/freemodbus/common/esp_modbus_slave_serial.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/common/esp_modbus_slave_serial.c + + + esp_idf_components/freemodbus/common/esp_modbus_slave_tcp.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/common/esp_modbus_slave_tcp.c + + + esp_idf_components/freemodbus/modbus/mb.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/mb.c + + + esp_idf_components/freemodbus/modbus/mb_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/mb_m.c + + + esp_idf_components/freemodbus/port/port.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/port/port.c + + + esp_idf_components/freemodbus/port/portevent.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/port/portevent.c + + + esp_idf_components/freemodbus/port/portevent_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/port/portevent_m.c + + + esp_idf_components/freemodbus/port/portother.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/port/portother.c + + + esp_idf_components/freemodbus/port/portother_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/port/portother_m.c + + + esp_idf_components/freemodbus/port/portserial.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/port/portserial.c + + + esp_idf_components/freemodbus/port/portserial_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/port/portserial_m.c + + + esp_idf_components/freemodbus/port/porttimer.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/port/porttimer.c + + + esp_idf_components/freemodbus/port/porttimer_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/port/porttimer_m.c + + + esp_idf_components/freertos/esp_additions/task_snapshot.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/esp_additions/task_snapshot.c + + + esp_idf_components/freertos/port/port_common.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/port/port_common.c + + + esp_idf_components/freertos/port/port_systick.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/port/port_systick.c + + + esp_idf_components/hal/esp32/adc_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/esp32/adc_hal.c + + + esp_idf_components/hal/esp32/brownout_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/esp32/brownout_hal.c + + + esp_idf_components/hal/esp32/gpio_hal_workaround.c + 1 + C:/Development/esp/esp-idf-44/components/hal/esp32/gpio_hal_workaround.c + + + esp_idf_components/hal/esp32/interrupt_descriptor_table.c + 1 + C:/Development/esp/esp-idf-44/components/hal/esp32/interrupt_descriptor_table.c + + + esp_idf_components/hal/esp32/touch_sensor_hal.c + 1 + C:/Development/esp/esp-idf-44/components/hal/esp32/touch_sensor_hal.c + + + esp_idf_components/heap/port/memory_layout_utils.c + 1 + C:/Development/esp/esp-idf-44/components/heap/port/memory_layout_utils.c + + + esp_idf_components/jsmn/src/jsmn.c + 1 + C:/Development/esp/esp-idf-44/components/jsmn/src/jsmn.c + + + esp_idf_components/json/cJSON/cJSON.c + 1 + C:/Development/esp/esp-idf-44/components/json/cJSON/cJSON.c + + + esp_idf_components/json/cJSON/cJSON_Utils.c + 1 + C:/Development/esp/esp-idf-44/components/json/cJSON/cJSON_Utils.c + + + esp_idf_components/libsodium/port/randombytes_esp32.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/port/randombytes_esp32.c + + + esp_idf_components/mbedtls/esp_crt_bundle/esp_crt_bundle.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c + + + esp_idf_components/mbedtls/port/esp_bignum.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/esp_bignum.c + + + esp_idf_components/mbedtls/port/esp_hardware.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/esp_hardware.c + + + esp_idf_components/mbedtls/port/esp_mem.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/esp_mem.c + + + esp_idf_components/mbedtls/port/esp_timing.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/esp_timing.c + + + esp_idf_components/mbedtls/port/mbedtls_debug.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/mbedtls_debug.c + + + esp_idf_components/mbedtls/port/net_sockets.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/net_sockets.c + + + esp_idf_components/mqtt/esp-mqtt/mqtt_client.c + 1 + C:/Development/esp/esp-idf-44/components/mqtt/esp-mqtt/mqtt_client.c + + + esp_idf_components/newlib/port/esp_time_impl.c + 1 + C:/Development/esp/esp-idf-44/components/newlib/port/esp_time_impl.c + + + esp_idf_components/nghttp/port/http_parser.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/port/http_parser.c + + + esp_idf_components/nvs_flash/src/nvs_api.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_api.cpp + + + esp_idf_components/nvs_flash/src/nvs_cxx_api.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_cxx_api.cpp + + + esp_idf_components/nvs_flash/src/nvs_handle_locked.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_handle_locked.cpp + + + esp_idf_components/nvs_flash/src/nvs_handle_simple.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_handle_simple.cpp + + + esp_idf_components/nvs_flash/src/nvs_item_hash_list.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_item_hash_list.cpp + + + esp_idf_components/nvs_flash/src/nvs_page.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_page.cpp + + + esp_idf_components/nvs_flash/src/nvs_pagemanager.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_pagemanager.cpp + + + esp_idf_components/nvs_flash/src/nvs_partition.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_partition.cpp + + + esp_idf_components/nvs_flash/src/nvs_partition_lookup.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_partition_lookup.cpp + + + esp_idf_components/nvs_flash/src/nvs_partition_manager.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_partition_manager.cpp + + + esp_idf_components/nvs_flash/src/nvs_storage.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_storage.cpp + + + esp_idf_components/nvs_flash/src/nvs_types.cpp + 1 + C:/Development/esp/esp-idf-44/components/nvs_flash/src/nvs_types.cpp + + + esp_idf_components/openssl/library/ssl_bio.c + 1 + C:/Development/esp/esp-idf-44/components/openssl/library/ssl_bio.c + + + esp_idf_components/openssl/library/ssl_cert.c + 1 + C:/Development/esp/esp-idf-44/components/openssl/library/ssl_cert.c + + + esp_idf_components/openssl/library/ssl_err.c + 1 + C:/Development/esp/esp-idf-44/components/openssl/library/ssl_err.c + + + esp_idf_components/openssl/library/ssl_lib.c + 1 + C:/Development/esp/esp-idf-44/components/openssl/library/ssl_lib.c + + + esp_idf_components/openssl/library/ssl_methods.c + 1 + C:/Development/esp/esp-idf-44/components/openssl/library/ssl_methods.c + + + esp_idf_components/openssl/library/ssl_pkey.c + 1 + C:/Development/esp/esp-idf-44/components/openssl/library/ssl_pkey.c + + + esp_idf_components/openssl/library/ssl_stack.c + 1 + C:/Development/esp/esp-idf-44/components/openssl/library/ssl_stack.c + + + esp_idf_components/openssl/library/ssl_x509.c + 1 + C:/Development/esp/esp-idf-44/components/openssl/library/ssl_x509.c + + + esp_idf_components/openssl/platform/ssl_pm.c + 1 + C:/Development/esp/esp-idf-44/components/openssl/platform/ssl_pm.c + + + esp_idf_components/openssl/platform/ssl_port.c + 1 + C:/Development/esp/esp-idf-44/components/openssl/platform/ssl_port.c + + + esp_idf_components/protocomm/proto-c/constants.pb-c.c + 1 + C:/Development/esp/esp-idf-44/components/protocomm/proto-c/constants.pb-c.c + + + esp_idf_components/protocomm/proto-c/sec0.pb-c.c + 1 + C:/Development/esp/esp-idf-44/components/protocomm/proto-c/sec0.pb-c.c + + + esp_idf_components/protocomm/proto-c/sec1.pb-c.c + 1 + C:/Development/esp/esp-idf-44/components/protocomm/proto-c/sec1.pb-c.c + + + esp_idf_components/protocomm/proto-c/session.pb-c.c + 1 + C:/Development/esp/esp-idf-44/components/protocomm/proto-c/session.pb-c.c + + + esp_idf_components/soc/esp32/adc_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/adc_periph.c + + + esp_idf_components/soc/esp32/dac_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/dac_periph.c + + + esp_idf_components/soc/esp32/gpio_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/gpio_periph.c + + + esp_idf_components/soc/esp32/i2c_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/i2c_periph.c + + + esp_idf_components/soc/esp32/i2s_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/i2s_periph.c + + + esp_idf_components/soc/esp32/interrupts.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/interrupts.c + + + esp_idf_components/soc/esp32/lcd_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/lcd_periph.c + + + esp_idf_components/soc/esp32/ledc_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/ledc_periph.c + + + esp_idf_components/soc/esp32/mcpwm_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/mcpwm_periph.c + + + esp_idf_components/soc/esp32/pcnt_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/pcnt_periph.c + + + esp_idf_components/soc/esp32/rmt_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/rmt_periph.c + + + esp_idf_components/soc/esp32/rtc_io_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/rtc_io_periph.c + + + esp_idf_components/soc/esp32/sdio_slave_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/sdio_slave_periph.c + + + esp_idf_components/soc/esp32/sdmmc_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/sdmmc_periph.c + + + esp_idf_components/soc/esp32/sigmadelta_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/sigmadelta_periph.c + + + esp_idf_components/soc/esp32/spi_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/spi_periph.c + + + esp_idf_components/soc/esp32/timer_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/timer_periph.c + + + esp_idf_components/soc/esp32/touch_sensor_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/touch_sensor_periph.c + + + esp_idf_components/soc/esp32/uart_periph.c + 1 + C:/Development/esp/esp-idf-44/components/soc/esp32/uart_periph.c + + + esp_idf_components/spi_flash/esp32/flash_ops_esp32.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/esp32/flash_ops_esp32.c + + + esp_idf_components/spi_flash/esp32/spi_flash_rom_patch.c + 1 + C:/Development/esp/esp-idf-44/components/spi_flash/esp32/spi_flash_rom_patch.c + + + esp_idf_components/wifi_provisioning/proto-c/wifi_config.pb-c.c + 1 + C:/Development/esp/esp-idf-44/components/wifi_provisioning/proto-c/wifi_config.pb-c.c + + + esp_idf_components/wifi_provisioning/proto-c/wifi_constants.pb-c.c + 1 + C:/Development/esp/esp-idf-44/components/wifi_provisioning/proto-c/wifi_constants.pb-c.c + + + esp_idf_components/wifi_provisioning/proto-c/wifi_scan.pb-c.c + 1 + C:/Development/esp/esp-idf-44/components/wifi_provisioning/proto-c/wifi_scan.pb-c.c + + + esp_idf_components/wifi_provisioning/src/handlers.c + 1 + C:/Development/esp/esp-idf-44/components/wifi_provisioning/src/handlers.c + + + esp_idf_components/wifi_provisioning/src/manager.c + 1 + C:/Development/esp/esp-idf-44/components/wifi_provisioning/src/manager.c + + + esp_idf_components/wifi_provisioning/src/scheme_console.c + 1 + C:/Development/esp/esp-idf-44/components/wifi_provisioning/src/scheme_console.c + + + esp_idf_components/wifi_provisioning/src/scheme_softap.c + 1 + C:/Development/esp/esp-idf-44/components/wifi_provisioning/src/scheme_softap.c + + + esp_idf_components/wifi_provisioning/src/wifi_config.c + 1 + C:/Development/esp/esp-idf-44/components/wifi_provisioning/src/wifi_config.c + + + esp_idf_components/wifi_provisioning/src/wifi_scan.c + 1 + C:/Development/esp/esp-idf-44/components/wifi_provisioning/src/wifi_scan.c + + + esp_idf_components/wpa_supplicant/port/os_xtensa.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/port/os_xtensa.c + + + esp_idf_components/bootloader_support/src/idf/bootloader_sha.c + 1 + C:/Development/esp/esp-idf-44/components/bootloader_support/src/idf/bootloader_sha.c + + + esp_idf_components/cbor/tinycbor/src/cborencoder.c + 1 + C:/Development/esp/esp-idf-44/components/cbor/tinycbor/src/cborencoder.c + + + esp_idf_components/cbor/tinycbor/src/cborencoder_close_container_checked.c + 1 + C:/Development/esp/esp-idf-44/components/cbor/tinycbor/src/cborencoder_close_container_checked.c + + + esp_idf_components/cbor/tinycbor/src/cborerrorstrings.c + 1 + C:/Development/esp/esp-idf-44/components/cbor/tinycbor/src/cborerrorstrings.c + + + esp_idf_components/cbor/tinycbor/src/cborparser.c + 1 + C:/Development/esp/esp-idf-44/components/cbor/tinycbor/src/cborparser.c + + + esp_idf_components/cbor/tinycbor/src/cborparser_dup_string.c + 1 + C:/Development/esp/esp-idf-44/components/cbor/tinycbor/src/cborparser_dup_string.c + + + esp_idf_components/cbor/tinycbor/src/cborpretty.c + 1 + C:/Development/esp/esp-idf-44/components/cbor/tinycbor/src/cborpretty.c + + + esp_idf_components/cbor/tinycbor/src/cborpretty_stdio.c + 1 + C:/Development/esp/esp-idf-44/components/cbor/tinycbor/src/cborpretty_stdio.c + + + esp_idf_components/cbor/tinycbor/src/cbortojson.c + 1 + C:/Development/esp/esp-idf-44/components/cbor/tinycbor/src/cbortojson.c + + + esp_idf_components/cbor/tinycbor/src/cborvalidation.c + 1 + C:/Development/esp/esp-idf-44/components/cbor/tinycbor/src/cborvalidation.c + + + esp_idf_components/cbor/tinycbor/src/open_memstream.c + 1 + C:/Development/esp/esp-idf-44/components/cbor/tinycbor/src/open_memstream.c + + + esp_idf_components/cmock/CMock/src/cmock.c + 1 + C:/Development/esp/esp-idf-44/components/cmock/CMock/src/cmock.c + + + esp_idf_components/coap/libcoap/src/address.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/address.c + + + esp_idf_components/coap/libcoap/src/async.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/async.c + + + esp_idf_components/coap/libcoap/src/block.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/block.c + + + esp_idf_components/coap/libcoap/src/coap_asn1.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_asn1.c + + + esp_idf_components/coap/libcoap/src/coap_cache.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_cache.c + + + esp_idf_components/coap/libcoap/src/coap_debug.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_debug.c + + + esp_idf_components/coap/libcoap/src/coap_event.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_event.c + + + esp_idf_components/coap/libcoap/src/coap_hashkey.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_hashkey.c + + + esp_idf_components/coap/libcoap/src/coap_io.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_io.c + + + esp_idf_components/coap/libcoap/src/coap_mbedtls.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_mbedtls.c + + + esp_idf_components/coap/libcoap/src/coap_notls.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_notls.c + + + esp_idf_components/coap/libcoap/src/coap_prng.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_prng.c + + + esp_idf_components/coap/libcoap/src/coap_session.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_session.c + + + esp_idf_components/coap/libcoap/src/coap_tcp.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_tcp.c + + + esp_idf_components/coap/libcoap/src/coap_time.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/coap_time.c + + + esp_idf_components/coap/libcoap/src/encode.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/encode.c + + + esp_idf_components/coap/libcoap/src/mem.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/mem.c + + + esp_idf_components/coap/libcoap/src/net.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/net.c + + + esp_idf_components/coap/libcoap/src/option.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/option.c + + + esp_idf_components/coap/libcoap/src/pdu.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/pdu.c + + + esp_idf_components/coap/libcoap/src/resource.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/resource.c + + + esp_idf_components/coap/libcoap/src/str.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/str.c + + + esp_idf_components/coap/libcoap/src/subscribe.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/subscribe.c + + + esp_idf_components/coap/libcoap/src/uri.c + 1 + C:/Development/esp/esp-idf-44/components/coap/libcoap/src/uri.c + + + esp_idf_components/esp_http_server/src/util/ctrl_sock.c + 1 + C:/Development/esp/esp-idf-44/components/esp_http_server/src/util/ctrl_sock.c + + + esp_idf_components/esp_hw_support/port/esp32/cache_sram_mmu.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/cache_sram_mmu.c + + + esp_idf_components/esp_hw_support/port/esp32/chip_info.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/chip_info.c + + + esp_idf_components/esp_hw_support/port/esp32/dport_access.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/dport_access.c + + + esp_idf_components/esp_hw_support/port/esp32/esp_himem.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/esp_himem.c + + + esp_idf_components/esp_hw_support/port/esp32/rtc_clk.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/rtc_clk.c + + + esp_idf_components/esp_hw_support/port/esp32/rtc_clk_init.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/rtc_clk_init.c + + + esp_idf_components/esp_hw_support/port/esp32/rtc_init.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/rtc_init.c + + + esp_idf_components/esp_hw_support/port/esp32/rtc_pm.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/rtc_pm.c + + + esp_idf_components/esp_hw_support/port/esp32/rtc_sleep.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/rtc_sleep.c + + + esp_idf_components/esp_hw_support/port/esp32/rtc_time.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/rtc_time.c + + + esp_idf_components/esp_hw_support/port/esp32/rtc_wdt.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/rtc_wdt.c + + + esp_idf_components/esp_hw_support/port/esp32/spiram.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/spiram.c + + + esp_idf_components/esp_hw_support/port/esp32/spiram_psram.c + 1 + C:/Development/esp/esp-idf-44/components/esp_hw_support/port/esp32/spiram_psram.c + + + esp_idf_components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr.c + 1 + C:/Development/esp/esp-idf-44/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr.c + + + esp_idf_components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_handler.S + 1 + C:/Development/esp/esp-idf-44/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_handler.S + + + esp_idf_components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_routines.S + 1 + C:/Development/esp/esp-idf-44/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_routines.S + + + esp_idf_components/fatfs/port/freertos/ffsystem.c + 1 + C:/Development/esp/esp-idf-44/components/fatfs/port/freertos/ffsystem.c + + + esp_idf_components/freemodbus/modbus/ascii/mbascii.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/ascii/mbascii.c + + + esp_idf_components/freemodbus/modbus/ascii/mbascii_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/ascii/mbascii_m.c + + + esp_idf_components/freemodbus/modbus/functions/mbfunccoils.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/functions/mbfunccoils.c + + + esp_idf_components/freemodbus/modbus/functions/mbfunccoils_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/functions/mbfunccoils_m.c + + + esp_idf_components/freemodbus/modbus/functions/mbfuncdiag.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/functions/mbfuncdiag.c + + + esp_idf_components/freemodbus/modbus/functions/mbfuncdisc.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/functions/mbfuncdisc.c + + + esp_idf_components/freemodbus/modbus/functions/mbfuncdisc_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/functions/mbfuncdisc_m.c + + + esp_idf_components/freemodbus/modbus/functions/mbfuncholding.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/functions/mbfuncholding.c + + + esp_idf_components/freemodbus/modbus/functions/mbfuncholding_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/functions/mbfuncholding_m.c + + + esp_idf_components/freemodbus/modbus/functions/mbfuncinput.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/functions/mbfuncinput.c + + + esp_idf_components/freemodbus/modbus/functions/mbfuncinput_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/functions/mbfuncinput_m.c + + + esp_idf_components/freemodbus/modbus/functions/mbfuncother.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/functions/mbfuncother.c + + + esp_idf_components/freemodbus/modbus/functions/mbutils.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/functions/mbutils.c + + + esp_idf_components/freemodbus/modbus/rtu/mbcrc.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/rtu/mbcrc.c + + + esp_idf_components/freemodbus/modbus/rtu/mbrtu.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/rtu/mbrtu.c + + + esp_idf_components/freemodbus/modbus/rtu/mbrtu_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/rtu/mbrtu_m.c + + + esp_idf_components/freemodbus/modbus/tcp/mbtcp.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/tcp/mbtcp.c + + + esp_idf_components/freemodbus/modbus/tcp/mbtcp_m.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/modbus/tcp/mbtcp_m.c + + + esp_idf_components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c + + + esp_idf_components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c + + + esp_idf_components/freemodbus/tcp_master/modbus_controller/mbc_tcp_master.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/tcp_master/modbus_controller/mbc_tcp_master.c + + + esp_idf_components/freemodbus/tcp_master/port/port_tcp_master.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/tcp_master/port/port_tcp_master.c + + + esp_idf_components/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.c + + + esp_idf_components/freemodbus/tcp_slave/port/port_tcp_slave.c + 1 + C:/Development/esp/esp-idf-44/components/freemodbus/tcp_slave/port/port_tcp_slave.c + + + esp_idf_components/freertos/port/xtensa/port.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/port/xtensa/port.c + + + esp_idf_components/freertos/port/xtensa/portasm.S + 1 + C:/Development/esp/esp-idf-44/components/freertos/port/xtensa/portasm.S + + + esp_idf_components/freertos/port/xtensa/xtensa_context.S + 1 + C:/Development/esp/esp-idf-44/components/freertos/port/xtensa/xtensa_context.S + + + esp_idf_components/freertos/port/xtensa/xtensa_init.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/port/xtensa/xtensa_init.c + + + esp_idf_components/freertos/port/xtensa/xtensa_overlay_os_hook.c + 1 + C:/Development/esp/esp-idf-44/components/freertos/port/xtensa/xtensa_overlay_os_hook.c + + + esp_idf_components/freertos/port/xtensa/xtensa_vector_defaults.S + 1 + C:/Development/esp/esp-idf-44/components/freertos/port/xtensa/xtensa_vector_defaults.S + + + esp_idf_components/freertos/port/xtensa/xtensa_vectors.S + 1 + C:/Development/esp/esp-idf-44/components/freertos/port/xtensa/xtensa_vectors.S + + + esp_idf_components/heap/port/esp32/memory_layout.c + 1 + C:/Development/esp/esp-idf-44/components/heap/port/esp32/memory_layout.c + + + esp_idf_components/lwip/apps/dhcpserver/dhcpserver.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/apps/dhcpserver/dhcpserver.c + + + esp_idf_components/lwip/apps/ping/esp_ping.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/apps/ping/esp_ping.c + + + esp_idf_components/lwip/apps/ping/ping.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/apps/ping/ping.c + + + esp_idf_components/lwip/apps/ping/ping_sock.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/apps/ping/ping_sock.c + + + esp_idf_components/lwip/apps/sntp/sntp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/apps/sntp/sntp.c + + + esp_idf_components/lwip/port/esp32/vfs_lwip.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/port/esp32/vfs_lwip.c + + + esp_idf_components/mbedtls/mbedtls/library/aes.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/aes.c + + + esp_idf_components/mbedtls/mbedtls/library/aesni.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/aesni.c + + + esp_idf_components/mbedtls/mbedtls/library/arc4.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/arc4.c + + + esp_idf_components/mbedtls/mbedtls/library/aria.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/aria.c + + + esp_idf_components/mbedtls/mbedtls/library/asn1parse.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/asn1parse.c + + + esp_idf_components/mbedtls/mbedtls/library/asn1write.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/asn1write.c + + + esp_idf_components/mbedtls/mbedtls/library/base64.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/base64.c + + + esp_idf_components/mbedtls/mbedtls/library/bignum.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/bignum.c + + + esp_idf_components/mbedtls/mbedtls/library/blowfish.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/blowfish.c + + + esp_idf_components/mbedtls/mbedtls/library/camellia.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/camellia.c + + + esp_idf_components/mbedtls/mbedtls/library/ccm.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ccm.c + + + esp_idf_components/mbedtls/mbedtls/library/certs.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/certs.c + + + esp_idf_components/mbedtls/mbedtls/library/chacha20.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/chacha20.c + + + esp_idf_components/mbedtls/mbedtls/library/chachapoly.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/chachapoly.c + + + esp_idf_components/mbedtls/mbedtls/library/cipher.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/cipher.c + + + esp_idf_components/mbedtls/mbedtls/library/cipher_wrap.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/cipher_wrap.c + + + esp_idf_components/mbedtls/mbedtls/library/cmac.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/cmac.c + + + esp_idf_components/mbedtls/mbedtls/library/ctr_drbg.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ctr_drbg.c + + + esp_idf_components/mbedtls/mbedtls/library/debug.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/debug.c + + + esp_idf_components/mbedtls/mbedtls/library/des.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/des.c + + + esp_idf_components/mbedtls/mbedtls/library/dhm.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/dhm.c + + + esp_idf_components/mbedtls/mbedtls/library/ecdh.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ecdh.c + + + esp_idf_components/mbedtls/mbedtls/library/ecdsa.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ecdsa.c + + + esp_idf_components/mbedtls/mbedtls/library/ecjpake.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ecjpake.c + + + esp_idf_components/mbedtls/mbedtls/library/ecp.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ecp.c + + + esp_idf_components/mbedtls/mbedtls/library/ecp_curves.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ecp_curves.c + + + esp_idf_components/mbedtls/mbedtls/library/entropy.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/entropy.c + + + esp_idf_components/mbedtls/mbedtls/library/entropy_poll.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/entropy_poll.c + + + esp_idf_components/mbedtls/mbedtls/library/error.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/error.c + + + esp_idf_components/mbedtls/mbedtls/library/gcm.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/gcm.c + + + esp_idf_components/mbedtls/mbedtls/library/havege.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/havege.c + + + esp_idf_components/mbedtls/mbedtls/library/hkdf.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/hkdf.c + + + esp_idf_components/mbedtls/mbedtls/library/hmac_drbg.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/hmac_drbg.c + + + esp_idf_components/mbedtls/mbedtls/library/md.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/md.c + + + esp_idf_components/mbedtls/mbedtls/library/md2.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/md2.c + + + esp_idf_components/mbedtls/mbedtls/library/md4.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/md4.c + + + esp_idf_components/mbedtls/mbedtls/library/md5.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/md5.c + + + esp_idf_components/mbedtls/mbedtls/library/md_wrap.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/md_wrap.c + + + esp_idf_components/mbedtls/mbedtls/library/memory_buffer_alloc.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/memory_buffer_alloc.c + + + esp_idf_components/mbedtls/mbedtls/library/nist_kw.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/nist_kw.c + + + esp_idf_components/mbedtls/mbedtls/library/oid.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/oid.c + + + esp_idf_components/mbedtls/mbedtls/library/padlock.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/padlock.c + + + esp_idf_components/mbedtls/mbedtls/library/pem.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/pem.c + + + esp_idf_components/mbedtls/mbedtls/library/pk.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/pk.c + + + esp_idf_components/mbedtls/mbedtls/library/pk_wrap.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/pk_wrap.c + + + esp_idf_components/mbedtls/mbedtls/library/pkcs11.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/pkcs11.c + + + esp_idf_components/mbedtls/mbedtls/library/pkcs12.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/pkcs12.c + + + esp_idf_components/mbedtls/mbedtls/library/pkcs5.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/pkcs5.c + + + esp_idf_components/mbedtls/mbedtls/library/pkparse.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/pkparse.c + + + esp_idf_components/mbedtls/mbedtls/library/pkwrite.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/pkwrite.c + + + esp_idf_components/mbedtls/mbedtls/library/platform.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/platform.c + + + esp_idf_components/mbedtls/mbedtls/library/platform_util.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/platform_util.c + + + esp_idf_components/mbedtls/mbedtls/library/poly1305.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/poly1305.c + + + esp_idf_components/mbedtls/mbedtls/library/ripemd160.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ripemd160.c + + + esp_idf_components/mbedtls/mbedtls/library/rsa.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/rsa.c + + + esp_idf_components/mbedtls/mbedtls/library/rsa_internal.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/rsa_internal.c + + + esp_idf_components/mbedtls/mbedtls/library/sha1.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/sha1.c + + + esp_idf_components/mbedtls/mbedtls/library/sha256.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/sha256.c + + + esp_idf_components/mbedtls/mbedtls/library/sha512.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/sha512.c + + + esp_idf_components/mbedtls/mbedtls/library/ssl_cache.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ssl_cache.c + + + esp_idf_components/mbedtls/mbedtls/library/ssl_ciphersuites.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ssl_ciphersuites.c + + + esp_idf_components/mbedtls/mbedtls/library/ssl_cli.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ssl_cli.c + + + esp_idf_components/mbedtls/mbedtls/library/ssl_cookie.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ssl_cookie.c + + + esp_idf_components/mbedtls/mbedtls/library/ssl_srv.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ssl_srv.c + + + esp_idf_components/mbedtls/mbedtls/library/ssl_ticket.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ssl_ticket.c + + + esp_idf_components/mbedtls/mbedtls/library/ssl_tls.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/ssl_tls.c + + + esp_idf_components/mbedtls/mbedtls/library/threading.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/threading.c + + + esp_idf_components/mbedtls/mbedtls/library/timing.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/timing.c + + + esp_idf_components/mbedtls/mbedtls/library/version.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/version.c + + + esp_idf_components/mbedtls/mbedtls/library/version_features.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/version_features.c + + + esp_idf_components/mbedtls/mbedtls/library/x509.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/x509.c + + + esp_idf_components/mbedtls/mbedtls/library/x509_create.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/x509_create.c + + + esp_idf_components/mbedtls/mbedtls/library/x509_crl.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/x509_crl.c + + + esp_idf_components/mbedtls/mbedtls/library/x509_crt.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/x509_crt.c + + + esp_idf_components/mbedtls/mbedtls/library/x509_csr.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/x509_csr.c + + + esp_idf_components/mbedtls/mbedtls/library/x509write_crt.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/x509write_crt.c + + + esp_idf_components/mbedtls/mbedtls/library/x509write_csr.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/x509write_csr.c + + + esp_idf_components/mbedtls/mbedtls/library/xtea.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/mbedtls/library/xtea.c + + + esp_idf_components/mbedtls/port/aes/esp_aes_common.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/aes/esp_aes_common.c + + + esp_idf_components/mbedtls/port/aes/esp_aes_xts.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/aes/esp_aes_xts.c + + + esp_idf_components/mbedtls/port/esp32/bignum.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/esp32/bignum.c + + + esp_idf_components/mbedtls/port/md/esp_md.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/md/esp_md.c + + + esp_idf_components/mbedtls/port/sha/esp_sha.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/sha/esp_sha.c + + + esp_idf_components/mqtt/esp-mqtt/lib/mqtt_msg.c + 1 + C:/Development/esp/esp-idf-44/components/mqtt/esp-mqtt/lib/mqtt_msg.c + + + esp_idf_components/mqtt/esp-mqtt/lib/mqtt_outbox.c + 1 + C:/Development/esp/esp-idf-44/components/mqtt/esp-mqtt/lib/mqtt_outbox.c + + + esp_idf_components/mqtt/esp-mqtt/lib/platform_esp32_idf.c + 1 + C:/Development/esp/esp-idf-44/components/mqtt/esp-mqtt/lib/platform_esp32_idf.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_buf.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_buf.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_callbacks.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_callbacks.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_debug.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_debug.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_frame.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_frame.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_hd.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_hd.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_hd_huffman.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_hd_huffman.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_hd_huffman_data.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_hd_huffman_data.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_helper.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_helper.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_http.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_http.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_map.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_map.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_mem.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_mem.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_npn.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_npn.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_option.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_option.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_outbound_item.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_outbound_item.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_pq.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_pq.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_priority_spec.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_priority_spec.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_queue.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_queue.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_rcbuf.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_rcbuf.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_session.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_session.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_stream.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_stream.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_submit.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_submit.c + + + esp_idf_components/nghttp/nghttp2/lib/nghttp2_version.c + 1 + C:/Development/esp/esp-idf-44/components/nghttp/nghttp2/lib/nghttp2_version.c + + + esp_idf_components/protobuf-c/protobuf-c/protobuf-c/protobuf-c.c + 1 + C:/Development/esp/esp-idf-44/components/protobuf-c/protobuf-c/protobuf-c/protobuf-c.c + + + esp_idf_components/protocomm/src/common/protocomm.c + 1 + C:/Development/esp/esp-idf-44/components/protocomm/src/common/protocomm.c + + + esp_idf_components/protocomm/src/security/security0.c + 1 + C:/Development/esp/esp-idf-44/components/protocomm/src/security/security0.c + + + esp_idf_components/protocomm/src/security/security1.c + 1 + C:/Development/esp/esp-idf-44/components/protocomm/src/security/security1.c + + + esp_idf_components/protocomm/src/transports/protocomm_console.c + 1 + C:/Development/esp/esp-idf-44/components/protocomm/src/transports/protocomm_console.c + + + esp_idf_components/protocomm/src/transports/protocomm_httpd.c + 1 + C:/Development/esp/esp-idf-44/components/protocomm/src/transports/protocomm_httpd.c + + + esp_idf_components/spiffs/spiffs/src/spiffs_cache.c + 1 + C:/Development/esp/esp-idf-44/components/spiffs/spiffs/src/spiffs_cache.c + + + esp_idf_components/spiffs/spiffs/src/spiffs_check.c + 1 + C:/Development/esp/esp-idf-44/components/spiffs/spiffs/src/spiffs_check.c + + + esp_idf_components/spiffs/spiffs/src/spiffs_gc.c + 1 + C:/Development/esp/esp-idf-44/components/spiffs/spiffs/src/spiffs_gc.c + + + esp_idf_components/spiffs/spiffs/src/spiffs_hydrogen.c + 1 + C:/Development/esp/esp-idf-44/components/spiffs/spiffs/src/spiffs_hydrogen.c + + + esp_idf_components/spiffs/spiffs/src/spiffs_nucleus.c + 1 + C:/Development/esp/esp-idf-44/components/spiffs/spiffs/src/spiffs_nucleus.c + + + esp_idf_components/unity/unity/src/unity.c + 1 + C:/Development/esp/esp-idf-44/components/unity/unity/src/unity.c + + + esp_idf_components/wpa_supplicant/esp_supplicant/src/esp_dpp.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/esp_supplicant/src/esp_dpp.c + + + esp_idf_components/wpa_supplicant/esp_supplicant/src/esp_hostap.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/esp_supplicant/src/esp_hostap.c + + + esp_idf_components/wpa_supplicant/esp_supplicant/src/esp_wpa2.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/esp_supplicant/src/esp_wpa2.c + + + esp_idf_components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c + + + esp_idf_components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c + + + esp_idf_components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c + + + esp_idf_components/wpa_supplicant/esp_supplicant/src/esp_wps.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/esp_supplicant/src/esp_wps.c + + + esp_idf_components/wpa_supplicant/src/ap/ap_config.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/ap/ap_config.c + + + esp_idf_components/wpa_supplicant/src/ap/ieee802_1x.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/ap/ieee802_1x.c + + + esp_idf_components/wpa_supplicant/src/ap/wpa_auth.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/ap/wpa_auth.c + + + esp_idf_components/wpa_supplicant/src/ap/wpa_auth_ie.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/ap/wpa_auth_ie.c + + + esp_idf_components/wpa_supplicant/src/common/dpp.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/common/dpp.c + + + esp_idf_components/wpa_supplicant/src/common/sae.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/common/sae.c + + + esp_idf_components/wpa_supplicant/src/common/wpa_common.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/common/wpa_common.c + + + esp_idf_components/wpa_supplicant/src/crypto/aes-ccm.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/aes-ccm.c + + + esp_idf_components/wpa_supplicant/src/crypto/aes-gcm.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/aes-gcm.c + + + esp_idf_components/wpa_supplicant/src/crypto/aes-omac1.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/aes-omac1.c + + + esp_idf_components/wpa_supplicant/src/crypto/aes-siv.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/aes-siv.c + + + esp_idf_components/wpa_supplicant/src/crypto/aes-unwrap.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/aes-unwrap.c + + + esp_idf_components/wpa_supplicant/src/crypto/aes-wrap.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/aes-wrap.c + + + esp_idf_components/wpa_supplicant/src/crypto/ccmp.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/ccmp.c + + + esp_idf_components/wpa_supplicant/src/crypto/crypto_mbedtls-bignum.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/crypto_mbedtls-bignum.c + + + esp_idf_components/wpa_supplicant/src/crypto/crypto_mbedtls-ec.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/crypto_mbedtls-ec.c + + + esp_idf_components/wpa_supplicant/src/crypto/crypto_mbedtls.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/crypto_mbedtls.c + + + esp_idf_components/wpa_supplicant/src/crypto/crypto_ops.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/crypto_ops.c + + + esp_idf_components/wpa_supplicant/src/crypto/des-internal.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/des-internal.c + + + esp_idf_components/wpa_supplicant/src/crypto/dh_group5.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/dh_group5.c + + + esp_idf_components/wpa_supplicant/src/crypto/dh_groups.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/dh_groups.c + + + esp_idf_components/wpa_supplicant/src/crypto/md4-internal.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/md4-internal.c + + + esp_idf_components/wpa_supplicant/src/crypto/ms_funcs.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/ms_funcs.c + + + esp_idf_components/wpa_supplicant/src/crypto/rc4.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/rc4.c + + + esp_idf_components/wpa_supplicant/src/crypto/sha1-prf.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/sha1-prf.c + + + esp_idf_components/wpa_supplicant/src/crypto/sha1-tlsprf.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/sha1-tlsprf.c + + + esp_idf_components/wpa_supplicant/src/crypto/sha256-kdf.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/sha256-kdf.c + + + esp_idf_components/wpa_supplicant/src/crypto/sha256-prf.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/sha256-prf.c + + + esp_idf_components/wpa_supplicant/src/crypto/sha256-tlsprf.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/sha256-tlsprf.c + + + esp_idf_components/wpa_supplicant/src/crypto/sha384-prf.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/sha384-prf.c + + + esp_idf_components/wpa_supplicant/src/crypto/sha384-tlsprf.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/sha384-tlsprf.c + + + esp_idf_components/wpa_supplicant/src/crypto/tls_mbedtls.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/crypto/tls_mbedtls.c + + + esp_idf_components/wpa_supplicant/src/eap_peer/chap.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/eap_peer/chap.c + + + esp_idf_components/wpa_supplicant/src/eap_peer/eap.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/eap_peer/eap.c + + + esp_idf_components/wpa_supplicant/src/eap_peer/eap_common.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/eap_peer/eap_common.c + + + esp_idf_components/wpa_supplicant/src/eap_peer/eap_mschapv2.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/eap_peer/eap_mschapv2.c + + + esp_idf_components/wpa_supplicant/src/eap_peer/eap_peap.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/eap_peer/eap_peap.c + + + esp_idf_components/wpa_supplicant/src/eap_peer/eap_peap_common.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/eap_peer/eap_peap_common.c + + + esp_idf_components/wpa_supplicant/src/eap_peer/eap_tls.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/eap_peer/eap_tls.c + + + esp_idf_components/wpa_supplicant/src/eap_peer/eap_tls_common.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/eap_peer/eap_tls_common.c + + + esp_idf_components/wpa_supplicant/src/eap_peer/eap_ttls.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/eap_peer/eap_ttls.c + + + esp_idf_components/wpa_supplicant/src/eap_peer/mschapv2.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/eap_peer/mschapv2.c + + + esp_idf_components/wpa_supplicant/src/rsn_supp/pmksa_cache.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c + + + esp_idf_components/wpa_supplicant/src/rsn_supp/wpa.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/rsn_supp/wpa.c + + + esp_idf_components/wpa_supplicant/src/rsn_supp/wpa_ie.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/rsn_supp/wpa_ie.c + + + esp_idf_components/wpa_supplicant/src/utils/base64.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/utils/base64.c + + + esp_idf_components/wpa_supplicant/src/utils/bitfield.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/utils/bitfield.c + + + esp_idf_components/wpa_supplicant/src/utils/common.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/utils/common.c + + + esp_idf_components/wpa_supplicant/src/utils/ext_password.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/utils/ext_password.c + + + esp_idf_components/wpa_supplicant/src/utils/json.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/utils/json.c + + + esp_idf_components/wpa_supplicant/src/utils/uuid.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/utils/uuid.c + + + esp_idf_components/wpa_supplicant/src/utils/wpa_debug.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/utils/wpa_debug.c + + + esp_idf_components/wpa_supplicant/src/utils/wpabuf.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/utils/wpabuf.c + + + esp_idf_components/wpa_supplicant/src/wps/wps.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/wps/wps.c + + + esp_idf_components/wpa_supplicant/src/wps/wps_attr_build.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/wps/wps_attr_build.c + + + esp_idf_components/wpa_supplicant/src/wps/wps_attr_parse.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/wps/wps_attr_parse.c + + + esp_idf_components/wpa_supplicant/src/wps/wps_attr_process.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/wps/wps_attr_process.c + + + esp_idf_components/wpa_supplicant/src/wps/wps_common.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/wps/wps_common.c + + + esp_idf_components/wpa_supplicant/src/wps/wps_dev_attr.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/wps/wps_dev_attr.c + + + esp_idf_components/wpa_supplicant/src/wps/wps_enrollee.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/wps/wps_enrollee.c + + + esp_idf_components/wpa_supplicant/src/wps/wps_registrar.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/wps/wps_registrar.c + + + esp_idf_components/wpa_supplicant/src/wps/wps_validate.c + 1 + C:/Development/esp/esp-idf-44/components/wpa_supplicant/src/wps/wps_validate.c + + + esp_idf_components/asio/asio/asio/src/asio.cpp + 1 + C:/Development/esp/esp-idf-44/components/asio/asio/asio/src/asio.cpp + + + esp_idf_components/esp_system/port/arch/xtensa/debug_helpers.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/arch/xtensa/debug_helpers.c + + + esp_idf_components/esp_system/port/arch/xtensa/debug_helpers_asm.S + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/arch/xtensa/debug_helpers_asm.S + + + esp_idf_components/esp_system/port/arch/xtensa/expression_with_stack.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/arch/xtensa/expression_with_stack.c + + + esp_idf_components/esp_system/port/arch/xtensa/expression_with_stack_asm.S + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/arch/xtensa/expression_with_stack_asm.S + + + esp_idf_components/esp_system/port/arch/xtensa/panic_arch.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/arch/xtensa/panic_arch.c + + + esp_idf_components/esp_system/port/arch/xtensa/panic_handler_asm.S + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/arch/xtensa/panic_handler_asm.S + + + esp_idf_components/esp_system/port/arch/xtensa/trax.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/arch/xtensa/trax.c + + + esp_idf_components/esp_system/port/soc/esp32/cache_err_int.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/soc/esp32/cache_err_int.c + + + esp_idf_components/esp_system/port/soc/esp32/clk.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/soc/esp32/clk.c + + + esp_idf_components/esp_system/port/soc/esp32/highint_hdl.S + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/soc/esp32/highint_hdl.S + + + esp_idf_components/esp_system/port/soc/esp32/reset_reason.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/soc/esp32/reset_reason.c + + + esp_idf_components/esp_system/port/soc/esp32/system_internal.c + 1 + C:/Development/esp/esp-idf-44/components/esp_system/port/soc/esp32/system_internal.c + + + esp_idf_components/espcoredump/src/port/xtensa/core_dump_port.c + 1 + C:/Development/esp/esp-idf-44/components/espcoredump/src/port/xtensa/core_dump_port.c + + + esp_idf_components/expat/expat/expat/lib/xmlparse.c + 1 + C:/Development/esp/esp-idf-44/components/expat/expat/expat/lib/xmlparse.c + + + esp_idf_components/expat/expat/expat/lib/xmlrole.c + 1 + C:/Development/esp/esp-idf-44/components/expat/expat/expat/lib/xmlrole.c + + + esp_idf_components/expat/expat/expat/lib/xmltok.c + 1 + C:/Development/esp/esp-idf-44/components/expat/expat/expat/lib/xmltok.c + + + esp_idf_components/expat/expat/expat/lib/xmltok_impl.c + 1 + C:/Development/esp/esp-idf-44/components/expat/expat/expat/lib/xmltok_impl.c + + + esp_idf_components/expat/expat/expat/lib/xmltok_ns.c + 1 + C:/Development/esp/esp-idf-44/components/expat/expat/expat/lib/xmltok_ns.c + + + esp_idf_components/lwip/lwip/src/api/api_lib.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/api/api_lib.c + + + esp_idf_components/lwip/lwip/src/api/api_msg.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/api/api_msg.c + + + esp_idf_components/lwip/lwip/src/api/err.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/api/err.c + + + esp_idf_components/lwip/lwip/src/api/if_api.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/api/if_api.c + + + esp_idf_components/lwip/lwip/src/api/netbuf.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/api/netbuf.c + + + esp_idf_components/lwip/lwip/src/api/netdb.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/api/netdb.c + + + esp_idf_components/lwip/lwip/src/api/netifapi.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/api/netifapi.c + + + esp_idf_components/lwip/lwip/src/api/sockets.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/api/sockets.c + + + esp_idf_components/lwip/lwip/src/api/tcpip.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/api/tcpip.c + + + esp_idf_components/lwip/lwip/src/core/def.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/def.c + + + esp_idf_components/lwip/lwip/src/core/dns.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/dns.c + + + esp_idf_components/lwip/lwip/src/core/inet_chksum.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/inet_chksum.c + + + esp_idf_components/lwip/lwip/src/core/init.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/init.c + + + esp_idf_components/lwip/lwip/src/core/ip.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ip.c + + + esp_idf_components/lwip/lwip/src/core/mem.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/mem.c + + + esp_idf_components/lwip/lwip/src/core/memp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/memp.c + + + esp_idf_components/lwip/lwip/src/core/netif.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/netif.c + + + esp_idf_components/lwip/lwip/src/core/pbuf.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/pbuf.c + + + esp_idf_components/lwip/lwip/src/core/raw.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/raw.c + + + esp_idf_components/lwip/lwip/src/core/stats.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/stats.c + + + esp_idf_components/lwip/lwip/src/core/sys.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/sys.c + + + esp_idf_components/lwip/lwip/src/core/tcp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/tcp.c + + + esp_idf_components/lwip/lwip/src/core/tcp_in.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/tcp_in.c + + + esp_idf_components/lwip/lwip/src/core/tcp_out.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/tcp_out.c + + + esp_idf_components/lwip/lwip/src/core/timeouts.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/timeouts.c + + + esp_idf_components/lwip/lwip/src/core/udp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/udp.c + + + esp_idf_components/lwip/lwip/src/netif/ethernet.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ethernet.c + + + esp_idf_components/lwip/lwip/src/netif/lowpan6.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/lowpan6.c + + + esp_idf_components/lwip/lwip/src/netif/slipif.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/slipif.c + + + esp_idf_components/lwip/port/esp32/debug/lwip_debug.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/port/esp32/debug/lwip_debug.c + + + esp_idf_components/lwip/port/esp32/freertos/sys_arch.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/port/esp32/freertos/sys_arch.c + + + esp_idf_components/lwip/port/esp32/hooks/lwip_default_hooks.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/port/esp32/hooks/lwip_default_hooks.c + + + esp_idf_components/lwip/port/esp32/hooks/tcp_isn_default.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/port/esp32/hooks/tcp_isn_default.c + + + esp_idf_components/lwip/port/esp32/netif/dhcp_state.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/port/esp32/netif/dhcp_state.c + + + esp_idf_components/lwip/port/esp32/netif/ethernetif.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/port/esp32/netif/ethernetif.c + + + esp_idf_components/lwip/port/esp32/netif/wlanif.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/port/esp32/netif/wlanif.c + + + esp_idf_components/mbedtls/port/aes/block/esp_aes.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/aes/block/esp_aes.c + + + esp_idf_components/mbedtls/port/sha/parallel_engine/esp_sha1.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/sha/parallel_engine/esp_sha1.c + + + esp_idf_components/mbedtls/port/sha/parallel_engine/esp_sha256.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/sha/parallel_engine/esp_sha256.c + + + esp_idf_components/mbedtls/port/sha/parallel_engine/esp_sha512.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/sha/parallel_engine/esp_sha512.c + + + esp_idf_components/mbedtls/port/sha/parallel_engine/sha.c + 1 + C:/Development/esp/esp-idf-44/components/mbedtls/port/sha/parallel_engine/sha.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_auth/crypto_auth.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_auth/crypto_auth.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_box/crypto_box.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_box/crypto_box.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_box/crypto_box_easy.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_box/crypto_box_easy.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_box/crypto_box_seal.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_box/crypto_box_seal.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_generichash/crypto_generichash.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_generichash/crypto_generichash.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_hash/crypto_hash.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_hash/crypto_hash.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_kdf/crypto_kdf.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_kdf/crypto_kdf.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_kx/crypto_kx.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_kx/crypto_kx.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_onetimeauth/crypto_onetimeauth.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_onetimeauth/crypto_onetimeauth.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/crypto_pwhash.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/crypto_pwhash.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/crypto_scalarmult.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/crypto_scalarmult.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_secretbox/crypto_secretbox.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_secretbox/crypto_secretbox.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_secretbox/crypto_secretbox_easy.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_secretbox/crypto_secretbox_easy.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_shorthash/crypto_shorthash.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_shorthash/crypto_shorthash.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_sign/crypto_sign.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_sign/crypto_sign.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/crypto_stream.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/crypto_stream.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/randombytes/randombytes.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/randombytes/randombytes.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/sodium/codecs.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/sodium/codecs.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/sodium/core.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/sodium/core.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/sodium/runtime.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/sodium/runtime.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/sodium/utils.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/sodium/utils.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/sodium/version.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/sodium/version.c + + + esp_idf_components/lwip/lwip/src/apps/netbiosns/netbiosns.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/apps/netbiosns/netbiosns.c + + + esp_idf_components/lwip/lwip/src/apps/sntp/sntp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/apps/sntp/sntp.c + + + esp_idf_components/lwip/lwip/src/core/ipv4/autoip.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv4/autoip.c + + + esp_idf_components/lwip/lwip/src/core/ipv4/dhcp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv4/dhcp.c + + + esp_idf_components/lwip/lwip/src/core/ipv4/etharp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv4/etharp.c + + + esp_idf_components/lwip/lwip/src/core/ipv4/icmp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv4/icmp.c + + + esp_idf_components/lwip/lwip/src/core/ipv4/igmp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv4/igmp.c + + + esp_idf_components/lwip/lwip/src/core/ipv4/ip4.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv4/ip4.c + + + esp_idf_components/lwip/lwip/src/core/ipv4/ip4_addr.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv4/ip4_addr.c + + + esp_idf_components/lwip/lwip/src/core/ipv4/ip4_frag.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv4/ip4_frag.c + + + esp_idf_components/lwip/lwip/src/core/ipv4/ip4_napt.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv4/ip4_napt.c + + + esp_idf_components/lwip/lwip/src/core/ipv6/dhcp6.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv6/dhcp6.c + + + esp_idf_components/lwip/lwip/src/core/ipv6/ethip6.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv6/ethip6.c + + + esp_idf_components/lwip/lwip/src/core/ipv6/icmp6.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv6/icmp6.c + + + esp_idf_components/lwip/lwip/src/core/ipv6/inet6.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv6/inet6.c + + + esp_idf_components/lwip/lwip/src/core/ipv6/ip6.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv6/ip6.c + + + esp_idf_components/lwip/lwip/src/core/ipv6/ip6_addr.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv6/ip6_addr.c + + + esp_idf_components/lwip/lwip/src/core/ipv6/ip6_frag.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv6/ip6_frag.c + + + esp_idf_components/lwip/lwip/src/core/ipv6/mld6.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv6/mld6.c + + + esp_idf_components/lwip/lwip/src/core/ipv6/nd6.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/core/ipv6/nd6.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/auth.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/auth.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/ccp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/ccp.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/chap-md5.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/chap-md5.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/chap-new.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/chap-new.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/chap_ms.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/chap_ms.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/demand.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/demand.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/eap.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/eap.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/ecp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/ecp.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/eui64.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/eui64.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/fsm.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/fsm.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/ipcp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/ipcp.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/ipv6cp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/ipv6cp.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/lcp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/lcp.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/magic.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/magic.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/mppe.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/mppe.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/multilink.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/multilink.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/ppp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/ppp.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/pppapi.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/pppapi.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/pppcrypt.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/pppcrypt.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/pppoe.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/pppoe.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/pppol2tp.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/pppol2tp.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/pppos.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/pppos.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/upap.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/upap.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/utils.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/utils.c + + + esp_idf_components/lwip/lwip/src/netif/ppp/vj.c + 1 + C:/Development/esp/esp-idf-44/components/lwip/lwip/src/netif/ppp/vj.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_auth/hmacsha256/auth_hmacsha256.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_auth/hmacsha256/auth_hmacsha256.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_auth/hmacsha512/auth_hmacsha512.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_auth/hmacsha512/auth_hmacsha512.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_auth/hmacsha512256/auth_hmacsha512256.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_auth/hmacsha512256/auth_hmacsha512256.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_box/curve25519xchacha20poly1305/box_curve25519xchacha20poly1305.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_box/curve25519xchacha20poly1305/box_curve25519xchacha20poly1305.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_box/curve25519xchacha20poly1305/box_seal_curve25519xchacha20poly1305.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_box/curve25519xchacha20poly1305/box_seal_curve25519xchacha20poly1305.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_box/curve25519xsalsa20poly1305/box_curve25519xsalsa20poly1305.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_box/curve25519xsalsa20poly1305/box_curve25519xsalsa20poly1305.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_core/ed25519/core_ed25519.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_core/ed25519/core_ed25519.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_core/ed25519/core_ristretto255.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_core/ed25519/core_ristretto255.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_core/hchacha20/core_hchacha20.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_core/hchacha20/core_hchacha20.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_core/hsalsa20/core_hsalsa20.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_core/hsalsa20/core_hsalsa20.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/generichash_blake2.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/generichash_blake2.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_hash/sha256/hash_sha256.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_hash/sha256/hash_sha256.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_hash/sha512/hash_sha512.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_hash/sha512/hash_sha512.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_kdf/blake2b/kdf_blake2b.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_kdf/blake2b/kdf_blake2b.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_onetimeauth/poly1305/onetimeauth_poly1305.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_onetimeauth/poly1305/onetimeauth_poly1305.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-core.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-core.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-encoding.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-encoding.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-fill-block-avx2.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-fill-block-avx2.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-fill-block-avx512f.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-fill-block-avx512f.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-fill-block-ref.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-fill-block-ref.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-fill-block-ssse3.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-fill-block-ssse3.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/argon2.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/blake2b-long.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/blake2b-long.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/pwhash_argon2i.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/pwhash_argon2i.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/pwhash_argon2id.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/argon2/pwhash_argon2id.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/scryptsalsa208sha256/crypto_scrypt-common.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/scryptsalsa208sha256/crypto_scrypt-common.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/scryptsalsa208sha256/pwhash_scryptsalsa208sha256.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/scryptsalsa208sha256/pwhash_scryptsalsa208sha256.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/scryptsalsa208sha256/scrypt_platform.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/scryptsalsa208sha256/scrypt_platform.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/scalarmult_curve25519.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/scalarmult_curve25519.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_secretbox/xchacha20poly1305/secretbox_xchacha20poly1305.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_secretbox/xchacha20poly1305/secretbox_xchacha20poly1305.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_secretbox/xsalsa20poly1305/secretbox_xsalsa20poly1305.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_secretbox/xsalsa20poly1305/secretbox_xsalsa20poly1305.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_secretstream/xchacha20poly1305/secretstream_xchacha20poly1305.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_secretstream/xchacha20poly1305/secretstream_xchacha20poly1305.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_shorthash/siphash24/shorthash_siphash24.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_shorthash/siphash24/shorthash_siphash24.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_shorthash/siphash24/shorthash_siphashx24.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_shorthash/siphash24/shorthash_siphashx24.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_sign/ed25519/sign_ed25519.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_sign/ed25519/sign_ed25519.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/chacha20/stream_chacha20.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/chacha20/stream_chacha20.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/stream_salsa20.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/stream_salsa20.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/salsa2012/stream_salsa2012.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/salsa2012/stream_salsa2012.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/salsa208/stream_salsa208.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/salsa208/stream_salsa208.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/xchacha20/stream_xchacha20.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/xchacha20/stream_xchacha20.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/xsalsa20/stream_xsalsa20.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/xsalsa20/stream_xsalsa20.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_verify/sodium/verify.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_verify/sodium/verify.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_core/salsa/ref/core_salsa_ref.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_core/salsa/ref/core_salsa_ref.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-avx2.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-avx2.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ref.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ref.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ssse3.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ssse3.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-ref.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-ref.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/ref/generichash_blake2b.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_generichash/blake2b/ref/generichash_blake2b.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_hash/sha256/cp/hash_sha256_cp.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_hash/sha256/cp/hash_sha256_cp.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_hash/sha512/cp/hash_sha512_cp.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_hash/sha512/cp/hash_sha512_cp.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_onetimeauth/poly1305/donna/poly1305_donna.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_onetimeauth/poly1305/donna/poly1305_donna.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_pwhash/scryptsalsa208sha256/nosse/pwhash_scryptsalsa208sha256_nosse.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_pwhash/scryptsalsa208sha256/nosse/pwhash_scryptsalsa208sha256_nosse.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/consts.S + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/consts.S + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/curve25519_sandy2x.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/curve25519_sandy2x.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/fe51_invert.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/fe51_invert.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/fe51_mul.S + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/fe51_mul.S + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/fe51_nsquare.S + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/fe51_nsquare.S + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/fe51_pack.S + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/fe51_pack.S + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/fe_frombytes_sandy2x.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/fe_frombytes_sandy2x.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/ladder.S + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/ladder.S + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/sandy2x.S + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/curve25519/sandy2x/sandy2x.S + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/ed25519/ref10/scalarmult_ed25519_ref10.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/ed25519/ref10/scalarmult_ed25519_ref10.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_scalarmult/ristretto255/ref10/scalarmult_ristretto255_ref10.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_scalarmult/ristretto255/ref10/scalarmult_ristretto255_ref10.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_shorthash/siphash24/ref/shorthash_siphash24_ref.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_shorthash/siphash24/ref/shorthash_siphash24_ref.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_shorthash/siphash24/ref/shorthash_siphashx24_ref.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_shorthash/siphash24/ref/shorthash_siphashx24_ref.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_sign/ed25519/ref10/keypair.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_sign/ed25519/ref10/keypair.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_sign/ed25519/ref10/obsolete.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_sign/ed25519/ref10/obsolete.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_sign/ed25519/ref10/open.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_sign/ed25519/ref10/open.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_sign/ed25519/ref10/sign.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_sign/ed25519/ref10/sign.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/chacha20/dolbeau/chacha20_dolbeau-avx2.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/chacha20/dolbeau/chacha20_dolbeau-avx2.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/chacha20/dolbeau/chacha20_dolbeau-ssse3.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/chacha20/dolbeau/chacha20_dolbeau-ssse3.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/chacha20/ref/chacha20_ref.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/chacha20/ref/chacha20_ref.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/ref/salsa20_ref.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/ref/salsa20_ref.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/xmm6/salsa20_xmm6-asm.S + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/xmm6/salsa20_xmm6-asm.S + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/xmm6/salsa20_xmm6.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/xmm6/salsa20_xmm6.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/xmm6int/salsa20_xmm6int-avx2.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/xmm6int/salsa20_xmm6int-avx2.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/xmm6int/salsa20_xmm6int-sse2.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/salsa20/xmm6int/salsa20_xmm6int-sse2.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/salsa2012/ref/stream_salsa2012_ref.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/salsa2012/ref/stream_salsa2012_ref.c + + + esp_idf_components/libsodium/libsodium/src/libsodium/crypto_stream/salsa208/ref/stream_salsa208_ref.c + 1 + C:/Development/esp/esp-idf-44/components/libsodium/libsodium/src/libsodium/crypto_stream/salsa208/ref/stream_salsa208_ref.c + + IOT_SOLUTION_PATH diff --git a/Makefile b/Makefile index e5c2a9c2..973108de 100755 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # project subdirectory. # -PROJECT_NAME := hello-world +PROJECT_NAME := grownode include $(IDF_PATH)/make/project.mk diff --git a/components/grownode/CMakeLists.txt b/components/grownode/CMakeLists.txt index b891c1c7..f1be67a3 100755 --- a/components/grownode/CMakeLists.txt +++ b/components/grownode/CMakeLists.txt @@ -113,7 +113,7 @@ else() if(DEFINED ENV{IDF_LIB_PATH}) list(APPEND components_required - "bmp280" "ds18x20" + "bmp280" "ds18x20" "ina219" ) endif() @@ -143,6 +143,7 @@ else() "leaves/gn_gpio.c" "leaves/gn_led.c" "leaves/gn_leaf_status_led.c" + "leaves/gn_leaf_ina219.c" "test/test_grownode.c" "test/test_pump.c" "test/test_ds18b20.c" @@ -152,6 +153,7 @@ else() "boards/gn_hydroboard2.c" "boards/gn_easypot1.c" "boards/gn_blink.c" + "boards/gn_oscilloscope.c" REQUIRES "${components_required}" INCLUDE_DIRS diff --git a/components/grownode/boards/gn_oscilloscope.c b/components/grownode/boards/gn_oscilloscope.c new file mode 100644 index 00000000..4c01486b --- /dev/null +++ b/components/grownode/boards/gn_oscilloscope.c @@ -0,0 +1,49 @@ +// Copyright 2021 Nicola Muratori (nicola.muratori@gmail.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "esp_log.h" + +#include "grownode.h" +#include "gn_leaf_ina219.h" + +#include "gn_oscilloscope.h" + +#define TAG "gn_oscilloscope" + + + +/** + * @brief + * + */ +void gn_configure_oscilloscope(gn_node_handle_t node) { + + esp_log_level_set("gn_leaf_ina219", esp_log_level_get(TAG)); + + gn_leaf_handle_t ina219 = gn_leaf_create(node, "ina219", gn_leaf_ina219_config, 8192); + gn_leaf_param_init_bool(ina219, GN_LEAF_INA219_PARAM_ACTIVE, true); + + char* ip = calloc(16, sizeof(char)); + strncpy(ip, "192.168.1.20", 16); + + gn_leaf_param_init_string(ina219, GN_LEAF_INA219_PARAM_IP, ip); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_PORT, 8094); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_MS, 50); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SDA, 26); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SCL, 27); + +} + diff --git a/components/grownode/boards/gn_oscilloscope.h b/components/grownode/boards/gn_oscilloscope.h new file mode 100644 index 00000000..d6011e50 --- /dev/null +++ b/components/grownode/boards/gn_oscilloscope.h @@ -0,0 +1,30 @@ +// Copyright 2021 Nicola Muratori (nicola.muratori@gmail.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MAIN_GN_OSCILLOSCOPE_H_ +#define MAIN_GN_OSCILLOSCOPE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "grownode.h" + +void gn_configure_oscilloscope(gn_node_handle_t node); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif /* MAIN_GN_OSCILLOSCOPE_H_ */ diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index 8bbc2103..e1274fc3 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -835,7 +835,7 @@ gn_err_t gn_node_loop(gn_node_handle_t node) { gn_node_sleep(node, GN_SLEEP_MODE_LIGHT, _node->config->config_init_params->sleep_delay_millisec, _node->config->config_init_params->sleep_time_millisec); - ESP_LOGI(TAG, "waking up from light sleep"); + ESP_LOGD(TAG, "waking up from light sleep"); } } else { @@ -1503,7 +1503,7 @@ gn_err_t gn_leaf_param_init_string(const gn_leaf_handle_t leaf_config, } //store the parameter - if (gn_storage_set(_buf, (void**) &val, strlen(val)) != ESP_OK) { + if (gn_storage_set(_buf, (void*) val, strlen(val)+1) != ESP_OK) { ESP_LOGW(TAG, "not possible to store leaf parameter value - key %s value %s", _buf, val); @@ -1611,7 +1611,7 @@ gn_err_t gn_leaf_param_write_string(const gn_leaf_handle_t leaf_config, _buf[_len] = '\0'; - if (gn_storage_set(_buf, (void**) &val, strlen(val)) != ESP_OK) { + if (gn_storage_set(_buf, (void*) val, strlen(val)) != ESP_OK) { ESP_LOGW(TAG, "not possible to store leaf parameter value - key %s value %s", _buf, val); @@ -1623,7 +1623,7 @@ gn_err_t gn_leaf_param_write_string(const gn_leaf_handle_t leaf_config, } - ESP_LOGD(TAG, "gn_leaf_param_write_string - result %s", + ESP_LOGD(TAG, "gn_leaf_param_write_string - result: %s", _param->param_val->v.s); //notify event loop @@ -2996,6 +2996,7 @@ gn_err_t gn_storage_set(const char *key, const void *value, goto fail; } */ + ESP_LOGD(TAG_NVS, "gn_storage_set(%s, %s)", key, (char*)value); nvs_handle_t my_handle; @@ -3082,7 +3083,7 @@ gn_err_t gn_storage_get(const char *key, void **value) { if (required_size > 0) { // Read previously saved blob if available - *value = malloc(required_size + sizeof(uint32_t)); + *value = calloc(required_size + sizeof(uint32_t),1); err = nvs_get_blob(my_handle, _hashedkey, *value, &required_size); @@ -3092,7 +3093,7 @@ gn_err_t gn_storage_get(const char *key, void **value) { free(*value); goto fail; } - ESP_LOGD(TAG_NVS, "gn_storage_get(%s) - %s - ESP_OK", key, + ESP_LOGD(TAG_NVS, "gn_storage_get(%s) - %s - OK", key, (char* ) *value); } else goto fail; diff --git a/components/grownode/leaves/gn_leaf_ina219.c b/components/grownode/leaves/gn_leaf_ina219.c new file mode 100644 index 00000000..1f0f92a2 --- /dev/null +++ b/components/grownode/leaves/gn_leaf_ina219.c @@ -0,0 +1,387 @@ +// Copyright 2021 Nicola Muratori (nicola.muratori@gmail.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIG_GROWNODE_DISPLAY_ENABLED +/* Littlevgl specific */ +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "lvgl_helpers.h" +#endif + +#include "esp_log.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "freertos/semphr.h" + +#include "driver/mcpwm.h" +#include "soc/mcpwm_periph.h" + +#include "esp_netif.h" + +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include + +#include "ina219.h" + +#include "gn_commons.h" + +#include "gn_leaf_ina219.h" + +#define TAG "gn_leaf_ina219" + +#define I2C_PORT 0 +#define I2C_ADDR INA219_ADDR_GND_GND + +typedef struct { + gn_leaf_param_handle_t gn_leaf_ina219_active_param; + gn_leaf_param_handle_t gn_leaf_ina219_ip_param; + gn_leaf_param_handle_t gn_leaf_ina219_port_param; + gn_leaf_param_handle_t gn_leaf_ina219_sampling_ms_param; + gn_leaf_param_handle_t gn_leaf_ina219_sda_param; + gn_leaf_param_handle_t gn_leaf_ina219_scl_param; + ina219_t dev; +} gn_leaf_ina219_data_t; + +void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config); + +gn_leaf_descriptor_handle_t gn_leaf_ina219_config(gn_leaf_handle_t leaf_config) { + + ESP_LOGD(TAG, "ina219 configuring.."); + + gn_leaf_descriptor_handle_t descriptor = + (gn_leaf_descriptor_handle_t) malloc(sizeof(gn_leaf_descriptor_t)); + strncpy(descriptor->type, GN_LEAF_INA219_TYPE, GN_LEAF_DESC_TYPE_SIZE); + descriptor->callback = gn_leaf_ina219_task; + descriptor->status = GN_LEAF_STATUS_NOT_INITIALIZED; + + gn_leaf_ina219_data_t *data = malloc(sizeof(gn_leaf_ina219_data_t)); + + data->gn_leaf_ina219_active_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_ACTIVE, GN_VAL_TYPE_BOOLEAN, (gn_val_t ) { .b = + true }, GN_LEAF_PARAM_ACCESS_NETWORK, + GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_boolean); + + data->gn_leaf_ina219_ip_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_IP, GN_VAL_TYPE_STRING, (gn_val_t ) { .s = + malloc(16) }, GN_LEAF_PARAM_ACCESS_NETWORK, + GN_LEAF_PARAM_STORAGE_PERSISTED, NULL); + + data->gn_leaf_ina219_port_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_PORT, GN_VAL_TYPE_DOUBLE, + (gn_val_t ) { .d = 0 }, GN_LEAF_PARAM_ACCESS_NETWORK, + GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); + + data->gn_leaf_ina219_sampling_ms_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_SAMPLING_MS, GN_VAL_TYPE_DOUBLE, (gn_val_t ) { + .d = 100 }, GN_LEAF_PARAM_ACCESS_NETWORK, + GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); + + data->gn_leaf_ina219_sda_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_SDA, GN_VAL_TYPE_DOUBLE, + (gn_val_t ) { .d = 26 }, GN_LEAF_PARAM_ACCESS_NODE, + GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); + + data->gn_leaf_ina219_scl_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_SCL, GN_VAL_TYPE_DOUBLE, + (gn_val_t ) { .d = 27 }, GN_LEAF_PARAM_ACCESS_NODE, + GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); + + gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_active_param); + gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_ip_param); + gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_port_param); + gn_leaf_param_add_to_leaf(leaf_config, + data->gn_leaf_ina219_sampling_ms_param); + gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_sda_param); + gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_scl_param); + + descriptor->status = GN_LEAF_STATUS_INITIALIZED; + descriptor->data = data; + return descriptor; + +} + +void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { + + char leaf_name[GN_LEAF_NAME_SIZE]; + gn_leaf_get_name(leaf_config, leaf_name); + ESP_LOGD(TAG, "[%s] - Initializing ..", leaf_name); + + gn_leaf_parameter_event_t evt; + + bool active = true; + gn_leaf_param_get_bool(leaf_config, GN_LEAF_INA219_PARAM_ACTIVE, &active); + + char ip[16]; + gn_leaf_param_get_string(leaf_config, GN_LEAF_INA219_PARAM_IP, ip, 16); + + double port = 0; + gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_PORT, &port); + + double sampling_ms = 0; + gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SAMPLING_MS, + &sampling_ms); + + double sda = 0; + gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SDA, &sda); + + double scl = 0; + gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SCL, &scl); + + //retrieves status descriptor from config + gn_leaf_ina219_data_t *data = + (gn_leaf_ina219_data_t*) gn_leaf_get_descriptor(leaf_config)->data; + + //setup driver + memset(&data->dev, 0, sizeof(ina219_t)); + + esp_err_t esp_ret; + esp_ret = ina219_init_desc(&data->dev, I2C_ADDR, I2C_PORT, (int) sda, + (int) scl); + ESP_LOGD(TAG, "ina219_init_desc (%d,%d,%d,%d): %s", I2C_ADDR, I2C_PORT, + (int )sda, (int )scl, esp_err_to_name(esp_ret)); + + esp_ret = ina219_init(&data->dev); + ESP_LOGD(TAG, "ina219_init: %s", esp_err_to_name(esp_ret)); + + esp_ret = ina219_configure(&data->dev, INA219_BUS_RANGE_16V, + INA219_GAIN_0_125, INA219_RES_12BIT_1S, INA219_RES_12BIT_1S, + INA219_MODE_CONT_SHUNT); + ESP_LOGD(TAG, "ina219_configure: %s", esp_err_to_name(esp_ret)); + + esp_ret = ina219_calibrate(&data->dev, 5.0, 0.1); // 5A max current, 0.1 Ohm shunt resistance + + ESP_LOGD(TAG, "ina219_calibrate: %s", esp_err_to_name(esp_ret)); + + float bus_voltage, shunt_voltage, current, power; + + int addr_family = 0; + int ip_protocol = 0; + + struct sockaddr_in dest_addr; + dest_addr.sin_addr.s_addr = inet_addr(ip); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons((uint16_t )port); + addr_family = AF_INET; + ip_protocol = IPPROTO_IP; + + int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); + if (sock < 0) { + ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); + } + ESP_LOGI(TAG, "Socket created, sending to %s:%d", ip, (int )port); + + char current_msg[80]; + char bus_voltage_msg[80]; + char shunt_voltage_msg[80]; + char load_voltage_msg[80]; + char power_msg[80]; + +//task cycle + while (true) { + + //ESP_LOGD(TAG, "task cycle.."); + + //check for messages and cycle every 100ms + if (xQueueReceive(gn_leaf_get_event_queue(leaf_config), &evt, + pdMS_TO_TICKS(sampling_ms)) == pdPASS) { + + ESP_LOGD(TAG, "%s - received message: %d", leaf_name, evt.id); + + //event arrived for this node + switch (evt.id) { + + //parameter change + case GN_LEAF_PARAM_CHANGE_REQUEST_EVENT: + + //parameter is update time + if (gn_leaf_event_mask_param(&evt, + data->gn_leaf_ina219_active_param) == 0) { + + //bool prev_active = active; + int _active = atoi(evt.data); + + //execute change + gn_leaf_param_write_bool(leaf_config, + GN_LEAF_INA219_PARAM_ACTIVE, + _active == 0 ? false : true); + + active = _active; + + } else if (gn_leaf_event_mask_param(&evt, + data->gn_leaf_ina219_ip_param) == 0) { + + //execute change + gn_leaf_param_write_string(leaf_config, + GN_LEAF_INA219_PARAM_IP, evt.data); + strncpy(ip, evt.data, 16); + + //restart messaging configuration + shutdown(sock, 0); + close(sock); + + dest_addr.sin_addr.s_addr = inet_addr(ip); + dest_addr.sin_port = htons((uint16_t )port); + sock = socket(addr_family, SOCK_DGRAM, ip_protocol); + if (sock < 0) { + ESP_LOGE(TAG, "Unable to create socket: errno %d", + errno); + } + ESP_LOGI(TAG, "Socket created, sending to %s:%d", ip, + (int )port); + + } else if (gn_leaf_event_mask_param(&evt, + data->gn_leaf_ina219_port_param) == 0) { + + port = atof(evt.data); + //execute change + gn_leaf_param_write_double(leaf_config, + GN_LEAF_INA219_PARAM_PORT, port); + + //restart messaging configuration + shutdown(sock, 0); + close(sock); + + dest_addr.sin_addr.s_addr = inet_addr(ip); + dest_addr.sin_port = htons((uint16_t )port); + sock = socket(addr_family, SOCK_DGRAM, ip_protocol); + if (sock < 0) { + ESP_LOGE(TAG, "Unable to create socket: errno %d", + errno); + } + ESP_LOGI(TAG, "Socket created, sending to %s:%d", ip, + (int )port); + + } else if (gn_leaf_event_mask_param(&evt, + data->gn_leaf_ina219_sampling_ms_param) == 0) { + + sampling_ms = atof(evt.data); + //execute change + gn_leaf_param_write_double(leaf_config, + GN_LEAF_INA219_PARAM_SAMPLING_MS, sampling_ms); + + } else if (gn_leaf_event_mask_param(&evt, + data->gn_leaf_ina219_sda_param) == 0) { + + sda = atof(evt.data); + //execute change + gn_leaf_param_write_double(leaf_config, + GN_LEAF_INA219_PARAM_SDA, sda); + + } else if (gn_leaf_event_mask_param(&evt, + data->gn_leaf_ina219_scl_param) == 0) { + + scl = atof(evt.data); + //execute change + gn_leaf_param_write_double(leaf_config, + GN_LEAF_INA219_PARAM_SCL, scl); + + } + + break; + + default: + break; + + } + + } + + if (active) { + + //retrieve sensor info + ESP_ERROR_CHECK(ina219_get_bus_voltage(&data->dev, &bus_voltage)); + ESP_ERROR_CHECK( + ina219_get_shunt_voltage(&data->dev, &shunt_voltage)); + ESP_ERROR_CHECK(ina219_get_current(&data->dev, ¤t)); + ESP_ERROR_CHECK(ina219_get_power(&data->dev, &power)); + + ESP_LOGD(TAG, + "VBUS: %.04f V, VSHUNT: %.04f mV, IBUS: %.04f mA, PBUS: %.04f mW\n", + bus_voltage, shunt_voltage * 1000, current * 1000, + power * 1000); + + int err; + + snprintf(current_msg, 80, "ina219,sensor=current value=%f", + current * 1000); + err = sendto(sock, current_msg, strlen(current_msg), 0, + (struct sockaddr*) &dest_addr, sizeof(dest_addr)); + if (err < 0) { + ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); + } + ESP_LOGD(TAG, "Message sent: %s to %s:%d", current_msg, ip, + (int )port); + + snprintf(bus_voltage_msg, 80, "ina219,sensor=voltage value=%f", + bus_voltage); + err = sendto(sock, bus_voltage_msg, strlen(bus_voltage_msg), 0, + (struct sockaddr*) &dest_addr, sizeof(dest_addr)); + if (err < 0) { + ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); + } + ESP_LOGD(TAG, "Message sent: %s to %s:%d", bus_voltage_msg, ip, + (int )port); + + snprintf(shunt_voltage_msg, 80, + "ina219,sensor=shunt_voltage value=%f", + shunt_voltage * 1000); + err = sendto(sock, shunt_voltage_msg, strlen(shunt_voltage_msg), 0, + (struct sockaddr*) &dest_addr, sizeof(dest_addr)); + if (err < 0) { + ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); + } + ESP_LOGD(TAG, "Message sent: %s to %s:%d", shunt_voltage_msg, ip, + (int )port); + + snprintf(load_voltage_msg, 80, "ina219,sensor=load_voltage value=%f", + power * 1000); + err = sendto(sock, load_voltage_msg, strlen(load_voltage_msg), 0, + (struct sockaddr*) &dest_addr, sizeof(dest_addr)); + if (err < 0) { + ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); + } + ESP_LOGD(TAG, "Message sent: %s to %s:%d", load_voltage_msg, ip, + (int )port); + + snprintf(power_msg, 80, "ina219,sensor=power value=%f", + power * 1000); + err = sendto(sock, power_msg, strlen(power_msg), 0, + (struct sockaddr*) &dest_addr, sizeof(dest_addr)); + if (err < 0) { + ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); + } + ESP_LOGD(TAG, "Message sent: %s to %s:%d", power_msg, ip, + (int )port); + + } + + //vTaskDelay(1000 / portTICK_PERIOD_MS); + } + +} + +#ifdef __cplusplus +} +#endif //__cplusplus diff --git a/components/grownode/leaves/gn_leaf_ina219.h b/components/grownode/leaves/gn_leaf_ina219.h new file mode 100644 index 00000000..8d196447 --- /dev/null +++ b/components/grownode/leaves/gn_leaf_ina219.h @@ -0,0 +1,41 @@ +// Copyright 2021 Nicola Muratori (nicola.muratori@gmail.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MAIN_GN_LEAF_INA219_H_ +#define MAIN_GN_LEAF_INA219_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "grownode.h" + +//define type +static const char GN_LEAF_INA219_TYPE[] = "ina219"; + +//parameters +static const char GN_LEAF_INA219_PARAM_ACTIVE[] = "active"; /*!< whether INA219 shall transmit data */ +static const char GN_LEAF_INA219_PARAM_IP[] = "ip"; /*!< ip address of the server */ +static const char GN_LEAF_INA219_PARAM_PORT[] = "port"; /*!< port of the server */ +static const char GN_LEAF_INA219_PARAM_SAMPLING_MS[] = "sampling"; /*!< sampling time in msec */ +static const char GN_LEAF_INA219_PARAM_SDA[] = "sda"; /*!< SDA PIN */ +static const char GN_LEAF_INA219_PARAM_SCL[] = "scl"; /*!< SCL PIN */ + +gn_leaf_descriptor_handle_t gn_leaf_ina219_config(gn_leaf_handle_t leaf_config); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif /* MAIN_GN_LEAF_INA219_H_ */ diff --git a/ext/esp-idf-lib b/ext/esp-idf-lib index 3147b587..3817d937 160000 --- a/ext/esp-idf-lib +++ b/ext/esp-idf-lib @@ -1 +1 @@ -Subproject commit 3147b5878bf9ae41f358ef542bf55c60a546ed53 +Subproject commit 3817d937f6cc41803133b030b687fb5c1732fee4 From 8d14f9331e50f71e35c57f84d1b63b277c2d22ef Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Fri, 11 Mar 2022 06:42:12 +0100 Subject: [PATCH 22/31] now leaves can have custom task priorities --- components/grownode/boards/gn_easypot1.c | 8 +++--- components/grownode/boards/gn_hydroboard1.c | 20 +++++++-------- components/grownode/boards/gn_hydroboard2.c | 26 ++++++++++---------- components/grownode/gn_commons.h | 6 +++-- components/grownode/gn_mqtt_protocol.c | 14 +++++++++++ components/grownode/grownode.c | 8 +++--- components/grownode/grownode.h | 2 +- components/grownode/grownode_intl.h | 1 + components/grownode/leaves/gn_gpio.c | 2 +- components/grownode/test/test_ds18b20.c | 6 ++--- components/grownode/test/test_pump.c | 6 ++--- components/grownode/test/test_pump_control.c | 8 +++--- components/grownode/test/test_relay.c | 6 ++--- 13 files changed, 66 insertions(+), 47 deletions(-) diff --git a/components/grownode/boards/gn_easypot1.c b/components/grownode/boards/gn_easypot1.c index bf785e1b..38acb2d3 100644 --- a/components/grownode/boards/gn_easypot1.c +++ b/components/grownode/boards/gn_easypot1.c @@ -111,7 +111,7 @@ void gn_configure_easypot1(gn_node_handle_t node) { //creates the moisture sensor moist = gn_leaf_create(node, "moist", gn_capacitive_moisture_sensor_config, - 4096); + 4096, GN_LEAF_TASK_PRIORITY); //set the channel gn_leaf_param_init_double(moist, GN_CMS_PARAM_ADC_CHANNEL, 5); //gpio33 //set update time @@ -120,7 +120,7 @@ void gn_configure_easypot1(gn_node_handle_t node) { gn_leaf_param_init_bool(moist, GN_CMS_PARAM_ACTIVE, true); //creates the temperature sensor - temp = gn_leaf_create(node, "temp", gn_ds18b20_config, 4096); + temp = gn_leaf_create(node, "temp", gn_ds18b20_config, 4096, GN_LEAF_TASK_PRIORITY); //set GPIO gn_leaf_param_init_double(temp, GN_DS18B20_PARAM_GPIO, 26); //set update time @@ -129,11 +129,11 @@ void gn_configure_easypot1(gn_node_handle_t node) { gn_leaf_param_init_bool(temp, GN_DS18B20_PARAM_ACTIVE, true); //create the temp led leaf - led_temp = gn_leaf_create(node, "led_temp", gn_led_config, 4096); + led_temp = gn_leaf_create(node, "led_temp", gn_led_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(led_temp, GN_LED_PARAM_GPIO, 14); //create the moisture led leaf - led_moist = gn_leaf_create(node, "led_moist", gn_led_config, 4096); + led_moist = gn_leaf_create(node, "led_moist", gn_led_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(led_moist, GN_LED_PARAM_GPIO, 27); //creates a timer that checks temperature and moisture, using esp_timer API diff --git a/components/grownode/boards/gn_hydroboard1.c b/components/grownode/boards/gn_hydroboard1.c index ac28b59a..9d17f53c 100644 --- a/components/grownode/boards/gn_hydroboard1.c +++ b/components/grownode/boards/gn_hydroboard1.c @@ -33,32 +33,32 @@ void gn_configure_hydroboard1(gn_node_handle_t node) { gn_leaf_handle_t lights1in = gn_leaf_create(node, "lights1in", - gn_gpio_config, 4096); + gn_gpio_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(lights1in, GN_GPIO_PARAM_GPIO, 25); gn_leaf_param_init_bool(lights1in, GN_GPIO_PARAM_TOGGLE, false); gn_leaf_handle_t lights2in = gn_leaf_create(node, "lights2in", - gn_gpio_config, 4096); + gn_gpio_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(lights2in, GN_GPIO_PARAM_GPIO, 5); gn_leaf_param_init_bool(lights2in, GN_GPIO_PARAM_TOGGLE, false); gn_leaf_handle_t plt_a = gn_leaf_create(node, "plt_a", - gn_gpio_config, 4096); + gn_gpio_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(plt_a, GN_GPIO_PARAM_GPIO, 23); gn_leaf_param_init_bool(plt_a, GN_GPIO_PARAM_TOGGLE, false); gn_leaf_handle_t plt_b = gn_leaf_create(node, "plt_b", - gn_gpio_config, 4096); + gn_gpio_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(plt_b, GN_GPIO_PARAM_GPIO, 17); gn_leaf_param_init_bool(plt_b, GN_GPIO_PARAM_TOGGLE, false); gn_leaf_handle_t waterpumpin = gn_leaf_create(node, "waterpumpin", - gn_gpio_config, 4096); + gn_gpio_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(waterpumpin, GN_GPIO_PARAM_GPIO, 19); gn_leaf_param_init_bool(waterpumpin, GN_GPIO_PARAM_TOGGLE, false); gn_leaf_handle_t waterlevelin = gn_leaf_create(node, "waterlevelin", - gn_capacitive_water_level_config, 4096); + gn_capacitive_water_level_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_bool(waterlevelin, GN_CWL_PARAM_ACTIVE, true); gn_leaf_param_init_double(waterlevelin, GN_CWL_PARAM_TOUCH_CHANNEL, 2); gn_leaf_param_init_double(waterlevelin, GN_CWL_PARAM_UPDATE_TIME_SEC, 10); @@ -66,7 +66,7 @@ void gn_configure_hydroboard1(gn_node_handle_t node) { gn_leaf_param_init_double(waterlevelin, GN_CWL_PARAM_MAX_LEVEL, 2048); gn_leaf_handle_t hcc_speed = gn_leaf_create(node, "hcc", - gn_pump_hs_config, 4096); + gn_pump_hs_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_bool(hcc_speed, GN_PUMP_HS_PARAM_CHANNEL, 0); gn_leaf_param_init_double(hcc_speed, GN_PUMP_HS_PARAM_GPIO_POWER, 18); gn_leaf_param_init_double(hcc_speed, GN_PUMP_HS_PARAM_POWER, 0); @@ -74,7 +74,7 @@ void gn_configure_hydroboard1(gn_node_handle_t node) { gn_leaf_param_init_bool(hcc_speed, GN_PUMP_HS_PARAM_TOGGLE, false); gn_leaf_handle_t fan_speed = gn_leaf_create(node, "fan", - gn_pump_hs_config, 4096); + gn_pump_hs_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_bool(fan_speed, GN_PUMP_HS_PARAM_CHANNEL, 1); gn_leaf_param_init_double(fan_speed, GN_PUMP_HS_PARAM_GPIO_POWER, 27); gn_leaf_param_init_double(fan_speed, GN_PUMP_HS_PARAM_POWER, 0); @@ -82,14 +82,14 @@ void gn_configure_hydroboard1(gn_node_handle_t node) { gn_leaf_param_init_bool(fan_speed, GN_PUMP_HS_PARAM_TOGGLE, false); gn_leaf_handle_t bme280 = gn_leaf_create(node, "bme280", - gn_bme280_config, 8192); + gn_bme280_config, 8192, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(bme280, GN_BME280_PARAM_SDA, 21); gn_leaf_param_init_double(bme280, GN_BME280_PARAM_SCL, 22); gn_leaf_param_init_bool(bme280, GN_BME280_PARAM_ACTIVE, true); gn_leaf_param_init_double(bme280, GN_BME280_PARAM_UPDATE_TIME_SEC, 10); gn_leaf_handle_t ds18b20 = gn_leaf_create(node, "ds18b20", - gn_ds18b20_config, 4096); + gn_ds18b20_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(ds18b20, GN_DS18B20_PARAM_GPIO, 4); gn_leaf_param_init_bool(ds18b20, GN_DS18B20_PARAM_ACTIVE, true); gn_leaf_param_init_double(ds18b20, GN_DS18B20_PARAM_UPDATE_TIME_SEC, 5); diff --git a/components/grownode/boards/gn_hydroboard2.c b/components/grownode/boards/gn_hydroboard2.c index 0f8e07b6..99c0b98f 100644 --- a/components/grownode/boards/gn_hydroboard2.c +++ b/components/grownode/boards/gn_hydroboard2.c @@ -59,59 +59,59 @@ void gn_configure_hydroboard2(gn_node_handle_t node) { const char *LIGHT_2 = "lig_2"; gn_leaf_handle_t lights1in = gn_leaf_create(node, LIGHT_1, - gn_gpio_config, 4096); + gn_gpio_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(lights1in, GN_GPIO_PARAM_GPIO, 25); gn_leaf_param_init_bool(lights1in, GN_GPIO_PARAM_INVERTED, true); gn_leaf_param_init_bool(lights1in, GN_GPIO_PARAM_TOGGLE, false); gn_leaf_handle_t lights2in = gn_leaf_create(node, LIGHT_2, - gn_gpio_config, 4096); + gn_gpio_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(lights2in, GN_GPIO_PARAM_GPIO, 33); gn_leaf_param_init_bool(lights2in, GN_GPIO_PARAM_INVERTED, true); gn_leaf_param_init_bool(lights2in, GN_GPIO_PARAM_TOGGLE, false); gn_leaf_handle_t plt_a = gn_leaf_create(node, PLT_HOT, - gn_gpio_config, 4096); + gn_gpio_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(plt_a, GN_GPIO_PARAM_GPIO, 5); gn_leaf_param_init_bool(plt_a, GN_GPIO_PARAM_INVERTED, true); gn_leaf_param_init_bool(plt_a, GN_GPIO_PARAM_TOGGLE, false); gn_leaf_handle_t plt_b = gn_leaf_create(node, PLT_COOL, - gn_gpio_config, 4096); + gn_gpio_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(plt_b, GN_GPIO_PARAM_GPIO, 23); gn_leaf_param_init_bool(plt_b, GN_GPIO_PARAM_INVERTED, true); gn_leaf_param_init_bool(plt_b, GN_GPIO_PARAM_TOGGLE, false); gn_leaf_handle_t wat_pump = gn_leaf_create(node, WAT_PUMP, - gn_leaf_pwm_config, 4096); + gn_leaf_pwm_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_bool(wat_pump, GN_LEAF_PWM_PARAM_TOGGLE, false); gn_leaf_param_init_double(wat_pump, GN_LEAF_PWM_PARAM_GPIO, 16); gn_leaf_param_init_double(wat_pump, GN_LEAF_PWM_PARAM_CHANNEL, 0); gn_leaf_param_init_double(wat_pump, GN_LEAF_PWM_PARAM_POWER, 0); gn_leaf_handle_t plt_pump = gn_leaf_create(node, PLT_PUMP, - gn_leaf_pwm_config, 4096); + gn_leaf_pwm_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_bool(plt_pump, GN_LEAF_PWM_PARAM_TOGGLE, false); gn_leaf_param_init_double(plt_pump, GN_LEAF_PWM_PARAM_GPIO, 19); gn_leaf_param_init_double(plt_pump, GN_LEAF_PWM_PARAM_CHANNEL, 1); gn_leaf_param_init_double(plt_pump, GN_LEAF_PWM_PARAM_POWER, 0); gn_leaf_handle_t plt_fan = gn_leaf_create(node, PLT_FAN, - gn_leaf_pwm_config, 4096); + gn_leaf_pwm_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_bool(plt_fan, GN_LEAF_PWM_PARAM_TOGGLE, false); gn_leaf_param_init_double(plt_fan, GN_LEAF_PWM_PARAM_GPIO, 18); gn_leaf_param_init_double(plt_fan, GN_LEAF_PWM_PARAM_CHANNEL, 2); gn_leaf_param_init_double(plt_fan, GN_LEAF_PWM_PARAM_POWER, 0); gn_leaf_handle_t env_fan = gn_leaf_create(node, ENV_FAN, - gn_leaf_pwm_config, 4096); + gn_leaf_pwm_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_bool(env_fan, GN_LEAF_PWM_PARAM_TOGGLE, false); gn_leaf_param_init_double(env_fan, GN_LEAF_PWM_PARAM_GPIO, 27); gn_leaf_param_init_double(env_fan, GN_LEAF_PWM_PARAM_CHANNEL, 3); gn_leaf_param_init_double(env_fan, GN_LEAF_PWM_PARAM_POWER, 0); gn_leaf_handle_t wat_lev = gn_leaf_create(node, WAT_LEV, - gn_capacitive_water_level_config, 4096); + gn_capacitive_water_level_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_bool(wat_lev, GN_CWL_PARAM_ACTIVE, true); gn_leaf_param_init_double(wat_lev, GN_CWL_PARAM_TOUCH_CHANNEL, 1); gn_leaf_param_init_double(wat_lev, GN_CWL_PARAM_UPDATE_TIME_SEC, 10); @@ -119,20 +119,20 @@ void gn_configure_hydroboard2(gn_node_handle_t node) { gn_leaf_param_init_double(wat_lev, GN_CWL_PARAM_MAX_LEVEL, 2048); gn_leaf_handle_t env_thp = gn_leaf_create(node, BME280, - gn_bme280_config, 8192); + gn_bme280_config, 8192, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(env_thp, GN_BME280_PARAM_SDA, 21); gn_leaf_param_init_double(env_thp, GN_BME280_PARAM_SCL, 22); gn_leaf_param_init_bool(env_thp, GN_BME280_PARAM_ACTIVE, true); gn_leaf_param_init_double(env_thp, GN_BME280_PARAM_UPDATE_TIME_SEC, 10); gn_leaf_handle_t temps = gn_leaf_create(node, DS18B20, - gn_ds18b20_config, 4096); + gn_ds18b20_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(temps, GN_DS18B20_PARAM_GPIO, 4); gn_leaf_param_init_bool(temps, GN_DS18B20_PARAM_ACTIVE, true); gn_leaf_param_init_double(temps, GN_DS18B20_PARAM_UPDATE_TIME_SEC, 5); gn_leaf_handle_t watering_control = gn_leaf_create(node, - "watering_control", gn_hb2_watering_control_config, 4096); + "watering_control", gn_hb2_watering_control_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(watering_control, GN_HYDROBOARD2_WAT_CTR_PARAM_WATERING_INTERVAL_SEC, 60 * 1); gn_leaf_param_init_double(watering_control, @@ -155,7 +155,7 @@ void gn_configure_hydroboard2(gn_node_handle_t node) { gn_leaf_param_init_string(watering_control, GN_HYDROBOARD2_WAT_CTR_PARAM_LEAF_LIGHT_2, LIGHT_2); gn_leaf_handle_t led = gn_leaf_create(node, "led", - gn_leaf_status_led_config, 4096); + gn_leaf_status_led_config, 4096, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_double(led, GN_LEAF_STATUS_LED_PARAM_GPIO, 32); } diff --git a/components/grownode/gn_commons.h b/components/grownode/gn_commons.h index 990fd4f4..7c4dc7cc 100755 --- a/components/grownode/gn_commons.h +++ b/components/grownode/gn_commons.h @@ -24,8 +24,10 @@ extern "C" { #endif -#define GN_NODE_NAME_SIZE 32 -#define GN_LEAF_NAME_SIZE 32 +#define GN_LEAF_TASK_PRIORITY 1 + +#define GN_NODE_NAME_SIZE 16 +#define GN_LEAF_NAME_SIZE 16 #define GN_LEAF_PARAM_NAME_SIZE 32 #define GN_LEAF_DATA_SIZE 512 #define GN_NODE_DATA_SIZE 512 diff --git a/components/grownode/gn_mqtt_protocol.c b/components/grownode/gn_mqtt_protocol.c index d788dbab..7221f62d 100755 --- a/components/grownode/gn_mqtt_protocol.c +++ b/components/grownode/gn_mqtt_protocol.c @@ -88,6 +88,8 @@ void _gn_mqtt_build_leaf_command_topic(gn_leaf_handle_t _leaf_config, char *buf) strncpy(buf, config->config_init_params->server_base_topic, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); + strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); + strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { strncat(buf, _gn_mqtt_build_node_name(config), 12); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); @@ -112,6 +114,8 @@ void _gn_mqtt_build_leaf_parameter_command_topic( strncpy(buf, config->config_init_params->server_base_topic, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); + strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); + strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { strncat(buf, _gn_mqtt_build_node_name(config), 12); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); @@ -138,6 +142,8 @@ void _gn_mqtt_build_leaf_parameter_status_topic(gn_leaf_handle_t _leaf_config, strncpy(buf, config->config_init_params->server_base_topic, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); + strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); + strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { strncat(buf, _gn_mqtt_build_node_name(config), 12); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); @@ -163,6 +169,8 @@ void _gn_mqtt_build_leaf_status_topic(gn_leaf_handle_t _leaf_config, char *buf) strncpy(buf, config->config_init_params->server_base_topic, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); + strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); + strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { strncat(buf, _gn_mqtt_build_node_name(config), 12); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); @@ -178,6 +186,8 @@ void _gn_mqtt_build_status_topic(gn_config_handle_intl_t config, char *buf) { strncpy(buf, config->config_init_params->server_base_topic, _GN_MQTT_MAX_TOPIC_LENGTH); + //strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); + //strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { strncat(buf, _gn_mqtt_build_node_name(config), 12); @@ -193,6 +203,8 @@ void _gn_mqtt_build_log_topic(gn_config_handle_intl_t config, char *buf) { strncpy(buf, config->config_init_params->server_base_topic, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); + //strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); + //strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { strncat(buf, _gn_mqtt_build_node_name(config), 12); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); @@ -205,6 +217,8 @@ void _gn_mqtt_build_log_topic(gn_config_handle_intl_t config, char *buf) { void _gn_mqtt_build_command_topic(gn_config_handle_intl_t config, char *buf) { strncpy(buf, config->config_init_params->server_base_topic, _GN_MQTT_MAX_TOPIC_LENGTH); + //strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); + //strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { strncat(buf, _gn_mqtt_build_node_name(config), 12); diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index e1274fc3..2fdcf0bc 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -108,7 +108,7 @@ gn_err_t _gn_leaf_start(gn_leaf_config_handle_intl_t leaf_config) { TaskHandle_t task_handle; if (xTaskCreate((void*) leaf_config->leaf_descriptor->callback, - leaf_config->name, leaf_config->task_size, leaf_config, 1, //configMAX_PRIORITIES - 1, + leaf_config->name, leaf_config->task_size, leaf_config, GN_LEAF_TASK_PRIORITY, //configMAX_PRIORITIES - 1, &task_handle) != pdPASS) { ESP_LOGE(TAG, "failed to create lef task for %s", leaf_config->name); goto fail; @@ -1045,14 +1045,15 @@ gn_err_t gn_node_get_name(gn_node_handle_t node_config, char *name) { * @param name the name of the leaf to be created * @param callback the callback to be called to configure the leaf * @param task callback function of the leaf task - * @param task_size the size of the task to be memory allocated + * @param task_size the size of the task to be memory allocated (see freertos documentation) + * @param priority the task priority (see freertos documentation) * * @return an handle to the leaf config * @return NULL if the handle cannot be created * */ gn_leaf_handle_t gn_leaf_create(gn_node_handle_t node_config, const char *name, - gn_leaf_config_callback callback, size_t task_size) { //, gn_leaf_display_task_t display_task) { + gn_leaf_config_callback callback, size_t task_size, UBaseType_t priority) { //, gn_leaf_display_task_t display_task) { gn_node_handle_intl_t node_cfg = (gn_node_handle_intl_t) node_config; @@ -1069,6 +1070,7 @@ gn_leaf_handle_t gn_leaf_create(gn_node_handle_t node_config, const char *name, l_c->node_config = node_cfg; //l_c->task_cb = task; l_c->task_size = task_size; + l_c->priority = priority; l_c->leaf_context = gn_leaf_context_create(); l_c->display_container = NULL; //l_c->display_task = display_task; diff --git a/components/grownode/grownode.h b/components/grownode/grownode.h index 9526776b..86778d62 100755 --- a/components/grownode/grownode.h +++ b/components/grownode/grownode.h @@ -62,7 +62,7 @@ gn_err_t gn_reboot(); gn_leaf_handle_t gn_leaf_create(gn_node_handle_t node_config, const char *name, gn_leaf_config_callback callback, - size_t task_size); + size_t task_size, UBaseType_t priority); gn_leaf_descriptor_handle_t gn_leaf_get_descriptor( gn_leaf_handle_t leaf_config); diff --git a/components/grownode/grownode_intl.h b/components/grownode/grownode_intl.h index 3d2af7a7..757a228f 100755 --- a/components/grownode/grownode_intl.h +++ b/components/grownode/grownode_intl.h @@ -71,6 +71,7 @@ struct gn_leaf_config_t { gn_leaf_descriptor_handle_t leaf_descriptor; char name[GN_LEAF_NAME_SIZE]; size_t task_size; + UBaseType_t priority; gn_node_handle_intl_t node_config; //gn_leaf_display_task_t display_task; //gn_leaf_config_handle_t next; diff --git a/components/grownode/leaves/gn_gpio.c b/components/grownode/leaves/gn_gpio.c index b9243157..3569bd75 100644 --- a/components/grownode/leaves/gn_gpio.c +++ b/components/grownode/leaves/gn_gpio.c @@ -73,7 +73,7 @@ gn_leaf_handle_t gn_gpio_fastcreate(gn_node_handle_t node, //creates the blink leave gn_leaf_handle_t leaf = gn_leaf_create(node, leaf_name, gn_gpio_config, - 4096); + 4096, GN_LEAF_TASK_PRIORITY); if (leaf == NULL) { ESP_LOGE(TAG, "gn_gpio_fastcreate - cannot create leaf %s", leaf_name); diff --git a/components/grownode/test/test_ds18b20.c b/components/grownode/test/test_ds18b20.c index a389a70d..d341b119 100644 --- a/components/grownode/test/test_ds18b20.c +++ b/components/grownode/test/test_ds18b20.c @@ -50,7 +50,7 @@ TEST_CASE("gn_init_add_ds18b20", "[ds18b20]") { gn_node_get_name(node_config, node_name); TEST_ASSERT_EQUAL_STRING("node", node_name); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 0); - ds18b20_config = gn_leaf_create(node_config, "ds18b20", gn_ds18b20_config, 4096); + ds18b20_config = gn_leaf_create(node_config, "ds18b20", gn_ds18b20_config, 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 1); esp_err_t ret = gn_node_start(node_config); TEST_ASSERT_EQUAL(ret, ESP_OK); @@ -60,7 +60,7 @@ TEST_CASE("gn_init_add_ds18b20", "[ds18b20]") { TEST_CASE("gn_leaf_create ds18b20", "[ds18b20]") { size_t oldsize = gn_node_get_size(node_config); - ds18b20_config = gn_leaf_create(node_config, "ds18b20", gn_ds18b20_config, 4096); + ds18b20_config = gn_leaf_create(node_config, "ds18b20", gn_ds18b20_config, 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), oldsize+1); TEST_ASSERT(ds18b20_config != NULL); @@ -78,7 +78,7 @@ TEST_CASE("gn_ds18b20_mqtt_stress_test", "[ds18b20]") { gn_node_get_name(node_config, node_name); TEST_ASSERT_EQUAL_STRING("node", node_name); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 0); - ds18b20_config = gn_leaf_create(node_config, "ds18b20", gn_ds18b20_config, 4096); + ds18b20_config = gn_leaf_create(node_config, "ds18b20", gn_ds18b20_config, 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 1); esp_err_t ret = gn_node_start(node_config); TEST_ASSERT_EQUAL(ret, ESP_OK); diff --git a/components/grownode/test/test_pump.c b/components/grownode/test/test_pump.c index 77e746d4..42e3722c 100755 --- a/components/grownode/test/test_pump.c +++ b/components/grownode/test/test_pump.c @@ -52,7 +52,7 @@ TEST_CASE("gn_init_add_pump", "[pump]") { gn_node_get_name(node_config, node_name); TEST_ASSERT_EQUAL_STRING("node", node_name); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 0); - pump_config = gn_leaf_create(node_config, "pump", gn_pump_config, 4096); + pump_config = gn_leaf_create(node_config, "pump", gn_pump_config, 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 1); esp_err_t ret = gn_node_start(node_config); TEST_ASSERT_EQUAL(ret, ESP_OK); @@ -62,7 +62,7 @@ TEST_CASE("gn_init_add_pump", "[pump]") { TEST_CASE("gn_leaf_create pump", "[pump]") { size_t oldsize = gn_node_get_size(node_config); - pump_config = gn_leaf_create(node_config, "pump", gn_pump_config, 4096); + pump_config = gn_leaf_create(node_config, "pump", gn_pump_config, 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), oldsize + 1); TEST_ASSERT(pump_config != NULL); @@ -242,7 +242,7 @@ TEST_CASE("gn_pump_mqtt_stress_test", "[pump]") { gn_node_get_name(node_config, node_name); TEST_ASSERT_EQUAL_STRING("node", node_name); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 0); - pump_config = gn_leaf_create(node_config, "pump", gn_pump_config, 4096); + pump_config = gn_leaf_create(node_config, "pump", gn_pump_config, 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 1); esp_err_t ret = gn_node_start(node_config); TEST_ASSERT_EQUAL(ret, ESP_OK); diff --git a/components/grownode/test/test_pump_control.c b/components/grownode/test/test_pump_control.c index 54da71b9..fdffe838 100644 --- a/components/grownode/test/test_pump_control.c +++ b/components/grownode/test/test_pump_control.c @@ -59,19 +59,19 @@ TEST_CASE("gn_pump_control_stress_test", "[pump_control]") { TEST_ASSERT_EQUAL_STRING("node", node_name); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 0); - pump_config = gn_leaf_create(node_config, "pump", gn_pump_config, 4096); + pump_config = gn_leaf_create(node_config, "pump", gn_pump_config, 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 1); ds18b20_config = gn_leaf_create(node_config, "ds18b20", gn_ds18b20_config, - 4096); + 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 2); ds18b20_config = gn_leaf_create(node_config, "cwl", - gn_capacitive_water_level_config, 8192); + gn_capacitive_water_level_config, 8192, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 3); pump_control_config = gn_leaf_create(node_config, "pump_control", - gn_pump_control_config, 4096); + gn_pump_control_config, 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 4); esp_err_t ret = gn_node_start(node_config); diff --git a/components/grownode/test/test_relay.c b/components/grownode/test/test_relay.c index c8a93d37..ce4607d9 100644 --- a/components/grownode/test/test_relay.c +++ b/components/grownode/test/test_relay.c @@ -58,7 +58,7 @@ TEST_CASE("gn_init_add_relay", "[relay]") { gn_node_get_name(node_config, node_name); TEST_ASSERT_EQUAL_STRING("node", node_name); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 0); - relay_config = gn_leaf_create(node_config, "relay", gn_gpio_config, 4096); + relay_config = gn_leaf_create(node_config, "relay", gn_gpio_config, 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 1); esp_err_t ret = gn_node_start(node_config); TEST_ASSERT_EQUAL(ret, ESP_OK); @@ -68,7 +68,7 @@ TEST_CASE("gn_init_add_relay", "[relay]") { TEST_CASE("gn_leaf_create relay", "[relay]") { size_t oldsize = gn_node_get_size(node_config); - relay_config = gn_leaf_create(node_config, "relay", gn_gpio_config, 4096); + relay_config = gn_leaf_create(node_config, "relay", gn_gpio_config, 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), oldsize+1); TEST_ASSERT(relay_config != NULL); @@ -150,7 +150,7 @@ TEST_CASE("gn_relay_mqtt_stress_test", "[relay]") { gn_node_get_name(node_config, node_name); TEST_ASSERT_EQUAL_STRING("node", node_name); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 0); - relay_config = gn_leaf_create(node_config, "relay", gn_gpio_config, 4096); + relay_config = gn_leaf_create(node_config, "relay", gn_gpio_config, 4096, 1); TEST_ASSERT_EQUAL(gn_node_get_size(node_config), 1); esp_err_t ret = gn_node_start(node_config); TEST_ASSERT_EQUAL(ret, ESP_OK); From 4ec3decc3c08ddd3d96ce431d3f297c75cc0a19e Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Fri, 11 Mar 2022 08:34:17 +0100 Subject: [PATCH 23/31] character array size check improved --- components/grownode/gn_commons.h | 1 + components/grownode/gn_mqtt_protocol.c | 87 ++++++++++++-------------- components/grownode/gn_mqtt_protocol.h | 2 + components/grownode/grownode.c | 28 ++++----- components/grownode/leaves/gn_gpio.c | 6 +- components/grownode/test/test_pump.c | 24 +++---- components/grownode/test/test_relay.c | 12 ++-- 7 files changed, 78 insertions(+), 82 deletions(-) diff --git a/components/grownode/gn_commons.h b/components/grownode/gn_commons.h index 7c4dc7cc..70544072 100755 --- a/components/grownode/gn_commons.h +++ b/components/grownode/gn_commons.h @@ -29,6 +29,7 @@ extern "C" { #define GN_NODE_NAME_SIZE 16 #define GN_LEAF_NAME_SIZE 16 #define GN_LEAF_PARAM_NAME_SIZE 32 +#define GN_LEAF_PARAM_VAL_SIZE 64 #define GN_LEAF_DATA_SIZE 512 #define GN_NODE_DATA_SIZE 512 #define GN_LEAF_DESC_TYPE_SIZE 32 diff --git a/components/grownode/gn_mqtt_protocol.c b/components/grownode/gn_mqtt_protocol.c index 7221f62d..a5b9df6a 100755 --- a/components/grownode/gn_mqtt_protocol.c +++ b/components/grownode/gn_mqtt_protocol.c @@ -91,7 +91,7 @@ void _gn_mqtt_build_leaf_command_topic(gn_leaf_handle_t _leaf_config, char *buf) strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { - strncat(buf, _gn_mqtt_build_node_name(config), 12); + strncat(buf, _gn_mqtt_build_node_name(config), 13); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); } strncat(buf, leaf_config->name, _GN_MQTT_MAX_TOPIC_LENGTH); @@ -117,7 +117,7 @@ void _gn_mqtt_build_leaf_parameter_command_topic( strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { - strncat(buf, _gn_mqtt_build_node_name(config), 12); + strncat(buf, _gn_mqtt_build_node_name(config), GN_MQTT_NODE_NAME_SIZE); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); } strncat(buf, leaf_config->name, _GN_MQTT_MAX_TOPIC_LENGTH); @@ -145,7 +145,7 @@ void _gn_mqtt_build_leaf_parameter_status_topic(gn_leaf_handle_t _leaf_config, strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { - strncat(buf, _gn_mqtt_build_node_name(config), 12); + strncat(buf, _gn_mqtt_build_node_name(config), GN_MQTT_NODE_NAME_SIZE); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); } strncat(buf, leaf_config->name, _GN_MQTT_MAX_TOPIC_LENGTH); @@ -172,7 +172,7 @@ void _gn_mqtt_build_leaf_status_topic(gn_leaf_handle_t _leaf_config, char *buf) strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { - strncat(buf, _gn_mqtt_build_node_name(config), 12); + strncat(buf, _gn_mqtt_build_node_name(config), GN_MQTT_NODE_NAME_SIZE); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); } strncat(buf, leaf_config->name, _GN_MQTT_MAX_TOPIC_LENGTH); @@ -190,7 +190,7 @@ void _gn_mqtt_build_status_topic(gn_config_handle_intl_t config, char *buf) { //strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { - strncat(buf, _gn_mqtt_build_node_name(config), 12); + strncat(buf, _gn_mqtt_build_node_name(config), GN_MQTT_NODE_NAME_SIZE); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); } strncat(buf, _GN_MQTT_STATUS_MESS, _GN_MQTT_MAX_TOPIC_LENGTH); @@ -206,7 +206,7 @@ void _gn_mqtt_build_log_topic(gn_config_handle_intl_t config, char *buf) { //strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); //strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { - strncat(buf, _gn_mqtt_build_node_name(config), 12); + strncat(buf, _gn_mqtt_build_node_name(config), GN_MQTT_NODE_NAME_SIZE); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); } strncat(buf, _GN_MQTT_LOG_MESS, _GN_MQTT_MAX_TOPIC_LENGTH); @@ -221,7 +221,7 @@ void _gn_mqtt_build_command_topic(gn_config_handle_intl_t config, char *buf) { //strncat(buf, config->node_handle->name, _GN_MQTT_MAX_TOPIC_LENGTH); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); if (config->config_init_params->server_board_id_topic) { - strncat(buf, _gn_mqtt_build_node_name(config), 12); + strncat(buf, _gn_mqtt_build_node_name(config), GN_MQTT_NODE_NAME_SIZE); strncat(buf, "/", _GN_MQTT_MAX_TOPIC_LENGTH); } strncat(buf, _GN_MQTT_COMMAND_MESS, _GN_MQTT_MAX_TOPIC_LENGTH); @@ -329,14 +329,14 @@ gn_err_t gn_mqtt_subscribe_leaf(gn_leaf_handle_t _leaf_config) { cJSON *root = cJSON_CreateObject(); //build parameter ID - char d_param_id[_GN_MQTT_MAX_TOPIC_LENGTH + 1] = { 0 }; + char d_param_id[_GN_MQTT_MAX_TOPIC_LENGTH] = { 0 }; strncpy(d_param_id, _node_config->config->deviceName, _GN_MQTT_MAX_TOPIC_LENGTH); - strncat(d_param_id, "-", _GN_MQTT_MAX_TOPIC_LENGTH); + strncat(d_param_id, "-", _GN_MQTT_MAX_TOPIC_LENGTH - 1); strncat(d_param_id, leaf_config->name, - _GN_MQTT_MAX_TOPIC_LENGTH); - strncat(d_param_id, "-", _GN_MQTT_MAX_TOPIC_LENGTH); - strncat(d_param_id, _param->name, _GN_MQTT_MAX_TOPIC_LENGTH); + _GN_MQTT_MAX_TOPIC_LENGTH - 1); + strncat(d_param_id, "-", _GN_MQTT_MAX_TOPIC_LENGTH - 1); + strncat(d_param_id, _param->name, _GN_MQTT_MAX_TOPIC_LENGTH - 1); //build payload strncpy(_d_msg_topic, @@ -605,8 +605,6 @@ gn_err_t gn_mqtt_send_leaf_param(gn_leaf_param_handle_t _param) { _gn_mqtt_build_leaf_parameter_status_topic(param->leaf_config, param->name, _topic); - size_t len = 0; - switch (param->param_val->t) { case GN_VAL_TYPE_BOOLEAN: if (param->param_val->v.b) { @@ -616,11 +614,8 @@ gn_err_t gn_mqtt_send_leaf_param(gn_leaf_param_handle_t _param) { } break; case GN_VAL_TYPE_STRING: - len = strlen(param->param_val->v.s); - strncpy(buf, param->param_val->v.s, - len > _GN_MQTT_MAX_PAYLOAD_LENGTH ? - _GN_MQTT_MAX_PAYLOAD_LENGTH : - len); + //size_t len = strlen(param->param_val->v.s); + strncpy(buf, param->param_val->v.s, _GN_MQTT_MAX_PAYLOAD_LENGTH); break; case GN_VAL_TYPE_DOUBLE: snprintf(buf, 31, "%f", param->param_val->v.d); @@ -1198,8 +1193,7 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, switch ((esp_mqtt_event_id_t) event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGD(TAG, "MQTT_EVENT_CONNECTED"); - xEventGroupSetBits(_gn_event_group_mqtt, - _GN_MQTT_CONNECTED_EVENT_BIT); + xEventGroupSetBits(_gn_event_group_mqtt, _GN_MQTT_CONNECTED_EVENT_BIT); /* msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, @@ -1218,8 +1212,7 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, break; case MQTT_EVENT_DISCONNECTED: ESP_LOGD(TAG, "MQTT_EVENT_DISCONNECTED"); - xEventGroupSetBits(_gn_event_group_mqtt, - _GN_MQTT_DISCONNECT_EVENT_BIT); + xEventGroupSetBits(_gn_event_group_mqtt, _GN_MQTT_DISCONNECT_EVENT_BIT); _gn_mqtt_on_disconnected(config); break; @@ -1250,7 +1243,8 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, if (event->retain) { //clear retain message - esp_mqtt_client_publish(config->mqtt_client, _gn_cmd_topic, "", 0, 1, true); + esp_mqtt_client_publish(config->mqtt_client, _gn_cmd_topic, + "", 0, 1, true); } esp_event_post_to(config->event_loop, GN_BASE_EVENT, @@ -1262,7 +1256,8 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, if (event->retain) { //clear retain message - esp_mqtt_client_publish(config->mqtt_client, _gn_cmd_topic, "", 0, 1, true); + esp_mqtt_client_publish(config->mqtt_client, _gn_cmd_topic, + "", 0, 1, true); } esp_event_post_to(config->event_loop, GN_BASE_EVENT, @@ -1274,7 +1269,8 @@ void _gn_mqtt_event_handler(void *handler_args, esp_event_base_t base, if (event->retain) { //clear retain message - esp_mqtt_client_publish(config->mqtt_client, _gn_cmd_topic, "", 0, 1, true); + esp_mqtt_client_publish(config->mqtt_client, _gn_cmd_topic, + "", 0, 1, true); } esp_event_post_to(config->event_loop, GN_BASE_EVENT, @@ -1520,17 +1516,17 @@ gn_err_t gn_mqtt_stop(gn_config_handle_t config) { } /* - ESP_LOGI(TAG, "gn_mqtt_stop waiting to disconnect"); + ESP_LOGI(TAG, "gn_mqtt_stop waiting to disconnect"); - xEventGroupWaitBits(_gn_event_group_mqtt, _GN_MQTT_DISCONNECT_EVENT_BIT, - pdFALSE, - pdFALSE, portMAX_DELAY); + xEventGroupWaitBits(_gn_event_group_mqtt, _GN_MQTT_DISCONNECT_EVENT_BIT, + pdFALSE, + pdFALSE, portMAX_DELAY); - xEventGroupClearBits(_gn_event_group_mqtt, _GN_MQTT_DISCONNECT_EVENT_BIT); - xEventGroupClearBits(_gn_event_group_mqtt, _GN_MQTT_CONNECTED_EVENT_BIT); + xEventGroupClearBits(_gn_event_group_mqtt, _GN_MQTT_DISCONNECT_EVENT_BIT); + xEventGroupClearBits(_gn_event_group_mqtt, _GN_MQTT_CONNECTED_EVENT_BIT); - ESP_LOGI(TAG, "gn_mqtt_stop returning"); - */ + ESP_LOGI(TAG, "gn_mqtt_stop returning"); + */ return GN_RET_OK; @@ -1570,16 +1566,16 @@ gn_err_t gn_mqtt_disconnect(gn_config_handle_t config) { } /* - ESP_LOGI(TAG, "gn_mqtt_disconnect waiting to disconnect"); + ESP_LOGI(TAG, "gn_mqtt_disconnect waiting to disconnect"); - EventBits_t uxBits; - uxBits = xEventGroupWaitBits(_gn_event_group_mqtt, - _GN_MQTT_DISCONNECT_EVENT_BIT, - pdFALSE, - pdFALSE, portMAX_DELAY); + EventBits_t uxBits; + uxBits = xEventGroupWaitBits(_gn_event_group_mqtt, + _GN_MQTT_DISCONNECT_EVENT_BIT, + pdFALSE, + pdFALSE, portMAX_DELAY); - ESP_LOGI(TAG, "gn_mqtt_disconnect returning"); - */ + ESP_LOGI(TAG, "gn_mqtt_disconnect returning"); + */ return GN_RET_OK; @@ -1620,10 +1616,9 @@ gn_err_t gn_mqtt_reconnect(gn_config_handle_t config) { ESP_LOGD(TAG, "gn_mqtt_reconnect waiting to connect"); - xEventGroupWaitBits(_gn_event_group_mqtt, - _GN_MQTT_CONNECTED_EVENT_BIT, - pdFALSE, - pdFALSE, portMAX_DELAY); + xEventGroupWaitBits(_gn_event_group_mqtt, _GN_MQTT_CONNECTED_EVENT_BIT, + pdFALSE, + pdFALSE, portMAX_DELAY); ESP_LOGD(TAG, "gn_mqtt_reconnect returning"); diff --git a/components/grownode/gn_mqtt_protocol.h b/components/grownode/gn_mqtt_protocol.h index cdca3c8b..6c511d6c 100755 --- a/components/grownode/gn_mqtt_protocol.h +++ b/components/grownode/gn_mqtt_protocol.h @@ -33,6 +33,8 @@ extern "C" { #define _GN_MQTT_PAYLOAD_OTA "OTA" #define _GN_MQTT_PAYLOAD_RBT "RBT" +#define GN_MQTT_NODE_NAME_SIZE 13 + #define _GN_MQTT_DEFAULT_QOS 0 gn_err_t gn_mqtt_subscribe_leaf(gn_leaf_handle_t leaf_config); diff --git a/components/grownode/grownode.c b/components/grownode/grownode.c index 2fdcf0bc..0462f9d7 100755 --- a/components/grownode/grownode.c +++ b/components/grownode/grownode.c @@ -683,7 +683,7 @@ gn_node_handle_t gn_node_create(gn_config_handle_t config, const char *name) { gn_node_handle_intl_t n_c = _gn_node_config_create(); - strncpy(n_c->name, name, GN_NODE_NAME_SIZE); + strncpy(n_c->name, name, GN_NODE_NAME_SIZE-1); //n_c->event_loop = config->event_loop; n_c->config = config; @@ -1019,7 +1019,7 @@ gn_leaf_config_handle_intl_t _gn_leaf_config_create() { * @brief gets the name of the node referenced by the handle * * @param node_config the handle to be queried - * @param name the pointer where the name will be set. set lenght to GN_LEAF_NAME_SIZE + * @param name the pointer where the name will be set. set length to GN_NODE_NAME_SIZE * * @return GN_RET_ERR_INVALID_ARG if the handle is not valid * @return GN_RET_OK if everything OK @@ -1029,8 +1029,7 @@ gn_err_t gn_node_get_name(gn_node_handle_t node_config, char *name) { if (!node_config) return GN_RET_ERR_INVALID_ARG; - strncpy(name, ((gn_node_handle_intl_t) node_config)->name, - strlen(((gn_node_handle_intl_t) node_config)->name) + 1); + strcpy(name, ((gn_node_handle_intl_t) node_config)->name); return GN_RET_OK; @@ -1066,7 +1065,7 @@ gn_leaf_handle_t gn_leaf_create(gn_node_handle_t node_config, const char *name, gn_leaf_config_handle_intl_t l_c = _gn_leaf_config_create(); gn_node_handle_intl_t n_c = node_cfg; - strncpy(l_c->name, name, GN_LEAF_NAME_SIZE); + strncpy(l_c->name, name, GN_LEAF_NAME_SIZE-1); l_c->node_config = node_cfg; //l_c->task_cb = task; l_c->task_size = task_size; @@ -1123,7 +1122,7 @@ gn_err_t _gn_leaf_destroy(gn_leaf_handle_t leaf_config) { * @brief gets the name of the leaf referenced by the handle * * @param leaf_config the handle to be queried - * @param name the pointer where the name will be set. set lenght to GN_LEAF_NAME_SIZE + * @param name the pointer where the name will be set. set length to GN_LEAF_NAME_SIZE * * @return GN_RET_ERR_INVALID_ARG if the handle is not valid * @return GN_RET_OK if everything OK @@ -1133,8 +1132,7 @@ gn_err_t gn_leaf_get_name(gn_leaf_handle_t leaf_config, char *name) { if (!leaf_config) return GN_RET_ERR_INVALID_ARG; - strncpy(name, ((gn_leaf_config_handle_intl_t) leaf_config)->name, - strlen(((gn_leaf_config_handle_intl_t) leaf_config)->name) + 1); + strcpy(name, ((gn_leaf_config_handle_intl_t) leaf_config)->name); return GN_RET_OK; @@ -1497,10 +1495,10 @@ gn_err_t gn_leaf_param_init_string(const gn_leaf_handle_t leaf_config, validate); if (val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_GENERIC && val_ret != GN_LEAF_PARAM_VALIDATOR_ERROR_NOT_ALLOWED) { - strncpy(_val->v.s, *validate, strlen(*validate)); + strcpy(_val->v.s, *validate); ESP_LOGD(TAG, "processing validator - result: %d", (int ) val_ret); } else { - strncpy(_val->v.s, val, strlen(val)); + strcpy(_val->v.s, val); } } @@ -1519,7 +1517,7 @@ gn_err_t gn_leaf_param_init_string(const gn_leaf_handle_t leaf_config, strcpy(evt.param_name, _param->name); evt.id = GN_LEAF_PARAM_INITIALIZED_EVENT; //evt.data = calloc((strlen(_param->param_val->v.s) + 1) * sizeof(char)); - strncpy(evt.data, _param->param_val->v.s, GN_LEAF_DATA_SIZE); + strncpy(evt.data, _param->param_val->v.s, GN_LEAF_DATA_SIZE-1); esp_err_t ret = esp_event_post_to( _leaf_config->node_config->config->event_loop, GN_BASE_EVENT, @@ -1584,7 +1582,7 @@ gn_err_t gn_leaf_param_write_string(const gn_leaf_handle_t leaf_config, sizeof(char) * (strlen(*validate) + 1)); memset(_param->param_val->v.s, 0, sizeof(char) * (strlen(*validate) + 1)); - strncpy(_param->param_val->v.s, *validate, strlen(*validate)); + strcpy(_param->param_val->v.s, *validate); } else { ESP_LOGD(TAG, "gn_leaf_param_write_string: validation error. code %d", @@ -1596,7 +1594,7 @@ gn_err_t gn_leaf_param_write_string(const gn_leaf_handle_t leaf_config, _param->param_val->v.s = (char*) realloc(_param->param_val->v.s, sizeof(char) * (strlen(val) + 1)); memset(_param->param_val->v.s, 0, sizeof(char) * (strlen(val) + 1)); - strncpy(_param->param_val->v.s, val, strlen(val)); + strncpy(_param->param_val->v.s, val, GN_LEAF_PARAM_VAL_SIZE -1); } if (_param->storage == GN_LEAF_PARAM_STORAGE_PERSISTED) { @@ -1634,7 +1632,7 @@ gn_err_t gn_leaf_param_write_string(const gn_leaf_handle_t leaf_config, strcpy(evt.param_name, _param->name); evt.id = GN_LEAF_PARAM_CHANGED_EVENT; //evt.data = calloc((strlen(_param->param_val->v.s) + 1) * sizeof(char)); - strncpy(evt.data, _param->param_val->v.s, GN_LEAF_DATA_SIZE); + strncpy(evt.data, _param->param_val->v.s, GN_LEAF_DATA_SIZE-1); esp_err_t ret = esp_event_post_to( _leaf_config->node_config->config->event_loop, GN_BASE_EVENT, @@ -2292,7 +2290,7 @@ gn_err_t _gn_leaf_parameter_update(const gn_leaf_handle_t leaf_config, strncpy(evt.leaf_name, _leaf_config->name, GN_LEAF_NAME_SIZE); strncpy(evt.param_name, param, - GN_LEAF_PARAM_NAME_SIZE); + GN_LEAF_PARAM_NAME_SIZE-1); memcpy(&evt.data[0], data, data_len); evt.data_size = data_len; diff --git a/components/grownode/leaves/gn_gpio.c b/components/grownode/leaves/gn_gpio.c index 3569bd75..724e8760 100644 --- a/components/grownode/leaves/gn_gpio.c +++ b/components/grownode/leaves/gn_gpio.c @@ -143,10 +143,10 @@ void gn_gpio_task(gn_leaf_handle_t leaf_config) { ESP_LOGD(TAG, "[%s] gn_gpio_task", leaf_name); - const size_t GN_GPIO_STATE_STOP = 0; - const size_t GN_GPIO_STATE_RUNNING = 1; + //const size_t GN_GPIO_STATE_STOP = 0; + //const size_t GN_GPIO_STATE_RUNNING = 1; - size_t gn_gpio_state = GN_GPIO_STATE_RUNNING; + //size_t gn_gpio_state = GN_GPIO_STATE_RUNNING; gn_leaf_parameter_event_t evt; //retrieves status descriptor from config diff --git a/components/grownode/test/test_pump.c b/components/grownode/test/test_pump.c index 42e3722c..24a9afb7 100755 --- a/components/grownode/test/test_pump.c +++ b/components/grownode/test/test_pump.c @@ -85,8 +85,8 @@ TEST_CASE("gn_receive_status_0", "[pump]") { event->topic_len = strlen(topic); event->data_len = strlen(data); - strncpy(event->topic, topic, event->topic_len); - strncpy(event->data, data, event->data_len); + strcpy(event->topic, topic); + strcpy(event->data, data); esp_event_base_t base = "base"; void *handler_args = 0; @@ -118,8 +118,8 @@ TEST_CASE("gn_receive_status_1", "[pump]") { event->topic_len = strlen(topic); event->data_len = strlen(data); - strncpy(event->topic, topic, event->topic_len); - strncpy(event->data, data, event->data_len); + strcpy(event->topic, topic); + strcpy(event->data, data); esp_event_base_t base = "base"; void *handler_args = 0; @@ -151,8 +151,8 @@ TEST_CASE("gn_receive_power_0", "[pump]") { event->topic_len = strlen(topic); event->data_len = strlen(data); - strncpy(event->topic, topic, event->topic_len); - strncpy(event->data, data, event->data_len); + strcpy(event->topic, topic); + strcpy(event->data, data); esp_event_base_t base = "base"; void *handler_args = 0; @@ -184,8 +184,8 @@ TEST_CASE("gn_receive_power_128", "[pump]") { event->topic_len = strlen(topic); event->data_len = strlen(data); - strncpy(event->topic, topic, event->topic_len); - strncpy(event->data, data, event->data_len); + strcpy(event->topic, topic); + strcpy(event->data, data); esp_event_base_t base = "base"; void *handler_args = 0; @@ -217,8 +217,8 @@ TEST_CASE("gn_receive_power_500", "[pump]") { event->topic_len = strlen(topic); event->data_len = strlen(data); - strncpy(event->topic, topic, event->topic_len); - strncpy(event->data, data, event->data_len); + strcpy(event->topic, topic); + strcpy(event->data, data); esp_event_base_t base = "base"; void *handler_args = 0; @@ -286,8 +286,8 @@ TEST_CASE("gn_pump_mqtt_stress_test", "[pump]") { event->topic_len = strlen(topic); event->data_len = strlen(data); - strncpy(event->topic, topic, event->topic_len); - strncpy(event->data, data, event->data_len); + strcpy(event->topic, topic); + strcpy(event->data, data); ESP_LOGD(TAG, "sending command - topic %s, data %s", topic, data); diff --git a/components/grownode/test/test_relay.c b/components/grownode/test/test_relay.c index ce4607d9..0b38167a 100644 --- a/components/grownode/test/test_relay.c +++ b/components/grownode/test/test_relay.c @@ -90,8 +90,8 @@ TEST_CASE("gn_receive_status_0", "[relay]") { event->topic_len = strlen(topic); event->data_len = strlen(data); - strncpy(event->topic, topic, event->topic_len); - strncpy(event->data, data, event->data_len); + strcpy(event->topic, topic); + strcpy(event->data, data); esp_event_base_t base = "base"; void *handler_args = 0; @@ -122,8 +122,8 @@ TEST_CASE("gn_receive_status_1", "[relay]") { event->topic_len = strlen(topic); event->data_len = strlen(data); - strncpy(event->topic, topic, event->topic_len); - strncpy(event->data, data, event->data_len); + strcpy(event->topic, topic); + strcpy(event->data, data); esp_event_base_t base = "base"; void *handler_args = 0; @@ -182,8 +182,8 @@ TEST_CASE("gn_relay_mqtt_stress_test", "[relay]") { event->topic_len = strlen(topic); event->data_len = strlen(data); - strncpy(event->topic, topic, event->topic_len); - strncpy(event->data, data, event->data_len); + strcpy(event->topic, topic); + strcpy(event->data, data); ESP_LOGD(TAG, "sending command - topic %s, data %s", topic, data); From a18e3a51aefc12da09731c02467a365ede3c0cdc Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Fri, 11 Mar 2022 08:34:42 +0100 Subject: [PATCH 24/31] oscilloscope protocol and timing improved --- components/grownode/boards/gn_oscilloscope.c | 5 +- components/grownode/leaves/gn_leaf_ina219.c | 139 ++++++++++--------- components/grownode/leaves/gn_leaf_ina219.h | 2 +- 3 files changed, 74 insertions(+), 72 deletions(-) diff --git a/components/grownode/boards/gn_oscilloscope.c b/components/grownode/boards/gn_oscilloscope.c index 4c01486b..2822f3bf 100644 --- a/components/grownode/boards/gn_oscilloscope.c +++ b/components/grownode/boards/gn_oscilloscope.c @@ -33,7 +33,7 @@ void gn_configure_oscilloscope(gn_node_handle_t node) { esp_log_level_set("gn_leaf_ina219", esp_log_level_get(TAG)); - gn_leaf_handle_t ina219 = gn_leaf_create(node, "ina219", gn_leaf_ina219_config, 8192); + gn_leaf_handle_t ina219 = gn_leaf_create(node, "ina219", gn_leaf_ina219_config, 8192, GN_LEAF_TASK_PRIORITY); gn_leaf_param_init_bool(ina219, GN_LEAF_INA219_PARAM_ACTIVE, true); char* ip = calloc(16, sizeof(char)); @@ -41,9 +41,10 @@ void gn_configure_oscilloscope(gn_node_handle_t node) { gn_leaf_param_init_string(ina219, GN_LEAF_INA219_PARAM_IP, ip); gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_PORT, 8094); - gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_MS, 50); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_TICKS, 50); gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SDA, 26); gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SCL, 27); + } diff --git a/components/grownode/leaves/gn_leaf_ina219.c b/components/grownode/leaves/gn_leaf_ina219.c index 1f0f92a2..ed0d3f23 100644 --- a/components/grownode/leaves/gn_leaf_ina219.c +++ b/components/grownode/leaves/gn_leaf_ina219.c @@ -54,6 +54,8 @@ extern "C" { #define I2C_PORT 0 #define I2C_ADDR INA219_ADDR_GND_GND +#define IP_STRING_SIZE 16 + typedef struct { gn_leaf_param_handle_t gn_leaf_ina219_active_param; gn_leaf_param_handle_t gn_leaf_ina219_ip_param; @@ -94,7 +96,7 @@ gn_leaf_descriptor_handle_t gn_leaf_ina219_config(gn_leaf_handle_t leaf_config) GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); data->gn_leaf_ina219_sampling_ms_param = gn_leaf_param_create(leaf_config, - GN_LEAF_INA219_PARAM_SAMPLING_MS, GN_VAL_TYPE_DOUBLE, (gn_val_t ) { + GN_LEAF_INA219_PARAM_SAMPLING_TICKS, GN_VAL_TYPE_DOUBLE, (gn_val_t ) { .d = 100 }, GN_LEAF_PARAM_ACCESS_NETWORK, GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); @@ -126,6 +128,10 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { char leaf_name[GN_LEAF_NAME_SIZE]; gn_leaf_get_name(leaf_config, leaf_name); + + char node_name[GN_NODE_NAME_SIZE]; + gn_node_get_name(gn_leaf_get_node(leaf_config), node_name); + ESP_LOGD(TAG, "[%s] - Initializing ..", leaf_name); gn_leaf_parameter_event_t evt; @@ -133,15 +139,15 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { bool active = true; gn_leaf_param_get_bool(leaf_config, GN_LEAF_INA219_PARAM_ACTIVE, &active); - char ip[16]; - gn_leaf_param_get_string(leaf_config, GN_LEAF_INA219_PARAM_IP, ip, 16); + char ip[IP_STRING_SIZE]; + gn_leaf_param_get_string(leaf_config, GN_LEAF_INA219_PARAM_IP, ip, IP_STRING_SIZE); double port = 0; gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_PORT, &port); - double sampling_ms = 0; - gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SAMPLING_MS, - &sampling_ms); + double sampling_ticks = 0; + gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SAMPLING_TICKS, + &sampling_ticks); double sda = 0; gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SDA, &sda); @@ -174,8 +180,6 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { ESP_LOGD(TAG, "ina219_calibrate: %s", esp_err_to_name(esp_ret)); - float bus_voltage, shunt_voltage, current, power; - int addr_family = 0; int ip_protocol = 0; @@ -190,22 +194,22 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { if (sock < 0) { ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); } - ESP_LOGI(TAG, "Socket created, sending to %s:%d", ip, (int )port); + ESP_LOGD(TAG, "Socket created, sending to %s:%d", ip, (int )port); + + float bus_voltage = 0, shunt_voltage = 0, current = 0, power = 0; + float bus_voltage_instant = 0, shunt_voltage_instant = 0, current_instant = + 0, power_instant = 0; - char current_msg[80]; - char bus_voltage_msg[80]; - char shunt_voltage_msg[80]; - char load_voltage_msg[80]; - char power_msg[80]; + char total_msg[255]; //task cycle while (true) { //ESP_LOGD(TAG, "task cycle.."); - //check for messages and cycle every 100ms + //check for messages if (xQueueReceive(gn_leaf_get_event_queue(leaf_config), &evt, - pdMS_TO_TICKS(sampling_ms)) == pdPASS) { + 0) == pdPASS) { ESP_LOGD(TAG, "%s - received message: %d", leaf_name, evt.id); @@ -235,7 +239,9 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { //execute change gn_leaf_param_write_string(leaf_config, GN_LEAF_INA219_PARAM_IP, evt.data); - strncpy(ip, evt.data, 16); + //strncpy(ip, evt.data, IP_STRING_SIZE-1); + memcpy(ip, evt.data, IP_STRING_SIZE-1); + ip[15]=0; //restart messaging configuration shutdown(sock, 0); @@ -276,10 +282,10 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { } else if (gn_leaf_event_mask_param(&evt, data->gn_leaf_ina219_sampling_ms_param) == 0) { - sampling_ms = atof(evt.data); + sampling_ticks = atof(evt.data); //execute change gn_leaf_param_write_double(leaf_config, - GN_LEAF_INA219_PARAM_SAMPLING_MS, sampling_ms); + GN_LEAF_INA219_PARAM_SAMPLING_TICKS, sampling_ticks); } else if (gn_leaf_event_mask_param(&evt, data->gn_leaf_ina219_sda_param) == 0) { @@ -310,74 +316,69 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { if (active) { + bus_voltage = 0; + shunt_voltage = 0; + current = 0; + power = 0; + + //ESP_LOGD(TAG,"start sampling"); + //retrieve sensor info - ESP_ERROR_CHECK(ina219_get_bus_voltage(&data->dev, &bus_voltage)); - ESP_ERROR_CHECK( - ina219_get_shunt_voltage(&data->dev, &shunt_voltage)); - ESP_ERROR_CHECK(ina219_get_current(&data->dev, ¤t)); - ESP_ERROR_CHECK(ina219_get_power(&data->dev, &power)); + //sampling configurable + for (int i = 0; i < sampling_ticks; i++) { - ESP_LOGD(TAG, - "VBUS: %.04f V, VSHUNT: %.04f mV, IBUS: %.04f mA, PBUS: %.04f mW\n", - bus_voltage, shunt_voltage * 1000, current * 1000, - power * 1000); + ESP_ERROR_CHECK( + ina219_get_bus_voltage(&data->dev, + &bus_voltage_instant)); + bus_voltage += bus_voltage_instant; - int err; + ESP_ERROR_CHECK( + ina219_get_shunt_voltage(&data->dev, + &shunt_voltage_instant)); + shunt_voltage += shunt_voltage_instant; - snprintf(current_msg, 80, "ina219,sensor=current value=%f", - current * 1000); - err = sendto(sock, current_msg, strlen(current_msg), 0, - (struct sockaddr*) &dest_addr, sizeof(dest_addr)); - if (err < 0) { - ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); - } - ESP_LOGD(TAG, "Message sent: %s to %s:%d", current_msg, ip, - (int )port); + ESP_ERROR_CHECK( + ina219_get_current(&data->dev, ¤t_instant)); + current += current_instant; - snprintf(bus_voltage_msg, 80, "ina219,sensor=voltage value=%f", - bus_voltage); - err = sendto(sock, bus_voltage_msg, strlen(bus_voltage_msg), 0, - (struct sockaddr*) &dest_addr, sizeof(dest_addr)); - if (err < 0) { - ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); - } - ESP_LOGD(TAG, "Message sent: %s to %s:%d", bus_voltage_msg, ip, - (int )port); + ESP_ERROR_CHECK(ina219_get_power(&data->dev, &power_instant)); + power += power_instant; - snprintf(shunt_voltage_msg, 80, - "ina219,sensor=shunt_voltage value=%f", - shunt_voltage * 1000); - err = sendto(sock, shunt_voltage_msg, strlen(shunt_voltage_msg), 0, - (struct sockaddr*) &dest_addr, sizeof(dest_addr)); - if (err < 0) { - ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); - } - ESP_LOGD(TAG, "Message sent: %s to %s:%d", shunt_voltage_msg, ip, - (int )port); - snprintf(load_voltage_msg, 80, "ina219,sensor=load_voltage value=%f", - power * 1000); - err = sendto(sock, load_voltage_msg, strlen(load_voltage_msg), 0, - (struct sockaddr*) &dest_addr, sizeof(dest_addr)); - if (err < 0) { - ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); + vTaskDelay(pdMS_TO_TICKS(0.9)); + } - ESP_LOGD(TAG, "Message sent: %s to %s:%d", load_voltage_msg, ip, - (int )port); - snprintf(power_msg, 80, "ina219,sensor=power value=%f", + bus_voltage = bus_voltage / (float) sampling_ticks; + shunt_voltage = shunt_voltage / (float) sampling_ticks; + current = current / (float) sampling_ticks; + power = power / (float) sampling_ticks; + + ESP_LOGD(TAG, + "VBUS: %.04f V, VSHUNT: %.04f mV, IBUS: %.04f mA, PBUS: %.04f mW\n", + bus_voltage, shunt_voltage * 1000, current * 1000, power * 1000); - err = sendto(sock, power_msg, strlen(power_msg), 0, + + int err; + + snprintf(total_msg, 255, + "grownode,node=%s,leaf=%s power=%f,current=%f,vbus=%f,vshunt=%f,vload=%f", + node_name, leaf_name, power, + current, + bus_voltage, + shunt_voltage, + (shunt_voltage + bus_voltage) ); + + err = sendto(sock, total_msg, strlen(total_msg), 0, (struct sockaddr*) &dest_addr, sizeof(dest_addr)); if (err < 0) { ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); } - ESP_LOGD(TAG, "Message sent: %s to %s:%d", power_msg, ip, + ESP_LOGD(TAG, "Message sent: %s to %s:%d", total_msg, ip, (int )port); } - //vTaskDelay(1000 / portTICK_PERIOD_MS); } } diff --git a/components/grownode/leaves/gn_leaf_ina219.h b/components/grownode/leaves/gn_leaf_ina219.h index 8d196447..e2eb0368 100644 --- a/components/grownode/leaves/gn_leaf_ina219.h +++ b/components/grownode/leaves/gn_leaf_ina219.h @@ -28,7 +28,7 @@ static const char GN_LEAF_INA219_TYPE[] = "ina219"; static const char GN_LEAF_INA219_PARAM_ACTIVE[] = "active"; /*!< whether INA219 shall transmit data */ static const char GN_LEAF_INA219_PARAM_IP[] = "ip"; /*!< ip address of the server */ static const char GN_LEAF_INA219_PARAM_PORT[] = "port"; /*!< port of the server */ -static const char GN_LEAF_INA219_PARAM_SAMPLING_MS[] = "sampling"; /*!< sampling time in msec */ +static const char GN_LEAF_INA219_PARAM_SAMPLING_TICKS[] = "sampling_ticks"; /*!< sampling time, approx 1 tick is 1 msec */ static const char GN_LEAF_INA219_PARAM_SDA[] = "sda"; /*!< SDA PIN */ static const char GN_LEAF_INA219_PARAM_SCL[] = "scl"; /*!< SCL PIN */ From 425273243e99ffec6681532c4e9ab5f9a8308ab2 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sat, 12 Mar 2022 08:40:41 +0100 Subject: [PATCH 25/31] updated ina219 functionalities, created ina219 docs --- components/grownode/boards/gn_oscilloscope.c | 4 +- components/grownode/leaves/gn_leaf_ina219.c | 162 +++++++++++++++---- components/grownode/leaves/gn_leaf_ina219.h | 14 +- docs/reference/leaves_list/ina219.md | 56 +++++++ main/main.c | 30 ++-- mkdocs.yml | 1 + 6 files changed, 221 insertions(+), 46 deletions(-) create mode 100644 docs/reference/leaves_list/ina219.md diff --git a/components/grownode/boards/gn_oscilloscope.c b/components/grownode/boards/gn_oscilloscope.c index 2822f3bf..d09c6d7d 100644 --- a/components/grownode/boards/gn_oscilloscope.c +++ b/components/grownode/boards/gn_oscilloscope.c @@ -41,9 +41,11 @@ void gn_configure_oscilloscope(gn_node_handle_t node) { gn_leaf_param_init_string(ina219, GN_LEAF_INA219_PARAM_IP, ip); gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_PORT, 8094); - gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_TICKS, 50); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_CYCLES, 5); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL, 10); gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SDA, 26); gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SCL, 27); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_WORKING_MODE, 1); } diff --git a/components/grownode/leaves/gn_leaf_ina219.c b/components/grownode/leaves/gn_leaf_ina219.c index ed0d3f23..8d2582f9 100644 --- a/components/grownode/leaves/gn_leaf_ina219.c +++ b/components/grownode/leaves/gn_leaf_ina219.c @@ -60,9 +60,16 @@ typedef struct { gn_leaf_param_handle_t gn_leaf_ina219_active_param; gn_leaf_param_handle_t gn_leaf_ina219_ip_param; gn_leaf_param_handle_t gn_leaf_ina219_port_param; - gn_leaf_param_handle_t gn_leaf_ina219_sampling_ms_param; + gn_leaf_param_handle_t gn_leaf_ina219_sampling_cycles_param; + gn_leaf_param_handle_t gn_leaf_ina219_sampling_interval_param; gn_leaf_param_handle_t gn_leaf_ina219_sda_param; gn_leaf_param_handle_t gn_leaf_ina219_scl_param; + gn_leaf_param_handle_t gn_leaf_ina219_working_mode_param; + gn_leaf_param_handle_t gn_leaf_ina219_power_param; + gn_leaf_param_handle_t gn_leaf_ina219_voltage_param; + gn_leaf_param_handle_t gn_leaf_ina219_shunt_voltage_param; + gn_leaf_param_handle_t gn_leaf_ina219_bus_voltage_param; + gn_leaf_param_handle_t gn_leaf_ina219_current_param; ina219_t dev; } gn_leaf_ina219_data_t; @@ -82,41 +89,86 @@ gn_leaf_descriptor_handle_t gn_leaf_ina219_config(gn_leaf_handle_t leaf_config) data->gn_leaf_ina219_active_param = gn_leaf_param_create(leaf_config, GN_LEAF_INA219_PARAM_ACTIVE, GN_VAL_TYPE_BOOLEAN, (gn_val_t ) { .b = - true }, GN_LEAF_PARAM_ACCESS_NETWORK, + true }, GN_LEAF_PARAM_ACCESS_ALL, GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_boolean); data->gn_leaf_ina219_ip_param = gn_leaf_param_create(leaf_config, GN_LEAF_INA219_PARAM_IP, GN_VAL_TYPE_STRING, (gn_val_t ) { .s = - malloc(16) }, GN_LEAF_PARAM_ACCESS_NETWORK, - GN_LEAF_PARAM_STORAGE_PERSISTED, NULL); + calloc(16, sizeof(char)) }, + GN_LEAF_PARAM_ACCESS_ALL, GN_LEAF_PARAM_STORAGE_PERSISTED, NULL); data->gn_leaf_ina219_port_param = gn_leaf_param_create(leaf_config, - GN_LEAF_INA219_PARAM_PORT, GN_VAL_TYPE_DOUBLE, - (gn_val_t ) { .d = 0 }, GN_LEAF_PARAM_ACCESS_NETWORK, + GN_LEAF_INA219_PARAM_PORT, GN_VAL_TYPE_DOUBLE, (gn_val_t ) { .d = + 8094 }, GN_LEAF_PARAM_ACCESS_ALL, + GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); + + data->gn_leaf_ina219_sampling_cycles_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_SAMPLING_CYCLES, GN_VAL_TYPE_DOUBLE, + (gn_val_t ) { .d = 1 }, GN_LEAF_PARAM_ACCESS_ALL, GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); - data->gn_leaf_ina219_sampling_ms_param = gn_leaf_param_create(leaf_config, - GN_LEAF_INA219_PARAM_SAMPLING_TICKS, GN_VAL_TYPE_DOUBLE, (gn_val_t ) { - .d = 100 }, GN_LEAF_PARAM_ACCESS_NETWORK, + data->gn_leaf_ina219_sampling_interval_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL, GN_VAL_TYPE_DOUBLE, + (gn_val_t ) { .d = 1000 }, GN_LEAF_PARAM_ACCESS_ALL, GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); data->gn_leaf_ina219_sda_param = gn_leaf_param_create(leaf_config, GN_LEAF_INA219_PARAM_SDA, GN_VAL_TYPE_DOUBLE, - (gn_val_t ) { .d = 26 }, GN_LEAF_PARAM_ACCESS_NODE, + (gn_val_t ) { .d = 26 }, GN_LEAF_PARAM_ACCESS_NODE_INTERNAL, GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); data->gn_leaf_ina219_scl_param = gn_leaf_param_create(leaf_config, GN_LEAF_INA219_PARAM_SCL, GN_VAL_TYPE_DOUBLE, - (gn_val_t ) { .d = 27 }, GN_LEAF_PARAM_ACCESS_NODE, + (gn_val_t ) { .d = 27 }, GN_LEAF_PARAM_ACCESS_NODE_INTERNAL, + GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); + + data->gn_leaf_ina219_working_mode_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_WORKING_MODE, GN_VAL_TYPE_DOUBLE, (gn_val_t ) { + .d = 1 }, GN_LEAF_PARAM_ACCESS_ALL, GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); + data->gn_leaf_ina219_power_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_POWER, GN_VAL_TYPE_DOUBLE, + (gn_val_t ) { .d = 0 }, GN_LEAF_PARAM_ACCESS_NETWORK, + GN_LEAF_PARAM_STORAGE_VOLATILE, NULL); + + data->gn_leaf_ina219_voltage_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_VOLTAGE, GN_VAL_TYPE_DOUBLE, (gn_val_t ) { .d = + 0 }, GN_LEAF_PARAM_ACCESS_NETWORK, + GN_LEAF_PARAM_STORAGE_VOLATILE, NULL); + + data->gn_leaf_ina219_bus_voltage_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_BUS_VOLTAGE, GN_VAL_TYPE_DOUBLE, (gn_val_t ) { + .d = 0 }, GN_LEAF_PARAM_ACCESS_NETWORK, + GN_LEAF_PARAM_STORAGE_VOLATILE, NULL); + + data->gn_leaf_ina219_shunt_voltage_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_SHUNT_VOLTAGE, GN_VAL_TYPE_DOUBLE, + (gn_val_t ) { .d = 0 }, GN_LEAF_PARAM_ACCESS_NETWORK, + GN_LEAF_PARAM_STORAGE_VOLATILE, NULL); + + data->gn_leaf_ina219_current_param = gn_leaf_param_create(leaf_config, + GN_LEAF_INA219_PARAM_CURRENT, GN_VAL_TYPE_DOUBLE, (gn_val_t ) { .d = + 0 }, GN_LEAF_PARAM_ACCESS_NETWORK, + GN_LEAF_PARAM_STORAGE_VOLATILE, NULL); + gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_active_param); gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_ip_param); gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_port_param); gn_leaf_param_add_to_leaf(leaf_config, - data->gn_leaf_ina219_sampling_ms_param); + data->gn_leaf_ina219_sampling_cycles_param); + gn_leaf_param_add_to_leaf(leaf_config, + data->gn_leaf_ina219_sampling_interval_param); gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_sda_param); gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_scl_param); + gn_leaf_param_add_to_leaf(leaf_config, + data->gn_leaf_ina219_working_mode_param); + gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_voltage_param); + gn_leaf_param_add_to_leaf(leaf_config, + data->gn_leaf_ina219_bus_voltage_param); + gn_leaf_param_add_to_leaf(leaf_config, + data->gn_leaf_ina219_shunt_voltage_param); + gn_leaf_param_add_to_leaf(leaf_config, data->gn_leaf_ina219_current_param); descriptor->status = GN_LEAF_STATUS_INITIALIZED; descriptor->data = data; @@ -140,14 +192,19 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { gn_leaf_param_get_bool(leaf_config, GN_LEAF_INA219_PARAM_ACTIVE, &active); char ip[IP_STRING_SIZE]; - gn_leaf_param_get_string(leaf_config, GN_LEAF_INA219_PARAM_IP, ip, IP_STRING_SIZE); + gn_leaf_param_get_string(leaf_config, GN_LEAF_INA219_PARAM_IP, ip, + IP_STRING_SIZE); double port = 0; gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_PORT, &port); - double sampling_ticks = 0; - gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SAMPLING_TICKS, - &sampling_ticks); + double sampling_cycles = 0; + gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SAMPLING_CYCLES, + &sampling_cycles); + + double sampling_interval = 0; + gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL, + &sampling_interval); double sda = 0; gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SDA, &sda); @@ -155,6 +212,10 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { double scl = 0; gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SCL, &scl); + double working_mode = 0; + gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_WORKING_MODE, + &working_mode); + //retrieves status descriptor from config gn_leaf_ina219_data_t *data = (gn_leaf_ina219_data_t*) gn_leaf_get_descriptor(leaf_config)->data; @@ -240,8 +301,8 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { gn_leaf_param_write_string(leaf_config, GN_LEAF_INA219_PARAM_IP, evt.data); //strncpy(ip, evt.data, IP_STRING_SIZE-1); - memcpy(ip, evt.data, IP_STRING_SIZE-1); - ip[15]=0; + memcpy(ip, evt.data, IP_STRING_SIZE - 1); + ip[15] = 0; //restart messaging configuration shutdown(sock, 0); @@ -280,12 +341,22 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { (int )port); } else if (gn_leaf_event_mask_param(&evt, - data->gn_leaf_ina219_sampling_ms_param) == 0) { + data->gn_leaf_ina219_sampling_cycles_param) == 0) { + + sampling_cycles = atof(evt.data); + //execute change + gn_leaf_param_write_double(leaf_config, + GN_LEAF_INA219_PARAM_SAMPLING_CYCLES, + sampling_cycles); + + } else if (gn_leaf_event_mask_param(&evt, + data->gn_leaf_ina219_sampling_interval_param) == 0) { - sampling_ticks = atof(evt.data); + sampling_interval = atof(evt.data); //execute change gn_leaf_param_write_double(leaf_config, - GN_LEAF_INA219_PARAM_SAMPLING_TICKS, sampling_ticks); + GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL, + sampling_interval); } else if (gn_leaf_event_mask_param(&evt, data->gn_leaf_ina219_sda_param) == 0) { @@ -303,6 +374,14 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { gn_leaf_param_write_double(leaf_config, GN_LEAF_INA219_PARAM_SCL, scl); + } else if (gn_leaf_event_mask_param(&evt, + data->gn_leaf_ina219_working_mode_param) == 0) { + + working_mode = atof(evt.data); + //execute change + gn_leaf_param_write_double(leaf_config, + GN_LEAF_INA219_PARAM_WORKING_MODE, working_mode); + } break; @@ -325,7 +404,7 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { //retrieve sensor info //sampling configurable - for (int i = 0; i < sampling_ticks; i++) { + for (int i = 0; i < sampling_cycles; i++) { ESP_ERROR_CHECK( ina219_get_bus_voltage(&data->dev, @@ -344,30 +423,27 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { ESP_ERROR_CHECK(ina219_get_power(&data->dev, &power_instant)); power += power_instant; - - vTaskDelay(pdMS_TO_TICKS(0.9)); + vTaskDelay(pdMS_TO_TICKS(sampling_interval)); } - bus_voltage = bus_voltage / (float) sampling_ticks; - shunt_voltage = shunt_voltage / (float) sampling_ticks; - current = current / (float) sampling_ticks; - power = power / (float) sampling_ticks; + bus_voltage = bus_voltage / (float) sampling_cycles; + shunt_voltage = shunt_voltage / (float) sampling_cycles; + current = current / (float) sampling_cycles; + power = power / (float) sampling_cycles; ESP_LOGD(TAG, "VBUS: %.04f V, VSHUNT: %.04f mV, IBUS: %.04f mA, PBUS: %.04f mW\n", bus_voltage, shunt_voltage * 1000, current * 1000, power * 1000); + if (working_mode == 0 || working_mode == 2) { int err; snprintf(total_msg, 255, "grownode,node=%s,leaf=%s power=%f,current=%f,vbus=%f,vshunt=%f,vload=%f", - node_name, leaf_name, power, - current, - bus_voltage, - shunt_voltage, - (shunt_voltage + bus_voltage) ); + node_name, leaf_name, power, current, bus_voltage, + shunt_voltage, (shunt_voltage + bus_voltage)); err = sendto(sock, total_msg, strlen(total_msg), 0, (struct sockaddr*) &dest_addr, sizeof(dest_addr)); @@ -376,6 +452,26 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { } ESP_LOGD(TAG, "Message sent: %s to %s:%d", total_msg, ip, (int )port); + } + + if (working_mode == 1 || working_mode == 2) { + + gn_leaf_param_write_double(leaf_config, + GN_LEAF_INA219_PARAM_VOLTAGE, (shunt_voltage + bus_voltage)); + + gn_leaf_param_write_double(leaf_config, + GN_LEAF_INA219_PARAM_SHUNT_VOLTAGE, shunt_voltage); + + gn_leaf_param_write_double(leaf_config, + GN_LEAF_INA219_PARAM_BUS_VOLTAGE, bus_voltage); + + gn_leaf_param_write_double(leaf_config, + GN_LEAF_INA219_PARAM_POWER, power); + + gn_leaf_param_write_double(leaf_config, + GN_LEAF_INA219_PARAM_CURRENT, current); + + } } diff --git a/components/grownode/leaves/gn_leaf_ina219.h b/components/grownode/leaves/gn_leaf_ina219.h index e2eb0368..c1023115 100644 --- a/components/grownode/leaves/gn_leaf_ina219.h +++ b/components/grownode/leaves/gn_leaf_ina219.h @@ -25,12 +25,22 @@ extern "C" { static const char GN_LEAF_INA219_TYPE[] = "ina219"; //parameters -static const char GN_LEAF_INA219_PARAM_ACTIVE[] = "active"; /*!< whether INA219 shall transmit data */ +static const char GN_LEAF_INA219_PARAM_ACTIVE[] = "active"; /*!< whether INA219 shall be enabled */ static const char GN_LEAF_INA219_PARAM_IP[] = "ip"; /*!< ip address of the server */ static const char GN_LEAF_INA219_PARAM_PORT[] = "port"; /*!< port of the server */ -static const char GN_LEAF_INA219_PARAM_SAMPLING_TICKS[] = "sampling_ticks"; /*!< sampling time, approx 1 tick is 1 msec */ +static const char GN_LEAF_INA219_PARAM_SAMPLING_CYCLES[] = "samp_cycles"; /*!< sampling cycles */ +static const char GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL[] = "samp_interval"; /*!< sampling interval, (approx) in msec */ static const char GN_LEAF_INA219_PARAM_SDA[] = "sda"; /*!< SDA PIN */ static const char GN_LEAF_INA219_PARAM_SCL[] = "scl"; /*!< SCL PIN */ +static const char GN_LEAF_INA219_PARAM_WORKING_MODE[] = "working_mode"; /*!< 0 = through UDP via influxdb protocol, 1 = parameters, 2 both*/ +static const char GN_LEAF_INA219_PARAM_POWER[] = "power"; /*!< last power measured, in mW*/ +static const char GN_LEAF_INA219_PARAM_VOLTAGE[] = "voltage"; /*!< last voltage measured, in mV */ +static const char GN_LEAF_INA219_PARAM_SHUNT_VOLTAGE[] = "shunt_voltage"; /*!< last shunt voltage measured, in mV */ +static const char GN_LEAF_INA219_PARAM_BUS_VOLTAGE[] = "bus_voltage"; /*!< last bus voltage measured, in mV */ +static const char GN_LEAF_INA219_PARAM_CURRENT[] = "current"; /*!< last current measured, in mA */ + + + gn_leaf_descriptor_handle_t gn_leaf_ina219_config(gn_leaf_handle_t leaf_config); diff --git a/docs/reference/leaves_list/ina219.md b/docs/reference/leaves_list/ina219.md new file mode 100644 index 00000000..61950a94 --- /dev/null +++ b/docs/reference/leaves_list/ina219.md @@ -0,0 +1,56 @@ + +# INA219 + +This leaf controls a INA219 current/voltage sensor through I2C protocol. + +It is useful when you need control over an electrical load or when running with batteries and you need to measure the battery status. + +It can also be used to build an oscilloscope. + +## Functionalities + +It collects measurements from sensor for `samp_cycles` cycles waiting each cycle a `samp_interval`. After this collection time, it computes the average measure and updates the final value. + +This leaf can be used in two ways through the `working_mode` parameter: + +- sending measurement to a telegraf server, in influxdb protocol through UDP. This is useful if you want to analyze metrics through influxdb or eg. directly connected to a grafana server + +- using internal parameters to store measured values + +## Parameters + +| name | description | type | default | access | storage | valid values | +| ----------- | ---------------------------------------- | ------------ | --------- | ------------- | --------- | ---------- | +| active | whether INA219 shall transmit data | boolean | true | GN_LEAF_PARAM_ACCESS_ALL | GN_LEAF_PARAM_STORAGE_PERSISTED | true/false | +| ip | ip address of the server | string | null | GN_LEAF_PARAM_ACCESS_ALL | GN_LEAF_PARAM_STORAGE_PERSISTED | 16 char array | +| port | port of the server | double | 8094 | GN_LEAF_PARAM_ACCESS_ALL | GN_LEAF_PARAM_STORAGE_PERSISTED | true/false | +| samp_cycles | sampling cycles | double | 1 | GN_LEAF_PARAM_ACCESS_ALL | GN_LEAF_PARAM_STORAGE_PERSISTED | positive double | +| samp_interval | sampling interval, (approx) in msec | double | 1000 | GN_LEAF_PARAM_ACCESS_ALL | GN_LEAF_PARAM_STORAGE_PERSISTED | positive double | +| sda | SDA PIN | double | 26 | GN_LEAF_PARAM_ACCESS_NODE_INTERNAL | GN_LEAF_PARAM_STORAGE_PERSISTED | positive double | +| scl | SCL PIN | double | 26 | GN_LEAF_PARAM_ACCESS_NODE_INTERNAL | GN_LEAF_PARAM_STORAGE_PERSISTED | positive double | +| working_mode | 0 = through UDP via influxdb protocol, 1 = parameters, 2 both | double | 1 | GN_LEAF_PARAM_ACCESS_ALL | GN_LEAF_PARAM_STORAGE_PERSISTED | double positive | +| power | last power measured, in mW | double | 0 | GN_LEAF_PARAM_ACCESS_NETWORK | GN_LEAF_PARAM_STORAGE_VOLATILE | any | +| voltage | last voltage measured, in mV | double | 0 | GN_LEAF_PARAM_ACCESS_NETWORK | GN_LEAF_PARAM_STORAGE_VOLATILE | any | +| shunt_voltage | last shunt voltage measured, in mV | double | 0 | GN_LEAF_PARAM_ACCESS_NETWORK | GN_LEAF_PARAM_STORAGE_VOLATILE | any | +| bus_voltage | last bus voltage measured, in mV | double | 0 | GN_LEAF_PARAM_ACCESS_NETWORK | GN_LEAF_PARAM_STORAGE_VOLATILE | any | +| current | last current measured, in mA | double | 0 | GN_LEAF_PARAM_ACCESS_NETWORK | GN_LEAF_PARAM_STORAGE_VOLATILE | any | + +## Example + +``` + gn_leaf_handle_t ina219 = gn_leaf_create(node, "ina219", gn_leaf_ina219_config, 8192, GN_LEAF_TASK_PRIORITY); + gn_leaf_param_init_bool(ina219, GN_LEAF_INA219_PARAM_ACTIVE, true); + + char* ip = calloc(16, sizeof(char)); + strncpy(ip, "192.168.1.20", 16); + + gn_leaf_param_init_string(ina219, GN_LEAF_INA219_PARAM_IP, ip); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_PORT, 8094); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_CYCLES, 5); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL, 10); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SDA, 26); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SCL, 27); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_WORKING_MODE, 1); + +``` + diff --git a/main/main.c b/main/main.c index 3252c71e..0742450d 100644 --- a/main/main.c +++ b/main/main.c @@ -22,14 +22,24 @@ #include "grownode.h" //include the board you want to start here -#include "gn_blink.h" +#include "gn_oscilloscope.h" +#include "gn_easypot1.h" #define TASK_STACK_SIZE 8192*4 #define TAG "gn_main" +#include "esp_check.h" + +//switch from LOGI to LOGD +//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE +#include "esp_log.h" + + void app_main(void) { + //vTaskDelay(5000 / portTICK_PERIOD_MS); + //set default log level esp_log_level_set("*", ESP_LOG_INFO); @@ -45,25 +55,25 @@ void app_main(void) { esp_log_level_set("gn_display", ESP_LOG_INFO); //boards - esp_log_level_set("gn_easypot1", ESP_LOG_INFO); - esp_log_level_set("gn_blink", ESP_LOG_INFO); + esp_log_level_set("gn_oscilloscope", ESP_LOG_INFO); + esp_log_level_set("gn_easypot1", ESP_LOG_DEBUG); gn_config_init_param_t config_init = { .provisioning_security = true, .provisioning_password = "grownode", .wifi_retries_before_reset_provisioning = 5, .server_board_id_topic = false, - .server_base_topic = "grownode/blink", + .server_base_topic = "grownode", .server_url = "mqtt://192.168.1.10:1883", .server_keepalive_timer_sec = 3600, .server_discovery = false, .server_discovery_prefix = "homeassistant", - .firmware_url = "http://grownode.duckdns.org/grownode/blink/grownode.bin", + .firmware_url = "http://grownode.duckdns.org/grownode/oscilloscope/grownode.bin", .sntp_url = "pool.ntp.org", .wakeup_time_millisec = 5000LL, .sleep_delay_millisec = 50LL, - .sleep_time_millisec = 120000LL, - .sleep_mode = GN_SLEEP_MODE_DEEP + .sleep_time_millisec = 10000LL, + .sleep_mode = GN_SLEEP_MODE_NONE }; //creates the config handle @@ -77,10 +87,11 @@ void app_main(void) { } //creates a new node - gn_node_handle_t node = gn_node_create(config, "node"); + gn_node_handle_t node = gn_node_create(config, "oscilloscope"); //the board to start - gn_configure_blink(node); + gn_configure_oscilloscope(node); + //gn_configure_easypot1(node); //finally, start node gn_node_start(node); @@ -88,7 +99,6 @@ void app_main(void) { //handles loop gn_node_loop(node); - } int main(int argc, char **argv) { diff --git a/mkdocs.yml b/mkdocs.yml index 7f51b11f..6f931771 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -32,6 +32,7 @@ nav: - Standard Leaves: - 'reference/leaves_list/index.md' - 'reference/leaves_list/bme280.md' + - 'reference/leaves_list/ina219.md' - 'reference/boards.md' - 'reference/events.md' - 'reference/networking.md' From cad450817a3da890286bf6a5dfb97cdc4654fd88 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sat, 12 Mar 2022 12:24:12 +0100 Subject: [PATCH 26/31] oscilloscope board doc added --- components/grownode/boards/gn_oscilloscope.c | 4 +- components/grownode/leaves/gn_leaf_ina219.c | 1 + docs/boards/boards_oscilloscope.md | 71 ++++++++++++++++++ docs/boards/index.md | 8 +- docs/reference/leaves_list/ina219.md | 2 +- docs/resources/images/oscilloscope_load.png | Bin 0 -> 54676 bytes docs/resources/images/oscilloscope_wiring.png | Bin 0 -> 138329 bytes mkdocs.yml | 1 + 8 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 docs/boards/boards_oscilloscope.md create mode 100644 docs/resources/images/oscilloscope_load.png create mode 100644 docs/resources/images/oscilloscope_wiring.png diff --git a/components/grownode/boards/gn_oscilloscope.c b/components/grownode/boards/gn_oscilloscope.c index d09c6d7d..31b29995 100644 --- a/components/grownode/boards/gn_oscilloscope.c +++ b/components/grownode/boards/gn_oscilloscope.c @@ -41,8 +41,8 @@ void gn_configure_oscilloscope(gn_node_handle_t node) { gn_leaf_param_init_string(ina219, GN_LEAF_INA219_PARAM_IP, ip); gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_PORT, 8094); - gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_CYCLES, 5); - gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL, 10); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_CYCLES, 10); + gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL, 100); gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SDA, 26); gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_SCL, 27); gn_leaf_param_init_double(ina219, GN_LEAF_INA219_PARAM_WORKING_MODE, 1); diff --git a/components/grownode/leaves/gn_leaf_ina219.c b/components/grownode/leaves/gn_leaf_ina219.c index 8d2582f9..c1370409 100644 --- a/components/grownode/leaves/gn_leaf_ina219.c +++ b/components/grownode/leaves/gn_leaf_ina219.c @@ -427,6 +427,7 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { } + //averaging bus_voltage = bus_voltage / (float) sampling_cycles; shunt_voltage = shunt_voltage / (float) sampling_cycles; current = current / (float) sampling_cycles; diff --git a/docs/boards/boards_oscilloscope.md b/docs/boards/boards_oscilloscope.md new file mode 100644 index 00000000..f3125707 --- /dev/null +++ b/docs/boards/boards_oscilloscope.md @@ -0,0 +1,71 @@ +--- +hide: + - navigation +--- + +#Oscilloscope + +This is a board dedicated to measure current and power of a circuit using INA219, capable of transferring information to a Telegraf instance or via MQTT. I did it to probe other board power consumption in battery powered mode. + +##Features + +- Measuring current, voltage, power +- Configurable sampling interval +- Configurable number of samples to average per interval +- Capable of transfer information to other reporting system via UDP using telegraf/influxdb + +Measurement Voltage 0V...26V +Max Current 3.2A +Max Power 83W +Operation Voltage 3V…5.5V +Communication Protocol I2C + +##Bill of Material + +- 1 [INA219 breakout board](https://it.aliexpress.com/item/1005001636648121.html?_randl_currency=EUR&_randl_shipto=IT&src=google&src=google&albch=shopping&acnt=631-313-3945&slnk=&plac=&mtctp=&albbt=Google_7_shopping&albagn=888888&isSmbActive=false&isSmbAutoCall=false&needSmbHouyi=false&albcp=15227475790&albag=129989760135&trgt=295988246616&crea=it1005001636648121&netw=u&device=c&albpg=295988246616&albpd=it1005001636648121&gclid=CjwKCAiAprGRBhBgEiwANJEY7CEH4F93zjJuso7mPsIiMDksqkgVWJ3XkpnOwNQi1BoqJuPTu2BkWhoCQ5EQAvD_BwE&gclsrc=aw.ds&aff_fcid=6af9e4ff550e4d83a24f6555c830d339-1647082489716-01285-UneMJZVf&aff_fsk=UneMJZVf&aff_platform=aaf&sk=UneMJZVf&aff_trace_key=6af9e4ff550e4d83a24f6555c830d339-1647082489716-01285-UneMJZVf&terminal_id=676d2c3560864e039f8e8255ccf6e8cf&afSmartRedirect=y) +- [breadboard and wires](https://it.aliexpress.com/item/4000805673115.html?spm=a2g0o.productlist.0.0.13c056a4HYIA1P&algo_pvid=3b89dd30-2e62-4c5e-9f47-a3d1ba448ff9&aem_p4p_detail=20220116111751259270886284480019365032&algo_exp_id=3b89dd30-2e62-4c5e-9f47-a3d1ba448ff9-14&pdp_ext_f=%7B%22sku_id%22%3A%2210000008092850406%22%7D&pdp_pi=-1%3B0.87%3B-1%3BEUR+0.59%40salePrice%3BEUR%3Bsearch-mainSearch) + +##Assembling + +- Wire your ESP32 to breadboard GND and 3V3 +- Wire your INA219 as per the table + +| ESP32 | INA219 | +| ----------- | ----------- | +| GND | GND | +| 5V | VCC | +| 27 | SCL | +| 26 | SDA | + +![oscilloscope wiring](resources/images/oscilloscope_wiring.png) + +- Wire your load to be measured to VIN+ and VIN- as per the figure + +![oscilloscope load](resources/images/oscilloscope_load.png) + +##Code + +The configuration code, other than `main\main.c` is contained in `components\grownode\boards\oscilloscope.c`. The code is meant to be basic in order to give you some training to basic features. + +### The Main + +You just need to 'load' the board into the firmware + +- add an `include "oscilloscope.h"` directive on the header declarations +- change the configuration row from the standard `gn_configure_blink(node)` to `gn_configure_oscilloscope(node)` + +This will tell the compiler to load the easypot1 code into the firmware upon the next build. + +### Playing with code + +See the [INA219 leaf reference doc](resources/leaves_list/ina219) to understand how to configure and play with parameters + +### Creating a full monitoring system + +TODO + +### Final Result + +Here's a quick demo I did while testing: + + diff --git a/docs/boards/index.md b/docs/boards/index.md index 4842a275..adce4805 100644 --- a/docs/boards/index.md +++ b/docs/boards/index.md @@ -38,4 +38,10 @@ It has an onboard logic to keep the reservoir at desired temperature prior to wa the board is powered with 220V - 12V 10A power adaptor so it can manage quite a lot of power consumption -[Board Detail](boards_hb2.md) \ No newline at end of file +[Board Detail](boards_hb2.md) + +##Oscilloscope + +This is a board dedicated to measure current and power of a circuit using INA219, capable of transferring information to a Telegraf instance or via MQTT. I did it to probe other board power consumption in battery powered mode. + +[Board Detail](boards_oscilloscope.md) \ No newline at end of file diff --git a/docs/reference/leaves_list/ina219.md b/docs/reference/leaves_list/ina219.md index 61950a94..586baeb9 100644 --- a/docs/reference/leaves_list/ina219.md +++ b/docs/reference/leaves_list/ina219.md @@ -21,7 +21,7 @@ This leaf can be used in two ways through the `working_mode` parameter: | name | description | type | default | access | storage | valid values | | ----------- | ---------------------------------------- | ------------ | --------- | ------------- | --------- | ---------- | -| active | whether INA219 shall transmit data | boolean | true | GN_LEAF_PARAM_ACCESS_ALL | GN_LEAF_PARAM_STORAGE_PERSISTED | true/false | +| active | whether INA219 shall be enabled | boolean | true | GN_LEAF_PARAM_ACCESS_ALL | GN_LEAF_PARAM_STORAGE_PERSISTED | true/false | | ip | ip address of the server | string | null | GN_LEAF_PARAM_ACCESS_ALL | GN_LEAF_PARAM_STORAGE_PERSISTED | 16 char array | | port | port of the server | double | 8094 | GN_LEAF_PARAM_ACCESS_ALL | GN_LEAF_PARAM_STORAGE_PERSISTED | true/false | | samp_cycles | sampling cycles | double | 1 | GN_LEAF_PARAM_ACCESS_ALL | GN_LEAF_PARAM_STORAGE_PERSISTED | positive double | diff --git a/docs/resources/images/oscilloscope_load.png b/docs/resources/images/oscilloscope_load.png new file mode 100644 index 0000000000000000000000000000000000000000..662daa07a6e0ebf33e812e3bb00a4e1745a78e73 GIT binary patch literal 54676 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 z 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 literal 0 HcmV?d00001 diff --git a/mkdocs.yml b/mkdocs.yml index 6f931771..4005d282 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -24,6 +24,7 @@ nav: - EasyPot1: 'boards/boards_easypot1.md' - HydroBoard1: 'boards/boards_hb1.md' - HydroBoard2: 'boards/boards_hb2.md' + - Oscilloscope: 'boards/oscilloscope.md' - Reference Guide: - 'reference/index.md' - 'reference/node.md' From 5572ad853611ad40c47b65e8171ab5ad7e7110eb Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sat, 12 Mar 2022 12:38:37 +0100 Subject: [PATCH 27/31] Update boards_oscilloscope.md --- docs/boards/boards_oscilloscope.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/boards/boards_oscilloscope.md b/docs/boards/boards_oscilloscope.md index f3125707..2260a534 100644 --- a/docs/boards/boards_oscilloscope.md +++ b/docs/boards/boards_oscilloscope.md @@ -17,7 +17,7 @@ This is a board dedicated to measure current and power of a circuit using INA219 Measurement Voltage 0V...26V Max Current 3.2A Max Power 83W -Operation Voltage 3V…5.5V +Operation Voltage 3V...5.5V Communication Protocol I2C ##Bill of Material From 1e5573abfaeaf03c6943041300d0d559a9e274f8 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sat, 12 Mar 2022 12:43:03 +0100 Subject: [PATCH 28/31] Update boards_oscilloscope.md --- docs/boards/boards_oscilloscope.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/boards/boards_oscilloscope.md b/docs/boards/boards_oscilloscope.md index 2260a534..a3ebc5f2 100644 --- a/docs/boards/boards_oscilloscope.md +++ b/docs/boards/boards_oscilloscope.md @@ -37,11 +37,11 @@ Communication Protocol I2C | 27 | SCL | | 26 | SDA | -![oscilloscope wiring](resources/images/oscilloscope_wiring.png) +![oscilloscope wiring](../../resources/images/oscilloscope_wiring.png) - Wire your load to be measured to VIN+ and VIN- as per the figure -![oscilloscope load](resources/images/oscilloscope_load.png) +![oscilloscope load](../../resources/images/oscilloscope_load.png) ##Code From 73d7b2b6bc1c8343661bf0c4e2866dc1b022e702 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sun, 13 Mar 2022 09:09:16 +0100 Subject: [PATCH 29/31] bug fix in validators --- components/grownode/gn_commons.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/components/grownode/gn_commons.c b/components/grownode/gn_commons.c index a54592f4..65351e4f 100755 --- a/components/grownode/gn_commons.c +++ b/components/grownode/gn_commons.c @@ -100,11 +100,11 @@ gn_leaf_param_validator_result_t gn_validator_double_positive( double _p1 = **(double**) param_value; ESP_LOGD(TAG, "gn_validator_double_positive - param: %f", _p1); - if (min > **(double**) param_value) { - memcpy(param_value, &zero, sizeof(min)); + if (**(double**) param_value < zero) { + memcpy(param_value, &zero, sizeof(double)); return GN_LEAF_PARAM_VALIDATOR_ERROR_BELOW_MIN; - } else if (max < **(double**) param_value) { - memcpy(param_value, &max, sizeof(max)); + } else if (**(double**) param_value >= max) { + memcpy(param_value, &max, sizeof(double)); return GN_LEAF_PARAM_VALIDATOR_ERROR_ABOVE_MAX; } @@ -125,11 +125,11 @@ gn_leaf_param_validator_result_t gn_validator_double( double _p1 = **(double**) param_value; ESP_LOGD(TAG, "gn_validator_double - param: %f", _p1); - if (min > **(double**) param_value) { - memcpy(param_value, &min, sizeof(min)); + if (**(double**) param_value < min) { + memcpy(param_value, &min, sizeof(double)); return GN_LEAF_PARAM_VALIDATOR_ERROR_BELOW_MIN; - } else if (max < **(double**) param_value) { - memcpy(param_value, &max, sizeof(max)); + } else if (**(double**) param_value >= max) { + memcpy(param_value, &max, sizeof(double)); return GN_LEAF_PARAM_VALIDATOR_ERROR_ABOVE_MAX; } From 22f5b255ffafc11c713f232def0234c2a4a8fd4a Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sun, 13 Mar 2022 09:09:36 +0100 Subject: [PATCH 30/31] revised units of measures --- components/grownode/leaves/gn_leaf_ina219.c | 77 +++++++++++---------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/components/grownode/leaves/gn_leaf_ina219.c b/components/grownode/leaves/gn_leaf_ina219.c index c1370409..865bf7ab 100644 --- a/components/grownode/leaves/gn_leaf_ina219.c +++ b/components/grownode/leaves/gn_leaf_ina219.c @@ -102,15 +102,17 @@ gn_leaf_descriptor_handle_t gn_leaf_ina219_config(gn_leaf_handle_t leaf_config) 8094 }, GN_LEAF_PARAM_ACCESS_ALL, GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); - data->gn_leaf_ina219_sampling_cycles_param = gn_leaf_param_create(leaf_config, - GN_LEAF_INA219_PARAM_SAMPLING_CYCLES, GN_VAL_TYPE_DOUBLE, - (gn_val_t ) { .d = 1 }, GN_LEAF_PARAM_ACCESS_ALL, - GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); - - data->gn_leaf_ina219_sampling_interval_param = gn_leaf_param_create(leaf_config, - GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL, GN_VAL_TYPE_DOUBLE, - (gn_val_t ) { .d = 1000 }, GN_LEAF_PARAM_ACCESS_ALL, - GN_LEAF_PARAM_STORAGE_PERSISTED, gn_validator_double_positive); + data->gn_leaf_ina219_sampling_cycles_param = gn_leaf_param_create( + leaf_config, GN_LEAF_INA219_PARAM_SAMPLING_CYCLES, + GN_VAL_TYPE_DOUBLE, (gn_val_t ) { .d = 1 }, + GN_LEAF_PARAM_ACCESS_ALL, GN_LEAF_PARAM_STORAGE_PERSISTED, + gn_validator_double_positive); + + data->gn_leaf_ina219_sampling_interval_param = gn_leaf_param_create( + leaf_config, GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL, + GN_VAL_TYPE_DOUBLE, (gn_val_t ) { .d = 1000 }, + GN_LEAF_PARAM_ACCESS_ALL, GN_LEAF_PARAM_STORAGE_PERSISTED, + gn_validator_double_positive); data->gn_leaf_ina219_sda_param = gn_leaf_param_create(leaf_config, GN_LEAF_INA219_PARAM_SDA, GN_VAL_TYPE_DOUBLE, @@ -193,7 +195,7 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { char ip[IP_STRING_SIZE]; gn_leaf_param_get_string(leaf_config, GN_LEAF_INA219_PARAM_IP, ip, - IP_STRING_SIZE); + IP_STRING_SIZE); double port = 0; gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_PORT, &port); @@ -203,8 +205,8 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { &sampling_cycles); double sampling_interval = 0; - gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL, - &sampling_interval); + gn_leaf_param_get_double(leaf_config, + GN_LEAF_INA219_PARAM_SAMPLING_INTERVAL, &sampling_interval); double sda = 0; gn_leaf_param_get_double(leaf_config, GN_LEAF_INA219_PARAM_SDA, &sda); @@ -234,7 +236,7 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { esp_ret = ina219_configure(&data->dev, INA219_BUS_RANGE_16V, INA219_GAIN_0_125, INA219_RES_12BIT_1S, INA219_RES_12BIT_1S, - INA219_MODE_CONT_SHUNT); + INA219_MODE_CONT_SHUNT_BUS); ESP_LOGD(TAG, "ina219_configure: %s", esp_err_to_name(esp_ret)); esp_ret = ina219_calibrate(&data->dev, 5.0, 0.1); // 5A max current, 0.1 Ohm shunt resistance @@ -423,42 +425,45 @@ void gn_leaf_ina219_task(gn_leaf_handle_t leaf_config) { ESP_ERROR_CHECK(ina219_get_power(&data->dev, &power_instant)); power += power_instant; - vTaskDelay(pdMS_TO_TICKS(sampling_interval)); + vTaskDelay(pdMS_TO_TICKS(sampling_interval-0.5)); //0.6msec is the time needed to perform a 12bit measure 1 sample } //averaging - bus_voltage = bus_voltage / (float) sampling_cycles; - shunt_voltage = shunt_voltage / (float) sampling_cycles; - current = current / (float) sampling_cycles; - power = power / (float) sampling_cycles; + bus_voltage = (bus_voltage / (float) sampling_cycles) * 1000; + shunt_voltage = (shunt_voltage / (float) sampling_cycles) * 1000; + current = (current / (float) sampling_cycles) * 1000; + power = (power / (float) sampling_cycles) * 1000; ESP_LOGD(TAG, - "VBUS: %.04f V, VSHUNT: %.04f mV, IBUS: %.04f mA, PBUS: %.04f mW\n", - bus_voltage, shunt_voltage * 1000, current * 1000, - power * 1000); + "VBUS: %.04f mV, VSHUNT: %.04f mV, IBUS: %.04f mA, PBUS: %.04f mW\n", + bus_voltage, shunt_voltage, current, + power); if (working_mode == 0 || working_mode == 2) { - int err; - - snprintf(total_msg, 255, - "grownode,node=%s,leaf=%s power=%f,current=%f,vbus=%f,vshunt=%f,vload=%f", - node_name, leaf_name, power, current, bus_voltage, - shunt_voltage, (shunt_voltage + bus_voltage)); - - err = sendto(sock, total_msg, strlen(total_msg), 0, - (struct sockaddr*) &dest_addr, sizeof(dest_addr)); - if (err < 0) { - ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); - } - ESP_LOGD(TAG, "Message sent: %s to %s:%d", total_msg, ip, - (int )port); + int err; + + snprintf(total_msg, 255, + "grownode,node=%s,leaf=%s power=%f,current=%f,vbus=%f,vshunt=%f,vload=%f", + node_name, leaf_name, power, current, + bus_voltage, shunt_voltage, + (shunt_voltage + bus_voltage)); + + err = sendto(sock, total_msg, strlen(total_msg), 0, + (struct sockaddr*) &dest_addr, sizeof(dest_addr)); + if (err < 0) { + ESP_LOGE(TAG, "Error occurred during sending: errno %d", + errno); + } + ESP_LOGD(TAG, "Message sent: %s to %s:%d", total_msg, ip, + (int )port); } if (working_mode == 1 || working_mode == 2) { gn_leaf_param_write_double(leaf_config, - GN_LEAF_INA219_PARAM_VOLTAGE, (shunt_voltage + bus_voltage)); + GN_LEAF_INA219_PARAM_VOLTAGE, + (shunt_voltage + bus_voltage)); gn_leaf_param_write_double(leaf_config, GN_LEAF_INA219_PARAM_SHUNT_VOLTAGE, shunt_voltage); From ee4d72cca562a2fd78a6a249259fb7d25893e4e4 Mon Sep 17 00:00:00 2001 From: Nicola Muratori Date: Sun, 13 Mar 2022 20:45:18 +0100 Subject: [PATCH 31/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 510c0fba..8f580206 100755 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

-GrowNode is a vertical framework to build IoT devices targeted to growing plants in a controlled environment. +GrowNode is a vertical framework to build esp32 based devices targeted to growing plants in a controlled environment. See [Doc Website](https://ogghst.github.io/grownode/)