Skip to content

Add native Windows build of meshtasticd#11031

Open
caveman99 wants to merge 5 commits into
developfrom
windows-support
Open

Add native Windows build of meshtasticd#11031
caveman99 wants to merge 5 commits into
developfrom
windows-support

Conversation

@caveman99

@caveman99 caveman99 commented Jul 16, 2026

Copy link
Copy Markdown
Member

Adds [env:native-windows]: a Portduino build of meshtasticd 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. Wired into main_matrix.yml next to the MacOS job. MSVC is not viable:
platform-native's builder calls env.Tool("gcc") and the firmware builds as
gnu17/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 (the portduino.ini bump).

CH341 radio

src/platform/portduino/windows/ implements the pinedio_* API over WCH's
CH341DLL, mirroring wasm/libpinedio_webusb.c. Ch341Hal is unchanged; only the
backend behind the pinedio_* seam differs, so the libch341 lib_dep is
lib_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.

  • 64-bit library is CH341DLLA64.DLL; CH341DLL.DLL is the 32-bit build.
  • Interrupts are polled at upstream's 30 Hz. CH341SetIntRoutine only fires on
    the CH341's INT# pin, but the adapter wires DIO1 to D6.
  • Bit order is set in hardware (CH341SetStream MSB-first) instead of
    reverse-byte'ing each buffer. This is the one behavioural difference from the
    libusb backend.
  • Autoconf (Module: auto) does not work: CH341GetDeviceName returns a device
    path, not a product string. Explicit config works.

libch341 fork

libch341-spi-userspace now points at meshtastic/libch341-spi-userspace
03bf505: pine64's previously pinned commit plus fixes for four data races
between the caller and the interrupt poll thread, which runs the RadioLib
interrupt callback while the main thread drives GPIO and SPI.

  • GPIO shadow state read-modify-written outside the mutex
  • callback pointer re-read after unlocking (jump to NULL on concurrent detach)
  • a second poll thread spawned when re-arming an already-attached pin
  • pthread_join on a handle read after unlocking

These 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 main has not
moved since 2026-01-24. The fork is upstream main plus one commit, so adopting
upstream 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/macOS
CH341 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.py appends -static to
LINKFLAGS, since PlatformIO routes build_flags only to the compile step (same
reason wasm_link_flags.py exists). Windows resolves DLLs from System32 before
PATH, so a stray libusb-1.0.dll there hijacks a dynamic build and kills it with
STATUS_ENTRYPOINT_NOT_FOUND.

HardwareRNG.cpp uses BCryptGenRandom() rather than falling through to
std::random_device, whose libstdc++ Windows backend reports entropy() == 0
and promises no cryptographic source. This buffer seeds key material.

PortduinoGlue.cpp derives the MAC via GetAdaptersAddresses(), standing in
for BlueZ (Linux) and en0 (macOS); otherwise the MAC is all-zero and every user
must pass --hwid. It lives in WindowsMacAddr.cpp because <iphlpapi.h> pulls
in RPC/OLE headers that collide with the Arduino API, which is why this env builds
with -DNOUSER -DWIN32_LEAN_AND_MEAN -DNOGDI.

PowerHAL.cpp defines its defaults strongly on Windows. PE/COFF has no
ELF-style weak definitions, so the weak defaults would not link. Only nrf52 and
nrf54l15 override them.

main.cpp now guards timedatectl with __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.h needs LONG from
windows.h; winsock2.h must precede windows.h). Sorting them breaks the build
on Windows only.

