---
# System prepended metadata

title: Tutorial of constructing an environment IoT tracker

---

# Tutorial of constructing an environment IoT tracker

###### tags: IoT, environment tracking


## Environmental IoT connected tracker 
This project has the purpose of tracking basic environmental data, such as relative humidity, temperature, environment lighting and heat index, i.e. feels-like-temperature. The product uses fundamental sensors and is supposed to have two modes, inside or outside, and send data from these sensors to an IoT platform.

Student ID: Jakob Pettersson (jp223hu)
Approximate development time: 10 h

### Table of contents
[ToC]
 

### Objective

This device serves the purpose of long term and long distance tracking of either inside or outside environment. With this tracker the user should be able to read temperature, relative humidity and heat index. The device also reads off the environment light intensity. This is to decide the weather conditions if set to outside mode, i.e. sunny, cloudy or partially sunny, or if the room is light, i.e. light turned on or daylight or dark when set in inside mode. I chose this project because I found this data to be interesting and useful to track, as well as a good possibility to be encountered with a few interesting sensors. While working with this project, my aim is for it to help develop an interest in IoT and give insights in the work of internet connected devices as well as using sensors to collect and process data from these. 


### Material

In this section the product list of material is 
included, along with a short description of each component's purpose, where they may be bought and for how much, and a figure including the most important components. 


| Material | Buying site  |Price    |
| ---------|:------------|:--------|
| Pycom LoPy 4 (Micro controller)| [:link:][Electro-kit]|€34.95 |
| Pycom Expansion Board |[:link:][Electro-kit]|€16.00|
| Micro USB cable (1.8 m) | [:link:][Electro-kit]|31.2 SEK|
| Resistor (Two)   | [:link:][Electro-kit]|0.8 SEK|
| Photo Resistance CdS 2-5 kΩ (Two)       | [:link:][Electro-kit]    | 6.4 SEK|
| Jumper Wire   | [:link:][Electro-kit] | - |
| Push Button   | [:link:][Push-button] |19 SEK|
| Temperature and humidity sensor (DHT11)   | [:link:][DHT11] |49 SEK|
|  Temperature sensor (DS18B20)  | [:link:][DS18B20] |42 SEK|


The LoPy 4 is, as the table above states the micro controller of the device, i.e. the heart of the product which controls all components, reads data and manages the connection with the platform. The expansion board provides a manageable view over the available GPIO's as well as a jack for USB connection and/or JST jack for battery connection. 

The photo resistors are used to read off a voltage strictly depending on the surrounding environment light intensity. Its output signal voltage decreases as light intensity increases. The two resistors are used to get a downsize of the signal voltage read from the photo resistor. One resistor is used for the inside mode and the other for outside mode. Since it is a significant different light intensity when inside from when outside, the device needs to divide the signal voltage differently. Suitable resistor values were choosen experimantally with reading values from different light intensities.

The push button is used to decide whether the device is placed inside or outside, i.e. if the outside or inside light intensity measuring circuit is to be evaluated. The DHT11 measures relative humidity, which gives a percentile value where a higher value shows a higher humidity. The temperature sensor DS18B20 simply reads the dry temperature of the environment.

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

> In the figure above, we see graphic images over some of the components in the table above. In the upper left corner is the LoPy4 and to the right is the expansion board. Beneath is the DHT11 and DS18B20. And at the bottom is the push button, a photo resistor and an ordinary resistor.


---

## Computer setup
The setup of the pycom firmware is set up accordingly with the step-by-step tutorial provided on the pycom webpage. [:link:][pycom] Before the LoPy 4 is connected to the expansion board, where the direction of the placement is of great importance, the expansion board may need updating of firmware which was done according to the pycom web page with the provided DFT-util tool. [:link:][exp-board] To easily be able to update the LoPy 4 with the latest pycom firmware an installation of an update tool is done. Then the firmware is flashed to the LoPy 4 using the update tool where the connected port is selected and the pybytes or legacy firmware is chosen depending on usage. [:link:][firmware-update] Lastly an IDE is installed, in this case Atom, which is connected to the LoPy 4. Atom IDE is installed, the pymakr plugin is installed via Atom and the device is connected with the corresponding serial port.  [:link:][atom] 



## Putting everygthing together
In the figure below is the circuit design shown (as a development setup). Using this together with the datasheet for the LoPy 4 (to know which GPIO corresponds to correct ports in the figure) and datasheet for the sensors (DHT11, push button and DS18B20), which is provided below, the device can be reconstructed.

LoPy 4 datasheet: [:link:][LoPy]
Datasheet for DHT11, push button and DS18B20: [:link:][sensors]


![](https://i.imgur.com/wFysaKn.png)
> In the figure above is a schematic picture shown to describe how the components are connected with each other. 

## Platform
The platform chosen is Blynk. It is a free cloud based platform. Firstly, the application is installed on a smart phone and a user is created. Secondly, the Blynk python library (BlynkLib.py) is included in the project library folder.  [:link:][BlynkLib] Lastly, the LoPy 4 is connected to the Blynk cloud, according to an example found on GitHub specifically for a LoPy4 which is included in the boot.py file. [:link:][Blynk_connect]

## The code

In lib-folder (all found online):
- BlynkLib.py: Library for connecting and transmitting to BLynk platform
- dht.py: Protocol for reading and communicating with DHT11 sensor.
- onewire.py: Onewire protocol used for DS18B20 sensor

Program code:

- boot.py
```python=1
## boot.py ##

import BlynkLib
from network import WLAN
import machine, time

############# WiFi parameters ####################
WIFI_SSID = '**SSID**'
WIFI_PASS = '**PASSWORD**'
#################################################


BLYNK_AUTH = '**AUTH_TOKEN**'                 #Blynk authorization token

print("Connecting to WiFi...")
wifi = WLAN(mode=WLAN.STA)
wifi.connect(ssid=WIFI_SSID, auth=(WLAN.WPA2,WIFI_PASS))
while not wifi.isconnected():
    time.sleep_ms(50)

print ('IP:', wifi.ifconfig()[0])

print("Connecting to Blynk...")
blynk = BlynkLib.Blynk(BLYNK_AUTH)

@blynk.on("connected")

def blynk_connected(ping):
    print('Blynk ready. Ping:', ping, 'ms')

def runLoop():
    while True:
        blynk.run()
        machine.idle()

# Run blynk in the main thread:

```
- main.py
```python=1
# main.py 
import pycom
import machine
import time
import math
from machine import Pin
from dht import DHT
from onewire import DS18X20
from onewire import OneWire
import _thread


pycom.heartbeat(False)                                          #Turn off heart beat
#When RGB starts to blink, user knows its connected to WiFi and Blynk
for i in range(3):
    pycom.rgbled(0xff00)
    time.sleep_ms(500)
    pycom.rgbled(0x000000)
    time.sleep_ms(500)
pycom.heartbeat(True)

blynk.virtual_write(2, 'Inside')                                #Initial mode: Inside, send to virtual pin 2 to Blynk
blynk.virtual_write(1, ' ')                                     #No initial light value, send to virtual pin 1 to Blynk

inside = 1                                      #Global indicator for inside or outside mode, default inside
light_intensity = []                            #Global vector to store light intensity values in

pin_mode = Pin('P11', mode = Pin.IN)            #Pin to read mode button

ow = OneWire(Pin('P10'))                        #Initialize wire  w. OneWireError
ds = DS18X20(ow)                                #Create new ds18x20 obj from onewire(temp sensor)

th = DHT(Pin('P23', mode=Pin.OPEN_DRAIN), 0)    #Create new DHT for reading RH and temp

adc = machine.ADC()                             #Create an ADC object to read light intensity, outside
apin_outside = adc.channel(pin='P20')               #analog pin outside ligth

apin_inside = adc.channel(pin='P19')                #analog pin inside light

c = (-8.78469475556, 1.61139411, 2.33854883889, -0.14611605, -0.012308094, -0.0164248277778, 0.002211732, 0.00072546, -0.000003582)     #Constants for heat index


time.sleep(2)

#Function for evaluating RH from DHT11
def dht_eval(th):
    result = th.read()                          #Read result from DHTResult
    #Keep reading if sensor fault
    while not result.is_valid():
        print('Non valid result')
        time.sleep(5)
        result = th.read()
    rh = result.humidity                        #Store resulting realtive humidity in variable rh
    blynk.virtual_write(4, rh)                  #Send relative humidity to Blynk (Pin 4)
    return rh

#Function for evaluating and sending measured temperature
def ds_eval(ds):
    temp = ds.read_temp_async()/100             #Read temperature from sensor, scale down by factor 100
    if temp < 40:
        blynk.virtual_write(5, temp)                 #Send temp to Blynk (Pin 5)
    time.sleep(1)
    ds.start_conversion()                       #Start conversion of temp
    return temp

def heat_index(temp, rh):
    hi = c[0] +  c[1]*temp + c[2]*rh + c[3]*temp*rh + c[4]*(temp**2) + c[5]*(rh**2) + c[6]*(temp**2)*rh + c[7]*temp*(rh**2) + c[8]*(temp**2)*(rh**2)
    if hi < 50 :
        blynk.virtual_write(3, hi)              #Send heat index to blynk (Pin 3)

# Light measurment outside
def light_eval_out():
    # LoPy  has 1.1 V input range for ADC
    light = apin_outside()                          #Read analog value of photo resistor
    #print("light = %5.1f" % (light))
    return light

# Light measurment Inside
def light_eval_in():
    # LoPy  has 1.1 V input range for ADC
    light = apin_inside()                          #Read analog value of photo resistor
    #print("light = %5.1f" % (light))
    return light

def evaluate_data():
    global inside                                   #Global mode indicator,
    global light_intensity                          #Global light intensity vector
    while True:
        light_intensity = []                        #Start with empty light vector

        while len(light_intensity) < 20:            #Collect 20 light intesity values
            rh = dht_eval(th)                       #evaluate relative humidity from dht11
            temp = ds_eval(ds)                      #Evaluate temperature from DS18X20
            heat_index(temp, rh)                    #Evaluate heat index
            if inside == 1:                         #If inside, evaluate inside light
                light_intensity.append(light_eval_in())
            else:                                   #else outside, evaluate outside light
                light_intensity.append(light_eval_out())
            time.sleep(4)

        mean_intensity = sum(light_intensity)/len(light_intensity)              #Calculate mean light intensity

        if inside == 1:                             #Inside mode
            if mean_intensity < 900:
                blynk.virtual_write(1, 'Light')     #Send "Light" to Blynk platform, Pin 1 
            else:
                blynk.virtual_write(1, 'Dark')      #Send "Dark" to Blynk platform, Pin 1
        else:                                       #Outside mode
            if mean_intensity < 1450:
                blynk.virtual_write(1, 'Sunny')    #Send "Sunny" to Blynk platform, Pin 1
            elif mean_intensity < 2200:
                blynk.virtual_write(1, 'Partially Sunny')     #Send "Partially Sunny" to Blynk platform, Pin 1
            else:
                blynk.virtual_write(1, 'Cloudy')    #Send "Cloudy" to Blynk platform, Pin 1

#Method for evaluating if user change mode, and if so, update to inside or outside mode
def mode_selection():
    global inside                #Global variable tp indicate current mode
    global light_intensity       #Global light vector to reset if change of mode
    while True:
        if inside == 1:
            if pin_mode() == 0:                     #If inside and button is pressed, change to outside mode
                inside = 0
                blynk.virtual_write(2, 'Outside')   #Update Blynk, virtual Pin 2
                blynk.virtual_write(1, ' ')         #Remove previous value of light info
                light_intensity = []                #Reset light intensity vector, collect new values
                time.sleep(1)
        else:
            if pin_mode() == 0:                     #If outside and button pressed, change to inside mode
                inside = 1
                blynk.virtual_write(2, 'Inside')
                blynk.virtual_write(1, ' ')
                light_intensity = []                #Reset vector to collect new light intensity values
                time.sleep(1)

#Two threads, one for continiously evaluating sensor data and sending 
#to platform, one for evaluating the mode selection button and 
#updating starting measurment of new mode 
_thread.start_new_thread(evaluate_data, ())

_thread.start_new_thread(mode_selection, ())

```


## Transmitting the data / connectivity
The data are sent to the Blynk platform continuously with the call of the down below function. Virtual Pin marks where to on the Blynk platform the data is meant to go, while the data is sent as a string. 

```python=1
blynk.virtual_write(**Virtual_Pin**, **Data**)
```
The data is sent continuously in the thread that evaluates the data except for a timer delay of totaling 5 seconds (1 second in the function ds_eval() and 4 seconds in the function evaluate_data() in infinite while-loop). The connection and transmission of data is based on WiFi and the transport protocol that Blynk uses is a customized TCP/IP protocol. The customized TCP/IP data packages are constructed as follows; 1 byte command, 2 byte message ID, 2 bytes length/status (this is the header) and a variable length payload.  


## Presenting the data

![](https://i.imgur.com/2XOJFeW.jpg)
> Figure of dashboard; in outside mode, with live history tracking and displaying both humidity and temperature in graph of history.

![](https://i.imgur.com/cf5l10u.jpg)
> Figure of dashboard; in outside mode, with live tracking of history and only focusing on history of temperature.

![](https://i.imgur.com/inuixIo.jpg)
> Figure of dashboard; in inside mode, with the historic view over the last 30 minutes focusing on relative humidity.

The data are by default saved in Blynk database and historic values are accessible via the graph of history for a maximum of 1 year back in time.


## Finalizing the design
![](https://i.imgur.com/jGRa5Zv.jpg)

![](https://i.imgur.com/TsnWAcM.jpg)

![](https://i.imgur.com/gil0lcH.jpg)
> In the three figures above, the final physical device is shown in three different angles. 

As seen in the figures above, the product is clearly in a development stadium. To be declared as a final product it needs to be built in a case for one thing. What could be improved on the product is to use better sensors for temperature and relative humidity whereas the ones currently used have a known error in its measurements. What also needs to be improved are that the light intensity sensors and the temperature sensor should be separated since direct sunlight on the temperature sensor increases the read off temperature. Possible solutions for this are either to place the light measurement circuits away from the other components so it can be placed in a position in direct sunlight where the other parts are to be placed in a shaded environment. Another possible solution is to develop a case which provides a shade for the temperature sensor with a built in gap where the light can reach the photo resistors. Lastly, the product may also be connected with a LiPo battery to the JST jack on the expansion board to be totally portable.

In conclusion, the project was successful and has fulfilled the purpose to be introduced to IoT development as well as actually creating a product which monitors environment data that are to be displayed on an internet connected platform.

[Electro-kit]: https://www.electrokit.com/en/product/lnu-1dt305-tillampad-iot-lopy4-and-sensors-bundle/
[Push-button]: https://www.electrokit.com/en/product/momentary-push-button-module/
[DHT11]: https://www.electrokit.com/en/product/digital-temperature-and-humidity-sensor-dht11/
[DS18B20]: https://www.electrokit.com/en/product/temperature-sensor-ds18b20/
[pycom]: https://docs.pycom.io/gettingstarted/
[exp-board]: https://docs.pycom.io/pytrackpysense/installation/firmware/
[firmware-update]: https://docs.pycom.io/gettingstarted/installation/firmwaretool/
[atom]: https://docs.pycom.io/pymakr/installation/atom/
[lopy]: https://docs.pycom.io/datasheets/development/lopy4/
[sensors]: https://www.electrokit.com/uploads/productfile/41015/User_guide.pdf
[BlynkLib]: https://github.com/vshymanskyy/blynk-library-python
[Blynk_connect]: https://github.com/vshymanskyy/blynk-library-python/blob/master/examples/hardware/PyCom_WiPy.py

