Plant monitor: light, soil, temperature and a pump, on an Arduino Uno
A photoresistor on A0, a soil moisture sensor on A1 and a thermistor on A2 report to an I2C LCD; when the soil dries out, a pump on an L293D (1A on pin 12) waters the plant. The whole build, an Arduino Uno and 8 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Advanced About 45 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $41. On Mokxi they're free.
What you'll learn
- Reading three analog sensors
- Converting a thermistor reading to temperature
- Switching a pump with an L293D driver
- Showing two screens on a 16x2 I2C LCD
- Using a threshold to make a decision
- Difficulty: Advanced
- Time: About 45 minutes
- Board: Arduino Uno R3
- Parts: 10
How it works
Three sensors report to the Uno: a photoresistor on A0 for light, a capacitive soil sensor on A1, and a thermistor on A2 for temperature. The LCD cycles between two screens, temperature first, then light and soil.
A capacitive soil sensor reads lower when the soil is wetter. When the soil reading is above DRY (500), the sketch prints a message and turns on pin 12 for one second.
Pin 12 feeds an L293D motor driver, and the driver runs the small water pump from 5V. In the simulator, drag the soil sensor towards dry to trigger it.
Parts list
10 parts, plus the jumper wires. They retail for about $41; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Full-size breadboard
- 1 × Light sensor
- 2 × Resistor, 10k ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × Thermistor, 10k NTC
- 1 × Motor driver, L293DIn Claw Machine Kit.
- 1 × Soil moisture sensor, capacitive
- 1 × Mini water pump
- 1 × Character LCD with I2C backpackIn Claw Machine Kit, Vending Machine Kit, Rhythm Game Kit.
Kits that can build it
This starter kit has every part in the circuit. Each kit's page lists what else it can build.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Motor driver, L293D pin IN1 | Arduino Uno R3 pin 12 |
| Resistor, 10k Ω (1) pin 2 | Arduino Uno R3 pin 5V |
| Thermistor, 10k NTC pin 1 | Arduino Uno R3 pin 5V |
| Motor driver, L293D pin EN12 | Arduino Uno R3 pin 5V |
| Motor driver, L293D pin VS | Arduino Uno R3 pin 5V |
| Motor driver, L293D pin VSS | Arduino Uno R3 pin 5V |
| Soil moisture sensor, capacitive pin VCC | Arduino Uno R3 pin 5V |
| Character LCD with I2C backpack pin VCC | Arduino Uno R3 pin 5V |
| Light sensor pin 1 | Arduino Uno R3 pin GND |
| Resistor, 10k Ω (2) pin 2 | Arduino Uno R3 pin GND |
| Motor driver, L293D pin GND1 | Arduino Uno R3 pin GND |
| Motor driver, L293D pin IN2 | Arduino Uno R3 pin GND |
| Soil moisture sensor, capacitive pin GND | Arduino Uno R3 pin GND |
| Mini water pump pin V- | Arduino Uno R3 pin GND |
| Character LCD with I2C backpack pin GND | Arduino Uno R3 pin GND |
| Light sensor pin 2 | Arduino Uno R3 pin A0 |
| Resistor, 10k Ω (1) pin 1 | Arduino Uno R3 pin A0 |
| Soil moisture sensor, capacitive pin AOUT | Arduino Uno R3 pin A1 |
| Thermistor, 10k NTC pin 2 | Arduino Uno R3 pin A2 |
| Resistor, 10k Ω (2) pin 1 | Arduino Uno R3 pin A2 |
| Character LCD with I2C backpack pin SDA | Arduino Uno R3 pin A4 |
| Character LCD with I2C backpack pin SCL | Arduino Uno R3 pin A5 |
| Mini water pump pin V+ | Motor driver, L293D pin OUT1 |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// A plant monitor: three sensors, a display, and a pump for dry soil.
//
// photoresistor from A0 -> GND, 10k from 5V -> A0
// soil moisture sensor AOUT -> A1 VCC -> 5V GND -> GND
// thermistor from 5V -> A2, 10k from A2 -> GND
// L293D 1,2EN, VCC1, VCC2 -> 5V GND -> GND 1A -> pin 12
// 1Y -> the pump's + lead; the pump's - lead -> GND
// I2C LCD GND -> GND VCC -> 5V SDA -> A4 SCL -> A5
//
// The display takes turns: temperature, then light and soil. A capacitive
// soil sensor reads lower when the soil is wetter, so a reading above DRY
// means the plant needs water, and the pump runs for a second.
//
// Drag the soil sensor towards dry.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
const int LIGHT = A0;
const int SOIL = A1;
const int TEMP = A2;
const int PUMP = 12;
const int DRY = 500;
const float SERIES_OHMS = 10000.0;
LiquidCrystal_I2C lcd(0x27, 16, 2);
// The thermistor is the top of this divider, so a warmer bead reads higher.
float celsius(int reading) {
float r = SERIES_OHMS * (1023.0 - reading) / reading;
float kelvin = 1.0 / (1.0 / (25.0 + 273.15) + log(r / 10000.0) / 3950.0);
return kelvin - 273.15;
}
void setup() {
pinMode(PUMP, OUTPUT);
digitalWrite(PUMP, LOW);
lcd.init();
lcd.backlight();
Serial.begin(9600);
Serial.println("Mokxi Uno: plant monitor");
}
void loop() {
float c = celsius(analogRead(TEMP));
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Temp C: ");
lcd.print(c);
lcd.setCursor(0, 1);
lcd.print("Temp F: ");
lcd.print(c * 9.0 / 5.0 + 32.0);
delay(1500);
int light = analogRead(LIGHT);
int soil = analogRead(SOIL);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Light: ");
lcd.print(light);
lcd.setCursor(0, 1);
lcd.print("Soil: ");
lcd.print(soil);
Serial.print("light ");
Serial.print(light);
Serial.print(" soil ");
Serial.print(soil);
Serial.print(" temp ");
Serial.println(c);
if (soil > DRY) {
Serial.println("dry soil: watering");
digitalWrite(PUMP, HIGH);
delay(1000);
digitalWrite(PUMP, LOW);
}
delay(1500);
}
Code walkthrough
The key functions of the sketch, and what each one does.
celsius sketch.ino
Turns the thermistor reading into degrees Celsius. It first finds the thermistor's resistance from the divider with a 10k resistor. It then uses the beta formula with 25 degrees, 10k and a beta of 3950.
// The thermistor is the top of this divider, so a warmer bead reads higher.
float celsius(int reading) {
float r = SERIES_OHMS * (1023.0 - reading) / reading;
float kelvin = 1.0 / (1.0 / (25.0 + 273.15) + log(r / 10000.0) / 3950.0);
return kelvin - 273.15;
}setup sketch.ino
Makes PUMP an output and starts it LOW, so the pump is off. It also starts the LCD with its backlight and opens serial at 9600.
void setup() {
pinMode(PUMP, OUTPUT);
digitalWrite(PUMP, LOW);
lcd.init();
lcd.backlight();
Serial.begin(9600);
Serial.println("Mokxi Uno: plant monitor");
}loop sketch.ino
Shows temperature in C and F for 1.5 seconds. Then it reads light and soil, shows them and prints them. If soil > DRY, the pump runs for one second. A final 1.5 second wait ends each round.
void loop() {
float c = celsius(analogRead(TEMP));
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Temp C: ");
lcd.print(c);
lcd.setCursor(0, 1);
lcd.print("Temp F: ");
lcd.print(c * 9.0 / 5.0 + 32.0);
delay(1500);
int light = analogRead(LIGHT);
int soil = analogRead(SOIL);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Light: ");
lcd.print(light);
lcd.setCursor(0, 1);
lcd.print("Soil: ");
lcd.print(soil);
Serial.print("light ");
Serial.print(light);
Serial.print(" soil ");
Serial.print(soil);
Serial.print(" temp ");
Serial.println(c);
if (soil > DRY) {
Serial.println("dry soil: watering");
// ... 6 more lines in the editorTroubleshooting
The usual mistakes with this circuit, and the fix for each.
- The pump never runs This project
The soil reading has to go above 500. Drag the soil sensor towards dry. Also check that the L293D's IN1 is on pin 12 and its IN2 is on ground. - The LCD is blank This project
Check SDA on A4 and SCL on A5. The sketch expects the I2C address 0x27. - The temperature looks wrong This project
The thermistor goes from 5V to A2, and the 10k resistor goes from A2 to ground.celsiusassumes that order. - The reading barely changes, or sits stuck near 0 or the top of the range. Light sensor
An LDR is a resistor, so use it in a divider with a fixed resistor, about 10k, and read the middle node. Without the divider the analog pin sees nothing useful. - Readings go the wrong way: higher in the dark. Light sensor
Which side the LDR sits on decides the direction. LDR on the supply side gives a higher reading in the light. LDR on the ground side gives a lower reading in the light. Swap them if needed. - The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure. - A pull-up or pull-down resistor is missing, so the input floats and reads random values. Resistor
Tie the input to 5 V or ground through about 10k. Or use INPUT_PULLUP and wire the switch to ground. - The temperature reads far off or jumps around. Thermistor
Use a fixed resistor equal to the thermistor's 10k at 25 C in the divider. Convert with the Steinhart-Hart or Beta equation (Beta is about 3950 for common parts), not a straight line.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.