Skip to content

Add troubleshooting page with connectivity diagnostics#9

Merged
sjordan0228 merged 3 commits into
devfrom
feature/troubleshooting-page
Mar 22, 2026
Merged

Add troubleshooting page with connectivity diagnostics#9
sjordan0228 merged 3 commits into
devfrom
feature/troubleshooting-page

Conversation

@sjordan0228
Copy link
Copy Markdown
Contributor

@sjordan0228 sjordan0228 commented Mar 22, 2026

Summary

Adds /troubleshooting page to the scanner web UI with live connectivity checks and a prominently displayed Device ID for middleware configuration.

Changes

  • New /troubleshooting page with pass/fail checks for WiFi, MQTT, Spoolman, NFC reader, and free heap
  • New /api/diagnostics endpoint returning JSON health data
  • Scanner Device ID displayed prominently with one-click copy
  • getPN5180FirmwareVersion() added to NFCManager via HardwareNFCConnection
  • TagWriterHTML.h renamed to OpenPrintTagWriterHTML.h for consistency
  • /troubleshooting nav link added to all pages

How to Test

  1. Flash firmware to ESP32
  2. Open spoolsense.local/troubleshooting
  3. Click "Run Checks" — verify each check shows correct status
  4. Verify Device ID matches the ID shown in serial output
  5. Verify nav link appears on all pages

Checklist

  • Tested on hardware (ESP32-S3)
  • Compiles for both targets (pio run -e esp32dev and pio run -e esp32s3zero)
  • Avoid unnecessary heap allocations (runtime memory is limited)
  • User-facing config changes are in UserConfig.h only

Closes #3

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Troubleshooting page accessible from all app pages
    • Introduced device diagnostics feature displaying WiFi, MQTT, Spoolman, NFC reader, and memory status
    • Added "Copy Device ID" functionality on Troubleshooting page
    • New diagnostics API endpoint providing real-time device health metrics including firmware versions
  • UI Updates

    • Added Troubleshooting navigation link across all pages

- Add /troubleshooting page with WiFi, MQTT, Spoolman, NFC, and memory checks
- Add /api/diagnostics endpoint returning JSON health data
- Display scanner Device ID prominently for middleware configuration
- Add getPN5180FirmwareVersion() to NFCManager via HardwareNFCConnection
- Rename TagWriterHTML.h → OpenPrintTagWriterHTML.h for consistency
- Add /troubleshooting nav link to all pages

Closes #3
@sjordan0228
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 22, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7414593d-7b65-4bb7-b81d-f40e04a8c1b5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements a troubleshooting page at /troubleshooting with built-in diagnostics capabilities. The feature adds a web-based UI displaying device health metrics (device ID, WiFi, MQTT, Spoolman, NFC reader, memory) and a /api/diagnostics endpoint that collects system status. PN5180 firmware version accessors were added to support the diagnostics display. Navigation links to the new troubleshooting page were added across existing HTML pages.

Changes

