khoih-prog / WiFiManager_RP2040W

MultiWiFi/Credentials Manager for RP2040W boards using built-in CYW43439 WiFi. Powerful-yet-simple-to-use feature to enable adding dynamic custom parameters. Library to configure MultiWiFi/Credentials at runtime for RP2040W boards, using CYW43439 WiFi. You can also specify DHCP HostName and STA IP. Use much less memory compared to full-fledge WiFiManager. Config Portal will be auto-adjusted to match the number of dynamic custom parameters. Optional default Credentials to be autoloaded into Config Portal to use or change instead of manually input. Credentials are saved in LittleFS. New powerful-yet-simple-to-use feature to enable adding dynamic custom parameters from sketch and input using the same Config Portal. DoubleDetectDetector as well as Virtual Switches feature permits entering Config Portal as requested. Now using WiFiMulti_Generic library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

WiFiManager_RP2040W

arduino-library-badge GitHub release GitHub contributions welcome GitHub issues

Donate to my libraries using BuyMeACoffee



Table of Contents



Why do we need this WiFiManager_RP2040W library

Features

This library is based on, modified, bug-fixed and improved from:

  1. Tzapu WiFiManager
  2. Ken Taylor WiFiManager
  3. Khoi Hoang's ESP_WiFiManager

to provide support to RASPBERRY_PI_PICO_W with CYW43439 WiFi.

This is a WiFi Connection manager with fallback web ConfigPortal. It's using a web ConfigPortal, served from the RASPBERRY_PI_PICO_W, and operating as an access point.

Important Notes

  1. To be used with arduino-pico core v2.4.1+ because of dependency on DNSServer library.


Prerequisites

  1. Arduino IDE 1.8.19+ for Arduino. GitHub release
  2. Earle Philhower's arduino-pico core v2.6.3+ for RASPBERRY_PI_PICO_W with CYW43439 WiFi, etc. GitHub release
  3. Functional-Vlpp library v1.0.2+ to use server's lambda function. To install. check arduino-library-badge
  4. WiFiWebServer library v1.10.0+. To install. check arduino-library-badge
  5. DoubleResetDetector_Generic v1.8.1+. To install, check arduino-library-badge
  6. WiFiMulti_Generic library v1.2.2+ to use WiFiMulti function. To install, check arduino-library-badge.

Installation

Use Arduino Library Manager

The best and easiest way is to use Arduino Library Manager. Search for WiFiManager_RP2040W, then select / install the latest version. You can also use this link arduino-library-badge for more detailed instructions.

Manual Install

  1. Navigate to WiFiManager_RP2040W page.
  2. Download the latest release WiFiManager_RP2040W-main.zip.
  3. Extract the zip file to WiFiManager_RP2040W-main directory
  4. Copy the whole WiFiManager_RP2040W-main folder to Arduino libraries' directory such as ~/Arduino/libraries/.

VS Code & PlatformIO:

  1. Install VS Code
  2. Install PlatformIO
  3. Install WiFiManager_RP2040W library by using Library Manager. Search for WiFiManager_RP2040W in Platform.io Author's Libraries
  4. Use included platformio.ini file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at Project Configuration File


HOWTO Fix Multiple Definitions Linker Error

The current library implementation, using xyz-Impl.h instead of standard xyz.cpp, possibly creates certain Multiple Definitions Linker error in certain use cases.

You can include this .hpp file

// Can be included as many times as necessary, without `Multiple Definitions` Linker Error
#include "WiFiManager_RP2040W.hpp"     //https://github.com/khoih-prog/WiFiManager_RP2040W

in many files. But be sure to use the following .h file in just 1 .h, .cpp or .ino file, which must not be included in any other file, to avoid Multiple Definitions Linker Error

// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include "WiFiManager_RP2040W.h"           //https://github.com/khoih-prog/WiFiManager_RP2040W


How It Works

  • The ConfigOnDoubleReset example shows how it works and should be used as the basis for a sketch that uses this library.

  • Using any WiFi enabled device with a browser (computer, phone, tablet) connect to the newly created Access Point (AP) using configurable SSID and Password (specified in sketch)

// SSID and PW for Config Portal
String ssid = "RP2040W_ConfigPortal";
const char* password = "RP2040W_Pass";

then connect WebBrowser to configurable ConfigPortal IP address, default is 192.168.42.1

  • Choose one of the access points scanned, enter password, click Save.
  • RP2040W will restart, then try to connect to the WiFi network using STA-only mode.


HOWTO Basic configurations

1. Using default for every configurable parameter

  • Include in your sketch
// Use from 0 to 4. Higher number, more debugging messages and memory usage.
#define _WIFIMGR_LOGLEVEL_    4

#include <WiFi.h>

#include <WiFiMulti_Generic.h>
WiFiMulti_Generic wifiMulti;

#include <LittleFS.h>

#define  CONFIG_FILENAME        ("/wifi_cred.dat")

#define DRD_FLAG_DATA_SIZE      4

#define DRD_GENERIC_DEBUG       true  //false

#include <DoubleResetDetector_Generic.h>      //https://github.com/khoih-prog/DoubleResetDetector_Generic

// Number of seconds after reset during which a
// subsequent reset will be considered a double reset.
#define DRD_TIMEOUT             10

// RTC Memory Address for the DoubleResetDetector to use
#define DRD_ADDRESS             0

//DoubleResetDetector_Generic drd(DRD_TIMEOUT, DRD_ADDRESS);
DoubleResetDetector_Generic* drd;//////

// SSID and PW for Config Portal
String ssid = "RP2040W_ConfigPortal";
const char* password = "RP2040W_Pass";

