Skip to content

Add MeshPager X2 support - #11255

Draft
mverch67 wants to merge 1 commit into
developfrom
mesh-pager-x2
Draft

Add MeshPager X2 support#11255
mverch67 wants to merge 1 commit into
developfrom
mesh-pager-x2

Conversation

@mverch67

@mverch67 mverch67 commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator
image
  • ST7789P3 TFT display
  • LR2021
  • AG3335MV/B ultra-low-power, dual-frequency GNSS
  • SDIO SD card
  • ES8311 I2S sound
  • TCA6424 IO expander
  • ES7243E audio DAC (mic)
  • ADC battery
  • AW35615 programmable USB Type-C PD controller
  • SHT4X temp + humidity
  • BMM350 3-axis magnometer
  • LSM6DSO 6-axis IMU
  • YSN8900E RTC
  • button navigation (via io expander)
    • full MUI support
    • full BaseUI support

🤝 Attestations

  • I have tested that my proposed changes behave as described.
  • I have tested that my proposed changes do not cause any obvious regressions on the following devices:
    • Heltec (Lora32) V3
    • LilyGo T-Deck
    • LilyGo T-Beam
    • RAK WisBlock 4631
    • Seeed Studio T-1000E tracker card
    • Other (please specify below)

Summary by CodeRabbit

  • New Features
    • Added support for the Seeed MeshPager X2 hardware platform.
    • Added board configuration for ESP32-S3, including Wi-Fi, Bluetooth, USB, debugging, and high-speed uploads.
    • Enabled hardware integration for the display, touch controls, LoRa radio, GPS, audio, sensors, battery monitoring, and power management.
    • Added automatic peripheral startup, reset, and power sequencing.
    • Bluetooth is disabled by default on this board.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Seeed MeshPager X2 ESP32-S3 board support, including PlatformIO configuration, pin and hardware definitions, TCA6424-controlled startup sequencing, audio initialization, LR2021 radio support, and a Bluetooth default override.

Changes

MeshPager X2 hardware support

