Merge branch 'dev' into wio-tracker-L1-1W

This commit is contained in:
Scott Powell
2026-09-15 18:24:52 +10:00
99 changed files with 2528 additions and 491 deletions
+4
View File
@@ -173,6 +173,7 @@ extends = Heltec_RC32
build_flags =
${Heltec_RC32.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D DISPLAY_CLASS=NullDisplayDriver
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
@@ -185,6 +186,7 @@ build_src_filter = ${Heltec_RC32.build_src_filter}
+<helpers/ui/buzzer.cpp>
+<helpers/ui/NullDisplayDriver.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
@@ -314,6 +316,7 @@ extends = Heltec_RC32_with_display
build_flags =
${Heltec_RC32_with_display.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D UI_HAS_ROTARY_INPUT
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
@@ -324,6 +327,7 @@ build_flags =
build_src_filter = ${Heltec_RC32_with_display.build_src_filter}
+<helpers/ui/buzzer.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
+44 -4
View File
@@ -131,10 +131,50 @@ bool T096Board::setLoRaFemLnaEnabled(bool enable) {
return true;
}
bool T096Board::canControlLoRaFemLna() const {
return loRaFEMControl.isLnaCanControl();
}
bool T096Board::isLoRaFemLnaEnabled() const {
return loRaFEMControl.isLNAEnabled();
}
void T096Board::attachDynamicPrefs(KeyValueStore* prefs) {
_prefs = prefs;
char radio_fem_rxgain[8] = { 0 };
_prefs->getByKey("fem_rxgain", radio_fem_rxgain, 7); // get initial values
setLoRaFemLnaEnabled(strcmp(radio_fem_rxgain, "1") == 0);
}
bool T096Board::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
if (strcmp(command, "get radio.fem.rxgain") == 0) {
if (!loRaFEMControl.isLnaCanControl()) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "> %s", isLoRaFemLnaEnabled() ? "on" : "off");
}
return true;
}
if (memcmp(command, "set radio.fem.rxgain ", 21) == 0) {
if (!loRaFEMControl.isLnaCanControl()) {
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemLnaEnabled(true)) {
_prefs->setByKey("fem_rxgain", "1");
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemLnaEnabled(false)) {
_prefs->setByKey("fem_rxgain", "0");
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else {
strcpy(reply, "Error: state must be on or off");
}
return true;
}
return false; // not handled
}
+8 -3
View File
@@ -4,28 +4,33 @@
#include <Arduino.h>
#include <helpers/NRF52Board.h>
#include <helpers/RefCountedDigitalPin.h>
#include <helpers/KeyValueStore.h>
#include "LoRaFEMControl.h"
class T096Board : public NRF52BoardDCDC {
KeyValueStore* _prefs = NULL;
protected:
#ifdef NRF52_POWER_MANAGEMENT
void initiateShutdown(uint8_t reason) override;
#endif
void variant_shutdown();
bool setLoRaFemLnaEnabled(bool enable);
bool isLoRaFemLnaEnabled() const;
public:
RefCountedDigitalPin periph_power;
LoRaFEMControl loRaFEMControl;
T096Board() :periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE), NRF52Board("T096_OTA") {}
void begin();
void attachDynamicPrefs(KeyValueStore* prefs);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
void onBeforeTransmit(void) override;
void onAfterTransmit(void) override;
uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override ;
void powerOff() override;
bool setLoRaFemLnaEnabled(bool enable) override;
bool canControlLoRaFemLna() const override;
bool isLoRaFemLnaEnabled() const override;
};
@@ -72,10 +72,50 @@ void HeltecTrackerV2Board::begin() {
return true;
}
bool HeltecTrackerV2Board::canControlLoRaFemLna() const {
return loRaFEMControl.isLnaCanControl();
}
bool HeltecTrackerV2Board::isLoRaFemLnaEnabled() const {
return loRaFEMControl.isLNAEnabled();
}
void HeltecTrackerV2Board::attachDynamicPrefs(KeyValueStore* prefs) {
_prefs = prefs;
char radio_fem_rxgain[8] = { 0 };
_prefs->getByKey("fem_rxgain", radio_fem_rxgain, 7); // get initial values
setLoRaFemLnaEnabled(strcmp(radio_fem_rxgain, "1") == 0);
}
bool HeltecTrackerV2Board::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
if (strcmp(command, "get radio.fem.rxgain") == 0) {
if (!loRaFEMControl.isLnaCanControl()) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "> %s", isLoRaFemLnaEnabled() ? "on" : "off");
}
return true;
}
if (memcmp(command, "set radio.fem.rxgain ", 21) == 0) {
if (!loRaFEMControl.isLnaCanControl()) {
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemLnaEnabled(true)) {
_prefs->setByKey("fem_rxgain", "1");
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemLnaEnabled(false)) {
_prefs->setByKey("fem_rxgain", "0");
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else {
strcpy(reply, "Error: state must be on or off");
}
return true;
}
return false; // not handled
}
@@ -6,6 +6,10 @@
#include "LoRaFEMControl.h"
class HeltecTrackerV2Board : public ESP32Board {
KeyValueStore* _prefs = NULL;
bool setLoRaFemLnaEnabled(bool enable);
bool isLoRaFemLnaEnabled() const;
public:
RefCountedDigitalPin periph_power;
@@ -14,13 +18,13 @@ public:
HeltecTrackerV2Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { }
void begin();
void attachDynamicPrefs(KeyValueStore* prefs);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
void onBeforeTransmit(void) override;
void onAfterTransmit(void) override;
void powerOff() override;
uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override ;
bool setLoRaFemLnaEnabled(bool enable) override;
bool canControlLoRaFemLna() const override;
bool isLoRaFemLnaEnabled() const override;
};
@@ -182,6 +182,7 @@ extends = Heltec_tracker_v2
build_flags =
${Heltec_tracker_v2.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D DISPLAY_CLASS=ST7735Display
@@ -195,6 +196,7 @@ build_src_filter = ${Heltec_tracker_v2.build_src_filter}
+<helpers/ui/ST7735Display.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
+2
View File
@@ -178,6 +178,7 @@ extends = Heltec_lora32_v2
build_flags =
${Heltec_lora32_v2.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=160
-D MAX_GROUP_CHANNELS=8
@@ -189,6 +190,7 @@ build_flags =
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<helpers/ui/SSD1306Display.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<../examples/companion_radio/*.cpp>
+4
View File
@@ -185,6 +185,7 @@ extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D DISPLAY_CLASS=SSD1306Display
@@ -198,6 +199,7 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
@@ -340,6 +342,7 @@ lib_deps =
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D WIFI_DEBUG_LOGGING=1
@@ -350,6 +353,7 @@ build_flags =
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
lib_deps =
${Heltec_lora32_v3.lib_deps}
+44 -4
View File
@@ -73,10 +73,50 @@ void HeltecV4Board::begin() {
return true;
}
bool HeltecV4Board::canControlLoRaFemLna() const {
return loRaFEMControl.isLnaCanControl();
}
bool HeltecV4Board::isLoRaFemLnaEnabled() const {
return loRaFEMControl.isLNAEnabled();
}
void HeltecV4Board::attachDynamicPrefs(KeyValueStore* prefs) {
_prefs = prefs;
char radio_fem_rxgain[8] = { 0 };
_prefs->getByKey("fem_rxgain", radio_fem_rxgain, 7); // get initial values
setLoRaFemLnaEnabled(strcmp(radio_fem_rxgain, "1") == 0);
}
bool HeltecV4Board::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
if (strcmp(command, "get radio.fem.rxgain") == 0) {
if (!loRaFEMControl.isLnaCanControl()) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "> %s", isLoRaFemLnaEnabled() ? "on" : "off");
}
return true;
}
if (memcmp(command, "set radio.fem.rxgain ", 21) == 0) {
if (!loRaFEMControl.isLnaCanControl()) {
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemLnaEnabled(true)) {
_prefs->setByKey("fem_rxgain", "1");
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemLnaEnabled(false)) {
_prefs->setByKey("fem_rxgain", "0");
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else {
strcpy(reply, "Error: state must be on or off");
}
return true;
}
return false; // not handled
}
+7 -3
View File
@@ -10,6 +10,10 @@
#endif
class HeltecV4Board : public ESP32Board {
KeyValueStore* _prefs = NULL;
bool setLoRaFemLnaEnabled(bool enable);
bool isLoRaFemLnaEnabled() const;
protected:
float adc_mult = ADC_MULTIPLIER;
@@ -20,12 +24,12 @@ public:
HeltecV4Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { }
void begin();
void attachDynamicPrefs(KeyValueStore* prefs);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
void onBeforeTransmit(void) override;
void onAfterTransmit(void) override;
void powerOff() override;
bool setLoRaFemLnaEnabled(bool enable) override;
bool canControlLoRaFemLna() const override;
bool isLoRaFemLnaEnabled() const override;
uint16_t getBattMilliVolts() override;
bool setAdcMultiplier(float multiplier) override {
if (multiplier == 0.0f) {
+4
View File
@@ -228,6 +228,7 @@ extends = heltec_v4_oled
build_flags =
${heltec_v4_oled.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
@@ -241,6 +242,7 @@ build_src_filter = ${heltec_v4_oled.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
@@ -393,6 +395,7 @@ extends = heltec_v4_tft
build_flags =
${heltec_v4_tft.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
@@ -406,6 +409,7 @@ build_src_filter = ${heltec_v4_tft.build_src_filter}
+<helpers/ui/ST7789LCDDisplay.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
+58
View File
@@ -84,3 +84,61 @@ const char* HeltecV4R8Board::getManufacturerName() const {
return "Heltec V4 R8 OLED";
#endif
}
bool HeltecV4R8Board::setLoRaFemLnaEnabled(bool enable) {
if (!loRaFEMControl.isLnaCanControl()) {
return false;
}
loRaFEMControl.setLNAEnable(enable);
loRaFEMControl.setRxModeEnable();
return true;
}
bool HeltecV4R8Board::isLoRaFemLnaEnabled() const {
return loRaFEMControl.isLNAEnabled();
}
void HeltecV4R8Board::attachDynamicPrefs(KeyValueStore* prefs) {
_prefs = prefs;
char radio_fem_rxgain[8] = { 0 };
_prefs->getByKey("fem_rxgain", radio_fem_rxgain, 7); // get initial values
setLoRaFemLnaEnabled(strcmp(radio_fem_rxgain, "1") == 0);
}
bool HeltecV4R8Board::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
if (strcmp(command, "get radio.fem.rxgain") == 0) {
if (!loRaFEMControl.isLnaCanControl()) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "> %s", isLoRaFemLnaEnabled() ? "on" : "off");
}
return true;
}
if (memcmp(command, "set radio.fem.rxgain ", 21) == 0) {
if (!loRaFEMControl.isLnaCanControl()) {
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemLnaEnabled(true)) {
_prefs->setByKey("fem_rxgain", "1");
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemLnaEnabled(false)) {
_prefs->setByKey("fem_rxgain", "0");
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else {
strcpy(reply, "Error: state must be on or off");
}
return true;
}
return false; // not handled
}
+7
View File
@@ -11,6 +11,10 @@
#endif
class HeltecV4R8Board : public ESP32Board {
KeyValueStore* _prefs = NULL;
bool setLoRaFemLnaEnabled(bool enable);
bool isLoRaFemLnaEnabled() const;
protected:
float adc_mult = ADC_MULTIPLIER;
@@ -21,6 +25,9 @@ public:
HeltecV4R8Board() : periph_power(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE) { }
void begin();
void attachDynamicPrefs(KeyValueStore* prefs);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
void onBeforeTransmit(void) override;
void onAfterTransmit(void) override;
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
+1
View File
@@ -17,6 +17,7 @@ public:
void setLNAEnable(bool enabled);
bool isLnaCanControl(void) { return true; }
void setLnaCanControl(bool can_control) { }
bool isLNAEnabled(void) const { return lna_enabled; }
LoRaFEMType getFEMType(void) const { return KCT8103L_PA; }
private:
+4
View File
@@ -175,6 +175,7 @@ extends = heltec_v4_r8_oled
build_flags =
${heltec_v4_r8_oled.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
@@ -186,6 +187,7 @@ build_src_filter = ${heltec_v4_r8_oled.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
@@ -300,6 +302,7 @@ extends = heltec_v4_r8_tft
build_flags =
${heltec_v4_r8_tft.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
@@ -311,6 +314,7 @@ build_src_filter = ${heltec_v4_r8_tft.build_src_filter}
+<helpers/ui/ST7789LCDDisplay.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
+288 -9
View File
@@ -1,5 +1,10 @@
#include "TBeam1WBoard.h"
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <stdlib.h>
void TBeam1WBoard::begin() {
ESP32Board::begin();
@@ -15,31 +20,61 @@ void TBeam1WBoard::begin() {
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// Initialize fan control (on by default - 1W PA can overheat)
// NTC ADC (PA-adjacent thermistor)
pinMode(NTC_PIN, INPUT);
analogSetPinAttenuation(NTC_PIN, ADC_11db);
analogReadResolution(12);
// Fan: auto/onoff. Thermal on at 36C / off below 30C; TX still forces a cooldown.
pinMode(FAN_CTRL_PIN, OUTPUT);
digitalWrite(FAN_CTRL_PIN, HIGH);
_fan_on = true;
_temp_c = readNtcTempC();
startFanTask();
}
void TBeam1WBoard::startFanTask() {
if (_fan_task) return;
xTaskCreate(fanTaskThunk, "tbeam1w_fan", 4096, this, 1, &_fan_task);
}
void TBeam1WBoard::fanTaskThunk(void* arg) {
auto* self = static_cast<TBeam1WBoard*>(arg);
for (;;) {
self->updateFan();
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void TBeam1WBoard::onBeforeTransmit() {
// RF switching handled by RadioLib via SX126X_DIO2_AS_RF_SWITCH and setRfSwitchPins()
digitalWrite(LED_PIN, HIGH); // TX LED on
portENTER_CRITICAL(&_fan_mux);
_tx_active = true;
if (_mode == FAN_AUTO && !_stopped) {
setFanOutputLocked(true);
}
portEXIT_CRITICAL(&_fan_mux);
}
void TBeam1WBoard::onAfterTransmit() {
digitalWrite(LED_PIN, LOW); // TX LED off
portENTER_CRITICAL(&_fan_mux);
if (!_stopped) {
_tx_until_ms = millis() + FAN_TX_COOLDOWN_MS;
_tx_cooldown_active = true;
}
_tx_active = false;
portEXIT_CRITICAL(&_fan_mux);
}
uint16_t TBeam1WBoard::getBattMilliVolts() {
// T-Beam 1W uses 7.4V battery with voltage divider
// ADC reads through divider - adjust multiplier based on actual divider ratio
analogReadResolution(12);
uint32_t raw = 0;
for (int i = 0; i < 8; i++) {
raw += analogRead(BATTERY_PIN);
}
raw = raw / 8;
// Assuming voltage divider ratio from ADC_MULTIPLIER
// 3.3V reference, 12-bit ADC (4095 max)
return static_cast<uint16_t>((raw * 3300 * ADC_MULTIPLIER) / 4095);
}
@@ -48,6 +83,13 @@ const char* TBeam1WBoard::getManufacturerName() const {
}
void TBeam1WBoard::powerOff() {
portENTER_CRITICAL(&_fan_mux);
_stopped = true;
_tx_active = false;
_tx_cooldown_active = false;
setFanOutputLocked(false);
portEXIT_CRITICAL(&_fan_mux);
// Turn off radio LNA (CTRL pin must be LOW when not receiving)
digitalWrite(SX126X_RXEN, LOW);
@@ -55,17 +97,254 @@ void TBeam1WBoard::powerOff() {
digitalWrite(SX126X_POWER_EN, LOW);
radio_powered = false;
// Turn off LED and fan
digitalWrite(LED_PIN, LOW);
digitalWrite(FAN_CTRL_PIN, LOW);
ESP32Board::powerOff();
}
void TBeam1WBoard::setFanEnabled(bool enabled) {
digitalWrite(FAN_CTRL_PIN, enabled ? HIGH : LOW);
portENTER_CRITICAL(&_fan_mux);
setFanOutputLocked(enabled && !_stopped);
portEXIT_CRITICAL(&_fan_mux);
}
bool TBeam1WBoard::isFanEnabled() const {
return digitalRead(FAN_CTRL_PIN) == HIGH;
portENTER_CRITICAL(&_fan_mux);
bool enabled = _fan_on;
portEXIT_CRITICAL(&_fan_mux);
return enabled;
}
float TBeam1WBoard::readNtcTempC() {
analogReadMilliVolts(NTC_PIN); // settle
uint32_t sum = 0;
for (int i = 0; i < 8; i++) {
uint32_t sample_mv = analogReadMilliVolts(NTC_PIN);
// GPIO14 is ADC2 on ESP32-S3. Wi-Fi/ESP-NOW arbitration failures are
// reported by Arduino as 0 mV; never average a failed sample into a
// plausible-but-low temperature.
if (sample_mv == 0 || sample_mv >= NTC_VCC_MV) return NAN;
sum += sample_mv;
}
float mv = sum / 8.0f;
// R_ntc = R_fixed * (Vcc - V) / V for 3.3V-NTC-ADC-10k-GND
float r_ntc = NTC_R_FIXED * (NTC_VCC_MV - mv) / mv;
if (r_ntc <= 0.0f) return NAN;
float temp_k = 1.0f / (1.0f / 298.15f + (1.0f / NTC_B) * logf(r_ntc / NTC_R25));
return temp_k - 273.15f;
}
bool TBeam1WBoard::ntcImplausible(float temp_c) const {
return isnan(temp_c) || temp_c < -20.0f || temp_c > 120.0f;
}
bool TBeam1WBoard::isTxCoolingLocked(uint32_t now) {
if (_tx_active) return true;
if (!_tx_cooldown_active) return false;
if ((int32_t)(now - _tx_until_ms) >= 0) {
_tx_cooldown_active = false;
return false;
}
return true;
}
int TBeam1WBoard::cooldownSecsLocked() {
if (_tx_active) return (FAN_TX_COOLDOWN_MS + 999) / 1000;
if (!_tx_cooldown_active) return 0;
int32_t remain_ms = (int32_t)(_tx_until_ms - millis());
if (remain_ms <= 0) {
_tx_cooldown_active = false;
return 0;
}
return (remain_ms + 999) / 1000;
}
void TBeam1WBoard::setFanOutputLocked(bool enabled) {
_fan_on = enabled;
digitalWrite(FAN_CTRL_PIN, enabled ? HIGH : LOW);
}
void TBeam1WBoard::updateFan() {
float t = readNtcTempC();
uint32_t now = millis();
portENTER_CRITICAL(&_fan_mux);
if (_stopped) {
portEXIT_CRITICAL(&_fan_mux);
return;
}
_temp_c = t;
bool tx_cooling = isTxCoolingLocked(now);
bool enabled;
if (_mode == FAN_ON) {
enabled = true;
} else if (_mode == FAN_OFF) {
enabled = false;
} else if (ntcImplausible(t)) {
enabled = true; // fail-safe: treat bad NTC as hot
} else {
// TX cooldown must not latch _thermal_on, or a TX at 30C keeps the fan
// running until the temperature dips under lo.
if (t >= (float)_hi_c) _thermal_on = true;
else if (t < (float)_lo_c) _thermal_on = false;
enabled = _thermal_on || tx_cooling;
}
setFanOutputLocked(enabled);
portEXIT_CRITICAL(&_fan_mux);
}
bool TBeam1WBoard::persistKey(const char* key, const char* value) {
return _prefs && _prefs->setByKey(key, value);
}
bool TBeam1WBoard::parseIntArg(const char* text, int& value) {
if (!text || !*text) return false;
errno = 0;
char* end = nullptr;
long parsed = strtol(text, &end, 10);
if (errno == ERANGE || end == text || *end != '\0' || parsed < INT_MIN || parsed > INT_MAX) {
return false;
}
value = (int)parsed;
return true;
}
void TBeam1WBoard::loadFanPrefs() {
if (!_prefs) return;
FanMode mode = FAN_AUTO;
char buf[12];
buf[0] = 0;
if (_prefs->getByKey("fan", buf, 11)) {
if (strcmp(buf, "off") == 0) mode = FAN_OFF;
else if (strcmp(buf, "on") == 0) mode = FAN_ON;
}
int lo = FAN_DEFAULT_LO_C;
int hi = FAN_DEFAULT_HI_C;
buf[0] = 0;
if (_prefs->getByKey("fan_lo", buf, 11)) lo = atoi(buf);
buf[0] = 0;
if (_prefs->getByKey("fan_hi", buf, 11)) hi = atoi(buf);
portENTER_CRITICAL(&_fan_mux);
_mode = mode;
if (lo >= 0 && hi <= 120 && lo < hi) {
_lo_c = lo;
_hi_c = hi;
}
portEXIT_CRITICAL(&_fan_mux);
}
void TBeam1WBoard::attachDynamicPrefs(KeyValueStore* prefs) {
_prefs = prefs;
loadFanPrefs();
updateFan();
}
const char* TBeam1WBoard::modeNameLocked() const {
if (_mode == FAN_AUTO) return "auto";
if (_mode == FAN_OFF) return "off";
return "on";
}
bool TBeam1WBoard::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
(void)sender_timestamp;
if (strcmp(command, "get fan") == 0) {
portENTER_CRITICAL(&_fan_mux);
int cd = cooldownSecsLocked();
float temp_c = _temp_c;
bool enabled = _fan_on;
const char* mode = modeNameLocked();
portEXIT_CRITICAL(&_fan_mux);
if (ntcImplausible(temp_c)) {
sprintf(reply, "> %s n/a fan=%s cd=%ds", mode, enabled ? "on" : "off", cd);
} else {
sprintf(reply, "> %s %.1fC fan=%s cd=%ds",
mode, (double)temp_c, enabled ? "on" : "off", cd);
}
return true;
}
if (strncmp(command, "set fan.lo ", 11) == 0) {
int lo;
portENTER_CRITICAL(&_fan_mux);
int hi_limit = _hi_c;
portEXIT_CRITICAL(&_fan_mux);
if (!parseIntArg(&command[11], lo) || lo < 0 || lo >= hi_limit || lo > 100) {
strcpy(reply, "Error: fan.lo must be 0..100 and < fan.hi");
} else if (!persistKey("fan_lo", &command[11])) {
strcpy(reply, "Error: failed to save fan.lo");
} else {
portENTER_CRITICAL(&_fan_mux);
_lo_c = lo;
portEXIT_CRITICAL(&_fan_mux);
sprintf(reply, "OK - fan.lo %d", lo);
}
return true;
}
if (strncmp(command, "set fan.hi ", 11) == 0) {
int hi;
portENTER_CRITICAL(&_fan_mux);
int lo_limit = _lo_c;
portEXIT_CRITICAL(&_fan_mux);
if (!parseIntArg(&command[11], hi) || hi <= lo_limit || hi > 120) {
strcpy(reply, "Error: fan.hi must be > fan.lo and <= 120");
} else if (!persistKey("fan_hi", &command[11])) {
strcpy(reply, "Error: failed to save fan.hi");
} else {
portENTER_CRITICAL(&_fan_mux);
_hi_c = hi;
portEXIT_CRITICAL(&_fan_mux);
sprintf(reply, "OK - fan.hi %d", hi);
}
return true;
}
if (strncmp(command, "set fan ", 8) == 0) {
const char* arg = &command[8];
if (strcmp(arg, "on") == 0) {
if (!persistKey("fan", "on")) {
strcpy(reply, "Error: failed to save fan mode");
} else {
portENTER_CRITICAL(&_fan_mux);
_mode = FAN_ON;
if (!_stopped) setFanOutputLocked(true);
portEXIT_CRITICAL(&_fan_mux);
strcpy(reply, "OK - fan on");
}
} else if (strcmp(arg, "off") == 0) {
if (!persistKey("fan", "off")) {
strcpy(reply, "Error: failed to save fan mode");
} else {
portENTER_CRITICAL(&_fan_mux);
_mode = FAN_OFF;
setFanOutputLocked(false);
portEXIT_CRITICAL(&_fan_mux);
strcpy(reply, "OK - fan off");
}
} else if (strcmp(arg, "auto") == 0) {
if (!persistKey("fan", "auto")) {
strcpy(reply, "Error: failed to save fan mode");
} else {
portENTER_CRITICAL(&_fan_mux);
_mode = FAN_AUTO;
portEXIT_CRITICAL(&_fan_mux);
updateFan();
strcpy(reply, "OK - fan auto");
}
} else {
strcpy(reply, "Error: fan must be on, off, or auto");
}
return true;
}
return false;
}
+33 -1
View File
@@ -1,6 +1,8 @@
#pragma once
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <helpers/ESP32Board.h>
#include "variant.h"
@@ -28,18 +30,48 @@
// - Battery must support 2A+ discharge for high-power TX
class TBeam1WBoard : public ESP32Board {
public:
enum FanMode { FAN_ON, FAN_OFF, FAN_AUTO };
private:
bool radio_powered = false;
bool _stopped = false;
KeyValueStore* _prefs = nullptr;
FanMode _mode = FAN_AUTO;
int _lo_c = FAN_DEFAULT_LO_C;
int _hi_c = FAN_DEFAULT_HI_C;
bool _thermal_on = false; // onoff hysteresis; TX boost must not latch this
bool _fan_on = true;
float _temp_c = NAN;
bool _tx_active = false;
bool _tx_cooldown_active = false;
uint32_t _tx_until_ms = 0;
TaskHandle_t _fan_task = nullptr;
mutable portMUX_TYPE _fan_mux = portMUX_INITIALIZER_UNLOCKED;
void startFanTask();
void updateFan();
void setFanOutputLocked(bool enabled);
float readNtcTempC();
int cooldownSecsLocked();
bool isTxCoolingLocked(uint32_t now);
bool ntcImplausible(float temp_c) const;
bool persistKey(const char* key, const char* value);
static bool parseIntArg(const char* text, int& value);
void loadFanPrefs();
const char* modeNameLocked() const;
static void fanTaskThunk(void* arg);
public:
void begin();
void attachDynamicPrefs(KeyValueStore* prefs);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
void onBeforeTransmit() override;
void onAfterTransmit() override;
uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override;
void powerOff() override;
// Fan control methods
void setFanEnabled(bool enabled);
bool isFanEnabled() const;
};
+2
View File
@@ -153,6 +153,7 @@ extends = LilyGo_TBeam_1W
build_flags =
${LilyGo_TBeam_1W.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
@@ -165,6 +166,7 @@ build_flags =
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TBeam_1W.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
+12 -2
View File
@@ -77,11 +77,21 @@
#define BATTERY_SENSE_SAMPLES 30
#define ADC_MULTIPLIER 3.0
// NTC temperature sensor
// NTC thermistor (Murata NCP18XH103F03RB, 10k, B25/50=3380K)
// Divider: 3.3V -> NTC -> GPIO14 -> 10k pull-down -> GND (Vadc rises with temp)
#define NTC_PIN 14
#define NTC_B 3380.0f
#define NTC_R25 10000.0f
#define NTC_R_FIXED 10000.0f
#define NTC_VCC_MV 3300.0f
// Fan control
// Fan control (GPIO41). NTC is PA-adjacent PCB temp, not die temp, so trip
// well below the SX1262/ESP32 85C operating limit. Hardware testing confirmed
// that this fan/MOSFET path is on/off; PWM below 100% does not spin the fan.
#define FAN_CTRL_PIN 41
#define FAN_TX_COOLDOWN_MS 15000
#define FAN_DEFAULT_LO_C 30 // off below typical indoor idle (~86F)
#define FAN_DEFAULT_HI_C 36 // on at ~97F PCB; still far below 85C chip ratings
// PA Ramp Time - T-Beam 1W requires >800us stabilization (default is 200us)
// Value 0x05 = RADIOLIB_SX126X_PA_RAMP_800U
@@ -148,6 +148,7 @@ extends = T_Beam_S3_Supreme_SX1262
build_flags =
${T_Beam_S3_Supreme_SX1262.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
@@ -160,6 +161,7 @@ build_flags =
; -D CORE_DEBUG_LEVEL=4
build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
@@ -132,6 +132,7 @@ extends = LilyGo_TLora_V2_1_1_6
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=160
-D MAX_GROUP_CHANNELS=8
-D WIFI_SSID='"ssid"'
@@ -140,6 +141,7 @@ build_flags =
-D OFFLINE_QUEUE_SIZE=128
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
+4 -2
View File
@@ -27,8 +27,8 @@ build_flags =
-D PIN_USER_BTN=0
-D PIN_VEXT_EN=45
-D PIN_VEXT_EN_ACTIVE=HIGH
-D LORA_TX_POWER=4
-D MAX_LORA_TX_POWER=4 ; GC1109 datasheet says max input at TX port is +5dbm, confirmed full saturation seems to occur at around 3-4dbm
-D LORA_TX_POWER=5 ; default to 5, which gives ~22dbm
-D MAX_LORA_TX_POWER=13 ; tested with tinySA, 13 gives ~28dbm
-D PIN_GPS_RX=38
-D PIN_GPS_TX=39
-D PIN_GPS_RESET=42
@@ -171,6 +171,7 @@ extends = meshnology_w12
build_flags =
${meshnology_w12.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
@@ -184,6 +185,7 @@ build_src_filter = ${meshnology_w12.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
@@ -143,6 +143,7 @@ extends = nibble_screen_connect_base
build_flags =
${nibble_screen_connect_base.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=300
-D MAX_GROUP_CHANNELS=8
@@ -154,6 +155,7 @@ build_src_filter = ${nibble_screen_connect_base.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
@@ -140,6 +140,7 @@ extends = nibble_zero_connect_base
build_flags =
${nibble_zero_connect_base.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=300
-D MAX_GROUP_CHANNELS=8
@@ -150,6 +151,7 @@ build_src_filter = ${nibble_zero_connect_base.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
+2
View File
@@ -172,6 +172,7 @@ extends = rak3112
build_flags =
${rak3112.build_flags}
-I examples/companion_radio/ui-orig
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D WIFI_DEBUG_LOGGING=1
@@ -182,6 +183,7 @@ build_flags =
; -D MESH_DEBUG=1
build_src_filter = ${rak3112.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-orig/*.cpp>
lib_deps =
+1
View File
@@ -6,6 +6,7 @@ build_flags = ${nrf52_base.build_flags}
${sensor_base.build_flags}
-I variants/rak3401
-D RAK_3401
-D RAK_BOARD
-D NRF52_POWER_MANAGEMENT
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
+2 -2
View File
@@ -188,8 +188,8 @@ static const uint8_t AREF = PIN_AREF;
// Power is on the controllable 3V3_S rail
#define PIN_GPS_PPS (17) // Pulse per second input from the GPS
#define PIN_GPS_RX PIN_SERIAL1_RX
#define PIN_GPS_TX PIN_SERIAL1_TX
#define PIN_GPS_TX PIN_SERIAL1_RX
#define PIN_GPS_RX PIN_SERIAL1_TX
#define PIN_GPS_1PPS PIN_GPS_PPS
#define GPS_BAUD_RATE 9600
+3
View File
@@ -2,6 +2,7 @@
#include <MeshCore.h>
#include <Arduino.h>
#include <helpers/KeyValueStore.h>
// built-ins
#define PIN_VBAT_READ 26
@@ -16,6 +17,8 @@ public:
void begin();
uint8_t getStartupReason() const override { return startup_reason; }
void attachDynamicPrefs(KeyValueStore* prefs) { } // no-op
void onBeforeTransmit() override {
digitalWrite(LED_BUILTIN, HIGH); // turn TX LED on
}
+53 -27
View File
@@ -67,34 +67,60 @@ lib_deps = ${rpi_picow.lib_deps}
densaugeo/base64 @ ~1.4.0
lib_ignore = BLE
; [env:PicoW_companion_radio_ble]
; extends = rpi_picow
; build_flags = ${rpi_picow.build_flags}
; -D MAX_CONTACTS=100
; -D MAX_GROUP_CHANNELS=8
; -D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; ; -D MESH_PACKET_LOGGING=1
; ; -D MESH_DEBUG=1
; build_src_filter = ${rpi_picow.build_src_filter}
; +<../examples/companion_radio/*.cpp>
; lib_deps = ${rpi_picow.lib_deps}
; densaugeo/base64 @ ~1.4.0
[env:PicoW_companion_radio_ble]
extends = rpi_picow
build_flags = ${rpi_picow.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D PIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${rpi_picow.build_src_filter}
+<helpers/rp2040/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp>
lib_deps = ${rpi_picow.lib_deps}
densaugeo/base64 @ ~1.4.0
; [env:PicoW_companion_radio_wifi]
; extends = rpi_picow
; build_flags = ${rpi_picow.build_flags}
; -D MAX_CONTACTS=100
; -D MAX_GROUP_CHANNELS=8
; -D WIFI_DEBUG_LOGGING=1
; -D WIFI_SSID='"myssid"'
; -D WIFI_PWD='"mypwd"'
; ; -D MESH_PACKET_LOGGING=1
; ; -D MESH_DEBUG=1
; build_src_filter = ${rpi_picow.build_src_filter}
; +<../examples/companion_radio/*.cpp>
; lib_deps = ${rpi_picow.lib_deps}
; densaugeo/base64 @ ~1.4.0
; USB + WiFi + BLE together; the interface manager fans frames out to all of them
[env:PicoW_companion_radio]
extends = rpi_picow
build_flags = ${rpi_picow.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D ENABLE_USB_INTERFACE
-D ENABLE_WIFI_INTERFACE
-D PIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH
-D BLE_PIN_CODE=123456
; NOTE: DO NOT ENABLE --> -D BLE_DEBUG_LOGGING=1 (shares Serial with the USB interface)
; NOTE: DO NOT ENABLE --> -D WIFI_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${rpi_picow.build_src_filter}
+<helpers/rp2040/SerialBLEInterface.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
lib_deps = ${rpi_picow.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:PicoW_companion_radio_wifi]
extends = rpi_picow
build_flags = ${rpi_picow.build_flags}
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D WIFI_DEBUG_LOGGING=1
-D WIFI_SSID='""' ; no default network, configure with 'set wifi.ssid' / 'set wifi.pwd'
-D WIFI_PWD='""'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${rpi_picow.build_src_filter}
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
lib_deps = ${rpi_picow.lib_deps}
densaugeo/base64 @ ~1.4.0
lib_ignore = BLE
[env:PicoW_terminal_chat]
extends = rpi_picow
+2
View File
@@ -224,6 +224,7 @@ extends = Station_G2
build_flags =
${Station_G2.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D WIFI_DEBUG_LOGGING=1
@@ -234,6 +235,7 @@ build_flags =
; -D MESH_DEBUG=1
build_src_filter = ${Station_G2.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
+80 -8
View File
@@ -21,10 +21,6 @@ bool StationG3Board::setLoRaFemLnaEnabled(bool enable) {
return true;
}
bool StationG3Board::canControlLoRaFemLna() const {
return loRaFEMControl.canControlLNA();
}
bool StationG3Board::isLoRaFemLnaEnabled() const {
return loRaFEMControl.isLNAEnabled();
}
@@ -37,10 +33,86 @@ bool StationG3Board::setLoRaFemPaGainEnabled(bool enable) {
return true;
}
bool StationG3Board::canControlLoRaFemPaGain() const {
return loRaFEMControl.canControlPAGain();
}
bool StationG3Board::isLoRaFemPaGainEnabled() const {
return loRaFEMControl.isPAGainEnabled();
}
void StationG3Board::attachDynamicPrefs(KeyValueStore* prefs) {
_prefs = prefs;
char gain[8];
gain[0] = 0;
_prefs->getByKey("fem_rxgain", gain, 7); // get initial values
setLoRaFemLnaEnabled(strcmp(gain, "1") == 0);
gain[0] = 0;
_prefs->getByKey("fem_txgain", gain, 7); // get initial values
setLoRaFemPaGainEnabled(strcmp(gain, "1") == 0);
}
bool StationG3Board::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
if (strcmp(command, "get radio.fem.rxgain") == 0) {
if (!loRaFEMControl.canControlLNA()) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "> %s", isLoRaFemLnaEnabled() ? "on" : "off");
}
return true;
}
if (memcmp(command, "set radio.fem.rxgain ", 21) == 0) {
if (!loRaFEMControl.canControlLNA()) {
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemLnaEnabled(true)) {
_prefs->setByKey("fem_rxgain", "1");
strcpy(reply, "OK - LoRa FEM RX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemLnaEnabled(false)) {
_prefs->setByKey("fem_rxgain", "0");
strcpy(reply, "OK - LoRa FEM RX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
}
} else {
strcpy(reply, "Error: state must be on or off");
}
return true;
}
if (strcmp(command, "get radio.fem.txgain") == 0) {
if (!loRaFEMControl.canControlPAGain()) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "> %s", isLoRaFemPaGainEnabled() ? "on" : "off");
}
return true;
}
if (memcmp(command, "set radio.fem.txgain ", 21) == 0) {
if (!loRaFEMControl.canControlPAGain()) {
strcpy(reply, "Error: unsupported");
} else if (memcmp(&command[21], "on", 2) == 0) {
if (setLoRaFemPaGainEnabled(true)) {
_prefs->setByKey("fem_txgain", "1");
strcpy(reply, "OK - LoRa FEM TX gain on");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM TX gain");
}
} else if (memcmp(&command[21], "off", 3) == 0) {
if (setLoRaFemPaGainEnabled(false)) {
_prefs->setByKey("fem_txgain", "0");
strcpy(reply, "OK - LoRa FEM TX gain off");
} else {
strcpy(reply, "Error: failed to apply LoRa FEM TX gain");
}
} else {
strcpy(reply, "Error: state must be on or off");
}
return true;
}
return false; // not handled
}
+10 -7
View File
@@ -6,6 +6,12 @@
#include "LoRaFEMControl.h"
class StationG3Board : public ESP32Board {
KeyValueStore* _prefs = NULL;
bool setLoRaFemLnaEnabled(bool enable);
bool isLoRaFemLnaEnabled() const;
bool setLoRaFemPaGainEnabled(bool enable);
bool isLoRaFemPaGainEnabled() const;
public:
LoRaFEMControl loRaFEMControl;
@@ -25,6 +31,10 @@ public:
}
}
void attachDynamicPrefs(KeyValueStore* prefs);
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
void setPrimaryLNAEnable(bool enabled) {
loRaFEMControl.setLNAEnable(enabled);
}
@@ -43,13 +53,6 @@ public:
loRaFEMControl.setRxModeEnable();
}
bool setLoRaFemLnaEnabled(bool enable) override;
bool canControlLoRaFemLna() const override;
bool isLoRaFemLnaEnabled() const override;
bool setLoRaFemPaGainEnabled(bool enable) override;
bool canControlLoRaFemPaGain() const override;
bool isLoRaFemPaGainEnabled() const override;
void powerOff() override;
uint16_t getBattMilliVolts() override {
+2
View File
@@ -139,6 +139,7 @@ extends = Station_G3_ESP32
build_flags =
${Station_G3_ESP32.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D WIFI_DEBUG_LOGGING=1
@@ -149,6 +150,7 @@ build_flags =
; -D MESH_DEBUG=1
build_src_filter = ${Station_G3_ESP32.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
+2
View File
@@ -176,6 +176,7 @@ extends = ThinkNode_M2
build_flags =
${ThinkNode_M2.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
@@ -184,6 +185,7 @@ build_flags =
-D WIFI_PWD='"mypwd"'
build_src_filter = ${ThinkNode_M2.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/ui/buzzer.cpp>
+<../examples/companion_radio/*.cpp>
+2
View File
@@ -190,6 +190,7 @@ extends = ThinkNode_M5
build_flags =
${ThinkNode_M5.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
@@ -198,6 +199,7 @@ build_flags =
-D WIFI_PWD='"mypwd"'
build_src_filter = ${ThinkNode_M5.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
+2
View File
@@ -135,6 +135,7 @@ extends = ThinkNode_M7
build_flags =
${ThinkNode_M7.build_flags}
-I examples/companion_radio/ui-orig
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D DISPLAY_CLASS=NullDisplayDriver
@@ -145,6 +146,7 @@ build_flags =
; -D MESH_PACKET_LOGGING=1
build_src_filter = ${ThinkNode_M7.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/ui/NullDisplayDriver.cpp>
+<../examples/companion_radio/*.cpp>
-1
View File
@@ -22,7 +22,6 @@ build_flags = ${nrf52_base.build_flags}
-D UI_HAS_ROTARY_INPUT=1
-D UI_HAS_NAV_INPUT=1
-D UI_RECENT_LIST_SIZE=9
-D UI_TZ_OFFSET=-4 # GMT-4
-D UI_MSG_PREVIEW_SIZE=165
-D EINK_MAX_PARTIAL_REFRESH=60
-D EINK_DISPLAY_MODEL=GxEPD2_154_D67
+2
View File
@@ -137,6 +137,7 @@ extends = ThinkNode_M9
build_flags =
${ThinkNode_M9.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D WIFI_DEBUG_LOGGING=1
@@ -146,6 +147,7 @@ build_flags =
; -D MESH_PACKET_LOGGING=1
build_src_filter = ${ThinkNode_M9.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<helpers/ui/buzzer.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<../examples/companion_radio/*.cpp>
@@ -2,6 +2,7 @@
#include <Arduino.h>
#include <MeshCore.h>
#include <helpers/KeyValueStore.h>
// LoRa radio module pins for Waveshare RP2040-LoRa-HF/LF
// https://files.waveshare.com/wiki/RP2040-LoRa/Rp2040-lora-sch.pdf
@@ -32,6 +33,8 @@ public:
void begin();
uint8_t getStartupReason() const override { return startup_reason; }
void attachDynamicPrefs(KeyValueStore* prefs) { } // no-op
#ifdef P_LORA_TX_LED
void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); }
void onAfterTransmit() override { digitalWrite(P_LORA_TX_LED, LOW); }
+2
View File
@@ -65,6 +65,7 @@ build_flags = ${WioTrackerL1.build_flags}
-D MAX_GROUP_CHANNELS=40
-D DISPLAY_CLASS=SH1106Display
-D UI_HAS_JOYSTICK=1
-D UI_NO_HIBERNATE
-D OFFLINE_QUEUE_SIZE=256
-D PIN_BUZZER=12
-D QSPIFLASH=1
@@ -95,6 +96,7 @@ build_flags = ${WioTrackerL1.build_flags}
-D OFFLINE_QUEUE_SIZE=256
-D DISPLAY_CLASS=SH1106Display
-D UI_HAS_JOYSTICK=1
-D UI_NO_HIBERNATE
-D PIN_BUZZER=12
-D QSPIFLASH=1
-D ADVERT_NAME='"@@MAC"'
+3
View File
@@ -73,6 +73,7 @@ lib_deps =
[env:Xiao_C3_companion_radio_ble]
extends = Xiao_esp32_C3
board_build.partitions = min_spiffs.csv ; get around 4mb flash limit
build_src_filter = ${Xiao_esp32_C3.build_src_filter}
+<../examples/companion_radio/*.cpp>
+<helpers/esp32/*.cpp>
@@ -114,8 +115,10 @@ extends = Xiao_esp32_C3
build_src_filter = ${Xiao_esp32_C3.build_src_filter}
+<../examples/companion_radio/*.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
build_flags =
${Xiao_esp32_C3.build_flags}
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
+3
View File
@@ -2,6 +2,7 @@
#include <Arduino.h>
#include <MeshCore.h>
#include <helpers/KeyValueStore.h>
/*
* This board has no built-in way to read battery voltage.
@@ -30,6 +31,8 @@ public:
void begin();
uint8_t getStartupReason() const override { return startup_reason; }
void attachDynamicPrefs(KeyValueStore* prefs) { } // no-op
#ifdef P_LORA_TX_LED
void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); }
void onAfterTransmit() override { digitalWrite(P_LORA_TX_LED, LOW); }
+2
View File
@@ -202,11 +202,13 @@ extends = Xiao_S3_WIO
build_src_filter = ${Xiao_S3_WIO.build_src_filter}
+<helpers/ui/NullDisplayDriver.cpp>
+<helpers/esp32/*.cpp>
+<helpers/wifi/*.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<../examples/companion_radio/*.cpp>
build_flags =
${Xiao_S3_WIO.build_flags}
-I examples/companion_radio/ui-new
-D ENABLE_WIFI_INTERFACE
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256