mirror of
https://github.com/meshtastic/firmware.git
synced 2026-05-31 12:18:18 -04:00
Move T-Beam-BPF work from #9703 at vidplace7/t-beam-bpf
Initial work to add T-Beam BPF (144-148 Mhz LoRa)
This commit is contained in:
39
boards/t-beam-bpf.json
Normal file
39
boards/t-beam-bpf.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"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"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"psram_type": "opi",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "t-beam-bpf"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "LilyGo TBeam-BPF",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 16777216,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "http://www.lilygo.cn/",
|
||||
"vendor": "LilyGo"
|
||||
}
|
||||
@@ -1362,6 +1362,22 @@ bool Power::axpChipInit()
|
||||
PMU->disablePowerOutput(XPOWERS_DLDO1); // Invalid power channel, it does not exist
|
||||
PMU->disablePowerOutput(XPOWERS_DLDO2); // Invalid power channel, it does not exist
|
||||
PMU->disablePowerOutput(XPOWERS_VBACKUP);
|
||||
} else if (HW_VENDOR == meshtastic_HardwareModel_TBEAM_BPF) {
|
||||
// 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.
|
||||
// LoRa power is outside the PMU (external P-MOSFET switched by RF95_POWER_EN / IO16).
|
||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO4);
|
||||
|
||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
||||
|
||||
// Make sure nothing's driving into an unused rail
|
||||
PMU->disablePowerOutput(XPOWERS_DCDC5);
|
||||
PMU->disablePowerOutput(XPOWERS_DLDO1);
|
||||
}
|
||||
|
||||
// disable all axp chip interrupt
|
||||
|
||||
@@ -176,6 +176,15 @@ void menuHandler::OnboardMessage()
|
||||
|
||||
void menuHandler::LoraRegionPicker(uint32_t duration)
|
||||
{
|
||||
#ifdef HAS_HAM_2M_ONLY
|
||||
// Hardware is restricted to the amateur 2m band — offer only the two 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},
|
||||
{"ITU23_2M (144-148)", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_ITU23_2M},
|
||||
};
|
||||
#else
|
||||
static const LoraRegionOption regionOptions[] = {
|
||||
{"Back", OptionsAction::Back},
|
||||
{"US", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_US},
|
||||
@@ -208,6 +217,7 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
||||
{"BR_902", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_BR_902},
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
constexpr size_t regionCount = sizeof(regionOptions) / sizeof(regionOptions[0]);
|
||||
static std::array<const char *, regionCount> regionLabels{};
|
||||
|
||||
@@ -46,7 +46,7 @@ extern const RegionProfile PROFILE_EU868;
|
||||
extern const RegionProfile PROFILE_UNDEF;
|
||||
extern const RegionProfile PROFILE_LITE;
|
||||
extern const RegionProfile PROFILE_NARROW;
|
||||
// extern const RegionProfile PROFILE_HAM;
|
||||
extern const RegionProfile PROFILE_HAM;
|
||||
|
||||
// Map from old region names to new region enums
|
||||
struct RegionInfo {
|
||||
|
||||
@@ -1520,6 +1520,13 @@ void NodeDB::installDefaultDeviceState()
|
||||
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
|
||||
owner.has_is_unmessagable = true;
|
||||
owner.is_unmessagable = false;
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
// We reserve a few nodenums for future use
|
||||
|
||||
@@ -113,6 +113,11 @@ void RF95Interface::setTransmitEnable(bool txon)
|
||||
/// \return true if initialisation succeeded.
|
||||
bool RF95Interface::init()
|
||||
{
|
||||
#ifdef RF95_POWER_EN
|
||||
pinMode(RF95_POWER_EN, OUTPUT);
|
||||
digitalWrite(RF95_POWER_EN, HIGH);
|
||||
#endif
|
||||
|
||||
RadioLibInterface::init();
|
||||
|
||||
#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT)
|
||||
@@ -335,6 +340,10 @@ bool RF95Interface::sleep()
|
||||
setStandby(); // First cancel any active receiving/sending
|
||||
lora->sleep();
|
||||
|
||||
#ifdef RF95_POWER_EN
|
||||
digitalWrite(RF95_POWER_EN, LOW);
|
||||
#endif
|
||||
|
||||
#ifdef RF95_FAN_EN
|
||||
digitalWrite(RF95_FAN_EN, 0);
|
||||
#endif
|
||||
|
||||
@@ -56,6 +56,7 @@ const RegionProfile PROFILE_EU868 = {PRESETS_EU_868, 0, 0, false, false, 0, 1, 1
|
||||
const RegionProfile PROFILE_UNDEF = {PRESETS_UNDEF, 0, 0, true, false, 0, 1, 1, 0};
|
||||
const RegionProfile PROFILE_LITE = {PRESETS_LITE, 0.4, 0.0375f, false, false, 0, 10, 10, 0};
|
||||
const RegionProfile PROFILE_NARROW = {PRESETS_NARROW, 0, 0.0104f, true, false, 0, 1, 1, 1};
|
||||
const RegionProfile PROFILE_HAM = {PRESETS_NARROW, 0, 0, false, true, 0, 1, 1, 17};
|
||||
|
||||
#define RDEF(name, freq_start, freq_end, duty_cycle, power_limit, frequency_switching, wide_lora, profile_ptr, default_preset) \
|
||||
{ \
|
||||
@@ -225,6 +226,19 @@ const RegionInfo regions[] = {
|
||||
*/
|
||||
RDEF(BR_902, 902.0f, 907.5f, 100, 30, false, false, PROFILE_STD, PRESET(LONG_FAST)),
|
||||
|
||||
/*
|
||||
ITU Region 1 (Europe, Africa, Middle East, former USSR) amateur 2m allocation: 144.000 - 146.000 MHz.
|
||||
Power limit is the regulatory ceiling (1 W / 30 dBm) — individual hardware will cap below this
|
||||
via its own PA curve; the field here is just the legal upper bound.
|
||||
*/
|
||||
RDEF(ITU1_2M, 144.0f, 146.0f, 100, 30, false, false, PROFILE_HAM, PRESET(NARROW_FAST)),
|
||||
|
||||
/*
|
||||
ITU Region 2 (Americas) and Region 3 (Asia/Pacific) amateur 2m allocation: 144.000 - 148.000 MHz.
|
||||
Typical admin rules (e.g. US FCC Part 97) allow well above 30 dBm for licensed operators.
|
||||
*/
|
||||
RDEF(ITU23_2M, 144.0f, 148.0f, 100, 30, false, false, PROFILE_HAM, PRESET(NARROW_FAST)),
|
||||
|
||||
/*
|
||||
2.4 GHZ WLAN Band equivalent. Only for SX128x chips.
|
||||
*/
|
||||
@@ -535,6 +549,23 @@ std::unique_ptr<RadioInterface> initLoRa()
|
||||
rebootAtMsec = millis() + 5000;
|
||||
}
|
||||
}
|
||||
|
||||
// Hardware/region crosscheck for the amateur 2m band: ham-only boards must run a 2m region,
|
||||
// and boards without 2m support must not run one. In either mismatch, drop to UNSET so the
|
||||
// first-start picker runs and the user re-selects a legal region for the hardware.
|
||||
const bool is2mRegion = config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_ITU1_2M ||
|
||||
config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_ITU23_2M;
|
||||
#ifdef HAS_HAM_2M_ONLY
|
||||
const bool mismatch = !is2mRegion && config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_UNSET;
|
||||
#else
|
||||
const bool mismatch = is2mRegion;
|
||||
#endif
|
||||
if (mismatch) {
|
||||
LOG_WARN("Saved region incompatible with this hardware's RF path. Revert to unset");
|
||||
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
|
||||
nodeDB->saveToDisk(SEGMENT_CONFIG);
|
||||
}
|
||||
|
||||
return rIf;
|
||||
}
|
||||
|
||||
@@ -834,7 +865,14 @@ bool RadioInterface::validateConfigRegion(const meshtastic_Config_LoRaConfig &lo
|
||||
const RegionInfo *newRegion = getRegion(loraConfig.region);
|
||||
|
||||
// If you are not licensed, you can't use ham regions.
|
||||
if (newRegion->profile->licensedOnly && !devicestate.owner.is_licensed) {
|
||||
// Exception: on hardware that can *only* operate on a ham band (e.g. T-Beam BPF), the user has
|
||||
// no other region to choose, so allow unlicensed selection — a commercial operator on adjacent
|
||||
// frequencies can still use the band plan and keep encryption enabled.
|
||||
bool allowUnlicensedHam = false;
|
||||
#ifdef HAS_HAM_2M_ONLY
|
||||
allowUnlicensedHam = true;
|
||||
#endif
|
||||
if (newRegion->profile->licensedOnly && !devicestate.owner.is_licensed && !allowUnlicensedHam) {
|
||||
char err_string[160];
|
||||
snprintf(err_string, sizeof(err_string), "Region %s requires licensed mode", newRegion->name);
|
||||
LOG_ERROR("%s", err_string);
|
||||
@@ -843,6 +881,34 @@ bool RadioInterface::validateConfigRegion(const meshtastic_Config_LoRaConfig &lo
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool is2mRegion = loraConfig.region == meshtastic_Config_LoRaConfig_RegionCode_ITU1_2M ||
|
||||
loraConfig.region == meshtastic_Config_LoRaConfig_RegionCode_ITU23_2M;
|
||||
|
||||
#ifdef HAS_HAM_2M_ONLY
|
||||
// This hardware's front-end / band-pass filter only passes 144-148 MHz. Any other region
|
||||
// selection would key the radio on a frequency the RF path cannot emit or receive.
|
||||
if (!is2mRegion && loraConfig.region != meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
char err_string[160];
|
||||
snprintf(err_string, sizeof(err_string), "Region %s not supported: this hardware is 2m-only", newRegion->name);
|
||||
LOG_ERROR("%s", err_string);
|
||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
||||
sendErrorNotification(err_string);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// Conversely, the 2m ham regions are illegal RF output for hardware not designed for that band
|
||||
// (e.g. selecting ITU23_2M on a 915 MHz node would transmit at ~3x the expected frequency with
|
||||
// an untuned antenna and filter). Refuse the selection entirely.
|
||||
if (is2mRegion) {
|
||||
char err_string[160];
|
||||
snprintf(err_string, sizeof(err_string), "Region %s requires 2m-band hardware", newRegion->name);
|
||||
LOG_ERROR("%s", err_string);
|
||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
||||
sendErrorNotification(err_string);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -198,6 +198,8 @@
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_DECK_PRO
|
||||
#elif defined(T_BEAM_1W)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_TBEAM_1_WATT
|
||||
#elif defined(T_BEAM_BPF)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_TBEAM_BPF
|
||||
#elif defined(T_LORA_PAGER)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_LORA_PAGER
|
||||
#elif defined(HELTEC_V4)
|
||||
|
||||
26
variants/esp32s3/t-beam-bpf/pins_arduino.h
Normal file
26
variants/esp32s3/t-beam-bpf/pins_arduino.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define USB_VID 0x303a
|
||||
#define USB_PID 0x1001
|
||||
|
||||
// UART1 (qwiic)
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
// I2C for OLED and sensors
|
||||
static const uint8_t SDA = 8;
|
||||
static const uint8_t SCL = 9;
|
||||
|
||||
// Default SPI mapped to Radio/SD
|
||||
static const uint8_t SS = 1; // LoRa CS
|
||||
static const uint8_t MOSI = 11;
|
||||
static const uint8_t MISO = 13;
|
||||
static const uint8_t SCK = 12;
|
||||
|
||||
// SD Card CS
|
||||
#define SDCARD_CS 10
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
23
variants/esp32s3/t-beam-bpf/platformio.ini
Normal file
23
variants/esp32s3/t-beam-bpf/platformio.ini
Normal file
@@ -0,0 +1,23 @@
|
||||
; LilyGo T-Beam-BPF (144-148Mhz)
|
||||
[env:t-beam-bpf]
|
||||
custom_meshtastic_hw_model = 124
|
||||
custom_meshtastic_hw_model_slug = TBEAM_BPF
|
||||
custom_meshtastic_architecture = esp32s3
|
||||
custom_meshtastic_actively_supported = true
|
||||
custom_meshtastic_support_level = 3
|
||||
custom_meshtastic_display_name = LILYGO T-Beam BPF
|
||||
custom_meshtastic_images = tbeam-1w.svg
|
||||
custom_meshtastic_tags = LilyGo
|
||||
|
||||
extends = esp32s3_base
|
||||
board = t-beam-bpf
|
||||
board_build.partitions = default_16MB.csv
|
||||
board_check = true
|
||||
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
|
||||
build_flags =
|
||||
${esp32s3_base.build_flags}
|
||||
-I variants/esp32s3/t-beam-bpf
|
||||
-D T_BEAM_BPF
|
||||
66
variants/esp32s3/t-beam-bpf/variant.h
Normal file
66
variants/esp32s3/t-beam-bpf/variant.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// LilyGo T-Beam-BPF variant.h
|
||||
// Configuration based on LilyGO utilities.h and RF documentation
|
||||
|
||||
// Hardware is restricted to the amateur 2m band (144-148 MHz).
|
||||
#define HAS_HAM_2M_ONLY 1
|
||||
|
||||
// I2C for OLED display (SH1106 at 0x3C)
|
||||
#define I2C_SDA 8
|
||||
#define I2C_SCL 9
|
||||
|
||||
// GPS - Quectel L76K. Per schematic sheet 7:
|
||||
|
||||
#define GPS_RX_PIN 5
|
||||
#define GPS_TX_PIN 6
|
||||
#define GPS_1PPS_PIN 7
|
||||
#define HAS_GPS 1
|
||||
#define GPS_BAUDRATE 9600
|
||||
|
||||
// Buttons
|
||||
#define BUTTON_PIN 0 // BUTTON 1
|
||||
#define ALT_BUTTON_PIN 3 // BUTTON 2
|
||||
|
||||
// SPI (shared by LoRa and SD)
|
||||
#define SPI_MOSI 11
|
||||
#define SPI_SCK 12
|
||||
#define SPI_MISO 13
|
||||
#define SPI_CS 10
|
||||
|
||||
// SD Card
|
||||
#define HAS_SDCARD
|
||||
#define SDCARD_USE_SPI1
|
||||
// #define SDCARD_CS SPI_CS (already defined in pins_arduino.h)
|
||||
|
||||
// LoRa Radio - SX1278 144-148Mhz
|
||||
#define USE_RF95
|
||||
#define LORA_SCK SPI_SCK
|
||||
#define LORA_MISO SPI_MISO
|
||||
#define LORA_MOSI SPI_MOSI
|
||||
#define LORA_CS 1
|
||||
#define LORA_RESET 18
|
||||
#define LORA_IRQ 14
|
||||
#define LORA_DIO0 LORA_IRQ
|
||||
#define LORA_DIO1 21
|
||||
#define RF95_RXEN 39 // LNA enable - HIGH during RX
|
||||
|
||||
// CRITICAL: Radio power enable - MUST be HIGH before lora.begin()!
|
||||
// GPIO 16 powers the SX1278 via LDO
|
||||
#define RF95_POWER_EN 16
|
||||
|
||||
// "+27dBm"? PA! Investigate further (poorly documented)
|
||||
// LilyGo Docs specify SX1278 power must be capped at 10
|
||||
#define RF95_MAX_POWER 10
|
||||
// TODO map PA output curve
|
||||
// #define TX_GAIN_LORA 17
|
||||
|
||||
// Display - SH1106 OLED (128x64)
|
||||
#define USE_SH1106
|
||||
#define OLED_WIDTH 128
|
||||
#define OLED_HEIGHT 64
|
||||
|
||||
// 32768 Hz crystal present
|
||||
#define HAS_32768HZ 1
|
||||
|
||||
// PMU
|
||||
#define HAS_AXP2101
|
||||
// #define PMU_IRQ 4 // Leave disabled for now
|
||||
Reference in New Issue
Block a user