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:
94
components/esp_rainmaker/CMakeLists.txt
Normal file
94
components/esp_rainmaker/CMakeLists.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
# CORE
|
||||
set(core_srcs "src/core/esp_rmaker_core.c"
|
||||
"src/core/esp_rmaker_node.c"
|
||||
"src/core/esp_rmaker_device.c"
|
||||
"src/core/esp_rmaker_param.c"
|
||||
"src/core/esp_rmaker_node_config.c"
|
||||
"src/core/esp_rmaker_client_data.c"
|
||||
"src/core/esp_rmaker_time_service.c"
|
||||
"src/core/esp_rmaker_system_service.c"
|
||||
"src/core/esp_rmaker_user_mapping.pb-c.c"
|
||||
"src/core/esp_rmaker_user_mapping.c"
|
||||
"src/core/esp_rmaker_node_auth.c"
|
||||
"src/core/esp_rmaker_schedule.c"
|
||||
"src/core/esp_rmaker_scenes.c"
|
||||
"src/core/esp_rmaker_cmd_resp_manager.c"
|
||||
"src/core/esp_rmaker_secure_boot_digest.c"
|
||||
)
|
||||
|
||||
set(priv_req protobuf-c json_parser json_generator
|
||||
nvs_flash esp_http_client app_update esp-tls mbedtls esp_https_ota
|
||||
console esp_local_ctrl esp_https_server mdns esp_schedule efuse driver rmaker_common wifi_provisioning)
|
||||
|
||||
if ("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
list(APPEND priv_req esp_app_format)
|
||||
endif()
|
||||
|
||||
if ("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.1")
|
||||
# NAT64 and DNS64 features were introduced for openthread component in IDF v5.1
|
||||
# Network Provisioning component is supported for IDF v5.1+
|
||||
list(APPEND priv_req openthread network_provisioning)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESP_RMAKER_ASSISTED_CLAIM)
|
||||
list(APPEND core_srcs
|
||||
"src/core/esp_rmaker_claim.c"
|
||||
"src/core/esp_rmaker_claim.pb-c.c")
|
||||
endif()
|
||||
if(CONFIG_ESP_RMAKER_SELF_CLAIM)
|
||||
list(APPEND core_srcs
|
||||
"src/core/esp_rmaker_claim.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE)
|
||||
list(APPEND core_srcs
|
||||
"src/core/esp_rmaker_local_ctrl.c")
|
||||
endif()
|
||||
|
||||
set(core_priv_includes "src/core")
|
||||
|
||||
# MQTT
|
||||
set(mqtt_srcs "src/mqtt/esp_rmaker_mqtt.c"
|
||||
"src/mqtt/esp_rmaker_mqtt_budget.c")
|
||||
set(mqtt_priv_includes "src/mqtt")
|
||||
|
||||
# OTA
|
||||
set(ota_srcs "src/ota/esp_rmaker_ota.c"
|
||||
"src/ota/esp_rmaker_ota_using_params.c"
|
||||
"src/ota/esp_rmaker_ota_using_topics.c")
|
||||
set(ota_priv_includes "src/ota")
|
||||
|
||||
# Thread BR
|
||||
set(thread_br_srcs )
|
||||
set(thread_br_priv_includes )
|
||||
if (CONFIG_OPENTHREAD_BORDER_ROUTER)
|
||||
list(APPEND thread_br_srcs "src/thread_br/esp_rmaker_thread_br.c"
|
||||
"src/thread_br/esp_rmaker_thread_br_service.c"
|
||||
"src/thread_br/esp_rmaker_thread_br_internal.c"
|
||||
"src/thread_br/esp_rmaker_thread_br_launcher.c")
|
||||
list(APPEND thread_br_priv_includes "src/thread_br")
|
||||
endif()
|
||||
|
||||
# CONSOLE
|
||||
set(console_srcs "src/console/esp_rmaker_console.c"
|
||||
"src/console/esp_rmaker_commands.c")
|
||||
set(console_priv_includes "src/console")
|
||||
|
||||
# STANDARD TYPES
|
||||
set(standard_types_srcs "src/standard_types/esp_rmaker_standard_params.c"
|
||||
"src/standard_types/esp_rmaker_standard_devices.c"
|
||||
"src/standard_types/esp_rmaker_standard_services.c")
|
||||
|
||||
idf_component_register(SRCS ${core_srcs} ${mqtt_srcs} ${ota_srcs} ${standard_types_srcs} ${console_srcs} ${thread_br_srcs}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS ${core_priv_includes} ${ota_priv_includes} ${console_priv_includes} ${mqtt_priv_includes} ${thread_br_priv_includes}
|
||||
REQUIRES rmaker_common
|
||||
PRIV_REQUIRES ${priv_req})
|
||||
|
||||
target_add_binary_data(${COMPONENT_TARGET} "server_certs/rmaker_mqtt_server.crt" TEXT)
|
||||
target_add_binary_data(${COMPONENT_TARGET} "server_certs/rmaker_claim_service_server.crt" TEXT)
|
||||
target_add_binary_data(${COMPONENT_TARGET} "server_certs/rmaker_ota_server.crt" TEXT)
|
||||
|
||||
# Added just to automatically trigger re-runs of CMake
|
||||
git_describe(RMAKER_VERSION ${COMPONENT_DIR})
|
||||
message("ESP RainMaker Project commit: " ${RMAKER_VERSION})
|
||||
396
components/esp_rainmaker/Kconfig.projbuild
Normal file
396
components/esp_rainmaker/Kconfig.projbuild
Normal file
@@ -0,0 +1,396 @@
|
||||
menu "ESP RainMaker Config"
|
||||
|
||||
choice ESP_RMAKER_CLAIM_TYPE
|
||||
bool "Claiming Type"
|
||||
default ESP_RMAKER_SELF_CLAIM
|
||||
default ESP_RMAKER_ASSISTED_CLAIM if IDF_TARGET_ESP32
|
||||
help
|
||||
Claiming type to be used.
|
||||
|
||||
config ESP_RMAKER_NO_CLAIM
|
||||
bool "Do not use Claiming"
|
||||
help
|
||||
Do not use any claiming. The MQTT credentials need to
|
||||
be pre-programmed for this to work. This should be used
|
||||
for all private RainMaker deployments.
|
||||
|
||||
config ESP_RMAKER_SELF_CLAIM
|
||||
bool "Use Self Claiming"
|
||||
depends on !IDF_TARGET_ESP32 && !IDF_TARGET_ESP32C2
|
||||
help
|
||||
Use Self Claiming i.e. get the MQTT credentials
|
||||
directly from the claiming service.
|
||||
|
||||
config ESP_RMAKER_ASSISTED_CLAIM
|
||||
bool "Use Assisted Claiming"
|
||||
depends on BT_ENABLED && !IDF_TARGET_ESP32S2
|
||||
help
|
||||
Use Assisted Claiming i.e. get the MQTT credentials
|
||||
from the claiming service via assistance from clients,
|
||||
like the phone apps.
|
||||
|
||||
endchoice
|
||||
|
||||
choice ESP_RMAKER_CHOOSE_PKI_ACCESS_METHOD
|
||||
prompt "Choose PKI credentials access method"
|
||||
default ESP_RMAKER_USE_NVS
|
||||
help
|
||||
ESP devices support multiple ways to secure store the PKI credentials.
|
||||
Currently, NVS and ESP Secure Cert Manager are supported.
|
||||
The default behaviour is to access the PKI credentials from the NVS.
|
||||
Consult the ESP-TLS documentation in ESP-IDF Programming guide for more details.
|
||||
|
||||
config ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||
bool "Use ESP Secure Certificate Manager"
|
||||
depends on ESP_RMAKER_NO_CLAIM
|
||||
help
|
||||
Enable the use of ESP Secure Certificate Manager APIs for the example.
|
||||
Please refer to ESP Secure Certificate Manager documentation for more details.
|
||||
|
||||
config ESP_RMAKER_USE_NVS
|
||||
bool "Use NVS (default)"
|
||||
help
|
||||
This option expects the Private key and Device certificate to be in the NVS.
|
||||
This is the default behaviour.
|
||||
endchoice
|
||||
|
||||
|
||||
config ESP_RMAKER_CLAIM_TYPE
|
||||
int
|
||||
default 0 if ESP_RMAKER_NO_CLAIM
|
||||
default 1 if ESP_RMAKER_SELF_CLAIM
|
||||
default 2 if ESP_RMAKER_ASSISTED_CLAIM
|
||||
|
||||
config ESP_RMAKER_CLAIM_SERVICE_BASE_URL
|
||||
string "ESP RainMaker Claiming Service Base URL"
|
||||
default "https://esp-claiming.rainmaker.espressif.com"
|
||||
depends on ESP_RMAKER_SELF_CLAIM
|
||||
help
|
||||
ESP RainMaker Claiming Service Base URL.
|
||||
|
||||
config ESP_RMAKER_READ_MQTT_HOST_FROM_CONFIG
|
||||
bool "Read MQTT Host from ESP_RMAKER_MQTT_HOST (Read Docs)"
|
||||
default n
|
||||
help
|
||||
Normally, if self claiming or assisted claiming is used, the MQTT Host is anyways read from
|
||||
ESP_RMAKER_MQTT_HOST, independent of this config option. However, if this is set, even if
|
||||
an MQTT host value is found in NVS, it will be overriden with ESP_RMAKER_MQTT_HOST.
|
||||
|
||||
config ESP_RMAKER_READ_NODE_ID_FROM_CERT_CN
|
||||
bool "Read Node ID from Device Certificate"
|
||||
default n
|
||||
help
|
||||
If enabled, the device will get its node id from the device certificate's CN field. If not enabled,
|
||||
it will read the node id either from nvs factory partition or mac address, depending on the configuration.
|
||||
|
||||
config ESP_RMAKER_MQTT_HOST
|
||||
string "ESP RainMaker MQTT Host"
|
||||
depends on ESP_RMAKER_SELF_CLAIM || ESP_RMAKER_ASSISTED_CLAIM || ESP_RMAKER_READ_MQTT_HOST_FROM_CONFIG
|
||||
default "a1p72mufdu6064-ats.iot.us-east-1.amazonaws.com"
|
||||
help
|
||||
ESP RainMaker MQTT Host name.
|
||||
|
||||
config ESP_RMAKER_MQTT_USE_BASIC_INGEST_TOPICS
|
||||
bool "Use Basic Ingest Topics"
|
||||
default y
|
||||
help
|
||||
This config enables the use of AWS Basic Ingest Topics for Node to Cloud communication,
|
||||
which eliminates the MQTT Broker and thus reduces messaging cost.
|
||||
|
||||
config ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||
bool "Enable MQTT budgeting"
|
||||
default y
|
||||
help
|
||||
Enable MQTT budgeting, which will control the number of MQTT messages sent by the node.
|
||||
|
||||
config ESP_RMAKER_MQTT_DEFAULT_BUDGET
|
||||
int "Default MQTT Budget"
|
||||
depends on ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||
default 100
|
||||
range 64 ESP_RMAKER_MQTT_MAX_BUDGET
|
||||
help
|
||||
Default MQTT budget. Budget will reduce on sending an MQTT message and increase based on
|
||||
ESP_RMAKER_MQTT_BUDGET_REVIVE_PERIOD. If no budget is available, MQTT message will be dropped.
|
||||
|
||||
config ESP_RMAKER_MQTT_MAX_BUDGET
|
||||
int "Max MQTT Budget"
|
||||
depends on ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||
default 1024
|
||||
range 64 2048
|
||||
help
|
||||
Maximum budget that the node can have. No additional budget will be allocated if this count is reached.
|
||||
|
||||
config ESP_RMAKER_MQTT_BUDGET_REVIVE_PERIOD
|
||||
int "MQTT Budget revive period"
|
||||
depends on ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||
default 5
|
||||
range 5 600
|
||||
help
|
||||
Period in seconds after which the MQTT budget should revive (by ESP_RMAKER_MQTT_BUDGET_REVIVE_COUNT).
|
||||
This is used to limit the messages being sent by the node.
|
||||
|
||||
config ESP_RMAKER_MQTT_BUDGET_REVIVE_COUNT
|
||||
int "MQTT Budget revive count"
|
||||
depends on ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||
default 1
|
||||
range 1 16
|
||||
help
|
||||
The count by which the budget will be increased periodically based on ESP_RMAKER_MQTT_BUDGET_REVIVE_PERIOD.
|
||||
|
||||
config ESP_RMAKER_MAX_PARAM_DATA_SIZE
|
||||
int "Maximum Parameters' data size"
|
||||
default 1024
|
||||
range 64 8192
|
||||
help
|
||||
Maximum size of the payload for reporting parameter values.
|
||||
|
||||
config ESP_RMAKER_DISABLE_USER_MAPPING_PROV
|
||||
bool "Disable User Mapping during Provisioning"
|
||||
default n
|
||||
help
|
||||
The handlers for User Node Mapping are now registered internally by ESP RainMaker core,
|
||||
by registering to appropriate Wi-Fi Provisioning events. If your application code also
|
||||
has the calls to create and register the user mapping handlers, enable this config
|
||||
option to prevent duplication.
|
||||
|
||||
config ESP_RMAKER_USER_ID_CHECK
|
||||
bool "User id check for User Node mapping"
|
||||
default n
|
||||
help
|
||||
This enables the additional user id checks during user node mapping. Whenever a new user
|
||||
id is received, it is checked against the existing user id in NVS. If there is a mismatch,
|
||||
or if no user id exists in NVS, this is considered as a reset state and the same is reported
|
||||
to the ESP RainMaker Cloud during the User Node association MQTT Publish so that the cloud
|
||||
can take appropriate action w.r.t user permissions. It is recommended to enable this option
|
||||
for security reasons.
|
||||
|
||||
config RMAKER_NAME_PARAM_CB
|
||||
bool "Call device callback for Name param"
|
||||
default n
|
||||
help
|
||||
By default, the "Name" parameter (esp.param.name) changes are handled internally. If Applications
|
||||
want to handle this themselves, this config option can be enabled. Please ensure that you update
|
||||
and report the name parameter in your callback so that it reflects correctly everywhere.
|
||||
If no device callback is registered, the name paramater will be handled internally.
|
||||
|
||||
config ESP_RMAKER_LOCAL_CTRL_FEATURE_ENABLE
|
||||
bool "ESP RainMaker Local Control Feature"
|
||||
default n
|
||||
select ESP_HTTPS_SERVER_ENABLE
|
||||
help
|
||||
Enabling this allows to discover and control the node over local Wi-Fi network.
|
||||
Note that this uses only Wi-Fi level security and so, any client on the same
|
||||
Wi-Fi network can potentially control the node. The communication is not encrypted
|
||||
and uses plain HTTP. Please Check the RainMaker documentation for additional details.
|
||||
Note that enabling this just means that the APIs to enable/disable local
|
||||
control will be compiled in and can be used in application code. If CONFIG_ESP_RMAKER_LOCAL_CTRL_AUTO_ENABLE
|
||||
is also enabled, then no additional APIs are required for actually enabling local control.
|
||||
|
||||
config ESP_RMAKER_LOCAL_CTRL_AUTO_ENABLE
|
||||
bool "Auto ESP RainMaker Local Control"
|
||||
default n
|
||||
select ESP_RMAKER_LOCAL_CTRL_FEATURE_ENABLE
|
||||
help
|
||||
Automatically enabled local control when RainMaker starts.
|
||||
|
||||
config ESP_RMAKER_LOCAL_CTRL_HTTP_PORT
|
||||
int "Local Control HTTP Port"
|
||||
depends on ESP_RMAKER_LOCAL_CTRL_FEATURE_ENABLE
|
||||
default 8080
|
||||
help
|
||||
The port number to be used for http for local control.
|
||||
|
||||
config ESP_RMAKER_LOCAL_CTRL_STACK_SIZE
|
||||
int "Local Control HTTP Server task stack size"
|
||||
depends on ESP_RMAKER_LOCAL_CTRL_FEATURE_ENABLE
|
||||
default 6144
|
||||
help
|
||||
The task stack size to be used for http server for local control.
|
||||
|
||||
choice ESP_RMAKER_LOCAL_CTRL_SECURITY
|
||||
prompt "Local Control Security Type"
|
||||
depends on ESP_RMAKER_LOCAL_CTRL_FEATURE_ENABLE
|
||||
default ESP_RMAKER_LOCAL_CTRL_SECURITY_1
|
||||
help
|
||||
Security type to be selected for local control.
|
||||
|
||||
config ESP_RMAKER_LOCAL_CTRL_SECURITY_0
|
||||
bool "sec0"
|
||||
config ESP_RMAKER_LOCAL_CTRL_SECURITY_1
|
||||
bool "sec1"
|
||||
endchoice
|
||||
|
||||
config ESP_RMAKER_LOCAL_CTRL_SECURITY
|
||||
int
|
||||
default 0 if ESP_RMAKER_LOCAL_CTRL_SECURITY_0
|
||||
default 1 if ESP_RMAKER_LOCAL_CTRL_SECURITY_1
|
||||
|
||||
choice ESP_RMAKER_CONSOLE_UART_NUM
|
||||
prompt "UART for console input"
|
||||
default ESP_RMAKER_CONSOLE_UART_NUM_0
|
||||
help
|
||||
UART to be selected for serial console.
|
||||
|
||||
config ESP_RMAKER_CONSOLE_UART_NUM_0
|
||||
bool "UART0"
|
||||
config ESP_RMAKER_CONSOLE_UART_NUM_1
|
||||
bool "UART1"
|
||||
endchoice
|
||||
|
||||
config ESP_RMAKER_CONSOLE_UART_NUM
|
||||
int
|
||||
default 0 if ESP_RMAKER_CONSOLE_UART_NUM_0
|
||||
default 1 if ESP_RMAKER_CONSOLE_UART_NUM_1
|
||||
|
||||
config ESP_RMAKER_USE_CERT_BUNDLE
|
||||
bool "Use Certificate Bundle"
|
||||
default y
|
||||
select ESP_RMAKER_MQTT_USE_CERT_BUNDLE
|
||||
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.
|
||||
|
||||
menu "ESP RainMaker OTA Config"
|
||||
|
||||
config ESP_RMAKER_OTA_AUTOFETCH
|
||||
bool "Auto Fetch OTA"
|
||||
default y
|
||||
help
|
||||
Applicable only for OTA using Topics.
|
||||
Fetch the OTA (i.e. get the URL and other details) by actively sending an
|
||||
OTA fetch request to ESP RainMaker Cloud. If this is disabled, the node
|
||||
will stay subscribed to the OTA Topics, but will get the information only
|
||||
if someone explicitly triggers it.
|
||||
|
||||
config ESP_RMAKER_OTA_AUTOFETCH_PERIOD
|
||||
int "OTA Auto Fetch Period"
|
||||
default 0
|
||||
range 0 168
|
||||
depends on ESP_RMAKER_OTA_AUTOFETCH
|
||||
help
|
||||
Periodically send an OTA fetch request. If set to 0, the request will be sent only once,
|
||||
when the node connects to the ESP RainMaker Cloud first time after a boot.
|
||||
Else, this defines the period (in hours) for the periodic fetch request.
|
||||
|
||||
config ESP_RMAKER_SKIP_COMMON_NAME_CHECK
|
||||
bool "Skip server certificate CN field check"
|
||||
default n
|
||||
help
|
||||
This allows you to skip the validation of OTA server certificate CN field.
|
||||
|
||||
config ESP_RMAKER_SKIP_VERSION_CHECK
|
||||
bool "Skip firmware version check"
|
||||
default n
|
||||
help
|
||||
This allows you to skip the firmware version check. Useful during development,
|
||||
but not for production.
|
||||
|
||||
config ESP_RMAKER_SKIP_SECURE_VERSION_CHECK
|
||||
bool "Skip secure version check"
|
||||
default n
|
||||
help
|
||||
This allows you to skip the secure version check. Useful during development,
|
||||
but not for production. Check out ESP IDF's Anti-rollback feature for more details.
|
||||
|
||||
config ESP_RMAKER_SKIP_PROJECT_NAME_CHECK
|
||||
bool "Skip project name check"
|
||||
default n
|
||||
help
|
||||
This allows you to skip the project name check.
|
||||
|
||||
config ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE
|
||||
int "OTA HTTP receive buffer size"
|
||||
default 1024
|
||||
range 512 LWIP_TCP_WND_DEFAULT
|
||||
help
|
||||
Increasing this value beyond the default would speed up the OTA download process.
|
||||
However, please ensure that your application has enough memory headroom to allow this,
|
||||
else, the OTA may fail.
|
||||
|
||||
config ESP_RMAKER_OTA_ROLLBACK_WAIT_PERIOD
|
||||
int "OTA Rollback Wait Period (Seconds)"
|
||||
default 90
|
||||
range 30 600
|
||||
help
|
||||
After an OTA Update, if CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is set, then the firmware will wait for MQTT
|
||||
connection to mark the new firmware as valid. However, if it is not able to do so within
|
||||
this wait period (in seconds), the firmware will be marked as invalid and the older
|
||||
firmware will be booted into.
|
||||
|
||||
config ESP_RMAKER_OTA_DISABLE_AUTO_REBOOT
|
||||
bool "Disable auto reboot"
|
||||
default n
|
||||
help
|
||||
After OTA image is flashed and active partition changed, the device automatically reboots. To disable this
|
||||
behaviour and handle reboot on your own, based on RMAKER_OTA event, enable this option.
|
||||
|
||||
config ESP_RMAKER_OTA_TIME_SUPPORT
|
||||
bool "Enable OTA Time Support"
|
||||
default y
|
||||
help
|
||||
OTA Jobs can include additional metadata for time to indicate a range of valid date and the time within
|
||||
those dates. Eg. Perform OTA between 1 Dec 2022 and 10 Dec 2022 that too only between 2:00am and 5:00am.
|
||||
If you want to ignore this, disable this option.
|
||||
endmenu
|
||||
|
||||
menu "ESP RainMaker Scheduling"
|
||||
|
||||
config ESP_RMAKER_SCHEDULING_MAX_SCHEDULES
|
||||
int "Maximum schedules"
|
||||
default 10
|
||||
range 1 50
|
||||
help
|
||||
Maximum Number of schedules allowed. The json size for report params increases as the number of schedules increases.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "ESP RainMaker Scenes"
|
||||
|
||||
config ESP_RMAKER_SCENES_MAX_SCENES
|
||||
int "Maximum scenes"
|
||||
default 10
|
||||
range 1 50
|
||||
help
|
||||
Maximum Number of scenes allowed. The json size for report params increases as the number of scenes increases.
|
||||
|
||||
config ESP_RMAKER_SCENES_DEACTIVATE_SUPPORT
|
||||
bool "Enable Deactivate support"
|
||||
default n
|
||||
help
|
||||
This enables the deactivate callback support. The application callback will be invoked with the source
|
||||
set to ESP_RMAKER_REQ_SRC_SCENE_DEACTIVATE when the deactivate operation is received. However, the
|
||||
param values would be the same as those for activate, since the RainMaker core does not know what the
|
||||
expected values are for scene deactivation.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "ESP RainMaker Command-Response"
|
||||
|
||||
config ESP_RMAKER_CMD_RESP_ENABLE
|
||||
bool "Enable Command-Response Module"
|
||||
default y
|
||||
help
|
||||
Enable the ESP RainMaker Command-Response module for semi-synchronous communication. Please refer the RainMaker documents
|
||||
for additional information.
|
||||
|
||||
config ESP_RMAKER_CMD_RESP_TEST_ENABLE
|
||||
bool "Enable Command-Response Testing"
|
||||
default n
|
||||
depends on ESP_RMAKER_CMD_RESP_ENABLE
|
||||
help
|
||||
Enable testing for Command-Response module. This enables triggering commands and parsing response from the node itself,
|
||||
rather than receiving the commands from cloud. C API or the serial console can be used to trigger the commands.
|
||||
This should be enabled only while testing commands, but should always be disabled in production firmware.
|
||||
|
||||
endmenu
|
||||
|
||||
config ESP_RMAKER_USING_NETWORK_PROV
|
||||
bool "Using Network Provisioning"
|
||||
default y
|
||||
help
|
||||
RainMaker will use network_provisioning component to provision a device to a Wi-Fi/Thread network if enabling this option.
|
||||
If the option is not enabled, it will use wifi_provisioning instead. This option only works when IDF verson is later than
|
||||
v5.1.
|
||||
|
||||
endmenu
|
||||
201
components/esp_rainmaker/LICENSE
Normal file
201
components/esp_rainmaker/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.
|
||||
5
components/esp_rainmaker/README.md
Normal file
5
components/esp_rainmaker/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# ESP RainMaker Agent Component
|
||||
|
||||
[](https://components.espressif.com/components/espressif/esp_rainmaker)
|
||||
|
||||
This is the main firmware agent for ESP RainMaker, which will then pull in other required components. Please check the [ESP RainMaker documentation](https://rainmaker.espressif.com/) for details.
|
||||
12
components/esp_rainmaker/component.mk
Normal file
12
components/esp_rainmaker/component.mk
Normal file
@@ -0,0 +1,12 @@
|
||||
COMPONENT_SRCDIRS := src/core src/mqtt src/ota src/standard_types src/console
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
COMPONENT_PRIV_INCLUDEDIRS := src/core src/ota src/console
|
||||
|
||||
ifndef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||
COMPONENT_OBJEXCLUDE += src/core/esp_rmaker_claim.pb-c.o
|
||||
ifndef CONFIG_ESP_RMAKER_SELF_CLAIM
|
||||
COMPONENT_OBJEXCLUDE += src/core/esp_rmaker_claim.o
|
||||
endif
|
||||
endif
|
||||
|
||||
COMPONENT_EMBED_TXTFILES := server_certs/rmaker_mqtt_server.crt server_certs/rmaker_claim_service_server.crt server_certs/rmaker_ota_server.crt
|
||||
33
components/esp_rainmaker/idf_component.yml
Normal file
33
components/esp_rainmaker/idf_component.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
## IDF Component Manager Manifest File
|
||||
version: "1.5.4"
|
||||
description: ESP RainMaker firmware agent
|
||||
url: https://github.com/espressif/esp-rainmaker/tree/master/components/esp_rainmaker
|
||||
repository: https://github.com/espressif/esp-rainmaker.git
|
||||
issues: https://github.com/espressif/esp-rainmaker/issues
|
||||
documentation: https://rainmaker.espressif.com/
|
||||
discussion: https://www.esp32.com/viewforum.php?f=41
|
||||
dependencies:
|
||||
espressif/mdns:
|
||||
version: "^1.2.0"
|
||||
rules:
|
||||
- if: "idf_version >=5.0"
|
||||
espressif/esp_secure_cert_mgr:
|
||||
version: "^2.2.1"
|
||||
rules:
|
||||
- if: "idf_version >=4.3"
|
||||
espressif/rmaker_common:
|
||||
version: "~1.4.6"
|
||||
espressif/json_parser:
|
||||
version: "~1.0.3"
|
||||
espressif/json_generator:
|
||||
version: "~1.1.1"
|
||||
espressif/esp_schedule:
|
||||
version: "~1.2.0"
|
||||
espressif/network_provisioning:
|
||||
version: "~1.0.0"
|
||||
rules:
|
||||
- if: "idf_version >= 5.1"
|
||||
espressif/esp_rcp_update:
|
||||
version: "~1.2.0"
|
||||
rules:
|
||||
- if: "idf_version >= 5.1"
|
||||
52
components/esp_rainmaker/include/esp_rmaker_console.h
Normal file
52
components/esp_rainmaker/include/esp_rmaker_console.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** 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_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
|
||||
1068
components/esp_rainmaker/include/esp_rmaker_core.h
Normal file
1068
components/esp_rainmaker/include/esp_rmaker_core.h
Normal file
File diff suppressed because it is too large
Load Diff
125
components/esp_rainmaker/include/esp_rmaker_mqtt.h
Normal file
125
components/esp_rainmaker/include/esp_rmaker_mqtt.h
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_rmaker_mqtt_glue.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
esp_rmaker_mqtt_conn_params_t *esp_rmaker_mqtt_get_conn_params(void);
|
||||
|
||||
/** Initialize ESP RainMaker MQTT
|
||||
*
|
||||
* @param[in] conn_params The MQTT configuration data
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
esp_err_t esp_rmaker_mqtt_init(esp_rmaker_mqtt_conn_params_t *conn_params);
|
||||
|
||||
/* Deinitialize ESP RainMaker MQTT
|
||||
*
|
||||
* Call this function after MQTT has disconnected.
|
||||
*/
|
||||
void esp_rmaker_mqtt_deinit(void);
|
||||
|
||||
/** MQTT Connect
|
||||
*
|
||||
* Starts the connection attempts to the MQTT broker as per the configuration
|
||||
* provided during initializing.
|
||||
* This should ideally be called after successful network connection.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
esp_err_t esp_rmaker_mqtt_connect(void);
|
||||
|
||||
/** MQTT Disconnect
|
||||
*
|
||||
* Disconnects from the MQTT broker.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
esp_err_t esp_rmaker_mqtt_disconnect(void);
|
||||
|
||||
/** Publish MQTT Message
|
||||
*
|
||||
* @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 Publish. Can be 0, 1 or 2. Also depends on what the MQTT broker supports.
|
||||
* @param[out] msg_id msg_id for tracking if message is queued
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
esp_err_t esp_rmaker_mqtt_publish(const char *topic, void *data, size_t data_len, uint8_t qos, int *msg_id);
|
||||
|
||||
/** Subscribe to MQTT topic
|
||||
*
|
||||
* @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] priv_data Optional private data to be passed to the callback
|
||||
* @param[in] qos Quality of Service for the Subscription. Can be 0, 1 or 2. Also depends on what the MQTT broker supports.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
esp_err_t esp_rmaker_mqtt_subscribe(const char *topic, esp_rmaker_mqtt_subscribe_cb_t cb, uint8_t qos, void *priv_data);
|
||||
|
||||
/** Unsubscribe from MQTT topic
|
||||
*
|
||||
* @param[in] topic Topic from which to unsubscribe.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of any error.
|
||||
*/
|
||||
esp_err_t esp_rmaker_mqtt_unsubscribe(const char *topic);
|
||||
esp_err_t esp_rmaker_mqtt_setup(esp_rmaker_mqtt_config_t mqtt_config);
|
||||
|
||||
/** Creates appropriate MQTT Topic String based on CONFIG_ESP_RMAKER_MQTT_USE_BASIC_INGEST_TOPICS
|
||||
* @param[out] buf Buffer to hold topic string
|
||||
* @param[in] buf_size Size of buffer
|
||||
* @param[in] topic_suffix MQTT Topic suffix
|
||||
* @param[in] rule Basic Ingests Rule Name
|
||||
*/
|
||||
void esp_rmaker_create_mqtt_topic(char *buf, size_t buf_size, const char *topic_suffix, const char *rule);
|
||||
|
||||
/**
|
||||
* @brief Check if budget is available to publish an mqtt message
|
||||
*
|
||||
* @return true if budget is available
|
||||
* @return false if budget is exhausted
|
||||
*
|
||||
* @note `esp_rmaker_mqtt_publish` API already does this check. In addition to that,
|
||||
* some use-cases might still need to check for this.
|
||||
*/
|
||||
bool esp_rmaker_mqtt_is_budget_available(void);
|
||||
|
||||
/**
|
||||
* @brief Check if device is connected to MQTT Server
|
||||
*
|
||||
* @return true if device is connected
|
||||
* @return false if device is not connected
|
||||
*
|
||||
*/
|
||||
bool esp_rmaker_is_mqtt_connected();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
272
components/esp_rainmaker/include/esp_rmaker_ota.h
Normal file
272
components/esp_rainmaker/include/esp_rmaker_ota.h
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** @cond **/
|
||||
/** ESP RainMaker Event Base */
|
||||
ESP_EVENT_DECLARE_BASE(RMAKER_OTA_EVENT);
|
||||
/** @endcond **/
|
||||
|
||||
/** ESP RainMaker Events */
|
||||
typedef enum {
|
||||
/* Invalid event. Used for internal handling only */
|
||||
RMAKER_OTA_EVENT_INVALID = 0,
|
||||
/** RainMaker OTA is Starting */
|
||||
RMAKER_OTA_EVENT_STARTING,
|
||||
/** RainMaker OTA has Started */
|
||||
RMAKER_OTA_EVENT_IN_PROGRESS,
|
||||
/** RainMaker OTA Successful */
|
||||
RMAKER_OTA_EVENT_SUCCESSFUL,
|
||||
/** RainMaker OTA Failed */
|
||||
RMAKER_OTA_EVENT_FAILED,
|
||||
/** RainMaker OTA Rejected */
|
||||
RMAKER_OTA_EVENT_REJECTED,
|
||||
/** RainMaker OTA Delayed */
|
||||
RMAKER_OTA_EVENT_DELAYED,
|
||||
/** OTA Image has been flashed and active partition changed. Reboot is requested. Applicable only if Auto reboot is disabled **/
|
||||
RMAKER_OTA_EVENT_REQ_FOR_REBOOT,
|
||||
} esp_rmaker_ota_event_t;
|
||||
|
||||
/** Default ESP RainMaker OTA Server Certificate */
|
||||
extern const char *ESP_RMAKER_OTA_DEFAULT_SERVER_CERT;
|
||||
|
||||
/** OTA Status to be reported to ESP RainMaker Cloud */
|
||||
typedef enum {
|
||||
/** OTA is in Progress. This can be reported multiple times as the OTA progresses. */
|
||||
OTA_STATUS_IN_PROGRESS = 1,
|
||||
/** OTA Succeeded. This should be reported only once, at the end of OTA. */
|
||||
OTA_STATUS_SUCCESS,
|
||||
/** OTA Failed. This should be reported only once, at the end of OTA. */
|
||||
OTA_STATUS_FAILED,
|
||||
/** OTA was delayed by the application */
|
||||
OTA_STATUS_DELAYED,
|
||||
/** OTA rejected due to some reason (wrong project, version, etc.) */
|
||||
OTA_STATUS_REJECTED,
|
||||
} ota_status_t;
|
||||
|
||||
/** OTA Workflow type */
|
||||
typedef enum {
|
||||
/** OTA will be performed using services and parameters. */
|
||||
OTA_USING_PARAMS = 1,
|
||||
/** OTA will be performed using pre-defined MQTT topics. */
|
||||
OTA_USING_TOPICS
|
||||
} esp_rmaker_ota_type_t;
|
||||
|
||||
/** The OTA Handle to be used by the OTA callback */
|
||||
typedef void *esp_rmaker_ota_handle_t;
|
||||
|
||||
/** OTA Data */
|
||||
typedef struct {
|
||||
/** The OTA URL received from ESP RainMaker Cloud */
|
||||
char *url;
|
||||
/** Size of the OTA File. Can be 0 if the file size isn't received from
|
||||
* the ESP RainMaker Cloud */
|
||||
int filesize;
|
||||
/** The firmware version of the OTA image **/
|
||||
char *fw_version;
|
||||
/** The OTA Job ID received from cloud **/
|
||||
char *ota_job_id;
|
||||
/** The server certificate passed in esp_rmaker_enable_ota() */
|
||||
const char *server_cert;
|
||||
/** The private data passed in esp_rmaker_enable_ota() */
|
||||
char *priv;
|
||||
/** OTA Metadata. Applicable only for OTA using Topics. Will be received (if applicable) from the backend, along with the OTA URL */
|
||||
char *metadata;
|
||||
} esp_rmaker_ota_data_t;
|
||||
|
||||
/** Function prototype for OTA Callback
|
||||
*
|
||||
* This function will be invoked by the ESP RainMaker core whenever an OTA is available.
|
||||
* The esp_rmaker_report_ota_status() API should be used to indicate the progress and
|
||||
* success/fail status.
|
||||
*
|
||||
* @param[in] handle An OTA handle assigned by the ESP RainMaker Core
|
||||
* @param[in] ota_data The data to be used for the OTA
|
||||
*
|
||||
* @return ESP_OK if the OTA was successful
|
||||
* @return ESP_FAIL if the OTA failed.
|
||||
*/
|
||||
typedef esp_err_t (*esp_rmaker_ota_cb_t) (esp_rmaker_ota_handle_t handle,
|
||||
esp_rmaker_ota_data_t *ota_data);
|
||||
|
||||
typedef enum {
|
||||
/** OTA Diagnostics Failed. Rollback the firmware. */
|
||||
OTA_DIAG_STATUS_FAIL,
|
||||
/** OTA Diagnostics Pending. Additional validations will be done later. */
|
||||
OTA_DIAG_STATUS_PENDING,
|
||||
/** OTA Diagnostics Succeeded. Firmware can be considered valid. */
|
||||
OTA_DIAG_STATUS_SUCCESS
|
||||
} esp_rmaker_ota_diag_status_t;
|
||||
|
||||
typedef enum {
|
||||
/** OTA State: Initialised. */
|
||||
OTA_DIAG_STATE_INIT,
|
||||
/** OTA state: MQTT has connected. */
|
||||
OTA_DIAG_STATE_POST_MQTT
|
||||
} esp_rmaker_ota_diag_state_t;
|
||||
|
||||
typedef struct {
|
||||
/** OTA diagnostic state */
|
||||
esp_rmaker_ota_diag_state_t state;
|
||||
/** Flag to indicate whether the OTA which has triggered the Diagnostics checks for rollback
|
||||
* was triggered via RainMaker or not. This would be useful only when your application has some
|
||||
* other mechanism for OTA too.
|
||||
*/
|
||||
bool rmaker_ota;
|
||||
} esp_rmaker_ota_diag_priv_t;
|
||||
|
||||
/** Function Prototype for Post OTA Diagnostics
|
||||
*
|
||||
* If the Application rollback feature is enabled, this callback will be invoked
|
||||
* as soon as you call esp_rmaker_ota_enable(), if it is the first
|
||||
* boot after an OTA. You may perform some application specific diagnostics and
|
||||
* report the status which will decide whether to roll back or not.
|
||||
*
|
||||
* This will be invoked once again after MQTT has connected, in case some additional validations
|
||||
* are to be done later.
|
||||
*
|
||||
* If OTA state == OTA_DIAG_STATE_INIT, then
|
||||
* return OTA_DIAG_STATUS_FAIL to indicate failure and rollback.
|
||||
* return OTA_DIAG_STATUS_SUCCESS or OTA_DIAG_STATUS_PENDING to tell internal OTA logic to continue further.
|
||||
*
|
||||
* If OTA state == OTA_DIAG_STATE_POST_MQTT, then
|
||||
* return OTA_DIAG_STATUS_FAIL to indicate failure and rollback.
|
||||
* return OTA_DIAG_STATUS_SUCCESS to indicate validation was successful and mark OTA as valid
|
||||
* return OTA_DIAG_STATUS_PENDING to indicate that some additional validations will be done later
|
||||
* and the OTA will eventually be marked valid/invalid using esp_rmaker_ota_mark_valid() or
|
||||
* esp_rmaker_ota_mark_invalid() respectively.
|
||||
*
|
||||
* @return esp_rmaker_ota_diag_status_t as applicable
|
||||
*/
|
||||
typedef esp_rmaker_ota_diag_status_t (*esp_rmaker_post_ota_diag_t)(esp_rmaker_ota_diag_priv_t *ota_diag_priv, void *priv);
|
||||
|
||||
/** ESP RainMaker OTA Configuration */
|
||||
typedef struct {
|
||||
/** OTA Callback.
|
||||
* The callback to be invoked when an OTA Job is available.
|
||||
* If kept NULL, the internal default callback will be used (Recommended).
|
||||
*/
|
||||
esp_rmaker_ota_cb_t ota_cb;
|
||||
/** OTA Diagnostics Callback.
|
||||
* A post OTA diagnostic handler to be invoked if app rollback feature is enabled.
|
||||
* If kept NULL, the new firmware will be assumed to be fine,
|
||||
* and no rollback will be performed.
|
||||
*/
|
||||
esp_rmaker_post_ota_diag_t ota_diag;
|
||||
/** Server Certificate.
|
||||
* The certificate to be passed to the OTA callback for server authentication.
|
||||
* This is mandatory, unless you have disabled it in ESP HTTPS OTA config option.
|
||||
* If you are using the ESP RainMaker OTA Service, you can just set this to
|
||||
* `ESP_RMAKER_OTA_DEFAULT_SERVER_CERT`.
|
||||
*/
|
||||
const char *server_cert;
|
||||
/** Private Data.
|
||||
* Optional private data to be passed to the OTA callback.
|
||||
*/
|
||||
void *priv;
|
||||
} esp_rmaker_ota_config_t;
|
||||
|
||||
/** Enable OTA
|
||||
*
|
||||
* Calling this API enables OTA as per the ESP RainMaker specification.
|
||||
* Please check the various ESP RainMaker configuration options to
|
||||
* use the different variants of OTA. Refer the documentation for
|
||||
* additional details.
|
||||
*
|
||||
* @param[in] ota_config Pointer to an OTA configuration structure
|
||||
* @param[in] type The OTA workflow type
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_ota_enable(esp_rmaker_ota_config_t *ota_config, esp_rmaker_ota_type_t type);
|
||||
|
||||
/** Report OTA Status
|
||||
*
|
||||
* This API must be called from the OTA Callback to indicate the status of the OTA. The OTA_STATUS_IN_PROGRESS
|
||||
* can be reported multiple times with appropriate additional information. The final success/failure should
|
||||
* be reported only once, at the end.
|
||||
*
|
||||
* This can be ignored if you are using the default internal OTA callback.
|
||||
*
|
||||
* @param[in] ota_handle The OTA handle received by the callback
|
||||
* @param[in] status Status to be reported
|
||||
* @param[in] additional_info NULL terminated string indicating additional information for the status
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_ota_report_status(esp_rmaker_ota_handle_t ota_handle, ota_status_t status, char *additional_info);
|
||||
|
||||
/** Default OTA callback
|
||||
*
|
||||
* This is the default OTA callback which will get used if you do not pass your own callback. You can call this
|
||||
* even from your callback, in case you want better control on when the OTA can proceed and yet let the actual
|
||||
* OTA process be managed by the RainMaker Core.
|
||||
*
|
||||
* @param[in] handle An OTA handle assigned by the ESP RainMaker Core
|
||||
* @param[in] ota_data The data to be used for the OTA
|
||||
*
|
||||
* @return ESP_OK if the OTA was successful
|
||||
* @return ESP_FAIL if the OTA failed.
|
||||
* */
|
||||
esp_err_t esp_rmaker_ota_default_cb(esp_rmaker_ota_handle_t handle, esp_rmaker_ota_data_t *ota_data);
|
||||
|
||||
/** Fetch OTA Info
|
||||
*
|
||||
* For OTA using Topics, this API can be used to explicitly ask the backend if an OTA is available.
|
||||
* If it is, then the OTA callback would get invoked.
|
||||
*
|
||||
* @return ESP_OK if the OTA fetch publish message was successful.
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_ota_fetch(void);
|
||||
|
||||
/** Fetch OTA Info with a delay
|
||||
*
|
||||
* For OTA using Topics, this API can be used to explicitly ask the backend if an OTA is available
|
||||
* after a delay (in seconds) passed as an argument.
|
||||
*
|
||||
* @param[in] time Delay (in seconds)
|
||||
*
|
||||
* @return ESP_OK if the OTA fetch timer was created.
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_ota_fetch_with_delay(int time);
|
||||
|
||||
/** Mark OTA as valid
|
||||
*
|
||||
* This should be called if the OTA validation has been kept pending by returning OTA_DIAG_STATUS_PENDING
|
||||
* in the ota_diag callback and then, the validation was eventually successful. This can also be used to mark
|
||||
* the OTA valid even before RainMaker core does its own validations (primarily MQTT connection).
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_ota_mark_valid(void);
|
||||
|
||||
/** Mark OTA as invalid
|
||||
*
|
||||
* This should be called if the OTA validation has been kept pending by returning OTA_DIAG_STATUS_PENDING
|
||||
* in the ota_diag callback and then, the validation eventually failed. This can even be used to rollback
|
||||
* at any point of time before RainMaker core's internal logic and the application's logic mark the OTA
|
||||
* as valid.
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_ota_mark_invalid(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
38
components/esp_rainmaker/include/esp_rmaker_scenes.h
Normal file
38
components/esp_rainmaker/include/esp_rmaker_scenes.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2022 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
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
/** Enable Scenes
|
||||
*
|
||||
* This API enables the scenes service for the node. For more information,
|
||||
* check [here](https://rainmaker.espressif.com/docs/scenes.html)
|
||||
*
|
||||
* @note This API should be called after esp_rmaker_node_init() but before esp_rmaker_start().
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_scenes_enable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
38
components/esp_rainmaker/include/esp_rmaker_schedule.h
Normal file
38
components/esp_rainmaker/include/esp_rmaker_schedule.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** Enable Schedules
|
||||
*
|
||||
* This API enables the scheduling service for the node. For more information,
|
||||
* check [here](https://rainmaker.espressif.com/docs/scheduling.html)
|
||||
*
|
||||
* It is recommended to set the timezone while using schedules. Check [here](https://rainmaker.espressif.com/docs/time-service.html#time-zone) for more information on timezones
|
||||
*
|
||||
* @note This API should be called after esp_rmaker_node_init() but before esp_rmaker_start().
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_schedule_enable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** Create a standard Switch device
|
||||
*
|
||||
* This creates a Switch device with the mandatory parameters and also assigns
|
||||
* the primary parameter. The default parameter names will be used.
|
||||
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||
*
|
||||
* @param[in] dev_name The unique device name
|
||||
* @param[in] priv_data (Optional) Private data associated with the device. This should stay
|
||||
* allocated throughout the lifetime of the device
|
||||
* #@param[in] power Default value of the mandatory parameter "power"
|
||||
*
|
||||
* @return Device handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_device_t *esp_rmaker_switch_device_create(const char *dev_name,
|
||||
void *priv_data, bool power);
|
||||
|
||||
/** Create a standard Lightbulb device
|
||||
*
|
||||
* This creates a Lightbulb device with the mandatory parameters and also assigns
|
||||
* the primary parameter. The default parameter names will be used.
|
||||
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||
*
|
||||
* @param[in] dev_name The unique device name
|
||||
* @param[in] priv_data (Optional) Private data associated with the device. This should stay
|
||||
* allocated throughout the lifetime of the device
|
||||
* @param[in] power Default value of the mandatory parameter "power"
|
||||
*
|
||||
* @return Device handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_device_t *esp_rmaker_lightbulb_device_create(const char *dev_name,
|
||||
void *priv_data, bool power);
|
||||
|
||||
/** Create a standard Fan device
|
||||
*
|
||||
* This creates a Fan device with the mandatory parameters and also assigns
|
||||
* the primary parameter. The default parameter names will be used.
|
||||
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||
*
|
||||
* @param[in] dev_name The unique device name
|
||||
* @param[in] priv_data (Optional) Private data associated with the device. This should stay
|
||||
* allocated throughout the lifetime of the device
|
||||
* @param[in] power Default value of the mandatory parameter "power"
|
||||
*
|
||||
* @return Device handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_device_t *esp_rmaker_fan_device_create(const char *dev_name,
|
||||
void *priv_data, bool power);
|
||||
|
||||
/** Create a standard Temperature Sensor device
|
||||
*
|
||||
* This creates a Temperature Sensor device with the mandatory parameters and also assigns
|
||||
* the primary parameter. The default parameter names will be used.
|
||||
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||
*
|
||||
* @param[in] dev_name The unique device name
|
||||
* @param[in] priv_data (Optional) Private data associated with the device. This should stay
|
||||
* allocated throughout the lifetime of the device
|
||||
* @param[in] temperature Default value of the mandatory parameter "temperature"
|
||||
*
|
||||
* @return Device handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_device_t *esp_rmaker_temp_sensor_device_create(const char *dev_name,
|
||||
void *priv_data, float temperature);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
352
components/esp_rainmaker/include/esp_rmaker_standard_params.h
Normal file
352
components/esp_rainmaker/include/esp_rmaker_standard_params.h
Normal file
@@ -0,0 +1,352 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Suggested default names for the parameters.
|
||||
* These will also be used by default if you use any standard device helper APIs.
|
||||
*
|
||||
* @note These names are not mandatory. You can use the ESP RainMaker Core APIs
|
||||
* to create your own parameters with custom names, if required.
|
||||
*/
|
||||
|
||||
#define ESP_RMAKER_DEF_NAME_PARAM "Name"
|
||||
#define ESP_RMAKER_DEF_POWER_NAME "Power"
|
||||
#define ESP_RMAKER_DEF_BRIGHTNESS_NAME "Brightness"
|
||||
#define ESP_RMAKER_DEF_HUE_NAME "Hue"
|
||||
#define ESP_RMAKER_DEF_SATURATION_NAME "Saturation"
|
||||
#define ESP_RMAKER_DEF_INTENSITY_NAME "Intensity"
|
||||
#define ESP_RMAKER_DEF_CCT_NAME "CCT"
|
||||
#define ESP_RMAKER_DEF_DIRECTION_NAME "Direction"
|
||||
#define ESP_RMAKER_DEF_SPEED_NAME "Speed"
|
||||
#define ESP_RMAKER_DEF_TEMPERATURE_NAME "Temperature"
|
||||
#define ESP_RMAKER_DEF_OTA_STATUS_NAME "Status"
|
||||
#define ESP_RMAKER_DEF_OTA_INFO_NAME "Info"
|
||||
#define ESP_RMAKER_DEF_OTA_URL_NAME "URL"
|
||||
#define ESP_RMAKER_DEF_TIMEZONE_NAME "TZ"
|
||||
#define ESP_RMAKER_DEF_TIMEZONE_POSIX_NAME "TZ-POSIX"
|
||||
#define ESP_RMAKER_DEF_SCHEDULE_NAME "Schedules"
|
||||
#define ESP_RMAKER_DEF_SCENES_NAME "Scenes"
|
||||
#define ESP_RMAKER_DEF_REBOOT_NAME "Reboot"
|
||||
#define ESP_RMAKER_DEF_FACTORY_RESET_NAME "Factory-Reset"
|
||||
#define ESP_RMAKER_DEF_WIFI_RESET_NAME "Wi-Fi-Reset"
|
||||
#define ESP_RMAKER_DEF_LOCAL_CONTROL_POP "POP"
|
||||
#define ESP_RMAKER_DEF_LOCAL_CONTROL_TYPE "Type"
|
||||
#define ESP_RMAKER_DEF_ADD_ZIGBEE_DEVICE "Add_zigbee_device"
|
||||
|
||||
/**
|
||||
* Create standard name param
|
||||
*
|
||||
* This will create the standard name parameter.
|
||||
* This should be added to all devices for which you want a user customisable name.
|
||||
* The value should be same as the device name.
|
||||
*
|
||||
* All standard device creation APIs will add this internally.
|
||||
* No application registered callback will be called for this parameter,
|
||||
* and changes will be managed internally.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val The device name
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_name_param_create(const char *param_name, const char *val);
|
||||
|
||||
/**
|
||||
* Create standard Power param
|
||||
*
|
||||
* This will create the standard power parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_power_param_create(const char *param_name, bool val);
|
||||
|
||||
/**
|
||||
* Create standard Brightness param
|
||||
*
|
||||
* This will create the standard brightness parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_brightness_param_create(const char *param_name, int val);
|
||||
|
||||
/**
|
||||
* Create standard Hue param
|
||||
*
|
||||
* This will create the standard hue parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_hue_param_create(const char *param_name, int val);
|
||||
|
||||
/**
|
||||
* Create standard Saturation param
|
||||
*
|
||||
* This will create the standard saturation parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_saturation_param_create(const char *param_name, int val);
|
||||
|
||||
/**
|
||||
* Create standard Intensity param
|
||||
*
|
||||
* This will create the standard intensity parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_intensity_param_create(const char *param_name, int val);
|
||||
|
||||
/**
|
||||
* Create standard CCT param
|
||||
*
|
||||
* This will create the standard cct parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_cct_param_create(const char *param_name, int val);
|
||||
|
||||
/**
|
||||
* Create standard Direction param
|
||||
*
|
||||
* This will create the standard direction parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_direction_param_create(const char *param_name, int val);
|
||||
|
||||
/**
|
||||
* Create standard Speed param
|
||||
*
|
||||
* This will create the standard speed parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_speed_param_create(const char *param_name, int val);
|
||||
|
||||
/**
|
||||
* Create standard Temperature param
|
||||
*
|
||||
* This will create the standard temperature parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_temperature_param_create(const char *param_name, float val);
|
||||
|
||||
/**
|
||||
* Create standard OTA Status param
|
||||
*
|
||||
* This will create the standard ota status parameter. Default value
|
||||
* is set internally.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_ota_status_param_create(const char *param_name);
|
||||
|
||||
/**
|
||||
* Create standard OTA Info param
|
||||
*
|
||||
* This will create the standard ota info parameter. Default value
|
||||
* is set internally.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_ota_info_param_create(const char *param_name);
|
||||
|
||||
/**
|
||||
* Create standard OTA URL param
|
||||
*
|
||||
* This will create the standard ota url parameter. Default value
|
||||
* is set internally.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_ota_url_param_create(const char *param_name);
|
||||
|
||||
/**
|
||||
* Create standard Timezone param
|
||||
*
|
||||
* This will create the standard timezone parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter (Eg. "Asia/Shanghai"). Can be kept NULL.
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_timezone_param_create(const char *param_name, const char *val);
|
||||
|
||||
/**
|
||||
* Create standard POSIX Timezone param
|
||||
*
|
||||
* This will create the standard posix timezone parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter (Eg. "CST-8"). Can be kept NULL.
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_timezone_posix_param_create(const char *param_name, const char *val);
|
||||
|
||||
/**
|
||||
* Create standard Schedules param
|
||||
*
|
||||
* This will create the standard schedules parameter. Default value
|
||||
* is set internally.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] max_schedules Maximum number of schedules allowed
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_schedules_param_create(const char *param_name, int max_schedules);
|
||||
|
||||
/**
|
||||
* Create standard Scenes param
|
||||
*
|
||||
* This will create the standard scenes parameter. Default value
|
||||
* is set internally.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] max_scenes Maximum number of scenes allowed
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_scenes_param_create(const char *param_name, int max_scenes);
|
||||
|
||||
/**
|
||||
* Create standard Reboot param
|
||||
*
|
||||
* This will create the standard reboot parameter.
|
||||
* Set value to true (via write param) for the action to trigger.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_reboot_param_create(const char *param_name);
|
||||
|
||||
/**
|
||||
* Create standard Factory Reset param
|
||||
*
|
||||
* This will create the standard factory reset parameter.
|
||||
* Set value to true (via write param) for the action to trigger.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_factory_reset_param_create(const char *param_name);
|
||||
|
||||
/**
|
||||
* Create standard Wi-Fi Reset param
|
||||
*
|
||||
* This will create the standard Wi-Fi Reset parameter.
|
||||
* Set value to true (via write param) for the action to trigger.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_wifi_reset_param_create(const char *param_name);
|
||||
|
||||
/**
|
||||
* Create standard Local Control POP param
|
||||
*
|
||||
* This will create the standard Local Control POP parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter (Eg. "abcd1234"). Can be kept NULL.
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_local_control_pop_param_create(const char *param_name, const char *val);
|
||||
|
||||
/**
|
||||
* Create standard Local Control Type param
|
||||
*
|
||||
* This will create the standard Local Control security type parameter.
|
||||
*
|
||||
* @param[in] param_name Name of the parameter
|
||||
* @param[in] val Default Value of the parameter
|
||||
*
|
||||
* @return Parameter handle on success.
|
||||
* @return NULL in case of failures.
|
||||
*/
|
||||
esp_rmaker_param_t *esp_rmaker_local_control_type_param_create(const char *param_name, int val);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
124
components/esp_rainmaker/include/esp_rmaker_standard_services.h
Normal file
124
components/esp_rainmaker/include/esp_rmaker_standard_services.h
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** Create a standard OTA service
|
||||
*
|
||||
* This creates an OTA service with the mandatory parameters. The default parameter names will be used.
|
||||
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||
*
|
||||
* @param[in] serv_name The unique service name
|
||||
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||
* allocated throughout the lifetime of the service.
|
||||
*
|
||||
* @return service_handle on success.
|
||||
* @return NULL in case of any error.
|
||||
*/
|
||||
esp_rmaker_device_t *esp_rmaker_ota_service_create(const char *serv_name, void *priv_data);
|
||||
|
||||
/** Create a standard Time service
|
||||
*
|
||||
* This creates a Time service with the mandatory parameters. The default parameter names will be used.
|
||||
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||
*
|
||||
* @param[in] serv_name The unique service name
|
||||
* @param[in] timezone Default value of timezone string (Eg. "Asia/Shanghai"). Can be kept NULL.
|
||||
* @param[in] timezone_posix Default value of posix timezone string (Eg. "CST-8"). Can be kept NULL.
|
||||
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||
* allocated throughout the lifetime of the service.
|
||||
*
|
||||
* @return service_handle on success.
|
||||
* @return NULL in case of any error.
|
||||
*/
|
||||
esp_rmaker_device_t *esp_rmaker_time_service_create(const char *serv_name, const char *timezone,
|
||||
const char *timezone_posix, void *priv_data);
|
||||
|
||||
/** Create a standard Schedule service
|
||||
*
|
||||
* This creates a Schedule service with the mandatory parameters. The default parameter names will be used.
|
||||
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||
*
|
||||
* @param[in] serv_name The unique service name
|
||||
* @param[in] write_cb Write callback.
|
||||
* @param[in] read_cb Read callback.
|
||||
* @param[in] max_schedules Maximum number of schedules supported.
|
||||
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||
* allocated throughout the lifetime of the service.
|
||||
*
|
||||
* @return service_handle on success.
|
||||
* @return NULL in case of any error.
|
||||
*/
|
||||
esp_rmaker_device_t *esp_rmaker_create_schedule_service(const char *serv_name, esp_rmaker_device_write_cb_t write_cb, esp_rmaker_device_read_cb_t read_cb, int max_schedules, void *priv_data);
|
||||
|
||||
/** Create a standard Scenes service
|
||||
*
|
||||
* This creates a Scenes service with the mandatory parameters. The default parameter names will be used.
|
||||
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||
*
|
||||
* @param[in] serv_name The unique service name
|
||||
* @param[in] write_cb Write callback.
|
||||
* @param[in] read_cb Read callback.
|
||||
* @param[in] max_scenes Maximum number of scenes supported.
|
||||
* @param[in] deactivation_support Deactivation callback support.
|
||||
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||
* allocated throughout the lifetime of the service.
|
||||
*
|
||||
* @return service_handle on success.
|
||||
* @return NULL in case of any error.
|
||||
*/
|
||||
esp_rmaker_device_t *esp_rmaker_create_scenes_service(const char *serv_name, esp_rmaker_device_write_cb_t write_cb, esp_rmaker_device_read_cb_t read_cb, int max_scenes, bool deactivation_support, void *priv_data);
|
||||
|
||||
/** Create a standard System service
|
||||
*
|
||||
* This creates an empty System service. Appropriate parameters should be added by the caller.
|
||||
*
|
||||
* @param[in] serv_name The unique service name
|
||||
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||
* allocated throughout the lifetime of the service.
|
||||
*
|
||||
* @return service_handle on success.
|
||||
* @return NULL in case of any error.
|
||||
*/
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_create_system_service(const char *serv_name, void *priv_data);
|
||||
|
||||
/** Create a standard Local Control service
|
||||
*
|
||||
* This creates a Local Control service with the mandatory parameters. The default parameter names will be used.
|
||||
* Refer \ref esp_rmaker_standard_params.h for default names.
|
||||
*
|
||||
* @param[in] serv_name The unique service name
|
||||
* @param[in] pop Proof of possession
|
||||
* @param[in] sec_type Security type
|
||||
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
|
||||
* allocated throughout the lifetime of the service.
|
||||
*
|
||||
* @return service_handle on success.
|
||||
* @return NULL in case of any error.
|
||||
*/
|
||||
esp_rmaker_device_t *esp_rmaker_create_local_control_service(const char *serv_name, const char *pop, int sec_type, void *priv_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
102
components/esp_rainmaker/include/esp_rmaker_standard_types.h
Normal file
102
components/esp_rainmaker/include/esp_rmaker_standard_types.h
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/********** STANDARD UI TYPES **********/
|
||||
|
||||
#define ESP_RMAKER_UI_TOGGLE "esp.ui.toggle"
|
||||
#define ESP_RMAKER_UI_SLIDER "esp.ui.slider"
|
||||
#define ESP_RMAKER_UI_DROPDOWN "esp.ui.dropdown"
|
||||
#define ESP_RMAKER_UI_TEXT "esp.ui.text"
|
||||
#define ESP_RMAKER_UI_HUE_SLIDER "esp.ui.hue-slider"
|
||||
#define ESP_RMAKER_UI_HUE_CIRCLE "esp.ui.hue-circle"
|
||||
#define ESP_RMAKER_UI_PUSHBUTTON "esp.ui.push-btn-big"
|
||||
#define ESP_RMAKER_UI_TRIGGER "esp.ui.trigger"
|
||||
#define ESP_RMAKER_UI_HIDDEN "esp.ui.hidden"
|
||||
#define ESP_RMAKER_UI_QR_SCAN "esp.ui.qr-scan"
|
||||
|
||||
/********** STANDARD PARAM TYPES **********/
|
||||
|
||||
#define ESP_RMAKER_PARAM_NAME "esp.param.name"
|
||||
#define ESP_RMAKER_PARAM_POWER "esp.param.power"
|
||||
#define ESP_RMAKER_PARAM_BRIGHTNESS "esp.param.brightness"
|
||||
#define ESP_RMAKER_PARAM_HUE "esp.param.hue"
|
||||
#define ESP_RMAKER_PARAM_SATURATION "esp.param.saturation"
|
||||
#define ESP_RMAKER_PARAM_INTENSITY "esp.param.intensity"
|
||||
#define ESP_RMAKER_PARAM_CCT "esp.param.cct"
|
||||
#define ESP_RMAKER_PARAM_SPEED "esp.param.speed"
|
||||
#define ESP_RMAKER_PARAM_DIRECTION "esp.param.direction"
|
||||
#define ESP_RMAKER_PARAM_TEMPERATURE "esp.param.temperature"
|
||||
#define ESP_RMAKER_PARAM_OTA_STATUS "esp.param.ota_status"
|
||||
#define ESP_RMAKER_PARAM_OTA_INFO "esp.param.ota_info"
|
||||
#define ESP_RMAKER_PARAM_OTA_URL "esp.param.ota_url"
|
||||
#define ESP_RMAKER_PARAM_TIMEZONE "esp.param.tz"
|
||||
#define ESP_RMAKER_PARAM_TIMEZONE_POSIX "esp.param.tz_posix"
|
||||
#define ESP_RMAKER_PARAM_SCHEDULES "esp.param.schedules"
|
||||
#define ESP_RMAKER_PARAM_SCENES "esp.param.scenes"
|
||||
#define ESP_RMAKER_PARAM_REBOOT "esp.param.reboot"
|
||||
#define ESP_RMAKER_PARAM_FACTORY_RESET "esp.param.factory-reset"
|
||||
#define ESP_RMAKER_PARAM_WIFI_RESET "esp.param.wifi-reset"
|
||||
#define ESP_RMAKER_PARAM_LOCAL_CONTROL_POP "esp.param.local_control_pop"
|
||||
#define ESP_RMAKER_PARAM_LOCAL_CONTROL_TYPE "esp.param.local_control_type"
|
||||
#define ESP_RMAKER_PARAM_TOGGLE "esp.param.toggle"
|
||||
#define ESP_RMAKER_PARAM_RANGE "esp.param.range"
|
||||
#define ESP_RMAKER_PARAM_MODE "esp.param.mode"
|
||||
#define ESP_RMAKER_PARAM_BLINDS_POSITION "esp.param.blinds-position"
|
||||
#define ESP_RMAKER_PARAM_GARAGE_POSITION "esp.param.garage-position"
|
||||
#define ESP_RMAKER_PARAM_LIGHT_MODE "esp.param.light-mode"
|
||||
#define ESP_RMAKER_PARAM_AC_MODE "esp.param.ac-mode"
|
||||
#define ESP_RMAKER_PARAM_ADD_ZIGBEE_DEVICE "esp.param.add_zigbee_device"
|
||||
|
||||
|
||||
/********** STANDARD DEVICE TYPES **********/
|
||||
|
||||
#define ESP_RMAKER_DEVICE_SWITCH "esp.device.switch"
|
||||
#define ESP_RMAKER_DEVICE_LIGHTBULB "esp.device.lightbulb"
|
||||
#define ESP_RMAKER_DEVICE_FAN "esp.device.fan"
|
||||
#define ESP_RMAKER_DEVICE_TEMP_SENSOR "esp.device.temperature-sensor"
|
||||
#define ESP_RMAKER_DEVICE_LIGHT "esp.device.light"
|
||||
#define ESP_RMAKER_DEVICE_OUTLET "esp.device.outlet"
|
||||
#define ESP_RMAKER_DEVICE_PLUG "esp.device.plug"
|
||||
#define ESP_RMAKER_DEVICE_SOCKET "esp.device.socket"
|
||||
#define ESP_RMAKER_DEVICE_LOCK "esp.device.lock"
|
||||
#define ESP_RMAKER_DEVICE_BLINDS_INTERNAL "esp.device.blinds-internal"
|
||||
#define ESP_RMAKER_DEVICE_BLINDS_EXTERNAL "esp.device.blinds-external"
|
||||
#define ESP_RMAKER_DEVICE_GARAGE_DOOR "esp.device.garage-door"
|
||||
#define ESP_RMAKER_DEVICE_GARAGE_LOCK "esp.device.garage-door-lock"
|
||||
#define ESP_RMAKER_DEVICE_SPEAKER "esp.device.speaker"
|
||||
#define ESP_RMAKER_DEVICE_AIR_CONDITIONER "esp.device.air-conditioner"
|
||||
#define ESP_RMAKER_DEVICE_THERMOSTAT "esp.device.thermostat"
|
||||
#define ESP_RMAKER_DEVICE_TV "esp.device.tv"
|
||||
#define ESP_RMAKER_DEVICE_WASHER "esp.device.washer"
|
||||
#define ESP_RMAKER_DEVICE_OTHER "esp.device.other"
|
||||
#define ESP_RMAKER_DEVICE_ZIGBEE_GATEWAY "esp.device.zigbee_gateway"
|
||||
#define ESP_RMAKER_DEVICE_THREAD_BR "esp.device.thread-br"
|
||||
|
||||
/********** STANDARD SERVICE TYPES **********/
|
||||
#define ESP_RMAKER_SERVICE_OTA "esp.service.ota"
|
||||
#define ESP_RMAKER_SERVICE_TIME "esp.service.time"
|
||||
#define ESP_RMAKER_SERVICE_SCHEDULE "esp.service.schedule"
|
||||
#define ESP_RMAKER_SERVICE_SCENES "esp.service.scenes"
|
||||
#define ESP_RMAKER_SERVICE_SYSTEM "esp.service.system"
|
||||
#define ESP_RMAKER_SERVICE_LOCAL_CONTROL "esp.service.local_control"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
44
components/esp_rainmaker/include/esp_rmaker_thread_br.h
Normal file
44
components/esp_rainmaker/include/esp_rmaker_thread_br.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2024 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
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <esp_openthread.h>
|
||||
#include <esp_rcp_update.h>
|
||||
|
||||
/** Enable Thread Border Router
|
||||
*
|
||||
* This API enables the Thread Border Router service for the node. For more information,
|
||||
* check [here](https://openthread.io/guides/border-router/espressif-esp32)
|
||||
*
|
||||
* @note This API should be called after esp_rmaker_node_init() but before esp_rmaker_start().
|
||||
*
|
||||
* @param[in] platform_config Platform config for OpenThread
|
||||
* @param[in] rcp_update_config RCP update config
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_thread_br_enable(const esp_openthread_platform_config_t *platform_config,
|
||||
const esp_rcp_update_config_t *rcp_update_config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
84
components/esp_rainmaker/include/esp_rmaker_user_mapping.h
Normal file
84
components/esp_rainmaker/include/esp_rmaker_user_mapping.h
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
#include <esp_err.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** User-Node Mapping states */
|
||||
typedef enum {
|
||||
/** Mapping does not exist or is not initialized */
|
||||
ESP_RMAKER_USER_MAPPING_RESET = 0,
|
||||
/** Mapping has started */
|
||||
ESP_RMAKER_USER_MAPPING_STARTED,
|
||||
/** Mapping request sent to cloud */
|
||||
ESP_RMAKER_USER_MAPPING_REQ_SENT,
|
||||
/** Mapping is done */
|
||||
ESP_RMAKER_USER_MAPPING_DONE,
|
||||
} esp_rmaker_user_mapping_state_t;
|
||||
|
||||
/**
|
||||
* Get User-Node mapping state
|
||||
*
|
||||
* This returns the current user-node mapping state.
|
||||
*
|
||||
* @return user mapping state
|
||||
*/
|
||||
esp_rmaker_user_mapping_state_t esp_rmaker_user_node_mapping_get_state(void);
|
||||
|
||||
/**
|
||||
* Create User Mapping Endpoint
|
||||
*
|
||||
* This will create a custom provisioning endpoint for user-node mapping.
|
||||
* This should be called after network_prov_mgr_init()/wifi_prov_mgr_init() but before
|
||||
* network_prov_mgr_start_provisioning()/wifi_prov_mgr_start_provisioning()
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_user_mapping_endpoint_create(void);
|
||||
|
||||
/**
|
||||
* Register User Mapping Endpoint
|
||||
*
|
||||
* This will register the callback for the custom provisioning endpoint
|
||||
* for user-node mapping which was created with esp_rmaker_user_mapping_endpoint_create().
|
||||
* This should be called immediately after network_prov_mgr_start_provisioning()/wifi_prov_mgr_start_provisioning().
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_user_mapping_endpoint_register(void);
|
||||
|
||||
/** Add User-Node mapping
|
||||
*
|
||||
* This call will start the user-node mapping workflow on the node.
|
||||
* This is automatically called if you have used esp_rmaker_user_mapping_endpoint_register().
|
||||
* Use this API only if you want to trigger the user-node mapping after the Wi-Fi provisioning
|
||||
* has already been done.
|
||||
*
|
||||
* @param[in] user_id The User identifier received from the client (Phone app/CLI)
|
||||
* @param[in] secret_key The Secret key received from the client (Phone app/CLI)
|
||||
*
|
||||
* @return ESP_OK if the workflow was successfully triggered. This does not guarantee success
|
||||
* of the actual mapping. The mapping status needs to be checked separately by the clients.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t esp_rmaker_start_user_node_mapping(char *user_id, char *secret_key);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
4
components/esp_rainmaker/sdkconfig.rename
Normal file
4
components/esp_rainmaker/sdkconfig.rename
Normal file
@@ -0,0 +1,4 @@
|
||||
# sdkconfig replacement configurations for deprecated options formatted as
|
||||
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
|
||||
|
||||
CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE CONFIG_ESP_RMAKER_LOCAL_CTRL_AUTO_ENABLE
|
||||
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
|
||||
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
|
||||
b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
|
||||
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
|
||||
b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
|
||||
ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
|
||||
9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
|
||||
IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
|
||||
VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
|
||||
93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
|
||||
jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
|
||||
AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
|
||||
U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
|
||||
N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
|
||||
o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
|
||||
5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
|
||||
rqXRfboQnoZsG4q5WTP468SQvvG5
|
||||
-----END CERTIFICATE-----
|
||||
20
components/esp_rainmaker/server_certs/rmaker_mqtt_server.crt
Normal file
20
components/esp_rainmaker/server_certs/rmaker_mqtt_server.crt
Normal file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
|
||||
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
|
||||
b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
|
||||
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
|
||||
b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
|
||||
ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
|
||||
9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
|
||||
IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
|
||||
VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
|
||||
93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
|
||||
jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
|
||||
AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
|
||||
U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
|
||||
N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
|
||||
o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
|
||||
5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
|
||||
rqXRfboQnoZsG4q5WTP468SQvvG5
|
||||
-----END CERTIFICATE-----
|
||||
54
components/esp_rainmaker/server_certs/rmaker_ota_server.crt
Normal file
54
components/esp_rainmaker/server_certs/rmaker_ota_server.crt
Normal file
@@ -0,0 +1,54 @@
|
||||
ESP RainMaker OTA Upgrade Server Certificate.
|
||||
Replace this if you choose to use any other Server.
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEYzCCA0ugAwIBAgIQAYL4CY6i5ia5GjsnhB+5rzANBgkqhkiG9w0BAQsFADBa
|
||||
MQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl
|
||||
clRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE1
|
||||
MTIwODEyMDUwN1oXDTI1MDUxMDEyMDAwMFowZDELMAkGA1UEBhMCVVMxFTATBgNV
|
||||
BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEjMCEG
|
||||
A1UEAxMaRGlnaUNlcnQgQmFsdGltb3JlIENBLTIgRzIwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQC75wD+AAFz75uI8FwIdfBccHMf/7V6H40II/3HwRM/
|
||||
sSEGvU3M2y24hxkx3tprDcFd0lHVsF5y1PBm1ITykRhBtQkmsgOWBGmVU/oHTz6+
|
||||
hjpDK7JZtavRuvRZQHJaZ7bN5lX8CSukmLK/zKkf1L+Hj4Il/UWAqeydjPl0kM8c
|
||||
+GVQr834RavIL42ONh3e6onNslLZ5QnNNnEr2sbQm8b2pFtbObYfAB8ZpPvTvgzm
|
||||
+4/dDoDmpOdaxMAvcu6R84Nnyc3KzkqwIIH95HKvCRjnT0LsTSdCTQeg3dUNdfc2
|
||||
YMwmVJihiDfwg/etKVkgz7sl4dWe5vOuwQHrtQaJ4gqPAgMBAAGjggEZMIIBFTAd
|
||||
BgNVHQ4EFgQUwBKyKHRoRmfpcCV0GgBFWwZ9XEQwHwYDVR0jBBgwFoAU5Z1ZMIJH
|
||||
WMys+ghUNoZ7OrUETfAwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMC
|
||||
AYYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
|
||||
Y2VydC5jb20wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybDMuZGlnaWNlcnQu
|
||||
Y29tL09tbmlyb290MjAyNS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYB
|
||||
BQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDQYJKoZIhvcNAQEL
|
||||
BQADggEBAC/iN2bDGs+RVe4pFPpQEL6ZjeIo8XQWB2k7RDA99blJ9Wg2/rcwjang
|
||||
B0lCY0ZStWnGm0nyGg9Xxva3vqt1jQ2iqzPkYoVDVKtjlAyjU6DqHeSmpqyVDmV4
|
||||
7DOMvpQ+2HCr6sfheM4zlbv7LFjgikCmbUHY2Nmz+S8CxRtwa+I6hXsdGLDRS5rB
|
||||
bxcQKegOw+FUllSlkZUIII1pLJ4vP1C0LuVXH6+kc9KhJLsNkP5FEx2noSnYZgvD
|
||||
0WyzT7QrhExHkOyL4kGJE7YHRndC/bseF/r/JUuOUFfrjsxOFT+xJd1BDKCcYm1v
|
||||
upcHi9nzBhDFKdT3uhaQqNBU4UtJx5g=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEdTCCA12gAwIBAgIJAKcOSkw0grd/MA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV
|
||||
BAYTAlVTMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIw
|
||||
MAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
|
||||
eTAeFw0wOTA5MDIwMDAwMDBaFw0zNDA2MjgxNzM5MTZaMIGYMQswCQYDVQQGEwJV
|
||||
UzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UE
|
||||
ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjE7MDkGA1UEAxMyU3RhcmZp
|
||||
ZWxkIFNlcnZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi
|
||||
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVDDrEKvlO4vW+GZdfjohTsR8/
|
||||
y8+fIBNtKTrID30892t2OGPZNmCom15cAICyL1l/9of5JUOG52kbUpqQ4XHj2C0N
|
||||
Tm/2yEnZtvMaVq4rtnQU68/7JuMauh2WLmo7WJSJR1b/JaCTcFOD2oR0FMNnngRo
|
||||
Ot+OQFodSk7PQ5E751bWAHDLUu57fa4657wx+UX2wmDPE1kCK4DMNEffud6QZW0C
|
||||
zyyRpqbn3oUYSXxmTqM6bam17jQuug0DuDPfR+uxa40l2ZvOgdFFRjKWcIfeAg5J
|
||||
Q4W2bHO7ZOphQazJ1FTfhy/HIrImzJ9ZVGif/L4qL8RVHHVAYBeFAlU5i38FAgMB
|
||||
AAGjgfAwge0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0O
|
||||
BBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMB8GA1UdIwQYMBaAFL9ft9HO3R+G9FtV
|
||||
rNzXEMIOqYjnME8GCCsGAQUFBwEBBEMwQTAcBggrBgEFBQcwAYYQaHR0cDovL28u
|
||||
c3MyLnVzLzAhBggrBgEFBQcwAoYVaHR0cDovL3guc3MyLnVzL3guY2VyMCYGA1Ud
|
||||
HwQfMB0wG6AZoBeGFWh0dHA6Ly9zLnNzMi51cy9yLmNybDARBgNVHSAECjAIMAYG
|
||||
BFUdIAAwDQYJKoZIhvcNAQELBQADggEBACMd44pXyn3pF3lM8R5V/cxTbj5HD9/G
|
||||
VfKyBDbtgB9TxF00KGu+x1X8Z+rLP3+QsjPNG1gQggL4+C/1E2DUBc7xgQjB3ad1
|
||||
l08YuW3e95ORCLp+QCztweq7dp4zBncdDQh/U90bZKuCJ/Fp1U1ervShw3WnWEQt
|
||||
8jxwmKy6abaVd38PMV4s/KCHOkdp8Hlf9BRUpJVeEXgSYCfOn8J3/yNTd126/+pZ
|
||||
59vPr5KW7ySaNRB6nJHGDn2Z9j8Z3/VyVOEVqQdZe4O/Ui5GjLIAZHYcSNPYeehu
|
||||
VsyuLAOQ1xk4meTKCRlb/weWsKh/NEnfVqn3sF/tM+2MR7cwA130A4w=
|
||||
-----END CERTIFICATE-----
|
||||
206
components/esp_rainmaker/src/console/esp_rmaker_commands.c
Normal file
206
components/esp_rainmaker/src/console/esp_rmaker_commands.c
Normal file
@@ -0,0 +1,206 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_console.h>
|
||||
#include <string.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_user_mapping.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <esp_rmaker_cmd_resp.h>
|
||||
|
||||
#include <esp_rmaker_internal.h>
|
||||
#include <esp_rmaker_console_internal.h>
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
#include <network_provisioning/manager.h>
|
||||
#else
|
||||
#include <wifi_provisioning/manager.h>
|
||||
#endif
|
||||
|
||||
static const char *TAG = "esp_rmaker_commands";
|
||||
|
||||
static int user_node_mapping_handler(int argc, char** argv)
|
||||
{
|
||||
if (argc == 3) {
|
||||
printf("%s: Starting user-node mapping\n", TAG);
|
||||
return esp_rmaker_start_user_node_mapping(argv[1], argv[2]);
|
||||
} else {
|
||||
printf("%s: Invalid Usage.\n", TAG);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void register_user_node_mapping()
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "add-user",
|
||||
.help = "Initiate the User-Node mapping from the node. Usage: add-user <user_id> <secret_key>",
|
||||
.func = &user_node_mapping_handler,
|
||||
};
|
||||
ESP_LOGI(TAG, "Registering command: %s", cmd.command);
|
||||
esp_console_cmd_register(&cmd);
|
||||
}
|
||||
|
||||
static int get_node_id_handler(int argc, char** argv)
|
||||
{
|
||||
printf("%s: Node ID: %s\n", TAG, esp_rmaker_get_node_id());
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void register_get_node_id()
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "get-node-id",
|
||||
.help = "Get the Node ID for this board",
|
||||
.func = &get_node_id_handler,
|
||||
};
|
||||
ESP_LOGI(TAG, "Registering command: %s", cmd.command);
|
||||
esp_console_cmd_register(&cmd);
|
||||
}
|
||||
|
||||
static int wifi_prov_handler(int argc, char** argv)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
if (argc < 2) {
|
||||
printf("%s: Invalid Usage.\n", TAG);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
wifi_config_t wifi_config;
|
||||
memset(&wifi_config, 0, sizeof(wifi_config));
|
||||
memcpy(wifi_config.sta.ssid, argv[1], strlen(argv[1]));
|
||||
if (argc == 3) {
|
||||
memcpy(wifi_config.sta.password, argv[2], strlen(argv[2]));
|
||||
}
|
||||
|
||||
/* If device is still provisioning, use network_prov_mgr_configure_wifi_sta/wifi_prov_mgr_configure_sta */
|
||||
bool provisioned = false;
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
network_prov_mgr_is_wifi_provisioned(&provisioned);
|
||||
#else
|
||||
wifi_prov_mgr_is_provisioned(&provisioned);
|
||||
#endif
|
||||
if (!provisioned) { // provisioning in progress
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
network_prov_mgr_configure_wifi_sta(&wifi_config);
|
||||
#else
|
||||
wifi_prov_mgr_configure_sta(&wifi_config);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* If already provisioned, just set the new credentials */
|
||||
/* Stop the Wi-Fi */
|
||||
if (esp_wifi_stop() != ESP_OK) {
|
||||
printf("%s: Failed to stop wifi\n", TAG);
|
||||
}
|
||||
/* Configure Wi-Fi station with provided host credentials */
|
||||
if (esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) != ESP_OK) {
|
||||
printf("%s: Failed to set WiFi configuration\n", TAG);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* (Re)Start Wi-Fi */
|
||||
if (esp_wifi_start() != ESP_OK) {
|
||||
printf("%s: Failed to start WiFi\n", TAG);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Connect to AP */
|
||||
if (esp_wifi_connect() != ESP_OK) {
|
||||
printf("%s: Failed to connect WiFi\n", TAG);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
#else
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
}
|
||||
|
||||
static void register_wifi_prov()
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "wifi-prov",
|
||||
.help = "Wi-Fi Provision the node. Usage: wifi-prov <ssid> [<passphrase>]",
|
||||
.func = &wifi_prov_handler,
|
||||
};
|
||||
ESP_LOGI(TAG, "Registering command: %s", cmd.command);
|
||||
esp_console_cmd_register(&cmd);
|
||||
}
|
||||
|
||||
static int cmd_resp_cli_handler(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 5) {
|
||||
printf("Usage: cmd <req_id> <user_role> <cmd> <data>\n");
|
||||
return -1;
|
||||
}
|
||||
char *req_id = argv[1];
|
||||
uint8_t user_role = atoi(argv[2]);
|
||||
uint16_t cmd = atoi(argv[3]);
|
||||
esp_rmaker_cmd_resp_test_send(req_id, user_role, cmd, (void *)argv[4], strlen(argv[4]), esp_rmaker_test_cmd_resp, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_cmd_resp_command()
|
||||
{
|
||||
const esp_console_cmd_t cmd_resp_cmd = {
|
||||
.command = "cmd",
|
||||
.help = "Send command to command-response module. Usage cmd <req_id> <cmd> <user_role> <data>",
|
||||
.func = &cmd_resp_cli_handler,
|
||||
};
|
||||
ESP_LOGI(TAG, "Registering command: %s", cmd_resp_cmd.command);
|
||||
esp_console_cmd_register(&cmd_resp_cmd);
|
||||
}
|
||||
|
||||
static int sign_data_command(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf("Usage: sign-data <data>\n");
|
||||
return -1;
|
||||
}
|
||||
char *data = (char *)argv[1];
|
||||
size_t outlen = 0;
|
||||
char *response = NULL;
|
||||
esp_err_t err = esp_rmaker_node_auth_sign_msg((const void *)data, strlen(data), (void **)&response, &outlen);
|
||||
if(err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to sign message");
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGI(TAG, "hex signature(len %d): %s", outlen, response);
|
||||
free(response);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_sign_data_command()
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "sign-data",
|
||||
.help = "sends some data and expects a rsa signed response",
|
||||
.func = &sign_data_command,
|
||||
};
|
||||
ESP_LOGI(TAG, "Registering command: %s", cmd.command);
|
||||
esp_console_cmd_register(&cmd);
|
||||
}
|
||||
|
||||
void register_commands()
|
||||
{
|
||||
register_user_node_mapping();
|
||||
register_get_node_id();
|
||||
register_wifi_prov();
|
||||
register_cmd_resp_command();
|
||||
register_sign_data_command();
|
||||
}
|
||||
24
components/esp_rainmaker/src/console/esp_rmaker_console.c
Normal file
24
components/esp_rainmaker/src/console/esp_rmaker_console.c
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_rmaker_common_console.h>
|
||||
#include <esp_rmaker_console_internal.h>
|
||||
|
||||
esp_err_t esp_rmaker_console_init()
|
||||
{
|
||||
esp_rmaker_common_console_init();
|
||||
register_commands();
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
void register_commands(void);
|
||||
1041
components/esp_rainmaker/src/core/esp_rmaker_claim.c
Normal file
1041
components/esp_rainmaker/src/core/esp_rmaker_claim.c
Normal file
File diff suppressed because it is too large
Load Diff
49
components/esp_rainmaker/src/core/esp_rmaker_claim.h
Normal file
49
components/esp_rainmaker/src/core/esp_rmaker_claim.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
#include <mbedtls/pk.h>
|
||||
#include <esp_err.h>
|
||||
#define MAX_CSR_SIZE 1024
|
||||
#define MAX_PAYLOAD_SIZE 3072
|
||||
|
||||
typedef enum {
|
||||
RMAKER_CLAIM_STATE_PK_GENERATED = 1,
|
||||
RMAKER_CLAIM_STATE_INIT,
|
||||
RMAKER_CLAIM_STATE_INIT_DONE,
|
||||
RMAKER_CLAIM_STATE_CSR_GENERATED,
|
||||
RMAKER_CLAIM_STATE_VERIFY,
|
||||
RMAKER_CLAIM_STATE_VERIFY_DONE,
|
||||
} esp_rmaker_claim_state_t;
|
||||
|
||||
typedef struct {
|
||||
esp_rmaker_claim_state_t state;
|
||||
unsigned char csr[MAX_CSR_SIZE];
|
||||
char payload[MAX_PAYLOAD_SIZE];
|
||||
size_t payload_offset;
|
||||
size_t payload_len;
|
||||
mbedtls_pk_context key;
|
||||
} esp_rmaker_claim_data_t;
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_SELF_CLAIM
|
||||
esp_rmaker_claim_data_t * esp_rmaker_self_claim_init(void);
|
||||
esp_err_t esp_rmaker_self_claim_perform(esp_rmaker_claim_data_t *claim_data);
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||
esp_rmaker_claim_data_t * esp_rmaker_assisted_claim_init(void);
|
||||
esp_err_t esp_rmaker_assisted_claim_perform(esp_rmaker_claim_data_t *claim_data);
|
||||
#endif
|
||||
|
||||
void esp_rmaker_claim_data_free(esp_rmaker_claim_data_t *claim_data);
|
||||
|
||||
398
components/esp_rainmaker/src/core/esp_rmaker_claim.pb-c.c
Normal file
398
components/esp_rainmaker/src/core/esp_rmaker_claim.pb-c.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
|
||||
/* Generated from: esp_rmaker_claim.proto */
|
||||
|
||||
/* Do not generate deprecated warnings for self */
|
||||
#ifndef PROTOBUF_C__NO_DEPRECATED
|
||||
#define PROTOBUF_C__NO_DEPRECATED
|
||||
#endif
|
||||
|
||||
#include "esp_rmaker_claim.pb-c.h"
|
||||
void rmaker_claim__payload_buf__init
|
||||
(RmakerClaim__PayloadBuf *message)
|
||||
{
|
||||
static const RmakerClaim__PayloadBuf init_value = RMAKER_CLAIM__PAYLOAD_BUF__INIT;
|
||||
*message = init_value;
|
||||
}
|
||||
size_t rmaker_claim__payload_buf__get_packed_size
|
||||
(const RmakerClaim__PayloadBuf *message)
|
||||
{
|
||||
assert(message->base.descriptor == &rmaker_claim__payload_buf__descriptor);
|
||||
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||
}
|
||||
size_t rmaker_claim__payload_buf__pack
|
||||
(const RmakerClaim__PayloadBuf *message,
|
||||
uint8_t *out)
|
||||
{
|
||||
assert(message->base.descriptor == &rmaker_claim__payload_buf__descriptor);
|
||||
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||
}
|
||||
size_t rmaker_claim__payload_buf__pack_to_buffer
|
||||
(const RmakerClaim__PayloadBuf *message,
|
||||
ProtobufCBuffer *buffer)
|
||||
{
|
||||
assert(message->base.descriptor == &rmaker_claim__payload_buf__descriptor);
|
||||
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||
}
|
||||
RmakerClaim__PayloadBuf *
|
||||
rmaker_claim__payload_buf__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data)
|
||||
{
|
||||
return (RmakerClaim__PayloadBuf *)
|
||||
protobuf_c_message_unpack (&rmaker_claim__payload_buf__descriptor,
|
||||
allocator, len, data);
|
||||
}
|
||||
void rmaker_claim__payload_buf__free_unpacked
|
||||
(RmakerClaim__PayloadBuf *message,
|
||||
ProtobufCAllocator *allocator)
|
||||
{
|
||||
if(!message)
|
||||
return;
|
||||
assert(message->base.descriptor == &rmaker_claim__payload_buf__descriptor);
|
||||
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||
}
|
||||
void rmaker_claim__resp_payload__init
|
||||
(RmakerClaim__RespPayload *message)
|
||||
{
|
||||
static const RmakerClaim__RespPayload init_value = RMAKER_CLAIM__RESP_PAYLOAD__INIT;
|
||||
*message = init_value;
|
||||
}
|
||||
size_t rmaker_claim__resp_payload__get_packed_size
|
||||
(const RmakerClaim__RespPayload *message)
|
||||
{
|
||||
assert(message->base.descriptor == &rmaker_claim__resp_payload__descriptor);
|
||||
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||
}
|
||||
size_t rmaker_claim__resp_payload__pack
|
||||
(const RmakerClaim__RespPayload *message,
|
||||
uint8_t *out)
|
||||
{
|
||||
assert(message->base.descriptor == &rmaker_claim__resp_payload__descriptor);
|
||||
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||
}
|
||||
size_t rmaker_claim__resp_payload__pack_to_buffer
|
||||
(const RmakerClaim__RespPayload *message,
|
||||
ProtobufCBuffer *buffer)
|
||||
{
|
||||
assert(message->base.descriptor == &rmaker_claim__resp_payload__descriptor);
|
||||
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||
}
|
||||
RmakerClaim__RespPayload *
|
||||
rmaker_claim__resp_payload__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data)
|
||||
{
|
||||
return (RmakerClaim__RespPayload *)
|
||||
protobuf_c_message_unpack (&rmaker_claim__resp_payload__descriptor,
|
||||
allocator, len, data);
|
||||
}
|
||||
void rmaker_claim__resp_payload__free_unpacked
|
||||
(RmakerClaim__RespPayload *message,
|
||||
ProtobufCAllocator *allocator)
|
||||
{
|
||||
if(!message)
|
||||
return;
|
||||
assert(message->base.descriptor == &rmaker_claim__resp_payload__descriptor);
|
||||
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||
}
|
||||
void rmaker_claim__rmaker_claim_payload__init
|
||||
(RmakerClaim__RMakerClaimPayload *message)
|
||||
{
|
||||
static const RmakerClaim__RMakerClaimPayload init_value = RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__INIT;
|
||||
*message = init_value;
|
||||
}
|
||||
size_t rmaker_claim__rmaker_claim_payload__get_packed_size
|
||||
(const RmakerClaim__RMakerClaimPayload *message)
|
||||
{
|
||||
assert(message->base.descriptor == &rmaker_claim__rmaker_claim_payload__descriptor);
|
||||
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||
}
|
||||
size_t rmaker_claim__rmaker_claim_payload__pack
|
||||
(const RmakerClaim__RMakerClaimPayload *message,
|
||||
uint8_t *out)
|
||||
{
|
||||
assert(message->base.descriptor == &rmaker_claim__rmaker_claim_payload__descriptor);
|
||||
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||
}
|
||||
size_t rmaker_claim__rmaker_claim_payload__pack_to_buffer
|
||||
(const RmakerClaim__RMakerClaimPayload *message,
|
||||
ProtobufCBuffer *buffer)
|
||||
{
|
||||
assert(message->base.descriptor == &rmaker_claim__rmaker_claim_payload__descriptor);
|
||||
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||
}
|
||||
RmakerClaim__RMakerClaimPayload *
|
||||
rmaker_claim__rmaker_claim_payload__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data)
|
||||
{
|
||||
return (RmakerClaim__RMakerClaimPayload *)
|
||||
protobuf_c_message_unpack (&rmaker_claim__rmaker_claim_payload__descriptor,
|
||||
allocator, len, data);
|
||||
}
|
||||
void rmaker_claim__rmaker_claim_payload__free_unpacked
|
||||
(RmakerClaim__RMakerClaimPayload *message,
|
||||
ProtobufCAllocator *allocator)
|
||||
{
|
||||
if(!message)
|
||||
return;
|
||||
assert(message->base.descriptor == &rmaker_claim__rmaker_claim_payload__descriptor);
|
||||
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||
}
|
||||
static const ProtobufCFieldDescriptor rmaker_claim__payload_buf__field_descriptors[3] =
|
||||
{
|
||||
{
|
||||
"Offset",
|
||||
1,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_UINT32,
|
||||
0, /* quantifier_offset */
|
||||
offsetof(RmakerClaim__PayloadBuf, offset),
|
||||
NULL,
|
||||
NULL,
|
||||
0, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
{
|
||||
"Payload",
|
||||
2,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_BYTES,
|
||||
0, /* quantifier_offset */
|
||||
offsetof(RmakerClaim__PayloadBuf, payload),
|
||||
NULL,
|
||||
NULL,
|
||||
0, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
{
|
||||
"TotalLen",
|
||||
3,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_UINT32,
|
||||
0, /* quantifier_offset */
|
||||
offsetof(RmakerClaim__PayloadBuf, totallen),
|
||||
NULL,
|
||||
NULL,
|
||||
0, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
};
|
||||
static const unsigned rmaker_claim__payload_buf__field_indices_by_name[] = {
|
||||
0, /* field[0] = Offset */
|
||||
1, /* field[1] = Payload */
|
||||
2, /* field[2] = TotalLen */
|
||||
};
|
||||
static const ProtobufCIntRange rmaker_claim__payload_buf__number_ranges[1 + 1] =
|
||||
{
|
||||
{ 1, 0 },
|
||||
{ 0, 3 }
|
||||
};
|
||||
const ProtobufCMessageDescriptor rmaker_claim__payload_buf__descriptor =
|
||||
{
|
||||
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||
"rmaker_claim.PayloadBuf",
|
||||
"PayloadBuf",
|
||||
"RmakerClaim__PayloadBuf",
|
||||
"rmaker_claim",
|
||||
sizeof(RmakerClaim__PayloadBuf),
|
||||
3,
|
||||
rmaker_claim__payload_buf__field_descriptors,
|
||||
rmaker_claim__payload_buf__field_indices_by_name,
|
||||
1, rmaker_claim__payload_buf__number_ranges,
|
||||
(ProtobufCMessageInit) rmaker_claim__payload_buf__init,
|
||||
NULL,NULL,NULL /* reserved[123] */
|
||||
};
|
||||
static const ProtobufCFieldDescriptor rmaker_claim__resp_payload__field_descriptors[2] =
|
||||
{
|
||||
{
|
||||
"Status",
|
||||
1,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_ENUM,
|
||||
0, /* quantifier_offset */
|
||||
offsetof(RmakerClaim__RespPayload, status),
|
||||
&rmaker_claim__rmaker_claim_status__descriptor,
|
||||
NULL,
|
||||
0, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
{
|
||||
"Buf",
|
||||
2,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_MESSAGE,
|
||||
0, /* quantifier_offset */
|
||||
offsetof(RmakerClaim__RespPayload, buf),
|
||||
&rmaker_claim__payload_buf__descriptor,
|
||||
NULL,
|
||||
0, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
};
|
||||
static const unsigned rmaker_claim__resp_payload__field_indices_by_name[] = {
|
||||
1, /* field[1] = Buf */
|
||||
0, /* field[0] = Status */
|
||||
};
|
||||
static const ProtobufCIntRange rmaker_claim__resp_payload__number_ranges[1 + 1] =
|
||||
{
|
||||
{ 1, 0 },
|
||||
{ 0, 2 }
|
||||
};
|
||||
const ProtobufCMessageDescriptor rmaker_claim__resp_payload__descriptor =
|
||||
{
|
||||
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||
"rmaker_claim.RespPayload",
|
||||
"RespPayload",
|
||||
"RmakerClaim__RespPayload",
|
||||
"rmaker_claim",
|
||||
sizeof(RmakerClaim__RespPayload),
|
||||
2,
|
||||
rmaker_claim__resp_payload__field_descriptors,
|
||||
rmaker_claim__resp_payload__field_indices_by_name,
|
||||
1, rmaker_claim__resp_payload__number_ranges,
|
||||
(ProtobufCMessageInit) rmaker_claim__resp_payload__init,
|
||||
NULL,NULL,NULL /* reserved[123] */
|
||||
};
|
||||
static const ProtobufCFieldDescriptor rmaker_claim__rmaker_claim_payload__field_descriptors[3] =
|
||||
{
|
||||
{
|
||||
"msg",
|
||||
1,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_ENUM,
|
||||
0, /* quantifier_offset */
|
||||
offsetof(RmakerClaim__RMakerClaimPayload, msg),
|
||||
&rmaker_claim__rmaker_claim_msg_type__descriptor,
|
||||
NULL,
|
||||
0, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
{
|
||||
"cmdPayload",
|
||||
10,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_MESSAGE,
|
||||
offsetof(RmakerClaim__RMakerClaimPayload, payload_case),
|
||||
offsetof(RmakerClaim__RMakerClaimPayload, cmdpayload),
|
||||
&rmaker_claim__payload_buf__descriptor,
|
||||
NULL,
|
||||
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
{
|
||||
"respPayload",
|
||||
11,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_MESSAGE,
|
||||
offsetof(RmakerClaim__RMakerClaimPayload, payload_case),
|
||||
offsetof(RmakerClaim__RMakerClaimPayload, resppayload),
|
||||
&rmaker_claim__resp_payload__descriptor,
|
||||
NULL,
|
||||
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
};
|
||||
static const unsigned rmaker_claim__rmaker_claim_payload__field_indices_by_name[] = {
|
||||
1, /* field[1] = cmdPayload */
|
||||
0, /* field[0] = msg */
|
||||
2, /* field[2] = respPayload */
|
||||
};
|
||||
static const ProtobufCIntRange rmaker_claim__rmaker_claim_payload__number_ranges[2 + 1] =
|
||||
{
|
||||
{ 1, 0 },
|
||||
{ 10, 1 },
|
||||
{ 0, 3 }
|
||||
};
|
||||
const ProtobufCMessageDescriptor rmaker_claim__rmaker_claim_payload__descriptor =
|
||||
{
|
||||
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||
"rmaker_claim.RMakerClaimPayload",
|
||||
"RMakerClaimPayload",
|
||||
"RmakerClaim__RMakerClaimPayload",
|
||||
"rmaker_claim",
|
||||
sizeof(RmakerClaim__RMakerClaimPayload),
|
||||
3,
|
||||
rmaker_claim__rmaker_claim_payload__field_descriptors,
|
||||
rmaker_claim__rmaker_claim_payload__field_indices_by_name,
|
||||
2, rmaker_claim__rmaker_claim_payload__number_ranges,
|
||||
(ProtobufCMessageInit) rmaker_claim__rmaker_claim_payload__init,
|
||||
NULL,NULL,NULL /* reserved[123] */
|
||||
};
|
||||
static const ProtobufCEnumValue rmaker_claim__rmaker_claim_status__enum_values_by_number[5] =
|
||||
{
|
||||
{ "Success", "RMAKER_CLAIM__RMAKER_CLAIM_STATUS__Success", 0 },
|
||||
{ "Fail", "RMAKER_CLAIM__RMAKER_CLAIM_STATUS__Fail", 1 },
|
||||
{ "InvalidParam", "RMAKER_CLAIM__RMAKER_CLAIM_STATUS__InvalidParam", 2 },
|
||||
{ "InvalidState", "RMAKER_CLAIM__RMAKER_CLAIM_STATUS__InvalidState", 3 },
|
||||
{ "NoMemory", "RMAKER_CLAIM__RMAKER_CLAIM_STATUS__NoMemory", 4 },
|
||||
};
|
||||
static const ProtobufCIntRange rmaker_claim__rmaker_claim_status__value_ranges[] = {
|
||||
{0, 0},{0, 5}
|
||||
};
|
||||
static const ProtobufCEnumValueIndex rmaker_claim__rmaker_claim_status__enum_values_by_name[5] =
|
||||
{
|
||||
{ "Fail", 1 },
|
||||
{ "InvalidParam", 2 },
|
||||
{ "InvalidState", 3 },
|
||||
{ "NoMemory", 4 },
|
||||
{ "Success", 0 },
|
||||
};
|
||||
const ProtobufCEnumDescriptor rmaker_claim__rmaker_claim_status__descriptor =
|
||||
{
|
||||
PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
|
||||
"rmaker_claim.RMakerClaimStatus",
|
||||
"RMakerClaimStatus",
|
||||
"RmakerClaim__RMakerClaimStatus",
|
||||
"rmaker_claim",
|
||||
5,
|
||||
rmaker_claim__rmaker_claim_status__enum_values_by_number,
|
||||
5,
|
||||
rmaker_claim__rmaker_claim_status__enum_values_by_name,
|
||||
1,
|
||||
rmaker_claim__rmaker_claim_status__value_ranges,
|
||||
NULL,NULL,NULL,NULL /* reserved[1234] */
|
||||
};
|
||||
static const ProtobufCEnumValue rmaker_claim__rmaker_claim_msg_type__enum_values_by_number[8] =
|
||||
{
|
||||
{ "TypeCmdClaimStart", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimStart", 0 },
|
||||
{ "TypeRespClaimStart", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimStart", 1 },
|
||||
{ "TypeCmdClaimInit", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimInit", 2 },
|
||||
{ "TypeRespClaimInit", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimInit", 3 },
|
||||
{ "TypeCmdClaimVerify", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimVerify", 4 },
|
||||
{ "TypeRespClaimVerify", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimVerify", 5 },
|
||||
{ "TypeCmdClaimAbort", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimAbort", 6 },
|
||||
{ "TypeRespClaimAbort", "RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimAbort", 7 },
|
||||
};
|
||||
static const ProtobufCIntRange rmaker_claim__rmaker_claim_msg_type__value_ranges[] = {
|
||||
{0, 0},{0, 8}
|
||||
};
|
||||
static const ProtobufCEnumValueIndex rmaker_claim__rmaker_claim_msg_type__enum_values_by_name[8] =
|
||||
{
|
||||
{ "TypeCmdClaimAbort", 6 },
|
||||
{ "TypeCmdClaimInit", 2 },
|
||||
{ "TypeCmdClaimStart", 0 },
|
||||
{ "TypeCmdClaimVerify", 4 },
|
||||
{ "TypeRespClaimAbort", 7 },
|
||||
{ "TypeRespClaimInit", 3 },
|
||||
{ "TypeRespClaimStart", 1 },
|
||||
{ "TypeRespClaimVerify", 5 },
|
||||
};
|
||||
const ProtobufCEnumDescriptor rmaker_claim__rmaker_claim_msg_type__descriptor =
|
||||
{
|
||||
PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
|
||||
"rmaker_claim.RMakerClaimMsgType",
|
||||
"RMakerClaimMsgType",
|
||||
"RmakerClaim__RMakerClaimMsgType",
|
||||
"rmaker_claim",
|
||||
8,
|
||||
rmaker_claim__rmaker_claim_msg_type__enum_values_by_number,
|
||||
8,
|
||||
rmaker_claim__rmaker_claim_msg_type__enum_values_by_name,
|
||||
1,
|
||||
rmaker_claim__rmaker_claim_msg_type__value_ranges,
|
||||
NULL,NULL,NULL,NULL /* reserved[1234] */
|
||||
};
|
||||
175
components/esp_rainmaker/src/core/esp_rmaker_claim.pb-c.h
Normal file
175
components/esp_rainmaker/src/core/esp_rmaker_claim.pb-c.h
Normal file
@@ -0,0 +1,175 @@
|
||||
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
|
||||
/* Generated from: esp_rmaker_claim.proto */
|
||||
|
||||
#ifndef PROTOBUF_C_esp_5frmaker_5fclaim_2eproto__INCLUDED
|
||||
#define PROTOBUF_C_esp_5frmaker_5fclaim_2eproto__INCLUDED
|
||||
|
||||
#include <protobuf-c/protobuf-c.h>
|
||||
|
||||
PROTOBUF_C__BEGIN_DECLS
|
||||
|
||||
#if PROTOBUF_C_VERSION_NUMBER < 1003000
|
||||
# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
|
||||
#elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION
|
||||
# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct _RmakerClaim__PayloadBuf RmakerClaim__PayloadBuf;
|
||||
typedef struct _RmakerClaim__RespPayload RmakerClaim__RespPayload;
|
||||
typedef struct _RmakerClaim__RMakerClaimPayload RmakerClaim__RMakerClaimPayload;
|
||||
|
||||
|
||||
/* --- enums --- */
|
||||
|
||||
typedef enum _RmakerClaim__RMakerClaimStatus {
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_STATUS__Success = 0,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_STATUS__Fail = 1,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_STATUS__InvalidParam = 2,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_STATUS__InvalidState = 3,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_STATUS__NoMemory = 4
|
||||
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RMAKER_CLAIM__RMAKER_CLAIM_STATUS)
|
||||
} RmakerClaim__RMakerClaimStatus;
|
||||
typedef enum _RmakerClaim__RMakerClaimMsgType {
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimStart = 0,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimStart = 1,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimInit = 2,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimInit = 3,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimVerify = 4,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimVerify = 5,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimAbort = 6,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeRespClaimAbort = 7
|
||||
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE)
|
||||
} RmakerClaim__RMakerClaimMsgType;
|
||||
|
||||
/* --- messages --- */
|
||||
|
||||
struct _RmakerClaim__PayloadBuf
|
||||
{
|
||||
ProtobufCMessage base;
|
||||
uint32_t offset;
|
||||
ProtobufCBinaryData payload;
|
||||
uint32_t totallen;
|
||||
};
|
||||
#define RMAKER_CLAIM__PAYLOAD_BUF__INIT \
|
||||
{ PROTOBUF_C_MESSAGE_INIT (&rmaker_claim__payload_buf__descriptor) \
|
||||
, 0, {0,NULL}, 0 }
|
||||
|
||||
|
||||
struct _RmakerClaim__RespPayload
|
||||
{
|
||||
ProtobufCMessage base;
|
||||
RmakerClaim__RMakerClaimStatus status;
|
||||
RmakerClaim__PayloadBuf *buf;
|
||||
};
|
||||
#define RMAKER_CLAIM__RESP_PAYLOAD__INIT \
|
||||
{ PROTOBUF_C_MESSAGE_INIT (&rmaker_claim__resp_payload__descriptor) \
|
||||
, RMAKER_CLAIM__RMAKER_CLAIM_STATUS__Success, NULL }
|
||||
|
||||
|
||||
typedef enum {
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__PAYLOAD__NOT_SET = 0,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__PAYLOAD_CMD_PAYLOAD = 10,
|
||||
RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__PAYLOAD_RESP_PAYLOAD = 11
|
||||
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__PAYLOAD)
|
||||
} RmakerClaim__RMakerClaimPayload__PayloadCase;
|
||||
|
||||
struct _RmakerClaim__RMakerClaimPayload
|
||||
{
|
||||
ProtobufCMessage base;
|
||||
RmakerClaim__RMakerClaimMsgType msg;
|
||||
RmakerClaim__RMakerClaimPayload__PayloadCase payload_case;
|
||||
union {
|
||||
RmakerClaim__PayloadBuf *cmdpayload;
|
||||
RmakerClaim__RespPayload *resppayload;
|
||||
};
|
||||
};
|
||||
#define RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__INIT \
|
||||
{ PROTOBUF_C_MESSAGE_INIT (&rmaker_claim__rmaker_claim_payload__descriptor) \
|
||||
, RMAKER_CLAIM__RMAKER_CLAIM_MSG_TYPE__TypeCmdClaimStart, RMAKER_CLAIM__RMAKER_CLAIM_PAYLOAD__PAYLOAD__NOT_SET, {0} }
|
||||
|
||||
|
||||
/* RmakerClaim__PayloadBuf methods */
|
||||
void rmaker_claim__payload_buf__init
|
||||
(RmakerClaim__PayloadBuf *message);
|
||||
size_t rmaker_claim__payload_buf__get_packed_size
|
||||
(const RmakerClaim__PayloadBuf *message);
|
||||
size_t rmaker_claim__payload_buf__pack
|
||||
(const RmakerClaim__PayloadBuf *message,
|
||||
uint8_t *out);
|
||||
size_t rmaker_claim__payload_buf__pack_to_buffer
|
||||
(const RmakerClaim__PayloadBuf *message,
|
||||
ProtobufCBuffer *buffer);
|
||||
RmakerClaim__PayloadBuf *
|
||||
rmaker_claim__payload_buf__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data);
|
||||
void rmaker_claim__payload_buf__free_unpacked
|
||||
(RmakerClaim__PayloadBuf *message,
|
||||
ProtobufCAllocator *allocator);
|
||||
/* RmakerClaim__RespPayload methods */
|
||||
void rmaker_claim__resp_payload__init
|
||||
(RmakerClaim__RespPayload *message);
|
||||
size_t rmaker_claim__resp_payload__get_packed_size
|
||||
(const RmakerClaim__RespPayload *message);
|
||||
size_t rmaker_claim__resp_payload__pack
|
||||
(const RmakerClaim__RespPayload *message,
|
||||
uint8_t *out);
|
||||
size_t rmaker_claim__resp_payload__pack_to_buffer
|
||||
(const RmakerClaim__RespPayload *message,
|
||||
ProtobufCBuffer *buffer);
|
||||
RmakerClaim__RespPayload *
|
||||
rmaker_claim__resp_payload__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data);
|
||||
void rmaker_claim__resp_payload__free_unpacked
|
||||
(RmakerClaim__RespPayload *message,
|
||||
ProtobufCAllocator *allocator);
|
||||
/* RmakerClaim__RMakerClaimPayload methods */
|
||||
void rmaker_claim__rmaker_claim_payload__init
|
||||
(RmakerClaim__RMakerClaimPayload *message);
|
||||
size_t rmaker_claim__rmaker_claim_payload__get_packed_size
|
||||
(const RmakerClaim__RMakerClaimPayload *message);
|
||||
size_t rmaker_claim__rmaker_claim_payload__pack
|
||||
(const RmakerClaim__RMakerClaimPayload *message,
|
||||
uint8_t *out);
|
||||
size_t rmaker_claim__rmaker_claim_payload__pack_to_buffer
|
||||
(const RmakerClaim__RMakerClaimPayload *message,
|
||||
ProtobufCBuffer *buffer);
|
||||
RmakerClaim__RMakerClaimPayload *
|
||||
rmaker_claim__rmaker_claim_payload__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data);
|
||||
void rmaker_claim__rmaker_claim_payload__free_unpacked
|
||||
(RmakerClaim__RMakerClaimPayload *message,
|
||||
ProtobufCAllocator *allocator);
|
||||
/* --- per-message closures --- */
|
||||
|
||||
typedef void (*RmakerClaim__PayloadBuf_Closure)
|
||||
(const RmakerClaim__PayloadBuf *message,
|
||||
void *closure_data);
|
||||
typedef void (*RmakerClaim__RespPayload_Closure)
|
||||
(const RmakerClaim__RespPayload *message,
|
||||
void *closure_data);
|
||||
typedef void (*RmakerClaim__RMakerClaimPayload_Closure)
|
||||
(const RmakerClaim__RMakerClaimPayload *message,
|
||||
void *closure_data);
|
||||
|
||||
/* --- services --- */
|
||||
|
||||
|
||||
/* --- descriptors --- */
|
||||
|
||||
extern const ProtobufCEnumDescriptor rmaker_claim__rmaker_claim_status__descriptor;
|
||||
extern const ProtobufCEnumDescriptor rmaker_claim__rmaker_claim_msg_type__descriptor;
|
||||
extern const ProtobufCMessageDescriptor rmaker_claim__payload_buf__descriptor;
|
||||
extern const ProtobufCMessageDescriptor rmaker_claim__resp_payload__descriptor;
|
||||
extern const ProtobufCMessageDescriptor rmaker_claim__rmaker_claim_payload__descriptor;
|
||||
|
||||
PROTOBUF_C__END_DECLS
|
||||
|
||||
|
||||
#endif /* PROTOBUF_C_esp_5frmaker_5fclaim_2eproto__INCLUDED */
|
||||
41
components/esp_rainmaker/src/core/esp_rmaker_claim.proto
Normal file
41
components/esp_rainmaker/src/core/esp_rmaker_claim.proto
Normal file
@@ -0,0 +1,41 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package rmaker_claim;
|
||||
|
||||
enum RMakerClaimStatus {
|
||||
Success = 0;
|
||||
Fail = 1;
|
||||
InvalidParam = 2;
|
||||
InvalidState = 3;
|
||||
NoMemory = 4;
|
||||
}
|
||||
|
||||
message PayloadBuf {
|
||||
uint32 Offset = 1;
|
||||
bytes Payload = 2;
|
||||
uint32 TotalLen = 3;
|
||||
}
|
||||
|
||||
message RespPayload {
|
||||
RMakerClaimStatus Status = 1;
|
||||
PayloadBuf Buf = 2;
|
||||
}
|
||||
|
||||
enum RMakerClaimMsgType {
|
||||
TypeCmdClaimStart = 0;
|
||||
TypeRespClaimStart = 1;
|
||||
TypeCmdClaimInit = 2;
|
||||
TypeRespClaimInit = 3;
|
||||
TypeCmdClaimVerify = 4;
|
||||
TypeRespClaimVerify = 5;
|
||||
TypeCmdClaimAbort = 6;
|
||||
TypeRespClaimAbort = 7;
|
||||
}
|
||||
|
||||
message RMakerClaimPayload {
|
||||
RMakerClaimMsgType msg = 1;
|
||||
oneof payload {
|
||||
PayloadBuf cmdPayload = 10;
|
||||
RespPayload respPayload = 11;
|
||||
}
|
||||
}
|
||||
178
components/esp_rainmaker/src/core/esp_rmaker_client_data.c
Normal file
178
components/esp_rainmaker/src/core/esp_rmaker_client_data.c
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <sdkconfig.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <esp_rmaker_factory.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
#include "esp_rmaker_internal.h"
|
||||
#include "esp_rmaker_client_data.h"
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||
#include "esp_secure_cert_read.h"
|
||||
/*
|
||||
* Since TAG is not used in any other place in this file at the moment,
|
||||
* it has been placed inside this #ifdef to avoid -Werror=unused-variable.
|
||||
*/
|
||||
static const char *TAG = "esp_rmaker_client_data";
|
||||
#endif
|
||||
|
||||
extern uint8_t mqtt_server_root_ca_pem_start[] asm("_binary_rmaker_mqtt_server_crt_start");
|
||||
extern uint8_t mqtt_server_root_ca_pem_end[] asm("_binary_rmaker_mqtt_server_crt_end");
|
||||
|
||||
char * esp_rmaker_get_mqtt_host()
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_READ_MQTT_HOST_FROM_CONFIG
|
||||
return strdup(CONFIG_ESP_RMAKER_MQTT_HOST);
|
||||
#else
|
||||
char *host = esp_rmaker_factory_get(ESP_RMAKER_MQTT_HOST_NVS_KEY);
|
||||
#if defined(CONFIG_ESP_RMAKER_SELF_CLAIM) || defined(CONFIG_ESP_RMAKER_ASSISTED_CLAIM)
|
||||
if (!host) {
|
||||
return strdup(CONFIG_ESP_RMAKER_MQTT_HOST);
|
||||
}
|
||||
#endif /* defined(CONFIG_ESP_RMAKER_SELF_CLAIM) || defined(CONFIG_ESP_RMAKER_ASSISTED_CLAIM) */
|
||||
return host;
|
||||
#endif /* !CONFIG_ESP_RMAKER_READ_MQTT_HOST_FROM_CONFIG */
|
||||
}
|
||||
|
||||
char * esp_rmaker_get_client_cert()
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||
uint32_t client_cert_len = 0;
|
||||
char *client_cert_addr = NULL;
|
||||
if (esp_secure_cert_get_device_cert(&client_cert_addr, &client_cert_len) == ESP_OK) {
|
||||
return client_cert_addr;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to obtain flash address of device cert");
|
||||
ESP_LOGI(TAG, "Attempting to fetch client certificate from NVS");
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||
return esp_rmaker_factory_get(ESP_RMAKER_CLIENT_CERT_NVS_KEY);
|
||||
}
|
||||
|
||||
size_t esp_rmaker_get_client_cert_len()
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||
uint32_t client_cert_len = 0;
|
||||
char *client_cert_addr = NULL;
|
||||
if (esp_secure_cert_get_device_cert(&client_cert_addr, &client_cert_len) == ESP_OK) {
|
||||
return client_cert_len;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to obtain flash address of device cert");
|
||||
ESP_LOGI(TAG, "Attempting to fetch client certificate from NVS");
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||
return esp_rmaker_factory_get_size(ESP_RMAKER_CLIENT_CERT_NVS_KEY) + 1; /* +1 for NULL terminating byte */
|
||||
}
|
||||
|
||||
char * esp_rmaker_get_client_key()
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||
uint32_t client_key_len = 0;
|
||||
char *client_key_addr = NULL;
|
||||
if (esp_secure_cert_get_priv_key(&client_key_addr, &client_key_len) == ESP_OK) {
|
||||
return client_key_addr;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to obtain flash address of private_key");
|
||||
ESP_LOGI(TAG, "Attempting to fetch key from NVS");
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||
return esp_rmaker_factory_get(ESP_RMAKER_CLIENT_KEY_NVS_KEY);
|
||||
}
|
||||
|
||||
size_t esp_rmaker_get_client_key_len()
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||
uint32_t client_key_len = 0;
|
||||
char *client_key_addr = NULL;
|
||||
if (esp_secure_cert_get_priv_key(&client_key_addr, &client_key_len) == ESP_OK) {
|
||||
return client_key_len;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to obtain flash address of private_key");
|
||||
ESP_LOGI(TAG, "Attempting to fetch key from NVS");
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||
return esp_rmaker_factory_get_size(ESP_RMAKER_CLIENT_KEY_NVS_KEY) + 1; /* +1 for NULL terminating byte */
|
||||
}
|
||||
|
||||
char * esp_rmaker_get_client_csr()
|
||||
{
|
||||
return esp_rmaker_factory_get(ESP_RMAKER_CLIENT_CSR_NVS_KEY);
|
||||
}
|
||||
|
||||
esp_rmaker_mqtt_conn_params_t *esp_rmaker_get_mqtt_conn_params()
|
||||
{
|
||||
esp_rmaker_mqtt_conn_params_t *mqtt_conn_params = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_mqtt_conn_params_t));
|
||||
|
||||
#if defined(CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR) && defined(CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL)
|
||||
mqtt_conn_params->ds_data = esp_secure_cert_get_ds_ctx();
|
||||
if (mqtt_conn_params->ds_data == NULL) /* Get client key only if ds_data is NULL */
|
||||
#endif /* (defined(CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR) && defined(CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL)) */
|
||||
{
|
||||
if ((mqtt_conn_params->client_key = esp_rmaker_get_client_key()) == NULL) {
|
||||
goto init_err;
|
||||
}
|
||||
mqtt_conn_params->client_key_len = esp_rmaker_get_client_key_len();
|
||||
}
|
||||
if ((mqtt_conn_params->client_cert = esp_rmaker_get_client_cert()) == NULL) {
|
||||
goto init_err;
|
||||
}
|
||||
mqtt_conn_params->client_cert_len = esp_rmaker_get_client_cert_len();
|
||||
if ((mqtt_conn_params->mqtt_host = esp_rmaker_get_mqtt_host()) == NULL) {
|
||||
goto init_err;
|
||||
}
|
||||
mqtt_conn_params->server_cert = (char *)mqtt_server_root_ca_pem_start;
|
||||
mqtt_conn_params->client_id = esp_rmaker_get_node_id();
|
||||
return mqtt_conn_params;
|
||||
init_err:
|
||||
esp_rmaker_clean_mqtt_conn_params(mqtt_conn_params);
|
||||
free(mqtt_conn_params);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void esp_rmaker_clean_mqtt_conn_params(esp_rmaker_mqtt_conn_params_t *mqtt_conn_params)
|
||||
{
|
||||
if (mqtt_conn_params) {
|
||||
if (mqtt_conn_params->mqtt_host) {
|
||||
free(mqtt_conn_params->mqtt_host);
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||
if (mqtt_conn_params->client_cert) {
|
||||
esp_secure_cert_free_device_cert(mqtt_conn_params->client_cert);
|
||||
}
|
||||
#ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
|
||||
if (mqtt_conn_params->ds_data) {
|
||||
esp_secure_cert_free_ds_ctx(mqtt_conn_params->ds_data);
|
||||
}
|
||||
#else /* !CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */
|
||||
if (mqtt_conn_params->client_key) {
|
||||
esp_secure_cert_free_priv_key(mqtt_conn_params->client_key);
|
||||
}
|
||||
#endif /* CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */
|
||||
#else /* !CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||
if (mqtt_conn_params->client_cert) {
|
||||
free(mqtt_conn_params->client_cert);
|
||||
}
|
||||
if (mqtt_conn_params->client_key) {
|
||||
free(mqtt_conn_params->client_key);
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||
}
|
||||
}
|
||||
31
components/esp_rainmaker/src/core/esp_rmaker_client_data.h
Normal file
31
components/esp_rainmaker/src/core/esp_rmaker_client_data.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <esp_rmaker_mqtt_glue.h>
|
||||
|
||||
#define ESP_RMAKER_CLIENT_CERT_NVS_KEY "client_cert"
|
||||
#define ESP_RMAKER_CLIENT_KEY_NVS_KEY "client_key"
|
||||
#define ESP_RMAKER_MQTT_HOST_NVS_KEY "mqtt_host"
|
||||
#define ESP_RMAKER_CLIENT_CSR_NVS_KEY "csr"
|
||||
#define ESP_RMAKER_CLIENT_RANDOM_NVS_KEY "random"
|
||||
|
||||
char *esp_rmaker_get_client_cert();
|
||||
size_t esp_rmaker_get_client_cert_len();
|
||||
char *esp_rmaker_get_client_key();
|
||||
size_t esp_rmaker_get_client_key_len();
|
||||
char *esp_rmaker_get_client_csr();
|
||||
char *esp_rmaker_get_mqtt_host();
|
||||
esp_rmaker_mqtt_conn_params_t *esp_rmaker_get_mqtt_conn_params();
|
||||
void esp_rmaker_clean_mqtt_conn_params(esp_rmaker_mqtt_conn_params_t *mqtt_conn_params);
|
||||
150
components/esp_rainmaker/src/core/esp_rmaker_cmd_resp_manager.c
Normal file
150
components/esp_rainmaker/src/core/esp_rmaker_cmd_resp_manager.c
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <sdkconfig.h>
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_rmaker_common_events.h>
|
||||
#include <esp_rmaker_mqtt.h>
|
||||
#include <esp_rmaker_cmd_resp.h>
|
||||
#include "esp_rmaker_internal.h"
|
||||
#include "esp_rmaker_mqtt_topics.h"
|
||||
|
||||
static const char *TAG = "esp_rmaker_cmd_resp";
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE
|
||||
|
||||
/* These are for testing purpose only */
|
||||
static void esp_rmaker_resp_callback(const char *topic, void *payload, size_t payload_len, void *priv_data)
|
||||
{
|
||||
esp_rmaker_cmd_resp_parse_response(payload, payload_len, priv_data);
|
||||
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_test_cmd_resp(const void *cmd, size_t cmd_len, void *priv_data)
|
||||
{
|
||||
if (!cmd) {
|
||||
ESP_LOGE(TAG, "No command data to send.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||
snprintf(publish_topic, sizeof(publish_topic), "node/%s/%s", esp_rmaker_get_node_id(), TO_NODE_TOPIC_SUFFIX);
|
||||
return esp_rmaker_mqtt_publish(publish_topic, (void *)cmd, cmd_len, RMAKER_MQTT_QOS1, NULL);
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_cmd_resp_test_enable(void)
|
||||
{
|
||||
char subscribe_topic[100];
|
||||
snprintf(subscribe_topic, sizeof(subscribe_topic), "node/%s/%s",
|
||||
esp_rmaker_get_node_id(), CMD_RESP_TOPIC_SUFFIX);
|
||||
esp_err_t err = esp_rmaker_mqtt_subscribe(subscribe_topic, esp_rmaker_resp_callback, RMAKER_MQTT_QOS1, NULL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to subscribe to %s. Error %d", subscribe_topic, err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Command-Response test support enabled.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#else
|
||||
esp_err_t esp_rmaker_test_cmd_resp(const void *cmd, size_t cmd_len, void *priv_data)
|
||||
{
|
||||
ESP_LOGE(TAG, "Please enable CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE to use this.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif /* !CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE */
|
||||
|
||||
static esp_err_t esp_rmaker_publish_response(void *output, size_t output_len)
|
||||
{
|
||||
if (output) {
|
||||
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||
esp_rmaker_create_mqtt_topic(publish_topic, sizeof(publish_topic), CMD_RESP_TOPIC_SUFFIX, CMD_RESP_TOPIC_RULE);
|
||||
esp_err_t err = esp_rmaker_mqtt_publish(publish_topic, output, output_len, RMAKER_MQTT_QOS1, NULL);
|
||||
free(output);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to publish reponse.");
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "No output generated by command-response handler.");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void esp_rmaker_cmd_callback(const char *topic, void *payload, size_t payload_len, void *priv_data)
|
||||
{
|
||||
void *output = NULL;
|
||||
size_t output_len = 0;
|
||||
/* Any command data received is directly sent to the command response framework and on success,
|
||||
* the response (if any) is sent back to the MQTT Broker.
|
||||
*/
|
||||
if (esp_rmaker_cmd_response_handler(payload, payload_len, &output, &output_len) == ESP_OK) {
|
||||
esp_rmaker_publish_response(output, output_len);
|
||||
}
|
||||
}
|
||||
|
||||
/* Keeping the variable outside the function as there would subsequently also be a function
|
||||
* to disable command response.
|
||||
*/
|
||||
static bool cmd_resp_topic_subscribed = false;
|
||||
static esp_err_t esp_rmaker_cmd_resp_check_pending(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Checking for pending commands.");
|
||||
char subscribe_topic[100];
|
||||
snprintf(subscribe_topic, sizeof(subscribe_topic), "node/%s/%s",
|
||||
esp_rmaker_get_node_id(), TO_NODE_TOPIC_SUFFIX);
|
||||
if (!cmd_resp_topic_subscribed) {
|
||||
/* Subscribing just once because any subsequent reconnect will automatically subscribe to the topic */
|
||||
esp_err_t err = esp_rmaker_mqtt_subscribe(subscribe_topic, esp_rmaker_cmd_callback, RMAKER_MQTT_QOS1, NULL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to subscribe to %s. Error %d", subscribe_topic, err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
cmd_resp_topic_subscribed = true;
|
||||
}
|
||||
void *output = NULL;
|
||||
size_t output_len = 0;
|
||||
if (esp_rmaker_cmd_prepare_empty_response(&output, &output_len) == ESP_OK) {
|
||||
return esp_rmaker_publish_response(output, output_len);
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
esp_rmaker_cmd_resp_check_pending();
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_cmd_response_enable(void)
|
||||
{
|
||||
static bool enabled = false;
|
||||
if (enabled == true) {
|
||||
ESP_LOGI(TAG, "Command-response Module already enabled.");
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_LOGI(TAG, "Enabling Command-Response Module.");
|
||||
if (esp_rmaker_is_mqtt_connected()) {
|
||||
esp_rmaker_cmd_resp_check_pending();
|
||||
}
|
||||
if (esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &event_handler, NULL) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "RMAKER_MQTT_EVENT_CONNECTED event subscription failed.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE
|
||||
esp_rmaker_cmd_resp_test_enable();
|
||||
#endif /* CONFIG_ESP_RMAKER_CMD_RESP_TEST_ENABLE */
|
||||
enabled = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
714
components/esp_rainmaker/src/core/esp_rmaker_core.c
Normal file
714
components/esp_rainmaker/src/core/esp_rmaker_core.c
Normal file
@@ -0,0 +1,714 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <sdkconfig.h>
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_bit_defs.h>
|
||||
|
||||
#include <esp_rmaker_factory.h>
|
||||
#include <esp_rmaker_work_queue.h>
|
||||
#include <esp_rmaker_common_events.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_user_mapping.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include "esp_rmaker_internal.h"
|
||||
#include "esp_rmaker_mqtt.h"
|
||||
#include "esp_rmaker_claim.h"
|
||||
#include "esp_rmaker_client_data.h"
|
||||
|
||||
#ifdef CONFIG_OPENTHREAD_ENABLED
|
||||
#include <esp_openthread.h>
|
||||
#include <esp_openthread_lock.h>
|
||||
#include <esp_openthread_dns64.h>
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||
static const int WIFI_CONNECTED_EVENT = BIT0;
|
||||
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD)
|
||||
static const int THREAD_SET_DNS_SEVER_EVENT = BIT0;
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
#include "esp_mac.h"
|
||||
#endif
|
||||
|
||||
static const int MQTT_CONNECTED_EVENT = BIT1;
|
||||
static EventGroupHandle_t rmaker_core_event_group;
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(RMAKER_EVENT);
|
||||
|
||||
static const char *TAG = "esp_rmaker_core";
|
||||
|
||||
|
||||
#if defined(CONFIG_ESP_RMAKER_SELF_CLAIM) || defined(CONFIG_ESP_RMAKER_ASSISTED_CLAIM)
|
||||
#define ESP_RMAKER_CLAIM_ENABLED
|
||||
#endif
|
||||
|
||||
#define ESP_RMAKER_CHECK_HANDLE(rval) \
|
||||
{ \
|
||||
if (!esp_rmaker_priv_data) {\
|
||||
ESP_LOGE(TAG, "ESP RainMaker not initialised"); \
|
||||
return rval; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ESP_CLAIM_NODE_ID_SIZE 12
|
||||
|
||||
/* Handle to maintain internal information (will move to an internal file) */
|
||||
typedef struct {
|
||||
char *node_id;
|
||||
const esp_rmaker_node_t *node;
|
||||
bool enable_time_sync;
|
||||
esp_rmaker_state_t state;
|
||||
bool mqtt_connected;
|
||||
esp_rmaker_mqtt_conn_params_t *mqtt_conn_params;
|
||||
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||
bool need_claim;
|
||||
esp_rmaker_claim_data_t *claim_data;
|
||||
#endif /* ESP_RMAKER_CLAIM_ENABLED */
|
||||
} esp_rmaker_priv_data_t;
|
||||
|
||||
static esp_rmaker_priv_data_t *esp_rmaker_priv_data;
|
||||
|
||||
bool esp_rmaker_is_mqtt_connected()
|
||||
{
|
||||
if (esp_rmaker_priv_data) {
|
||||
return esp_rmaker_priv_data->mqtt_connected;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_rmaker_state_t esp_rmaker_get_state(void)
|
||||
{
|
||||
if (esp_rmaker_priv_data) {
|
||||
return esp_rmaker_priv_data->state;
|
||||
}
|
||||
return ESP_RMAKER_STATE_DEINIT;
|
||||
}
|
||||
|
||||
static void reset_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
switch (event_id) {
|
||||
case RMAKER_EVENT_WIFI_RESET:
|
||||
esp_rmaker_mqtt_disconnect();
|
||||
break;
|
||||
case RMAKER_EVENT_FACTORY_RESET:
|
||||
esp_rmaker_reset_user_node_mapping();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_rmaker_mqtt_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (!esp_rmaker_priv_data) {
|
||||
return;
|
||||
}
|
||||
if (event_base == RMAKER_COMMON_EVENT && event_id == RMAKER_MQTT_EVENT_CONNECTED) {
|
||||
esp_rmaker_priv_data->mqtt_connected = true;
|
||||
} else if (event_base == RMAKER_COMMON_EVENT && event_id == RMAKER_MQTT_EVENT_DISCONNECTED) {
|
||||
esp_rmaker_priv_data->mqtt_connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_READ_NODE_ID_FROM_CERT_CN
|
||||
|
||||
#include "mbedtls/x509_crt.h"
|
||||
#include "mbedtls/oid.h"
|
||||
|
||||
static char* esp_rmaker_populate_node_id_from_cert()
|
||||
{
|
||||
void *addr = NULL;
|
||||
size_t len = 0;
|
||||
addr = esp_rmaker_get_client_cert();
|
||||
if (addr) {
|
||||
len = esp_rmaker_get_client_cert_len();
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to get device certificate.");
|
||||
return NULL;
|
||||
}
|
||||
mbedtls_x509_crt crt;
|
||||
mbedtls_x509_crt_init(&crt);
|
||||
char *node_id = NULL;
|
||||
int ret = mbedtls_x509_crt_parse(&crt, addr, len);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Parsing of device certificate failed, returned %02X", ret);
|
||||
} else {
|
||||
mbedtls_asn1_named_data *cn_data;
|
||||
cn_data = mbedtls_asn1_find_named_data(&crt.subject, MBEDTLS_OID_AT_CN,
|
||||
MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_CN));
|
||||
if (cn_data) {
|
||||
node_id = MEM_CALLOC_EXTRAM(1, cn_data->val.len + 1);
|
||||
memcpy(node_id, (const char *)cn_data->val.p, cn_data->val.len);
|
||||
}
|
||||
}
|
||||
mbedtls_x509_crt_free(&crt);
|
||||
return node_id;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char *esp_rmaker_populate_node_id(bool use_claiming)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_READ_NODE_ID_FROM_CERT_CN
|
||||
char *node_id = esp_rmaker_populate_node_id_from_cert();
|
||||
#else /* !CONFIG_ESP_RMAKER_READ_NODE_ID_FROM_CERT_CN */
|
||||
char *node_id = esp_rmaker_factory_get("node_id");
|
||||
#endif /* CONFIG_ESP_RMAKER_READ_NODE_ID_FROM_CERT_CN */
|
||||
|
||||
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||
if (!node_id && use_claiming) {
|
||||
uint8_t mac_addr[6];
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
/* ESP_MAC_BASE was introduced in ESP-IDF v5.1. It is the same as the Wi-Fi Station MAC address
|
||||
* for chips supporting Wi-Fi. We can use base MAC address to generate claim init request for both
|
||||
* Wi-Fi and Thread devices
|
||||
*/
|
||||
esp_err_t err = esp_read_mac(mac_addr, ESP_MAC_BASE);
|
||||
#else
|
||||
/* Thread was officially supported in ESP-IDF v5.1. Use Wi-Fi Station MAC address to generate claim
|
||||
* init request.
|
||||
*/
|
||||
esp_err_t err = esp_wifi_get_mac(WIFI_IF_STA, mac_addr);
|
||||
#endif
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not fetch MAC address.");
|
||||
return NULL;
|
||||
}
|
||||
node_id = MEM_CALLOC_EXTRAM(1, ESP_CLAIM_NODE_ID_SIZE + 1); /* +1 for NULL terminatation */
|
||||
snprintf(node_id, ESP_CLAIM_NODE_ID_SIZE + 1, "%02X%02X%02X%02X%02X%02X",
|
||||
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
}
|
||||
#endif /* ESP_RMAKER_CLAIM_ENABLED */
|
||||
return node_id;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_change_node_id(char *node_id, size_t len)
|
||||
{
|
||||
if(esp_rmaker_priv_data) {
|
||||
char *new_node_id = strndup(node_id, len);
|
||||
if (!new_node_id) {
|
||||
ESP_LOGE(TAG, "Failed to allocate %lu bytes for new node_id.", (unsigned long) len);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (esp_rmaker_priv_data->node_id) {
|
||||
free(esp_rmaker_priv_data->node_id);
|
||||
}
|
||||
esp_rmaker_priv_data->node_id = new_node_id;
|
||||
_esp_rmaker_node_t *node = (_esp_rmaker_node_t *)esp_rmaker_get_node();
|
||||
node->node_id = new_node_id;
|
||||
ESP_LOGI(TAG, "New Node ID ----- %s", new_node_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
|
||||
/* Event handler for catching system events */
|
||||
static void esp_rmaker_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
#ifdef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||
if (esp_rmaker_priv_data->claim_data) {
|
||||
ESP_LOGE(TAG, "Node connected to Wi-Fi without Assisted claiming. Cannot proceed to MQTT connection.");
|
||||
ESP_LOGE(TAG, "Please update your phone apps and repeat Wi-Fi provisioning with BLE transport.");
|
||||
}
|
||||
#endif
|
||||
if (rmaker_core_event_group) {
|
||||
/* Signal rmaker thread to continue execution */
|
||||
xEventGroupSetBits(rmaker_core_event_group, WIFI_CONNECTED_EVENT);
|
||||
}
|
||||
} else
|
||||
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD) /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
if (event_base == OPENTHREAD_EVENT && event_id == OPENTHREAD_EVENT_SET_DNS_SERVER) {
|
||||
#ifdef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||
if (esp_rmaker_priv_data->claim_data) {
|
||||
ESP_LOGE(TAG, "Node connected to Thread network without Assisted claiming. Cannot proceed to MQTT connection.");
|
||||
ESP_LOGE(TAG, "Please update your phone apps and repeat Wi-Fi provisioning with BLE transport.");
|
||||
}
|
||||
#endif
|
||||
if (rmaker_core_event_group) {
|
||||
/* Signal rmaker thread to continue execution */
|
||||
xEventGroupSetBits(rmaker_core_event_group, THREAD_SET_DNS_SEVER_EVENT);
|
||||
}
|
||||
} else
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||
if (event_base == RMAKER_EVENT &&
|
||||
(event_id == RMAKER_EVENT_USER_NODE_MAPPING_DONE ||
|
||||
event_id == RMAKER_EVENT_USER_NODE_MAPPING_RESET)) {
|
||||
esp_event_handler_unregister(RMAKER_EVENT, event_id, &esp_rmaker_event_handler);
|
||||
esp_rmaker_params_mqtt_init();
|
||||
} else if (event_base == RMAKER_COMMON_EVENT && event_id == RMAKER_MQTT_EVENT_CONNECTED) {
|
||||
if (rmaker_core_event_group) {
|
||||
/* Signal rmaker thread to continue execution */
|
||||
xEventGroupSetBits(rmaker_core_event_group, MQTT_CONNECTED_EVENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_deinit_priv_data(esp_rmaker_priv_data_t *rmaker_priv_data)
|
||||
{
|
||||
if (!rmaker_priv_data) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_rmaker_work_queue_deinit();
|
||||
#ifndef CONFIG_ESP_RMAKER_DISABLE_USER_MAPPING_PROV
|
||||
esp_rmaker_user_mapping_prov_deinit();
|
||||
#endif
|
||||
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||
if (rmaker_priv_data->claim_data) {
|
||||
esp_rmaker_claim_data_free(rmaker_priv_data->claim_data);
|
||||
}
|
||||
#endif
|
||||
if (rmaker_priv_data->mqtt_conn_params) {
|
||||
esp_rmaker_clean_mqtt_conn_params(rmaker_priv_data->mqtt_conn_params);
|
||||
free(rmaker_priv_data->mqtt_conn_params);
|
||||
}
|
||||
if (rmaker_priv_data->node_id) {
|
||||
free(rmaker_priv_data->node_id);
|
||||
}
|
||||
free(rmaker_priv_data);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_node_deinit(const esp_rmaker_node_t *node)
|
||||
{
|
||||
if (!esp_rmaker_priv_data) {
|
||||
ESP_LOGE(TAG, "ESP RainMaker already de-initialized.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (esp_rmaker_priv_data->state != ESP_RMAKER_STATE_INIT_DONE) {
|
||||
ESP_LOGE(TAG, "ESP RainMaker is still running. Please stop it first.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_rmaker_node_delete(node);
|
||||
esp_rmaker_priv_data->node = NULL;
|
||||
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||
esp_rmaker_priv_data = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
char *esp_rmaker_get_node_id(void)
|
||||
{
|
||||
if (esp_rmaker_priv_data) {
|
||||
return esp_rmaker_priv_data->node_id;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_report_node_config_and_state()
|
||||
{
|
||||
if (esp_rmaker_report_node_config() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Report node config failed.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (esp_rmaker_user_node_mapping_get_state() == ESP_RMAKER_USER_MAPPING_DONE) {
|
||||
if (esp_rmaker_report_node_state() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Report node state failed.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void __esp_rmaker_report_node_config_and_state(void *data)
|
||||
{
|
||||
esp_rmaker_report_node_config_and_state();
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_report_node_details()
|
||||
{
|
||||
return esp_rmaker_work_queue_add_task(__esp_rmaker_report_node_config_and_state, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void esp_rmaker_task(void *data)
|
||||
{
|
||||
ESP_RMAKER_CHECK_HANDLE();
|
||||
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_STARTING;
|
||||
esp_err_t err = ESP_FAIL;
|
||||
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||
wifi_ap_record_t ap_info;
|
||||
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD)
|
||||
ip6_addr_t nat64_prefix;
|
||||
#endif
|
||||
esp_rmaker_priv_data->mqtt_connected = false;
|
||||
esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &esp_rmaker_mqtt_event_handler, NULL);
|
||||
esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_DISCONNECTED, &esp_rmaker_mqtt_event_handler, NULL);
|
||||
rmaker_core_event_group = xEventGroupCreate();
|
||||
if (!rmaker_core_event_group) {
|
||||
ESP_LOGE(TAG, "Failed to create event group. Aborting");
|
||||
goto rmaker_end;
|
||||
}
|
||||
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||
err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &esp_rmaker_event_handler, esp_rmaker_priv_data);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register event handler. Error: %d. Aborting", err);
|
||||
goto rmaker_end;
|
||||
}
|
||||
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD)
|
||||
err = esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_SET_DNS_SERVER, &esp_rmaker_event_handler, esp_rmaker_priv_data);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register event handler. Error: %d. Aborting", err);
|
||||
goto rmaker_end;
|
||||
}
|
||||
#endif
|
||||
err = esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &esp_rmaker_event_handler, esp_rmaker_priv_data);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register event handler. Error: %d. Aborting", err);
|
||||
goto rmaker_end;
|
||||
}
|
||||
/* Assisted claiming needs to be done before Wi-Fi connection */
|
||||
#ifdef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||
if (esp_rmaker_priv_data->need_claim) {
|
||||
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||
if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) {
|
||||
ESP_LOGE(TAG, "Node connected to Wi-Fi without Assisted claiming. Cannot proceed to MQTT connection.");
|
||||
ESP_LOGE(TAG, "Please update your phone apps and repeat Wi-Fi provisioning with BLE transport.");
|
||||
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &esp_rmaker_event_handler);
|
||||
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD)
|
||||
if (esp_openthread_get_nat64_prefix(&nat64_prefix) == ESP_OK) {
|
||||
ESP_LOGE(TAG, "Node connected to Thread without Assisted claiming. Cannot proceed to MQTT connection.");
|
||||
ESP_LOGE(TAG, "Please update your phone apps and repeat Thread provisioning with BLE transport.");
|
||||
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_SET_DNS_SERVER, &esp_rmaker_event_handler);
|
||||
#endif
|
||||
err = ESP_FAIL;
|
||||
goto rmaker_end;
|
||||
}
|
||||
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_STARTED, NULL, 0);
|
||||
err = esp_rmaker_assisted_claim_perform(esp_rmaker_priv_data->claim_data);
|
||||
if (err != ESP_OK) {
|
||||
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_FAILED, NULL, 0);
|
||||
ESP_LOGE(TAG, "esp_rmaker_self_claim_perform() returned %d. Aborting", err);
|
||||
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &esp_rmaker_event_handler);
|
||||
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD)
|
||||
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_SET_DNS_SERVER, &esp_rmaker_event_handler);
|
||||
#endif
|
||||
goto rmaker_end;
|
||||
}
|
||||
esp_rmaker_priv_data->claim_data = NULL;
|
||||
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_SUCCESSFUL, NULL, 0);
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_ASSISTED_CLAIM */
|
||||
#if defined(CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI)
|
||||
/* Check if already connected to Wi-Fi */
|
||||
if (esp_wifi_sta_get_ap_info(&ap_info) != ESP_OK) {
|
||||
/* Wait for Wi-Fi connection */
|
||||
xEventGroupWaitBits(rmaker_core_event_group, WIFI_CONNECTED_EVENT, false, true, portMAX_DELAY);
|
||||
}
|
||||
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &esp_rmaker_event_handler);
|
||||
#elif defined(CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD) /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
err = esp_openthread_get_nat64_prefix(&nat64_prefix);
|
||||
esp_openthread_lock_release();
|
||||
/* Check if already get nat64 prefix */
|
||||
if (err != ESP_OK) {
|
||||
/* Wait for Thread connection */
|
||||
xEventGroupWaitBits(rmaker_core_event_group, THREAD_SET_DNS_SEVER_EVENT, false, true, portMAX_DELAY);
|
||||
err = ESP_OK;
|
||||
}
|
||||
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_SET_DNS_SERVER, &esp_rmaker_event_handler);
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||
|
||||
if (esp_rmaker_priv_data->enable_time_sync) {
|
||||
#ifdef CONFIG_MBEDTLS_HAVE_TIME_DATE
|
||||
esp_rmaker_time_wait_for_sync(portMAX_DELAY);
|
||||
#endif
|
||||
}
|
||||
/* Self claiming can be done only after Wi-Fi connection */
|
||||
#ifdef CONFIG_ESP_RMAKER_SELF_CLAIM
|
||||
if (esp_rmaker_priv_data->need_claim) {
|
||||
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_STARTED, NULL, 0);
|
||||
err = esp_rmaker_self_claim_perform(esp_rmaker_priv_data->claim_data);
|
||||
if (err != ESP_OK) {
|
||||
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_FAILED, NULL, 0);
|
||||
ESP_LOGE(TAG, "esp_rmaker_self_claim_perform() returned %d. Aborting", err);
|
||||
goto rmaker_end;
|
||||
}
|
||||
esp_rmaker_priv_data->claim_data = NULL;
|
||||
esp_rmaker_post_event(RMAKER_EVENT_CLAIM_SUCCESSFUL, NULL, 0);
|
||||
}
|
||||
#endif
|
||||
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||
if (esp_rmaker_priv_data->need_claim) {
|
||||
esp_rmaker_priv_data->mqtt_conn_params = esp_rmaker_get_mqtt_conn_params();
|
||||
if (!esp_rmaker_priv_data->mqtt_conn_params) {
|
||||
ESP_LOGE(TAG, "Failed to initialise MQTT Config after claiming. Aborting");
|
||||
err = ESP_FAIL;
|
||||
goto rmaker_end;
|
||||
}
|
||||
err = esp_rmaker_mqtt_init(esp_rmaker_priv_data->mqtt_conn_params);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_rmaker_mqtt_init() returned %d. Aborting", err);
|
||||
goto rmaker_end;
|
||||
}
|
||||
esp_rmaker_priv_data->need_claim = false;
|
||||
}
|
||||
#endif /* ESP_RMAKER_CLAIM_ENABLED */
|
||||
#ifdef CONFIG_ESP_RMAKER_CMD_RESP_ENABLE
|
||||
err = esp_rmaker_cmd_response_enable();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to enable Command - Response module. Aborting!!!");
|
||||
goto rmaker_end;
|
||||
}
|
||||
esp_rmaker_node_add_attribute(esp_rmaker_get_node(), "cmd-resp", "1");
|
||||
#else
|
||||
ESP_LOGW(TAG, "Command-Response Module not enabled. Set CONFIG_ESP_RMAKER_CMD_RESP_ENABLE=y to use it.");
|
||||
#endif /* !CONFIG_ESP_RMAKER_CMD_RESP_ENABLE */
|
||||
#ifdef CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE
|
||||
err = esp_rmaker_local_ctrl_enable();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to start local control service. Aborting!!!");
|
||||
goto rmaker_end;
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE */
|
||||
err = esp_rmaker_mqtt_connect();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_rmaker_mqtt_connect() returned %d. Aborting", err);
|
||||
goto rmaker_end;
|
||||
}
|
||||
ESP_LOGI(TAG, "Waiting for MQTT connection");
|
||||
xEventGroupWaitBits(rmaker_core_event_group, MQTT_CONNECTED_EVENT, false, true, portMAX_DELAY);
|
||||
esp_event_handler_unregister(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &esp_rmaker_event_handler);
|
||||
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_STARTED;
|
||||
err = esp_rmaker_report_node_config();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Aborting!!!");
|
||||
goto rmaker_end;
|
||||
}
|
||||
if (esp_rmaker_user_node_mapping_get_state() == ESP_RMAKER_USER_MAPPING_DONE) {
|
||||
err = esp_rmaker_params_mqtt_init();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Aborting!!!");
|
||||
goto rmaker_end;
|
||||
}
|
||||
} else {
|
||||
/* If network is connected without even starting the user-node mapping workflow,
|
||||
* it could mean that some incorrect app was used to provision the device. Even
|
||||
* if the older user would not be able to update the params, it would be better
|
||||
* to completely reset the user permissions by sending a dummy user node mapping
|
||||
* request, so that the earlier user won't even see the connectivity and other
|
||||
* status.
|
||||
*/
|
||||
if (esp_rmaker_user_node_mapping_get_state() != ESP_RMAKER_USER_MAPPING_STARTED) {
|
||||
esp_rmaker_reset_user_node_mapping();
|
||||
/* Wait for user reset to finish. */
|
||||
err = esp_event_handler_register(RMAKER_EVENT, RMAKER_EVENT_USER_NODE_MAPPING_RESET,
|
||||
&esp_rmaker_event_handler, NULL);
|
||||
} else {
|
||||
/* Wait for User Node mapping to finish. */
|
||||
err = esp_event_handler_register(RMAKER_EVENT, RMAKER_EVENT_USER_NODE_MAPPING_DONE,
|
||||
&esp_rmaker_event_handler, NULL);
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Aborting!!!");
|
||||
goto rmaker_end;
|
||||
}
|
||||
ESP_LOGI(TAG, "Waiting for User Node Association.");
|
||||
}
|
||||
err = ESP_OK;
|
||||
|
||||
rmaker_end:
|
||||
if (rmaker_core_event_group) {
|
||||
vEventGroupDelete(rmaker_core_event_group);
|
||||
}
|
||||
rmaker_core_event_group = NULL;
|
||||
if (err == ESP_OK) {
|
||||
return;
|
||||
}
|
||||
if (esp_rmaker_priv_data->mqtt_connected) {
|
||||
esp_rmaker_mqtt_disconnect();
|
||||
}
|
||||
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_INIT_DONE;
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t esp_rmaker_mqtt_conn_params_init(esp_rmaker_priv_data_t *rmaker_priv_data, bool use_claiming)
|
||||
{
|
||||
rmaker_priv_data->mqtt_conn_params = esp_rmaker_get_mqtt_conn_params();
|
||||
if (rmaker_priv_data->mqtt_conn_params) {
|
||||
return ESP_OK;
|
||||
}
|
||||
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||
if (use_claiming) {
|
||||
#ifdef CONFIG_ESP_RMAKER_SELF_CLAIM
|
||||
rmaker_priv_data->claim_data = esp_rmaker_self_claim_init();
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_ASSISTED_CLAIM
|
||||
rmaker_priv_data->claim_data = esp_rmaker_assisted_claim_init();
|
||||
#endif
|
||||
if (!rmaker_priv_data->claim_data) {
|
||||
ESP_LOGE(TAG, "Failed to initialise Claiming.");
|
||||
return ESP_FAIL;
|
||||
} else {
|
||||
rmaker_priv_data->need_claim = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
#endif /* ESP_RMAKER_CLAIM_ENABLED */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Initialize ESP RainMaker */
|
||||
static esp_err_t esp_rmaker_init(const esp_rmaker_config_t *config, bool use_claiming)
|
||||
{
|
||||
if (esp_rmaker_priv_data) {
|
||||
ESP_LOGE(TAG, "ESP RainMaker already initialised");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!config) {
|
||||
ESP_LOGE(TAG, "RainMaker config missing. Cannot initialise");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (esp_rmaker_factory_init() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialise storage");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_rmaker_priv_data = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_priv_data_t));
|
||||
if (!esp_rmaker_priv_data) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_rmaker_priv_data->node_id = esp_rmaker_populate_node_id(use_claiming);
|
||||
if (!esp_rmaker_priv_data->node_id) {
|
||||
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||
esp_rmaker_priv_data = NULL;
|
||||
ESP_LOGE(TAG, "Failed to initialise Node Id. Please perform \"claiming\" using RainMaker CLI.");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (esp_rmaker_work_queue_init() != ESP_OK) {
|
||||
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||
esp_rmaker_priv_data = NULL;
|
||||
ESP_LOGE(TAG, "ESP RainMaker Queue Creation Failed");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
#ifndef CONFIG_ESP_RMAKER_DISABLE_USER_MAPPING_PROV
|
||||
if (esp_rmaker_user_mapping_prov_init()) {
|
||||
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||
esp_rmaker_priv_data = NULL;
|
||||
ESP_LOGE(TAG, "Could not initialise User-Node mapping.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif /* !CONFIG_ESP_RMAKER_DISABLE_USER_MAPPING_PROV */
|
||||
if (esp_rmaker_mqtt_conn_params_init(esp_rmaker_priv_data, use_claiming) != ESP_OK) {
|
||||
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||
esp_rmaker_priv_data = NULL;
|
||||
ESP_LOGE(TAG, "Failed to initialise MQTT Params. Please perform \"claiming\" using RainMaker CLI.");
|
||||
return ESP_FAIL;
|
||||
} else {
|
||||
#ifdef ESP_RMAKER_CLAIM_ENABLED
|
||||
if (!esp_rmaker_priv_data->need_claim)
|
||||
#endif /* ESP_RMAKER_CLAIM_ENABLED */
|
||||
{
|
||||
if (esp_rmaker_mqtt_init(esp_rmaker_priv_data->mqtt_conn_params) != ESP_OK) {
|
||||
esp_rmaker_deinit_priv_data(esp_rmaker_priv_data);
|
||||
esp_rmaker_priv_data = NULL;
|
||||
ESP_LOGE(TAG, "Failed to initialise MQTT");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
esp_rmaker_user_node_mapping_init();
|
||||
#ifdef CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE
|
||||
esp_rmaker_init_local_ctrl_service();
|
||||
#endif
|
||||
esp_rmaker_priv_data->enable_time_sync = config->enable_time_sync;
|
||||
esp_rmaker_post_event(RMAKER_EVENT_INIT_DONE, NULL, 0);
|
||||
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_INIT_DONE;
|
||||
|
||||
/* Adding the RainMaker Task to the queue so that it is will be the first function
|
||||
* to be executed when the Work Queue task begins.
|
||||
*/
|
||||
esp_rmaker_work_queue_add_task(esp_rmaker_task, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_register_node(const esp_rmaker_node_t *node)
|
||||
{
|
||||
ESP_RMAKER_CHECK_HANDLE(ESP_ERR_INVALID_STATE);
|
||||
if (esp_rmaker_priv_data->node) {
|
||||
ESP_LOGE(TAG, "A node has already been registered. Cannot register another.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!node) {
|
||||
ESP_LOGE(TAG, "Node handle cannot be NULL.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_rmaker_priv_data->node = node;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_rmaker_node_t *esp_rmaker_node_init(const esp_rmaker_config_t *config, const char *name, const char *type)
|
||||
{
|
||||
esp_err_t err = esp_rmaker_init(config, true);
|
||||
if (err != ESP_OK) {
|
||||
return NULL;
|
||||
}
|
||||
esp_rmaker_node_t *node = esp_rmaker_node_create(name, type);
|
||||
if (!node) {
|
||||
ESP_LOGE(TAG, "Failed to create node");
|
||||
return NULL;
|
||||
}
|
||||
err = esp_rmaker_register_node(node);
|
||||
if (err != ESP_OK) {
|
||||
free(node);
|
||||
return NULL;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
const esp_rmaker_node_t *esp_rmaker_get_node()
|
||||
{
|
||||
ESP_RMAKER_CHECK_HANDLE(NULL);
|
||||
return esp_rmaker_priv_data->node;
|
||||
}
|
||||
|
||||
/* Start the ESP RainMaker Core Task */
|
||||
esp_err_t esp_rmaker_start(void)
|
||||
{
|
||||
ESP_RMAKER_CHECK_HANDLE(ESP_ERR_INVALID_STATE);
|
||||
if (esp_rmaker_priv_data->enable_time_sync) {
|
||||
esp_rmaker_time_sync_init(NULL);
|
||||
}
|
||||
ESP_LOGI(TAG, "Starting RainMaker Work Queue task");
|
||||
if (esp_rmaker_work_queue_start() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Couldn't create RainMaker Work Queue task");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, &reset_event_handler, NULL));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_stop()
|
||||
{
|
||||
ESP_RMAKER_CHECK_HANDLE(ESP_ERR_INVALID_STATE);
|
||||
esp_rmaker_priv_data->state = ESP_RMAKER_STATE_STOP_REQUESTED;
|
||||
return ESP_OK;
|
||||
}
|
||||
379
components/esp_rainmaker/src/core/esp_rmaker_device.c
Normal file
379
components/esp_rainmaker/src/core/esp_rmaker_device.c
Normal file
@@ -0,0 +1,379 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <sdkconfig.h>
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_standard_types.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
#include "esp_rmaker_internal.h"
|
||||
|
||||
static const char *TAG = "esp_rmaker_device";
|
||||
|
||||
esp_err_t esp_rmaker_device_delete(const esp_rmaker_device_t *device)
|
||||
{
|
||||
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||
if (_device) {
|
||||
if (_device->parent) {
|
||||
ESP_LOGE(TAG, "Cannot delete device as it is part of a node. Remove it from the node first.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_rmaker_attr_t *attr = _device->attributes;
|
||||
while (attr) {
|
||||
esp_rmaker_attr_t *next_attr = attr->next;
|
||||
esp_rmaker_attribute_delete(attr);
|
||||
attr = next_attr;
|
||||
}
|
||||
_esp_rmaker_param_t *param = _device->params;
|
||||
while (param) {
|
||||
_esp_rmaker_param_t *next_param = param->next;
|
||||
esp_rmaker_param_delete((esp_rmaker_param_t *)param);
|
||||
param = next_param;
|
||||
}
|
||||
if (_device->subtype) {
|
||||
free(_device->subtype);
|
||||
}
|
||||
if (_device->model) {
|
||||
free(_device->model);
|
||||
}
|
||||
if (_device->name) {
|
||||
free(_device->name);
|
||||
}
|
||||
if (_device->type) {
|
||||
free(_device->type);
|
||||
}
|
||||
free(_device);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_default_bulk_write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_write_req_t write_req[],
|
||||
uint8_t count, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||
{
|
||||
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||
_esp_rmaker_param_t *param;
|
||||
if (_device) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
param = (_esp_rmaker_param_t *)(write_req[i].param);
|
||||
if (param->type && (strcmp(param->type, ESP_RMAKER_PARAM_NAME) == 0)) {
|
||||
#ifndef CONFIG_RMAKER_NAME_PARAM_CB
|
||||
esp_rmaker_param_update(write_req[i].param, write_req[i].val);
|
||||
continue;
|
||||
#else
|
||||
if (!_device->write_cb) {
|
||||
esp_rmaker_param_update(write_req[i].param, write_req[i].val);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (_device->write_cb) {
|
||||
if (_device->write_cb(device, write_req[i].param, write_req[i].val, priv_data, ctx) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Remote update to param %s - %s failed", _device->name, ((_esp_rmaker_param_t *)(write_req[i].param))->name);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "No write callback for device %s", _device->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_rmaker_device_t *__esp_rmaker_device_create(const char *name, const char *type, void *priv, bool is_service)
|
||||
{
|
||||
if (!name) {
|
||||
ESP_LOGE(TAG, "%s name is mandatory", is_service ? "Service":"Device");
|
||||
return NULL;
|
||||
}
|
||||
_esp_rmaker_device_t *_device = MEM_CALLOC_EXTRAM(1, sizeof(_esp_rmaker_device_t));
|
||||
if (!_device) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for %s %s", is_service ? "Service":"Device", name);
|
||||
return NULL;
|
||||
}
|
||||
_device->name = strdup(name);
|
||||
if (!_device->name) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for name for %s %s", is_service ? "Service":"Device", name);
|
||||
goto device_create_err;
|
||||
}
|
||||
if (type) {
|
||||
_device->type = strdup(type);
|
||||
if (!_device->type) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for type for %s %s", is_service ? "Service":"Device", name);
|
||||
goto device_create_err;
|
||||
}
|
||||
}
|
||||
_device->priv_data = priv;
|
||||
_device->is_service = is_service;
|
||||
/* Adding a default bulk write callback for backward compatibility with application code using single param write callback */
|
||||
_device->bulk_write_cb = esp_rmaker_default_bulk_write_cb;
|
||||
|
||||
return (esp_rmaker_device_t *)_device;
|
||||
|
||||
device_create_err:
|
||||
esp_rmaker_device_delete((esp_rmaker_device_t *)_device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_device_create(const char *name, const char *type, void *priv)
|
||||
{
|
||||
return __esp_rmaker_device_create(name, type, priv, false);
|
||||
}
|
||||
esp_rmaker_device_t *esp_rmaker_service_create(const char *name, const char *type, void *priv)
|
||||
{
|
||||
return __esp_rmaker_device_create(name, type, priv, true);
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_device_add_param(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param)
|
||||
{
|
||||
if (!device || !param) {
|
||||
ESP_LOGE(TAG, "Device or Param handle cannot be NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||
_esp_rmaker_param_t *_new_param = (_esp_rmaker_param_t *)param;
|
||||
|
||||
_esp_rmaker_param_t *_param = _device->params;
|
||||
while(_param) {
|
||||
if (strcmp(_param->name, _new_param->name) == 0) {
|
||||
ESP_LOGE(TAG, "Parameter with name %s already exists in Device %s", _new_param->name, _device->name);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (_param->next) {
|
||||
_param = _param->next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_new_param->parent = _device;
|
||||
if (_param) {
|
||||
_param->next = _new_param;
|
||||
} else {
|
||||
_device->params = _new_param;
|
||||
}
|
||||
_device->param_count++;
|
||||
/* We check the stored value here, and not during param creation, because a parameter
|
||||
* in itself isn't unique. However, it is unique within a given device and hence can
|
||||
* be uniquely represented in storage only when added to a device.
|
||||
*/
|
||||
esp_rmaker_param_val_t stored_val;
|
||||
stored_val.type = _new_param->val.type;
|
||||
if (_new_param->prop_flags & PROP_FLAG_PERSIST) {
|
||||
if (esp_rmaker_param_get_stored_value(_new_param, &stored_val) == ESP_OK) {
|
||||
if ((_new_param->val.type == RMAKER_VAL_TYPE_STRING) || (_new_param->val.type == RMAKER_VAL_TYPE_OBJECT)
|
||||
|| (_new_param->val.type == RMAKER_VAL_TYPE_ARRAY)) {
|
||||
if (_new_param->val.val.s) {
|
||||
free(_new_param->val.val.s);
|
||||
}
|
||||
}
|
||||
_new_param->val = stored_val;
|
||||
/* The device callback should be invoked once with the stored value, so
|
||||
* that applications can do initialisations as required.
|
||||
*/
|
||||
if (_device->bulk_write_cb) {
|
||||
/* However, the callback should be invoked, only if the parameter is not
|
||||
* of type ESP_RMAKER_PARAM_NAME, as it has special handling internally.
|
||||
*/
|
||||
if (!(_new_param->type && strcmp(_new_param->type, ESP_RMAKER_PARAM_NAME) == 0)) {
|
||||
esp_rmaker_write_ctx_t ctx = {
|
||||
.src = ESP_RMAKER_REQ_SRC_INIT,
|
||||
};
|
||||
esp_rmaker_param_write_req_t write_req = {
|
||||
.param = (esp_rmaker_param_t *)param,
|
||||
.val = stored_val,
|
||||
};
|
||||
_device->bulk_write_cb(device, &write_req, 1, _device->priv_data, &ctx);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
esp_rmaker_param_store_value(_new_param);
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "Param %s added in %s", _new_param->name, _device->name);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Add a new Device Attribute */
|
||||
esp_err_t esp_rmaker_device_add_attribute(const esp_rmaker_device_t *device, const char *attr_name, const char *val)
|
||||
{
|
||||
if (!device || !attr_name || !val) {
|
||||
ESP_LOGE(TAG, "Device handle, attribute name or value cannot be NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
_esp_rmaker_device_t *_device = ( _esp_rmaker_device_t *)device;
|
||||
esp_rmaker_attr_t *attr = _device->attributes;
|
||||
while(attr) {
|
||||
if (strcmp(attr_name, attr->name) == 0) {
|
||||
ESP_LOGE(TAG, "Attribute with name %s already exists in Device %s", attr_name, _device->name);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (attr->next) {
|
||||
attr = attr->next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
esp_rmaker_attr_t *new_attr = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_attr_t));
|
||||
if (!new_attr) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for device attribute");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
new_attr->name = strdup(attr_name);
|
||||
new_attr->value = strdup(val);
|
||||
if (!new_attr->name || !new_attr->value) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for device attribute name or value");
|
||||
esp_rmaker_attribute_delete(new_attr);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (attr) {
|
||||
attr->next = new_attr;
|
||||
} else {
|
||||
_device->attributes = new_attr;
|
||||
}
|
||||
ESP_LOGD(TAG, "Device attribute %s.%s added", _device->name, attr_name);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Add a device subtype */
|
||||
esp_err_t esp_rmaker_device_add_subtype(const esp_rmaker_device_t *device, const char *subtype)
|
||||
{
|
||||
if (!device || !subtype) {
|
||||
ESP_LOGE(TAG, "Device handle or subtype cannot be NULL.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||
if (_device->subtype) {
|
||||
free(_device->subtype);
|
||||
}
|
||||
if ((_device->subtype = strdup(subtype)) != NULL ){
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for device subtype");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a device model */
|
||||
esp_err_t esp_rmaker_device_add_model(const esp_rmaker_device_t *device, const char *model)
|
||||
{
|
||||
if (!device || !model) {
|
||||
ESP_LOGE(TAG, "Device handle or model cannot be NULL.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||
if (_device->model) {
|
||||
free(_device->model);
|
||||
}
|
||||
if ((_device->model = strdup(model)) != NULL ){
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for device model");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_device_assign_primary_param(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param)
|
||||
{
|
||||
if (!device || !param) {
|
||||
ESP_LOGE(TAG,"Device or Param handle cannot be NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
((_esp_rmaker_device_t *)device)->primary = (_esp_rmaker_param_t *)param;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_device_add_cb(const esp_rmaker_device_t *device, esp_rmaker_device_write_cb_t write_cb, esp_rmaker_device_read_cb_t read_cb)
|
||||
{
|
||||
if (!device) {
|
||||
ESP_LOGE(TAG, "Device handle cannot be NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||
_device->write_cb = write_cb;
|
||||
_device->read_cb = read_cb;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_device_add_bulk_cb(const esp_rmaker_device_t *device, esp_rmaker_device_bulk_write_cb_t write_cb,
|
||||
esp_rmaker_device_bulk_read_cb_t read_cb)
|
||||
{
|
||||
if (!device) {
|
||||
ESP_LOGE(TAG, "Device handle cannot be NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||
_device->bulk_write_cb = write_cb;
|
||||
_device->bulk_read_cb = read_cb;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
char *esp_rmaker_device_get_name(const esp_rmaker_device_t *device)
|
||||
{
|
||||
if (!device) {
|
||||
ESP_LOGE(TAG, "Device handle cannot be NULL.");
|
||||
return NULL;
|
||||
}
|
||||
return ((_esp_rmaker_device_t *)device)->name;
|
||||
}
|
||||
|
||||
void *esp_rmaker_device_get_priv_data(const esp_rmaker_device_t *device)
|
||||
{
|
||||
if (!device) {
|
||||
ESP_LOGE(TAG, "Device handle cannot be NULL.");
|
||||
return NULL;
|
||||
}
|
||||
return ((_esp_rmaker_device_t *)device)->priv_data;
|
||||
}
|
||||
|
||||
char *esp_rmaker_device_get_type(const esp_rmaker_device_t *device)
|
||||
{
|
||||
if (!device) {
|
||||
ESP_LOGE(TAG, "Device handle cannot be NULL.");
|
||||
return NULL;
|
||||
}
|
||||
return ((_esp_rmaker_device_t *)device)->type;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_device_get_param_by_type(const esp_rmaker_device_t *device, const char *param_type)
|
||||
{
|
||||
if (!device || !param_type) {
|
||||
ESP_LOGE(TAG, "Device handle or param type cannot be NULL");
|
||||
return NULL;
|
||||
}
|
||||
_esp_rmaker_param_t *param = ((_esp_rmaker_device_t *)device)->params;
|
||||
while(param) {
|
||||
if (strcmp(param->type, param_type) == 0) {
|
||||
break;
|
||||
}
|
||||
param = param->next;
|
||||
}
|
||||
return (esp_rmaker_param_t *)param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_device_get_param_by_name(const esp_rmaker_device_t *device, const char *param_name)
|
||||
{
|
||||
if (!device || !param_name) {
|
||||
ESP_LOGE(TAG, "Device handle or param name cannot be NULL");
|
||||
return NULL;
|
||||
}
|
||||
_esp_rmaker_param_t *param = ((_esp_rmaker_device_t *)device)->params;
|
||||
while(param) {
|
||||
if (strcmp(param->name, param_name) == 0) {
|
||||
break;
|
||||
}
|
||||
param = param->next;
|
||||
}
|
||||
return (esp_rmaker_param_t *)param;
|
||||
}
|
||||
129
components/esp_rainmaker/src/core/esp_rmaker_internal.h
Normal file
129
components/esp_rainmaker/src/core/esp_rmaker_internal.h
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <json_generator.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_idf_version.h>
|
||||
|
||||
#define RMAKER_PARAM_FLAG_VALUE_CHANGE (1 << 0)
|
||||
#define RMAKER_PARAM_FLAG_VALUE_NOTIFY (1 << 1)
|
||||
#define ESP_RMAKER_NVS_PART_NAME "nvs"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) && defined(CONFIG_ESP_RMAKER_USING_NETWORK_PROV)
|
||||
#define RMAKER_USING_NETWORK_PROV 1
|
||||
#else
|
||||
#define RMAKER_USING_NETWORK_PROV 0
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ESP_RMAKER_STATE_DEINIT = 0,
|
||||
ESP_RMAKER_STATE_INIT_DONE,
|
||||
ESP_RMAKER_STATE_STARTING,
|
||||
ESP_RMAKER_STATE_STARTED,
|
||||
ESP_RMAKER_STATE_STOP_REQUESTED,
|
||||
} esp_rmaker_state_t;
|
||||
|
||||
typedef struct {
|
||||
esp_rmaker_param_val_t min;
|
||||
esp_rmaker_param_val_t max;
|
||||
esp_rmaker_param_val_t step;
|
||||
} esp_rmaker_param_bounds_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t str_list_cnt;
|
||||
const char **str_list;
|
||||
} esp_rmaker_param_valid_str_list_t;
|
||||
|
||||
struct esp_rmaker_param {
|
||||
char *name;
|
||||
char *type;
|
||||
uint8_t flags;
|
||||
uint8_t prop_flags;
|
||||
char *ui_type;
|
||||
esp_rmaker_param_val_t val;
|
||||
esp_rmaker_param_bounds_t *bounds;
|
||||
esp_rmaker_param_valid_str_list_t *valid_str_list;
|
||||
struct esp_rmaker_device *parent;
|
||||
struct esp_rmaker_param * next;
|
||||
};
|
||||
typedef struct esp_rmaker_param _esp_rmaker_param_t;
|
||||
|
||||
struct esp_rmaker_attr {
|
||||
char *name;
|
||||
char *value;
|
||||
struct esp_rmaker_attr *next;
|
||||
};
|
||||
typedef struct esp_rmaker_attr esp_rmaker_attr_t;
|
||||
|
||||
|
||||
struct esp_rmaker_device {
|
||||
char *name;
|
||||
char *type;
|
||||
char *subtype;
|
||||
char *model;
|
||||
uint8_t param_count;
|
||||
esp_rmaker_device_write_cb_t write_cb;
|
||||
esp_rmaker_device_read_cb_t read_cb;
|
||||
esp_rmaker_device_bulk_write_cb_t bulk_write_cb;
|
||||
esp_rmaker_device_bulk_read_cb_t bulk_read_cb;
|
||||
void *priv_data;
|
||||
bool is_service;
|
||||
esp_rmaker_attr_t *attributes;
|
||||
_esp_rmaker_param_t *params;
|
||||
_esp_rmaker_param_t *primary;
|
||||
const esp_rmaker_node_t *parent;
|
||||
struct esp_rmaker_device *next;
|
||||
};
|
||||
typedef struct esp_rmaker_device _esp_rmaker_device_t;
|
||||
|
||||
typedef struct {
|
||||
char *node_id;
|
||||
esp_rmaker_node_info_t *info;
|
||||
esp_rmaker_attr_t *attributes;
|
||||
_esp_rmaker_device_t *devices;
|
||||
} _esp_rmaker_node_t;
|
||||
|
||||
esp_rmaker_node_t *esp_rmaker_node_create(const char *name, const char *type);
|
||||
esp_err_t esp_rmaker_change_node_id(char *node_id, size_t len);
|
||||
esp_err_t esp_rmaker_report_value(const esp_rmaker_param_val_t *val, char *key, json_gen_str_t *jptr);
|
||||
esp_err_t esp_rmaker_report_data_type(esp_rmaker_val_type_t type, char *data_type_key, json_gen_str_t *jptr);
|
||||
esp_err_t esp_rmaker_report_node_config(void);
|
||||
esp_err_t esp_rmaker_report_node_state(void);
|
||||
_esp_rmaker_device_t *esp_rmaker_node_get_first_device(const esp_rmaker_node_t *node);
|
||||
esp_rmaker_attr_t *esp_rmaker_node_get_first_attribute(const esp_rmaker_node_t *node);
|
||||
esp_err_t esp_rmaker_params_mqtt_init(void);
|
||||
esp_err_t esp_rmaker_param_get_stored_value(_esp_rmaker_param_t *param, esp_rmaker_param_val_t *val);
|
||||
esp_err_t esp_rmaker_param_store_value(_esp_rmaker_param_t *param);
|
||||
esp_err_t esp_rmaker_node_delete(const esp_rmaker_node_t *node);
|
||||
esp_err_t esp_rmaker_param_delete(const esp_rmaker_param_t *param);
|
||||
esp_err_t esp_rmaker_attribute_delete(esp_rmaker_attr_t *attr);
|
||||
char *esp_rmaker_get_node_config(void);
|
||||
char *esp_rmaker_get_node_params(void);
|
||||
esp_err_t esp_rmaker_handle_set_params(char *data, size_t data_len, esp_rmaker_req_src_t src);
|
||||
esp_err_t esp_rmaker_user_mapping_prov_init(void);
|
||||
esp_err_t esp_rmaker_user_mapping_prov_deinit(void);
|
||||
esp_err_t esp_rmaker_user_node_mapping_init(void);
|
||||
esp_err_t esp_rmaker_user_node_mapping_deinit(void);
|
||||
esp_err_t esp_rmaker_reset_user_node_mapping(void);
|
||||
esp_err_t esp_rmaker_init_local_ctrl_service(void);
|
||||
esp_err_t esp_rmaker_start_local_ctrl_service(const char *serv_name);
|
||||
static inline esp_err_t esp_rmaker_post_event(esp_rmaker_event_t event_id, void* data, size_t data_size)
|
||||
{
|
||||
return esp_event_post(RMAKER_EVENT, event_id, data, data_size, portMAX_DELAY);
|
||||
}
|
||||
esp_rmaker_state_t esp_rmaker_get_state(void);
|
||||
esp_err_t esp_rmaker_cmd_response_enable(void);
|
||||
650
components/esp_rainmaker/src/core/esp_rmaker_local_ctrl.c
Normal file
650
components/esp_rainmaker/src/core/esp_rmaker_local_ctrl.c
Normal file
@@ -0,0 +1,650 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <esp_log.h>
|
||||
#include <nvs.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_local_ctrl.h>
|
||||
#include <esp_rmaker_internal.h>
|
||||
#include <esp_rmaker_standard_services.h>
|
||||
#include <esp_https_server.h>
|
||||
#include <esp_rmaker_work_queue.h>
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
#include <mdns.h>
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
#include <esp_openthread.h>
|
||||
#include <esp_openthread_lock.h>
|
||||
#include <openthread/srp_client.h>
|
||||
#include <openthread/srp_client_buffers.h>
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
#include <esp_idf_version.h>
|
||||
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
#include <network_provisioning/manager.h>
|
||||
#else
|
||||
#include <wifi_provisioning/manager.h>
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
// Features supported in 4.2
|
||||
|
||||
#define ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE CONFIG_ESP_RMAKER_LOCAL_CTRL_SECURITY
|
||||
|
||||
#else
|
||||
|
||||
#if CONFIG_ESP_RMAKER_LOCAL_CTRL_SECURITY != 0
|
||||
#warning "Local control security type is not supported in idf versions below 4.2. Using sec0 by default."
|
||||
#endif
|
||||
#define ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE 0
|
||||
|
||||
#endif /* !IDF4.2 */
|
||||
|
||||
static const char * TAG = "esp_rmaker_local";
|
||||
|
||||
/* Random Port number that will be used by the local control http instance
|
||||
* for internal control communication.
|
||||
*/
|
||||
#define ESP_RMAKER_LOCAL_CTRL_DEVICE_NAME "Local Control"
|
||||
#define ESP_RMAKER_LOCAL_CTRL_HTTP_CTRL_PORT 12312
|
||||
#define ESP_RMAKER_NVS_PART_NAME "nvs"
|
||||
#define ESP_RMAKER_NVS_LOCAL_CTRL_NAMESPACE "local_ctrl"
|
||||
#define ESP_RMAKER_NVS_LOCAL_CTRL_POP "pop"
|
||||
#define ESP_RMAKER_POP_LEN 9
|
||||
|
||||
/* Custom allowed property types */
|
||||
enum property_types {
|
||||
PROP_TYPE_NODE_CONFIG = 1,
|
||||
PROP_TYPE_NODE_PARAMS,
|
||||
};
|
||||
|
||||
/* Custom flags that can be set for a property */
|
||||
enum property_flags {
|
||||
PROP_FLAG_READONLY = (1 << 0)
|
||||
};
|
||||
|
||||
static bool g_local_ctrl_is_started = false;
|
||||
|
||||
static char *g_serv_name;
|
||||
static bool wait_for_provisioning;
|
||||
/********* Handler functions for responding to control requests / commands *********/
|
||||
|
||||
static esp_err_t get_property_values(size_t props_count,
|
||||
const esp_local_ctrl_prop_t props[],
|
||||
esp_local_ctrl_prop_val_t prop_values[],
|
||||
void *usr_ctx)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint32_t i;
|
||||
for (i = 0; i < props_count && ret == ESP_OK ; i++) {
|
||||
ESP_LOGD(TAG, "(%"PRIu32") Reading property : %s", i, props[i].name);
|
||||
switch (props[i].type) {
|
||||
case PROP_TYPE_NODE_CONFIG: {
|
||||
char *node_config = esp_rmaker_get_node_config();
|
||||
if (!node_config) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for %s", props[i].name);
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
} else {
|
||||
prop_values[i].size = strlen(node_config);
|
||||
prop_values[i].data = node_config;
|
||||
prop_values[i].free_fn = free;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROP_TYPE_NODE_PARAMS: {
|
||||
char *node_params = esp_rmaker_get_node_params();
|
||||
if (!node_params) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for %s", props[i].name);
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
} else {
|
||||
prop_values[i].size = strlen(node_params);
|
||||
prop_values[i].data = node_params;
|
||||
prop_values[i].free_fn = free;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret != ESP_OK) {
|
||||
for (uint32_t j = 0; j <= i; j++) {
|
||||
if (prop_values[j].free_fn) {
|
||||
ESP_LOGI(TAG, "Freeing memory for %s", props[j].name);
|
||||
prop_values[j].free_fn(prop_values[j].data);
|
||||
prop_values[j].free_fn = NULL;
|
||||
prop_values[j].data = NULL;
|
||||
prop_values[j].size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t set_property_values(size_t props_count,
|
||||
const esp_local_ctrl_prop_t props[],
|
||||
const esp_local_ctrl_prop_val_t prop_values[],
|
||||
void *usr_ctx)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint32_t i;
|
||||
/* First check if any of the properties are read-only properties. If found, just abort */
|
||||
for (i = 0; i < props_count; i++) {
|
||||
/* Cannot set the value of a read-only property */
|
||||
if (props[i].flags & PROP_FLAG_READONLY) {
|
||||
ESP_LOGE(TAG, "%s is read-only", props[i].name);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < props_count && ret == ESP_OK; i++) {
|
||||
switch (props[i].type) {
|
||||
case PROP_TYPE_NODE_PARAMS:
|
||||
ret = esp_rmaker_handle_set_params((char *)prop_values[i].data,
|
||||
prop_values[i].size, ESP_RMAKER_REQ_SRC_LOCAL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *__esp_rmaker_local_ctrl_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_LOCAL_CTRL_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_local_ctrl_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_LOCAL_CTRL_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;
|
||||
}
|
||||
|
||||
static char *esp_rmaker_local_ctrl_get_pop()
|
||||
{
|
||||
char *pop = __esp_rmaker_local_ctrl_get_nvs(ESP_RMAKER_NVS_LOCAL_CTRL_POP);
|
||||
if (pop) {
|
||||
return pop;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Couldn't find POP in NVS. Generating a new one.");
|
||||
pop = (char *)MEM_CALLOC_EXTRAM(1, ESP_RMAKER_POP_LEN);
|
||||
if (!pop) {
|
||||
ESP_LOGE(TAG, "Couldn't allocate POP");
|
||||
return NULL;
|
||||
}
|
||||
uint8_t random_bytes[ESP_RMAKER_POP_LEN] = {0};
|
||||
esp_fill_random(&random_bytes, sizeof(random_bytes));
|
||||
snprintf(pop, ESP_RMAKER_POP_LEN, "%02x%02x%02x%02x", random_bytes[0], random_bytes[1], random_bytes[2], random_bytes[3]);
|
||||
|
||||
__esp_rmaker_local_ctrl_set_nvs(ESP_RMAKER_NVS_LOCAL_CTRL_POP, pop);
|
||||
return pop;
|
||||
}
|
||||
|
||||
static int esp_rmaker_local_ctrl_get_security_type()
|
||||
{
|
||||
return ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_local_ctrl_service_enable(void)
|
||||
{
|
||||
char *pop_str = esp_rmaker_local_ctrl_get_pop();
|
||||
if (!pop_str) {
|
||||
ESP_LOGE(TAG, "Get POP failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int sec_ver = esp_rmaker_local_ctrl_get_security_type();
|
||||
|
||||
esp_rmaker_device_t *local_ctrl_service = esp_rmaker_create_local_control_service(ESP_RMAKER_LOCAL_CTRL_DEVICE_NAME, pop_str, sec_ver, NULL);;
|
||||
if (!local_ctrl_service) {
|
||||
ESP_LOGE(TAG, "Failed to create Local Control Service.");
|
||||
free(pop_str);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
free(pop_str);
|
||||
|
||||
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), local_ctrl_service);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to add Local Control Service to Node. err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
#ifndef CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE
|
||||
/* Report node details only if the local control is not enabled via config option,
|
||||
* but instead via an API call.
|
||||
*/
|
||||
err = esp_rmaker_report_node_details();
|
||||
#endif
|
||||
ESP_LOGI(TAG, "Local Control Service Enabled");
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_local_ctrl_service_disable(void)
|
||||
{
|
||||
const esp_rmaker_node_t *node = esp_rmaker_get_node();
|
||||
esp_rmaker_device_t *local_ctrl_service = esp_rmaker_node_get_device_by_name(node, ESP_RMAKER_LOCAL_CTRL_DEVICE_NAME);
|
||||
esp_err_t err = esp_rmaker_node_remove_device(node, local_ctrl_service);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to remove Local Control Service from Node. err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
err = esp_rmaker_device_delete(local_ctrl_service);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to delete Local Control Service. err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
#ifndef CONFIG_ESP_RMAKER_LOCAL_CTRL_ENABLE
|
||||
/* Report node details only if the local control is not enabled via config option,
|
||||
* but instead via an API call.
|
||||
*/
|
||||
err = esp_rmaker_report_node_details();
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
#define SRP_MAX_HOST_NAME_LEN 32
|
||||
static char srp_host_name[SRP_MAX_HOST_NAME_LEN + 1];
|
||||
|
||||
static esp_err_t srp_client_set_host(const char *host_name)
|
||||
{
|
||||
if (!host_name || strlen(host_name) > 15) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
// Avoid adding the same host name multiple times
|
||||
if (strcmp(srp_host_name, host_name) != 0) {
|
||||
strncpy(srp_host_name, host_name, SRP_MAX_HOST_NAME_LEN);
|
||||
srp_host_name[strnlen(host_name, SRP_MAX_HOST_NAME_LEN)] = 0;
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
otInstance *instance = esp_openthread_get_instance();
|
||||
if (otSrpClientSetHostName(instance, srp_host_name) != OT_ERROR_NONE) {
|
||||
esp_openthread_lock_release();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (otSrpClientEnableAutoHostAddress(instance) != OT_ERROR_NONE) {
|
||||
esp_openthread_lock_release();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_openthread_lock_release();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t srp_client_add_local_ctrl_service(const char *serv_name)
|
||||
{
|
||||
static uint8_t rainmaker_node_id_txt_value[30];
|
||||
char *rmaker_node_id = esp_rmaker_get_node_id();
|
||||
if (rmaker_node_id == NULL || strlen(rmaker_node_id) > sizeof(rainmaker_node_id_txt_value)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
memcpy(rainmaker_node_id_txt_value, rmaker_node_id, strlen(rmaker_node_id));
|
||||
const static uint8_t text_values[3][23] = {
|
||||
{'/', 'e', 's', 'p', '_', 'l', 'o', 'c', 'a', 'l', '_', 'c', 't', 'r', 'l', '/', 'v', 'e', 'r', 's', 'i', 'o', 'n'},
|
||||
{'/', 'e', 's', 'p', '_', 'l', 'o', 'c', 'a', 'l', '_', 'c', 't', 'r', 'l', '/', 's', 'e', 's', 's', 'i', 'o', 'n'},
|
||||
{'/', 'e', 's', 'p', '_', 'l', 'o', 'c', 'a', 'l', '_', 'c', 't', 'r', 'l', '/', 'c', 'o', 'n', 't', 'r', 'o', 'l'}};
|
||||
static otDnsTxtEntry txt_entries[4] = {
|
||||
{
|
||||
.mKey = "version_endpoint",
|
||||
.mValue = text_values[0],
|
||||
.mValueLength = 23,
|
||||
},
|
||||
{
|
||||
.mKey = "session_endpoint",
|
||||
.mValue = text_values[1],
|
||||
.mValueLength = 23,
|
||||
},
|
||||
{
|
||||
.mKey = "control_endpoint",
|
||||
.mValue = text_values[2],
|
||||
.mValueLength = 23,
|
||||
},
|
||||
{
|
||||
.mKey = "node_id",
|
||||
.mValue = rainmaker_node_id_txt_value,
|
||||
.mValueLength = sizeof(rainmaker_node_id_txt_value),
|
||||
}
|
||||
};
|
||||
txt_entries[3].mValueLength = (uint16_t)strlen(rmaker_node_id);
|
||||
static char s_serv_name[30];
|
||||
strncpy(s_serv_name, serv_name, strnlen(serv_name, sizeof(s_serv_name) - 1));
|
||||
s_serv_name[strnlen(serv_name, sizeof(s_serv_name) - 1)] = 0;
|
||||
static otSrpClientService srp_client_service = {
|
||||
.mName = "_esp_local_ctrl._tcp",
|
||||
.mInstanceName = (const char*)s_serv_name,
|
||||
.mTxtEntries = txt_entries,
|
||||
.mPort = CONFIG_ESP_RMAKER_LOCAL_CTRL_HTTP_PORT,
|
||||
.mNumTxtEntries = 4,
|
||||
.mNext = NULL,
|
||||
.mLease = 0,
|
||||
.mKeyLease = 0,
|
||||
};
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
otInstance *instance = esp_openthread_get_instance();
|
||||
// Try to remove the service registered before adding a new service. If the previous service is not removed,
|
||||
// Adding service will fail with a duplicated instance error. This could happen when the device reboots, which
|
||||
// might result in the wrong resolved IP addresss on the phone app side.
|
||||
(void)otSrpClientRemoveService(instance, &srp_client_service);
|
||||
if (otSrpClientAddService(instance, &srp_client_service) != OT_ERROR_NONE) {
|
||||
esp_openthread_lock_release();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
otSrpClientEnableAutoStartMode(instance, NULL, NULL);
|
||||
esp_openthread_lock_release();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t srp_client_clean_up()
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
otInstance *instance = esp_openthread_get_instance();
|
||||
if (otSrpClientRemoveHostAndServices(instance, false, true) != OT_ERROR_NONE) {
|
||||
ret = ESP_FAIL;
|
||||
}
|
||||
memset(srp_host_name, 0, sizeof(srp_host_name));
|
||||
esp_openthread_lock_release();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||
|
||||
static esp_err_t __esp_rmaker_start_local_ctrl_service(const char *serv_name)
|
||||
{
|
||||
if (!serv_name) {
|
||||
ESP_LOGE(TAG, "Service name cannot be empty.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Starting ESP Local control with HTTP Transport and security version: %d", esp_rmaker_local_ctrl_get_security_type());
|
||||
/* Set the configuration */
|
||||
static httpd_ssl_config_t https_conf = HTTPD_SSL_CONFIG_DEFAULT();
|
||||
https_conf.transport_mode = HTTPD_SSL_TRANSPORT_INSECURE;
|
||||
https_conf.port_insecure = CONFIG_ESP_RMAKER_LOCAL_CTRL_HTTP_PORT;
|
||||
https_conf.httpd.ctrl_port = ESP_RMAKER_LOCAL_CTRL_HTTP_CTRL_PORT;
|
||||
https_conf.httpd.stack_size = CONFIG_ESP_RMAKER_LOCAL_CTRL_STACK_SIZE;
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
mdns_init();
|
||||
mdns_hostname_set(serv_name);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
srp_client_set_host(serv_name);
|
||||
#endif
|
||||
|
||||
esp_local_ctrl_config_t config = {
|
||||
.transport = ESP_LOCAL_CTRL_TRANSPORT_HTTPD,
|
||||
.transport_config = {
|
||||
.httpd = &https_conf
|
||||
},
|
||||
.handlers = {
|
||||
/* User defined handler functions */
|
||||
.get_prop_values = get_property_values,
|
||||
.set_prop_values = set_property_values,
|
||||
.usr_ctx = NULL,
|
||||
.usr_ctx_free_fn = NULL
|
||||
},
|
||||
/* Maximum number of properties that may be set */
|
||||
.max_properties = 10
|
||||
};
|
||||
|
||||
/* If sec1, add security type details to the config */
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#define PROTOCOMM_SEC_DATA protocomm_security1_params_t
|
||||
#else
|
||||
#define PROTOCOMM_SEC_DATA protocomm_security_pop_t
|
||||
#endif /* ESP_IDF_VERSION */
|
||||
PROTOCOMM_SEC_DATA *pop = NULL;
|
||||
#if ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE == 1
|
||||
char *pop_str = esp_rmaker_local_ctrl_get_pop();
|
||||
/* Note: pop_str shouldn't be freed. If it gets freed, the pointer which is internally copied in esp_local_ctrl_start() will become invalid which would cause corruption. */
|
||||
|
||||
int sec_ver = esp_rmaker_local_ctrl_get_security_type();
|
||||
|
||||
if (sec_ver != 0 && pop_str) {
|
||||
pop = (PROTOCOMM_SEC_DATA *)MEM_CALLOC_EXTRAM(1, sizeof(PROTOCOMM_SEC_DATA));
|
||||
if (!pop) {
|
||||
ESP_LOGE(TAG, "Failed to allocate pop");
|
||||
free(pop_str);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
pop->data = (uint8_t *)pop_str;
|
||||
pop->len = strlen(pop_str);
|
||||
}
|
||||
|
||||
config.proto_sec.version = sec_ver;
|
||||
config.proto_sec.custom_handle = NULL;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
config.proto_sec.sec_params = pop;
|
||||
#else
|
||||
config.proto_sec.pop = pop;
|
||||
#endif /* ESP_IDF_VERSION */
|
||||
#endif
|
||||
|
||||
/* Start esp_local_ctrl service */
|
||||
ESP_ERROR_CHECK(esp_local_ctrl_start(&config));
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
/* The instance name of mdns service set by esp_local_ctrl_start is 'Local Control Service'.
|
||||
* We should ensure that each end-device should have an unique instance name.
|
||||
*/
|
||||
mdns_service_instance_name_set("_esp_local_ctrl", "_tcp", serv_name);
|
||||
/* Add node_id in mdns */
|
||||
mdns_service_txt_item_set("_esp_local_ctrl", "_tcp", "node_id", esp_rmaker_get_node_id());
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
srp_client_add_local_ctrl_service(serv_name);
|
||||
#endif
|
||||
|
||||
if (pop) {
|
||||
free(pop);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "esp_local_ctrl service started with name : %s", serv_name);
|
||||
|
||||
/* Create the Node Config property */
|
||||
esp_local_ctrl_prop_t node_config = {
|
||||
.name = "config",
|
||||
.type = PROP_TYPE_NODE_CONFIG,
|
||||
.size = 0,
|
||||
.flags = PROP_FLAG_READONLY,
|
||||
.ctx = NULL,
|
||||
.ctx_free_fn = NULL
|
||||
};
|
||||
|
||||
/* Create the Node Params property */
|
||||
esp_local_ctrl_prop_t node_params = {
|
||||
.name = "params",
|
||||
.type = PROP_TYPE_NODE_PARAMS,
|
||||
.size = 0,
|
||||
.flags = 0,
|
||||
.ctx = NULL,
|
||||
.ctx_free_fn = NULL
|
||||
};
|
||||
|
||||
/* Now register the properties */
|
||||
ESP_ERROR_CHECK(esp_local_ctrl_add_property(&node_config));
|
||||
ESP_ERROR_CHECK(esp_local_ctrl_add_property(&node_params));
|
||||
|
||||
/* update the global status */
|
||||
g_local_ctrl_is_started = true;
|
||||
esp_rmaker_post_event(RMAKER_EVENT_LOCAL_CTRL_STARTED, (void *)serv_name, strlen(serv_name) + 1);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void esp_rmaker_local_ctrl_prov_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
ESP_LOGI(TAG, "Event %"PRIu32, event_id);
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
if (event_base == NETWORK_PROV_EVENT) {
|
||||
#else
|
||||
if (event_base == WIFI_PROV_EVENT) {
|
||||
#endif
|
||||
switch (event_id) {
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
case NETWORK_PROV_START:
|
||||
#else
|
||||
case WIFI_PROV_START:
|
||||
#endif
|
||||
wait_for_provisioning = true;
|
||||
break;
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
case NETWORK_PROV_DEINIT:
|
||||
#else
|
||||
case WIFI_PROV_DEINIT:
|
||||
#endif
|
||||
if (wait_for_provisioning == true) {
|
||||
wait_for_provisioning = false;
|
||||
if (g_serv_name) {
|
||||
__esp_rmaker_start_local_ctrl_service(g_serv_name);
|
||||
free(g_serv_name);
|
||||
g_serv_name = NULL;
|
||||
}
|
||||
}
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||
#else
|
||||
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool esp_rmaker_local_ctrl_service_started(void)
|
||||
{
|
||||
return g_local_ctrl_is_started;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_init_local_ctrl_service(void)
|
||||
{
|
||||
/* ESP Local Control uses protocomm_httpd, which is also used by SoftAP Provisioning.
|
||||
* If local control is started before provisioning ends, it fails because only one protocomm_httpd
|
||||
* instance is allowed at a time.
|
||||
* So, we check for the NETWORK_PROV_START event, and if received, wait for the NETWORK_PROV_DEINIT
|
||||
* event before starting local control.
|
||||
* This would not be required in case of BLE Provisioning, but this code has no easy way of knowing
|
||||
* what provisioning transport is being used and hence this logic will come into picture for both,
|
||||
* SoftAP and BLE provisioning.
|
||||
*/
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
esp_event_handler_register(NETWORK_PROV_EVENT, NETWORK_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler, NULL);
|
||||
esp_event_handler_register(NETWORK_PROV_EVENT, NETWORK_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler, NULL);
|
||||
#else
|
||||
esp_event_handler_register(WIFI_PROV_EVENT, WIFI_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler, NULL);
|
||||
esp_event_handler_register(WIFI_PROV_EVENT, WIFI_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler, NULL);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_start_local_ctrl_service(const char *serv_name)
|
||||
{
|
||||
if (ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE == 1) {
|
||||
esp_rmaker_local_ctrl_service_enable();
|
||||
}
|
||||
|
||||
if (!wait_for_provisioning) {
|
||||
return __esp_rmaker_start_local_ctrl_service(serv_name);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Waiting for Wi-Fi provisioning to finish.");
|
||||
g_serv_name = strdup(serv_name);
|
||||
if (g_serv_name) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_local_ctrl_enable(void)
|
||||
{
|
||||
if (g_local_ctrl_is_started) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
ESP_LOGI(TAG, "Enabling Local Control");
|
||||
esp_rmaker_init_local_ctrl_service();
|
||||
esp_err_t err;
|
||||
if ((err = esp_rmaker_start_local_ctrl_service(esp_rmaker_get_node_id())) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to start Local Control Service. err=0x%x", err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_local_ctrl_disable(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Disabling Local Control");
|
||||
if (g_serv_name) {
|
||||
free(g_serv_name);
|
||||
g_serv_name = NULL;
|
||||
}
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||
#else
|
||||
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_START, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_DEINIT, &esp_rmaker_local_ctrl_prov_event_handler);
|
||||
#endif
|
||||
if (!g_local_ctrl_is_started) {
|
||||
return ESP_OK;
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
mdns_free();
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
srp_client_clean_up();
|
||||
#endif
|
||||
esp_err_t err = esp_local_ctrl_stop();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (ESP_RMAKER_LOCAL_CTRL_SECURITY_TYPE == 1) {
|
||||
err = esp_rmaker_local_ctrl_service_disable();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
/* update the global status */
|
||||
g_local_ctrl_is_started = false;
|
||||
esp_rmaker_post_event(RMAKER_EVENT_LOCAL_CTRL_STOPPED, NULL, 0);
|
||||
return err;
|
||||
}
|
||||
35
components/esp_rainmaker/src/core/esp_rmaker_mqtt_topics.h
Normal file
35
components/esp_rainmaker/src/core/esp_rmaker_mqtt_topics.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define NODE_CONFIG_TOPIC_RULE "esp_node_config"
|
||||
#define NODE_PARAMS_LOCAL_TOPIC_RULE "esp_set_params"
|
||||
#define NODE_PARAMS_LOCAL_INIT_RULE "esp_init_params"
|
||||
#define NODE_PARAMS_ALERT_TOPIC_RULE "esp_node_alert"
|
||||
#define USER_MAPPING_TOPIC_RULE "esp_user_node_mapping"
|
||||
#define OTAFETCH_TOPIC_RULE "esp_node_otafetch"
|
||||
#define OTASTATUS_TOPIC_RULE "esp_node_otastatus"
|
||||
#define TIME_SERIES_DATA_TOPIC_RULE "esp_ts_ingest"
|
||||
#define SIMPLE_TS_DATA_TOPIC_RULE "esp_simple_ts_ingest"
|
||||
#define CMD_RESP_TOPIC_RULE "esp_cmd_resp"
|
||||
|
||||
|
||||
#define USER_MAPPING_TOPIC_SUFFIX "user/mapping"
|
||||
#define NODE_PARAMS_LOCAL_TOPIC_SUFFIX "params/local"
|
||||
#define NODE_PARAMS_LOCAL_INIT_TOPIC_SUFFIX "params/local/init"
|
||||
#define NODE_PARAMS_REMOTE_TOPIC_SUFFIX "params/remote"
|
||||
#define TIME_SERIES_DATA_TOPIC_SUFFIX "tsdata"
|
||||
#define SIMPLE_TS_DATA_TOPIC_SUFFIX "simple_tsdata"
|
||||
#define NODE_PARAMS_ALERT_TOPIC_SUFFIX "alert"
|
||||
#define NODE_CONFIG_TOPIC_SUFFIX "config"
|
||||
#define OTAURL_TOPIC_SUFFIX "otaurl"
|
||||
#define OTAFETCH_TOPIC_SUFFIX "otafetch"
|
||||
#define OTASTATUS_TOPIC_SUFFIX "otastatus"
|
||||
#define CMD_RESP_TOPIC_SUFFIX "from-node"
|
||||
#define TO_NODE_TOPIC_SUFFIX "to-node"
|
||||
#define INSIGHTS_TOPIC_SUFFIX "diagnostics/from-node"
|
||||
|
||||
#define MQTT_TOPIC_BUFFER_SIZE 150
|
||||
375
components/esp_rainmaker/src/core/esp_rmaker_node.c
Normal file
375
components/esp_rainmaker/src/core/esp_rmaker_node.c
Normal file
@@ -0,0 +1,375 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <sdkconfig.h>
|
||||
#include <string.h>
|
||||
#include "esp_idf_version.h"
|
||||
#include <esp_log.h>
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include <esp_app_desc.h>
|
||||
#else
|
||||
#include <esp_ota_ops.h>
|
||||
#endif
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_secure_boot_digest.h>
|
||||
|
||||
#include "esp_rmaker_internal.h"
|
||||
|
||||
static const char *TAG = "esp_rmaker_node";
|
||||
|
||||
static void esp_rmaker_node_info_free(esp_rmaker_node_info_t *info)
|
||||
{
|
||||
if (info) {
|
||||
if (info->name) {
|
||||
free(info->name);
|
||||
}
|
||||
if (info->type) {
|
||||
free(info->type);
|
||||
}
|
||||
if (info->model) {
|
||||
free(info->model);
|
||||
}
|
||||
if (info->fw_version) {
|
||||
free(info->fw_version);
|
||||
}
|
||||
if (info->subtype) {
|
||||
free(info->subtype);
|
||||
}
|
||||
if (info->secure_boot_digest) {
|
||||
esp_rmaker_secure_boot_digest_free(info->secure_boot_digest);
|
||||
info->secure_boot_digest = NULL;
|
||||
}
|
||||
free(info);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_attribute_delete(esp_rmaker_attr_t *attr)
|
||||
{
|
||||
if (attr) {
|
||||
if (attr->name) {
|
||||
free(attr->name);
|
||||
}
|
||||
if (attr->value) {
|
||||
free(attr->value);
|
||||
}
|
||||
free(attr);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_node_delete(const esp_rmaker_node_t *node)
|
||||
{
|
||||
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||
if (_node) {
|
||||
esp_rmaker_attr_t *attr = _node->attributes;
|
||||
while (attr) {
|
||||
esp_rmaker_attr_t *next_attr = attr->next;
|
||||
esp_rmaker_attribute_delete(attr);
|
||||
attr = next_attr;
|
||||
}
|
||||
_esp_rmaker_device_t *device = _node->devices;
|
||||
while (device) {
|
||||
_esp_rmaker_device_t *next_device = device->next;
|
||||
device->parent = NULL;
|
||||
esp_rmaker_device_delete((esp_rmaker_device_t *)device);
|
||||
device = next_device;
|
||||
}
|
||||
/* Node ID is created in the context of esp_rmaker_init and just assigned
|
||||
* here. So, we would not free it here.
|
||||
*/
|
||||
if (_node->node_id) {
|
||||
_node->node_id = NULL;
|
||||
}
|
||||
if (_node->info) {
|
||||
esp_rmaker_node_info_free(_node->info);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_rmaker_node_t *esp_rmaker_node_create(const char *name, const char *type)
|
||||
{
|
||||
static bool node_created;
|
||||
if (node_created) {
|
||||
ESP_LOGE(TAG, "Node has already been created. Cannot create another");
|
||||
return NULL;
|
||||
}
|
||||
if (!name || !type) {
|
||||
ESP_LOGE(TAG, "Node Name and Type are mandatory.");
|
||||
return NULL;
|
||||
}
|
||||
_esp_rmaker_node_t *node = MEM_CALLOC_EXTRAM(1, sizeof(_esp_rmaker_node_t));
|
||||
if (!node) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for node.");
|
||||
return NULL;
|
||||
}
|
||||
node->node_id = esp_rmaker_get_node_id();
|
||||
if (!node->node_id) {
|
||||
ESP_LOGE(TAG, "Failed to initialise Node Id. Please perform \"claiming\" using RainMaker CLI.");
|
||||
goto node_create_err;
|
||||
}
|
||||
ESP_LOGI(TAG, "Node ID ----- %s", node->node_id);
|
||||
|
||||
node->info = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_node_info_t));
|
||||
if (!node->info) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for node info.");
|
||||
goto node_create_err;
|
||||
}
|
||||
node->info->name = strdup(name);
|
||||
node->info->type = strdup(type);
|
||||
const esp_app_desc_t *app_desc;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
app_desc = esp_app_get_description();
|
||||
#else
|
||||
app_desc = esp_ota_get_app_description();
|
||||
#endif
|
||||
node->info->fw_version = strdup(app_desc->version);
|
||||
node->info->model = strdup(app_desc->project_name);
|
||||
if (esp_secure_boot_enabled()) {
|
||||
node->info->secure_boot_digest = esp_rmaker_get_secure_boot_digest();
|
||||
if (!node->info->secure_boot_digest) {
|
||||
goto node_create_err;
|
||||
}
|
||||
}
|
||||
if (!node->info->name || !node->info->type
|
||||
|| !node->info->fw_version || !node->info->model) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for node info.");
|
||||
goto node_create_err;
|
||||
}
|
||||
node_created = true;
|
||||
return (esp_rmaker_node_t *)node;
|
||||
node_create_err:
|
||||
esp_rmaker_node_delete((esp_rmaker_node_t *)node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_node_add_fw_version(const esp_rmaker_node_t *node, const char *fw_version)
|
||||
{
|
||||
if (!node || !fw_version) {
|
||||
ESP_LOGE(TAG, "Node handle or fw version cannot be NULL.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_rmaker_node_info_t *info = esp_rmaker_node_get_info(node);
|
||||
if (!info) {
|
||||
ESP_LOGE(TAG, "Failed to get Node Info.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (info->fw_version) {
|
||||
free(info->fw_version);
|
||||
}
|
||||
info->fw_version = strdup(fw_version);
|
||||
if (!info->fw_version) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for fw version.");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_node_add_model(const esp_rmaker_node_t *node, const char *model)
|
||||
{
|
||||
if (!node || !model) {
|
||||
ESP_LOGE(TAG, "Node handle or model cannot be NULL.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_rmaker_node_info_t *info = esp_rmaker_node_get_info(node);
|
||||
if (!info) {
|
||||
ESP_LOGE(TAG, "Failed to get Node Info.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (info->model) {
|
||||
free(info->model);
|
||||
}
|
||||
info->model = strdup(model);
|
||||
if (!info->model) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for node model.");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_node_add_subtype(const esp_rmaker_node_t *node, const char *subtype)
|
||||
{
|
||||
if (!node || !subtype) {
|
||||
ESP_LOGE(TAG, "Node handle or subtype cannot be NULL.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_rmaker_node_info_t *info = esp_rmaker_node_get_info(node);
|
||||
if (!info) {
|
||||
ESP_LOGE(TAG, "Failed to get Node Info.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (info->subtype) {
|
||||
free(info->subtype);
|
||||
}
|
||||
info->subtype = strdup(subtype);
|
||||
if (!info->subtype) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for node subtype.");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_node_add_attribute(const esp_rmaker_node_t *node, const char *attr_name, const char *value)
|
||||
{
|
||||
if (!node || !attr_name || !value) {
|
||||
ESP_LOGE(TAG, "Node handle, attribute name or value cannot be NULL.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_rmaker_attr_t *attr = ((_esp_rmaker_node_t *)node)->attributes;
|
||||
while(attr && attr->next) {
|
||||
if (strcmp(attr->name, attr_name) == 0) {
|
||||
ESP_LOGE(TAG, "Node attribute with name %s already exists.", attr_name);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
attr = attr->next;
|
||||
}
|
||||
esp_rmaker_attr_t *new_attr = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_attr_t));
|
||||
if (!new_attr) {
|
||||
ESP_LOGE(TAG, "Failed to create node attribute %s.", attr_name);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
new_attr->name = strdup(attr_name);
|
||||
new_attr->value = strdup(value);
|
||||
if (!new_attr->name || !new_attr->value) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for name/value for attribute %s.", attr_name);
|
||||
esp_rmaker_attribute_delete(new_attr);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (attr) {
|
||||
attr->next = new_attr;
|
||||
} else {
|
||||
((_esp_rmaker_node_t *)node)->attributes = new_attr;
|
||||
}
|
||||
ESP_LOGI(TAG, "Node attribute %s created", attr_name);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_node_add_device(const esp_rmaker_node_t *node, const esp_rmaker_device_t *device)
|
||||
{
|
||||
if (!node || !device) {
|
||||
ESP_LOGE(TAG, "Node or Device/Service handle cannot be NULL.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||
_esp_rmaker_device_t *_new_device = (_esp_rmaker_device_t *)device;
|
||||
_esp_rmaker_device_t *_device = _node->devices;
|
||||
while(_device) {
|
||||
if (strcmp(_device->name, _new_device->name) == 0) {
|
||||
ESP_LOGE(TAG, "%s with name %s already exists", _new_device->is_service ? "Service":"Device", _new_device->name);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (_device->next) {
|
||||
_device = _device->next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_device) {
|
||||
_device->next = _new_device;
|
||||
} else {
|
||||
_node->devices = _new_device;
|
||||
}
|
||||
_new_device->parent = node;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_node_remove_device(const esp_rmaker_node_t *node, const esp_rmaker_device_t *device)
|
||||
{
|
||||
if (!node || !device) {
|
||||
ESP_LOGE(TAG, "Node or Device/Service handle cannot be NULL.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||
_esp_rmaker_device_t *_device = (_esp_rmaker_device_t *)device;
|
||||
|
||||
_esp_rmaker_device_t *tmp_device = _node->devices;
|
||||
_esp_rmaker_device_t *prev_device = NULL;
|
||||
while(tmp_device) {
|
||||
if (tmp_device == _device) {
|
||||
break;
|
||||
}
|
||||
prev_device = tmp_device;
|
||||
tmp_device = tmp_device->next;
|
||||
}
|
||||
if (!tmp_device) {
|
||||
ESP_LOGE(TAG, "Device %s not found in node %s", _device->name, _node->info->name);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (tmp_device == _node->devices) {
|
||||
_node->devices = tmp_device->next;
|
||||
} else {
|
||||
prev_device->next = tmp_device->next;
|
||||
}
|
||||
tmp_device->parent = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_node_get_device_by_name(const esp_rmaker_node_t *node, const char *device_name)
|
||||
{
|
||||
if (!node || !device_name) {
|
||||
ESP_LOGE(TAG, "Node handle or device name cannot be NULL");
|
||||
return NULL;
|
||||
}
|
||||
_esp_rmaker_device_t *device = ((_esp_rmaker_node_t *)node)->devices;
|
||||
while(device) {
|
||||
if (strcmp(device->name, device_name) == 0) {
|
||||
break;
|
||||
}
|
||||
device = device->next;
|
||||
}
|
||||
return (esp_rmaker_device_t *)device;
|
||||
}
|
||||
|
||||
_esp_rmaker_device_t *esp_rmaker_node_get_first_device(const esp_rmaker_node_t *node)
|
||||
{
|
||||
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||
if (!_node) {
|
||||
ESP_LOGE(TAG, "Node handle cannot be NULL.");
|
||||
return NULL;
|
||||
}
|
||||
return _node->devices;
|
||||
}
|
||||
|
||||
esp_rmaker_node_info_t *esp_rmaker_node_get_info(const esp_rmaker_node_t *node)
|
||||
{
|
||||
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||
if (!_node) {
|
||||
ESP_LOGE(TAG, "Node handle cannot be NULL.");
|
||||
return NULL;
|
||||
}
|
||||
return _node->info;
|
||||
}
|
||||
|
||||
esp_rmaker_attr_t *esp_rmaker_node_get_first_attribute(const esp_rmaker_node_t *node)
|
||||
{
|
||||
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||
if (!_node) {
|
||||
ESP_LOGE(TAG, "Node handle cannot be NULL.");
|
||||
return NULL;
|
||||
}
|
||||
return _node->attributes;
|
||||
}
|
||||
|
||||
char *esp_rmaker_node_get_id(const esp_rmaker_node_t *node)
|
||||
{
|
||||
_esp_rmaker_node_t *_node = (_esp_rmaker_node_t *)node;
|
||||
if (!_node) {
|
||||
ESP_LOGE(TAG, "Node handle cannot be NULL.");
|
||||
return NULL;
|
||||
}
|
||||
return _node->node_id;
|
||||
}
|
||||
191
components/esp_rainmaker/src/core/esp_rmaker_node_auth.c
Normal file
191
components/esp_rainmaker/src/core/esp_rmaker_node_auth.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <esp_idf_version.h>
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include "mbedtls/platform.h"
|
||||
#include "mbedtls/pk.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "sha/sha_parallel_engine.h"
|
||||
|
||||
#include "esp_secure_cert_read.h"
|
||||
#include "esp_rmaker_utils.h"
|
||||
#include "esp_rmaker_client_data.h"
|
||||
#include "mbedtls/x509_crt.h"
|
||||
|
||||
static const char *TAG = "esp_rmaker_user_node_auth";
|
||||
|
||||
static int myrand(void *rng_state, unsigned char *output, size_t len)
|
||||
{
|
||||
esp_fill_random(output, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint8_t to_hex_digit(unsigned val)
|
||||
{
|
||||
return (val < 10) ? ('0' + val) : ('a' + val - 10);
|
||||
}
|
||||
|
||||
static void bytes_to_hex(uint8_t *src, uint8_t *dst, int in_len)
|
||||
{
|
||||
for (int i = 0; i < in_len; i++) {
|
||||
dst[2 * i] = to_hex_digit(src[i] >> 4);
|
||||
dst[2 * i + 1] = to_hex_digit(src[i] & 0xf);
|
||||
}
|
||||
dst[2 * in_len] = 0;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_node_auth_sign_msg(const void *challenge, size_t inlen, void **response, size_t *outlen)
|
||||
{
|
||||
if (!challenge || (inlen == 0)) {
|
||||
ESP_LOGE(TAG, "function arguments challenge and inlen cannot be NULL.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Get private key */
|
||||
char *priv_key = NULL;
|
||||
size_t priv_key_len = 0;
|
||||
#if CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR
|
||||
esp_secure_cert_key_type_t key_type;
|
||||
esp_err_t err = esp_secure_cert_get_priv_key_type(&key_type);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get the type of private key from secure cert partition, err:%d", err);
|
||||
return err;
|
||||
}
|
||||
if (key_type == ESP_SECURE_CERT_INVALID_KEY) {
|
||||
ESP_LOGE(TAG, "Private key type in secure cert partition is invalid");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* This flow is for devices supporting ECDSA peripheral */
|
||||
if (key_type == ESP_SECURE_CERT_ECDSA_PERIPHERAL_KEY) {
|
||||
#if CONFIG_USE_ESP32_ECDSA_PERIPHERAL
|
||||
/* TODO: code for signing the challenge on devices that have a DS peripheral. */
|
||||
return ESP_FAIL;
|
||||
#else /* !CONFIG_USE_ESP32_ECDSA_PERIPHERAL */
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
#endif /* CONFIG_USE_ESP32_ECDSA_PERIPHERAL */
|
||||
} else
|
||||
#endif /* CONFIG_ESP_RMAKER_USE_ESP_SECURE_CERT_MGR */
|
||||
{
|
||||
/* This flow is for devices which do not support ECDSA peripheral */
|
||||
#if !CONFIG_USE_ESP32_ECDSA_PERIPHERAL
|
||||
priv_key = esp_rmaker_get_client_key();
|
||||
priv_key_len = esp_rmaker_get_client_key_len();
|
||||
#else /* CONFIG_USE_ESP32_ECDSA_PERIPHERAL */
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
#endif /* !CONFIG_USE_ESP32_ECDSA_PERIPHERAL */
|
||||
}
|
||||
if (!priv_key) {
|
||||
ESP_LOGE(TAG, "Error getting private key");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Calculate SHA of challenge */
|
||||
uint8_t hash[32];
|
||||
esp_sha(SHA2_256,(const unsigned char *)challenge, inlen, hash);
|
||||
|
||||
/* Sign the hash using RSA or ECDSA */
|
||||
mbedtls_pk_context pk_ctx;
|
||||
mbedtls_pk_init(&pk_ctx);
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
int ret = mbedtls_pk_parse_key(&pk_ctx, (uint8_t *)priv_key, priv_key_len, NULL, 0, NULL, 0);
|
||||
#else /* !(ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) */
|
||||
int ret = mbedtls_pk_parse_key(&pk_ctx, (uint8_t *)priv_key, priv_key_len, NULL, 0);
|
||||
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) */
|
||||
uint8_t *signature = NULL;
|
||||
if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_RSA) {
|
||||
ESP_LOGI(TAG, "RSA key found");
|
||||
signature = (uint8_t *)MEM_CALLOC_EXTRAM(1, 256); // TODO: replace magic number 256
|
||||
} else if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_ECKEY) {
|
||||
ESP_LOGI(TAG, "ECDSA key found");
|
||||
signature = (uint8_t *)MEM_CALLOC_EXTRAM(1, MBEDTLS_ECDSA_MAX_LEN);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "found different key: %d", mbedtls_pk_get_type(&pk_ctx));
|
||||
}
|
||||
if (!signature) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory to signature.");
|
||||
mbedtls_pk_free(&pk_ctx);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
size_t slen = 0;
|
||||
if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_ECKEY) {
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
ret = mbedtls_ecdsa_write_signature(mbedtls_pk_ec(pk_ctx), MBEDTLS_MD_SHA256, hash, sizeof(hash), signature, MBEDTLS_ECDSA_MAX_LEN, &slen, myrand, NULL);
|
||||
#else /* !(ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) */
|
||||
ret = mbedtls_ecdsa_write_signature(mbedtls_pk_ec(pk_ctx), MBEDTLS_MD_SHA256, hash, sizeof(hash), signature, &slen, myrand, NULL);
|
||||
#endif /* (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) */
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Error in writing signature. err = %d", ret);
|
||||
free(signature);
|
||||
mbedtls_pk_free(&pk_ctx);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
} else if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_RSA) {
|
||||
mbedtls_rsa_context *rsa_ctx = mbedtls_pk_rsa(pk_ctx);
|
||||
// rsa_ctx->MBEDTLS_PRIVATE(len) = 256;
|
||||
mbedtls_rsa_set_padding(rsa_ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256);
|
||||
ret = mbedtls_rsa_rsassa_pss_sign(rsa_ctx,
|
||||
myrand,
|
||||
NULL,
|
||||
MBEDTLS_MD_SHA256,
|
||||
sizeof(hash),
|
||||
hash,
|
||||
signature);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Error in writing signature. err = %d", ret);
|
||||
free(signature);
|
||||
mbedtls_pk_free(&pk_ctx);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
slen = mbedtls_rsa_get_len(rsa_ctx);
|
||||
ESP_LOGI(TAG, "signature length %d", slen);
|
||||
}
|
||||
|
||||
#if TEST_SIGNATURE_VERIFICATION
|
||||
char *cert = esp_rmaker_get_client_cert();
|
||||
mbedtls_x509_crt crt;
|
||||
mbedtls_x509_crt_init(&crt);
|
||||
mbedtls_x509_crt_parse(&crt, (const unsigned char *)cert, strlen(cert) + 1);
|
||||
mbedtls_pk_context *pk = &crt.pk;
|
||||
|
||||
if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_ECKEY) {
|
||||
ret = mbedtls_pk_verify(pk, MBEDTLS_MD_SHA256, hash, sizeof(hash), signature, sizeof(signature));
|
||||
} else if (mbedtls_pk_get_type(&pk_ctx) == MBEDTLS_PK_RSA) {
|
||||
mbedtls_pk_rsassa_pss_options opt = {
|
||||
.mgf1_hash_id = MBEDTLS_MD_SHA256,
|
||||
.expected_salt_len = MBEDTLS_RSA_SALT_LEN_ANY
|
||||
};
|
||||
ret = mbedtls_pk_verify_ext(MBEDTLS_PK_RSASSA_PSS, (const void *)&opt, pk, MBEDTLS_MD_SHA256, hash, sizeof(hash), signature, slen);
|
||||
}
|
||||
|
||||
mbedtls_x509_crt_free(&crt);
|
||||
free(cert);
|
||||
if (ret == 0) {
|
||||
ESP_LOGI(TAG, "Signature is valid");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Signature verification failed %d", ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Convert hex stream to bytes */
|
||||
#define BYTE_ENCODED_SIGNATURE_LEN ((2 * slen) + 1) /* +1 for null character */
|
||||
char *char_signature = (char *)MEM_ALLOC_EXTRAM(BYTE_ENCODED_SIGNATURE_LEN);
|
||||
if (!char_signature) {
|
||||
ESP_LOGE(TAG, "Error in allocating memory for challenge response.");
|
||||
free(signature);
|
||||
mbedtls_pk_free(&pk_ctx);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
bytes_to_hex(signature, (uint8_t *)char_signature, slen);
|
||||
mbedtls_pk_free(&pk_ctx);
|
||||
free(signature);
|
||||
/* Set output variables */
|
||||
*(char **)response = char_signature;
|
||||
*outlen = 2 * slen; /* hex encoding takes 2 bytes per input byte */
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) */
|
||||
311
components/esp_rainmaker/src/core/esp_rmaker_node_config.c
Normal file
311
components/esp_rainmaker/src/core/esp_rmaker_node_config.c
Normal file
@@ -0,0 +1,311 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <sdkconfig.h>
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include <json_generator.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include "esp_rmaker_internal.h"
|
||||
#include "esp_rmaker_mqtt.h"
|
||||
#include "esp_rmaker_mqtt_topics.h"
|
||||
#include <esp_rmaker_secure_boot_digest.h>
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include <esp_app_desc.h>
|
||||
#else
|
||||
#include <esp_ota_ops.h>
|
||||
#endif
|
||||
|
||||
#define NODE_CONFIG_TOPIC_SUFFIX "config"
|
||||
|
||||
static const char *TAG = "esp_rmaker_node_config";
|
||||
static esp_err_t esp_rmaker_report_info(json_gen_str_t *jptr)
|
||||
{
|
||||
/* TODO: Error handling */
|
||||
esp_rmaker_node_info_t *info = esp_rmaker_node_get_info(esp_rmaker_get_node());
|
||||
json_gen_obj_set_string(jptr, "node_id", esp_rmaker_get_node_id());
|
||||
json_gen_obj_set_string(jptr, "config_version", ESP_RMAKER_CONFIG_VERSION);
|
||||
json_gen_push_object(jptr, "info");
|
||||
json_gen_obj_set_string(jptr, "name", info->name);
|
||||
json_gen_obj_set_string(jptr, "fw_version", info->fw_version);
|
||||
json_gen_obj_set_string(jptr, "type", info->type);
|
||||
if (info->subtype) {
|
||||
json_gen_obj_set_string(jptr, "subtype", info->subtype);
|
||||
}
|
||||
json_gen_obj_set_string(jptr, "model", info->model);
|
||||
const esp_app_desc_t *app_desc;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
app_desc = esp_app_get_description();
|
||||
#else
|
||||
app_desc = esp_ota_get_app_description();
|
||||
#endif
|
||||
json_gen_obj_set_string(jptr, "project_name", (char *)app_desc->project_name);
|
||||
json_gen_obj_set_string(jptr, "platform", CONFIG_IDF_TARGET);
|
||||
#ifdef CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
json_gen_push_object(jptr, "secure_boot_digest");
|
||||
for (int i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
char key_name[3];
|
||||
snprintf(key_name, sizeof(key_name), "k%d", i);
|
||||
key_name[2] = '\0';
|
||||
if (info->secure_boot_digest[i] == NULL) {
|
||||
json_gen_obj_set_null(jptr, key_name);
|
||||
continue;
|
||||
}
|
||||
json_gen_obj_set_string(jptr, key_name, info->secure_boot_digest[i]);
|
||||
}
|
||||
json_gen_pop_object(jptr);
|
||||
#endif
|
||||
json_gen_pop_object(jptr);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void esp_rmaker_report_attribute(esp_rmaker_attr_t *attr, json_gen_str_t *jptr)
|
||||
{
|
||||
json_gen_start_object(jptr);
|
||||
json_gen_obj_set_string(jptr, "name", attr->name);
|
||||
json_gen_obj_set_string(jptr, "value", attr->value);
|
||||
json_gen_end_object(jptr);
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_report_node_attributes(json_gen_str_t *jptr)
|
||||
{
|
||||
esp_rmaker_attr_t *attr = esp_rmaker_node_get_first_attribute(esp_rmaker_get_node());
|
||||
if (!attr) {
|
||||
return ESP_OK;
|
||||
}
|
||||
json_gen_push_array(jptr, "attributes");
|
||||
while (attr) {
|
||||
esp_rmaker_report_attribute(attr, jptr);
|
||||
attr = attr->next;
|
||||
}
|
||||
json_gen_pop_array(jptr);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_report_value(const esp_rmaker_param_val_t *val, char *key, json_gen_str_t *jptr)
|
||||
{
|
||||
if (!key || !jptr) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (!val) {
|
||||
json_gen_obj_set_null(jptr, key);
|
||||
return ESP_OK;
|
||||
}
|
||||
switch (val->type) {
|
||||
case RMAKER_VAL_TYPE_BOOLEAN:
|
||||
json_gen_obj_set_bool(jptr, key, val->val.b);
|
||||
break;
|
||||
case RMAKER_VAL_TYPE_INTEGER:
|
||||
json_gen_obj_set_int(jptr, key, val->val.i);
|
||||
break;
|
||||
case RMAKER_VAL_TYPE_FLOAT:
|
||||
json_gen_obj_set_float(jptr, key, val->val.f);
|
||||
break;
|
||||
case RMAKER_VAL_TYPE_STRING:
|
||||
json_gen_obj_set_string(jptr, key, val->val.s);
|
||||
break;
|
||||
case RMAKER_VAL_TYPE_OBJECT:
|
||||
json_gen_push_object_str(jptr, key, val->val.s);
|
||||
break;
|
||||
case RMAKER_VAL_TYPE_ARRAY:
|
||||
json_gen_push_array_str(jptr, key, val->val.s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_report_data_type(esp_rmaker_val_type_t type, char *data_type_key, json_gen_str_t *jptr)
|
||||
{
|
||||
switch (type) {
|
||||
case RMAKER_VAL_TYPE_BOOLEAN:
|
||||
json_gen_obj_set_string(jptr, data_type_key, "bool");
|
||||
break;
|
||||
case RMAKER_VAL_TYPE_INTEGER:
|
||||
json_gen_obj_set_string(jptr, data_type_key, "int");
|
||||
break;
|
||||
case RMAKER_VAL_TYPE_FLOAT:
|
||||
json_gen_obj_set_string(jptr, data_type_key, "float");
|
||||
break;
|
||||
case RMAKER_VAL_TYPE_STRING:
|
||||
json_gen_obj_set_string(jptr, data_type_key, "string");
|
||||
break;
|
||||
case RMAKER_VAL_TYPE_OBJECT:
|
||||
json_gen_obj_set_string(jptr, data_type_key, "object");
|
||||
break;
|
||||
case RMAKER_VAL_TYPE_ARRAY:
|
||||
json_gen_obj_set_string(jptr, data_type_key, "array");
|
||||
break;
|
||||
default:
|
||||
json_gen_obj_set_string(jptr, data_type_key, "invalid");
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_report_param_config(_esp_rmaker_param_t *param, json_gen_str_t *jptr)
|
||||
{
|
||||
json_gen_start_object(jptr);
|
||||
if (param->name) {
|
||||
json_gen_obj_set_string(jptr, "name", param->name);
|
||||
}
|
||||
if (param->type) {
|
||||
json_gen_obj_set_string(jptr, "type", param->type);
|
||||
}
|
||||
esp_rmaker_report_data_type(param->val.type, "data_type", jptr);
|
||||
json_gen_push_array(jptr, "properties");
|
||||
if (param->prop_flags & PROP_FLAG_READ) {
|
||||
json_gen_arr_set_string(jptr, "read");
|
||||
}
|
||||
if (param->prop_flags & PROP_FLAG_WRITE) {
|
||||
json_gen_arr_set_string(jptr, "write");
|
||||
}
|
||||
/* A parameter cannot have both, PROP_FLAG_TIME_SERIES and PROP_FLAG_SIMPLE_TIME_SERIES */
|
||||
if (param->prop_flags & PROP_FLAG_TIME_SERIES) {
|
||||
json_gen_arr_set_string(jptr, "time_series");
|
||||
} else if (param->prop_flags & PROP_FLAG_SIMPLE_TIME_SERIES) {
|
||||
json_gen_arr_set_string(jptr, "simple_ts");
|
||||
}
|
||||
json_gen_pop_array(jptr);
|
||||
if (param->bounds) {
|
||||
json_gen_push_object(jptr, "bounds");
|
||||
esp_rmaker_report_value(¶m->bounds->min, "min", jptr);
|
||||
esp_rmaker_report_value(¶m->bounds->max, "max", jptr);
|
||||
if (param->bounds->step.val.i) {
|
||||
esp_rmaker_report_value(¶m->bounds->step, "step", jptr);
|
||||
}
|
||||
json_gen_pop_object(jptr);
|
||||
}
|
||||
if (param->valid_str_list) {
|
||||
json_gen_push_array(jptr, "valid_strs");
|
||||
for (int i = 0; i < param->valid_str_list->str_list_cnt; i++) {
|
||||
json_gen_arr_set_string(jptr, (char *)param->valid_str_list->str_list[i]);
|
||||
}
|
||||
json_gen_pop_array(jptr);
|
||||
}
|
||||
if (param->ui_type) {
|
||||
json_gen_obj_set_string(jptr, "ui_type", param->ui_type);
|
||||
}
|
||||
json_gen_end_object(jptr);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_report_devices_or_services(json_gen_str_t *jptr, char *key)
|
||||
{
|
||||
_esp_rmaker_device_t *device = esp_rmaker_node_get_first_device(esp_rmaker_get_node());
|
||||
if (!device) {
|
||||
return ESP_OK;
|
||||
}
|
||||
bool is_service = false;
|
||||
if (strcmp(key, "services") == 0) {
|
||||
is_service = true;
|
||||
}
|
||||
json_gen_push_array(jptr, key);
|
||||
while (device) {
|
||||
if (device->is_service == is_service) {
|
||||
json_gen_start_object(jptr);
|
||||
json_gen_obj_set_string(jptr, "name", device->name);
|
||||
if (device->type) {
|
||||
json_gen_obj_set_string(jptr, "type", device->type);
|
||||
}
|
||||
if (device->subtype) {
|
||||
json_gen_obj_set_string(jptr, "subtype", device->subtype);
|
||||
}
|
||||
if (device->model) {
|
||||
json_gen_obj_set_string(jptr, "model", device->model);
|
||||
}
|
||||
if (device->attributes) {
|
||||
json_gen_push_array(jptr, "attributes");
|
||||
esp_rmaker_attr_t *attr = device->attributes;
|
||||
while (attr) {
|
||||
esp_rmaker_report_attribute(attr, jptr);
|
||||
attr = attr->next;
|
||||
}
|
||||
json_gen_pop_array(jptr);
|
||||
}
|
||||
if (device->primary) {
|
||||
json_gen_obj_set_string(jptr, "primary", device->primary->name);
|
||||
}
|
||||
if (device->params) {
|
||||
json_gen_push_array(jptr, "params");
|
||||
_esp_rmaker_param_t *param = device->params;
|
||||
while (param) {
|
||||
esp_rmaker_report_param_config(param, jptr);
|
||||
param = param->next;
|
||||
}
|
||||
json_gen_pop_array(jptr);
|
||||
}
|
||||
json_gen_end_object(jptr);
|
||||
}
|
||||
device = device->next;
|
||||
}
|
||||
json_gen_pop_array(jptr);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int __esp_rmaker_get_node_config(char *buf, size_t buf_size)
|
||||
{
|
||||
json_gen_str_t jstr;
|
||||
json_gen_str_start(&jstr, buf, buf_size, NULL, NULL);
|
||||
json_gen_start_object(&jstr);
|
||||
esp_rmaker_report_info(&jstr);
|
||||
esp_rmaker_report_node_attributes(&jstr);
|
||||
esp_rmaker_report_devices_or_services(&jstr, "devices");
|
||||
esp_rmaker_report_devices_or_services(&jstr, "services");
|
||||
if (json_gen_end_object(&jstr) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return json_gen_str_end(&jstr);
|
||||
}
|
||||
|
||||
char *esp_rmaker_get_node_config(void)
|
||||
{
|
||||
/* Setting buffer to NULL and size to 0 just to get the required buffer size */
|
||||
int req_size = __esp_rmaker_get_node_config(NULL, 0);
|
||||
if (req_size < 0) {
|
||||
ESP_LOGE(TAG, "Failed to get required size for Node config JSON.");
|
||||
return NULL;
|
||||
}
|
||||
char *node_config = MEM_CALLOC_EXTRAM(1, req_size);
|
||||
if (!node_config) {
|
||||
ESP_LOGE(TAG, "Failed to allocate %d bytes for node config", req_size);
|
||||
return NULL;
|
||||
}
|
||||
if (__esp_rmaker_get_node_config(node_config, req_size) < 0) {
|
||||
free(node_config);
|
||||
ESP_LOGE(TAG, "Failed to generate Node config JSON.");
|
||||
return NULL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Generated Node config of length %d", req_size);
|
||||
return node_config;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_report_node_config()
|
||||
{
|
||||
char *publish_payload = esp_rmaker_get_node_config();
|
||||
if (!publish_payload) {
|
||||
ESP_LOGE(TAG, "Could not get node configuration for reporting to cloud");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||
esp_rmaker_create_mqtt_topic(publish_topic, MQTT_TOPIC_BUFFER_SIZE, NODE_CONFIG_TOPIC_SUFFIX, NODE_CONFIG_TOPIC_RULE);
|
||||
ESP_LOGD(TAG, "Reporting Node Configuration of length %lu bytes.", (unsigned long) strlen(publish_payload));
|
||||
ESP_LOGD(TAG, "%s", publish_payload);
|
||||
esp_err_t ret = esp_rmaker_mqtt_publish(publish_topic, publish_payload, strlen(publish_payload),
|
||||
RMAKER_MQTT_QOS1, NULL);
|
||||
free(publish_payload);
|
||||
return ret;
|
||||
}
|
||||
1001
components/esp_rainmaker/src/core/esp_rmaker_param.c
Normal file
1001
components/esp_rainmaker/src/core/esp_rmaker_param.c
Normal file
File diff suppressed because it is too large
Load Diff
548
components/esp_rainmaker/src/core/esp_rmaker_scenes.c
Normal file
548
components/esp_rainmaker/src/core/esp_rmaker_scenes.c
Normal file
@@ -0,0 +1,548 @@
|
||||
// Copyright 2022 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_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <json_parser.h>
|
||||
#include <json_generator.h>
|
||||
|
||||
#include <esp_rmaker_internal.h>
|
||||
#include <esp_rmaker_standard_services.h>
|
||||
#include <esp_rmaker_standard_types.h>
|
||||
#include <esp_rmaker_scenes.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
#define MAX_ID_LEN 8
|
||||
#define MAX_NAME_LEN 32
|
||||
#define MAX_INFO_LEN 100
|
||||
#define MAX_OPERATION_LEN 10
|
||||
#define MAX_SCENES CONFIG_ESP_RMAKER_SCENES_MAX_SCENES
|
||||
|
||||
static const char *TAG = "esp_rmaker_scenes";
|
||||
|
||||
typedef struct esp_rmaker_scene_action {
|
||||
void *data;
|
||||
size_t data_len;
|
||||
} esp_rmaker_scene_action_t;
|
||||
|
||||
typedef struct esp_rmaker_scene {
|
||||
char name[MAX_NAME_LEN + 1]; /* +1 for NULL termination */
|
||||
char id[MAX_ID_LEN + 1]; /* +1 for NULL termination */
|
||||
/* Info is used to store additional information, it is limited to MAX_INFO_LEN bytes. */
|
||||
char *info;
|
||||
/* Flags can be used to identify the scene. */
|
||||
uint32_t flags;
|
||||
esp_rmaker_scene_action_t action;
|
||||
struct esp_rmaker_scene *next;
|
||||
} esp_rmaker_scene_t;
|
||||
|
||||
typedef enum scenes_operation {
|
||||
OPERATION_INVALID,
|
||||
OPERATION_ADD,
|
||||
OPERATION_EDIT,
|
||||
OPERATION_REMOVE,
|
||||
OPERATION_ACTIVATE,
|
||||
OPERATION_DEACTIVATE,
|
||||
} scenes_operation_t;
|
||||
|
||||
typedef struct {
|
||||
esp_rmaker_scene_t *scenes_list;
|
||||
int total_scenes;
|
||||
bool deactivate_support;
|
||||
esp_rmaker_device_t *scenes_service;
|
||||
} esp_rmaker_scenes_priv_data_t;
|
||||
|
||||
static esp_rmaker_scenes_priv_data_t *scenes_priv_data;
|
||||
|
||||
static void esp_rmaker_scenes_free(esp_rmaker_scene_t *scene)
|
||||
{
|
||||
if (!scene) {
|
||||
return;
|
||||
}
|
||||
if (scene->action.data) {
|
||||
free(scene->action.data);
|
||||
}
|
||||
if (scene->info) {
|
||||
free(scene->info);
|
||||
}
|
||||
free(scene);
|
||||
}
|
||||
|
||||
static esp_rmaker_scene_t *esp_rmaker_scenes_get_scene_from_id(const char *id)
|
||||
{
|
||||
if (!id) {
|
||||
return NULL;
|
||||
}
|
||||
esp_rmaker_scene_t *scene = scenes_priv_data->scenes_list;
|
||||
while(scene) {
|
||||
if (strncmp(id, scene->id, sizeof(scene->id)) == 0) {
|
||||
ESP_LOGD(TAG, "Scene with id %s found in list for get.", id);
|
||||
return scene;
|
||||
}
|
||||
scene = scene->next;
|
||||
}
|
||||
ESP_LOGD(TAG, "Scene with id %s not found in list for get.", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_scenes_add_to_list(esp_rmaker_scene_t *scene)
|
||||
{
|
||||
if (!scene) {
|
||||
ESP_LOGE(TAG, "Scene is NULL. Not adding to list.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (esp_rmaker_scenes_get_scene_from_id(scene->id) != NULL) {
|
||||
ESP_LOGI(TAG, "Scene with id %s already added to list. Not adding again.", scene->id);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Parse list */
|
||||
esp_rmaker_scene_t *prev_scene = scenes_priv_data->scenes_list;
|
||||
while(prev_scene) {
|
||||
if (prev_scene->next) {
|
||||
prev_scene = prev_scene->next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add to list */
|
||||
if (prev_scene) {
|
||||
prev_scene->next = scene;
|
||||
} else {
|
||||
scenes_priv_data->scenes_list = scene;
|
||||
}
|
||||
ESP_LOGD(TAG, "Scene with id %s added to list.", scene->id);
|
||||
scenes_priv_data->total_scenes++;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_scenes_remove_from_list(esp_rmaker_scene_t *scene)
|
||||
{
|
||||
if (!scene) {
|
||||
ESP_LOGE(TAG, "Scene is NULL. Not removing from list.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
/* Parse list */
|
||||
esp_rmaker_scene_t *curr_scene = scenes_priv_data->scenes_list;
|
||||
esp_rmaker_scene_t *prev_scene = curr_scene;
|
||||
while(curr_scene) {
|
||||
if (strncmp(scene->id, curr_scene->id, sizeof(scene->id)) == 0) {
|
||||
ESP_LOGD(TAG, "Scene with id %s found in list for removing", scene->id);
|
||||
break;
|
||||
}
|
||||
prev_scene = curr_scene;
|
||||
curr_scene = curr_scene->next;
|
||||
}
|
||||
if (!curr_scene) {
|
||||
ESP_LOGE(TAG, "Scene with id %s not found in list. Not removing.", scene->id);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* Remove from list */
|
||||
if (curr_scene == scenes_priv_data->scenes_list) {
|
||||
scenes_priv_data->scenes_list = curr_scene->next;
|
||||
} else {
|
||||
prev_scene->next = curr_scene->next;
|
||||
}
|
||||
scenes_priv_data->total_scenes--;
|
||||
ESP_LOGD(TAG, "Scene with id %s removed from list.", scene->id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
scenes_operation_t esp_rmaker_scenes_get_operation_from_str(char *operation)
|
||||
{
|
||||
if (!operation) {
|
||||
return OPERATION_INVALID;
|
||||
}
|
||||
if (strncmp(operation, "add", strlen(operation)) == 0) {
|
||||
return OPERATION_ADD;
|
||||
} else if (strncmp(operation, "edit", strlen(operation)) == 0) {
|
||||
return OPERATION_EDIT;
|
||||
} else if (strncmp(operation, "remove", strlen(operation)) == 0) {
|
||||
return OPERATION_REMOVE;
|
||||
} else if (strncmp(operation, "activate", strlen(operation)) == 0) {
|
||||
return OPERATION_ACTIVATE;
|
||||
} else if (strncmp(operation, "deactivate", strlen(operation)) == 0) {
|
||||
return OPERATION_DEACTIVATE;
|
||||
}
|
||||
return OPERATION_INVALID;
|
||||
}
|
||||
|
||||
static scenes_operation_t esp_rmaker_scenes_parse_operation(jparse_ctx_t *jctx, char *id)
|
||||
{
|
||||
char operation_str[MAX_OPERATION_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||
scenes_operation_t operation = OPERATION_INVALID;
|
||||
json_obj_get_string(jctx, "operation", operation_str, sizeof(operation_str));
|
||||
if (strlen(operation_str) <= 0) {
|
||||
ESP_LOGE(TAG, "Operation not found in scene with id: %s", id);
|
||||
return operation;
|
||||
}
|
||||
operation = esp_rmaker_scenes_get_operation_from_str(operation_str);
|
||||
if (operation == OPERATION_EDIT) {
|
||||
/* Get scene temporarily */
|
||||
if (esp_rmaker_scenes_get_scene_from_id(id) == NULL) {
|
||||
/* Operation is edit, but scene not present already. Consider this as add. */
|
||||
ESP_LOGD(TAG, "Operation is edit, but scene with id %s not found. Changing the operation to add.", id);
|
||||
operation = OPERATION_ADD;
|
||||
}
|
||||
} else if (operation == OPERATION_INVALID) {
|
||||
ESP_LOGE(TAG, "Invalid scene operation found: %s", operation_str);
|
||||
}
|
||||
return operation;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_scenes_parse_info_and_flags(jparse_ctx_t *jctx, char **info, uint32_t *flags)
|
||||
{
|
||||
char _info[MAX_INFO_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||
int _flags = 0;
|
||||
|
||||
int err_code = json_obj_get_string(jctx, "info", _info, sizeof(_info));
|
||||
if (err_code == OS_SUCCESS) {
|
||||
if (*info) {
|
||||
free(*info);
|
||||
*info = NULL;
|
||||
}
|
||||
|
||||
int len = strlen(_info);
|
||||
if (len > 0) {
|
||||
/* +1 for NULL termination */
|
||||
*info = (char *)MEM_CALLOC_EXTRAM(1, len + 1);
|
||||
if (*info) {
|
||||
memcpy(*info, _info, len + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err_code = json_obj_get_int(jctx, "flags", &_flags);
|
||||
if (err_code == OS_SUCCESS) {
|
||||
if (flags) {
|
||||
*flags = _flags;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_scenes_parse_action(jparse_ctx_t *jctx, esp_rmaker_scene_action_t *action)
|
||||
{
|
||||
int data_len = 0;
|
||||
json_obj_get_object_strlen(jctx, "action", &data_len);
|
||||
if (data_len <= 0) {
|
||||
ESP_LOGD(TAG, "Action not found in JSON");
|
||||
return ESP_OK;
|
||||
}
|
||||
action->data_len = data_len + 1;
|
||||
|
||||
if (action->data) {
|
||||
free(action->data);
|
||||
}
|
||||
action->data = (void *)MEM_CALLOC_EXTRAM(1, action->data_len);
|
||||
if (!action->data) {
|
||||
ESP_LOGE(TAG, "Could not allocate action");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
json_obj_get_object_str(jctx, "action", action->data, action->data_len);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_rmaker_scene_t *esp_rmaker_scenes_find_or_create(jparse_ctx_t *jctx, char *id, scenes_operation_t operation)
|
||||
{
|
||||
char name[MAX_NAME_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||
esp_rmaker_scene_t *scene = NULL;
|
||||
if (operation == OPERATION_ADD) {
|
||||
/* Checking if scene with same id already exists. */
|
||||
scene = esp_rmaker_scenes_get_scene_from_id(id);
|
||||
if (scene) {
|
||||
ESP_LOGE(TAG, "Scene with id %s already exists. Not adding it again.", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get name */
|
||||
json_obj_get_string(jctx, "name", name, sizeof(name));
|
||||
if (strlen(name) <= 0) {
|
||||
ESP_LOGE(TAG, "Name not found for scene with id: %s", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This is a new scene. Fill it. */
|
||||
scene = (esp_rmaker_scene_t *)MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_scene_t));
|
||||
if (!scene) {
|
||||
ESP_LOGE(TAG, "Couldn't allocate scene with id: %s", id);
|
||||
return NULL;
|
||||
}
|
||||
strlcpy(scene->id, id, sizeof(scene->id));
|
||||
strlcpy(scene->name, name, sizeof(scene->name));
|
||||
} else {
|
||||
/* This scene should already be present */
|
||||
scene = esp_rmaker_scenes_get_scene_from_id(id);
|
||||
if (!scene) {
|
||||
ESP_LOGE(TAG, "Scene with id %s not found", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get name */
|
||||
if (operation == OPERATION_EDIT) {
|
||||
json_obj_get_string(jctx, "name", name, sizeof(name));
|
||||
if (strlen(name) > 0) {
|
||||
/* If there is name in the request, replace the name in the scene with this new one */
|
||||
memset(scene->name, 0, sizeof(scene->name));
|
||||
strlcpy(scene->name, name, sizeof(scene->name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return scene;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_scenes_perform_operation(esp_rmaker_scene_t *scene, scenes_operation_t operation)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
switch (operation) {
|
||||
case OPERATION_ADD:
|
||||
if (scenes_priv_data->total_scenes < MAX_SCENES) {
|
||||
err = esp_rmaker_scenes_add_to_list(scene);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Max sceness (%d) reached. Not adding this scene with id %s", MAX_SCENES,
|
||||
scene->id);
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPERATION_EDIT:
|
||||
/* Nothing to do here. name, info, action have already been handled */
|
||||
break;
|
||||
|
||||
case OPERATION_REMOVE:
|
||||
err = esp_rmaker_scenes_remove_from_list(scene);
|
||||
if (err == ESP_OK) {
|
||||
esp_rmaker_scenes_free(scene);
|
||||
}
|
||||
break;
|
||||
|
||||
case OPERATION_ACTIVATE:
|
||||
err = esp_rmaker_handle_set_params(scene->action.data, scene->action.data_len, ESP_RMAKER_REQ_SRC_SCENE_ACTIVATE);
|
||||
break;
|
||||
|
||||
case OPERATION_DEACTIVATE:
|
||||
if (scenes_priv_data->deactivate_support) {
|
||||
err = esp_rmaker_handle_set_params(scene->action.data, scene->action.data_len, ESP_RMAKER_REQ_SRC_SCENE_DEACTIVATE);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Deactivate operation not supported.");
|
||||
err = ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid Operation: %d", operation);
|
||||
err = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_scenes_parse_json(void *data, size_t data_len, esp_rmaker_req_src_t src,
|
||||
bool *report_params)
|
||||
{
|
||||
char id[MAX_ID_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||
scenes_operation_t operation = OPERATION_INVALID;
|
||||
int current_scene = 0;
|
||||
esp_rmaker_scene_t *scene = NULL;
|
||||
|
||||
/* Get details from JSON */
|
||||
jparse_ctx_t jctx;
|
||||
if (json_parse_start(&jctx, (char *)data, data_len) != 0) {
|
||||
ESP_LOGE(TAG, "Json parse start failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Parse all scenes */
|
||||
while(json_arr_get_object(&jctx, current_scene) == 0) {
|
||||
/* Get ID */
|
||||
json_obj_get_string(&jctx, "id", id, sizeof(id));
|
||||
if (strlen(id) <= 0) {
|
||||
ESP_LOGE(TAG, "ID not found in scene JSON");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Get operation */
|
||||
if (src == ESP_RMAKER_REQ_SRC_INIT) {
|
||||
/* Scene loaded from NVS. Add it */
|
||||
operation = OPERATION_ADD;
|
||||
} else {
|
||||
operation = esp_rmaker_scenes_parse_operation(&jctx, id);
|
||||
if (operation == OPERATION_INVALID) {
|
||||
ESP_LOGE(TAG, "Error getting operation");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find/Create new scene */
|
||||
scene = esp_rmaker_scenes_find_or_create(&jctx, id, operation);
|
||||
if (!scene) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Get other scene details */
|
||||
if (operation == OPERATION_ADD || operation == OPERATION_EDIT) {
|
||||
/* Get info and flags */
|
||||
esp_rmaker_scenes_parse_info_and_flags(&jctx, &scene->info, &scene->flags);
|
||||
|
||||
/* Get action */
|
||||
esp_rmaker_scenes_parse_action(&jctx, &scene->action);
|
||||
}
|
||||
|
||||
/* Set report_params */
|
||||
if (operation == OPERATION_ADD || operation == OPERATION_EDIT || operation == OPERATION_REMOVE) {
|
||||
*report_params = true;
|
||||
} else {
|
||||
*report_params = false;
|
||||
}
|
||||
|
||||
/* Perform operation */
|
||||
esp_rmaker_scenes_perform_operation(scene, operation);
|
||||
|
||||
cleanup:
|
||||
json_arr_leave_object(&jctx);
|
||||
current_scene++;
|
||||
}
|
||||
json_parse_end(&jctx);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t __esp_rmaker_scenes_get_params(char *buf, size_t *buf_size)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
esp_rmaker_scene_t *scene = scenes_priv_data->scenes_list;
|
||||
json_gen_str_t jstr;
|
||||
json_gen_str_start(&jstr, buf, *buf_size, NULL, NULL);
|
||||
json_gen_start_array(&jstr);
|
||||
|
||||
while (scene) {
|
||||
json_gen_start_object(&jstr);
|
||||
|
||||
/* Add details */
|
||||
json_gen_obj_set_string(&jstr, "name", scene->name);
|
||||
json_gen_obj_set_string(&jstr, "id", scene->id);
|
||||
/* If info and flags is not zero, add it. */
|
||||
if (scene->info != NULL) {
|
||||
json_gen_obj_set_string(&jstr, "info", scene->info);
|
||||
}
|
||||
if (scene->flags != 0) {
|
||||
json_gen_obj_set_int(&jstr, "flags", scene->flags);
|
||||
}
|
||||
|
||||
/* Add action */
|
||||
json_gen_push_object_str(&jstr, "action", scene->action.data);
|
||||
|
||||
json_gen_end_object(&jstr);
|
||||
|
||||
/* Go to next scene */
|
||||
scene = scene->next;
|
||||
}
|
||||
|
||||
if (json_gen_end_array(&jstr) < 0) {
|
||||
ESP_LOGE(TAG, "Buffer size %lu not sufficient for reporting Scenes Params.", (unsigned long) *buf_size);
|
||||
err = ESP_ERR_NO_MEM;
|
||||
}
|
||||
*buf_size = json_gen_str_end(&jstr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static char *esp_rmaker_scenes_get_params(void)
|
||||
{
|
||||
size_t req_size = 0;
|
||||
esp_err_t err = __esp_rmaker_scenes_get_params(NULL, &req_size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get required size for scenes JSON.");
|
||||
return NULL;
|
||||
}
|
||||
char *data = MEM_CALLOC_EXTRAM(1, req_size);
|
||||
if (!data) {
|
||||
ESP_LOGE(TAG, "Failed to allocate %lu bytes for scenes.", (unsigned long) req_size);
|
||||
return NULL;
|
||||
}
|
||||
err = __esp_rmaker_scenes_get_params(data, &req_size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error occured while trying to populate sceness JSON.");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_scenes_report_params(void)
|
||||
{
|
||||
char *data = esp_rmaker_scenes_get_params();
|
||||
esp_rmaker_param_val_t val = {
|
||||
.type = RMAKER_VAL_TYPE_ARRAY,
|
||||
.val.s = data,
|
||||
};
|
||||
esp_rmaker_param_t *param = esp_rmaker_device_get_param_by_type(scenes_priv_data->scenes_service, ESP_RMAKER_PARAM_SCENES);
|
||||
esp_rmaker_param_update_and_report(param, val);
|
||||
|
||||
free(data);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||
{
|
||||
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_SCENES) != 0) {
|
||||
ESP_LOGE(TAG, "Got callback for invalid param with name %s and type %s", esp_rmaker_param_get_name(param), esp_rmaker_param_get_type(param));
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (strlen(val.val.s) <= 0) {
|
||||
ESP_LOGI(TAG, "Invalid length for params: %lu", (unsigned long) strlen(val.val.s));
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
bool report_params = false;
|
||||
esp_rmaker_scenes_parse_json(val.val.s, strlen(val.val.s), ctx->src, &report_params);
|
||||
if (ctx->src != ESP_RMAKER_REQ_SRC_INIT) {
|
||||
/* Since this is a persisting param, we get a write_cb while booting up. We need not report the param when the source is 'init' as this will get reported when the device first reports all the params. */
|
||||
if (report_params) {
|
||||
/* report_params is only set for add, edit, remove operations. The scenes params are not changed for
|
||||
activate, deactivate operations. So need to report the params in that case. */
|
||||
esp_rmaker_scenes_report_params();
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_scenes_enable(void)
|
||||
{
|
||||
scenes_priv_data = (esp_rmaker_scenes_priv_data_t *)MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_scenes_priv_data_t));
|
||||
if (!scenes_priv_data) {
|
||||
ESP_LOGE(TAG, "Couldn't allocate scenes_priv_data");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_SCENES_DEACTIVATE_SUPPORT
|
||||
scenes_priv_data->deactivate_support = CONFIG_ESP_RMAKER_SCENES_DEACTIVATE_SUPPORT;
|
||||
#endif
|
||||
|
||||
scenes_priv_data->scenes_service = esp_rmaker_create_scenes_service("Scenes", write_cb, NULL, MAX_SCENES, scenes_priv_data->deactivate_support, NULL);
|
||||
if (!scenes_priv_data->scenes_service) {
|
||||
ESP_LOGE(TAG, "Failed to create Scenes Service");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), scenes_priv_data->scenes_service);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to add Scenes Service");
|
||||
return err;
|
||||
}
|
||||
ESP_LOGD(TAG, "Scenes Service Enabled");
|
||||
return err;
|
||||
}
|
||||
983
components/esp_rainmaker/src/core/esp_rmaker_schedule.c
Normal file
983
components/esp_rainmaker/src/core/esp_rmaker_schedule.c
Normal file
@@ -0,0 +1,983 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <json_parser.h>
|
||||
#include <esp_rmaker_work_queue.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <esp_rmaker_internal.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <esp_rmaker_standard_services.h>
|
||||
#include <esp_rmaker_standard_types.h>
|
||||
#include <esp_rmaker_schedule.h>
|
||||
#include <esp_schedule.h>
|
||||
|
||||
#define MAX_ID_LEN 8
|
||||
#define MAX_NAME_LEN 32
|
||||
#define MAX_INFO_LEN 128
|
||||
#define MAX_OPERATION_LEN 10
|
||||
#define TIME_SYNC_DELAY 10 /* 10 seconds */
|
||||
#define MAX_SCHEDULES CONFIG_ESP_RMAKER_SCHEDULING_MAX_SCHEDULES
|
||||
|
||||
static const char *TAG = "esp_rmaker_schedule";
|
||||
|
||||
typedef enum trigger_type {
|
||||
TRIGGER_TYPE_INVALID = 0,
|
||||
TRIGGER_TYPE_DAYS_OF_WEEK,
|
||||
TRIGGER_TYPE_DATE,
|
||||
TRIGGER_TYPE_RELATIVE,
|
||||
} trigger_type_t;
|
||||
|
||||
typedef struct esp_rmaker_schedule_trigger {
|
||||
trigger_type_t type;
|
||||
/* Relative Seconds */
|
||||
int relative_seconds;
|
||||
/* Minutes from 12am */
|
||||
uint16_t minutes;
|
||||
struct {
|
||||
/* 'OR' list of days or the week. Eg. Monday = 0b1, Tuesday = 0b10 */
|
||||
uint8_t repeat_days;
|
||||
} day;
|
||||
struct {
|
||||
/* Day of the month */
|
||||
uint8_t day;
|
||||
/* 'OR' list of months of the year. Eg. January = 0b1, February = 0b10.
|
||||
0 for next date (either this month or next). */
|
||||
uint16_t repeat_months;
|
||||
uint16_t year;
|
||||
bool repeat_every_year;
|
||||
} date;
|
||||
/* Used for non repeating schedules */
|
||||
int64_t next_timestamp;
|
||||
} esp_rmaker_schedule_trigger_t;
|
||||
|
||||
typedef struct esp_rmaker_schedule_action {
|
||||
void *data;
|
||||
size_t data_len;
|
||||
} esp_rmaker_schedule_action_t;
|
||||
|
||||
typedef struct esp_rmaker_schedule {
|
||||
char name[MAX_NAME_LEN + 1]; /* +1 for NULL termination */
|
||||
char id[MAX_ID_LEN + 1]; /* +1 for NULL termination */
|
||||
/* Info is used to store additional information, it is limited to 128 bytes. */
|
||||
char *info;
|
||||
/* Index is used in the callback to get back the schedule. */
|
||||
long index;
|
||||
/* Flags are used to identify the schedule. Eg. timing, countdown */
|
||||
uint32_t flags;
|
||||
bool enabled;
|
||||
esp_schedule_handle_t handle;
|
||||
esp_rmaker_schedule_action_t action;
|
||||
esp_rmaker_schedule_trigger_t trigger;
|
||||
esp_schedule_validity_t validity;
|
||||
struct esp_rmaker_schedule *next;
|
||||
} esp_rmaker_schedule_t;
|
||||
|
||||
enum time_sync_state {
|
||||
TIME_SYNC_NOT_STARTED,
|
||||
TIME_SYNC_STARTED,
|
||||
TIME_SYNC_DONE,
|
||||
};
|
||||
|
||||
typedef enum schedule_operation {
|
||||
OPERATION_INVALID,
|
||||
OPERATION_ADD,
|
||||
OPERATION_EDIT,
|
||||
OPERATION_REMOVE,
|
||||
OPERATION_ENABLE,
|
||||
OPERATION_DISABLE,
|
||||
} schedule_operation_t;
|
||||
|
||||
typedef struct {
|
||||
esp_rmaker_schedule_t *schedule_list;
|
||||
int total_schedules;
|
||||
/* This index just increases. This makes sure it is unique for the given schedules */
|
||||
int32_t index;
|
||||
esp_rmaker_device_t *schedule_service;
|
||||
TimerHandle_t time_sync_timer;
|
||||
enum time_sync_state time_sync_state;;
|
||||
} esp_rmaker_schedule_priv_data_t;
|
||||
|
||||
static esp_rmaker_schedule_priv_data_t *schedule_priv_data;
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_operation_enable(esp_rmaker_schedule_t *schedule);
|
||||
static esp_err_t esp_rmaker_schedule_operation_disable(esp_rmaker_schedule_t *schedule);
|
||||
static esp_err_t esp_rmaker_schedule_report_params(void);
|
||||
static esp_err_t esp_rmaker_schedule_timesync_timer_deinit(void);
|
||||
static esp_err_t esp_rmaker_schedule_timesync_timer_start(void);
|
||||
|
||||
static void esp_rmaker_schedule_free(esp_rmaker_schedule_t *schedule)
|
||||
{
|
||||
if (!schedule) {
|
||||
return;
|
||||
}
|
||||
if (schedule->action.data) {
|
||||
free(schedule->action.data);
|
||||
}
|
||||
if (schedule->info) {
|
||||
free(schedule->info);
|
||||
}
|
||||
free(schedule);
|
||||
}
|
||||
|
||||
static esp_rmaker_schedule_t *esp_rmaker_schedule_get_schedule_from_id(const char *id)
|
||||
{
|
||||
if (!id) {
|
||||
return NULL;
|
||||
}
|
||||
esp_rmaker_schedule_t *schedule = schedule_priv_data->schedule_list;
|
||||
while(schedule) {
|
||||
if (strncmp(id, schedule->id, sizeof(schedule->id)) == 0) {
|
||||
ESP_LOGD(TAG, "Schedule with id %s found in list for get.", id);
|
||||
return schedule;
|
||||
}
|
||||
schedule = schedule->next;
|
||||
}
|
||||
ESP_LOGD(TAG, "Schedule with id %s not found in list for get.", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static esp_rmaker_schedule_t *esp_rmaker_schedule_get_schedule_from_index(long index)
|
||||
{
|
||||
esp_rmaker_schedule_t *schedule = schedule_priv_data->schedule_list;
|
||||
while(schedule) {
|
||||
if (schedule->index == index) {
|
||||
ESP_LOGD(TAG, "Schedule with index %ld found in list for get.", index);
|
||||
return schedule;
|
||||
}
|
||||
schedule = schedule->next;
|
||||
}
|
||||
ESP_LOGD(TAG, "Schedule with index %ld not found in list for get.", index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_add_to_list(esp_rmaker_schedule_t *schedule)
|
||||
{
|
||||
if (!schedule) {
|
||||
ESP_LOGE(TAG, "Schedule is NULL. Not adding to list.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (esp_rmaker_schedule_get_schedule_from_id(schedule->id) != NULL) {
|
||||
ESP_LOGI(TAG, "Schedule with id %s already added to list. Not adding again.", schedule->id);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Parse list */
|
||||
esp_rmaker_schedule_t *prev_schedule = schedule_priv_data->schedule_list;
|
||||
while(prev_schedule) {
|
||||
if (prev_schedule->next) {
|
||||
prev_schedule = prev_schedule->next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add to list */
|
||||
if (prev_schedule) {
|
||||
prev_schedule->next = schedule;
|
||||
} else {
|
||||
schedule_priv_data->schedule_list = schedule;
|
||||
}
|
||||
ESP_LOGD(TAG, "Schedule with id %s added to list.", schedule->id);
|
||||
schedule_priv_data->total_schedules++;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_remove_from_list(esp_rmaker_schedule_t *schedule)
|
||||
{
|
||||
if (!schedule) {
|
||||
ESP_LOGE(TAG, "Schedule is NULL. Not removing from list.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
/* Parse list */
|
||||
esp_rmaker_schedule_t *curr_schedule = schedule_priv_data->schedule_list;
|
||||
esp_rmaker_schedule_t *prev_schedule = curr_schedule;
|
||||
while(curr_schedule) {
|
||||
if (strncmp(schedule->id, curr_schedule->id, sizeof(schedule->id)) == 0) {
|
||||
ESP_LOGD(TAG, "Schedule with id %s found in list for removing", schedule->id);
|
||||
break;
|
||||
}
|
||||
prev_schedule = curr_schedule;
|
||||
curr_schedule = curr_schedule->next;
|
||||
}
|
||||
if (!curr_schedule) {
|
||||
ESP_LOGE(TAG, "Schedule with id %s not found in list. Not removing.", schedule->id);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* Remove from list */
|
||||
if (curr_schedule == schedule_priv_data->schedule_list) {
|
||||
schedule_priv_data->schedule_list = curr_schedule->next;
|
||||
} else {
|
||||
prev_schedule->next = curr_schedule->next;
|
||||
}
|
||||
schedule_priv_data->total_schedules--;
|
||||
ESP_LOGD(TAG, "Schedule with id %s removed from list.", schedule->id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static bool esp_rmaker_schedule_is_expired(esp_rmaker_schedule_t *schedule)
|
||||
{
|
||||
time_t current_timestamp = 0;
|
||||
struct tm current_time = {0};
|
||||
time(¤t_timestamp);
|
||||
localtime_r(¤t_timestamp, ¤t_time);
|
||||
|
||||
if (schedule->trigger.type == TRIGGER_TYPE_RELATIVE) {
|
||||
if (schedule->trigger.next_timestamp > 0 && schedule->trigger.next_timestamp <= current_timestamp) {
|
||||
/* Relative seconds based schedule has expired */
|
||||
return true;
|
||||
}
|
||||
} else if (schedule->trigger.type == TRIGGER_TYPE_DAYS_OF_WEEK) {
|
||||
if (schedule->trigger.day.repeat_days == 0) {
|
||||
if (schedule->trigger.next_timestamp > 0 && schedule->trigger.next_timestamp <= current_timestamp) {
|
||||
/* One time schedule has expired */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (schedule->trigger.type == TRIGGER_TYPE_DATE) {
|
||||
if (schedule->trigger.date.repeat_months == 0) {
|
||||
if (schedule->trigger.next_timestamp > 0 && schedule->trigger.next_timestamp <= current_timestamp) {
|
||||
/* One time schedule has expired */
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (schedule->trigger.date.repeat_every_year == true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct tm schedule_time = {0};
|
||||
localtime_r(¤t_timestamp, &schedule_time);
|
||||
schedule_time.tm_sec = 0;
|
||||
schedule_time.tm_min = schedule->trigger.minutes;
|
||||
schedule_time.tm_hour = 0;
|
||||
schedule_time.tm_mday = schedule->trigger.date.day;
|
||||
/* For expiry, just check the last month of the repeat_months. */
|
||||
/* '-1' because struct tm has months starting from 0 and we have months starting from 1. */
|
||||
schedule_time.tm_mon = fls(schedule->trigger.date.repeat_months) - 1;
|
||||
/* '-1900' because struct tm has number of years after 1900 */
|
||||
schedule_time.tm_year = schedule->trigger.date.year - 1900;
|
||||
time_t schedule_timestamp = mktime(&schedule_time);
|
||||
|
||||
if (schedule_timestamp < current_timestamp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_process_action(esp_rmaker_schedule_action_t *action)
|
||||
{
|
||||
return esp_rmaker_handle_set_params(action->data, action->data_len, ESP_RMAKER_REQ_SRC_SCHEDULE);
|
||||
}
|
||||
|
||||
static void esp_rmaker_schedule_trigger_work_cb(void *priv_data)
|
||||
{
|
||||
long index = (long)priv_data;
|
||||
esp_rmaker_schedule_t *schedule = esp_rmaker_schedule_get_schedule_from_index(index);
|
||||
if (!schedule) {
|
||||
ESP_LOGE(TAG, "Schedule with index %ld not found for trigger work callback", index);
|
||||
return;
|
||||
}
|
||||
esp_rmaker_schedule_process_action(&schedule->action);
|
||||
if (esp_rmaker_schedule_is_expired(schedule)) {
|
||||
/* This schedule does not repeat anymore. Disable it and report the params. */
|
||||
esp_rmaker_schedule_operation_disable(schedule);
|
||||
esp_rmaker_schedule_report_params();
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_rmaker_schedule_trigger_common_cb(esp_schedule_handle_t handle, void *priv_data)
|
||||
{
|
||||
/* Adding to work queue to change the context from timer's task. */
|
||||
esp_rmaker_work_queue_add_task(esp_rmaker_schedule_trigger_work_cb, priv_data);
|
||||
}
|
||||
|
||||
static void esp_rmaker_schedule_timestamp_common_cb(esp_schedule_handle_t handle, uint32_t next_timestamp, void *priv_data)
|
||||
{
|
||||
long index = (long)priv_data;
|
||||
esp_rmaker_schedule_t *schedule = esp_rmaker_schedule_get_schedule_from_index(index);
|
||||
if (!schedule) {
|
||||
ESP_LOGE(TAG, "Schedule with index %ld not found for timestamp callback", index);
|
||||
return;
|
||||
}
|
||||
schedule->trigger.next_timestamp = next_timestamp;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_prepare_config(esp_rmaker_schedule_t *schedule, esp_schedule_config_t *schedule_config)
|
||||
{
|
||||
if (!schedule || !schedule_config) {
|
||||
ESP_LOGE(TAG, "schedule or schedule_config is NULL.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (schedule->trigger.type == TRIGGER_TYPE_RELATIVE) {
|
||||
schedule_config->trigger.next_scheduled_time_utc = (time_t)schedule->trigger.next_timestamp;
|
||||
schedule_config->trigger.relative_seconds = schedule->trigger.relative_seconds;
|
||||
schedule_config->trigger.type = ESP_SCHEDULE_TYPE_RELATIVE;
|
||||
schedule_config->trigger_cb = esp_rmaker_schedule_trigger_common_cb;
|
||||
schedule_config->timestamp_cb = esp_rmaker_schedule_timestamp_common_cb;
|
||||
} else {
|
||||
int hours = schedule->trigger.minutes / 60;
|
||||
int minutes = schedule->trigger.minutes % 60;
|
||||
schedule_config->trigger.hours = hours;
|
||||
schedule_config->trigger.minutes = minutes;
|
||||
schedule_config->trigger_cb = esp_rmaker_schedule_trigger_common_cb;
|
||||
|
||||
if (schedule->trigger.type == TRIGGER_TYPE_DAYS_OF_WEEK) {
|
||||
schedule_config->trigger.type = ESP_SCHEDULE_TYPE_DAYS_OF_WEEK;
|
||||
schedule_config->trigger.day.repeat_days = schedule->trigger.day.repeat_days;
|
||||
if (schedule->trigger.day.repeat_days == 0) {
|
||||
schedule_config->timestamp_cb = esp_rmaker_schedule_timestamp_common_cb;
|
||||
}
|
||||
} else if (schedule->trigger.type == TRIGGER_TYPE_DATE) {
|
||||
schedule_config->trigger.type = ESP_SCHEDULE_TYPE_DATE;
|
||||
schedule_config->trigger.date.day = schedule->trigger.date.day;
|
||||
schedule_config->trigger.date.repeat_months = schedule->trigger.date.repeat_months;
|
||||
schedule_config->trigger.date.year = schedule->trigger.date.year;
|
||||
schedule_config->trigger.date.repeat_every_year = schedule->trigger.date.repeat_every_year;
|
||||
if (schedule->trigger.date.repeat_months == 0) {
|
||||
schedule_config->timestamp_cb = esp_rmaker_schedule_timestamp_common_cb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In esp_schedule, name should be unique and is used as the primary key.
|
||||
We are setting the id in esp_rmaker_schedule as the name in esp_schedule */
|
||||
strlcpy(schedule_config->name, schedule->id, sizeof(schedule_config->name));
|
||||
/* Just passing the schedule pointer as priv_data could create a race condition between the schedule getting a
|
||||
callback and the schedule getting removed. Using this unique index as the priv_data solves it to some extent. */
|
||||
schedule_config->priv_data = (void *)schedule->index;
|
||||
schedule_config->validity = schedule->validity;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_add(esp_rmaker_schedule_t *schedule)
|
||||
{
|
||||
esp_schedule_config_t schedule_config = {0};
|
||||
esp_rmaker_schedule_prepare_config(schedule, &schedule_config);
|
||||
|
||||
schedule->handle = esp_schedule_create(&schedule_config);
|
||||
if (schedule->handle == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_operation_add(esp_rmaker_schedule_t *schedule)
|
||||
{
|
||||
esp_err_t ret = esp_rmaker_schedule_add(schedule);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = esp_rmaker_schedule_add_to_list(schedule);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_operation_edit(esp_rmaker_schedule_t *schedule)
|
||||
{
|
||||
esp_schedule_config_t schedule_config = {0};
|
||||
esp_rmaker_schedule_prepare_config(schedule, &schedule_config);
|
||||
|
||||
esp_err_t ret = esp_schedule_edit(schedule->handle, &schedule_config);
|
||||
if (schedule->enabled == true) {
|
||||
/* If the schedule is already enabled, disable it and enable it again so that the new changes after the
|
||||
edit are reflected. */
|
||||
esp_rmaker_schedule_operation_disable(schedule);
|
||||
esp_rmaker_schedule_operation_enable(schedule);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_remove(esp_rmaker_schedule_t *schedule)
|
||||
{
|
||||
return esp_schedule_delete(schedule->handle);
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_operation_remove(esp_rmaker_schedule_t *schedule)
|
||||
{
|
||||
esp_err_t ret = esp_rmaker_schedule_remove_from_list(schedule);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = esp_rmaker_schedule_remove(schedule);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void esp_rmaker_schedule_timesync_timer_work_cb(void *priv_data)
|
||||
{
|
||||
if (esp_rmaker_time_check() != true) {
|
||||
esp_rmaker_schedule_timesync_timer_start();
|
||||
return;
|
||||
}
|
||||
esp_rmaker_schedule_timesync_timer_deinit();
|
||||
schedule_priv_data->time_sync_state = TIME_SYNC_DONE;
|
||||
ESP_LOGI(TAG, "Time is synchronised now. Enabling the schedules.");
|
||||
esp_rmaker_schedule_t *schedule = schedule_priv_data->schedule_list;
|
||||
while (schedule) {
|
||||
if (schedule->enabled == true) {
|
||||
esp_rmaker_schedule_operation_enable(schedule);
|
||||
}
|
||||
schedule = schedule->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_rmaker_schedule_timesync_timer_cb(TimerHandle_t timer)
|
||||
{
|
||||
esp_rmaker_work_queue_add_task(esp_rmaker_schedule_timesync_timer_work_cb, NULL);
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_timesync_timer_init(void)
|
||||
{
|
||||
schedule_priv_data->time_sync_timer = xTimerCreate("esp_rmaker_schedule", (TIME_SYNC_DELAY * 1000) / portTICK_PERIOD_MS, pdFALSE, NULL, esp_rmaker_schedule_timesync_timer_cb);
|
||||
if (schedule_priv_data->time_sync_timer == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to create timer for time sync");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_timesync_timer_deinit(void)
|
||||
{
|
||||
xTimerDelete(schedule_priv_data->time_sync_timer, portMAX_DELAY);
|
||||
schedule_priv_data->time_sync_timer = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_timesync_timer_start(void)
|
||||
{
|
||||
xTimerStart(schedule_priv_data->time_sync_timer, portMAX_DELAY);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_operation_enable(esp_rmaker_schedule_t *schedule)
|
||||
{
|
||||
/* Setting enabled to true even if time is not synced yet. This reports the correct enabled state when reporting the schedules.*/
|
||||
schedule->enabled = true;
|
||||
|
||||
/* Check for time sync */
|
||||
if (schedule_priv_data->time_sync_state == TIME_SYNC_NOT_STARTED) {
|
||||
if (esp_rmaker_time_check() != true) {
|
||||
ESP_LOGI(TAG, "Time is not synchronised yet. The schedule will actually be enabled when time is synchronised. This may take time.");
|
||||
esp_rmaker_schedule_timesync_timer_init();
|
||||
esp_rmaker_schedule_timesync_timer_start();
|
||||
schedule_priv_data->time_sync_state = TIME_SYNC_STARTED;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
schedule_priv_data->time_sync_state = TIME_SYNC_DONE;
|
||||
} else if (schedule_priv_data->time_sync_state == TIME_SYNC_STARTED) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (esp_rmaker_schedule_is_expired(schedule)) {
|
||||
/* This schedule does not repeat anymore. Disable it. */
|
||||
/* While time sync is happening, it might be possible that this schedule will be shown as enabled, but actually it is disabled. */
|
||||
ESP_LOGI(TAG, "Schedule with id %s does not repeat anymore. Disabling it.", schedule->id);
|
||||
esp_rmaker_schedule_operation_disable(schedule);
|
||||
/* Since the enabled state has been changed, report this */
|
||||
esp_rmaker_schedule_report_params();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Time is synced. Enable the schedule */
|
||||
return esp_schedule_enable(schedule->handle);
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_operation_disable(esp_rmaker_schedule_t *schedule)
|
||||
{
|
||||
esp_err_t ret = esp_schedule_disable(schedule->handle);
|
||||
schedule->trigger.next_timestamp = 0;
|
||||
schedule->enabled = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
schedule_operation_t esp_rmaker_schedule_get_operation_from_str(char *operation)
|
||||
{
|
||||
if (!operation) {
|
||||
return OPERATION_INVALID;
|
||||
}
|
||||
if (strncmp(operation, "add", strlen(operation)) == 0) {
|
||||
return OPERATION_ADD;
|
||||
} else if (strncmp(operation, "edit", strlen(operation)) == 0) {
|
||||
return OPERATION_EDIT;
|
||||
} else if (strncmp(operation, "remove", strlen(operation)) == 0) {
|
||||
return OPERATION_REMOVE;
|
||||
} else if (strncmp(operation, "enable", strlen(operation)) == 0) {
|
||||
return OPERATION_ENABLE;
|
||||
} else if (strncmp(operation, "disable", strlen(operation)) == 0) {
|
||||
return OPERATION_DISABLE;
|
||||
}
|
||||
return OPERATION_INVALID;
|
||||
}
|
||||
|
||||
static schedule_operation_t esp_rmaker_schedule_parse_operation(jparse_ctx_t *jctx, char *id)
|
||||
{
|
||||
char operation_str[MAX_OPERATION_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||
schedule_operation_t operation = OPERATION_INVALID;
|
||||
json_obj_get_string(jctx, "operation", operation_str, sizeof(operation_str));
|
||||
if (strlen(operation_str) <= 0) {
|
||||
ESP_LOGE(TAG, "Operation not found in schedule with id: %s", id);
|
||||
return operation;
|
||||
}
|
||||
operation = esp_rmaker_schedule_get_operation_from_str(operation_str);
|
||||
if (operation == OPERATION_EDIT) {
|
||||
/* Get schedule temporarily */
|
||||
if (esp_rmaker_schedule_get_schedule_from_id(id) == NULL) {
|
||||
/* Operation is edit, but schedule not present already. Consider this as add. */
|
||||
ESP_LOGD(TAG, "Operation is edit, but schedule with id %s not found. Changing the operation to add.", id);
|
||||
operation = OPERATION_ADD;
|
||||
}
|
||||
} else if (operation == OPERATION_INVALID) {
|
||||
ESP_LOGE(TAG, "Invalid schedule operation found: %s", operation_str);
|
||||
}
|
||||
return operation;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_parse_action(jparse_ctx_t *jctx, esp_rmaker_schedule_action_t *action)
|
||||
{
|
||||
int data_len = 0;
|
||||
json_obj_get_object_strlen(jctx, "action", &data_len);
|
||||
if (data_len <= 0) {
|
||||
ESP_LOGD(TAG, "Action not found in JSON");
|
||||
return ESP_OK;
|
||||
}
|
||||
action->data_len = data_len + 1;
|
||||
|
||||
if (action->data) {
|
||||
free(action->data);
|
||||
}
|
||||
action->data = (void *)MEM_CALLOC_EXTRAM(1, action->data_len);
|
||||
if (!action->data) {
|
||||
ESP_LOGE(TAG, "Could not allocate action");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
json_obj_get_object_str(jctx, "action", action->data, action->data_len);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_parse_trigger(jparse_ctx_t *jctx, esp_rmaker_schedule_trigger_t *trigger)
|
||||
{
|
||||
int total_triggers = 0;
|
||||
int relative_seconds = 0, minutes = 0, repeat_days = 0, day = 0, repeat_months = 0, year = 0;
|
||||
bool repeat_every_year = false;
|
||||
int64_t timestamp = 0;
|
||||
trigger_type_t type = TRIGGER_TYPE_INVALID;
|
||||
if(json_obj_get_array(jctx, "triggers", &total_triggers) != 0) {
|
||||
ESP_LOGD(TAG, "Trigger not found in JSON");
|
||||
return ESP_OK;
|
||||
}
|
||||
if (total_triggers <= 0) {
|
||||
ESP_LOGD(TAG, "No triggers found in trigger array");
|
||||
json_obj_leave_array(jctx);
|
||||
return ESP_OK;
|
||||
}
|
||||
if(json_arr_get_object(jctx, 0) == 0) {
|
||||
json_obj_get_int64(jctx, "ts", ×tamp);
|
||||
if (json_obj_get_int(jctx, "rsec", &relative_seconds) == 0) {
|
||||
type = TRIGGER_TYPE_RELATIVE;
|
||||
} else {
|
||||
json_obj_get_int(jctx, "m", &minutes);
|
||||
/* Check if it is of type day */
|
||||
if (json_obj_get_int(jctx, "d", &repeat_days) == 0) {
|
||||
type = TRIGGER_TYPE_DAYS_OF_WEEK;
|
||||
}
|
||||
if (json_obj_get_int(jctx, "dd", &day) == 0) {
|
||||
type = TRIGGER_TYPE_DATE;
|
||||
json_obj_get_int(jctx, "mm", &repeat_months);
|
||||
json_obj_get_int(jctx, "yy", &year);
|
||||
json_obj_get_bool(jctx, "r", &repeat_every_year);
|
||||
}
|
||||
}
|
||||
json_arr_leave_object(jctx);
|
||||
}
|
||||
json_obj_leave_array(jctx);
|
||||
|
||||
trigger->type = type;
|
||||
trigger->relative_seconds = relative_seconds;
|
||||
trigger->minutes = minutes;
|
||||
trigger->day.repeat_days = repeat_days;
|
||||
trigger->date.day = day;
|
||||
trigger->date.repeat_months = repeat_months;
|
||||
trigger->date.year = year;
|
||||
trigger->date.repeat_every_year = repeat_every_year;
|
||||
trigger->next_timestamp = timestamp;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_parse_info_and_flags(jparse_ctx_t *jctx, char **info, uint32_t *flags)
|
||||
{
|
||||
char _info[MAX_INFO_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||
int _flags = 0;
|
||||
|
||||
int err_code = json_obj_get_string(jctx, "info", _info, sizeof(_info));
|
||||
if (err_code == OS_SUCCESS) {
|
||||
if (*info) {
|
||||
free(*info);
|
||||
*info = NULL;
|
||||
}
|
||||
|
||||
int len = strlen(_info);
|
||||
if (len > 0) {
|
||||
/* +1 for NULL termination */
|
||||
*info = (char *)MEM_CALLOC_EXTRAM(1, len + 1);
|
||||
if (*info) {
|
||||
memcpy(*info, _info, len + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err_code = json_obj_get_int(jctx, "flags", &_flags);
|
||||
if (err_code == OS_SUCCESS) {
|
||||
if (flags) {
|
||||
*flags = _flags;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_parse_validity(jparse_ctx_t *jctx, esp_schedule_validity_t *validity)
|
||||
{
|
||||
if (json_obj_get_object(jctx, "validity") == OS_SUCCESS) {
|
||||
json_obj_get_int64(jctx, "start", (int64_t *)&validity->start_time);
|
||||
json_obj_get_int64(jctx, "end", (int64_t *)&validity->end_time);
|
||||
json_obj_leave_object(jctx);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_rmaker_schedule_t *esp_rmaker_schedule_find_or_create(jparse_ctx_t *jctx, char *id, schedule_operation_t operation)
|
||||
{
|
||||
char name[MAX_NAME_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||
esp_rmaker_schedule_t *schedule = NULL;
|
||||
if (operation == OPERATION_ADD) {
|
||||
/* Checking if schedule with same id already exists. */
|
||||
schedule = esp_rmaker_schedule_get_schedule_from_id(id);
|
||||
if (schedule) {
|
||||
ESP_LOGE(TAG, "Schedule with id %s already exists. Not adding it again.", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get name */
|
||||
json_obj_get_string(jctx, "name", name, sizeof(name));
|
||||
if (strlen(name) <= 0) {
|
||||
ESP_LOGE(TAG, "Name not found for schedule with id: %s", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This is a new schedule. Fill it. */
|
||||
schedule = (esp_rmaker_schedule_t *)MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_schedule_t));
|
||||
if (!schedule) {
|
||||
ESP_LOGE(TAG, "Couldn't allocate schedule with id: %s", id);
|
||||
return NULL;
|
||||
}
|
||||
strlcpy(schedule->id, id, sizeof(schedule->id));
|
||||
strlcpy(schedule->name, name, sizeof(schedule->name));
|
||||
schedule->index = schedule_priv_data->index++;
|
||||
} else {
|
||||
/* This schedule should already be present */
|
||||
schedule = esp_rmaker_schedule_get_schedule_from_id(id);
|
||||
if (!schedule) {
|
||||
ESP_LOGE(TAG, "Schedule with id %s not found", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get name */
|
||||
if (operation == OPERATION_EDIT) {
|
||||
json_obj_get_string(jctx, "name", name, sizeof(name));
|
||||
if (strlen(name) > 0) {
|
||||
/* If there is name in the request, replace the name in the schedule with this new one */
|
||||
memset(schedule->name, 0, sizeof(schedule->name));
|
||||
strlcpy(schedule->name, name, sizeof(schedule->name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return schedule;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_perform_operation(esp_rmaker_schedule_t *schedule, schedule_operation_t operation, bool enabled)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
switch (operation) {
|
||||
case OPERATION_ADD:
|
||||
if (schedule_priv_data->total_schedules < MAX_SCHEDULES) {
|
||||
esp_rmaker_schedule_operation_add(schedule);
|
||||
if (enabled == true) {
|
||||
esp_rmaker_schedule_operation_enable(schedule);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Max schedules (%d) reached. Not adding this schedule with id %s", MAX_SCHEDULES,
|
||||
schedule->id);
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPERATION_EDIT:
|
||||
esp_rmaker_schedule_operation_edit(schedule);
|
||||
break;
|
||||
|
||||
case OPERATION_REMOVE:
|
||||
esp_rmaker_schedule_operation_remove(schedule);
|
||||
esp_rmaker_schedule_free(schedule);
|
||||
break;
|
||||
|
||||
case OPERATION_ENABLE:
|
||||
esp_rmaker_schedule_operation_enable(schedule);
|
||||
break;
|
||||
|
||||
case OPERATION_DISABLE:
|
||||
esp_rmaker_schedule_operation_disable(schedule);
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid Operation: %d", operation);
|
||||
err = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_parse_json(void *data, size_t data_len, esp_rmaker_req_src_t src)
|
||||
{
|
||||
char id[MAX_ID_LEN + 1] = {0}; /* +1 for NULL termination */
|
||||
schedule_operation_t operation = OPERATION_INVALID;
|
||||
bool enabled = true;
|
||||
int current_schedule = 0;
|
||||
esp_rmaker_schedule_t *schedule = NULL;
|
||||
|
||||
/* Get details from JSON */
|
||||
jparse_ctx_t jctx;
|
||||
if (json_parse_start(&jctx, (char *)data, data_len) != 0) {
|
||||
ESP_LOGE(TAG, "Json parse start failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Parse all schedules */
|
||||
while(json_arr_get_object(&jctx, current_schedule) == 0) {
|
||||
/* Get ID */
|
||||
json_obj_get_string(&jctx, "id", id, sizeof(id));
|
||||
if (strlen(id) <= 0) {
|
||||
ESP_LOGE(TAG, "ID not found in schedule JSON");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Get operation */
|
||||
if (src == ESP_RMAKER_REQ_SRC_INIT) {
|
||||
/* Schedule loaded from NVS. Add it */
|
||||
operation = OPERATION_ADD;
|
||||
} else {
|
||||
operation = esp_rmaker_schedule_parse_operation(&jctx, id);
|
||||
if (operation == OPERATION_INVALID) {
|
||||
ESP_LOGE(TAG, "Error getting operation");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find/Create new schedule */
|
||||
schedule = esp_rmaker_schedule_find_or_create(&jctx, id, operation);
|
||||
if (!schedule) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Get other schedule details */
|
||||
if (operation == OPERATION_ADD || operation == OPERATION_EDIT) {
|
||||
/* Get enabled state */
|
||||
if (operation == OPERATION_ADD) {
|
||||
/* If loaded from NVS, check for previous enabled state. If new schedule, enable it */
|
||||
if (src == ESP_RMAKER_REQ_SRC_INIT) {
|
||||
json_obj_get_bool(&jctx, "enabled", &enabled);
|
||||
} else {
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get action */
|
||||
esp_rmaker_schedule_parse_action(&jctx, &schedule->action);
|
||||
|
||||
/* Get trigger */
|
||||
/* There is only one trigger for now. If more triggers are added, then they should be parsed here in a loop */
|
||||
esp_rmaker_schedule_parse_trigger(&jctx, &schedule->trigger);
|
||||
|
||||
/* Get info and flags */
|
||||
esp_rmaker_schedule_parse_info_and_flags(&jctx, &schedule->info, &schedule->flags);
|
||||
|
||||
/* Get validity */
|
||||
esp_rmaker_schedule_parse_validity(&jctx, &schedule->validity);
|
||||
}
|
||||
|
||||
/* Perform operation */
|
||||
esp_rmaker_schedule_perform_operation(schedule, operation, enabled);
|
||||
|
||||
cleanup:
|
||||
json_arr_leave_object(&jctx);
|
||||
current_schedule++;
|
||||
}
|
||||
json_parse_end(&jctx);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t __esp_rmaker_schedule_get_params(char *buf, size_t *buf_size)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
esp_rmaker_schedule_t *schedule = schedule_priv_data->schedule_list;
|
||||
json_gen_str_t jstr;
|
||||
json_gen_str_start(&jstr, buf, *buf_size, NULL, NULL);
|
||||
json_gen_start_array(&jstr);
|
||||
while (schedule) {
|
||||
json_gen_start_object(&jstr);
|
||||
|
||||
/* Add details */
|
||||
json_gen_obj_set_string(&jstr, "name", schedule->name);
|
||||
json_gen_obj_set_string(&jstr, "id", schedule->id);
|
||||
json_gen_obj_set_bool(&jstr, "enabled", schedule->enabled);
|
||||
/* If info and flags is not zero, add it. */
|
||||
if (schedule->info != NULL) {
|
||||
json_gen_obj_set_string(&jstr, "info", schedule->info);
|
||||
}
|
||||
if (schedule->flags != 0) {
|
||||
json_gen_obj_set_int(&jstr, "flags", schedule->flags);
|
||||
}
|
||||
/* Add validity */
|
||||
if (schedule->validity.start_time != 0 || schedule->validity.end_time != 0) {
|
||||
json_gen_push_object(&jstr, "validity");
|
||||
if (schedule->validity.start_time != 0) {
|
||||
json_gen_obj_set_int(&jstr, "start", schedule->validity.start_time);
|
||||
}
|
||||
if (schedule->validity.end_time != 0) {
|
||||
json_gen_obj_set_int(&jstr, "end", schedule->validity.end_time);
|
||||
}
|
||||
json_gen_pop_object(&jstr);
|
||||
}
|
||||
/* Add action */
|
||||
json_gen_push_object_str(&jstr, "action", schedule->action.data);
|
||||
|
||||
/* Add trigger */
|
||||
json_gen_push_array(&jstr, "triggers");
|
||||
json_gen_start_object(&jstr);
|
||||
if (schedule->trigger.type == TRIGGER_TYPE_RELATIVE) {
|
||||
json_gen_obj_set_int(&jstr, "rsec", schedule->trigger.relative_seconds);
|
||||
json_gen_obj_set_int(&jstr, "ts", schedule->trigger.next_timestamp);
|
||||
} else {
|
||||
json_gen_obj_set_int(&jstr, "m", schedule->trigger.minutes);
|
||||
if (schedule->trigger.type == TRIGGER_TYPE_DAYS_OF_WEEK) {
|
||||
json_gen_obj_set_int(&jstr, "d", schedule->trigger.day.repeat_days);
|
||||
if (schedule->trigger.day.repeat_days == 0) {
|
||||
json_gen_obj_set_int(&jstr, "ts", schedule->trigger.next_timestamp);
|
||||
}
|
||||
} else if (schedule->trigger.type == TRIGGER_TYPE_DATE) {
|
||||
json_gen_obj_set_int(&jstr, "dd", schedule->trigger.date.day);
|
||||
json_gen_obj_set_int(&jstr, "mm", schedule->trigger.date.repeat_months);
|
||||
json_gen_obj_set_int(&jstr, "yy", schedule->trigger.date.year);
|
||||
json_gen_obj_set_int(&jstr, "r", schedule->trigger.date.repeat_every_year);
|
||||
if (schedule->trigger.date.repeat_months == 0) {
|
||||
json_gen_obj_set_int(&jstr, "ts", schedule->trigger.next_timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
json_gen_end_object(&jstr);
|
||||
json_gen_pop_array(&jstr);
|
||||
|
||||
json_gen_end_object(&jstr);
|
||||
|
||||
/* Go to next schedule */
|
||||
schedule = schedule->next;
|
||||
}
|
||||
if (json_gen_end_array(&jstr) < 0) {
|
||||
ESP_LOGE(TAG, "Buffer size %lu not sufficient for reporting Schedule Params.", (unsigned long) *buf_size);
|
||||
err = ESP_ERR_NO_MEM;
|
||||
}
|
||||
*buf_size = json_gen_str_end(&jstr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static char *esp_rmaker_schedule_get_params(void)
|
||||
{
|
||||
size_t req_size = 0;
|
||||
esp_err_t err = __esp_rmaker_schedule_get_params(NULL, &req_size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get required size for schedules JSON.");
|
||||
return NULL;
|
||||
}
|
||||
char *data = MEM_CALLOC_EXTRAM(1, req_size);
|
||||
if (!data) {
|
||||
ESP_LOGE(TAG, "Failed to allocate %lu bytes for schedule.", (unsigned long) req_size);
|
||||
return NULL;
|
||||
}
|
||||
err = __esp_rmaker_schedule_get_params(data, &req_size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error occured while trying to populate schedules JSON.");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_schedule_report_params(void)
|
||||
{
|
||||
char *data = esp_rmaker_schedule_get_params();
|
||||
esp_rmaker_param_val_t val = {
|
||||
.type = RMAKER_VAL_TYPE_ARRAY,
|
||||
.val.s = data,
|
||||
};
|
||||
esp_rmaker_param_t *param = esp_rmaker_device_get_param_by_type(schedule_priv_data->schedule_service, ESP_RMAKER_PARAM_SCHEDULES);
|
||||
esp_rmaker_param_update_and_report(param, val);
|
||||
|
||||
free(data);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||
{
|
||||
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_SCHEDULES) != 0) {
|
||||
ESP_LOGE(TAG, "Got callback for invalid param with name %s and type %s", esp_rmaker_param_get_name(param), esp_rmaker_param_get_type(param));
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (strlen(val.val.s) <= 0) {
|
||||
ESP_LOGI(TAG, "Invalid length for params: %lu", (unsigned long) strlen(val.val.s));
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_rmaker_schedule_parse_json(val.val.s, strlen(val.val.s), ctx->src);
|
||||
if (ctx->src != ESP_RMAKER_REQ_SRC_INIT) {
|
||||
/* Since this is a persisting param, we get a write_cb while booting up. We need not report the param when the source is 'init' as this will get reported when the device first reports all the params. */
|
||||
esp_rmaker_schedule_report_params();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_schedule_enable(void)
|
||||
{
|
||||
schedule_priv_data = (esp_rmaker_schedule_priv_data_t *)MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_schedule_priv_data_t));
|
||||
if (!schedule_priv_data) {
|
||||
ESP_LOGE(TAG, "Couldn't allocate schedule_priv_data");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
esp_rmaker_time_sync_init(NULL);
|
||||
|
||||
esp_schedule_init(false, NULL, NULL);
|
||||
|
||||
schedule_priv_data->schedule_service = esp_rmaker_create_schedule_service("Schedule", write_cb, NULL, MAX_SCHEDULES, NULL);
|
||||
if (!schedule_priv_data->schedule_service) {
|
||||
ESP_LOGE(TAG, "Failed to create Schedule Service");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), schedule_priv_data->schedule_service);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to add service Service");
|
||||
return err;
|
||||
}
|
||||
ESP_LOGD(TAG, "Scheduling Service Enabled");
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sys/time.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include "esp_rmaker_secure_boot_digest.h"
|
||||
|
||||
static const char *TAG = "rmaker_secure_boot";
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
|
||||
static inline uint8_t to_hex_digit(unsigned val)
|
||||
{
|
||||
return (val < 10) ? ('0' + val) : ('a' + val - 10);
|
||||
}
|
||||
|
||||
static void bytes_to_hex(uint8_t *src, uint8_t *dst, int in_len)
|
||||
{
|
||||
for (int i = 0; i < in_len; i++) {
|
||||
dst[2 * i] = to_hex_digit(src[i] >> 4);
|
||||
dst[2 * i + 1] = to_hex_digit(src[i] & 0xf);
|
||||
}
|
||||
dst[2 * in_len] = 0;
|
||||
}
|
||||
|
||||
// Hex representation of secure boot digest. +1 for NULL termination
|
||||
#define SECURE_BOOT_DIGEST_LEN (ESP_SECURE_BOOT_DIGEST_LEN * 2 + 1)
|
||||
|
||||
esp_err_t esp_rmaker_secure_boot_digest_free(char **digest)
|
||||
{
|
||||
// cleanup
|
||||
for(int i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
if (digest[i]) {
|
||||
free(digest[i]);
|
||||
}
|
||||
}
|
||||
free(digest);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
char** esp_rmaker_get_secure_boot_digest()
|
||||
{
|
||||
char **secure_boot_digest = NULL;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
esp_secure_boot_key_digests_t trusted_keys;
|
||||
#else
|
||||
ets_secure_boot_key_digests_t trusted_keys;
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
esp_err_t ret = esp_secure_boot_read_key_digests(&trusted_keys);
|
||||
#else
|
||||
esp_err_t ret = ets_secure_boot_read_key_digests(&trusted_keys);
|
||||
#endif
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not read the secure boot key digests from efuse.");
|
||||
return NULL;
|
||||
}
|
||||
secure_boot_digest = MEM_ALLOC_EXTRAM(SECURE_BOOT_NUM_BLOCKS * sizeof(char *));
|
||||
if (!secure_boot_digest) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for secure boot digest");
|
||||
return NULL;
|
||||
}
|
||||
for(int i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
secure_boot_digest[i] = NULL;
|
||||
if (trusted_keys.key_digests[i] != NULL) {
|
||||
secure_boot_digest[i] = MEM_ALLOC_EXTRAM(SECURE_BOOT_DIGEST_LEN);
|
||||
if (!secure_boot_digest[i]) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for secure boot digest");
|
||||
for (int k = 0; k < i; k++) {
|
||||
free(secure_boot_digest[k]);
|
||||
secure_boot_digest[k] = NULL;
|
||||
}
|
||||
free(secure_boot_digest);
|
||||
secure_boot_digest = NULL;
|
||||
break;
|
||||
}
|
||||
bytes_to_hex((uint8_t *)trusted_keys.key_digests[i], (uint8_t *)secure_boot_digest[i], ESP_SECURE_BOOT_DIGEST_LEN);
|
||||
}
|
||||
}
|
||||
return secure_boot_digest;
|
||||
}
|
||||
|
||||
#else /* CONFIG_SECURE_BOOT_V2_ENABLED */
|
||||
|
||||
esp_err_t esp_rmaker_secure_boot_digest_free(char **digest)
|
||||
{
|
||||
(void) digest;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
char** esp_rmaker_get_secure_boot_digest()
|
||||
{
|
||||
ESP_LOGI(TAG, "Secure boot is not enabled. Could not get digest.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <esp_secure_boot.h>
|
||||
#include <esp_efuse.h>
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include "esp32/rom/secure_boot.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/secure_boot.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/secure_boot.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/secure_boot.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
#include "esp32c2/rom/secure_boot.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#include "esp32c6/rom/secure_boot.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#include "esp32h2/rom/secure_boot.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get secure boot digest
|
||||
*
|
||||
* @return 2D pointer with secure boot digest array
|
||||
* @note the memory allocated gets freed with \ref `esp_rmaker_secure_boot_digest_free` API
|
||||
*/
|
||||
char** esp_rmaker_get_secure_boot_digest();
|
||||
|
||||
/**
|
||||
* @brief free secure boot digest buffer
|
||||
*/
|
||||
esp_err_t esp_rmaker_secure_boot_digest_free(char **digest);
|
||||
@@ -0,0 +1,90 @@
|
||||
// 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 <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <esp_rmaker_standard_types.h>
|
||||
#include <esp_rmaker_standard_params.h>
|
||||
#include <esp_rmaker_standard_services.h>
|
||||
|
||||
static const char *TAG = "esp_rmaker_system_service";
|
||||
|
||||
#define ESP_RMAKER_SYSTEM_SERV_NAME "System"
|
||||
|
||||
static esp_err_t esp_rmaker_system_serv_write_cb(const esp_rmaker_device_t *device,
|
||||
const esp_rmaker_param_t *param, const esp_rmaker_param_val_t val,
|
||||
void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
esp_rmaker_system_serv_config_t *config = (esp_rmaker_system_serv_config_t *)priv_data;
|
||||
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_REBOOT) == 0) {
|
||||
if (val.val.b == true) {
|
||||
err = esp_rmaker_reboot(config->reboot_seconds);
|
||||
}
|
||||
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_FACTORY_RESET) == 0) {
|
||||
if (val.val.b == true) {
|
||||
err = esp_rmaker_factory_reset(config->reset_seconds, config->reset_reboot_seconds);
|
||||
}
|
||||
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_WIFI_RESET) == 0) {
|
||||
if (val.val.b == true) {
|
||||
err = esp_rmaker_wifi_reset(config->reset_seconds, config->reset_reboot_seconds);
|
||||
}
|
||||
} else {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (err == ESP_OK) {
|
||||
esp_rmaker_param_update_and_report(param, val);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_system_service_enable(esp_rmaker_system_serv_config_t *config)
|
||||
{
|
||||
if ((config->flags & SYSTEM_SERV_FLAGS_ALL) == 0) {
|
||||
ESP_LOGE(TAG, "Atleast one flag should be set for system service.");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_rmaker_system_serv_config_t *priv_config = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_system_serv_config_t));
|
||||
if (!priv_config) {
|
||||
ESP_LOGE(TAG, "Failed to allocate data for system service config.");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
*priv_config = *config;
|
||||
esp_rmaker_device_t *service = esp_rmaker_create_system_service(ESP_RMAKER_SYSTEM_SERV_NAME, (void *)priv_config);
|
||||
if (service) {
|
||||
esp_rmaker_device_add_cb(service, esp_rmaker_system_serv_write_cb, NULL);
|
||||
if (priv_config->flags & SYSTEM_SERV_FLAG_REBOOT) {
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_reboot_param_create(ESP_RMAKER_DEF_REBOOT_NAME));
|
||||
}
|
||||
if (priv_config->flags & SYSTEM_SERV_FLAG_FACTORY_RESET) {
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_factory_reset_param_create(ESP_RMAKER_DEF_FACTORY_RESET_NAME));
|
||||
}
|
||||
if (priv_config->flags & SYSTEM_SERV_FLAG_WIFI_RESET) {
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_wifi_reset_param_create(ESP_RMAKER_DEF_WIFI_RESET_NAME));
|
||||
}
|
||||
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), service);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "System service enabled.");
|
||||
} else {
|
||||
esp_rmaker_device_delete(service);
|
||||
}
|
||||
return err;
|
||||
} else {
|
||||
free(priv_config);
|
||||
ESP_LOGE(TAG, "Failed to create System service.");
|
||||
}
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
82
components/esp_rainmaker/src/core/esp_rmaker_time_service.c
Normal file
82
components/esp_rainmaker/src/core/esp_rmaker_time_service.c
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include <nvs.h>
|
||||
#include <esp_rmaker_standard_types.h>
|
||||
#include <esp_rmaker_standard_services.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
static const char *TAG = "esp_rmaker_time_service";
|
||||
|
||||
#define ESP_RMAKER_TIME_SERV_NAME "Time"
|
||||
|
||||
static esp_err_t esp_rmaker_time_service_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TIMEZONE) == 0) {
|
||||
ESP_LOGI(TAG, "Received value = %s for %s - %s",
|
||||
val.val.s, esp_rmaker_device_get_name(device), esp_rmaker_param_get_name(param));
|
||||
err = esp_rmaker_time_set_timezone(val.val.s);
|
||||
if (err == ESP_OK) {
|
||||
char *tz_posix = esp_rmaker_time_get_timezone_posix();
|
||||
if (tz_posix) {
|
||||
esp_rmaker_param_t *tz_posix_param = esp_rmaker_device_get_param_by_type(
|
||||
device, ESP_RMAKER_PARAM_TIMEZONE_POSIX);
|
||||
esp_rmaker_param_update_and_report(tz_posix_param, esp_rmaker_str(tz_posix));
|
||||
free(tz_posix);
|
||||
}
|
||||
}
|
||||
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TIMEZONE_POSIX) == 0) {
|
||||
ESP_LOGI(TAG, "Received value = %s for %s - %s",
|
||||
val.val.s, esp_rmaker_device_get_name(device), esp_rmaker_param_get_name(param));
|
||||
err = esp_rmaker_time_set_timezone_posix(val.val.s);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
esp_rmaker_param_update_and_report(param, val);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_time_add_service(const char *tz, const char *tz_posix)
|
||||
{
|
||||
esp_rmaker_device_t *service = esp_rmaker_time_service_create(ESP_RMAKER_TIME_SERV_NAME, tz, tz_posix, NULL);
|
||||
if (!service) {
|
||||
ESP_LOGE(TAG, "Failed to create Time Service");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_rmaker_device_add_cb(service, esp_rmaker_time_service_cb, NULL);
|
||||
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), service);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Time service enabled");
|
||||
} else {
|
||||
esp_rmaker_device_delete(service);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_timezone_service_enable(void)
|
||||
{
|
||||
char *tz_posix = esp_rmaker_time_get_timezone_posix();
|
||||
char *tz = esp_rmaker_time_get_timezone();
|
||||
esp_err_t err = esp_rmaker_time_add_service(tz, tz_posix);
|
||||
if (tz_posix) {
|
||||
free(tz_posix);
|
||||
}
|
||||
if (tz) {
|
||||
free(tz);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
414
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.c
Normal file
414
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.c
Normal file
@@ -0,0 +1,414 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_event.h>
|
||||
#include <nvs.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <json_generator.h>
|
||||
#include <esp_rmaker_work_queue.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_user_mapping.h>
|
||||
#include <esp_rmaker_mqtt.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <esp_rmaker_common_events.h>
|
||||
#include "esp_rmaker_user_mapping.pb-c.h"
|
||||
#include "esp_rmaker_internal.h"
|
||||
#include "esp_rmaker_mqtt_topics.h"
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
#include <network_provisioning/manager.h>
|
||||
#else
|
||||
#include <wifi_provisioning/manager.h>
|
||||
#endif
|
||||
|
||||
static const char *TAG = "esp_rmaker_user_mapping";
|
||||
|
||||
#define USER_MAPPING_ENDPOINT "cloud_user_assoc"
|
||||
#define USER_MAPPING_NVS_NAMESPACE "user_mapping"
|
||||
#define USER_ID_NVS_NAME "user_id"
|
||||
#define USER_RESET_ID "esp-rmaker"
|
||||
#define USER_RESET_KEY "failed"
|
||||
|
||||
/* A delay large enough to allow the tasks to get the semaphore, but small
|
||||
* enough to prevent tasks getting blocked for long.
|
||||
*/
|
||||
#define SEMAPHORE_DELAY_MSEC 5000
|
||||
|
||||
typedef struct {
|
||||
char *user_id;
|
||||
char *secret_key;
|
||||
int mqtt_msg_id;
|
||||
bool sent;
|
||||
} esp_rmaker_user_mapping_data_t;
|
||||
|
||||
static esp_rmaker_user_mapping_data_t *rmaker_user_mapping_data;
|
||||
esp_rmaker_user_mapping_state_t rmaker_user_mapping_state;
|
||||
SemaphoreHandle_t esp_rmaker_user_mapping_lock = NULL;
|
||||
|
||||
static void esp_rmaker_user_mapping_cleanup_data(void)
|
||||
{
|
||||
if (rmaker_user_mapping_data) {
|
||||
if (rmaker_user_mapping_data->user_id) {
|
||||
free(rmaker_user_mapping_data->user_id);
|
||||
}
|
||||
if (rmaker_user_mapping_data->secret_key) {
|
||||
free(rmaker_user_mapping_data->secret_key);
|
||||
}
|
||||
free(rmaker_user_mapping_data);
|
||||
rmaker_user_mapping_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_rmaker_user_mapping_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
if (event_base == NETWORK_PROV_EVENT) {
|
||||
switch (event_id) {
|
||||
case NETWORK_PROV_INIT: {
|
||||
if (esp_rmaker_user_mapping_endpoint_create() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to create user mapping end point.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NETWORK_PROV_START:
|
||||
if (esp_rmaker_user_mapping_endpoint_register() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register user mapping end point.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (event_base == WIFI_PROV_EVENT) {
|
||||
switch (event_id) {
|
||||
case WIFI_PROV_INIT: {
|
||||
if (esp_rmaker_user_mapping_endpoint_create() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to create user mapping end point.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WIFI_PROV_START:
|
||||
if (esp_rmaker_user_mapping_endpoint_register() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register user mapping end point.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
} else if ((event_base == RMAKER_COMMON_EVENT) && (event_id == RMAKER_MQTT_EVENT_PUBLISHED)) {
|
||||
/* Checking for the PUBACK for the user node association message to be sure that the message
|
||||
* has indeed reached the RainMaker cloud.
|
||||
*/
|
||||
int msg_id = *((int *)event_data);
|
||||
if (xSemaphoreTake(esp_rmaker_user_mapping_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to take semaphore.");
|
||||
return;
|
||||
}
|
||||
if ((rmaker_user_mapping_data != NULL) && (msg_id == rmaker_user_mapping_data->mqtt_msg_id)) {
|
||||
ESP_LOGI(TAG, "User Node association message published successfully.");
|
||||
if (strcmp(rmaker_user_mapping_data->user_id, USER_RESET_ID) == 0) {
|
||||
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_RESET;
|
||||
esp_rmaker_post_event(RMAKER_EVENT_USER_NODE_MAPPING_RESET, NULL, 0);
|
||||
} else {
|
||||
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_DONE;
|
||||
esp_rmaker_post_event(RMAKER_EVENT_USER_NODE_MAPPING_DONE, rmaker_user_mapping_data->user_id,
|
||||
strlen(rmaker_user_mapping_data->user_id) + 1);
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_USER_ID_CHECK
|
||||
/* Store User Id in NVS since acknowledgement of the user-node association message is received */
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, USER_MAPPING_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err == ESP_OK) {
|
||||
nvs_set_blob(handle, USER_ID_NVS_NAME, rmaker_user_mapping_data->user_id, strlen(rmaker_user_mapping_data->user_id));
|
||||
nvs_close(handle);
|
||||
}
|
||||
#endif
|
||||
esp_rmaker_user_mapping_cleanup_data();
|
||||
esp_event_handler_unregister(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_PUBLISHED,
|
||||
&esp_rmaker_user_mapping_event_handler);
|
||||
}
|
||||
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_rmaker_user_mapping_cb(void *priv_data)
|
||||
{
|
||||
if (xSemaphoreTake(esp_rmaker_user_mapping_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to take semaphore.");
|
||||
return;
|
||||
}
|
||||
/* If there is no user node mapping data, or if the data is already sent, just return */
|
||||
if (rmaker_user_mapping_data == NULL || rmaker_user_mapping_data->sent == true) {
|
||||
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||
return;
|
||||
}
|
||||
esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_PUBLISHED,
|
||||
&esp_rmaker_user_mapping_event_handler, NULL);
|
||||
char publish_payload[200];
|
||||
json_gen_str_t jstr;
|
||||
json_gen_str_start(&jstr, publish_payload, sizeof(publish_payload), NULL, NULL);
|
||||
json_gen_start_object(&jstr);
|
||||
char *node_id = esp_rmaker_get_node_id();
|
||||
json_gen_obj_set_string(&jstr, "node_id", node_id);
|
||||
json_gen_obj_set_string(&jstr, "user_id", rmaker_user_mapping_data->user_id);
|
||||
json_gen_obj_set_string(&jstr, "secret_key", rmaker_user_mapping_data->secret_key);
|
||||
if (esp_rmaker_user_node_mapping_get_state() != ESP_RMAKER_USER_MAPPING_DONE) {
|
||||
json_gen_obj_set_bool(&jstr, "reset", true);
|
||||
}
|
||||
json_gen_end_object(&jstr);
|
||||
json_gen_str_end(&jstr);
|
||||
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||
esp_rmaker_create_mqtt_topic(publish_topic, sizeof(publish_topic), USER_MAPPING_TOPIC_SUFFIX, USER_MAPPING_TOPIC_RULE);
|
||||
esp_err_t err = esp_rmaker_mqtt_publish(publish_topic, publish_payload, strlen(publish_payload), RMAKER_MQTT_QOS1, &rmaker_user_mapping_data->mqtt_msg_id);
|
||||
ESP_LOGI(TAG, "MQTT Publish: %s", publish_payload);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "MQTT Publish Error %d", err);
|
||||
} else {
|
||||
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_REQ_SENT;
|
||||
rmaker_user_mapping_data->sent = true;
|
||||
}
|
||||
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
static bool esp_rmaker_user_mapping_detect_reset(const char *user_id)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_USER_ID_CHECK
|
||||
bool reset_state = true;
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, USER_MAPPING_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return true;
|
||||
}
|
||||
char *nvs_user_id = NULL;
|
||||
size_t len = 0;
|
||||
if ((err = nvs_get_blob(handle, USER_ID_NVS_NAME, NULL, &len)) == ESP_OK) {
|
||||
nvs_user_id = MEM_CALLOC_EXTRAM(1, len + 1); /* +1 for NULL termination */
|
||||
if (nvs_user_id) {
|
||||
nvs_get_blob(handle, USER_ID_NVS_NAME, nvs_user_id, &len);
|
||||
/* If existing user id and new user id are same, this is not a reset state */
|
||||
if (strcmp(nvs_user_id, user_id) == 0) {
|
||||
reset_state = false;
|
||||
} else {
|
||||
/* Deleting the key in case of a mismatch. It will be stored only after the user node association
|
||||
* message is acknowledged from the cloud.
|
||||
*/
|
||||
nvs_erase_key(handle, USER_ID_NVS_NAME);
|
||||
}
|
||||
free(nvs_user_id);
|
||||
}
|
||||
}
|
||||
nvs_close(handle);
|
||||
return reset_state;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_start_user_node_mapping(char *user_id, char *secret_key)
|
||||
{
|
||||
if (esp_rmaker_user_mapping_lock == NULL) {
|
||||
ESP_LOGE(TAG, "User Node mapping not initialised.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (xSemaphoreTake(esp_rmaker_user_mapping_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to take semaphore.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (rmaker_user_mapping_data) {
|
||||
esp_rmaker_user_mapping_cleanup_data();
|
||||
}
|
||||
|
||||
rmaker_user_mapping_data = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_user_mapping_data_t));
|
||||
if (!rmaker_user_mapping_data) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for rmaker_user_mapping_data.");
|
||||
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
rmaker_user_mapping_data->user_id = strdup(user_id);
|
||||
if (!rmaker_user_mapping_data->user_id) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for user_id.");
|
||||
goto user_mapping_error;
|
||||
}
|
||||
rmaker_user_mapping_data->secret_key = strdup(secret_key);
|
||||
if (!rmaker_user_mapping_data->secret_key) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for secret_key.");
|
||||
goto user_mapping_error;
|
||||
}
|
||||
if (esp_rmaker_user_mapping_detect_reset(user_id)) {
|
||||
ESP_LOGI(TAG, "User Node mapping reset detected.");
|
||||
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_STARTED;
|
||||
} else {
|
||||
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_DONE;
|
||||
}
|
||||
if (esp_rmaker_work_queue_add_task(esp_rmaker_user_mapping_cb, NULL) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to queue user mapping task.");
|
||||
goto user_mapping_error;
|
||||
}
|
||||
esp_rmaker_user_mapping_prov_deinit();
|
||||
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||
return ESP_OK;
|
||||
|
||||
user_mapping_error:
|
||||
esp_rmaker_user_mapping_cleanup_data();
|
||||
xSemaphoreGive(esp_rmaker_user_mapping_lock);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_reset_user_node_mapping(void)
|
||||
{
|
||||
return esp_rmaker_start_user_node_mapping(USER_RESET_ID, USER_RESET_KEY);
|
||||
}
|
||||
|
||||
int esp_rmaker_user_mapping_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, uint8_t **outbuf, ssize_t *outlen, void *priv_data)
|
||||
{
|
||||
Rainmaker__RMakerConfigPayload *data;
|
||||
data = rainmaker__rmaker_config_payload__unpack(NULL, inlen, inbuf);
|
||||
|
||||
if (!data) {
|
||||
ESP_LOGE(TAG, "No Data Received");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
switch (data->msg) {
|
||||
case RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeCmdSetUserMapping: {
|
||||
ESP_LOGI(TAG, "Received request for node details");
|
||||
Rainmaker__RMakerConfigPayload resp;
|
||||
Rainmaker__RespSetUserMapping payload;
|
||||
rainmaker__rmaker_config_payload__init(&resp);
|
||||
rainmaker__resp_set_user_mapping__init(&payload);
|
||||
|
||||
if (data->payload_case != RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_USER_MAPPING) {
|
||||
ESP_LOGE(TAG, "Invalid payload type in the message: %d", data->payload_case);
|
||||
payload.status = RAINMAKER__RMAKER_CONFIG_STATUS__InvalidParam;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Got user_id = %s, secret_key = %s", data->cmd_set_user_mapping->userid, data->cmd_set_user_mapping->secretkey);
|
||||
if (esp_rmaker_start_user_node_mapping(data->cmd_set_user_mapping->userid,
|
||||
data->cmd_set_user_mapping->secretkey) != ESP_OK) {
|
||||
ESP_LOGI(TAG, "Sending status Invalid Param");
|
||||
payload.status = RAINMAKER__RMAKER_CONFIG_STATUS__InvalidParam;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Sending status SUCCESS");
|
||||
payload.status = RAINMAKER__RMAKER_CONFIG_STATUS__Success;
|
||||
payload.nodeid = esp_rmaker_get_node_id();
|
||||
}
|
||||
}
|
||||
resp.msg = RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeRespSetUserMapping;
|
||||
resp.payload_case = RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_USER_MAPPING;
|
||||
resp.resp_set_user_mapping = &payload;
|
||||
|
||||
*outlen = rainmaker__rmaker_config_payload__get_packed_size(&resp);
|
||||
*outbuf = (uint8_t *)MEM_ALLOC_EXTRAM(*outlen);
|
||||
rainmaker__rmaker_config_payload__pack(&resp, *outbuf);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGE(TAG, "Received invalid message type: %d", data->msg);
|
||||
break;
|
||||
}
|
||||
rainmaker__rmaker_config_payload__free_unpacked(data, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_err_t esp_rmaker_user_mapping_endpoint_create(void)
|
||||
{
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
esp_err_t err = network_prov_mgr_endpoint_create(USER_MAPPING_ENDPOINT);
|
||||
#else
|
||||
esp_err_t err = wifi_prov_mgr_endpoint_create(USER_MAPPING_ENDPOINT);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_user_mapping_endpoint_register(void)
|
||||
{
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
return network_prov_mgr_endpoint_register(USER_MAPPING_ENDPOINT, esp_rmaker_user_mapping_handler, NULL);
|
||||
#else
|
||||
return wifi_prov_mgr_endpoint_register(USER_MAPPING_ENDPOINT, esp_rmaker_user_mapping_handler, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_user_mapping_prov_init(void)
|
||||
{
|
||||
int ret = ESP_OK;
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
ret = esp_event_handler_register(NETWORK_PROV_EVENT, NETWORK_PROV_INIT, &esp_rmaker_user_mapping_event_handler, NULL);
|
||||
#else
|
||||
ret = esp_event_handler_register(WIFI_PROV_EVENT, WIFI_PROV_INIT,&esp_rmaker_user_mapping_event_handler, NULL);
|
||||
#endif
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
ret = esp_event_handler_register(NETWORK_PROV_EVENT, NETWORK_PROV_START, &esp_rmaker_user_mapping_event_handler, NULL);
|
||||
#else
|
||||
ret = esp_event_handler_register(WIFI_PROV_EVENT, WIFI_PROV_START,&esp_rmaker_user_mapping_event_handler, NULL);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_user_mapping_prov_deinit(void)
|
||||
{
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_INIT, &esp_rmaker_user_mapping_event_handler);
|
||||
esp_event_handler_unregister(NETWORK_PROV_EVENT, NETWORK_PROV_START, &esp_rmaker_user_mapping_event_handler);
|
||||
#else
|
||||
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_INIT, &esp_rmaker_user_mapping_event_handler);
|
||||
esp_event_handler_unregister(WIFI_PROV_EVENT, WIFI_PROV_START, &esp_rmaker_user_mapping_event_handler);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_rmaker_user_mapping_state_t esp_rmaker_user_node_mapping_get_state(void)
|
||||
{
|
||||
return rmaker_user_mapping_state;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_user_node_mapping_init(void)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_USER_ID_CHECK
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, USER_MAPPING_NVS_NAMESPACE, NVS_READONLY, &handle);
|
||||
if (err == ESP_OK) {
|
||||
size_t len = 0;
|
||||
if ((err = nvs_get_blob(handle, USER_ID_NVS_NAME, NULL, &len)) == ESP_OK) {
|
||||
/* Some User Id found, which means that user node association is already done */
|
||||
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_DONE;
|
||||
}
|
||||
nvs_close(handle);
|
||||
}
|
||||
#else
|
||||
rmaker_user_mapping_state = ESP_RMAKER_USER_MAPPING_DONE;
|
||||
#endif
|
||||
if (!esp_rmaker_user_mapping_lock) {
|
||||
esp_rmaker_user_mapping_lock = xSemaphoreCreateMutex();
|
||||
if (!esp_rmaker_user_mapping_lock) {
|
||||
ESP_LOGE(TAG, "Failed to create Mutex");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_user_node_mapping_deinit(void)
|
||||
{
|
||||
if (esp_rmaker_user_mapping_lock) {
|
||||
vSemaphoreDelete(esp_rmaker_user_mapping_lock);
|
||||
esp_rmaker_user_mapping_lock = NULL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
369
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.pb-c.c
Normal file
369
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.pb-c.c
Normal file
@@ -0,0 +1,369 @@
|
||||
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
|
||||
/* Generated from: esp_rmaker_user_mapping.proto */
|
||||
|
||||
/* Do not generate deprecated warnings for self */
|
||||
#ifndef PROTOBUF_C__NO_DEPRECATED
|
||||
#define PROTOBUF_C__NO_DEPRECATED
|
||||
#endif
|
||||
|
||||
#include "esp_rmaker_user_mapping.pb-c.h"
|
||||
void rainmaker__cmd_set_user_mapping__init
|
||||
(Rainmaker__CmdSetUserMapping *message)
|
||||
{
|
||||
static const Rainmaker__CmdSetUserMapping init_value = RAINMAKER__CMD_SET_USER_MAPPING__INIT;
|
||||
*message = init_value;
|
||||
}
|
||||
size_t rainmaker__cmd_set_user_mapping__get_packed_size
|
||||
(const Rainmaker__CmdSetUserMapping *message)
|
||||
{
|
||||
assert(message->base.descriptor == &rainmaker__cmd_set_user_mapping__descriptor);
|
||||
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||
}
|
||||
size_t rainmaker__cmd_set_user_mapping__pack
|
||||
(const Rainmaker__CmdSetUserMapping *message,
|
||||
uint8_t *out)
|
||||
{
|
||||
assert(message->base.descriptor == &rainmaker__cmd_set_user_mapping__descriptor);
|
||||
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||
}
|
||||
size_t rainmaker__cmd_set_user_mapping__pack_to_buffer
|
||||
(const Rainmaker__CmdSetUserMapping *message,
|
||||
ProtobufCBuffer *buffer)
|
||||
{
|
||||
assert(message->base.descriptor == &rainmaker__cmd_set_user_mapping__descriptor);
|
||||
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||
}
|
||||
Rainmaker__CmdSetUserMapping *
|
||||
rainmaker__cmd_set_user_mapping__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data)
|
||||
{
|
||||
return (Rainmaker__CmdSetUserMapping *)
|
||||
protobuf_c_message_unpack (&rainmaker__cmd_set_user_mapping__descriptor,
|
||||
allocator, len, data);
|
||||
}
|
||||
void rainmaker__cmd_set_user_mapping__free_unpacked
|
||||
(Rainmaker__CmdSetUserMapping *message,
|
||||
ProtobufCAllocator *allocator)
|
||||
{
|
||||
if(!message)
|
||||
return;
|
||||
assert(message->base.descriptor == &rainmaker__cmd_set_user_mapping__descriptor);
|
||||
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||
}
|
||||
void rainmaker__resp_set_user_mapping__init
|
||||
(Rainmaker__RespSetUserMapping *message)
|
||||
{
|
||||
static const Rainmaker__RespSetUserMapping init_value = RAINMAKER__RESP_SET_USER_MAPPING__INIT;
|
||||
*message = init_value;
|
||||
}
|
||||
size_t rainmaker__resp_set_user_mapping__get_packed_size
|
||||
(const Rainmaker__RespSetUserMapping *message)
|
||||
{
|
||||
assert(message->base.descriptor == &rainmaker__resp_set_user_mapping__descriptor);
|
||||
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||
}
|
||||
size_t rainmaker__resp_set_user_mapping__pack
|
||||
(const Rainmaker__RespSetUserMapping *message,
|
||||
uint8_t *out)
|
||||
{
|
||||
assert(message->base.descriptor == &rainmaker__resp_set_user_mapping__descriptor);
|
||||
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||
}
|
||||
size_t rainmaker__resp_set_user_mapping__pack_to_buffer
|
||||
(const Rainmaker__RespSetUserMapping *message,
|
||||
ProtobufCBuffer *buffer)
|
||||
{
|
||||
assert(message->base.descriptor == &rainmaker__resp_set_user_mapping__descriptor);
|
||||
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||
}
|
||||
Rainmaker__RespSetUserMapping *
|
||||
rainmaker__resp_set_user_mapping__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data)
|
||||
{
|
||||
return (Rainmaker__RespSetUserMapping *)
|
||||
protobuf_c_message_unpack (&rainmaker__resp_set_user_mapping__descriptor,
|
||||
allocator, len, data);
|
||||
}
|
||||
void rainmaker__resp_set_user_mapping__free_unpacked
|
||||
(Rainmaker__RespSetUserMapping *message,
|
||||
ProtobufCAllocator *allocator)
|
||||
{
|
||||
if(!message)
|
||||
return;
|
||||
assert(message->base.descriptor == &rainmaker__resp_set_user_mapping__descriptor);
|
||||
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||
}
|
||||
void rainmaker__rmaker_config_payload__init
|
||||
(Rainmaker__RMakerConfigPayload *message)
|
||||
{
|
||||
static const Rainmaker__RMakerConfigPayload init_value = RAINMAKER__RMAKER_CONFIG_PAYLOAD__INIT;
|
||||
*message = init_value;
|
||||
}
|
||||
size_t rainmaker__rmaker_config_payload__get_packed_size
|
||||
(const Rainmaker__RMakerConfigPayload *message)
|
||||
{
|
||||
assert(message->base.descriptor == &rainmaker__rmaker_config_payload__descriptor);
|
||||
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
|
||||
}
|
||||
size_t rainmaker__rmaker_config_payload__pack
|
||||
(const Rainmaker__RMakerConfigPayload *message,
|
||||
uint8_t *out)
|
||||
{
|
||||
assert(message->base.descriptor == &rainmaker__rmaker_config_payload__descriptor);
|
||||
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
|
||||
}
|
||||
size_t rainmaker__rmaker_config_payload__pack_to_buffer
|
||||
(const Rainmaker__RMakerConfigPayload *message,
|
||||
ProtobufCBuffer *buffer)
|
||||
{
|
||||
assert(message->base.descriptor == &rainmaker__rmaker_config_payload__descriptor);
|
||||
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
|
||||
}
|
||||
Rainmaker__RMakerConfigPayload *
|
||||
rainmaker__rmaker_config_payload__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data)
|
||||
{
|
||||
return (Rainmaker__RMakerConfigPayload *)
|
||||
protobuf_c_message_unpack (&rainmaker__rmaker_config_payload__descriptor,
|
||||
allocator, len, data);
|
||||
}
|
||||
void rainmaker__rmaker_config_payload__free_unpacked
|
||||
(Rainmaker__RMakerConfigPayload *message,
|
||||
ProtobufCAllocator *allocator)
|
||||
{
|
||||
if(!message)
|
||||
return;
|
||||
assert(message->base.descriptor == &rainmaker__rmaker_config_payload__descriptor);
|
||||
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
|
||||
}
|
||||
static const ProtobufCFieldDescriptor rainmaker__cmd_set_user_mapping__field_descriptors[2] =
|
||||
{
|
||||
{
|
||||
"UserID",
|
||||
1,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_STRING,
|
||||
0, /* quantifier_offset */
|
||||
offsetof(Rainmaker__CmdSetUserMapping, userid),
|
||||
NULL,
|
||||
&protobuf_c_empty_string,
|
||||
0, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
{
|
||||
"SecretKey",
|
||||
2,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_STRING,
|
||||
0, /* quantifier_offset */
|
||||
offsetof(Rainmaker__CmdSetUserMapping, secretkey),
|
||||
NULL,
|
||||
&protobuf_c_empty_string,
|
||||
0, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
};
|
||||
static const unsigned rainmaker__cmd_set_user_mapping__field_indices_by_name[] = {
|
||||
1, /* field[1] = SecretKey */
|
||||
0, /* field[0] = UserID */
|
||||
};
|
||||
static const ProtobufCIntRange rainmaker__cmd_set_user_mapping__number_ranges[1 + 1] =
|
||||
{
|
||||
{ 1, 0 },
|
||||
{ 0, 2 }
|
||||
};
|
||||
const ProtobufCMessageDescriptor rainmaker__cmd_set_user_mapping__descriptor =
|
||||
{
|
||||
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||
"rainmaker.CmdSetUserMapping",
|
||||
"CmdSetUserMapping",
|
||||
"Rainmaker__CmdSetUserMapping",
|
||||
"rainmaker",
|
||||
sizeof(Rainmaker__CmdSetUserMapping),
|
||||
2,
|
||||
rainmaker__cmd_set_user_mapping__field_descriptors,
|
||||
rainmaker__cmd_set_user_mapping__field_indices_by_name,
|
||||
1, rainmaker__cmd_set_user_mapping__number_ranges,
|
||||
(ProtobufCMessageInit) rainmaker__cmd_set_user_mapping__init,
|
||||
NULL,NULL,NULL /* reserved[123] */
|
||||
};
|
||||
static const ProtobufCFieldDescriptor rainmaker__resp_set_user_mapping__field_descriptors[2] =
|
||||
{
|
||||
{
|
||||
"Status",
|
||||
1,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_ENUM,
|
||||
0, /* quantifier_offset */
|
||||
offsetof(Rainmaker__RespSetUserMapping, status),
|
||||
&rainmaker__rmaker_config_status__descriptor,
|
||||
NULL,
|
||||
0, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
{
|
||||
"NodeId",
|
||||
2,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_STRING,
|
||||
0, /* quantifier_offset */
|
||||
offsetof(Rainmaker__RespSetUserMapping, nodeid),
|
||||
NULL,
|
||||
&protobuf_c_empty_string,
|
||||
0, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
};
|
||||
static const unsigned rainmaker__resp_set_user_mapping__field_indices_by_name[] = {
|
||||
1, /* field[1] = NodeId */
|
||||
0, /* field[0] = Status */
|
||||
};
|
||||
static const ProtobufCIntRange rainmaker__resp_set_user_mapping__number_ranges[1 + 1] =
|
||||
{
|
||||
{ 1, 0 },
|
||||
{ 0, 2 }
|
||||
};
|
||||
const ProtobufCMessageDescriptor rainmaker__resp_set_user_mapping__descriptor =
|
||||
{
|
||||
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||
"rainmaker.RespSetUserMapping",
|
||||
"RespSetUserMapping",
|
||||
"Rainmaker__RespSetUserMapping",
|
||||
"rainmaker",
|
||||
sizeof(Rainmaker__RespSetUserMapping),
|
||||
2,
|
||||
rainmaker__resp_set_user_mapping__field_descriptors,
|
||||
rainmaker__resp_set_user_mapping__field_indices_by_name,
|
||||
1, rainmaker__resp_set_user_mapping__number_ranges,
|
||||
(ProtobufCMessageInit) rainmaker__resp_set_user_mapping__init,
|
||||
NULL,NULL,NULL /* reserved[123] */
|
||||
};
|
||||
static const ProtobufCFieldDescriptor rainmaker__rmaker_config_payload__field_descriptors[3] =
|
||||
{
|
||||
{
|
||||
"msg",
|
||||
1,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_ENUM,
|
||||
0, /* quantifier_offset */
|
||||
offsetof(Rainmaker__RMakerConfigPayload, msg),
|
||||
&rainmaker__rmaker_config_msg_type__descriptor,
|
||||
NULL,
|
||||
0, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
{
|
||||
"cmd_set_user_mapping",
|
||||
10,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_MESSAGE,
|
||||
offsetof(Rainmaker__RMakerConfigPayload, payload_case),
|
||||
offsetof(Rainmaker__RMakerConfigPayload, cmd_set_user_mapping),
|
||||
&rainmaker__cmd_set_user_mapping__descriptor,
|
||||
NULL,
|
||||
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
{
|
||||
"resp_set_user_mapping",
|
||||
11,
|
||||
PROTOBUF_C_LABEL_NONE,
|
||||
PROTOBUF_C_TYPE_MESSAGE,
|
||||
offsetof(Rainmaker__RMakerConfigPayload, payload_case),
|
||||
offsetof(Rainmaker__RMakerConfigPayload, resp_set_user_mapping),
|
||||
&rainmaker__resp_set_user_mapping__descriptor,
|
||||
NULL,
|
||||
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
|
||||
0,NULL,NULL /* reserved1,reserved2, etc */
|
||||
},
|
||||
};
|
||||
static const unsigned rainmaker__rmaker_config_payload__field_indices_by_name[] = {
|
||||
1, /* field[1] = cmd_set_user_mapping */
|
||||
0, /* field[0] = msg */
|
||||
2, /* field[2] = resp_set_user_mapping */
|
||||
};
|
||||
static const ProtobufCIntRange rainmaker__rmaker_config_payload__number_ranges[2 + 1] =
|
||||
{
|
||||
{ 1, 0 },
|
||||
{ 10, 1 },
|
||||
{ 0, 3 }
|
||||
};
|
||||
const ProtobufCMessageDescriptor rainmaker__rmaker_config_payload__descriptor =
|
||||
{
|
||||
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
|
||||
"rainmaker.RMakerConfigPayload",
|
||||
"RMakerConfigPayload",
|
||||
"Rainmaker__RMakerConfigPayload",
|
||||
"rainmaker",
|
||||
sizeof(Rainmaker__RMakerConfigPayload),
|
||||
3,
|
||||
rainmaker__rmaker_config_payload__field_descriptors,
|
||||
rainmaker__rmaker_config_payload__field_indices_by_name,
|
||||
2, rainmaker__rmaker_config_payload__number_ranges,
|
||||
(ProtobufCMessageInit) rainmaker__rmaker_config_payload__init,
|
||||
NULL,NULL,NULL /* reserved[123] */
|
||||
};
|
||||
static const ProtobufCEnumValue rainmaker__rmaker_config_status__enum_values_by_number[3] =
|
||||
{
|
||||
{ "Success", "RAINMAKER__RMAKER_CONFIG_STATUS__Success", 0 },
|
||||
{ "InvalidParam", "RAINMAKER__RMAKER_CONFIG_STATUS__InvalidParam", 1 },
|
||||
{ "InvalidState", "RAINMAKER__RMAKER_CONFIG_STATUS__InvalidState", 2 },
|
||||
};
|
||||
static const ProtobufCIntRange rainmaker__rmaker_config_status__value_ranges[] = {
|
||||
{0, 0},{0, 3}
|
||||
};
|
||||
static const ProtobufCEnumValueIndex rainmaker__rmaker_config_status__enum_values_by_name[3] =
|
||||
{
|
||||
{ "InvalidParam", 1 },
|
||||
{ "InvalidState", 2 },
|
||||
{ "Success", 0 },
|
||||
};
|
||||
const ProtobufCEnumDescriptor rainmaker__rmaker_config_status__descriptor =
|
||||
{
|
||||
PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
|
||||
"rainmaker.RMakerConfigStatus",
|
||||
"RMakerConfigStatus",
|
||||
"Rainmaker__RMakerConfigStatus",
|
||||
"rainmaker",
|
||||
3,
|
||||
rainmaker__rmaker_config_status__enum_values_by_number,
|
||||
3,
|
||||
rainmaker__rmaker_config_status__enum_values_by_name,
|
||||
1,
|
||||
rainmaker__rmaker_config_status__value_ranges,
|
||||
NULL,NULL,NULL,NULL /* reserved[1234] */
|
||||
};
|
||||
static const ProtobufCEnumValue rainmaker__rmaker_config_msg_type__enum_values_by_number[2] =
|
||||
{
|
||||
{ "TypeCmdSetUserMapping", "RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeCmdSetUserMapping", 0 },
|
||||
{ "TypeRespSetUserMapping", "RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeRespSetUserMapping", 1 },
|
||||
};
|
||||
static const ProtobufCIntRange rainmaker__rmaker_config_msg_type__value_ranges[] = {
|
||||
{0, 0},{0, 2}
|
||||
};
|
||||
static const ProtobufCEnumValueIndex rainmaker__rmaker_config_msg_type__enum_values_by_name[2] =
|
||||
{
|
||||
{ "TypeCmdSetUserMapping", 0 },
|
||||
{ "TypeRespSetUserMapping", 1 },
|
||||
};
|
||||
const ProtobufCEnumDescriptor rainmaker__rmaker_config_msg_type__descriptor =
|
||||
{
|
||||
PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
|
||||
"rainmaker.RMakerConfigMsgType",
|
||||
"RMakerConfigMsgType",
|
||||
"Rainmaker__RMakerConfigMsgType",
|
||||
"rainmaker",
|
||||
2,
|
||||
rainmaker__rmaker_config_msg_type__enum_values_by_number,
|
||||
2,
|
||||
rainmaker__rmaker_config_msg_type__enum_values_by_name,
|
||||
1,
|
||||
rainmaker__rmaker_config_msg_type__value_ranges,
|
||||
NULL,NULL,NULL,NULL /* reserved[1234] */
|
||||
};
|
||||
166
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.pb-c.h
Normal file
166
components/esp_rainmaker/src/core/esp_rmaker_user_mapping.pb-c.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
|
||||
/* Generated from: esp_rmaker_user_mapping.proto */
|
||||
|
||||
#ifndef PROTOBUF_C_esp_5frmaker_5fuser_5fmapping_2eproto__INCLUDED
|
||||
#define PROTOBUF_C_esp_5frmaker_5fuser_5fmapping_2eproto__INCLUDED
|
||||
|
||||
#include <protobuf-c/protobuf-c.h>
|
||||
|
||||
PROTOBUF_C__BEGIN_DECLS
|
||||
|
||||
#if PROTOBUF_C_VERSION_NUMBER < 1003000
|
||||
# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
|
||||
#elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION
|
||||
# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct _Rainmaker__CmdSetUserMapping Rainmaker__CmdSetUserMapping;
|
||||
typedef struct _Rainmaker__RespSetUserMapping Rainmaker__RespSetUserMapping;
|
||||
typedef struct _Rainmaker__RMakerConfigPayload Rainmaker__RMakerConfigPayload;
|
||||
|
||||
|
||||
/* --- enums --- */
|
||||
|
||||
typedef enum _Rainmaker__RMakerConfigStatus {
|
||||
RAINMAKER__RMAKER_CONFIG_STATUS__Success = 0,
|
||||
RAINMAKER__RMAKER_CONFIG_STATUS__InvalidParam = 1,
|
||||
RAINMAKER__RMAKER_CONFIG_STATUS__InvalidState = 2
|
||||
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RAINMAKER__RMAKER_CONFIG_STATUS)
|
||||
} Rainmaker__RMakerConfigStatus;
|
||||
typedef enum _Rainmaker__RMakerConfigMsgType {
|
||||
RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeCmdSetUserMapping = 0,
|
||||
RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeRespSetUserMapping = 1
|
||||
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RAINMAKER__RMAKER_CONFIG_MSG_TYPE)
|
||||
} Rainmaker__RMakerConfigMsgType;
|
||||
|
||||
/* --- messages --- */
|
||||
|
||||
struct _Rainmaker__CmdSetUserMapping
|
||||
{
|
||||
ProtobufCMessage base;
|
||||
char *userid;
|
||||
char *secretkey;
|
||||
};
|
||||
#define RAINMAKER__CMD_SET_USER_MAPPING__INIT \
|
||||
{ PROTOBUF_C_MESSAGE_INIT (&rainmaker__cmd_set_user_mapping__descriptor) \
|
||||
, (char *)protobuf_c_empty_string, (char *)protobuf_c_empty_string }
|
||||
|
||||
|
||||
struct _Rainmaker__RespSetUserMapping
|
||||
{
|
||||
ProtobufCMessage base;
|
||||
Rainmaker__RMakerConfigStatus status;
|
||||
char *nodeid;
|
||||
};
|
||||
#define RAINMAKER__RESP_SET_USER_MAPPING__INIT \
|
||||
{ PROTOBUF_C_MESSAGE_INIT (&rainmaker__resp_set_user_mapping__descriptor) \
|
||||
, RAINMAKER__RMAKER_CONFIG_STATUS__Success, (char *)protobuf_c_empty_string }
|
||||
|
||||
|
||||
typedef enum {
|
||||
RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD__NOT_SET = 0,
|
||||
RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_USER_MAPPING = 10,
|
||||
RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_USER_MAPPING = 11
|
||||
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD)
|
||||
} Rainmaker__RMakerConfigPayload__PayloadCase;
|
||||
|
||||
struct _Rainmaker__RMakerConfigPayload
|
||||
{
|
||||
ProtobufCMessage base;
|
||||
Rainmaker__RMakerConfigMsgType msg;
|
||||
Rainmaker__RMakerConfigPayload__PayloadCase payload_case;
|
||||
union {
|
||||
Rainmaker__CmdSetUserMapping *cmd_set_user_mapping;
|
||||
Rainmaker__RespSetUserMapping *resp_set_user_mapping;
|
||||
};
|
||||
};
|
||||
#define RAINMAKER__RMAKER_CONFIG_PAYLOAD__INIT \
|
||||
{ PROTOBUF_C_MESSAGE_INIT (&rainmaker__rmaker_config_payload__descriptor) \
|
||||
, RAINMAKER__RMAKER_CONFIG_MSG_TYPE__TypeCmdSetUserMapping, RAINMAKER__RMAKER_CONFIG_PAYLOAD__PAYLOAD__NOT_SET, {0} }
|
||||
|
||||
|
||||
/* Rainmaker__CmdSetUserMapping methods */
|
||||
void rainmaker__cmd_set_user_mapping__init
|
||||
(Rainmaker__CmdSetUserMapping *message);
|
||||
size_t rainmaker__cmd_set_user_mapping__get_packed_size
|
||||
(const Rainmaker__CmdSetUserMapping *message);
|
||||
size_t rainmaker__cmd_set_user_mapping__pack
|
||||
(const Rainmaker__CmdSetUserMapping *message,
|
||||
uint8_t *out);
|
||||
size_t rainmaker__cmd_set_user_mapping__pack_to_buffer
|
||||
(const Rainmaker__CmdSetUserMapping *message,
|
||||
ProtobufCBuffer *buffer);
|
||||
Rainmaker__CmdSetUserMapping *
|
||||
rainmaker__cmd_set_user_mapping__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data);
|
||||
void rainmaker__cmd_set_user_mapping__free_unpacked
|
||||
(Rainmaker__CmdSetUserMapping *message,
|
||||
ProtobufCAllocator *allocator);
|
||||
/* Rainmaker__RespSetUserMapping methods */
|
||||
void rainmaker__resp_set_user_mapping__init
|
||||
(Rainmaker__RespSetUserMapping *message);
|
||||
size_t rainmaker__resp_set_user_mapping__get_packed_size
|
||||
(const Rainmaker__RespSetUserMapping *message);
|
||||
size_t rainmaker__resp_set_user_mapping__pack
|
||||
(const Rainmaker__RespSetUserMapping *message,
|
||||
uint8_t *out);
|
||||
size_t rainmaker__resp_set_user_mapping__pack_to_buffer
|
||||
(const Rainmaker__RespSetUserMapping *message,
|
||||
ProtobufCBuffer *buffer);
|
||||
Rainmaker__RespSetUserMapping *
|
||||
rainmaker__resp_set_user_mapping__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data);
|
||||
void rainmaker__resp_set_user_mapping__free_unpacked
|
||||
(Rainmaker__RespSetUserMapping *message,
|
||||
ProtobufCAllocator *allocator);
|
||||
/* Rainmaker__RMakerConfigPayload methods */
|
||||
void rainmaker__rmaker_config_payload__init
|
||||
(Rainmaker__RMakerConfigPayload *message);
|
||||
size_t rainmaker__rmaker_config_payload__get_packed_size
|
||||
(const Rainmaker__RMakerConfigPayload *message);
|
||||
size_t rainmaker__rmaker_config_payload__pack
|
||||
(const Rainmaker__RMakerConfigPayload *message,
|
||||
uint8_t *out);
|
||||
size_t rainmaker__rmaker_config_payload__pack_to_buffer
|
||||
(const Rainmaker__RMakerConfigPayload *message,
|
||||
ProtobufCBuffer *buffer);
|
||||
Rainmaker__RMakerConfigPayload *
|
||||
rainmaker__rmaker_config_payload__unpack
|
||||
(ProtobufCAllocator *allocator,
|
||||
size_t len,
|
||||
const uint8_t *data);
|
||||
void rainmaker__rmaker_config_payload__free_unpacked
|
||||
(Rainmaker__RMakerConfigPayload *message,
|
||||
ProtobufCAllocator *allocator);
|
||||
/* --- per-message closures --- */
|
||||
|
||||
typedef void (*Rainmaker__CmdSetUserMapping_Closure)
|
||||
(const Rainmaker__CmdSetUserMapping *message,
|
||||
void *closure_data);
|
||||
typedef void (*Rainmaker__RespSetUserMapping_Closure)
|
||||
(const Rainmaker__RespSetUserMapping *message,
|
||||
void *closure_data);
|
||||
typedef void (*Rainmaker__RMakerConfigPayload_Closure)
|
||||
(const Rainmaker__RMakerConfigPayload *message,
|
||||
void *closure_data);
|
||||
|
||||
/* --- services --- */
|
||||
|
||||
|
||||
/* --- descriptors --- */
|
||||
|
||||
extern const ProtobufCEnumDescriptor rainmaker__rmaker_config_status__descriptor;
|
||||
extern const ProtobufCEnumDescriptor rainmaker__rmaker_config_msg_type__descriptor;
|
||||
extern const ProtobufCMessageDescriptor rainmaker__cmd_set_user_mapping__descriptor;
|
||||
extern const ProtobufCMessageDescriptor rainmaker__resp_set_user_mapping__descriptor;
|
||||
extern const ProtobufCMessageDescriptor rainmaker__rmaker_config_payload__descriptor;
|
||||
|
||||
PROTOBUF_C__END_DECLS
|
||||
|
||||
|
||||
#endif /* PROTOBUF_C_esp_5frmaker_5fuser_5fmapping_2eproto__INCLUDED */
|
||||
@@ -0,0 +1,31 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package rainmaker;
|
||||
|
||||
enum RMakerConfigStatus {
|
||||
Success = 0;
|
||||
InvalidParam = 1;
|
||||
InvalidState = 2;
|
||||
}
|
||||
|
||||
message CmdSetUserMapping {
|
||||
string UserID = 1;
|
||||
string SecretKey = 2;
|
||||
}
|
||||
message RespSetUserMapping {
|
||||
RMakerConfigStatus Status = 1;
|
||||
string NodeId = 2;
|
||||
}
|
||||
|
||||
enum RMakerConfigMsgType {
|
||||
TypeCmdSetUserMapping = 0;
|
||||
TypeRespSetUserMapping = 1;
|
||||
}
|
||||
|
||||
message RMakerConfigPayload {
|
||||
RMakerConfigMsgType msg = 1;
|
||||
oneof payload {
|
||||
CmdSetUserMapping cmd_set_user_mapping = 10;
|
||||
RespSetUserMapping resp_set_user_mapping = 11;
|
||||
}
|
||||
}
|
||||
135
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt.c
Normal file
135
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt.c
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_rmaker_mqtt_glue.h>
|
||||
#include <esp_rmaker_client_data.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
|
||||
#include "esp_rmaker_mqtt.h"
|
||||
#include "esp_rmaker_mqtt_budget.h"
|
||||
|
||||
static const char *TAG = "esp_rmaker_mqtt";
|
||||
static esp_rmaker_mqtt_config_t g_mqtt_config;
|
||||
|
||||
esp_rmaker_mqtt_conn_params_t *esp_rmaker_mqtt_get_conn_params(void)
|
||||
{
|
||||
if (g_mqtt_config.get_conn_params) {
|
||||
return g_mqtt_config.get_conn_params();
|
||||
} else {
|
||||
return esp_rmaker_get_mqtt_conn_params();
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_setup(esp_rmaker_mqtt_config_t mqtt_config)
|
||||
{
|
||||
g_mqtt_config = mqtt_config;
|
||||
g_mqtt_config.setup_done = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_init(esp_rmaker_mqtt_conn_params_t *conn_params)
|
||||
{
|
||||
if (!g_mqtt_config.setup_done) {
|
||||
esp_rmaker_mqtt_glue_setup(&g_mqtt_config);
|
||||
}
|
||||
if (g_mqtt_config.init) {
|
||||
esp_err_t err = g_mqtt_config.init(conn_params);
|
||||
if (err == ESP_OK) {
|
||||
if (esp_rmaker_mqtt_budgeting_init() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialise MQTT Budgeting.");
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
ESP_LOGW(TAG, "esp_rmaker_mqtt_init not registered");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_rmaker_mqtt_deinit(void)
|
||||
{
|
||||
esp_rmaker_mqtt_budgeting_deinit();
|
||||
if (g_mqtt_config.deinit) {
|
||||
return g_mqtt_config.deinit();
|
||||
}
|
||||
ESP_LOGW(TAG, "esp_rmaker_mqtt_deinit not registered");
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_connect(void)
|
||||
{
|
||||
if (g_mqtt_config.connect) {
|
||||
esp_err_t err = g_mqtt_config.connect();
|
||||
if (err == ESP_OK) {
|
||||
esp_rmaker_mqtt_budgeting_start();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
ESP_LOGW(TAG, "esp_rmaker_mqtt_connect not registered");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_disconnect(void)
|
||||
{
|
||||
esp_rmaker_mqtt_budgeting_stop();
|
||||
if (g_mqtt_config.disconnect) {
|
||||
return g_mqtt_config.disconnect();
|
||||
}
|
||||
ESP_LOGW(TAG, "esp_rmaker_mqtt_disconnect not registered");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_subscribe(const char *topic, esp_rmaker_mqtt_subscribe_cb_t cb, uint8_t qos, void *priv_data)
|
||||
{
|
||||
if (g_mqtt_config.subscribe) {
|
||||
return g_mqtt_config.subscribe(topic, cb, qos, priv_data);
|
||||
}
|
||||
ESP_LOGW(TAG, "esp_rmaker_mqtt_subscribe not registered");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_unsubscribe(const char *topic)
|
||||
{
|
||||
if (g_mqtt_config.unsubscribe) {
|
||||
return g_mqtt_config.unsubscribe(topic);
|
||||
}
|
||||
ESP_LOGW(TAG, "esp_rmaker_mqtt_unsubscribe not registered");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_publish(const char *topic, void *data, size_t data_len, uint8_t qos, int *msg_id)
|
||||
{
|
||||
if (esp_rmaker_mqtt_is_budget_available() != true) {
|
||||
ESP_LOGE(TAG, "Out of MQTT Budget. Dropping publish message.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (g_mqtt_config.publish) {
|
||||
esp_err_t err = g_mqtt_config.publish(topic, data, data_len, qos, msg_id);
|
||||
if (err == ESP_OK) {
|
||||
esp_rmaker_mqtt_decrease_budget(1);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
ESP_LOGW(TAG, "esp_rmaker_mqtt_publish not registered");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_rmaker_create_mqtt_topic(char *buf, size_t buf_size, const char *topic_suffix, const char *rule)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_MQTT_USE_BASIC_INGEST_TOPICS
|
||||
snprintf(buf, buf_size, "$aws/rules/%s/node/%s/%s", rule, esp_rmaker_get_node_id(), topic_suffix);
|
||||
#else
|
||||
snprintf(buf, buf_size, "node/%s/%s", esp_rmaker_get_node_id(), topic_suffix);
|
||||
#endif
|
||||
}
|
||||
183
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt_budget.c
Normal file
183
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt_budget.c
Normal file
@@ -0,0 +1,183 @@
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sdkconfig.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
#include <stdbool.h>
|
||||
static const char *TAG = "esp_rmaker_mqtt_budget";
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_MQTT_ENABLE_BUDGETING
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
#define DEFAULT_BUDGET CONFIG_ESP_RMAKER_MQTT_DEFAULT_BUDGET
|
||||
#define MAX_BUDGET CONFIG_ESP_RMAKER_MQTT_MAX_BUDGET
|
||||
#define BUDGET_REVIVE_COUNT CONFIG_ESP_RMAKER_MQTT_BUDGET_REVIVE_COUNT
|
||||
#define BUDGET_REVIVE_PERIOD CONFIG_ESP_RMAKER_MQTT_BUDGET_REVIVE_PERIOD
|
||||
|
||||
static int16_t mqtt_budget = DEFAULT_BUDGET;
|
||||
static TimerHandle_t mqtt_budget_timer;
|
||||
static SemaphoreHandle_t mqtt_budget_lock;
|
||||
#define SEMAPHORE_DELAY_MSEC 500
|
||||
|
||||
bool esp_rmaker_mqtt_is_budget_available(void)
|
||||
{
|
||||
if (mqtt_budget_lock == NULL) {
|
||||
ESP_LOGW(TAG, "MQTT budgeting not started yet. Allowing publish.");
|
||||
return true;
|
||||
}
|
||||
if (xSemaphoreTake(mqtt_budget_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||
ESP_LOGW(TAG, "Could not acquire MQTT budget lock. Allowing publish.");
|
||||
return true;
|
||||
}
|
||||
int16_t budget = mqtt_budget;
|
||||
xSemaphoreGive(mqtt_budget_lock);
|
||||
return budget ? true : false;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_increase_budget(uint8_t budget)
|
||||
{
|
||||
if (mqtt_budget_lock == NULL) {
|
||||
ESP_LOGW(TAG, "MQTT budgeting not started. Not increasing the budget.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (xSemaphoreTake(mqtt_budget_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to increase MQTT budget.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
mqtt_budget += budget;
|
||||
if (mqtt_budget > MAX_BUDGET) {
|
||||
mqtt_budget = MAX_BUDGET;
|
||||
}
|
||||
xSemaphoreGive(mqtt_budget_lock);
|
||||
ESP_LOGD(TAG, "MQTT budget increased to %d", mqtt_budget);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_decrease_budget(uint8_t budget)
|
||||
{
|
||||
if (mqtt_budget_lock == NULL) {
|
||||
ESP_LOGW(TAG, "MQTT budgeting not started. Not decreasing the budget.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (xSemaphoreTake(mqtt_budget_lock, SEMAPHORE_DELAY_MSEC/portTICK_PERIOD_MS) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to decrease MQTT budget.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
mqtt_budget -= budget;
|
||||
if (mqtt_budget < 0) {
|
||||
mqtt_budget = 0;
|
||||
}
|
||||
xSemaphoreGive(mqtt_budget_lock);
|
||||
ESP_LOGD(TAG, "MQTT budget decreased to %d.", mqtt_budget);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void esp_rmaker_mqtt_revive_budget(TimerHandle_t handle)
|
||||
{
|
||||
esp_rmaker_mqtt_increase_budget(BUDGET_REVIVE_COUNT);
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_start(void)
|
||||
{
|
||||
if (mqtt_budget_timer) {
|
||||
xTimerStart(mqtt_budget_timer, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_stop(void)
|
||||
{
|
||||
if (mqtt_budget_timer) {
|
||||
xTimerStop(mqtt_budget_timer, 100);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_deinit(void)
|
||||
{
|
||||
if (mqtt_budget_timer) {
|
||||
esp_rmaker_mqtt_budgeting_stop();
|
||||
xTimerDelete(mqtt_budget_timer, 100);
|
||||
mqtt_budget_timer = NULL;
|
||||
}
|
||||
if (mqtt_budget_lock) {
|
||||
vSemaphoreDelete(mqtt_budget_lock);
|
||||
mqtt_budget_lock = NULL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_init(void)
|
||||
{
|
||||
if (mqtt_budget_timer) {
|
||||
ESP_LOGI(TAG, "MQTT budgeting already initialised.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
mqtt_budget_lock = xSemaphoreCreateMutex();
|
||||
if (!mqtt_budget_lock) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
mqtt_budget_timer = xTimerCreate("mqtt_budget_tm", (BUDGET_REVIVE_PERIOD * 1000) / portTICK_PERIOD_MS,
|
||||
pdTRUE, NULL, esp_rmaker_mqtt_revive_budget);
|
||||
if (mqtt_budget_timer) {
|
||||
ESP_LOGI(TAG, "MQTT Budgeting initialised. Default: %d, Max: %d, Revive count: %d, Revive period: %d",
|
||||
DEFAULT_BUDGET, MAX_BUDGET, BUDGET_REVIVE_COUNT, BUDGET_REVIVE_PERIOD);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#else /* ! CONFIG_ESP_RMAKER_MQTT_ENABLE_BUDGETING */
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_init(void)
|
||||
{
|
||||
/* Adding a print only here, because this is always going to be the first function
|
||||
* to be invoked since it is called from MQTT init. Else, MQTT itself is going to fail.
|
||||
*/
|
||||
ESP_LOGW(TAG, "MQTT Budgeting is not enabled.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_deinit(void)
|
||||
{
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_stop(void)
|
||||
{
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_start(void)
|
||||
{
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_increase_budget(uint8_t budget)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_decrease_budget(uint8_t budget)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool esp_rmaker_mqtt_is_budget_available(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* ! CONFIG_ESP_RMAKER_MQTT_ENABLE_BUDGETING */
|
||||
17
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt_budget.h
Normal file
17
components/esp_rainmaker/src/mqtt/esp_rmaker_mqtt_budget.h
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_init(void);
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_deinit(void);
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_stop(void);
|
||||
esp_err_t esp_rmaker_mqtt_budgeting_start(void);
|
||||
esp_err_t esp_rmaker_mqtt_increase_budget(uint8_t budget);
|
||||
esp_err_t esp_rmaker_mqtt_decrease_budget(uint8_t budget);
|
||||
726
components/esp_rainmaker/src/ota/esp_rmaker_ota.c
Normal file
726
components/esp_rainmaker/src/ota/esp_rmaker_ota.c
Normal file
@@ -0,0 +1,726 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_efuse.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_partition.h>
|
||||
#include <esp_https_ota.h>
|
||||
#include <esp_wifi_types.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <nvs.h>
|
||||
#include <json_parser.h>
|
||||
#if CONFIG_BT_ENABLED
|
||||
#include <esp_bt.h>
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <esp_rmaker_common_events.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include "esp_rmaker_internal.h"
|
||||
#include "esp_rmaker_ota_internal.h"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
// Features supported in 4.4+
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_USE_CERT_BUNDLE
|
||||
#define ESP_RMAKER_USE_CERT_BUNDLE
|
||||
#include <esp_crt_bundle.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_USE_CERT_BUNDLE
|
||||
#warning "Certificate Bundle not supported below IDF v4.4. Using provided certificate instead."
|
||||
#endif
|
||||
|
||||
#endif /* !IDF4.4 */
|
||||
static const char *TAG = "esp_rmaker_ota";
|
||||
|
||||
#define OTA_REBOOT_TIMER_SEC 10
|
||||
#define DEF_HTTP_TX_BUFFER_SIZE 1024
|
||||
#define DEF_HTTP_RX_BUFFER_SIZE CONFIG_ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE
|
||||
#define RMAKER_OTA_ROLLBACK_WAIT_PERIOD CONFIG_ESP_RMAKER_OTA_ROLLBACK_WAIT_PERIOD
|
||||
extern const char esp_rmaker_ota_def_cert[] asm("_binary_rmaker_ota_server_crt_start");
|
||||
const char *ESP_RMAKER_OTA_DEFAULT_SERVER_CERT = esp_rmaker_ota_def_cert;
|
||||
ESP_EVENT_DEFINE_BASE(RMAKER_OTA_EVENT);
|
||||
|
||||
typedef enum {
|
||||
OTA_OK = 0,
|
||||
OTA_ERR,
|
||||
OTA_DELAYED
|
||||
} esp_rmaker_ota_action_t;
|
||||
|
||||
static esp_rmaker_ota_t *g_ota_priv;
|
||||
|
||||
char *esp_rmaker_ota_status_to_string(ota_status_t status)
|
||||
{
|
||||
switch (status) {
|
||||
case OTA_STATUS_IN_PROGRESS:
|
||||
return "in-progress";
|
||||
case OTA_STATUS_SUCCESS:
|
||||
return "success";
|
||||
case OTA_STATUS_FAILED:
|
||||
return "failed";
|
||||
case OTA_STATUS_DELAYED:
|
||||
return "delayed";
|
||||
case OTA_STATUS_REJECTED:
|
||||
return "rejected";
|
||||
default:
|
||||
return "invalid";
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
esp_rmaker_ota_event_t esp_rmaker_ota_status_to_event(ota_status_t status)
|
||||
{
|
||||
switch (status) {
|
||||
case OTA_STATUS_IN_PROGRESS:
|
||||
return RMAKER_OTA_EVENT_IN_PROGRESS;
|
||||
case OTA_STATUS_SUCCESS:
|
||||
return RMAKER_OTA_EVENT_SUCCESSFUL;
|
||||
case OTA_STATUS_FAILED:
|
||||
return RMAKER_OTA_EVENT_FAILED;
|
||||
case OTA_STATUS_DELAYED:
|
||||
return RMAKER_OTA_EVENT_DELAYED;
|
||||
case OTA_STATUS_REJECTED:
|
||||
return RMAKER_OTA_EVENT_REJECTED;
|
||||
default:
|
||||
ESP_LOGD(TAG, "No Rmaker OTA Event for given status: %d: %s",
|
||||
status, esp_rmaker_ota_status_to_string(status));
|
||||
}
|
||||
return RMAKER_OTA_EVENT_INVALID;
|
||||
}
|
||||
|
||||
static inline esp_err_t esp_rmaker_ota_post_event(esp_rmaker_event_t event_id, void* data, size_t data_size)
|
||||
{
|
||||
return esp_event_post(RMAKER_OTA_EVENT, event_id, data, data_size, portMAX_DELAY);
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_ota_report_status(esp_rmaker_ota_handle_t ota_handle, ota_status_t status, char *additional_info)
|
||||
{
|
||||
ESP_LOGI(TAG, "Reporting %s: %s", esp_rmaker_ota_status_to_string(status), additional_info);
|
||||
|
||||
if (!ota_handle) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)ota_handle;
|
||||
esp_err_t err = ESP_FAIL;
|
||||
if (ota->type == OTA_USING_PARAMS) {
|
||||
err = esp_rmaker_ota_report_status_using_params(ota_handle, status, additional_info);
|
||||
} else if (ota->type == OTA_USING_TOPICS) {
|
||||
err = esp_rmaker_ota_report_status_using_topics(ota_handle, status, additional_info);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)ota_handle;
|
||||
ota->last_reported_status = status;
|
||||
}
|
||||
esp_rmaker_ota_post_event(esp_rmaker_ota_status_to_event(status), additional_info, strlen(additional_info) + 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
void esp_rmaker_ota_common_cb(void *priv)
|
||||
{
|
||||
if (!priv) {
|
||||
return;
|
||||
}
|
||||
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)priv;
|
||||
if (!ota->url) {
|
||||
goto ota_finish;
|
||||
}
|
||||
esp_rmaker_ota_data_t ota_data = {
|
||||
.url = ota->url,
|
||||
.filesize = ota->filesize,
|
||||
.fw_version = ota->fw_version,
|
||||
.ota_job_id = (char *)ota->transient_priv,
|
||||
.server_cert = ota->server_cert,
|
||||
.priv = ota->priv,
|
||||
.metadata = ota->metadata
|
||||
};
|
||||
ota->ota_cb((esp_rmaker_ota_handle_t) ota, &ota_data);
|
||||
ota_finish:
|
||||
if (ota->type == OTA_USING_PARAMS) {
|
||||
esp_rmaker_ota_finish_using_params(ota);
|
||||
} else if (ota->type == OTA_USING_TOPICS) {
|
||||
esp_rmaker_ota_finish_using_topics(ota);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t validate_image_header(esp_rmaker_ota_handle_t ota_handle,
|
||||
esp_app_desc_t *new_app_info)
|
||||
{
|
||||
if (new_app_info == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
esp_app_desc_t running_app_info;
|
||||
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
|
||||
ESP_LOGD(TAG, "Running firmware version: %s", running_app_info.version);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ESP_RMAKER_SKIP_PROJECT_NAME_CHECK
|
||||
if (memcmp(new_app_info->project_name, running_app_info.project_name, sizeof(new_app_info->project_name)) != 0) {
|
||||
ESP_LOGW(TAG, "OTA Image built for Project: %s. Expected: %s",
|
||||
new_app_info->project_name, running_app_info.project_name);
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_REJECTED, "Project Name mismatch");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ESP_RMAKER_SKIP_VERSION_CHECK
|
||||
if (memcmp(new_app_info->version, running_app_info.version, sizeof(new_app_info->version)) == 0) {
|
||||
ESP_LOGW(TAG, "Current running version is same as the new. We will not continue the update.");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_REJECTED, "Same version received");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ESP_RMAKER_SKIP_SECURE_VERSION_CHECK
|
||||
if (esp_efuse_check_secure_version(new_app_info->secure_version) == false) {
|
||||
ESP_LOGW(TAG, "New secure version is lower than stored in efuse. We will not continue the update.");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_REJECTED, "Lower secure version received");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT
|
||||
|
||||
/* Retry delay for cases wherein time info itself is not available */
|
||||
#define OTA_FETCH_RETRY_DELAY 30
|
||||
#define MINUTES_IN_DAY (24 * 60)
|
||||
#define OTA_DELAY_TIME_BUFFER 5
|
||||
|
||||
/* Check if time data is available in the metadata. Format
|
||||
* {"download_window":{"end":1155,"start":1080},"validity":{"end":1665426600,"start":1665081000}}
|
||||
*/
|
||||
esp_rmaker_ota_action_t esp_rmaker_ota_handle_time(jparse_ctx_t *jptr, esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data)
|
||||
{
|
||||
bool time_info = false;
|
||||
int start_min = -1, end_min = -1, start_date = -1, end_date = -1;
|
||||
if (json_obj_get_object(jptr, "download_window") == 0) {
|
||||
/* Download window means specific time of day. Eg, Between 02:00am and 05:00am only */
|
||||
time_info = true;
|
||||
json_obj_get_int(jptr, "start", &start_min);
|
||||
json_obj_get_int(jptr, "end", &end_min);
|
||||
json_obj_leave_object(jptr);
|
||||
ESP_LOGI(TAG, "Download Window : %d %d", start_min, end_min);
|
||||
}
|
||||
if (json_obj_get_object(jptr, "validity") == 0) {
|
||||
/* Validity indicates start and end epoch time, typicaly useful if OTA is to be performed between some dates */
|
||||
time_info = true;
|
||||
json_obj_get_int(jptr, "start", &start_date);
|
||||
json_obj_get_int(jptr, "end", &end_date);
|
||||
json_obj_leave_object(jptr);
|
||||
ESP_LOGI(TAG, "Validity : %d %d", start_date, end_date);
|
||||
}
|
||||
if (time_info) {
|
||||
/* If time info is present, but time is not yet synchronised, we will re-fetch OTA after some time */
|
||||
if (esp_rmaker_time_check() != true) {
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_DELAYED, "No time information available yet.");
|
||||
esp_rmaker_ota_fetch_with_delay(OTA_FETCH_RETRY_DELAY);
|
||||
return OTA_DELAYED;
|
||||
}
|
||||
time_t current_timestamp = 0;
|
||||
struct tm current_time = {0};
|
||||
time(¤t_timestamp);
|
||||
localtime_r(¤t_timestamp, ¤t_time);
|
||||
|
||||
/* Check for date validity first */
|
||||
if ((start_date != -1) && (current_timestamp < start_date)) {
|
||||
int delay_time = start_date - current_timestamp;
|
||||
/* The delay logic here can include the start_min and end_min as well, but it makes the logic quite complex,
|
||||
* just for a minor optimisation.
|
||||
*/
|
||||
ESP_LOGI(TAG, "Delaying OTA by %d seconds (%d min) as it is not valid yet.", delay_time, delay_time / 60);
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_DELAYED, "Not within valid window.");
|
||||
esp_rmaker_ota_fetch_with_delay(delay_time + OTA_DELAY_TIME_BUFFER);
|
||||
return OTA_DELAYED;
|
||||
} else if ((end_date != -1) && (current_timestamp > end_date)) {
|
||||
ESP_LOGE(TAG, "OTA download window lapsed");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "OTA download window lapsed.");
|
||||
return OTA_ERR;
|
||||
}
|
||||
|
||||
/* Check for download window */
|
||||
if (start_min != -1) {
|
||||
/* end_min is required if start_min is provided */
|
||||
if (end_min == -1) {
|
||||
ESP_LOGE(TAG, "Download window should have an end time if start time is specified.");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Invalid download window specified.");
|
||||
return OTA_ERR;
|
||||
}
|
||||
int cur_min = current_time.tm_hour * 60 + current_time.tm_min;
|
||||
if (start_min > end_min) {
|
||||
/* This means that the window is across midnight (Eg. 23:00 to 02:00 i.e. 1380 to 120).
|
||||
* We are just moving the window here such that start_min becomes 0 and the comparisons are simplified.
|
||||
* For this example, diff_min will be 1440 - 1380 = 60.
|
||||
* Effective end_min: 180
|
||||
* If cur_time is 18:00, effective cur_time = 1080 + 60 = 1140
|
||||
* If cur_time is 23:30, effective cur_time = 1410 + 60 = 1470 ( > MINUTES_IN_DAY)
|
||||
* So, cur_time = 1470 - 1440 = 30
|
||||
* */
|
||||
int diff_min = MINUTES_IN_DAY - start_min;
|
||||
start_min = 0;
|
||||
end_min += diff_min;
|
||||
cur_min += diff_min;
|
||||
if (cur_min >= MINUTES_IN_DAY) {
|
||||
cur_min -= MINUTES_IN_DAY;
|
||||
}
|
||||
}
|
||||
/* Current time is within OTA download window */
|
||||
if ((cur_min >= start_min) && (cur_min <= end_min)) {
|
||||
ESP_LOGI(TAG, "OTA received within download window.");
|
||||
return OTA_OK;
|
||||
} else {
|
||||
/* Delay the OTA if it is not in the download window. Even if it later goes outside the valid date range,
|
||||
* that will be handled in subsequent ota fetch. Reporting failure here itself would mark the OTA job
|
||||
* as failed and the node will no more get the OTA even if it tries to fetch it again due to a reboot or
|
||||
* other action within the download window.
|
||||
*/
|
||||
int delay_min = start_min - cur_min;
|
||||
if (delay_min < 0) {
|
||||
delay_min += MINUTES_IN_DAY;
|
||||
}
|
||||
ESP_LOGI(TAG, "Delaying OTA by %d seconds (%d min) as it is not within download window.", delay_min * 60, delay_min);
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_DELAYED, "Not within download window.");
|
||||
esp_rmaker_ota_fetch_with_delay(delay_min * 60 + OTA_DELAY_TIME_BUFFER);
|
||||
return OTA_DELAYED;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGI(TAG, "OTA received within validity period.");
|
||||
}
|
||||
}
|
||||
return OTA_OK;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT */
|
||||
|
||||
esp_rmaker_ota_action_t esp_rmaker_ota_handle_metadata(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data)
|
||||
{
|
||||
if (!ota_data->metadata) {
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_rmaker_ota_action_t ota_action = OTA_OK;
|
||||
jparse_ctx_t jctx;
|
||||
if (json_parse_start(&jctx, ota_data->metadata, strlen(ota_data->metadata)) == 0) {
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT
|
||||
/* Handle OTA timing data, if any */
|
||||
ota_action = esp_rmaker_ota_handle_time(&jctx, ota_handle, ota_data);
|
||||
#endif /* CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT */
|
||||
json_parse_end(&jctx);
|
||||
}
|
||||
return ota_action;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_ota_default_cb(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data)
|
||||
{
|
||||
if (!ota_data->url) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Handle OTA metadata, if any */
|
||||
if (ota_data->metadata) {
|
||||
if (esp_rmaker_ota_handle_metadata(ota_handle, ota_data) != OTA_OK) {
|
||||
ESP_LOGW(TAG, "Cannot proceed with the OTA as per the metadata received.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
esp_rmaker_ota_post_event(RMAKER_OTA_EVENT_STARTING, NULL, 0);
|
||||
int buffer_size_tx = DEF_HTTP_TX_BUFFER_SIZE;
|
||||
/* In case received url is longer, we will increase the tx buffer size
|
||||
* to accomodate the longer url and other headers.
|
||||
*/
|
||||
if (strlen(ota_data->url) > buffer_size_tx) {
|
||||
buffer_size_tx = strlen(ota_data->url) + 128;
|
||||
}
|
||||
esp_err_t ota_finish_err = ESP_OK;
|
||||
esp_http_client_config_t config = {
|
||||
.url = ota_data->url,
|
||||
#ifdef ESP_RMAKER_USE_CERT_BUNDLE
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
#else
|
||||
.cert_pem = ota_data->server_cert,
|
||||
#endif
|
||||
.timeout_ms = 5000,
|
||||
.buffer_size = DEF_HTTP_RX_BUFFER_SIZE,
|
||||
.buffer_size_tx = buffer_size_tx,
|
||||
.keep_alive_enable = true
|
||||
};
|
||||
#ifdef CONFIG_ESP_RMAKER_SKIP_COMMON_NAME_CHECK
|
||||
config.skip_cert_common_name_check = true;
|
||||
#endif
|
||||
|
||||
esp_https_ota_config_t ota_config = {
|
||||
.http_config = &config,
|
||||
};
|
||||
|
||||
if (ota_data->filesize) {
|
||||
ESP_LOGD(TAG, "Received file size: %d", ota_data->filesize);
|
||||
}
|
||||
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Starting OTA Upgrade");
|
||||
|
||||
/* Using a warning just to highlight the message */
|
||||
ESP_LOGW(TAG, "Starting OTA. This may take time.");
|
||||
esp_https_ota_handle_t https_ota_handle = NULL;
|
||||
esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ESP HTTPS OTA Begin failed");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "ESP HTTPS OTA Begin failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
/* Get the current Wi-Fi power save type. In case OTA fails and we need this
|
||||
* to restore power saving.
|
||||
*/
|
||||
wifi_ps_type_t ps_type;
|
||||
esp_wifi_get_ps(&ps_type);
|
||||
/* Disable Wi-Fi power save to speed up OTA, iff BT is controller is idle/disabled.
|
||||
* Co-ex requirement, device panics otherwise.*/
|
||||
#if CONFIG_BT_ENABLED
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
}
|
||||
#else
|
||||
esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
|
||||
esp_app_desc_t app_desc;
|
||||
err = esp_https_ota_get_img_desc(https_ota_handle, &app_desc);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_https_ota_read_img_desc failed");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Failed to read image decription");
|
||||
goto ota_end;
|
||||
}
|
||||
err = validate_image_header(ota_handle, &app_desc);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "image header verification failed");
|
||||
goto ota_end;
|
||||
}
|
||||
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Downloading Firmware Image");
|
||||
int count = 0;
|
||||
while (1) {
|
||||
err = esp_https_ota_perform(https_ota_handle);
|
||||
if (err == ESP_ERR_INVALID_VERSION) {
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_REJECTED, "Chip revision mismatch");
|
||||
goto ota_end;
|
||||
}
|
||||
if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
|
||||
break;
|
||||
}
|
||||
/* esp_https_ota_perform returns after every read operation which gives user the ability to
|
||||
* monitor the status of OTA upgrade by calling esp_https_ota_get_image_len_read, which gives length of image
|
||||
* data read so far.
|
||||
* We are using a counter just to reduce the number of prints
|
||||
*/
|
||||
|
||||
count++;
|
||||
if (count == 50) {
|
||||
ESP_LOGI(TAG, "Image bytes read: %d", esp_https_ota_get_image_len_read(https_ota_handle));
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed %s", esp_err_to_name(err));
|
||||
char description[40];
|
||||
snprintf(description, sizeof(description), "OTA failed: Error %s", esp_err_to_name(err));
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, description);
|
||||
}
|
||||
|
||||
if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) {
|
||||
// the OTA image was not completely received and user can customise the response to this situation.
|
||||
ESP_LOGE(TAG, "Complete data was not received.");
|
||||
} else {
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Firmware Image download complete");
|
||||
}
|
||||
|
||||
ota_end:
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
esp_wifi_set_ps(ps_type);
|
||||
}
|
||||
#else
|
||||
esp_wifi_set_ps(ps_type);
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
ota_finish_err = esp_https_ota_finish(https_ota_handle);
|
||||
if ((err == ESP_OK) && (ota_finish_err == ESP_OK)) {
|
||||
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err == ESP_OK) {
|
||||
uint8_t ota_update = 1;
|
||||
nvs_set_blob(handle, RMAKER_OTA_UPDATE_FLAG_NVS_NAME, &ota_update, sizeof(ota_update));
|
||||
nvs_close(handle);
|
||||
}
|
||||
/* Success will be reported after a reboot since Rollback is enabled */
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Rebooting into new firmware");
|
||||
#else
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_SUCCESS, "OTA Upgrade finished successfully");
|
||||
#endif
|
||||
#ifndef CONFIG_ESP_RMAKER_OTA_DISABLE_AUTO_REBOOT
|
||||
ESP_LOGI(TAG, "OTA upgrade successful. Rebooting in %d seconds...", OTA_REBOOT_TIMER_SEC);
|
||||
esp_rmaker_reboot(OTA_REBOOT_TIMER_SEC);
|
||||
#else
|
||||
ESP_LOGI(TAG, "OTA upgrade successful. Auto reboot is disabled. Requesting a Reboot via Event handler.");
|
||||
esp_rmaker_ota_post_event(RMAKER_OTA_EVENT_REQ_FOR_REBOOT, NULL, 0);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
} else {
|
||||
if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||
ESP_LOGE(TAG, "Image validation failed, image is corrupted");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Image validation failed");
|
||||
} else {
|
||||
/* Not reporting status here, because relevant error will already be reported
|
||||
* in some earlier step
|
||||
*/
|
||||
ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed %d", ota_finish_err);
|
||||
}
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)arg;
|
||||
esp_rmaker_ota_diag_status_t diag_status = OTA_DIAG_STATUS_SUCCESS;
|
||||
if (ota->ota_diag) {
|
||||
esp_rmaker_ota_diag_priv_t ota_diag_priv = {
|
||||
.state = OTA_DIAG_STATE_POST_MQTT,
|
||||
.rmaker_ota = ota->validation_in_progress
|
||||
};
|
||||
diag_status = ota->ota_diag(&ota_diag_priv, ota->priv);
|
||||
}
|
||||
if (diag_status == OTA_DIAG_STATUS_SUCCESS) {
|
||||
esp_rmaker_ota_mark_valid();
|
||||
} else if (diag_status == OTA_DIAG_STATUS_FAIL) {
|
||||
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
|
||||
esp_rmaker_ota_mark_invalid();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Waiting for application to validate OTA.");
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_erase_rollback_flag(void)
|
||||
{
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err == ESP_OK) {
|
||||
nvs_erase_key(handle, RMAKER_OTA_UPDATE_FLAG_NVS_NAME);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_ota_mark_valid(void)
|
||||
{
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
esp_ota_img_states_t ota_state;
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||
if (ota_state != ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
ESP_LOGW(TAG, "OTA Already marked as valid");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
esp_event_handler_unregister(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &event_handler);
|
||||
esp_rmaker_ota_t *ota = g_ota_priv;
|
||||
if (!ota) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
esp_rmaker_erase_rollback_flag();
|
||||
ota->ota_in_progress = false;
|
||||
if (ota->rollback_timer) {
|
||||
xTimerStop(ota->rollback_timer, portMAX_DELAY);
|
||||
xTimerDelete(ota->rollback_timer, portMAX_DELAY);
|
||||
ota->rollback_timer = NULL;
|
||||
}
|
||||
esp_rmaker_ota_report_status((esp_rmaker_ota_handle_t )ota, OTA_STATUS_SUCCESS, "OTA Upgrade finished and verified successfully");
|
||||
if (ota->type == OTA_USING_TOPICS) {
|
||||
if (esp_rmaker_ota_fetch_with_delay(RMAKER_OTA_FETCH_DELAY) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to create OTA Fetch timer.");
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_ota_mark_invalid(void)
|
||||
{
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
esp_ota_img_states_t ota_state;
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||
if (ota_state != ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
ESP_LOGE(TAG, "Cannot rollback due to invalid OTA state.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static void esp_ota_rollback(TimerHandle_t handle)
|
||||
{
|
||||
ESP_LOGE(TAG, "Could not verify firmware even after %d seconds since boot-up. Rolling back.",
|
||||
RMAKER_OTA_ROLLBACK_WAIT_PERIOD);
|
||||
esp_rmaker_ota_mark_invalid();
|
||||
}
|
||||
|
||||
static esp_err_t esp_ota_check_for_mqtt(esp_rmaker_ota_t *ota)
|
||||
{
|
||||
ota->rollback_timer = xTimerCreate("ota_rollback_tm", (RMAKER_OTA_ROLLBACK_WAIT_PERIOD * 1000) / portTICK_PERIOD_MS,
|
||||
pdTRUE, NULL, esp_ota_rollback);
|
||||
if (ota->rollback_timer) {
|
||||
xTimerStart(ota->rollback_timer, 0);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Could not create rollback timer. Will require manual reboot if firmware verification fails");
|
||||
}
|
||||
|
||||
return esp_event_handler_register(RMAKER_COMMON_EVENT, RMAKER_MQTT_EVENT_CONNECTED, &event_handler, ota);
|
||||
}
|
||||
|
||||
static void esp_rmaker_ota_manage_rollback(esp_rmaker_ota_t *ota)
|
||||
{
|
||||
/* If rollback is enabled, and the ota update flag is found, it means that the OTA validation is pending
|
||||
*/
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err == ESP_OK) {
|
||||
uint8_t ota_update = 0;
|
||||
size_t len = sizeof(ota_update);
|
||||
if ((err = nvs_get_blob(handle, RMAKER_OTA_UPDATE_FLAG_NVS_NAME, &ota_update, &len)) == ESP_OK) {
|
||||
ota->validation_in_progress = true;
|
||||
}
|
||||
nvs_close(handle);
|
||||
}
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
esp_ota_img_states_t ota_state;
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "OTA state = %d", ota_state);
|
||||
/* Not checking for CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE here because the firmware may have
|
||||
* it disabled, but bootloader may have it enabled, in which case, we will have to
|
||||
* handle this state.
|
||||
*/
|
||||
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
ESP_LOGI(TAG, "First Boot after an OTA");
|
||||
/* Run diagnostic function */
|
||||
esp_rmaker_ota_diag_status_t diag_status = OTA_DIAG_STATUS_SUCCESS;
|
||||
if (ota->ota_diag) {
|
||||
esp_rmaker_ota_diag_priv_t ota_diag_priv = {
|
||||
.state = OTA_DIAG_STATE_INIT,
|
||||
.rmaker_ota = ota->validation_in_progress
|
||||
};
|
||||
diag_status = ota->ota_diag(&ota_diag_priv, ota->priv);
|
||||
}
|
||||
if (diag_status != OTA_DIAG_STATUS_FAIL) {
|
||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
|
||||
/* Will not mark the image valid here immediately, but instead will wait for
|
||||
* MQTT connection. The below flag will tell the OTA functions that the earlier
|
||||
* OTA is still in progress.
|
||||
*/
|
||||
ota->ota_in_progress = true;
|
||||
esp_ota_check_for_mqtt(ota);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
|
||||
esp_rmaker_ota_mark_invalid();
|
||||
}
|
||||
} else {
|
||||
/* If rollback is enabled, and the ota update flag is found, it means that the firmware was rolled back
|
||||
*/
|
||||
if (ota->validation_in_progress) {
|
||||
ota->rolled_back = true;
|
||||
esp_rmaker_erase_rollback_flag();
|
||||
if (ota->type == OTA_USING_PARAMS) {
|
||||
/* Calling this only for OTA_USING_PARAMS, because for OTA_USING_TOPICS,
|
||||
* the work queue function will manage the status reporting later.
|
||||
*/
|
||||
esp_rmaker_ota_report_status((esp_rmaker_ota_handle_t )ota,
|
||||
OTA_STATUS_REJECTED, "Firmware rolled back");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const esp_rmaker_ota_config_t ota_default_config = {
|
||||
.server_cert = esp_rmaker_ota_def_cert,
|
||||
};
|
||||
/* Enable the ESP RainMaker specific OTA */
|
||||
esp_err_t esp_rmaker_ota_enable(esp_rmaker_ota_config_t *ota_config, esp_rmaker_ota_type_t type)
|
||||
{
|
||||
if (ota_config == NULL) {
|
||||
ota_config = (esp_rmaker_ota_config_t *)&ota_default_config;
|
||||
}
|
||||
if ((type != OTA_USING_PARAMS) && (type != OTA_USING_TOPICS)) {
|
||||
ESP_LOGE(TAG,"Invalid arguments for esp_rmaker_ota_enable()");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
static bool ota_init_done;
|
||||
if (ota_init_done) {
|
||||
ESP_LOGE(TAG, "OTA already initialised");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_rmaker_ota_t *ota = MEM_CALLOC_EXTRAM(1, sizeof(esp_rmaker_ota_t));
|
||||
if (!ota) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for esp_rmaker_ota_t");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (ota_config->ota_cb) {
|
||||
ota->ota_cb = ota_config->ota_cb;
|
||||
} else {
|
||||
ota->ota_cb = esp_rmaker_ota_default_cb;
|
||||
}
|
||||
ota->ota_diag = ota_config->ota_diag;
|
||||
ota->priv = ota_config->priv;
|
||||
ota->server_cert = ota_config->server_cert;
|
||||
esp_err_t err = ESP_FAIL;
|
||||
ota->type = type;
|
||||
if (type == OTA_USING_PARAMS) {
|
||||
err = esp_rmaker_ota_enable_using_params(ota);
|
||||
} else if (type == OTA_USING_TOPICS) {
|
||||
err = esp_rmaker_ota_enable_using_topics(ota);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
esp_rmaker_ota_manage_rollback(ota);
|
||||
ota_init_done = true;
|
||||
g_ota_priv = ota;
|
||||
} else {
|
||||
free(ota);
|
||||
ESP_LOGE(TAG, "Failed to enable OTA");
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT
|
||||
esp_rmaker_time_sync_init(NULL);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_ota_enable_default(void)
|
||||
{
|
||||
return esp_rmaker_ota_enable(NULL, OTA_USING_TOPICS);
|
||||
}
|
||||
55
components/esp_rainmaker/src/ota/esp_rmaker_ota_internal.h
Normal file
55
components/esp_rainmaker/src/ota/esp_rmaker_ota_internal.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <esp_rmaker_ota.h>
|
||||
|
||||
#define RMAKER_OTA_NVS_NAMESPACE "rmaker_ota"
|
||||
#define RMAKER_OTA_JOB_ID_NVS_NAME "rmaker_ota_id"
|
||||
#define RMAKER_OTA_UPDATE_FLAG_NVS_NAME "ota_update"
|
||||
#define RMAKER_OTA_FETCH_DELAY 5
|
||||
|
||||
typedef struct {
|
||||
esp_rmaker_ota_type_t type;
|
||||
esp_rmaker_ota_cb_t ota_cb;
|
||||
void *priv;
|
||||
esp_rmaker_post_ota_diag_t ota_diag;
|
||||
TimerHandle_t rollback_timer;
|
||||
const char *server_cert;
|
||||
char *url;
|
||||
char *fw_version;
|
||||
int filesize;
|
||||
bool ota_in_progress;
|
||||
bool validation_in_progress;
|
||||
bool rolled_back;
|
||||
ota_status_t last_reported_status;
|
||||
void *transient_priv;
|
||||
char *metadata;
|
||||
} esp_rmaker_ota_t;
|
||||
|
||||
char *esp_rmaker_ota_status_to_string(ota_status_t status);
|
||||
void esp_rmaker_ota_common_cb(void *priv);
|
||||
void esp_rmaker_ota_finish_using_params(esp_rmaker_ota_t *ota);
|
||||
void esp_rmaker_ota_finish_using_topics(esp_rmaker_ota_t *ota);
|
||||
esp_err_t esp_rmaker_ota_enable_using_params(esp_rmaker_ota_t *ota);
|
||||
esp_err_t esp_rmaker_ota_report_status_using_params(esp_rmaker_ota_handle_t ota_handle,
|
||||
ota_status_t status, char *additional_info);
|
||||
esp_err_t esp_rmaker_ota_enable_using_topics(esp_rmaker_ota_t *ota);
|
||||
esp_err_t esp_rmaker_ota_report_status_using_topics(esp_rmaker_ota_handle_t ota_handle,
|
||||
ota_status_t status, char *additional_info);
|
||||
114
components/esp_rainmaker/src/ota/esp_rmaker_ota_using_params.c
Normal file
114
components/esp_rainmaker/src/ota/esp_rmaker_ota_using_params.c
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_rmaker_work_queue.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_standard_types.h>
|
||||
#include <esp_rmaker_standard_params.h>
|
||||
#include <esp_rmaker_standard_services.h>
|
||||
#include <esp_rmaker_ota.h>
|
||||
|
||||
#include "esp_rmaker_ota_internal.h"
|
||||
#include "esp_rmaker_mqtt.h"
|
||||
|
||||
static const char *TAG = "esp_rmaker_ota_using_params";
|
||||
|
||||
#define ESP_RMAKER_OTA_SERV_NAME "OTA"
|
||||
|
||||
void esp_rmaker_ota_finish_using_params(esp_rmaker_ota_t *ota)
|
||||
{
|
||||
if (ota->url) {
|
||||
free(ota->url);
|
||||
ota->url = NULL;
|
||||
}
|
||||
ota->filesize = 0;
|
||||
if (ota->transient_priv) {
|
||||
ota->transient_priv = NULL;
|
||||
}
|
||||
if (ota->metadata) {
|
||||
free(ota->metadata);
|
||||
ota->metadata = NULL;
|
||||
}
|
||||
ota->ota_in_progress = false;
|
||||
}
|
||||
static esp_err_t esp_rmaker_ota_service_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||
{
|
||||
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)priv_data;
|
||||
if (!ota) {
|
||||
ESP_LOGE(TAG, "No OTA specific data received in callback");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (ota->ota_in_progress) {
|
||||
ESP_LOGE(TAG, "OTA already in progress. Please try later.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_OTA_URL) == 0) {
|
||||
ESP_LOGI(TAG, "Received value = %s for %s - %s",
|
||||
val.val.s, esp_rmaker_device_get_name(device), esp_rmaker_param_get_name(param));
|
||||
if (ota->url) {
|
||||
free(ota->url);
|
||||
ota->url = NULL;
|
||||
}
|
||||
ota->url = strdup(val.val.s);
|
||||
if (ota->url) {
|
||||
ota->filesize = 0;
|
||||
ota->ota_in_progress = true;
|
||||
ota->transient_priv = (void *)device;
|
||||
ota->metadata = NULL;
|
||||
if (esp_rmaker_work_queue_add_task(esp_rmaker_ota_common_cb, ota) != ESP_OK) {
|
||||
esp_rmaker_ota_finish_using_params(ota);
|
||||
} else {
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_ota_report_status_using_params(esp_rmaker_ota_handle_t ota_handle, ota_status_t status, char *additional_info)
|
||||
{
|
||||
const esp_rmaker_device_t *device = esp_rmaker_node_get_device_by_name(esp_rmaker_get_node(), ESP_RMAKER_OTA_SERV_NAME);
|
||||
if (!device) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_rmaker_param_t *info_param = esp_rmaker_device_get_param_by_type(device, ESP_RMAKER_PARAM_OTA_INFO);
|
||||
esp_rmaker_param_t *status_param = esp_rmaker_device_get_param_by_type(device, ESP_RMAKER_PARAM_OTA_STATUS);
|
||||
|
||||
esp_rmaker_param_update_and_report(info_param, esp_rmaker_str(additional_info));
|
||||
esp_rmaker_param_update_and_report(status_param, esp_rmaker_str(esp_rmaker_ota_status_to_string(status)));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Enable the ESP RainMaker specific OTA */
|
||||
esp_err_t esp_rmaker_ota_enable_using_params(esp_rmaker_ota_t *ota)
|
||||
{
|
||||
esp_rmaker_device_t *service = esp_rmaker_ota_service_create(ESP_RMAKER_OTA_SERV_NAME, ota);
|
||||
if (!service) {
|
||||
ESP_LOGE(TAG, "Failed to create OTA Service");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_rmaker_device_add_cb(service, esp_rmaker_ota_service_cb, NULL);
|
||||
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), service);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "OTA enabled with Params");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
346
components/esp_rainmaker/src/ota/esp_rmaker_ota_using_topics.c
Normal file
346
components/esp_rainmaker/src/ota/esp_rmaker_ota_using_topics.c
Normal file
@@ -0,0 +1,346 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <json_parser.h>
|
||||
#include <json_generator.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_system.h>
|
||||
#include <nvs.h>
|
||||
#include <esp_rmaker_work_queue.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_ota.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
#include "esp_rmaker_internal.h"
|
||||
#include "esp_rmaker_ota_internal.h"
|
||||
#include "esp_rmaker_mqtt.h"
|
||||
#include "esp_rmaker_mqtt_topics.h"
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_AUTOFETCH
|
||||
#include <esp_timer.h>
|
||||
static esp_timer_handle_t ota_autofetch_timer;
|
||||
/* Autofetch period in hours */
|
||||
#define OTA_AUTOFETCH_PERIOD CONFIG_ESP_RMAKER_OTA_AUTOFETCH_PERIOD
|
||||
/* Autofetch period in micro-seconds */
|
||||
static uint64_t ota_autofetch_period = (OTA_AUTOFETCH_PERIOD * 60 * 60 * 1000000LL);
|
||||
#endif /* CONFIG_ESP_RMAKER_OTA_AUTOFETCH */
|
||||
|
||||
static const char *TAG = "esp_rmaker_ota_using_topics";
|
||||
|
||||
esp_err_t esp_rmaker_ota_report_status_using_topics(esp_rmaker_ota_handle_t ota_handle, ota_status_t status, char *additional_info)
|
||||
{
|
||||
if (!ota_handle) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)ota_handle;
|
||||
|
||||
char publish_payload[200];
|
||||
json_gen_str_t jstr;
|
||||
json_gen_str_start(&jstr, publish_payload, sizeof(publish_payload), NULL, NULL);
|
||||
json_gen_start_object(&jstr);
|
||||
if (ota->transient_priv) {
|
||||
json_gen_obj_set_string(&jstr, "ota_job_id", (char *)ota->transient_priv);
|
||||
} else {
|
||||
/* This will get executed only when the OTA status is being reported after a reboot, either to
|
||||
* indicate successful verification of new firmware, or to indicate that firmware was rolled back
|
||||
*/
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err == ESP_OK) {
|
||||
char job_id[64] = {0};
|
||||
size_t len = sizeof(job_id);
|
||||
if ((err = nvs_get_blob(handle, RMAKER_OTA_JOB_ID_NVS_NAME, job_id, &len)) == ESP_OK) {
|
||||
json_gen_obj_set_string(&jstr, "ota_job_id", job_id);
|
||||
nvs_erase_key(handle, RMAKER_OTA_JOB_ID_NVS_NAME);
|
||||
}
|
||||
nvs_close(handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Not reporting any status, since there is no Job ID available");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
json_gen_obj_set_string(&jstr, "status", esp_rmaker_ota_status_to_string(status));
|
||||
json_gen_obj_set_string(&jstr, "additional_info", additional_info);
|
||||
json_gen_end_object(&jstr);
|
||||
json_gen_str_end(&jstr);
|
||||
|
||||
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||
esp_rmaker_create_mqtt_topic(publish_topic, sizeof(publish_topic), OTASTATUS_TOPIC_SUFFIX, OTASTATUS_TOPIC_RULE);
|
||||
ESP_LOGI(TAG, "%s",publish_payload);
|
||||
esp_err_t err = esp_rmaker_mqtt_publish(publish_topic, publish_payload, strlen(publish_payload),
|
||||
RMAKER_MQTT_QOS1, NULL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_rmaker_mqtt_publish_data returned error %d",err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_rmaker_ota_finish_using_topics(esp_rmaker_ota_t *ota)
|
||||
{
|
||||
if (ota->url) {
|
||||
free(ota->url);
|
||||
ota->url = NULL;
|
||||
}
|
||||
ota->filesize = 0;
|
||||
if (ota->transient_priv) {
|
||||
free(ota->transient_priv);
|
||||
ota->transient_priv = NULL;
|
||||
}
|
||||
if (ota->metadata) {
|
||||
free(ota->metadata);
|
||||
ota->metadata = NULL;
|
||||
}
|
||||
if (ota->fw_version) {
|
||||
free(ota->fw_version);
|
||||
ota->fw_version = NULL;
|
||||
}
|
||||
ota->ota_in_progress = false;
|
||||
}
|
||||
static void ota_url_handler(const char *topic, void *payload, size_t payload_len, void *priv_data)
|
||||
{
|
||||
if (!priv_data) {
|
||||
return;
|
||||
}
|
||||
esp_rmaker_ota_handle_t ota_handle = priv_data;
|
||||
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)ota_handle;
|
||||
if (ota->ota_in_progress) {
|
||||
ESP_LOGE(TAG, "OTA already in progress. Please try later.");
|
||||
return;
|
||||
}
|
||||
ota->ota_in_progress = true;
|
||||
/* Starting Firmware Upgrades */
|
||||
ESP_LOGI(TAG, "Upgrade Handler got:%.*s on %s topic\n", (int) payload_len, (char *)payload, topic);
|
||||
/*
|
||||
{
|
||||
"ota_job_id": "<ota_job_id>",
|
||||
"url": "<fw_url>",
|
||||
"fw_version": "<fw_version>",
|
||||
"filesize": <size_in_bytes>
|
||||
}
|
||||
*/
|
||||
jparse_ctx_t jctx;
|
||||
char *url = NULL, *ota_job_id = NULL, *fw_version = NULL;
|
||||
int ret = json_parse_start(&jctx, (char *)payload, (int) payload_len);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Invalid JSON received: %s", (char *)payload);
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. JSON Payload error");
|
||||
ota->ota_in_progress = false;
|
||||
return;
|
||||
}
|
||||
int len = 0;
|
||||
ret = json_obj_get_strlen(&jctx, "ota_job_id", &len);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Aborted. OTA Job ID not found in JSON");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. OTA Updated ID not found in JSON");
|
||||
goto end;
|
||||
}
|
||||
len++; /* Increment for NULL character */
|
||||
ota_job_id = MEM_CALLOC_EXTRAM(1, len);
|
||||
if (!ota_job_id) {
|
||||
ESP_LOGE(TAG, "Aborted. OTA Job ID memory allocation failed");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. OTA Updated ID memory allocation failed");
|
||||
goto end;
|
||||
}
|
||||
json_obj_get_string(&jctx, "ota_job_id", ota_job_id, len);
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err == ESP_OK) {
|
||||
nvs_set_blob(handle, RMAKER_OTA_JOB_ID_NVS_NAME, ota_job_id, strlen(ota_job_id));
|
||||
nvs_close(handle);
|
||||
}
|
||||
ESP_LOGI(TAG, "OTA Job ID: %s", ota_job_id);
|
||||
ota->transient_priv = ota_job_id;
|
||||
len = 0;
|
||||
ret = json_obj_get_strlen(&jctx, "url", &len);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Aborted. URL not found in JSON");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. URL not found in JSON");
|
||||
goto end;
|
||||
}
|
||||
len++; /* Increment for NULL character */
|
||||
url = MEM_CALLOC_EXTRAM(1, len);
|
||||
if (!url) {
|
||||
ESP_LOGE(TAG, "Aborted. URL memory allocation failed");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. URL memory allocation failed");
|
||||
goto end;
|
||||
}
|
||||
json_obj_get_string(&jctx, "url", url, len);
|
||||
ESP_LOGI(TAG, "URL: %s", url);
|
||||
|
||||
int filesize = 0;
|
||||
json_obj_get_int(&jctx, "file_size", &filesize);
|
||||
ESP_LOGI(TAG, "File Size: %d", filesize);
|
||||
|
||||
len = 0;
|
||||
ret = json_obj_get_strlen(&jctx, "fw_version", &len);
|
||||
if (ret == ESP_OK && len > 0) {
|
||||
len++; /* Increment for NULL character */
|
||||
fw_version = MEM_CALLOC_EXTRAM(1, len);
|
||||
if (!fw_version) {
|
||||
ESP_LOGE(TAG, "Aborted. Firmware version memory allocation failed");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. Firmware version memory allocation failed");
|
||||
goto end;
|
||||
}
|
||||
json_obj_get_string(&jctx, "fw_version", fw_version, len);
|
||||
ESP_LOGI(TAG, "Firmware version: %s", fw_version);
|
||||
}
|
||||
|
||||
int metadata_size = 0;
|
||||
char *metadata = NULL;
|
||||
ret = json_obj_get_object_strlen(&jctx, "metadata", &metadata_size);
|
||||
if (ret == ESP_OK && metadata_size > 0) {
|
||||
metadata_size++; /* Increment for NULL character */
|
||||
metadata = MEM_CALLOC_EXTRAM(1, metadata_size);
|
||||
if (!metadata) {
|
||||
ESP_LOGE(TAG, "Aborted. OTA metadata memory allocation failed");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. OTA metadata memory allocation failed");
|
||||
goto end;
|
||||
}
|
||||
json_obj_get_object_str(&jctx, "metadata", metadata, metadata_size);
|
||||
ota->metadata = metadata;
|
||||
}
|
||||
|
||||
json_parse_end(&jctx);
|
||||
if (ota->url) {
|
||||
free(ota->url);
|
||||
}
|
||||
ota->url = url;
|
||||
ota->fw_version = fw_version;
|
||||
ota->filesize = filesize;
|
||||
ota->ota_in_progress = true;
|
||||
if (esp_rmaker_work_queue_add_task(esp_rmaker_ota_common_cb, ota) != ESP_OK) {
|
||||
esp_rmaker_ota_finish_using_topics(ota);
|
||||
}
|
||||
return;
|
||||
end:
|
||||
if (url) {
|
||||
free(url);
|
||||
}
|
||||
if (fw_version) {
|
||||
free(fw_version);
|
||||
}
|
||||
esp_rmaker_ota_finish_using_topics(ota);
|
||||
json_parse_end(&jctx);
|
||||
return;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_ota_fetch(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Fetching OTA details, if any.");
|
||||
esp_rmaker_node_info_t *info = esp_rmaker_node_get_info(esp_rmaker_get_node());
|
||||
if (!info) {
|
||||
ESP_LOGE(TAG, "Node info not found. Cant send otafetch request");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
char publish_payload[150];
|
||||
json_gen_str_t jstr;
|
||||
json_gen_str_start(&jstr, publish_payload, sizeof(publish_payload), NULL, NULL);
|
||||
json_gen_start_object(&jstr);
|
||||
json_gen_obj_set_string(&jstr, "node_id", esp_rmaker_get_node_id());
|
||||
json_gen_obj_set_string(&jstr, "fw_version", info->fw_version);
|
||||
json_gen_end_object(&jstr);
|
||||
json_gen_str_end(&jstr);
|
||||
char publish_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||
esp_rmaker_create_mqtt_topic(publish_topic, sizeof(publish_topic), OTAFETCH_TOPIC_SUFFIX, OTAFETCH_TOPIC_RULE);
|
||||
esp_err_t err = esp_rmaker_mqtt_publish(publish_topic, publish_payload, strlen(publish_payload),
|
||||
RMAKER_MQTT_QOS1, NULL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "OTA Fetch Publish Error %d", err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void esp_rmaker_ota_autofetch_timer_cb(void *priv)
|
||||
{
|
||||
esp_rmaker_ota_fetch();
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_ota_subscribe(void *priv_data)
|
||||
{
|
||||
char subscribe_topic[MQTT_TOPIC_BUFFER_SIZE];
|
||||
|
||||
snprintf(subscribe_topic, sizeof(subscribe_topic),"node/%s/%s", esp_rmaker_get_node_id(), OTAURL_TOPIC_SUFFIX);
|
||||
|
||||
ESP_LOGI(TAG, "Subscribing to: %s", subscribe_topic);
|
||||
/* First unsubscribe, in case there is a stale subscription */
|
||||
esp_rmaker_mqtt_unsubscribe(subscribe_topic);
|
||||
esp_err_t err = esp_rmaker_mqtt_subscribe(subscribe_topic, ota_url_handler, RMAKER_MQTT_QOS1, priv_data);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "OTA URL Subscription Error %d", err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void esp_rmaker_ota_work_fn(void *priv_data)
|
||||
{
|
||||
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)priv_data;
|
||||
/* If the firmware was rolled back, indicate that first */
|
||||
if (ota->rolled_back) {
|
||||
esp_rmaker_ota_report_status((esp_rmaker_ota_handle_t )ota, OTA_STATUS_REJECTED, "Firmware rolled back");
|
||||
ota->rolled_back = false;
|
||||
}
|
||||
esp_rmaker_ota_subscribe(priv_data);
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_AUTOFETCH
|
||||
if (ota->ota_in_progress != true) {
|
||||
if (esp_rmaker_ota_fetch_with_delay(RMAKER_OTA_FETCH_DELAY) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to create OTA Fetch timer.");
|
||||
}
|
||||
}
|
||||
if (ota_autofetch_period > 0) {
|
||||
esp_timer_create_args_t autofetch_timer_conf = {
|
||||
.callback = esp_rmaker_ota_autofetch_timer_cb,
|
||||
.arg = priv_data,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "ota_autofetch_tm"
|
||||
};
|
||||
if (esp_timer_create(&autofetch_timer_conf, &ota_autofetch_timer) == ESP_OK) {
|
||||
esp_timer_start_periodic(ota_autofetch_timer, ota_autofetch_period);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to create OTA Autofetch timer");
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_OTA_AUTOFETCH */
|
||||
}
|
||||
|
||||
/* Enable the ESP RainMaker specific OTA */
|
||||
esp_err_t esp_rmaker_ota_enable_using_topics(esp_rmaker_ota_t *ota)
|
||||
{
|
||||
esp_err_t err = esp_rmaker_work_queue_add_task(esp_rmaker_ota_work_fn, ota);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "OTA enabled with Topics");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void esp_rmaker_ota_fetch_timer_cb(TimerHandle_t xTimer)
|
||||
{
|
||||
esp_rmaker_ota_fetch();
|
||||
xTimerDelete(xTimer, 0);
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_ota_fetch_with_delay(int time)
|
||||
{
|
||||
TimerHandle_t timer = xTimerCreate(NULL, (time * 1000) / portTICK_PERIOD_MS, pdFALSE, NULL, esp_rmaker_ota_fetch_timer_cb);
|
||||
if (timer == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
} else {
|
||||
xTimerStart(timer, 0);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
#include <esp_rmaker_standard_types.h>
|
||||
#include <esp_rmaker_standard_params.h>
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_switch_device_create(const char *dev_name,
|
||||
void *priv_data, bool power)
|
||||
{
|
||||
esp_rmaker_device_t *device = esp_rmaker_device_create(dev_name, ESP_RMAKER_DEVICE_SWITCH, priv_data);
|
||||
if (device) {
|
||||
esp_rmaker_device_add_param(device, esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, dev_name));
|
||||
esp_rmaker_param_t *primary = esp_rmaker_power_param_create(ESP_RMAKER_DEF_POWER_NAME, power);
|
||||
esp_rmaker_device_add_param(device, primary);
|
||||
esp_rmaker_device_assign_primary_param(device, primary);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_lightbulb_device_create(const char *dev_name,
|
||||
void *priv_data, bool power)
|
||||
{
|
||||
esp_rmaker_device_t *device = esp_rmaker_device_create(dev_name, ESP_RMAKER_DEVICE_LIGHTBULB, priv_data);
|
||||
if (device) {
|
||||
esp_rmaker_device_add_param(device, esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, dev_name));
|
||||
esp_rmaker_param_t *primary = esp_rmaker_power_param_create(ESP_RMAKER_DEF_POWER_NAME, power);
|
||||
esp_rmaker_device_add_param(device, primary);
|
||||
esp_rmaker_device_assign_primary_param(device, primary);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_fan_device_create(const char *dev_name,
|
||||
void *priv_data, bool power)
|
||||
{
|
||||
esp_rmaker_device_t *device = esp_rmaker_device_create(dev_name, ESP_RMAKER_DEVICE_FAN, priv_data);
|
||||
if (device) {
|
||||
esp_rmaker_device_add_param(device, esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, dev_name));
|
||||
esp_rmaker_param_t *primary = esp_rmaker_power_param_create(ESP_RMAKER_DEF_POWER_NAME, power);
|
||||
esp_rmaker_device_add_param(device, primary);
|
||||
esp_rmaker_device_assign_primary_param(device, primary);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_temp_sensor_device_create(const char *dev_name,
|
||||
void *priv_data, float temperature)
|
||||
{
|
||||
esp_rmaker_device_t *device = esp_rmaker_device_create(dev_name, ESP_RMAKER_DEVICE_TEMP_SENSOR, priv_data);
|
||||
if (device) {
|
||||
esp_rmaker_device_add_param(device, esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, dev_name));
|
||||
esp_rmaker_param_t *primary = esp_rmaker_temperature_param_create(ESP_RMAKER_DEF_TEMPERATURE_NAME, temperature);
|
||||
esp_rmaker_device_add_param(device, primary);
|
||||
esp_rmaker_device_assign_primary_param(device, primary);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_standard_types.h>
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_name_param_create(const char *param_name, const char *val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_NAME,
|
||||
esp_rmaker_str(val), PROP_FLAG_READ | PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_power_param_create(const char *param_name, bool val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_POWER,
|
||||
esp_rmaker_bool(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
if (param) {
|
||||
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_TOGGLE);
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_brightness_param_create(const char *param_name, int val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_BRIGHTNESS,
|
||||
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
if (param) {
|
||||
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_SLIDER);
|
||||
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(100), esp_rmaker_int(1));
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_hue_param_create(const char *param_name, int val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_HUE,
|
||||
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
if (param) {
|
||||
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_HUE_SLIDER);
|
||||
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(360), esp_rmaker_int(1));
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_saturation_param_create(const char *param_name, int val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_SATURATION,
|
||||
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
if (param) {
|
||||
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_SLIDER);
|
||||
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(100), esp_rmaker_int(1));
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_intensity_param_create(const char *param_name, int val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_INTENSITY,
|
||||
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
if (param) {
|
||||
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_SLIDER);
|
||||
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(100), esp_rmaker_int(1));
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_cct_param_create(const char *param_name, int val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_CCT,
|
||||
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
if (param) {
|
||||
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_SLIDER);
|
||||
esp_rmaker_param_add_bounds(param, esp_rmaker_int(2700), esp_rmaker_int(6500), esp_rmaker_int(100));
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_direction_param_create(const char *param_name, int val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_DIRECTION,
|
||||
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
if (param) {
|
||||
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_DROPDOWN);
|
||||
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(1), esp_rmaker_int(1));
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_speed_param_create(const char *param_name, int val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_SPEED,
|
||||
esp_rmaker_int(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
if (param) {
|
||||
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_SLIDER);
|
||||
esp_rmaker_param_add_bounds(param, esp_rmaker_int(0), esp_rmaker_int(5), esp_rmaker_int(1));
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_temperature_param_create(const char *param_name, float val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TEMPERATURE,
|
||||
esp_rmaker_float(val), PROP_FLAG_READ | PROP_FLAG_TIME_SERIES);
|
||||
if (param) {
|
||||
esp_rmaker_param_add_ui_type(param, ESP_RMAKER_UI_TEXT);
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_ota_status_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_OTA_STATUS,
|
||||
esp_rmaker_str(""), PROP_FLAG_READ);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_ota_info_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_OTA_INFO,
|
||||
esp_rmaker_str(""), PROP_FLAG_READ);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_ota_url_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_OTA_URL,
|
||||
esp_rmaker_str(""), PROP_FLAG_WRITE);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_timezone_param_create(const char *param_name, const char *val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TIMEZONE,
|
||||
esp_rmaker_str(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_timezone_posix_param_create(const char *param_name, const char *val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TIMEZONE_POSIX,
|
||||
esp_rmaker_str(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_schedules_param_create(const char *param_name, int max_schedules)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_SCHEDULES,
|
||||
esp_rmaker_array("[]"), PROP_FLAG_READ | PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||
esp_rmaker_param_add_array_max_count(param, max_schedules);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_scenes_param_create(const char *param_name, int max_scenes)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_SCENES,
|
||||
esp_rmaker_array("[]"), PROP_FLAG_READ | PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||
esp_rmaker_param_add_array_max_count(param, max_scenes);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_reboot_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_REBOOT,
|
||||
esp_rmaker_bool(false), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_factory_reset_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_FACTORY_RESET,
|
||||
esp_rmaker_bool(false), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_wifi_reset_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_WIFI_RESET,
|
||||
esp_rmaker_bool(false), PROP_FLAG_READ | PROP_FLAG_WRITE);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_local_control_pop_param_create(const char *param_name, const char *val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_LOCAL_CONTROL_POP,
|
||||
esp_rmaker_str(val), PROP_FLAG_READ);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *esp_rmaker_local_control_type_param_create(const char *param_name, int val)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_LOCAL_CONTROL_TYPE,
|
||||
esp_rmaker_int(val), PROP_FLAG_READ);
|
||||
return param;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
#include <esp_rmaker_standard_types.h>
|
||||
#include <esp_rmaker_standard_params.h>
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_ota_service_create(const char *serv_name, void *priv_data)
|
||||
{
|
||||
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_OTA, priv_data);
|
||||
if (service) {
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_ota_status_param_create(ESP_RMAKER_DEF_OTA_STATUS_NAME));
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_ota_info_param_create(ESP_RMAKER_DEF_OTA_INFO_NAME));
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_ota_url_param_create(ESP_RMAKER_DEF_OTA_URL_NAME));
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_time_service_create(const char *serv_name, const char *timezone,
|
||||
const char *timezone_posix, void *priv_data)
|
||||
{
|
||||
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_TIME, priv_data);
|
||||
if (service) {
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_timezone_param_create(
|
||||
ESP_RMAKER_DEF_TIMEZONE_NAME, timezone));
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_timezone_posix_param_create(
|
||||
ESP_RMAKER_DEF_TIMEZONE_POSIX_NAME, timezone_posix));
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_create_schedule_service(const char *serv_name, esp_rmaker_device_write_cb_t write_cb,
|
||||
esp_rmaker_device_read_cb_t read_cb, int max_schedules, void *priv_data)
|
||||
{
|
||||
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_SCHEDULE, priv_data);
|
||||
if (service) {
|
||||
esp_rmaker_device_add_cb(service, write_cb, read_cb);
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_schedules_param_create(ESP_RMAKER_DEF_SCHEDULE_NAME, max_schedules));
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_create_scenes_service(const char *serv_name, esp_rmaker_device_write_cb_t write_cb,
|
||||
esp_rmaker_device_read_cb_t read_cb, int max_scenes, bool deactivation_support, void *priv_data)
|
||||
{
|
||||
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_SCENES, priv_data);
|
||||
if (service) {
|
||||
esp_rmaker_device_add_cb(service, write_cb, read_cb);
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_scenes_param_create(ESP_RMAKER_DEF_SCENES_NAME, max_scenes));
|
||||
esp_rmaker_device_add_attribute(service, "deactivation_support", deactivation_support ? "yes" : "no");
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_create_system_service(const char *serv_name, void *priv_data)
|
||||
{
|
||||
return esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_SYSTEM, priv_data);
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_create_local_control_service(const char *serv_name, const char *pop, int sec_type, void *priv_data)
|
||||
{
|
||||
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_LOCAL_CONTROL, priv_data);
|
||||
if (service) {
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_local_control_pop_param_create(ESP_RMAKER_DEF_LOCAL_CONTROL_POP, pop));
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_local_control_type_param_create(ESP_RMAKER_DEF_LOCAL_CONTROL_TYPE, sec_type));
|
||||
}
|
||||
return service;
|
||||
}
|
||||
196
components/esp_rainmaker/src/thread_br/esp_rmaker_thread_br.c
Normal file
196
components/esp_rainmaker/src/thread_br/esp_rmaker_thread_br.c
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright 2024 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_check.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_rmaker_thread_br.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_rmaker_thread_br_priv.h"
|
||||
|
||||
static const char *TAG = "thread_br";
|
||||
static const esp_rmaker_device_t *thread_br_service = NULL;
|
||||
|
||||
static int char_to_num(char ch)
|
||||
{
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
return ch - '0';
|
||||
} else if (ch >= 'A' && ch <= 'F') {
|
||||
return ch - 'A' + 10;
|
||||
} else if (ch >= 'a' && ch <= 'f') {
|
||||
return ch - 'a' + 10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_bytes(uint8_t *buffer, size_t buffer_size, const char *str)
|
||||
{
|
||||
if (!str || strlen(str) % 2 != 0 || strlen(str) / 2 > buffer_size) {
|
||||
return -1;
|
||||
}
|
||||
for (size_t i = 0; i < strlen(str) / 2; ++i) {
|
||||
int byte_h = char_to_num(str[2 * i]);
|
||||
int byte_l = char_to_num(str[2 * i + 1]);
|
||||
if (byte_h < 0 || byte_l < 0) {
|
||||
return -1;
|
||||
}
|
||||
buffer[i] = (byte_h << 4) + byte_l;
|
||||
}
|
||||
return strlen(str) / 2;
|
||||
}
|
||||
|
||||
static bool convert_bytes_to_str(const uint8_t *bytes, size_t bytes_size, char *str, size_t str_size)
|
||||
{
|
||||
if (str_size < bytes_size * 2 + 1) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < bytes_size; ++i) {
|
||||
uint8_t byte_h = bytes[i] >> 4;
|
||||
uint8_t byte_l = bytes[i] & 0xF;
|
||||
str[2 * i] = byte_h > 9 ? (byte_h - 0xA + 'A') : (byte_h + '0');
|
||||
str[2 * i + 1] = byte_l > 9 ? (byte_l - 0xA + 'A') : (byte_l + '0');
|
||||
}
|
||||
str[2 * bytes_size] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->src == ESP_RMAKER_REQ_SRC_INIT) {
|
||||
return ESP_OK;
|
||||
}
|
||||
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TBR_ACTIVE_DATASET) == 0) {
|
||||
if (val.type != RMAKER_VAL_TYPE_STRING || !val.val.s) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
otOperationalDatasetTlvs active_dataset;
|
||||
otOperationalDatasetTlvs current_active_dataset;
|
||||
int len = parse_bytes(active_dataset.mTlvs, sizeof(active_dataset.mTlvs), val.val.s);
|
||||
if (len < 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
active_dataset.mLength = len;
|
||||
if (thread_br_get_active_dataset_tlvs(¤t_active_dataset) != ESP_OK ||
|
||||
current_active_dataset.mLength != active_dataset.mLength ||
|
||||
memcmp(current_active_dataset.mTlvs, active_dataset.mTlvs, len)) {
|
||||
ESP_RETURN_ON_ERROR(thread_br_set_thread_enabled(false), TAG, "Failed to disable Thread");
|
||||
ESP_RETURN_ON_ERROR(thread_br_set_active_dataset_tlvs(&active_dataset), TAG, "Failed to set active dataset");
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(thread_br_set_thread_enabled(true), TAG, "Failed to enable Thread");
|
||||
ESP_RETURN_ON_ERROR(esp_rmaker_param_update_and_report(param, val), TAG,
|
||||
"Failed to update and report active dataset");
|
||||
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TBR_PENDING_DATASET) == 0) {
|
||||
if (val.type != RMAKER_VAL_TYPE_STRING || !val.val.s) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
otOperationalDatasetTlvs pending_dataset;
|
||||
int len = parse_bytes(pending_dataset.mTlvs, sizeof(pending_dataset.mTlvs), val.val.s);
|
||||
if (len < 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
pending_dataset.mLength = len;
|
||||
ESP_RETURN_ON_ERROR(thread_br_set_pending_dataset_tlvs(&pending_dataset), TAG, "Failed to set pending dataset");
|
||||
ESP_RETURN_ON_ERROR(esp_rmaker_param_update_and_report(param, val), TAG,
|
||||
"Failed to update and report active dataset");
|
||||
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TBR_CMD) == 0) {
|
||||
if (val.type != RMAKER_VAL_TYPE_INTEGER) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (val.val.i == ESP_RMAKER_THREAD_BR_CMD_GEN_DATASET) {
|
||||
ESP_RETURN_ON_ERROR(thread_br_set_thread_enabled(false), TAG, "Failed to disable Thread");
|
||||
ESP_RETURN_ON_ERROR(thread_br_generate_and_commit_new_dataset(), TAG, "Failed to generate a new dataset");
|
||||
ESP_RETURN_ON_ERROR(thread_br_set_thread_enabled(true), TAG, "Failed to enable Thread");
|
||||
// We need to report the generated active dataset.
|
||||
otOperationalDatasetTlvs active_dataset;
|
||||
ESP_RETURN_ON_ERROR(thread_br_get_active_dataset_tlvs(&active_dataset), TAG, "Failed to get active dataset");
|
||||
char *dataset_str = (char *)MEM_CALLOC_EXTRAM(2 * active_dataset.mLength + 1, 1);
|
||||
if (!dataset_str) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
convert_bytes_to_str(active_dataset.mTlvs, active_dataset.mLength, dataset_str, 2 * active_dataset.mLength + 1);
|
||||
esp_rmaker_param_val_t report_val = esp_rmaker_str(dataset_str);
|
||||
esp_rmaker_param_t *report_param = esp_rmaker_device_get_param_by_type(device, ESP_RMAKER_PARAM_TBR_ACTIVE_DATASET);
|
||||
esp_err_t err = esp_rmaker_param_update_and_report(report_param, report_val);
|
||||
free(dataset_str);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to update and report active dataset");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_thread_br_enable(const esp_openthread_platform_config_t *platform_config,
|
||||
const esp_rcp_update_config_t *rcp_update_config)
|
||||
{
|
||||
if (thread_br_service) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
thread_br_service = esp_rmaker_thread_br_service_create(ESP_RMAKER_DEF_THREAD_BR_SERVICE, write_cb, NULL, NULL);
|
||||
if (!thread_br_service) {
|
||||
ESP_LOGE(TAG, "Failed to create thread br service");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), thread_br_service);
|
||||
if (err != ESP_OK) {
|
||||
esp_rmaker_device_delete(thread_br_service);
|
||||
thread_br_service = NULL;
|
||||
}
|
||||
thread_border_router_start(platform_config, rcp_update_config);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_thread_br_report_device_role(void)
|
||||
{
|
||||
if (!thread_br_service) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_rmaker_param_t *param = esp_rmaker_device_get_param_by_type(thread_br_service,
|
||||
ESP_RMAKER_PARAM_TBR_DEVICE_ROLE);
|
||||
if (!param) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
otDeviceRole role = thread_br_get_device_role();
|
||||
esp_rmaker_param_val_t val = esp_rmaker_int(role);
|
||||
ESP_RETURN_ON_ERROR(esp_rmaker_param_update_and_report(param, val), TAG,
|
||||
"Failed to update and report Thread device role");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_thread_br_report_border_agent_id(void)
|
||||
{
|
||||
if (!thread_br_service) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_rmaker_param_t *param = esp_rmaker_device_get_param_by_type(thread_br_service,
|
||||
ESP_RMAKER_PARAM_TBR_BORDER_AGENT_ID);
|
||||
if (!param) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
otBorderAgentId border_agent_id;
|
||||
ESP_RETURN_ON_ERROR(thread_br_get_border_agent_id(&border_agent_id), TAG, "Failed to get Border Agent ID");
|
||||
char border_agent_id_str[sizeof(border_agent_id.mId) * 2 + 1];
|
||||
convert_bytes_to_str(border_agent_id.mId, sizeof(border_agent_id.mId), border_agent_id_str,
|
||||
sizeof(border_agent_id_str));
|
||||
esp_rmaker_param_val_t report_val = esp_rmaker_str(border_agent_id_str);
|
||||
ESP_RETURN_ON_ERROR(esp_rmaker_param_update_and_report(param, report_val), TAG,
|
||||
"Failed to update and report border agent id");
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// Copyright 2024 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_check.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_openthread.h>
|
||||
#include <esp_openthread_lock.h>
|
||||
|
||||
#include <openthread/border_agent.h>
|
||||
#include <openthread/dataset.h>
|
||||
#include <openthread/dataset_ftd.h>
|
||||
#include <openthread/error.h>
|
||||
#include <openthread/logging.h>
|
||||
#include <openthread/thread.h>
|
||||
|
||||
#include "esp_rmaker_thread_br_priv.h"
|
||||
|
||||
static const char* TAG = "thread_br";
|
||||
|
||||
esp_err_t thread_br_set_thread_enabled(bool enabled)
|
||||
{
|
||||
otInstance *instance = esp_openthread_get_instance();
|
||||
if (!instance) {
|
||||
ESP_LOGE(TAG, "Thread not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
bool is_enabled = (otThreadGetDeviceRole(instance) != OT_DEVICE_ROLE_DISABLED);
|
||||
bool is_ip6_enabled = otIp6IsEnabled(instance);
|
||||
if (enabled && !is_ip6_enabled) {
|
||||
if (otIp6SetEnabled(instance, enabled) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to %s netif", enabled ? "enable" : "disable");
|
||||
esp_openthread_lock_release();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
if (enabled != is_enabled) {
|
||||
if (otThreadSetEnabled(instance, enabled) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to %s thread", enabled ? "enable" : "disable");
|
||||
esp_openthread_lock_release();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
if (!enabled && is_ip6_enabled) {
|
||||
if (otIp6SetEnabled(instance, enabled) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to %s netif", enabled ? "enable" : "disable");
|
||||
esp_openthread_lock_release();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
esp_openthread_lock_release();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t thread_br_set_active_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs)
|
||||
{
|
||||
if (!dataset_tlvs) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
otInstance *instance = esp_openthread_get_instance();
|
||||
if (!instance) {
|
||||
ESP_LOGE(TAG, "Thread not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_err_t err = ESP_OK;
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
if (otDatasetSetActiveTlvs(instance, dataset_tlvs) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to set active Thread DatasetTlvs");
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
esp_openthread_lock_release();
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t thread_br_get_active_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs)
|
||||
{
|
||||
if (!dataset_tlvs) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
otInstance *instance = esp_openthread_get_instance();
|
||||
if (!instance) {
|
||||
ESP_LOGE(TAG, "Thread not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_err_t err = ESP_OK;
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
if (otDatasetGetActiveTlvs(instance, dataset_tlvs) != OT_ERROR_NONE) {
|
||||
ESP_LOGI(TAG, "Active Dataset not set");
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
esp_openthread_lock_release();
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t thread_br_set_pending_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs)
|
||||
{
|
||||
if (!dataset_tlvs) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
otInstance *instance = esp_openthread_get_instance();
|
||||
if (!instance) {
|
||||
ESP_LOGE(TAG, "Thread not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_err_t err = ESP_OK;
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
if (otDatasetSetPendingTlvs(instance, dataset_tlvs) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to set pending Thread DatasetTlvs");
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
esp_openthread_lock_release();
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t thread_br_get_border_agent_id(otBorderAgentId *border_agent_id)
|
||||
{
|
||||
if (!border_agent_id) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
otInstance *instance = esp_openthread_get_instance();
|
||||
if (!instance) {
|
||||
ESP_LOGE(TAG, "Thread not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_err_t err = ESP_OK;
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
if (otBorderAgentGetId(instance, border_agent_id) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to get Border Agent Id");
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
esp_openthread_lock_release();
|
||||
return err;
|
||||
}
|
||||
|
||||
otDeviceRole thread_br_get_device_role()
|
||||
{
|
||||
if (!esp_openthread_get_instance()) {
|
||||
return OT_DEVICE_ROLE_DISABLED;
|
||||
}
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
otDeviceRole role = otThreadGetDeviceRole(esp_openthread_get_instance());
|
||||
esp_openthread_lock_release();
|
||||
return role;
|
||||
}
|
||||
|
||||
esp_err_t thread_br_generate_and_commit_new_dataset()
|
||||
{
|
||||
otInstance *instance = esp_openthread_get_instance();
|
||||
if (!instance) {
|
||||
ESP_LOGE(TAG, "Thread not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (thread_br_get_device_role() != OT_DEVICE_ROLE_DISABLED) {
|
||||
ESP_LOGE(TAG, "The device role should be disabled when calling generate_and_commit_new_dataset");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
otOperationalDataset dataset;
|
||||
if (otDatasetCreateNewNetwork(instance, &dataset) != OT_ERROR_NONE) {
|
||||
esp_openthread_lock_release();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (otDatasetSetActive(instance, &dataset) != OT_ERROR_NONE) {
|
||||
esp_openthread_lock_release();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_openthread_lock_release();
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
|
||||
#include <esp_check.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_event_base.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_netif.h>
|
||||
#include <esp_openthread.h>
|
||||
#include <esp_openthread_border_router.h>
|
||||
#include <esp_openthread_lock.h>
|
||||
#include <esp_openthread_netif_glue.h>
|
||||
#include <esp_openthread_types.h>
|
||||
#include <esp_openthread_cli.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_thread_br.h>
|
||||
#include <esp_vfs_dev.h>
|
||||
#include <esp_vfs_eventfd.h>
|
||||
#include <esp_wifi_types.h>
|
||||
#include <mdns.h>
|
||||
#include <openthread/logging.h>
|
||||
#include <openthread/thread.h>
|
||||
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||
#include <esp_rcp_update.h>
|
||||
#endif
|
||||
|
||||
#include "esp_rmaker_thread_br_priv.h"
|
||||
|
||||
static const char *TAG = "thread_br";
|
||||
|
||||
static esp_openthread_platform_config_t s_platform_config;
|
||||
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||
static bool s_rcp_update = false;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||
#define RCP_VERSION_MAX_SIZE 100
|
||||
static void update_rcp(void)
|
||||
{
|
||||
// Deinit uart to transfer UART to the serial loader
|
||||
esp_openthread_rcp_deinit();
|
||||
if (esp_rcp_update() == ESP_OK) {
|
||||
esp_rcp_mark_image_verified(true);
|
||||
} else {
|
||||
esp_rcp_mark_image_verified(false);
|
||||
}
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
static void try_update_ot_rcp(const esp_openthread_platform_config_t *config)
|
||||
{
|
||||
char internal_rcp_version[RCP_VERSION_MAX_SIZE];
|
||||
const char *running_rcp_version = otPlatRadioGetVersionString(esp_openthread_get_instance());
|
||||
|
||||
if (esp_rcp_load_version_in_storage(internal_rcp_version, sizeof(internal_rcp_version)) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Internal RCP Version: %s", internal_rcp_version);
|
||||
ESP_LOGI(TAG, "Running RCP Version: %s", running_rcp_version);
|
||||
if (strcmp(internal_rcp_version, running_rcp_version) == 0) {
|
||||
esp_rcp_mark_image_verified(true);
|
||||
} else {
|
||||
update_rcp();
|
||||
}
|
||||
} else {
|
||||
ESP_LOGI(TAG, "RCP firmware not found in storage, will reboot to try next image");
|
||||
esp_rcp_mark_image_verified(false);
|
||||
esp_restart();
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_AUTO_UPDATE_RCP
|
||||
|
||||
static void rcp_failure_handler(void)
|
||||
{
|
||||
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||
esp_rcp_mark_image_unusable();
|
||||
char internal_rcp_version[RCP_VERSION_MAX_SIZE];
|
||||
if (esp_rcp_load_version_in_storage(internal_rcp_version, sizeof(internal_rcp_version)) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Internal RCP Version: %s", internal_rcp_version);
|
||||
update_rcp();
|
||||
} else {
|
||||
ESP_LOGI(TAG, "RCP firmware not found in storage, will reboot to try next image");
|
||||
esp_rcp_mark_image_verified(false);
|
||||
esp_restart();
|
||||
}
|
||||
#endif // CONFIG_AUTO_UPDATE_RCP
|
||||
}
|
||||
|
||||
static void ot_task_worker(void *aContext)
|
||||
{
|
||||
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD();
|
||||
esp_netif_t *openthread_netif = esp_netif_new(&cfg);
|
||||
assert(openthread_netif != NULL);
|
||||
|
||||
esp_openthread_register_rcp_failure_handler(rcp_failure_handler);
|
||||
// Initialize the OpenThread stack
|
||||
ESP_ERROR_CHECK(esp_openthread_init(&s_platform_config));
|
||||
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||
try_update_ot_rcp(&s_platform_config);
|
||||
#endif
|
||||
// Initialize border routing features
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
ESP_ERROR_CHECK(esp_netif_attach(openthread_netif, esp_openthread_netif_glue_init(&s_platform_config)));
|
||||
#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC
|
||||
(void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL);
|
||||
#endif
|
||||
otInstance *instance = esp_openthread_get_instance();
|
||||
if (otDatasetIsCommissioned(instance)) {
|
||||
(void)otIp6SetEnabled(instance, true);
|
||||
(void)otThreadSetEnabled(instance, true);
|
||||
}
|
||||
esp_openthread_lock_release();
|
||||
|
||||
esp_openthread_launch_mainloop();
|
||||
// Clean up
|
||||
esp_netif_destroy(openthread_netif);
|
||||
esp_openthread_netif_glue_deinit();
|
||||
esp_vfs_eventfd_unregister();
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void thread_br_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id,
|
||||
void* event_data)
|
||||
{
|
||||
if (event_base == OPENTHREAD_EVENT && event_id == OPENTHREAD_EVENT_ROLE_CHANGED) {
|
||||
esp_openthread_role_changed_event_t *role_changed = (esp_openthread_role_changed_event_t *)event_data;
|
||||
if (!role_changed) {
|
||||
return;
|
||||
}
|
||||
esp_rmaker_thread_br_report_device_role();
|
||||
if (role_changed->current_role == OT_DEVICE_ROLE_ROUTER ||
|
||||
role_changed->current_role == OT_DEVICE_ROLE_LEADER) {
|
||||
esp_rmaker_thread_br_report_border_agent_id();
|
||||
}
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
// Create link local address for Wi-Fi Station interface
|
||||
esp_netif_create_ip6_linklocal(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"));
|
||||
static bool border_router_initialized = false;
|
||||
if (!border_router_initialized) {
|
||||
ESP_ERROR_CHECK(esp_openthread_border_router_init());
|
||||
border_router_initialized = true;
|
||||
}
|
||||
esp_openthread_lock_release();
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t thread_border_router_start(const esp_openthread_platform_config_t *platform_config,
|
||||
const esp_rcp_update_config_t *rcp_update_config)
|
||||
{
|
||||
static bool thread_br_started = false;
|
||||
if (thread_br_started) {
|
||||
return ESP_OK;
|
||||
}
|
||||
if (!platform_config) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
memcpy(&s_platform_config, platform_config, sizeof(esp_openthread_platform_config_t));
|
||||
esp_vfs_eventfd_config_t eventfd_config = {
|
||||
.max_fds = 4,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(esp_vfs_eventfd_register(&eventfd_config), TAG, "Failed to register eventfd");
|
||||
esp_openthread_set_backbone_netif(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"));
|
||||
ESP_ERROR_CHECK(mdns_init());
|
||||
#define MDNS_MAX_NAME_LEN 64
|
||||
char hostname[MDNS_MAX_NAME_LEN];
|
||||
if (mdns_hostname_get(hostname) != ESP_OK) {
|
||||
// if hostname is not set we will set it with the rainmaker node id.
|
||||
ESP_ERROR_CHECK(mdns_hostname_set(esp_rmaker_get_node_id()));
|
||||
}
|
||||
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||
if (rcp_update_config) {
|
||||
esp_rcp_update_init(rcp_update_config);
|
||||
s_rcp_update = true;
|
||||
}
|
||||
#endif
|
||||
#define THREAD_TASK_STACK_SIZE 8192
|
||||
if (xTaskCreate(ot_task_worker, "ot_br", THREAD_TASK_STACK_SIZE, NULL, 5, NULL) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to start openthread task for thread br");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_ROLE_CHANGED, thread_br_event_handler, NULL);
|
||||
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, thread_br_event_handler, NULL);
|
||||
thread_br_started = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2024 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_rcp_update.h"
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_openthread.h>
|
||||
#include <openthread/border_agent.h>
|
||||
|
||||
/********** THREAD BR PARAMS NAME ***********/
|
||||
#define ESP_RMAKER_DEF_BORDER_AGENT_ID "BorderAgentId"
|
||||
#define ESP_RMAKER_DEF_ACTIVE_DATASET "ActiveDataset"
|
||||
#define ESP_RMAKER_DEF_PENDING_DATASET "PendingDataset"
|
||||
#define ESP_RMAKER_DEF_DEVICE_ROLE "DeviceRole"
|
||||
#define ESP_RMAKER_DEF_THREAD_BR_CMD "ThreadCmd"
|
||||
|
||||
/********** THREAD BR SERVICE NAME ***********/
|
||||
#define ESP_RMAKER_DEF_THREAD_BR_SERVICE "TBRService"
|
||||
|
||||
/********** THREAD BR PARAM TYPES ***********/
|
||||
#define ESP_RMAKER_PARAM_TBR_BORDER_AGENT_ID "esp.param.tbr-border-agent-id"
|
||||
#define ESP_RMAKER_PARAM_TBR_ACTIVE_DATASET "esp.param.tbr-active-dataset"
|
||||
#define ESP_RMAKER_PARAM_TBR_PENDING_DATASET "esp.param.tbr-pending-dataset"
|
||||
#define ESP_RMAKER_PARAM_TBR_DEVICE_ROLE "esp.param.tbr-device-role"
|
||||
#define ESP_RMAKER_PARAM_TBR_CMD "esp.param.tbr-cmd"
|
||||
|
||||
/********** THREAD BR SERVICE TYPE ***********/
|
||||
#define ESP_RMAKER_SERVICE_THREAD_BR "esp.service.thread-br"
|
||||
|
||||
/********** THREAD BR CMD TYPE ***********/
|
||||
#define ESP_RMAKER_THREAD_BR_CMD_GEN_DATASET 1
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_thread_br_service_create(const char *serv_name, esp_rmaker_device_write_cb_t write_cb,
|
||||
esp_rmaker_device_read_cb_t read_cb, void *priv_data);
|
||||
|
||||
esp_err_t esp_rmaker_thread_br_report_device_role(void);
|
||||
|
||||
esp_err_t esp_rmaker_thread_br_report_border_agent_id(void);
|
||||
|
||||
esp_err_t thread_br_set_thread_enabled(bool enabled);
|
||||
|
||||
esp_err_t thread_br_set_active_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs);
|
||||
|
||||
esp_err_t thread_br_get_active_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs);
|
||||
|
||||
esp_err_t thread_br_set_pending_dataset_tlvs(otOperationalDatasetTlvs *dataset_tlvs);
|
||||
|
||||
esp_err_t thread_br_get_border_agent_id(otBorderAgentId *border_agent_id);
|
||||
|
||||
otDeviceRole thread_br_get_device_role(void);
|
||||
|
||||
esp_err_t thread_br_generate_and_commit_new_dataset(void);
|
||||
|
||||
esp_err_t thread_border_router_start(const esp_openthread_platform_config_t *platform_config,
|
||||
const esp_rcp_update_config_t *rcp_update_config);
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright 2024 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_err.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_standard_types.h>
|
||||
#include <esp_rmaker_thread_br_priv.h>
|
||||
|
||||
static esp_rmaker_param_t *esp_rmaker_thread_br_border_agent_id_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TBR_BORDER_AGENT_ID,
|
||||
esp_rmaker_str(""), PROP_FLAG_READ);
|
||||
return param;
|
||||
}
|
||||
|
||||
static esp_rmaker_param_t *esp_rmaker_thread_br_active_dataset_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TBR_ACTIVE_DATASET,
|
||||
esp_rmaker_str(""),
|
||||
PROP_FLAG_READ | PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||
return param;
|
||||
}
|
||||
|
||||
static esp_rmaker_param_t *esp_rmaker_thread_br_pending_dataset_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TBR_PENDING_DATASET,
|
||||
esp_rmaker_str(""),
|
||||
PROP_FLAG_READ | PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||
return param;
|
||||
}
|
||||
|
||||
static esp_rmaker_param_t *esp_rmaker_thread_br_thread_role_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TBR_DEVICE_ROLE,
|
||||
esp_rmaker_int(0), PROP_FLAG_READ);
|
||||
return param;
|
||||
}
|
||||
|
||||
static esp_rmaker_param_t *esp_rmaker_thread_br_cmd_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TBR_CMD,
|
||||
esp_rmaker_int(0), PROP_FLAG_WRITE);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *esp_rmaker_thread_br_service_create(const char *serv_name, esp_rmaker_device_write_cb_t write_cb,
|
||||
esp_rmaker_device_read_cb_t read_cb, void *priv_data)
|
||||
{
|
||||
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_THREAD_BR, priv_data);
|
||||
if (service) {
|
||||
esp_rmaker_device_add_cb(service, write_cb, read_cb);
|
||||
esp_rmaker_device_add_param(
|
||||
service, esp_rmaker_thread_br_border_agent_id_param_create(ESP_RMAKER_DEF_BORDER_AGENT_ID));
|
||||
esp_rmaker_device_add_param(
|
||||
service, esp_rmaker_thread_br_active_dataset_param_create(ESP_RMAKER_DEF_ACTIVE_DATASET));
|
||||
esp_rmaker_device_add_param(
|
||||
service, esp_rmaker_thread_br_pending_dataset_param_create(ESP_RMAKER_DEF_PENDING_DATASET));
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_thread_br_thread_role_param_create(ESP_RMAKER_DEF_DEVICE_ROLE));
|
||||
esp_rmaker_device_add_param(service, esp_rmaker_thread_br_cmd_param_create(ESP_RMAKER_DEF_THREAD_BR_CMD));
|
||||
}
|
||||
return service;
|
||||
}
|
||||
10
components/esp_schedule/CMakeLists.txt
Normal file
10
components/esp_schedule/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
set(component_srcs "src/esp_schedule.c"
|
||||
"src/esp_schedule_nvs.c")
|
||||
|
||||
idf_component_register(SRCS "${component_srcs}"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "src"
|
||||
PRIV_REQUIRES "rmaker_common"
|
||||
REQUIRES "nvs_flash")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function")
|
||||
201
components/esp_schedule/LICENSE
Normal file
201
components/esp_schedule/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.
|
||||
50
components/esp_schedule/README.md
Normal file
50
components/esp_schedule/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# ESP Scheduling
|
||||
|
||||
[](https://components.espressif.com/components/espressif/esp_schedule)
|
||||
|
||||
This component is used internally by ESP RainMaker to implement schedules.
|
||||
|
||||
> Note: By default, the time is w.r.t. UTC. If the timezone has been set, then the time is w.r.t. the specified timezone.
|
||||
|
||||
## Test code:
|
||||
|
||||
```
|
||||
#include <esp_schedule.h>
|
||||
|
||||
void app_schedule_trigger_cb(esp_schedule_handle_t handle, void *priv_data)
|
||||
{
|
||||
printf("priv_data: %.*s\n", (char *)priv_data);
|
||||
}
|
||||
|
||||
static char *priv_data_global = "from app";
|
||||
|
||||
void app_schedule_set()
|
||||
{
|
||||
esp_schedule_config_t schedule_config = {
|
||||
.name = "test",
|
||||
.trigger.type = ESP_SCHEDULE_TYPE_DAYS_OF_WEEK,
|
||||
.trigger.hours = 13,
|
||||
.trigger.minutes = 30,
|
||||
.trigger.day.repeat_days = ESP_SCHEDULE_DAY_MONDAY | ESP_SCHEDULE_DAY_THURSDAY,
|
||||
.trigger_cb = app_schedule_trigger_cb,
|
||||
.priv_data = priv_data_global,
|
||||
};
|
||||
esp_schedule_create(&schedule_config);
|
||||
}
|
||||
|
||||
void app_schedule_init()
|
||||
{
|
||||
uint8_t schedule_count;
|
||||
esp_schedule_handle_t *schedule_list = esp_schedule_init(true, NULL, &schedule_count);
|
||||
if (schedule_count > 0 && schedule_list != NULL) {
|
||||
ESP_LOGI(TAG, "Found %d schedule(s) in NVS.", schedule_count);
|
||||
for (size_t i = 0; i < schedule_count; i++) {
|
||||
esp_schedule_config_t schedule_config;
|
||||
esp_schedule_get(schedule_list[i], &schedule_config);
|
||||
schedule_config.trigger_cb = app_schedule_trigger_cb;
|
||||
schedule_config.priv_data = priv_data_global;
|
||||
esp_schedule_edit(schedule_list[i], &schedule_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
5
components/esp_schedule/component.mk
Normal file
5
components/esp_schedule/component.mk
Normal file
@@ -0,0 +1,5 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
|
||||
COMPONENT_SRCDIRS := src
|
||||
|
||||
CFLAGS += -Wno-unused-function
|
||||
9
components/esp_schedule/idf_component.yml
Normal file
9
components/esp_schedule/idf_component.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
## IDF Component Manager Manifest File
|
||||
version: "1.2.0"
|
||||
description: ESP Schedules, used in RainMaker
|
||||
url: https://github.com/espressif/esp-rainmaker/tree/master/components/esp_schedule
|
||||
repository: https://github.com/espressif/esp-rainmaker.git
|
||||
issues: https://github.com/espressif/esp-rainmaker/issues
|
||||
dependencies:
|
||||
espressif/rmaker_common:
|
||||
version: "~1.4.2"
|
||||
236
components/esp_schedule/include/esp_schedule.h
Normal file
236
components/esp_schedule/include/esp_schedule.h
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
/** Schedule Handle */
|
||||
typedef void *esp_schedule_handle_t;
|
||||
|
||||
/** Maximum length of the schedule name allowed. This value cannot be more than 16 as it is used for NVS key. */
|
||||
#define MAX_SCHEDULE_NAME_LEN 16
|
||||
|
||||
/** Callback for schedule trigger
|
||||
*
|
||||
* This callback is called when the schedule is triggered.
|
||||
*
|
||||
* @param[in] handle Schedule handle.
|
||||
* @param[in] priv_data Pointer to the private data passed while creating/editing the schedule.
|
||||
*/
|
||||
typedef void (*esp_schedule_trigger_cb_t)(esp_schedule_handle_t handle, void *priv_data);
|
||||
|
||||
/** Callback for schedule timestamp
|
||||
*
|
||||
* This callback is called when the next trigger timestamp of the schedule is changed. This might be useful to check if
|
||||
* one time schedules have already passed while the device was powered off.
|
||||
*
|
||||
* @param[in] handle Schedule handle.
|
||||
* @param[in] next_timestamp timestamp at which the schedule will trigger next.
|
||||
* @param[in] priv_data Pointer to the user data passed while creating/editing the schedule.
|
||||
*/
|
||||
typedef void (*esp_schedule_timestamp_cb_t)(esp_schedule_handle_t handle, uint32_t next_timestamp, void *priv_data);
|
||||
|
||||
/** Schedule type */
|
||||
typedef enum esp_schedule_type {
|
||||
ESP_SCHEDULE_TYPE_INVALID = 0,
|
||||
ESP_SCHEDULE_TYPE_DAYS_OF_WEEK,
|
||||
ESP_SCHEDULE_TYPE_DATE,
|
||||
ESP_SCHEDULE_TYPE_RELATIVE,
|
||||
} esp_schedule_type_t;
|
||||
|
||||
/** Schedule days. Used for ESP_SCHEDULE_TYPE_DAYS_OF_WEEK. */
|
||||
typedef enum esp_schedule_days {
|
||||
ESP_SCHEDULE_DAY_ONCE = 0,
|
||||
ESP_SCHEDULE_DAY_EVERYDAY = 0b1111111,
|
||||
ESP_SCHEDULE_DAY_MONDAY = 1 << 0,
|
||||
ESP_SCHEDULE_DAY_TUESDAY = 1 << 1,
|
||||
ESP_SCHEDULE_DAY_WEDNESDAY = 1 << 2,
|
||||
ESP_SCHEDULE_DAY_THURSDAY = 1 << 3,
|
||||
ESP_SCHEDULE_DAY_FRIDAY = 1 << 4,
|
||||
ESP_SCHEDULE_DAY_SATURDAY = 1 << 5,
|
||||
ESP_SCHEDULE_DAY_SUNDAY = 1 << 6,
|
||||
} esp_schedule_days_t;
|
||||
|
||||
/** Schedule months. Used for ESP_SCHEDULE_TYPE_DATE. */
|
||||
typedef enum esp_schedule_months {
|
||||
ESP_SCHEDULE_MONTH_ONCE = 0,
|
||||
ESP_SCHEDULE_MONTH_ALL = 0b1111111,
|
||||
ESP_SCHEDULE_MONTH_JANUARY = 1 << 0,
|
||||
ESP_SCHEDULE_MONTH_FEBRUARY = 1 << 1,
|
||||
ESP_SCHEDULE_MONTH_MARCH = 1 << 2,
|
||||
ESP_SCHEDULE_MONTH_APRIL = 1 << 3,
|
||||
ESP_SCHEDULE_MONTH_MAY = 1 << 4,
|
||||
ESP_SCHEDULE_MONTH_JUNE = 1 << 5,
|
||||
ESP_SCHEDULE_MONTH_JULY = 1 << 6,
|
||||
ESP_SCHEDULE_MONTH_AUGUST = 1 << 7,
|
||||
ESP_SCHEDULE_MONTH_SEPTEMBER = 1 << 8,
|
||||
ESP_SCHEDULE_MONTH_OCTOBER = 1 << 9,
|
||||
ESP_SCHEDULE_MONTH_NOVEMBER = 1 << 10,
|
||||
ESP_SCHEDULE_MONTH_DECEMBER = 1 << 11,
|
||||
} esp_schedule_months_t;
|
||||
|
||||
/** Trigger details of the schedule */
|
||||
typedef struct esp_schedule_trigger {
|
||||
/** Type of schedule */
|
||||
esp_schedule_type_t type;
|
||||
/** Hours in 24 hour format. Accepted values: 0-23 */
|
||||
uint8_t hours;
|
||||
/** Minutes in the given hour. Accepted values: 0-59. */
|
||||
uint8_t minutes;
|
||||
/** For type ESP_SCHEDULE_TYPE_DAYS_OF_WEEK */
|
||||
struct {
|
||||
/** 'OR' list of esp_schedule_days_t */
|
||||
uint8_t repeat_days;
|
||||
} day;
|
||||
/** For type ESP_SCHEDULE_TYPE_DATE */
|
||||
struct {
|
||||
/** Day of the month. Accepted values: 1-31. */
|
||||
uint8_t day;
|
||||
/* 'OR' list of esp_schedule_months_t */
|
||||
uint16_t repeat_months;
|
||||
/** Year */
|
||||
uint16_t year;
|
||||
/** If the schedule is to be repeated every year. */
|
||||
bool repeat_every_year;
|
||||
} date;
|
||||
/** For type ESP_SCHEDULE_TYPE_SECONDS */
|
||||
int relative_seconds;
|
||||
/** Used for passing the next schedule timestamp for
|
||||
* ESP_SCHEDULE_TYPE_RELATIVE */
|
||||
time_t next_scheduled_time_utc;
|
||||
} esp_schedule_trigger_t;
|
||||
|
||||
/** Schedule Validity
|
||||
* Start and end time within which the schedule will be applicable.
|
||||
*/
|
||||
typedef struct esp_schedule_validity {
|
||||
/* Start time as UTC timestamp */
|
||||
time_t start_time;
|
||||
/* End time as UTC timestamp */
|
||||
time_t end_time;
|
||||
} esp_schedule_validity_t;
|
||||
|
||||
/** Schedule config */
|
||||
typedef struct esp_schedule_config {
|
||||
/** Name of the schedule. This is like a primary key for the schedule. This is required. +1 for NULL termination. */
|
||||
char name[MAX_SCHEDULE_NAME_LEN + 1];
|
||||
/** Trigger details */
|
||||
esp_schedule_trigger_t trigger;
|
||||
/** Trigger callback */
|
||||
esp_schedule_trigger_cb_t trigger_cb;
|
||||
/** Timestamp callback */
|
||||
esp_schedule_timestamp_cb_t timestamp_cb;
|
||||
/** Private data associated with the schedule. This will be passed to callbacks. */
|
||||
void *priv_data;
|
||||
/** Validity of schedules. */
|
||||
esp_schedule_validity_t validity;
|
||||
} esp_schedule_config_t;
|
||||
|
||||
/** Initialize ESP Schedule
|
||||
*
|
||||
* This initializes ESP Schedule. This must be called first before calling any of the other APIs.
|
||||
* This API also gets all the schedules from NVS (if it has been enabled).
|
||||
*
|
||||
* Note: After calling this API, the pointers to the callbacks should be updated for all the schedules by calling
|
||||
* esp_schedule_get() followed by esp_schedule_edit() with the correct callbacks.
|
||||
*
|
||||
* @param[in] enable_nvs If NVS is to be enabled or not.
|
||||
* @param[in] nvs_partition (Optional) The NVS partition to be used. If NULL is passed, the default partition is used.
|
||||
* @param[out] schedule_count Number of active schedules found in NVS.
|
||||
*
|
||||
* @return Array of schedule handles if any schedules have been found.
|
||||
* @return NULL if no schedule is found in NVS (or if NVS is not enabled).
|
||||
*/
|
||||
esp_schedule_handle_t *esp_schedule_init(bool enable_nvs, char *nvs_partition, uint8_t *schedule_count);
|
||||
|
||||
/** Create Schedule
|
||||
*
|
||||
* This API can be used to create a new schedule. The schedule still needs to be enabled using
|
||||
* esp_schedule_enable().
|
||||
*
|
||||
* @param[in] schedule_config Configuration of the schedule to be created.
|
||||
*
|
||||
* @return Schedule handle if successfully created.
|
||||
* @return NULL in case of error.
|
||||
*/
|
||||
esp_schedule_handle_t esp_schedule_create(esp_schedule_config_t *schedule_config);
|
||||
|
||||
/** Remove Schedule
|
||||
*
|
||||
* This API can be used to remove an existing schedule.
|
||||
*
|
||||
* @param[in] handle Schedule handle for the schedule to be removed.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_schedule_delete(esp_schedule_handle_t handle);
|
||||
|
||||
/** Edit Schedule
|
||||
*
|
||||
* This API can be used to edit an existing schedule.
|
||||
* The schedule name should be same as when the schedule was created. The complete config must be provided
|
||||
* or the previously stored config might be over-written.
|
||||
*
|
||||
* Note: If a schedule is edited when it is on-going, the new changes will not be reflected.
|
||||
* You will need to disable the schedule, edit it, and then enable it again.
|
||||
*
|
||||
* @param[in] handle Schedule handle for the schedule to be edited.
|
||||
* @param[in] schedule_config Configuration of the schedule to be edited.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_schedule_edit(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config);
|
||||
|
||||
/** Enable Schedule
|
||||
*
|
||||
* This API can be used to enable an existing schedule.
|
||||
* It can be used to enable a schedule after it has been created using esp_schedule_create()
|
||||
* or if the schedule has been disabled using esp_schedule_disable().
|
||||
*
|
||||
* @param[in] handle Schedule handle for the schedule to be enabled.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_schedule_enable(esp_schedule_handle_t handle);
|
||||
|
||||
/** Disable Schedule
|
||||
*
|
||||
* This API can be used to disable an on-going schedule.
|
||||
* It does not remove the schedule, just stops it. The schedule can be enabled again using
|
||||
* esp_schedule_enable().
|
||||
*
|
||||
* @param[in] handle Schedule handle for the schedule to be disabled.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_schedule_disable(esp_schedule_handle_t handle);
|
||||
|
||||
/** Get Schedule
|
||||
*
|
||||
* This API can be used to get details of an existing schedule.
|
||||
* The schedule_config is populated with the schedule details.
|
||||
*
|
||||
* @param[in] handle Schedule handle.
|
||||
* @param[out] schedule_config Details of the schedule whose handle is passed.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t esp_schedule_get(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
600
components/esp_schedule/src/esp_schedule.c
Normal file
600
components/esp_schedule/src/esp_schedule.c
Normal file
@@ -0,0 +1,600 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_sntp.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include "esp_schedule_internal.h"
|
||||
|
||||
static const char *TAG = "esp_schedule";
|
||||
|
||||
#define SECONDS_TILL_2020 ((2020 - 1970) * 365 * 24 * 3600)
|
||||
#define SECONDS_IN_DAY (60 * 60 * 24)
|
||||
|
||||
static bool init_done = false;
|
||||
|
||||
static int esp_schedule_get_no_of_days(esp_schedule_trigger_t *trigger, struct tm *current_time, struct tm *schedule_time)
|
||||
{
|
||||
/* for day, monday = 0, sunday = 6. */
|
||||
int next_day = 0;
|
||||
/* struct tm has tm_wday with sunday as 0. Whereas we have monday as 0. Converting struct tm to our format */
|
||||
int today = ((current_time->tm_wday + 7 - 1) % 7);
|
||||
|
||||
esp_schedule_days_t today_bit = 1 << today;
|
||||
uint8_t repeat_days = trigger->day.repeat_days;
|
||||
int current_seconds = (current_time->tm_hour * 60 + current_time->tm_min) * 60 + current_time->tm_sec;
|
||||
int schedule_seconds = (schedule_time->tm_hour * 60 + schedule_time->tm_min) * 60;
|
||||
|
||||
/* Handling for one time schedule */
|
||||
if (repeat_days == ESP_SCHEDULE_DAY_ONCE) {
|
||||
if (schedule_seconds > current_seconds) {
|
||||
/* The schedule is today and is yet to go off */
|
||||
return 0;
|
||||
} else {
|
||||
/* The schedule is tomorrow */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handling for repeating schedules */
|
||||
/* Check if it is today */
|
||||
if ((repeat_days & today_bit)) {
|
||||
if (schedule_seconds > current_seconds) {
|
||||
/* The schedule is today and is yet to go off. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* Check if it is this week or next week */
|
||||
if ((repeat_days & (today_bit ^ 0xFF)) > today_bit) {
|
||||
/* Next schedule is yet to come in this week */
|
||||
next_day = ffs(repeat_days & (0xFF << (today + 1))) - 1;
|
||||
return (next_day - today);
|
||||
} else {
|
||||
/* First scheduled day of the next week */
|
||||
next_day = ffs(repeat_days) - 1;
|
||||
if (next_day == today) {
|
||||
/* Same day, next week */
|
||||
return 7;
|
||||
}
|
||||
return (7 - today + next_day);
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "No of days could not be found. This should not happen.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t esp_schedule_get_next_month(esp_schedule_trigger_t *trigger, struct tm *current_time, struct tm *schedule_time)
|
||||
{
|
||||
int current_seconds = (current_time->tm_hour * 60 + current_time->tm_min) * 60 + current_time->tm_sec;
|
||||
int schedule_seconds = (schedule_time->tm_hour * 60 + schedule_time->tm_min) * 60;
|
||||
/* +1 is because struct tm has months starting from 0, whereas we have them starting from 1 */
|
||||
uint8_t current_month = current_time->tm_mon + 1;
|
||||
/* -1 because month_bit starts from 0b1. So for January, it should be 1 << 0. And current_month starts from 1. */
|
||||
uint16_t current_month_bit = 1 << (current_month - 1);
|
||||
uint8_t next_schedule_month = 0;
|
||||
uint16_t repeat_months = trigger->date.repeat_months;
|
||||
|
||||
/* Check if month is not specified */
|
||||
if (repeat_months == ESP_SCHEDULE_MONTH_ONCE) {
|
||||
if (trigger->date.day == current_time->tm_mday) {
|
||||
/* The schedule day is same. Check if time has already passed */
|
||||
if (schedule_seconds > current_seconds) {
|
||||
/* The schedule is today and is yet to go off */
|
||||
return current_month;
|
||||
} else {
|
||||
/* Today's time has passed */
|
||||
return (current_month + 1);
|
||||
}
|
||||
} else if (trigger->date.day > current_time->tm_mday) {
|
||||
/* The day is yet to come in this month */
|
||||
return current_month;
|
||||
} else {
|
||||
/* The day has passed in the current month */
|
||||
return (current_month + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if schedule is not this year itself, it is in future. */
|
||||
if (trigger->date.year > (current_time->tm_year + 1900)) {
|
||||
/* Find first schedule month of next year */
|
||||
next_schedule_month = ffs(repeat_months);
|
||||
/* Year will be handled by the caller. So no need to add any additional months */
|
||||
return next_schedule_month;
|
||||
}
|
||||
|
||||
/* Check if schedule is this month and is yet to come */
|
||||
if (current_month_bit & repeat_months) {
|
||||
if (trigger->date.day == current_time->tm_mday) {
|
||||
/* The schedule day is same. Check if time has already passed */
|
||||
if (schedule_seconds > current_seconds) {
|
||||
/* The schedule is today and is yet to go off */
|
||||
return current_month;
|
||||
}
|
||||
}
|
||||
if (trigger->date.day > current_time->tm_mday) {
|
||||
/* The day is yet to come in this month */
|
||||
return current_month;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if schedule is this year */
|
||||
if ((repeat_months & (current_month_bit ^ 0xFFFF)) > current_month_bit) {
|
||||
/* Next schedule month is yet to come in this year */
|
||||
next_schedule_month = ffs(repeat_months & (0xFFFF << (current_month)));
|
||||
return next_schedule_month;
|
||||
}
|
||||
|
||||
/* Check if schedule is for this year and does not repeat */
|
||||
if (!trigger->date.repeat_every_year) {
|
||||
if (trigger->date.year <= (current_time->tm_year + 1900)) {
|
||||
ESP_LOGE(TAG, "Schedule does not repeat next year, but get_next_month has been called.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedule is not this year */
|
||||
/* Find first schedule month of next year */
|
||||
next_schedule_month = ffs(repeat_months);
|
||||
/* +12 because the schedule is next year */
|
||||
return (next_schedule_month + 12);
|
||||
}
|
||||
|
||||
static uint16_t esp_schedule_get_next_year(esp_schedule_trigger_t *trigger, struct tm *current_time, struct tm *schedule_time)
|
||||
{
|
||||
uint16_t current_year = current_time->tm_year + 1900;
|
||||
uint16_t schedule_year = trigger->date.year;
|
||||
if (schedule_year > current_year) {
|
||||
return schedule_year;
|
||||
}
|
||||
/* If the schedule is set to repeat_every_year, we return the current year */
|
||||
/* If the schedule has already passed in this year, we still return current year, as the additional months will be handled in get_next_month */
|
||||
return current_year;
|
||||
}
|
||||
|
||||
static uint32_t esp_schedule_get_next_schedule_time_diff(const char *schedule_name, esp_schedule_trigger_t *trigger)
|
||||
{
|
||||
struct tm current_time, schedule_time;
|
||||
time_t now;
|
||||
char time_str[64];
|
||||
int32_t time_diff;
|
||||
|
||||
/* Get current time */
|
||||
time(&now);
|
||||
/* Handling ESP_SCHEDULE_TYPE_RELATIVE first since it doesn't require any
|
||||
* computation based on days, hours, minutes, etc.
|
||||
*/
|
||||
if (trigger->type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||
/* If next scheduled time is already set, just compute the difference
|
||||
* between current time and next scheduled time and return that diff.
|
||||
*/
|
||||
time_t target;
|
||||
if (trigger->next_scheduled_time_utc > 0) {
|
||||
target = (time_t)trigger->next_scheduled_time_utc;
|
||||
time_diff = difftime(target, now);
|
||||
} else {
|
||||
target = now + (time_t)trigger->relative_seconds;
|
||||
time_diff = trigger->relative_seconds;
|
||||
}
|
||||
localtime_r(&target, &schedule_time);
|
||||
trigger->next_scheduled_time_utc = mktime(&schedule_time);
|
||||
/* Print schedule time */
|
||||
memset(time_str, 0, sizeof(time_str));
|
||||
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &schedule_time);
|
||||
ESP_LOGI(TAG, "Schedule %s will be active on: %s. DST: %s", schedule_name, time_str, schedule_time.tm_isdst ? "Yes" : "No");
|
||||
return time_diff;
|
||||
}
|
||||
localtime_r(&now, ¤t_time);
|
||||
|
||||
/* Get schedule time */
|
||||
localtime_r(&now, &schedule_time);
|
||||
schedule_time.tm_sec = 0;
|
||||
schedule_time.tm_min = trigger->minutes;
|
||||
schedule_time.tm_hour = trigger->hours;
|
||||
mktime(&schedule_time);
|
||||
|
||||
/* Adjust schedule day */
|
||||
if (trigger->type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||
int no_of_days = 0;
|
||||
no_of_days = esp_schedule_get_no_of_days(trigger, ¤t_time, &schedule_time);
|
||||
schedule_time.tm_sec += no_of_days * SECONDS_IN_DAY;
|
||||
}
|
||||
if (trigger->type == ESP_SCHEDULE_TYPE_DATE) {
|
||||
schedule_time.tm_mday = trigger->date.day;
|
||||
schedule_time.tm_mon = esp_schedule_get_next_month(trigger, ¤t_time, &schedule_time) - 1;
|
||||
schedule_time.tm_year = esp_schedule_get_next_year(trigger, ¤t_time, &schedule_time) - 1900;
|
||||
if (schedule_time.tm_mon < 0) {
|
||||
ESP_LOGE(TAG, "Invalid month found: %d. Setting it to next month.", schedule_time.tm_mon);
|
||||
schedule_time.tm_mon = current_time.tm_mon + 1;
|
||||
}
|
||||
if (schedule_time.tm_mon >= 12) {
|
||||
schedule_time.tm_year += schedule_time.tm_mon / 12;
|
||||
schedule_time.tm_mon = schedule_time.tm_mon % 12;
|
||||
}
|
||||
}
|
||||
mktime(&schedule_time);
|
||||
|
||||
/* Adjust time according to DST */
|
||||
time_t dst_adjust = 0;
|
||||
if (!current_time.tm_isdst && schedule_time.tm_isdst) {
|
||||
dst_adjust = -3600;
|
||||
} else if (current_time.tm_isdst && !schedule_time.tm_isdst ) {
|
||||
dst_adjust = 3600;
|
||||
}
|
||||
ESP_LOGD(TAG, "DST adjust seconds: %lld", (long long) dst_adjust);
|
||||
schedule_time.tm_sec += dst_adjust;
|
||||
mktime(&schedule_time);
|
||||
|
||||
/* Print schedule time */
|
||||
memset(time_str, 0, sizeof(time_str));
|
||||
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &schedule_time);
|
||||
ESP_LOGI(TAG, "Schedule %s will be active on: %s. DST: %s", schedule_name, time_str, schedule_time.tm_isdst ? "Yes" : "No");
|
||||
|
||||
/* Calculate difference */
|
||||
time_diff = difftime((mktime(&schedule_time)), mktime(¤t_time));
|
||||
|
||||
/* For one time schedules to check for expiry after a reboot. If NVS is enabled, this should be stored in NVS. */
|
||||
trigger->next_scheduled_time_utc = mktime(&schedule_time);
|
||||
|
||||
return time_diff;
|
||||
}
|
||||
|
||||
static bool esp_schedule_is_expired(esp_schedule_trigger_t *trigger)
|
||||
{
|
||||
time_t current_timestamp = 0;
|
||||
struct tm current_time = {0};
|
||||
time(¤t_timestamp);
|
||||
localtime_r(¤t_timestamp, ¤t_time);
|
||||
|
||||
if (trigger->type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||
if (trigger->next_scheduled_time_utc > 0 && trigger->next_scheduled_time_utc <= current_timestamp) {
|
||||
/* Relative seconds based schedule has expired */
|
||||
return true;
|
||||
} else if (trigger->next_scheduled_time_utc == 0) {
|
||||
/* Schedule has been disabled , so it is as good as expired. */
|
||||
return true;
|
||||
}
|
||||
} else if (trigger->type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||
if (trigger->day.repeat_days == ESP_SCHEDULE_DAY_ONCE) {
|
||||
if (trigger->next_scheduled_time_utc > 0 && trigger->next_scheduled_time_utc <= current_timestamp) {
|
||||
/* One time schedule has expired */
|
||||
return true;
|
||||
} else if (trigger->next_scheduled_time_utc == 0) {
|
||||
/* Schedule has been disabled , so it is as good as expired. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (trigger->type == ESP_SCHEDULE_TYPE_DATE) {
|
||||
if (trigger->date.repeat_months == 0) {
|
||||
if (trigger->next_scheduled_time_utc > 0 && trigger->next_scheduled_time_utc <= current_timestamp) {
|
||||
/* One time schedule has expired */
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (trigger->date.repeat_every_year == true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct tm schedule_time = {0};
|
||||
localtime_r(¤t_timestamp, &schedule_time);
|
||||
schedule_time.tm_sec = 0;
|
||||
schedule_time.tm_min = trigger->minutes;
|
||||
schedule_time.tm_hour = trigger->hours;
|
||||
schedule_time.tm_mday = trigger->date.day;
|
||||
/* For expiry, just check the last month of the repeat_months. */
|
||||
/* '-1' because struct tm has months starting from 0 and we have months starting from 1. */
|
||||
schedule_time.tm_mon = fls(trigger->date.repeat_months) - 1;
|
||||
/* '-1900' because struct tm has number of years after 1900 */
|
||||
schedule_time.tm_year = trigger->date.year - 1900;
|
||||
time_t schedule_timestamp = mktime(&schedule_time);
|
||||
|
||||
if (schedule_timestamp < current_timestamp) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
/* Invalid type. Mark as expired */
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void esp_schedule_stop_timer(esp_schedule_t *schedule)
|
||||
{
|
||||
xTimerStop(schedule->timer, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static void esp_schedule_start_timer(esp_schedule_t *schedule)
|
||||
{
|
||||
time_t current_time = 0;
|
||||
time(¤t_time);
|
||||
if (current_time < SECONDS_TILL_2020) {
|
||||
ESP_LOGE(TAG, "Time is not updated");
|
||||
return;
|
||||
}
|
||||
|
||||
schedule->next_scheduled_time_diff = esp_schedule_get_next_schedule_time_diff(schedule->name, &schedule->trigger);
|
||||
ESP_LOGI(TAG, "Starting a timer for %"PRIu32" seconds for schedule %s", schedule->next_scheduled_time_diff, schedule->name);
|
||||
|
||||
if (schedule->timestamp_cb) {
|
||||
schedule->timestamp_cb((esp_schedule_handle_t)schedule, schedule->trigger.next_scheduled_time_utc, schedule->priv_data);
|
||||
}
|
||||
|
||||
xTimerStop(schedule->timer, portMAX_DELAY);
|
||||
xTimerChangePeriod(schedule->timer, (schedule->next_scheduled_time_diff * 1000) / portTICK_PERIOD_MS, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static void esp_schedule_common_timer_cb(TimerHandle_t timer)
|
||||
{
|
||||
void *priv_data = pvTimerGetTimerID(timer);
|
||||
if (priv_data == NULL) {
|
||||
return;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)priv_data;
|
||||
time_t now;
|
||||
time(&now);
|
||||
struct tm validity_time;
|
||||
char time_str[64] = {0};
|
||||
if (schedule->validity.start_time != 0) {
|
||||
if (now < schedule->validity.start_time) {
|
||||
memset(time_str, 0, sizeof(time_str));
|
||||
localtime_r(&schedule->validity.start_time, &validity_time);
|
||||
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &validity_time);
|
||||
ESP_LOGW(TAG, "Schedule %s skipped. It will be active only after: %s. DST: %s.", schedule->name, time_str, validity_time.tm_isdst ? "Yes" : "No");
|
||||
/* TODO: Start the timer such that the next time it triggeres, it will be within the valid window.
|
||||
* Currently, it will just keep triggering and then get skipped if not in valid range.
|
||||
*/
|
||||
goto restart_schedule;
|
||||
}
|
||||
}
|
||||
if (schedule->validity.end_time != 0) {
|
||||
if (now > schedule->validity.end_time) {
|
||||
localtime_r(&schedule->validity.end_time, &validity_time);
|
||||
strftime(time_str, sizeof(time_str), "%c %z[%Z]", &validity_time);
|
||||
ESP_LOGW(TAG, "Schedule %s skipped. It can't be active after: %s. DST: %s.", schedule->name, time_str, validity_time.tm_isdst ? "Yes" : "No");
|
||||
/* Return from here will ensure that the timer does not start again for this schedule */
|
||||
return;
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "Schedule %s triggered", schedule->name);
|
||||
if (schedule->trigger_cb) {
|
||||
schedule->trigger_cb((esp_schedule_handle_t)schedule, schedule->priv_data);
|
||||
}
|
||||
|
||||
restart_schedule:
|
||||
|
||||
if (esp_schedule_is_expired(&schedule->trigger)) {
|
||||
/* Not deleting the schedule here. Just not starting it again. */
|
||||
return;
|
||||
}
|
||||
esp_schedule_start_timer(schedule);
|
||||
}
|
||||
|
||||
static void esp_schedule_delete_timer(esp_schedule_t *schedule)
|
||||
{
|
||||
xTimerDelete(schedule->timer, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static void esp_schedule_create_timer(esp_schedule_t *schedule)
|
||||
{
|
||||
if (esp_schedule_nvs_is_enabled()) {
|
||||
/* This is just used for calculating next_scheduled_time_utc for ESP_SCHEDULE_DAY_ONCE (in case of ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) or for ESP_SCHEDULE_MONTH_ONCE (in case of ESP_SCHEDULE_TYPE_DATE), and only used when NVS is enabled. And if NVS is enabled, time will already be synced and the time will be correctly calculated. */
|
||||
schedule->next_scheduled_time_diff = esp_schedule_get_next_schedule_time_diff(schedule->name, &schedule->trigger);
|
||||
}
|
||||
|
||||
/* Temporarily setting the timer for 1 (anything greater than 0) tick. This will get changed when xTimerChangePeriod() is called. */
|
||||
schedule->timer = xTimerCreate("schedule", 1, pdFALSE, (void *)schedule, esp_schedule_common_timer_cb);
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_get(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config)
|
||||
{
|
||||
if (schedule_config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (handle == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||
|
||||
strcpy(schedule_config->name, schedule->name);
|
||||
schedule_config->trigger.type = schedule->trigger.type;
|
||||
schedule_config->trigger.hours = schedule->trigger.hours;
|
||||
schedule_config->trigger.minutes = schedule->trigger.minutes;
|
||||
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||
schedule_config->trigger.day.repeat_days = schedule->trigger.day.repeat_days;
|
||||
} else if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DATE) {
|
||||
schedule_config->trigger.date.day = schedule->trigger.date.day;
|
||||
schedule_config->trigger.date.repeat_months = schedule->trigger.date.repeat_months;
|
||||
schedule_config->trigger.date.year = schedule->trigger.date.year;
|
||||
schedule_config->trigger.date.repeat_every_year = schedule->trigger.date.repeat_every_year;
|
||||
}
|
||||
|
||||
schedule_config->trigger_cb = schedule->trigger_cb;
|
||||
schedule_config->timestamp_cb = schedule->timestamp_cb;
|
||||
schedule_config->priv_data = schedule->priv_data;
|
||||
schedule_config->validity = schedule->validity;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_enable(esp_schedule_handle_t handle)
|
||||
{
|
||||
if (handle == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||
esp_schedule_start_timer(schedule);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_disable(esp_schedule_handle_t handle)
|
||||
{
|
||||
if (handle == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||
esp_schedule_stop_timer(schedule);
|
||||
/* Disabling a schedule should also reset the next_scheduled_time.
|
||||
* It would be re-computed after enabling.
|
||||
*/
|
||||
schedule->trigger.next_scheduled_time_utc = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_schedule_set(esp_schedule_t *schedule, esp_schedule_config_t *schedule_config)
|
||||
{
|
||||
/* Setting everything apart from name. */
|
||||
schedule->trigger.type = schedule_config->trigger.type;
|
||||
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||
schedule->trigger.relative_seconds = schedule_config->trigger.relative_seconds;
|
||||
schedule->trigger.next_scheduled_time_utc = schedule_config->trigger.next_scheduled_time_utc;
|
||||
} else {
|
||||
schedule->trigger.hours = schedule_config->trigger.hours;
|
||||
schedule->trigger.minutes = schedule_config->trigger.minutes;
|
||||
|
||||
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DAYS_OF_WEEK) {
|
||||
schedule->trigger.day.repeat_days = schedule_config->trigger.day.repeat_days;
|
||||
} else if (schedule->trigger.type == ESP_SCHEDULE_TYPE_DATE) {
|
||||
schedule->trigger.date.day = schedule_config->trigger.date.day;
|
||||
schedule->trigger.date.repeat_months = schedule_config->trigger.date.repeat_months;
|
||||
schedule->trigger.date.year = schedule_config->trigger.date.year;
|
||||
schedule->trigger.date.repeat_every_year = schedule_config->trigger.date.repeat_every_year;
|
||||
}
|
||||
}
|
||||
|
||||
schedule->trigger_cb = schedule_config->trigger_cb;
|
||||
schedule->timestamp_cb = schedule_config->timestamp_cb;
|
||||
schedule->priv_data = schedule_config->priv_data;
|
||||
schedule->validity = schedule_config->validity;
|
||||
esp_schedule_nvs_add(schedule);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_edit(esp_schedule_handle_t handle, esp_schedule_config_t *schedule_config)
|
||||
{
|
||||
if (handle == NULL || schedule_config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||
if (strncmp(schedule->name, schedule_config->name, sizeof(schedule->name)) != 0) {
|
||||
ESP_LOGE(TAG, "Schedule name mismatch. Expected: %s, Passed: %s", schedule->name, schedule_config->name);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Editing a schedule with relative time should also reset it. */
|
||||
if (schedule->trigger.type == ESP_SCHEDULE_TYPE_RELATIVE) {
|
||||
schedule->trigger.next_scheduled_time_utc = 0;
|
||||
}
|
||||
esp_schedule_set(schedule, schedule_config);
|
||||
ESP_LOGD(TAG, "Schedule %s edited", schedule->name);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_delete(esp_schedule_handle_t handle)
|
||||
{
|
||||
if (handle == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)handle;
|
||||
ESP_LOGI(TAG, "Deleting schedule %s", schedule->name);
|
||||
if (schedule->timer) {
|
||||
esp_schedule_stop_timer(schedule);
|
||||
esp_schedule_delete_timer(schedule);
|
||||
}
|
||||
esp_schedule_nvs_remove(schedule);
|
||||
free(schedule);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_schedule_handle_t esp_schedule_create(esp_schedule_config_t *schedule_config)
|
||||
{
|
||||
if (schedule_config == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (strlen(schedule_config->name) <= 0) {
|
||||
ESP_LOGE(TAG, "Set schedule failed. Please enter a unique valid name for the schedule.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (schedule_config->trigger.type == ESP_SCHEDULE_TYPE_INVALID) {
|
||||
ESP_LOGE(TAG, "Schedule type is invalid.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)MEM_CALLOC_EXTRAM(1, sizeof(esp_schedule_t));
|
||||
if (schedule == NULL) {
|
||||
ESP_LOGE(TAG, "Could not allocate handle");
|
||||
return NULL;
|
||||
}
|
||||
strlcpy(schedule->name, schedule_config->name, sizeof(schedule->name));
|
||||
|
||||
esp_schedule_set(schedule, schedule_config);
|
||||
|
||||
esp_schedule_create_timer(schedule);
|
||||
ESP_LOGD(TAG, "Schedule %s created", schedule->name);
|
||||
return (esp_schedule_handle_t)schedule;
|
||||
}
|
||||
|
||||
esp_schedule_handle_t *esp_schedule_init(bool enable_nvs, char *nvs_partition, uint8_t *schedule_count)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
if (!esp_sntp_enabled()) {
|
||||
ESP_LOGI(TAG, "Initializing SNTP");
|
||||
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
esp_sntp_setservername(0, "pool.ntp.org");
|
||||
esp_sntp_init();
|
||||
}
|
||||
#else
|
||||
if (!sntp_enabled()) {
|
||||
ESP_LOGI(TAG, "Initializing SNTP");
|
||||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
sntp_setservername(0, "pool.ntp.org");
|
||||
sntp_init();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!enable_nvs) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Wait for time to be updated here */
|
||||
|
||||
|
||||
/* Below this is initialising schedules from NVS */
|
||||
esp_schedule_nvs_init(nvs_partition);
|
||||
|
||||
/* Get handle list from NVS */
|
||||
esp_schedule_handle_t *handle_list = NULL;
|
||||
*schedule_count = 0;
|
||||
handle_list = esp_schedule_nvs_get_all(schedule_count);
|
||||
if (handle_list == NULL) {
|
||||
ESP_LOGI(TAG, "No schedules found in NVS");
|
||||
return NULL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Schedules found in NVS: %"PRIu8, *schedule_count);
|
||||
/* Start/Delete the schedules */
|
||||
esp_schedule_t *schedule = NULL;
|
||||
for (size_t handle_count = 0; handle_count < *schedule_count; handle_count++) {
|
||||
schedule = (esp_schedule_t *)handle_list[handle_count];
|
||||
schedule->trigger_cb = NULL;
|
||||
schedule->timer = NULL;
|
||||
/* Check for ONCE and expired schedules and delete them. */
|
||||
if (esp_schedule_is_expired(&schedule->trigger)) {
|
||||
/* This schedule has already expired. */
|
||||
ESP_LOGI(TAG, "Schedule %s does not repeat and has already expired. Deleting it.", schedule->name);
|
||||
esp_schedule_delete((esp_schedule_handle_t)schedule);
|
||||
/* Removing the schedule from the list */
|
||||
handle_list[handle_count] = handle_list[*schedule_count - 1];
|
||||
(*schedule_count)--;
|
||||
handle_count--;
|
||||
continue;
|
||||
}
|
||||
esp_schedule_create_timer(schedule);
|
||||
esp_schedule_start_timer(schedule);
|
||||
}
|
||||
init_done = true;
|
||||
return handle_list;
|
||||
}
|
||||
28
components/esp_schedule/src/esp_schedule_internal.h
Normal file
28
components/esp_schedule/src/esp_schedule_internal.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <esp_schedule.h>
|
||||
|
||||
typedef struct esp_schedule {
|
||||
char name[MAX_SCHEDULE_NAME_LEN + 1];
|
||||
esp_schedule_trigger_t trigger;
|
||||
uint32_t next_scheduled_time_diff;
|
||||
TimerHandle_t timer;
|
||||
esp_schedule_trigger_cb_t trigger_cb;
|
||||
esp_schedule_timestamp_cb_t timestamp_cb;
|
||||
void *priv_data;
|
||||
esp_schedule_validity_t validity;
|
||||
} esp_schedule_t;
|
||||
|
||||
esp_err_t esp_schedule_nvs_add(esp_schedule_t *schedule);
|
||||
esp_err_t esp_schedule_nvs_remove(esp_schedule_t *schedule);
|
||||
esp_schedule_handle_t *esp_schedule_nvs_get_all(uint8_t *schedule_count);
|
||||
bool esp_schedule_nvs_is_enabled(void);
|
||||
esp_err_t esp_schedule_nvs_init(char *nvs_partition);
|
||||
296
components/esp_schedule/src/esp_schedule_nvs.c
Normal file
296
components/esp_schedule/src/esp_schedule_nvs.c
Normal file
@@ -0,0 +1,296 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <esp_log.h>
|
||||
#include <nvs.h>
|
||||
#include "esp_schedule_internal.h"
|
||||
|
||||
static const char *TAG = "esp_schedule_nvs";
|
||||
|
||||
#define ESP_SCHEDULE_NVS_NAMESPACE "schd"
|
||||
#define ESP_SCHEDULE_COUNT_KEY "schd_count"
|
||||
|
||||
static char *esp_schedule_nvs_partition = NULL;
|
||||
static bool nvs_enabled = false;
|
||||
|
||||
esp_err_t esp_schedule_nvs_add(esp_schedule_t *schedule)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not adding to NVS.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check if this is new schedule or editing an existing schedule */
|
||||
size_t buf_size;
|
||||
bool editing_schedule = true;
|
||||
err = nvs_get_blob(nvs_handle, schedule->name, NULL, &buf_size);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
editing_schedule = false;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Updating the existing schedule %s", schedule->name);
|
||||
}
|
||||
|
||||
err = nvs_set_blob(nvs_handle, schedule->name, schedule, sizeof(esp_schedule_t));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS set failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
if (editing_schedule == false) {
|
||||
uint8_t schedule_count;
|
||||
err = nvs_get_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, &schedule_count);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
schedule_count = 0;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
schedule_count++;
|
||||
err = nvs_set_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, schedule_count);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS set failed for schedule count with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
nvs_commit(nvs_handle);
|
||||
nvs_close(nvs_handle);
|
||||
ESP_LOGI(TAG, "Schedule %s added in NVS", schedule->name);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_nvs_remove_all(void)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not removing from NVS.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||
return err;
|
||||
}
|
||||
err = nvs_erase_all(nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS erase all keys failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
nvs_commit(nvs_handle);
|
||||
nvs_close(nvs_handle);
|
||||
ESP_LOGI(TAG, "All schedules removed from NVS");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_nvs_remove(esp_schedule_t *schedule)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not removing from NVS.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||
return err;
|
||||
}
|
||||
err = nvs_erase_key(nvs_handle, schedule->name);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS erase key failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
uint8_t schedule_count;
|
||||
err = nvs_get_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, &schedule_count);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS get failed for schedule count with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
schedule_count--;
|
||||
err = nvs_set_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, schedule_count);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS set failed for schedule count with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return err;
|
||||
}
|
||||
nvs_commit(nvs_handle);
|
||||
nvs_close(nvs_handle);
|
||||
ESP_LOGI(TAG, "Schedule %s removed from NVS", schedule->name);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static uint8_t esp_schedule_nvs_get_count(void)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not getting count from NVS.");
|
||||
return 0;
|
||||
}
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||
return 0;
|
||||
}
|
||||
uint8_t schedule_count;
|
||||
err = nvs_get_u8(nvs_handle, ESP_SCHEDULE_COUNT_KEY, &schedule_count);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS get failed for schedule count with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return 0;
|
||||
}
|
||||
nvs_close(nvs_handle);
|
||||
ESP_LOGI(TAG, "Schedules in NVS: %d", schedule_count);
|
||||
return schedule_count;
|
||||
}
|
||||
|
||||
static esp_schedule_handle_t esp_schedule_nvs_get(char *nvs_key)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not getting from NVS.");
|
||||
return NULL;
|
||||
}
|
||||
size_t buf_size;
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t err = nvs_open_from_partition(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open failed with error %d", err);
|
||||
return NULL;
|
||||
}
|
||||
err = nvs_get_blob(nvs_handle, nvs_key, NULL, &buf_size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
return NULL;
|
||||
}
|
||||
esp_schedule_t *schedule = (esp_schedule_t *)malloc(buf_size);
|
||||
if (schedule == NULL) {
|
||||
ESP_LOGE(TAG, "Could not allocate handle");
|
||||
nvs_close(nvs_handle);
|
||||
return NULL;
|
||||
}
|
||||
err = nvs_get_blob(nvs_handle, nvs_key, schedule, &buf_size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS get failed with error %d", err);
|
||||
nvs_close(nvs_handle);
|
||||
free(schedule);
|
||||
return NULL;
|
||||
}
|
||||
nvs_close(nvs_handle);
|
||||
ESP_LOGI(TAG, "Schedule %s found in NVS", schedule->name);
|
||||
return (esp_schedule_handle_t) schedule;
|
||||
}
|
||||
|
||||
esp_schedule_handle_t *esp_schedule_nvs_get_all(uint8_t *schedule_count)
|
||||
{
|
||||
if (!nvs_enabled) {
|
||||
ESP_LOGD(TAG, "NVS not enabled. Not Initialising NVS.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*schedule_count = esp_schedule_nvs_get_count();
|
||||
if (*schedule_count == 0) {
|
||||
ESP_LOGI(TAG, "No Entries found in NVS");
|
||||
return NULL;
|
||||
}
|
||||
esp_schedule_handle_t *handle_list = (esp_schedule_handle_t *)malloc(sizeof(esp_schedule_handle_t) * (*schedule_count));
|
||||
if (handle_list == NULL) {
|
||||
ESP_LOGE(TAG, "Could not allocate schedule list");
|
||||
*schedule_count = 0;
|
||||
return NULL;
|
||||
}
|
||||
int handle_count = 0;
|
||||
|
||||
nvs_entry_info_t nvs_entry;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
nvs_iterator_t nvs_iterator = NULL;
|
||||
esp_err_t err = nvs_entry_find(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_TYPE_BLOB, &nvs_iterator);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "No entry found in NVS");
|
||||
return NULL;;
|
||||
}
|
||||
while (err == ESP_OK) {
|
||||
nvs_entry_info(nvs_iterator, &nvs_entry);
|
||||
ESP_LOGI(TAG, "Found schedule in NVS with key: %s", nvs_entry.key);
|
||||
handle_list[handle_count] = esp_schedule_nvs_get(nvs_entry.key);
|
||||
if (handle_list[handle_count] != NULL) {
|
||||
/* Increase count only if nvs_get was successful */
|
||||
handle_count++;
|
||||
}
|
||||
err = nvs_entry_next(&nvs_iterator);
|
||||
}
|
||||
nvs_release_iterator(nvs_iterator);
|
||||
#else
|
||||
nvs_iterator_t nvs_iterator = nvs_entry_find(esp_schedule_nvs_partition, ESP_SCHEDULE_NVS_NAMESPACE, NVS_TYPE_BLOB);
|
||||
if (nvs_iterator == NULL) {
|
||||
ESP_LOGE(TAG, "No entry found in NVS");
|
||||
return NULL;;
|
||||
}
|
||||
while (nvs_iterator != NULL) {
|
||||
nvs_entry_info(nvs_iterator, &nvs_entry);
|
||||
ESP_LOGI(TAG, "Found schedule in NVS with key: %s", nvs_entry.key);
|
||||
handle_list[handle_count] = esp_schedule_nvs_get(nvs_entry.key);
|
||||
if (handle_list[handle_count] != NULL) {
|
||||
/* Increase count only if nvs_get was successful */
|
||||
handle_count++;
|
||||
}
|
||||
nvs_iterator = nvs_entry_next(nvs_iterator);
|
||||
}
|
||||
#endif
|
||||
*schedule_count = handle_count;
|
||||
ESP_LOGI(TAG, "Found %d schedules in NVS", *schedule_count);
|
||||
return handle_list;
|
||||
}
|
||||
|
||||
bool esp_schedule_nvs_is_enabled(void)
|
||||
{
|
||||
return nvs_enabled;
|
||||
}
|
||||
|
||||
esp_err_t esp_schedule_nvs_init(char *nvs_partition)
|
||||
{
|
||||
if (nvs_enabled) {
|
||||
ESP_LOGI(TAG, "NVS already enabled");
|
||||
return ESP_OK;
|
||||
}
|
||||
if (nvs_partition) {
|
||||
esp_schedule_nvs_partition = strndup(nvs_partition, strlen(nvs_partition));
|
||||
} else {
|
||||
esp_schedule_nvs_partition = strndup("nvs", strlen("nvs"));
|
||||
}
|
||||
if (esp_schedule_nvs_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Could not allocate nvs_partition");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
nvs_enabled = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
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;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user