First steps with LoRa Radio Node (Arduino)

The LoRa Radio Node is an AVR Arduino board with a RFM95 LoRa module. This all-in-one LoRa module allows to have a LoRaWan device for a reasonable price around 15€. You need to add a battery (like a LS14500 3,7V battery) on the battery holder for a 4€ extra cost to make it mobile. LiPo option are also available using the power connector. Even if the connectors are looking like grove, they are not compatible so you will have to make your own wiring to connect extensions.

This post is reviewing how to getting started with this board to fire your first LoRaWan frame over The Things Network.

Powering the board

There are different ways to power the board:

  • After some long search on a good way to power this board, I finally found something that looks good ! I’m using 2x 1/2 AA (LS14250) 3V style LiSoCL2 in series to get a 6V input and support the current pic during transmission. Price is about 4.00€ for that pack.
  • You can use a LS14500 LiSo4 battery inserted in the battery holder. This will be the best use of the board as it takes half of the PCB so that’s better to use this space. The limitation of a such battery is the cost as you can’t recharge it. The use of Arduino low power is needed in a such configuration. VBat is passing through the LDO and over a diode to regulate 3.3V. When the battery is under 3.2V the VDD Voltage will be Battery Voltage – Diode Voltage – LDO Voltage drop.
  • You can use a LiPo batteries 3,7V or 7,4V connected to the VCC/GND pins with a 2,54 pin spacing connector. There is an LDO to regulate 3.3V on the board from 2.5V to 16V.
  • You can use the programming cable to supply a 3,3V. This is ok until you fire RF messages as in most of the case the FTDI cable only deliver 50mAh. This is ok for running the MCU, not for emitting on RF.

There is an issue to manage regarding power with this board: when you are using Lithium Thyonil LS14500 you need to take into account the current delivery capacity of the battery. It is normally 50mA not more. It means you can’t transmit on LoRaWan over 12-14dB. If you want to use more power (typically for North America) you need to select another source of power like a LiPo battery, LiFESo4 or Lithium Ion 3,7 & 3,2V can match with this kind of requirement. Check the previously linked post to find the one best matching with your need.

The second issue to manage is a bug into the LMIC library: even if you setup 14dB or less as transmission power, the device will transmit at +20dB. You will have a 120mA consumption peak. With a LS14500 your device will reboot right after trying to connect. Here is a major issue as the RFM95 on the board will require 50mA only when you will transmit at +5dB. You need to consider this point when selecting your battery.

See details of this issue at end of the post.

Programming the board

The LoRa Radio Node is a Arduino Pro or Arduino Mini compatible board. You need to select this type of board in Arduino tool to program it. Then you need to select the ATMega328P, 3,3V, 8Mhz subtype. With this setting you will be able to program the board with a FTDI Cable.

On the left you can see the kind of FTDI module I’m using. This have a male connector so you can easily adapt the pin mapping to the board as the LoRa Radio Node do not have a standard FTDI connector.

The cabling is following this mapping:

FTDI SideArduino board side
DTRDTR
TXRXI
RXTXO
3.3VVCC
GNDGND

Configuring the board

When you want to use that board with a LoRaWan stack you need to assign some of the pins to the RFM95 signals. The pin mapping is the following one:

