Skip to content

Instantly share code, notes, and snippets.

@IgorDePaula
Created October 21, 2025 17:43
Show Gist options
  • Save IgorDePaula/bae42f577827e69a5ecffb06b063045a to your computer and use it in GitHub Desktop.
Save IgorDePaula/bae42f577827e69a5ecffb06b063045a to your computer and use it in GitHub Desktop.
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <SSD1306.h>
#include <RTClib.h>
#include <WiFi.h>
#include "LoRa.h"
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include "Preferences.h" // Para salvar SSID/senha
//lora
#define BAND 915E6 // Frequência (915MHz para América do Sul)
#define SCK 5 // GPIO5 -- SX1278's SCK
#define MISO 19 // GPIO19 -- SX1278's MISnO
#define MOSI 27 // GPIO27 -- SX1278's MOSI
#define SS 18 // GPIO18 -- SX1278's CS
#define RST 14 // GPIO14 -- SX1278's RESET
#define DI0 26 // GPIO26 -- SX1278's IRQ(Interrupt Request)
int packetCount = 0;
int lastSendTime = 0;
int interval = 2000; // Intervalo entre envios (2 segundos)
String lastReceived = "";
int lastRSSI = 0;
String message = "";
String boiaState = "";
bool comunicacaoLora = false;
int lastLora = 0;
int loraTimeOut = 3000;
// Configuração do Display OLED
#define OLED_SDA 4
#define OLED_SCL 15
#define OLED_RST 16
SSD1306 display(0x3c, OLED_SDA, OLED_SCL);
SPIClass spiSD(HSPI);
// Configuração do SD Card - Pinos seguros
#define SD_CS 17 // Chip Select (CS)
#define SD_MOSI 23 // Master Out Slave In (MOSI)
#define SD_MISO 2 // Master In Slave Out (MISO)
#define SD_SCK 13 // Serial Clock (SCK)
// RTC DS3231 (usa I2C - SDA=21, SCL=22 por padrão)
// ATENÇÃO: Como SDA/SCL do display já usam 4/15,
// vamos usar I2C secundário nos pinos 21/22
TwoWire I2C_RTC = TwoWire(1);
RTC_DS3231 rtc;
//SPIClass sdSPI(VSPI);
bool sdOk = false;
bool rtcOk = false;
int releState = 0;
// Controle de alternância do display
unsigned long lastDisplayToggle = 0;
bool showNetworkInfo = true;
String resposta = "";
bool deviceConnected = false;
char dataHora[32];
#define RELELIGA 32
#define RELEDESLIGA 25
const float THRESHOLD_LIGAR = 3.0;
const float THRESHOLD_DESLIGAR = 1.0;
bool pinoLigado = false;
//flow sensor
#define FLOW_SENSOR_PIN 33 // GPIO para o sensor de fluxo
volatile int pulseCount = 0;
float flowRate = 0.0;
float flowMilliLitres = 0.0;
float totalMilliLitres = 0.0;
unsigned long oldTimeFlow = 0;
WiFiServer server(80);
// Interrupção do sensor de fluxo
void IRAM_ATTR pulseCounter() {
pulseCount++;
}
void inicializaFlowSensor() {
pinMode(FLOW_SENSOR_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(FLOW_SENSOR_PIN), pulseCounter, FALLING);
oldTimeFlow = millis();
Serial.println("Sensor de fluxo inicializado no GPIO 33");
}
void calculaFluxo() {
unsigned long currentTime = millis();
if ((currentTime - oldTimeFlow) >= 1000) { // Calcula a cada 1 segundo
detachInterrupt(digitalPinToInterrupt(FLOW_SENSOR_PIN));
// Fator de calibração: pulsos por litro (para YF-S201 é ~7.5 pulsos/L)
float calibrationFactor = 7.5;
// Cálculo da vazão
flowRate = ((1000.0 / (currentTime - oldTimeFlow)) * pulseCount) / calibrationFactor;
oldTimeFlow = currentTime;
// Calcula volume em mL no último segundo
flowMilliLitres = (flowRate / 60) * 1000;
// Adiciona ao total
totalMilliLitres += flowMilliLitres;
if (flowRate > THRESHOLD_LIGAR && !pinoLigado) {
digitalWrite(RELELIGA, LOW);
pinoLigado = true;
delay(100);
digitalWrite(RELELIGA, HIGH);
}
if (flowRate < THRESHOLD_DESLIGAR && pinoLigado) {
delay(100);
digitalWrite(RELELIGA, HIGH);
delay(100);
digitalWrite(RELEDESLIGA, LOW);
delay(100);
digitalWrite(RELEDESLIGA, HIGH);
pinoLigado = false;
}
pulseCount = 0;
attachInterrupt(digitalPinToInterrupt(FLOW_SENSOR_PIN), pulseCounter, FALLING);
}
}
//lora
// UUIDs originais para SSID e Senha
#define UUID_SSID "c505b1de-4a31-11ef-9d90-47f7f9b3a434"
#define UUID_PASS "c505b49e-4a31-11ef-9d90-47f7f9b3a434"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
// Novas UUIDs para comando scan, resultado e status WiFi
#define UUID_SCAN_CMD "c505b600-4a31-11ef-9d90-47f7f9b3a434"
#define UUID_SCAN_RESULT "c505b601-4a31-11ef-9d90-47f7f9b3a434"
#define UUID_WIFI_STATUS "c505b602-4a31-11ef-9d90-47f7f9b3a434"
#define UUID_BOMB_CMD "c505b603-4a31-11ef-9d90-47f7f9b3a434"
#define UUID_BOMB_RESULT "c505b604-4a31-11ef-9d90-47f7f9b3a434"
BLEServer* pServer;
BLECharacteristic* pScanCmdChar;
BLECharacteristic* pBombCmdChar;
BLECharacteristic* pScanResultChar;
BLECharacteristic* pWifiStatusChar;
BLECharacteristic* pBombStatusChar;
String ip, rede;
Preferences preferences; // NVS
BLEAdvertising* pAdvertising;
uint16_t connId = 0; // guarda o ID da conexão para desconectar
String performWifiScanAsJson() {
int n = WiFi.scanNetworks(true); // show_hidden = true
int o = WiFi.scanComplete();
if (o >= 0) { // scan terminou
// gerar JSON e enviar Notify
WiFi.scanDelete(); // limpa resultado
}
String json = "[";
for (int i = 0; i < n; i++) {
String ssid = WiFi.SSID(i);
if (ssid.length() == 0) ssid = "<OCULTO>";
json += "{\"ssid\":\"" + ssid + "\",\"rssi\":" + String(WiFi.RSSI(i)) + "}";
if (i < n - 1) json += ",";
}
json += "]";
return json;
}
void ligaRele(int rele) {
digitalWrite(rele, LOW);
delay(1000);
digitalWrite(rele, HIGH);
releState = 0;
}
// --- Callbacks para comando SCAN ---
class ScanCmdCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic* pCharacteristic) {
String value = pCharacteristic->getValue();
if (value == "SCAN") {
Serial.println("Comando SCAN recebido via BLE...");
// Pausa Advertising BLE
pAdvertising->stop();
delay(200);
// Scan WiFi
String jsonResult = performWifiScanAsJson();
// Reativa Advertising BLE
pAdvertising->start();
// Envia resultado via Notify
pScanResultChar->setValue(jsonResult.c_str());
pScanResultChar->notify();
Serial.println("Scan concluído e JSON enviado via BLE Notify:");
Serial.println(jsonResult);
}
if (value == "OFF") {
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
resposta = "Wi-Fi desligado";
Serial.println("Wi-Fi desligado via BLE");
if (deviceConnected && pCharacteristic != NULL) {
pCharacteristic->setValue(resposta.c_str());
pCharacteristic->notify();
}
}
}
};
class BombCmdCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic* pCharacteristic) {
String value = pCharacteristic->getValue();
if (value == "ONBOMB") {
Serial.println("Comando ONBOMB recebido");
releState = 1;
String resposta = "Bomba ligada";
ligaRele(RELELIGA);
if (deviceConnected && pCharacteristic != NULL) {
pBombStatusChar->setValue("Bomba ligada");
pBombStatusChar->notify();
}
}
if (value == "OFFBOMB") {
Serial.println("Comando OFFBOMB recebido");
releState = 2;
ligaRele(RELEDESLIGA);
if (deviceConnected && pCharacteristic != NULL) {
pBombStatusChar->setValue("Bomba desligada");
pBombStatusChar->notify();
}
}
}
};
// --- Callbacks para SSID/Password ---
class WifiCredentialsCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic* pCharacteristic) {
String value = pCharacteristic->getValue();
static String ssid = "";
static String pass = "";
if (pCharacteristic->getUUID().toString() == UUID_SSID) {
ssid = String(value.c_str());
Serial.println("SSID recebido: " + ssid);
} else if (pCharacteristic->getUUID().toString() == UUID_PASS) {
pass = String(value.c_str());
Serial.println("Senha recebida: " + pass);
}
if (ssid.length() > 0 && pass.length() > 0) {
Serial.println("Tentando conectar à rede WiFi...");
WiFi.begin(ssid.c_str(), pass.c_str());
int timeout = 0;
while (WiFi.status() != WL_CONNECTED && timeout < 20) { // ~10s
delay(500);
Serial.print(".");
timeout++;
}
Serial.println();
if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi conectado! IP: " + WiFi.localIP().toString());
pWifiStatusChar->setValue("OK");
pWifiStatusChar->notify();
// Salva SSID/senha na NVS
preferences.begin("wifi", false);
preferences.putString("ssid", ssid);
preferences.putString("pass", pass);
preferences.end();
} else {
Serial.println("Falha na conexão WiFi");
pWifiStatusChar->setValue("ERROR");
pWifiStatusChar->notify();
}
ssid = "";
pass = "";
}
}
};
// botoes
class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
connId = pServer->getConnId();
Serial.println("Cliente BLE conectado");
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
Serial.println("Cliente BLE desconectado");
// pServer->startAdvertising(); // Reinicia advertising
pAdvertising->start();
}
};
void setup() {
Serial.begin(115200);
delay(2000);
Serial.println("=== HELTEC ESP32 + SD CARD + RTC ===");
// Inicializa display
inicializaDisplay();
// Inicializa I2C secundário para RTC (pinos 21, 22)
I2C_RTC.begin(21, 22, 100000); // SDA=21, SCL=22, 100kHz
delay(100);
// Inicializa I2C secundário para RTC (pinos 21, 22)
//Wire1.begin(21, 22); // SDA=21, SCL=22
inicializaLora();
// Inicializa RTC
inicializaRTC();
// Inicializa SD Card
inicializaSD();
inicializaFlowSensor();
// Se tudo OK, grava dados iniciais
if (sdOk && rtcOk) {
gravaLogInicial();
}
atualizaDisplay();
pinMode(RELELIGA, OUTPUT);
pinMode(RELEDESLIGA, OUTPUT);
digitalWrite(RELELIGA, HIGH);
digitalWrite(RELEDESLIGA, HIGH);
// --- Inicializa BLE ---
BLEDevice::init("Water Pump Ranger");
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
BLEService* pService = pServer->createService(BLEUUID((uint16_t)0x180A));
BLECharacteristic* pSSIDChar = pService->createCharacteristic(UUID_SSID, BLECharacteristic::PROPERTY_WRITE);
BLECharacteristic* pPassChar = pService->createCharacteristic(UUID_PASS, BLECharacteristic::PROPERTY_WRITE);
pSSIDChar->setCallbacks(new WifiCredentialsCallbacks());
pPassChar->setCallbacks(new WifiCredentialsCallbacks());
pScanCmdChar = pService->createCharacteristic(UUID_SCAN_CMD, BLECharacteristic::PROPERTY_WRITE);
pScanCmdChar->setCallbacks(new ScanCmdCallbacks());
pBombCmdChar = pService->createCharacteristic(UUID_BOMB_CMD, BLECharacteristic::PROPERTY_WRITE);
pBombCmdChar->setCallbacks(new BombCmdCallbacks());
pScanResultChar = pService->createCharacteristic(UUID_SCAN_RESULT, BLECharacteristic::PROPERTY_NOTIFY);
pScanResultChar->addDescriptor(new BLE2902());
pBombStatusChar = pService->createCharacteristic(UUID_BOMB_RESULT, BLECharacteristic::PROPERTY_NOTIFY);
pBombStatusChar->addDescriptor(new BLE2902());
pWifiStatusChar = pService->createCharacteristic(UUID_WIFI_STATUS, BLECharacteristic::PROPERTY_NOTIFY);
pWifiStatusChar->addDescriptor(new BLE2902());
pService->start();
pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(pService->getUUID());
pAdvertising->start();
Serial.println("BLE iniciado e advertising ativo.");
// --- Inicializa WiFi ---
WiFi.setHostname("Water111-Pump_Ranger");
WiFi.mode(WIFI_STA);
WiFi.disconnect(true);
Serial.println("WiFi pronto.");
//conecta automaticamente usando NVS ---
preferences.begin("wifi", false);
String savedSSID = preferences.getString("ssid", "");
String savedPass = preferences.getString("pass", "");
preferences.end();
if (savedSSID.length() > 0) {
Serial.println("Reconectando ao WiFi salvo: " + savedSSID);
WiFi.begin(savedSSID.c_str(), savedPass.c_str());
int timeout = 0;
while (WiFi.status() != WL_CONNECTED && timeout < 20) {
delay(500);
Serial.print(".");
timeout++;
}
Serial.println();
if (WiFi.status() == WL_CONNECTED) {
ip = WiFi.localIP().toString();
rede = WiFi.SSID();
Serial.println("Reconexão bem sucedida! IP: " + WiFi.localIP().toString());
} else {
Serial.println("Falha na reconexão automática");
}
}
server.begin();
}
void inicializaLora() {
SPI.begin(SCK, MISO, MOSI, SS);
LoRa.setPins(SS, RST, DI0);
// Inicializa LoRa
if (!LoRa.begin(BAND)) {
Serial.println("Erro ao iniciar LoRa!");
//displayMessage("ERRO LoRa!", "Verifique", "conexões");
while (1)
;
}
LoRa.setSpreadingFactor(10); // SF7 - SF12 (maior = mais alcance, menor velocidade)
LoRa.setSignalBandwidth(125E3); // 125kHz
LoRa.setCodingRate4(5); // 4/5
LoRa.setPreambleLength(8); // Preâmbulo
LoRa.enableCrc(); // Habilita CRC
Serial.println("LoRa inicializado com sucesso!");
Serial.println("Frequência: " + String(BAND / 1E6) + " MHz");
}
void sendMessage() {
//packetCount++;
//String state = voltagemMean < 30 ? "alto" : "baixo";
//String message = "Boia em nivel " + state;
// Envia mensagem
LoRa.beginPacket();
LoRa.print(dataHora);
LoRa.endPacket();
Serial.println("Enviado: " + String(dataHora));
//displayMessage("ENVIANDO", message, "");
};
void loop() {
int packetSize = LoRa.parsePacket();
if (packetSize) {
receiveMessage();
}
if (millis() - lastLora > loraTimeOut) {
comunicacaoLora = false;
}
else{
comunicacaoLora = true;
}
// Envia mensagem a cada intervalo definido
if (millis() - lastSendTime > interval) {
sendMessage();
//releState = 20;
lastSendTime = millis();
}
static unsigned long ultimaGravacao = 0;
// digitalWrite(ENBTN, HIGH);
if (millis() - lastDisplayToggle > 2000) {
lastDisplayToggle = millis();
showNetworkInfo = !showNetworkInfo;
}
// Calcula fluxo de água
calculaFluxo();
// Grava dados a cada 10 segundos
if (millis() - ultimaGravacao > 10000) {
ultimaGravacao = millis();
if (sdOk && rtcOk) {
//gravaDadosComTimestamp();
}
}
atualizaDisplay();
verifyFlow();
checaRele();
//checkBoia();
//static unsigned long lastCheck = 0;
// if (millis() - lastCheck > 10000) {
// lastCheck = millis();
// if (WiFi.status() != WL_CONNECTED) {
// Serial.println("WiFi caiu, tentando reconectar...");
// preferences.begin("wifi", false);
// String savedSSID = preferences.getString("ssid", "");
// String savedPass = preferences.getString("pass", "");
// preferences.end();
// if (savedSSID.length() > 0) {
// WiFi.begin(savedSSID.c_str(), savedPass.c_str());
// int timeout = 0;
// while (WiFi.status() != WL_CONNECTED && timeout < 20) {
// delay(500);
// Serial.print(".");
// timeout++;
// }
// Serial.println();
// if (WiFi.status() == WL_CONNECTED) {
// ip = WiFi.localIP().toString();
// rede = WiFi.SSID();
// Serial.println("Reconexão WiFi bem sucedida! IP: " + WiFi.localIP().toString());
// } else {
// Serial.println("Falha na reconexão WiFi");
// }
// }
// }
// }
}
void registraInformacao() {
DateTime now = rtc.now();
char secondString[3]; // Character array to hold the second (e.g., "05", "59")
// Size 3: 2 digits + null terminator
char minuteString[3];
sprintf(secondString, "%02d", now.second());
sprintf(minuteString, "%02d", now.minute());
if (minuteString == "00" && secondString == "00") {
// Nome do arquivo baseado na data
char nomeArquivo[32];
sprintf(nomeArquivo, "/dados_%04d_%02d_%02d.csv",
now.year(), now.month(), now.day());
// Verifica se é um arquivo novo (precisa de cabeçalho)
bool arquivoNovo = !SD.exists(nomeArquivo);
File arquivo = SD.open(nomeArquivo, FILE_APPEND);
if (!arquivo) {
Serial.println("Erro ao abrir arquivo de dados");
return;
}
// Adiciona cabeçalho se arquivo novo
if (arquivoNovo) {
arquivo.println("timestamp,data,hora,estado bomba,nivel boia,fluxo agua");
}
String releStateString = "";
if (releState == 1) {
releStateString = "Ligado";
}
if (releState == 2) {
releStateString = "Desligado";
}
// Grava dados
arquivo.printf("%lu,%02d/%02d/%04d,%02d:%02d:%02d,%s,%s,%.1f,\n",
now.unixtime(),
now.day(), now.month(), now.year(),
now.hour(), now.minute(), now.second(),
releStateString, lastReceived == "1" ? "Alto" : "Baixo", flowRate);
arquivo.close();
}
}
void checkBoia() {
if (message == "1") {
releState = 2;
}
}
void checaRele() {
// Serial.print("Relestate: ");
// Serial.println(releState);
if (releState == 1) {
digitalWrite(RELELIGA, LOW);
} else if (releState == 2) {
digitalWrite(RELEDESLIGA, LOW);
}
releState = 0;
}
void verifyFlow() {
Serial.print("Flow na funcao: ");
Serial.println(flowRate);
if (flowRate >= 3.0 && releState == 0) {
//ligaRele(RELELIGA);
}
if (flowRate <= 1.0 && releState == 1) {
releState = 2;
ligaRele(RELEDESLIGA);
//flowRate = 0.0;
}
registraInformacao();
}
void inicializaDisplay() {
pinMode(OLED_RST, OUTPUT);
digitalWrite(OLED_RST, LOW);
delay(20);
digitalWrite(OLED_RST, HIGH);
display.init();
display.flipScreenVertically();
display.clear();
display.setFont(ArialMT_Plain_10);
display.drawString(0, 0, "Inicializando...");
display.display();
Serial.println("Display OK");
}
void receiveMessage() {
// Lê mensagem recebida
while (LoRa.available()) {
lastLora = millis();
message += (char)LoRa.read();
Serial.print("mensagem ");
Serial.println(message);
if (message == "1") {
releState = 2;
}
}
int rssi = LoRa.packetRssi();
float snr = LoRa.packetSnr();
lastReceived = message;
lastRSSI = rssi;
message = "";
// Debug serial
Serial.println("========================================");
Serial.println("Mensagem recebida: " + message);
Serial.println("RSSI: " + String(rssi) + " dBm");
Serial.println("SNR: " + String(snr) + " dB");
Serial.println("========================================");
// Atualiza display
//displayMessage("RECEBIDO", message, "RSSI: " + String(rssi) + "dBm");
}
void inicializaRTC() {
Serial.print("Inicializando RTC DS3231... ");
if (!rtc.begin(&I2C_RTC)) {
Serial.println("RTC não encontrado!");
rtcOk = false;
return;
}
rtcOk = true;
Serial.println("OK");
// Verifica se RTC perdeu energia
if (rtc.lostPower()) {
Serial.println("RTC perdeu energia, configurando data/hora...");
// Configura data/hora atual (ajuste conforme necessário)
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// Mostra data/hora atual
DateTime now = rtc.now();
Serial.printf("Data/Hora atual: %02d/%02d/%04d %02d:%02d:%02d\n",
now.day(), now.month(), now.year(),
now.hour(), now.minute(), now.second());
// Configurações do DS3231
rtc.disable32K(); // Desabilita saída 32kHz
rtc.clearAlarm(1); // Limpa alarme 1
rtc.clearAlarm(2); // Limpa alarme 2
rtc.writeSqwPinMode(DS3231_OFF); // Desabilita onda quadrada
}
void inicializaSD() {
Serial.print("Inicializando SD Card... ");
// Configura SPI customizado
//sdSPI.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
spiSD.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
if (!SD.begin(SD_CS, spiSD)) {
Serial.println("FALHOU!");
sdOk = false;
return;
}
sdOk = true;
Serial.println("OK");
// Mostra informações do cartão
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("Tamanho do cartão: %llu MB\n", cardSize);
Serial.printf("Espaço usado: %llu MB\n", SD.usedBytes() / (1024 * 1024));
}
void gravaLogInicial() {
DateTime now = rtc.now();
File arquivo = SD.open("/system_log.txt", FILE_APPEND);
if (!arquivo) {
Serial.println("Erro ao abrir log do sistema");
return;
}
arquivo.println("===============================");
arquivo.printf("SISTEMA INICIADO: %02d/%02d/%04d %02d:%02d:%02d\n",
now.day(), now.month(), now.year(),
now.hour(), now.minute(), now.second());
arquivo.printf("ESP32 ID: %08X\n", (uint32_t)ESP.getEfuseMac());
arquivo.printf("RAM Livre: %d KB\n", ESP.getFreeHeap() / 1024);
arquivo.printf("SD Card: %llu MB\n", SD.cardSize() / (1024 * 1024));
arquivo.println("Status: RTC + SD funcionando");
if (rede.length() > 0) {
arquivo.printf("Rede: %s \n", rede);
}
if (ip.length() > 0) {
arquivo.printf("IP: %s \n", ip);
}
arquivo.println("===============================");
arquivo.close();
Serial.println("Log inicial gravado com sucesso!");
}
void gravaDadosComTimestamp() {
DateTime now = rtc.now();
// Nome do arquivo baseado na data
char nomeArquivo[32];
sprintf(nomeArquivo, "/dados_%04d_%02d_%02d.csv",
now.year(), now.month(), now.day());
// Verifica se é um arquivo novo (precisa de cabeçalho)
bool arquivoNovo = !SD.exists(nomeArquivo);
File arquivo = SD.open(nomeArquivo, FILE_APPEND);
if (!arquivo) {
Serial.println("Erro ao abrir arquivo de dados");
return;
}
// Adiciona cabeçalho se arquivo novo
if (arquivoNovo) {
arquivo.println("timestamp,data,hora,temperatura,umidade,bateria,ram_livre");
}
// Simula dados de sensores
float temperatura = 20.0 + random(0, 150) / 10.0; // 20.0 a 35.0°C
float umidade = 40.0 + random(0, 400) / 10.0; // 40.0 a 80.0%
int bateria = random(70, 101); // 70% a 100%
int ramLivre = ESP.getFreeHeap() / 1024; // KB
// Grava dados
arquivo.printf("%lu,%02d/%02d/%04d,%02d:%02d:%02d,%.1f,%.1f,%d,%d\n",
now.unixtime(),
now.day(), now.month(), now.year(),
now.hour(), now.minute(), now.second(),
temperatura, umidade, bateria, ramLivre);
arquivo.close();
Serial.printf("Dados gravados: %02d/%02d/%04d %02d:%02d:%02d",
now.day(), now.month(), now.year(),
now.hour(), now.minute(), now.second());
Serial.println("");
}
void atualizaDisplay() {
display.clear();
// Status dos módulos
display.drawString(0, 0, "Status Sistema:");
display.drawString(0, 12, rtcOk ? "RTC: OK ✓" : "RTC: ERRO ✗");
display.drawString(64, 12, sdOk ? "SD: OK ✓" : "SD: ERRO ✗");
// Se RTC OK, mostra data/hora
if (rtcOk) {
DateTime now = rtc.now();
//char dataHora[32];
sprintf(dataHora, "%02d/%02d/%04d %02d:%02d:%02d",
now.day(), now.month(), now.year(),
now.hour(), now.minute(), now.second());
display.drawString(0, 26, dataHora);
}
if (showNetworkInfo) {
String boiaState = lastReceived == "1" ? "alto" : "baixo";
if (!comunicacaoLora) {
display.drawString(0, 38, "Boia sem comunicacao");
} else {
display.drawString(0, 38, "Boia em nivel " + boiaState);
}
// Mostra Wi-Fi e IP
if (WiFi.status() == WL_CONNECTED) {
//display.drawString(0, 26, "WiFi: " + WiFi.SSID());
display.drawString(0, 50, "IP: " + WiFi.localIP().toString());
}
} else {
if (WiFi.status() == WL_CONNECTED) {
display.drawString(0, 50, "REDE: " + WiFi.SSID());
}
char flowText[32];
sprintf(flowText, "Fluxo: %.1f L/min", flowRate);
display.drawString(0, 38, flowText);
}
char flowText[32];
sprintf(flowText, "Fluxo: %.1f L/min", flowRate);
//Serial.println(flowText);
display.display();
boiaState = "";
//lastReceived = "";
}
// Funções auxiliares para ajustar RTC manualmente
void ajustarRTC(int ano, int mes, int dia, int hora, int minuto, int segundo) {
rtc.adjust(DateTime(ano, mes, dia, hora, minuto, segundo));
Serial.printf("RTC ajustado para: %02d/%02d/%04d %02d:%02d:%02d\n",
dia, mes, ano, hora, minuto, segundo);
}
// Função para listar arquivos do SD
void listarArquivos() {
if (!sdOk) return;
Serial.println("\n--- ARQUIVOS NO SD CARD ---");
File root = SD.open("/");
File arquivo = root.openNextFile();
while (arquivo) {
if (!arquivo.isDirectory()) {
Serial.printf("%-20s %8d bytes\n", arquivo.name(), arquivo.size());
}
arquivo = root.openNextFile();
}
Serial.printf("Espaço total: %llu MB\n", SD.totalBytes() / (1024 * 1024));
Serial.printf("Espaço usado: %llu MB\n", SD.usedBytes() / (1024 * 1024));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment