Initial commit.
Some checks failed
Sync remain PRs to Jira / Sync PRs to Jira (push) Has been cancelled

This commit is contained in:
2025-04-30 16:33:57 +03:00
commit 34cf3ec285
193 changed files with 25742 additions and 0 deletions

View 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;
}

View 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;
}

View 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;
}

View File

@@ -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);

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}