Layer / File(s) Summary
Board contract and build wiring
boards/seeed_meshpager_x2.json, variants/esp32s3/seeed_mesh_pager_x2/*
Defines board metadata, ESP32-S3 build settings, pin mappings, peripheral feature macros, PlatformIO flags, source filtering, and required libraries.
Peripheral startup and codec initialization
variants/esp32s3/seeed_mesh_pager_x2/variant.cpp
Adds TCA6424 register handling, display/GNSS power and reset sequencing, and conditional ES8311 codec initialization.
Radio reset and board defaults
variants/esp32s3/seeed_mesh_pager_x2/rfswitch.h, variants/esp32s3/seeed_mesh_pager_x2/variant.cpp, src/mesh/NodeDB.cpp
Adds LR2021 RF-switch modes, expander-controlled radio reset, and MeshPager X2 selection in the TFT-board Bluetooth default branch.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Firmware
  participant TCA6424
  participant ES8311
  participant LR2021
  Firmware->>TCA6424: initialize expander and configure hardware
  TCA6424-->>Firmware: initialization status
  Firmware->>TCA6424: sequence display and GNSS power/reset
  Firmware->>ES8311: initialize audio codec when ready
  Firmware->>TCA6424: pulse LoRa reset
  TCA6424->>LR2021: drive reset line
  Firmware->>LR2021: apply RF-switch mode configuration
Loading

Possibly related PRs

Suggested labels: hardware-support, needs-review

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: adding MeshPager X2 support.
Description check ✅ Passed The description follows the template with a feature checklist and attestations, though the testing boxes remain unchecked.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mesh-pager-x2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@variants/esp32s3/seeed_mesh_pager_x2/platformio.ini`:
- Line 2: Update the custom_meshtastic_hw_model setting for the MeshPager X2 to
an unassigned hardware model ID that is not 100 or otherwise already reserved,
and remove the TODO comment. Keep the change limited to this platform
configuration.

In `@variants/esp32s3/seeed_mesh_pager_x2/variant.cpp`:
- Around line 117-120: Update configureOutput() to call
ioExpander.digitalWrite(pin, level) before ioExpander.pinMode(pin, true), while
preserving short-circuit failure handling. Ensure EXP_PIN_PWR_HOLD receives its
requested latch level before the pin is enabled as an output.

In `@variants/esp32s3/seeed_mesh_pager_x2/variant.h`:
- Around line 139-146: Remove the HAS_ES7243E, HAS_BMM350, HAS_SHT4X, and
HAS_LSM6DSO feature definitions from the variant configuration, including the
associated ES7243E_I2C_ADDR definition if it is only used by the unsupported
device. Leave unsupported peripherals disabled until their initialization and
validation are implemented.
- Around line 40-47: The battery ADC and power-save declarations in
variants/esp32s3/seeed_mesh_pager_x2/variant.h (lines 40-47) must be removed
until battery support is implemented and validated. In
variants/esp32s3/seeed_mesh_pager_x2/variant.cpp (lines 124-152), do not rely on
expander initialization in the variant setup to make the ADC available to
pre-earlyInitVariant() power checks; this site requires no direct change if
removing the declarations eliminates those checks.
- Around line 3-13: Replace the multi-line board banner in variant.h with a
concise one- or two-line comment identifying the SenseCAP MeshPager X2 and its
ESP32-S3 target, AG3335MV/B GNSS, and LR2021 radio; remove the inaccurate
peripheral and implementation details.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: daee657f-5709-47c6-a2d9-a3ad309726c1

📥 Commits

Reviewing files that changed from the base of the PR and between 1e982fa and 0730aaa.

📒 Files selected for processing (7)
  • boards/seeed_meshpager_x2.json
  • src/mesh/NodeDB.cpp
  • variants/esp32s3/seeed_mesh_pager_x2/pins_arduino.h
  • variants/esp32s3/seeed_mesh_pager_x2/platformio.ini
  • variants/esp32s3/seeed_mesh_pager_x2/rfswitch.h
  • variants/esp32s3/seeed_mesh_pager_x2/variant.cpp
  • variants/esp32s3/seeed_mesh_pager_x2/variant.h

@@ -0,0 +1,105 @@
[env:seeed_meshpager_x2]
custom_meshtastic_hw_model = 100 ; TODO: assign a unique number for this hardware model

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n --glob 'platformio.ini' 'custom_meshtastic_hw_model\s*=' .
rg -n 'SEEED_MESHPAGER_X2|HW_MODEL|HardwareModel' .

Repository: meshtastic/firmware

Length of output: 43304


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Target platformio.ini and files =="
git ls-files 'variants/esp32s3/seeed_mesh_pager_x2/*'
cat -n variants/esp32s3/seeed_mesh_pager_x2/platformio.ini

echo
echo "== SEEED_MESHPAGER_X2 definitions/usages =="
rg -n 'SEEED_MESHPAGER_X2|SEEED.*MESHPAGER|MESHPAGER' src variants platforms include 2>/dev/null || true

echo
echo "== HardwareModel enum names/values around SEEED entries and near 100/106/137 =="
python3 - <<'PY'
from pathlib import Path
p=Path('src/mesh/generated/meshtastic/mesh.pb.h')
text=p.read_text()
for name in ['SEEED_WIO_TRACKER_L1','SEEED_WIO_TRACKER_L1_EINK','SEEED_WIO_TRACKER_L2','MESHPAGER','SEEED_MESHPAGER_X2','SEEED_MESH_PAGER_X2','PRIVATE_HW']:
    print(f'--- {name}')
    idx=text.find(name)
    while idx!=-1:
        start=max(0, text.rfind('\n',0,idx))
        end=text.find('\n', idx+1)
        print(text[start:end])
        idx=text.find(name,end)
PY

echo
echo "== platformio.ini model slugs =="
python3 - <<'PY'
import re
values={}
for path in __import__('pathlib').Path('variants').rglob('platformio.ini'):
    txt=path.read_text()
    m=re.search(r'custom_meshtastic_hw_model\s*=\s*(\d+)', txt)
    if m:
        values.setdefault(int(m.group(1)), []).append(str(path))
for value, paths in sorted(values.items()):
    if value==100:
        print(f'ID 100 used by: {"; ".join(paths)}')
PY

Repository: meshtastic/firmware

Length of output: 6006


Use a unique hardware model ID for MeshPager X2.

100 is already assigned by variants/nrf52840/seeed_wio_tracker_L1_eink/platformio.ini, while SEEED_MESHPAGER_X2 is not reserved in meshtastic_HardwareModel, so this variant would collide with the Wio Tracker L1 eInk build. Pick an unassigned ID and remove the TODO.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@variants/esp32s3/seeed_mesh_pager_x2/platformio.ini` at line 2, Update the
custom_meshtastic_hw_model setting for the MeshPager X2 to an unassigned
hardware model ID that is not 100 or otherwise already reserved, and remove the
TODO comment. Keep the change limited to this platform configuration.

Comment on lines +117 to +120
bool configureOutput(uint8_t pin, bool level)
{
return ioExpander.pinMode(pin, true) && ioExpander.digitalWrite(pin, level);
}

@coderabbitai coderabbitai Bot Jul 27, 2026

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Program the output latch before enabling output mode.

configureOutput() exposes the expander’s initialized-low latch when it changes a pin to output. For EXP_PIN_PWR_HOLD at line 147, that transient deasserts power hold and can shut the board down during boot. Write the latch first, then change direction.

Proposed fix
 bool configureOutput(uint8_t pin, bool level)
 {
-    return ioExpander.pinMode(pin, true) && ioExpander.digitalWrite(pin, level);
+    return ioExpander.digitalWrite(pin, level) && ioExpander.pinMode(pin, true);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bool configureOutput(uint8_t pin, bool level)
{
return ioExpander.pinMode(pin, true) && ioExpander.digitalWrite(pin, level);
}
bool configureOutput(uint8_t pin, bool level)
{
return ioExpander.digitalWrite(pin, level) && ioExpander.pinMode(pin, true);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@variants/esp32s3/seeed_mesh_pager_x2/variant.cpp` around lines 117 - 120,
Update configureOutput() to call ioExpander.digitalWrite(pin, level) before
ioExpander.pinMode(pin, true), while preserving short-circuit failure handling.
Ensure EXP_PIN_PWR_HOLD receives its requested latch level before the pin is
enabled as an output.

@mverch67 mverch67 Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Arduino 3.3.x requires to first write the pinMode before issueing the digitalWrite command.

https://docs.espressif.com/projects/arduino-esp32/en/latest/api/gpio.html#digitalwrite

For the io expander I don't know the specifics, so I wanted to use same behavior.

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.

🧩 Analysis chain

🤖 web_search executed:

Espressif Arduino-ESP32 3.3.x GPIO digitalWrite documentation pinMode output before digitalWrite

Length of output: 7082


The Comment image shows the MeshPager X2 running a blue Meshtastic status screen with “3 new messages,” “2 of 3 nodes online,” radio metrics, and the labeled physical buttons below the display.

@mverch67, thanks for the link. The Arduino-ESP32 requirement applies to the global ::digitalWrite() GPIO API, but this code calls the locally defined Tca6424::digitalWrite(). That method directly writes the TCA6424 output-latch register over I²C; its pinMode() separately writes the direction/configuration register.

So the Arduino constraint does not apply here. Writing the expander latch first while the pin remains an input is the intended way to avoid exposing its initialized-low latch when EXP_PIN_PWR_HOLD becomes an output. The original finding remains valid.

🐇💭

Comment on lines +3 to +13
/**
* SenseCAP MeshPager X2 - ESP32-S3 LoRa/GNSS/Audio Pager
*
* Target: ESP32-S3
* Display: ST7789P3 LCD 240×320 RGB565 (SPI)
* Radio: LR20xx wideband (LR1120/LR1121)
* Audio: ES8311 DAC + ES7243E ADC
* GNSS: L76K UART
* Storage: SD card (1-bit SDIO mode)
* IO Expander: TCA6424
*/

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct and shorten the board banner.

