Skip to content

Add T-Beam BPF - 144-148 Mhz LoRa @ ~37 dBm (5 Watts)#10558

Open
vidplace7 wants to merge 48 commits into
developfrom
t-beam-bpf
Open

Add T-Beam BPF - 144-148 Mhz LoRa @ ~37 dBm (5 Watts)#10558
vidplace7 wants to merge 48 commits into
developfrom
t-beam-bpf

Conversation

@vidplace7

@vidplace7 vidplace7 commented May 26, 2026

Copy link
Copy Markdown
Member

Add LilyGo T-Beam BPF 144-148Mhz LoRa node 🍖

This node contains a +27 dBm PA! 🔥
Observed RF output power measurements:

  • 38.4 dBm (6.9 Watts) USB 5V
  • 37.0 dBm (5 Watts) Battery power (4.2V full charge)

Requires:

This is a continuation of original work located at:

Summary by CodeRabbit

  • New Features
    • Added support for the LilyGo T-Beam-BPF hardware variant, including board setup, pin mapping, and device-specific build configuration.
    • Enabled display, GPS, SD card, LoRa, and power-management settings for the new variant.
  • Bug Fixes
    • Updated power handling to properly manage unused rails on the new hardware.
    • Ensured the radio power-enable pin is driven correctly during startup and shutdown.
  • Style
    • Limited LoRa region choices for 2m-only builds and marked the device as licensed by default in that mode.

vidplace7 and others added 21 commits February 20, 2026 21:13
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This is an appropriate default in the USA but not the EU.

The slot override really should follow the region itself, not the regionprofile.
Initial work to add T-Beam BPF (144-148 Mhz LoRa)
@vidplace7 vidplace7 added hardware-support Hardware related: new devices or modules, problems specific to hardware triaged Reviewed by the team, has enough information and ready to work on now. 2.8 needs-tacos Every night can be taco night labels May 26, 2026
@github-actions github-actions Bot added the needs-review Needs human review label May 26, 2026
@vidplace7 vidplace7 removed the needs-review Needs human review label May 26, 2026
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Firmware Size Report

22 targets | vs develop: 22 increased, net +11,128 (+10.9 KB)

Target Size vs develop
heltec-vision-master-e213-inkhud 2,230,240 📈 +2,880 (+2.8 KB)
station-g3 2,266,848 📈 +496
t-deck-tft 3,811,168 📈 +496
rak11200 1,861,088 📈 +480
station-g2 2,266,832 📈 +480
Show 17 more target(s)
Target Size vs develop
heltec-v3 2,264,544 📈 +464
heltec-v4 2,277,760 📈 +464
rak3312 2,272,704 📈 +464
heltec-ht62-esp32c3-sx1262 2,135,440 📈 +448
seeed-xiao-s3 2,276,592 📈 +448
tlora-c6 2,368,752 📈 +448
t-eth-elite 2,491,920 📈 +432
elecrow-adv-35-tft 3,417,040 📈 +416
pico2w 1,220,932 📈 +416
picow 1,245,240 📈 +360
seeed_xiao_rp2350 768,296 📈 +360
pico2 770,128 📈 +352
pico 783,016 📈 +328
rak11310 805,752 📈 +328
seeed_xiao_rp2040 781,232 📈 +328
wio-e5 238,756 📈 +128
rak3172 186,408 📈 +112

Updated for 6a5fe81

@vidplace7 vidplace7 marked this pull request as ready for review June 4, 2026 20:55
@vidplace7 vidplace7 requested a review from caveman99 June 4, 2026 20:56
@vidplace7 vidplace7 changed the title Add T-Beam BPF (144-148 Mhz LoRa) Add T-Beam BPF - 144-148 Mhz LoRa @ 37 dBm (5 Watts) Jun 5, 2026
@vidplace7 vidplace7 changed the title Add T-Beam BPF - 144-148 Mhz LoRa @ 37 dBm (5 Watts) Add T-Beam BPF - 144-148 Mhz LoRa @ ~37 dBm (5 Watts) Jun 5, 2026
@vidplace7

Copy link
Copy Markdown
Member Author

This is finally ready to go 😊

Copilot AI 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.

Pull request overview

This PR adds support for the LilyGo T-Beam BPF (ESP32-S3 + SX1278) 2‑meter (144–148 MHz) high-power LoRa variant, integrating it into the Meshtastic firmware’s build/board system and platform-specific initialization paths.

Changes:

  • Adds a new ESP32-S3 variant (t-beam-bpf) with board definitions, pins, and PlatformIO environment.
  • Wires the new hardware model into ESP32 hardware detection and AXP2101 rail enablement (GNSS + SD).
  • Adds RF95 power-rail enable/disable handling and constrains the region picker UI to 2m-only regions on ham-band-only hardware.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
variants/esp32s3/t-beam-bpf/variant.h New variant pinout + radio/PA configuration + ham-2m-only marker define.
variants/esp32s3/t-beam-bpf/platformio.ini New t-beam-bpf PlatformIO environment and build flags.
variants/esp32s3/t-beam-bpf/pins_arduino.h Arduino pin mappings (I2C/SPI/UART/SD CS) for the new board variant.
src/Power.cpp Adds AXP2101 rail enablement for GNSS + SD on T-Beam BPF.
src/platform/esp32/architecture.h Adds HW model selection for T_BEAM_BPF.
src/mesh/RF95Interface.cpp Enables/disables a variant-provided RF95 power rail around init/sleep.
src/mesh/NodeDB.cpp Sets ham-only hardware default licensed mode behavior.
src/graphics/draw/MenuHandler.cpp Limits region picker options to ITU*_2M regions on ham-2m-only hardware.
boards/t-beam-bpf.json Adds PlatformIO board definition for the new hardware target.

Comment thread src/mesh/NodeDB.cpp
Comment on lines +1527 to +1532
#ifdef HAS_HAM_2M_ONLY
// Ham-band-only hardware defaults to licensed operation. The user can still flip this off later
// (e.g. a commercial operator on an adjacent allocation who wants to keep encryption on) — we
// only set the default here, not on every boot.
owner.is_licensed = true;
#endif
Comment thread src/mesh/NodeDB.cpp
Comment on lines +1528 to +1531
// Ham-band-only hardware defaults to licensed operation. The user can still flip this off later
// (e.g. a commercial operator on an adjacent allocation who wants to keep encryption on) — we
// only set the default here, not on every boot.
owner.is_licensed = true;
Comment thread src/Power.cpp
Comment on lines +1366 to +1370
// T-Beam BPF rail map (per schematic LilyGo_TBeam_BPF r2025-05-08):
// DCDC1 -> ESP32 + OLED 3V3 (always on, protected)
// ALDO2 -> MicroSD 3V3 (OFF at reset, must enable)
// ALDO4 -> L76K GNSS 3V3 (OFF at reset, must enable)
// ALDO1/3, BLDO1/2, DLDO1 -> user headers / unused at boot, leave at reset defaults.
@github-actions

github-actions Bot commented Jun 17, 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 (25)
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
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
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 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-07-29. Updated for e6ea43f.

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds complete firmware support for the LilyGo T-Beam-BPF (ESP32-S3) board: board JSON, PlatformIO environment, Arduino pin header, and variant.h with all GPIO/radio/PMU macros. Wires the new T_BEAM_BPF define into the architecture header, configures AXP2101 PMU rails, adds RF95_POWER_EN GPIO control on init/sleep, and restricts the LoRa region UI to HAM 2m bands with is_licensed defaulting to true.

Changes

T-Beam BPF Board Support

Layer / File(s) Summary
Board definition and variant headers
boards/t-beam-bpf.json, variants/esp32s3/t-beam-bpf/platformio.ini, variants/esp32s3/t-beam-bpf/pins_arduino.h, variants/esp32s3/t-beam-bpf/variant.h
Introduces all build-system and hardware-description files: board JSON with ESP32-S3 flash/PSRAM/upload config, PlatformIO environment inheriting esp32s3_base, Arduino pin header with USB/UART/I2C/SPI/SD macros, and variant.h defining all GPIO assignments for GPS, buttons, SPI, LoRa SX1278/RF95, SH1106 OLED, AXP2101 PMU, and HAM 2m-only restrictions.
Architecture mapping, PMU rail config, and RF95 power GPIO
src/platform/esp32/architecture.h, src/Power.cpp, src/mesh/RF95Interface.cpp
Maps T_BEAM_BPF to meshtastic_HardwareModel_TBEAM_BPF in the HW_VENDOR chain. Adds AXP2101 init block enabling ALDO4/ALDO2 at 3300 mV and disabling DCDC5/DLDO1. Adds RF95_POWER_EN GPIO HIGH on init and LOW on sleep.
HAM 2m-only region picker and license defaults
src/graphics/draw/MenuHandler.cpp, src/mesh/NodeDB.cpp
Under HAS_HAM_2M_ONLY, narrows the LoRa region picker to Back + ITU1/2/3_2M options, and sets owner.is_licensed = true during default device-state installation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 A new beam takes flight on the 2m band,
With rails powered up by the PMU's hand.
The region picker narrows, ham-licensed by default,
SX1278 wakes with a GPIO exalt.
Hops the rabbit through ESP32-S3 skies —
T-Beam BPF soars where the signal flies!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the feature but omits the template's Attestations/testing section and device regression confirmations. Add the Attestations checklist, note what was tested, and state any devices not verified so reviewers can assess regressions.
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the new T-Beam BPF hardware support and LoRa power focus.
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 t-beam-bpf

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@boards/t-beam-bpf.json`:
- Around line 8-15: The board definition only exports LILYGO_TBEAM_BPF, while
the rest of the build logic keys off T_BEAM_BPF in platformio.ini and
architecture.h. Update the t-beam-bpf board JSON extra_flags so the canonical
T_BEAM_BPF macro is defined there as well, ensuring builds that select board =
t-beam-bpf take the T-Beam-BPF-specific code path instead of falling back to
PRIVATE_HW.

In `@src/graphics/draw/MenuHandler.cpp`:
- Around line 227-236: The HAM-only region picker in LoraRegionPicker is
blocking all menu-driven licensed-region choices when owner.is_licensed is
false, which can leave the user unable to re-enable licensed mode. Update the
selection flow so HAM region picks are handled before
RadioInterface::checkConfigRegion() rejects them, or explicitly bypass the
licensed-only rejection for the region options shown under HAS_HAM_2M_ONLY. Keep
the existing HamModeConfirm path intact, and ensure the fix applies to the
region selection handling in MenuHandler::LoraRegionPicker and the related menu
branch referenced in the diff.

In `@src/mesh/RF95Interface.cpp`:
- Around line 116-120: In RF95Interface initialization, the RF power rail is
enabled before calling lora->begin() but is not turned off on failure paths.
Update the init flow so every early return and the final false return after a
failed begin() disables RF95_POWER_EN before exiting, keeping the external RF
supply from staying powered on a missing or bad radio.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9dc0075c-4a9e-4840-b335-d63fe04ccf9e

📥 Commits

Reviewing files that changed from the base of the PR and between 8345c2b and e6ea43f.

📒 Files selected for processing (9)
  • boards/t-beam-bpf.json
  • src/Power.cpp
  • src/graphics/draw/MenuHandler.cpp
  • src/mesh/NodeDB.cpp
  • src/mesh/RF95Interface.cpp
  • src/platform/esp32/architecture.h
  • variants/esp32s3/t-beam-bpf/pins_arduino.h
  • variants/esp32s3/t-beam-bpf/platformio.ini
  • variants/esp32s3/t-beam-bpf/variant.h

Comment thread boards/t-beam-bpf.json
Comment on lines +8 to +15
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DLILYGO_TBEAM_BPF",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Export the canonical board macro from the board definition.

Everything else in this bring-up keys off T_BEAM_BPF (variants/esp32s3/t-beam-bpf/platformio.ini, Line 23 and src/platform/esp32/architecture.h, Lines 201-202), but this board JSON only defines LILYGO_TBEAM_BPF. Any build that selects board = t-beam-bpf without this exact env overlay will fall through to PRIVATE_HW and skip the T-Beam-BPF-specific paths.

Suggested fix
     "extra_flags": [
       "-DBOARD_HAS_PSRAM",
+      "-DT_BEAM_BPF",
       "-DLILYGO_TBEAM_BPF",
       "-DARDUINO_USB_CDC_ON_BOOT=1",
📝 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
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DLILYGO_TBEAM_BPF",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DT_BEAM_BPF",
"-DLILYGO_TBEAM_BPF",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
🤖 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 `@boards/t-beam-bpf.json` around lines 8 - 15, The board definition only
exports LILYGO_TBEAM_BPF, while the rest of the build logic keys off T_BEAM_BPF
in platformio.ini and architecture.h. Update the t-beam-bpf board JSON
extra_flags so the canonical T_BEAM_BPF macro is defined there as well, ensuring
builds that select board = t-beam-bpf take the T-Beam-BPF-specific code path
instead of falling back to PRIVATE_HW.

Comment on lines +227 to +236
#ifdef HAS_HAM_2M_ONLY
// Hardware is restricted to the amateur 2m band — offer only the 2m regions
// so the user cannot pick a sub-GHz region the RF path cannot emit or receive.
static const LoraRegionOption regionOptions[] = {
{"Back", OptionsAction::Back},
{"ITU1_2M (144-146)", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_ITU1_2M},
{"ITU2_2M (144-148)", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_ITU2_2M},
{"ITU3_2M (144-148)", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_ITU3_2M},
};
#else

Copy link
Copy Markdown

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

HAM-only picker can lock the user out of re-enabling licensed mode.

After this change, every visible choice is licensed-only, but LoraRegionPicker() still calls RadioInterface::checkConfigRegion() before it queues HamModeConfirm. If owner.is_licensed is ever false, all three selections are rejected and there is no on-device path back into licensed mode. Prompt first for HAM regions, or bypass the licensed-only rejection for menu-driven HAM selections.

Also applies to: 273-273

🤖 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 `@src/graphics/draw/MenuHandler.cpp` around lines 227 - 236, The HAM-only
region picker in LoraRegionPicker is blocking all menu-driven licensed-region
choices when owner.is_licensed is false, which can leave the user unable to
re-enable licensed mode. Update the selection flow so HAM region picks are
handled before RadioInterface::checkConfigRegion() rejects them, or explicitly
bypass the licensed-only rejection for the region options shown under
HAS_HAM_2M_ONLY. Keep the existing HamModeConfirm path intact, and ensure the
fix applies to the region selection handling in MenuHandler::LoraRegionPicker
and the related menu branch referenced in the diff.

Comment on lines +116 to +120
#ifdef RF95_POWER_EN
pinMode(RF95_POWER_EN, OUTPUT);
digitalWrite(RF95_POWER_EN, HIGH);
#endif

Copy link
Copy Markdown

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

Power the RF rail back down on init failures.

These lines turn the external RF supply on before lora->begin(), but the failure exit at Line 186 and the final false return path leave it high. On a missing/bad radio that keeps the PA chain powered indefinitely.

Suggested fix
 bool RF95Interface::init()
 {
 `#ifdef` RF95_POWER_EN
     pinMode(RF95_POWER_EN, OUTPUT);
     digitalWrite(RF95_POWER_EN, HIGH);
 `#endif`
+    auto powerOffRf95 = []() {
+#ifdef RF95_POWER_EN
+        digitalWrite(RF95_POWER_EN, LOW);
+#endif
+    };
 
     RadioLibInterface::init();
@@
     LOG_INFO("RF95 init result %d", res);
-    if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED)
+    if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED) {
+        powerOffRf95();
         return false;
+    }
@@
-    return res == RADIOLIB_ERR_NONE;
+    if (res != RADIOLIB_ERR_NONE)
+        powerOffRf95();
+    return res == RADIOLIB_ERR_NONE;
 }
📝 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
#ifdef RF95_POWER_EN
pinMode(RF95_POWER_EN, OUTPUT);
digitalWrite(RF95_POWER_EN, HIGH);
#endif
bool RF95Interface::init()
{
`#ifdef` RF95_POWER_EN
pinMode(RF95_POWER_EN, OUTPUT);
digitalWrite(RF95_POWER_EN, HIGH);
`#endif`
auto powerOffRf95 = []() {
`#ifdef` RF95_POWER_EN
digitalWrite(RF95_POWER_EN, LOW);
`#endif`
};
RadioLibInterface::init();
@@
LOG_INFO("RF95 init result %d", res);
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED) {
powerOffRf95();
return false;
}
@@
if (res != RADIOLIB_ERR_NONE)
powerOffRf95();
return res == RADIOLIB_ERR_NONE;
}
🤖 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 `@src/mesh/RF95Interface.cpp` around lines 116 - 120, In RF95Interface
initialization, the RF power rail is enabled before calling lora->begin() but is
not turned off on failure paths. Update the init flow so every early return and
the final false return after a failed begin() disables RF95_POWER_EN before
exiting, keeping the external RF supply from staying powered on a missing or bad
radio.

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

Labels

2.8 hardware-support Hardware related: new devices or modules, problems specific to hardware needs-tacos Every night can be taco night requires-protos Requires changes to protobufs to work triaged Reviewed by the team, has enough information and ready to work on now.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants