Sharjah, UAE GST
Home/ Kits/ ESP32 Plant Monitoring Learning Kit
Kit Intermediate 90-150 minutes; calibration and repeat tests extra 7 parts 7 projects 14-step build guide

ESP32 Plant Monitoring Learning Kit

AED 79.00 3 built and ready to ship

Buying these 7 parts one by one costs AED 132.33 — the kit saves you AED 53.33 (40%).

Low-voltage educational prototype - age 14+ with adult supervision. Disconnect USB before changing any wire. Power the required DIY-002, DIY-025, and DIY-018 sensors from ESP32 3V3 and keep every GPIO signal at or below 3.3 V; never apply 5 V to an ESP32 GPIO.

  • Keep water physically separated from the breadboard, ESP32, BME280, jumper ends, USB connector, and computer.
  • Use the conductive water sensor only for brief supervised tests. Power down, rinse only the sensing tracks, and dry them after use; continuous DC exposure accelerates corrosion.
  • This kit does not include or support a pump, mains switching, battery wiring, automatic watering, unattended monitoring, or a weatherproof installation.
  • The optional DIY-016 is a 5 V buzzer. Do not connect it during the main build; its current, trigger threshold, active level, and driver arrangement require separate physical confirmation.
  • The firmware was compiler-checked, not physically bench-tested with the exact catalog assembly. Verify labels, board fit, signal voltages, BME address, and repeatable calibration before release.
ESP32 Plant Monitoring Learning Kit
KIT-5 · ESP32

Build a USB-powered ESP32 learning prototype that calibrates a capacitive soil sensor and a short-duration water sensor, reads a 3.3 V BME280, and reports local Serial Monitor data. The checked firmware includes guided calibration and fault handling; no pump, Wi-Fi dashboard, unattended control, or verified buzzer driver is included.

Difficulty
Intermediate
Build time
90-150 minutes; calibration and repeat tests extra
Parts included
7 components
Build guide
14 steps

Included parts (7)

7 parts packed in this kit

Lost or damaged a part? Every part name below opens its own page, so you can buy a replacement or an extra on its own.

Substitutions
Need a part swapped?
Any component here can be substituted — a different board, a larger breadboard, extra sensors for a classroom set.
Ask for a variant

7 projects in this kit

Every one is built from the parts in the box, plus the basics listed under You'll also need.

Sensing 01

Calibrated plant-monitoring lab

Build the reviewed main project, capture four local calibration endpoints, and stream soil, water, temperature, humidity, pressure, and status data to Serial Monitor. This is the supported core lesson; physical verification of the exact catalog assembly is still required.

ESP32U DIY-002 capacitive soil sensor DIY-025 water sensor DIY-018 BME280 full-size breadboard jumper wires
Parts, guide & add to cart →
Sensing 02

Soil-moisture calibration study

Compare raw ADC readings across documented dry, damp, and wet samples of one soil type. Repeat probe depth and compaction, graph the results, and explain why the percentages are relative to the chosen endpoints rather than universal soil-moisture values.

ESP32U DIY-002 capacitive soil sensor breadboard jumper wires user-supplied dry and wet soil references
Parts, guide & add to cart →
Sensing 03

Short-duration water-level experiment

Mark several safe test depths, energize only long enough to record stable readings, then power down, rinse, and dry the tracks. Study repeatability and corrosion risk; do not use this board as a permanent reservoir sensor.

ESP32U DIY-025 conductive water sensor breadboard jumper wires user-supplied shallow non-metallic container and clean water
Parts, guide & add to cart →
Basics 04

BME280 room environment station

Measure temperature, relative humidity, and pressure near a plant while keeping the module dry and away from direct sun, water, fingers, and warm electronics. Compare settling time and plausible room ranges rather than claiming laboratory accuracy.

ESP32U DIY-018 BME280 breadboard jumper wires
Parts, guide & add to cart →
Basics 05

Serial data logger and charts

Capture the sketch's machine-readable DATA lines, import them into a spreadsheet, and chart how the references change after watering or as a test sample dries. Add timestamps on the computer side; the kit has no real-time clock.