// SSID and PW for your Router
String Router_SSID;
String Router_Pass;

#define LED_ON      HIGH
#define LED_OFF     LOW

#define MIN_AP_PASSWORD_SIZE    8

#define SSID_MAX_LEN            32
//From v1.0.10, WPA2 passwords can be up to 63 characters long.
#define PASS_MAX_LEN            64

typedef struct
{
  char wifi_ssid[SSID_MAX_LEN];
  char wifi_pw  [PASS_MAX_LEN];
}  WiFi_Credentials;

typedef struct
{
  String wifi_ssid;
  String wifi_pw;
}  WiFi_Credentials_String;

#define NUM_WIFI_CREDENTIALS      2

typedef struct
{
  WiFi_Credentials  WiFi_Creds [NUM_WIFI_CREDENTIALS];
} WM_Config;

WM_Config         WM_config;

//////

// Indicates whether ESP has WiFi credentials saved from previous session, or double reset detected
bool initialConfig = false;
//////

// Use false if you don't like to display Available Pages in Information Page of Config Portal
// Comment out or use true to display Available Pages in Information Page of Config Portal
// Must be placed before #include <WiFiManager_RP2040W.h>
#define USE_AVAILABLE_PAGES     false

// New in v1.0.11
#define USING_CORS_FEATURE          true

///////////////////////////////////////////////////

// Use DHCP
#warning Using DHCP IP
IPAddress stationIP   = IPAddress(0, 0, 0, 0);
IPAddress gatewayIP   = IPAddress(192, 168, 42, 1);
IPAddress netMask     = IPAddress(255, 255, 255, 0);

#define USE_CONFIGURABLE_DNS      false   //true

IPAddress dns1IP      = gatewayIP;

///////////////////////////////////////////////////

#define USE_CUSTOM_AP_IP          false

IPAddress APStaticIP  = IPAddress(192, 168, 42, 1);
IPAddress APStaticGW  = IPAddress(192, 168, 42, 1);
IPAddress APStaticSN  = IPAddress(255, 255, 255, 0);

#include <WiFiManager_RP2040W.h>              //https://github.com/khoih-prog/WiFiManager_RP2040W

2. Using CORS (Cross-Origin Resource Sharing) feature

  1. To use CORS feature with default CORS Header "". Some WebBrowsers won't accept this allowing-all "" CORS Header.
// Default false for using only whenever necessary to avoid security issue
#define USING_CORS_FEATURE     true
  1. To use CORS feature with specific CORS Header "Your Access-Control-Allow-Origin". To be modified according to your specific Allowed-Origin.
// Default false for using only whenever necessary to avoid security issue
#define USING_CORS_FEATURE     true

...

#if USING_CORS_FEATURE
  RP2040W_WiFiManager.setCORSHeader("Your Access-Control-Allow-Origin");
#endif
  1. Not use CORS feature (default)
// Default false for using only whenever necessary to avoid security issue
#define USING_CORS_FEATURE     false

3. Using MultiWiFi auto(Re)connect feature

  1. In loop()
void check_WiFi(void)
{
  if ( (WiFi.status() != WL_CONNECTED) )
  {
    Serial.println("\nWiFi lost. Call connectMultiWiFi in loop");
    connectMultiWiFi();
  }
}

void check_status(void)
{
  static ulong checkwifi_timeout    = 0;

  static ulong current_millis;

#define WIFICHECK_INTERVAL    1000L

  current_millis = millis();
  
  // Check WiFi every WIFICHECK_INTERVAL (1) seconds.
  if ((current_millis > checkwifi_timeout) || (checkwifi_timeout == 0))
  {
    check_WiFi();
    checkwifi_timeout = current_millis + WIFICHECK_INTERVAL;
  }
}

void loop()
{
  // put your main code here, to run repeatedly
  check_status();
}


HOWTO Open Config Portal

  • When you want to open a config portal, just add
WiFiManager_RP2040W RP2040W_WiFiManager;

then later call

RP2040W_WiFiManager.startConfigPortal()

While in AP mode, connect to it using its SSID ("RP2040W_ConfigPortal") / Password ("RP2040W_Pass"), then open a browser to the AP IP, default 192.168.42.1, configure wifi then save. The WiFi connection information will be saved in non volatile memory. It will then reboot and autoconnect.

Once WiFi network information is saved in the RP2040W, it will try to autoconnect to WiFi every time it is started, without requiring any function calls in the sketch.



Examples

  1. ConfigOnDoubleReset
  2. ConfigOnStartup


So, how it works?

In ConfigPortal Mode, it starts an access point called RP2040W_ConfigPortal. Connect to it using the configurable password you can define in the code. For example, RP2040W_Pass (see examples):

// SSID and PW for Config Portal
String ssid = "RP2040W_ConfigPortal";
const char* password = "RP2040W_Pass";

After you connected, please, go to http://192.168.42.1, you'll see this Main page:

Select Information to enter the Info page where the board info will be shown (short page)

Select Configuration to enter this page where you can select an AP and specify its WiFi Credentials

Enter your credentials, then click Save. The WiFi Credentials will be saved and the board reboots to connect to the selected WiFi AP.

If you're already connected to a listed WiFi AP and don't want to change anything, just select Exit Portal from the Main page to reboot the board and connect to the previously-stored AP. The WiFi Credentials are still intact.



Documentation

Password protect the configuration Access Point

You can password protect the ConfigPortal AP. Simply add an SSID as the first parameter and the password as a second parameter to startConfigPortal. See the above examples. A short password seems to have unpredictable results so use one that's around 8 characters or more in length. The guidelines are that a wifi password must consist of 8 to 63 ASCII-encoded characters in the range of 32 to 126 (decimal)

