Not sure this line at end of setup is super correct
Agh, yes. I wasn't sure. I want to put all solenoids HIGH at that point before void() loop
Please advise correct way. Maybe its not even necessary at all.
Mark
I'm away from real computers where it would be easy to be sure that line of code is the last one in the loop. If so, it is superfluous, as the hole idea of the for loop is that it knows when it is finished. break is very much useful and used, but is "unnatural" in the sense that it woukd make a reader wonder about it, in any context, but here for sure.
I'm sure it is not correct. Nice try, though, and there are probably languages that let you do that. Here you need to loop over the pins to be written high, or use two lines of code.
digitalWrite(myPlants[0].SOL_PIN, HIGH);
digitalWrite(myPlants[1].SOL_PIN, HIGH);
If the intent was to write all SOL_PINs HIGH,
for (ii = 0; ii < numPlants; ii++)
digitalWrite(myPlants[ii].SOL_PIN, HIGH);
FWIW, the code line you doubted is syntactically correct, it just won't do what you clearly expected it to… welcome to C/C++, it allows you to make non-mistakes.
a7
Yes insert it in a single loop thet sets direction and state
for (byte ii = 0; ii < numPlants; ii++) {
pinMode(myPlants[ii].SOL_PIN, OUTPUT);
digitalWrite(myPlants[ii].SOL_PIN, TURNOFF);
}
thats quite obvious now you mention it. Thank you
code in post 119 seems to do what i need it too. I need to play with it a bit more to understand it.
1 thing, if i increase the maxruntime1 to 60000 (1 minute) it just gets stuck and doesnt change to second plant
Mark
Yes, you have to define maxruntime1 and 2 as long
Agh yes ok. I get it. Thanks
Small side note. Is there a recommended mosfet to use?
Each will run 5v solenoid of around 1a
Mark
Everyone on the forum is using irlz44n ( to220, 47A ) a little bit overkill... but you won't be able to destroy it ( unless you use the hammer ; - )
thank you. readily available here which is good
Ive now transfered the code over to esp32 board. I cant say i understand fully the sleep mode sand wake up bits but with a bit of copy and paste from arduino example it seems to be working.
for your review.....
#include "driver/rtc_io.h"
unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
const int wakeUpPin = 18;
struct plantStation {
const int SOIL_PIN;
const int SOL_PIN;
const int thresholdon;
const int thresholdint;
const int thresholdoff;
const int mapdry;
const int mapwet;
const unsigned long MaxRunTime1;
const unsigned long MaxRunTime2;
};
plantStation myPlants[2] = {
{ 2, 26, 50, 70, 80, 770, 397, 10000, 10000 },
{ 15, 27, 50, 70, 80, 770, 397, 10000, 10000 }
};
byte numPlants = sizeof(myPlants) / sizeof(*myPlants);
#define TURNON LOW
#define TURNOFF HIGH
int tickMs25 = 0; // flag
int tickMs100 = 0; // flag
int tickMs1000 = 0; // flag
unsigned long timeMs25Prev = 0;
unsigned long timeMs100Prev = 0;
unsigned long timeMs1000Prev = 0;
enum State {
SS_INIT,
SS_WATERING1,
SS_ENDWATERING1,
SS_DELAY,
SS_WATERING2,
SS_ENDWATERING2,
SS_SLEEP,
SS_LAST
};
// used only in debug print ( when changing state )
String stateStrSoil[SS_LAST] = {"Init","Watering1","End Watering1","Delay","Watering2","End Watering2","Sleep"};
State stateSoil = SS_INIT; // fsm
unsigned long stateTimeSoil = 0; // fsm
#define INIT_DELAY 1000 // delay at startup ( in milliseconds )
#define DELAY_DELAY 20000 // delay between two cycles ( in milliseconds )
#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO) // 2 ^ GPIO_NUMBER in hex
#define USE_EXT0_WAKEUP 1 // 1 = EXT0 wakeup, 0 = EXT1 wakeup
#define WAKEUP_GPIO GPIO_NUM_33 // Only RTC IO are allowed - ESP32 Pin example
#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex
RTC_DATA_ATTR int bootCount = 0;
int soilIndex = 0;
// returns milliseconds elapsed from beginning of current state
unsigned long getStateTimeSoil()
{
return millis() - stateTimeSoil;
}
// call this function when you have to change state
void changeStateSoil(State nextState)
{
Serial.print("State: "); Serial.print(stateStrSoil[stateSoil]);
Serial.print(" duration: "); Serial.println(getStateTimeSoil()/1000.0);
Serial.print("State change from "); Serial.print(stateStrSoil[stateSoil]);
Serial.print(" to "); Serial.print(stateStrSoil[nextState]);
Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// enable this switch if you have to include any state initialization routine
/*switch (stateSoil)
{
case SS_INIT: ctrlInitEntrySoil(); break;
case SS_WATERING1: ctrlWatering1EntryeSoil(); break;
case SS_DELAY: ctrlDelayEntrySoil(); break;
case SS_WATERING2: ctrlWatering2EntrySoil(); break;
}*/
stateTimeSoil = millis();
stateSoil = nextState;
}
void ctrlInitStateSoil()
{
if (getStateTimeSoil() > INIT_DELAY)
{
soilIndex = 0;
changeStateSoil(SS_WATERING1);
}
}
void ctrlDelayStateSoil()
{
if (getStateTimeSoil() > DELAY_DELAY)
{
soilIndex = 0;
changeStateSoil(SS_WATERING2);
}
}
void ctrlSleepStateSoil()
{
Serial.println ("SLEEEEEEEEEEEEEEEEEEEEEEEEEEP");
esp_deep_sleep_start();
// restart cycle
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
soilIndex = 0;
changeStateSoil(SS_WATERING1);
}
void ctrlWatering1StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
if (Soil_Moisture <= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON);
changeStateSoil(SS_ENDWATERING1);
}
else
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
//while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}
void ctrlEndWatering1StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// check soil moisture
if (Soil_Moisture >= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
// check max time
if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime1)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
}
void ctrlWatering2StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
if (Soil_Moisture <= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON);
changeStateSoil(SS_ENDWATERING2);
}
else
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
//while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}
void ctrlEndWatering2StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// check soil moisture
if (Soil_Moisture >= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
// check max time
if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime2)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
}
// finite state machine main loop
void ctrlSoil()
{
switch (stateSoil)
{
case SS_INIT: ctrlInitStateSoil(); break;
case SS_WATERING1: ctrlWatering1StateSoil(); break;
case SS_ENDWATERING1: ctrlEndWatering1StateSoil(); break;
case SS_DELAY: ctrlDelayStateSoil(); break;
case SS_WATERING2: ctrlWatering2StateSoil(); break;
case SS_ENDWATERING2: ctrlEndWatering2StateSoil(); break;
case SS_SLEEP: ctrlSleepStateSoil(); break;
}
}
// sets periodic flags
// better using a sysTick interrupt if available
void ctrlTime()
{
currentMillis = millis();
unsigned long d = currentMillis / 25;
if (d != timeMs25Prev )
{
tickMs25 = 1; // elapsed 25mS
timeMs25Prev = d;
}
else
{tickMs25 = 0;}
d = currentMillis / 100;
if (d != timeMs100Prev )
{
tickMs100 = 1; // elapsed 100mS
timeMs100Prev = d;
}
else
{tickMs100 = 0;}
d = currentMillis / 1000;
if (d != timeMs1000Prev )
{
tickMs1000 = 1; // elapsed 1000mS
timeMs1000Prev = d;
}
else
{tickMs1000 = 0;}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Starting Soil Master");
Serial.print("sensors n. ="); Serial.println(numPlants);
esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 0); //1 = High, 0 = Low
// Configure pullup/downs via RTCIO to tie wakeup pins to inactive level during deepsleep.
// EXT0 resides in the same power domain (RTC_PERIPH) as the RTC IO pullup/downs.
// No need to keep that power domain explicitly, unlike EXT1.
rtc_gpio_pullup_en(WAKEUP_GPIO);
rtc_gpio_pulldown_dis(WAKEUP_GPIO);
for (byte ii = 0; ii < numPlants; ii++) {
pinMode(myPlants[ii].SOL_PIN, OUTPUT);
digitalWrite(myPlants[ii].SOL_PIN, HIGH);
}
startMillis = millis();
changeStateSoil(SS_INIT);
}
void loop()
{
// put your main code here, to run repeatedly:
ctrlTime();
if (tickMs25)
{
// elaborations need to be done every 25mS
//readButtons();
ctrlSoil();
}
if (tickMs100)
{
// elaborations need to be done every 100mS
//ctrlLeds();
}
if (tickMs1000)
{
// elaborations need to be done every second
//ctrlLcd();
}
}
Chasing low power sleep is fun. It can become an obsession.
Do you have a meter that can measure the sleep current? The last little bits of current can be hard to find, and eliminate, just by thinking.
a7
Haha. No I don’t think my tooling allows for such small and accurate current measurements. It’s ok for now.
Once I move over to the WiFi 1010 I’ll consider my options. I want data on iot, probably every 30 minutes update. Thats for later anyway.
For now, if the latest code gets your approval I want to….
-
Get onto the WiFi and send data. Linking to Alexa
-
Get the OLED up and running again. This I will defo need some guidance on as I already tried transferring some of the old code into the new fsm code for the oled with no success.
Mark
Im just adding wifi.
Once i put the wifi bit of code in SETUP the program seems to run on but it doesnt actually control my "Solenoids". Not doing anything,
#include "driver/rtc_io.h"
#include <WiFi.h>
const char* ssid = "CornishRes";
const char* password = "Crabpeople2023!!";
unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
struct plantStation {
const int SOIL_PIN;
const int SOL_PIN;
const int thresholdon;
const int thresholdint;
const int thresholdoff;
const int mapdry;
const int mapwet;
const unsigned long MaxRunTime1;
const unsigned long MaxRunTime2;
};
plantStation myPlants[2] = {
{ 2, 26, 50, 70, 80, 770, 397, 10000, 10000 },
{ 15, 27, 50, 70, 80, 770, 397, 10000, 10000 }
};
byte numPlants = sizeof(myPlants) / sizeof(*myPlants);
#define TURNON LOW
#define TURNOFF HIGH
int tickMs25 = 0; // flag
int tickMs100 = 0; // flag
int tickMs1000 = 0; // flag
unsigned long timeMs25Prev = 0;
unsigned long timeMs100Prev = 0;
unsigned long timeMs1000Prev = 0;
enum State {
SS_INIT,
SS_WATERING1,
SS_ENDWATERING1,
SS_DELAY,
SS_WATERING2,
SS_ENDWATERING2,
SS_SLEEP,
SS_LAST
};
// used only in debug print ( when changing state )
String stateStrSoil[SS_LAST] = {"Init","Watering1","End Watering1","Delay","Watering2","End Watering2","Sleep"};
State stateSoil = SS_INIT; // fsm
unsigned long stateTimeSoil = 0; // fsm
#define INIT_DELAY 1000 // delay at startup ( in milliseconds )
#define DELAY_DELAY 20000 // delay between two cycles ( in milliseconds )
#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO) // 2 ^ GPIO_NUMBER in hex
#define USE_EXT0_WAKEUP 1 // 1 = EXT0 wakeup, 0 = EXT1 wakeup
#define WAKEUP_GPIO GPIO_NUM_33 // Only RTC IO are allowed - ESP32 Pin example
#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex
RTC_DATA_ATTR int bootCount = 0;
int soilIndex = 0;
// returns milliseconds elapsed from beginning of current state
unsigned long getStateTimeSoil()
{
return millis() - stateTimeSoil;
}
// call this function when you have to change state
void changeStateSoil(State nextState)
{
Serial.print("State: "); Serial.print(stateStrSoil[stateSoil]);
Serial.print(" duration: "); Serial.println(getStateTimeSoil()/1000.0);
Serial.print("State change from "); Serial.print(stateStrSoil[stateSoil]);
Serial.print(" to "); Serial.print(stateStrSoil[nextState]);
Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// enable this switch if you have to include any state initialization routine
/*switch (stateSoil)
{
case SS_INIT: ctrlInitEntrySoil(); break;
case SS_WATERING1: ctrlWatering1EntryeSoil(); break;
case SS_DELAY: ctrlDelayEntrySoil(); break;
case SS_WATERING2: ctrlWatering2EntrySoil(); break;
}*/
stateTimeSoil = millis();
stateSoil = nextState;
}
void ctrlInitStateSoil()
{
if (getStateTimeSoil() > INIT_DELAY)
{
soilIndex = 0;
changeStateSoil(SS_WATERING1);
}
}
void ctrlDelayStateSoil()
{
if (getStateTimeSoil() > DELAY_DELAY)
{
soilIndex = 0;
changeStateSoil(SS_WATERING2);
}
}
void ctrlSleepStateSoil()
{
Serial.println ("SLEEEEEEEEEEEEEEEEEEEEEEEEEEP");
esp_deep_sleep_start();
// restart cycle
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
soilIndex = 0;
changeStateSoil(SS_WATERING1);
}
void ctrlWatering1StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
if (Soil_Moisture <= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON);
changeStateSoil(SS_ENDWATERING1);
}
else
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
//while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}
void ctrlEndWatering1StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// check soil moisture
if (Soil_Moisture >= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
// check max time
if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime1)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
}
void ctrlWatering2StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
if (Soil_Moisture <= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON);
changeStateSoil(SS_ENDWATERING2);
}
else
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
//while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}
void ctrlEndWatering2StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// check soil moisture
if (Soil_Moisture >= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
// check max time
if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime2)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
}
// finite state machine main loop
void ctrlSoil()
{
switch (stateSoil)
{
case SS_INIT: ctrlInitStateSoil(); break;
case SS_WATERING1: ctrlWatering1StateSoil(); break;
case SS_ENDWATERING1: ctrlEndWatering1StateSoil(); break;
case SS_DELAY: ctrlDelayStateSoil(); break;
case SS_WATERING2: ctrlWatering2StateSoil(); break;
case SS_ENDWATERING2: ctrlEndWatering2StateSoil(); break;
case SS_SLEEP: ctrlSleepStateSoil(); break;
}
}
// sets periodic flags
// better using a sysTick interrupt if available
void ctrlTime()
{
currentMillis = millis();
unsigned long d = currentMillis / 25;
if (d != timeMs25Prev )
{
tickMs25 = 1; // elapsed 25mS
timeMs25Prev = d;
}
else
{tickMs25 = 0;}
d = currentMillis / 100;
if (d != timeMs100Prev )
{
tickMs100 = 1; // elapsed 100mS
timeMs100Prev = d;
}
else
{tickMs100 = 0;}
d = currentMillis / 1000;
if (d != timeMs1000Prev )
{
tickMs1000 = 1; // elapsed 1000mS
timeMs1000Prev = d;
}
else
{tickMs1000 = 0;}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
WiFi.mode(WIFI_STA); //Optional
WiFi.begin(ssid, password);
Serial.println("\nConnecting");
while(WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(100);
}
Serial.println("\nConnected to the WiFi network");
Serial.print("Local ESP32 IP: ");
Serial.println(WiFi.localIP());
delay(1000);
Serial.println("Starting Soil Master");
Serial.print("sensors n. ="); Serial.println(numPlants);
esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 0); //1 = High, 0 = Low
// Configure pullup/downs via RTCIO to tie wakeup pins to inactive level during deepsleep.
// EXT0 resides in the same power domain (RTC_PERIPH) as the RTC IO pullup/downs.
// No need to keep that power domain explicitly, unlike EXT1.
rtc_gpio_pullup_en(WAKEUP_GPIO);
rtc_gpio_pulldown_dis(WAKEUP_GPIO);
for (byte ii = 0; ii < numPlants; ii++) {
pinMode(myPlants[ii].SOL_PIN, OUTPUT);
digitalWrite(myPlants[ii].SOL_PIN, HIGH);
}
startMillis = millis();
changeStateSoil(SS_INIT);
}
void loop()
{
// put your main code here, to run repeatedly:
ctrlTime();
if (tickMs25)
{
// elaborations need to be done every 25mS
//readButtons();
ctrlSoil();
}
if (tickMs100)
{
// elaborations need to be done every 100mS
//ctrlLeds();
}
if (tickMs1000)
{
// elaborations need to be done every second
//ctrlLcd();
}
}
Moved over to the cloud now. Still having issue with the solenoids not being actuated
#include "thingProperties.h"
#include "driver/rtc_io.h"
unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
const int wakeUpPin = 18;
struct plantStation {
const int SOIL_PIN;
const int SOL_PIN;
const int thresholdon;
const int thresholdint;
const int thresholdoff;
const int mapdry;
const int mapwet;
const unsigned long MaxRunTime1;
const unsigned long MaxRunTime2;
};
plantStation myPlants[2] = {
{ 2, 26, 50, 70, 80, 770, 397, 10000, 10000 },
{ 15, 27, 50, 70, 80, 770, 397, 10000, 10000 }
};
byte numPlants = sizeof(myPlants) / sizeof(*myPlants);
#define TURNON LOW
#define TURNOFF HIGH
int tickMs25 = 0; // flag
int tickMs100 = 0; // flag
int tickMs1000 = 0; // flag
unsigned long timeMs25Prev = 0;
unsigned long timeMs100Prev = 0;
unsigned long timeMs1000Prev = 0;
enum State {
SS_INIT,
SS_WATERING1,
SS_ENDWATERING1,
SS_DELAY,
SS_WATERING2,
SS_ENDWATERING2,
SS_SLEEP,
SS_LAST
};
// used only in debug print ( when changing state )
String stateStrSoil[SS_LAST] = {"Init","Watering1","End Watering1","Delay","Watering2","End Watering2","Sleep"};
State stateSoil = SS_INIT; // fsm
unsigned long stateTimeSoil = 0; // fsm
#define INIT_DELAY 1000 // delay at startup ( in milliseconds )
#define DELAY_DELAY 20000 // delay between two cycles ( in milliseconds )
#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO) // 2 ^ GPIO_NUMBER in hex
#define USE_EXT0_WAKEUP 1 // 1 = EXT0 wakeup, 0 = EXT1 wakeup
#define WAKEUP_GPIO GPIO_NUM_33 // Only RTC IO are allowed - ESP32 Pin example
#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex
RTC_DATA_ATTR int bootCount = 0;
int soilIndex = 0;
// returns milliseconds elapsed from beginning of current state
unsigned long getStateTimeSoil()
{
return millis() - stateTimeSoil;
}
// call this function when you have to change state
void changeStateSoil(State nextState)
{
Serial.print("State: "); Serial.print(stateStrSoil[stateSoil]);
Serial.print(" duration: "); Serial.println(getStateTimeSoil()/1000.0);
Serial.print("State change from "); Serial.print(stateStrSoil[stateSoil]);
Serial.print(" to "); Serial.print(stateStrSoil[nextState]);
Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// enable this switch if you have to include any state initialization routine
/*switch (stateSoil)
{
case SS_INIT: ctrlInitEntrySoil(); break;
case SS_WATERING1: ctrlWatering1EntryeSoil(); break;
case SS_DELAY: ctrlDelayEntrySoil(); break;
case SS_WATERING2: ctrlWatering2EntrySoil(); break;
}*/
stateTimeSoil = millis();
stateSoil = nextState;
}
void ctrlInitStateSoil()
{
if (getStateTimeSoil() > INIT_DELAY)
{
soilIndex = 0;
changeStateSoil(SS_WATERING1);
}
}
void ctrlDelayStateSoil()
{
if (getStateTimeSoil() > DELAY_DELAY)
{
soilIndex = 0;
changeStateSoil(SS_WATERING2);
}
}
void ctrlSleepStateSoil()
{
Serial.println ("SLEEEEEEEEEEEEEEEEEEEEEEEEEEP");
esp_deep_sleep_start();
// restart cycle
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
soilIndex = 0;
changeStateSoil(SS_WATERING1);
}
void ctrlWatering1StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
if (Soil_Moisture <= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON);
changeStateSoil(SS_ENDWATERING1);
}
else
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
//while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}
void ctrlEndWatering1StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// check soil moisture
if (Soil_Moisture >= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
// check max time
if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime1)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
}
void ctrlWatering2StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
if (Soil_Moisture <= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON);
changeStateSoil(SS_ENDWATERING2);
}
else
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
//while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}
void ctrlEndWatering2StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// check soil moisture
if (Soil_Moisture >= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
// check max time
if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime2)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
}
// finite state machine main loop
void ctrlSoil()
{
switch (stateSoil)
{
case SS_INIT: ctrlInitStateSoil(); break;
case SS_WATERING1: ctrlWatering1StateSoil(); break;
case SS_ENDWATERING1: ctrlEndWatering1StateSoil(); break;
case SS_DELAY: ctrlDelayStateSoil(); break;
case SS_WATERING2: ctrlWatering2StateSoil(); break;
case SS_ENDWATERING2: ctrlEndWatering2StateSoil(); break;
case SS_SLEEP: ctrlSleepStateSoil(); break;
}
}
// sets periodic flags
// better using a sysTick interrupt if available
void ctrlTime()
{
currentMillis = millis();
unsigned long d = currentMillis / 25;
if (d != timeMs25Prev )
{
tickMs25 = 1; // elapsed 25mS
timeMs25Prev = d;
}
else
{tickMs25 = 0;}
d = currentMillis / 100;
if (d != timeMs100Prev )
{
tickMs100 = 1; // elapsed 100mS
timeMs100Prev = d;
}
else
{tickMs100 = 0;}
d = currentMillis / 1000;
if (d != timeMs1000Prev )
{
tickMs1000 = 1; // elapsed 1000mS
timeMs1000Prev = d;
}
else
{tickMs1000 = 0;}
}
void setup() {
// Initialize serial and wait for port to open:
Serial.begin(115200);
// This delay gives the chance to wait for a Serial Monitor without blocking if none is found
delay(1500);
// Defined in thingProperties.h
initProperties();
// Connect to Arduino IoT Cloud
ArduinoCloud.begin(ArduinoIoTPreferredConnection);
/*
The following function allows you to obtain more information
related to the state of network and IoT Cloud connection and errors
the higher number the more granular information you’ll get.
The default is 0 (only errors).
Maximum is 4
*/
setDebugMessageLevel(2);
ArduinoCloud.printDebugInfo();
Serial.println("Starting Soil Master");
Serial.print("sensors n. ="); Serial.println(numPlants);
esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 0); //1 = High, 0 = Low
// Configure pullup/downs via RTCIO to tie wakeup pins to inactive level during deepsleep.
// EXT0 resides in the same power domain (RTC_PERIPH) as the RTC IO pullup/downs.
// No need to keep that power domain explicitly, unlike EXT1.
rtc_gpio_pullup_en(WAKEUP_GPIO);
rtc_gpio_pulldown_dis(WAKEUP_GPIO);
for (byte ii = 0; ii < numPlants; ii++) {
pinMode(myPlants[ii].SOL_PIN, OUTPUT);
digitalWrite(myPlants[ii].SOL_PIN, HIGH);
}
startMillis = millis();
changeStateSoil(SS_INIT);
}
void loop() {
ArduinoCloud.update();
// Your code here
ctrlTime();
if (tickMs25)
{
// elaborations need to be done every 25mS
//readButtons();
ctrlSoil();
}
if (tickMs100)
{
// elaborations need to be done every 100mS
//ctrlLeds();
}
if (tickMs1000)
{
// elaborations need to be done every second
//ctrlLcd();
}
}
I removed this from void loop(0) and it works again
ArduinoCloud.update();
My sensors are now giving analog readings of 2450 - 4095
It started working properly once I re configured sensor mapping
Here is the latest. I have working widgets on the cloud showing readings
#include "thingProperties.h"
#include "driver/rtc_io.h"
unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
struct plantStation {
const int SOIL_PIN;
const int SOL_PIN;
const int thresholdon;
const int thresholdint;
const int thresholdoff;
const int mapdry;
const int mapwet;
const unsigned long MaxRunTime1;
const unsigned long MaxRunTime2;
};
plantStation myPlants[2] = {
{ 34, 26, 50, 70, 80, 4095, 2450, 10000, 10000 },
{ 35, 27, 50, 70, 80, 4095, 2450, 10000, 10000 }
};
byte numPlants = sizeof(myPlants) / sizeof(*myPlants);
#define TURNON LOW
#define TURNOFF HIGH
int tickMs25 = 0; // flag
int tickMs100 = 0; // flag
int tickMs1000 = 0; // flag
unsigned long timeMs25Prev = 0;
unsigned long timeMs100Prev = 0;
unsigned long timeMs1000Prev = 0;
enum State {
SS_INIT,
SS_WATERING1,
SS_ENDWATERING1,
SS_DELAY,
SS_WATERING2,
SS_ENDWATERING2,
SS_SLEEP,
SS_LAST
};
// used only in debug print ( when changing state )
String stateStrSoil[SS_LAST] = {"Init","Watering1","End Watering1","Delay","Watering2","End Watering2","Sleep"};
State stateSoil = SS_INIT; // fsm
unsigned long stateTimeSoil = 0; // fsm
#define INIT_DELAY 1000 // delay at startup ( in milliseconds )
#define DELAY_DELAY 20000 // delay between two cycles ( in milliseconds )
#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO) // 2 ^ GPIO_NUMBER in hex
#define USE_EXT0_WAKEUP 1 // 1 = EXT0 wakeup, 0 = EXT1 wakeup
#define WAKEUP_GPIO GPIO_NUM_33 // Only RTC IO are allowed - ESP32 Pin example
#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex
RTC_DATA_ATTR int bootCount = 0;
int soilIndex = 0;
// returns milliseconds elapsed from beginning of current state
unsigned long getStateTimeSoil()
{
return millis() - stateTimeSoil;
}
// call this function when you have to change state
void changeStateSoil(State nextState)
{
Serial.print("State: "); Serial.print(stateStrSoil[stateSoil]);
Serial.print(" duration: "); Serial.println(getStateTimeSoil()/1000.0);
Serial.print("State change from "); Serial.print(stateStrSoil[stateSoil]);
Serial.print(" to "); Serial.print(stateStrSoil[nextState]);
Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// enable this switch if you have to include any state initialization routine
/*switch (stateSoil)
{
case SS_INIT: ctrlInitEntrySoil(); break;
case SS_WATERING1: ctrlWatering1EntryeSoil(); break;
case SS_DELAY: ctrlDelayEntrySoil(); break;
case SS_WATERING2: ctrlWatering2EntrySoil(); break;
}*/
stateTimeSoil = millis();
stateSoil = nextState;
}
void ctrlInitStateSoil()
{
if (getStateTimeSoil() > INIT_DELAY)
{
soilIndex = 0;
changeStateSoil(SS_WATERING1);
}
}
void ctrlDelayStateSoil()
{
if (getStateTimeSoil() > DELAY_DELAY)
{
soilIndex = 0;
changeStateSoil(SS_WATERING2);
}
}
void ctrlSleepStateSoil()
{
Serial.println ("SLEEEEEEEEEEEEEEEEEEEEEEEEEEP");
esp_deep_sleep_start();
// restart cycle
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
soilIndex = 0;
changeStateSoil(SS_WATERING1);
}
void ctrlWatering1StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
if (Soil_Moisture <= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON);
changeStateSoil(SS_ENDWATERING1);
}
else
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
//while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}
void ctrlEndWatering1StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// check soil moisture
if (Soil_Moisture >= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
// check max time
if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime1)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
}
void ctrlWatering2StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
if (Soil_Moisture <= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON);
changeStateSoil(SS_ENDWATERING2);
}
else
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
//while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}
void ctrlEndWatering2StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// check soil moisture
if (Soil_Moisture >= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
// check max time
if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime2)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
}
// finite state machine main loop
void ctrlSoil()
{
switch (stateSoil)
{
case SS_INIT: ctrlInitStateSoil(); break;
case SS_WATERING1: ctrlWatering1StateSoil(); break;
case SS_ENDWATERING1: ctrlEndWatering1StateSoil(); break;
case SS_DELAY: ctrlDelayStateSoil(); break;
case SS_WATERING2: ctrlWatering2StateSoil(); break;
case SS_ENDWATERING2: ctrlEndWatering2StateSoil(); break;
case SS_SLEEP: ctrlSleepStateSoil(); break;
}
}
// sets periodic flags
// better using a sysTick interrupt if available
void ctrlTime()
{
currentMillis = millis();
unsigned long d = currentMillis / 25;
if (d != timeMs25Prev )
{
tickMs25 = 1; // elapsed 25mS
timeMs25Prev = d;
}
else
{tickMs25 = 0;}
d = currentMillis / 100;
if (d != timeMs100Prev )
{
tickMs100 = 1; // elapsed 100mS
timeMs100Prev = d;
}
else
{tickMs100 = 0;}
d = currentMillis / 1000;
if (d != timeMs1000Prev )
{
tickMs1000 = 1; // elapsed 1000mS
timeMs1000Prev = d;
}
else
{tickMs1000 = 0;}
}
void setup() {
Serial.begin(115200);
delay(1000);
// Defined in thingProperties.h
initProperties();
// Connect to Arduino IoT Cloud
ArduinoCloud.begin(ArduinoIoTPreferredConnection, false);
/*
The following function allows you to obtain more information
related to the state of network and IoT Cloud connection and errors
the higher number the more granular information you’ll get.
The default is 0 (only errors).
Maximum is 4
*/
setDebugMessageLevel(2);
ArduinoCloud.printDebugInfo();
delay(10000);
Serial.println("Starting Soil Master");
Serial.print("sensors n. ="); Serial.println(numPlants);
for (byte ii = 0; ii < numPlants; ii++) {
pinMode(myPlants[ii].SOL_PIN, OUTPUT);
digitalWrite(myPlants[ii].SOL_PIN, HIGH);
}
startMillis = millis();
changeStateSoil(SS_INIT);
esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 0); //1 = High, 0 = Low
// Configure pullup/downs via RTCIO to tie wakeup pins to inactive level during deepsleep.
// EXT0 resides in the same power domain (RTC_PERIPH) as the RTC IO pullup/downs.
// No need to keep that power domain explicitly, unlike EXT1.
rtc_gpio_pullup_en(WAKEUP_GPIO);
rtc_gpio_pulldown_dis(WAKEUP_GPIO);
}
void loop() {
Moisture1 = analogRead(myPlants[0].SOIL_PIN);
Moisture1 = map(Moisture1, myPlants[0].mapdry, myPlants[0].mapwet, 0, 100);
Moisture2 = analogRead(myPlants[1].SOIL_PIN);
Moisture2 = map(Moisture2, myPlants[1].mapdry, myPlants[1].mapwet, 0, 100);
ArduinoCloud.update();
ctrlTime();
if (tickMs25)
{
// elaborations need to be done every 25mS
//readButtons();
ctrlSoil();
}
if (tickMs100)
{
// elaborations need to be done every 100mS
//ctrlLeds();
}
if (tickMs1000)
{
// elaborations need to be done every second
//ctrlLcd();
}
}
Update - Now waits for wifi and cloud connection before proceeding. Also wakes up every x minutes.
What I want to achieve is arduino waking up every 30 minutes only to update sensors. Main program to run at specific tome of day
Mark
#include "thingProperties.h"
#include "driver/rtc_io.h"
unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
struct plantStation {
const int SOIL_PIN;
const int SOL_PIN;
const int thresholdon;
const int thresholdint;
const int thresholdoff;
const int mapdry;
const int mapwet;
const unsigned long MaxRunTime1;
const unsigned long MaxRunTime2;
};
plantStation myPlants[2] = {
{ 34, 26, 50, 70, 80, 4095, 2450, 10000, 10000 },
{ 35, 27, 50, 70, 80, 4095, 2450, 10000, 10000 }
};
byte numPlants = sizeof(myPlants) / sizeof(*myPlants);
#define TURNON LOW
#define TURNOFF HIGH
int tickMs25 = 0; // flag
int tickMs100 = 0; // flag
int tickMs1000 = 0; // flag
unsigned long timeMs25Prev = 0;
unsigned long timeMs100Prev = 0;
unsigned long timeMs1000Prev = 0;
enum State {
SS_INIT,
SS_WATERING1,
SS_ENDWATERING1,
SS_DELAY,
SS_WATERING2,
SS_ENDWATERING2,
SS_SLEEP,
SS_LAST
};
// used only in debug print ( when changing state )
String stateStrSoil[SS_LAST] = {"Init","Watering1","End Watering1","Delay","Watering2","End Watering2","Sleep"};
State stateSoil = SS_INIT; // fsm
unsigned long stateTimeSoil = 0; // fsm
#define INIT_DELAY 1000 // delay at startup ( in milliseconds )
#define DELAY_DELAY 20000 // delay between two cycles ( in milliseconds )
#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO) // 2 ^ GPIO_NUMBER in hex
#define USE_EXT0_WAKEUP 1 // 1 = EXT0 wakeup, 0 = EXT1 wakeup
#define WAKEUP_GPIO GPIO_NUM_33 // Only RTC IO are allowed - ESP32 Pin example
#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex
#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 30
RTC_DATA_ATTR int bootCount = 0;
int soilIndex = 0;
// returns milliseconds elapsed from beginning of current state
unsigned long getStateTimeSoil()
{
return millis() - stateTimeSoil;
}
// call this function when you have to change state
void changeStateSoil(State nextState)
{
Serial.print("State: "); Serial.print(stateStrSoil[stateSoil]);
Serial.print(" duration: "); Serial.println(getStateTimeSoil()/1000.0);
Serial.print("State change from "); Serial.print(stateStrSoil[stateSoil]);
Serial.print(" to "); Serial.print(stateStrSoil[nextState]);
Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// enable this switch if you have to include any state initialization routine
/*switch (stateSoil)
{
case SS_INIT: ctrlInitEntrySoil(); break;
case SS_WATERING1: ctrlWatering1EntryeSoil(); break;
case SS_DELAY: ctrlDelayEntrySoil(); break;
case SS_WATERING2: ctrlWatering2EntrySoil(); break;
}*/
stateTimeSoil = millis();
stateSoil = nextState;
}
void ctrlInitStateSoil()
{
if (getStateTimeSoil() > INIT_DELAY)
{
soilIndex = 0;
changeStateSoil(SS_WATERING1);
}
}
void ctrlDelayStateSoil()
{
if (getStateTimeSoil() > DELAY_DELAY)
{
soilIndex = 0;
changeStateSoil(SS_WATERING2);
}
}
void ctrlSleepStateSoil()
{
Serial.println ("SLEEEEEEEEEEEEEEEEEEEEEEEEEEP");
esp_deep_sleep_start();
// restart cycle
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
soilIndex = 0;
changeStateSoil(SS_WATERING1);
}
void ctrlWatering1StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
if (Soil_Moisture <= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON);
changeStateSoil(SS_ENDWATERING1);
}
else
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
//while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}
void ctrlEndWatering1StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// check soil moisture
if (Soil_Moisture >= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
// check max time
if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime1)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_DELAY);}
else
{changeStateSoil(SS_WATERING1);}
}
}
void ctrlWatering2StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
if (Soil_Moisture <= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON);
changeStateSoil(SS_ENDWATERING2);
}
else
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
//while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}
void ctrlEndWatering2StateSoil()
{
int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
//Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
// check soil moisture
if (Soil_Moisture >= myPlants[soilIndex].thresholdint)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
// check max time
if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime2)
{
digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF);
soilIndex++;
if (soilIndex >= numPlants )
{changeStateSoil(SS_SLEEP);}
else
{changeStateSoil(SS_WATERING2);}
}
}
void ctrlSoil()
{
switch (stateSoil)
{
case SS_INIT: ctrlInitStateSoil(); break;
case SS_WATERING1: ctrlWatering1StateSoil(); break;
case SS_ENDWATERING1: ctrlEndWatering1StateSoil(); break;
case SS_DELAY: ctrlDelayStateSoil(); break;
case SS_WATERING2: ctrlWatering2StateSoil(); break;
case SS_ENDWATERING2: ctrlEndWatering2StateSoil(); break;
case SS_SLEEP: ctrlSleepStateSoil(); break;
}
}
void ctrlTime()
{
currentMillis = millis();
unsigned long d = currentMillis / 25;
if (d != timeMs25Prev )
{
tickMs25 = 1; // elapsed 25mS
timeMs25Prev = d;
}
else
{tickMs25 = 0;}
d = currentMillis / 100;
if (d != timeMs100Prev )
{
tickMs100 = 1; // elapsed 100mS
timeMs100Prev = d;
}
else
{tickMs100 = 0;}
d = currentMillis / 1000;
if (d != timeMs1000Prev )
{
tickMs1000 = 1; // elapsed 1000mS
timeMs1000Prev = d;
}
else
{tickMs1000 = 0;}
}
void setup() {
Serial.begin(115200);
delay(1000);
initProperties();
ArduinoCloud.begin(ArduinoIoTPreferredConnection, false);
setDebugMessageLevel(2);
ArduinoCloud.printDebugInfo();
while (ArduinoCloud.connected() == 0)
{
ArduinoCloud.update();//required so things don't crash on us
Serial.println("Waiting for connection to Arduino IoT Cloud");
delay(1000);
}
Serial.println("Connected to cloud");
delay(1000);
Serial.println("Starting Soil Master");
Serial.print("sensors n. ="); Serial.println(numPlants);
for (byte ii = 0; ii < numPlants; ii++) {
pinMode(myPlants[ii].SOL_PIN, OUTPUT);
digitalWrite(myPlants[ii].SOL_PIN, HIGH);
}
startMillis = millis();
changeStateSoil(SS_INIT);
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 0); //1 = High, 0 = Low
// Configure pullup/downs via RTCIO to tie wakeup pins to inactive level during deepsleep.
// EXT0 resides in the same power domain (RTC_PERIPH) as the RTC IO pullup/downs.
// No need to keep that power domain explicitly, unlike EXT1.
rtc_gpio_pullup_en(WAKEUP_GPIO);
rtc_gpio_pulldown_dis(WAKEUP_GPIO);
}
void loop() {
Moisture1 = analogRead(myPlants[0].SOIL_PIN);
Moisture1 = map(Moisture1, myPlants[0].mapdry, myPlants[0].mapwet, 0, 100);
Moisture2 = analogRead(myPlants[1].SOIL_PIN);
Moisture2 = map(Moisture2, myPlants[1].mapdry, myPlants[1].mapwet, 0, 100);
ArduinoCloud.update();
ctrlTime();
if (tickMs25)
{
// elaborations need to be done every 25mS
//readButtons();
ctrlSoil();
}
if (tickMs100)
{
// elaborations need to be done every 100mS
//ctrlLeds();
}
if (tickMs1000)
{
// elaborations need to be done every second
//ctrlLcd();
}
}