//watchdog
extern "C" {
#include "user_interface.h"
}
//wifi and https
#include <ESP8266WiFi.h>
#include "HTTPSRedirect.h"
#include "DebugMacros.h"
#include <ArduinoOTA.h>
//for get time via internet
//#include <NTPClient.h>
//#include <WiFiUdp.h>
#include "user_interface.h"
//for time
#include <Wire.h>
#include <SPI.h>
#include "RTClib.h"
//for temp
#include <DHT.h>
//temp and anemo
#define DHTPIN D7 // pin for termometer
//#define DHTTYPE DHT11 // DHT 22 (AM2302)
DHT dht;
#include <math.h>
#define WindSensorPin D5 // The pin location of the anemometer sensor
//rain gauge
#define RainPin D8
//wifi settings
const char* ssid = "xxxxx"; //replace with our wifi ssid
const char* password = "yyyyy"; //replace with your wifi password
//for send to google
const char* host = "script.google.com";
const char *GScriptId = "yyyyy"; // Replace with your own google script id
const int httpsPort = 443; //the https port is same
// echo | openssl s_client -connect script.google.com:443 |& openssl x509 -fingerprint -noout
const char* fingerprint = "";
//const uint8_t fingerprint[20] = {};
char url[] = "/macros/s/myid/exec?value=Temperature"; // Write Teperature to Google Spreadsheet at cell A1
// Fetch Google Calendar events for 1 week ahead
char url2[] = "/macros/s/myid/exec?cal"; // Write to Cell A continuosly
//replace with sheet name not with spreadsheet file name taken from google
String payload_base = "{\"command\": \"appendRow\", \
\"sheet_name\": \"Data\", \
\"values\": ";
String payload = "";
HTTPSRedirect* client = nullptr;
//for Time
RTC_DS3231 rtc;
const long utcOffsetInSeconds = 3600;
bool wasSet;
//rainGauge
bool bucketPositionA = false; // one of the two positions of tipping-bucket
const double bucketAmount = 2.25; // ml to trip tipping-bucket
double dailyRain = 0.00; // rain accumulated for the day
double hourlyRain = 0.0; // rain accumulated for one hour
double dailyRain_till_LastHour = 0.0; // rain accumulated for the day till the last hour
bool first;
void ICACHE_RAM_ATTR handleInterruptRain();// as we want readings of the (MHz) loops only at the 0th moment
volatile unsigned long tipCount;
volatile unsigned long ContactBounceTime2;
//anemo
volatile unsigned long Rotations; // cup rotation counter used in interrupt routine
volatile unsigned long ContactBounceTime; // Timer to avoid contact bounce in interrupt routine
float WindSpeed; // speed kmh
float WindMaxSpeedToday;
void ICACHE_RAM_ATTR handleInterruptWind();
DateTime previousTimeRead;
//temp
//DHT dht(DHTPIN, DHTTYPE); //// Initialize DHT sensor for normal 16mhz Arduino
int chk;
float hum; //Stores humidity value
float temp; //Stores temperature value
int count;
//for get time over wifi
//WiFiUDP ntpUDP;
//NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);
//String formattedDate;
//-------------------------------------SETUP-------------------------------------------------------------------------
void setup(void) {
Serial.begin(115200);
//wifi set
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
ArduinoOTA.setPassword((const char *)"yyyyy");
ArduinoOTA.onStart([]() {
Serial.println("Start");
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
// start the serial port
//for Time
// Wire.begin(D4, D3);
//rtc.begin();
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
// while (1);
}
Wire.begin();
// timeClient.begin();
//Serial.println("RTC lost power, lets set the time!");
//rtc.adjust(DateTime(__DATE__, __TIME__)); // Time and date is expanded to date and time on your computer at compiletime
//rtc.adjust(DateTime(2021, 4, 5, 19, 34, 30 )); // UNCOMMENT TO SET TIME & DATE MANUALLY. (YEAR, MONTH, DAY, 24-HOUR, MINUTE, SECOND)
// timeClient.update();
//temp
//dht.begin();
dht.setup(DHTPIN);
count = 0;
//anemo
pinMode(WindSensorPin, INPUT);
attachInterrupt(digitalPinToInterrupt(WindSensorPin), handleInterruptWind, FALLING);
WindMaxSpeedToday = 0;
//rain gauge
pinMode(RainPin, INPUT); // set the Rain Pin as input.
attachInterrupt(digitalPinToInterrupt(RainPin), handleInterruptRain, FALLING);
tipCount=0;
//for send to GOOGLE
// Use HTTPSRedirect class to create a new TLS connection
client = new HTTPSRedirect(httpsPort);
client->setInsecure();
//client->setPrintResponseBody(true);
client->setContentTypeHeader("application/json");
Serial.print("Connecting to ");
Serial.println(host); //try to connect with "script.google.com"
Serial.println("Status/signal: ");
Serial.println(WiFi.status());
Serial.println(WiFi.RSSI());
Serial.println("--------------");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
// Try to connect for a maximum of 5 times then exit
bool flag = false;
for (int i = 0; i < 5; i++) {
int retval = client->connect(host, httpsPort);
if (retval == 1) {
flag = true;
break;
}
else
Serial.println("Connection failed. Retrying...");
}
if (!flag) {
Serial.print("Could not connect to server: ");
Serial.println(host);
Serial.println("Exiting...");
return;
}
// Finish setup() function in 1s since it will fire watchdog timer and will reset the chip.
//So avoid too many requests in setup()
Serial.println("\nStart Sending Sensor Data to Google Spreadsheet");
// delete HTTPSRedirect object
delete client;
client = nullptr;
previousTimeRead = 1;
//watchdog to 8 seconds
ESP.wdtDisable();
ESP.wdtEnable(WDTO_8S);
//ENABLE INTERRUPTS FOR ANEMOMETER AND RAIN GAUGE
sei();
//---------------
Serial.println("Ready!!!"); // not necessary too
}
//-----------------------------------------RTC reset. Use this if 165 165 error
#define SDA_LOW() (GPES = (1 << SDA))
#define SDA_HIGH() (GPEC = (1 << SDA))
#define SCL_LOW() (GPES = (1 << SCL))
#define SCL_HIGH() (GPEC = (1 << SCL))
#define SDA_READ() ((GPI & (1 << SDA)) != 0)
void resetRTC() {
pinMode(SDA, INPUT_PULLUP);
pinMode(SCL, INPUT_PULLUP);
do {
SDA_HIGH();
SCL_HIGH();
if (SDA_READ()) {
SDA_LOW();
SDA_HIGH();
}
SCL_LOW();
// ESP.wdtFeed();
} while (SDA_READ() == 0);
}
// This is the function that the interrupt calls to increment the rotation count
void handleInterruptWind() {
if ((millis() - ContactBounceTime) > 15 ) { // debounce the switch contact.
Rotations++;
ContactBounceTime = millis();
}
}
void handleInterruptRain() {
if ((millis() - ContactBounceTime2) > 15 ) { // debounce the switch contact.
dailyRain+= bucketAmount;
ContactBounceTime2 = millis();
}
}
bool checkForRTCTimeChange(DateTime now)
{
if ((now.year() % 4) == 0 && now.day() == 1 && now.month() == 3) {
rtc.adjust(DateTime(now.year(), 2, 29, now.hour(), now.minute(), now.second()));
return true;
}
if(now.dayOfTheWeek()==0 && now.month() == 3 && now.day() > 24)
{
rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour()+1, now.minute(), now.second()));
wasSet = false;
return true;
}
else if(now.dayOfTheWeek()==0 && now.month() == 10 && now.day() > 24 && wasSet != true)
{
rtc.adjust(DateTime(now.year(), now.month(), now.day()-1, 23, now.minute(), now.second()));
//variable used to prevent forever going back for 1h
wasSet=true;
return true;
}
}
void(* resetFunc) (void) = 0; //declare reset function @ address 0
//----------------------LOOP--------------------------------
//----------------------LOOP--------------------------------
//----------------------LOOP--------------------------------
//----------------------LOOP--------------------------------
void loop(void){
//reset watchdog back to 8s
ESP.wdtFeed();
Serial.println("---------------------------------------------------");
//resetRTC();
DateTime now = rtc.now();
/*if(previousTimeRead.day() != now.day())
{
}*/
if(previousTimeRead.day() != now.day() && previousTimeRead != 1) {
if(checkForRTCTimeChange(now))
{
now = rtc.now();
}
resetFunc(); //call reset
}
Serial.print("Status/signal: ");
Serial.print(WiFi.status());
Serial.print("/");
int rssi = WiFi.RSSI();
Serial.println(rssi);
//check if wifi was in sleep mode(21:00 - 6:00)
if((previousTimeRead.hour()<6 || previousTimeRead.hour()>21) && previousTimeRead.hour() < 24)
{
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
// WiFi.reconnect ();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
}
//temp and anemo-----------------------------------------------------------------------------------------------
Rotations = 0; // Set Rotations count to 0 ready for calculations
//sei(); // Enables interrupts
//delay (2000);
//CAUSES HTTPCLIENT TO STOP WORKING!!!!!!
//cli(); // Disable interrupts
//delay (2000);
// Wait 3 seconds to average
delay (3000);
WindSpeed = Rotations * 0.75 *1.609344;//to kmh; 1 mph = 1.609344
if(WindMaxSpeedToday < WindSpeed || (previousTimeRead.day() != now.day()&& previousTimeRead != 1)) WindMaxSpeedToday = WindSpeed;
Serial.print(" WindSpeed: ");
Serial.print(WindSpeed);
Serial.println(" kmh");
//reset watchdog back to 8s
ESP.wdtFeed();
if(count == 0)
{
cli();
//Read data and store it to variables hum and temp
delay(dht.getMinimumSamplingPeriod());
//Serial.println(dht.getMinimumSamplingPeriod());
hum = dht.getHumidity();
temp= dht.getTemperature();
sei();
count = 8;
}
count = count -1;
//Print temp and humidity values to serial monitor
Serial.print("Humidity: ");
Serial.print(hum);
Serial.print(" %, Temp: ");
Serial.print(temp);
Serial.println(" Celsius");
//----------RAIN GAUGE
if(now.minute() != 0) first = true; // after the first minute is over, be ready for next read
bool saveData = false;
if(now.minute() == 0 && first == true){
saveData = true;
hourlyRain = dailyRain - dailyRain_till_LastHour; // calculate the last hour's rain
dailyRain_till_LastHour = dailyRain; // update the rain till last hour for next calculation
// facny display for humans to comprehend
Serial.print(now.hour());
Serial.print(":");
Serial.print(now.minute());
Serial.print(": Total Rain for the day = ");
Serial.print(dailyRain,8); // the '8' ensures the required accuracy
Serial.println(" ml");
Serial.println();
Serial.print(" : Rain in last hour = ");
Serial.print(hourlyRain,8);
Serial.println(" ml");
Serial.println();
first = 0; // execute calculations only once per hour
}
if(now.hour()== 0) {
dailyRain = 0.0; // clear daily-rain at midnight
dailyRain_till_LastHour = 0.0; // we do not want negative rain at 01:00
}
//reset watchdog back to 8s
ESP.wdtFeed();
//for send to GOOGLE spreadsheets---------------------------------
static int error_count = 0;
const unsigned int MAX_CONNECT = 20;
static bool flag = false;
//String sheetDateTime = String(now.hour(),1)+":"+String(now.minute(),1)+":"+String(now.second(),1)+" "+String(now.day(),1)+". "+String(now.month(),1)+". "+String(now.year(),1);
char buf1[20];
sprintf(buf1, "%02d:%02d:%02d %02d.%02d.%02d", now.hour(), now.minute(), now.second(), now.day(), now.month(), now.year());
uint32_t free = system_get_free_heap_size();
Serial.println(free);
if(saveData == true)
payload = payload_base + "\"" + buf1+ ";" +temp + ";" + hum + ";" + WindSpeed+ ";" +WindMaxSpeedToday +";" + hourlyRain+";" +dailyRain+";true;"+free+";"+rssi+"\"}";
else
payload = payload_base + "\"" + buf1+ ";" +temp + ";" + hum + ";" + WindSpeed+ ";" +WindMaxSpeedToday +";" + hourlyRain+";" +dailyRain+";false;"+free+";"+rssi+"\"}";
// saveDataString = "false";
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println(payload);
if (!flag) {
client = new HTTPSRedirect(httpsPort);
client->setInsecure();
flag = true;
client->setPrintResponseBody(true);
client->setContentTypeHeader("application/json");
}
//bool wasSent= false;
if (client != nullptr) {
if (!client->connected()) {
client->connect(host, httpsPort);
//client->POST(url2, host, payload, false);
//Serial.print("Sent : "); Serial.println("Temp and Humid");
}
}
else {
DPRINTLN("Error creating client object!");
error_count = 5;
}
Serial.println("client->POST");
bool x = client->POST(url2, host, payload);
Serial.println("client->POST - done");
if (x) {
Serial.println("DATA SENT");
error_count = 0;
}
else {
++error_count;
DPRINT("Error-count while connecting: ");
DPRINTLN(error_count);
}
if (error_count > 3) {
Serial.println("Error count more than 3");
Serial.printf("Final free heap: %u\n", ESP.getFreeHeap());
Serial.printf("Final stack: %u\n", ESP.getFreeContStack());
Serial.flush();
}
previousTimeRead= now;
ArduinoOTA.handle();
client->stop();
if(now.hour()<6)
{
Serial.println("go to sleep for 63 seconds");
//reset watchdog back to 8s
ESP.wdtFeed();
WiFi.disconnect();
WiFi.mode(WIFI_OFF);
delay(1);
WiFi.forceSleepBegin();
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
}
else if(now.hour()>21 && now.hour() < 24)
{
Serial.println("go to sleep for 21 seconds");
//reset watchdog back to 8s
ESP.wdtFeed();
WiFi.disconnect();
WiFi.mode(WIFI_OFF);
delay(1);
WiFi.forceSleepBegin();
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
}
} // end of loop