Browse Source

Add Lierda AM36 Pico (L-LRMAM36-FANN4-PK02) variant: ESP32-S3 + LR2021

pull/3074/head
Omegaconjecture 11 hours ago
parent
commit
ebd0843a72
  1. 24
      variants/lierda_am36_pico/LierdaAM36Board.h
  2. 90
      variants/lierda_am36_pico/README.md
  3. 100
      variants/lierda_am36_pico/platformio.ini
  4. 34
      variants/lierda_am36_pico/target.cpp
  5. 24
      variants/lierda_am36_pico/target.h

24
variants/lierda_am36_pico/LierdaAM36Board.h

@ -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";
}
};

90
variants/lierda_am36_pico/README.md

@ -0,0 +1,90 @@
# 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) against a live US915 MeshCore 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 channel messaging through a third-party repeater
> (SNR up 11.5 dB / down 13.0 dB, replies received at SNR 3.25 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
variants/lierda_am36_pico/platformio.ini

@ -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
variants/lierda_am36_pico/target.cpp

@ -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
variants/lierda_am36_pico/target.h

@ -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();
Loading…
Cancel
Save