Last active
August 4, 2022 15:11
-
-
Save Thewest123/9e1db8db753056add4f5902816aaa618 to your computer and use it in GitHub Desktop.
Smart Plant Watering (Thinger.io + NodeMCU + OTA)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//#define _DEBUG_ //Uncomment for ThingerIO debug print | |
#include <ThingerESP8266.h> | |
#include <ThingerConsole.h> | |
#include <ESP8266WiFi.h> | |
#include <ESP8266mDNS.h> | |
#include <WiFiUDP.h> | |
#include <ArduinoOTA.h> | |
#include <NTPClient.h> | |
#define USERNAME "<THINGER_USERNAME>" | |
#define DEVICE_ID "<THINGER_ID>" | |
#define DEVICE_CREDENTIAL "<THINGER_SECRET>" | |
#define SSID "WIFI_SSID" | |
#define SSID_PASSWORD "<WIFI_PASSWORD>" | |
#define HOSTNAME "ESP-MintGrower" | |
#define OTA_PASSWORD "<OTA_PASSWORD>" | |
#define pumpPin 13 | |
#define ledsPin 15 | |
#define analogPin A0 | |
#define moisturePin 14 | |
const int turnOffTimes[] = {23,0,1,2,3,4,5,6}; //Hours when LEDs are disabled, according to NTP time | |
const int AirValue = 680; //Moisture sensor value in air | |
const int WaterValue = 280; //Moisture sensor value in water | |
const int utcOffsetInSeconds = 7200; //UTC offset - set to 2 hours (Czech Republic) | |
// -------------------------------------------------------------------- | |
ThingerESP8266 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL); | |
ThingerConsole console(thing); | |
// Define NTP Client to get time | |
WiFiUDP ntpUDP; | |
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds); | |
int soilMoistureValue; | |
int soilMoisturePercent; | |
bool leds_enabled = false; | |
bool pump_enabled = false; | |
int moisture = 99; | |
unsigned long lastMillisNTP = 10000; | |
unsigned long lastMillisPump = 10000; | |
unsigned long lastMillisBlink = 10000; | |
unsigned long lastMillisMoistureRead = 10000; | |
int onboardLedState = LOW; | |
bool input = false; | |
void setup() { | |
Serial.begin(115200); | |
setupPins(); | |
setupWiFi(); | |
setupOTA(); | |
setupThingerIO(); | |
//NTP Client | |
timeClient.begin(); | |
updateNtpTime(); | |
} | |
void loop() { | |
ArduinoOTA.handle(); | |
unsigned long currentMillis = millis(); | |
//Update NTP every 2 hours | |
if (currentMillis - lastMillisNTP >= 7200000) { | |
lastMillisNTP = currentMillis; | |
updateNtpTime(); | |
} | |
//Turn on/off lights based on NTP time and schedule | |
if (leds_enabled && isNightTime()) { | |
disableLEDs(); | |
} | |
else if (!leds_enabled && !isNightTime()) { | |
enableLEDs(); | |
} | |
//Read Soil Moisture every 1s | |
if (currentMillis - lastMillisMoistureRead >= 1000) { | |
lastMillisMoistureRead = currentMillis; | |
readSoilMoisture(); | |
} | |
//Pump water if moisture is below 30% and last pumping was more than 5 minutes ago | |
if (moisture <= 30 && currentMillis-lastMillisPump > 301000) { | |
printTerminal("Automatic pumping started"); | |
enablePump(); | |
} | |
//Disable pump after 5 seconds of running | |
if(pump_enabled && currentMillis-lastMillisPump > 5000) { | |
disablePump(); | |
} | |
//Blink the LED every 5s | |
if (currentMillis - lastMillisBlink >= 500) { | |
lastMillisBlink = currentMillis; | |
onboardLedState = !onboardLedState; | |
digitalWrite(LED_BUILTIN, onboardLedState); | |
} | |
thing.handle(); | |
} | |
/* | |
* Methods for measuring | |
*/ | |
void readSoilMoisture() { | |
soilMoistureValue = analogRead(analogPin); | |
soilMoisturePercent = constrain(map(soilMoistureValue, AirValue, WaterValue, 0, 100), 0, 100); | |
moisture = soilMoisturePercent; | |
} | |
void enablePump() { | |
lastMillisPump = millis(); | |
printTerminal("Pump enabled"); | |
pump_enabled = true; | |
digitalWrite(pumpPin, HIGH); | |
thing.stream("pump_enabled"); | |
pson data; | |
data["value1"] = moisture; | |
thing.call_endpoint("ifttt_pump_enabled", data); | |
} | |
void disablePump() { | |
digitalWrite(pumpPin, LOW); | |
pump_enabled = false; | |
thing.stream("pump_enabled"); | |
printTerminal("Pump disabled"); | |
} | |
void enableLEDs() { | |
leds_enabled = true; | |
digitalWrite(ledsPin, HIGH); | |
thing.stream("leds_enabled"); | |
printTerminal("LEDs enabled");; | |
} | |
void disableLEDs() { | |
digitalWrite(ledsPin, LOW); | |
leds_enabled = false; | |
thing.stream("leds_enabled"); | |
printTerminal("LEDs disabled"); | |
} | |
/* | |
* Utils | |
*/ | |
void printTerminal(String message) { | |
Serial.println(message); | |
console.println(message); | |
} | |
bool isNightTime() { | |
for (int i = 0; i < sizeof(turnOffTimes); i++) | |
{ | |
if (timeClient.getHours() == turnOffTimes[i]) { | |
return true; | |
} | |
} | |
return false; | |
} | |
void updateNtpTime() { | |
timeClient.update(); | |
printTerminal("(NTP) Time update: " + timeClient.getFormattedTime()); | |
} | |
/* | |
* Setups | |
*/ | |
void setupPins() { | |
pinMode(LED_BUILTIN, OUTPUT); | |
pinMode(pumpPin, OUTPUT); | |
pinMode(ledsPin, OUTPUT); | |
} | |
void setupWiFi() { | |
WiFi.mode(WIFI_STA); | |
WiFi.hostname(HOSTNAME); | |
WiFi.begin(SSID, SSID_PASSWORD); | |
while (WiFi.waitForConnectResult() != WL_CONNECTED) { | |
printTerminal("(WiFi) Connection Failed! Rebooting..."); | |
delay(20000); | |
ESP.restart(); | |
} | |
printTerminal("(WiFi) Connected!"); | |
} | |
void setupOTA() { | |
ArduinoOTA.setPort(8266); | |
ArduinoOTA.setHostname(HOSTNAME); | |
ArduinoOTA.setPassword(OTA_PASSWORD); | |
ArduinoOTA.onStart([]() { | |
printTerminal("(OTA) Start"); | |
}); | |
ArduinoOTA.onEnd([]() { | |
printTerminal("(OTA) End"); | |
}); | |
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { | |
Serial.printf("(OTA) Progress: %u%%\r", (progress / (total / 100))); | |
console.printf("(OTA) Progress: %u%%\r", (progress / (total / 100))); | |
}); | |
ArduinoOTA.onError([](ota_error_t error) { | |
Serial.printf("(OTA) Error[%u]: ", error); | |
console.printf("(OTA) Error[%u]: ", error); | |
if (error == OTA_AUTH_ERROR) printTerminal("Auth Failed"); | |
else if (error == OTA_BEGIN_ERROR) printTerminal("Begin Failed"); | |
else if (error == OTA_CONNECT_ERROR) printTerminal("Connect Failed"); | |
else if (error == OTA_RECEIVE_ERROR) printTerminal("Receive Failed"); | |
else if (error == OTA_END_ERROR) printTerminal("End Failed"); | |
}); | |
ArduinoOTA.begin(); | |
} | |
void setupThingerIO() { | |
//Thinger.io set resources | |
thing["millis"] >> outputValue(millis()); | |
thing["RSSI"] >> outputValue (WiFi.RSSI()); | |
thing["moisture"] >> outputValue(moisture); | |
thing["leds_enabled"] >> [](pson& out){ | |
out = leds_enabled ? 1 : 0; | |
}; | |
thing["pump_enabled"] >> [](pson& out){ | |
out = pump_enabled ? 1 : 0; | |
}; | |
thing["enablePump"] << inputValue(input,{ | |
input = false; | |
enablePump(); | |
}); | |
thing["updateCurrentLEDsState"] << inputValue(input,{ | |
input = false; | |
thing.stream("leds_enabled"); | |
printTerminal("LEDs state updated"); | |
}); | |
thing.handle(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I don't think Thinger.io had its own OTA system at the time of writing this script. But thank you for the info, really good to know for future projects! :)