Skip to content

esp32/boards: Add Pycom LoPy and LoPy4 board definitions with SX127x LoRa driver - #19026

Open
ddtdanilo wants to merge 5 commits into
micropython:masterfrom
ddtdanilo:pycom-lora-upstream
Open

esp32/boards: Add Pycom LoPy and LoPy4 board definitions with SX127x LoRa driver#19026
ddtdanilo wants to merge 5 commits into
micropython:masterfrom
ddtdanilo:pycom-lora-upstream

Conversation

@ddtdanilo

@ddtdanilo ddtdanilo commented Apr 2, 2026

Copy link
Copy Markdown

Summary

Add board definitions, LoRa driver, LoRaWAN MAC, OTA support, and hardware validation for Pycom LoPy and LoPy4 — bringing these archived boards back to life on modern MicroPython.

Boards

  • PYCOM_LOPY: ESP32 + 4MB flash + SX1272 LoRa (868 MHz)
  • PYCOM_LOPY4: ESP32 + 8MB flash + 4MB PSRAM + SX1276 LoRa (868/915 MHz)

Changes addressing review feedback

  • LoRa pins in pins.csv: LORA_MOSI, LORA_MISO, LORA_SCLK, LORA_CS, LORA_DIO0, LORA_RESET, NEOPIXEL — eliminates board_config.py
  • Board images: Added board photos (to be submitted to micropython-media separately)
  • Driver location: Currently in pycom_common/ as frozen modules; ready to move to micropython-lib in a follow-up PR (note: upstream lora-sx127x only supports SX1276, our driver also handles SX1272)
  • ruff CI fix: Added missing from micropython import const, removed unused SPI import

Features

  • SX127x driver (pycom_common/sx127x.py): Unified SX1272/SX1276 register-level driver
  • LoRaWAN 1.0.x MAC (pycom_common/lorawan.py): Class A with OTAA/ABP, AES-128 crypto
  • OTA variants: BOARD_VARIANT=OTA for both boards with dual app partitions
  • RGB LED helper (pycom_common/pycom_rgb.py): WS2812 NeoPixel
  • OTA helper (pycom_common/ota.py): Firmware update via esp32.Partition API
  • Hardware validation (pycom_common/test_hardware.py): GPIO, WiFi, BLE, LoRa, PSRAM tests

Structure

ports/esp32/boards/
├── pycom_common/              # Shared frozen modules
│   ├── sx127x.py              # SX1272/SX1276 unified driver
│   ├── lorawan.py             # LoRaWAN 1.0.x MAC (OTAA/ABP, Class A)
│   ├── lorawan_crypto.py      # AES-128-CMAC/CTR for LoRaWAN
│   ├── ota.py                 # OTA firmware update helper
│   ├── pycom_rgb.py           # RGB LED helper
│   └── test_hardware.py       # Hardware validation script
├── PYCOM_LOPY/                # LoPy (SX1272, 4MB flash)
│   ├── pins.csv               # P0-P23 + LORA_* + NEOPIXEL named pins
│   └── ...
└── PYCOM_LOPY4/               # LoPy4 (SX1276, 8MB flash, 4MB PSRAM)
    ├── pins.csv               # P0-P23 + LORA_* + NEOPIXEL named pins
    └── ...

Testing

Build verification (all configurations compile):

  • make BOARD=PYCOM_LOPY — 1.7MB app
  • make BOARD=PYCOM_LOPY4 — 1.5MB app, 24% free
  • make BOARD=PYCOM_LOPY BOARD_VARIANT=OTA
  • make BOARD=PYCOM_LOPY4 BOARD_VARIANT=OTA

Test files in tests/ports/esp32/:

  • pycom_sx127x.py: Driver init, config, TX power, validation (uses named pins)
  • pycom_rgb.py: LED color, off, heartbeat
  • pycom_lorawan_crypto.py: AES-128-CMAC (RFC 4493 vectors), CTR, MIC, keys

All Python files pass ruff check, ruff format, codespell, tools/verifygitlog.py.

Follow-up items

  • Submit board images to micropython/micropython-media
  • Move SX127x driver and LoRaWAN to micropython-lib (separate PR)

@codecov

codecov Bot commented Apr 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.46%. Comparing base (e8a3ee0) to head (9abf3d2).
⚠️ Report is 405 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #19026   +/-   ##
=======================================
  Coverage   98.46%   98.46%           
=======================================
  Files         176      176           
  Lines       22784    22784           
=======================================
  Hits        22435    22435           
  Misses        349      349           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Apr 2, 2026

Copy link
Copy Markdown

Code size report:

