Command Response
The Command-Response framework in ESP RainMaker is an alternative to the set parameters workflow to send data from clients (like phone apps) to devices. It can be used by admin as well as end users. Some advantages of using this framework
- Get status of any request reliably
- Allow setting a validity for the request so that a request can be triggered even when a device is offline
- Provide better access control since the node knows the role of the user who triggered the command (admin, primary, secondary)
High Level Workflow
The diagram below explains how command - response works
Specifications
Client side
Client to cloud communication uses REST APIs with JSON Payload. To invoke a command, a POST API is invoked with this payload:
{
"node_ids": [
"6jyMKuFuAq3xxxxxxxxyBQ"
],
"cmd": 1,
"data": {
"brightness": 10
},
"timeout": 30
}
- node_ids: List of node ids to which the command is to be sent.
- cmd: The command id.
- data: The data payload for the command.
- timeout: The timeout (in seconds) for which the command is valid.
For passing binary data to the nodes, send it as a base64 string and add is_base64 as an additional key. Eg.
: {
"node_ids": [
"6jyMKuFuAq3xxxxxxxxyBQ"
],
"cmd": 1,
"data": "YWJjZAo="
"timeout": 30,
"is_base64": true
}
The cloud backend assigns a request id for this and responds with the following:
{
"request_id": "6jyMKuFxxxxxxxxStTKyBQ",
"status": "success"
}
The client can then query for the status of this request using the GET API, passing the above request id as a query parameter. The status can be: requested
, in_progress
, success
, timed_out
, or failure
.
For success
and failure
, the corresponding response value will also be returned.
Device side
Device to cloud communication uses TLV8, which is a variant of TLV encoding with 8bit length field. This allows flexibility in communication, as the application data can be JSON, XML, binary or any other convenient format.
Types
Type | Macro in code | Description | Length |
---|---|---|---|
1 | ESP_RMAKER_TLV_TYPE_REQ_ID | Request Id | Variable length string, max 32 characters |
2 | ESP_RMAKER_TLV_TYPE_USER_ROLE | User Role | 1 byte |
3 | ESP_RMAKER_TLV_TYPE_STATUS | Status | 1 byte |
4 | ESP_RMAKER_TLV_TYPE_TIMESTAMP | Timestamp | TBD (Unused) |
5 | ESP_RMAKER_TLV_TYPE_CMD | Command | 2 bytes (little endian) |
6 | ESP_RMAKER_TLV_TYPE_DATA | Data | Variable length |
Roles
These are mapped to bits
Value | Macro in code | Description |
---|---|---|
1 | ESP_RMAKER_USER_ROLE_SUPER_ADMIN | Super admin |
2 | ESP_RMAKER_USER_ROLE_PRIMARY_USER | Primary End User |
4 | ESP_RMAKER_USER_ROLE_SECONDARY_USER | Secondary End User |
3: Secondary user
Values for Status
Value | Macro in Code | Description |
---|---|---|
0 | ESP_RMAKER_CMD_STATUS_SUCCESS | Success |
1 | ESP_RMAKER_CMD_STATUS_FAILED | Generic Failure |
2 | ESP_RMAKER_CMD_STATUS_CMD_INVALID | Invalid Command |
3 | ESP_RMAKER_CMD_STATUS_AUTH_FAIL | Authentication Failed |
4 | ESP_RMAKER_CMD_STATUS_NOT_FOUND | Command not found |
Usage
In the firmware application code, enable command-response by setting CONFIG_ESP_RMAKER_CMD_RESP_ENABLE=y
in sdkconfig. Thereafter, just calling the API esp_rmaker_cmd_register()
is sufficient to register a command.
Definitions of the API and callback prototype:
esp_err_t esp_rmaker_cmd_register(uint16_t cmd, uint8_t access, esp_rmaker_cmd_handler_t handler, bool free_on_return, void *priv);
typedef esp_err_t (*esp_rmaker_cmd_handler_t)(const void *in_data, size_t in_len, void **out_data, size_t *out_len, esp_rmaker_cmd_ctx_t *ctx, void *priv);
- cmd: Command Identifier. Custom commands should use ids beyond ESP_RMAKER_CMD_CUSTOM_START (0x1000)
- access: User Access for the command. Can be a logical OR of the various user role flags like ESP_RMAKER_USER_ROLE_SUPER_ADMIN, ESP_RMAKER_USER_ROLE_PRIMARY_USER and ESP_RMAKER_USER_ROLE_SECONDARY_USER
- handler: The handler to be invoked when the given command is received.
- free_on_return: Flag to indicate if the framework should free the output after it has been sent as response.
- priv: Optional private data to be passed to the handler.
The handler esp_rmaker_cmd_handler_t
has following arguments:
- in_data: Pointer to input data.
- in_len: Data length.
- out_data: Pointer to output data which should be set by the handler.
- out_len: Length of output generated.
- ctx: Command Context. Includes command id, request id and role of the user who invoked the command.
- priv: Private data, if specified while registering command.
Example
#define LIGHT_CMD ESP_RMAKER_CMD_CUSTOM_START /* Custom commands should start from here */
/* Callback for command */
esp_err_t led_light_cmd_handler(const void *in_data, size_t in_len, void **out_data, size_t *out_len, esp_rmaker_cmd_ctx_t *ctx, void *priv)
{
ESP_LOGI(TAG, "Got command: %.*s", in_len, (char *)in_data);
/* Sending dummy response */
static char resp_data[100];
snprintf(resp_data, sizeof(resp_data), "{\"status\":\"success\"}");
*out_data = resp_data;
*out_len = strlen(resp_data);
return ESP_OK;
}
void app_main()
{
...
/* Register a command accessible to primary as well as secondary user (but not admin) */
esp_rmaker_cmd_register(LIGHT_CMD, ESP_RMAKER_USER_ROLE_PRIMARY_USER | ESP_RMAKER_USER_ROLE_SECONDARY_USER, led_light_cmd_handler, false, NULL);
...
}
Testing
The best way to test command response framework is via RainMaker CLI
Creating a Request
esp-rainmaker-cli create_cmd_request -h
usage: usage: esp-rainmaker-cli create_cmd_request [-h] (Beta)
Create command response requests for the node(s) associated with the current (logged-in) user. The format of this command might change in future.
positional arguments:
nodes Node Ids of the node(s) format: <nodeid1>,<nodeid2>,...,<nodeid25>
cmd ID of the command response request
data JSON data containing parameters to be sent to the node(s). Note: Enter JSON data in single quotes
optional arguments:
-h, --help show this help message and exit
--timeout TIMEOUT Time in seconds till which the command response request will be valid
Eg.
$ esp-rainmaker-cli create_cmd_request --timeout 60 6055F97E2008 4096 '{"brightness":50}'
Request Id: BK00t2QNe7oT12dBdh9f8X
Status: success
Monitoring Response
$ esp-rainmaker-cli get_cmd_requests -h
usage: usage: esp-rainmaker-cli get_cmd_requests [-h] (Beta)
Get command response requests created by current (logged-in) user. The format of this command might change in future.
positional arguments:
request_id ID of the command response request
optional arguments:
-h, --help show this help message and exit
--node_id NODE_ID Node Id of the node
--start_id START_ID Start Id used for pagination. This should be the Next Id received in the previous batch
--num_records NUM_RECORDS Number of requests to get
Eg.
Command in Progress:
$ esp-rainmaker-cli get_cmd_requests BK00t2QNe7oT12dBdh9f8X
Requests: [{'node_id': '6055F97E2008', 'request_id': 'BK00t2QNe7oT12dBdh9f8X', 'request_timestamp': 1685382006, 'status': 'in_progress', 'expiration_timestamp': 1685382066}]
Total: 1
Command completed:
$ esp-rainmaker-cli get_cmd_requests BK00t2QNe7oT12dBdh9f8X
Requests: [{'node_id': '6055F97E2008', 'request_id': 'BK00t2QNe7oT12dBdh9f8X', 'request_timestamp': 1685382006, 'response_timestamp': 1685382039, 'response_data': {'status': 'success'}, 'status': 'success', 'device_status': 0, 'expiration_timestamp': 1685382066}]
More Info
- REST API usage information can be found in the Swagger documentation.
- Sample firmware usage can be found in the led_light example. Check here for a sample command handler.