Completed main build computer USB data cable spreadsheet software
Parts, guide & add to cart →
Basics 06

Optional audible-alert investigation

Characterize the buzzer's current, trigger threshold, and active level before choosing a transistor or driver. Write and compile a separate alert sketch only after the interface is reviewed. This project is not wired or implemented by the supplied main firmware.

Optional DIY-016 5 V buzzer ESP32U and additional user-supplied driver/protection parts selected after measurement
Parts, guide & add to cart →
Network 07

Future Wi-Fi dashboard

Add networking only after the USB/Serial build is stable. Keep credentials out of shared code and review privacy, reconnection behavior, power, enclosure, and unattended-operation risks. No cloud service or dashboard is included.

Completed local main build and user-supplied Wi-Fi network
Parts, guide & add to cart →
Build guide - ESP32 - Intermediate

ESP32 Plant Monitoring Learning Kit

Build a USB-powered learning prototype that calibrates two analog sensors, identifies a BME280, stores reference points, and reports local plant and room data in Serial Monitor.

7 learning projects6 required catalog items90-150 minutes3.3 V logic

01What you will build

Main learning build

The ESP32 reads a capacitive soil sensor on GPIO34, a short-duration conductive water sensor on GPIO35, and a 3.3 V BME280 on the GPIO21/22 I2C bus. The supplied program averages measurements, guides calibration, stores four endpoints in non-volatile memory, and prints data every two seconds.

Clear scope

This is not an automatic irrigation controller, weatherproof product, calibrated scientific instrument, plant-care guarantee, or unattended alarm. No pump, valve, relay, enclosure, battery, cloud service, Wi-Fi dashboard, or verified buzzer driver is part of the main build.

Verification level. The firmware was compiler-checked for the ESP32 target in normal and diagnostic modes. The exact catalog parts, physical pin labels, board fit, signal voltages, sensor accuracy, and assembled wiring have not been bench-tested together. Complete the release checklist before describing the kit as physically verified.

02What is included

QtyCatalog itemPurpose
1ESP32U development boardMain controller, USB programming, two ADC1 inputs, and I2C.
1DIY-002 capacitive soil moisture sensor v2.0Analog soil reference. Calibrate it in your own dry and wet materials.
1DIY-025 conductive water-level sensorBrief supervised water-contact experiment only; rinse and dry after use.
1DIY-018 BME280 module3.3 V I2C temperature, humidity, and pressure readings.
1DIY-295 full-size 830-point breadboardMore working room than a half-size board for the wide ESP32 and sensor wiring.
1 packDIY-212 assorted jumper wiresMale-to-male, male-to-female, and female-to-female connections.
Optional extension: DIY-016 5 V active buzzer. It is not driven by the checked main firmware. Before connecting it, measure or confirm module current, trigger threshold, active-high/active-low behavior, and a suitable transistor or driver stage. Never put 5 V on an ESP32 GPIO.

03What you also need

Computer and software

Computer with Arduino IDE 2.x, Arduino-ESP32 board support, Adafruit BME280, Adafruit Unified Sensor, and Adafruit BusIO libraries, plus permission to install them.

USB and test references

A known-good USB-C data cable, dry and wet soil references, a shallow non-metallic container, a small amount of clean water, labels, notebook, and spreadsheet software if you want charts.

Safety and cleanup

Dry work tray, paper towels, clean rinse water, eye protection, and adult supervision for learners under 14.

Recommended test tool

A digital multimeter for checking 3.3 V, ground continuity, rail breaks, and analog signal voltage before an ESP32 GPIO is connected.

A soldering iron, solder, stand, ventilation, and supervision are needed only if a supplied module arrives with loose headers. Never solder a powered circuit.

04Reviewed pin plan

Disconnect USB before changing wires. Verify the labels printed on the exact modules because pin order can vary between revisions. Power all three required sensors from the ESP32 3V3 pin and join every ground.

Part pinESP32UPurpose and check
DIY-002 VCC3V3Keeps the analog output in the ESP32 voltage domain.
DIY-002 GNDGNDCommon reference.
DIY-002 AOUTGPIO34Input-only ADC1 pin; verify signal remains 0-3.3 V.
DIY-025 + / VCC3V3Use 3.3 V for this ESP32 experiment, not 5 V.
DIY-025 - / GNDGNDCommon reference.
DIY-025 S / AOGPIO35Input-only ADC1 pin; verify signal remains 0-3.3 V.
DIY-018 VIN / VCC3V3The linked module is specified for 1.8-3.6 V logic.
DIY-018 GNDGNDCommon reference.
DIY-018 SDAGPIO21I2C data used by the sketch.
DIY-018 SCLGPIO22I2C clock used by the sketch.
Stop before power if you cannot identify a pin, a wire can touch water, a sensor output may exceed 3.3 V, an ESP32 header is misaligned, or a rail is connected to both 5 V and 3.3 V. Do not immerse the ESP32, breadboard, jumper ends, BME280, USB connector, or the electronics above a sensor's marked sensing area.

05Step-by-step main build

Work in this order. Disconnect USB before every wiring change and add one subsystem at a time.

  1. Inspect and identify every part. Match physical labels to the included list and photograph both sides. Do not guess an unlabeled power or signal pin.
  2. Check the breadboard rails. Determine whether each long rail is split in the middle. Bridge only the 3.3 V and GND sections you intend to use.
  3. Test the ESP32 alone. Keep all sensors disconnected. Use a data-capable USB-C cable, choose the matching ESP32 profile and port, and confirm a basic serial upload.
  4. Position the ESP32. Straddle the center gap only if both header rows enter freely and usable rows remain. If this revision is too wide, place it beside the breadboard and use female-to-male jumpers.
  5. Create the 3.3 V rails. Connect 3V3 and GND. Power briefly with no sensors and measure approximately 3.3 V across the intended rails, then disconnect USB.
  6. Add the BME280 first. Connect 3V3, GND, SDA to GPIO21, and SCL to GPIO22. Upload the supplied firmware and open Serial Monitor at 115200 baud. Continue only when exactly one BME280 is found at 0x76 or 0x77.
  7. Add the soil sensor. Disconnect USB. Connect DIY-002 VCC to 3V3, GND to GND, and AOUT to GPIO34. Keep the connector and electronics dry.
  8. Check soil raw values. Compare readings in air, a documented dry reference, and a documented wet reference. Values must change consistently and must not remain fixed near 0 or 4095.
  9. Add the water sensor while dry. Disconnect USB. Connect DIY-025 VCC to 3V3, GND to GND, and S/AO to GPIO35. Take the dry baseline first.
  10. Perform a brief water test. Lower only the exposed sensing tracks to a marked depth for a few seconds. Keep the connector and every other electronic part dry. Power down, rinse only the tracks, and dry after capture.
  11. Capture four calibration points. At stable references, send soil-dry, soil-wet, water-empty, and water-full.
  12. Validate calibration. Send status. The program requires all four endpoints and a span of at least 150 ADC counts on each sensor.
  13. Start monitoring. Send run. Confirm DATA lines include raw values, relative percentages, BME readings, and a status. The program must refuse run when calibration is invalid.
  14. Repeat and document. Run at least three dry/wet and empty/marked-depth transitions. Record direction, raw range, percentage, settling time, BME address, resets, and repeatability.
  15. Power down and store dry. Disconnect USB before moving wires. Remove soil gently, rinse and dry the conductive tracks, and keep all electronics away from moisture.

06Firmware setup

Arduino settings

Select the exact matching ESP32 development-board profile and port. Use Serial Monitor at 115200 baud with newline enabled. Install Adafruit BME280, Unified Sensor, and BusIO.

Built-in checks

The sketch discovers one BME280 at 0x76/0x77, averages 24 ADC samples, saves calibration in ESP32 preferences, rejects small calibration spans, and enters a fault after three invalid BME readings.

