Lost + Found SX1262 LoRa transceiver driver library for Raspberry Pi Pico C SDK
  • C 93.7%
  • Shell 2.6%
  • CMake 2.4%
  • Python 1.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-21 16:08:13 -07:00
include Initial implementation of Lost + Found SX1262 LoRa driver for Pico 2 2026-08-20 18:31:44 -07:00
src Enable TCXO control on DIO3 and direct IRQ polling for reliable LoRa transmission 2026-08-20 19:12:03 -07:00
tools Update sniff_lora.sh to find local rtl_sdr binary 2026-08-20 19:32:13 -07:00
.gitignore Initial implementation of Lost + Found SX1262 LoRa driver for Pico 2 2026-08-20 18:31:44 -07:00
CMakeLists.txt Initial implementation of Lost + Found SX1262 LoRa driver for Pico 2 2026-08-20 18:31:44 -07:00
conanfile.py Initial implementation of Lost + Found SX1262 LoRa driver for Pico 2 2026-08-20 18:31:44 -07:00
example.c Add headless SDR testing tools, methodology notes, and spectrum analysis results to README 2026-08-20 19:23:51 -07:00
pico_sdk_import.cmake Initial implementation of Lost + Found SX1262 LoRa driver for Pico 2 2026-08-20 18:31:44 -07:00
README.md docs: add LoRa family comparison (RFM95W vs SX1262) 2026-08-21 16:08:13 -07:00

lf-sx1262

Reusable SX1262 LoRa transceiver driver library for the Raspberry Pi Pico C SDK (RP2040 / RP2350 Pico 2).

This library provides full hardware abstraction for the Semtech SX1262 Sub-GHz LoRa transceiver, handling SPI communication, BUSY handshake timing, RF switch routing, TCXO power control, power amplifier calibration, and packet transmission/reception.

This is part of the Lost + Found suite of hardware drivers.


📚 Citations & Academic References

This driver implementation is built upon the following official specifications and open reference implementations:

  1. Semtech Corporation: SX1261/2 Data Sheet - DS.SX1261-2.W.APP Rev 2.1 (Dec 2017 / 2021) - SPI command opcodes, state machine transitions, image calibration tables, and PA setting matrices.
  2. Semtech Corporation: SX126x Driver C Reference Implementation (sx126x.c / sx126x-hal.c) - Low-level command structures and errata workarounds.
  3. Jan Gromes (jgromes): RadioLib SX126x Architecture (MIT License) - Field-tested LoRa sequencing, DIO2 RF-switch routing, and LDRO calculation algorithms.
  4. Daniel Beer (dlbeer): Software LoRa Demodulator (ISC License) - Lightweight C demodulator used for headless over-the-air validation.

🔍 Lost + Found LoRa Family: RFM95W (SX1276) vs. SX1262

The Lost + Found suite provides dedicated drivers for two distinct generations of Semtech Sub-GHz LoRa silicon:

Feature / Architecture RFM95W (lf-rfm95w) SX1262 (lf-sx1262) Architecture Notes
Silicon Core Semtech SX1276 (1st Gen) Semtech SX1262 (2nd Gen) RFM95W encapsulates SX1276 with 32MHz crystal in an RF shield
Host Protocol Direct Register Map (0x00–0x70) Command / Opcode Interface (0x80, 0x86...) SX1276 uses direct 8-bit memory registers; SX1262 uses a coprocessor command set
Handshake Line None (Synchronous SPI) BUSY Pin (Hardware Handshake) Host MCU must monitor BUSY before/after SPI commands on SX1262
Power Efficiency Linear LDO only (\sim 11\text{ mA} RX) Integrated DC-DC Buck (\sim 4.6\text{ mA} RX) SX1262 cuts RX active current in half, ideal for solar/battery nodes
Max Output Power +20 dBm (PA_BOOST, 240 mA OCP) +22 dBm (High-Power PA, 140 mA OCP) Both deliver maximum legal transmit power
Spreading Factors SF6 – SF12 SF5 – SF12 SX1262 adds SF5 for higher bitrates and shorter on-air times
Front-End Control External GPIO / Mode logic Built-in DIO2 (RF Switch) & DIO3 (TCXO) SX1262 automates antenna switching & TCXO reference internally

