1 /* BLE based Provisioning Example
2 
3    This example code is in the Public Domain (or CC0 licensed, at your option.)
4 
5    Unless required by applicable law or agreed to in writing, this
6    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
7    CONDITIONS OF ANY KIND, either express or implied.
8 */
9 
10 #include <string.h>
11 #include <freertos/FreeRTOS.h>
12 #include <freertos/task.h>
13 #include <esp_system.h>
14 #include <esp_wifi.h>
15 #include <esp_event.h>
16 #include <esp_log.h>
17 #include <nvs_flash.h>
18 
19 #include <lwip/err.h>
20 #include <lwip/sys.h>
21 
22 #include "app_prov.h"
23 #include "qrcode.h"
24 
25 #define EXAMPLE_AP_RECONN_ATTEMPTS  CONFIG_EXAMPLE_AP_RECONN_ATTEMPTS
26 #define PROV_QR_VERSION         "v1"
27 #define PROV_TRANSPORT_BLE      "ble"
28 #define QRCODE_BASE_URL         "https://espressif.github.io/esp-jumpstart/qrcode.html"
29 
30 static const char *TAG = "app";
31 
32 static void start_ble_provisioning(void);
33 
event_handler(void * arg,esp_event_base_t event_base,int32_t event_id,void * event_data)34 static void event_handler(void* arg, esp_event_base_t event_base,
35                           int32_t event_id, void* event_data)
36 {
37     static int s_retry_num_ap_not_found = 0;
38     static int s_retry_num_ap_auth_fail = 0;
39 
40     if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
41         esp_wifi_connect();
42     } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
43         wifi_event_sta_disconnected_t* disconnected = (wifi_event_sta_disconnected_t*) event_data;
44         switch (disconnected->reason) {
45         case WIFI_REASON_AUTH_EXPIRE:
46         case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
47         case WIFI_REASON_BEACON_TIMEOUT:
48         case WIFI_REASON_AUTH_FAIL:
49         case WIFI_REASON_ASSOC_FAIL:
50         case WIFI_REASON_HANDSHAKE_TIMEOUT:
51             ESP_LOGW(TAG, "connect to the AP fail : auth Error");
52             if (s_retry_num_ap_auth_fail < EXAMPLE_AP_RECONN_ATTEMPTS) {
53                 s_retry_num_ap_auth_fail++;
54                 esp_wifi_connect();
55                 ESP_LOGI(TAG, "retry connecting to the AP...");
56             } else {
57                 /* Restart provisioning if authentication fails */
58                 start_ble_provisioning();
59             }
60             break;
61         case WIFI_REASON_NO_AP_FOUND:
62             ESP_LOGW(TAG, "connect to the AP fail : not found");
63             if (s_retry_num_ap_not_found < EXAMPLE_AP_RECONN_ATTEMPTS) {
64                 s_retry_num_ap_not_found++;
65                 esp_wifi_connect();
66                 ESP_LOGI(TAG, "retry to connecting to the AP...");
67             }
68             break;
69         default:
70             /* None of the expected reasons */
71             esp_wifi_connect();
72             break;
73         }
74     } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
75         ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
76         ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
77         s_retry_num_ap_not_found = 0;
78         s_retry_num_ap_auth_fail = 0;
79     }
80 }
81 
wifi_init_sta(void)82 static void wifi_init_sta(void)
83 {
84     /* Set our event handling */
85     ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL));
86     ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, NULL));
87 
88     /* Start Wi-Fi in station mode with credentials set during provisioning */
89     ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
90     ESP_ERROR_CHECK(esp_wifi_start());
91 }
92 
start_ble_provisioning(void)93 static void start_ble_provisioning(void)
94 {
95     /* Security version */
96     int security = 0;
97     /* Proof of possession */
98     const protocomm_security_pop_t *pop = NULL;
99 
100 #ifdef CONFIG_EXAMPLE_USE_SEC_1
101     security = 1;
102 #endif
103 
104     /* Having proof of possession is optional */
105 #ifdef CONFIG_EXAMPLE_USE_POP
106     const static protocomm_security_pop_t app_pop = {
107         .data = (uint8_t *) CONFIG_EXAMPLE_POP,
108         .len = (sizeof(CONFIG_EXAMPLE_POP)-1)
109     };
110     pop = &app_pop;
111 #endif
112 
113     ESP_ERROR_CHECK(app_prov_start_ble_provisioning(security, pop));
114 }
115 
get_device_service_name(char * service_name,size_t max)116 static void get_device_service_name(char *service_name, size_t max)
117 {
118     uint8_t eth_mac[6];
119     const char *ssid_prefix = "PROV_";
120     esp_wifi_get_mac(WIFI_IF_STA, eth_mac);
121     snprintf(service_name, max, "%s%02X%02X%02X",
122              ssid_prefix, eth_mac[3], eth_mac[4], eth_mac[5]);
123 }
124 
ble_prov_print_qr(void)125 static void ble_prov_print_qr(void)
126 {
127     char payload[150] = {0};
128     char name[12] = {0};
129     char *pop = NULL;
130 #ifdef CONFIG_EXAMPLE_USE_POP
131     pop = CONFIG_EXAMPLE_POP;
132 #endif
133     get_device_service_name(name, sizeof(name));
134     if (pop) {
135         snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \
136                     ",\"pop\":\"%s\",\"transport\":\"%s\"}",
137                     PROV_QR_VERSION, name, pop, PROV_TRANSPORT_BLE);
138     } else {
139         snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \
140                     ",\"transport\":\"%s\"}",
141                     PROV_QR_VERSION, name, PROV_TRANSPORT_BLE);
142     }
143 #ifdef CONFIG_EXAMPLE_PROV_SHOW_QR
144     ESP_LOGI(TAG, "Scan this QR code from the provisioning application for Provisioning.");
145     esp_qrcode_config_t cfg = ESP_QRCODE_CONFIG_DEFAULT();
146     esp_qrcode_generate(&cfg, payload);
147 #endif /* CONFIG_APP_WIFI_PROV_SHOW_QR */
148     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);
149 }
150 
app_main(void)151 void app_main(void)
152 {
153     /* Initialize networking stack */
154     ESP_ERROR_CHECK(esp_netif_init());
155 
156     /* Create default event loop needed by the
157      * main app and the provisioning service */
158     ESP_ERROR_CHECK(esp_event_loop_create_default());
159 
160     /* Initialize NVS needed by Wi-Fi */
161     ESP_ERROR_CHECK(nvs_flash_init());
162 
163     /* Initialize Wi-Fi including netif with default config */
164     esp_netif_create_default_wifi_sta();
165     wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
166     ESP_ERROR_CHECK(esp_wifi_init(&cfg));
167 
168     /* Check if device is provisioned */
169     bool provisioned;
170     if (app_prov_is_provisioned(&provisioned) != ESP_OK) {
171         ESP_LOGE(TAG, "Error getting device provisioning state");
172         return;
173     }
174 
175     if (provisioned == false) {
176         /* If not provisioned, start provisioning via BLE */
177         ESP_LOGI(TAG, "Starting BLE provisioning");
178         start_ble_provisioning();
179         ble_prov_print_qr();
180     } else {
181         /* Else start as station with credentials set during provisioning */
182         ESP_LOGI(TAG, "Starting WiFi station");
183         wifi_init_sta();
184     }
185 }
186