ESP32 Bench
Boards: ESP32, S2, S3, C3, C6
Flash and monitor: Web Serial
← All projects

Talking plant

Give a house plant a voice. A soil moisture sensor tells you when it is thirsty, and the plant says so on the serial port.

Starter15 min115200 baud
Board
Fig. 1. Pins this project uses on the ESP32-DevKitC V4, 38 pin. USB connector at the bottom.

Wiring

FromTo boardNote
Sensor VCC3V33.3 V keeps the analog output inside the ESP32's range
Sensor GNDGND
Sensor AOUTGPIO 34An ADC1 pin

Tested on this board

Not run on this board yet. Treat the wiring and code as a first draft.

Firmware, Arduino sketch for ESP32 Dev Module

// Talking plant: a capacitive soil sensor gives the plant a voice.
const int SOIL = 34;       // an ADC1 pin
// Calibrate: read the raw value with the sensor in dry air, then in a glass of water.
const int AIR_RAW = 2800;
const int WATER_RAW = 1200;
const unsigned long EVERY_MS = 10000;

enum Mood { PARCHED, THIRSTY, HAPPY, SOGGY, UNKNOWN };
Mood mood = UNKNOWN;

int readRaw() {
  long sum = 0;
  for (int i = 0; i < 16; i++) {
    sum += analogRead(SOIL);
    delay(5);
  }
  return sum / 16;
}

Mood moodFor(int pct) {
  // A few percent of slack around the extremes, so the plant does not dither.
  const int slack = 3;
  if (mood == PARCHED && pct < 20 + slack) return PARCHED;
  if (mood == SOGGY && pct > 85 - slack) return SOGGY;
  if (pct < 20) return PARCHED;
  if (pct < 40) return THIRSTY;
  if (pct <= 85) return HAPPY;
  return SOGGY;
}

void say(Mood next) {
  if (next == HAPPY && (mood == PARCHED || mood == THIRSTY)) Serial.println("Plant: Ahh, that's better. Thank you!");
  else if (next == HAPPY) Serial.println("Plant: Feeling good.");
  else if (next == THIRSTY) Serial.println("Plant: I could use a drink soon.");
  else if (next == PARCHED) Serial.println("Plant: I'm parched! Water, please.");
  else Serial.println("Plant: Glub. That's plenty, my roots need air too.");
}

void setup() {
  Serial.begin(115200);
  delay(500);
  analogReadResolution(12);
  Serial.println("Talking plant. Calibrate AIR_RAW and WATER_RAW in the sketch for your sensor.");
}

void loop() {
  int raw = readRaw();
  int pct = constrain(map(raw, AIR_RAW, WATER_RAW, 0, 100), 0, 100);
  Serial.printf("moisture=%d%% raw=%d\n", pct, raw);
  Mood next = moodFor(pct);
  if (next != mood) {
    say(next);
    mood = next;
  }
  delay(EVERY_MS);
}

Parts

QuantityPartWhere to buy
1×ESP32 dev boardBuy at Amazon
1×Capacitive soil moisture sensor, v1.2 or similar, with analog output
3×Jumper wires

Amazon buy links may include an Associates tag, and this site may earn a commission from qualifying Amazon purchases at no extra cost to you. Adafruit links are direct.

Libraries

None beyond the ESP32 Arduino core.

Load it

  1. In the Arduino IDE, install the esp32 board package and pick the board named above the sketch.
  2. Paste the sketch, choose the board's port and upload. Hold BOOT if the upload stalls at Connecting.
  3. Close the IDE's serial monitor, then open the bench here to watch the output.

Field notes

  • Every sensor reads a little differently. Set AIR_RAW and WATER_RAW to the raw values you see in dry air and in a glass of water, and the percentages start to mean something.
  • Wetter soil gives a lower raw number on capacitive sensors, which is why WATER_RAW is the smaller one.
  • Only push the sensor in up to the line printed on it. The electronics at the top are not waterproof; a coat of nail varnish or hot glue on the edges helps it last.
  • On a battery, read once and go to deep sleep for half an hour with esp_sleep_enable_timer_wakeup() and esp_deep_sleep_start(). Power the sensor from a GPIO so it is off while the board sleeps.
  • The sensor is on an ADC1 pin, so the same wiring keeps working if you add Wi-Fi later.

Not tested yet

  • Not tested on hardware yet. Compiles for every board listed with Arduino-ESP32 core 3.3.12.

Built it? Send a build report with your board and what the serial port printed. Working reports become the "Tested on this board" entries above.