Initial commit.
Some checks failed
Sync remain PRs to Jira / Sync PRs to Jira (push) Has been cancelled
Some checks failed
Sync remain PRs to Jira / Sync PRs to Jira (push) Has been cancelled
This commit is contained in:
429
components/rmaker_common/src/cmd_resp.c
Normal file
429
components/rmaker_common/src/cmd_resp.c
Normal file
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sdkconfig.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_rmaker_cmd_resp.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
#define RMAKER_MAX_CMD CONFIG_ESP_RMAKER_MAX_COMMANDS
|
||||
|
||||
static const char *TAG = "esp_rmaker_common_cmd_resp";
|
||||
|
||||
typedef struct {
|
||||
uint16_t cmd;
|
||||
uint8_t access;
|
||||
bool free_on_return;
|
||||
esp_rmaker_cmd_handler_t handler;
|
||||
void *priv;
|
||||
} esp_rmaker_cmd_info_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *bufptr;
|
||||
int bufsize;
|
||||
int curlen;
|
||||
} esp_rmaker_tlv_data_t;
|
||||
|
||||
static esp_rmaker_cmd_info_t *esp_rmaker_cmd_list[RMAKER_MAX_CMD];
|
||||
|
||||
/* Get uint16 from Little Endian data buffer */
|
||||
static uint16_t get_u16_le(const void *val_ptr)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *) val_ptr;
|
||||
uint16_t val;
|
||||
|
||||
val = (uint16_t)p[0];
|
||||
val |= (uint16_t)p[1] << 8;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Put uint16 in Little Endian ordering into data buffer */
|
||||
static void put_u16_le(void *val_ptr, const uint16_t val)
|
||||
{
|
||||
uint8_t *p = (uint8_t *) val_ptr;
|
||||
|
||||
p[0] = (uint8_t)val & 0xff;
|
||||
p[1] = (uint8_t)(val >> 8) & 0xff;
|
||||
}
|
||||
|
||||
/* Initialise TLV Data */
|
||||
static void esp_rmaker_tlv_data_init(esp_rmaker_tlv_data_t *tlv_data, uint8_t *buf, int buf_size)
|
||||
{
|
||||
tlv_data->bufptr = buf;
|
||||
tlv_data->bufsize = buf_size;
|
||||
tlv_data->curlen = 0;
|
||||
}
|
||||
|
||||
/* Get length of data, for given type.
|
||||
*
|
||||
* Returns length of data on success and -1 if the TLV was not found
|
||||
*/
|
||||
static int esp_rmaker_get_tlv_length(const uint8_t *buf, int buflen, uint8_t type)
|
||||
{
|
||||
if (!buf ) {
|
||||
return -1;
|
||||
}
|
||||
int curlen = 0;
|
||||
int val_len = 0;
|
||||
bool found = false;
|
||||
while (buflen > 0) {
|
||||
if (buf[curlen] == type) {
|
||||
uint8_t len = buf[curlen + 1];
|
||||
if ((buflen - len) < 2) {
|
||||
return -1;
|
||||
}
|
||||
val_len += len;
|
||||
if (len < 255) {
|
||||
return val_len;
|
||||
} else {
|
||||
found = true;
|
||||
}
|
||||
|
||||
} else if (found) {
|
||||
return val_len;
|
||||
}
|
||||
|
||||
/* buf[curlen +1] will give the Length */
|
||||
buflen -= (2 + buf[curlen + 1]);
|
||||
curlen += (2 + buf[curlen + 1]);
|
||||
}
|
||||
if (found) {
|
||||
return val_len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the value for the given type.
|
||||
*
|
||||
* Returns length of data on success and -1 if the TLV was not found
|
||||
*/
|
||||
static int esp_rmaker_get_value_from_tlv(const uint8_t *buf, int buflen, uint8_t type, void *val, int val_size)
|
||||
{
|
||||
if (!buf || !val) {
|
||||
return -1;
|
||||
}
|
||||
int curlen = 0;
|
||||
int val_len = 0;
|
||||
bool found = false;
|
||||
while (buflen > 0) {
|
||||
if (buf[curlen] == type) {
|
||||
uint8_t len = buf[curlen + 1];
|
||||
if ((val_size < len) || ((buflen - len) < 2)) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(val + val_len, &buf[curlen + 2], len);
|
||||
val_len += len;
|
||||
val_size -= len;
|
||||
if (len < 255) {
|
||||
return val_len;
|
||||
} else {
|
||||
found = true;
|
||||
}
|
||||
|
||||
} else if (found) {
|
||||
return val_len;
|
||||
}
|
||||
|
||||
/* buf[curlen +1] will give the Length */
|
||||
buflen -= (2 + buf[curlen + 1]);
|
||||
curlen += (2 + buf[curlen + 1]);
|
||||
}
|
||||
if (found) {
|
||||
return val_len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Add a TLV to the TLV buffer */
|
||||
static int esp_rmaker_add_tlv(esp_rmaker_tlv_data_t *tlv_data, uint8_t type, int len, const void *val)
|
||||
{
|
||||
if (!tlv_data->bufptr || ((len + 2) > (tlv_data->bufsize - tlv_data->curlen))) {
|
||||
return -1;
|
||||
}
|
||||
if (len > 0 && val == NULL) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t *buf_ptr = (uint8_t *)val;
|
||||
int orig_len = tlv_data->curlen;
|
||||
do {
|
||||
tlv_data->bufptr[tlv_data->curlen++] = type;
|
||||
int tmp_len;
|
||||
if (len > 255) {
|
||||
tmp_len = 255;
|
||||
} else {
|
||||
tmp_len = len;
|
||||
}
|
||||
tlv_data->bufptr[tlv_data->curlen++] = tmp_len;
|
||||
memcpy(&tlv_data->bufptr[tlv_data->curlen], buf_ptr, tmp_len);
|
||||
tlv_data->curlen += tmp_len;
|
||||
buf_ptr += tmp_len;
|
||||
len -= tmp_len;
|
||||
} while (len);
|
||||
return tlv_data->curlen - orig_len;
|
||||
}
|
||||
|
||||
/* Get user role string from flag. Useful for printing */
|
||||
const char *esp_rmaker_get_user_role_string(uint8_t user_role)
|
||||
{
|
||||
switch (user_role) {
|
||||
case ESP_RMAKER_USER_ROLE_SUPER_ADMIN:
|
||||
return "Admin";
|
||||
case ESP_RMAKER_USER_ROLE_PRIMARY_USER:
|
||||
return "Primary";
|
||||
case ESP_RMAKER_USER_ROLE_SECONDARY_USER:
|
||||
return "Secondary";
|
||||
default:
|
||||
return "Invalid Role";
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare the response TLV8 which includes
|
||||
*
|
||||
* Request Id
|
||||
* Status
|
||||
* Command Id
|
||||
* Response Data
|
||||
*/
|
||||
static esp_err_t esp_rmaker_cmd_prepare_response(esp_rmaker_cmd_ctx_t *cmd_ctx, uint8_t status, void *response, size_t response_size, void **output, size_t *output_len)
|
||||
{
|
||||
size_t publish_size = response_size + 100; /* +100 for rest of metadata. TODO: Do exact calculation */
|
||||
void *publish_data = MEM_CALLOC_EXTRAM(1, publish_size);
|
||||
if (!publish_data) {
|
||||
ESP_LOGE(TAG, "Failed to allocate buffer of size %d for response.", response_size + 100);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
esp_rmaker_tlv_data_t tlv_data;
|
||||
esp_rmaker_tlv_data_init(&tlv_data, publish_data, publish_size);
|
||||
if (strlen(cmd_ctx->req_id)) {
|
||||
esp_rmaker_add_tlv(&tlv_data, ESP_RMAKER_TLV_TYPE_REQ_ID, strlen(cmd_ctx->req_id), cmd_ctx->req_id);
|
||||
}
|
||||
esp_rmaker_add_tlv(&tlv_data, ESP_RMAKER_TLV_TYPE_STATUS, sizeof(status), &status);
|
||||
uint8_t cmd_buf[2];
|
||||
put_u16_le(cmd_buf, cmd_ctx->cmd);
|
||||
esp_rmaker_add_tlv(&tlv_data, ESP_RMAKER_TLV_TYPE_CMD, sizeof(cmd_buf), cmd_buf);
|
||||
if (response != NULL && response_size != 0) {
|
||||
esp_rmaker_add_tlv(&tlv_data, ESP_RMAKER_TLV_TYPE_DATA, response_size, response);
|
||||
}
|
||||
ESP_LOGD(TAG, "Generated response of size %d for cmd %d", tlv_data.curlen, cmd_ctx->cmd);
|
||||
*output = publish_data;
|
||||
*output_len = tlv_data.curlen;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_cmd_prepare_empty_response(void **output, size_t *output_len)
|
||||
{
|
||||
size_t publish_size = 6; /* unit16 cmd = 0 (4 bytes in TLV), req_id = empty (2 bytes) */
|
||||
void *publish_data = MEM_CALLOC_EXTRAM(1, publish_size);
|
||||
if (!publish_data) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
esp_rmaker_tlv_data_t tlv_data;
|
||||
esp_rmaker_tlv_data_init(&tlv_data, publish_data, publish_size);
|
||||
esp_rmaker_add_tlv(&tlv_data, ESP_RMAKER_TLV_TYPE_REQ_ID, 0, NULL);
|
||||
uint8_t cmd_buf[2];
|
||||
put_u16_le(cmd_buf, 0);
|
||||
esp_rmaker_add_tlv(&tlv_data, ESP_RMAKER_TLV_TYPE_CMD, sizeof(cmd_buf), cmd_buf);
|
||||
*output = publish_data;
|
||||
*output_len = tlv_data.curlen;
|
||||
ESP_LOGD(TAG, "Generated empty response for requesting pending commands.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Register a new command with its handler
|
||||
*/
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < RMAKER_MAX_CMD; i++) {
|
||||
if (esp_rmaker_cmd_list[i] && (esp_rmaker_cmd_list[i]->cmd == cmd)) {
|
||||
ESP_LOGE(TAG, "Handler for command %d already exists.", cmd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < RMAKER_MAX_CMD; i++) {
|
||||
if (!esp_rmaker_cmd_list[i]) {
|
||||
esp_rmaker_cmd_info_t *cmd_info = calloc(1, sizeof(esp_rmaker_cmd_info_t));
|
||||
if (!cmd_info) {
|
||||
ESP_LOGE(TAG, "Could not allocate memory for cmd %d", cmd);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
cmd_info->cmd = cmd;
|
||||
cmd_info->access = access;
|
||||
cmd_info->free_on_return = free_on_return;
|
||||
cmd_info->handler = handler;
|
||||
cmd_info->priv = priv;
|
||||
esp_rmaker_cmd_list[i] = cmd_info;
|
||||
ESP_LOGI(TAG, "Registered command %d", cmd);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
ESP_LOGE(TAG, "No space to add command %d", cmd);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
/* Find the command infor for given command
|
||||
*
|
||||
* Returns pointer to the info if found and NULL on error
|
||||
*/
|
||||
static esp_rmaker_cmd_info_t *esp_rmaker_get_cmd_info(uint16_t cmd)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < RMAKER_MAX_CMD; i++) {
|
||||
if (esp_rmaker_cmd_list[i] && (esp_rmaker_cmd_list[i]->cmd == cmd)) {
|
||||
ESP_LOGI(TAG, "Handler found for command %d.", cmd);
|
||||
return esp_rmaker_cmd_list[i];
|
||||
}
|
||||
}
|
||||
ESP_LOGE(TAG, "No handler found for command %d.", cmd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* De-register given command */
|
||||
esp_err_t esp_rmaker_cmd_deregister(uint16_t cmd)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < RMAKER_MAX_CMD; i++) {
|
||||
if (esp_rmaker_cmd_list[i] && (esp_rmaker_cmd_list[i]->cmd == cmd)) {
|
||||
free(esp_rmaker_cmd_list[i]);
|
||||
esp_rmaker_cmd_list[i] = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
ESP_LOGE(TAG, "Cannot unregister command %d as it wasn't registered.", cmd);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Main command response handling function.
|
||||
*
|
||||
* It parses the rceived data to find the command and other metadata and
|
||||
* prepares the response to be sent
|
||||
*/
|
||||
esp_err_t esp_rmaker_cmd_response_handler(const void *input, size_t input_len, void **output, size_t *output_len)
|
||||
{
|
||||
esp_rmaker_cmd_ctx_t cmd_ctx = {0};
|
||||
|
||||
/* Read request id, user role and command, since these are mandatory fields */
|
||||
esp_rmaker_get_value_from_tlv(input, input_len, ESP_RMAKER_TLV_TYPE_REQ_ID, &cmd_ctx.req_id, sizeof(cmd_ctx.req_id));
|
||||
esp_rmaker_get_value_from_tlv(input, input_len, ESP_RMAKER_TLV_TYPE_USER_ROLE, &cmd_ctx.user_role, sizeof(cmd_ctx.user_role));
|
||||
uint8_t cmd_buf[2] = {0};
|
||||
esp_rmaker_get_value_from_tlv(input, input_len, ESP_RMAKER_TLV_TYPE_CMD, cmd_buf, sizeof(cmd_buf));
|
||||
cmd_ctx.cmd = get_u16_le(cmd_buf);
|
||||
|
||||
if (strlen(cmd_ctx.req_id) == 0 || cmd_ctx.user_role == 0 || cmd_ctx.cmd == 0) {
|
||||
ESP_LOGE(TAG, "Request id, user role or command id cannot be 0");
|
||||
return esp_rmaker_cmd_prepare_response(&cmd_ctx, ESP_RMAKER_CMD_STATUS_CMD_INVALID, NULL, 0, output, output_len);
|
||||
}
|
||||
ESP_LOGI(TAG, "Got Req. Id: %s, Role = %s, Cmd = %d", cmd_ctx.req_id,
|
||||
esp_rmaker_get_user_role_string(cmd_ctx.user_role), cmd_ctx.cmd);
|
||||
|
||||
/* Search for the command info and handle it if found */
|
||||
esp_rmaker_cmd_info_t *cmd_info = esp_rmaker_get_cmd_info(cmd_ctx.cmd);
|
||||
if (cmd_info) {
|
||||
if (cmd_info->access & cmd_ctx.user_role) {
|
||||
void *data = NULL;
|
||||
int data_size = esp_rmaker_get_tlv_length(input, input_len, ESP_RMAKER_TLV_TYPE_DATA);
|
||||
if (data_size > 0) {
|
||||
/* TODO: If data size < 255, can just use the pointer to input */
|
||||
data = MEM_CALLOC_EXTRAM(1, data_size);
|
||||
if (!data) {
|
||||
ESP_LOGE(TAG, "Failed to allocate buffer of size %d for data.", data_size);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
esp_rmaker_get_value_from_tlv(input, input_len, ESP_RMAKER_TLV_TYPE_DATA, data, data_size);
|
||||
} else {
|
||||
/* It is not mandatory to have data for a given command. So, just throwing a warning */
|
||||
ESP_LOGW(TAG, "No data received for the command.");
|
||||
data_size = 0;
|
||||
}
|
||||
void *response;
|
||||
size_t response_size = 0;
|
||||
esp_err_t err = cmd_info->handler(data, data_size, &response, &response_size, &cmd_ctx, cmd_info->priv);
|
||||
if (err == ESP_OK) {
|
||||
err = esp_rmaker_cmd_prepare_response(&cmd_ctx, ESP_RMAKER_CMD_STATUS_SUCCESS, response, response_size, output, output_len);
|
||||
} else {
|
||||
err = esp_rmaker_cmd_prepare_response(&cmd_ctx, ESP_RMAKER_CMD_STATUS_FAILED, NULL, 0, output, output_len);
|
||||
}
|
||||
if (response && cmd_info->free_on_return) {
|
||||
ESP_LOGI(TAG, "Freeing response buffer.");
|
||||
free(response);
|
||||
}
|
||||
return err;
|
||||
} else {
|
||||
return esp_rmaker_cmd_prepare_response(&cmd_ctx, ESP_RMAKER_CMD_STATUS_AUTH_FAIL, NULL, 0, output, output_len);
|
||||
}
|
||||
}
|
||||
return esp_rmaker_cmd_prepare_response(&cmd_ctx, ESP_RMAKER_CMD_STATUS_NOT_FOUND, NULL, 0, output, output_len);
|
||||
}
|
||||
|
||||
/****************************************** Testing Functions ******************************************/
|
||||
static const char *cmd_status[] = {
|
||||
[ESP_RMAKER_CMD_STATUS_SUCCESS] = "Success",
|
||||
[ESP_RMAKER_CMD_STATUS_FAILED] = "Fail",
|
||||
[ESP_RMAKER_CMD_STATUS_CMD_INVALID] = "Invalid command data",
|
||||
[ESP_RMAKER_CMD_STATUS_AUTH_FAIL] = "Auth fail",
|
||||
[ESP_RMAKER_CMD_STATUS_NOT_FOUND] = "Command not found",
|
||||
};
|
||||
|
||||
/* Send test command */
|
||||
esp_err_t esp_rmaker_cmd_resp_test_send(const char *req_id, uint8_t role, uint16_t cmd, const void *data,
|
||||
size_t data_size, esp_rmaker_cmd_send_t cmd_send, void *priv_data)
|
||||
{
|
||||
if (!cmd_send) {
|
||||
ESP_LOGE(TAG, "No callback to trigger the command.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint8_t cmd_data[200];
|
||||
esp_rmaker_tlv_data_t tlv_data;
|
||||
esp_rmaker_tlv_data_init(&tlv_data, cmd_data, sizeof(cmd_data));
|
||||
if (req_id) {
|
||||
esp_rmaker_add_tlv(&tlv_data, ESP_RMAKER_TLV_TYPE_REQ_ID, strlen(req_id), req_id);
|
||||
}
|
||||
esp_rmaker_add_tlv(&tlv_data, ESP_RMAKER_TLV_TYPE_USER_ROLE, sizeof(role), &role);
|
||||
uint8_t cmd_buf[2];
|
||||
put_u16_le(cmd_buf, cmd);
|
||||
esp_rmaker_add_tlv(&tlv_data, ESP_RMAKER_TLV_TYPE_CMD, sizeof(cmd_buf), cmd_buf);
|
||||
if (data != NULL && data_size != 0) {
|
||||
esp_rmaker_add_tlv(&tlv_data, ESP_RMAKER_TLV_TYPE_DATA, data_size, data);
|
||||
}
|
||||
ESP_LOGI(TAG, "Sending command of size %d for cmd %d", tlv_data.curlen, cmd);
|
||||
return cmd_send(cmd_data, tlv_data.curlen, priv_data);
|
||||
}
|
||||
|
||||
/* Parse response */
|
||||
esp_err_t esp_rmaker_cmd_resp_parse_response(const void *response, size_t response_len, void *priv_data)
|
||||
{
|
||||
if (!response) {
|
||||
ESP_LOGE(TAG, "NULL response. Cannot parse.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
char req_id[REQ_ID_LEN] = {0};
|
||||
if (esp_rmaker_get_value_from_tlv(response, response_len, ESP_RMAKER_TLV_TYPE_REQ_ID, req_id, sizeof(req_id)) > 0) {
|
||||
ESP_LOGI(TAG, "RESP: Request Id: %s", req_id);
|
||||
}
|
||||
|
||||
uint16_t cmd;
|
||||
uint8_t cmd_buf[2];
|
||||
if (esp_rmaker_get_value_from_tlv(response, response_len, ESP_RMAKER_TLV_TYPE_CMD, cmd_buf, sizeof(cmd_buf)) > 0) {
|
||||
cmd = get_u16_le(cmd_buf);
|
||||
ESP_LOGI(TAG, "RESP: Command: %" PRIu16, cmd);
|
||||
}
|
||||
|
||||
uint8_t status;
|
||||
if (esp_rmaker_get_value_from_tlv(response, response_len, ESP_RMAKER_TLV_TYPE_STATUS, &status, sizeof(status)) > 0) {
|
||||
ESP_LOGI(TAG, "RESP: Status: %" PRIu8 ": %s", status, cmd_status[status]);
|
||||
}
|
||||
|
||||
char resp_data[200];
|
||||
int resp_size = esp_rmaker_get_value_from_tlv(response, response_len, ESP_RMAKER_TLV_TYPE_DATA, resp_data, sizeof(resp_data));
|
||||
if (resp_size > 0) {
|
||||
resp_data[resp_size] = 0;
|
||||
ESP_LOGI(TAG, "RESP: Data: %s", resp_data);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
359
components/rmaker_common/src/console/rmaker_common_cmds.c
Normal file
359
components/rmaker_common/src/console/rmaker_common_cmds.c
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include <esp_timer.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_console.h>
|
||||
#include <lwip/sockets.h>
|
||||
#include <esp_system.h>
|
||||
#include <argtable3/argtable3.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#ifdef CONFIG_HEAP_TRACING
|
||||
#include <esp_heap_trace.h>
|
||||
#endif
|
||||
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
|
||||
#include "rmaker_console_internal.h"
|
||||
|
||||
static const char *TAG = "esp_rmaker_commands";
|
||||
|
||||
static int reboot_cli_handler(int argc, char *argv[])
|
||||
{
|
||||
/* Just to go to the next line */
|
||||
printf("\n");
|
||||
esp_restart();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int up_time_cli_handler(int argc, char *argv[])
|
||||
{
|
||||
printf("%s: Uptime of the device: %lld milliseconds\n", TAG, esp_timer_get_time() / 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int task_dump_cli_handler(int argc, char *argv[])
|
||||
{
|
||||
#ifndef CONFIG_FREERTOS_USE_TRACE_FACILITY
|
||||
printf("%s: To use this utility enable: Component config --> FreeRTOS --> Enable FreeRTOS trace facility\n", TAG);
|
||||
#else
|
||||
int num_of_tasks = uxTaskGetNumberOfTasks();
|
||||
TaskStatus_t *task_array = MEM_CALLOC_EXTRAM(num_of_tasks, sizeof(TaskStatus_t));
|
||||
if (!task_array) {
|
||||
ESP_LOGE(TAG, "Memory allocation for task list failed.");
|
||||
return -1;
|
||||
}
|
||||
num_of_tasks = uxTaskGetSystemState(task_array, num_of_tasks, NULL);
|
||||
printf("%s: \tName\tNumber\tPriority\tStackWaterMark\n", TAG);
|
||||
for (int i = 0; i < num_of_tasks; i++) {
|
||||
printf("%16s\t%d\t%d\t%" PRIu32 "\n",
|
||||
task_array[i].pcTaskName,
|
||||
task_array[i].xTaskNumber,
|
||||
task_array[i].uxCurrentPriority,
|
||||
task_array[i].usStackHighWaterMark);
|
||||
}
|
||||
free(task_array);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpu_dump_cli_handler(int argc, char *argv[])
|
||||
{
|
||||
#ifndef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
|
||||
printf("%s: To use this utility enable: Component config --> FreeRTOS --> Enable FreeRTOS to collect run time stats\n", TAG);
|
||||
#else
|
||||
char *buf = MEM_CALLOC_EXTRAM(1, 2 * 1024);
|
||||
if (!buf) {
|
||||
ESP_LOGE(TAG, "Memory allocation for cpu dump failed.");
|
||||
return -1;
|
||||
}
|
||||
vTaskGetRunTimeStats(buf);
|
||||
printf("%s: Run Time Stats:\n%s\n", TAG, buf);
|
||||
free(buf);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mem_dump_cli_handler(int argc, char *argv[])
|
||||
{
|
||||
printf("\tDescription\tInternal\tSPIRAM\n");
|
||||
printf("Current Free Memory\t%d\t\t%d\n",
|
||||
heap_caps_get_free_size(MALLOC_CAP_8BIT) - heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
|
||||
heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
|
||||
printf("Largest Free Block\t%d\t\t%d\n",
|
||||
heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL),
|
||||
heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM));
|
||||
printf("Min. Ever Free Size\t%d\t\t%d\n",
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL),
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sock_dump_cli_handler(int argc, char *argv[])
|
||||
{
|
||||
#if LWIP_IPV4
|
||||
int i, ret, used_sockets = 0;
|
||||
|
||||
struct sockaddr_in local_sock, peer_sock;
|
||||
socklen_t local_sock_len = sizeof(struct sockaddr_in), peer_sock_len = sizeof(struct sockaddr_in);
|
||||
char local_ip_addr[16], peer_ip_addr[16];
|
||||
unsigned int local_port, peer_port;
|
||||
|
||||
int sock_type;
|
||||
socklen_t sock_type_len;
|
||||
|
||||
#define TOTAL_NUM_SOCKETS MEMP_NUM_NETCONN
|
||||
printf("sock_fd\tprotocol\tlocal_addr\t\tpeer_addr\n");
|
||||
for (i = LWIP_SOCKET_OFFSET; i < LWIP_SOCKET_OFFSET + TOTAL_NUM_SOCKETS; i++) {
|
||||
memset(&local_sock, 0, sizeof(struct sockaddr_in));
|
||||
memset(&peer_sock, 0, sizeof(struct sockaddr_in));
|
||||
local_sock_len = sizeof(struct sockaddr);
|
||||
peer_sock_len = sizeof(struct sockaddr);
|
||||
memset(local_ip_addr, 0, sizeof(local_ip_addr));
|
||||
memset(peer_ip_addr, 0, sizeof(peer_ip_addr));
|
||||
local_port = 0;
|
||||
peer_port = 0;
|
||||
sock_type = 0;
|
||||
sock_type_len = sizeof(int);
|
||||
|
||||
ret = getsockname(i, (struct sockaddr *)&local_sock, &local_sock_len);
|
||||
if (ret >= 0) {
|
||||
used_sockets++;
|
||||
inet_ntop(AF_INET, &local_sock.sin_addr, local_ip_addr, sizeof(local_ip_addr));
|
||||
local_port = ntohs(local_sock.sin_port);
|
||||
getsockopt(i, SOL_SOCKET, SO_TYPE, &sock_type, &sock_type_len);
|
||||
printf("%d\t%d:%s\t%16s:%d", i, sock_type, sock_type == SOCK_STREAM ? "tcp" : sock_type == SOCK_DGRAM ? "udp" : "raw", local_ip_addr, local_port);
|
||||
|
||||
ret = getpeername(i, (struct sockaddr *)&peer_sock, &peer_sock_len);
|
||||
if (ret >= 0) {
|
||||
inet_ntop(AF_INET, &peer_sock.sin_addr, peer_ip_addr, sizeof(peer_ip_addr));
|
||||
peer_port = ntohs(peer_sock.sin_port);
|
||||
printf("\t%16s:%d", peer_ip_addr, peer_port);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("Remaining sockets: %d\n", TOTAL_NUM_SOCKETS - used_sockets);
|
||||
#else
|
||||
printf("%s: To use this utility enable: Component config --> LWIP --> Enable IPv4 \n", TAG);
|
||||
#endif /* LWIP_IPV4 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HEAP_TRACING
|
||||
static int heap_trace_records;
|
||||
static heap_trace_record_t *heap_trace_records_buf;
|
||||
static int cli_heap_trace_start()
|
||||
{
|
||||
if (!heap_trace_records_buf) {
|
||||
// this buffer is required to be in internal
|
||||
heap_trace_records_buf = malloc(heap_trace_records * sizeof(heap_trace_record_t));
|
||||
if (!heap_trace_records_buf) {
|
||||
printf("%s: Failed to allocate records buffer\n", TAG);
|
||||
return -1;
|
||||
}
|
||||
if (heap_trace_init_standalone(heap_trace_records_buf, heap_trace_records) != ESP_OK) {
|
||||
printf("%s: Failed to initialise tracing\n", TAG);
|
||||
goto error1;
|
||||
}
|
||||
}
|
||||
if (heap_trace_start(HEAP_TRACE_LEAKS) != ESP_OK) {
|
||||
printf("%s: Failed to start heap trace\n", TAG);
|
||||
goto error2;
|
||||
}
|
||||
return 0;
|
||||
error2:
|
||||
heap_trace_init_standalone(NULL, 0);
|
||||
error1:
|
||||
free(heap_trace_records_buf);
|
||||
heap_trace_records_buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cli_heap_trace_stop()
|
||||
{
|
||||
if (!heap_trace_records_buf) {
|
||||
printf("%s: Tracing not started?\n", TAG);
|
||||
return 0;
|
||||
}
|
||||
heap_trace_stop();
|
||||
heap_trace_dump();
|
||||
heap_trace_init_standalone(NULL, 0);
|
||||
free(heap_trace_records_buf);
|
||||
heap_trace_records_buf = NULL;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int heap_trace_cli_handler(int argc, char *argv[])
|
||||
{
|
||||
int ret = 0;
|
||||
#ifndef CONFIG_HEAP_TRACING
|
||||
printf("%s: To use this utility enable: Component config --> Heap memory debugging --> Enable heap tracing\n", TAG);
|
||||
#else
|
||||
if (argc < 2) {
|
||||
printf("%s: Incorrect arguments\n", TAG);
|
||||
return -1;
|
||||
}
|
||||
if (strcmp(argv[1], "start") == 0) {
|
||||
#define DEFAULT_HEAP_TRACE_RECORDS 200
|
||||
if (argc != 3) {
|
||||
heap_trace_records = DEFAULT_HEAP_TRACE_RECORDS;
|
||||
} else {
|
||||
heap_trace_records = atoi(argv[2]);
|
||||
}
|
||||
printf("%s: Using a buffer to trace %d records\n", TAG, heap_trace_records);
|
||||
ret = cli_heap_trace_start();
|
||||
} else if (strcmp(argv[1], "stop") == 0) {
|
||||
ret = cli_heap_trace_stop();
|
||||
} else {
|
||||
printf("%s: Invalid argument:%s:\n", TAG, argv[1]);
|
||||
ret = -1;
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int register_generic_debug_commands()
|
||||
{
|
||||
const esp_console_cmd_t debug_commands[] = {
|
||||
{
|
||||
.command = "reboot",
|
||||
.help = "",
|
||||
.func = reboot_cli_handler,
|
||||
},
|
||||
{
|
||||
.command = "up-time",
|
||||
.help = "Get the device up time in milliseconds.",
|
||||
.func = up_time_cli_handler,
|
||||
},
|
||||
{
|
||||
.command = "mem-dump",
|
||||
.help = "Get the available memory.",
|
||||
.func = mem_dump_cli_handler,
|
||||
},
|
||||
{
|
||||
.command = "task-dump",
|
||||
.help = "Get the list of all the running tasks.",
|
||||
.func = task_dump_cli_handler,
|
||||
},
|
||||
{
|
||||
.command = "cpu-dump",
|
||||
.help = "Get the CPU utilisation by all the runninng tasks.",
|
||||
.func = cpu_dump_cli_handler,
|
||||
},
|
||||
{
|
||||
.command = "sock-dump",
|
||||
.help = "Get the list of all the active sockets.",
|
||||
.func = sock_dump_cli_handler,
|
||||
},
|
||||
{
|
||||
.command = "heap-trace",
|
||||
.help = "Start or stop heap tracing. Usage: heap-trace <start|stop> <bufer_size>",
|
||||
.func = heap_trace_cli_handler,
|
||||
},
|
||||
};
|
||||
|
||||
int cmds_num = sizeof(debug_commands) / sizeof(esp_console_cmd_t);
|
||||
int i;
|
||||
for (i = 0; i < cmds_num; i++) {
|
||||
ESP_LOGI(TAG, "Registering command: %s", debug_commands[i].command);
|
||||
esp_console_cmd_register(&debug_commands[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reset_to_factory_handler(int argc, char** argv)
|
||||
{
|
||||
#define RESET_DELAY 2
|
||||
#define REBOOT_DELAY 2
|
||||
printf("%s: Resetting to Factory Defaults...\n", TAG);
|
||||
esp_rmaker_factory_reset(RESET_DELAY, REBOOT_DELAY);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void register_reset_to_factory()
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "reset-to-factory",
|
||||
.help = "Reset the board to factory defaults",
|
||||
.func = &reset_to_factory_handler,
|
||||
};
|
||||
ESP_LOGI(TAG, "Registering command: %s", cmd.command);
|
||||
esp_console_cmd_register(&cmd);
|
||||
}
|
||||
|
||||
static int local_time_cli_handler(int argc, char *argv[])
|
||||
{
|
||||
char local_time[64];
|
||||
if (esp_rmaker_get_local_time_str(local_time, sizeof(local_time)) == ESP_OK) {
|
||||
printf("%s: Current local time: %s\n", TAG, local_time);
|
||||
} else {
|
||||
printf("%s: Current local time (truncated): %s\n", TAG, local_time);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int tz_set_cli_handler(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
printf("%s: Invalid Usage.\n", TAG);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (strcmp(argv[1], "posix") == 0) {
|
||||
if (argv[2]) {
|
||||
esp_rmaker_time_set_timezone_posix(argv[2]);
|
||||
} else {
|
||||
printf("%s: Invalid Usage.\n", TAG);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
} else {
|
||||
esp_rmaker_time_set_timezone(argv[1]);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void register_time_commands()
|
||||
{
|
||||
const esp_console_cmd_t local_time_cmd = {
|
||||
.command = "local-time",
|
||||
.help = "Get the local time of device.",
|
||||
.func = &local_time_cli_handler,
|
||||
};
|
||||
ESP_LOGI(TAG, "Registering command: %s", local_time_cmd.command);
|
||||
esp_console_cmd_register(&local_time_cmd);
|
||||
|
||||
const esp_console_cmd_t tz_set_cmd = {
|
||||
.command = "tz-set",
|
||||
.help = "Set Timezone. Usage: tz-set [posix] <tz_string>.",
|
||||
.func = &tz_set_cli_handler,
|
||||
};
|
||||
ESP_LOGI(TAG, "Registering command: %s", tz_set_cmd.command);
|
||||
esp_console_cmd_register(&tz_set_cmd);
|
||||
}
|
||||
|
||||
void esp_rmaker_common_register_commands()
|
||||
{
|
||||
static bool registered = false;
|
||||
if (registered) {
|
||||
ESP_LOGI(TAG, "Skipping already registered commands");
|
||||
return;
|
||||
}
|
||||
register_generic_debug_commands();
|
||||
register_reset_to_factory();
|
||||
register_time_commands();
|
||||
registered = true;
|
||||
}
|
||||
117
components/rmaker_common/src/console/rmaker_console.c
Normal file
117
components/rmaker_common/src/console/rmaker_console.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_console.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <driver/uart.h>
|
||||
|
||||
#include <esp_rmaker_common_console.h>
|
||||
#include "rmaker_console_internal.h"
|
||||
|
||||
static const char *TAG = "esp_rmaker_console";
|
||||
static int stop;
|
||||
|
||||
static void scli_task(void *arg)
|
||||
{
|
||||
int uart_num = 0;
|
||||
uint8_t linebuf[256];
|
||||
int i, cmd_ret;
|
||||
esp_err_t ret;
|
||||
QueueHandle_t uart_queue;
|
||||
uart_event_t event;
|
||||
bool first_done = false;
|
||||
|
||||
ESP_LOGI(TAG, "Initialising UART on port %d", uart_num);
|
||||
uart_driver_install(uart_num, 256, 0, 8, &uart_queue, 0);
|
||||
|
||||
/* Initialize the console */
|
||||
esp_console_config_t console_config = {
|
||||
.max_cmdline_args = 8,
|
||||
.max_cmdline_length = 256,
|
||||
};
|
||||
esp_console_init(&console_config);
|
||||
esp_console_register_help_command();
|
||||
|
||||
while (!stop) {
|
||||
if (first_done) {
|
||||
uart_write_bytes(uart_num, "\n>> ", 4);
|
||||
} else {
|
||||
first_done = true;
|
||||
}
|
||||
memset(linebuf, 0, sizeof(linebuf));
|
||||
i = 0;
|
||||
do {
|
||||
ret = xQueueReceive(uart_queue, (void * )&event, (TickType_t)portMAX_DELAY);
|
||||
if (ret != pdPASS) {
|
||||
if(stop == 1) {
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (event.type == UART_DATA) {
|
||||
while (uart_read_bytes(uart_num, (uint8_t *) &linebuf[i], 1, 0)) {
|
||||
if (linebuf[i] == '\r') {
|
||||
uart_write_bytes(uart_num, "\r\n", 2);
|
||||
} else {
|
||||
uart_write_bytes(uart_num, (char *) &linebuf[i], 1);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} while ((i < 255) && linebuf[i-1] != '\r');
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
/* Remove the truncating \r\n */
|
||||
linebuf[strlen((char *)linebuf) - 1] = '\0';
|
||||
/* Just to go to the next line */
|
||||
printf("\n");
|
||||
ret = esp_console_run((char *) linebuf, &cmd_ret);
|
||||
if (cmd_ret != 0) {
|
||||
printf("%s: Console command failed with error: %d\n", TAG, cmd_ret);
|
||||
cmd_ret = 0;
|
||||
}
|
||||
if (ret < 0) {
|
||||
printf("%s: Console dispatcher error\n", TAG);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ESP_LOGE(TAG, "Stopped CLI");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static esp_err_t scli_init()
|
||||
{
|
||||
static bool cli_started = false;
|
||||
if (cli_started) {
|
||||
return ESP_OK;
|
||||
}
|
||||
#define SCLI_STACK_SIZE 4096
|
||||
if (xTaskCreate(&scli_task, "console_task", SCLI_STACK_SIZE, NULL, 3, NULL) != pdPASS) {
|
||||
ESP_LOGE(TAG, "Couldn't create thread");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
cli_started = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_common_console_init()
|
||||
{
|
||||
esp_err_t ret = scli_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Couldn't initialise console");
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_rmaker_common_register_commands();
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void esp_rmaker_common_register_commands(void);
|
||||
246
components/rmaker_common/src/create_APN3_PPI_string.c
Normal file
246
components/rmaker_common/src/create_APN3_PPI_string.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* APN3 PPI string generator - sample code
|
||||
*
|
||||
* NOTE: All code is provided as sample code for informational purposes only, and should not be used for any testing or production workloads.
|
||||
* All code is provided “as is” and AWS expressly disclaims all warranties, including, without limitation: any implied warranties of
|
||||
* noninfringement, merchantability, or fitness for a particular purpose; any warranty that operation of the code will be error-free
|
||||
* or free of harmful components; or any warranty arising out of any course of dealing or usage of trade. In no event shall AWS or
|
||||
* any of its affiliates be liable for any damages arising out of the use of this code, including, without limitation, any direct,
|
||||
* indirect, special, incidental or consequential damages.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_log.h>
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
#include <esp_mac.h>
|
||||
#endif
|
||||
|
||||
static const char *TAG = "aws_ppi";
|
||||
|
||||
#define PPI_STRING_GEN_VERSION 0.4
|
||||
#define MAX_PPI_STRING_LEN 64
|
||||
#define MAX_LEN_FIELDS_1_2_3 37 /* max length of fields ProductName + ProductUID + ProductVersion */
|
||||
|
||||
#define PPI_PREFIX_1 "?"
|
||||
#define PPI_PREFIX_1_ALT "&"
|
||||
|
||||
#define PPI_PREFIX_2 "Platform=APN3|"
|
||||
#define PPI_RESERVED "A0|" /* reserved for AWS use */
|
||||
|
||||
#define PPI_PLATFORM_AWS "AZ|"
|
||||
#define PPI_PLATFORM_RAINMAKER "RM|"
|
||||
#define PPI_PLATFORM PPI_PLATFORM_RAINMAKER
|
||||
|
||||
#define PPI_SILICON_SKU_CODE CONFIG_ESP_RMAKER_MQTT_PRODUCT_SKU
|
||||
#define PPI_PRODUCT_NAME CONFIG_ESP_RMAKER_MQTT_PRODUCT_NAME
|
||||
#define PPI_PRODUCT_VERSION CONFIG_ESP_RMAKER_MQTT_PRODUCT_VERSION
|
||||
|
||||
#define PPI_SEPARATOR_CHAR '|'
|
||||
#define PPI_SEPARATOR "|"
|
||||
|
||||
#define PPI_FIXED_PART PPI_PREFIX_1 PPI_PREFIX_2 PPI_RESERVED PPI_PLATFORM PPI_SILICON_SKU_CODE PPI_SEPARATOR PPI_PRODUCT_NAME PPI_SEPARATOR PPI_PRODUCT_VERSION PPI_SEPARATOR
|
||||
|
||||
#define VALID 1
|
||||
#define INVALID 0
|
||||
|
||||
/*
|
||||
* platform_get_product_name is a stub function to be customized for each platform.
|
||||
* It must return a pointer to the product name string.
|
||||
* Can use a #define instead of a function in most cases.
|
||||
*/
|
||||
char __attribute__((weak)) *platform_get_product_name()
|
||||
{
|
||||
return(PPI_PRODUCT_NAME);
|
||||
}
|
||||
|
||||
/*
|
||||
* platform_get_product_UID is a stub function to be customized for each platform.
|
||||
* It must return a pointer to the product UID string.
|
||||
*/
|
||||
char __attribute__((weak)) *platform_get_product_UID()
|
||||
{
|
||||
static char mac_str[13];
|
||||
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||
uint8_t eth_mac[6];
|
||||
esp_err_t err = esp_wifi_get_mac(WIFI_IF_STA, eth_mac);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not fetch MAC address. Please initialise Wi-Fi first");
|
||||
return NULL;
|
||||
}
|
||||
snprintf(mac_str, sizeof(mac_str), "%02X%02X%02X%02X%02X%02X",
|
||||
eth_mac[0], eth_mac[1], eth_mac[2], eth_mac[3], eth_mac[4], eth_mac[5]);
|
||||
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD) /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
uint8_t base_mac[6];
|
||||
esp_err_t err = esp_read_mac(base_mac, ESP_MAC_BASE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not fetch base mac");
|
||||
return NULL;
|
||||
}
|
||||
snprintf(mac_str, sizeof(mac_str), "%02X%02X%02X%02X%02X%02X",
|
||||
base_mac[0], base_mac[1], base_mac[2], base_mac[3], base_mac[4], base_mac[5]);
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||
return mac_str;
|
||||
}
|
||||
|
||||
/*
|
||||
* platform_get_product_version is a stub function to be customized for each platform.
|
||||
* It must return a pointer to the product version string.
|
||||
* Can use a #define instead of a function in most cases.
|
||||
*/
|
||||
char __attribute__((weak)) *platform_get_product_version()
|
||||
{
|
||||
return(PPI_PRODUCT_VERSION);
|
||||
}
|
||||
|
||||
/*
|
||||
* platform_get_silicom_sku_code is a stub function to be customized for each platform.
|
||||
* It must return a pointer to a valid Silicon SKU code.
|
||||
* Can use a #define instead of a function in most cases.
|
||||
*/
|
||||
char __attribute__((weak)) *platform_get_silicon_sku_code()
|
||||
{
|
||||
return(PPI_SILICON_SKU_CODE);
|
||||
}
|
||||
|
||||
/*
|
||||
* validate_sku_code takes as input the 4 character SKU code and validates it.
|
||||
*
|
||||
* Returns
|
||||
* VALID if valid, else INVALID
|
||||
*/
|
||||
int validate_sku_code(char *silicon_sku_code)
|
||||
{
|
||||
int retval = INVALID;
|
||||
|
||||
typedef struct sku_item_t {
|
||||
/* entry in sku code lookup table */
|
||||
char *skucode; /* code corresponding to sku name */
|
||||
} sku_item;
|
||||
|
||||
static sku_item skutable[] =
|
||||
{
|
||||
/*
|
||||
* codes for partner 1
|
||||
*/
|
||||
{ "ES00" }, /* Older Default */
|
||||
{ "EX00" }, /* Default */
|
||||
{ "EX01" }, /* ESP32-C3, including 8685 */
|
||||
{ "EX02" }, /* ESP32-C2, including 8684 */
|
||||
{ "EX03" }, /* ESP32-S3 */
|
||||
{ "EX04" }, /* ESP32 */
|
||||
{ "EX05" }, /* ESP32-C5 */
|
||||
{ "EX06" }, /* ESP32-S2 */
|
||||
/*
|
||||
* add new entries above in any order
|
||||
*/
|
||||
{ NULL } /* this must be the last entry */
|
||||
};
|
||||
|
||||
/*
|
||||
* linear search for a matching silicon sku code entry
|
||||
*/
|
||||
for (sku_item *p = skutable; p->skucode != NULL; p++)
|
||||
{
|
||||
if (strcmp(p->skucode, silicon_sku_code) == 0)
|
||||
{ /* found match */
|
||||
retval = VALID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* validate_platform_inputs
|
||||
*
|
||||
* Returns
|
||||
* VALID if inputs are valid, INVALID otherwise
|
||||
*/
|
||||
int validate_platform_inputs(char *product_name, char *product_uid, char *product_version, char *silicon_sku_code)
|
||||
{
|
||||
int retval = INVALID;
|
||||
|
||||
if ( (strlen(product_name) + strlen(product_uid) + strlen(product_version)) <= MAX_LEN_FIELDS_1_2_3)
|
||||
{ /* field 1,2,3 length check passed */
|
||||
|
||||
if (validate_sku_code(silicon_sku_code) == VALID)
|
||||
{
|
||||
retval = VALID;
|
||||
}
|
||||
else
|
||||
{ /* invalid sku code */
|
||||
ESP_LOGE(TAG, "Error: Invalid sku code");
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* invalid string length */
|
||||
ESP_LOGE(TAG, "Error: Invalid field(s) length");
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* create_APN_PPI_string creates the string that must be passed in the UserName field of the MQTT Connect
|
||||
* packet as required by the IAP Module Incentive program. Details are given in the APN3 IoT Acceleration
|
||||
* Module Incentive Program Guide-Tech-<partnername> document.
|
||||
*
|
||||
* Inputs:
|
||||
* product_name = pointer to a string holding the product name.
|
||||
* product_uid = pointer to a string holding the product UID (unique id)
|
||||
* product_version = version number of the product.
|
||||
* silicon_sku_code = pointer to a string holding a valid SKU code as specified in the guide.
|
||||
*
|
||||
* Return values
|
||||
* NULL if there is an error and the PPI string could not be generated, otherwise the PPI string is returned.
|
||||
*/
|
||||
char * create_APN_PPI_string(char *product_name, char *product_uid, char *product_version, char *silicon_sku_code)
|
||||
{
|
||||
char *retval = NULL;
|
||||
static char ppistring[MAX_PPI_STRING_LEN + 1];
|
||||
|
||||
|
||||
ESP_LOGD(TAG, "PPI string generator v%2.1f", PPI_STRING_GEN_VERSION);
|
||||
|
||||
/*
|
||||
* Validate inputs
|
||||
*/
|
||||
if (validate_platform_inputs(product_name, product_uid, product_version, silicon_sku_code))
|
||||
{ /* valid inputs - build the string */
|
||||
snprintf(ppistring, sizeof(ppistring),"%s%s%s%s%s%s%s%s%s%s%s",
|
||||
PPI_PREFIX_1, PPI_PREFIX_2, PPI_RESERVED, PPI_PLATFORM, silicon_sku_code,
|
||||
PPI_SEPARATOR, product_name, PPI_SEPARATOR, product_version, PPI_SEPARATOR,
|
||||
product_uid);
|
||||
retval = ppistring;
|
||||
}
|
||||
else
|
||||
{ /* error - some inputs are not valid */
|
||||
ESP_LOGE(TAG, "Error: Some inputs are not valid");
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* main - setup values for your platform and invoke string generator.
|
||||
*/
|
||||
const char __attribute__((weak)) *esp_get_aws_ppi(void)
|
||||
{
|
||||
char *ppistring = NULL;
|
||||
|
||||
ppistring = create_APN_PPI_string( platform_get_product_name(),
|
||||
platform_get_product_UID(),
|
||||
platform_get_product_version(),
|
||||
platform_get_silicon_sku_code() );
|
||||
|
||||
if (ppistring != NULL)
|
||||
{
|
||||
ESP_LOGD(TAG, "PPI inputs pass all checks. String value is shown below:\n\n%s", ppistring);
|
||||
}
|
||||
else
|
||||
{ /* error */
|
||||
ESP_LOGE(TAG, "Error creating PPI string");
|
||||
}
|
||||
return ppistring;
|
||||
}
|
||||
477
components/rmaker_common/src/esp-mqtt/esp-mqtt-glue.c
Normal file
477
components/rmaker_common/src/esp-mqtt/esp-mqtt-glue.c
Normal file
@@ -0,0 +1,477 @@
|
||||
// Copyright 2021 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_log.h>
|
||||
#include <mqtt_client.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_rmaker_common_events.h>
|
||||
#include <esp_rmaker_mqtt_glue.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0)
|
||||
// Features supported in 4.1+
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_MQTT_PORT_443
|
||||
#define ESP_RMAKER_MQTT_USE_PORT_443
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_MQTT_PORT_443
|
||||
#warning "Certificate Bundle not supported below IDF v4.4. Using provided certificate instead."
|
||||
#endif
|
||||
|
||||
#endif /* !IDF4.1 */
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
// Features supported in 4.4+
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_MQTT_USE_CERT_BUNDLE
|
||||
#define ESP_RMAKER_MQTT_USE_CERT_BUNDLE
|
||||
#include <esp_crt_bundle.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_MQTT_USE_CERT_BUNDLE
|
||||
#warning "Certificate Bundle not supported below 4.4. Using provided certificate instead."
|
||||
#endif
|
||||
|
||||
#endif /* !IDF4.4 */
|
||||
|
||||
static const char *TAG = "esp_mqtt_glue";
|
||||
|
||||
#define MAX_MQTT_SUBSCRIPTIONS CONFIG_ESP_RMAKER_MAX_MQTT_SUBSCRIPTIONS
|
||||
|
||||
typedef struct {
|
||||
char *topic;
|
||||
esp_rmaker_mqtt_subscribe_cb_t cb;
|
||||
void *priv;
|
||||
} esp_mqtt_glue_subscription_t;
|
||||
|
||||
typedef struct {
|
||||
esp_mqtt_client_handle_t mqtt_client;
|
||||
esp_rmaker_mqtt_conn_params_t *conn_params;
|
||||
esp_mqtt_glue_subscription_t *subscriptions[MAX_MQTT_SUBSCRIPTIONS];
|
||||
} esp_mqtt_glue_data_t;
|
||||
esp_mqtt_glue_data_t *mqtt_data;
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
char *topic;
|
||||
} esp_mqtt_glue_long_data_t;
|
||||
|
||||
static void esp_mqtt_glue_deinit(void);
|
||||
|
||||
static void esp_mqtt_glue_subscribe_callback(const char *topic, int topic_len, const char *data, int data_len)
|
||||
{
|
||||
esp_mqtt_glue_subscription_t **subscriptions = mqtt_data->subscriptions;
|
||||
int i;
|
||||
for (i = 0; i < MAX_MQTT_SUBSCRIPTIONS; i++) {
|
||||
if (subscriptions[i]) {
|
||||
if ((strncmp(topic, subscriptions[i]->topic, topic_len) == 0)
|
||||
&& (topic_len == strlen(subscriptions[i]->topic))) {
|
||||
subscriptions[i]->cb(subscriptions[i]->topic, (void *)data, data_len, subscriptions[i]->priv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t esp_mqtt_glue_subscribe(const char *topic, esp_rmaker_mqtt_subscribe_cb_t cb, uint8_t qos, void *priv_data)
|
||||
{
|
||||
if ( !mqtt_data || !topic || !cb) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < MAX_MQTT_SUBSCRIPTIONS; i++) {
|
||||
if (!mqtt_data->subscriptions[i]) {
|
||||
esp_mqtt_glue_subscription_t *subscription = calloc(1, sizeof(esp_mqtt_glue_subscription_t));
|
||||
if (!subscription) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
subscription->topic = strdup(topic);
|
||||
if (!subscription->topic) {
|
||||
free(subscription);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int ret = esp_mqtt_client_subscribe(mqtt_data->mqtt_client, subscription->topic, qos);
|
||||
if (ret < 0) {
|
||||
free(subscription->topic);
|
||||
free(subscription);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
subscription->priv = priv_data;
|
||||
subscription->cb = cb;
|
||||
mqtt_data->subscriptions[i] = subscription;
|
||||
ESP_LOGD(TAG, "Subscribed to topic: %s", topic);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static void unsubscribe_helper(esp_mqtt_glue_subscription_t **subscription)
|
||||
{
|
||||
if (subscription && *subscription) {
|
||||
if (esp_mqtt_client_unsubscribe(mqtt_data->mqtt_client, (*subscription)->topic) < 0) {
|
||||
ESP_LOGW(TAG, "Could not unsubscribe from topic: %s", (*subscription)->topic);
|
||||
}
|
||||
free((*subscription)->topic);
|
||||
free(*subscription);
|
||||
*subscription = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t esp_mqtt_glue_unsubscribe(const char *topic)
|
||||
{
|
||||
if (!mqtt_data || !topic) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_mqtt_glue_subscription_t **subscriptions = mqtt_data->subscriptions;
|
||||
int i;
|
||||
for (i = 0; i < MAX_MQTT_SUBSCRIPTIONS; i++) {
|
||||
if (subscriptions[i]) {
|
||||
if (strncmp(topic, subscriptions[i]->topic, strlen(topic)) == 0) {
|
||||
unsubscribe_helper(&subscriptions[i]);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static esp_err_t esp_mqtt_glue_publish(const char *topic, void *data, size_t data_len, uint8_t qos, int *msg_id)
|
||||
{
|
||||
if (!mqtt_data || !topic || !data) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGD(TAG, "Publishing to %s", topic);
|
||||
int ret = esp_mqtt_client_publish(mqtt_data->mqtt_client, topic, data, data_len, qos, 0);
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "MQTT Publish failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (msg_id) {
|
||||
*msg_id = ret;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_mqtt_glue_long_data_t *esp_mqtt_glue_free_long_data(esp_mqtt_glue_long_data_t *long_data)
|
||||
{
|
||||
if (long_data) {
|
||||
if (long_data->topic) {
|
||||
free(long_data->topic);
|
||||
}
|
||||
if (long_data->data) {
|
||||
free(long_data->data);
|
||||
}
|
||||
free(long_data);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static esp_mqtt_glue_long_data_t *esp_mqtt_glue_manage_long_data(esp_mqtt_glue_long_data_t *long_data,
|
||||
esp_mqtt_event_handle_t event)
|
||||
{
|
||||
if (event->topic) {
|
||||
/* This is new data. Free any earlier data, if present. */
|
||||
esp_mqtt_glue_free_long_data(long_data);
|
||||
long_data = calloc(1, sizeof(esp_mqtt_glue_long_data_t));
|
||||
if (!long_data) {
|
||||
ESP_LOGE(TAG, "Could not allocate memory for esp_mqtt_glue_long_data_t");
|
||||
return NULL;
|
||||
}
|
||||
long_data->data = MEM_CALLOC_EXTRAM(1, event->total_data_len);
|
||||
if (!long_data->data) {
|
||||
ESP_LOGE(TAG, "Could not allocate %d bytes for received data.", event->total_data_len);
|
||||
return esp_mqtt_glue_free_long_data(long_data);
|
||||
}
|
||||
long_data->topic = strndup(event->topic, event->topic_len);
|
||||
if (!long_data->topic) {
|
||||
ESP_LOGE(TAG, "Could not allocate %d bytes for received topic.", event->topic_len);
|
||||
return esp_mqtt_glue_free_long_data(long_data);
|
||||
}
|
||||
}
|
||||
if (long_data) {
|
||||
memcpy(long_data->data + event->current_data_offset, event->data, event->data_len);
|
||||
|
||||
if ((event->current_data_offset + event->data_len) == event->total_data_len) {
|
||||
esp_mqtt_glue_subscribe_callback(long_data->topic, strlen(long_data->topic),
|
||||
long_data->data, event->total_data_len);
|
||||
return esp_mqtt_glue_free_long_data(long_data);
|
||||
}
|
||||
}
|
||||
return long_data;
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
#else
|
||||
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
|
||||
#endif
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
esp_mqtt_event_handle_t event = event_data;
|
||||
#else
|
||||
uint32_t event_id = event->event_id;
|
||||
#endif
|
||||
|
||||
switch (event_id) {
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT Connected");
|
||||
/* Resubscribe to all topics after reconnection */
|
||||
for (int i = 0; i < MAX_MQTT_SUBSCRIPTIONS; i++) {
|
||||
if (mqtt_data->subscriptions[i]) {
|
||||
esp_mqtt_client_subscribe(event->client, mqtt_data->subscriptions[i]->topic, 1);
|
||||
}
|
||||
}
|
||||
esp_event_post(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, NULL, 0, portMAX_DELAY);
|
||||
break;
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
ESP_LOGW(TAG, "MQTT Disconnected. Will try reconnecting in a while...");
|
||||
esp_event_post(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_DISCONNECTED, NULL, 0, portMAX_DELAY);
|
||||
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);
|
||||
esp_event_post(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_PUBLISHED, &event->msg_id, sizeof(event->msg_id), portMAX_DELAY);
|
||||
break;
|
||||
#ifdef CONFIG_MQTT_REPORT_DELETED_MESSAGES
|
||||
case MQTT_EVENT_DELETED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_DELETED, msg_id=%d", event->msg_id);
|
||||
esp_event_post(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_MSG_DELETED, &event->msg_id, sizeof(event->msg_id), portMAX_DELAY);
|
||||
break;
|
||||
#endif /* CONFIG_MQTT_REPORT_DELETED_MESSAGES */
|
||||
case MQTT_EVENT_DATA: {
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_DATA");
|
||||
static esp_mqtt_glue_long_data_t *long_data;
|
||||
/* Topic can be NULL, for data longer than the MQTT buffer */
|
||||
if (event->topic) {
|
||||
ESP_LOGD(TAG, "TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
||||
}
|
||||
ESP_LOGD(TAG, "DATA=%.*s\r\n", event->data_len, event->data);
|
||||
if (event->data_len == event->total_data_len) {
|
||||
/* If long_data still exists, it means there was some issue getting the
|
||||
* long data, and so, it needs to be freed up.
|
||||
*/
|
||||
if (long_data) {
|
||||
long_data = esp_mqtt_glue_free_long_data(long_data);
|
||||
}
|
||||
esp_mqtt_glue_subscribe_callback(event->topic, event->topic_len, event->data, event->data_len);
|
||||
} else {
|
||||
long_data = esp_mqtt_glue_manage_long_data(long_data, event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MQTT_EVENT_ERROR:
|
||||
ESP_LOGE(TAG, "MQTT_EVENT_ERROR");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGD(TAG, "Other event id:%d", event->event_id);
|
||||
break;
|
||||
}
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
static esp_err_t esp_mqtt_glue_connect(void)
|
||||
{
|
||||
if (!mqtt_data) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Connecting to %s", mqtt_data->conn_params->mqtt_host);
|
||||
esp_err_t ret = esp_mqtt_client_start(mqtt_data->mqtt_client);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_mqtt_client_start() failed with err = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void esp_mqtt_glue_unsubscribe_all(void)
|
||||
{
|
||||
if (!mqtt_data) {
|
||||
return;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < MAX_MQTT_SUBSCRIPTIONS; i++) {
|
||||
if (mqtt_data->subscriptions[i]) {
|
||||
unsubscribe_helper(&(mqtt_data->subscriptions[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t esp_mqtt_glue_disconnect(void)
|
||||
{
|
||||
if (!mqtt_data) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_mqtt_glue_unsubscribe_all();
|
||||
esp_err_t err = esp_mqtt_client_stop(mqtt_data->mqtt_client);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to disconnect from MQTT");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "MQTT Disconnected.");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#ifdef ESP_RMAKER_MQTT_USE_PORT_443
|
||||
static const char *alpn_protocols[] = { "x-amzn-mqtt-ca", NULL };
|
||||
#endif /* ESP_RMAKER_MQTT_USE_PORT_443 */
|
||||
static esp_err_t esp_mqtt_glue_init(esp_rmaker_mqtt_conn_params_t *conn_params)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_MQTT_SEND_USERNAME
|
||||
const char *username = esp_get_aws_ppi();
|
||||
ESP_LOGI(TAG, "AWS PPI: %s", username);
|
||||
#endif
|
||||
if (mqtt_data) {
|
||||
ESP_LOGE(TAG, "MQTT already initialized");
|
||||
return ESP_OK;
|
||||
}
|
||||
if (!conn_params) {
|
||||
ESP_LOGE(TAG, "Connection params are mandatory for esp_mqtt_glue_init");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Initialising MQTT");
|
||||
mqtt_data = calloc(1, sizeof(esp_mqtt_glue_data_t));
|
||||
if (!mqtt_data) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for esp_mqtt_glue_data_t");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
mqtt_data->conn_params = conn_params;
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
const esp_mqtt_client_config_t mqtt_client_cfg = {
|
||||
.broker = {
|
||||
.address = {
|
||||
.hostname = conn_params->mqtt_host,
|
||||
#ifdef ESP_RMAKER_MQTT_USE_PORT_443
|
||||
.port = 443,
|
||||
#else
|
||||
.port = 8883,
|
||||
#endif
|
||||
.transport = MQTT_TRANSPORT_OVER_SSL,
|
||||
},
|
||||
.verification = {
|
||||
#ifdef ESP_RMAKER_MQTT_USE_PORT_443
|
||||
.alpn_protos = alpn_protocols,
|
||||
#endif
|
||||
#ifdef ESP_RMAKER_MQTT_USE_CERT_BUNDLE
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
#else
|
||||
.certificate = (const char *)conn_params->server_cert,
|
||||
.certificate_len = conn_params->server_cert_len,
|
||||
#endif
|
||||
}
|
||||
},
|
||||
.credentials = {
|
||||
#ifdef CONFIG_ESP_RMAKER_MQTT_SEND_USERNAME
|
||||
.username = username,
|
||||
#endif
|
||||
.client_id = (const char *)conn_params->client_id,
|
||||
.authentication = {
|
||||
.certificate = (const char *)conn_params->client_cert,
|
||||
.certificate_len = conn_params->client_cert_len,
|
||||
.key = (const char *)conn_params->client_key,
|
||||
.key_len = conn_params->client_key_len,
|
||||
.ds_data = conn_params->ds_data
|
||||
},
|
||||
},
|
||||
.session = {
|
||||
.keepalive = CONFIG_ESP_RMAKER_MQTT_KEEP_ALIVE_INTERVAL,
|
||||
#ifdef CONFIG_ESP_RMAKER_MQTT_PERSISTENT_SESSION
|
||||
.disable_clean_session = 1,
|
||||
#endif /* CONFIG_ESP_RMAKER_MQTT_PERSISTENT_SESSION */
|
||||
},
|
||||
};
|
||||
#else
|
||||
const esp_mqtt_client_config_t mqtt_client_cfg = {
|
||||
.host = conn_params->mqtt_host,
|
||||
#ifdef ESP_RMAKER_MQTT_USE_PORT_443
|
||||
.port = 443,
|
||||
.alpn_protos = alpn_protocols,
|
||||
#else
|
||||
.port = 8883,
|
||||
#endif /* !ESP_RMAKER_MQTT_USE_PORT_443 */
|
||||
#ifdef ESP_RMAKER_MQTT_USE_CERT_BUNDLE
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
#else
|
||||
.cert_pem = (const char *)conn_params->server_cert,
|
||||
.cert_len = conn_params->server_cert_len,
|
||||
#endif
|
||||
.client_cert_pem = (const char *)conn_params->client_cert,
|
||||
.client_cert_len = conn_params->client_cert_len,
|
||||
.client_key_pem = (const char *)conn_params->client_key,
|
||||
.client_key_len = conn_params->client_key_len,
|
||||
.client_id = (const char *)conn_params->client_id,
|
||||
.keepalive = CONFIG_ESP_RMAKER_MQTT_KEEP_ALIVE_INTERVAL,
|
||||
.event_handle = mqtt_event_handler,
|
||||
.transport = MQTT_TRANSPORT_OVER_SSL,
|
||||
#ifdef CONFIG_ESP_RMAKER_MQTT_PERSISTENT_SESSION
|
||||
.disable_clean_session = 1,
|
||||
#endif /* CONFIG_ESP_RMAKER_MQTT_PERSISTENT_SESSION */
|
||||
#ifdef CONFIG_ESP_RMAKER_MQTT_SEND_USERNAME
|
||||
.username = username,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
mqtt_data->mqtt_client = esp_mqtt_client_init(&mqtt_client_cfg);
|
||||
if (!mqtt_data->mqtt_client) {
|
||||
ESP_LOGE(TAG, "esp_mqtt_client_init failed");
|
||||
esp_mqtt_glue_deinit();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
esp_mqtt_client_register_event(mqtt_data->mqtt_client , ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void esp_mqtt_glue_deinit(void)
|
||||
{
|
||||
esp_mqtt_glue_unsubscribe_all();
|
||||
if (mqtt_data && mqtt_data->mqtt_client) {
|
||||
esp_mqtt_client_destroy(mqtt_data->mqtt_client);
|
||||
}
|
||||
if (mqtt_data) {
|
||||
free(mqtt_data);
|
||||
mqtt_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_glue_setup(esp_rmaker_mqtt_config_t *mqtt_config)
|
||||
{
|
||||
mqtt_config->init = esp_mqtt_glue_init;
|
||||
mqtt_config->deinit = esp_mqtt_glue_deinit;
|
||||
mqtt_config->connect = esp_mqtt_glue_connect;
|
||||
mqtt_config->disconnect = esp_mqtt_glue_disconnect;
|
||||
mqtt_config->publish = esp_mqtt_glue_publish;
|
||||
mqtt_config->subscribe = esp_mqtt_glue_subscribe;
|
||||
mqtt_config->unsubscribe = esp_mqtt_glue_unsubscribe;
|
||||
mqtt_config->setup_done = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
120
components/rmaker_common/src/factory.c
Normal file
120
components/rmaker_common/src/factory.c
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright 2021 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 <string.h>
|
||||
#include <esp_log.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <nvs.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <esp_partition.h>
|
||||
|
||||
static const char *TAG = "esp_rmaker_fctry";
|
||||
|
||||
#define RMAKER_FACTORY_NAMESPACE CONFIG_ESP_RMAKER_FACTORY_NAMESPACE
|
||||
#define RMAKER_FACTORY_PART CONFIG_ESP_RMAKER_FACTORY_PARTITION_NAME
|
||||
|
||||
esp_err_t esp_rmaker_factory_init(void)
|
||||
{
|
||||
static bool esp_rmaker_storage_init_done;
|
||||
if (esp_rmaker_storage_init_done) {
|
||||
ESP_LOGW(TAG, "ESP RainMaker Storage already initialized");
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_err_t err = ESP_OK;
|
||||
#ifdef CONFIG_ESP_RMAKER_ENCRYPT_FACTORY_PARTITION
|
||||
const char *nvs_keys_partition_name = CONFIG_ESP_RMAKER_FACTORY_NVS_KEYS_PARTITION_NAME;
|
||||
if (strlen(nvs_keys_partition_name) == 0) {
|
||||
nvs_keys_partition_name = NULL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Initialising factory partition in secure mode.");
|
||||
const esp_partition_t* key_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, nvs_keys_partition_name);
|
||||
if (!key_part) {
|
||||
ESP_LOGE(TAG, "Partition with subtype nvs_keys not found");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
nvs_sec_cfg_t cfg = {};
|
||||
err = nvs_flash_read_security_cfg(key_part, &cfg);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read NVS security cfg: 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
err = nvs_flash_secure_init_partition(RMAKER_FACTORY_PART, &cfg);
|
||||
#else
|
||||
err = nvs_flash_init_partition(RMAKER_FACTORY_PART);
|
||||
#endif
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS Flash init failed");
|
||||
} else {
|
||||
esp_rmaker_storage_init_done = true;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void *esp_rmaker_factory_get(const char *key)
|
||||
{
|
||||
nvs_handle handle;
|
||||
esp_err_t err;
|
||||
if ((err = nvs_open_from_partition(RMAKER_FACTORY_PART, RMAKER_FACTORY_NAMESPACE,
|
||||
NVS_READONLY, &handle)) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "NVS open for %s %s %s failed with error %d", RMAKER_FACTORY_PART, RMAKER_FACTORY_NAMESPACE, key, err);
|
||||
return NULL;
|
||||
}
|
||||
size_t required_size = 0;
|
||||
if ((err = nvs_get_blob(handle, key, NULL, &required_size)) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Failed to read key %s with error %d size %d", key, err, required_size);
|
||||
nvs_close(handle);
|
||||
return NULL;
|
||||
}
|
||||
void *value = MEM_CALLOC_EXTRAM(required_size + 1, 1); /* + 1 for NULL termination */
|
||||
if (value) {
|
||||
nvs_get_blob(handle, key, value, &required_size);
|
||||
}
|
||||
nvs_close(handle);
|
||||
return value;
|
||||
}
|
||||
|
||||
size_t esp_rmaker_factory_get_size(const char *key)
|
||||
{
|
||||
nvs_handle handle;
|
||||
esp_err_t err;
|
||||
if ((err = nvs_open_from_partition(RMAKER_FACTORY_PART, RMAKER_FACTORY_NAMESPACE,
|
||||
NVS_READONLY, &handle)) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "NVS open for %s %s %s failed with error %d", RMAKER_FACTORY_PART, RMAKER_FACTORY_NAMESPACE, key, err);
|
||||
return 0;
|
||||
}
|
||||
size_t required_size = 0;
|
||||
if ((err = nvs_get_blob(handle, key, NULL, &required_size)) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Failed to read key %s with error %d size %d", key, err, required_size);
|
||||
}
|
||||
nvs_close(handle);
|
||||
return required_size;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_factory_set(const char *key, void *value, size_t len)
|
||||
{
|
||||
nvs_handle handle;
|
||||
esp_err_t err;
|
||||
if ((err = nvs_open_from_partition(RMAKER_FACTORY_PART, RMAKER_FACTORY_NAMESPACE,
|
||||
NVS_READWRITE, &handle)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if ((err = nvs_set_blob(handle, key, value, len)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to write key %s with error %d size %d", key, err, len);
|
||||
nvs_close(handle);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
return ESP_OK;
|
||||
}
|
||||
277
components/rmaker_common/src/time.c
Normal file
277
components/rmaker_common/src/time.c
Normal file
@@ -0,0 +1,277 @@
|
||||
// Copyright 2021 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 <esp_idf_version.h>
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
#include <esp_sntp.h>
|
||||
#else
|
||||
#include <lwip/apps/sntp.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <esp_log.h>
|
||||
#include <nvs.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <esp_rmaker_common_events.h>
|
||||
#include <esp_idf_version.h>
|
||||
|
||||
static const char *TAG = "esp_rmaker_time";
|
||||
|
||||
#define ESP_RMAKER_NVS_PART_NAME "nvs"
|
||||
|
||||
#define ESP_RMAKER_NVS_TIME_NAMESPACE "rmaker_time"
|
||||
#define ESP_RMAKER_TZ_POSIX_NVS_NAME "tz_posix"
|
||||
#define ESP_RMAKER_TZ_NVS_NAME "tz"
|
||||
|
||||
#define REF_TIME 1546300800 /* 01-Jan-2019 00:00:00 */
|
||||
static bool init_done = false;
|
||||
extern const char *esp_rmaker_tz_db_get_posix_str(const char *name);
|
||||
|
||||
#define ESP_RMAKER_DEF_TZ CONFIG_ESP_RMAKER_DEF_TIMEZONE
|
||||
|
||||
int esp_setenv(const char *name, const char *value, int rewrite)
|
||||
{
|
||||
/* IDF version lower than v4.4.3 not support parse bracket POSIX TZ in newlib.
|
||||
Wrap setenv function to convert bracket POSIX TZ, such as <+08>-8 to TZ-08 */
|
||||
#if ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 3)
|
||||
#define ESP_TZNAME_MIN 3
|
||||
#define ESP_TZNAME_MAX 10
|
||||
if (value) {
|
||||
const char *tzenv = value;
|
||||
if (*tzenv == '<') {
|
||||
++ tzenv;
|
||||
char tzname[ESP_TZNAME_MIN + 1] = {0};
|
||||
char real_value[6] = {0};
|
||||
int n = 0;
|
||||
if (sscanf(tzenv, "%10[-+0-9A-Za-z]%n", tzname, &n) <= 0 || n < ESP_TZNAME_MIN || n > ESP_TZNAME_MAX || '>' != tzenv[n]) {
|
||||
ESP_LOGW(TAG, "Failed to convert Posix TZ %s", value);
|
||||
goto exit;
|
||||
}
|
||||
tzname[0] = (tzname[0] == '-') ? '+' : '-';
|
||||
sprintf(real_value, "TZ%s", tzname);
|
||||
ESP_LOGI(TAG, "Real Posix TZ is %s", real_value);
|
||||
return setenv(name, real_value, rewrite);
|
||||
}
|
||||
}
|
||||
exit:
|
||||
#endif
|
||||
return setenv(name, value, rewrite);
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_get_local_time_str(char *buf, size_t buf_len)
|
||||
{
|
||||
struct tm timeinfo;
|
||||
char strftime_buf[64];
|
||||
time_t now;
|
||||
time(&now);
|
||||
localtime_r(&now, &timeinfo);
|
||||
strftime(strftime_buf, sizeof(strftime_buf), "%c %z[%Z]", &timeinfo);
|
||||
size_t print_size = snprintf(buf, buf_len, "%s, DST: %s", strftime_buf, timeinfo.tm_isdst ? "Yes" : "No");
|
||||
if (print_size >= buf_len) {
|
||||
ESP_LOGE(TAG, "Buffer size %d insufficient for localtime string. Required size: %d", buf_len, print_size);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_print_current_time(void)
|
||||
{
|
||||
char local_time[64];
|
||||
if (esp_rmaker_get_local_time_str(local_time, sizeof(local_time)) == ESP_OK) {
|
||||
if (esp_rmaker_time_check() == false) {
|
||||
ESP_LOGI(TAG, "Time not synchronised yet.");
|
||||
}
|
||||
ESP_LOGI(TAG, "The current time is: %s.", local_time);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static char *__esp_rmaker_time_get_nvs(const char *key)
|
||||
{
|
||||
char *val = NULL;
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, ESP_RMAKER_NVS_TIME_NAMESPACE, NVS_READONLY, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return NULL;
|
||||
}
|
||||
size_t len = 0;
|
||||
if ((err = nvs_get_blob(handle, key, NULL, &len)) == ESP_OK) {
|
||||
val = MEM_CALLOC_EXTRAM(1, len + 1); /* +1 for NULL termination */
|
||||
if (val) {
|
||||
nvs_get_blob(handle, key, val, &len);
|
||||
}
|
||||
}
|
||||
nvs_close(handle);
|
||||
return val;
|
||||
|
||||
}
|
||||
|
||||
static esp_err_t __esp_rmaker_time_set_nvs(const char *key, const char *val)
|
||||
{
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, ESP_RMAKER_NVS_TIME_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
err = nvs_set_blob(handle, key, val, strlen(val));
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
return err;
|
||||
}
|
||||
|
||||
char *esp_rmaker_time_get_timezone_posix(void)
|
||||
{
|
||||
return __esp_rmaker_time_get_nvs(ESP_RMAKER_TZ_POSIX_NVS_NAME);
|
||||
}
|
||||
|
||||
char *esp_rmaker_time_get_timezone(void)
|
||||
{
|
||||
return __esp_rmaker_time_get_nvs(ESP_RMAKER_TZ_NVS_NAME);
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_time_set_timezone_posix(const char *tz_posix)
|
||||
{
|
||||
esp_err_t err = __esp_rmaker_time_set_nvs(ESP_RMAKER_TZ_POSIX_NVS_NAME, tz_posix);
|
||||
if (err == ESP_OK) {
|
||||
esp_setenv("TZ", tz_posix, 1);
|
||||
tzset();
|
||||
esp_event_post(RMAKER_COMMON_EVENT, RMAKER_EVENT_TZ_POSIX_CHANGED,
|
||||
(void *)tz_posix, strlen(tz_posix) + 1, portMAX_DELAY);
|
||||
esp_rmaker_print_current_time();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_time_set_timezone(const char *tz)
|
||||
{
|
||||
const char *tz_posix = esp_rmaker_tz_db_get_posix_str(tz);
|
||||
if (!tz_posix) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_err_t err = esp_rmaker_time_set_timezone_posix(tz_posix);
|
||||
if (err == ESP_OK) {
|
||||
esp_event_post(RMAKER_COMMON_EVENT, RMAKER_EVENT_TZ_CHANGED, (void *)tz,
|
||||
strlen(tz) + 1, portMAX_DELAY);
|
||||
err = __esp_rmaker_time_set_nvs(ESP_RMAKER_TZ_NVS_NAME, tz);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_timezone_enable(void)
|
||||
{
|
||||
char *tz_posix = esp_rmaker_time_get_timezone_posix();
|
||||
if (tz_posix) {
|
||||
esp_setenv("TZ", tz_posix, 1);
|
||||
tzset();
|
||||
free(tz_posix);
|
||||
} else {
|
||||
if (strlen(ESP_RMAKER_DEF_TZ) > 0) {
|
||||
const char *tz_def = esp_rmaker_tz_db_get_posix_str(ESP_RMAKER_DEF_TZ);
|
||||
if (tz_def) {
|
||||
esp_setenv("TZ", tz_def, 1);
|
||||
tzset();
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid Timezone %s specified.", ESP_RMAKER_DEF_TZ);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
static void esp_rmaker_time_sync_cb(struct timeval *tv)
|
||||
{
|
||||
ESP_LOGI(TAG, "SNTP Synchronised.");
|
||||
esp_rmaker_print_current_time();
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_time_sync_init(esp_rmaker_time_config_t *config)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
if (esp_sntp_enabled()) {
|
||||
#else
|
||||
if (sntp_enabled()) {
|
||||
#endif
|
||||
ESP_LOGI(TAG, "SNTP already initialized.");
|
||||
init_done = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
char *sntp_server_name;
|
||||
if (!config || !config->sntp_server_name) {
|
||||
sntp_server_name = CONFIG_ESP_RMAKER_SNTP_SERVER_NAME;
|
||||
} else {
|
||||
sntp_server_name = config->sntp_server_name;
|
||||
}
|
||||
ESP_LOGI(TAG, "Initializing SNTP. Using the SNTP server: %s", sntp_server_name);
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
esp_sntp_setservername(0, sntp_server_name);
|
||||
esp_sntp_init();
|
||||
#else
|
||||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
sntp_setservername(0, sntp_server_name);
|
||||
sntp_init();
|
||||
#endif
|
||||
if (config && config->sync_time_cb) {
|
||||
sntp_set_time_sync_notification_cb(config->sync_time_cb);
|
||||
} else {
|
||||
sntp_set_time_sync_notification_cb(esp_rmaker_time_sync_cb);
|
||||
}
|
||||
esp_rmaker_timezone_enable();
|
||||
init_done = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool esp_rmaker_time_check(void)
|
||||
{
|
||||
time_t now;
|
||||
time(&now);
|
||||
if (now > REF_TIME) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#define DEFAULT_TICKS (2000 / portTICK_PERIOD_MS) /* 2 seconds in ticks */
|
||||
|
||||
esp_err_t esp_rmaker_time_wait_for_sync(uint32_t ticks_to_wait)
|
||||
{
|
||||
if (!init_done) {
|
||||
ESP_LOGW(TAG, "Time sync not initialized using 'esp_rmaker_time_sync_init'");
|
||||
}
|
||||
ESP_LOGW(TAG, "Waiting for time to be synchronized. This may take time.");
|
||||
uint32_t ticks_remaining = ticks_to_wait;
|
||||
uint32_t ticks = DEFAULT_TICKS;
|
||||
while (ticks_remaining > 0) {
|
||||
if (esp_rmaker_time_check() == true) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGD(TAG, "Time not synchronized yet. Retrying...");
|
||||
ticks = ticks_remaining < DEFAULT_TICKS ? ticks_remaining : DEFAULT_TICKS;
|
||||
ticks_remaining -= ticks;
|
||||
vTaskDelay(ticks);
|
||||
}
|
||||
|
||||
/* Check if ticks_to_wait expired and time is not synchronized yet. */
|
||||
if (esp_rmaker_time_check() == false) {
|
||||
ESP_LOGE(TAG, "Time not synchronized within the provided ticks: %" PRIu32, ticks_to_wait);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Get current time */
|
||||
esp_rmaker_print_current_time();
|
||||
return ESP_OK;
|
||||
}
|
||||
516
components/rmaker_common/src/timezone.c
Normal file
516
components/rmaker_common/src/timezone.c
Normal file
@@ -0,0 +1,516 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Nayar Systems
|
||||
// Copyright (c) 2020 Jacob Lambert
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Original code taken from: https://github.com/jdlambert/micro_tz_db
|
||||
// which was forked from https://github.com/nayarsystems/posix_tz_db
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *posix_str;
|
||||
} esp_rmaker_tz_db_pair_t;
|
||||
|
||||
static const esp_rmaker_tz_db_pair_t esp_rmaker_tz_db_tzs[] = {
|
||||
{"Africa/Abidjan", "GMT0"},
|
||||
{"Africa/Accra", "GMT0"},
|
||||
{"Africa/Addis_Ababa", "EAT-3"},
|
||||
{"Africa/Algiers", "CET-1"},
|
||||
{"Africa/Asmara", "EAT-3"},
|
||||
{"Africa/Bamako", "GMT0"},
|
||||
{"Africa/Bangui", "WAT-1"},
|
||||
{"Africa/Banjul", "GMT0"},
|
||||
{"Africa/Bissau", "GMT0"},
|
||||
{"Africa/Blantyre", "CAT-2"},
|
||||
{"Africa/Brazzaville", "WAT-1"},
|
||||
{"Africa/Bujumbura", "CAT-2"},
|
||||
{"Africa/Cairo", "EET-2"},
|
||||
{"Africa/Casablanca", "<+01>-1"},
|
||||
{"Africa/Ceuta", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Africa/Conakry", "GMT0"},
|
||||
{"Africa/Dakar", "GMT0"},
|
||||
{"Africa/Dar_es_Salaam", "EAT-3"},
|
||||
{"Africa/Djibouti", "EAT-3"},
|
||||
{"Africa/Douala", "WAT-1"},
|
||||
{"Africa/El_Aaiun", "<+01>-1"},
|
||||
{"Africa/Freetown", "GMT0"},
|
||||
{"Africa/Gaborone", "CAT-2"},
|
||||
{"Africa/Harare", "CAT-2"},
|
||||
{"Africa/Johannesburg", "SAST-2"},
|
||||
{"Africa/Juba", "EAT-3"},
|
||||
{"Africa/Kampala", "EAT-3"},
|
||||
{"Africa/Khartoum", "CAT-2"},
|
||||
{"Africa/Kigali", "CAT-2"},
|
||||
{"Africa/Kinshasa", "WAT-1"},
|
||||
{"Africa/Lagos", "WAT-1"},
|
||||
{"Africa/Libreville", "WAT-1"},
|
||||
{"Africa/Lome", "GMT0"},
|
||||
{"Africa/Luanda", "WAT-1"},
|
||||
{"Africa/Lubumbashi", "CAT-2"},
|
||||
{"Africa/Lusaka", "CAT-2"},
|
||||
{"Africa/Malabo", "WAT-1"},
|
||||
{"Africa/Maputo", "CAT-2"},
|
||||
{"Africa/Maseru", "SAST-2"},
|
||||
{"Africa/Mbabane", "SAST-2"},
|
||||
{"Africa/Mogadishu", "EAT-3"},
|
||||
{"Africa/Monrovia", "GMT0"},
|
||||
{"Africa/Nairobi", "EAT-3"},
|
||||
{"Africa/Ndjamena", "WAT-1"},
|
||||
{"Africa/Niamey", "WAT-1"},
|
||||
{"Africa/Nouakchott", "GMT0"},
|
||||
{"Africa/Ouagadougou", "GMT0"},
|
||||
{"Africa/Porto-Novo", "WAT-1"},
|
||||
{"Africa/Sao_Tome", "GMT0"},
|
||||
{"Africa/Tripoli", "EET-2"},
|
||||
{"Africa/Tunis", "CET-1"},
|
||||
{"Africa/Windhoek", "CAT-2"},
|
||||
{"America/Adak", "HST10HDT,M3.2.0,M11.1.0"},
|
||||
{"America/Anchorage", "AKST9AKDT,M3.2.0,M11.1.0"},
|
||||
{"America/Anguilla", "AST4"},
|
||||
{"America/Antigua", "AST4"},
|
||||
{"America/Araguaina", "<-03>3"},
|
||||
{"America/Argentina/Buenos_Aires", "<-03>3"},
|
||||
{"America/Argentina/Catamarca", "<-03>3"},
|
||||
{"America/Argentina/Cordoba", "<-03>3"},
|
||||
{"America/Argentina/Jujuy", "<-03>3"},
|
||||
{"America/Argentina/La_Rioja", "<-03>3"},
|
||||
{"America/Argentina/Mendoza", "<-03>3"},
|
||||
{"America/Argentina/Rio_Gallegos", "<-03>3"},
|
||||
{"America/Argentina/Salta", "<-03>3"},
|
||||
{"America/Argentina/San_Juan", "<-03>3"},
|
||||
{"America/Argentina/San_Luis", "<-03>3"},
|
||||
{"America/Argentina/Tucuman", "<-03>3"},
|
||||
{"America/Argentina/Ushuaia", "<-03>3"},
|
||||
{"America/Aruba", "AST4"},
|
||||
{"America/Asuncion", "<-04>4<-03>,M10.1.0/0,M3.4.0/0"},
|
||||
{"America/Atikokan", "EST5"},
|
||||
{"America/Bahia", "<-03>3"},
|
||||
{"America/Bahia_Banderas", "CST6CDT,M4.1.0,M10.5.0"},
|
||||
{"America/Barbados", "AST4"},
|
||||
{"America/Belem", "<-03>3"},
|
||||
{"America/Belize", "CST6"},
|
||||
{"America/Blanc-Sablon", "AST4"},
|
||||
{"America/Boa_Vista", "<-04>4"},
|
||||
{"America/Bogota", "<-05>5"},
|
||||
{"America/Boise", "MST7MDT,M3.2.0,M11.1.0"},
|
||||
{"America/Cambridge_Bay", "MST7MDT,M3.2.0,M11.1.0"},
|
||||
{"America/Campo_Grande", "<-04>4"},
|
||||
{"America/Cancun", "EST5"},
|
||||
{"America/Caracas", "<-04>4"},
|
||||
{"America/Cayenne", "<-03>3"},
|
||||
{"America/Cayman", "EST5"},
|
||||
{"America/Chicago", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Chihuahua", "MST7MDT,M4.1.0,M10.5.0"},
|
||||
{"America/Costa_Rica", "CST6"},
|
||||
{"America/Creston", "MST7"},
|
||||
{"America/Cuiaba", "<-04>4"},
|
||||
{"America/Curacao", "AST4"},
|
||||
{"America/Danmarkshavn", "GMT0"},
|
||||
{"America/Dawson", "MST7"},
|
||||
{"America/Dawson_Creek", "MST7"},
|
||||
{"America/Denver", "MST7MDT,M3.2.0,M11.1.0"},
|
||||
{"America/Detroit", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Dominica", "AST4"},
|
||||
{"America/Edmonton", "MST7MDT,M3.2.0,M11.1.0"},
|
||||
{"America/Eirunepe", "<-05>5"},
|
||||
{"America/El_Salvador", "CST6"},
|
||||
{"America/Fortaleza", "<-03>3"},
|
||||
{"America/Fort_Nelson", "MST7"},
|
||||
{"America/Glace_Bay", "AST4ADT,M3.2.0,M11.1.0"},
|
||||
{"America/Godthab", "<-03>3<-02>,M3.5.0/-2,M10.5.0/-1"},
|
||||
{"America/Goose_Bay", "AST4ADT,M3.2.0,M11.1.0"},
|
||||
{"America/Grand_Turk", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Grenada", "AST4"},
|
||||
{"America/Guadeloupe", "AST4"},
|
||||
{"America/Guatemala", "CST6"},
|
||||
{"America/Guayaquil", "<-05>5"},
|
||||
{"America/Guyana", "<-04>4"},
|
||||
{"America/Halifax", "AST4ADT,M3.2.0,M11.1.0"},
|
||||
{"America/Havana", "CST5CDT,M3.2.0/0,M11.1.0/1"},
|
||||
{"America/Hermosillo", "MST7"},
|
||||
{"America/Indiana/Indianapolis", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Indiana/Knox", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Indiana/Marengo", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Indiana/Petersburg", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Indiana/Tell_City", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Indiana/Vevay", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Indiana/Vincennes", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Indiana/Winamac", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Inuvik", "MST7MDT,M3.2.0,M11.1.0"},
|
||||
{"America/Iqaluit", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Jamaica", "EST5"},
|
||||
{"America/Juneau", "AKST9AKDT,M3.2.0,M11.1.0"},
|
||||
{"America/Kentucky/Louisville", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Kentucky/Monticello", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Kralendijk", "AST4"},
|
||||
{"America/La_Paz", "<-04>4"},
|
||||
{"America/Lima", "<-05>5"},
|
||||
{"America/Los_Angeles", "PST8PDT,M3.2.0,M11.1.0"},
|
||||
{"America/Lower_Princes", "AST4"},
|
||||
{"America/Maceio", "<-03>3"},
|
||||
{"America/Managua", "CST6"},
|
||||
{"America/Manaus", "<-04>4"},
|
||||
{"America/Marigot", "AST4"},
|
||||
{"America/Martinique", "AST4"},
|
||||
{"America/Matamoros", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Mazatlan", "MST7MDT,M4.1.0,M10.5.0"},
|
||||
{"America/Menominee", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Merida", "CST6CDT,M4.1.0,M10.5.0"},
|
||||
{"America/Metlakatla", "AKST9AKDT,M3.2.0,M11.1.0"},
|
||||
{"America/Mexico_City", "CST6CDT,M4.1.0,M10.5.0"},
|
||||
{"America/Miquelon", "<-03>3<-02>,M3.2.0,M11.1.0"},
|
||||
{"America/Moncton", "AST4ADT,M3.2.0,M11.1.0"},
|
||||
{"America/Monterrey", "CST6CDT,M4.1.0,M10.5.0"},
|
||||
{"America/Montevideo", "<-03>3"},
|
||||
{"America/Montreal", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Montserrat", "AST4"},
|
||||
{"America/Nassau", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/New_York", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Nipigon", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Nome", "AKST9AKDT,M3.2.0,M11.1.0"},
|
||||
{"America/Noronha", "<-02>2"},
|
||||
{"America/North_Dakota/Beulah", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/North_Dakota/Center", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/North_Dakota/New_Salem", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Ojinaga", "MST7MDT,M3.2.0,M11.1.0"},
|
||||
{"America/Panama", "EST5"},
|
||||
{"America/Pangnirtung", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Paramaribo", "<-03>3"},
|
||||
{"America/Phoenix", "MST7"},
|
||||
{"America/Port-au-Prince", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Port_of_Spain", "AST4"},
|
||||
{"America/Porto_Velho", "<-04>4"},
|
||||
{"America/Puerto_Rico", "AST4"},
|
||||
{"America/Punta_Arenas", "<-03>3"},
|
||||
{"America/Rainy_River", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Rankin_Inlet", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Recife", "<-03>3"},
|
||||
{"America/Regina", "CST6"},
|
||||
{"America/Resolute", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Rio_Branco", "<-05>5"},
|
||||
{"America/Santarem", "<-03>3"},
|
||||
{"America/Santiago", "<-04>4<-03>,M9.1.6/24,M4.1.6/24"},
|
||||
{"America/Santo_Domingo", "AST4"},
|
||||
{"America/Sao_Paulo", "<-03>3"},
|
||||
{"America/Scoresbysund", "<-01>1<+00>,M3.5.0/0,M10.5.0/1"},
|
||||
{"America/Sitka", "AKST9AKDT,M3.2.0,M11.1.0"},
|
||||
{"America/St_Barthelemy", "AST4"},
|
||||
{"America/St_Johns", "NST3:30NDT,M3.2.0,M11.1.0"},
|
||||
{"America/St_Kitts", "AST4"},
|
||||
{"America/St_Lucia", "AST4"},
|
||||
{"America/St_Thomas", "AST4"},
|
||||
{"America/St_Vincent", "AST4"},
|
||||
{"America/Swift_Current", "CST6"},
|
||||
{"America/Tegucigalpa", "CST6"},
|
||||
{"America/Thule", "AST4ADT,M3.2.0,M11.1.0"},
|
||||
{"America/Thunder_Bay", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Tijuana", "PST8PDT,M3.2.0,M11.1.0"},
|
||||
{"America/Toronto", "EST5EDT,M3.2.0,M11.1.0"},
|
||||
{"America/Tortola", "AST4"},
|
||||
{"America/Vancouver", "PST8PDT,M3.2.0,M11.1.0"},
|
||||
{"America/Whitehorse", "MST7"},
|
||||
{"America/Winnipeg", "CST6CDT,M3.2.0,M11.1.0"},
|
||||
{"America/Yakutat", "AKST9AKDT,M3.2.0,M11.1.0"},
|
||||
{"America/Yellowknife", "MST7MDT,M3.2.0,M11.1.0"},
|
||||
{"Antarctica/Casey", "<+08>-8"},
|
||||
{"Antarctica/Davis", "<+07>-7"},
|
||||
{"Antarctica/DumontDUrville", "<+10>-10"},
|
||||
{"Antarctica/Macquarie", "<+11>-11"},
|
||||
{"Antarctica/Mawson", "<+05>-5"},
|
||||
{"Antarctica/McMurdo", "NZST-12NZDT,M9.5.0,M4.1.0/3"},
|
||||
{"Antarctica/Palmer", "<-03>3"},
|
||||
{"Antarctica/Rothera", "<-03>3"},
|
||||
{"Antarctica/Syowa", "<+03>-3"},
|
||||
{"Antarctica/Troll", "<+00>0<+02>-2,M3.5.0/1,M10.5.0/3"},
|
||||
{"Antarctica/Vostok", "<+06>-6"},
|
||||
{"Arctic/Longyearbyen", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Asia/Aden", "<+03>-3"},
|
||||
{"Asia/Almaty", "<+06>-6"},
|
||||
{"Asia/Amman", "EET-2EEST,M3.5.4/24,M10.5.5/1"},
|
||||
{"Asia/Anadyr", "<+12>-12"},
|
||||
{"Asia/Aqtau", "<+05>-5"},
|
||||
{"Asia/Aqtobe", "<+05>-5"},
|
||||
{"Asia/Ashgabat", "<+05>-5"},
|
||||
{"Asia/Atyrau", "<+05>-5"},
|
||||
{"Asia/Baghdad", "<+03>-3"},
|
||||
{"Asia/Bahrain", "<+03>-3"},
|
||||
{"Asia/Baku", "<+04>-4"},
|
||||
{"Asia/Bangkok", "<+07>-7"},
|
||||
{"Asia/Barnaul", "<+07>-7"},
|
||||
{"Asia/Beirut", "EET-2EEST,M3.5.0/0,M10.5.0/0"},
|
||||
{"Asia/Bishkek", "<+06>-6"},
|
||||
{"Asia/Brunei", "<+08>-8"},
|
||||
{"Asia/Chita", "<+09>-9"},
|
||||
{"Asia/Choibalsan", "<+08>-8"},
|
||||
{"Asia/Colombo", "<+0530>-5:30"},
|
||||
{"Asia/Damascus", "EET-2EEST,M3.5.5/0,M10.5.5/0"},
|
||||
{"Asia/Dhaka", "<+06>-6"},
|
||||
{"Asia/Dili", "<+09>-9"},
|
||||
{"Asia/Dubai", "<+04>-4"},
|
||||
{"Asia/Dushanbe", "<+05>-5"},
|
||||
{"Asia/Famagusta", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Asia/Gaza", "EET-2EEST,M3.5.5/0,M10.5.6/1"},
|
||||
{"Asia/Hebron", "EET-2EEST,M3.5.5/0,M10.5.6/1"},
|
||||
{"Asia/Ho_Chi_Minh", "<+07>-7"},
|
||||
{"Asia/Hong_Kong", "HKT-8"},
|
||||
{"Asia/Hovd", "<+07>-7"},
|
||||
{"Asia/Irkutsk", "<+08>-8"},
|
||||
{"Asia/Jakarta", "WIB-7"},
|
||||
{"Asia/Jayapura", "WIT-9"},
|
||||
{"Asia/Jerusalem", "IST-2IDT,M3.4.4/26,M10.5.0"},
|
||||
{"Asia/Kabul", "<+0430>-4:30"},
|
||||
{"Asia/Kamchatka", "<+12>-12"},
|
||||
{"Asia/Karachi", "PKT-5"},
|
||||
{"Asia/Kathmandu", "<+0545>-5:45"},
|
||||
{"Asia/Khandyga", "<+09>-9"},
|
||||
{"Asia/Kolkata", "IST-5:30"},
|
||||
{"Asia/Krasnoyarsk", "<+07>-7"},
|
||||
{"Asia/Kuala_Lumpur", "<+08>-8"},
|
||||
{"Asia/Kuching", "<+08>-8"},
|
||||
{"Asia/Kuwait", "<+03>-3"},
|
||||
{"Asia/Macau", "CST-8"},
|
||||
{"Asia/Magadan", "<+11>-11"},
|
||||
{"Asia/Makassar", "WITA-8"},
|
||||
{"Asia/Manila", "PST-8"},
|
||||
{"Asia/Muscat", "<+04>-4"},
|
||||
{"Asia/Nicosia", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Asia/Novokuznetsk", "<+07>-7"},
|
||||
{"Asia/Novosibirsk", "<+07>-7"},
|
||||
{"Asia/Omsk", "<+06>-6"},
|
||||
{"Asia/Oral", "<+05>-5"},
|
||||
{"Asia/Phnom_Penh", "<+07>-7"},
|
||||
{"Asia/Pontianak", "WIB-7"},
|
||||
{"Asia/Pyongyang", "KST-9"},
|
||||
{"Asia/Qatar", "<+03>-3"},
|
||||
{"Asia/Qyzylorda", "<+05>-5"},
|
||||
{"Asia/Riyadh", "<+03>-3"},
|
||||
{"Asia/Sakhalin", "<+11>-11"},
|
||||
{"Asia/Samarkand", "<+05>-5"},
|
||||
{"Asia/Seoul", "KST-9"},
|
||||
{"Asia/Shanghai", "CST-8"},
|
||||
{"Asia/Singapore", "<+08>-8"},
|
||||
{"Asia/Srednekolymsk", "<+11>-11"},
|
||||
{"Asia/Taipei", "CST-8"},
|
||||
{"Asia/Tashkent", "<+05>-5"},
|
||||
{"Asia/Tbilisi", "<+04>-4"},
|
||||
{"Asia/Tehran", "<+0330>-3:30<+0430>,J79/24,J263/24"},
|
||||
{"Asia/Thimphu", "<+06>-6"},
|
||||
{"Asia/Tokyo", "JST-9"},
|
||||
{"Asia/Tomsk", "<+07>-7"},
|
||||
{"Asia/Ulaanbaatar", "<+08>-8"},
|
||||
{"Asia/Urumqi", "<+06>-6"},
|
||||
{"Asia/Ust-Nera", "<+10>-10"},
|
||||
{"Asia/Vientiane", "<+07>-7"},
|
||||
{"Asia/Vladivostok", "<+10>-10"},
|
||||
{"Asia/Yakutsk", "<+09>-9"},
|
||||
{"Asia/Yangon", "<+0630>-6:30"},
|
||||
{"Asia/Yekaterinburg", "<+05>-5"},
|
||||
{"Asia/Yerevan", "<+04>-4"},
|
||||
{"Atlantic/Azores", "<-01>1<+00>,M3.5.0/0,M10.5.0/1"},
|
||||
{"Atlantic/Bermuda", "AST4ADT,M3.2.0,M11.1.0"},
|
||||
{"Atlantic/Canary", "WET0WEST,M3.5.0/1,M10.5.0"},
|
||||
{"Atlantic/Cape_Verde", "<-01>1"},
|
||||
{"Atlantic/Faroe", "WET0WEST,M3.5.0/1,M10.5.0"},
|
||||
{"Atlantic/Madeira", "WET0WEST,M3.5.0/1,M10.5.0"},
|
||||
{"Atlantic/Reykjavik", "GMT0"},
|
||||
{"Atlantic/South_Georgia", "<-02>2"},
|
||||
{"Atlantic/Stanley", "<-03>3"},
|
||||
{"Atlantic/St_Helena", "GMT0"},
|
||||
{"Australia/Adelaide", "ACST-9:30ACDT,M10.1.0,M4.1.0/3"},
|
||||
{"Australia/Brisbane", "AEST-10"},
|
||||
{"Australia/Broken_Hill", "ACST-9:30ACDT,M10.1.0,M4.1.0/3"},
|
||||
{"Australia/Currie", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
|
||||
{"Australia/Darwin", "ACST-9:30"},
|
||||
{"Australia/Eucla", "<+0845>-8:45"},
|
||||
{"Australia/Hobart", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
|
||||
{"Australia/Lindeman", "AEST-10"},
|
||||
{"Australia/Lord_Howe", "<+1030>-10:30<+11>-11,M10.1.0,M4.1.0"},
|
||||
{"Australia/Melbourne", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
|
||||
{"Australia/Perth", "AWST-8"},
|
||||
{"Australia/Sydney", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
|
||||
{"Europe/Amsterdam", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Andorra", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Astrakhan", "<+04>-4"},
|
||||
{"Europe/Athens", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Belgrade", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Berlin", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Bratislava", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Brussels", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Bucharest", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Budapest", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Busingen", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Chisinau", "EET-2EEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Copenhagen", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Dublin", "IST-1GMT0,M10.5.0,M3.5.0/1"},
|
||||
{"Europe/Gibraltar", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Guernsey", "GMT0BST,M3.5.0/1,M10.5.0"},
|
||||
{"Europe/Helsinki", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Isle_of_Man", "GMT0BST,M3.5.0/1,M10.5.0"},
|
||||
{"Europe/Istanbul", "<+03>-3"},
|
||||
{"Europe/Jersey", "GMT0BST,M3.5.0/1,M10.5.0"},
|
||||
{"Europe/Kaliningrad", "EET-2"},
|
||||
{"Europe/Kiev", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Kirov", "<+03>-3"},
|
||||
{"Europe/Lisbon", "WET0WEST,M3.5.0/1,M10.5.0"},
|
||||
{"Europe/Ljubljana", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/London", "GMT0BST,M3.5.0/1,M10.5.0"},
|
||||
{"Europe/Luxembourg", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Madrid", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Malta", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Mariehamn", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Minsk", "<+03>-3"},
|
||||
{"Europe/Monaco", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Moscow", "MSK-3"},
|
||||
{"Europe/Oslo", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Paris", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Podgorica", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Prague", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Riga", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Rome", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Samara", "<+04>-4"},
|
||||
{"Europe/San_Marino", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Sarajevo", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Saratov", "<+04>-4"},
|
||||
{"Europe/Simferopol", "MSK-3"},
|
||||
{"Europe/Skopje", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Sofia", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Stockholm", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Tallinn", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Tirane", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Ulyanovsk", "<+04>-4"},
|
||||
{"Europe/Uzhgorod", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Vaduz", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Vatican", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Vienna", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Vilnius", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Volgograd", "<+04>-4"},
|
||||
{"Europe/Warsaw", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Zagreb", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Europe/Zaporozhye", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||
{"Europe/Zurich", "CET-1CEST,M3.5.0,M10.5.0/3"},
|
||||
{"Indian/Antananarivo", "EAT-3"},
|
||||
{"Indian/Chagos", "<+06>-6"},
|
||||
{"Indian/Christmas", "<+07>-7"},
|
||||
{"Indian/Cocos", "<+0630>-6:30"},
|
||||
{"Indian/Comoro", "EAT-3"},
|
||||
{"Indian/Kerguelen", "<+05>-5"},
|
||||
{"Indian/Mahe", "<+04>-4"},
|
||||
{"Indian/Maldives", "<+05>-5"},
|
||||
{"Indian/Mauritius", "<+04>-4"},
|
||||
{"Indian/Mayotte", "EAT-3"},
|
||||
{"Indian/Reunion", "<+04>-4"},
|
||||
{"Pacific/Apia", "<+13>-13<+14>,M9.5.0/3,M4.1.0/4"},
|
||||
{"Pacific/Auckland", "NZST-12NZDT,M9.5.0,M4.1.0/3"},
|
||||
{"Pacific/Bougainville", "<+11>-11"},
|
||||
{"Pacific/Chatham", "<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45"},
|
||||
{"Pacific/Chuuk", "<+10>-10"},
|
||||
{"Pacific/Easter", "<-06>6<-05>,M9.1.6/22,M4.1.6/22"},
|
||||
{"Pacific/Efate", "<+11>-11"},
|
||||
{"Pacific/Enderbury", "<+13>-13"},
|
||||
{"Pacific/Fakaofo", "<+13>-13"},
|
||||
{"Pacific/Fiji", "<+12>-12<+13>,M11.2.0,M1.2.3/99"},
|
||||
{"Pacific/Funafuti", "<+12>-12"},
|
||||
{"Pacific/Galapagos", "<-06>6"},
|
||||
{"Pacific/Gambier", "<-09>9"},
|
||||
{"Pacific/Guadalcanal", "<+11>-11"},
|
||||
{"Pacific/Guam", "ChST-10"},
|
||||
{"Pacific/Honolulu", "HST10"},
|
||||
{"Pacific/Kiritimati", "<+14>-14"},
|
||||
{"Pacific/Kosrae", "<+11>-11"},
|
||||
{"Pacific/Kwajalein", "<+12>-12"},
|
||||
{"Pacific/Majuro", "<+12>-12"},
|
||||
{"Pacific/Marquesas", "<-0930>9:30"},
|
||||
{"Pacific/Midway", "SST11"},
|
||||
{"Pacific/Nauru", "<+12>-12"},
|
||||
{"Pacific/Niue", "<-11>11"},
|
||||
{"Pacific/Norfolk", "<+11>-11<+12>,M10.1.0,M4.1.0/3"},
|
||||
{"Pacific/Noumea", "<+11>-11"},
|
||||
{"Pacific/Pago_Pago", "SST11"},
|
||||
{"Pacific/Palau", "<+09>-9"},
|
||||
{"Pacific/Pitcairn", "<-08>8"},
|
||||
{"Pacific/Pohnpei", "<+11>-11"},
|
||||
{"Pacific/Port_Moresby", "<+10>-10"},
|
||||
{"Pacific/Rarotonga", "<-10>10"},
|
||||
{"Pacific/Saipan", "ChST-10"},
|
||||
{"Pacific/Tahiti", "<-10>10"},
|
||||
{"Pacific/Tarawa", "<+12>-12"},
|
||||
{"Pacific/Tongatapu", "<+13>-13"},
|
||||
{"Pacific/Wake", "<+12>-12"},
|
||||
{"Pacific/Wallis", "<+12>-12"}
|
||||
};
|
||||
|
||||
static char lower(char start) {
|
||||
if ('A' <= start && start <= 'Z') {
|
||||
return start - 'A' + 'a';
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basically strcmp, but accounting for spaces that have become underscores
|
||||
* @param[in] target - the 0-terminated string on the left hand side of the comparison
|
||||
* @param[in] other - the 0-terminated string on the right hand side of the comparison
|
||||
* @return > 0 if target comes before other alphabetically,
|
||||
* ==0 if they're the same,
|
||||
* < 0 if other comes before target alphabetically
|
||||
* (we don't expect NULL arguments, but, -1 if either is NULL)
|
||||
**/
|
||||
static int tz_name_cmp(const char * target, const char * other)
|
||||
{
|
||||
if (!target || !other) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (*target) {
|
||||
if (lower(*target) != lower(*other)) {
|
||||
break;
|
||||
}
|
||||
do {
|
||||
target++;
|
||||
} while (*target == '_');
|
||||
do {
|
||||
other++;
|
||||
} while (*other == '_');
|
||||
}
|
||||
|
||||
return lower(*target) - lower(*other);
|
||||
}
|
||||
|
||||
const char *esp_rmaker_tz_db_get_posix_str(const char *name)
|
||||
{
|
||||
int lo = 0, hi = sizeof(esp_rmaker_tz_db_tzs) / sizeof(esp_rmaker_tz_db_pair_t);
|
||||
while (lo < hi) {
|
||||
int mid = (lo + hi) / 2;
|
||||
esp_rmaker_tz_db_pair_t mid_pair = esp_rmaker_tz_db_tzs[mid];
|
||||
int comparison = tz_name_cmp(name, mid_pair.name);
|
||||
if (comparison == 0) {
|
||||
return mid_pair.posix_str;
|
||||
} else if (comparison < 0) {
|
||||
hi = mid;
|
||||
} else {
|
||||
lo = mid + 1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
174
components/rmaker_common/src/utils.c
Normal file
174
components/rmaker_common/src/utils.c
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2021 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 <stdint.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <esp_rmaker_common_events.h>
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(RMAKER_COMMON_EVENT);
|
||||
|
||||
static TimerHandle_t reboot_timer;
|
||||
static TimerHandle_t reset_timer;
|
||||
|
||||
static void esp_rmaker_reboot_cb(TimerHandle_t handle)
|
||||
{
|
||||
xTimerDelete(reboot_timer, 10);
|
||||
reboot_timer = NULL;
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_reboot(int8_t seconds)
|
||||
{
|
||||
/* If specified time is 0, reboot immediately */
|
||||
if (seconds == 0) {
|
||||
esp_event_post(RMAKER_COMMON_EVENT, RMAKER_EVENT_REBOOT, &seconds, sizeof(seconds), portMAX_DELAY);
|
||||
esp_restart();
|
||||
return ESP_OK;
|
||||
} else if (reboot_timer) {
|
||||
/* If reboot timer already exists, it means that a reboot operation is already in progress.
|
||||
* So, just return an error from here.
|
||||
*/
|
||||
return ESP_FAIL;
|
||||
}
|
||||
reboot_timer = xTimerCreate("rmaker_reboot_tm", (seconds * 1000) / portTICK_PERIOD_MS,
|
||||
pdFALSE, NULL, esp_rmaker_reboot_cb);
|
||||
if (reboot_timer) {
|
||||
if (xTimerStart(reboot_timer, 10) != pdTRUE) {
|
||||
xTimerDelete(reboot_timer, 10);
|
||||
reboot_timer = NULL;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
} else {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
esp_event_post(RMAKER_COMMON_EVENT, RMAKER_EVENT_REBOOT, &seconds, sizeof(seconds), portMAX_DELAY);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
static esp_err_t __esp_rmaker_wifi_reset(int8_t reboot_seconds)
|
||||
{
|
||||
esp_wifi_restore();
|
||||
if (reboot_seconds >= 0) {
|
||||
esp_rmaker_reboot(reboot_seconds);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
|
||||
static esp_err_t __esp_rmaker_factory_reset(int8_t reboot_seconds)
|
||||
{
|
||||
nvs_flash_deinit();
|
||||
nvs_flash_erase();
|
||||
if (reboot_seconds >= 0) {
|
||||
esp_rmaker_reboot(reboot_seconds);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
static void esp_rmaker_wifi_reset_cb(TimerHandle_t handle)
|
||||
{
|
||||
/* (Hack) Using the timer id as reboot seconds */
|
||||
int8_t *reboot_seconds = (int8_t *)pvTimerGetTimerID(handle);
|
||||
if (reboot_seconds) {
|
||||
__esp_rmaker_wifi_reset((int8_t)*reboot_seconds);
|
||||
free(reboot_seconds);
|
||||
} else {
|
||||
__esp_rmaker_wifi_reset(0);
|
||||
}
|
||||
xTimerDelete(reset_timer, 10);
|
||||
reset_timer = NULL;
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
|
||||
static void esp_rmaker_factory_reset_cb(TimerHandle_t handle)
|
||||
{
|
||||
/* (Hack) Using the timer id as reboot seconds */
|
||||
int8_t *reboot_seconds = (int8_t *)pvTimerGetTimerID(handle);
|
||||
if (reboot_seconds) {
|
||||
__esp_rmaker_factory_reset((int8_t)*reboot_seconds);
|
||||
free(reboot_seconds);
|
||||
} else {
|
||||
__esp_rmaker_factory_reset(0);
|
||||
}
|
||||
xTimerDelete(reset_timer, 10);
|
||||
reset_timer = NULL;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_start_reset_timer(int8_t reset_seconds, int8_t reboot_seconds, TimerCallbackFunction_t cb)
|
||||
{
|
||||
/* If reset timer already exists, it means that a reset operation is already in progress.
|
||||
* So, just return an error from here.
|
||||
*/
|
||||
if (reset_timer) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* (Hack) Passing the reboot delay as timer id */
|
||||
int8_t *reboot_delay = calloc(1, sizeof(int8_t));
|
||||
if (reboot_delay) {
|
||||
*reboot_delay = reboot_seconds;
|
||||
}
|
||||
reset_timer = xTimerCreate("rmaker_reset_tm", (reset_seconds * 1000) / portTICK_PERIOD_MS,
|
||||
pdFALSE, (void *)reboot_delay, cb);
|
||||
if (reset_timer) {
|
||||
if (xTimerStart(reset_timer, 10) != pdTRUE) {
|
||||
xTimerDelete(reset_timer, 10);
|
||||
reset_timer = NULL;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
} else {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_wifi_reset(int8_t reset_seconds, int8_t reboot_seconds)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
esp_err_t err = ESP_FAIL;
|
||||
/* If reset time is 0, just do it right away */
|
||||
if (reset_seconds == 0) {
|
||||
err = __esp_rmaker_wifi_reset(reboot_seconds);
|
||||
} else {
|
||||
/* Else start a timer so that the task gets performed after the specified delay */
|
||||
err = esp_rmaker_start_reset_timer(reset_seconds, reboot_seconds, esp_rmaker_wifi_reset_cb);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
esp_event_post(RMAKER_COMMON_EVENT, RMAKER_EVENT_WIFI_RESET, NULL, 0, portMAX_DELAY);
|
||||
}
|
||||
return ESP_OK;
|
||||
#else
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_factory_reset(int8_t reset_seconds, int8_t reboot_seconds)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
/* If reset time is 0, just do it right away */
|
||||
if (reset_seconds == 0) {
|
||||
err = __esp_rmaker_factory_reset(reboot_seconds);
|
||||
} else {
|
||||
/* Else start a timer so that the task gets performed after the specified delay */
|
||||
err = esp_rmaker_start_reset_timer(reset_seconds, reboot_seconds, esp_rmaker_factory_reset_cb);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
esp_event_post(RMAKER_COMMON_EVENT, RMAKER_EVENT_FACTORY_RESET, NULL, 0, portMAX_DELAY);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
148
components/rmaker_common/src/work_queue.c
Normal file
148
components/rmaker_common/src/work_queue.c
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2021 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 <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <esp_rmaker_work_queue.h>
|
||||
|
||||
#define ESP_RMAKER_TASK_QUEUE_SIZE 8
|
||||
#define ESP_RMAKER_TASK_STACK CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK
|
||||
#define ESP_RMAKER_TASK_PRIORITY CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_PRIORITY
|
||||
|
||||
static const char *TAG = "esp_rmaker_work_queue";
|
||||
|
||||
typedef enum {
|
||||
WORK_QUEUE_STATE_DEINIT = 0,
|
||||
WORK_QUEUE_STATE_INIT_DONE,
|
||||
WORK_QUEUE_STATE_RUNNING,
|
||||
WORK_QUEUE_STATE_STOP_REQUESTED,
|
||||
} esp_rmaker_work_queue_state_t;
|
||||
|
||||
typedef struct {
|
||||
esp_rmaker_work_fn_t work_fn;
|
||||
void *priv_data;
|
||||
} esp_rmaker_work_queue_entry_t;
|
||||
|
||||
static QueueHandle_t work_queue;
|
||||
static esp_rmaker_work_queue_state_t queue_state;
|
||||
|
||||
static void esp_rmaker_handle_work_queue(void)
|
||||
{
|
||||
esp_rmaker_work_queue_entry_t work_queue_entry;
|
||||
/* 2 sec delay to prevent spinning */
|
||||
BaseType_t ret = xQueueReceive(work_queue, &work_queue_entry, 2000 / portTICK_PERIOD_MS);
|
||||
while (ret == pdTRUE) {
|
||||
work_queue_entry.work_fn(work_queue_entry.priv_data);
|
||||
ret = xQueueReceive(work_queue, &work_queue_entry, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_rmaker_work_queue_task(void *param)
|
||||
{
|
||||
ESP_LOGI(TAG, "RainMaker Work Queue task started.");
|
||||
while (queue_state != WORK_QUEUE_STATE_STOP_REQUESTED) {
|
||||
esp_rmaker_handle_work_queue();
|
||||
}
|
||||
ESP_LOGI(TAG, "Stopping Work Queue task");
|
||||
queue_state = WORK_QUEUE_STATE_INIT_DONE;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_work_queue_add_task(esp_rmaker_work_fn_t work_fn, void *priv_data)
|
||||
{
|
||||
if (!work_queue) {
|
||||
ESP_LOGE(TAG, "Cannot enqueue function as Work Queue hasn't been created.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_rmaker_work_queue_entry_t work_queue_entry = {
|
||||
.work_fn = work_fn,
|
||||
.priv_data = priv_data,
|
||||
};
|
||||
if (xQueueSend(work_queue, &work_queue_entry, 0) == pdTRUE) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_work_queue_init(void)
|
||||
{
|
||||
if (queue_state != WORK_QUEUE_STATE_DEINIT) {
|
||||
ESP_LOGW(TAG, "Work Queue already initialiased/started.");
|
||||
return ESP_OK;
|
||||
}
|
||||
work_queue = xQueueCreate(ESP_RMAKER_TASK_QUEUE_SIZE, sizeof(esp_rmaker_work_queue_entry_t));
|
||||
if (!work_queue) {
|
||||
ESP_LOGE(TAG, "Failed to create Work Queue.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Work Queue created.");
|
||||
queue_state = WORK_QUEUE_STATE_INIT_DONE;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_work_queue_deinit(void)
|
||||
{
|
||||
if (queue_state != WORK_QUEUE_STATE_STOP_REQUESTED) {
|
||||
esp_rmaker_work_queue_stop();
|
||||
}
|
||||
|
||||
while (queue_state == WORK_QUEUE_STATE_STOP_REQUESTED) {
|
||||
ESP_LOGI(TAG, "Waiting for esp_rmaker_work_queue being stopped...");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
if (queue_state == WORK_QUEUE_STATE_DEINIT) {
|
||||
return ESP_OK;
|
||||
} else if (queue_state != WORK_QUEUE_STATE_INIT_DONE) {
|
||||
ESP_LOGE(TAG, "Cannot deinitialize Work Queue as the task is still running.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
} else {
|
||||
vQueueDelete(work_queue);
|
||||
work_queue = NULL;
|
||||
queue_state = WORK_QUEUE_STATE_DEINIT;
|
||||
}
|
||||
ESP_LOGI(TAG, "esp_rmaker_work_queue was successfully deinitialized");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_work_queue_start(void)
|
||||
{
|
||||
if (queue_state == WORK_QUEUE_STATE_RUNNING) {
|
||||
ESP_LOGW(TAG, "Work Queue already started.");
|
||||
return ESP_OK;
|
||||
}
|
||||
if (queue_state != WORK_QUEUE_STATE_INIT_DONE) {
|
||||
ESP_LOGE(TAG, "Failed to start Work Queue as it wasn't initialized.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (xTaskCreate(&esp_rmaker_work_queue_task, "rmaker_queue_task", ESP_RMAKER_TASK_STACK,
|
||||
NULL, ESP_RMAKER_TASK_PRIORITY, NULL) != pdPASS) {
|
||||
ESP_LOGE(TAG, "Couldn't create RainMaker work queue task");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
queue_state = WORK_QUEUE_STATE_RUNNING;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_work_queue_stop(void)
|
||||
{
|
||||
if (queue_state == WORK_QUEUE_STATE_RUNNING) {
|
||||
queue_state = WORK_QUEUE_STATE_STOP_REQUESTED;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user