CommandWhen to use itResult
soil-drySoil probe is in your documented dry reference.Saves the 0% soil endpoint.
soil-wetProbe is stable in your documented wet reference.Saves the 100% soil endpoint.
water-emptyConductive tracks are clean and completely dry.Saves the 0% water endpoint.
water-fullTracks are at your marked short-test depth.Saves the 100% water endpoint.
statusAfter wiring, calibration, or troubleshooting.Shows mode, BME address, endpoints, and fault reason.
runAll four endpoints are valid.Starts calibrated monitoring.
calibrateYou need raw readings again.Returns to calibration mode.
reset-calYou changed soil, sensor depth, supply, or setup.Erases saved endpoints.
helpAny time.Prints the command list.
Understand the percentages. They are relative to your two reference endpoints, not absolute volumetric water content or a laboratory water-level measurement. The 30% dry-soil and 20% low-water thresholds are teaching examples, not advice for a particular plant.

07How to judge a successful build

  1. Stable boardThe ESP32 uploads reliably and does not reset when required sensors are added one at a time.
  2. One BME280The program finds exactly one module at 0x76 or 0x77 and reports plausible room readings.
  3. Usable analog rangeNeither channel stays near 0 or 4095, and each calibrated span is at least 150 counts.
  4. Correct control flowrun is rejected before valid calibration and accepted afterward.
  5. RepeatabilityThree trials show the same direction of change and reasonably similar endpoints.
  6. Dry, cool hardwareNo corrosion, heat, smell, loose connection, or water exposure appears on the electronics.
P1
Main project

Calibrated plant monitoring lab

Uses: all six required catalog items, plus the customer-supplied USB cable and test references.

Complete the staged build, capture four local endpoints, and stream soil, water, temperature, humidity, pressure, and status data. This is the supported core lesson. Physical verification of the exact catalog assembly is still required.

P2
Experiment

Soil moisture calibration study

Uses: ESP32U, DIY-002, full-size breadboard, jumpers, and dry/damp/wet references.

Graph raw ADC readings across repeatable samples. Compare probe depth, compaction, and settling time. Explain why copied thresholds are unreliable when soil, position, sensor coating, or supply changes.

P3
Experiment

Short-duration water-level study

Uses: ESP32U, DIY-025, breadboard, jumpers, shallow non-metallic container, and clean water.

Record several marked depths, then rinse and dry after each run. Study non-linearity, residue, drift, and corrosion risk instead of treating this exposed-track board as a permanent tank sensor.

P4
Environment project

BME280 room station

Uses: ESP32U, DIY-018, breadboard, and jumpers.

Measure temperature, humidity, and pressure near the plant while keeping the sensor dry, shaded, and away from fingers and warm electronics. Compare with a trusted room reference after settling; do not claim laboratory accuracy.

P5
Data project

Serial data logger and charts

Uses: completed main build, computer, USB data cable, and spreadsheet software.

Capture the machine-readable DATA lines, add timestamps on the computer side, and graph how a sample changes after watering or while drying. The kit does not contain a real-time clock.

P6
Optional investigation

Audible alert interface

Uses: optional DIY-016 plus additional measured driver/protection parts selected after testing.

Characterize the 5 V module's current, trigger threshold, and active level before choosing a transistor or driver. Write and compile a separate alert sketch after interface review. The main firmware intentionally does not wire or drive this module.

P7
Future extension

Wi-Fi dashboard

Uses: completed local build and a customer-supplied network.

Add networking only after the USB/Serial prototype is stable. Keep private credentials out of shared code and review privacy, reconnection behavior, enclosure, power, and unattended-operation risks. No cloud service or dashboard is included.

08Troubleshooting

SymptomChecks
No port or upload failsUse a data USB cable, install the correct ESP32 core/driver, select the correct port, close other serial tools, and retry with sensors disconnected.
Need exactly one BME280Check 3.3 V, GND, SDA, SCL, soldered headers, and address. A response at a different address may be a different sensor.
BME values are invalidKeep the module dry and away from fingers or board heat; check address, supply, and wiring, then allow time to settle.
ADC reads 0 or 4095Power down. Check signal and ground continuity, confirm 3.3 V supply, and measure the analog output before reconnecting GPIO34/35.
Calibration span under 150Use more distinct and stable references, keep position consistent, clean/dry the sensor, send reset-cal, and repeat.
Percentage moves backwardEndpoints were captured in reverse or under unstable conditions. Reset and recapture; the firmware accepts either raw direction when endpoints are correct.
ESP32 resets after adding a partRemove all parts, inspect for shorts or 5 V connections, measure the 3.3 V rail, and add one required sensor at a time. Leave the optional buzzer disconnected.
Water baseline driftsResidue and retained moisture conduct. Power down, rinse only the sensing tracks, dry fully, and recalibrate if baseline changed.

