
Pico W, photo taken by author, free to use.
Author: Caroline Ellbäck, ce222hj Linneaus University (July 2023).
**Summary**: This is a tutorial on how to build an indoor temperature and humidity monitor. The hardware essentials of this project includes a DHT11 sensor, LCD 1602 screen, multi-color LED with built-in resistors and a small case fan mounted on a Raspberry Pi Pico W. The software publishes to Adafruit including webhooks separately and the rest printing on the screen, LED and synchronizing the Pico's built-in RTC to a global NTP when connecting to the Internet. The small fan is included to keep the humidity away from the electronics.
Time approximation to create this project is 8-10 hours, depending on previous knowledges and skills.
:black_square_for_stop: **OBS! Due to educational purposes, only temperature is viewed as trigger throughout the tutorial. It is easier to control temperature than humidity, and easier to demonstrate.**
# Objective
The project was chosen to measure temperature and humidity in bathrooms, to prevent mold and have a healthier indoor environment. Is it more effective to open a window compared to just let the indoor ventilation do the job during a shower?
# Materials
Materials in this project:
| Device | Description | Store and prize |
| -------- | -------- | -------- |
| | Raspberry Pi Pico W, got it pre-soldered due to WH shortages. It is a powerful small device that can be programmed with Micropython, CircuitPython and C/C++. 30 GPIO pins, 4 of which can be used as analog inputs, 2 UARTs, 2 SPI controllers, 2 I2C controllers, 16 PWM channels, DMA controller, 2 cores and much more, but most importantly able to connect to 2.5GHz WiFi! Raspberry Pi Pico WH is to prefer if you dont want to soldered it yourself, cost a little bit more but not much.| Elecrokit 98kr(W), 109kr(WH) |
| RGB LED, multi-color, with built-in resistors. Able to be programmed with color charts/codes.| Electrokit 19kr
|DHT11, temperature and humidity sensor.| Electrokit 49kr
|CPU case fan for Raspberry Pi 4, case included.| Electrokit 69kr
|Jumper wires, male-male, 100mm| Electrokit 39kr
|Jumper wires, female-male, 150mm|Electrokit 29kr
|Breadboard 840 connections|Electrokit 69kr
|I2C LCD screen 1602, able to display 2 lines of characters, 16 each. Integrated conversion circuit back on the screen with only 4 pins, which is important if you dont want to solder 16 pins on the screen yourself or buy conversion circuit beside.|Amazon, Freenove 99kr
|Acrylic plastic, to cut and glue your own case, if you dont have a 3D-printer. *Tip*: Picture frames that are not having a glass cover, is also having an acrylic plastic cover.|Amazon 172kr
|Hot glue gun including 30 sticks.|Amazon 107kr
# Computer setup
To get started, Node.js needs to be installed on the computer ([link here](https://nodejs.org/en)) and then Visual Studio Code (aka vscode, [link here](https://code.visualstudio.com/Download)). Vscode was chosen because it's widely used and has many features built-in already. Vscode also supports the programming language Micropython which was used for this project. Another known IDE in this area is Thonny [more info here](https://thonny.org/), which can be chosen instead of vscode depending on preferences. Thonny is simplier built, reminds a lot of Python IDLE, and has clearer design than vscode. Vscode also has a special built-in mode in Micropython called "Development mode" which saves to the Pico as soon as any changes are made and tab functions, which are a huge advantages compared to Thonny.
Pymakr was installed and setup in vscode, a short tutorial on this is found [here](https://docs.pycom.io/gettingstarted/software/vscode/). Then lastly, the Pico needs to be flashed via UF2 bootloader to be prepared for Micropython, a short tutorial is found [here](https://micropython.org/download/rp2-pico-w/). *Obs!* Dont load the Pico with "Nightly builds" UF2 files, use the latest release.
*Other tips and tricks*:
* If the Pico is behaving weirdly or things stops functioning or for some reason is not behaving as it should, "nuke" the Pico is an option. It is a special UF2 file called *flash_nuke.uf2* ([link here](https://learn.adafruit.com/getting-started-with-raspberry-pi-pico-circuitpython/circuitpython)) that's overwriting the whole flash memory of the Pico, then the regular UF2 bootloader file needs to be loaded back again as previous steps. **Save your code in a text file before proceeding this**.
* If the Pico is starting up in "safe boot" in vscode, it is impossible to work with it. Erasing all data with the nuke-file will not solve the problem. Check settings in vscode according to *fig. 1* below:

*Fig. 1, Settings in Visual Studio Code.* Ctrl+Shift+P to open the Command Palette in vscode, type "settings" and select "Preferences: Open Settings (UI)" > "Extensions" > "Pymakr" > "Misc" and check the Values. If any Value says anything with safe boot, it needs to be changed. Compare settings with the *fig. 1* above.
* If vscode doesnt find a file in the same project that is referenced in the main.py-file, try to start/save the code from that file (or all files) or unplugg/plugg the Pico or restart vscode.
* Lastly, *always* do backups of the code during your journey. Even send it to your own email to be double-safe. Notepad works perfectly, takes little space, easy to send and open everywhere without special programs or other programs interferring and no auto-correction.
# Putting everything together
A quick walkthrough what the purpose of each electronic components for this project and how they interact with each other:
* The DHT11 collects temperature(Celsius) and humidity(%) and store it at Pico.
* The LED changes its color depending on temperature.
* The LCD screen changes its messages depending on temperature. It is showing time and date along with temperature, humidity and a short message.
* A case to protect the equipment, made of transparent Acrylic plastic and hot glue.
* The fan is on as soon as the Pico is powered up, to keep humidity away from the electronics inside. The case has a small opening for the cords and the DHT11 sensor is outside of the case. The fan is blowing towards the opening, we do not want any humidity or condensation to be trapped inside the case. An opening is also good to keep the equipment airy and cool.
* Temperature and humidity are published to Adafruit every minute, to visualize the data for longer periods and how fast humidity/temperature drops. Adafruit sends webhooks depending on requirements to Discord.
Circuit diagram for this project, a pinout scheme for Raspberry Pi Pico W is [found here](https://datasheets.raspberrypi.com/pico/Pico-R3-A4-Pinout.pdf).

*Fig. 2*, a circuit diagram showing the components' connections.
Place the Pico all way down into the breadboard. Connect the DHT11 sensor with signal(s) to GPIO22, power(VCC) to GPIO36 and ground(-) to a GND. Connect the LCD screen with power(VDD) to GPIO39, the ground to GND, the serial data pin(SDA) to GPIO0, and the clock pin(SCL) to GPIO1. The multi-color RGB LED's pin ground(black) to a GND, red pin to GPIO10, green pin to GPIO11 and blue pin to GPIO12. The fan's power pin(red) connects to GPIO39, ground(black) to GND and UART transmit pin TX/TXD(blue) to GPIO0 or leave it untouched, it is not needed for this project.
*Other tips and tricks*:
* The pico only has a set of each UART TX(transmit) and UART RX(receive) by default located on GPIO0-1. It is possible to move these pins by software to other marked pins that can be found on the pinout scheme. These other marked pins dont have UART enabled by default.
* Do not ever mix power and ground, it will burn the sensor. In case of uncertainty, check pinout scheme and/or check the manufacture's manual, or simply contact them. E.g. the DHT11 sensor has different pins and some cases even four pins. It is totally up to the manufacturer which one is what. Double-check everything before turning on power. This applies to LCD screens, other sensors too etc.
* Always try power up components with 3.3V(GPIO36) first, this pin is also programmable. Most components works with 3.3V. In this case, the LCD screen needed 5V(GPIO39) to function, even the fan required 5V. The 5V GPIO39 is not programmable.
* All LEDs require resistors, otherwise they will just pull as much power as possible, and it will damage the Pico long term. In this case, resistors are built-in for each pin/color.
# The Code
Not all code is built-in in Micropython, for that importing libraries(or creating your own) is necessary. The DHT11 sensor library is built-in already and easy to reach with **import dht**. The LCD screen needs two libraries found [here](https://github.com/T-622/RPI-PICO-I2C-LCD), and for more advanced libraries to screens are found [here](https://github.com/dhylands/python_lcd/tree/master/lcd). The libraries I used are based on those more advanced libraries. More LCD information and tutorials are found [here](https://www.freva.com/how-to-connect-an-lcd-display-to-a-raspberry-pi-pico/) and [here](https://github.com/T-622/RPI-PICO-I2C-LCD/blob/main/README.md). For a screen, an I2C address is needed and can be found on the manufactures' site or manual, otherwise it is easy to find it with some code [here](https://www.freva.com/find-out-an-i2c-address-with-the-raspberry-pi-pico/).

*Fig. 3, the libraries used for this project. The library "secrets" contains all sensitive credentials such as usernames, passwords etc. The boot library is the first thing that starts up when the Pico powers up. The main library is where all the running code is placed and calling other libraries when needed. The lcdapi, picoi2clcd, ntp_config and mqtt libraries are all gathered from the links provided above. Best practice is to place all libraries inside its own lib folder, but for such a small project, it was placed as is like the figure is showing.*

*Fig. 4, imported libraries in main, both built-in and created libraries.*
Credentials are placed before the functions in the code, so the functions know where to get its credentials from.

*Fig. 5, the DHT11 and the LCD screens credentials, calling from their libraries.*
The functions are placed after the credentials in the code, here is a short snippet of a very big code block, showing the function screen() that conatins code to display temperature, humidity, time and date on the LCD and REPL, and also code for the multi-color LED.

*Fig. 6, a part of the code that shows how the code interacts together as whole, with the screen, LED and inside the async def() module.*
Here is a short snippet of the end of the big code block, showing how the LED is changing color and the LCD's loops depending on temperature.

*Fig. 7, a part of the code showing how the LED is programmed with different color codes depending on what temperature it is.*
Raspberry Pi Pico W, where W stands for wireless, means that with software the Pico is able to connect to the 2.4GHz WiFi without any additional device or antenna. The software to WiFi connection is from a library that is found [here](https://github.com/iot-lnu/applied-iot/blob/master/Raspberry%20Pi%20Pico%20(W)%20Micropython/network-examples/N2_WiFi_MQTT_Webhook_Adafruit/lib/wifiConnection.py). A library for WiFi and other sensitive credentials is needed as well, a good structure is found [here](https://github.com/iot-lnu/applied-iot/blob/master/Raspberry%20Pi%20Pico%20(W)%20Micropython/network-examples/N2_WiFi_MQTT_Webhook_Adafruit/lib/keys.py). In this project, the WiFi connection was placed in boot library to start up as soon as the Pico is powered on and the sensitive credentials were placed in the secrets library.
A library is needed to sync Pico's built-in RTC with an NTP when connecting to WiFi, and that library is found [here](https://github.com/LutzEmbeddedTec/Pico_w_RTC). Other useful resources about RTC and NTP are these two YouTube videos
[RTC and NTP](https://www.youtube.com/watch?v=fItvY5JIvo8) and [RTC, NTP and WIFI](https://www.youtube.com/watch?v=fqsOZ_FVgfg), the videos have helped me a lot. The original plan was to connect an RTC device called RTC DS3231 to the Pico. That would have involved a battery and more resources than needed for this project. For outside or long distances projects without Internet connection I would recommend DS3231, otherwise it is better to use software. Battery fumes without proper ventilation is not healthy and the less material the better for the environment. I tried to connect the RTC DS3231 but got stuck at conflicts at the I2C bus on the Pico, after troubleshooting and looking for solutions I found out that it is not necessary to have a DS3231, it is possible to set Pico's RTC with software and WiFi connections, which turned out to be a better solution for my project. Another example is the built-in resistors on the RGB LED, less space and less material, more software and better for the environment. In this project, a global NTP was used to just show the time on the LCD, but for more accuracy it is better to use an NTP close as possible if timing is a necessity.

*Fig. 8, simple code made of slicing, if, elif and else statements. In the code, it is possible to choose (with the help of two dicts) the date to be displayed as a text or as numbers. In my project I went on with numbers. The raw data from the NTP library is returned in a big tuple displaying (year, month, date, day of the week starting at 0=monday, hour, minutes, seconds, subseconds).*
To the multi-color RGB LED, a color chart was used to set the code of the colors, the tool can be found [here](https://www.rapidtables.com/web/color/RGB_Color.html). An inspiring and beginner-friendly video about how to code a multi-color LED is found [here](https://www.youtube.com/watch?v=9hxMxZGVc5w). For more information about PWM and Micropython is found [here](https://randomnerdtutorials.com/esp32-esp8266-pwm-micropython/). I tried to program the LED to flash when some specific conditions were not met, but the LED is quite slow turning on/off, which made it miss some flashing sometimes or just very little flash. I also tried mixing color-codes, like sending the color orange to the LED. The LED made an attempt to show orange but it was not the orange I expected and too weak.I decided then to just go with the regular red, green and blue color codes to the LED and no flashing.

*Fig. 9, the multi-color LED's function for getting the correct color.*
To be able to publish values to Adafruit, a library for MQTT is needed and found [here](https://github.com/iot-lnu/applied-iot/blob/master/Pycom%20Micropython%20(esp32)/network-examples/mqtt_ubidots/mqtt.py). For testing purposes with Adafruit, [this code](https://github.com/iot-lnu/applied-iot/blob/master/Raspberry%20Pi%20Pico%20(W)%20Micropython/network-examples/N2_WiFi_MQTT_Webhook_Adafruit/main.py) is a good start.

*Fig. 10, Adafruits credentials and a function to publish the data to the server. Sensistive credentials are stored in a library called "secrets" and called from there.*
With the built-in module **uasyncio** in micropython, it is possible to handle several code functions at the same time independently. It is a type of task scheduler. With the **await uasyncio.sleep()** function it is possible to pause an infinity loop and open up for other instructions outside to take place. The await function is set to pause in seconds, then it continues with the rest of the code. The **async def main()** function is prioritized in the queue. All code is operating on the Pico's first core with a queue system that is handled by uasyncio. Another module was tested at first, the **_thread** module. This module separates task on different cores. The Pico has two cores which means it is possible to divide tasks between the cores, which is also more efficient. The problem encountered with the _thread module in this case was that it is only possible to stop the first core with commands. The other core will only stop if the power stops, but continues as soon as the power is back on. In this project, uasyncio was used with the publishing function to Adafruit placed in async def main() function while the LCD and LED code were placed in another function called async def screen(). Publishing was placed in the main function to have first priority when called in the task scheduler's queue.

*Fig. 11, showing code snippets with the beginning and the end of **async def**. The "send_data()" function, which can be seen at the end of Fig. 10 above, is set to run and send data once every minute to Adafruit through **await uasyncio.sleep_ms(60000)**.*
# Transmitting the data / connectivity
The platform Adafruit with webhooks was chosen for this project. It is cloud based, beginner-friendly, free and very easy to manage. This platform fits perfectly for small projects and this project. The free version allows 5 feeds and 30 messages per minute, but it is upgradable for a fee. Less programming skills are needed to use Adafruit. I wanted a free service for my project. As shown in the figure 10 above, it is set to publish the code with MQTT every minute to Adafruit. From Adafruit, webhooks are sent as a message when reaching a certain temperature or humidity to Discord.
My initial idea was to implement LoRaWAN with the TTN network, unfortunately I was too far away to the nearest gateway. With Helium I could have had coverage but did not want to have a service to pay for, even if Helium is a very cheap option. In my opinion, Helium is not good for experimental stages to beginners, especially for small indoor projects. The credits are running out fast if not done properly. This project is aimed for indoor use only, therefore was WiFi a good solution too and easy to implement. The TTN and Helium use lower bandwidth than WiFi, which is good for long distances and use very little resources. If a project is aimed for outdoor-usage with long distances and require low energy consumtption (as in a battery) LoRaWAN is definitely a better choice. It is possible to buy your own TTG (the Tings Gateway) router for TTN usage to extend TTN both to yourself and others.
# Presenting the data
The project's dashboard in Adafruit looks like this:

*Fig. 12, the dashboard in Adafruit, with both line charts and gauges for temperature and humidity data.*
The data is published and saved to Adafruit every minute when the device is powered on. Each feed in Adafruit stores the data for 30 days. The automation/triggers are the LED changing color depending on the temperature, the LCD screen showing warning texts along with showing temperature, date and time, and webhooks warnings to Discord.
Mosquitto with Node-Red is another more advanced solution that was tested. There are a lot of good tutorials for installing and manage Mosquitto and Node-Red with Windows 10 without the need of a virtual machine. Windows is unfortunately known for being a bit difficult compared to Linux, especially with diffuse permissions. Windows 11(used with this project) is even more difficult to manage compared to Windows 10. Win11 hides a lot of functions that are visible in win10, and also internally with updates etc. win11 works very differently than win10 while digging deeper. The first problem was permissions in the Mosquitto folder, but changing permission settings for the folder made that work. Another solution could have been running the Administrator account from the beginning. Then the last problem, vscode was not able to contact Mosquitto, but Mosquitto was connected to Node-Red and it was reachable from the web browser. All tests were fine, except running netstat in command prompt showed 127.0.0.1:1883, when it was supposed to show 0.0.0.0:1883. After searching and trying out less secure settings in the mosquitto.conf that seem to work for others did not work with my computer. At this point, I had Adafruit up and running already, and went with that instead to finish this project on time. A future solution could be using a virtual machine, I just wanted to try out installing directly on a Windows machine, it seemed rare but still doable and exciting. But manage Mosquitto require more skills with how to handle configuration files, it is not beginner-friendly nor Windows-friendly in my opinion. The firewall could interfer with Mosquitto too, here I tried to open up port 1883 in all directions, even turning off the whole firewall, unfortunately without any luck. Mosquitto is also PATH sensitive, just like the programming language Python. Node-Red was pretty easy to install, add libraries to it and also to make it work. It gives feedback instantly if it is connected properly. There are a lot of documentation available online for Node-Red to all operating systems.
# Finalizing the design
*Figure 13 and 14* beneath shows how the device looks like at its finished state, with the handmade case placed on top. There are also link to a video showing how it is working further down.

*Fig. 13, the final result with the case placed on top.*

*Fig. 14, close-up on the final project without the case.*
### Final thoughts
The project went very well, I was a total beginner when starting this course at LNU. I had seen a regular Raspberry Pi before but that was it, I didn't even know breadboards existed. In the beginning the learning curve was quite steep, I am proud that I managed most of the hurdles I encountered on my own. I enrolled to this course to learn more about IoT, to apply the knowledge in my future IT career. I have gained A LOT of invalued knowledge! I had just finished a basic programming course in Python two days prior this course there I also wanted to expand my knowledge which I really did with this course! I am so grateful for the knowledge I have gained, to such kind teachers and students in this course that have helped me during my journey.
My project is highly scalable, which makes it easier for someone to develop further and make an even better project in the future! Examples would be to control the fan by hand(a button) or by code, maybe control a big bathroom fan and be able to turn it on/off by phone or set timers for it. Perhaps make your own bathroom fan too? I tried making code to control the fan, to powered it on only at certain requirements. This fan is made for Raspberry Pi's CPUs, it is a CPU fan. With that said, it is able to control several of these fans with the regular Raspberry Pi by turning on/off GPIOs depending on the CPUs internal temperature. I tried to translate the code into the Pico's version, example you can not control GPIO the same way as with RPi. Unfortunately I got the fan quite late and with lack of time I needed to possible to make it work with the fan, I had to put that adventure down after a while. The fan is only able to work properly with 5V, which at Pico is not a programmable pin as with the 3.3V. Maybe adding a transistor 2N2222 and some code could make it work, I tried the 2N2222 transistor the last minute but was still unfortunate. Otherwise develop and make this project sufficients on its own by adding a battery and apply the LoRaWAN to long distances, to your summer house? Solar-energy? Use a different screen, add cursors to it etc. With a bigger and better screen and a 3D-printer, you could do a really nice and professional device! Show what kind of weather is outside, add weather APIs. Using the DHT22 that has more accuracy would have been better too, it also measures air pressure! Which is exciting too!
:star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2::star2:
:film_projector: Link to a video showing the device in **action** is found :point_right: [here](https://www.veed.io/view/9599474c-8556-42ec-8fcd-47322dfcfee0?panel=showcase)! :point_left:
*Obs! The video is muted.*
**A tutorial is not good enough
without an old-fashioned GIF,
here you go** :sunglasses:
