Help build the future of open source observability software Open positions

Check out the open source projects we support Downloads

Grot cannot remember your choice unless you click the consent notice at the bottom.

Monitor temperature and humidity with Grafana and Raspberry Pi

Monitor temperature and humidity with Grafana and Raspberry Pi


There are a lot of reasons you might need to monitor temperature and humidity — from data center storage to preserving valuable artwork to making sure your plants thrive in that greenhouse you just installed. 

Fortunately, you can pair multiple sensor types with Grafana to monitor and protect whatever you care about most from the elements — computers, Picassos, or tomatoes.

A sample Grafana dashboard for monitoring temperature and humidity.
An example of a Grafana dashboard for monitoring temperature and humidity.

In the first part of this blog, Antonio Calero, developer advocate here at Grafana Labs, will walk through how to use Grafana to monitor temperature and humidity with a DHT22 sensor. (Note: all the code for this part of the blog can also be found in this GitHub repo.) 

In the second part of this blog, Laura Fernández Fernández, software engineer at Grafana Labs, will explore how to monitor temperature and humidity using Grafana and another sensor type: RuuviTag sensors.

In both cases, we will also use a Raspberry Pi computer and Prometheus.

Part 1: Using DHT22 sensors to track temperature and humidity 

Note: Part 1 of this blog is by Antonio Calero.

To get started, you will need to meet a few hardware and software requirements.

For hardware, you will need a Raspberry Pi. I used a Raspberry Pi 4 B+ with the Raspberry Pi OS, but any Raspberry Pi model should work.You will also need a DHT22 sensor.

The software requirements include:

Connect the sensor to Raspberry Pi

After meeting the requirements above, our first step is to connect our DHT22 sensor with the Raspberry Pi computer. 

The DHT22 sensor comes with three pins that we will connect to the Raspberry Pi pins with jumper wires that have female ends. On the DHT22 sensor, the power supply is represented with the positive symbol “+,” the data output as “out,” and the ground wire with the negative symbol “-.” 

To connect the sensor to the Raspberry Pi board, we need to:

  • Connect the power supply to pin 1 (3v3 power)
  • Connect the ground wire to pin 6  (Ground)
  • Connect the data output wire to pin 7 (GPIO 4)

(Tip: The pins on the Raspberry Pi board can be easily identified by counting. Place the Raspberry Pi in front of you, so you see two rows of pins at the top. The bottom row is numbered with odd numbers  — 1,3,5, etc. — and the row above it is numbered with even numbers — 2,4,6, etc.) 

Check that the sensor is working

Raspberry Pi makes it easy to read data from external devices. In this section, we’ll quickly check that the sensor is working by writing a simple Python program that pulls data off of the sensor in real-time.

ssh into your Raspberry Pi with:

ssh [username]@[IP address]

Or, if you set up a hostname during Raspberry Pi installation, you can also use:

 ssh [username]@[hostname].local

Install this Python library  to read the DHT series of humidity and temperature sensors:

pip3 install Adafruit_DHT

To keep our home directory clean, create a new directory and navigate to it. In this directory, create a file called “sensor-test.py” and paste the following code:

python
import Adafruit_DHT as dht_sensor
import time

def get_temperature_readings():
    humidity, temperature = dht_sensor.read_retry(dht_sensor.DHT22, 4) #gio pin 4 data out
    humidity = format(humidity, ".2f") + "%"
    temperature = format(temperature, ".2f") + "C"
    return {"temperature": temperature, "humidity": humidity}

while True:
    print(get_temperature_readings())
    time.sleep(30)

The important line of code here is dht_sensor.read_retry(dht_sensor.DHT22, 4). The 4 represents the GPIO 4 pin (physical pin 7 in the Raspberry Pi pin header), which is the data pin we wired to the sensor. 

Finally, run the test:

python3 sensor-test.py

If everything is set up properly, you should see output that looks like the following: 

mypi@mypi:~/grafana $ python3 sensor-test.py
{'temperature': '26.80C', 'humidity': '54.60%'}
{'temperature': '27.00C', 'humidity': '53.90%'}
{'temperature': '27.00C', 'humidity': '54.90%'}
...

This means we’ve got data! Let’s keep going.

Create the Python exporter for Prometheus

To visualize this data in Grafana, we need time-series metrics that we can collect and store to see how they vary over time. We can do that with Prometheus. However, we first need a way to export the data through a port. For that, we will use a Python application based on Flask, a web framework for Python that can run using a single file.

Install the Python dependencies

Before we can create our Python app, we need to install a couple libraries. We will do that from our Raspberry Pi. You should already be connected via ssh:

pip3 install flask prometheus_client

Create the Python app

Next, we will create another Python file named “app.py” and paste the following code.

This is the main application, which is the exporter of our data.

python
import Adafruit_DHT as dht_sensor
import time
from flask import Flask, Response
from prometheus_client import Counter, Gauge, start_http_server, generate_latest

content_type = str('text/plain; version=0.0.4; charset=utf-8')

def get_temperature_readings():
    humidity, temperature = dht_sensor.read_retry(dht_sensor.DHT22, 4)
    humidity = format(humidity, ".2f")
    temperature = format(temperature, ".2f")
    if all(v is not None for v in [humidity, temperature]):
        response = {"temperature": temperature, "humidity": humidity}
        return response
    else:
        time.sleep(0.2)
        humidity, temperature = dht_sensor.read_retry(dht_sensor.DHT22, 4)
        humidity = format(humidity, ".2f")
        temperature = format(temperature, ".2f")
        response = {"temperature": temperature, "humidity": humidity}
        return response

app = Flask(__name__)

current_humidity = Gauge(
        'current_humidity',
        'the current humidity percentage, this is a gauge as the value can increase or decrease',
        ['room']
)

current_temperature = Gauge(
        'current_temperature',
        'the current temperature in celsius, this is a gauge as the value can increase or decrease',
        ['room']
)

@app.route('/metrics')
def metrics():
    metrics = get_temperature_readings()
    current_humidity.labels('study').set(metrics['humidity'])
    current_temperature.labels('study').set(metrics['temperature'])
    return Response(generate_latest(), mimetype=content_type)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Before you run the app, understand that we have created a web application based on Python in a single file, which can do a number of things, including:

  • Get indoor temperature and humidity readings by interacting with your sensor
  • Enable metrics that can be counted, measured, and exposed over HTTP, and to be read by the Prometheus server via the exposed port 5000

Run the script as a service. Because we are about to run an application that needs to stay active, and because we want to keep running commands in the terminal, it is convenient to create a service that will run our script in the background. We can do that via systemd, which is a system and service manager for Linux.

First, create a new file with a text editor of your choice, and give it the .service extension.

In this example the name is sensor-flask.

sudo nano /etc/systemd/system/sensor-flask.service

Inside, paste the following:

[Unit]
Description=A service that will keep my python app running in the background
After=multi-user.target
[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/python3 /home/path-to-python-file-/app.py
[Install]
WantedBy=multi-user.target

The important piece that we need to change is the ExecStart flag, which takes in the command that you want to run. The first argument is the Python path (in my case, it’s python3) and the second argument is the path to the script that needs to be executed:

ExecStart=/usr/bin/python3 /path-to-python-file-/app.py

Now, since we have changed a service file in /etc/systemd/system/, we need to reload the systemd manager, so our service file kicks off:

sudo systemctl daemon-reload

Finally, we enable and start our service right away:

sudo systemctl enable sensor-flask.service --now

You can check the status of your service by running the following:

systemctl status sensor-flask.service

Check that everything is working

Our Python app is already running in the background, thanks to the service that we have just created. So, if you send a request to http://localhost:5000/metrics or visit http://{you-raspberry-pi-IP-address}:5000/metrics from another machine in your network, you should be able to see the exported metrics:

mypi@mypi:~ $ curl http://localhost:5000/metrics
python
(...)

current_humidity{room="study"} 54.0
# HELP current_temperature the current temperature in celsius, this is a gauge as the value can increase or decrease
# TYPE current_temperature gauge
current_temperature{room="study"} 27.0

Scrape your sensor data with Prometheus

Grafana can’t really talk to our application directly. That’s why we have built an exporter app to open a door for our data, so it can be scrapped at regular intervals. This is where Prometheus comes into play. But, before we can run Prometheus, we need to create a configuration file to establish the port we want to scrape. In this case, we are targeting port 5000, since it is the port that we have exposed in our Python application (see client server in the diagram below).

A diagram depicting how Grafana works with the Prometheus Scraper.

To create our Prometheus configuration file, the first step is to create a file with a .yml extension. The standard practice is to call it “prometheus.yml.” 

Next paste the code:

scrape_configs:
  - job_name: 'temperature-exporter'
    scrape_interval: 15s
    static_configs:
    - targets: ['host.docker.internal:5000']
      labels:
        instance: 'my-pi'
        room: 'study'

Notice that instead of using our internal IP address in targets, we are using the special DNS name host.docker.internal which will resolve to the internal IP address used by the host.

Save and exit.

Mount your prometheus.yml into a Docker container

For development purposes, we will access the host port (our Raspberry Pi’s port) from inside the Docker container. To do that, add --add-host=host.docker.internal:host-gateway to the command below:

docker run \
  -d \
    -p 9090:9090 \
    -v ./prometheus.yml:/etc/prometheus/prometheus.yml \
  --add-host=host.docker.internal:host-gateway \
    prom/prometheus

Also, make sure you tell Docker the path to the prometheus.yml file.

Monitor your temperature and humidity data from Grafana

You are just a few strokes away from finishing this painting. 

From your machine, access your Raspberry Pi by entering the IP address into the browser: http://{you-raspberry-pi-IP-address}:3000. The Grafana login page should appear (if you installed Grafana prior to this point).

Log in to Grafana (the default username and password is admin:admin).

Then, add Prometheus as a data source:

  1. Open the menu by clicking on the Home button at the top left corner
  2. Go to Connections > Data sources > + Add new data source
  3. In the field Prometheus server URL, enter http://localhost:9090
  4. Click Save & Test

Explore your data and create your own dashboard

If everything went well, this is the sweet moment when you get to see your data graphed. To do this: 

  1. Open the side menu
  2. Click Explore
  3. Select Prometheus from the dropdown menu at the top left corner
  4. Select the metrics
  5. Click Add to dashboard

You made it! Now, you can customize your dashboard and make it look pretty.

Part 2: Using RuuviTag Sensors to track temperature and humidity

Note: Part 2 of this blog is by Laura Fernández Fernández.

Galicia is commonly known as a green land, with lots of rivers, beaches, and cliffs facing the Cantabrian Sea and the Atlantic Ocean  — but it also has its rainy days. This leads to issues with humidity, and my flat is no exception.

As the rooms next to my terrace can become moldy during winter, I want to track temperature and humidity so I know when to turn on the dehumidifier.

To follow along with this part of the blog, you will need:

  1. RuuviTag sensors (I used two, because I wanted to monitor the temperature and humidity in two different rooms)
  2. Raspberry Pi (I used the Raspberry Pi 4 advanced kit to be sure I have all the necessary components)
  3. Ruuvi Prometheus scraper
  4. Prometheus
  5. Grafana

Set up Raspberry Pi and the Ruuvi Prometheus scraper

First, you will need to build the Raspberry Pi and install the OS. If you are using the Raspberry Pi 4 advanced kit, as I did, you just have to insert the MicroSD that has NOOBs pre-installed, choose the software, and follow the instructions.

Next, set up the Ruuvi Prometheus scraper. According to the Read.me file, run:

curl -fsSL "https://raw.githubusercontent.com/arimkevi/ruuvi-prometheus-scraper/master/install.sh" | sudo bash

You won’t need to clone the repo, as this command will install the necessary packages, download the repo in “/home/ruuvi/…,” create a virtual environment with the dependencies, and kick-start the service.

Add the MAC address of your RuuviTag sensors in /home/ruuvi/ruuvi-prometheus-scraper/config.json. Your sensor’s MAC can be found in the Ruuvi Station mobile app > Sensor settings > More info.

With the sensor turned on, check if the Ruuvi Prometheus scraper is working through a browser by looking at localhost:8000.

You should check that it not only shows some data, but also has these three metrics with some value:
#HELP ruuvi_temperature_c Temperature in Celsius
#TYPE ruuvi_temperature_c gauge
ruuvi_temperature_c{location=”your sensor name”} 29.59

#HELP ruuvi_humidity_percent Humidity %
#TYPE ruuvi_humidity_percent gauge
ruuvi_humidity_percent{location=”your sensor name”} 0.3979

#HELP ruuvi_pressure_hpa Air pressure hPa
#TYPE ruuvi_pressure_hpa gauge
ruuvi_pressure_hpa{location=”your sensor name”} 1006.63

(Note: If you try to run main.ly and it triggers a  ‘ModuleNotFound,’ you can install the prometheus_client: pip install prometheus_client)

Run Prometheus

To start, clone the Prometheus repository.

Modify the prometheus.yml file by adding the Ruuvi sensors to the scrape_configs:

json
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "prometheus"
    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.
    static_configs:
      - targets: ["localhost:9090"]
    
  # Ruuvi sensors
  - job_name: "ruuvi"
      static_configs:
        - targets: ["localhost:8000"]

You can start Prometheus by following the ‘Read.me’ instructions or running this in the repo:

sudo ./prometheus –-config.file=prometheus.yml

As we did with the Ruuvi scraper, we should check if Prometheus is working as expected. Open localhost:9090 in your browser. In localhost:9090/graph, look for the Ruuvi metrics, as ruuvi_temperature_c, and execute it to visualize them. You can also see the data in a table format in localhost:9090/table.

Visualize data in Grafana 

Now that we have both the Ruuvi Prometheus scraper and Prometheus running, we should clone the Grafana repo so we can start visualizing our data. 

Open Grafana in the correspondent port, which will be localhost:3000 by default, and log in with “admin” as the user and password keys. You can modify these later.

Create a dashboard by importing the one from the Ruuvi Prometheus scraper and you will be able to visualize your data in Grafana. Adjust the thresholds and other settings, as needed.

You’ve done it! Congratulations!

What I’ve learned

As a final thought, here are a few of the key things I learned about the process of getting data from the sensors and then visualizing it on a Grafana dashboard.

The scraper exposes the RuuviTag sensors’ data on localhost:8000. Prometheus pulls the metrics from that address  — this is why its config should be modified to add this target as a new scrape config block — and stores them in a time-series database.

Also, Grafana uses Prometheus as the data source, pointing at localhost:9090. The user should get the specific data from the RuuviTag sensors running the correspondent queries, such as ruuvi_temperature_c{location=”your location”} and then the metrics will be displayed in the visualization previously chosen.

Grafana Cloud is the easiest way to get started with metrics, logs, traces, and dashboards. We have a generous forever-free tier and plans for every use case. Sign up for free today!