Site: how-tos and the FAQ (phase 3)
Site / build (pull_request) Successful in 8s

Eight how-to recipes and a FAQ page with a list of its questions, from the
README, the milestone documents and the Apps' source. The guide templates
become generic (the parent section gives the eyebrow, title and pager).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-06 21:00:34 +02:00
co-authored by Claude Sonnet 5.5
parent 2729f2e218
commit ba10ff4a5d
14 changed files with 296 additions and 4 deletions
+7
View File
@@ -98,3 +98,10 @@ Changed before it ships:
- **The CI split on a change that touches only `site/` or `docs/`.** This pull request touches both the workflows and the site, so it runs both; the first docs-only pull request will show it. - **The CI split on a change that touches only `site/` or `docs/`.** This pull request touches both the workflows and the site, so it runs both; the first docs-only pull request will show it.
- Firefox and Safari rendering, screen readers, and a printed page. - Firefox and Safari rendering, screen readers, and a printed page.
- The unverified wording in the Install text is kept to what's known: nothing about how long flashing takes, or what the screen shows in download mode. - The unverified wording in the Install text is kept to what's known: nothing about how long flashing takes, or what the screen shows in download mode.
## As built (phase 3, how-tos and the FAQ)
- **`/howto/`** has eight short recipes: when flashing fails, find your files on the SD card, install an update from the card, use a network without DHCP, record a Track, capture LoRa packets for Wireshark, read Gemini pages offline, and what to do when a connection says "not enough memory". **`/faq/`** is one page of questions with a list at the top. Both use the guide's templates (`guide-index.html`, `guide-page.html`, now generic: the page's parent section gives the eyebrow, the title and the pager).
- **The FAQ starts from the README** and from the problems the project met (Q183): the flash troubles and the memory limit are the two that were hit most. The issues labelled `kind/docs` turned out to be design rounds for the mesh, not user questions, so they gave nothing to answer.
- **Every step comes from the README, the milestone documents or the Apps' source.** The privacy answer says plainly that the device contacts the project's server once a day for the update check (on by default, one switch to turn it off).
- **Linked from the guide's index,** not the navigation, which stays short.
+76
View File
@@ -0,0 +1,76 @@
+++
title = "Questions and answers"
description = "What the firmware does and does not do today, what it talks to over the network, and how to run, update and fix it."
template = "guide-page.html"
[extra]
toc = true
+++
## Can I send messages over the mesh?
**Not yet.** The LoRa Scanner **listens** to Meshtastic traffic and shows what it hears, but nothing is transmitted. A mesh messenger is the goal and is planned in two milestones, one for receiving and one for transmitting; it waits for a second node to test against.
## Is this Meshtastic?
No. roro9stack is its own firmware, written from scratch, which aims to be **compatible** with Meshtastic on the air. Today that means the Scanner can read a Meshtastic packet's header. The project isn't affiliated with or endorsed by Meshtastic or M5Stack.
## Does it ever transmit on LoRa?
No. The radio is receive-only in the current firmware. Nothing will transmit until you have confirmed your region in Settings, and the region's limits (frequencies, power, duty cycle) will bound anything that does.
## What do I need?
- an **M5Stack Cardputer ADV**: that is the device the firmware is built and tested for;
- the **Cap LoRa-1262**, for the LoRa Scanner and GNSS only: the other Apps do not need it;
- a **microSD card**, for notes, logs, tracks, captures and saved pages;
- **Wi-Fi** (2.4 GHz) for IRC, Gemini and updates.
## Which regions are supported?
**EU868** only, today. Setup asks for your region once, and the radio does not transmit until you confirm it.
## Why do I need Chrome or Edge to install it?
The browser flasher uses Web Serial, which Chrome and Edge have on desktop and Firefox and Safari don't. Without it, the **esptool** steps on the [Install page](/install/) flash the same file from a terminal.
## How do I update it?
Three ways, all with the same signed update file: from the project's releases on the device itself (**Settings → Firmware**), from the SD card, or pushed over Wi-Fi from a PC. See [Updates](/guide/updates/) and [Install an update from the SD card](/howto/update-from-sd/).
## Is it safe to update? What if it goes wrong?
A new firmware runs on **Probation**: if it crashes, restarts, or cannot get Wi-Fi back within 3 minutes, the device returns to the previous version by itself. If a confirmed firmware crashes 3 times in a row, it starts in **Safe Mode** with only Wi-Fi and updates, so a fix can be installed without a cable. And an update that is not signed with the project's key is refused before anything is written.
## Can I run my own build?
Yes: it is [open source](https://git.twis.la/twisla/roro9stack) (GPL-3.0). The device only accepts updates signed with the key that its firmware was built with, so a build of your own, signed with your own key, is flashed once over USB (the README explains it); after that, your own updates go over Wi-Fi or the card. Releases from this project are signed with the project's key.
## Does it phone home? What about privacy?
**The firmware contacts the project's server once a day,** to see whether a new release exists: **Settings → Check for updates**, on by default, only when Wi-Fi is up and the clock is set. It installs nothing by itself, and you can switch it off. Besides that, the device talks to what you ask it to: the IRC server and Gemini capsules you open, DNS servers (9.9.9.9 and 1.1.1.1 by default) and time servers (pool.ntp.org and time.cloudflare.com by default), all of which you can change. There is no account, no analytics and no telemetry.
**This website** sets no cookies and has no analytics, loads nothing from other sites, and its server logs keep only a masked part of visitors' addresses. The Install page asks the project's own server for the latest release.
## Why does IRC disconnect when I update, or when I open a Gemini page?
A secure connection takes about 52 KB of the 107 KB the device has, and IRC's takes about 40 KB. Both together do not always fit. See [When a connection says "not enough memory"](/howto/not-enough-memory/).
## Do I need an SD card?
For the radio, GNSS position, Wi-Fi tools, IRC chat and Gemini browsing, no. For anything that is *kept*, yes: notes, IRC logs, Wi-Fi scan logs, GNSS Tracks, LoRa captures, saved Gemini pages and update files. See [Find your files on the SD card](/howto/sd-files/).
## How big can a note be?
Up to 16 KB while it is edited. A larger text file opens read-only in the [Storage App](/guide/storage/). Editing a text file of any size is planned.
## Why can't I rename or delete some folders?
The firmware keeps its files in the top-level folders of the card, and `/gemini/cache` and any file being written right now are protected. What is inside the top-level folders can be changed. The Storage App says why when it refuses.
## The upload can't connect, or the port is missing
Try a data cable, put the Cardputer in download mode (hold **G0** while plugging in USB), and see [When flashing fails](/howto/flash-fails/).
## Something is wrong, or missing from this site
Write to the [issue tracker](https://git.twis.la/twisla/roro9stack/issues) or to contact@roro9stack.net. Security problems: the address in the site's `security.txt`, and please not in the public tracker.
+2
View File
@@ -10,4 +10,6 @@ This guide says what the firmware does **today** and nothing else. Start with th
**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. **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.
Looking for a recipe or a quick answer? There are [how-tos](/howto/) and a [FAQ](/faq/).
Something missing or wrong? Write to the [issue tracker](https://git.twis.la/twisla/roro9stack/issues) or to contact@roro9stack.net. Something missing or wrong? Write to the [issue tracker](https://git.twis.la/twisla/roro9stack/issues) or to contact@roro9stack.net.
+12
View File
@@ -0,0 +1,12 @@
+++
title = "How-tos"
description = "Short recipes for things you will want to do: put a file on the card, record a track, capture radio packets, and what to try when something does not work."
template = "guide-index.html"
page_template = "guide-page.html"
sort_by = "weight"
[extra]
eyebrow = "How-tos"
+++
Each recipe is a handful of steps and says what you should see after each one. For what a screen or a key does, the [user guide](/guide/) has a page for every App; for quick answers, there is the [FAQ](/faq/).
+35
View File
@@ -0,0 +1,35 @@
+++
title = "When flashing fails"
description = "What to try when the browser or esptool cannot find the Cardputer, cannot connect, or loses the connection halfway."
weight = 1
[extra]
tag = "Install"
+++
## 1. Use a cable that carries data
A charge-only USB-C cable powers the device but never shows up as a serial port. If a cable has worked for data before, use that one.
## 2. Put the Cardputer in download mode
Hold **G0**, the button next to the screen, while you plug in the USB cable, or while you press the reset button. Then try again. This is the first thing to try when an upload "can't connect".
## 3. Browser flashing: Chrome or Edge, on a desktop
The Install page flashes through the browser's Web Serial feature, which Firefox and Safari don't have. In Chrome or Edge, the browser asks you to pick the serial port from a list: pick the one that appears when you plug the Cardputer in. If the list is empty, go back to steps 1 and 2.
## 4. Linux: "permission denied" on the port
Your user needs access to serial devices. Run this once, then log out and back in:
```
sudo usermod -aG dialout "$USER"
```
## 5. In a virtual machine
USB passthrough can fail with `OSError: [Errno 71] Protocol error`. Retrying alone doesn't help; **resetting the Cardputer does**, and putting it in download mode moves the serial port to a new name, so pick the port again afterwards.
## Still stuck?
The **esptool** steps on the [Install page](/install/) flash the same file without a browser, and their error messages are more detailed. If it still fails, open an [issue](https://git.twis.la/twisla/roro9stack/issues) and say what you see.
+22
View File
@@ -0,0 +1,22 @@
+++
title = "Read Gemini pages offline"
description = "Save pages to the SD card, and read them later with Wi-Fi off."
weight = 7
[extra]
tag = "Gemini"
+++
You need an **SD card**.
1. **Open the page** in the [Gemini App](/guide/gemini/).
2. **Press <kbd>s</kbd>** to save it. It is kept with the address it came from and the time it was saved. Saving the same page again replaces it, and says so.
3. **To keep a whole capsule corner,** press <kbd>S</kbd> instead: it saves the page and the pages it links to on the **same capsule**, as text only, up to 30, in the background.
4. **Later, offline:** open the Gemini App. The start page lists **Saved Pages**, newest first, grouped by capsule, below your bookmarks. They open with no network.
5. **Links inside a saved page** open the saved copy when there is one. Any other link needs Wi-Fi, or says `not saved, offline`.
## Keeping it tidy
- <kbd>r</kbd> **refreshes** a saved page from the web, and <kbd>d</kbd> **deletes** it.
- **Bookmarks** (<kbd>b</kbd>) are a list of addresses, not copies: they need Wi-Fi to open.
- Saved Pages are never offered for deletion by the clean-up. They are in `/gemini/saved/` if you want to move them.
- A file that is not text (a picture, say) is saved to `/gemini/downloads/` and cannot be shown on the device.
+20
View File
@@ -0,0 +1,20 @@
+++
title = "Capture LoRa packets and open them in Wireshark"
description = "Record what the radio hears into a pcap file, then read it on a computer."
weight = 6
[extra]
tag = "LoRa Scanner"
+++
The radio only **listens**: nothing is transmitted. You need the **Cap LoRa-1262** and an **SD card**.
1. **Open the LoRa Scanner.** The Status Bar shows `L` while the radio listens.
2. **Pick a preset with <kbd>p</kbd>.** The Scanner offers the 7 Meshtastic presets allowed in EU868; LongFast is the default.
3. **Wait for packets.** Each line is a packet: the time, RSSI and SNR, and for a Meshtastic packet the sender, the receiver and the hops. <kbd>Enter</kbd> shows a packet's header and its bytes.
4. **Press <kbd>c</kbd> to start a capture.** The Status Bar shows `CAP`. It keeps recording with the App closed.
5. **Press <kbd>c</kbd> again to stop.**
6. **Get the file.** It is in `/captures/lora/`, a `.pcap` with LoRaTap headers. In the [Storage App](/guide/storage/) you can already look at its packets; on a computer, open it in Wireshark.
**What you will and will not see.** Meshtastic's header is never encrypted, so who sent a packet and how far it hopped is visible. The message itself is encrypted with the channel's key, and the Scanner does not decrypt it.
**Hearing nothing is normal** if no node is in range, or if the preset does not match what the nodes nearby use. The [Sweep](/guide/lora-scanner/) (<kbd>Tab</kbd>) shows whether anything is on the air at all across 863 to 870 MHz.
+22
View File
@@ -0,0 +1,22 @@
+++
title = "Use a network without DHCP"
description = "Give the device a fixed IP address, and your own DNS and time servers, on a network that does not hand out addresses."
weight = 4
[extra]
tag = "Wi-Fi"
+++
1. **Add the network first.** In **Settings → Wi-Fi**, choose **Add a network**, pick it and type the password. (A hidden network has its own entry, which asks for the name first.)
2. **Open the network's page:** <kbd>Enter</kbd> on it in the list of saved networks.
3. **Set *IP address* to *Fixed*.** The address, the prefix and the gateway start from what the network is giving the device at that moment, so you only change what is wrong.
- **Address:** four numbers, like `10.39.39.13`.
- **Prefix:** 1 to 30. 24 is 255.255.255.0.
- **Gateway:** optional; empty means none.
4. **Leave the page.** The setting is checked and applied then; a bad address is refused with the reason.
5. **Check it.** Back in **Settings → Wi-Fi**, <kbd>Enter</kbd> on **Status** shows the address, the mask, the gateway, the DNS and NTP servers, and where each came from.
## DNS and time
**DNS and NTP** on the Wi-Fi page holds two DNS servers (9.9.9.9 and 1.1.1.1 by default) and two NTP servers (pool.ntp.org and time.cloudflare.com). They are used on Fixed networks, or on every network if **Always use my DNS** is on. Leave a second server empty if you only have one. IPv4 only.
To go back, set *IP address* to *Automatic*. **Forget this network** is at the bottom of the same page.
+25
View File
@@ -0,0 +1,25 @@
+++
title = "When a connection says \"not enough memory\""
description = "The device has about 107 KB to share, and a secure connection takes about 52 KB. How to free some, and why it happens."
weight = 8
[extra]
tag = "Memory"
+++
## What you see
Opening a Gemini page fails with *not enough memory: stop IRC or retry*. Or an update check or install makes IRC disconnect for a moment.
## What to do
1. **Stop IRC.** In the IRC App, type `/quit` and press <kbd>Enter</kbd>. IRC disconnects and **stays** disconnected until you type something again, so it does not take the memory back.
2. **Try again.** A Gemini fetch needs 55 KB free before it starts.
3. **Start IRC again** when you are done: type a line in the IRC App, and it reconnects and rejoins its channels.
## See it
The [System App](/guide/system/)'s **Memory** view shows free memory, the lowest since the device started, and the largest free block, drawn against the three memory floors (55, 40 and 20 KB). Watch it fall when a connection opens, and recover when it closes.
## Why it happens
The Cardputer's chip has no extra memory (no PSRAM). A secure (TLS) connection costs about **52 KB** at its peak, and IRC's own connection holds about 40 KB of the 107 KB there is. A second secure connection on top does not fit, so the firmware refuses it early instead of crashing. This is also why IRC steps aside during an update, and why the daily update check waits until IRC is not connected: see [Updates](/guide/updates/).
+18
View File
@@ -0,0 +1,18 @@
+++
title = "Record a Track and open it"
description = "Record where you have been with the GNSS receiver, and open the file in a mapping tool."
weight = 5
[extra]
tag = "GNSS"
+++
You need the **Cap LoRa-1262**, an **SD card**, and a place with a view of the sky.
1. **Switch the receiver on:** **Settings → GNSS** to *On*. The Status Bar shows `G` once it is searching.
2. **Open the GNSS App** and wait for a fix. The first line says `Searching: n in view` and how long it has been, then `3D Fix, n of m satellites`. The first fix outdoors can take a while.
3. **The clock must be set.** A fix sets it, and so does Wi-Fi. A Track will not start without one: the App says `Waiting for the time`.
4. **Press <kbd>r</kbd>.** The bottom line says `REC`, with the number of points and how long it has been going, and the Status Bar shows `REC`. You can leave the App: the Track keeps recording.
5. **Press <kbd>r</kbd> again to stop,** in the GNSS App.
6. **Open it.** In the [Storage App](/guide/storage/), go to `/gnss/tracks` and open the `.gpx` file: it shows the number of points, the start, the duration and the distance. To see the route on a map, take the card to a computer and open the file in any GPX viewer or mapping tool.
If *Pause GNSS for LoRa* is on, the receiver stays awake while a Track is being recorded, so recording is not interrupted by the radio.
+28
View File
@@ -0,0 +1,28 @@
+++
title = "Find your files on the SD card"
description = "Where the notes, tracks, captures, logs and saved pages are written, so you can take the card to a computer and use them."
weight = 2
[extra]
tag = "SD card"
+++
Everything the firmware writes goes in a folder at the top of the card. Switch the Cardputer off, take the card out and read it in a computer; or look at the same folders in the [Storage App](/guide/storage/).
| What | Where | Kind of file |
|---|---|---|
| Notes | `/notes` | `.txt`, named after the first line |
| GNSS Tracks | `/gnss/tracks` | `.gpx`, named by date and time |
| LoRa captures | `/captures/lora` | `.pcap` |
| IRC logs | `/irc` | one text file per buffer and day |
| Wi-Fi scan logs | `/wifi/scans` | `<date>.csv` |
| Gemini Saved Pages | `/gemini/saved/<host>/…` | `.gmi` |
| Gemini bookmarks | `/gemini/bookmarks.gmi` | gemtext |
| Files saved from Gemini that are not text | `/gemini/downloads` | whatever they were |
| Update files | `/updates` | `.ota` |
## Rules worth knowing
- **Eject first.** A file being written (today's IRC log, a Track or a capture being recorded) is not complete until it stops. Stop it, or switch the device off, before you take the card out.
- **Notes are yours.** You can edit them on a computer and they show up on the device; the clean-up never offers them for deletion.
- **Don't rename the top-level folders.** The firmware looks for them by name.
- **Logs stop at 90% full,** so the remaining space is kept for captures. The [Storage App](/guide/storage/)'s Maintenance shows what is using the card and clears old logs and captures, after showing what it would free.
+15
View File
@@ -0,0 +1,15 @@
+++
title = "Install an update from the SD card"
description = "Update the firmware with no Wi-Fi and no cable: copy one file onto the card and install it from the device."
weight = 3
[extra]
tag = "Updates"
+++
1. **Get the update file.** On the [Downloads page](/downloads/), take the `.ota` file of the release you want: `roro9stack-<version>.ota`. Every release has one.
2. **Copy it to `/updates`** on the SD card, from a computer. If the folder is not there, create it. (With the Cardputer on USB, a developer can also send it with `scripts/sd_put.sh`; see the [README](https://git.twis.la/twisla/roro9stack#firmware-updates-over-wi-fi-ota).)
3. **Put the card in the Cardputer.** Open **Settings → Firmware.** Under *On the SD card* the file is listed. If it says `No .ota files in /updates`, the name or the folder is wrong.
4. **Press Enter on the file,** then **Install**. The device checks the signature and the contents before it writes anything, installs, and restarts. It waits up to 60 seconds if you are typing.
5. **After the restart** the new firmware is on **Probation**: if it crashes or cannot get Wi-Fi back within 3 minutes, the device goes back to the previous version by itself and says so.
You can also open the file in the [Storage App](/guide/storage/): it shows the version and whether the file would install, then <kbd>Enter</kbd> installs it. A file that is not signed with the project's key is refused, and nothing changes.
+1 -1
View File
@@ -4,7 +4,7 @@
{% block main %} {% block main %}
<div class="wrap page"> <div class="wrap page">
<header> <header>
<span class="eyebrow">User guide</span> <span class="eyebrow">{% if section.extra.eyebrow %}{{ section.extra.eyebrow }}{% else %}User guide{% endif %}</span>
<h1>{{ section.title }}</h1> <h1>{{ section.title }}</h1>
<p class="lead">{{ section.description }}</p> <p class="lead">{{ section.description }}</p>
</header> </header>
+13 -3
View File
@@ -1,11 +1,13 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}{{ page.title }}: roro9stack user guide{% endblock %} {% block title %}{% set parent_path = page.ancestors | last %}{% set parent = get_section(path=parent_path) %}{{ page.title }}: roro9stack {% if parent_path == "_index.md" %}FAQ{% else %}{{ parent.title | lower }}{% endif %}{% endblock %}
{% block description %}{{ page.description }}{% endblock %} {% block description %}{{ page.description }}{% endblock %}
{% block main %} {% block main %}
{% set parent_path = page.ancestors | last %}
{% set parent = get_section(path=parent_path) %}
{% set screens = load_data(path="data/screens.toml", format="toml") %} {% set screens = load_data(path="data/screens.toml", format="toml") %}
<div class="wrap page"> <div class="wrap page">
<header> <header>
<span class="eyebrow"><a href="/guide/">User guide</a>{% if page.extra.tag %} · {{ page.extra.tag }}{% endif %}</span> <span class="eyebrow">{% if parent_path != "_index.md" %}<a href="{{ parent.permalink }}">{{ parent.title }}</a>{% else %}FAQ{% endif %}{% if page.extra.tag %} · {{ page.extra.tag }}{% endif %}</span>
<h1>{{ page.title }}</h1> <h1>{{ page.title }}</h1>
<p class="lead">{{ page.description }}</p> <p class="lead">{{ page.description }}</p>
</header> </header>
@@ -21,11 +23,19 @@
</div> </div>
{% endif %} {% endif %}
{% if page.extra.toc %}
<nav class="prose toc" aria-label="Questions">
<ul>{% for h in page.toc %}<li><a class="accent-link" href="{{ h.permalink }}">{{ h.title }}</a></li>{% endfor %}</ul>
</nav>
{% endif %}
<div class="prose">{{ page.content | safe }}</div> <div class="prose">{{ page.content | safe }}</div>
<nav class="pager" aria-label="User guide"> {% if page.lower or page.higher %}
<nav class="pager" aria-label="{{ parent.title }}">
{% if page.lower %}<a class="accent-link" href="{{ page.lower.permalink }}">← {{ page.lower.title }}</a>{% else %}<span></span>{% endif %} {% 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 %} {% if page.higher %}<a class="accent-link" href="{{ page.higher.permalink }}">{{ page.higher.title }} →</a>{% endif %}
</nav> </nav>
{% endif %}
</div> </div>
{% endblock main %} {% endblock main %}