Marie Cruz


STEM in the garden: how to monitor plants with IoT sensors and Grafana Cloud

This post was originally published on the Grafana blog which you can view here.

As a Senior Developer Advocate here at Grafana Labs, I’m always looking for opportunities to introduce tech concepts to my 8-year-old daughter in a fun and relatable way. She’s been into gardening since last year, so during a recent STEM week, I decided to use gardening as a way to introduce her to the concepts of IoT, monitoring, and observability.

In this blog post, I’ll walk through how my daughter and I recently set up an IoT project to monitor the moisture levels of our plants using Arduino, Prometheus and Grafana Cloud — and also recap all the fun we had along the way.

Green thumb or not, you can read on to set up this project at home. You can also check out our GitHub project, plant-monitoring, to find all the code in this post.

Understanding IoT through gardening

Since the start of COVID-19, we’ve bought a few indoor plants. Some have flourished, while others failed to survive. It seemed we either watered the plants too much or didn’t water them enough.

To start explaining IoT to my daughter, I asked her to imagine if each plant had a little green-thumb helper (aka, a sensor) in its soil that could detect moisture levels and inform us when it needed water. Sounds cool, right? I explained that IoT is just that — a way to connect objects, including sensors, to the internet so they can monitor, communicate, and, in some cases, even take action automatically to make our lives easier.

A diagram of the IoT setup.

We observed that our soil moisture sensor has two metal parts, similar to a fork. When the soil is wet, water helps electricity flow easily between the metal parts, and when the soil is dry, electricity can’t flow as well.

The moisture sensor passes this information as electrical signals to a development board like an Arduino or ESP32, which acts as the brain of this project.

Once the sensor is connected to a development board, it can have two possible outputs:

  • Analog Output (AO), where output is of a variable number depending on the moisture level. When it’s a high number, the soil is dry. When it’s a low number, the soil is wet.
  • Digital Output (DO), where output is HIGH when the soil is wet and LOW when the soil is dry.

Project setup

Next, it was time to start setting up our IoT project. I used this avocado monitoring project from Grafana Labs Senior Software Engineer Ivana Huckova for inspiration (along with lots of Googling). I also watched a GrafanaCON 2021 session, Easy DIY IoT Projects with Grafana, by Ivana and Grafana Labs Principal Software Engineer Ed Welch, to see other cool ways to monitor IoT setups with Grafana.

Then, there were a few bits of hardware I had to purchase for the project:

For the software, I also had to install Arduino IDE, which is open source software you use to write, edit, and upload code to a development board.

Connecting all the hardware

Now that we had everything we needed, it was time to piece together all the hardware and get to the most exciting part of the project.

Our first step was to connect the soil moisture sensor pieces using the Dupont cables. Afterward, we attached the ESP32 development board to the breadboard. While this is an optional step, we found that, in our case, inserting the development board into the breadboard made it more stable.

Next, we connected the moisture sensor to the development board by connecting the following pins:

  • VCC pin to 3.3V pin
  • GND pin to GND pin
  • AO to pin 11
A screenshot of the hardware for the soil moisture sensor project.
Connected hardware for the soil moisture sensor project

We also inserted the moisture sensor directly into the soil of a plant that had recently been watered.

Creating the initial program

Once the hardware bits were done, I connected the development board to my laptop via a USB-C cable. Next, I followed the instructions on the GitHub repo IoT with Arduino and Grafana to set up the Arduino IDE on my laptop, including adding support for our ESP32 board. (Note: Ivana also explains this process in a previous blog post.)

For our first iteration, I wanted to keep things as simple as possible so my daughter could easily follow along.

In Arduino IDE, we created a new file called plant-monitoring.ino and added the following lines of code:

// Define the sensor pin and sensor variable
int sensorPin = 11;
int sensorValue = 0;
void setup() {
  // Initialize serial communication at 9600 baud rate
  Serial.begin(9600);
}
void loop() {
  // Read the analog value from the sensor
  sensorValue = analogRead(sensorPin);
  
  // Check if the soil is dry
  if (sensorValue > 500) {
    Serial.print(sensorValue);
    Serial.println(" - Status: Soil is too dry - time to water!");

  } else {
    Serial.print(sensorValue);
    Serial.println(" - Status: Soil is perfect!");
  }

  // Wait 5s before taking another reading
  delay(5000);
}

Essentially, the code snippet above defines our sensor pin. Within the setup function, we initialize the serial communication at a 9600 baud rate. Within the loop function, we continuously read the analog value provided by the sensor. If the sensor value is above 500, the soil is dry. Otherwise, if the value is low, the soil is wet.

Note: To test our project easily, we waited 5 seconds before taking another moisture reading. You can increase this value so you wait for a longer amount of time to avoid quickly corroding the moisture sensor.

To explain the baud rate to my daughter, I used the following analogy: “Imagine you are talking with your best friend, and you both agree to speak 9600 letters every second. If you speak less or more than 9600 letters, your communication might not be as clear.”

Running the program

After uploading the code to the development board and opening up the serial monitor in Arduino IDE, we saw the following messages:

A screenshot of serial monitor logs when the soil is wet. The message says 'Status: Soil is perfect!'
Serial monitor logs when the soil is wet

Seeing these logs was a good sign after inserting the moisture sensor into our plant’s soil. After taking the sensor out, we saw the following messages:

A screenshot of serial monitor logs when the soil is too dry. The message says 'Status: Soil is too dry - time to water!'
Serial monitor logs when the soil is dry

What’s observability, Mom?

After completing the project setup, the next step was to explain the concepts of monitoring and observability to my daughter — and this, of course, is where Prometheus and Grafana came in.

I explained that monitoring is a way to check the plant’s health. We monitor the moisture level of the soil, and if it’s too dry, we get an alert to water it. We are reacting to the situation, and to the plant’s needs.

On the other hand, observability takes monitoring to another level. Observability is a way to understand why our plant’s health changes over time. It’s about looking at all our data, identifying patterns over time, and predicting what will happen next. For example, does the plant’s soil moisture drop during a specific time of day? Do we need to include other factors, such as room temperature and humidity, into our project to support our predictions? Observability considers all these points and more, so we can optimize the living conditions of our plants.

I then introduced my daughter to Prometheus and Grafana as the de-facto platforms for monitoring and observability.

Prometheus: The Gardener

To introduce Prometheus, I asked her to imagine an imaginary gardener who collects sensor values and stores all of them in a toolbox. I also told her that, in order to get these values from Prometheus, we must speak its language: PromQL.

soil_moisture{monitoring_type="gabby_plant"}

Prometheus will return the soil moisture sensor values with timestamps so we know when the values are collected. Here’s an example:

TimeSensor value
2025-03-17 19:30:00180
2025-03-17 20:00:00200
2025-03-17 20:30:00260

Setting up Grafana Cloud

The easiest way for me to show Prometheus was to set up a Grafana Cloud account using the free tier, which comes with a fully managed Prometheus instance. After signing up, we were redirected to a landing page like the one shown below.

A screenshot of the Grafana Cloud landing page.
Grafana Cloud landing page

Installing Prometheus libraries in Arduino

To make it easier for our Arduino code to connect to Prometheus, we installed additional libraries (shown below), as instructed in the IoT with Arduino and Grafana GitHub repo, which was relatively straightforward.

Arduino libraries for Prometheus.

Setting up Prometheus

The next step was to store the details of our Prometheus instance in a configuration file so that the code we wrote in Arduino could connect to Prometheus.

To do this, we followed all the steps listed in the Sending metrics section of the IoT with Arduino and Grafana GitHub project. We ended up with a new file called config.h, which contained the following (some values omitted for security):

// Prometheus details
#define GC_PROM_URL "prometheus-prod-13-prod-us-east-0.grafana.net"
#define GC_PROM_USER "" // username
#define GC_PROM_PASS "" // token
#define GC_PROM_PATH "/api/prom/push"

#define GC_PORT 443

// Wifi details
#define WIFI_SSID     "" // WIFI name
#define WIFI_PASSWORD "" // WIFI password

We also created another file called certificates.h, which included a certificate we could use to connect to Grafana Cloud securely. The certificate content was copied from the certificate file of the prometheus-arduino GitHub project.

These two files were eventually included in our plant-monitoring.ino file (more on that below).

Updating our initial program

The prometheus-arduino GitHub project also has a code example of how to send the metrics from Arduino to Prometheus. Instead of reinventing the wheel, we copied the code example and updated it slightly to work with our code. I remember my daughter asking if this was cheating, to which I replied, “This is quite normal with programming. 😅”

The final program can be seen here.

After uploading the updated program to our development board, we saw the following logs on our serial monitor. 🙌🏻

A screenshot of serial monitor logs with Prometheus successfully connected.
Serial monitor logs with Prometheus successfully connected

Grafana Cloud: The Visualizer

The final piece of the project was to visualize all our data with Grafana Cloud. While Prometheus was our gardener, I explained that Grafana is our cool visualizer. It takes the sensor values from Prometheus and turns them into beautiful graphs, making those values easier to understand.

The Plant Dashboard

We created our dashboard using different visualization types, such as the classic time series, stat, gauge, and text panels. Since Prometheus is the default data source for Grafana Cloud, we just had to select the correct metric and label values when creating the different panels.

We ended up with the following dashboard by configuring value mappings to print different texts based on the values. 🙌🏻

Grafana dashboard when the plant is healthy
Grafana dashboard when the plant is healthy
Grafana dashboard when the plant is thirsty
Grafana dashboard when the plant is thirsty

Thirsty Plant Alert

Having a dashboard to look at is excellent, but we thought getting an alert when our plant was thirsty would be even better. So, the final piece of our project was to configure Grafana Alerting.

To set this up, we created a new alert rule with the alert condition “when the query is above 500” to ensure we would only be alerted if the soil moisture value was above 500.

A screenshot of the alert rule conditions for our soil moisture sensor project.
Alert rule conditions for our soil moisture sensor project

We also linked the alert to our dashboard and time series panel for an additional visual indicator of whether an alert was firing or not. Note: you can learn more about this process in this video.

Finally, we created a new contact point to define where our alerts should be sent when an alert rule is triggered. We chose Telegram and followed the instructions to configure Telegram for alerting. So, every time the sensor values go above 500, we get the following notification on Telegram:

A screenshot of the alerting notifications sent to Telegram.
Alerting notifications sent to Telegram

Wrapping up

Overall, the soil moisture sensor project was a great starter kit for my daughter to learn more about IoT and Grafana. She was ecstatic to see everything working from start to finish and even presented the project to her classmates, who were all impressed.

By introducing her to new technical concepts, she also learned more about my work and what I do as a Developer Advocate here at Grafana Labs.

Happy planting!