From a4619aa17dfb88c3a3bc77c68ea02ebaa81ff13c Mon Sep 17 00:00:00 2001 From: Jean-Pierre Martineau Date: Sun, 4 Oct 2026 11:42:51 -0400 Subject: [PATCH] The docs are in folders, and the Cardputer Zero has a page docs/ was one flat folder: the user guides, the CLI manual, the device manuals, the code notes and an old survey side by side. It is now guide/ (features, configuration, connecting, terminals), cli/ (the manual as its README, the cookbook), devices/ (which manual is yours as its README, then one per device) and development/ (architecture, the survey); screenshots/ stays put. The index follows the folders. The device manuals are named for the device they are about: the PicoCalc's is picocalc-lyra.md, since MeshTerm runs on the PicoCalc's Luckfox Lyra core and on none of the others, and the uConsole's stays uconsole.md, since its CM4 and CM5 run it alike. A new cardputer-zero.md opens on a warning that the device is not supported yet, says what has been built and tested only in the simulator, and how to try it there. Every relative link was resolved from where it was written and rewritten from where it now lives, and the repo-root spellings in code, config and the release skill moved with them; the changelog's GitHub URLs too. Co-Authored-By: Claude Opus 5.5 (1M context) --- .claude/skills/release/SKILL.md | 4 +- CHANGELOG.md | 2 +- README.md | 43 +++++---- config.example.toml | 2 +- docs/README.md | 41 +++++--- docs/{cli.md => cli/README.md} | 6 +- docs/{ => cli}/cookbook.md | 10 +- docs/{ => development}/architecture.md | 4 +- .../survey-ui-consistency.md | 52 +++++----- docs/devices/README.md | 73 ++++++++++++++ docs/devices/cardputer-zero.md | 96 +++++++++++++++++++ .../{picocalc.md => devices/picocalc-lyra.md} | 0 docs/{ => devices}/uconsole.md | 2 +- docs/{ => guide}/configuration.md | 12 +-- docs/{ => guide}/connecting.md | 4 +- docs/{ => guide}/features.md | 2 +- docs/{ => guide}/terminals.md | 0 docs/hardware.md | 72 -------------- meshterm/cli.py | 2 +- meshterm/context.py | 2 +- pyproject.toml | 2 +- scripts/README.md | 2 +- scripts/picocalc/xiao-radio/README.md | 2 +- 23 files changed, 273 insertions(+), 162 deletions(-) rename docs/{cli.md => cli/README.md} (99%) rename docs/{ => cli}/cookbook.md (93%) rename docs/{ => development}/architecture.md (84%) rename docs/{ => development}/survey-ui-consistency.md (81%) create mode 100644 docs/devices/README.md create mode 100644 docs/devices/cardputer-zero.md rename docs/{picocalc.md => devices/picocalc-lyra.md} (100%) rename docs/{ => devices}/uconsole.md (99%) rename docs/{ => guide}/configuration.md (94%) rename docs/{ => guide}/connecting.md (97%) rename docs/{ => guide}/features.md (99%) rename docs/{ => guide}/terminals.md (100%) delete mode 100644 docs/hardware.md diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index e363e12..500e5cc 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -125,11 +125,11 @@ types it. `github-release.yml` now attaches a **version-less copy of every binary** beside the stamped one, so `releases/latest/download/meshterm-macos-arm64` is a URL that never moves — -which is what the README and `docs/uconsole.md` use. Nothing in either names a version. +which is what the README and `docs/devices/uconsole.md` use. Nothing in either names a version. Leave them alone, and if you find yourself adding a version to an install command, add an alias to the gather step instead. -What *does* still carry the number: `docs/cli.md` has a sample document with a `"version"` +What *does* still carry the number: `docs/cli/README.md` has a sample document with a `"version"` field, which is the live `{version}` placeholder and must show the new one. ## 5. Commit, tag, push diff --git a/CHANGELOG.md b/CHANGELOG.md index a6386a2..d1289bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -161,7 +161,7 @@ companion pairs on Linux, and a connection that fails says which step failed and refusal a wrong PIN. A USB port says whether it is missing, in use by another program (ModemManager, on Linux), or not yours to open (the `dialout` group). Every message, and what to do about it, is in - [When a companion won't connect](https://github.com/jpmartineau/MeshTerm/blob/main/docs/connecting.md). + [When a companion won't connect](https://github.com/jpmartineau/MeshTerm/blob/main/docs/guide/connecting.md). - **Clock sync explains a radio whose clock is ahead, instead of calling it malformed.** MeshCore firmware never sets its clock back, so once a radio's clock ran ahead of the computer's — a GPS fix, another app, drift — every sync was refused and logged as "the diff --git a/README.md b/README.md index f240796..d14193e 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ install Microsoft's [Cascadia Mono PL](https://github.com/microsoft/cascadia-cod for you — no administrator rights, nothing downloaded. Preferences → Display → Console setup turns all of this off if you'd rather stay put. -[Terminals, icons, and the Windows console](docs/terminals.md) explains why any of it is +[Terminals, icons, and the Windows console](docs/guide/terminals.md) explains why any of it is necessary. > **Trying a build without touching your real data.** MeshTerm keeps everything in @@ -196,18 +196,18 @@ Setting up for development instead? That's in [CONTRIBUTING.md](CONTRIBUTING.md) ## Documentation -**[The documentation index](docs/README.md)** lists everything, including the two code -surveys kept as a historical record. The short version: +**[The documentation index](docs/README.md)** lists everything, including the code survey +kept as a historical record. The short version: | Read | For | | --- | --- | -| **[The command line](docs/cli.md)** | Every subcommand and option, what each prints on the plain and JSON faces, the exit statuses, recipes. | -| **[What MeshTerm does](docs/features.md)** | Every screen the menu offers, and the command that does the same job without it. | -| [The CLI cookbook](docs/cookbook.md) | Common one-liners, by the thing you're trying to do. | -| [Configuring MeshTerm](docs/configuration.md) | Preferences, device profiles, and everything kept under `~/.meshterm`. | -| [When a companion won't connect](docs/connecting.md) | Bluetooth pairing on each system, finding the PIN, USB permissions, and every connection error, with what to do about it. | -| [MeshTerm on hardware](docs/hardware.md) | Which handheld manual is yours — the [PicoCalc](docs/picocalc.md) build, or the [uConsole](docs/uconsole.md), whose LoRa chip MeshTerm can drive directly. | -| [How the code is laid out](docs/architecture.md) | The layering, and where a new feature goes. | +| **[The command line](docs/cli/README.md)** | Every subcommand and option, what each prints on the plain and JSON faces, the exit statuses, recipes. | +| **[What MeshTerm does](docs/guide/features.md)** | Every screen the menu offers, and the command that does the same job without it. | +| [The CLI cookbook](docs/cli/cookbook.md) | Common one-liners, by the thing you're trying to do. | +| [Configuring MeshTerm](docs/guide/configuration.md) | Preferences, device profiles, and everything kept under `~/.meshterm`. | +| [When a companion won't connect](docs/guide/connecting.md) | Bluetooth pairing on each system, finding the PIN, USB permissions, and every connection error, with what to do about it. | +| [MeshTerm on hardware](docs/devices/README.md) | Which handheld manual is yours — the [PicoCalc](docs/devices/picocalc-lyra.md) build, or the [uConsole](docs/devices/uconsole.md), whose LoRa chip MeshTerm can drive directly. The [Cardputer Zero](docs/devices/cardputer-zero.md) is **not supported yet**. | +| [How the code is laid out](docs/development/architecture.md) | The layering, and where a new feature goes. | | [CONTRIBUTING.md](CONTRIBUTING.md) | The development setup and the house rules. | | [CHANGELOG.md](CHANGELOG.md) | What changed, newest first. | @@ -237,15 +237,16 @@ meshterm specimen ``` Putting MeshTerm on a handheld has its own manuals. **[MeshTerm on the -PicoCalc](docs/picocalc.md)** takes a stock PicoCalc from the shopping list to a Linux -handheld with a LoRa radio soldered inside; **[MeshTerm on the uConsole](docs/uconsole.md)** -drives a uConsole's SPI LoRa board directly with `--spi`, or through a small bridge if you -want the node on the mesh all the time. The device-side scripts both manuals run are in -[`scripts/`](scripts/); not sure which manual is yours? -[`docs/hardware.md`](docs/hardware.md) says. +PicoCalc](docs/devices/picocalc-lyra.md)** takes a stock PicoCalc from the shopping list to a +Linux handheld with a LoRa radio soldered inside; **[MeshTerm on the +uConsole](docs/devices/uconsole.md)** drives a uConsole's SPI LoRa board directly with +`--spi`, or through a small bridge if you want the node on the mesh all the time. M5Stack's +Cardputer Zero has a page too, but it is **[not supported yet](docs/devices/cardputer-zero.md)**. +The device-side scripts the manuals run are in [`scripts/`](scripts/); not sure which +manual is yours? [`docs/devices/README.md`](docs/devices/README.md) says. A companion that won't connect? **[When a companion won't -connect](docs/connecting.md)** covers Bluetooth pairing on Windows, Linux, and macOS, the +connect](docs/guide/connecting.md)** covers Bluetooth pairing on Windows, Linux, and macOS, the `dialout` group and ModemManager for USB on Linux, and every error message MeshTerm gives. ## What it does @@ -261,7 +262,7 @@ Nearly every one of them has a mirror `meshterm` subcommand — the same registr the menu and the CLI, so they can't drift apart. The live screens, such as the map and the dashboard, are only in the menu. -**[What MeshTerm does](docs/features.md)** is the full catalogue: every screen, what it +**[What MeshTerm does](docs/guide/features.md)** is the full catalogue: every screen, what it does, and its scripted equivalent where one exists. ## Scripting it @@ -286,9 +287,9 @@ meshterm config set radio_sf 9 meshterm monitor --seconds 60 ``` -**[The command line](docs/cli.md)** is the manual — every command and option, what each +**[The command line](docs/cli/README.md)** is the manual — every command and option, what each prints on both faces, and the exit statuses. -**[The CLI cookbook](docs/cookbook.md)** has more one-liners like these. +**[The CLI cookbook](docs/cli/cookbook.md)** has more one-liners like these. ## How this project is run @@ -383,7 +384,7 @@ short form throughout. The About page inside the app spells out the licence URL. MeshTerm ships **Cascadia Mono PL**, © 2019–present Microsoft Corporation, redistributed unmodified under the [SIL Open Font License 1.1](meshterm/assets/fonts/CascadiaMono-OFL.txt). It's offered to Windows users whose console can't draw the charts — see -[Terminals, icons, and the Windows console](docs/terminals.md). Microsoft doesn't endorse MeshTerm; the font is +[Terminals, icons, and the Windows console](docs/guide/terminals.md). Microsoft doesn't endorse MeshTerm; the font is simply the right tool, being one of the very few monospace faces that carries the braille block the timelines are drawn from. diff --git a/config.example.toml b/config.example.toml index 655445b..d8187d4 100644 --- a/config.example.toml +++ b/config.example.toml @@ -77,7 +77,7 @@ transport = "spi" # Any other board states its whole wiring — every pin and both switches, because a key # left out keeps the AIO's value. Read them off the board's meshtasticd config; the -# values here are an illustration only. docs/configuration.md has the key-by-key mapping. +# values here are an illustration only. docs/guide/configuration.md has the key-by-key mapping. # [profiles.hat.spi] # bus_id = 0 # /dev/spidev0.0 # cs_id = 0 diff --git a/docs/README.md b/docs/README.md index c323ef6..12e7cee 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,39 +2,52 @@ Everything that didn't belong on the front page. The [README](../README.md) is the front door — what MeshTerm is, how to install it, and enough to get a first session running. -This folder is the rest. +This folder is the rest, in four parts: + +| Folder | What's in it | +| --- | --- | +| [`guide/`](#using-meshterm) | Using MeshTerm: what each screen does, configuring it, connecting a companion, terminals. | +| [`cli/`](#the-command-line) | The command line manual, and a cookbook of one-liners. | +| [`devices/`](#running-it-on-a-handheld) | Running MeshTerm on a handheld's own screen, one manual per device. | +| [`development/`](#reading-the-code) | How the code is laid out, and a historical survey. | ## Using MeshTerm | Read | For | | --- | --- | -| **[What MeshTerm does](features.md)** | Every screen the menu offers, grouped the way the menu groups them, with the command that does the same job without it. | -| **[The command line](cli.md)** | The manual. Every subcommand and option, what each prints on the plain and JSON faces, the exit statuses, and recipes built on `jq`. | -| [The CLI cookbook](cookbook.md) | Common one-liners by task, for when you want the shape of a command rather than its specification. | -| [Configuring MeshTerm](configuration.md) | Preferences versus config, device profiles, and everything kept under `~/.meshterm`. | -| [When a companion won't connect](connecting.md) | Bluetooth pairing on each system, finding the PIN, and every connection error MeshTerm reports, with what to do about it. | -| [Terminals, icons, and the Windows console](terminals.md) | Why the icons and charts draw on some terminals and not others, and what MeshTerm does about it on Windows. | +| **[What MeshTerm does](guide/features.md)** | Every screen the menu offers, grouped the way the menu groups them, with the command that does the same job without it. | +| [Configuring MeshTerm](guide/configuration.md) | Preferences versus config, device profiles, and everything kept under `~/.meshterm`. | +| [When a companion won't connect](guide/connecting.md) | Bluetooth pairing on each system, finding the PIN, and every connection error MeshTerm reports, with what to do about it. | +| [Terminals, icons, and the Windows console](guide/terminals.md) | Why the icons and charts draw on some terminals and not others, and what MeshTerm does about it on Windows. | -## Putting it on hardware +## The command line + +| Read | For | +| --- | --- | +| **[The command line](cli/README.md)** | The manual. Every subcommand and option, what each prints on the plain and JSON faces, the exit statuses, and recipes built on `jq`. | +| [The CLI cookbook](cli/cookbook.md) | Common one-liners by task, for when you want the shape of a command rather than its specification. | + +## Running it on a handheld Only if you want MeshTerm running on a handheld's own screen. A MeshCore companion on USB, Bluetooth, or Wi-Fi needs none of this. | Read | For | | --- | --- | -| **[MeshTerm on hardware](hardware.md)** | Start here — which of the two manuals below is yours, and what the device-side scripts do. | -| [MeshTerm on the PicoCalc](picocalc.md) | A stock ClockworkPi PicoCalc to a Linux handheld with a LoRa radio soldered inside: the shopping list, the Lyra swap, Calculinux, and the radio. | -| [MeshTerm on the uConsole](uconsole.md) | A uConsole with the hackergadgets AIO LoRa board, whose SX1262 sits on the host's own SPI bus — MeshTerm drives it directly with `--spi`, or a small bridge fronts it as a companion for an always-on node. | +| **[MeshTerm on hardware](devices/README.md)** | Start here — which manual below is yours, and what the device-side scripts do. | +| [MeshTerm on the PicoCalc](devices/picocalc-lyra.md) | A stock ClockworkPi PicoCalc to a Linux handheld with a LoRa radio soldered inside: the shopping list, the swap to a Luckfox Lyra core, Calculinux, and the radio. | +| [MeshTerm on the uConsole](devices/uconsole.md) | A uConsole (CM4 or CM5) with the hackergadgets AIO LoRa board, whose SX1262 sits on the computer's own SPI bus — MeshTerm drives it directly with `--spi`, or a small bridge fronts it as a companion for an always-on node. | +| [MeshTerm on the Cardputer Zero](devices/cardputer-zero.md) | ⚠️ **Not supported yet.** What exists for M5Stack's Linux Cardputer, and how to try it in the desktop simulator. | ## Reading the code | Read | For | | --- | --- | -| [How the code is laid out](architecture.md) | The layering, and where a new feature goes. | +| [How the code is laid out](development/architecture.md) | The layering, and where a new feature goes. | | [CLAUDE.md](../CLAUDE.md) | The actual style guide — terminology, UX standards, and the reusable building blocks. Written for AI assistants, and the house rules for everyone. | | [CONTRIBUTING.md](../CONTRIBUTING.md) | How to propose a change, the rules on AI use, the dev install, the three checks a change must pass, and what happens to your contribution's licensing. | -## Historical record +### Historical record A survey of the code, kept because a project that writes down its own problems is easier to trust than one that doesn't — but it is a **snapshot of a date, not a to-do list**. @@ -42,7 +55,7 @@ Some of what it describes has been fixed since, and it does not mark which. | Read | For | | --- | --- | -| [UI consistency survey](survey-ui-consistency.md) | Everything else in the CLAUDE.md UX standard — six sweeps found nothing, which is the useful part. | +| [UI consistency survey](development/survey-ui-consistency.md) | Everything else in the CLAUDE.md UX standard — six sweeps found nothing, which is the useful part. | ## Also here diff --git a/docs/cli.md b/docs/cli/README.md similarity index 99% rename from docs/cli.md rename to docs/cli/README.md index 449ac65..f919e49 100644 --- a/docs/cli.md +++ b/docs/cli/README.md @@ -53,9 +53,9 @@ With no command, MeshTerm launches the interactive menu instead. | `-p`, `--profile NAME` | Use a named device profile from `config.toml`. | | `--port PORT` | Serial port to connect to (`COM5`, `/dev/ttyACM0`), overriding the profile. | | `--ble ADDRESS` | Bluetooth address of a companion; selects the Bluetooth transport. | -| `--ble-pin PIN` | Pairing PIN, if the Bluetooth companion asks for one. MeshTerm pairs with it itself on Windows and Linux; on macOS the system asks for the code in its own dialog, and this option has no effect. See [When a companion won't connect](connecting.md#bluetooth). | +| `--ble-pin PIN` | Pairing PIN, if the Bluetooth companion asks for one. MeshTerm pairs with it itself on Windows and Linux; on macOS the system asks for the code in its own dialog, and this option has no effect. See [When a companion won't connect](../guide/connecting.md#bluetooth). | | `--tcp HOST[:PORT]` | Network address of a TCP companion; selects the TCP transport. Default port 5000. | -| `--spi` | The LoRa radio on this machine's own SPI bus; selects the SPI transport. MeshTerm runs the node for the length of the command. With one radio attached, that's the one, profile or not; with none attached, the one SPI profile, else the uConsole AIO's wiring. With two, it refuses (exit `3`) and lists them — pick one with `-p`. See [Adding a radio on the SPI bus](configuration.md#adding-a-radio-on-the-spi-bus). Linux only. | +| `--spi` | The LoRa radio on this machine's own SPI bus; selects the SPI transport. MeshTerm runs the node for the length of the command. With one radio attached, that's the one, profile or not; with none attached, the one SPI profile, else the uConsole AIO's wiring. With two, it refuses (exit `3`) and lists them — pick one with `-p`. See [Adding a radio on the SPI bus](../guide/configuration.md#adding-a-radio-on-the-spi-bus). Linux only. | | `--mock` | Use the built-in simulator instead of real hardware. Nothing transmits, and the run records to `meshterm-mock.db` rather than your real history. | | `--db PATH` | Use this SQLite database instead of `~/.meshterm/meshterm.db` (or `meshterm-mock.db`, under `--mock`). **It moves the database and nothing else** — see [Where MeshTerm keeps its state](#where-meshterm-keeps-its-state). | | `--json` | Print the answer as JSON instead of aligned text. | @@ -489,7 +489,7 @@ $ echo $? The sentence names the cause and the remedy: a port that is missing, in use by another program, or not yours to open, and for Bluetooth a link that never opened, a pairing that is -out of date, or a PIN that was wrong. [When a companion won't connect](connecting.md) lists +out of date, or a PIN that was wrong. [When a companion won't connect](../guide/connecting.md) lists every one of them. With nothing named and more than one candidate attached, the same `3` arrives as a list diff --git a/docs/cookbook.md b/docs/cli/cookbook.md similarity index 93% rename from docs/cookbook.md rename to docs/cli/cookbook.md index 9258304..56e0295 100644 --- a/docs/cookbook.md +++ b/docs/cli/cookbook.md @@ -1,14 +1,14 @@ # The CLI cookbook -Common one-liners, by the thing you are trying to do. [The command line](cli.md) is the +Common one-liners, by the thing you are trying to do. [The command line](README.md) is the full manual — every command, every option, both output faces, the exit statuses — and its -own [Recipes](cli.md#recipes) section goes further than this one, into `jq` pipelines and +own [Recipes](README.md#recipes) section goes further than this one, into `jq` pipelines and shell loops. Nearly every menu option is also a subcommand — ideal for scripting, cron, and bots. The live pictures stay in the menu, where their meaning is (the map, dashboard, live feed, watchtower, and mesh walk); everything else has a command, and every command speaks -`--json`. **[`docs/cli.md`](cli.md) is the full manual**: every command and option, +`--json`. **[`docs/cli.md`](README.md) is the full manual**: every command and option, what each one prints on both faces, and the exit statuses. A taste: ```bash @@ -60,7 +60,7 @@ same run. ## Two output faces -*(The short version — [`docs/cli.md`](cli.md) has the whole of it.)* +*(The short version — [`docs/cli.md`](README.md) has the whole of it.)* The **plain face** is for a person at a prompt. It prints like a standard Unix utility — no colour, no borders, one record per line, nothing wrapped except the four written @@ -93,7 +93,7 @@ Anything structural should go through `--json` and `jq`. The plain face is for l `0` success · `1` failure · `2` usage error · `3` no device found (nothing was transmitted) · `4` the device was reached but the operation failed · `5` nothing to report. The table with its full wording is printed under `meshterm --help` and explained -in [`docs/cli.md`](cli.md#exit-status). +in [`docs/cli.md`](README.md#exit-status). `5` is the one worth knowing about: it lets a script tell "found nothing" from "worked" without counting output lines. diff --git a/docs/architecture.md b/docs/development/architecture.md similarity index 84% rename from docs/architecture.md rename to docs/development/architecture.md index 6e2c8a2..f2e9bb8 100644 --- a/docs/architecture.md +++ b/docs/development/architecture.md @@ -1,8 +1,8 @@ # How the code is laid out A map of the package for anyone reading it for the first time. The house rules that -govern what goes where are in [CLAUDE.md](../CLAUDE.md), which is the real style guide; -[CONTRIBUTING.md](../CONTRIBUTING.md) has the dev install and the gates a change has to +govern what goes where are in [CLAUDE.md](../../CLAUDE.md), which is the real style guide; +[CONTRIBUTING.md](../../CONTRIBUTING.md) has the dev install and the gates a change has to pass. MeshTerm is layered so a new feature is one file in `meshterm/tools/`: diff --git a/docs/survey-ui-consistency.md b/docs/development/survey-ui-consistency.md similarity index 81% rename from docs/survey-ui-consistency.md rename to docs/development/survey-ui-consistency.md index c9af8ad..74bfd9f 100644 --- a/docs/survey-ui-consistency.md +++ b/docs/development/survey-ui-consistency.md @@ -36,11 +36,11 @@ are one-offs and were not kept; each is a dozen lines of `ast.walk` and is trivi Two more worth calling out because they are the kind of rule that usually rots: - **`Del` on a row is gated correctly.** The hint atom only appears when the highlighted row - is actually deletable ([`select.py:448`](../meshterm/ui/tui/select.py#L448)) — a real + is actually deletable ([`select.py:448`](../../meshterm/ui/tui/select.py#L448)) — a real instance of "never advertise a key that would do nothing", implemented rather than assumed. - **The PicoCalc lane's "a chord earns a chip" rule holds.** The one bare-letter shortcut in the app — Time Machine's `w` — has its F3 chip, with the reasoning written out in full at - [`timemachine_screen.py:227-242`](../meshterm/ui/timemachine_screen.py#L227). + [`timemachine_screen.py:227-242`](../../meshterm/ui/timemachine_screen.py#L227). --- @@ -55,24 +55,24 @@ are the empty states of four whole screens, so each is the *only* thing on scree | Site | Current | House voice | |---|---|---| -| [`chat.py:234`](../meshterm/ui/chat.py#L234) | `No messages yet — say hello!` | `no messages yet — say hello` | -| [`message_paths_screen.py:199-202`](../meshterm/ui/message_paths_screen.py#L199) | `Nothing overheard — the radio only logs frames it hears while MeshTerm is listening.` / `No direct-message frames logged in the window.` | lowercase, no full stop | -| [`timemachine_screen.py:335-336`](../meshterm/ui/timemachine_screen.py#L335) | `Nothing recorded in this window.` + `Press w to widen it.` | lowercase, no full stop | -| [`timemachine_screen.py:453-454`](../meshterm/ui/timemachine_screen.py#L453) | `Nothing sent in this window.` + `Press w to widen it.` | ditto | +| [`chat.py:234`](../../meshterm/ui/chat.py#L234) | `No messages yet — say hello!` | `no messages yet — say hello` | +| [`message_paths_screen.py:199-202`](../../meshterm/ui/message_paths_screen.py#L199) | `Nothing overheard — the radio only logs frames it hears while MeshTerm is listening.` / `No direct-message frames logged in the window.` | lowercase, no full stop | +| [`timemachine_screen.py:335-336`](../../meshterm/ui/timemachine_screen.py#L335) | `Nothing recorded in this window.` + `Press w to widen it.` | lowercase, no full stop | +| [`timemachine_screen.py:453-454`](../../meshterm/ui/timemachine_screen.py#L453) | `Nothing sent in this window.` + `Press w to widen it.` | ditto | The two `Press w to widen it.` lines are a second question: they are *hints*, sitting in the body, on a screen whose F-key lane and footer already advertise `w`. Either the empty state absorbs it (`nothing recorded in this window — press w to widen it`) or it goes. -Deliberately **not** on this list: `Never heard` ([`contacts_screen.py:258`](../meshterm/ui/contacts_screen.py#L258)) -is a purge-ladder *row label*, and `No revisits` ([`records.py:237`](../meshterm/services/records.py#L237)) +Deliberately **not** on this list: `Never heard` ([`contacts_screen.py:258`](../../meshterm/ui/contacts_screen.py#L258)) +is a purge-ladder *row label*, and `No revisits` ([`records.py:237`](../../meshterm/services/records.py#L237)) is a discipline *title*. Both correctly capitalised. ### 2.2 One dialog inverts the button convention The rule: *safe way out on the left, committing verb on the right and default*. -[`config_editor.py:538-541`](../meshterm/ui/config_editor.py#L538) — the Location dialog: +[`config_editor.py:538-541`](../../meshterm/ui/config_editor.py#L538) — the Location dialog: ```python [("Pick on map", "map"), ("Type coordinates", "type"), ("Clear", "clear")], @@ -86,7 +86,7 @@ the only dialog in the app shaped this way, out of 49 call sites. ### 2.3 One dialog defaults to the middle button -[`config_editor.py:988-992`](../meshterm/ui/config_editor.py#L988) — `[Cancel, Preview, Apply]` +[`config_editor.py:988-992`](../../meshterm/ui/config_editor.py#L988) — `[Cancel, Preview, Apply]` with `default=1`, so Enter lands on **Preview**, not the rightmost `Apply`. This is very likely deliberate — previewing before overwriting a whole device config is the @@ -100,12 +100,12 @@ Beyond confirms, the app has two other dialog shapes, both used consistently but written down: - **The toggle picker** — `[Off, On]` with the default tracking the current value - ([`config_editor.py:441`](../meshterm/ui/config_editor.py#L441), - [`repeater_admin.py:313`](../meshterm/ui/repeater_admin.py#L313)). No Cancel; `Esc keep` + ([`config_editor.py:441`](../../meshterm/ui/config_editor.py#L441), + [`repeater_admin.py:313`](../../meshterm/ui/repeater_admin.py#L313)). No Cancel; `Esc keep` carries it, which is exactly the footer verb CLAUDE.md reserves for a value picker. Correct in practice, undescribed in the standard. - **The notification with an onward door** — `[Trophy case, Close]`, `default=1` - ([`trace_screen.py:637-639`](../meshterm/ui/trace_screen.py#L637)). Appears unbidden after a + ([`trace_screen.py:637-639`](../../meshterm/ui/trace_screen.py#L637)). Appears unbidden after a record-setting trace. `Close` is the default so a stray Enter dismisses rather than navigating — the inverse of the usual rule, and right for a dialog the user did not ask for. @@ -116,21 +116,21 @@ dialog of either shape has nothing to copy. Twelve classes declare `floating = False` (they are full screens, not dialogs). Ten override `fkey_lane`. Two do not, and so inherit `Screen.fkey_lane` → the raw `DEFAULT_LANE` -([`fkeys.py:116`](../meshterm/ui/tui/fkeys.py#L116)): +([`fkeys.py:116`](../../meshterm/ui/tui/fkeys.py#L116)): | Screen | | |---|---| -| [`TraceScreen`](../meshterm/ui/trace_screen.py#L345) (`trace_screen.py:345`) | the app's busiest screen | -| [`TxSweepScreen`](../meshterm/ui/tx_screen.py#L93) (`tx_screen.py:93`) | | +| [`TraceScreen`](../../meshterm/ui/trace_screen.py#L345) (`trace_screen.py:345`) | the app's busiest screen | +| [`TxSweepScreen`](../../meshterm/ui/tx_screen.py#L93) (`tx_screen.py:93`) | | `DEFAULT_LANE` is the *always-enabled* pager. The ten others build from -`default_lane(nav=self.content_overflows)` ([`fkeys.py:131`](../meshterm/ui/tui/fkeys.py#L131)), +`default_lane(nav=self.content_overflows)` ([`fkeys.py:131`](../../meshterm/ui/tui/fkeys.py#L131)), which dims the chips when the body already fits. So on PicoCalc both screens advertise live `Page ↑` / `Page ↓` chips whether or not there is anything to page — the one thing the lane's standing rule forbids. Trace is the one worth a proper look rather than a one-liner. Its footer -([`trace_screen.py:498-505`](../meshterm/ui/trace_screen.py#L498)) reads +([`trace_screen.py:498-505`](../../meshterm/ui/trace_screen.py#L498)) reads `↑↓ actions · Enter run · PgUp/PgDn scroll · Esc back` — every key physical on the PicoCalc, so nothing is *undiscoverable*. But it is the screen where F1–F3 would earn their keep most (re-run, flip the path, open the composer are all one cursor move away today), and it is @@ -141,7 +141,7 @@ its three free slots should say, not just gating the pager. (The About page reads as an exception in a grep but isn't: it passes `floating=False` as a constructor argument rather than a class attribute, and it does define its own lane at -[`about.py:115`](../meshterm/ui/about.py#L115).) +[`about.py:115`](../../meshterm/ui/about.py#L115).) ### 2.6 Dialogs that scroll but declare no lane @@ -150,9 +150,9 @@ surfaces set it explicitly — the confirms, the text prompt, the busy spinner, list, one trace dialog. But two floating dialogs that genuinely *do* page inherit `DEFAULT_LANE` instead: -- `RecordDialog` ([`records_screen.py:146`](../meshterm/ui/records_screen.py#L146)) — a record's +- `RecordDialog` ([`records_screen.py:146`](../../meshterm/ui/records_screen.py#L146)) — a record's story, which scrolls -- `PathComposerScreen` ([`path_composer.py`](../meshterm/ui/path_composer.py)) — a windowed +- `PathComposerScreen` ([`path_composer.py`](../../meshterm/ui/path_composer.py)) — a windowed list of hop suggestions Both arguably *want* the pager, which would make the doc's "every dialog" too strong. The @@ -162,7 +162,7 @@ should read *every dialog that doesn't scroll*). ### 2.7 One row label that may want an ellipsis -[`config_editor.py:787`](../meshterm/ui/config_editor.py#L787) — `Share QR / URI`. The rule is +[`config_editor.py:787`](../../meshterm/ui/config_editor.py#L787) — `Share QR / URI`. The rule is "a row that opens further prompts ends with `…`". This one opens a *view* (the QR popup), not a prompt — so it turns on whether "further prompts" means "any further surface". Every other row in the app is unambiguous; this is the only edge. @@ -174,9 +174,9 @@ as a separator. CLAUDE.md itself lists it twice: `★ you (yellow)` under node types, and `★ best/winner` under concept icons. In practice both are live — the map and every path line draw `★` for our own -node ([`pathline.SELF_GLYPH`](../meshterm/ui/pathline.py)), while the trophy case and the +node ([`pathline.SELF_GLYPH`](../../meshterm/ui/pathline.py)), while the trophy case and the new-record dialog draw `★` in accent for a record -([`trace_screen.py:635-636`](../meshterm/ui/trace_screen.py#L635)). +([`trace_screen.py:635-636`](../../meshterm/ui/trace_screen.py#L635)). They are never on the same surface, and the colours differ (yellow vs accent), so nothing is actually ambiguous today. Flagged only because it is the single glyph in the whole marks table @@ -189,8 +189,8 @@ trophy case and would carry it. CLAUDE.md is emphatic that an unidentified node takes `node.unknown` and that this is *deliberately not* `muted` ("muted is chrome and may sit a step darker, while an unidentified node is content you can still act on"). `node.unknown` appears at 11 sites. There are also 26 -`style="muted"` uses inside [`pathline.py`](../meshterm/ui/pathline.py) and -[`widgets.py`](../meshterm/ui/widgets.py) — the two modules that draw nodes. +`style="muted"` uses inside [`pathline.py`](../../meshterm/ui/pathline.py) and +[`widgets.py`](../../meshterm/ui/widgets.py) — the two modules that draw nodes. Most of those 26 are certainly chrome (separators, labels, the un-lit part of a key, which the standard explicitly says stays muted). But this is the one rule in the standard that a diff --git a/docs/devices/README.md b/docs/devices/README.md new file mode 100644 index 0000000..cb0c897 --- /dev/null +++ b/docs/devices/README.md @@ -0,0 +1,73 @@ +# MeshTerm on hardware + +MeshTerm talks to a **node** — a MeshCore radio that speaks the companion protocol over +serial, Bluetooth LE, or TCP. Plug a companion board into USB, or pair one over Bluetooth, +and none of this page applies: MeshTerm finds it, and the [README](../../README.md) and the +[command line manual](../cli/README.md) are all you need. + +This page is for the hardware that *doesn't* present a node that way. Each such device has +its own step-by-step manual, and this page only tells you which one is yours. + +## Which guide is yours + +| I have… | Read | +| --- | --- | +| a ClockworkPi **PicoCalc**, stock or already running Calculinux on a Luckfox Lyra, and I want MeshTerm on its own screen | **[MeshTerm on the PicoCalc](picocalc-lyra.md)** — from the shopping list to a working machine | +| …and I want a **radio** inside it | the same manual, [Phase 3](picocalc-lyra.md#phase-3--add-the-radio) — a XIAO nRF52840 + Wio-SX1262 soldered to the Lyra's UART | +| a ClockworkPi **uConsole** with the hackergadgets AIO board — an SX1262 on the host's own SPI bus | **[MeshTerm on the uConsole](uconsole.md)** — MeshTerm drives it directly with `--spi`, or a bridge service fronts it as a companion if you want it always on | +| an M5Stack **Cardputer Zero** — the one with a Raspberry Pi inside, not the ESP32 Cardputer or Cardputer-Adv | **[MeshTerm on the Cardputer Zero](cardputer-zero.md)** — ⚠️ **not supported yet**: it has never run on the device; the page says what works in the simulator | +| a MeshCore companion on USB, Bluetooth, or Wi-Fi | nothing here — the [README](../../README.md), and [When a companion won't connect](../guide/connecting.md) if it doesn't | + +The devices have nothing in common but MeshTerm itself. The device-side scripts each +manual runs live in [`scripts/`](../../scripts/), one folder per handheld; the +[index below](#the-scripts) says what every file does and which machine it runs on. Read a +script before you run it; most run as root and change the machine they run on. + +## The scripts + +[`scripts/`](../../scripts/) holds the device-side helpers, one folder per manual: +[`picocalc/`](../../scripts/picocalc/) for the PicoCalc — bringing up Calculinux on the Lyra, +building its console font, and (under [`xiao-radio/`](../../scripts/picocalc/xiao-radio/)) +giving it a MeshCore radio over UART — and [`uconsole/`](../../scripts/uconsole/) for the +always-on bridge that fronts the uConsole's SPI-attached LoRa chip as a companion (MeshTerm +can also drive that chip directly, with no script at all — the uConsole manual covers +both). The manual is the +walkthrough; each script's own header says what it assumes about the machine and why each +step is the way it is. On the Lyra, only one program may hold `/dev/ttyS1` — MeshTerm +directly, or a pump in front of it, never both. + +| File | Runs on | What it does | +| --- | --- | --- | +| [`picocalc/calculinux-setup.sh`](../../scripts/picocalc/calculinux-setup.sh) | the Lyra (root) | One-time bring-up: python packages, the Wi-Fi boot-scan kick, a boot clock sync (there is no RTC), the deploy user, the checkout, the venv and `pip install -e .`, the login PATH, and the console font. Idempotent. | +| [`picocalc/calculinux-console-font-6x12.sh`](../../scripts/picocalc/calculinux-console-font-6x12.sh) | the Lyra (root) | Just the console font: builds, installs and persists the 512-glyph **meshterm** PSF (braille, node marks, rounded frame corners, the list cursor) and restores the stock 16-slot palette. Calls the 6×8 build below when it sits alongside. Run it alone after a MeshTerm update, from the pulled checkout (`/home/meshterm/MeshTerm/scripts/picocalc/`) rather than the copy made at install time. | +| [`picocalc/calculinux-console-font-6x8.sh`](../../scripts/picocalc/calculinux-console-font-6x8.sh) | the Lyra (root) | The shorter-cell companion font, **meshterm8** (6×8 → 53×40), for anyone who wants more rows than the 6×12 default gives. Its base bitmap is the kernel's `font_6x8`, so this one file and the PSF it builds are **GPL-2.0-only** ([`LICENSE.GPL-2.0`](../../scripts/picocalc/LICENSE.GPL-2.0) sits beside it); it reads the donor/alias/keeper tables out of the script above rather than keeping a second copy. Pick between the two fonts from MeshTerm's Preferences page. | +| [`picocalc/calculinux-wifi-set.sh`](../../scripts/picocalc/calculinux-wifi-set.sh) | the Lyra (sudo) | Interactive Wi-Fi join: prompts for an SSID and passphrase, writes iwd's credentials, connects now. This is how a network *becomes* known to the boot kick. | +| [`picocalc/calculinux-radio-bridge.sh`](../../scripts/picocalc/calculinux-radio-bridge.sh) | the Lyra (root) | **Unfinished, unsupported.** An early UART-to-TCP pump: it serves `/dev/ttyS1` on TCP port 5000 and writes a `radio` TCP profile. It does not route UART1 to the header pads. Superseded by `xiao-radio/lyra-setup.sh`, which does the routing and points MeshTerm at the serial port directly. | +| [`picocalc/xiao-radio/`](../../scripts/picocalc/xiao-radio/) | see below | Everything for the XIAO nRF52840 + Wio-SX1262 radio wired to the Lyra's UART1; its [bench card](../../scripts/picocalc/xiao-radio/README.md) has the wiring table. The firmware is MeshCore, MIT-licensed — [`LICENSE.MeshCore`](../../scripts/picocalc/xiao-radio/LICENSE.MeshCore) sits beside the patch. | +| [`xiao-radio/build-firmware.sh`](../../scripts/picocalc/xiao-radio/build-firmware.sh) | dev machine | Clones and patches MeshCore, builds the `Xiao_nrf52_companion_radio_serial` environment, emits the `.uf2`. | +| [`xiao-radio/flash.py`](../../scripts/picocalc/xiao-radio/flash.py) | dev machine | Flashes the `.uf2` to the XIAO over its UF2 bootloader, refusing the wrong board or SoftDevice; prints the serial-DFU command when the bootloader offers no drive. | +| [`xiao-radio/meshcore-uart1.patch`](../../scripts/picocalc/xiao-radio/meshcore-uart1.patch) | — | The two firmware hunks: the companion on nRF52 `Serial1`, and I²C moved off the UART pads. Not upstream yet. | +| [`xiao-radio/lyra-setup.sh`](../../scripts/picocalc/xiao-radio/lyra-setup.sh) | the Lyra (root) | Boot-persistent UART1 → GP4/GP5 mux service, `dialout` membership, and a default MeshTerm serial profile for `/dev/ttyS1`. | +| [`xiao-radio/uart1-mux.py`](../../scripts/picocalc/xiao-radio/uart1-mux.py) | the Lyra (root) | The matrix-IO register poke that routes UART1 to the header pads, run once per boot by the service above. | +| [`uconsole/meshterm-spi-bridge`](../../scripts/uconsole/meshterm-spi-bridge) | the radio host (your user) | The **always-on** option: runs a mesh node in software over an SPI-attached SX1262 (uConsole AIO, Waveshare HATs) and serves the companion protocol on `127.0.0.1:5000`, so the node stays on the mesh while MeshTerm is closed. Setup menu, preflight, optional `systemd --user` service. Runs on `openhop_core` (preferred) or its predecessor `pymc_core`; see the manual for the venv it wants on a bookworm uConsole. Most people should reach for `meshterm --spi` instead — no script, no service, MeshTerm drives the chip itself while it runs. | + +## The regular and picocalc platforms + +MeshTerm resolves one frozen **platform** at boot and draws everything through it. The +**regular** platform is a desktop or ssh terminal: 72 readable columns, truecolour, emoji, +a footer hint line. The **picocalc** platform is a framebuffer console: 53 columns, no +emoji, an F-key lane instead of a hint line, and sixteen colour slots rather than a colour +space. Nothing loses its colour there: the node hue and the recency heat gradient +quantise to those slots instead. It is detected from the device tree on the Lyra, and +`--platform picocalc` or `MESHTERM_PLATFORM=picocalc` forces it anywhere, which is how you +preview the handheld layout on a desktop: + +```bash +meshterm --mock --platform picocalc # the whole app, simulated radio, handheld layout +meshterm specimen # the visual language on one card +meshterm platform # which platform this terminal resolved to +``` + +What that platform needs from the console — a font with the glyphs the interface draws, +and the stock sixteen-slot palette — is installed by the PicoCalc manual's setup script; +the details are in that manual and in the scripts' own headers. diff --git a/docs/devices/cardputer-zero.md b/docs/devices/cardputer-zero.md new file mode 100644 index 0000000..88086f1 --- /dev/null +++ b/docs/devices/cardputer-zero.md @@ -0,0 +1,96 @@ +# MeshTerm on the Cardputer Zero + +> [!CAUTION] +> **The Cardputer Zero is not supported yet.** +> +> MeshTerm has never run on a real Cardputer Zero. Everything on this page was built from +> M5Stack's published source code and documentation, and tested only in a simulator on a +> desktop computer. There is no package to install on the device, the screen and keyboard +> code has never touched the hardware, and the LoRa add-on board is not wired up at all. +> +> Don't buy a Cardputer Zero to run MeshTerm on it. This page will say so when that +> changes. + +M5Stack makes three Cardputers. This page is about the **Cardputer Zero**: the one with a +Raspberry Pi Compute Module 0 inside, running Linux. The original Cardputer and the +Cardputer-Adv are ESP32 microcontroller boards; they can't run MeshTerm, and nothing here +applies to them. + +- [The device](#the-device) +- [Where the work stands](#where-the-work-stands) +- [Trying it in the simulator](#trying-it-in-the-simulator) +- [Radios](#radios) + +--- + +## The device + +| | | +| --- | --- | +| Computer | Raspberry Pi Compute Module 0: four 1 GHz cores, 512 MB of memory | +| Screen | 1.9 inches, 320×170 pixels | +| Keyboard | 46 keys, with Shift, Fn and Sym layers | +| System | Raspberry Pi OS "Trixie", with M5Stack's own app launcher | + +MeshTerm draws the screen in 53 columns by 14 rows: the same width as on the PicoCalc, +but only about half the height, so many screens have a more compact layout here. + +The launcher gives an app the whole screen and the keyboard, but no text console to run +in. So MeshTerm brings its own: a small program inside MeshTerm that draws every +character on the screen itself and reads the keys directly. + +## Where the work stands + +**Done, and tested only in the simulator:** + +- MeshTerm's layout for the 14-row screen: a title bar that also carries the unread count + and the battery, one-row tab strips, and dialogs sized for the short screen. +- The row of function-key labels along the bottom of the screen, driven by **Fn+4** to + **Fn+8**, the keys that sit right under the display. **Shift** with them is a second set. +- The program that draws MeshTerm on the screen, and a desktop simulator built on it. + +**Not done:** + +- Running on the real device at all. +- A package for M5Stack's app store, so the launcher can start MeshTerm. +- The LoRa radio add-on (see [Radios](#radios)). +- Checking the keyboard on the device. Its layout was read from M5Stack's driver code, + and two printings of the keyboard exist. + +## Trying it in the simulator + +The simulator opens a window showing the Cardputer Zero's screen, drawn pixel for pixel +the way the device would draw it, scaled up so it's readable. It runs MeshTerm with a +simulated radio, so you can try the layout without the device or a radio. + +You need a copy of MeshTerm's source code (see +[CONTRIBUTING.md](../../CONTRIBUTING.md) for setting one up). Then, once, fetch the font +the screen is drawn in: + +```bash +python scripts/cardputer/fetch-terminus.py +``` + +and start the simulator: + +```bash +python -m meshterm.host --mock +``` + +Point `MESHTERM_HOME` at an empty folder first, so the simulated radio's made-up nodes +stay out of your real history. + +In the window, your keyboard's **F4** to **F8** stand in for **Fn+4** to **Fn+8**, and +holding **Shift** shows the second set. **Ctrl+Shift+S** saves a screenshot at the screen's +real size. Closing the window quits MeshTerm. + +## Radios + +The Cardputer Zero has no LoRa radio built in. Once MeshTerm runs on it, there will be +three ways to add one: + +| Radio | Status | +| --- | --- | +| A MeshCore companion on USB | Expected to work as on any Linux computer. | +| A MeshCore companion over Bluetooth | Expected to work as on any Linux computer. | +| M5Stack's **Cap LoRa-1262** add-on board | Not wired up. Its pins on the Cardputer Zero are known, but MeshTerm still needs two setup steps it can't do yet: switching on the board's antenna chip, and freeing two of the expansion header's pins from the USB port that shares them. | diff --git a/docs/picocalc.md b/docs/devices/picocalc-lyra.md similarity index 100% rename from docs/picocalc.md rename to docs/devices/picocalc-lyra.md diff --git a/docs/uconsole.md b/docs/devices/uconsole.md similarity index 99% rename from docs/uconsole.md rename to docs/devices/uconsole.md index e47425e..f374da0 100644 --- a/docs/uconsole.md +++ b/docs/devices/uconsole.md @@ -263,7 +263,7 @@ same two commands. The defaults match the hackergadgets uConsole AIO **v1**, which needs no `spi` table at all. State only what differs, under `[profiles..spi]`. For a board that isn't an AIO — a LoRa HAT on a Raspberry Pi, say — [Adding a radio on the SPI -bus](configuration.md#adding-a-radio-on-the-spi-bus) walks through a whole profile, including +bus](../guide/configuration.md#adding-a-radio-on-the-spi-bus) walks through a whole profile, including how to read the pins off the board's `meshtasticd` configuration: ```toml diff --git a/docs/configuration.md b/docs/guide/configuration.md similarity index 94% rename from docs/configuration.md rename to docs/guide/configuration.md index 5d290ce..1e023ff 100644 --- a/docs/configuration.md +++ b/docs/guide/configuration.md @@ -24,10 +24,10 @@ delete a line and the default takes over again. and remembers the last good default. A network (TCP) companion isn't discoverable, so reach it with `--tcp host:port`, a TCP profile, or the picker's "add a network device" prompt. A LoRa radio on this machine's own SPI bus is listed on the picker when its device node -exists, or reached with `--spi`; see [the uConsole manual](uconsole.md). +exists, or reached with `--spi`; see [the uConsole manual](../devices/uconsole.md). Write a config file only to give your hardware stable aliases. -Copy [`config.example.toml`](../config.example.toml) to `~/.meshterm/config.toml`: +Copy [`config.example.toml`](../../config.example.toml) to `~/.meshterm/config.toml`: ```toml # default_profile = "s3" # uncomment and name your own profile to make it the default @@ -64,7 +64,7 @@ history, the outbox, the contact and channel caches, stored admin passwords, and or a `--mock` session — without touching the one you use every day. `--db` moves the database alone; the rest stays where it was. A `--mock` run that names neither writes to `meshterm-mock.db` beside the real history, so the simulator's invented nodes never enter -it. [`docs/cli.md`](cli.md#where-meshterm-keeps-its-state) +it. [`docs/cli.md`](../cli/README.md#where-meshterm-keeps-its-state) lists every file. Timestamps are stored as UTC and rendered in your local time. Heard packets older than the @@ -76,7 +76,7 @@ and at the start of each `meshterm monitor` run. Nothing else is pruned. Some boards put the LoRa chip straight on the computer's SPI bus, with no firmware in front of it: the uConsole's AIO board, and LoRa HATs for the Raspberry Pi. MeshTerm drives these itself — it runs the node while it is connected and lets go of the radio when it quits. -It needs Linux and the radio library; [the uConsole manual](uconsole.md#step-2--install-the-radio-library) +It needs Linux and the radio library; [the uConsole manual](../devices/uconsole.md#step-2--install-the-radio-library) shows how to install that. **An AIO v1 needs no configuration.** Its wiring is MeshTerm's default, so whenever @@ -115,7 +115,7 @@ between a Pi 4/CM4 and a Pi 5/CM5 and has moved between kernel releases on the l Left out, it defaults to `-1`, and MeshTerm finds the header's chip by its label (`pinctrl-rp1`, `pinctrl-bcm2711`, `pinctrl-bcm2835`) — falling back to chip `0` on a board whose chips carry none of them. `gpiodetect` lists every chip with its label. The -[uConsole guide](uconsole.md#which-gpio-chip) has the whole story. +[uConsole guide](../devices/uconsole.md#which-gpio-chip) has the whole story. **State every pin and both switches.** Where a `meshtasticd` file leaves a key out, it means "none" or "off". Where the `spi` table leaves a key out, it means *the AIO's value* — IRQ on @@ -145,7 +145,7 @@ use_dio3_tcxo = false # no TCXO on this board A `spi` table is enough to make it an SPI profile; `transport = "spi"` says so outright and is what an AIO profile with no table uses. Every key, with the AIO defaults, is in -[the uConsole manual's wiring table](uconsole.md#board-wiring). A key MeshTerm doesn't know, +[the uConsole manual's wiring table](../devices/uconsole.md#board-wiring). A key MeshTerm doesn't know, or a value of the wrong type, stops it at startup with the profile named — a misspelt pin is never quietly ignored. diff --git a/docs/connecting.md b/docs/guide/connecting.md similarity index 97% rename from docs/connecting.md rename to docs/guide/connecting.md index 90cc5ad..964b2ea 100644 --- a/docs/connecting.md +++ b/docs/guide/connecting.md @@ -6,7 +6,7 @@ to do about it, in the device picker and on the command line alike. This page co messages, so you can look one up before you meet it, or search for the one you have. Every failure is also written to the log file, `~/.meshterm/meshterm.log` (see -[Where MeshTerm keeps its state](cli.md#where-meshterm-keeps-its-state)). Attach it to a +[Where MeshTerm keeps its state](../cli/README.md#where-meshterm-keeps-its-state)). Attach it to a bug report. For more detail, set the **Log detail** preference to `DEBUG` (`meshterm preferences set log_level DEBUG`) and run it again. @@ -125,4 +125,4 @@ Bluetooth, a network companion serves one client at a time. A radio MeshTerm drives itself (`--spi`) is not a companion. It has no pairing and no port, and its failures are about the node MeshTerm starts. Its own guides cover them: -[the uConsole](uconsole.md) and [adding a radio on the SPI bus](configuration.md#adding-a-radio-on-the-spi-bus). +[the uConsole](../devices/uconsole.md) and [adding a radio on the SPI bus](configuration.md#adding-a-radio-on-the-spi-bus). diff --git a/docs/features.md b/docs/guide/features.md similarity index 99% rename from docs/features.md rename to docs/guide/features.md index 135fa56..855007c 100644 --- a/docs/features.md +++ b/docs/guide/features.md @@ -1,7 +1,7 @@ # What MeshTerm does Every screen the menu offers, and the command that does the same job without it. This is -the catalogue; [the command line](cli.md) is the manual for the scripted half of it. +the catalogue; [the command line](../cli/README.md) is the manual for the scripted half of it. MeshTerm's menu asks one question — *what would you like to do?* — and its six sections answer it in two halves. The first three name a **doing**: Message, Watch, Explore. The diff --git a/docs/terminals.md b/docs/guide/terminals.md similarity index 100% rename from docs/terminals.md rename to docs/guide/terminals.md diff --git a/docs/hardware.md b/docs/hardware.md deleted file mode 100644 index f76190a..0000000 --- a/docs/hardware.md +++ /dev/null @@ -1,72 +0,0 @@ -# MeshTerm on hardware - -MeshTerm talks to a **node** — a MeshCore radio that speaks the companion protocol over -serial, Bluetooth LE, or TCP. Plug a companion board into USB, or pair one over Bluetooth, -and none of this page applies: MeshTerm finds it, and the [README](../README.md) and the -[command line manual](cli.md) are all you need. - -This page is for the hardware that *doesn't* present a node that way. Each such device has -its own step-by-step manual, and this page only tells you which one is yours. - -## Which guide is yours - -| I have… | Read | -| --- | --- | -| a ClockworkPi **PicoCalc**, stock or already running Calculinux on a Luckfox Lyra, and I want MeshTerm on its own screen | **[MeshTerm on the PicoCalc](picocalc.md)** — from the shopping list to a working machine | -| …and I want a **radio** inside it | the same manual, [Phase 3](picocalc.md#phase-3--add-the-radio) — a XIAO nRF52840 + Wio-SX1262 soldered to the Lyra's UART | -| a ClockworkPi **uConsole** with the hackergadgets AIO board — an SX1262 on the host's own SPI bus | **[MeshTerm on the uConsole](uconsole.md)** — MeshTerm drives it directly with `--spi`, or a bridge service fronts it as a companion if you want it always on | -| a MeshCore companion on USB, Bluetooth, or Wi-Fi | nothing here — the [README](../README.md), and [When a companion won't connect](connecting.md) if it doesn't | - -The two targets have nothing in common but MeshTerm itself. The device-side scripts each -manual runs live in [`scripts/`](../scripts/), one folder per handheld; the -[index below](#the-scripts) says what every file does and which machine it runs on. Read a -script before you run it; most run as root and change the machine they run on. - -## The scripts - -[`scripts/`](../scripts/) holds the device-side helpers, one folder per manual: -[`picocalc/`](../scripts/picocalc/) for the PicoCalc — bringing up Calculinux on the Lyra, -building its console font, and (under [`xiao-radio/`](../scripts/picocalc/xiao-radio/)) -giving it a MeshCore radio over UART — and [`uconsole/`](../scripts/uconsole/) for the -always-on bridge that fronts the uConsole's SPI-attached LoRa chip as a companion (MeshTerm -can also drive that chip directly, with no script at all — the uConsole manual covers -both). The manual is the -walkthrough; each script's own header says what it assumes about the machine and why each -step is the way it is. On the Lyra, only one program may hold `/dev/ttyS1` — MeshTerm -directly, or a pump in front of it, never both. - -| File | Runs on | What it does | -| --- | --- | --- | -| [`picocalc/calculinux-setup.sh`](../scripts/picocalc/calculinux-setup.sh) | the Lyra (root) | One-time bring-up: python packages, the Wi-Fi boot-scan kick, a boot clock sync (there is no RTC), the deploy user, the checkout, the venv and `pip install -e .`, the login PATH, and the console font. Idempotent. | -| [`picocalc/calculinux-console-font-6x12.sh`](../scripts/picocalc/calculinux-console-font-6x12.sh) | the Lyra (root) | Just the console font: builds, installs and persists the 512-glyph **meshterm** PSF (braille, node marks, rounded frame corners, the list cursor) and restores the stock 16-slot palette. Calls the 6×8 build below when it sits alongside. Run it alone after a MeshTerm update, from the pulled checkout (`/home/meshterm/MeshTerm/scripts/picocalc/`) rather than the copy made at install time. | -| [`picocalc/calculinux-console-font-6x8.sh`](../scripts/picocalc/calculinux-console-font-6x8.sh) | the Lyra (root) | The shorter-cell companion font, **meshterm8** (6×8 → 53×40), for anyone who wants more rows than the 6×12 default gives. Its base bitmap is the kernel's `font_6x8`, so this one file and the PSF it builds are **GPL-2.0-only** ([`LICENSE.GPL-2.0`](../scripts/picocalc/LICENSE.GPL-2.0) sits beside it); it reads the donor/alias/keeper tables out of the script above rather than keeping a second copy. Pick between the two fonts from MeshTerm's Preferences page. | -| [`picocalc/calculinux-wifi-set.sh`](../scripts/picocalc/calculinux-wifi-set.sh) | the Lyra (sudo) | Interactive Wi-Fi join: prompts for an SSID and passphrase, writes iwd's credentials, connects now. This is how a network *becomes* known to the boot kick. | -| [`picocalc/calculinux-radio-bridge.sh`](../scripts/picocalc/calculinux-radio-bridge.sh) | the Lyra (root) | **Unfinished, unsupported.** An early UART-to-TCP pump: it serves `/dev/ttyS1` on TCP port 5000 and writes a `radio` TCP profile. It does not route UART1 to the header pads. Superseded by `xiao-radio/lyra-setup.sh`, which does the routing and points MeshTerm at the serial port directly. | -| [`picocalc/xiao-radio/`](../scripts/picocalc/xiao-radio/) | see below | Everything for the XIAO nRF52840 + Wio-SX1262 radio wired to the Lyra's UART1; its [bench card](../scripts/picocalc/xiao-radio/README.md) has the wiring table. The firmware is MeshCore, MIT-licensed — [`LICENSE.MeshCore`](../scripts/picocalc/xiao-radio/LICENSE.MeshCore) sits beside the patch. | -| [`xiao-radio/build-firmware.sh`](../scripts/picocalc/xiao-radio/build-firmware.sh) | dev machine | Clones and patches MeshCore, builds the `Xiao_nrf52_companion_radio_serial` environment, emits the `.uf2`. | -| [`xiao-radio/flash.py`](../scripts/picocalc/xiao-radio/flash.py) | dev machine | Flashes the `.uf2` to the XIAO over its UF2 bootloader, refusing the wrong board or SoftDevice; prints the serial-DFU command when the bootloader offers no drive. | -| [`xiao-radio/meshcore-uart1.patch`](../scripts/picocalc/xiao-radio/meshcore-uart1.patch) | — | The two firmware hunks: the companion on nRF52 `Serial1`, and I²C moved off the UART pads. Not upstream yet. | -| [`xiao-radio/lyra-setup.sh`](../scripts/picocalc/xiao-radio/lyra-setup.sh) | the Lyra (root) | Boot-persistent UART1 → GP4/GP5 mux service, `dialout` membership, and a default MeshTerm serial profile for `/dev/ttyS1`. | -| [`xiao-radio/uart1-mux.py`](../scripts/picocalc/xiao-radio/uart1-mux.py) | the Lyra (root) | The matrix-IO register poke that routes UART1 to the header pads, run once per boot by the service above. | -| [`uconsole/meshterm-spi-bridge`](../scripts/uconsole/meshterm-spi-bridge) | the radio host (your user) | The **always-on** option: runs a mesh node in software over an SPI-attached SX1262 (uConsole AIO, Waveshare HATs) and serves the companion protocol on `127.0.0.1:5000`, so the node stays on the mesh while MeshTerm is closed. Setup menu, preflight, optional `systemd --user` service. Runs on `openhop_core` (preferred) or its predecessor `pymc_core`; see the manual for the venv it wants on a bookworm uConsole. Most people should reach for `meshterm --spi` instead — no script, no service, MeshTerm drives the chip itself while it runs. | - -## The regular and picocalc platforms - -MeshTerm resolves one frozen **platform** at boot and draws everything through it. The -**regular** platform is a desktop or ssh terminal: 72 readable columns, truecolour, emoji, -a footer hint line. The **picocalc** platform is a framebuffer console: 53 columns, no -emoji, an F-key lane instead of a hint line, and sixteen colour slots rather than a colour -space. Nothing loses its colour there: the node hue and the recency heat gradient -quantise to those slots instead. It is detected from the device tree on the Lyra, and -`--platform picocalc` or `MESHTERM_PLATFORM=picocalc` forces it anywhere, which is how you -preview the handheld layout on a desktop: - -```bash -meshterm --mock --platform picocalc # the whole app, simulated radio, handheld layout -meshterm specimen # the visual language on one card -meshterm platform # which platform this terminal resolved to -``` - -What that platform needs from the console — a font with the glyphs the interface draws, -and the stock sixteen-slot palette — is installed by the PicoCalc manual's setup script; -the details are in that manual and in the scripts' own headers. diff --git a/meshterm/cli.py b/meshterm/cli.py index f99c4cf..91f0c67 100644 --- a/meshterm/cli.py +++ b/meshterm/cli.py @@ -262,7 +262,7 @@ def main_callback( # wins, because naming a database is a deliberate claim about where a run records. # ``$MESHTERM_HOME`` remains the only thing that isolates a run whole: the contact # and channel caches, the outbox and the remembered devices live beside the - # database either way (see docs/cli.md). + # database either way (see docs/cli/README.md). settings.db_path = settings.config_dir / MOCK_DB_FILENAME # Two front ends, two consoles, and which one this process gets is settled here — a diff --git a/meshterm/context.py b/meshterm/context.py index f585a1d..74aed85 100644 --- a/meshterm/context.py +++ b/meshterm/context.py @@ -755,7 +755,7 @@ class AppContext: f"{what}, and --spi doesn't say which.\n" + "\n".join(rows) + "\nChoose one with -p . A radio with no profile needs one first — see " - "'Adding a radio on the SPI bus' in docs/configuration.md." + "'Adding a radio on the SPI bus' in docs/guide/configuration.md." ) def spi_wiring_for(self, spidev: str) -> SpiWiring: diff --git a/pyproject.toml b/pyproject.toml index 2ff5639..d98eda2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -100,7 +100,7 @@ exclude = [ "/.git-blame-ignore-revs", "/.gitattributes", "/CLAUDE.md", - "/docs/survey-*.md", + "/docs/development/survey-*.md", "/packaging", "/scripts", ] diff --git a/scripts/README.md b/scripts/README.md index 94f652c..6b9956f 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -6,7 +6,7 @@ Repo-side tools. None of these ship in the wheel. |---|---| | `basemap-doctor.py` | Why a user's map has no basemap — certificates, DNS, proxy, or colour depth | | `picocalc/` | Calculinux device setup: console font, palette, Wi-Fi; `xiao-radio/` is the XIAO radio kit (firmware build, flashing, UART setup) | -| `uconsole/` | The uConsole's always-on SPI bridge service (MeshTerm can also drive that radio directly, with no script at all — see `docs/uconsole.md`) | +| `uconsole/` | The uConsole's always-on SPI bridge service (MeshTerm can also drive that radio directly, with no script at all — see `docs/devices/uconsole.md`) | | `cardputer/` | The Cardputer Zero's console host: `fetch-terminus.py` installs the 12-pixel Terminus fonts it draws in (OFL, so never shipped with MeshTerm) | ## basemap-doctor.py diff --git a/scripts/picocalc/xiao-radio/README.md b/scripts/picocalc/xiao-radio/README.md index a0217a0..52fe188 100644 --- a/scripts/picocalc/xiao-radio/README.md +++ b/scripts/picocalc/xiao-radio/README.md @@ -4,7 +4,7 @@ A Seeed XIAO nRF52840 + Wio-SX1262 wired to the Luckfox Lyra's UART1, running Me companion firmware on `Serial1`, so MeshTerm on the PicoCalc opens `/dev/ttyS1` like any other serial companion. No BLE, no USB host, no SD-slot sacrifice. -**The step-by-step guide is [`docs/picocalc.md`, Phase 3](../../../docs/picocalc.md#phase-3--add-the-radio)** — parts, +**The step-by-step guide is [`docs/devices/picocalc-lyra.md`, Phase 3](../../../docs/devices/picocalc-lyra.md#phase-3--add-the-radio)** — parts, the firmware build and the patch explained, flashing, wiring, the Lyra setup, and what has only been proven on one bench. What the register poke does is in `uart1-mux.py`'s own header. This page is the bench card.