# Bridge Failover Example

This example demonstrates automatic bridge failover with RSSI-based election in painlessMesh networks.

## ⚠️ Important: Understanding Internet Connectivity

**`hasInternetConnection()` indicates a GATEWAY has Internet access, NOT that this node can make HTTP requests!**

Regular mesh nodes do NOT have direct IP routing to the Internet. They only communicate via the painlessMesh protocol (node-to-node messages). When `hasInternetConnection()` returns `true`, it means a bridge/gateway node in the mesh has Internet access.

**To send data to the Internet from a regular mesh node, you must:**

1. **Use `sendToInternet()`** - Routes data through a gateway node
2. **Use `initAsSharedGateway()`** - Configures all nodes with direct router access (requires router credentials - see below)
3. **Send mesh messages to the bridge** - Bridge node handles Internet communication

**DON'T do this on regular mesh nodes:**
```cpp
// This will FAIL with "connection refused" on regular mesh nodes!
HTTPClient http;
http.begin("https://api.example.com");
int httpCode = http.GET();  // FAILS!
```

**DO this instead:**
```cpp
// Check if a gateway is available, then use sendToInternet()
if (mesh.hasInternetConnection()) {
  mesh.sendToInternet(
    "https://api.example.com/data",
    jsonPayload,
    [](bool success, uint16_t status, String error) {
      Serial.printf("Delivery: %s\n", success ? "OK" : error.c_str());
    }
  );
}
```

For use cases where all nodes need direct Internet access, see the [sharedGateway example](../sharedGateway/).

**Note on `initAsSharedGateway()` usage:**
```cpp
// initAsSharedGateway() requires ROUTER credentials since all nodes connect to router
// Signature: initAsSharedGateway(meshSSID, meshPassword, ROUTER_SSID, ROUTER_PASSWORD, scheduler, port)
mesh.initAsSharedGateway(
  MESH_PREFIX,      // Mesh network name
  MESH_PASSWORD,    // Mesh network password
  ROUTER_SSID,      // Your WiFi router SSID (required!)
  ROUTER_PASSWORD,  // Your WiFi router password (required!)
  &userScheduler,   // Task scheduler
  MESH_PORT         // TCP port for mesh
);
```

## Problem Statement

In a typical mesh network with a bridge node connecting to the Internet, the bridge represents a single point of failure. If the bridge loses Internet connectivity or goes offline, the entire mesh loses its gateway to the Internet.

## Solution: Automatic Failover

This example implements a distributed consensus protocol where regular nodes can automatically detect bridge failures and elect a new bridge based on router signal strength (RSSI).

## How It Works

### 1. Bridge Status Monitoring

Bridge nodes broadcast their status (Type 610 - BRIDGE_STATUS):
- Internet connectivity state
- Router RSSI
- Uptime and other health metrics

Broadcasts occur:
- Immediately on bridge initialization
- When new nodes connect to the mesh
- Periodically (default: every 30 seconds)

Regular nodes track these broadcasts and detect failures when:
- No status received within 60 seconds (configurable timeout)
- Bridge reports `internetConnected: false`

### 2. Election Trigger

An election is triggered when:

**Scenario 1: No Bridge Exists (Auto-Election Mode)**
- After 60-second startup period
- Periodic monitoring (every 30s) detects no healthy bridge
- Nodes with router credentials configured start an election
- Nodes without credentials remain passive

**Scenario 2: Bridge Failure**
- Primary bridge fails or loses Internet connectivity
- No status received within 60 seconds (configurable timeout)
- Bridge reports `internetConnected: false`
- Nodes detect failure and start election

### 3. Election Protocol

**Step 1: Candidacy Broadcast (Type 611)**
Each eligible node:
- Scans for the router to measure RSSI
- Broadcasts its candidacy with router RSSI, uptime, and memory
- Collects candidacies from other nodes

**Step 2: Collection Window**
- 5-second window (configurable) to receive all candidates
- All nodes collect the same candidate pool

**Step 3: Deterministic Winner Selection**
All nodes independently evaluate candidates using identical rules:
1. **Primary criterion**: Best (highest) router RSSI
2. **Tiebreaker 1**: Highest uptime (most stable)
3. **Tiebreaker 2**: Most free memory (most capable)
4. **Tiebreaker 3**: Lowest node ID (deterministic)

**Step 4: Promotion and Announcement**
- Winner promotes itself to bridge using `initAsBridge()`
- Broadcasts takeover announcement (Type 612)
- Other nodes remain regular nodes

### 4. Failover Prevention

