Mara C1.12 — serial standby / wake — HARDWARE TEST v1
21 October 2026

DELIVERABLE
Mara_Flash_112_serial_standby_wake_TEST_v1.bin
8192 bytes. Program into FLASH at 0x8000 through 0x9FFF.
This is an experimental hardware-test build, not a hardware-validated release.
156 bytes differ from the exact supplied original, in seven patch regions.

Original SHA-256:
78126b253706b6af92caab55043b15526bd0cd9018685e2a67810e3e7820d1c8
Patched SHA-256:
69b691a43e47f76c9153a892b7c24eeb88f4e1888d71e821ce3e80f437f90ab1

REQUIREMENTS AND BEHAVIOR
- Target only the STM8S003F3P board using the supplied C1.12 firmware image.
- Auto-standby must be enabled on the actual test unit. The supplied EEPROM
  dump has it enabled (byte 0 = 01); this patch does not change that preference.
- The processor and UART remain running during standby, as confirmed by you.
- Keep the existing UART electrical connection/logic levels. This firmware
  change does not turn a logic-level UART into an RS-232 voltage interface.
- UART settings are unchanged. The original initialization indicates nominal
  9600 baud, 8 data bits, no parity, 1 stop bit. Confirm baseline telemetry with
  your existing adapter/settings before sending commands.

WIRE COMMANDS (RAW HEX BYTES, NOT TEXT)
  STANDBY:  D3 2C
  WAKE:     D7 28
Send each pair in a single binary write. Do not send the characters "D3 2C".
No CR/LF terminator is required. Neither an ASCII command nor an ACK was added.
Only complete complement pairs are recognized. Other bytes do not request an
operation; UART overrun, noise, framing or parity errors reset the partial pair.
The partial-pair decoder has no inter-byte timeout. Both bytes must be consecutive
in the received byte stream. A newer complete command can replace one pending
command; send one command at a time and allow the machine to respond.

STANDBY is accepted only when all of these code-level conditions hold:
  RAM B6 = 1; B3 = 4; B5 bits 0/3/4 clear;
  debounced lever counter 78 < 25; physical PD1 high; countdown A5 = 0.
These guards exclude startup/error states and identified lever/activity phases.
A busy/ineligible request is discarded, not queued for later. Warm-up or a
post-brew phase can therefore prevent immediate standby. Allow the machine to
settle into idle and resend once if necessary. No firmware ACK reports rejection.
An eligible request sets RAM word AB–AC to 0001, the native timer-expired sentinel.
The original control loop then applies its existing standby behavior. It does
not directly force heater or pump GPIOs.

WAKE is accepted when B6 = 1, B3 = 4, and AB–AC = 0001. It sets B3 = 3 and B4 = 1,
matching the state/substate transition used by the native lever wake-up path.
The normal restart checks run, and the normal transition back to state 4 reloads
AB–AC with 1800. The patch does not set the brew/lever flag or directly drive the
pump. Native restart conditions can delay completion. Repeated wake commands
while awake or already restarting do not restart the sequence.

This build supports native standby with auto-standby enabled. It does not add a
manual standby override for a unit whose auto-standby preference is disabled.

PROGRAM AND FIRST TEST
1. Save a fresh readback of the actual unit's flash, EEPROM and option bytes.
   Use an erase/program operation that preserves EEPROM and option bytes.
   Program only the 8192-byte patched flash image at base address 0x8000.
   Do not change readout protection or option bytes for this patch.
2. Read flash back after programming and compare it byte-for-byte with the
   patched image (or compare the SHA-256 above). Confirm all 8192 bytes.
3. Power-cycle, with no serial commands. Confirm ordinary startup, plausible
   sensor/temperature telemetry, normal controls and no unexpected outputs.
4. With auto-standby enabled, allow the machine to reach a settled idle condition
   with the lever released. Send D3 2C once. Compare the indicators and heater
   behavior with ordinary timer-induced standby; record telemetry.
5. Send D7 28 once. Check the normal wake/restart sequence, continued telemetry,
   and that no unintended brewing/pump action occurs.
6. Repeat STANDBY while already in standby and WAKE while already awake. Also
   verify that normal lever wake-up still works. Then confirm a later natural
   timeout still occurs after the normal interval.
7. If a command seems ignored, first check actual auto-standby preference, raw
   byte transmission, startup/idle state, lever and activity guards. The sender
   cannot distinguish a firmware rejection from a missing electrical connection.

Perform the first tests attended. If baseline sensor readings, temperature
control or outputs differ unexpectedly, stop the test and restore the original
firmware before continuing. Software checks below cannot verify the real machine.

