# Part 2: LoRaWAN on The Things Stack (TTS) ###### tags: `TA Stuff RP2` `Raspberry Pi Pico` <!--- ## Table of Contents [TOC]---> This tutorial will focus on connecting your device to **LoRaWAN** (**Lo**ng **Ra**nge **W**ide **A**rea **N**etwork). You can watch the following video from The Things Academy about LoRaWAN; the complete playlist is here. [**(here)**](https://youtube.com/playlist?list=PLM8eOeiKY7JWeGAJfpsJmVXqlanzJfJ_X). ### The Things Academy YouTube {%youtube TonUXQeMctY %} As always, we'll continuously update this walkthrough. **Is there anything missing or unclear, or have you experienced some issues? Please add a comment.** You do this by highlighting the text and then you can write a comment on the highlighted part. You need to log in/create an account on hackMD first. ## LoRaWAN Coverage Map Before being able to use TTS (The Things Stack) and sending LoRa packages you should check if you have coverage in your area. You can check the coverage map [**here**](https://www.thethingsnetwork.org/map) and if you are close to a gateway around then you can send a message to TTS. :::warning Some gateways are indoor gateways and there may be lots of obstacles between your device and that gateway so it is possible you see a gateway close to your place but the signal is weak and you can not send messages. ::: ## Required Hardware (Raspberry Pi Pico W and M5Stack LoRaWAN Unit) We use Raspberry Pi Pico W and M5Stack LoRaWAN Unit to connect to TTS. You can see an image of M5Stack [here](#Connect-the-antenna) before purchasing M5Stack check your area frequency band [**here**](https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country/) and based on that you can find the [**915MHz (here)**](https://shop.m5stack.com/collections/all-products/products/lorawan-unit-915mhz-asr6501-with-antenna), [**868MHz (here)**](https://shop.m5stack.com/collections/all-products/products/lorawan-unit-868mhz-asr6501-with-antenna), or [**470MHz (here)**](https://shop.m5stack.com/collections/all-products/products/lorawan-unit-470mhz-asr6501-with-antenna) version of M5Stack. If you live in **North America or Australia** you should use the **915 MHz** band and connect the antenna accordingly. If you live in **Europe** you can choose either the **433 MHz** band or the **868 MHz** band. Although both are license-free, we recommend going with the **868 MHz** since it has less interference. **❗ Remember, you can always ask a TA if you are not sure**. ## Getting started ### Connect the antenna :::danger **⚠️ Important ⚠️ Make sure the LoRa antenna is connected properly before running any LoRa code on your device. Not doing so might break your device.** ::: ![](https://hackmd.io/_uploads/BkppJ7zw3.jpg =500x) </br> ### Connecting M5Stack to Raspberry Pi Pico W <!--![](https://hackmd.io/_uploads/SkosYEfD3.jpg)--> </br> ![M5_Stack_bb](https://hackmd.io/_uploads/ryNpow1rA.png) ### LoRa --- #### LoRa OTAA LoRa has 2 main methods of authentication. One is **ABP** and the other is **OTAA**. We will mostly be going through the **OTAA** method of connecting to LoRaWAN since it is the easier and the simpler of the two. There are three different types of keys/identifiers that you need to provide in OTAA method: **app_eui**, **app_key**, and **dev_eui** The **APP_EUI** is what identifies the application you will be creating on whichever platform you choose in the next step. The **APP_KEY** is the encryption key that will be used to encrypt all the data you will be sending to the gateway. This is a private key that should be kept secure. Otherwise, others will be able to send data to your application. Lastly, the **DEV_EUI** is a unique key that identifies your end device. Usually, the MAC of the **Raspberry Pi Pico W** is used if nothing is provided by the network you are using. :::info The following text shows how and where to get/generate these three values. ::: ## The Things Stack (TTS) :::danger **⚠️ Important ⚠️ Make sure the LoRa antenna is connected properly before running any LoRa code on your device. Not doing so might break your device.** You can find how to properly connect the antenna [here](#Connect-the-antenna). ::: ### 1. Register --- Before you can start using TTS, you need to [create a TTS account](https://www.thethingsnetwork.org/get-started). The Things Stack has two versions the community version (TTN) which is free and the industrial version (TTI) which has a paid plan. Follow the numbered clicks below to register for the community version. ![01](https://hackmd.io/_uploads/Sk8EaPkr0.jpg) Once you have done that, go ahead and navigate to Console. Then select Europe 1 (eu1) and choose the `Login with The Things ID` option to log in. <!--![](https://i.imgur.com/eb22wU8.png) ![](https://i.imgur.com/3apo8Cv.png)--> ![03](https://hackmd.io/_uploads/SyBARvkSA.jpg) ![02](https://hackmd.io/_uploads/B1yyJ_krR.jpg) ### 2. Create an application Once you've logged in, you'll be redirected to your homepage. There, you navigate to `Go to applications >> Create application`. Give the application an appropriate ID, name, and description. ![](https://i.imgur.com/gkJoy8n.png) When the application is created, you'll be redirected to the application page. ![](https://i.imgur.com/wvWgZf0.png) ### 3. Add the device to the application --- Once there, navigate to `+ Register end device` in the bottom-right corner to add your device to the application. ![04](https://hackmd.io/_uploads/HJrKKqJrC.png) Select `Enter end device specifics manually` and choose the `Frequency plan`, `LoRaWAN Version`, and `Regional Parameters Version`. Finally, enter the `JoinEUI` a value like `0000000000001234` and click `Confirm`. <kbd> <img src="https://hackmd.io/_uploads/B1dH44fwh.png"> </kbd> </br></br> Then move on to generate `DevEUI` and `AppKey`. These keys will be used by the LoRaWAN unit to join the LoRaWAN network (The Things Stack in this case). <kbd> <img src="https://hackmd.io/_uploads/B1gLH4zPn.png"> </kbd> </br></br> The `End device ID` should be auto-generated, but feel free to change it if you want to customize it. Click on `Register end device` when you're done. Once done, you should be redirected to your device page. The landing page should look like this: ![](https://hackmd.io/_uploads/HkqrKVzDh.png) </br> ### 4. Connect to TTS using your device --- Now, you connect to TTS using the `AppKey` you generated earlier. :::danger **⚠️ Important ⚠️ Make sure the LoRa antenna is connected properly before running any LoRa code on your device. Not doing so might break your device.** You can find how to properly connect the antenna [here](#Connect-the-antenna). ::: First, download the LoRaWAN library file from our [github repository](https://github.com/iot-lnu/pico-w/tree/main/network-examples/N4_LoRaWAN_Connection) and add it to the root folder of the project. This is how your project structure should look like: ![](https://hackmd.io/_uploads/r1Ig-hXP3.png) Then, add the snippet below at the beginning of your `main.py` that you have in your project folder in VSCode or Thonny. ```python= import struct import time from LoRaWAN import lora lora = lora() APP_EUI = "0000000000000000" DEV_EUI = "0000000000000000" APP_KEY = "00000000000000000000000000000000" lora.configure(DEV_EUI, APP_EUI, APP_KEY) lora.startJoin() print("Start Join.....") while not lora.checkJoinStatus(): print("Joining....") time.sleep(1) print("Join success!") # Your old code from main.py should be here ``` :::info Remove duplicate imports if you have any after adding the snippet above. ::: Now, replace the zeros in row 7 - 9 with your generated values. In our example case: - The `JoinEUI (App_EUI)` was set to `0000000000001234` - The generated `DevEUI` was `70B3D57ED005E758` - The generated `AppKey` was `31CC62B0256EA3311EEFDCB9ED2CEC3B` Note that only row 7 - 9 are changed. ```python= import struct import time from LoRaWAN import lora lora = lora() APP_EUI = "0000000000001234" # Substitute Your APP_EUI here DEV_EUI = "70B3D57ED005E758" # Substitute Your DEV_EUI here APP_KEY = "31CC62B0256EA3311EEFDCB9ED2CEC3B" # Substitute Your APP_KEY here lora.configure(DEV_EUI, APP_EUI, APP_KEY) lora.startJoin() print("Start Join.....") while not lora.checkJoinStatus(): print("Joining....") time.sleep(1) print("Join success!") # Your old code from main.py should be here ``` Try to run `main.py` and now in the console, you should see something like: ![](https://hackmd.io/_uploads/rJtKgnmD3.png) If you see the `Join success!` message, then congratulations! You have now successfully connected to TTS and are ready to start sending messages! :::warning If your device tries to join for more than a few minutes, then it is an indication of poor coverage in your area. Perhaps you should consider other networks, like [**Helium**](https://hackmd.io/@lnu-iot/H1z3FYQP2). ::: ### 5. Send messages with TTS --- Before we start sending data, we need to encode our data using the `struct` library and `hex()` function. In the following code snippet, we will be encoding and sending data using LoRaWAN and MicroPython. To encode the data, we'll be using the `struct` library and `hex()` function. ```python=23 # Example temperature (in Celsius) and humidity (%) values with a negative temperature temperature, humidity = -14.2, 42.5 # Convert the float values to integers by multiplying them by a factor (example: 10) temp_int = int(temperature * 10) humidity_int = int(humidity * 10) # https://docs.micropython.org/en/latest/library/struct.html # >: Indicates big-endian byte order. Big-endian means the most significant byte is stored first. # h: Represents a short integer (2 bytes). # H: Represents an unsigned short integer (2 bytes). payload = struct.pack(">hH", temp_int, humidity_int) payload = payload.hex() while True: lora.sendMsg(payload) print("Sent message:", payload) response = lora.receiveMsg() if (response != ""): print("Received: ", end=": ") print(response) time.sleep(30) ``` In the code snippet above: 1. We first pack two integers (-14.2 and 42.5) with `struct.pack()`. The `">hh"` format string indicates that we are packing two big-endian 2-byte short integers. 2. The packed binary payload is then converted to a hexadecimal string using `hex()`. 3. In the main loop, we send the encoded payload using `lora.sendMsg()`. 4. We also attempt to receive a response from the LoRa device using `lora.receiveMsg()`. 5. If a response is received, it is printed to the console. 6. The loop then sleeps for 30 seconds before sending the message again. By running the code snippet above (in VSCode or Thonny), you should get an output similar to the one below. ![](https://hackmd.io/_uploads/BkH_W3EP2.png) </br> And under `Live data` in the end device page in your TTS console, you should see something like: <kbd> <img src="https://hackmd.io/_uploads/r1lRi34w3.png"> </kbd> </br></br> :::warning On The Things Network's public community network, a Fair Use Policy applies which limits the uplink airtime to 30 seconds per day (24 hours) per node and the downlink messages to 10 messages per day (24 hours) per node. More about that [here](https://www.thethingsnetwork.org/forum/t/fair-use-policy-explained/1300). ::: ### 6. Encoding and Decoding Messages :::info The following part is not mandatory to follow but is recommended. The encoding and decoding will be necessary in Part 3, when working with data visualization. ::: In Example 1, we handle negative temperatures by using the ">h" format specifier in the `struct.pack()` function. The ">" means "big-endian format," and the "h" specifies a "signed short integer" which occupies 2 bytes or 16 bits. With this format, we can handle temperatures in the range of -3276.8°C to 3276.7°C (after dividing by the factor of 10). Here's how the code works for a negative temperature: ```python= import struct # Example temperature (in Celsius) and humidity (%) values with a negative temperature temperature, humidity = -14.2, 42.5 # Convert the float values to integers by multiplying them by a factor (example: 10) temp_int = int(temperature * 10) humidity_int = int(humidity * 10) payload = struct.pack(">hH", temp_int, humidity_int) payload = payload.hex() ``` Now, if you have a negative temperature, like -14.2°C, it is converted to -142 as a signed short integer. The struct.pack() method takes care of the negative value correctly, and it is encoded in the payload as a signed integer. When you decode the payload on the receiving end, you'll get the exact temperature value (-14.2°C) by dividing it by the same factor (10) used during encoding. In TTS (and most of the IoT platforms), it is necessary to decode the previously encoded payload from the end device. To add a decoder that expects a temperature value (2 bytes) and a humidity value (2 bytes), copy the following Javascript code and paste it under `Payload formatters` in the end-device page in TTS. ```javascript= function Decoder(bytes) { // Ensure the payload is the correct length (4 bytes for >hH) if (bytes.length !== 4) { return { error: "Invalid payload length" }; } // Parse the payload var temperature = (((bytes[0] << 8) | bytes[1]) << 16 >> 16) / 10; // 2-byte signed integer (h) var humidity = ((bytes[2] << 8) | bytes[3]) / 10; // 2-byte unsigned integer (H) return { Temperature: temperature, Humidity: humidity }; } ``` Here is where the code snippet above should go: <!--![](https://hackmd.io/_uploads/H1qaHq4Ph.png)--> ![04](https://hackmd.io/_uploads/SJryNjJrC.jpg) Now if you go back to `Live data` in the end-device page in TTS, you should see the decoded temperature and humidity, and not only their hexadecimal representation. ![](https://hackmd.io/_uploads/rkvuLq4Dh.png) :::success You can get the complete code for both `LoRaWAN.py` library and the `main.py` code from our [**GitHub repo here**](https://github.com/iot-lnu/pico-w/tree/main/network-examples/N4_LoRaWAN_Connection). ::: :::info If you made it this far, good job! Now grab some coffee before selecting the next box from the roadmap. :::