Merge branch 'task/cmd_response' into 'master'

esp_rmaker_core: Add framework for command-response workflow

See merge request app-frameworks/esp-rainmaker!207
This commit is contained in:
Piyush Shah
2022-07-19 01:47:00 +08:00
10 changed files with 191 additions and 5 deletions

2
cli

Submodule cli updated: e61d9528c1...85e7124161

View File

@@ -10,7 +10,9 @@ set(core_srcs "src/core/esp_rmaker_core.c"
"src/core/esp_rmaker_user_mapping.pb-c.c"
"src/core/esp_rmaker_user_mapping.c"
"src/core/esp_rmaker_schedule.c"
"src/core/esp_rmaker_scenes.c")
"src/core/esp_rmaker_scenes.c"
"src/core/esp_rmaker_cmd_resp_manager.c"
)
set(priv_req protobuf-c json_parser json_generator wifi_provisioning nvs_flash esp_http_client app_update esp-tls mbedtls esp_https_ota console esp_local_ctrl esp_https_server mdns esp_schedule efuse)

View File

@@ -242,4 +242,24 @@ menu "ESP RainMaker Config"
endmenu
menu "ESP RainMaker Command-Response"
config ESP_RMAKER_CMD_RESP_ENABLE
bool "Enable Command-Response Module"
default y
help
Enable the ESP RainMaker Command-Response module for semi-synchronous communication. Please refer the RainMaker documents
for additional information.
config ESP_RMAKER_CMD_RESP_TEST_ENABLE
bool "Enable Command-Response Testing"
default n
depends on ESP_RMAKER_CMD_RESP_ENABLE
help
Enable testing for Command-Response module. This enables triggering commands and parsing response from the node itself,
rather than receiving the commands from cloud. C API or the serial console can be used to trigger the commands.
This should be enabled only while testing commands, but should always be disabled in production firmware.
endmenu
endmenu

View File

@@ -931,6 +931,20 @@ bool esp_rmaker_local_ctrl_service_started(void);
* @return error on failure
*/
esp_err_t esp_rmaker_ota_enable_default(void);
/*
* Send a command to self (TESTING only)
*
* This is to be passed as an argument to esp_rmaker_cmd_resp_test_send().
*
* @param[in] cmd The TLV encoded command data.
* @param[in] cmd_len Length of the command data.
* @param[in] priv_data Private data passed to esp_rmaker_cmd_resp_test_send().
*
* @return ESP_OK on success
* @return error on failure
*/
esp_err_t esp_rmaker_test_cmd_resp(const void *cmd, size_t cmd_len, void *priv_data);
#ifdef __cplusplus
}
#endif

View File

@@ -34,6 +34,7 @@
#include <esp_rmaker_core.h>
#include <esp_rmaker_user_mapping.h>
#include <esp_rmaker_utils.h>
#include <esp_rmaker_cmd_resp.h>
#include <esp_rmaker_console_internal.h>
@@ -402,6 +403,31 @@ static void register_time_commands()
esp_console_cmd_register(&tz_set_cmd);
}
static int cmd_resp_cli_handler(int argc, char *argv[])
{
if (argc != 5) {
printf("Usage: cmd <req_id> <user_role> <cmd> <data>\n");
return -1;
}
char *req_id = argv[1];
uint8_t user_role = atoi(argv[2]);
uint16_t cmd = atoi(argv[3]);
esp_rmaker_cmd_resp_test_send(req_id, user_role, cmd, (void *)argv[4], strlen(argv[4]), esp_rmaker_test_cmd_resp, NULL);
return 0;
}
static void register_cmd_resp_command()
{
const esp_console_cmd_t cmd_resp_cmd = {
.command = "cmd",
.help = "Send command to command-response module. Usage cmd <req_id> <cmd> <user_role> <data>",
.func = &cmd_resp_cli_handler,
};
ESP_LOGI(TAG, "Registering command: %s", cmd_resp_cmd.command);
esp_console_cmd_register(&cmd_resp_cmd);
}
void register_commands()
{
register_generic_debug_commands();
@@ -410,4 +436,5 @@ void register_commands()
register_get_node_id();
register_wifi_prov();
register_time_commands();
register_cmd_resp_command();
}

View File

