diff --git a/arch/nrf52/extra_scripts/patch_bluefruit.py b/arch/nrf52/extra_scripts/patch_bluefruit.py index b43bffb5db..af0b7be0c7 100644 --- a/arch/nrf52/extra_scripts/patch_bluefruit.py +++ b/arch/nrf52/extra_scripts/patch_bluefruit.py @@ -7,6 +7,8 @@ Patches applied: 1. BLEConnection.h: Add _hvn_qsize member to track semaphore queue size 2. BLEConnection.cpp: Store hvn_qsize and restore semaphore on disconnect +3. bluefruit.h: Add bluefruit_blinky_cb as friend so it can access _led_conn +4. bluefruit.cpp: Guard LED toggle in blinky callback with _led_conn check Bug description: - When a BLE central disconnects unexpectedly (reason=8 supervision timeout), @@ -133,6 +135,89 @@ def _patch_ble_connection_source(source: Path) -> bool: return False +def _patch_bluefruit_header_led(source: Path) -> bool: + """ + Add bluefruit_blinky_cb as a friend function so it can access _led_conn. + + Without this, the blink timer callback toggles LED_BLUE unconditionally, + ignoring autoConnLed(false). + + Returns True if patch was applied or already applied, False on error. + """ + try: + content = source.read_text() + + # Check if already patched + if "bluefruit_blinky_cb" in content: + return True # Already patched + + original_pattern = ''' friend void adafruit_soc_task(void* arg); + friend class BLECentral;''' + + patched_pattern = ''' friend void adafruit_soc_task(void* arg); + friend void bluefruit_blinky_cb(TimerHandle_t xTimer); + friend class BLECentral;''' + + if original_pattern not in content: + print("Bluefruit patch: WARNING - bluefruit.h friend pattern not found") + return False + + content = content.replace(original_pattern, patched_pattern) + source.write_text(content) + + if "bluefruit_blinky_cb" not in source.read_text(): + return False + + return True + except Exception as e: + print(f"Bluefruit patch: ERROR patching bluefruit.h: {e}") + return False + + +def _patch_bluefruit_source_led(source: Path) -> bool: + """ + Guard the LED toggle in bluefruit_blinky_cb with a _led_conn check. + + The blink timer can be inadvertently started by Advertising.start() -> + setConnLedInterval() -> xTimerChangePeriod() (FreeRTOS starts dormant + timers as a side effect). Without this guard, the LED toggles even when + autoConnLed(false) has been called. + + Returns True if patch was applied or already applied, False on error. + """ + try: + content = source.read_text() + + # Check if already patched + if "Bluefruit._led_conn" in content: + return True # Already patched + + original_pattern = '''static void bluefruit_blinky_cb( TimerHandle_t xTimer ) +{ + (void) xTimer; + digitalToggle(LED_BLUE);''' + + patched_pattern = '''void bluefruit_blinky_cb( TimerHandle_t xTimer ) +{ + (void) xTimer; + if ( Bluefruit._led_conn ) digitalToggle(LED_BLUE);''' + + if original_pattern not in content: + print("Bluefruit patch: WARNING - bluefruit.cpp blinky_cb pattern not found") + return False + + content = content.replace(original_pattern, patched_pattern) + source.write_text(content) + + if "Bluefruit._led_conn" not in source.read_text(): + return False + + return True + except Exception as e: + print(f"Bluefruit patch: ERROR patching bluefruit.cpp: {e}") + return False + + def _apply_bluefruit_patches(target, source, env): # pylint: disable=unused-argument framework_path = env.get("PLATFORMFW_DIR") if not framework_path: @@ -185,6 +270,44 @@ def _apply_bluefruit_patches(target, source, env): # pylint: disable=unused-arg print(f"Bluefruit patch: ERROR - BLEConnection.cpp not found at {conn_source}") patch_failed = True + # Patch bluefruit.h - add friend declaration for blinky callback + bf_header = bluefruit_lib / "bluefruit.h" + if bf_header.exists(): + before = bf_header.read_text() + success = _patch_bluefruit_header_led(bf_header) + after = bf_header.read_text() + + if success: + if before != after: + print("Bluefruit patch: OK - Applied bluefruit.h fix (added blinky_cb friend)") + else: + print("Bluefruit patch: OK - bluefruit.h already patched") + else: + print("Bluefruit patch: FAILED - bluefruit.h") + patch_failed = True + else: + print(f"Bluefruit patch: ERROR - bluefruit.h not found at {bf_header}") + patch_failed = True + + # Patch bluefruit.cpp - guard LED toggle with _led_conn check + bf_source = bluefruit_lib / "bluefruit.cpp" + if bf_source.exists(): + before = bf_source.read_text() + success = _patch_bluefruit_source_led(bf_source) + after = bf_source.read_text() + + if success: + if before != after: + print("Bluefruit patch: OK - Applied bluefruit.cpp fix (guard blinky_cb with _led_conn)") + else: + print("Bluefruit patch: OK - bluefruit.cpp already patched") + else: + print("Bluefruit patch: FAILED - bluefruit.cpp") + patch_failed = True + else: + print(f"Bluefruit patch: ERROR - bluefruit.cpp not found at {bf_source}") + patch_failed = True + if patch_failed: print("Bluefruit patch: CRITICAL - Patch failed! Build aborted.") env.Exit(1) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index d9ebacb418..d9b20a75e3 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -230,6 +230,9 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86 file.read((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87 file.read((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88 + file.read((uint8_t *)&_prefs.led_ble_mode, sizeof(_prefs.led_ble_mode)); // 89 + file.read((uint8_t *)&_prefs.led_status_mode, sizeof(_prefs.led_status_mode)); // 90 + file.read((uint8_t *)&_prefs.led_activity_mode, sizeof(_prefs.led_activity_mode)); // 91 file.close(); } @@ -267,6 +270,9 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86 file.write((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87 file.write((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88 + file.write((uint8_t *)&_prefs.led_ble_mode, sizeof(_prefs.led_ble_mode)); // 89 + file.write((uint8_t *)&_prefs.led_status_mode, sizeof(_prefs.led_status_mode)); // 90 + file.write((uint8_t *)&_prefs.led_activity_mode, sizeof(_prefs.led_activity_mode)); // 91 file.close(); } diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 1f71a9bc6c..cfae986727 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -859,6 +859,15 @@ void MyMesh::begin(bool has_display) { _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, -9, MAX_LORA_TX_POWER); _prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1 _prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours + _prefs.led_ble_mode = constrain(_prefs.led_ble_mode, 0, 3); + _prefs.led_status_mode = constrain(_prefs.led_status_mode, 0, 1); + _prefs.led_activity_mode = constrain(_prefs.led_activity_mode, 0, 1); + + // Sync LED prefs to sensor manager (exposed as custom vars) + sensors.led_ble_mode = _prefs.led_ble_mode; + sensors.led_status_mode = _prefs.led_status_mode; + sensors.led_activity_mode = _prefs.led_activity_mode; + board.activity_led_enabled = (_prefs.led_activity_mode == LED_ACTIVITY_ENABLED); #ifdef BLE_PIN_CODE // 123456 by default if (_prefs.ble_pin == 0) { @@ -1680,6 +1689,19 @@ void MyMesh::handleCmdFrame(size_t len) { savePrefs(); } #endif + // Update node preferences for LED settings + if (strcmp(sp, "led.ble") == 0) { + _prefs.led_ble_mode = sensors.led_ble_mode; + _serial->setLedBleMode(_prefs.led_ble_mode); + savePrefs(); + } else if (strcmp(sp, "led.status") == 0) { + _prefs.led_status_mode = sensors.led_status_mode; + savePrefs(); + } else if (strcmp(sp, "led.activity") == 0) { + _prefs.led_activity_mode = sensors.led_activity_mode; + board.activity_led_enabled = (_prefs.led_activity_mode == LED_ACTIVITY_ENABLED); + savePrefs(); + } writeOKFrame(); } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); @@ -1844,6 +1866,32 @@ void MyMesh::checkCLIRescueCmd() { _prefs.ble_pin = atoi(&config[4]); savePrefs(); Serial.printf(" > pin is now %06d\n", _prefs.ble_pin); + } else if (memcmp(config, "led.ble ", 8) == 0) { + if (sensors.setSettingValue("led.ble", &config[8])) { + _prefs.led_ble_mode = sensors.led_ble_mode; + _serial->setLedBleMode(_prefs.led_ble_mode); + savePrefs(); + Serial.printf(" > led.ble is now %d\n", _prefs.led_ble_mode); + } else { + Serial.println(" Error: must be 0-3 (0=enabled, 1=disconn_only, 2=conn_only, 3=disabled)"); + } + } else if (memcmp(config, "led.status ", 11) == 0) { + if (sensors.setSettingValue("led.status", &config[11])) { + _prefs.led_status_mode = sensors.led_status_mode; + savePrefs(); + Serial.printf(" > led.status is now %d\n", _prefs.led_status_mode); + } else { + Serial.println(" Error: must be 0-1 (0=enabled, 1=disabled)"); + } + } else if (memcmp(config, "led.activity ", 13) == 0) { + if (sensors.setSettingValue("led.activity", &config[13])) { + _prefs.led_activity_mode = sensors.led_activity_mode; + board.activity_led_enabled = (_prefs.led_activity_mode == LED_ACTIVITY_ENABLED); + savePrefs(); + Serial.printf(" > led.activity is now %d\n", _prefs.led_activity_mode); + } else { + Serial.println(" Error: must be 0-1 (0=enabled, 1=disabled)"); + } } else { Serial.printf(" Error: unknown config: %s\n", config); } diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 090209c1dc..3037a94745 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -8,6 +8,17 @@ #define ADVERT_LOC_NONE 0 #define ADVERT_LOC_SHARE 1 +#define LED_BLE_ENABLED 0 +#define LED_BLE_DISCONN_ONLY 1 +#define LED_BLE_CONN_ONLY 2 +#define LED_BLE_DISABLED 3 + +#define LED_STATUS_ENABLED 0 +#define LED_STATUS_DISABLED 1 + +#define LED_ACTIVITY_ENABLED 0 +#define LED_ACTIVITY_DISABLED 1 + struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; @@ -31,4 +42,7 @@ struct NodePrefs { // persisted to file uint8_t client_repeat; uint8_t path_hash_mode; // which path mode to use when sending uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) + uint8_t led_ble_mode; // LED_BLE_ENABLED, LED_BLE_DISCONN_ONLY, LED_BLE_CONN_ONLY, LED_BLE_DISABLED + uint8_t led_status_mode; // LED_STATUS_ENABLED, LED_STATUS_DISABLED + uint8_t led_activity_mode; // LED_ACTIVITY_ENABLED, LED_ACTIVITY_DISABLED }; \ No newline at end of file diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index eff9efca47..1020520eee 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -151,7 +151,7 @@ void setup() { ); #ifdef BLE_PIN_CODE - serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin()); + serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin(), the_mesh.getNodePrefs()->led_ble_mode); #else serial_interface.begin(Serial); #endif @@ -198,7 +198,7 @@ void setup() { WiFi.begin(WIFI_SSID, WIFI_PWD); serial_interface.begin(TCP_PORT); #elif defined(BLE_PIN_CODE) - serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin()); + serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin(), the_mesh.getNodePrefs()->led_ble_mode); #elif defined(SERIAL_RX) companion_serial.setPins(SERIAL_RX, SERIAL_TX); companion_serial.begin(115200); diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 265532be0b..209692b65d 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -655,6 +655,13 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i void UITask::userLedHandler() { #ifdef PIN_STATUS_LED + if (_node_prefs->led_status_mode == LED_STATUS_DISABLED) { + if (led_state != 0) { + led_state = 0; + digitalWrite(PIN_STATUS_LED, !LED_STATE_ON); + } + return; + } int cur_time = millis(); if (cur_time > next_led_change) { if (led_state == 0) { @@ -674,6 +681,22 @@ void UITask::userLedHandler() { #endif } +void UITask::bleLedHandler() { +#ifdef BLE_LED_PIN + if (_node_prefs->led_ble_mode != LED_BLE_DISCONN_ONLY) return; + + // Disconnected-only mode: blink when not connected + if (hasConnection()) return; + + unsigned long now = millis(); + if (now >= next_ble_led_change) { + ble_led_state = !ble_led_state; + digitalWrite(BLE_LED_PIN, ble_led_state ? LED_STATE_ON : !LED_STATE_ON); + next_ble_led_change = now + 250; + } +#endif +} + void UITask::setCurrScreen(UIScreen* c) { curr = c; _next_refresh = 100; @@ -785,6 +808,7 @@ void UITask::loop() { } userLedHandler(); + bleLedHandler(); #ifdef PIN_BUZZER if (buzzer.isPlaying()) buzzer.loop(); diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index a77ad6e7ec..e5431203f9 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -12,6 +12,12 @@ #define LED_STATE_ON 1 #endif +#ifdef LED_CONN + #define BLE_LED_PIN LED_CONN +#elif defined(LED_BLUE) + #define BLE_LED_PIN LED_BLUE +#endif + #ifdef PIN_BUZZER #include #endif @@ -43,6 +49,10 @@ class UITask : public AbstractUITask { int next_led_change = 0; int last_led_increment = 0; #endif +#ifdef BLE_LED_PIN + int ble_led_state = 0; + unsigned long next_ble_led_change = 0; +#endif #ifdef PIN_USER_BTN_ANA unsigned long _analogue_pin_read_millis = millis(); @@ -54,6 +64,7 @@ class UITask : public AbstractUITask { UIScreen* curr; void userLedHandler(); + void bleLedHandler(); // Button action handlers char checkDisplayOn(char c); diff --git a/examples/companion_radio/ui-orig/UITask.cpp b/examples/companion_radio/ui-orig/UITask.cpp index 3ad36fb000..252e76a9d7 100644 --- a/examples/companion_radio/ui-orig/UITask.cpp +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -266,6 +266,13 @@ void UITask::userLedHandler() { static int next_change = 0; static int last_increment = 0; + if (_node_prefs->led_status_mode == LED_STATUS_DISABLED) { + if (state != 0) { + state = 0; + digitalWrite(PIN_STATUS_LED, !LED_STATE_ON); + } + return; + } int cur_time = millis(); if (cur_time > next_change) { if (state == 0) { @@ -285,6 +292,24 @@ void UITask::userLedHandler() { #endif } +void UITask::bleLedHandler() { +#ifdef BLE_LED_PIN + if (_node_prefs->led_ble_mode != LED_BLE_DISCONN_ONLY) return; + + // Disconnected-only mode: blink when not connected + if (hasConnection()) return; + + static int ble_led_state = 0; + static unsigned long next_ble_led_change = 0; + unsigned long now = millis(); + if (now >= next_ble_led_change) { + ble_led_state = !ble_led_state; + digitalWrite(BLE_LED_PIN, ble_led_state ? LED_STATE_ON : !LED_STATE_ON); + next_ble_led_change = now + 250; + } +#endif +} + /* hardware-agnostic pre-shutdown activity should be done here */ @@ -323,6 +348,7 @@ void UITask::loop() { } #endif userLedHandler(); + bleLedHandler(); #ifdef PIN_BUZZER if (buzzer.isPlaying()) buzzer.loop(); diff --git a/examples/companion_radio/ui-orig/UITask.h b/examples/companion_radio/ui-orig/UITask.h index 60cd0d042c..9da4bc767e 100644 --- a/examples/companion_radio/ui-orig/UITask.h +++ b/examples/companion_radio/ui-orig/UITask.h @@ -12,6 +12,16 @@ #include "../AbstractUITask.h" #include "../NodePrefs.h" +#ifndef LED_STATE_ON + #define LED_STATE_ON 1 +#endif + +#ifdef LED_CONN + #define BLE_LED_PIN LED_CONN +#elif defined(LED_BLUE) + #define BLE_LED_PIN LED_BLUE +#endif + #include "Button.h" class UITask : public AbstractUITask { @@ -41,6 +51,7 @@ class UITask : public AbstractUITask { void renderCurrScreen(); void userLedHandler(); + void bleLedHandler(); void renderBatteryIndicator(uint16_t batteryMilliVolts); // Button action handlers diff --git a/src/MeshCore.h b/src/MeshCore.h index 70cd0f0672..4cffda9692 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -42,6 +42,7 @@ namespace mesh { class MainBoard { public: + bool activity_led_enabled = true; // runtime flag for led.activity custom var virtual uint16_t getBattMilliVolts() = 0; virtual float getMCUTemperature() { return NAN; } virtual bool setAdcMultiplier(float multiplier) { return false; }; diff --git a/src/helpers/BaseSerialInterface.h b/src/helpers/BaseSerialInterface.h index e60927654b..76b7455e90 100644 --- a/src/helpers/BaseSerialInterface.h +++ b/src/helpers/BaseSerialInterface.h @@ -18,4 +18,5 @@ class BaseSerialInterface { virtual bool isWriteBusy() const = 0; virtual size_t writeFrame(const uint8_t src[], size_t len) = 0; virtual size_t checkRecvFrame(uint8_t dest[]) = 0; + virtual void setLedBleMode(uint8_t mode) { (void)mode; } }; diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index bade3e8980..462d8fc93c 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -81,18 +81,22 @@ class ESP32Board : public mesh::MainBoard { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #elif defined(P_LORA_TX_NEOPIXEL_LED) #define NEOPIXEL_BRIGHTNESS 64 // white brightness (max 255) void onBeforeTransmit() override { + if (!activity_led_enabled) return; neopixelWrite(P_LORA_TX_NEOPIXEL_LED, NEOPIXEL_BRIGHTNESS, NEOPIXEL_BRIGHTNESS, NEOPIXEL_BRIGHTNESS); // turn TX neopixel on (White) } void onAfterTransmit() override { + if (!activity_led_enabled) return; neopixelWrite(P_LORA_TX_NEOPIXEL_LED, 0, 0, 0); // turn TX neopixel off } #endif diff --git a/src/helpers/esp32/SerialBLEInterface.cpp b/src/helpers/esp32/SerialBLEInterface.cpp index dcfa0e1e34..b0848079a8 100644 --- a/src/helpers/esp32/SerialBLEInterface.cpp +++ b/src/helpers/esp32/SerialBLEInterface.cpp @@ -10,7 +10,8 @@ #define ADVERT_RESTART_DELAY 1000 // millis -void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code) { +void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code, uint8_t led_ble_mode) { + (void)led_ble_mode; // LED mode control not implemented on ESP32 _pin_code = pin_code; if (strcmp(name, "@@MAC") == 0) { diff --git a/src/helpers/esp32/SerialBLEInterface.h b/src/helpers/esp32/SerialBLEInterface.h index 965e90fd19..dc3df03c7f 100644 --- a/src/helpers/esp32/SerialBLEInterface.h +++ b/src/helpers/esp32/SerialBLEInterface.h @@ -67,7 +67,7 @@ class SerialBLEInterface : public BaseSerialInterface, BLESecurityCallbacks, BLE * @param name IN/OUT - a name for the device (combined with prefix). If "@@MAC", is modified and returned * @param pin_code the BLE security pin */ - void begin(const char* prefix, char* name, uint32_t pin_code); + void begin(const char* prefix, char* name, uint32_t pin_code, uint8_t led_ble_mode = 0); // BaseSerialInterface methods void enable() override; diff --git a/src/helpers/esp32/TBeamBoard.h b/src/helpers/esp32/TBeamBoard.h index 4ff9555103..5e73a171a7 100644 --- a/src/helpers/esp32/TBeamBoard.h +++ b/src/helpers/esp32/TBeamBoard.h @@ -124,9 +124,11 @@ bool power_init(); #ifndef TBEAM_SUPREME_SX1262 void onBeforeTransmit() override{ + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on - invert pin for SX1276 } void onAfterTransmit() override{ + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off - invert pin for SX1276 } #endif diff --git a/src/helpers/nrf52/SerialBLEInterface.cpp b/src/helpers/nrf52/SerialBLEInterface.cpp index 5648707e6a..de867888b2 100644 --- a/src/helpers/nrf52/SerialBLEInterface.cpp +++ b/src/helpers/nrf52/SerialBLEInterface.cpp @@ -4,6 +4,23 @@ #include "ble_gap.h" #include "ble_hci.h" +#ifndef LED_STATE_ON + #define LED_STATE_ON 1 +#endif + +// Resolve the BLE LED pin: prefer LED_CONN, fall back to LED_BLUE +#ifdef LED_CONN + #define BLE_LED_PIN LED_CONN +#elif defined(LED_BLUE) + #define BLE_LED_PIN LED_BLUE +#endif + +// BLE LED mode constants (must match NodePrefs.h) +#define LED_BLE_ENABLED 0 +#define LED_BLE_DISCONN_ONLY 1 +#define LED_BLE_CONN_ONLY 2 +#define LED_BLE_DISABLED 3 + // Magic numbers came from actual testing #define BLE_HEALTH_CHECK_INTERVAL 10000 // Advertising watchdog check every 10 seconds #define BLE_RETRY_THROTTLE_MS 250 // Throttle retries to 250ms when queue buildup detected @@ -40,6 +57,11 @@ void SerialBLEInterface::onDisconnect(uint16_t connection_handle, uint8_t reason instance->_conn_handle = BLE_CONN_HANDLE_INVALID; instance->_isDeviceConnected = false; instance->clearBuffers(); +#ifdef BLE_LED_PIN + if (instance->_led_ble_mode == LED_BLE_CONN_ONLY || instance->_led_ble_mode == LED_BLE_DISABLED) { + digitalWrite(BLE_LED_PIN, !LED_STATE_ON); + } +#endif } } } @@ -49,6 +71,13 @@ void SerialBLEInterface::onSecured(uint16_t connection_handle) { if (instance) { if (instance->isValidConnection(connection_handle, true)) { instance->_isDeviceConnected = true; +#ifdef BLE_LED_PIN + if (instance->_led_ble_mode == LED_BLE_CONN_ONLY) { + digitalWrite(BLE_LED_PIN, LED_STATE_ON); + } else if (instance->_led_ble_mode == LED_BLE_DISCONN_ONLY) { + digitalWrite(BLE_LED_PIN, !LED_STATE_ON); + } +#endif // Connection interval units: 1.25ms, supervision timeout units: 10ms // Apple: "The product will not read or use the parameters in the Peripheral Preferred Connection Parameters characteristic." @@ -123,16 +152,27 @@ void SerialBLEInterface::onBLEEvent(ble_evt_t* evt) { } } -void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code) { +void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code, uint8_t led_ble_mode) { instance = this; + _led_ble_mode = led_ble_mode; char charpin[20]; snprintf(charpin, sizeof(charpin), "%lu", (unsigned long)pin_code); - - // If we want to control BLE LED ourselves, uncomment this: - // Bluefruit.autoConnLed(false); + + if (_led_ble_mode != LED_BLE_ENABLED) { + Bluefruit.autoConnLed(false); + } Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); Bluefruit.begin(); + + // Re-assert autoConnLed after begin() to ensure the SDK doesn't interfere + if (_led_ble_mode != LED_BLE_ENABLED) { + Bluefruit.autoConnLed(false); +#ifdef BLE_LED_PIN + pinMode(BLE_LED_PIN, OUTPUT); + digitalWrite(BLE_LED_PIN, !LED_STATE_ON); // start with LED off for all non-default modes +#endif + } char dev_name[32+16]; if (strcmp(name, "@@MAC") == 0) { @@ -247,6 +287,13 @@ void SerialBLEInterface::enable() { _last_health_check = millis(); Bluefruit.Advertising.start(0); + + // Re-assert autoConnLed after Advertising.start(), which may inadvertently + // start the SDK's blink timer via xTimerChangePeriod() side effect. + // UITask::bleLedHandler() continuously enforces the correct LED state. + if (_led_ble_mode != LED_BLE_ENABLED) { + Bluefruit.autoConnLed(false); + } } void SerialBLEInterface::disconnect() { @@ -395,3 +442,29 @@ bool SerialBLEInterface::isConnected() const { bool SerialBLEInterface::isWriteBusy() const { return send_queue_len >= (FRAME_QUEUE_SIZE * 2 / 3); } + +void SerialBLEInterface::setLedBleMode(uint8_t mode) { + _led_ble_mode = mode; + if (mode == LED_BLE_ENABLED) { + Bluefruit.autoConnLed(true); +#ifdef BLE_LED_PIN + // SDK won't update LED until next connect/disconnect event, so set it now + digitalWrite(BLE_LED_PIN, _isDeviceConnected ? LED_STATE_ON : !LED_STATE_ON); +#endif + } else { + Bluefruit.autoConnLed(false); +#ifdef BLE_LED_PIN + // Apply immediate state based on new mode + if (mode == LED_BLE_DISABLED) { + digitalWrite(BLE_LED_PIN, !LED_STATE_ON); + } else if (mode == LED_BLE_CONN_ONLY) { + digitalWrite(BLE_LED_PIN, _isDeviceConnected ? LED_STATE_ON : !LED_STATE_ON); + } else if (mode == LED_BLE_DISCONN_ONLY) { + // Blink handled by UITask::bleLedHandler(); just set initial state + if (_isDeviceConnected) { + digitalWrite(BLE_LED_PIN, !LED_STATE_ON); + } + } +#endif + } +} diff --git a/src/helpers/nrf52/SerialBLEInterface.h b/src/helpers/nrf52/SerialBLEInterface.h index e2fc6cb95f..b388ecc3b0 100644 --- a/src/helpers/nrf52/SerialBLEInterface.h +++ b/src/helpers/nrf52/SerialBLEInterface.h @@ -14,6 +14,7 @@ class SerialBLEInterface : public BaseSerialInterface { uint16_t _conn_handle; unsigned long _last_health_check; unsigned long _last_retry_attempt; + uint8_t _led_ble_mode; struct Frame { uint8_t len; @@ -48,6 +49,7 @@ class SerialBLEInterface : public BaseSerialInterface { _conn_handle = BLE_CONN_HANDLE_INVALID; _last_health_check = 0; _last_retry_attempt = 0; + _led_ble_mode = 0; send_queue_len = 0; recv_queue_len = 0; } @@ -58,7 +60,7 @@ class SerialBLEInterface : public BaseSerialInterface { * @param name IN/OUT - a name for the device (combined with prefix). If "@@MAC", is modified and returned * @param pin_code the BLE security pin */ - void begin(const char* prefix, char* name, uint32_t pin_code); + void begin(const char* prefix, char* name, uint32_t pin_code, uint8_t led_ble_mode = 0); void disconnect(); void enable() override; @@ -68,6 +70,7 @@ class SerialBLEInterface : public BaseSerialInterface { bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; + void setLedBleMode(uint8_t mode) override; }; #if BLE_DEBUG_LOGGING && ARDUINO diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 07807011db..08fb97b87e 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -494,6 +494,7 @@ int EnvironmentSensorManager::getNumSettings() const { #if ENV_INCLUDE_GPS if (gps_detected) settings++; // only show GPS setting if GPS is detected #endif + settings += 3; // led.ble, led.status, led.activity return settings; } @@ -504,11 +505,14 @@ const char* EnvironmentSensorManager::getSettingName(int i) const { return "gps"; } #endif - // convenient way to add params (needed for some tests) -// if (i == settings++) return "param.2"; + if (i == settings++) return "led.ble"; + if (i == settings++) return "led.status"; + if (i == settings++) return "led.activity"; return NULL; } +static char _led_val_buf[4]; + const char* EnvironmentSensorManager::getSettingValue(int i) const { int settings = 0; #if ENV_INCLUDE_GPS @@ -516,8 +520,18 @@ const char* EnvironmentSensorManager::getSettingValue(int i) const { return gps_active ? "1" : "0"; } #endif - // convenient way to add params ... -// if (i == settings++) return "2"; + if (i == settings++) { + sprintf(_led_val_buf, "%d", led_ble_mode); + return _led_val_buf; + } + if (i == settings++) { + sprintf(_led_val_buf, "%d", led_status_mode); + return _led_val_buf; + } + if (i == settings++) { + sprintf(_led_val_buf, "%d", led_activity_mode); + return _led_val_buf; + } return NULL; } @@ -541,6 +555,30 @@ bool EnvironmentSensorManager::setSettingValue(const char* name, const char* val return true; } #endif + if (strcmp(name, "led.ble") == 0) { + int mode = atoi(value); + if (mode >= 0 && mode <= 3) { + led_ble_mode = mode; + return true; + } + return false; + } + if (strcmp(name, "led.status") == 0) { + int mode = atoi(value); + if (mode >= 0 && mode <= 1) { + led_status_mode = mode; + return true; + } + return false; + } + if (strcmp(name, "led.activity") == 0) { + int mode = atoi(value); + if (mode >= 0 && mode <= 1) { + led_activity_mode = mode; + return true; + } + return false; + } return false; // not supported } diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index f176a33f5f..16ad7a250d 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -46,6 +46,10 @@ class EnvironmentSensorManager : public SensorManager { #else EnvironmentSensorManager(){}; #endif + uint8_t led_ble_mode = 0; // runtime copy, synced from/to NodePrefs + uint8_t led_status_mode = 0; // runtime copy, synced from/to NodePrefs + uint8_t led_activity_mode = 0; // runtime copy, synced from/to NodePrefs + bool begin() override; bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; #if ENV_INCLUDE_GPS diff --git a/src/helpers/stm32/STM32Board.h b/src/helpers/stm32/STM32Board.h index 06bc768f88..ec489f3eb1 100644 --- a/src/helpers/stm32/STM32Board.h +++ b/src/helpers/stm32/STM32Board.h @@ -34,9 +34,11 @@ class STM32Board : public mesh::MainBoard { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off } #endif diff --git a/variants/heltec_t114/T114Board.h b/variants/heltec_t114/T114Board.h index f27dc291d0..51c989b8a2 100644 --- a/variants/heltec_t114/T114Board.h +++ b/variants/heltec_t114/T114Board.h @@ -21,9 +21,11 @@ class T114Board : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off } #endif diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp index bd7f680ea2..26c64525a1 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp @@ -38,12 +38,16 @@ void HeltecTrackerV2Board::begin() { } void HeltecTrackerV2Board::onBeforeTransmit(void) { - digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + if (activity_led_enabled) { + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + } digitalWrite(P_LORA_PA_TX_EN,HIGH); } void HeltecTrackerV2Board::onAfterTransmit(void) { - digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off + if (activity_led_enabled) { + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off + } digitalWrite(P_LORA_PA_TX_EN,LOW); } diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index 8186f2d4b2..b265c949ed 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -39,12 +39,16 @@ void HeltecV4Board::begin() { } void HeltecV4Board::onBeforeTransmit(void) { - digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + if (activity_led_enabled) { + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + } digitalWrite(P_LORA_PA_TX_EN,HIGH); } void HeltecV4Board::onAfterTransmit(void) { - digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off + if (activity_led_enabled) { + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off + } digitalWrite(P_LORA_PA_TX_EN,LOW); } diff --git a/variants/ikoka_handheld_nrf/IkokaNrf52Board.h b/variants/ikoka_handheld_nrf/IkokaNrf52Board.h index 372dac5662..b0236dbbf5 100644 --- a/variants/ikoka_handheld_nrf/IkokaNrf52Board.h +++ b/variants/ikoka_handheld_nrf/IkokaNrf52Board.h @@ -13,9 +13,11 @@ class IkokaNrf52Board : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off } #endif diff --git a/variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h b/variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h index eb05092e02..458c6aa1cc 100644 --- a/variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h +++ b/variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h @@ -13,14 +13,18 @@ class IkokaNanoNRFBoard : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { - digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on + if (activity_led_enabled) { + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on + } #if defined(LED_BLUE) // turn off that annoying blue LED before transmitting digitalWrite(LED_BLUE, HIGH); #endif } void onAfterTransmit() override { - digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off + if (activity_led_enabled) { + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off + } #if defined(LED_BLUE) // do it after transmitting too, just in case digitalWrite(LED_BLUE, HIGH); diff --git a/variants/ikoka_stick_nrf/IkokaStickNRFBoard.h b/variants/ikoka_stick_nrf/IkokaStickNRFBoard.h index 3c04930f1e..14270f8ead 100644 --- a/variants/ikoka_stick_nrf/IkokaStickNRFBoard.h +++ b/variants/ikoka_stick_nrf/IkokaStickNRFBoard.h @@ -13,14 +13,18 @@ class IkokaStickNRFBoard : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { - digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on + if (activity_led_enabled) { + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on + } #if defined(LED_BLUE) // turn off that annoying blue LED before transmitting digitalWrite(LED_BLUE, HIGH); #endif } void onAfterTransmit() override { - digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off + if (activity_led_enabled) { + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off + } #if defined(LED_BLUE) // do it after transmitting too, just in case digitalWrite(LED_BLUE, HIGH); diff --git a/variants/keepteen_lt1/KeepteenLT1Board.h b/variants/keepteen_lt1/KeepteenLT1Board.h index 752b27e752..92c5b68ae3 100644 --- a/variants/keepteen_lt1/KeepteenLT1Board.h +++ b/variants/keepteen_lt1/KeepteenLT1Board.h @@ -31,9 +31,11 @@ class KeepteenLT1Board : public NRF52Board { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #endif diff --git a/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp b/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp index 1719d73334..0aaad8a996 100644 --- a/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp +++ b/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp @@ -22,11 +22,15 @@ void TBeam1WBoard::begin() { void TBeam1WBoard::onBeforeTransmit() { // RF switching handled by RadioLib via SX126X_DIO2_AS_RF_SWITCH and setRfSwitchPins() - digitalWrite(LED_PIN, HIGH); // TX LED on + if (activity_led_enabled) { + digitalWrite(LED_PIN, HIGH); // TX LED on + } } void TBeam1WBoard::onAfterTransmit() { - digitalWrite(LED_PIN, LOW); // TX LED off + if (activity_led_enabled) { + digitalWrite(LED_PIN, LOW); // TX LED off + } } uint16_t TBeam1WBoard::getBattMilliVolts() { diff --git a/variants/lilygo_tdeck/TDeckBoard.h b/variants/lilygo_tdeck/TDeckBoard.h index 7ed007af9c..5265d17800 100644 --- a/variants/lilygo_tdeck/TDeckBoard.h +++ b/variants/lilygo_tdeck/TDeckBoard.h @@ -15,10 +15,12 @@ class TDeckBoard : public ESP32Board { #ifdef P_LORA_TX_LED void onBeforeTransmit() override{ + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on - invert pin for SX1276 } void onAfterTransmit() override{ + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off - invert pin for SX1276 } #endif diff --git a/variants/meshtiny/MeshtinyBoard.h b/variants/meshtiny/MeshtinyBoard.h index b69c0e419c..3238a568cf 100644 --- a/variants/meshtiny/MeshtinyBoard.h +++ b/variants/meshtiny/MeshtinyBoard.h @@ -14,9 +14,11 @@ class MeshtinyBoard : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #endif diff --git a/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h b/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h index 6858a1062f..511f327d8a 100644 --- a/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h +++ b/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h @@ -70,9 +70,11 @@ class MinewsemiME25LS01Board : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH);// turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #endif diff --git a/variants/rak11310/RAK11310Board.h b/variants/rak11310/RAK11310Board.h index ea0f15e26c..5c244390c4 100644 --- a/variants/rak11310/RAK11310Board.h +++ b/variants/rak11310/RAK11310Board.h @@ -21,8 +21,8 @@ class RAK11310Board : public mesh::MainBoard { uint8_t getStartupReason() const override { return startup_reason; } #ifdef P_LORA_TX_LED - void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); } - void onAfterTransmit() override { digitalWrite(P_LORA_TX_LED, LOW); } + void onBeforeTransmit() override { if (activity_led_enabled) digitalWrite(P_LORA_TX_LED, HIGH); } + void onAfterTransmit() override { if (activity_led_enabled) digitalWrite(P_LORA_TX_LED, LOW); } #endif uint16_t getBattMilliVolts() override { diff --git a/variants/rak_wismesh_tag/RAKWismeshTagBoard.h b/variants/rak_wismesh_tag/RAKWismeshTagBoard.h index cc5aa06f59..96ed37ebad 100644 --- a/variants/rak_wismesh_tag/RAKWismeshTagBoard.h +++ b/variants/rak_wismesh_tag/RAKWismeshTagBoard.h @@ -15,9 +15,11 @@ class RAKWismeshTagBoard : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) && defined(LED_STATE_ON) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LED_STATE_ON); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, !LED_STATE_ON); // turn TX LED off } #endif diff --git a/variants/rpi_picow/PicoWBoard.h b/variants/rpi_picow/PicoWBoard.h index 708e96558b..ae795f838c 100644 --- a/variants/rpi_picow/PicoWBoard.h +++ b/variants/rpi_picow/PicoWBoard.h @@ -17,10 +17,12 @@ class PicoWBoard : public mesh::MainBoard { uint8_t getStartupReason() const override { return startup_reason; } void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(LED_BUILTIN, HIGH); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(LED_BUILTIN, LOW); // turn TX LED off } diff --git a/variants/sensecap_solar/SenseCapSolarBoard.h b/variants/sensecap_solar/SenseCapSolarBoard.h index 67215b8e09..ae452fc0f0 100644 --- a/variants/sensecap_solar/SenseCapSolarBoard.h +++ b/variants/sensecap_solar/SenseCapSolarBoard.h @@ -11,9 +11,11 @@ class SenseCapSolarBoard : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #endif diff --git a/variants/thinknode_m1/ThinkNodeM1Board.h b/variants/thinknode_m1/ThinkNodeM1Board.h index ebc46e6e6f..4ec7d3abf2 100644 --- a/variants/thinknode_m1/ThinkNodeM1Board.h +++ b/variants/thinknode_m1/ThinkNodeM1Board.h @@ -21,9 +21,11 @@ class ThinkNodeM1Board : public NRF52Board { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #endif diff --git a/variants/thinknode_m3/ThinkNodeM3Board.h b/variants/thinknode_m3/ThinkNodeM3Board.h index 1435d31d47..b295ff02c5 100644 --- a/variants/thinknode_m3/ThinkNodeM3Board.h +++ b/variants/thinknode_m3/ThinkNodeM3Board.h @@ -20,9 +20,11 @@ class ThinkNodeM3Board : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #endif diff --git a/variants/thinknode_m5/ThinknodeM5Board.h b/variants/thinknode_m5/ThinknodeM5Board.h index 3c120027b7..46e2d89708 100644 --- a/variants/thinknode_m5/ThinknodeM5Board.h +++ b/variants/thinknode_m5/ThinknodeM5Board.h @@ -19,9 +19,11 @@ class ThinknodeM5Board : public ESP32Board { const char* getManufacturerName() const override ; void onBeforeTransmit() override { + if (!activity_led_enabled) return; expander.digitalWrite(EXP_PIN_LED, HIGH); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; expander.digitalWrite(EXP_PIN_LED, LOW); // turn TX LED off } }; \ No newline at end of file diff --git a/variants/thinknode_m6/ThinkNodeM6Board.h b/variants/thinknode_m6/ThinkNodeM6Board.h index 32baa2a0a2..e0f1afb1df 100644 --- a/variants/thinknode_m6/ThinkNodeM6Board.h +++ b/variants/thinknode_m6/ThinkNodeM6Board.h @@ -25,9 +25,11 @@ class ThinkNodeM6Board : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #endif diff --git a/variants/waveshare_rp2040_lora/WaveshareBoard.h b/variants/waveshare_rp2040_lora/WaveshareBoard.h index 694b8bd122..c6e2562bcb 100644 --- a/variants/waveshare_rp2040_lora/WaveshareBoard.h +++ b/variants/waveshare_rp2040_lora/WaveshareBoard.h @@ -33,8 +33,8 @@ class WaveshareBoard : public mesh::MainBoard { uint8_t getStartupReason() const override { return startup_reason; } #ifdef P_LORA_TX_LED - void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); } - void onAfterTransmit() override { digitalWrite(P_LORA_TX_LED, LOW); } + void onBeforeTransmit() override { if (activity_led_enabled) digitalWrite(P_LORA_TX_LED, HIGH); } + void onAfterTransmit() override { if (activity_led_enabled) digitalWrite(P_LORA_TX_LED, LOW); } #endif uint16_t getBattMilliVolts() override { diff --git a/variants/wio-tracker-l1/WioTrackerL1Board.h b/variants/wio-tracker-l1/WioTrackerL1Board.h index 052238e66f..5a9553778f 100644 --- a/variants/wio-tracker-l1/WioTrackerL1Board.h +++ b/variants/wio-tracker-l1/WioTrackerL1Board.h @@ -14,9 +14,11 @@ class WioTrackerL1Board : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #endif diff --git a/variants/wio_wm1110/WioWM1110Board.h b/variants/wio_wm1110/WioWM1110Board.h index 26f95c8684..f2d379f1af 100644 --- a/variants/wio_wm1110/WioWM1110Board.h +++ b/variants/wio_wm1110/WioWM1110Board.h @@ -18,9 +18,11 @@ class WioWM1110Board : public NRF52BoardDCDC { #if defined(LED_GREEN) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(LED_RED, HIGH); } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(LED_RED, LOW); } #endif diff --git a/variants/xiao_c3/XiaoC3Board.h b/variants/xiao_c3/XiaoC3Board.h index 6ea1c15ffc..b108ad4c13 100644 --- a/variants/xiao_c3/XiaoC3Board.h +++ b/variants/xiao_c3/XiaoC3Board.h @@ -74,7 +74,9 @@ class XiaoC3Board : public ESP32Board { #if defined(LORA_TX_BOOST_PIN) || defined(P_LORA_TX_LED) void onBeforeTransmit() override { #if defined(P_LORA_TX_LED) - digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + if (activity_led_enabled) { + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + } #endif #if defined(LORA_TX_BOOST_PIN) digitalWrite(LORA_TX_BOOST_PIN, LOW); @@ -86,7 +88,9 @@ class XiaoC3Board : public ESP32Board { digitalWrite(LORA_TX_BOOST_PIN, HIGH); #endif #if defined(P_LORA_TX_LED) - digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off + if (activity_led_enabled) { + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off + } #endif } #endif diff --git a/variants/xiao_nrf52/XiaoNrf52Board.h b/variants/xiao_nrf52/XiaoNrf52Board.h index bd0fd9b12f..d9129aa0b4 100644 --- a/variants/xiao_nrf52/XiaoNrf52Board.h +++ b/variants/xiao_nrf52/XiaoNrf52Board.h @@ -18,9 +18,11 @@ class XiaoNrf52Board : public NRF52BoardDCDC { #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on } void onAfterTransmit() override { + if (!activity_led_enabled) return; digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off } #endif diff --git a/variants/xiao_rp2040/XiaoRP2040Board.h b/variants/xiao_rp2040/XiaoRP2040Board.h index d2951c7555..377ca794ca 100644 --- a/variants/xiao_rp2040/XiaoRP2040Board.h +++ b/variants/xiao_rp2040/XiaoRP2040Board.h @@ -31,8 +31,8 @@ class XiaoRP2040Board : public mesh::MainBoard { uint8_t getStartupReason() const override { return startup_reason; } #ifdef P_LORA_TX_LED - void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); } - void onAfterTransmit() override { digitalWrite(P_LORA_TX_LED, LOW); } + void onBeforeTransmit() override { if (activity_led_enabled) digitalWrite(P_LORA_TX_LED, HIGH); } + void onAfterTransmit() override { if (activity_led_enabled) digitalWrite(P_LORA_TX_LED, LOW); } #endif