RP2040W_WiFiManager.startConfigPortal( SSID , password )

Callbacks

Save settings

This gets called when custom parameters have been set AND a connection has been established. Use it to set a flag, so when all the configuration finishes, you can save the extra parameters somewhere.

RP2040W_WiFiManager.setSaveConfigCallback(saveConfigCallback);

saveConfigCallback declaration and example

//flag for saving data
bool shouldSaveConfig = false;

//callback notifying us of the need to save config
void saveConfigCallback () 
{
  Serial.println("Should save config");
  shouldSaveConfig = true;
}

ConfigPortal Timeout

If you need to set a timeout so the RP2040W doesn't hang waiting to be configured for ever.

#define CONFIG_PORTAL_TIMEOUT_SEC       120

RP2040W_WiFiManager.setConfigPortalTimeout(CONFIG_PORTAL_TIMEOUT_SEC);

which will wait 2 minutes (120 seconds). When the time passes, the startConfigPortal function will return and continue the sketch, unless you're accessing the Config Portal.

In this case, the startConfigPortal function will stay until you save config data or exit the Config Portal.

See ConfigOnDoubleReset example for a more complex version.



Custom Parameters

Many applications need configuration parameters like MQTT host and port, Blynk or emoncms tokens, etc. While it is possible to use WiFiManager_RP2040W to collect additional parameters it is better to read these parameters from a web service once WiFiManager_RP2040W has been used to connect to the internet.

To capture other parameters with WiFiManager_RP2040W is a lot more involved than all the other features and requires adding custom HTML to your form. If you want to do it with WiFiManager_RP2040W see the example ConfigOnDoubleReset

Custom IP Configuration

You can set a custom IP for both AP (access point, config mode) and STA (station mode, client mode, normal project state)

Custom Access Point IP Configuration (currently not working)

This will set your captive portal to a specific IP should you need/want such a feature. Add the following snippet before startConfigPortal()

//set custom ip for portal
RP2040W_WiFiManager.setAPStaticIPConfig(IPAddress(192,168,42,1), IPAddress(192,168,42,1), IPAddress(255,255,255,0));
Custom Station (client) Static IP Configuration

This will use the specified IP configuration instead of using DHCP in station mode.

RP2040W_WiFiManager.setSTAStaticIPConfig(IPAddress(192,168,2,180), IPAddress(192,168,2,1), IPAddress(255,255,255,0));

Custom HTML, CSS, Javascript

There are various ways in which you can inject custom HTML, CSS or Javascript into the ConfigPortal.

The options are:

  • inject custom head element You can use this to any html bit to the head of the ConfigPortal. If you add a <style> element, bare in mind it overwrites the included css, not replaces.
RP2040W_WiFiManager.setCustomHeadElement("<style>html{filter: invert(100%); -webkit-filter: invert(100%);}</style>");
  • inject a custom bit of html in the configuration form
WIO_WMParameter custom_text("<p>This is just a text paragraph</p>");
RP2040W_WiFiManager.addParameter(&custom_text);
  • inject a custom bit of html in a configuration form element Just add the bit you want added as the last parameter to the custom parameter constructor.
WIO_WMParameter custom_mqtt_server("server", "mqtt server", "iot.eclipse", 40, " readonly");

Filter Networks

You can filter networks based on signal quality and show/hide duplicate networks.

  • If you would like to filter low signal quality networks you can tell WiFiManager to not show networks below an arbitrary quality %;
RP2040W_WiFiManager.setMinimumSignalQuality(10);

will not show networks under 10% signal quality. If you omit the parameter it defaults to 8%;

  • You can also remove or show duplicate networks (default is remove). Use this function to show (or hide) all networks.
RP2040W_WiFiManager.setRemoveDuplicateAPs(false);


