A LoRaWAN “The Things Network” Gateway for Windows IoT Core

Build your own LoRaWAN “The Things Network” packet-forwarding Gateway on Windows 10 IoT Core in native .NET code.

This tutorial describes how to build, install and run a packet-forwarding LoRaWAN gateway running on a Raspberry Pi with a Dragino LoRa extension board, forwarding received radio packets to The Things Network backend. The gateway is implemented in C# (having no external dependencies) and runs on the Windows IoT Core platform.

Get the full source code from GitHub


Please note that the radio expansion boards used in this tutorial are single-channel only, which means that you will not be able to create a full-fledged gateway. It does not fulfill the complete standard and are not LoRaWAN compliant.

This gateway will be suitable for testing purposes only.


The LoRa Alliance describes LoRaWAN like this:

LoRaWAN™ is a Low Power Wide Area Network (LPWAN) specification intended for wireless battery operated Things in a regional, national or global network. LoRaWAN targets key requirements of Internet of Things such as secure bi-directional communication, mobility and localization services. The LoRaWAN specification provides seamless interoperability among smart Things without the need of complex local installations and gives back the freedom to the user, developer, businesses enabling the roll out of Internet of Things.

On top of that, The Things Network describes themselves like this:

We are a global community of 15235 [2017-05-07] people over 84 countries building a global Internet of Things data network. We use a long range and low power radio frequency protocol called LoRaWAN and for short range Bluetooth 4.2. The technology allows for things to talk to the internet without 3G or WiFi. So no WiFi codes and no mobile subscriptions.

So what is a Packet-Forwarding LoRaWAN Gateway?

In short; The Things Network is built up by people placing Gateways virtually all over the world that picks up LoRaWAN radio packets and forwards them to the backend of The Things Network. From there developers can access and process the data sent from their nodes in applications (living in the cloud).

A full-fledged gateway can also transmit messages from the backend (i.e. from the cloud applications) over the air back to the nodes. This functionality is missing in a packet-forwarding gateway.

But it’s a good start to play with the technique, anyway. And cheap.


The C# code is developed and tested on Raspberry Pi 3 (but version 2 would work just fine) with one of the following LoRa expansion boards:

The easiest expansion board to use is the Dragino LoRa/GPS HAT together with a Raspberry Pi, since it’s just to plug it on top (see the top photo).

If you decide to use one of the Dragino Arduino Shields you will need to connect the wires manually. A few of the wires can be attached to any of the general purpose IO pins of the Raspberry Pi, but others must be placed as in my descriptions. The schematics at the end of this article shows how the LoRa/GPS HAT would be attached if it would be attached using wires (except the serial port, which is not attached in my schematics). If you use my suggestion you can use my code as-is.

A Dragino LoRa Shield connected to a Raspberry Pi

Here’s the schematics (download a Fritzing file):

Connecting a Dragino LoRa Shield to a Raspberry Pi


First you need to install Windows IoT Core on your Raspberry Pi. This is done by following these instructions from Microsoft.

Next you will need to get my code, either clone or download it from my GitHub repository.

You will also need Visual Studio, preferably Visual Studio 2017. There is a free but very competent community edition which you may use.

Now you open up the Dragino.Lora.Demo.Gateway.sln  solution file from my GitHub repository in Visual Studio.

In the top bar of Visual Studio, choose Arm as the solution platform and Remote Machine as target.

Open up the MainPage.xaml.cs source code file and locate the four YOUR EDITING IS REQUIRED HERE!-comments.

Here’s what you need to consider in those places:

#1. Configuring the radio chip

The first comment points out where you configure the LoRa radio chip by specifying the LoraWanGatewaySettings-object instance.

The default is Europe868, but depending on where you live (and your radio chip hardware) you might need to change this.

Either use one of the predefined ones, or create a custom one. Take a look at how the predefined ones are created, if you need to create a custom!

#2. Configuring the connection to the LoRa expansion board

