Introduction

This document got written for an English. This project is for monitoring my plantsā€™ moisture. Optionally there can be a pump connected to regulate the moisture of the plantā€™s soil.

The documentation here is written like an Instructables, to it is easy for anyone to reproduce it. Last 7 days of my moisture Showing the moisture level the span of almost a month in a nice graph. Instead of going for an automated watering plant with valves/pumps you can opt for getting notifications if a plant doesnā€™t report moisture or is below some level.

Materials and tools šŸ› 

What do you need to get started?

AmountNameComment
1ESP32Doesnā€™t matter witch one. See if you cannot read the analog value on a certaine pin.
1Pumps and soWe opted for a 12V motor and a lot of valves.
1sensorsWe chose this one because it is cheap
Bunch ofCables
1multiplexerIf you want to use more plants with a mcu than it has analog inputs we need to multiplex the pin.

ESP32 šŸ“¶

esp

Pumps or Valves āš™

pumps Iā€™ve opted for valves.

Sensors šŸŒæ

sensor

Waterpipes šŸ’§

pipes

Cables and transistors šŸ”Œ

box

For multiple plants āŽ˜

Multiplexer / Demultiplexer

This is very cheap and you can read 8 analog values from one analog pin if you switch correctly. logic

Multiplexer for the Valves/Pumps

In case you want to have a lot of plants dependent on the same esp32 (which I donā€™t recommend) you can use a multiplexer. You can read a lot of more analog inputs with this connected before the real analog input. You can control what external plant to connect to and read the moisture. If you donā€™t use this you have to use transistors. In the picture of Cables and transistors you can see a few transistors that are good to use. multiplexer

Preparation/Prerequisites

Programming knowledge

In the beginning of the class it as mandatory to learn the basic of Arduino C++. We developed a traffic lightšŸš¦. We gathered a understanding of how the Arduino Framework works and meddled around with with timers (not really hardware timers but writing the milliseconds since boot into variables and using that as timers). We used inputs (buttons) to control the traffic light. Problems encountered

Server

Another prerequisite is to own a domain that your plants can pipe the information to no matter where they are. On this domain there should be a database. It is inherently useful to get a timeseries database for our project.

InfluxDB

We went for InfluxDB but you can choose a another. Check out other time series databases on Wikipedia :Time series database - Wikipedia. The nice thing about time series databases is that you just send something there like a moisture value and a name and you can already make nice plots because all the timestamps when the DB received the messages are recorded down to the microsecond.

Hints and Tips

If you are in the same network as the server you donā€™t have to send the data to your public domain but to the local address. You donā€™t even have to use https. I just use http and I donā€™t care if it can be read in this instance. Usually Iā€™m more picky about network security but not when it is about something like this.

Problems encountered

I had lots of problems with the adc (Analog digital converter) while using WiFi.

With some ESP there are lots of booting issues with Wifi enabled.

Problems with measuring the moisture

Here you can See that the system was sending the moisture to the server every 10s. This is a lot and will be reduced in the future. The problem here is that the value is not even correct. Another board was used with other analog values.

Code šŸ’»

SendToInfluxDB

This is rather straight forward. You donā€™t really have to understand it all.

 
void sendDataToInfluxDB(const String &plantName, float moisture)
{
  HTTPClient http;
 
  // Specify the InfluxDB endpoint
  String url = "https://influx.lucacordes.xyz/api/v2/write?org=myorg&bucket=mybucket";
 
  // Specify the headers
  http.begin(url);
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  http.addHeader("Authorization", "Token YP4ZEs5WFSWgjiBUoZXjjy9Oo03IGZ-0l3j5EOVpD93R-w8lbME1vrzic3xD1xH_rPLpjG3LRlVrNHjspfnBxA==");
 
  // Construct the InfluxDB line protocol data
  String data = plantName + " moisture=" + String(moisture);
 
  // Send the POST request
  int httpResponseCode = http.POST(data);
 
  // Check for a successful response
  if (httpResponseCode > 0)
  {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
  }
  else
  {
    Serial.print("Error on HTTP request: ");
    Serial.println(httpResponseCode);
  }
 
  // Free resources
  http.end();
}
 

Get the moisture from the plant

In order to get the moisture form the soil of a plant you have to know the sensor before. The sensor consists of three wires: Aout, GND (Ground), Vcc (our Voltage here 3V3). Basically the voltage on Aout changes depending on the mostisture.

In order to get the maxima of the voltage of Aout we have to measure it dry and submerged in water. Here you can see in the comments of the code the voltages for the edges of our scale.

