Hi All,
I’m working on a self-automated plant watering system. About to upload the code but I seem to have run into some problems! I have an error saying ‘D1 was not declared in this scope’. The details are below. Thanks in advance!
• Hardware model + communication type: ESP8266 Node MCU v3 WiFi
• Smartphone OS (Android)
• Code in chunks GitHub - Emilostuff/PlantKeeper: Automated Plant Watering System
// Libraries
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include<ADS1115_WE.h>
#include<Wire.h>
#include <TimeLib.h>
#include <WidgetRTC.h>
// Networking
char auth[] = "";
char ssid[] = "";
char pass[] = "";
// Blynk
BlynkTimer timer;
BlynkTimer timer1;
WidgetRTC rtc;
// System state
int plantSelect = 0; // plant 1 selected on default (index 0)
bool systemOn = 0;
bool systFlag = false;
long ontime;
long lastWater[4] = {0, 0, 0, 0}; // 0 means not set
bool pumpOn[4] = {false, false, false, false};
// Settings (can be adjusted from app)
int modes[4] = {0, 0, 0, 0};
int amount[4] = {0, 0, 0, 0};
int interval[4] = {0, 0, 0, 0};
int thresh[4] = {0, 0, 0, 0};
int minInterval[4] = {0, 0, 0, 0};
// sensor/ADC
ADS1115_WE adc(0x48);
float tf = 0.1; // trust factor for smoothing filter
float sensor[4] = {100, 100, 100, 100}; // Set highest start value to avoid unwanted triggers
float sensorDry[4] = {2760, 2680, 2780, 2760}; // Reading from when fully emerged in water
float sensorWet[4] = {1460, 1210, 1510, 1500}; // Reading from when in 'dry' air
#define water
// BLYNK ///////////////////////////////////////
BLYNK_CONNECTED()
{
// Synchronize unix-time on connection
rtc.begin();
}
// IN-APP EVENT CALLS ///////////////////////
// for when the user presses any button in the app
// System on-off button event
BLYNK_WRITE(V7)
{
// change system state
systemOn = param.asInt();
if (systemOn) {
if (systFlag) {
// system has just been turned on!
systFlag = false;
// set lastWater to now:
ontime = now();
Blynk.virtualWrite(V34, ontime, ontime, ontime, ontime);
Blynk.syncVirtual(V34);
}
// system was turned on when connected -> do nothing
} else {
// system is off
systFlag = true;
}
}
// Manual water button event
BLYNK_WRITE(V8)
{
// if button was pressed and plant is eligible for water
if (param.asInt() == 1 and now() - lastWater[plantSelect] > 5 and systemOn) {
// execute water routine
water(plantSelect);
} else {
// reset water button to unpressed state
Blynk.virtualWrite(V8, 0);
}
}
// Reload button event
BLYNK_WRITE(V2)
{
// Reload requested -> update display values in app
if (param.asInt() == 1); {
Blynk.virtualWrite(V1, modes[plantSelect]);
Blynk.virtualWrite(V3, amount[plantSelect]);
Blynk.virtualWrite(V4, interval[plantSelect]);
Blynk.virtualWrite(V5, thresh[plantSelect]);
Blynk.virtualWrite(V9, minInterval[plantSelect]);
}
}
// Plant Select event
BLYNK_WRITE(V0)
{
// Plant selected for edit -> store updated value
plantSelect = param.asInt() - 1;
// update displays
Blynk.virtualWrite(V1, modes[plantSelect]);
Blynk.virtualWrite(V3, amount[plantSelect]);
Blynk.virtualWrite(V4, interval[plantSelect]);
Blynk.virtualWrite(V5, thresh[plantSelect]);
Blynk.virtualWrite(V9, minInterval[plantSelect]);
}
// Mode select event
BLYNK_WRITE(V1)
{
// Store updated value depending on selected plant
modes[plantSelect] = param.asInt();
// save updated value on server
Blynk.virtualWrite(V30, modes[0], modes[1], modes[2], modes[3]);
}
// Amount change event
BLYNK_WRITE(V3)
{
// Store updated value depending on selected plant
amount[plantSelect] = param.asInt();
// save to server
Blynk.virtualWrite(V31, amount[0], amount[1], amount[2], amount[3]);
}
// Interval change event
BLYNK_WRITE(V4)
{
// Store updated value depending on selected plant
interval[plantSelect] = param.asInt();
// save to server
Blynk.virtualWrite(V32, interval[0], interval[1], interval[2], interval[3]);
}
// Threshold change event
BLYNK_WRITE(V5)
{
// Store updated value depending on selected plant
thresh[plantSelect] = param.asInt();
// save to server
Blynk.virtualWrite(V33, thresh[0], thresh[1], thresh[2], thresh[3]);
}
// Minimum interval change event
BLYNK_WRITE(V9)
{
// Store updated value depending on selected plant
minInterval[plantSelect] = param.asInt();
// save to server
Blynk.virtualWrite(V35, minInterval[0], minInterval[1], minInterval[2], minInterval[3]);
}
// GET CALLS //////////////////////////////
// used when 'sync' is called at startup
BLYNK_WRITE(V30)
{
// Get values from server:
for (int i = 0; i < 4; i++) {
modes[i] = param[i].asInt();
}
}
BLYNK_WRITE(V31)
{
// Get values from server:
for (int i = 0; i < 4; i++) {
amount[i] = param[i].asInt();
}
}
BLYNK_WRITE(V32)
{
// Get values from server:
for (int i = 0; i < 4; i++) {
interval[i] = param[i].asInt();
}
}
BLYNK_WRITE(V33)
{
// Get values from server:
for (int i = 0; i < 4; i++) {
thresh[i] = param[i].asInt();
}
}
BLYNK_WRITE(V35)
{
// Get values from server:
for (int i = 0; i < 4; i++) {
minInterval[i] = param[i].asInt();
}
}
// Last Water - retreive values from server
BLYNK_WRITE(V34)
{
// Get values from server:
for (int i = 0; i < 4; i++) {
lastWater[i] = param[i].asInt();
}
}
// STATUS DISPLAY FUNCTIONS /////////////////////
// when app request current plant status
BLYNK_READ(V11)
{
Blynk.virtualWrite(V11, getStatus(0));
}
BLYNK_READ(V12)
{
Blynk.virtualWrite(V12, getStatus(1));
}
BLYNK_READ(V13)
{
Blynk.virtualWrite(V13, getStatus(2));
}
BLYNK_READ(V14)
{
Blynk.virtualWrite(V14, getStatus(3));
}
void setup()
{
// Serial
Serial.begin(9600);
// Blynk
Blynk.begin(auth, ssid, pass);
// fetch stored data from server
Blynk.syncVirtual(V0);
Blynk.syncVirtual(V7);
Blynk.syncVirtual(V30);
Blynk.syncVirtual(V31);
Blynk.syncVirtual(V32);
Blynk.syncVirtual(V33);
Blynk.syncVirtual(V34);
Blynk.syncVirtual(V35);
// Sensors
Wire.begin(D1, D2); // SDA, SCL
if (!adc.init()) {
Serial.println("ADS1115 not connected!");
}
adc.setVoltageRange_mV(ADS1115_RANGE_6144);
// timers
timer.setInterval(30000L, control); // for control loop, run every 30 secs
timer1.setInterval(5000L, readSensors); // for sensor read loop, run every 5 sec
// RTC
setSyncInterval(10 * 60); // Sync interval in seconds (10 minutes)
// pin assignments
pinMode(D7, OUTPUT);
pinMode(D4, OUTPUT);
pinMode(D5, OUTPUT);
pinMode(D6, OUTPUT);
// set pumps to OFF (active-low)
digitalWrite(D7, HIGH);
digitalWrite(D4, HIGH);
digitalWrite(D5, HIGH);
digitalWrite(D6, HIGH);
// Set a reasonable start value for sensors (a little above the triggering threshold)
for (int i = 0; i < 4; i++) {
sensor[i] = thresh[i] + 10;
}
Serial.println("Setup Complete");
}
void loop()
{
// main blynk loop
Blynk.run();
// timers
timer.run();
timer1.run();
}
void control ()
{
// check if system is on
if (systemOn) {
// check if it's time to water
for (int i = 0; i < 4; i++) {
if (plantCheck(i)) {
// yes -> execute water routine
water(i);
}
}
}
}
bool plantCheck (int plant)
{
// find elapsed time since last water
long elapsedTime = now() - lastWater[plant];
// check if in auto mode
if (modes[plant] == 2 and elapsedTime > minInterval[plant] * 60 * 60 and sensor[plant] < thresh[plant]) {
// minimun interval exceed and moisturelevels too low -> time to water!
return true;
}
// check if in timer mode
else if (modes[plant] == 3 and elapsedTime > interval[plant] * 60 * 60 * 24) {
// time interval has been exceeded -> time to water:
return true;
}
// no hit, no water
return false;
}
void water(int plant)
{
Serial.print("Watering plant "); Serial.println(plant + 1);
// set flag (used for status update)
pumpOn[plant] = true;
// push-update status in app and then turn pump on, and
switch (plant) {
case 0:
Blynk.virtualWrite(V11, getStatus(plant));
digitalWrite(D6, LOW);
break;
case 1:
Blynk.virtualWrite(V12, getStatus(plant));
digitalWrite(D5, LOW);
break;
case 2:
Blynk.virtualWrite(V13, getStatus(plant));
digitalWrite(D4, LOW);
break;
case 3:
Blynk.virtualWrite(V14, getStatus(plant));
digitalWrite(D7, LOW);
break;
}
// delay loop
long startTime = millis();
while (millis() - startTime < waterdur(plant)) {
// keep everything running in the meantime (except the control loop)
Blynk.run();
timer1.run();
}
// remove flag
pumpOn[plant] = false;
// turn pump off and push new status
switch (plant) {
case 0:
digitalWrite(D6, HIGH);
Blynk.virtualWrite(V11, getStatus(plant));
break;
case 1:
digitalWrite(D5, HIGH);
Blynk.virtualWrite(V12, getStatus(plant));
break;
case 2:
digitalWrite(D4, HIGH);
Blynk.virtualWrite(V13, getStatus(plant));
break;
case 3:
digitalWrite(D7, HIGH);
Blynk.virtualWrite(V14, getStatus(plant));
break;
}
// Reset water button (if used)
Blynk.virtualWrite(V8, 0);
// update lastwater to server
lastWater[plant] = now();
Blynk.virtualWrite(V34, lastWater[0], lastWater[1], lastWater[2], lastWater[3]);
}
int waterdur (int plant)
{
// convert ml to ms for controlling pump on-time, different values depending on the chosen pump
switch (plant) {
case 0:
return amount[plant] * 57 + 350; // set experimentally
case 1:
return amount[plant] * 50 + 320; // set experimentally
case 2:
return amount[plant] * 59 + 350; // set experimentally
case 3:
return amount[plant] * 47 + 340; // set experimentallyl
}
}
void readSensors()
{
float reading[4];
// read raw values
reading[0] = readChannel(ADS1115_COMP_0_GND);
reading[1] = readChannel(ADS1115_COMP_1_GND);
reading[2] = readChannel(ADS1115_COMP_2_GND);
reading[3] = readChannel(ADS1115_COMP_3_GND);
// convert to percentage, filter and constrain (if we got a rouge reading)
for (int i = 0; i<4; i++) {
reading[i] = map(reading[i], sensorDry[i], sensorWet[i], 0, 100);
sensor[i] = tf * reading[i] + (1 - tf) * sensor[i];
sensor[i] = constrain(sensor[i], 0, 100);
}
// Write to server
Blynk.virtualWrite(V21, sensor[0]);
Blynk.virtualWrite(V22, sensor[1]);
Blynk.virtualWrite(V23, sensor[2]);
Blynk.virtualWrite(V24, sensor[3]);
}
float readChannel(ADS1115_MUX channel)
{
float voltage = 0.0;
adc.setCompareChannels(channel);
adc.startSingleMeasurement();
while (adc.isBusy()) {
Blynk.run(); // nope!
}
voltage = adc.getResult_mV();
return voltage;
}
String getStatus (int plant)
{
// string variables
String mode = "";
String status = "";
if (pumpOn[plant] == true) {
// pump is on
return "Watering ...";
} else if (systemOn) {
// determine mode
switch (modes[plant]) {
case 0:
// not set up yet
return "Not set up yet!";
break;
case 1:
mode = "Manual";
break;
case 2:
mode = "Auto";
break;
case 3:
mode = "Timer";
break;
}
// Determine time of last water
if (lastWater[plant] == ontime) {
status = "not watered.";
} else {
// calculate difference
long diff = now() - lastWater[plant];
// onvert between minutes, hours, days and too much
if (diff < 60) {
status = "just now.";
} else if (diff < 2 * 60) {
status = "1 min ago.";
} else if (diff < 60 * 60) {
status = String(diff / 60) + " mins ago.";
} else if (diff < 60 * 60 * 2) {
status = "1 hour ago";
} else if (diff < 60 * 60 * 24) {
status = String(diff / (60 * 60)) + " hours ago.";
} else if (diff < 60 * 60 * 24 * 2) {
status = "1 day ago";
} else if (diff < 60 * 60 * 24 * 31) {
status = String(diff / (60 * 60 * 24)) + " days ago.";
} else {
status = "+1 month ago.";
}
}
// return concatenated results
return mode + ", " + status;
} else {
// system is off
return "Off";
}
}