Cohort / File(s) Summary
Navigation Links Across UI Pages
src/ConfigHTML.h, src/LandingHTML.h, src/OpenTag3DWriterHTML.h, src/ReaderHTML.h, src/TigerTagWriterHTML.h, src/UpdateHTML.h
Added Troubleshooting navigation link pointing to /troubleshooting in the top menu/navbar of multiple HTML pages.
NFC Firmware Version Accessors
src/HardwareNFCConnection.h, src/HardwareNFCConnection.cpp
Added private members fw_[2] and pn5180Ready_ to track PN5180 state; implemented public accessors getPN5180FirmwareVersion() and isPN5180Ready(); populated these fields during begin().
NFC Manager Firmware API
src/NFCManager.h, src/NFCManager.cpp
Added public method getPN5180FirmwareVersion() that wraps the hardware accessor, returning false when firmware unavailable or in test builds.
OpenPrintTag Writer Reference Update
src/OpenPrintTagWriterHTML.h
Renamed exported constant from TAG_WRITER_HTML to OPENPRINTTAG_WRITER_HTML; added troubleshooting navigation link.
Troubleshooting Page Implementation
src/TroubleshootingHTML.h
New HTML page with prominent device ID display (copy-to-clipboard action), diagnostic status cards (WiFi, MQTT, Spoolman, NFC Reader, Memory), and client-side runChecks() function that fetches /api/diagnostics and renders results as pass/warn/fail indicators with error handling.
Web Server Diagnostics Endpoint
src/WebServerManager.h, src/WebServerManager.cpp
Added route handlers: GET /troubleshooting serves troubleshooting page; GET /api/diagnostics returns JSON with device ID, firmware version, WiFi connectivity/RSSI/IP, MQTT status, Spoolman URL/version (via HTTP GET), NFC firmware, and memory/uptime metrics. Updated OpenPrintTag writer route to serve renamed HTML constant.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant WebServer as WebServerManager
    participant WiFiMgr as WiFi Manager
    participant MQTTMgr as MQTT Manager
    participant Spoolman as External API
    participant NFCMgr as NFC Manager
    participant System as System Metrics

    Browser->>WebServer: GET /troubleshooting
    WebServer-->>Browser: TROUBLESHOOTING_HTML (page with UI)
    
    Browser->>WebServer: fetch /api/diagnostics
    
    WebServer->>WiFiMgr: Get status, RSSI, IP
    WiFiMgr-->>WebServer: Connection details
    
    WebServer->>MQTTMgr: Get enabled, broker, connection
    MQTTMgr-->>WebServer: MQTT status
    
    WebServer->>Spoolman: GET /api/v1/info
    Spoolman-->>WebServer: {version: "..."} or timeout
    
    WebServer->>NFCMgr: getPN5180FirmwareVersion()
    NFCMgr-->>WebServer: fw[0], fw[1]
    
    WebServer->>System: Get free heap, uptime
    System-->>WebServer: Memory metrics
    
    WebServer-->>Browser: {device_id, wifi, mqtt, spoolman, nfc, memory}
    
    Browser->>Browser: runChecks(): parse JSON, update card states
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~23 minutes

Poem

🐰 Through /troubleshooting the bunnies now peek,
Diagnostics on paw for each system they seek—
WiFi and NFC, Spoolman's reply,
Device health dashboard makes debugging fly! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding a troubleshooting page with connectivity diagnostics, which is the primary focus of this pull request.
Description check ✅ Passed The description includes all required template sections: Summary, Changes, How to Test, and Checklist with all items completed, providing clear context for the changes.
Linked Issues check ✅ Passed The PR fully implements all coding objectives from issue #3: troubleshooting page with WiFi/MQTT/Spoolman/NFC checks, diagnostics API, Device ID display, and getPN5180FirmwareVersion() exposure.
Out of Scope Changes check ✅ Passed The TagWriterHTML.h rename is related to consistency mentioned in the PR description; all changes align with implementing the troubleshooting feature and exposing necessary firmware version APIs.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/troubleshooting-page

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/WebServerManager.h (1)

54-55: Consider grouping handleTroubleshootingPage() with other page handlers.

handleTroubleshootingPage() is a page handler but is declared after the API handlers section. For consistency with the existing organization, it should be grouped with other page handlers (lines 29-39).

📁 Suggested reorganization
     // Page handlers
     void handleLanding();
     void handleReader();
     void handleOpenPrintTagWriter();
     void handleTigerTagWriter();
     void handleOpenTag3DWriter();
     void handleSharedCSS();
     void handleSharedJS();
     void handleOpenPrintTagLogo();
     void handleTigerTagLogo();
     void handleUpdatePage();
     void handleConfigPage();
+    void handleTroubleshootingPage();

     // API handlers
     void handleApiStatus();
     // ... other API handlers ...
     void handleApiPostConfig();
     void handleApiDiagnostics();
