feat(lora+gps): Add components for gps, sx126x lora radio. Add preliminary meshtastic component (ai-generated), and update bsps to start testing - #678
Conversation
…eliminary meshtastic component (ai-generated), and update bsps to start testing
|
✅Static analysis result - no issues found! ✅ |
There was a problem hiding this comment.
Pull request overview
Adds a new “wireless” documentation section and introduces new gps, sx126x (LoRa radio), and preliminary meshtastic components, then wires LoRa/GPS support into the LilyGo T-Deck and M5Stack Cardputer-Adv BSPs and examples so they can be built/tested in CI.
Changes:
- Add new components:
gps(UART NMEA GNSS),sx126x(SX1261/2/LLCC68 LoRa driver),meshtastic(radio-agnostic Meshtastic-compatible node + example). - Update BSPs/examples (
t-deck,m5stack-cardputer) to initialize and demonstrate LoRa + GPS (plus shared-bus handling on the Cardputer Cap). - Update docs (new
doc/en/wireless/*) and CI/component publishing lists to include the new components and examples.
Reviewed changes
Copilot reviewed 68 out of 68 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| doc/en/wireless/index.rst | Introduces the “Wireless & Radio APIs” doc section and links new wireless component pages. |
| doc/en/wireless/sx126x.rst | Adds SX126x component documentation entry and API include hooks. |
| doc/en/wireless/gps.rst | Adds GPS component documentation entry and API include hooks. |
| doc/en/wireless/meshtastic.rst | Adds Meshtastic component documentation entry and API include hooks. |
| doc/en/index.rst | Adds the wireless docs section to the top-level docs index. |
| doc/Doxyfile | Adds new components’ headers/examples to Doxygen inputs/example paths. |
| components/sx126x/CMakeLists.txt | Registers the sx126x component build. |
| components/sx126x/include/sx126x.hpp | Defines the public SX126x driver API/types/config. |
| components/sx126x/src/sx126x.cpp | Implements the SX126x driver (SPI command layer + LoRa modem support). |
| components/sx126x/README.md | Adds SX126x component overview and feature list. |
| components/sx126x/idf_component.yml | Adds SX126x component manager metadata (deps/tags/docs link). |
| components/sx126x/example/CMakeLists.txt | Adds SX126x example project configuration. |
| components/sx126x/example/sdkconfig.defaults | Adds SX126x example default sdkconfig. |
| components/sx126x/example/README.md | Adds SX126x example usage documentation. |
| components/sx126x/example/main/CMakeLists.txt | Registers the SX126x example “main” component. |
| components/sx126x/example/main/Kconfig.projbuild | Adds SX126x example menuconfig options (hardware/pins/frequency/etc.). |
| components/sx126x/example/main/sx126x_example.cpp | Adds SX126x example app (bring-up + RX + periodic TX ping). |
| components/gps/CMakeLists.txt | Registers the gps component build. |
| components/gps/include/gps.hpp | Defines the UART-attached GPS driver public API. |
| components/gps/include/nmea_parser.hpp | Defines the platform-independent NMEA parser API. |
| components/gps/include/gps_fix.hpp | Defines the parsed fix struct and timestamp conversion helper. |
| components/gps/src/gps.cpp | Implements the UART driver + background read/parsing task. |
| components/gps/src/nmea_parser.cpp | Implements NMEA parsing/checksum and fix extraction. |
| components/gps/README.md | Adds GPS component overview and capabilities. |
| components/gps/idf_component.yml | Adds GPS component manager metadata (deps/tags/docs link). |
| components/gps/example/CMakeLists.txt | Adds GPS example project configuration. |
| components/gps/example/sdkconfig.defaults | Adds GPS example default sdkconfig. |
| components/gps/example/README.md | Adds GPS example usage documentation. |
| components/gps/example/main/CMakeLists.txt | Registers the GPS example “main” component. |
| components/gps/example/main/Kconfig.projbuild | Adds GPS example menuconfig options (hardware/pins/baud). |
| components/gps/example/main/gps_example.cpp | Adds GPS example app (print fixes). |
| components/meshtastic/CMakeLists.txt | Registers the meshtastic component build. |
| components/meshtastic/include/meshtastic.hpp | Defines MeshtasticNode public API/config/callbacks. |
| components/meshtastic/include/meshtastic_types.hpp | Adds protocol constants and message structs. |
| components/meshtastic/include/meshtastic_protocol.hpp | Declares channel hashing/modem config/header pack/unpack helpers. |
| components/meshtastic/include/meshtastic_crypto.hpp | Declares PSK expansion and AES-CTR payload crypt helpers. |
| components/meshtastic/include/meshtastic_protobuf.hpp | Declares minimal protobuf encode/decode helpers for used messages. |
| components/meshtastic/src/meshtastic.cpp | Implements node behavior (send/receive/dedup/rebroadcast). |
| components/meshtastic/src/meshtastic_protocol.cpp | Implements region/preset-based modem config + header pack/unpack + hashes. |
| components/meshtastic/src/meshtastic_crypto.cpp | Implements PSK expansion and AES-CTR crypt via mbedtls. |
| components/meshtastic/src/meshtastic_protobuf.cpp | Implements minimal protobuf wire-format encode/decode for required messages. |
| components/meshtastic/README.md | Adds Meshtastic component overview/scope/licensing notes. |
| components/meshtastic/idf_component.yml | Adds Meshtastic component manager metadata (deps/tags/docs link). |
| components/meshtastic/example/CMakeLists.txt | Adds Meshtastic example project configuration. |
| components/meshtastic/example/sdkconfig.defaults | Adds Meshtastic example default sdkconfig. |
| components/meshtastic/example/README.md | Adds Meshtastic example usage documentation. |
| components/meshtastic/example/main/CMakeLists.txt | Registers the Meshtastic example “main” component. |
| components/meshtastic/example/main/Kconfig.projbuild | Adds Meshtastic example menuconfig options (hardware/region/intervals/etc.). |
| components/meshtastic/example/main/meshtastic_example.cpp | Adds Meshtastic node example using BSP-provided SX1262 radio. |
| components/t-deck/CMakeLists.txt | Adds gps/sx126x to T-Deck component requirements. |
| components/t-deck/idf_component.yml | Adds gps/sx126x dependencies to the T-Deck component manifest. |
| components/t-deck/include/t-deck.hpp | Adds LoRa + GPS APIs/pins/state to the T-Deck BSP. |
| components/t-deck/src/lora.cpp | Implements T-Deck LoRa + GPS initialization. |
| components/t-deck/example/README.md | Updates T-Deck example docs to include LoRa demo behavior. |
| components/t-deck/example/main/t_deck_example.cpp | Adds LoRa tab/demo wiring and non-blocking TX dispatch via main loop. |
| components/t-deck/example/main/gui.hpp | Extends T-Deck example GUI API to support a LoRa tab and logging. |
| components/t-deck/example/main/gui.cpp | Implements the new LoRa tab UI and thread-safe message/status updates. |
| components/m5stack-cardputer/CMakeLists.txt | Adds gps/sx126x to Cardputer component requirements. |
| components/m5stack-cardputer/idf_component.yml | Adds gps/sx126x dependencies to the Cardputer component manifest. |
| components/m5stack-cardputer/include/m5stack-cardputer.hpp | Adds LoRa+GPS Cap APIs/pins/state and new helper methods. |
| components/m5stack-cardputer/src/lora_gps_cap.cpp | Implements LoRa+GPS Cap SPI-bus bring-up and LoRa/GPS initialization. |
| components/m5stack-cardputer/src/sdcard.cpp | Adjusts SD init to reuse the shared expansion SPI bus when LoRa is present. |
| components/m5stack-cardputer/example/README.md | Updates Cardputer example docs for new tabbed UI + LoRa/GPS features. |
| components/m5stack-cardputer/example/main/m5stack_cardputer_example.cpp | Wires LoRa+GPS demos into the example and updates tab/shortcut behavior. |
| components/m5stack-cardputer/example/main/gui.hpp | Refactors Cardputer GUI into tabbed layout with LoRa/GPS/Sys tabs. |
| components/m5stack-cardputer/example/main/gui.cpp | Implements tabbed UI, LoRa log/status rendering, and new per-tab helpers. |
| .github/workflows/build.yml | Adds gps/meshtastic/sx126x examples to CI build matrix. |
| .github/workflows/upload_components.yml | Adds gps/meshtastic/sx126x to component upload list. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 71 out of 71 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (3)
components/t-deck/example/main/t_deck_example.cpp:1
std::transformis used but<algorithm>is not included in this file, which can cause compilation failures depending on toolchain/headers. Add#include <algorithm>alongside the other standard includes.
components/t-deck/example/main/t_deck_example.cpp:1std::transformis used but<algorithm>is not included in this file, which can cause compilation failures depending on toolchain/headers. Add#include <algorithm>alongside the other standard includes.
components/meshtastic/src/meshtastic_protobuf.cpp:1pos + length > sizecan overflow whenlengthis a large varint (and then incorrectly pass the bounds check), and(size_t)lengthtruncation can produce a span smaller than intended. Use an overflow-safe bounds check (e.g.,length > (size - pos)after validatinglength <= SIZE_MAX) before creating the span and advancingpos.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 71 out of 71 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
components/t-deck/example/main/t_deck_example.cpp:1
std::transformis used later in this file (inprintable()), but<algorithm>is not included in the shown includes. This is a compile error on standard toolchains; add#include <algorithm>.
components/sx126x/src/sx126x.cpp:1receiving_is set tofalseinstart_transmit(), so this TX_DONE path will never restart RX even if the radio was in continuous receive before TX (for non-blockingstart_transmit()usage). If the intended behavior is “return to RX if we were receiving before TX”, store that prior state (e.g.,return_to_rx_after_tx_) instart_transmit()and consult it here; otherwise, update/remove the misleading comment.
components/meshtastic/src/meshtastic_protobuf.cpp:1- The decode paths parse fields without validating that
wirematches the expected protobuf wire type for that field (e.g.,portnumshould be VARINT,payloadshould be LENGTH). Because this processes untrusted over-the-air frames, add wire-type checks per field andskip(wire)(or returnnullopt) on mismatch to avoid mis-decoding random/corrupted bytes into plausible-looking messages.
components/meshtastic/src/meshtastic.cpp:1 - On invalid PSK length,
expand_psk()returns an empty key and the node continues operating unencrypted (only logging an error). That’s a dangerous failure mode because it can silently downgrade confidentiality if logs are missed. Consider making this a hard failure (e.g., set an internalpsk_valid_flag and havesend_*()/handle_frame()refuse to operate when invalid), or provide an explicitallow_unencrypted_fallbackconfig to require a conscious opt-in.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 71 out of 71 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
components/gps/include/gps.hpp:7
- This header uses std::atomic and std::error_code but does not include or <system_error>, relying on transitive includes. Adding the direct includes makes the header self-contained and less fragile across toolchains.
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
components/sx126x/include/sx126x.hpp:8
- This header declares APIs taking std::error_code but does not include <system_error>. It currently relies on transitive includes; add the direct include so the header is self-contained.
components/meshtastic/include/meshtastic_types.hpp:5 - This header uses size_t for constants but does not include , which is the standard header that defines size_t. Relying on indirect includes (e.g. from ) is fragile.
components/meshtastic/src/meshtastic.cpp:140 - config_.hop_limit is documented as 0-7, but it is not validated before packing into 3 bits in the header. Values >7 will be truncated by pack_header(), leading to unexpected behavior for API consumers. Clamp to 7 when building the header.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 71 out of 71 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
components/t-deck/example/main/gui.cpp:1
- Hard-coding the LoRa tab index (
== 2) is brittle if tabs are reordered/inserted later. Prefer deriving the index from stored tab objects/IDs (or a named constant/enum) so future UI changes don’t silently break keyboard routing.
components/meshtastic/src/meshtastic_protocol.cpp:1 RegionInfo::spacing_mhzis always returned as0.0ffor every region, which makes the field (and any logic expecting non-zero spacing) confusing. Either removespacing_mhzentirely or populate it with meaningful values and document what it represents in the Meshtastic slot algorithm.
components/meshtastic/src/meshtastic.cpp:1- With an invalid PSK length, the node continues with an unencrypted channel (it logs loudly, but still 'fails open'). Consider failing closed by default (e.g., refuse to send/accept frames, or require an explicit
allow_unencrypted_fallbackconfig flag) to prevent accidental cleartext operation due to misconfiguration.
components/m5stack-cardputer/example/README.md:30 - The README says the UI has 'five tabs' but the table enumerates six (Text, LoRa, IMU, GPS, Sys, Help). Update the prose to match the actual tab count so docs don’t contradict the UI.
The UI is a tabview with five tabs, switched with **fn+Tab** (or jumped to
directly with fn+1 / fn+2 / fn+9):
| Tab | Contents |
|-----|----------|
| **Text** | The text editor - type here; this is also where LoRa messages are composed |
| **LoRa** | Radio status and a scrolling log of sent / received messages with RSSI |
| **IMU** | Live accelerometer / gyroscope readings (ADV only) |
| **GPS** | GNSS fix status (position, satellites, UTC time) from the LoRa+GPS Cap |
| **Sys** | Board variant, battery, and speaker / mic volumes |
| **Help** | The list of controls |
…to feat/gps-lora-meshtastic
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 71 out of 71 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (4)
components/t-deck/example/main/t_deck_example.cpp:1
std::transformis used but this file’s includes (in this diff) don’t include<algorithm>, which can break compilation on stricter toolchains. Add#include <algorithm>alongside the other headers in this translation unit (mandatory).
components/meshtastic/src/meshtastic_protobuf.cpp:1- The decoders compute
wirebut do not validate it for known fields before parsing (e.g., field 1 is assumed varint even if the incoming data uses a different wire type). Since frames are untrusted over-the-air inputs, validate wire types for each known field and either (a) skip the field on mismatch or (b) reject the message (nullopt). Apply the same fix pattern indecode_user()anddecode_position().
components/meshtastic/src/meshtastic.cpp:1 - With an invalid PSK length, the node continues operating with
key_empty, which can result in transmitting/processing traffic as if encryption were disabled. This is easy to miss operationally and can lead to accidental cleartext transmission. Prefer failing closed: treat invalid PSK as a configuration error that disablessend_*(and possiblyhandle_frame) until corrected, or require the user to explicitly opt into unencrypted mode.
components/sx126x/src/sx126x.cpp:1 - This method reads IRQ flags but never clears
IRQ_PREAMBLE_DETECTED/IRQ_HEADER_VALID. Since those flags can remain set until explicitly cleared on SX126x,is_reception_in_progress()can become ‘sticky true’ after a transient preamble/header event (especially because those IRQs aren’t routed to DIO1 in your current DIO1 mask). Consider clearing just these bits after reading (or maintaining an internal, explicitly-cleared snapshot) so the result reflects current/recent activity as documented.
| // whether the board has a working IMU / microphone / LoRa radio / GPS; set | ||
| // after keyboard / variant detection below, referenced by the keypress | ||
| // callback | ||
| static bool have_imu = false; | ||
| static bool have_mic = false; | ||
| static bool have_lora = false; | ||
| static bool have_gps = false; | ||
| static std::shared_ptr<espp::Sx126x> lora_radio; |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 71 out of 71 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (5)
components/meshtastic/src/meshtastic.cpp:1
- If the PSK length is invalid, the code currently logs an error but implicitly falls back to an unencrypted channel (empty
key_). That creates an encryption-downgrade footgun (a typo/mis-sized key silently disables confidentiality). Consider treating an invalid PSK as a hard configuration error: track apsk_valid_flag set false here and havesend_packet()/handle_frame()refuse to operate (or require an explicitpsk_disabledsetting) rather than silently proceeding unencrypted.
components/meshtastic/example/main/meshtastic_example.cpp:1 - The comment says the probe node 'does no I/O', but constructing
MeshtasticNodereads the MAC address and logs. For accuracy (and to avoid side effects in an example), consider either updating the comment or replacing the probe-node construction with a direct call toespp::meshtastic::compute_modem_config(...)to derive the modem config without instantiating a node.
components/t-deck/example/main/t_deck_example.cpp:1 std::transformis used here; ensure the file includes<algorithm>explicitly rather than relying on transitive includes. This improves build robustness across toolchains/stdlib implementations.
components/t-deck/example/main/gui.cpp:1- Hard-coding the LoRa tab index (
== 2) makes this fragile if tabs are reordered or additional tabs are inserted. Consider defining a named constant for the LoRa tab index (shared with tab creation), or deriving the index from the tab handle if LVGL provides an API for that, so future UI changes don’t silently break keyboard routing.
components/m5stack-cardputer/example/README.md:30 - This section says 'five tabs' but the table lists six (Text, LoRa, IMU, GPS, Sys, Help). Update the count to match the actual tab list to avoid confusing users.
The UI is a tabview with five tabs, switched with **fn+Tab** (or jumped to
directly with fn+1 / fn+2 / fn+9):
| Tab | Contents |
|-----|----------|
| **Text** | The text editor - type here; this is also where LoRa messages are composed |
| **LoRa** | Radio status and a scrolling log of sent / received messages with RSSI |
| **IMU** | Live accelerometer / gyroscope readings (ADV only) |
| **GPS** | GNSS fix status (position, satellites, UTC time) from the LoRa+GPS Cap |
| **Sys** | Board variant, battery, and speaker / mic volumes |
| **Help** | The list of controls |
Description
Adds LoRa + GPS support to espp, along with a radio-agnostic Meshtastic node, and wires all three into the T-Deck and M5Stack Cardputer BSPs.
New components
sx126x—espp::Sx126xdriver for Semtech SX1261 / SX1262 / LLCC68 LoRa radios. Built onespp::BasePeripheral<uint8_t, false>; the caller supplies awrite_then_readthat holds chip-select across the write and read phases (one full-duplex SPI transfer), since SX126x commands return their status/data in the same CS assertion. LoRa modulation with explicit header, configurable frequency / SF / BW / CR / preamble / CRC / sync word, TCXO and DC-DC/LDO options, and DIO1 IRQ servicing.gps—espp::Gps(UART NMEA reader task) plus a platform-independentespp::NmeaParser(RMC/GGA) and anespp::GpsFixvalue type with a user-supplied on-fix callback.meshtastic—espp::MeshtasticNode, radio-agnostic (you provide atransmitfunction and feed it received frames). Implements over-the-air interop on the public default channel: 16-byte little-endian packet header, AES-CTR payload encryption with the default PSK, djb2 frequency-slot + XOR channel hashing, a small hand-written protobuf codec for Text / NodeInfo / Position, packet de-duplication, and optional managed-flood rebroadcast. The README carries a clean-room / licensing note (Meshtastic firmware and protobufs are GPL-3; this is an independent MIT implementation of the wire format).BSP integration
— both boards gain
initialize_lora(radio_config)/lora()andinitialize_gps(...)/gps():Examples
sx126x,gps, andmeshtasticexamples (the meshtastic example has a Kconfig board + region selector and runs a full node).Docs / CI
— per-component READMEs, a new
doc/en/wireless/docs section (index + sx126x + gps + meshtastic, each with an example page), Doxyfile inputs, andbuild.yml/upload_components.ymlmatrix entries for the three new examples.Motivation and Context
espp had no LoRa or GPS support and no path to interoperate with the Meshtastic ecosystem. This adds reusable, radio-/board-agnostic building blocks (a general SX126x driver, an NMEA GPS reader, and a Meshtastic wire codec) and demonstrates them end-to-end on two popular ESP32-S3 dev boards, so espp users can build LoRa messaging / mesh / tracker applications.
How has this been tested?
0x12), confirmed bidirectional on real devices using the GUI examples' LoRa tabs. GPS fixes displayed on both boards. TCXO voltage / regulator settings for each module were validated against the vendors' RadioLib configs after an initial dead-oscillator symptom on the Cardputer module.esp32s3with ESP-IDF v5.5.2 —sx126x,gps, andmeshtasticexamples (the latter for both the T-Deck and Cardputer-Adv Kconfig targets), plus the updated T-Deck and Cardputer GUI examples.--enable=all, the repo's CI config) clean on the changed files.Screenshots (if appropriate, e.g. schematic, board, console logs, lab pictures):
Types of changes
Checklist:
Software
.github/workflows/build.ymlfile to add my new test to the automated cloud build github action.