Draft: native radio driver for meshtasticd hardware (SX126x, SX127x, SX1280, LR11x0, CH341 USB) - #3
Merged
Merged
Conversation
radio.driver: spi drives an SX126x on a Linux SPI bus directly, with radio.device set to the meshtasticd board file (config.d/lora-*.yaml). Pure Go: spidev and GPIO v2 chardev ioctls, no CGO. 49 of meshtasticd's 61 board files parse; LR1121, RF95 and CH341 USB adapters are rejected. - internal/radio/sx126x: board parser (pins, gpiochip maps, Enable_Pins, SX126X_ANT_SW, TCXO, DIO2 RF switch, SX126X_MAX_POWER, TX_GAIN_LORA), Linux HAL, driver with datasheet errata, tests against a fake chip - kisstool --board: info (SPI readback, device errors, noise floor), listen and send-text - setup wizard keeps an spi radio's board file; editing a radio no longer forces kiss - docs/spi-radio-testing.md: step-by-step guide for testers with hardware Untested on hardware. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
…, board names and auto The native radio driver moves to internal/radio/spi and splits into a shared radio loop and chip drivers, so it now covers every chip and bus meshtasticd's board files use: - chips: SX1262/SX1268/LLCC68, SX1276/SX1278 (RF95), SX1280 (2.4 GHz) and LR1110/LR1120/LR1121, each from the datasheet and RadioLib's command sequences, with fake-chip tests for RX and TX - CH341 USB-to-SPI sticks (MeshStick, Meshtoad, RAK19714, uMesh, PiNedio, PiggyStick) over usbfs, in pure Go - boards: meshtasticd's 61 board files are built in; radio.device takes a file path, a board name, or auto (CH341 product string, Pi HAT+ EEPROM, RAK I2C EEPROM, as meshtasticd's autoconf) - board file keys: per-chip power limits and defaults, LR11x0 rfswitch_table, optional BUSY, repeated YAML keys resolved like yaml-cpp (first wins) - kisstool boards lists them; kisstool info prints each chip's diagnostics LR2021 is left out: no meshtasticd board uses it. Untested on hardware. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
A kiss radio's device can now be tcp://host:port. The driver dials it with a connect timeout and TCP keepalives instead of opening a serial port, and reconnects and re-applies the config exactly as for a serial modem. This is how RepeaterTastic uses meshtasticd's raw modem mode, which serves a Linux LoRa HAT or CH341 stick with the Mesh KISS v2 protocol (currently the raw-modem branch of A13xB0/firmware). - config: a malformed tcp:// device is rejected, and two radios can't share one (host names compared case-insensitively). - kisstool: --dev tcp://host:port works. - web: the setup "Test modem" probe accepts tcp:// devices; before a password is set it only dials loopback addresses. - docs: meshtasticd-raw-modem.md, linked from the README, configuration and hardware pages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
Edit radio and Add radio get a USB modem / meshtasticd switch: meshtasticd takes a host and port and saves tcp://host:port, with a hint about RawModemPort. The setup wizard lists meshtasticd's raw modem next to the serial ports. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
FESC FESC TFEND decoded to FEND; the second FESC is an invalid escaped byte and is dropped, so TFEND is then a literal 0xDC, matching the modem firmware. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
… uses Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
GET /boards (setup-level auth) lists what radio.device can be with driver spi: auto, meshtasticd's board files in /etc/meshtasticd/config.d and available.d, then the built-in copies, each with its module, bus and whether this driver supports it. POST /setup/probe takes driver: spi and opens the board, answering with the chip's diagnostic lines in a new details field; a board a running radio already drives is reported, not opened twice, and board files are limited to /etc/meshtasticd since the probe runs before a password is set. POST /setup takes driver (kiss or spi) and applies it with device; an older wizard that sends only a serial port still can't overwrite an spi board. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
ModemDeviceField switches between "USB modem" (the serial port as before) and "Board (SPI or USB stick)": a select of the boards from GET /boards, grouped by where they come from, with a board file path as the last choice. It sets the radio's driver (kiss or spi) with the device, so Edit radio and Add radio save both. The setup wizard gets a "LoRa board on SPI or a USB stick" card under the serial ports; Test modem probes the board and shows the chip's diagnostics, and finishing sends driver with device. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
One lazy radio serves both drivers, so picking a Board in first-time setup takes effect at once instead of showing the kiss driver until a restart. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
ModemDeviceField now offers all three ways to reach a radio: a USB KISS modem on a serial port, meshtasticd's raw modem over TCP (from kiss-tcp), and a LoRa board the spi driver runs itself. The setup wizard shows the meshtasticd card and the board card under the serial ports. Picking meshtasticd or a port sets driver kiss; picking a board sets spi. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
…erTastic into native-spi-radio-gui
…uto finds it Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
A reply left in the adapter by an interrupted session is longer than the next
short read expects, and usbfs answers EOVERFLOW ('value too large for defined
data type'). Read full 32-byte packets, and throw away leftovers when opening.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
…ial port by name experimental.meshtasticd_raw_modem gates radio.device tcp://…, the setup probe and the meshtasticd choice in the modem pickers, until raw modem mode lands upstream (meshtastic/firmware#11863). The setup wizard gets a card for typing a serial port that isn't listed, and Windows COM ports pass the serial-path check. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
…ial port by name experimental.meshtasticd_raw_modem gates radio.device tcp://…, the setup probe and the meshtasticd choice in the modem pickers, until raw modem mode lands upstream (meshtastic/firmware#11863). The setup wizard gets a card for typing a serial port that isn't listed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
…tible The same HAT has one meshtasticd file per host (Raspberry Pi, Luckfox Lyra Zero W, OK3506…) and they all carry the same name, so the dropdown groups by host and files found on this machine say where they came from. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
The raw modem mode isn't going upstream: meshtasticd stays a full node and RepeaterTastic will drive meshtasticd instances over the client API instead. Remove the tcp:// KISS transport, the experimental switch, the meshtasticd choice in the modem pickers and setup wizard, and the guide. The KISS decoder escape fix and its test stay. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
13 tasks
A13xB0
marked this pull request as ready for review
September 16, 2026 10:27
Collaborator
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Native radio driver (option D in the meshtasticd plan): RepeaterTastic drives the LoRa chip on meshtasticd hardware itself, with no USB KISS modem and no meshtasticd. It's a new
radio.Radioimplementation, so identities, relay, plugins and MQTT don't change.Not yet run on hardware. This PR stays a draft until testers have worked through docs/spi-radio-testing.md. Test binaries are on the
spi-draft-2prerelease.Coverage
Module: sx1280boardsAll 61 of meshtasticd's
bin/config.d/lora-*.yamlfiles are built in, copied unchanged under GPL-3.0 (seeinternal/radio/spi/boards/README.md), and all of them parse. LR2021 is left out because no meshtasticd board uses it.What's in it
radio.driver: spi.radio.deviceis one of:MeshAdv-900M30S);auto: detect a CH341 stick's product string, a Pi HAT+ EEPROM or a RAK I²C EEPROM, following meshtasticd's autoconf.internal/radio/spi: one radio loop (IRQ or polling, RX frames with RSSI/SNR, TX with TxDone, noise floor, channel busy) plus a driver per chip family:rfswitch_table, HP/LP/HF PA selection, bootloader detection.SPI_IOC_MESSAGEplus GPIO v2 chardev lines and edge events;{pin, gpiochip, line}, andRADIOLIB_NC;Enable_Pins,SX126X_ANT_SW,DIO2_AS_RF_SWITCH,DIO3_TCXO_VOLTAGE;*_MAX_POWERwith meshtasticd's defaults;TX_GAIN_LORA, with the same power-limit walk asRadioInterface::limitPower;USB_VID/USB_PID/USB_Serialnum;kisstool:boardslists the built-in boards;--boardworks withinfo(chip diagnostics, noise floor),listenandsend-text.spiradio's board; editing a radio in the GUI no longer forceskiss.GUI
USB modem | Board. The board list (GET /api/v1/boards) is grouped by host computer from each file'sMeta.compatible, with/etc/meshtasticdboards first and a custom path option.lora-usb-xiao-sx1262-ch341.yamlfor the XIAO nRF52840 + Wio-SX1262 CH341 bridge, soautofinds it.meshtasticd's raw modem mode (PR #4) was merged in here and then removed again: it won't go upstream, and RepeaterTastic will drive meshtasticd over its client API instead (epic PR to follow).
Known gaps