diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index bf586a2..fea40d6 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,22 +1,19 @@ name: Bug report -description: Report unexpected behavior in Stickwalker. +description: Report a Stickwalker problem. body: - type: textarea id: reproduction attributes: - label: What happened? - description: Describe the steps, expected result and observed result. + label: Steps and result validations: required: true - type: textarea id: setup attributes: - label: Version and setup - description: Which release or custom build are you using? Is USB connected? For button problems, include the layout and rotation; for connection problems, include the game, distance and alignment. + label: Firmware version validations: required: true - type: textarea id: evidence attributes: label: Screenshots or logs - description: Attach anything that helps explain the problem, if available. Keep full flash and EEPROM backups private because they contain your game data. diff --git a/.github/ISSUE_TEMPLATE/change.yml b/.github/ISSUE_TEMPLATE/change.yml index 84e4fef..2948eaf 100644 --- a/.github/ISSUE_TEMPLATE/change.yml +++ b/.github/ISSUE_TEMPLATE/change.yml @@ -4,12 +4,6 @@ body: - type: textarea id: need attributes: - label: What would this help you do? - description: Describe the use case and any current obstacle. + label: What would you change? validations: required: true - - type: textarea - id: proposal - attributes: - label: Proposed behavior - description: Include an example or tradeoff if you have one. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 49797e7..c8b9bfb 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,5 +1 @@ -Describe the problem and resulting behavior. - -## Validation - -Record the checks run and any physical qualification still needed. + diff --git a/CONTEXT.md b/CONTEXT.md deleted file mode 100644 index f786114..0000000 --- a/CONTEXT.md +++ /dev/null @@ -1,20 +0,0 @@ -# Stickwalker terms - -Stickwalker is firmware for recreating a Pokéwalker on the M5Stack Stick S3. -The reconstructed firmware implements the game and infrared protocol. -The Stick adapters connect it to the new hardware. - -| Term | Meaning | -| --- | --- | -| Native firmware | The original Pokéwalker program represented by the matching H8 reconstruction. | -| Experience fidelity | Recreating gameplay, progression, screen availability, responsiveness, walking and game compatibility on the Stick. Hardware settings can change to produce the same experience. | -| Native UI cadence | The application's schedule for rendering and visual state. Ordinary refresh opportunities occur every quarter second; individual animations may advance more slowly. | -| Panel scan rate | The TFT controller's internal rate for driving its stored image, independent of application cadence and processor image transfers. | -| Stationary carry | Screen-off operation detecting whether meaningful movement has begun. | -| Moving carry | Screen-off operation collecting and processing acceleration for walking progression. | -| Interactive use | Viewing or controlling the UI, including idle-on time after the last action. | -| Native screen availability | The original rules for waking, remaining visible and turning the display off, with foreground-specific exceptions. | -| Motion processing equivalence | Agreement in native motion/game results from the same chronological samples, external events and initial state. This detects scheduling regressions. | -| Motion accuracy | Agreement between detected steps and actual walking, measured on hardware and affected by sensor input, carrying position and the estimator. | -| Native sampling gap | An interval where sound or IR owns foreground execution and shared state, suspending native motion sampling. Ordinary menus and games continue sampling. | -| IR session | A native protocol exchange with its peer, retaining foreground ownership and timing requirements. | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6625811..3c4a763 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,56 +1,9 @@ # Contributing -For firmware changes, start with the [Stick development guide](stick/README.md) -or the [application source guide](docs/source.md). +Use [Issues](https://github.com/lumirth/stickwalker/issues) for bugs and feature requests. Run the [firmware and formatting checks](docs/build.md#checks) for code changes. -## Report a problem +Test through compiled-firmware workflows with physical or simulated inputs and independent expected results. Extend an existing workflow where possible. Do not add unit tests, integration tests or tests that mirror the implementation. -Include the release version, or commit for a custom build, what you did, and -what happened. A short sequence that reproduces the problem is especially -useful. Also include the details relevant to it: +Changes to shared Pokéwalker code must still match the original H8 binary under both supported compilers. -- For buttons or wake, the layout, rotation and whether USB was connected. -- For infrared, the photodiode wiring, other device or game, distance and - orientation. Include how often the connection succeeds or fails. -- For a test script, the command and relevant output. - -Keep flash backups and EEPROM images private; they contain registration and -game data. Share the diagnostic output needed to describe the problem. - -## Propose a change - -Explain the problem and how the patch changes the Stick's behavior. Stick -hardware adapters live in `stick/`; game rules and the infrared protocol live -in `src/`. Preserve stored record sizes, byte order and the order in which tasks -use shared working memory. - -Run `uv run python stick/check.py` for code changes. It builds the firmware and -executes the E2E workflows. -Describe the device tests you ran. Shared H8 application changes also need the -format, lint and binary-matching checks in the [H8 build guide](docs/build.md). -For documentation, check the instructions and links you changed. - -## Tests - -Run the compiled firmware through complete user workflows. Drive physical -inputs on the Stick or its simulated board, then check what someone can see, -hear, receive over infrared or recover from saved flash. Establish expectations -from the user contract, protocol specification or independent recordings. - -The automated [firmware E2E checks](stick/simulation/README.md) boot the production -image through the ESP32-S3 ROM and its bootloader. Their board models implement -GPIO and bus devices from the hardware specifications. Keep the application and -SDK drivers in that execution path. - -Review coverage before adding a test for a bug fix. Extend a relevant workflow -when there is a genuine gap. Remove unit and integration tests, -tautologies and tests that merely detect implementation changes. Expectations -must describe useful behavior and come from evidence independent of the code -being exercised. Apply this review to existing tests too. - -## Source and license - -Contributions use the project's [MIT license](LICENSE). Credit third-party -material and keep its notices. Keep ROM dumps, extracted retail artwork, -proprietary compiler binaries and confidential source out of contributions. -See [third-party notices](docs/third-party.md) for the included dependencies. +Contributions use the [MIT license](LICENSE). Retain third-party notices. Do not commit ROMs, extracted retail artwork or proprietary compilers. diff --git a/README.md b/README.md index e3e6961..444ebb4 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,27 @@

- Stickwalker handheld with a Pineco on its screen + Stickwalker

