nRF52840 LED won't turn off in deep sleep mode

I am using a nRF52840 along with a LTC3588-1 dev board. My goal is to activate BLE only when the PGOOD signal of the LTC is high, acting as an enable signal, otherwise the nRF goes into deep sleep, while still being powered by the LTC. However, when the nRF goes into deep sleep (PGOOD low), the 3-in-1 user led stays always on, which is undesirable since it is wasting unecessary power. This is my code:

#include <bluefruit.h>
// UUIDs
#define SERVICE_UUID        "12345678-1234-1234-1234-1234567890ab"
#define CHARACTERISTIC_UUID "87654321-4321-4321-4321-ba0987654321"



// Pin configuration
#define PGOOD_PIN           D6      // PGOOD from LTC3588
#define SEND_INTERVAL       5000    // ms
BLEService myService(SERVICE_UUID);
BLECharacteristic myCharacteristic(CHARACTERISTIC_UUID);
unsigned long lastSend = 0;

void enterDeepSleep() {
  Serial.println("Entering SYSTEMOFF (deep sleep)...");
  // Configure PGOOD as a wake-up source
  nrf_gpio_cfg_sense_input(
    g_ADigitalPinMap[PGOOD_PIN],   // convert Arduino pin to nRF52 pin
    NRF_GPIO_PIN_PULLDOWN,         // adjust if your LTC3588 pulls HIGH instead
    NRF_GPIO_PIN_SENSE_HIGH        // wake when PGOOD goes HIGH
  );
  delay(100);  // give UART time to flush
  //sd_power_system_off();
  digitalWrite(LED_BUILTIN, HIGH);
  NRF_POWER->SYSTEMOFF = 1;   // <- never returns, device resets on wake
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("Booted from reset / wake");
  pinMode(PGOOD_PIN, INPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  // Initialize BLE
  Bluefruit.begin();
  Bluefruit.setTxPower(4);
  Bluefruit.setName("XIAO_BLE");
  myService.begin();
  myCharacteristic.setProperties(CHR_PROPS_NOTIFY);
  myCharacteristic.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
  myCharacteristic.begin();
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.Advertising.addName();
  Bluefruit.Advertising.addService(myService);
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);
  if (digitalRead(PGOOD_PIN)) {
    Bluefruit.Advertising.start(0);
    Serial.println("PGOOD HIGH → started advertising");
  } else {
    Serial.println("PGOOD LOW → going to deep sleep...");
    enterDeepSleep();
  }
}

void loop() {
  // If PGOOD drops, shut down into SYSTEMOFF
  if (!digitalRead(PGOOD_PIN)) {
    Serial.println("PGOOD went LOW → stopping BLE + deep sleep");
    Bluefruit.Advertising.stop();
    if (Bluefruit.connected()) {
      Bluefruit.disconnect(0);
    }
    delay(100);
    enterDeepSleep();
  }
  // If connected and PGOOD is HIGH, send notification periodically
  if (Bluefruit.connected() && digitalRead(PGOOD_PIN)) {
    unsigned long now = millis();
    if (now - lastSend >= SEND_INTERVAL) {
      lastSend = now;
      String message = "LTC3588 Power Good! Time: " + String(now / 1000) + "s";
      myCharacteristic.notify(message.c_str());
      Serial.println("Sent: " + message);
    }
  }
  // Light sleep until next event (saves some power between notifications)
  __WFE();
}

I am using pinMode(LED_BUILTIN, OUTPUT) in void setup() and digitalWrite(LED_BUILTIN, HIGH) in void enterDeepSleep() , so that it turns off before going into sleep. However, the LED always stays on even when PGOOD is low (while being powered) and I really cannot understand why. I have checked that it does indeed go to sleep when PGOOD is low, I just to want to turn off the LED to save more power.

Hi there,

So this is a thing on certain BSP’s. Which one are you using?

You need to park them on certain cases, Sleep sometimes is one.
the LED is lit because its pin isn’t truly “high/idle” once you drop into SYSTEMOFF. On the XIAO nRF52840 the RGB user LED channels are active-low (HIGH = off, LOW = on) and, unless you explicitly park those GPIOs correctly, they can sink current (or float just enough to glow) after SYSTEMOFF.

Also double-check you’re not actually looking at the charger/power LED, which is hard-wired and not MCU-controllable.

// Turn OFF and park the RGB LED channels so they don't glow in SYSTEMOFF
static inline void park_rgb_led_pins() {
  const uint8_t pins[] = { LED_RED, LED_GREEN, LED_BLUE };

  for (uint8_t i = 0; i < 3; i++) {
    pinMode(pins[i], OUTPUT);
    digitalWrite(pins[i], HIGH);  // active-low LED: HIGH = OFF

    // Make them inputs with pull-up and NO sense, so they stay high in SYSTEMOFF
    uint32_t nrfpin = g_ADigitalPinMap[pins[i]];
    nrf_gpio_cfg_sense_input((uint32_t)nrfpin,
                             NRF_GPIO_PIN_PULLUP,
                             NRF_GPIO_PIN_NOSENSE);
  }
}

The battery/charge LED on XIAO is tied to the charger; firmware can’t turn it off (you’d have to remove a resistor/LED on the board)

You already do the right thing :+1: for PGOOD_PIN (SENSE_HIGH). Keep SENSE disabled on the LED pins (as above), otherwise you risk wakeups or unintended biasing. Nordic confirms: GPIO config isn’t “kept” as regular outputs across SYSTEMOFF—treat pins as inputs and rely on pulls.

Also, If the SoftDevice is running, use sd_power_system_off();
If not, NRF_POWER->SYSTEMOFF = 1; is fine. Make sure UART is flushed; stop BLE/USB first.

Pull direction: For active-low LEDs, you want PULL-UP in SYSTEMOFF so the pin sits high and no current flows through the LED.

HTH
GL :slight_smile: PJ :v:

thank you so much, it worked!

2 Likes