WINDOWS SENDER
The included send_command.ps1 uses Windows' built-in serial API, with no extra
modules. Close any terminal that already owns the COM port. From this folder:
  .\send_command.ps1 -Port COM5 -Command standby
  .\send_command.ps1 -Port COM5 -Command wake
Replace COM5 with your adapter. It displays up to five seconds of the existing
telemetry. "Sent" confirms only the host write, not device acceptance.
If local script policy prevents running it, use your usual serial tool's raw
hex/binary send function instead; no policy change is required by this patch.

ROLLBACK
Program rollback/Mara_Flash_112_ORIGINAL.bin at 0x8000, verify readback against
its original SHA-256, and power-cycle. If the actual unit's pre-test readback
was different, restore that readback instead. EEPROM is not modified by the new
command handler. The original firmware may still write EEPROM for its existing
functions; use your own fresh EEPROM/option-byte backup if restoration is needed.
rollback/Eeprom_112_ORIGINAL.bin is the supplied reference dump, not a requirement
or instruction to overwrite the current unit's EEPROM.

IMPLEMENTATION
1. Original UART Rx ISR 9D2A–9D56 is replaced with a bounded decoder. Hardware
   saves/restores CPU registers. It reads UART SR before DR and touches only
   the old Rx state bytes 001D (partial pair) and 001E (pending command).
2. Main-loop call at 9B51 invokes a wrapper at 8080. The wrapper reads/clears
   the mailbox with interrupts briefly masked, restores the previous mask,
   handles one command, then tail-calls the original UART routine at 9300.
3. Obsolete Rx-length writes at 9312 and 93D0 are NOPs. The old receive-buffer
   clearing block at 932A jumps over an embedded lookup helper to 9339.
4. To fit, the 44 zero words at the start of the conversion table (8080–80D7)
   are replaced with dispatcher code. This is a deliberate semantic replacement,
   not use of unreferenced padding. The table read at 975B now calls 932D:
       if doubled_index < 88: return 0
       else: return original_table[doubled_index]
   The caller's original clamp to index 920 remains. The remaining 877 words
   at 80D8–87B1 are byte-for-byte unchanged. Every 16-bit raw input was checked
   through the clamp/lookup/store sequence, including resulting registers/flags.
   Lookup helper comparison flags are overwritten by the caller's ADD at 9760
   before any dependent branch. The helper adds one two-byte CALL stack frame.
5. The original vectors, native state handlers, watchdog service, output
   routines, nonzero lookup entries, and UART output format are unchanged.
   The transmitted version string remains C1.12. Identify this build by flash
   readback/hash; the telemetry version alone cannot distinguish it.

VERIFICATION INCLUDED
- Independent decoding with radare2/gmtdisas STM8 decoder.
- Exact patch bounds, expected original bytes, branch targets and image length.
- All 65536 raw lookup inputs: identical clamp/lookup/store result and registers/
  flags in the instruction-subset interpreter.
- All 65536 two-byte streams: only the two command pairs request an operation.
- 34560 UART error cases and parser overlap/malformed-stream cases.
- 34560 dispatcher/state/guard cases, all initial interrupt-mask combinations,
  and interrupt interleavings around mailbox exchange.
- Existing native restart code and 1800-count reload preserved.

verify_patch.py is a small interpreter for the relevant instruction subset. It
is not a complete STM8 simulator: peripheral timing, all original firmware paths,
physical outputs, UART electrical behavior and actual hardware are not simulated.

REBUILD (PYTHON 3, STANDARD LIBRARY ONLY)
  python build_patch.py rollback/Mara_Flash_112_ORIGINAL.bin --output-dir rebuilt
  python verify_patch.py rollback/Mara_Flash_112_ORIGINAL.bin rebuilt/Mara_Flash_112_serial_standby_wake_TEST_v1.bin --report rebuilt/verification_results.json
The builder rejects any original image with a different SHA-256. The full source
is included. patch_manifest.json lists before/after bytes for every patch region;
patch_listing.txt and independent_disassembly.txt show the inserted instructions.

SOURCES FOR INSTRUCTION AND REGISTER CHECKS
ST STM8 CPU programming manual PM0044:
https://www.st.com/resource/en/programming_manual/pm0044-stm8-cpu-programming-manual-stmicroelectronics.pdf
ST STM8S003F3 datasheet:
https://www.st.com/resource/en/datasheet/stm8s003f3.pdf
ST STM8S reference manual RM0016:
https://www.st.com/resource/en/reference_manual/rm0016-stm8s-series-and-stm8af-series-8bit-microcontrollers-stmicroelectronics.pdf
Independent decoder:
https://github.com/radareorg/radare2/tree/master/libr/arch/p/stm8/gmtdisas
