Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions variants/lierda_am36_pico/LierdaAM36Board.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <Arduino.h>
#include <helpers/ESP32Board.h>

// Lierda AM36 Pico dev board (L-LRMAM36-FANN4-PK02).
// The L-LRMAM36-FANN4 module pairs an ESP32-S3 with a Semtech LR2021.
// Nothing exotic at the board level: USB-C power, no battery divider on the
// dev board. The only LED is a hardwired 3V3 power indicator (no status LED).
class LierdaAM36Board : public ESP32Board {
public:
LierdaAM36Board() { }

void begin() {
ESP32Board::begin();
#ifdef PIN_USER_BTN
pinMode(PIN_USER_BTN, INPUT_PULLUP); // KEY, pressed = LOW
#endif
}

const char* getManufacturerName() const override {
return "Lierda AM36 Pico";
}
};
94 changes: 94 additions & 0 deletions variants/lierda_am36_pico/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# MeshCore on the Lierda AM36 Pico (L-LRMAM36-FANN4-PK02)

Community variant for the Lierda AM36 Pico evaluation board. The on-board
L-LRMAM36-FANN4 module is an **ESP32-S3** (8MB flash / 8MB PSRAM) paired with a
**Semtech LR2021** Generation-4 LoRa transceiver.

> Status: **hardware-validated** (Jul 2026) on a live mesh: radio init, noise
> floor sampling (~-119 dBm), BLE pairing with the iOS companion app, flood +
> direct TX with interrupt-driven send-complete, and two-way messaging with a
> Heltec Tracker v2 (SX1262) peer - i.e. confirmed LR2021 <-> SX1262 interop -
> at 927.875 MHz / BW 62.5 / SF9 / CR7, both nodes using muzi works 17 cm whip
> antennas (replies at SNR 3.25 dB). Also received channel messages from six
> third-party senders at 3-9 hops with full path reconstruction, and measured a
> third-party repeater (not operated by the author) at
> up 11.5 dB / down 13.0 dB.
> Requires the `CustomLR2021` radio classes from MeshCore PR #2944.

## Hardware facts used by this variant

| Signal | ESP32-S3 GPIO | Source | Verified |
|---|---|---|---|
| LR2021 SPI SCK | 40 | `esp32_lora_driver` Kconfig (`LIERDA_BOARD_LRMAM36_FANN4`) | yes (on air) |
| LR2021 SPI MOSI | 41 | same | yes |
| LR2021 SPI MISO | 42 | same | yes |
| LR2021 NSS | 39 | same | yes |
| LR2021 NRST | 38 | same | yes |
| LR2021 BUSY | 17 | same | yes |
| LR2021 IRQ | 21 - **wired to LR2021 DIO7** | Kconfig `LR20XX_DIO7_GPIO` | yes (TX-done + RX interrupts) |
| KEY button | 4 (schematic net `GPIO4/KEY1`) | schematic | **not yet** - left disabled |

The IRQ DIO matters: the Seeed Wio-LR2021 (which PR #2944 targets) uses DIO8,
so this variant sets `-D LR2021_IRQ_DIO=7`. No external TCXO or RF-switch
GPIOs exist on the MCU side - the module handles RF internally. The board's
only LED is a hardwired 3V3 power indicator, so no `PIN_STATUS_LED` is defined.

## Build

1. Install VS Code + the PlatformIO extension.
2. Get the source with the LR2021 driver:
```bash
git clone https://github.com/meshcore-dev/MeshCore
cd MeshCore
git fetch origin pull/2944/head:lr2021
git checkout lr2021
```
(Once PR #2944 is merged, plain `dev` works.)
3. Copy this `lierda_am36_pico/` folder into `variants/`.
(PlatformIO auto-discovers `variants/*/platformio.ini`.)
4. Radio parameters: builds inherit MeshCore's defaults; set your region's
frequency/BW/SF/CR from the companion app after first boot (settings are
persisted on the device), or override `LORA_*` flags in a
`platformio.local.ini` for your own builds. The module covers 863-930 MHz.
5. Build:
```bash
pio run -e Lierda_AM36_Pico_companion_radio_ble
```
Other envs: `..._companion_radio_usb`, `..._repeater`.
Note: the iOS companion app connects over BLE only, so iPhone users want
the `_ble` build.

## Flash

1. Connect the Type-C port labelled **USB** (NOT the one labelled UART - that
goes to the CP2105 bridge). The USB port is the ESP32-S3's native
USB-JTAG/CDC and is what esptool talks to.
2. ```bash
pio run -e Lierda_AM36_Pico_companion_radio_ble -t upload
```
3. If the port doesn't enumerate or upload fails to sync: hold **BOOT**, tap
**RST**, release **BOOT** (forces download mode), then retry the upload.
4. Tap **RST** after flashing completes.

## Verify

- Serial console is on the same USB port
(`pio device monitor -e Lierda_AM36_Pico_companion_radio_ble`). Note the
ESP32-S3 re-enumerates USB on reset, so the monitor may need a restart after
tapping RST. A failed radio bring-up prints
`ERROR: LR2021 init failed: <code>` and halts (no BLE advertising).
- **Attach the LoRa antenna before any TX.**
- Pair from the MeshCore app over Bluetooth (default PIN `123456`, set via
`BLE_PIN_CODE`), set your region's radio parameters in the app, and send a
channel message or advert.

## Known quirks / notes

- SPI to the LR2021 sits on GPIO39-42, which are the S3's JTAG pins. That's
fine (flashing/debug uses USB-JTAG), just don't try external JTAG.
- PSRAM is present but left disabled - MeshCore doesn't need it, and octal
PSRAM misconfiguration is a classic S3 boot-loop source.
- The KEY button (schematic net `GPIO4/KEY1`) is left disabled until traced
on hardware; uncomment `PIN_USER_BTN` in `platformio.ini` once confirmed.
- PR #2944's wrapper drops the radio to standby before every `startReceive()`
(LR2021 wedges if re-armed mid-RX) - don't "optimize" that away.
100 changes: 100 additions & 0 deletions variants/lierda_am36_pico/platformio.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
; Lierda AM36 Pico (L-LRMAM36-FANN4-PK02)
; Module: L-LRMAM36-FANN4 = ESP32-S3 (8MB flash / 8MB PSRAM) + Semtech LR2021
;
; Pin map taken from lierda-iot/esp32_lora_driver Kconfig defaults for
; LIERDA_BOARD_LRMAM36_FANN4 and verified against the board schematic
; (L-LRMAM36-FANN4-PK02_SCH_V01.pdf):
; LR2021 SPI: SCK=40 MOSI=41 MISO=42 NSS=39
; LR2021 ctrl: NRST=38 BUSY=17 IRQ=GPIO21 (wired to LR2021 DIO7!)
; KEY button: GPIO4 per schematic net label (not yet verified on hardware)
; LED1: hardwired 3V3 power indicator (not GPIO-controllable)
;
; Requires the CustomLR2021 radio classes from MeshCore PR #2944.
; Flash via the board's "USB" Type-C port (ESP32-S3 native USB), not "UART".

[Lierda_AM36_Pico]
extends = esp32_base
board = esp32-s3-devkitc-1
board_build.mcu = esp32s3
board_build.flash_size = 8MB
board_upload.flash_size = 8MB
build_flags = ${esp32_base.build_flags}
-I variants/lierda_am36_pico
-D LIERDA_AM36_PICO
; --- native-USB serial console on the "USB" Type-C port ---
-D ARDUINO_USB_MODE=1
-D ARDUINO_USB_CDC_ON_BOOT=1
; --- LR2021 wiring (ESP32-S3 GPIO numbers) ---
-D P_LORA_SCLK=40
-D P_LORA_MOSI=41
-D P_LORA_MISO=42
-D P_LORA_NSS=39
-D P_LORA_RESET=38
-D P_LORA_BUSY=17
-D P_LORA_DIO_1=21
; host IRQ line is routed from the LR2021's DIO7 (PR default is DIO8 for
; the Seeed Wio-LR2021 - this override is essential on the Lierda module)
-D LR2021_IRQ_DIO=7
; --- radio driver selection ---
-D RADIO_CLASS=CustomLR2021
-D WRAPPER_CLASS=CustomLR2021Wrapper
-D LORA_TX_POWER=22
; --- board UI ---
; KEY button is believed to be GPIO4 (schematic net GPIO4/KEY1) but has not
; been traced/verified on hardware yet; uncomment once confirmed:
; -D PIN_USER_BTN=4
; The board's only LED (LED1) is a hardwired 3V3 power indicator - there is
; no GPIO-controllable status LED, so PIN_STATUS_LED is not defined.
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/lierda_am36_pico>
+<helpers/ui/MomentaryButton.cpp>
lib_deps =
${esp32_base.lib_deps}

; ---------------- Companion (phone app over the USB port) ----------------
[env:Lierda_AM36_Pico_companion_radio_usb]
extends = Lierda_AM36_Pico
build_flags =
${Lierda_AM36_Pico.build_flags}
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Lierda_AM36_Pico.build_src_filter}
+<../examples/companion_radio/*.cpp>
lib_deps =
${Lierda_AM36_Pico.lib_deps}
densaugeo/base64 @ ~1.4.0

; ---------------- Companion over BLE ----------------
[env:Lierda_AM36_Pico_companion_radio_ble]
extends = Lierda_AM36_Pico
build_flags =
${Lierda_AM36_Pico.build_flags}
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D BLE_PIN_CODE=123456
-D OFFLINE_QUEUE_SIZE=256
build_src_filter = ${Lierda_AM36_Pico.build_src_filter}
+<helpers/esp32/*.cpp>
+<../examples/companion_radio/*.cpp>
lib_deps =
${Lierda_AM36_Pico.lib_deps}
densaugeo/base64 @ ~1.4.0

; ---------------- Repeater ----------------
[env:Lierda_AM36_Pico_repeater]
extends = Lierda_AM36_Pico
build_src_filter = ${Lierda_AM36_Pico.build_src_filter}
+<../examples/simple_repeater/*.cpp>
build_flags =
${Lierda_AM36_Pico.build_flags}
-D ADVERT_NAME='"AM36 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
lib_deps =
${Lierda_AM36_Pico.lib_deps}
${esp32_ota.lib_deps}
34 changes: 34 additions & 0 deletions variants/lierda_am36_pico/target.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <Arduino.h>
#include "target.h"

#include <helpers/ArduinoHelpers.h>

LierdaAM36Board board;

// LR2021 on dedicated SPI pins (module-internal wiring; see platformio.ini)
static SPIClass spi;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);

WRAPPER_CLASS radio_driver(radio, board);

ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
SensorManager sensors; // no-op stub - dev board has no on-board sensors

#ifdef PIN_USER_BTN
MomentaryButton user_btn(PIN_USER_BTN, 1000, true); // KEY, active low
#endif

bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire);

// CustomLR2021::std_init() routes the IRQ to DIO7 (LR2021_IRQ_DIO),
// brings up SPI on P_LORA_SCLK/MISO/MOSI, and applies LORA_* defaults.
return radio.std_init(&spi);
}

mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}
24 changes: 24 additions & 0 deletions variants/lierda_am36_pico/target.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/radiolib/RadioLibWrappers.h>
#include <helpers/ESP32Board.h>
// CustomLR2021 / CustomLR2021Wrapper come from MeshCore PR #2944
#include <helpers/radiolib/CustomLR2021Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
#include <helpers/SensorManager.h>
#include <helpers/ui/MomentaryButton.h>
#include "LierdaAM36Board.h"

extern LierdaAM36Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
extern SensorManager sensors;

#ifdef PIN_USER_BTN
extern MomentaryButton user_btn;
#endif

bool radio_init();
mesh::LocalIdentity radio_new_identity();