// Use from 0 to 4. Higher number, more debugging messages and memory usage.
#define _WIFIMGR_LOGLEVEL_ 4
#include <WiFi.h>
#include <WiFiMulti_Generic.h>
WiFiMulti_Generic wifiMulti;
#include <LittleFS.h>
#define CONFIG_FILENAME ("/wifi_cred.dat")
#define DRD_FLAG_DATA_SIZE 4
#define DRD_GENERIC_DEBUG true //false
#include <DoubleResetDetector_Generic.h> //https://github.com/khoih-prog/DoubleResetDetector_Generic
// Number of seconds after reset during which a
// subseqent reset will be considered a double reset.
#define DRD_TIMEOUT 10
// RTC Memory Address for the DoubleResetDetector to use
#define DRD_ADDRESS 0
//DoubleResetDetector_Generic drd(DRD_TIMEOUT, DRD_ADDRESS);
DoubleResetDetector_Generic* drd;//////
// SSID and PW for Config Portal
String ssid = "RP2040W_ConfigPortal";
const char* password = "RP2040W_Pass";
// SSID and PW for your Router
String Router_SSID;
String Router_Pass;
#define LED_ON HIGH
#define LED_OFF LOW
#define MIN_AP_PASSWORD_SIZE 8
#define SSID_MAX_LEN 32
//From v1.0.10, WPA2 passwords can be up to 63 characters long.
#define PASS_MAX_LEN 64
typedef struct
{
char wifi_ssid[SSID_MAX_LEN];
char wifi_pw [PASS_MAX_LEN];
} WiFi_Credentials;
typedef struct
{
String wifi_ssid;
String wifi_pw;
} WiFi_Credentials_String;
#define NUM_WIFI_CREDENTIALS 2
typedef struct
{
WiFi_Credentials WiFi_Creds [NUM_WIFI_CREDENTIALS];
} WM_Config;
WM_Config WM_config;
//////
// Indicates whether ESP has WiFi credentials saved from previous session, or double reset detected
bool initialConfig = false;
//////
// Use false if you don't like to display Available Pages in Information Page of Config Portal
// Comment out or use true to display Available Pages in Information Page of Config Portal
// Must be placed before #include <WiFiManager_RP2040W.h>
#define USE_AVAILABLE_PAGES false
// New in v1.0.11
#define USING_CORS_FEATURE true
///////////////////////////////////////////////////
// Use DHCP
#warning Using DHCP IP
IPAddress stationIP = IPAddress(0, 0, 0, 0);
IPAddress gatewayIP = IPAddress(192, 168, 42, 1);
IPAddress netMask = IPAddress(255, 255, 255, 0);
#define USE_CONFIGURABLE_DNS false //true
IPAddress dns1IP = gatewayIP;
///////////////////////////////////////////////////
#define USE_CUSTOM_AP_IP false
IPAddress APStaticIP = IPAddress(192, 168, 42, 1);
IPAddress APStaticGW = IPAddress(192, 168, 42, 1);
IPAddress APStaticSN = IPAddress(255, 255, 255, 0);
#include <WiFiManager_RP2040W.h> //https://github.com/khoih-prog/WiFiManager_RP2040W
// Function Prototypes
uint8_t connectMultiWiFi();
///////////////////////////////////////////
/******************************************
* // Defined in WiFiManager_RP2040W.hpp
typedef struct
{
IPAddress _ap_static_ip;
IPAddress _ap_static_gw;
IPAddress _ap_static_sn;
} WiFi_AP_IPConfig;
typedef struct
{
IPAddress _sta_static_ip;
IPAddress _sta_static_gw;
IPAddress _sta_static_sn;
#if USE_CONFIGURABLE_DNS
IPAddress _sta_static_dns1;
#endif
} WiFi_STA_IPConfig;
******************************************/
WiFi_AP_IPConfig WM_AP_IPconfig;
WiFi_STA_IPConfig WM_STA_IPconfig;
void initAPIPConfigStruct(WiFi_AP_IPConfig &in_WM_AP_IPconfig)
{
in_WM_AP_IPconfig._ap_static_ip = APStaticIP;
in_WM_AP_IPconfig._ap_static_gw = APStaticGW;
in_WM_AP_IPconfig._ap_static_sn = APStaticSN;
}
void initSTAIPConfigStruct(WiFi_STA_IPConfig &in_WM_STA_IPconfig)
{
in_WM_STA_IPconfig._sta_static_ip = stationIP;
in_WM_STA_IPconfig._sta_static_gw = gatewayIP;
in_WM_STA_IPconfig._sta_static_sn = netMask;
#if USE_CONFIGURABLE_DNS
in_WM_STA_IPconfig._sta_static_dns1 = dns1IP;
#endif
}
void displayIPConfigStruct(WiFi_STA_IPConfig in_WM_STA_IPconfig)
{
LOGERROR3(("stationIP ="), in_WM_STA_IPconfig._sta_static_ip, ", gatewayIP =", in_WM_STA_IPconfig._sta_static_gw);
LOGERROR1(("netMask ="), in_WM_STA_IPconfig._sta_static_sn);
#if USE_CONFIGURABLE_DNS
LOGERROR1(("dns1IP ="), in_WM_STA_IPconfig._sta_static_dns1);
#endif
}
void configWiFi(WiFi_STA_IPConfig in_WM_STA_IPconfig)
{
#if USE_CONFIGURABLE_DNS
// Set static IP, Gateway, Subnetmask, DNS1
WiFi.config(in_WM_STA_IPconfig._sta_static_ip, in_WM_STA_IPconfig._sta_static_gw, in_WM_STA_IPconfig._sta_static_sn, in_WM_STA_IPconfig._sta_static_dns1);
#else
// Set static IP, Gateway, Subnetmask, Use auto DNS1
WiFi.config(in_WM_STA_IPconfig._sta_static_ip, in_WM_STA_IPconfig._sta_static_gw, in_WM_STA_IPconfig._sta_static_sn);
#endif
}
///////////////////////////////////////////
void heartBeatPrint()
{
static int num = 1;
if (WiFi.status() == WL_CONNECTED)
Serial.print("H"); // H means connected to WiFi
else
Serial.print("F"); // F means not connected to WiFi
if (num == 80)
{
Serial.println();
num = 1;
}
else if (num++ % 10 == 0)
{
Serial.print(" ");
}
}
void check_WiFi()
{
if ( (WiFi.status() != WL_CONNECTED) )
{
Serial.println("\nWiFi lost. Call connectMultiWiFi in loop");
connectMultiWiFi();
}
}
void check_status()
{
static ulong checkstatus_timeout = 0;
static ulong checkwifi_timeout = 0;
static ulong current_millis;
#define WIFICHECK_INTERVAL 1000L
#define HEARTBEAT_INTERVAL 10000L
current_millis = millis();
// Check WiFi every WIFICHECK_INTERVAL (1) seconds.
if ((current_millis > checkwifi_timeout) || (checkwifi_timeout == 0))
{
check_WiFi();
checkwifi_timeout = current_millis + WIFICHECK_INTERVAL;
}
// Print hearbeat every HEARTBEAT_INTERVAL (10) seconds.
if ((current_millis > checkstatus_timeout) || (checkstatus_timeout == 0))
{
heartBeatPrint();
checkstatus_timeout = current_millis + HEARTBEAT_INTERVAL;
}
}
bool loadConfigData()
{
File file = LittleFS.open(CONFIG_FILENAME, "r");
LOGERROR(("LoadWiFiCfgFile "));
memset((void*) &WM_config, 0, sizeof(WM_config));
// New in v1.4.0
memset((void*) &WM_STA_IPconfig, 0, sizeof(WM_STA_IPconfig));
//////
if (file)
{
file.readBytes((char *) &WM_config, sizeof(WM_config));
file.readBytes((char *) &WM_STA_IPconfig, sizeof(WM_STA_IPconfig));
file.close();
LOGERROR(("OK"));
return true;
}
else
{
LOGERROR(("failed"));
return false;
}
}
void saveConfigData()
{
File file = LittleFS.open(CONFIG_FILENAME, "w");
LOGERROR(("SaveWiFiCfgFile "));
if (file)
{
file.write((uint8_t*) &WM_config, sizeof(WM_config));
file.write((uint8_t*) &WM_STA_IPconfig, sizeof(WM_STA_IPconfig));
file.close();
LOGERROR(("OK"));
}
else
{
LOGERROR(("failed"));
}
}
uint8_t connectMultiWiFi()
{
// This better be 0 to shorten the connect time
#define WIFI_MULTI_1ST_CONNECT_WAITING_MS 0
#define WIFI_MULTI_CONNECT_WAITING_MS 100L
uint8_t status;
LOGERROR(("ConnectMultiWiFi with :"));
if ( (Router_SSID != "") && (Router_Pass != "") )
{
LOGERROR3(("* LittleFS-stored Router_SSID = "), Router_SSID, (", Router_Pass = "), Router_Pass );
}
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
// Don't permit NULL SSID and password len < MIN_AP_PASSWORD_SIZE (8)
if ( (String(WM_config.WiFi_Creds[i].wifi_ssid) != "") && (strlen(WM_config.WiFi_Creds[i].wifi_pw) >= MIN_AP_PASSWORD_SIZE) )
{
LOGERROR3(("* Additional SSID = "), WM_config.WiFi_Creds[i].wifi_ssid, (", PW = "), WM_config.WiFi_Creds[i].wifi_pw );
}
}
LOGERROR(("Connecting MultiWifi..."));
//WiFi.mode(WIFI_STA);
int i = 0;
status = wifiMulti.run();
delay(WIFI_MULTI_1ST_CONNECT_WAITING_MS);
while ( ( i++ < 10 ) && ( status != WL_CONNECTED ) )
{
status = wifiMulti.run();
if ( status == WL_CONNECTED )
break;
else
delay(WIFI_MULTI_CONNECT_WAITING_MS);
}
if ( status == WL_CONNECTED )
{
LOGERROR1(("WiFi connected after time: "), i);
LOGERROR3(("SSID:"), WiFi.SSID(), (",RSSI="), WiFi.RSSI());
LOGERROR1(("IP address:"), WiFi.localIP() );
}
else
LOGERROR(("WiFi not connected"));
return status;
}
void processDataFromCP(WiFiManager_RP2040W &RP2040W_WiFiManager)
{
// Stored for later usage, from v1.1.0, but clear first
memset(&WM_config, 0, sizeof(WM_config));
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
String tempSSID = RP2040W_WiFiManager.getSSID(i);
String tempPW = RP2040W_WiFiManager.getPW(i);
if (strlen(tempSSID.c_str()) < sizeof(WM_config.WiFi_Creds[i].wifi_ssid) - 1)
strcpy(WM_config.WiFi_Creds[i].wifi_ssid, tempSSID.c_str());
else
strncpy(WM_config.WiFi_Creds[i].wifi_ssid, tempSSID.c_str(), sizeof(WM_config.WiFi_Creds[i].wifi_ssid) - 1);
if (strlen(tempPW.c_str()) < sizeof(WM_config.WiFi_Creds[i].wifi_pw) - 1)
strcpy(WM_config.WiFi_Creds[i].wifi_pw, tempPW.c_str());
else
strncpy(WM_config.WiFi_Creds[i].wifi_pw, tempPW.c_str(), sizeof(WM_config.WiFi_Creds[i].wifi_pw) - 1);
}
}
void setup()
{
// put your setup code here, to run once:
// initialize the LED digital pin as an output.
pinMode(PIN_LED, OUTPUT);
Serial.begin(115200);
while (!Serial && millis() < 5000);
Serial.print("\nStarting ConfigOnDoubleReset with DoubleResetDetect on "); Serial.println(BOARD_NAME);
Serial.println(WIFI_MANAGER_RP2040W_VERSION);
drd = new DoubleResetDetector_Generic(DRD_TIMEOUT, DRD_ADDRESS);
unsigned long startedAt = millis();
//Local intialization. Once its business is done, there is no need to keep it around
WiFiManager_RP2040W RP2040W_WiFiManager;
RP2040W_WiFiManager.setDebugOutput(true);
// Use only to erase stored WiFi Credentials
//RP2040W_WiFiManager.resetSettings();
RP2040W_WiFiManager.setMinimumSignalQuality(-1);
#if USING_CORS_FEATURE
RP2040W_WiFiManager.setCORSHeader("Your Access-Control-Allow-Origin");
#endif
if (drd->detectDoubleReset())
{
// DRD, disable timeout.
RP2040W_WiFiManager.setConfigPortalTimeout(0);
LOGERROR("Open Config Portal without Timeout: Double Reset Detected");
initialConfig = true;
}
else
{
LOGERROR("No Double Reset Detected");
}
// We can't use WiFi.SSID() in ESP32as it's only valid after connected.
// SSID and Password stored in ESP32 wifi_ap_record_t and wifi_config_t are also cleared in reboot
// Have to create a new function to store in EEPROM/SPIFFS for this purpose
Router_SSID = RP2040W_WiFiManager.WiFi_SSID();
//Router_Pass = RP2040W_WiFiManager.WiFi_Pass();
//Remove this line if you do not want to see WiFi password printed
//Serial.println("Stored: SSID = " + Router_SSID + ", Pass = " + Router_Pass);
// If no stored WiFi data, load from saved EEPROM
if ( (Router_SSID == "") /*|| (Router_Pass == "")*/ )
{
// Load stored data and verify CheckSum
if (loadConfigData())
{
// Don't permit NULL SSID and password len < MIN_AP_PASSWORD_SIZE (8)
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
// Don't permit NULL SSID and password len < MIN_AP_PASSWORD_SIZE (8)
if ( (String(WM_config.WiFi_Creds[i].wifi_ssid) != "") && (strlen(WM_config.WiFi_Creds[i].wifi_pw) >= MIN_AP_PASSWORD_SIZE) )
{
LOGERROR3(("* Add SSID = "), WM_config.WiFi_Creds[i].wifi_ssid, (", PW = "), WM_config.WiFi_Creds[i].wifi_pw );
wifiMulti.addAP(WM_config.WiFi_Creds[i].wifi_ssid, WM_config.WiFi_Creds[i].wifi_pw);
}
}
if ( ( WiFi.status() != WL_CONNECTED ) && !initialConfig )
{
LOGERROR(("ConnectWiFi in setup"));
if ( connectMultiWiFi() == WL_CONNECTED )
{
Serial.println("Connected to " + Router_SSID);
return;
}
}
}
}
// From v1.1.0, Don't permit NULL password
if ( (Router_SSID != "") && (Router_Pass != "") && !initialConfig )
{
LOGERROR3(("* Add SSID = "), Router_SSID, (", PW = "), Router_Pass);
wifiMulti.addAP(Router_SSID.c_str(), Router_Pass.c_str());
RP2040W_WiFiManager.setConfigPortalTimeout(120); //If no access point name has been previously entered disable timeout.
LOGERROR("Got stored Credentials. Timeout 120s for Config Portal");
}
else
{
LOGERROR("Open Config Portal without Timeout: No stored Credentials.");
digitalWrite(PIN_LED, LED_ON); // Turn led on as we are in configuration mode.
initialConfig = true;
}
if (initialConfig)
{
LOGERROR("Starting configuration portal.");
digitalWrite(PIN_LED, LED_ON); // turn the LED on by making the voltage LOW to tell us we are in configuration mode.
//sets timeout in seconds until configuration portal gets turned off.
//If not specified device will remain in configuration mode until
//switched off via webserver or device is restarted.
//RP2040W_WiFiManager.setConfigPortalTimeout(600);
// Starts an access point
if (!RP2040W_WiFiManager.startConfigPortal((const char *) ssid.c_str(), password))
{
LOGERROR("Not connected to WiFi but continuing anyway.");
}
else
{
LOGERROR("WiFi connected...yeey :)");
}
// Stored for later usage, from v1.1.0, but clear first
memset(&WM_config, 0, sizeof(WM_config));
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
String tempSSID = RP2040W_WiFiManager.getSSID(i);
String tempPW = RP2040W_WiFiManager.getPW(i);
if (strlen(tempSSID.c_str()) < sizeof(WM_config.WiFi_Creds[i].wifi_ssid) - 1)
strcpy(WM_config.WiFi_Creds[i].wifi_ssid, tempSSID.c_str());
else
strncpy(WM_config.WiFi_Creds[i].wifi_ssid, tempSSID.c_str(), sizeof(WM_config.WiFi_Creds[i].wifi_ssid) - 1);
if (strlen(tempPW.c_str()) < sizeof(WM_config.WiFi_Creds[i].wifi_pw) - 1)
strcpy(WM_config.WiFi_Creds[i].wifi_pw, tempPW.c_str());
else
strncpy(WM_config.WiFi_Creds[i].wifi_pw, tempPW.c_str(), sizeof(WM_config.WiFi_Creds[i].wifi_pw) - 1);
// Don't permit NULL SSID and password len < MIN_AP_PASSWORD_SIZE (8)
if ( (String(WM_config.WiFi_Creds[i].wifi_ssid) != "") && (strlen(WM_config.WiFi_Creds[i].wifi_pw) >= MIN_AP_PASSWORD_SIZE) )
{
LOGERROR3(("* Add SSID = "), WM_config.WiFi_Creds[i].wifi_ssid, (", PW = "), WM_config.WiFi_Creds[i].wifi_pw );
wifiMulti.addAP(WM_config.WiFi_Creds[i].wifi_ssid, WM_config.WiFi_Creds[i].wifi_pw);
}
}
saveConfigData();
}
digitalWrite(PIN_LED, LED_OFF); // Turn led off as we are not in configuration mode.
startedAt = millis();
if (!initialConfig)
{
// Load stored data, the addAP ready for MultiWiFi reconnection
loadConfigData();
for (uint8_t i = 0; i < NUM_WIFI_CREDENTIALS; i++)
{
// Don't permit NULL SSID and password len < MIN_AP_PASSWORD_SIZE (8)
if ( (String(WM_config.WiFi_Creds[i].wifi_ssid) != "") && (strlen(WM_config.WiFi_Creds[i].wifi_pw) >= MIN_AP_PASSWORD_SIZE) )
{
LOGERROR3(("* Add SSID = "), WM_config.WiFi_Creds[i].wifi_ssid, (", PW = "), WM_config.WiFi_Creds[i].wifi_pw );
wifiMulti.addAP(WM_config.WiFi_Creds[i].wifi_ssid, WM_config.WiFi_Creds[i].wifi_pw);
}
}
if ( WiFi.status() != WL_CONNECTED )
{
LOGERROR("ConnectMultiWiFi in setup");
connectMultiWiFi();
}
}
Serial.print("After waiting ");
Serial.print((float) (millis() - startedAt) / 1000L);
Serial.print(" secs more in setup(), connection result is ");
if (WiFi.status() == WL_CONNECTED)
{
Serial.println("connected");
}
else
Serial.println(RP2040W_WiFiManager.getStatus(WiFi.status()));
}
void loop()
{
// Call the double reset detector loop method every so often,
// so that it can recognise when the timeout expires.
// You can also call drd.stop() when you wish to no longer
// consider the next reset as a double reset.
drd->loop();
// put your main code here, to run repeatedly
check_status();
}



Debug Terminal Output Samples

1. ConfigOnDoubleReset

This is terminal debug output when running ConfigOnDoubleReset on RASPBERRY_PI_PICO_W.

Config Portal (CP) was requested to input and save WiFi Credentials. The boards then connected to WiFi successfully. Then DRD was detected, CP was again requested to update WiFi Credentials.

DRD detected, Config Portal will open

Starting ConfigOnDoubleReset with DoubleResetDetect on RASPBERRY_PI_PICO_W
WiFiManager_RP2040W v1.0.0
[WM] RFC925 Hostname = RP2040W-179A4E
[WM] Set CORS Header to :  Your Access-Control-Allow-Origin
LittleFS Flag read = 0xd0d01234
Flag read = 0xd0d01234
doubleResetDetected
Saving to DRD file : 0xd0d04321
Saving DRD file OK
LittleFS Flag read = 0xd0d04321
ClearFlag write = 0xd0d04321
[WM] Open Config Portal without Timeout: Double Reset Detected
[WM] LoadWiFiCfgFile 
[WM] OK
[WM] * Add SSID =  HueNet1 , PW =  12345678
[WM] * Add SSID =  HueNet2 , PW =  12345678
[WM] Open Config Portal without Timeout: No stored Credentials.
[WM] Starting configuration portal.
[WM] WiFi.waitForConnectResult Done
[WM] SET AP
[WM] Configuring AP SSID = RP2040W_ConfigPortal
[WM] AP PWD = RP2040W_Pass
[WM] AP IP address = 192.168.42.1
[WM] HTTP server started
[WM] startConfigPortal : Enter loop
[WM] Handle root
[WM] captivePortal: hostHeader =  192.168.42.1
[WM] Handle WiFi
[WM] Scanning Network
[WM] scanWifiNetworks: Done, Scanned Networks n = 13
[WM] Sorting
[WM] Removing Dup
[WM] DUP AP: 
[WM] Index = 0
[WM] SSID = HueNet
[WM] RSSI = -32
[WM] Index = 1
[WM] SSID = HueNetTek
[WM] RSSI = -46
[WM] Index = 2
[WM] SSID = HueNet1
[WM] RSSI = -48
[WM] Index = 3
[WM] SSID = HueNet2
[WM] RSSI = -58
[WM] Index = 4
[WM] SSID = SmartRG-02a2
[WM] RSSI = -84
[WM] Index = 5
[WM] SSID = Guest5655
[WM] RSSI = -84
[WM] Index = 6
[WM] SSID = 
[WM] RSSI = -86
[WM] Index = 8
[WM] SSID = ESP151CD5
[WM] RSSI = -92
[WM] Index = 9
[WM] SSID = Rogers 786
[WM] RSSI = -93
[WM] Index = 10
[WM] SSID = Rogers5651
[WM] RSSI = -93
[WM] Index = 11
[WM] SSID = FishBowl
[WM] RSSI = -93
[WM] Index = 12
[WM] SSID = FishTank
[WM] RSSI = -95
[WM] Static IP = (IP unset)
[WM] Sent config page
[WM] WiFi save
[WM] TZ name = America/Toronto
[WM] Sent wifi save page
[WM] Connecting to new AP
[WM] Already connected. Bailing out.
[WM] WiFi connected...yeey :)
[WM] * Add SSID =  HueNet1 , PW =  12345678
[WM] * Add SSID =  HueNet2 , PW =  12345678
[WM] SaveWiFiCfgFile 
[WM] OK
After waiting 0.00 secs more in setup(), connection result is connected
[WM] freeing allocated params!
HH

