Build Your Own Plant-Watering System
Going on vacation but don't know who will take care of your plants? Build your own irrigation system so you don't have to worry about it.
Join the DZone community and get the full member experience.
Join For Freein this tutorial, we will show how to build a plant irrigation system with an esp8266 and easyiot cloud. with a web interface, we can control water pump and soil humidity remotely. now you can go on holiday and water the plants with your phone!
introduction
our irrigation system features:
- a remotely controlled water pump
- automatic irrigation if soil the humidity is low
- automatic and manual modes
- a humidity and water pump status display
- historical log soil humidity values
watering can be triggered in a web user interface. in this case, the pump will start for 10s. you can also use automatic mode. in this case, you set the soil humidity threshold in the interface. if the soil humidity drops below this value, the pump will switch on for 10s. after 10 minutes, it will check the soil moisture again and switch the pump on, if needed.
here's our easyiot cloud user interface:
and our historical soil humidity historical values:
materials
-
esp8266 module
-
water pump
-
soil moisture sensor module
-
regulator module 0-30v
-
12v power supply
-
2n2222 npn transistor
-
mb102 breadboard
-
5k resistor
easyiot cloud configuration
no easyiot cloud configuration is needed. just register to easyiot cloud .
program
our program is written in the esp8266 arduino ide . it uses the esp8266 mqtt easyiot cloud library to communicate and configure easyiot cloud. the program can be downloaded from our github .
in the program, change the following lines:
#define ap_ssid "xxx"
#define ap_password "xxx"
#define eiotcloud_username "xxx"
#define eiotcloud_password "xxx"
in the first two lines, set your access point name and password, then set your easyiot cloud username and password. then, upload the program to your esp8266 nodemcu. you can also use a different type of esp8266, but you'll need an additional power supply and an fdti usb adapter.
#include <esp8266wifi.h>
#include <mqtt.h>
#include <eeprom.h>
#define ap_ssid "xxx"
#define ap_password "xxx"
#define eiotcloud_username "xxx"
#define eiotcloud_password "xxx"
// create mqtt object
#define eiot_cloud_address "cloud.iot-playground.com"
#define pin_pump builtin_led //d0 // nodemcu built in led
#define pin_button d3 // nodemcu flash button
#define pin_hum_analog a0 // humidity pin
#define max_analog_val 956
#define min_analog_val 250
#define irrigation_time 10 // irrigation time in sec
#define irrigation_pause_time 300 // irrigation pause time in sec - only for auto irrigator
// irrigator state
typedef enum {
s_idle = 0, // irrigation idle
s_irrigation_start = 1, // start irrigation
s_irrigation = 2, // irrigate
s_irrigation_stop = 3, // irrigation stop
} e_state;
#define config_start 0
#define config_version "v01"
struct storestruct {
// this is for mere detection if they are your settings
char version[4];
// the variables of your settings
uint moduleid; // module id
} storage = {
config_version,
// the default module 0
0,
};
#define param_humidity_treshold "/sensor.parameter1"
#define param_manual_auto_mode "/sensor.parameter2"
#define param_pump_on "/sensor.parameter3"
#define param_humidity "/sensor.parameter4"
#define ms_in_sec 1000 // 1s
mqtt mymqtt("", eiot_cloud_address, 1883);
// intvariables
int state;
bool stepok = false;
int soilhumiditythreshold;
bool automode;
string valuestr("");
string topic("");
boolean result;
int lastanalogreading;
bool automodeold;
int soilhumiditythresholdold;
unsigned long starttime;
int soilhum;
int irrigatorcounter;
void setup() {
state = s_idle;
pinmode(pin_pump, output);
pinmode(pin_button, input);
automode = false;
stepok = false;
soilhumiditythresholdold = -1;
starttime = millis();
soilhum = -1;
serial.begin(115200);
wifi.mode(wifi_sta);
wifi.begin(ap_ssid, ap_password);
serial.println();
serial.println();
serial.print("connecting to ");
serial.println(ap_ssid);
while (wifi.status() != wl_connected) {
delay(500);
serial.print(".");
};
serial.println("wifi connected");
serial.println("connecting to mqtt server");
eeprom.begin(512);
loadconfig();
//set client id
// generate client name based on mac address and last 8 bits of microsecond counter
string clientname;
//clientname += "esp8266-";
uint8_t mac[6];
wifi.macaddress(mac);
clientname += mactostr(mac);
clientname += "-";
clientname += string(micros() & 0xff, 16);
mymqtt.setclientid((char*) clientname.c_str());
serial.print("mqtt client id:");
serial.println(clientname);
// setup callbacks
mymqtt.onconnected(myconnectedcb);
mymqtt.ondisconnected(mydisconnectedcb);
mymqtt.onpublished(mypublishedcb);
mymqtt.ondata(mydatacb);
//////serial.println("connect mqtt...");
mymqtt.setuserpwd(eiotcloud_username, eiotcloud_password);
mymqtt.connect();
delay(500);
serial.print("moduleid: ");
serial.println(storage.moduleid);
//create module if necessary
if (storage.moduleid == 0)
{
//create module
serial.println("create module: /newmodule");
storage.moduleid = mymqtt.newmodule();
if (storage.moduleid == 0)
{
serial.println("module not created. check module limit");
while(1)
delay(100);
}
// set module type
serial.println("set module type");
mymqtt.setmoduletype(storage.moduleid, "zmt_irrigator");
// create sensor.parameter1 - humidity treshold value
serial.println("new parameter: /"+string(storage.moduleid)+ param_humidity_treshold);
mymqtt.newmoduleparameter(storage.moduleid, param_humidity_treshold);
// set iscommand
serial.println("set iscommand: /"+string(storage.moduleid)+ param_humidity_treshold);
mymqtt.setparameteriscommand(storage.moduleid, param_humidity_treshold, true);
// create sensor.parameter2
// sensor.parameter2 - manual/auto mode 0 - manual, 1 - auto mode
serial.println("new parameter: /"+string(storage.moduleid)+ param_manual_auto_mode);
mymqtt.newmoduleparameter(storage.moduleid, param_manual_auto_mode);
// set iscommand
serial.println("set iscommand: /"+string(storage.moduleid)+ param_manual_auto_mode);
mymqtt.setparameteriscommand(storage.moduleid, param_manual_auto_mode, true);
// create sensor.parameter3
// sensor.parameter3 - pump on/ pump off
serial.println("new parameter: /"+string(storage.moduleid)+ param_pump_on);
mymqtt.newmoduleparameter(storage.moduleid, param_pump_on);
// set iscommand
serial.println("set iscommand: /"+string(storage.moduleid)+ param_pump_on);
mymqtt.setparameteriscommand(storage.moduleid, param_pump_on, true);
// create sensor.parameter4
// sensor.parameter4 - current soil humidity
serial.println("new parameter: /"+string(storage.moduleid)+ param_humidity);
mymqtt.newmoduleparameter(storage.moduleid, param_humidity);
// set description
serial.println("set description: /"+string(storage.moduleid)+ param_humidity);
mymqtt.setparameterdescription(storage.moduleid, param_humidity, "soil hum.");
// set unit
serial.println("set unit: /"+string(storage.moduleid)+ param_humidity);
mymqtt.setparameterunit(storage.moduleid, param_humidity, "%");
// set dblogging
serial.println("set unit: /"+string(storage.moduleid)+ param_humidity);
mymqtt.setparameterdblogging(storage.moduleid, param_humidity, true);
// save new module id
saveconfig();
}
subscribe();
lastanalogreading = analogread(pin_hum_analog);
automodeold = !automode;
}
void loop() {
while (wifi.status() != wl_connected) {
delay(500);
#ifdef debug
serial.print(".");
#endif
}
int in = digitalread(pin_button);
//serial.println(in);
if (in == 0)
{
while(digitalread(pin_button) == 0)
delay(100);
if (state == s_idle || state == s_irrigation_start)
state = s_irrigation_start;
else
state = s_irrigation_stop;
}
// post auto mode changes
if (automodeold != automode)
{
automodeold = automode;
if (automode)
valuestr = string("1");
else
valuestr = string("0");
topic = "/"+string(storage.moduleid)+ param_manual_auto_mode;
result = mymqtt.publish(topic, valuestr, 0, 1);
serial.print("publish topic: ");
serial.print(topic);
serial.print(" value: ");
serial.println(valuestr);
}
// post treshold changes
if (soilhumiditythreshold != soilhumiditythresholdold)
{
soilhumiditythresholdold = soilhumiditythreshold;
valuestr = string(soilhumiditythreshold);
topic = "/"+string(storage.moduleid)+ param_humidity_treshold;
result = mymqtt.publish(topic, valuestr, 0, 1);
serial.print("publish topic: ");
serial.print(topic);
serial.print(" value: ");
serial.println(valuestr);
}
if (istimeout())
{
starttime = millis();
// process every second
int aireading = analogread(pin_hum_analog);
serial.print("analog value: ");
serial.print(aireading);
serial.print(" ");
// filter s
lastanalogreading += (aireading - lastanalogreading) / 10;
serial.print(lastanalogreading);
// calculate soil humidity in %
int newsoilhum = map(lastanalogreading, min_analog_val, max_analog_val, 100, 0);
serial.print(", soil hum %:");
serial.println(newsoilhum);
// limit to 0-100%
if (newsoilhum < 0)
newsoilhum = 0;
if (newsoilhum > 100)
newsoilhum = 100;
// report soil humidity if changed
if (soilhum != newsoilhum)
{
soilhum = newsoilhum;
//esp.send(msghum.set(soilhum));
valuestr = string(soilhum);
topic = "/"+string(storage.moduleid)+ param_humidity;
result = mymqtt.publish(topic, valuestr, 0, 1);
serial.print("publish topic: ");
serial.print(topic);
serial.print(" value: ");
serial.println(valuestr);
}
// irrigator state machine
switch(state)
{
case s_idle:
if (irrigatorcounter <= irrigation_pause_time)
irrigatorcounter++;
if (irrigatorcounter >= irrigation_pause_time && automode)
{
if (soilhum <= soilhumiditythreshold)
state = s_irrigation_start;
}
break;
case s_irrigation_start:
irrigatorcounter = 0;
digitalwrite(pin_pump, high);
//esp.send(msgmotorpump.set((uint8_t)1));
valuestr = string(1);
topic = "/"+string(storage.moduleid)+ param_pump_on;
result = mymqtt.publish(topic, valuestr, 0, 1);
serial.print("publish topic: ");
serial.print(topic);
serial.print(" value: ");
serial.println(valuestr);
state = s_irrigation;
break;
case s_irrigation:
if (irrigatorcounter++ > irrigation_time)
state = s_irrigation_stop;
break;
case s_irrigation_stop:
irrigatorcounter = 0;
state = s_idle;
//esp.send(msgmotorpump.set((uint8_t)0));
valuestr = string(0);
topic = "/"+string(storage.moduleid)+ param_pump_on;
result = mymqtt.publish(topic, valuestr, 0, 1);
digitalwrite(pin_pump, low);
break;
}
}
}
void loadconfig() {
// to make sure there are settings, and they are yours!
// if nothing is found it will use the default settings.
if (eeprom.read(config_start + 0) == config_version[0] &&
eeprom.read(config_start + 1) == config_version[1] &&
eeprom.read(config_start + 2) == config_version[2])
for (unsigned int t=0; t<sizeof(storage); t++)
*((char*)&storage + t) = eeprom.read(config_start + t);
}
void saveconfig() {
for (unsigned int t=0; t<sizeof(storage); t++)
eeprom.write(config_start + t, *((char*)&storage + t));
eeprom.commit();
}
string mactostr(const uint8_t* mac)
{
string result;
for (int i = 0; i < 6; ++i) {
result += string(mac[i], 16);
if (i < 5)
result += ':';
}
return result;
}
void waitok()
{
while(!stepok)
delay(100);
stepok = false;
}
boolean istimeout()
{
unsigned long now = millis();
if (starttime <= now)
{
if ( (unsigned long)(now - starttime ) < ms_in_sec )
return false;
}
else
{
if ( (unsigned long)(starttime - now) < ms_in_sec )
return false;
}
return true;
}
void subscribe()
{
if (storage.moduleid != 0)
{
// sensor.parameter1 - humidity treshold value
mymqtt.subscribe("/"+string(storage.moduleid)+ param_humidity_treshold);
// sensor.parameter2 - manual/auto mode 0 - manual, 1 - auto mode
mymqtt.subscribe("/"+string(storage.moduleid)+ param_manual_auto_mode);
// sensor.parameter3 - pump on/ pump off
mymqtt.subscribe("/"+string(storage.moduleid)+ param_pump_on);
}
}
void myconnectedcb() {
#ifdef debug
serial.println("connected to mqtt server");
#endif
subscribe();
}
void mydisconnectedcb() {
#ifdef debug
serial.println("disconnected. try to reconnect...");
#endif
delay(500);
mymqtt.connect();
}
void mypublishedcb() {
#ifdef debug
serial.println("published.");
#endif
}
void mydatacb(string& topic, string& data) {
#ifdef debug
serial.print("receive topic: ");
serial.print(topic);
serial.print(": ");
serial.println(data);
#endif
if (topic == string("/"+string(storage.moduleid)+ param_humidity_treshold))
{
soilhumiditythreshold = data.toint();
serial.println("soilhumiditythreshold");
serial.println(data);
}
else if (topic == string("/"+string(storage.moduleid)+ param_manual_auto_mode))
{
automode = (data == string("1"));
serial.println("auto mode");
serial.println(data);
}
else if (topic == string("/"+string(storage.moduleid)+ param_pump_on))
{
//switchstate = (data == string("1"))? true: false;
if (data == string("1"))
state = s_irrigation_start;
else
state = s_irrigation_stop;
serial.println("pump");
serial.println(data);
}
}
hardware
for the sake of simplicity, we will use an esp8266 nodemcu. you can use any other esp8266 type with an additional 3.3v power supply.
connecting the soil humidity sensor
use an esp8266 3.3v power supply to power the soil humidity sensor module. this means that you connect esp8266 3.3v to vcc on the sensor and the esp8266 gnd with the sensor gnd. then connect the soil humidity sensor analog output (mark ao) to the analog input on the esp8266 board a0. leave the do on the soil humidity sensor unconnected.
connecting the water pump motor to your esp8266
to connect the water pump motor, we will need a relay module. because a relay module uses 5v a power supply, and the esp8266 uses a 3.3v power supply, we will add a transistor to drive a 5v relay with 3.3v. the transistor is connected to the esp8266 d0 pin through a resistor. if you connect a relay directly to the esp8266, you will fry it. the other side of the relay is connected to the motor pump and adjustable power supply. set the voltage of the motor pump between 6-9v.
connecting the manual button
if you are using an esp8266 nodemcu, you can use the onboard flash button to manually switch on and off the motor pump. if you are using another type of esp8266, then use a 10k resistor on one side and connect it to the 3.3v power supply, then connect the other side to the d3 pin. then connect d3 to gnd through the button.
Published at DZone with permission of Igor Jarc, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
What Is mTLS? How To Implement It With Istio
-
Web Development Checklist
-
Essential Architecture Framework: In the World of Overengineering, Being Essential Is the Answer
-
Design Patterns for Microservices: Ambassador, Anti-Corruption Layer, and Backends for Frontends
Comments