Reference:  esp32/esp32_common.cmake: Add missing C flags to user C module sources. [e8a3ee0]
Comparison: tests/ports/esp32: Add tests for Pycom LoRa driver and crypto. [merge of 9abf3d2]
  mpy-cross:    +0 +0.000% 
   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
      esp32:    +0 +0.000% ESP32_GENERIC
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@robert-hh

robert-hh commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

It is clearly a pity that Pycom disappeared and there is not software support any more for the Pycom boards. Therefore boards defintions for Pycom boards may be helpful. For this PR, the file structure should be changed matching the structure of MicroPython.

  • The python driver would find it's home in micropython-lib. Structure to be defined, maybe a separate micropython/pycom directory. There is already a lora-sx127x driver in micropython/lora, but that one may be different.

Some detailed comments:

  • You could add pin definitions for LORA pins to pins.csv. The entries the are not limited to external pins. Then a Pin can be referenced by it's name in the machine.Pin module, and board_config.py is not needed.
  • Add a board image to micropython-media.

Edit: I made a trial build for a LoPy4 board. It builds with ESP-IDF V5.5.1, the firmware works at the board. WiFi connects. I did not try LoRa.

@agatti agatti added port-esp32 board-definition New or updated board definition files. Combine with a port- label. labels Apr 2, 2026
Add a unified SX1272/SX1276 LoRa driver and WS2812 NeoPixel RGB LED
helper as shared frozen modules for Pycom boards. The driver handles
register-level differences between both chip variants including
bandwidth encoding, coding rate bits, CRC location, and RSSI offsets.

Signed-off-by: Danilo D <[email protected]>
Add board definition for the Pycom LoPy (L01 module) with ESP32,
4MB flash, SX1272 LoRa transceiver, and WS2812 NeoPixel RGB LED.

Pin names in pins.csv include P0-P23 user pin aliases and LORA_*
named pins for the SX1272 radio, eliminating the need for a
separate board_config module. Includes OTA variant support.

Signed-off-by: Danilo D <[email protected]>
Add board definition for the Pycom LoPy4 (L04 module) with ESP32,
8MB flash, 4MB PSRAM, SX1276 LoRa transceiver, and WS2812 NeoPixel.

Includes custom 8MB partition table, SPIRAM configuration, OTA
variant with dual app partitions, and LORA_* named pins. The SX1276
has no hardware reset pin so the driver uses soft reset.

Signed-off-by: Danilo D <[email protected]>
Add LoRaWAN 1.0.x MAC layer with OTAA/ABP Class A support and
AES-128-CMAC/CTR crypto built on ucryptolib. Add OTA firmware
update helper using esp32.Partition API. Add hardware validation
script for GPIO, WiFi, BLE, NeoPixel, PSRAM, and LoRa chip testing.

Signed-off-by: Danilo D <[email protected]>
Add hardware tests for SX127x driver (config, TX power, parameter
validation), RGB LED, and LoRaWAN crypto (RFC 4493 CMAC vectors,
CTR round-trip, MIC, key derivation). Tests use named pins from
pins.csv and skip gracefully on non-Pycom boards.

Signed-off-by: Danilo D <[email protected]>
@ddtdanilo
ddtdanilo force-pushed the pycom-lora-upstream branch from 0e2f582 to 9abf3d2 Compare April 2, 2026 14:49
ddtdanilo added a commit to ddtdanilo/micropython-media that referenced this pull request Apr 2, 2026
Add board photos for the Pycom LoPy and LoPy4 development boards
as referenced by the board definitions in micropython/micropython#19026.

Signed-off-by: Danilo D <[email protected]>
ddtdanilo added a commit to ddtdanilo/micropython-lib that referenced this pull request Apr 2, 2026
Add a LoRa radio driver that handles both SX1272 and SX1276
transceivers. The existing lora-sx127x package only supports
SX1276 (version register 0x12); this driver also handles SX1272
(version 0x22) with its different register layouts for bandwidth,
coding rate, CRC, and RSSI calculation.

Used by Pycom LoPy (SX1272) and LoPy4 (SX1276) boards as defined
in micropython/micropython#19026.

Signed-off-by: Danilo D <[email protected]>
ddtdanilo added a commit to ddtdanilo/micropython-lib that referenced this pull request Apr 2, 2026
Add LoRaWAN Class A device operation with OTAA and ABP activation.
Includes AES-128-CMAC/CTR crypto primitives built on ucryptolib for
MIC calculation, payload encryption, and session key derivation per
the LoRaWAN 1.0.x specification.

Used by Pycom LoPy/LoPy4 boards as defined in
micropython/micropython#19026.

Signed-off-by: Danilo D <[email protected]>
@ddtdanilo

