Unlocking Bonsai Brilliance

Unlocking Bonsai Brilliance

Unleashing the Power of IoT for Seamless Watering and Optimal Growth.


In the realm of software development, the pursuit of observability and data-driven decision-making has become a fundamental aspect of building and maintaining robust microservices architectures. Drawing inspiration from this ethos, I sought to apply these principles to a rather unexpected domain - the care and cultivation of my cherished Bonsai tree.

Similar to the complex systems we engineer, the art of growing a Bonsai tree involves a multitude of parameters that must be carefully balanced for optimal results. However, fine-tuning these variables can be a time-consuming process. Fortunately, certain rules of thumb exist within this horticultural art form that provide valuable guidance in our quest for nurturing these miniature living masterpieces.

Be sure not to water your tree if the soil is still wet, but don't let the tree dry out either.

As a beginner, use your fingers at about one centimeter deep, (0.4") to check the soil moisture. If it's slightly dry, go ahead and water your tree.

followed by:

Avoid watering all of your trees on a daily routine, until you know exactly what you are doing.

This sounds not too exact (for me at least) given my objective is to have a good-looking tree on my desk with healthy and green leaves


To get started you'll need:

  • Arduino-like Board

  • Soil-Moisture Sensor

  • Wi-Fi module (optional)

In this project, NodeMCU V2 ESP8266 paired with Soil Moisture Hygrometer Detection Humidity Sensor was used.


The schematics are pretty straight-forward and as follows:

I've settled on analogous mode for the sensor.


The prerequisites of this step are to connect your development board to a serial monitor and decide on a Baud Rate (in this example 9600). The source code is available on Aleksandar1932/overkill-bonsai.

The next step is to write a lib that provides an API to the soil moisture sensor.

Stating by taking defining a few constants in lib/Constants /Constants.cpp:

#define sensorPower 0
#define sensorPin A0

#define WET_THRESHOLD 500     // Define max value we consider soil 'wet'
#define DRY_TREHSHOLD 750     // Define min value we consider soil 'dry'
#define MEASURE_INTERVAL 1000 // Define how often we check soil moisture (milliseconds)

Next is the lib/Moisture /Moisture.cpp:

#include <Arduino.h>
#include <Constants.h>

int readSensor()
    digitalWrite(sensorPower, HIGH);
    int val = analogRead(sensorPin);
    digitalWrite(sensorPower, LOW);
    return val;

void setupSoilMoistureSensor()
  pinMode(sensorPower, OUTPUT);
  digitalWrite(sensorPower, LOW);

void logMoisture(int moisture)
  Serial.print("Analog Output: ");

  // Determine status of our soil
  if (moisture < WET_THRESHOLD)
    Serial.println("Status: Soil is too wet");
  else if (moisture >= WET_THRESHOLD && moisture < DRY_TREHSHOLD)
    Serial.println("Status: Soil moisture is perfect");
    Serial.println("Status: Soil is too dry - time to water!");

At this point, the core API is defined, and the reset is implementing the presentation layer that will allow us to interact with the sensor. For this I've used ESP8266WebServer for the web server and ESP8266WiFiMulti for Wi-Fi connectivity.

The web server implements two handlers

  • on / will return json response containing the soil-moisture reading alongside the status (wet, perfect and dry) determined by the thresholds.

  • on /display will return an HTML response containing the Moisture and some "prettier" UI.

The presentation layer alongside rest of the logic is as follows:

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266mDNS.h>
#include <Moisture.h>
#include <Constants.h>

ESP8266WebServer server(80);
ESP8266WiFiMulti wifiMulti;

void handleMeasurement();
void handleDisplayHTML();
void connectToWifi();
void handleDisplayPrettyHTML();

void setup()
  server.on("/", handleMeasurement);
  server.on("/display", handleDisplayHTML);

void loop()
  int moisture = readSensor();

void handleMeasurement()
  int moisture = readSensor();
  server.send(200, "application/json", "{\"moisture\": " + String(moisture) + ", \"status\": \"" + (moisture < WET_THRESHOLD ? "wet" : (moisture >= WET_THRESHOLD && moisture < DRY_TREHSHOLD ? "perfect" : "dry")) + "\"}");

void handleDisplayHTML()
  server.send(200, "text/html", "<html><head><title>ESP8266 Soil Moisture Sensor</title></head><body><h1>Aleksandar's Bonsai</h1><p>Moisture: " + String(readSensor()) + "</p></body></html>");

void connectToWifi()
  wifiMulti.addAP(getenv("WIFI_SSID"), getenv("WIFI_PASSWORD"));
  Serial.println("Connecting ...");

  while (wifiMulti.run() != WL_CONNECTED)
  Serial.print("Connected to ");
  Serial.print("IP address:\t");

  if (MDNS.begin(getenv("MDNS_HOSTNAME")))
    Serial.println("mDNS responder started");
    Serial.println("Error setting up MDNS responder!");

Additionally, I've used mDNS for development purposes.

Determining optimal thresholds

To determine the thresholds I've used this chart as a reference, but the freedom is yours to fine-tune to achieve optimal results (whatever your objective is).

Final thoughts

The integrated solution turned out to look as:

This implementation serves as an initial foundation for an IoT project, with future plans to expand its capabilities. I intend to enhance the system by integrating a water pump and canister, enabling automated watering when the soil moisture threshold is low. Moreover, I plan to incorporate a feedback loop using a photo-sensor directed at the leaves, providing insights into the tree's performance and enabling the determination of an optimal threshold.

In addition to these advancements, I aim to integrate the board into the Tuya platform, replacing the existing presentation layer. This integration will seamlessly incorporate the Bonsai care system into my smart-home ecosystem, enhancing its accessibility and integration with other connected devices.