# Stickwalker -Stickwalker is firmware that turns an M5Stack Stick S3 into a recreation of the -Pokéwalker. Take a Pokémon from HeartGold or SoulSilver for a walk, earn Watts, -catch Pokémon and find items, then send your Pokémon and finds back to the game -over infrared. +Stickwalker ports my [matching Pokéwalker firmware reconstruction](https://github.com/lumirth/pw) to the M5Stack Stick S3. -I built it from my [binary-matching reconstruction of the Pokéwalker firmware](docs/h8-reconstruction.md). -It runs the original game logic, including step counting, minigames, progression, -sounds and communication with the games and other walkers. +## Install -## Get started +You need a Stick S3, a BPV10NF photodiode and a USB-C data cable. Connect the photodiode's anode to GND and cathode to G5. -You'll need: +Download the firmware ZIP from [Releases](https://github.com/lumirth/stickwalker/releases) and follow the [installation guide](stick/docs/install.md). -- An M5Stack Stick S3. -- A BPV10NF photodiode for receiving infrared. The Stick has a built-in transmitter. -- A USB-C data cable and a computer to install the firmware. -- An infrared-equipped HeartGold or SoulSilver Game Card and a console that can run it. +Downloads use placeholder built-in artwork. For the original graphics, [build with artwork from your own ROM](docs/build.md#artwork). -Download the firmware ZIP from [Releases](https://github.com/lumirth/stickwalker/releases), -then follow the [installation guide](stick/docs/install.md). It covers backups, -firmware updates and the two photodiode connections, G5 and GND. Once the Stick -is ready, [connect to your game and choose a Pokémon to take with you](stick/docs/playing.md). +## Controls -The download uses placeholder pictures and a font for the built-in screens. -Pokémon and route artwork arrive from your game. You can also -[build the firmware](stick/docs/build.md) with -[artwork extracted from your own Pokéwalker firmware](assets/README.md). +L is Left, M is Center and R is Right. The default orientation is left-side-down. -## The Pokéwalker on the Stick S3 +Hold M for about half a second to wake the screen. Hold L+R for one second to open Stick Settings. See [controls](stick/docs/controls.md) for the other layouts. -Step counting uses the Pokéwalker's original algorithm. Both devices use Bosch -accelerometers, the BMA150 in the Pokéwalker and the BMI270 in the Stick. -Stickwalker converts the readings to the original scale and processes them at -the original cadence, so step tracking should behave much like an original -Pokéwalker. [How step counting works](stick/docs/motion.md) explains the details. +## Development -The original 96×64 screen appears at twice its size on the Stick's display. -You can rotate it and choose a light or dark appearance. The speaker plays the -original tone sequences. Your Pokémon, route and progress stay saved across -restarts. Return your Pokémon to HGSS before updating pre-release firmware; -updates can require resetting game data. +[Build instructions](docs/build.md) · [Contributing](CONTRIBUTING.md) · [Issues](https://github.com/lumirth/stickwalker/issues) -Walking continues while the screen is off. Hold M for half a second to wake it. -With the default controls, L moves left, M confirms, and R moves right. -The screen starts left-side-down. Hold L+R for one second to open Stick Settings. -See [controls and settings](stick/docs/controls.md) -for the other button layouts. - -## Help and contribute - -To report a problem or work on the firmware, start with -[Contributing](CONTRIBUTING.md). The [development guide](stick/README.md) maps -out the code. [Release notes](https://github.com/lumirth/stickwalker/releases) -cover changes and testing for each version; the -[validation history](stick/docs/validation-history.md) records the device trials. - -Stickwalker is an unofficial project by [lumirth](https://github.com/lumirth), -released under [MIT](LICENSE). Dependencies and marked assets have their own -[licenses and notices](docs/third-party.md). The -[references and acknowledgments](docs/references.md) credit the research and -tools used in the reconstruction and port. +By [lumirth](https://github.com/lumirth), under [MIT](LICENSE). [Credits](docs/references.md) and [third-party licenses](docs/third-party.md). diff --git a/assets/README.md b/assets/README.md index c772be7..a509caf 100644 --- a/assets/README.md +++ b/assets/README.md @@ -1,129 +1,19 @@ -# Artwork inputs +# Edit built-in artwork -The H8 build reads six NCG images and a BMP font sheet. Each file comes from -`assets/local/` when present, with original artwork in `assets/placeholders/` -filling any missing entry. The build reports the placeholders it uses. +Copy the images and palette you want to edit from `assets/placeholders/` into `assets/local/`, or [extract them from your own ROM](../docs/build.md#artwork). -Extract the resident artwork and font once: +Build with `--artwork local` to use your edits. -```sh -uv run python -m tools.assets extract /path/to/retail.bin -``` - -Extraction checks the ROM's size and SHA-256, then saves the seven images and an -editor palette in the ignored `assets/local/` directory. Future builds read those -files, so the ROM can be moved or removed. Existing files are protected; use -`--force` to replace the extracted images and palette. Other files in the local -directory are preserved. Keep a copy of edited artwork before extracting again. - -| File | Size | Contents | -| --- | --- | --- | -| `walker.ncg` | 32 × 32 | Device image used by the connection screens | -| `face-neutral.ncg` | 16 × 8 | Waiting expression | -| `face-smile.ncg` | 16 × 8 | Successful connection expression | -| `face-frown.ncg` | 16 × 8 | Failed connection expression | -| `button-arrow.ncg` | 8 × 8 | Button prompt | -| `ir-signal.ncg` | 8 × 8 | Infrared activity indicator | -| `font.bmp` | 108 × 8 | 36 adjacent 3 × 8 glyphs: `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ` | -| `grayscale.ncl` | 16 palette entries | Editor palette; the first four entries show the firmware shades | - -For Stickwalker, the default build always uses placeholders. After extracting -or editing local images, select them with `--artwork local`; missing entries -fall back to placeholders. See the [Stick build guide](../stick/docs/build.md). - -## Editing - -To create custom artwork without a ROM, copy the desired placeholder files and -`grayscale.ncl` into `assets/local/` and edit them there. - -NCG and NCL are Nitro intermediate graphics formats supported by -[NitroPaint](https://github.com/Garhoogin/NitroPaint). Keep four-bit tiles, -palette zero, and pixel indices 0 through 3. Those indices select white, light -gray, dark gray, and black. The supplied NCL palette displays this mapping in -an editor. - -For an ordinary bitmap editor, convert an image to BMP and back: +Open NCG images in [NitroPaint](https://github.com/Garhoogin/NitroPaint), or convert them to BMP for another editor. ```sh -uv run python -m tools.assets convert assets/local/walker.ncg walker.bmp -# Edit walker.bmp, preserving its dimensions and grayscale colors. -uv run python -m tools.assets convert walker.bmp assets/local/walker.ncg --force +uv run -m tools.assets convert assets/local/walker.ncg walker.bmp +# Edit walker.bmp. +uv run -m tools.assets convert walker.bmp assets/local/walker.ncg --force ``` -Use opaque grayscale values **255, 170, 85, and 0**. The converter also accepts -**173 and 82**, the neighboring display values produced by the NCL palette's -five-bit channels. The font uses opaque **255 and 0**. Disable antialiasing while -drawing, and use nearest-neighbor scaling when viewing pixel art. Unsupported -colors produce an error identifying the file and pixel to fix. - -The font sheet can be edited directly as a BMP. Every three consecutive columns -form one glyph, and every row of each cell is preserved. The firmware supplies -the blank separator column when drawing text. - -### Walker pixels and rest timing - -The sound engine's 42 note-period constants are defined in -`src/application/pw_builtin.c`, immediately before the walker image. Rest code -`0x7D` indexes beyond that table into the image and uses packed byte `0x53` as -its duration divisor. That byte is **2** in the retail image. Zero creates a -zero divisor; other values change the duration of rests. - -All 256 walker bytes describe image pixels. Byte `0x53` is the low bit plane of -column **9**, rows **8–15**, in the generated firmware packing. To preserve it, -use light gray or black at **(9, 9)** and white or dark gray at **(9, 8)** and -**(9, 10–15)**. Coordinates start at zero. +Keep the original dimensions and use opaque grayscale values 255, 170, 85 and 0. The converter also accepts 173 and 82. Edit `font.bmp` directly, using only 255 and 0. Turn off antialiasing. -The placeholder includes a light-gray pixel at (9, 9) for this purpose. -`config/artwork.json` records the required byte, and conversion checks it for -both local and placeholder images. An incompatible edit stops conversion with -the filename, packed offset, and expected value. - -## File conversion - -[`config/artwork.json`](../config/artwork.json) records each resource's ROM range, -dimensions, firmware packing, and shared audio-byte requirements. -[`tools/assets.py`](../tools/assets.py) performs -extraction and conversion. BMP handling uses -[Pillow](https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#bmp). - -The sprite files use the `NCCG` signature and a `CHAR` block containing dimensions -in tiles, four-bit color depth, and 8 × 8 tiles. Tiles appear from left to right -and top to bottom; each byte holds the left pixel in its low nibble. The public -[NitroPaint NCG reader](https://github.com/Garhoogin/NitroPaint/blob/be4d73c4843350665e0547590d7449996c6c33e8/NitroPaint/object/NitroCharacter.c#L642) -and [NCL writer](https://github.com/Garhoogin/NitroPaint/blob/be4d73c4843350665e0547590d7449996c6c33e8/NitroPaint/object/NitroPalette.c#L513) -describe these containers. - -Extraction reconstructs the sprite pixels from the ROM. Its NCG writer supplies -fresh palette-zero tile attributes and a link to `grayscale.ncl`. The palette lets an editor display the four firmware shades. The BMP font sheet -arranges the stored glyph bytes into editable cells. - -The generated firmware images consist of eight-row pages, read from top to -bottom. Each page contains columns from left to right. A column stores two bytes: -the high bit plane, then the low bit plane. Bit zero describes the upper pixel. -This is the layout consumed by `DisplayBlit` and `RasterColumn`. - -The font stores one byte per column, with bit zero at the top row and a set bit -for black. `DisplayText` reads the 108 column bytes as 36 adjacent glyphs. All -eight rows survive extraction and conversion, including pixels outside the -usual letter height. - -Conversion produces the aggregate initializer included by -`src/application/pw_builtin.c`. -The font and all six images retain their byte extents and order. Artwork sent -from the console into serial EEPROM is handled by the firmware's communication -path and remains separate from these resident images. - -## Placeholders - -The bundled artwork shows a simple rectangular device, facial expressions, a -button arrow, a radio symbol, and small alphanumeric glyphs. Its drawing source -is [`placeholders/draw.py`](placeholders/draw.py). Regenerate the files with: - -```sh -uv run python -m assets.placeholders.draw -``` +If conversion flags byte `0x53` in `walker.ncg`, keep pixel 9,9 light gray or black, and pixels 9,8 and 9,10 through 9,15 white or dark gray. Coordinates start at zero. -The source and images are [available under CC0](placeholders/LICENSE.md). -Placeholder builds retain the resident image dimensions and glyph cells while -producing a different firmware hash. The firmware continues to obtain its other -artwork through console-provided EEPROM resources. +The bundled placeholders are [CC0](placeholders/LICENSE.md). diff --git a/config/README.md b/config/README.md deleted file mode 100644 index 4515cd4..0000000 --- a/config/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Build configuration - -`artwork.json` is shared by both targets. The remaining files describe the H8 -build and the tools used around it. `configure.py` -reads them to generate `build.ninja`; the build utilities also record their -contents with the produced firmware. - -| File | Purpose | -| --- | --- | -| `build.json` | Shared compiler options, per-module flag overrides, linker sections, and the retail image's size and SHA-256 | -| `artwork.json` | Resident asset names, ROM extraction ranges, image dimensions, packed pixel formats, and bytes shared with audio timing | -| `toolchains.json` | Fingerprints of the qualified 6.02.01 and 6.02.02 suites and known updater packages | -| `host-tools.json` | Managed Wibo releases, download locations, and integrity hashes | -| `h8.xml` | Cppcheck's target widths and alignment for the H8 C implementation | -| `lint-suppressions.txt` | Two narrow Cppcheck exceptions for preserved target arithmetic | - -Compiler fingerprints describe tested inputs. Other complete installations can -be imported and built with a warning. Each build records the actual compiler -files selected on that machine. Downloaded Wibo executables must pass their -recorded integrity hash before use. - -`tools/modules.py` discovers C files recursively under `src/` and sorts their -unique lowercase basenames. Configuration, compilation, linking, and receipt -validation use the resulting module list. Directory paths serve navigation; -basename order determines object order. - -`build.json` supplies extra compiler options through `source_flags`, keyed by -the full lowercase basename including `.c`: - -```json -{ - "source_flags": { - "h8_rominfo.c": ["-section=const=M"], - "pw_fourier.c": ["-speed=shift,loop=2"], - "pw_pedometer.c": ["-speed"] - } -} -``` - -Other modules use the common compiler options. An override must name an -existing source. Project `#include` directives name headers relative to -`include/`, such as `application/pw_pedometer.h` or `support/lib_common.h`. - -Source order, integer widths, compiler flags, and linker placement can affect -retail identity. After changing them, build under both qualified suites and run -`uv run ninja verify` for each. The [build guide](../docs/build.md) explains the -commands, and the [artwork guide](../assets/README.md) explains the image formats. diff --git a/docs/adr/0001-preserve-native-motion-ownership.md b/docs/adr/0001-preserve-native-motion-ownership.md deleted file mode 100644 index beb131c..0000000 --- a/docs/adr/0001-preserve-native-motion-ownership.md +++ /dev/null @@ -1,14 +0,0 @@ -# Process motion samples on the original schedule - -Stickwalker processes each acceleration sample as it arrives, using the -Pokéwalker's inactive and walking schedules and its 64-sample FFT window. -Sound and infrared pause sampling while they use the shared working memory. -Menus and games continue sampling. - -This preserves the order in which the original program receives movement and -input. Buffering samples for later processing would also require replaying -button events and task changes in that order. The port therefore keeps -immediate processing. - -See the [screen-off wake policy](0002-m-only-dark-wake.md) for how buttons affect -this schedule. diff --git a/docs/adr/0002-m-only-dark-wake.md b/docs/adr/0002-m-only-dark-wake.md deleted file mode 100644 index 901a46d..0000000 --- a/docs/adr/0002-m-only-dark-wake.md +++ /dev/null @@ -1,35 +0,0 @@ -# Use M to wake the screen - -Holding physical M through native Center qualification wakes the Stick in every -layout and rotation, roughly half a second. The native firmware also retains -its social-offer opportunity; the adapter no longer starts interactive mode -from a separate 500 ms wall-clock threshold. -L and R resume their configured roles after wake. Releasing M early cancels -panel preparation. A held L or R still allows M to wake the display. - -While the screen is off, EXT1 watches M and the next game deadline sets the -timer wake. L polling stops. After wake, the adapter clears L's retained event -and waits for all buttons to be released. A successful PMIC read is required -to establish that L has been released. - -When the game or Settings is visible, the front buttons can wake the CPU and -required button polls supply a 5 ms deadline. Walking and interactive game work -retain their native timing instead of accelerating to consume queued input. - -## GPIO restoration - -EXT1's incremental API appends pins. Clear its old mask before switching -between M and M/R. After every sleep attempt, return both pads to digital GPIO, -including when the sleep controller rejects entry. Configuration failures -keep the CPU awake until a scheduled retry. - -## Checks - -Production-firmware simulation covers timeout, canceled and qualified M holds, -held-input suppression, RTC timer/EXT1 wake and digital GPIO restoration. The -[device test records](../../stick/docs/history/power-and-wake.md) record the -button, panel and boot trials. USB keeps the CPU awake during bench commands; -test physical sleep and wake on battery. - -The [ESP-IDF sleep API](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-reference/system/sleep_modes.html) -describes the wake-source and GPIO functions. diff --git a/docs/build.md b/docs/build.md index ed1d73d..4e59bb6 100644 --- a/docs/build.md +++ b/docs/build.md @@ -1,246 +1,57 @@ -# Building the H8 reconstruction +# Build Stickwalker -For M5StickS3, use the [Stickwalker build guide](../stick/docs/build.md). - -Compiler import and artwork extraction are separate, one-time setup operations. -Compilation converts the extracted files and runs the imported tools. Its final -check uses the retail image's recorded size and SHA-256, so it works without a -ROM file. - -## Host setup - -The supported hosts are macOS Arm64 using Wibo/Rosetta and Windows x86_64 -running the compiler directly. - -Install [uv](https://docs.astral.sh/uv/getting-started/installation/) through your -preferred package manager. For example, macOS with Homebrew: +Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and [Arduino CLI](https://arduino.github.io/arduino-cli/latest/installation/). Run these commands from the repository root. ```sh -brew install uv -softwareupdate --install-rosetta +git clone https://github.com/lumirth/stickwalker.git +cd stickwalker +uv sync --locked +arduino-cli config add board_manager.additional_urls https://static-cdn.m5stack.com/resource/arduino/package_m5stack_index.json +arduino-cli core update-index +arduino-cli core install m5stack:esp32@3.3.9 +arduino-cli lib install 'M5GFX@0.2.29' +arduino-cli lib install --no-deps 'M5Unified@0.2.21' +uv run python stick/build_port.py ``` -On Windows with WinGet: - -```powershell -winget install --id=astral-sh.uv -e -``` - -Run the remaining commands from the repository root. `uv run` prepares Python -and the dependencies recorded in `uv.lock`. The default Python series is 3.12; -the tools support Python 3.11 or newer. Ninja schedules compilation and Pillow -reads and writes BMP images. The C compiler remains user-provided. - -On macOS, configuration downloads the Wibo version recorded in -`config/host-tools.json` and validates it before execution. To use an existing -launcher, pass `--wibo PATH` to `configure.py` or set `PW_WIBO`. To return to the -managed launcher, use `--managed-wibo`. Windows uses native execution. +The firmware is in `stick/.build/output/`. Follow [installation](../stick/docs/install.md) to flash it. -Compiler processes run in separate scratch directories with short ASCII paths. -A checkout can contain spaces. If the system temporary directory is unsuitable, -pass `--work-dir PATH` naming an existing writable ASCII directory. +## Artwork -## Import a compiler +Extract the built-in graphics from your own 49,152-byte Pokéwalker ROM, then select them when building. ```sh -uv run -m tools.toolchain import path/to/compiler +uv run -m tools.assets extract /path/to/pokewalker.bin +uv run python stick/build_port.py --artwork local ``` -The input can be a suite directory, its `bin` directory, `ch38.exe`, an enclosing -HEW installation containing one suite, an H8 updater executable, or a ZIP -containing one H8 updater. The importer reads archives directly and keeps the -compiler phases, runtime packs, and headers together in `.local/toolchains/`. -The original installation or installer is needed only during import. +The supported ROM has SHA-256 `f9e210a3b74afbbd12c5a66a51cc05cb9fbac986805ff0a3bfb4be6074d15607`. -These suites reproduce the retail image: +For custom graphics, see [editing artwork](../assets/README.md). -| Suite | CH38 | OPTLNK | Known package | -| --- | --- | --- | --- | -| 6.02.01 | 6.02.01 | 9.04.01 | `h8v6201u.exe` | -| 6.02.02 | 6.02.02 | 9.05.00 | `h8v6202u.exe` or `h8v6202u-doc-e.zip` | +## Checks -The `h8v6202u-doc-e.zip` archive contains the updater and can be imported directly. -Importing an updater supplies a complete compiler suite without running the -installer or having an existing HEW installation. - -`config/toolchains.json` records known package and installed-file hashes. -Unfamiliar fingerprints warn and remain usable. Missing necessary compiler -components or malformed archives stop import with a diagnostic. The matching -requirement applies to the produced image when strict verification is requested. - -Use `--name LABEL` to give an import a distinct local name, especially when -trying another bundle of the same compiler revision: +Install Rust 1.98.1 and Tesseract. On macOS, install Tesseract with `brew install tesseract`. On Debian or Ubuntu, use `sudo apt-get install tesseract-ocr`. Then run the firmware workflows. ```sh -uv run -m tools.toolchain import path/to/other/bundle --name experiment -uv run -m tools.toolchain list -uv run configure.py --toolchain experiment +uv run python stick/check.py ``` -Reimporting a name replaces that local installation after the new input has been -parsed and checked. A failed replacement restores the prior installation; if -restoration also fails, the error identifies the retained recovery directory. - -## Extract artwork +For formatting and static analysis, install Cppcheck 2.21.0 and run: ```sh -uv run -m tools.assets extract path/to/retail.bin -``` - -The supported image is **49,152 bytes**, with SHA-256: - -```text -f9e210a3b74afbbd12c5a66a51cc05cb9fbac986805ff0a3bfb4be6074d15607 +uv run --group dev -m tools.check ``` -Extraction checks this identity because the resource offsets describe that -image. It saves six NCG sprite files, a BMP font, and an NCL editor palette in -`assets/local/`. Existing files require `--force` before replacement. See the -[artwork guide](../assets/README.md) for formats, conversion and editing. +## Build the original H8 target -You can build before extraction: any missing image uses its original CC0 -placeholder. An existing image with invalid dimensions or colors produces an -error identifying what needs correction. Placeholder use appears in the build -report and normally changes the output hash. - -The editable containers preserve the recovered pixels and their packing. The -NCG writer supplies fresh editor metadata; the BMP font sheet is a reconstructed -representation of the glyphs. - -## Compile and check - -```sh -uv run configure.py -uv run ninja -``` - -Configuration selects the previous compiler, or the only available import. With -multiple imports, select one explicitly: +Use Renesas H8 compiler suite 6.02.01 or 6.02.02. Windows x86_64 runs it directly. On macOS Apple Silicon, install Rosetta; configuration supplies Wibo. ```sh +uv run -m tools.toolchain import /path/to/compiler-or-updater uv run configure.py --toolchain 6.02.01 uv run ninja -``` - -Outputs are written to `build//`: - -| Output | Contents | -| --- | --- | -| `pw.bin`, `pw.map` | Firmware image and linker map | -| `rom_assets.h` | Converted resident artwork arrays | -| `pw_pedometer.obj`, `pw_pedometer.log`, `pw_pedometer.json` | A module's object, compiler output, and input/output receipt; each C module has its own set | -| `runtime.log`, `link.log` | Runtime-library and linker output | -| `artwork.json`, `runtime.json`, `link.json`, `build.json` | Actual source, artwork, compiler, recipe and output hashes | -| `status.json` | Image size, SHA-256, retail-match result and placeholder use | - -The object name comes from the C file's basename: for example, -`src/application/pw_pedometer.c` produces `build//pw_pedometer.obj`. -The build discovers sources recursively and links their objects in alphabetical -basename order. Names must be unique and lowercase across the source tree. -Directory moves preserve that ordering; filename changes can affect the image. -See the [source guide](source.md#modules-and-reading-paths) for the module families. - -A different output hash is a warning. A completed modified build returns success -and leaves `pw.bin` available. Compiler errors, invalid assets and inconsistent -build artifacts return failure with an actionable message. - -For release verification, use: - -```sh uv run ninja verify ``` -This requires the exact retail size and SHA-256 and writes `verified.json` on -success. Run it under **both 6.02.01 and 6.02.02** for accepted matching-source -changes. The build records also check that the image came from the current -source, assets and compiler files. They catch stale objects even when timestamps -have been preserved. Firmware E2E checks and formatting complement this check. - -## Clean, recover and work offline - -Remove generated outputs for the selected compiler: - -```sh -uv run ninja -t clean -uv run ninja -``` - -The clean command keeps `assets/local/`, imported compilers, and cached Wibo. -To clear another compiler's outputs, configure that compiler and run the same -clean command. `ninja -t clean` removes the outputs declared in the graph; -extra files saved manually in `build/` remain there. - -Ninja rebuilds a changed C file individually; a header change rebuilds all -firmware units. Discovery includes nested source and header directories, and -source additions or removals regenerate the graph. Asset edits rebuild -conversion, `pw_builtin.c`, and the linked image. Adding or removing a local asset, -or restoring the local artwork directory, automatically updates its fallback -selection. Changing imported compiler files refreshes their recorded inventory -before the next build. A compiler whose required files have been removed needs -to be reimported. - -After moving the checkout, rerun `uv run configure.py` to select the local -launcher and scratch paths. If a build reports obsolete or replaced artifacts, -refresh the graph before cleaning and rebuilding: - -```sh -uv run configure.py -uv run ninja -t clean -uv run ninja -``` - -Compilation errors include the failing tool's log path. Fix the reported input -and repeat the build. - -Once dependencies and Wibo are cached, the setup and build can run offline: - -```sh -uv run --offline configure.py --offline -uv run --offline ninja -``` - -For an offline machine, prepare its Python environment and required host tools -in advance. The compiler and artwork import commands operate on local files. - -## Development checks - -The `dev` dependency group supplies clang-format 21.1.8 and Ruff. Install -Cppcheck 2.21.0 separately, then run: - -```sh -uv run --group dev -m tools.check -``` - -This checks C formatting, Python formatting and lint, and Cppcheck with the H8 -target model. Run `uv run python stick/check.py` separately for the production -firmware workflows. To apply formatting: - -```sh -uv run --group dev -m tools.check --format -``` - -Formatting includes nested source and header directories. The accepted HEW -device header at `include/startup/iodefine.h` keeps its original formatting. -Cppcheck has two narrow suppressions for arithmetic whose evaluation follows -the target's 16-bit rules. - -`uv` is a convenience for a reproducible host environment. An existing Python -environment with Ninja and Pillow can also run `python configure.py` and `ninja`; -the dependency versions are recorded in `uv.lock`. - -## Why generate Ninja? - -Ninja represents each build output and dependency explicitly. `tools/modules.py` -resolves the recursively discovered sources into one ordered module list used -by configuration, compilation, linking, and receipt validation. -`config/build.json` holds common options, per-module flag overrides, and section -placement. Common dependency groups keep repeated paths out of each -compiler command. See [Ninja's design](https://ninja-build.org/manual.html#_philosophical_overview). - -Separate extraction and compilation are established decompilation workflows; -[SM64](https://github.com/n64decomp/sm64/blob/master/extract_assets.py) retains -extracted assets and [OOT](https://github.com/zeldaret/oot#4-setup-the-rom-and-build-process) -provides a separate asset-setup step. This project combines that workflow with -[uv's managed project environment](https://docs.astral.sh/uv/guides/projects/). -Ninja already supplies incremental builds and cleaning, so those tasks need no -additional task runner. +Extract the original artwork first to reproduce the reference ROM. Repeat with `--toolchain 6.02.02` when changing shared application code. diff --git a/docs/h8-reconstruction.md b/docs/h8-reconstruction.md deleted file mode 100644 index 126f715..0000000 --- a/docs/h8-reconstruction.md +++ /dev/null @@ -1,35 +0,0 @@ -# The H8 firmware reconstruction - -The application in `src/` and `include/` reconstructs the Pokéwalker's firmware. -With retail artwork and a supported Renesas compiler suite, it builds a binary -identical to the reference image with SHA-256: - -```text -f9e210a3b74afbbd12c5a66a51cc05cb9fbac986805ff0a3bfb4be6074d15607 -``` - -## Build the original target - -Provide a Renesas H8/H8S/H8SX compiler installation. Suites 6.02.01 and 6.02.02 -are supported. The compiler's 32-bit Windows executables run natively on Windows -x86_64 and through Wibo/Rosetta on macOS arm64. - -Follow the [H8 build guide](build.md) to import the compiler, configure and -build. Placeholder artwork is sufficient for studying or modifying the program. -To reproduce the reference hash, [extract the resident artwork](../assets/README.md) -from your own firmware image and run `uv run ninja verify` under each supported -suite. - -For an M5StickS3 build, use the [Stickwalker build guide](../stick/docs/build.md). -That build uses the M5Stack Arduino toolchain. - -## Read and contribute - -The [source guide](source.md) follows boot, scheduling, motion, storage and -infrared across modules. File prefixes preserve the observed link order; -renaming a file can alter binary placement. [Build configuration](../config/README.md) -describes the associated flags and sections. - -Follow [Contributing](../CONTRIBUTING.md) when changing either target. See the -[references and acknowledgments](references.md) for research sources and -community projects. diff --git a/docs/references.md b/docs/references.md index 269e6c4..82f54bd 100644 --- a/docs/references.md +++ b/docs/references.md @@ -1,280 +1,13 @@ -# References +# Credits -This document catalogs the sources consulted for this project and related -community projects. +Stickwalker builds on [lumirth/pw](https://github.com/lumirth/pw), my matching reconstruction of the Pokéwalker firmware. -Source-code links use the consulted revision where it is recorded. License notices for third-party tools appear in the [third-party acknowledgments](third-party.md). +UnrealPowerz's [pw_firm](https://github.com/UnrealPowerz/pw_firm) inspired the reconstruction. h4lfheart's [PocketWalker](https://github.com/h4lfheart/PocketWalker) inspired the initial Pokéwalker work. -## Pokéwalker research and game behavior +Dmitry Grinberg's [Pokéwalker research](https://dmitry.gr/?r=05.Projects&proj=28.%20pokewalker), Martin Korth's [GBATEK](https://problemkaputt.de/gbatek.htm#dscartinfraredpwalkerircommands), [PRET's HGSS reconstruction](https://github.com/pret/pokeheartgold) and mamba2410's [reverse-pokewalker](https://github.com/mamba2410/reverse-pokewalker) supplied research and references. -- Dmitry Grinberg's investigation of the hardware, firmware, infrared protocol, - EEPROM contents, and device workflows. The accompanying annotated firmware - disassembly and DECODEIMG utility helped interpret instructions and packed - artwork. - [PokéWalker hacking](https://dmitry.gr/?r=05.Projects&proj=28.%20pokewalker). -- Martin Korth's technical reference covering Pokéwalker commands, memory - organization, peripherals, and cartridge communication. - [GBATEK: Pokéwalker IR commands](https://problemkaputt.de/gbatek.htm#dscartinfraredpwalkerircommands). -- The memory-map tables used to investigate persistent records, EEPROM regions, - resource boundaries, and their RAM counterparts. - [GBATEK: Pokéwalker memory map](https://problemkaputt.de/gbatek.htm#dscartinfraredpwalkermemorymap). -- The register tables used to identify hardware accesses, including the RTC - time registers. - [GBATEK: H8/386 special-function registers](https://problemkaputt.de/gbatek.htm#h8386sfrs). -- nocash's firsthand account of reconstructing command tables and correcting - packet lengths and memory-map interpretations, consulted during the - persistent-record and protocol investigations. - [NESdev Pokéwalker discussion](https://forums.nesdev.org/viewtopic.php?start=30&t=21140). -- The public reconstruction of HeartGold and SoulSilver, used to understand - Pokémon data, saved records, serialization, and the console endpoint. - [PRET pokeheartgold, consulted revision](https://github.com/pret/pokeheartgold/tree/0985e8718df4f25e64d6507d89c0c97c0d288981). -- The principal HGSS communication application, used to follow complete - Pokéwalker transactions and their effects on game state. - [PRET's Pokéwalker overlay](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/asm/overlay_112.s). -- Community reverse engineering covering infrared communication, captures, - compression, and stored data. - [mamba2410/reverse-pokewalker](https://github.com/mamba2410/reverse-pokewalker). -- Tools and experiments connecting HGSS under DeSmuME to a software Walker, - together with EEPROM and artwork inspection utilities. - [Pusty/PokewalkerUtils](https://github.com/Pusty/PokewalkerUtils). -- A public implementation useful for understanding how course data is - assembled and selected for transfer. - [Lincoln-LM's Pokéwalker course injector](https://github.com/lincoln-lm/pokewalker-course-injector/blob/43dc0617ea34bd4ac14e20ea0e64109a013d7f1b/main.py). +Thanks to the [Pokéwalker Hacking community](https://discord.gg/ymbTMsS), including mamba2410, zenithknight, mriancamp and porocyon. -### PRET reading list +The port uses [M5Stack's Stick S3 hardware and libraries](https://docs.m5stack.com/en/core/StickS3). Firmware tests use [esp32sim](https://github.com/mikroverk/esp32sim), by Joakim Eriksson and Alice. -These paths were consulted alongside the communication overlay. Each link -uses revision `0985e8718df4f25e64d6507d89c0c97c0d288981`. - -- Pokémon creation, field access, and gender and shininess calculations used - to interpret transferred Pokémon records. - [Pokémon operations](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/pokemon.c). -- Saved Trainer House records and the operations that manage them, used when - following exchanged trainer and party data. - [Trainer House storage](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/save_trainer_house.c). -- Trainer-profile initialization and accessors, used to follow identity - values into communication records. - [Player data](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/player_data.c). -- Pokémon and course gift processing, used to investigate distribution data - and course unlocks. - [Mystery Gift operations](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/scrcmd_mystery_gift.c). -- Inventory insertion and its success or failure result, used to establish - the effect of return-trip item awards. - [Bag operations](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/bag.c). -- Encoded-string copying and termination, used to follow names between - communication records and game string objects. - [String operations](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/pm_string.c). -- String buffering for messages, used to trace displayed text back to the - values supplied by communication paths. - [Message formatting](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/message_format.c). - -## Hardware - -- The microcontroller family's registers, memory organization, interrupts, - timers, ADC, and serial peripherals. - [Renesas H8/38602R Group Hardware Manual](https://www.renesas.com/en/document/mah/h838602r-group-hardware-manual). -- Device-specific additions needed when applying the family manual to the - H8/38606. - [Renesas H8/38606 addition note](https://www.renesas.com/en/document/tcu/addition-h838606-group). -- Instruction semantics, addressing modes, arithmetic, and processor flags - used when interpreting the executable. - [Renesas H8/300H Series Software Manual](https://www.renesas.com/en/document/mah/h8300h-series-software-manual). -- The older instruction-set reference retained for decoding H8 instructions - and checking operand semantics: Hitachi document ADE-602-053A. - [H8/300H Series Programming Manual, hosted by ManualsLib](https://www.manualslib.com/manual/1585252/Hitachi-H8-300h-Series.html). -- The accelerometer's sample representation, registers, measurement settings, - and operating modes. The consulted edition is BST-BMA150-DS000-06, - version 1.6, October 30, 2008. - [Bosch BMA150 datasheet, hosted by DigiKey](https://media.digikey.com/pdf/Data%20Sheets/Bosch/BMA150.pdf). -- The serial EEPROM's commands, addressing, page programming, and timing - constraints. The consulted family datasheet covers M95512-W, M95512-R, - and M95512-DF: DS4192, Rev.24, September 2021. - [ST M95512-W/-R/-DF datasheet](https://www.st.com/resource/en/datasheet/m95512-w.pdf). -- LCD commands, page and column addressing, display RAM, and initial-line - selection. The consulted edition is V1.0, July 1, 2008. Dmitry's article - records the August 2026 NT7508 identification and credits Lukas Unguraitis's - research. - [Novatek NT7508 datasheet, hosted by Orient Display](https://www.orientdisplay.com/wp-content/uploads/2022/08/NT7508_V1.0.pdf). -- The earlier LCD comparison reference for four-level grayscale display - memory and command encodings: Rev.1.0, August 2002. The subsequent LCD - investigation identified NT7508 as the better match for the firmware's - command sequence. - [Solomon Systech SSD1854 datasheet mirror](https://datasheet4u.com/pdf-down/S/S/D/SSD1854-SolomonSystech.pdf). - -## Compiler documentation and C behavior - -- C representation, calling conventions, compiler options, assembly, - libraries, and linking. The retained manual is Compiler Package Ver.6.01, - REJ10B0161-0100, Rev.1.00, January 12, 2005. - [Renesas H8S/H8/300 compiler-package manual](https://www.renesas.com/en/document/mat/h8s-h8300-series-cc-compiler-assembler-optimizing-linkage-editor-compiler-package-v601-users-manual). -- HEW project and build behavior. Section 3.10 documents the default - alphabetical object-link order used as context for this project's module - naming and organization. - [HEW V4.02 User's Manual](https://www.renesas.com/en/document/mat/high-performance-embedded-workshop-v402-users-manual). -- Compiler behavior, extensions, options, and examples relevant to the - historical H8 toolchain. - [Renesas H8S/H8/300 compiler application note](https://www.renesas.com/en/document/apn/h8s-h8300-series-cc-compiler-package-application-note). -- Background on structure padding, alignment, addressing, and optimization, - consulted during the storage-reconstruction work: AN0403008, Rev.1.00, - March 2004. - [Renesas Embedded C Programming III — Optimization](https://www.renesas.com/en/document/apn/embedded-programming-iii-ecprogramiiiopt). -- C committee discussion of C90 union-member interpretation, consulted when - reviewing the resident-resource byte view. - [WG14 N847](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n847.htm). -- Configuring target integer widths for static analysis, used to establish - the project's H8 platform configuration. - [Cppcheck platform documentation](https://cppcheck.sourceforge.io/manual.html#platform). -- The relationship between compiler-package releases and their constituent - compiler, assembler, linker, and library tools. - [Renesas software component list](https://www.renesas.com/en/document/oth/cc-compiler-package-h8sx-h8s-h8-family-software-component-list). -- Official release information for the first supported compiler suite. - [V6.02 Release 01 announcement](https://www.renesas.com/en/document/tnr/cc-compiler-package-h8sx-h8s-and-h8-mcu-families-revised-v602-release-01). -- Official release information for the second supported compiler suite. - [V6.02 Release 02 announcement](https://www.renesas.com/en/document/tnr/cc-compiler-package-h8sx-h8s-and-h8-mcu-families-revised-v602-release-02). - -## Reconstruction methods and tools - -- The H8 instruction decoder used during reconstruction. The retained tool - is GNU Binutils 2.16.1's `h8300-hms-objdump`. - [GNU Binutils 2.16.1 release](https://ftp.gnu.org/gnu/binutils/binutils-2.16.1.tar.bz2). -- Background reading on inferring types from machine code, consulted during - the storage-context investigation: Noonan, Loginov and Cok, 2016. - [Polymorphic Type Inference for Machine Code](https://arxiv.org/abs/1603.05495v2). - -## Artwork and setup tools - -- A public implementation of the NCG container and tile representation used - by the extracted sprite files. - [NitroPaint's NCG reader](https://github.com/Garhoogin/NitroPaint/blob/be4d73c4843350665e0547590d7449996c6c33e8/NitroPaint/object/NitroCharacter.c#L642). -- A public implementation of the NCL palette container used for editing - those sprites. - [NitroPaint's NCL writer](https://github.com/Garhoogin/NitroPaint/blob/be4d73c4843350665e0547590d7449996c6c33e8/NitroPaint/object/NitroPalette.c#L513). -- Another public reader documenting the NCCG container through its - implementation. - [Tinke's NCCG reader](https://github.com/pleonex/tinke/blob/3263fc2baeea40c847e5267f826730210864fb8c/Plugins/Images/Images/NCCG.cs). -- The image library used for BMP artwork and font-sheet handling. - [Pillow's BMP documentation](https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#bmp). -- InstallShield extraction work that informed the project's installer parser - and decoding approach. - [ISx](https://github.com/lifenjoiner/ISx). -- Public InstallShield cabinet extraction code that also informed - compiler-package extraction. - [Unshield](https://github.com/twogood/unshield). -- Installer behavior and extraction options consulted while evaluating - compiler-input handling. - [Revenera's InstallShield command-line reference](https://docs.revenera.com/installshield/helplibrary/IHelpSetup_EXECmdLine.htm). -- The compatibility tool used to execute historical Windows compiler - programs on supported Unix hosts. - [Wibo](https://github.com/decompals/wibo). - -## Build design - -These projects and manuals informed contributor setup and build tooling. - -- A decompilation project template consulted for Python configuration, - Ninja integration, tool provisioning, and executable overrides. - [DTK template, consulted revision](https://github.com/encounter/dtk-template/tree/95a941f755919ebe50c1725a4ce73524470e7a02). -- A working example of user-supplied original inputs, generated build - configuration, and managed compiler wrappers. - [The Wind Waker decompilation](https://github.com/zeldaret/tww/tree/ff768cc1d9ed10e884e006773a5f9ac6a8d6eff4). -- An additional comparison for contributor setup, historical compiler - execution, and development environments. - [Super Smash Bros. Melee decompilation](https://github.com/doldecomp/melee/tree/35acdca612f5dae409a37cb481fd3230cd42d560). -- A comparison for manually installed compiler wrappers and ordinary - host prerequisites. - [New Super Mario Bros. Wii decompilation](https://github.com/NSMBW-Community/NSMBW-Decomp/tree/5e9c76d50573d5659ba70bb174553e5d2ec3c708). -- A concrete example of extracting reusable artwork inputs before - compilation, consulted when designing the separate artwork-extraction step. - [Super Mario 64 asset extractor](https://github.com/n64decomp/sm64/blob/master/extract_assets.py). -- Another example of a separate asset-setup stage before normal builds. - [Ocarina of Time setup documentation](https://github.com/zeldaret/oot#4-setup-the-rom-and-build-process). -- The reasoning behind generated build graphs and explicit dependencies. - [Ninja's design documentation](https://ninja-build.org/manual.html#_philosophical_overview). -- Managed Python environments and dependency locking used by the project. - [uv project documentation](https://docs.astral.sh/uv/guides/projects/). - -## Historical development context - -These references informed the reconstruction's coding conventions. - -- Contemporary Japanese embedded-development guidance consulted when - choosing consistent naming, type, and coding policies. - [IPA/SEC embedded coding guide](https://www.ipa.go.jp/archive/publish/qv6pgp0000000x2q-att/000005106.pdf). -- A contemporary Renesas firmware example providing context for period - C conventions. - [Renesas user-boot application note](https://www.renesas.com/en/document/apn/example-using-user-boot-mode-renesas-018um-flash-devices-xmodem-data-transfer). -- Another contemporary H8 firmware example consulted during the style - investigation. This example uses the IAR compiler and project generator. - [Renesas H8/38024F peripheral examples](https://www.renesas.com/en/document/apn/example-code-h838024f-peripherals). -- Nintendo's public account of the Pokéwalker's development and intended - experience. - [Iwata Asks: HeartGold and SoulSilver, section 5](https://www.nintendo.co.jp/ds/interview/ipkj/vol1/index5.html). - -## Related projects - -Community projects for Pokéwalker emulation, hardware recreation, artwork, -and data inspection. - -- A community implementation connecting infrared communication, EEPROM - records, audio, and application behavior. - [PicoWalker core](https://github.com/mamba2410/picowalker-core). -- mamba2410's recreation of the Pokéwalker using the Raspberry Pi RP2350 - microcontroller. This repository supplies the device drivers and board - integration for the application core. - [PicoWalker](https://github.com/mamba2410/picowalker). -- PCB designs for the PicoWalker recreation, including RP2350 hardware and - a Raspberry Pi Pico 2 carrier board. - [PicoWalker hardware](https://github.com/mamba2410/picowalker-hardware). -- jpcerrone's experimental Windows emulator for exploring the Pokéwalker's - menus and minigames with ROM and EEPROM images. - [PokeStroller](https://github.com/jpcerrone/pokestroller). -- UnrealPowerz' prototype Pokéwalker emulator, including CPU, EEPROM, LCD, - and button emulation. - [Powar](https://github.com/UnrealPowerz/powar). -- UnrealPowerz' browser application for viewing and editing data and artwork - in Pokéwalker EEPROM images. - [Pokéwalker EEPROM Editor](https://unrealpowerz.github.io/pokewalker-eeprom-editor/). - Its implementation provides readable views of EEPROM contents for format - exploration. - [EEPROM editor source](https://github.com/UnrealPowerz/pokewalker-eeprom-editor). -- PoroCYon's project for dumping Pokéwalker ROM and EEPROM contents. - [Pokéwalker ROM dumper](https://gitlab.ulyssis.org/pcy/pokewalker-rom-dumper). -- mamba2410's image viewer and converter, which displays packed Pokéwalker - images in a terminal and converts grayscale BMP artwork to the device's - image format. - [pw-lcd](https://github.com/mamba2410/pw-lcd). -- An interoperability project illustrating how HGSS's Pokéwalker protocol - can operate through another infrared transport. - [RtcPwalker](https://github.com/francesco265/RtcPwalker). - -## Acknowledgments - -UnrealPowerz's [pw_firm](https://github.com/UnrealPowerz/pw_firm/tree/main/) -inspired the matching reconstruction. h4lfheart's -[PocketWalker](https://github.com/h4lfheart/PocketWalker) inspired the initial -Pokéwalker work. Dmitry Grinberg's hardware and firmware research laid much of -the groundwork. - -The [Pokéwalker Hacking community](https://discord.gg/ymbTMsS), including -mamba2410, zenithknight, mriancamp, porocyon and its other contributors, provided -research, tools and a place to compare findings. The projects and authors linked -above also contributed to the reconstruction's technical foundations. - -## Stick hardware and runtime - -The port uses M5Unified 0.2.21, M5GFX 0.2.29 and ESP-IDF 5.5 interfaces from -the pinned M5Stack board core. These references describe their hardware and APIs. - -| Subject | Primary sources | -| --- | --- | -| Board wiring, supplies and battery | [M5StickS3 specification](https://docs.m5stack.com/en/core/StickS3), [schematic](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/K150_Stick_S3_PRJ_V0.6_20251111_2025_11_17_16_10_24.pdf), [M5PM1 power configuration](https://docs.m5stack.com/en/arduino/m5sticks3/m5pm1) | -| PMIC register and button behavior | [M5PM1 V1.9 datasheet](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/M5PM1_Datasheet_EN.pdf), [M5Unified PMIC driver](https://github.com/m5stack/M5Unified/blob/0.2.21/src/utility/power/M5PM1_Class.cpp) | -| Motion sensor | [BMI270 datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi270-ds000.pdf) | -| Display and buffer interpretation | [StickS3 display specification](https://docs.m5stack.com/en/accessory/display/Display_1.14_For_StickS3), [ST7789P3 datasheet](https://files.waveshare.com/wiki/ESP32-S3-GEEK/ST7789P3.pdf), [M5GFX ST7789 driver](https://github.com/m5stack/M5GFX/blob/0.2.29/src/lgfx/v1/panel/Panel_ST7789.hpp) | -| Codec startup and word format | [ES8311 datasheet](https://www.lcdwiki.com/res/PublicFile/ES8311_DS.pdf), [Espressif codec initialization](https://github.com/espressif/esp-adf/blob/release/v2.x/components/esp_codec_dev/device/es8311/es8311.c) | -| Sleep and RTC GPIO ownership | [ESP-IDF sleep modes](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-reference/system/sleep_modes.html), [sleep errors](https://github.com/espressif/esp-idf/blob/v5.5/components/esp_hw_support/include/esp_sleep.h) | -| USB transport during sleep | [ESP-IDF USB Serial/JTAG console](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/usb-serial-jtag-console.html) | -| Debugger effects and failures | [JTAG setup](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/jtag-debugging/index.html), [breakpoints and watchpoints](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/jtag-debugging/tips-and-quirks.html), [watchdogs](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-reference/system/wdts.html), [panic handling](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/fatal-errors.html) | -| Crash records | [Reset reasons](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-reference/system/misc_system_api.html#reset-reason), [retention attributes](https://github.com/espressif/esp-idf/blob/v5.5/components/esp_common/include/esp_attr.h), [core dumps](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/core_dump.html) | -| Failed PMIC transactions | [ESP-IDF I2C probe guidance](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-reference/peripherals/i2c.html#i2c-master-probe), [M5Unified bus wrapper](https://github.com/m5stack/M5Unified/blob/0.2.21/src/utility/I2C_Class.cpp), [M5GFX backend](https://github.com/m5stack/M5GFX/blob/0.2.29/src/lgfx/v1/platforms/esp32/common.cpp) | -| Current measurement | [Espressif measurement guidance](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/current-consumption-measurement-modules.html) | +[Third-party licenses](third-party.md). diff --git a/docs/source.md b/docs/source.md deleted file mode 100644 index 23fb47b..0000000 --- a/docs/source.md +++ /dev/null @@ -1,229 +0,0 @@ -# Reading the application source - -The original target runs on the H8/38606 in normal mode: 8-bit bytes, 16-bit integers -and pointers, and 32-bit longs. It uses C90 with CH38 extensions for interrupts, -sections, bitfield order and machine instructions. - -## Choose the target - -For Stickwalker adapter changes, start with the [Stick development guide](../stick/README.md). -The application paths below are shared, but compiler widths, register access -and startup are target-specific. `PW_STICK_S3` selects the port's hardware and compiler adaptations; C++ adapters use the platform ABI. Preserve explicit widths and byte order -in serialized records rather than relying on host structure layout. - -H8 link-order and retail-matching requirements below apply to the original -target. A shared application change needs both matching checks and Stick host -regressions; an adapter-only change needs the Stick checks and relevant hardware -qualification. - -## Modules and reading paths - -The source retains 40 separately compiled C files. Four `h8_` modules in -`src/startup/` initialize sections, enter interrupts, reset the device, and -describe the ROM. `src/support/` holds `ir.c` and the two `lib_` helper modules. -The 32 `pw_` modules in `src/application/` cover activities, storage, and device -interfaces. `src/globals.c` defines shared storage. - -Module headers generally follow the source directories and basenames under -`include/`. Shared types, records, resources, and view state remain at the -include root. -Project includes use paths from that root, such as -`#include "application/pw_pedometer.h"` and `#include "support/lib_common.h"`. -The shared `application/pw_eeprom_m95512.h` interface covers the serial bus and -single-byte programming in `pw_eeprom_m95512_bus.c`, plus bulk transfers and -mirrored-record handling in `pw_eeprom_m95512_io.c`. Its status-receipt helpers -are implemented in `lib_common.c`. That module also groups raster operations, -artwork loading, scratch allocation, step pacing and small runtime helpers. -`InstallTask` lives in `pw_home.c`; communication completion and staged-walk -activation are declared in `h8_resetprg.h`. - -Each source must have a unique lowercase basename. The build discovers C files -recursively and links their objects in alphabetical basename order. Directory -paths organize navigation independently of that order. The `h8_`, `lib_`, and -`pw_` prefixes are reconstruction conventions that express the observed module -sequence. Renaming a file can change code and data placement and requires the -same complete matching checks as an implementation change. Section placement -and runtime-library handling have their own build rules. - -These paths connect the modules by responsibility: - -| Start here | Responsibility | -| --- | --- | -| [h8_resetprg.c](../src/startup/h8_resetprg.c), [pw_main.c](../src/application/pw_main.c), [pw_power.c](../src/application/pw_power.c) | Boot, foreground tasks, activity scheduling and low-power transitions | -| [view.h](../include/view.h), [globals.h](../include/globals.h) | View state, interrupt-shared values and workspace ownership | -| [pw_accel_bma150.c](../src/application/pw_accel_bma150.c), [pw_fourier.c](../src/application/pw_fourier.c), [pw_pedometer.c](../src/application/pw_pedometer.c) | Samples, spectrum analysis and accepted step batches | -| [lib_common.c](../src/support/lib_common.c), [pw_rtc.c](../src/application/pw_rtc.c), [save.h](../include/save.h) | Step pacing, Watts, time, counters and persistence | -| [pw_eeprom_m95512_bus.c](../src/application/pw_eeprom_m95512_bus.c), [pw_eeprom_m95512_io.c](../src/application/pw_eeprom_m95512_io.c), [records.h](../include/records.h), [resources.h](../include/resources.h) | Serial storage, repairing reads, records and resource layouts | -| [ir.c](../src/support/ir.c), [ir_state.h](../include/support/ir_state.h), [protocol.h](../include/protocol.h) | Connection, packets, peer exchange and deferred completion | -| [pw_home.c](../src/application/pw_home.c), [pw_pictogram_menu.c](../src/application/pw_pictogram_menu.c), [pw_pack.c](../src/application/pw_pack.c), [pw_trainer.c](../src/application/pw_trainer.c), [pw_local_settings.c](../src/application/pw_local_settings.c) | Navigation, inventory, trainer history and settings | -| [pw_pokeradar.c](../src/application/pw_pokeradar.c), [pw_battle.c](../src/application/pw_battle.c), [pw_dowsing.c](../src/application/pw_dowsing.c), [pw_carry_overflow.c](../src/application/pw_carry_overflow.c) | Search, battle, rewards and full-inventory replacement | -| [pw_follower_events.c](../src/application/pw_follower_events.c), [pw_follower_prompts.c](../src/application/pw_follower_prompts.c), [pw_friend.c](../src/application/pw_friend.c), [pw_cutscenes.c](../src/application/pw_cutscenes.c), [pw_diary.c](../src/application/pw_diary.c) | Social offers, peer play, presentations and the diary | -| [pw_nt7508.c](../src/application/pw_nt7508.c), [pw_builtin.c](../src/application/pw_builtin.c), [pw_buzzer.c](../src/application/pw_buzzer.c), [pw_player_input.c](../src/application/pw_player_input.c) | Drawing, resident artwork, sound and buttons | -| [pw_factory_test.c](../src/application/pw_factory_test.c), [pw_selftest.c](../src/application/pw_selftest.c), [pw_battery.c](../src/application/pw_battery.c) | Fixture operations, diagnostics and battery references | - -## Execution and shared state - -Boot restores mirrored records, repairs an interrupted staged copy, initializes -the peripherals and installs the foreground task. Interrupts update counters -and latch work for that task. The quarter-second RTC interrupt requests a UI -refresh; the one-second interrupt maintains seconds separately. Several ticks -can coalesce into one pending refresh, which the foreground handles when it runs. - -`MainTick` samples on every wake. A completed motion batch takes priority over -rendering; a pending refresh takes priority over deferred RTC processing. -Inactive mode suspends regular sampling and refresh timers, while later wakes -still sample for activity detection. Activity resumes motion mode for thirty -seconds. Interactive entry sets sixty seconds for the display and ninety for -motion; later button edges refresh the display timeout to ninety seconds. - -`g_state` contains runtime state, including the RAM copy of `SaveData`. -`g_ui.view` overlays the active view in an 18-byte bank. Changing `g_state.view` -preserves that bank. Radar passes its encounter byte to battle; a full inventory -passes its selection to discard; IR carries its LCD readback result into -diagnostics. Initializers deliberately preserve those bytes. - -`g_work` overlays motion, sound and IR storage. Audio reuses the three motion -sample arrays for a score and takes over the foreground while it plays. IR -reuses motion state and part of the scratch area. Task handoffs finish using the -old interpretation before preparing the next one. - -Scratch allocation reserves consecutive bytes; callers provide initialization -and alignment. Resetting scratch rewinds the cursor. The bytes remain until -another user writes them. Peer rewards deliberately place retained records -beyond the course prefix that a nested lookup reloads. Drawing helpers similarly -reuse known portions of the backing storage. Callers must account for every -intervening write before using a retained pointer. Ordinary allocation checks -1,024 bytes and sleeps after an overflow; direct large-artwork transfers use all -1,536 bytes of the backing storage. - -## From motion to steps - -The main task collects signed high bytes of the accelerometer's three axes. -Each 64-sample batch passes through the FFT, accumulating approximate magnitudes -from all axes. - -`MotionEstimateQ9` visits candidates in a defined order, compares relative -magnitudes and changes its rejection test according to the preceding batch. -These checks determine which bin is accepted. - -The accepted bin position represents steps per batch in Q9 units: one step is -512. Fractional credit carries into later batches. `StepPacingTick` spreads the -whole-step budget across subsequent samples, credits at most one step per call -and updates hourly, daily and lifetime counters. Every twenty credited steps -can earn a Watt, up to 9,999. The arithmetic, single-credit conditions and -saturation points are part of the firmware's behavior. - -RTC processing handles minutes, hours and the configured daily rollover. -The elapsed-hours counter runs through periods with an empty Pokemon slot. It -resets on a new walk or lifetime reset and stops when it or the lifetime step -count reaches 9,999,999. Rollover shifts daily history and clears the IDs of -prior peer records while preserving their other fields. - -## Views and activities - -Activity state includes input, update and rendering. Renderers can advance or -clamp animation counters, change a presentation stage or finish a view. State -names therefore appear across those paths. Timers explicitly distinguish input -polls, rendered calls, hardware counts and completed motion batches. - -Battle separates presentation phase, player action, probability row and -response code. The same response code has different meanings for Attack and -Evade; the table and dispatch document that relationship. Dowsing separates -patch selection from the chosen course item. Inventory selectors distinguish -ordinary slots from held Pokemon and event gifts, including their index bases. - -Reward persistence and its presentation can happen at different times. An IR -reward is already stored when its arrival animation begins. Some local rewards -commit on acknowledgement, while others are stored before display. Diary -appending is a separate operation; the device's cursor rotates through 23 slots -within the 24-entry stored area. - -## Stored data and communication - -`eeprom_map.h` owns serial-storage addresses. `save.h`, `records.h` and -`resources.h` describe the corresponding layouts. Resource expressions use -pointer syntax to calculate EEPROM addresses for the driver. Transfer spans -can contain more than one logical object or extend beyond the image being -displayed. - -Mirrored reads repair a bad copy, or fill both copies with `FF` when neither -checksum validates. When both validate, the buffer holds the primary copy. -The repair decision compares their additive checksums: unequal sums trigger a -backup rewrite from primary. Status receipt checks and battery-reference checks -also perform the I/O and repair described by their interfaces. - -Native H8 counters and console-produced little-endian fields coexist. Stored -fields with an `Le` suffix retain little-endian bytes during forwarding and -equality checks. Numeric consumers decode them where needed. Course item -lookup returns an encoded ID suitable for copying directly into inventory. - -`PersistentReset` clears pairing, session state, return inventory, weekly steps -and peer history. Its arguments control event and lifetime clearing independently. - -The IR layer separates handshake role, peer-status role and bulk phase. -Both peers eventually send and receive. Packet handlers may queue a foreground -completion action, which takes priority over a later error. Walk activation -sets a recovery marker around the staged page copies; diary, counter and -inventory changes after clearing the marker are outside that recovery span. - -## Graphics and sound - -Resident artwork is extracted once into six NCG files and a BMP font sheet. -The build converts these editable files into C arrays; missing images use -original placeholders. See the [artwork guide](../assets/README.md). -EEPROM artwork arrives from the console separately. -A raster page covers eight rows and stores the high then low bit plane for -each column. Small Pokemon animations contain two 32×24 frames; large animations -contain two 64×48 frames. - -The display has two drawing banks. Helpers distinguish page numbers from pixel -coordinates. Horizontal animation deliberately extends beyond the visible -96 columns; the driver transmits the requested width. Vertical page addresses -must stay within the bank. Name frames and message rules also write fixed -regions around their artwork, as described in the helper interfaces. -A blank transfer sprite still follows its arrival/departure animation. - -EEPROM score descriptors hold a little-endian offset, byte length and checksum. -Loading a score replaces the motion samples in shared RAM. A borrowed score's -storage must stay valid until playback ends and satisfy the command sequencing -and preceding-record requirements in `score.h` and `pw_buzzer.h`. Repeat commands -return to the shared RAM score. - -Notes hold duration units and timer-table indices. The worker and Timer W -interrupt cooperate on duration, separation and legato. The rest index reaches -past the pitch table into adjacent resident artwork; the shared storage -declaration includes the full accessed span. - -## Conventions and matching - -Use concise PascalCase functions and types, lowerCamelCase locals and fields, -`g_` globals, uppercase constants, and two-space indentation. Function braces -start on the next line; control braces stay on the same line. The project -selected these conventions using embedded C guidance from the period. - -Names express meaning and useful units. Comments explain relationships and -constraints that the code alone cannot make clear. Keep ordinary coordinates, -indices and numeric tables simple when their meaning is already clear. - -H8 application changes must preserve full retail identity under both supported -compiler suites. Integer promotions, signed shifts, access widths, volatile reads, -bitfield order, alignment, expression grouping and local lifetimes matter. -Call order also preserves shared random-number consumption and workspace -ownership. Run the [formatter, linter and complete builds](build.md). - -One CH38-specific return convention needs care when porting: -`EepromWriteByte` passes the last sampled event byte through the CH38 return -register without an ordinary C return expression. Its callers consistently -forward or inspect the whole event byte, including its clock and UI flags. -A compiler port must make that return explicit and review the other target -assumptions. The qualified suites are CH38 6.02.01 and 6.02.02. - -## Remaining uncertainties - -- The IR-request event has no ordinary setter in this firmware. -- `pendingStepsQ9` is consumed and cleared without an established nonzero - producer; the motion batch's `resetOnlyByte` is only cleared. -- The threshold-test `resetWord` is initialized without an established consumer. -- Preserved fields keep their observed initialization and transfer behavior - where their meaning remains unresolved, including the event-item prefix, - leading walk/peer words and the separately cleared Pokemon appearance bit. diff --git a/docs/third-party.md b/docs/third-party.md index d54d208..1520c89 100644 --- a/docs/third-party.md +++ b/docs/third-party.md @@ -1,31 +1,8 @@ -# Third-party material and tools +# Third-party licenses -The [references](references.md) describe the research and tools that inform this project. +Stickwalker uses the [MIT license](../LICENSE). Included dependencies and assets keep their own licenses. -The compiler archive reader in `tools/installshield.py` uses the InstallShield format and -block-decoding approach documented by [ISx](https://github.com/lifenjoiner/ISx) -and [Unshield](https://github.com/twogood/unshield). Their MIT notices follow. - -[Wibo](https://github.com/decompals/wibo) is downloaded separately from its -upstream release, or supplied locally. uv, Ninja, Pillow, Ruff, clang-format and Cppcheck retain their own licenses. -The Python dependencies are recorded in `uv.lock`. - -The NCG/NCL converter follows public format descriptions in -[NitroPaint](https://github.com/Garhoogin/NitroPaint) and -[Tinke](https://github.com/pleonex/tinke). The [artwork guide](../assets/README.md) -links the consulted revisions and explains the reconstructed editor metadata. - -## Renesas HEW device header - -[`include/startup/iodefine.h`](../include/startup/iodefine.h) is the retained -Renesas HEW device-register template. It identifies itself as **H8/38602 Series -Include File, Ver 2.1**, with the marker **HEW_2006.10.05**. It provides the -memory-mapped register declarations used by the firmware. - -This vendor template is outside the project's MIT license. Its existing -identification is retained in the file, and automated formatting and line-ending -conversion exclude it. The retained file contains no standalone license grant; -the project adds no license grant over Renesas's material. +The compiler archive reader follows [ISx](https://github.com/lifenjoiner/ISx) and [Unshield](https://github.com/twogood/unshield). Their notices follow. ## ISx @@ -73,38 +50,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -## Firmware simulation - -The development checks use [esp32sim](https://github.com/mikroverk/esp32sim) at -`fe3a9c08125cc760107865df3ffa979362952fe9`, by Joakim Eriksson and Alice. -[`stick/simulation/soc.patch`](../stick/simulation/soc.patch) extends its SPI, -GPIO, watchdog and RMT models. Its [MIT notice](../stick/simulation/ESP32SIM-LICENSE) -is retained with the patch. The simulator downloads Espressif's ESP32-S3 mask ROM -from [esp-rom-elfs](https://github.com/espressif/esp-rom-elfs), under Apache-2.0. - -## Stickwalker runtime - -The Stick build uses M5Stack's ESP32 board package **3.3.9**, -[M5Unified 0.2.21](https://github.com/m5stack/M5Unified/tree/0.2.21) and -[M5GFX 0.2.29](https://github.com/m5stack/M5GFX/tree/0.2.29). -The two M5 libraries use MIT; their full notices are retained in -[`stick/licenses/`](../stick/licenses/). The Arduino ESP32 core contains -LGPL-2.1-or-later material, and its underlying ESP-IDF, precompiled libraries -and components have their own licenses. - -Each firmware release includes a runtime archive with the exact application -objects, Arduino core archive, linked SDK libraries, linker scripts, dependency -sources and their original notices. Its relinking recipe reproduces the release -ELF and supports replacing the Arduino core. See the [relinking guide](../stick/docs/relink.md). - -The runtime uses ESP-IDF **v5.5.4** at -`735507283d5b2f9fb363a1901172dbd9e847945d`, including its pinned submodules, plus -LittleFS **1.22.1** and ESP Diagnostics **1.2.1**. The source inventory in -[`stick/runtime-sources.json`](../stick/runtime-sources.json) records the archive -origins, hashes and submodule revisions. The SDK's linked Espressif radio -libraries retain their included binary-library licenses. GCC runtime and Newlib -notices are included with the firmware and runtime archives. +## Other material -Earlier versions of the reconstruction remain under their original CC0 terms. -The [placeholder artwork](../assets/placeholders/LICENSE.md) also retains its -explicit CC0 dedication. +- The [Renesas HEW register header](../include/startup/iodefine.h) retains its vendor identification and is outside the project's MIT license. +- [M5Unified](https://github.com/m5stack/M5Unified) and [M5GFX](https://github.com/m5stack/M5GFX) use MIT. Their notices are in [stick/licenses](../stick/licenses/). +- [esp32sim](https://github.com/mikroverk/esp32sim) uses MIT. Its [notice](../stick/simulation/ESP32SIM-LICENSE) accompanies the simulator patch. Espressif's mask ROM uses Apache-2.0. +- The Arduino core uses LGPL-2.1-or-later. Runtime downloads include dependency sources, notices and a [relinking recipe](../stick/docs/relink.md). [runtime-sources.json](../stick/runtime-sources.json) records the dependency versions and origins. +- The [placeholder artwork](../assets/placeholders/LICENSE.md) uses CC0. Earlier reconstruction releases retain their original CC0 terms. +- Wibo, uv, Ninja, Pillow, Ruff, clang-format and Cppcheck keep their upstream licenses. +- The graphics converter follows format descriptions from [NitroPaint](https://github.com/Garhoogin/NitroPaint) and [Tinke](https://github.com/pleonex/tinke). diff --git a/stick/README.md b/stick/README.md deleted file mode 100644 index 4e36f2a..0000000 --- a/stick/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# Developing Stickwalker - -The [build guide](docs/build.md) covers toolchain setup, firmware checks and compiling -firmware. For installing a release on your Stick, use the -[installation guide](docs/install.md). - -## Find the code - -The reconstructed application in `src/` and `include/` implements game rules, records -and the infrared protocol. The adapters here connect that application to M5StickS3. - -| Change | Start here | -| --- | --- | -| Native timing and foreground scheduling | [native_runtime.cpp](native_runtime.cpp), [foreground_bridge.c](foreground_bridge.c) | -| Board coordination and Settings overlay | [board_runtime.cpp](board_runtime.cpp), [board_settings.cpp](board_settings.cpp) | -| Button acquisition and native control mapping | [input_bridge.cpp](input_bridge.cpp), [controls.cpp](controls.cpp) | -| Panel, palette, rotation and backlight | [display_panel.cpp](display_panel.cpp), [display_bus.cpp](display_bus.cpp) | -| Optical capture, receive decoding and transmission | [ir_transport.cpp](ir_transport.cpp), [ir_rx_core.cpp](ir_rx_core.cpp), [ir_gate.h](ir_gate.h) | -| Motion samples and sensor scaling | [motion_sample.cpp](motion_sample.cpp), [accel_bridge.cpp](accel_bridge.cpp) | -| Physical audio readiness and codec supply | [sound_bridge.cpp](sound_bridge.cpp) | -| Game-data persistence and recovery | [eeprom_backend.cpp](eeprom_backend.cpp), [device_identity.c](device_identity.c) | -| Board supplies, PMIC setup and processor sleep | [board_hal.cpp](board_hal.cpp), [power_sleep.cpp](power_sleep.cpp) | - -The [player guide](docs/playing.md) describes the complete walk and transfer flow. -The [native platform guide](docs/native-platform.md) describes ownership, -readiness boundaries, input cancellation and storage recovery. -The [motion guide](docs/motion.md) explains the sensor adaptation. -The [source guide](../docs/source.md) follows the native application across -modules. [CONTEXT.md](../CONTEXT.md) defines motion, display and power terms. -The [power guide](docs/power.md), [settings/display implementation](docs/device-settings-and-display.md) -and [debugging guide](docs/debugging.md) explain the adapters. - -## Change and validate - -Keep protocol/session behavior in `src/support/ir.c`; optical acquisition and -decoding belong in the Stick adapters. Preserve [the original motion schedule](../docs/adr/0001-preserve-native-motion-ownership.md), -and the [M-only screen-off wake policy](../docs/adr/0002-m-only-dark-wake.md). -Changes to the optical sampler need the [machine-code timing check](docs/release.md#build-and-check). - -`PW_STICK_BENCH_CONTROL` enables state-changing serial diagnostics. Build it with -`--bench-control` and keep those images separate from production candidates. -The scripts in `audits/` state whether they use host simulation, saved inputs or -a physical board. Logs, flash backups and received data belong in ignored -`stick/.build/`. - -The [test history](docs/validation-history.md) records device trials and the -[release guide](docs/release.md) covers packaging and testing a release. Follow -[Contributing](../CONTRIBUTING.md) for patches and problem reports. - -## Device identity and save repair - -Each Stick needs a persistent ID for peer play. `device_identity.c` provisions -missing IDs from the ESP32's factory MAC, keeping all six MAC bytes in the -40-byte identity. It preserves existing valid IDs and recovers a damaged master -copy from its backup or saved status. - -At mount, the storage backend replays the save journal, repairs the master and -status IDs and the outgoing trainer record, then checkpoints the image before -boot. Repair changes the identity fields and their checksums; the game data -stays intact. `CommitStagedWalk` also copies the current ID into the trainer -record, including when boot finishes a transfer interrupted on older firmware. - -The production workflows exercise journal recovery, stale-checkpoint records, -optical status/page exchange and persistence across reset. Run them with -`uv run python stick/check.py`. Repeat encounters, day rollover and complete -retail transfers still need the device checks in the release guide. diff --git a/stick/build_port.py b/stick/build_port.py index 64d0067..a993012 100644 --- a/stick/build_port.py +++ b/stick/build_port.py @@ -111,14 +111,14 @@ def query(*parts): {}, ) if core.get("installed_version") != CORE: - raise InputError(f"Install m5stack:esp32@{CORE}; see stick/docs/build.md.") + raise InputError(f"Install m5stack:esp32@{CORE}; see docs/build.md.") installed = { row["library"]["name"]: row["library"]["version"] for row in query("lib", "list")["installed_libraries"] } for name, expected in LIBRARIES.items(): if installed.get(name) != expected: - raise InputError(f"Install {name}@{expected}; see stick/docs/build.md.") + raise InputError(f"Install {name}@{expected}; see docs/build.md.") output = args.output_dir.resolve() # Never leave a successful manifest attached to a failed/replaced build. output.mkdir(parents=True, exist_ok=True) diff --git a/stick/docs/build.md b/stick/docs/build.md deleted file mode 100644 index 3bedefe..0000000 --- a/stick/docs/build.md +++ /dev/null @@ -1,80 +0,0 @@ -# Building Stickwalker - -This creates the firmware you will install on your Stick S3. Download the source -from [the repository](https://github.com/lumirth/stickwalker) and unpack it, or -clone it with Git: - -```sh -git clone https://github.com/lumirth/stickwalker.git -cd stickwalker -``` - -Open a terminal in that folder. Run the following commands on your computer. - -Use Python 3.11 or later, [uv](https://docs.astral.sh/uv/), and -[Arduino CLI](https://arduino.github.io/arduino-cli/latest/installation/). -The build was tested with Arduino CLI 1.5.1 on macOS arm64. - -Install the tested board core and libraries in your Arduino environment: - -```sh -arduino-cli config add board_manager.additional_urls https://static-cdn.m5stack.com/resource/arduino/package_m5stack_index.json -arduino-cli core update-index -arduino-cli core install m5stack:esp32@3.3.9 -arduino-cli lib install 'M5GFX@0.2.29' -arduino-cli lib install --no-deps 'M5Unified@0.2.21' -``` - -Build the firmware: - -```sh -uv sync --locked -uv run python stick/build_port.py -``` - -The builder checks those core and library versions and selects -`m5stack:esp32:m5stack_sticks3:PSRAM=opi`. It generates resident artwork directly -from the included placeholders and compiles production firmware without an -H8 compiler. - -Output is `stick/.build/output/`: `PwStick.ino.bin` is the application; -`PwStick.ino.elf` retains symbols for debugging. Bootloader and partition binaries -are generated for first installation. `build-manifest.json` records the board, -tool versions, production/diagnostic mode, artwork, source inputs and output -SHA-256 hashes. Follow the [install guide](install.md) before writing any image. - -For an isolated Arduino installation, pass the executable and configuration: - -```sh -uv run python stick/build_port.py --arduino-cli /path/to/arduino-cli --config-file /path/to/arduino-cli.yaml -``` - -For resident retail or edited artwork, follow the [artwork guide](../../assets/README.md), -then add `--artwork local`. Missing local images fall back to placeholders. Keep -that build for personal use. The default build always uses placeholders, even -when `assets/local/` exists. - -Use `--output-dir` to keep builds in separate directories. Add `--bench-control` -for a diagnostic build with serial commands and tracing. Release builds use the -default production mode. - -## Firmware E2E checks - -Install Rust 1.98.1 and Tesseract, then run: - -```sh -uv run python stick/check.py -``` - -This builds production firmware and boots it on a simulated Stick S3. It checks -Settings navigation, rotation and appearance changes, persistence after restart, -and Connect recovery and retry with the receiver held low or high. The runner -reads the rendered display and decodes the transmitted infrared waveform. - -Each run saves screenshots, console output, signal recordings, flash and a -result record under `stick/.build/e2e-results/`. The record identifies the -application and simulator used. See the [simulation guide](../simulation/README.md) -for setup, retained builds and scenario commands. - -For distributable archives and optical timing verification, follow -[release preparation](release.md). diff --git a/stick/docs/controls.md b/stick/docs/controls.md index e58c4ef..18544d4 100644 --- a/stick/docs/controls.md +++ b/stick/docs/controls.md @@ -1,53 +1,16 @@ -# Controls and settings +# Stick controls -Use the Stick's buttons to navigate the Pokéwalker screens. The default layout -uses L for Left, M for Center and R for Right, with the screen left-side-down. +The default screen orientation is left-side-down. -In a two-key layout, if Center is hard to trigger, increase the M+R window in Stick Settings. This -gives you more time to press the second button. Keep both pressed together for -at least 20 ms. +Hold M for about half a second to wake the screen. -| Layout | Left | Center | Right | Open Stick Settings | +| Layout | Left | Center | Right | Stick Settings | | --- | --- | --- | --- | --- | -| 2-key M/R | M | M+R | R | L | -| 2-key R/M | R | M+R | M | L | | 3-key L/R, default | L | M | R | Hold L+R for one second | | 3-key R/L | R | M | L | Hold L+R for one second | +| 2-key M/R | M | M+R | R | L | +| 2-key R/M | R | M+R | M | L | -## Wake the screen - -With the screen off, hold M until the screen wakes, roughly half a second in any layout or rotation. -Release all buttons before navigating. L and R work again once the screen is -awake. - -Wake uses native Center-hold qualification and its social-offer opportunity, -rather than a separate wall-clock shortcut. An abandoned hold cancels -preparation. IR entry also consumes its gesture through release, preventing -held input from being replayed into the result screen. - -## Change settings - -In Stick Settings, M selects the next row, R changes its value, and L closes -the menu. Input layout and screen rotation are independent. - -Choose Light or Dark appearance and use Test speaker to check audio. -In two-key layouts, M+R window selects 80, 120 or 160 ms between the two presses; -80 ms is the default. Three-key layouts show Center: M instead. -The game's Contrast setting adjusts backlight brightness. - -Your layout, rotation and appearance are remembered across restarts, separately -from your Pokémon and game progress. Settings also shows battery voltage and an -estimated charge percentage. - -In three-key layouts, hold physical L and R together for one second to open -Stick Settings. Gameplay receives both directions while the hold is pending. -Release all buttons after the overlay opens before using its controls. Native -Volume and Contrast retain their original two-choice selector. Native time and -background work continue while Stick Settings captures buttons. - -Rapid taps are buffered, but stale input is not played into later screens. If -pending transitions exceed 250 ms or fill the bounded queue, the gesture is -canceled until release. Diagnostic builds report these cancellations. +For two-key layouts, M+R window sets how much time you have to press the second button. -Unplug USB to carry the Stick. It sleeps between game tasks while continuing -to track walking; hold M for half a second when you want to check your Pokémon. +Contrast controls the Stick's backlight. diff --git a/stick/docs/debugging.md b/stick/docs/debugging.md deleted file mode 100644 index db1aed3..0000000 --- a/stick/docs/debugging.md +++ /dev/null @@ -1,57 +0,0 @@ -# Debugging Stickwalker - -Use the ELF from the firmware you are testing; release ZIPs include it alongside -the application binary. [Back up the flash](install.md#back-up-before-writing) -before installing a diagnostic build so you can restore the current game data. - -## Startup or a disappearing USB connection - -USB power keeps the ESP32 awake. Use it to inspect startup, PMIC registers and -normal execution. Opening a serial port or changing DTR/RTS can reset the board, -and ESP32 Light-sleep interrupts USB Serial/JTAG. Record resets and port changes -alongside the firmware's reset reason. - -After flashing, check the application descriptor, ready state and advancing -RTC. These establish that the application has booted. A flash verification -checks the stored bytes. - -## Battery sleep and wake - -Arm a finite diagnostic trial over USB, disconnect it, then reconnect after the -trial. Retained records survive the USB reset and contain sample counts, sleep -returns, elapsed time, wake sources and entry errors. Save incomplete records -too; they help locate where execution stopped. - -A pending wake source or a sleep interval shorter than entry overhead can -cancel a sleep attempt. Record the API stage and error code to distinguish -these cases from a configuration failure. For power consumption, measure -current at the battery connection using the [power guide](power.md). - -## Breakpoints and register inspection - -Use Espressif OpenOCD with `board/esp32s3-builtin.cfg` and the matching ELF. -Keep USB connected and place hardware breakpoints before or after infrared -acquisition. Halting inside the optical sampler changes the signal timing. -Watchpoint 1 is reserved for stack-end detection. - -Set `ESP_FLASH_SIZE 0` before loading the board configuration to disable flash -breakpoints. If CPU1 inspection fails with an invalid OCD ID, try -`set ESP_ONLYCPU 1` before loading the configuration. This allowed CPU0 reads -in the [recorded board investigation](history/power-and-wake.md). -Restart the board without the debugger when testing watchdogs or panic behavior. - -## Crash dumps and PMIC failures - -The coredump partition starts at `0x7F0000` and occupies `0x10000` bytes. Decode -a saved dump with its matching ELF and check its integrity and application -identity. Record when you collected it, since flash can retain an older dump. -If no dump was written, inspect the reset reason and power supply, then check -whether execution is stuck or just the USB connection has dropped. - -For a failed PMIC read, capture the I2C address, register, attempt number, -duration, error code and reset reason before retrying. Use M5Unified's existing -I2C bus and bounded reinitialization. Measuring SDA and SCL after the failure -can show a stuck bus; a trace during the transfer is needed to locate the fault. - -The [hardware references](../../docs/references.md#stick-hardware-and-runtime) -link the vendor's debugging, sleep and peripheral documentation. diff --git a/stick/docs/device-settings-and-display.md b/stick/docs/device-settings-and-display.md deleted file mode 100644 index 0c8e472..0000000 --- a/stick/docs/device-settings-and-display.md +++ /dev/null @@ -1,60 +0,0 @@ -# Settings and display implementation - -For button instructions, see [controls and settings](controls.md). This guide -covers persistence and pixel presentation for changes to the adapters. - -## Persist settings - -`pw-controls/layout-v2` stores layout, rotation and the two-key window. -The display adapter applies rotation. Button mapping uses the selected layout. -Older configuration keys are ignored. -Without a saved setting, the layout is three-key L/M/R and rotation is left-side-down. -Game progress is stored in the EEPROM image. - -Three-key Settings access observes a one-second physical L+R hold in `Controls`; -normal directional transitions continue until recognition. The input bridge -then requests the overlay, clears pending Center activity and requires release -at entry and closure. The native Settings renderer keeps its two original -choices. - -`pw-display/dark` stores appearance. Unsaved installations default to Dark. -A failed persistence write leaves the previous appearance active. A successful -change repaints the overlay immediately and invalidates the game image so it -receives a complete repaint when the overlay closes. - -## Present native shades - -The port scales native pixels 2× with nearest-neighbor sampling and preserves -native rendering cadence. Its RGB565 palette is: - -| Native shade | Light | Dark | -| --- | --- | --- | -| 0, background | `FFFF`, white | `0000`, black | -| 1 | `AD75`, light gray | `528A`, dark gray | -| 2 | `528A`, dark gray | `AD75`, light gray | -| 3, foreground | `0000`, black | `FFFF`, white | - -RGB565 gives green six bits and red/blue five. The intermediate channels expand -to (173,174,173) and (82,81,82) with M5GFX, within one 8-bit level of neutral. - -Call `setSwapBytes(true)` after panel reinitialization. M5GFX's -`create_pc(uint16_t*, bool)` path otherwise interprets intermediate buffer words -in the wrong byte order. Black and white still look correct, which can conceal -the mistake. Light/Dark selects the software palette. Panel inversion, gamma -and RGB order remain driver settings. - -The selected theme also applies to Settings, battery text and the game border. -Wake clears the controller with the current background. Native Contrast maps -to backlight brightness. - -## Check changes - -The presenter consumes an immutable native LCD snapshot and revision. A call -transfers at most eight native rows and finishes with SPI idle, retaining no -caller pointer. Remaining strips are scheduled independently of native refresh; -appearance, rotation and overlay changes also invalidate presentation. - -`uv run python stick/check.py` exercises production input gestures, layout and -appearance persistence, and returning from Settings to the native screen. -After a display change, check the four shades and Settings on the board in both -appearances and after wake. diff --git a/stick/docs/firmware-simulation-research.md b/stick/docs/firmware-simulation-research.md deleted file mode 100644 index 47b16e0..0000000 --- a/stick/docs/firmware-simulation-research.md +++ /dev/null @@ -1,130 +0,0 @@ -# Simulating the Stick S3 firmware - -The useful test target is the compiled Stickwalker application running on an -emulated ESP32-S3 and a model of the Stick S3. Drive its buttons, acceleration and -infrared receiver, then observe the screen, transmitted packets and persistent -flash. This exercises the application, FreeRTOS, SDK drivers and board adapters -together. - -Research checked on 10 October 2026. The selected bring-up target is -[esp32sim](https://github.com/mikroverk/esp32sim), pinned to -`fe3a9c08125cc760107865df3ffa979362952fe9`. It provides editable CPU and peripheral -models, runs locally, and exposes the board events needed by Stickwalker. The local -bring-up now executes Settings and Connect workflows on production firmware. -The runner and setup instructions are in the [simulation guide](../simulation/README.md). - -## Simulator choices - -| Simulator | Relevant support | Work needed for Stickwalker | -| --- | --- | --- | -| [esp32sim](https://github.com/mikroverk/esp32sim) | Dual-core Xtensa S3; real ROM, bootloader and app; flash/PSRAM; I2C, SPI2, I2S, RMT; editable board models and scripted inputs | Stick S3 board devices, SPI3, dedicated GPIO input and timer-group watchdog behavior | -| [Wokwi](https://docs.wokwi.com/guides/esp32) | S3 CPU, GPIO/IOMUX, flash/PSRAM, I2C, SPI, RMT, LEDC and watchdogs; loads compiled images | Custom Stick S3 devices; S3 I2S is unsupported; dedicated GPIO instruction support needs a runtime probe | -| [Espressif QEMU](https://github.com/espressif/esp-toolchain-docs/blob/main/qemu/README.md) | S3 CPU, ROM, flash/PSRAM, interrupts and timers | Its support table lists I2C, GP-SPI, I2S, RMT and GPIO/IOMUX as unsupported; the existing framebuffer device uses a separate virtual interface | -| [PastorCatto ESP32-Emulator](https://github.com/PastorCatto/ESP32-Emulator/tree/main/qemu) | QEMU patches add S3 SPI2/SPI3, I2C and native USB serial | Board devices and the remaining receiver/audio peripherals; inspect patches because the top-level README predates them | -| [Velxio](https://github.com/davidmonterocrespo24/velxio/blob/master/docs/BUILD-QEMU.md) | Its own QEMU variant adds S3 peripheral callbacks and GP-SPI | Obtain its matching source/runtime archive through a free personal key, then inspect S3 I2S, dedicated GPIO and watchdog behavior | -| [Espressif esp-emu](https://github.com/espressif/esp-emulator) | S3 compiled images, octal PSRAM, GP-SPI, I2C, RMT, GPIO, LEDC and watchdogs; native macOS arm64 binary | Establish how to attach Stick S3 bus devices; the public repository distributes binaries, documentation and tools | - -Espressif's [Arduino CI](https://github.com/espressif/arduino-esp32/blob/master/.github/CI_README.md) -uses Wokwi and physical boards for runtime checks. Its current configuration -disables QEMU by default because Wokwi covers more peripherals. M5Stack's -[Stick S3 documentation](https://docs.m5stack.com/en/core/StickS3) supplies the -board wiring, chip identities and firmware flashing routes; the documented -development routes run on the board. - -### Wokwi automation - -Load a merged image containing the production bootloader, partition table and -application. Set the board attributes to 8 MB flash and 8 MB octal PSRAM, and -`cpuFrequency` to `max`: the default instruction-rate cap is about 8 MHz, while -the optical sampler uses the 240 MHz cycle counter. - -[Automation scenarios](https://docs.wokwi.com/wokwi-ci/automation-scenarios) -control buttons and sensors. The [CLI](https://docs.wokwi.com/wokwi-ci/cli-usage) -records serial output, screenshots and logic-analyzer traces; it requires a -Wokwi CI token. The [Python client](https://github.com/wokwi/wokwi-python-client) -also reads display framebuffers and controls simulation start, pause and restart. - -[Custom chips](https://docs.wokwi.com/chips-api/getting-started) run as WebAssembly -and can implement I2C/SPI devices. The [time API](https://docs.wokwi.com/chips-api/time) -provides nanosecond deadlines. For the photodiode, model the voltage threshold -crossing after the firmware releases GPIO5; Wokwi's -[analog support](https://docs.wokwi.com/chips-api/analog) is limited, so a generic -photoresistor or 38 kHz remote receiver would model a different circuit. - -## Stick S3 model - -The production board uses the following devices and routes: - -| Device | Connection | Behavior the model must provide | -| --- | --- | --- | -| M5PM1 | I2C1, SDA47/SCL48, address `0x6e` | Register access, supply state, millivolt readings, L-button state and read-cleared press latch | -| BMI270 | Same bus, address `0x68` | Reset/configuration upload, initialization status, data-ready flags and timed acceleration samples | -| ST7789P3 | SPI3; MOSI39, SCLK40, DC45, CS41, reset21 | Display commands, RGB565 RAM, orientation, visible window and status reads | -| ES8311 | I2C address `0x18`; I2S0 MCLK18/BCLK17/WS15/data14 | Codec register state and I2S sample capture, with amplifier enable from M5PM1 GPIO3 | -| M/S buttons | GPIO11/12 | Active-low contacts and timed presses/releases | -| Optical link | Transmitter GPIO46; external photodiode GPIO5 | RMT pulse output and charge/discharge threshold input, including disconnected and loose-contact states | -| Saved data | Production flash partitions | Real erase/write operations, retained data across reset and application updates | - -These routes come from `board_hal.cpp`, `sound_bridge.cpp`, `input_bridge.cpp` and -[M5Stack's pin map](https://docs.m5stack.com/en/core/StickS3#pinmap). -Use the [M5PM1 manual](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/M5PM1_Datasheet_EN.pdf) -for defaults and latch semantics, and the -[Bosch BMI270 datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi270-ds000.pdf) -for sensor configuration and readiness. The Stick S3 has a BMI270; the driver's -optional BMM150 probe does not establish that a magnetometer is fitted. - -In the pinned esp32sim source, `DcsPanel::st7789()` already interprets display -writes into GRAM. Add the Stick's visible crop and P3 status replies. Its ES8311 -device is currently register memory, so codec power behavior needs its own model. -`BoardModel` exposes cycle-stamped output-enable changes and scheduled input -edges, which fit the photodiode measurement. - -The pinned upstream needs three SoC additions for this firmware. SPI3 is named -but unmounted; `ee.get_gpio_in` is an unimplemented Xtensa instruction; -timer-group watchdog registers retain writes without executing watchdog stages. -The repository's `soc.patch` implements these and records RMT pulse durations. CPU timing uses an approximate instruction -cost, with 64-instruction scheduling rounds by default. Functional scenarios can -use this scheduler; optical measurements need comparison with retained hardware -traces before interpreting timing results. - -## Extending coverage - -Load a retained installation save and update only the application partition. -Read the Pokémon, route and progress through the UI before and after updating. -Inspect the final saved bytes with an independent save decoder, allowing only -the documented identity and checksum changes. - -For Connect, drive disconnected, continuously active and intermittent receiver -states. Observe the connection result and continued navigation, then restore a -healthy optical link and complete an exchange. The bring-up comparison below exercises watchdog resets through the compiled -receiver. - -Peer scenarios should use separate device identities and persistent saves: -first meeting, repeat meeting, next-day meeting, reboot between encounters and -interrupted walk transfer. Check the visible outcome and independently decoded -transmitted packets/save contents. Retained Pokéwalker recordings and documented -protocol behavior provide expectations; a second copy of Stickwalker supplies a -peer but does not by itself establish interoperability with an original walker. - -## Bring-up results - -The production Connect candidate boots through the ESP32-S3 ROM and bootloader. -Physical button actions open Settings, change rotation and appearance, restart -the chip and reopen Settings with both choices retained. OCR checks the rendered -screen against the behavior described in the controls guide. - -A second workflow holds GPIO5 low during Connect, opens Settings after the -attempt, closes it, holds GPIO5 high and retries. It decodes emitted GPIO46 -pulses against [Dmitry's recorded protocol](https://dmitry.gr/?proj=28.+pokewalker&r=05.Projects). -It checks 115200-baud 8N1 SIR framing, the transport XOR and the `0xFC` -advertisement, observes the no-reply sequence and -opens Settings afterward. Both workflows pass on the current candidate with -application SHA-256 -`24ef7b4c95c8ccfd31369957cbfe2c98ec1b12c1017bbc32efa49fe0bda67661`. - -As a comparison, the released `candidate-2026-10-10` application with SHA-256 -`26fa8a557a05083a9df8f9e527cd169c95796e423d4a7267da990a9f005a54c9` -resets during the held-low scenario. This comparison uses the same simulator -models and compiled application images. The low/high inputs exercise electrical -fault and no-reply behavior; retained optical measurements will establish the -charge/discharge model for peer and Game Card exchanges. diff --git a/stick/docs/history/README.md b/stick/docs/history/README.md deleted file mode 100644 index de79689..0000000 --- a/stick/docs/history/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Device test records - -These records collect the transfer, power, input and audio trials used to develop -Stickwalker. Each names the firmware, setup and result. Raw traces and private -saves are kept locally under `stick/.build/`. - -- [Transfer trials](transfers.md) cover a retail HeartGold Game Card, the 3DS - test peer, EEPROM readback and packet failures. -- [Power and wake trials](power-and-wake.md) cover battery sleep, button handling, - PMIC faults, display wake and save preservation. -- [Audio trials](audio.md) cover score timing, codec startup and acoustic checks. - -For current instructions, see [development](../../README.md), -[power management](../power.md), [debugging](../debugging.md) or -[release preparation](../release.md). Each release's qualification record lists -which checks ran on that version. diff --git a/stick/docs/history/audio.md b/stick/docs/history/audio.md deleted file mode 100644 index 743a9be..0000000 --- a/stick/docs/history/audio.md +++ /dev/null @@ -1,92 +0,0 @@ -# Audio trials, September 26-27, 2026 - -These tests measured score timing and acoustic output. Raw -artifacts remain in `stick/.build/sound-scheduling-2026-09-26/` and -`stick/.build/codec-startup-2026-09-27/`. - -These are historical results, not qualification of the current -[native-platform redesign](../native-platform.md). It removes score reanchoring -and the fixed cold-start delay, preparing physical output separately from native -score timing. Its acoustic onset and settling need fresh device measurements. - -## Score scheduling - -The native score walker and Stick adapter initially failed 102 of 176 host -timing cases. An 80 ms foreground stall reduced a warm move cue's only tone -to zero duration; even an 8 ms stall clipped its first note. Anchoring new -notes to the actual output transition and selecting speaker channel 0 fixed -the tested scheduling and overlapping-voice behavior. - -All 176 final cases passed across sixteen HGSS scores, including cold starts, -warm repeats, initial stalls of 0/8/20/80/200 ms and a stall during playback. -Tone/separator lifetimes agreed with unstalled playback within 200 µs. -These are host command-lifetime observations. - -A USB bench recorded 20/20 complete native move cues after an 80 ms stall, -with zero tone/begin/power errors and minimum command lifetime 52,667 µs. -One I2S startup served the batch; codec/I2S flags returned to zero after the -hold expired. - -The production app was -`d9e3c352cf217a292557c099923fd2c7c3fa75c28a5db3be5059a2336d90aef3`. -Its app-only flash and complete storage preservation were verified. The logical -EEPROM hash stayed -`5ccd615bca4bd063098e94bbab297c38b22475b64093869eefad74f2d421aac4`; -boot checkpointed the existing journal from generation 567 to 568. - -## Cold output path - -The proposed missing-clock cause was falsified: all six observations found -168 to 175 BCLK transitions per 400 µs before the first note. - -The codec retained power-sequence register `0x0C=0x20` and 24-bit input while -output was 16-bit stereo. The adapter now verifies stage setup before activation, -configures DAC bias/format and handles an acknowledged first-write loss. -Cold preparation uses 200 ms, with no preparation delay for warm repeats. -At 65 ms the diagnostic ADC transient obscured the 52 ms cue; 200 ms is a -the preparation interval used by the passing tests. - -The board self-test uses codec digital feedback and microphone energy, requiring -both the native cue near FFT bin 16/17 and its observed fifth harmonic near -bin 82/83. It reports the spectral measurements and discards the samples. - -## Results - -- BCLK-before-tone: 6/6 present. -- Physical outgoing PCM: cold and warm native cues both present. -- Original ADC startup: samples stayed zero until approximately 730 ms during - a sustained calibration tone. Selecting fast VMID charge alone did not - remove that delay in the tested configuration. -- Clearing the stage timing removed the long ADC gate, but cold 65 ms captures - still had a large analog transient. In the retained eight-case replay, all - four cold captures failed the spectral criterion; all four warm cues passed. -- Final 200 ms implementation: **24/24 acoustic cases**, 12 cold rail cycles - and 12 warm repeats on the same boot. Minimum qualifying sample count was - 1,367. All 24 had complete 512-frame spectra and the expected spectral bins. - Twelve I2S starts, no begin/power/tone errors, and final codec/I2S flags zero. -- Amplifier-disabled control: the digital peak stayed at bin 17, while the - microphone peak moved from bin 83 to bin 6. Maximum note-band amplitude - fell from 293 to 8. This distinguishes acoustic pickup from digital leakage. -- Native score timing: **176/176** cases over the 16 HGSS scores, initial - foreground stalls and mid-note stalls. Pitch and note/separator durations - remain within the existing 200 µs comparison tolerance. -- Register-order/fault regression: the previous adapter fails every case; - the final adapter passes normal startup, first-write loss, retained resume, - full rail loss, and safe failure after persistent write loss. -- All 16 existing lifecycle/fault cases pass with the updated cold deadline. - -The microphone capture includes the ADC's startup transient, which obscured -the tone in the failed cold captures. The final 200 ms setup produced the -expected signal in both cold and warm tests. These measurements describe the -recorded signal spectrum; listening checks assess the resulting sound. - -One intermediate spectral diagnostic overflowed loopTask's stack while printing. -That batch is invalid and remains in the evidence directory. Diagnostic buffers -were moved to bounded static storage; the final 24-case batch had no resets. - -The production sampler functions retained their complete bytes and addresses. - -The installed codec-startup production app was -`edcf812717e01e48ac78c1aa9bd2d6e2f930608778bf68cca3afd394deebee94`. -App readback, the complete storage partition and all logical EEPROM bytes -matched their backups; CPU0 readback found ready=1 and advancing RTC after resume. diff --git a/stick/docs/history/ir-readiness-regression.md b/stick/docs/history/ir-readiness-regression.md deleted file mode 100644 index 0943eb7..0000000 --- a/stick/docs/history/ir-readiness-regression.md +++ /dev/null @@ -1,146 +0,0 @@ -# IR readiness and DS reply regression - -PR #5 moved receive handoff and durable EEPROM work into the compiled Stick -port. The user subsequently reported both immediate DS connection failures and -failures after the transfer sound began. Hardware was unavailable for this -investigation. - -The references are the matching [Pokéwalker firmware](https://github.com/lumirth/pw/tree/6dc7bc09950078fa3fe0dffa4dae34e9549a99da) -and the user's HGSS `src/phc/irc.c`, `phc_lib.c` and PHC record definitions. -The pre-PR comparison is `b3ef7a2`; its existing Watts byte-order defect was -accepted only by the diagnostic comparison peer, so it could exercise the -same acquisition and transfer sequence. - -## Reproduction - -The original ideal, short status/read exchange passed. A longer synthetic -uncompressed course transfer at the captured retail-card baud, with local -transmitter echo and separate CPU instruction-cost scheduling, failed on the -PR firmware. The pre-PR firmware completed the same exchange. One retained -failure changed a data bit in a 136-byte page, causing the native checksum to -reject it and the DS peer to reach its unchanged 100 ms timeout. - -The course-only comparisons used the same 10,430-byte public payload -(`b5dcc0c82d28b8f9270aa4add67523011952a0c5aeb63253126188ee5574afa8`), -115760 baud, 5 ms peer turnaround and live local transmitter echo: - -| Firmware | Result | -| --- | --- | -| Pre-PR `b3ef7a2` | All 82 course pages complete; legacy Watts accepted only for comparison | -| PR's original IR implementation at `68ae1fe` | Corrupted page; timeout after eight native transmissions | -| Sampling/handoff repair alone | Valid pages, then a late reply after 27 native transmissions | -| Sampling, bounded readback and exact decoder optimization | All 82 course pages complete | - -The producer had acquired additional request/ack fences and handoff bookkeeping -inside its timing-sensitive gate function. The worker now stops at a complete -burst and performs notification and release waiting outside that function. -Readiness observes fresh progress in the existing published sample counter; -it still waits for physical samples before initial transmission and replies. -Receive-to-reply storage remains protected by a consumed-event ticket. - -After removing the acquisition failure, the broader payload exposed late -replies during journal growth. The write path retains checked open, size, write, -sync and close operations. Its independent readback uses a fresh POSIX -descriptor to read only the appended header and payload, avoiding buffered -seek/read prefix work. Reader closure is checked too. - -The decoder's complete candidate set, framing-first/phase-second ranking, -original tie order, baud range, phase range, strong/weak thresholds and recovery -rules are retained. It visits likely baud candidates first and rejects a -candidate only when its already-observed framing errors and nonnegative phase -sum prove it cannot win. Winning data-cell occupancy is built once. A -sanitizer-backed comparison against the original decoder matched all output -bytes, geometry, cached-baud behavior and recovery diagnostics on 2,048 -generated waveforms. This creates reply-time headroom without accepting a -packet the previous search would reject. - -## Permanent coverage - -`peer-transfer` follows HGSS's uncompressed put choreography with public data: -status/Watts, 82 course pages, five trainer-record pages, PHC and distribution -reads, the checksum/ping exchange, commit and one-way shutdown. It checks the -native committed course and record independently after reset. - -`peer-play` uses a synthetic eligible Walker and exchanges status, animation, -name, trainer-house records, peer information and start. It checks both bulk -transfer directions and the received resources and native peer-history entry -after reset. This exercises one handshake role, not all collision/backoff cases. - -Run the exact production build through: - -```sh -uv run python stick/check.py --scenario peer-transfer --scenario peer-play -``` - -These workflows execute the ROM, bootloader, FreeRTOS, SDK and application and -use optical/button/bus inputs. They do not inject native game state. Their -instruction-cost mode schedules the CPUs independently in one-instruction -quanta using the simulator's opcode, branch, call and load-use costs. It is a -software timing regression test; analog acquisition, cache contention, flash -part latency and retail hardware interoperability remain outside its model. -The frozen machine-byte timing reference remains a separate diagnostic. - -## Initial software candidate (`22769ce`) - -All 13 production workflows pass on application -`05ed911a060fca5c3e023a7994e351ed553974e68789a5caff5e6283f31730e5`. -The full put completes 135 native transmissions, then independently verified -course/record persistence after reset. Its maximum modeled DS send-to-complete- -response interval is 91.99 ms against the unchanged 100 ms deadline. The same -exchange also completes on the fresh bench build. Walker resources and native -peer history survive reset. - -Both builds' input and output hashes match their manifests. Static checks pass, -and Renesas 6.02.01 and 6.02.02 each reproduce the canonical 49,152-byte image -(`f9e210a3b74afbbd12c5a66a51cc05cb9fbac986805ff0a3bfb4be6074d15607`). -The frozen machine-byte comparison differs for all three tracked functions; -its reference has not been updated. These new images were not flashed. - -## Retail put failure and flash busy timing - -The initial local-artwork candidate was installed and app/non-app flash verified. -The user then reported successful return from stroll, failed start of stroll, and -occasional black screens. A single subsequent diagnostic attempt decoded all five -incoming frames with no checksum or malformed-page rejection. They were response, -status, put request, and compressed course pages at `0xD700` and `0xD780`. -The last acknowledgment began 108.039 ms after the peer's final pulse; the journal -append maximum was 98.022 ms. The earlier production capture retained a 103.471 ms -append. No crash was captured; the user could not reproduce that symptom. - -HGSS `compress`/`sendData` independently compress 128-byte course and trainer pages -using Nintendo LZ10, falling back to raw pages when compression does not help. -The last partial page is sent in a padded 128-byte form. The original full-put -workflow covered only uncompressed writes and therefore omitted this path. - -The SPI flash model also completed programming and erases instantly. Optional WIP -busy periods now exercise the real SDK's polling: 300 microseconds per program -command and 20 milliseconds per sector erase are explicit stress parameters, not -claimed measurements of the installed flash chip. With those periods and the -compressed put sequence, the installed filesystem-backed implementation fails -unattended: a 116.078 ms append produces a 132.544 ms acknowledgment and the peer's -unchanged 100 ms timeout. - -The backend now uses a single flash-record format in the existing game-data -partition. Two checkpoints cover a bounded log. Headers and bodies are programmed -and independently read back before a final publication word. Log space is erased -before acquisition; append operations cannot erase during an IR session. Checkpoint -replacement and journal clearing run after publication outside that session. -The filesystem and old save/configuration conversions are removed. - -The first raw-log build completes the full uncompressed put and Walker exchange -with the same flash-busy profile; its maximum modeled DS response is 39.04 ms. -Recovery checks cover current records, stale records and an unpublished truncated -append. Permanent compressed-put coverage and a bus-observed zero-erase invariant -protect the timing window. Unsupported filesystem data and a corrupt published -checkpoint are rejected without conversion or erasure. - -This storage format requires initializing the game-data region. Return the Pokémon -to HGSS before pre-release updates; see the [install guide](../install.md). - -The final public and local-artwork production images each pass all 14 workflows, -including compressed put and the zero-erase reply-window check. Public application -SHA-256: `a8aa2659834cfed7deeb800a95bca5a57ff360ee415897d78377dd4aaa01591b`. -Local application SHA-256: `a5b890cad28ec7f800d98944243e12cb35c987aad983046717734faff57c7ab4`. -Independent negative boots also reject unsupported data, a corrupt published -checkpoint, and a corrupt newer checkpoint even when an older valid save exists; -all leave the data partition unchanged. Source/output hashes and static checks pass. diff --git a/stick/docs/history/power-and-wake.md b/stick/docs/history/power-and-wake.md deleted file mode 100644 index b1a65ec..0000000 --- a/stick/docs/history/power-and-wake.md +++ /dev/null @@ -1,113 +0,0 @@ -# Power and wake trials, September 26-27, 2026 - -This record preserves measured outcomes and failed observations from successive -images. The [current power guide](../power.md) defines the implemented policy. -The early trials used an M/R wake mask and a dark-screen L polling cap; the later -M-only policy superseded both. - -## Initial sleep and wake tests - -Raw artifacts are local under `stick/.build/power-hardware-2026-09-26/`. - -| Artifact / attempt | Outcome and meaning | -| --- | --- | -| `sleep.json`, `sleep-reconnect.json` | USB-connected sleep commands lost subsequent observation. The trials ended without a retrieved sleep count. | -| `startup.json`, startup/boot diagnostic logs | Awake startup/configuration observations; stage reporting introduced after PMIC startup failures. | -| `dark-focused.json` → `dark-recovered.json` | First retained dark observation: 56 returns, 4.956 s in sleep calls. Recovered from the retained trial record. | -| `dark-final.json` | Harness could not retrieve its expected previous record; failed observation. | -| `dark-bus-diagnostic.json` | PMIC startup failure after warm reset; SDA and SCL were both high after the failure. | -| `dark-recovery.json` | Completed approximately 5.093 s dark trial: 56 successful sleep returns, five accelerometer reads, native clock +5 s, zero recorded failures. | -| `lit-recovery.json` | Completed sampling trial but four sleep entry errors and only 17 successful returns / 0.912 s in sleep calls. Failed power-scheduling result; original error codes unavailable. | -| `lit-reject-cause.json` | Completed approximately 5.025 s visible trial: 82 returns, 80 accelerometer reads, clock +5 s, zero recorded failures. | -| `lit-reject-cause-2.json` | USB device disappeared; ROM recovery could not open the missing port. Failed observation, required physical replug. | -| `audio-reconnect.json` | Incomplete initial audio observation; later `audio-final.json` is the completed check. | -| `audio-final.json` | Two cold-supply playback cycles. LCD/audio supply and amp 00→0c→00, codec/I2S stopped after hold, zero begin/power errors. | -| `rx-final.json` | Two-second receiver worker start/stop, 240 MHz startup prerequisite, return to 80 MHz and IR boost off. PMIC first read failed and recovered on retry 1 during receiver startup. The trial exercised start and stop with no peer. | -| `jtag-availability.log` | Espressif OpenOCD examined both cores using built-in USB JTAG; no halt, breakpoint, reset or flash command in that probe. | -| `pre-flash-coredump-analysis.log` | Initial backup already contained a CRC-valid historical dump. Official decoder rejected a mismatched ELF; matching historical ELF not found. | -| `rejection-host-before-fix.log` / `rejection-host-after-fix.log` | Regression exposed the one-second veto; revised scheduler permits ordinary rejection without resetting wake configuration. | -| `vbus-regression-before.log` / `vbus-regression-after.json` | Persistent failed power reads incorrectly allowed sleep before fix. Fifteen host lifecycle cases now pass, including unknown VBUS veto and transient read recovery. | -| `final-host-ir-regression.log` | Three TX lifecycle/symbol tests pass. | -| `usb-veto-final.json` | Final diagnostic build kept sleep count/error count zero during six seconds after Y arm, with measured VBUS 5218 mV. | -| `production-jtag-*.log` | Initial running-memory read was rejected (target not halted). Subsequent dual-core reads were inconsistent with the ELF while CPU1 could not be examined. The installed supported ESP_ONLYCPU=1 configuration returned ready=1, USB veto=1 and the expected application descriptor magic. Brief halt/resume was followed by a fresh standalone reset. | -| `flash-interim-production.log` | A previous production output was flashed while a diagnostic rebuild was still running. The interim image received no tests. Later trials used completed builds, and each flash preserved the storage partition. | - -Sleep-call durations include entry and wake overhead. The PMIC recovered on -retry after a failed read; the records do not locate the electrical fault. - -## Panel and physical-input findings - -The display review established that a command without chip-select ownership -was ignored. RDDPM remained `98` after an unselected display-on command and -changed to `9c` after the selected command. Following restoration, MADCTL was -`b8` and pixel format `05`. - -The wake diagnostic found a pending gesture could inherit the inactive -one-second deadline. Before correction, a generated Center gesture still had -the panel/supply off near 923 ms. After correction, all nine generated cold-wake -cycles had panel, PWM and supply ready near the 0.9-second observation. -A subsequent physical held-M failure showed that generated inputs had not -qualified switch acquisition. - -EXT1 preparation left M/R in RTC IO while polling used digital reads. The -corrected handoff restores digital ownership after every sleep return. The -hardware mux probe reported `before=3 restored=1 after=0` twice. Two timed -physical-button windows captured no M edge. - -`rtc-mux-production-liveness.log` had invalid descriptor `0xbad00bad` and no -clock progression, so its RAM values cannot identify a crash cause. A fresh -USB-UART reset using DTR/RTS false before opening the connection was followed -by valid descriptor `0xabcd5432`, ready=1 and RTC progression over 1.5 seconds. -That reset-capable recovery and its post-reset verification are both retained. -Wake artifacts are under `stick/.build/wake-diagnostic-2026-09-26/`. - -## M-only policy verification - -The September 27 host checks covered eight input configurations, sixteen -runtime/fault cases, fifteen abandoned-wake cases and three codec cases. -The dark-mask/cadence regressions failed on the preceding code and passed -after the M-only change. - -Eight USB-awake generated-input trials passed: two abandoned M taps, two -accepted M holds, two ignored dark L events and two Settings opens after wake. -Accepted wakes reported LCD status `9c`, pixel format `05`, PWM ready and PMIC -output `04`; rejected gestures left output `00` and the panel asleep. - -The first probe failed its obsolete native-Center injection expectation at -0.9 seconds. It was changed to exercise M wake; `hardware-wake-attempt1.json` -and its log remain under `stick/.build/m-only-wake-2026-09-27/`. - -## Installation identities and storage checks - -Each listed image had app-only flash verification and retained sampler machine -bytes. Each storage comparison used that trial's pre-flash backup. - -| Milestone | Production app SHA-256 | Local evidence directory | -| --- | --- | --- | -| Lifecycle | `b94ddf991fb495b0ac01849f855bd4426e69632700b19fd01a9f998723465b68` | `power-hardware-2026-09-26/` | -| Wake deadline | `7778124e9eb41f2a59a766a1360f1a6e1575055340c98b6bcdcd2e39aae2f7a7` | `wake-diagnostic-2026-09-26/` | -| RTC GPIO handoff | `12fa6823643b6d62d60242b2cb8fbc04deb877d41c0c274593c2348e8ed2f98b` | `wake-diagnostic-2026-09-26/` | -| Panel restoration | `a782fd21b5aa5380481b4412406948faabbba09808b0ef44137c6bb859cf3024` | `wake-diagnostic-2026-09-26/` | -| Abandoned wake cleanup | `4fb5efcec62ace583f9b19ec46e35b0f677c2f39314f27a437725de8cde56398` | `abandoned-wake-2026-09-27/` | -| M-only policy | `5e29d017a468958a07f74da98e4d60602f1e8a28530a742ee5f1bd81631b6c43` | `m-only-wake-2026-09-27/` | - -The lifecycle trial's independently reconstructed 65,536-byte EEPROM hash -remained `324c0e5d46de68c861195b71132179cc1fed7eea94c02918d6aa7717d9f11885`. -Boot checkpointed six valid journal records into snapshot generation 566; -physical filesystem bytes changed but logical EEPROM did not. - -The panel-restoration block retained an initial failed whole-filesystem equality -check in `display-production-storage-verify.log`. Independent reconstruction -found course resources, status/device IDs, paired Pokémon and save checksums -preserved. Logical EEPROM did change: the clock advanced 23 seconds, diary -cursor moved from 21 to 22, and entry 21 recorded native boredom action `0x15` -after a generated Center gesture accepted it. Changes were confined to that -136-byte diary entry and the two 24-byte save payloads/checksums. Steps, Watts -and Pokémon-minute counters were unchanged. The resulting filesystem then -matched a fresh snapshot. `display-save-comparison.json` preserves both results. - -The later abandoned-wake and M-only trials preserved logical EEPROM hash -`671d392987cf62c9dd845c62e4c2ae13cc679dc828f788c5af44e047701120d5`. -Checkpointing moved generation 568 to 569 with zero changed logical addresses. -The M-only production flash also matched its complete pre-production storage -partition; CPU0 readback found ready=1 and advancing RTC after resume. diff --git a/stick/docs/history/transfers.md b/stick/docs/history/transfers.md deleted file mode 100644 index 6aac8bc..0000000 --- a/stick/docs/history/transfers.md +++ /dev/null @@ -1,92 +0,0 @@ -# Transfer trials - -These trials used a retail HeartGold Game Card and a 3DS test peer running the -HGSS communication protocol with generated course data. Raw traces and EEPROM -readbacks are kept under `stick/.build/trials/`. - -## Recorded operations - -- A source-faithful 3DS entry completed all 368 pages with 374 valid Stick - receives and no invalid packets in - `stick/.build/trials/port-hgss-real-image-atomic-complete/`. Independent - EEPROM readback matched the HGSS source-built image and the transmitted - course byte for byte. The course and record in that trial were sealed random - payloads used to check transport and storage. A frozen `.137` - app image remains at `../bench/experiments/g5-terminal-start-137/app.bin`; - its full pre-port flash backup is also retained under `stick/.build/`. -- A real HeartGold Game Card completed a walk-start transfer with the Stick. - Its optical UART clock measured about 2.9855 gates/cell, outside the old - 3.0000-3.0080 fit range learned from the 3DS bench transmitter. A wider - full fit recovered its packets but took 132-149 ms, exceeding both the - game's 100 ms reply timeout and the port's roughly 98 ms inactivity check. - The receiver now searches the full start-phase range around the current - burst's or a previous credible long burst's baud estimate, and computes - the expensive phase score only for competitive fits. This uses UART framing - only; the original protocol still checks commands, tokens, and checksums. - The successful real-card trace recorded 374/374 valid optical bursts, - no rejected burst or checksum failure, and a normal completion. Its first - 32 retained burst diagnostics all used the fast fit; maximum recorded decode time - was 30.9 ms and maximum recorded reply-start time was 31.7 ms. A 65,536-byte - EEPROM readback after transfer had matching status and save mirrors and - clear commit markers. After reboot, the received course and staged-course - regions remained exact; only mirrored save counters changed. The validated - bench app hash is `56b27c350da65bde2efcd855709cfcbb87519a31f482f08a6ff4f69482f9a7c2`. - The cleaned bench app had the same sampler machine bytes; its hash is - `6e38abbed86906bb4d98d2ec3ecbe954f159a366d006929bff53df1b707bbc35`. - The same source was then compiled without `PW_STICK_BENCH_CONTROL` and - flashed as an app-only update. The diagnostic-free image hash is - `317943ba93b54810ea5be57fc936574f70bf403b63a1196458b4b174b59129d9`. - Flash verification passed, and the user reported that a normal HeartGold - connection on this production image worked. This second real-card success - is user-observed; the production image has no per-burst serial trace. -- A source-faithful registered-walker `back` transaction on the integrated - firmware received 90 valid bursts, ended with the original HGSS DONE result, - and cleared the saved Pokémon flag while retaining registration. An `entry` - request on that already registered walker was correctly rejected by HGSS. - The bench artifacts are under `stick/.build/trials/journal-hgss-005-back/`. -- Two subsequent source-faithful `put` transactions and an intervening `back` - completed on the same Stick boot (`journal-hgss-006-put` through - `journal-hgss-008-put`). The two `put` runs each received 133 valid bursts - without an invalid packet, and the intervening `back` received 90 valid - bursts without an invalid packet. Each operation reached the original PHC - DONE state. Independent 64 KiB EEPROM readback after both `put` runs matched - every byte of the 10,430-byte course fixture, with both status copies and - checksums valid and the Pokémon flag set. The fixture comes from HGSS's - first-course table, compressed artwork, and course background. It chooses a - plausible starter and renders English labels because no trainer save or DS - message renderer is running on this bench; `hgss_course_fixture.py` records - these choices. The 3DS still executes the original PHC protocol engine. -- A further same-boot diagnostic series completed ten `back`/`put` cycles: - 20/20 original PHC operations reached DONE, with 90/90 valid packets in - each `back` and 133/133 in each `put`. The ledger and independent final - EEPROM verification are in `stick/.build/trials/opening-repeat-ledger.json` - and `opening-repeat-final-verification.json`. The first attempted repeat - before this series failed on `put` page 59: the source 3DS transmitted wire - `AA` in the page's first byte, the Stick decoded `AB`, the source checksum - rejected it, no ACK was sent, and both ends timed out. That trial is retained - as `journal-repeat-01-put`. - A bench-only 96-gate opening snapshot now prints when a checksum fails so - the next occurrence can distinguish weak/missing optical observation from - phase assignment. The flashed diagnostic app SHA-256 is - `b8ec2c57cf16cf8834e9f1816adb95d2666ba6542ee4dad7ada4b724a9325dd9`. -- A longer frozen-image diagnostic recorded 44 successful whole operations - before `cycle-023-back` failed. The source 3DS sent a page control packet - beginning `A6`; the Stick decoded `A7`, the source checksum rejected it, - and the peer timed out. Two release-late gate sentinels occur near that - packet's opening UART cells. The artifact retains the full trace and the - opening gate snapshot under `stick/.build/trials/opening-long-001/`. - The sentinel encoding overwrites any measured crossing on those gates, - so the capture does not distinguish a missed optical pulse from a late - sample. All 45 attempts remain in the ledger. - -## After initial power changes - -The first power candidate completed 20 same-boot operations with exact course -readback. After wake corrections, a later candidate completed a fresh `back` -and `put`, then 10/10 further operations with valid status mirrors and exact -course readback. Artifacts are under `power-001-repeat/` and `power-004-repeat/`. -The initial Light-sleep diagnostic also completed a `back` and `put` with -90/90 and 133/133 valid receives and all 10,430 course bytes matching. - -These runs qualify their own images. See the [validation summary](../validation-history.md) -and [release checklist](../release.md) for the remaining production trials. diff --git a/stick/docs/images/three-key-settings.png b/stick/docs/images/three-key-settings.png deleted file mode 100644 index 2590f0d..0000000 Binary files a/stick/docs/images/three-key-settings.png and /dev/null differ diff --git a/stick/docs/install.md b/stick/docs/install.md index 8a662ed..d8ee6ba 100644 --- a/stick/docs/install.md +++ b/stick/docs/install.md @@ -1,120 +1,42 @@ -# Installing and updating Stickwalker +# Install Stickwalker -Installing Stickwalker replaces the firmware currently on your Stick S3. -Back it up first so you can restore it if needed. +Download and unpack the firmware ZIP from [Releases](https://github.com/lumirth/stickwalker/releases). Run the commands below from the unpacked folder. They use [uv](https://docs.astral.sh/uv/getting-started/installation/) to run esptool. -Download the firmware ZIP from the -[Releases](https://github.com/lumirth/stickwalker/releases) -and unpack it. Check its SHA-256 against the release's `SHA256SUMS`, using -`shasum -a 256` on macOS, `sha256sum` on Linux or `Get-FileHash` in PowerShell. -You can also [build the firmware yourself](build.md). -Use the M5StickS3 USB-C port with a data cable. On macOS, the serial port usually -appears as `/dev/cu.usbmodem*`; on Linux, `/dev/ttyACM*`; on Windows, check the -COM port in Device Manager. Compare the device list before and after connecting -if several ports are present. +Connect the Stick S3 with a USB-C data cable. Replace `PORT` with its serial port, usually `/dev/cu.usbmodem*` on macOS, `/dev/ttyACM*` on Linux or a `COM` port on Windows. -Replace `PORT` in the commands below with that port. Run them from the firmware -output directory. [uv](https://docs.astral.sh/uv/) runs the pinned esptool version; -esptool connects to and resets the board. +## Photodiode -## Back up before writing +The Stick's built-in IR LED transmits. Add a BPV10NF to receive. -Save the full 8 MiB flash somewhere outside the firmware output directory: - -```sh -uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT read-flash 0 0x800000 /path/to/stickwalker-before.bin -``` - -Keep this backup private: it contains registration, Pokémon, game data and device -settings. Check that it is exactly 8,388,608 bytes before proceeding. If the -board won't connect, enter its USB download mode using M5Stack's -[StickS3 instructions](https://docs.m5stack.com/en/core/StickS3). - -## Update a pre-release installation +| BPV10NF | Stick S3 | +| --- | --- | +| Anode | GND | +| Cathode | G5 | -Return your Pokémon to HGSS before updating. Pre-release builds do not promise -save compatibility. Keep the flash backup, then initialize the game-data region -for the new firmware. This resets registration, steps, Watts, items and other -walker progress; the Pokémon is already safe in your game. Device settings in -NVS and resident artwork are separate from this region. +Use the [package drawing](https://www.vishay.com/docs/81503/bpv10nf.pdf#page=4) to identify the leads. -Check that the installed partition layout matches the new build: +## Back up ```sh -uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT read-flash 0x8000 0xC00 /path/to/installed-partitions.bin +uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT read-flash 0 0x800000 stickwalker-backup.bin ``` -Compare those bytes with `PwStick.ino.partitions.bin`, using `cmp` on macOS/Linux -or `fc /b` on Windows. For the matching app0 layout used by this guide: +## Update + +Return your Pokémon to HGSS first. Pre-release updates reset registration and walker progress. Button layout, rotation and appearance stay saved. ```sh uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT --after no-reset erase-region 0x670000 0x180000 uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT write-flash 0x10000 PwStick.ino.bin -uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT verify-flash 0x10000 PwStick.ino.bin ``` -The backend supports only the current flash-record format. It starts a new -walker on blank storage; unsupported or damaged storage reports a fault rather -than converting an old save. After installation, connect HGSS to start a stroll. - -Use these commands for updates. Arduino's Upload command also rewrites the -bootloader, partition table and boot selection. If the installed layout differs -or app1 is active, inspect that installation before choosing an application -address. These instructions use app0 at `0x10000`. - ## First installation -After making a backup, initialize the region where Stickwalker will store its -game data, then install the firmware. This starts a new walker. - -```sh -uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT erase-region 0x670000 0x180000 -``` - -Install the bootloader, partition table, boot selection and application: - ```sh +uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT --after no-reset erase-region 0x670000 0x180000 uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT write-flash 0x0 PwStick.ino.bootloader.bin 0x8000 PwStick.ino.partitions.bin 0xE000 boot_app0.bin 0x10000 PwStick.ino.bin ``` -The selected layout has app0 at `0x10000` (size `0x330000`), app1 at `0x340000`, -game-data storage at `0x670000` (size `0x180000`), and coredump at `0x7F0000`. -NVS starts at `0x9000`. On first boot, Stickwalker publishes its initial -checkpoint in the erased game-data region. - -After boot, check the display and controls, open Settings and test sound. -Confirm the device settings, then connect HGSS to register the new walker. -Unplug USB, let the screen go dark, and hold M until it wakes, roughly half a -second. You can now -[connect to your game and take a Pokémon for a walk](playing.md). The -[controls guide](controls.md) covers button layouts and device settings. - -## Infrared receiver - -Stickwalker uses the Stick S3's built-in infrared LED to transmit and a -**BPV10NF photodiode** on the expansion header to receive. Connect the photodiode -before trying a Game Card transfer or peer play. - -Turn the Stick off and disconnect USB while fitting it: - -| BPV10NF lead | Stick S3 header pin | -| --- | --- | -| Anode (A) | **GND** | -| Cathode (C) | **G5** (GPIO5) | - -G5 and GND are adjacent on the header. Identify the leads using the -[BPV10NF package drawing](https://www.vishay.com/docs/81503/bpv10nf.pdf#page=4), -especially if they have already been trimmed. Connect the bare photodiode -directly between those two pins. Keep the leads from touching each other or -neighboring pins. Use a connector or mount that holds both contacts firmly; -the connection needs to stay steady while you carry the Stick and during transfers. - -Point the photodiode's rounded face and the Stick's infrared transmitter toward -the Game Card's infrared window or the other walker's infrared window. Both -need a clear line of sight. If a transfer times out, check the two connections -and polarity, then adjust alignment and separation while keeping the devices -still. +[Stick controls](controls.md). -If transfers work intermittently or report a disrupted connection, turn the -Stick off, disconnect USB and reseat the photodiode. Once both contacts are -secure, turn it on and retry the transfer. +If esptool cannot connect, use M5Stack's [USB download-mode instructions](https://docs.m5stack.com/en/core/StickS3). diff --git a/stick/docs/motion.md b/stick/docs/motion.md deleted file mode 100644 index 704ddbc..0000000 --- a/stick/docs/motion.md +++ /dev/null @@ -1,29 +0,0 @@ -# How Stickwalker counts steps - -Stickwalker runs the Pokéwalker's original step-counting algorithm. The Stick's -accelerometer measures movement, and the game code decides which movement -counts as walking and how many steps to award. - -The Pokéwalker uses a Bosch BMA150 accelerometer. The Stick S3 uses a -[Bosch BMI270](https://www.bosch-sensortec.com/en/products/motion-sensors/imus/bmi270), -a newer sensor designed for wearables. Stickwalker reads all three axes through -M5Unified and converts them to the signed eight-bit values used by the original -firmware. One g becomes 64 counts, matching the BMA150's ±2 g output after the -original firmware takes its high byte. Values beyond that range saturate. -Between fresh sensor readings, the adapter keeps the previous sample. - -The game collects 64 samples at its original cadence, then uses a fixed-point -Fourier transform to find repeated movement across the three axes. It selects -a walking frequency and carries fractional steps into the next batch. The -original pacing logic adds steps to the daily and lifetime totals and awards -Watts. - -Matching the input scale and timing lets the original algorithm work with the -new sensor, so Stickwalker should count steps much like a Pokéwalker. As with -the original device, how you carry it affects the motion it measures. - -For the sensor conversion, see [`motion_sample.cpp`](../motion_sample.cpp) and -[`accel_bridge.cpp`](../accel_bridge.cpp). The step algorithm is in -[`pw_fourier.c`](../../src/application/pw_fourier.c) and -[`pw_pedometer.c`](../../src/application/pw_pedometer.c). -[`pw_power.c`](../../src/application/pw_power.c) controls the sampling schedule. diff --git a/stick/docs/native-platform.md b/stick/docs/native-platform.md deleted file mode 100644 index 5ef3a9c..0000000 --- a/stick/docs/native-platform.md +++ /dev/null @@ -1,142 +0,0 @@ -# Native execution and Stick services - -The behavioral reference is the [matching Pokéwalker reconstruction](https://github.com/lumirth/pw/tree/6dc7bc09950078fa3fe0dffa4dae34e9549a99da). -Both supported H8 compiler suites must continue producing the reference image. -Stick-specific branches adapt hardware and interaction; they do not replace -native game rules, rendering, score interpretation, or motion estimation. -DS-side byte order and completion behavior were also checked against locally -supplied HGSS source. That source and its artwork are not distributed here. - -## Execution ownership - -`native_runtime.cpp` owns Timer B1, Timer W, RTC events, Center IRQ, interrupt -masks, and foreground wake eligibility. Timer W deadlines retain 32768 Hz -watch-clock precision. The native score interpreter runs from compare events, -not the physical speaker service or the former fixed 8 ms BeepTick scheduler. - -RTC time-of-day advances independently of interrupt delivery and the native -saved-seconds counter. Pending RTC sources coalesce while masked; flag state is -separate from interrupt enable. Interrupt effects delivered during foreground -execution do not themselves request another foreground run. Timer B1 follows -native motion transitions, not an input queue's desire for faster consumption. - -The application task owns native state. Interrupt service occurs at runtime and -adapter boundaries, including LCD writes; output workers do not call game -updates or mutate the motion/sound/IR workspace. This is a source-level native -platform implementation, not instruction-accurate H8 CPU emulation. - -`board_runtime.cpp` composes services and chooses the next deadline. Settings -owns its overlay in `board_settings.cpp`; diagnostic commands live in -`board_diagnostics.cpp` and compile out of production. Native renderers still -advance only when the firmware consumes a refresh request. - -## Inputs and interaction - -Physical acquisition and Settings share debounce state. PMIC L supplies a -current level and retained press; configuration/read failures are retried. -The four current layout encodings remain unchanged. Older configuration keys are ignored. - -The delivery queue timestamps transitions. Disjoint buttons may omit a redundant -neutral state, but repeated presses of one button require a delivered release. -Center IRQ becomes eligible only at the delivery front, so an older direction -cannot merge with a later confirmation. Pending transitions older than 250 ms -or a full queue cancel the gesture and require physical release. Diagnostic -counters expose both cases; native scans are not manufactured to drain a queue. - -While dark, M presents native Center activity. Native hold qualification starts -interactive mode and requests the original social-offer check. Panel and audio -preparation start earlier without claiming the native score workspace. Wake, -overlay, and IR transitions consume their physical gesture through release. -Opening Settings cancels an undelivered Center latch. Closing it resumes sampling -and visibility without pretending another native Center wake occurred. - -Two-key layouts retain dedicated L access to Stick Settings. Three-key layouts -observe a one-second physical L+R hold alongside normal directional delivery. -Recognition opens the overlay and consumes its gesture through release; pending -holds reserve no input. The native Settings selector retains Volume and Contrast. -Settings captures buttons, but native time, motion and eligible foreground work -continue; it is not a game pause. - -## Output and power - -The LCD bus publishes a revision for visible RAM, start-line and power changes. -The presenter consumes an immutable 96-by-64 snapshot, not `uiFrame` or live game -state. Each call transfers at most eight native rows, returns with SPI idle, and -retains no caller pointer. A newer snapshot can supersede remaining strips. -Appearance, rotation and overlay invalidation also require presentation even -without a new native LCD revision. - -Codec/I2S preparation is independent of Timer W. Output remains prepared during -interactive use and is suspended for screen-off carry and IR. Readiness means -configuration succeeded, not that acoustic settling was measured. The existing -single-voice waveform, codec readback, rail-generation handling and teardown -protections remain. Digital PCM checks do not establish analog onset or power. - -Sleep deadlines include native events, required input polling, Settings -inactivity, and display/audio/supply recovery. Quiet dark M-only input uses GPIO -wake; when CPU sleep is unavailable, polling still detects M. Digital GPIO -ownership is restored after every sleep attempt. Powered I2S is an actual sleep -constraint, distinct from native score ownership. - -## Infrared handoff - -Native framing, tokens, checksums, commands and completion actions are retained. -Optical acquisition retains its dedicated implementation and 240 MHz requirement. -Audio is quiesced and the prepared Connect image presented before acquisition. - -A completed non-self burst hands execution to the protocol with the receiver -core's interrupts restored. The worker waits while decoding and bounded storage -work complete. Consumed-event tickets prevent lost or premature release. Startup -and transmission wait for fresh acquisition progress before emitting a bit; -that preparation is not tagged as self-echo. The gate producer ends at a complete -non-self burst. Notification and release waiting run in the worker with -interrupts restored, outside the gate function; readiness uses its existing -published sample count. Ignored or rejected input releases -reception when no native packet remains. Stop wakes a waiting receiver and waits -for completion and suspension before deleting its task. - -Native-requested bank changes can be presented in bounded strips in the -receive-to-reply window. Acquisition coordination changed and must be physically -requalified: simulator success is not proof of unchanged optical timing. - -## Persistence - -The native 64 KiB EEPROM address space, mixed-endian records, mirrors and staging -regions remain intact. The status path explicitly writes big-endian Watts, as -required by the DS reader. - -The backend has one flash-record format, with two alternating 64 KiB -checkpoints and a bounded append log in the existing game-data partition. -Generation and sequence numbers prevent a covered journal from overriding a -published checkpoint. Native records retain their mixed byte order and mirrors. - -A record programs its body and checked header into pre-erased space, reads both -back independently, and programs its publication word last. Recovery applies only -published records with valid CRCs and consecutive sequences. An unpublished tail -is ignored. Published corruption fails closed. Inactive checkpoints are retired -before replacement; journal erasure follows publication of the covering checkpoint. - -Journal capacity is prepared before acquisition. During IR, append operations -perform only bounded programming and readback; flash erasure is prohibited by the -storage module. Checkpoint replacement and clearing dirty journal sectors happen -outside the session. The decoder's exact search pruning retains reply-time -headroom; see the [IR regression investigation](history/ir-readiness-regression.md). - -The filesystem, legacy save readers, migration and old control-layout conversion -are removed. Unsupported game data is not interpreted or erased automatically. -Pre-release upgrades require returning the Pokémon to HGSS, backing up, and -initializing the game-data partition as described in the [install guide](install.md). -Board settings remain separately stored in NVS. - -The DS's Back and present-collection paths do not wait for a final deletion -acknowledgment. No new handshake or claim of two-device atomicity is introduced. -Native staging and completion order retain their existing recovery limits. - -## Qualification - -Run the [production firmware workflows](../simulation/README.md), native-source -formatting/static analysis, and exact H8 matching under both supported compilers. -Public EEPROM fixtures exercise normal startup and persisted flash, not game -memory hooks. Physical controls, acoustics, walking comparisons, current draw, -flash interruption, and real peer/Game Card exchanges need hardware qualification -on the exact candidate. diff --git a/stick/docs/playing.md b/stick/docs/playing.md deleted file mode 100644 index 10a065e..0000000 --- a/stick/docs/playing.md +++ /dev/null @@ -1,65 +0,0 @@ -# Take your Pokémon for a walk - -Choose a Pokémon and a route in HeartGold or SoulSilver, transfer them to the -Stick, and take it with you. Walking earns Watts, which you can spend searching -for Pokémon and items along the route. - -## Connect to your game - -Start with [Stickwalker installed and the photodiode connected](install.md). -Use an infrared-equipped HeartGold or SoulSilver Game Card and a console that -can run it. Have a Pokémon in a PC Box and at least five free PC Box spaces. -If a Pokémon from this save is out on another walker, return it first. - -1. Start the game and choose **Connect to Pokéwalker** from the main menu, then - **Go for a Stroll**. -2. Choose a Pokémon from your PC Boxes and a route. -3. On the Stick, choose **Connect**. With the default buttons, L moves left, - R moves right, and M confirms. -4. Aim the Stick's infrared transmitter and photodiode at the Game Card's - infrared window. Hold them still until both sides finish. - -Your Pokémon will appear on the Stick with the chosen route. The transfer also -sets its clock and registers it with your game. Nintendo's -[connection instructions](https://en-americas-support.nintendo.com/app/answers/detail/a_id/7374/p/50/c/120) -show the game-side prompts. - -## Walk and play - -Carry the Stick while you walk. It counts steps with the screen off; hold M for -half a second to wake it, then release the buttons to navigate. Open **Poké -Radar** to search for Pokémon or **Dowsing Machine** to search for items. Both -use the same rules and Watts costs as the original Pokéwalker. - -The [controls guide](controls.md) covers button layouts, screen rotation, -appearance and sound settings. Your Pokémon and progress are saved across -restarts. - -## Bring your Pokémon and finds back - -Open **Connect to Pokéwalker** in the game again. Choose **Return from a Stroll** -to bring your walking Pokémon back, or **Receive a gift** to collect your finds -while leaving that Pokémon on the Stick. Choose **Connect** on the Stick and -align its transmitter and photodiode with the Game Card again. - -Return your Pokémon to the same game it came from. The -[HeartGold manual](https://csassets.nintendo.com/noaext/image/private/t_KA_PDF/DS_Pokemon_HeartGold) -explains both transfer options. - -## Connect with another walker - -With a Pokémon on each walker, choose **Connect** on both and aim their infrared -transmitters and receivers at each other. Peer play records the encounter and -awards a gift. A repeat connection on the same day reports that you have already -connected. You can connect again after the walker's next day rollover. - -## Connection trouble - -For a timeout, check the [photodiode wiring](install.md#infrared-receiver), then -adjust alignment and distance. Keep both the Stick's transmitter and its -photodiode pointed at the other device throughout the transfer. - -If an older Stickwalker says "cannot connect again" with a walker it has never -met, return its Pokémon to HGSS, then -[update its firmware and initialize game data](install.md#update-a-pre-release-installation). -Update both Sticks if both run older firmware. diff --git a/stick/docs/pmic-input.md b/stick/docs/pmic-input.md deleted file mode 100644 index 5d4f170..0000000 --- a/stick/docs/pmic-input.md +++ /dev/null @@ -1,46 +0,0 @@ -# Reading the L button - -The Stick's L button connects to the M5PM1 power-management chip. Stickwalker -reads the chip's button status over I2C. M and R connect directly to ESP32 GPIOs. - -## Status and click timing - -| Register | Field | Use | -| --- | --- | --- | -| `BTN_Status`, `0x48` | Bit 0 | Current pressed or released state | -| `BTN_Status`, `0x48` | Bit 7 | A press occurred; reading the register clears it | -| `BTN_CFG_1`, `0x49` | Bits 2:1, SINGLE | Classified single-click timing: 125, 250, 500 or 1000 ms | - -Stickwalker reads both status bits together. The press latch catches taps that -end between polls; the current-state bit supports holding L. SINGLE controls -the PMIC's classified-click behavior, which is separate from these raw reads. -The documented register default is `0x2A`, selecting 250 ms. See the -[M5PM1 datasheet, pp. 23–24](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/M5PM1_Datasheet_EN.pdf#page=25) -and [register definitions](https://github.com/m5stack/M5PM1/blob/be9a5456c007c333e7ac963f33bfde1ffa5d82ee/src/M5PM1.h). - -## Polling and release - -Awake input polls run every 5 ms. During visible operation, sleep lasts at most -100 ms, and the native 62.5 ms work deadline usually wakes the CPU sooner. After -an L gesture, 50 ms of released state rearms it. This prevents one press from -opening and immediately closing Settings. - -With the screen off, M wakes the Stick and L polling stops. After wake, the -adapter clears any retained L press and waits for button release before -resuming navigation. See [`input_bridge.cpp`](../input_bridge.cpp) and -[`power_sleep.cpp`](../power_sleep.cpp). - -## Why the port polls - -The PMIC's IRQ output connects to GPIO13. Its documented button events are -classified single-click, double-click and power-on events. Polling `0x48` gives -the port the pressed state and short-tap latch it needs for navigation and held-L -Settings access. - -The IRQ function also flashes the PMIC's status LED when button reset or -power-off is disabled, as they are in Stickwalker. The current setup keeps that -IRQ output disabled. An IRQ-based implementation would need to preserve both -press/release handling and LED behavior. See the -[board wiring](https://docs.m5stack.com/en/arduino/m5sticks3/m5pm1), -[datasheet, pp. 21–27](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/M5PM1_Datasheet_EN.pdf#page=23) -and [`board_hal.cpp`](../board_hal.cpp). diff --git a/stick/docs/power.md b/stick/docs/power.md deleted file mode 100644 index 840dbaa..0000000 --- a/stick/docs/power.md +++ /dev/null @@ -1,45 +0,0 @@ -# Power management - -Stickwalker sleeps the ESP32-S3 between game tasks when running on battery. -USB power keeps the CPU awake so the serial and debugger connections remain -available. - -## Sampling and buttons - -The original game checks for activity at 1 Hz and samples walking at 16 Hz. -Stickwalker processes each sample as it arrives. Sound and infrared pause motion -sampling while they use the shared working memory; menus and games continue to -sample. The [motion scheduling decision](../../docs/adr/0001-preserve-native-motion-ownership.md) -explains why. - -Required awake button polls run every 5 ms. While the game or Settings is visible, -that acquisition deadline participates in CPU sleep decisions, including L's -retained PMIC press. Delivery does not change the native sampling clock. - -With the screen off, holding M through native Center qualification wakes it. Releasing M early cancels -panel preparation. After wake, the adapter clears L's retained press and waits -for all buttons to be released before navigation resumes. See the -[wake policy](../../docs/adr/0002-m-only-dark-wake.md) and -[L button reference](pmic-input.md). - -## Peripherals - -`board_hal.cpp` configures the PMIC. The display, sound and infrared adapters -request their supplies through `peripheral_power.cpp`, which switches a shared -supply off after its last user releases it. - -`power_sleep.cpp` sets the wake sources and restores M and R to digital GPIO -after each sleep attempt, including rejected entries. Configuration failures -keep the CPU awake until a scheduled retry succeeds. - -## Measuring power use - -Settings estimates charge percentage from battery voltage. To measure battery -life, record current at the battery connection during stationary carry, -walking and screen-on use. Include time spent with the screen lit, sound, -infrared transfers and transient peaks. This measures the whole device, -including the PMIC, sensors and peripheral supplies. - -The [device test records](history/power-and-wake.md) contain sleep and wake -results. The [release checklist](release.md#device-checks-for-a-stable-release) -lists the battery tests for a release. diff --git a/stick/docs/release.md b/stick/docs/release.md deleted file mode 100644 index 4283605..0000000 --- a/stick/docs/release.md +++ /dev/null @@ -1,117 +0,0 @@ -# Preparing a release - -A release contains installable firmware, source, runtime dependencies and a test -record identifying the firmware tested. Use a prerelease for early testing; -reserve a stable release for an image that completes the device checks below. - -## Build and check - -From the intended checkout, run: - -```sh -uv sync --locked -uv run python stick/build_port.py --output-dir stick/.build/candidate --retain-build -uv run python stick/check.py --use-build --build-dir stick/.build/candidate -uv run python stick/check_timing.py --elf stick/.build/candidate/PwStick.ino.elf --tool-prefix /path/to/xtensa-esp32s3-elf- --output stick/.build/candidate/ir-timing.json -``` - -The [build guide](build.md) covers Arduino setup and pinned versions. The -`--retain-build` option saves the objects and link command used by the candidate. -Find `xtensa-esp32s3-elf-` under the Arduino data directory at -`packages/m5stack/tools/esp-x32/2601/bin/`. - -The timing check compares `measure_ir_gate`, `aligned_next` and `run_gate_segment` -with the production reference. The report records the new ELF hash and whether -each function's instruction bytes match. - -The native-platform redesign changes the receiver handoff and its gate-loop -machine code. The frozen reference is intentionally unchanged. A differing -report is a physical requalification requirement, not permission to replace the -reference with the candidate's hashes. Simulator protocol checks alone cannot -qualify this candidate for a stable release. - -## Assemble the runtime kit - -Use the source archives and `sources.json` from the previous release's runtime -kit, or obtain the pinned archives recorded in -[`runtime-sources.json`](../runtime-sources.json). Keep their hashes and original -notices. The ESP-IDF archive contains commit -`735507283d5b2f9fb363a1901172dbd9e847945d` and the recursive submodule revisions -recorded in that inventory, with Git metadata excluded. - -Pass the installed SDK, compiler and dependency source directory: - -```sh -uv run python stick/package_runtime.py --build-dir stick/.build/candidate --sdk /path/to/packages/m5stack/tools/esp32s3-libs/3.3.9 --toolchain /path/to/packages/m5stack/tools/esp-x32/2601 --sources-dir /path/to/dependency-sources --output-dir stick/.build/runtime -``` - -This retains application/library objects, the Arduino core archive, referenced -SDK archives, linker scripts, build configuration, dependency sources and -notices. It creates a portable relinking recipe and verifies that it reproduces -the candidate ELF byte for byte. The [relinking guide](relink.md) explains how -to use it with a modified Arduino runtime. - -## Package the release - -Commit the final source and documentation, then run from that clean checkout: - -```sh -uv run python stick/package_release.py --build-dir stick/.build/candidate --runtime-dir stick/.build/runtime --output-dir stick/.build/release --allow-ir-timing-change -``` - -Packaging verifies the source inventory, firmware hashes, production mode, -placeholder artwork, absence of diagnostic markers, infrared timing report and -runtime kit. It produces three archives and `SHA256SUMS`: - -`--allow-ir-timing-change` permits a prerelease with changed IR instruction bytes; -the original timing report is bundled and the manifest remains unqualified. -Omit the flag to require the frozen machine-byte reference. Neither mode replaces -physical qualification for a stable release. - -- The firmware ZIP contains the binaries, symbols, installation and player - guides, manifests, timing result and dependency notices. -- The source archive contains the committed code and documentation. -- The runtime archive contains dependency sources and the relinking kit. - -The firmware manifest records `hardware_qualified: false`. A separate -qualification record identifies the commit, app and ELF hashes and the tests -completed for that image. [Validation history](validation-history.md) provides -the previous device results. - -## Device checks for a stable release - -Run these checks on the firmware you intend to publish. Record the setup, -observations and failures. - -1. [Back up and install](install.md) the candidate. Read back the app and compare - its hash. Return the Pokémon to HGSS before pre-release updates, initialize - game data, and confirm device settings, new registration and valid EEPROM - mirrors. -2. Disconnect USB. Exercise native M-hold wake in every layout and rotation, - ignored dark L/R, abandoned short M taps, navigation after release, Settings, - display restoration, short sounds and clock progression. -3. Repeat retail walk-start and return transfers on that image. Record - whole-operation successes and failures, game/version, distance, orientation, - elapsed time and EEPROM readback. Test both HeartGold and SoulSilver. -4. Compare walking and stationary behavior with actual steps and, where - available, an original Pokéwalker. Measure whole-device battery current with - USB disconnected. Record the time spent walking, idle and using the screen. -5. Interrupt EEPROM journal/image writes on hardware and verify recovery - without losing the last completed save. -6. With the [photodiode connected](install.md#infrared-receiver), peer-connect - with an original Pokéwalker and with another Stickwalker. Check first contact, - same-day repeat rejection, a different peer and reconnection after day - rollover. Record each device's firmware and game language. - -Use the [debugging guide](debugging.md) for retained diagnostics and USB/JTAG -procedures. - -## Publish on GitHub - -Run the Linux and macOS CI jobs for the final commit. Check unpacked archives, -installation links and checksums, including a relink from the unpacked runtime -archive. Attach all three archives, checksums and the qualification record. - -Write release notes around what changed and how to update. Link the installation -and controls guides. List the tests completed on this build and the device -checks still to run. Publish as a prerelease until the stable-release checks pass. diff --git a/stick/docs/relink.md b/stick/docs/relink.md index 75e6f9b..d67f4d8 100644 --- a/stick/docs/relink.md +++ b/stick/docs/relink.md @@ -1,63 +1,17 @@ -# Relinking the firmware +# Relink a runtime kit -Use the runtime kit to rebuild a release ELF or replace the libraries linked -into it. For ordinary installation, download the firmware ZIP and follow the -[install guide](install.md). - -The kit contains the release's application and library objects, Arduino core -archive, linked ESP32-S3 SDK archives, linker scripts and dependency source -archives. `sources.json` records their origins and hashes. Licenses and notices -are retained under `licenses/` and in the source archives. - -## Reproduce the release ELF - -Install the **esp-x32 2601** toolchain through M5Stack's **3.3.9** Arduino board -package. Pass its directory containing `bin/` to the relinker: +Unpack the runtime archive and run this command from its directory. Use the esp-x32 2601 toolchain supplied by M5Stack's 3.3.9 board package. ```sh python3 relink.py --toolchain /path/to/packages/m5stack/tools/esp-x32/2601 ``` -Run this command from the unpacked kit, using Python 3.12 or later. It writes -`output/PwStick.ino.elf` and a linker map. Compare the ELF's SHA-256 with -`runtime-manifest.json`. The release packager verifies that the supplied objects -reproduce the original ELF byte for byte. - -## Use a modified Arduino runtime - -Unpack `sources/m5stack-3.3.9.zip`. It contains the board configuration, Arduino -core, bundled libraries and build recipes used by the release. Modify that -source in your Arduino package installation, keeping the release's board and -library versions. Build the accompanying Stickwalker source with -`stick/build_port.py --retain-build`, following its build guide. - -To link the original application objects with your rebuilt core: +This writes `output/PwStick.ino.elf`. Its SHA-256 should match `runtime-manifest.json`. -```sh -python3 relink.py --toolchain /path/to/esp-x32/2601 --core /path/to/new-build/relink-build/core/core.a -``` - -For changes to Arduino's bundled FS, LittleFS or Preferences libraries, replace -the corresponding files in `objects/libraries/` with those from the new build. -The same approach applies to M5 library objects. `link.json` records the ordered -link arguments; SDK archives and scripts are under `sdk/`. +To use a rebuilt Arduino core, add `--core /path/to/core.a`. -Convert a relinked ELF into an ESP32-S3 flash image with the pinned esptool: +To create a flash image: ```sh uvx --from esptool==5.3.0 esptool --chip esp32s3 elf2image --flash-mode dio --flash-freq 80m --flash-size 8MB --elf-sha256-offset 0xb0 -o output/PwStick.ino.bin output/PwStick.ino.elf ``` - -After changing the runtime, run the [infrared timing check and device tests](release.md) -on the resulting firmware. - -## ESP-IDF sources - -The SDK identifies ESP-IDF **v5.5.4**, commit -`735507283d5b2f9fb363a1901172dbd9e847945d`. Its source archive includes the pinned -recursive submodules; `sources.json` records each revision. The Arduino library -builder source and SDK's `versions.txt`, `sdkconfig`, include files and flags are -also included. The SDK supplies managed components LittleFS **1.22.1** and -ESP Diagnostics **1.2.1**; Stickwalker's EEPROM backend does not use LittleFS. Their component source archives retain their own -notices and build files. Espressif's radio libraries are supplied as upstream -binary archives under their included licenses. diff --git a/stick/docs/validation-history.md b/stick/docs/validation-history.md deleted file mode 100644 index 183cb71..0000000 --- a/stick/docs/validation-history.md +++ /dev/null @@ -1,47 +0,0 @@ -# Device and software validation - -These records cover device trials and host tests during development. Each -release also includes a qualification record listing its firmware hashes and -completed tests. - -| Area | Evidence and results | -| --- | --- | -| Game connection | A real HeartGold Game Card completed a walk-start transfer, with 374/374 valid optical bursts and independently checked EEPROM mirrors. A subsequent production connection was reported successful. | -| Repeated transfers and storage | A source-faithful 3DS peer completed repeated `back`/`put` operations, including a 20/20 series and exact course readback. The [transfer record](history/transfers.md) identifies the fixtures and retained failures. | -| Sound | Sixteen native scores passed 176 host timing cases. A board trial passed 24/24 acoustic cases across cold starts and warm repeats. See [audio trials](history/audio.md). | -| Power and wake | Battery-only trials exercised sleep returns, advancing RTC, panel restoration and RTC GPIO handoff. Generated gestures exercised the M-only wake policy. See [power and wake trials](history/power-and-wake.md). | -| Production installation | The September 27 readback verified the installed application, preserved paired storage and advancing RTC after resume. | -| Host regressions | Checks cover utilities, controls, wire formats, EEPROM recovery, identity repair, peer encounters and day rollover, RTC events, display commands, decoding and peripheral failures. | -| October 10 identity repair | Host tests repaired three retained device saves and preserved every byte outside the identity fields and their checksums. Simulated checkpoint failures recovered on reboot; peer lifecycle tests passed for both status roles. | - -## Optical failures under repeated testing - -One `put` trial decoded page 59's first byte as `AB` where the peer sent `AA`; -checksum rejection led to timeout. A subsequent series completed 20/20 -operations. A longer diagnostic run completed 44 operations before -`cycle-023-back` decoded `A7` where the peer sent `A6`. Its gate sentinel encoding -does not distinguish a missed pulse from a late sample. The -[transfer record](history/transfers.md#recorded-operations) retains all attempts. - -## Reference production image - -The saved M-only-wake production application has SHA-256: - -```text -5e29d017a468958a07f74da98e4d60602f1e8a28530a742ee5f1bd81631b6c43 -``` - -Its [dated verification](history/power-and-wake.md) records that installation. -The candidate timing check compares the optical sampler with the machine code -from this image. This establishes continuity of acquisition code across builds. - -## Completing device qualification - -The [release checklist](release.md#device-checks-for-a-stable-release) calls for -physical controls, repeated retail transfers with both games, walking -comparisons, battery measurements and interrupted-write recovery on the exact -candidate. - -The [evidence archive](history/README.md) retains image identities, measurements, -failed probes and test methods. Raw traces and private flash snapshots are kept -under ignored `stick/.build/`. diff --git a/stick/package_release.py b/stick/package_release.py index 6720b22..46532da 100644 --- a/stick/package_release.py +++ b/stick/package_release.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Package a committed production build without saves, retail artwork or diagnostics.""" +"""Package Stickwalker firmware, source and runtime archives.""" from __future__ import annotations @@ -112,7 +112,7 @@ def validate_build(folder: Path, allow_ir_timing_change: bool = False) -> dict: if row.get("bytes") != expected["bytes"] or row.get("sha256") != expected["sha256"]: if not allow_ir_timing_change: raise InputError( - f"IR timing changed: {name}; use --allow-ir-timing-change for an unqualified prerelease." + f"IR timing changed: {name}; use --allow-ir-timing-change for a prerelease." ) if not allow_ir_timing_change and not timing.get("passed"): raise InputError("IR timing verification did not pass.") @@ -161,7 +161,6 @@ def git(*parts): "git_commit": commit, "hardware_qualified": False, "ir_baseline_identical": read_json(args.build_dir / "ir-timing.json")["passed"], - "qualification": "See RELEASE.md; qualify these exact output hashes.", "runtime_archive": runtime.name, "runtime_sha256": digest(runtime), } @@ -170,8 +169,6 @@ def git(*parts): for original, name in ( ("LICENSE", "LICENSE"), ("stick/docs/install.md", "INSTALL.md"), - ("stick/docs/release.md", "RELEASE.md"), - ("stick/docs/playing.md", "PLAYING.md"), ("stick/docs/controls.md", "CONTROLS.md"), ("docs/third-party.md", "THIRD-PARTY.md"), ): @@ -186,8 +183,6 @@ def link(match): rel = source_target.resolve().relative_to(ROOT).as_posix() bundled = { "stick/docs/install.md": "INSTALL.md", - "stick/docs/release.md": "RELEASE.md", - "stick/docs/playing.md": "PLAYING.md", "stick/docs/controls.md": "CONTROLS.md", "docs/third-party.md": "THIRD-PARTY.md", "LICENSE": "LICENSE", @@ -229,7 +224,7 @@ def link(match): (output / "SHA256SUMS").write_text( "".join(f"{digest(p)} {p.name}\n" for p in (source, firmware, runtime)) ) - print(f"Candidate prepared at {output}; hardware qualification remains open.") + print(f"Release archives: {output}") def main() -> int: diff --git a/stick/simulation/README.md b/stick/simulation/README.md deleted file mode 100644 index 5541ce9..0000000 --- a/stick/simulation/README.md +++ /dev/null @@ -1,235 +0,0 @@ -# Firmware E2E checks - -Run the production Stickwalker image on a simulated Stick S3, press its buttons, -and check the screen and infrared output. The ESP32-S3 ROM, bootloader, -FreeRTOS, SDK drivers and application execute together. Flash writes use the -firmware's NVS and flash-record code. - -## Run the checks - -Set up the [Arduino build tools](../docs/build.md), Rust 1.98.1 and Tesseract. -On macOS, `brew install tesseract` installs the screen reader. On Debian or -Ubuntu, use `sudo apt-get install tesseract-ocr`. - -From the repository root: - -```sh -uv sync --locked -uv run python stick/check.py -``` - -The first run downloads the pinned simulator and Espressif mask ROM, verifies -them, applies `soc.patch`, builds the simulator and builds production firmware. -Later runs reuse the simulator checkout and Cargo output in `stick/.build/simulator/`. - -To check a retained firmware build: - -```sh -uv run python stick/check.py --use-build --build-dir stick/.build/candidate -``` - -The runner checks that its source and binary hashes match the build record. -`--arduino-cli` and `--config-file` select a separate Arduino installation when -building through `check.py`. - -Repeat `--scenario` to run selected workflows, for example -`--scenario controls --scenario wake`. Fixtures are generated directly in the -current flash-record format; no filesystem packer is needed. - -## Workflows and expectations - -`settings.actions` opens Settings with L, uses M and R to change rotation and -appearance, closes the menu and restarts the chip. OCR reads the display in the selected physical orientation before and after -restart. The background brightness also checks the visible appearance. The [controls guide](../docs/controls.md) defines the button -behavior and persistence expected by this workflow. - -`connect.actions` selects Connect while GPIO5 is held low. After the attempt -finishes, L opens Settings. It closes the menu, holds GPIO5 high and retries -Connect through M+R. The firmware completes its no-reply sequence and Settings -opens again. A separate decoder checks the emitted light pulses as 115200-baud -8N1 SIR, removes the transport XOR and checks the `0xFC` advertisement on both -attempts. These expectations come from [Dmitry's captured Pokéwalker protocol](https://dmitry.gr/?proj=28.+pokewalker&r=05.Projects). This exercises receiver -sampling, interrupt masking, watchdog service, session cleanup and reuse through -the real firmware. - -`controls.actions` boots a public, synthetic registered walker. Thirty-millisecond -contacts navigate Settings, Inventory and Trainer with repeated and alternating -direction taps. A ten-millisecond overlap must remain two directions rather than -become Center. A real M+R chord enters native Settings. Opening the board overlay -while holding R must not change its layout until R is released and pressed again. -The navigation expectations follow `HomeUpdate`, `MainMenuUpdate` and the documented -Stick control mapping, not an injected foreground callback. - -`layouts.actions` selects every layout, resets the chip and reopens Settings to -verify persistence. Both three-key mappings deliver physical L+R to gameplay -before a one-second hold opens the board overlay. The pending-hold capture must -show the native menu; the overlay's input layout must remain unchanged while -both keys, then R alone, remain held. A fresh R changes the layout only after -release. Native Volume and Contrast remain the only selector choices: a second -Right press stays on Contrast. The workflow changes volume 2 to 0 and contrast -4 to 5, saves, resets, and checks both checksummed EEPROM mirrors in the -resulting flash records. Editor cursor positions are checked independently. -After cycling from three-key back to two-key, a fresh dedicated L press must -reopen the overlay with the same two-key layout. - -`wake.actions` waits for the native 60-second display timeout with USB present. -R and L cannot wake the sleeping panel. A short M press cancels preparation and -releases the shared supply; a qualified M hold wakes the screen and must be consumed -through release. The following two presses navigate normally. This exercises native -eight-scan Center acceptance, not a diagnostic wake function. It does **not** exercise -CPU Light-sleep: USB keeps the CPU awake throughout this workflow. - -`battery` runs the same contacts with VBUS at zero. It additionally requires a -retained RTC timer-wake cause, an M-only EXT1 mask while dark, an EXT1 wake for -the accepted M hold, and both front-button pads restored to digital GPIO. These -are hardware-register observations from the model, not firmware diagnostic hooks. -The timer cause is observed at wake rather than an arbitrary later instant: the -RTC model clears that status when the CPU enters its next sleep. - -The controls and wake workflows capture actual I2S DMA output from the simulator. -Their generated score contains tempo 120, a six-unit note at pitch index 20 and an -end marker. The matching firmware's compare table gives 77, so its fundamental is -32768/78 Hz. Native duration arithmetic and final-period muting give 48 periods, -or 114.26 ms. Frequency is measured between the first and last complete positive -zero crossing, with two samples of endpoint quantization tolerance. Counting -crossings over the entire amplitude envelope would bias a short note's estimate -downward. Duration tolerance conservatively budgets both edges of the configured -four-by-64-frame DMA queue, one 1 ms service tick and two PCM samples (24.31 ms at -22050 Hz). M5Unified's stop request is consumed by its mixer; it does not discard -already queued samples. This is an acceptance tolerance, not a hardware latency -guarantee. Five navigation notes and the -first cold-wake note plus a warm repeat must all be present separately. These are -digital-output checks, not measurements of acoustic onset, loudness or codec analog -settling. - -### Public registered fixture - -`fixture.py` generates all 64 KiB from public record layouts and original synthetic -labels rendered with Pillow's default font. It supplies the `nintendo` signature, -valid mirrored identity/status/save records, a registered flag, 100 Watts, no -companion, volume 2 and contrast 4. Its score and simple cursor rasters are generated -locally. It imports no private saves, Nintendo artwork, DS assets or retail scores. - -The runner wraps this image in the current committed checkpoint format and -loads it into the game-data partition before boot. Later reads, writes, record -publication and NVS changes go through the real firmware. It never patches game -RAM or replaces native callbacks. Fixture hashes are retained with the evidence. - -Three recovery cases reuse `recovery.actions`, navigate the native menu and reset -again. `recovery-stale` starts from 200 Watts with an older published journal -containing 100; the checkpoint must win. `recovery-journal` publishes a valid next -record changing both save mirrors from 200 to 100; replay must survive reset. -`recovery-truncated` leaves an incomplete, unpublished append after a 200-Watt -checkpoint; it must not partially apply. Both native mirrors must retain their -expected big-endian Watts and checksums. These are current-format recovery states, -not compatibility fixtures or exhaustive physical power-loss coverage. - -`peer.actions` uses a minimal DS-side protocol peer, not a second copy of the -firmware. It observes GPIO46 SIR, validates checksums and session tokens, sends -status/read requests through GPIO5, and requires the 100-Watt value as `00 64`. -`check.py` separately decodes that reply from the transmitted optical waveform. -`peer-storage.actions` additionally writes one public 128-byte pattern at `0xFF80`, -reads it back, resets the chip and reads it again in a new session. The final -committed checkpoint must contain the same pattern. Both workflows finish by -opening Settings through physical inputs. The peer's ideal charge/discharge -model drives the compiled photodiode receiver; it does not replace decoding or -native protocol callbacks. It is a functional status/page exchange, not a complete -retail DS transfer or an analog interoperability qualification. - -`peer-transfer.actions` follows the supplied HGSS put sequence with synthetic -uncompressed data: status/Watts, 82 course pages, five trainer-record pages, -PHC/distribution reads, checksum/ping, commit and one-way shutdown. After reset, -independent flash extraction checks the committed course and trainer identity. - -`peer-transfer-compressed.actions` follows the same sequence using independently -encoded Nintendo LZ10 pages, as HGSS does for starting a stroll. Pages that grow -are sent raw, and the final partial course/record page uses HGSS's padded -128-byte form. Native commit and reset must retain the same expected course and -trainer record. - -`peer-play.actions` uses an eligible public Walker fixture and exchanges status, -animation, name, trainer-house records, peer information and start. Both bulk -transfer directions are checked. After reset, the native peer-history entry -and received animation/name must remain in flash. It covers one handshake role; -it does not yet cover all opening collisions or both native role branches. - -All three full exchanges enable local transmitter echo and use 115760 baud, matching -the recorded retail-card clock. `STICK_E2E_OPCODE_TIMING=1` uses independently -scheduled CPUs in one-instruction quanta with opcode/branch/call/load-use costs. -Full exchanges also hold the NOR WIP bit for 300 microseconds per program -command and 20 milliseconds per sector erase. These are explicit regression -stress parameters, not a claim about the installed flash chip. An independent -bus observer requires zero flash erases while a protocol reply is pending. -The default short workflows retain their original timing mode. This is a -software timing sensitivity check, not an analog or cache-accurate hardware -model. The [regression investigation](../docs/history/ir-readiness-regression.md) -records the before/after evidence and decoder-equivalence check. - -Each run creates a fresh directory under `stick/.build/e2e-results/` with screen -images, OCR text, console output, optical waveforms and flash. `results.json` -records the completed workflows and hashes of the application, merged image and -simulator patch. CI retains these files for review, including failed runs. - -The suite does not establish retail hardware transfers, exhaustive power-loss -recovery, walking accuracy, social-offer acceptance, battery life or -physical optical timing. The stationary fixture has no eligible social offer; -observing the wake alone does not prove the offer consequence. An exploratory -battery-only run (`vbus 0`) originally reached an RTC-watchdog reset with cause16 -on both unchanged main and the redesign. Guest stacks established that the pinned -RTC model left SLEEP_EN set without generating wake/reject status, trapping the SDK -in `rtc_sleep_start`. The local simulator patch now supplies timer/EXT1 wake, -sleep rejection and watchdog pause during modeled sleep. This enables the battery -workflow but does not measure current consumption or establish physical battery -life. - -## Board model and simulator - -`board.rs` implements the Stick's I2C1 devices on GPIO47/48, the M5PM1 button -latch, selectable input voltage, stationary BMI270 samples and the ST7789 display on SPI3. Its visible -window crops the controller's GRAM to the Stick's 135-by-240 panel. Chip resets -retain the external devices; their reset commands reset their own state. - -The models follow the [M5PM1 manual](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/M5PM1_Datasheet_EN.pdf), -[BMI270 datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi270-ds000.pdf) -and [Stick S3 wiring](https://docs.m5stack.com/en/core/StickS3#pinmap). -`soc.patch` adds SPI3 using the existing GP-SPI engine, dedicated GPIO matrix -reads, timer-group watchdog stages and RMT waveform capture to -[esp32sim](https://github.com/mikroverk/esp32sim). The -[simulation research](../docs/firmware-simulation-research.md) compares the -available ESP32 testing tools and describes the additional devices and evidence -needed for walking, peer exchange and sleep scenarios. - -## Scenario commands - -Commands run in order. `run` advances both cores and peripherals for the given -number of milliseconds. Every run stops on an unexpected chip reset or panic. - -| Command | Action | -| --- | --- | -| `run 100` | Run firmware for 100 ms | -| `run-until-timer-wake 1000` | Run until an RTC timer wake is observed, failing after the given maximum milliseconds | -| `press M`, `release M` | Change a physical M, R or L contact | -| `gpio 5 0` | Hold a GPIO input low or high | -| `serial d` | Send USB serial bytes; used for bench-image investigation, never by production acceptance workflows | -| `screen name.png` | Capture the visible display | -| `optical name.optical` | Save emitted GPIO46 light pulses with durations in CPU cycles | -| `flash name.bin` | Save the flash image | -| `vbus 0` | Set the PMIC input-voltage register; 5000 restores simulated USB power | -| `board name.txt` | Record panel sleep/on state and the shared-supply output latch | -| `audio-clear` | Clear only the host's captured PCM samples, without changing firmware or I2S registers | -| `audio name.pcm` | Save mono signed little-endian 16-bit digital PCM and its `.rate` sample-rate file | -| `peer start` | Enable the ideal optical DS status/Watts peer | -| `peer write-page 65408` | Also write/read a page containing byte `index XOR 0x5A` at the given address | -| `peer read-page 65408` | Also read and verify that page without writing it | -| `peer report name.txt` | Save peer phase, packet trace, observed Watts and any protocol error | -| `peer put path baud turnaround_us` | Run the HGSS put sequence with a public 10430-byte course | -| `peer put-compressed path baud turnaround_us` | Run HGSS put with per-page LZ10 compression | -| `peer walker path` | Run a Walker exchange using a public 64 KiB fixture | -| `peer echo on` | Illuminate the receiver with live local RMT pulses during transmission | -| `run-until-peer 15000` | Advance until peer completion/error, bounded by the given milliseconds | -| `peer stop` | Disconnect the test peer before restarting the board | -| `reset` | Restart the chip while retaining flash and board devices | - -The simulator executable also accepts an optional initial flash offset and image -after its normal arguments. `check.py` uses this only for the generated flash-record -fixture, before the ROM boots.