09Safety and intended use

Low-voltage educational prototype only. Keep mains electricity, pumps, high-current loads, and lithium battery wiring outside this build. Work with USB disconnected while changing wires. Power required sensors from 3.3 V. Never apply 5 V to an ESP32 GPIO.

Keep water physically separated from the breadboard, ESP32, BME280, USB connector, computer, and jumper ends. Use the conductive sensor only for brief supervised experiments because continuous DC exposure accelerates electrolysis and corrosion. The capacitive soil sensor's electronics are not guaranteed waterproof. Stop using any cracked, corroded, hot, or unstable part.

10Release checklist

  1. Exact assemblyBuild with the exact linked catalog items and photograph the final layout.
  2. Mechanical fitConfirm ESP32 header spacing and usable breadboard rows without forcing pins.
  3. Electrical limitsVerify labels, continuity, 3.3 V rail, and every required signal at or below 3.3 V.
  4. Sensor identityRecord the BME280 address and compare readings with a trusted room reference.
  5. Repeat trialsComplete three soil and water transitions, record endpoints, drift, and settling time.
  6. Media reviewReplace any photo showing a half-size breadboard or implying the optional buzzer is part of the checked main build.
Compiler-check record. The supplied 289-line source was compiled for ESP32 core 3.3.11 in normal and PLANT_DIAGNOSTIC=true modes with Adafruit BME280 2.3.0, BusIO 1.17.4, and Unified Sensor 1.1.15. Compilation confirms source/toolchain compatibility, not physical wiring or sensor performance.

Images are visual representations only. Component appearance, pin order, connector style, and board revision may vary. Always follow labels and specifications on the supplied part.

Build steps (14)

Tick steps off as you go — your place is kept on this device.

0 / 14
These steps come from the build reference recorded for this kit. The firmware is in the Code section below.

Code

The sketch we ship with this kit. Copy it, or ask us for the version matched to your board.

1
289
arduino
ESP32 plant monitoring learning lab (PlantMonitorLearningLab.ino)
#include <Arduino.h>
#include <Wire.h>
#include <Preferences.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

// Educational bench prototype. Not a plant-care guarantee or unattended controller.
#ifndef PLANT_DIAGNOSTIC
#define PLANT_DIAGNOSTIC false
#endif

constexpr uint8_t SDA_PIN = 21;
constexpr uint8_t SCL_PIN = 22;
constexpr uint8_t SOIL_PIN = 34;
constexpr uint8_t WATER_PIN = 35;
constexpr uint32_t SAMPLE_INTERVAL_MS = 2000;
constexpr uint8_t ADC_SAMPLES = 24;
constexpr int MIN_CALIBRATION_SPAN = 150;
constexpr int DRY_SOIL_PERCENT = 30;
constexpr int LOW_WATER_PERCENT = 20;

Adafruit_BME280 bme;
Preferences preferences;

enum class Mode { Calibration, Monitoring, Fault };

struct Calibration {
  int soilDry = -1;
  int soilWet = -1;
  int waterEmpty = -1;
  int waterFull = -1;
};

Calibration calibration;
Mode mode = Mode::Fault;
uint8_t bmeAddress = 0;
uint8_t bmeFailures = 0;
uint32_t lastSample = 0;
String commandBuffer;
String faultReason;

bool elapsed(uint32_t now, uint32_t since, uint32_t interval) {
  return uint32_t(now - since) >= interval;
}

bool i2cAck(uint8_t address) {
  Wire.beginTransmission(address);
  return Wire.endTransmission() == 0;
}

int readAverage(uint8_t pin) {
  uint32_t sum = 0;
  for (uint8_t i = 0; i < ADC_SAMPLES; ++i) {
    sum += analogRead(pin);
    delay(2);
  }
  return int((sum + ADC_SAMPLES / 2) / ADC_SAMPLES);
}

bool validEndpoint(int value) {
  return value >= 0 && value <= 4095;
}

bool calibrationValid() {
  return validEndpoint(calibration.soilDry) &&
         validEndpoint(calibration.soilWet) &&
         validEndpoint(calibration.waterEmpty) &&
         validEndpoint(calibration.waterFull) &&
         abs(calibration.soilWet - calibration.soilDry) >= MIN_CALIBRATION_SPAN &&
         abs(calibration.waterFull - calibration.waterEmpty) >= MIN_CALIBRATION_SPAN;
}

int calibratedPercent(int raw, int zeroPoint, int hundredPoint) {
  long span = long(hundredPoint) - long(zeroPoint);
  if (span == 0) return 0;
  long scaled = (long(raw) - long(zeroPoint)) * 100L / span;
  return constrain(int(scaled), 0, 100);
}

void saveCalibration() {
  preferences.putInt("soilDry", calibration.soilDry);
  preferences.putInt("soilWet", calibration.soilWet);
  preferences.putInt("waterEmpty", calibration.waterEmpty);
  preferences.putInt("waterFull", calibration.waterFull);
}

void loadCalibration() {
  calibration.soilDry = preferences.getInt("soilDry", -1);
  calibration.soilWet = preferences.getInt("soilWet", -1);
  calibration.waterEmpty = preferences.getInt("waterEmpty", -1);
  calibration.waterFull = preferences.getInt("waterFull", -1);
}

void printCommands() {
  Serial.println(F("Commands:"));
  Serial.println(F("  soil-dry    capture the soil probe in air or verified dry reference soil"));
  Serial.println(F("  soil-wet    capture the soil probe in verified wet reference soil"));
  Serial.println(F("  water-empty capture the water sensor clean and dry"));
  Serial.println(F("  water-full  capture it at your marked short-duration test depth"));
  Serial.println(F("  run         start monitoring when all four calibration points are valid"));
  Serial.println(F("  calibrate   return to raw-reading mode"));
  Serial.println(F("  status      show addresses, calibration values and mode"));
  Serial.println(F("  reset-cal   erase saved calibration"));
  Serial.println(F("  help        show this list"));
}

const __FlashStringHelper *modeName() {
  if (mode == Mode::Calibration) return F("CALIBRATION");
  if (mode == Mode::Monitoring) return F("MONITORING");
  return F("FAULT");
}

void printStatus() {
  Serial.print(F("Mode="));
  Serial.print(modeName());
  Serial.print(F(" BME=0x"));
  if (bmeAddress < 0x10) Serial.print('0');
  Serial.println(bmeAddress, HEX);
  Serial.printf("Calibration soilDry=%d soilWet=%d waterEmpty=%d waterFull=%d valid=%s\n",
                calibration.soilDry, calibration.soilWet,
                calibration.waterEmpty, calibration.waterFull,
                calibrationValid() ? "yes" : "no");
  if (mode == Mode::Fault) {
    Serial.print(F("Fault="));
    Serial.println(faultReason);
  }
}

void enterFault(const __FlashStringHelper *reason) {
  mode = Mode::Fault;
  faultReason = String(reason);
  Serial.print(F("FAULT: "));
  Serial.println(reason);
}

void capturePoint(const String &command) {
  if (command == "soil-dry") {
    calibration.soilDry = readAverage(SOIL_PIN);
    Serial.printf("Captured soilDry=%d\n", calibration.soilDry);
  } else if (command == "soil-wet") {
    calibration.soilWet = readAverage(SOIL_PIN);
    Serial.printf("Captured soilWet=%d\n", calibration.soilWet);
  } else if (command == "water-empty") {
    calibration.waterEmpty = readAverage(WATER_PIN);
    Serial.printf("Captured waterEmpty=%d\n", calibration.waterEmpty);
  } else if (command == "water-full") {
    calibration.waterFull = readAverage(WATER_PIN);
    Serial.printf("Captured waterFull=%d\n", calibration.waterFull);
  }
  saveCalibration();
  Serial.println(calibrationValid()
                     ? F("Calibration spans are valid. Type run to monitor.")
                     : F("Calibration incomplete or a span is under 150 counts."));
}

void handleCommand(String command) {
  command.trim();
  command.toLowerCase();
  if (command.length() == 0) return;

  if (command == "help") {
    printCommands();
  } else if (command == "status") {
    printStatus();
  } else if (command == "soil-dry" || command == "soil-wet" ||
             command == "water-empty" || command == "water-full") {
    if (mode == Mode::Fault) {
      Serial.println(F("Resolve the hardware fault before calibration."));
    } else {
      mode = Mode::Calibration;
      capturePoint(command);
    }
  } else if (command == "run") {
    if (mode == Mode::Fault) {
      Serial.println(F("Resolve the hardware fault before monitoring."));
    } else if (!calibrationValid()) {
      Serial.println(F("Cannot monitor: capture four valid calibration points first."));
    } else {
      mode = Mode::Monitoring;
      Serial.println(F("Monitoring started. Thresholds are teaching defaults, not plant-specific advice."));
    }
  } else if (command == "calibrate") {
    if (mode != Mode::Fault) {
      mode = Mode::Calibration;
      Serial.println(F("Calibration mode: raw values will be printed."));
    }
  } else if (command == "reset-cal") {
    preferences.clear();
    calibration = Calibration{};
    if (mode != Mode::Fault) mode = Mode::Calibration;
    Serial.println(F("Saved calibration erased."));
  } else {
    Serial.println(F("Unknown command. Type help."));
  }
}

void readSerialCommands() {
  while (Serial.available()) {
    char c = char(Serial.read());
    if (c == '\r') continue;
    if (c == '\n') {
      handleCommand(commandBuffer);
      commandBuffer = "";
    } else if (commandBuffer.length() < 63) {
      commandBuffer += c;
    }
  }
}

void sampleAndReport() {
  int soilRaw = readAverage(SOIL_PIN);
  int waterRaw = readAverage(WATER_PIN);
  float temperature = bme.readTemperature();
  float humidity = bme.readHumidity();
  float pressure = bme.readPressure() / 100.0f;

  if (isnan(temperature) || isnan(humidity) || isnan(pressure)) {
    if (++bmeFailures >= 3) enterFault(F("BME280 read failed three times"));
    else Serial.println(F("WARN: invalid BME280 reading"));
    return;
  }
  bmeFailures = 0;

  if (mode == Mode::Calibration) {
    Serial.printf("CAL soil_raw=%d water_raw=%d temp_c=%.1f humidity_pct=%.1f pressure_hpa=%.1f\n",
                  soilRaw, waterRaw, temperature, humidity, pressure);
    return;
  }
  if (mode != Mode::Monitoring) return;

  int soilPercent = calibratedPercent(soilRaw, calibration.soilDry, calibration.soilWet);
  int waterPercent = calibratedPercent(waterRaw, calibration.waterEmpty, calibration.waterFull);
  bool drySoil = soilPercent < DRY_SOIL_PERCENT;
  bool lowWater = waterPercent < LOW_WATER_PERCENT;
  const char *attention = drySoil && lowWater ? "DRY_SOIL+LOW_WATER" :
                          drySoil ? "DRY_SOIL" :
                          lowWater ? "LOW_WATER" : "OK";

  Serial.printf("DATA soil_raw=%d soil_pct=%d water_raw=%d water_pct=%d temp_c=%.1f humidity_pct=%.1f pressure_hpa=%.1f status=%s\n",
                soilRaw, soilPercent, waterRaw, waterPercent,
                temperature, humidity, pressure, attention);
}

void setup() {
  Serial.begin(115200);
  delay(200);
  pinMode(SOIL_PIN, INPUT);
  pinMode(WATER_PIN, INPUT);
  analogReadResolution(12);
  analogSetPinAttenuation(SOIL_PIN, ADC_11db);
  analogSetPinAttenuation(WATER_PIN, ADC_11db);

  if (!Wire.begin(SDA_PIN, SCL_PIN, 100000)) {
    enterFault(F("I2C start failed"));
    return;
  }

  bool found76 = i2cAck(0x76);
  bool found77 = i2cAck(0x77);
  if (found76 == found77) {
    enterFault(F("Need exactly one BME280 at 0x76 or 0x77"));
    return;
  }
  bmeAddress = found76 ? 0x76 : 0x77;
  if (!bme.begin(bmeAddress, &Wire)) {
    enterFault(F("Device at BME address did not initialise"));
    return;
  }

  preferences.begin("plant-lab", false);
  loadCalibration();
  mode = PLANT_DIAGNOSTIC || !calibrationValid() ? Mode::Calibration : Mode::Monitoring;

  Serial.println(F("ESP32 PLANT MONITORING LEARNING LAB"));
  Serial.println(F("USB/3.3 V bench build; no pump, buzzer, Wi-Fi or unattended control."));
  Serial.println(F("The conductive water sensor is for short supervised tests; rinse and dry it after use."));
  printStatus();
  printCommands();
}

void loop() {
  readSerialCommands();
  if (mode == Mode::Fault) return;

  uint32_t now = millis();
  if (!elapsed(now, lastSample, SAMPLE_INTERVAL_MS)) return;
  lastSample = now;
  sampleAndReport();
}
Pin numbers and thresholds are written for the board in this kit. Change the board and the pin map changes with it — ask and we'll adjust it.

What this kit teaches

Beginner ESP32 kit for building a smart plant monitor and alert system: read soil moisture, check water level, measure temperature and humidity with BME280, and sound a buzzer when the plant needs attention. Designed for students, home makers, and small IoT demos.

Difficulty · Intermediate

Assumes you have built a simple circuit before and can follow a wiring guide. You will install a toolchain and upload code.

You'll also need
  • Computer with Arduino IDE 2.x and permission to install ESP32 board support and libraries
  • Known-good USB-C data cable with data lines; charge-only cables cannot upload
  • Adafruit BME280 Library, Adafruit Unified Sensor, and Adafruit BusIO
  • Small plant pot or cups containing documented dry and wet soil references
  • Shallow non-metallic container and a small amount of clean water for a brief supervised sensor test
  • Paper towels, clean rinse water, a dry work tray, labels, notebook, and spreadsheet if logging data
  • Digital multimeter strongly recommended for 3.3 V, continuity, and sensor-output checks
  • Eye protection and adult supervision for learners under 14
  • Soldering iron, solder, stand, ventilation, and adult supervision only if a supplied module arrives with loose headers
  • Optional buzzer extension additionally needs a measured, suitable transistor/driver and protection components; these ar…
Who it's for

Catalogued as suited to classroom. Built around DIYKIT catalog bundle hardware. Classroom sets are quoted per seat — ask for a bulk price.

How it compares

KitLevelPartsBuild guidePrice
This kit ESP32 Plant Monitoring Learning Kit Intermediate 7 14 steps AED 79.00
Arduino UNO Sensor & Output Starter Kit - LED… Beginner 10 10 steps AED 85.00
ESP32 ECG & Optical Pulse Learning Kit Advanced 6 14 steps AED 101.50
ESP32 Fall-Detection Learning Prototype Intermediate 7 10 steps AED 80.99
Distance & Parking Sensor Kit Beginner 9 1 steps On request

Frequently asked

This kit is rated Intermediate. You should have built a simple circuit before and be comfortable with breadboard wiring. It is built around the esp32 platform.

90-150 minutes; calibration and repeat tests extra. The build guide below breaks it into 14 steps.

All 7 components listed in the parts table are packed in the kit. Current availability depends on finished kit stock and component restock status.

Recorded in our catalogue as suited to classroom. Bulk pricing is available for classroom sets.

Root
Online — usually replies instantly
Catalog assistant: verify final stock/price on the product page before checkout.