Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/rmaker init deinit (MEGH-6105) #341

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 62 additions & 11 deletions components/esp_rainmaker/src/core/esp_rmaker_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,32 @@ static esp_err_t esp_rmaker_deinit_priv_data(esp_rmaker_priv_data_t *rmaker_priv
free(rmaker_priv_data);
return ESP_OK;
}
static void esp_rmaker_end()
{
ESP_LOGW(TAG, "rmaker end was called..");
esp_err_t err = ESP_FAIL;
#ifdef CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE
err = esp_rmaker_local_ctrl_disable();
if (err != ESP_OK) {
ESP_LOGW(TAG, "Failed to disable local control service...");
}
#endif /* CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE */
if (esp_rmaker_priv_data->mqtt_connected) {
err = esp_rmaker_mqtt_disconnect();
}
esp_rmaker_mqtt_deinit();
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_INIT_DONE;
if (err == ESP_OK) {
ESP_LOGI(TAG, "esp_rmaker_end returned successfully");
}
else {
ESP_LOGE(TAG, "mqtt_disconnect failed!");
}
}

esp_err_t esp_rmaker_node_deinit(const esp_rmaker_node_t *node)
{
esp_rmaker_end();
if (!esp_rmaker_priv_data) {
ESP_LOGE(TAG, "ESP RainMaker already de-initialized.");
return ESP_ERR_INVALID_ARG;
Expand Down Expand Up @@ -420,7 +443,18 @@ static void esp_rmaker_task(void *data)
/* Check if already connected to Wi-Fi */
if (esp_wifi_sta_get_ap_info(&ap_info) != ESP_OK) {
/* Wait for Wi-Fi connection */
xEventGroupWaitBits(rmaker_core_event_group, WIFI_CONNECTED_EVENT, false, true, portMAX_DELAY);
do {
EventBits_t evt_bits = xEventGroupWaitBits(
rmaker_core_event_group, WIFI_CONNECTED_EVENT, false, true, pdMS_TO_TICKS(2*1000));
if (evt_bits) {
break; /* We got connected */
}
if (esp_rmaker_priv_data->state == ESP_RMAKER_STATE_STOP_REQUESTED) {
ESP_LOGI(TAG, "Rainmaker stop was requested. Aborting!");
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &esp_rmaker_event_handler);
goto rmaker_end;
}
} while (true);
}
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &esp_rmaker_event_handler);
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD) /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
Expand Down Expand Up @@ -494,7 +528,18 @@ static void esp_rmaker_task(void *data)
goto rmaker_end;
}
ESP_LOGI(TAG, "Waiting for MQTT connection");
xEventGroupWaitBits(rmaker_core_event_group, MQTT_CONNECTED_EVENT, false, true, portMAX_DELAY);
do {
EventBits_t evt_bits = xEventGroupWaitBits(
rmaker_core_event_group, MQTT_CONNECTED_EVENT, false, true, pdMS_TO_TICKS(2 * 1000));
if (evt_bits) {
break; /* We got connected */
}
if (esp_rmaker_priv_data->state == ESP_RMAKER_STATE_STOP_REQUESTED) {
ESP_LOGI(TAG, "Rainmaker stop was requested. Aborting!");
esp_event_handler_unregister(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &esp_rmaker_event_handler);
goto rmaker_end;
}
} while (true);
esp_event_handler_unregister(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &esp_rmaker_event_handler);
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_STARTED;
err = esp_rmaker_report_node_config();
Expand Down Expand Up @@ -533,19 +578,13 @@ static void esp_rmaker_task(void *data)
ESP_LOGI(TAG, "Waiting for User Node Association.");
}
err = ESP_OK;

rmaker_end:
if (rmaker_core_event_group) {
vEventGroupDelete(rmaker_core_event_group);
}
rmaker_core_event_group = NULL;
if (err == ESP_OK) {
return;
}
if (esp_rmaker_priv_data->mqtt_connected) {
esp_rmaker_mqtt_disconnect();
}
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_INIT_DONE;
return;
rmaker_end:
esp_rmaker_end();
}


Expand Down Expand Up @@ -709,6 +748,18 @@ esp_err_t esp_rmaker_start(void)
esp_err_t esp_rmaker_stop()
{
ESP_RMAKER_CHECK_HANDLE(ESP_ERR_INVALID_STATE);
/* Since, rmaker_work_queue might be getting used out of rainmaker, we do not stop it.
* Just, remove the reset handler...
*/
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_unregister(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, &reset_event_handler));

if (esp_rmaker_priv_data->state == ESP_RMAKER_STATE_STARTED) {
esp_rmaker_end();
ESP_LOGI(TAG, "RainMaker stopped");
return ESP_OK;
}
/* rmaker_task is still executing. rely on that task for stop. */
ESP_LOGI(TAG, "Rainmaker stop was requested, from state %d", esp_rmaker_priv_data->state);
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_STOP_REQUESTED;
return ESP_OK;
}
5 changes: 4 additions & 1 deletion components/esp_rainmaker/src/core/esp_rmaker_node.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "esp_rmaker_internal.h"

