Commit Graph
16 Commits
Author SHA1 Message Date
twislaandClaude Opus 5.5 2fce79aebd M3 step 4: the LoRa Scanner App, Sniffer and Captures
The Sniffer lists packets newest first (time, RSSI, SNR, and for
Meshtastic the sender, receiver and hops), with the clear header and a
hex dump on Enter (Q96); `p` picks an EU868 preset, kept in Settings
(Q95). `c` starts a Capture: pcap with LoRaTap in /captures/lora, its own
Clean-up category, recorded by a small Service so it carries on with the
App closed (Q97, Q100). The Status Bar shows L while listening, bright on
each packet, and CAP while capturing (Q101). StorageService gains raw
appends for binary files. Debug Builds get `lora inject` to test all of
this with no transmitter in range: a Capture made on the device reads
back in TShark field for field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 20:14:58 +02:00
twislaandClaude Opus 5.5 fd306d5013 G1 steps 5-6: input prompts, bookmarks, downloads, Saved Pages
Everything touching the network or the card is a job on the Gemini
task (a missing card can block 5 s, past the main loop's watchdog):
about:start is composed from /gemini/bookmarks.gmi and the Saved Pages
(by capsule, newest first); file:// opens a Saved Page; s, S, r, d, b
and downloads report one line to the URL bar, S with progress Toasts.

A Saved Page is the cached body with a first line "> Saved from <url>
on <date>": it shows as a quote and gives relative links their base;
inside one, links to other Saved Pages open the saved copy, others go
online or say "Not saved, and offline". Input prompts (11 masked)
request the same URL with the answer as its query.

Fixed on the way: re-wrapping indented lines rebuilt the text from its
rows, inserting a space where a long word had been cut; refreshing
loaded the whole Saved Page next to a TLS connection (heap down to 436
bytes), now it reads one line and every fetch checks the 55 KB floor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 01:24:29 +02:00
twislaandClaude Opus 5.5 fff28af961 Rebuild the framework with smaller TLS buffers (ADR 0006)
custom_sdkconfig makes pioarduino regenerate the ESP-IDF libraries:
asymmetric TLS buffers (16 KB in, 4 KB out) and dynamic buffers that
free handshake-only data once connected. The rebuild also follows the
board: no PSRAM, 8 MB flash. The project now carries the partition
table the hybrid build needs (identical to the framework's: the app
slots must not move under updates over the air). Generated files are
ignored.

Debug Build, GNSS on, IRC on TLS: 78 KB free and a 59 KB low (M2 began
at 31 KB and 12.6 KB; the floor is 40 KB). The full stress set passes
on it: refused installs over Wi-Fi and from SD, put/get, screenshot,
core dump decode, Probation; under all of it at once, the low is 46 KB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 23:32:29 +02:00
twislaandClaude Opus 5.5 4e439410cd Stop IRC by hand; remove mDNS
IrcService::disconnect() stops the session from any state: QUIT if
connected, then no more retries. /quit goes through it (before, it only
stopped a connected session; while waiting for Wi-Fi or retrying it did
nothing), and so does the new `irc stop` command. Stopped by hand,
opening the IRC App no longer reconnects; typing a line does.

mDNS is gone: it never crossed the dev box's routed network, and it
cost about 7.5 KB of RAM. Pushes go to the IP shown in Settings ->
Firmware, which drops its Name row. OTA Q54 records the change.

On the device, Debug Build: 105.6 KB free with Wi-Fi (was 98); with IRC
on TLS 54 KB free (was 46); after `irc stop`, back to 99 KB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 23:17:48 +02:00
twislaandClaude Opus 5.5 388e847cd4 Debug Console: files, screenshots and Update from SD over Wi-Fi
New commands everywhere: ls, rm and install <path> (Update from SD
without the Firmware page), all as Storage Service jobs. In Debug
Builds the console task answers get and put (one storage job per
transfer, file kept open, TCP flow control: about 300 KB/s, against
55 KB/s over serial) and screenshot (the RGB332 frame the UI composes
into). rdbg.py turns those into files and PNGs.

A failed put closes the connection: the rest of the file had been
parsed as commands. Card writes are retried 3 times after closing,
truncating to the last good byte and reopening, since FATFS keeps a
file in error after one failed write (seen once at 1.3 MB on this card).
onStorage() decides with one compare-and-swap whether the job or the
timeout wins, so an abandoned job can't touch a returned stack frame.

Verified on the device: screenshot; a get round trip byte-identical;
4 puts in a row; a tampered .ota refused by install; a good one put,
installed from SD, confirmed on Probation. The retry path itself has
not fired since it was added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 03:18:23 +02:00
twislaandClaude Opus 5.5 14ff13f634 Safe Mode, crash reports, and a watched main loop
Every build now records at boot which version runs and, after a crash
restart, which one crashed (even across a Rollback). The core dump
summary (task, PC, reason, backtrace) is printed and raised as a
Notification; `crash` shows it later. After 3 crash restarts in a row
the firmware starts in Safe Mode: clock, Wi-Fi, Update Service and Debug
Console only (SafeMode, 2 host tests). A normal restart or a minute up
resets the count.

The main loop is now on the task watchdog (enableLoopWDT): Arduino only
watched core 0's idle task, so a stuck loop hung the device for good.
The Update Service restarts into an installed update by itself if the
main loop hasn't after 90 s.

Debug Builds: `coredump get` and `reset` are answered by the console's
own task; rdbg.py crash decodes the backtrace and rdbg.py coredump runs
esp-coredump, against ELFs archived by version and digest in .pio/elves.

The StorageService mutex is now made in the constructor: Safe Mode never
starts that Service, and `info` crashed on the null mutex, 29 times in a
row before the fix was pushed into Safe Mode over Wi-Fi.

Verified on the device: crash report and full core dump decoded over
Wi-Fi; Safe Mode at exactly 3 crashes, left by `reboot`; a hung loop
caught by the watchdog in 5 s; `reset` from the console task. ADR 0005.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 03:07:56 +02:00
twislaandClaude Opus 5.5 0fb7f4e9d5 Debug Builds: the console over Wi-Fi (Debug Console, TCP 2323)
cardputer-adv-debug (-DRORO_DEBUG, version +debug) adds a Debug Console:
after a token line, a client gets the last 6 KB of console output, live
lines (ESP-IDF logs included) and the serial commands. The socket task
only queues lines; the main loop runs them. Release builds compile none
of it. The token lives in ~/.config/roro9stack/debug-token, created by
_docker.sh and passed into the container.

All output now goes through `console`, which never waits for USB: a host
that was attached but not reading stalled the main loop up to 2 s per
line. New commands everywhere: info (slots with their versions from NVS,
since the framework stamps its own into each image), tasks, reboot,
boot other, log level, help. scripts/rdbg.py is the client; flash.sh
--debug builds it; CI builds both variants. ADR 0004.

Verified on the device: USB-flashed, then updated over Wi-Fi to a Debug
Build that confirmed on Probation; both slots hold Debug Builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 02:44:14 +02:00
twislaandClaude Opus 5.5 5b199c2436 sd put: copy a file to the SD card over USB serial
scripts/sd_put.sh <file> [card path] sends a file (by default into
/updates, for Update from SD) without taking the card out. The serial
driver drops bytes once its receive buffer is full, so the transfer is
stop-and-wait: 1 KB chunks, each acknowledged once the Storage Service
has written it, into a 2 KB receive buffer. The device checks the
SHA-256 before renaming <path>.part into place, and gives up after 5 s
of silence or a card job that never returns. FileReceiver holds the
logic, with 12 host tests. About 55 KB/s: 1.6 MB in under 30 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 02:31:19 +02:00
twislaandClaude Opus 5.5 fa62a07993 README: Firmware Updates over Wi-Fi and from the SD card
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-03 21:37:29 +02:00
twislaandClaude Opus 4.8 28d025fb13 Wi-Fi Tools: scan logging to CSV, plus sort and filter
- lib/wifi (host-tested): scan_log (CSV field quoting, header/row, a
  once-per-interval throttle) and network_list_view (sort by signal /
  channel / name, filter open-only / hide-hidden / strong-only)
- ScanEntry moved to lib/wifi so both are testable on the PC
- Wi-Fi Tools networks view: s sort, o/h/w filters, l toggles logging
  to /wifi/scans/<date>.csv (header + one row per AP per logged scan,
  throttled 30 s, needs the clock for timestamps); foreground-only
- Wi-Fi scan logs replace probe-request logs as a clean-up category
- Serial: cat <path>, and key <char> injects a character

Verified on the device: CSV written with header, timestamps, BSSID,
channel, RSSI, security and SSID; hidden networks marked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-03 20:52:43 +02:00
twislaandClaude Opus 5.5 74a341418e M1 step 4b: IRC Service on the device; 8-bit frame buffer
- IrcService: networking on its own task, TLS with the built-in CA
  bundle (or trust-on-first-use pinning when self-signed is allowed),
  plain TCP when TLS is off; connects only while Wi-Fi is Connected,
  marks pauses for Monitoring, reconnects with backoff, pings a quiet
  server, writes Logs, raises Notifications for Mentions
- Session: forget /quit once disconnected (it was handled every loop);
  the server Buffer never counts as unread (MOTD showed as [4])
- Status Bar: unread count
- Frame buffer 16 -> 8-bit colour (M1 Q46): min free heap with IRC on
  TLS went from 51 KB to 79 KB
- Serial: irc start / say / dump

Verified on the device against irc.libera.chat:6697: certificate
checked, joined #roro9stack-test, sent a message, quit cleanly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-02 15:10:03 +02:00
twislaandClaude Opus 5.5 d3948ccc3a M1 step 3: Log writing and Storage Clean-up
- lib/storage_model (host-tested): FAT-safe names, daily Log paths,
  dates from Log/Capture file names, CleanupPlan by category and age,
  byte formatting
- StorageService does all card I/O on its task: queued Log lines are
  written in batches each second (dropped while Logs are paused),
  plus file listing and deletion jobs
- Settings > Storage moves into StoragePage: usage, Clean up
  (category, age with size preview, confirmation), Erase SD card
- Clock: local date for Log names
- Serial: log <text>, sd list

Verified on the device: lines land in /irc/dev/#test/2026-10-02.log
with folders created as needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-02 14:56:06 +02:00
twislaandClaude Opus 5.5 1a0baf4ac7 M1 step 2: Wi-Fi Service, Saved Networks, NTP, Settings page
- lib/wifi (host-tested): SavedNetworks (up to 8, validated, hidden
  flag, persisted) and WifiController (joins the strongest Saved
  Network, tries hidden ones in turn, backoff 10/30/60 s, connect
  timeout, Monitoring override, radio off without Saved Networks)
- WifiService carries out the controller's actions, sets the EU
  country code, and syncs the Clock over SNTP without touching the TZ
- Wi-Fi On/Off setting; Settings > Wi-Fi page: status, add from a scan
  or a hidden network, forget
- Status Bar: W + signal bars, W? while searching, MON while monitoring
- Serial: wifi add / wifi status (replaces the step 1 heap probe)

Verified on the device: joins the test network ~5 s after boot, clock
set over NTP, ~129 KB free heap while connected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-02 14:46:07 +02:00
twislaandClaude Opus 5.5 05c9d9cc56 Add serial dev commands and a serial log helper
The firmware accepts burst, key <name>, sound on|off and short|normal
over serial, so UI behaviour can be reproduced on the device without
the keyboard. scripts/serial_log.sh records (and drives) it in a named
container that flash.sh removes before uploading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-02 13:38:25 +02:00
twislaandClaude Opus 5.5 b293197ace flash.sh: auto-detect the Cardputer serial port
The port number changes when the device re-enumerates (ttyACM0 -> ttyACM1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-02 09:05:15 +02:00
twislaandClaude Opus 5.5 fd4afd54da Scaffold PlatformIO project with Docker-based build, test and flash
- pioarduino platform 55.03.312 (Arduino-ESP32 3.3.x / ESP-IDF 5.5),
  board m5stack-stamps3, M5Cardputer 1.1.1
- native env with Unity for host-side tests
- scripts/ci.sh and scripts/flash.sh run PlatformIO in a container
- version injected from git describe
- boot stub shows name, version and pressed keys
- GPL-3.0 license

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-01 18:35:08 +02:00