#include <WiFi.h>
#include <PubSubClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <ArduinoJson.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <SPIFFS.h>
#include <RTClib.h> // Para DS3231
#include <Wire.h> // Para I2C
#define SENSOR_PIN 4 // DS18B20 no GPIO4
#define RELE_PIN 5 // Relé no GPIO5
#define SDA_PIN 21 // DS3231 SDA no GPIO21
#define SCL_PIN 20 // DS3231 SCL no GPIO20
#define WIFI_SSID "Wokwi-GUEST" // Wi-Fi simulado do Wokwi
#define WIFI_PASSWORD ""
#define MQTT_SERVER "broker.hivemq.com" // MQTT público para simulação
#define MQTT_PORT 1883
#define MQTT_USER ""
#define MQTT_PASS ""
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", -10800); // UTC-3 (Brasil)
OneWire oneWire(SENSOR_PIN);
DallasTemperature sensors(&oneWire);
WiFiClient espClient;
PubSubClient client(espClient);
RTC_DS3231 rtc;
unsigned long lastSyncTime = 0;
unsigned long savedEpochTime = 0; // Para armazenar o último tempo salvo
bool timeSynced = false;
bool useRTC = true; // Para DS3231
void setup() {
Serial.begin(115200);
pinMode(RELE_PIN, OUTPUT);
digitalWrite(RELE_PIN, LOW);
sensors.begin();
Wire.begin(SDA_PIN, SCL_PIN); // I2C com SDA no GPIO21, SCL no GPIO20
SPIFFS.begin(true);
timeClient.begin();
if (useRTC && rtc.begin()) {
if (rtc.lostPower()) {
Serial.println("RTC perdeu energia, sincronizando com NTP...");
syncRTCwithNTP();
}
} else {
useRTC = false;
}
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
if (WiFi.waitForConnectResult() == WL_CONNECTED) {
Serial.println("Wi-Fi conectado!");
timeClient.update();
lastSyncTime = millis();
timeSynced = true;
savedEpochTime = timeClient.getEpochTime();
saveLastSyncTime(savedEpochTime);
if (useRTC) syncRTCwithNTP();
} else {
Serial.println("Wi-Fi desconectado, usando RTC interno ou DS3231!");
loadLastSyncTime();
}
client.setServer(MQTT_SERVER, MQTT_PORT);
client.setCallback(callback);
reconnect();
}
void loop() {
if (WiFi.status() == WL_CONNECTED) {
if (!client.connected()) reconnect();
client.loop();
timeClient.update();
lastSyncTime = millis();
timeSynced = true;
savedEpochTime = timeClient.getEpochTime();
saveLastSyncTime(savedEpochTime);
if (useRTC) syncRTCwithNTP();
}
unsigned long now = getCurrentTime();
sensors.requestTemperatures();
float temp = sensors.getTempCByIndex(0);
if (temp != DEVICE_DISCONNECTED_C && temp >= 15 && temp <= 30) {
int dose = calculateDose(temp, 100, 0.5); // 100 peixes, 0.5kg
if (isScheduledTime(now) || !timeSynced) {
digitalWrite(RELE_PIN, HIGH);
delay(dose * 1000); // Dose em segundos
digitalWrite(RELE_PIN, LOW);
saveToFlash(temp, dose, now);
}
if (WiFi.status() == WL_CONNECTED) {
StaticJsonDocument<200> doc;
doc["tanque"] = 1;
doc["temperatura"] = temp;
doc["dose"] = dose;
doc["hora"] = now;
char buffer[200];
serializeJson(doc, buffer);
client.publish("piscicultura/dados", buffer);
Serial.println("Dados enviados: T=" + String(temp) + "°C, Dose=" + String(dose) + "g");
}
} else {
Serial.println("Erro: DS18B20 desconectado ou temperatura inválida!");
}
delay(60000); // Lê a cada 1 minuto
}
void callback(char* topic, byte* payload, unsigned int length) {
String message;
for (int i = 0; i < length; i++) message += (char)payload[i];
StaticJsonDocument<200> doc;
deserializeJson(doc, message);
if (doc["comando"] == "alimentar") {
int dose = doc["dose"];
digitalWrite(RELE_PIN, HIGH);
delay(dose * 1000);
digitalWrite(RELE_PIN, LOW);
}
}
void reconnect() {
while (!client.connected() && WiFi.status() == WL_CONNECTED) {
if (client.connect("ESP32_Piscicultura")) {
Serial.println("MQTT conectado!");
client.subscribe("piscicultura/comandos");
} else {
Serial.println("Falha no MQTT, tentando novamente...");
delay(5000);
}
}
}
int calculateDose(float temp, int numPeixes, float pesoMedio) {
float percent = (temp > 25) ? 0.03 : (temp < 20) ? 0.02 : 0.025; // EMBRAPA
return (numPeixes * pesoMedio * percent) / 0.006; // 6g/s
}
bool isScheduledTime(unsigned long now) {
int hour = (now % 86400) / 3600; // Horas do dia
return (hour == 8 || hour == 12 || hour == 15 || hour == 18); // 4x/dia
}
void saveToFlash(float temp, int dose, unsigned long hora) {
File file = SPIFFS.open("/dados.txt", "a");
file.printf("{\"tanque\":1,\"temperatura\":%.1f,\"dose\":%d,\"hora\":%lu}\n", temp, dose, hora);
file.close();
}
void saveLastSyncTime(unsigned long epochTime) {
File file = SPIFFS.open("/lastSync.txt", "w");
file.println(epochTime);
file.close();
}
void loadLastSyncTime() {
File file = SPIFFS.open("/lastSync.txt", "r");
if (file) {
savedEpochTime = file.readString().toInt();
lastSyncTime = millis();
timeSynced = true;
file.close();
}
}
unsigned long getCurrentTime() {
if (useRTC && !rtc.lostPower()) {
return rtc.now().unixtime();
}
return timeSynced ? (savedEpochTime + (millis() - lastSyncTime) / 1000) : (millis() / 1000);
}
void syncRTCwithNTP() {
if (WiFi.status() == WL_CONNECTED) {
timeClient.update();
rtc.adjust(DateTime(timeClient.getEpochTime()));
}
}
Loading
esp32-s2-devkitm-1
esp32-s2-devkitm-1