# 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