# Coffee Temperature Monitor Using Raspberry Pi Pico WH (prototype)
*Emil Olsson, eo223mw*
*This project is a part of the course [Applied Internet of Things](https://lnu.se/en/education/contract-education/utveckla-dig-sjalv/digitalisering-och-it/applied-internet-of-things/) at Linnaeus University*
## Short Project Overview
This project involves building a temperature monitor for hot beverages using a Raspberry Pi Pico WH, a DHT11 sensor for room temperature and humidity, and a MCP9700 sensor for hot beverage temperature. The system sends data to a Node.js server, which displays the data in real-time on a web interface using Chart.js.
**Estimated Time to Complete:** 5-6 hours
### Why This Project?
As a Swede and a programmer, coffee is an essential part of my daily routine. However, I noticed I have a tendency to forget about my coffee when working. The purpose of this project is to create a practical IoT device that monitors the temperature of a coffee cup and provides real-time data visualization. This can be useful in everyday life to ensure your coffee stays at the perfect temperature for drinking.
### Purpose
The device tracks the temperature of your hot beverage, room temperature, and humidity levels. Its aim is to remind users to drink their beverage while it's at the ideal temperature.
### Insights
- Learn about integrating multiple sensors with Raspberry Pi Pico and visualizing data in real-time.
## Bill of Materials
| **Item** | **Specifications** | **Cost** | **Where to Buy** |
|-----------------------------------|---------------------------------------------------------|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Raspberry Pi Pico WH** | Microcontroller board with Wi-Fi and Bluetooth | 9.59€ ($10.32) | [Electro:kit](https://www.electrokit.com/en/raspberry-pi-pico-wh) |
| **Solderless Breadboard 840 tie-points** (optional) | Standard breadboard | 4,91€ ($5,28) | [Electro:kit](https://www.electrokit.com/en/kopplingsdack-840-anslutningar) |
| **USB cable A-male to micro B-male 1.8m** | USB cable for connecting Pico WH to PC | 2,78€ ($2,98) | [Electro:kit](https://www.electrokit.com/usb-kabel-a-hane-micro-b-5p-hane-1.8m) |
| **DHT11 Temperature and Humidity Sensor** | Digital sensor for measuring temperature and humidity | 3,49€ ($3,75) | [Electro:kit](https://www.electrokit.com/en/digital-temperatur-och-fuktsensor-dht11) |
| **MCP9700 Temperature Sensor** | Analog temperature sensor | 0,85€ ($0,92) | [Electro:kit](https://www.electrokit.com/en/mcp9700-to-92-temperaturgivare) |
| **Push button PCB 3mm black** | Tact switch for PCB mounting with black plastic button | 0,46€ ($0,50) | [Electro:kit](https://www.electrokit.com/en/tryckknapp-pcb-6x6x6mm-svart) |
| **LED 5mm red diffuse 1500mcd** | Red LED | 0,36€ ($0,38) | [Electro:kit](https://www.electrokit.com/en/led-5mm-rod-diffus-1500mcd) |
| **Resistor 0.25W 330ohm (330R)** | | 0,076€ ($0,081) | [Electro:kit](https://www.electrokit.com/motstand-kolfilm-0.25w-330ohm-330r) |
| **Wires** | | | |
| **Tape** | | | |
| **Cardboard** | | | |
| **Lid of old snusbox*** | | | |
Note: All parts used except "Push button PCB 3mm black" are from the [Start Kit - Applied IoT at Linnaeus University (2024)](https://www.electrokit.com/lnu-starter)
*Note: I do not promote the use of snus
## Computer Setup
### Chosen IDE
- **VSCode**: Visual Studio Code with Pymakr extension for MicroPython development.
### Steps to Set Up the Computer
1. **Install VSCode**: Download and install [VSCode](https://code.visualstudio.com/).
2. **Install Pymakr Extension**: Go to Extensions in VSCode, search for Pymakr, and install it.
3. **Install Node.js**: Download and install [Node.js](https://nodejs.org/).
4. **Setup Pico WH**:
- **Drivers**: Install the required USB drivers for the Pico WH.
- **Flashing MicroPython**: Follow the [official guide](https://www.raspberrypi.org/documentation/microcontrollers/micropython.html) to flash MicroPython firmware onto the Pico WH.
## Putting Everything Together
### Circuit Diagram

**Figure 1: Schematic diagram of the connections**
#### 1. Raspberry Pi Pico WH
- The Pico WH (1) is installed on the breadboard (2) in the slots 1-20. This is for easier access to the micro-USB port on the Pico.
- Connect one wire from the 3V3 pin on the Pico to the positive outer right lane of the breadboard. This will power that entire lane.
- Connect one wire from any GND pin on the Pico to the negative outer lane.
#### 2. Breadboard
- The wires on the bottom of the diagram are for bridging the outer lanes to ground and power.
#### 3. Push button
- The push button is connected to 3V3 (red wire) to the positive outer lane for power. It is then connected to a GPIO pin (brown wire) on the Pico for sending data. In this example it is the pin GP14.
#### 4. MCP9700
- The MCP9700 is connected to 3V3 (red) and ground (black) on the outer lanes of the breadboard and to GP26 for data transfer.
#### 5. DHT11
- Similarly to the MCP9700, the DHT11 is connected to 3V3 and ground on the outer lanes of the breadboard. It is then connected to GP6 for datatransfer.
#### 6. LED
- The anode (long wire) of the red LED is connected to row 30 of the breadboard and the cathode in row 29. From row 30 a (blue) wire goes to GP16. Then a resistor is connected from row 29 over the middle gap of the breadboard to row 29. Then a wire is connected from row 29 to the ground lane.
### Electrical Calculations
**Voltage and Current Requirements**
To ensure the proper functioning of all components, it is important to meet their voltage and current requirements. Here are the details for each component used in this project:
- Raspberry Pi Pico WH
- Voltage: 3.3V for GPIO pins
- Current: Ensure the power supply can provide enough current for the Pico and all connected sensors and LEDs.
- MCP9700 Temperature Sensor
- Voltage: 2.3V to 5.5V
- Current: 6 μA (very low current, negligible in total power budget)
- DHT11 Temperature and Humidity Sensor
- Voltage: 3.3V to 5V
- Current: 0.3 mA (average), 1-2.5 mA (during measurement)
- LEDs
- Voltage: Typically around 2V (depends on color)
- Current: 10-25 mA (limit to 20 mA using appropriate resistors)
- Resistors
- Resistors are used to limit the current flowing through the LEDs to prevent them from burning out. In this project, a 330 ohm resistor is used for this purpose.
Using Ohm's Law:
`𝑉 = 𝐼𝑅`
Where:
𝑉 is the voltage drop across the resistor
𝐼 is the desired current through the LED (typically 10-20 mA)
𝑅 is the resistance
For example, if using a red LED with a forward voltage of 2V and a 330 ohm resistor:
```
V resistor = V supply − V LED = 3.3V−2V = 1.3V
I = V resistor/R = 1.3V/330Ω ≈ 0.00394A = 3.94mA
```
With a 330 ohm resistor, the current through the LED will be approximately 3.94 mA, which is safe for typical LEDs.
### Battery Consumption
Given the project's setup it is most likely for having power via USB from a nearby computer. Therefore, battery consumption would not be an issue. The Raspberry Pi Pico WH, along with the sensors and LED, operates at a low voltage of 3.3V. The MCP9700 and DHT11 sensors draw minimal current, making the setup energy efficient. However, for a battery-powered implementation, optimizing data transmission frequency and employing power-saving modes could extend battery life. For example, reducing the frequency of temperature updates or using a larger capacity battery could significantly enhance the operational duration.
#### Total Battery Consumption (estimate)
Total current
𝐼𝑡𝑜𝑡𝑎𝑙 = 30mA + 0.3mA + 0.006mA + 25mA
𝐼𝑡𝑜𝑡𝑎𝑙 ≈ 55.306mA
Power 𝑃 is calculated using the formula
𝑃=𝑉×𝐼, where:
𝑉 is the voltage
𝐼 is the current
Assuming the entire circuit operates at 3.3V (the supply voltage for most components):
𝑃𝑡𝑜𝑡𝑎𝑙=3.3𝑉×55.306𝑚𝐴
𝑃𝑡𝑜𝑡𝑎𝑙≈182.51𝑚𝑊
## The Coaster
I wanted some sort of platform to place my cup. On this platform I needed:
1. Some sort of coaster to place my cup
2. a method to attach my MCP9700 sensor to read the temperature
3. A base to attach my push button
I started with the base. I used old cardboard as it was readily available to me. I cut out a rectangle which I then taped my button to. On each side of the button I then taped two longer pieces of folded cardboard to function as my "springs" so that my cup wouldn't immediately tip over.

*Image 1: Cardboard baseplate, springs and push button*
During testing I noticed that my "springs" were exactly the correct height for pushing in the button if there was liquid in the cup. This is not a viable solution as different cups and mugs have different weights, so a lighter cup might not have worked. This is something I've considered and written about in [Future Work](#Future-Work).
I have the bad habit of using snus, but for this project I realized I could use the lid of a snusdosa (hereforth called snuffbox). The lid of swedish snuffboxes has a small compartment for putting used snus in. This would serve as a good space to place my MCP9700 and then use the lid as a coaster.
I cut out a small hole on top of the lid leading into the compartment. I then cut a wider hole on the side for my wires to run through.
Then I took the foam that came with the Pico WH and placed it as a way to push up the MCP9700 so that it would be as flush as possible with the lid. Then I applied tape so that the compartment would stay sealed.

*Image 2: Cut snuffbox lid with attached MCP9700 in the center and wires connected through hole on side*
## Platform
### Choice of Platform
- **Local Installation**: The project uses a local Node.js server to receive data from the Pico WH and display it on a web interface.
- **Scalability**: The platform can be scaled by deploying the Node.js server on a cloud service like AWS or Heroku for remote access.
### Platform Functionality
I initially used [Adafruit](https://io.adafruit.com/) as my platform for visualizing my collected data. This worked just fine, but as a background in web development I knew I would much prefer to design my own web page where I could specify exactly what I needed. I cover this in more detail in the section [Presenting the data](#Presenting-the-Data).
The basic idea was to alert the user when the coffee was getting cold so I didn't feel the need for any database for storing any data. I felt that a session-based approach where the data is stored during use was sufficient for my needs.
- **Data Handling**: Receives temperature and humidity data from Pico WH on the local network.
- **Real-Time Display**: Uses WebSocket to update the web interface in real-time.
## The Code for Pico WH
### Project folder structure
```
hot-beverage-temperature-monitor/
│
├── boot.py
├── main.py
├── pymakr.conf
├── lib/
│ ├── keys.py
│ ├── wifiConnection.py
```
### Micropython
### *wifiConnection .py*
```python
import network
import time
from lib import keys
def connect_to_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(keys.WIFI_SSID, keys.WIFI_PASSWORD)
print(f"Connecting to {keys.WIFI_SSID}...")
# Wait for connection
while not wlan.isconnected():
time.sleep(1)
print("Trying to connect...")
print("Connected to Wi-Fi!")
print(wlan.ifconfig()) # Print the IP address
```
When connecting my Pico WH to WiFi I'm using the built in "network" and "time" modules.
- **network:** provides functions to configure network interfaces and manage connections
- **time:** time-related functions like sleeping for a certain period
I'm then utilizing the built in functions from network to create a WLAN object using the parameter `network.STA_IF`. This sets the mode of the Wi-Fi interface to STA (Station), which means the device will connect to an existing Wi-Fi network.
Then I try to connect to the WiFi specified in my "keys"-file.
### *main .py*
**Reading temperatures**
Since the DHT11 sensor is digital it is quite simple to read its values through the dht-library.
To read the temperature of the MCP9700, which is an analog sensor, we have to write some logic to interpret the readings and convert them to Celsius.
```python
# Function to read temperature from MCP9700
def read_temp():
reading = adc_temp.read_u16()
voltage = reading * (3.3 / 65535)
temp_c = (voltage - 0.5) * 100
return temp_c
```
What the code above does is:
- reads the analog signal from the sensor as a 16-bit integer (ranging from 0 to 65535). The analog signal comes from the ADC pin.
- converts the analog signal to voltage using the formula:
`voltage = reading × (3.3/65535)` where `3.3` is a reference voltage to the ADC.
- The MCP9700's output voltage at 0°C is 500mV (0.5V). Each degree Celsius increases the output voltage by 10mV (0.01V). To calculate the temperature, subtract the 0.5V offset from the voltage, then divide by 0.01V/°C. This is equivalent to multiplying by 100:
`temp_c = (voltage−0.5) × 100`
**Smoothing values from MCP9700**
The values received from the MCP9700 can fluctuate quite drastically between readings. Therefore, I felt a need for "smoothing" my values to "ignore" the outliers.
```python
def update_temperature():
global ema_temp
current_temp = read_temp()
ema_temp = alpha * current_temp + (1 - alpha) * ema_temp
return ema_temp
```
The code above uses something called an [exponential moving average (ema)](https://en.wikipedia.org/wiki/Exponential_smoothing) for smoothing out the read values. The EMA is calculated using the following formula:
`
EMA𝑡 = 𝛼 ⋅ 𝑥𝑡 + (1−𝛼) ⋅ EMA𝑡−1 where:
`
`EMA𝑡` is the EMA at time
`𝑥𝑡` is the current sensor reading
`𝛼` is the smoothing factor (a value between 0 and 1)
This smooths the data by weighting recent values more heavily than older values.
*Example:*
Example temperatures: 22.5, 22.7 and 25.6.
22.5 is the first value read, and is therefore set as the initial value.
```
First EMA Temperature (22.7)
current_temp = 22.7
ema_temp = alpha * current_temp + (1 - alpha) * ema_temp
ema_temp = 0.1 * 22.7 + 0.9 * 22.5
ema_temp = 2.27 + 20.25
ema_temp = 22.52
```
```
Second EMA Temperature (25.6)
current_temp = 25.6
ema_temp = alpha * current_temp + (1 - alpha) * ema_temp
ema_temp = 0.1 * 25.6 + 0.9 * 22.52
ema_temp = 2.56 + 20.268
ema_temp = 22.828
```
So using the alpha value of 0.1 each new temperature reading has a 10% influence on the new EMA value, while the previous EMA value retains a 90% influence. This makes it more responsive to recent changes compared to older values but still avoids drastic changes with each new reading.
**Sending data over HTTP**
```python
def send_to_server(temp, room_temp, humidity):
url = 'http://<your-ip>:3000/temperature'
headers = {'Content-Type': 'application/json'}
data = {'temperature': temp, 'room_temp': room_temp, 'humidity': humidity}
try:
response = urequests.post(url, json=data, headers=headers)
response.close()
print(f"Data sent to server: {data}")
except Exception as e:
print(f"Failed to send data to server: {e}")
wifiConnection.connect_to_wifi()
```
The send_to_server function sends temperature and humidity data to a specified server endpoint using an HTTP POST request. It handles potential errors by attempting to reconnect to Wi-Fi if the data fails to send.
## Transmitting the Data / Connectivity
### Data Transmission
- **Frequency**: Temperature from MCP9700 is sent every 10 seconds while the button is pressed. Room temperature and humidity is sent every 10 minutes.
- **Protocols**:
- **Wireless**: Wi-Fi (using the onboard Wi-Fi module of the Pico WH).
- **Transport**: HTTP POST requests to send data to the Node.js server.
### Design Choices
- **Wi-Fi**: Chosen for ease of setup and reliable connectivity.
- **HTTP**: Used for simplicity in data transmission.
### Security Perspective
Security is a paramount concern in IoT projects. For this project, data transmitted from the Raspberry Pi Pico WH to the Node.js server is sent over HTTP. In a production environment, implementing HTTPS would encrypt the data and protect it from interception. Additionally, employing authentication mechanisms would ensure that only authorized devices can send data to the server, further securing the system. Considering these security measures is crucial to protect sensitive information and maintain the integrity of the device.
## Presenting the Data
### Dashboard
#### Adafruit IO
I initially used Adafruit IO for visualization. While it worked adequately, I found it challenging to customize the dashboard to fit my specific needs. Adafruit IO's flexibility was limited in terms of UI customization and responsiveness, which prompted me to develop my own solution. This allowed for a more tailored and user-friendly interface, providing better control over the displayed data and the overall user experience.

**Image 3: Screenshot of the Adafruit IO dashboard showing the temperature graph**
#### Own web solution
For my own solution I wanted to create a simple dashboard which displayed:
* Room temperature (from DHT11)
* Highest (peak) temperature (from MCP9700)
* Current temperature (from MCP9700)
* Visual indication that the cup containing the hot beverage is getting cold
I figured I needed three states for the indicator:
1. Cup is warming up to peak temperature (image 4)
2. Cup has reached peak temperature but is not cold (image 5)
3. Cup is now getting cold (image 6)
The "warming" state was needed as the MCP9700 took quite a while (5-10 min) to get up to the peak temperature.
## Webserver (Node.js)
I used a [node.js](https://nodejs.org/en) server to locally host my web page. All the data was handled in here to later be passed on to my web page where the data would be stored in local storage. This would mean that the data would be stored in the current session of the browser, but not stored persistently in a database or similar.
The communication between the node.js server and the webapp was handled using the module [express](https://expressjs.com/). Express broadcasts JSON-data through the HTTP header. Express comes pre-installed when installing node.js (see [Steps to set up the computer](#Steps-to-Set-Up-the-Computer))
## Dashboard
Here I implemented a graph using [Chart.js](https://www.chartjs.org/) and some simple text fields to display room temperature and the highest temperature reached during this current session.
Here I implemented three separate classes that would represent my three temperature states: "warming", "hot" and "cold". The "Warming" state would be indicated by the coffee cup blinking on screen (see Image 4). The "hot" state would be indicated by not blinking but giving the coffee cup a green background (see Image 5). Lastly the "cold" state would be indicated with a red background.

*Image 4: Screenshot of the web interface showing the initial state with no data read. Here the coffee cup is blinking to indicate "warming"*

*Image 5: Screenshot depicting the web interface when data has been read and displayed in the graph. Here the coffee cup is marked as "green" since its reached peak temperature*

*Image 6: Screenshot of the web interface when the temperature of the coffee cup has gone below the average between its peak and room temperatures*
The logic for this was implemented with a function in the index.html file for the dashboard with Javascript.
```javascript
function updateStatusIcon(currentTemp, roomTemp) {
const icon = document.getElementById("statusIcon");
const currentTime = Date.now();
const timeSinceLastHigh = (currentTime - lastHighTime) / 1000;
if (timeSinceLastHigh < 15) {
status = "warming";
icon.classList.remove("green", "red");
icon.classList.add("icon");
} else if (currentTemp > (roomTemp + highestReading) / 2) {
status = "hot";
icon.classList.remove("icon", "red");
icon.classList.add("green");
} else if (currentTemp < (roomTemp + highestReading) / 2) {
status = "cold";
icon.classList.remove("icon", "green");
icon.classList.add("red");
}
}
```
In the code above I check the current time using the Javascript Date module to see how long ago the highest reading was done. If it was less than 15 seconds ago that we recieved a highest temperature reading, the MCP9700 is still warming up. If the current temperature is higher than the average between the peak and the room temperature the coffee is hot. If it is lower, the coffee is starting to get cold.
## Finalizing the Design
### Final Results
The Hot Beverage Temperature Monitor successfully measures and displays coffee temperature, room temperature, and humidity levels in real-time. The web interface updates dynamically and provides visual feedback on the coffee's status.

*Image 7: Final setup of the project with all components connected*
### Conclusion
The project demonstrates a practical application of IoT technology combined with my previous knowledge of Javascript and web development. It was a great learning experience for me as I have not worked this closely with the hardware aspect before. This has definately piqued my interest in continuing to develop my skills in hardware.
### Future Work
- **3d printing a coaster**: By 3d printing a coaster would not only increase stability but also provide better solutions for both keeping the cup in place and improving the placement for the MCP9700 sensor.
- **Replacing the push button with a pressure sensor**: This would open up possibilities such as measuring how much liquid is left in the cup and more accurately determine when a cup actually is present or not.
- **Notifications**: Implement some sort of external notification when the coffee is getting too cold, maybe through a smartphone app.
- **Smart Home Integration**: Integrate with smart home assistants like Amazon Alexa or Google Home.