MiniPill LoRa - STM32 Low Power Node

Dear @leo_korbee,
I am sorry to bother again. Since you have the same idea regarding a switch I think you will encounter soon the same ‘problem’.
Side remark: I could successfully use and change your code according to my requirements. My STM32 works and transmits the data via ABP to the things network.

Problem: The attachInterruptWakeup; as soon as I introduce it into the code accoding to the example " ExternalWakeup.ino" by STM32LowPower the sending procedure (lora.Send_Data(Data, Data_Length, Frame_Counter_Tx)) stucks. The additional required code for an wakup via button is:

volatile bool wakeupbool = false;
// Pin used to trigger a wakeup
#ifndef USER_BTN
#define USER_BTN PA15
#endif
// LowPower:
const int wakeuppin = USER_BTN;

void wakeuptask(){
wakeupbool = true;
}

void(){

LowPower.begin();
pinMode(wakeuppin, INPUT_PULLUP);
// critical code
LowPower.attachInterruptWakeup(wakeuppin, wakeuptask, RISING);

}

Without the “critical code” your program works fine. Any idea why the data transmission stucks?

Dear @pallago,
At this moment I have no clue. Maybe when I see all the code. Maybe you can send it in a PM or GitHub reference. In a few day the holidays are starting and I have some time to try it for myself. Thanks for the possible pitfalls. Good job you get it running on a STM32F103 - Bluepill I guess.
Regards, Leo.

Hello cslorabox,
Thanks for the tips.I’m a hardware engineer and hence rather solve issues first in hardware. Maybe not the smartest and cheapest way but I try to avoid multiple interrupts and timing issues in software. It sometimes take a lot of time to debug those issues.
Regards, Leo.

Hello @Leo_korbee and @pallago,
I am working on a project similar to yours, I am building a LoRaWan node with a stm32f103c8t6 bluepill and rfm95, I use the MCCI_LoRaWAN_LMIC_library library with the ttn-abp example, I am trying to add a push button that does the function to send an uplink every time I press the button, instead of each TX_INTERVAL, but I have not obtained success, could you guide me or share an example that may be of help to me,
thanks in advance.

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>
// LORA
#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
#endif
// 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.begin(9600);
  Serial.println("test hello");
  
  //Initialize RFM module
  rfm.init();
  lora.setKeys(NwkSkey, AppSkey, DevAddr);
  Serial.println("Initialize RFM module done");
  delay(500);

  // LowPower: Configure low power at startup
  LowPower.begin();
  // LowPower: Set pin as INPUT_PULLUP to avoid spurious wakeup
  pinMode(wakeuppin, INPUT_PULLUP);
  delay(100);
  // 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
  delay(8000);
}


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
  Frame_Counter_Tx++;

  // take SLEEP_INTERVAL time to sleep
  Serial.println("I sleep now");
  delay(100);
  LowPower.deepSleep(SLEEP_INTERVAL);
  //LowPower.sleep();
  Serial.println("I woke up AFTER sleep");
  delay(100);
}

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

[env:genericSTM32F103C8]
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!

A1423F19-4458-4C87-B70E-10E2A2AB5930_1_105_c

Have fun!

4 Likes

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).

https://gitlab.com/iot-lab-org/minipill_lora_prop_button_wakeup

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 https://github.com/stm32duino/wiki/wiki/Add-a-new-variant-(board). 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).
bluepill.display.display

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

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 https://gitlab.com/iot-lab-org/minipill_lora-stm32_low_power_node/-/tree/master/customboard/variants/MINIPILL_L051XX_LORA 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 https://www.iot-lab.org/blog/566/. There are two code examples available.

image

image

2 Likes

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!
Leo.