Hope RFM95/98 power usage in sleep mode?

I’ve also seen power usage in the single-digit and two-digit micro-amp range for my entire node (most of which was taken by the microcontroller, I believe), so it’s definitely something in your setup.

@matthijs @epyon

Might be something SPI related which works differently on the STM32.

I assume that I may have all RFM95 SPI and IO pins floating and RFM95 NSS/RESET high while sleeping?

Have NSS/RESET lines on the RFM95 internal pullup? May I also leave them floating?

The point is that I only added the RFM95 to a working setup which used 2μA in sleep mode. :-/

I will experiment some more…

or the resistors on the i2c maybe ?

@borroz Yes, I expect something related to that. I just editted my last reply…

On most micro’s and all CMOS I/O, leaving input pins to float consumes quite a lot of power (relatively speaking). You need to pull them up to VCC or down to GND. Output pins can safely be left floating.

Pullups do not consume power if the ports or pins they are connected to are tristated or configured as an input while sleeping.

1 Like

Can confirm here. Pulled some hair until discovered DIO ports on RFM while connected to GPIO and left floating (no pulled up) were draining some 0.5mA.

@petekmet The arduino-lmic lib sets the DIO ports to INPUT.

Did you change that to INPUT_PULLUP?

Still pulling my hair :confused:

My case wasn’t with arduino-lmic, just bare SPI to RFM95 on mbed and nRF51, so I was in full control of GPIO and in my responsibility. But in your case, on arduino, something like INPUT PULLUP could be what you need. Give it a try.

If you really want to save every last nanoamp it’s better to use external pullups than internal. Or use something more 21th century than a AVR :smile: .

1 Like


The irony is that currently my Atmega setup works but the 21th century STM32 not :smile:
I discovered that with all pins floating (STM32 Standby mode: 2 μA) the RFM95 uses 0.39mA.

The fundamental problem is that the STM32 uses in STOP mode (GPIOs in last setting, all clocks stopped except wakeup timer) in default GPIO state 0.7 mA.
With all pins programmed to analog input (according to the STM manual the most power efficient state) 0.018 mA in the same STOP mode.

So I am currently trading reduced leakage through the RFM pins for a less efficient STM32 GPIO setting. Pulling NSS low drops the current from 0.7mA to 0.4mA so the SPI configuration is probably the root cause, but I have not found a configuration with lower usage than 0.4mA for the STM32 setup.

Perhaps additional external pull up resistors are indeed the only solution.

You’ve got yourself a real catch-22 there indeed. If you go for the analog input sleep option, using pull-ups will probably leak some current through the ADC input capacitor as well. Did you try setting all pins to digital input with internal pull-up OR digital output written low before going to sleep?

I use Freescale excuse me NXP Kinetis MK20 controllers, which are based on the same Cortex cores as the STM32, together with this excellent library for very satisfying results. The library saves the pin state, then puts all pins to output low and goes to sleep. When it wakes again, pin state is restored and the program continues as if nothing happened. The controller uses around 15µA during sleep.

1 Like


I’m a happy man, the total power consumption is at 12µA !

I discovered that the 5V tolerant pins used as DIO inputs consume a lot of current!
Disconnecting the unused DIO-2 and rewiring DIO-0/1 to 3.3V pins solved that.

Setting the SPI and other pins with the following code did the rest:

digitalWrite(PA5, LOW); // SCK
pinMode(PA5, OUTPUT);

digitalWrite(PA7, LOW); // MOSI
pinMode(PA7, OUTPUT);


digitalWrite(lmic_pins.nss, LOW); // NSS
pinMode(lmic_pins.nss, OUTPUT);

// DIO Inputs

pinMode(lmic_pins.rst, INPUT_ANALOG);

// Serial
pinMode(PA10, INPUT_ANALOG);

Ah yes, 5V tolerant pins often have an internal level shifter that does draw significant current. Didn’t know the STM32 had those pins. Excellent work!

1 Like

@epyon Another issue was the USB pullup on PA12 which I missed. :wink: It accounted for at least 0.15 mA.

Thanks for all the feedback!

1 Like


I faced this kind of problem when played with ULPNode, In my case I was powering down RFM module with a mosfet to be sure 0 power will be consumed but had problem with port config pull up/down, I solved with the following code

The one part interesting for you is the part after f (!power)

