Compare commits

...
6 Commits
Author SHA1 Message Date
alex 63b0291961 fix: declare async TCP PlatformIO dependencies 2026-08-26 01:06:22 +10:00
alex 3b07903bf4 docs: clarify provisioning and release workflow 2026-08-25 09:01:06 +10:00
alex 1f196895ce Update DFTE dependency to 1.0.2 2026-08-24 22:42:16 +10:00
alex 35e02f54e5 Redact WiFiManager diagnostic values 2026-08-24 22:17:22 +10:00
alex e2495843ff Release WiFiManager 3.0.0 2026-08-24 22:05:16 +10:00
alex 95e9f9a751 Stream portal templates via TemplateEngineAsyncWeb chunked wrapper
Use the DFTE async integration helper so portal HTML chunk callbacks handle
zero-length buffers and no-progress retries without terminating transfers
early on constrained hosts.
2026-05-06 00:44:56 +10:00
14 changed files with 334 additions and 55 deletions
+23
View File
@@ -0,0 +1,23 @@
name: Build
on:
push:
pull_request:
permissions:
contents: read
jobs:
compile-tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
environment: ["esp8266", "esp32"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: python -m pip install --upgrade platformio==6.1.19
- run: ./scripts/compile-check.sh --platform ${{ matrix.environment }}
+23
View File
@@ -0,0 +1,23 @@
name: Publish release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: python -m pip install --upgrade platformio==6.1.19
- run: ./scripts/prepare-release.sh "$GITHUB_REF_NAME"
- run: gh release create "$GITHUB_REF_NAME" --generate-notes --title "$GITHUB_REF_NAME"
env:
GH_TOKEN: ${{ github.token }}
+20
View File
@@ -0,0 +1,20 @@
# Changelog
## 3.0.3
- Declare the ESP8266 and ESP32 async TCP libraries explicitly in the PlatformIO package manifest so clean consumers resolve the headers required by the asynchronous portal.
## 3.0.2
- Update the pinned DFTE dependency to the iterator-lifecycle and configuration-safe 1.0.2 release.
## 3.0.1
- Redact WiFi, AP, portal-form, and custom-parameter values from diagnostic logs.
## 3.0.0
- Establish `device-framework` as the independently maintained canonical branch.
- Add safe default parameter construction and allocation-failure handling.
- Pin the DFTE dependency used by PlatformIO builds.
+86 -12
View File
@@ -8,6 +8,38 @@ If you are evaluating this fork, assume that core web-portal architecture has ch
**This repository:** [alexhopeoconnor/WiFiManager](https://github.com/alexhopeoconnor/WiFiManager)
**Upstream:** [tzapu/WiFiManager](https://github.com/tzapu/WiFiManager)
## Basic provisioning
For a standalone Arduino project, create one long-lived `WiFiManager` and call
`autoConnect` during setup. It first tries saved station credentials; when that
fails it starts the configuration portal with the supplied AP name and password.
```cpp
#include <Arduino.h>
#include <WiFiManager.h>
WiFiManager wifi;
void setup() {
Serial.begin(115200);
wifi.setConfigPortalTimeout(180); // seconds; 0 leaves it open
if (!wifi.autoConnect("Device Setup", "change-me")) {
ESP.restart();
}
}
void loop() {
// Normal application work after WiFi is connected.
}
```
The AP password must meet the Wi-Fi password requirements. For applications
built on DeviceFramework, configure `setConfigDevicePassword(...)` instead:
DeviceFramework passes the same validated password to WiFiManager, Arduino OTA,
HTTP Basic authentication, and WebSerial.
## Breaking Changes
This fork intentionally modernizes and restructures the configuration portal.
@@ -168,32 +200,74 @@ Notes:
This fork depends on **DFTE** ([Device Framework Template Engine](https://github.com/alexhopeoconnor/DFTE)) and **ESP32Async/ESPAsyncWebServer**.
For this repo's `platformio.ini` test setup, DFTE is expected as a sibling checkout:
The published `library.json` resolves DFTE from the maintained, pinned
`v1.0.2` Git tag. No sibling checkout is required to consume WiFiManager.
When developing both libraries together, use a local `symlink://` or
`file://` dependency in your own ignored PlatformIO override.
```ini
lib_deps =
ESP32Async/ESPAsyncWebServer@3.9.1
symlink://../DFTE
```
## Installation
Use the release tag and list the asynchronous web/TCP packages at project level.
PlatformIO installs the manifest dependencies automatically, but its dependency
finder requires these headers to be direct project dependencies before it adds
their include paths:
```ini
[env:your_environment]
platform = espressif8266 ; or espressif32
board = d1_mini ; your board
[common]
lib_deps =
WiFiManager=https://github.com/alexhopeoconnor/WiFiManager.git#v3.0.3
ESP32Async/ESPAsyncWebServer@3.9.1
[env:esp8266]
extends = common
platform = espressif8266
board = d1_mini
framework = arduino
lib_deps =
https://github.com/alexhopeoconnor/WiFiManager.git
${common.lib_deps}
ESP32Async/ESPAsyncTCP@2.0.0
[env:esp32]
extends = common
lib_deps =
platform = espressif32
board = esp32dev
framework = arduino
${common.lib_deps}
ESP32Async/AsyncTCP@3.4.9
```
Or a local path:
The text after `#` is a Git ref. PlatformIO clones the repository and checks
out that tag; it does not download a GitHub Release asset. A tag gives a
reproducible library input. Use a local checkout only while actively changing
the library:
```ini
lib_deps =
file:///path/to/WiFiManager
WiFiManager=file:///path/to/WiFiManager
```
## Tests and releases
The CI workflow compiles a minimal clean consumer project for both supported
targets. These commands need no connected board:
```bash
./scripts/compile-check.sh --platform esp8266
./scripts/compile-check.sh --platform esp32
```
Before publishing a version, update `library.json`, `CHANGELOG.md`, and this
README, run both checks, then create the annotated tag:
```bash
./scripts/prepare-release.sh vMAJOR.MINOR.PATCH --tag
```
Push the branch and tag. GitHub Actions validates the PlatformIO package again
and creates the GitHub Release from that tag.
## AI Assistance Notice
Parts of this codebase have been developed and refactored with the aid of AI coding agents under human direction and review.
@@ -34,7 +34,9 @@ class WiFiManagerParameter {
WiFiManagerParameter(const char *id, const char *label, const char *defaultValue, int length, const char *custom);
WiFiManagerParameter(const char *id, const char *label, const char *defaultValue, int length, const char *custom, int labelPlacement);
virtual ~WiFiManagerParameter();
// WiFiManagerParameter& operator=(const WiFiManagerParameter& rhs);
WiFiManagerParameter(const WiFiManagerParameter&) = delete;
WiFiManagerParameter& operator=(const WiFiManagerParameter&) = delete;
const char *getID() const;
const char *getValue() const;
@@ -48,7 +50,6 @@ class WiFiManagerParameter {
protected:
void init(const char *id, const char *label, const char *defaultValue, int length, const char *custom, int labelPlacement);
WiFiManagerParameter& operator=(const WiFiManagerParameter&);
const char *_id;
const char *_label;
char *_value;
+19 -9
View File
@@ -63,6 +63,9 @@ bool normalizeHostname(String& hostname) {
* @return true if added successfully, false on error
*/
bool WiFiManager::portalAddParameter(WiFiManagerParameter *p) {
if (!p) {
return false;
}
// check param id is valid, unless null
if(p->getID()){
@@ -82,18 +85,25 @@ bool WiFiManager::portalAddParameter(WiFiManagerParameter *p) {
log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("allocating params bytes:"),_max_params * sizeof(WiFiManagerParameter*));
#endif
_params = (WiFiManagerParameter**)malloc(_max_params * sizeof(WiFiManagerParameter*));
if (_params == NULL) {
#ifndef WM_NO_LOG
log(WiFiManagerLogLevel::Error, kWiFiMgrLogSubsystem,F("[ERROR] failed to allocate params"));
#endif
return false;
}
}
// resize the params array by increment of WIFI_MANAGER_MAX_PARAMS
// Resize only after a successful allocation so _max_params always matches storage.
if(_paramsCount == _max_params){
_max_params += WIFI_MANAGER_MAX_PARAMS;
const size_t expandedMaxParams = _max_params + WIFI_MANAGER_MAX_PARAMS;
#ifndef WM_NO_LOG
log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("Updated _max_params:"),_max_params);
log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("re-allocating params bytes:"),_max_params * sizeof(WiFiManagerParameter*));
log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("Updated _max_params:"),expandedMaxParams);
log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("re-allocating params bytes:"),expandedMaxParams * sizeof(WiFiManagerParameter*));
#endif
WiFiManagerParameter** new_params = (WiFiManagerParameter**)realloc(_params, _max_params * sizeof(WiFiManagerParameter*));
WiFiManagerParameter** new_params = (WiFiManagerParameter**)realloc(_params, expandedMaxParams * sizeof(WiFiManagerParameter*));
if (new_params != NULL) {
_params = new_params;
_max_params = expandedMaxParams;
} else {
#ifndef WM_NO_LOG
log(WiFiManagerLogLevel::Error, kWiFiMgrLogSubsystem,F("[ERROR] failed to realloc params, size not increased!"));
@@ -976,7 +986,7 @@ bool WiFiManager::wifiConnectNew(String ssid, String pass,bool connect){
bool ret = false;
#ifndef WM_NO_LOG
log(WiFiManagerLogLevel::Info, kWiFiMgrLogSubsystem, F("Connecting to NEW AP:"),ssid);
log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("Using Password:"),pass);
log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem, F("WiFi password supplied: [redacted]"));
#endif
WiFi_enableSTA(true,storeSTAmode); // storeSTAmode will also toggle STA on in default opmode (persistent) if true (default)
WiFi.persistent(true);
@@ -997,7 +1007,7 @@ bool WiFiManager::wifiConnectDefault(){
#ifndef WM_NO_LOG
log(WiFiManagerLogLevel::Info, kWiFiMgrLogSubsystem, F("Connecting to SAVED AP:"),WiFi_SSID(true));
log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("Using Password:"),WiFi_psk(true));
log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem, F("Using saved WiFi credentials"));
#endif
ret = WiFi_enableSTA(true,storeSTAmode);
@@ -2433,7 +2443,7 @@ void WiFiManager::debugSoftAPConfig(){
log(WiFiManagerLogLevel::Info, kWiFiMgrLogSubsystem, F("SoftAP Configuration"));
log(WiFiManagerLogLevel::Info, kWiFiMgrLogSubsystem, F("--------------------"));
log(WiFiManagerLogLevel::Info, kWiFiMgrLogSubsystem, F("ssid: "),(char *) config.ssid);
log(WiFiManagerLogLevel::Info, kWiFiMgrLogSubsystem, F("password: "),(char *) config.password);
log(WiFiManagerLogLevel::Info, kWiFiMgrLogSubsystem, F("password: [redacted]"));
log(WiFiManagerLogLevel::Info, kWiFiMgrLogSubsystem, F("ssid_len: "),config.ssid_len);
log(WiFiManagerLogLevel::Info, kWiFiMgrLogSubsystem, F("channel: "),config.channel);
log(WiFiManagerLogLevel::Info, kWiFiMgrLogSubsystem, F("authmode: "),config.authmode);
@@ -2508,7 +2518,7 @@ boolean WiFiManager::validApPassword(){
}
#ifndef WM_NO_LOG
log(WiFiManagerLogLevel::Debug, kWiFiMgrLogSubsystem,F("AccessPoint set password is VALID"));
log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,"ap pass",_apPassword);
log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem, F("Access point password: [redacted]"));
#endif
}
return true;
+14 -5
View File
@@ -12,6 +12,7 @@
#include "templates/RootShell.h"
#include "templates/PortalAppJS.h"
#include <TemplateEngine.h>
#include <TemplateEngineAsyncWeb.h>
#include <cstring>
#if defined(ESP8266) || defined(ESP32)
@@ -95,9 +96,16 @@ AsyncWebServerResponse* beginTemplateResponse(AsyncWebServerRequest* request,
const char* templateData) {
bundle->context.setRegistry(&bundle->registry);
TemplateRenderer::initializeContext(bundle->context, templateData);
return request->beginChunkedResponse(String(FPSTR(HTTP_HEAD_CT)),
[bundle](uint8_t *buffer, size_t maxLen, size_t /*index*/) -> size_t {
return TemplateRenderer::renderNextChunk(bundle->context, buffer, maxLen);
return TemplateEngineAsyncWeb::beginSafeChunkedResponse(
request,
String(FPSTR(HTTP_HEAD_CT)),
bundle,
[](BundleT& responseBundle, uint8_t *buffer, size_t maxLen, size_t /*index*/) -> size_t {
return TemplateEngineAsyncWeb::renderTemplateChunkWithRetries(
responseBundle.context, buffer, maxLen, 128);
},
[](const BundleT& responseBundle) -> bool {
return TemplateEngineAsyncWeb::isTemplateTerminal(responseBundle.context);
}
);
}
@@ -731,7 +739,7 @@ void WiFiManagerHandlers::applyWifiAndParamsFromRequest(AsyncWebServerRequest *r
requestinfo += "\n";
for (size_t i = 0; i < request->params(); i++) {
const AsyncWebParameter* p = request->getParam(i);
requestinfo += " " + p->name() + ": " + p->value() + "\n";
requestinfo += " " + p->name() + ": [redacted, " + String(p->value().length()) + " chars]\n";
}
_wm->log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem, requestinfo);
@@ -815,7 +823,8 @@ void WiFiManagerHandlers::doParamSave(WiFiManager::WiFiManagerRequestArgs reques
_wm->_params[i]->setValue(value.c_str(), value.length());
#ifndef WM_NO_LOG
_wm->log(WiFiManagerLogLevel::Debug, kWiFiMgrLogSubsystem,(String)_wm->_params[i]->getID() + ":",value);
_wm->log(WiFiManagerLogLevel::Debug, kWiFiMgrLogSubsystem,
(String)_wm->_params[i]->getID() + F(": [redacted, ") + String(value.length()) + F(" chars]"));
#endif
}
#ifndef WM_NO_LOG
+15 -16
View File
@@ -8,9 +8,11 @@
#include "WiFiManagerParameter.h"
#include <cstring>
#include <new>
WiFiManagerParameter::WiFiManagerParameter() {
WiFiManagerParameter("");
WiFiManagerParameter::WiFiManagerParameter()
: WiFiManagerParameter("")
{
}
WiFiManagerParameter::WiFiManagerParameter(const char *custom) {
@@ -63,27 +65,24 @@ WiFiManagerParameter::~WiFiManagerParameter() {
// @note debug is not available in wmparameter class
void WiFiManagerParameter::setValue(const char *defaultValue, int length) {
if(!_id){
// Serial.println("cannot set value of this parameter");
if (!_id || length < 0) {
return;
}
// if(strlen(defaultValue) > length){
// // Serial.println("defaultValue length mismatch");
// // return false; //@todo bail
// }
if(_length != length || _value == nullptr){
_length = length;
if( _value != nullptr){
delete[] _value;
if (_length != length || _value == nullptr) {
char* replacement = new (std::nothrow) char[static_cast<size_t>(length) + 1];
if (!replacement) {
return;
}
_value = new char[_length + 1];
delete[] _value;
_value = replacement;
_length = length;
}
memset(_value, 0, _length + 1); // explicit null
memset(_value, 0, static_cast<size_t>(_length) + 1);
if (defaultValue != NULL) {
if (defaultValue != nullptr) {
strncpy(_value, defaultValue, _length);
}
}
+24 -6
View File
@@ -1,6 +1,6 @@
{
"name": "WiFiManager",
"version": "2.0.19",
"version": "3.0.3",
"keywords": [
"wifi",
"wi-fi",
@@ -13,7 +13,7 @@
"wemos",
"arduino"
],
"description": "WiFi Configuration manager with web configuration portal for ESP boards",
"description": "Maintained ESP8266/ESP32 WiFi configuration portal with asynchronous web support.",
"authors": [
{
"name": "tzapu",
@@ -40,10 +40,26 @@
],
"dependencies": [
{
"name": "ESP32Async/ESPAsyncWebServer",
"version": "^3.9.1"
"owner": "ESP32Async",
"name": "ESPAsyncWebServer",
"version": "3.9.1"
},
"https://github.com/alexhopeoconnor/DFTE.git"
{
"owner": "ESP32Async",
"name": "ESPAsyncTCP",
"version": "2.0.0",
"platforms": "espressif8266"
},
{
"owner": "ESP32Async",
"name": "AsyncTCP",
"version": "3.4.9",
"platforms": "espressif32"
},
{
"name": "DeviceFrameworkTemplateEngine",
"version": "https://github.com/alexhopeoconnor/DFTE.git#v1.0.2"
}
],
"build": {
"includeDir": "lib/WiFiManager/include",
@@ -57,5 +73,7 @@
"README.md",
"library.json"
]
}
},
"$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json",
"homepage": "https://github.com/alexhopeoconnor/WiFiManager"
}
+7 -5
View File
@@ -15,11 +15,12 @@ build_flags =
-DWM_LOG_LEVEL=5
-DUNIT_TEST
lib_deps =
ESP32Async/ESPAsyncWebServer@^3.9.1
symlink://../DFTE
ESP32Async/ESPAsyncWebServer@3.9.1
DeviceFrameworkTemplateEngine=https://github.com/alexhopeoconnor/DFTE.git#v1.0.2
ESP32Async/ESPAsyncTCP@2.0.0
[env:esp32]
platform = espressif32
platform = espressif32@6.13.0
board = esp32dev
framework = arduino
monitor_speed = 115200
@@ -32,8 +33,9 @@ build_flags =
-DWM_LOG_LEVEL=5
-DUNIT_TEST
lib_deps =
ESP32Async/ESPAsyncWebServer@^3.9.1
symlink://../DFTE
ESP32Async/ESPAsyncWebServer@3.9.1
DeviceFrameworkTemplateEngine=https://github.com/alexhopeoconnor/DFTE.git#v1.0.2
ESP32Async/AsyncTCP@3.4.9
; Optional: compile tests with DFTE logs bridged into WiFiManager::log (see README)
[env:esp8266_dfte_log]
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
echo "Usage: $0 --platform esp8266|esp32" >&2
exit 2
}
[[ "${1:-}" == "--platform" ]] || usage
platform="${2:-}"
[[ "$platform" == "esp8266" || "$platform" == "esp32" ]] || usage
[[ $# -eq 2 ]] || usage
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
pio run -d "$root/test/compile-project" -e "$platform"
echo "WiFiManager consumer compile check passed for $platform"
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
echo "Usage: $0 vMAJOR.MINOR.PATCH [--tag]"
exit 2
}
tag="${1:-}"
[[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || usage
[[ "${2:-}" == "" || "${2:-}" == "--tag" ]] || usage
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
version="${tag#v}"
manifest_version="$(sed -n 's/.*"version": "\([^"]*\)".*/\1/p' "$root/library.json" | head -n 1)"
if [[ "$manifest_version" != "$version" ]]; then
echo "library.json is $manifest_version; expected $version for $tag" >&2
exit 1
fi
if [[ -f "$root/library.properties" ]]; then
properties_version="$(sed -n 's/^version=//p' "$root/library.properties" | head -n 1)"
if [[ "$properties_version" != "$version" ]]; then
echo "library.properties is $properties_version; expected $version for $tag" >&2
exit 1
fi
fi
package_dir="$(mktemp -d)"
trap 'rm -rf "$package_dir"' EXIT
pio pkg pack "$root" --output "$package_dir/package.tar.gz" >/dev/null
echo "Validated PlatformIO package for $tag"
if [[ "${2:-}" == "--tag" ]]; then
git -C "$root" diff --quiet
git -C "$root" diff --cached --quiet
git -C "$root" tag -a "$tag" -m "Release $tag"
echo "Created $tag. Push the branch and tag; GitHub Actions will publish the release."
fi
+33
View File
@@ -0,0 +1,33 @@
[platformio]
default_envs = esp8266
[common]
framework = arduino
lib_ldf_mode = chain
lib_deps =
WiFiManager=symlink://../..
ESP32Async/ESPAsyncWebServer@3.9.1
[env:esp8266]
extends = common
platform = espressif8266
board = d1_mini
platform_packages =
platformio/framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#521ae60a89e64bb0d1eb7a0b7addf620ced5cad3
build_flags =
-DWM_LOG_LEVEL=3
lib_deps =
${common.lib_deps}
ESP32Async/ESPAsyncTCP@2.0.0
[env:esp32]
extends = common
platform = espressif32@6.13.0
board = esp32dev
build_unflags = -std=gnu++11
build_flags =
-std=gnu++14
-DWM_LOG_LEVEL=3
lib_deps =
${common.lib_deps}
ESP32Async/AsyncTCP@3.4.9
+10
View File
@@ -0,0 +1,10 @@
#include <Arduino.h>
#include <WiFiManager.h>
WiFiManager wifiManager;
void setup() {
wifiManager.setConfigPortalTimeout(1);
}
void loop() {}