Important update (2019-06-02)
After publishing this post, I received some relevant remarks from LoRaWAN experts who are active in the The Things Network community. The most important is the fact that the system here described is not based on a true gateway because my implementation is not compliant with the LoRaWAN specifications.
Further issues:
- The test device should never send ASCII text
- We should never use APB as activation method
- We should never disable frame counter check.
I’d like to reiterate that this is a simple test implementation, which I assembled in my very limited spare time to get familiar with LoRaWAN. It is not an example of a real LoRaWAN system at all!
I stronlgy encourage the readers to check this thread out for more details: https://www.thethingsnetwork.org/forum/t/rpi-3-b-dragino-lora-gps-hat-v1-4-pro-mini-3-3v-node-a-working-example.
Introduction
As stated by the LoRa Alliance official website, LoRaWAN™ is …
… a Low Power, Wide Area (LPWA) networking protocol designed to wirelessly connect battery operated ‘things’ to the internet in regional, national or global networks, and targets key Internet of Things (IoT) requirements such as bi-directional communication, end-to-end security, mobility and localization services.
Even though I have never worked professionally on a LoRaWAN™-based project, this new technology has piqued my curiosity. For instance, I thought that it could be useful to install wireless home sensors where you don’t have a power socket. To date, I don’t have the need to connect specific sensors but I’m confident that I’ll figure it out something interesting in the future. So I have decided to play with this technology to in the meantime. Specifically, I have chosen to design a simple system that would allow me to experiment in the future with actual devices.
The project started last spring when I googled around to get more information about LoRa and LoraWAN™. In essence, a LoRaWAN™ system is composed of four elements:
- Devices (usually sensors)
- Gateway
- Network server
- Application server.
With regard to the network and application servers, I decided to use the infrastructure provided by “The Things Network” aka TTN.
The Things Network is an open Internet of Things infrastructure supported by its members. Members contribute by placing gateways or running network servers. Together we create a secure and redundant collaborative network. The Things Network is growing towards a robust and stable global network, providing connectivity where it is needed.
If you’re developing applications and cloud services using The Things Network, you can rely on support through great documentation and the global DIY community. In case you require more dedicated, instant support, we have a professional community of businesses that are happy to assist.
The Things Network allows to create applications to exchange data with your devices—often referred to as nodes as well—as shown here. In my case, my goal was to create this simple unidirectional data flow: device -> gateway -> network server -> application server.
In short, I took the following steps to achieve it:
- I signed up on TTN
- I built a simple single-channel gateway
- I built a cheap testing node
- I registered the gateway and the device on the TTN platform
- On the TTN platform, I created a simple application to collect data flowing from the testing node
- I performed some testing to check the proper operation of the whole system.
The Application
To add an application on the TTN application server, I followed these instructions. I named it llandre_test1. I didn’t need any complex data processing as I just had to see the received data for testing and debugging purposes.
The Gateway
According to the map available on The Things Network website, there were no TTN gateways in my area. So I decided to build a simple one to be installed in my backyard. To do that, I bought a Raspberry PI 3 Model B and a Dragino LoRa GPS HAT.
Once again, I take the opportunity to say that I would never use a board like the Raspberry PI to implement a real product. For many reasons, including but not limited to reliability, in my opinion, these boards are OK to make Proof of concepts (PoC) or hobbyist projects only. This project confirmed the poor reliability of such a solution: during my testing, the root file system—which is stored on a microSD card—corrupted irreparably.
Apart from the basic services, the gateway just needs to run the packet forwarding software. This module retrieves the LoRa packets and relays them to a network server. Consequently, the setting up the gateway is straightforward.
I started from a Raspbian Jessie Lite distribution. I installed it following the official instructions. I used this version of etcher to create the microSD card.
The following list details the operations that I did to customize the configuration of the gateway:
- Enabling ssh server
- Changing the hostname
- Installing git
- Changing the default password
- Configuring the WiFi interface
- In this regard, I edited the /etc/wpa_supplicant/wpa_supplicant.conf file and I encrypted the password in order to avoid using clear text
- Selecting the right timezone to get NTP client to set up the time properly on startup (see raspi-config documentation for more details)
- Enabling the SPI interface.
For what concerns packet forwarding, two options are available:
- Semtech UDP Packet Forwarder
- TTN Packet Forwarder
I chose to use the first one, which is simpler to manage although it provides fewer functionalities. To run it, I had to install the wiringPi library. The source code of the forwarder is available here. I made a couple of modifications to the main.cpp file in order to make it work. I changed the IP address associated with the server:
#define SERVER1 "52.169.76.203"
I also replaced the invocation of the die() function with perror() in order to avoid exiting the program in case the network connection is down (this happened a couple of times during my testing).
To run the forwarder on startup, I edited /etc/rc.local by adding the following lines:
# Start LoRa single channel packet forwarder /home/pi/devel/lora/pkt-forwarder/legacy-semtech-UDP/single_chan_pkt_fwd-master.am/single_chan_pkt_fwd.sh &
The single_chan_pkt_fwd.sh script looks like this:
#! /bin/sh /home/pi/devel/lora/pkt-forwarder/legacy-semtech-UDP/single_chan_pkt_fwd-master.am/single_chan_pkt_fwd 2&1 | logger
To complete the setting up of the gateway, I registered it by using the TTN. The procedure is explained here.
The Test Device
In order to check the proper operation of the gateway, I needed a test device to send known data. The purpose was to send some data from the node to the gateway. In turn, the gateway had to forward this data to the TTN server. If everything works fine, you should see these data in the console. I couldn’t find a simple and cheap device, so I decided to build one. Before doing that, I had to register it, however. In this case, I used the ttnctl command-line program. It is way more powerful and flexible than the graphical web interface. The following box shows the registration procedure that I did on my Linux PC. I named the node as llandre_test1_dev1.
llandre@jbhome1$ ttnctl applications list INFO Found one application: ID Description EUIs Access Keys Collaborators 1 llandre_test1 Test node 1 1 1 llandre@jbhome1$ ttnctl applications select INFO Found one application "llandre_test1", selecting that one. INFO Found one EUI "70B3D57ED000F981", selecting that one. INFO Updated configuration AppEUI=70B3D57ED000F981 AppID=llandre_test1 llandre@jbhome1$ ttnctl devices register llandre_test1_dev1 INFO Using Application AppEUI=70B3D57ED000F981 AppID=llandre_test1 INFO Generating random DevEUI... INFO Generating random AppKey... INFO Discovering Handler... Handler=ttn-handler-eu INFO Connecting with Handler... Handler=eu.thethings.network:1904 INFO Registered device AppEUI=70B3D57ED000F981 AppID=llandre_test1 AppKey=7E66EE9B060387E825CE0BB0C932CBCD DevEUI=0014CC88F2F45E92 DevID=llandre_test1_dev1 llandre@jbhome1$ ttnctl devices personalize llandre_test1_dev1 INFO Using Application AppEUI=70B3D57ED000F981 AppID=llandre_test1 INFO Generating random NwkSKey... INFO Generating random AppSKey... INFO Discovering Handler... Handler=ttn-handler-eu INFO Connecting with Handler... Handler=eu.thethings.network:1904 INFO Requesting DevAddr for device... INFO Personalized device AppID=llandre_test1 AppSKey= DevAddr=26011CB4 DevID=llandre_test1_dev1 NwkSKey= llandre@jbhome1$ ttnctl devices info llandre_test1_dev1 INFO Using Application AppEUI=70B3D57ED000F981 AppID=llandre_test1 INFO Discovering Handler... Handler=ttn-handler-eu INFO Connecting with Handler... Handler=eu.thethings.network:1904 INFO Found device Application ID: llandre_test1 Device ID: llandre_test1_dev1 Last Seen: never LoRaWAN Info: AppEUI: 70B3D57ED000F981 DevEUI: 0014CC88F2F45E92 DevAddr: 26011CB4 AppKey: AppSKey: NwkSKey: FCntUp: 0 FCntDown: 0 Options: FCntCheckEnabled, 32BitFCnt
Then, I built a very simple device by following these instructions. I used a cheap Arduino Pro Mini 3.3V operating at 8MHz. It is worth remembering that I experienced some difficulties when downloading the firmware image to the device. To do that, I had to keep the microcontroller in reset until the Arduino IDE finished to build the project. By releasing the press button right after the end of this operation, the firmware was downloaded to the device through the USB port successfully. If I released the button too early or too late, the IDE wasn’t able to establish the communication with the bootloader and the downloading failed.

To build the firmware, I downloaded the source code of Arduino-LMIC library from here. The device needs to be activated before it can communicate via TTN. As explained here, there are two activation methods available. For the sake of simplicity, I chose to use APB. Consequently, I used this sketch as the starting point. I changed it a little bit. Apart from entering the device address, the Network Session Key, and the Application Session Key that are generated during the registration processes, I added some debugging stuff. I also reduced the transmission time interval to 10 seconds to make the testing easier.
diff --git a/examples/ttn-abp/ttn-abp.ino b/examples/ttn-abp/ttn-abp.ino index a8b4a18..eef8610 100644 --- a/examples/ttn-abp/ttn-abp.ino +++ b/examples/ttn-abp/ttn-abp.ino @@ -36,15 +36,16 @@ // LoRaWAN NwkSKey, network session key // This is the default Semtech key, which is used by the early prototype TTN // network. -static const PROGMEM u1_t NWKSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; +static const PROGMEM u1_t NWKSKEY[16] = { put your Network Session Key here }; // LoRaWAN AppSKey, application session key // This is the default Semtech key, which is used by the early prototype TTN // network. -static const u1_t PROGMEM APPSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; +static const u1_t PROGMEM APPSKEY[16] = { put your Application Session Key here }; + // LoRaWAN end-device address (DevAddr) -static const u4_t DEVADDR = 0x03FF0001 ; // -- Change this address for every node! +static const u4_t DEVADDR = 0x26011CB4 ; // -- Change this address for every node! // These callbacks are only used in over-the-air activation, so they are // left empty here (we cannot leave them out completely unless @@ -58,7 +59,7 @@ static osjob_t sendjob; // Schedule TX every this many seconds (might become longer due to duty // cycle limitations). -const unsigned TX_INTERVAL = 60; +const unsigned TX_INTERVAL = 10; // Pin mapping const lmic_pinmap lmic_pins = { @@ -134,19 +135,48 @@ void onEvent (ev_t ev) { } void do_send(osjob_t* j){ + static unsigned long int counter = 0; + //uint8_t data[256]; + + //sprintf(data, "Hello, world! #%u", counter); + // Check if there is not a current TX/RX job running if (LMIC.opmode & OP_TXRXPEND) { Serial.println(F("OP_TXRXPEND, not sending")); } else { // Prepare upstream data transmission at the next possible time. + Serial.print("counter = "); + Serial.println(counter, DEC); + + Serial.print("LMIC.txpow = "); + Serial.println(LMIC.txpow, DEC); + Serial.print("LMIC.adrTxPow = "); + Serial.println(LMIC.adrTxPow, DEC); + + Serial.print("LMIC.datarate = "); + Serial.println(LMIC.datarate, DEC); + + Serial.print("LMIC.freq = "); + Serial.println(LMIC.freq, DEC); + + Serial.print("LMIC.dn2Dr = "); + Serial.println(LMIC.dn2Dr, DEC); + Serial.print("LMIC.dn2Freq = "); + Serial.println(LMIC.dn2Freq, DEC); + LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); + Serial.println(F("Packet queued")); + counter++; } // Next TX is scheduled after TX_COMPLETE event. } void setup() { Serial.begin(115200); + Serial.println(F("")); + Serial.println(F("ttn-apb")); + Serial.println(F("")); Serial.println(F("Starting")); #ifdef VCC_ENABLE
As shown in the previous picture, I assembled the device on a solid piece of wood: this makes bringing it with me more manageable.
Testing
Of course, the first thing I tested is that the test packets were received by the gateway properly. This can be verified by inspecting the system log (/var/log/messages or /var/log/syslog). For example, the last line of the following dump traces a received packet:
Nov 7 16:10:39 loragw logger: SX1276 detected, starting. Nov 7 16:10:39 loragw logger: Gateway ID: b8:27:eb:ff:ff:0d:87:7b Nov 7 16:10:39 loragw logger: Listening at SF7 on 868.100000 Mhz. Nov 7 16:10:39 loragw logger: ------------------ Nov 7 16:10:39 loragw logger: stat update: {"stat":{"time":"2018-11-07 14:17:15 GMT","lati":0.00000,"long":0.00000,"alti":0,"rxnb":0,"rxok":0,"rxfw":0,"ackr":0.0,"dwnb":0,"txnb":0,"pfrm":"S ingle Channel Gateway","mail":"am.dev.75@gmail.com","desc":"LLandre.com LoRa gateway #1"}} Nov 7 16:10:39 loragw logger: stat update: {"stat":{"time":"2018-11-07 15:07:58 GMT","lati":0.00000,"long":0.00000,"alti":0,"rxnb":0,"rxok":0,"rxfw":0,"ackr":0.0,"dwnb":0,"txnb":0,"pfrm":"S ingle Channel Gateway","mail":"am.dev.75@gmail.com","desc":"LLandre.com LoRa gateway #1"}} Nov 7 16:10:39 loragw logger: stat update: {"stat":{"time":"2018-11-07 15:08:28 GMT","lati":0.00000,"long":0.00000,"alti":0,"rxnb":0,"rxok":0,"rxfw":0,"ackr":0.0,"dwnb":0,"txnb":0,"pfrm":"Single Channel Gateway","mail":"am.dev.75@gmail.com","desc":"LLandre.com LoRa gateway #1"}} Nov 7 16:10:39 loragw logger: stat update: {"stat":{"time":"2018-11-07 15:08:58 GMT","lati":0.00000,"long":0.00000,"alti":0,"rxnb":0,"rxok":0,"rxfw":0,"ackr":0.0,"dwnb":0,"txnb":0,"pfrm":"Single Channel Gateway","mail":"am.dev.75@gmail.com","desc":"LLandre.com LoRa gateway #1"}} Nov 7 16:10:39 loragw logger: stat update: {"stat":{"time":"2018-11-07 15:09:28 GMT","lati":0.00000,"long":0.00000,"alti":0,"rxnb":0,"rxok":0,"rxfw":0,"ackr":0.0,"dwnb":0,"txnb":0,"pfrm":"Single Channel Gateway","mail":"am.dev.75@gmail.com","desc":"LLandre.com LoRa gateway #1"}} Nov 7 16:10:39 loragw logger: stat update: {"stat":{"time":"2018-11-07 15:09:58 GMT","lati":0.00000,"long":0.00000,"alti":0,"rxnb":0,"rxok":0,"rxfw":0,"ackr":0.0,"dwnb":0,"txnb":0,"pfrm":"Single Channel Gateway","mail":"am.dev.75@gmail.com","desc":"LLandre.com LoRa gateway #1"}} Nov 7 16:10:39 loragw logger: stat update: {"stat":{"time":"2018-11-07 15:10:28 GMT","lati":0.00000,"long":0.00000,"alti":0,"rxnb":0,"rxok":0,"rxfw":0,"ackr":0.0,"dwnb":0,"txnb":0,"pfrm":"Single Channel Gateway","mail":"am.dev.75@gmail.com","desc":"LLandre.com LoRa gateway #1"}} Nov 7 16:10:39 loragw logger: Packet RSSI: -119, RSSI: -125, SNR: 0, Length: 26 Nov 7 16:10:39 loragw logger: rxpk update: {"rxpk":[{"tmst":2238304632,"chan":0,"rfch":0,"freq":868.100000,"stat":1,"modu":"LORA","datr":"SF7BW125","codr":"4/5","lsnr":0,"rssi":-119,"size":26,"data":"QLQcASaABQABpDH341NfYYH9xkWIMmxDI1A="}]}
The received packets are also visible in the TTN console:
The payload shows the ASCII codes of the character forming the “Hello, world!” string. It is worth to remember that it is recommended to disable the “Frame Counter Checks” option referring to the testing device during development. This option is available on the TTN console.
Antenna optimization
After verifying the proper functioning of the whole system, I did some tests to characterize the receiving capabilities of the gateway. I had two antennas available. The Dragino LoRa GPS hat comes with a white 868MHz glue stick antenna. In addition to this one, I bought on eBay a cheap magnetic antenna that comes with a 2 meters cable:
The following picture shows the metal box where I installed the gateway. The two antennas are also visible.

To compare different configurations, I used two measurements: RSSI and SNR. For each received packet, these are visible on the TTN console. This table recaps the measurements that I did for 6 different configurations.
I achieved the best results with the glue stick antenna connected with 25 cm cable. This is the definitive configuration that I will be using from now on.
Future work
In the next post of this series, I’ll illustrate how I worked on the transmission parameters of the device in order to achieve a wider range.