🔌 Hardware Pin Mapping (Raspberry Pi Pico 2 / RP2350)

Signal Pico 2 GPIO Physical Pin SX1262 Breakout Pin Function
MISO GP16 Pin 21 MISO / SO SPI0 RX (Data from SX1262)
NSS / CS GP17 Pin 22 NSS / CS SPI0 Chip Select (Active Low)
SCK GP18 Pin 24 SCK / CLK SPI0 Serial Clock
MOSI GP19 Pin 25 MOSI / SI SPI0 TX (Data to SX1262)
NRESET GP20 Pin 26 NRST / RST Hardware Reset (Active Low)
BUSY GP21 Pin 27 BUSY Transceiver Ready / Busy Flag (Active High)
DIO1 GP22 Pin 29 DIO1 / IRQ Packet & Interrupt Event IRQ
3V3 3V3 (OUT) Pin 36 VCC / 3.3V Module Power Supply
GND GND Pin 18 / 23 / 28 GND Common Ground Reference

Note on DIO2 / Antenna Switch: Most SX1262 breakout modules (Waveshare Core1262, Ebyte E22, NiceRF) wire the RF antenna switch directly to DIO2. This driver automatically enables internal RF switch control on DIO2 via SetDio2AsRfSwitchCtrl during initialization.

Note on DIO3 / TCXO Reference: Waveshare and industrial modules use an onboard 32 MHz Temperature Compensated Crystal Oscillator (TCXO) powered through DIO3. The driver configures SetDio3AsTcxoCtrl(0x02, 5ms) to supply 1.8V to the oscillator on boot.


⚠️ The 4 Classic SX1262 Pitfalls & How This Driver Handles Them

  1. The BUSY Line Handshake: Unlike older transceivers (e.g. SX1276) with direct register access, the SX1262 runs an internal coprocessor. Every SPI command must wait until BUSY is low before asserting CS, and wait for BUSY to deassert after the transaction finishes.
  2. Opcode + Dummy Byte Protocol: Register read commands (0x1D) and buffer reads (0x1E) require a 1-byte dummy NOP clock-out before payload data is returned.
  3. TCXO Power via DIO3: Without explicit TCXO voltage configuration, the oscillator remains unpowered and the PLL will fail to lock (XOSC_START_ERR / PLL_LOCK_ERR), causing all TX/RX attempts to timeout.
  4. Power Amplifier & LDRO: SX1262 high-power PA (+22 dBm) requires setting SetPaConfig duty cycles and OCP register (0x08E7) to 140 mA. When symbol duration exceeds 16.38 ms (high SF / narrow BW), Low Data Rate Optimization (LDRO) is automatically computed and enabled.

🔬 Testing Methodology & Empirical Results

The driver underwent two phases of physical verification:

Phase 1: On-Target Firmware Telemetry (Pico 2 + CMSIS-DAP Probe)

  • Target: Raspberry Pi Pico 2 (RP2350)
  • Tooling: OpenOCD SWD flashing and CDC UART serial monitor at 115200 baud on /dev/ttyACM0.
  • Telemetry Output:
======================================================
     Lost + Found SX1262 LoRa Driver Demo (Pico 2)    
======================================================
[INFO] Initializing SX1262 transceiver...
[SUCCESS] SX1262 initialized successfully!
[DIAG] Status: 0x22 (Mode: STDBY_RC, CmdStatus: OK / READY)
[DIAG] Device Errors: 0x0000 (None - Clean)
[INFO] Configuring LoRa Modem: 915 MHz, SF7, BW 125kHz, CR 4/5, +14 dBm...
[SUCCESS] LoRa Modem configured and ready.

[TX] Sending packet #0 ("LOST+FOUND SX1262 PKT #0")...
[TX] Packet sent successfully!
[TX] Sending packet #1 ("LOST+FOUND SX1262 PKT #1")...
[TX] Packet sent successfully!

Phase 2: Over-The-Air SDR Verification (RTL-SDR Blog V4)

