Plant guard for the window sill Part 5 - Illuminance measurement

In this part of the series, I will recapitulate the most important one from the previous parts. In retrospect, we have already made some changes and additions to our plant guardian. For example, we expanded the number of soil moisture sensors by 6 and added a combined temperature and humidity sensor. For plants, these are a good basis. However, another important environmental factor is still missing for our flora. This is the light, especially the illuminance! The illuminance in the Unit Lux is measured by gene measurement.

Most plants feel most comfortable in the illuminance range of 300-1500 lux (depending on the plant type). Of course, we want to take this into account and keep an eye on the brightness in Lux at all times with our plant monitor. For this purpose, we record the illuminance in the Lux unit and display it as an information value on the mobile phone display in the usual way.

For this task, in our case, the illuminance sensor BH1750 is suitable, as it is compatible with the 3.3 volt data levels of the ESP32, as well as the illuminance as a data packet via the I2C interface directly in Lux unit to our ESP.

The light sensor can be ordered, e.g. at AZ-Delivery Shop as Module GY-302.

If you are interested in further details about the module, you will find e.g. Here for more information.

 

The BH1750 has a very large measuring range and the resolution can be chosen by configuration parameters between 0.5 lux, 1 lux and 4 lux.

For our project we select the average resolution of 1 lux.

With the addition of the light sensor, we have now put our required sensors together for the project

 

Here you will find the previous plant monitor parts, the study of which I would like to recommend, as important information, e.g. about the calibration of the soil moisture sensors, is included:


Let's get back to the important parts list. You will find all the relevant parts of this project that you need for a replica in the following parts list:

Number

Description

Note

1

DHT 22

 

 

DHT 11

Alternative to DHT 22

1

KY-016 LED RGB Module

 

1

ESP-32 Dev Kit C

 

6

Soil Moisture Sensor Module V1.2

 

1

MB102 Power Supply Adapter

For breadboard setup

1

Module GY-302

Illuminance sensor

12

Resistanceaccording to the description

 

 

 

Let's take a look at the updated schematic/wiring plan of the plant monitor:

Part 5: Circuit

 

We recognize the newly added light sensor BH1570. We connect the periphery as follows:

RGB_Led module

RGB-Led Anode

ESP32 Pin

Red

0

Green

15

Blue

14

 

Soil moisture sensors

Humidity sensor

ESP32 Pin

1

Sp

2

Sn

3

34

4

35

5

32

6

33

 

Temperature/humidity sensor DHT 22

Pin

ESP32 Pin

DATA /IN OUT

4

 

Illuminance sensor BH1570

BH1570 PIN

ESP32 Pin

Sda

21

Scl

22

Addr

Gnd

 

After we have convinced ourselves of the correct wiring upload the following code to our ESP:

 

#include <Driver/Adc.H>     Build In Library. No external library needed
#include <esp_wifi.h>
#include <Wifi.H>
#include <WiFiClient.H>
#include <ESPmDNS.H>
#include <BlynkSimpleEsp32.H>
#include <Eeprom.H>
#include <Preferences.H>
#include <Wire.H>
#include "DHT.h"    REQUIRES the following Arduino libraries:
- DHT Sensor Library: https://github.com/adafruit/DHT-sensor-library
- Adafruit Unified Sensor Lib: https://github.com/adafruit/Adafruit_Sensor


Port definition RGB LED module
#define LED_Rot     0     Red LED 
#define LED_Blau    14    Blue LED
#define LED_Gruen   15    // Green LED

// I2C port definitions
#define SDA1 21
#define SCL1 22

// LED PWM settings
#define PWMfreq 5000  // 5 kHz base frequency for LED display
#define PWMledChannelA  0
#define PWMledChannelB  1
#define PWMledChannelC  2
#define PWM resolution  8 // 8 bit resolution for LED PWM

// Time / timing definitions for sensor queries
#define ADCAttenuation ADC_ATTEN_DB_11       // ADC_ATTEN_DB_11 = 0-3.6V damping ADC (ADC extension
#define MoisureSens_Poll_MinInterval 3600       // Minimum measured value transmission interval between two soil moisture measurements in seconds.
#define DHT_Poll_MinInterval 2700               // Minimum measured value transmission interval between two temperature and humidity measurements in seconds.
#define BH_Poll_MinInterval  1800             // Minimum measured value transmission interval between two light intensity queries in seconds.

#define DEBUG                                // When defined, various information and measured values ​​are output on the serial interface. Please delete before productive use!
#define MaxSensors 6                         // Maximum number of connectable humidity sensors
#define MinSensorValue 500                   // Mnidest AD value to report a sensor input channel (1-6) as "active" to the system. (Humidity sensor is connected during the boot phase)
#define StartInit true
#define RunTime false
#define Sens_Calib true
#define Sens_NOTCalib false
#define EEPROM_SIZE 512                      // Definition size of the internal EEPROM

