855 lines
26 KiB
C++
855 lines
26 KiB
C++
// LoRaService.h
|
||
|
||
#ifndef LoRaService_h
|
||
#define LoRaService_h
|
||
|
||
#include "SerialService.h"
|
||
#include "CanService.h"
|
||
#include <Arduino.h>
|
||
#include <vector>
|
||
#include <map>
|
||
|
||
class LoRaService {
|
||
public:
|
||
|
||
enum LoRaMode {
|
||
NORMAL,
|
||
WAKE_UP,
|
||
POWER_SAVING,
|
||
CONFIG
|
||
};
|
||
|
||
enum CodigosFuncoes {
|
||
CfgTx = 0xC0,
|
||
CfgRx = 0xC1,
|
||
Msg = 0xE1,
|
||
WrongMode = 0xFC,
|
||
BeginMsg = 0xAA,
|
||
EndMsg = 0x55,
|
||
};
|
||
|
||
struct LoRaParametrosModel {
|
||
uint8_t address;
|
||
uint8_t baudAndAirRate;
|
||
uint8_t packetSizeAndPower;
|
||
uint8_t channel;
|
||
uint8_t tranModeAndWorCycle;
|
||
};
|
||
|
||
|
||
|
||
LoRaService(CanService* canService) {
|
||
_canService = canService;
|
||
}
|
||
|
||
bool Conectado = false;
|
||
bool Configurado = false;
|
||
byte AddressBase = 0x01;
|
||
|
||
// Inicializa pinos e UART
|
||
void Inicializar(uint8_t baudRate) {
|
||
Configurado = false;
|
||
|
||
_baudRate = encontrarChavePorValor(CodigosBaudRates, baudRate, 9600);
|
||
|
||
MostrarLog("Iniciando LoRa nos pinos TXD=" + String(_pinTXD) + ", RXD=" + String(_pinRXD) + ", baudRate=" + String(_baudRate) + "...");
|
||
|
||
// Inicializa GPIOs
|
||
pinMode(_pinENA, OUTPUT);
|
||
digitalWrite(_pinENA, HIGH);
|
||
vTaskDelay(100);
|
||
digitalWrite(_pinENA, LOW);
|
||
vTaskDelay(20);
|
||
pinMode(_pinM0, OUTPUT);
|
||
pinMode(_pinM1, OUTPUT);
|
||
pinMode(_pinAUX, INPUT);
|
||
|
||
_serialLoRa = &Serial2;
|
||
if (Conectado) {
|
||
_serialLoRa->end();
|
||
}
|
||
_serialLoRa->begin(_baudRate, SERIAL_8N1, _pinTXD, _pinRXD);
|
||
|
||
// Coloca o módulo em modo normal
|
||
setLoRaMode(NORMAL);
|
||
|
||
vTaskDelay(500); // Aguarda estabilização
|
||
|
||
// Testa conexão
|
||
Conectado = checkLoRaConnected();
|
||
if (Conectado) {
|
||
MostrarLog("E220 detectado!");
|
||
|
||
if (LraTaskRxHandle == NULL) {
|
||
lraQueueRx = xQueueCreate(50, sizeof(std::vector<uint8_t>));
|
||
if (lraQueueRx == NULL) {
|
||
MostrarLog("Falha ao criar fila RX!");
|
||
}
|
||
xTaskCreatePinnedToCore(LoRaService::LraTaskRxWrapper, "LraTaskRx", 4096, this, 14, &LraTaskRxHandle, APP_CPU_NUM);
|
||
}
|
||
|
||
if (LraTaskTxHandle == NULL) {
|
||
lraQueueTx = xQueueCreate(50, sizeof(std::vector<uint8_t>));
|
||
if (lraQueueTx == NULL) {
|
||
MostrarLog("Falha ao criar fila TX!");
|
||
}
|
||
xTaskCreatePinnedToCore(LoRaService::LraTaskTxWrapper, "LraTaskTx", 4096, this, 13, &LraTaskTxHandle, APP_CPU_NUM);
|
||
}
|
||
|
||
if (LraTaskProcessHandle == NULL) {
|
||
xTaskCreatePinnedToCore(LoRaService::LraTaskProcessWrapper, "LraTaskProcess", 4096, this, 15, &LraTaskProcessHandle, APP_CPU_NUM);
|
||
}
|
||
} else {
|
||
MostrarLog("E220 não detectado.");
|
||
}
|
||
}
|
||
|
||
|
||
std::vector<uint8_t> MontarMensagemCAN(CanMessagePosicaoDados posicao) {
|
||
std::vector<uint8_t> data;
|
||
data.push_back(static_cast<uint8_t>(posicao));
|
||
data.push_back(_canService->ID_Num_sLRA);
|
||
switch (posicao) {
|
||
case CanMessagePosicaoDados::Status: {
|
||
data.push_back(Conectado ? 1 : 0);
|
||
data.push_back(Configurado ? 1 : 0);
|
||
break;
|
||
}
|
||
case CanMessagePosicaoDados::Dados1: {
|
||
data.push_back(parametrosAtual.address);
|
||
data.push_back(parametrosAtual.baudAndAirRate);
|
||
data.push_back(parametrosAtual.packetSizeAndPower);
|
||
data.push_back(parametrosAtual.channel);
|
||
data.push_back(parametrosAtual.tranModeAndWorCycle);
|
||
break;
|
||
}
|
||
}
|
||
return data;
|
||
}
|
||
|
||
void EnviarComando(std::vector<uint8_t> dados) {
|
||
if (dados.size() < 2) return;
|
||
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)dados[0];
|
||
uint8_t idNum = dados[1];
|
||
switch (posicao) {
|
||
case CanMessagePosicaoDados::Command1: {
|
||
//bool sucesso = requisitarParametrosLoRa();
|
||
EnviarDadosCAN(MontarMensagemCAN(CanMessagePosicaoDados::Dados1));
|
||
break;
|
||
}
|
||
default: {
|
||
AdicionarMensagemFilaLoRa(dados);
|
||
}
|
||
}
|
||
}
|
||
|
||
bool AdicionarMensagemFilaLoRa(std::vector<uint8_t> dados) {
|
||
if (xQueueSend(lraQueueTx, &dados, 0) != pdTRUE) {
|
||
MostrarLog("Fila TX cheia! Mensagem descartada.");
|
||
return false;
|
||
}
|
||
else {
|
||
MostrarLog("Mensagem adicionada na fila TX");
|
||
return true;
|
||
}
|
||
}
|
||
|
||
std::vector<uint8_t> ConfigurarModulo(std::vector<uint8_t> data) {
|
||
std::vector<uint8_t> status;
|
||
if (data.size() < 2) return status;
|
||
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)data[0];
|
||
uint8_t idNum = data[1];
|
||
//MostrarLog("Recebido comando ConfigurarModulo: posicao = " + String((int)posicao));
|
||
switch (posicao) {
|
||
case CanMessagePosicaoDados::Config1: {
|
||
if (data.size() < 8) return data;
|
||
_pinENA = data[2];
|
||
_pinM0 = data[3];
|
||
_pinM1 = data[4];
|
||
_pinAUX = data[5];
|
||
_pinRXD = data[6];
|
||
_pinTXD = data[7];
|
||
break;
|
||
}
|
||
case CanMessagePosicaoDados::Config2: {
|
||
if (data.size() < 8) return status;
|
||
if (_pinRXD == 0 || _pinTXD == 0) {
|
||
MostrarLog("Erro ao iniciar E220, pinout ainda nao definido!");
|
||
return status;
|
||
}
|
||
|
||
AddressBase = data[2];
|
||
uint8_t addr = data[3]; // address
|
||
uint8_t baud = data[4]; // baudAndAirRate
|
||
uint8_t packet = data[5]; // packetSizeAndPower
|
||
uint8_t channel = data[6]; // channel
|
||
uint8_t worCycle = data[7]; // tranModeAndWorCycle
|
||
|
||
if (DebugMode) {
|
||
String log = String("Parametros Config:") +
|
||
" 0x" + String(AddressBase, HEX) +
|
||
" 0x" + String(addr, HEX) +
|
||
" 0x" + String(baud, HEX) +
|
||
" 0x" + String(packet, HEX) +
|
||
" 0x" + String(channel, HEX) +
|
||
" 0x" + String(worCycle, HEX);
|
||
MostrarLog(log);
|
||
}
|
||
|
||
uint8_t baudField = baud & 0xE0; // bits 5,6,7 (baudrate)
|
||
Inicializar(baudField);
|
||
//EnviarDadosCAN(MontarMensagemCAN(CanMessagePosicaoDados::Status));
|
||
if (Conectado) {
|
||
if (requisitarParametrosLoRa()) {
|
||
// Log detalhado do que veio da EEPROM
|
||
MostrarLog("EEPROM -> "
|
||
"Addr=" + String(parametrosAtual.address, HEX) +
|
||
" BaudAir=" + String(parametrosAtual.baudAndAirRate, HEX) +
|
||
" PacketPower=" + String(parametrosAtual.packetSizeAndPower, HEX) +
|
||
" Channel=" + String(parametrosAtual.channel, HEX) +
|
||
" TranModeWor=" + String(parametrosAtual.tranModeAndWorCycle, HEX)
|
||
);
|
||
|
||
MostrarLog("Desejado -> "
|
||
"Addr=" + String(addr, HEX) +
|
||
" BaudAir=" + String(baud, HEX) +
|
||
" PacketPower=" + String(packet, HEX) +
|
||
" Channel=" + String(channel, HEX) +
|
||
" TranModeWor=" + String(worCycle, HEX)
|
||
);
|
||
|
||
Configurado =
|
||
parametrosAtual.address == addr &&
|
||
parametrosAtual.baudAndAirRate == baud &&
|
||
parametrosAtual.packetSizeAndPower == packet &&
|
||
parametrosAtual.channel == channel &&
|
||
parametrosAtual.tranModeAndWorCycle== worCycle;
|
||
|
||
MostrarLog(String("Modulo ja configurado? ") + (Configurado ? "SIM" : "NAO"));
|
||
|
||
if (!Configurado) {
|
||
Configurado = configurarModuloLoRa(addr, baud, packet, channel, worCycle);
|
||
if (Configurado) {
|
||
std::vector<uint8_t> frame;
|
||
frame.push_back(0xFF);
|
||
frame.push_back(0xFF);
|
||
frame.push_back(channel);
|
||
frame.push_back(0x01);
|
||
enviarDadosSerial(frame.data(), frame.size(), "PING");
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
MostrarLog("Sem resposta do modulo ao requisitar parametros");
|
||
}
|
||
}
|
||
EnviarDadosCAN(MontarMensagemCAN(CanMessagePosicaoDados::Status));
|
||
EnviarDadosCAN(MontarMensagemCAN(CanMessagePosicaoDados::Dados1));
|
||
break;
|
||
}
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
private:
|
||
bool DebugMode = true;
|
||
|
||
void MostrarLog(String mensagem) {
|
||
if (DebugMode) {
|
||
PrintTela("[LORA] " + mensagem);
|
||
}
|
||
}
|
||
|
||
bool recebendo = false;
|
||
int bytesEsperados = -1;
|
||
int addrRemetente = -1;
|
||
int addrDestinatario = -1;
|
||
std::vector<uint8_t> buffer;
|
||
unsigned long ultimoByteRecebido = 0;
|
||
const unsigned long timeoutRecebimentoMs = 800; // por exemplo, 100ms
|
||
const unsigned long tempoEntreEnvios = 200;
|
||
|
||
void reiniciarEstado() {
|
||
recebendo = false;
|
||
PausarTX = false;
|
||
bytesEsperados = -1;
|
||
addrRemetente = -1;
|
||
addrDestinatario = -1;
|
||
buffer.clear();
|
||
}
|
||
|
||
|
||
QueueHandle_t lraQueueRx;
|
||
TaskHandle_t LraTaskRxHandle = NULL;
|
||
static void LraTaskRxWrapper(void *pvParameters) {
|
||
LoRaService* service = static_cast<LoRaService*>(pvParameters);
|
||
service->LraTaskRx(pvParameters);
|
||
}
|
||
|
||
void LraTaskRx(void* pvParameters) {
|
||
LoRaService* service = static_cast<LoRaService*>(pvParameters);
|
||
while (true) {
|
||
if (!Conectado || !Configurado || PausarRX) {
|
||
vTaskDelay(500);
|
||
continue;
|
||
}
|
||
if (recebendo && (millis() - ultimoByteRecebido > timeoutRecebimentoMs)) {
|
||
MostrarLog("Timeout de recebimento. Reiniciando estado.");
|
||
reiniciarEstado();
|
||
}
|
||
while (currentMode == LoRaMode::NORMAL && service->_serialLoRa->available()) {
|
||
uint8_t byteRecebido = service->_serialLoRa->read();
|
||
ultimoByteRecebido = millis(); // <- atualiza o tempo
|
||
MostrarLog("byteRecebido=0x" + String(byteRecebido, HEX));
|
||
|
||
if (!recebendo) {
|
||
if (byteRecebido == CodigosFuncoes::BeginMsg) {
|
||
reiniciarEstado();
|
||
MostrarLog("Iniciou o recebimento dos dados LoRa");
|
||
recebendo = true;
|
||
PausarTX = true;
|
||
}
|
||
}
|
||
else {
|
||
if (byteRecebido == CodigosFuncoes::BeginMsg) {
|
||
MostrarLog("Re-sync: novo BeginMsg detectado durante recepcao");
|
||
reiniciarEstado();
|
||
recebendo = true;
|
||
PausarTX = true;
|
||
continue;
|
||
}
|
||
if (bytesEsperados == -1) {
|
||
bytesEsperados = byteRecebido;
|
||
//MostrarLog("Definindo bytesEsperados = " + String(bytesEsperados));
|
||
}
|
||
else if (addrRemetente == -1) {
|
||
addrRemetente = byteRecebido;
|
||
//MostrarLog("Definindo addrRemetente = " + String(addrRemetente));
|
||
}
|
||
else if (addrDestinatario == -1) {
|
||
addrDestinatario = byteRecebido;
|
||
if (addrDestinatario != parametrosAtual.address && addrDestinatario != 0xFF) {
|
||
MostrarLog("Mensagem para outro ID, descartando ate EndMsg");
|
||
// drenar ate checksum + 0x55 com base em 'bytesEsperados'
|
||
size_t toDrain = (bytesEsperados >= 0 ? bytesEsperados + 2 : 0);
|
||
for (size_t i = 0; i < toDrain; ++i) {
|
||
uint8_t dump;
|
||
if (!service->_serialLoRa->available()) break;
|
||
dump = service->_serialLoRa->read();
|
||
}
|
||
reiniciarEstado();
|
||
continue;
|
||
}
|
||
}
|
||
else {
|
||
buffer.push_back(byteRecebido);
|
||
|
||
if (bytesEsperados >= 0 && buffer.size() == (size_t)bytesEsperados) {
|
||
// tentar ler checksum e EndMsg com timeouts curtinhos
|
||
uint32_t t0 = millis();
|
||
while (service->_serialLoRa->available() < 2 && (millis() - t0) < 200) {
|
||
vTaskDelay(1);
|
||
}
|
||
if (service->_serialLoRa->available() >= 2) {
|
||
uint8_t cks = service->_serialLoRa->read();
|
||
uint8_t endb = service->_serialLoRa->read();
|
||
buffer.push_back(cks);
|
||
buffer.push_back(endb);
|
||
}
|
||
else {
|
||
MostrarLog("Cauda perdida, adicionando manualmente...");
|
||
uint8_t soma = 0;
|
||
for (size_t i = 0; i < buffer.size(); ++i) {
|
||
soma += buffer[i];
|
||
}
|
||
buffer.push_back(soma);
|
||
buffer.push_back(CodigosFuncoes::EndMsg);
|
||
}
|
||
}
|
||
|
||
bool protocoloCompleto = addrRemetente != -1 && addrDestinatario != -1 && buffer.size() == bytesEsperados + 2; // +2 = checksum + end
|
||
MostrarLog("protocoloCompleto = " + String(protocoloCompleto) + ", bufferSize = " + buffer.size() + ", bytesEsperados = " + String(bytesEsperados));
|
||
|
||
if (protocoloCompleto) {
|
||
if (buffer.back() == CodigosFuncoes::EndMsg) {
|
||
buffer.pop_back(); // remove 0x55 (EndMsg)
|
||
|
||
uint8_t checksumRecebido = buffer.back();
|
||
buffer.pop_back(); // remove checksum
|
||
|
||
uint8_t soma = 0;
|
||
for (size_t i = 0; i < buffer.size(); ++i) {
|
||
soma += buffer[i];
|
||
}
|
||
|
||
if ((soma % 256) == checksumRecebido) {
|
||
MostrarLog("Mensagem LoRa recebida de " + String(addrRemetente) + " com checksum válido");
|
||
|
||
if (buffer.size() >= 2) {
|
||
if (xQueueSend(lraQueueRx, &buffer, 0) != pdTRUE) {
|
||
MostrarLog("Fila RX cheia! Mensagem descartada.");
|
||
}
|
||
else {
|
||
MostrarLog("Mensagem adicionada na fila RX");
|
||
}
|
||
}
|
||
} else {
|
||
MostrarLog("Checksum inválido. Descartando.");
|
||
}
|
||
}
|
||
|
||
reiniciarEstado();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
vTaskDelay(1);
|
||
}
|
||
}
|
||
|
||
QueueHandle_t lraQueueTx;
|
||
TaskHandle_t LraTaskTxHandle = NULL;
|
||
static void LraTaskTxWrapper(void *pvParameters) {
|
||
LoRaService* service = static_cast<LoRaService*>(pvParameters);
|
||
service->LraTaskTx(pvParameters);
|
||
}
|
||
|
||
void LraTaskTx(void* pvParameters) {
|
||
LoRaService* service = static_cast<LoRaService*>(pvParameters);
|
||
std::vector<uint8_t> msg;
|
||
while (true) {
|
||
if (!Conectado || !Configurado || PausarTX) {
|
||
vTaskDelay(500);
|
||
continue;
|
||
}
|
||
|
||
if (xQueueReceive(service->lraQueueTx, &msg, portMAX_DELAY) == pdTRUE) {
|
||
service->EnviarDadosLoRa(msg);
|
||
}
|
||
else {
|
||
MostrarLog("Erro ao enviar mensagem lora");
|
||
}
|
||
vTaskDelay(1);
|
||
}
|
||
}
|
||
|
||
TaskHandle_t LraTaskProcessHandle = NULL;
|
||
static void LraTaskProcessWrapper(void *pvParameters) {
|
||
LoRaService* service = static_cast<LoRaService*>(pvParameters);
|
||
service->LraTaskProcess(pvParameters);
|
||
}
|
||
|
||
void LraTaskProcess(void* pvParameters) {
|
||
LoRaService* service = static_cast<LoRaService*>(pvParameters);
|
||
std::vector<uint8_t> msg;
|
||
while (true) {
|
||
if (!Conectado || !Configurado) {
|
||
vTaskDelay(1000);
|
||
continue;
|
||
}
|
||
if (xQueueReceive(service->lraQueueRx, &msg, portMAX_DELAY) == pdTRUE) {
|
||
service->EnviarDadosCAN(msg);
|
||
}
|
||
vTaskDelay(1);
|
||
}
|
||
}
|
||
|
||
|
||
CanService* _canService;
|
||
HardwareSerial* _serialLoRa;
|
||
uint8_t _pinENA;
|
||
uint8_t _pinTXD;
|
||
uint8_t _pinRXD;
|
||
uint8_t _pinM0;
|
||
uint8_t _pinM1;
|
||
uint8_t _pinAUX;
|
||
int _baudRate = 9600;
|
||
LoRaMode currentMode = NORMAL;
|
||
LoRaParametrosModel parametrosAtual;
|
||
int commandTimeout = 800;
|
||
bool PausarTX = false;
|
||
bool PausarRX = false;
|
||
|
||
// Métodos internos
|
||
void pauseLoRaTasks() {
|
||
// pare produtoras de TX e consumidoras de RX antes de mexer no modo
|
||
PausarTX = true; // sua flag para impedir enqueue/envio
|
||
PausarRX = true; // idem recepção
|
||
vTaskDelay(5);
|
||
}
|
||
|
||
void resumeLoRaTasks() {
|
||
PausarTX = false;
|
||
PausarRX = false;
|
||
}
|
||
|
||
// Define o modo do LoRa
|
||
bool setLoRaMode(LoRaMode mode) {
|
||
if (mode == currentMode) return true;
|
||
|
||
pauseLoRaTasks();
|
||
|
||
// Ajusta os pinos M0 e M1 conforme o modo desejado
|
||
switch (mode) {
|
||
case NORMAL:
|
||
digitalWrite(_pinM0, LOW);
|
||
digitalWrite(_pinM1, LOW);
|
||
break;
|
||
case WAKE_UP:
|
||
digitalWrite(_pinM0, HIGH);
|
||
digitalWrite(_pinM1, LOW);
|
||
break;
|
||
case POWER_SAVING:
|
||
digitalWrite(_pinM0, LOW);
|
||
digitalWrite(_pinM1, HIGH);
|
||
break;
|
||
case CONFIG:
|
||
digitalWrite(_pinM0, HIGH);
|
||
digitalWrite(_pinM1, HIGH);
|
||
break;
|
||
}
|
||
|
||
MostrarLog("Modo alterado de " + String(currentMode) + " para " + String(mode));
|
||
|
||
currentMode = mode;
|
||
|
||
vTaskDelay(100); // Aguarda sinalização inicial de troca
|
||
|
||
// ESPERA o AUX ir para HIGH, indicando que a troca completou
|
||
unsigned long timeout = millis() + commandTimeout;
|
||
while (digitalRead(_pinAUX) == LOW) {
|
||
if (millis() > timeout) {
|
||
MostrarLog("Erro: Timeout aguardando AUX após mudança de modo.");
|
||
resumeLoRaTasks();
|
||
return false;
|
||
}
|
||
}
|
||
|
||
vTaskDelay(100); // Manual pede 2ms após AUX ficar HIGH
|
||
resumeLoRaTasks();
|
||
return true;
|
||
}
|
||
|
||
// Verifica se o E220 responde
|
||
bool checkLoRaConnected() {
|
||
MostrarLog("Verificando se o E220 esta conectado...");
|
||
|
||
if (!setLoRaMode(CONFIG)) {
|
||
MostrarLog("Falha ao verificar se o E220 esta conectado, erro ao mudar para CONFIG!");
|
||
return false;
|
||
}
|
||
|
||
bool conectado = false;
|
||
|
||
// Lista de possíveis baud rates (em ordem dos mais prováveis)
|
||
const uint32_t baudRates[] = { 9600 }; // , 19200, 38400, 57600, 115200, 4800, 2400, 1200
|
||
const int totalBauds = sizeof(baudRates) / sizeof(baudRates[0]);
|
||
|
||
int tentativaAtual = -1; // -1 = usar o baudrate já setado primeiro
|
||
|
||
while (!conectado && tentativaAtual < totalBauds) {
|
||
if (tentativaAtual >= 0) {
|
||
// Se estamos tentando outra taxa, mudar a serial:
|
||
_serialLoRa->end();
|
||
_serialLoRa->begin(baudRates[tentativaAtual], SERIAL_8N1, _pinTXD, _pinRXD);
|
||
_baudRate = baudRates[tentativaAtual];
|
||
MostrarLog("Tentando BaudRate: " + String(_baudRate));
|
||
}
|
||
|
||
// Limpa buffers antes de testar
|
||
limparBufferLoRa();
|
||
|
||
uint8_t command[1] = { CodigosFuncoes::CfgRx };
|
||
|
||
enviarDadosSerial(command, 1, "Check Conectado");
|
||
|
||
unsigned long startTime = millis();
|
||
while (millis() - startTime < commandTimeout) {
|
||
if (_serialLoRa->available()) {
|
||
uint8_t dado = _serialLoRa->read();
|
||
if (dado == CodigosFuncoes::CfgRx) {
|
||
conectado = true;
|
||
break;
|
||
}
|
||
}
|
||
vTaskDelay(1);
|
||
}
|
||
|
||
tentativaAtual++;
|
||
}
|
||
|
||
limparBufferLoRa();
|
||
|
||
setLoRaMode(NORMAL);
|
||
|
||
if (conectado) {
|
||
MostrarLog("E220 conectado com baudRate " + String(_baudRate));
|
||
} else {
|
||
MostrarLog("Falha ao conectar no E220!");
|
||
}
|
||
|
||
return conectado;
|
||
}
|
||
|
||
// Método que monta o comando de configuração
|
||
bool configurarModuloLoRa(byte addr, byte baudAndAir, byte packetAndPower, byte channel, byte tranModeAndworCycle) {
|
||
MostrarLog("Configurando modulo...");
|
||
|
||
if (!setLoRaMode(CONFIG)) {
|
||
MostrarLog("Falha ao configurar modulo, erro ao mudar para CONFIG!");
|
||
return false;
|
||
}
|
||
|
||
limparBufferLoRa();
|
||
|
||
uint8_t command[9] = {
|
||
CodigosFuncoes::CfgTx, // Código para configurar
|
||
0x00, // Registro inicial
|
||
0x06, // Quantidade de registros
|
||
0x00, // AddrH
|
||
addr, // addrL
|
||
baudAndAir,
|
||
packetAndPower,
|
||
channel,
|
||
tranModeAndworCycle
|
||
};
|
||
|
||
enviarDadosSerial(command, 9, "Config Modulo");
|
||
|
||
bool sucesso = receberParametrosModulo(command[1], command[2]);
|
||
|
||
setLoRaMode(NORMAL);
|
||
|
||
return sucesso;
|
||
}
|
||
|
||
// Método que requisita a leitura dos parâmetros atuais
|
||
bool requisitarParametrosLoRa() {
|
||
MostrarLog("Requisitando parametros do modulo...");
|
||
|
||
if (!setLoRaMode(CONFIG)) {
|
||
MostrarLog("Falha ao requisitar parametros do modulo, erro ao mudar para CONFIG!");
|
||
return false;
|
||
}
|
||
|
||
limparBufferLoRa();
|
||
|
||
uint8_t command[3] = { CodigosFuncoes::CfgRx, 0x00, 0x0B };
|
||
|
||
enviarDadosSerial(command, 3, "Requisitar Parametros");
|
||
|
||
bool sucesso = receberParametrosModulo(command[1], command[2]);
|
||
|
||
setLoRaMode(NORMAL);
|
||
|
||
return sucesso;
|
||
}
|
||
|
||
bool readByteWithTimeout(uint8_t &out, unsigned long toMs = 500) {
|
||
unsigned long t0 = millis();
|
||
while ((millis() - t0) < toMs) {
|
||
if (_serialLoRa && _serialLoRa->available()) {
|
||
int r = _serialLoRa->read();
|
||
if (r >= 0) {
|
||
out = (uint8_t)r;
|
||
//MostrarLog("0x" + String(out, HEX));
|
||
return true;
|
||
}
|
||
}
|
||
// no ESP32 tem FreeRTOS; se preferir, use vTaskDelay(1);
|
||
vTaskDelay(1);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool receberParametrosModulo(uint8_t _startAddr, uint8_t _dataLen) {
|
||
unsigned long t0 = millis();
|
||
// janela total mais folgada p/ rádio
|
||
while ((millis() - t0) < 1500UL) {
|
||
uint8_t b = 0;
|
||
|
||
if (!readByteWithTimeout(b, 500)) {
|
||
// não chegou nada ainda, segue tentando até estourar 1.5s
|
||
continue;
|
||
}
|
||
|
||
if (b == CodigosFuncoes::CfgRx) {
|
||
uint8_t startAddr = 0, dataLen = 0;
|
||
if (!readByteWithTimeout(startAddr) || !readByteWithTimeout(dataLen)) {
|
||
limparBufferLoRa();
|
||
return false;
|
||
}
|
||
|
||
if (startAddr == _startAddr && dataLen == _dataLen) {
|
||
uint8_t addh=0, addl=0, bAir=0, pwr=0, ch=0, wor=0;
|
||
if (!readByteWithTimeout(addh) || !readByteWithTimeout(addl) ||
|
||
!readByteWithTimeout(bAir) || !readByteWithTimeout(pwr) ||
|
||
!readByteWithTimeout(ch) || !readByteWithTimeout(wor)) {
|
||
limparBufferLoRa();
|
||
return false;
|
||
}
|
||
|
||
parametrosAtual.address = addl; // assumindo ADDL como address
|
||
parametrosAtual.baudAndAirRate = bAir;
|
||
parametrosAtual.packetSizeAndPower = pwr;
|
||
parametrosAtual.channel = ch;
|
||
parametrosAtual.tranModeAndWorCycle = wor;
|
||
|
||
uint8_t n1 = 0;
|
||
uint8_t n2 = 0;
|
||
uint8_t n3 = 0;
|
||
uint8_t n4 = 0;
|
||
uint8_t n5 = 0;
|
||
readByteWithTimeout(n1);
|
||
readByteWithTimeout(n2);
|
||
readByteWithTimeout(n3);
|
||
readByteWithTimeout(n4);
|
||
readByteWithTimeout(n5);
|
||
|
||
MostrarLog(
|
||
"0x" + String(startAddr, HEX) + " " +
|
||
"0x" + String(dataLen, HEX) + " " +
|
||
"0x" + String(addh, HEX) + " " +
|
||
"0x" + String(addl, HEX) + " " +
|
||
"0x" + String(bAir, HEX) + " " +
|
||
"0x" + String(pwr, HEX) + " " +
|
||
"0x" + String(ch, HEX) + " " +
|
||
"0x" + String(wor, HEX) + " " +
|
||
"0x" + String(n1, HEX) + " " +
|
||
"0x" + String(n2, HEX) + " " +
|
||
"0x" + String(n3, HEX) + " " +
|
||
"0x" + String(n4, HEX) + " " +
|
||
"0x" + String(n5, HEX) + " " +
|
||
""
|
||
);
|
||
|
||
return true;
|
||
} else {
|
||
// endereçamento diferente: drena os bytes anunciados pra limpar o stream
|
||
for (int i = 0; i < dataLen; i++) {
|
||
uint8_t dump;
|
||
if (!readByteWithTimeout(dump)) break;
|
||
}
|
||
}
|
||
} else if (b == CodigosFuncoes::WrongMode) {
|
||
MostrarLog("Resposta WrongMode");
|
||
// opcional: logar que o módulo respondeu “modo errado”
|
||
// e talvez sair para re-tentar setLoRaMode(CONFIG)
|
||
}
|
||
// senão, segue laçando e coletando até bater timeout total
|
||
}
|
||
|
||
limparBufferLoRa(); // garantir que não fique lixo
|
||
return false;
|
||
}
|
||
|
||
void enviarDadosSerial(const uint8_t* dados, int tamanho, const String& descricao = "") {
|
||
if (!_serialLoRa) return;
|
||
|
||
while (digitalRead(_pinAUX) == LOW) { }
|
||
|
||
_serialLoRa->write(dados, tamanho);
|
||
//_serialLoRa->flush();
|
||
|
||
uint32_t t0 = millis();
|
||
while (digitalRead(_pinAUX) == HIGH && (millis()-t0) < 50) { }
|
||
while (digitalRead(_pinAUX) == LOW && (millis()-t0) < 200) { }
|
||
delayMicroseconds(1500); // ~1–2 ms entre frames curtos @9600
|
||
|
||
if (DebugMode) {
|
||
String log = "Enviando [" + descricao + "] (" + String(tamanho) + " bytes): ";
|
||
for (size_t i = 0; i < tamanho; i++) {
|
||
log += "0x" + String(dados[i], HEX) + " ";
|
||
}
|
||
MostrarLog(log);
|
||
}
|
||
}
|
||
|
||
// Limpa buffer anterior
|
||
void limparBufferLoRa() {
|
||
while (_serialLoRa->available()) {
|
||
_serialLoRa->read();
|
||
}
|
||
}
|
||
|
||
std::vector<uint8_t> MontarFrameLoRa(uint16_t destino, uint8_t canal, const std::vector<uint8_t>& payloadApp) {
|
||
std::vector<uint8_t> frame;
|
||
|
||
// Cabeçalho fixo LoRa (modo Fixed)
|
||
if (destino == 0xFF) {
|
||
frame.push_back(0xFF);
|
||
frame.push_back(0xFF);
|
||
}
|
||
else {
|
||
frame.push_back((destino >> 8) & 0xFF);
|
||
frame.push_back(destino & 0xFF);
|
||
}
|
||
frame.push_back(static_cast<uint8_t>(canal)); // Canal
|
||
|
||
// Estrutura do payload
|
||
frame.push_back(CodigosFuncoes::BeginMsg); // 0xAA
|
||
frame.push_back(payloadApp.size()); // Tamanho do payload
|
||
frame.push_back(parametrosAtual.address); // rementente
|
||
frame.push_back(destino); // destinatario
|
||
|
||
frame.insert(frame.end(), payloadApp.begin(), payloadApp.end());
|
||
|
||
// Calcula checksum (soma dos dados de payload)
|
||
uint8_t soma = 0;
|
||
for (uint8_t b : payloadApp) {
|
||
soma += b;
|
||
}
|
||
uint8_t checksum = soma % 256;
|
||
frame.push_back(checksum); // Checksum
|
||
|
||
frame.push_back(CodigosFuncoes::EndMsg); // 0x55
|
||
|
||
String log = "Frame LoRa montado: ";
|
||
for (int i = 0; i < frame.size(); i++) {
|
||
log += "0x" + String(frame[i], HEX) + " ";
|
||
}
|
||
MostrarLog(log);
|
||
|
||
return frame;
|
||
}
|
||
|
||
bool EnviarDadosCAN(std::vector<uint8_t> dados) {
|
||
_canService->adicionarMensagemFila(dados);
|
||
return true;
|
||
}
|
||
|
||
bool EnviarDadosLoRa(std::vector<uint8_t> dados) {
|
||
MostrarLog("Enviando dados via LoRa para o endereco 0x" + String(AddressBase, HEX) + " no canal 0x" + String(parametrosAtual.channel, HEX));
|
||
std::vector<uint8_t> dadosLora = MontarFrameLoRa(AddressBase, parametrosAtual.channel, dados);
|
||
enviarDadosSerial(dadosLora.data(), dadosLora.size(), "Dados LoRa");
|
||
//vTaskDelay(pdMS_TO_TICKS(tempoEntreEnvios));
|
||
return true;
|
||
}
|
||
|
||
|
||
std::map<int, uint8_t> CodigosBaudRates = {
|
||
{1200, 0x00},
|
||
{2400, 0x20},
|
||
{4800, 0x40},
|
||
{9600, 0x60},
|
||
{19200, 0x80},
|
||
{38400, 0xA0},
|
||
{57600, 0xC0},
|
||
{115200, 0xE0},
|
||
};
|
||
|
||
template<typename K, typename V>
|
||
K encontrarChavePorValor(const std::map<K, V>& mapa, V valorProcurado, K valorDefault) {
|
||
for (const auto& par : mapa) {
|
||
if (par.second == valorProcurado) {
|
||
return par.first;
|
||
}
|
||
}
|
||
return valorDefault;
|
||
}
|
||
|
||
|
||
};
|
||
|
||
#endif |