It identifies an L76K and generic LR20xx radio, while this PR targets AG3335MV/B and LR2021. Keep this comment accurate and concise. As per coding guidelines, “Keep code comments minimal—one or two lines maximum—and comment only when the reason is not obvious; do not restate straightforward code or add multi-paragraph explanatory blocks.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@variants/esp32s3/seeed_mesh_pager_x2/variant.h` around lines 3 - 13, Replace
the multi-line board banner in variant.h with a concise one- or two-line comment
identifying the SenseCAP MeshPager X2 and its ESP32-S3 target, AG3335MV/B GNSS,
and LR2021 radio; remove the inaccurate peripheral and implementation details.

Source: Coding guidelines

Comment on lines +40 to +47
// Battery & Power
#define BATTERY_PIN 1 // GPIO 1, ADC for VBAT with 2:1 divider
#define ADC_CHANNEL ADC_CHANNEL_0 // ADC1_GPIO1_CHANNEL
#define ADC_MULTIPLIER 2.0 * 1.045 // 2.0 divider + 4.5% correction
#define ADC_ATTEN ADC_ATTEN_DB_12

#define USE_POWERSAVE
#define SLEEP_TIME 120

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.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Do not advertise battery power management before its ADC path is usable.

The PR states battery ADC support is incomplete, yet the board declares BATTERY_PIN and USE_POWERSAVE. src/main.cpp runs powerHAL_init() and waitUntilPowerLevelSafe() before earlyInitVariant(), so the expander-controlled ADC path cannot be configured before power safety logic reads it. This can report invalid voltage or block boot.

  • variants/esp32s3/seeed_mesh_pager_x2/variant.h#L40-L47: remove battery ADC/power-save declarations until battery support is implemented and validated.
  • variants/esp32s3/seeed_mesh_pager_x2/variant.cpp#L124-L152: do not rely on expander setup here to make the ADC valid for pre-earlyInitVariant() power checks.
📍 Affects 2 files
  • variants/esp32s3/seeed_mesh_pager_x2/variant.h#L40-L47 (this comment)
  • variants/esp32s3/seeed_mesh_pager_x2/variant.cpp#L124-L152
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@variants/esp32s3/seeed_mesh_pager_x2/variant.h` around lines 40 - 47, The
battery ADC and power-save declarations in
variants/esp32s3/seeed_mesh_pager_x2/variant.h (lines 40-47) must be removed
until battery support is implemented and validated. In
variants/esp32s3/seeed_mesh_pager_x2/variant.cpp (lines 124-152), do not rely on
expander initialization in the variant setup to make the ADC available to
pre-earlyInitVariant() power checks; this site requires no direct change if
removing the declarations eliminates those checks.