Start normally with correct config data

Starting ConfigOnDoubleReset with DoubleResetDetect on RASPBERRY_PI_PICO_W
WiFiManager_RP2040W v1.0.0
[WM] RFC925 Hostname = RP2040W-11BD0A
[WM] Set CORS Header to :  Your Access-Control-Allow-Origin
LittleFS Flag read = 0xd0d04321
Flag read = 0xd0d04321
No doubleResetDetected
Saving DOUBLERESETDETECTOR_FLAG to DRD file : 0xd0d01234
Saving DRD file OK
SetFlag write = 0xd0d01234
[WM] No Double Reset Detected
[WM] LoadWiFiCfgFile 
[WM] OK
[WM] * Add SSID =  HueNet1 , PW =  12345678
[WM] * Add SSID =  HueNet2 , PW =  12345678
[WM] ConnectWiFi in setup
[WM] ConnectMultiWiFi with :
[WM] * Additional SSID =  HueNet1 , PW =  12345678
[WM] * Additional SSID =  HueNet2 , PW =  12345678
[WM] Connecting MultiWifi...
[WM] WiFi connected after time:  2
[WM] SSID: HueNet1 ,RSSI= 0
[WM] IP address: 192.168.2.180

[WM] freeing allocated params!
Stop doubleResetDetecting
Saving to DRD file : 0xd0d04321
Saving DRD file OK
LittleFS Flag read = 0xd0d04321
ClearFlag write = 0xd0d04321
HHH

