mirror of https://github.com/meshcore-dev/MeshCore
5 changed files with 272 additions and 0 deletions
@ -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"; |
|||
} |
|||
}; |
|||
@ -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. |
|||
@ -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} |
|||
@ -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
|
|||
} |
|||
@ -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…
Reference in new issue