The guides and the device manuals are in ASD-STE100
The four user guides and the manuals for the PicoCalc, the uConsole, and the Cardputer Zero. Procedures are numbered steps, with a WARNING or a CAUTION before each step that has a risk. The step headings use a colon, and each link to them changed with them. Quoted error messages are in quotation marks, and the menu question is quoted as the app shows it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
e6f6cf2621
commit
c0e7c17252
8 files changed
+1425
-1244
No files matched your search
+90
-73
@@ -1,66 +1,74 @@
|
||||
# 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.
|
||||
MeshTerm talks to a **node**. The node is a MeshCore radio that speaks the companion
|
||||
protocol over serial, Bluetooth LE, or TCP. If you plug a companion board into USB, or pair
|
||||
one over Bluetooth, nothing on this page applies. MeshTerm finds the companion. For this
|
||||
case, read the [README](../../README.md) and the [command line manual](../cli/README.md).
|
||||
|
||||
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.
|
||||
This page is for the hardware that does not present a node in that way. Each such handheld
|
||||
has its own manual with numbered steps. This page tells you which manual 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 emulator |
|
||||
| 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 |
|
||||
| a ClockworkPi **PicoCalc**, stock or already running Calculinux on a Luckfox Lyra, and I want MeshTerm on its own display | **[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 UART of the Lyra |
|
||||
| a ClockworkPi **uConsole** with the hackergadgets AIO board (an SX1262 on the SPI bus of the host) | **[MeshTerm on the uConsole](uconsole.md)**: MeshTerm drives the radio directly with `--spi`. A bridge service can also present the radio 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 the Cardputer-Adv) | **[MeshTerm on the Cardputer Zero](cardputer-zero.md)**: ⚠️ **not supported yet**. MeshTerm has never run on the handheld. The page tells you what works in the emulator |
|
||||
| a MeshCore companion on USB, Bluetooth, or Wi-Fi | nothing here. Read the [README](../../README.md). If the companion does not connect, read [When a companion does not connect](../guide/connecting.md) |
|
||||
|
||||
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 handhelds have nothing in common except MeshTerm. Each manual runs scripts on the
|
||||
handheld. The scripts are in [`scripts/`](../../scripts/), with one folder for each
|
||||
handheld. The [index below](#the-scripts) tells you what each file does and which machine
|
||||
runs it. Read a script before you run it. Most scripts run as root and change the machine
|
||||
that runs them.
|
||||
|
||||
## The scripts
|
||||
|
||||
[`scripts/`](../../scripts/) holds the device-side helpers, one folder per manual:
|
||||
[`picocalc-lyra/`](../../scripts/picocalc-lyra/) for the PicoCalc — bringing up Calculinux on the Lyra,
|
||||
building its console font, and (under [`xiao-radio/`](../../scripts/picocalc-lyra/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.
|
||||
[`scripts/`](../../scripts/) has the helper scripts that run on the handheld. There is one
|
||||
folder for each manual:
|
||||
|
||||
- [`picocalc-lyra/`](../../scripts/picocalc-lyra/) is for the PicoCalc. It has the scripts
|
||||
that bring up Calculinux on the Lyra and build the console font. Under
|
||||
[`xiao-radio/`](../../scripts/picocalc-lyra/xiao-radio/), it also has the scripts that
|
||||
give the PicoCalc a MeshCore radio over UART.
|
||||
- [`uconsole/`](../../scripts/uconsole/) has the always-on bridge. The bridge presents the
|
||||
LoRa chip on the SPI bus of the uConsole as a companion. MeshTerm can also drive that chip
|
||||
directly, with no script. The uConsole manual describes both ways.
|
||||
|
||||
The manual is the walkthrough. The header of each script says what the script assumes about
|
||||
the machine, and why each step is as it is. On the Lyra, only one program can hold
|
||||
`/dev/ttyS1`. It is MeshTerm directly, or a pump in front of MeshTerm, but never both.
|
||||
|
||||
| File | Runs on | What it does |
|
||||
| --- | --- | --- |
|
||||
| [`picocalc-lyra/calculinux-setup.sh`](../../scripts/picocalc-lyra/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-lyra/calculinux-console-font-6x12.sh`](../../scripts/picocalc-lyra/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-lyra/`) rather than the copy made at install time. |
|
||||
| [`picocalc-lyra/calculinux-console-font-6x8.sh`](../../scripts/picocalc-lyra/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-lyra/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-lyra/calculinux-wifi-set.sh`](../../scripts/picocalc-lyra/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-lyra/calculinux-radio-bridge.sh`](../../scripts/picocalc-lyra/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-lyra/xiao-radio/`](../../scripts/picocalc-lyra/xiao-radio/) | see below | Everything for the XIAO nRF52840 + Wio-SX1262 radio wired to the Lyra's UART1; its [bench card](../../scripts/picocalc-lyra/xiao-radio/README.md) has the wiring table. The firmware is MeshCore, MIT-licensed — [`LICENSE.MeshCore`](../../scripts/picocalc-lyra/xiao-radio/LICENSE.MeshCore) sits beside the patch. |
|
||||
| [`xiao-radio/build-firmware.sh`](../../scripts/picocalc-lyra/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-lyra/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-lyra/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-lyra/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-lyra/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. |
|
||||
| [`picocalc-lyra/calculinux-setup.sh`](../../scripts/picocalc-lyra/calculinux-setup.sh) | the Lyra (root) | One-time bring-up. It sets up the python packages, the Wi-Fi boot-scan kick, a clock sync at boot (there is no RTC), the deploy user, the checkout, the venv and `pip install -e .`, the login PATH, and the console font. You can run it again without a problem. |
|
||||
| [`picocalc-lyra/calculinux-console-font-6x12.sh`](../../scripts/picocalc-lyra/calculinux-console-font-6x12.sh) | the Lyra (root) | Only the console font. It builds, installs, and keeps the 512-glyph **meshterm** PSF (braille, node marks, rounded frame corners, and the list pointer). It also restores the stock 16-slot palette. It calls the 6×8 build below when that script is in the same folder. After a MeshTerm update, run it alone from the pulled checkout (`/home/meshterm/MeshTerm/scripts/picocalc-lyra/`). Do not run the copy that the install made. |
|
||||
| [`picocalc-lyra/calculinux-console-font-6x8.sh`](../../scripts/picocalc-lyra/calculinux-console-font-6x8.sh) | the Lyra (root) | The companion font with shorter cells, **meshterm8** (6×8, which gives 53×40). Use it if you want more rows than the 6×12 default gives. Its base bitmap is the `font_6x8` of the kernel. Thus this file and the PSF that it builds are **GPL-2.0-only**. The file [`LICENSE.GPL-2.0`](../../scripts/picocalc-lyra/LICENSE.GPL-2.0) is next to it. The script reads the donor, alias, and keeper tables from the script above and does not keep a second copy. Select between the two fonts on the Preferences page of MeshTerm. |
|
||||
| [`picocalc-lyra/calculinux-wifi-set.sh`](../../scripts/picocalc-lyra/calculinux-wifi-set.sh) | the Lyra (sudo) | Interactive Wi-Fi join. It asks for an SSID and a passphrase, writes the credentials of iwd, and connects at once. This is how a network becomes known to the boot kick. |
|
||||
| [`picocalc-lyra/calculinux-radio-bridge.sh`](../../scripts/picocalc-lyra/calculinux-radio-bridge.sh) | the Lyra (root) | **Unfinished and not supported.** 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. `xiao-radio/lyra-setup.sh` replaces it. That script does the routing and points MeshTerm at the serial port directly. |
|
||||
| [`picocalc-lyra/xiao-radio/`](../../scripts/picocalc-lyra/xiao-radio/) | see below | Everything for the XIAO nRF52840 + Wio-SX1262 radio that is wired to UART1 of the Lyra. Its [bench card](../../scripts/picocalc-lyra/xiao-radio/README.md) has the wiring table. The firmware is MeshCore, with the MIT licence. The file [`LICENSE.MeshCore`](../../scripts/picocalc-lyra/xiao-radio/LICENSE.MeshCore) is next to the patch. |
|
||||
| [`xiao-radio/build-firmware.sh`](../../scripts/picocalc-lyra/xiao-radio/build-firmware.sh) | development machine | Clones and patches MeshCore, builds the `Xiao_nrf52_companion_radio_serial` environment, and makes the `.uf2` file. |
|
||||
| [`xiao-radio/flash.py`](../../scripts/picocalc-lyra/xiao-radio/flash.py) | development machine | Flashes the `.uf2` file to the XIAO through its UF2 bootloader. It refuses the wrong board or SoftDevice. When the bootloader gives no drive, it prints the serial-DFU command. |
|
||||
| [`xiao-radio/meshcore-uart1.patch`](../../scripts/picocalc-lyra/xiao-radio/meshcore-uart1.patch) | none | The two firmware hunks: the companion on `Serial1` of the nRF52, and I²C moved off the UART pads. It is not upstream yet. |
|
||||
| [`xiao-radio/lyra-setup.sh`](../../scripts/picocalc-lyra/xiao-radio/lyra-setup.sh) | the Lyra (root) | A UART1 → GP4/GP5 mux service that stays after a reboot, membership of `dialout`, and a default MeshTerm serial profile for `/dev/ttyS1`. |
|
||||
| [`xiao-radio/uart1-mux.py`](../../scripts/picocalc-lyra/xiao-radio/uart1-mux.py) | the Lyra (root) | The matrix-IO register poke that routes UART1 to the header pads. The service above runs it once at each boot. |
|
||||
| [`uconsole/meshterm-spi-bridge`](../../scripts/uconsole/meshterm-spi-bridge) | the radio host (your user) | The **always-on** option. It runs a mesh node in software over an SX1262 on an SPI bus (uConsole AIO, Waveshare HATs). It serves the companion protocol on `127.0.0.1:5000`. Thus the node stays on the mesh while MeshTerm is closed. It has a setup menu, a preflight check, and an optional `systemd --user` service. It runs on `openhop_core` (preferred) or on its predecessor `pymc_core`. The manual describes the venv that it needs on a bookworm uConsole. Most users must use `meshterm --spi` instead. It needs no script and no service, and MeshTerm drives the chip itself while MeshTerm runs. |
|
||||
|
||||
## The 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-lyra** 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-lyra` or `MESHTERM_PLATFORM=picocalc-lyra` forces it anywhere, which
|
||||
is how you preview the handheld layout in your own terminal:
|
||||
**regular** platform is a desktop terminal or an ssh terminal. It has 72 readable columns,
|
||||
truecolour, emoji, and a footer hint line.
|
||||
|
||||
The **picocalc-lyra** platform is a framebuffer console. It has 53 columns and no emoji.
|
||||
It has an F-key lane, not a hint line. It has sixteen colour slots, not a colour space.
|
||||
No element loses its colour there. The hue of a node and the heat gradient of recency are
|
||||
quantized to those slots instead. MeshTerm detects this platform from the device tree on
|
||||
the Lyra. You can force it on any machine with `--platform picocalc-lyra` or
|
||||
`MESHTERM_PLATFORM=picocalc-lyra`. With this, you can preview the layout of the handheld
|
||||
in your own terminal:
|
||||
|
||||
```bash
|
||||
meshterm --mock --platform picocalc-lyra # the whole app, simulated radio, handheld layout
|
||||
@@ -68,19 +76,19 @@ meshterm specimen # the visual language on one card
|
||||
meshterm platform # which platform this terminal resolved to
|
||||
```
|
||||
|
||||
The **cardputer-zero** platform is the Cardputer Zero's 53×14 screen, which MeshTerm draws
|
||||
itself. It is [not supported yet](cardputer-zero.md), so it is never detected: only
|
||||
`--platform cardputer-zero` chooses it.
|
||||
The **cardputer-zero** platform is the display of the Cardputer Zero, which is 53×14.
|
||||
MeshTerm draws this display itself. The platform is [not supported yet](cardputer-zero.md),
|
||||
thus MeshTerm never detects it. Only `--platform cardputer-zero` selects it.
|
||||
|
||||
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.
|
||||
The platform needs two things from the console: a font that has the glyphs that the
|
||||
interface draws, and the stock sixteen-slot palette. The setup script in the PicoCalc
|
||||
manual installs both. The manual and the headers of the scripts give the details.
|
||||
|
||||
## The emulator
|
||||
|
||||
`--platform` gives you a handheld's layout in your own terminal, drawn in your terminal's
|
||||
font and colours. For a closer look, `meshterm emulate` opens a window showing MeshTerm the
|
||||
way the handheld's own screen draws it:
|
||||
`--platform` gives you the layout of a handheld in your own terminal. MeshTerm draws it in
|
||||
the font and the colours of your terminal. For a closer look, run `meshterm emulate`. It
|
||||
opens a window that shows MeshTerm as the display of the handheld draws it:
|
||||
|
||||
```bash
|
||||
meshterm emulate --fetch-fonts # once: the font the handhelds draw in
|
||||
@@ -88,25 +96,34 @@ meshterm emulate picocalc-lyra --mock # the PicoCalc's 320×320 screen
|
||||
meshterm emulate cardputer-zero --mock # the Cardputer Zero's 320×170 screen
|
||||
```
|
||||
|
||||
**It isn't a strict emulator.** It doesn't imitate the device's computer: MeshTerm runs on
|
||||
your own machine, at your machine's speed. What it copies is the display's constraints —
|
||||
the screen's size in pixels, the grid of letters on it, the font, the colours the device
|
||||
can show, and the keys that drive the F-key lane. On the PicoCalc that means sixteen
|
||||
colours, and bold drawn as a brighter colour rather than a heavier letter, the way its
|
||||
console does it. Those are what decide whether a screen fits and reads well, so those are
|
||||
what the window reproduces.
|
||||
**The emulator is not a strict emulator.** It does not copy the computer of the handheld.
|
||||
MeshTerm runs on your own machine, at the speed of your machine. The emulator copies the
|
||||
constraints of the display and the keyboard:
|
||||
|
||||
Your keyboard's F-keys stand in for the lane's keys: **F1** to **F5** for the PicoCalc's,
|
||||
and **F4** to **F8** for the Cardputer Zero's **Fn+4** to **Fn+8**. Hold **Shift** for the
|
||||
second set. You can also click them: click a coloured key at the bottom of the screen, or
|
||||
one of the Cardputer Zero's keys drawn under it, and hold **Shift** while you click for the
|
||||
second set. Everything you type after the device's name goes to the MeshTerm in the window:
|
||||
`--mock` for the simulated radio, or `--port`, `--ble` or `--tcp` for a real one. `--scale`
|
||||
sets the zoom, **Ctrl+Shift+S** saves a screenshot at the screen's real size, and closing
|
||||
the window quits MeshTerm.
|
||||
- the size of the display in pixels
|
||||
- the grid of letters on the display
|
||||
- the font
|
||||
- the colours that the handheld can show
|
||||
- the keys that drive the F-key lane
|
||||
|
||||
It needs MeshTerm installed with pip or pipx; the one-file downloads leave out the part of
|
||||
Python that opens windows. The font is Terminus, which has its own licence, so MeshTerm
|
||||
doesn't ship it: `--fetch-fonts` downloads the official release and checks it is the real
|
||||
one. On a machine that can't download it, fetch `terminus-font-4.49.1.tar.gz` some other
|
||||
way and install it with `meshterm emulate --archive terminus-font-4.49.1.tar.gz`.
|
||||
On the PicoCalc, this means sixteen colours. It also means that bold text is a brighter
|
||||
colour and not a heavier letter, as on the console of the PicoCalc. These constraints
|
||||
decide if a screen fits and is easy to read. Thus the window reproduces them.
|
||||
|
||||
The F-keys of your keyboard replace the keys of the lane. Use **F1** to **F5** for the
|
||||
PicoCalc. Use **F4** to **F8** for **Fn+4** to **Fn+8** on the Cardputer Zero. Hold
|
||||
**Shift** for the second set. You can also click the keys. Click a coloured key at the
|
||||
bottom of the display, or one of the keys of the Cardputer Zero that are drawn under it.
|
||||
Hold **Shift** while you click for the second set.
|
||||
|
||||
Each argument that you type after the name of the handheld goes to the MeshTerm in the
|
||||
window. Use `--mock` for the simulated radio. Use `--port`, `--ble`, or `--tcp` for a real
|
||||
one. `--scale` sets the zoom. **Ctrl+Shift+S** saves a screenshot at the real size of the
|
||||
display. When you close the window, MeshTerm quits.
|
||||
|
||||
The emulator needs MeshTerm installed with pip or pipx. The one-file downloads do not have
|
||||
the part of Python that opens windows. The font is Terminus, which has its own licence.
|
||||
Thus MeshTerm does not include it. `--fetch-fonts` downloads the official release and
|
||||
checks that it is the real one. If your machine cannot download it, download
|
||||
`terminus-font-4.49.1.tar.gz` in another way. Then install it with
|
||||
`meshterm emulate --archive terminus-font-4.49.1.tar.gz`.
|
||||
+113
-96
@@ -3,18 +3,18 @@
|
||||
> [!CAUTION]
|
||||
> **The Cardputer Zero is not supported yet.**
|
||||
>
|
||||
> MeshTerm now runs on a real Cardputer Zero, with M5Stack's Cap LoRa-1262 as its radio,
|
||||
> but only from a copy of the source code you set up yourself. There is no package in
|
||||
> M5Stack's app store, and the keyboard has only been checked by sending key events to it
|
||||
> from another computer, not by someone typing on it.
|
||||
> MeshTerm now runs on a real Cardputer Zero, with M5Stack's Cap LoRa-1262 as its
|
||||
> radio. But it runs only from a copy of the source code that you set up yourself. There is
|
||||
> no package in M5Stack's app store. Also, nobody has typed on the keyboard. Its keys
|
||||
> are checked only when you send key events to it from another computer.
|
||||
>
|
||||
> Don't buy a Cardputer Zero to run MeshTerm on it yet. This page will say so when that
|
||||
> Do not buy a Cardputer Zero to run MeshTerm on it yet. This page will tell you 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.
|
||||
M5Stack makes three Cardputers. This page is about the **Cardputer Zero**. It has a
|
||||
Raspberry Pi Compute Module 0 inside, and it runs Linux. The original Cardputer and the
|
||||
Cardputer-Adv are ESP32 microcontroller boards. They cannot run MeshTerm, and nothing on
|
||||
this page applies to them.
|
||||
|
||||
- [The device](#the-device)
|
||||
- [Where the work stands](#where-the-work-stands)
|
||||
@@ -33,46 +33,50 @@ applies to them.
|
||||
| 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.
|
||||
MeshTerm draws the display in 53 columns by 14 rows. The width is the same as on the
|
||||
PicoCalc, but the height is approximately half. Thus 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.
|
||||
The launcher gives an app the whole display and the keyboard, but it gives no text console
|
||||
to run in. Thus MeshTerm has its own console. This is a small program inside MeshTerm. It
|
||||
draws each character on the display itself and reads the keys directly.
|
||||
|
||||
## Where the work stands
|
||||
|
||||
**Done, and tried on a real Cardputer Zero:**
|
||||
|
||||
- MeshTerm starts from M5Stack's app launcher and draws on the screen itself.
|
||||
- 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 battery in the title bar is the Cardputer Zero's own.
|
||||
- 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.
|
||||
- M5Stack's Cap LoRa-1262 radio add-on (see [Radios](#radios)).
|
||||
- Holding **Esc** to quit, which is how the launcher closes any app. After a second,
|
||||
MeshTerm shows a box saying so, with a bar that fills over the next two seconds, and it
|
||||
stops sending anything new over the radio while the box is up. Let go before the bar
|
||||
fills and you're back where you were. Hold on, and MeshTerm saves everything and closes
|
||||
in good time, before the launcher would force it. A quick press of **Esc** still goes
|
||||
back a screen, as it does everywhere else.
|
||||
- MeshTerm starts from M5Stack's app launcher and draws on the display itself.
|
||||
- MeshTerm has a layout for the display of 14 rows. The title bar also shows the unread
|
||||
count and the battery. The tab strips have one row. The dialogs fit the short display.
|
||||
- The battery in the title bar is the Cardputer Zero's own battery.
|
||||
- The row of function-key labels is along the bottom of the display. **Fn+4** to **Fn+8**
|
||||
drive it. These keys are directly under the display. **Shift** with them gives a second
|
||||
set.
|
||||
- M5Stack's Cap LoRa-1262 radio add-on (refer to [Radios](#radios)).
|
||||
- Hold **Esc** to quit. This is how the launcher closes each app. After one second,
|
||||
MeshTerm shows a box that says so. The box has a bar that fills over the next two
|
||||
seconds. While the box is visible, MeshTerm sends nothing new over the radio. If you
|
||||
release **Esc** before the bar is full, you return to the screen that you were on. If you
|
||||
keep holding it, MeshTerm saves everything and closes in good time, before the launcher
|
||||
forces it. A short press of **Esc** still goes back one screen, as on all other
|
||||
handhelds.
|
||||
|
||||
**Not done:**
|
||||
|
||||
- A package for M5Stack's app store. Until there is one, you install MeshTerm yourself
|
||||
(see below).
|
||||
- Typing on the real keyboard. The arrows, Enter, Esc, Fn+4 to Fn+8, and Shift have been
|
||||
checked by sending their key events to the keyboard from another computer, and the
|
||||
symbols on the Sym layer match M5Stack's own keymap on the device. Nobody has typed a
|
||||
- A package for M5Stack's app store. Until there is one, you install MeshTerm
|
||||
yourself (refer to the next section).
|
||||
- Typing on the real keyboard. Key events for the arrows, Enter, Esc, Fn+4 to Fn+8, and
|
||||
Shift were sent to the keyboard from another computer. They worked. The symbols on the
|
||||
Sym layer agree with M5Stack's own keymap on the handheld. Nobody has typed a
|
||||
message on it yet.
|
||||
|
||||
## Installing it on the device
|
||||
|
||||
This is for people comfortable with a Linux command line. You need the Cardputer Zero on
|
||||
Wi-Fi, and a way to type commands on it: SSH from another computer is easiest.
|
||||
This section is for users who know a Linux command line. The Cardputer Zero must be on
|
||||
Wi-Fi. You also need a way to type commands on it. SSH from another computer is the
|
||||
easiest way.
|
||||
|
||||
Get MeshTerm and the radio library, and fetch the font the screen is drawn in:
|
||||
1. Get MeshTerm and the radio library, and download the font that the display uses:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/jpmartineau/MeshTerm.git
|
||||
@@ -82,124 +86,137 @@ python3 -m venv .venv
|
||||
.venv/bin/meshterm emulate --fetch-fonts
|
||||
```
|
||||
|
||||
Then put MeshTerm in the launcher. This asks for your password once, because the launcher's
|
||||
list of apps belongs to the system:
|
||||
2. Put MeshTerm in the launcher. The command asks for your password one time, because the
|
||||
list of apps of the launcher belongs to the system:
|
||||
|
||||
```bash
|
||||
scripts/cardputer-zero/launcher-entry.sh
|
||||
```
|
||||
|
||||
MeshTerm now has an icon in the launcher. `scripts/cardputer-zero/launcher-entry.sh remove`
|
||||
takes it out again.
|
||||
MeshTerm now has an icon in the launcher. To remove the icon, run
|
||||
`scripts/cardputer-zero/launcher-entry.sh remove`.
|
||||
|
||||
### Typing long text from your computer
|
||||
|
||||
A long password or key is hard work on a keyboard this small. From another computer that
|
||||
can reach the Cardputer over SSH, `scripts/cardputer-zero/type-text.py` types it for you,
|
||||
into whatever app has the screen:
|
||||
A long password or key is hard to type on a keyboard this small. Use another computer that
|
||||
can reach the Cardputer over SSH. The tool `scripts/cardputer-zero/type-text.py` types the
|
||||
text for you, into the app that has the display:
|
||||
|
||||
```bash
|
||||
python scripts/cardputer-zero/type-text.py --host pi@cardputer.local
|
||||
```
|
||||
|
||||
Paste the text when it asks, and press Enter. Your typing stays hidden, and the text goes
|
||||
to the Cardputer over SSH, so it never shows on either screen. Open the field you want it
|
||||
in on the Cardputer first, and leave its keyboard alone until the tool says it's done.
|
||||
1. On the Cardputer, open the field that you want to fill.
|
||||
2. Run the command above on your computer.
|
||||
3. Paste the text when the tool asks for it, and press Enter. The tool does not show your
|
||||
typing. The text goes to the Cardputer over SSH, thus it does not show on either
|
||||
display.
|
||||
4. Do not touch the Cardputer's keyboard until the tool says that it is done.
|
||||
|
||||
## Trying it in the emulator
|
||||
|
||||
The emulator opens a window showing the Cardputer Zero's screen, scaled up so it's
|
||||
readable. Run it with a simulated radio, and you can try the layout without the device or
|
||||
a radio.
|
||||
The emulator opens a window that shows the display of the Cardputer Zero. The window
|
||||
scales the display up so that you can read it. If you run it with a simulated radio, you
|
||||
can try the layout without the handheld and without a radio.
|
||||
|
||||
It isn't a strict emulator. It doesn't pretend to be the Cardputer Zero's computer:
|
||||
MeshTerm runs on your own computer, at your computer's speed. What it copies is the
|
||||
screen and the keyboard — the screen's size in pixels, the font, the colours, and the keys
|
||||
that drive the labels along the bottom. That's enough to show what fits on the screen and
|
||||
what doesn't, which is what it's for.
|
||||
The emulator is not a strict emulator. It does not copy the computer of the Cardputer
|
||||
Zero. MeshTerm runs on your own computer, at the speed of your computer. The emulator
|
||||
copies the constraints of the display and the keyboard: the size of the display in pixels,
|
||||
the font, the colours, and the keys that drive the labels along the bottom. This shows what
|
||||
fits on the display and what does not. That is the purpose of the emulator.
|
||||
|
||||
It needs MeshTerm installed with pip or pipx (see the [README](../../README.md)). The
|
||||
one-file downloads leave out the part of Python that opens windows.
|
||||
The emulator needs MeshTerm installed with pip or pipx (refer to the
|
||||
[README](../../README.md)). The one-file downloads do not have the part of Python that
|
||||
opens windows.
|
||||
|
||||
Once, fetch the font the screen is drawn in:
|
||||
1. Download the font that the display uses. Do this one time:
|
||||
|
||||
```bash
|
||||
meshterm emulate --fetch-fonts
|
||||
```
|
||||
|
||||
Then start the emulator:
|
||||
2. Start the emulator:
|
||||
|
||||
```bash
|
||||
meshterm emulate cardputer-zero --mock
|
||||
```
|
||||
|
||||
`--mock` is the simulated radio. Point `MESHTERM_HOME` at an empty folder first, so its
|
||||
made-up nodes stay out of your real history. `--scale 4` makes the window bigger.
|
||||
`--mock` is the simulated radio. Before you start, set `MESHTERM_HOME` to an empty folder.
|
||||
Then the made-up nodes of the simulation stay out of your real history. `--scale 4` makes
|
||||
the window bigger.
|
||||
|
||||
In the window, your keyboard's **F4** to **F8** stand in for **Fn+4** to **Fn+8**, and
|
||||
holding **Shift** shows the second set. You can click the keys instead, either the ones
|
||||
drawn under the screen or the coloured keys along its bottom row; hold **Shift** while you
|
||||
click for the second set. **Ctrl+Shift+S** saves a screenshot at the screen's
|
||||
real size. Closing the window quits MeshTerm, and so does holding **Esc** for three
|
||||
seconds, as it does on the device.
|
||||
In the window, the **F4** to **F8** keys of your keyboard replace **Fn+4** to **Fn+8**.
|
||||
Hold **Shift** to show the second set. You can also click the keys. Click the keys that are
|
||||
drawn under the display, or the coloured keys along its bottom row. Hold **Shift** while
|
||||
you click for the second set. **Ctrl+Shift+S** saves a screenshot at the real size of the
|
||||
display. When you close the window, MeshTerm quits. It also quits when you hold **Esc** for
|
||||
three seconds, as it does on the handheld.
|
||||
|
||||
## Radios
|
||||
|
||||
The Cardputer Zero has no LoRa radio built in. There are three ways to add one:
|
||||
The Cardputer Zero has no LoRa radio built in. You can add one in three ways:
|
||||
|
||||
| Radio | Status |
|
||||
| --- | --- |
|
||||
| M5Stack's **Cap LoRa-1262** add-on board | Works, with no setup. MeshTerm runs the mesh node itself while it's open, the same way it does on a uConsole. |
|
||||
| **Cap LoRa-1262** add-on board from M5Stack | Works, with no setup. MeshTerm runs the mesh node itself while it is open, as it does on a uConsole. |
|
||||
| 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. |
|
||||
|
||||
### The Cap LoRa-1262
|
||||
|
||||
Push the Cap onto the expansion header at the back, then start MeshTerm. The device screen
|
||||
lists it as **Cap LoRa-1262**, and `meshterm --spi` reaches it from the command line.
|
||||
1. Push the Cap onto the expansion header at the back.
|
||||
2. Start MeshTerm.
|
||||
|
||||
The Cap has no MeshCore firmware of its own: it's just a radio chip. So while MeshTerm is
|
||||
open, MeshTerm *is* the node, the way it is on a uConsole's AIO board. It needs the radio
|
||||
library installed beside MeshTerm:
|
||||
The device screen lists the Cap as **Cap LoRa-1262**. `meshterm --spi` also reaches it from the
|
||||
command line.
|
||||
|
||||
The Cap has no MeshCore firmware of its own. It is only a radio chip. Thus, while MeshTerm
|
||||
is open, MeshTerm is the node, as it is on the AIO board of a uConsole. MeshTerm needs the
|
||||
radio library, installed next to it:
|
||||
|
||||
```bash
|
||||
pip install 'mesh-term[spi]'
|
||||
```
|
||||
|
||||
When MeshTerm connects, it does three things the Cardputer Zero needs before the radio
|
||||
answers. It switches on the power to the expansion header, which is off until something
|
||||
asks for it. It makes sure two of the header's pins are connected to the radio and not to
|
||||
the USB port that shares them. And it switches on the Cap's antenna path. When MeshTerm
|
||||
quits, it switches the header's power back off, so the Cap doesn't drain the battery
|
||||
while nothing is using it.
|
||||
When MeshTerm connects, it does three things that the Cardputer Zero needs before the
|
||||
radio answers:
|
||||
|
||||
A new node starts on MeshCore's US/Canada settings (910.525 MHz). Anywhere else, change
|
||||
them on **Device config** before you send anything. The node's key, contacts, and channels
|
||||
are kept in `~/.meshterm/radio/spidev0.1/`.
|
||||
- It switches on the power to the expansion header. The power is off until a program asks
|
||||
for it.
|
||||
- It makes sure that two pins of the header connect to the radio and not to the USB port
|
||||
that shares them.
|
||||
- It switches on the antenna path of the Cap.
|
||||
|
||||
When MeshTerm quits, it switches the power of the header off. Thus the Cap does not drain
|
||||
the battery while nothing uses it.
|
||||
|
||||
A new node starts with the US/Canada settings of MeshCore (910.525 MHz). In other regions,
|
||||
change the settings on **Device config** before you send anything. MeshTerm keeps the key,
|
||||
the contacts, and the channels of the node in `~/.meshterm/radio/spidev0.1/`.
|
||||
|
||||
#### The Cap's GPS
|
||||
|
||||
The Cap also carries a GPS, and MeshTerm can keep your node's position up to date from it.
|
||||
It's off until you switch it on, the same as on MeshCore firmware. To switch it on:
|
||||
The Cap also has a GPS. MeshTerm can use it to keep the position of your node current. The
|
||||
GPS is off until you switch it on, as on MeshCore firmware. To switch it on:
|
||||
|
||||
1. Open **Device config**.
|
||||
2. Under **Custom variables**, set **GPS** to on.
|
||||
3. Apply.
|
||||
|
||||
Once the GPS has a fix, it becomes your node's position. Out in the open, the first fix
|
||||
usually takes about a minute. Indoors, it may never come.
|
||||
When the GPS has a fix, the fix becomes the position of your node. In the open air, the
|
||||
first fix usually takes approximately one minute. Indoors, the fix may never come.
|
||||
|
||||
Two things don't change:
|
||||
Two things do not change:
|
||||
|
||||
- **Whether your adverts include your position is still up to you.** That's the **Share
|
||||
location** setting on Device config. With it off, the GPS still moves your node on
|
||||
MeshTerm's own map, but nobody else sees where you are.
|
||||
- **Moving your node on the map by hand doesn't stick while the GPS is on.** The next fix
|
||||
puts it back where the GPS says you are. Switch the GPS off first.
|
||||
- **You decide if your adverts include your position.** This is the **Share location**
|
||||
setting on Device config. If it is off, the GPS still moves your node on MeshTerm's own map,
|
||||
but nobody else sees where you are.
|
||||
- **A hand move of your node on the map does not stay while the GPS is on.** The next fix
|
||||
puts the node back where the GPS says you are. Switch the GPS off first.
|
||||
|
||||
**GPS interval** sets how often the position is updated, in seconds. It's 0 to start with,
|
||||
which means every fix, about once a second. MeshTerm remembers your last fix when it quits,
|
||||
so the next start begins where you were.
|
||||
**GPS interval** sets how often the position changes, in seconds. The default is 0. This
|
||||
means each fix, approximately one each second. MeshTerm remembers your last fix when it
|
||||
quits. Thus the next start begins where you were.
|
||||
|
||||
The GPS talks to the Cardputer over a serial port, the way a companion on a USB cable
|
||||
does. It isn't a companion, though, so the device screen doesn't list it.
|
||||
The GPS talks to the Cardputer over a serial port, as a companion on a USB cable does. But
|
||||
it is not a companion, thus the device screen does not list it.
|
||||
+586
-505
File diff suppressed because it is too large.
Load diff
+350
-320
@@ -1,38 +1,39 @@
|
||||
# MeshTerm on the uConsole
|
||||
|
||||
A ClockworkPi uConsole with the hackergadgets AIO LoRa board has no companion
|
||||
microcontroller and no firmware to flash — the LoRa chip sits straight on the host's SPI
|
||||
bus. MeshTerm can drive it directly: it starts a mesh node as a child process the moment
|
||||
you connect, talks to it over the standard companion protocol on a loopback port, and ends
|
||||
it when you disconnect or quit. No separate service, no TCP port to remember — just
|
||||
`meshterm --spi`.
|
||||
microcontroller and no firmware to flash. The LoRa chip is directly on the SPI bus of the
|
||||
host. MeshTerm can drive the chip directly. When you connect, MeshTerm starts a mesh node
|
||||
as a child process. It talks to the node over the standard companion protocol on a loopback
|
||||
port. When you disconnect or quit, MeshTerm ends the node. There is no separate service and
|
||||
no TCP port to remember. You only run `meshterm --spi`.
|
||||
|
||||
If you'd rather the node stayed on the mesh while MeshTerm is closed, a small bridge can
|
||||
run it as a background service instead, fronting the same chip as a network companion.
|
||||
That's [further down](#always-on-the-bridge) — most people want the section above it.
|
||||
If you want the node to stay on the mesh while MeshTerm is closed, you can use a small
|
||||
bridge. The bridge runs the node as a background service. It presents the same chip as a
|
||||
network companion. The bridge is [further down](#always-on-the-bridge). Most users must
|
||||
use the direct connection that is described before it.
|
||||
|
||||
> ⚠ **Read this first.** Either route edits your uConsole's boot configuration, adds your
|
||||
> user to hardware groups, and then keys a LoRa transmitter. It assumes you are comfortable
|
||||
> at a Linux shell and know which frequency plan and power limits apply where you live —
|
||||
> what the radio does on air is your responsibility, not MeshTerm's. There is a real risk
|
||||
> of leaving the system, the radio, or the mesh around you in a worse state if a step goes
|
||||
> wrong. You do this at your own risk.
|
||||
> ⚠ **WARNING: Read this first.** Each route edits the boot configuration of your
|
||||
> uConsole, adds your user to hardware groups, and then keys a LoRa transmitter. You must
|
||||
> know a Linux shell well. You must also know which frequency plan and which power limits
|
||||
> apply where you live. What the radio does on the air is your responsibility, not the
|
||||
> responsibility of MeshTerm. If a step goes wrong, the system, the radio, or the mesh
|
||||
> around you can be in a worse state. You do this at your own risk.
|
||||
>
|
||||
> **Read the whole manual through once before you start.** Which install route you take
|
||||
> depends on how you're running MeshTerm, and that's easier to get right knowing the later
|
||||
> steps.
|
||||
> **Read the whole manual one time before you start.** The install route that you take
|
||||
> depends on how you run MeshTerm. You can choose the correct route more easily when you
|
||||
> know the later steps.
|
||||
>
|
||||
> This is a guide, not a prescription. Despite the author's best efforts it may contain
|
||||
> errors, and parts of it will go out of date as packages, runtimes, and boards change. It
|
||||
> is up to you to check every fact in it against your own hardware and the current sources
|
||||
> before you act on it, and every decision along the way is yours. The author accepts no
|
||||
> responsibility for any damage, loss, or injury that results from following this guide.
|
||||
> This is a guide, not a prescription. The author tried to make it correct, but it can
|
||||
> have errors. Parts of it will become out of date when packages, runtimes, and boards
|
||||
> change. You must check each fact in it against your own hardware and the current sources
|
||||
> before you act on it. Each decision is yours. The author accepts no responsibility for
|
||||
> damage, loss, or injury that results from this guide.
|
||||
|
||||
- [What you need](#what-you-need)
|
||||
- [Step 1 — Prepare the system](#step-1--prepare-the-system)
|
||||
- [Step 2 — Install the radio library](#step-2--install-the-radio-library)
|
||||
- [Step 3 — Install MeshTerm](#step-3--install-meshterm)
|
||||
- [Step 4 — Connect](#step-4--connect)
|
||||
- [Step 1: Prepare the system](#step-1-prepare-the-system)
|
||||
- [Step 2: Install the radio library](#step-2-install-the-radio-library)
|
||||
- [Step 3: Install MeshTerm](#step-3-install-meshterm)
|
||||
- [Step 4: Connect](#step-4-connect)
|
||||
- [Moving over from the bridge (0.9.0 and earlier)](#moving-over-from-the-bridge-090-and-earlier)
|
||||
- [Board wiring](#board-wiring)
|
||||
- [Which GPIO chip](#which-gpio-chip)
|
||||
@@ -48,111 +49,119 @@ That's [further down](#always-on-the-bridge) — most people want the section ab
|
||||
|
||||
| Part | Notes |
|
||||
| --- | --- |
|
||||
| ClockworkPi **uConsole** with a Raspberry Pi **CM4 or CM5** | Both cores work the same way; MeshTerm finds the GPIO chip behind the header on either (see [Which GPIO chip](#which-gpio-chip)). ClockworkPi's own A-04, A-06 and R-01 cores are not supported. |
|
||||
| hackergadgets **AIO** expansion board | The board this guide is written for and the one we recommend: an SX1262 on SPI bus 1, plus GPS, an RTL-SDR and a USB hub you don't need here. MeshTerm's SPI defaults are its wiring (AIO v1; the v2 needs one extra setting — see [Board wiring](#board-wiring)). |
|
||||
| An antenna for your region's LoRa band | Required. |
|
||||
| ClockworkPi **uConsole** with a Raspberry Pi **CM4 or CM5** | Both cores work in the same way. MeshTerm finds the GPIO chip behind the header on each core (refer to [Which GPIO chip](#which-gpio-chip)). ClockworkPi's A-04, A-06 and R-01 cores are not supported. |
|
||||
| hackergadgets **AIO** expansion board | This guide is written for this board, and we recommend it. It has an SX1262 on SPI bus 1. It also has a GPS, an RTL-SDR, and a USB hub that you do not need here. MeshTerm's SPI defaults are the wiring of this board (AIO v1). The v2 needs one more setting (refer to [Board wiring](#board-wiring)). |
|
||||
| An antenna for the LoRa band of your region | Necessary. |
|
||||
|
||||
Installing the AIO board into the uConsole is out of scope here — follow
|
||||
[hackergadgets' own setup guide](https://hackergadgets.com/pages/hackergadgets-uconsole-rtl-sdr-lora-gps-rtc-usb-hub-all-in-one-extension-board-setup-guide)
|
||||
for that part.
|
||||
This guide does not describe how to install the AIO board in the uConsole. For that part,
|
||||
follow
|
||||
[the setup guide of hackergadgets](https://hackergadgets.com/pages/hackergadgets-uconsole-rtl-sdr-lora-gps-rtc-usb-hub-all-in-one-extension-board-setup-guide).
|
||||
|
||||
**Software:**
|
||||
|
||||
- Raspberry Pi OS / Debian **bookworm** or **trixie** on the uConsole, 64-bit. This is
|
||||
Linux-only — driving a radio on the host's own SPI bus is not something Windows or macOS
|
||||
can do.
|
||||
- A `/dev/spidev*` node. This needs `dtoverlay=spi1-1cs` in `/boot/firmware/config.txt`.
|
||||
- The GPIO character devices (`/dev/gpiochip*`), and membership of the `spi` and `gpio`
|
||||
groups so you can read and write both without root.
|
||||
- The radio library, **`openhop-core[hardware]` 1.1.3 or newer** — [Step
|
||||
2](#step-2--install-the-radio-library) gets you there. It doesn't have to be in
|
||||
MeshTerm's own Python; MeshTerm looks in several places for it.
|
||||
- MeshTerm itself, on the uConsole — [Step 3](#step-3--install-meshterm).
|
||||
- Raspberry Pi OS / Debian **bookworm** or **trixie** on the uConsole, 64-bit. This is for
|
||||
Linux only. Windows and macOS cannot drive a radio on the SPI bus of the host.
|
||||
- A `/dev/spidev*` device file. This needs `dtoverlay=spi1-1cs` in
|
||||
`/boot/firmware/config.txt`.
|
||||
- The GPIO character device files (`/dev/gpiochip*`). You must also be a member of the
|
||||
`spi` and `gpio` groups, so that you can read and write both without root.
|
||||
- The radio library, **`openhop-core[hardware]` 1.1.3 or newer**. [Step
|
||||
2](#step-2-install-the-radio-library) tells you how to get it. It does not have to be
|
||||
in MeshTerm's own Python. MeshTerm looks for it in several places.
|
||||
- MeshTerm itself, on the uConsole ([Step 3](#step-3-install-meshterm)).
|
||||
|
||||
A node's own radio settings — frequency, bandwidth, spreading factor, coding rate, TX
|
||||
power — are not part of this setup. They're the node's, saved by the node, and you change
|
||||
them the same way you would on any companion: MeshTerm's Device config page. A brand-new
|
||||
node starts on MeshCore's US/Canada preset (910.525 MHz, 62.5 kHz, SF 7, CR 4/5, 22 dBm)
|
||||
until you change it.
|
||||
The radio settings of a node are not part of this setup. These settings are the frequency,
|
||||
the bandwidth, the spreading factor, the coding rate, and the TX power. The node owns them
|
||||
and saves them. You change them in the same way as on any companion, on the Device config
|
||||
page of MeshTerm. A new node starts with the US/Canada preset of MeshCore (910.525 MHz,
|
||||
62.5 kHz, SF 7, CR 4/5, 22 dBm) until you change it.
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Prepare the system
|
||||
## Step 1: Prepare the system
|
||||
|
||||
Enable the SPI overlay. Edit `/boot/firmware/config.txt` and add:
|
||||
1. Enable the SPI overlay. Edit `/boot/firmware/config.txt` and add this line:
|
||||
|
||||
```
|
||||
dtoverlay=spi1-1cs
|
||||
```
|
||||
|
||||
Add yourself to the groups that own the SPI and GPIO device nodes:
|
||||
2. Add yourself to the groups that own the SPI and GPIO device files:
|
||||
|
||||
```bash
|
||||
sudo usermod -aG spi,gpio $USER
|
||||
```
|
||||
|
||||
Reboot so both the overlay and the group membership take effect:
|
||||
3. Reboot. The overlay and the group membership take effect after the reboot:
|
||||
|
||||
```bash
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
After it comes back, check the device nodes exist and that you can use them:
|
||||
4. After the uConsole starts again, check that the device files exist and that you can use
|
||||
them:
|
||||
|
||||
```bash
|
||||
ls -l /dev/spidev* /dev/gpiochip*
|
||||
groups
|
||||
```
|
||||
|
||||
You should see `/dev/spidev1.0` (or another `spidev*` node) and at least one `gpiochip`
|
||||
listed, and `spi` and `gpio` in your `groups` output. Which `gpiochip` carries the radio's
|
||||
pins depends on your core and your kernel; MeshTerm works that out itself (see [Which GPIO
|
||||
chip](#which-gpio-chip)).
|
||||
You must see `/dev/spidev1.0` (or another `spidev*` file) and at least one `gpiochip` in
|
||||
the list. You must also see `spi` and `gpio` in the output of `groups`. The `gpiochip` that
|
||||
has the pins of the radio depends on your core and your kernel. MeshTerm finds it itself
|
||||
(refer to [Which GPIO chip](#which-gpio-chip)).
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Install the radio library
|
||||
## Step 2: Install the radio library
|
||||
|
||||
MeshTerm needs `openhop-core[hardware]` 1.1.3 or newer importable by *some* Python on the
|
||||
machine — not necessarily MeshTerm's own. It looks, in order: a `python` named in the
|
||||
profile's `spi` table; MeshTerm's own interpreter; the bridge's venv at
|
||||
`~/.local/share/meshterm-spi-bridge/venv/bin/python`; `/opt/venvs/meshcore-uconsole/bin/python`;
|
||||
then `python3` on your `PATH`. Whichever route you take below, that's the interpreter it
|
||||
lands in.
|
||||
MeshTerm needs `openhop-core[hardware]` 1.1.3 or newer. Some Python on the machine must be
|
||||
able to import it. It does not have to be MeshTerm's own Python. MeshTerm looks in
|
||||
this order:
|
||||
|
||||
- **Installed MeshTerm with `pipx`?** Inject it into MeshTerm's own environment:
|
||||
1. A `python` that is named in the `spi` table of the profile.
|
||||
2. MeshTerm's own interpreter.
|
||||
3. The venv of the bridge, at `~/.local/share/meshterm-spi-bridge/venv/bin/python`.
|
||||
4. `/opt/venvs/meshcore-uconsole/bin/python`.
|
||||
5. `python3` on your `PATH`.
|
||||
|
||||
Whichever route you take below, the library goes into one of these interpreters. Select
|
||||
the route that agrees with how you installed MeshTerm.
|
||||
|
||||
- **Did you install MeshTerm with `pipx`?** Inject the library into MeshTerm's own environment:
|
||||
|
||||
```bash
|
||||
pipx inject mesh-term 'openhop-core[hardware]'
|
||||
```
|
||||
|
||||
- **Installed with `pip`?** Pull in the `spi` extra when you install MeshTerm (see
|
||||
[Step 3](#step-3--install-meshterm)):
|
||||
- **Did you install MeshTerm with `pip`?** Add the `spi` extra when you install MeshTerm
|
||||
(refer to [Step 3](#step-3-install-meshterm)):
|
||||
|
||||
```bash
|
||||
pip install 'mesh-term[spi]'
|
||||
```
|
||||
|
||||
- **Running the one-file Linux ARM64 build?** It's a single frozen binary and can't carry
|
||||
the library, so give it a venv of its own — the same one the bridge guide uses, so it's
|
||||
shared if you ever run both:
|
||||
- **Do you run the one-file Linux ARM64 build?** It is a single frozen binary, and it
|
||||
cannot hold the library. Thus give it a venv of its own. This is the same venv that the
|
||||
bridge guide uses, so you can share it if you run both:
|
||||
|
||||
```bash
|
||||
python3 -m venv ~/.local/share/meshterm-spi-bridge/venv
|
||||
~/.local/share/meshterm-spi-bridge/venv/bin/pip install "openhop-core[hardware]"
|
||||
```
|
||||
|
||||
MeshTerm finds it there automatically; no configuration needed.
|
||||
MeshTerm finds the venv there automatically. It needs no configuration.
|
||||
|
||||
The older `pymc_core` runtime (the library's name before 2026) is not supported for a
|
||||
direct connection — only the standalone bridge still carries the compatibility shims it
|
||||
needs.
|
||||
A direct connection does not support the older `pymc_core` runtime (the name of the library
|
||||
before 2026). Only the standalone bridge still has the compatibility shims that this
|
||||
runtime needs.
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Install MeshTerm
|
||||
## Step 3: Install MeshTerm
|
||||
|
||||
Same as any other machine — the simplest way is the Linux ARM64 one-file release:
|
||||
The installation is the same as on any other machine. The simplest way is the Linux ARM64
|
||||
one-file release:
|
||||
|
||||
```bash
|
||||
curl -fL -o meshterm https://github.com/jpmartineau/MeshTerm/releases/latest/download/meshterm-linux-arm64
|
||||
@@ -160,7 +169,7 @@ chmod +x meshterm
|
||||
sudo mv meshterm /usr/local/bin/meshterm
|
||||
```
|
||||
|
||||
Or install it from the repository with `pipx`:
|
||||
You can also install it from the repository with `pipx`:
|
||||
|
||||
```bash
|
||||
sudo apt install pipx
|
||||
@@ -169,7 +178,7 @@ pipx ensurepath
|
||||
pipx inject mesh-term 'openhop-core[hardware]'
|
||||
```
|
||||
|
||||
Or with `pip`, pulling in the radio library as its own extra in one line:
|
||||
Or use `pip`, which adds the radio library as its own extra in one line:
|
||||
|
||||
```bash
|
||||
pip install 'mesh-term[spi]'
|
||||
@@ -177,36 +186,43 @@ pip install 'mesh-term[spi]'
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Connect
|
||||
## Step 4: Connect
|
||||
|
||||
```bash
|
||||
meshterm --spi
|
||||
```
|
||||
|
||||
`--spi` finds the AIO's radio at `/dev/spidev1.0` and starts a node on it. The startup
|
||||
device picker also lists it — a row reading **uConsole AIO (/dev/spidev1.0)** with a 📍
|
||||
icon — whenever that device node exists, so `meshterm` with no flags gets you there too.
|
||||
MeshTerm only calls it that once four things all say it's on a uConsole: the uConsole's
|
||||
screen, power chip, and backlight as ClockworkPi's uConsole overlay declares them, and the
|
||||
uConsole's own keyboard on USB. If any one is missing, the row reads **SPI radio** instead.
|
||||
`--spi` finds the radio of the AIO at `/dev/spidev1.0` and starts a node on it. The startup
|
||||
device picker also lists the radio, when that device file exists. The row reads **uConsole
|
||||
AIO (/dev/spidev1.0)** and has a 📍 icon. Thus `meshterm` with no flags also gets you there.
|
||||
|
||||
You're connected when **Device info** or the dashboard shows a node name instead of a
|
||||
connection error. MeshTerm reports the device model as **uConsole AIO** (or **MeshTerm SPI
|
||||
node** where it isn't sure it's on a uConsole), and the header's connection label reads
|
||||
**SPI**.
|
||||
MeshTerm uses that name only when four things show that it is on a uConsole:
|
||||
|
||||
**What happens underneath.** MeshTerm starts the node when it connects and ends it when
|
||||
you disconnect or quit — including a crash: the node exits with MeshTerm, and the kernel
|
||||
frees the GPIO lines and SPI handle the moment its process does. The trade-off is the one
|
||||
this implies: **the node is off the mesh while MeshTerm is closed.** If you want it on all
|
||||
the time, that's what [the bridge](#always-on-the-bridge) is for.
|
||||
- the screen of the uConsole, as the overlay of ClockworkPi for the uConsole declares it
|
||||
- the power chip, as the same overlay declares it
|
||||
- the backlight, as the same overlay declares it
|
||||
- the uConsole's own keyboard, on USB
|
||||
|
||||
**Where its state lives.** Each radio keeps its identity, preferences, channels, and
|
||||
contacts under `~/.meshterm/radio/spidev1.0/` — `identity.key`, `prefs.json`,
|
||||
`channels.json`, `contacts.json` — plus a running log, `node.log` (the previous run's is
|
||||
kept as `node.log.1`). All of it survives a restart.
|
||||
If one of these is missing, the row reads **SPI radio**.
|
||||
|
||||
**A profile, if you want a name for it:**
|
||||
You are connected when **Device info** or the dashboard shows a node name and not a
|
||||
connection error. MeshTerm shows the device model as **uConsole AIO**. If it is not sure
|
||||
that it is on a uConsole, it shows **MeshTerm SPI node**. The connection label in the
|
||||
header reads **SPI**.
|
||||
|
||||
**What happens underneath.** MeshTerm starts the node when it connects. It ends the node
|
||||
when you disconnect or quit. This includes a crash: the node exits with MeshTerm, and the
|
||||
kernel frees the GPIO lines and the SPI handle when the process ends. The result is that
|
||||
**the node is off the mesh while MeshTerm is closed.** If you want the node on the mesh
|
||||
all the time, use [the bridge](#always-on-the-bridge).
|
||||
|
||||
**Where the state is.** Each radio keeps its identity, preferences, channels, and contacts
|
||||
in `~/.meshterm/radio/spidev1.0/`. The files are `identity.key`, `prefs.json`,
|
||||
`channels.json`, and `contacts.json`. The folder also has a running log, `node.log`.
|
||||
MeshTerm keeps the log of the previous run as `node.log.1`. All of these files stay after
|
||||
a restart.
|
||||
|
||||
**A profile, if you want a name for the connection:**
|
||||
|
||||
```toml
|
||||
[profiles.aio]
|
||||
@@ -217,58 +233,62 @@ transport = "spi"
|
||||
meshterm -p aio
|
||||
```
|
||||
|
||||
The AIO's wiring is the default, so this profile needs nothing beyond `transport = "spi"`.
|
||||
[Board wiring](#board-wiring) is for when yours differs.
|
||||
The AIO's wiring is the default. Thus this profile needs only `transport = "spi"`.
|
||||
Read [Board wiring](#board-wiring) if the wiring of your board is different.
|
||||
|
||||
---
|
||||
|
||||
## Moving over from the bridge (0.9.0 and earlier)
|
||||
|
||||
> **Only if you set up the bridge with MeshTerm 0.9.0 or earlier.** Before 0.10.0 the bridge
|
||||
> was the only way to use the uConsole's radio, so an older setup is almost certainly
|
||||
> running one. On a fresh install of 0.10.0 or newer there is nothing to move over — skip
|
||||
> ahead to [Board wiring](#board-wiring).
|
||||
> **Read this section only if you set up the bridge with MeshTerm 0.9.0 or earlier.** Before
|
||||
> 0.10.0, the bridge was the only way to use the uConsole's radio. Thus an older setup
|
||||
> almost always runs a bridge. On a new install of 0.10.0 or newer, there is nothing to move
|
||||
> over. Go to [Board wiring](#board-wiring).
|
||||
|
||||
Upgraded from 0.9.0 or earlier with the bridge still installed? The first time you connect
|
||||
with `--spi` (or a profile, or the picker row), MeshTerm carries the bridge's node over —
|
||||
**copying**, never moving, so the bridge still works afterwards if you go back to it:
|
||||
Did you upgrade from 0.9.0 or earlier, and is the bridge still installed? The first time
|
||||
that you connect with `--spi` (or with a profile, or with the picker row), MeshTerm carries
|
||||
the bridge's node over. MeshTerm **copies** the data and never moves it. Thus the
|
||||
bridge still works if you go back to it. MeshTerm copies these items:
|
||||
|
||||
- the identity key — from the `meshcore-console` GUI's copy first
|
||||
(`~/.local/share/meshcore-uconsole/identity.key`), the way the bridge itself looks for
|
||||
it, else the bridge's own (`~/.local/share/meshterm-spi-bridge/identity.key`);
|
||||
- the bridge's `contacts.json`;
|
||||
- the name `uConsole`;
|
||||
- the bridge's radio settings — frequency, bandwidth, spreading factor, coding rate, TX
|
||||
power — read out of the bridge service's `MESHCORE_*` environment.
|
||||
- The identity key. MeshTerm looks first for the copy of the `meshcore-console` GUI
|
||||
(`~/.local/share/meshcore-uconsole/identity.key`), as the bridge itself does. If it is
|
||||
not there, MeshTerm uses the bridge's own key
|
||||
(`~/.local/share/meshterm-spi-bridge/identity.key`).
|
||||
- The bridge's `contacts.json`.
|
||||
- The name `uConsole`.
|
||||
- The bridge's radio settings: the frequency, the bandwidth, the spreading factor, the
|
||||
coding rate, and the TX power. MeshTerm reads them from the `MESHCORE_*` environment of
|
||||
the bridge service.
|
||||
|
||||
This only happens once, while `~/.meshterm/radio/spidev1.0/` has no `identity.key` yet — so
|
||||
your node keeps its public key, and with it its place in everyone else's contact list.
|
||||
This happens only one time, while `~/.meshterm/radio/spidev1.0/` has no `identity.key`.
|
||||
Thus your node keeps its public key, and it keeps its place in the contact list of each
|
||||
other node.
|
||||
|
||||
**Wiring is not carried over.** If your board needed `MESHCORE_EN_PINS` or another wiring
|
||||
variable for the bridge, put the equivalent in the profile's `spi` table (see
|
||||
[Board wiring](#board-wiring)) — the direct connection reads wiring from there, never from
|
||||
the bridge's environment.
|
||||
**MeshTerm does not copy the wiring.** Your board can need `MESHCORE_EN_PINS` or another
|
||||
wiring variable for the bridge. If it does, put the equivalent in the `spi` table of the
|
||||
profile (refer to [Board wiring](#board-wiring)). The direct connection reads the wiring
|
||||
from there, and never from the bridge's environment.
|
||||
|
||||
**Stop the bridge before switching over**, since only one program may hold the radio at a
|
||||
time:
|
||||
Only one program can hold the radio at a time. Thus stop the bridge before you switch over:
|
||||
|
||||
```bash
|
||||
systemctl --user stop meshterm-spi-bridge
|
||||
systemctl --user disable meshterm-spi-bridge # keep it stopped across reboots
|
||||
```
|
||||
|
||||
If MeshTerm sees the bridge service running, it refuses to start the node and tells you the
|
||||
same two commands.
|
||||
If MeshTerm sees that the bridge service runs, it refuses to start the node. It then shows
|
||||
you the same two commands.
|
||||
|
||||
---
|
||||
|
||||
## Board wiring
|
||||
|
||||
The defaults match the hackergadgets uConsole AIO **v1**, which needs no `spi` table at
|
||||
all. State only what differs, under `[profiles.<name>.spi]`. For a board that isn't an
|
||||
AIO — a LoRa HAT on a Raspberry Pi, say — [Adding a radio on the SPI
|
||||
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:
|
||||
The defaults agree with the hackergadgets uConsole AIO **v1**. This board needs no `spi`
|
||||
table. State only what is different, under `[profiles.<name>.spi]`. Your board can be
|
||||
something other than an AIO, for example a LoRa HAT on a Raspberry Pi. In that case,
|
||||
[Adding a radio on the SPI
|
||||
bus](../guide/configuration.md#adding-a-radio-on-the-spi-bus) shows a whole profile. It
|
||||
also shows how to read the pins from the `meshtasticd` configuration of the board:
|
||||
|
||||
```toml
|
||||
[profiles.aio2]
|
||||
@@ -280,47 +300,48 @@ en_pins = [27] # the AIO v2 needs this
|
||||
|
||||
| Setting | Default | What it is |
|
||||
| --- | --- | --- |
|
||||
| `bus_id`, `cs_id` | `1`, `0` | Which SPI bus and chip-select the radio is on (`/dev/spidev<bus_id>.<cs_id>`). |
|
||||
| `cs_pin` | `-1` | A GPIO driven as chip select by hand, instead of the bus's own. |
|
||||
| `gpio_chip` | `-1` | Which `/dev/gpiochip<n>` the pins below are on. `-1` finds the chip behind the 40-pin header by its label — see [Which GPIO chip](#which-gpio-chip). |
|
||||
| `bus_id`, `cs_id` | `1`, `0` | The SPI bus and the chip select of the radio (`/dev/spidev<bus_id>.<cs_id>`). |
|
||||
| `cs_pin` | `-1` | A GPIO that MeshTerm drives by hand as the chip select, instead of the chip select of the bus. |
|
||||
| `gpio_chip` | `-1` | The `/dev/gpiochip<n>` that has the pins below. `-1` finds the chip behind the 40-pin header by its label (refer to [Which GPIO chip](#which-gpio-chip)). |
|
||||
| `use_gpiod_backend` | `false` | Drive the pins through `gpiod` instead of `python-periphery`. |
|
||||
| `reset_pin`, `busy_pin`, `irq_pin` | `25`, `24`, `26` | The chip's reset, busy and interrupt (DIO1) lines. |
|
||||
| `txen_pin`, `rxen_pin` | `-1`, `-1` | An external RF switch's transmit/receive-enable lines, if the board has one. |
|
||||
| `en_pins` | `[]` | Power-enable lines to raise before the chip is touched. The **AIO v2 needs `[27]`.** |
|
||||
| `leds` | `[]` | Switches the board's device tree exposes as LEDs (under `/sys/class/leds/`), each written as `"name=brightness"`. They are set before the chip is touched and put back as they were when MeshTerm lets go. The Cardputer Zero powers its expansion header this way; its Cap needs no profile, but this is what one would say: `["ext_5v_out=1", "ext_usb_gpio_fun=0"]`. |
|
||||
| `pi4io_bus`, `pi4io_address`, `pi4io_high` | `-1`, `0x43`, `[]` | A PI4IOE5V6408 I/O expander that switches the radio's antenna path: its I2C bus (`-1` for none), its address, and the pins to drive high. |
|
||||
| `use_dio2_rf`, `use_dio3_tcxo` | `true`, `true` | Whether DIO2 drives the RF switch and DIO3 a TCXO (both on for the AIO). |
|
||||
| `is_waveshare` | `false` | The Waveshare HAT's wiring quirks, which the radio library knows about. |
|
||||
| `python` | `""` (empty) | An interpreter to run the node under, when the one MeshTerm would find on its own isn't the one you want. Empty lets MeshTerm look. |
|
||||
| `gps_port`, `gps_baud` | `""`, `9600` | A GPS receiver on the same board: its serial port (empty for none) and its speed. The node runs it the way MeshCore firmware runs a board's GPS, switched on from Device config's **GPS** row, and the device screen stops listing the port as a companion. The Cardputer Zero's Cap needs no profile, but this is what one would say: `"/dev/serial0"`, `115200`. |
|
||||
| `reset_pin`, `busy_pin`, `irq_pin` | `25`, `24`, `26` | The reset line, the busy line, and the interrupt line (DIO1) of the chip. |
|
||||
| `txen_pin`, `rxen_pin` | `-1`, `-1` | The transmit-enable and receive-enable lines of an external RF switch, if the board has one. |
|
||||
| `en_pins` | `[]` | The power-enable lines that go high before MeshTerm touches the chip. The **AIO v2 needs `[27]`.** |
|
||||
| `leds` | `[]` | Switches that the device tree of the board shows as LEDs (under `/sys/class/leds/`). Write each one as `"name=brightness"`. MeshTerm sets them before it touches the chip. It puts them back as they were when it releases the radio. The Cardputer Zero powers its expansion header in this way. Its Cap needs no profile, but this is what you would write: `["ext_5v_out=1", "ext_usb_gpio_fun=0"]`. |
|
||||
| `pi4io_bus`, `pi4io_address`, `pi4io_high` | `-1`, `0x43`, `[]` | A PI4IOE5V6408 I/O expander that switches the antenna path of the radio. These are its I2C bus (`-1` for none), its address, and the pins that go high. |
|
||||
| `use_dio2_rf`, `use_dio3_tcxo` | `true`, `true` | If DIO2 drives the RF switch, and if DIO3 drives a TCXO (both are on for the AIO). |
|
||||
| `is_waveshare` | `false` | The wiring quirks of the Waveshare HAT, which the radio library knows. |
|
||||
| `python` | `""` (empty) | An interpreter to run the node under. Use it when the interpreter that MeshTerm finds is not the one that you want. If it is empty, MeshTerm looks for one. |
|
||||
| `gps_port`, `gps_baud` | `""`, `9600` | A GPS receiver on the same board: its serial port (empty for none) and its speed. The node runs the GPS as MeshCore firmware runs the GPS of a board. You switch it on with the **GPS** row on Device config. The device screen then stops listing the port as a companion. The Cap of the Cardputer Zero needs no profile, but this is what you would write: `"/dev/serial0"`, `115200`. |
|
||||
|
||||
An unknown key, or one of the wrong type, stops MeshTerm at startup with the profile
|
||||
named — a misspelt pin is never silently ignored.
|
||||
If a key is unknown or has the wrong type, MeshTerm stops at startup and names the profile.
|
||||
MeshTerm never ignores a misspelt pin without a message.
|
||||
|
||||
Radio settings — frequency, bandwidth, spreading factor, coding rate, TX power — are
|
||||
**not** in this table. They're the node's own, on Device config, as noted in
|
||||
[What you need](#what-you-need).
|
||||
The radio settings are **not** in this table. These are the frequency, the bandwidth, the
|
||||
spreading factor, the coding rate, and the TX power. The node owns them. You change them on
|
||||
Device config, as [What you need](#what-you-need) says.
|
||||
|
||||
### Which GPIO chip
|
||||
|
||||
The AIO's reset, busy and interrupt lines are GPIO 25, 24 and 26 on either core — the pin
|
||||
numbers don't change. What changes is *which* `/dev/gpiochip<n>` Linux puts them on:
|
||||
The reset line, the busy line, and the interrupt line of the AIO are GPIO 25, 24, and 26 on
|
||||
each core. The pin numbers do not change. What changes is the `/dev/gpiochip<n>` that Linux
|
||||
puts them on:
|
||||
|
||||
| Core | The header's GPIO controller | Its chip number |
|
||||
| Core | The GPIO controller of the header | Its chip number |
|
||||
| --- | --- | --- |
|
||||
| CM4 | `pinctrl-bcm2711` | `0` |
|
||||
| CM5, early kernels | `pinctrl-rp1` | `4` |
|
||||
| CM5, kernels since Raspberry Pi aliased the RP1 to chip 0 ([raspberrypi/linux#6144](https://github.com/raspberrypi/linux/pull/6144)) | `pinctrl-rp1` | `0` |
|
||||
| CM5, some later kernels | `pinctrl-rp1` | another number again, as the order the drivers load in moved it |
|
||||
| CM5, kernels since Raspberry Pi made the RP1 an alias of chip 0 ([raspberrypi/linux#6144](https://github.com/raspberrypi/linux/pull/6144)) | `pinctrl-rp1` | `0` |
|
||||
| CM5, some later kernels | `pinctrl-rp1` | another number, because the load order of the drivers moved it |
|
||||
|
||||
So a chip number is not a fact about the board. **MeshTerm looks the chip up by its label
|
||||
instead**: with `gpio_chip` left at its default of `-1`, it asks each `/dev/gpiochip<n>` for
|
||||
its label and uses the one called `pinctrl-rp1`, `pinctrl-bcm2711` or `pinctrl-bcm2835`, so
|
||||
the same profile works on a CM4, a CM5, and across kernel updates. Where no chip carries one
|
||||
of those labels, it falls back to chip `0`.
|
||||
Thus a chip number is not a fixed fact about the board. **MeshTerm looks for the chip by its
|
||||
label.** If `gpio_chip` has its default of `-1`, MeshTerm asks each `/dev/gpiochip<n>` for
|
||||
its label. It uses the chip that is called `pinctrl-rp1`, `pinctrl-bcm2711`, or
|
||||
`pinctrl-bcm2835`. Thus the same profile works on a CM4, on a CM5, and after kernel
|
||||
updates. If no chip has one of these labels, MeshTerm uses chip `0`.
|
||||
|
||||
To see what your system has, `gpiodetect` (from the `gpiod` package) lists every chip with
|
||||
its label:
|
||||
To see what chips your system has, run `gpiodetect` (from the `gpiod` package). It lists
|
||||
each chip with its label:
|
||||
|
||||
```
|
||||
gpiochip0 [pinctrl-rp1] (54 lines)
|
||||
@@ -328,10 +349,10 @@ gpiochip10 [gpio-brcmstb@107d508500] (32 lines)
|
||||
…
|
||||
```
|
||||
|
||||
Set `gpio_chip` to a number only to override the lookup — on a board whose header
|
||||
controller has some other label, say. The [bridge](#always-on-the-bridge) does not look
|
||||
chips up by label: on a CM5 it needs `MESHCORE_GPIO_CHIP` set to whatever `gpiodetect` shows
|
||||
for `pinctrl-rp1`, if that isn't `0`.
|
||||
Set `gpio_chip` to a number only to replace the lookup. For example, do this on a board
|
||||
whose header controller has some other label. The [bridge](#always-on-the-bridge) does not
|
||||
look for chips by label. On a CM5, set `MESHCORE_GPIO_CHIP` to the number that `gpiodetect`
|
||||
shows for `pinctrl-rp1`, if that number is not `0`.
|
||||
|
||||
---
|
||||
|
||||
@@ -339,125 +360,127 @@ for `pinctrl-rp1`, if that isn't `0`.
|
||||
|
||||
| Symptom | Fix |
|
||||
| --- | --- |
|
||||
| The radio library isn't found | Follow [Step 2](#step-2--install-the-radio-library) for however you installed MeshTerm, or set `python` in the profile's `spi` table to an interpreter that has `openhop-core[hardware]`. |
|
||||
| MeshTerm does not find the radio library | Follow [Step 2](#step-2-install-the-radio-library) for the way that you installed MeshTerm. Or set `python` in the `spi` table of the profile to an interpreter that has `openhop-core[hardware]`. |
|
||||
| No `/dev/spidev1.0` | Add `dtoverlay=spi1-1cs` to `/boot/firmware/config.txt` and reboot. |
|
||||
| Permission denied opening the SPI or GPIO device | `sudo usermod -aG spi,gpio $USER`, then log out and back in. |
|
||||
| "the meshterm-spi-bridge service has the radio" | Stop it: `systemctl --user stop meshterm-spi-bridge` (and `disable` it to keep it stopped). |
|
||||
| Permission denied when MeshTerm opens the SPI or GPIO device | Run `sudo usermod -aG spi,gpio $USER`, then log out and log in again. |
|
||||
| "the meshterm-spi-bridge service has the radio" | Stop it with `systemctl --user stop meshterm-spi-bridge`. Also `disable` it to keep it stopped. |
|
||||
| "meshcore-console has the radio open" | Close the GUI and connect again. |
|
||||
| Some other program has the radio | The message names the chip: `sudo lsof /dev/gpiochip<n>` on it shows which program; only one may hold the radio at a time. |
|
||||
| The node starts but never answers | Check its log: `~/.meshterm/radio/spidev1.0/node.log`. |
|
||||
| An AIO v2 radio never comes up | It needs `en_pins = [27]` in the profile's `spi` table — see [Board wiring](#board-wiring). |
|
||||
| Some other program has the radio | The message names the chip. Run `sudo lsof /dev/gpiochip<n>` on it to see which program has it. Only one program can hold the radio at a time. |
|
||||
| The node starts but never answers | Examine its log: `~/.meshterm/radio/spidev1.0/node.log`. |
|
||||
| An AIO v2 radio never starts | It needs `en_pins = [27]` in the `spi` table of the profile (refer to [Board wiring](#board-wiring)). |
|
||||
|
||||
---
|
||||
|
||||
## Always on: the bridge
|
||||
|
||||
Most people should use the direct connection above. Use the bridge instead if you want the
|
||||
node reachable on the mesh **while MeshTerm is closed** — it runs as a `systemd --user`
|
||||
service, on its own, independent of whether MeshTerm is even installed. The trade-off runs
|
||||
the other way from the direct connection: once the bridge is running, it holds the radio's
|
||||
pins for good, so no other LoRa program — including a direct `meshterm --spi` — can use
|
||||
them until you stop it.
|
||||
Most users must use the direct connection that is described above. Use the bridge if you
|
||||
want the node to be on the mesh **while MeshTerm is closed**. The bridge runs as a
|
||||
`systemd --user` service, on its own. It does not need MeshTerm to be installed.
|
||||
|
||||
The bridge puts the standard MeshCore companion protocol in front of the same SPI-attached
|
||||
chip, on a local TCP port. MeshTerm then connects to that port like it would any network
|
||||
companion.
|
||||
The bridge has a disadvantage that the direct connection does not have. When the bridge
|
||||
runs, it holds the pins of the radio all the time. No other LoRa program can use them until
|
||||
you stop the bridge. This includes a direct `meshterm --spi` connection.
|
||||
|
||||
- [Bridge step 1 — Install the radio runtime](#bridge-step-1--install-the-radio-runtime)
|
||||
- [Bridge step 2 — Get the bridge](#bridge-step-2--get-the-bridge)
|
||||
- [Bridge step 3 — Run the preflight](#bridge-step-3--run-the-preflight)
|
||||
- [Bridge step 4 — Run it once in the foreground](#bridge-step-4--run-it-once-in-the-foreground)
|
||||
- [Bridge step 5 — Install it as a service](#bridge-step-5--install-it-as-a-service)
|
||||
- [Bridge step 6 — Connect MeshTerm to it](#bridge-step-6--connect-meshterm-to-it)
|
||||
The bridge puts the standard MeshCore companion protocol in front of the same chip on the
|
||||
SPI bus, on a local TCP port. MeshTerm then connects to that port as it connects to any
|
||||
network companion.
|
||||
|
||||
- [Bridge step 1: Install the radio runtime](#bridge-step-1-install-the-radio-runtime)
|
||||
- [Bridge step 2: Get the bridge](#bridge-step-2-get-the-bridge)
|
||||
- [Bridge step 3: Run the preflight](#bridge-step-3-run-the-preflight)
|
||||
- [Bridge step 4: Run it once in the foreground](#bridge-step-4-run-it-once-in-the-foreground)
|
||||
- [Bridge step 5: Install it as a service](#bridge-step-5-install-it-as-a-service)
|
||||
- [Bridge step 6: Connect MeshTerm to it](#bridge-step-6-connect-meshterm-to-it)
|
||||
- [Updating and uninstalling the bridge](#updating-and-uninstalling-the-bridge)
|
||||
- [Bridge hardware knobs](#bridge-hardware-knobs)
|
||||
- [Bridge troubleshooting](#bridge-troubleshooting)
|
||||
|
||||
The bridge's own wiring and radio defaults are the same AIO v1 numbers as the direct
|
||||
connection's — see the table in [Board wiring](#board-wiring) — just set through
|
||||
`MESHCORE_*` environment variables instead of a profile's `spi` table.
|
||||
The wiring and radio defaults of the bridge are the same AIO v1 numbers as the defaults of
|
||||
the direct connection (refer to the table in [Board wiring](#board-wiring)). But you set
|
||||
them with `MESHCORE_*` environment variables, not with the `spi` table of a profile.
|
||||
|
||||
The radio library that actually drives the chip was renamed in 2026: `pymc_core` became
|
||||
`openhop_core`. PyPI's `pymc-core` stopped at 1.0.12; `openhop-core` carries on from 1.1.x.
|
||||
The bridge supports both and prefers the newer one when it finds both installed.
|
||||
In 2026, the project renamed the radio library that drives the chip: `pymc_core` became
|
||||
`openhop_core`. The `pymc-core` package on PyPI stopped at 1.0.12. `openhop-core` continues
|
||||
from 1.1.x. The bridge supports both libraries. When both are installed, it prefers the
|
||||
newer one.
|
||||
|
||||
### Bridge step 1 — Install the radio runtime
|
||||
### Bridge step 1: Install the radio runtime
|
||||
|
||||
Which route you take depends on your Debian release:
|
||||
The route that you take depends on your Debian release:
|
||||
|
||||
- **trixie**, with the `meshcore-uconsole` package (by cwill747) installed: you already
|
||||
have a working runtime (Python 3.13-based). Skip to
|
||||
[step 2](#bridge-step-2--get-the-bridge).
|
||||
have a working runtime (based on Python 3.13). Go to
|
||||
[step 2](#bridge-step-2-get-the-bridge).
|
||||
- **bookworm**: the trixie `.deb` of `meshcore-uconsole` 1.12.0 is built on Python 3.13
|
||||
and needs glibc 2.38, so it will not install on bookworm, and the APT repository offers
|
||||
nothing for bookworm. Upstream's GitHub Releases also carry a separate bookworm `.deb`
|
||||
built on Python 3.11; it has not been tested with the bridge. The documented route is to
|
||||
give the bridge a runtime of its own:
|
||||
and needs glibc 2.38. Thus it does not install on bookworm, and the APT repository has
|
||||
nothing for bookworm. The GitHub Releases of the upstream project also have a separate
|
||||
bookworm `.deb`, built on Python 3.11. It was not tested with the bridge. The documented
|
||||
route is to give the bridge a runtime of its own:
|
||||
|
||||
```bash
|
||||
python3 -m venv ~/.local/share/meshterm-spi-bridge/venv
|
||||
~/.local/share/meshterm-spi-bridge/venv/bin/pip install "openhop-core[hardware]"
|
||||
```
|
||||
|
||||
This is also the route to take if you'd rather not install the `meshcore-uconsole` package
|
||||
at all.
|
||||
Use this route also if you do not want to install the `meshcore-uconsole` package.
|
||||
|
||||
You'll confirm which runtime the bridge actually found in
|
||||
[step 3](#bridge-step-3--run-the-preflight) — its first check line names the interpreter
|
||||
and the runtime.
|
||||
In [step 3](#bridge-step-3-run-the-preflight), you can confirm which runtime the bridge
|
||||
found. The first check line names the interpreter and the runtime.
|
||||
|
||||
### Bridge step 2 — Get the bridge
|
||||
### Bridge step 2: Get the bridge
|
||||
|
||||
`scripts/uconsole/meshterm-spi-bridge` is a single, self-contained Python file — no install step of
|
||||
its own. Either clone the MeshTerm repo:
|
||||
`scripts/uconsole/meshterm-spi-bridge` is one self-contained Python file. It has no install
|
||||
step of its own. Get it in one of two ways.
|
||||
|
||||
Clone the MeshTerm repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/jpmartineau/MeshTerm
|
||||
cd MeshTerm/scripts/uconsole
|
||||
```
|
||||
|
||||
or download just the one file:
|
||||
Or download only the one file:
|
||||
|
||||
```bash
|
||||
curl -L -o meshterm-spi-bridge \
|
||||
https://raw.githubusercontent.com/jpmartineau/MeshTerm/main/scripts/uconsole/meshterm-spi-bridge
|
||||
```
|
||||
|
||||
Make it executable:
|
||||
Then make the file executable:
|
||||
|
||||
```bash
|
||||
chmod +x meshterm-spi-bridge
|
||||
```
|
||||
|
||||
### Bridge step 3 — Run the preflight
|
||||
### Bridge step 3: Run the preflight
|
||||
|
||||
```bash
|
||||
./meshterm-spi-bridge --check
|
||||
```
|
||||
|
||||
This prints a `Prerequisite check:` block with one line per check. What each line means and
|
||||
what to do if it fails:
|
||||
This command prints a `Prerequisite check:` block with one line for each check. The table
|
||||
tells you what each line means and what to do if the check fails:
|
||||
|
||||
| Check | Meaning if it fails | Fix |
|
||||
| --- | --- | --- |
|
||||
| `node runtime` | Neither `openhop_core` nor `pymc_core` is importable by any interpreter the bridge tried. | Build the venv from [step 1](#bridge-step-1--install-the-radio-runtime), or set `MESHTERM_PYMC_PYTHON` to a python that has one. |
|
||||
| `SPI device` | No `/dev/spidev*` node exists, or it exists but you can't read/write it. | No node at all: add `dtoverlay=spi1-1cs` to `/boot/firmware/config.txt` and reboot. Node present but no permission: `sudo usermod -aG spi $USER`, then log out and back in. |
|
||||
| `GPIO chip` | `/dev/gpiochip0` (or the chip `MESHCORE_GPIO_CHIP` names) is missing or not read/write for you. | `sudo usermod -aG gpio $USER`, then log out and back in. On a CM5, check [which chip](#which-gpio-chip) the header is on. |
|
||||
| `bridge service` / `radio in use` / `port 5000 busy` / `radio is free` | Tells you whether something already holds the radio: the bridge's own boot service, a running `meshcore-console`, or something else already listening on the TCP port. | Only one program may use the radio at a time — see [step 6](#bridge-step-6--connect-meshterm-to-it). If `meshcore-console` is running, close it before running the bridge. |
|
||||
| `node runtime` | None of the interpreters that the bridge tried can import `openhop_core` or `pymc_core`. | Build the venv from [step 1](#bridge-step-1-install-the-radio-runtime). Or set `MESHTERM_PYMC_PYTHON` to a python that has one of them. |
|
||||
| `SPI device` | There is no `/dev/spidev*` file, or you cannot read and write it. | If there is no file, add `dtoverlay=spi1-1cs` to `/boot/firmware/config.txt` and reboot. If the file exists but you have no permission, run `sudo usermod -aG spi $USER`, then log out and log in again. |
|
||||
| `GPIO chip` | `/dev/gpiochip0` (or the chip that `MESHCORE_GPIO_CHIP` names) is missing, or you cannot read and write it. | Run `sudo usermod -aG gpio $USER`, then log out and log in again. On a CM5, check [which chip](#which-gpio-chip) has the header. |
|
||||
| `bridge service` / `radio in use` / `port 5000 busy` / `radio is free` | These lines tell you if a program already holds the radio. It can be the boot service of the bridge, a running `meshcore-console`, or another program that listens on the TCP port. | Only one program can use the radio at a time (refer to [step 6](#bridge-step-6-connect-meshterm-to-it)). If `meshcore-console` runs, close it before you run the bridge. |
|
||||
|
||||
If the first three checks all pass, you're ready to run it.
|
||||
If the first three checks pass, you are ready to run the bridge.
|
||||
|
||||
### Bridge step 4 — Run it once in the foreground
|
||||
### Bridge step 4: Run it once in the foreground
|
||||
|
||||
```bash
|
||||
./meshterm-spi-bridge --run
|
||||
```
|
||||
|
||||
This runs the preflight again and prints it. Before it starts the bridge, it asks whether
|
||||
to add a `bridge` profile to your MeshTerm config
|
||||
This command runs the preflight again and prints it. Before it starts the bridge, it asks
|
||||
if it must add a `bridge` profile to your MeshTerm config
|
||||
(`` Add a 'bridge' profile to /home/you/.meshterm/config.toml so you can just run `meshterm`? [y/N] ``).
|
||||
Either answer is fine; step 5 asks again if you say no. Then it starts the bridge in this
|
||||
terminal. A healthy startup logs something like:
|
||||
You can give either answer. If you answer no, step 5 asks again. Then the command starts
|
||||
the bridge in this terminal. A healthy startup logs lines like these:
|
||||
|
||||
```
|
||||
node runtime openhop_core 1.1.3 (/home/you/.local/share/meshterm-spi-bridge/venv/bin/python)
|
||||
@@ -468,44 +491,45 @@ node identity ab12cd34ef567890… (hash 0xab, name 'uConsole')
|
||||
READY — connect with: meshterm --tcp 127.0.0.1:5000
|
||||
```
|
||||
|
||||
The `radio config` line lists every setting as `key=value`, so the one above is
|
||||
shortened; yours is longer.
|
||||
The `radio config` line lists each setting as `key=value`. The line above is shortened.
|
||||
Your line is longer.
|
||||
|
||||
You can test the bridge this way: leave it running, and in another terminal on the same
|
||||
machine run:
|
||||
To test the bridge, leave it running. In another terminal on the same machine, run:
|
||||
|
||||
```bash
|
||||
meshterm --tcp 127.0.0.1:5000
|
||||
```
|
||||
|
||||
Back in the bridge's terminal, press **Ctrl+C** to stop it.
|
||||
Then go to the terminal of the bridge and press **Ctrl+C** to stop it.
|
||||
|
||||
### Bridge step 5 — Install it as a service
|
||||
### Bridge step 5: Install it as a service
|
||||
|
||||
Run the bridge with no flags to get the interactive menu:
|
||||
1. Run the bridge with no flags to get the interactive menu:
|
||||
|
||||
```bash
|
||||
./meshterm-spi-bridge
|
||||
```
|
||||
|
||||
Choose **2) Install as a service**. This:
|
||||
2. Choose **2) Install as a service**.
|
||||
|
||||
- copies the script to `~/.local/bin/meshterm-spi-bridge`,
|
||||
- writes a `systemd --user` unit at
|
||||
`~/.config/systemd/user/meshterm-spi-bridge.service` that restarts on failure and starts
|
||||
with your session,
|
||||
- enables and starts it now,
|
||||
- tries to enable lingering for your user, so the service comes up at boot even before you
|
||||
log in. If it can't, it prints the command to run yourself:
|
||||
This choice does these things:
|
||||
|
||||
- It copies the script to `~/.local/bin/meshterm-spi-bridge`.
|
||||
- It writes a `systemd --user` unit at
|
||||
`~/.config/systemd/user/meshterm-spi-bridge.service`. The unit restarts on a failure and
|
||||
starts with your session.
|
||||
- It enables the unit and starts it now.
|
||||
- It tries to enable lingering for your user. Then the service starts at boot, before you
|
||||
log in. If lingering fails, the script prints a command for you to run:
|
||||
|
||||
```bash
|
||||
sudo loginctl enable-linger $USER
|
||||
```
|
||||
|
||||
It also offers to add a `[profiles.bridge]` block to your MeshTerm `config.toml` — say yes,
|
||||
it's the profile you'll use next.
|
||||
The script also offers to add a `[profiles.bridge]` block to your MeshTerm `config.toml`.
|
||||
Answer yes. You use this profile next.
|
||||
|
||||
Check on it any time:
|
||||
To check the service at any time, run:
|
||||
|
||||
```bash
|
||||
systemctl --user status meshterm-spi-bridge.service
|
||||
@@ -514,14 +538,14 @@ journalctl --user -u meshterm-spi-bridge.service -f
|
||||
|
||||
Menu option **4) Show status** prints the same information from inside the script.
|
||||
|
||||
### Bridge step 6 — Connect MeshTerm to it
|
||||
### Bridge step 6: Connect MeshTerm to it
|
||||
|
||||
Install MeshTerm on the uConsole the same way as [Step 3](#step-3--install-meshterm)
|
||||
above — the bridge doesn't need the `spi` extra or `openhop-core[hardware]` in MeshTerm's
|
||||
own Python, since the bridge is the one running the node.
|
||||
Install MeshTerm on the uConsole in the same way as in [Step 3](#step-3-install-meshterm)
|
||||
above. The bridge runs the node. Thus MeshTerm's own Python does not need the `spi` extra or
|
||||
`openhop-core[hardware]`.
|
||||
|
||||
If you accepted the profile in step 4 or 5, `~/.meshterm/config.toml` now has this block
|
||||
(the bridge always writes to that path, even if you set `MESHTERM_HOME`):
|
||||
If you accepted the profile in step 4 or step 5, `~/.meshterm/config.toml` now has this
|
||||
block. The bridge always writes to that path, also if you set `MESHTERM_HOME`:
|
||||
|
||||
```toml
|
||||
[profiles.bridge]
|
||||
@@ -536,128 +560,134 @@ Connect with:
|
||||
meshterm -p bridge
|
||||
```
|
||||
|
||||
or, without a profile:
|
||||
Or connect without a profile:
|
||||
|
||||
```bash
|
||||
meshterm --tcp 127.0.0.1:5000
|
||||
```
|
||||
|
||||
You're connected when **Device info** or the dashboard shows a node name (`uConsole` by
|
||||
default) instead of a connection error. MeshTerm reports the device model as
|
||||
`pyMC-spi-bridge` — that's how you tell a bridged node from a real companion or a direct
|
||||
SPI connection.
|
||||
You are connected when **Device info** or the dashboard shows a node name (`uConsole` by
|
||||
default) and not a connection error. MeshTerm shows the device model as `pyMC-spi-bridge`.
|
||||
With this model name, you can tell a node on the bridge from a real companion or from a
|
||||
direct SPI connection.
|
||||
|
||||
**Identity.** If the `meshcore-console` GUI's package is present, the bridge loads *that*
|
||||
package's identity key, so MeshTerm and the GUI share the same public key. Contacts and
|
||||
radio settings are the bridge's own: it keeps contacts in
|
||||
`~/.local/share/meshterm-spi-bridge/contacts.json`, its channels in
|
||||
`~/.local/share/meshterm-spi-bridge/channels.json` (both survive a restart), and builds its
|
||||
radio settings from the `MESHCORE_*` variables and its defaults, never from the GUI's saved
|
||||
settings. Set the `MESHCORE_*` variables to match what the GUI uses. On a library-only
|
||||
install, the bridge mints and keeps its own identity key under
|
||||
**Identity.** If the package of the `meshcore-console` GUI is present, the bridge loads the
|
||||
identity key of *that* package. Thus MeshTerm and the GUI have the same public key. The
|
||||
contacts and the radio settings belong to the bridge:
|
||||
|
||||
- The bridge keeps its contacts in `~/.local/share/meshterm-spi-bridge/contacts.json`.
|
||||
- The bridge keeps its channels in `~/.local/share/meshterm-spi-bridge/channels.json`.
|
||||
Both files stay after a restart.
|
||||
- The bridge builds its radio settings from the `MESHCORE_*` variables and its defaults.
|
||||
It never uses the saved settings of the GUI. Set the `MESHCORE_*` variables to match the
|
||||
values that the GUI uses.
|
||||
|
||||
On an install that has only the library, the bridge makes and keeps its own identity key in
|
||||
`~/.local/share/meshterm-spi-bridge/identity.key`.
|
||||
|
||||
**Only one program may use the radio at a time.** Never run the bridge and the
|
||||
`meshcore-console` GUI at the same time — whichever started second will fail to open the
|
||||
radio. The same goes for a direct `meshterm --spi` connection while the bridge is running.
|
||||
**Only one program can use the radio at a time.** Never run the bridge and the
|
||||
`meshcore-console` GUI at the same time. The program that starts second fails to open the
|
||||
radio. The same is true of a direct `meshterm --spi` connection while the bridge runs.
|
||||
|
||||
### Updating and uninstalling the bridge
|
||||
|
||||
To update, pull or re-download `meshterm-spi-bridge` and reinstall the service (menu option
|
||||
2 again — it overwrites the copy in `~/.local/bin/`). The running service keeps the old
|
||||
code until you restart it:
|
||||
To update the bridge, pull or download `meshterm-spi-bridge` again. Then install the service
|
||||
again (menu option 2). This overwrites the copy in `~/.local/bin/`. The running service
|
||||
keeps the old code until you restart it:
|
||||
|
||||
```bash
|
||||
systemctl --user restart meshterm-spi-bridge.service
|
||||
```
|
||||
|
||||
Option 2 also rewrites the unit file, so any `Environment=` lines you added to it by hand
|
||||
are lost. Put them in a drop-in instead, as [Bridge hardware knobs](#bridge-hardware-knobs)
|
||||
shows.
|
||||
Option 2 also writes the unit file again. Thus it removes each `Environment=` line that you
|
||||
added to the file by hand. Put these lines in a drop-in instead, as
|
||||
[Bridge hardware knobs](#bridge-hardware-knobs) shows.
|
||||
|
||||
To remove the bridge, run the menu and choose **3) Uninstall the service**. This stops and
|
||||
removes the `systemd --user` unit and the installed script copy. It does **not** touch
|
||||
`meshcore-console`, the radio library, your node identity, or your saved contacts and
|
||||
channels — those stay put. If you'd added `[profiles.bridge]` to your MeshTerm config, it
|
||||
tells you to remove that block by hand if you no longer want it.
|
||||
To remove the bridge, run the menu and choose **3) Uninstall the service**. This choice
|
||||
stops the `systemd --user` unit and removes it. It also removes the installed copy of the
|
||||
script. It does **not** change `meshcore-console`, the radio library, the identity of your
|
||||
node, or your saved contacts and channels. These stay as they are. If you added
|
||||
`[profiles.bridge]` to your MeshTerm config, the script tells you to remove that block by
|
||||
hand when you no longer want it.
|
||||
|
||||
### Bridge hardware knobs
|
||||
|
||||
The pin defaults match the hackergadgets uConsole AIO v1, which needs none of these. The
|
||||
**AIO v2 needs `MESHCORE_EN_PINS=27`**, whether or not the `meshcore-console` package is
|
||||
installed. The radio settings default to the US/Canada preset. For a foreground run, export
|
||||
what differs before launching the bridge. For the service, add a drop-in, which survives a
|
||||
reinstall:
|
||||
The pin defaults agree with the hackergadgets uConsole AIO v1. This board needs none of
|
||||
these variables. The **AIO v2 needs `MESHCORE_EN_PINS=27`**, whether or not the
|
||||
`meshcore-console` package is installed. The radio settings default to the US/Canada
|
||||
preset. To run the bridge in the foreground, export the variables that are different before
|
||||
you start it. For the service, add a drop-in. A drop-in stays after a reinstall:
|
||||
|
||||
```bash
|
||||
systemctl --user edit meshterm-spi-bridge.service
|
||||
```
|
||||
|
||||
and put the variables under a `[Service]` heading, one `Environment=` line each:
|
||||
Put the variables under a `[Service]` heading, with one `Environment=` line for each:
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
Environment=MESHCORE_EN_PINS=27
|
||||
```
|
||||
|
||||
Then `systemctl --user restart meshterm-spi-bridge.service`.
|
||||
Then run `systemctl --user restart meshterm-spi-bridge.service`.
|
||||
|
||||
| Variable | What it sets |
|
||||
| --- | --- |
|
||||
| `MESHCORE_BUS_ID`, `MESHCORE_CS_ID`, `MESHCORE_CS_PIN` | which SPI bus, device, and chip-select pin the radio is on |
|
||||
| `MESHCORE_RESET_PIN`, `MESHCORE_BUSY_PIN`, `MESHCORE_IRQ_PIN` | the three control lines |
|
||||
| `MESHCORE_FREQUENCY`, `MESHCORE_TX_POWER` | frequency in Hz, power in dBm |
|
||||
| `MESHCORE_SPREADING_FACTOR`, `MESHCORE_BANDWIDTH`, `MESHCORE_CODING_RATE` | the modem preset — all three must match the mesh you are joining. Whole numbers only: bandwidth in Hz (`62500`), coding rate as the denominator (`5` for 4/5). A decimal or `4/5` stops the bridge at startup. |
|
||||
| `MESHCORE_TXEN_PIN`, `MESHCORE_RXEN_PIN`, `MESHCORE_EN_PINS` | the RF-switch and power-enable lines a board may need (`-1` for none; `EN_PINS` is a comma list — the AIO v2 wants `27`) |
|
||||
| `MESHCORE_USE_DIO2_RF`, `MESHCORE_USE_DIO3_TCXO`, `MESHCORE_IS_WAVESHARE` | whether DIO2 drives the RF switch and DIO3 the TCXO (both on for the AIO), and a flag for a different vendor's wiring the runtime knows about |
|
||||
| `MESHCORE_GPIO_CHIP`, `MESHCORE_USE_GPIOD_BACKEND`, `MESHCORE_PREAMBLE_LENGTH` | which gpiochip (`0` unless set — the bridge doesn't look it up by label, so on a CM5 see [Which GPIO chip](#which-gpio-chip)), whether to drive it through `gpiod`, and the LoRa preamble (leave it unset: the bridge follows MeshCore, 32 symbols up to SF8 and 16 above, and a shorter one leaves the radio deaf to most of the mesh) |
|
||||
| `MESHTERM_PYMC_PYTHON` | force a specific interpreter instead of letting the bridge discover one |
|
||||
| `MESHCORE_BUS_ID`, `MESHCORE_CS_ID`, `MESHCORE_CS_PIN` | The SPI bus, the SPI device, and the chip-select pin of the radio |
|
||||
| `MESHCORE_RESET_PIN`, `MESHCORE_BUSY_PIN`, `MESHCORE_IRQ_PIN` | The three control lines |
|
||||
| `MESHCORE_FREQUENCY`, `MESHCORE_TX_POWER` | The frequency in Hz and the power in dBm |
|
||||
| `MESHCORE_SPREADING_FACTOR`, `MESHCORE_BANDWIDTH`, `MESHCORE_CODING_RATE` | The modem preset. All three must match the mesh that you join. Use whole numbers only: the bandwidth in Hz (`62500`), and the coding rate as the denominator (`5` for 4/5). A decimal or `4/5` stops the bridge at startup. |
|
||||
| `MESHCORE_TXEN_PIN`, `MESHCORE_RXEN_PIN`, `MESHCORE_EN_PINS` | The RF-switch lines and the power-enable lines that a board can need (`-1` for none). `EN_PINS` is a comma list. The AIO v2 needs `27`. |
|
||||
| `MESHCORE_USE_DIO2_RF`, `MESHCORE_USE_DIO3_TCXO`, `MESHCORE_IS_WAVESHARE` | If DIO2 drives the RF switch, and if DIO3 drives the TCXO (both are on for the AIO). The last one is a flag for the wiring of a different vendor that the runtime knows. |
|
||||
| `MESHCORE_GPIO_CHIP`, `MESHCORE_USE_GPIOD_BACKEND`, `MESHCORE_PREAMBLE_LENGTH` | The gpiochip (`0` unless you set it). The bridge does not look for it by label. On a CM5, refer to [Which GPIO chip](#which-gpio-chip). Then the choice to drive the pins through `gpiod`. Then the LoRa preamble. Leave the preamble unset. The bridge follows MeshCore: 32 symbols up to SF8, and 16 symbols above SF8. A shorter preamble makes the radio deaf to most of the mesh. |
|
||||
| `MESHTERM_PYMC_PYTHON` | Forces a specific interpreter, so that the bridge does not look for one |
|
||||
|
||||
The bridge passes a knob only when the runtime's radio constructor accepts it, so the same
|
||||
environment works under `pymc_core` 1.0.x, which lacks the newer ones. Where the
|
||||
`meshcore-console` package is installed, the bridge builds the radio through that
|
||||
package's environment reader, which reads the same names plus a few more of its own (see
|
||||
that package's documentation) and ignores the GUI's saved presets. On a library-only
|
||||
install the table above is the whole of it. Either way, an AIO v2 without
|
||||
`MESHCORE_EN_PINS=27` will not power its radio.
|
||||
The bridge passes a knob only when the radio constructor of the runtime accepts it. Thus
|
||||
the same environment works under `pymc_core` 1.0.x, which does not have the newer knobs.
|
||||
If the `meshcore-console` package is installed, the bridge builds the radio through the
|
||||
environment reader of that package. This reader reads the same names and a few more of its
|
||||
own (refer to the documentation of that package). It ignores the saved presets of the GUI.
|
||||
On an install that has only the library, the table above is complete. In each case, an AIO
|
||||
v2 without `MESHCORE_EN_PINS=27` does not power its radio.
|
||||
|
||||
### Bridge troubleshooting
|
||||
|
||||
| Symptom | Check |
|
||||
| --- | --- |
|
||||
| `node runtime` fails in the preflight | Did you build the venv in [step 1](#bridge-step-1--install-the-radio-runtime)? Is `MESHTERM_PYMC_PYTHON` pointing at the right interpreter, if set? |
|
||||
| `SPI device` shows "no /dev/spidev* found" | Is `dtoverlay=spi1-1cs` in `/boot/firmware/config.txt`? Did you reboot after adding it? |
|
||||
| `SPI device` or `GPIO chip` shows a permission error | Are you in the `spi` and `gpio` groups (`groups`)? Did you log out and back in after `usermod`? |
|
||||
| Preflight warns `radio in use meshcore-console is running:` | Close the GUI before running or installing the bridge. |
|
||||
| Preflight warns `port 5000 busy something is already on 127.0.0.1:5000` | Something other than the bridge service is listening there (an active service is reported as `bridge service running` instead). Usually it is a foreground `--run` in another terminal; stop that one. |
|
||||
| Bridge exits with `bridge failed: GPIO pins still busy after retries` | The bridge retries `radio.begin()` four times only when the GPIO lines are busy. They may still be held by a session that just closed — wait a few seconds and try again. |
|
||||
| MeshTerm connects but direct messages to an old contact say "not found" | Contacts are restored from the bridge's snapshot file at startup. If the contact is not in it, wait until the contact is heard again. |
|
||||
| Reboot, factory reset, or setting a device PIN silently does nothing | Expected — the radio library has no handler for these three, whatever runtime you're on. |
|
||||
| Traces or the live feed don't show up | These rely on compatibility shims the bridge installs (or, under `openhop_core`, on the library's own equivalents). Check the bridge's log for lines starting `compat:` to confirm they're wired. |
|
||||
| `node runtime` fails in the preflight | Did you build the venv in [step 1](#bridge-step-1-install-the-radio-runtime)? If `MESHTERM_PYMC_PYTHON` is set, does it point to the correct interpreter? |
|
||||
| `SPI device` shows "no /dev/spidev* found" | Is `dtoverlay=spi1-1cs` in `/boot/firmware/config.txt`? Did you reboot after you added it? |
|
||||
| `SPI device` or `GPIO chip` shows a permission error | Are you in the `spi` and `gpio` groups (`groups`)? Did you log out and log in again after `usermod`? |
|
||||
| Preflight warns `radio in use meshcore-console is running:` | Close the GUI before you run or install the bridge. |
|
||||
| Preflight warns `port 5000 busy something is already on 127.0.0.1:5000` | A program other than the bridge service listens there. (An active service shows as `bridge service running` instead.) Usually it is a foreground `--run` in another terminal. Stop that one. |
|
||||
| Bridge exits with `bridge failed: GPIO pins still busy after retries` | The bridge tries `radio.begin()` four times, only when the GPIO lines are busy. A session that just closed can still hold them. Wait a few seconds and try again. |
|
||||
| MeshTerm connects but direct messages to an old contact say "not found" | The bridge restores contacts from its snapshot file at startup. If the contact is not in that file, wait until the node hears the contact again. |
|
||||
| A reboot, a factory reset, or a device PIN change does nothing, and there is no message | This is expected. The radio library has no handler for these three actions, with each runtime. |
|
||||
| Traces or the live feed do not show | These use the compatibility shims that the bridge installs. (Under `openhop_core`, they use the equivalents of the library.) Look in the log of the bridge for lines that start with `compat:`. These lines show that the shims are wired. |
|
||||
|
||||
---
|
||||
|
||||
## What this has been tested on
|
||||
|
||||
**The direct connection** has run on a bookworm uConsole with a **CM5** and an AIO v1
|
||||
since 2026-09-24, on MeshTerm's defaults. It has also been tested against the real radio
|
||||
library (`openhop-core`) with a simulated radio. A CM4 has not been tried with it; its
|
||||
header chip is found by the same label lookup (see [Which GPIO chip](#which-gpio-chip)).
|
||||
since 2026-09-24, with the defaults of MeshTerm. It was also tested against the real radio
|
||||
library (`openhop-core`) with a simulated radio. A CM4 was not tried with it. MeshTerm finds
|
||||
the header chip of a CM4 with the same label lookup (refer to
|
||||
[Which GPIO chip](#which-gpio-chip)).
|
||||
|
||||
**The bridge** has been verified on a bookworm uConsole with `openhop-core` 1.1.3: the
|
||||
radio comes up, contacts are restored across a restart, and `info` and `contacts` answer
|
||||
correctly over the TCP connection. The older `pymc-core` 1.0.x path, with all three of the
|
||||
bridge's compatibility shims active, has been exercised in full, including traces and the
|
||||
live feed. MeshTerm's test suite does not cover the bridge.
|
||||
**The bridge** was verified on a bookworm uConsole with `openhop-core` 1.1.3. The radio
|
||||
starts, contacts are restored across a restart, and `info` and `contacts` answer correctly
|
||||
over the TCP connection. The older `pymc-core` 1.0.x path was tested in full, with all
|
||||
three compatibility shims of the bridge active. This includes traces and the live feed.
|
||||
The test suite of MeshTerm does not cover the bridge.
|
||||
|
||||
What has not been confirmed:
|
||||
What is not confirmed:
|
||||
|
||||
- **The direct connection on a CM4** — only the CM5 has run it.
|
||||
- **The bridge under `openhop_core` beyond `info` and `contacts`.** Startup, identity,
|
||||
contact restore and those two reads were exercised; a trace, a message and the live feed
|
||||
under the new runtime were not, though the library's own trace push is what the bridge
|
||||
now relies on for the first.
|
||||
- **Everything on-air** — the per-hop trace semantics the bridge reassembles and the
|
||||
acknowledgement bytes newer firmware appends after the CRC. The code is internally
|
||||
consistent with the library's API; the wire behaviour was seen on one mesh, against the
|
||||
radios in range of one bench.
|
||||
- **The direct connection on a CM4.** Only the CM5 has run it.
|
||||
- **The bridge under `openhop_core`, beyond `info` and `contacts`.** The tests covered
|
||||
startup, identity, contact restore, and those two reads. They did not cover a trace, a
|
||||
message, or the live feed under the new runtime. For traces, the bridge now uses the
|
||||
trace push of the library itself.
|
||||
- **Everything on the air.** This includes the trace semantics for each hop that the bridge
|
||||
puts together again. It also includes the acknowledgement bytes that newer firmware adds
|
||||
after the CRC. The code is consistent with the API of the library. The behaviour on the
|
||||
wire was seen on one mesh, with the radios in range of one bench.
|
||||
+110
-87
@@ -1,14 +1,17 @@
|
||||
# Configuring MeshTerm
|
||||
|
||||
Preferences, device profiles, and where everything is kept. You need none of it to run
|
||||
MeshTerm: it discovers a companion, lets you pick one, and remembers the choice.
|
||||
This page covers preferences, device profiles, and the place where MeshTerm keeps each
|
||||
file. You do not need any of it to run MeshTerm. MeshTerm discovers a companion, lets you
|
||||
select one, and remembers your choice.
|
||||
|
||||
Two files, two jobs — and you need neither to run.
|
||||
MeshTerm has two files for two jobs. You need neither file to run MeshTerm.
|
||||
|
||||
**Preferences** are how MeshTerm behaves: the pause it leaves between transmissions, how
|
||||
hard it retries a message, how far back it keeps history, how a map frames itself. Every one has a built-in default, so change them only where you disagree — from
|
||||
the **Preferences** page in the menu (grouped, staged, saved by one action at the bottom),
|
||||
or from a shell:
|
||||
**Preferences** are how MeshTerm behaves. Examples are the pause between transmissions, how
|
||||
many times MeshTerm tries to send a message again, how far back it keeps history, and how
|
||||
the map frames itself. Each preference has a built-in default. Change a preference only
|
||||
where you do not agree with the default. You can change preferences on the **Preferences**
|
||||
page in the menu. The page stages your changes, and you save them with one action at the
|
||||
bottom. You can also change them from a shell:
|
||||
|
||||
```console
|
||||
$ meshterm preferences show # every preference, its value, its default
|
||||
@@ -16,16 +19,21 @@ $ meshterm preferences set history_days 90
|
||||
$ meshterm preferences reset --yes # back to the built-in defaults
|
||||
```
|
||||
|
||||
They are kept in `~/.meshterm/preferences.toml`, which lists only what you have changed;
|
||||
delete a line and the default takes over again.
|
||||
MeshTerm keeps the preferences in `~/.meshterm/preferences.toml`. This file lists only the
|
||||
preferences that you changed. If you delete a line, the default applies again.
|
||||
|
||||
**Config** is where things live and which device to talk to. MeshTerm runs with none of it
|
||||
— it discovers attached serial devices and nearby Bluetooth companions, lets you pick one,
|
||||
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](../devices/uconsole.md).
|
||||
Write a config file only to give your hardware stable aliases.
|
||||
A preference is not a setting. A **setting** is a value of the radio. The device keeps it,
|
||||
and you edit it on Device config.
|
||||
|
||||
**Config** is the setup of the machine: where files are, and which device to talk to.
|
||||
MeshTerm runs without it. It discovers the serial devices that are attached and the
|
||||
Bluetooth companions that are near. It lets you select one, and it remembers the last good
|
||||
default. A network (TCP) companion cannot be discovered. To reach one, use `--tcp
|
||||
host:port`, a TCP profile, or the "add a network device" prompt of the picker. The picker
|
||||
lists a LoRa radio on the SPI bus of this machine when its device file exists. You can also
|
||||
reach it with `--spi`. For more information, refer to [the uConsole manual](../devices/uconsole.md).
|
||||
Write a config file only to give your hardware stable aliases. You edit this file with a
|
||||
text editor.
|
||||
|
||||
Copy [`config.example.toml`](../../config.example.toml) to `~/.meshterm/config.toml`:
|
||||
|
||||
@@ -58,79 +66,86 @@ transport = "spi"
|
||||
# en_pins = [27] # the AIO v2 powers its radio from pin 27
|
||||
```
|
||||
|
||||
Both live in `~/.meshterm`, along with everything else MeshTerm remembers: the SQLite
|
||||
history, the outbox, the contact and channel caches, stored admin passwords, and the log.
|
||||
**`$MESHTERM_HOME` moves the whole directory**, which is the way to run a second radio —
|
||||
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/README.md#where-meshterm-keeps-its-state)
|
||||
lists every file.
|
||||
Both files are in `~/.meshterm`. MeshTerm also keeps everything else that it remembers
|
||||
there: the SQLite history, the outbox, the caches of contacts and channels, the stored
|
||||
admin passwords, and the log.
|
||||
|
||||
Timestamps are stored as UTC and rendered in your local time. Heard packets older than the
|
||||
`history_days` preference are deleted when monitoring starts: once in each menu session,
|
||||
and at the start of each `meshterm monitor` run. Nothing else is pruned.
|
||||
**`$MESHTERM_HOME` moves the whole directory.** Use it to run a second radio, or a `--mock`
|
||||
session, without a change to the files that you use each day. `--db` moves only the
|
||||
database. The other files stay where they were. A `--mock` run that has neither option
|
||||
writes to `meshterm-mock.db` beside the real history. Thus the invented nodes of the
|
||||
simulator never enter the real history.
|
||||
[`docs/cli.md`](../cli/README.md#where-meshterm-keeps-its-state) lists each file.
|
||||
|
||||
MeshTerm stores timestamps as UTC and shows them in your local time. MeshTerm deletes
|
||||
heard packets that are older than the `history_days` preference when monitoring starts.
|
||||
This happens one time in each menu session, and at the start of each `meshterm monitor`
|
||||
run. MeshTerm deletes no other data in this way.
|
||||
|
||||
## Adding a radio on the SPI bus
|
||||
|
||||
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](../devices/uconsole.md#step-2--install-the-radio-library)
|
||||
shows how to install that.
|
||||
Some boards put the LoRa chip directly on the SPI bus of the computer, with no firmware in
|
||||
front of it. Examples are the AIO board of the uConsole, and LoRa HATs for the Raspberry
|
||||
Pi. MeshTerm drives these radios itself. It runs the node while it is connected, and it
|
||||
releases the radio when it quits. MeshTerm needs Linux and the radio library.
|
||||
[The uConsole manual](../devices/uconsole.md#step-2-install-the-radio-library) shows how
|
||||
to install the library.
|
||||
|
||||
**Two boards need no configuration.** An AIO v1's wiring is MeshTerm's default, so
|
||||
whenever `/dev/spidev1.0` exists the device screen lists it: as **uConsole AIO** on a
|
||||
uConsole, and as **SPI radio** on any other machine. M5Stack's
|
||||
Cap LoRa-1262 on a Cardputer Zero ships with MeshTerm too, and is listed as **Cap
|
||||
LoRa-1262** on a Cardputer Zero (see [the Cardputer Zero page](../devices/cardputer-zero.md#the-cap-lora-1262)).
|
||||
Either way, `meshterm --spi` reaches it. Any other board is added with a profile that
|
||||
states how it is wired, and a profile on the same `/dev/spidev*` replaces the shipped
|
||||
wiring.
|
||||
**Two boards need no configuration.** The wiring of an AIO v1 is the MeshTerm default.
|
||||
Thus, when `/dev/spidev1.0` exists, the device screen lists it. The name is **uConsole AIO**
|
||||
on a uConsole, and **SPI radio** on any other machine. MeshTerm also includes the Cap
|
||||
LoRa-1262 of M5Stack on a Cardputer Zero. The device screen lists it as **Cap LoRa-1262**
|
||||
on a Cardputer Zero (refer to [the Cardputer Zero page](../devices/cardputer-zero.md#the-cap-lora-1262)).
|
||||
For either board, `meshterm --spi` reaches the radio. To add any other board, write a
|
||||
profile that states how the board is wired. A profile on the same `/dev/spidev*` replaces
|
||||
the wiring that MeshTerm includes.
|
||||
|
||||
**Why a profile, and not a prompt on the device screen.** A USB companion describes itself:
|
||||
plug it in and there is nothing to ask. An SPI radio says nothing about how it is wired, and
|
||||
the wiring is a dozen facts — the bus, the chip select, the reset, busy, and interrupt pins,
|
||||
the antenna switch, the TCXO — any one of which, wrong, leaves a radio that comes up deaf
|
||||
rather than failing. Those are facts to look up once and write down, not to type into a
|
||||
dialog. And the radio doesn't come and go the way a cable does: it is soldered, stacked on
|
||||
a header, or built into the case, so its description belongs somewhere as lasting as the
|
||||
hardware is. A file you write once, and can read back when something is off, is that place.
|
||||
**Why a profile, and not a prompt on the device screen.** A USB companion describes itself.
|
||||
When you connect it, MeshTerm has nothing to ask. An SPI radio does not tell how it is
|
||||
wired. The wiring is a dozen facts: the bus, the chip select, the reset, busy, and
|
||||
interrupt pins, the antenna switch, and the TCXO. If any one of these is wrong, the radio
|
||||
starts but cannot hear. It does not fail with an error. You look up these facts one time
|
||||
and write them down. You do not type them into a dialog. Also, an SPI radio does not come
|
||||
and go as a cable does. It is soldered, stacked on a header, or built into the case. Thus
|
||||
its description must last as long as the hardware lasts. A file that you write one time,
|
||||
and read again when something is wrong, is the correct place.
|
||||
|
||||
### 1. Find your board's wiring
|
||||
|
||||
The easiest place to find it is the board's `meshtasticd` configuration, which most vendors
|
||||
publish (usually a `config.yaml` or a `config.d/` fragment with a `Lora:` section). Its keys
|
||||
map one to one:
|
||||
The easiest place to find the wiring is the `meshtasticd` configuration of the board. Most
|
||||
vendors publish it. It is usually a `config.yaml` or a fragment in `config.d/` that has a
|
||||
`Lora:` section. The keys map one to one:
|
||||
|
||||
| `meshtasticd` (`Lora:`) | MeshTerm (`[profiles.<name>.spi]`) |
|
||||
| --- | --- |
|
||||
| `spidev: spidev0.0` | `bus_id = 0` and `cs_id = 0` — the two numbers of `spidevB.C` |
|
||||
| `spidev: spidev0.0` | `bus_id = 0` and `cs_id = 0`, the two numbers of `spidevB.C` |
|
||||
| `CS: 21` | `cs_pin = 21` (only when the board drives chip select from a GPIO) |
|
||||
| `Reset`, `Busy`, `IRQ` | `reset_pin`, `busy_pin`, `irq_pin` |
|
||||
| `TXen`, `RXen` | `txen_pin`, `rxen_pin` |
|
||||
| `DIO2_AS_RF_SWITCH: true` | `use_dio2_rf = true` |
|
||||
| `DIO3_TCXO_VOLTAGE: true` | `use_dio3_tcxo = true` (1.8 V) |
|
||||
| `gpiochip: 4` | usually nothing — see below; `gpio_chip = 4` to pin it |
|
||||
| `gpiochip: 4` | usually nothing (refer to the text below), or `gpio_chip = 4` to set it |
|
||||
|
||||
`Module` must be `sx1262`; that is the chip MeshTerm's node drives. **Leave `gpio_chip` out**
|
||||
unless you need to override it: a `meshtasticd` file pins a number because Meshtastic can't
|
||||
look the chip up, but the number that carries a Raspberry Pi's 40-pin header differs
|
||||
between a Pi 4/CM4 and a Pi 5/CM5 and has moved between kernel releases on the latter.
|
||||
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
|
||||
`Module` must be `sx1262`, because the node of MeshTerm drives that chip. **Leave out
|
||||
`gpio_chip`** unless you must override it. A `meshtasticd` file sets a number because
|
||||
Meshtastic cannot look up the chip. But the number of the chip that has the 40-pin header
|
||||
of a Raspberry Pi is not the same on a Pi 4/CM4 and a Pi 5/CM5. On the Pi 5/CM5 it also
|
||||
changed between kernel releases. When you leave it out, it defaults to `-1`. Then MeshTerm
|
||||
finds the chip of the header by its label (`pinctrl-rp1`, `pinctrl-bcm2711`, or
|
||||
`pinctrl-bcm2835`). If a board has no chip with one of these labels, MeshTerm uses chip
|
||||
`0`. `gpiodetect` lists each chip with its label. The
|
||||
[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
|
||||
26, DIO2 and DIO3 on — which is wrong for almost any other board, and a radio wired wrong
|
||||
comes up deaf rather than failing.
|
||||
**State each pin and both switches.** In a `meshtasticd` file, a key that is left out means
|
||||
"none" or "off". In the `spi` table, a key that is left out means the value of the AIO.
|
||||
For example, IRQ is on pin 26, and DIO2 and DIO3 are on. These values are wrong for almost
|
||||
any other board. A radio with the wrong wiring starts but cannot hear. It does not fail
|
||||
with an error.
|
||||
|
||||
### 2. Write the profile
|
||||
|
||||
In `~/.meshterm/config.toml`, give the radio a name and its wiring. The values below are an
|
||||
illustration, not any particular board's — take yours from step 1:
|
||||
example. They are not the values of a particular board. Take your values from step 1:
|
||||
|
||||
```toml
|
||||
[profiles.hat]
|
||||
@@ -148,11 +163,11 @@ use_dio2_rf = false # …so DIO2 doesn't
|
||||
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](../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.
|
||||
A `spi` table is enough to make the profile an SPI profile. `transport = "spi"` says so
|
||||
directly. An AIO profile that has no table uses it. [The wiring table in the uConsole
|
||||
manual](../devices/uconsole.md#board-wiring) has each key with its AIO default. If a key
|
||||
is not known to MeshTerm, or a value has the wrong type, MeshTerm stops at startup and
|
||||
names the profile. MeshTerm never ignores a misspelt pin without telling you.
|
||||
|
||||
### 3. Check it
|
||||
|
||||
@@ -160,22 +175,30 @@ is never quietly ignored.
|
||||
meshterm -p hat info
|
||||
```
|
||||
|
||||
The first run starts the node, which mints the radio's identity. If something is wrong, the
|
||||
error says what to fix — a missing `/dev/spidev*` (enable SPI and reboot), a permission
|
||||
problem (join the `spi` and `gpio` groups), a missing radio library, or another program
|
||||
already holding the radio. A node that starts but never answers leaves its log in
|
||||
`~/.meshterm/radio/spidev0.0/node.log`. Once it answers, the device screen lists the radio
|
||||
under the profile's name.
|
||||
The first run starts the node, which creates the identity of the radio. If something is
|
||||
wrong, the error message tells you what to correct. These are the possible problems:
|
||||
|
||||
### What the profile doesn't hold
|
||||
- A `/dev/spidev*` file is missing. Enable SPI and reboot.
|
||||
- A permission problem. Join the `spi` and `gpio` groups.
|
||||
- The radio library is missing.
|
||||
- Another program already holds the radio.
|
||||
|
||||
- **Radio settings.** Frequency, bandwidth, spreading factor, coding rate, and TX power are
|
||||
the node's own settings, saved by the node and changed on **Device config** like any
|
||||
companion's. A new node starts on MeshCore's **US/Canada preset** (910.525 MHz, 62.5 kHz,
|
||||
SF7, CR 4/5, 22 dBm): anywhere else, change it on Device config before you send anything.
|
||||
- **The preamble.** The node follows MeshCore's own rule — 32 symbols up to SF8, 16 above —
|
||||
and a receiver that expects anything else misses most of the mesh. `preamble_length` was
|
||||
once a wiring key and is refused now; delete the line if an old profile has it.
|
||||
- **Identity.** The node's key, contacts, and channels live in
|
||||
`~/.meshterm/radio/<spidev>/`, filed by the device node rather than the profile, so
|
||||
renaming a profile, or reaching the same radio from the device screen, keeps the same node.
|
||||
If the node starts but never answers, it writes its log to
|
||||
`~/.meshterm/radio/spidev0.0/node.log`. When the node answers, the device screen lists the
|
||||
radio under the name of the profile.
|
||||
|
||||
### What the profile does not hold
|
||||
|
||||
- **Radio settings.** The frequency, bandwidth, spreading factor, coding rate, and TX power
|
||||
are settings of the node. The node saves them, and you change them on **Device config**,
|
||||
as for any companion. A new node starts on the **US/Canada preset** of MeshCore
|
||||
(910.525 MHz, 62.5 kHz, SF7, CR 4/5, 22 dBm). In any other region, change the preset on
|
||||
Device config before you send anything.
|
||||
- **The preamble.** The node follows the rule of MeshCore: 32 symbols up to SF8, and 16
|
||||
symbols above SF8. A receiver that expects a different length misses most of the mesh.
|
||||
`preamble_length` was once a wiring key. MeshTerm now refuses it. If an old profile has
|
||||
this key, delete the line.
|
||||
- **Identity.** The key, contacts, and channels of the node are in
|
||||
`~/.meshterm/radio/<spidev>/`. MeshTerm files them under the device file, not the
|
||||
profile. Thus, if you rename a profile, or you reach the same radio from the device
|
||||
screen, you keep the same node.
|
||||
+84
-79
@@ -1,30 +1,30 @@
|
||||
# When a companion won't connect
|
||||
# When a companion does not connect
|
||||
|
||||
MeshTerm talks to a MeshCore companion over USB, Bluetooth, or TCP, or runs the node itself
|
||||
on a radio on the SPI bus. When a connection fails, MeshTerm says which step failed and what
|
||||
to do about it. This page collects those messages, so you can look one up before you meet
|
||||
it, or search for the one you have.
|
||||
MeshTerm talks to a MeshCore companion over USB, Bluetooth, or TCP. It can also run the
|
||||
node itself on a radio on the SPI bus. When a connection fails, MeshTerm tells you which
|
||||
step failed and what to do. This page lists these messages. Use it to look up a message
|
||||
before you see it, or to search for the message that you have.
|
||||
|
||||
You see the same message wherever the connection fails:
|
||||
|
||||
- **In the device picker**, when you choose a device.
|
||||
- **In the device picker**, when you select a device.
|
||||
- **When MeshTerm starts**, if you named the device on the command line (`--port`, `--ble`,
|
||||
`--tcp`, or `--profile`). MeshTerm shows the message, then opens the menu without a
|
||||
`--tcp`, or `--profile`). MeshTerm shows the message. Then it opens the menu without a
|
||||
radio. A tool that needs the radio tries to connect again when you open it.
|
||||
- **While MeshTerm waits for a device to come back**, after it was unplugged or rebooted.
|
||||
If the device comes back but still won't connect, the message appears under the
|
||||
spinner, so you are not left waiting for something that will not happen. Fix the
|
||||
problem, and MeshTerm connects without you doing anything else.
|
||||
- **While MeshTerm waits for a device to come back**, after you unplugged the device or it
|
||||
rebooted. If the device comes back but still does not connect, the message shows under
|
||||
the spinner. Thus you do not wait for something that will not happen. Correct the
|
||||
problem. Then MeshTerm connects, and you do nothing more.
|
||||
- **On the command line**, as the error message.
|
||||
|
||||
Every failure is also written to the log file, `~/.meshterm/meshterm.log` (see
|
||||
[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.
|
||||
MeshTerm also writes each failure to the log file, `~/.meshterm/meshterm.log` (refer to
|
||||
[Where MeshTerm keeps its state](../cli/README.md#where-meshterm-keeps-its-state)). Attach
|
||||
the log file to a bug report. To get more detail, set the **Log detail** preference to
|
||||
`DEBUG` (`meshterm preferences set log_level DEBUG`), and run the command again.
|
||||
|
||||
Sometimes MeshTerm meets a failure it has no name for. Then the message is the error in
|
||||
its own words, and a second line says where the log file is. The log holds the full
|
||||
error, which is what a bug report needs.
|
||||
Sometimes MeshTerm meets a failure that it has no name for. Then the message is the error
|
||||
in its own words, and a second line tells where the log file is. The log has the full
|
||||
error, which a bug report needs.
|
||||
|
||||
- [Bluetooth](#bluetooth)
|
||||
- [Who does the pairing](#who-does-the-pairing)
|
||||
@@ -39,77 +39,81 @@ error, which is what a bug report needs.
|
||||
|
||||
## Bluetooth
|
||||
|
||||
**A companion takes one Bluetooth connection at a time.** If your phone's MeshCore app is
|
||||
connected to it, MeshTerm cannot connect until the phone lets go, and the companion stops
|
||||
advertising while it is connected, so MeshTerm cannot even see it. Disconnect the phone
|
||||
first, or turn its Bluetooth off.
|
||||
**A companion takes one Bluetooth connection at a time.** If the MeshCore app on your phone
|
||||
is connected to the companion, MeshTerm cannot connect until the phone disconnects. While
|
||||
the companion is connected, it stops its BLE advertisement, so MeshTerm cannot see it.
|
||||
Disconnect the phone first, or turn its Bluetooth off.
|
||||
|
||||
### Who does the pairing
|
||||
|
||||
A PIN-protected companion must be *paired* before it will talk: the computer and the
|
||||
companion exchange keys once, using the six-digit PIN, and remember them. Who runs that
|
||||
exchange depends on the system.
|
||||
A companion that has a PIN must be *paired* before it will talk. The computer and the
|
||||
companion exchange keys one time, with the six-digit PIN, and they remember the keys. The
|
||||
system decides who does this exchange.
|
||||
|
||||
| System | Who pairs | What you do |
|
||||
| --- | --- | --- |
|
||||
| Windows | MeshTerm | Give the PIN when MeshTerm asks for it (the picker's PIN dialog, or `--ble-pin` on the command line). You do not need to pair in Settings first. |
|
||||
| Linux | MeshTerm | The same. MeshTerm answers the pairing itself, so it works over SSH and on machines with no desktop, and you do not need `bluetoothctl` or the desktop's Bluetooth settings. |
|
||||
| macOS | macOS | macOS shows its own dialog asking for the code; type the PIN there. `--ble-pin` has no effect on macOS. |
|
||||
| Windows | MeshTerm | Give the PIN when MeshTerm asks for it (in the PIN dialog of the picker, or with `--ble-pin` on the command line). You do not pair in Settings first. |
|
||||
| Linux | MeshTerm | Do the same as on Windows. MeshTerm answers the pairing itself. Thus it works over SSH and on machines that have no desktop. You do not need `bluetoothctl` or the Bluetooth settings of the desktop. |
|
||||
| macOS | macOS | macOS shows its own dialog that asks for the code. Type the PIN there. `--ble-pin` has no effect on macOS. |
|
||||
|
||||
Once paired, the system remembers it, and later connections need no PIN.
|
||||
After the pairing, the system remembers it. Later connections do not need the PIN.
|
||||
|
||||
**On macOS, the terminal needs Bluetooth permission.** macOS grants Bluetooth to the app
|
||||
MeshTerm runs in (Terminal, iTerm2, Ghostty), not to MeshTerm itself. If no pairing dialog
|
||||
appears, or no Bluetooth devices are listed, check System Settings > Privacy & Security >
|
||||
Bluetooth. **Over SSH, macOS denies Bluetooth outright** and never asks, so run MeshTerm in
|
||||
a terminal on the Mac itself.
|
||||
**On macOS, the terminal needs permission for Bluetooth.** macOS gives Bluetooth permission
|
||||
to the app that MeshTerm runs in (Terminal, iTerm2, or Ghostty). It does not give it to
|
||||
MeshTerm. If no pairing dialog appears, or no Bluetooth devices are listed, check System
|
||||
Settings > Privacy & Security > Bluetooth. **Over SSH, macOS denies Bluetooth completely**
|
||||
and never asks. Thus you must run MeshTerm in a terminal on the Mac itself.
|
||||
|
||||
### Finding the PIN
|
||||
|
||||
The PIN is shown on the companion's screen, or in the MeshCore app's settings for it. A
|
||||
companion with no screen (a T1000-E, for instance) can tell you over USB instead:
|
||||
The PIN is on the screen of the companion, or in the settings for it in the MeshCore app.
|
||||
If the companion has no screen (a T1000-E, for example), you can get the PIN over USB:
|
||||
|
||||
```console
|
||||
$ meshterm --port /dev/ttyACM0 config get device_pin
|
||||
123456
|
||||
```
|
||||
|
||||
`meshterm config set device_pin <PIN>` changes it, and so does the **Device PIN** row on
|
||||
the menu's Device config page. A changed PIN makes every existing pairing with that
|
||||
companion out of date. See [a stale pairing](#what-each-bluetooth-message-means) below.
|
||||
To change the PIN, run `meshterm config set device_pin <PIN>`. You can also use the
|
||||
**Device PIN** row on the Device config page of the menu. When you change the PIN, each
|
||||
existing pairing with that companion is out of date. Refer to
|
||||
[a stale pairing](#what-each-bluetooth-message-means) below.
|
||||
|
||||
### What each Bluetooth message means
|
||||
|
||||
In the device picker, a pairing problem opens the PIN dialog with a one-line reason under
|
||||
the question. On the command line, it is the error message. Both come from the same
|
||||
diagnosis.
|
||||
In the device picker, a pairing problem opens the PIN dialog. The dialog shows a one-line
|
||||
reason under the question. On the command line, the reason is the error message. Both come
|
||||
from the same diagnosis.
|
||||
|
||||
| The message starts with… | What happened | What to do |
|
||||
| --- | --- | --- |
|
||||
| **couldn't open a Bluetooth link** | The companion was never reached. | Move closer, check it is powered on, and make sure no phone or other computer is connected to it. |
|
||||
| **requires a Bluetooth pairing PIN** | The companion is PIN-protected and not paired with this computer. | Give the PIN: the picker asks for it; on the command line, add `--ble-pin <PIN>`. |
|
||||
| **Windows has … paired** / **Linux has … paired, but the device refused that pairing** | Your computer remembers a pairing that the companion no longer accepts. It was reflashed, reset, or given a new PIN, or it was paired *without* a PIN back when it did not have one. | Give the PIN, and MeshTerm removes the old pairing and pairs again. Or [forget the pairing](#forgetting-a-pairing) yourself and reconnect. |
|
||||
| **rejected the Bluetooth PIN** | The PIN was wrong. | Check the code on the device or in the MeshCore app, and try again. |
|
||||
| **refused to pair** | The companion turned the pairing down. It is usually connected to a phone, or still holds an old pairing for this computer. | Disconnect it from anything else, or restart it, and try again. |
|
||||
| **Windows couldn't pair** / **Linux couldn't pair** | The pairing failed for another reason, named in brackets. | Restart the companion, [forget the pairing](#forgetting-a-pairing), and try again. |
|
||||
| **Windows couldn't reach** / **Linux couldn't reach … to pair it** | The system could not find the companion when it came to pair. | As for a link that never opened: range, power, or another connection. |
|
||||
| **paired with …, but the device still refuses the connection** | Pairing succeeded, yet the companion still refuses. It is holding an old pairing for this computer. | Restart the companion, [forget the pairing](#forgetting-a-pairing), and try again. |
|
||||
| **was not paired. macOS asks for the pairing code in its own dialog** | macOS had not finished pairing. | Type the PIN into the macOS dialog when it appears. If none appears, check the terminal's Bluetooth permission (above). |
|
||||
| **connecting … took longer than** / **connecting … timed out** | The link, the pairing, or the service discovery stalled. | Move closer, restart the companion, and try again. |
|
||||
| **connected …, but it didn't answer the identity query** | The link came up and the companion went quiet. | It may be busy with another app. Restart it and try again. |
|
||||
| **connected …, but it never answered as a MeshCore companion** | Something answered, but not as a companion. | It may be running repeater or room server firmware, which has no companion interface. |
|
||||
| **"couldn't open a Bluetooth link"** | MeshTerm did not reach the companion. | Move closer. Make sure that the companion is on. Make sure that no phone or other computer is connected to it. |
|
||||
| **"requires a Bluetooth pairing PIN"** | The companion has a PIN, and it is not paired with this computer. | Give the PIN. The picker asks for it. On the command line, add `--ble-pin <PIN>`. |
|
||||
| **"Windows has … paired"** / **"Linux has … paired, but the device refused that pairing"** | Your computer remembers a pairing that the companion does not accept now. The companion was reflashed, reset, or given a new PIN. Or you paired it *without* a PIN when it had no PIN. | Give the PIN. MeshTerm removes the old pairing and pairs again. Or [forget the pairing](#forgetting-a-pairing) yourself, and connect again. |
|
||||
| **"rejected the Bluetooth PIN"** | The PIN was wrong. | Check the code on the device or in the MeshCore app. Then try again. |
|
||||
| **"refused to pair"** | The companion did not accept the pairing. Usually it is connected to a phone, or it still has an old pairing for this computer. | Disconnect the companion from anything else, or restart it. Then try again. |
|
||||
| **"Windows couldn't pair"** / **"Linux couldn't pair"** | The pairing failed for another reason. The message names the reason in brackets. | Restart the companion. [Forget the pairing](#forgetting-a-pairing). Then try again. |
|
||||
| **"Windows couldn't reach"** / **"Linux couldn't reach … to pair it"** | The system could not find the companion when it tried to pair. | Do the same as for a link that did not open: check the range, the power, and other connections. |
|
||||
| **"paired with …, but the device still refuses the connection"** | The pairing worked, but the companion still refuses the connection. It has an old pairing for this computer. | Restart the companion. [Forget the pairing](#forgetting-a-pairing). Then try again. |
|
||||
| **"was not paired. macOS asks for the pairing code in its own dialog"** | macOS did not finish the pairing. | Type the PIN in the macOS dialog when it appears. If no dialog appears, check the Bluetooth permission of the terminal (refer to the text above). |
|
||||
| **"connecting … took longer than"** / **"connecting … timed out"** | The link, the pairing, or the service discovery stopped. | Move closer. Restart the companion. Then try again. |
|
||||
| **"connected …, but it didn't answer the identity query"** | The link came up, and then the companion did not answer. | Make sure that no other app uses the companion, because it can be busy. Restart it and try again. |
|
||||
| **"connected …, but it never answered as a MeshCore companion"** | Something answered, but it did not answer as a companion. | Find out if the device has the firmware of a repeater or a room server. These have no companion interface. |
|
||||
|
||||
### Forgetting a pairing
|
||||
|
||||
MeshTerm fixes a stale pairing itself when you give it the PIN. To remove one by hand:
|
||||
When you give MeshTerm the PIN, it repairs a stale pairing itself. To remove a pairing by
|
||||
hand, do the step for your system:
|
||||
|
||||
- **Windows:** Settings > Bluetooth & devices, open the companion, and choose *Remove device*.
|
||||
- **Linux:** `bluetoothctl remove AA:BB:CC:DD:EE:FF`, with your companion's address.
|
||||
- **macOS:** System Settings > Bluetooth, the companion's ⓘ button, then *Forget This Device*.
|
||||
- **Windows:** Go to Settings > Bluetooth & devices. Open the companion, and select *Remove
|
||||
device*.
|
||||
- **Linux:** Run `bluetoothctl remove AA:BB:CC:DD:EE:FF`. Use the address of your companion.
|
||||
- **macOS:** Go to System Settings > Bluetooth. Select the ⓘ button of the companion, then
|
||||
*Forget This Device*.
|
||||
|
||||
On Windows and Linux, MeshTerm's quit dialog also offers **Unpair & quit** while a paired
|
||||
Bluetooth companion is connected. It forgets the pairing on the way out, so the next
|
||||
connection asks for the PIN again.
|
||||
On Windows and Linux, the quit dialog of MeshTerm also has **Unpair & quit** while a paired
|
||||
Bluetooth companion is connected. This choice forgets the pairing as MeshTerm leaves. Thus
|
||||
the next connection asks for the PIN again.
|
||||
|
||||
---
|
||||
|
||||
@@ -117,35 +121,36 @@ connection asks for the PIN again.
|
||||
|
||||
| The message starts with… | What happened | What to do |
|
||||
| --- | --- | --- |
|
||||
| **no permission to open** | Your user may not open the port. On Linux, serial ports belong to a group, `dialout` on Ubuntu and Debian. | `sudo usermod -aG dialout $USER`, then log out and back in. The new group applies only to a new login. |
|
||||
| **… is in use by another program** | Something else has the port open: the MeshCore app, a firmware flasher, or a serial monitor. On Windows this is reported as *Access is denied*. | Close the other program. On Linux, ModemManager probes new USB serial devices and can hold one: `sudo systemctl stop ModemManager`, and try again. |
|
||||
| **… isn't there** | The port does not exist. The device was unplugged, or came back under another name (`/dev/ttyACM0` becoming `/dev/ttyACM1`, `COM5` becoming `COM6`). | `meshterm devices` lists what is attached now. |
|
||||
| **no response from a MeshCore companion on …** | The port opened, but nothing answered as a companion. | It may not be a MeshCore companion (repeater firmware, or another device), or it is powered off or busy. On Linux, ModemManager may be probing it: `sudo systemctl stop ModemManager`, and try again. |
|
||||
| **"no permission to open"** | Your user may not open the port. On Linux, a group owns the serial ports. It is `dialout` on Ubuntu and Debian. | Run `sudo usermod -aG dialout $USER`. Then log out and log in again. The new group applies only to a new login. |
|
||||
| **"… is in use by another program"** | Another program has the port open: the MeshCore app, a firmware flasher, or a serial monitor. On Windows, the system reports this as *Access is denied*. | Close the other program. On Linux, ModemManager probes new USB serial devices, and it can hold one. Run `sudo systemctl stop ModemManager`, and try again. |
|
||||
| **"… isn't there"** | The port does not exist. The device was unplugged, or it came back under another name (`/dev/ttyACM0` became `/dev/ttyACM1`, or `COM5` became `COM6`). | Run `meshterm devices` to list what is attached now. |
|
||||
| **"no response from a MeshCore companion on …"** | The port opened, but nothing answered as a companion. | Make sure that the device is a MeshCore companion. It can have the firmware of a repeater, or it can be another device. It can also be off or busy. On Linux, ModemManager can be probing it. Run `sudo systemctl stop ModemManager`, and try again. |
|
||||
|
||||
To stop ModemManager for good rather than for one boot, `sudo systemctl disable --now
|
||||
ModemManager`, if nothing else on the machine needs it. Nothing does unless you have a
|
||||
cellular modem.
|
||||
To stop ModemManager for good, and not only for one boot, run `sudo systemctl disable --now
|
||||
ModemManager`. Do this only if nothing else on the machine needs ModemManager. Nothing
|
||||
needs it unless you have a cellular modem.
|
||||
|
||||
---
|
||||
|
||||
## TCP
|
||||
|
||||
Like Bluetooth, a network companion serves one client at a time. MeshTerm's default port
|
||||
is `5000`.
|
||||
A network companion serves one client at a time, as a Bluetooth companion does. The default
|
||||
port of MeshTerm is `5000`.
|
||||
|
||||
| The message starts with… | What happened | What to do |
|
||||
| --- | --- | --- |
|
||||
| **couldn't find a host named** | The name didn't lead to any address. | Check the spelling, or use the companion's IP address instead. |
|
||||
| **nothing is accepting connections on port** | The computer was reached, but nothing is listening on that port. | Check the port number, and that the companion, or the bridge in front of it, is running. |
|
||||
| **there's no route to** | Your computer can't reach that address at all. | Check the address, and that your computer is on the companion's network (or its VPN). |
|
||||
| **no answer from … on port** | Nothing answered in time. | Check that the companion is powered on and on your network. A firewall can also hold a connection without refusing it. |
|
||||
| **… closed the connection as it opened** | The companion hung up straight away. | Another client is probably connected to it. Disconnect that one first. |
|
||||
| **the connection to … opened, but nothing answered as a MeshCore companion** | Something is listening on the port, but it isn't a companion. | Check the port number. If it is right, the companion may be busy with another client. |
|
||||
| **"couldn't find a host named"** | The name did not lead to an address. | Check the spelling, or use the IP address of the companion. |
|
||||
| **"nothing is accepting connections on port"** | MeshTerm reached the computer, but nothing listens on that port. | Check the port number. Make sure that the companion, or the bridge in front of it, is running. |
|
||||
| **"there's no route to"** | Your computer cannot reach that address. | Check the address. Make sure that your computer is on the network of the companion (or on its VPN). |
|
||||
| **"no answer from … on port"** | Nothing answered in time. | Make sure that the companion is on and is on your network. A firewall can hold a connection without a refusal. |
|
||||
| **"… closed the connection as it opened"** | The companion hung up at once. | Disconnect the other client first. Probably another client is connected to the companion. |
|
||||
| **"the connection to … opened, but nothing answered as a MeshCore companion"** | Something listens on the port, but it is not a companion. | Check the port number. If the number is correct, the companion can be busy with another client. |
|
||||
|
||||
---
|
||||
|
||||
## A radio on the SPI bus
|
||||
|
||||
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](../devices/uconsole.md) and [adding a radio on the SPI bus](configuration.md#adding-a-radio-on-the-spi-bus).
|
||||
A radio that MeshTerm drives itself (`--spi`) is not a companion. It has no pairing and no
|
||||
port. Its failures are about the node that MeshTerm starts. Its own guides cover these
|
||||
failures: [the uConsole](../devices/uconsole.md) and
|
||||
[adding a radio on the SPI bus](configuration.md#adding-a-radio-on-the-spi-bus).
|
||||
+58
-55
@@ -1,94 +1,97 @@
|
||||
# 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/README.md) is the manual for the scripted half of it.
|
||||
This page lists each screen that the menu has, and the command that does the same job
|
||||
without the menu. It is the catalogue. [The command line](../cli/README.md) is the manual
|
||||
for the scripted half.
|
||||
|
||||
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
|
||||
last three name **whose** a setting is: This node, the radio in your hand;
|
||||
Other nodes, someone else's over the mesh; and This app, the program in front of you.
|
||||
The main menu asks one question: "What would you like to do?" It has six sections. The first
|
||||
three sections name an action: Message, Watch, and Explore. The last three sections name
|
||||
whose setting it is. "This node" is the radio in your hand. "Other nodes" are radios of
|
||||
other persons, which you reach over the mesh. "This app" is the program in front of you.
|
||||
|
||||
Every interactive screen is listed here with its scripted equivalent, where one exists.
|
||||
This page lists each interactive screen with its scripted equivalent, where one exists.
|
||||
|
||||
## 💬 Message — the people on the other end
|
||||
## 💬 Message: the people on the other end
|
||||
|
||||
| Feature | What it does | Scripted |
|
||||
| --- | --- | --- |
|
||||
| **💬 Chat** | Live full-screen channel and direct messaging — a scrolling transcript with a pinned input line where sent and received messages stream together. The rooms you've joined are here too, each post shown under whoever wrote it. Every message is logged; unread counts show in the menu header. | `meshterm chat send / history / list / listen` |
|
||||
| **📻 Channels** | Create, join, reorder, mute, and share mesh channels — with QR codes and `meshcore://` share links. Give a channel a **send scope** and its messages are flooded into one region, relayed only by the repeaters that carry it. | `meshterm channels list / add / join / import / share / clear / scope` |
|
||||
| **📌 Rooms** | Every room server your radio knows. A room server is a message board on a radio that keeps the latest posts and sends you the ones you missed. Join one with its password; when a room doesn't answer, MeshTerm explains what it knows and offers the next thing to try, such as resending by flood when the route your radio learned has gone stale. | `meshterm rooms list / join / forget` |
|
||||
| **📨 Courier** | Store-and-forward outbox for contacts that aren't reachable yet. Queue a message; it goes out (with ack tracking and polite exponential backoff) the moment the contact is next heard, or at a scheduled time. | `meshterm courier queue / list / send / cancel / clear` |
|
||||
| **👥 Contacts** | This node and its known contacts — a recency heat-map, overheard packet counts, and full public keys with the path-hash prefix highlighted. The scripted listing is the contacts alone (this node is `meshterm info`'s answer, in far more detail). | `meshterm contacts` |
|
||||
| **💬 Chat** | Live full-screen messages on channels and to contacts. The transcript scrolls, the input line stays at the bottom, and sent and received messages appear together. The rooms that you joined are also here. Each post shows under the name of its author. MeshTerm stores each message. Unread counts show in the menu header. | `meshterm chat send / history / list / listen` |
|
||||
| **📻 Channels** | Create, join, reorder, mute, and share mesh channels. You can share a channel with a QR code or a `meshcore://` link. If you give a channel a **send scope**, its messages are flooded into one region. Only the repeaters that carry that region relay them. | `meshterm channels list / add / join / import / share / clear / scope` |
|
||||
| **📌 Rooms** | Each room server that your radio knows. A room server is a message board on a radio. It keeps the latest posts and sends you the posts that you missed. Join a room with its password. If a room does not answer, MeshTerm tells you what it knows and offers the next step to try. For example, it can send again by flood when the route that your radio learned is no longer good. | `meshterm rooms list / join / forget` |
|
||||
| **📨 Courier** | A store-and-forward outbox for contacts that you cannot reach yet. Queue a message. MeshTerm sends it when the contact is next heard, or at a time that you set. It tracks the acks, and it waits longer between each try (exponential backoff). | `meshterm courier queue / list / send / cancel / clear` |
|
||||
| **👥 Contacts** | This node and its known contacts. The screen shows a heat map of how recently each contact was heard, the counts of overheard packets, and the full public keys. The hash of each key is highlighted. The scripted listing shows only the contacts. For this node, run `meshterm info`, which gives much more detail. | `meshterm contacts` |
|
||||
|
||||
## 📊 Watch — what the mesh is doing, and what it did
|
||||
## 📊 Watch: what the mesh is doing, and what it did
|
||||
|
||||
| Feature | What it does | Scripted |
|
||||
| --- | --- | --- |
|
||||
| **📊 Dashboard** | The live mesh overview: a two-hour all-packet activity chart with a pulse line, session traffic tallies by packet class, and window RF health beside the radio's own live numbers. Repaints every second. | — |
|
||||
| **📰 Live feed** | Every packet as it arrives, newest first: time, what the frame *is* (its parsed payload class), and what it is *about* — the node for an advert, the channel for a channel text, sender → recipient for a direct message or a request, the tag for a trace — with reception quality beside it, and the region a scoped flood was sent into. Enter opens any row in the packet viewer, route graph and all. | — |
|
||||
| **🚨 Watchtower** | A passive sentinel over the nodes you star: silence alarms when a watched node goes quiet, SNR-sag warnings when reception degrades, recovery notes when it returns, and a heads-up when a node is heard for the first time. Runs in the background all session; unacked alerts show as a header badge. | — |
|
||||
| **⏳ Time machine** | Everything the recorder ever heard, as braille charts: reception volume, the median-SNR band, hour-of-day rhythm, packets and nodes per day, and first-ever arrivals — over a switchable 24h / 7d / 30d / all-time window (on the PicoCalc the windows stop at 30d), per node or mesh-wide. | — |
|
||||
| **🎧 Monitor** | Passive recording is always on in the app. On the CLI, capture a bounded foreground window, tailing each overheard packet and summarising when it ends. It transmits nothing — it only listens. | `meshterm monitor --seconds 60` |
|
||||
| **📊 Dashboard** | The live overview of the mesh. It has a two-hour chart of the activity of all packets with a pulse line, the traffic counts of the session for each packet class, and window RF health next to the live numbers of the radio. The screen updates each second. | none |
|
||||
| **📰 Live feed** | Each packet as it arrives, newest first. A row shows the time, the type of the packet (its parsed payload class), and what the packet is about. For an advert, that is the node. For a channel text, it is the channel. For a direct message or a request, it is sender → recipient. For a trace, it is the tag. The row also shows the reception quality and, for a scoped flood, the region of the flood. Press Enter to open a row in the packet viewer, with its route graph. | none |
|
||||
| **🚨 Watchtower** | A passive watcher of the nodes that you star. It gives a silence alarm when a watched node goes quiet. It warns you when the SNR of a node gets worse, and it tells you when the node returns. It also tells you when a node is heard for the first time. It runs in the background during the whole session. Alerts that you did not acknowledge show as a badge in the header. | none |
|
||||
| **⏳ Time machine** | Everything that the recorder ever heard, as braille charts: reception volume, the median-SNR band, the rhythm of the hours of the day, packets and nodes for each day, and first-ever arrivals. You can switch the time window between 24h, 7d, 30d, and all time. On the PicoCalc, the windows stop at 30d. You can look at one node or at the whole mesh. | none |
|
||||
| **🎧 Monitor** | The app always records passively. On the command line, this command captures a window of a fixed length in the foreground. It shows each overheard packet as it arrives, and it gives a summary at the end. It transmits nothing. It only listens. | `meshterm monitor --seconds 60` |
|
||||
|
||||
## 🧭 Explore — where the nodes are and how they reach each other
|
||||
## 🧭 Explore: where the nodes are and how they reach each other
|
||||
|
||||
| Feature | What it does | Scripted |
|
||||
| --- | --- | --- |
|
||||
| **🌍 Map** | Located nodes plotted over a real OpenStreetMap street basemap rendered as Unicode braille (streets, rivers, place names). Pannable and zoomable; repeaters highlighted and drawn on top. Falls back to a blank grid offline. | — (a map is a picture; `meshterm contacts` lists the same nodes) |
|
||||
| **🌐 Mesh walk** | The mesh's *observed shape*, walked one node at a time: an evidence graph built from trace walks, firmware routes, overheard relay chains, and repeater neighbour tables. SNR-coloured braille edges, quality bars, Enter to walk, ⌫ to backtrack, type to find any node. The map answers *where*; the walk answers *how it hangs together*. | — |
|
||||
| **🎯 Trace target** | A live trace screen: pick a target and watch each trace stream in hop by hop, with running per-hop medians and reliability. Compose or force a route through specific repeaters. A trace transmits **exactly once** (repeaters can blacklist nodes that burst) — sample more by running it again. | `meshterm trace --target …` |
|
||||
| **👣 Trace path** | The other half of tracing: compose the whole circuit by hand — out and back whichever way you choose — and walk it. The path composer suggests each next hop from the links actually observed, strongest first, and can fetch a repeater's neighbour table over the mesh when you hold its admin password. | `meshterm trace-path --path …` |
|
||||
| **🏆 Trophy case** | Every trace that comes home is scored, on seven boards: longest distance, farthest node, longest single leg, most nodes (with and without revisits), weakest surviving link, and biggest enclosed loop. Records are kept per hash width, and a record walk must be a *trail* — no link crossed twice the same way. | `meshterm records` |
|
||||
| **🌍 Map** | The nodes that have a position, plotted on a real OpenStreetMap street map. The map is drawn as Unicode braille (streets, rivers, and place names). You can pan and zoom. Repeaters are highlighted and drawn on top. When you are offline, the map shows a blank grid. | none (a map is a picture, but `meshterm contacts` lists the same nodes) |
|
||||
| **🌐 Mesh walk** | The observed shape of the mesh, which you walk one node at a time. It is a graph of evidence from trace walks, firmware routes, overheard relay chains, and the neighbour tables of repeaters. It has braille edges that are coloured by SNR, and quality bars. Press Enter to walk to a node. Press ⌫ to go back. Type to find any node. The map shows where the nodes are. The walk shows how the nodes connect. | none |
|
||||
| **🎯 Trace target** | A live trace screen. Select a target, and each trace streams in, hop by hop. The screen shows the running median and the reliability of each hop. You can compose a route through specific repeaters, or force one. A trace transmits **exactly once**, because repeaters can blacklist nodes that send bursts. To get more samples, run the trace again. | `meshterm trace --target …` |
|
||||
| **👣 Trace path** | The other half of tracing. You compose the whole circuit by hand, out and back, by the way that you choose. Then you walk it. The path composer suggests each next hop from the links that MeshTerm observed, strongest first. If you have the admin password of a repeater, the composer can also get the neighbour table of that repeater over the mesh. | `meshterm trace-path --path …` |
|
||||
| **🏆 Trophy case** | MeshTerm scores each trace that comes home on seven boards: longest distance, farthest node, longest single leg, most nodes (with and without revisits), weakest surviving link, and biggest enclosed loop. It keeps the records for each hash width. A record walk must be a trail, which means that no link is crossed twice in the same direction. | `meshterm records` |
|
||||
|
||||
## 🔧 This node — the radio in your hand
|
||||
## 🔧 This node: the radio in your hand
|
||||
|
||||
| Feature | What it does | Scripted |
|
||||
| --- | --- | --- |
|
||||
| **📋 Device info** | The connected companion's identity and full radio configuration at a glance. | `meshterm info` |
|
||||
| **🔧 Device config** | Every setting the companion firmware exposes — name, radio, client repeat, auto-add, telemetry, custom variables — *staged* for review and applied in place, plus the operations on the box itself: clock sync, TOML backup/restore, the identity key, reboot, and factory reset, each acting the moment it's confirmed, with destructive ops gated. Laid out like Repeater admin, so the radio in your hand and one over the mesh are configured the same way. | `meshterm config` / `config set <key> <value>` / `config backup` / `config reboot` / … |
|
||||
| **📡 Send advert** | Announce this node to the mesh — a zero-hop or flood advertisement, or share this node's contact card as a QR code. | `meshterm config advert` / `config share` |
|
||||
| **🔌 Devices** | Not a menu screen. The companion is picked on the startup splash, which enumerates serial *and* Bluetooth LE companions (or auto-selects the only one present), adds a network (TCP) companion by host:port, and remembers the last good default. A dropped link is detected live and offers to reconnect. `meshterm devices` lists what discovery finds. | `meshterm devices` |
|
||||
| **📋 Device info** | The identity of the connected companion and its full radio configuration, on one screen. | `meshterm info` |
|
||||
| **🔧 Device config** | Each setting that the companion firmware has: name, radio, client repeat, auto-add, telemetry, and custom variables. MeshTerm stages your changes for review and then applies them in place. The screen also has operations on the device itself: clock sync, TOML backup and restore, the identity key, reboot, and factory reset. Each operation acts when you confirm it, and the destructive operations need a confirm step. The screen has the same layout as Repeater admin. Thus you configure the radio in your hand and a radio over the mesh in the same way. | `meshterm config` / `config set <key> <value>` / `config backup` / `config reboot` / … |
|
||||
| **📡 Send advert** | Announce this node to the mesh with a zero-hop advert or a flood advert. You can also share the contact card of this node as a QR code. | `meshterm config advert` / `config share` |
|
||||
| **🔌 Devices** | This is not a menu screen. You select the companion on the startup splash. The splash lists the serial companions and the Bluetooth LE companions. If only one companion is present, MeshTerm selects it. You can add a network (TCP) companion with host:port. MeshTerm remembers the last good default. If the link drops, MeshTerm detects it at once and offers to reconnect. `meshterm devices` lists what discovery finds. | `meshterm devices` |
|
||||
|
||||
## 🗼 Other nodes — someone else's radio, over the mesh
|
||||
## 🗼 Other nodes: the radio of another person, over the mesh
|
||||
|
||||
| Feature | What it does | Scripted |
|
||||
| --- | --- | --- |
|
||||
| **🗼 Repeater admin** | Set up remote repeaters and room servers over the mesh: log in (remembered or prompted password), then a config-style editor speaking the node's text CLI — including repeater-only knobs (TX delay, airtime factor, advert intervals) — plus one-shot actions and a readline remote command line. A **Regions** page edits the repeater's region tree: which regions it relays floods for, whether it relays unscoped floods, its home and default scope. | `meshterm repeater-admin <node> <command…>` |
|
||||
| **🔖 Regions** | Not a menu screen. A repeater's node page lists the regions it carries and asks it once, without logging in (it answers only a neighbour, or over a known route); the packet viewer, message paths and live feed name the region a flood was scoped to. | `meshterm regions <node>` |
|
||||
| **📶 TX optimize** | Sweep a remote node's transmit power live — coarse, then refine, then verify — watch each level land, and decide whether to apply the winner. The scripted form applies the winner unless told not to. | `meshterm tx-optimize --path … [--no-apply]` |
|
||||
| **🗼 Repeater admin** | Set up remote repeaters and room servers over the mesh. First, log in with a remembered password or a password that MeshTerm asks for. Then use an editor in the style of Device config, which speaks the text CLI of the node. It includes the settings that only repeaters have (TX delay, airtime factor, and advert intervals). It also has one-shot actions and a remote command line with readline. A **Regions** page edits the region tree of the repeater: the regions for which it relays floods, and whether it relays unscoped floods. It also edits the home scope and the default scope. | `meshterm repeater-admin <node> <command…>` |
|
||||
| **🔖 Regions** | This is not a menu screen. The node page of a repeater lists the regions that the repeater carries. MeshTerm asks the repeater one time, without a login. The repeater answers only a neighbour, or a request over a known route. The packet viewer, the message paths, and the live feed show the region of a scoped flood. | `meshterm regions <node>` |
|
||||
| **📶 TX optimize** | Sweep the transmit power of a remote node while you watch. The sweep has three steps: coarse, then refine, then verify. You see each level as it lands, and you decide whether to apply the best level. The scripted form applies the best level unless you tell it not to. | `meshterm tx-optimize --path … [--no-apply]` |
|
||||
|
||||
## ⚙ This app — the program in front of you
|
||||
## ⚙ This app: the program in front of you
|
||||
|
||||
The last of the three, and the one nothing on the mesh can answer. Preferences leads it because
|
||||
it is the only row here that *changes* MeshTerm rather than describing it; the written
|
||||
pages close it.
|
||||
This is the last of the three sections. Nothing on the mesh can answer for it. Preferences
|
||||
is the first row because it is the only row here that changes MeshTerm. The other rows
|
||||
only describe it. The written pages are at the end.
|
||||
|
||||
| Feature | What it does | Scripted |
|
||||
| --- | --- | --- |
|
||||
| **⚙ Preferences** | How MeshTerm behaves, in eight groups: Device, Sending, TX optimize, Watchtower, Map, History, Display, and Diagnostics. Grouped and staged, saved by one action at the bottom, and written to `preferences.toml` as only the values you changed. [Configuring MeshTerm](configuration.md) has the detail. | `meshterm preferences show / set / reset` |
|
||||
| **🩺 Diagnostics** | Everything a bug report opens with, in one block: which build this is and how it was installed, the OS, the terminal and its size, the boot-time verdicts that show on no screen (icons, powerline, platform flavour, and why), the radio and its link, and how much history is stored. Nothing private — no keys, passwords, positions, or contact names; the mesh is described as per-table counts and the span they cover. `s` saves it beside the log for attaching. | `meshterm diagnostics [--out PATH]` |
|
||||
| **📖 About MeshTerm** | What MeshTerm is, where it came from, and the terms it ships under — including the map and font credits and the licence URL. | `meshterm about` |
|
||||
| **👤 About the author** | The person behind MeshTerm, on the mesh and off it. | `meshterm about-author` |
|
||||
| **🔗 Join Discord** | The community server: one invite link, and a QR of it for a phone to read. | `meshterm discord` |
|
||||
| **💰 Support MeshTerm** | What keeps MeshTerm going, and the ways — paid and unpaid — to help it along. | `meshterm support` |
|
||||
| **⚙ Preferences** | How MeshTerm behaves, in eight groups: Device, Sending, TX optimize, Watchtower, Map, History, Display, and Diagnostics. The page stages your changes, and you save them with one action at the bottom. MeshTerm writes only the values that you changed to `preferences.toml`. [Configuring MeshTerm](configuration.md) has the detail. | `meshterm preferences show / set / reset` |
|
||||
| **🩺 Diagnostics** | One block of information that each bug report starts with. It shows which build you have and how you installed it, the operating system, the terminal and its size, and the radio and its link. It also shows the verdicts from boot time that no other screen shows (icons, powerline, platform flavour, and the reasons), and how much history MeshTerm stored. It has nothing private: no keys, passwords, positions, or contact names. It describes the mesh only as counts for each table and the span of time that the tables cover. Press `s` to save the block beside the log, to attach to a report. | `meshterm diagnostics [--out PATH]` |
|
||||
| **📖 About MeshTerm** | What MeshTerm is, where it came from, and the terms of its licence. It includes the map credit, the font credit, and the licence URL. | `meshterm about` |
|
||||
| **👤 About the author** | The person behind MeshTerm, on the mesh and away from it. | `meshterm about-author` |
|
||||
| **🔗 Join Discord** | The community server. The page has one invite link and a QR code of the link, which a phone can read. | `meshterm discord` |
|
||||
| **💰 Support MeshTerm** | What keeps MeshTerm going, and the ways to help it, with money and without money. | `meshterm support` |
|
||||
|
||||
## Leaving MeshTerm
|
||||
|
||||
**Quit** at the bottom of the main menu leaves, after asking. **Ctrl+Q** asks the same
|
||||
question from any screen, and pressing it again while the question is up leaves at once.
|
||||
**Quit** at the bottom of the main menu leaves MeshTerm, after it asks you to confirm.
|
||||
**Ctrl+Q** asks the same question from any screen. If you press it again while the question
|
||||
is open, MeshTerm leaves at once.
|
||||
|
||||
You can also hold **Esc**. After a second, a box says *Hold Esc to quit*, with a bar that
|
||||
fills over the next two seconds. When it's full, MeshTerm closes, the same way Quit closes
|
||||
it. Let go before then and you're back where you were. While the box is up, MeshTerm
|
||||
doesn't start sending anything new over the radio, so a message that's going out finishes
|
||||
first. A quick press of **Esc** still goes back a screen, as always.
|
||||
You can also hold **Esc**. After one second, a box shows "Hold Esc to quit". A bar in the
|
||||
box fills during the next two seconds. When the bar is full, MeshTerm closes, in the same
|
||||
way as with Quit. If you let go before the bar is full, you return to where you were.
|
||||
While the box is open, MeshTerm does not start to send anything new over the radio. A
|
||||
message that is already going out finishes first. A quick press of **Esc** still goes back
|
||||
one screen, as always.
|
||||
|
||||
Holding Esc only works where MeshTerm can tell that a key is being held down, and a
|
||||
terminal never says when a key comes back up. These can tell:
|
||||
Holding Esc works only where MeshTerm can tell that a key is down. A terminal does not
|
||||
tell a program when a key comes up. MeshTerm can tell on these systems:
|
||||
|
||||
- Windows.
|
||||
- Linux, where you can read your keyboard's input device. That includes the PicoCalc, and
|
||||
a uConsole or any other Linux computer where your user is in the `input` group.
|
||||
- Linux, where MeshTerm can read the input device of your keyboard. This includes the
|
||||
PicoCalc, and a uConsole or other Linux computer where your user is in the `input` group.
|
||||
- The Cardputer Zero, and the [emulator](../devices/cardputer-zero.md#trying-it-in-the-emulator)
|
||||
window for either handheld.
|
||||
|
||||
|
||||
+34
-29
@@ -1,37 +1,42 @@
|
||||
# Terminals, icons, and the Windows console
|
||||
|
||||
Why MeshTerm looks right in some terminals and draws empty boxes in others, what it does
|
||||
about it on Windows, and how to turn all of that off.
|
||||
This page explains why MeshTerm looks correct in some terminals and draws empty boxes in
|
||||
others. It also tells what MeshTerm does about this on Windows, and how to turn that off.
|
||||
|
||||
MeshTerm is drawn with emoji icons, braille charts, and powerline path chips. Whether you
|
||||
see them is up to your *terminal*, not really your font: a modern terminal, asked for a
|
||||
character its font doesn't have, quietly borrows it from another font on the machine.
|
||||
That's why the app looks right in Windows Terminal, in VS Code's terminal, and on macOS
|
||||
and Linux — usually with a font that contains almost none of it. The exception is macOS's
|
||||
Terminal app, where the charts may draw misaligned: no Mac monospace font has braille, so
|
||||
it is borrowed from a proportional one at the wrong width.
|
||||
MeshTerm draws emoji icons, braille charts, and powerline path chips. Your terminal decides
|
||||
if you see them. The font is less important. When a modern terminal must draw a character
|
||||
that its font does not have, it takes the character from another font on the machine. Thus
|
||||
the app looks correct in Windows Terminal, in the terminal of VS Code, and on macOS and
|
||||
Linux. This is usually true also when the font has almost none of these characters.
|
||||
|
||||
The classic Windows console — the black `cmd.exe` window a double-click opens on
|
||||
Windows 10 — doesn't borrow. (Windows 11, 22H2 and later, opens Windows Terminal by
|
||||
default instead.) It draws what its one font holds and empty boxes for everything else, and
|
||||
no font fixes the icons there: the only two fonts on a Windows machine with emoji in them
|
||||
are proportional, and a console won't take a proportional font.
|
||||
The Terminal app of macOS is the exception. The charts can be misaligned there. No Mac
|
||||
monospace font has braille, so the terminal takes it from a proportional font, at the wrong
|
||||
width.
|
||||
|
||||
So when MeshTerm lands in that console it just **moves to Windows Terminal** — it says
|
||||
so, and opens there. Nothing is installed and nothing is changed; it's one window instead
|
||||
of another, and it's the whole app exactly as the screenshots show it. Windows Terminal is
|
||||
already on every Windows 11 machine and is a free install on Windows 10.
|
||||
The classic Windows console does not take characters from other fonts. This is the black
|
||||
`cmd.exe` window that a double-click opens on Windows 10. (Windows 11, version 22H2 and
|
||||
later, opens Windows Terminal by default.) The console draws what its one font has, and
|
||||
empty boxes for all other characters. No font can correct the icons there. Only two fonts
|
||||
on a Windows machine have emoji, and both are proportional. A console does not accept a
|
||||
proportional font.
|
||||
|
||||
Where there's no Windows Terminal to move to, and the console's font can't already draw
|
||||
the charts, MeshTerm offers the next best thing instead — the charts and marks, without
|
||||
the icons. It ships
|
||||
[Cascadia Mono PL](https://github.com/microsoft/cascadia-code), Microsoft's own console
|
||||
font and one of the very few monospace faces that carries braille at all, and will install
|
||||
it just for you: no administrator rights, nothing downloaded. If you already have a font
|
||||
that can draw the charts (Cascadia, or another such as Iosevka), it offers to switch to
|
||||
that one instead. Either way it asks first (`[y/N]`), and the answer defaults to no.
|
||||
When MeshTerm starts in this console, it **moves to Windows Terminal**. It tells you, and
|
||||
it opens the app there. MeshTerm does not install or change anything. You get one window
|
||||
instead of another, and the whole app looks exactly as the screenshots show. Windows
|
||||
Terminal is already on each Windows 11 machine. On Windows 10 it is a free install.
|
||||
|
||||
Preferences → Display → Console setup turns all of this off if you'd rather stay put.
|
||||
Sometimes there is no Windows Terminal to move to, and the font of the console cannot
|
||||
draw the charts. Then MeshTerm offers the next best choice: the charts and marks, without
|
||||
the icons. MeshTerm includes
|
||||
[Cascadia Mono PL](https://github.com/microsoft/cascadia-code), the console font of
|
||||
Microsoft. It is one of the very few monospace fonts that have braille. MeshTerm can
|
||||
install it for your user account only. This needs no administrator rights and no download. If you
|
||||
already have a font that can draw the charts (Cascadia, or another such as Iosevka),
|
||||
MeshTerm offers to switch to that font instead. In each case, MeshTerm asks first
|
||||
(`[y/N]`), and the default answer is no.
|
||||
|
||||
Where MeshTerm keeps its state, and how to point a trial run somewhere else, is in
|
||||
[Configuring MeshTerm](configuration.md).
|
||||
To keep the console that you have, turn all of this off. Go to Preferences → Display →
|
||||
Console setup.
|
||||
|
||||
[Configuring MeshTerm](configuration.md) tells where MeshTerm keeps its state, and how to
|
||||
point a trial run to another place.
|
||||
Reference in new issue
Block a user