Modern async captive portal WiFi manager for ESP32 that combines the best features from existing libraries while solving their key limitations.
Flexifi was mostly developed with the help of Claude, an AI assistant by Anthropic. It provided a majority of the code and suggestions throughout the development process. This statement is included to acknowledge the role of AI in enhancing the development experience. Check out CLAUDE.md for more details on how Claude assisted in the development of Flexifi and docs/PLAN.md for the initial project plan.
- AsyncWebServer Integration - Uses your existing AsyncWebServer instance
- Dual Communication Protocol - WebSocket-driven real-time updates with GET/POST fallback
- Flexible Storage System - LittleFS primary with NVS fallback
- Multiple Templates - Modern, Classic, Minimal, and Custom template support
- Event-Driven Architecture - Rich callback system for all portal states
- Non-blocking Operation - Fully async design for responsive performance
- Automatic Password Generation - Optional secure password generation for captive portal
Using PlatformIO:
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
esp32async/ESPAsyncWebServer@^3.7.0
bblanchon/ArduinoJson@^6.21.0
Flexifi
# Or for development: https://github.com/andyshinn/flexifi.git
Note: Web assets are automatically embedded during library installation via the postinstall script.
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <Flexifi.h>
AsyncWebServer server(80);
Flexifi portal(&server); // Or portal(&server, true) for password generation
void setup() {
Serial.begin(115200);
// Configure callbacks
portal.onWiFiConnect([](const String& ssid) {
Serial.printf("Connected to: %s\n", ssid.c_str());
portal.stopPortal();
});
// Try to load saved configuration
if (!portal.loadConfig()) {
// No saved config, start portal
portal.startPortal("MyDevice-Setup");
}
server.begin();
}
void loop() {
portal.loop();
}
Flexifi includes three built-in responsive templates:
- Clean Material Design-inspired interface
- Dark/light mode support
- Animated connection progress
- Mobile-first responsive layout
- Traditional form-based layout
- Bootstrap-style components
- Broad browser compatibility
- Ultra-lightweight design (< 5KB)
- Essential features only
- Perfect for memory-constrained projects
String customHTML = R"(
<!DOCTYPE html>
<html>
<head><title>{{TITLE}}</title></head>
<body>
<h1>My Custom Portal</h1>
<div id="networks">{{NETWORKS}}</div>
<div id="status">{{STATUS}}</div>
<script src="/portal.js"></script>
</body>
</html>
)";
portal.setCustomTemplate(customHTML);
Flexifi can automatically generate secure passwords for the captive portal:
// Enable password generation in constructor
Flexifi portal(&server, true);
void setup() {
// Start portal without specifying password - uses generated one
portal.startPortal("MyDevice-Setup");
// Get generated password for display (e.g., LCD screen)
String password = portal.getGeneratedPassword();
Serial.printf("Portal Password: %s\n", password.c_str());
}
// Customize password logging interval (default: 30 seconds)
#define FLEXIFI_PASSWORD_LOG_INTERVAL 60000 // 60 seconds
The generated password:
- Uses 8 alphanumeric characters (0-9, A-Z, a-z)
- Logged periodically to serial output while portal is active
- Available via
getGeneratedPassword()
for external display - Only used when no password is provided to
startPortal()
// Portal Management
bool startPortal(const String& apName, const String& apPassword = "");
void stopPortal();
bool isPortalActive();
String getGeneratedPassword(); // Get auto-generated password
// Configuration
void setTemplate(const String& templateName);
void setCustomTemplate(const String& htmlTemplate);
bool saveConfig();
bool loadConfig();
void clearConfig();
// Network Management
void scanNetworks();
bool connectToWiFi(const String& ssid, const String& password);
String getNetworksJSON();
WiFiState getWiFiState();
// Portal events
portal.onPortalStart([]() { /* Portal started */ });
portal.onPortalStop([]() { /* Portal stopped */ });
// WiFi events
portal.onWiFiConnect([](const String& ssid) { /* Connected */ });
portal.onWiFiDisconnect([]() { /* Disconnected */ });
portal.onConnectStart([](const String& ssid) { /* Connecting... */ });
portal.onConnectFailed([](const String& ssid) { /* Failed */ });
// Scan events
portal.onScanComplete([](int count) { /* Scan done */ });
// Configuration events
portal.onConfigSave([](const String& ssid, const String& password) {
/* Config saved */
});
// Storage configuration
#define FLEXIFI_FORCE_LITTLEFS // Force LittleFS usage
#define FLEXIFI_FORCE_NVS // Force NVS usage
#define FLEXIFI_DISABLE_LITTLEFS // Disable LittleFS support
#define FLEXIFI_DISABLE_NVS // Disable NVS support
// Feature configuration
#define FLEXIFI_DISABLE_WEBSOCKET // Disable WebSocket support
// Debug configuration
#define FLEXIFI_DEBUG_LEVEL 3 // 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug
// Network configuration
#define FLEXIFI_SCAN_TIMEOUT 10000 // WiFi scan timeout (ms)
#define FLEXIFI_CONNECT_TIMEOUT 15000 // Connection timeout (ms)
#define FLEXIFI_PORTAL_TIMEOUT 300000 // Portal timeout (ms)
// Password generation
#define FLEXIFI_PASSWORD_LOG_INTERVAL 30000 // Password log interval (ms)
If you encounter dependency conflicts, specify exact packages:
[env:esp32dev]
lib_deps =
esp32async/ESPAsyncWebServer@^3.7.0 # Specify owner to avoid conflicts
bblanchon/ArduinoJson@^6.21.0 # Official ArduinoJson package
Flexifi
portal.setPortalTimeout(300000); // 5 minutes
portal.setConnectTimeout(15000); // 15 seconds
Flexifi uses WebSocket for real-time communication with automatic fallback to HTTP.
{
"type": "scan_complete|connect_start|connect_success|connect_failed|status_update",
"data": {
"networks": [...],
"status": "scanning|connecting|connected|disconnected",
"message": "Human readable status"
}
}
GET / - Portal main page
GET /scan - Scan networks (JSON)
POST /connect - Connect to network
GET /status - Connection status
POST /reset - Reset configuration
GET /networks.json - Network list (JSON)
- basic_usage - Simple portal setup
- advanced_callbacks - Event handling and custom logic
- custom_template - Custom UI templates
- multi_server - Using with existing web server
Flexifi uses a dual storage system for maximum reliability:
- JSON-formatted credential storage
- Configuration file management
- Automatic partition management
- Key-value credential storage
- Namespace isolation
- Flash wear leveling
The library automatically handles storage initialization and failover between systems.
For detailed troubleshooting information, see TROUBLESHOOTING.md.
Dependency conflicts:
lib_deps =
esp32async/ESPAsyncWebServer@^3.7.0 # Specify exact package
bblanchon/ArduinoJson@^6.21.0
Flexifi
Assets not found:
rm -rf src/generated/ && python3 tools/embed_assets.py
Enable debug logging:
#define FLEXIFI_DEBUG_LEVEL 4
#include <Flexifi.h>
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
MIT License - see LICENSE file for details.
Inspired by and improves upon:
- WiFiManager
- NetWizard
- EasyWifi
Built with modern C++ practices and async-first design principles.