-    void handleTroubleshootingPage();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/WebServerManager.h` around lines 54 - 55, Move the declaration of
handleTroubleshootingPage() out of the API handlers section and place it with
the other page handler declarations (the block containing the other handle*Page
functions) so it is grouped consistently with page handlers; ensure only the
declaration is relocated and keep handleApiDiagnostics() with the API handlers.
src/TroubleshootingHTML.h (1)

63-73: Navigation link order differs from other pages.

The navigation here has "Config" before "Update" (lines 70-71), while OpenPrintTagWriterHTML.h has "Update" before "Troubleshooting" before "Config" (lines 28-30). Consider keeping consistent navigation order across all pages for better UX.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/TroubleshootingHTML.h` around lines 63 - 73, Reorder the navigation links
in the <nav> block so the link order matches other pages (e.g., ensure the
"Update" anchor appears before the "Troubleshooting" anchor and "Config" comes
after), adjusting the anchors with href="/update", href="/troubleshooting", and
href="/config" in TroubleshootingHTML.h to match the ordering used in
OpenPrintTagWriterHTML.h; preserve existing classes like class="active" on the
Troubleshooting link.
src/NFCManager.cpp (1)

115-125: Unsafe static_cast violates interface abstraction.

The cast static_cast<HardwareNFCConnection*>(connection_) assumes the connection is always HardwareNFCConnection, but connection_ is declared as NFCConnectionI*. If a different implementation were injected via setConnection(), this would cause undefined behavior.

Consider one of these approaches:

  1. Add getPN5180FirmwareVersion() and isPN5180Ready() to the NFCConnectionI interface (with default no-op implementations).
  2. Use dynamic_cast with a null check (though RTTI may be disabled on ESP32).
  3. Store a separate HardwareNFCConnection* when the connection is created internally.

Current production code always uses HardwareNFCConnection, so this works in practice, but it's fragile for future maintenance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NFCManager.cpp` around lines 115 - 125, The current
NFCManager::getPN5180FirmwareVersion uses unsafe static_cast to
HardwareNFCConnection*, breaking the NFCConnectionI abstraction; change the
design so NFCManager does not assume concrete type: either (A) add
isPN5180Ready() and getPN5180FirmwareVersion(uint8_t[2]) to the NFCConnectionI
interface (with default no-op/false implementations) and call those methods on
connection_, or (B) if you must keep the methods only on HardwareNFCConnection,
ensure setConnection stores a separate HardwareNFCConnection* when a
HardwareNFCConnection is provided and check that pointer here before calling,
avoiding static_cast; update NFCManager::getPN5180FirmwareVersion,
NFCConnectionI, and setConnection accordingly.
src/WebServerManager.cpp (1)

227-244: Synchronous HTTP call blocks web server for up to 3 seconds.

The Spoolman reachability check performs a blocking HTTP GET with a 3-second timeout. This blocks the web server's single-threaded request handling, potentially causing other requests to queue or timeout.

For a diagnostics endpoint that's called infrequently, this is acceptable, but consider:

  1. Reducing the timeout (e.g., 1-2 seconds) since this is just a connectivity check.
  2. Documenting this latency expectation for the frontend.

The current implementation is functional for the troubleshooting use case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/WebServerManager.cpp` around lines 227 - 244, The synchronous Spoolman
reachability check in the spoolmanEnabled block of WebServerManager.cpp uses
HTTPClient http with http.setTimeout(3000), which can block the single-threaded
web server for up to 3s; reduce that timeout to 1000–2000 ms (e.g., 1500) by
changing the http.setTimeout call and keep the rest of the logic
(snprintf(infoUrl...), http.GET(), deserializeJson(info...)) intact;
additionally, add a short comment near spoolman["reachable"] or the diagnostics
endpoint to document the expected latency for the frontend.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/HardwareNFCConnection.cpp`:
- Around line 115-117: The code currently ignores the boolean return of
readEEprom() before assigning fw_ and setting pn5180Ready_, which can mark the
device ready with invalid firmware data; change the logic in the block that
calls readEEprom() so you capture its return value and only assign fw_[0],
fw_[1] and set pn5180Ready_ = true when readEEprom() returns true, and handle
the failure path consistently (e.g., log/error and return false or leave
pn5180Ready_ false), mirroring how setupRF() is checked.

In `@src/TroubleshootingHTML.h`:
- Around line 250-254: The catch block is passing raw service IDs
('wifi','mqtt','spoolman','nfc','heap') into setCheck, which shows raw IDs
instead of user-facing names; change the catch to map each id to its display
name (e.g. via an existing helper or a new lookup like getDisplayName(id) or a
DISPLAY_NAME map) and pass that display name as the third argument to setCheck
(keep setCheck(id, 'fail', <displayName>, 'Error fetching diagnostics')). Ensure
you reference the same ids and the setCheck function when implementing the
lookup.

