The Mini Ultra Pro V2 is capable to be used as a class A LoRaWAN device due to it’s ultra power feature. A typical setup of a rechargeable Li-Ion/Pol battery couple with an external charging source like a solar panel provide the possibility of deploying it in remote location. Various sensors can be attached to the Mini Ultra Pro to monitor various parameters such as temperature, humidity, precipitation, and water level.
The Things Network (TTN) provides a network to relay messages from LoRaWAN devices through gateways. An intuitive back-end (known as the TTN dashboard) provide an easy way to manage your LoRaWAN devices.
In this tutorial, we will demonstrate how to run the Arduino-LMIC library on the Mini Ultra Pro to send data to the TTN back-end. The example will also include the low power feature of the Mini Ultra Pro to enable the board to run from rechargeable Li-Ion/Pol batteries. We will configure the Mini Ultra Pro board to join the LoRaWAN network through OTAA (Over-the-Air Activation) mode instead of the less secure ABP (Activation by Personalization).
We’ll using a Mini Ultra Pro V2 equipped with a RFM95W (either 868 MHz or 915 MHz). A V1 of Mini Ultra Pro can also be used but will require some wire jumpers to connect few pins between the RFM95W and the ATSAMD21G18A MCU.
Rechargeable Li-Ion/Pol battery is optional and you can choose to power the board with USB power. On top of that, if you plan to deploy the unit outside permanently, a 3W 6V solar panel is highly recommended.
In order send our messages from the Mini Ultra Pro, you will either need:
- a nearby LoRaWAN gateway that is registered to the The Things Network (TTN). Make sure that the gateway is within the reach of your radio range.
- running your own LoRaWAN gateway and registering it with TTN.
It is important to note that, we will be using a multi channel LoRaWAN gateway (not single channel gateway) in this tutorial. We will be using a Raspberry Pi based gateway with a RisingHF RHF0M301 frontend module. We won’t be covering on how to setup the gateway in this tutorial but the detailed guide here is easy to understand.
Mini Ultra Pro V2
The Mini Ultra Pro V2 was designed with the intention of connecting it to the TTN by running the Arduino-LMIC library. In order to run the Arduino-LMIC library, few extra connections between the RFM95W radio module and ATSAMD21G18A MCU is required. By default, these solder jumpers are left open and we will need to short them. The DIO2 pin on the radio is only needed when operating in FSK mode, you can leave the solder jumper open in this case.
Mini Ultra Pro V1
On V1 of the Mini Ultra Pro, you can use any free pins (other than D2, D4 & D5) and connect the DIO1 and RST pins up using wires. For consistency with the Mini Ultra Pro V2 In this tutorial, it’s best to use the same pin configurations:
- RST = D3
- DIO1 = D6
The connection used will result in the following LMIC pin configuration:
// Pin mapping const lmic_pinmap lmic_pins = { .nss = 5, .rxtx = LMIC_UNUSED_PIN, .rst = 3, .dio = {2, 6, LMIC_UNUSED_PIN}, };
Instead of sending a less than meaningful data of “Hello world”, we will sending the battery voltage level of our board. We’ll be needing some extra components to do this (yes! yes! we’ll be adding this on the next minor revision of the Mini Ultra Pro). A basic resistor divider network with a capacitor is required to scale down the full voltage of 4.2V of a fully charge Li-Ion/Polymer battery to just below 3.3V. We used a 1 MΩ parallel with a 3.3 MΩ in order to limit the amount of current flowing down this resistor network. But, due to the limited maximum impedance on the microcontroller ADC input pin, the 100 nF capacitor is required to help holding the charge, else the reading will be way off the mark. This simple circuit is best described and discussed by Jean-Claude Wippler of Jee Labs. Here’s the simple schematic of the battery monitoring circuit.
We work with SMD components most of the time, so you’ll rarely see any through hole components in our lab. But, here’s our SMD hack to our battery monitoring circuit. But of course you can use through hole resistor and capacitor to build the same circuit.
Here’s a complete hardware setup with the battery voltage monitoring circuit output fed to analog pin A5 powered with a Li-Polymer battery. You are free to run the setup solely using the USB power but the battery voltage measured will be a random floating number in this case.
We are done with the hardware setup, let’s move on to the code portion and TTN dashboard settings.
Arduino-LMIC Library
The Arduino-LMIC library allows any Arduino compatible board to run Class A LoRaWAN device stack when paired with a LoRa capable radio such as the SX1276 through a SPI interface. The RFM95W LoRa module manufactured by HopeRF uses an SX1276 chip with the output configured to use the PA_BOOST option. By default, this is the radio chip selected by the library as seen on line #10 to #15 in the config.h file in the library source files.
// This is the SX1272/SX1273 radio, which is also used on the HopeRF // RFM92 boards. //#define CFG_sx1272_radio 1 // This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on // the HopeRF RFM95 boards. #define CFG_sx1276_radio 1
Currently, the library supports the 868 MHz (Europe) and 915 MHz (North America) bands. Usage in regions where the frequency band in use does not use the complete number of channels (example Malaysia!) requires some modification to get it up and running.
Depending on your frequency band, you will need to comment/uncomment the necessary line #8 and #9 of the file config.h in the library source files.
#define CFG_eu868 1 //#define CFG_us915 1
The library also require the user to specify which pin (in Arduino pin naming convention) is connected to the radio module. Here’s how it looks like with the Mini Ultra Pro setup:
// Pin mapping const lmic_pinmap lmic_pins = { .nss = 5, .rxtx = LMIC_UNUSED_PIN, .rst = 3, .dio = {2, 6, LMIC_UNUSED_PIN}, };
Basically, these are the only settings required to use the Arduino-LMIC library with Mini Ultra Pro.
As we have covered in our tutorial on using the on-chip RTC module in sleep mode, we will be using the RTC somehow differently in this example. Besides providing calendar (date and time) functionality, the RTCZero library is also capable of providing time keeping in UNIX format. The UNIX time is basically a representation of time in seconds using an unsigned 32-bit variable with the value 0 representing 00:00:00 1 January 1970. This feature on the library provides a very handy and simple approach in timing our periodical transmission in the LoRaWAN network.
As in most low power wireless sensor network, randomization of transmission from node to node provides a simple way of avoiding congestion and on-air collision between them. In this case, the calendar (date and time) functionality of the RTCZero library is not needed here. The RTC module is basically going to be used as a free running seconds timer with alarm enabled. Here’s a snapshot of the code to use the RTC as mentioned above:
// Initialize RTC rtc.begin(); // Use RTC as a second timer instead of calendar rtc.setEpoch(0); // Sleep for a period of TX_INTERVAL using single shot alarm rtc.setAlarmEpoch(rtc.getEpoch() + TX_INTERVAL); rtc.enableAlarm(rtc.MATCH_YYMMDDHHMMSS); rtc.attachInterrupt(alarmMatch); rtc.standbyMode();
The last line of code will put the ATSAMD21 microcontroller into sleep mode and only wakes up upon the trigger of the RTC alarm after TX_INTERVAL seconds. Using this simple approach, we can put the Mini Ultra Pro into sleep mode for a period of time before waking up to do radio transmission.
The Arduino-LMIC library takes care of putting the RFM95W radio module into sleep and the RTCZero library puts the microcontroller into sleep mode. On top of that, the following are the few things that we need to look at in order to have a very low power consumption Mini Ultra Pro board during sleep:
Serial Flash Chip
The on-board serial flash chip must be put into sleep mode when not in use. In this guide, we will not be using the serial flash for logging but we will still initialize the chip and put it into sleep mode (the chip select pin is connected to digital pin D4):
// Initialize serial flash SerialFlash.begin(4); // Put serial flash in sleep SerialFlash.sleep();
Unused IO Pins
Unused IO pins on the microcontroller must not be left floating in an unknown state. Floating IO pins can consumes at least few tens of μA and that can add up quite a bit if you have few of them floating around. Configure unused IO pins to enable the build-in internal pull up.
// ***** Put unused pins into known state ***** pinMode(0, INPUT_PULLUP);
Disable USB Port
The USB port must be disabled whenever going into sleep mode to reduce the current consumption as the internal USB circuitry will consume an additional few hundred of μA. If you plan to deploy the unit in the field without the need to use the USB port while hot plugging them, you can disable them in the power up sequence. In this guide, we will disable and enable the USB port accordingly during active and sleep mode.
// USB port consumes extra current USBDevice.detach(); // Enter sleep mode rtc.standbyMode(); // Reinitialize USB for debugging USBDevice.init(); USBDevice.attach();
Do take note that these event of enabling the USB port and the subsequent RF data transmission is pretty quick that you might not have the time to be able to select the USB COM (COM port number changes usually at least on Linux) to view the serial monitor. Adding some delay upon waking up from sleep would help especially during development work but definitely a thing to avoid for real deployment.
Usage of LED
LED indicator is a very useful tool to indicate the operation of the board. However, turning them on for long period of time will definitely add up to the total current consumption. In this guide, we’ll be using them (yellow LED connected to pin D13) to indicate the transmission process only which is a short burst of time.
The Things Network (TTN) has a very intuitive and simple dashboard to manage your applications, gateway and devices. We will assume that you know how to add an application and adding a device to the application using the TTN dashboard. However, there are quite a few places where mistakes could be made without you noticing them. These mistakes usually resulted in join process either through ABP or OTAA that fails to work. If you follow the following section carefully, you will be less likely to fall into that trap.
Here’s a typical device configuration page on the TTN dashboard for a Mini Ultra Pro:
Device EUI
The Device EUI is an 8-byte unique physical address of a device. On the Mini Ultra Pro V2, this is provided by a 64-bit (8-byte) unique global identifier MAC IC, the 24AA02E64. The example code will retrieve this unique identification during initialization and it will be printed on the USB port. Please copy this value in the same sequence onto the TTN Device EUI section. These MAC address will have the first 3-byte sequence of 0x00, 0x04, 0xA3 for all Mini Ultra Pro V2 boards as these first 3-byte are the Organizationally Unique Identifier (OUI) assigned to Microchip by the IEEE Registration Authority.
Application EUI
The Application EUI is an 64-bit unique identifier that is generated and allocated by TTN server. Application EUI generated by the TTN server have the last 3-bytes as 0xD5, 0xB3 and 0x70. On the TTN dashboard, these are displayed by default as MSB first (0x70, 0xB3, 0xD5,…..). You will need to reverse the order to LSB first before copying them onto the code.
// This EUI must be in little-endian format, so least-significant-byte // first. When copying an EUI from ttnctl output, this means to reverse // the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, // 0x70. static const u1_t PROGMEM APPEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xB3, 0x70 };
App Key
The App Key is a 128-bit unique and secret identifier generated by the TTN server. Copy the App Key as it is onto your code without changing it’s order.
// This key should be in big endian format (or, since it is not really a // number but a block of memory, endianness does not really apply). In // practice, a key taken from ttnctl can be copied as-is. // The key shown here is the semtech default key. static const u1_t PROGMEM APPKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Payload Format
Data are usually send in simple and short binary format in LoRaWAN network as the longer the data, the more air time it requires to send them over. Sending data in ASCII format or sending data in overly precise format (example: 2 decimal point is good enough than 6 decimal point for most data) is a big NO. In this guide, we will be periodically sending a 2-byte application data which represent the battery voltage in int format. To understand how we get the battery voltage reading in the unusual int format, we shall take a look at the code acquiring them. The code basically performs averaging to improve the resolution and reduce noise. The raw ADC reading is then converted into voltage (battery voltage is fed through a resistor divider network). By multiplying with 100, we are basically reducing the decimal point of the battery voltage to 2 decimal point.
adcReading = analogRead(A5); // Discard inaccurate 1st reading adcReading = 0; // Perform averaging for (counter = 10; counter > 0; counter--) { adcReading += analogRead(A5); } adcReading = adcReading / 10; // Convert to volts batteryVoltage = adcReading * (4.3 / 1023.0); // Pack float into int with 2 decimal point resolution voltage = batteryVoltage * 100; data[0] = voltage >> 8; data[1] = voltage;
Payload Formats
As we are sending somehow specially manipulated data over the LoRaWAN network, how does one retrieve the reading on the other side? The TTN dashboard has a feature call Payload Formats. In this section, you will be able to write decoder functions that decodes your incoming binary data from your Mini Ultra Pro board into the format that human can understand. In this example, we will be decoding our 2-byte (int) data into a 2 decimal point battery voltage:
With all that set and done, we are ready to get our Mini Ultra Pro to start sending data over the LoRaWAN network to the TTN backend.
Serial Monitor
The example code is written in such a way that it is closer to real deployment than development. Even though we added the debugging messages and information, the process of going into sleep mode and waking up for a short period of time would mean the USB COM port will appear and disappear from your serial terminal of your choice. If you want to see all the debug information, please add some necessary delay after each wake up from sleep event in the code.
Lift Off
Upon attaching the USB port to your PC and opening a serial terminal program (baud rate doesn’t matter as it is a native USB port), you should be able to see the following information. Please ignore the very first battery reading (if you have the battery attached with the battery monitoring circuit or no battery is being used) as the voltage needs to built up across the capacitor (and the huge resistor) upon power up. Upon successful OTAA join process, you should be able to see the EV_JOINED status and subsequently send the very 1st data packet (yes the wrong invalid battery voltage reading!). As mentioned above, the debug information will no longer be shown on the serial terminal for subsequent data transmission (default 60 s interval) as by then, the board has gone into sleep mode (and disable the USB port) before waking up for a short period of time for data transmission (USB COM port will most likely change so you need to close and open the serial terminal).
On the TTN dashboard application data tab of the device, you should start seeing strings of information such as the OTAA join event and the subsequent data from the Mini Ultra Pro board. Yay, we are in like Flynn!
Sleep Current Consumption
One of the main important feature of Mini Ultra Pro is the capability to run from batteries (Li-Ion/Polymer) for long period of time by residing in ultra low power sleep mode. In sleep mode with the on-chip RTC running, the Mini Ultra Pro board consumes around 19.6 μA of current. With careful integration with more sensor interface and other peripherals, the board will be able to run for a very long time (also depends on how frequent the transmission is performed).
Duty Cycle Limitation
As the name implies, the Arduino-LMIC library was written for the Arduino platform. Several Arduino core features are used as the basis of the LMIC operating system such as the time keeping (millis and micros). These are used to schedule a task such as transmission and to take care of duty cycle limitation in a LoRaWAN network. As we add the sleeping capability onto the code, possible problems (such as transmission blocking due to duty cycle violation) could be introduced as millis and micros are not updated during the sleep period (the RTC seconds timer does update though). We have raised our concern here on the Arduino-LMIC repository although we have yet to encounter any issue (we believe it might happen with high SF setting such as SF=12) As of today (6 September 2017), a proper workaround is yet to be available for the SAMD21 based boards like the Mini Ultra Pro but quick but dirty workaround for AVR based boards can be found here.
Data Transmission Size
The example code only transmit 2-byte of application data which is stored in the data[] array. Please do specify the array size accordingly at the top of the code if you want to send other number of data bytes.
#define MAX_DATA_SIZE 2 static uint8_t data[MAX_DATA_SIZE];
The bulk of the code is derived from the OTAA example provided with the Arduino-LMIC library. Without the great work of Thomas Telkamp and Matthijs Kooijman, we won’t be able to write this guide out! The example code can be downloaded from our Mini Ultra Pro repository.