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:
21
components/rmaker_common/.github/workflows/upload_components.yml
vendored
Normal file
21
components/rmaker_common/.github/workflows/upload_components.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Push components to Espressif Component Registry
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
upload_components:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Upload components to component registry
|
||||
uses: espressif/upload-components-ci-action@v1
|
||||
with:
|
||||
namespace: "espressif"
|
||||
name: "rmaker_common"
|
||||
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||
23
components/rmaker_common/CMakeLists.txt
Normal file
23
components/rmaker_common/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
set(srcs "src/work_queue.c" "src/factory.c" "src/time.c" "src/timezone.c" "src/utils.c"
|
||||
"src/cmd_resp.c" "src/console/rmaker_common_cmds.c" "src/console/rmaker_console.c")
|
||||
|
||||
set(priv_req mqtt nvs_flash console nvs_flash esp_wifi driver)
|
||||
set(requires esp_event)
|
||||
|
||||
# esp_timer component was introduced in v4.2
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.1")
|
||||
list(APPEND priv_req esp_timer)
|
||||
endif()
|
||||
|
||||
#if(CONFIG_ESP_RMAKER_LIB_ESP_MQTT)
|
||||
list(APPEND srcs "src/esp-mqtt/esp-mqtt-glue.c")
|
||||
#endif()
|
||||
if(CONFIG_ESP_RMAKER_MQTT_SEND_USERNAME)
|
||||
list(APPEND srcs "src/create_APN3_PPI_string.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS
|
||||
REQUIRES ${requires}
|
||||
PRIV_REQUIRES ${priv_req})
|
||||
181
components/rmaker_common/Kconfig
Normal file
181
components/rmaker_common/Kconfig
Normal file
@@ -0,0 +1,181 @@
|
||||
menu "ESP RainMaker Common"
|
||||
|
||||
choice ESP_RMAKER_MQTT_GLUE_LIB
|
||||
bool "MQTT Library"
|
||||
default ESP_RMAKER_LIB_ESP_MQTT
|
||||
help
|
||||
MQTT Library to be used
|
||||
|
||||
config ESP_RMAKER_LIB_ESP_MQTT
|
||||
bool "ESP-MQTT"
|
||||
config ESP_RMAKER_LIB_AWS_IOT
|
||||
bool "AWS-IOT"
|
||||
endchoice
|
||||
|
||||
config ESP_RMAKER_MQTT_GLUE_LIB
|
||||
int
|
||||
default 1 if ESP_RMAKER_LIB_ESP_MQTT
|
||||
default 2 if ESP_RMAKER_LIB_AWS_IOT
|
||||
|
||||
choice ESP_RMAKER_MQTT_PORT
|
||||
bool "MQTT Port"
|
||||
default ESP_RMAKER_MQTT_PORT_443
|
||||
help
|
||||
ESP RainMaker MQTT Broker can be connected to either on Port 8883 or port 443.
|
||||
Port 443 is recommended as it is generally not blocked by any firewalls,
|
||||
since it is standard HTTPS port.
|
||||
|
||||
config ESP_RMAKER_MQTT_PORT_443
|
||||
bool "443"
|
||||
config ESP_RMAKER_MQTT_PORT_8883
|
||||
bool "8883"
|
||||
endchoice
|
||||
|
||||
config ESP_RMAKER_MQTT_PORT
|
||||
int
|
||||
default 1 if ESP_RMAKER_MQTT_PORT_443
|
||||
default 2 if ESP_RMAKER_MQTT_PORT_8883
|
||||
|
||||
config ESP_RMAKER_MQTT_PERSISTENT_SESSION
|
||||
bool "Use Persisent MQTT sessions"
|
||||
default n
|
||||
help
|
||||
Use persistent MQTT sessions. This improves reliability as QOS1 messages missed
|
||||
out due to some network issue are received after the MQTT reconnects. The broker
|
||||
caches messages for a period of upto 1 hour. However, a side-effect of this is that
|
||||
messages can be received at unexpected time. Enable this option only if it suits
|
||||
your use case. Please read MQTT specs to understand more about persistent sessions
|
||||
and the cleanSession flag.
|
||||
|
||||
config ESP_RMAKER_MQTT_SEND_USERNAME
|
||||
bool "Send MQTT Username"
|
||||
default y
|
||||
help
|
||||
Send a Username during MQTT Connect. This is generally required only for tracking
|
||||
the MQTT client types, platform, SDK, etc. in AWS.
|
||||
|
||||
config ESP_RMAKER_MQTT_PRODUCT_NAME
|
||||
string "Product Name"
|
||||
depends on ESP_RMAKER_MQTT_SEND_USERNAME
|
||||
default "RMDev"
|
||||
help
|
||||
Approved AWS product name. Please get in touch with your Espressif/AWS representative for more info.
|
||||
|
||||
config ESP_RMAKER_MQTT_PRODUCT_VERSION
|
||||
string "Product Version"
|
||||
depends on ESP_RMAKER_MQTT_SEND_USERNAME
|
||||
default "1x0"
|
||||
help
|
||||
Approved AWS product version. Please get in touch with your Espressif/AWS representative for more info.
|
||||
|
||||
config ESP_RMAKER_MQTT_PRODUCT_SKU
|
||||
string "Product SKU"
|
||||
depends on ESP_RMAKER_MQTT_SEND_USERNAME
|
||||
default "EX00"
|
||||
help
|
||||
Product SKU. Please get in touch with your Espressif/AWS representative for more info.
|
||||
|
||||
config ESP_RMAKER_MQTT_USE_CERT_BUNDLE
|
||||
bool "Use Certificate Bundle"
|
||||
default y
|
||||
help
|
||||
Use Certificate Bundle for server authentication. Enabling this is recommended to safeguard
|
||||
against any changes in the server certificates in future. This has an impact on the binary
|
||||
size as well as heap requirement.
|
||||
|
||||
config ESP_RMAKER_MAX_MQTT_SUBSCRIPTIONS
|
||||
int "Maximum number of MQTT Subscriptions"
|
||||
default 10
|
||||
help
|
||||
This value controls the maximum number of topics that the device can subscribe to.
|
||||
|
||||
config ESP_RMAKER_MQTT_KEEP_ALIVE_INTERVAL
|
||||
int "MQTT Keep Alive Internal"
|
||||
default 120
|
||||
range 30 1200
|
||||
help
|
||||
MQTT Keep Alive Interval. Note that it can take upto 1.5x of keep alive interval for a device
|
||||
to be reported by offline by the MQTT Broker. Change this only if required.
|
||||
|
||||
choice ESP_RMAKER_NETWORK_PROTOCOL_TYPE
|
||||
prompt "ESP RainMaker Network Type"
|
||||
default ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
help
|
||||
Network protocol type over which the ESP RainMaker will run.
|
||||
|
||||
config ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
# ESP_WIFI_ENABLED was introduced in IDF v5.0
|
||||
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || ESP_WIFI_ENABLED
|
||||
bool "ESP RainMaker over Wi-Fi"
|
||||
|
||||
config ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
depends on OPENTHREAD_ENABLED
|
||||
bool "ESP RainMaker over Thread"
|
||||
endchoice
|
||||
|
||||
config ESP_RMAKER_WORK_QUEUE_TASK_STACK
|
||||
int "ESP RainMaker Work Queue Task stack"
|
||||
default 4096
|
||||
help
|
||||
Stack size for the ESP RainMaker Work Queue Task.
|
||||
|
||||
config ESP_RMAKER_WORK_QUEUE_TASK_PRIORITY
|
||||
int "ESP RainMaker Work Queue Task priority"
|
||||
default 5
|
||||
help
|
||||
Priority for the ESP RainMaker Work Queue Task. Not recommended to be changed
|
||||
unless you really need it.
|
||||
|
||||
config ESP_RMAKER_FACTORY_PARTITION_NAME
|
||||
string "ESP RainMaker Factory Partition Name"
|
||||
default "fctry"
|
||||
help
|
||||
Factory NVS Partition name which will have the MQTT connectivity credentials.
|
||||
|
||||
config ESP_RMAKER_FACTORY_NAMESPACE
|
||||
string "ESP RainMaker Factory Namespace"
|
||||
default "rmaker_creds"
|
||||
help
|
||||
Namespace in the Factory NVS Partition name which will have the MQTT
|
||||
connectivity credentials.
|
||||
|
||||
config ESP_RMAKER_ENCRYPT_FACTORY_PARTITION
|
||||
bool "Encrypt Rainmaker Factory partition"
|
||||
default false
|
||||
depends on NVS_ENCRYPTION
|
||||
help
|
||||
Enable this option if the factory partition is pre-encrypted before flashing and the encryption keys
|
||||
are flashed in partition with subtype nvs_keys specified by CONFIG_ESP_RMAKER_FACTORY_NVS_KEYS_PARTITION_NAME.
|
||||
If an unencrypted factory partition is flashed, the device would not be able to read its data and
|
||||
the partition would be considered empty.
|
||||
If nvs encryption keys are not flashed onto device, they would be auto-generated and any previous data
|
||||
in nvs/factory partition would become invalid.
|
||||
|
||||
config ESP_RMAKER_FACTORY_NVS_KEYS_PARTITION_NAME
|
||||
string "ESP Rainmaker Factory NVS keys partition label"
|
||||
default "nvs_key"
|
||||
depends on ESP_RMAKER_ENCRYPT_FACTORY_PARTITION
|
||||
help
|
||||
Label of the partition of subtype "nvs_keys" used for encrypting/decrypting Rainmaker factory partition.
|
||||
|
||||
config ESP_RMAKER_DEF_TIMEZONE
|
||||
string "Default Timezone"
|
||||
default "Asia/Shanghai"
|
||||
help
|
||||
Default Timezone to use. Eg. "Asia/Shanghai", "America/Los_Angeles".
|
||||
Check documentation for complete list of valid values. This value
|
||||
will be used only if no timezone is set using the C APIs.
|
||||
|
||||
config ESP_RMAKER_SNTP_SERVER_NAME
|
||||
string "ESP RainMaker SNTP Server Name"
|
||||
default "pool.ntp.org"
|
||||
help
|
||||
Default SNTP Server which is used for time synchronization.
|
||||
|
||||
config ESP_RMAKER_MAX_COMMANDS
|
||||
int "Maximum commands supported for command-response"
|
||||
default 10
|
||||
help
|
||||
Maximum number of commands supported by the command-response framework
|
||||
|
||||
endmenu
|
||||
201
components/rmaker_common/LICENSE
Normal file
201
components/rmaker_common/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
9
components/rmaker_common/README.md
Normal file
9
components/rmaker_common/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# ESP RainMaker Common modules
|
||||
|
||||
This component consists of some common modules used by ESP RainMaker and ESP RainMaker Diagnostics repos.
|
||||
Currently, it consists of
|
||||
- MQTT glue layer
|
||||
- Timing APIs (SNTP helpers, timezone, etc.)
|
||||
- Factory NVS helpers
|
||||
- Work Queue
|
||||
|
||||
5
components/rmaker_common/component.mk
Normal file
5
components/rmaker_common/component.mk
Normal file
@@ -0,0 +1,5 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
COMPONENT_SRCDIRS := src
|
||||
ifdef CONFIG_ESP_RMAKER_LIB_ESP_MQTT
|
||||
COMPONENT_SRCDIRS += src/esp-mqtt
|
||||
endif
|
||||
3
components/rmaker_common/idf_component.yml
Normal file
3
components/rmaker_common/idf_component.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
version: "1.4.6"
|
||||
description: ESP RainMaker firmware agent Common component
|
||||
url: https://github.com/espressif/esp-rainmaker-common
|
||||
184
components/rmaker_common/include/esp_rmaker_cmd_resp.h
Normal file
184
components/rmaker_common/include/esp_rmaker_cmd_resp.h
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_event.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Super Admin User Flag*/
|
||||
#define ESP_RMAKER_USER_ROLE_SUPER_ADMIN (1 << 0)
|
||||
|
||||
/** Primary User Flag */
|
||||
#define ESP_RMAKER_USER_ROLE_PRIMARY_USER (1 << 1)
|
||||
|
||||
/** Secondary User Flag */
|
||||
#define ESP_RMAKER_USER_ROLE_SECONDARY_USER (1 << 2)
|
||||
|
||||
|
||||
/** RainMaker Command Response TLV8 Types */
|
||||
typedef enum {
|
||||
/** Request Id : Variable length string, max 32 characters*/
|
||||
ESP_RMAKER_TLV_TYPE_REQ_ID = 1,
|
||||
/** User Role : 1 byte */
|
||||
ESP_RMAKER_TLV_TYPE_USER_ROLE,
|
||||
/** Status : 1 byte */
|
||||
ESP_RMAKER_TLV_TYPE_STATUS,
|
||||
/** Timestamp : TBD */
|
||||
ESP_RMAKER_TLV_TYPE_TIMESTAMP,
|
||||
/** Command : 2 bytes*/
|
||||
ESP_RMAKER_TLV_TYPE_CMD,
|
||||
/** Data : Variable length */
|
||||
ESP_RMAKER_TLV_TYPE_DATA
|
||||
} esp_rmaker_tlv_type_t;
|
||||
|
||||
/* RainMaker Command Response Status */
|
||||
typedef enum {
|
||||
/** Success */
|
||||
ESP_RMAKER_CMD_STATUS_SUCCESS = 0,
|
||||
/** Generic Failure */
|
||||
ESP_RMAKER_CMD_STATUS_FAILED,
|
||||
/** Invalid Command */
|
||||
ESP_RMAKER_CMD_STATUS_CMD_INVALID,
|
||||
/** Authentication Failed */
|
||||
ESP_RMAKER_CMD_STATUS_AUTH_FAIL,
|
||||
/** Command not found */
|
||||
ESP_RMAKER_CMD_STATUS_NOT_FOUND,
|
||||
/** Last status value */
|
||||
ESP_RMAKER_CMD_STATUS_MAX,
|
||||
} esp_rmaker_cmd_status_t;
|
||||
|
||||
#define REQ_ID_LEN 32
|
||||
typedef struct {
|
||||
/** Command id */
|
||||
uint16_t cmd;
|
||||
/** Request id */
|
||||
char req_id[REQ_ID_LEN];
|
||||
/** User Role */
|
||||
uint8_t user_role;
|
||||
} esp_rmaker_cmd_ctx_t;
|
||||
|
||||
typedef enum {
|
||||
/** Standard command: Set Parameters */
|
||||
ESP_RMAKER_CMD_TYPE_SET_PARAMS = 1,
|
||||
/** Last Standard command */
|
||||
ESP_RMAKER_CMD_STANDARD_LAST = 0xfff,
|
||||
/** Custom commands can start from here */
|
||||
ESP_RMAKER_CMD_CUSTOM_START = 0x1000
|
||||
} esp_rmaker_cmd_t;
|
||||
|
||||
/** Command Response Handler
|
||||
*
|
||||
* If any command data is received from any of the supported transports (which are outside the scope of this core framework),
|
||||
* this function should be called to handle it and fill in the response.
|
||||
*
|
||||
* @param[in] input Pointer to input data.
|
||||
* @param[in] input_len data len.
|
||||
* @param[in] output Pointer to output data which should be set by the handler.
|
||||
* @param[out] output_len Length of output generated.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_cmd_response_handler(const void *input, size_t input_len, void **output, size_t *output_len);
|
||||
|
||||
/** Prototype for Command Handler
|
||||
*
|
||||
* The handler to be invoked when a given command is received.
|
||||
*
|
||||
* @param[in] in_data Pointer to input data.
|
||||
* @param[in] in_len data len.
|
||||
* @param[in] out_data Pointer to output data which should be set by the handler.
|
||||
* @param[out] out_len Length of output generated.
|
||||
* @param[in] ctx Command Context.
|
||||
* @param[in] priv Private data, if specified while registering command.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
typedef esp_err_t (*esp_rmaker_cmd_handler_t)(const void *in_data, size_t in_len, void **out_data, size_t *out_len, esp_rmaker_cmd_ctx_t *ctx, void *priv);
|
||||
|
||||
/** Register a new command
|
||||
*
|
||||
* @param[in] cmd Command Identifier. Custom commands should start beyond ESP_RMAKER_CMD_STANDARD_LAST
|
||||
* @param[in] access User Access for the command. Can be an OR of the various user role flags like ESP_RMAKER_USER_ROLE_SUPER_ADMIN,
|
||||
* ESP_RMAKER_USER_ROLE_PRIMARY_USER and ESP_RMAKER_USER_ROLE_SECONDARY_USER
|
||||
* @param[in] handler The handler to be invoked when the given command is received.
|
||||
* @param[in] free_on_return Flag to indicate of the framework should free the output after it has been sent as response.
|
||||
* @param[in] priv Optional private data to be passed to the handler.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
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);
|
||||
|
||||
/** De-register a command
|
||||
*
|
||||
* @param[in] cmd Command Identifier. Custom commands should start beyond ESP_RMAKER_CMD_STANDARD_LAST
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_cmd_deregister(uint16_t cmd);
|
||||
|
||||
/* Prepare an empty command response
|
||||
*
|
||||
* This can be used to populate the request to be sent to get all pending commands
|
||||
*
|
||||
* @param[in] out_data Pointer to output data. This function will allocate memory and set this pointer
|
||||
* accordingly.
|
||||
* @param[out] out_len Length of output generated.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_cmd_prepare_empty_response(void **output, size_t *output_len);
|
||||
|
||||
/** Prototype for Command sending function (TESTING only)
|
||||
*
|
||||
* @param[in] data Pointer to the data to be sent.
|
||||
* @param[in] data_len Size of data to be sent.
|
||||
* @param[in] priv Private data, if applicable.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
typedef esp_err_t (*esp_rmaker_cmd_send_t)(const void *data, size_t data_len, void *priv);
|
||||
|
||||
/** Send Test command (TESTING only)
|
||||
*
|
||||
* @param[in] req_id NULL terminated request id of max 32 characters.
|
||||
* @param[in] role User Role flag.
|
||||
* @param[in] cmd Command Identifier.
|
||||
* @param[in] data Pointer to data for the command.
|
||||
* @param[in] data_size Size of the data.
|
||||
* @param[in] cmd_send Transport specific function to send the command data.
|
||||
* @param[in] priv Private data (if any) to be sent to cmd_send.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
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);
|
||||
|
||||
/** Parse response (TESTING only)
|
||||
*
|
||||
* @param[in] response Pointer to the response received
|
||||
* @param[in] response_len Length of the response
|
||||
* @param[in] priv Private data, if any. Can be NULL.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_cmd_resp_parse_response(const void *response, size_t response_len, void *priv);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
47
components/rmaker_common/include/esp_rmaker_common_console.h
Normal file
47
components/rmaker_common/include/esp_rmaker_common_console.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize console
|
||||
*
|
||||
* Initializes serial console and adds basic commands.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failures.
|
||||
*/
|
||||
esp_err_t esp_rmaker_common_console_init(void);
|
||||
|
||||
/* Reference for adding custom console commands:
|
||||
#include <esp_console.h>
|
||||
|
||||
static int command_console_handler(int argc, char *argv[])
|
||||
{
|
||||
// Command code here
|
||||
}
|
||||
|
||||
static void register_console_command()
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "<command_name>",
|
||||
.help = "<help_details>",
|
||||
.func = &command_console_handler,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
60
components/rmaker_common/include/esp_rmaker_common_events.h
Normal file
60
components/rmaker_common/include/esp_rmaker_common_events.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <esp_event.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** ESP RainMaker Common Event Base */
|
||||
ESP_EVENT_DECLARE_BASE(RMAKER_COMMON_EVENT);
|
||||
|
||||
typedef enum {
|
||||
/** Node reboot has been triggered. The associated event data is the time in seconds
|
||||
* (type: uint8_t) after which the node will reboot. Note that this time may not be
|
||||
* accurate as the events are received asynchronously.*/
|
||||
RMAKER_EVENT_REBOOT,
|
||||
/** Wi-Fi credentials reset. Triggered after calling esp_rmaker_wifi_reset() */
|
||||
RMAKER_EVENT_WIFI_RESET,
|
||||
/** Node reset to factory defaults. Triggered after calling esp_rmaker_factory_reset() */
|
||||
RMAKER_EVENT_FACTORY_RESET,
|
||||
/** Connected to MQTT Broker */
|
||||
RMAKER_MQTT_EVENT_CONNECTED,
|
||||
/** Disconnected from MQTT Broker */
|
||||
RMAKER_MQTT_EVENT_DISCONNECTED,
|
||||
/** MQTT message published successfully.
|
||||
* Event data will contain the message ID (integer) of published message.
|
||||
*/
|
||||
RMAKER_MQTT_EVENT_PUBLISHED,
|
||||
/** POSIX Timezone Changed. Associated data would be NULL terminated POSIX Timezone
|
||||
* Eg. "PST8PDT,M3.2.0,M11.1.0" */
|
||||
RMAKER_EVENT_TZ_POSIX_CHANGED,
|
||||
/** Timezone Changed. Associated data would be NULL terminated Timezone.
|
||||
* Eg. "America/Los_Angeles"
|
||||
* Note that whenever this event is received, the RMAKER_EVENT_TZ_POSIX_CHANGED event
|
||||
* will also be received, but not necessarily vice versa.
|
||||
*/
|
||||
RMAKER_EVENT_TZ_CHANGED,
|
||||
/**
|
||||
* MQTT message deleted from the outbox if the message couldn't have been sent and acknowledged.
|
||||
* Event data will contain the message ID (integer) of deleted message.
|
||||
* Valid only if CONFIG_MQTT_REPORT_DELETED_MESSAGES is enabled.
|
||||
*/
|
||||
RMAKER_MQTT_EVENT_MSG_DELETED,
|
||||
} esp_rmaker_common_event_t;
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
73
components/rmaker_common/include/esp_rmaker_factory.h
Normal file
73
components/rmaker_common/include/esp_rmaker_factory.h
Normal file
@@ -0,0 +1,73 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_event.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** Initialize Factory NVS
|
||||
*
|
||||
* This initializes the Factory NVS partition which will store data
|
||||
* that should not be cleared even after a reset to factory.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_factory_init(void);
|
||||
|
||||
/** Get value from factory NVS
|
||||
*
|
||||
* This will search for the specified key in the Factory NVS partition,
|
||||
* allocate the required memory to hold it, copy the value and return
|
||||
* the pointer to it. It is responsibility of the caller to free the
|
||||
* memory when the value is no more required.
|
||||
*
|
||||
* @param[in] key The key of the value to be read from factory NVS.
|
||||
*
|
||||
* @return pointer to the value on success.
|
||||
* @return NULL on failure.
|
||||
*/
|
||||
void *esp_rmaker_factory_get(const char *key);
|
||||
|
||||
/** Get size of value from factory NVS
|
||||
*
|
||||
* This will search for the specified key in the Factory NVS partition,
|
||||
* and return the size of the value associated with the key.
|
||||
*
|
||||
* @param[in] key The key of the value to be read from factory NVS.
|
||||
*
|
||||
* @return size of the value on success.
|
||||
* @return 0 on failure.
|
||||
*/
|
||||
size_t esp_rmaker_factory_get_size(const char *key);
|
||||
|
||||
/** Set a value in factory NVS
|
||||
*
|
||||
* This will write the value for the specified key into factory NVS.
|
||||
*
|
||||
* @param[in] key The key for the value to be set in factory NVS.
|
||||
* @param[in] value Pointer to the value.
|
||||
* @param[in] len Length of the value.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_factory_set(const char *key, void *value, size_t len);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
177
components/rmaker_common/include/esp_rmaker_mqtt_glue.h
Normal file
177
components/rmaker_common/include/esp_rmaker_mqtt_glue.h
Normal file
@@ -0,0 +1,177 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_event.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define RMAKER_MQTT_QOS0 0
|
||||
#define RMAKER_MQTT_QOS1 1
|
||||
|
||||
/** MQTT Connection parameters */
|
||||
typedef struct {
|
||||
/** MQTT Host */
|
||||
char *mqtt_host;
|
||||
/** Client ID */
|
||||
char *client_id;
|
||||
/** Client Certificate in DER format or NULL-terminated PEM format */
|
||||
char *client_cert;
|
||||
/** Client Certificate length */
|
||||
size_t client_cert_len;
|
||||
/** Client Key in DER format or NULL-terminated PEM format */
|
||||
char *client_key;
|
||||
/** Client Key length */
|
||||
size_t client_key_len;
|
||||
/** Server Certificate in DER format or NULL-terminated PEM format */
|
||||
char *server_cert;
|
||||
/** Server Certificate length */
|
||||
size_t server_cert_len;
|
||||
/** Pointer for digital signature peripheral context */
|
||||
void *ds_data;
|
||||
} esp_rmaker_mqtt_conn_params_t;
|
||||
|
||||
/** MQTT Get Connection Parameters function prototype
|
||||
*
|
||||
* @return Pointer to \ref esp_rmaker_mqtt_conn_params_t on success.
|
||||
* @return NULL on failure.
|
||||
*/
|
||||
typedef esp_rmaker_mqtt_conn_params_t *(*esp_rmaker_mqtt_get_conn_params_t)(void);
|
||||
|
||||
/** MQTT Subscribe callback prototype
|
||||
*
|
||||
* @param[in] topic Topic on which the message was received
|
||||
* @param[in] payload Data received in the message
|
||||
* @param[in] payload_len Length of the data
|
||||
* @param[in] priv_data The private data passed during subscription
|
||||
*/
|
||||
typedef void (*esp_rmaker_mqtt_subscribe_cb_t)(const char *topic, void *payload, size_t payload_len, void *priv_data);
|
||||
|
||||
/** MQTT Init function prototype
|
||||
*
|
||||
* @param[in] conn_params The MQTT connection parameters. If NULL is passed, it should internally use the
|
||||
* \ref esp_rmaker_mqtt_get_conn_params call if registered.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
typedef esp_err_t (*esp_rmaker_mqtt_init_t)(esp_rmaker_mqtt_conn_params_t *conn_params);
|
||||
|
||||
/** MQTT Deinit function prototype
|
||||
*
|
||||
* Call this function after MQTT has disconnected.
|
||||
*/
|
||||
typedef void (*esp_rmaker_mqtt_deinit_t)(void);
|
||||
|
||||
/** MQTT Connect function prototype
|
||||
*
|
||||
* Starts the connection attempts to the MQTT broker.
|
||||
* This should ideally be called after successful network connection.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
typedef esp_err_t (*esp_rmaker_mqtt_connect_t)(void);
|
||||
|
||||
/** MQTT Disconnect function prototype
|
||||
*
|
||||
* Disconnects from the MQTT broker.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
typedef esp_err_t (*esp_rmaker_mqtt_disconnect_t)(void);
|
||||
|
||||
/** MQTT Publish Message function prototype
|
||||
*
|
||||
* @param[in] topic The MQTT topic on which the message should be published.
|
||||
* @param[in] data Data to be published.
|
||||
* @param[in] data_len Length of the data.
|
||||
* @param[in] qos Quality of service for the message.
|
||||
* @param[out] msg_id If a non NULL pointer is passed, the id of the published message will be returned in this.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
typedef esp_err_t (*esp_rmaker_mqtt_publish_t)(const char *topic, void *data, size_t data_len, uint8_t qos, int *msg_id);
|
||||
|
||||
/** MQTT Subscribe function prototype
|
||||
*
|
||||
* @param[in] topic The topic to be subscribed to.
|
||||
* @param[in] cb The callback to be invoked when a message is received on the given topic.
|
||||
* @param[in] qos Quality of service for the subscription.
|
||||
* @param[in] priv_data Optional private data to be passed to the callback.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
typedef esp_err_t (*esp_rmaker_mqtt_subscribe_t)(const char *topic, esp_rmaker_mqtt_subscribe_cb_t cb, uint8_t qos, void *priv_data);
|
||||
|
||||
/** MQTT Unsubscribe function prototype
|
||||
*
|
||||
* @param[in] topic Topic from which to unsubscribe.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
typedef esp_err_t (*esp_rmaker_mqtt_unsubscribe_t)(const char *topic);
|
||||
|
||||
/** MQTT configuration */
|
||||
typedef struct {
|
||||
/** Flag to indicate if the MQTT config setup is done */
|
||||
bool setup_done;
|
||||
/** Pointer to the Get MQTT params function. */
|
||||
esp_rmaker_mqtt_get_conn_params_t get_conn_params;
|
||||
/** Pointer to MQTT Init function. */
|
||||
esp_rmaker_mqtt_init_t init;
|
||||
/** Pointer to MQTT Deinit function. */
|
||||
esp_rmaker_mqtt_deinit_t deinit;
|
||||
/** Pointer to MQTT Connect function. */
|
||||
esp_rmaker_mqtt_connect_t connect;
|
||||
/** Pointer to MQTQ Disconnect function */
|
||||
esp_rmaker_mqtt_disconnect_t disconnect;
|
||||
/** Pointer to MQTT Publish function */
|
||||
esp_rmaker_mqtt_publish_t publish;
|
||||
/** Pointer to MQTT Subscribe function */
|
||||
esp_rmaker_mqtt_subscribe_t subscribe;
|
||||
/** Pointer to MQTT Unsubscribe function */
|
||||
esp_rmaker_mqtt_unsubscribe_t unsubscribe;
|
||||
} esp_rmaker_mqtt_config_t;
|
||||
|
||||
/** Setup MQTT Glue
|
||||
*
|
||||
* This function initializes MQTT glue layer with all the default functions.
|
||||
*
|
||||
* @param[out] mqtt_config Pointer to an allocated MQTT configuration structure.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
esp_err_t esp_rmaker_mqtt_glue_setup(esp_rmaker_mqtt_config_t *mqtt_config);
|
||||
|
||||
/* Get the ESP AWS PPI String
|
||||
*
|
||||
* @return pointer to a NULL terminated PPI string on success.
|
||||
* @return NULL in case of any error.
|
||||
*/
|
||||
const char *esp_get_aws_ppi(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
211
components/rmaker_common/include/esp_rmaker_utils.h
Normal file
211
components/rmaker_common/include/esp_rmaker_utils.h
Normal file
@@ -0,0 +1,211 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
#include <esp_idf_version.h>
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
#include <esp_sntp.h>
|
||||
#else
|
||||
#include <sntp.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <sdkconfig.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
#define MEM_ALLOC_EXTRAM(size) heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL)
|
||||
#define MEM_CALLOC_EXTRAM(num, size) heap_caps_calloc_prefer(num, size, 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL)
|
||||
#define MEM_REALLOC_EXTRAM(ptr, size) heap_caps_realloc_prefer(ptr, size, 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL)
|
||||
#else
|
||||
#define MEM_ALLOC_EXTRAM(size) malloc(size)
|
||||
#define MEM_CALLOC_EXTRAM(num, size) calloc(num, size)
|
||||
#define MEM_REALLOC_EXTRAM(ptr, size) realloc(ptr, size)
|
||||
#endif
|
||||
|
||||
typedef struct esp_rmaker_time_config {
|
||||
/** If not specified, then 'CONFIG_ESP_RMAKER_SNTP_SERVER_NAME' is used as the SNTP server. */
|
||||
char *sntp_server_name;
|
||||
/** Optional callback to invoke, whenever time is synchronised. This will be called
|
||||
* periodically as per the SNTP polling interval (which is 60min by default).
|
||||
* If kept NULL, the default callback will be invoked, which will just print the
|
||||
* current local time.
|
||||
*/
|
||||
sntp_sync_time_cb_t sync_time_cb;
|
||||
} esp_rmaker_time_config_t;
|
||||
|
||||
/** Reboot the device after a delay
|
||||
*
|
||||
* This API just starts a reboot timer and returns immediately.
|
||||
* The actual reboot is trigerred asynchronously in the timer callback.
|
||||
* This is useful if you want to reboot after a delay, to allow other tasks to finish
|
||||
* their operations (Eg. MQTT publish to indicate OTA success). The \ref RMAKER_EVENT_REBOOT
|
||||
* event is triggered when the reboot timer is started.
|
||||
*
|
||||
* @param[in] seconds Time in seconds after which the device should reboot.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_reboot(int8_t seconds);
|
||||
|
||||
/** Reset Wi-Fi credentials and (optionally) reboot
|
||||
*
|
||||
* This will reset just the Wi-Fi credentials and (optionally) trigger a reboot.
|
||||
* This is useful when you want to keep all the entries in NVS memory
|
||||
* intact, but just change the Wi-Fi credentials. The \ref RMAKER_EVENT_WIFI_RESET
|
||||
* event is triggered when this API is called. The actual reset will happen after a
|
||||
* delay if reset_seconds is not zero.
|
||||
*
|
||||
* @note This reset and reboot operations will happen asynchronously depending
|
||||
* on the values passed to the API.
|
||||
*
|
||||
* @param[in] reset_seconds Time in seconds after which the reset should get triggered.
|
||||
* This will help other modules take some actions before the device actually resets.
|
||||
* If set to zero, the operation would be performed immediately.
|
||||
* @param[in] reboot_seconds Time in seconds after which the device should reboot. If set
|
||||
* to negative value, the device will not reboot at all.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_wifi_reset(int8_t reset_seconds, int8_t reboot_seconds);
|
||||
|
||||
/** Reset to factory defaults and reboot
|
||||
*
|
||||
* This will clear entire NVS partition and (optionally) trigger a reboot.
|
||||
* The \ref RMAKER_EVENT_FACTORY_RESET event is triggered when this API is called.
|
||||
* The actual reset will happen after a delay if reset_seconds is not zero.
|
||||
*
|
||||
* @note This reset and reboot operations will happen asynchronously depending
|
||||
* on the values passed to the API.
|
||||
*
|
||||
* @param[in] reset_seconds Time in seconds after which the reset should get triggered.
|
||||
* This will help other modules take some actions before the device actually resets.
|
||||
* If set to zero, the operation would be performed immediately.
|
||||
* @param[in] reboot_seconds Time in seconds after which the device should reboot. If set
|
||||
* to negative value, the device will not reboot at all.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_factory_reset(int8_t reset_seconds, int8_t reboot_seconds);
|
||||
|
||||
/** Initialize time synchronization
|
||||
*
|
||||
* This API initializes SNTP for time synchronization.
|
||||
*
|
||||
* @param[in] config Configuration to be used for SNTP time synchronization. The default configuration is used if NULL is passed.
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_time_sync_init(esp_rmaker_time_config_t *config);
|
||||
|
||||
/** Check if current time is updated
|
||||
*
|
||||
* This API checks if the current system time is updated against the reference time of 1-Jan-2019.
|
||||
*
|
||||
* @return true if time is updated
|
||||
* @return false if time is not updated
|
||||
*/
|
||||
bool esp_rmaker_time_check(void);
|
||||
|
||||
/** Wait for time synchronization
|
||||
*
|
||||
* This API waits for the system time to be updated against the reference time of 1-Jan-2019.
|
||||
* This is a blocking call.
|
||||
*
|
||||
* @param[in] ticks_to_wait Number of ticks to wait for time synchronization. Accepted values: 0 to portMAX_DELAY.
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_time_wait_for_sync(uint32_t ticks_to_wait);
|
||||
|
||||
/** Set POSIX timezone
|
||||
*
|
||||
* Set the timezone (TZ environment variable) as per the POSIX format
|
||||
* specified in the [GNU libc documentation](https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html).
|
||||
* Eg. For China: "CST-8"
|
||||
* For US Pacific Time (including daylight saving information): "PST8PDT,M3.2.0,M11.1.0"
|
||||
*
|
||||
* @param[in] tz_posix NULL terminated TZ POSIX string
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_time_set_timezone_posix(const char *tz_posix);
|
||||
|
||||
/** Set timezone location string
|
||||
*
|
||||
* Set the timezone as a user friendly location string.
|
||||
* Check [here](https://rainmaker.espressif.com/docs/time-service.html) for a list of valid values.
|
||||
*
|
||||
* Eg. For China: "Asia/Shanghai"
|
||||
* For US Pacific Time: "America/Los_Angeles"
|
||||
*
|
||||
* @note Setting timezone using this API internally also sets the POSIX timezone string.
|
||||
*
|
||||
* @param[in] tz NULL terminated Timezone location string
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_time_set_timezone(const char *tz);
|
||||
|
||||
/** Get the current POSIX timezone
|
||||
*
|
||||
* This fetches the current timezone in POSIX format, read from NVS.
|
||||
*
|
||||
* @return Pointer to a NULL terminated POSIX timezone string on success.
|
||||
* Freeing this is the responsibility of the caller.
|
||||
* @return NULL on failure.
|
||||
*/
|
||||
char *esp_rmaker_time_get_timezone_posix(void);
|
||||
|
||||
/** Get the current timezone
|
||||
*
|
||||
* This fetches the current timezone in POSIX format, read from NVS.
|
||||
*
|
||||
* @return Pointer to a NULL terminated timezone string on success.
|
||||
* Freeing this is the responsibility of the caller.
|
||||
* @return NULL on failure.
|
||||
*/
|
||||
char *esp_rmaker_time_get_timezone(void);
|
||||
|
||||
/** Get printable local time string
|
||||
*
|
||||
* Get a printable local time string, with information of timezone and Daylight Saving.
|
||||
* Eg. "Tue Sep 1 09:04:38 2020 -0400[EDT], DST: Yes"
|
||||
* "Tue Sep 1 21:04:04 2020 +0800[CST], DST: No"
|
||||
*
|
||||
*
|
||||
* @param[out] buf Pointer to a pre-allocated buffer into which the time string will
|
||||
* be populated.
|
||||
* @param[in] buf_len Length of the above buffer.
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_get_local_time_str(char *buf, size_t buf_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
82
components/rmaker_common/include/esp_rmaker_work_queue.h
Normal file
82
components/rmaker_common/include/esp_rmaker_work_queue.h
Normal file
@@ -0,0 +1,82 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_event.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
/** Prototype for ESP RainMaker Work Queue Function
|
||||
*
|
||||
* @param[in] priv_data The private data associated with the work function.
|
||||
*/
|
||||
typedef void (*esp_rmaker_work_fn_t)(void *priv_data);
|
||||
|
||||
/** Initializes the Work Queue
|
||||
*
|
||||
* This initializes the work queue, which is basically a mechanism to run
|
||||
* tasks in the context of a dedicated thread. You can start queueing tasks
|
||||
* after this, but they will get executed only after calling
|
||||
* esp_rmaker_work_queue_start().
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_work_queue_init(void);
|
||||
|
||||
/** De-initialize the Work Queue
|
||||
*
|
||||
* This de-initializes the work queue. Note that the work queue needs to
|
||||
* be stopped using esp_rmaker_work_queue_stop() before calling this.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_work_queue_deinit(void);
|
||||
|
||||
/** Start the Work Queue
|
||||
*
|
||||
* This starts the Work Queue thread which then starts executing the tasks queued.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_work_queue_start(void);
|
||||
|
||||
/** Stop the Work Queue
|
||||
*
|
||||
* This stops a running Work Queue.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_work_queue_stop(void);
|
||||
|
||||
/** Queue execution of a function in the Work Queue's context
|
||||
*
|
||||
* This API queues a work function for execution in the Work Queue Task's context.
|
||||
*
|
||||
* @param[in] work_fn The Work function to be queued.
|
||||
* @param[in] priv_data Private data to be passed to the work function.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_work_queue_add_task(esp_rmaker_work_fn_t work_fn, void *priv_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
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