Files
firmware/src/board/PORTING.md
T
swaits 46822c182c feat: add Waveshare RP2040-LoRa board support
First RP2040 board and third MCU family (after ESP32-S3 and nRF52840).

Hardware: RP2040 + SX1262 radio, 850-930 MHz HF, PE4259 antenna switch,
LED on GPIO25, no OLED, USB-C via FPC ribbon cable to adapter board.

New files:
- src/hal/rp2040.rs: MCU primitives (SPI bus, USB driver, flash unique ID MAC)
- src/board/waveshare_rp2040_lora.rs: board implementation
- ld/rp2040-memory.x: linker memory layout

Also fixes cortex-m-rt/set-vtor (moved from global dep to per-board feature,
since Cortex-M0+ has no VTOR register) and makes build.rs memory.x selection
target-aware.
2026-04-10 16:05:08 -06:00

2.9 KiB

Porting DongLoRa to a New Board

Steps

1. Create the board file

Create src/board/your_board.rs. Use an existing board as a template:

  • ESP32-S3 boards: copy heltec_v4.rs (uses shared helpers from board/esp32s3.rs and hal/esp32s3.rs)
  • nRF52840 boards: copy rak_wisblock_4631.rs (uses shared helpers from hal/nrf52840.rs)
  • RP2040 boards: copy waveshare_rp2040_lora.rs (uses shared helpers from hal/rp2040.rs)
  • New MCU family: create a new hal/<mcu>.rs with MCU primitives first

build.rs auto-discovers your board file — any .rs file in src/board/ that implements LoRaBoard for Board is automatically detected as a board. Helper modules (like esp32s3.rs) are auto-discovered via use super::<helper> imports in board files. No template editing or exclusion lists needed.

2. Define concrete types

Your board must export these type aliases:

pub type RadioDriver = ...; // Must work with lora_phy::LoRa<RadioDriver, Delay>
pub type UsbDriver = ...;   // Must implement embassy_usb_driver::Driver<'static>
                             // (or UartDriver for UART boards)
pub type DisplayI2c = ...;  // Must implement embedded_hal_async::i2c::I2c
pub type DisplayDriver = ...; // Must implement DrawTarget<Color = BinaryColor>

3. Define peripheral bundles

pub struct RadioParts {
    pub driver: RadioDriver,
    pub delay: embassy_time::Delay,
}

pub struct UsbParts {  // or UartParts for UART boards
    pub driver: UsbDriver,
}

pub struct DisplayParts {
    pub i2c: DisplayI2c,
}

4. Implement the LoRaBoard trait

use super::traits::{BoardParts, LoRaBoard};

impl LoRaBoard for Board {
    const NAME: &'static str = "Your Board Name";
    const TX_POWER_RANGE: (i8, i8) = (-9, 22); // check your radio + PA

    type RadioParts = RadioParts;
    type CommParts = UsbParts;       // or UartParts
    type DisplayParts = DisplayParts;
    type DisplayDriver = DisplayDriver;

    fn init() -> Self { ... }
    fn mac_address() -> [u8; 6] { ... } // read from efuse, FICR, etc.
    fn into_parts(self) -> BoardParts<RadioParts, UsbParts, DisplayParts> {
        // Initialize buses via hal::, construct drivers, return BoardParts
        BoardParts { radio, host, display: Some(display_parts), mac: Self::mac_address() }
    }
}

5. Add display init

pub async fn create_display(i2c: DisplayI2c) -> Option<DisplayDriver> {
    // Construct and initialize your display driver (SSD1306, SH1106, etc.)
}

6. Add Cargo feature

In firmware/Cargo.toml, add a feature with your board's HAL dependencies:

[features]
your_board = ["dep:your-hal", ...]

7. Add to justfile

Add your board's feature/target/chip to the board definitions at the top of justfile.

8. Build and test

just check your_board    # Must compile
just clippy your_board   # Must be clean
just build your_board    # Produces firmware