Wireless control over a long distance with the LoRa modem

Long Range

© Lead Image © 3355m, 123RF.com

© Lead Image © 3355m, 123RF.com

Author(s):

WiFi is convenient for devices that are in the same house. If you want to extend the distance, give LoRa a call.

Tell LoRa I love her… – Ricky Valence

Some variants of the Raspberry Pi come equipped with WiFi, which make them suitable for wireless data gathering and control applications in certain circumstances. However, the range of WiFi is limited, and it generally requires a WiFi-enabled router issuing IP addresses. Beyond WiFi, a number of standards exist for long range wireless communications in the 868MHz and 915MHz license-free bands that are more suitable for embedded applications. One of these license-free bands is LoRa [1], which is widely used and supported by several silicon implementations. The silicon itself is usually packaged onto modules, which are available for sale in the $20 region. These modules are often further mounted on standard PCBs and offered as development kits by the silicon manufacturers and others. This article describes how to build LoRa into a Raspberry-Pi-based telemetry system and outlines the design of a low-cost HAT for the Pi with an integrated PCB antenna (which you can build for less than $10).

Introducing LoRa

The name LoRa is derived from "Long Range," and long range is indeed one of the system's defining characteristics. One group of users claims to have achieved a 476-mile range with a transmit power of just 25mW [2]. LoRa is also low power. In the license-free ISM bands where LoRa operates [3] [4], maximum power is limited by law. But low power is often also mandated by the need for battery operation or dependence on solar energy at a remote location. So it is this combination of low power and long range that makes LoRa attractive for telemetry applications. Extreme range figures like 476 miles should be examined with a critical eye. These results are typically achieved at high altitudes with line of sight between transmitter and receiver and other optimum conditions. However, in an urban environment, ranges of up to a mile are possible with low cost, compact hardware.

All this performance comes at a price of course, as in general, the laws of physics limit range based on transmitted power, distance, signal-to-noise ratio at the receiver, and bandwidth. The more bandwidth, the more data you can transmit in a given time, and bandwidth is a limited resource. The license-free channel most used in the USA is the 915MHz band, with a bandwidth of 26MHz. This bandwidth is divided into channels, and LoRa can be configured to use channel widths of between 125kHz and 500kHz. LoRa uses spread-spectrum techniques to improve noise immunity (and thus signal-to-noise ratio), and, as a result, those longest range setups using a 125kHz channel width may be limited to data rates of a few bits per second, once packet synchronization, channel usage limits, addressing, and error detection are factored in. (Further information on LoRa modulation is available online [5].) These rates are no good for streaming live audio but are perfect for background data gathering and control. After all, to control a relay state, you simply need to deliver one bit. Similarly, the water level in a tank or reservoir, for example to an accuracy of 1 percent, can easily be expressed in an 8-bit packet. See Wikipedia for some more interesting LoRa application examples [6].

First Steps

To get started with LoRa, I purchased a pair of complete modules manufactured by Semtech. This company holds patents on the LoRa standard, manufactures silicon, and sells development PCBs, as well as accessories. However, Semtech licenses to members of the LoRa alliance, so you will find plenty of alternatives. The module I chose was the SX1272RF1BAS [7], which is available from many distributors. The module is equipped with a 16-pin and a 10-pin header (Figure 1). The modem chip is controlled over a four-wire SPI interface, which is supported by the Raspberry Pi, and it is very easy to connect this module without requiring a soldering iron! The downside is that the modules cost about three times the price of the Raspberry Pi, and, of course, they come with no control or other I/O capabilities. I used some jumper wires designed to push onto the Pi's pins to connect to the modules, using the diagram in Figure 2, which was derived from the datasheets for the Raspberry Pi and the SX1272RF1BAS module.

Figure 1: Semtech's module with antenna connected.
Figure 2: Connecting a Raspberry Pi to Semtech's SX1272RF1BAS LoRa module.

With two Raspberry Pis connected to two modules, as well as antennas connected and powered up, it is time to fire up some software. Martin Giess's excellent SX1272 test suite [8] is a great place to start. Log onto each Pi and run the following command to download the test suite:

git clone https://github.com/mngiess/lora-sx1272-test.git

Follow the excellent instructions for reference. (Make sure you have the relevant build tools installed for your chosen distro – see the box entitled "Build Tools.") You should end up with a transmitter and receiver application on each Pi. This software expects to find the SX1272RF1BAS module on SPI bus 0, with CS0, and that's how the modules are wired.

Build Tools

On Linux systems with apt as the repo tool, installing the required build tools is as simple as:

sudo apt-get install build-essential

On Arch Linux systems, enter:

sudo pacman -Sy base_devel

Run the transmitter on one Pi and the receiver on the other. If all goes well, you should see messages on the console of each Pi showing messages being sent by the transmitter and acknowledged by the receiver. That's it. You're up and running with LoRa! Don't forget, because these programs access hardware, they have to run as root.

Hardware Design

The next step is to create a plugin or HAT for the Raspberry Pi. An initial search of the distributors turned up a number of LoRa modules in the price range of $15 to $20. These modules include the SX1272 (or similar) chip, an RF switch (for switching the antenna from RX to TX), and a few passive components. It is not uncommon to find these modules, which are around the size of a large postage stamp, soldered onto a larger PCB to form part of a larger system. But the SX1272 and the antenna switch (PE4259) are available for less than $5. I needed to design a PCB anyway to interface to the Pi, so the module solution seemed overpriced. Certainly, it removes a lot of the uncertainty from the design of the RF part (from the chip to the antenna), but Semtech's website is full of resources, including complete PCB layouts for many of their modules and a lot of technical discussion on good RF layout practice and antenna matching. On top of that, they provide a reference design for a PCB-based antenna.

The resulting compact HAT design has the antenna protruding over the HDMI connector on the Pi, and the RF circuitry itself takes up less than half the HAT PCB area, leaving plenty of space for application-specific circuitry, such as relays or sensor interfaces. This arrangement has the advantage that the whole system can be placed inside a waterproof box for deployment outdoors with no concerns over weatherproofing of the antenna or its cables. It is also much more robust, an advantage in mobile applications. In terms of I/O, the current design simply has two LEDs, as I intended to use external switches and relays in my demonstrator system. I chose to have a local 3.3V power supply derived from the Pi's 5V supply for the modem chip to try to avoid any digital noise from the Pi interfering with the RF signals. The design was implemented using KiCad, an excellent open-source schematic capture and PCB design package that runs on Linux.

The PCB layout is very straightforward (Figure 3), sticking as closely as possible to the RF layout from the SX1272 reference design [9] and the antenna reference design [10]. I increased the physical size of the passive components from 0402 to 0603 to help ease hand-soldering. As I said earlier, the design leaves room for plenty of other circuitry, so it would be easy to modify the existing design to add application-specific circuitry. The one caveat is that the ground plane on the underside of the board forms an integral part of the antenna, so it is a good idea not to pierce it with too many long tracks. The resulting PCB can be assembled by (steady) hand in about half an hour (Figure 4).

Figure 3: The complete LoRa HAT schematic.
Figure 4: The completed design, as rendered by KiCad.

A Practical Telemetry System

To demonstrate the utility of the LoRa modem module, I packaged two Raspberry Pis into waterproof plastic boxes. One Pi was set up as the transmitter running the switch application, with two control switches and an LED mounted externally (Figure 5). Toggling either switch causes a packet to be sent to the receiver. The LED flashes when the transmitter receives an ACK from the receiver.

Figure 5: The switch module.

The second Pi runs the relay application and has a small module with two relays [11] wired and mounted internally, and a single LED mounted externally that flashes on reception of a valid packet from the transmitter (Figure 6).

Figure 6: The relay module.

This is pretty much the simplest application of LoRa. You can configure a lot more, such as an example spreading factor, channel frequency, and bandwidth that allow you to tailor the radio link to a specific situation. I was able to outfit both modules to run on solar-charged battery packs (Figure 7).

Figure 7: Switch and relay modules powered from solar-charged battery packs, ready for deployment.

Software

The software for this system builds on the test software described earlier. The complete code for both switch and relay applications, together with a makefile are available for download from my GitHub page [12]. Only the end-user parts of the code are listed here; for brevity, I have omitted error-reporting code.

Listing 1 shows the switch module. This module sets up the required GPIO pins and then enters a loop looking for pin changes from the external switches. In detecting a change, it sends a message to the receiver to turn on or off the associated relay.

Listing 1

The Switch

§§number
01 #include "SX1272.h"
02 #include <stdio.h>
03 #include <syslog.h>
04
05 int main ()
06 {
07         // set up the LoRa modem
08         sx1272.ON();
09     sx1272.setMode(4);
10         sx1272.setHeaderON();
11         sx1272.setChannel(CH_17_868);
12         sx1272.setCRC_ON();
13         sx1272.setPower('M');
14         sx1272.setNodeAddress(3);
15
16         // set up GPIO: 2 inputs and one output
17         bcm2835_gpio_fsel(RPI_V2_GPIO_P1_18,BCM2835_GPIO_FSEL_INPT);
18         bcm2835_gpio_set_pud(RPI_V2_GPIO_P1_18,BCM2835_GPIO_PUD_UP);
19         bcm2835_gpio_fsel(RPI_V2_GPIO_P1_08,          BCM2835_GPIO_FSEL_INPT);
20         bcm2835_gpio_set_pud(RPI_V2_GPIO_P1_08,          BCM2835_GPIO_PUD_UP);
21         bcm2835_gpio_fsel(RPI_V2_GPIO_P1_16,BCM2835_GPIO_FSEL_OUTP);
22
23         // get the current switch states
24         uint8_t oldLev_08=bcm2835_gpio_lev(RPI_V2_GPIO_P1_08);
25         uint8_t oldLev_18=bcm2835_gpio_lev(RPI_V2_GPIO_P1_18);
26
27         while(1)
28         {
29                 // get the new states
30                 uint8_t lev_08=bcm2835_gpio_lev(RPI_V2_GPIO_P1_08);
31                 uint8_t lev_18=bcm2835_gpio_lev(RPI_V2_GPIO_P1_18);
32
33                 //if state is different, send message and update
34                 if(lev_08 != oldLev_08)
35                 {
36                         char *msg=(char*)(!lev_08?"ON0":"OFF0");
37                         syslog(LOG_USER,msg);
38                 uint8_t e = sx1272.sendPacketTimeoutACKRetries(8, msg);
39
40                         if(e==0)
41                         {
42                                 // flash the LED
43                                 bcm2835_gpio_set(RPI_V2_GPIO_P1_16);
44                                 bcm2835_delay(500);
45                                 bcm2835_gpio_clr(RPI_V2_GPIO_P1_16);
46                                 // update rthe state
47                                 oldLev_08=lev_08;
48                         }
49                 }
50
51                 //if state is different, send message and update
52                 if(lev_18 != oldLev_18)
53                 {
54                         char *msg = (char*)(!lev_18?"ON1":"OFF1");
55                         syslog(LOG_USER,msg);
56                 uint8_t e=sx1272.sendPacketTimeoutACKRetries(8, msg);
57
58                         if(e==0)
59                         {
60                                 // flash the LED
61                                 bcm2835_gpio_set(RPI_V2_GPIO_P1_16);
62                                 bcm2835_delay(500);
63                                 bcm2835_gpio_clr(RPI_V2_GPIO_P1_16);
64                                 // update rthe state
65                                 oldLev_18=lev_18;
66                         }
67                 }
68
69                 // wait a bit & repeat
70                 bcm2835_delay(500);
71         }
72 }

Listing 2 shows the relay module, which sets up the required GPIO pins and then enters a loop listening for messages from the switch module. In detecting a message, it turns on or off the associated relay.

Listing 2

The Relay

§§number
01 #include "SX1272.h"
02 #include <stdio.h>
03 #include <string.h>
04
05 int main ()
06 {
07         // set up the LoRa modem
08         sx1272.ON();
09     sx1272.setMode(4);
10         sx1272.setHeaderON();
11         sx1272.setChannel(CH_17_868);
12         sx1272.setCRC_ON();
13         sx1272.setPower('M');
14         sx1272.setNodeAddress(8);
15
16         // set up GPIO: 3 outputs
17         bcm2835_gpio_fsel(RPI_V2_GPIO_P1_26,BCM2835_GPIO_FSEL_OUTP);
18         bcm2835_gpio_fsel(RPI_V2_GPIO_P1_18,BCM2835_GPIO_FSEL_OUTP);
19         bcm2835_gpio_fsel(RPI_V2_GPIO_P1_16,BCM2835_GPIO_FSEL_OUTP);
20
21         // turn both relays off
22         bcm2835_gpio_clr(RPI_V2_GPIO_P1_18);
23         bcm2835_gpio_clr(RPI_V2_GPIO_P1_26);
24
25         while(1)
26         {
27                 // wait for an incoming message and sens an ACK
28         if (sx1272.receivePacketTimeoutACK(10000) == 0)
29         {
30                         // get the packet data and act on it
31             char *msg = (char *)sx1272.packet_received.data;
32             printf("%s\n", msg);
33             sx1272.getRSSI();
34
35             if(strcmp("ON0", msg)==0)
36             {
37                         bcm2835_gpio_set(RPI_V2_GPIO_P1_18);
38             }
39
40             if(strcmp("OFF0", msg)==0)
41             {
42                         bcm2835_gpio_clr(                          RPI_V2_GPIO_P1_18);
43             }
44
45             if(strcmp("ON1", msg)==0)
46             {
47                         bcm2835_gpio_set(RPI_V2_GPIO_P1_26);
48             }
49
50             if(strcmp("OFF1", msg)==0)
51             {
52                         bcm2835_gpio_clr(RPI_V2_GPIO_P1_26);
53             }
54
55                         // flash the LED
56                         bcm2835_gpio_set(RPI_V2_GPIO_P1_16);
57                         bcm2835_delay(500);
58                         bcm2835_gpio_clr(RPI_V2_GPIO_P1_16);
59                 }
60         }
61 }

Next Steps

For further development, I should point out that LoRa data flow is truly bidirectional. There's nothing to stop the remote node, in this case the relay node, from sending unsolicited packets to the switch node. So any node can be both a sender and a receiver. It is also possible to send broadcast packets to any node listening on the same channel, as well as to send unacknowledged packets, similar to TCP/IP's UDP packets. A complete, freely available WAN implementation called LoRaWAN [13] even turns a bunch of LoRa nodes and a gateway node into a true network, with semantics very similar to TCP/IP.

Conclusion

I hope the system described in this article, and the details of how it was built, will prove of interest. For an in-depth understanding of the modem chip, see the datasheet [14], as well as many of the LoRa resources on Semtech's site. There is always scope for improvement, both in hardware and software. The LoRa HAT board could be put to many different uses with simple changes and additions to the software. The SX1272 software module provides a very simple abstraction of the modem function, so replacing something like an RS485 link or an existing TCP/IP messaging system would be fairly simple. For a more in-depth understanding of LoRa and its applications, see the many excellent YouTube tutorials on the subject – and on low-power radio in general.

The Author

Andrew Malcolm (MIEE, CEng) is a software engineer for Guru Systems (https://www.gurusystems.com/), a fast-growing IoT hardware and SaaS company working on low carbon energy projects In his spare time, he likes to combine software engineering with his first love, hardware engineering. With all the open source tools available, he is never short of things to design. The Raspberry Pi has proved a source of inspiration. To date, Andrew has designed five different add-ons, or HATs. He is currently working on micro-stepping motor drives for a Pi-based laser cutting machine. You can reach him at mailto:andrewrussellmalcolm@gmail.com.