Repository navigation
BRIDGE_INITIALIZATION_FALLBACK
This document describes the behavior of initAsBridge() when router connection fails, and demonstrates recommended fallback patterns that give library users control over error handling and recovery strategies.
As a library, painlessMesh provides the building blocks for mesh networking but does not dictate application-level recovery strategies. When bridge initialization fails, the library:
-
Returns failure status (
false) instead of forcing actions like restart - Provides clear error logging to help diagnose issues
- Leaves mesh state clean for user-controlled recovery
- Allows graceful fallback to regular mesh node
This design enables flexible deployment patterns:
- Single-bridge networks with manual recovery
- Multi-bridge networks with automatic redundancy
- Hybrid approaches with controlled failover behavior
When initAsBridge() successfully connects to the router:
bool initAsBridge(TSTRING meshSSID, TSTRING meshPassword,
TSTRING routerSSID, TSTRING routerPassword,
Scheduler *baseScheduler, uint16_t port = 5555);Steps:
- Disconnect from any existing WiFi connections
- Connect to router in STA mode (30 second timeout)
- Detect router's WiFi channel
- Initialize mesh AP on same channel as router
- Re-establish router connection using stationManual
- Set node as root/bridge
- Start bridge status broadcasting
- Return
true
Result: Device functions as bridge on router's channel, maintaining both router and mesh connectivity.
When router connection fails (timeout after 30 seconds):
Steps:
- Log error: "Failed to connect to router"
- Log error: "Cannot become bridge without router connection"
- Log error: "Bridge initialization aborted - remaining as regular node"
- Return
falsewithout initializing mesh
Result: Device is not initialized as bridge OR regular node. User code must decide next steps.
The library does not automatically restart or force fallback because:
- User control: Application knows its deployment context and requirements
- Network stability: Avoid restart loops that could destabilize mesh
- Flexibility: Different use cases need different recovery strategies
- Predictability: Library behavior should be deterministic and explicit
Use Case: Single bridge network where bridge is not critical to mesh operation.
bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
ROUTER_SSID, ROUTER_PASSWORD,
&userScheduler, MESH_PORT);
if (!bridgeSuccess) {
Serial.println("✗ Failed to initialize as bridge!");
Serial.println("Router unreachable - falling back to regular mesh node");
// Fallback: Initialize as regular mesh node
// Device can still participate in mesh without bridge functionality
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
Serial.println("✓ Initialized as regular mesh node");
Serial.println("Note: To function as a bridge, fix router and restart");
}Benefits:
- Device remains part of mesh network
- Can receive messages from other nodes
- Can participate in mesh topology
- Manual intervention needed to restore bridge role
Use Case: Networks where any node can become bridge when router becomes available.
bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
ROUTER_SSID, ROUTER_PASSWORD,
&userScheduler, MESH_PORT);
if (!bridgeSuccess) {
Serial.println("✗ Failed to initialize as bridge!");
Serial.println("Router unreachable - enabling bridge failover");
// Fallback: Regular node with automatic bridge promotion
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
mesh.setRouterCredentials(ROUTER_SSID, ROUTER_PASSWORD);
mesh.enableBridgeFailover(true);
mesh.setElectionTimeout(5000);
Serial.println("✓ Running as regular node");
Serial.println("Will auto-promote to bridge when router available");
}Benefits:
- Automatic recovery when router comes online
- No manual intervention needed
- Participates in bridge elections
- Maintains mesh connectivity throughout
Use Case: Critical networks with multiple potential bridges.
Primary Bridge:
bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
ROUTER_SSID, ROUTER_PASSWORD,
&userScheduler, MESH_PORT, 10); // Priority 10
if (!bridgeSuccess) {
Serial.println("✗ Primary bridge init failed!");
Serial.println("Router unreachable - secondary should be active");
// Fallback: Regular node (secondary bridge takes over)
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
Serial.println("✓ Running as regular node");
Serial.println("Secondary bridge should provide connectivity");
}Secondary Bridge:
// Secondary bridge configuration (priority 5)
// If primary fails, secondary provides redundancyBenefits:
- Immediate redundancy via secondary bridge
- Graceful degradation of primary
- No single point of failure
- Maintains Internet connectivity for mesh
Use Case: Environments with intermittent router availability.
const int MAX_RETRIES = 3;
int retryCount = 0;
int retryDelay = 5000; // Start with 5 seconds
while (retryCount < MAX_RETRIES) {
bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
ROUTER_SSID, ROUTER_PASSWORD,
&userScheduler, MESH_PORT);
if (bridgeSuccess) {
Serial.println("✓ Bridge initialized successfully");
break;
}
retryCount++;
if (retryCount < MAX_RETRIES) {
Serial.printf("Retry %d/%d in %d seconds...\n",
retryCount, MAX_RETRIES, retryDelay/1000);
delay(retryDelay);
retryDelay *= 2; // Exponential backoff
}
}
if (retryCount >= MAX_RETRIES) {
Serial.println("Max retries reached - falling back to regular node");
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
}Benefits:
- Handles temporary router outages
- Exponential backoff prevents network flooding
- Eventually falls back if persistent failure
- Configurable retry strategy
Use Case: Explicit bridge nodes that require bridge functionality to operate.
bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
ROUTER_SSID, ROUTER_PASSWORD,
&userScheduler, MESH_PORT);
if (!bridgeSuccess) {
Serial.println("✗ Failed to initialize as bridge!");
Serial.println("This device must function as a bridge");
Serial.println("Restarting in 30 seconds to retry...");
delay(30000);
ESP.restart(); // User's choice to restart
}Benefits:
- Clear intent that bridge role is required
- User controls restart timing
- Can implement watchdog or LED indicators
- Appropriate for dedicated bridge hardware
When two mesh nodes start on different channels:
-
Station Scan Discovery:
- Each node scans for mesh SSID on all channels
- Nodes discover each other via beacon frames
- Connection negotiation determines channel
-
Channel Selection:
- Node with more connections typically maintains its channel
- New node switches to join existing network
- Root node (bridge) has priority in channel selection
-
Bridge Channel Priority:
- Bridge nodes maintain router's channel
- Regular nodes switch to bridge's channel to join
- This ensures bridge maintains router connection
When a new bridge appears in an existing mesh:
-
Bridge Broadcasts Status (Type 610):
- Announces presence on its channel (router's channel)
- Other nodes receive status updates
-
Nodes Evaluate Connectivity:
- If bridge is on different channel, nodes must decide:
- Stay on current mesh channel?
- Switch to bridge's channel for Internet access?
- If bridge is on different channel, nodes must decide:
-
Channel Re-synchronization:
- Nodes may perform channel re-sync if mesh fragmented
- After 6 consecutive empty scans, trigger re-sync
- Re-scan all channels to find mesh
-
Avoiding Channel Chase Loops:
- Election mechanism includes channel check
- Nodes defer election if approaching re-sync threshold
- Prevents oscillation between channels
- Critical Infrastructure: Use Pattern 3 (Multi-Bridge Redundancy)
- General IoT: Use Pattern 2 (Auto-Promotion)
- Testing/Development: Use Pattern 1 (Basic Fallback)
- Dedicated Bridges: Use Pattern 5 (User-Controlled Restart)
mesh.onBridgeStatusChanged([](uint32_t bridgeNodeId, bool hasInternet) {
Serial.printf("Bridge %u: Internet %s\n",
bridgeNodeId, hasInternet ? "UP" : "DOWN");
});mesh.onBridgeRoleChanged([](bool isBridge, String reason) {
if (isBridge) {
Serial.printf("Promoted to bridge: %s\n", reason.c_str());
} else {
Serial.printf("Demoted from bridge: %s\n", reason.c_str());
}
});- Implement maximum retry counts
- Use exponential backoff for retries
- Monitor consecutive failures and adjust strategy
- Consider manual intervention threshold
Clearly document in your application code:
- Which fallback pattern is used
- Why that pattern was chosen
- Expected behavior during failures
- Manual recovery procedures if needed
- Configure node as bridge
- Make router unreachable (wrong password, powered off, etc.)
- Verify node falls back gracefully
- Check node can join mesh as regular node
- Verify no restart loops occur
- Start with router unreachable
- Node falls back to regular node with failover enabled
- Power on router
- Verify node detects router and promotes to bridge
- Check channel switching works correctly
- Start primary and secondary bridges
- Make primary's router unreachable
- Verify primary falls back to regular node
- Check secondary bridge continues providing connectivity
- Restore primary's router and verify it resumes bridge role
- Start mesh on channel 6
- Add bridge on channel 11
- Verify nodes handle channel mismatch
- Check no continuous restart or channel oscillation
- Verify eventual mesh convergence on single channel
painlessMesh provides flexible bridge initialization with graceful failure handling:
- Library returns status, doesn't force actions
- Users choose recovery strategy based on their requirements
- Multiple fallback patterns for different use cases
- Channel management prevents network instability
- Comprehensive callbacks for monitoring and control
This design philosophy ensures painlessMesh can be used in diverse deployments while maintaining network stability and giving users full control over their mesh behavior.