@@ -0,0 +1,113 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// 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 <sdkconfig.h>
#include <esp_log.h>
#include <string.h>
#include <esp_rmaker_mqtt.h>
#include <esp_rmaker_cmd_resp.h>
#include "esp_rmaker_internal.h"
#define TO_NODE_TOPIC_SUFFIX "to-node"
#define FROM_NODE_TOPIC_SUFFIX "from-node"
static const char *TAG = "esp_rmaker_cmd_resp";
#ifdef CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE
/* These are for testing purpose only */
static void esp_rmaker_resp_callback(const char *topic, void *payload, size_t payload_len, void *priv_data)
{
esp_rmaker_cmd_resp_parse_response(payload, payload_len, priv_data);
}
esp_err_t esp_rmaker_test_cmd_resp(const void *cmd, size_t cmd_len, void *priv_data)
{
if (!cmd) {
ESP_LOGE(TAG, "No command data to send.");
return ESP_ERR_INVALID_ARG;
}
char publish_topic[100];
snprintf(publish_topic, sizeof(publish_topic), "node/%s/%s", esp_rmaker_get_node_id(), TO_NODE_TOPIC_SUFFIX);
return esp_rmaker_mqtt_publish(publish_topic, cmd, cmd_len, RMAKER_MQTT_QOS1, NULL);
}
static esp_err_t esp_rmaker_cmd_resp_test_enable(void)
{
char subscribe_topic[100];
snprintf(subscribe_topic, sizeof(subscribe_topic), "node/%s/%s",
esp_rmaker_get_node_id(), FROM_NODE_TOPIC_SUFFIX);
esp_err_t err = esp_rmaker_mqtt_subscribe(subscribe_topic, esp_rmaker_resp_callback, RMAKER_MQTT_QOS1, NULL);
if(err != ESP_OK) {
ESP_LOGE(TAG, "Failed to subscribe to %s. Error %d", subscribe_topic, err);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Command-Response test support enabled.");
return ESP_OK;
}
#else
esp_err_t esp_rmaker_test_cmd_resp(const void *cmd, size_t cmd_len, void *priv_data)
{
ESP_LOGE(TAG, "Please enable CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE to use this.");
return ESP_FAIL;
}
#endif /* !CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE */
#ifdef CONFIG_ESP_RMAKER_CMD_RESP_ENABLE
static void esp_rmaker_cmd_callback(const char *topic, void *payload, size_t payload_len, void *priv_data)
{
void *output = NULL;
size_t output_len = 0;
/* Any command data received is directly sent to the command response framework and on success,
* the response (if any) is sent back to the MQTT Broker.
*/
if (esp_rmaker_cmd_response_handler(payload, payload_len, &output, &output_len) == ESP_OK) {
if (output) {
char publish_topic[100];
snprintf(publish_topic, sizeof(publish_topic), "node/%s/%s", esp_rmaker_get_node_id(), FROM_NODE_TOPIC_SUFFIX);
if (esp_rmaker_mqtt_publish(publish_topic, output, output_len, RMAKER_MQTT_QOS1, NULL) != ESP_OK) {
ESP_LOGE(TAG, "Failed to publish reponse.");
}
free(output);
} else {
ESP_LOGE(TAG, "No output generated by command-response handler.");
}
}
}
esp_err_t esp_rmaker_cmd_response_enable(void)
{
ESP_LOGI(TAG, "Enabling Command-Response Module.");
char subscribe_topic[100];
snprintf(subscribe_topic, sizeof(subscribe_topic), "node/%s/%s",
esp_rmaker_get_node_id(), TO_NODE_TOPIC_SUFFIX);
esp_err_t err = esp_rmaker_mqtt_subscribe(subscribe_topic, esp_rmaker_cmd_callback, RMAKER_MQTT_QOS1, NULL);
if(err != ESP_OK) {
ESP_LOGE(TAG, "Failed to subscribe to %s. Error %d", subscribe_topic, err);
return ESP_FAIL;
}
#ifdef CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE
esp_rmaker_cmd_resp_test_enable();
#endif /* CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE */
return ESP_OK;
}
#else
esp_err_t esp_rmaker_cmd_response_enable(void)
{
ESP_LOGW(TAG, "Command-Response Module not enabled. Set CONFIG_ESP_RMAKER_CMD_RESP_ENABLE=y to use this.");
return ESP_OK;
}
#endif /* !CONFIG_ESP_RMAKER_CMD_RESP_ENABLE */

View File

@@ -137,8 +137,9 @@ static void esp_rmaker_event_handler(void* arg, esp_event_base_t event_base,
} else if (event_base == RMAKER_EVENT &&
(event_id == RMAKER_EVENT_USER_NODE_MAPPING_DONE ||
event_id == RMAKER_EVENT_USER_NODE_MAPPING_RESET)) {
esp_rmaker_params_mqtt_init();
esp_event_handler_unregister(RMAKER_EVENT, event_id, &esp_rmaker_event_handler);
esp_rmaker_params_mqtt_init();
esp_rmaker_cmd_response_enable();
}
}
@@ -324,6 +325,11 @@ static void esp_rmaker_task(void *data)
ESP_LOGE(TAG, "Aborting!!!");
goto rmaker_end;
}
err = esp_rmaker_cmd_response_enable();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Aborting!!!");
goto rmaker_end;
}
} else {
/* If network is connected without even starting the user-node mapping workflow,
* it could mean that some incorrect app was used to provision the device. Even

View File

@@ -116,3 +116,4 @@ static inline esp_err_t esp_rmaker_post_event(esp_rmaker_event_t event_id, void*
return esp_event_post(RMAKER_EVENT, event_id, data, data_size, portMAX_DELAY);
}
esp_rmaker_state_t esp_rmaker_get_state(void);
esp_err_t esp_rmaker_cmd_response_enable(void);

View File

@@ -25,6 +25,7 @@ INPUT = \
../components/esp_rainmaker/include/esp_rmaker_core.h \
../components/esp_rainmaker/include/esp_rmaker_user_mapping.h \
../components/esp_rainmaker/include/esp_rmaker_schedule.h \
../components/esp_rainmaker/include/esp_rmaker_scenes.h \
## RainMaker Standard Types
../components/esp_rainmaker/include/esp_rmaker_standard_types.h \
../components/esp_rainmaker/include/esp_rmaker_standard_params.h \
@@ -40,7 +41,9 @@ INPUT = \
../components/rmaker_common/include/esp_rmaker_common_events.h \
../components/rmaker_common/include/esp_rmaker_factory.h \
../components/rmaker_common/include/esp_rmaker_work_queue.h \
../components/rmaker_common/include/esp_rmaker_utils.h
../components/rmaker_common/include/esp_rmaker_utils.h \
../components/rmaker_common/include/esp_rmaker_cmd_resp.h \
../components/rmaker_common/include/esp_rmaker_mqtt_glue.h
## Get warnings for functions that have no documentation for their parameters or return value
##