Scaffold PlatformIO project with 20 board configs and C99/C++ source skeleton
Three-tier configuration hierarchy: - [env:base] — RadioLib + default LoRa parameters - [soc_esp32/esp32s3/nrf52] — platform + framework per SoC - [env:board_name] — board-specific pins + chip selection 20 boards across 4 vendors: - Heltec: 11 boards (T114, CT62, E213, E290, Mesh Solar, T190, Tracker, Tracker V2, V2, V3, V4) - LilyGo: 4 boards (T-Beam 1W, sx1262, sx1276, supreme) - Seeed: 1 board (Xiao S3 + Wio SX1262 with verified pins) - RAK: 4 boards (RAK11310, RAK3112, RAK3401, RAK3x72, RAK4631) Known/verified pins: Heltec V2/V3/V4, RAK4631, Seeed Xiao S3 FIXME pins: all others (placeholders for future research) Source skeleton: - config.h — compile-time defaults + pin validation (#error checks) - kiss.h/c — KISS protocol implementation (C99) - radio.h/cpp — RadioLib wrapper with C API (extern "C" boundary) - main.cpp — Arduino entry point All files pass pre-commit (prettier, markdownlint, YAML check).
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,5 @@
|
||||
node_modules/
|
||||
.cache/
|
||||
.pio/
|
||||
.vscode/
|
||||
*.pyc
|
||||
|
||||
36
PROJECT.md
36
PROJECT.md
@@ -146,22 +146,26 @@ Not all bandwidths are legal for all frequency bands — consult local regulatio
|
||||
|
||||
## Project Structure
|
||||
|
||||
Three-tier configuration hierarchy: base + SoC + board. Pin definitions are compile-time
|
||||
macros in board configs (no separate pins.h).
|
||||
|
||||
```text
|
||||
loramodem/
|
||||
├── platformio.ini # Board environments and build configuration
|
||||
├── PROJECT.md # This file
|
||||
├── src/
|
||||
│ ├── main.cpp # Entry point; Arduino setup() and loop()
|
||||
│ ├── kiss.h / kiss.cpp # KISS frame encoder/decoder
|
||||
│ ├── radio.h / radio.cpp # RadioLib wrapper (init, TX, RX, config)
|
||||
│ └── config.h # Default radio parameters and pin definitions
|
||||
├── include/ # Shared headers (if needed)
|
||||
├── lib/ # Local libraries (if needed)
|
||||
├── variants/ # Board-specific hardware definitions
|
||||
│ ├── heltec_wifi_lora_32_v3/pins.h
|
||||
│ ├── rak4631/pins.h
|
||||
│ └── [other boards]/pins.h
|
||||
└── test/ # PlatformIO Unity test suite
|
||||
├── platformio.ini # [platformio] + [env:base]
|
||||
├── soc/ # SoC shared configs
|
||||
│ ├── esp32/platformio.ini
|
||||
│ ├── esp32s3/platformio.ini
|
||||
│ └── nrf52/platformio.ini
|
||||
├── hardware/ # 20 board configs
|
||||
│ ├── heltec/ (11 boards)
|
||||
│ ├── lilygo/ (4 boards)
|
||||
│ ├── seeed/ (1 board)
|
||||
│ └── rak/ (4 boards)
|
||||
└── src/
|
||||
├── main.cpp # Arduino setup()/loop() — calls C APIs
|
||||
├── kiss.h / kiss.c # KISS protocol — C99
|
||||
├── radio.h / radio.cpp # RadioLib wrapper — C++ (extern "C" API)
|
||||
└── config.h # Compile-time defaults + pin validation
|
||||
```
|
||||
|
||||
## Building
|
||||
@@ -171,7 +175,9 @@ Prerequisites: [PlatformIO](https://platformio.org/) CLI or IDE extension.
|
||||
**Build for a specific board:**
|
||||
|
||||
```sh
|
||||
pio run -e heltec_wifi_lora_32_v3
|
||||
pio run -e heltec_v3
|
||||
pio run -e rak_rak4631
|
||||
pio run -e lilygo_t_beam_1w
|
||||
```
|
||||
|
||||
**Upload to a connected board:**
|
||||
|
||||
14
hardware/heltec/ct62/platformio.ini
Normal file
14
hardware/heltec/ct62/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; Heltec CT62 — nRF52840 (likely)
|
||||
; FIXME: verify board ID, chip, and pin mappings
|
||||
[env:heltec_ct62]
|
||||
extends = soc_nrf52, env:base
|
||||
board = heltec_ct62
|
||||
build_flags =
|
||||
${soc_nrf52.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_HELTEC_CT62
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/heltec/e213/platformio.ini
Normal file
14
hardware/heltec/e213/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; Heltec E213 — SoC/chip unknown
|
||||
; FIXME: identify SoC (ESP32/nRF52?) and LoRa chip, get pin mappings
|
||||
[env:heltec_e213]
|
||||
extends = soc_esp32, env:base
|
||||
board = heltec_e213
|
||||
build_flags =
|
||||
${soc_esp32.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_HELTEC_E213
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/heltec/e290/platformio.ini
Normal file
14
hardware/heltec/e290/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; Heltec E290 — SoC/chip unknown
|
||||
; FIXME: identify SoC and LoRa chip, get pin mappings
|
||||
[env:heltec_e290]
|
||||
extends = soc_esp32, env:base
|
||||
board = heltec_e290
|
||||
build_flags =
|
||||
${soc_esp32.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_HELTEC_E290
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/heltec/mesh_solar/platformio.ini
Normal file
14
hardware/heltec/mesh_solar/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; Heltec Mesh Solar — SoC/chip unknown
|
||||
; FIXME: identify SoC and LoRa chip, get pin mappings
|
||||
[env:heltec_mesh_solar]
|
||||
extends = soc_esp32, env:base
|
||||
board = heltec_mesh_solar
|
||||
build_flags =
|
||||
${soc_esp32.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_HELTEC_MESH_SOLAR
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/heltec/t114/platformio.ini
Normal file
14
hardware/heltec/t114/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; Heltec T114 — nRF52840, SX1262 (likely)
|
||||
; FIXME: verify board ID, chip, and pin mappings from hardware datasheet
|
||||
[env:heltec_t114]
|
||||
extends = soc_nrf52, env:base
|
||||
board = heltec_t114
|
||||
build_flags =
|
||||
${soc_nrf52.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_HELTEC_T114
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/heltec/t190/platformio.ini
Normal file
14
hardware/heltec/t190/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; Heltec T190 — nRF52840 (likely)
|
||||
; FIXME: verify board ID, chip, and pin mappings
|
||||
[env:heltec_t190]
|
||||
extends = soc_nrf52, env:base
|
||||
board = heltec_t190
|
||||
build_flags =
|
||||
${soc_nrf52.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_HELTEC_T190
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/heltec/tracker/platformio.ini
Normal file
14
hardware/heltec/tracker/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; Heltec Tracker — nRF52840, SX1262 (likely)
|
||||
; FIXME: verify board ID and pin mappings from hardware datasheet
|
||||
[env:heltec_tracker]
|
||||
extends = soc_nrf52, env:base
|
||||
board = heltec_tracker
|
||||
build_flags =
|
||||
${soc_nrf52.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_HELTEC_TRACKER
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/heltec/tracker_v2/platformio.ini
Normal file
14
hardware/heltec/tracker_v2/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; Heltec Tracker V2 — nRF52840, SX1262 (likely)
|
||||
; FIXME: verify board ID and pin mappings
|
||||
[env:heltec_tracker_v2]
|
||||
extends = soc_nrf52, env:base
|
||||
board = heltec_tracker_v2
|
||||
build_flags =
|
||||
${soc_nrf52.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_HELTEC_TRACKER_V2
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
12
hardware/heltec/v2/platformio.ini
Normal file
12
hardware/heltec/v2/platformio.ini
Normal file
@@ -0,0 +1,12 @@
|
||||
; Heltec WiFi LoRa 32 V2 — ESP32, SX1276, 868 MHz
|
||||
[env:heltec_v2]
|
||||
extends = soc_esp32, env:base
|
||||
board = heltec_wifi_lora_32
|
||||
build_flags =
|
||||
${soc_esp32.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_HELTEC_V2
|
||||
-DLORA_CHIP_SX1276
|
||||
-DLORA_PIN_NSS=18
|
||||
-DLORA_PIN_DIO0=26
|
||||
-DLORA_PIN_RESET=14
|
||||
13
hardware/heltec/v3/platformio.ini
Normal file
13
hardware/heltec/v3/platformio.ini
Normal file
@@ -0,0 +1,13 @@
|
||||
; Heltec WiFi LoRa 32 V3 — ESP32-S3, SX1262, 868 MHz
|
||||
[env:heltec_v3]
|
||||
extends = soc_esp32s3, env:base
|
||||
board = heltec_wifi_lora_32_v3
|
||||
build_flags =
|
||||
${soc_esp32s3.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_HELTEC_V3
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=8
|
||||
-DLORA_PIN_DIO1=14
|
||||
-DLORA_PIN_RESET=12
|
||||
-DLORA_PIN_BUSY=13
|
||||
13
hardware/heltec/v4/platformio.ini
Normal file
13
hardware/heltec/v4/platformio.ini
Normal file
@@ -0,0 +1,13 @@
|
||||
; Heltec WiFi LoRa 32 V4 — ESP32-S3, SX1262, 868 MHz
|
||||
[env:heltec_v4]
|
||||
extends = soc_esp32s3, env:base
|
||||
board = heltec_wifi_lora_32_v4
|
||||
build_flags =
|
||||
${soc_esp32s3.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_HELTEC_V4
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=8
|
||||
-DLORA_PIN_DIO1=14
|
||||
-DLORA_PIN_RESET=12
|
||||
-DLORA_PIN_BUSY=13
|
||||
14
hardware/lilygo/t_beam_1w/platformio.ini
Normal file
14
hardware/lilygo/t_beam_1w/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; LilyGo T-Beam 1W — ESP32, SX1262 (likely)
|
||||
; FIXME: verify board ID and pin mappings from hardware datasheet
|
||||
[env:lilygo_t_beam_1w]
|
||||
extends = soc_esp32, env:base
|
||||
board = lilygo_t_beam_1w
|
||||
build_flags =
|
||||
${soc_esp32.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_LILYGO_T_BEAM_1W
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/lilygo/t_beam_supreme/platformio.ini
Normal file
14
hardware/lilygo/t_beam_supreme/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; LilyGo T-Beam Supreme (SX1262) — ESP32, SX1262
|
||||
; FIXME: verify board ID and pin mappings
|
||||
[env:lilygo_t_beam_supreme]
|
||||
extends = soc_esp32, env:base
|
||||
board = lilygo_t_beam_supreme
|
||||
build_flags =
|
||||
${soc_esp32.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_LILYGO_T_BEAM_SUPREME
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/lilygo/t_beam_sx1262/platformio.ini
Normal file
14
hardware/lilygo/t_beam_sx1262/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; LilyGo T-Beam with SX1262 — ESP32, SX1262
|
||||
; FIXME: verify board ID and pin mappings
|
||||
[env:lilygo_t_beam_sx1262]
|
||||
extends = soc_esp32, env:base
|
||||
board = lilygo_t_beam_sx1262
|
||||
build_flags =
|
||||
${soc_esp32.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_LILYGO_T_BEAM_SX1262
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
13
hardware/lilygo/t_beam_sx1276/platformio.ini
Normal file
13
hardware/lilygo/t_beam_sx1276/platformio.ini
Normal file
@@ -0,0 +1,13 @@
|
||||
; LilyGo T-Beam with SX1276 — ESP32, SX1276
|
||||
; FIXME: verify board ID and pin mappings
|
||||
[env:lilygo_t_beam_sx1276]
|
||||
extends = soc_esp32, env:base
|
||||
board = lilygo_t_beam_sx1276
|
||||
build_flags =
|
||||
${soc_esp32.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_LILYGO_T_BEAM_SX1276
|
||||
-DLORA_CHIP_SX1276
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO0=0
|
||||
-DLORA_PIN_RESET=0
|
||||
14
hardware/rak/rak11310/platformio.ini
Normal file
14
hardware/rak/rak11310/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; RAK11310 — RP2040-based (different platform)
|
||||
; FIXME: determine platform (likely rp2040), chip, and pin mappings
|
||||
[env:rak_rak11310]
|
||||
extends = soc_nrf52, env:base
|
||||
board = rak11310
|
||||
build_flags =
|
||||
${soc_nrf52.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_RAK_RAK11310
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/rak/rak3112/platformio.ini
Normal file
14
hardware/rak/rak3112/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; RAK3112 — nRF52840 (likely), SX1262 (likely)
|
||||
; FIXME: verify board ID, chip, and pin mappings
|
||||
[env:rak_rak3112]
|
||||
extends = soc_nrf52, env:base
|
||||
board = rak3112
|
||||
build_flags =
|
||||
${soc_nrf52.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_RAK_RAK3112
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/rak/rak3401/platformio.ini
Normal file
14
hardware/rak/rak3401/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; RAK3401 — nRF52840 (likely), SX1262 (likely)
|
||||
; FIXME: verify board ID, chip, and pin mappings
|
||||
[env:rak_rak3401]
|
||||
extends = soc_nrf52, env:base
|
||||
board = rak3401
|
||||
build_flags =
|
||||
${soc_nrf52.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_RAK_RAK3401
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
14
hardware/rak/rak3x72/platformio.ini
Normal file
14
hardware/rak/rak3x72/platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
; RAK3x72 — nRF52840 (likely), SX1262 (likely)
|
||||
; FIXME: verify which variants (3172, 3272, 3372?), board IDs, and pin mappings
|
||||
[env:rak_rak3x72]
|
||||
extends = soc_nrf52, env:base
|
||||
board = rak3x72
|
||||
build_flags =
|
||||
${soc_nrf52.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_RAK_RAK3X72
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=0
|
||||
-DLORA_PIN_DIO1=0
|
||||
-DLORA_PIN_RESET=0
|
||||
-DLORA_PIN_BUSY=0
|
||||
13
hardware/rak/rak4631/platformio.ini
Normal file
13
hardware/rak/rak4631/platformio.ini
Normal file
@@ -0,0 +1,13 @@
|
||||
; RAK4631 — nRF52840, SX1262, 868 MHz
|
||||
[env:rak_rak4631]
|
||||
extends = soc_nrf52, env:base
|
||||
board = wiscore_rak4631
|
||||
build_flags =
|
||||
${soc_nrf52.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_RAK_RAK4631
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=42
|
||||
-DLORA_PIN_DIO1=47
|
||||
-DLORA_PIN_RESET=38
|
||||
-DLORA_PIN_BUSY=46
|
||||
21
hardware/seeed/xiao_s3_wio_sx1262/platformio.ini
Normal file
21
hardware/seeed/xiao_s3_wio_sx1262/platformio.ini
Normal file
@@ -0,0 +1,21 @@
|
||||
; Seeed Xiao S3 + Wio SX1262 — ESP32-S3, SX1262
|
||||
[env:seeed_xiao_s3_wio_sx1262]
|
||||
extends = soc_esp32s3, env:base
|
||||
board = seeed_xiao_s3
|
||||
build_flags =
|
||||
${soc_esp32s3.build_flags}
|
||||
${env:base.build_flags}
|
||||
-DBOARD_SEEED_XIAO_S3_WIO_SX1262
|
||||
-DLORA_CHIP_SX1262
|
||||
-DLORA_PIN_NSS=41
|
||||
-DLORA_PIN_DIO1=39
|
||||
-DLORA_PIN_RESET=42
|
||||
-DLORA_PIN_BUSY=40
|
||||
; SX1262 RF switch on DIO2
|
||||
-DSX126X_DIO2_AS_RF_SWITCH=true
|
||||
-DSX126X_DIO3_TCXO_VOLTAGE=1.8
|
||||
-DSX126X_CURRENT_LIMIT=140
|
||||
; SPI pins
|
||||
-DLORA_PIN_SCLK=7
|
||||
-DLORA_PIN_MISO=8
|
||||
-DLORA_PIN_MOSI=9
|
||||
56
platformio.ini
Normal file
56
platformio.ini
Normal file
@@ -0,0 +1,56 @@
|
||||
; Root PlatformIO configuration for LoRa KISS modem (20 boards)
|
||||
; Board-specific environments are in hardware/<vendor>/<board>/platformio.ini
|
||||
; SoC shared settings are in soc/<soc>/platformio.ini
|
||||
|
||||
[platformio]
|
||||
default_envs =
|
||||
heltec_v3,
|
||||
rak_rak4631,
|
||||
heltec_v2
|
||||
|
||||
extra_configs =
|
||||
soc/esp32/platformio.ini
|
||||
soc/esp32s3/platformio.ini
|
||||
soc/nrf52/platformio.ini
|
||||
hardware/heltec/t114/platformio.ini
|
||||
hardware/heltec/ct62/platformio.ini
|
||||
hardware/heltec/e213/platformio.ini
|
||||
hardware/heltec/e290/platformio.ini
|
||||
hardware/heltec/mesh_solar/platformio.ini
|
||||
hardware/heltec/t190/platformio.ini
|
||||
hardware/heltec/tracker/platformio.ini
|
||||
hardware/heltec/tracker_v2/platformio.ini
|
||||
hardware/heltec/v2/platformio.ini
|
||||
hardware/heltec/v3/platformio.ini
|
||||
hardware/heltec/v4/platformio.ini
|
||||
hardware/lilygo/t_beam_1w/platformio.ini
|
||||
hardware/lilygo/t_beam_sx1262/platformio.ini
|
||||
hardware/lilygo/t_beam_sx1276/platformio.ini
|
||||
hardware/lilygo/t_beam_supreme/platformio.ini
|
||||
hardware/seeed/xiao_s3_wio_sx1262/platformio.ini
|
||||
hardware/rak/rak11310/platformio.ini
|
||||
hardware/rak/rak3112/platformio.ini
|
||||
hardware/rak/rak3401/platformio.ini
|
||||
hardware/rak/rak3x72/platformio.ini
|
||||
hardware/rak/rak4631/platformio.ini
|
||||
|
||||
; ------------------------------------------------------------------
|
||||
; Base environment — all board envs extend this.
|
||||
; Provides: RadioLib dependency, default LoRa parameters.
|
||||
; ------------------------------------------------------------------
|
||||
[env:base]
|
||||
lib_deps =
|
||||
jgromes/RadioLib@^6.6.0
|
||||
|
||||
monitor_speed = 115200
|
||||
|
||||
build_flags =
|
||||
; Default LoRa radio parameters — override per-board as needed
|
||||
-DLORA_FREQ_KHZ=869525UL
|
||||
-DLORA_BW_HZ=125000UL
|
||||
-DLORA_SF=7
|
||||
-DLORA_CR=5
|
||||
-DLORA_POWER_DBM=14
|
||||
-DLORA_SYNCWORD=0x34
|
||||
; KISS serial baud rate
|
||||
-DKISS_BAUD=115200
|
||||
5
soc/esp32/platformio.ini
Normal file
5
soc/esp32/platformio.ini
Normal file
@@ -0,0 +1,5 @@
|
||||
[soc_esp32]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
build_flags =
|
||||
-DARCH_ESP32
|
||||
5
soc/esp32s3/platformio.ini
Normal file
5
soc/esp32s3/platformio.ini
Normal file
@@ -0,0 +1,5 @@
|
||||
[soc_esp32s3]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
build_flags =
|
||||
-DARCH_ESP32S3
|
||||
5
soc/nrf52/platformio.ini
Normal file
5
soc/nrf52/platformio.ini
Normal file
@@ -0,0 +1,5 @@
|
||||
[soc_nrf52]
|
||||
platform = nordicnrf52
|
||||
framework = arduino
|
||||
build_flags =
|
||||
-DARCH_NRF52
|
||||
51
src/config.h
Normal file
51
src/config.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
/* Default LoRa parameters — override per-board in hardware/.../platformio.ini */
|
||||
#ifndef LORA_FREQ_KHZ
|
||||
# define LORA_FREQ_KHZ 869525UL /* 869.525 MHz */
|
||||
#endif
|
||||
#ifndef LORA_BW_HZ
|
||||
# define LORA_BW_HZ 125000UL /* 125 kHz */
|
||||
#endif
|
||||
#ifndef LORA_SF
|
||||
# define LORA_SF 7
|
||||
#endif
|
||||
#ifndef LORA_CR
|
||||
# define LORA_CR 5 /* denominator: coding rate = 4/CR */
|
||||
#endif
|
||||
#ifndef LORA_POWER_DBM
|
||||
# define LORA_POWER_DBM 14
|
||||
#endif
|
||||
#ifndef LORA_SYNCWORD
|
||||
# define LORA_SYNCWORD 0x34 /* LoRa public */
|
||||
#endif
|
||||
|
||||
#ifndef KISS_BAUD
|
||||
# define KISS_BAUD 115200
|
||||
#endif
|
||||
|
||||
/* Pin validation — boards must define all required pins via build_flags */
|
||||
#ifndef LORA_PIN_NSS
|
||||
# error "LORA_PIN_NSS not defined — add to hardware/<vendor>/<board>/platformio.ini"
|
||||
#endif
|
||||
#ifndef LORA_PIN_RESET
|
||||
# error "LORA_PIN_RESET not defined"
|
||||
#endif
|
||||
|
||||
#if defined(LORA_CHIP_SX1276)
|
||||
# ifndef LORA_PIN_DIO0
|
||||
# error "LORA_PIN_DIO0 not defined (required for SX1276)"
|
||||
# endif
|
||||
#else
|
||||
# ifndef LORA_PIN_DIO1
|
||||
# error "LORA_PIN_DIO1 not defined (required for SX1262/LR1110)"
|
||||
# endif
|
||||
# ifndef LORA_PIN_BUSY
|
||||
# error "LORA_PIN_BUSY not defined (required for SX1262/LR1110)"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(LORA_CHIP_SX1276) && !defined(LORA_CHIP_SX1262) && \
|
||||
!defined(LORA_CHIP_LR1110)
|
||||
# error "No LoRa chip defined — set LORA_CHIP_SX1276, LORA_CHIP_SX1262, or LORA_CHIP_LR1110"
|
||||
#endif
|
||||
111
src/kiss.c
Normal file
111
src/kiss.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/* KISS protocol implementation — C99 */
|
||||
#include "kiss.h"
|
||||
|
||||
void kiss_decoder_init(kiss_decoder_t *dec) {
|
||||
dec->state = KISS_STATE_IDLE;
|
||||
dec->len = 0;
|
||||
}
|
||||
|
||||
bool kiss_decode(kiss_decoder_t *dec, uint8_t byte, kiss_frame_t *frame) {
|
||||
switch (dec->state) {
|
||||
case KISS_STATE_IDLE:
|
||||
if (byte == KISS_FEND) {
|
||||
dec->len = 0;
|
||||
dec->state = KISS_STATE_IN_FRAME;
|
||||
}
|
||||
return false;
|
||||
|
||||
case KISS_STATE_IN_FRAME:
|
||||
if (byte == KISS_FESC) {
|
||||
dec->state = KISS_STATE_ESCAPE;
|
||||
return false;
|
||||
}
|
||||
if (byte == KISS_FEND) {
|
||||
if (dec->len > 0) {
|
||||
/* Frame complete */
|
||||
frame->port = dec->buf[0] & 0x0Fu;
|
||||
frame->len = dec->len - 1;
|
||||
if (frame->len > 0) {
|
||||
for (size_t i = 0; i < frame->len; i++) {
|
||||
frame->data[i] = dec->buf[i + 1];
|
||||
}
|
||||
}
|
||||
dec->state = KISS_STATE_IDLE;
|
||||
return true;
|
||||
}
|
||||
dec->state = KISS_STATE_IDLE;
|
||||
return false;
|
||||
}
|
||||
if (dec->len < KISS_MAX_FRAME + 1) {
|
||||
dec->buf[dec->len++] = byte;
|
||||
}
|
||||
return false;
|
||||
|
||||
case KISS_STATE_ESCAPE:
|
||||
if (byte == KISS_TFEND) {
|
||||
byte = KISS_FEND;
|
||||
} else if (byte == KISS_TFESC) {
|
||||
byte = KISS_FESC;
|
||||
}
|
||||
if (dec->len < KISS_MAX_FRAME + 1) {
|
||||
dec->buf[dec->len++] = byte;
|
||||
}
|
||||
dec->state = KISS_STATE_IN_FRAME;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t kiss_encode(uint8_t port, const uint8_t *data, size_t len,
|
||||
uint8_t *dst, size_t dst_cap) {
|
||||
if (dst_cap < len + 3)
|
||||
return 0; /* Need at least: FEND type data... FEND */
|
||||
|
||||
size_t pos = 0;
|
||||
dst[pos++] = KISS_FEND;
|
||||
|
||||
/* Type byte: port in upper nibble, cmd in lower nibble (0 for data) */
|
||||
uint8_t type = (port << 4) | 0x00;
|
||||
if (type == KISS_FEND) {
|
||||
dst[pos++] = KISS_FESC;
|
||||
dst[pos++] = KISS_TFEND;
|
||||
} else if (type == KISS_FESC) {
|
||||
dst[pos++] = KISS_FESC;
|
||||
dst[pos++] = KISS_TFESC;
|
||||
} else {
|
||||
dst[pos++] = type;
|
||||
}
|
||||
|
||||
/* Payload with escaping */
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (pos + 2 > dst_cap)
|
||||
return 0; /* Overflow */
|
||||
if (data[i] == KISS_FEND) {
|
||||
dst[pos++] = KISS_FESC;
|
||||
dst[pos++] = KISS_TFEND;
|
||||
} else if (data[i] == KISS_FESC) {
|
||||
dst[pos++] = KISS_FESC;
|
||||
dst[pos++] = KISS_TFESC;
|
||||
} else {
|
||||
dst[pos++] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (pos + 1 > dst_cap)
|
||||
return 0;
|
||||
dst[pos++] = KISS_FEND;
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t kiss_encode_quality(int8_t snr, int16_t rssi, uint8_t *dst,
|
||||
size_t dst_cap) {
|
||||
if (dst_cap < 5)
|
||||
return 0; /* Need: FEND type snr rssi_hi rssi_lo FEND */
|
||||
|
||||
uint8_t payload[3];
|
||||
payload[0] = (uint8_t)snr;
|
||||
payload[1] = (uint8_t)((rssi >> 8) & 0xFF);
|
||||
payload[2] = (uint8_t)(rssi & 0xFF);
|
||||
|
||||
return kiss_encode(KISS_PORT_QUALITY, payload, 3, dst, dst_cap);
|
||||
}
|
||||
70
src/kiss.h
Normal file
70
src/kiss.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* KISS special bytes */
|
||||
#define KISS_FEND 0xC0u
|
||||
#define KISS_FESC 0xDBu
|
||||
#define KISS_TFEND 0xDCu
|
||||
#define KISS_TFESC 0xDDu
|
||||
|
||||
/* Port assignments */
|
||||
#define KISS_PORT_DATA 0u
|
||||
#define KISS_PORT_QUALITY 1u
|
||||
#define KISS_PORT_CONFIG 2u
|
||||
|
||||
/* Configuration command opcodes (port 2) */
|
||||
#define KISS_CMD_RESERVED 0x00u
|
||||
#define KISS_CMD_RES_OK 0x01u
|
||||
#define KISS_CMD_RES_ERROR 0x02u
|
||||
#define KISS_CMD_GET_RADIO 0x10u
|
||||
#define KISS_CMD_SET_RADIO 0x11u
|
||||
#define KISS_CMD_GET_FREQ 0x12u
|
||||
#define KISS_CMD_SET_FREQ 0x13u
|
||||
#define KISS_CMD_GET_BW 0x14u
|
||||
#define KISS_CMD_SET_BW 0x15u
|
||||
#define KISS_CMD_GET_SF 0x16u
|
||||
#define KISS_CMD_SET_SF 0x17u
|
||||
#define KISS_CMD_GET_CR 0x18u
|
||||
#define KISS_CMD_SET_CR 0x19u
|
||||
#define KISS_CMD_GET_POWER 0x1Au
|
||||
#define KISS_CMD_SET_POWER 0x1Bu
|
||||
#define KISS_CMD_GET_SYNCWORD 0x1Cu
|
||||
#define KISS_CMD_SET_SYNCWORD 0x1Du
|
||||
|
||||
#define KISS_MAX_FRAME 256u
|
||||
|
||||
typedef struct {
|
||||
uint8_t port;
|
||||
uint8_t data[KISS_MAX_FRAME];
|
||||
uint16_t len;
|
||||
} kiss_frame_t;
|
||||
|
||||
typedef enum {
|
||||
KISS_STATE_IDLE,
|
||||
KISS_STATE_IN_FRAME,
|
||||
KISS_STATE_ESCAPE,
|
||||
} kiss_state_t;
|
||||
|
||||
typedef struct {
|
||||
kiss_state_t state;
|
||||
uint8_t buf[KISS_MAX_FRAME + 1u]; /* +1 for type byte */
|
||||
uint16_t len;
|
||||
} kiss_decoder_t;
|
||||
|
||||
void kiss_decoder_init(kiss_decoder_t *dec);
|
||||
|
||||
/* Feed one byte into the decoder. Returns true when a complete frame is
|
||||
ready in *frame. frame must not be NULL when return value is checked. */
|
||||
bool kiss_decode(kiss_decoder_t *dec, uint8_t byte, kiss_frame_t *frame);
|
||||
|
||||
/* Encode port+data into a KISS frame. Returns bytes written, or 0 on
|
||||
overflow. */
|
||||
size_t kiss_encode(uint8_t port, const uint8_t *data, size_t len,
|
||||
uint8_t *dst, size_t dst_cap);
|
||||
|
||||
/* Encode a 3-byte signal quality frame for port 1. Big-endian: int8 snr,
|
||||
int16 rssi. */
|
||||
size_t kiss_encode_quality(int8_t snr, int16_t rssi, uint8_t *dst,
|
||||
size_t dst_cap);
|
||||
48
src/main.cpp
Normal file
48
src/main.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "kiss.h"
|
||||
#include "radio.h"
|
||||
|
||||
static kiss_decoder_t rx_decoder; /* host → modem (serial in) */
|
||||
static uint8_t tx_buf[KISS_MAX_FRAME];
|
||||
static uint8_t radio_buf[KISS_MAX_FRAME];
|
||||
|
||||
void setup() {
|
||||
Serial.begin(KISS_BAUD);
|
||||
kiss_decoder_init(&rx_decoder);
|
||||
radio_init();
|
||||
radio_rx_start();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
kiss_frame_t frame;
|
||||
|
||||
/* Serial → radio: decode incoming KISS frames and transmit */
|
||||
while (Serial.available()) {
|
||||
if (kiss_decode(&rx_decoder, (uint8_t)Serial.read(), &frame)) {
|
||||
if (frame.port == KISS_PORT_DATA) {
|
||||
radio_tx(frame.data, frame.len);
|
||||
}
|
||||
/* port 2 config handling goes here */
|
||||
}
|
||||
}
|
||||
|
||||
/* Radio → serial: forward received packets as KISS frames */
|
||||
if (radio_rx_available()) {
|
||||
radio_rx_info_t info;
|
||||
int n =
|
||||
radio_rx_read(radio_buf, sizeof(radio_buf), &info);
|
||||
if (n > 0) {
|
||||
/* Port 1: signal quality */
|
||||
size_t qlen = kiss_encode_quality(info.snr, info.rssi, tx_buf,
|
||||
sizeof(tx_buf));
|
||||
Serial.write(tx_buf, qlen);
|
||||
|
||||
/* Port 0: raw packet */
|
||||
size_t dlen = kiss_encode(KISS_PORT_DATA, radio_buf, (size_t)n,
|
||||
tx_buf, sizeof(tx_buf));
|
||||
Serial.write(tx_buf, dlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/radio.cpp
Normal file
106
src/radio.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "radio.h"
|
||||
#include "config.h"
|
||||
#include <RadioLib.h>
|
||||
|
||||
/* ── Chip instantiation ─────────────────────────────────────────────────── */
|
||||
#if defined(LORA_CHIP_SX1262)
|
||||
static SX1262 radio(new Module(LORA_PIN_NSS, LORA_PIN_DIO1, LORA_PIN_RESET,
|
||||
LORA_PIN_BUSY));
|
||||
#elif defined(LORA_CHIP_LR1110)
|
||||
static LR1110 radio(new Module(LORA_PIN_NSS, LORA_PIN_DIO1, LORA_PIN_RESET,
|
||||
LORA_PIN_BUSY));
|
||||
#elif defined(LORA_CHIP_SX1276)
|
||||
static SX1276 radio(new Module(LORA_PIN_NSS, LORA_PIN_DIO0, LORA_PIN_RESET,
|
||||
RADIOLIB_NC));
|
||||
#else
|
||||
# error "No LoRa chip defined"
|
||||
#endif
|
||||
|
||||
static radio_config_t current_cfg;
|
||||
|
||||
/* ── Chip-specific overloads ─────────────────────────────────────────────
|
||||
Use C++ overloading to isolate per-chip API deviations. */
|
||||
|
||||
#if defined(LORA_CHIP_SX1262)
|
||||
static int16_t chip_begin(SX1262 &r, const radio_config_t &cfg) {
|
||||
return r.begin(cfg.freq_khz / 1000.0f, cfg.bw_hz / 1000.0f, cfg.sf,
|
||||
cfg.cr, cfg.syncword, cfg.power_dbm);
|
||||
}
|
||||
#elif defined(LORA_CHIP_LR1110)
|
||||
static int16_t chip_begin(LR1110 &r, const radio_config_t &cfg) {
|
||||
return r.begin(cfg.freq_khz / 1000.0f, cfg.bw_hz / 1000.0f, cfg.sf,
|
||||
cfg.cr, cfg.syncword, cfg.power_dbm);
|
||||
}
|
||||
#elif defined(LORA_CHIP_SX1276)
|
||||
static int16_t chip_begin(SX1276 &r, const radio_config_t &cfg) {
|
||||
return r.begin(cfg.freq_khz / 1000.0f, cfg.bw_hz / 1000.0f, cfg.sf,
|
||||
cfg.cr, cfg.syncword, cfg.power_dbm);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ── C API implementation ────────────────────────────────────────────────── */
|
||||
|
||||
int radio_init(void) {
|
||||
current_cfg = {
|
||||
.freq_khz = LORA_FREQ_KHZ,
|
||||
.bw_hz = LORA_BW_HZ,
|
||||
.sf = LORA_SF,
|
||||
.cr = LORA_CR,
|
||||
.power_dbm = LORA_POWER_DBM,
|
||||
.syncword = LORA_SYNCWORD,
|
||||
};
|
||||
int16_t err = chip_begin(radio, current_cfg);
|
||||
return (err == RADIOLIB_ERR_NONE) ? 0 : (int)err;
|
||||
}
|
||||
|
||||
int radio_tx(const uint8_t *data, size_t len) {
|
||||
int16_t err = radio.transmit(data, len);
|
||||
if (err == RADIOLIB_ERR_NONE) {
|
||||
radio.startReceive();
|
||||
}
|
||||
return (err == RADIOLIB_ERR_NONE) ? 0 : (int)err;
|
||||
}
|
||||
|
||||
int radio_rx_start(void) {
|
||||
int16_t err = radio.startReceive();
|
||||
return (err == RADIOLIB_ERR_NONE) ? 0 : (int)err;
|
||||
}
|
||||
|
||||
bool radio_rx_available(void) {
|
||||
return radio.available();
|
||||
}
|
||||
|
||||
int radio_rx_read(uint8_t *buf, size_t buf_cap, radio_rx_info_t *info) {
|
||||
int16_t err = radio.readData(buf, (size_t)buf_cap);
|
||||
if (err != RADIOLIB_ERR_NONE)
|
||||
return (int)err;
|
||||
if (info) {
|
||||
info->snr = (int8_t)radio.getSNR();
|
||||
info->rssi = (int16_t)radio.getRSSI();
|
||||
}
|
||||
radio.startReceive();
|
||||
return (int)radio.getPacketLength();
|
||||
}
|
||||
|
||||
int radio_set_config(const radio_config_t *cfg) {
|
||||
int16_t err;
|
||||
if ((err = radio.setFrequency(cfg->freq_khz / 1000.0f)) !=
|
||||
RADIOLIB_ERR_NONE)
|
||||
return err;
|
||||
if ((err = radio.setBandwidth(cfg->bw_hz / 1000.0f)) != RADIOLIB_ERR_NONE)
|
||||
return err;
|
||||
if ((err = radio.setSpreadingFactor(cfg->sf)) != RADIOLIB_ERR_NONE)
|
||||
return err;
|
||||
if ((err = radio.setCodingRate(cfg->cr)) != RADIOLIB_ERR_NONE)
|
||||
return err;
|
||||
if ((err = radio.setOutputPower(cfg->power_dbm)) != RADIOLIB_ERR_NONE)
|
||||
return err;
|
||||
if ((err = radio.setSyncWord(cfg->syncword)) != RADIOLIB_ERR_NONE)
|
||||
return err;
|
||||
current_cfg = *cfg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void radio_get_config(radio_config_t *cfg) {
|
||||
*cfg = current_cfg;
|
||||
}
|
||||
48
src/radio.h
Normal file
48
src/radio.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t freq_khz;
|
||||
uint32_t bw_hz;
|
||||
uint8_t sf;
|
||||
uint8_t cr;
|
||||
int8_t power_dbm;
|
||||
uint8_t syncword;
|
||||
} radio_config_t;
|
||||
|
||||
typedef struct {
|
||||
int8_t snr;
|
||||
int16_t rssi;
|
||||
} radio_rx_info_t;
|
||||
|
||||
/* Initialise radio using config.h macro defaults. Returns 0 on success. */
|
||||
int radio_init(void);
|
||||
|
||||
/* Transmit packet. Blocks until TX complete. Returns 0 on success. */
|
||||
int radio_tx(const uint8_t *data, size_t len);
|
||||
|
||||
/* Enter continuous RX mode. Returns 0 on success. */
|
||||
int radio_rx_start(void);
|
||||
|
||||
/* True if a packet is waiting in the RX buffer. */
|
||||
bool radio_rx_available(void);
|
||||
|
||||
/* Read received packet into buf. Fills *info if non-NULL.
|
||||
Returns byte count or negative error code. */
|
||||
int radio_rx_read(uint8_t *buf, size_t buf_cap, radio_rx_info_t *info);
|
||||
|
||||
/* Apply new configuration. Returns 0 on success. */
|
||||
int radio_set_config(const radio_config_t *cfg);
|
||||
|
||||
/* Copy current configuration into *cfg. */
|
||||
void radio_get_config(radio_config_t *cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user