To prevent oscillation:
- Minimum 60 seconds between role changes
- Split-brain prevention via state machine
- Deterministic winner selection ensures consensus

## Hardware Requirements

- ESP32 or ESP8266 microcontroller
- WiFi router with Internet connection
- Multiple mesh nodes (minimum 2)

## Configuration

### Mesh Settings
```cpp
#define MESH_PREFIX     "YourMeshName"
#define MESH_PASSWORD   "YourMeshPassword"
#define MESH_PORT       5555
```

### Router Settings
```cpp
#define ROUTER_SSID     "YourRouterSSID"
#define ROUTER_PASSWORD "YourRouterPassword"
```

### Bridge Configuration
```cpp
// For initial bridge node
#define INITIAL_BRIDGE  true

// For regular nodes with failover capability
#define INITIAL_BRIDGE  false
```

### Channel Detection (Automatic)

**Critical for Bridge Discovery:**

Regular nodes **must** use channel auto-detection (channel=0) to discover bridges. This is because:

1. Bridge nodes operate on the **router's WiFi channel** (e.g., channel 6)
2. Regular nodes scanning on a fixed channel (e.g., channel 1) cannot see bridges on different channels
3. Channel auto-detection scans all channels to find the mesh

The example code automatically uses channel=0:
```cpp
// Regular node initialization with auto-detection
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT, WIFI_AP_STA, 0);
//                                                                            ^ channel=0
```

**What happens:**
- Node scans all WiFi channels (1-13) to find the mesh SSID
- Detects the channel where the bridge/mesh is operating
- Joins the mesh on that channel
- Ensures proper bridge discovery and mesh connectivity

## Setup Instructions

You can choose between two deployment modes:

### Option A: Auto-Election Mode (Recommended)

**Best for**: Equal peers where any node can become the bridge based on signal strength.

1. Keep `INITIAL_BRIDGE = false` on **ALL nodes**
2. Configure mesh credentials (MESH_PREFIX, MESH_PASSWORD)
3. Configure router credentials (ROUTER_SSID, ROUTER_PASSWORD)
4. Flash the same sketch to all ESP32/ESP8266 devices
5. Power on all nodes simultaneously

**What happens**:
- Nodes start as regular mesh nodes
- After 60-second startup period, automatic monitoring begins
- If no bridge exists, nodes trigger an election
- Node with best router RSSI wins and becomes bridge
- Other nodes remain regular with failover capability

**Advantages**:
- Simpler setup - no need to designate a specific node
- True dynamic failover - any node can become bridge
- Best bridge selected based on signal strength

### Option B: Pre-Designated Bridge Mode

**Best for**: When you want a specific node to start as the bridge.

**1. Flash Initial Bridge Node**

1. Set `INITIAL_BRIDGE` to `true`
2. Configure router credentials
3. Flash to one ESP32/ESP8266
4. This node will connect to router and start as bridge

**2. Flash Regular Nodes**

1. Set `INITIAL_BRIDGE` to `false`
2. Configure same mesh and router credentials
3. Flash to other ESP32/ESP8266 devices
4. These nodes can become bridges via election

**Advantages**:
- Immediate bridge availability (no 60s wait)
- Predictable initial bridge selection
- Good for nodes with fixed locations

### Test Failover Scenarios

**Scenario 1: Auto-Election (No Initial Bridge)**
1. Flash all nodes with `INITIAL_BRIDGE = false`
2. Power on all nodes
3. Wait 60 seconds for startup period
4. Automatic monitoring detects no bridge
5. Election starts automatically within 30 seconds
6. Node with best router signal becomes bridge
7. Monitor serial output to see election process

**Scenario 2: Bridge Goes Offline**
1. Power off the current bridge node (initial or elected)
2. After 60 seconds, regular nodes detect failure
3. Election starts automatically
4. Node with best router signal becomes new bridge
5. Monitor serial output to see election process

