Merge pull request 'Site: the user guide (phase 2)' (#63) from site-guide into main
Site / build (push) Successful in 8s

Reviewed-on: #63
This commit was merged in pull request #63.
This commit is contained in:
2026-10-06 18:39:59 +00:00
18 changed files with 534 additions and 2 deletions
+7 -1
View File
@@ -1,6 +1,6 @@
# W1: Website
**Status:** phase 1 is built and checked in a browser (branch `site`, pull request to follow); the Install page works against the real server once Caddy allows the origin (below). Issue #12.
**Status:** phase 1 (home, Install, Downloads) is live at roro9stack.net; phase 2 (the user guide) is built, in a pull request. Issue #12.
**Goal:** a public home for the project at **roro9stack.net**, separate from the blog (stories) and from Gitea (developers): what it is, how to install it, how to use each App, and the docs.
@@ -76,6 +76,12 @@ Changed before it ships:
- **The Downloads page** lists the last 30 releases with their files, read at build time.
- **The wordmark SVGs** from the design carried an embedded C2PA content-credentials block; it is removed from the site's copies.
## As built (phase 2, the user guide)
- **`/guide/`** is a section of 11 pages, `site/content/guide/`, each with `template` from the section's `page_template` and an order from `weight`: the basics (keys, Launcher, Status Bar, first start, the card), then one page per App (LoRa Scanner, GNSS, Gemini, IRC, Wi-Fi tools, Notes, Storage, System), Settings and Updates. Pages with real screenshots list them in `extra.screens`, looked up in `data/screens.toml`.
- **Facts come from the README, the milestone documents and the Apps' own source** (key handlers, labels, the Status Bar's drawing code), not from memory. Some wording was corrected against the source while writing: the Track folder is `/gnss/tracks`, the reasons a Track won't start, what the Status Bar shows.
- **Not covered:** the mesh messenger (planned), the debug console and Debug Builds beyond a pointer to the README. How-tos and the FAQ are phase 3.
## Checks (2026-10-06)
| Check | Result |
+13
View File
@@ -0,0 +1,13 @@
+++
title = "User guide"
description = "How to use each App on the Cardputer: the keys, what the screens show, and what is written to the SD card."
template = "guide-index.html"
sort_by = "weight"
page_template = "guide-page.html"
+++
This guide says what the firmware does **today** and nothing else. Start with the basics (the keys, the Launcher, the first start), then read the page of any App. It describes the latest release; the numbers and key names come from the firmware's own source.
**The mesh messenger is planned, not built.** The LoRa Scanner listens to Meshtastic traffic and shows it, but the device sends nothing yet: that is the next milestone and waits for a second node to test with.
Something missing or wrong? Write to the [issue tracker](https://git.twis.la/twisla/roro9stack/issues) or to contact@roro9stack.net.
+63
View File
@@ -0,0 +1,63 @@
+++
title = "The basics"
description = "The keys, the Launcher, the Status Bar and what happens the first time you switch the device on."
weight = 1
[extra]
tag = "Start here"
+++
## The keys
The Cardputer's keyboard has no arrow keys and no Escape, so the firmware gives a few keys a second job:
| Key | Does |
|---|---|
| <kbd>Enter</kbd> | Opens or confirms the selected item |
| <kbd>`</kbd> | **Back**: one step out of a screen, and out of an App |
| <kbd>Fn</kbd> + <kbd>`</kbd> | **Home**: back to the Launcher |
| <kbd>Fn</kbd> + <kbd>;</kbd> <kbd>.</kbd> <kbd>,</kbd> <kbd>/</kbd> | The arrows: up, down, left, right |
| <kbd>;</kbd> <kbd>.</kbd> <kbd>,</kbd> <kbd>/</kbd> alone | The same arrows, as long as you are **not** typing text |
| <kbd>Tab</kbd> | Switches view in an App that has more than one |
| <kbd>Del</kbd> | Deletes backwards when you type |
| <kbd>opt</kbd> then an accent, then a letter | Types an accented letter: <kbd>opt</kbd> <kbd>'</kbd> <kbd>e</kbd> gives é |
While you type text (a note, an IRC line, a setting), `;` `.` `,` `/` type their own characters and you need <kbd>Fn</kbd> for the arrows. The bottom of the screen shows `opt` while a compose is waiting for its letter.
## The Launcher
The home screen lists the Apps. Move with the arrows, open one with <kbd>Enter</kbd>. Back inside an App returns here.
## The Status Bar
A strip at the top of every screen: the name of the App on the left, and on the right, from the edge inwards:
| Shows | Means |
|---|---|
| the time | The clock, once Wi-Fi or a GNSS fix has set it |
| `[3]` | Unread IRC messages that mention you, or private ones |
| `97%` | The battery (in the warning colour at 15% and under) |
| `SD` | A card is in; the warning colour at 80% full |
| bars and `W` | Wi-Fi connected, with its signal; `W?` is searching; `MON` is the Wi-Fi radio in its monitoring mode, which pauses IRC |
| `REC` | A GNSS Track is being recorded |
| `CAP` | A LoRa capture is being recorded |
| `L` | The radio is listening; it lights up for a moment on each packet. `SW` while a Sweep runs |
| `G` or `G12` | The GNSS receiver is searching (`G`, dim), has a 2D fix (`G`), or has a 3D fix with that many satellites (`G12`) |
## Toasts
News from a background service (an IRC mention, a Storage warning, an update that is out) shows as a short message over whatever App is open, and can beep and flash the LED. **Sound & LED** in Settings turns that off.
## The first start
On a new device a short Setup asks four things, then never appears again:
1. **Long name**, up to 39 bytes.
2. **Short name**, up to 4 characters.
3. **Radio region.** Nothing will transmit until you confirm yours. EU868 is the supported region.
4. **Timezone.**
## The SD card
Put a microSD card in the Cardputer. Without one the radio, GNSS, Wi-Fi and IRC still work, but nothing can be saved: no notes, IRC logs, Wi-Fi scan logs, GPX tracks, LoRa captures or saved Gemini pages. The [Storage App](/guide/storage/) shows what is on the card.
The firmware keeps its own folders at the top of the card (`captures`, `gemini`, `gnss`, `irc`, `updates`, `wifi`, plus `notes`). You can use the card in a computer too, but those names are the firmware's.
+42
View File
@@ -0,0 +1,42 @@
+++
title = "Gemini"
description = "Browse Geminispace: follow links, go back, bookmark pages and keep them on the SD card to read offline."
weight = 4
[extra]
tag = "Gemini"
screens = ["gemini.png"]
+++
[Gemini](https://geminiprotocol.net/) is a small, text-first protocol: pages are plain **gemtext**, served over an encrypted connection, from **capsules** instead of sites. It needs Wi-Fi.
## Reading
| Key | Does |
|---|---|
| <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Picks the next or previous link |
| <kbd>Enter</kbd> | Follows the link |
| <kbd>`</kbd> (Back) | Returns to the previous page, at the place where you scrolled to |
| Up and down | Scroll a line |
| <kbd>Space</kbd> | Pages down |
| <kbd>g</kbd> | Types an address |
| <kbd>b</kbd> | Bookmarks the page |
| <kbd>s</kbd> | Saves the page to the card |
| <kbd>S</kbd> | Saves it with the pages it links to |
Headings, lists, quotes and preformatted blocks are drawn as gemtext intends, wrapped to the screen. Links to other protocols show their address and are not followed. A page that asks for input (a search, say) shows a prompt, and a password prompt hides what you type. Characters outside the Latin fonts show as `?`. The history keeps the last 20 pages.
## The start page
It lists your **bookmarks**, then your **Saved Pages**, then a few starting points: geminiprotocol.net, a search engine and an aggregator. Without a card it shows only the built-in starting points.
## Certificates: trust on first use
Most capsules sign their own certificate. The first certificate seen for a host is **remembered**; if it later changes, the page is not shown and you are asked whether to trust the new one, with both fingerprints on screen. Expired or self-signed certificates are fine: only a *change* counts. Client certificates are not supported.
## Saved Pages
<kbd>s</kbd> keeps the page on the card, with the address it came from and when it was saved, and you can read it with no network. <kbd>S</kbd> also saves the pages it links to on the **same capsule**, as text only, up to 30, in the background. On the start page, Saved Pages are listed by capsule, newest first. Inside one, <kbd>r</kbd> **refreshes** it and <kbd>d</kbd> **deletes** it. A link to another saved page opens the saved copy; any other link fetches online if Wi-Fi is up, or says it is not saved. A file that is not text (a picture, say) is saved to `/gemini/downloads/` instead of being shown. The Storage clean-up never offers Saved Pages for deletion.
## Big pages and memory
With a card, every page streams to the card first, so a page larger than the device's memory still arrives whole and is read from the card as you scroll. Without a card, a page is limited to what fits in memory. If there is not enough free memory to open a secure connection, the fetch says so instead of failing silently; stopping IRC frees the most.
+30
View File
@@ -0,0 +1,30 @@
+++
title = "GNSS"
description = "Where you are and which satellites you can see, from the Cap's receiver, with Tracks saved to the SD card as GPX."
weight = 3
[extra]
tag = "GNSS"
screens = ["sky.png"]
+++
GNSS needs the **Cap LoRa-1262**, an antenna with a view of the sky, and **Settings → GNSS** switched **On**. Otherwise the App says `GNSS is off` (or `paused`, when "Pause GNSS for LoRa" has put the receiver on standby while the radio listens). A first fix outdoors can take a while: the App says how long it has been searching.
## Position
The first view shows the fix (`No Fix`, `2D Fix` or `3D Fix`, and how many satellites it uses), then:
- **Lat** and **Lon**, in decimal degrees or degrees, minutes and seconds (**Settings → Coordinates**);
- the **Locator**, the Maidenhead grid square;
- **Altitude**, **Speed** and the direction you are moving;
- **HDOP**, the horizontal precision: a smaller number is better;
- the **Time**, in UTC.
Once there is a fix, the device's clock follows it.
## Sky
<kbd>Tab</kbd> switches between Position and **Sky**: a circle with N, E, S and W, where each satellite is a dot, coloured by constellation, and filled when the receiver uses it. A legend counts, for each constellation (GPS, GLONASS, Galileo, BeiDou), the satellites used and in view.
## Tracks
<kbd>r</kbd> starts recording a **Track**: the route is saved as a **GPX** file on the SD card, in `/gnss/tracks` (named by date and time), and the Status Bar shows `REC`. Press <kbd>r</kbd> again to stop. A Track keeps recording with the App closed. It needs a card and a clock (a fix or Wi-Fi sets it); if it cannot start, the App says why: `GNSS is off`, `No SD card` or `Waiting for the time`. The [Storage App](/guide/storage/) opens a `.gpx` file and shows its points, start, duration and distance.
+51
View File
@@ -0,0 +1,51 @@
+++
title = "IRC"
description = "Chat on an IRC server from the keyboard, with a connection that outlives the App and daily logs on the SD card."
weight = 5
[extra]
tag = "IRC"
+++
## Connecting
The first time, type `/settings` and press <kbd>Enter</kbd>. The settings page has:
- **Server** and **Port**, and **TLS** (on or off).
- **Self-signed:** for a server whose certificate no authority signed. It is *pinned on first use*: the first certificate it sees is remembered, and a different one later is refused.
- **Nick**, **SASL user** and **SASL password**, **NickServ password**: passwords show only as `(set)`.
- **Auto-join:** the channels to join after connecting.
- **Save & reconnect.**
Opening the IRC App connects, if Wi-Fi is up. The connection belongs to a **background service**: leaving the App does not disconnect, and new messages keep arriving and being logged. It reconnects by itself after a drop, and does not start by itself after a restart.
## Chatting
You type at the bottom; <kbd>Enter</kbd> sends. A line starting with `/` is a command:
| Command | Does |
|---|---|
| `/join #channel [key]` (or `/j`) | Joins; the `#` is optional |
| `/part [#channel] [message]` | Leaves |
| `/msg nick text` (or `/query`) | Opens a private chat, sending the text if there is any |
| `/me text` | An action line |
| `/nick newnick` | Changes your nick |
| `/topic [text]` | Shows or sets the channel topic |
| `/names [#channel]` | Lists who is there |
| `/quit [message]` | Disconnects and **stays disconnected** until you type something again |
| `/raw …` (or `/quote`) | Sends a line to the server as it is |
| `/settings` | The settings page |
Each server, channel and private chat is a **buffer**. <kbd>Tab</kbd> moves to the next one, each shows how many messages are unread, and your own lines are in the accent colour. Scroll back with <kbd>Alt</kbd> + <kbd>;</kbd> (older) and <kbd>Alt</kbd> + <kbd>.</kbd> (newer). The up and down arrows (<kbd>Fn</kbd> + <kbd>;</kbd> and <kbd>.</kbd>) recall lines you sent.
## Mentions and the unread count
A message with your nick in it, or any private message, is a **mention**: it shows a Toast over whatever App is open, and counts in the Status Bar's `[n]`. Other traffic only adds to the buffer's unread count.
## Logs
Every buffer is logged to the SD card, one file per day, under `/irc`. Logs stop when the card passes 90% full, to keep the rest for captures; nothing is deleted without your asking, in the [Storage App](/guide/storage/)'s Maintenance.
## Good to know
- IRC **pauses** while Wi-Fi is monitoring (the Status Bar shows `MON`) and while the device **installs an update**. It reconnects and rejoins its channels afterwards; the App says `paused` in the meantime.
- A secure connection costs memory: IRC's takes about 40 KB of the 107 KB the device has, and an update's download needs about 52 KB more. That is why IRC steps aside for an update, and why the daily update check waits for IRC to be disconnected (see [Updates](/guide/updates/)).
+35
View File
@@ -0,0 +1,35 @@
+++
title = "LoRa Scanner"
description = "Listen to the radio: every packet it hears, a survey of signal strength from 863 to 870 MHz, and captures for Wireshark. It only listens."
weight = 2
[extra]
tag = "LoRa Scanner"
screens = ["sniffer.png", "sweep.png"]
+++
The Scanner uses the **Cap LoRa-1262**. It **never transmits**: it listens and shows.
## Sniffer
The first view lists what the radio hears, newest first: the time, the RSSI (signal strength, in dBm) and the SNR (signal over noise, in dB). For a **Meshtastic** packet it also shows the sender and the receiver (the last four hex digits of their numbers) and the number of hops.
| Key | Does |
|---|---|
| <kbd>Enter</kbd> | The details of a packet: the Meshtastic header, which is never encrypted, and a hex dump |
| <kbd>p</kbd> | Picks one of the 7 Meshtastic presets allowed in EU868 (LongFast by default) |
| <kbd>c</kbd> | Starts or stops a **capture** |
| <kbd>Tab</kbd> | Switches to the Sweep |
The Scanner shows the header and the bytes; it does not decrypt the message itself, which Meshtastic encrypts with the channel's key.
## Capture
A capture records packets into a **pcap** file with LoRaTap headers in `/captures/lora/` on the SD card, to open in Wireshark. It keeps recording with the App closed (the Status Bar shows `CAP`); the radio sleeps when the App is not open and no capture is running. Captures are never deleted unless you ask, in the [Storage App](/guide/storage/), which can also show a pcap's packets on the device.
## Sweep
<kbd>Tab</kbd> switches to the Sweep: the signal strength across **863 to 870 MHz** in 100 kHz steps, drawn as bars with a mark at each peak, and a waterfall under them. The frequency the Sniffer listens on is marked. The Sniffer is paused during a Sweep and picks up where it was. The Status Bar shows `SW`.
## The GNSS receiver raises the noise
The GNSS receiver on the same Cap makes the radio's noise floor about **8 dB** worse while it runs. **Settings → Pause GNSS for LoRa** (off by default) puts the receiver on standby while the radio listens, except while a Track is being recorded.
+37
View File
@@ -0,0 +1,37 @@
+++
title = "Notes"
description = "Plain text notes on the SD card, with no save key: the editor saves for you, and a power cut never costs the note."
weight = 7
[extra]
tag = "Notes"
screens = ["notes.png"]
+++
Notes are plain text files in `/notes` on the SD card. They are ordinary `.txt` files: you can read them on a computer too.
## The list
Each note shows its first line and its date, newest first. <kbd>s</kbd> switches to sorting by file name.
| Key | Does |
|---|---|
| <kbd>n</kbd> | Starts a new note |
| <kbd>Enter</kbd> | Opens the note |
| <kbd>r</kbd> | Renames its file |
| <kbd>d</kbd> | Deletes it, after asking |
## The editor
Type. <kbd>Enter</kbd> starts a line and <kbd>Del</kbd> deletes backwards. <kbd>Fn</kbd> with the arrow keys moves the cursor through the wrapped text, <kbd>Ctrl</kbd>+<kbd>A</kbd> and <kbd>Ctrl</kbd>+<kbd>E</kbd> go to the start and the end of the line, <kbd>Tab</kbd> types two spaces, and the compose key gives accents as everywhere.
**There is no save key.** The note is written five seconds after your last key, when you press Back, when you leave the App, when the screen turns off and before the device powers off. The top line says `typing` or `saved`.
Each save writes a temporary file and then puts it in the note's place, so a power cut costs a few seconds of typing and never the note. If a save was cut short, opening the note offers its copy back.
## Names
A new note has no file until you type something. The file is then named after its first line (`shopping-list.txt`), or `note-<date>-<time>.txt` if that line gives no usable name.
## Limits
A note holds up to **16 KB** while it is edited. A bigger text file opens read-only in the [Storage App](/guide/storage/); editing a file of any size is planned. In Storage, <kbd>e</kbd> on a text file opens it in the same editor, anywhere on the card, unless the file is read-only. Notes are never offered for deletion by the clean-up.
+42
View File
@@ -0,0 +1,42 @@
+++
title = "Settings"
description = "The device's names, region, screen, sound, GNSS and Wi-Fi, and where firmware updates are found."
weight = 10
[extra]
tag = "Settings"
+++
Move with the arrows. On a toggle, a choice or a slider, left and right change the value; <kbd>Enter</kbd> opens a text field or a list of choices, or a page marked `>`. Back leaves.
| Setting | What it is |
|---|---|
| **Long name**, **Short name** | The names you gave in the first-start Setup (up to 39 bytes, and up to 4 characters) |
| **Region** | The regulatory region; EU868 |
| **Timezone** | For the clock and the dates in file names |
| **Brightness** | A slider |
| **Dim after**, **Screen off after** | Screen timeouts. Dimming must come before the screen turns off |
| **Sound & LED** | The beep and the flash on a [Toast](/guide/basics/) |
| **GNSS** | Switches the receiver on and off (see [GNSS](/guide/gnss/)) |
| **Pause GNSS for LoRa** | Puts the receiver on standby while the radio listens (see [LoRa Scanner](/guide/lora-scanner/)) |
| **Coordinates** | Decimal degrees, or degrees, minutes and seconds |
| **Wi-Fi** | The page below |
| **Check for updates** | Once a day, see [Updates](/guide/updates/) |
| **Firmware** | The page described in [Updates](/guide/updates/) |
| **About** | The firmware version, the node number, battery, memory, uptime, clock and licence |
## Wi-Fi
**Wi-Fi** on its own page switches the radio on and off. The page also has:
- **Status:** whether it is searching, joining, or connected, and to what.
- **Add a network:** scans, lets you pick one, and asks for the password (leave it empty for an open network). **Add a hidden network** asks for the name first.
- **DNS and NTP:** see below.
- **Your saved networks.** The device joins one on its own whenever it is in range, the strongest first. <kbd>Enter</kbd> on a network opens its page.
### An address by hand
A network normally gives the device its address by itself (DHCP, **Automatic**). On a network without DHCP, open its page and set **IP address** to **Fixed**: then it takes an **address**, a **prefix** (24 is 255.255.255.0) and an optional **gateway**. Switching to Fixed starts from what the network is giving the device at that moment, and the setting is checked and applied when you leave the page. IPv4 only. **Forget this network** is on the same page.
### DNS and NTP
Two DNS servers (9.9.9.9 and 1.1.1.1 by default), used on Fixed networks, or on every network if **Always use my DNS** is on; and two NTP servers (pool.ntp.org and time.cloudflare.com), used after any that the network's DHCP offers. <kbd>Enter</kbd> on **Status** shows what is in use and where each value came from.
+47
View File
@@ -0,0 +1,47 @@
+++
title = "Storage"
description = "Browse the SD card: copy, move, rename and delete with a clipboard, and open a file by its type."
weight = 8
[extra]
tag = "Storage"
screens = ["storage.png"]
+++
Storage shows what is on the SD card: each folder's entries with their size and date, folders first. <kbd>Enter</kbd> opens a folder, Back goes up, and <kbd>s</kbd> sorts by name, date or size. A folder with more than 256 entries shows the first 256 by name, and says so.
## Working on one item
| Key | Does |
|---|---|
| <kbd>c</kbd> / <kbd>x</kbd> | Copies or cuts the selected file or folder; the footer shows what <kbd>v</kbd> would paste |
| <kbd>v</kbd> | Pastes it into the folder shown. A copy next to its original is named `name (2).txt`; anything in the way is asked about first |
| <kbd>r</kbd> | Renames |
| <kbd>d</kbd> | Deletes, after saying what is inside: "Delete saved and its 42 files (1.2 MB)?" |
| <kbd>n</kbd> | Makes a folder |
| <kbd>i</kbd> | Details: type, exact size, date, and why an item is read-only if it is |
A copy runs in the background of the card (about 400 KB a second), in short turns, so logs and captures keep being written. It shows its progress, Back cancels it and takes back what was copied, and each file's size is checked afterwards.
## What cannot be changed
- the **top-level folders** the firmware keeps its files in (what is inside them can be);
- `/gemini/cache`;
- a file being **written right now**: today's IRC log, a Track or a capture being recorded.
The App says why when it refuses.
## Opening files
<kbd>Enter</kbd> on a file opens it by its type, and <kbd>Tab</kbd> switches the same file to a hex dump or to text:
- **Text** (`.txt`, `.log`, `.gmi`, `.csv`, and anything that looks like text): only a screenful is read from the card, so a file of any size opens at once, and logs open at the end. Up and down move a line, left and right a page, <kbd>t</kbd> and <kbd>b</kbd> go to the top and the end, and <kbd>e</kbd> edits it (up to 16 KB, as in [Notes](/guide/notes/)).
- **Captures** (`.pcap`): the packets as the [LoRa Scanner](/guide/lora-scanner/) lists them; <kbd>Enter</kbd> shows one with its Meshtastic header and bytes.
- **Tracks** (`.gpx`): the number of points, the start, the duration and the distance.
- **Update files** (`.ota`): the version, and whether the file would install: it is checked as an install checks it, signature and contents, without writing anything. <kbd>Enter</kbd> then installs it (see [Updates](/guide/updates/)).
- **Anything else:** a hex dump.
## Maintenance
At the top of the card, the last row, **Maintenance** (or <kbd>m</kbd>), shows the card's usage and holds **Storage clean-up** and **Erase SD card**. They delete for good, so they sit behind a warning. Clean-up deletes old logs and captures by category and age, showing the space it would free first. Notes and Saved Pages are never offered.
The firmware warns once per start when the card passes **80%** full; past **90%**, logs stop being written so that the rest is kept for captures.
+20
View File
@@ -0,0 +1,20 @@
+++
title = "System"
description = "What the device is doing right now: load, tasks, memory, network traffic, battery and temperature. Live, and read-only."
weight = 9
[extra]
tag = "System"
screens = ["system.png"]
+++
System changes nothing: it shows. It samples once a second and keeps its history only while it is open. <kbd>Tab</kbd> moves between five views.
- **Overview:** each core's load, free memory, network traffic, battery, uptime and chip temperature, and both cores' load over the last two minutes.
- **Tasks:** every task the system runs, with its core, its share of a core over the last second, and the least stack it ever had left (in the warning colour under 512 bytes). <kbd>s</kbd> sorts by share, stack or name.
- **Memory:** free memory, the lowest since start, and the largest free block, with two minutes of free memory drawn against the three memory floors (55, 40 and 20 KB).
- **Network:** the connection, then for IRC, Gemini, the debug console and firmware updates the bytes read and written since start, and what is moving now. For secure connections these are the bytes the service sees, without the encryption overhead.
- **System:** the firmware version, uptime, the last start reason, memory, Wi-Fi, the battery, the SD card with its write faults, the radio and the GNSS receiver.
## Why it exists
The device has about 107 KB of free memory and no PSRAM, so memory is the resource that decides what can run together: a secure connection takes about 52 KB at its peak. System makes that visible, and is how the project measures its own changes.
+44
View File
@@ -0,0 +1,44 @@
+++
title = "Updates"
description = "How the device updates itself from the project's releases, from the SD card or from a PC, and how it protects itself when an update goes wrong."
weight = 11
[extra]
tag = "Firmware"
screens = ["update.png"]
+++
Every update is one **signed file** (`.ota`). The device installs only a file signed with the project's key, so it cannot be tricked into installing anything else, whichever way the file arrives.
## Settings → Firmware
The page shows the **version** running, its **status**, the address to **push updates to** over Wi-Fi, and:
- **Latest release:** <kbd>Enter</kbd> (or <kbd>c</kbd>) asks the server and says `v0.11.0 (new)` or `(current)`. <kbd>Enter</kbd> again opens the release: its version, date, size and the tag's message, with **Install** when it is newer.
- **Older releases:** the last ten, newest first. Opening an older one offers to **go back** to it, with a different question.
- **On the SD card:** the `.ota` files found in `/updates`, ready to install. Copy one there with a computer or the [Storage App](/guide/storage/); the Storage App also opens an `.ota` file and says whether it would install.
An install needs Wi-Fi if it is a download. The new firmware is checked before anything is written (the signature after the first 160 bytes) and again at the end (the image's hash). Then the device restarts. It will **wait up to 60 seconds** if you are typing, so that a restart never eats a note.
## Check for updates
**Settings → Check for updates** is on by default. Once a day, with Wi-Fi up and the clock set, the device looks at the latest release and says `v0.11.0 is out: see Settings > Firmware`, once per version. It installs **nothing** by itself, and it does not announce a version that already failed and rolled back on this device.
## Probation and Rollback
A newly installed firmware runs on **Probation**: it must boot, draw its screen, start its services and run 30 seconds without a crash, and reconnect Wi-Fi if that is configured (within 3 minutes), before it is confirmed for good. If it crashes or restarts, or cannot get Wi-Fi back, the device **rolls back** to the previous firmware by itself and says so.
## Safe Mode
If a confirmed firmware crashes and restarts **3 times in a row**, the device starts in **Safe Mode** instead: only Wi-Fi and firmware updates, so a fix can be installed without a cable. A normal restart leaves it.
## IRC steps aside
A secure connection takes about 52 KB of memory at its peak, and IRC's own takes about 40 KB of the 107 KB there is. So a check or an install that you ask for makes IRC disconnect for the few seconds it takes, and reconnect afterwards. The **daily check never does that**: with IRC connected it waits for a moment when IRC is not, so if IRC stays connected for days the daily check does not run, and **Latest release** is the way to check.
## How the connection is trusted
The connection to the project's server is checked against the two root certificates that Let's Encrypt's chains end in, not against the usual bundle of about 130 authorities. Whatever the connection, the update file's own signature decides what gets installed.
## For developers
Updates can also be pushed from a PC over Wi-Fi, with `scripts/flash.sh --ota <ip>`, or put on the card with `scripts/sd_put.sh`: see the [README](https://git.twis.la/twisla/roro9stack#firmware-updates-over-wi-fi-ota). **Debug Builds** show the latest release but do not install it, because a release has no debug console: update a Debug Build from the PC.
+36
View File
@@ -0,0 +1,36 @@
+++
title = "Wi-Fi tools"
description = "See the networks around you, which Wi-Fi channels are busy, and follow one network's signal as you move."
weight = 6
[extra]
tag = "Wi-Fi tools"
+++
The Wi-Fi tools show what the radio hears **now**: they scan for the networks around you and list them. They do not join anything.
## The menu
Three entries: **Networks nearby**, **Channel occupancy** and **Signal tracker**. Pick one with the arrows and press <kbd>Enter</kbd>.
## Networks nearby
A list that refreshes by itself: each network's name (or `(hidden)`), channel, signal in dBm and security. The top line says how it is sorted and filtered.
| Key | Does |
|---|---|
| <kbd>s</kbd> | Sorts by signal, channel or name, in turn |
| <kbd>o</kbd> | Shows only open networks |
| <kbd>h</kbd> | Hides the hidden networks |
| <kbd>w</kbd> | Shows only the strong ones |
| <kbd>l</kbd> | Starts and stops **logging** |
| <kbd>Enter</kbd> | Opens the **Signal tracker** on that network |
**Logging** adds each scan to `/wifi/scans/<date>.csv` on the SD card, one row per network, once the device knows the date; `LOG` and a row count show while it runs. Logs stop when the card passes 90% full.
## Channel occupancy
One bar for each of the 13 Wi-Fi channels shows how busy it is: how many networks use it, and how strong they are. The top line counts the networks and names the quietest of the three channels that don't overlap, **1, 6 and 11**. It is the quick answer to "which channel should my router use?". <kbd>`</kbd> (Back) returns to the menu.
## Signal tracker
Follows one network: its name, address and channel, then the signal in dBm in large type, a strength bar, and a graph of the recent readings, so you can walk towards the strongest signal. <kbd>m</kbd> turns audible clicks on and off. If the network is no longer heard the screen says `lost`. <kbd>`</kbd> (Back) returns to the list.
+10
View File
@@ -232,3 +232,13 @@ footer small { display: block; max-width: 760px; }
.accent-link { color: var(--cyantext); text-decoration: underline; text-underline-offset: 4px; }
.page > header { display: flex; flex-direction: column; gap: 12px; }
.page .lead { max-width: 720px; }
/* The user guide */
.pager { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.card h2 { font-size: 24px; line-height: 28px; }
.card h2 a { color: inherit; text-decoration: none; }
.card h2 a:hover, .card h2 a:focus-visible { text-decoration: underline; text-underline-offset: 4px; }
.eyebrow a { color: inherit; }
.prose kbd { font: 500 13px/16px var(--mono); background: var(--s2); padding: 1px 6px; border: 1px solid var(--line); }
.prose .note { background: var(--s2); padding: 16px 20px; }
.prose h3 { margin-top: 12px; }
+1
View File
@@ -29,6 +29,7 @@
<nav aria-label="Main">
<a href="/#apps">Apps</a>
<a href="/install/">Install</a>
<a href="/guide/">Guide</a>
<a href="/downloads/">Downloads</a>
<a href="{{ config.extra.blog }}">Blog</a>
<button class="link-btn js-only" id="theme-toggle" type="button">Light</button>
+24
View File
@@ -0,0 +1,24 @@
{% extends "base.html" %}
{% block title %}{{ section.title }}: roro9stack{% endblock %}
{% block description %}{{ section.description }}{% endblock %}
{% block main %}
<div class="wrap page">
<header>
<span class="eyebrow">User guide</span>
<h1>{{ section.title }}</h1>
<p class="lead">{{ section.description }}</p>
</header>
<div class="prose">{{ section.content | safe }}</div>
<div class="cards">
{% for p in section.pages %}
<article class="card n-lg">
<div class="card-top"><span class="tag">{{ p.extra.tag }}</span><span class="num">{% if loop.index < 10 %}0{% endif %}{{ loop.index }}</span></div>
<h2><a href="{{ p.permalink }}">{{ p.title }}</a></h2>
<p>{{ p.description }}</p>
</article>
{% endfor %}
</div>
</div>
{% endblock main %}
+31
View File
@@ -0,0 +1,31 @@
{% extends "base.html" %}
{% block title %}{{ page.title }}: roro9stack user guide{% endblock %}
{% block description %}{{ page.description }}{% endblock %}
{% block main %}
{% set screens = load_data(path="data/screens.toml", format="toml") %}
<div class="wrap page">
<header>
<span class="eyebrow"><a href="/guide/">User guide</a>{% if page.extra.tag %} · {{ page.extra.tag }}{% endif %}</span>
<h1>{{ page.title }}</h1>
<p class="lead">{{ page.description }}</p>
</header>
{% if page.extra.screens %}
<div class="shots">
{% for s in screens.screen %}{% if s.file in page.extra.screens %}
<figure class="shot n-md">
<img src="/screens/{{ s.file }}" width="480" height="270" alt="{{ s.alt }}" loading="lazy">
<figcaption>{{ s.caption }}</figcaption>
</figure>
{% endif %}{% endfor %}
</div>
{% endif %}
<div class="prose">{{ page.content | safe }}</div>
<nav class="pager" aria-label="User guide">
{% if page.lower %}<a class="accent-link" href="{{ page.lower.permalink }}">← {{ page.lower.title }}</a>{% else %}<span></span>{% endif %}
{% if page.higher %}<a class="accent-link" href="{{ page.higher.permalink }}">{{ page.higher.title }} →</a>{% endif %}
</nav>
</div>
{% endblock main %}
+1 -1
View File
@@ -68,7 +68,7 @@
</article>
{% endfor %}
</div>
<p class="cards-note">Plus Settings, with its Wi-Fi and Firmware pages.</p>
<p class="cards-note">Plus Settings, with its Wi-Fi and Firmware pages. Every App has a page in the <a class="accent-link" href="/guide/">user guide</a>.</p>
</section>
<section class="wrap" id="screens" aria-labelledby="screens-title">