---
# System prepended metadata

title: IoT Workshop

---

# IoT Workshop
![image alt](https://lnu.se/api/media/27188-w849h425cy12cw849ch413 "title")
## Agenda

- 13:00 - 13:20 – Short presentation
- 13:20 – 14:30 – Part 1, Connect sensor & Send data
- 14:30 – 14:40 – Break
- 14:40 – 16:00 – Part 2, Send & visualise data




## Hardware & Software
:::info
Everyone has the following?
:::

| Name	| Type  	| Quantity	|   	|
|:-:	|:-:	|:-:	|:-:	|
| Lopy4	| Development board	|   1	|  ![Alt text](https://se.farnell.com/productimages/standard/en_GB/2834985-40.jpg?raw=true "Title")|
|   Expansion Board V.3	|   Expansion board	|   1	|  ![Alt text](https://docs.pycom.io/gitbook/assets/expansion3.png?raw=true "Title") 	|
|   Antenna	|   LoRa (868MHz/915MHz), Sigfox Antenna	|   1	|   ![image alt](https://pycom.io/wp-content/uploads/2018/08/atennaNewTransGX.png "title")	|
|   DHT11	|   Temperature & Humidity sensor	|  1 	|   ![image alt](https://images-na.ssl-images-amazon.com/images/I/61lSag1UfVL._SX342_.jpg? "title")	|
|   Breadboard	|   -	|   1	|   ![image alt](https://cdn.bodanius.com/media/1/0d8100207_Breadboard-wit-400-punten_x.png "title")	|
|   Cables	|   Jumper wires	|   3	|   ![image alt](https://www.exploringarduino.com/wp-content/uploads/2013/05/45040-dscn0624-400x300.jpg?raw=true "title")	|
|   USB-cable	|  Micro USB 	|   1	|   ![image alt](https://www.elfa.se/Web/WebShopImages/landscape_large/_e/ps/maxxtro-pb-8043-10.jpg "title")	|

:::info
Did everyone manage to install the needed software? (VSCode, NodeJs och Pymakr)
https://docs.pycom.io/gettingstarted/software/vscode/ 
:::

| Software	|   	|
|:-:	|:-:	|
| VSCode	| ![Alt text](https://logowik.com/content/uploads/images/visual-studio-code7642.jpg "Title")|
| NodeJs	| ![Alt text](https://seeklogo.com/images/N/nodejs-logo-D26404F360-seeklogo.com.png?raw=true "Title")|
| Pymakr	| ![Alt text](https://pycom.io/wp-content/uploads/2020/02/PyMakr-logo.png?raw=true "Title")|
-----


# The Things Stack (TTS)

:::danger
**⚠️ Important ⚠️ Make sure the LoRa antenna is connected properly before running any LoRa or Sigfox code on your device. Not doing so might break your device.** You can find how to properly connect the antenna [here](https://docs.pycom.io/tutorials/networks/sigfox/).
:::

![image alt](https://i.imgur.com/jTWsOPP.png)

## 1. Register
Before you can start using TTS, you need to [create a TTS account](https://account.thethingsnetwork.org/register). Once you have done that, go ahead and navigate to Console.

![](https://i.imgur.com/eb22wU8.png)

Then select Europe 1 (eu1) and choose the `Login with The Things ID` option to log in.

![](https://i.imgur.com/3apo8Cv.png)


## 2. Create an application
Once you've logged in, you'll be redirected to the your homepage. There, you navigate to `Create an 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 pycom device to the application

Once there, navigate to `Add end device` in the bottom-right corner to add your pycom device to the application. The Lopy4 is not in TTS's LoRaWAN device repository yet, but you can still add it manually.

![](https://i.imgur.com/7vG3L8R.png)

Set the **frequency plan** to `Europe 863-870 MHz (SF9 for RX2 - recommended)`, **LoRaWAN version** to `MAC V1.0.2` and the **regional parameters version** to `PHY V1.0.2 REV A`. 

More information about activation modes can be found [here](https://www.thethingsnetwork.org/docs/lorawan/end-device-activation/). 

The next step is to enter your device information. The `DevEUI` can be retrieved from your pycom device by running the following code-snippet in your interpreter, either in Atom or VSCode. 

Alternatively, you can paste the code snippet inside your `main.py` and upload your files. Create`main.py` if it does not exist. 

```python=
from network import LoRa
import binascii

lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868)
print(binascii.hexlify(lora.mac()).upper().decode('utf-8'))
```

The output should look something like:

```python=
70B3D54997C25011
```


Fill the `AppEUI` with zeros by pressing on the `Fill with zeros` button. 


Finally, you generate your `AppKey` and register your end device to your application. 



Once you've added your end device to your application, you should be redirected to your device page. The landing page should look like:

![](https://i.imgur.com/LrkMxl3.png)


## 4. Connect to TTS using your pycom device
Download the code fom the [Github repo](https://github.com/iot-lnu/dht11-workshop) and continue.

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 or Sigfox code on your device. Not doing so might break your device.** You can find how to properly connect the antenna [here](https://docs.pycom.io/tutorials/networks/sigfox/).
:::

First, add the snippet below at the begining of your `boot.py` that you have in your project folder in Atom or VSCode. 

```python=
from network import LoRa
import time
import binascii
import socket

lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868)

app_eui = binascii.unhexlify('0000000000000000')
app_key = binascii.unhexlify('00000000000000000000000000000000')

lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=0)

# wait until the module has joined the network
while not lora.has_joined():
    time.sleep(2.5)
    print('Not joined yet...')

print('Network joined!')


s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5)
s.setblocking(False)


```

:::info
Remove duplicate imports if you have any after adding the snippet above. 
:::

Now, replace the zeros in row 8 with your `AppKey`. In our case, the generated `AppKey` was `2AFCC3031C924792687395AB9D384262`. Note that only row 8 is changed. 


```python=
from network import LoRa
import time
import binascii
import socket

lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868)

app_eui = binascii.unhexlify('0000000000000000')
app_key = binascii.unhexlify('2AFCC3031C924792687395AB9D384262')# Add your own app_key here

lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=0)

# wait until the module has joined the network
while not lora.has_joined():
    time.sleep(2.5)
    print('Not joined yet...')

print('Network joined!')


s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5)
s.setblocking(False)

```

:::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/ikBVVe3zQymcUxo6rbIdlQ?view) or [Sigfox](https://hackmd.io/iItkcV_XTgCdjRooqlUyOg?view). 
:::

## 5. Send messages with TTS

Now we need something to send, so let's read data from a DHT11 and send that data to TTS. Connect the sensor according to the schematic below: 

![image alt](https://i.imgur.com/VMnX3ag.png "title")

Then, we need to create a file named `dht.py` and paste the following code inside it: 

```python= 
# https://github.com/JurassicPork/DHT_PyCom
import time
import pycom
from machine import enable_irq, disable_irq,  Pin

class DHTResult:
    'DHT sensor result returned by DHT.read() method'

    ERR_NO_ERROR = 0
    ERR_MISSING_DATA = 1
    ERR_CRC = 2

    error_code = ERR_NO_ERROR
    temperature = -1
    humidity = -1

    def __init__(self, error_code, temperature, humidity):
        self.error_code = error_code
        self.temperature = temperature
        self.humidity = humidity

    def is_valid(self):
        return self.error_code == DHTResult.ERR_NO_ERROR


class DHT:
    'DHT sensor (dht11, dht21,dht22) reader class for Pycom'

    #__pin = Pin('P3', mode=Pin.OPEN_DRAIN)
    __dhttype = 0

    def __init__(self, pin, sensor=0):
        self.__pin = Pin(pin, mode=Pin.OPEN_DRAIN)
        self.__dhttype = sensor
        self.__pin(1)
        time.sleep(1.0)

    def read(self):
        # pull down to low
        self.__send_and_sleep(0, 0.019)
        data = pycom.pulses_get(self.__pin,100)
        self.__pin.init(Pin.OPEN_DRAIN)
        self.__pin(1)
        #print(data)
        bits = []
        for a,b in data:
        	if a ==1 and 18 <= b <= 28:
        		bits.append(0)
        	if a ==1 and 65 <= b <= 75:
        		bits.append(1)
        #print("longueur bits : %d " % len(bits))
        if len(bits) != 40:
            return DHTResult(DHTResult.ERR_MISSING_DATA, 0, 0)
        #print(bits)
        # we have the bits, calculate bytes
        the_bytes = self.__bits_to_bytes(bits)
        # calculate checksum and check
        checksum = self.__calculate_checksum(the_bytes)
        if the_bytes[4] != checksum:
            return DHTResult(DHTResult.ERR_CRC, 0, 0)
        # ok, we have valid data, return it
        [int_rh, dec_rh, int_t, dec_t, csum] = the_bytes
        if self.__dhttype==0:		#dht11
            rh = int_rh 		#dht11 20% ~ 90%
            t = int_t 	#dht11 0..50°C
        else:			#dht21,dht22
            rh = ((int_rh * 256) + dec_rh)/10
            t = (((int_t & 0x7F) * 256) + dec_t)/10
            if (int_t & 0x80) > 0:
                t *= -1
        return DHTResult(DHTResult.ERR_NO_ERROR, t, rh)


    def __send_and_sleep(self, output, mysleep):
        self.__pin(output)
        time.sleep(mysleep)

    def __bits_to_bytes(self, bits):
        the_bytes = []
        byte = 0

        for i in range(0, len(bits)):
            byte = byte << 1
            if (bits[i]):
                byte = byte | 1
            else:
                byte = byte | 0
            if ((i + 1) % 8 == 0):
                the_bytes.append(byte)
                byte = 0
        #print(the_bytes)
        return the_bytes

    def __calculate_checksum(self, the_bytes):
        return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255


```



Below, you can see how your `main.py` file should look like.


```python=
# Data is sent to Pybytes. Needs to flashed with Pybyte firmware
import time
from machine import Pin
from dht import DHT # https://github.com/JurassicPork/DHT_PyCom
import struct

# Type 0 = dht11
# Type 1 = dht22

th = DHT(Pin('P23', mode=Pin.OPEN_DRAIN), 0)
time.sleep(1)

while True:
    result = th.read()
    while not result.is_valid():
        time.sleep(.5)
        print("Not valid")
        result = th.read()

    temperature = result.temperature
    humidity = result.humidity

    print('Temperature:', temperature, 'Humidity:', humidity)

    """
    Prepare the data by packing it before sending it to sigfox
    Payload format is: >bb BB HHHH HHHH HHHH where
    b =                     (1 byte,  8 bits,  signed)       Range: -128 to 127
    B = Humidity            (1 byte,  8 bits,  unsigned)     Range: 0 to 255
    H =                     (2 bytes, 16 bits, unsigned)     Range: 0 to 65,535
    h = Temperature         (2 bytes, 16 bits, signed)       Range: -32,768 to 32,767
    """
    package = struct.pack('>hB',
                            int(temperature),
                            int(humidity))
    s.send(package)

    time.sleep(60)

```

By saving `boot.py`, `dht.py` and `main.py` and uploading your code, you should see an output similar to the one below:

![](https://i.imgur.com/STiHX3c.png)

And in the end device page in your TTS console, you should see something like: 

matches

![](https://i.imgur.com/1uvEUVy.png)


Note that the MAC payload is in hex (base 16). The first two bytes represents our temperature value, and the third byte respresents our humidity value.


:::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).
:::

# Datacake

Now that we have some sensor data being sent to the cloud using LoRaWAN, why not visualize it in a dashboard? 

## Create a datacake account
https://datacake.co/

Then navigate to dashboard and klick on your profile icon:

![image alt](https://i.imgur.com/zJDW1B3.png "title")

then `Edit profile` -> `API` -> `Show` and copy the token.

## Create a datacake webhook in TTS

![](https://i.imgur.com/imorggI.png "title")

Now, we input the token we copied from datacake into the token field

![](https://i.imgur.com/4wPGfPs.png)

and hit `Create datacake webhook`.


## Create a decoder and add fields

In datacake, choose your device and navigate to `Configuration` and scroll down until you see `Payload decoder`, then replace what ever code there is there with the code snippet below and hit `Save`.

```js=
function Decoder(payload, port) {
    if(port === 1 || port === 2) {
        return [
          {
              field: "TEMPERATURE",
              // value: payload[0] // Unsigned and only one byte
              // To view negative values, we shift bits.
              value: (payload[0] << 24 >> 16 | payload[1]) // / 100
          },
          {
              field: "HUMIDITY",
              value: payload[2]
          }
        ];
    }
}
```

Finally, scroll down a little more and you should see `Fields`. Add two fields, one for `temperature` and one for `humidity`.

![image alt](https://i.imgur.com/UDVPKoH.png "title")

## Create a dashboard

Navigate to dashboard and toggle the button at the far right and start adding widgets! 