/* ======================================================================
Function: powerRadio
Purpose : expose driver method of power or unpower the RF module 
Input   : true to power on false to power off
Output  : true if powered and module found
Comments: -
====================================================================== */
boolean ULPNode::powerRadio(uint8_t power)

  // do we need to power up the sensors
  if ( power) {
    uint8_t status_mask = 0;

    // From here and with latest Arduino version we have a problem
    // Arduino SPI library now check if SPI has already been initialized
    // if so, init is not done again and as we changed SS pin and some 
    // others to have full Low Power, we need to enhance back all as it
    // should be done in a real Spi init EACH time.
    //SPCR |= _BV(SPE);

    // Warning: if the SS pin ever becomes a LOW INPUT then SPI
    // automatically switches to Slave, so the data direction of
    // the SS pin MUST be kept as OUTPUT.

    // set back CSN pin with pullup (it was input)
    digitalWrite(RF_CSN_PIN, HIGH); 
    // now set it as output high
    pinMode(RF_CSN_PIN, OUTPUT); 
    digitalWrite(RF_CSN_PIN, HIGH); 

    // Power Up Modules SPI 

    // Enable back SPI and set as MASTER
    SPCR |= _BV(SPE);
    SPCR |= _BV(MSTR);

    // MISO pin automatically overrides to INPUT.
    // By doing this AFTER enabling SPI, we avoid accidentally
    // clocking in a single bit since the lines go directly
    // from "input" to SPI control.
    // http://code.google.com/p/arduino/issues/detail?id=888
    // Not needed because we didn't changed these pins 
    //pinMode(SCK, OUTPUT);
    //pinMode(MOSI, OUTPUT);

    // power ON VCC the radio module

    // RF module settle delay 
    sleepQuickWake( WDTO_15MS );

    if (_radio_type == RF_MOD_NRF24)
      status_mask = RF_NODE_STATE_NRF24;
    if (_radio_type == RF_MOD_RFM69)
      status_mask = RF_NODE_STATE_RFM69;

    // Init the radio driver with moteino config
    if (!driver.init()) {
      // Radio state not OK
      _status &= ~status_mask;
      return false;

    // Radio is okay
    _status |= status_mask;

    // Specific init for RFM69
    if ( _status & RF_NODE_STATE_RFM69) {
      RH_RF69 * prf69_drv = (RH_RF69 *) &driver;

      // Moteino settings
      // Copied from LowPowerLab 
      prf69_drv->spiWrite(RH_RF69_REG_29_RSSITHRESH, 220);

      // default moteino Frequency For 433 MHz 

    // Specific init for NRF24
    if ( _status & RF_NODE_STATE_NRF24) {
      // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm
      //nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm))
  // So this is a power off
  if ( !power) {
    // This will configure the radio pins for correct low power mode

    // We're going to sleep, we've done our job no need to be awake by
    // RF module firing up a IRQ when we're in power down (can cause trouble?)

    // Once agin, very important even if we power off the module, because 
    // of pullup, module still powered via SS/IRQ Pin. if we don't do this
    // even if VDD of RFModule set to "float" using mosfet, current is get
    // drawn by other pins pullup (CS or IRQ), so disable pull up
    pinMode(RF_CSN_PIN, INPUT); 
    digitalWrite(RF_CSN_PIN, 0); 

    // Disable SPI device
    SPCR &= ~_BV(SPE);
    // unpower SPI of Arduino

    // unpower RF module

  return (true);

And for those interested here the code to disable all ATMega328 device for low power

Remember that I power down devices (I2C and SPI) using a mosfet to enable/disable them so all VDD pin of devices is then left float

/* ======================================================================
Function: disableCPUDevices
Purpose : disable Atmel integrated devices (for low power)
Input   : -
Output  : - 
Comments: - 
====================================================================== */
void ULPNode::disableCPUDevices(void)
  // Disable ADC 
  ADCSRA &= ~_BV(ADEN)  ; 

  // disable Analog comparator  
  ACSR |= _BV(ACD); 
  // Disable digital input buffers on all ADC0-ADC5 pins
  //DIDR0 = 0x3F;    

  // set I2C pin as input no pull up
  // this prevent current draw on I2C pins that
  // completly destroy our low power mode

  //Disable I2C interface so we can control the SDA and SCL pins directly
  TWCR &= ~(_BV(TWEN)); 

  // disable I2C module this allow us to control
  // SCA/SCL pins and reinitialize the I2C bus at wake up
  TWCR = 0;
  pinMode(SDA, INPUT);
  pinMode(SCL, INPUT);
  digitalWrite(SDA, LOW);
  digitalWrite(SCL, LOW);  



Even easier to minimize power use during sleep mode is to define unused IO pins as output (commonly used on ATmega/‘Arduino’).

23 posts were merged into an existing topic: Big STM32 boards topic

@BoRRoZ @everyone

I think the STM32 related posts should be placed in the Big STM32 boards topic
Maybe above STM32 posts can be moved to the STM32 topic (where they are easier to find and don’t hijack this topic).

1 Like

done… let’s keep this topic for the RFM95/98.

I stumbled across this conversation about enabeling the PA_BOOST to have +20dBm

1 Like