One-liners: USBHal.h includes <sys/time.h> for gettimeofday() (previously
reached via libusb.h); RTC.cpp copies timeval::tv_sec through a time_t
(64-bit on Windows); MQTT.cpp takes ntohl() from <winsock2.h>;
AdminModule.h uses uint32_t for session_time (uint is 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-windows with no local overrides, so the pinned platform,
framework and WiFi submodule resolve as a clean consumer gets them.

  • Static PE32+ binary, no third-party DLL imports, runs on a clean PATH with no
    MSYS2 present.
  • SimRadio: boots headless, API server on 4403, round-trips all 5 pref files.
  • Real MESHSTICK on EU_868: sx1262 init success, transmitted a NodeInfo, and a
    separate mesh node rebroadcast it back (rxSNR=6.5 rxRSSI=-71). Zero SPI errors.
  • meshtastic --info over TCP returns full config.

build_windows_bin.yml has never run before this PR, so its argp-from-source step
and static-link guard are unproven. It is continue-on-error like 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 write
land, 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 does shutdown(SHUT_RDWR) without draining.
Meanwhile config can be set by editing prefs/config.proto directly.

Summary by CodeRabbit

  • New Features
    • Added automated native Windows build CI that publishes versioned meshtasticd.exe artifacts and verifies bundled Windows DLL dependencies before publishing.
    • Added Windows Portduino CH341DLL-based SPI support plus Windows MAC address detection, including the needed libpinedio USB header for compatibility.
  • Bug Fixes
    • Fixed cross-platform RTC time conversion and disabled Linux-only time-sync probing on non-Linux targets.
    • Improved Windows networking byte-order handling for MQTT and hardened GPSD socket behavior on Windows.
  • Other
    • Improved Windows cryptographic entropy, static-linking for native-windows builds, and small portability/type tweaks.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 307936e7-8456-4332-9445-ecda76e7ebf6

📥 Commits

Reviewing files that changed from the base of the PR and between 517c6c9 and 9428899.

📒 Files selected for processing (1)
  • .github/workflows/build_windows_bin.yml
💤 Files with no reviewable changes (1)
  • .github/workflows/build_windows_bin.yml

📝 Walkthrough

Walkthrough

Adds 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.

Changes

Windows native support

Layer / File(s) Summary
Windows build pipeline and environment
.github/workflows/..., variants/native/..., extra_scripts/windows_link_flags.py
Adds the reusable workflow, CI entry point, MSYS2/UCRT64 dependencies, argp build, native-windows environment, static-link flags, binary checks, smoke test, and artifact upload.
Portduino hardware and device APIs
src/platform/portduino/windows/...
Adds the Windows libpinedio API and CH341DLL backend for SPI, GPIO, device initialization, and interrupt polling.
Windows platform API adaptations
src/mesh/HardwareRNG.cpp, src/mqtt/MQTT.cpp, src/power/PowerHAL.cpp, src/modules/AdminModule.h
Adds Windows cryptographic randomness and byte-order headers, adjusts platform hook linkage, and uses uint32_t for session_time.
Portduino networking and host identity
src/platform/portduino/GpsdSerial.cpp, src/platform/portduino/PortduinoGlue.cpp, src/platform/portduino/WindowsMacAddr.cpp, src/platform/portduino/USBHal.h
Adds cross-platform socket helpers, Windows MAC-address discovery, header guards, explicit time declarations, and narrow-string filesystem path handling.
Windows timekeeping behavior
src/gps/RTC.cpp, src/main.cpp
Uses local time_t values for RTC conversions and excludes timedatectl RTC quality detection on Windows.

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
Loading

Possibly related PRs

Suggested reviewers: jp-bennett, thebentern

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding a native Windows meshtasticd build.
Description check ✅ Passed The description is detailed and covers scope, dependencies, testing, and known issues, even though it does not follow the template's attestation checklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch windows-support

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4308487 and dc5e67b.

📒 Files selected for processing (13)
  • .github/workflows/build_windows_bin.yml
  • extra_scripts/windows_link_flags.py
  • src/gps/RTC.cpp
  • src/main.cpp
  • src/mesh/HardwareRNG.cpp
  • src/modules/AdminModule.h
  • src/mqtt/MQTT.cpp
  • src/platform/portduino/GpsdSerial.cpp
  • src/platform/portduino/PortduinoGlue.cpp
  • src/platform/portduino/WindowsMacAddr.cpp
  • src/power/PowerHAL.cpp
  • variants/native/portduino.ini
  • variants/native/portduino/platformio.ini

Comment thread .github/workflows/build_windows_bin.yml
Comment thread src/main.cpp Outdated
Comment thread src/modules/AdminModule.h
@caveman99
caveman99 force-pushed the windows-support branch 2 times, most recently from ecbeeed to 3dcf4dc Compare July 16, 2026 20:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
extra_scripts/windows_link_flags.py (1)

4-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Condense 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

📥 Commits

Reviewing files that changed from the base of the PR and between ecbeeed and 3dcf4dc.

📒 Files selected for processing (13)
  • .github/workflows/build_windows_bin.yml
  • extra_scripts/windows_link_flags.py
  • src/gps/RTC.cpp
  • src/main.cpp
  • src/mesh/HardwareRNG.cpp
  • src/modules/AdminModule.h
  • src/mqtt/MQTT.cpp
  • src/platform/portduino/GpsdSerial.cpp
  • src/platform/portduino/PortduinoGlue.cpp
  • src/platform/portduino/WindowsMacAddr.cpp
  • src/power/PowerHAL.cpp
  • variants/native/portduino.ini
  • variants/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

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Flash this PR in the Web Flasher

firmware commit boards expires

Warning

This is an automated, unreviewed CI test build. Back up your device configuration
before flashing, and only flash devices you are able to recover.

Supported boards built by this PR (29)
Device Board Platform
Crowpanel Adv 3.5 TFT elecrow-adv-35-tft esp32-s3
Heltec HT62 heltec-ht62-esp32c3-sx1262 esp32-c3
Heltec Mesh Node 096 heltec-mesh-node-t096 nrf52840
Heltec Mesh Node T1 heltec-mesh-node-t1 nrf52840
Heltec Mesh Node T114 heltec-mesh-node-t114 nrf52840
Heltec V3 heltec-v3 esp32-s3
Heltec V4 heltec-v4 esp32-s3
Meshnology W10 meshnology_w10 esp32-s3
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
RAK WisMesh Pocket V3 rak_wismesh_pocket nrf52840
RAK WisMesh Repeater Mini V2 rak_wismesh_repeater_mini nrf52840
RAK WisMesh Tag rak_wismeshtag nrf52840
RAK WisBlock 11200 rak11200 esp32
RAK WisBlock 11310 rak11310 rp2040
RAK3312 rak3312 esp32-s3
RAK WisBlock 4631 rak4631 nrf52840
Seeed SenseCAP Mesh-Tracker-X1 seeed_mesh_tracker_X1 nrf52840
Seeed Wio Tracker L1 seeed_wio_tracker_L1 nrf52840
Seeed Xiao NRF52840 Kit seeed_xiao_nrf52840_kit nrf52840
Seeed Xiao ESP32-S3 seeed-xiao-s3 esp32-s3
Station G2 station-g2 esp32-s3
Station G3 station-g3 esp32-s3
LILYGO T-Deck t-deck-tft esp32-s3
LILYGO T-Echo t-echo nrf52840
LILYGO T-Echo Plus t-echo-plus nrf52840
LILYGO T-Impulse Plus t-impulse-plus nrf52840
LilyGo T3-C6 tlora-c6 esp32-c6
Seeed SenseCAP T1000-E tracker-t1000-e nrf52840

Build artifacts expire on 2026-08-16. Updated for 9428899.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
extra_scripts/windows_link_flags.py (1)

5-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep 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

📥 Commits

Reviewing files that changed from the base of the PR and between 3dcf4dc and fb9796f.

📒 Files selected for processing (13)
  • .github/workflows/build_windows_bin.yml
  • extra_scripts/windows_link_flags.py
  • src/gps/RTC.cpp
  • src/main.cpp
  • src/mesh/HardwareRNG.cpp
  • src/modules/AdminModule.h
  • src/mqtt/MQTT.cpp
  • src/platform/portduino/GpsdSerial.cpp
  • src/platform/portduino/PortduinoGlue.cpp
  • src/platform/portduino/WindowsMacAddr.cpp
  • src/power/PowerHAL.cpp
  • variants/native/portduino.ini
  • variants/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

Comment thread src/main.cpp Outdated
Comment thread .github/workflows/build_windows_bin.yml
@jp-bennett

Copy link
Copy Markdown
Collaborator

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between fb9796f and edcf3d1.

📒 Files selected for processing (16)
  • .github/workflows/build_windows_bin.yml
  • extra_scripts/windows_link_flags.py
  • src/gps/RTC.cpp
  • src/main.cpp
  • src/mesh/HardwareRNG.cpp
  • src/modules/AdminModule.h
  • src/mqtt/MQTT.cpp
  • src/platform/portduino/GpsdSerial.cpp
  • src/platform/portduino/PortduinoGlue.cpp
  • src/platform/portduino/USBHal.h
  • src/platform/portduino/WindowsMacAddr.cpp
  • src/platform/portduino/windows/include/libpinedio-usb.h
  • src/platform/portduino/windows/libpinedio_ch341dll.c
  • src/power/PowerHAL.cpp
  • variants/native/portduino.ini
  • variants/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

Comment thread src/platform/portduino/windows/libpinedio_ch341dll.c
Comment thread src/platform/portduino/windows/libpinedio_ch341dll.c
Comment thread src/platform/portduino/windows/libpinedio_ch341dll.c
Comment thread src/platform/portduino/windows/libpinedio_ch341dll.c
@caveman99

Copy link
Copy Markdown
Member Author

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.

Sorry i didn't read that memo ;-) CH341 is working.

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.
@caveman99 caveman99 added enhancement New feature or request hardware-support Hardware related: new devices or modules, problems specific to hardware labels Jul 17, 2026
thebentern and others added 2 commits July 17, 2026 08:25
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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 52bc459 and 5e719da.

📒 Files selected for processing (1)
  • .github/workflows/build_windows_bin.yml

Comment thread .github/workflows/build_windows_bin.yml Outdated
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request hardware-support Hardware related: new devices or modules, problems specific to hardware

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants