Files
tactility/.claude/skills/tactility-firmware/references/webserver-devserver.md
T
Adolfo Reyna 12035f2f39 chore: move firmware skills in-repo + AGENTS.md
- Migrates tactility-firmware and tactility-bluetooth from
  ~/.hermes/skills/ into .claude/skills/ for repo co-location
- Adds AGENTS.md with local workstation context, board table,
  board-direct build rule, Hermes PYTHONPATH pitfall, common
  Tactility pitfalls, and in-repo skill index

Refs: personal Gitea mirror
2026-07-17 09:51:08 -04:00

3.0 KiB

WebServer + DevelopmentService Collision Debugging

Session: 2026-07-11 — Dashboard server started but MCP tools 404, dev service blocked.

Symptoms

  • Dashboard (port 80) loads, but POST /api/mcp returns 404 even with mcpEnabled=true
  • Development service (6666 /info) not reachable
  • MCP screen not appearing on RLCD board (Waveshare 4.2" monochrome ST7305)

Root Causes Found

1. httpd ctrl_port collision

HttpServer::startInternal() used HTTPD_DEFAULT_CONFIG() → ctrl_port 32768 fixed for all instances. When WebServer (80) + DevServer (6666) start near-simultaneously (service manager dispatches), second bind fails → httpd_start returns error → httpServer stays null → handlers never registered. Fix: unique ctrl_port per server: 32768 + (port % 1000) → 80→32848, 6666→33434.

Src: Tactility/Source/network/HttpServer.cpp

2. max URI handlers too low

ESP-IDF default CONFIG_HTTPD_MAX_URI_HANDLERS=8. WebServer registers 7 handlers: /, /filebrowser, /fs/* GET, /fs/* POST, /admin/*, /api/* GET, /api/* POST, /api/* PUT, /* = 7, plus 2 internal slots → config.max_uri_handlers = 9. If Kconfig max 8 < 9, httpd_start fails or later httpd_register_uri_handler fails → "no slots left". Fix: Buildscripts/sdkconfig/default.propertiesCONFIG_HTTPD_MAX_URI_HANDLERS=20.

3. DisplayIdle early return killed MCP on RLCD

D3919344 added:

if (!supportsBacklightDuty()) return true;

in onStart() — disables timer + settings load for ST7305 (no backlight fn). startMcpScreensaver() depends on settings + timer for update/auto-off. Fix: always load settings + start timer; only skip auto-backlight logic via existing if (supportsBacklightDuty()) guard in tick().

Files: Tactility/Source/service/displayidle/DisplayIdle.cpp:235-250, 303-353

4. Video stream task overflow

Stack 4096 + SPIRAM allocs + LVGL lock + socket loops → stack overflow → heap corruption blocking other tasks. Also tight loop vTaskDelay(5) even idle. Fix: 8192 + idle delay 50ms, busy 5ms.

Verification Commands

source ~/esp/esp-idf/export.sh
cd firmware
python device.py waveshare-esp32-s3-rlcd --dev
idf.py build
idf.py -p /dev/cu.usbmodem101 flash monitor
# Monitor logs:
# - Started on port 80 (ctrl_port 32848)
# - Started on port 6666 (ctrl_port 33434)
# - Mono TCP Video stream server started on port 8081
# - Color TCP Video stream server started on port 8083
# - Monochrome/RLCD detected: idle timer will run but auto-backlight off disabled
curl http://<ip>/api/sysinfo  # public
curl -X POST http://<ip>/api/mcp -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
curl http://<ip>:6666/info
  • Upstream #547 merged 2026-07-04 changed settings load to check isFile first — more correct but didn't cause this.
  • Board mis-ID: user has both ES3C28P color (240x320) and Waveshare RLCD mono (400x300). Wrong sdkconfig → wrong display driver → blank screen misattributed to MCP bug. Always verify grep CONFIG_TT_DEVICE_ID sdkconfig.