Comment on lines +139 to +146
#define HAS_ES7243E
#define ES7243E_I2C_ADDR 0x14 /* I2C1 address for ES7243E ADC
Note: May conflict with BMM350 sensor in some configs */

// Sensors - I2C1
#define HAS_BMM350 /* Magnetometer on I2C1 */
#define HAS_SHT4X /* Temperature/Humidity on I2C1 */
#define HAS_LSM6DSO /* IMU (Accel/Gyro) on I2C1 */

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not enable unsupported peripherals.

HAS_ES7243E, HAS_BMM350, HAS_SHT4X, and HAS_LSM6DSO advertise devices that the PR explicitly lists as incomplete. Remove these flags until their initialization and validation are implemented; otherwise generic subsystem setup may attempt to use unavailable hardware.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@variants/esp32s3/seeed_mesh_pager_x2/variant.h` around lines 139 - 146,
Remove the HAS_ES7243E, HAS_BMM350, HAS_SHT4X, and HAS_LSM6DSO feature
definitions from the variant configuration, including the associated
ES7243E_I2C_ADDR definition if it is only used by the unsupported device. Leave
unsupported peripherals disabled until their initialization and validation are
implemented.

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Flash this PR in the Web Flasher

firmware commit boards expires

Warning

This is an automated, unreviewed CI test build. Back up your device configuration
before flashing, and only flash devices you are able to recover.

Supported boards built by this PR (32)
Device Board Platform
Crowpanel Adv 3.5 TFT elecrow-adv-35-tft esp32-s3
Heltec HT62 heltec-ht62-esp32c3-sx1262 esp32-c3
Heltec Mesh Node 096 heltec-mesh-node-t096 nrf52840
Heltec Mesh Node T1 heltec-mesh-node-t1 nrf52840
Heltec Mesh Node T114 heltec-mesh-node-t114 nrf52840
Heltec V3 heltec-v3 esp32-s3
Heltec V4 heltec-v4 esp32-s3
Meshnology W10 meshnology_w10 esp32-s3
Meshnology W12 meshnology_w12 esp32-s3
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
RAK WisMesh Pocket V3 rak_wismesh_pocket nrf52840
RAK WisMesh Pod rak_wismesh_pod nrf52840
RAK WisMesh Repeater Mini V2 rak_wismesh_repeater_mini nrf52840
RAK WisMesh Tag rak_wismeshtag nrf52840
RAK WisBlock 11200 rak11200 esp32
RAK WisBlock 11310 rak11310 rp2040
RAK3312 rak3312 esp32-s3
RAK WisBlock 4631 rak4631 nrf52840
Seeed SenseCAP Mesh-Tracker-X1 seeed_mesh_tracker_X1 nrf52840
Seeed MeshPager X2 seeed_meshpager_x2 esp32s3
Seeed Wio Tracker L1 seeed_wio_tracker_L1 nrf52840
Seeed Xiao NRF52840 Kit seeed_xiao_nrf52840_kit nrf52840
Seeed Xiao ESP32-S3 seeed-xiao-s3 esp32-s3
Station G2 station-g2 esp32-s3
Station G3 station-g3 esp32-s3
LILYGO T-Deck t-deck-tft esp32-s3
LILYGO T-Echo t-echo nrf52840
LILYGO T-Echo Plus t-echo-plus nrf52840
LILYGO T-Impulse Plus t-impulse-plus nrf52840
LilyGo T3-C6 tlora-c6 esp32-c6
Seeed SenseCAP T1000-E tracker-t1000-e nrf52840

Build artifacts expire on 2026-08-26. Updated for 0730aaa.

@mverch67 mverch67 added the hardware-support Hardware related: new devices or modules, problems specific to hardware label Jul 27, 2026
@mverch67
mverch67 marked this pull request as draft July 27, 2026 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hardware-support Hardware related: new devices or modules, problems specific to hardware

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant