# Tutorial to build an indoor air quality measurement IoT device >By Melanie Augustinus (ma226bg) Prepared for Applied Internet of Things (1DT305) @ Linnaeus University, summer 2021 The aim of this tutorial is to show the process of building an indoor air quality measurement device which will send data to the Internet. The basis of this device is a Pycom microcontroller and it will measure temperature, humidity, Total Volatile Organic Compounds (TVOC) and CO2 using two sensors. It will send this data over WiFi to the Internet, making it an Internet of Things (IoT) device. The data is sent to a cloud service, where it is stored and visualised, which can be viewed from any computer or smart phone with an internet connection. This makes it possible to monitor the data over a longer period of time without being physically present. The estimated time to follow this tutorial and build this device - given that you have all the necessary hardware - is 30-75 minutes, depending on the amount of previous experience with similar projects, for example with Arduino. ## Objective The main purpose of this project is learning about IoT, software, coding, electronics and hardware, while also making a useful device for a home or work environment. Many people spend many hours indoors each day, so it is useful to know what the air quality is like and if any actions should be undertaken to improve it, for example better ventilation. The importance of ventilation has also been underlined as one of the [preventive measures in the COVID-19 pandemic](https://pubmed.ncbi.nlm.nih.gov/33889481/). By measuring data that indicates the air quality over time, insight in patterns may be gained (for examples differences between day/night, windows open/closed, curtains open/closed). This could provide opportunities to make better informed choices and actions with regard to air quality. Ultimately, improved air quality could lead to better a healthier living and working environment. ## Material Table 1 - List of material | Hardware | Price (including VAT) | Vendor | |:-------------------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------- | | [Pycom LoPy4](https://docs.pycom.io/datasheets/development/lopy4/) | € 39,87 | [Antratek](https://www.antratek.com/lopy4) | | [Pycom Expansion Board 3.0](https://docs.pycom.io/datasheets/expansionboards/expansion3/) | € 20,51 | [Antratek](https://www.antratek.com/expansion-board-for-pycom-modules) | | [SparkFun Air Quality Sensor - SGP30 (Qwiic)](https://learn.sparkfun.com/tutorials/sparkfun-air-quality-sensor---sgp30-qwiic-hookup-guide) | € 24,14 | [Antratek](https://www.antratek.com/air-quality-sensor-sgp30-qwiic) | | | [SparkFun Qwiic Cable - Breadboard Jumper (4-pin)](https://www.sparkfun.com/products/14425) | € 1,82 | [Antratek](https://www.antratek.com/qwiic-cable-breadboard-jumper-4-pin) | | [SparkFun Humidity and Temperature Sensor - DHT22](https://www.sparkfun.com/products/10167) | € 12,04 | [Antratek](https://www.antratek.com/humidity-and-temperature-sensor-rht03) | | Breadboard | € 5,00 (approx., part of basic Arduino kit) | [Antratek](https://www.antratek.com/sidekick-basic-kit-for-arduino-v2) | 5 x Jumper cables (M/M) | € 2,50 (approx., part of basic Arduino kit) | [Antratek](https://www.antratek.com/sidekick-basic-kit-for-arduino-v2) | | Micro USB data cable | € 5,00 (approx., previously owned) | n/a | | USB wall plug | € 4,00 (approx., previously owned) | n/a | | **Total** | **€ 114,88** | ### Pycom LoPy4 and Expansion Board 3.0 In this project I worked with a Pycom LoPy4 and Expansion Board 3.0, since this was the advised microcontroller for the course. The device could also be made with other microcontrollers with some adjustments in the code and/or the connections. The LoPy4 is a small microcontroller with connectivity for WiFi, LoRaWAN, Sigfox and BLE (Bluetooth Low Energy). This makes it very versatile regarding how and where a device can be deployed: especially the LoRaWAN and Sigfox integration make it possible to place the device in the outdoors or other places where no WiFi coverage is available, given there is access to networks using LoRaWAN (such as [Helium](https://www.helium.com/) and [The Things Network](https://www.thethingsnetwork.org/)) or [Sigfox](https://www.sigfox.com/en). The code used on Pycom products is MicroPython: a lighter version of Python. This is a high level programming language, which is easy to learn and work with. The Expansion Board 3.0 is a shield for the LoPy4 and provides the possibility to easily connect sensors to the pins and the usb cable. The two parts can be connected by placing the LoPy4 on top of the expansion board and pressing the headers firmly in the middle two rows. Both Pycom logo's should be facing the same way and the LED on the LoPy should be aligned with the USB port on the expansion board (see figure 2). ![](https://i.imgur.com/vEtaIQT.jpg =250x) ![](https://i.imgur.com/jyoK1T0.jpg =300x) *Figure 1 - LoPy4 and Expansion Board 3.0 (pycom.io)* ![](https://i.imgur.com/J6nb7ht.jpg =300x) *Figure 2 - LoPy4 mounted on Expansion Board (sparkfun.com)* ### SparkFun Air Quality Sensor (SGP30) and Qwiic breadboard jumper The [SparkFun Air Quality Sensor (SGP30)](https://learn.sparkfun.com/tutorials/sparkfun-air-quality-sensor---sgp30-qwiic-hookup-guide) measures ethanol and H2 in the air and returns as output CO2 levels and Total Volatile Organic Compounds (TVOC), which gives an indication of indoor air quality (see the [datasheet](https://cdn.sparkfun.com/assets/c/0/a/2/e/Sensirion_Gas_Sensors_SGP30_Datasheet.pdf) for further information). It is a digital sensor that uses the [I2C protocol](https://learn.sparkfun.com/tutorials/i2c/all), which allows for serial and synchronous communication of multiple circuits in one sensor. Compared to other air quality sensors, the SGP30 sensor is much faster and easier to work with. It has a very short start-up time (around 15 seconds) and no burn-in time, while other sensors require a longer startup time plus a burn-in time. The sensor has no pins but uses a Qwiic connector to connect to microcontroller. The [Qwiic system](https://www.sparkfun.com/qwiic) is designed especially for I2C sensors and comprises of a small connector with four jumper wires built in, thus ensuring that no mistakes are made in connecting jumper wires. However, since the microcontroller chosen in this project does not have Qwiic connectors on board, the sensor is connected using a [Qwiic breadboard jumper cable](https://www.sparkfun.com/products/14425), which has a Qwiic connector on one side and four separate jumper cables on the other side. Note that the SGP30 sensor can also be bought from other manufacturers, such as [Adafruit](https://www.adafruit.com/product/3709). This should work the same. ![](https://i.imgur.com/kEOe6Z1.jpg =300x) ![](https://i.imgur.com/HsBjv4w.jpg =350x) *Figure 3 - Air Quality Sensor (SGP30) and Qwiic breadboard jumper cable (sparkfun.com)* ### SparkFun Humidity and Temperature Sensor (DHT22) The [SparkFun Humidity and Temperature Sensor (DHT22)](https://www.sparkfun.com/products/10167) is a simple digital sensor which measures temperature and relative humidity. It is ready to use without calibration and it can be inserted directly into a breadboard. It is more accurate than the cheaper [DHT11](https://www.adafruit.com/product/386) sensor, although this sensor could also be used in this project with only a minor adjustment in the code (see the [comment in the code](https://github.com/meaug/indoor_air_quality_dht22_sgp30/blob/main/main.py) in line 60). Both the DHT22 and DHT11 sensors can be bought from mulitple manufacturers. ![](https://i.imgur.com/ZJ8kI2E.jpg =250x) *Figure 4 - Humidity and Temperature Sensor (DHT22) (sparkfun.com)* ### Breadboard and jumper cables A breadboard provides space to connect multiple sensors to a single microcontroller. The microcontroller powers the breadboard + and - band on the sides. The sensors are then connected with these bands. The input and output to and from the sensors is either connected via the breadboard or directly to the microcontroller pins. These connections are made using small male/male jumper cables. The holes on the breadboard are part of interconnected rows as shown in figure 6. This results in the possibility to make a connection to a jumper cable or a sensor pin using any hole in the same row. ![](https://i.imgur.com/gnBMaPF.jpg =300x) ![](https://i.imgur.com/alwCIpl.jpg =350x) *Figure 5 - Jumperwire and breadboard (antratek.com)* ![](https://i.imgur.com/cacDBKA.jpg =400x) *Figure 6- Interconnected rows on breadboard (seeedstudio.com)* If this is the first time doing a project with a microcontroller, I would highly recommend getting a basic components kit such as the one by [Seeed studio](https://www.seeedstudio.com/Sidekick-Basic-Kit-for-Arduino-V2-p-1858.html) (€ 19,30 at [Antratek](https://www.antratek.com/sidekick-basic-kit-for-arduino-v2)). It contains many convenient items (including a breadboard and jumper cables) for many basic projects. ### Micro USB data cable and wall plug A micro USB data cable is used to connect the device to your computer. The cable should be able to handle data, as it is needed to send the input and output between the computer and the device. Some cables are only suitable for charging and will not work properly with a microcontroller. A cable that has been used previously for data, such as for an e-reader, should work well for connecting this device as well. Otherwise a new cable should be bought. The device is also powered through the USB cable. When properly programmed, you can move your device away from the computer, place it somewhere else and power it by connecting the USB cable to a wall plug. Alternatively, you can use a [rechargable LiPo battery with a JST-PH-connector](https://www.antratek.com/polymer-lithium-ion-battery-2000mah). This can be connected directly to the expansion board and can be charged with the USB cable. ## Computer setup For this project I used a laptop with Windows 10. I will describe the steps necessary to run the project guided by the [first tutorial](https://hackmd.io/@lnu-iot/rk4qNlajd) from by the Applied IoT course's teaching assistants. If any of the software is already installed on the system, check that it is the most recent version. It is also possible to use Mac or Linux. For these systems guidance can also be found in the tutorial above. ### 1. Updating the Pycom firmware To get the best performance of the LoPy4 and the Expansion Board, the firmware should be up to date. The process is described in this [tutorial by Pycom](https://docs.pycom.io/updatefirmware/device/). Make sure that the LoPy4 is mounted on the Expansion Board as [described above](#Pycom-LoPy4-and-Expansion-Board-30), that the device is connected to the USB port and that the hightest COM port is selected. ### 2. Installing Node.js Installing [Node.js](https://nodejs.org/en/) is necessary to run certain parts of software in the IDE. ### 3. Installing an IDE To be able to program the device, an IDE (Integrated Development Environment) is needed. Both [Visual Studio Code](https://code.visualstudio.com/) and [Atom](https://atom.io/) will work for this project due to the availability of the Pymakr plug-in. I chose Atom because it has a cleaner interface and there are [many tutorials](https://flight-manual.atom.io/getting-started/sections/installing-atom/) to learn to work with it, but both should work fine. ### 4. Installing Python The device will be programmed in [MicroPython](https://micropython.org/). You will need to install the latest version of [Python](https://www.python.org/downloads/) to work with it. When installing, make sure to select the tickbox **Add to PATH**, or it will not work. Do not install the Python distribution from the Windows Store Package, as it will not be possible to select this option. ### 5. Installing Visual Studio with Desktop development with C++ In order to work with Python, the installation of [Visual Studio](https://docs.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=msvc-160) is necessary (note that this is not the same as the IDE). Make sure to select and install the workload **Desktop development with C++**. ### 6. Installing Pymakr The last step is to install the plug-in [Pymakr](https://docs.pycom.io/gettingstarted/software/atom/) to Atom. This will add a REPL (Read-Evaluate-Print loop) to Atom and makes it possible to communicate directly between the Pycom device and the computer. It is used to upload and run code on the device. ## Putting everything together As stated before, the **LoPy4** is placed firmly on top of the **Expansion Board** as shown in figure 2. The **DHT22** is put directly into the breadboard. The device and the breadboard are connected to each other and the sensors with **jumper wires**, as shown in the graphical circuit diagram in figure 7 (made with [Fritzing](https://fritzing.org/)). Make sure to use all the same pins on the device when making the connections. The **SGP30** is connected with the **Qwiic cable** to the device with the blue and the yellow jumper wires on P9 (blue cable) and P10 (yellow cable). These are the standard pins that should be used when working with an I2C sensor: P9 is the SDA (data) pin and P10 is the SCL (clock) pin (see [LoPy Pinout](https://docs.pycom.io/datasheets/development/lopy/)). Except for the Qwiic cable, the **colours of the jumper wires** are arbitrary, although it can be helpful to add some kind of logic. In this case I used the same colours performing the same functions (red for power in, black for ground), and different colours for the other functions. The **USB cable** is connected to the port on the expansion board and a port on the computer. ![](https://i.imgur.com/5uIVhp8.png) *Figure 7 - Graphical circuit diagram of the project (Note that the SGP30 in this diagram has a different appearance to the one I used, however its connection is the same.)* ## Platform I have tried out two cloud platforms to store and visualize the data: Pybytes and Ubidots. [Pybytes](https://pybytes.pycom.io/) is made by Pycom for their devices exclusively and is completely free to use. Ubidots is a more general IoT platform where devices from different companies can be used. Ubidots provides commercial services to organisations who work with IoT, but also provides a free plan for educational use, called [Ubidots STEM](https://ubidots.com/stem/). Both Pybytes and Ubidots STEM retain data for one month. This is adequate for educational and testing purposes, but longer data retention is wanted, using a [paid plan from Ubidots](https://ubidots.com/pricing/) is a possibility. Pybytes does not offer any plans with longer data retention. If paying is not an option and not much data will be sent, for example only from this one device, it might be possible to make use of the [Google Cloud free tier](https://cloud.google.com/free/docs/gcp-free-tier#free-tier), although I have not tried that out. Pybytes offers an [integration with Google Cloud](https://docs.pycom.io/pybytes/integrations/google/), so it should be relatively easy to set up. If intending to scale up and deploy many devices with an advanced dashboard, a paid account with an IoT cloud service is needed. ### Pybytes Pybytes is easy to set up ([see the Pycom documentation](https://docs.pycom.io/pybytes/gettingstarted/) or the [teacher assistants' tutorial](https://hackmd.io/@lnu-iot/r1bGPUOhu) for more detail). The WiFi connection is set up in Pybytes and not in the code, which makes it less prone to mistakes and more suitable for beginners. As soon as data is sent, it is also very easy to visualize it and [make a dashboard](https://docs.pycom.io/pybytes/dashboard/). However, the options for visualizations are limited: only a table, a bar chart and a line chart can be selected. Little changes can be made to their appearance such as colour. Also, multiple steps need to be taken to change the parameters and to see an effect of these changes, which makes it cumbersome to do finetuning. ### Ubidots Ubidots is slightly harder to set up due to the coding required, although the [tutorial](https://help.ubidots.com/en/articles/961994-connect-any-pycom-board-to-ubidots-using-wi-fi-over-http) is relatively easy to follow. Some of the screenshots and steps in the tutorial are out-dated, but the code works fine. A [personal token](https://help.ubidots.com/en/articles/590078-find-your-token-from-your-ubidots-account) needs to be added to the code. It is important to use the token and not the API key. Another important step is to add the credentials for the WiFi network to the code in the place indicated. When everything has been set up correctly and the code is uploaded to the device, it should start sending data almost immediately. Then it is possible to configure the dashboard to visualize the data. There are many options to do this: from different kinds of charts to various informational graphics. By visualizing the data in different ways, it is more engaging to work with: it is possible to set different parameters and styling options and play around with it. This can also be a convenient way of displaying information, as I will demonstrate in [Presenting the data](#Presenting-the-data). ## The code This project consists of five files: * `main.py`: the main program that is executed when the device is running, see further explanation below * `boot.py`: runs before `main.py`, when the device has been booted. The code can be copied from the [Ubidots setup tutorial](https://help.ubidots.com/en/articles/961994-connect-any-pycom-board-to-ubidots-using-wi-fi-over-http). * `adafruit_sgp.py`: library file for [SGP30 (GitHub)](https://github.com/alexmrqt/micropython-sgp30/blob/master/adafruit_sgp30.py) * `dht.py`: library file for [DHT22 (GitHub)](https://github.com/JurassicPork/DHT_PyCom/blob/master/dth.py) * `urequests.py`: library file for [Ubidots (GitHub)](https://github.com/jotathebest/micropython-lib/blob/master/urequests/urequests.py) The files with the code should be uploaded according to the naming and folder structure as shown in figure 8. The library files should be placed in a seperate folder (inside the project folder) called `lib`. These files are necessary for the sensors and the connection to Ubidots to work properly. The libraries can be downloaded or copied from GitHub via the links above. If running the SGP30 library returns this error: `TypeError: unsupported types for : 'int', 'int'` the following line needs to be adjusted: ```python _SGP30_FEATURESET = const(0x0020) ``` into: ```python _SGP30_FEATURESET = (0x0020, 0x0022) ```` ![](https://i.imgur.com/lJgSTnv.png) *Figure 8 - Folder structure and file names* ### `Main.py` The `main.py` file is mainly built up from different parts of example code of the three libraries that are used with changes and adjustments for the variables of this project. I have added code to send the humidity measurement from the DHT22 sensor to the algorithm that is used by SGP30 library: ````python sgp30.set_iaq_rel_humidity(result.humidity, result.temperature) ```` Without a direct measurement of humidity, this library would work with an approximated value of humidity. Using an actual measurement of humidity should make the output more accurate. I have also made some minor adjustments, mainly in print out commands to make wording clearer or more informative. The [complete code can be found on GitHub](https://github.com/meaug/indoor_air_quality_dht22_sgp30/blob/main/main.py), where I have placed comments to explain what various parts of the code do. ## Transmitting the data/connectivity The data is sent to Pybytes and Ubidots over WiFi every 5 minutes. It is not necessary to send data more often than that, as any changes are not something that needs to be acted on urgently. WiFi is the obvious choice for a device that is used at home and indoors, where a WiFi connection is likely to be more readily available and more stable than other types of connectivity, such as LoRaWAN and Sigfox. Unlike these networks, WiFi does not have a maximum amount of data that may be sent over the network. It is more power hungry, but seeing the device is on AC power, this is not a problem. The WiFi connection to Pybytes is configured through the Pybytes website and starts automatically when the device is started. The connection to the MQTT broker also starts automatically, which can be seen in the REPL when the device boots when it is connected to the computer. The data is then sent with this code in `main.py`: ````python pybytes.send_signal(1,result.temperature) pybytes.send_signal(2,result.humidity) pybytes.send_signal(3,co2_eq) pybytes.send_signal(4,tvoc) ```` The WiFi connection to Ubidots is configured in `main.py`. The data is built into a JSON comprising of four variables and four values, which is then sent to Ubidots via a HTTP POST method using the [REST API](https://ubidots.com/docs/sw/). The code is adapted from the [Ubidots tutorial](https://help.ubidots.com/en/articles/961994-connect-any-pycom-board-to-ubidots-using-wi-fi-over-http) to fit the four variables in this project (see the [code in GitHub](https://github.com/meaug/indoor_air_quality_dht22_sgp30/blob/main/main.py)). If a battery and/or a different wireless connection would be used, the data should be sent every 10 minutes at the most. This can be done by changing the sleep time in the last line of `main.py`. You would also need to make changes in the code to connect with other networks. This falls out of the scope of this tutorial, but can be looked up in the [tutorial by teacher assistants](https://hackmd.io/@lnu-iot/HyNMCxinu). ## Presenting the data The data is saved every 5 minutes and retained for 1 month. I have chosen to show both the dashboard from Pybytes (see figure 9) and the one from Ubidots (see figure 10), because I find it interesting to compare the very simple dashboard in Pybytes and the more elaborate one made with Ubidots. While it is slightly harder set up Ubidots, the the dashboard is much more useful. I would therefore advise using Ubidots. ![](https://i.imgur.com/uGVRuF8.png) *Figure 9 - Pybytes dashboard* ![](https://i.imgur.com/QJ6ITBs.png) *Figure 10 - Ubidots dashboard* In Pybytes, it is only possible to choose a table or a chart and there are little options, while in Ubidots various widgets can be selected with many different settings. For example, for the CO2 levels I have used a gauge widget with a pointer and set different colours for different ranges of values, indicating which levels are normal/safe (two shades of green: [up until 400 ppm](https://www.kane.co.uk/knowledge-centre/what-are-safe-levels-of-co-and-co2-in-rooms) is considered normal for outdoor air, up until 800 ppm is safe for inside environments), which levels are high, but not yet dangerous (orange: between [800 and 1200 ppm](https://klimaatbeheer.eu/news/co2-binnenklimaat/)) and which levels are too high (red: over 1200 ppm). For the other three variables I have also chosen a graphic widget to show the current value. For the temperature I have used the thermometer, for the humidity the tank, and for the TVOC levels a number. For all four variables I have also placed a line chart widget, so it is easy to see how the values have changed over time. Next to designing an appealing dashboard, Ubidots also provides the possibility to add event triggers. In this project I have configured it to send an e-mail when the CO2 level rises above 1200 ppm for more than 15 minutes, so I would know when I would need to take action regarding the air quality in my room. It is possible to set different triggers for the various variables and also use different methods of sending a message, including SMS and voice call. This is very convenient if you want to monitor certain values without continuously checking the dashboard. A trigger for device inactivity can also be set. Pybytes does offer some trigger events (called notifications), but at the moment only for battery level, data usage and inactivity. ## Finalizing the design The end result of my device is shown in figure 11. I have kept the design of my project simple, in order to execute it in the best possible way while learning about hardware, coding, protocols, electronics and more, while trying different types of sensors, networks and platforms, and getting it all still to work. This has generally gone well. I have some ideas about how it might be done differently or better. First of all, the device is still a raw prototype and would benefit from a case with an appealing design and adequate air holes. In the future, it would be interesting to explore 3D design to be able to 3D print a suitable case. Another nice addition would be to add green, orange and red LEDs which would serve as the indicator of the CO2 levels on the device itself, similar to the widget on the Ubidots dashboard. Lastly, sending the data to two different platforms is not necessary for functionality, so to prepare this device for a more permanent set-up, I would remove the connection to Pybytes (both in the code and on the device itself via Pybytes' website). I would also consider using a cheaper microcontroller with WiFi which uses the same chipset (ESP32) as the LoPy4 does. The LoPy4's different kinds of connectivity are an advantage when learning about IoT and developing a device, but not necessary when only WiFi is used in the final device. To sum up, I am satisfied with the final results given my previous knowledge. I have made a useful device and I have learnt about many new topics, but I realise there is much more to learn. This course has inspired me to continue improving my knowledge and skills by doing more microcontroller and IoT projects in the future. ![](https://i.imgur.com/Lw08dQg.jpg) *Figure 11 - Final result*