Projects

Practical LoRa® Long Range Wireless

Tim Blythman

Issue 6, December 2017

This article includes additional downloadable resources.
Please log in to access.

Log in

Better than WiFi for many applications, we take a good look through LoRa tech with a great starter project.

What do you think is the longest distance you could send sensor data without using the mobile phone network or the internet? If you’re thinking about 700km*, then you might have already heard of LoRa. The great thing is, LoRa is available to hobbyists, with LoRa-compatible modules and shields fast becoming readily available.

WHAT IS LoRa?

LoRa is a packet radio technology that allows long distance, low power transmissions. It was designed with the “Internet of Things” (IoT) in mind, and allows remote sensors to operate for long periods on batteries. What is great for makers is that LoRa-enabled modules and shields are now available, so adding LoRa to a project is quite easy.

LoRa technology does not imply a specific frequency, and modules are available covering the most common ISM (Industrial, Scientific and Medical) bands. These bands do not generally require a formal license to use (like mobile phone or TV broadcast frequencies), but also have power limitations and requirements so as not to cause interference with other devices. ACMA is the authority in Australia for radio spectrum licensing and it’s definitely worth checking out the specific license requirements, as there are some places where you can’t use radio equipment due to local restrictions.

LoRa technology is all about how the signal is modulated to achieve the long range in spite of the low power requirement. The first way this is done is by reducing the data rate, which means you aren’t going to be using it to stream video or even audio. In fact, a typical use might be to transmit a handful of bytes at a time (say, every hour), reporting, for example, the temperature near a sensor.

The modules also use spread spectrum modulation, which means that more of the available frequencies are used. For example, a burst of interference on a particular frequency is less likely to be an issue if the signal is spread over a wider part of the spectrum. The other technique is to alter the coding rate, so that each byte is encoded by more than eight bits; this is called “forward error correction”, and means that a byte of data can be recovered, even if one or more bits is lost due to interference.

LoRa also implements an adjustable preamble, which is a sequence that precedes the transmission and helps the receiver “lock on” to an incoming packet, and also an optional CRC, which can be used to verify the packet has been received correctly.

While all this might sound fairly technical, they are all parameters that can be set with the LoRa modules, and knowledge of them can help improve what you can achieve with LoRa, and how you can get around problems if you are not receiving packets correctly. In particular, the transmitter and receiver must have the same settings so that they understand each other. Another way this can be used is to know what settings can be changed if you need to increase your data rate. A general rule is that the slower the data rate (taking into account all the settings), the more “robust” the data will be, and the longer the range will be – although there are limitations that occur when the data rate is very slow.

More and more technologies are incorporating LoRa, especially agriculture (see the Smart Paddock article), where the distances and lack of infrastructure will see the benefits from low power, long range radio technologies.

While the range boasted by LoRa is impressive, it’s not designed to be used for high bandwidth data like video or audio, but rather things like sensor data and remote telemetry; things that an Arduino would be good at doing.

So let’s explore how to use an Arduino to achieve long range data transmission. We’re probably not going to be able to get 700km range (the experiment above involved a balloon flying at 38km!), but I had no trouble getting a kilometre of range, using stock-standard unmodified shields.

What we’re going to be doing isn’t anything extraordinary – reading sensors or controlling pins on a remote board – but the great thing is that this will work beyond the range of most other wireless technologies. On a farm, or in a large warehouse, or even around a sports field, WiFi and Bluetooth simply would not have the range, but LoRa can do the job of activating the sprinklers in the back paddock, or reporting the temperature of the coolroom.

THE BROAD OVERVIEW

The above examples correspond to two basic data types that we might want to transfer: the sprinkler is a Boolean (true/false or on/off), and the temperature is a number. Apart from using the awesome range of our LoRa modules, we want to make sure that the data is sent and received completely and correctly. Some of the strategies we’ll use are not restricted to LoRa, but can be used with other technologies. For example, the LoRa modules I am using have the ability to both send and receive, so it’s entirely possible to get the receiver to send back a “message received” acknowledgment, so the sender can try resending the message again if it fails.

The two main elements for getting this to work are wrapping up the data in a fashion that can be sent via LoRa, and also knowing what the best settings are to use for the LoRa radios to achieve that.

TURNING ON THE SPRINKLER