2. ConfigOnStartup

This is terminal debug output when running ConfigOnStartup on RASPBERRY_PI_PICO_W.

This example will open a configuration portal for CONFIG_PORTAL_TIMEOUT_SEC seconds when first powered up if the boards has stored WiFi Credentials.

Otherwise, it'll stay indefinitely in ConfigPortal until getting WiFi Credentials and connecting to WiFi

No stored Credentials, Config Portal will open without timeout

Starting ConfigOnStartup on RASPBERRY_PI_PICO_W
WiFiManager_RP2040W v1.0.0
[WM] RFC925 Hostname = RP2040W-221895
[WM] Set CORS Header to :  Your Access-Control-Allow-Origin
[WM] LoadWiFiCfgFile 
[WM] failed
[WM] Stored: SSID =  , Pass = 
[WM] Opening configuration portal.
[WM] Open Config Portal without Timeout: No stored Credentials.
[WM] Starting configuration portal.
[WM] WiFi.waitForConnectResult Done
[WM] SET AP
[WM] Configuring AP SSID = RP2040W_ConfigPortal
[WM] AP PWD = RP2040W_Pass
[WM] AP IP address = 192.168.42.1
[WM] HTTP server started
[WM] startConfigPortal : Enter loop

Restarted, Config Portal will open with 120s timeout

