Created
October 21, 2025 17:43
-
-
Save IgorDePaula/bae42f577827e69a5ecffb06b063045a to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| #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