The front page's App cards were nine, and had stopped at v0.18.0: no
SSH, no Shell, no games, no compass, no MeshCore. A new page, /apps/,
has all twelve in the Launcher's order and the planned messenger, each
with its icon, what it does, a fact, its guide page, and a screen from
the device (ten taken from it now). The front page keeps six of them,
links each to its row, and says how many there are; the menu's Apps goes
to the page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
Asteroids is the sixth Game: lean the device to turn, ; for thrust, .
to fire, the arrows too for a device on a table. In Pong, 0 switches
between the keys and tilting and 9 turns the tilt round, both
remembered. Both read the accelerometer through the same smoothing, and
take however the device is held at the start as the middle.
Played on the device: the default way of Pong's tilt was swapped after
trying it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
The player's move goes from -1 to 1 instead of three steps, and two
rules make it from the device's tilt: where the paddle is asked to be
(it rolls to the low side, about 15 degrees for the whole height) and
how to get there without overshooting. Host-tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
A ship, rocks that break in two and again in two, short-lived bullets,
three ships and waves that grow, on a field that wraps round. Its turn
comes from -1 to 1, which tiltTurn makes from the device's lean around
the angle it was held at when the game began. Host-tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
Two more Games in the row of icons, which now holds five. Minesweeper
moves a cursor (a held arrow runs it along), digs with Enter, flags with
f, shows where the mines were when it is lost, and keeps the fastest
cleared field. 2048 slides with the arrows and keeps the best score; it
has no clock, so nothing to pause. Neither keeps the screen lit.
Played on the device: Minesweeper's cells went from 8 to 12 pixels to be
read, and its field to 20 x 9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
Minesweeper on 20 x 9 with 28 mines, laid at the first dig and never
under or next to it, with flags and the open-around-a-number dig; 2048
on 4 x 4, a tile merging once a move. Seeded, host-tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
The Games App of v0.24.0: why a firmware wants games, what Pong, Snake
and Tetris carry, how little room they take, and what playing changed
in the design. With five screenshots from the device.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
A new App (issue #101) that shows its three Games as icons, as the
Launcher shows Apps, and plays the one chosen. A Game left in the middle
is found again paused; one being played keeps the screen lit. Tetris
plays Korobeiniki on a speaker channel of its own, faster with each
level: 0 switches it, 8 and 9 set its volume, both remembered. The drop
is on 1, away from the arrows. The best of each Game is kept.
Played on the device. The speeds are as tuned there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
The rules of the three Games (issue #101), with no screen and no
keyboard in them: time and keys go in, the new state and what happened
come out, and a seed decides the dice. With a player for a tune a note
at a time, and Korobeiniki's notes. Host-tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
When the connection breaks during a release download (issue #54), the
device waits up to a minute for Wi-Fi, asks for the rest of the file
with a Range request and carries on: the slot and the hash so far are
kept. It gives up after three tries in a row that bring nothing.
Nothing is kept across a restart.
Carrying on asks for 64 KB free, not 80: between two connections of an
install 78 KB is free, and the first attempt on the device was refused
for that. update damage cut now breaks every connection after n bytes.
Tried on the device: v0.23.0 installed over four connections, and a
download that brought nothing given up with nothing switched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
For carrying on a download that broke (issue #54): the head's ETag and
Content-Range are read, a 206 is checked to be the rest of the same
file, and a policy says how many tries a download is worth. Host-tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
With Settings > System > Install updates on By itself (issue #52), a
release found by the daily check is offered once no key was pressed for
2 minutes and nothing would be cut by a restart. The question goes over
whatever App is open, with Cancel and Accept, and installs after 30 s
without an answer. Each update that held, and each one undone, is a
dated line in /system/updates.log on the card.
Tried on the device: the question, Cancel, and an install of v0.23.0
left unanswered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
Tab goes on from the Sky to a third view (issue #16): a rose that turns
with the device, the heading, where north came from and how long ago,
the sun on the rose, and a level. m marks where you stand as a Place,
t picks one as the target: an arrowhead on the rose, its distance and
bearing. The sensor's directions were checked by hand on the device.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
The device has no magnetometer (issue #16): north is given by the course
over ground while walking, or by aiming at the sun, and the gyroscope
follows the turn around the vertical in between. With the sun's position
from the time and the place, the bearing to a point, the 16 points and
distances as text. Host-tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
Settings had grown to 21 rows in one list. It opens on five groups (This
device, Display, GNSS and radio, Network, System); Enter opens one, Back
returns to the groups, and Back from the groups leaves Settings.
SettingsMenu holds the groups and which one is open (host-tested: every
setting is in exactly one group). The guide, the how-tos, the README,
the scripts' messages and the firmware's own name the new paths, such as
"Settings > System > Firmware".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
Settings > Theme and Settings > Light or dark pick the colours of every
screen (issue #10): roro9stack (the website's, the default), Catppuccin,
Dracula, Nord, ANSI terminal, Gruvbox and Solarized.
- A palette is 14 roles (lib/ui/src/palette.h). theme::kText and the
others are now references into the one in use, so the Apps are
unchanged; the main loop applies it when a Setting changes.
- Every colour is one the RGB332 frame buffer holds exactly: the themes'
own were rounded, and those that fell together or lost contrast were
picked by hand. test_palette checks exactness and 18 contrast ratios
for each palette.
- The Sky view's constellation colours come from the theme.
- `theme [0-6] [light|dark]` on the consoles.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
The Launcher shows the Apps as a 4 x 3 grid of 32 x 32 icons, the
selected one's name under it (issue #9). The icons are the website's
own, doubled, and two drawn the same way for SSH and the Shell; they
are PNGs in assets/icons, turned into arrays by scripts/make_icons.py,
which CI checks. The selected tile is the website's: dark blue on cyan,
with notched corners.
- GridModel (lib/ui, host-tested) holds the selection and the scrolling.
- App::badge(): a dot on the tile for unread IRC messages, a Track
recording, a Capture running, an SSH session open.
- Settings > Launcher: Grid or List.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
The theme takes the website's palette, each a colour the RGB332 frame
buffer holds exactly: cyan for the accent, a selected row and a chosen
button in cyan with dark blue text, a slate Status Bar (the grey it
asked for was shown as a dull olive), the website's orange for
warnings and its pink for messages. "Good" (a Fix, the quietest
channel) keeps a green of its own, which the website doesn't have.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
The bottom-line message (a malformed address, not enough memory, a
session still closing) is timed from millis() when a key sets it, and
update() compared that with the pass's own time, read before the keys
were handled. The unsigned difference wrapped, so the message was
cleared before it was ever drawn: Enter on a new connection that was
refused looked like it did nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
The MeshCore preset is the default for a new device, in the Settings and
in the radio before the Scanner is opened. The setting now accepts it:
it only took the 7 Meshtastic presets, so MeshCore picked in the App was
not saved.
Messages on MeshCore's public channel are decrypted with the channel's
published key (AES-128, checked with 2 bytes of HMAC-SHA256): the row
shows who says they sent it and the start of the text, the details the
sender, the time and the whole text. Other channels and private messages
stay encrypted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
- MeshCore joins the presets (EU/UK Narrow: 869.618 MHz, 62.5 kHz, SF8,
CR 4/8, sync 0x12), after the Meshtastic ones so stored choices keep
their number; `lora preset MeshCore` on the console
- lib/meshcore reads what MeshCore sends in clear: header, path, and a
node's advert (name, kind, key, position)
- the list row, the details, a Capture's viewer and the console echo read
a packet by the sync word it was received on
- the bandwidth is printed as 62.5, not 62
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
WireGuard brings libsodium in unpinned. 1.10021.12 defines
crypto_sign_ed25519_open, as LibSSH-ESP32 does: a clean build failed with
a multiple definition. A cached build kept linking, which hid it.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
- uname in the console and /uname in IRC: the firmware, its version and the chip
- scp: one file between the card and a trusted server, over SSH, with the
device's key or a password asked (masked) in the Shell
- shared libssh helpers in src/platform/libssh_util
- README, user guide, command reference and a how-to updated
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
The SSH App opens one session to a shell, over libssh (LibSSH-ESP32 5.10.0)
on mbedTLS. A server is trusted the first time on its fingerprint, and a
changed key is a warning with Cancel selected. The password is typed each
time and kept nowhere; or the device makes itself an Ed25519 key, whose
public half is shown, written to /ssh/id_ed25519.pub and printed by
`ssh status`.
lib/term is the terminal: what a shell, less, top, nano and vim send, with
sixteen colours, scroll regions, the alternate screen and 100 lines of
scrollback. Five text sizes with Ctrl and + or -, from 60x20 to 26x8, told
to the far end. The session goes on when the App is left; SSH shows in the
Status Bar. `ssh user@host` in the Shell opens the App.
Also:
- Keys that aren't characters carry Shift, Ctrl and Alt. The terminal needs
it, and it makes Ctrl+Fn+up/down in a note and Shift+Tab in Gemini work
from the real keyboard.
- IRC doesn't try to connect under 60 KB free: started with a session open,
its TLS handshake took the heap down to 236 bytes.
- libssh's own curve25519 is left out of the build (scripts/libssh_filter.py):
libsodium's has the same names.
Costs 292 KB of flash and about 50 KB of heap while a session is open; not
started under 75 KB free.
Docs: guide page, how-to, FAQ, home page, Status Bar, SD card folders, the
memory how-to, README, glossary, N1 notes with Q254 to Q266 and the checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
The rest of the issue's list. tls makes a handshake that checks nothing,
then says the certificate in words: who it is for, who signed it, until
when, and whether this device's roots and the name asked for accept it,
with the reason when they don't. ntp compares a time server's clock with
the device's. netstat lists what listens and what is connected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT