Deploy, Flash & Test
This page consolidates the key commands for FastBee-Arduino from online flashing, source build, release packaging to device verification. It covers development, field deployment, release packaging and pre-production checks. For a first-time experience, see Quick Start; for browser-based flashing, open the Online Flasher.
Source repository: FastBee-Arduino.
Flow Overview
Firmware & Environment Selection
Select the PlatformIO environment that matches your hardware to avoid Flash/PSRAM mismatches.
| Environment | Chip | Flash / PSRAM | Use Case |
|---|---|---|---|
esp32-F4R0 | ESP32 | 4MB / None | Classic ESP32 node |
esp32-F8R0 | ESP32 | 8MB / None | ESP32-WROOM OTA node |
esp32-F8R8 | ESP32 | 8MB / 8MB | ESP32-WROVER gateway/cache |
esp32c3-F4R0 | ESP32-C3 | 4MB / None | Low-cost WiFi/MQTT node |
esp32c6-F4R0 | ESP32-C6 | 4MB / None | WiFi 6 low-cost node |
esp32c6-F8R0 | ESP32-C6 | 8MB / None | WiFi 6 OTA node |
esp32s3-F8R0 | ESP32-S3 | 8MB / None | S3 OTA node without PSRAM |
esp32s3-F8R8 | ESP32-S3 | 8MB / 8MB | Recommended S3 controller target |
esp32s3-F16R8 | ESP32-S3 | 16MB / 8MB | High-capacity gateway/controller |
All nine release environments share one feature baseline. Hardware-dependent compile overrides, the Web feature switch and the startup memory budget determine the final runtime capability.
F = Flash size, R = PSRAM size. Feature differences are detailed in Hardware and Environments; build switches in Build Configuration.
Online Flashing
For quick trials and field recovery, use the Online Flasher. The built-in firmware is a merged image containing bootloader, partition table, application firmware and LittleFS web filesystem, flashed at address 0x0.
- Open the online flasher with Chrome or Edge.
- Connect the ESP32 board and select the serial port.
- Select the built-in firmware matching your chip, Flash and PSRAM.
- Keep address
0x0, click flash and wait for auto-reset.
The merged firmware includes all partitions — no additional files need to be flashed separately. Default peripherals and exec rules are safe templates; verify pins and power before first wiring.
If the page is abnormal after flashing, confirm the environment matches the chip, Flash and PSRAM. When switching partition tables or if old configs cause issues, erase the entire Flash first and re-flash firmware and LittleFS from the same build.
Source Deployment
For development and debugging, use the unified deploy script. It checks the environment, builds and uploads the LittleFS web filesystem and firmware for the target environment.
cd D:\project\gitee\FastBee-Arduino
powershell -ExecutionPolicy Bypass -File scripts\doctor.ps1 -Port COM6
powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1 -Env esp32-F4R0 -Port COM6Common deploy commands:
# Classic ESP32 4MB
powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1 -Env esp32-F4R0 -Port COM6
# ESP32-S3 16MB + 8MB PSRAM
powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1 -Env esp32s3-F16R8 -Port COM6
# Build only, no flash
powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1 -Env esp32s3-F16R8 -BuildOnly
# Open serial monitor after deploy
powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1 -Env esp32s3-F16R8 -Port COM6 -Monitor
# Skip doctor (environment already verified)
powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1 -Env esp32s3-F16R8 -Port COM6 -SkipDoctor
# Only update filesystem
powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1 -Env esp32s3-F16R8 -Port COM6 -SkipFirmware
# Only update firmware
powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1 -Env esp32s3-F16R8 -Port COM6 -SkipFsBefore flashing, the script automatically: cleans residual processes → checks build cache integrity → runs doctor. Build failures trigger automatic clean + rebuild.
Release Packaging
powershell -ExecutionPolicy Bypass -File scripts\build-all-artifacts.ps1 -CleanOutputdist\firmware\all-latest\manifest.json records environment, version, hardware, deploy commands, file sizes, SHA-256 and smoke status placeholders. Naming convention: fastbee-{chip}-F{flash}R{psram}.bin.
To flash an existing release package directly:
powershell -ExecutionPolicy Bypass -File scripts\flash-release.ps1 -Env esp32s3-F16R8 -Port COM6This writes the merged image from dist\firmware\all-latest for the target environment (default baud 921600), suitable for interrupted upgrade recovery or mass production. Add -DryRun to preview without writing.
First Access Verification
After deployment, open a browser and navigate to the device IP (or http://fastbee.local). Verify:
- Login page loads, authenticate with default credentials.
- Dashboard shows device status (uptime, heap, flash).
- Network page shows connected status and IP.
- Peripheral and PeriphExec pages load without errors.
If the dashboard is accessible but status is empty, the device may still be starting up or the session has expired — refresh and re-login.
Test Layers
Test layering follows a pyramid approach from low-cost to high-fidelity; daily development prioritizes lower layers, release verification must cover device and long-run layers.
| Layer | Entry Point | Coverage | When |
|---|---|---|---|
| Static checks | scripts\test-all.ps1 -Checks static | UTF-8, test registration, API matrix, build matrix, doc links, default config safety, i18n, web assets, Git whitespace | Every commit |
| Native unit tests | scripts\test-all.ps1 -Checks native | Host-side C++ unit tests (CommandBus, ErrorHandler, OTA, RuleScript, Batch/SSE, PeriphExec, anti-brick source locks) | Core logic/API/config changes |
| Full build | scripts\test-all.ps1 -Checks build | Nine release environments across ESP32, ESP32-C3, ESP32-C6 and ESP32-S3 | Firmware or build switch changes |
| Release artifacts | scripts\test-all.ps1 -Checks artifacts | Merged firmware, LittleFS, manifest.json | Preparing release or delivery |
| Device smoke | scripts\test-all.ps1 -Checks device-smoke | Login, system, capabilities, network, device, protocol, MQTT, peripherals, PeriphExec, logs/files/OTA/users | After flashing a real device |
| Device stability | scripts\test-all.ps1 -Checks device-soak | Multi-round API cycling, capabilities, low-memory protection, auth stability, response latency | After critical changes and before release |
| Browser quick | scripts\test-all.ps1 -Checks browser-quick | Discovered @quick core cases: login/dashboard/network/MQTT/peripherals/rules | Daily development and firmware smoke |
| Browser full | scripts\test-all.ps1 -Checks browser | All discovered Playwright cases and edge cases | Pre-release full regression |
Version Matrix
| PlatformIO Env | Chip/Hardware | Required Checks |
|---|---|---|
esp32-F4R0 | ESP32 4MB Flash | static, native, build, device smoke |
esp32-F8R0 | ESP32 8MB Flash, no PSRAM | static, build, OTA and device smoke |
esp32-F8R8 | ESP32 8MB Flash + 8MB PSRAM | static, build, OTA and device smoke |
esp32c3-F4R0 | ESP32-C3 4MB Flash | static, build, device smoke |
esp32c6-F4R0 | ESP32-C6 4MB Flash | static, build, device smoke |
esp32c6-F8R0 | ESP32-C6 8MB Flash, no PSRAM | static, build, OTA and device smoke |
esp32s3-F8R0 | ESP32-S3 8MB Flash, no PSRAM | static, build, OTA and device smoke |
esp32s3-F8R8 | ESP32-S3 8MB Flash + 8MB PSRAM | static, build, OTA and device smoke |
esp32s3-F16R8 | ESP32-S3 16MB Flash + 8MB PSRAM | static, native, build, artifacts, device smoke & stability |
4MB environments use partitions/4MB.csv and have no OTA route. 8MB and 16MB environments use partitions/8MB.csv and partitions/16MB.csv with A/B application slots. Runtime features still depend on the feature state and available memory.
Temporary Five-board STA Bench
Use STA addresses for lab verification and do not switch the test computer to a device AP. The values below are temporary bench mappings, not firmware defaults. Update the local fleet configuration after DHCP or serial-port changes, and never interchange the five configuration sources.
| Serial | Environment | STA address | Configuration source |
|---|---|---|---|
| COM4 | esp32c3-F4R0 | 192.168.5.55 | docs/esp32c3-deployment-config.json |
| COM11 | esp32c6-F4R0 | 192.168.5.19 | docs/c6-rgb-led-f4r0/ |
| COM15 | esp32s3-F16R8 | 192.168.5.22 | docs/ecm50a-s3-f16r8/ |
| COM16 | esp32-F4R0 | 192.168.5.21 | docs/puzhong-esp32-config.json |
| COM7 | esp32s3-F8R0 | 192.168.5.67 | docs/waveshare-esp32s3-relay6ch/ |
Deploy one isolated configuration and run the fleet checks from the firmware source repository:
powershell -ExecutionPolicy Bypass -File scripts\deploy-config-bundle.ps1 `
-Env esp32-F4R0 -Port COM16 `
-BundlePath docs\puzhong-esp32-config.json -MemoryProfile auto
powershell -ExecutionPolicy Bypass -File scripts\run-device-fleet-tests.ps1 `
-DeviceConfig scripts\device-fleet.example.json -BrowserTests:$falseCommon Test Commands
All tests share a single entry point scripts\test-all.ps1, with -Checks to specify which checks to run (supports multiple).
# Quick pre-commit check (static + build)
powershell -ExecutionPolicy Bypass -Command ".\scripts\test-all.ps1 -Checks static,build"
# Full local matrix, no real device
powershell -ExecutionPolicy Bypass -Command ".\scripts\test-all.ps1 -Checks static,native,build,artifacts"
# Real device API smoke test
powershell -ExecutionPolicy Bypass -File scripts\test-all.ps1 -Checks device-smoke -BaseUrl http://<device-ip> -DeviceProfile unified
# Real device long-term stability test, CSV output to .pio/test-results
powershell -ExecutionPolicy Bypass -File scripts\test-all.ps1 -Checks device-soak -BaseUrl http://<device-ip> -DeviceProfile unified -SoakRounds 100
# Quick browser test (recommended for daily development)
powershell -ExecutionPolicy Bypass -File scripts\test-all.ps1 -Checks browser-quick -BaseUrl http://<device-ip> -DeviceProfile unified
# Full browser test (pre-release regression)
powershell -ExecutionPolicy Bypass -File scripts\test-all.ps1 -Checks browser -BaseUrl http://<device-ip> -DeviceProfile unified
# Run multiple checks in one pass
powershell -ExecutionPolicy Bypass -File scripts\test-all.ps1 -Checks static,build,device-smoke,browser-quick -BaseUrl http://<device-ip> -DeviceProfile unified-Checks Values
| Value | Description | Requires Device Params |
|---|---|---|
static | Static checks: UTF-8, test registration, API matrix, build matrix, doc links, default config safety, i18n, web assets, Git whitespace | No |
native | Host-side C++ unit tests (CommandBus, ErrorHandler, OTA, RuleScript, Batch/SSE, PeriphExec, anti-brick source locks) | No |
build | Full build across all nine release environments | No |
artifacts | Generate merged firmware, LittleFS, manifest.json release artifacts | No |
device-smoke | Device API smoke: login, system, capabilities, network, device, protocol, MQTT, peripherals, logs/files/OTA/users | Yes |
device-soak | Device long-run: multi-round API cycling, low-memory protection, auth stability, response latency | Yes |
browser-quick | All discovered @quick core cases | Yes |
browser | All discovered Playwright cases | Yes |
all | Run the release set, including browser-quick; run browser explicitly for the extended suite | Yes |
-BaseUrland-DeviceProfileare required for device-accessing checks (device-smoke,device-soak,browser-quick,browser); omitting them will cause connection errors.
Common Parameters
| Parameter | Default | Description |
|---|---|---|
-Checks | static,native,build | Checks to run, supports multiple |
-BaseUrl | empty | Required device Web address for device/browser checks |
-DeviceProfile | unified | Current release capability assertions; legacy profiles remain for old reports |
-Username / -Password | admin / admin123 | Web login credentials |
-SoakRounds | 10 | Long-run test cycle count |
-Environments | All 9 | PlatformIO environments to build |
-ContinueOnError | false | Continue running after a check fails |
-DeviceSerial | empty | Serial port for browser test device pre-check |
-BrowserHeaded | false | Show browser window (for debugging) |
-ReportDir | .pio\test-results | Long-run CSV report output directory |
-RequireNetworkConnected | false | Smoke test requires device online |
-RequireMqttConnected | false | Smoke test requires MQTT connected |
If the local Windows environment lacks gcc/g++, native will fail. This is not a firmware build failure — install MinGW/MSYS2 or run native tests in CI/Linux.
Use -DeviceProfile unified for current release firmware. The test reads capability state and accepts unavailable optional APIs when the feature is not compiled, not enabled, or rejected by the runtime budget.
Browser Automation Testing
Browser tests use Playwright to drive the device Web console, covering login, dashboard, network/device/MQTT configuration, peripheral management, rule scripts, logs and users. Integration cases also check optional file APIs in custom firmware.
Quick vs Full Tests
| Mode | Config File | Cases | Est. Time | Use Case |
|---|---|---|---|---|
Quick (browser-quick) | playwright.quick.config.ts | Discovered @quick cases | Depends on device/network | Dev-stage fast validation, CI quick regression, firmware smoke |
Full (browser) | playwright.config.ts | All discovered cases | Depends on device/network | Pre-release full acceptance, deep regression |
Quick tests filter core cases tagged @quick from each spec file, covering auth, dashboard, network, device, MQTT, peripheral CRUD, rule scripts, logs, users and optional file APIs.
Running Playwright Directly
Bypass test-all.ps1 and call Playwright directly for finer-grained control:
cd test\browser
$env:DEVICE_IP = "<device-ip>"
# Run quick / full tests
npx playwright test -c playwright.quick.config.ts # Quick
npx playwright test # Full
# Run specific tests
npx playwright test -g "AUTH-001" # Filter by name
npx playwright test -g "@quick" --project=smoke # Combined filter
# View HTML report
npx playwright show-report reports\quick-html # Quick test
npx playwright show-report reports\html # Full testTiming Comparison Report
After running quick tests, run the comparison script to see timing differences vs the last full run:
node scripts\compare-test-timing.jsOutput includes total time, per-file breakdown, top 10 slowest cases, and speedup multiplier.
@quick Tag Maintenance
Core test cases include the @quick tag in their title; the quick config filters via --grep @quick:
test('AUTH-001: Login with valid credentials @quick', async ({ page }) => { ... });When adding new tests, decide whether to include them in the quick set:
- Covers core user paths (login, config save, protocol connect) → add
@quick - Boundary values, error scenarios, detailed form field validation → full tests only
Progress Display
Both modes use the list reporter to show real-time pass/fail status and duration for each test. Quick mode has no retries (retries: 0), so failures are reported immediately for fast diagnosis.
Device Smoke Coverage
Device API tests are driven by scripts\device-api-test-matrix.json, covering:
| Category | Checks |
|---|---|
| Auth | Login, Bearer Token priority, multi-session |
| System | /api/health, /api/system/health, /api/system/info, /api/system/status, /api/system/web-runtime, /api/system/capabilities |
| Metrics | /api/system/metrics, heap, memguard, runtime metrics |
| Network | /api/network/status, /api/network/config, connection, IP, network type, AP/STA config |
| Device | /api/device/config, /api/device/info, /api/device/time |
| Protocol | MQTT status, MQTT config, compact protocol config and Modbus state when available |
| Peripherals | List, types, param validation, exec rules, exec controls, triggers, static/dynamic events, batch API |
| Capabilities | Compile support, user switch, memory-budget result and final active state |
| Optional APIs | Filesystem, logs, OTA, RuleScript, users, roles and permissions are checked against runtime capability state |
| Capability boundary | An unavailable optional API must return the documented unavailable status, not hang or crash |
Release Environment Stability Baseline
All release environments share the same recovery and long-run requirements. Hardware-specific capabilities are verified only when compiled and active.
| Acceptance | Requirement |
|---|---|
| Auto-start, config recovery and health monitoring | Required on every environment |
| WiFi/AP provisioning and network recovery | Required on every environment |
| MQTT reconnect | Required when MQTT is enabled and the broker is reachable |
| Low-memory response | Reject or degrade optional work without reboot loops or permanent Web loss |
| OTA | Required only on 8MB/16MB A/B partition environments |
| PSRAM metrics | Required only on R8 environments; psramTotal must be greater than zero |
| Long-run test | At least 72 hours; seven days is recommended for gateway or high-load deployments |
Smoke Test Checklist
| # | Test Item | Applies To | Pass Criteria |
|---|---|---|---|
| SMK-01 | Full build | All nine environments | No PlatformIO build errors |
| SMK-02 | Firmware and filesystem flash | Target hardware | Flash completes, auto-reboots and serves matching Web assets |
| SMK-03 | Power-on boot | All devices | Boot logs appear with no crash loop |
| SMK-04 | Filesystem mount | All devices | /config/*.json is readable |
| SMK-05 | AP/STA network | All devices | Provisioning remains available; STA gets an IP when credentials/network are valid |
| SMK-06 | Web/API health | All devices | /api/health returns OK and authenticated pages load |
| SMK-07 | Capability state | All devices | /api/system/capabilities and /api/features/status match the build and switches |
| SMK-08 | MQTT connection/upload | MQTT-enabled devices | Broker state and property/event upload are correct |
| SMK-09 | Command dispatch | Configured devices | Device executes and returns a result |
| SMK-10 | Reboot recovery | All devices | Network, protocol and config restore automatically |
| SMK-11 | Modbus polling/control | Modbus-active devices | Polling and writes remain stable |
| SMK-12 | OTA | 8MB/16MB A/B environments | /api/ota/status matches capability and a validated image can be applied |
| SMK-13 | Optional management APIs | When active | Logs/files/users/scripts return normally or a documented unavailable status |
Functional Test Matrix
| # | Module | Case | Expected |
|---|---|---|---|
| FUN-01 | Boot init | First boot, default config, AP | Default config loads safely |
| FUN-02 | Network | WiFi SSID/password persist | Survives reboot |
| FUN-03 | Network | Static IP/DHCP switch | Status page matches the selected mode |
| FUN-05 | MQTT | Connect, subscribe, publish, LWT | Platform state is correct when enabled |
| FUN-06 | Data collect | GPIO/ADC/basic sensor read | Correct format with no blocking |
| FUN-07 | PeriphExec | Timer/event/platform trigger | Conditions and actions match config when active |
| FUN-08 | Modbus | Slave scan, register poll, control | Stable when compiled, enabled and wired |
| FUN-09 | Web/API | Login, Bearer Token, multi-session | Auth/session behavior is correct |
| FUN-10 | Optional APIs | Logs, files, users, scripts and OTA | Works when active; otherwise returns the documented unavailable state |
| FUN-15 | Boundary | Empty config, oversized fields | Clear error response with no crash |
| FUN-16 | Resource | Low memory, concurrent API, SSE | Graceful degradation and recovery |
Disconnect/Reconnect Test
| # | Scenario | Steps | Pass Criteria |
|---|---|---|---|
| NET-01 | No network at boot | Disconnect router, power on | AP/Web accessible, no crash loop |
| NET-02 | WiFi disconnect during run | Kill router for 5 min | Logs record disconnect, main loop continues |
| NET-03 | Network recovery | Restore router | Auto IP, MQTT reconnect, platform online |
| NET-04 | MQTT unreachable | Stop broker | Backoff reconnect, local Web/PeriphExec unaffected |
| NET-05 | DNS failure | Block DNS | Connection failure reported, auto-recover on restore |
| NET-06 | Weak network | Add latency/packet loss | Timeout recoverable, no memory decline |
Power Loss Recovery Test
| # | Scenario | Steps | Pass Criteria |
|---|---|---|---|
| PWR-01 | Normal operation power loss | Power off during stable run, restore | Config intact, auto-recover online |
| PWR-02 | Data upload power loss | Power off during continuous upload | Main loop, MQTT, peripherals normal after reboot |
| PWR-03 | Config save power loss | Power off during config save | Old or new config valid, no corrupt JSON |
| PWR-04 | Filesystem update power loss | Power off during config import | Filesystem mountable, files re-writable |
| PWR-05 | OTA upload power loss | Power off during OTA upload on an A/B environment | Old firmware boots or the device remains recoverable |
| PWR-07 | Rapid power cycling | 10 consecutive power on/off cycles | No config loss, no boot hang |
Pre-Release Checklist
- Run
scripts\test-all.ps1 -Checks static,native,build,artifacts. - Flash target chips, confirming
-Envmatches the actual chip, Flash and PSRAM. - Run
node scripts\validate-config-defaults.js --staging-root .pio\fs-staging --latest-only. - Confirm default
peripherals.jsontemplates andperiph_exec.jsonrules are disabled at factory. - Run device smoke test for each delivery environment (
-Checks device-smoke -DeviceProfile unified). - Run quick browser tests (
-Checks browser-quick) to verify core page interactions. - Run at least 100 soak rounds on
esp32s3-F16R8and other high-load delivery targets. - Check
/api/system/infoheap, maxAlloc and PSRAM, plus/api/features/statusruntime states. - Check Web pages: login, network, device configuration, protocols, peripherals, PeriphExec, logs, plus Users and RuleScript when active; verify configuration import/export under Device Configuration.
- Generate release artifacts and verify
dist\firmware\all-latest\manifest.json.
Long-Run Test Report Template
Project: FastBee-Arduino IoT System
Test Type: Long-run Stability
PlatformIO Env:
Hardware Model:
Flash/PSRAM:
Firmware SHA-256:
LittleFS SHA-256:
Test Location/Network:
Platform/Broker:
Start Time:
End Time:
Total Runtime:
Key Metrics:
- Abnormal reboots:
- Network disconnects:
- Auto-recoveries:
- MQTT disconnects:
- Total data uploads:
- Upload success rate:
- Total commands:
- Command response rate:
- Min heapFree:
- Min heapMaxAlloc:
- psramTotal/psramFree (R8 only):
- Low-memory triggers:
- OTA status checks:
- Error code stats:
Conclusion:
- Pass / Fail:
- Known issues:
- Release recommendation:Recommended stability targets:
| Metric | Target |
|---|---|
| Continuous run | >= 72h; seven days recommended for gateway/high-load deployments |
| Abnormal reboots | 0, unless a documented and accepted external cause exists |
| Network recovery | 100% automatic when the configured network returns |
| Upload success | >= 99% while the broker/network is healthy |
| Command response | >= 99% while the command path is healthy |
| Memory trend | No sustained heap/maxAlloc decline; PSRAM stable on R8 hardware |
FAQ
| Symptom | First Check |
|---|---|
| Serial port not found | Confirm USB cable supports data; install CH340/CP210x drivers (see the Downloads Center); close Arduino IDE / PIO Monitor |
| Stuck waiting for sync | Hold BOOT during flash, release when write starts; press EN/RST to retry |
| Web console not loading | Confirm LittleFS was uploaded; use deploy.ps1 or run uploadfs before upload |
| PSRAM environment unstable | Verify PSRAM is present via /api/system/info (psramTotal > 0) and the environment matches the module |
| Config lost after flash | Erase full Flash first when switching partition tables or migrating from other firmware |
-DeviceProfile mismatch | Use unified for current release firmware; legacy profiles are only for compatible historical reports |
native build fails | Windows lacks gcc/g++; install MinGW/MSYS2 or run in CI/Linux |
| Browser test timeout | Check -BaseUrl IP and device reachability; globalSetup prints a warning if device is unreachable |
| Flaky browser results | Ensure no other process uses the serial/Web port; increase TEST_DELAY_MS to 800 for stability; full tests auto-retry (retries: 2), quick tests do not (retries: 0) |
| soak heap declining | Likely a leak or fragmentation; confirm MQTTS memguard degradation, check /api/system/metrics heapMinFree, and monitor psramFree on R8 hardware |
/api/mqtt/test persists config (side effect) | Same path as the Save button: persists the form config then restarts the main MQTT client. Automation must restore the original config afterwards (rewrite protocol.json via /api/files/save, set mqtt.enabled back to false) |
| STA alive but Web/lwIP hangs (serial OK, TCP dead) | A live serial heartbeat with all HTTP requests timing out points to the Web/lwIP path rather than a full device freeze. Capture WebGuard/[MEMGUARD] and network logs before recovery; use serial reset or power cycle in the field. Treat any reproducible occurrence as release-blocking and add a regression test |
| Device stays long in AP mode after fallback (probe backoff is normal) | AP-probe self-heal scans the target SSID every 2 minutes, backing off to 10 minutes after 5 consecutive failures (AP_PROBE_INTERVAL_MS/AP_PROBE_BACKOFF_THRESHOLD/AP_PROBE_BACKOFF_MS). [WiFi-AP] Pre-scan/Probing WiFi logs confirm the mechanism is working; after STA recovery mDNS rebinds and MQTT auto-reconnects |