Of course, I haven’t set up a real sprinkler just to test this out, but I do have an LED on the “remote” unit to simulate an output. It could easily be a relay module controlling a solenoid valve, which activates the water flow, or anything else that can be turned on and off.

The control unit has two buttons and two LEDs: one pair for “on” and one for “off”. We’re going to assume that the sprinkler is so far away that you can’t see it, although for testing, both units are on the same desk. Thus, we need a feedback mechanism to tell us that the sprinkler (aka LED) is doing what we want, which is what the two LEDs are for.

When you push the “on” button, a signal is sent to turn on the output, and if it is received, the output is turned on, and a signal is sent back saying that the current state is on. A similar thing occurs for the “off” button.

As well as responding to inputs, both units periodically send out a status packet (which is the same as the normal packet), so that if a packet is missed, the two units will quickly come back in sync. The LEDs on the control unit will light up brightly if the correct feedback is received, and they’ll appear dull if they don’t have matching feedback. The LEDs will also dim if they haven’t received correct feedback in the last ten seconds.

The Build

We’re using the LoRa shields from Jaycar Electronics, which have the ease of plugging straight onto Arduino main boards. There are also bare modules out there, but you’ll have to know how to wire these up. Here’s what we used for the test setup:

Parts Required: Jaycar Altronics
2 x Arduino Compatible Main Board Uno / Leonardo / Mega XC4410 Z6280
2 x LoRa Shields XC4392 -
3 x LEDs ZD0150 Z0800
2 x Tactile Pushbuttons SP0601 S1120

You’ll also need standard prototyping equipment like a breadboard and jumper leads.

Plug the LoRa Shields into the main boards and make sure their antennas are attached. There are also some optional configuration jumpers, which connect the main board to extra functions on the shield. Since we don’t need them, put them on one pin so that they aren’t connected; this will free up some IO pins as well.

The remote module is easy to set up – I didn’t even bother with a breadboard, just plug the LED between GND and the SDA pin (this is connected to A4 on the Uno – it might need to be changed if you use a different main board).

Standard LoRa shield with indicator LED.
Standard LoRa shield with indicator LED.

The control unit is wired up to a breadboard and looks like this:

The control unit is wired up to a breadboard The control unit is wired up to a breadboard

Low Power with LoRa®

One of the chief aims of the LoRa technology is to use minimal power so that battery-powered sensors are practical for long-term installation. While it’s not something that’s been tested in this project, there are sleep and idle modes available on the modules, which both have nominal current usage of about 1uA. These can be accessed from the LoRa.sleep() and LoRa.idle() functions within the library. Of course, you can’t receive or transmit while the module is in low power mode, so the way the communication occurs has to change.

A simple enough system for a sensor node is to periodically send data (hourly, or even daily), and then go into a deep sleep, which uses minimal power. This will depend on the receiving node always being on, but is not unreasonable for a base station that will be located somewhere with power that is always available (possibly even fitted with a solar panel if in a remote area).

If there is a situation where you need to send data back to a sensor node (such as setting an alarm threshold), then the node can send a packet saying “I’m awake” and the base station will know that it can communicate for a while before the node goes back to sleep. Alternatively, the base station can keep transmitting data in the hope that the node will wake up at some point and receive the data.

The Code

There is an external library that is needed for the Arduino, to be able to communicate with the LoRa shield. It can be found in the Library Manager by searching for “lora”. The author is Sandeep Mistry, and it can also be downloaded for manual install from the GitHub page at https://github.com/sandeepmistry/arduino-LoRa.

You’ll need Arduino 1.6.4 or later to be able to use the Library Manager, but it is the much easier option (click Sketch>Include Libraries>Library Manager…). The LoRa_Booean_RX sketch should be loaded onto the remote unit and the LoRa_Boolean_TX sketch should be loaded onto the control unit. The sketches do not change any of the default LoRa settings, and the sketch should just work. Check that the LEDs on the control module light up when the buttons are pushed. If the light on the remote unit doesn’t change, check the serial monitor on both units to see whether signals are being sent or received.

The code isn’t too in-depth, although the LoRa library behaves differently to other “stream” type libraries, mostly because it is packet-based. You can set up a “callback” function that is called when a packet is received; this ensures you can respond to incoming data as soon as possible. There’s also a command to set receive mode. These LoRa shields are only half-duplex, meaning that they can’t send and receive at the same time; if they did transmit, they’d probably overwhelm the incoming signal anyway.

LoRa.onReceive(onReceive);  
//set up callback function for when packet received
LoRa.receive();  
//set to receive mode (half-duplex only)  

This callback function must be defined somewhere in the sketch.

void onReceive(int packetSize) {  
  //runs if a packet arrives
  char p;
  p=LoRa.read();  
  //just capture the first character
  while(LoRa.available()){LoRa.read();}  
  //empty out rest of packet
  Serial.print("RX:");
  Serial.write(p);
  Serial.println();
  if(p==’i’){s=1;}  
  //updates
  if(p==’o’){s=0;}
  Serial.print("RSSI:");
  Serial.println(LoRa.packetRssi());
  //display RSSI for debugging
  rxtmout=millis();
}  

Here we read the first byte of the packet and ignore the rest, print it to the serial port for debugging, then set the “s” state variable depending on the value. I’ve used “I” and “O” for the output signals and “i” and “o” for the feedback acknowledgements.

We also display the RSSI (Received Signal Strength Indicator) of the packet – the higher the better. For the default settings, you should be able to receive down to about -120dBm, and according to the datasheet, reception is possible at -148dBm.

The final line is part of the timeout process. The rxtmout variable is set to millis() every time data is received, so that if rxtmout and millis() ever differ by more than 10s (10000ms), we know there hasn’t been a signal received in a while; we can report the fault by dimming the LED.

Step it up : Reporting The Temperature

The code for the temperature reporting version is a bit simpler, in that we don’t need to report back whether we’ve received the signal. In this case, the receiving station is likely to be the control station, and can take its own action in the case it doesn’t receive any data. The hardest part is converting the number into a format that can be sent over the LoRa link.

I’ve decided to convert the number into a character array (printable characters) rather than a binary representation of the number. This has one advantage in that the raw data can be interpreted by a person even before it’s processed, making debugging easier. And given that a float data type takes up four bytes, but most temperatures around ambient can be displayed as five characters to two decimal points (e.g., 25.68), we aren’t wasting much bandwidth anyway. Even converting the number to fixed point (where you assume an integer data type is actually a decimal with a fixed number of points – e.g., 234 becomes 23.4 and 120 becomes 12.0) would only take us down to two bytes. Since the packet actually carries much more information that the data itself, a few bytes here makes little difference.

The initial conversion to printable characters is done by the “print” function and the conversion back to a floating point number is done by a single function.

t=atof(p);                          
//convert char array to float  

The Build

I’ve kept this one simple. The displayed data simply goes back to the serial monitor. So all we need, apart from two main boards and two LoRa shields, is a thermistor module to measure temperature.

Additional Parts Required: Jaycar Altronics
1 x Thermistor Module XC4494 Z6320

As before, set up the shields by plugging them into the main boards, and removing and placing all the jumper shunts on just one side of their respective headers. The receiving unit is complete, as this will simply output data to the serial monitor.

The sender simply has the thermistor module hooked up to 5V, GND and A0.

THE SETUP

If you have not already done so, download and install the LoRa library, as mentioned in the previous section, then upload the LoRa_Thermistor_RX and LoRa_Thermistor_TX sketches. Then open the serial monitor on the receiver unit, and you should see an output like the screenshot [1].

1
serial monitor output

If you see only timeout errors, your unit isn’t getting any data, so check the serial monitor on the sender unit to see that it is functioning.

You can test the thermistor is working by holding the end of the thermistor bulb to raise its temperature.

thermistor
A photograph of the assembled module connected
A photograph of the assembled module connected.
Connection diagram for thermistor, using LoRa shield on top of an UNO.
Connection diagram for thermistor, using LoRa shield on top of an UNO.

Go Bigger: Extending The Range

If you’re like me, then you’ve already tested the range of the LoRa shields with the built in antennas, and decided that a bit more range would be handy. There’s a couple of ways we can attempt this, using extra hardware in the form of a better antenna, and by tweaking the LoRa parameters to trade off data speed for signal integrity.

The Build

You might be thinking that we need a specialised LoRa antenna, but because the shields we are using operate at 915MHz, which is close to 3G frequencies, a 3G antenna with an SMA connector can help us out.

Additional Parts Required: Jaycar Altronics
1 x 3G Antenna AR3310 -