Arduino GPIORFM95 signal
D10NSS (Chip select
D9Reset
D2DIO0
D5DIO1
D6DIO2
D7DIO3
D8DIO5

RFM95 DIO4 is unconnected.

The DIO1, DIO2, DIO3, DIO5 are not connected directly, they are connected through a Header J3 and, by default unconnected. This will have an impact when implementing the LoRaWan stacks as usually DIO0 and DIO1 are at least needed. So you will have to short the J3 corresponding pins. Only these pins need to be shortcut.

Please note this important point.

Minimal setting you need for having LoRaWan stack working with Interruption. Connect the J3 pins like above.

The Board led is as usual with Arduino associated with D13. This pin is also used by SPI so once the LoRaWan part is setup you can’t use it anymore. In case you want to use it, you need to SPI.end(), then pinMode it for using it and after being back with SPI.begin().

You can fin the detailed of the schematics on project GitHub.

Demo program

Now, as the setup is completed, we can upload our first sketch ! The sketch is the one I used for the RFM95 test previously. You can go to my post on RFM95 on Arduino to get it.

The only modification is the following lines for the configuration:

// Pin mapping
const lmic_pinmap lmic_pins = {
    .nss = 10,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 9,
    .dio = {2, 5, LMIC_UNUSED_PIN},
};

If you directly want to load a already configured sketch with a simplified access to the LMIC function you can take a look to my sample demo code for LoRa Radio Node V1.0 on Github.

The size of the LMIC is really limiting the ability to add code into your sketch, but with some tuning in the LMIC configuration you can get a lot of free space:

Add this in the LMIC project config file

#define LMIC_LORAWAN_SPEC_VERSION   LMIC_LORAWAN_SPEC_VERSION_1_0_2
#define DISABLE_BEACONS
#define DISABLE_PING
#define DISABLE_LMIC_FAILURE_TO
#define DISABLE_MCMD_DutyCycleReq
#define DISABLE_MCMD_RXParamSetupReq
#define DISABLE_MCMD_NewChannelReq
#define DISABLE_MCMD_DlChannelReq
#define DISABLE_MCMD_RXTimingSetupReq
#define LMIC_MAX_FRAME_LENGTH 64

LMIC and deep sleep

The LMIC is counting tics to manage the duty-cycle. When the device is set to deepsleep, the ticks number is not increased and as a consequence you can have difficulties in emitting after a certain time. To manage this you can add the following line in the file hap.cpp of LMIC library:

uint64_t hal_compensate_tics = 0;
u4_t hal_ticks () {
 ...
 return (scaled | ((uint32_t)overflow << 24)) + (hal_compensate_tics);

This allows to add a static number of tics to the tics counter. So later in your project code you can add the following code:

extern uint64_t hal_compensate_tics;
void updateHalTime(uint64_t ms) {
  // We have 62.5 tics per ms -- 1 tics = 16uS
  hal_compensate_tics += (625*ms)/10;
}
...
loop() {
    ...

    LowPower.powerDown(SLEEP_8S, ADC_OFF,BOD_OFF);
    updateHalTime(8000);
}

Low Power & Power question

As in the previously given Github sample code, you can run the Arduino MCU low power mode. The result is quite good with a 70uA consumption during the sleep phase. I’ve been surprised by the 120mA peak on transmission as in Europe we are more around 60mA for 14dB. Let me know if you achieve lower consumption during sleep.

LoRa Radio Node v1.0 power consumption

The peak of current is due to a bug in the LMIC library for RFM95 (and others) board. According to the Semtech documentation the consumption during transmission may be the following:

Semtech SX1276 power consumption at transmission

The 120mA are corresponding to +20dB. This is out of standard and regulation for Europe so be careful with this.

So to go back to the normal transmission power and normal current consumption a battery will support you can make some change in the LMIC code until a bugfix will be provided.

Edit the radio.c file in the LMIC library and change as following: (I’m not fan of it actually bust its the best compromise I found.

static void configPower () {
#ifdef CFG_sx1276_radio
    s1_t pw = (s1_t)LMIC.txpow;
    if ( pw >= 20 ) {
        // limit current at 130mA / Activate PA Max Power
        writeReg(RegOcp,0x31);
        writeReg(RegPaConfig, (u1_t)(0x8F));
        writeReg(RegPaDac, (readReg(RegPaDac)&0xF8)|0x7);
    } else if ( pw > MAX_RFO_PWR ){
        // limit current at 130mA / Activate PA Max Power
        // use PA_BOOST
        if ( pw < 0 ) pw = 0;
        if ( pw > 15 ) pw = 15;
        writeReg(RegOcp,0x31);
        writeReg(RegPaConfig, (u1_t)(0x80) | ( pw - 2 ));
        writeReg(RegPaDac, (readReg(RegPaDac)&0xF8)|0x4);
    } else {
        if ( pw < 2 ) pw = 2;
        if ( pw > MAX_RFO_PWR ) pw = MAX_RFO_PWR;
        // limit current at 80mA / use RFO 
        // max power 13dB on RFO (PA_HF)
        writeReg(RegOcp,0x27);
        writeReg(RegPaConfig, (u1_t)(0x00) | ( ((10*MAX_RFO_PWR - 108)/6) << 4 ) | (pw+(15-MAX_RFO_PWR)) );
        writeReg(RegPaDac, (readReg(RegPaDac)&0xF8)|0x4);        
    }

In the lmic_project_config.h you need to add the following setting:

// Max RFO Power 0 when no power amplifier behind RFO
#define MAX_RFO_PWR 0

This is due to the RFM95 module: this module only have PA_BOOST connected and not RFO. As a consequence you have an over consumption for low power transmission. For +20dB you need 120mA (normal) but for +14dB you need 93mA. At +5dB you will consume 55mA. I assume the reason is related to the RFM95 module on boarded. I assume they are using 915Mhz version for all zone. Due to unmatching with the 868Mhz it is possible to get a such over-consumption.

36 thoughts on “First steps with LoRa Radio Node (Arduino)

    • DIOx are Semtech SX1276 interrupt lines. The LoRaWan stack is using some of them. So you need to connect some of them as indicated in my post.

    • You may ask to board developers 😉
      I see two reasons:
      – The number of available IO on the board is really low, so it’s good to have some potentially available.
      – The DIO interrupt depends on the use of the communication (LoRa, LoRaWan, FSK …) so none of them are all needed depends on your use case. I assume you can also use the chip in a non interrupted mode.

  1. Where is pin D2 on this board for connecting DIOo?

    Sorry to be critical, but I found this explanation of pins and jumpers really unclear:

    The DIO1, DIO2, DIO3, DIO5 are not connected directly, they are connected through a Header J3 and, by default unconnected. This will have an impact when implementing the LoRaWan stacks as usually DIO0 and DIO1 are at least needed. So you will have to short the J3 corresponding pins. Only these pins need to be shortcut.

    • I think you should take a look to the bottom of the board where the connector names and pin mapping is printed …
      BTW, as a picture is better than responding xTimes the question, I’ve updated the blog post.

  2. Thanks Paul.
    Sorry for the multiple enquiries but this Elecrow board has been driving me nuts with no progress for a week. I tried that jumper connection and your suggested sketch with pin mappings but nothing works. I guess the board is faulty somehow. Binning it and going to use separate Arduino Mini Pro (3.3 V and 8 MHz).

  3. Made some nodes with temp IR and SHT10 sensors with your sketch. It works good after I changed the antenna’s. Has somebody experience with this node to read the battery value? I like to send the battery value also to the Cayenne platform.

    • Thank you for your feedback. There is nothing really made on that board to measure battery level. You can use the VCC pin with a voltage divider and connect it to one of the ADC pin.

  4. hi Paul,
    Do you know if the power consumption bug has been fixed in the latest revision of the library? It seems that the configPower () function has changed and the workaround you presented here is no longer applicable.

  5. Hi Paul,

    I am trying to make use of this board together with a Ultrasonic sensor, which needs a step up and the level shifter for the signals. All is connected and good, but then the board keeps on recycling every 1 second.. Driving me nuts… I am using a Li-ion 3.7V 14500 battery that claims to provide up to 1A but I have no luck.. any ideas of what can be wrong..? It seems stable as longs as I do not connect the trig pin for the ultrasonic sensor.. I am borrowing a circuit design from here
    https://forum.mysensors.org/assets/uploads/files/1496348100985-upload-8af864a8-ed84-400a-ad21-5adc67c72da4.png

  6. Hi Paul, I hope u re doing great, look I have a problem, My end-node is always getting an empty downlink like this:
    2222523: Received downlink, window=RX2, port=-1, ack=0
    The problem is when I try to send a real Downlink for example: 01 00 00 3C
    In arduino consolo I get this:
    Received :
    4 bytes of payload
    40 2 0 71
    8316450: engineUpdate, opmode=0x800
    The problem is that im getting a different payload from the one Im sending. Any thoughs?
    Regards Jose

  7. I have really good power results with this module. I am testing the battery and with about 600 sensor readings and transmissions at highest power in last 5 days, the battery did not drop even by 0.01V! I am using bigger battery 18650 but still, very impressive!

  8. Pingback: LoRa | wer bastelt mit?

  9. Pingback: LoRaWan solar gateway monitoring - disk91.com - technology blogdisk91.com – technology blog

  10. Could anyone tell me which version of “MCCI_LoRaWAN_LMIC_library” you are using? I finally got it to compile but am now stuck at joining TTN.

  11. Fof anyone interested, the following connectors fit perfectly (Ebay)
    Stemedu LoRa Radio Node V1.0 868MHz RFM95 SX1276 for Arduino ATmega328P 3.7-12V uFL

    Good luck!Roger Burris

  12. Hello, I use this board as a transmitter for my little weather station.
    I use a battery type 18650 with 8800 mAh. The battery lasts almost 20 days. Since I’m a programming newbie, I don’t have the knowledge to extend this term significantly. I always send a data packet from my BME280 with LoRa every 5 minutes and then put the board to sleep. Can someone give me tips on how to optimize it?

    Hier der Code:

    // More information at:
    // Build: 2019-DEC-20
    // → SENDER –
    // SyncWord(0x77)
    // Board Arduino „Pro or Pro mini“
    // Processor „ATmega328P (3,3V, 8MHz)“

    #include
    #include
    #include “LowPower.h”
    #include
    #include
    #include

    String iot_id = “KW2”; //ID of Station

    float temperature = 0;
    float humidity = 0;
    float pressure = 0;

    String LoRaMessage = “”;

    Adafruit_BME280 bme;

    void setup() {
    Serial.begin(115200);

    while (!Serial);
    Serial.println(„LoRa Sender“);
    Serial.println(“BME280 test”);

    bme.begin(); // Adresse I2C: 0x076 Please proof the library!!

    // bme.begin();
    bool status1 = bme.begin();
    if (!status1) {
    Serial.println(“Could not find a valid BME280 sensor, check wiring!”);
    while (1);
    }

    if (!LoRa.begin(868E6)) {
    Serial.println(“Starting LoRa failed!”);
    while (1);
    }

    // The sync word assures you don’t get LoRa messages from other LoRa transceivers
    // ranges from 0-0xFF
    LoRa.setSyncWord(0x99);

    Serial.println(“LoRa Initializing OK!”);

    }

    void loop() {
    delay(1);
    temperature = bme.readTemperature();
    humidity = bme.readHumidity() + 8;
    pressure = bme.readPressure() / 100.0F;

    Serial.print(temperature, 1); Serial.print(“°C „);
    Serial.print(humidity, 0); Serial.print(“% “);
    Serial.print(pressure, 0); Serial.println(„hPa“);

    // send packet every 5 min.
    LoRaMessage = String(iot_id) + ‚/‘ + String(temperature,1) + ‚&‘ + String(humidity,0) + ‚#‘ + String(pressure,0);
    //Send LoRa packet to receiver
    LoRa.beginPacket();
    LoRa.print(LoRaMessage);
    LoRa.endPacket();

    Serial.print(„LoRaMessage:“);
    Serial.println(LoRaMessage);

    delay(500);

    // 37 x 8 sec = 296 sec + 4 sec → 5Min. SLEEP

    LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF); //Goto sleep 4 sec
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //Goto sleep 8 sec
    … 35x
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //Goto sleep 8 sec

    }

    • Every 5 minutes isnt a litle overkill?
      Try to reduce te time that data is sent, to about every 20 minutes.

      • depends on what you want to do … basically this is just a tutorial, 5 minutes is better to debug, do not have to wait for 20 minutes to confirm you can get something a second time 😉

    • There must be something wrong with your power management. 8800mAh battery should last for years with LoRa. I use 600mAh battery and I am sending LoRa every hour just for test and I have voltage loss about 0.07V per month. It is running for about 7 months now and battery is at 3.72V. Write me mail at jbrepo – at – gmail . com and I will send you my script.

  13. Hello, I build low power sensors for years. Couple of tips:
    1) Power BME280 by atmel pins only when needed and then set pins as input, it will be powered off during deep sleep
    2) Use deep sleep
    3) Instead of using INA219 get internal chip voltage with this. You need to calibrate by adjusting Vconv:

    float readVcc() {
    /*
    long result;
    // Read 1.1V reference against AVcc
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // orig for Pro Mini
    //ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // modified for 1284P
    delay(2); // Wait for Vref to settle
    ADCSRA |= _BV(ADSC); // Convert
    while (bit_is_set(ADCSRA,ADSC));
    result = ADCL;
    result |= ADCH<<8;
    result = 1105000L / result; // Back-calculate AVcc in mV
    return result;
    */
    //return 0.0;

    int m_count = 10;
    float Vcc = 0.0;
    float Vconv = 561.0;
    for (int i=1; i <= m_count; i++) {
    Vcc = Vcc + analogRead(35)/Vconv/m_count;
    delay(50);
    }
    Serial.println("Battery: "+String(Vcc)+"V");
    return Vcc;

    }

  14. Hello,
    I would like to reset the board every 12 hours using my program. Is that possible?
    Best wishes

    • I assume you can try something like this :

      void(* resetFunc) (void) = 0; //declare reset function @ address 0

      But it is not really good to reset on every 12 hours, it’s a bit short for a LoRaWan session in my point of view.

  15. Hello,

    I have an issue with your demo code for the LoRa Node V1.0. When I want to upload the sketch on the board an error occured. It says the board hasnt enough dynamic storage (global variables 2462 Bytes (120%); data section exceeds available space in board). What am I doing wrong?

    Thank you very much for help.

    • That’s a good question I did not had a such experience, make sure you are using the right library with the right options (like disabling Beacon and other un-needed code)

  16. If you but a rechargeable battery and also connect the 5V input to a solar panel with a voltage regulator, will that charge the onboard battery or the 5v input is only to power the board?

  17. Hi, good description. I have been trying to connect a max485 (receiving data from a soil sensor) to this atmega328P but it seems the pin 2 is already used as dio0. I tried other pins but to no avail. The pins D5-D8 dont seem to do. Can anyone help.

  18. Hello
    I have tried to make it work with helium network without success could you tell me if something is wrong in the code

    #include
    #include

  19. Hi Paul, sorry for the inconvenience, I would like to ask why with your example I only get this message on the serious port: 4007694: EV_TXSTART
    4409291: EV_JOIN_TXCOMPLETE: not JoinAccept
    4411631: EV_TXSTART
    4791899: EV_JOIN_TXCOMPLETE: not JoinAccept
    , I have already configured in TTN but I cannot communicate, if I have been able to communicate two boards together, one point to point, but not against a server

    • Because it does not receives the Join Accept.
      The common issue is when the device is too close to the gateway.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.