Skip to content

take the SX1262 model from its own repository - #14

Merged
A13xB0 merged 7 commits into
mainfrom
consume-virtual-sx1262
Sep 3, 2026
Merged

A13xB0 merged 7 commits into
mainfrom
consume-virtual-sx1262

Conversation

@A13xB0

@A13xB0 A13xB0 commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

The chip model was a file in variants/host/, beside the firmware build, and three other things wanted it: QEMU's sx1262 device, Renode's C# peripheral, and the simulator. They reached it by talking to radioserver over a socket, because that was the only way in.

It now lives in MeshBench/virtual-sx1262 with a C ABI and its own test suite, and arrives here as a submodule at vendor/virtual-sx1262.

Nothing about how this repository builds changes. The native node still links the class in process; radioserver still wraps it. What changes is that there is one copy and one history. Two copies of a chip model drift, and the day they do, a native node and an emulated one stop being comparable, which is the whole reason both backends exist.

It brings two fixes this copy did not have

Both found while chasing why emulated boards received every advert and relayed none:

  • The detection flags were raised at RxDone, asserting a carrier was present at the instant it stopped. MeshCore's CustomSX1262::isReceiving reads HEADER_VALID as "the channel is busy", so a repeater holding a packet to forward never saw a clear channel. They are now raised as the signal arrives and cleared when it goes.
  • A frame handed over while the chip was not listening waited in the inbox indefinitely, so a packet could arrive seconds after the air that carried it went quiet. Now bounded, with a grace long enough for the driver's own standby/configure/SetRx re-arm and no longer.

Both are covered by tests over there, which is the point of it having a bench of its own: these took a four minute emulated boot to see from here.

Checked

radioserver and simple_repeater both build for linux-amd64 against the submodule.

Note for reviewers: git submodule update --init after checkout, and the build says so plainly if the submodule is empty rather than failing further down.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Q9HbD44EKWWTRYgxbFGxf6

random-thought and others added 7 commits September 3, 2026 16:27
The chip model was a file in this variant, beside the firmware build, and three
other things wanted it: QEMU's sx1262 device, Renode's C# peripheral and the
simulator. They reached it by talking to radioserver over a socket, because that
was the only way in.

It now lives in MeshBench/virtual-sx1262 with a C ABI and its own tests, and
comes in here as a submodule. Nothing about how this repository builds changes:
the native node still links the class in process and radioserver still wraps it.
What changes is that there is one copy and one history. Two copies of a chip
model drift, and the day they do a native node and an emulated one stop being
comparable, which is the whole reason both backends exist.

The extracted model carries two fixes this copy did not have, both found while
chasing why emulated boards received every advert and relayed none:

  - The detection flags were raised at RxDone, asserting that a carrier was
    present at the instant it stopped. MeshCore's isReceiving reads HEADER_VALID
    as "the channel is busy", so a repeater holding a packet to forward never saw
    a clear channel. They are raised as the signal arrives now and cleared when
    it goes.
  - A frame handed over while the chip was not listening waited in the inbox
    indefinitely, so a packet could arrive seconds after the air that carried it
    went quiet. It is bounded now, with a grace long enough for the driver's own
    standby-configure-SetRx re-arm and no longer.

Both are covered by tests in that repository, which is the point of it having a
bench of its own: these took a four minute emulated boot to see from here.

Checked: radioserver and simple_repeater both build for linux-amd64 against the
submodule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9HbD44EKWWTRYgxbFGxf6
Picks up the fix to how long an undeliverable frame waits for the chip to start
listening. It was a fixed ten milliseconds, which is a simulator-time constant
imposed on hosts that advance the clock at their own rate, and it could drop a
frame the firmware would have received.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9HbD44EKWWTRYgxbFGxf6
Two changes upstream matter here. The model is split into the datasheet, the
radio and the wire, which is why build.sh globs its sources rather than naming
one: a library splitting a file should not break a build in a repository its
author is not in. And a frame is now handed over when the firmware arms its
receiver as well as on a tick, which is the fault that made an emulated board
relay less often than a native one - delivery on a tick alone made every
reception depend on a tick landing after the SetRx, and radioserver's ticks
arrive from the engine over a socket rather than from the same process.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9HbD44EKWWTRYgxbFGxf6
Two faults, one of which would have broken the next release.

build.yml's two compiling jobs checked out the repository without submodules.
With the model vendored that is a bare gitlink, so build.sh would stop on its
own guard and every platform's radioserver would fail to build - loudly, which
is the guard working, but at release time.

And nothing verified a pull request here: build.yml runs on a tag or a button
only. That is how a change to build.sh and a new submodule got as far as a
merge with no evidence they compiled. pr.yml builds radioserver on one platform,
which is the whole of what this repository adds around the vendored model and
needs no MeshCore checkout, so it costs about a minute. The submodule's presence
is asserted before the build rather than inside it, so an empty submodule reads
as an empty submodule instead of as a compile error in a file that is not there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9HbD44EKWWTRYgxbFGxf6
The glob added in the last commit picks up abi.cpp, which includes the C ABI
header from include/ rather than the class header from src/, and only src/ was
on the include path. Caught by the pull request check in that same commit, on
its first run, which is the argument for having it.

Both roots are on the path now. Carrying the ABI in these binaries costs a few
unreferenced symbols and means radioserver already exports the surface QEMU and
Renode link against.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9HbD44EKWWTRYgxbFGxf6
radioserver and a native role link the same vendored model through different
include paths and different source lists, and the glob two commits ago broke
exactly one of them. Checking one and not the other would have found that fault
by luck rather than by design.

The native job takes MeshCore at its default branch rather than a pinned commit.
This job asks whether our own wiring compiles; a second MeshCore pin to keep
honest buys nothing, and a break caused by upstream moving is worth seeing on a
pull request as well as at release time.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9HbD44EKWWTRYgxbFGxf6
The fault that made an emulated board hear every advert and relay one in three.
SetDioIrqParams carries an IRQ enable mask and then a DIO1 routing mask; the
model read the first and used it for the pin. RadioLib enables HeaderValid in
the status register but routes only RxDone to DIO1, so the pin went high twelve
symbols into the carrier and was still high when RxDone arrived - no rising
edge, and RadioLib attaches that pin on the rising edge, so recvRaw never ran.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9HbD44EKWWTRYgxbFGxf6
@A13xB0
A13xB0 merged commit 0f43543 into main Sep 3, 2026
2 checks passed
@A13xB0
A13xB0 deleted the consume-virtual-sx1262 branch September 3, 2026 16:12
A13xB0 added a commit that referenced this pull request Sep 3, 2026
This repository builds MeshCore for this host. radioserver was here only
because the chip model used to be here too: it wrapped VirtualSX1262 behind a
socket so QEMU and Renode could reach the same model a native node links. The
model moved to MeshBench/virtual-sx1262 in #14, and both emulators now load it
directly, so nothing calls this and nothing needs to.

What that arrangement cost, beyond the round trips: it framed SPI a byte at a
time, which is how the model came to answer GetRssiInst correctly for a native
node and with zero for an emulated one for months; it left DIO1 pollable only,
because the protocol was request-response; and it put three clocks in three
processes in front of anybody asking what happened when.

The submodule stays. A native node compiles the model in, which is what keeps
it the same chip as the one the emulators load, from the same commit.

Gone: bridge/radioserver.cpp, the radioserver branch of build.sh, its two
release jobs, its pull-request job, RADIOSERVER_PLATFORMS, the
radioserver_only input and radioserver.version. The published
radioserver-v1..v3 releases stay where they are; nothing new is cut.

A native role still builds: simple_repeater for linux-amd64, from this tree.


Claude-Session: https://claude.ai/code/session_01Q9HbD44EKWWTRYgxbFGxf6

Co-authored-by: R0ck <random@email.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants