ESP32 Powered Smart Terrarium

by yotitote in Circuits > Microcontrollers

2903 Views, 12 Favorites, 0 Comments

ESP32 Powered Smart Terrarium

490693100_1240581290953597_6134664874983456472_n.jpg
smart-terrarium-artificial-environment-fogger-temperature.png

ESP32 Powered Smart Terrarium

This project is a smart terrarium controlled with an ESP32 and packed with features such as temperature and humidity sensors, programmable LED strips, an OLED display, and a fogger. The goal of this project is to protect and support plant growth as much as possible, while maintaining a dark forest aesthetic. Endangered or exotic plants can also be kept in this terrarium, as it ensures the specific environmental conditions needed for their growth are sustained. Additionally, a similar or even the same system can be used to cultivate fresh vegetables at home and support a healthy lifestyle :)

Supplies

479457375_9146107198802943_2979152502963673043_n (1).jpg

To make this project the following materials will be needed:

Terrarium and plants:

  1. A terrarium, the size doesn’t matter but the project will be easier with smaller ones. I used a 25 x 25 x 30 cm one.
  2. Drainage layer, I used organic pebbles. This will also be the reservoir for our fogger.
  3. Substrate mixture, I used coco soil combined with some gardening soil.
  4. A mesh, separating the drainage layer from the substrate. Make sure that the chosen one lets water pass but doesn’t let dirt pass.
  5. Moss, I used a mixture of moss collected from a friend’s garden and store-bought one. Make sure to clean the collected moss to ensure no parasites or unwanted bugs get into the terrarium.
  6. Plants, a mixture of exotic and common plants with high humidity acceptance will do well in this terrarium.

Electronics:

  1. ESP32 DevKit, the manufacturer doesn’t matter as long as it is a working ESP32.
  2. A small breadboard for connecting the components.
  3. A small fogger, commonly used in terrariums.
  4. A one-channel relay for connecting the fogger.
  5. A power supply matching the voltage of the fogger, usually 12V.
  6. A step-down voltage converter to power the ESP and the LEDs from the power supply.
  7. Addressable LED strips with a controller, two strips. I used two 5 LED strips, but that can be changed accordingly to the terrarium’s size.
  8. DHT22 humidity and temperature sensor.
  9. 1.4 inch OLED display.
  10. Wires.

3D Printing

Screenshot 2025-04-14 193831.png
479500860_658026593268289_1685675498364325985_n (1).jpg

3D printing the needed part:

To start the build, one part needs to be printed. It’s the drainage layer filter to keep the pellets out of the fogger’s way. The filter can be printed vertically without supports, although additional adhesion may be needed to keep it in place.

Downloads

Building the Terrarium

483110794_1203229967989133_633357633928893332_n (1).jpg
477872141_612798088187801_1233154566284928666_n (2).jpg
481234844_1783969449052694_7458252542922017902_n (1).jpg
474983964_516361087748510_7573466857034940138_n (1).jpg

Preparation:

To start off the project, a clean terrarium is needed. To clean mine, I just soaked it in a bath and scrubbed it with a sponge. After this process is done, the drainage layer reservoir can be made. The pellets need to be flushed of any dust and dirt to avoid contaminating the water used for the fogger. Next, the 3D printed part can be placed in the front corner of the terrarium and glued in place. Now, the clean pellets can be added to the terrarium to form a 6 cm drainage layer. Remember to form the reservoir around the 3D printed piece and avoid letting any pellets fall inside the corner.

Build process:

The drainage mesh can now be cut down to size and secured on the drainage layer. Make sure to leave some excess and tuck it into the sides to create a seal for the substrate to sit on. Next, we can mix the coco soil brick with water and ground soil to achieve a consistent texture. The product of this step can now be poured into the terrarium to make a 5 cm layer. The coco soil can be mixed with common potting dirt. Now, all of the chosen plants and moss can be added in a chosen configuration. I used two types of moss: one I collected from a friend’s garden and the other one I bought from an online shop. Finally, some finishing touches like small pieces of wood and bark can be added.

Electronics

smart-terrarium-electronics-diagram-the-electronic.png
480522077_649286707627981_9159184657845664432_n (1).jpg
477302956_2016818595448911_5749495518513874190_n (1).jpg
491026811_693929199666174_1316067515569046604_n.jpg

Connections:

The image above is just an illustrative diagram. Here are the connections made in the IRL project:

OLED_SDA - GPIO 21

OLED_SCL - GPIO 22

DHTPIN - GPIO 15

RELAY_PIN - GPIO 2

LED_PIN - GPIO 5

LED_BUTTON_PIN - GPIO 4

LED_BUTTON_PIN_2 - ESP32 GND

RELAY_BUTTON_PIN - GPIO 18

RELAY_BUTTON_PIN_2 - ESP32 GND

All of the other power pins (3.3V and GND) were connected to the ESP32 3.3V and GND pins.

Additionally, the power source, outputting 12V, was connected to a voltage regulator, bringing the voltage down to 5V and then to the ESP32. The power source was also connected to the fogger voltage pin through the relay, and the ground pin was connected normally.

Additional information:

The system is connected through a breadboard visible in the images above. The DHT22 sensor is located inside the terrarium, and the LEDs are glued to the inner side of the top cover. The two LED strips are connected with thin wires, which are soldered to the LED pads. It is important that the LED signal goes from DO to DIN, reversing this order will result in the LEDs not working. The fogger is mounted in the reservoir corner at the bottom of the tank, and the power cable can be hot-glued to the nearest corner to minimize its visibility. As seen in the pictures, I secured the electronics on a cardboard stand made from two pieces glued together with hot glue.

Programming

Uploading:

To program the ESP32, a program called Arduino IDE is needed. Make sure that the right model of the ESP32 board is selected. If the code upload fails, try holding the boot button while uploading. If any other issues arise, seek help on YouTube or Reddit. Make sure to unplug the power and GND pins powering the rest of the components from the ESP32 before uploading the code. Neglecting this step may cause problems in the uploading process. Installing the following libraries is needed: U8g2lib.h, Wire.h, DHT.h, FastLED.h.

The following code is a simple way of controlling the terrarium. It contains light modes such as sunrise, sunset, or a storm, which can simulate the natural environment of the plants. The relay can also be turned on and off to enable or disable the fogger’s action. These two features can be controlled by the two tact switches installed in the system. I plan on adding a more advanced version of the code with built-in automation features such as time-based controlled lighting.

Here is the code:

#include <U8g2lib.h>
#include <Wire.h>
#include <DHT.h>
#include <FastLED.h>


#define OLED_SDA 21
#define OLED_SCL 22
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, OLED_SCL, OLED_SDA);


#define DHTPIN 15
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);


#define RELAY_PIN 2
bool relayState = HIGH;


#define LED_PIN 5
#define NUM_LEDS 10
#define BRIGHTNESS 200
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB


#define LED_BUTTON_PIN 4
#define RELAY_BUTTON_PIN 18
int mode = 0;

CRGB leds[NUM_LEDS];

void setup() {
Serial.begin(115200);

// OLED Setup
u8g2.begin();
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_6x12_tr);
u8g2.drawStr(35, 10, "Initializing...");
u8g2.sendBuffer();
delay(2000);


dht.begin();
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, relayState);


FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
clearLEDs();


pinMode(LED_BUTTON_PIN, INPUT_PULLUP);
pinMode(RELAY_BUTTON_PIN, INPUT_PULLUP);
}

void loop() {
handleButtonPresses();

float humidity = dht.readHumidity();
float temperature = dht.readTemperature();

if (isnan(humidity) || isnan(temperature)) {
Serial.println(F("Failed to read from DHT sensor!"));
return;
}

displayReadings(temperature, humidity, relayState, mode);

switch (mode) {
case 0: clearLEDs(); break;
case 1: stormEffect(); break;
case 2: sunsetEffect(); break;
case 3: sunriseEffect(); break;
}

delay(500);
}

void handleButtonPresses() {
static bool ledPrevState = HIGH;
static bool relayPrevState = HIGH;

static unsigned long ledLastChange = 0;
static unsigned long relayLastChange = 0;
const unsigned long debounceTime = 50;

bool ledCurrent = digitalRead(LED_BUTTON_PIN);
bool relayCurrent = digitalRead(RELAY_BUTTON_PIN);
unsigned long now = millis();

// LED button
if (ledCurrent != ledPrevState && (now - ledLastChange) > debounceTime) {
ledLastChange = now;
if (ledCurrent == LOW) {
mode = (mode + 1) % 4;
Serial.print("LED Mode changed to: ");
Serial.println(mode);
}
}
ledPrevState = ledCurrent;

// Relay button
if (relayCurrent != relayPrevState && (now - relayLastChange) > debounceTime) {
relayLastChange = now;
if (relayCurrent == LOW) {
relayState = !relayState;
digitalWrite(RELAY_PIN, relayState);
Serial.print("Relay manually set to: ");
Serial.println(relayState == LOW ? "ON" : "OFF");
}
}
relayPrevState = relayCurrent;
}

void displayReadings(float temperature, float humidity, bool relayState, int mode) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_6x12_tr);
u8g2.drawStr(30, 10, "Terrarium Stats");

char tempBuffer[20];
snprintf(tempBuffer, sizeof(tempBuffer), "Temp: %.1f C", temperature);
u8g2.drawStr(30, 30, tempBuffer);

char humBuffer[20];
snprintf(humBuffer, sizeof(humBuffer), "Hum: %.1f %%", humidity);
u8g2.drawStr(30, 45, humBuffer);

char relayBuffer[20];
snprintf(relayBuffer, sizeof(relayBuffer), "Relay: %s", relayState == LOW ? "ON" : "OFF");
u8g2.drawStr(30, 60, relayBuffer);

char modeBuffer[20];
snprintf(modeBuffer, sizeof(modeBuffer), "Mode: %s", getModeName(mode));
u8g2.drawStr(30, 75, modeBuffer);

u8g2.sendBuffer();
}

const char* getModeName(int mode) {
switch (mode) {
case 0: return "Normal";
case 1: return "Storm";
case 2: return "Sunset";
case 3: return "Sunrise";
default: return "Unknown";
}
}

void stormEffect() {
int flashes = random(5, 10);
for (int i = 0; i < flashes; i++) {
fill_solid(leds, NUM_LEDS, CRGB::White);
FastLED.show();
delay(random(30, 120));
clearLEDs();
delay(random(20, 100));
}
}

void sunsetEffect() {
for (int i = 0; i < 256; i += 2) {
fill_solid(leds, NUM_LEDS, CRGB(i, i / 3, 0));
FastLED.show();
delay(40);
}
}

void sunriseEffect() {
for (int i = 255; i >= 0; i -= 2) {
fill_solid(leds, NUM_LEDS, CRGB(i, i, i / 2));
FastLED.show();
delay(40);
}
}

void clearLEDs() {
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
}

Finished Terrarium

490975602_537907189370029_2406654236112125403_n.jpg

Finished project:

After completing all of the steps above, the terrarium is ready. You can now watch the plants thrive and code new features to make this project even more fascinating. Some examples include time-based light controlling, a web interface, a light sensor, or a pump misting system. If you have any questions or inquiries, please comment or DM me — I will try to answer them as soon as possible. I encourage commenting and sharing useful features made by other makers :)