Skip to content

Instantly share code, notes, and snippets.

@IgorDePaula
Created October 3, 2025 22:43
Show Gist options
  • Save IgorDePaula/5077ee518b095455520920341da85ec8 to your computer and use it in GitHub Desktop.
Save IgorDePaula/5077ee518b095455520920341da85ec8 to your computer and use it in GitHub Desktop.
/*
* Script Simples para Comunicação LoRa
* Heltec ESP32 V2 LoRa - Usando bibliotecas padrão
* Alternativa ao heltec.h para evitar erros de compilação
*/
#include <SPI.h>
#include <U8g2lib.h>
#include "LoRa.h"
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include "ZMPT101B.h"
#include "EEPROM.h"
// Pinout Heltec ESP32 V2
#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)
#define OLED_SDA 4 // GPIO4 -- OLED's SDA
#define OLED_SCL 15 // GPIO15 -- OLED's SCL
#define OLED_RST 16 // GPIO16 -- OLED's RESET
#define BOIA_PIN 13
// Configurações LoRa
#define BAND 915E6 // Frequência (915MHz para América do Sul)
#define SENSITIVITY 500.00f
// Display OLED
U8G2_SSD1306_128X64_NONAME_F_SW_I2C display(U8G2_R0, OLED_SCL, OLED_SDA, OLED_RST);
ZMPT101B voltage(13, 60.0);
// Variáveis globais
int packetCount = 0;
int lastSendTime = 0;
int interval = 2000; // Intervalo entre envios (2 segundos)
String lastReceived = "";
int lastRSSI = 0;
int boiaState = 0;
float voltagemMean = 0.0;
void setup() {
Serial.begin(115200);
while (!Serial)
;
Serial.println("Heltec ESP32 LoRa - Comunicação Simples");
voltage.setSensitivity(SENSITIVITY);
// Inicializa display OLED
display.begin();
display.enableUTF8Print();
displayMessage("Iniciando LoRa...", "", "");
// Configura pinos SPI para o LoRa
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)
;
}
// Configurações LoRa
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");
displayMessage("LoRa OK!", "915 MHz", "Aguardando...");
delay(2000);
}
void loop() {
// Verifica se há mensagens recebidas
for (int count = 0; count < 5; count++) {
float volt = voltage.getRmsVoltage();
voltagemMean += volt;
delay(10);
}
voltagemMean = voltagemMean / 5;
Serial.print("boia ");
Serial.println(voltagemMean);
int packetSize = LoRa.parsePacket();
Serial.println(packetSize);
if (packetSize) {
receiveMessage();
}
// Envia mensagem a cada intervalo definido
if (millis() - lastSendTime > interval) {
sendMessage();
lastSendTime = millis();
}
delay(10);
voltagemMean = 0;
}
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 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 displayMessage(String line1, String line2, String line3) {
display.clearBuffer();
// Linha 1
display.setFont(u8g2_font_ncenB08_tr);
display.drawStr(0, 12, line1.c_str());
// Linha 2
display.setFont(u8g2_font_ncenB08_tr);
display.drawStr(0, 28, line2.c_str());
// Linha 3
display.setFont(u8g2_font_ncenB08_tr);
display.drawStr(0, 44, line3.c_str());
// Status na parte inferior
display.setFont(u8g2_font_u8glib_4_tr);
String status = "TX: " + String(packetCount) + " | " + (lastReceived != "" ? "RX: " + String(lastRSSI) + "dBm" : "RX: --");
display.drawStr(0, 60, status.c_str());
display.sendBuffer();
}
const unsigned char myBitmap [] PROGMEM = {
// 'image982, 113x44px
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xf0,
0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x03, 0xe0, 0xff,
0xff, 0xff, 0xff, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x03, 0xe0, 0x7f, 0xff,
0xff, 0xff, 0xfe, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xe0, 0x7f, 0xff, 0xff,
0xff, 0xfe, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0xe0, 0x7f, 0xff, 0xff, 0xff,
0xfe, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x03, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xfe,
0x07, 0x80, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x03, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x07,
0x80, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x01, 0x03, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80,
0xff, 0xff, 0xff, 0xff, 0x00, 0x07, 0x03, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80, 0xff,
0xff, 0xff, 0xfc, 0x00, 0x1f, 0x03, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80, 0xff, 0xff,
0xff, 0xf0, 0x00, 0x7f, 0x03, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff,
0xc0, 0x01, 0xff, 0x03, 0xe0, 0x7f, 0xff, 0xe7, 0xff, 0xfe, 0x07, 0x80, 0xff, 0xff, 0xff, 0x80,
0x07, 0xff, 0x03, 0xe0, 0x7f, 0xff, 0x81, 0xff, 0xfe, 0x07, 0x80, 0xff, 0xff, 0xfe, 0x00, 0x1f,
0xff, 0x03, 0xe0, 0x7f, 0xfe, 0x00, 0x7f, 0xfe, 0x07, 0x80, 0xff, 0xff, 0xf8, 0x00, 0x3f, 0xff,
0x03, 0xe0, 0x7f, 0xf8, 0x00, 0x1f, 0xfe, 0x07, 0x80, 0xff, 0xff, 0xe0, 0x00, 0xff, 0xff, 0x03,
0xe0, 0x7f, 0xe0, 0x00, 0x07, 0xfe, 0x07, 0x80, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0x03, 0xe0,
0x7f, 0x80, 0x00, 0x01, 0xfe, 0x07, 0x80, 0xff, 0xfe, 0x00, 0x0f, 0xff, 0xff, 0x03, 0xe0, 0x7f,
0x00, 0x00, 0x00, 0xfe, 0x07, 0x80, 0xff, 0xf8, 0x00, 0x3f, 0xff, 0xff, 0x03, 0xe0, 0x7c, 0x00,
0x3c, 0x00, 0x3e, 0x07, 0x80, 0xff, 0xe0, 0x00, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x70, 0x00, 0x7e,
0x00, 0x0e, 0x07, 0x80, 0xff, 0x80, 0x03, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x40, 0x01, 0xff, 0x80,
0x03, 0x07, 0x80, 0xfe, 0x00, 0x0f, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x00, 0x07, 0xff, 0xe0, 0x00,
0x07, 0x80, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x07,
0x80, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x07, 0x80,
0xf0, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x01, 0xff, 0xff, 0xff, 0x80, 0x07, 0x80, 0xf0,
0x07, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xe0, 0x07, 0x80, 0xf0, 0x1f,
0xff, 0xff, 0xff, 0xff, 0x83, 0xe0, 0x1f, 0xff, 0xff, 0xff, 0xf8, 0x07, 0x80, 0xf8, 0x7f, 0xff,
0xff, 0xff, 0xff, 0x87, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x80, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x80
};
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <SSD1306.h>
#include <RTClib.h>
#include "LoRa.h"
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
//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)
// Configuração do Display OLED
#define OLED_SDA 4
#define OLED_SCL 15
#define OLED_RST 16
SSD1306 display(0x3c, OLED_SDA, OLED_SCL);
// 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
RTC_DS3231 rtc;
SPIClass sdSPI(VSPI);
bool sdOk = false;
bool rtcOk = false;
char dataHora[32];
//lora
int packetCount = 0;
int lastSendTime = 0;
int interval = 2000; // Intervalo entre envios (2 segundos)
String lastReceived = "";
int lastRSSI = 0;
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
inicializaLora();
// Inicializa RTC
inicializaRTC();
// Inicializa SD Card
inicializaSD();
// Se tudo OK, grava dados iniciais
if (sdOk && rtcOk) {
gravaLogInicial();
}
atualizaDisplay();
}
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() {
static unsigned long ultimaGravacao = 0;
// Grava dados a cada 10 segundos
if (millis() - ultimaGravacao > 10000) {
ultimaGravacao = millis();
if (sdOk && rtcOk) {
gravaDadosComTimestamp();
}
}
atualizaDisplay();
delay(1000);
}
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");
sendMessage();
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