Woman viewing a PlantMate soil moisture sensor connected to an Arduino Nano and OLED beside a healthy potted plant

PlantMate 3.3V Soil-Moisture Sensor: Arduino Nano and OLED Demo

Build a simple live soil-moisture display with the PlantMate 3.3 V capacitive soil moisture sensor, a classic Arduino Nano, and a 0.91-inch 128 x 32 OLED. The display shows the averaged raw reading, a calibrated relative moisture percentage, DRY/MOIST/WET status, and a live bar graph.

Watch the complete demo

The video follows the sensor from a dry reading of about 824 to a wet reference of about 413, then shows the sensor responding after it is installed inside a plant pot. These are example calibration values from this demonstration, not universal values for every sensor, plant, soil, or pot.

Why choose this capacitive moisture sensor?

  • Capacitive sensing: The dual-sided sensing region responds to the surrounding soil without exposed resistive probe traces, which helps reduce the corrosion problems commonly associated with fork-style resistive sensors.
  • Designed for 3.3 V: The sensor runs directly from 3.3 V and does not waste battery energy in an onboard 5 V to 3.3 V regulator.
  • Long 150 cm cable: The extra reach makes it easier to keep the controller and display away from watering areas.
  • Flexible connection: The Micro-USB male cable works with the supplied female adapter. The cable can also be cut and connected directly to a controller after every conductor is identified.
  • Analog output: The 0 to 3 V signal can be read by a suitable ADC on boards such as the classic Arduino Nano, ESP32, and Raspberry Pi Pico.

Important: Power the PlantMate sensor from 3.3 V DC only. Never apply 5 V. The sensor’s Micro-USB connector carries 3.3 V power and analog signals for this product. Never plug it into a computer, charger, power bank, or the Nano’s USB socket.

Parts required

Electronics

  1. PlantMate 3.3 V capacitive soil moisture sensor with integrated 150 cm cable and Micro-USB male connector
  2. PlantMate Micro-USB female adapter and its supplied header, if soldering is required
  3. Classic Arduino Nano with the ATmega328P processor
  4. 0.91-inch 128 x 32 SSD1306 I2C OLED at address 0x3C
  5. Solderless breadboard
  6. Male-to-male jumper wires
  7. A breadboard power rail or proper three-way junction for the 3.3 V distribution point
  8. USB data cable that fits the Nano
  9. Computer with the Arduino IDE

Demo and calibration items

  • Healthy potted plant with soil
  • Clear glass or container of clean water
  • Watering bottle with a narrow spout
  • Clean cloth or paper towel for drying the sensing blade

Arduino libraries and IDE settings

Open Tools > Manage Libraries in the Arduino IDE and install:

  • Adafruit GFX Library
  • Adafruit SSD1306

Adafruit BusIO is a supporting dependency and is normally installed automatically. Select Arduino Nano as the board and ATmega328P as the processor. Some compatible Nano boards require the ATmega328P (Old Bootloader) option. The Serial Monitor speed for this sketch is 115200 baud.

Wiring the PlantMate sensor to the Nano

PlantMate connectionClassic Arduino Nano
VCC3.3 V distribution rail supplied by Nano 3V3
GNDGND
D+A0
D-A0
IDLeave unconnected if exposed

Tie D+ and D- together before they reach A0. Use the terminal labels printed on the PlantMate female adapter. If you cut a cable, disconnect power first and identify every conductor. Do not assume that all cable manufacturers use the same internal wire colors.

Use the Nano’s 3.3 V rail as the external ADC reference

ConnectionDestination
Nano 3V33.3 V distribution rail
Nano AREFSame 3.3 V distribution rail
Sensor VCCSame 3.3 V distribution rail

This makes the Nano’s ADC full-scale reference match the sensor’s 3.3 V supply. Use a breadboard rail or a proper three-way junction instead of forcing two wires into one header socket.

  1. Upload the sketch while the AREF wire is disconnected.
  2. Power the Nano off.
  3. Connect Nano 3V3 to the distribution rail.
  4. Connect that rail to both sensor VCC and Nano AREF.
  5. Power the Nano again.

AREF safety: This sketch calls analogReference(EXTERNAL) before its first analogRead(). Disconnect the 3.3 V to AREF wire before using any different sketch unless that sketch also selects the external reference before reading the ADC.

OLED wiring

OLED connectionClassic Arduino Nano
VCC5V only if the exact OLED module is rated for 3.3 V to 5 V operation
GNDGND
SDAA4
SCLA5

Confirm the voltage rating of your exact OLED module before powering it from 5V. If it is a 3.3 V-only module, follow its manufacturer’s supply and I2C logic-level requirements. The PlantMate sensor and Nano AREF remain on the 3.3 V distribution rail.

Complete Arduino Nano code

Copy this sketch into a new Arduino IDE project. The values 824 and 413 reproduce the video demonstration. Replace them with readings from your own final soil, pot, depth, and sensor position.

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// PlantMate 3.3 V capacitive soil moisture sensor demo.
// Target: classic Arduino Nano with the ATmega328P processor.
// ADC reference: Nano 3.3 V pin connected to AREF.

namespace {

constexpr uint8_t SENSOR_PIN = A0;

constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 32;
constexpr uint8_t OLED_ADDRESS = 0x3C;
constexpr int8_t OLED_RESET = -1;

// CALIBRATION: Replace these example values with readings from your sensor.
// Many capacitive sensors produce a lower raw reading when the soil is wetter.
// The calculation also works if your sensor produces a higher wet reading.
constexpr int16_t DRY_RAW = 824;
constexpr int16_t WET_RAW = 413;

// These are demonstration thresholds. Tune them for the plant and soil.
constexpr uint8_t DRY_LIMIT_PERCENT = 30;
constexpr uint8_t WET_LIMIT_PERCENT = 70;

constexpr uint8_t SAMPLE_COUNT = 16;
constexpr uint16_t SAMPLE_GAP_US = 500;
constexpr uint16_t UPDATE_INTERVAL_MS = 250;

static_assert(DRY_RAW != WET_RAW,
              "DRY_RAW and WET_RAW must be different values");

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

uint16_t readAveragedRaw() {
  uint32_t total = 0;

  // Discard one conversion after startup or a long interval so the ADC sample
  // capacitor has time to settle on A0.
  analogRead(SENSOR_PIN);

  for (uint8_t sample = 0; sample < SAMPLE_COUNT; ++sample) {
    total += analogRead(SENSOR_PIN);
    delayMicroseconds(SAMPLE_GAP_US);
  }

  return static_cast<uint16_t>((total + (SAMPLE_COUNT / 2)) / SAMPLE_COUNT);
}

uint8_t rawToPercent(uint16_t rawValue) {
  const int32_t numerator =
      (static_cast<int32_t>(rawValue) - DRY_RAW) * 100L;
  const int32_t denominator = static_cast<int32_t>(WET_RAW) - DRY_RAW;
  int32_t moisturePercent = numerator / denominator;

  moisturePercent = constrain(moisturePercent, 0L, 100L);
  return static_cast<uint8_t>(moisturePercent);
}

const __FlashStringHelper* moistureLabel(uint8_t moisturePercent) {
  if (moisturePercent < DRY_LIMIT_PERCENT) {
    return F("DRY");
  }

  if (moisturePercent < WET_LIMIT_PERCENT) {
    return F("MOIST");
  }

  return F("WET");
}

void drawReading(uint16_t rawValue, uint8_t moisturePercent) {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextWrap(false);

  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print(F("PlantMate"));
  display.setCursor(72, 0);
  display.print(F("RAW:"));
  display.print(rawValue);

  display.setTextSize(2);
  display.setCursor(0, 10);
  display.print(moisturePercent);
  display.print('%');

  display.setTextSize(1);
  display.setCursor(54, 12);
  display.print(moistureLabel(moisturePercent));

  constexpr int16_t BAR_X = 52;
  constexpr int16_t BAR_Y = 23;
  constexpr int16_t BAR_WIDTH = 76;
  constexpr int16_t BAR_HEIGHT = 9;
  constexpr int16_t BAR_INNER_WIDTH = BAR_WIDTH - 4;

  display.drawRect(BAR_X, BAR_Y, BAR_WIDTH, BAR_HEIGHT, SSD1306_WHITE);
  const int16_t fillWidth =
      (static_cast<int16_t>(moisturePercent) * BAR_INNER_WIDTH + 50) / 100;
  display.fillRect(BAR_X + 2, BAR_Y + 2, fillWidth, BAR_HEIGHT - 4,
                   SSD1306_WHITE);

  display.display();
}

void showStartupScreen() {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextWrap(false);
  display.setTextSize(1);
  display.setCursor(0, 4);
  display.println(F("PlantMate 3.3 V"));
  display.println(F("Moisture Sensor"));
  display.display();
  delay(1200);
}

void stopWithDisplayError() {
  Serial.println(F("ERROR: SSD1306 OLED not found at I2C address 0x3C"));
  pinMode(LED_BUILTIN, OUTPUT);

  while (true) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    delay(250);
  }
}

}  // namespace

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

  // The Nano 3.3 V pin must be connected to AREF for this sketch.
  // Select EXTERNAL before the first analogRead() call.
  analogReference(EXTERNAL);
  pinMode(SENSOR_PIN, INPUT);

  // On a classic Nano, Wire uses A4 for SDA and A5 for SCL.
  Wire.begin();

  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
    stopWithDisplayError();
  }

  showStartupScreen();
}

void loop() {
  static uint32_t previousUpdateMs = 0;
  const uint32_t nowMs = millis();

  if (nowMs - previousUpdateMs < UPDATE_INTERVAL_MS) {
    return;
  }
  previousUpdateMs = nowMs;

  const uint16_t rawValue = readAveragedRaw();
  const uint8_t moisturePercent = rawToPercent(rawValue);

  Serial.print(F("RAW="));
  Serial.print(rawValue);
  Serial.print(F("  MOISTURE="));
  Serial.print(moisturePercent);
  Serial.print(F("%  STATUS="));
  Serial.println(moistureLabel(moisturePercent));

  drawReading(rawValue, moisturePercent);
}

How the calibration works

The sketch maps the dry endpoint to 0 percent and the wet endpoint to 100 percent, then limits the displayed result to that range. It averages 16 ADC samples and refreshes the OLED about four times per second.

Demo raw readingDisplayed result
8240 percent, DRY
413100 percent, WET

The default status boundaries are:

  • Below 30 percent: DRY
  • 30 through 69 percent: MOIST
  • 70 percent and above: WET

This is a relative percentage for your calibrated setup. It is not a laboratory measurement of volumetric water content. Soil type, compaction, fertilizer, temperature, probe depth, pot geometry, watering location, and the exact 3.3 V reference can all change the reading.

Recommended calibration in the final pot

  1. Insert the sensor at its final depth and position inside the pot, preferably near the pot edge.
  2. Let the soil reach the moisture level at which the plant should be watered.
  3. Wait for the OLED RAW value to stabilize and record it as DRY_RAW.
  4. Water the pot thoroughly and allow excess water to drain.
  5. Wait for the reading to stabilize and record it as WET_RAW.
  6. Replace the two values in the sketch and upload it again.

Fast glass-of-water demonstration

A glass of clean water provides a clear visual response and a quick wet reference for a demo. Slowly immerse the complete blue sensing blade only. Keep the black molded cable hood, Micro-USB adapter, cable joints, Nano, OLED, breadboard, and every exposed connection above and away from the water. Remove the sensor and dry the blade before installing it in soil.

Water and potting soil have different dielectric properties, so a water-glass endpoint should never be presented as a universal soil-moisture threshold. Repeat the calibration in the actual pot for plant care.

Using the sensor with other popular controllers

Controller3.3 V powerAnalog inputImportant note
Classic Arduino NanoYesYes, A0 in this demoUse the external 3.3 V AREF procedure shown above.
ESP32YesYesUse a suitable ESP32 ADC pin and recalibrate for that board.
Raspberry Pi PicoYesYesUse a Pico ADC pin and write or adapt code for the Pico platform.
Raspberry Pi computerYesNo onboard analog inputAdd a compatible external ADC before reading the sensor signal.

Do not reuse this Nano AREF wiring or sketch unchanged on a different board. Check that board’s analog input range, ADC pin rules, reference behavior, and software API first.

Troubleshooting

  • OLED stays blank: Check power, GND, A4 to SDA, A5 to SCL, and the display’s I2C address. The sketch expects 0x3C.
  • Nano LED flashes rapidly: The sketch did not find the SSD1306 OLED. Recheck the display wiring and address.
  • Reading moves in the opposite direction: Keep your measured dry and wet values in the constants. The conversion works whether the raw value rises or falls as moisture increases.
  • Percentage jumps: Keep the sensor depth and position fixed, check the ground connection, and make sure AREF and sensor VCC share the same stable 3.3 V rail.
  • Upload fails: Some Nano-compatible boards require the ATmega328P (Old Bootloader) processor option.

Product, guide, and related PlantMate projects


Ready to build the demo? Start with the PlantMate 3.3 V capacitive soil-moisture sensor, follow the 3.3 V and AREF safety steps, then calibrate it in the same soil, pot, depth, and position you will use for monitoring.

Leave a Reply

Your email address will not be published. Required fields are marked *