static const char *TAG = "esp_rmaker_node";
static bool node_created;

static void esp_rmaker_node_info_free(esp_rmaker_node_info_t *info)
{
Expand Down Expand Up @@ -84,6 +85,7 @@ esp_err_t esp_rmaker_node_delete(const esp_rmaker_node_t *node)
_esp_rmaker_device_t *next_device = device->next;
device->parent = NULL;
esp_rmaker_device_delete((esp_rmaker_device_t *)device);
free(device);
device = next_device;
}
/* Node ID is created in the context of esp_rmaker_init and just assigned
Expand All @@ -95,14 +97,15 @@ esp_err_t esp_rmaker_node_delete(const esp_rmaker_node_t *node)
if (_node->info) {
esp_rmaker_node_info_free(_node->info);
}
free(_node);
node_created = false;
return ESP_OK;
}
return ESP_ERR_INVALID_ARG;
}

esp_rmaker_node_t *esp_rmaker_node_create(const char *name, const char *type)
{
static bool node_created;
if (node_created) {
ESP_LOGE(TAG, "Node has already been created. Cannot create another");
return NULL;
Expand Down
7 changes: 7 additions & 0 deletions components/esp_rainmaker/src/core/esp_rmaker_param.c
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,13 @@ esp_err_t esp_rmaker_param_delete(const esp_rmaker_param_t *param)
if (_param->ui_type) {
free(_param->ui_type);
}
if (_param->bounds) {
free(_param->bounds);
}
if (_param->val.type == RMAKER_VAL_TYPE_STRING || _param->val.type == RMAKER_VAL_TYPE_OBJECT ||
_param->val.type == RMAKER_VAL_TYPE_ARRAY) {
free(_param->val.val.s);
}
free(_param);
return ESP_OK;
}
Expand Down
4 changes: 4 additions & 0 deletions components/esp_rainmaker/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(SRCS "test_rmaker.c"
INCLUDE_DIRS "."
PRIV_INCLUDE_DIRS . ${CMAKE_CURRENT_BINARY_DIR}
PRIV_REQUIRES unity nvs_flash esp_rainmaker app_network app_insights test_utils espressif__network_provisioning)
73 changes: 73 additions & 0 deletions components/esp_rainmaker/test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# esp_rainmaker unit tests

Please take a look at how to build, flash, and run [esp-idf unit tests](https://github.com/espressif/esp-idf/tree/master/tools/unit-test-app#unit-test-app).

Follow the steps mentioned below to unit test the esp_rainmaker

* Change to the unit test app directory
```
cd $IDF_PATH/tools/unit-test-app
```

* Set RMAKER_PATH to esp-rainmaker directory
* Note: This is cloned `espressif/esp-rainmaker` directory and not its internal component `esp_rainmaker`
```
export RMAKER_PATH=/path/to/esp-rainmaker
```

* Add following line to partition table csv file
```
fctry, data, nvs, , 0x6000
```

* Copy following contents into `sdkconfig.defaults`
```
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table_unit_test_app.csv"

# mbedtls
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
CONFIG_MBEDTLS_DYNAMIC_FREE_PEER_CERT=y
CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y

# For BLE Provisioning using NimBLE stack (Not applicable for ESP32-S2)
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BT_NIMBLE_ENABLED=y

# Temporary Fix for Timer Overflows
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120

# For additional security on reset to factory
CONFIG_ESP_RMAKER_USER_ID_CHECK=y

# Secure Local Control
CONFIG_ESP_RMAKER_LOCAL_CTRL_AUTO_ENABLE=y
#CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE is deprecated but will continue to work
CONFIG_ESP_RMAKER_LOCAL_CTRL_SECURITY_1=y

# Application Rollback
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y

# If ESP-Insights is enabled, we need MQTT transport selected
# Takes out manual efforts to enable this option
CONFIG_ESP_INSIGHTS_TRANSPORT_MQTT=y

```
* Append `/path/to/esp-rainmaker/components` and `/path/to/esp-rainmaker/examples/common` directory to `EXTRA_COMPONENT_DIRS` in `CMakeLists.txt`

## Build, flash and run tests
```
# Clean any previous configuration and builds
rm -r sdkconfig build

# Set the target
idf.py set-target esp32s3

# Building the firmware
idf.py -T esp_rainmaker build

# Flash and run the test cases
idf.py -p <serial-port> -T esp_rainmaker flash monitor
```
146 changes: 146 additions & 0 deletions components/esp_rainmaker/test/test_rmaker.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <esp_random.h>
#include <unity.h>
#include "memory_checks.h"

#include <esp_rmaker_console.h>
#include <esp_rmaker_core.h>
#include <esp_rmaker_standard_params.h>
#include <esp_rmaker_standard_devices.h>
#include <esp_rmaker_schedule.h>
#include <esp_rmaker_console.h>
#include <esp_rmaker_scenes.h>

#include <network_provisioning/manager.h>

#include "app_network.h"
#include "app_insights.h"

esp_rmaker_device_t *light_device;

static const char * TAG = "test_rmaker";
bool config_done = false;
static const int repeat = 1;

#define DEFAULT_POWER true
#define DEFAULT_HUE 180
#define DEFAULT_SATURATION 100
#define DEFAULT_BRIGHTNESS 25

static void init_nvs_flash(void)
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_LOGI(TAG, "nvs_flash_init");
TEST_ASSERT(err == ESP_OK);
}

static void test_config(void)
{
if (!config_done) {
init_nvs_flash();
app_network_init();
test_utils_record_free_mem();
config_done = true;
}
}

/* Callback to handle param updates received from the RainMaker cloud */
static esp_err_t bulk_write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_write_req_t write_req[],
uint8_t count, void *priv_data, esp_rmaker_write_ctx_t *ctx)
{
if (ctx) {
ESP_LOGI(TAG, "Received write request via : %s", esp_rmaker_device_cb_src_to_str(ctx->src));
}
ESP_LOGI(TAG, "Light received %d params in write", count);
// We will return ESP_OK as testing callbacks is currently out of scope of this test
return ESP_OK;
}

