#include <Arduino.h>
#include <AsyncTCP.h>
#include <HTTPClient.h>
#include <Arduino_JSON.h>
#include <WiFi.h>
//#include <ESP8266WiFi.h>
#include "SPI.h"
#include <Wire.h>
#include "DHTesp.h"
#include "NTPClient.h"
#include "WiFiUdp.h"
#include <EEPROM.h>
#define DEBUG 1
#if DEBUG
#define PRINT(x) Serial.print(x);
#define PRINTLN(x) Serial.println(x);
#else
#define PRINT(x)
#define PRINTLN(x)
#endif
const int DHT_PIN = 19;
DHTesp dhtSensor;
String inTemp;
String inHum;
String openWeatherMapApiKey = "7e04cc3aaf4976b9ffd56f77d83b1fa4";
String temperature_unit = "metric";
String city = "Bom Jesus do Itabapoana";
String jsonBuffer;
float OutTemperature;
float OutTemperatureMin;
float OutTemperatureMax;
float FeelsTemperature;
int OutHumidity;
String Icon;
int Sun_rise;
int Sun_set;
String endpoint;
String outTemp;
String outHum;
String outTempMax,outTempMin;
String outFeels;
const long utcOffsetInSeconds = 7200;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
String months[12]={"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);
int RELAY_1_PIN = 32;
int RELAY_2_PIN = 33;
int PIR_SENSOR_PIN = 18;
int PIR_SENSOR_STATUS = 0;
int previousStatus = -1;
bool moved;
String movedMessage;
unsigned long currentStateStartTime = 0;
const unsigned long stateDuration = 5000;
unsigned long lastTimeAcc = 0;
unsigned long lastTime = 0;
unsigned long lastTimeMotion = 0;
unsigned long syncDelay = 30000;
unsigned long OWMapDelay = 60000;
unsigned long SensorDelay = 5000;
// TODO: utilizar os estados de uma maneira mais
// apropriada para a solução que buscamos.
// Porque este código foi copiado e os estados funcionavam para outro objetivo.
// Recomendo:
// 1. utilizar um estado para obter todas as informações
// 2. Outro estado para verificar as informações e decidir o que fazer (ligar ou desligar relés, salvar informações no banco de dados, ou em um cartão de memória, etc.)
// Ou qualquer outra solução mais viável.
enum State {
STATE_1, // exibe data e hora
STATE_2, // exibe temperatura e umidade pelo sensor
STATE_3, // exibe temperatura e umidade pelo site
STATE_4, // exibe a informação do sensor de presença
STATE_5, // fecha os relés
STATE_6 // abrir os relés
};
State currentState = STATE_1;
bool printedState2 = false;
bool printedState3 = false;
bool printedState4 = false;
bool printedState5 = false;
bool printedState6 = false;
unsigned long lastSync = 0;
unsigned long lastSecond = 0;
String SunSet;
String SunRise;
String httpGETRequest(const char* serverName) {
WiFiClient client;
HTTPClient http;
// Your Domain name with URL path or IP address with path
http.begin(client, serverName);
// Send HTTP POST request
int httpResponseCode = http.GET();
String payload = "{}";
if (httpResponseCode > 0) {
payload = http.getString();
}
else {
PRINTLN("Error code: ");
PRINTLN(httpResponseCode);
}
// Free resources
http.end();
return payload;
}
String timeConverter(long int epoch){
String sun;
int hr=(epoch % 86400L) / 3600;
int min=(epoch % 3600) / 60;
int sec=(epoch % 60);
sun= String(hr)+":"+String(min);
PRINTLN(sun);
return sun;
}
// Funções "get" -> "coletar as informações para depois lermos"
void getIndoor()
{
TempAndHumidity data = dhtSensor.getTempAndHumidity();
inTemp = String(data.temperature, 0) + " oC";
inHum = String(data.humidity, 0) + "%";
}
// TODO: Verificar se vamos utilizar essas informações externas.
void getOutdoor()
{
endpoint = "http://api.openweathermap.org/data/2.5/weather?q=" + city + "&units=" + temperature_unit + "&appid=" + openWeatherMapApiKey;
jsonBuffer = httpGETRequest(endpoint.c_str());
JSONVar myObject = JSON.parse(jsonBuffer);
if (JSON.typeof(myObject) == "undefined") {
PRINTLN("Parsing input failed!");
}
Icon = JSON.stringify(myObject["weather"][0]["icon"]);
OutTemperature = double(myObject["main"]["temp"]);
OutTemperatureMin = double(myObject["main"]["temp_min"]);
OutTemperatureMax = double(myObject["main"]["temp_max"]);
FeelsTemperature = double(myObject["main"]["feels_like"]);
OutHumidity = int(myObject["main"]["humidity"]);
Sun_rise = int(myObject["sys"]["sunrise"]);
Sun_set = int(myObject["sys"]["sunset"]);
int timeOffset = int(myObject["timezone"]);
outTemp = String(OutTemperature, 0) +" oC";
outHum = String(OutHumidity) + "%";
outFeels = String(FeelsTemperature, 0) + " oC";
outTempMin = String(OutTemperatureMin, 0) + " oC";
outTempMax = String(OutTemperatureMax, 0) + " oC";
SunRise = timeConverter(Sun_rise+utcOffsetInSeconds);
SunSet = timeConverter(Sun_set+utcOffsetInSeconds);
}
void getOutdoor12h()
{
endpoint = "http://api.openweathermap.org/data/2.5/forecast?q=" + city + "&units=" + temperature_unit +"&cnt=3"+ "&appid=" + openWeatherMapApiKey;
jsonBuffer = httpGETRequest(endpoint.c_str());
JSONVar myObject = JSON.parse(jsonBuffer);
if (JSON.typeof(myObject) == "undefined") {
PRINTLN("Parsing input failed!");
}
}
void getMotion()
{
PIR_SENSOR_STATUS = digitalRead(PIR_SENSOR_PIN);
if (PIR_SENSOR_STATUS != previousStatus) {
previousStatus = PIR_SENSOR_STATUS;
moved = PIR_SENSOR_STATUS != 0;
if (moved) {
movedMessage = "Moved!";
} else {
movedMessage = "No moviment.";
}
}
}
// Funções "display" -> "escrevem as coisas no console para lermos"
void displayIndoor()
{
String header = "Indoor " + city;
PRINTLN(header)
PRINT("Temperature: ");
PRINTLN("Max: " + outTempMax);
PRINTLN("Med: " + inTemp);
PRINTLN("Min: " + outTempMin);
PRINT("Humidity: ");
PRINTLN(inHum);
}
void displayOutdoor()
{
String header = Icon + " Outdoor " + city;
PRINTLN(header)
PRINT("Temperature: ");
PRINTLN("Max: " + outTempMax);
PRINTLN("Med: " + outTemp);
PRINTLN("Min: " + outTempMin);
PRINTLN("Feels like: " + outFeels);
PRINT("Humidity: ");
PRINTLN(outHum);
}
void displayTime()
{
unsigned long currentClockMillis = millis();
unsigned long epochTime = 0;
int monthDay = 0;
int currentMonth = 0;
int currentYear = 0;
String Date;
int hours;
int mins;
int seconds;
String Hours;
String Mins;
String Seconds;
if (currentClockMillis - lastSecond >= 1000)
{
lastSecond = currentClockMillis;
PRINT("Time: ");
PRINTLN(timeClient.getFormattedTime());
//timeClient.update();
epochTime = timeClient.getEpochTime();
struct tm *ptm = gmtime ((time_t *)&epochTime);
PRINT("Day: ");
PRINT(daysOfTheWeek[timeClient.getDay()]);
PRINT(", ");
PRINT(timeClient.getHours());
PRINT(":");
PRINT(timeClient.getMinutes());
PRINT(":");
PRINTLN(timeClient.getSeconds());
monthDay = ptm->tm_mday;
PRINT("Month day: ");
PRINTLN(monthDay);
currentMonth = ptm->tm_mon+1;
PRINT("Month: ");
PRINT(currentMonth);
PRINTLN( months[currentMonth-1]);
currentYear = ptm->tm_year+1900;
Date = String(monthDay)+ "/" + String(currentMonth)+ "/"+String(currentYear);
hours = timeClient.getHours();
mins = timeClient.getMinutes();
seconds = timeClient.getSeconds();
Hours = String(hours);
Mins = String(mins);
Seconds = String(seconds);
if (hours<10) Hours = " "+Hours;
if (mins<10) Mins = "0"+Mins;
if (seconds<10) Seconds = "0"+Seconds;
Date = String(daysOfTheWeek[timeClient.getDay()]) + " " + Date;
PRINTLN(Date.length());
}
}
void displayMotion()
{
String header = "Motion";
PRINTLN(header)
PRINT("Moved: ");
PRINTLN(movedMessage);
}
// Funções de verificação
// TODO: inserir a lógica da presença das vacas aqui
bool verifyPresence() {
return moved;
}
// TODO: inserir a lógica de conforto aqui
bool verifyDiscomfort() {
// inTemp
// inHum
// outTemp
// outHum
return true;
}
// Funções de controle dos relés
void turnOnFans() {
digitalWrite(RELAY_1_PIN, HIGH);
}
void turnOffFans() {
digitalWrite(RELAY_1_PIN, LOW);
}
void turnOnNebulizers() {
digitalWrite(RELAY_2_PIN, HIGH);
}
void turnOffNebulizers() {
digitalWrite(RELAY_2_PIN, LOW);
}
// Setup: Prepara todas as IO (entradas e saídas)
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
PRINTLN("Hello, ESP!");
pinMode(RELAY_1_PIN, OUTPUT);
turnOffFans();
pinMode(RELAY_2_PIN, OUTPUT);
turnOffNebulizers();
pinMode(PIR_SENSOR_PIN, INPUT);
dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
getIndoor();
PRINTLN(inTemp);
PRINTLN(inHum);
PRINTLN("---");
PRINT("Connecting to WiFi");
WiFi.begin("Wokwi-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
PRINT(".");
}
PRINTLN(" Connected!");
// WiFi.setAutoReconnect(true);
// WiFi.persistent(true);
timeClient.begin();
configTime(0, 0, "pool.ntp.org");
while (!time(nullptr)) {
delay(1000);
PRINTLN("Waiting for time to synchronize...");
}
getOutdoor();
if (timeClient.update()) {
PRINTLN("Synchronized with NTP.");
} else {
PRINTLN("Error synchronizing with NTP.");
}
//WiFi.disconnect();
//PRINTLN("Wi-Fi turned off.");
PRINTLN(Sun_rise);
PRINTLN(Sun_set);
EEPROM.begin(12);
EEPROM.put(0, 13);
EEPROM.commit();
}
// O loop está estruturado em um padrão de desenho (design pattern)
// chamado "máquina de estados".
void loop() {
unsigned long currentMillis = millis();
// Coleta de dados
if (millis() - lastSync > syncDelay) {
lastSync = millis();
if(WiFi.status() == WL_CONNECTED)
{
timeClient.begin();
if (timeClient.update()) {
PRINTLN("Synchronized with NTP (in loop).");
} else {
PRINTLN("Error synchronizing with NTP (in loop).");
}
}
}
if ((millis() - lastTimeAcc) > OWMapDelay) {
// Send Events to the Web Server with the Sensor Readings
getOutdoor();
lastTimeAcc = millis();
}
if ((millis() - lastTime) > SensorDelay) {
// Send Events to the Web Server with the Sensor Readings
getIndoor();
lastTime = millis();
}
if ((millis() - lastTimeMotion) > SensorDelay) {
// Send Events to the Web Server with the Sensor Readings
getMotion();
lastTimeMotion = millis();
}
// Máquina de estados
if (currentMillis - currentStateStartTime >= stateDuration) {
currentStateStartTime = currentMillis;
printedState2 = false;
printedState3 = false;
printedState4 = false;
printedState5 = false;
printedState6 = false;
switch (currentState) {
case STATE_1:
currentState = STATE_2;
break;
case STATE_2:
currentState = STATE_3;
break;
case STATE_3:
currentState = STATE_4;
break;
case STATE_4:
if (verifyPresence() && verifyDiscomfort()) {
currentState = STATE_5;
} else {
currentState = STATE_6;
}
break;
case STATE_5:
currentState = STATE_1;
break;
case STATE_6:
currentState = STATE_1;
break;
}
}
switch (currentState) {
case STATE_1:
displayTime();
break;
case STATE_2:
if (!printedState2) {
displayIndoor();
printedState2 = true;
}
break;
case STATE_3:
if (!printedState3) {
displayOutdoor();
printedState3 = true;
}
break;
case STATE_4:
if (!printedState4) {
displayMotion();
printedState4 = true;
}
break;
case STATE_5:
if (!printedState5) {
turnOnFans();
turnOnNebulizers();
printedState5 = true;
}
break;
case STATE_6:
if (!printedState6) {
turnOffFans();
turnOffNebulizers();
printedState6 = true;
}
break;
}
}