Skip to content

Commit e1dacc2

Browse files
committed
homekit_switch: Add provisioning timeout support similar to rest of the examples
1 parent baf1dea commit e1dacc2

File tree

4 files changed

+194
-34
lines changed

4 files changed

+194
-34
lines changed

examples/homekit_switch/components/app_wifi/Kconfig.projbuild

+33
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,37 @@ menu "App Wi-Fi Provisioning"
3232
help
3333
"Use Apple WAC Provisioning"
3434

35+
config APP_WIFI_RESET_PROV_ON_FAILURE
36+
bool
37+
default y
38+
prompt "Reset provisioned credentials and state machine after session failure"
39+
help
40+
Enable reseting provisioned credentials and state machine after session failure.
41+
This will restart the provisioning service after retries are exhausted.
42+
43+
config APP_WIFI_PROV_MAX_RETRY_CNT
44+
int
45+
default 5
46+
prompt "Max retries before reseting provisioning state machine"
47+
depends on APP_WIFI_RESET_PROV_ON_FAILURE
48+
help
49+
Set the Maximum retry to avoid reconnecting to an inexistent AP or if credentials
50+
are misconfigured. Provisioned credentials are erased and internal state machine
51+
is reset after this threshold is reached.
52+
53+
config APP_WIFI_PROV_TIMEOUT_PERIOD
54+
int "Provisioning Timeout"
55+
default 10
56+
help
57+
Timeout (in minutes) after which the provisioning will auto stop. A reboot will be required
58+
to restart provisioning. Set to 0 if you do not want provisioning to auto stop.
59+
It is recommended to set this to 15 or 10 depending on the HomeKit spec version supported,
60+
to match the WAC timeout.
61+
62+
config APP_WIFI_PROV_NAME_PREFIX
63+
string "Provisioning Name Prefix"
64+
default "PROV"
65+
help
66+
Provisioning Name Prefix.
67+
3568
endmenu

examples/homekit_switch/components/app_wifi/app_wifi_with_homekit.c

+116-33
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@
3535
#include <qrcode.h>
3636
#include <nvs.h>
3737
#include <nvs_flash.h>
38+
#include <esp_timer.h>
3839
#include "app_wifi_with_homekit.h"
3940

41+
ESP_EVENT_DEFINE_BASE(APP_WIFI_EVENT);
4042
#ifdef CONFIG_APP_WIFI_USE_WAC_PROVISIONING
4143
#include <hap_wac.h>
4244
#endif /* CONFIG_APP_WIFI_USE_WAC_PROVISIONING */
@@ -54,21 +56,35 @@ static EventGroupHandle_t wifi_event_group;
5456
#define CREDENTIALS_NAMESPACE "rmaker_creds"
5557
#define RANDOM_NVS_KEY "random"
5658

59+
#define POP_STR_SIZE 9
60+
static esp_timer_handle_t prov_stop_timer;
61+
/* Timeout period in minutes */
62+
#define APP_WIFI_PROV_TIMEOUT_PERIOD CONFIG_APP_WIFI_PROV_TIMEOUT_PERIOD
63+
/* Autofetch period in micro-seconds */
64+
static uint64_t prov_timeout_period = (APP_WIFI_PROV_TIMEOUT_PERIOD * 60 * 1000000LL);
65+
5766
static void app_wifi_print_qr(const char *name, const char *pop, const char *transport)
5867
{
59-
if (!name || !pop || !transport) {
68+
if (!name || !transport) {
6069
ESP_LOGW(TAG, "Cannot generate QR code payload. Data missing.");
6170
return;
6271
}
6372
char payload[150];
64-
snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \
65-
",\"pop\":\"%s\",\"transport\":\"%s\"}",
66-
PROV_QR_VERSION, name, pop, transport);
73+
if (pop) {
74+
snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \
75+
",\"pop\":\"%s\",\"transport\":\"%s\"}",
76+
PROV_QR_VERSION, name, pop, transport);
77+
} else {
78+
snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \
79+
",\"transport\":\"%s\"}",
80+
PROV_QR_VERSION, name, transport);
81+
}
6782
#ifdef CONFIG_APP_WIFI_PROV_SHOW_QR
6883
ESP_LOGI(TAG, "Scan this QR code from the phone app for Provisioning.");
6984
qrcode_display(payload);
7085
#endif /* CONFIG_APP_WIFI_PROV_SHOW_QR */
7186
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);
87+
esp_event_post(APP_WIFI_EVENT, APP_WIFI_EVENT_QR_DISPLAY, payload, strlen(payload) + 1, portMAX_DELAY);
7288
}
7389

7490
#ifdef CONFIG_APP_WIFI_USE_WAC_PROVISIONING
@@ -97,6 +113,9 @@ static void app_wac_sta_connect(wifi_config_t *wifi_cfg)
97113
static void event_handler(void* arg, esp_event_base_t event_base,
98114
int32_t event_id, void* event_data)
99115
{
116+
#ifdef CONFIG_APP_WIFI_RESET_PROV_ON_FAILURE
117+
static int retries = 0;
118+
#endif
100119
if (event_base == WIFI_PROV_EVENT) {
101120
switch (event_id) {
102121
case WIFI_PROV_START:
@@ -116,12 +135,33 @@ static void event_handler(void* arg, esp_event_base_t event_base,
116135
"\n\tPlease reset to factory and retry provisioning",
117136
(*reason == WIFI_PROV_STA_AUTH_ERROR) ?
118137
"Wi-Fi station authentication failed" : "Wi-Fi access-point not found");
138+
#ifdef CONFIG_APP_WIFI_RESET_PROV_ON_FAILURE
139+
retries++;
140+
if (retries >= CONFIG_APP_WIFI_PROV_MAX_RETRY_CNT) {
141+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 1)
142+
ESP_LOGI(TAG, "Failed to connect with provisioned AP, reseting provisioned credentials");
143+
wifi_prov_mgr_reset_sm_state_on_failure();
144+
esp_event_post(APP_WIFI_EVENT, APP_WIFI_EVENT_PROV_RESTART, NULL, 0, portMAX_DELAY);
145+
#else
146+
ESP_LOGW(TAG, "Failed to connect with provisioned AP, please reset to provisioning manually");
147+
#endif
148+
retries = 0;
149+
}
150+
#endif
119151
break;
120152
}
121153
case WIFI_PROV_CRED_SUCCESS:
122154
ESP_LOGI(TAG, "Provisioning successful");
155+
#ifdef CONFIG_APP_WIFI_RESET_PROV_ON_FAILURE
156+
retries = 0;
157+
#endif
123158
break;
124159
case WIFI_PROV_END:
160+
if (prov_stop_timer) {
161+
esp_timer_stop(prov_stop_timer);
162+
esp_timer_delete(prov_stop_timer);
163+
prov_stop_timer = NULL;
164+
}
125165
#ifdef CONFIG_APP_WIFI_USE_WAC_PROVISIONING
126166
hap_wac_stop();
127167
#endif
@@ -209,14 +249,14 @@ static esp_err_t read_random_bytes_from_nvs(uint8_t **random_bytes, size_t *len)
209249
static esp_err_t get_device_service_name(char *service_name, size_t max)
210250
{
211251
uint8_t *nvs_random = NULL;
212-
const char *ssid_prefix = "PROV_";
252+
const char *ssid_prefix = CONFIG_APP_WIFI_PROV_NAME_PREFIX;
213253
size_t nvs_random_size = 0;
214254
if ((read_random_bytes_from_nvs(&nvs_random, &nvs_random_size) != ESP_OK) || nvs_random_size < 3) {
215255
uint8_t eth_mac[6];
216256
esp_wifi_get_mac(WIFI_IF_STA, eth_mac);
217-
snprintf(service_name, max, "%s%02x%02x%02x", ssid_prefix, eth_mac[3], eth_mac[4], eth_mac[5]);
257+
snprintf(service_name, max, "%s_%02x%02x%02x", ssid_prefix, eth_mac[3], eth_mac[4], eth_mac[5]);
218258
} else {
219-
snprintf(service_name, max, "%s%02x%02x%02x", ssid_prefix, nvs_random[nvs_random_size - 3],
259+
snprintf(service_name, max, "%s_%02x%02x%02x", ssid_prefix, nvs_random[nvs_random_size - 3],
220260
nvs_random[nvs_random_size - 2], nvs_random[nvs_random_size - 1]);
221261
}
222262
if (nvs_random) {
@@ -226,34 +266,45 @@ static esp_err_t get_device_service_name(char *service_name, size_t max)
226266
}
227267

228268

229-
static esp_err_t get_device_pop(char *pop, size_t max, app_wifi_pop_type_t pop_type)
269+
static char *get_device_pop(app_wifi_pop_type_t pop_type)
230270
{
231-
if (!pop || !max) {
232-
return ESP_ERR_INVALID_ARG;
271+
if (pop_type == POP_TYPE_NONE) {
272+
return NULL;
273+
}
274+
char *pop = calloc(1, POP_STR_SIZE);
275+
if (!pop) {
276+
ESP_LOGE(TAG, "Failed to allocate memory for PoP.");
277+
return NULL;
233278
}
234279

235280
if (pop_type == POP_TYPE_MAC) {
236281
uint8_t eth_mac[6];
237282
esp_err_t err = esp_wifi_get_mac(WIFI_IF_STA, eth_mac);
238283
if (err == ESP_OK) {
239-
snprintf(pop, max, "%02x%02x%02x%02x", eth_mac[2], eth_mac[3], eth_mac[4], eth_mac[5]);
240-
return ESP_OK;
284+
snprintf(pop, POP_STR_SIZE, "%02x%02x%02x%02x", eth_mac[2], eth_mac[3], eth_mac[4], eth_mac[5]);
285+
return pop;
241286
} else {
242-
return err;
287+
ESP_LOGE(TAG, "Failed to get MAC address to generate PoP.");
288+
goto pop_err;
243289
}
244290
} else if (pop_type == POP_TYPE_RANDOM) {
245-
uint8_t *nvs_random;
291+
uint8_t *nvs_random = NULL;
246292
size_t nvs_random_size = 0;
247293
if ((read_random_bytes_from_nvs(&nvs_random, &nvs_random_size) != ESP_OK) || nvs_random_size < 4) {
248-
return ESP_ERR_NOT_FOUND;
294+
ESP_LOGE(TAG, "Failed to read random bytes from NVS to generate PoP.");
295+
if (nvs_random) {
296+
free(nvs_random);
297+
}
298+
goto pop_err;
249299
} else {
250-
snprintf(pop, max, "%02x%02x%02x%02x", nvs_random[0], nvs_random[1], nvs_random[2], nvs_random[3]);
300+
snprintf(pop, POP_STR_SIZE, "%02x%02x%02x%02x", nvs_random[0], nvs_random[1], nvs_random[2], nvs_random[3]);
251301
free(nvs_random);
252-
return ESP_OK;
302+
return pop;
253303
}
254-
} else {
255-
return ESP_ERR_INVALID_ARG;
256304
}
305+
pop_err:
306+
free(pop);
307+
return NULL;
257308
}
258309

259310
void app_wifi_with_homekit_init(void)
@@ -288,6 +339,35 @@ void app_wifi_with_homekit_init(void)
288339
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
289340
}
290341

342+
static void app_wifi_prov_stop(void *priv)
343+
{
344+
ESP_LOGW(TAG, "Provisioning timed out. Please reboot device to restart provisioning.");
345+
wifi_prov_mgr_stop_provisioning();
346+
esp_event_post(APP_WIFI_EVENT, APP_WIFI_EVENT_PROV_TIMEOUT, NULL, 0, portMAX_DELAY);
347+
}
348+
349+
esp_err_t app_wifi_start_timer(void)
350+
{
351+
if (prov_timeout_period == 0) {
352+
return ESP_OK;
353+
}
354+
esp_timer_create_args_t prov_stop_timer_conf = {
355+
.callback = app_wifi_prov_stop,
356+
.arg = NULL,
357+
.dispatch_method = ESP_TIMER_TASK,
358+
.name = "app_wifi_prov_stop_tm"
359+
};
360+
if (esp_timer_create(&prov_stop_timer_conf, &prov_stop_timer) == ESP_OK) {
361+
esp_timer_start_once(prov_stop_timer, prov_timeout_period);
362+
ESP_LOGI(TAG, "Provisioning will auto stop after %d minute(s).",
363+
APP_WIFI_PROV_TIMEOUT_PERIOD);
364+
return ESP_OK;
365+
} else {
366+
ESP_LOGE(TAG, "Failed to create Provisioning auto stop timer.");
367+
}
368+
return ESP_FAIL;
369+
}
370+
291371
esp_err_t app_wifi_with_homekit_start(app_wifi_pop_type_t pop_type)
292372
{
293373
/* Configuration for the provisioning manager */
@@ -329,7 +409,6 @@ esp_err_t app_wifi_with_homekit_start(app_wifi_pop_type_t pop_type)
329409
#ifdef ESP_NETIF_SUPPORTED
330410
esp_netif_create_default_wifi_ap();
331411
#endif
332-
333412
/* What is the Device Service Name that we want
334413
* This translates to :
335414
* - Wi-Fi SSID when scheme is wifi_prov_scheme_softap
@@ -338,6 +417,12 @@ esp_err_t app_wifi_with_homekit_start(app_wifi_pop_type_t pop_type)
338417
char service_name[12];
339418
get_device_service_name(service_name, sizeof(service_name));
340419

420+
/* What is the service key (Wi-Fi password)
421+
* NULL = Open network
422+
* This is ignored when scheme is wifi_prov_scheme_ble
423+
*/
424+
const char *service_key = NULL;
425+
341426
/* What is the security level that we want (0 or 1):
342427
* - WIFI_PROV_SECURITY_0 is simply plain text communication.
343428
* - WIFI_PROV_SECURITY_1 is secure communication which consists of secure handshake
@@ -350,19 +435,11 @@ esp_err_t app_wifi_with_homekit_start(app_wifi_pop_type_t pop_type)
350435
* - this should be a string with length > 0
351436
* - NULL if not used
352437
*/
353-
char pop[9];
354-
esp_err_t err = get_device_pop(pop, sizeof(pop), pop_type);
355-
if (err != ESP_OK) {
356-
ESP_LOGE(TAG, "Error: %d. Failed to get PoP from NVS, Please perform Claiming.", err);
357-
return err;
438+
char *pop = get_device_pop(pop_type);
439+
if ((pop_type != POP_TYPE_NONE) && (pop == NULL)) {
440+
return ESP_ERR_NO_MEM;
358441
}
359442

360-
/* What is the service key (Wi-Fi password)
361-
* NULL = Open network
362-
* This is ignored when scheme is wifi_prov_scheme_ble
363-
*/
364-
const char *service_key = NULL;
365-
366443
#ifdef CONFIG_APP_WIFI_PROV_TRANSPORT_BLE
367444
/* This step is only useful when scheme is wifi_prov_scheme_ble. This will
368445
* set a custom 128 bit UUID which will be included in the BLE advertisement
@@ -379,12 +456,14 @@ esp_err_t app_wifi_with_homekit_start(app_wifi_pop_type_t pop_type)
379456
0xb4, 0xdf, 0x5a, 0x1c, 0x3f, 0x6b, 0xf4, 0xbf,
380457
0xea, 0x4a, 0x82, 0x03, 0x04, 0x90, 0x1a, 0x02,
381458
};
382-
err = wifi_prov_scheme_ble_set_service_uuid(custom_service_uuid);
459+
esp_err_t err = wifi_prov_scheme_ble_set_service_uuid(custom_service_uuid);
383460
if (err != ESP_OK) {
384461
ESP_LOGE(TAG, "wifi_prov_scheme_ble_set_service_uuid failed %d", err);
385462
return err;
386463
}
387464
#endif /* CONFIG_APP_WIFI_PROV_TRANSPORT_BLE */
465+
466+
388467
#ifdef CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP
389468
wifi_prov_scheme_softap_set_httpd_handle(hap_platform_httpd_get_handle());
390469
#endif /* CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP */
@@ -397,9 +476,13 @@ esp_err_t app_wifi_with_homekit_start(app_wifi_pop_type_t pop_type)
397476
#else /* CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP */
398477
app_wifi_print_qr(service_name, pop, PROV_TRANSPORT_SOFTAP);
399478
#endif /* CONFIG_APP_WIFI_PROV_TRANSPORT_BLE */
400-
ESP_LOGI(TAG, "Provisioning Started. Name : %s, POP : %s", service_name, pop);
479+
ESP_LOGI(TAG, "Provisioning Started. Name : %s, POP : %s", service_name, pop ? pop : "<null>");
480+
app_wifi_start_timer();
401481
#ifdef CONFIG_APP_WIFI_USE_WAC_PROVISIONING
402482
esp_event_handler_register(HAP_WAC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL);
483+
if (pop) {
484+
free(pop);
485+
}
403486
hap_wac_start();
404487
#endif /* CONFIG_APP_WIFI_USE_WAC_PROVISIONING */
405488
} else {

examples/homekit_switch/components/app_wifi/app_wifi_with_homekit.h

+19-1
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,31 @@
77
*/
88
#pragma once
99
#include <esp_err.h>
10+
#include <esp_event.h>
11+
12+
/** ESP RainMaker Event Base */
13+
ESP_EVENT_DECLARE_BASE(APP_WIFI_EVENT);
14+
15+
/** App Wi-Fir Events */
16+
typedef enum {
17+
/** QR code available for display. Associated data is the NULL terminated QR payload. */
18+
APP_WIFI_EVENT_QR_DISPLAY = 1,
19+
/** Provisioning timed out */
20+
APP_WIFI_EVENT_PROV_TIMEOUT,
21+
/** Provisioning has restarted due to failures (Invalid SSID/Passphrase) */
22+
APP_WIFI_EVENT_PROV_RESTART,
23+
} app_wifi_event_t;
1024

1125
/** Types of Proof of Possession */
1226
typedef enum {
1327
/** Use MAC address to generate PoP */
1428
POP_TYPE_MAC,
1529
/** Use random stream generated and stored in fctry partition during claiming process as PoP */
16-
POP_TYPE_RANDOM
30+
POP_TYPE_RANDOM,
31+
/** Do not use any PoP.
32+
* Use this option with caution. Consider using `CONFIG_APP_WIFI_PROV_TIMEOUT_PERIOD` with this.
33+
*/
34+
POP_TYPE_NONE
1735
} app_wifi_pop_type_t;
1836

1937
void app_wifi_with_homekit_init();

examples/homekit_switch/main/app_main.c

+26
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,33 @@ static void event_handler(void* arg, esp_event_base_t event_base,
7979
case RMAKER_EVENT_FACTORY_RESET:
8080
ESP_LOGI(TAG, "Node reset to factory defaults.");
8181
break;
82+
case RMAKER_MQTT_EVENT_CONNECTED:
83+
ESP_LOGI(TAG, "MQTT Connected.");
84+
break;
85+
case RMAKER_MQTT_EVENT_DISCONNECTED:
86+
ESP_LOGI(TAG, "MQTT Disconnected.");
87+
break;
88+
case RMAKER_MQTT_EVENT_PUBLISHED:
89+
ESP_LOGI(TAG, "MQTT Published. Msg id: %d.", *((int *)event_data));
90+
break;
8291
default:
8392
ESP_LOGW(TAG, "Unhandled RainMaker Common Event: %d", event_id);
8493
}
94+
} else if (event_base == APP_WIFI_EVENT) {
95+
switch (event_id) {
96+
case APP_WIFI_EVENT_QR_DISPLAY:
97+
ESP_LOGI(TAG, "Provisioning QR : %s", (char *)event_data);
98+
break;
99+
case APP_WIFI_EVENT_PROV_TIMEOUT:
100+
ESP_LOGI(TAG, "Provisioning Timed Out. Please reboot.");
101+
break;
102+
case APP_WIFI_EVENT_PROV_RESTART:
103+
ESP_LOGI(TAG, "Provisioning has restarted due to failures.");
104+
break;
105+
default:
106+
ESP_LOGW(TAG, "Unhandled App Wi-Fi Event: %d", event_id);
107+
break;
108+
}
85109
} else {
86110
ESP_LOGW(TAG, "Invalid event received!");
87111
}
@@ -110,6 +134,8 @@ void app_main()
110134

111135
/* Register an event handler to catch RainMaker events */
112136
ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
137+
ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
138+
ESP_ERROR_CHECK(esp_event_handler_register(APP_WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
113139

114140
/* Initialize the ESP RainMaker Agent.
115141
* Note that this should be called after app_wifi_with_homekit_init() but before app_wifi_with_homekit_start()

0 commit comments

Comments
 (0)