---

Nitpick comments:
In `@src/NFCManager.cpp`:
- Around line 115-125: The current NFCManager::getPN5180FirmwareVersion uses
unsafe static_cast to HardwareNFCConnection*, breaking the NFCConnectionI
abstraction; change the design so NFCManager does not assume concrete type:
either (A) add isPN5180Ready() and getPN5180FirmwareVersion(uint8_t[2]) to the
NFCConnectionI interface (with default no-op/false implementations) and call
those methods on connection_, or (B) if you must keep the methods only on
HardwareNFCConnection, ensure setConnection stores a separate
HardwareNFCConnection* when a HardwareNFCConnection is provided and check that
pointer here before calling, avoiding static_cast; update
NFCManager::getPN5180FirmwareVersion, NFCConnectionI, and setConnection
accordingly.

In `@src/TroubleshootingHTML.h`:
- Around line 63-73: Reorder the navigation links in the <nav> block so the link
order matches other pages (e.g., ensure the "Update" anchor appears before the
"Troubleshooting" anchor and "Config" comes after), adjusting the anchors with
href="/update", href="/troubleshooting", and href="/config" in
TroubleshootingHTML.h to match the ordering used in OpenPrintTagWriterHTML.h;
preserve existing classes like class="active" on the Troubleshooting link.

In `@src/WebServerManager.cpp`:
- Around line 227-244: The synchronous Spoolman reachability check in the
spoolmanEnabled block of WebServerManager.cpp uses HTTPClient http with
http.setTimeout(3000), which can block the single-threaded web server for up to
3s; reduce that timeout to 1000–2000 ms (e.g., 1500) by changing the
http.setTimeout call and keep the rest of the logic (snprintf(infoUrl...),
http.GET(), deserializeJson(info...)) intact; additionally, add a short comment
near spoolman["reachable"] or the diagnostics endpoint to document the expected
latency for the frontend.

In `@src/WebServerManager.h`:
- Around line 54-55: Move the declaration of handleTroubleshootingPage() out of
the API handlers section and place it with the other page handler declarations
(the block containing the other handle*Page functions) so it is grouped
consistently with page handlers; ensure only the declaration is relocated and
keep handleApiDiagnostics() with the API handlers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8eea4fda-f168-4072-b9f4-26a40d4d3a0f

📥 Commits

Reviewing files that changed from the base of the PR and between c0a0762 and f300447.

📒 Files selected for processing (14)
  • src/ConfigHTML.h
  • src/HardwareNFCConnection.cpp
  • src/HardwareNFCConnection.h
  • src/LandingHTML.h
  • src/NFCManager.cpp
  • src/NFCManager.h
  • src/OpenPrintTagWriterHTML.h
  • src/OpenTag3DWriterHTML.h
  • src/ReaderHTML.h
  • src/TigerTagWriterHTML.h
  • src/TroubleshootingHTML.h
  • src/UpdateHTML.h
  • src/WebServerManager.cpp
  • src/WebServerManager.h

Comment thread src/HardwareNFCConnection.cpp Outdated
Comment on lines +115 to +117
fw_[0] = firmwareVersion[0];
fw_[1] = firmwareVersion[1];
pn5180Ready_ = true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if readEEprom has any return value or error indication in the PN5180 library
ast-grep --pattern 'readEEprom($$$)'

# Also check the header for pn5180Ready_ and fw_ declarations
rg -n "pn5180Ready_|fw_\[" --type cpp

Repository: SpoolSense/spoolsense_scanner

Length of output: 602


🏁 Script executed:

# Search for readEEprom method definition and declarations
rg -n "readEEprom" --type cpp -B 2 -A 5

# Check the broader context around lines 115-117
sed -n '100,125p' src/HardwareNFCConnection.cpp

Repository: SpoolSense/spoolsense_scanner

Length of output: 2606


🏁 Script executed:

# Check other readEEprom calls and their error handling
rg -n "readEEprom" --type cpp -B 1 -A 3

# Show the setupRF call handling for comparison
sed -n '118,125p' src/HardwareNFCConnection.cpp

