Add native Windows build of meshtasticd#11031
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
💤 Files with no reviewable changes (1)
📝 WalkthroughWalkthroughAdds a reusable Windows binary workflow and native-windows PlatformIO environment, with MSYS2 toolchain setup, static linking, artifact validation, and Windows-specific adaptations for device I/O, sockets, MAC discovery, randomness, timekeeping, headers, and platform hooks. ChangesWindows native support
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant GitHubActions
participant MSYS2
participant PlatformIO
participant ArtifactStore
GitHubActions->>MSYS2: Install UCRT64 dependencies and argp
GitHubActions->>PlatformIO: Run native-windows build
PlatformIO->>GitHubActions: Produce meshtasticd.exe
GitHubActions->>ArtifactStore: Upload versioned Windows artifact
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/build_windows_bin.yml:
- Around line 24-27: Update the actions/checkout step to explicitly set
persist-credentials to false in its with configuration, while preserving
recursive submodule checkout.
In `@src/main.cpp`:
- Around line 431-440: Shorten the comment above the non-Windows timedatectl
check to one or two lines, retaining only the essential reason for excluding
Windows while removing implementation details and follow-up context. Leave the
guarded command and RTCQualityNTP assignment unchanged.
In `@src/modules/AdminModule.h`:
- Line 44: Update the session timeout logic using session_time so it no longer
compares a future deadline against millis() / 1000; use the existing Throttle
mechanism or a rollover-safe unsigned elapsed-time subtraction, preserving the
300-second timeout behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: ec8aa4b2-fa99-4857-b66c-4c2785f588bd
📒 Files selected for processing (13)
.github/workflows/build_windows_bin.ymlextra_scripts/windows_link_flags.pysrc/gps/RTC.cppsrc/main.cppsrc/mesh/HardwareRNG.cppsrc/modules/AdminModule.hsrc/mqtt/MQTT.cppsrc/platform/portduino/GpsdSerial.cppsrc/platform/portduino/PortduinoGlue.cppsrc/platform/portduino/WindowsMacAddr.cppsrc/power/PowerHAL.cppvariants/native/portduino.inivariants/native/portduino/platformio.ini
ecbeeed to
3dcf4dc
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
extra_scripts/windows_link_flags.py (1)
4-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCondense the static-linking rationale.
This 14-line comment block is more detailed than necessary and includes brittle dependency/runtime claims. Keep only the reason this script appends linker flags; move historical or diagnostic detail to documentation if needed.
Suggested simplification
-# -# Static-link settings for [env:native-windows]. -# -# PlatformIO routes build_flags to the compile step, not the link step (SCons' -# ParseFlags sends an unrecognized flag like -static to CCFLAGS, where the linker -# never sees it), so it is appended to LINKFLAGS here. Same reason -# extra_scripts/wasm_link_flags.py exists for [env:native-wasm]. -# -# Static keeps meshtasticd.exe a single self-contained file needing no MSYS2 -# runtime, and removes a real failure mode: Windows resolves DLLs from System32 -# before PATH, so an unrelated libusb-1.0.dll installed there by another -# product's driver package hijacks a dynamically linked build and kills it at -# startup with STATUS_ENTRYPOINT_NOT_FOUND. MSYS2 ships static archives for every -# dependency this env uses, and openssl is not actually linked in. +# Static-link settings for [env:native-windows]; build_flags do not reliably +# place -static on the linker command, so append it explicitly here.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@extra_scripts/windows_link_flags.py` around lines 4 - 17, Condense the comment block above the Windows linker-flag configuration to only explain why this script appends static-link flags to LINKFLAGS instead of relying on build_flags, matching the purpose of extra_scripts/wasm_link_flags.py. Remove the detailed runtime, dependency, and historical failure-mode claims from this code comment.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@extra_scripts/windows_link_flags.py`:
- Around line 4-17: Condense the comment block above the Windows linker-flag
configuration to only explain why this script appends static-link flags to
LINKFLAGS instead of relying on build_flags, matching the purpose of
extra_scripts/wasm_link_flags.py. Remove the detailed runtime, dependency, and
historical failure-mode claims from this code comment.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 47577c82-eb0d-480a-b64c-c725824f5cb0
📒 Files selected for processing (13)
.github/workflows/build_windows_bin.ymlextra_scripts/windows_link_flags.pysrc/gps/RTC.cppsrc/main.cppsrc/mesh/HardwareRNG.cppsrc/modules/AdminModule.hsrc/mqtt/MQTT.cppsrc/platform/portduino/GpsdSerial.cppsrc/platform/portduino/PortduinoGlue.cppsrc/platform/portduino/WindowsMacAddr.cppsrc/power/PowerHAL.cppvariants/native/portduino.inivariants/native/portduino/platformio.ini
🚧 Files skipped from review as they are similar to previous changes (11)
- variants/native/portduino.ini
- src/gps/RTC.cpp
- variants/native/portduino/platformio.ini
- src/mesh/HardwareRNG.cpp
- src/mqtt/MQTT.cpp
- src/platform/portduino/WindowsMacAddr.cpp
- src/power/PowerHAL.cpp
- src/main.cpp
- src/platform/portduino/GpsdSerial.cpp
- src/platform/portduino/PortduinoGlue.cpp
- .github/workflows/build_windows_bin.yml
⚡ Try this PR in the Web FlasherWarning This is an automated, unreviewed CI test build. Back up your device configuration Supported boards built by this PR (29)
Build artifacts expire on 2026-08-16. Updated for |
3dcf4dc to
fb9796f
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
extra_scripts/windows_link_flags.py (1)
5-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winKeep the explanatory comment within the project’s comment-length convention.
Condense this three-line explanation to one or two lines; the implementation itself is already clear from the referenced environment and linker flags.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@extra_scripts/windows_link_flags.py` around lines 5 - 7, Condense the explanatory comment in extra_scripts/windows_link_flags.py to one or two lines while preserving that PlatformIO applies build_flags only during compilation, so the native-windows static link must be added to LINKFLAGS, as in the wasm counterpart.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@extra_scripts/windows_link_flags.py`:
- Around line 5-7: Condense the explanatory comment in
extra_scripts/windows_link_flags.py to one or two lines while preserving that
PlatformIO applies build_flags only during compilation, so the native-windows
static link must be added to LINKFLAGS, as in the wasm counterpart.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3284aff8-0c92-4901-bf26-ea124cd73848
📒 Files selected for processing (13)
.github/workflows/build_windows_bin.ymlextra_scripts/windows_link_flags.pysrc/gps/RTC.cppsrc/main.cppsrc/mesh/HardwareRNG.cppsrc/modules/AdminModule.hsrc/mqtt/MQTT.cppsrc/platform/portduino/GpsdSerial.cppsrc/platform/portduino/PortduinoGlue.cppsrc/platform/portduino/WindowsMacAddr.cppsrc/power/PowerHAL.cppvariants/native/portduino.inivariants/native/portduino/platformio.ini
🚧 Files skipped from review as they are similar to previous changes (8)
- src/mqtt/MQTT.cpp
- src/modules/AdminModule.h
- src/mesh/HardwareRNG.cpp
- src/gps/RTC.cpp
- .github/workflows/build_windows_bin.yml
- src/platform/portduino/GpsdSerial.cpp
- src/platform/portduino/WindowsMacAddr.cpp
- src/platform/portduino/PortduinoGlue.cpp
|
I commented on Discord, but seems a good idea to capture the thought here: It may be useful to target serialHal as the first real hardware target, as that should be an easier lift than ch341. |
fb9796f to
edcf3d1
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/platform/portduino/windows/libpinedio_ch341dll.c`:
- Around line 184-199: Protect shared pin-state read-modify-write operations
with usb_mutex: in pinedio_digital_write, acquire the mutex before updating
pin_state and keep it held through apply_pins, then unlock afterward; in
pinedio_set_pin_mode, wrap pin_dir_out mutations with the same mutex. Apply
these changes at both specified locations in
src/platform/portduino/windows/libpinedio_ch341dll.c (lines 184-199 and
170-182).
- Around line 357-376: Cache the thread handle while holding usb_mutex to avoid
joining a newly spawned poll thread. In pinedio_deattach_interrupt, assign
poll_thread to a local thread_to_join when stop is true, then use that local for
pthread_equal and pthread_join; apply the same caching pattern in pinedio_deinit
(src/platform/portduino/windows/libpinedio_ch341dll.c, lines 357-376 and
378-392).
- Around line 312-320: In the interrupt dispatch block, cache inst_int->callback
in a local callback variable while usb_mutex is still held, then unlock and
invoke the cached pointer instead of rereading inst_int->callback after
unlocking. Keep the existing mode check and lock reacquisition behavior
unchanged.
- Around line 336-353: Update pinedio_attach_interrupt so replacing an existing
callback does not decrement int_running_cnt or create another poll_thread. Only
increment int_running_cnt and start pin_poll_thread_fn when
inst->interrupts[int_pin].callback is currently NULL; preserve the existing
callback replacement fields and mutex handling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 3a136e26-a5c5-432d-9559-e4d9d062d82f
📒 Files selected for processing (16)
.github/workflows/build_windows_bin.ymlextra_scripts/windows_link_flags.pysrc/gps/RTC.cppsrc/main.cppsrc/mesh/HardwareRNG.cppsrc/modules/AdminModule.hsrc/mqtt/MQTT.cppsrc/platform/portduino/GpsdSerial.cppsrc/platform/portduino/PortduinoGlue.cppsrc/platform/portduino/USBHal.hsrc/platform/portduino/WindowsMacAddr.cppsrc/platform/portduino/windows/include/libpinedio-usb.hsrc/platform/portduino/windows/libpinedio_ch341dll.csrc/power/PowerHAL.cppvariants/native/portduino.inivariants/native/portduino/platformio.ini
🚧 Files skipped from review as they are similar to previous changes (9)
- variants/native/portduino.ini
- variants/native/portduino/platformio.ini
- src/platform/portduino/WindowsMacAddr.cpp
- src/mqtt/MQTT.cpp
- src/power/PowerHAL.cpp
- .github/workflows/build_windows_bin.yml
- src/mesh/HardwareRNG.cpp
- src/platform/portduino/GpsdSerial.cpp
- src/platform/portduino/PortduinoGlue.cpp
Sorry i didn't read that memo ;-) CH341 is working. |
edcf3d1 to
5bc073a
Compare
5bc073a to
1a3bf2f
Compare
Add [env:native-windows], a Portduino build for Windows x86_64 via the MSYS2
UCRT64 MinGW-w64 toolchain, alongside the existing Linux and macOS native
targets. Runs headless in SimRadio mode or against a CH341 USB LoRa adapter.
MSVC is not viable: platform-native's builder calls env.Tool("gcc") and the
firmware builds as gnu17/gnu++17. Wire the build into main_matrix.yml next to
the MacOS job, and allow workflow_dispatch.
Link statically. Windows resolves DLLs from System32 before PATH, so an unrelated
libusb-1.0.dll installed there by another product's driver package hijacks a
dynamically linked build and kills it at startup with STATUS_ENTRYPOINT_NOT_FOUND.
Static linking also leaves a single self-contained meshtasticd.exe needing no
MSYS2 runtime. -static cannot ride in build_flags, which PlatformIO routes only
to the compile step, so extra_scripts/windows_link_flags.py appends it to
LINKFLAGS, as wasm_link_flags.py does for native-wasm.
CH341 radio support: add a libpinedio backend over WCH's CH341DLL in
platform/portduino/windows/, mirroring what wasm/libpinedio_webusb.c does over
WebUSB. Ch341Hal is unchanged; only the backend behind the pinedio_* API differs,
so the pine64 libch341 lib_dep is ignored for this env. libusb is not usable
here: on Windows it can only reach a device bound to WinUSB, which means running
Zadig per machine and trusting a self-signed CA, whereas CH341DLL ships with
WCH's WHQL-signed CH341PAR driver. The DLL is closed-source and resolved at
runtime, so a host without the driver just gets a clear error. The 64-bit library
is named CH341DLLA64.DLL; plain CH341DLL.DLL is the 32-bit build. Interrupts are
polled at upstream's rate because CH341SetIntRoutine only fires on the CH341's
INT# pin, while the adapter wires the radio's DIO1 to D6.
HardwareRNG.cpp: use BCryptGenRandom() where Linux uses getrandom() and Darwin
uses arc4random_buf(). Not std::random_device, whose libstdc++ Windows backend
reports entropy() == 0 and so promises no cryptographic source, and this buffer
seeds key material.
PortduinoGlue.cpp: derive the MAC from the host's primary adapter via
GetAdaptersAddresses(), standing in for the BlueZ path on Linux and the en0 path
on macOS. Without it the MAC stays all-zero, device_id is left unset and every
user has to pass --hwid. The call lives in WindowsMacAddr.cpp because
<iphlpapi.h> pulls in RPC/OLE headers that collide with the Arduino API. Also
guard the ioctl() include, which only the Linux hardware path uses.
USBHal.h: include <sys/time.h> for gettimeofday(), which was previously reaching
it transitively through libusb.h.
GpsdSerial.cpp: port the gpsd TCP client to Winsock.
PowerHAL.cpp: define the defaults strongly on Windows. PE/COFF has no ELF-style
weak definitions; GNU as lowers __attribute__((weak)) to a COFF weak external
that the linker treats as undefined, so these would not link. Only nrf52 and
nrf54l15 override them.
RTC.cpp: copy timeval::tv_sec through a time_t before taking its address, as
time_t is 64-bit on Windows while long is 32-bit.
MQTT.cpp: take ntohl() from <winsock2.h>.
AdminModule.h: uint32_t for session_time, as uint is a glibc typedef.
main.cpp: only run the timedatectl shell-out on Linux. It is systemd-only, so
macOS, Windows and WASM all stayed at RTCQualityDevice anyway, and on those hosts
it printed a "not recognized" error on every boot.
Bump platform-native to 86c62ed for the Windows builder support, which pulls
framework-portduino 0fdf803.
Point libch341-spi-userspace at meshtastic/libch341-spi-userspace 03bf505, a
fork of pine64/libch341-spi-userspace at the previously pinned commit plus
fixes for four data races between the caller and the interrupt poll thread
(GPIO shadow state read-modify-written outside the mutex, callback pointer
re-read after unlocking, a second poll thread spawned on re-attach, and
pthread_join on a handle read after unlocking). Those affect the libusb
backend, so Linux and macOS, not Windows, which uses the CH341DLL backend
added here. Upstream is slow-moving: three PRs have been open since Dec 2024
and main has not moved since January. The Renovate opt-out from #9587 is
preserved.
Build with the MSYS2 UCRT64 toolchain; see the env comment for the argp
prerequisite, which is not packaged for MSYS2's mingw environments.
1a3bf2f to
d8734e4
Compare
Run PlatformIO under the runner's CPython instead of MSYS2's MinGW Python, which reported a mingw platform tag that matched no PyPI wheel, built grpcio from source, and failed the link with a SCons response-file error. MSYS2 now supplies only the UCRT64 toolchain, reached via PATH. Drop continue-on-error from the build, verify, and smoke steps so a failing pio turns the job red. Rewrite the self-contained check to fail on a missing binary or empty import table instead of printing OK.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/build_windows_bin.yml:
- Around line 118-119: Update the DLL allowlist filter in the dependency
extraction command to include ucrtbase alongside the existing system DLL
patterns, so UCRT64’s ucrtbase.dll dependency is excluded from static-link
regression checks.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: f38447fa-8d73-4d68-84c2-085a08f132db
📒 Files selected for processing (1)
.github/workflows/build_windows_bin.yml
UCRT cannot be static-linked, so meshtasticd.exe always imports the Universal C Runtime. Depending on the toolchain it appears as either the api-ms-win-crt-* API sets or a direct ucrtbase.dll import; allowlist the latter so the check does not falsely report a static-link regression.
The manual-dispatch inputs tripped checkov CKV_GHA_7, which requires workflow_dispatch inputs to be empty. The job is reached through main_matrix.yml, so match build_macos_bin.yml and keep workflow_call only.
Adds
[env:native-windows]: a Portduino build of meshtasticd for Windows x86_64via the MSYS2 UCRT64 MinGW-w64 toolchain, alongside the existing Linux and macOS
native targets. Runs headless in SimRadio mode or against a CH341 USB LoRa
adapter. Wired into
main_matrix.ymlnext to the MacOS job. MSVC is not viable:platform-native's builder calls
env.Tool("gcc")and the firmware builds asgnu17/gnu++17.Last part of a four-repo cascade; the others are merged and pinned here:
meshtastic/WiFi#7 ->
03e866d, meshtastic/framework-portduino#76 ->0fdf803,meshtastic/platform-native#18 ->
86c62ed(theportduino.inibump).CH341 radio
src/platform/portduino/windows/implements thepinedio_*API over WCH'sCH341DLL, mirroring
wasm/libpinedio_webusb.c.Ch341Halis unchanged; only thebackend behind the
pinedio_*seam differs, so the libch341 lib_dep islib_ignored for this env.libusb is not usable on Windows: it only reaches devices bound to WinUSB, which
means Zadig per machine plus a self-signed CA in the trust store. CH341DLL comes
with WCH's WHQL-signed CH341PAR driver. It is closed-source, so it is loaded at
runtime and a host without it gets a clear error.
CH341DLLA64.DLL;CH341DLL.DLLis the 32-bit build.CH341SetIntRoutineonly fires onthe CH341's INT# pin, but the adapter wires DIO1 to D6.
CH341SetStreamMSB-first) instead ofreverse-byte'ing each buffer. This is the one behavioural difference from the
libusb backend.
Module: auto) does not work:CH341GetDeviceNamereturns a devicepath, not a product string. Explicit config works.
libch341 fork
libch341-spi-userspacenow points at meshtastic/libch341-spi-userspace03bf505: pine64's previously pinned commit plus fixes for four data racesbetween the caller and the interrupt poll thread, which runs the RadioLib
interrupt callback while the main thread drives GPIO and SPI.
pthread_joinon a handle read after unlockingThese affect the libusb backend, so Linux and macOS - not Windows, which uses
the CH341DLL backend added here. The Windows backend was modelled on that file and
had inherited all four; they were caught in review here and fixed on both sides.
Forked rather than sent upstream because pine64's repo is slow-moving: three PRs
open since Dec 2024/Jan 2025, the last merge took four months, and
mainhas notmoved since 2026-01-24. The fork is upstream
mainplus one commit, so adoptingupstream work later is still easy. The Renovate opt-out from #9587 is preserved.
Compile-checked on Linux (
gcc -Wall -Wextra), but not exercised on Linux/macOSCH341 hardware - the equivalent fixes are what run on Windows against a real
MESHSTICK. The changes are lock placement only, no protocol change.
Notes for review
Static link.
extra_scripts/windows_link_flags.pyappends-statictoLINKFLAGS, since PlatformIO routes
build_flagsonly to the compile step (samereason
wasm_link_flags.pyexists). Windows resolves DLLs from System32 beforePATH, so a stray
libusb-1.0.dllthere hijacks a dynamic build and kills it withSTATUS_ENTRYPOINT_NOT_FOUND.HardwareRNG.cppusesBCryptGenRandom()rather than falling through tostd::random_device, whose libstdc++ Windows backend reportsentropy() == 0and promises no cryptographic source. This buffer seeds key material.
PortduinoGlue.cppderives the MAC viaGetAdaptersAddresses(), standing infor BlueZ (Linux) and en0 (macOS); otherwise the MAC is all-zero and every user
must pass
--hwid. It lives inWindowsMacAddr.cppbecause<iphlpapi.h>pullsin RPC/OLE headers that collide with the Arduino API, which is why this env builds
with
-DNOUSER -DWIN32_LEAN_AND_MEAN -DNOGDI.PowerHAL.cppdefines its defaults strongly on Windows. PE/COFF has noELF-style weak definitions, so the weak defaults would not link. Only nrf52 and
nrf54l15 override them.
main.cppnow guardstimedatectlwith__linux__rather than!_WIN32.It is systemd-only, so macOS and WASM were also running it, failing, and printing
an error every boot; all three already fell back to
RTCQualityDevice.Two include blocks rely on blank-line separation (
bcrypt.hneedsLONGfromwindows.h;winsock2.hmust precedewindows.h). Sorting them breaks the buildon Windows only.
One-liners:
USBHal.hincludes<sys/time.h>forgettimeofday()(previouslyreached via
libusb.h);RTC.cppcopiestimeval::tv_secthrough atime_t(64-bit on Windows);
MQTT.cpptakesntohl()from<winsock2.h>;AdminModule.husesuint32_tforsession_time(uintis a glibc typedef).Linux and macOS behaviour is unchanged.
Prerequisites
argp is not packaged for MSYS2's mingw environments and must be built once from
source, the same dependency macOS meets with
brew install argp-standalone.Steps are in the env comment and the CI workflow. CH341 hardware additionally
needs WCH's CH341PAR driver.
Testing
Built and run on Windows 11, MSYS2 UCRT64 (gcc 16.1.0), via plain
pio run -e native-windowswith no local overrides, so the pinned platform,framework and WiFi submodule resolve as a clean consumer gets them.
MSYS2 present.
sx1262 init success, transmitted a NodeInfo, and aseparate mesh node rebroadcast it back (
rxSNR=6.5 rxRSSI=-71). Zero SPI errors.meshtastic --infoover TCP returns full config.build_windows_bin.ymlhas never run before this PR, so its argp-from-source stepand static-link guard are unproven. It is
continue-on-errorlike the MacOS job.Known issue
Config writes over the TCP API (
meshtastic --set ...) are lost on Windows;reads work. The client sends its admin message then closes immediately, the peer
RSTs, and Winsock discards already-received unread data where Linux returns it
before reporting
ECONNRESET. Holding the connection open ~50ms makes the writeland, so the API path itself is fine. Not CH341-specific (SimRadio does it too)
and not introduced here; the fix belongs in meshtastic-python's
TCPInterface.close(), which doesshutdown(SHUT_RDWR)without draining.Meanwhile config can be set by editing
prefs/config.protodirectly.Summary by CodeRabbit
meshtasticd.exeartifacts and verifies bundled Windows DLL dependencies before publishing.