Application example with mcci-catena/arduino-lmic on STM32L071

Hello everyone,
I wanted to shortly present a port of mcci-catena/arduino-lmic for a STM32L071KBT. The application is an electric fence monitor which in regular intervals wakes up and samples the fence voltage through a resistor divider. Should the voltage fall below a certain level or be not present it sends out a message using an RFM95 module. It also sends out a heartbeat with the current voltage measurement regularly.
You can find the source files for hardware and software at Beware that the current hardware needs a second revision with a few fixes.
The software is based on generated code from STM32CubeMX using the LL drivers. It features:

  • Regular wakeup with RTC.
  • Save of all lmic variables in EEPROM with wear leveling.
  • Comparator + ADC for voltage measurement.
  • Simple battery empty detection with PVD.
  • Current arduino-lmic v3.1.0 with HAL in pure C

The sleep current is ~4uA. So two AA batteries should last a few years.
I hope this helps a few people that want to use this library without Arduino, like I did.
Thanks to terrillmoore and all other contributers to this library.

I am aware that there are similiar projects like which would work just as well. My goal was to have a smaller BOM cost. It should be around 20€ per device in low quantities.

Leave any questions or comments if you like.

One of the hardware problems is the PCB antenna which does not perform as expected. So i resorted to a simple monopole wire for now :slightly_smiling_face:

Best regards


will it work with stm32f103 stm32duino?

You would want to use the unaltered version from mcci-catena/arduino-lmic for that.

1 Like

With stm32duino do you mean:

The name stm32duino is confusing because:

  • It is the name of the STM32 Arduino forum.
    Which has web site URL:
  • It is the name of Github account
    (Where the Arduino_Core_STM32 repository is hosted.)
  • Some use it for Roger Clark’s ‘Arduino STM32’ Arduino core.
  • Others use it for the ‘Arduino Core STM32’ Arduino core, while both cores are different.

It is better and more clear to use the full names of above Arduino cores.
The name stm32duino should be used for the website only and not for an Arduino core.

1 Like

I have installed STM Core by ST in arduino IDE. now which LoRa library is suitable for me?

I use the with mcci-catena/arduino-lmic for LoRaWAN.
But to make easier to work with the lmic I design the library SimpleLMIC to make it more user-friendly. Uses the mcci-catena/arduino-lmic library behind the scene.

#include <SimpleLMIC.h>

const char *devEui = "1111111111111111";
const char *appEui = "1111111111111111";
const char *appKey = "11111111111111111111111111111111";

const lmic_pinmap lmic_pins = {
  .nss = 10,
  .rxtx = LMIC_UNUSED_PIN,
  .dio = {2, 2, 2}

SimpleLMIC ttn;

void setup() {
  ttn.join(devEui, appEui, appKey);

void loop() {
  if (!ttn.isBusy())
    Serial.println("Not Busy!");
    ttn.print("Hello World");

void message(uint8_t *payload, size_t size, uint8_t port)
  Serial.println("Received " + String(size) + " bytes on port " + String(port));
  switch (port) {
    case 1:

See yaa

That is the ‘Arduino Core STM32’.
(In the Arduino IDE it has been given a different name to confuse people even more. I really don’t understand the lack of naming consistency and why they make things so confusing.)

You normally should be able to use MCCI LoRaWAN LMIC library.

There appear to be some timing issues with STM32 in combination with LMIC (both classic and MCCI) but I am not aware of the exact cause and if this has been fixed in MCCI LMIC already. It is fixed in MCCI LMIC for the ST L072Z-LRWAN1 Discovery kit board but only specific for that board (in a board definition file included with MCCI LMIC library). I am not sure how it will behave with other STM32 boards (e.g. bluepill).

As a workaround, for testing purposes I currently use the classic LMIC Arduino library. This library is no longer maintained and does not include the enhancements added to MCCI’s version but at least appears to work properly with both uplink and downlink messages (and hence also works with OTAA). But requires the following:

I use the following workaround to fix the timing issue experienced with downlinks (I determined this workaround empirically):


See the following start topic for Big LoRa32u4 boards topic for where to place above lines in the code.

When using this workaround with MCCI LMIC I ran into certain issues that I have not yet further investigated.

1 Like

If using the newer MCCI version, be aware that the maximum clock error is limited to 0.4% if the LMIC_ENABLE_arbitrary_clock_error is not defined. See also
With the STM32L071 running from the 16MHz HSI a clock error setting of 0.4% works for me. Though I have not tested at highest or lowest expected temperatures yet, which will have an effect on clock drift.

1 Like

I wonder if SPI clock speed has any influence on the timing issues.

The time critical part in Class A is between end of uplink and beginning of downlink Receive Delay 1/2.
To my understanding of the lmic library the following parts play a role here:

  • The end of transmission is time stamped in lmic using the radio_irq_handler() in hal.c. This can be done via interrupts on DIO0 or in software by polling the pin. The software version requires the os_runloop_once() to be called often enough to precisely register an event. So the interrupt version should be preferred if possible.
  • Waiting the exact delay of Receive Delay 1/2 is based on the micros() timer and requires the system clock to be precise. Using a quartz over an internal oscillator is an example of how this could be improved. This is where LMIC_setClockError() can be used to allow more drift over this timespan.
  • A setup time in which latency, also due to SPI clock rate is accounted for is defined in oslmic:128. To my understanding this is where such a constant delay should be corrected.
//! \brief RX_RAMPUP_DEFAULT specifies the extra time we must allow to set up an RX event due
//! to platform issues. It's specified in units of ostime_t. It must reflect
//! platform jitter and latency, as well as the speed of the LMIC when running
//! on this plaform. It's not used directly; clients call os_getRadioRxRampup(),
//! which might adaptively vary this based on observed timeouts.
#define RX_RAMPUP_DEFAULT (us2osticks(10000))

Best regards


That would require ‘hacking’ the library source code which is not the way (anti-pattern) to set configuration parameters.

You are right, it should not be changed at that point. But it is checked if RX_RAMPUP_DEFAULT is already defined and only defines it if not done already.
To change this and other values like spi frequency or debug level which are set in config.h the lmic_project_config.h states:

// We need to be able to compile with different options without editing source.
// When building with a more advanced environment, set the following variable:
// ARDUINO_LMIC_PROJECT_CONFIG_H=my_project_config.h
// otherwise the lmic_project_config.h from the ../../project_config directory will be used.
# define ARDUINO_LMIC_PROJECT_CONFIG_H ../../project_config/lmic_project_config.h

So that is to my understanding where these settings could be applied without messing with the library itself.

With a bluepill board (STM32F103C8T6, 128k) I am unable to get MCCI LMIC library working.
See my response in: Big STM32 boards topic

(Posted in general STM32 boards topic because off-topic for current thread.)

I’m using PlatformIO and define configuration settings in PlatformIO’s platformio.ini project configuration file (which is possible with PltformIO but not Arduino IDE).

I wasn’t aware of RX_RAMPUP_DEFAULT.