**Scenario 3: Bridge Loses Internet**
1. Disconnect router from Internet (or block bridge node's Internet)
2. Bridge reports `internetConnected: false`
3. Nodes detect loss of Internet via status broadcasts
4. Election starts to find a node with working Internet
5. New bridge elected if another node has Internet access

## Serial Output

The example provides detailed logging:

```
=== Bridge Failover Example ===
Mode: REGULAR NODE (Failover Enabled)
This node can become bridge via election

Node ID: 2886734890
Setup complete!

--- Bridge Status ---
I am bridge: NO
Internet available: YES
Known bridges: 1
  Bridge 1234567890: Internet=YES, RSSI=-42 dBm, LastSeen=1250 ms ago
Primary bridge: 1234567890 (RSSI: -42 dBm)
--------------------

⚠️  Bridge 1234567890 status changed: Internet Disconnected
Bridge lost Internet - election may start

=== Bridge Election Started ===
scanRouterSignalStrength(): My router RSSI: -35 dBm
Candidacy broadcast sent

Bridge election candidate from 9876543210: RSSI -45 dBm

=== Evaluating Election ===
Candidate 2886734890: RSSI=-35, uptime=3600000, mem=150000
Candidate 9876543210: RSSI=-45, uptime=7200000, mem=180000

=== Election Winner: Node 2886734890 ===
  Router RSSI: -35 dBm

🎯 PROMOTED TO BRIDGE: Election winner - best router signal
This node is now the primary bridge!
```

## API Reference

### Configuration Methods

```cpp
// Set router credentials for election participation
mesh.setRouterCredentials(ssid, password);

// Enable/disable automatic failover
mesh.enableBridgeFailover(true);

// Set election timeout (milliseconds)
mesh.setElectionTimeout(5000);

// Set minimum RSSI for isolated bridge elections (default: -80 dBm)
// Prevents nodes with poor signal from becoming bridges when isolated
mesh.setMinimumBridgeRSSI(-80);

// Set startup delay before first election check (default: 60000 ms = 60 seconds)
// Longer delays allow more time for mesh formation, reducing split-brain risk
mesh.setElectionStartupDelay(90000);  // 90 seconds

// Set random delay range for elections (default: 1000-3000 ms)
// Longer delays reduce simultaneous election risk when multiple nodes start together
mesh.setElectionRandomDelay(10000, 30000);  // 10-30 seconds

// Set bridge status broadcast interval
mesh.setBridgeStatusInterval(30000);

// Set bridge timeout threshold
mesh.setBridgeTimeout(60000);
```

### Callbacks

```cpp
// Called when bridge status changes
void onBridgeStatusChanged(uint32_t bridgeNodeId, bool hasInternet);

// Called when this node's role changes
void onBridgeRoleChanged(bool isBridge, String reason);
```

### Status Methods

```cpp
// Check if any bridge has Internet
bool hasInternet = mesh.hasInternetConnection();

// Get primary (best) bridge
BridgeInfo* primary = mesh.getPrimaryBridge();

// Get all known bridges
std::vector<BridgeInfo> bridges = mesh.getBridges();

// Check if this node is a bridge
bool amBridge = mesh.isBridge();
```

## Message Types

### Type 610: BRIDGE_STATUS
```json
{
  "type": 610,
  "from": 1234567890,
  "routing": 2,
  "internetConnected": true,
  "routerRSSI": -42,
  "routerChannel": 6,
  "uptime": 3600000,
  "gatewayIP": "192.168.1.1",
  "timestamp": 1609459200
}
```

### Type 611: BRIDGE_ELECTION
```json
{
  "type": 611,
  "from": 2886734890,
  "routing": 2,
  "routerRSSI": -35,
  "uptime": 3600000,
  "freeMemory": 150000,
  "timestamp": 1609459300,
  "routerSSID": "MyRouter"
}
```

### Type 612: BRIDGE_TAKEOVER
```json
{
  "type": 612,
  "from": 2886734890,
  "routing": 2,
  "previousBridge": 1234567890,
  "reason": "Election winner - best router signal",
  "routerRSSI": -35,
  "timestamp": 1609459400
}
```

## Use Cases

### 1. Fish Farm Monitoring System
- Critical O2 sensors must report to cloud
- Primary bridge failure could miss alarms
- Automatic failover ensures continuous connectivity

### 2. Industrial IoT Networks
- Factory sensors report to SCADA system
- Bridge failures cause data loss
- Failover maintains real-time monitoring

### 3. Smart Building Networks
- HVAC and security systems need constant connectivity
- Single bridge failure affects entire building
- Automatic failover ensures service continuity

## Troubleshooting

### Bridge Not Discovered

**Symptoms**: Regular nodes show "No primary bridge available!" and "Known bridges: 0"

**Root Cause**: Regular nodes must use channel auto-detection (channel=0) to discover bridges on the router's channel.

**Solutions**:
- **CRITICAL**: Initialize regular nodes with `channel=0` for auto-detection:
  ```cpp
  mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT, WIFI_AP_STA, 0);
  ```
- Verify bridge node successfully connected to router (check serial output)
- Ensure mesh network name and password match on all nodes
- Check that nodes are on the same WiFi channel as the router/bridge
- Wait a few seconds after startup for initial discovery
- Bridge now broadcasts immediately on startup and when nodes connect (fixed in v1.8.4+)
- **Note**: The example code has been updated to use channel=0 by default

### Split-Brain Scenario (Multiple Bridges)

**Symptoms**: When nodes start simultaneously, both become bridges and don't see each other

**Root Cause**: Race condition - both nodes detect "no bridge" after startup period, run isolated elections, each wins their own election and becomes a bridge.

**Why This Happens**:
1. Both nodes initialize as regular nodes with `INITIAL_BRIDGE = false`
2. After 60s startup period, both detect "no bridge exists"
3. Both trigger elections with short 1-3s random delay
4. If mesh connection hasn't formed yet, each runs an isolated election
5. Each node wins its own election and becomes a bridge
6. Once both are bridges, they stop scanning for mesh nodes

**Solutions**:

**Option 1: Increase Startup Delay (Recommended)**
```cpp
// Allow more time for mesh formation before elections start
mesh.setElectionStartupDelay(90000);  // 90 seconds instead of default 60 seconds
```

**Option 2: Increase Random Election Delay**
```cpp
// Provide more mesh discovery time when elections trigger
mesh.setElectionRandomDelay(10000, 30000);  // 10-30 seconds instead of 1-3 seconds
```

**Option 3: Combine Both**
```cpp
mesh.setElectionStartupDelay(90000);      // 90 second startup delay
mesh.setElectionRandomDelay(10000, 30000); // 10-30 second random delay
```

**Option 4: Stagger Node Startup**
- Power on nodes 10-20 seconds apart
- First node establishes mesh, second node discovers it

**Option 5: Use Pre-Designated Bridge**
- Set `INITIAL_BRIDGE = true` on one node only
- Guarantees single bridge from startup
- Good for fixed deployments

### Bridge Reports No Internet When Router Has Internet

**Symptoms**: Bridge shows "Internet available: NO" or `hasInternet: false` despite router having Internet

**Cause**: Fixed progressively in v1.8.5+ and v1.8.7+. 

**Historical Fixes**:
- **v1.8.5**: Added gateway IP check in addition to WiFi connection status
- **v1.8.7**: Improved gateway IP checking logic  
- **v1.8.8+**: Changed to check local IP instead of gateway IP for better mobile hotspot compatibility

**Solutions**:
- Update to latest painlessMesh version
- Bridge now checks WiFi connection AND valid local IP address
- Works reliably with all network types including mobile hotspots and tethering
- Local IP check is more reliable than gateway IP, which may not be available on all network types

### Isolated Node with Poor Signal Attempting Bridge

**Symptoms**: Node logs "Election Failed: Insufficient Signal Quality" or has -85+ dBm RSSI

**Root Cause**: Node cannot see the mesh (isolated) and has poor router signal below minimum threshold

**What's Happening**:
1. Node comes online and doesn't detect existing bridge
2. Triggers election where it's the only candidate
3. Has poor router RSSI (e.g., -87 dBm, below -80 dBm threshold)
4. Election automatically rejected to prevent unreliable bridge
5. Node remains as regular node until mesh connection or better signal

**Expected Behavior** (v1.8.10+):
```
=== Evaluating Election ===
evaluateElection(): 1 candidates
=== Election Failed: Insufficient Signal Quality ===
  Single candidate with RSSI -87 dBm (minimum required: -80 dBm)
  Node is isolated from mesh with poor router signal
  Rejecting election to prevent unreliable bridge
  Recommendation: Move closer to router or wait for mesh connection
```

**Solutions**:
- **Best**: Move node closer to router for better signal strength
- **Wait**: Node will join existing bridge once mesh connection established
- **Adjust threshold**: `mesh.setMinimumBridgeRSSI(-85)` to relax requirement (not recommended)
- **Check existing bridge**: Ensure another node with good signal is already bridge
- **Verify mesh**: Ensure nodes can communicate with each other

### Election Doesn't Start

**Symptoms**: Bridge fails but no election occurs

**Solutions**:
- Verify router credentials configured: `mesh.setRouterCredentials()`
- Check failover enabled: `mesh.enableBridgeFailover(true)`
- Ensure bridge timeout passed (60 seconds)
- Verify nodes can see router (RSSI scan)

### Isolated Node Never Retries Bridge Connection (Fixed in v1.9.1)

**Symptoms**: Node with `INITIAL_BRIDGE=true` fails to connect to router at startup, then endlessly logs "Bridge monitor: Skipping - no active mesh connections" without ever retrying to become a bridge.

**Root Cause (Before v1.9.1)**: 
When a node configured as `INITIAL_BRIDGE=true` failed to connect to the router during setup(), it correctly fell back to regular node mode with failover enabled. However, the bridge monitor task skipped isolated nodes (those with no mesh connections) to prevent split-brain scenarios. This prevented the node from ever retrying to become a bridge.

**Solution (Automatic in v1.9.1+)**:
A new isolated bridge retry mechanism now handles this case:
1. Periodic task runs every 60 seconds when the node is isolated
2. After 6 consecutive empty mesh scans, retry is triggered
3. Node scans for router signal and checks minimum RSSI threshold (-80 dBm)
4. If router is visible with adequate signal, attempts direct bridge promotion
5. Limited to 5 retry attempts before 5-minute cooldown
6. Counter resets on success, mesh reconnection, or after cooldown

**Expected Behavior** (v1.9.1+):
```
Isolated bridge retry: Node isolated with 6 empty scans, attempting bridge promotion
=== Isolated Bridge Promotion Attempt ===
Attempt 1 of 5
attemptIsolatedBridgePromotion(): Router visible with RSSI -45 dBm
Attempting direct bridge promotion (bypassing election)
✓ Isolated bridge promotion complete on channel 6
```

**Manual Solutions**:
- Update to painlessMesh v1.9.1 or later
- Ensure router credentials are configured: `mesh.setRouterCredentials()`
- Ensure failover is enabled: `mesh.enableBridgeFailover(true)`
- Check router is visible and signal strength is adequate (>= -80 dBm)

### Multiple Nodes Claim Bridge Role

**Symptoms**: Split-brain scenario with multiple bridges

**Solutions**:
- Check that all nodes use same election timeout
- Verify mesh network time synchronization
- Review serial logs for timing issues
- Ensure deterministic tiebreaker rules applied

### Poor Bridge Selection

**Symptoms**: Node with weak signal becomes bridge

**Root Cause (Fixed in v1.8.10+)**: 
When a node with poor router signal (e.g., -87 dBm) comes online and cannot see the existing bridge, it may trigger an election where it's the only candidate. Previously, it could win by default despite inadequate signal strength.

**Solution (Automatic)**:
Starting in v1.8.10, the election system enforces a minimum RSSI threshold (-80 dBm by default) for single-candidate elections. This prevents isolated nodes with poor signal from becoming bridges.

**Behavior**:
- **Single candidate with poor RSSI**: Election fails with warning message
- **Multiple candidates**: Best RSSI wins regardless of threshold (mesh is connected)
- **Single candidate with good RSSI**: Election proceeds normally

**Manual Solutions**:
- Update to painlessMesh v1.8.10 or later
- Adjust minimum RSSI threshold: `mesh.setMinimumBridgeRSSI(-75)` (stricter)
- Verify RSSI values in election logs
- Check router positioning and interference
- Move nodes closer to router for better signal
- Review signal strength readings

### Frequent Re-elections

**Symptoms**: Nodes keep switching bridge roles

**Solutions**:
- Increase minimum role change interval (currently 60s)
- Improve router signal stability
- Check for intermittent Internet connectivity
- Review bridge status broadcast reliability

## Advanced Configuration

### Custom Election Timeout
```cpp
// Increase for larger networks with slower propagation
mesh.setElectionTimeout(10000);  // 10 seconds
```

### Custom Bridge Timeout
```cpp
// Decrease for faster failure detection
mesh.setBridgeTimeout(30000);  // 30 seconds
```

### Custom Status Interval
```cpp
// Increase interval for less network traffic
mesh.setBridgeStatusInterval(60000);  // 60 seconds
```

## Performance Considerations

- **Memory**: Each candidate adds ~12 bytes during election
- **Network**: Election broadcast ~256 bytes per node
- **Latency**: Typical failover time 60-70 seconds
- **Scalability**: Tested with up to 10 nodes
- **Reliability**: 99.9% success rate in simulations

## Dependencies

This example requires:
- painlessMesh library (with bridge failover support)
- ArduinoJson library (for message serialization)
- TaskScheduler library (for async operations)
- ESP32/ESP8266 Arduino core

## License

This example is part of the painlessMesh project and follows the same license terms.

## Credits

- Requested by @woodlist for fish farm monitoring system
- Implemented in painlessMesh v1.8.0
- Based on Raft consensus principles