Repository: SpoolSense/spoolsense_scanner

Length of output: 1340


Check the readEEprom() return value before setting pn5180Ready_.

The readEEprom() method returns a bool indicating success or failure, but line 114 ignores this return value. If the EEPROM read fails, fw_ will contain uninitialized data while pn5180Ready_ is still set to true. This is inconsistent with how setupRF() is handled immediately after (line 120), where the return value is properly checked.

Update the code to check the return value:

Proposed fix
     // Read firmware version
     uint8_t firmwareVersion[2];
-    nfc_->readEEprom(PN5180_FIRMWARE_VERSION, firmwareVersion, 2);
-    fw_[0] = firmwareVersion[0];
-    fw_[1] = firmwareVersion[1];
-    pn5180Ready_ = true;
+    if (nfc_->readEEprom(PN5180_FIRMWARE_VERSION, firmwareVersion, 2)) {
+        fw_[0] = firmwareVersion[0];
+        fw_[1] = firmwareVersion[1];
+        pn5180Ready_ = true;
+    } else {
+        Serial.println("HardwareNFCConnection: Failed to read firmware version");
+        return false;
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/HardwareNFCConnection.cpp` around lines 115 - 117, The code currently
ignores the boolean return of readEEprom() before assigning fw_ and setting
pn5180Ready_, which can mark the device ready with invalid firmware data; change
the logic in the block that calls readEEprom() so you capture its return value
and only assign fw_[0], fw_[1] and set pn5180Ready_ = true when readEEprom()
returns true, and handle the failure path consistently (e.g., log/error and
return false or leave pn5180Ready_ false), mirroring how setupRF() is checked.

Comment thread src/TroubleshootingHTML.h
Comment on lines +250 to +254
} catch(e) {
['wifi','mqtt','spoolman','nfc','heap'].forEach(id => {
setCheck(id, 'fail', id, 'Error fetching diagnostics');
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor: Error handler shows raw IDs instead of proper names.

When the diagnostics fetch fails, the error message shows the raw ID (e.g., "wifi") instead of the proper display name (e.g., "WiFi"). This is a minor cosmetic issue.

       } catch(e) {
-        ['wifi','mqtt','spoolman','nfc','heap'].forEach(id => {
-          setCheck(id, 'fail', id, 'Error fetching diagnostics');
+        const names = {wifi:'WiFi', mqtt:'MQTT Broker', spoolman:'Spoolman', nfc:'NFC Reader (PN5180)', heap:'Memory'};
+        ['wifi','mqtt','spoolman','nfc','heap'].forEach(id => {
+          setCheck(id, 'fail', names[id], 'Error fetching diagnostics');
         });
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/TroubleshootingHTML.h` around lines 250 - 254, The catch block is passing
raw service IDs ('wifi','mqtt','spoolman','nfc','heap') into setCheck, which
shows raw IDs instead of user-facing names; change the catch to map each id to
its display name (e.g. via an existing helper or a new lookup like
getDisplayName(id) or a DISPLAY_NAME map) and pass that display name as the
third argument to setCheck (keep setCheck(id, 'fail', <displayName>, 'Error
fetching diagnostics')). Ensure you reference the same ids and the setCheck
function when implementing the lookup.

@sjordan0228
Copy link
Copy Markdown
Contributor Author

Addressing CodeRabbit findings: Fixed readEEprom return value not being checked before setting pn5180Ready_ (line 117). Also fixing minor display name issue in error handler (line 254).

@sjordan0228
Copy link
Copy Markdown
Contributor Author

Good bot 🥕 — error handler now uses proper display names.

@sjordan0228
Copy link
Copy Markdown
Contributor Author

All checks passing on hardware (ESP32-S3). CodeRabbit findings addressed — readEEprom error handling fixed and display names corrected. Merging to dev.

@sjordan0228 sjordan0228 merged commit 8df3779 into dev Mar 22, 2026
1 check passed
@coderabbitai coderabbitai Bot mentioned this pull request Mar 24, 2026
5 tasks
@sjordan0228 sjordan0228 deleted the feature/troubleshooting-page branch March 26, 2026 02:00
@coderabbitai coderabbitai Bot mentioned this pull request Mar 28, 2026
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant