From 018bed76b03936136f8eaae67136785b675e4225 Mon Sep 17 00:00:00 2001 From: Robowarrior834 Date: Mon, 27 Apr 2026 10:33:55 -0400 Subject: [PATCH 1/6] Tbeam 1W Deafness Fix and Fan Control --- examples/simple_repeater/MyMesh.cpp | 3 ++ examples/simple_repeater/main.cpp | 4 ++- examples/simple_room_server/MyMesh.cpp | 3 ++ examples/simple_room_server/main.cpp | 3 ++ variants/lilygo_tbeam_1w/TBeam1WBoard.cpp | 35 +++++++++++++++++------ variants/lilygo_tbeam_1w/TBeam1WBoard.h | 7 +++-- variants/lilygo_tbeam_1w/platformio.ini | 12 ++++++-- variants/lilygo_tbeam_1w/target.cpp | 3 ++ variants/lilygo_tbeam_1w/target.h | 2 +- variants/lilygo_tbeam_1w/variant.h | 3 +- 10 files changed, 60 insertions(+), 15 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 666f79fc5c..e6ca41bb9f 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -888,6 +888,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_max = 64; _prefs.interference_threshold = 0; // disabled + #ifdef DEFAULT_AGC_RESET_INTERVAL + _prefs.agc_reset_interval = DEFAULT_AGC_RESET_INTERVAL; + #endif // bridge defaults _prefs.bridge_enabled = 1; // enabled _prefs.bridge_delay = 500; // milliseconds diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index e37078ce5f..28e2e751db 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -153,7 +153,9 @@ void loop() { ui_task.loop(); #endif rtc_clock.tick(); - + #ifdef P_FAN_CTRL + update_fan_control(); + #endif if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { #if defined(NRF52_PLATFORM) board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 145fb0fd9f..66332494f6 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -644,6 +644,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_advert_interval = 12; // 12 hours _prefs.flood_max = 64; _prefs.interference_threshold = 0; // disabled + #ifdef DEFAULT_AGC_RESET_INTERVAL + _prefs.agc_reset_interval = DEFAULT_AGC_RESET_INTERVAL; + #endif #ifdef ROOM_PASSWORD StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)); #endif diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 825fb007d5..266bacffc0 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -113,4 +113,7 @@ void loop() { ui_task.loop(); #endif rtc_clock.tick(); +#ifdef P_FAN_CTRL + update_fan_control(); +#endif } diff --git a/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp b/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp index 1719d73334..e732096bcf 100644 --- a/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp +++ b/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp @@ -3,30 +3,49 @@ void TBeam1WBoard::begin() { ESP32Board::begin(); - // Power on radio module (must be done before radio init) + // Power on radio module (must be done before radio init). + // Drive LOW first to ensure a clean SX1262 reset — on soft reboot the ESP32 + // GPIO glitch may be too brief to drain module capacitors, leaving the SX1262 + // in a stuck state. Holding LOW for 50ms guarantees a full power cycle. pinMode(SX126X_POWER_EN, OUTPUT); + digitalWrite(SX126X_POWER_EN, LOW); + delay(50); digitalWrite(SX126X_POWER_EN, HIGH); radio_powered = true; delay(10); // Allow radio to power up - // RF switch RXEN pin handled by RadioLib via setRfSwitchPins() + // Explicitly drive RXEN HIGH (LNA on) from boot, before RadioLib takes over. + // Without this the pin floats until the first startReceive() call. + pinMode(SX126X_RXEN, OUTPUT); + digitalWrite(SX126X_RXEN, HIGH); // Initialize LED pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); - // Initialize fan control (on by default - 1W PA can overheat) + // Fan starts off; onAfterTransmit() enables it with a 30s cooldown timer pinMode(FAN_CTRL_PIN, OUTPUT); - digitalWrite(FAN_CTRL_PIN, HIGH); + digitalWrite(FAN_CTRL_PIN, LOW); } void TBeam1WBoard::onBeforeTransmit() { - // RF switching handled by RadioLib via SX126X_DIO2_AS_RF_SWITCH and setRfSwitchPins() - digitalWrite(LED_PIN, HIGH); // TX LED on + digitalWrite(SX126X_RXEN, LOW); // disconnect LNA before PA ramps up + digitalWrite(LED_PIN, HIGH); } void TBeam1WBoard::onAfterTransmit() { - digitalWrite(LED_PIN, LOW); // TX LED off + digitalWrite(SX126X_RXEN, HIGH); // re-enable LNA immediately after TX + digitalWrite(LED_PIN, LOW); + // Keep fan running for 5s after TX + setFanEnabled(true); + _fan_off_millis = millis() + 5000; +} + +void TBeam1WBoard::updateFan() { + if (_fan_off_millis > 0 && (long)(millis() - _fan_off_millis) >= 0) { + setFanEnabled(false); + _fan_off_millis = 0; + } } uint16_t TBeam1WBoard::getBattMilliVolts() { @@ -68,4 +87,4 @@ void TBeam1WBoard::setFanEnabled(bool enabled) { bool TBeam1WBoard::isFanEnabled() const { return digitalRead(FAN_CTRL_PIN) == HIGH; -} +} \ No newline at end of file diff --git a/variants/lilygo_tbeam_1w/TBeam1WBoard.h b/variants/lilygo_tbeam_1w/TBeam1WBoard.h index d999dfd4c1..766ef67632 100644 --- a/variants/lilygo_tbeam_1w/TBeam1WBoard.h +++ b/variants/lilygo_tbeam_1w/TBeam1WBoard.h @@ -30,6 +30,7 @@ class TBeam1WBoard : public ESP32Board { private: bool radio_powered = false; + unsigned long _fan_off_millis = 0; // 0 = no pending fan-off public: void begin(); @@ -39,7 +40,9 @@ class TBeam1WBoard : public ESP32Board { const char* getManufacturerName() const override; void powerOff() override; - // Fan control methods + // Called each main loop iteration to manage fan cooldown after TX + void updateFan(); + void setFanEnabled(bool enabled); bool isFanEnabled() const; -}; +}; \ No newline at end of file diff --git a/variants/lilygo_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini index 7c8453077f..b7ec76990a 100644 --- a/variants/lilygo_tbeam_1w/platformio.ini +++ b/variants/lilygo_tbeam_1w/platformio.ini @@ -1,6 +1,7 @@ [LilyGo_TBeam_1W] extends = esp32_base board = t_beam_1w +board_build.partitions = default_16MB.csv build_flags = ${esp32_base.build_flags} -I variants/lilygo_tbeam_1w @@ -27,10 +28,17 @@ build_flags = -D SX126X_DIO3_TCXO_VOLTAGE=3.0 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 + -D SX126X_REGISTER_PATCH=1 ; Patch register 0x8B5 for improved RX with external PA+LNA FEM ; TX power: 22dBm to SX1262, PA module adds ~10dB for 32dBm total -D LORA_TX_POWER=22 + ; AGC reset interval default: 60 seconds (value * 4000ms). + ; Periodically recalibrates the SX1262 frontend to prevent progressive + ; deafness caused by the receiver getting stuck in a high-attenuation state + ; after repeated TX/RX transitions with the external PA+LNA module. + -D DEFAULT_AGC_RESET_INTERVAL=15 + ; Battery - 2S 7.4V LiPo (6.0V min, 8.4V max) -D BATT_MIN_MILLIVOLTS=6000 -D BATT_MAX_MILLIVOLTS=8400 @@ -154,8 +162,8 @@ build_flags = -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 - -D OFFLINE_QUEUE_SIZE=256 -D WIFI_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 -D WIFI_SSID='"myssid"' -D WIFI_PWD='"mypwd"' -D PERSISTANT_GPS=1 @@ -191,4 +199,4 @@ build_src_filter = ${LilyGo_TBeam_1W.build_src_filter} +<../examples/simple_repeater> lib_deps = ${LilyGo_TBeam_1W.lib_deps} - ${esp32_ota.lib_deps} + ${esp32_ota.lib_deps} \ No newline at end of file diff --git a/variants/lilygo_tbeam_1w/target.cpp b/variants/lilygo_tbeam_1w/target.cpp index 17733deb87..e36d43f615 100644 --- a/variants/lilygo_tbeam_1w/target.cpp +++ b/variants/lilygo_tbeam_1w/target.cpp @@ -63,3 +63,6 @@ mesh::LocalIdentity radio_new_identity() { return mesh::LocalIdentity(&rng); } +void update_fan_control() { + board.updateFan(); +} \ No newline at end of file diff --git a/variants/lilygo_tbeam_1w/target.h b/variants/lilygo_tbeam_1w/target.h index a521332f80..5a1895f02c 100644 --- a/variants/lilygo_tbeam_1w/target.h +++ b/variants/lilygo_tbeam_1w/target.h @@ -25,4 +25,4 @@ uint32_t radio_get_rng_seed(); void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); void radio_set_tx_power(int8_t dbm); mesh::LocalIdentity radio_new_identity(); - +void update_fan_control(); \ No newline at end of file diff --git a/variants/lilygo_tbeam_1w/variant.h b/variants/lilygo_tbeam_1w/variant.h index f6807e56b4..40c0103c9f 100644 --- a/variants/lilygo_tbeam_1w/variant.h +++ b/variants/lilygo_tbeam_1w/variant.h @@ -82,6 +82,7 @@ // Fan control #define FAN_CTRL_PIN 41 +#define P_FAN_CTRL FAN_CTRL_PIN // enables update_fan_control() in main loop // PA Ramp Time - T-Beam 1W requires >800us stabilization (default is 200us) // Value 0x05 = RADIOLIB_SX126X_PA_RAMP_800U @@ -93,4 +94,4 @@ #define OLED_HEIGHT 64 // 32768 Hz crystal present -#define HAS_32768HZ 1 +#define HAS_32768HZ 1 \ No newline at end of file From be9f6744763fdd59f807818168d94ab11fb807a6 Mon Sep 17 00:00:00 2001 From: Robowarrior834 Date: Mon, 27 Apr 2026 10:41:15 -0400 Subject: [PATCH 2/6] Fixed Fan time to be 10 seconds --- variants/lilygo_tbeam_1w/TBeam1WBoard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp b/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp index e732096bcf..fae682342e 100644 --- a/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp +++ b/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp @@ -36,9 +36,9 @@ void TBeam1WBoard::onBeforeTransmit() { void TBeam1WBoard::onAfterTransmit() { digitalWrite(SX126X_RXEN, HIGH); // re-enable LNA immediately after TX digitalWrite(LED_PIN, LOW); - // Keep fan running for 5s after TX + // Keep fan running for 10s after TX setFanEnabled(true); - _fan_off_millis = millis() + 5000; + _fan_off_millis = millis() + 10000; } void TBeam1WBoard::updateFan() { From 2933d9edc75371c9218f3261df9bbd4dd4300049 Mon Sep 17 00:00:00 2001 From: Robowarrior834 Date: Mon, 27 Apr 2026 10:46:13 -0400 Subject: [PATCH 3/6] Added Fan control main loop to the companion main.cpp --- examples/companion_radio/main.cpp | 3 +++ variants/lilygo_tbeam_1w/platformio.ini | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 876dc9c33c..5e9e9d90aa 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -229,4 +229,7 @@ void loop() { ui_task.loop(); #endif rtc_clock.tick(); +#ifdef P_FAN_CTRL + update_fan_control(); +#endif } diff --git a/variants/lilygo_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini index b7ec76990a..2d1713a538 100644 --- a/variants/lilygo_tbeam_1w/platformio.ini +++ b/variants/lilygo_tbeam_1w/platformio.ini @@ -41,7 +41,7 @@ build_flags = ; Battery - 2S 7.4V LiPo (6.0V min, 8.4V max) -D BATT_MIN_MILLIVOLTS=6000 - -D BATT_MAX_MILLIVOLTS=8400 + -D BATT_MAX_MILLIVOLTS=7900 ; Display - SH1106 OLED at 0x3C -D DISPLAY_CLASS=SH1106Display From 6977a1da08ed26b920e7b5c747458b1f65e05dd9 Mon Sep 17 00:00:00 2001 From: Robowarrior834 Date: Mon, 27 Apr 2026 10:46:39 -0400 Subject: [PATCH 4/6] set bat back to default --- variants/lilygo_tbeam_1w/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/lilygo_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini index 2d1713a538..108bc100cd 100644 --- a/variants/lilygo_tbeam_1w/platformio.ini +++ b/variants/lilygo_tbeam_1w/platformio.ini @@ -41,7 +41,7 @@ build_flags = ; Battery - 2S 7.4V LiPo (6.0V min, 8.4V max) -D BATT_MIN_MILLIVOLTS=6000 - -D BATT_MAX_MILLIVOLTS=7900 + -D BATT_MAX_MILLIVOLTS=8200 ; Display - SH1106 OLED at 0x3C -D DISPLAY_CLASS=SH1106Display From 414b5dcb7e3588dbb8e28d616513fdaa785e3e95 Mon Sep 17 00:00:00 2001 From: Robowarrior834 Date: Tue, 28 Apr 2026 08:42:39 -0400 Subject: [PATCH 5/6] Converted fan update to a mainboard loop function Co-authored-by: Copilot --- examples/companion_radio/main.cpp | 4 +--- examples/simple_repeater/main.cpp | 5 ++--- examples/simple_room_server/main.cpp | 4 +--- variants/lilygo_tbeam_1w/TBeam1WBoard.cpp | 4 +++- variants/lilygo_tbeam_1w/TBeam1WBoard.h | 1 + 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 5e9e9d90aa..c7ef5aec46 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -225,11 +225,9 @@ void setup() { void loop() { the_mesh.loop(); sensors.loop(); + board.loop(); #ifdef DISPLAY_CLASS ui_task.loop(); #endif rtc_clock.tick(); -#ifdef P_FAN_CTRL - update_fan_control(); -#endif } diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 28e2e751db..774ba87b5b 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -149,13 +149,12 @@ void loop() { the_mesh.loop(); sensors.loop(); + board.loop(); #ifdef DISPLAY_CLASS ui_task.loop(); #endif rtc_clock.tick(); - #ifdef P_FAN_CTRL - update_fan_control(); - #endif + if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { #if defined(NRF52_PLATFORM) board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 266bacffc0..6e9ca6307b 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -109,11 +109,9 @@ void loop() { the_mesh.loop(); sensors.loop(); + board.loop(); #ifdef DISPLAY_CLASS ui_task.loop(); #endif rtc_clock.tick(); -#ifdef P_FAN_CTRL - update_fan_control(); -#endif } diff --git a/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp b/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp index fae682342e..c4632d86f7 100644 --- a/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp +++ b/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp @@ -40,7 +40,9 @@ void TBeam1WBoard::onAfterTransmit() { setFanEnabled(true); _fan_off_millis = millis() + 10000; } - +void TBeam1WBoard::loop() { + updateFan(); +} void TBeam1WBoard::updateFan() { if (_fan_off_millis > 0 && (long)(millis() - _fan_off_millis) >= 0) { setFanEnabled(false); diff --git a/variants/lilygo_tbeam_1w/TBeam1WBoard.h b/variants/lilygo_tbeam_1w/TBeam1WBoard.h index 766ef67632..01705c12fe 100644 --- a/variants/lilygo_tbeam_1w/TBeam1WBoard.h +++ b/variants/lilygo_tbeam_1w/TBeam1WBoard.h @@ -34,6 +34,7 @@ class TBeam1WBoard : public ESP32Board { public: void begin(); + void loop(); void onBeforeTransmit() override; void onAfterTransmit() override; uint16_t getBattMilliVolts() override; From 73f473121e1e23ce4c60dcdf1c2a25e2a73573cf Mon Sep 17 00:00:00 2001 From: Robowarrior834 Date: Tue, 28 Apr 2026 09:28:23 -0400 Subject: [PATCH 6/6] Added new virtual loop function so other boards compile normally. This is needed for the TBeam1W board to update the fan state. --- src/MeshCore.h | 2 +- variants/lilygo_tbeam_1w/TBeam1WBoard.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MeshCore.h b/src/MeshCore.h index 2db1d4c3ec..e4437ca330 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -58,7 +58,7 @@ class MainBoard { virtual uint8_t getStartupReason() const = 0; virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; } virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported - + virtual void loop() { /* no op */ } // override to implement periodic tasks in the main loop // Power management interface (boards with power management override these) virtual bool isExternalPowered() { return false; } virtual uint16_t getBootVoltage() { return 0; } diff --git a/variants/lilygo_tbeam_1w/TBeam1WBoard.h b/variants/lilygo_tbeam_1w/TBeam1WBoard.h index 01705c12fe..4b055c5858 100644 --- a/variants/lilygo_tbeam_1w/TBeam1WBoard.h +++ b/variants/lilygo_tbeam_1w/TBeam1WBoard.h @@ -34,7 +34,7 @@ class TBeam1WBoard : public ESP32Board { public: void begin(); - void loop(); + void loop() override; void onBeforeTransmit() override; void onAfterTransmit() override; uint16_t getBattMilliVolts() override;