---
# System prepended metadata

title: Storage Enviroment Guard using LoRaWAN

---

**Date:** 2023-07-9
**Author:** Jakob Porsfelt - jp223tz
**Course:**  Introduction to Applied IoT- 1DT305, Linnaeus University
**Estimated time:** About 3-4 hours


You know all the painkillers, hayfever pills etc. that you have lying around? wouldn't it be great to know if they are actually stored in prober conditions? Anyways, I have them pilled up and have no idea in what conditions they are stored and therefore no idea if they still are good enough for usage and have any effect except placebo. So I built this rather simple project to learn about IoT as I find it facsinating, especially learning and realizing all the new ways real world data can be gathered by deploying IoT.
So I wanted to measure temperature and humidity as well as other factors such as if a cabinet is closed or not(which could affect temperature and humidity etc.)
However, the system can pretty much be applied to any indoor eviroment tracking system. When the set up of the sensors and microcontroller as well as loRa is done there is really no limits as to how it can be programmed and how the data are visualised as well as gathered. For an example you can change the way the limits are set, as well as what is reported, when and how. It is really up to your imagination to decide what suits the project best!
All in all it is a fun project to do and you will learn alot about both electronics such as transistors, breadboards, microcontrollers, and how to set up a transmitter such as LoRaWAN, and how sending payloads work. Again, there are really no limits, so the project can end up in developing an actual app or setting up sql servers and start running statistical predictions and what not.


---
**Material**

Overall the project is centered around using the raspberry pi pico to control sensors and recieve data. It is basicly a mini computer that is able to decide when and how data is gathered from sensors and manipulate it for transmission via either Wifi, LoRaWAN etc. - It is the brain of the system.
The second big player in this system is the LoRa(Long Range) which is responsible for sending the data via LoRaWAN protocol. [The LoRa](https://knowhow.distrelec.com/telecommunications/what-is-lora-lorawan-technology/) can send data over vast distances, which of course is kind of an overkill for our purpose, but it is an increasingly popular method of sending data, and by knowing how to deploy this technology all sorts of possiblities become available, for an example keeping track on remote silos or storage facilities.


**What you will need:**


| Device and Price | Purpose |
| -------- | -------- |
| [Raspberry pi pico](https://https://www.amazon.com/Pico-Raspberry-Pre-Soldered-Dual-core-Processor/dp/B0BK9W4H2Q/ref=sr_1_3?crid=20UKPVSPJT6BI&keywords=raspberry+pi+pico+wh&qid=1688386551&sprefix=Raspberry+pi+pico+wh%2Caps%2C156&sr=8-3) (15 USD) | Everything(Recieves measurements from sensors. Programs them to do what and when, and manipulates the inputs from sensors for transmission)    | 
| [DHT11](https://https://www.amazon.com/Digital-Temperature-Relative-Humidity-Arduino/dp/B07868Z9LX/ref=sr_1_16?crid=JE93ZMYWKEKS&keywords=dht11&qid=1688386670&sprefix=dht11%2Caps%2C160&sr=8-16) (9 USD)     | Measuring temperature and humidity     | 
| [Hall-effect sensor](https://https://www.amazon.com/EPLZON-Effect-Magnetic-Detector-Arduino/dp/B09PG3PGH6/ref=sr_1_1?crid=2F1TH6AYUNA4L&keywords=SIP-3+Hall-effektsensor&qid=1688389536&sprefix=sip-3+hall-effektsensor%2Caps%2C145&sr=8-1)(7 USD)     | Also measuring stuff. For this example it tracks wheter an event is on/off     | 
| [LoRa](https://https://www.elfadistrelec.dk/en/asr6501-868mhz-lorawan-communications-unit-with-antenna-m5stack-u117/p/30221929?ext_cid=shgooaqdkda-PerformanceMax-GoogleCSS&cq_src=google_ads&cq_cmp=18004483913&cq_con=&cq_term=&cq_med=pla&cq_plac=&cq_net=x&cq_pos=&cq_plt=gp&gclid=CjwKCAjw44mlBhAQEiwAqP3eVugmMkFoDKOViO-UrfWAmj0nulNkvwE6Ibsl2KsR4ytrXx3s09iOWxoCMiwQAvD_BwE&gclsrc=aw.ds)(16 USD)    | This is what transmits the payload to the network    | 
|2 [LED's](https://https://www.amazon.com/MCIGICM-Circuit-Assorted-Science-Experiment/dp/B07PG84V17/ref=sr_1_3?crid=3612MQUHY510T&keywords=LED+5mm&qid=1688390643&sprefix=led+5mm%2Caps%2C174&sr=8-3)(5 USD)  | Serves as a visual real world alarm when the limits are crossed    | 
|10 [male to male lab cables](https://www.amazon.com/Solderless-Multicolor-Electronic-Breadboard-Protoboard/dp/B09FPJ9TSP/ref=sr_1_10?crid=1E5MJ69LFV4IP&keywords=Lab+cable+30cm+male+to+male&qid=1688390773&sprefix=lab+cable+30cm+male+to+male%2Caps%2C172&sr=8-10)(10 USD) | Connecting the various devices on the breadboard |
|3 [male to female wires](https://www.amazon.com/Solderless-Multicolor-Electronic-Breadboard-Protoboard/dp/B09FPJ9TSP/ref=sr_1_10?crid=1E5MJ69LFV4IP&keywords=Lab+cable+30cm+male+to+male&qid=1688390773&sprefix=lab+cable+30cm+male+to+male%2Caps%2C172&sr=8-10)(included in above) |  Connecting the LoRa|
|[Breadboard](https://www.amazon.com/Breadboards-Solderless-Breadboard-Distribution-Connecting/dp/B07DL13RZH/ref=sr_1_1_sspa?crid=3COK4YOK53F65&keywords=breadboard&qid=1688391107&sprefix=breadboar%2Caps%2C165&sr=8-1-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9hdGY&psc=1)(10 USD) | Conveniently connecting everything to the pico |
|2 [10kohm resistors](https://www.amazon.com/EDGELEC-Resistor-Tolerance-Multiple-Resistance/dp/B07QJB31M7/ref=sr_1_3?crid=14PQ3F376SAL&keywords=10k+ohm+resistor&qid=1688390901&sprefix=10k+ohm+resist%2Caps%2C161&sr=8-3) (6 usd)| Connecting the LEDs (And making sure we dont burn the pico!) |
In total 68 USD | 

Because electrokit.com had premade packages made for the course, I bought everyhing here  - but any IoT provider would work. The price was about 600 Swedish Crowns or 55 USD at the time I wrote this.
Most equipment is sold in package - that is why the price I listed doesn't match the quantity needed. 



---


**Computer setup**

First you need to update the firmware on the pico.
How to do it is described here: https://docs.pycom.io/updatefirmware/device/

But in short, you need to download the latest firmware and paste it onto the device. 
Remember to hold down the BOOTSEL button when you connect the pico via the USB for the first time.

If the device doesnt appear again, it is ok - It just means the device is no longer considered a new device.

Next step is to install your preferred IDE. I used VScode as it is the IDE I am the most familiar with.
Secondly, follow these steps:
 
1. [![](https://hackmd.io/_uploads/ByVkMezt2.png)](https://code.visualstudio.com/) & [![](https://hackmd.io/_uploads/By5ZMgfY2.png)](https://nodejs.org/en) - Download and install [VScode](https://code.visualstudio.com/) then [NodeJS](https://nodejs.org/en)




1. [![](https://hackmd.io/_uploads/rkWHfezKh.png)](https://code.visualstudio.com/) - In VScode, go to plugins and install pymakr.



1. [![](https://hackmd.io/_uploads/SycZQezF2.png)](https://console.helium.com/)  - We will use helium for transmitting the data in this tutorial.



1. [![](https://hackmd.io/_uploads/HkDD7ezYh.png)](https://datacake.co/) - Datacake will be used to decode the payload and visualize it.





---


**Putting everything together**


![](https://hackmd.io/_uploads/rJLcQgGt2.png)


This is a rough schematic of the connections. The sensors on the picture does not mirror how they look in real life. The sensors in the pictures are just placeholders(or lack of better illustrations) to show how the pins has to be connected!
All the wires used are male to male(or wires where each end has a pin on it)
Also note, that my raspberry pi pico only had a 3v3 output.
![](https://hackmd.io/_uploads/S1juXgzYn.png)

(for reference on what each pin does and its number.)

First make sure the pico is attached to the motherboard. Now connect the 3rd pin on the pico to the ground (-) on the breadboard, and the 5th pin to power(+)
On my breadboard minus was blue line and red was power.

next install the DHT11 sensor. It is very important to check the documentation before adding the component, as the power and ground pin can vary. The leftmost pin goes to pin 26 on the pico, the middle to power and rightmost to ground.

The hall-effect sensor has the output(blue) on the right and I connected it to pin 16. Middle is ground and left is power.


For the LED's, mine had to be placed with long end in power, and power goes directly from the pin on the pico. In this case pin 28 and 15. The reason resistors are used, is because we need to limit the power being consumed by the LED in order to protect the pico.


As you will see in the next part, the idea is that the green LED lights up for a few seconds if everything is good, and the red if something is wrong. 

The hall-effect sensor tracks whether a magnetic field is detector by measuring the change on voltage. It returns boolean value of either detected or not. In out case it means whether a door is open or closed, because it will be placed next to the door and the door will have a magnet attached to it. (In our case it will just work by moving a magnet close to the sensor and see if it works)


---

**Helium Console**
Before we get started with the coding, we need to set up the network for the LoRaWAN to transmit to.
So create a [Helium Account](https://https://console.helium.com/welcome) and add a new device.
Give it a name and save it. Now keep these keys close, we will need it in the next part.


![](https://hackmd.io/_uploads/rk9p7lfFh.png)


**The code**

In the following I will in quick steps explain how the code works in sections.
I will end this section with the full script.

Let's first import some key libraries that are needed for this code. The machine library in MicroPython is used for accessing low-level hardware, such as GPIO pins. struct and binascii are standard Python libraries for handling binary data. time is for accessing system time, and LoRaWAN is for communicating over LoRaWAN networks. Finally, dht is for interfacing with a DHT11 humidity and temperature sensor.

![](https://hackmd.io/_uploads/SJnG4lzY3.png)



Next, initialize the DHT11 sensor, the Hall effect sensor, and two LED indicators. The DHT11 sensor is connected to pin 26, while the Hall sensor is set to input mode and connected to pin 16. The LED indicators are set to output mode and connected to pins 28 and 15.
Also, note the PULL_UP on the hall effect variable. Nowadays the are many magnetic fields everywhere due to all the electronic devices in our houses. That is why we use an internal pull up resistor. More on [pull up resistors here](https://https://www.electronics-tutorials.ws/logic/pull-up-resistor.html).
For our example, this is where the internal pull up resistor in the microcontroller is being switched on.

![](https://hackmd.io/_uploads/BkKNVxMF2.png)



Now we create a lorawan object and paste in the keys from the device we created on helium earlier.

![](https://hackmd.io/_uploads/SyxOEeGYh.png)


Next, the code initiates a join request to the LoRaWAN network and waits for it to succeed.

![](https://hackmd.io/_uploads/B1PYVgfYh.png)


Given that the join succeeded the next part of the code prompts the user to input the thresholds for the temperature and humidity. It will keep asking until the user is happy with their choices.

In the main loop of the program, temperature and humidity are measured, and the Hall sensor value is read. The program also keeps track of average temperatures and humidity, and if these averages are above or below the set thresholds for a certain number of times, an alarm is triggered, causing the corresponding LED to flash.
The average is calculated by using a counter variable that had added 1 for every time the loop is executed, or measurement done.
Then dividing the corresponding accumulated variable for either temperature or Humidity.
![](https://hackmd.io/_uploads/H13n4lMth.png)

The alarm works by evaluating whether the average temperature is above or beneath the thresholds, then adding one if it happens. If this is the case for 4 measurments in a row the alarm is "triggered" or the variable is set to 1, which means we can vizualise this as a boolean value in the visualisation as on/off.

the number 4 is completly arbitary, but the idea is that both humidity and temperature can fluctuate quickly and turn back to be inside the limits for various reasons that might not mean anything. So it is meant to be a kind of way to determine whether it is a persitant problem or not.



If either alarm is "ON" the red light will be turned on for a few seconds instead of the green LED, indicating something is wrong.

![](https://hackmd.io/_uploads/B1NMHefKh.png)

![](https://hackmd.io/_uploads/H15gSxzFn.png)


Lastly, the program creates a payload of the sensor readings and alarm status and sends it over LoRaWAN. It then waits for a response and prints it if one is received.

![](https://hackmd.io/_uploads/S1jHSxzK2.png)


The whole script:


```
import machine
import struct
import binascii
import time
from LoRaWAN import lora
import dht

d = dht.DHT11(machine.Pin(26))
hall_sensor = machine.Pin(16, machine.Pin.IN, machine.Pin.PULL_UP)
led1 = machine.Pin(28, machine.Pin.OUT)
led2 = machine.Pin(15, machine.Pin.OUT)


lora = lora()
DEV_EUI = "KEY HERE"
APP_EUI = "KEY HERE"
APP_KEY = "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!")

# Initialize counter and accumulated_temp
counter = 0
accumulated_temp = 0
accumulated_humid = 0
temp_high_counter = 0
temp_low_counter = 0
humid_high_counter = 0
humid_low_counter = 0
happy = 'n'
while happy.lower() == 'n':
    min_temp = int(input("Please state the minimum temperature: "))
    max_temp = int(input("Please state the maximum temperature: "))
    while max_temp < min_temp:
        print("Maximum temperature cannot be lower than minimum temperature!")
        min_temp = int(input("Please state the minimum temperature: "))
        max_temp = int(input("Please state the maximum temperature: "))

    min_humid = int(input("Please state the minimum humidity: "))
    max_humid = int(input("Please state the maximum humidity: "))
    while max_humid < min_humid:
        print("Maximum humidity cannot be lower than minimum humidity!")
        min_humid = int(input("Please state the minimum humidity: "))
        max_humid = int(input("Please state the maximum humidity: "))

    print(f"Minimum temperature: {min_temp}\nMaximum temperature: {max_temp}\nMinimum humidity: {min_humid}\nMaximum humidity: {max_humid}")
    happy = input("Happy with thresholds? Y/N: ")

while True:
  d.measure()
  temperature = d.temperature()
  humidity = d.humidity()
  hall_value = hall_sensor.value()

  counter += 1
  accumulated_temp += temperature
  accumulated_humid += humidity
  avg_temp = accumulated_temp / counter
  avg_humid = accumulated_humid / counter
  
  temp_high_alarm = 0
  temp_low_alarm = 0
  humid_high_alarm = 0
  humid_low_alarm = 0

  print(f"temp: {temperature}, avg temp: {avg_temp}, humid: {humidity}, avg humid: {avg_humid}, Hall sensor: {hall_value}")

  if avg_temp > max_temp:
    temp_high_counter += 1
    if temp_high_counter >= 4:
      temp_high_alarm = 1
  else:
    temp_high_counter = 0

  if avg_temp < min_temp:
    temp_low_counter += 1
    if temp_low_counter >= 4:
      temp_low_alarm = 2
  else:
    temp_low_counter = 0

  if avg_humid > max_humid:
    humid_high_counter += 1
    if humid_high_counter >= 4:
      humid_high_alarm = 3
  else:
    humid_high_counter = 0

  if avg_humid < min_humid:
    humid_low_counter += 1
    if humid_low_counter >= 4:
      humid_low_alarm = 4
  else:
    humid_low_counter = 0

  if temp_high_alarm or temp_low_alarm or humid_high_alarm or humid_low_alarm:
    led1.value(1)
    time.sleep(5)
    led1.value(0)
  else:
    led2.value(1)
    time.sleep(5)
    led2.value(0)

  if hall_value == 1:
    print("No magnetic field detected")
  elif hall_value == 0:
    print("Magnetic field detected!")
  else:
    print("Unexpected value from Hall sensor")
  
  temp_int = int(temperature * 10)
  humidity_int = int(humidity * 10)
  avg_temp_int = int(avg_temp * 10)
  avg_humid_int = int(avg_humid * 10)
  
  payload = struct.pack(">hHHHHhhhh", temp_int, humidity_int, avg_temp_int, avg_humid_int, hall_value, temp_high_alarm, temp_low_alarm, humid_high_alarm, humid_low_alarm)
  payload = binascii.hexlify(payload).decode("utf-8")
  lora.sendMsg(payload)
  print("Sent message:", payload)

  response = lora.receiveMsg()
  if (response != ""):
    print("Received: ", end=": ")
    print(response) 

  time.sleep(30)
```




---

In order to put the code on the pico simply go to vs code, under pymakr in the left menu, create a project, choose empty template and add the device. Now go back to the explorer in the left menu in the top and paste the code into the main.py file.
Also download the LoRaWAN.py file [here](https://github.com/iot-lnu/applied-iot/tree/master/Raspberry%20Pi%20Pico%20(W)%20Micropython/network-examples/N4_LoRaWAN_Connection) and put it together with the main.py.


![](https://hackmd.io/_uploads/ryxFi8DFh.png)


**Transmitting the data / connectivity**

The data is transmitted via the helium network. This choice was purely due to network coverage vs. other platforms.
While the script tries to take the size of the payload, you will probably have to pay a tiny amount to be able to set the whole thing up and making it run. [The prices can be checked out here.](https://https://docs.helium.com/data-credit/)

I paid 1 USD for 100 000 points, and I havent crossed 90 000 yet.

A measurement is executed and payload sent to the network every 30 seconds as indicated by the script.

![](https://hackmd.io/_uploads/HJlvHeMt3.png)



The payload is transformed into a [hexidecimal](https://https://learn.sparkfun.com/tutorials/hexadecimal/all#:~:text=Hexadecimal%20is%20a%20base%2D16,value%20you're%20used%20to.) number  which is handy, because it limits the amount of data having to be transmitted to the helium network. It is sent in a utf-8 format which is great for general purpose decoding, making it esier to decode later.

Next up, we have to set up a connection from helium to datacake.
First we need to add a device to datacake.
Choose Lorawan and next.

![](https://hackmd.io/_uploads/rypsBeMtn.png)



then 'New Product' and give it a name.
![](https://hackmd.io/_uploads/SkC6rgMF3.png)


Choose 'Helium' and provide the DEV_UI key details from when we set up the device on helium.
![](https://hackmd.io/_uploads/SypjLgMKn.png)








![](https://hackmd.io/_uploads/B19zQYxK3.png)


Select the free plan for now.


![](https://hackmd.io/_uploads/H1vyDlzKh.png)



Now set up the integration with Datacake on helium by [following this guide.](https://docs.helium.com/use-the-network/console/integrations/datacake/)

When that is done move on to configuring the device on Datacake.

Fill in the boxes, and when you get to the decoder, paste in the following script:
![](https://hackmd.io/_uploads/r1u8wezYh.png)






```
function Decoder(bytes, port) {
    var offset = 0;
    var decoded = {};

    // Here we unpack the temperature
    decoded.temperature = ((bytes[offset++] << 8) | bytes[offset++]) / 10;

    // Humidity
    decoded.humidity = ((bytes[offset++] << 8) | bytes[offset++]) / 10;

    // Average temperature
    decoded.avg_temp = ((bytes[offset++] << 8) | bytes[offset++]) / 10;

    // Average humidity
    decoded.avg_humid = ((bytes[offset++] << 8) | bytes[offset++]) / 10;

    // Hall sensor value or open/closed alarm
    decoded.hall_value = ((bytes[offset++] << 8) | bytes[offset++]);

    // Alarm status for each limit set up by the user
    decoded.temp_high_alarm = ((bytes[offset++] << 8) | bytes[offset++]);
    decoded.temp_low_alarm = ((bytes[offset++] << 8) | bytes[offset++]);
    decoded.humid_high_alarm = ((bytes[offset++] << 8) | bytes[offset++]);
    decoded.humid_low_alarm = ((bytes[offset++] << 8) | bytes[offset++]);

    return [
        {
            field: "TEMPERATURE",
            value: decoded.temperature
        },
        {
            field: "HUMIDITY",
            value: decoded.humidity
        },
        {
            field: "AVERAGE_TEMPERATURE",
            value: decoded.avg_temp
        },
        {
            field: "AVERAGE_HUMIDITY",
            value: decoded.avg_humid
        },
        {
            field: "HALL_VALUE",
            value: decoded.hall_value
        },
        {
            field: "TEMP_HIGH_ALARM",
            value: decoded.temp_high_alarm
        },
        {
            field: "TEMP_LOW_ALARM",
            value: decoded.temp_low_alarm
        },
        {
            field: "HUMID_HIGH_ALARM",
            value: decoded.humid_high_alarm
        },
        {
            field: "HUMID_LOW_ALARM",
            value: decoded.humid_low_alarm
        }
    ];
}

```
![](https://hackmd.io/_uploads/SJ3pvxMFh.png)



What this script does is basicly translating the payload we send from the loRaWAN to the helium network. If you want a more thorough understanding of what each part does, read the commented lines beginning with // 

Here the code comes in as a long string of numbers. This script is breaking those numbers down into chunks, and each chunk represents a specific piece of information: temperature, humidity, average temperature, average humidity, the Hall sensor value, and the alarm status for high and low temperature and humidity.

The way it decodes the numbers is by shifting and combining them (using '<< 8' and '|'), and then it divides by 10 for the measurements to get them in the correct units.

Finally, it neatly packages all these translated values into a new format (a list of objects) that makes it easier to understand and use the data.

Next up, we need to add the fields for the dashboard.
![](https://hackmd.io/_uploads/H14eOxfY2.png)


It will end up looking something like this.
![](https://hackmd.io/_uploads/B1mbYlzth.png)




The type is datatype of the data being sent. For the temperature and humidity a float will do sufficiently.
The name should correspond to the name the value is given in the decoder, so TEMPERATURE for temperate, and AVERAGE_TEMPERATURE for the average temperate. But you can also give them a different name, just remember to keep the identifier the same as in the script!

For the alarm types we choose boolean, as we want to indicate that it is either on or off, or yes/no if you prefer.

**Presenting the data**
Now we can move on to the dashboard where there are lots of option. In this tutorial we use Datacake simply because it is so convenient when it comes to setting up an MQTT connection with helium, as well as the ease of decoding the message back into measurements.
In this dashboard I chose to visualize the average temperature and humidity the last 24 hours, as well as if the alarm limits were crossed or not - This is again just arbitary and could be higher or lower, but in this example I had monitoring the storage conditions for medicine at home in mind so it made sense to display it hourly.


but you can really tailor it to your needs and what you want to see, as well as what the numbers should represent!

![](https://hackmd.io/_uploads/BkDnuxzK3.png)



So there you go! The data is saved every time you sent the data from helium via the HTTP integration you made earlier. Meaning a new payload is sent every 30 seconds. This can be made more or less frequent by changing the integer in time.sleep() in the very end of the micropython script we went through earlier.

The next step could be to actually making an app interface as well as storing the data on a sql server somewhere.

![](https://hackmd.io/_uploads/HyQKNRzF2.png)


