- C 93.7%
- Shell 2.6%
- CMake 2.4%
- Python 1.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| include | ||
| src | ||
| tools | ||
| .gitignore | ||
| CMakeLists.txt | ||
| conanfile.py | ||
| example.c | ||
| pico_sdk_import.cmake | ||
| README.md | ||
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:
- 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.
- Semtech Corporation: SX126x Driver C Reference Implementation (
sx126x.c/sx126x-hal.c) - Low-level command structures and errata workarounds. - Jan Gromes (
jgromes): RadioLib SX126x Architecture (MIT License) - Field-tested LoRa sequencing, DIO2 RF-switch routing, and LDRO calculation algorithms. - 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
- 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
BUSYis low before assertingCS, and wait forBUSYto deassert after the transaction finishes. - 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. - 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. - Power Amplifier & LDRO: SX1262 high-power PA (+22 dBm) requires setting
SetPaConfigduty 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(MatchesSX126X_SYNC_WORD_PRIVATE0x1424). - Demodulated SNR:
+20.1 dB. - Payload Length:
24 bytesmatching"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 .