Lost + Found RFM95W / SX1276 LoRa transceiver driver library for Raspberry Pi Pico C SDK
  • C 91.8%
  • CMake 3.4%
  • Shell 3.2%
  • Python 1.6%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-21 16:13:20 -07:00
include fix(rfm95w): resolve SPI FIFO synchronization and high-frequency LoRa mode transition 2026-08-21 15:55:58 -07:00
src fix(rfm95w): resolve SPI FIFO synchronization and high-frequency LoRa mode transition 2026-08-21 15:55:58 -07:00
tools fix(rfm95w): resolve SPI FIFO synchronization and high-frequency LoRa mode transition 2026-08-21 15:55:58 -07:00
.gitignore fix(rfm95w): resolve SPI FIFO synchronization and high-frequency LoRa mode transition 2026-08-21 15:55:58 -07:00
CMakeLists.txt Add autonomous beacon transmitter with LED pulse and RP2040-Zero receiver app 2026-08-21 14:32:24 -07:00
conanfile.py Initial commit of Lost + Found RFM95W / SX1276 LoRa transceiver driver 2026-08-21 14:21:48 -07:00
example.c fix(rfm95w): resolve SPI FIFO synchronization and high-frequency LoRa mode transition 2026-08-21 15:55:58 -07:00
example_rx_zero.c fix(rfm95w): resolve SPI FIFO synchronization and high-frequency LoRa mode transition 2026-08-21 15:55:58 -07:00
pico_sdk_import.cmake Initial commit of Lost + Found RFM95W / SX1276 LoRa transceiver driver 2026-08-21 14:21:48 -07:00
README.md docs: explain 6 DIO pins legacy FSK/OOK and LoRa interrupt context 2026-08-21 16:13:20 -07:00

lf-rfm95w

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

This library provides complete hardware abstraction for the Semtech SX1276 / HopeRF RFM95W Sub-GHz LoRa transceiver, handling SPI register access, hardware resets, power amplifier control (+20 dBm PA_BOOST with 240 mA OCP), modem parameters, and bidirectional packet transmission and continuous reception.

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: SX1276/77/78/79 Data Sheet Rev 7 (May 2020) - Register mapping, FIFO operations, spreading factor modem configurations, and PA_DAC settings.
  2. HopeRF: RFM95W/96W/98W Transceiver Module Specification v2.0 - Pinout definitions, frequency bands, and matching requirements.
  3. Jan Gromes (jgromes): RadioLib SX127x Architecture (MIT License) - Register sequencing, OCP formulas, and LoRa packet framing.

🔍 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 Mappings

Standard Raspberry Pi Pico / Pico 2 (RP2350)

RFM95W Breakout Pin Pico 2 GPIO Physical Pin Function
3.3V / VIN 3V3 (OUT) Pin 36 Power supply (3.3V)
GND GND Pin 18 / 23 / 28 Common ground reference
MISO GP16 Pin 21 SPI0 RX (Data from RFM95W)
NSS / CS GP17 Pin 22 SPI0 Chip Select (Active Low)
SCK GP18 Pin 24 SPI0 Serial Clock
MOSI GP19 Pin 25 SPI0 TX (Data to RFM95W)
RESET GP20 Pin 26 Hardware Reset (Active Low)
DIO0 GP22 Pin 29 Primary IRQ (TX Done / RX Done)
ANA Antenna — 915 MHz Monopole (8.2–8.4 cm wire / SMA)

Waveshare RP2040-Zero (Right-Edge Consecutive Pins)

RFM95W Breakout Pin RP2040-Zero GPIO Zero Pin Position Function
3.3V / VIN 3V3 3V3 Power supply (3.3V)
GND GND GND Common ground reference
MISO GP0 Pin 0 SPI0 RX (Data from RFM95W)
NSS / CS GP1 Pin 1 SPI0 Chip Select (Active Low)
SCK GP2 Pin 2 SPI0 Serial Clock
MOSI GP3 Pin 3 SPI0 TX (Data to RFM95W)
RESET GP4 Pin 4 Hardware Reset (Active Low)
DIO0 GP5 Pin 5 Primary IRQ (TX Done / RX Done)
ANA Antenna — 915 MHz Monopole (8.2–8.4 cm wire)

Note

DIO1, DIO2, DIO3, DIO4, and DIO5 can remain unconnected for standard packet operations.


🧠 Why Does the RFM95W Expose 6 DIO Pins? (Legacy FSK/OOK Context)

The RFM95W breakout exposes 6 Digital I/O pins (DIO0 through DIO5). This is due to its dual-personality silicon architecture:

1. Legacy FSK / OOK Streaming

The Semtech SX1276 core is a multi-mode transceiver capable of legacy FSK, GFSK, and OOK modulation (the standards used in garage door openers, 433 MHz weather stations, and older industrial SCADA links). In continuous streaming FSK/OOK modes without packet FIFOs, legacy 8-bit microcontrollers (like AVR / 8051) needed hardware pins for:

  • Bit clock recovery (ClkOut)
  • Raw data sampling (DataReady)
  • Sync address word matches
  • Preamble detection

