Add MeshPager X2 support - #11255
Conversation
📝 WalkthroughWalkthroughAdds 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. ChangesMeshPager X2 hardware support
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
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (7)
boards/seeed_meshpager_x2.jsonsrc/mesh/NodeDB.cppvariants/esp32s3/seeed_mesh_pager_x2/pins_arduino.hvariants/esp32s3/seeed_mesh_pager_x2/platformio.inivariants/esp32s3/seeed_mesh_pager_x2/rfswitch.hvariants/esp32s3/seeed_mesh_pager_x2/variant.cppvariants/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 | |||
There was a problem hiding this comment.
📐 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)}')
PYRepository: 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.
| bool configureOutput(uint8_t pin, bool level) | ||
| { | ||
| return ioExpander.pinMode(pin, true) && ioExpander.digitalWrite(pin, level); | ||
| } |
There was a problem hiding this comment.
🩺 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.
| 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
🧩 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.
🐇💭
| /** | ||
| * 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 | ||
| */ |
There was a problem hiding this comment.
📐 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
| // 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 |
There was a problem hiding this comment.
🩺 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.
| #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 */ |
There was a problem hiding this comment.
🎯 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.
⚡ Try this PR in the Web FlasherWarning This is an automated, unreviewed CI test build. Back up your device configuration Supported boards built by this PR (32)
Build artifacts expire on 2026-08-26. Updated for |
🤝 Attestations
Summary by CodeRabbit