// Blynk APP definitions
#define BLYNK_GREEN     "# 23C48E"
#define BLYNK_BLUE      "# 04C0F8"
#define BLYNK_YELLOW    "# ED9D00"
#define BLYNK_RED       "# D3435C"
#define BLYNK_BLACK     "#000000"
#define BLYNK_WHITE     "#FFFFFF"
#define BLYNK_PRINT Serial 1
#define BLYNK_NO_BUILTIN
#define BLYNK_NO_FLOAT
// # define BLYNK_DEBUG

// DHT configuration
#define DHTPIN 4                             // Digital pin connected to the DHT sensor
#define DHTTYPE DHT22                        // DHT 22 (AM2302), AM2321
// # define DHTTYPE DHT21 // If a DHT 21 (AM2301) is used in your project, please define this setting

struct SystemRunParameters
{   int Data[MaxSensors * 2] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; // Calibration data for moisture sensor. Please note the project text and adjust the values ​​accordingly   byte StatusBorderPercentValues[MaxSensors * 2][2] = { {10, 50},     // Two-dimensional array for percentage limit values ​​(traffic light), individually for each humidity sensor (1 -6)     {10, 50},     {10, 50},     {10, 50},     {10, 50},     {10, 50}   };   String SensorName[MaxSensors + 3] = {"Plant 1", "Plant 2", "Plant 3", "Plant 4", "Plant 5", "Plant 6", "Humidity", "Temperature", "Luminous intensity"}; // Sensor name, which is also shown in the APP as a heading
};

struct MoistureSensorData
{   int Percent[MaxSensors] = {0, 0, 0, 0, 0, 0};  // Moisture sensor data in percent   byte Old_Percent[MaxSensors] = {0, 0, 0, 0, 0, 0}; // Previous _ moisture sensor data in percent (purpose: save data quantity.)   bool DataValid [MaxSensors] = {false, false, false, false, false, false};
};

struct DHTSensorData
{   float Humidity = 0 ;      // Humidity sensor data in percent   float Temperature = 0;   float Old_Humidity = 0 ;      // Humidity sensor data in percent   float Old_Temperature = 0;   bool DataValid  = false;   bool SensorEnabled  = false;
};

struct BHLightSensorData
{   int lux = 0 ;          // Luminous intensity in lux   int Old_Lux = 0 ;      // Luminous intensity in lux   bool DataValid  = false;   bool SensorEnabled  = false;
};

DHT dht(DHTPIN, DHTTYPE); // Initialize DHT sensor instance

TwoWire I2CWire = TwoWire(0);


// Global Variables

char auth[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // Enter the Blynk app according to the instructions for the Auth Token (email).
// Your WiFi access data.
char ssid[] = "Your_WLAN_SSID";                 // Please adapt to your own WLAN SSID
char passport[] = "Your _WLAN _Passwort!";           // Please adapt to your own WiFi password
char ESPName[] = "PlantSensor1";

SystemRunParameters SysConfig;
MoistureSensorData MMeasure;
DHTSensorData  DHTMeasure;
BHLightSensorData BHMeasure;


byte AttachedMoistureSensors = 0;               // Detected Active Moisture Sensors (Count)
byte BH1750I2CAddress = 0;                      // Detected BH1750 I2C Address
bool Connected2Blynk = false;                   // Bool Variable. Stores the Connectionstate to the Blynk Cloud
unsigned long Moisure_ServiceCall_Handler = 0;  // Delay Variable for Delay between Moisure Readings
unsigned long DHT_ServiceCall_Handler = 0;      // Delay Variable for Delay between DHT Readings
unsigned long BH_ServiceCall_Handler = 0;       // Delay Variable for Delay between BH1750 Readings
unsigned long chipid;

void setup() {   pinMode(LED_Rot, OUTPUT);   pinMode(LED_Blau, OUTPUT);   pinMode(LED_Gruen, OUTPUT);   Serial.begin(115200);             // initialize serial communication at 115200 bits per second:   I2CWire.begin(SDA1, SCL1, 400000); // join i2c bus (address optional for master)   //chipid=ESP.getEfuseMac();         //The chip ID is essentially its MAC address(length: 6 bytes).   //sprintf(ESPName, "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d", chipid);   ledcSetup(PWMledChannelA, PWMfreq, PWMresolution);   ledcSetup(PWMledChannelB, PWMfreq, PWMresolution);   ledcSetup(PWMledChannelC, PWMfreq, PWMresolution);   ledcAttachPin(LED_Rot, PWMledChannelA);   // attach the channel to the GPIO to be controlled   ledcAttachPin(LED_Blau, PWMledChannelB);   ledcAttachPin(LED_Gruen, PWMledChannelC);   SetLedConfig(255, 255, 255);
#ifdef DEBUG   Serial.print(F("Verbindung zu WLAN"));
#endif   WiFi.disconnect();   WiFi.setHostname(ESPName);   // esp_wifi_set_mode(WIFI_MODE_STA);   WiFi.mode(WIFI_STA);   WiFi.begin(ssid, pass);   WiFi.setSleep(false);   if (WiFi.waitForConnectResult() != WL_CONNECTED)   {     while (WiFi.status() != WL_CONNECTED)     {       delay(1000);       WiFi.begin(ssid, pass);  // try again, if first Connect is unsuccessful
#ifdef DEBUG       Serial.print(F("."));
#endif     }   }
#ifdef DEBUG   Serial.println(F(" erfolgreich."));   Serial.print(F("IP Address: "));   Serial.println(WiFi.localIP());   Serial.print(F("Hostname: "));   Serial.println(ESPName);
#endif   if (!MDNS.begin(ESPName))   {     Serial.println("Error setting up MDNS responder!");   }   MDNS.addService("plant", "tcp", 400);   Blynk.config(auth);   // in place of Blynk.begin(auth, ssid, pass);   while (Blynk.connect() == false) {     delay(500);     Serial.print(".");   }

#ifdef DEBUG   Serial.println(F("Systemkonfiguration:"));
#endif   if (EEPROM.begin(EEPROM_SIZE))   {
#ifdef DEBUG     Serial.print(EEPROM_SIZE);     Serial.println(F(" Bytes EEPROM"));
#endif   }   Run_MoistureSensors(StartInit);
#ifdef DEBUG   Serial.print(AttachedMoistureSensors);   Serial.println(F(" Bodenfeuchtigkeitsensor(en)"));
#endif   dht.begin();   DHTMeasure.SensorEnabled = Run_DHTSensor (StartInit);   if (DHTMeasure.SensorEnabled)   {
#ifdef DEBUG     Serial.println(F("1 DHT 22 Sensor"));
#endif   }   BHMeasure.SensorEnabled = Run_BH1750Sensor(StartInit);   if (BHMeasure.SensorEnabled)   {
#ifdef DEBUG     Serial.println(F("1 B1750 Light Sensor"));
#endif   }
}

void CheckConnection() {   Connected2Blynk = Blynk.connected();   if (!Connected2Blynk) {     Serial.println("Not connected to Blynk server");     Blynk.connect(3333);  // timeout set to 10 seconds and then continue without Blynk   }   else {     Serial.println("Connected to Blynk server");   }
}

bool Run_BH1750Sensor (bool Init)   // Runtime Funktion für den BH170 Lichtsensor
{   byte ec;   if ((millis() - BH_ServiceCall_Handler >= BH_Poll_MinInterval * 1000) | (Init))   {     BH_ServiceCall_Handler = millis();     if (Init)     {       bool BH1750Detected = false;       I2CWire.beginTransmission(35);       ec = I2CWire.endTransmission(true);       if (ec == 0)       {         BH1750Detected = true;         BH1750I2CAddress = 35; // BH1750 I2C Adresse ist DEC 35       } else       {         I2CWire.beginTransmission(92);         ec = I2CWire.endTransmission(true);         if (ec == 0)         {           BH1750Detected = true;           BH1750I2CAddress = 92; // BH1750 I2C Adresse ist DEC 92         }       }       if (BH1750Detected)       {         // Intialize Sensor         I2CWire.beginTransmission(BH1750I2CAddress);         I2CWire.write(0x01);    // Turn it on before we can reset it         I2CWire.endTransmission();         I2CWire.beginTransmission(BH1750I2CAddress);         I2CWire.write(0x07);    // Reset         I2CWire.endTransmission();         I2CWire.beginTransmission(BH1750I2CAddress);         I2CWire.write(0x10);    // Continuously H-Resolution Mode ( 1 lux Resolution) Weitere Modis möglich, gemäß Datenblatt         //I2CWire.write(0x11);  // Continuously H-Resolution Mode 2 ( 0.5 lux Resolution)         //I2CWire.write(0x20);  // One Time H-Resolution Mode  ( 1 lux Resolution)         //I2CWire.write(0x21);  // One Time H-Resolution Mode2 ( 0.5 lux Resolution)         I2CWire.endTransmission();         Blynk.setProperty(V9, "color", BLYNK_WHITE);         Blynk.setProperty(V9, "label", SysConfig.SensorName[8]);       } else       {         Blynk.setProperty(V9, "label", "Deaktiviert");         Blynk.setProperty(V9, "color", BLYNK_BLACK);         Blynk.virtualWrite(V9, 0);         return BH1750Detected;       }     }     I2CWire.beginTransmission(BH1750I2CAddress);     ec = I2CWire.endTransmission(true);     if (ec == 0)     {       I2CWire.requestFrom(BH1750I2CAddress, 2);       BHMeasure.Lux = I2CWire.read();       BHMeasure.Lux <<= 8;                  // Verschieben der unteren 8 Bits in die höhreren 8 Bits der 16 Bit breiten Zahl       BHMeasure.Lux |= I2CWire.read();       BHMeasure.Lux = BHMeasure.Lux / 1.2;       BHMeasure.DataValid = true;       if (BHMeasure.Lux != BHMeasure.Old_Lux)       {         BHMeasure.Old_Lux = BHMeasure.Lux;         Update_Blynk_APP(8, true); //  Lichtstärkeanzeige in Lux aktualisieren
#ifdef DEBUG         Serial.print ("Lichtstärke in Lux :");         Serial.println (BHMeasure.Lux);
#endif       }     } else     {       BHMeasure.DataValid = false;       BHMeasure.SensorEnabled = false;       Blynk.setProperty(V9, "color", BLYNK_BLUE);     }   }   return true;
}

bool Run_DHTSensor (bool Init)   // Runtime Funktion für den DHT Temp und Luftfeuchtesensor
{   if ((millis() - DHT_ServiceCall_Handler >= DHT_Poll_MinInterval * 1000) | (Init))   {     DHT_ServiceCall_Handler = millis();     DHTMeasure.Humidity = dht.readHumidity();     DHTMeasure.Temperature = dht.readTemperature(false);   // Read temperature as Celsius (isFahrenheit = true)     if (Init)     {       if (isnan(DHTMeasure.Humidity) || isnan(DHTMeasure.Temperature))       {         Blynk.setProperty(V7, "label", "Deaktiviert");         Blynk.setProperty(V8, "label", "Deaktiviert");         Blynk.virtualWrite(V7, 0);         Blynk.virtualWrite(V8, -20);         Blynk.setProperty(V7, "color", BLYNK_BLACK);         Blynk.setProperty(V8, "color", BLYNK_BLACK);         DHTMeasure.DataValid  = false;         return false;       } else       {         Blynk.setProperty(V7, "color", BLYNK_WHITE);         Blynk.setProperty(V7, "label", SysConfig.SensorName[6]);         Blynk.setProperty(V8, "color", BLYNK_WHITE);         Blynk.setProperty(V8, "label", SysConfig.SensorName[7]);         DHTMeasure.DataValid  = true;         DHTMeasure.Old_Humidity = DHTMeasure.Humidity;         Update_Blynk_APP(6, true); //  Luftfeuchteanzeige         DHTMeasure.Old_Temperature = DHTMeasure.Temperature;         Update_Blynk_APP(7, true); //  Temperaturanzeige         return true;       }     }     if (isnan(DHTMeasure.Humidity) || isnan(DHTMeasure.Temperature))     {       Blynk.setProperty(V7, "color", BLYNK_BLUE);       Blynk.setProperty(V8, "color", BLYNK_BLUE);       DHTMeasure.DataValid  = false;       DHTMeasure.SensorEnabled = false;       return false;     }     DHTMeasure.DataValid  = true;     if (DHTMeasure.Humidity != DHTMeasure.Old_Humidity)     {       DHTMeasure.Old_Humidity = DHTMeasure.Humidity;       Update_Blynk_APP(6, true); //  Luftfeuchteanzeige     }     if (DHTMeasure.Temperature !=  DHTMeasure.Old_Temperature)     {       DHTMeasure.Old_Temperature = DHTMeasure.Temperature;       Update_Blynk_APP(7, true); //  Temperaturanzeige     }   }   return true;
}

bool SetLedConfig(byte Red, byte Green, byte Blue)
{   ledcWrite(PWMledChannelA, Red); // Rote LED   ledcWrite(PWMledChannelB, Blue); // Blaue LED   ledcWrite(PWMledChannelC, Green); // Gruene LED   return true;
}

int ReadMoistureSensor_Raw_Val(byte Sensor)
{   int ReturnValue, i;   long sum = 0;
#define NUM_READS 6   adc1_config_width(ADC_WIDTH_BIT_12);   //Range 0-4095   switch (Sensor)   {     case 0:       {         adc1_config_channel_atten(ADC1_CHANNEL_0, ADCAttenuation);         for (i = 0; i < NUM_READS; i++) { // Averaging algorithm           sum += adc1_get_raw( ADC1_CHANNEL_0 ); //Read analog         }         ReturnValue = sum / NUM_READS;         break;       }     case 1:       {         adc1_config_channel_atten(ADC1_CHANNEL_3, ADCAttenuation);         for (i = 0; i < NUM_READS; i++) { // Averaging algorithm           sum += adc1_get_raw( ADC1_CHANNEL_3 ); //Read analog         }         ReturnValue = sum / NUM_READS;         break;       }     case 2:       {         adc1_config_channel_atten(ADC1_CHANNEL_6, ADCAttenuation);         for (i = 0; i < NUM_READS; i++) { // Averaging algorithm           sum += adc1_get_raw( ADC1_CHANNEL_6 ); //Read analog         }         ReturnValue = sum / NUM_READS;         break;       }     case 3:       {         adc1_config_channel_atten(ADC1_CHANNEL_7, ADCAttenuation);         for (i = 0; i < NUM_READS; i++) { // Averaging algorithm           sum += adc1_get_raw( ADC1_CHANNEL_7 ); //Read analog         }         ReturnValue = sum / NUM_READS;         break;       }     case 4:       {         adc1_config_channel_atten(ADC1_CHANNEL_4, ADCAttenuation);         for (i = 0; i < NUM_READS; i++) { // Averaging algorithm           sum += adc1_get_raw( ADC1_CHANNEL_4 ); //Read analog         }         ReturnValue = sum / NUM_READS;         break;       }     default:       {         adc1_config_channel_atten(ADC1_CHANNEL_5, ADCAttenuation);         for (i = 0; i < NUM_READS; i++) { // Averaging algorithm           sum += adc1_get_raw( ADC1_CHANNEL_5 ); //Read analog         }         ReturnValue = sum / NUM_READS;         break;       }   }   return ReturnValue;
}


void Update_Local_Display()
{   byte red1 = 0;   byte yellow1 = 0;   byte green1 = 0;   for (byte i = 0; i < AttachedMoistureSensors; i++)   {     if (MMeasure.DataValid[i])     {       if ( MMeasure.Percent[i] > SysConfig.StatusBorderPercentValues[i][1])       {         green1++;       } else if ( MMeasure.Percent[i] > SysConfig.StatusBorderPercentValues[i][0])       {         yellow1++;       } else       {         red1++;       }     }   }   if (red1 > 0)   {     SetLedConfig(255, 0, 0);   }   else if (yellow1 > 0)   {     SetLedConfig(255, 255, 0);   }   else if (green1 > 0)   {     SetLedConfig(0, 255, 0);   }   else   {     SetLedConfig(0, 0, 255);   }
}

void Update_Blynk_APP(byte Sensor, bool Calibrated)
{   switch (Sensor)   {     case 0:       {         if ((MMeasure.DataValid[0]) & (Calibrated))         {           if ( MMeasure.Percent[0] > SysConfig.StatusBorderPercentValues[0][1])           {             Blynk.setProperty(V1, "color", BLYNK_GREEN);           } else if ( MMeasure.Percent[0] > SysConfig.StatusBorderPercentValues[0][0])           {             Blynk.setProperty(V1, "color", BLYNK_YELLOW);           } else           {             Blynk.setProperty(V1, "color", BLYNK_RED);           }           delay(100);           Blynk.virtualWrite(V1, MMeasure.Percent[0]);         } else         {           Blynk.setProperty(V1, "color", BLYNK_BLUE);         }         break;       }     case 1:       {         if ((MMeasure.DataValid[1]) & (Calibrated))         {           if ( MMeasure.Percent[1] > SysConfig.StatusBorderPercentValues[1][1])           {             Blynk.setProperty(V2, "color", BLYNK_GREEN);           } else if ( MMeasure.Percent[1] > SysConfig.StatusBorderPercentValues[1][0])           {             Blynk.setProperty(V2, "color", BLYNK_YELLOW);           } else           {             Blynk.setProperty(V2, "color", BLYNK_RED);           }           delay(100);           Blynk.virtualWrite(V2, MMeasure.Percent[1]);         } else         {           Blynk.setProperty(V3, "color", BLYNK_BLUE);         }         break;       }     case 2:       {         if ((MMeasure.DataValid[2]) & (Calibrated))         {           if ( MMeasure.Percent[2] > SysConfig.StatusBorderPercentValues[2][1])           {             Blynk.setProperty(V3, "color", BLYNK_GREEN);           } else if ( MMeasure.Percent[2] > SysConfig.StatusBorderPercentValues[2][0])           {             Blynk.setProperty(V3, "color", BLYNK_YELLOW);           } else           {             Blynk.setProperty(V3, "color", BLYNK_RED);           }           delay(100);           Blynk.virtualWrite(V3, MMeasure.Percent[2]);         } else         {           Blynk.setProperty(V3, "color", BLYNK_BLUE);         }         break;       }     case 3:       {         if ((MMeasure.DataValid[3]) & (Calibrated))         {           if ( MMeasure.Percent[3] > SysConfig.StatusBorderPercentValues[3][1])           {             Blynk.setProperty(V4, "color", BLYNK_GREEN);           } else if ( MMeasure.Percent[3] > SysConfig.StatusBorderPercentValues[3][0])           {             Blynk.setProperty(V4, "color", BLYNK_YELLOW);           } else           {             Blynk.setProperty(V4, "color", BLYNK_RED);           }           delay(100);           Blynk.virtualWrite(V4, MMeasure.Percent[3]);         } else         {           Blynk.setProperty(V4, "color", BLYNK_BLUE);         }         break;       }     case 4:       {         if ((MMeasure.DataValid[4]) & (Calibrated))         {           if ( MMeasure.Percent[4] > SysConfig.StatusBorderPercentValues[4][1])           {             Blynk.setProperty(V5, "color", BLYNK_GREEN);           } else if ( MMeasure.Percent[4] > SysConfig.StatusBorderPercentValues[4][0])           {             Blynk.setProperty(V5, "color", BLYNK_YELLOW);           } else           {             Blynk.setProperty(V5, "color", BLYNK_RED);           }           delay(100);           Blynk.virtualWrite(V5, MMeasure.Percent[4]);         } else         {           Blynk.setProperty(V5, "color", BLYNK_BLUE);         }         break;       }     case 5:       {         if ((MMeasure.DataValid[5]) & (Calibrated))         {           if ( MMeasure.Percent[5] > SysConfig.StatusBorderPercentValues[5][1])           {             Blynk.setProperty(V6, "color", BLYNK_GREEN);           } else if ( MMeasure.Percent[5] > SysConfig.StatusBorderPercentValues[5][0])           {             Blynk.setProperty(V6, "color", BLYNK_YELLOW);           } else           {             Blynk.setProperty(V6, "color", BLYNK_RED);           }           delay(100);           Blynk.virtualWrite(V6, MMeasure.Percent[5]);         } else         {           Blynk.setProperty(V6, "color", BLYNK_BLUE);         }         break;       }     case 6:       {         if (DHTMeasure.DataValid)         {           if (DHTMeasure.Humidity < 40)  //  https://www.pflanzenfreunde.com/luftfeuchtigkeit.htm und https://www.brune.info/magazin/richtige-luftfeuchtigkeit-fuer-pflanzen/           {             Blynk.setProperty(V7, "color", BLYNK_RED);           } else if (DHTMeasure.Humidity < 60)           {             Blynk.setProperty(V7, "color", BLYNK_YELLOW);           } else  if (DHTMeasure.Humidity < 85)           {             Blynk.setProperty(V7, "color", BLYNK_GREEN);           } else           {             Blynk.setProperty(V7, "color", BLYNK_YELLOW);           }           Blynk.virtualWrite(V7, DHTMeasure.Humidity);         }         break;       }     case 7:       {         if (DHTMeasure.DataValid)         {           if (DHTMeasure.Temperature > 43)   // https://www.spektrum.de/lexikon/biologie-kompakt/hitzeresistenz/5543           {             Blynk.setProperty(V8, "color", BLYNK_RED);           } else if (DHTMeasure.Temperature < 11) // https://de.wikipedia.org/wiki/K%C3%A4ltestress_bei_Pflanzen           {             Blynk.setProperty(V8, "color", BLYNK_RED);           } else           {             Blynk.setProperty(V8, "color", BLYNK_WHITE);           }           Blynk.virtualWrite(V8, DHTMeasure.Temperature);         }         break;       }     case 8:       {         if (BHMeasure.DataValid)         {           if (BHMeasure.Lux < 500)   // https://www.zimmerpflanzenlexikon.info/artikel/lichtbedarf-von-pflanzen           {             Blynk.setProperty(V9, "color", BLYNK_RED);           } else if (BHMeasure.Lux < 1000)           {             Blynk.setProperty(V9, "color", BLYNK_GREEN);           } else if (BHMeasure.Lux < 1500)           {             Blynk.setProperty(V9, "color", BLYNK_WHITE);           } else           {             Blynk.setProperty(V9, "color", BLYNK_YELLOW);           }           Blynk.virtualWrite(V9, BHMeasure.Lux);         }         break;       }   } // End Switch
}

void Get_Moisture_DatainPercent()
{   byte CalibDataOffset = 0;   for (byte i = 0; i < AttachedMoistureSensors; i++)   {     CalibDataOffset =  i * 2;     int RawMoistureValue = ReadMoistureSensor_Raw_Val(i);     if ((SysConfig.Data[CalibDataOffset] == 0) || (SysConfig.Data[CalibDataOffset + 1] == 0)) // MinADC Value maxADC ADC Value     {       MMeasure.Percent[i] = RawMoistureValue;       MMeasure.DataValid[i] = false;     } else     {       RawMoistureValue = SysConfig.Data[CalibDataOffset + 1] - RawMoistureValue;       RawMoistureValue = SysConfig.Data[CalibDataOffset] + RawMoistureValue;       MMeasure.Percent[i] = map(RawMoistureValue, SysConfig.Data[CalibDataOffset], SysConfig.Data[CalibDataOffset + 1], 0, 100);       if ((MMeasure.Percent[i] > 100 ) | (MMeasure.Percent[i] < 0 ))       {         MMeasure.Percent[i] = RawMoistureValue;         MMeasure.DataValid[i] = false;       } else  {         MMeasure.DataValid[i] = True;       }     }   }   Return ;
}

Void Run_MoistureSensors (Bool Init)   Main function for operating soil moisture sensors
{   Byte MinSensValue = 100;   If ((millis() - Moisure_ServiceCall_Handler >= MoisureSens_Poll_MinInterval * 1000) | (Init))   {     Moisure_ServiceCall_Handler = millis();     If (Init)     {       for (Int  = 0;  < MaxSensors; ++)       {         Int MSensorRawValue = ReadMoistureSensor_Raw_Val();         If ( MSensorRawValue > MinSensorValue)         {           AttachedMoistureSensors++;         } else {           Break;         }       }       If (AttachedMoistureSensors < 1)       {
#ifdef Debug         Serial.println(Q("No soil moisture sensors detected. system paused."));
#endif         SetLedConfig(255, 0, 255);         digitalWrite(LED_Rot, High); System Suspended Led Display: Purple         digitalWrite(LED_Blau, High);         digitalWrite(LED_Gruen, Low);         Delay(1200000);         esp_deep_sleep_start();         while (1) {}       }       for (Int  = 0;  < AttachedMoistureSensors; ++)       {         If ( == 0) {           Blynk.Setproperty(V1, "label", Sysconfig.SensorName[0]);         }         If ( == 1) {           Blynk.Setproperty(V2, "label", Sysconfig.SensorName[1]);         }         If ( == 2) {           Blynk.Setproperty(V3, "label", Sysconfig.SensorName[2]);         }         If ( == 3) {           Blynk.Setproperty(V4, "label", Sysconfig.SensorName[3]);         }         If ( == 4) {           Blynk.Setproperty(V5, "label", Sysconfig.SensorName[4]);         }         If ( == 5) {           Blynk.Setproperty(V6, "label", Sysconfig.SensorName[5]);         }       }       for (Int  = AttachedMoistureSensors;  < MaxSensors; ++)       {         If ( == 0) {           Blynk.Setproperty(V1, "label", "Disabled");           Blynk.Setproperty(V1, "color", BLYNK_BLACK);         }         If ( == 1) {           Blynk.Setproperty(V2, "label", "Disabled");           Blynk.Setproperty(V2, "color", BLYNK_BLACK);         }         If ( == 2) {           Blynk.Setproperty(V3, "label", "Disabled");           Blynk.Setproperty(V3, "color", BLYNK_BLACK);         }         If ( == 3) {           Blynk.Setproperty(V4, "label", "Disabled");           Blynk.Setproperty(V4, "color", BLYNK_BLACK);         }         If ( == 4) {           Blynk.Setproperty(V5, "label", "Disabled");           Blynk.Setproperty(V5, "color", BLYNK_BLACK);         }         If ( == 5) {           Blynk.Setproperty(V6, "label", "Disabled");           Blynk.Setproperty(V6, "color", BLYNK_BLACK);         }       }     }     Get_Moisture_DatainPercent();     for (Int  = 0;  < AttachedMoistureSensors; ++)     {       If (MMeasure.DataValid[])       {         If (MMeasure.Percent[] != MMeasure.Old_Percent[])         {           MMeasure.Old_Percent[] = MMeasure.Percent[];           If (MMeasure.Percent[] < MinSensValue ) {             MinSensValue = MMeasure.Percent[];           };
#ifdef Debug           Serial.Print(Q("Moisture Value Sensor"));           Serial.Print();           Serial.Print(Q(" in percent :"));           Serial.Print(MMeasure.Percent[]);           Serial.println(Q(" %"));
#endif           Update_Blynk_APP(, Sens_Calib);    Update mobile phone values         }       } else       {         Update_Blynk_APP(, Sens_NOTCalib);    Update mobile phone values         Serial.Print(Q("Sensor"));         Serial.Print();         Serial.Print(Q(" not calibrated. Please calibrate. Raw data value:"));         Serial.println(MMeasure.Percent[]);       }     }     Update_Local_Display();           Update Local Plant Guardian Display (Led)   }

}

Main Loop
Void Loop()
{   Run_MoistureSensors(Runtime);   If (DHTMeasure.SensorEnabled) {     Run_DHTSensor(Runtime);   }   If (BHMeasure.SensorEnabled)  {     Run_BH1750Sensor(Runtime);   }   Blynk.Run();
}

 

 

In the code, before uploading it for the first time, the Must however, the following lines of code can still be adapted to their own needs:

int Data[MaxSensors*2] = .0,0,0,0,0,0,0,0,0,0,0,0,0,0,"

 

Values please according to the description in Part 1 the series.

#define MoisureSens_Poll_MinInterval 3600

 

Interval between two Soil moisture measurements in seconds. The value influences how often the mobile phone data of the soil moisture sensors are updated, and thus also what data volume is generated for the transmission per time. The higher, the greater the interval. Please set to a value that matches your own data volume or budget.

[Example: 3600 s =1 hour]

#define DHT_Poll_MinInterval 2700

 

Interval between two Temperature/humidity measurements in seconds. The value influences how often the mobile phone data of the temperature/humidity measurements are updated, and thus also what data volume is generated for the transmission per time. The higher, the greater the interval. Please set to a value that matches your own data volume or budget.

[Example: 2700 s = 45 minutes data transfer interval]

#define BH_Poll_MinInterval 1800

 

Interval between two Illuminance measurements in seconds. The value influences how often the mobile phone data of the Illuminance measurements and thus also the amount of data required for the transfer per time. The higher, the greater the interval. Please set to a value that matches your own data volume or budget. 

[Example: 1800 s = 30 minutes data transfer interval]

char auth[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

char ssid[] = "Deine_WLAN_SSID";

char pass[] = "Your _WLAN _Passwort!";

 

Values please according to the description in Part 2 the series.

It Can the following parameters/code lines to be adapted to the respective needs:

string sensorname[maxsensors] = "plant 1", "plant 2", "plant 3", "plant 4", "plant 5", "plant 6";

 

Sensor name that appears as a heading in the APP.

byte StatusBorderPercentValues[MaxSensors*2][2]= . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

 

Two-dimensional array for percentage limits (traffic lights) per humidity sensor (1 -6). Affects the "traffic light" display and the display of values in the APP. First who (10) specifies the transition limit between status "red" and status "yellow". Second value indicates the transition boundary between status yellow and status green. Example: from 51% soil moisture "green" from 9% soil moisture "red".

#define DEBUG

 

If definition "DEBUG" exists, runtime messages are output on the serial interface. For productive use, the definition "DEBUG" could be deleted to avoid unnecessary resource use.

 

Now, as in the other parts, let's get back to the customization of our mobile app. In this we now have to create a new "H-Level" element and set it up as described below.

We add the "Level H" 1 time to display the current soil moisture

 Part5 App1

And configure the new item as follows:

 Part5 App2
  • Colour: Red
  • Input Pin: V9 (virtual)
  • Minimum value: 0
  • Maximum value: 1400 (or 1500)

The most important adjustment here is again the input variable. This must be set to "V9" for fhere

We define "push" again as Reading Rate

Part5 App3

The end result, when the project is activated, should now look something like this:

Part5 App4

Congratulations! You have successfully configured your plant guard.

Please note the following:

We now have 9 "H-Level" display elements on our DashBoard. Each "H-level" element costs us 200 energy. This makes 9*200 = 1800 energy points. Adding additional items that are more expensive than 200 energy points is only possible with paid IN-APP purchases from this point on. The other examples and screenshots with diagrams are therefore subject to a charge! For the general function of the plant monitor, these are by no means necessary!

Also, it is possible to Not guaranteed that in the future the APP or the purchased energy points will still be used or will be used in future series. Therefore, please decide on your own responsibility whether you want to invest money in this.

Even if the app is in any case also WITHOUT further investment fully functional and an already a real eye-catcher, I do not want to withhold from you the already possible paid extensions. This does not require a change in the ESP code. All screenshots are configurable via the Blynk APP alone, regardless of the firmware.

 

Feasibility example: Temperature curve (together in a chart) :

Part5 - App5 - Example

 

Part5 App6

 

Feasibility example: Temperature curve (separated into two charts) 

Part5 App7 - Example 2

Feasibility example Illuminance progression

Part5 App8 Lux

For more information about the Blynk APP and its use in controllers, please visit:

 

 

Please note that this project is not suitable for hydroculture plants or airroots. Plants have different requirements for your environment. Since our plant monitor does not know the individual requirements of the plants, the interpretation of the plant guardian's values is exclusively in the hands of the user.

 

The plant monitor is no substitute for responsible care of the plants!

 

You can also use this project as well as other projects of mine on my Git Hub PageTo find.

 

Have fun rebuilding.

 

Esp-32Projekte für fortgeschritteneSensorsSmart home

5 comments

Siegfried

Siegfried

Hallo Tobias,
ich habe selten so gute Beiträge gelesen wie deine “Pflanzenwächter 1-5”. Da ist wirklich alles dabei und für richtige Experten “volles Programm”!
Meine vorsichtige Frage: Hast Du auch eine vereinfachte Version, z. B. ohne Handy App und WLAN. Dafür mit Display für die Anzeige vor Ort (2,8" TFT LCD Shield wie RM68090 oder ähnlich)? Das könnte eine interessante Ergänzung zu dem perfekten Erfassungsmodul sein!?
Freundliche Grüße aus München,
Siegfried

Helmut Lieven

Helmut Lieven

Hallo und danke für das SUPER Projekt. Ich suche schon lange etwas um die Bodenfeuchtigkeit zu messen.
Das Projekt funktioniert mit allen Anzeigen auf dem Handy. Nur ein Problem bereit mir die Feuchtigkeitsanzeige, habe 2 Sensoren angeschlossen. Wenn ich die Werte mit Code Teil 1 auswerte und in Code Teil 5 übertrage kommt die Anzeige BITTE Kalibrieren. Bei geänderten Werten funktioniert es aber die Anzeige % weicht von der ersten Messung ab. Wie kann man am besten Daten ändern?
Gruß Helmut

Dieter

Dieter

Hallo, ich bin noch Anfänger, und finde die Anleitungen sehr gut. Frage könnte man noch ein Tutorial machen in den man eine Pumpe, bzw Lüfter bei bestimmten Werten schalten kann?
Wäre echt toll.
Danke.

Tobias

Tobias

Hallo Knut,
versuche mal einen Sensor in trockene Erde und den anderen in feuchte Erde zu stecken. Das sollte in jedem Fall unterschiedliche Ergebnisse liefern. Des weiteren ist eine Kalibrierung der Sensoren wichtig. Ist dies durchgeführt worden ?

Knut Dorendorff

Knut Dorendorff

Hallo,
ich habe für 6 Bodenfeuchtsensoren alles eingerichtet und auch dazu den Code installiert. Ich habe im Moment nur 2 Sensoren angeschlossen. Diese arbeiten auch, zeigen aber für beide Sensoren den gleichen Wert an. Habe mehrmals überprüft, ob irgendwo überbrücken sind, aber nein. Ist das ein typischen verhalten bei nur angeschlossenen 2 Sensoren oder muss ich dann im Code etwas verändern?
Danke
Knut

Leave a comment

All comments are moderated before being published

Recommended blog posts

  1. Install ESP32 now from the board manager
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - ESP programming via WLAN