TEST_CASE("nvs init deinit", "[rmaker]")
{
init_nvs_flash();
nvs_flash_deinit();
}

TEST_CASE("node init", "[rmaker]")
{
test_config();
for (int i = 0; i < repeat; i++) {
esp_rmaker_config_t rainmaker_cfg = {
.enable_time_sync = false,
};
esp_rmaker_node_t *node = esp_rmaker_node_init(&rainmaker_cfg, "ESP RainMaker Device", "Lightbulb");
TEST_ASSERT(node != NULL);

vTaskDelay(1000 / portTICK_PERIOD_MS);

TEST_ASSERT(esp_rmaker_node_deinit(node) == ESP_OK);
}
}

TEST_CASE("add device", "[rmaker]")
{
test_config();
for (int i = 0; i < repeat; i++) {
esp_rmaker_config_t rainmaker_cfg = {
.enable_time_sync = false,
};
esp_rmaker_node_t *node = esp_rmaker_node_init(&rainmaker_cfg, "ESP RainMaker Device", "Lightbulb");
TEST_ASSERT(node != NULL);

light_device = esp_rmaker_lightbulb_device_create("Light", NULL, DEFAULT_POWER);
TEST_ASSERT(light_device != NULL);

esp_rmaker_device_add_param(light_device, esp_rmaker_brightness_param_create(ESP_RMAKER_DEF_BRIGHTNESS_NAME, DEFAULT_BRIGHTNESS));

int ret = esp_rmaker_node_add_device(node, light_device);

TEST_ASSERT(ret == ESP_OK);

vTaskDelay(1000 / portTICK_PERIOD_MS);

TEST_ASSERT(esp_rmaker_node_deinit(node) == ESP_OK);
}
}

TEST_CASE("rmaker start stop", "[rmaker]")
{
test_config();
for (int i = 0; i < repeat; i++) {
esp_rmaker_config_t rainmaker_cfg = {
.enable_time_sync = false,
};
esp_rmaker_node_t *node = esp_rmaker_node_init(&rainmaker_cfg, "ESP RainMaker Device", "Lightbulb");
TEST_ASSERT(node != NULL);

light_device = esp_rmaker_lightbulb_device_create("Light", NULL, DEFAULT_POWER);
TEST_ASSERT(light_device != NULL);

TEST_ASSERT(esp_rmaker_device_add_bulk_cb(light_device, bulk_write_cb, NULL) == ESP_OK);

esp_rmaker_device_add_param(light_device, esp_rmaker_brightness_param_create(ESP_RMAKER_DEF_BRIGHTNESS_NAME, DEFAULT_BRIGHTNESS));
esp_rmaker_device_add_param(light_device, esp_rmaker_hue_param_create(ESP_RMAKER_DEF_HUE_NAME, DEFAULT_HUE));
esp_rmaker_device_add_param(light_device, esp_rmaker_saturation_param_create(ESP_RMAKER_DEF_SATURATION_NAME, DEFAULT_SATURATION));

int ret = esp_rmaker_node_add_device(node, light_device);
TEST_ASSERT(ret == ESP_OK);

TEST_ASSERT(esp_rmaker_start() == ESP_OK);

TEST_ASSERT(app_network_start(POP_TYPE_RANDOM) == ESP_OK);

ESP_LOGD(TAG, "Will wait for 1 seconds before calling esp_rmaker_stop");
vTaskDelay(5*1000 / portTICK_PERIOD_MS);

TEST_ASSERT(esp_rmaker_stop() == ESP_OK);
TEST_ASSERT(esp_rmaker_node_deinit(node) == ESP_OK);
}
}