1+ /**
2+ * Bootwifi - Boot the WiFi environment.
3+ *
4+ * Compile with -DBOOTWIFI_OVERRIDE_GPIO=<num> where <num> is a GPIO pin number
5+ * to use a GPIO override.
6+ * See the README.md for full information.
7+ *
8+ */
9+ #include <freertos/FreeRTOS.h>
10+ #include <freertos/task.h>
111#include <esp_log.h>
212#include <esp_err.h>
313#include <esp_system.h>
616#include <esp_wifi.h>
717#include <nvs.h>
818#include <nvs_flash.h>
9- #include <freertos/FreeRTOS.h>
10- #include <freertos/task.h>
1119#include <driver/gpio.h>
20+ #include <tcpip_adapter.h>
21+ #include <lwip/sockets.h>
1222#include <mongoose.h>
1323#include "bootwifi.h"
1424#include "sdkconfig.h"
1525#include "selectAP.h"
16- // Key used in NVS for connection info
17- #define KEY_CONNECTION_INFO "connectionInfo"
1826
19- // Namespace in NVS for bootwifi
20- #define BOOTWIFI_NAMESPACE "bootwifi"
27+ // If the structure of a record saved for a subsequent reboot changes
28+ // then consider using semver to change the version number or else
29+ // we may try and boot with the wrong data.
30+ #define KEY_VERSION "version"
31+ uint32_t g_version = 0x0100 ;
2132
22- #define SSID_SIZE (32)
23- #define PASSWORD_SIZE (64)
24-
25- #define OVERRIDE_GPIO GPIO_NUM_25
33+ #define KEY_CONNECTION_INFO "connectionInfo"
C02E
// Key used in NVS for connection info
34+ #define BOOTWIFI_NAMESPACE "bootwifi" // Namespace in NVS for bootwifi
35+ #define SSID_SIZE (32) // Maximum SSID size
36+ #define PASSWORD_SIZE (64) // Maximum password size
2637
2738typedef struct {
2839 char ssid [SSID_SIZE ];
2940 char password [PASSWORD_SIZE ];
41+ tcpip_adapter_ip_info_t ipInfo ; // Optional static IP information
3042} connection_info_t ;
3143
32- static char tag [] = "bootwifi" ;
33-
34- static bootwifi_callback_t g_callback = NULL ;
44+ static bootwifi_callback_t g_callback = NULL ; // Callback function to be invoked when we have finished.
3545
3646static int g_mongooseStarted = 0 ; // Has the mongoose server started?
3747static int g_mongooseStopRequest = 0 ; // Request to stop the mongoose server.
3848
49+ // Forward declarations
3950static void saveConnectionInfo (connection_info_t * pConnectionInfo );
4051static void becomeAccessPoint ();
4152static void bootWiFi2 ();
4253
54+ static char tag [] = "bootwifi" ;
55+
56+
57+
58+
59+
4360/**
44- * Convert a Mongoose event type to a string.
61+ * Convert a Mongoose event type to a string. Used for debugging.
4562 */
4663static char * mongoose_eventToString (int ev ) {
4764 static char temp [100 ];
@@ -134,21 +151,52 @@ static void mongoose_event_handler(struct mg_connection *nc, int ev, void *evDat
134151 } if (strcmp (uri , "/" ) == 0 ) {
135152 mg_send_head (nc , 200 , sizeof (selectAP_html ), "Content-Type: text/html" );
136153 mg_send (nc , selectAP_html , sizeof (selectAP_html ));
137- } if (strcmp (uri , "/ssidSelected" ) == 0 ) {
154+ }
155+ // Handle /ssidSelected
156+ // This is an incoming form with properties:
157+ // * ssid - The ssid of the network to connect against.
158+ // * password - the password to use to connect.
159+ // * ip - Static IP address ... may be empty
160+ // * gw - Static GW address ... may be empty
161+ // * netmask - Static netmask ... may be empty
162+ if (strcmp (uri , "/ssidSelected" ) == 0 ) {
138163 // We have received a form page containing the details. The form body will
139164 // contain:
140165 // ssid=<value>&password=<value>
141166 ESP_LOGD (tag , "- body: %.*s" , message -> body .len , message -> body .p );
142167 connection_info_t connectionInfo ;
143- mg_get_http_var (& message -> body , "ssid" ,
144- connectionInfo .ssid , SSID_SIZE );
145- mg_get_http_var (& message -> body , "password" ,
146- connectionInfo .password , PASSWORD_SIZE );
168+ mg_get_http_var (& message -> body , "ssid" , connectionInfo .ssid , SSID_SIZE );
169+ mg_get_http_var (& message -> body , "password" , connectionInfo .password , PASSWORD_SIZE );
170+
171+ char ipBuf [20 ];
172+ if (mg_get_http_var (& message -> body , "ip" , ipBuf , sizeof (ipBuf )) > 0 ) {
173+ inet_pton (AF_INET , ipBuf , & connectionInfo .ipInfo .ip );
174+ } else {
175+ connectionInfo .ipInfo .ip .addr = 0 ;
176+ }
177+
178+ if (mg_get_http_var (& message -> body , "gw" , ipBuf , sizeof (ipBuf )) > 0 ) {
179+ inet_pton (AF_INET , ipBuf , & connectionInfo .ipInfo .gw );
180+ }
181+ else {
182+ connectionInfo .ipInfo .gw .addr = 0 ;
183+ }
184+
185+ if (mg_get_http_var (& message -> body , "netmask" , ipBuf , sizeof (ipBuf )) > 0 ) {
186+ inet_pton (AF_INET , ipBuf , & connectionInfo .ipInfo .netmask );
187+ }
188+ else {
189+ connectionInfo .ipInfo .netmask .addr = 0 ;
190+ }
191+
147192 ESP_LOGD (tag , "ssid: %s, password: %s" , connectionInfo .ssid , connectionInfo .password );
193+
148194 mg_send_head (nc , 200 , 0 , "Content-Type: text/plain" );
149195 saveConnectionInfo (& connectionInfo );
150196 bootWiFi2 ();
151- } else {
197+ } // url is "/ssidSelected"
198+ // Else ... unknown URL
199+ else {
152200 mg_send_head (nc , 404 , 0 , "Content-Type: text/plain" );
153201 }
154202 nc -> flags |= MG_F_SEND_AND_CLOSE ; @@ -189,6 +237,11 @@ static void mongooseTask(void *data) {
189237 mg_mgr_free (& mgr );
190238 g_mongooseStarted = 0 ;
191239
240+ // Since we HAVE ended mongoose, time to invoke the callback.
241+ if (g_callback ) {
242+ g_callback (1 );
243+ }
244+
192245 ESP_LOGD (tag , "<< mongooseTask" );
193246 vTaskDelete (NULL );
194247 return ;
@@ -218,18 +271,24 @@ static esp_err_t esp32_wifi_eventHandler(void *ctx, system_event_t *event) {
218271 switch (event -> event_id ) {
219272 // When we have started being an access point, then start being a web server.
220273 case SYSTEM_EVENT_AP_START : { // Handle the AP start event
221- ESP_LOGD (tag , "AP started" );
274+ tcpip_adapter_ip_info_t ip_info ;
275+ tcpip_adapter_get_ip_info (TCPIP_ADAPTER_IF_AP , & ip_info );
276+ ESP_LOGD (tag , "**********************************************" );
277+ ESP_LOGD (tag , "* We are now an access point and you can point" )
278+ ESP_LOGD (tag , "* your browser to http://" IPSTR , IP2STR (& ip_info .ip ));
279+ ESP_LOGD (tag , "**********************************************" );
222280 // Start Mongoose ...
223281 if (!g_mongooseStarted )
224282 {
225283 g_mongooseStarted = 1 ;
226- xTaskCreatePinnedToCore (& mongooseTask , "mongoose_task " , 2048 , NULL , 5 , NULL , 0 );
284+ xTaskCreatePinnedToCore (& mongooseTask , "bootwifi_mongoose_task " , 8000 , NULL , 5 , NULL , 0 );
227285 }
228286 break ;
229287 } // SYSTEM_EVENT_AP_START
230288
231289 // If we fail to connect to an access point as a station, become an access point.
232290 case SYSTEM_EVENT_STA_DISCONNECTED : {
291+ ESP_LOGD (tag , "Station disconnected started" );
233292 // We think we tried to connect as a station and failed! ... become
234293 // an access point.
235294 becomeAccessPoint ();
@@ -239,14 +298,18 @@ static esp_err_t esp32_wifi_eventHandler(void *ctx, system_event_t *event) {
239298 // If we connected as a station then we are done and we can stop being a
240299 // web server.
241300 case SYSTEM_EVENT_STA_GOT_IP : {
242- ESP_LOGD (tag , " *******************************************" );
301+ ESP_LOGD (tag , "* *******************************************" );
243302 ESP_LOGD (tag , "* We are now connected and ready to do work!" )
244303 ESP_LOGD (tag , "* - Our IP address is: " IPSTR , IP2STR (& event -> event_info .got_ip .ip_info .ip ));
245- ESP_LOGD (tag , " *******************************************" );
304+ ESP_LOGD (tag , "* *******************************************" );
246305 g_mongooseStopRequest = 1 ; // Stop mongoose (if it is running).
247- if (g_callback ) {
248- g_callback (1 );
249- }
306+ // Invoke the callback if Mongoose has NOT been started ... otherwise
307+ // we will invoke the callback when mongoose has ended.
308+ if (!g_mongooseStarted ) {
309+ if (g_callback ) {
310+ g_callback (1 );
311+ }
312+ } // Mongoose was NOT started
250313 break ;
251314 } // SYSTEM_EVENT_STA_GOTIP
252315
@@ -265,24 +328,49 @@ static int getConnectionInfo(connection_info_t *pConnectionInfo) {
265328 nvs_handle handle ;
266329 size_t size ;
267330 esp_err_t err ;
331+ uint32_t version ;
268332 err = nvs_open (BOOTWIFI_NAMESPACE , NVS_READWRITE , & handle );
269333 if (err != 0 ) {
270334 ESP_LOGE (tag , "nvs_open: %x" , err );
271335 return -1 ;
272336 }
273337
338+ // Get the version that the data was saved against.
339+ err = nvs_get_u32 (handle , KEY_VERSION , & version );
340+ if (err != ESP_OK ) {
341+ ESP_LOGD (tag , "No version record found (%d)." , err );
342+ nvs_close (handle );
343+ return -1 ;
344+ }
345+
346+ // Check the versions match
347+ if ((version & 0xff00 ) != (g_version & 0xff00 )) {
348+ ESP_LOGD (tag , "Incompatible versions ... current is %x, found is %x" , version , g_version );
349+ nvs_close (handle );
350+ return -1 ;
351+ }
352+
274353 size = sizeof (connection_info_t );
275354 err = nvs_get_blob (handle , KEY_CONNECTION_INFO , pConnectionInfo , & size );
276- if (err == ESP_ERR_NVS_NOT_FOUND ) {
355+ if (err != ESP_OK ) {
356+ ESP_LOGD (tag , "No connection record found (%d)." , err );
277357 nvs_close (handle );
278358 return -1 ;
279359 }
280- if (err != 0 ) {
360+ if (err != ESP_OK ) {
281361 ESP_LOGE (tag , "nvs_open: %x" , err );
282362 nvs_close (handle );
283363 return -1 ;
284364 }
365+
366+ // Cleanup
285367 nvs_close (handle );
368+
369+ // Do a sanity check on the SSID
370+ if (strlen (pConnectionInfo -> ssid ) == 0 ) {
371+ ESP_LOGD (tag , "NULL ssid detected" );
372+ return -1 ;
373+ }
286374 return 0 ;
287375} // getConnectionInfo
288376
@@ -295,6 +383,7 @@ static void saveConnectionInfo(connection_info_t *pConnectionInfo) {
295383 ESP_ERROR_CHECK (nvs_open (BOOTWIFI_NAMESPACE , NVS_READWRITE , & handle ));
296384 ESP_ERROR_CHECK (nvs_set_blob (handle , KEY_CONNECTION_INFO , pConnectionInfo ,
297385 sizeof (connection_info_t )));
386+ ESP_ERROR_CHECK (nvs_set_u32 (handle , KEY_VERSION , g_version ));
298387 ESP_ERROR_CHECK (nvs_commit (handle ));
299388 nvs_close (handle );
300389} // setConnectionInfo
@@ -304,7 +393,17 @@ static void saveConnectionInfo(connection_info_t *pConnectionInfo) {
304393 * Become a station connecting to an existing access point.
305394 */
306395static void becomeStation (connection_info_t * pConnectionInfo ) {
307- ESP_LOGD (tag , "- Connecting to access point %s ..." , pConnectionInfo -> ssid );
396+ ESP_LOGD (tag , "- Connecting to access point \"%s\" ..." , pConnectionInfo -> ssid );
397+ assert (strlen (pConnectionInfo -> ssid ) > 0 );
398+
399+ // If we have a static IP address information, use that.
400+ if (pConnectionInfo -> ipInfo .ip .addr != 0 ) {
401+ ESP_LOGD (tag , " - using a static IP address of " IPSTR , IP2STR (& pConnectionInfo -> ipInfo .ip ));
402+ tcpip_adapter_dhcpc_stop (TCPIP_ADAPTER_IF_STA );
403+ tcpip_adapter_set_ip_info (TCPIP_ADAPTER_IF_STA , & pConnectionInfo -> ipInfo );
404+ } else {
405+ tcpip_adapter_dhcpc_start (TCPIP_ADAPTER_IF_STA );
406+ }
308407
309408 ESP_ERROR_CHECK ( esp_wifi_set_mode (WIFI_MODE_STA ));
310409 wifi_config_t sta_config ;
@@ -346,14 +445,20 @@ static void becomeAccessPoint() {
346445 * indicate that we should not attempt to connect to any previously saved
347446 * access point we may know about.
348447 */
448+
349449static int checkOverrideGpio () {
350- gpio_pad_select_gpio (OVERRIDE_GPIO );
351- gpio_set_direction (OVERRIDE_GPIO , GPIO_MODE_INPUT );
352- gpio_set_pull_mode (OVERRIDE_GPIO , GPIO_PULLDOWN_ONLY );
353- return gpio_get_level (OVERRIDE_GPIO );
450+ #ifdef BOOTWIFI_OVERRIDE_GPIO
451+ gpio_pad_select_gpio (BOOTWIFI_OVERRIDE_GPIO );
452+ gpio_set_direction (BOOTWIFI_OVERRIDE_GPIO , GPIO_MODE_INPUT );
453+ gpio_set_pull_mode (BOOTWIFI_OVERRIDE_GPIO , GPIO_PULLDOWN_ONLY );
454+ return gpio_get_level (BOOTWIFI_OVERRIDE_GPIO );
455+ #else
456+ return 0 ; // If no boot override, return false
457+ #endif
354458} // checkOverrideGpio
355459
356460
461+
357462static void bootWiFi2 () {
358463 ESP_LOGD (tag , ">> bootWiFi2" );
359464 // Check for a GPIO override which occurs when a physical Pin is high
0 commit comments