Skip to content

Instantly share code, notes, and snippets.

@IgorDePaula
Created October 3, 2025 11:02
Show Gist options
  • Save IgorDePaula/bee85231b162c5bbe4a023449e355f07 to your computer and use it in GitHub Desktop.
Save IgorDePaula/bee85231b162c5bbe4a023449e355f07 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 <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include "LoRa.h"
// Configuração do Display OLED
#define OLED_SDA 4
#define OLED_SCL 15
#define OLED_RST 16
SSD1306 display(0x3c, OLED_SDA, OLED_SCL);
#define BAND 915E6 // Frequência (915MHz para América do Sul)
#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;
// Configuração do SD Card - Pinos seguros
#define SD_CS 5 // 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
RTC_DS3231 rtc;
SPIClass sdSPI(VSPI);
bool sdOk = false;
bool rtcOk = false;
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)
Wire1.begin(21, 22); // SDA=21, SCL=22
// Inicializa RTC
inicializaRTC();
// Inicializa SD Card
inicializaSD();
inicializaLoRa();
// Se tudo OK, grava dados iniciais
if (sdOk && rtcOk) {
gravaLogInicial();
}
atualizaDisplay();
}
void inicializaLoRa() {
LoRa.setPins(SS, RST, DI0);
// Inicializa LoRa
if (!LoRa.begin(BAND)) {
Serial.println("Erro ao iniciar LoRa!");
display.drawString(0,0,"Erro Lora!");//displayMessage("ERRO LoRa!", "Verifique", "conexões");
while (1)
;
}
// Configurações LoRa
LoRa.setSpreadingFactor(7); // SF7 - SF12 (maior = mais alcance, menor velocidade)
LoRa.setSignalBandwidth(125E3); // 125kHz
LoRa.setCodingRate4(5); // 4/5
LoRa.setPreambleLength(8); // Preâmbulo
LoRa.enableCrc();
Serial.println("LoRa inicializado com sucesso!");
Serial.println("Frequência: " + String(BAND / 1E6) + " MHz");
//displayMessage("LoRa OK!", "915 MHz", "Aguardando...");
}
void loop() {
static unsigned long ultimaGravacao = 0;
// Grava dados a cada 10 segundos
if (millis() - ultimaGravacao > 10000) {
ultimaGravacao = millis();
if (sdOk && rtcOk) {
gravaDadosComTimestamp();
}
}
int packetSize = LoRa.parsePacket();
if (packetSize) {
receiveMessage();
}
atualizaDisplay();
delay(1000);
}
void receiveMessage() {
String message = "";
// Lê mensagem recebida
while (LoRa.available()) {
message += (char)LoRa.read();
}
int rssi = LoRa.packetRssi();
float snr = LoRa.packetSnr();
lastReceived = message;
lastRSSI = rssi;
// 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 sendMessage() {
packetCount++;
//String state = voltagemMean < 30 ? "alto" : "baixo";
//String message = "Boia em nivel " + state;
// Envia mensagem
//LoRa.beginPacket();
//LoRa.print(voltagemMean < 30 ? 1 : 0);
//LoRa.endPacket();
//Serial.println("Enviado: " + message);
//displayMessage("ENVIANDO", message, "");
}
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 inicializaRTC() {
Serial.print("Inicializando RTC DS3231... ");
if (!rtc.begin(&Wire1)) {
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);
if (!SD.begin(SD_CS, sdSPI)) {
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");
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 - T:%.1f°C, U:%.1f%%, B:%d%%\n",
now.day(), now.month(), now.year(),
now.hour(), now.minute(), now.second(),
temperatura, umidade, bateria);
}
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);
}
// Informações do sistema
display.drawString(0, 38, "RAM: " + String(ESP.getFreeHeap() / 1024) + " KB");
display.drawString(0, 50, "Tempo: " + String(millis() / 1000) + "s");
display.display();
}
// 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