diff --git a/platformio.ini b/platformio.ini index dd4acafc2..7e1733d34 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,8 +16,16 @@ framework = arduino monitor_speed = 115200 lib_compat_mode = strict lib_deps = + ;;;;;;;;;;; FunHouse / LVGL Boards uncomment these ;;;;;;;;;;;;;; + ; https://github.com/adafruit/Adafruit_HX8357_Library.git + ; https://github.com/adafruit/Adafruit_ILI9341.git + ; https://github.com/adafruit/Adafruit_STMPE610.git + ; https://github.com/adafruit/Adafruit-ST7735-Library.git + ; https://github.com/adafruit/Adafruit_TouchScreen.git + ; https://github.com/brentru/lvgl.git#wippersnapper + ; https://github.com/brentru/Adafruit_LvGL_Glue.git#development + ;;;;;;;;;;; All Boards need these libraries included ;;;;;;;;;;;;;; adafruit/Adafruit Zero DMA Library - https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git adafruit/Adafruit NeoPixel adafruit/Adafruit SPIFlash adafruit/Adafruit DotStar @@ -78,14 +86,8 @@ lib_deps = https://github.com/Sensirion/arduino-i2c-sen5x.git https://github.com/adafruit/WiFiNINA.git https://github.com/Starmbi/hp_BH1750.git - ;;;;;;;;;;; FunHouse / LVGL Boards ;;;;;;;;;;;;;; - https://github.com/adafruit/Adafruit_HX8357_Library.git - https://github.com/adafruit/Adafruit_ILI9341.git - https://github.com/adafruit/Adafruit_STMPE610.git - https://github.com/adafruit/Adafruit-ST7735-Library.git - https://github.com/adafruit/Adafruit_TouchScreen.git - https://github.com/brentru/lvgl.git#wippersnapper - https://github.com/brentru/Adafruit_LvGL_Glue.git#development + https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git + ; Common build environment for ESP32 platform @@ -107,9 +109,10 @@ platform = atmelsam platform_packages = platformio/framework-arduino-samd-adafruit@^1.7.13 platformio/tool-jlink@^1.78811.0 -lib_ldf_mode = deep +lib_ldf_mode = chain+ +lib_compat_mode = strict lib_archive = no ; debug timer issues see https://community.platformio.org/t/choose-usb-stack-as-tiny-usb/22451/5 -lib_ignore = OneWire +lib_ignore = OneWire, USBHost [common:rp2040] platform = https://github.com/maxgerhardt/platform-raspberrypi.git @@ -408,7 +411,23 @@ extra_scripts = pre:rename_usb_config.py [env:huzzah] extends=common:esp8266 board = huzzah -build_flags = -DARDUINO_ESP8266_ADAFRUIT_HUZZAH +board_build.f_cpu = 80000000L +; Arduino CLI uses this from adafruit_ci#ci-wippersnapper +; esp8266:esp8266:huzzah:xtal=80,vt=flash,exception=disabled,stacksmash=disabled,ssl=all,mmu=3232,non32xfer=fast,eesz=4M2M,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none,baud=115200 +build_flags = + -Wl,--gc-sections + -Wl,-Map=output.map + -DARDUINO_ESP8266_ADAFRUIT_HUZZAH + -DDEBUG_ESP_PORT=Serial + -DVTABLES_IN_FLASH + -DNO_EXCEPTIONS + -DNO_STACK_SMASH_PROTECTION + -DSSL_ALL + -DMMU_3232 + -DNON32XFER_FAST + -DDEBUG_DISABLED + -DDEBUG_LEVEL_NONE +board_build.eesz=4M2M board_build.filesystem = littlefs upload_port = /dev/cu.SLAB_USBtoUART @@ -418,8 +437,9 @@ upload_port = /dev/cu.SLAB_USBtoUART [env:adafruit_pyportal_m4] extends = common:atsamd board = adafruit_pyportal_m4 -build_flags = -DUSE_TINYUSB=1 +build_flags = -DUSE_TINYUSB -DADAFRUIT_PYPORTAL +extra_scripts = pre:rename_usb_config.py ; Adafruit PyPortal M4 Titano [env:adafruit_pyportal_m4_titano] @@ -463,8 +483,9 @@ build_flags = -DUSE_TINYUSB [env:adafruit_metro_m4_airliftlite] extends = common:atsamd board = adafruit_metro_m4_airliftlite -build_flags = -DUSE_TINYUSB=1 +build_flags = -DUSE_TINYUSB -DADAFRUIT_METRO_M4_AIRLIFT_LITE +; extra_scripts = pre:rename_usb_config.py upload_port = /dev/cu.usbmodem1201 @@ -494,7 +515,7 @@ build_flags = -DDEBUG_RP2040_CORE -DDEBUG_RP2040_WIFI -DNDEBUG - -DLWIP_DEBUG + -DLWIP_DEBUG=1 -DDEBUG_RP2040_PORT=Serial1 -DDEBUG_RP2040_UART_1 -DDEBUG_RP2040_UART=1 diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index a4c190825..a52d4a53f 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -2500,8 +2500,10 @@ void Wippersnapper::runNetFSM() { */ /**************************************************************************/ void Wippersnapper::haltError(String error, ws_led_status_t ledStatusColor) { - for (;;) { - WS_DEBUG_PRINT("ERROR [WDT RESET]: "); + for (int i=0;;i++) { + WS_DEBUG_PRINT("ERROR [WDT RESET IN "); + WS_DEBUG_PRINT(25 - i); + WS_DEBUG_PRINTLN("]: "); WS_DEBUG_PRINTLN(error); // let the WDT fail out and reset! statusLEDSolid(ledStatusColor); @@ -2512,6 +2514,12 @@ void Wippersnapper::haltError(String error, ws_led_status_t ledStatusColor) { // hardware and software watchdog timers, delayMicroseconds does not. delayMicroseconds(1000000); #endif + if (i < 20) { + yield(); + WS.feedWDT(); // feed the WDT for the first 20 seconds + } else if (i == 20) { + WS.enableWDT(5000); + } } } @@ -2711,8 +2719,10 @@ void print_reset_reason(int reason) { */ /**************************************************************************/ void printDeviceInfo() { + WS_PRINTER.flush(); WS_DEBUG_PRINTLN("-------Device Information-------"); WS_DEBUG_PRINT("Firmware Version: "); + WS_PRINTER.flush(); WS_DEBUG_PRINTLN(WS_VERSION); WS_DEBUG_PRINT("Board ID: "); WS_DEBUG_PRINTLN(BOARD_ID); diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index ede523228..d175c9963 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -19,6 +19,7 @@ #define WIPPERSNAPPER_H // Cpp STD +#include #include // Nanopb dependencies @@ -46,17 +47,28 @@ // Define actual debug output functions when necessary. #ifdef WS_DEBUG +#if defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_SAMD_MKRWIFI1010) #define WS_DEBUG_PRINT(...) \ - { WS_PRINTER.print(__VA_ARGS__); } ///< Prints debug output. + { WS_PRINTER.print(__VA_ARGS__); yield(); } ///< Prints line from debug output. #define WS_DEBUG_PRINTLN(...) \ - { WS_PRINTER.println(__VA_ARGS__); } ///< Prints line from debug output. + { WS_PRINTER.println(__VA_ARGS__); yield(); } ///< Prints line from debug output. #define WS_DEBUG_PRINTHEX(...) \ - { WS_PRINTER.print(__VA_ARGS__, HEX); } ///< Prints debug output. + { WS_PRINTER.print(__VA_ARGS__, HEX); yield(); } ///< Prints debug output. +#else +#define WS_DEBUG_PRINT(...) \ + { WS_PRINTER.print(__VA_ARGS__); WS_PRINTER.flush(); yield(); } ///< Prints line from debug output. +#define WS_DEBUG_PRINTLN(...) \ + { WS_PRINTER.println(__VA_ARGS__); WS_PRINTER.flush(); yield(); } ///< Prints line from debug output. +#define WS_DEBUG_PRINTHEX(...) \ + { WS_PRINTER.print(__VA_ARGS__, HEX); WS_PRINTER.flush(); yield(); } ///< Prints debug output. +#endif #else #define WS_DEBUG_PRINT(...) \ {} ///< Prints debug output #define WS_DEBUG_PRINTLN(...) \ {} ///< Prints line from debug output. +#define WS_DEBUG_PRINTHEX(...) \ + {} ///< Prints debug output. #endif #define WS_DELAY_WITH_WDT(timeout) \ @@ -215,7 +227,6 @@ typedef enum { } fsm_net_t; #define WS_WDT_TIMEOUT 60000 ///< WDT timeout -#define WS_MAX_ALT_WIFI_NETWORKS 3 ///< Maximum number of alternative networks /* MQTT Configuration */ #define WS_KEEPALIVE_INTERVAL_MS \ 5000 ///< Session keepalive interval time, in milliseconds diff --git a/src/Wippersnapper_Networking.h b/src/Wippersnapper_Networking.h index 5e401a4ff..03a5c44d5 100644 --- a/src/Wippersnapper_Networking.h +++ b/src/Wippersnapper_Networking.h @@ -1,44 +1,51 @@ -/*! - * @file Wippersnapper_Networking.h - * - * This file includes network interfaces at compile-time. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - * - */ - -#ifndef WIPPERSNAPPER_NETWORKING_H -#define WIPPERSNAPPER_NETWORKING_H - -#if defined(ADAFRUIT_METRO_M4_EXPRESS) || \ - defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE) || defined(ADAFRUIT_PYPORTAL) || \ - defined(ADAFRUIT_PYPORTAL_M4_TITANO) || defined(USE_AIRLIFT) -#include "network_interfaces/Wippersnapper_AIRLIFT.h" -/** Nina-FW (adafruit fork) networking class */ -typedef Wippersnapper_AIRLIFT Wippersnapper_WiFi; -#elif defined(ARDUINO_ARCH_ESP8266) -#include "network_interfaces/Wippersnapper_ESP8266.h" -/** ESP8266's networking class */ -typedef Wippersnapper_ESP8266 Wippersnapper_WiFi; -#elif defined(ARDUINO_ARCH_ESP32) -#include "network_interfaces/Wippersnapper_ESP32.h" -/** ESP32's networking class */ -typedef Wippersnapper_ESP32 Wippersnapper_WiFi; -#elif defined(ARDUINO_ARCH_RP2040) -#include "network_interfaces/ws_networking_pico.h" -typedef ws_networking_pico Wippersnapper_WiFi; -#elif defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_SAMD_MKRWIFI1010) -/** Nina-FW (arduino) networking class */ -#include "network_interfaces/Wippersnapper_WIFININA.h" -typedef Wippersnapper_WIFININA Wippersnapper_WiFi; -#else -#warning "Must define network interface in config.h!" -#endif - +/*! + * @file Wippersnapper_Networking.h + * + * This file includes network interfaces at compile-time. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#ifndef WIPPERSNAPPER_NETWORKING_H +#define WIPPERSNAPPER_NETWORKING_H + +#ifndef WL_MAC_ADDR_LENGTH +#define WL_MAC_ADDR_LENGTH 6 // MAC address length - from RP2040 BSP +#endif +#define WS_MAX_SORTED_NETWORKS 15 ///< Maximum number of networks to sort +#define WS_MAX_ALT_WIFI_NETWORKS 3 ///< Maximum number of alternative networks + + +#if defined(ADAFRUIT_METRO_M4_EXPRESS) || \ + defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE) || defined(ADAFRUIT_PYPORTAL) || \ + defined(ADAFRUIT_PYPORTAL_M4_TITANO) || defined(USE_AIRLIFT) +#include "network_interfaces/Wippersnapper_AIRLIFT.h" +/** Nina-FW (adafruit fork) networking class */ +typedef Wippersnapper_AIRLIFT Wippersnapper_WiFi; +#elif defined(ARDUINO_ARCH_ESP8266) +#include "network_interfaces/Wippersnapper_ESP8266.h" +/** ESP8266's networking class */ +typedef Wippersnapper_ESP8266 Wippersnapper_WiFi; +#elif defined(ARDUINO_ARCH_ESP32) +#include "network_interfaces/Wippersnapper_ESP32.h" +/** ESP32's networking class */ +typedef Wippersnapper_ESP32 Wippersnapper_WiFi; +#elif defined(ARDUINO_ARCH_RP2040) +#include "network_interfaces/ws_networking_pico.h" +typedef ws_networking_pico Wippersnapper_WiFi; +#elif defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_SAMD_MKRWIFI1010) +/** Nina-FW (arduino) networking class */ +#include "network_interfaces/Wippersnapper_WIFININA.h" +typedef Wippersnapper_WIFININA Wippersnapper_WiFi; +#else +#warning "Must define network interface in config.h!" +#endif + #endif // WIPPERSNAPPER_NETWORKING_H \ No newline at end of file diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index 4c564b4e1..056e37bb5 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -101,10 +101,34 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { _pass = WS._config.network.pass; } + /*******************************************************************/ + /*! + @brief fixed size structure to hold network information for sorting + */ + /*******************************************************************/ + struct WiFiNetwork { + char ssid[33]; /*!< SSID (Max 32 characters + null terminator */ + int32_t rssi = -99; /*!< Received Signal Strength Indicator */ + }; + + /*******************************************************************/ + /*! + @brief Comparison function to sort by RSSI in descending order + @param a + WiFiNetwork object + @param b + WiFiNetwork object + @returns True if a.rssi > b.rssi + */ + /*******************************************************************/ + bool static compareByRSSI(const WiFiNetwork &a, const WiFiNetwork &b) { + return a.rssi > b.rssi; + } + /***********************************************************/ /*! - @brief Performs a scan of local WiFi networks. - @returns True if `_network_ssid` is found, False otherwise. + @brief Performs a scan of local WiFi networks. + @returns True if `_network_ssid` is found, False otherwise. */ /***********************************************************/ bool check_valid_ssid() { @@ -119,26 +143,35 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { return false; } - // Was the network within secrets.json found? - for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) { - WS_DEBUG_PRINT("SSID found! RSSI: "); - WS_DEBUG_PRINTLN(WiFi.RSSI(i)); - return true; - } - } + bool foundNetwork = false; - // User-set network not found, print scan results to serial console - WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); - WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); - for (int i = 0; i < n; ++i) { + WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks:"); + for (uint8_t i = 0; i < n; i++) { + if (strcmp(WiFi.SSID(i), _ssid) == 0) { + foundNetwork = true; + break; + } WS_DEBUG_PRINT(WiFi.SSID(i)); - WS_DEBUG_PRINT(" "); + WS_DEBUG_PRINT(" ("); + uint8_t BSSID[WL_MAC_ADDR_LENGTH]; + WiFi.BSSID(i, BSSID); + for (int m = 0; m < WL_MAC_ADDR_LENGTH; m++) { + if (m != 0) + WS_DEBUG_PRINT(":"); + WS_DEBUG_PRINTHEX(BSSID[m]); + } + WS_DEBUG_PRINT(") "); WS_DEBUG_PRINT(WiFi.RSSI(i)); - WS_DEBUG_PRINTLN("dB"); + WS_DEBUG_PRINT("dB (ch"); + WS_DEBUG_PRINT(WiFi.channel(i)) + WS_DEBUG_PRINTLN(")"); } - return false; + if(foundNetwork == 0) { + WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); + return false; + } + return true; } /********************************************************/ diff --git a/src/network_interfaces/Wippersnapper_ESP32.h b/src/network_interfaces/Wippersnapper_ESP32.h index 99c4d47be..f2c90306d 100644 --- a/src/network_interfaces/Wippersnapper_ESP32.h +++ b/src/network_interfaces/Wippersnapper_ESP32.h @@ -19,6 +19,7 @@ #ifdef ARDUINO_ARCH_ESP32 #include "Wippersnapper.h" +#include "Wippersnapper_Networking.h" #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" @@ -90,6 +91,30 @@ class Wippersnapper_ESP32 : public Wippersnapper { _pass = WS._config.network.pass; } + /****************************************************************/ + /*! + @brief a structure to hold network information for sorting + */ + /****************************************************************/ + struct WiFiNetwork { + char ssid[33]; /*!< SSID (Max 32 characters + null terminator */ + int32_t rssi; /*!< Received Signal Strength Indicator */ + }; + + /*******************************************************************/ + /*! + @brief Comparison function to sort by RSSI in descending order + @param a + WiFiNetwork object + @param b + WiFiNetwork object + @returns True if a.rssi > b.rssi + */ + /*******************************************************************/ + bool static compareByRSSI(const WiFiNetwork &a, const WiFiNetwork &b) { + return a.rssi > b.rssi; + } + /***********************************************************/ /*! @brief Performs a scan of local WiFi networks. @@ -120,23 +145,47 @@ class Wippersnapper_ESP32 : public Wippersnapper { return false; } - // Was the network within secrets.json found? + WiFiNetwork networks[WS_MAX_SORTED_NETWORKS]; + uint8_t numSavedNetworks = 0; + // Store the scanned networks in the vector for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) { + if (i < WS_MAX_SORTED_NETWORKS) { + strncpy(networks[i].ssid, WiFi.SSID(i).c_str(), + sizeof(networks[i].ssid)); + networks[i].ssid[sizeof(networks[i].ssid) - 1] = '\0'; + networks[i].rssi = WiFi.RSSI(i); + numSavedNetworks++; + } else { + WS_DEBUG_PRINT("ERROR: Too many networks found! (>"); + WS_DEBUG_PRINT(WS_MAX_SORTED_NETWORKS); + WS_DEBUG_PRINT(") Ignoring "); + WS_DEBUG_PRINT(WiFi.SSID(i)); + WS_DEBUG_PRINT("("); + WS_DEBUG_PRINT(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN(")"); + } + } + + // Sort the networks by RSSI in descending order + std::sort(networks, networks + numSavedNetworks, compareByRSSI); + + // Was the network within secrets.json found? + for (int i = 0; i < numSavedNetworks; ++i) { + if (strcmp(_ssid, networks[i].ssid) == 0) { WS_DEBUG_PRINT("SSID ("); WS_DEBUG_PRINT(_ssid); WS_DEBUG_PRINT(") found! RSSI: "); - WS_DEBUG_PRINTLN(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN(networks[i].rssi); return true; } if (WS._isWiFiMulti) { // multi network mode for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) { - if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0) { + if (strcmp(WS._multiNetworks[j].ssid, networks[i].ssid) == 0) { WS_DEBUG_PRINT("SSID ("); WS_DEBUG_PRINT(WS._multiNetworks[j].ssid); WS_DEBUG_PRINT(") found! RSSI: "); - WS_DEBUG_PRINTLN(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN(networks[i].rssi); return true; } } @@ -145,12 +194,24 @@ class Wippersnapper_ESP32 : public Wippersnapper { // User-set network not found, print scan results to serial console WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); - WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); - for (int i = 0; i < n; ++i) { + WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks:"); + for (uint8_t i = 0; i < n; ++i) + { WS_DEBUG_PRINT(WiFi.SSID(i)); - WS_DEBUG_PRINT(" "); + WS_DEBUG_PRINT(" ("); + uint8_t BSSID[WL_MAC_ADDR_LENGTH]; + WiFi.BSSID(i, BSSID); + for (int m = 0; m < WL_MAC_ADDR_LENGTH; m++) + { + if (m != 0) + WS_DEBUG_PRINT(":"); + WS_DEBUG_PRINTHEX(BSSID[m]); + } + WS_DEBUG_PRINT(") "); WS_DEBUG_PRINT(WiFi.RSSI(i)); - WS_DEBUG_PRINTLN("dB"); + WS_DEBUG_PRINT("dB (ch"); + WS_DEBUG_PRINT(WiFi.channel(i)) + WS_DEBUG_PRINTLN(")"); } return false; diff --git a/src/network_interfaces/Wippersnapper_ESP8266.h b/src/network_interfaces/Wippersnapper_ESP8266.h index c7fa6b693..0d162ec4e 100644 --- a/src/network_interfaces/Wippersnapper_ESP8266.h +++ b/src/network_interfaces/Wippersnapper_ESP8266.h @@ -23,6 +23,7 @@ #include "ESP8266WiFi.h" #include "ESP8266WiFiMulti.h" #include "Wippersnapper.h" +#include "Wippersnapper_Networking.h" /* NOTE - Projects that require "Secure MQTT" (TLS/SSL) also require a new * SSL certificate every year. If adding Secure MQTT to your ESP8266 project is @@ -113,6 +114,30 @@ class Wippersnapper_ESP8266 : public Wippersnapper { _pass = WS._config.network.pass; } + /****************************************************************/ + /*! + @brief a structure to hold network information for sorting + */ + /****************************************************************/ + struct WiFiNetwork { + char ssid[33]; /*!< SSID (Max 32 characters + null terminator */ + int rssi; /*!< Received Signal Strength Indicator */ + }; + + /*******************************************************************/ + /*! + @brief Comparison function to sort by RSSI in descending order + @param a + WiFiNetwork object + @param b + WiFiNetwork object + @returns True if a.rssi > b.rssi + */ + /*******************************************************************/ + bool static compareByRSSI(const WiFiNetwork &a, const WiFiNetwork &b) { + return a.rssi > b.rssi; + } + /***********************************************************/ /*! @brief Performs a scan of local WiFi networks. @@ -133,23 +158,47 @@ class Wippersnapper_ESP8266 : public Wippersnapper { return false; } + WiFiNetwork networks[WS_MAX_SORTED_NETWORKS]; + uint8_t numSavedNetworks = 0; + // Store the scanned networks in the vector + for (int i = 0; i < n; i++) { + if (i < WS_MAX_SORTED_NETWORKS) { + strncpy(networks[i].ssid, WiFi.SSID(i).c_str(), + sizeof(networks[i].ssid)); + networks[i].ssid[sizeof(networks[i].ssid) - 1] = '\0'; + networks[i].rssi = WiFi.RSSI(i); + numSavedNetworks++; + } else { + WS_DEBUG_PRINT("ERROR: Too many networks found! (>"); + WS_DEBUG_PRINT(WS_MAX_SORTED_NETWORKS); + WS_DEBUG_PRINT(") Ignoring "); + WS_DEBUG_PRINT(WiFi.SSID(i)); + WS_DEBUG_PRINT("("); + WS_DEBUG_PRINT(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN(")"); + } + } + + // Sort the networks by RSSI in descending order + std::sort(networks, networks + numSavedNetworks, compareByRSSI); + // Was the network within secrets.json found? - for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) { + for (int i = 0; i < numSavedNetworks; i++) { + if (strcmp(_ssid, networks[i].ssid) == 0) { WS_DEBUG_PRINT("SSID ("); WS_DEBUG_PRINT(_ssid); WS_DEBUG_PRINT(") found! RSSI: "); - WS_DEBUG_PRINTLN(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN(networks[i].rssi); return true; } if (WS._isWiFiMulti) { // multi network mode for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) { - if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0) { + if (strcmp(WS._multiNetworks[j].ssid, networks[i].ssid) == 0) { WS_DEBUG_PRINT("SSID ("); WS_DEBUG_PRINT(WS._multiNetworks[j].ssid); WS_DEBUG_PRINT(") found! RSSI: "); - WS_DEBUG_PRINTLN(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN(networks[i].rssi); return true; } } @@ -157,13 +206,24 @@ class Wippersnapper_ESP8266 : public Wippersnapper { } // User-set network not found, print scan results to serial console - WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); - WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); - for (int i = 0; i < n; ++i) { + WS_DEBUG_PRINTLN("ERROR: Your WiFi network was not found!"); + WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks:"); + for (uint8_t i = 0; i < n; i++) + { WS_DEBUG_PRINT(WiFi.SSID(i)); - WS_DEBUG_PRINT(" "); + WS_DEBUG_PRINT(" ("); + const uint8_t* BSSID = WiFi.BSSID(i); + for (int m = 0; m < WL_MAC_ADDR_LENGTH; m++) + { + if (m != 0) + WS_DEBUG_PRINT(":"); + WS_DEBUG_PRINTHEX(BSSID[m]); + } + WS_DEBUG_PRINT(") "); WS_DEBUG_PRINT(WiFi.RSSI(i)); - WS_DEBUG_PRINTLN("dB"); + WS_DEBUG_PRINT("dB (ch"); + WS_DEBUG_PRINT(WiFi.channel(i)) + WS_DEBUG_PRINTLN(")"); } return false; @@ -260,7 +320,6 @@ class Wippersnapper_ESP8266 : public Wippersnapper { delay(100); // ESP8266 MUST be in STA mode to avoid device acting as client/server WiFi.mode(WIFI_STA); - WiFi.begin(_ssid, _pass); _status = WS_NET_DISCONNECTED; delay(100); @@ -274,38 +333,28 @@ class Wippersnapper_ESP8266 : public Wippersnapper { WS._multiNetworks[i].pass); } } - // add default network - if (_wifiMulti.existsAP(_ssid) == false) { - _wifiMulti.addAP(_ssid, _pass); - } - long startRetry = millis(); - WS_DEBUG_PRINTLN("CONNECTING"); - while (_wifiMulti.run(5000) != WL_CONNECTED && - millis() - startRetry < 10000) { - // ESP8266 WDT requires yield() during a busy-loop so it doesn't bite - yield(); - } - if (WiFi.status() == WL_CONNECTED) { - _status = WS_NET_CONNECTED; - } else { - _status = WS_NET_DISCONNECTED; - } + } + + // add default network + if (_wifiMulti.existsAP(_ssid) == false) { + _wifiMulti.addAP(_ssid, _pass); + } + + long startRetry = millis(); + WS_DEBUG_PRINTLN("CONNECTING"); + + while (_wifiMulti.run(5000) != WL_CONNECTED && + millis() - startRetry < 10000) { + // ESP8266 WDT requires yield() during a busy-loop so it doesn't bite + yield(); + } + + if (WiFi.status() == WL_CONNECTED) { + _status = WS_NET_CONNECTED; } else { - // single network mode - - // wait for a connection to be established - long startRetry = millis(); - WS_DEBUG_PRINTLN("CONNECTING"); - while (WiFi.status() != WL_CONNECTED && millis() - startRetry < 10000) { - // ESP8266 WDT requires yield() during a busy-loop so it doesn't bite - yield(); - } - if (WiFi.status() == WL_CONNECTED) { - _status = WS_NET_CONNECTED; - } else { - _status = WS_NET_DISCONNECTED; - } + _status = WS_NET_DISCONNECTED; } + WS.feedWDT(); } } diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index 74f50bffe..77bfefe72 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -109,10 +109,34 @@ class Wippersnapper_WIFININA : public Wippersnapper { strlcpy(WS._config.network.pass, _pass, sizeof(WS._config.network.pass)); } + /****************************************************************/ + /*! + @brief a structure to hold network information for sorting + */ + /****************************************************************/ + struct WiFiNetwork { + char ssid[33]; /*!< SSID (Max 32 characters + null terminator */ + int rssi; /*!< Received Signal Strength Indicator */ + }; + + /*******************************************************************/ + /*! + @brief Comparison function to sort by RSSI in descending order + @param a + WiFiNetwork object + @param b + WiFiNetwork object + @returns True if a.rssi > b.rssi + */ + /*******************************************************************/ + bool static compareByRSSI(const WiFiNetwork &a, const WiFiNetwork &b) { + return a.rssi > b.rssi; + } + /***********************************************************/ /*! - @brief Performs a scan of local WiFi networks. - @returns True if `_network_ssid` is found, False otherwise. + @brief Performs a scan of local WiFi networks. + @returns True if `_network_ssid` is found, False otherwise. */ /***********************************************************/ bool check_valid_ssid() { @@ -122,29 +146,67 @@ class Wippersnapper_WIFININA : public Wippersnapper { delay(100); // Perform a network scan - int n = WiFi.scanNetworks(); + int8_t n = WiFi.scanNetworks(); if (n == 0) { WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!"); return false; } - // Was the network within secrets.json found? + WiFiNetwork networks[WS_MAX_SORTED_NETWORKS]; + uint8_t numSavedNetworks = 0; + + // Store the scanned networks in the vector for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) { - WS_DEBUG_PRINT("SSID found! RSSI: "); - WS_DEBUG_PRINTLN(WiFi.RSSI(i)); + if (i < WS_MAX_SORTED_NETWORKS) { + strncpy(networks[i].ssid, WiFi.SSID(i), sizeof(networks[i].ssid)); + networks[i].ssid[sizeof(networks[i].ssid) - 1] = '\0'; + networks[i].rssi = WiFi.RSSI(i); + numSavedNetworks++; + } else { + WS_DEBUG_PRINT("ERROR: Too many networks found! (>"); + WS_DEBUG_PRINT(WS_MAX_SORTED_NETWORKS); + WS_DEBUG_PRINT(") Ignoring "); + WS_DEBUG_PRINT(WiFi.SSID(i)); + WS_DEBUG_PRINT("("); + WS_DEBUG_PRINT(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN(")"); + } + } + + /// Sort the networks by RSSI in descending order + std::sort(networks, networks + numSavedNetworks, compareByRSSI); + + // Was the network within secrets.json found? + for (int i = 0; i < numSavedNetworks; ++i) { + if (strcmp(_ssid, networks[i].ssid) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(_ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(networks[i].rssi); return true; } } // User-set network not found, print scan results to serial console WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); - WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); - for (int i = 0; i < n; ++i) { + WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks:"); + for (uint8_t i = 0; i < n; ++i) + { WS_DEBUG_PRINT(WiFi.SSID(i)); - WS_DEBUG_PRINT(" "); + WS_DEBUG_PRINT(" ("); + uint8_t BSSID[WL_MAC_ADDR_LENGTH]; + WiFi.BSSID(i, BSSID); + for (int m = 0; m < WL_MAC_ADDR_LENGTH; m++) + { + if (m != 0) + WS_DEBUG_PRINT(":"); + WS_DEBUG_PRINTHEX(BSSID[m]); + } + WS_DEBUG_PRINT(") "); WS_DEBUG_PRINT(WiFi.RSSI(i)); - WS_DEBUG_PRINTLN("dB"); + WS_DEBUG_PRINT("dB (ch"); + WS_DEBUG_PRINT(WiFi.channel(i)) + WS_DEBUG_PRINTLN(")"); } return false; diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index 25ce278b1..b6b7136d3 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -23,6 +23,7 @@ #define PICO_CONNECT_RETRY_DELAY_MS 200 /*!< delay time between retries. */ #include "Wippersnapper.h" +#include "Wippersnapper_Networking.h" #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" @@ -92,6 +93,30 @@ class ws_networking_pico : public Wippersnapper { _pass = WS._config.network.pass; } + /****************************************************************/ + /*! + @brief a structure to hold network information for sorting + */ + /****************************************************************/ + struct WiFiNetwork { + char ssid[33]; /*!< SSID (Max 32 characters + null terminator */ + int rssi; /*!< Received Signal Strength Indicator */ + }; + + /*******************************************************************/ + /*! + @brief Comparison function to sort by RSSI in descending order + @param a + WiFiNetwork object + @param b + WiFiNetwork object + @returns True if a.rssi > b.rssi + */ + /*******************************************************************/ + bool static compareByRSSI(const WiFiNetwork &a, const WiFiNetwork &b) { + return a.rssi > b.rssi; + } + /***********************************************************/ /*! @brief Performs a scan of local WiFi networks. @@ -112,23 +137,47 @@ class ws_networking_pico : public Wippersnapper { return false; } - // Was the network within secrets.json found? + WiFiNetwork networks[WS_MAX_SORTED_NETWORKS]; + uint8_t numSavedNetworks = 0; + + // Store the scanned networks in the vector for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) { + if (i < WS_MAX_SORTED_NETWORKS) { + strncpy(networks[i].ssid, WiFi.SSID(i), sizeof(networks[i].ssid)); + networks[i].ssid[sizeof(networks[i].ssid) - 1] = '\0'; + networks[i].rssi = WiFi.RSSI(i); + numSavedNetworks++; + } else { + WS_DEBUG_PRINT("ERROR: Too many networks found! (>"); + WS_DEBUG_PRINT(WS_MAX_SORTED_NETWORKS); + WS_DEBUG_PRINT(") Ignoring "); + WS_DEBUG_PRINT(WiFi.SSID(i)); + WS_DEBUG_PRINT("("); + WS_DEBUG_PRINT(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN(")"); + } + } + + /// Sort the networks by RSSI in descending order + std::sort(networks, networks + numSavedNetworks, compareByRSSI); + + // Was the network within secrets.json found? + for (int i = 0; i < numSavedNetworks; ++i) { + if (strcmp(_ssid, networks[i].ssid) == 0) { WS_DEBUG_PRINT("SSID ("); WS_DEBUG_PRINT(_ssid); WS_DEBUG_PRINT(") found! RSSI: "); - WS_DEBUG_PRINTLN(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN(networks[i].rssi); return true; } if (WS._isWiFiMulti) { // multi network mode for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) { - if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i)) == 0) { + if (strcmp(WS._multiNetworks[j].ssid, networks[i].ssid) == 0) { WS_DEBUG_PRINT("SSID ("); WS_DEBUG_PRINT(WS._multiNetworks[j].ssid); WS_DEBUG_PRINT(") found! RSSI: "); - WS_DEBUG_PRINTLN(WiFi.RSSI(i)); + WS_DEBUG_PRINTLN(networks[i].rssi); return true; } } @@ -137,12 +186,24 @@ class ws_networking_pico : public Wippersnapper { // User-set network not found, print scan results to serial console WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!"); - WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: "); - for (int i = 0; i < n; ++i) { + WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks:"); + for (uint8_t i = 0; i < n; ++i) + { WS_DEBUG_PRINT(WiFi.SSID(i)); - WS_DEBUG_PRINT(" "); + WS_DEBUG_PRINT(" ("); + uint8_t BSSID[WL_MAC_ADDR_LENGTH]; + WiFi.BSSID(i, BSSID); + for (int m = 0; m < WL_MAC_ADDR_LENGTH; m++) + { + if (m != 0) + WS_DEBUG_PRINT(":"); + WS_DEBUG_PRINTHEX(BSSID[m]); + } + WS_DEBUG_PRINT(") "); WS_DEBUG_PRINT(WiFi.RSSI(i)); - WS_DEBUG_PRINTLN("dB"); + WS_DEBUG_PRINT("dB (ch"); + WS_DEBUG_PRINT(WiFi.channel(i)) + WS_DEBUG_PRINTLN(")"); } return false; diff --git a/src/provisioning/tinyusb/Wippersnapper_FS.cpp b/src/provisioning/tinyusb/Wippersnapper_FS.cpp index e4fa0ee72..a71495fd2 100644 --- a/src/provisioning/tinyusb/Wippersnapper_FS.cpp +++ b/src/provisioning/tinyusb/Wippersnapper_FS.cpp @@ -106,6 +106,7 @@ Wippersnapper_FS::Wippersnapper_FS() { // If a filesystem does not already exist - attempt to initialize a new // filesystem if (!initFilesystem() && !initFilesystem(true)) { + TinyUSBDevice.attach(); setStatusLEDColor(RED); fsHalt("ERROR Initializing Filesystem"); } @@ -217,7 +218,12 @@ void Wippersnapper_FS::initUSBMSC() { // init MSC usb_msc.begin(); + // Attach MSC and wait for enumeration + if (TinyUSBDevice.mounted()) { + TinyUSBDevice.detach(); + delay(10); + } TinyUSBDevice.attach(); delay(500); } @@ -232,6 +238,13 @@ bool Wippersnapper_FS::configFileExists() { // Does secrets.json file exist? if (!wipperFatFs.exists("/secrets.json")) return false; + File32 file = wipperFatFs.open("/secrets.json", FILE_READ); + if (!file) + return false; + int firstChar = file.peek(); + file.close(); + if (firstChar <= 0 || firstChar == 255) + return false; return true; } @@ -318,7 +331,7 @@ bool Wippersnapper_FS::createBootFile() { void Wippersnapper_FS::createSecretsFile() { // Open file for writing File32 secretsFile = wipperFatFs.open("/secrets.json", FILE_WRITE); - + secretsFile.truncate(0); // Create a default secretsConfig structure secretsConfig secretsConfig; strcpy(secretsConfig.aio_user, "YOUR_IO_USERNAME_HERE"); @@ -333,7 +346,7 @@ void Wippersnapper_FS::createSecretsFile() { serializeJsonPretty(doc, secretsFile); secretsFile.flush(); secretsFile.close(); - + writeToBootOut( "ERROR: Please edit the secrets.json file. Then, reset your board.\n"); #ifdef USE_DISPLAY @@ -485,8 +498,6 @@ void Wippersnapper_FS::writeToBootOut(PGM_P str) { */ /**************************************************************************/ void Wippersnapper_FS::fsHalt(String msg) { - TinyUSBDevice.attach(); - delay(500); statusLEDSolid(WS_LED_STATUS_FS_WRITE); while (1) { WS_DEBUG_PRINTLN("Fatal Error: Halted execution!");