Try   HackMD

Written by Joel Josefsson (jj224jr)


Table of Contents


Project Overview

The aim of this project is to expand the knowledge of IoT using the Firebase Realtime Database to access the data anywhere in the world.
This project features temperature and humidity measurements using a DHT11 module. The values are then transmitted to the database using WiFi. An android app then subscribes to the changes in the database and visualizes the data.

Time Estimation

The estimated time the project took to complete is presented in the table below.

Circuit Database MicroPython Android app Total
Hours 3 6 10 10 29

Objective

This project was chosen based on the interest in building something functional to use in the home which can be used with a smartphone anywhere in the world.

When you want to know the outside temperature it is common to go on your smartphone and look at some weather application which shows the temperature over a whole city. However, is that the actual temperature on my balcony? Hence, the purpose of this project is to get an accurate temperature and humidity reading on my balcony during the summer heat which can be viewed on my phone.

Bill of Materials (BOM)

The materials used in this project is presented in the table below:

Device Quantity Description Price/Link
Raspberry Pi Pico W
Raspberry Pi Pico W
1 Used to collect sensor data and transmit to the database. It features WiFi and the RP2040 chip. It has 26 GPIO pins and operates on 1.8-5.5V DC input power. 98 SEK
Electrokit
USB-A male - Micro-USB
usb
1 Used to connect the Raspberry Pi Pico W to the computer to upload code 39 SEK
Electrokit
Push Button
1 Used as a way to reset the board without pulling the cable. 9,22 SEK
Elfa
DHT11 Module
1 Used to measure the ambient temperature and humidity. It measures temperature using an NTC component and humidity using a resistive component. It operates at 3.3-5.5V DC with the accuracy of ±2°C for temperature and ±5%RH for the humidity. 49 SEK
Electrokit
Breadboard
1 Used to put the components together 69 SEK
Electrokit
Jumper Wires
7 Used to connect the components on the breadboard. 49 SEK
Electrokit
Android Phone (Optional) 1 Used to visualize the data. (An emulator can be used instead)

Computer setup

To program the Raspberry Pi Pico W we need an IDE. This project uses Thonny based on the simplicity of setting it up and saving the file to the microcontroller. The project is also utilizing Android Studio to make the android app.

Thonny

Thonny is a very simple IDE for Python and is very easy to setup for Raspberry Pi Pico W. Micropython can be flashed on the microcontroller directly from Thonny.

Installing Thonny

  1. Thonny is downloaded from their Github with instructions on which to download based on your operating system. Since This project uses windows we download thonny-4.1.1.exe
  2. Start the downloaded .exe file and press next.
  3. Accept the Licence Agreement and press next.
  4. Choose if you want to have a desktop icon and press next.
  5. Press install to start the installation.
  6. When the installation is complete click finish to close the window.

Setup

  1. Open Thonny and plug in the Raspberry Pi Pico W using the micro-USB to USB-A cable where the micro-USB plugs into the microcontroller and USB-A to the computer.
  2. Next press the Python interpreter option in Thonnys lower right corner and select Install MicroPython.
  3. A window appears with some information on how to install. The Target volume will be automatically selected to the appropriate volume as well as the MicroPython family. You only need to select the variant Raspberry Pi Pico W and then install. Then press close when the installation is done.
  4. Now you can select the Raspberry Pi Pico board in the interpreter and start coding

Using Thonny

In Thonny you can either save your python files on the computer or on the board. To save the file on the board simply select save and then a popup window lets you choose the Raspberry Pi Pico

save file

Then choose a name for you file ending in .py and press ok to save on the board.

The file is now saved as hello_world.py on your Raspberry Pi Pico W and can be executed by pressing the play button at the top. The board is then reset and the words "Hello World" is printed in the REPL.

If you want the code to run when the microcontroller gets plugged in without pressing the play button name it main.py. You can also have a boot.py file which is the first file to run when plugged in. This is usually for connecting to the network and such.

Android Studio

Android Studio is used to make the app for the android phone.

To install Android Studio, they have very good instruction with a video on how to do the installation steps. It also have instructions for several different operating systems. Furthermore, to create a virtual device Android studio have great tutorial on how to create a android virtual device to debug your code on.

Firebase

Firebase is googles own cloud database service. Hence, first you will need to have a google account and sign in to it. Next, to create a Realtime Database, Firebase have a good tutorial on how to set up the database. They also have a tutorial on how to set up Firebase for your app in Android Studio on Firebase.

Putting everything together

Circuit

The circuit of the components are illustrated below.

Circuit

Power and Ground

We make one 3.3V line from pin 36 on Raspberry Pi Pico W to the row closest to the blue line and one ground line from pin 38 to the row closest to the red line.

DHT11

The DHT11 module have the pins signal, power and ground respectively from left to right. Hence, the signal pin is connected to the GPIO pin 16 on our microcontroller, ground is connected to the ground line and the power line is connected to the 3.3V power line which will be sufficient to power the DHT11. Since the module already features a pull up resistor there is no need to add an extra resistor between the signal and power line. The DHT11 also draws 2.5mA at the maximum which is significantly lower than the limit of our microcontroller, so no need for external power.

Button

The button is connected with the left side to ground and right side to GPIO pin 17. The internal circuit of the Raspberry Pi Pico W already have a pull up resistor so no need for that. One not when connecting a button is on which pins to connect. A button is basically two circuits which connects when the button is pressed. One circuit on the two right pins and one circuit on the two left pins. Hence, when connecting the button make sure to connect the ground and signal to two different internal circuits, otherwise it will constantly be short circuited and read the same value regardless of the state of the button. When the button is not pressed no signal is carried to the GPIO pin and the pull up resistor will pull up the signal to 1. When the button is pressed the circuit is short circuited and the resulting signal to the pin is 0.

Platform

The platform used for this project is Firebase Realtime Database which is a NoSQL cloud database synced in realtime across all connected devices. The data is stored as JSON. The free account can have 100 connections, 1 GB storage and 360 MB Downloads per day which is more than enough for this project. You can either update a variable to have sort of MQTT functionality but you can also save lots of data in order to make a dataset or to have the data to present in a graph.

The code

The main code for the microcontroller and for the android app is presented here.

Microcontroller

The library to connect and push data to the Firebase database is ufirebase downloaded from Github and saved to the microcontroller under the lib folder.

Secrets

The first step is to make a file where all the secrets are held such as the WiFi information as is structured as such:

SSID = "Your SSID" WIFIPASS = "Your WiFi password" FIREBASEURL = "URL to the Firebase Realtime Database"

and save it as secrets.py on the microcontroller

Connecting to WiFi

First off we need to connect to the WiFi in our boot.py file. We import the necessary libraries

from machine import Pin from time import sleep_ms import network import secrets

The Pin class is used to blink the on board LED to have visual feedback when connecting. The sleep_ms is used when waiting on connecting. The network is used to connect to the WiFi and secrets are our secrets file made before.

Next we define a variable led to refer to the on board LED:

led = Pin('LED', Pin.OUT)

Then to connect to the network:

sta_if = network.WLAN(network.STA_IF) if not sta_if.isconnected(): sta_if.active(True) sta_if.connect(secrets.SSID, secrets.WIFIPASS) while not sta_if.isconnected(): led.toggle() sleep_ms(200) led.off()

First off we make a WLAN client which is then checked if already connected. If it is not we make the network interface active and connect using the SSID and password of the router. Next while the board is connected the on board led is blinking every 0.2s to indicate that it is trying to connect.

Main program

The ufirebase is the library downloaded to access Firebase Realtime Database. It has the function to connect to the database, get data and put data from Firebase. We import it and give it the name firebase To define the URL to the firebase to set it up we use firebase.setURL() as such:

import secrets import lib.ufirebase as firebase firebase.setURL(secrets.FIREBASEURL)

We also need to define the variables refering to the DHT11 and the button. It is done with the Pin class from the machine library. The DHT11 is defined using the DHT11 class from the dht library.

from machine import Pin from dht import DHT11 d = DHT11(Pin(16)) button = Pin(17, Pin.IN, Pin.PULL_UP)

To have our microcontroller take maesurements every hour and post them to the database we use a timer defined in the machine library. The timer is set to periodic with a period of one hour meaning that the timer will restart after it runs out every hour. The callback function of the timer takes the measurements using the d variable and post them in the database under the respective key using firebase.put(). They are saved as strings as it is easier to work with in android studio later.

from machine import Timer def timer_handler(t): d.measure() temp = d.temperature() hum = d.humidity() print(temp) print(hum) firebase.put("Temperature", str(temp), bg=0) firebase.put("Humidity", str(hum), bg=0) t = Timer() t.init(mode=Timer.PERIODIC, period=3600000, callback=timer_handler)

Finally we use an IRQ on the button to instantly reset the board when pressed. The variable button is set to trigger on a falling edge with the function to reset the board as handler. The button is triggered on the falling edge since the pull up resistor in the board always makes the button read as 1 and when we press the button it reads 0.

from machine import reset def reset_button_handler(pin): print("Resetting Machine") reset() button.irq(trigger=Pin.IRQ_FALLING, handler=reset_button_handler)

Android app

App Design

The layout of the app is made using jetpack composables. The function Card(){} is used to present each of the measurements. Inside the function Text() is used to present the data.

@Composable fun cardLayout(title: String, mainText: String, CardColor: Color, modifier: Modifier = Modifier) { Card(modifier = modifier .padding(16.dp), colors = CardDefaults.cardColors(containerColor = CardColor), elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)) { Text(text = title, textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp)) Text(text = mainText, fontSize = 50.sp, textAlign = TextAlign.Center, fontWeight = FontWeight.Bold, modifier = Modifier.fillMaxWidth().padding(start = 16.dp)) } }

To have the data in our cards update with the database Livedata<T> is used which is observed in the composable as a state.

@Composable fun appLayout(CurrentHum:LiveData<String>, CurrentTemp:LiveData<String>, modifier: Modifier = Modifier){ val CurrentHumidity: String? by CurrentHum.observeAsState() val CurrentTemperature: String? by CurrentTemp.observeAsState() }

Recieving data from Firebase

In the class MainActivity three constructors are defined, two for the data from the database using MutableLiveData<T> and one refering to the database.

class MainActivity : ComponentActivity() { private val tempData = MutableLiveData<String>() private val humidData = MutableLiveData<String>() private val database = Firebase.database

In the class, a function readData() takes the database constructor and defines two references, one for the path to the temperature data and one for the path to the humidity data.

private fun readData(){ val tempRef = database.getReference("Temperature") val humRef = database.getReference("Humidity")

Then two eventlisteners are made, one for temperature and one for humidity. They are exactly the same except they post to the data to tempData for temperature and humData for humidity. When new data is posted to the database the eventlistener will be triggered and get the key and the value of the changed data which will be posted to the respective MutableLiveData<T> variable.

val tempPostListener = object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { Log.i("firebase", "Number of messages: ${snapshot.childrenCount}") val post = snapshot.getValue<String>() tempData.postValue(post) } override fun onCancelled(error: DatabaseError) { Log.e("firebase", "loadPost:onCancelled", error.toException()) } }

Transmitting data

The data from the DHT11 is transmitted to Firebase Realtime Database using WiFi once every hour in order to minimize the power consumption but also since the temperature outside does not often change instantly. The transport protocol used to send data to the database is TCP protocol using sockets.

Presenting the data


The data is saved to the database once every hour and stays there until it is removed.

Finalizing the design

The database looks like this when done:


There are only two keys, Humidity and Temperature which is all we need for this project. However, it can be further expanded in the future to include past data which can serve as a database for an machine learning model or to have displayed in the app.