MiniPill LoRa - STM32 Low Power Node

Hello @dvalencia,
I will do some experiments in a few days, maybe I’ll get a solution.
With regards.

Dear @leo_korbee and @dvalencia,

thanks a lot for the replies. Indeed I use a bluepill a (genericSTM32F103C8) board. I provide here a (minimal) code where you (if you like) could see where the problems occurs. Some comments:

  • The ABP works if LowPower.attachInterruptWakeup is removed in the code, i.e.
    if you comment line 60 (LowPower.attachInterruptWakeup) you see that sending via ABP works, the BluePill switched into LowPower mode and wakes up periodically.

  • Low Power wakeup works only if the lora.Send_data is removed in the code, i.e.
    if you comment line 89 (lora.Send_Data(...)) the BluePill sleeps and can be woken up by pressing a button.

Maybe this helps a bit as a starting point. But don’t take too much time; I thought about implementing a displaying method when starting the BluePill, i.e., using the setup() function, i.e. just need to reset the BluePill, then the code in the setup() is executed once.

#include <Arduino.h>
// Low Power for sleeping
#include <STM32LowPower.h>
#include "LoRaWAN.h"
#include "STM32IntRef.h"
// configuration of ABP parameters/keys ...
#include "secconfig.h" 

// RFM95W connection on MiniPill LoRa
#define DIO0 PB10
#define NSS  PA4
RFM95 rfm(SPI, DIO0, NSS);

// define LoRaWAN layer
LoRaWAN lora = LoRaWAN(rfm);
// frame counter for lora
unsigned int Frame_Counter_Tx = 0x0000;

// LowPower
// Sleep this many microseconds.
#define SLEEP_INTERVAL 60000
// Declare it volatile since it's incremented inside an interrupt
volatile bool wakeupbool = false;
// wakeup: Pin used to trigger a wakeup
#ifndef USER_BTN
#define USER_BTN PA15
// wakeup: define pin for waking up
const int wakeuppin = USER_BTN;