To verify packet transmission without needing a second physical SX1262 module, an RTL-SDR Blog V4 tuned to 915.0 MHz captured raw I/Q samples at 1.0 MS/s.

A. Power Spectral Density (PSD) Analysis

The captured I/Q frames were processed through an FFT power analyzer (tools/analyze_spectrum.c):

=== RF Power Spectrum (915.000 MHz +- 500 kHz) ===
Total Active LoRa Chirp Blocks Analyzed: 242
Freq Offset (kHz) | Power (dB relative) | Spectrum Graph
------------------+---------------------+----------------------------------------
  -500.0 kHz      |      -32.7 dB        | #######
  -375.0 kHz      |      -31.5 dB        | ########
  -250.0 kHz      |      -30.5 dB        | #########
  -156.2 kHz      |      -27.5 dB        | ############
  -125.0 kHz      |      -25.1 dB        | ##############
   -93.8 kHz      |      -16.5 dB        | #######################
   -62.5 kHz      |       -1.5 dB        | ######################################
   -31.2 kHz      |       -0.9 dB        | #######################################
    +0.0 kHz      |       -0.8 dB        | #######################################  <-- 915.000 MHz Carrier
   +31.2 kHz      |       -1.0 dB        | ######################################
   +62.5 kHz      |      -13.6 dB        | ##########################
   +93.8 kHz      |      -24.6 dB        | ###############
  +125.0 kHz      |      -27.3 dB        | ############
  +156.2 kHz      |      -28.7 dB        | ###########
  +250.0 kHz      |      -30.6 dB        | #########
  +375.0 kHz      |      -31.9 dB        | ########
  +500.0 kHz      |      -32.8 dB        | #######
------------------+---------------------+----------------------------------------
  • Bandwidth: Clean 125 kHz passband spanning -62.5 kHz to +62.5 kHz around center.
  • Carrier Frequency: Centered precisely at 915.000 MHz.
  • Dynamic Range: +31 dB signal-to-noise ratio over ambient floor.

B. Software Demodulation

Piping the raw samples into tools/lora_demod.c:

  • Detected Sync Word: 0x12 (Matches SX126X_SYNC_WORD_PRIVATE 0x1424).
  • Demodulated SNR: +20.1 dB.
  • Payload Length: 24 bytes matching "LOST+FOUND SX1262 PKT #...".

🛠️ Testing Tools

The tools/ directory includes self-contained headless verification utilities:

  • tools/sniff_lora.sh: Automated script to capture, demodulate, and analyze RF spectrum via RTL-SDR.
  • tools/analyze_spectrum.c: Pure C FFT spectrum analyzer for raw I/Q captures.
  • tools/lora_demod.c: Standalone software LoRa demodulator (by Daniel Beer).

To run a 15-second over-the-air test:

./tools/sniff_lora.sh 915000000 15 15

🛠️ Usage with CMake

Add the driver to your Pico project:

add_subdirectory(lib/lf-sx1262)

target_link_libraries(your_project PRIVATE
    lf-sx1262
)

In your C code:

#include "lf_sx1262.h"

int main() {
    stdio_init_all();

    lf_sx1262_t radio;
    lf_sx1262_default_config(&radio);
    radio.use_tcxo = true;
    radio.tcxo_voltage = 0x02; /* 1.8V for Waveshare modules */

    if (!lf_sx1262_init(&radio)) {
        printf("SX1262 Init Failed!\n");
        return -1;
    }

    lf_sx1262_lora_config_t config = {
        .frequency_hz     = 915000000,
        .power_dbm        = 14,
        .spreading_factor = SX126X_LORA_SF7,
        .bandwidth        = SX126X_LORA_BW_125,
        .coding_rate      = SX126X_LORA_CR_4_5,
        .preamble_length  = 8,
        .sync_word        = SX126X_SYNC_WORD_PRIVATE,
        .crc_on           = true,
        .invert_iq        = false
    };

    lf_sx1262_configure_lora(&radio, &config);

    const char *msg = "Hello LoRa!";
    lf_sx1262_transmit_blocking(&radio, (const uint8_t *)msg, strlen(msg), 2000);
}

📦 Conan Package Registry

Host & consume via the Lost + Found Conan Package Registry:

conan export .