Starting ConfigOnStartup on RASPBERRY_PI_PICO_W
WiFiManager_RP2040W v1.0.0
[WM] RFC925 Hostname = RP2040W-148FE7
[WM] Set CORS Header to :  Your Access-Control-Allow-Origin
[WM] LoadWiFiCfgFile 
[WM] OK
[WM] * Add SSID =  HueNet1 , PW =  12345678
[WM] * Add SSID =  HueNet2 , PW =  12345678
[WM] Stored: SSID = HueNet2 , Pass = 12345678
[WM] Opening configuration portal.
[WM] * Add SSID =  HueNet2 , PW =  12345678
[WM] Got stored Credentials. Timeout for Config Portal = 120
[WM] Starting configuration portal.
[WM] WiFi.waitForConnectResult Done
[WM] SET AP
[WM] Configuring AP SSID = RP2040W_ConfigPortal
[WM] AP PWD = RP2040W_Pass
[WM] AP IP address = 192.168.42.1
[WM] HTTP server started
[WM] startConfigPortal : Enter loop
[WM] Connected after waiting (s) : 3.26
[WM] Local ip = (IP unset)
[WM] Timed out connection result: WL_CONNECT_FAILED
[WM] Not connected to WiFi but continuing anyway.
[WM] LoadWiFiCfgFile 
[WM] OK
[WM] * Add SSID =  HueNet1 , PW =  12345678
[WM] * Add SSID =  HueNet2 , PW =  12345678
[WM] ConnectMultiWiFi in setup
[WM] ConnectMultiWiFi with :
[WM] * LittleFS-stored Router_SSID =  HueNet2 , Router_Pass =  12345678
[WM] * Additional SSID =  HueNet1 , PW =  12345678
[WM] * Additional SSID =  HueNet2 , PW =  12345678
[WM] Connecting MultiWifi...
[WM] WiFi connected after time:  2
[WM] SSID: HueNet1 ,RSSI= 0
[WM] IP address: 192.168.2.180
After waiting 14.00 secs more in setup(), connection result is connected
Local IP = 192.168.2.180
[WM] freeing allocated params!
H


Debug

Debug is enabled by default on Serial. To disable, add before startConfigPortal()

RP2040W_WiFiManager.setDebugOutput(false);

You can also change the debugging level from 0 to 4

// Use from 0 to 4. Higher number, more debugging messages and memory usage.
#define _WIFIMGR_LOGLEVEL_    3


Troubleshooting

If you get compilation errors, more often than not, you may need to install a newer version of the arduino-pico core for Arduino.

Sometimes, the library will only work if you update the arduino-pico core to the latest version because I am using newly added functions.


Issues

Submit issues to: WiFiManager_RP2040W issues



TO DO

  1. Bug Searching and Killing

DONE

  1. Add support to RP2040W built-in CYW43439 WiFi using arduino-pico core


Contributions and Thanks

  1. Based on and modified from Tzapu and KenTaylor's version
Tzapu
⭐️ Tzapu

kentaylor
⭐️ Ken Taylor


Contributing

If you want to contribute to this project:

  • Report bugs and errors
  • Ask for enhancements
  • Create issues and pull requests
  • Tell other people about this library

License and credits

  • The library is licensed under MIT

Copyright

Copyright 2022- Khoi Hoang

About

MultiWiFi/Credentials Manager for RP2040W boards using built-in CYW43439 WiFi. Powerful-yet-simple-to-use feature to enable adding dynamic custom parameters. Library to configure MultiWiFi/Credentials at runtime for RP2040W boards, using CYW43439 WiFi. You can also specify DHCP HostName and STA IP. Use much less memory compared to full-fledge WiFiManager. Config Portal will be auto-adjusted to match the number of dynamic custom parameters. Optional default Credentials to be autoloaded into Config Portal to use or change instead of manually input. Credentials are saved in LittleFS. New powerful-yet-simple-to-use feature to enable adding dynamic custom parameters from sketch and input using the same Config Portal. DoubleDetectDetector as well as Virtual Switches feature permits entering Config Portal as requested. Now using WiFiMulti_Generic library

License:MIT License


Languages

Language:C++ 50.1%Language:C 49.8%Language:Shell 0.1%