The second edit-comment in the MainPage code file is found in the GetTransceiverPinSettings()-method. It’s here you tell how your transceiver expansion board is wired to your Raspberry Pi. If you are using the LoRa/GPS HAT — or using an Arduino Shield with the wiring according to my schematics below — you can leave the code as is.

But if you used a custom wiring you will need to uncomment the last lines and specify the pin numbers yourself.

#3. Using a GPS

Locate the UseGpsManager()-method. Depending on which expansion board you have, a GPS module might be present. The default code assumes that you have one, and returns true. If you don’t have a GPS module, change this to false.

#4. Specifying the Gateway EUI

The last part of the code you will need to configure is the EUI of your gateway and is found in the GetGatewayEui()-method. The EUI is a globally unique ID for your gateway. Unfortunately there is no unique ID provided by the LoRa module itself, so, for instance the MAC address of the Raspberry Pi could be used instead.

Using the MAC address

If you want to use the MAC address it’s slightly more complicated, because there isn’t (yet) an API giving you this information in Windows IoT Core. There is a however workaround, and that is to call the embedded REST API (the same that you can visit in a web browser to configure your device).

The URI that gives the MAC address information is found at http://localhost:8080/api/networking/ipconfig.

But you must provide the user name and password to get access to this API; i.e. the credentials you used during the installation of Windows IoT Core on the device. The default in the code is Administrator and p@ssw0rd.

Using a hard coded EUI

You can also specify any EUI you want by removing the two top lines of this method and uncomment the last one which simply returnes a fixed value at new GatewayEui("0123456789ABCDEF"). Obviously you should change the 0123...EF to your own unique hexadecimal value.

The Things Network

Now it’s time to register your gateway on The Things Network. For this you need to create an account there.

The next step is to go to the console and click the register gateway link. Now register your gateway using the following setup:

As Protocol, use packet forwarder since this gateway implementation is not a full-blown gateway connector.

As Gateway EUI you enter the EUI used by your application (see above). If you decided to use the MAC address you might need to start the application once to see the EUI written in the debug console of Visual Studio (in the output window). This is written by the line that says WriteLog("The gateway EUI: " + gatewayEui);.

Place a breakpoint there, start the code and see what it prints:

Stepping past the WriteLog line will reveal the EUI if it’s unknown to you

Then you should give your gateway a name in the Description field, and choose the correct Frequency Plan depending on where you live and which radio module you are using.

Registering your gateway

If you like you can also click on the map to tell where your gateway is, and below you can specify your Antenna Placement.

At the bottom of the page, click the Register Gateway button when you are finished.

Your newly registered gateway

Click the Settings button in the top right corner and set the Router address:Setting the Router address in the gateway settings

Click the Update Gateway button and then the Overview button to go back to the main screen.

Your gateway is now configured and The Things Network now expects it to deliver the radio packets it receives.

Run the Gateway application

Now you should be all set to run the gateway application. Just press F5 in Visual Studio (given that you have specified the target remote machine – i.e. your Raspberry Pi – in the Debug tab of the project properties).

If you now look at the Overview tab in the gateway console on The Things Network site, you can see if your gateway successfully can communicate with the network. The Last Seen should update around every 30 seconds:

Your gateway is up and running

The MainPage in your application has a timer that ticks every 30 seconds. When it ticks is calls the SendStatus()-method. This makes the gateway code send a short JSON status message to the backend, basically telling it that it is still alive and kicking.

The field below on the overview page is Received Messages (3 in my screenshot). This value goes up when the radio chip successfully receives a full packet (from anyone sending anything on your channel) and the gateway implementation succeeds in forwarding it to the backend. Depending on where you live your gateway will pick up packets more or less frequently.

The Transmitted Messages will stay 0 since this gateway is just a packet forwarder — it never transmit any radio messages from The Things Network.

The debug output in Visual Studio

