Plant monitoring / digital farming with HiGrow ESP32
For checking up on our plants, vertical farming walls etc. during corona, we have a monitoring system based on the HiGrow ESP32 capacitative soil moisture measuring system.
sand | mulch/soil |
|
---|---|---|
Dry | 2747 | 2958 |
Slight moisture | 2501 | 2731 |
Moist | 2108 | 2362 |
Very moist | 1636 | 1856 |
Wet/soaking | 1513 | 1488 |
Here is the code:
/*
Code for Higrow to read the temp & RH sensor and call a URL on a server with all data
Reads DHT11/22 temp & rh sensors from an ESP32 using multitasking.
This example depends on the ESP32Ticker library to wake up the task every 20 seconds. Please install Ticker-esp32 library
Thanks:
lucafabbri for original highgrow board and code
https://github.com/lucafabbri/HiGrow-Arduino-Esp/blob/master/HiGrowEsp32/HiGrowEsp32.ino
jenschr updated simpler code
https://gist.github.com/jenschr/711899fe286ba5a63c044191b63813ca
https://flashgamer.com/blog/comments/review-of-higrow-esp32-moisture-and-temperature-sensor
*/
#define UNITNAME "Plant%20soil%2002" // remember no spaces. Spaces may be replaced with URLencoded %20
//#define UPDATEINTERVAL 60000 // how often to connect on the internet and update data. 60000 = once a minute 3600000 = once an hour
#define UPDATEINTERVAL 3600000
#define DISPLAYPLACE 1200
#define UNIT "Soil%20cap"
#define MAXDOWN 14400
#define SERVERNAME "serverName"
#define ALARMDRYTHRESHOLD 2450
#define ALARMWETTHRESHOLD 1700
// credentials are stored in include file credentials.h , must look like this:
//#define WIFIPASS "secretPassword"
//#define WIFISSID "theSsid"
//#define REALM "theRealm"
//#define SRVPASS "theRealmPassword"
#include "DHTesp.h"
#include "Ticker.h"
#include <HTTPClient.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include "credentials.h"
WiFiMulti wifiMulti;
int waterlevel = 0;
unsigned long updateTimer = 0;
float temp = 0;
float rh = 0;
float dewPoint = 0;
unsigned long dat = 0;
char url[512]; // character buffer for storing URL to get in
char dataText[1024]; // character buffer for storing text in
char text[1024]; // character buffer for storing text in
unsigned int alarmstatus = 0;
char netStatus[512]; // buffer for storing the network status in
long last_display_update_millis = 0;
boolean toggle = 0;
int disconnected_seconds = 0;
HTTPClient http;
DHTesp dht;
const int LEDPIN = 16;
const int SOILPIN = 32;
int dhtPin = 22;
void tempTask(void *pvParameters);
bool getTemperature();
void triggerGetTemp();
/** Task handle for the light value read task */
TaskHandle_t tempTaskHandle = NULL;
/** Ticker for temperature reading */
Ticker tempTicker;
/** Flag if task should run */
bool tasksEnabled = false;
/** Pin number for DHT11 data pin */
/**
initTemp
Setup DHT library
Setup task and timer for repeated measurement
@return bool
true if task and timer are started
false if task or timer couldn't be started
*/
bool initTemp() {
byte resultValue = 0;
// Initialize temperature sensor
dht.setup(dhtPin, DHTesp::DHT11);
Serial.println("DHT initiated");
// Start task to get temperature
xTaskCreatePinnedToCore(
tempTask, /* Function to implement the task */
"tempTask ", /* Name of the task */
4000, /* Stack size in words */
NULL, /* Task input parameter */
5, /* Priority of the task */
&tempTaskHandle, /* Task handle. */
1); /* Core where the task should run */
if (tempTaskHandle == NULL) {
Serial.println("Failed to start task for temperature update");
return false;
} else {
// Start update of environment data every 20 seconds
tempTicker.attach(20, triggerGetTemp);
}
return true;
}
/**
triggerGetTemp
Sets flag dhtUpdated to true for handling in loop()
called by Ticker getTempTimer
*/
void triggerGetTemp() {
if (tempTaskHandle != NULL) {
xTaskResumeFromISR(tempTaskHandle);
}
}
/**
Task to reads temperature from DHT11 sensor
@param pvParameters
pointer to task parameters
*/
void tempTask(void *pvParameters) {
Serial.println("tempTask loop started");
while (1) // tempTask loop
{
if (tasksEnabled) {
// Get temperature values
getTemperature();
}
// Go to sleep again
vTaskSuspend(NULL);
}
}
/**
getTemperature
Reads temperature from DHT11 sensor
@return bool
true if temperature could be aquired
false if aquisition failed
*/
bool getTemperature() {
// Reading temperature for humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor)
TempAndHumidity newValues = dht.getTempAndHumidity();
// Check if any reads failed and exit early (to try again).
if (dht.getStatus() != 0) {
Serial.println("DHT11 error status: " + String(dht.getStatusString()));
return false;
}
temp = newValues.temperature;
rh = newValues.humidity;
dewPoint = dht.computeDewPoint(newValues.temperature, newValues.humidity);
waterlevel = analogRead(SOILPIN);
Serial.print("Soil wetness: ");
Serial.print(waterlevel);
Serial.println(" Temp C: " + String(temp) + " Humidity %RH: " + String(rh) + " Dewpoint: " + String(dewPoint));
sprintf(dataText, "Temp%%20%.1f%%20Humidity%%20RH%%20%.1f%%20Dewpoint%%20%.1f",
temp,
rh,
dewPoint
);
//text = " Temp C: " + String(temp) + " Humidity %RH: " + String(rh) + " Dewpoint: " + String(dewPoint);
return true;
}
void displayUpdate() {
netStatus[0] = '\0';
sprintf(netStatus, "%s %s %s %ddBm %i %s",
WIFISSID,
WiFi.macAddress().c_str(),
WiFi.localIP().toString().c_str(),
WiFi.RSSI(),
WiFi.status(),
UNITNAME
);
Serial.println(netStatus);
}
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println("Boot");
initTemp();
wifiMulti.addAP(WIFISSID, WIFIPASS);
// Signal end of setup() to tasks
tasksEnabled = true;
}
void loop() {
if (!tasksEnabled) {
// Wait 2 seconds to let system settle down
delay(2000);
// Enable task that will read values from the DHT sensor
tasksEnabled = true;
if (tempTaskHandle != NULL) {
vTaskResume(tempTaskHandle);
}
}
yield();
if (wifiMulti.run() == WL_CONNECTED) { // keep wifimulti connected and happy
}
if (millis() - last_display_update_millis > 1000) { // update display/serial debugging
last_display_update_millis = millis();
displayUpdate();
digitalWrite(LEDPIN, toggle);
if (WiFi.status() == WL_CONNECTED) { // flash led if connected to wifi
toggle = !toggle;
disconnected_seconds = 0;
}
else {
toggle = 0; // turn led fixed on if not connected to wifi
disconnected_seconds++; // count how many seconds we have been disconnected from wifi
}
}
if (disconnected_seconds > 30) { // if we have been disconnected for a while, reboot the whole processor
ESP.restart();
}
//------- function specific
// dry warning
if (waterlevel > ALARMDRYTHRESHOLD) {
sprintf(text, "%s%s",
"Warning%20soil%20dry%20",
dataText
);
alarmstatus = 2;
}
else if (waterlevel < ALARMWETTHRESHOLD) {
// wet warning
sprintf(text, "%s%s",
"Warning%20soil%20too%20wet%20",
dataText
);
alarmstatus = 2;
}
else {
// ok, no warning
sprintf(text, "%s%s",
"Soil%20probably%20ok%20",
dataText
);
alarmstatus = 0;
}
if (millis() - updateTimer > UPDATEINTERVAL || updateTimer == 0) // do something every x milliseconds)
{
statusUpdate(); // do it!
updateTimer = millis();
}
/*
int waterlevel = analogRead(SOILPIN);
Serial.print("waterlevel: ");
Serial.println(waterlevel);
*/
}
void statusUpdate() {
dat = waterlevel;
Serial.print("[HTTP] begin...\n");
sprintf(url, "%s?realm=%s&pass=%s&device=%s&dpos=%i&data=%i&unit=%s&alarmlevel=%i&descr=%s&maxdown=%i",
SERVERNAME,
REALM,
SRVPASS,
UNITNAME,
DISPLAYPLACE,
dat, //data
UNIT, // unit
alarmstatus, // alarmlevel
text, // descr
MAXDOWN // maxdown
);
http.begin(url);
//http.begin("https://www.howsmyssl.com/a/check");
Serial.print("[HTTP] GET...\n");
Serial.println(url);
// start connection and send HTTP header
int httpCode = http.GET();
// httpCode will be negative on error
if (httpCode > 0) {
// HTTP header has been send and Server response header has been handled
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
} // end statusUpdate