Hello friends, I hope you all are doing great. Today, I am going to share the new Soil Moisture Sensor Library for Proteus V3.0. The soil moisture sensor is one of the first sensors students use in a garden or agriculture project: a YL-69 fork probe that you push into the soil, and the blue FC-28 board with an LM393 comparator that gives an analog output (A0) and a digital output (D0). With this Soil Moisture Sensor Library for Proteus, you set the soil with a click, water the plant, let the sun dry it, and your Arduino reads A0 with analogRead() and D0 with digitalRead(), just like on a real board.

Version 3.0 is the next version of our soil moisture library for Proteus, and it is built around an analog model: the board is made of real resistances inside Proteus's SPICE simulator, so A0 is a real voltage. You get two devices: Soil Simple, with an animated scene panel on the schematic, and Soil Advance, with a pop-up window and four measured sensor test tools. The new soil moisture sensor Arduino Proteus demo runs on two Arduino UNOs with a pump LED and calibration commands, shown in the compact Simple interface of our TEP Serial Monitor.

NOTICE: This library is very special to our team. Our soil moisture model runs inside Proteus's analog SPICE simulator: the probe, the board's divider, the LM393's open-collector output and its pull-up are real resistances, and D0 switches at the moment A0 crosses the pot's voltage, even in the middle of watering. It took our team a lot of hard work, many test runs and many design changes. Your feedback is the fuel that keeps us going, so please tell us what you think in the comments below or in the Soil Moisture Sensor board of our forum. And if our free libraries help you, you can buy us a coffee. So, let's get started with the Soil Moisture Sensor Library for Proteus:

Figure: The soil moisture demo in Proteus 8.5: NODE 1 (Simple) and NODE 2 (Advance), U2's Soil page at MOIST 50 % (A0 2.930 V, ADC 600, D0 LOW) and U4 printing "ADC: 600 Moisture: 50 % D0: LOW Soil moisture OK".

What is a Soil Moisture Sensor?

A soil moisture sensor tells your microcontroller how wet the soil is. The common low-cost kit has two parts:

  • The YL-69 probe: a small board with two long gold-plated prongs that you push into the soil. Water in the soil conducts electricity, so the wetter the soil, the lower the resistance between the prongs.
  • The FC-28 board (also sold as YL-38): a small blue board with an LM393 comparator, a blue trimmer pot, a red PWR LED, a green D0 LED and a 4-pin header (VCC, GND, D0, A0). Two jumper wires connect the probe to it.

The board gives you two outputs. A0 is an analog voltage: high in dry soil, low in wet soil. D0 is a digital output: the LM393 compares A0 with the voltage of the blue pot and pulls D0 LOW when the soil is wetter than the threshold you set. So you can read the moisture with analogRead(), or just ask "wet or dry?" with digitalRead(). Typical uses are automatic plant watering and garden or greenhouse monitors.

The Soil Moisture Sensor at a Glance

The soil moisture sensor in numbers (the A0 counts are our model's typical values; calibrate a real probe)
FeatureValue
ProbeYL-69 resistive fork probe (two prongs)
BoardFC-28 / YL-38: LM393 comparator, threshold pot, PWR and D0 LEDs
OutputsA0 analog (0 V to VCC); D0 digital (LOW = wetter than the threshold)
A0 in our model at 5 VADC 950 in bone-dry soil, 250 with the probe in water, 1023 in air
Slope7 ADC counts per percent of moisture
D0Open-collector output with a 10 k pull-up, no hysteresis
Supply3.3 V to 5 V, about 5 to 15 mA with the LEDs
PinsVCC, GND, D0, A0

Soil Moisture Terms You Will See in This Article

Soil moisture terms
TermMeaning
A0The analog output: a voltage your Arduino reads with analogRead()
D0The digital output of the LM393: LOW = wet, HIGH = dry
ADC countThe number analogRead() returns: 0 to 1023 for 0 to 5 V on an Arduino UNO
LM393A dual comparator chip; the board uses one half of it for D0
ComparatorCompares two voltages and switches its output when one passes the other
Threshold potThe blue trimmer: it sets the voltage, and so the moisture, where D0 switches
Open collector, pull-upThe LM393 can only pull D0 LOW; a 10 k resistor pulls it HIGH
RatiometricA0 is a fraction of VCC, so it scales with the supply voltage
map(), constrain()Arduino functions that turn the count into 0 to 100 %
DRY_VALUE, WET_VALUEYour calibration: the reading in dry soil and the reading in water
HysteresisA gap between the switch-on and the switch-off point; this board has none

How the Soil Moisture Sensor Works

A0: a Voltage Divider with the Soil

On the board, a 10 k resistor goes from VCC to A0, and the probe goes from A0 to GND. Together they make a voltage divider:

A0 = VCC x Rprobe / (Rprobe + 10 k)

Dry soil hardly conducts, so the probe's resistance is high and A0 stays close to VCC. Wet soil conducts well, the resistance falls and A0 drops. In our model, at 5 V, bone-dry soil gives 4.638 V (ADC 950), soil at 50 % gives 2.930 V (ADC 600), and the probe in water gives 1.221 V (ADC 250). Work the formula backwards and the probe is about 128 k in bone-dry soil, 14 k at 50 % and 3.2 k in water. Pull the probe out into the air and nothing conducts: A0 rises to VCC and reads 1023.

Between bone-dry soil and water, our model is a straight line: 7 ADC counts per percent of moisture. So 10 % reads 880, 30 % reads 740, 50 % reads 600 and 80 % reads 390.

D0: the LM393 Comparator and the Blue Pot

The blue pot makes a reference voltage. The LM393 compares A0 (its + input) with that reference (its - input). When the soil is wetter than the threshold, A0 is below the reference, the LM393 pulls D0 LOW and the green D0 LED lights. When the soil is drier, the LM393 lets go and the board's 10 k pull-up takes D0 HIGH. In our model you set the pot as a moisture percentage: the default 40 % means a reference of 3.27 V, which is ADC 670.

The real board has no hysteresis resistor, and neither has our model: D0 switches at the same point going wet and going dry. With a noisy real probe, D0 can chatter near that point, so debounce it in your sketch.

From the ADC Count to a Percentage

The sensor knows nothing about percentages. Your sketch makes one from two calibration readings with map(): the reading in bone-dry soil becomes 0 % and the reading in water becomes 100 %. Our demo uses 950 and 250, the values of the model. In air, map() gives -10 %, and constrain() turns that into 0 %. Keep in mind that this percentage is a straight line between your two calibration points, not a measured soil water content.

An Analog Model in Proteus

Our soil moisture model is an analog model. The DLL puts the board's resistances into Proteus's SPICE solver: the 10 k divider with the probe, the 10 k pull-up on D0, the LM393's sink while D0 is LOW and the PWR LED's load. So A0 and D0 are solved like any other part of your circuit, A0 follows the supply, and the D0 edge comes exactly where A0 crosses the pot's voltage, also in the middle of watering or drying.

One small difference, noted in the README: Proteus rounds the UNO's conversion to the nearest count (ADC = A0 x 1024 / 5 V, rounded), while a real AVR truncates and can read one count lower. The model sets every moisture point at the centre of its count, so the panel and the Serial Monitor both show 950, 880, 600 and 250 exactly.

What's New in Soil Moisture Sensor Library for Proteus V3.0

Here is what version 3.0 brings:

  • Two devices in TEPSOIL.LIB: Soil Simple (SOILTEP) and Soil Advance (SOILADVTEP), one model, TEPSOIL.DLL.
  • An analog SPICE model: a real, ratiometric A0 voltage and the LM393's open-collector D0.
  • A clean, animated FC-28 board with a turning pot, the PWR and D0 LEDs, an A0 LEVEL bar and a probe cell.
  • Measured numbers: the panels show the measured A0, the UNO's ADC and the demo sketch's percentage, so they agree with the Serial Monitor.
  • A pin / event log and four measured test tools on the Advance.
  • A two-UNO demo with the TEP Serial Monitor, a pump LED on D13, and calibration and pump commands.
  • A light package: about 1.43 MB, without the C++ source code.

Used one of our earlier soil moisture libraries? Version 3.0 uses new file names (TEPSOIL.LIB and TEPSOIL.DLL), so the old files can stay where they are.

Soil Moisture Sensor Library for Proteus: Simple vs Advance

Both devices run the same model with the same properties; only the panel differs:

Soil Moisture Simple vs Advance
FeatureSimpleAdvance
Full analog model (the A0 divider, the LM393 D0 output, a ratiometric supply, watering, drying)✔✔
Blue FC-28 board: PWR and D0 LEDs, turning pot, A0 LEVEL bar, animated probe cell✔✔
Scene panel on the schematic (plant pot, A0 vs POT gauge, readouts, buttons)✔✘
Pop-up window you can move, resize and minimise✘✔
Soil page: moisture slider, presets, what the board does (measured), 20 s chart, pin / event log✘✔
A0 sweep, D0 threshold, Wet / dry and Calibrate test tools✘✔
TEP Dark / Light theme, text size, Help page with Copy diagnostics✘✔
MOISTURE, THRESHOLD and DRYING properties✔✔

Choose Simple to keep the controls beside your circuit, and Advance when your sheet is full or you want hard numbers for your sketch. The demo has one of each.

Download Soil Moisture Sensor Library for Proteus

Click the button below to download Soil-Moisture-Sensor-Library-for-Proteus-v3.0.zip (about 1.43 MB, without the C++ source code):

Soil Moisture Sensor Library for Proteus V3.0
  • README.txt: a detailed guide to the files, the installation, the wiring, the model, the test tools, real hardware and troubleshooting.
  • Proteus Library Files: TEPSOIL.LIB (both devices), TEPSERIALMON.LIB and our Arduino UNO library ArduinoV3TEP.LIB / ArduinoV3TEP.IDX.
  • Proteus Model Files: TEPSOIL.DLL and TEPSERIALMON.DLL.
  • Proteus Simulation: Soil-Moisture-ArduinoUnoV3.pdsprj, Soil_Moisture.hex (both UNOs run it) and copies of both DLLs.
  • Arduino Code: Soil_Moisture.ino, the demo sketch. It needs no Arduino library.

How to Install Soil Moisture Sensor Library for Proteus

  1. Close Proteus and extract the whole zip file to a normal folder, for example your Desktop.
  2. Copy the four files from Proteus Library Files into the LIBRARY folder, usually C:\Program Files (x86)\Labcenter Electronics\Proteus 8 Professional\LIBRARY (on some PCs C:\ProgramData\Labcenter Electronics\Proteus 8 Professional\LIBRARY; use the folder that already holds Proteus's own .LIB files).
  3. Copy TEPSOIL.DLL and TEPSERIALMON.DLL into the MODELS folder.
  4. If the LIBRARY folder holds a TEPSOIL.IDX, delete it; Proteus makes a new index.
  5. Start Proteus, press P and search for SOIL. You get the Simple and the Advance device (category Sensors > Moisture).

Note: Tested in Proteus 8.5; Proteus 7 is not supported. Earlier versions of our soil moisture library use other file names, so they do not clash with V3.0. Keep the .hex file and both DLLs next to the demo project when you move it.

The Soil Moisture Sensor Board in Proteus

We drew the sensor as a clean TEP board in the blue of the real FC-28, in its real portrait shape: the blue threshold trimmer, the LM393, the red PWR and green D0 LEDs and the 4-pin header. Above the board sits the probe cell. Here is U1, the Simple device, at the start moisture of 50 %:

Figure: U1 (Simple) at MOIST: "MOISTURE 50 % ADC 600", A0 2.93 V, D0 LOW and the banner "SOIL MOISTURE OK - D0 LOW, D0 LED ON".

The Probe Cell

Above the board, the probe cell shows the YL-69 fork in a cup of soil, wired to the board. While the simulation runs, the soil gets darker as it gets wetter, and small sparks between the prongs show the current through the soil: more and brighter in wet soil, none in air. The probe lifts out in DRY AIR, a watering can pours while you water, and the sun shines while the soil dries. The caption shows the sketch's numbers, here "MOISTURE 50 % ADC 600". The probe cell is an animated picture, not part of the real board.

Pinout

The four pins have the names and the order printed on the real board's header:

Soil moisture sensor pins in Proteus (VCC GND D0 A0)
PinWhat it doesDemo connection
VCCPower, 3.3 V to 5 V+5V terminal
GNDGroundGND
D0Digital output: LOW when the soil is wetter than the pot's thresholdArduino D2
A0Analog output: a higher voltage means drier soilArduino A0

LEDs and Indicators

Board animations (the same on both devices)
IndicatorWhat it showsOn the real board?
PWR LED (red)Lit while the board has powerYes, the real board's LED
D0 LED (green)Lit while D0 is LOW: the soil is wetter than the thresholdYes, the real board's LED
Blue trimmerIts rotor turns with the THRESHOLD pot settingYes, the real pot
A0 LEVEL barA0 / VCC in 10 segments; the red mark is the pot's reference; blue segments are wetter than the threshold, amber ones drierNo, a TEP addition
Probe cellThe soil, the probe, the current sparks, watering and dryingNo, a TEP addition (an animated picture)
PANEL / OPEN PANEL, SIMPLE / ADVANCEThe panel button and the device badgeNo, TEP additions

In the image above, the lit segments of the A0 LEVEL bar stop left of the red mark: A0 (2.93 V) is below the pot's reference (3.27 V), so the soil is wetter than the threshold and the D0 LED is on.

Component Properties

Soil moisture properties (double-click the sensor > Edit Properties)
PropertyMeaningDefault
MOISTURESoil moisture at the start of a run: 0 to 100 (percent), or AIR (the probe pulled out)50
THRESHOLDThe D0 threshold pot as a moisture percent, 5 to 95 (40 = ADC 670 = 3.27 V at 5 V)40
DRYINGThe sun at the start: OFF, SLOW or FASTOFF
PANELSimple only: the scene panel at the start, OPEN or CLOSEDOPEN

Every Run starts again from these properties. A bad value is logged in the Simulation Log, and the default is used. There is one hidden extra: add REDRAW=FULL under Other Properties only if a part of the board ever stays stale. It redraws the whole sheet on every frame, which is slower; normally only what changed is drawn.

Soil Moisture Simple: The Scene Panel on the Sheet

Beside the Simple board sits the FC-28 SOIL MOISTURE SCENE panel (on the right of the image above):

The Soil Moisture Simple scene panel
PartWhat it does
Header, red XFC-28 SOIL MOISTURE SCENE; the X closes the panel
BannerThe state in words, for example "SOIL MOISTURE OK - D0 LOW, D0 LED ON"
SceneA plant pot in cross-section: the soil's colour, the probe, a plant that droops when the soil is dry, the watering can and the sun
A0 vs POT gaugeThe comparator explained: the pot's voltage (red line), A0 (blue bar), "A0 < POT" or "A0 > POT", "so D0 = LOW" or "HIGH", and the D0 LED lamp
MOISTURE, A0, ADC, D0The readouts; MOISTURE is what the demo sketch prints
SOIL MOISTURE rowDRY AIR (the probe pulled out), DRY SOIL 10 %, SLIGHTLY MOIST 30 %, MOIST 50 %, WET 80 %, IN WATER 100 %
WATERINGWATER THE PLANT: +20 % in 2 s
DRYING (sun)OFF, SLOW (0.5 % per second) or FAST (2 % per second)
THRESHOLD POT- 5 % and + 5 %: D0 goes LOW above this moisture

In the start image, MOIST and OFF are selected: the soil is at 50 %, A0 is 2.93 V and the ADC 600. The pot sits at 3.27 V, so the gauge says "A0 < POT", "so D0 = LOW" and "D0 LED ON", and the sketch on UNO 1 prints the same numbers.

WET: Lower A0, More Sparks

Figure: WET: "MOISTURE 80 % ADC 390", A0 1.90 V, D0 LOW; more sparks run between the prongs.

Click WET and the soil jumps to 80 %. Wet soil conducts better, so A0 falls to 1.90 V and the ADC to 390 (950 - 7 x 80 = 390). More sparks run between the prongs, because more current flows through the soil, and only four segments of the A0 LEVEL bar are lit now. D0 stays LOW, and map() turns 390 into exactly 80 %.

DRY AIR: the Probe Pulled Out

Figure: DRY AIR: the probe is out of the soil, "PROBE IN AIR ADC 1023", A0 5.00 V, "A0 > POT", D0 HIGH and the D0 LED off.

Click DRY AIR and the probe lifts out of the soil, in the probe cell and in the scene. With nothing between the prongs, the probe is an open circuit, so A0 rises to the supply: 5.00 V, ADC 1023. The orange banner says "PROBE IN AIR - A0 AT VCC, D0 HIGH". A0 is now above the pot's voltage, so the gauge shows "A0 > POT" and "so D0 = HIGH", and the D0 LED goes off. The A0 LEVEL bar is full: blue up to the red mark, amber after it. The MOISTURE readout says AIR. The soil keeps its moisture while the probe is out, so the plant still stands. Note that air reads higher than bone-dry soil: the demo's map() gives -10 % there, and constrain() shows 0 %.

Watering, Drying and the Threshold Pot

Click WATER THE PLANT: the watering can pours, and the moisture rises by 20 % over 2 s (a second click while it pours adds 2 s more). The banner says "WATERING THE PLANT - MOISTURE RISING". Click SLOW or FAST under DRYING (sun), and the soil dries at 0.5 % or 2 % per second: much faster than a real plant pot, so a demo takes seconds, not days. Below about 35 % the plant starts to droop. While the moisture glides, D0 switches the moment it passes the pot's threshold.

The - 5 % and + 5 % buttons turn the pot: the trimmer's rotor turns on the board, and the red mark moves on the A0 LEVEL bar and on the gauge. A higher setting means the soil must be wetter before D0 goes LOW.

Closing the Panel

Click the red X: the panel disappears, the board button says PANEL: CLOSED, and the sensor keeps running. Click PANEL to bring the panel back.

Figure: The panel closed: PANEL: CLOSED; the board and the probe cell keep running at "MOISTURE 50 % ADC 600".

Soil Moisture Advance: The Pop-Up Window

The Advance device keeps only the board and the probe cell on the schematic. Here is U2:

Figure: U2 (Advance) with OPEN PANEL and the ADVANCE badge, at "MOISTURE 50 % ADC 600".

At Run, the "TEP Soil Moisture Advance" pop-up opens with the look of our TEP Serial Monitor and no Windows title bar: move it by its header, resize or minimise it, and reopen it with OPEN PANEL after you close it. The header shows the state (here U2 · MOIST 50 %), the Soil and Test tabs, and the palette (theme), gear (Settings) and ? (Help) icons.

The Soil Page

Figure: The Soil page at MOIST: A0 2.930 V (58.6 % of VCC), UNO ADC 600, the sketch prints 50 %, D0 LOW (0.015 V), probe current 0.207 mA.
  • Toolbar chips: MOIST, ADC 600 with its 50 %, D0 LOW, Pot 40 % and the waterings (here 2).
  • THE SOIL - LIVE: the same scene as the Simple panel.
  • SET THE SOIL: a 0 to 100 % slider, the six presets, Water the plant (+20 %), the sun (Off, Slow, Fast) and the pot: "Pot: D0 LOW above 40 % (ADC 670, 3.27 V)".
  • WHAT THE BOARD DOES (MEASURED): supply 5.00 V; A0 2.930 V (58.6 % of VCC); UNO ADC 600; the sketch prints 50 %; D0 LOW (0.015 V); probe current 0.207 mA; pot reference 3.27 V = ADC 670; LM393: A0 < POT, so D0 LOW.
  • LIVE - THE LAST 20 SECONDS: A0, the pot's reference, the moisture and a D0 LOW band.
  • PIN / EVENT LOG (NEWEST FIRST): power, every D0 edge with A0 at that moment, scene changes and the tools.

The event log in this picture is the end of a Wet / dry test (see below). At 38.649 s the tool set the soil to 10 %: "D0 HIGH at A0 4.297 V (ADC 880)". At 42.649 s the watering passed the pot: "D0 LOW at A0 3.271 V (ADC 670), moisture 40.0 %". At 44.153 s the test finished and put the soil back to 50 %. 3.271 V is exactly the pot's reference, so D0 switched right at the crossing.

The Test Page: Four Measured Soil Sensor Test Tools

The four tools on the Test tab move the soil only with the panel's own actions (set the moisture, pull the probe out, water, dry, turn the pot) and read the node voltages that Proteus's SPICE solver computes at A0, D0 and VCC at every step. So every number is measured on the running circuit. Each tool shows a bold "Good for:" line and puts your scene back when it ends. All results below come from U2 in the demo.

1. A0 Sweep: the Whole Curve and Your map()

Figure: A0 sweep, 11 points: slope -7.00 counts per %, ADC 950 / 250 at 0 / 100 %, the sketch's error 0 %, D0 LOW from 50 % (pot 40 %).

Good for: "seeing the whole A0 curve and checking that your map() gives the right percentage." Choose 11 points (10 % steps) or 21 points (5 % steps) and click Run test. The tool sets each moisture, waits for a flat A0 and averages it, and adds the probe in air at the end. Here, 12 points took 3.0 s:

A0 sweep (U2, 11 points + AIR, the pot at 40 %)
SetA0ADCSketch printsD0
0 %4.638 V9500 %HIGH
10 %4.297 V88010 %HIGH
20 %3.955 V81020 %HIGH
30 %3.613 V74030 %HIGH
40 %3.271 V67040 %HIGH
50 %2.930 V60050 %LOW
60 %2.588 V53060 %LOW
70 %2.246 V46070 %LOW
80 %1.904 V39080 %LOW
90 %1.562 V32090 %LOW
100 %1.221 V250100 %LOW
AIR4.999 V10230 % (-10)HIGH

The slope is exactly -7.00 counts per percent at every point, and the sketch's percentage equals the set moisture everywhere (sketch error 0 %). D0 is LOW from the 50 % point. At exactly 40 %, the pot's own setting, it is still HIGH, because D0 goes LOW only once the soil is wetter than the threshold. In air, map() gives -10 (shown in brackets) and constrain() prints 0 %. The chart shows the measured points on the line 950 - 7 x %, green where D0 is LOW, with the pot as a red line.

2. D0 Threshold: Where Does D0 Switch?

Figure: D0 threshold at the pot's 40 %: LOW at 40.0 % = ADC 670 going wet, HIGH at 40.0 % = ADC 670 going dry, hysteresis 0.0 %, 1 / 1 edges.

Good for: "finding where D0 switches, and checking your pot setting before your sketch relies on D0." Choose the pot as it is now (here 40 %) or 20 / 40 / 60 / 80 %, and a ramp of 4 or 1 % per second. The tool settles the soil 10 % below the threshold, ramps it up to 10 % above, and back down. With the pot at 40 % and 4 % per second, one pot setting took 10.9 s:

  • D0 LOW when rising past: 40.0 % = ADC 670.
  • D0 HIGH when falling past: 40.0 % = ADC 670.
  • Hysteresis 0.0 % (none) and 1 / 1 edges: one clean edge each way, no chatter.
  • The levels: the reference is 3.271 V; D0 LOW is 0.015 V and HIGH is 5.00 V.

Both edges sit exactly on the pot's reference of 3.271 V, in the middle of the ramp. LOW is the sink of the LM393's open-collector output, HIGH the board's 10 k pull-up. The 20 / 40 / 60 / 80 % option checks four pot settings in one run: ADC 810, 670, 530 and 390. And the tool's tip: "No hysteresis: with a noisy probe a real D0 chatters near the trip point - debounce it in the sketch."

3. Wet / Dry: How Fast Does A0 Follow the Soil?

Figure: Wet / dry, 1 step: 880 to 600, 10 - 90 % in 3.20 s, D0 LOW after 3.00 s (ADC 670), settled in 3.96 s, 659 readings.

Good for: "seeing how fast A0 follows watering and drying, and when D0 tells your sketch the pump state." Choose the start (10 or 20 %), the water (+20, +40 or +60 %), the sun (Fast 2 % per second or Slow 0.5 % per second) and 1 to 3 steps: water, let the sun dry it back to the start, water again. Every step starts from a flat reading (a lead-in of at least 1 s) and lasts until A0 is flat again, and every reading is kept. Here, start 10 %, +40 %, one step took 5.5 s:

Wet / dry, start 10 %, water +40 %, sun Fast, 1 step (U2): 659 readings in all, with the lead-in
StepADC from / to10 - 90 %D0 changed afterSettledRateReadings
1 water880 / 6003.20 s3.00 s, LOW3.96 s-70 per s539

The numbers follow from the watering: +40 % is 4 s at 10 % per second, which is -70 counts per second, and the 10 - 90 % part takes 0.8 x 4 s = 3.20 s. D0 went LOW 3.00 s after the watering started, at ADC 670: 10 % + 3 s x 10 % per second = 40 %, the pot's threshold. A0 follows the moisture at once, because the probe is a resistor. With 3 steps, the sun (2 % per second) then needs 5 s to dry the soil from 50 % to 40 %, so D0 goes HIGH 5 s into the drying.

4. Calibrate: DRY_VALUE and WET_VALUE for Your Sketch

Figure: Calibrate: air 1023, bone-dry soil 950, water 250, the D0 trip 670, and "The demo sketch uses 950 / 250 - it matches this sensor."

Good for: "getting the DRY_VALUE and WET_VALUE for your sketch - the two numbers every soil sketch needs." Click Run test: the tool holds the probe in air, in bone-dry soil (0 %), in water (100 %) and at the pot's trip point, 400 ms each, and averages A0 once it is flat. Here, 4 points took 1.6 s:

Calibrate (U2, measured at VCC 5.00 V)
PointA0ADCD0Reads
Probe in air4.999 V1023HIGH38
Bone-dry soil (0 %)4.638 V950HIGH38
In water (100 %)1.221 V250LOW79
The pot's trip point3.271 V670HIGH101

The card "PUT THESE IN YOUR SKETCH" gives the lines to copy (const int DRY_VALUE = 950;, const int WET_VALUE = 250;, map() and constrain()), and below it what the sketch then prints: ADC 880 = 10 %, ADC 600 = 50 %, ADC 390 = 80 %, and the D0 trip, ADC 670, = 40 %. The trip point is the A0 count your sketch can use instead of D0: below 670, the soil is wet. With a real probe, the demo's CAL DRY and CAL WET commands do the same job.

Settings and Help

The gear opens Settings: theme (TEP Dark or Light), text size, open the panel at Run, and the window size, saved for your Windows user. The ? icon opens Help & Support, as in our TEP Serial Monitor: eight cards (bug report, feature request, this article, user guide, updates, donate, website, forum) and Copy diagnostics for a bug report.

Soil Moisture Sensor with Arduino in Proteus

Open Soil-Moisture-ArduinoUnoV3.pdsprj from the Proteus Simulation folder, with Soil_Moisture.hex and both DLLs beside it. Its two Arduino UNOs, from our TEP Arduino UNO V3 library, run the same HEX file:

  • NODE 1 - SIMPLE (PANEL ON THE SHEET): UNO 1 (ARD1) with the Soil Simple (U1), the pump LED D1 with R1 on D13, and Serial Monitor U3.
  • NODE 2 - ADVANCE (POP-UP PANEL): UNO 2 (ARD2) with the Soil Advance (U2), the pump LED D2 with R2 on D13, and Serial Monitor U4.
Figure: The whole circuit running, without the pop-up windows: U1 at MOIST with its scene panel, U2 with its probe cell, both pump LEDs off because the soil is moist.

Wiring

Demo wiring (the same on both UNOs, and on a real board)
FromToWhy
Sensor VCC+5V terminalPower (the PWR LED lights)
Sensor GNDGNDGround
Sensor D0Arduino D2The comparator's output, read with digitalRead()
Sensor A0Arduino A0The analog voltage, read with analogRead()
LED + 220 ΩD13 to GNDThe pump indicator: on while the soil is dry
Serial Monitor RXD / TXD / GNDArduino D1 / D0 / groundShows what the Arduino prints, sends what you type
Serial Monitor RXD2Not connectedThe sensor sends no serial data

On a real UNO, D13 is also the board's own LED. A real pump needs a relay or a transistor; the LED only shows when the sketch would switch it on.

The Arduino Code

The sketch needs no Arduino library: only analogRead(), digitalRead(), map() and Serial. First come the pins and the two calibration values:

const int SENSOR_A0 = A0;     // analog output of the sensor board
const int SENSOR_D0 = 2;      // digital output of the sensor board
const int PUMP_LED  = 13;     // the "water pump" indicator (the UNO's on-board LED)

int DRY_VALUE = 950;          // analogRead() in completely dry soil    (= 0 % moisture)
int WET_VALUE = 250;          // analogRead() with the probe in water   (= 100 % moisture)

setup() opens the serial port at 9600 baud and prints three start lines:

void setup() {
  Serial.begin(9600);
  pinMode(SENSOR_D0, INPUT);
  pinMode(PUMP_LED, OUTPUT);
  Serial.println("Soil Moisture Sensor - The Engineering Projects");
  Serial.println("Reading A0 and D0 every 0.5 s...");
  Serial.println("Type HELP for the commands.");
}

readSensor() reads both outputs. A0 is high in dry soil; D0 is LOW when the soil is wetter than the pot's threshold:

  int adc = analogRead(SENSOR_A0);              // 0..1023, high = dry
  int d0 = digitalRead(SENSOR_D0);              // LOW = wetter than the pot's threshold

Then map() turns the count into a percentage between the two calibration points, and constrain() keeps it inside 0 to 100 %:

  int percent = map(adc, DRY_VALUE, WET_VALUE, 0, 100);
  percent = constrain(percent, 0, 100);

The reading goes to the Serial Monitor:

  Serial.print("ADC: ");
  Serial.print(adc);
  Serial.print("   Moisture: ");
  Serial.print(percent);
  Serial.print(" %   D0: ");
  Serial.print(d0 == LOW ? "LOW " : "HIGH");
  Serial.print("   ");

Now the pump decision. By default, the board's comparator decides (D0 HIGH means dry); after PUMP A0 n, the sketch compares the percentage itself:

  bool dry = pumpFromA0 ? (percent < pumpBelow) : (d0 == HIGH);
  if (dry) {                                    // dry soil: switch the pump (LED) on
    digitalWrite(PUMP_LED, HIGH);
    Serial.println("Soil is DRY - water the plant!");
  } else {
    digitalWrite(PUMP_LED, LOW);
    Serial.println("Soil moisture OK");
  }

In loop(), the readings run on millis() instead of delay(), so a command you type is answered at once:

  if (everyMs > 0 && millis() - lastRead >= everyMs) {   // the readings (millis, not delay: commands stay quick)
    lastRead = millis();
    readSensor();
  }

CAL DRY stores the current reading as DRY_VALUE (CAL WET does the same for WET_VALUE):

  } else if (strcmp(cmd, "CAL DRY") == 0) {
    DRY_VALUE = analogRead(SENSOR_A0);
    Serial.print("DRY_VALUE = ");
    Serial.print(DRY_VALUE);
    Serial.println(" (the probe is in dry soil now)");
    checkCalibration();

And checkCalibration() warns you when the two values are the wrong way round:

void checkCalibration() {
  if (DRY_VALUE <= WET_VALUE) {
    Serial.println("Note: DRY_VALUE must be above WET_VALUE (dry soil reads higher)");
  }
}

PUMP A0 n moves the pump decision from the board's pot into the sketch:

  } else if (strncmp(cmd, "PUMP A0", 7) == 0 && numberAfter(cmd + 7) >= 0 && numberAfter(cmd + 7) <= 100) {
    pumpFromA0 = true;
    pumpBelow = numberAfter(cmd + 7);

HELP prints two short lines. Every line of the sketch is 80 characters or less (the longest is 71), so it fits the Serial Monitor's Simple interface:

void printHelp() {
  Serial.println("Commands: HELP, STATUS, READ, EVERY ms (0 = pause),");
  Serial.println("  CAL DRY, CAL WET, DRY n, WET n, PUMP D0, PUMP A0 n");
}

The settings live in RAM, so a new Run brings back 950 and 250. To change the sketch for good, open Soil_Moisture.ino in the Arduino IDE, select Arduino Uno, use Sketch > Export Compiled Binary, and replace the simulation's .hex file with the exported one.

Serial Monitor Commands

Commands of the demo sketch, upper or lower case (quick buttons: U3 STATUS, READ, EVERY 2000, EVERY 500; U4 STATUS, CAL DRY, CAL WET, PUMP A0 40)
CommandWhat it does
HELPThe command list (two lines)
STATUSTwo lines: DRY_VALUE, WET_VALUE and the reading interval; where the pump decision comes from and the last reading
READOne reading now
EVERY msThe reading interval in milliseconds (0 = pause)
CAL DRYThe probe is in dry soil now: this reading becomes DRY_VALUE
CAL WETThe probe is in water now: this reading becomes WET_VALUE
DRY n, WET nSet them by hand (0 to 1023)
PUMP D0The pump (D13) follows D0: the board's pot decides (the default)
PUMP A0 nThe pump runs below n % moisture: the sketch decides

How to Run the Demo

  1. Press Run. Both Serial Monitors (9600 baud) and U2's pop-up open, and both monitors print a reading twice a second.
  2. On U1's panel, click DRY SOIL: D0 goes HIGH and the pump LED D1 lights. Click WATER THE PLANT twice: the readings fall by 35 counts per line, D0 goes LOW once 40 % is passed, and D1 goes off. Then try FAST, DRY AIR and the pot buttons.
  3. Calibrate on UNO 2: set U2's slider to 0 % and click CAL DRY on U4, then 100 % and CAL WET. PUMP A0 40 lets the sketch switch the pump below 40 %; PUMP D0 goes back to the pot.
  4. On U2, try the Soil page and the four tools on the Test tab.

Soil Moisture Sensor Proteus Simulation Results

The Serial Monitor in Its Simple Interface

Both monitors in this demo run in the Simple interface of our TEP Serial Monitor, as in every monitor screenshot here: only the log, the send box and one slim toolbar row stay visible.

  • Switch to it: right-click the log and choose Simple interface, or press Ctrl+Shift+U.
  • Back to the full view: click the corner button (the expand arrows at the top right), press Esc or Ctrl+Shift+U, or right-click and choose Full interface.
  • Your choice is remembered.

Type commands such as STATUS or CAL DRY in the send box and press Enter; the quick buttons come back with the full view. Read all about the monitor in TEP Serial Monitor Advance for Proteus.

Start-Up

After Run, U4 prints the three start lines and then the first reading:

Figure: U4 in the Simple interface: the three start lines, then "ADC: 600 Moisture: 50 % D0: LOW Soil moisture OK".
Soil Moisture Sensor - The Engineering Projects
Reading A0 and D0 every 0.5 s...
Type HELP for the commands.
ADC: 600   Moisture: 50 %   D0: LOW    Soil moisture OK

At MOIST the soil reads ADC 600, and map(600, 950, 250, 0, 100) gives exactly 50 %. D0 is LOW, so the sketch says "Soil moisture OK" and keeps the pump off.

Two Readings a Second

Figure: U4 later in the run, lines 252 to 264: the same steady reading twice a second.
ADC: 600   Moisture: 50 %   D0: LOW    Soil moisture OK
ADC: 600   Moisture: 50 %   D0: LOW    Soil moisture OK
ADC: 600   Moisture: 50 %   D0: LOW    Soil moisture OK

The sketch prints only the readings and the answers to your commands, so the log stays easy to read. The model adds no noise, so a steady soil gives the same count every time.

What the Other Presets and STATUS Print

The README lists the lines for the other presets and for STATUS; they were checked with the real sketch on two UNOs in our PC tests. After DRY SOIL, IN WATER and DRY AIR on U1, and STATUS on U3:

ADC: 880   Moisture: 10 %   D0: HIGH   Soil is DRY - water the plant!
ADC: 250   Moisture: 100 %   D0: LOW    Soil moisture OK
ADC: 1023   Moisture: 0 %   D0: HIGH   Soil is DRY - water the plant!
Status: DRY_VALUE 950, WET_VALUE 250, every 500 ms
  pump from D0 (the pot), last ADC 1023 = 0 %, D0 HIGH, pump ON

Look at the last reading: in air the sketch prints 0 % and switches the pump on. If a probe lying on the bench matters in your project, treat a reading above DRY_VALUE as "probe not in the soil".

Troubleshooting

  • "ADC: 0" and D0 LOW all the time: the sensor has no power. Check VCC and GND; the PWR LED must be lit.
  • 0 % in air and in very dry soil: correct; map() gives a negative number and constrain() makes it 0 %.
  • D0 does not switch where you expect: run the D0 threshold tool; the pot is set as a moisture percent.
  • The simulation runs slowly: with U2's pop-up, U1's animated board and both monitors open, it runs at about half real time (24 s of simulation took 46 s in our test). Close the pop-up or the panel for the fastest run.
  • The panel's buttons do nothing: a test tool owns the soil; wait for it or click Stop.
  • No panel, or the part is not simulated: TEPSOIL.DLL is missing from MODELS and from the project folder. No monitor window: TEPSERIALMON.DLL is missing, or click OPEN MONITOR.
  • The Advance pop-up does not appear: click OPEN PANEL, or turn "Open the panel at Run" back on in Settings.
  • The red X does nothing: LIBRARY holds an older TEPSOIL.LIB; replace it, delete TEPSOIL.IDX and re-pick the part.
  • D0 is HIGH for the first 10 ms in wet soil: harmless; Proteus solves the DC operating point before the model sees the supply.

Found a bug or have an idea? Post it on the Soil Moisture Sensor board of our forum with a screenshot, the Serial Monitor text, the Simulation Log and, for the Advance, the Copy diagnostics text.

Things to Know Before Using a Real Soil Moisture Sensor

The demo sketch uses only standard Arduino functions, so it runs on a real UNO with a real FC-28 and YL-69, too. Keep in mind:

  • Supply: 3.3 V to 5 V, about 5 to 15 mA with the LEDs. A0 is ratiometric: from 3.3 V every reading scales by 3.3 / 5, so calibrate at the voltage you use.
  • Calibrate your own probe: read it in dry soil (DRY_VALUE) and in a glass of water (WET_VALUE). Probes and soils differ: tutorials report 850 to 1020 for dry soil and 300 to 500 in water.
  • Corrosion: a resistive probe that is powered all the time corrodes (electrolysis) within weeks to months. Power it from a digital pin only while reading, for example D7: HIGH, wait 10 ms, read, LOW. Capacitive sensors avoid the problem; they are a different module, with an analog output only.
  • No hysteresis: near the threshold, a noisy probe makes D0 chatter. Debounce it in the sketch.
  • The percentage is a straight line between two calibration readings, not a calibrated soil water content. Soil type, salt and temperature change the probe's resistance, too.
  • ADC rounding: Proteus rounds the conversion, a real ATmega328P truncates, so a real UNO can read one count lower than the simulation.
  • The pump: an Arduino pin can light an LED, but a real pump needs a relay or a transistor.

Limitations of the Simulation

  • Modelled: the board as SPICE resistances, the probe's resistance from the moisture, the comparator against the pot (5 to 95 %), watering, drying and the supply (any source, also a digital pin).
  • Not modelled: the noise and chatter of a real probe, the 10 nF filter capacitor on A0 (its time constant is microseconds), corrosion, temperature and salt (conductivity) effects, and capacitive sensors.
  • Watering and drying are much faster than in a real plant pot: seconds instead of hours or days.
  • The model is our own implementation, written from the module's published circuit (LM393 comparator, 10 k pull-ups) and tutorials; it contains no third-party code. The HEX file contains the Arduino AVR core 1.8.6, which includes LGPL-licensed code; its source code is on GitHub.
  • Tested in Proteus 8.5 in October 2026 (the demo, both devices, the four tools and every screenshot here) and with 1,623 automatic PC checks, all passed, 43 of them with the real demo sketch on two UNOs. Proteus 7 is not supported.

Building a smart garden? Add the light level with our BH1750 Light Sensor Library for Proteus, or temperature, humidity and pressure with our BME280 Sensor Library for Proteus. Our Rain Sensor Library for Proteus V2.0 uses the same kind of blue LM393 board, and our Water Sensor Library for Proteus V2.0 measures the water level in a tank. To send your soil readings to another Arduino by radio, add our HC-12 Library for Proteus.

So, that was all about the Soil Moisture Sensor Library for Proteus V3.0. I hope the analog A0, the live LM393 gauge, the probe cell and the four test tools make the soil moisture sensor much easier to understand, so your plant watering project works the first time you wire a real probe. If you use the Soil Moisture Sensor Library for Proteus in a project, please share your feedback in the comments or in our forum, and if you have any questions, ask in the comments and I will help you out. Till the next tutorial, take care and have fun!