XIAO nRF52840 & Wio-SX1262 Kit for Meshtastic Power Consumption

I have spent some time reducing the power consumption of the Wio-SX1262 module in this kit. To save time for anyone else using this kit, here are my findings:

  1. Putting the nRF52840 SPI interface to sleep is necessary to prevent repeatedly waking the transceiver and increasing current consumption drastically.
  2. Putting the RF switch pin HIGH to enable single pin mode of the antenna switch increases current consumption by 55 µA, so keep it LOW when not in use.
  3. In warm-start sleep mode, current consumption of the SX1262 is 1.2 µA, trivially higher than cold-start mode that draws 0.6 µA and remembers all modem settings except for boosted gain mode.
  4. With the voltage divider enabled and an external interrupt enabled, I am measuring about 16 µA total current draw including the nRF52840. This is about 20 years on 3 lithium AA cells.

A function to switch between wake and sleep for the SX1262 module:

#include <Arduino.h>
#include <RadioLib.h>

const uint32_t kPinRF_SW = D5;
const bool kRxBoostedGainMode = true;

pinMode(kPinRF_SW, OUTPUT);

// Set transceiver wake/sleep mode
// Receives bool: true = wake up, false = go to sleep
// Returns Radiolib error status code, otherwise RADIOLIB_ERR_NONE = 0
int16_t TransceiverWake(const bool& is_awake) {
  int16_t state;

  if (is_awake) {
    SPI.begin();
    // set RF_SW high to allow single pin control by DIO2
    digitalWrite(kPinRF_SW, HIGH);
    // force immediate wake to standby mode, use external crystal oscillator
    // takes 340 µs
    if ((state = radio.standby(RADIOLIB_SX126X_STANDBY_XOSC, true)) != RADIOLIB_ERR_NONE) {
      return state;
    }
    // data sheet specifies that warm start does not restore boosted gain mode, so set it again
    if ((state = radio.setRxBoostedGainMode(kRxBoostedGainMode, true)) != RADIOLIB_ERR_NONE) {
      return state;
    }
  } else {
    // data sheet specifies that transceiver should be in STDBY_RC mode 1st to put to sleep
    if ((state = radio.standby(RADIOLIB_SX126X_STANDBY_RC)) != RADIOLIB_ERR_NONE) {
      return state;
    }
    // put to sleep with warm start - i.e. retain modem configuration
    // warm start increases current consumption from 600 nA to 1.2 µA
    if ((state = radio.sleep(true)) != RADIOLIB_ERR_NONE) {
      return state;
    }
    // leaving high increases current consumption by 55 µA!
    digitalWrite(kPinRF_SW, LOW);
    // stop SPI to prevent transceiver wakes
    SPI.end();
  }

  return RADIOLIB_ERR_NONE;
}

Hi there,

And Solid numbers there. :grinning_face_with_smiling_eyes: :oncoming_fist:
can you provide the BSP that was used for the tests and was a PPK2 used by chance to obtain the measurements?
The battery sounds great, but a little large of a PACK. I’m thinking CR2450 ? perhaps.
Are you testing a PING or a Data Payload?

HTH
GL :slight_smile: PJ :v:

Hi, am counting pulses from an external interrupt (varies between 2 and 20 per second) and sending totals to a hub once every 5 minutes. The data packet is 10 bytes with 12 symbol preamble, 14 dB power (optimised), SF 5, 500 kHz bandwidth, 4/8 CR, CRC, DC-DC regulator, boosted gain receive. The hub returns a 4 byte ack with same settings except 32 byte preamble. It will retransmit several times if no ack.

Using the Semtech LoRa Calculator, I get total charge for transmit + ack receive of 214.6 μC. Over 300 seconds, this works out at mean current 0.72 μA.

I have been using a good quality multimeter to measure current consumption of the kit. Obviously, it is not fast enough to measure the wake current peaks, and just averages.

The kit itself averages about 16 µA, so under 17 µA including radio overhead. Without an external interrupt (or using daCoder’s low power interrupts) and sending the battery divider pin high, the current consumption would be under 10 µA. That would give several years on a coin cell.

I am going to perform some range tests over the weekend.

I am using PlatformIO in VSCode.

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:seeed_xiao_nrf52840]
platform = https://github.com/Seeed-Studio/platform-seeedboards.git
board = seeed-xiao-afruitnrf52-nrf52840
framework = arduino
lib_deps = 
	adafruit/Adafruit SPIFlash@^5.1.1
	jgromes/RadioLib@^7.5.0
	thijse/ArduinoLog@^1.1.1
monitor_speed = 115200
upload_speed = 115200
1 Like

Hi there,

SO, that looks Great! , Please post your results. Using the sleep effectively is the key to longevity :+1: here and In life :grin:

WoW, 14dB pretty loud and on the High-end from what I have seen here. Curious to hear those range results. Your settings favor high speed over range. Spreading Factor 5 (SF5) combined with a wide 500 kHz bandwidth provides the lowest possible receiver sensitivity for LoRa. While this keeps “Time on Air” low, it significantly limits indoor penetration and outdoor range compared to standard SF7/125kHz settings.
Where are you located approx :grin: In many regions (like the EU), a 500 kHz bandwidth is restricted or prohibited for standard LoRa use, which often mandates 125 kHz to comply with duty cycle or spectral density limits.

I tried Standard CR2032 batteries but they are designed for microampere loads and struggle with the ~80–125mA peaks required for 14 dBm LoRa transmissions. Even with a DC-DC regulator, the high internal resistance of a coin cell can cause a voltage dip (brown-out), resetting the MCU or radio. I had to switch to a
CR2450 it has (higher peak current capability) “High Drain” coin cell.
I have found Measuring with a standard multimeter will miss the massive, millisecond-long current spikes during transmission. Your “17 µA” average is likely an underestimate of the actual energy extracted under load, as battery capacity “fades” much faster when hit with high-current pulses than its nominal mAh rating suggests.

If range tests are disappointing, You may try to implement a manual override for your LoRa parameters when the User button is pressed & switch to SF7 and 125 kHz. This significantly improves sensitivity and is more widely supported across all LoRa modules, often yielding better link stability without a massive increase in power consumption. :+1: I do this in a demo I posted on the PING PONG example range tests.
Also it is known that while a 32-byte preamble for the ACK ensures the node catches the signal, as it increases the node’s “Listen” time. If timing is precise, you can likely reduce the ACK preamble to 8–12 symbols to save some energy :v:

Do post those findings… Thanks for the contribution. Nothing like hands on testing this stuff.

HTH
GL :slight_smile: PJ :v:

The power amplifier consumption does not scale linearly with output power - it is most efficient at full power (22 dB). For the lower powers of 14 dB and 17 dB it is possible to optimise the settings to make the amplifier more efficient and cut the current draw. In Radiolib, you can cal the function like this to optimise the settings: (at 14 dB, cuts current from 90 to 45 mA)

uint16_t state = radio.setOutputPower(14, true);

I have a 45 mA current spike for 4 ms every 5 minutes. For a coin cell designed to supply small continuous mA currents, that would be very high but for a lithium AA cell, that is negligible. I suppose for a coin cell, a supercapacitor in parallel would protect it. As long as the voltage is kept below its Vmax, the leakage current is less than a µA.

https://community.element14.com/challenges-projects/design-challenges/experimenting-with-supercapacitors/b/blog/posts/what-s-super-about-supercapacitors-part-4-measuring-leakage-sizing-a-solution-lifetime#mcetoc_1h8oou5on3

I haven’t seen any restrictions on bandwidth in the 868 Mhz European band. It is split into several sub-bands with varying power and duty cycle restrictions. For example, the whole 869.4-869.65 MHz range can be used at up to 10% duty cycle and 500 mW power. There are 2 wide band slots of at least 500 kHz available: 868.0-868.6 MHz and 868.7-869.2 MHz although the latter has only a 0.1% duty cycle.

I just tested and using the standard flexible antennas that come with the the kit (using an ESP32 SX1262 as a hub). I get about 1/4 mile range across a field with a few trees obstructing the line-of-sight. I only need to get the signal down from my chimney, so that should be more than enough. I tried it with the minimum settings 1st so I change the settings to increase range later if necessary.

I am not worried about the longer preamble, as it is only increases receive time for a millisecond or so at 5 mA current draw.

1 Like

I have tested the watchdog timer, and it seems to function without noticeably affecting power consumption of the nRF52840. As my device will be mounted on the roof, I have added a watchdog in case it hangs so that the microcontroller will get a hard reset and restart. The watchdog countdown timer is driven by the low frequency 32768 Hz clock. The timer counts down from its initial value and if it reaches zero before it is reset, triggers a hard reset of the microcontroller. I have set the countdown duration to 6 minutes and reset the watchdog every 5 minutes in my loop.

To start the watchdog:

// configure and start the watchdog timer
// receives:  countdown duration in seconds
void StartWatchdog(const uint16_t& duration) {
  NRF_WDT->CONFIG         = 0x01;     // Keep WDT running while the CPU is sleeping
  NRF_WDT->CRV            = (32769 * duration) + 1;    // Counter reload value in number of cycles of the 32768 Hz clock
  NRF_WDT->RREN           = 0x01;     // Enable the RR[0] reload register
  NRF_WDT->TASKS_START    = 1;        // Start WDT     
}

To reset the watchdog:

// reset watchdog timer
NRF_WDT->RR[0] = WDT_RR_RR_Reload;
1 Like