Copy link
Copy Markdown
Author

Hi @robert-hh, thank you very much for the review and for taking the time to do a trial build — really appreciated! Great to hear it builds and WiFi works on actual hardware.

I've addressed all your feedback:

LoRa pins in pins.csv — Added LORA_MOSI, LORA_MISO, LORA_SCLK, LORA_CS, LORA_DIO0, LORA_RESET, and NEOPIXEL as named pins. Removed board_config.py entirely. The driver and tests now use Pin("LORA_CS") etc.

Board images — Submitted to micropython-media: micropython/micropython-media#116

Driver to micropython-lib — Submitted as a separate PR: micropython/micropython-lib#1102. It includes two packages:

  • lora-sx127x-pycom: Unified SX1272/SX1276 driver (the existing lora-sx127x only supports SX1276)
  • lora-lorawan: LoRaWAN 1.0.x MAC layer (OTAA/ABP, Class A)

Once those two PRs are merged, I'll update this PR to use require() instead of freeze() and remove the driver files from pycom_common/.

ruff CI — Fixed the missing from micropython import const and unused SPI import.

Also restructured the commits to be cleaner (5 focused commits). Let me know if there's anything else I should adjust!

@ddtdanilo

Copy link
Copy Markdown
Author

Quick status update on the micropython-lib dependencies referenced earlier in this thread.

Per @projectgus's request on micropython/micropython-lib#1102, the LoRa stack is now split into two independently reviewable PRs:

Both are mergeable with all CI checks passing (build, ruff, codespell). Once they land on master, I'll update this PR to switch from freeze() of the modules in pycom_common/ to require() of the lib packages and drop the driver/MAC files from this branch — the change is already prepared locally.

@ddtdanilo

Copy link
Copy Markdown
Author

Hi @robert-hh — quick friendly check-in. The pins.csv + driver-to-lib points from your earlier review are in place, and the lib split landed as #1102 (driver) and #1111 (LoRaWAN MAC). All 44 CI checks have been passing for a while. I have a local follow-up commit ready that flips the manifests from freeze() to require() once the two lib PRs merge. Happy to address anything else when you get a chance — thanks!

@projectgus projectgus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributing this @ddtdanilo, and thanks @robert-hh for the initial review pass.

I have some comments below, but my main concern is about the supporting micropython-lib PRs - they may take a while.

How would you feel about merging minimal board support for now, without the frozen LoRa drivers, and then after we get the drivers in micropython-lib we can easily open a PR to freeze them onto these boards?

In the meantime if you wanted to host the Pycom-specific Python drivers somewhere else then people can still use mpremote mip install to install them onto their boards.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The images can be pulled out now they're submitted in micropython-media

Comment on lines +5 to +15
// LoRa radio (SX1272) pin configuration
#define MICROPY_HW_LORA_MOSI (27)
#define MICROPY_HW_LORA_MISO (19)
#define MICROPY_HW_LORA_SCLK (5)
#define MICROPY_HW_LORA_CS (17)
#define MICROPY_HW_LORA_DIO0 (23)
#define MICROPY_HW_LORA_RESET (18)
#define MICROPY_HW_LORA_CHIP (1272)

// RGB LED (WS2812 NeoPixel)
#define MICROPY_HW_NEOPIXEL_PIN (0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these used anywhere? (Same question for the other board.)

If not then suggest removing.

@@ -0,0 +1,8 @@
set(SDKCONFIG_DEFAULTS
${SDKCONFIG_DEFAULTS}
boards/PYCOM_LOPY/sdkconfig.ota

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
boards/PYCOM_LOPY/sdkconfig.ota
boards/ESP32_GENERIC/sdkconfig.ota

(These files have the same contents, so can remove the duplicate from this PR and reference the original directly.)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest moving this to the micropython-lib LoRaWAN PR (unfortunately micropython-lib tests are a bit more ad hoc, but we do have them!)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the lorawan crypto tests, this can go in a tests directory for the micropython-lib driver

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably too board-specific to go in the esp32 port tests. Not sure if we have a place for a test like this (board-specific and requires manual verification of the LED colours) in MicroPython, but maybe in the board directory? @dpgeorge what do you think?

boards/sdkconfig.ble
boards/PYCOM_LOPY/sdkconfig.board
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've just merged #18873 so this section can be simplified (after rebase) to something like:

include(boards/mpconfigboard_esp32s3_common.cmake)

list(APPEND SDKCONFIG_DEFAULTS
    boards/PYCOM_LOPY/sdkconfig.board)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

board-definition New or updated board definition files. Combine with a port- label. port-esp32

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants