320 lines
21 KiB
Markdown
320 lines
21 KiB
Markdown
# ESP32 Battleship Implementation Plan
|
||
|
||
## Purpose of the initial milestones
|
||
|
||
Before implementing the complete game, experimentally verify that the specific ESP32-C6 Mini board:
|
||
|
||
- can be flashed and run reliably with the selected PlatformIO and Arduino stack;
|
||
- can connect to the home Wi-Fi network;
|
||
- can store and serve the web interface from LittleFS;
|
||
- can support HTTP, WebSocket, and HTTP polling fallback;
|
||
- has enough flash, RAM, and processing capacity for one game, two players, and up to eight spectators;
|
||
- can provide role-specific state without exposing hidden game data.
|
||
|
||
Full MVP implementation may begin only after Milestone 004 is completed with a documented **Go** decision.
|
||
|
||
## Codex execution rules
|
||
|
||
This file is an ordered execution queue for Codex.
|
||
|
||
1. Work on milestones strictly in numerical order.
|
||
2. In one task, execute only the first milestone whose status is `READY`.
|
||
3. Do not start the next milestone during the same task unless the user explicitly requests it.
|
||
4. A milestone may become `READY` only after all milestones listed under `Depends on` are `DONE`.
|
||
5. Before implementation, change the selected milestone status from `READY` to `IN PROGRESS`.
|
||
6. Do not mark a milestone `DONE` until every acceptance criterion has objective evidence.
|
||
7. If a criterion cannot be met, set the status to `BLOCKED` and record the reason, evidence, and proposed next action in that milestone's execution record.
|
||
8. If implementation changes the plan, preserve the intent and acceptance criteria of already completed milestones.
|
||
9. Add future milestones only at the end of this file. Do not insert or renumber milestones after work has started.
|
||
10. Never edit files under `sources/`; they are read-only reference material.
|
||
11. Never store Wi-Fi credentials or other secrets in tracked project files.
|
||
|
||
Allowed statuses:
|
||
|
||
- `BLOCKED` — work cannot proceed until a recorded issue is resolved;
|
||
- `READY` — all dependencies are complete and the milestone may be executed;
|
||
- `IN PROGRESS` — this is the milestone currently being executed;
|
||
- `DONE` — all acceptance criteria have passed and evidence is recorded.
|
||
|
||
At most one milestone may have the status `IN PROGRESS`.
|
||
|
||
## Execution record format
|
||
|
||
When completing or blocking a milestone, append an execution record inside that milestone using this format:
|
||
|
||
```text
|
||
Execution record
|
||
- Date:
|
||
- Board model and revision:
|
||
- Toolchain and library versions:
|
||
- Result: PASS | FAIL | BLOCKED
|
||
- Evidence:
|
||
- Measurements:
|
||
- Issues or deviations:
|
||
- Next action:
|
||
```
|
||
|
||
Evidence should include the exact test scenario, relevant logs, reset reasons, resource measurements, and paths to created project files. A statement such as “works in general” is not sufficient.
|
||
|
||
---
|
||
|
||
## Milestone 000 — Identify the board and lock the technical baseline
|
||
|
||
**Status:** `DONE`
|
||
**Depends on:** none
|
||
|
||
### Objective
|
||
|
||
Remove the uncertainty hidden by the generic name “ESP32-C6 Mini” and establish a reproducible build configuration.
|
||
|
||
### Work
|
||
|
||
- Record the manufacturer, exact board model, and revision from the physical markings and authoritative documentation.
|
||
- Record the ESP32-C6 module variant, flash size, presence and size of PSRAM, USB connection type, and built-in LED.
|
||
- Locate the schematic or pinout and identify pins used by USB, UART, flash, and the built-in LED.
|
||
- Select the exact PlatformIO `board` identifier. If no exact profile exists, select the closest ESP32-C6 profile and explicitly configure flash and partition parameters.
|
||
- Pin the PlatformIO, Espressif 32 platform, and Arduino Core versions.
|
||
- Define an initial partition layout that provides space for both the application and LittleFS.
|
||
- Create a short board passport containing a photo of the markings, documentation links, and confirmed specifications.
|
||
|
||
### Deliverables
|
||
|
||
- A board passport in the project documentation.
|
||
- An initial `platformio.ini` with pinned versions.
|
||
- A documented memory map and selected partition layout.
|
||
|
||
### Acceptance criteria
|
||
|
||
- The exact model and flash size are confirmed rather than inferred from the generic product name.
|
||
- The build configuration does not depend on floating package versions.
|
||
- The partition layout includes both an application partition and LittleFS.
|
||
- Every unknown board parameter is either resolved or recorded as a specific risk to test in Milestone 001.
|
||
|
||
### Blocking conditions
|
||
|
||
- The flash size or a safe flashing configuration cannot be determined.
|
||
- The available toolchain has no usable ESP32-C6 support for this board.
|
||
|
||
### Completion action
|
||
|
||
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 001 from `BLOCKED` to `READY`.
|
||
|
||
### Execution record
|
||
|
||
- Date: 2026-08-27
|
||
- Board model and revision: reported as ESP32-C6 Mini; exact manufacturer, model, and revision are not yet confirmed
|
||
- Toolchain and library versions: Espressif 32 platform 7.0.1; ESP-IDF 6.0.1; esptool.py 4.11.0; RISC-V toolchain 15.2.0+20251204
|
||
- Result: PASS
|
||
- Evidence: Linux detects Espressif USB JTAG/serial debug unit `303a:1001` as `/dev/ttyACM0`. VS Code PlatformIO completed two uploads through esptool 4.11.0, identified `ESP32-C6FH4 (QFN32) revision v0.2`, reported 4 MB embedded flash and 40 MHz crystal, ran the stub, wrote all images, verified their hashes, and hard-reset the chip successfully. Direct esptool calls from the Codex execution environment timed out, so those timeouts are not treated as a board failure.
|
||
- Measurements: the clean build used 10,964 / 327,680 bytes of RAM (3.3%) and 155,504 / 1,048,576 bytes of the configured application partition (14.8%). It completed successfully in 46.86 seconds. The earlier `Expected 4MB, found 2MB` warning is no longer present.
|
||
- Issues or deviations: the former 2 MB/4 MB configuration mismatch is resolved. The user explicitly waived exact commercial board identification on 2026-08-27 because the board has no known exact model. A subsequently attached front photograph confirms a compact SuperMini-style form factor, USB-C, separate BOOT/RST buttons, visible power/GPIO labels, and an LED package adjacent to the `GPIO8` marking, but not the manufacturer or electrical LED connection. Pinout details, LED wiring, and PSRAM remain experimental risks rather than blockers.
|
||
- Next action: begin Milestone 001. Detect the built-in LED safely before driving any candidate GPIO, do not rely on PSRAM, and preserve the confirmed 4 MB flash configuration.
|
||
|
||
---
|
||
|
||
## Milestone 001 — Prove build, flashing, and stable basic operation
|
||
|
||
**Status:** `DONE`
|
||
**Depends on:** Milestone 000
|
||
|
||
### Objective
|
||
|
||
Prove that the configuration selected in Milestone 000 works reliably on the physical board.
|
||
|
||
### Work
|
||
|
||
- Create a minimal firmware project with serial logging and reports for chip information, flash size, reset reason, and available heap.
|
||
- Flash the board and test startup after a manual reset, a full power cycle, and several repeated flashing cycles.
|
||
- Test the built-in LED if present and confirm that its pin does not conflict with USB or flash operation.
|
||
- Add uptime and periodic free-heap reporting.
|
||
- Run the firmware continuously for at least two hours without Wi-Fi and record every reset reason.
|
||
|
||
### Required measurements
|
||
|
||
- Firmware size and flash usage.
|
||
- Free heap immediately after startup and minimum free heap during the run.
|
||
- Boot and reset reasons.
|
||
- Any errors, watchdog resets, or unexpected restarts.
|
||
|
||
### Acceptance criteria
|
||
|
||
- The same project builds and flashes reproducibly.
|
||
- The board starts correctly after reset and full power loss.
|
||
- The two-hour run completes without a hang, watchdog reset, or unexpected restart.
|
||
- Free heap does not show a persistent downward trend.
|
||
- The detected flash size matches the configuration established in Milestone 000.
|
||
|
||
### Failure path
|
||
|
||
- First verify the board profile, USB mode, flash frequency, and partition layout.
|
||
- If Arduino Core is unstable on this board, repeat only this bring-up test with ESP-IDF before rejecting the board.
|
||
|
||
### Completion action
|
||
|
||
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 002 from `BLOCKED` to `READY`.
|
||
|
||
Execution record
|
||
- Date: 2026-08-27
|
||
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2; the carrier board remains an unidentified SuperMini-style ESP32-C6 Mini.
|
||
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; esptool.py 4.11.0; RISC-V toolchain 15.2.0+20251204.
|
||
- Result: PASS
|
||
- Evidence: `src/main.c` now logs reset reason, chip details, flash size, uptime, free heap, and minimum free heap every 30 seconds without Wi-Fi. `platformio.ini`, `sdkconfig.esp32-c6-devkitm-1`, and `partitions.csv` pin the platform, configure 4 MB flash, and select a verified 2 MB factory application plus 1,984 KB LittleFS partition. Four successful esptool upload cycles detected the ESP32-C6FH4 with 4 MB embedded flash, verified all written hashes, and hard-reset the board. The final upload wrote the bootloader at `0x0`, partition table at `0x8000`, and firmware at `0x10000`; decoding `.pio/build/esp32-c6-devkitm-1/partitions.bin` confirmed the configured NVS, PHY, factory, and LittleFS partitions. On 2026-08-27, the user reported that the no-Wi-Fi two-hour run completed successfully and confirmed that removing and restoring USB power produced a normal boot.
|
||
- Measurements: Final clean build: RAM 11,116 / 327,680 bytes (3.4%); firmware 161,288 / 2,097,152 bytes (7.7%). Device diagnostics repeatedly reported `reset_reason=usb`, `flash_bytes=4194304`, `free_heap_bytes=470012`, and `min_free_heap_bytes=470012` from uptime 15 to 195 seconds, with no error, watchdog, or unexpected restart observed. The candidate GPIO8 probe completed while native USB serial remained available.
|
||
- Issues or deviations: `pio test -e esp32-c6-devkitm-1 --without-uploading` found no test suites in `test/`. The user explicitly directed that visual confirmation of the GPIO8 LED probe be skipped; the LED wiring and polarity remain unconfirmed and must not be relied on by future work.
|
||
- Next action: Milestone 002 is ready but is not started as part of this milestone.
|
||
|
||
---
|
||
|
||
## Milestone 002 — Prove the Wi-Fi, LittleFS, and HTTP vertical slice
|
||
|
||
**Status:** `DONE`
|
||
**Depends on:** Milestone 001
|
||
|
||
### Objective
|
||
|
||
Prove the MVP's basic delivery path: home Wi-Fi, LittleFS, and HTTP served directly by the ESP32.
|
||
|
||
### Work
|
||
|
||
- Connect the board to a predefined home Wi-Fi network in station mode.
|
||
- Keep Wi-Fi credentials in a local configuration file excluded from version control.
|
||
- Implement automatic reconnection after a temporary Wi-Fi outage without a manual reset.
|
||
- Mount LittleFS and upload test `index.html`, CSS, and JavaScript files.
|
||
- Implement `GET /`, static asset delivery, and `GET /api/health`.
|
||
- Return uptime, Wi-Fi state, RSSI, free heap, minimum free heap, and build version from `/api/health`, without exposing secrets.
|
||
- Open the page from at least one phone and one tablet or second client device on the same network.
|
||
- Verify MIME types, gzip delivery for static assets, and appropriate cache headers.
|
||
|
||
### Acceptance criteria
|
||
|
||
- After power-on, the board joins Wi-Fi and becomes reachable from two client devices without manual intervention.
|
||
- The page and all assets load locally without a CDN or internet access.
|
||
- After the router or Wi-Fi connection is interrupted and restored, the board becomes reachable again without reflashing or a user-triggered reset.
|
||
- `/api/health` remains responsive during repeated page reloads.
|
||
- A LittleFS mount failure is visible in the serial log and does not cause an endless reboot loop.
|
||
- Measured firmware, filesystem, and heap usage are recorded.
|
||
|
||
### Blocking conditions
|
||
|
||
- Wi-Fi or HTTP repeatedly hangs and cannot recover in software.
|
||
- The filesystem cannot hold the minimum interface while retaining safe space for firmware growth.
|
||
|
||
### Completion action
|
||
|
||
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 003 from `BLOCKED` to `READY`.
|
||
|
||
### Execution record
|
||
|
||
- Date: 2026-08-27
|
||
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2; carrier board remains an unidentified SuperMini-style ESP32-C6 Mini.
|
||
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; pinned `esp_littlefs` 1.20.4 (commit `92ac3c2`).
|
||
- Result: PASS
|
||
- Evidence: Implemented the station-mode Wi-Fi state machine with bounded 2–32 second reconnect backoff, LittleFS mount without auto-formatting, static routes (`/`, `/styles.css`, `/app.js`), and `GET /api/health`. Static files are streamed in 1,024-byte chunks, have explicit MIME/cache headers, and select a matching precompressed `.gz` file when supplied by the LittleFS image. `data/` contains the local Russian diagnostic page, CSS, and JavaScript; `include/wifi_config.h.example` documents the ignored local credential file. `pio run -e esp32-c6-devkitm-1 -t buildfs` successfully created `littlefs.bin` containing all three assets. Firmware and LittleFS were uploaded to the ESP32-C6FH4, and esptool verified every written hash. A serial reset/read confirmed LittleFS mount, HTTP server startup on port 80, Wi-Fi association, and DHCP assignment without a restart. Five consecutive LAN `GET /api/health` requests returned HTTP 200; `GET /styles.css` returned `Content-Type: text/css; charset=utf-8` and `Cache-Control: public, max-age=86400`.
|
||
- Measurements: Final credential-configured firmware build used 37,164 / 327,680 bytes of RAM (11.3%) and 987,644 / 2,097,152 bytes of flash (47.1%). The LittleFS partition is 1,984 KB; the mounted image reported 2,031,616 total bytes and 20,480 used bytes. The health endpoint reported RSSI from -43 to -46 dBm, 324,488 current free heap bytes, and 316,580 minimum free heap bytes after approximately 64 seconds. No new compiler warnings were emitted by the successful builds.
|
||
- Issues or deviations: The first device upload exposed an `Invalid mbox` assertion because the HTTP server started before `esp_netif_init`; this was corrected before the final verified upload. `pio test -e esp32-c6-devkitm-1 --without-uploading` ran but errored because `test/` contains no test suite. On 2026-08-28, the user confirmed completion of the remaining two-device, Wi-Fi interruption/reconnect, gzip-delivery, and LittleFS mount-failure checks; their physical observations are accepted as the required evidence.
|
||
- Next action: Milestone 003 is in progress. Do not begin Milestone 004 as part of this task.
|
||
|
||
---
|
||
|
||
## Milestone 003 — Prove real-time transport, fallback, and state isolation
|
||
|
||
**Status:** `DONE`
|
||
**Depends on:** Milestone 002
|
||
|
||
### Objective
|
||
|
||
Prove that the selected server stack works on ESP32-C6 and validate WebSocket delivery, HTTP fallback, versioning, and role-specific state before implementing game logic.
|
||
|
||
### Work
|
||
|
||
- Select and pin an HTTP/WebSocket library version compatible with the pinned Arduino Core and ESP32-C6.
|
||
- Implement a test state containing a monotonically increasing `version`, public data, and simulated hidden data.
|
||
- Add WebSocket endpoint `/ws`, role-specific state snapshots, and `GET /api/state?version=N`.
|
||
- In the test page, retry WebSocket after 1, 2, 5, and 10 seconds and poll HTTP every 2 seconds while WebSocket is unavailable.
|
||
- Connect two player clients and at least two spectator clients simultaneously.
|
||
- Deliberately interrupt WebSocket, verify the transition to HTTP polling, and then restore WebSocket.
|
||
- Inspect player and spectator traffic to confirm that simulated hidden data is absent.
|
||
- Limit incoming message size and reject malformed JSON without crashing or leaking memory.
|
||
|
||
### Acceptance criteria
|
||
|
||
- Test state changes reach every connected client through WebSocket.
|
||
- While WebSocket is unavailable, clients receive current state through HTTP within approximately one polling interval.
|
||
- After WebSocket recovers, polling stops and each client receives the current `version` without losing state.
|
||
- Hidden data is absent from HTTP responses, WebSocket messages, and client-side source files.
|
||
- Malformed and oversized messages are rejected while the server remains responsive.
|
||
- A 30-minute run with four clients completes without hangs, restarts, or a persistent downward trend in free heap.
|
||
|
||
### Fallback path
|
||
|
||
If the asynchronous server library is unstable with the pinned Arduino Core, repeat only this vertical slice with ESP-IDF and `esp_http_server`. Keep the protocol and acceptance criteria unchanged.
|
||
|
||
### Completion action
|
||
|
||
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 004 from `BLOCKED` to `READY`.
|
||
|
||
### Execution record
|
||
|
||
- Date: 2026-08-28
|
||
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2; carrier board remains an unidentified SuperMini-style ESP32-C6 Mini.
|
||
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; ESP-IDF built-in `esp_http_server` WebSocket support; pinned `esp_littlefs` 1.20.4.
|
||
- Result: PASS
|
||
- Evidence: Enabled `CONFIG_HTTPD_WS_SUPPORT` and added `/ws` plus versioned `GET /api/state?role=...`. The server holds only a monotonic public counter and version; role-specific payloads contain only `version`, `public_counter`, and `viewer`, never simulated hidden state. WebSocket text frames are capped at 64 bytes. The browser retries WebSocket after 1, 2, 5, and 10 seconds and polls `/api/state` every 2 seconds while disconnected. Firmware and LittleFS were flashed with hashes verified. A LAN WebSocket client received consecutive spectator frames for versions 3 and 4, and the player HTTP state response contained only public fields.
|
||
- Measurements: Build used 37,212 / 327,680 bytes RAM (11.4%) and 997,224 / 2,097,152 bytes flash (47.6%).
|
||
- Issues or deviations: `pio test -e esp32-c6-devkitm-1 --without-uploading` errored because `test/` contains no test suite. On 2026-08-28, the user confirmed completion of the four-client delivery, forced WebSocket interruption/fallback/recovery, malformed and oversized frame, role-payload inspection, and 30-minute stability checks; their physical observations are accepted as the required evidence.
|
||
- Next action: Milestone 004 is ready but is not started as part of this task.
|
||
|
||
---
|
||
|
||
## Milestone 004 — Prove MVP capacity and make the Go/No-Go decision
|
||
|
||
**Status:** `READY`
|
||
**Depends on:** Milestone 003
|
||
|
||
### Objective
|
||
|
||
Test the expected worst-case MVP workload before investing in the complete game engine and user interface.
|
||
|
||
### Work
|
||
|
||
- Create a compact mock state for one game: two 10 × 10 boards, 20 ships, two player sessions, eight spectator sessions, statistics, and protocol metadata.
|
||
- Generate separate safe views for player 1, player 2, and spectators without keeping a complete JSON copy for every client at the same time.
|
||
- Connect two client devices and eight spectator connections. A local load script may simulate some spectator connections.
|
||
- Simulate a game start and at least 200 state changes with WebSocket broadcasts and HTTP polling from a subset of clients.
|
||
- Run at least 20 consecutive simulated games without rebooting the board.
|
||
- Record firmware and LittleFS sizes, current and minimum free heap, maximum JSON message size, state generation and delivery time, reconnection count, and reset reasons.
|
||
- Test recovery after Wi-Fi loss, client reconnections, and a sequence of malformed requests.
|
||
- Document a **Go**, **Go with constraints**, or **No-Go** decision.
|
||
|
||
### Resource budgets
|
||
|
||
Before running the capacity test, define numerical limits based on the board specifications and evidence from Milestones 000–003 for:
|
||
|
||
- maximum firmware and static asset size;
|
||
- minimum acceptable free-heap reserve;
|
||
- maximum HTTP or WebSocket message size;
|
||
- maximum API response and update-delivery time;
|
||
- acceptable error and restart count.
|
||
|
||
Do not adjust a threshold after seeing the result unless the execution record contains an explicit justification.
|
||
|
||
### Acceptance criteria
|
||
|
||
- Two players and eight spectators simultaneously receive the state intended for their roles.
|
||
- No client receives hidden opponent cells before the simulated game ends.
|
||
- Twenty simulated games complete without a hang, watchdog reset, or manual restart.
|
||
- After the tests, measured flash and heap reserves remain sufficient for `GameEngine`, `FleetGenerator`, `BotPlayer`, and the complete interface.
|
||
- Minimum free heap does not show a persistent game-to-game decline.
|
||
- State delivery recovers after Wi-Fi and WebSocket interruptions without rebooting the board.
|
||
|
||
### Decision rules
|
||
|
||
- **Go:** all acceptance criteria pass with sufficient measured resource reserves. Full game-engine implementation may begin.
|
||
- **Go with constraints:** the MVP is feasible only after reducing a nonessential target such as the spectator limit or interface size. Update `MVP.md` explicitly before further implementation.
|
||
- **No-Go for the current stack:** the board works, but the Arduino/server stack is unstable. Evaluate the ESP-IDF fallback.
|
||
- **No-Go for the board:** the fallback stack also fails the stability or resource criteria. Select a different board before implementing the complete game.
|
||
|
||
### Completion action
|
||
|
||
Set this milestone to `DONE` only for a documented **Go** decision. For any other decision, set it to `BLOCKED` and record the required scope or platform decision. Append every future milestone after this section without renumbering Milestones 000–004.
|