# Bedroom humidity/temperature/light sensor
### Project Report by Carl Gösfeldt/cg222zw
## Overview
The device measures the humidity, temperature, and light. This could be used for several purposes, but the primary one I had in mind when creating this project was to measure the conditions of a bedroom to ensure optimal sleeping conditions.
An approximation of the time needed to copy this project is less than a day. It is quite simple to do and suitable for beginners.
## Objective
This project aims to ensure that a bedroom's conditions are optimal for sleep. This is done by utilizing a raspberry pi pico WH, a humidity sensor, a temperature sensor, and a light sensor. The device can be placed in the bedroom together with a power cable connected to an outlet. Data will then be sent to Ubidots, where it can be reviewed by the user. If the conditions are not optimal for sleep, the user can make changes and review the impact of the changes on Ubidots.
I chose this project because I had previously considered buying a temperature and humidity measurer. Instead of doing that, I decided to create my own. It gave me a project to make and fulfilled my needs. Also, I only had the starter-kit from Electrokit (https://www.electrokit.com/lnu-starter), which limited me a bit in what project I could make.
## Material
| Material | Purpose | Cost | Purchased from | Image |
|-|-|-|-|-|
| **1x** Raspberry Pi Pico | Microcontroller for sensor control, calculations, and data transmission to Ubidots. | 110 SEK | Electrokit |  |
| **1x** Breadboard | Platform to connect the different components. | 70 SEK | Electrokit |  |
| **8x** Male-to-Male Lab Cables | Connects components on the breadboard. | 50 SEK | Electrokit |  |
| **1x** DHT11 Digital Temperature & Humidity Sensor | Measures temperature (0-50℃, ±2℃) and humidity (20-90% RH, ±5 RH). | 50 SEK | Electrokit |  |
| **1x** CdS Photoresistor 4-7k ohm | Measures light level. Resistance decreases with increasing light. | 8 SEK | Electrokit |  |
| **1x** 4.7k ohm resistor, 5% tolerance | Controls current to the photoresistor. | 1 SEK | Electrokit |  |
| **1x** USB A Male to Micro B Male Cable | Connects the pico to a power source. | 40 SEK | Electrokit |  |
| **1x** USB A Wall Charger | Connects the pico to a wall outlet for power. | 100 SEK | N/A |  |
## Computer setup
1. To start off, micropython needs to be installed to the pico. This can be done by going to: https://www.raspberrypi.com/documentation/microcontrollers/micropython.html#what-is-micropython and downloading a UF2 file. The one for the "Raspberry Pi Pico W" is the one to use.

2. After that, the BOOTSEL button needs to be held down on the pico while plugging it in to the computer. This makes the pico enter USB Mass Storage Device mode. Then, the UF2 file is simply dragged into the virtual pico drive on the computer.

3. I chose visual studio code as my editor because I have previous experience with it. It's also very simple to use and lightweight. The only plugins that need to be installed are "MicroPico" and its dependencies: "Python", "IntelliCode", and "Pylance".

Plugin links:
- https://marketplace.visualstudio.com/items?itemName=paulober.pico-w-go
- https://marketplace.visualstudio.com/items?itemName=ms-python.python
- https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode
- https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance
4. Open a workspace folder of your choosing in visual studio code.

Then click "configure project", which is a part of the MicroPico plugin. This will create a ".micropico" file in the folder.

MicroPico commands can easily be found by clicking "all commands" in the MicroPico toolbar at the bottom.

5. When you want to upload code to the pico you can press "upload current file" or "upload project" in VS code. They are also a part of the MicroPico plugin.


There's also a virtual workspace where you can see what files are uploaded to the pico.

You may need to click "Toggle Pico-W-FS" in the MicroPico toolbar for the virtual workspace to appear.

6. To run code on the pico, you press the run-button in the MicroPico toolbar. This should not be confused with the built-in VScode run-button, which is not what you want to use.

## Eletronical connections

*Circuit visualization*
**Note that the sensors in the image do not match with the actual sensors! The DHT11 sensor is represented by a temperature sensor (blue), and the photoresistor is represented by an LED (pink).**
**Power-wires are red, ground-wires are black, data-wires are blue/pink.**
The pico is connected to the outer edge of the breadboard to allow for easy USB insertion. This placement leaves two free rows on each side for connections.
The `GND8` pin and the `3V3` pin are connected to the powerrail of the breadboard. The powerrail is then used to deliver power to the different components.
The DHT11 has a ground-wire connected to the negative powerrail, and also a power-wire connected to the positive powerrail. Finally, it has a data wire connected to the `GP27` pin of the pico. Ensure that the correct pins are used, they are different and serve a specific purpose. The pins in the image *should* match up with the actual DHT11 sensor, where the rightmost is the GND, the middle is the VCC, and the leftmost is the data.
One end of the photoresistor has a ground-wire connected to the negative powerrail. The other end is connected to the same column as the 4.7k ohm resistor. The resistor then has a power-wire from its other end, which connects to the positive powerrail. Finally, a data wire on the same column as the photoresistor and 4.7k ohm resistor is connected to the `GP26` pin of the pico. The resistor ensures that the reading from the photoresistor is accurate. The resistor doesn't have to be exactly 4.7k ohm, it should ideally be at the midpoint between 4-7k ohm, such as around 5.5k ohm.
### Development/Production setup considerations
This setup is only a development setup. Components can easily be moved around and it's easy to experiment. Other components can also easily be added and removed.
This should not be used in production though. If handled without care, components could get loose and break. If this were to be used in production, some kind of case needs to be used to ensure that the components do not move out of place. The wires would also ideally be soldered to ensure that they do not get loose.
## Platform
The platform used for this project is Ubidots. It's a good fit for this project because it's able to receive data in the form of variables and then display them in various ways, such as graphs and gauges. There are 3 variables used: light, humidity, and temperature. When data is read from the sensors, it is sent over the network and stored in the corresponding variables on Ubidots. The variables keep track of their previous values, which enables viewing histographic data.
A free subscription is used for this project, which works very well since there isn't much data to store. However, if the project was to be scaled this would not work well. Since every user needs to see separate data it would need to be organized into separate dashboards for every user, which would require a paid subscription. Ideally, all the setup would be automated using the Ubidots API to handle the creation of users, devices, and dashboards programmatically.
## Code
### boot.py
**Imports**
```python=
from network import WLAN, STA_IF
from utime import sleep
from machine import Pin
from keys import WIFI_SSID, WIFI_PASS
```
Note that "keys" is a separate file containing the Wi-Fi ID, and the Wi-Fi password.
**Wi-Fi connection**
```python=
LED.on()
wlan = WLAN(STA_IF)
if not wlan.isconnected():
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASS)
counter = 0
while not wlan.isconnected():
if counter > RETRY_DELAY:
wlan.active(False)
sleep(1)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASS)
counter = 0
sleep(1)
counter += 1
LED.off()
```
This code tries to connect to the Wi-Fi by using the "network" library. It uses the values from the "keys" file, and employs a retry mechanism. Without the retry mechanism, the pico sometimes gets stuck and needs to reboot in order to connect to the Wi-Fi. The retry-timer is set to 30 seconds. While the onboard LED is on, it indicates that the pico is trying to connect to the Wi-Fi.
### main.py
**Imports**
```python=
import uasyncio
from machine import Pin, ADC, reset
from dht import DHT11
from urequests import post
from gc import collect
from keys import TOKEN
```
Note that "keys" is a separate file containing the Ubidots API token.
**Global variables**
```python=
TEMP_HUMID_SENSOR = DHT11(Pin(27))
LIGHT_SENSOR = ADC(Pin(26))
LED = Pin("LED", Pin.OUT)
DEVICE_LABEL = "pico"
TEMPERATURE_LABEL = "temperature"
HUMIDITY_LABEL = "humidity"
LIGHT_LABEL = "light"
MAX_LIGHT = 65535 # The max value of the light sensor
MAX_GENERAL_RETRIES = 10 # The maximum number of retries before a reset
MAX_SEND_RETRIES = 4 # The maximum number of retries to send data
DELAY = 600000 # The sensor read interval in milliseconds
sensor_error = False # Confirms that data is being correctly read from sensors
# Confirms that data is being correctly sent for every sensor
send_data_dict = {
TEMPERATURE_LABEL: 0,
HUMIDITY_LABEL: 0,
LIGHT_LABEL: 0,
}
```
This code initializes some global variables that are needed.
The different pins connected to the sensors, and the onboard LED is assigned to variables.
Labels for the Ubidots API are also initialized.
Other variables like the max light reading the photoresistor and the delay between reading/sending data are initialized.
Finally, some error handling variables are initialized.
**Sensor reading**
```python=
async def read_sensor_data():
global sensor_error
while True:
try:
TEMP_HUMID_SENSOR.measure()
temperature = TEMP_HUMID_SENSOR.temperature()
humidity = TEMP_HUMID_SENSOR.humidity()
light = LIGHT_SENSOR.read_u16() # low value = high light | high value = low light
light_percentage = round((MAX_LIGHT - light) / MAX_LIGHT * 100, 2)
sensor_error = False
return [
(TEMPERATURE_LABEL, temperature),
(HUMIDITY_LABEL, humidity),
(LIGHT_LABEL, light_percentage)
]
except Exception:
sensor_error = True
collect()
await uasyncio.sleep(1)
```
This function is used to read data from the sensors. First it uses the methods from the "dht" library to get the temperature and humidity. Afterwards, it gets the light reading from the photoresistor and converts it into a percentage. Finally, it returns the gathered values.
The function also includes error handling, which notifies the user of the specific error by flashing the onboard LED at a specific rate. The LED handling in located in a different function and cannot be seen here.
**Data sending**
```python=
async def send_sensor_data(sensor_data):
for variable, value in sensor_data:
retries = 0
while True:
try:
url = "https://industrial.api.ubidots.com/api/v1.6/devices/" + DEVICE_LABEL
headers = {"X-Auth-Token": TOKEN, "Content-Type": "application/json"}
json = {variable: {"value": value}}
response = post(url=url, headers=headers, json=json)
response.close()
send_data_dict[variable] = 0
break
except Exception:
retries += 1
collect()
if retries > MAX_SEND_RETRIES:
send_data_dict[variable] = 1
break
await uasyncio.sleep(1)
```
This function is used to send the gathered sensor values to Ubidots. It takes the sensor data as an argument. For every variable (temperature, humidity, light), it tries to send the data to Ubidots via a POST request by utilizing the "urequest" library.
The function also includes error handling, which notifies the user of the specific error by flashing the onboard LED at a specific rate. The LED handling in located in a different function and cannot be seen here.
## Connectivity
Data is sent about every 10 minutes using Wi-Fi, leveraging the REST protocol. REST is a popular protocol for web services and APIs and operates over HTTP. The sensor data is sent by utilizing the Ubidots API via a HTTP request.
The reason data is sent about every 10 minutes instead of exactly every 10 minutes is because data from the sensors is sent one at a time. This means that there is a delay between when the first data is sent, and when the last data is sent. This is not noticable in the short-term, but in the long-term it can be noticed. This could be improved by sending all data from the different sensors simultaneously.
Wi-Fi was a good fit for this project because the device will most likely be located in the bedroom, or perhaps another room, which almost always has access to Wi-Fi. It will also most likely be connected to a wall-outlet, which means that power is not a significant concern.
## Data presentation

***Dashboard visualization***
The dashboard shows both historic data and the most recent values. In the graphs, the user can see exactly what value a variable had at a certain time. They can then see how this fluctuates over an extended period. Below the graphs, gauges or a thermometer shows the most recent calculated value for each respective variable. This setup makes it easy for the user to see both current values, and historic values.
Data is saved to the database around every 10 minutes as previously described, and is preserved for 1 month. A data preservation time of 1 month is more than ample time for this project since the time it takes for room climate changes to take effect is often a maximum of a few days.
## Final results

***Physical design***
The project went well, and I found it fun to learn about embedded devices, how they work and how they can be programmed. I had never done anything like this before, so I feel like I learned a lot.
There are some changes that could be made though:
- Currently, everything is done asynchronously on a single core, but utilizing both cores would be ideal for code simplicity. I tried the 2 core approach first, but it seems that there are some issues with either the "_thread" library, or the actual hardware, which unfortunately resulted in a bad approach.
- Sending data closer to every 10 minutes instead of approximately every 10 minutes would be ideal. This isn't particularly hard to implement and would result in more streamlined data.
- Implementing error handling for Wi-Fi disconnections. If the Wi-Fi disconnects while the device is running, a restart is required. This is because the the Wi-Fi handling is only done while booting.