2. LoRa Interrupt Multiplexing

In LoRa mode, the internal state machine maps events across the DIO lines via RegDioMapping1 (0x40) and RegDioMapping2 (0x41):

Pin Primary LoRa Mapping Purpose in LoRa Mode
DIO0 RxDone / TxDone The Primary Line: Pulses high when a full packet is sent or received. Used by this driver.
DIO1 RxTimeout / CadDetected Fires if an expected packet preamble times out or if Channel Activity Detection (CAD) hears RF energy.
DIO2 FhssChangeChannel Triggers rapid frequency synthesizer hops in Frequency Hopping Spread Spectrum (FHSS) systems.
DIO3 ValidHeader / PayloadCrcError Fires early when the 2.5-byte header arrives, allowing an ultra-low-power MCU to wake up while the payload is in-flight.
DIO4 PllLock / ModeReady Hardware indicator that the internal RF PLL synthesizer has locked onto the target frequency.
DIO5 ClkOut Outputs an internal divided clock from the 32 MHz oscillator to drive the MCU's clock, saving a crystal on low-cost PCBs.

3. Why Modern Drivers Only Wire DIO0

On modern 32-bit microcontrollers (like the RP2040 and RP2350), when DIO0 pulses high on RxDone, the MCU reads RegIrqFlags (0x12) over SPI in under 5 microseconds. This instantly retrieves CRC status, header validity, SNR, and RSSI directly from the silicon registers, making DIO1–DIO5 completely optional for standard packet transceivers.


🛠️ Usage with CMake

Add the driver to your Pico project:

add_subdirectory(lib/lf-rfm95w)

target_link_libraries(your_project PRIVATE
    lf-rfm95w
)

Transmitter Example

#include "lf_rfm95w.h"

int main() {
    stdio_init_all();

    lf_rfm95w_t radio;
    lf_rfm95w_default_config(&radio);

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

    lf_rfm95w_lora_config_t config = {
        .frequency_hz     = 915000000,
        .power_dbm        = 17,
        .spreading_factor = RFM95W_SF_7,
        .bandwidth        = RFM95W_BW_125_KHZ,
        .coding_rate      = RFM95W_CR_4_5,
        .preamble_length  = 8,
        .sync_word        = RFM95W_SYNC_WORD_PRIVATE,
        .crc_on           = true
    };

    lf_rfm95w_configure_lora(&radio, &config);

    const char *msg = "LOST+FOUND BEACON #1";
    if (lf_rfm95w_transmit_blocking(&radio, (const uint8_t *)msg, strlen(msg), 2000)) {
        printf("Sent OK!\n");
    }
}

Continuous Receiver Example

#include "lf_rfm95w.h"

int main() {
    stdio_init_all();

    lf_rfm95w_t radio;
    lf_rfm95w_default_config(&radio);

    lf_rfm95w_init(&radio);

    lf_rfm95w_lora_config_t config = {
        .frequency_hz     = 915000000,
        .power_dbm        = 17,
        .spreading_factor = RFM95W_SF_7,
        .bandwidth        = RFM95W_BW_125_KHZ,
        .coding_rate      = RFM95W_CR_4_5,
        .preamble_length  = 8,
        .sync_word        = RFM95W_SYNC_WORD_PRIVATE,
        .crc_on           = true
    };

    lf_rfm95w_configure_lora(&radio, &config);
    lf_rfm95w_start_receive(&radio);

    char rx_buf[128];
    while (1) {
        lf_rfm95w_rx_status_t status;
        uint8_t len = lf_rfm95w_read_packet(&radio, (uint8_t *)rx_buf, sizeof(rx_buf) - 1, &status);
        if (len > 0) {
            rx_buf[len] = '\0';
            printf("Received %d bytes (RSSI: %d dBm, SNR: %d dB, CRC: %s): \"%s\"\n",
                   len, status.rssi_pkt_dbm, status.snr_pkt_db,
                   status.crc_error ? "FAIL" : "OK", rx_buf);
        }
        sleep_ms(10);
    }
}

📡 Verified Over-The-Air Telemetry

Synchronized dual-node live capture between RP2040-Zero (Transmitter) and Pico 2 RP2350 (Receiver) on 915.000 MHz:

[RP2040-Zero TX]  [TX #39] Broadcasting: "LOST+FOUND ZERO BEACON #39"... Sent OK!
[Pico 2 Receiver] >>> [PACKET RECEIVED #37] 26 bytes | RSSI: -32 dBm | SNR: 6 dB | CRC: OK
[Pico 2 Receiver] Payload: "LOST+FOUND ZERO BEACON #39"

📦 Conan Package Registry

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

conan export .