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;
|
||||
}
|
||||
Reference in New Issue
Block a user