The antenna simply unscrews and is replaced by the new antenna. Given that these larger antennas also have a good length of flylead, this gives us the flexibility to carefully place the antenna in a good spot while keeping the electronics tucked away safely in an enclosure.

THE CODE

Within the LoRa specification there are a number of techniques that can be used to balance speed and distance. Fortunately, we can access these parameters through the library we are using. The one important catch is that the transmitter and receiver must be expecting the same parameters, otherwise they will not understand each other. The ways these parameters are accessed in the library is noted here: https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md

There are two main parameters that we can tweak: spreading factor, and coding rate. The spreading factor can vary between 6 and 12 (6 needs some other special conditions, 7 is default), with 12 offering the most robust, but slowest signal.

Similarly, the coding rate can be between 5 and 8, with 5 being the default, and 8 providing the best signal immunity. You might notice that the defaults are at the faster end of the spectrum, so there is heaps of room for improvement of range.

LoRa.setSpreadingFactor(spreadingFactor);
LoRa.setCodingRate4(codingRateDenominator);
LoRa.setSignalBandwidth(signalBandwidth);  

It’s also worth noting that the compromise between speed and accuracy isn’t trivial. Although we’re only sending a handful of bytes, it actually takes almost a second just to send our temperature value at the slower settings, meaning that there is a practical limit to the data we can send; and because we can’t receive while we’re transmitting, there’s more time for received data to be lost. This should also be considered if you have multiple transmitters sending to a single receiver, as they will have to share airtime.

Here is a summary of the distance results I was able to achieve (in an urban area with lots of tall buildings). Of course, other environments will produce different results:

Setup Distance before signal lost
Default LoRa setting, inbuilt antenna 700m
Default LoRa setting, external antenna 750m
Spreading factor 12, Coding rate 8, inbuilt antenna 1100m

For all these setups I was getting the occasional data error, such as incorrect characters being received, which is okay for a testing situation. If I was to implement this in a critical situation, I would implement the CRC option as well, remembering to turn it on at both transmitter and receiver end.

LoRa.enableCrc();  

A CRC is extra bit of data added to the packet to detect transmission errors. While it won’t help recover data, it can avoid processing any data that's received incorrectly.

WHERE TO FROM HERE?

You can see from the above, the range of LoRa is impressive and has great applications in an agricultural environment, or even other places where the likes of WiFi provides insufficient range. Why not build a GPS tracker that simply reports the position of itself back to a base station? Great for keeping track of the cows, golf buggies, or even the kids…!

What if a single LoRa node wasn’t able to transmit far enough? Because practically all LoRa radios are both transmitters and receivers, it’s possible to build a “mesh” of devices to allow packets to take multiple hops to their destination. In fact, that’s why I chose the symbols to be different in our sprinkler example. If we had a case where the control unit and remote unit could not “talk” successfully, we could add a third node in between, which simply receives and retransmits the data, with a slight delay so that it doesn’t try to talk over the top of other transmitters. This works fine because it isn’t possible for the data to get stuck in a loop – if you had multiple retransmitters, you would have to watch out that they didn’t end up passing a packet around in circles and getting locked up. There are even publicly available LoRa nodes popping up, which can pass data directly to the internet, so that it can then be accessed from practically anywhere.

Another thing that may not be obvious from all the discussion so far, is that security is practically non-existent on the LoRa hardware. As long as someone has the same LoRa parameters, they would be able to hear what you are transmitting. Encryption is possible, but not built into the code, so it would need to be added if security is an issue.

As you can imagine, there are many possible uses for long range radio technology, especially where the ongoing cost or power requirements of 3G technology is prohibitive, although LoRa will probably never replace 3G. The experiment noted at the start of the article is a great example – a balloon would have no trouble maintaining line of sight to a station on the ground, and would generate minimal data.

Despite all this, LoRa is the sort of technology that could easily fill those gaps that WiFi doesn’t have the range for, or where 3G is too expensive. Best of all, it’s easily accessible to the maker via easy-to-use shields and modules.

We have already seen commercial applications of LoRa technology taking over in many rural technology solutions. They often have minimal infrastructure in place that they can make use of, and 3G service can often be scattered.

While long-range Cat5e / Cat6 cables can solve many solutions, they still require conduit and trenches, or poles and protection from the elements. The costs quickly add up, and LoRa, even with expensive long-range directional antennas to boost things as far as possible, is a VERY cost-effective choice.