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:
3
examples/common/app_insights/CMakeLists.txt
Normal file
3
examples/common/app_insights/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "app_insights.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES esp_insights esp_diagnostics esp_rainmaker)
|
||||
11
examples/common/app_insights/Kconfig
Normal file
11
examples/common/app_insights/Kconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
menu "App Insights"
|
||||
visible if ESP_INSIGHTS_ENABLED
|
||||
|
||||
config APP_INSIGHTS_ENABLE_LOG_TYPE_ALL
|
||||
bool "Enable all diagnostics log type"
|
||||
default n
|
||||
help
|
||||
By default only error logs are enabled.
|
||||
This config option enables the capture of all log types (errors/warnings/events).
|
||||
|
||||
endmenu
|
||||
122
examples/common/app_insights/app_insights.c
Normal file
122
examples/common/app_insights/app_insights.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
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_err.h>
|
||||
#include <esp_log.h>
|
||||
#ifdef CONFIG_ESP_INSIGHTS_ENABLED
|
||||
#include <esp_rmaker_mqtt.h>
|
||||
#include <esp_insights.h>
|
||||
#include <string.h>
|
||||
#include <esp_rmaker_core.h>
|
||||
#include <esp_rmaker_common_events.h>
|
||||
|
||||
#if CONFIG_APP_INSIGHTS_ENABLE_LOG_TYPE_ALL
|
||||
#define APP_INSIGHTS_LOG_TYPE ESP_DIAG_LOG_TYPE_ERROR \
|
||||
| ESP_DIAG_LOG_TYPE_WARNING \
|
||||
| ESP_DIAG_LOG_TYPE_EVENT
|
||||
#else
|
||||
#define APP_INSIGHTS_LOG_TYPE ESP_DIAG_LOG_TYPE_ERROR
|
||||
#endif /* CONFIG_APP_INSIGHTS_ENABLE_LOG_TYPE_ALL */
|
||||
|
||||
#define INSIGHTS_TOPIC_SUFFIX "diagnostics/from-node"
|
||||
#define INSIGHTS_TOPIC_RULE "insights_message_delivery"
|
||||
|
||||
static int app_insights_data_send(void *data, size_t len)
|
||||
{
|
||||
char topic[128];
|
||||
int msg_id = -1;
|
||||
if (data == NULL) {
|
||||
return 0;
|
||||
}
|
||||
char *node_id = esp_rmaker_get_node_id();
|
||||
if (!node_id) {
|
||||
return -1;
|
||||
}
|
||||
if (esp_rmaker_mqtt_is_budget_available() == false) {
|
||||
/* the API `esp_rmaker_mqtt_publish` already checks if the budget is available.
|
||||
This also raises an error message, which we do not want for esp-insights.
|
||||
silently return with error */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_rmaker_create_mqtt_topic(topic, sizeof(topic), INSIGHTS_TOPIC_SUFFIX, INSIGHTS_TOPIC_RULE);
|
||||
esp_rmaker_mqtt_publish(topic, data, len, RMAKER_MQTT_QOS1, &msg_id);
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
static void rmaker_common_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (event_base != RMAKER_COMMON_EVENT) {
|
||||
return;
|
||||
}
|
||||
esp_insights_transport_event_data_t data;
|
||||
switch(event_id) {
|
||||
case RMAKER_MQTT_EVENT_PUBLISHED:
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.msg_id = *(int *)event_data;
|
||||
esp_event_post(INSIGHTS_EVENT, INSIGHTS_EVENT_TRANSPORT_SEND_SUCCESS, &data, sizeof(data), portMAX_DELAY);
|
||||
break;
|
||||
#ifdef CONFIG_MQTT_REPORT_DELETED_MESSAGES
|
||||
case RMAKER_MQTT_EVENT_MSG_DELETED:
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.msg_id = *(int *)event_data;
|
||||
esp_event_post(INSIGHTS_EVENT, INSIGHTS_EVENT_TRANSPORT_SEND_FAILED, &data, sizeof(data), portMAX_DELAY);
|
||||
break;
|
||||
#endif /* CONFIG_MQTT_REPORT_DELETED_MESSAGES */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_ESP_INSIGHTS_ENABLED */
|
||||
|
||||
#define TAG "app_insights"
|
||||
|
||||
esp_err_t app_insights_enable(void)
|
||||
{
|
||||
#ifdef CONFIG_ESP_INSIGHTS_ENABLED
|
||||
#ifndef CONFIG_ESP_INSIGHTS_TRANSPORT_MQTT
|
||||
ESP_LOGE(TAG, "Please select the CONFIG_ESP_INSIGHTS_TRANSPORT_MQTT option from menuconfig");
|
||||
#endif
|
||||
/* Initialize the event loop, if not done already. */
|
||||
esp_err_t err = esp_event_loop_create_default();
|
||||
/* If the default event loop is already initialized, we get ESP_ERR_INVALID_STATE */
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGW(TAG, "Event loop creation failed with ESP_ERR_INVALID_STATE. Proceeding since it must have been created elsewhere.");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to create default event loop, err = %x", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_SELF_CLAIM
|
||||
ESP_LOGW(TAG, "Nodes with Self Claiming may not be accessible for Insights.");
|
||||
#endif
|
||||
char *node_id = esp_rmaker_get_node_id();
|
||||
|
||||
esp_insights_transport_config_t transport = {
|
||||
.callbacks.data_send = app_insights_data_send,
|
||||
};
|
||||
esp_insights_transport_register(&transport);
|
||||
|
||||
esp_event_handler_register(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, rmaker_common_event_handler, NULL);
|
||||
|
||||
esp_insights_config_t config = {
|
||||
.log_type = APP_INSIGHTS_LOG_TYPE,
|
||||
.node_id = node_id,
|
||||
.alloc_ext_ram = true,
|
||||
};
|
||||
|
||||
esp_insights_enable(&config);
|
||||
|
||||
if (esp_insights_cmd_resp_enable()!= ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to enabled insights command response");
|
||||
}
|
||||
#else
|
||||
ESP_LOGI(TAG, "Enable CONFIG_ESP_INSIGHTS_ENABLED to get Insights.");
|
||||
#endif /* ! CONFIG_ESP_INSIGHTS_ENABLED */
|
||||
return ESP_OK;
|
||||
}
|
||||
25
examples/common/app_insights/app_insights.h
Normal file
25
examples/common/app_insights/app_insights.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Enable ESP Insights in the application
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t app_insights_enable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
2
examples/common/app_insights/component.mk
Normal file
2
examples/common/app_insights/component.mk
Normal file
@@ -0,0 +1,2 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
COMPONENT_SRCDIRS := .
|
||||
7
examples/common/app_insights/idf_component.yml
Normal file
7
examples/common/app_insights/idf_component.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_rainmaker:
|
||||
version: ">=1.0"
|
||||
override_path: '../../../components/esp_rainmaker/'
|
||||
espressif/esp_insights:
|
||||
version: "~1.2.2"
|
||||
14
examples/common/app_network/CMakeLists.txt
Normal file
14
examples/common/app_network/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
set(priv_req qrcode nvs_flash esp_event rmaker_common vfs wifi_provisioning)
|
||||
|
||||
if ("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.1")
|
||||
list(APPEND priv_req network_provisioning openthread)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "app_wifi_internal.c" "app_network.c" "app_thread_internal.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_INCLUDE_DIRS "private_include"
|
||||
PRIV_REQUIRES ${priv_req})
|
||||
|
||||
if(CONFIG_APP_WIFI_SHOW_DEMO_INTRO_TEXT)
|
||||
target_compile_definitions(${COMPONENT_TARGET} PRIVATE "-D RMAKER_DEMO_PROJECT_NAME=\"${CMAKE_PROJECT_NAME}\"")
|
||||
endif()
|
||||
85
examples/common/app_network/Kconfig.projbuild
Normal file
85
examples/common/app_network/Kconfig.projbuild
Normal file
@@ -0,0 +1,85 @@
|
||||
menu "ESP RainMaker App Wi-Fi Provisioning"
|
||||
|
||||
config APP_NETWORK_PROV_SHOW_QR
|
||||
bool "Show provisioning QR code"
|
||||
default y
|
||||
help
|
||||
Show the QR code for provisioning.
|
||||
|
||||
config APP_NETWORK_PROV_MAX_POP_MISMATCH
|
||||
int
|
||||
default 5
|
||||
range 0 20
|
||||
prompt "Max wrong pop attempts allowed"
|
||||
help
|
||||
Set the maximum wrong pop attempts allowed before stopping provisioning.
|
||||
Set 0 for the feature to be disabled.
|
||||
This safeguards the device from brute-force attempt by limiting the wrong pop allowed.
|
||||
Needs IDF version >= 5.1.3
|
||||
|
||||
choice APP_NETWORK_PROV_TRANSPORT
|
||||
bool "Provisioning Transport method"
|
||||
default APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
help
|
||||
Wi-Fi/Network provisioning component offers both, SoftAP and BLE transports. Choose any one.
|
||||
|
||||
config APP_NETWORK_PROV_TRANSPORT_SOFTAP
|
||||
bool "Soft AP"
|
||||
depends on !IDF_TARGET_ESP32H2
|
||||
config APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
bool "BLE"
|
||||
select BT_ENABLED
|
||||
depends on !IDF_TARGET_ESP32S2
|
||||
endchoice
|
||||
|
||||
config APP_NETWORK_PROV_TRANSPORT
|
||||
int
|
||||
default 1 if APP_NETWORK_PROV_TRANSPORT_SOFTAP
|
||||
default 2 if APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
|
||||
config APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
bool
|
||||
default y
|
||||
prompt "Reset provisioned credentials and state machine after session failure"
|
||||
help
|
||||
Enable reseting provisioned credentials and state machine after session failure.
|
||||
This will restart the provisioning service after retries are exhausted.
|
||||
|
||||
config APP_NETWORK_PROV_MAX_RETRY_CNT
|
||||
int
|
||||
default 5
|
||||
prompt "Max retries before reseting provisioning state machine"
|
||||
depends on APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
help
|
||||
Set the Maximum retry to avoid reconnecting to an inexistent network or if credentials
|
||||
are misconfigured. Provisioned credentials are erased and internal state machine
|
||||
is reset after this threshold is reached.
|
||||
|
||||
config APP_NETWORK_SHOW_DEMO_INTRO_TEXT
|
||||
bool "Show intro text for demos"
|
||||
default n
|
||||
help
|
||||
Show some intro text for demos in order to help users understand more about ESP RainMaker.
|
||||
|
||||
config APP_NETWORK_PROV_TIMEOUT_PERIOD
|
||||
int "Provisioning Timeout"
|
||||
default 30
|
||||
help
|
||||
Timeout (in minutes) after which the provisioning will auto stop. A reboot will be required
|
||||
to restart provisioning. It is always recommended to set this to some non zero value, especially
|
||||
if you are not using PoP. Set to 0 if you do not want provisioning to auto stop.
|
||||
|
||||
config APP_NETWORK_PROV_NAME_PREFIX
|
||||
string "Provisioning Name Prefix"
|
||||
default "PROV"
|
||||
help
|
||||
Provisioning Name Prefix.
|
||||
|
||||
config APP_WIFI_PROV_COMPAT
|
||||
bool "Stay compatible with App Wi-Fi component"
|
||||
depends on ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
default y
|
||||
help
|
||||
Stay compatible with Previous App Wi-Fi component
|
||||
|
||||
endmenu
|
||||
495
examples/common/app_network/app_network.c
Normal file
495
examples/common/app_network/app_network.c
Normal file
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
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 <sdkconfig.h>
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <app_network.h>
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
#include <app_wifi_internal.h>
|
||||
#include <esp_netif_types.h>
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
#include <esp_openthread_types.h>
|
||||
#include <app_thread_internal.h>
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
#include <esp_mac.h>
|
||||
#else
|
||||
#include <esp_wifi.h>
|
||||
#endif
|
||||
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
#include <network_provisioning/manager.h>
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
#include <network_provisioning/scheme_ble.h>
|
||||
#else /* CONFIG_APP_NETOWRK_PROV_TRANSPORT_SOFTAP */
|
||||
#include <network_provisioning/scheme_softap.h>
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
#else
|
||||
#include <wifi_provisioning/manager.h>
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
#include <wifi_provisioning/scheme_ble.h>
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
#include <wifi_provisioning/scheme_softap.h>
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_SHOW_QR
|
||||
#include <qrcode.h>
|
||||
#endif
|
||||
|
||||
#include <nvs.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <esp_timer.h>
|
||||
#include <app_network.h>
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(APP_NETWORK_EVENT);
|
||||
static const char *TAG = "app_network";
|
||||
static const int NETWORK_CONNECTED_EVENT = BIT0;
|
||||
static EventGroupHandle_t network_event_group;
|
||||
|
||||
#define PROV_QR_VERSION "v1"
|
||||
|
||||
#define PROV_TRANSPORT_SOFTAP "softap"
|
||||
#define PROV_TRANSPORT_BLE "ble"
|
||||
#define QRCODE_BASE_URL "https://rainmaker.espressif.com/qrcode.html"
|
||||
|
||||
#define CREDENTIALS_NAMESPACE "rmaker_creds"
|
||||
#define RANDOM_NVS_KEY "random"
|
||||
|
||||
#define POP_STR_SIZE 9
|
||||
static esp_timer_handle_t prov_stop_timer;
|
||||
/* Timeout period in minutes */
|
||||
#define APP_NETWORK_PROV_TIMEOUT_PERIOD CONFIG_APP_NETWORK_PROV_TIMEOUT_PERIOD
|
||||
/* Autofetch period in micro-seconds */
|
||||
static uint64_t prov_timeout_period = (APP_NETWORK_PROV_TIMEOUT_PERIOD * 60 * 1000000LL);
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 3)
|
||||
#define APP_PROV_STOP_ON_CREDS_MISMATCH
|
||||
#elif (CONFIG_APP_NETWOKR_PROV_MAX_RETRY_CNT > 0)
|
||||
#warning "Provisioning window stop on max credentials failures, needs IDF version >= 5.1.3"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_APP_NETWORK_SHOW_DEMO_INTRO_TEXT
|
||||
|
||||
#define ESP_RAINMAKER_GITHUB_EXAMPLES_PATH "https://github.com/espressif/esp-rainmaker/blob/master/examples"
|
||||
#define ESP_RAINMAKER_INTRO_LINK "https://rainmaker.espressif.com"
|
||||
#define ESP_RMAKER_PHONE_APP_LINK "http://bit.ly/esp-rmaker"
|
||||
char esp_rainmaker_ascii_art[] = \
|
||||
" ______ _____ _____ _____ _____ _ _ __ __ _ ________ _____\n"\
|
||||
" | ____|/ ____| __ \\ | __ \\ /\\ |_ _| \\ | | \\/ | /\\ | |/ / ____| __ \\\n"\
|
||||
" | |__ | (___ | |__) | | |__) | / \\ | | | \\| | \\ / | / \\ | ' /| |__ | |__) |\n"\
|
||||
" | __| \\___ \\| ___/ | _ / / /\\ \\ | | | . ` | |\\/| | / /\\ \\ | < | __| | _ /\n"\
|
||||
" | |____ ____) | | | | \\ \\ / ____ \\ _| |_| |\\ | | | |/ ____ \\| . \\| |____| | \\ \\\n"\
|
||||
" |______|_____/|_| |_| \\_\\/_/ \\_\\_____|_| \\_|_| |_/_/ \\_\\_|\\_\\______|_| \\_\\\n";
|
||||
|
||||
static void intro_print(bool provisioned)
|
||||
{
|
||||
printf("####################################################################################################\n");
|
||||
printf("%s\n", esp_rainmaker_ascii_art);
|
||||
printf("Welcome to ESP RainMaker %s demo application!\n", RMAKER_DEMO_PROJECT_NAME);
|
||||
if (!provisioned) {
|
||||
printf("Follow these steps to get started:\n");
|
||||
printf("1. Download the ESP RainMaker phone app by visiting this link from your phone's browser:\n\n");
|
||||
printf(" %s\n\n", ESP_RMAKER_PHONE_APP_LINK);
|
||||
printf("2. Sign up and follow the steps on screen to add the device to your Wi-Fi/Thread network.\n");
|
||||
printf("3. You are now ready to use the device and control it locally as well as remotely.\n");
|
||||
printf(" You can also use the Boot button on the board to control your device.\n");
|
||||
}
|
||||
printf("\nIf you want to reset network credentials, or reset to factory, press and hold the Boot button.\n");
|
||||
printf("\nThis application uses ESP RainMaker, which is based on ESP IDF.\n");
|
||||
printf("Check out the source code for this application here:\n %s/%s\n",
|
||||
ESP_RAINMAKER_GITHUB_EXAMPLES_PATH, RMAKER_DEMO_PROJECT_NAME);
|
||||
printf("\nPlease visit %s for additional information.\n\n", ESP_RAINMAKER_INTRO_LINK);
|
||||
printf("####################################################################################################\n");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void intro_print(bool provisioned)
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
#endif /* !APP_NETWORK_SHOW_DEMO_INTRO_TEXT */
|
||||
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_SHOW_QR
|
||||
static esp_err_t qrcode_display(const char *text)
|
||||
{
|
||||
#define MAX_QRCODE_VERSION 5
|
||||
esp_qrcode_config_t cfg = ESP_QRCODE_CONFIG_DEFAULT();
|
||||
cfg.max_qrcode_version = MAX_QRCODE_VERSION;
|
||||
return esp_qrcode_generate(&cfg, text);
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint8_t *custom_mfg_data = NULL;
|
||||
static size_t custom_mfg_data_len = 0;
|
||||
|
||||
esp_err_t app_network_set_custom_mfg_data(uint16_t device_type, uint8_t device_subtype)
|
||||
{
|
||||
int8_t mfg_data[] = {MFG_DATA_HEADER, MGF_DATA_APP_ID, MFG_DATA_VERSION, MFG_DATA_CUSTOMER_ID};
|
||||
size_t mfg_data_len = sizeof(mfg_data) + 4; // 4 bytes of device type, subtype, and extra-code
|
||||
custom_mfg_data = (uint8_t *)MEM_ALLOC_EXTRAM(mfg_data_len);
|
||||
if (custom_mfg_data == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory to custom mfg data");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(custom_mfg_data, mfg_data, sizeof(mfg_data));
|
||||
custom_mfg_data[8] = 0xff & (device_type >> 8);
|
||||
custom_mfg_data[9] = 0xff & device_type;
|
||||
custom_mfg_data[10] = device_subtype;
|
||||
custom_mfg_data[11] = 0;
|
||||
custom_mfg_data_len = mfg_data_len;
|
||||
ESP_LOG_BUFFER_HEXDUMP("tag", custom_mfg_data, mfg_data_len, 3);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void app_network_print_qr(const char *name, const char *pop, const char *transport)
|
||||
{
|
||||
if (!name || !transport) {
|
||||
ESP_LOGW(TAG, "Cannot generate QR code payload. Data missing.");
|
||||
return;
|
||||
}
|
||||
char payload[150];
|
||||
if (pop) {
|
||||
snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \
|
||||
",\"pop\":\"%s\",\"transport\":\"%s\"}",
|
||||
PROV_QR_VERSION, name, pop, transport);
|
||||
} else {
|
||||
snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \
|
||||
",\"transport\":\"%s\"}",
|
||||
PROV_QR_VERSION, name, transport);
|
||||
}
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_SHOW_QR
|
||||
ESP_LOGI(TAG, "Scan this QR code from the ESP RainMaker phone app for Provisioning.");
|
||||
qrcode_display(payload);
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_SHOW_QR */
|
||||
ESP_LOGI(TAG, "If QR code is not visible, copy paste the below URL in a browser.\n%s?data=%s", QRCODE_BASE_URL, payload);
|
||||
esp_event_post(APP_NETWORK_EVENT, APP_NETWORK_EVENT_QR_DISPLAY, payload, strlen(payload) + 1, portMAX_DELAY);
|
||||
}
|
||||
|
||||
/* Free random_bytes after use only if function returns ESP_OK */
|
||||
static esp_err_t read_random_bytes_from_nvs(uint8_t **random_bytes, size_t *len)
|
||||
{
|
||||
nvs_handle handle;
|
||||
esp_err_t err;
|
||||
*len = 0;
|
||||
|
||||
if ((err = nvs_open_from_partition(CONFIG_ESP_RMAKER_FACTORY_PARTITION_NAME, CREDENTIALS_NAMESPACE,
|
||||
NVS_READONLY, &handle)) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "NVS open for %s %s %s failed with error %d", CONFIG_ESP_RMAKER_FACTORY_PARTITION_NAME, CREDENTIALS_NAMESPACE, RANDOM_NVS_KEY, err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if ((err = nvs_get_blob(handle, RANDOM_NVS_KEY, NULL, len)) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Error %d. Failed to read key %s.", err, RANDOM_NVS_KEY);
|
||||
nvs_close(handle);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
*random_bytes = calloc(*len, 1);
|
||||
if (*random_bytes) {
|
||||
nvs_get_blob(handle, RANDOM_NVS_KEY, *random_bytes, len);
|
||||
nvs_close(handle);
|
||||
return ESP_OK;
|
||||
}
|
||||
nvs_close(handle);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
static char *custom_pop;
|
||||
esp_err_t app_network_set_custom_pop(const char *pop)
|
||||
{
|
||||
/* NULL PoP is not allowed here. Use POP_TYPE_NONE instead. */
|
||||
if (!pop) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Freeing up the PoP in case it is already allocated */
|
||||
if (custom_pop) {
|
||||
free(custom_pop);
|
||||
custom_pop = NULL;
|
||||
}
|
||||
|
||||
custom_pop = strdup(pop);
|
||||
if (!custom_pop) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t get_device_service_name(char *service_name, size_t max)
|
||||
{
|
||||
uint8_t *nvs_random = NULL;
|
||||
const char *ssid_prefix = CONFIG_APP_NETWORK_PROV_NAME_PREFIX;
|
||||
size_t nvs_random_size = 0;
|
||||
if ((read_random_bytes_from_nvs(&nvs_random, &nvs_random_size) != ESP_OK) || nvs_random_size < 3) {
|
||||
uint8_t mac_addr[6];
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
esp_read_mac(mac_addr, ESP_MAC_BASE);
|
||||
#else
|
||||
esp_wifi_get_mac(WIFI_IF_STA, mac_addr);
|
||||
#endif
|
||||
snprintf(service_name, max, "%s_%02x%02x%02x", ssid_prefix, mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
} else {
|
||||
snprintf(service_name, max, "%s_%02x%02x%02x", ssid_prefix, nvs_random[nvs_random_size - 3],
|
||||
nvs_random[nvs_random_size - 2], nvs_random[nvs_random_size - 1]);
|
||||
}
|
||||
if (nvs_random) {
|
||||
free(nvs_random);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *get_device_pop(app_network_pop_type_t pop_type)
|
||||
{
|
||||
if (pop_type == POP_TYPE_NONE) {
|
||||
return NULL;
|
||||
} else if (pop_type == POP_TYPE_CUSTOM) {
|
||||
if (!custom_pop) {
|
||||
ESP_LOGE(TAG, "Custom PoP not set. Please use app_wifi_set_custom_pop().");
|
||||
return NULL;
|
||||
}
|
||||
return strdup(custom_pop);
|
||||
}
|
||||
char *pop = calloc(1, POP_STR_SIZE);
|
||||
if (!pop) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for PoP.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pop_type == POP_TYPE_MAC) {
|
||||
uint8_t mac_addr[6];
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
esp_err_t err = esp_read_mac(mac_addr, ESP_MAC_BASE);
|
||||
#else
|
||||
esp_err_t err = esp_wifi_get_mac(WIFI_IF_STA, mac_addr);
|
||||
#endif
|
||||
if (err == ESP_OK) {
|
||||
snprintf(pop, POP_STR_SIZE, "%02x%02x%02x%02x", mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
return pop;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to get MAC address to generate PoP.");
|
||||
goto pop_err;
|
||||
}
|
||||
} else if (pop_type == POP_TYPE_RANDOM) {
|
||||
uint8_t *nvs_random = NULL;
|
||||
size_t nvs_random_size = 0;
|
||||
if ((read_random_bytes_from_nvs(&nvs_random, &nvs_random_size) != ESP_OK) || nvs_random_size < 4) {
|
||||
ESP_LOGE(TAG, "Failed to read random bytes from NVS to generate PoP.");
|
||||
if (nvs_random) {
|
||||
free(nvs_random);
|
||||
}
|
||||
goto pop_err;
|
||||
} else {
|
||||
snprintf(pop, POP_STR_SIZE, "%02x%02x%02x%02x", nvs_random[0], nvs_random[1], nvs_random[2], nvs_random[3]);
|
||||
free(nvs_random);
|
||||
return pop;
|
||||
}
|
||||
}
|
||||
pop_err:
|
||||
free(pop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void network_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
|
||||
{
|
||||
|
||||
#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH
|
||||
static int failed_cnt = 0;
|
||||
#endif
|
||||
#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH
|
||||
if (event_base == PROTOCOMM_SECURITY_SESSION_EVENT) {
|
||||
switch (event_id) {
|
||||
case PROTOCOMM_SECURITY_SESSION_SETUP_OK:
|
||||
ESP_LOGI(TAG, "Secured session established!");
|
||||
break;
|
||||
case PROTOCOMM_SECURITY_SESSION_INVALID_SECURITY_PARAMS:
|
||||
/* fall-through */
|
||||
case PROTOCOMM_SECURITY_SESSION_CREDENTIALS_MISMATCH:
|
||||
ESP_LOGE(TAG, "Received incorrect PoP or invalid security params! event: %d", (int) event_id);
|
||||
if (CONFIG_APP_NETWORK_PROV_MAX_POP_MISMATCH &&
|
||||
(++failed_cnt >= CONFIG_APP_NETWORK_PROV_MAX_POP_MISMATCH)) {
|
||||
/* stop provisioning for security reasons */
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
network_prov_mgr_stop_provisioning();
|
||||
#else
|
||||
wifi_prov_mgr_stop_provisioning();
|
||||
#endif
|
||||
ESP_LOGW(TAG, "Max PoP attempts reached! Provisioning disabled for security reasons. Please reboot device to restart provisioning");
|
||||
esp_event_post(APP_NETWORK_EVENT, APP_NETWORK_EVENT_PROV_CRED_MISMATCH, NULL, 0, portMAX_DELAY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* APP_PROV_STOP_ON_CREDS_MISMATCH */
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
||||
ESP_LOGI(TAG, "Connected with IP Address:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
/* Signal main application to continue execution */
|
||||
xEventGroupSetBits(network_event_group, NETWORK_CONNECTED_EVENT);
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
if (event_base == OPENTHREAD_EVENT && event_id == OPENTHREAD_EVENT_ATTACHED) {
|
||||
/* Signal main application to continue execution */
|
||||
xEventGroupSetBits(network_event_group, NETWORK_CONNECTED_EVENT);
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
if (event_base == NETWORK_PROV_EVENT && event_id == NETWORK_PROV_END) {
|
||||
#else
|
||||
if (event_base == WIFI_PROV_EVENT && event_id == WIFI_PROV_END) {
|
||||
#endif
|
||||
if (prov_stop_timer) {
|
||||
esp_timer_stop(prov_stop_timer);
|
||||
esp_timer_delete(prov_stop_timer);
|
||||
prov_stop_timer = NULL;
|
||||
}
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
network_prov_mgr_deinit();
|
||||
#else
|
||||
wifi_prov_mgr_deinit();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void app_network_init()
|
||||
{
|
||||
/* Initialize the event loop, if not done already. */
|
||||
esp_err_t err = esp_event_loop_create_default();
|
||||
/* If the default event loop is already initialized, we get ESP_ERR_INVALID_STATE */
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGW(TAG, "Event loop creation failed with ESP_ERR_INVALID_STATE. Proceeding since it must have been created elsewhere.");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to create default event loop, err = %x", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
ESP_ERROR_CHECK(wifi_init());
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
ESP_ERROR_CHECK(thread_init());
|
||||
#endif
|
||||
network_event_group = xEventGroupCreate();
|
||||
#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(PROTOCOMM_SECURITY_SESSION_EVENT, ESP_EVENT_ANY_ID, &network_event_handler, NULL));
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &network_event_handler, NULL));
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(OPENTHREAD_EVENT, ESP_EVENT_ANY_ID, &network_event_handler, NULL));
|
||||
#endif
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_PROV_EVENT, NETWORK_PROV_END, &network_event_handler, NULL));
|
||||
#else
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_PROV_EVENT, WIFI_PROV_END, &network_event_handler, NULL));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void app_network_prov_stop(void *priv)
|
||||
{
|
||||
ESP_LOGW(TAG, "Provisioning timed out. Please reboot device to restart provisioning.");
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
network_prov_mgr_stop_provisioning();
|
||||
#else
|
||||
wifi_prov_mgr_stop_provisioning();
|
||||
#endif
|
||||
esp_event_post(APP_NETWORK_EVENT, APP_NETWORK_EVENT_PROV_TIMEOUT, NULL, 0, portMAX_DELAY);
|
||||
}
|
||||
|
||||
esp_err_t app_network_start_timer(void)
|
||||
{
|
||||
if (prov_timeout_period == 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_timer_create_args_t prov_stop_timer_conf = {
|
||||
.callback = app_network_prov_stop,
|
||||
.arg = NULL,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "app_wifi_prov_stop_tm"
|
||||
};
|
||||
if (esp_timer_create(&prov_stop_timer_conf, &prov_stop_timer) == ESP_OK) {
|
||||
esp_timer_start_once(prov_stop_timer, prov_timeout_period);
|
||||
ESP_LOGI(TAG, "Provisioning will auto stop after %d minute(s).",
|
||||
APP_NETWORK_PROV_TIMEOUT_PERIOD);
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to create Provisioning auto stop timer.");
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t app_network_start(app_network_pop_type_t pop_type)
|
||||
{
|
||||
/* Do we want a proof-of-possession (ignored if Security 0 is selected):
|
||||
* - this should be a string with length > 0
|
||||
* - NULL if not used
|
||||
*/
|
||||
char *pop = get_device_pop(pop_type);
|
||||
if ((pop_type != POP_TYPE_NONE) && (pop == NULL)) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
/* What is the Device Service Name that we want
|
||||
* This translates to :
|
||||
* - device name when scheme is network_prov_scheme_ble/wifi_prov_scheme_ble
|
||||
*/
|
||||
char service_name[12];
|
||||
get_device_service_name(service_name, sizeof(service_name));
|
||||
/* What is the service key (Wi-Fi password)
|
||||
* NULL = Open network
|
||||
* This is ignored when scheme is network_prov_scheme_ble/wifi_prov_scheme_ble
|
||||
*/
|
||||
const char *service_key = NULL;
|
||||
esp_err_t err = ESP_OK;
|
||||
bool provisioned = false;
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
err = wifi_start(pop, service_name, service_key, custom_mfg_data, custom_mfg_data_len, &provisioned);
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
err = thread_start(pop, service_name, service_key, custom_mfg_data, custom_mfg_data_len, &provisioned);
|
||||
#endif
|
||||
if (err != ESP_OK) {
|
||||
free(pop);
|
||||
return err;
|
||||
}
|
||||
if (!provisioned) {
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
app_network_print_qr(service_name, pop, PROV_TRANSPORT_BLE);
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
app_network_print_qr(service_name, pop, PROV_TRANSPORT_SOFTAP);
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
app_network_start_timer();
|
||||
}
|
||||
free(pop);
|
||||
intro_print(provisioned);
|
||||
if (custom_mfg_data) {
|
||||
free(custom_mfg_data);
|
||||
custom_mfg_data = NULL;
|
||||
custom_mfg_data_len = 0;
|
||||
}
|
||||
/* Wait for Network connection */
|
||||
xEventGroupWaitBits(network_event_group, NETWORK_CONNECTED_EVENT, false, true, portMAX_DELAY);
|
||||
return err;
|
||||
}
|
||||
120
examples/common/app_network/app_network.h
Normal file
120
examples/common/app_network/app_network.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
#include <esp_err.h>
|
||||
#include <esp_event.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MFG_DATA_HEADER 0xe5, 0x02
|
||||
#define MGF_DATA_APP_ID 'N', 'o', 'v'
|
||||
#define MFG_DATA_VERSION 'a'
|
||||
#define MFG_DATA_CUSTOMER_ID 0x00, 0x01
|
||||
|
||||
#define MGF_DATA_DEVICE_TYPE_LIGHT 0x0005
|
||||
#define MGF_DATA_DEVICE_TYPE_SWITCH 0x0080
|
||||
|
||||
#define MFG_DATA_DEVICE_SUBTYPE_SWITCH 0x01
|
||||
#define MFG_DATA_DEVICE_SUBTYPE_LIGHT 0x01
|
||||
|
||||
#define MFG_DATA_DEVICE_EXTRA_CODE 0x00
|
||||
|
||||
/** ESP RainMaker Event Base */
|
||||
ESP_EVENT_DECLARE_BASE(APP_NETWORK_EVENT);
|
||||
|
||||
/** App Network Events */
|
||||
typedef enum {
|
||||
/** QR code available for display. Associated data is the NULL terminated QR payload. */
|
||||
APP_NETWORK_EVENT_QR_DISPLAY = 1,
|
||||
/** Provisioning timed out */
|
||||
APP_NETWORK_EVENT_PROV_TIMEOUT,
|
||||
/** Provisioning has restarted due to failures (Invalid SSID/Passphrase) */
|
||||
APP_NETWORK_EVENT_PROV_RESTART,
|
||||
/** Provisioning closed due to invalid credentials */
|
||||
APP_NETWORK_EVENT_PROV_CRED_MISMATCH,
|
||||
} app_network_event_t;
|
||||
|
||||
/** Types of Proof of Possession */
|
||||
typedef enum {
|
||||
/** Use MAC address to generate PoP */
|
||||
POP_TYPE_MAC,
|
||||
/** Use random stream generated and stored in fctry partition during claiming process as PoP */
|
||||
POP_TYPE_RANDOM,
|
||||
/** Do not use any PoP.
|
||||
* Use this option with caution. Consider using `CONFIG_APP_NETWORK_PROV_TIMEOUT_PERIOD` with this.
|
||||
*/
|
||||
POP_TYPE_NONE,
|
||||
/** Use a custom PoP.
|
||||
* Set a custom PoP using app_network_set_custom_pop() first.
|
||||
*/
|
||||
POP_TYPE_CUSTOM
|
||||
} app_network_pop_type_t;
|
||||
|
||||
/** Initialize Wi-Fi/Thread
|
||||
*
|
||||
* This initializes Wi-Fi/Thread stack and the network provisioning manager
|
||||
*/
|
||||
void app_network_init();
|
||||
|
||||
/** Start Wi-Fi/Thread
|
||||
*
|
||||
* This will start provisioning if the node is not provisioned and will connect to any network
|
||||
* if node is provisioned. Function will return successfully only after network is connected
|
||||
*
|
||||
* @param[in] pop_type The type for Proof of Possession (PoP) pin
|
||||
*
|
||||
* @return ESP_OK on success (Network connected).
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t app_network_start(app_network_pop_type_t pop_type);
|
||||
|
||||
/** Set custom manufacturing data
|
||||
*
|
||||
* This can be used to add some custom manufacturing data in BLE advertisements during
|
||||
* provisioning. This can be used by apps to filter the scanned BLE devices and show
|
||||
* only the relevant one. Supported by Nova Home app for light and switch
|
||||
*
|
||||
* @param[in] device_type Type of the device, like light or switch
|
||||
* @param[in] device_subtype Sub Type of the device (application specific)
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t app_network_set_custom_mfg_data(uint16_t device_type, uint8_t device_subtype);
|
||||
|
||||
/** Set custom PoP
|
||||
*
|
||||
* This can be used to set a custom Proof of Possession (PoP) pin for provisioning.
|
||||
* Applicable only if POP_TYPE_CUSTOM is used for app_network_start().
|
||||
*
|
||||
* @param[in] pop A NULL terminated PoP string (typically 8 characters alphanumeric)
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t app_network_set_custom_pop(const char *pop);
|
||||
|
||||
#if CONFIG_APP_WIFI_PROV_COMPAT
|
||||
#define APP_WIFI_EVENT APP_NETWORK_EVENT
|
||||
typedef app_network_event_t app_wifi_event_t;
|
||||
#define APP_WIFI_EVENT_QR_DISPLAY APP_NETWORK_EVENT_QR_DISPLAY
|
||||
#define APP_WIFI_EVENT_PROV_TIMEOUT APP_NETWORK_EVENT_PROV_TIMEOUT
|
||||
#define APP_WIFI_EVENT_PROV_RESTART APP_NETWORK_EVENT_PROV_RESTART
|
||||
#define APP_WIFI_EVENT_PROV_CRED_MISMATCH APP_NETWORK_EVENT_PROV_CRED_MISMATCH
|
||||
typedef app_network_pop_type_t app_wifi_pop_type_t;
|
||||
#define app_wifi_init() app_network_init()
|
||||
#define app_wifi_start(pop_type) app_network_start(pop_type)
|
||||
#define app_wifi_set_custom_mfg_data(device_type, device_subtype) app_network_set_custom_mfg_data(device_type, device_subtype)
|
||||
#define app_wifi_set_custom_pop(pop) app_network_set_custom_pop(pop)
|
||||
#endif /* !CONFIG_APP_WIFI_PROV_COMPAT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
220
examples/common/app_network/app_thread_internal.c
Normal file
220
examples/common/app_network/app_thread_internal.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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 <sdkconfig.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_netif.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <app_network.h>
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
#include <app_thread_internal.h>
|
||||
#include <esp_vfs_eventfd.h>
|
||||
#include <esp_openthread.h>
|
||||
#include <esp_openthread_cli.h>
|
||||
#include <esp_openthread_lock.h>
|
||||
#include <esp_openthread_netif_glue.h>
|
||||
#include <esp_openthread_types.h>
|
||||
|
||||
#include <openthread/cli.h>
|
||||
#include <openthread/instance.h>
|
||||
#include <openthread/logging.h>
|
||||
#include <openthread/tasklet.h>
|
||||
#include <openthread/thread.h>
|
||||
|
||||
#include <network_provisioning/manager.h>
|
||||
#include <network_provisioning/scheme_ble.h>
|
||||
|
||||
|
||||
|
||||
static const char* TAG = "app_thread";
|
||||
/* Event handler for catching system events */
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
#ifdef CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
static int retries = 0;
|
||||
#endif
|
||||
if (event_base == NETWORK_PROV_EVENT) {
|
||||
switch (event_id) {
|
||||
case NETWORK_PROV_START:
|
||||
ESP_LOGI(TAG, "Provisioning started");
|
||||
break;
|
||||
case NETWORK_PROV_THREAD_DATASET_RECV: {
|
||||
break;
|
||||
}
|
||||
case NETWORK_PROV_THREAD_DATASET_FAIL: {
|
||||
#ifdef CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
retries++;
|
||||
if (retries >= CONFIG_APP_NETWORK_PROV_MAX_RETRY_CNT) {
|
||||
ESP_LOGI(TAG, "Failed to connect with provisioned network, reseting provisioned dataset");
|
||||
network_prov_mgr_reset_thread_sm_state_on_failure();
|
||||
esp_event_post(APP_NETWORK_EVENT, APP_NETWORK_EVENT_PROV_RESTART, NULL, 0, portMAX_DELAY);
|
||||
retries = 0;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case NETWORK_PROV_THREAD_DATASET_SUCCESS:
|
||||
ESP_LOGI(TAG, "Provisioning successful");
|
||||
#ifdef CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
retries = 0;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static esp_netif_t* init_openthread_netif(const esp_openthread_platform_config_t* config)
|
||||
{
|
||||
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD();
|
||||
esp_netif_t* netif = esp_netif_new(&cfg);
|
||||
assert(netif != NULL);
|
||||
ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config)));
|
||||
|
||||
return netif;
|
||||
}
|
||||
|
||||
static void ot_task_worker(void* aContext)
|
||||
{
|
||||
esp_openthread_platform_config_t config = {
|
||||
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
|
||||
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
|
||||
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
|
||||
};
|
||||
/* Initialize the OpenThread stack */
|
||||
ESP_ERROR_CHECK(esp_openthread_init(&config));
|
||||
#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC
|
||||
/* The OpenThread log level directly matches ESP log level */
|
||||
(void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL);
|
||||
#endif
|
||||
esp_netif_t *openthread_netif = init_openthread_netif(&config);
|
||||
/* Initialize the esp_netif bindings */
|
||||
esp_netif_set_default_netif(openthread_netif);
|
||||
|
||||
/* Run the main loop */
|
||||
esp_openthread_launch_mainloop();
|
||||
/* Clean up */
|
||||
esp_netif_destroy(openthread_netif);
|
||||
esp_openthread_netif_glue_deinit();
|
||||
|
||||
esp_vfs_eventfd_unregister();
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||
|
||||
esp_err_t thread_init()
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
/* Initialize TCP/IP */
|
||||
esp_netif_init();
|
||||
|
||||
/* Register our event handler for OpenThread and Provisioning related events */
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_PROV_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
|
||||
|
||||
esp_vfs_eventfd_config_t eventfd_config = {
|
||||
.max_fds = 3,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
|
||||
xTaskCreate(ot_task_worker, "ot_task", 6144, xTaskGetCurrentTaskHandle(), 5, NULL);
|
||||
return ESP_OK;
|
||||
#else
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||
}
|
||||
|
||||
esp_err_t thread_start(const char *pop, const char *service_name, const char *service_key, uint8_t *mfg_data,
|
||||
size_t mfg_data_len, bool *provisioned)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD
|
||||
/* Configuration for the provisioning manager */
|
||||
network_prov_mgr_config_t config = {
|
||||
.scheme = network_prov_scheme_ble,
|
||||
|
||||
/* Any default scheme specific event handler that you would
|
||||
* like to choose. Since our example application requires
|
||||
* neither BT nor BLE, we can choose to release the associated
|
||||
* memory once provisioning is complete, or not needed
|
||||
* (in case when device is already provisioned). Choosing
|
||||
* appropriate scheme specific event handler allows the manager
|
||||
* to take care of this automatically.*/
|
||||
.scheme_event_handler = NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM
|
||||
};
|
||||
|
||||
/* Initialize provisioning manager with the
|
||||
* configuration parameters set above */
|
||||
ESP_ERROR_CHECK(network_prov_mgr_init(config));
|
||||
|
||||
/* Let's find out if the device is provisioned */
|
||||
ESP_ERROR_CHECK(network_prov_mgr_is_thread_provisioned(provisioned));
|
||||
/* If device is not yet provisioned start provisioning service */
|
||||
if (!(*provisioned)) {
|
||||
ESP_LOGI(TAG, "Starting provisioning");
|
||||
|
||||
/* What is the security level that we want (0 or 1):
|
||||
* - NETWORK_PROV_SECURITY_0 is simply plain text communication.
|
||||
* - NETWORK_PROV_SECURITY_1 is secure communication which consists of secure handshake
|
||||
* using X25519 key exchange and proof of possession (pop) and AES-CTR
|
||||
* for encryption/decryption of messages.
|
||||
*/
|
||||
network_prov_security_t security = NETWORK_PROV_SECURITY_1;
|
||||
|
||||
/* This step is only useful when scheme is network_prov_scheme_ble. This will
|
||||
* set a custom 128 bit UUID which will be included in the BLE advertisement
|
||||
* and will correspond to the primary GATT service that provides provisioning
|
||||
* endpoints as GATT characteristics. Each GATT characteristic will be
|
||||
* formed using the primary service UUID as base, with different auto assigned
|
||||
* 12th and 13th bytes (assume counting starts from 0th byte). The client side
|
||||
* applications must identify the endpoints by reading the User Characteristic
|
||||
* Description descriptor (0x2901) for each characteristic, which contains the
|
||||
* endpoint name of the characteristic */
|
||||
uint8_t custom_service_uuid[] = {
|
||||
/* This is a random uuid. This can be modified if you want to change the BLE uuid. */
|
||||
/* 12th and 13th bit will be replaced by internal bits. */
|
||||
0xb4, 0xdf, 0x5a, 0x1c, 0x3f, 0x6b, 0xf4, 0xbf,
|
||||
0xea, 0x4a,0x82, 0x03, 0x04, 0x90, 0x1a, 0x02,
|
||||
};
|
||||
esp_err_t err = network_prov_scheme_ble_set_service_uuid(custom_service_uuid);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "thread_prov_scheme_ble_set_service_uuid failed %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (mfg_data) {
|
||||
err = network_prov_scheme_ble_set_mfg_data(mfg_data, mfg_data_len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set mfg data, err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start provisioning service */
|
||||
ESP_ERROR_CHECK(network_prov_mgr_start_provisioning(security, pop, service_name, service_key));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Already provisioned, enabling netif and starting Thread");
|
||||
/* We don't need the manager as device is already provisioned,
|
||||
* so let's release it's resources */
|
||||
network_prov_mgr_deinit();
|
||||
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
otInstance* instance = esp_openthread_get_instance();
|
||||
(void)otIp6SetEnabled(instance, true);
|
||||
(void)otThreadSetEnabled(instance, true);
|
||||
esp_openthread_lock_release();
|
||||
}
|
||||
return ESP_OK;
|
||||
#else
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||
}
|
||||
32
examples/common/app_network/app_wifi.h
Normal file
32
examples/common/app_network/app_wifi.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
#include <sdkconfig.h>
|
||||
#include <app_network.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_APP_WIFI_PROV_COMPAT
|
||||
#define APP_WIFI_EVENT APP_NETWORK_EVENT
|
||||
typedef app_network_event_t app_wifi_event_t;
|
||||
#define APP_WIFI_EVENT_QR_DISPLAY APP_NETWORK_EVENT_QR_DISPLAY
|
||||
#define APP_WIFI_EVENT_PROV_TIMEOUT APP_NETWORK_EVENT_PROV_TIMEOUT
|
||||
#define APP_WIFI_EVENT_PROV_RESTART APP_NETWORK_EVENT_PROV_RESTART
|
||||
#define APP_WIFI_EVENT_PROV_CRED_MISMATCH APP_NETWORK_EVENT_PROV_CRED_MISMATCH
|
||||
typedef app_network_pop_type_t app_wifi_pop_type_t;
|
||||
#define app_wifi_init() app_network_init()
|
||||
#define app_wifi_start(pop_type) app_network_start(pop_type)
|
||||
#define app_wifi_set_custom_mfg_data(device_type, device_subtype) app_network_set_custom_mfg_data(device_type, device_subtype)
|
||||
#define app_wifi_set_custom_pop(pop) app_network_set_custom_pop(pop)
|
||||
#endif /* !CONFIG_APP_WIFI_PROV_COMPAT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
334
examples/common/app_network/app_wifi_internal.c
Normal file
334
examples/common/app_network/app_wifi_internal.c
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
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 <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <app_network.h>
|
||||
#include <app_wifi_internal.h>
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0)
|
||||
// Features supported in 4.1+
|
||||
#define ESP_NETIF_SUPPORTED
|
||||
#endif
|
||||
|
||||
#ifdef ESP_NETIF_SUPPORTED
|
||||
#include <esp_netif.h>
|
||||
#else
|
||||
#include <tcpip_adapter.h>
|
||||
#endif
|
||||
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
#include <network_provisioning/manager.h>
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
#include <network_provisioning/scheme_ble.h>
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
#include <network_provisioning/scheme_softap.h>
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
#else
|
||||
#include <wifi_provisioning/manager.h>
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
#include <wifi_provisioning/scheme_ble.h>
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
#include <wifi_provisioning/scheme_softap.h>
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
|
||||
#endif
|
||||
|
||||
#include <app_wifi_internal.h>
|
||||
#include <app_network.h>
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 3)
|
||||
#define APP_PROV_STOP_ON_CREDS_MISMATCH
|
||||
#elif (CONFIG_APP_NETWORK_PROV_MAX_RETRY_CNT > 0)
|
||||
#warning "Provisioning window stop on max credentials failures, needs IDF version >= 5.1.3"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
static const char* TAG = "app_wifi";
|
||||
/* Event handler for catching system events */
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
#ifdef CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
static int retries = 0;
|
||||
#endif
|
||||
|
||||
#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
|
||||
ESP_LOGI(TAG, "Provisioning started");
|
||||
break;
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
case NETWORK_PROV_WIFI_CRED_RECV: {
|
||||
#else
|
||||
case WIFI_PROV_CRED_RECV: {
|
||||
#endif
|
||||
wifi_sta_config_t *wifi_sta_cfg = (wifi_sta_config_t *)event_data;
|
||||
ESP_LOGI(TAG, "Received Wi-Fi credentials"
|
||||
"\n\tSSID : %s\n\tPassword : %s",
|
||||
(const char *) wifi_sta_cfg->ssid,
|
||||
(const char *) wifi_sta_cfg->password);
|
||||
break;
|
||||
}
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
case NETWORK_PROV_WIFI_CRED_FAIL: {
|
||||
network_prov_wifi_sta_fail_reason_t *reason = (network_prov_wifi_sta_fail_reason_t *)event_data;
|
||||
#else
|
||||
case WIFI_PROV_CRED_FAIL: {
|
||||
wifi_prov_sta_fail_reason_t *reason = (wifi_prov_sta_fail_reason_t *)event_data;
|
||||
#endif
|
||||
ESP_LOGE(TAG, "Provisioning failed!\n\tReason : %s"
|
||||
"\n\tPlease reset to factory and retry provisioning",
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
(*reason == NETWORK_PROV_WIFI_STA_AUTH_ERROR) ?
|
||||
#else
|
||||
(*reason == WIFI_PROV_STA_AUTH_ERROR) ?
|
||||
#endif
|
||||
"Wi-Fi station authentication failed" : "Wi-Fi access-point not found");
|
||||
#ifdef CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
retries++;
|
||||
if (retries >= CONFIG_APP_NETWORK_PROV_MAX_RETRY_CNT) {
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 1)
|
||||
ESP_LOGI(TAG, "Failed to connect with provisioned AP, reseting provisioned credentials");
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
network_prov_mgr_reset_wifi_sm_state_on_failure();
|
||||
#else
|
||||
wifi_prov_mgr_reset_sm_state_on_failure();
|
||||
#endif // RMAKER_USING_NETWORK_PROV
|
||||
esp_event_post(APP_NETWORK_EVENT, APP_NETWORK_EVENT_PROV_RESTART, NULL, 0, portMAX_DELAY);
|
||||
#else
|
||||
ESP_LOGW(TAG, "Failed to connect with provisioned AP, please reset to provisioning manually");
|
||||
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 1)
|
||||
retries = 0;
|
||||
}
|
||||
#endif // CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
break;
|
||||
}
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
case NETWORK_PROV_WIFI_CRED_SUCCESS:
|
||||
#else
|
||||
case WIFI_PROV_CRED_SUCCESS:
|
||||
#endif
|
||||
ESP_LOGI(TAG, "Provisioning successful");
|
||||
#ifdef CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
retries = 0;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
ESP_LOGI(TAG, "Disconnected. Connecting to the AP again...");
|
||||
esp_wifi_connect();
|
||||
}
|
||||
}
|
||||
|
||||
static void wifi_init_sta()
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
}
|
||||
#endif // CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
|
||||
esp_err_t wifi_init(void)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
/* Initialize TCP/IP */
|
||||
#ifdef ESP_NETIF_SUPPORTED
|
||||
esp_netif_init();
|
||||
#else
|
||||
tcpip_adapter_init();
|
||||
#endif
|
||||
/* Register our event handler for Wi-Fi, IP and Provisioning related events */
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_PROV_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
|
||||
#else
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_PROV_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
|
||||
#endif
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
|
||||
|
||||
/* Initialize Wi-Fi including netif with default config */
|
||||
#ifdef ESP_NETIF_SUPPORTED
|
||||
esp_netif_create_default_wifi_sta();
|
||||
#endif
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
return ESP_OK;
|
||||
#else /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
}
|
||||
|
||||
esp_err_t wifi_start(const char *pop, const char *service_name, const char *service_key, uint8_t *mfg_data,
|
||||
size_t mfg_data_len, bool *provisioned)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
/* Configuration for the provisioning manager */
|
||||
network_prov_mgr_config_t config = {
|
||||
/* What is the Provisioning Scheme that we want ?
|
||||
* network_prov_scheme_softap or network_prov_scheme_ble */
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
.scheme = network_prov_scheme_ble,
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
.scheme = network_prov_scheme_softap,
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
|
||||
/* Any default scheme specific event handler that you would
|
||||
* like to choose. Since our example application requires
|
||||
* neither BT nor BLE, we can choose to release the associated
|
||||
* memory once provisioning is complete, or not needed
|
||||
* (in case when device is already provisioned). Choosing
|
||||
* appropriate scheme specific event handler allows the manager
|
||||
* to take care of this automatically. This can be set to
|
||||
* NETWORK_PROV_EVENT_HANDLER_NONE when using network_prov_scheme_softap*/
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
.scheme_event_handler = NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
.scheme_event_handler = NETWORK_PROV_EVENT_HANDLER_NONE,
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
};
|
||||
|
||||
/* Initialize provisioning manager with the
|
||||
* configuration parameters set above */
|
||||
ESP_ERROR_CHECK(network_prov_mgr_init(config));
|
||||
#else // RMAKER_USING_NETWORK_PROV
|
||||
/* Configuration for the provisioning manager */
|
||||
wifi_prov_mgr_config_t config = {
|
||||
/* What is the Provisioning Scheme that we want ?
|
||||
* wifi_prov_scheme_softap or wifi_prov_scheme_ble */
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
.scheme = wifi_prov_scheme_ble,
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
.scheme = wifi_prov_scheme_softap,
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
|
||||
/* Any default scheme specific event handler that you would
|
||||
* like to choose. Since our example application requires
|
||||
* neither BT nor BLE, we can choose to release the associated
|
||||
* memory once provisioning is complete, or not needed
|
||||
* (in case when device is already provisioned). Choosing
|
||||
* appropriate scheme specific event handler allows the manager
|
||||
* to take care of this automatically. This can be set to
|
||||
* WIFI_PROV_EVENT_HANDLER_NONE when using wifi_prov_scheme_softap*/
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
.scheme_event_handler = WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
.scheme_event_handler = WIFI_PROV_EVENT_HANDLER_NONE,
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
};
|
||||
|
||||
/* Initialize provisioning manager with the
|
||||
* configuration parameters set above */
|
||||
ESP_ERROR_CHECK(wifi_prov_mgr_init(config));
|
||||
#endif // RMAKER_USING_NETWORK_PROV
|
||||
/* Let's find out if the device is provisioned */
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
network_prov_mgr_is_wifi_provisioned(provisioned);
|
||||
#else
|
||||
wifi_prov_mgr_is_provisioned(provisioned);
|
||||
#endif
|
||||
/* If device is not yet provisioned start provisioning service */
|
||||
if (!(*provisioned)) {
|
||||
ESP_LOGI(TAG, "Starting provisioning");
|
||||
#ifdef ESP_NETIF_SUPPORTED
|
||||
#if CONFIG_ESP_WIFI_SOFTAP_SUPPORT
|
||||
esp_netif_create_default_wifi_ap();
|
||||
#endif
|
||||
#endif
|
||||
/* What is the security level that we want (0 or 1):
|
||||
* - NETWORK_PROV_SECURITY_0/WIFI_PROV_SECURITY_0 is simply plain text communication.
|
||||
* - NETWORK_PROV_SECURITY_1/WIFI_PROV_SECURITY_1 is secure communication which consists of secure handshake
|
||||
* using X25519 key exchange and proof of possession (pop) and AES-CTR
|
||||
* for encryption/decryption of messages.
|
||||
*/
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
network_prov_security_t security = NETWORK_PROV_SECURITY_1;
|
||||
#else
|
||||
wifi_prov_security_t security = WIFI_PROV_SECURITY_1;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
/* This step is only useful when scheme is wifi_prov_scheme_ble. This will
|
||||
* set a custom 128 bit UUID which will be included in the BLE advertisement
|
||||
* and will correspond to the primary GATT service that provides provisioning
|
||||
* endpoints as GATT characteristics. Each GATT characteristic will be
|
||||
* formed using the primary service UUID as base, with different auto assigned
|
||||
* 12th and 13th bytes (assume counting starts from 0th byte). The client side
|
||||
* applications must identify the endpoints by reading the User Characteristic
|
||||
* Description descriptor (0x2901) for each characteristic, which contains the
|
||||
* endpoint name of the characteristic */
|
||||
uint8_t custom_service_uuid[] = {
|
||||
/* This is a random uuid. This can be modified if you want to change the BLE uuid. */
|
||||
/* 12th and 13th bit will be replaced by internal bits. */
|
||||
0xb4, 0xdf, 0x5a, 0x1c, 0x3f, 0x6b, 0xf4, 0xbf,
|
||||
0xea, 0x4a, 0x82, 0x03, 0x04, 0x90, 0x1a, 0x02,
|
||||
};
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
esp_err_t err = network_prov_scheme_ble_set_service_uuid(custom_service_uuid);
|
||||
#else
|
||||
esp_err_t err = wifi_prov_scheme_ble_set_service_uuid(custom_service_uuid);
|
||||
#endif
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "wifi_prov_scheme_ble_set_service_uuid failed %d", err);
|
||||
return err;
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
if (mfg_data) {
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
err = network_prov_scheme_ble_set_mfg_data(mfg_data, mfg_data_len);
|
||||
#else
|
||||
err = wifi_prov_scheme_ble_set_mfg_data(mfg_data, mfg_data_len);
|
||||
#endif
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set mfg data, err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
|
||||
/* Start provisioning service */
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
ESP_ERROR_CHECK(network_prov_mgr_start_provisioning(security, pop, service_name, service_key));
|
||||
#else
|
||||
ESP_ERROR_CHECK(wifi_prov_mgr_start_provisioning(security, pop, service_name, service_key));
|
||||
#endif
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Already provisioned, starting Wi-Fi STA");
|
||||
/* We don't need the manager as device is already provisioned,
|
||||
* so let's release it's resources */
|
||||
#if RMAKER_USING_NETWORK_PROV
|
||||
network_prov_mgr_deinit();
|
||||
#else
|
||||
wifi_prov_mgr_deinit();
|
||||
#endif
|
||||
|
||||
/* Start Wi-Fi station */
|
||||
wifi_init_sta();
|
||||
}
|
||||
return ESP_OK;
|
||||
#else /* CONFIG_ESP_RMAKER_NETWORK_OVER_THREAD */
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
}
|
||||
5
examples/common/app_network/component.mk
Normal file
5
examples/common/app_network/component.mk
Normal file
@@ -0,0 +1,5 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
COMPONENT_SRCDIRS := .
|
||||
ifdef CONFIG_APP_WIFI_SHOW_DEMO_INTRO_TEXT
|
||||
CPPFLAGS += -D RMAKER_DEMO_PROJECT_NAME=\"$(PROJECT_NAME)\"
|
||||
endif
|
||||
4
examples/common/app_network/idf_component.yml
Normal file
4
examples/common/app_network/idf_component.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/qrcode:
|
||||
version: "*"
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.
|
||||
* */
|
||||
#pragma once
|
||||
#include <esp_err.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_openthread_types.h>
|
||||
#include <esp_idf_version.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#if !RMAKER_USING_NETWORK_PROV
|
||||
#error "Please use IDF v5.1+ and enable ESP_RMAKER_USING_NETWORK_PROV for Thread devices"
|
||||
#endif
|
||||
|
||||
#if SOC_IEEE802154_SUPPORTED
|
||||
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
|
||||
{ \
|
||||
.radio_mode = RADIO_MODE_NATIVE, \
|
||||
}
|
||||
|
||||
#else
|
||||
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
|
||||
{ \
|
||||
.radio_mode = RADIO_MODE_UART_RCP, \
|
||||
.radio_uart_config = { \
|
||||
.port = 1, \
|
||||
.uart_config = { \
|
||||
.baud_rate = 460800, \
|
||||
.data_bits = UART_DATA_8_BITS, \
|
||||
.parity = UART_PARITY_DISABLE, \
|
||||
.stop_bits = UART_STOP_BITS_1, \
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \
|
||||
.rx_flow_ctrl_thresh = 0, \
|
||||
.source_clk = UART_SCLK_DEFAULT, \
|
||||
}, \
|
||||
.rx_pin = 4, \
|
||||
.tx_pin = 5, \
|
||||
}, \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
|
||||
{ \
|
||||
.host_connection_mode = HOST_CONNECTION_MODE_NONE, \
|
||||
}
|
||||
|
||||
#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \
|
||||
{ \
|
||||
.storage_partition_name = "nvs", \
|
||||
.netif_queue_size = 10, \
|
||||
.task_queue_size = 10, \
|
||||
}
|
||||
|
||||
esp_err_t thread_init();
|
||||
|
||||
esp_err_t thread_start(const char *pop, const char *service_name, const char *service_key, uint8_t *mfg_data,
|
||||
size_t mfg_data_len, bool *provisioned);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
#include <esp_err.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include "app_network.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
/** Initialize Wi-Fi
|
||||
*
|
||||
* This initializes Wi-Fi and the network/wifi provisioning manager
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t wifi_init();
|
||||
|
||||
/** Start Wi-Fi
|
||||
*
|
||||
* This will start provisioning if the node is not provisioned and will connect to Wi-Fi
|
||||
* if node is provisioned. Function will return successfully only after Wi-Fi is connect
|
||||
*
|
||||
* @param[in] pop The Proof of Possession (PoP) pin
|
||||
* @param[in] service_name The service name of network/wifi provisioning. This translates to
|
||||
* - Wi-Fi SSID when scheme is network_prov_scheme_softap/wifi_prov_scheme_softap
|
||||
* - device name when scheme is network_prov_scheme_ble/wifi_prov_scheme_ble
|
||||
* @param[in] service_key The service key of network/wifi provisioning. This translates to
|
||||
* - Wi-Fi password when scheme is network_prov_scheme_softap/wifi_prov_scheme_softap (NULL = Open network)
|
||||
* @param[in] mfg_data The manufactuer specific data of network/wifi provisioning.
|
||||
* @param[in] mfg_data The manufactuer specific data length of network/wifi provisioning.
|
||||
* @param[out] provisioned Whether the device is provisioned.
|
||||
*
|
||||
* @return ESP_OK on success (Wi-Fi connected).
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t wifi_start(const char *pop, const char *service_name, const char *service_key, uint8_t *mfg_data,
|
||||
size_t mfg_data_len, bool *provisioned);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
13
examples/common/app_network/sdkconfig.rename
Normal file
13
examples/common/app_network/sdkconfig.rename
Normal file
@@ -0,0 +1,13 @@
|
||||
# sdkconfig replacement configurations for deprecated options formatted as
|
||||
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
|
||||
|
||||
|
||||
CONFIG_APP_WIFI_PROV_SHOW_QR CONFIG_APP_NETWORK_PROV_SHOW_QR
|
||||
CONFIG_APP_WIFI_PROV_MAX_POP_MISMATCH CONFIG_APP_NETWORK_PROV_MAX_POP_MISMATCH
|
||||
CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP
|
||||
CONFIG_APP_WIFI_PROV_TRANSPORT_BLE CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
CONFIG_APP_WIFI_PROV_TRANSPORT CONFIG_APP_NETWORK_PROV_TRANSPORT
|
||||
CONFIG_APP_WIFI_RESET_PROV_ON_FAILURE CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
CONFIG_APP_WIFI_SHOW_DEMO_INTRO_TEXT CONFIG_APP_NETWORK_SHOW_DEMO_INTRO_TEXT
|
||||
CONFIG_APP_WIFI_PROV_TIMEOUT_PERIOD CONFIG_APP_NETWORK_PROV_TIMEOUT_PERIOD
|
||||
CONFIG_APP_WIFI_PROV_NAME_PREFIX CONFIG_APP_NETWORK_PROV_NAME_PREFIX
|
||||
3
examples/common/app_reset/CMakeLists.txt
Normal file
3
examples/common/app_reset/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "app_reset.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES gpio_button rmaker_common)
|
||||
66
examples/common/app_reset/app_reset.c
Normal file
66
examples/common/app_reset/app_reset.c
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/* It is recommended to copy this code in your example so that you can modify as
|
||||
* per your application's needs, especially for the indicator calbacks,
|
||||
* wifi_reset_indicate() and factory_reset_indicate().
|
||||
*/
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
#include <iot_button.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
|
||||
static const char *TAG = "app_reset";
|
||||
|
||||
#define REBOOT_DELAY 2
|
||||
#define RESET_DELAY 2
|
||||
|
||||
static void wifi_reset_trigger(void *arg)
|
||||
{
|
||||
esp_rmaker_wifi_reset(RESET_DELAY, REBOOT_DELAY);
|
||||
}
|
||||
|
||||
static void wifi_reset_indicate(void *arg)
|
||||
{
|
||||
ESP_LOGI(TAG, "Release button now for Wi-Fi reset. Keep pressed for factory reset.");
|
||||
}
|
||||
|
||||
static void factory_reset_trigger(void *arg)
|
||||
{
|
||||
esp_rmaker_factory_reset(RESET_DELAY, REBOOT_DELAY);
|
||||
}
|
||||
|
||||
static void factory_reset_indicate(void *arg)
|
||||
{
|
||||
ESP_LOGI(TAG, "Release button to trigger factory reset.");
|
||||
}
|
||||
|
||||
esp_err_t app_reset_button_register(button_handle_t btn_handle, uint8_t wifi_reset_timeout,
|
||||
uint8_t factory_reset_timeout)
|
||||
{
|
||||
if (!btn_handle) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (wifi_reset_timeout) {
|
||||
iot_button_add_on_release_cb(btn_handle, wifi_reset_timeout, wifi_reset_trigger, NULL);
|
||||
iot_button_add_on_press_cb(btn_handle, wifi_reset_timeout, wifi_reset_indicate, NULL);
|
||||
}
|
||||
if (factory_reset_timeout) {
|
||||
if (factory_reset_timeout <= wifi_reset_timeout) {
|
||||
ESP_LOGW(TAG, "It is recommended to have factory_reset_timeout > wifi_reset_timeout");
|
||||
}
|
||||
iot_button_add_on_release_cb(btn_handle, factory_reset_timeout, factory_reset_trigger, NULL);
|
||||
iot_button_add_on_press_cb(btn_handle, factory_reset_timeout, factory_reset_indicate, NULL);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
button_handle_t app_reset_button_create(gpio_num_t gpio_num, button_active_t active_level)
|
||||
{
|
||||
return iot_button_create(gpio_num, active_level);
|
||||
}
|
||||
50
examples/common/app_reset/app_reset.h
Normal file
50
examples/common/app_reset/app_reset.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <iot_button.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Create a button handle
|
||||
*
|
||||
* This is just a wrapper over iot_button_create(). This can be used to register
|
||||
* Wi-Fi/Factory reset functionality for a button.
|
||||
*
|
||||
* @param[in] gpio_num GPIO index of the pin that the button uses.
|
||||
* @param[in] active_level button hardware active level.
|
||||
* "BUTTON_ACTIVE_LOW" means that when the button is pressed, the GPIO will read low level.
|
||||
* For "BUTTON_ACTIVE_HIGH", it will be reverse.
|
||||
*
|
||||
* @return A button_handle_t handle to the created button object, or NULL in case of error.
|
||||
*/
|
||||
button_handle_t app_reset_button_create(gpio_num_t gpio_num, button_active_t active_level);
|
||||
|
||||
/** Register callbacks for Wi-Fi/Factory reset
|
||||
*
|
||||
* Register Wi-Fi reset or factory reset functionality on a button.
|
||||
* If you want to use different buttons for these two, call this API twice, with appropriate
|
||||
* button handles.
|
||||
*
|
||||
* @param[in] btn_handle Button handle returned by iot_button_create() or app_button_create()
|
||||
* @param[in] wifi_reset_timeout Timeout after which the Wi-Fi reset should be triggered. Set to 0,
|
||||
* if you do not want Wi-Fi reset.
|
||||
* @param[in] factory_reset_timeout Timeout after which the factory reset should be triggered. Set to 0,
|
||||
* if you do not want factory reset.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t app_reset_button_register(button_handle_t btn_handle, uint8_t wifi_reset_timeout, uint8_t factory_reset_timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
2
examples/common/app_reset/component.mk
Normal file
2
examples/common/app_reset/component.mk
Normal file
@@ -0,0 +1,2 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
COMPONENT_SRCDIRS := .
|
||||
3
examples/common/gpio_button/CMakeLists.txt
Normal file
3
examples/common/gpio_button/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "button/button.c" "button/button_obj.cpp"
|
||||
INCLUDE_DIRS "button/include"
|
||||
REQUIRES "driver")
|
||||
6
examples/common/gpio_button/Kconfig
Normal file
6
examples/common/gpio_button/Kconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
menu "GPIO Button"
|
||||
config IO_GLITCH_FILTER_TIME_MS
|
||||
int "IO glitch filter timer ms (10~100)"
|
||||
range 10 100
|
||||
default 50
|
||||
endmenu
|
||||
353
examples/common/gpio_button/button/button.c
Normal file
353
examples/common/gpio_button/button/button.c
Normal file
@@ -0,0 +1,353 @@
|
||||
// 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 <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <esp_log.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <iot_button.h>
|
||||
|
||||
#define IOT_CHECK(tag, a, ret) if(!(a)) { \
|
||||
ESP_LOGE(tag,"%s:%d (%s)", __FILE__, __LINE__, __FUNCTION__); \
|
||||
return (ret); \
|
||||
}
|
||||
#define ERR_ASSERT(tag, param) IOT_CHECK(tag, (param) == ESP_OK, ESP_FAIL)
|
||||
#define POINT_ASSERT(tag, param, ret) IOT_CHECK(tag, (param) != NULL, (ret))
|
||||
|
||||
typedef enum {
|
||||
BUTTON_STATE_IDLE = 0,
|
||||
BUTTON_STATE_PUSH,
|
||||
BUTTON_STATE_PRESSED,
|
||||
} button_status_t;
|
||||
|
||||
typedef struct button_dev button_dev_t;
|
||||
typedef struct btn_cb button_cb_t;
|
||||
|
||||
struct btn_cb{
|
||||
TickType_t interval;
|
||||
button_cb cb;
|
||||
void* arg;
|
||||
uint8_t on_press;
|
||||
TimerHandle_t tmr;
|
||||
button_dev_t *pbtn;
|
||||
button_cb_t *next_cb;
|
||||
};
|
||||
|
||||
struct button_dev{
|
||||
uint8_t io_num;
|
||||
uint8_t active_level;
|
||||
uint32_t serial_thres_sec;
|
||||
uint8_t taskq_on;
|
||||
QueueHandle_t taskq;
|
||||
QueueHandle_t argq;
|
||||
button_status_t state;
|
||||
button_cb_t tap_short_cb;
|
||||
button_cb_t tap_psh_cb;
|
||||
button_cb_t tap_rls_cb;
|
||||
button_cb_t press_serial_cb;
|
||||
button_cb_t* cb_head;
|
||||
};
|
||||
|
||||
#define BUTTON_GLITCH_FILTER_TIME_MS CONFIG_IO_GLITCH_FILTER_TIME_MS
|
||||
static const char* TAG = "button";
|
||||
|
||||
static void button_press_cb(TimerHandle_t tmr)
|
||||
{
|
||||
button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr);
|
||||
button_dev_t* btn = btn_cb->pbtn;
|
||||
// low, then restart
|
||||
if (btn->active_level == gpio_get_level(btn->io_num)) {
|
||||
btn->state = BUTTON_STATE_PRESSED;
|
||||
if (btn->taskq != NULL && btn->argq != NULL && btn->taskq_on && !btn_cb->on_press) {
|
||||
void *tmp = btn_cb->cb;
|
||||
xQueueOverwrite(btn->taskq, &tmp);
|
||||
xQueueOverwrite(btn->argq, &btn_cb->arg);
|
||||
} else if (btn_cb->cb) {
|
||||
btn_cb->cb(btn_cb->arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void button_tap_psh_cb(TimerHandle_t tmr)
|
||||
{
|
||||
button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr);
|
||||
button_dev_t* btn = btn_cb->pbtn;
|
||||
xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY);
|
||||
int lv = gpio_get_level(btn->io_num);
|
||||
|
||||
if (btn->active_level == lv) {
|
||||
// True implies key is pressed
|
||||
btn->state = BUTTON_STATE_PUSH;
|
||||
if (btn->press_serial_cb.tmr) {
|
||||
xTimerChangePeriod(btn->press_serial_cb.tmr, btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, portMAX_DELAY);
|
||||
xTimerReset(btn->press_serial_cb.tmr, portMAX_DELAY);
|
||||
}
|
||||
if (btn->tap_psh_cb.cb) {
|
||||
btn->tap_psh_cb.cb(btn->tap_psh_cb.arg);
|
||||
}
|
||||
} else {
|
||||
// 50ms, check if this is a real key up
|
||||
if (btn->tap_rls_cb.tmr) {
|
||||
xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY);
|
||||
xTimerReset(btn->tap_rls_cb.tmr, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void button_tap_rls_cb(TimerHandle_t tmr)
|
||||
{
|
||||
button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr);
|
||||
button_dev_t* btn = btn_cb->pbtn;
|
||||
xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY);
|
||||
if (btn->active_level == gpio_get_level(btn->io_num)) {
|
||||
|
||||
} else {
|
||||
// high, then key is up
|
||||
button_cb_t *pcb = btn->cb_head;
|
||||
while (pcb != NULL) {
|
||||
if (pcb->tmr != NULL) {
|
||||
xTimerStop(pcb->tmr, portMAX_DELAY);
|
||||
}
|
||||
pcb = pcb->next_cb;
|
||||
}
|
||||
if (btn->taskq != NULL && btn->argq != NULL && btn->taskq_on && uxQueueMessagesWaiting(btn->taskq) != 0 && btn->state != BUTTON_STATE_IDLE) {
|
||||
void (*task)(void*);
|
||||
void *arg;
|
||||
xQueueReceive(btn->taskq, &task, 0);
|
||||
xQueueReceive(btn->argq, &arg, 0);
|
||||
task(arg);
|
||||
}
|
||||
if (btn->press_serial_cb.tmr && btn->press_serial_cb.tmr != NULL) {
|
||||
xTimerStop(btn->press_serial_cb.tmr, portMAX_DELAY);
|
||||
}
|
||||
if (btn->tap_short_cb.cb && btn->state == BUTTON_STATE_PUSH) {
|
||||
btn->tap_short_cb.cb(btn->tap_short_cb.arg);
|
||||
}
|
||||
if(btn->tap_rls_cb.cb && btn->state != BUTTON_STATE_IDLE) {
|
||||
btn->tap_rls_cb.cb(btn->tap_rls_cb.arg);
|
||||
}
|
||||
btn->state = BUTTON_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
static void button_press_serial_cb(TimerHandle_t tmr)
|
||||
{
|
||||
button_dev_t* btn = (button_dev_t*) pvTimerGetTimerID(tmr);
|
||||
if (btn->press_serial_cb.cb) {
|
||||
btn->press_serial_cb.cb(btn->press_serial_cb.arg);
|
||||
}
|
||||
xTimerChangePeriod(btn->press_serial_cb.tmr, btn->press_serial_cb.interval, portMAX_DELAY);
|
||||
xTimerReset(btn->press_serial_cb.tmr, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static void button_gpio_isr_handler(void* arg)
|
||||
{
|
||||
button_dev_t* btn = (button_dev_t*) arg;
|
||||
portBASE_TYPE HPTaskAwoken = pdFALSE;
|
||||
int level = gpio_get_level(btn->io_num);
|
||||
if (level == btn->active_level) {
|
||||
if (btn->tap_psh_cb.tmr) {
|
||||
xTimerStopFromISR(btn->tap_psh_cb.tmr, &HPTaskAwoken);
|
||||
xTimerResetFromISR(btn->tap_psh_cb.tmr, &HPTaskAwoken);
|
||||
}
|
||||
|
||||
button_cb_t *pcb = btn->cb_head;
|
||||
while (pcb != NULL) {
|
||||
if (pcb->tmr != NULL) {
|
||||
xTimerStopFromISR(pcb->tmr, &HPTaskAwoken);
|
||||
xTimerResetFromISR(pcb->tmr, &HPTaskAwoken);
|
||||
}
|
||||
pcb = pcb->next_cb;
|
||||
}
|
||||
} else {
|
||||
// 50ms, check if this is a real key up
|
||||
if (btn->tap_rls_cb.tmr) {
|
||||
xTimerStopFromISR(btn->tap_rls_cb.tmr, &HPTaskAwoken);
|
||||
xTimerResetFromISR(btn->tap_rls_cb.tmr, &HPTaskAwoken);
|
||||
}
|
||||
}
|
||||
if(HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
static void button_free_tmr(TimerHandle_t* tmr)
|
||||
{
|
||||
if (tmr && *tmr) {
|
||||
xTimerStop(*tmr, portMAX_DELAY);
|
||||
xTimerDelete(*tmr, portMAX_DELAY);
|
||||
*tmr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t iot_button_delete(button_handle_t btn_handle)
|
||||
{
|
||||
POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG);
|
||||
button_dev_t* btn = (button_dev_t*) btn_handle;
|
||||
gpio_set_intr_type(btn->io_num, GPIO_INTR_DISABLE);
|
||||
gpio_isr_handler_remove(btn->io_num);
|
||||
|
||||
button_free_tmr(&btn->tap_rls_cb.tmr);
|
||||
button_free_tmr(&btn->tap_psh_cb.tmr);
|
||||
button_free_tmr(&btn->tap_short_cb.tmr);
|
||||
button_free_tmr(&btn->press_serial_cb.tmr);
|
||||
|
||||
button_cb_t *pcb = btn->cb_head;
|
||||
while (pcb != NULL) {
|
||||
button_cb_t *cb_next = pcb->next_cb;
|
||||
button_free_tmr(&pcb->tmr);
|
||||
free(pcb);
|
||||
pcb = cb_next;
|
||||
}
|
||||
free(btn);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
button_handle_t iot_button_create(gpio_num_t gpio_num, button_active_t active_level)
|
||||
{
|
||||
IOT_CHECK(TAG, gpio_num < GPIO_NUM_MAX, NULL);
|
||||
button_dev_t* btn = (button_dev_t*) calloc(1, sizeof(button_dev_t));
|
||||
POINT_ASSERT(TAG, btn, NULL);
|
||||
btn->active_level = active_level;
|
||||
btn->io_num = gpio_num;
|
||||
btn->state = BUTTON_STATE_IDLE;
|
||||
btn->taskq_on = 0;
|
||||
btn->taskq = xQueueCreate(1, sizeof(void*));
|
||||
btn->argq = xQueueCreate(1, sizeof(void *));
|
||||
btn->tap_rls_cb.arg = NULL;
|
||||
btn->tap_rls_cb.cb = NULL;
|
||||
btn->tap_rls_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS;
|
||||
btn->tap_rls_cb.pbtn = btn;
|
||||
btn->tap_rls_cb.tmr = xTimerCreate("btn_rls_tmr", btn->tap_rls_cb.interval, pdFALSE,
|
||||
&btn->tap_rls_cb, button_tap_rls_cb);
|
||||
btn->tap_psh_cb.arg = NULL;
|
||||
btn->tap_psh_cb.cb = NULL;
|
||||
btn->tap_psh_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS;
|
||||
btn->tap_psh_cb.pbtn = btn;
|
||||
btn->tap_psh_cb.tmr = xTimerCreate("btn_psh_tmr", btn->tap_psh_cb.interval, pdFALSE,
|
||||
&btn->tap_psh_cb, button_tap_psh_cb);
|
||||
gpio_install_isr_service(0);
|
||||
gpio_config_t gpio_conf;
|
||||
gpio_conf.intr_type = GPIO_INTR_ANYEDGE;
|
||||
gpio_conf.mode = GPIO_MODE_INPUT;
|
||||
gpio_conf.pin_bit_mask = (uint64_t)1 << gpio_num;
|
||||
gpio_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
gpio_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
gpio_config(&gpio_conf);
|
||||
gpio_isr_handler_add(gpio_num, button_gpio_isr_handler, btn);
|
||||
return (button_handle_t) btn;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_rm_cb(button_handle_t btn_handle, button_cb_type_t type)
|
||||
{
|
||||
button_dev_t* btn = (button_dev_t*) btn_handle;
|
||||
button_cb_t* btn_cb = NULL;
|
||||
if (type == BUTTON_CB_PUSH) {
|
||||
btn_cb = &btn->tap_psh_cb;
|
||||
} else if (type == BUTTON_CB_RELEASE) {
|
||||
btn_cb = &btn->tap_rls_cb;
|
||||
} else if (type == BUTTON_CB_TAP) {
|
||||
btn_cb = &btn->tap_short_cb;
|
||||
} else if (type == BUTTON_CB_SERIAL) {
|
||||
btn_cb = &btn->press_serial_cb;
|
||||
}
|
||||
btn_cb->cb = NULL;
|
||||
btn_cb->arg = NULL;
|
||||
btn_cb->pbtn = btn;
|
||||
button_free_tmr(&btn_cb->tmr);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_set_serial_cb(button_handle_t btn_handle, uint32_t start_after_sec, TickType_t interval_tick, button_cb cb, void* arg)
|
||||
{
|
||||
button_dev_t* btn = (button_dev_t*) btn_handle;
|
||||
btn->serial_thres_sec = start_after_sec;
|
||||
if (btn->press_serial_cb.tmr == NULL) {
|
||||
btn->press_serial_cb.tmr = xTimerCreate("btn_serial_tmr", btn->serial_thres_sec*1000 / portTICK_PERIOD_MS,
|
||||
pdFALSE, btn, button_press_serial_cb);
|
||||
}
|
||||
btn->press_serial_cb.arg = arg;
|
||||
btn->press_serial_cb.cb = cb;
|
||||
btn->press_serial_cb.interval = interval_tick;
|
||||
btn->press_serial_cb.pbtn = btn;
|
||||
xTimerChangePeriod(btn->press_serial_cb.tmr, btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, portMAX_DELAY);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_set_evt_cb(button_handle_t btn_handle, button_cb_type_t type, button_cb cb, void* arg)
|
||||
{
|
||||
POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG);
|
||||
button_dev_t* btn = (button_dev_t*) btn_handle;
|
||||
if (type == BUTTON_CB_PUSH) {
|
||||
btn->tap_psh_cb.arg = arg;
|
||||
btn->tap_psh_cb.cb = cb;
|
||||
btn->tap_psh_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS;
|
||||
btn->tap_psh_cb.pbtn = btn;
|
||||
xTimerChangePeriod(btn->tap_psh_cb.tmr, btn->tap_psh_cb.interval, portMAX_DELAY);
|
||||
} else if (type == BUTTON_CB_RELEASE) {
|
||||
btn->tap_rls_cb.arg = arg;
|
||||
btn->tap_rls_cb.cb = cb;
|
||||
btn->tap_rls_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS;
|
||||
btn->tap_rls_cb.pbtn = btn;
|
||||
xTimerChangePeriod(btn->tap_rls_cb.tmr, btn->tap_psh_cb.interval, portMAX_DELAY);
|
||||
} else if (type == BUTTON_CB_TAP) {
|
||||
btn->tap_short_cb.arg = arg;
|
||||
btn->tap_short_cb.cb = cb;
|
||||
btn->tap_short_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS;
|
||||
btn->tap_short_cb.pbtn = btn;
|
||||
} else if (type == BUTTON_CB_SERIAL) {
|
||||
iot_button_set_serial_cb(btn_handle, 1, 1000 / portTICK_PERIOD_MS, cb, arg);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_add_on_press_cb(button_handle_t btn_handle, uint32_t press_sec, button_cb cb, void* arg)
|
||||
{
|
||||
POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG);
|
||||
IOT_CHECK(TAG, press_sec != 0, ESP_ERR_INVALID_ARG);
|
||||
button_dev_t* btn = (button_dev_t*) btn_handle;
|
||||
button_cb_t* cb_new = (button_cb_t*) calloc(1, sizeof(button_cb_t));
|
||||
POINT_ASSERT(TAG, cb_new, ESP_FAIL);
|
||||
cb_new->on_press = 1;
|
||||
cb_new->arg = arg;
|
||||
cb_new->cb = cb;
|
||||
cb_new->interval = press_sec * 1000 / portTICK_PERIOD_MS;
|
||||
cb_new->pbtn = btn;
|
||||
cb_new->tmr = xTimerCreate("btn_press_tmr", cb_new->interval, pdFALSE, cb_new, button_press_cb);
|
||||
cb_new->next_cb = btn->cb_head;
|
||||
btn->cb_head = cb_new;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_add_on_release_cb(button_handle_t btn_handle, uint32_t press_sec, button_cb cb, void* arg)
|
||||
{
|
||||
POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG);
|
||||
IOT_CHECK(TAG, press_sec != 0, ESP_ERR_INVALID_ARG);
|
||||
button_dev_t* btn = (button_dev_t*) btn_handle;
|
||||
button_cb_t* cb_new = (button_cb_t*) calloc(1, sizeof(button_cb_t));
|
||||
POINT_ASSERT(TAG, cb_new, ESP_FAIL);
|
||||
btn->taskq_on = 1;
|
||||
cb_new->arg = arg;
|
||||
cb_new->cb = cb;
|
||||
cb_new->interval = press_sec * 1000 / portTICK_PERIOD_MS;
|
||||
cb_new->pbtn = btn;
|
||||
cb_new->tmr = xTimerCreate("btn_press_tmr", cb_new->interval, pdFALSE, cb_new, button_press_cb);
|
||||
cb_new->next_cb = btn->cb_head;
|
||||
btn->cb_head = cb_new;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
54
examples/common/gpio_button/button/button_obj.cpp
Normal file
54
examples/common/gpio_button/button/button_obj.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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 <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_system.h>
|
||||
#include <iot_button.h>
|
||||
|
||||
CButton::CButton(gpio_num_t gpio_num, button_active_t active_level)
|
||||
{
|
||||
m_btn_handle = iot_button_create(gpio_num, active_level);
|
||||
}
|
||||
|
||||
CButton::~CButton()
|
||||
{
|
||||
iot_button_delete(m_btn_handle);
|
||||
m_btn_handle = NULL;
|
||||
}
|
||||
|
||||
esp_err_t CButton::set_evt_cb(button_cb_type_t type, button_cb cb, void* arg)
|
||||
{
|
||||
return iot_button_set_evt_cb(m_btn_handle, type, cb, arg);
|
||||
}
|
||||
|
||||
esp_err_t CButton::set_serial_cb(button_cb cb, void* arg, TickType_t interval_tick, uint32_t start_after_sec)
|
||||
{
|
||||
return iot_button_set_serial_cb(m_btn_handle, start_after_sec, interval_tick, cb, arg);
|
||||
}
|
||||
|
||||
esp_err_t CButton::add_on_press_cb(uint32_t press_sec, button_cb cb, void* arg)
|
||||
{
|
||||
return iot_button_add_on_press_cb(m_btn_handle, press_sec, cb, arg);
|
||||
}
|
||||
|
||||
esp_err_t CButton::add_on_release_cb(uint32_t press_sec, button_cb cb, void* arg)
|
||||
{
|
||||
return iot_button_add_on_release_cb(m_btn_handle, press_sec, cb, arg);
|
||||
}
|
||||
|
||||
esp_err_t CButton::rm_cb(button_cb_type_t type)
|
||||
{
|
||||
return iot_button_rm_cb(m_btn_handle, type);
|
||||
}
|
||||
272
examples/common/gpio_button/button/include/iot_button.h
Normal file
272
examples/common/gpio_button/button/include/iot_button.h
Normal file
@@ -0,0 +1,272 @@
|
||||
// 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.
|
||||
|
||||
|
||||
#ifndef _IOT_BUTTON_H_
|
||||
#define _IOT_BUTTON_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/portmacro.h>
|
||||
typedef void (* button_cb)(void*);
|
||||
typedef void* button_handle_t;
|
||||
|
||||
typedef enum {
|
||||
BUTTON_ACTIVE_HIGH = 1, /*!<button active level: high level*/
|
||||
BUTTON_ACTIVE_LOW = 0, /*!<button active level: low level*/
|
||||
} button_active_t;
|
||||
|
||||
typedef enum {
|
||||
BUTTON_CB_PUSH = 0, /*!<button push callback event */
|
||||
BUTTON_CB_RELEASE, /*!<button release callback event */
|
||||
BUTTON_CB_TAP, /*!<button quick tap callback event(will not trigger if there already is a "PRESS" event) */
|
||||
BUTTON_CB_SERIAL, /*!<button serial trigger callback event */
|
||||
} button_cb_type_t;
|
||||
|
||||
/**
|
||||
* @brief Init button functions
|
||||
*
|
||||
* @param gpio_num GPIO index of the pin that the button uses
|
||||
* @param active_level button hardware active level.
|
||||
* For "BUTTON_ACTIVE_LOW" it means when the button pressed, the GPIO will read low level.
|
||||
*
|
||||
* @return A button_handle_t handle to the created button object, or NULL in case of error.
|
||||
*/
|
||||
button_handle_t iot_button_create(gpio_num_t gpio_num, button_active_t active_level);
|
||||
|
||||
/**
|
||||
* @brief Register a callback function for a serial trigger event.
|
||||
*
|
||||
* @param btn_handle handle of the button object
|
||||
* @param start_after_sec define the time after which to start serial trigger action
|
||||
* @param interval_tick serial trigger interval
|
||||
* @param cb callback function for "TAP" action.
|
||||
* @param arg Parameter for callback function
|
||||
* @note
|
||||
* Button callback functions execute in the context of the timer service task.
|
||||
* It is therefore essential that button callback functions never attempt to block.
|
||||
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
|
||||
* or specify a non zero block time when accessing a queue or a semaphore.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t iot_button_set_serial_cb(button_handle_t btn_handle, uint32_t start_after_sec, TickType_t interval_tick, button_cb cb, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Register a callback function for a button_cb_type_t action.
|
||||
*
|
||||
* @param btn_handle handle of the button object
|
||||
* @param type callback function type
|
||||
* @param cb callback function for "TAP" action.
|
||||
* @param arg Parameter for callback function
|
||||
* @note
|
||||
* Button callback functions execute in the context of the timer service task.
|
||||
* It is therefore essential that button callback functions never attempt to block.
|
||||
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
|
||||
* or specify a non zero block time when accessing a queue or a semaphore.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t iot_button_set_evt_cb(button_handle_t btn_handle, button_cb_type_t type, button_cb cb, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Callbacks invoked as timer events occur while button is pressed.
|
||||
* Example: If a button is configured for 2 sec, 5 sec and 7 sec callbacks and if the button is pressed for 6 sec then 2 sec callback would be invoked at 2 sec event and 5 sec callback would be invoked at 5 sec event
|
||||
*
|
||||
* @param btn_handle handle of the button object
|
||||
* @param press_sec the callback function would be called if you press the button for a specified period of time
|
||||
* @param cb callback function for "PRESS and HOLD" action.
|
||||
* @param arg Parameter for callback function
|
||||
*
|
||||
* @note
|
||||
* Button callback functions execute in the context of the timer service task.
|
||||
* It is therefore essential that button callback functions never attempt to block.
|
||||
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
|
||||
* or specify a non zero block time when accessing a queue or a semaphore.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t iot_button_add_on_press_cb(button_handle_t btn_handle, uint32_t press_sec, button_cb cb, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Single callback invoked according to the latest timer event on button release.
|
||||
* Example: If a button is configured for 2 sec, 5 sec and 7 sec callbacks and if the button is released at 6 sec then only 5 sec callback would be invoked
|
||||
*
|
||||
* @param btn_handle handle of the button object
|
||||
* @param press_sec the callback function would be called if you press the button for a specified period of time
|
||||
* @param cb callback function for "PRESS and RELEASE" action.
|
||||
* @param arg Parameter for callback function
|
||||
*
|
||||
* @note
|
||||
* Button callback functions execute in the context of the timer service task.
|
||||
* It is therefore essential that button callback functions never attempt to block.
|
||||
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
|
||||
* or specify a non zero block time when accessing a queue or a semaphore.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t iot_button_add_on_release_cb(button_handle_t btn_handle, uint32_t press_sec, button_cb cb, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Delete button object and free memory
|
||||
* @param btn_handle handle of the button object
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t iot_button_delete(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Remove callback
|
||||
*
|
||||
* @param btn_handle The handle of the button object
|
||||
* @param type callback function event type
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t iot_button_rm_cb(button_handle_t btn_handle, button_cb_type_t type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/**
|
||||
* class of button
|
||||
* simple usage:
|
||||
* CButton* btn = new CButton(BUTTON_IO_NUM, BUTTON_ACTIVE_LEVEL, BUTTON_SERIAL_TRIGGER, 3);
|
||||
* btn->add_cb(BUTTON_CB_PUSH, button_tap_cb, (void*) push, 50 / portTICK_PERIOD_MS);
|
||||
* btn->add_custom_cb(5, button_press_5s_cb, NULL);
|
||||
* ......
|
||||
* delete btn;
|
||||
*/
|
||||
class CButton
|
||||
{
|
||||
private:
|
||||
button_handle_t m_btn_handle;
|
||||
|
||||
/**
|
||||
* prevent copy constructing
|
||||
*/
|
||||
CButton(const CButton&);
|
||||
CButton& operator = (const CButton&);
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief constructor of CButton
|
||||
*
|
||||
* @param gpio_num GPIO index of the pin that the button uses
|
||||
* @param active_level button hardware active level.
|
||||
* For "BUTTON_ACTIVE_LOW" it means when the button pressed, the GPIO will read low level.
|
||||
*/
|
||||
CButton(gpio_num_t gpio_num, button_active_t active_level = BUTTON_ACTIVE_LOW);
|
||||
|
||||
~CButton();
|
||||
|
||||
/**
|
||||
* @brief Register a callback function for a button_cb_type_t action.
|
||||
*
|
||||
* @param type callback function type
|
||||
* @param cb callback function for "TAP" action.
|
||||
* @param arg Parameter for callback function
|
||||
* @note
|
||||
* Button callback functions execute in the context of the timer service task.
|
||||
* It is therefore essential that button callback functions never attempt to block.
|
||||
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
|
||||
* or specify a non zero block time when accessing a queue or a semaphore.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t set_evt_cb(button_cb_type_t type, button_cb cb, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Register a callback function for a serial trigger event.
|
||||
*
|
||||
* @param btn_handle handle of the button object
|
||||
* @param start_after_sec define the time after which to start serial trigger action
|
||||
* @param interval_tick serial trigger interval
|
||||
* @param cb callback function for "TAP" action.
|
||||
* @param arg Parameter for callback function
|
||||
* @note
|
||||
* Button callback functions execute in the context of the timer service task.
|
||||
* It is therefore essential that button callback functions never attempt to block.
|
||||
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
|
||||
* or specify a non zero block time when accessing a queue or a semaphore.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t set_serial_cb(button_cb cb, void* arg, TickType_t interval_tick, uint32_t start_after_sec);
|
||||
|
||||
/**
|
||||
* @brief Callbacks invoked as timer events occur while button is pressed
|
||||
*
|
||||
* @param press_sec the callback function would be called if you press the button for a specified period of time
|
||||
* @param cb callback function for "PRESS and HOLD" action.
|
||||
* @param arg Parameter for callback function
|
||||
*
|
||||
* @note
|
||||
* Button callback functions execute in the context of the timer service task.
|
||||
* It is therefore essential that button callback functions never attempt to block.
|
||||
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
|
||||
* or specify a non zero block time when accessing a queue or a semaphore.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t add_on_press_cb(uint32_t press_sec, button_cb cb, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Single callback invoked according to the latest timer event on button release.
|
||||
*
|
||||
* @param press_sec the callback function would be called if you press the button for a specified period of time
|
||||
* @param cb callback function for "PRESS and RELEASE" action.
|
||||
* @param arg Parameter for callback function
|
||||
*
|
||||
* @note
|
||||
* Button callback functions execute in the context of the timer service task.
|
||||
* It is therefore essential that button callback functions never attempt to block.
|
||||
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
|
||||
* or specify a non zero block time when accessing a queue or a semaphore.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t add_on_release_cb(uint32_t press_sec, button_cb cb, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Remove callback
|
||||
*
|
||||
* @param type callback function event type
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t rm_cb(button_cb_type_t type);
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
2
examples/common/gpio_button/component.mk
Normal file
2
examples/common/gpio_button/component.mk
Normal file
@@ -0,0 +1,2 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := ./button/include
|
||||
COMPONENT_SRCDIRS := ./button
|
||||
5
examples/common/ledc_driver/CMakeLists.txt
Normal file
5
examples/common/ledc_driver/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
set(srcs "ledc_driver.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES "driver")
|
||||
2
examples/common/ledc_driver/component.mk
Normal file
2
examples/common/ledc_driver/component.mk
Normal file
@@ -0,0 +1,2 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
COMPONENT_SRCDIRS := .
|
||||
179
examples/common/ledc_driver/ledc_driver.c
Normal file
179
examples/common/ledc_driver/ledc_driver.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <driver/ledc.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
#include "ledc_driver.h"
|
||||
|
||||
/**
|
||||
* @brief LEDC driver: Basic LEDC driver
|
||||
*/
|
||||
|
||||
#define IS_ACTIVE_HIGH 0
|
||||
#define LEDC_LS_TIMER LEDC_TIMER_0
|
||||
#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE
|
||||
|
||||
#define LEDC_LS_CH0_GPIO (0)
|
||||
#define LEDC_LS_CH0_CHANNEL LEDC_CHANNEL_0
|
||||
#define LEDC_LS_CH1_GPIO (1)
|
||||
#define LEDC_LS_CH1_CHANNEL LEDC_CHANNEL_1
|
||||
#define LEDC_LS_CH2_GPIO (8)
|
||||
#define LEDC_LS_CH2_CHANNEL LEDC_CHANNEL_2
|
||||
|
||||
#define LEDC_NUM_CHANNELS (3)
|
||||
#define LEDC_DUTY_RES LEDC_TIMER_13_BIT // Set duty resolution to 13 bits
|
||||
#define LEDC_DUTY_MAX (8192 - 1) // (2 ** 13) - 1
|
||||
#define LEDC_FREQUENCY (5000) // Frequency in Hertz. Set frequency at 5 kHz
|
||||
|
||||
/*
|
||||
* Prepare individual configuration
|
||||
* for each channel of LED Controller
|
||||
* by selecting:
|
||||
* - controller's channel number
|
||||
* - output duty cycle, set initially to 0
|
||||
* - GPIO number where LED is connected to
|
||||
* - speed mode, either high or low
|
||||
* - timer servicing selected channel
|
||||
* Note: if different channels use one timer,
|
||||
* then frequency and bit_num of these channels
|
||||
* will be the same
|
||||
*/
|
||||
static ledc_channel_config_t ledc_channel[LEDC_NUM_CHANNELS] = {
|
||||
{
|
||||
.channel = LEDC_LS_CH0_CHANNEL,
|
||||
.duty = 0,
|
||||
.gpio_num = LEDC_LS_CH0_GPIO,
|
||||
.speed_mode = LEDC_LS_MODE,
|
||||
.hpoint = 0,
|
||||
.timer_sel = LEDC_LS_TIMER,
|
||||
},
|
||||
{
|
||||
.channel = LEDC_LS_CH1_CHANNEL,
|
||||
.duty = 0,
|
||||
.gpio_num = LEDC_LS_CH1_GPIO,
|
||||
.speed_mode = LEDC_LS_MODE,
|
||||
.hpoint = 0,
|
||||
.timer_sel = LEDC_LS_TIMER, },
|
||||
{
|
||||
.channel = LEDC_LS_CH2_CHANNEL,
|
||||
.duty = 0,
|
||||
.gpio_num = LEDC_LS_CH2_GPIO,
|
||||
.speed_mode = LEDC_LS_MODE,
|
||||
.hpoint = 0,
|
||||
.timer_sel = LEDC_LS_TIMER,
|
||||
},
|
||||
};
|
||||
|
||||
esp_err_t ledc_init(void)
|
||||
{
|
||||
/*
|
||||
* Prepare and set configuration of timers
|
||||
* that will be used by LED Controller
|
||||
*/
|
||||
ledc_timer_config_t ledc_timer = {
|
||||
.duty_resolution = LEDC_DUTY_RES, // resolution of PWM duty
|
||||
.freq_hz = LEDC_FREQUENCY, // frequency of PWM signal
|
||||
.speed_mode = LEDC_LS_MODE, // timer mode
|
||||
.timer_num = LEDC_LS_TIMER, // timer index
|
||||
.clk_cfg = LEDC_AUTO_CLK, // Auto select the source clock
|
||||
};
|
||||
// Set configuration of timer0 for high speed channels
|
||||
ledc_timer_config(&ledc_timer);
|
||||
|
||||
// Set LED Controller with previously prepared configuration
|
||||
for (int ch = 0; ch < LEDC_NUM_CHANNELS; ch++) {
|
||||
ledc_channel_config(&ledc_channel[ch]);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void ledc_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint32_t *r, uint32_t *g, uint32_t *b)
|
||||
{
|
||||
h %= 360; // h -> [0,360]
|
||||
uint32_t rgb_max = v * 2.55f;
|
||||
uint32_t rgb_min = rgb_max * (100 - s) / 100.0f;
|
||||
|
||||
uint32_t i = h / 60;
|
||||
uint32_t diff = h % 60;
|
||||
|
||||
// RGB adjustment amount by hue
|
||||
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
*r = rgb_max;
|
||||
*g = rgb_min + rgb_adj;
|
||||
*b = rgb_min;
|
||||
break;
|
||||
case 1:
|
||||
*r = rgb_max - rgb_adj;
|
||||
*g = rgb_max;
|
||||
*b = rgb_min;
|
||||
break;
|
||||
case 2:
|
||||
*r = rgb_min;
|
||||
*g = rgb_max;
|
||||
*b = rgb_min + rgb_adj;
|
||||
break;
|
||||
case 3:
|
||||
*r = rgb_min;
|
||||
*g = rgb_max - rgb_adj;
|
||||
*b = rgb_max;
|
||||
break;
|
||||
case 4:
|
||||
*r = rgb_min + rgb_adj;
|
||||
*g = rgb_min;
|
||||
*b = rgb_max;
|
||||
break;
|
||||
default:
|
||||
*r = rgb_max;
|
||||
*g = rgb_min;
|
||||
*b = rgb_max - rgb_adj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t ledc_set_rgb(uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
red = red * LEDC_DUTY_MAX / 255;
|
||||
green = green * LEDC_DUTY_MAX / 255;
|
||||
blue = blue * LEDC_DUTY_MAX / 255;
|
||||
|
||||
if (!IS_ACTIVE_HIGH) {
|
||||
red = LEDC_DUTY_MAX - red;
|
||||
green = LEDC_DUTY_MAX - green;
|
||||
blue = LEDC_DUTY_MAX - blue;
|
||||
}
|
||||
|
||||
ledc_set_duty(ledc_channel[0].speed_mode, ledc_channel[0].channel, red);
|
||||
ledc_update_duty(ledc_channel[0].speed_mode, ledc_channel[0].channel);
|
||||
|
||||
ledc_set_duty(ledc_channel[1].speed_mode, ledc_channel[1].channel, green);
|
||||
ledc_update_duty(ledc_channel[1].speed_mode, ledc_channel[1].channel);
|
||||
|
||||
ledc_set_duty(ledc_channel[2].speed_mode, ledc_channel[2].channel, blue);
|
||||
ledc_update_duty(ledc_channel[2].speed_mode, ledc_channel[2].channel);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ledc_set_hsv(uint32_t hue, uint32_t saturation, uint32_t value)
|
||||
{
|
||||
uint32_t red = 0;
|
||||
uint32_t green = 0;
|
||||
uint32_t blue = 0;
|
||||
ledc_hsv2rgb(hue, saturation, value, &red, &green, &blue);
|
||||
return ledc_set_rgb(red, green, blue);
|
||||
}
|
||||
|
||||
esp_err_t ledc_clear()
|
||||
{
|
||||
return ledc_set_rgb(0, 0, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
49
examples/common/ledc_driver/ledc_driver.h
Normal file
49
examples/common/ledc_driver/ledc_driver.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize the LEDC RGB LED
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t ledc_init(void);
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Set RGB value for the LED
|
||||
*
|
||||
* @param[in] red Intensity of Red color (0-100)
|
||||
* @param[in] green Intensity of Green color (0-100)
|
||||
* @param[in] blue Intensity of Green color (0-100)
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t ledc_set_rgb(uint32_t red, uint32_t green, uint32_t blue);
|
||||
|
||||
/**
|
||||
* @brief Set HSV value for the LED
|
||||
*
|
||||
* @param[in] hue Value of hue in arc degrees (0-360)
|
||||
* @param[in] saturation Saturation in percentage (0-100)
|
||||
* @param[in] value Value (also called Intensity) in percentage (0-100)
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t ledc_set_hsv(uint32_t hue, uint32_t saturation, uint32_t value);
|
||||
|
||||
/**
|
||||
* @brief Clear (turn off) the LED
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t ledc_clear();
|
||||
9
examples/common/ws2812_led/CMakeLists.txt
Normal file
9
examples/common/ws2812_led/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
if(CONFIG_WS2812_LED_ENABLE)
|
||||
set(srcs "ws2812_led.c" "led_strip_rmt_ws2812.c")
|
||||
else()
|
||||
set(srcs "ws2812_led.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES "driver")
|
||||
19
examples/common/ws2812_led/Kconfig
Normal file
19
examples/common/ws2812_led/Kconfig
Normal file
@@ -0,0 +1,19 @@
|
||||
menu "WS2812 RGB LED"
|
||||
|
||||
config WS2812_LED_ENABLE
|
||||
bool "Enable RGB LED"
|
||||
default n if IDF_TARGET_ESP32 || !SOC_RMT_SUPPORTED
|
||||
default y
|
||||
help
|
||||
Disable the WS2812 RGB LED.
|
||||
|
||||
config WS2812_LED_GPIO
|
||||
int "WS2812 LED GPIO"
|
||||
default 8 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32H2
|
||||
default 48 if IDF_TARGET_ESP32S3
|
||||
default 18
|
||||
depends on WS2812_LED_ENABLE
|
||||
help
|
||||
Set the WS2812 RGB LED GPIO.
|
||||
|
||||
endmenu
|
||||
5
examples/common/ws2812_led/component.mk
Normal file
5
examples/common/ws2812_led/component.mk
Normal file
@@ -0,0 +1,5 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
COMPONENT_SRCDIRS := .
|
||||
ifndef CONFIG_WS2812_LED_ENABLE
|
||||
COMPONENT_OBJEXCLUDE += led_strip_rmt_ws2812.o
|
||||
endif
|
||||
126
examples/common/ws2812_led/led_strip.h
Normal file
126
examples/common/ws2812_led/led_strip.h
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* @brief LED Strip Type
|
||||
*
|
||||
*/
|
||||
typedef struct led_strip_s led_strip_t;
|
||||
|
||||
/**
|
||||
* @brief LED Strip Device Type
|
||||
*
|
||||
*/
|
||||
typedef void *led_strip_dev_t;
|
||||
|
||||
/**
|
||||
* @brief Declare of LED Strip Type
|
||||
*
|
||||
*/
|
||||
struct led_strip_s {
|
||||
/**
|
||||
* @brief Set RGB for a specific pixel
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGB for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
|
||||
|
||||
/**
|
||||
* @brief Refresh memory colors to LEDs
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param timeout_ms: timeout value for refreshing task
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Refresh successfully
|
||||
* - ESP_ERR_TIMEOUT: Refresh failed because of timeout
|
||||
* - ESP_FAIL: Refresh failed because some other error occurred
|
||||
*
|
||||
* @note:
|
||||
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||
*/
|
||||
esp_err_t (*refresh)(led_strip_t *strip, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Clear LED strip (turn off all LEDs)
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param timeout_ms: timeout value for clearing task
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Clear LEDs successfully
|
||||
* - ESP_ERR_TIMEOUT: Clear LEDs failed because of timeout
|
||||
* - ESP_FAIL: Clear LEDs failed because some other error occurred
|
||||
*/
|
||||
esp_err_t (*clear)(led_strip_t *strip, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Free LED strip resources
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Free resources successfully
|
||||
* - ESP_FAIL: Free resources failed because error occurred
|
||||
*/
|
||||
esp_err_t (*del)(led_strip_t *strip);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief LED Strip Configuration Type
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t max_leds; /*!< Maximum LEDs in a single strip */
|
||||
led_strip_dev_t dev; /*!< LED strip device (e.g. RMT channel, PWM channel, etc) */
|
||||
} led_strip_config_t;
|
||||
|
||||
/**
|
||||
* @brief Default configuration for LED strip
|
||||
*
|
||||
*/
|
||||
#define LED_STRIP_DEFAULT_CONFIG(number, dev_hdl) \
|
||||
{ \
|
||||
.max_leds = number, \
|
||||
.dev = dev_hdl, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Install a new ws2812 driver (based on RMT peripheral)
|
||||
*
|
||||
* @param config: LED strip configuration
|
||||
* @return
|
||||
* LED strip instance or NULL
|
||||
*/
|
||||
led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
171
examples/common/ws2812_led/led_strip_rmt_ws2812.c
Normal file
171
examples/common/ws2812_led/led_strip_rmt_ws2812.c
Normal file
@@ -0,0 +1,171 @@
|
||||
// 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 <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_attr.h"
|
||||
#include "led_strip.h"
|
||||
#include "driver/rmt.h"
|
||||
|
||||
static const char *TAG = "ws2812";
|
||||
#define STRIP_CHECK(a, str, goto_tag, ret_value, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(a)) \
|
||||
{ \
|
||||
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
ret = ret_value; \
|
||||
goto goto_tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define WS2812_T0H_NS (350)
|
||||
#define WS2812_T0L_NS (1000)
|
||||
#define WS2812_T1H_NS (1000)
|
||||
#define WS2812_T1L_NS (350)
|
||||
#define WS2812_RESET_US (280)
|
||||
|
||||
static uint32_t ws2812_t0h_ticks = 0;
|
||||
static uint32_t ws2812_t1h_ticks = 0;
|
||||
static uint32_t ws2812_t0l_ticks = 0;
|
||||
static uint32_t ws2812_t1l_ticks = 0;
|
||||
|
||||
typedef struct {
|
||||
led_strip_t parent;
|
||||
rmt_channel_t rmt_channel;
|
||||
uint32_t strip_len;
|
||||
uint8_t buffer[0];
|
||||
} ws2812_t;
|
||||
|
||||
/**
|
||||
* @brief Conver RGB data to RMT format.
|
||||
*
|
||||
* @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t)
|
||||
*
|
||||
* @param[in] src: source data, to converted to RMT format
|
||||
* @param[in] dest: place where to store the convert result
|
||||
* @param[in] src_size: size of source data
|
||||
* @param[in] wanted_num: number of RMT items that want to get
|
||||
* @param[out] translated_size: number of source data that got converted
|
||||
* @param[out] item_num: number of RMT items which are converted from source data
|
||||
*/
|
||||
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
|
||||
size_t wanted_num, size_t *translated_size, size_t *item_num)
|
||||
{
|
||||
if (src == NULL || dest == NULL) {
|
||||
*translated_size = 0;
|
||||
*item_num = 0;
|
||||
return;
|
||||
}
|
||||
const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0
|
||||
const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1
|
||||
size_t size = 0;
|
||||
size_t num = 0;
|
||||
uint8_t *psrc = (uint8_t *)src;
|
||||
rmt_item32_t *pdest = dest;
|
||||
while (size < src_size && num < wanted_num) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
// MSB first
|
||||
if (*psrc & (1 << (7 - i))) {
|
||||
pdest->val = bit1.val;
|
||||
} else {
|
||||
pdest->val = bit0.val;
|
||||
}
|
||||
num++;
|
||||
pdest++;
|
||||
}
|
||||
size++;
|
||||
psrc++;
|
||||
}
|
||||
*translated_size = size;
|
||||
*item_num = num;
|
||||
}
|
||||
|
||||
static esp_err_t ws2812_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
|
||||
STRIP_CHECK(index < ws2812->strip_len, "index out of the maximum number of leds", err, ESP_ERR_INVALID_ARG);
|
||||
uint32_t start = index * 3;
|
||||
// In thr order of GRB
|
||||
ws2812->buffer[start + 0] = green & 0xFF;
|
||||
ws2812->buffer[start + 1] = red & 0xFF;
|
||||
ws2812->buffer[start + 2] = blue & 0xFF;
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t ws2812_refresh(led_strip_t *strip, uint32_t timeout_ms)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
|
||||
STRIP_CHECK(rmt_write_sample(ws2812->rmt_channel, ws2812->buffer, ws2812->strip_len * 3, true) == ESP_OK,
|
||||
"transmit RMT samples failed", err, ESP_FAIL);
|
||||
return rmt_wait_tx_done(ws2812->rmt_channel, pdMS_TO_TICKS(timeout_ms));
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t ws2812_clear(led_strip_t *strip, uint32_t timeout_ms)
|
||||
{
|
||||
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
|
||||
// Write zero to turn off all leds
|
||||
memset(ws2812->buffer, 0, ws2812->strip_len * 3);
|
||||
return ws2812_refresh(strip, timeout_ms);
|
||||
}
|
||||
|
||||
static esp_err_t ws2812_del(led_strip_t *strip)
|
||||
{
|
||||
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
|
||||
free(ws2812);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config)
|
||||
{
|
||||
led_strip_t *ret = NULL;
|
||||
STRIP_CHECK(config, "configuration can't be null", err, NULL);
|
||||
|
||||
// 24 bits per led
|
||||
uint32_t ws2812_size = sizeof(ws2812_t) + config->max_leds * 3;
|
||||
ws2812_t *ws2812 = calloc(1, ws2812_size);
|
||||
STRIP_CHECK(ws2812, "request memory for ws2812 failed", err, NULL);
|
||||
|
||||
uint32_t counter_clk_hz = 0;
|
||||
STRIP_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev, &counter_clk_hz) == ESP_OK,
|
||||
"get rmt counter clock failed", err, NULL);
|
||||
// ns -> ticks
|
||||
float ratio = (float)counter_clk_hz / 1e9;
|
||||
ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
|
||||
ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
|
||||
ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
|
||||
ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
|
||||
|
||||
// set ws2812 to rmt adapter
|
||||
rmt_translator_init((rmt_channel_t)config->dev, ws2812_rmt_adapter);
|
||||
|
||||
ws2812->rmt_channel = (rmt_channel_t)config->dev;
|
||||
ws2812->strip_len = config->max_leds;
|
||||
|
||||
ws2812->parent.set_pixel = ws2812_set_pixel;
|
||||
ws2812->parent.refresh = ws2812_refresh;
|
||||
ws2812->parent.clear = ws2812_clear;
|
||||
ws2812->parent.del = ws2812_del;
|
||||
|
||||
return &ws2812->parent;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
159
examples/common/ws2812_led/ws2812_led.c
Normal file
159
examples/common/ws2812_led/ws2812_led.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/* WS2812 RGB LED helper functions
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/* It is recommended to copy this code in your example so that you can modify as
|
||||
* per your application's needs.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char *TAG = "ws2812_led";
|
||||
|
||||
#ifdef CONFIG_WS2812_LED_ENABLE
|
||||
#include <driver/rmt.h>
|
||||
#include "led_strip.h"
|
||||
#define RMT_TX_CHANNEL RMT_CHANNEL_0
|
||||
|
||||
static led_strip_t *g_strip;
|
||||
|
||||
/**
|
||||
* @brief Simple helper function, converting HSV color space to RGB color space
|
||||
*
|
||||
* Wiki: https://en.wikipedia.org/wiki/HSL_and_HSV
|
||||
*
|
||||
*/
|
||||
static void ws2812_led_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint32_t *r, uint32_t *g, uint32_t *b)
|
||||
{
|
||||
h %= 360; // h -> [0,360]
|
||||
uint32_t rgb_max = v * 2.55f;
|
||||
uint32_t rgb_min = rgb_max * (100 - s) / 100.0f;
|
||||
|
||||
uint32_t i = h / 60;
|
||||
uint32_t diff = h % 60;
|
||||
|
||||
// RGB adjustment amount by hue
|
||||
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
*r = rgb_max;
|
||||
*g = rgb_min + rgb_adj;
|
||||
*b = rgb_min;
|
||||
break;
|
||||
case 1:
|
||||
*r = rgb_max - rgb_adj;
|
||||
*g = rgb_max;
|
||||
*b = rgb_min;
|
||||
break;
|
||||
case 2:
|
||||
*r = rgb_min;
|
||||
*g = rgb_max;
|
||||
*b = rgb_min + rgb_adj;
|
||||
break;
|
||||
case 3:
|
||||
*r = rgb_min;
|
||||
*g = rgb_max - rgb_adj;
|
||||
*b = rgb_max;
|
||||
break;
|
||||
case 4:
|
||||
*r = rgb_min + rgb_adj;
|
||||
*g = rgb_min;
|
||||
*b = rgb_max;
|
||||
break;
|
||||
default:
|
||||
*r = rgb_max;
|
||||
*g = rgb_min;
|
||||
*b = rgb_max - rgb_adj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t ws2812_led_set_rgb(uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
if (!g_strip) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
g_strip->set_pixel(g_strip, 0, red, green, blue);
|
||||
g_strip->refresh(g_strip, 100);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ws2812_led_set_hsv(uint32_t hue, uint32_t saturation, uint32_t value)
|
||||
{
|
||||
if (!g_strip) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
uint32_t red = 0;
|
||||
uint32_t green = 0;
|
||||
uint32_t blue = 0;
|
||||
ws2812_led_hsv2rgb(hue, saturation, value, &red, &green, &blue);
|
||||
return ws2812_led_set_rgb(red, green, blue);
|
||||
}
|
||||
|
||||
esp_err_t ws2812_led_clear(void)
|
||||
{
|
||||
if (!g_strip) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
g_strip->clear(g_strip, 100);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ws2812_led_init(void)
|
||||
{
|
||||
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(CONFIG_WS2812_LED_GPIO, RMT_TX_CHANNEL);
|
||||
// set counter clock to 40MHz
|
||||
config.clk_div = 2;
|
||||
|
||||
if (rmt_config(&config) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "RMT Config failed.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (rmt_driver_install(config.channel, 0, 0) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "RMT Driver install failed.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// install ws2812 driver
|
||||
led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(1, (led_strip_dev_t)config.channel);
|
||||
g_strip = led_strip_new_rmt_ws2812(&strip_config);
|
||||
if (!g_strip) {
|
||||
ESP_LOGE(TAG, "Install WS2812 driver failed.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
#else /* !CONFIG_WS2812_LED_ENABLE */
|
||||
esp_err_t ws2812_led_set_rgb(uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
/* Empty function, since WS2812 RGB LED has been disabled. */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ws2812_led_set_hsv(uint32_t hue, uint32_t saturation, uint32_t value)
|
||||
{
|
||||
/* Empty function, since WS2812 RGB LED has been disabled. */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ws2812_led_clear(void)
|
||||
{
|
||||
/* Empty function, since WS2812 RGB LED has been disabled. */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ws2812_led_init(void)
|
||||
{
|
||||
ESP_LOGW(TAG, "WS2812 LED is disabled");
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif /* !CONFIG_WS2812_LED_ENABLE */
|
||||
44
examples/common/ws2812_led/ws2812_led.h
Normal file
44
examples/common/ws2812_led/ws2812_led.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
#include <esp_err.h>
|
||||
|
||||
/** Initialize the WS2812 RGB LED
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t ws2812_led_init(void);
|
||||
|
||||
/** Set RGB value for the WS2812 LED
|
||||
*
|
||||
* @param[in] red Intensity of Red color (0-100)
|
||||
* @param[in] green Intensity of Green color (0-100)
|
||||
* @param[in] blue Intensity of Green color (0-100)
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t ws2812_led_set_rgb(uint32_t red, uint32_t green, uint32_t blue);
|
||||
|
||||
/** Set HSV value for the WS2812 LED
|
||||
*
|
||||
* @param[in] hue Value of hue in arc degrees (0-360)
|
||||
* @param[in] saturation Saturation in percentage (0-100)
|
||||
* @param[in] value Value (also called Intensity) in percentage (0-100)
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t ws2812_led_set_hsv(uint32_t hue, uint32_t saturation, uint32_t value);
|
||||
|
||||
/** Clear (turn off) the WS2812 LED
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t ws2812_led_clear(void);
|
||||
Reference in New Issue
Block a user