// wakeup - LowPower
// this function is a callback function; it will be called when the wakeup is done
// try to avoid any delays or operations which takes time (see library example of lowpower)
void wakeuptask(){
  wakeupbool = true;

void setup() {
  // Serial monitor for debugging:
  Serial.println("test hello");
  //Initialize RFM module
  lora.setKeys(NwkSkey, AppSkey, DevAddr);
  Serial.println("Initialize RFM module done");

  // LowPower: Configure low power at startup
  // LowPower: Set pin as INPUT_PULLUP to avoid spurious wakeup
  pinMode(wakeuppin, INPUT_PULLUP);
  // LowPower: Attach a wakeup interrupt on pin, calling repetitionsIncrease when the device is woken up
  // cirtical code: use interrupt: lora.Send_Data will stuck
  LowPower.attachInterruptWakeup(wakeuppin, wakeuptask, RISING);
  Serial.println("LowPower init done");

  // use this delay for first packet send 8 seconds after reset

void loop() {
  Serial.println("In the loop");

  // define bytebuffer
  uint8_t Data_Length = 0x09;
  uint8_t Data[Data_Length];
  // fill Data with useful data later, e.g. with sensor data
  for (int i=0; i<Data_Length; i++){
    Data[i] = i;

  if (wakeupbool){
    // here something should ONLY happen when wakeup is done
    // e.g. displaying a value
    Serial.println("I woke up");
    wakeupbool = false;
  // LORA, send the data
  Serial.println("I will send");
  //lora.Send_Data(Data, Data_Length, Frame_Counter_Tx);
  Serial.println("I have sent");
  // LORA, increase framecounter by 1

  // take SLEEP_INTERVAL time to sleep
  Serial.println("I sleep now");
  Serial.println("I woke up AFTER sleep");

I use PlatformIO as environment, the platform.ini file looks like:

platform = ststm32
board = genericSTM32F103C8
framework = arduino
upload_protocol = serial
monitor_speed = 9600
lib_deps = 
	; Low Power
	stm32duino/STM32duino Low Power @ 1.0.3

Hello friends,
I managed to create some example code with the LMIC library and a button as wakeup device. I used a small debounce circuit. Here is the link to the example:

A picture showing the very low power consumption in deepSleep mode: 0.5uA!


Have fun!


Worth keeping in mind that you’re working on different processors.

While they’re both ARM cores from ST Micro, @pallago has an STM32F103 which is a fairly early one with a lot of details different than the more recent and LoRaWAN-appropriate STM32L0’s. IMHO questions about an STM32F103 are a bit tangential to this thread.

Dear @pallago,
I cannot see whats’s missing of wrong in your code. Mind the remark @cslorabox wrote, you are running code on a different controller than I do. Some features on your controller can be different, especially on interrupt handling. Did you try another pins for your button?

Here my working example code for the proprietary LoRaWAN library also you used in your code. It runs on my MiniPill LoRa (STM32L051C8T6 controller).

This code is very responsive, you should mind to use debouncing hardware/software.
With regards, Leo.

1 Like

Dear @leo_korbee and @cslorabox,
thanks a lot for the replies, remarks and experiments. Most probably I compared apples and oranges. I did not check the differences between the STM32F103 and STM32L051C8T6 - shame on me.
Anyway, thanks a lot for the support; I really appreciated. I should continue with the appropriate micro controllers.

Best regards!

Dear all,
in the meantime I could obtain a STM32L051C8T6 :slight_smile: . I also used the code provided by @leo_korbee. Thanks to the detailed README I could run the code in platformIO, added the custom board (adopted the minipill_l051c8_lora.json file) and I can “talk” to the STM32L051C8T6. There is one little problem on which I am working the last days: The I2C bus.
I did all the steps, most probably not correct, according to Thanks to the existing custom board, which is provided in the gitlab repo, I started with these files and added the “missing” parts for I2C, which were generated by the STM32CubeMX code after having setup the STM32 and configured the pins appropriately. Comparing the variant files yields:

  • ldscript.ld is the same
  • PeripheralPins.c:
    • in WEAK const PinMap PinMap_I2C_SDA changed the pin to PB_7 and commented out the others (PB_9, PB_11, PB_14)
    • WEAK const PinMap PinMap_I2C_SCL[] changed the pin to PB_6 and commented out the others (PB_8, PB_10, PB_13)
    • commented out the pin PB_6 in WEAK const PinMap PinMap_UART_TX[]
    • commented out the pin PB_7 in WEAK const PinMap PinMap_UART_RX[]
  • variant.cpp - changed the WEAK void SystemClock_Config(void) according to the main.cpp file generated by STM32CubeMX, i.e.
    • changed RCC_OscInitTypeDef RCC_OscInitStruct = {}; --> RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    • changed RCC_ClkInitTypeDef RCC_ClkInitStruct = {}; --> RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    • changed RCC_PeriphCLKInitTypeDef PeriphClkInit = {}; --> RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
    • changed PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1 | RCC_PERIPHCLK_USART2; --> PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2 | RCC_PERIPHCLK_I2C1;
    • deleted PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
    • added PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
  • variant.h
    • added #define PIN_WIRE_SDA PB7
    • added #define PIN_WIRE_SCL PB6

The result that I can see the I2C clock and some data measured by an oscilloscope. However, the data seems to be cut, the data is too short. Does anybody have an idea how why this happens or how to activate the I2C bus?

Dear @pallago,
I already wanted to test the I2C functionality on the MiniPill LoRa, so you gave me a little push. I applied the changes you did, and got at first attempts the same results, or actually no data/clock at all. I did the same test on the STM32F103 (BluePill), with the same result. Then I realised that you have to apply a 10k pull up resistor on the SDA and SCL line, because it is a bus protocol. I got correct I2C signals. Finally I read the data of a TCN75A temperature chip as a test :grimacing: It worked great!. Soon I will update my repository on the MiniPill LoRa.
Regards, Leo.

Thanks again @leo_korbee. I try to communicate with an OLED display via I2C. Using the BluePill the communications perfectly but I struggle a lot with the STM32L051C8T6. (I used the same code.)
Here is a screenshot of the scope with the BluePill (blue CLK, yellow SDA).

And another one with the STM32L051C8T6 (compare timing: us and ms)

I think I imagined everything too easy; I must look more deeply into the code(s) and figure out why the Bluepill period is us meanwhile for the STM32L051C8T6 it is ms, a factor 1000 slower. I did definetly something wrong.

Probably wrong clock divisors. Are you running the L0 off the clock PLL like a conventional STM32, or are you using the internal low power clock that runs just over 2 MHz? Most LoRaWAN nodes would do so, unless they needed to operate the USB capability of the family members that have that.

But I2C actually should work even drastically underclocked.

Hello @pallago,
In the test I did with your changes in the variants files I also got 10 ms clock signal instead of us. I updated the variants files with minimal changes to support I2C and updated the git repository. I think the clock changes you added to the variant.cpp are the cause of this behaviour. In my tests with the new variants files I get a I2C clock of 100kHz (10us).

I2C test 2 on MiniPill LoRa
With regards!

Thanks a lot @cslorabox and @leo_korbee,
I am currently studying the clock settings. I see that I can set many different options. I downloaded and use now the files from but I still see the ms clock. The variant.cpp is still the ‘old’ one? I also added PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI; and tried PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;. But well, since I just started today I need to learn much more :slight_smile:

Okay, I got it working; I copied the newly generated code from CubeMX with the I2C1 clock source from HSI16.

You probably don’t want to use the HSI in a LoRaWAN node, at least not unless you plan to wake up and do a fairly intense burst of processing.

@pallago: I have reproduced your problem with a OLED SSD1306 128x32 with Adafruit drivers. The problem seems indeed the clock config. I leave the variant.cpp as it is for reduction of power as @cslorabox mentioned.
For testing you can put SystemClock_Config(void) function generated by CubeMX in your code and call it in the first line of the setup function. This works fine for me.
I do not usually use OLED displays, when used frequently/constant they burn-in the display.
Thanks for you remarks, I’ve learned again about the clock settings and use of I2C.
With regards.

LoRa Button
With the MiniPill LoRa I have created a node with button activation. Basically it goes into deepsleep forever and is activated by external interrupt (button). It is also possible to combine timed interrupt and button interrupt. Check out There are two code examples available.




MiniPill LoRa and ATtiny84 node ready for The Stack V3
For the folks who want to switch to The Things Stack V3, I’ve made a blog on how to do the switch with these nodes. It can be done with the LMIC library, both OTAA and ABP or a proprietary LoRaWAN library and ABP only. The blog also describes the use of a proprietary “TinyLoRa” kind of library for a ATtiny84 controller (8K of flash).

Have fun!

The issue of TinyLoRa has already been discussed in the context of the new v3 stack and a ‘compromise’ based on the potential lifespan of such devices is largely settled.

So sorry but not sorry. This is the worst sort of hack. Mostly because if the stack doesn’t happen to send the downlinks as you expect, it will be stuck with trying repetitively. And as the deployment of devices continues, ADR becomes more important, so being able to handle actual downlinks is a good thing.

As someone would have to retrieve their ATtiny device to update its firmware with this new code base, for a modest sum they could swap it over to to a Pro Mini, reuse the radio & I can show them how to use the MCC LMiC 3.3 stack that will allow OTAA as well as ABP and process downlinks.

Plus, for your MiniPill, if you have a whole STM32, why use LMiC1.6 which is years out of date, when you have access to MCCI LMiC, a stack that is striving for LoRaWAN compliance?

I appreciate this is a well intentioned effort, but for the end goals of moving TTN to a more robust, resilient and technically correct system, IMHO this is not the right way to do it.

1 Like

Plain and Simple LoraWan (LowPower) Library for STM32

I have made some example project with a new library to keep code simple and to reduce power as much as possible. I am testing now with CR2032 and LR44 batteries. Sleep current is about 1.4uA without sensor and 2.4uA with BME280 sensor. Transmit power is reduced and also the (sleep) power during RX1/RX2 window.

Results of the batteries test will be published as soon as the batteries are depleted. Here is the article already:

Have fun and happy holidays!


Plain and Simple LoraWan (LowPower) Library for STM32 (update)

The previous post was accidentally deleted. Time for an update :slight_smile:
I have made some example project with a new library to keep code simple and to reduce power as much as possible. I am testing now with CR2032 and LR44 batteries. Sleep current is about 1.4uA without sensor and 2.4uA with BME280 sensor. Transmit power is reduced and also the (sleep) power during RX1/RX2 window.

Results of the first batteries test are added (09-07-2023). Here is the article:

Have fun!


Code looks very accessible!

Would be interested how you get on with the higher current draw on Tx - without some SuperCaps I’ve not had much luck with reliability.