float getMoisture(int pin)
{
  int sensorValue = analogRead(pin);
 
  float analog = float(sensorValue) / (2 << 12) * 3.3;
 
  // v ref 3.3
 
  // dry 2.67v
 
  // wet 1v
 
  //(1.67-(analog-1))/1.67
 
  float moisture = (1.67 - (analog - 1.0)) / 1.67;
 
  // Print the analog value to the serial monitor
  Serial.print("Voltage: ");
  Serial.print(analog);
  Serial.print("  Moisture: ");
  Serial.println(moisture);
  return moisture;
}

Object Orientated Programming

Now we need some way to use it. We need a class or a struct to organize the code and make it more readable and less prone to human errors.

struct plant
{
  String name;
  int pin;
  plant(String passedname,int passedpin){
    name=passedname;
    pin=passedpin;
  };
  void MeasureAndSend()
  {
    sendDataToInfluxDB(name, getMoisture(pin));
  };
};

Now with the struct we can simply call the function to send the moisture to the DB.

The Code as a whole

#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
 
void sendDataToInfluxDB(const String &plantname, float mostisture);
float getMoisture(int pin);
const char *ssid = "Android";
const char *password = "fortytwo";
struct plant
{
  String name;
  int pinPlant;
  int pinValve;
  plant(String passedname,int passedPlantPin,int passedValvePin=0){
    name=passedname;
    pinPlant=passedPlantPin;
    pinValve=passedValvePin;
    digitalWrite(pinValve, LOW);
    pinMode(pinValve, OUTPUT);
  };
  
  /*This function has three taks:
  Measure the Mostisture 
  Send the Moisture to DB
  Water the plant if needed
  */
  void call()
  {
	float moisture=getMoisture(pinPlant);
    sendDataToInfluxDB(name, moisture);
    if(moisture <0.40){
	   digitalWrite(pinValve, HIGH);
	   delay(2000); //let the valve be open for 2 seconds
	   digitalWrite(pinValve, LOW);
     Serial.print("opening");
    }
  };
};
 
//Here are my plants 
//plant MajaMonsterra;
plant testing("testing",7,39);
void setup()
{
    Serial.begin(115200);
 
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");
  
}
 
void loop()
{
  testing.call();
  delay(10*1000); //every 10 sec
  
  //delay(10*60*1000); //every 10 min
}
 
void sendDataToInfluxDB(const String &plantName, float moisture)
{
  HTTPClient http;
 
  // Specify the InfluxDB endpoint
  String url = "https://influx.lucacordes.xyz/api/v2/write?org=myorg&bucket=mybucket";
 
  // Specify the headers
  http.begin(url);
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  http.addHeader("Authorization", "Token YP4ZEs5WFSWgjiBUoZXjjy9Oo03IGZ-0l3j5EOVpD93R-w8lbME1vrzic3xD1xH_rPLpjG3LRlVrNHjspfnBxA==");
 
  // Construct the InfluxDB line protocol data
  String data = plantName + " moisture=" + String(moisture);
 
  // Send the POST request
  int httpResponseCode = http.POST(data);
 
  // Check for a successful response
  if (httpResponseCode > 0)
  {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
  }
  else
  {
    Serial.print("Error on HTTP request: ");
    Serial.println(httpResponseCode);
  }
 
  // Free resources
  http.end();
}
 
float getMoisture(int pin)
{
  int sensorValue = analogRead(pin);
 
  float analog = float(sensorValue) / (2 << 12) * 3.3;
 
  // v ref 3.3
 
  // dry 2.67v
 
  // wet 1v
 
  //(1.67-(analog-1))/1.67
 
  float moisture = (1.67 - (analog - 1)) / 1.67;
 
  // Print the analog value to the serial monitor
  Serial.print("Voltage: ");
  Serial.print(analog);
  Serial.print("  Moisture: ");
  Serial.println(moisture);
  return moisture;
}

Conclusion

This project has been a blast with my mates. Phillip and Oliver were helpful and were happy to lean. We not only enjoyed our own project but all the projects in the class. Please have a look at them here.

Future Ideas šŸš€

On the backend side there is a lot to be done. Letā€™s say we have 20 plants and each sends every 10min. Every entry in our DB is 200 Bytes. This would accumulate to 210 Mega Byte in one Year. . It would be nice to downsample our data after one year or so. But honestly there is not really much storage to recapture and it might not be worth the hassle to pursue this idea. Carlo will use this as a base on constructing his automated farm.

Links to Classmatesā€™ Projects

Other Watering plant group with nice PCB and lots of features we didnā€™t have. RC Motionsensor in action