When running the application, open up the Output window in Visual Studio to see some details of what’s happening:

GPS information

Having a GPS chip on your LoRa expansion board will show all serial communication data received from that module. Those lines starts with GPS: in the log window.

Please note that even though your GPS coordinates are sent to The Things Network in the JSON status message, your gateway configuration will not be updated by The Things Network backend. I don’t know why.

Note: Since you are probably now sitting indoors, trying out this project, you may need to place your Raspberry Pi (or at least the LoRa expansion board) in a window frame for it to successfully receive GPS signals.

A blue led will start flashing on the LoRa expansion board when the GPS receives signals from the satellites.

Sending status information

Every 30 seconds, when the main page calls the SendStatus method, you will see a line saying something like:

Packages: 10 / 4 / 4

The first number tells how many radio packets the LoRa chip has received. The second number how many of those that were OK (correct CRC and no timeout). The last number tells how many packets the gateway has forwarded to backend of The Things Network. In my example (10/4/4) only 4 packets were perfectly fine and 6 packets was not received correctly and was discarded.

The line after shows the JSON message that will be sent, and it will look something like this:

Sending JSON: {"stat":{"time":"2017-05-07 16:21:39 GMT","lati":55.597447,"long":12.958467,"alti":-7,"rxnb":0,"rxok":0,"rxfw":0,"ackr":100,"dwnb":0,"txnb":0}}

After that you will see the same JSON message as it is actually transferred to the backend, as a series of bytes transferred over the UDP protocol:

UDP sending (to "router.eu.thethings.network"): 01, F6, 29, 00, 19, C6, 83, A9, 64, 9B, 61, 29, 7B, 22, 73, 74, 61, 74, 22, 3A, 7B, 22, 74, 69, 6D, 65, 22, 3A, 22, 32, 30, 31, 37, 2D, 30, 35, 2D, 30, 37, 20, 31, 36, 3A, 32, 31, 3A, 33, 39, 20, 47, 4D, 54, 22, 2C, 22, 6C, 61, 74, 69, 22, 3A, 35, 35, 2E, 35, 39, 37, 34, 34, 37, 2C, 22, 6C, 6F, 6E, 67, 22, 3A, 31, 32, 2E, 39, 35, 38, 34, 36, 37, 2C, 22, 61, 6C, 74, 69, 22, 3A, 2D, 37, 2C, 22, 72, 78, 6E, 62, 22, 3A, 30, 2C, 22, 72, 78, 6F, 6B, 22, 3A, 30, 2C, 22, 72, 78, 66, 77, 22, 3A, 30, 2C, 22, 61, 63, 6B, 72, 22, 3A, 31, 30, 30, 2C, 22, 64, 77, 6E, 62, 22, 3A, 30, 2C, 22, 74, 78, 6E, 62, 22, 3A, 30, 7D, 7D (155 byte(s)).

Lastly, if everything worked as it should, you will see a short acknowledge from the backend, basically saying that it has received your message:

UDP receiving (from ""): 01, F6, 29, 01 (4 byte(s)).

Receiving a packet successfully

When a radio packet is received successfully you will see something like this in the debug log:

[1] Packet RSSI: -104, RSSI: -102, SNR: 56.2, Length: 26

[2] Message Received: CRC OK, Rssi=-102, PacketRssi=-104, PacketSnr=56.2, Buffer:[40, 3f, 16, 01, 26, 80, 12, 00, 01, a3, ba, da, b8, b0, 59, 14, 67, 5b, 11, 55, 40, 06, fa, e7, ad, 4a], 2017-05-07 17:00:27

[3] Received: CRC OK, Rssi=-102, PacketRssi=-104, PacketSnr=56.2, Buffer:[40, 3f, 16, 01, 26, 80, 12, 00, 01, a3, ba, da, b8, b0, 59, 14, 67, 5b, 11, 55, 40, 06, fa, e7, ad, 4a], 2017-05-07 17:00:27

[4] Sending JSON: {"rxpk":[{"time":"2017-05-07T17:00:27.1374110Z","tmst":2387966326,"freq":868.1,"chan":0,"rfch":0,"stat":1,"modu":"LORA","datr":"SF7BW125","codr":"4/5","rssi":-104,"lsnr":56.200000762939453,"size":26,"data":"QD8WASaAEgABo7rauLBZFGdbEVVABvrnrUo="}]}

[5] UDP sending (to "router.eu.thethings.network"): 01, 19, FD, 00, 19, C6, 83, A9, 64, 9B, 61, 29, 7B, 22, 72, 78, 70, 6B, 22, 3A, 5B, 7B, 22, 74, 69, 6D, 65, 22, 3A, 22, 32, 30, 31, 37, 2D, 30, 35, 2D, 30, 37, 54, 31, 37, 3A, 30, 30, 3A, 32, 37, 2E, 31, 33, 37, 34, 31, 31, 30, 5A, 22, 2C, 22, 74, 6D, 73, 74, 22, 3A, 32, 33, 38, 37, 39, 36, 36, 33, 32, 36, 2C, 22, 66, 72, 65, 71, 22, 3A, 38, 36, 38, 2E, 31, 2C, 22, 63, 68, 61, 6E, 22, 3A, 30, 2C, 22, 72, 66, 63, 68, 22, 3A, 30, 2C, 22, 73, 74, 61, 74, 22, 3A, 31, 2C, 22, 6D, 6F, 64, 75, 22, 3A, 22, 4C, 4F, 52, 41, 22, 2C, 22, 64, 61, 74, 72, 22, 3A, 22, 53, 46, 37, 42, 57, 31, 32, 35, 22, 2C, 22, 63, 6F, 64, 72, 22, 3A, 22, 34, 2F, 35, 22, 2C, 22, 72, 73, 73, 69, 22, 3A, 2D, 31, 30, 34, 2C, 22, 6C, 73, 6E, 72, 22, 3A, 35, 36, 2E, 32, 30, 30, 30, 30, 30, 37, 36, 32, 39, 33, 39, 34, 35, 33, 2C, 22, 73, 69, 7A, 65, 22, 3A, 32, 36, 2C, 22, 64, 61, 74, 61, 22, 3A, 22, 51, 44, 38, 57, 41, 53, 61, 41, 45, 67, 41, 42, 6F, 37, 72, 61, 75, 4C, 42, 5A, 46, 47, 64, 62, 45, 56, 56, 41, 42, 76, 72, 6E, 72, 55, 6F, 3D, 22, 7D, 5D, 7D (259 byte(s)).

[6] UDP receiving (from ""): 01, 19, FD, 01 (4 byte(s)).

Line [1] tells some overall information about the packet. RSSI means Received Signal Strength Indicator, SNR is the Signal-to-Noise Ratio. Length is the number of bytes received.

Line [2] and [3] tells the same thing as the first line, but in more detail; including the actual bytes received.

Line [4] shows the composed JSON message that will be sent to The Things Network to inform about the received packet.

Line [5] is the JSON as it actually will be transferred, and line [6] is the acknowledge from the backend of The Things Network.

Receiving a bad radio packet

Sometimes a packet is not received correctly. The reason could be bad reception (a distant transmitter) or collisions with other messages.

You will see something like this in the debug output:

Message Received: Bad CRC, Rssi=-94, PacketRssi=-102, PacketSnr=59.2, Buffer:[40, 3f, 1a, 01, 26, 80, 04, 00, 01, fb, 75, 0b, 03, a7, 30, 17, a8, 05, 6e, b0, 29, 89, 6e, 81, b8, 7d], 2017-05-07 16:45:01

You will still get access to the packet data in the MainPage (if you would like to do some analysis or further processing), but it will not be forwarded to The Things Network.


I have had great help creating this project from the following resources:

…and countless of other online resources from here & there…