ajustes na firmware can e lora async
This commit is contained in:
parent
f82131569d
commit
fcd545a5b5
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -503,6 +503,7 @@
|
|||
<Compile Include="Services\IDispositivosService.cs" />
|
||||
<Compile Include="Services\IFirmwareSensorBase.cs" />
|
||||
<Compile Include="Services\KinectService.cs" />
|
||||
<Compile Include="Services\LoRaEspService.cs" />
|
||||
<Compile Include="Services\LoRaSerializer.cs" />
|
||||
<Compile Include="Services\LoRaService.cs" />
|
||||
<Compile Include="Services\MapasService.cs" />
|
||||
|
|
|
|||
|
|
@ -212,8 +212,8 @@ namespace AgroBase.Forms.IHM
|
|||
return;
|
||||
}
|
||||
|
||||
var Sucesso = await Variaveis.ConexaoLoRa.ConfigurarModuloAsync(Parametros);
|
||||
if (Sucesso != null)
|
||||
(bool Sucesso, LoRaParametrosModel par) = await Variaveis.ConexaoLoRa.ConfigurarModuloAsync(Parametros);
|
||||
if (Sucesso)
|
||||
{
|
||||
CustomDialog.ShowDialog(
|
||||
"Sucesso",
|
||||
|
|
|
|||
|
|
@ -837,7 +837,7 @@ namespace AgroBase.Forms.IHM
|
|||
bool Sucesso = false;
|
||||
while (!Sucesso && Inicio.AddMilliseconds(Timeout) > DateTime.Now)
|
||||
{
|
||||
Dispositivo.Dados.RequisitarDadosModulo(F_Code.ReqTx);
|
||||
Dispositivo.Dados.RequisitarDadosModulo(F_Code.ReqTx, ServoFreio.ID_Num, CanMessagePosicaoDados.Dados1);
|
||||
await Task.Delay(1000);
|
||||
|
||||
Sucesso =
|
||||
|
|
@ -1453,7 +1453,7 @@ namespace AgroBase.Forms.IHM
|
|||
var novoEstado = Ligar ? Estado.Ligado : Estado.Desligado;
|
||||
GeneralJoystick.EnviarComandoSensoriamento(S_Code.sRLE, Rele.ID, novoEstado);
|
||||
await Task.Delay(Delay);
|
||||
Dispositivo.Dados.RequisitarDadosModulo(F_Code.ReqTx);
|
||||
Dispositivo.Dados.RequisitarDadosModulo(F_Code.ReqTx, Rele.ID_Num, CanMessagePosicaoDados.Dados1);
|
||||
await Task.Delay(Delay);
|
||||
}
|
||||
|
||||
|
|
@ -1714,7 +1714,7 @@ namespace AgroBase.Forms.IHM
|
|||
Comportamento.statusLED = Ligar ? StatusLED.Aceso : StatusLED.Apagado;
|
||||
GeneralJoystick.EnviarComandoSensoriamento(S_Code.sLED, Sinaleiro.ID, Comportamento);
|
||||
await Task.Delay(Delay);
|
||||
Dispositivo.Dados.RequisitarDadosModulo(F_Code.ReqTx);
|
||||
Dispositivo.Dados.RequisitarDadosModulo(F_Code.ReqTx, Sinaleiro.ID_Num, CanMessagePosicaoDados.Dados1);
|
||||
await Task.Delay(Delay);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using AgroBase.Models;
|
||||
using AgroBase.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO.Ports;
|
||||
using System.Linq;
|
||||
|
|
@ -11,7 +12,6 @@ namespace AgroBase.Forms
|
|||
{
|
||||
public partial class frmLoraConfig : Form
|
||||
{
|
||||
LoRaService loraService = Variaveis.ConexaoLoRa;
|
||||
LoRaParametrosModel Parametros = null;
|
||||
AsyncTaskTimerModel tmrLeitura;
|
||||
|
||||
|
|
@ -34,14 +34,22 @@ namespace AgroBase.Forms
|
|||
|
||||
private async Task AtualizarParametros()
|
||||
{
|
||||
var (sucesso, par) = await loraService.RequisitarParametrosModuloAsync();
|
||||
(bool sucesso, LoRaParametrosModel par) = (false, null);
|
||||
if (Variaveis.IsAgroMonitor)
|
||||
{
|
||||
(sucesso, par) = await Variaveis.ConexaoLoRa.RequisitarParametrosModuloAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
(sucesso, par) = await Variaveis.LoraService?.RequisitarParametrosModulo();
|
||||
}
|
||||
|
||||
if (!sucesso)
|
||||
{
|
||||
MessageBox.Show("Erro ao requisitar parâmetros! Verifique o modo do módulo LoRa!");
|
||||
}
|
||||
|
||||
Parametros = par;
|
||||
Parametros = par.Clone();
|
||||
|
||||
txtEndereco.Text = ((int)Parametros.address).ToString();
|
||||
txtCanal.Text = ((int)Parametros.channel).ToString();
|
||||
|
|
@ -72,7 +80,7 @@ namespace AgroBase.Forms
|
|||
{
|
||||
if (LoRaService.Iniciado)
|
||||
{
|
||||
var MensagemDisponivel = loraService.MensagemPendente;
|
||||
var MensagemDisponivel = Variaveis.ConexaoLoRa.MensagemPendente;
|
||||
if (MensagemDisponivel != null)
|
||||
{
|
||||
AtualizarConsole(MensagemDisponivel.Remetente, MensagemDisponivel.Destinatario, MensagemDisponivel.Mensagem);
|
||||
|
|
@ -131,8 +139,16 @@ namespace AgroBase.Forms
|
|||
|
||||
private async Task ConfigurarModulo()
|
||||
{
|
||||
var Sucesso = await loraService.ConfigurarModuloAsync(Parametros);
|
||||
if (Sucesso != null)
|
||||
(bool Sucesso, LoRaParametrosModel par) = (false, null);
|
||||
if (Variaveis.IsAgroMonitor)
|
||||
{
|
||||
(Sucesso, par) = await Variaveis.ConexaoLoRa.ConfigurarModuloAsync(Parametros);
|
||||
}
|
||||
else
|
||||
{
|
||||
(Sucesso, par) = await Variaveis.LoraService?.RedefinirParametros(Parametros);
|
||||
}
|
||||
if (Sucesso)
|
||||
{
|
||||
MessageBox.Show("Módulo configurado com sucesso!");
|
||||
}
|
||||
|
|
@ -147,8 +163,50 @@ namespace AgroBase.Forms
|
|||
{
|
||||
if (LoRaService.Iniciado)
|
||||
{
|
||||
LoRaService.MontarMensagemEnvio(txtMensagem.Text, Convert.ToByte(txtEnderecoDestino.Text), LoRaTipoProtocolo.MensagemTexto, true);
|
||||
AtualizarConsole(Parametros.address, Convert.ToByte(txtEnderecoDestino.Text), txtMensagem.Text);
|
||||
bool valido = byte.TryParse(txtEnderecoDestino.Text, out byte destinatario);
|
||||
if (!valido)
|
||||
{
|
||||
MessageBox.Show("Preencha o destinatario corretamente!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Variaveis.IsAgroMonitor)
|
||||
{
|
||||
LoRaService.MontarMensagemEnvio(txtMensagem.Text, destinatario, LoRaTipoProtocolo.MensagemTexto, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// Remove espaços extras e divide por espaço
|
||||
string[] partes = txtMensagem.Text.Trim().Split(' ');
|
||||
|
||||
// Converte cada string para byte
|
||||
List<byte> payload = new List<byte>();
|
||||
foreach (var parte in partes)
|
||||
{
|
||||
if (byte.TryParse(parte, System.Globalization.NumberStyles.HexNumber, null, out byte valor))
|
||||
{
|
||||
payload.Add(valor);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show($"Valor inválido: '{parte}'", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Envia usando seu método
|
||||
bool success = Variaveis.LoraService.EnviarMensagem(CanMessagePosicaoDados.Dados2, payload.ToArray());
|
||||
|
||||
MessageBox.Show("Dados enviados com sucesso!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Erro ao enviar: {ex.Message}", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
AtualizarConsole(Parametros.address, destinatario, txtMensagem.Text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,11 @@ namespace AgroBase.Forms
|
|||
ID = x.ID,
|
||||
Funcoes = x.Funcoes
|
||||
}));
|
||||
Dispositivos.Add(new DispositivoDataModel()
|
||||
{
|
||||
ID = Disp.Dados.loRaService.ID,
|
||||
Funcoes = Disp.Dados.loRaService.Funcoes
|
||||
});
|
||||
break;
|
||||
}
|
||||
case T_Code.Atu:
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@
|
|||
sPOT = 23,
|
||||
sMOD = 24,
|
||||
sIMU = 25,
|
||||
sLRA = 26,
|
||||
sTOD = 27,
|
||||
}
|
||||
|
||||
public enum BotoesJoystick
|
||||
|
|
|
|||
|
|
@ -27,9 +27,7 @@ namespace AgroBase.Models
|
|||
{
|
||||
get
|
||||
{
|
||||
string _baudRate = Convert.ToInt32(baudRate.ToString("X")).ToString("00");
|
||||
string _airRate = Convert.ToInt32(airRate.ToString("X")).ToString("00");
|
||||
return Convert.ToByte(_baudRate[0].ToString() + _airRate[1].ToString(), 16);
|
||||
return (byte)(baudRate | (airRate << 2));
|
||||
}
|
||||
}
|
||||
public byte parity { get; set; }
|
||||
|
|
@ -39,9 +37,7 @@ namespace AgroBase.Models
|
|||
{
|
||||
get
|
||||
{
|
||||
string _packetSize = Convert.ToInt32(packetSize.ToString("X")).ToString("00");
|
||||
string _power = Convert.ToInt32(power.ToString("X")).ToString("00");
|
||||
return Convert.ToByte(_packetSize[0].ToString() + _power[1].ToString(), 16);
|
||||
return (byte)((packetSize & 0b11000000) | (power & 0b00000011));
|
||||
}
|
||||
}
|
||||
public byte tranMode { get; set; }
|
||||
|
|
@ -50,9 +46,7 @@ namespace AgroBase.Models
|
|||
{
|
||||
get
|
||||
{
|
||||
string _tranMode = Convert.ToInt32(tranMode.ToString("X")).ToString("00");
|
||||
string _worCycle = Convert.ToInt32(worCycle.ToString("X")).ToString("00");
|
||||
return Convert.ToByte(_tranMode[0].ToString() + _worCycle[1].ToString(), 16);
|
||||
return (byte)((tranMode & 0b01000000) | (worCycle & 0b00000111));
|
||||
}
|
||||
}
|
||||
public byte channelRssi { get; set; }
|
||||
|
|
@ -61,6 +55,27 @@ namespace AgroBase.Models
|
|||
public byte address { get; set; }
|
||||
public byte channel { get; set; }
|
||||
public byte key { get; set; }
|
||||
|
||||
public LoRaParametrosModel Clone()
|
||||
{
|
||||
return new LoRaParametrosModel()
|
||||
{
|
||||
address = address,
|
||||
airRate = airRate,
|
||||
baudRate = baudRate,
|
||||
channel = channel,
|
||||
channelRssi = channelRssi,
|
||||
key = key,
|
||||
lbt = lbt,
|
||||
packetRssi = packetRssi,
|
||||
packetSize = packetSize,
|
||||
parity = parity,
|
||||
power = power,
|
||||
tranMode = tranMode,
|
||||
worCycle = worCycle,
|
||||
UltimaLeitura = UltimaLeitura
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class LoRaMensagemModel
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ namespace AgroBase.Models.Modules
|
|||
CanService.DefinirPortaCOM(porta);
|
||||
}
|
||||
|
||||
CanService.RegistrarHandler(_EnderecoCAN, _AtuHandler);
|
||||
CanService.RegistrarHandler((byte)((int)_EnderecoCAN + 1), _AtuHandler);
|
||||
|
||||
if (CanService.IsConnected)
|
||||
{
|
||||
|
|
@ -1380,7 +1380,7 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
byte ID_Num = mensagem.DataRx[0];
|
||||
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)mensagem.DataRx[1];
|
||||
byte id = mensagem.Id;
|
||||
byte id = mensagem.IdTx;
|
||||
var data = mensagem.DataRx;
|
||||
|
||||
var item = Variaveis.OperacaoEmAndamento.DispAtu.Dados.DadosLeitura;
|
||||
|
|
|
|||
|
|
@ -406,7 +406,10 @@ namespace AgroBase.Models.Modules
|
|||
ModbusRO,
|
||||
ModbusDE_RE,
|
||||
ServoFreio,
|
||||
Potenciometro
|
||||
Potenciometro,
|
||||
M0,
|
||||
M1,
|
||||
Aux,
|
||||
}
|
||||
|
||||
public static class PinoutUtils
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ namespace AgroBase.Models.Modules
|
|||
public UltrasonicA05Configuracoes ConfiguracaoSensorUltrassom { get; set; }
|
||||
public SensoriamentoMensagemJsonModel DadosLeitura { get; set; } = new SensoriamentoMensagemJsonModel();
|
||||
private SenHandler _SenHandler = new SenHandler();
|
||||
public LoRaEspService loRaService { get; set; } = new LoRaEspService();
|
||||
public LoRaParametrosModel LoRaParametrosBase { get; set; } = new LoRaParametrosModel()
|
||||
{
|
||||
address = 0x01,
|
||||
|
|
@ -700,7 +701,24 @@ namespace AgroBase.Models.Modules
|
|||
},
|
||||
_AnguloControle = 180,
|
||||
},
|
||||
}
|
||||
},
|
||||
loRaService = new LoRaEspService()
|
||||
{
|
||||
ID = "LoRa",
|
||||
ID_Num = Variaveis.ID_Num_sLRA,
|
||||
Componente = S_Code.sLRA,
|
||||
Funcoes = new List<FuncoesPinout>()
|
||||
{
|
||||
FuncoesPinout.M0,
|
||||
FuncoesPinout.M1,
|
||||
FuncoesPinout.Aux,
|
||||
FuncoesPinout.RX,
|
||||
FuncoesPinout.TX
|
||||
},
|
||||
Conectado = false,
|
||||
Configurado = false,
|
||||
Parametros = new LoRaParametrosModel()
|
||||
},
|
||||
};
|
||||
|
||||
for (int i = 0; i < Variaveis.OperacaoEmAndamento.QuantidadeCamerasSolo; i++)
|
||||
|
|
@ -724,6 +742,9 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
x._AnguloControle = x.Incremental ? 0 : 180;
|
||||
});
|
||||
loRaService.Conectado = false;
|
||||
loRaService.Configurado = false;
|
||||
loRaService.Parametros.UltimaLeitura = DateTime.MinValue;
|
||||
|
||||
ReiniciarLeituras(Dados);
|
||||
}
|
||||
|
|
@ -755,11 +776,11 @@ namespace AgroBase.Models.Modules
|
|||
CanService.DefinirPortaCOM(porta);
|
||||
}
|
||||
|
||||
CanService.RegistrarHandler(_EnderecoCAN, _SenHandler);
|
||||
CanService.RegistrarHandler((byte)((int)_EnderecoCAN + 1), _SenHandler);
|
||||
|
||||
if (CanService.IsConnected)
|
||||
{
|
||||
RequisitarDadosModulo(F_Code.ChkTx);
|
||||
RequisitarDadosModulo(F_Code.ChkTx, Variaveis.ID_Num_sMOD);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
|
|
@ -797,9 +818,9 @@ namespace AgroBase.Models.Modules
|
|||
}
|
||||
}
|
||||
|
||||
public void RequisitarDadosModulo(F_Code dado, bool aguardaResposta = true)
|
||||
public void RequisitarDadosModulo(F_Code f_code, int ID_Num, CanMessagePosicaoDados posicao = CanMessagePosicaoDados.Status, bool aguardaResposta = true)
|
||||
{
|
||||
Variaveis.OperacaoEmAndamento.DispSen?.AdicionarMensagemFila(dado, null, aguardaResposta);
|
||||
Variaveis.OperacaoEmAndamento.DispSen?.AdicionarMensagemFila(f_code, new byte[] { (byte)ID_Num, (byte)posicao }, aguardaResposta);
|
||||
}
|
||||
|
||||
public List<byte[]> ProtocoloConfiguracao(bool Conectar)
|
||||
|
|
@ -832,6 +853,12 @@ namespace AgroBase.Models.Modules
|
|||
List<byte[]> sensoresConfig = SensoresConfig(Conectar, configurarTodos);
|
||||
config.AddRange(sensoresConfig);
|
||||
|
||||
// Adicionar configurações do LoRa
|
||||
if (!loRaService.Conectado || !loRaService.Configurado || configurarTodos)
|
||||
{
|
||||
config.AddRange(loRaService.ProtocoloConfiguracao(Pinout, Conectar));
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
|
@ -845,6 +872,7 @@ namespace AgroBase.Models.Modules
|
|||
(byte)(Conectar ? 1 : 0),
|
||||
(byte)(_TaxaAmostragem / 10),
|
||||
(byte)(RequisitarDados ? 1 : 0),
|
||||
LoRaParametrosBase.address
|
||||
};
|
||||
|
||||
var config2 = new List<byte>
|
||||
|
|
@ -981,7 +1009,7 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
if (RequisitarDados)
|
||||
{
|
||||
RequisitarDadosModulo(F_Code.ReqTx, false);
|
||||
RequisitarDadosModulo(F_Code.ReqTx, Variaveis.ID_Num_sTOD, CanMessagePosicaoDados.DadosAll, false);
|
||||
}
|
||||
|
||||
VerificaControleLeitura();
|
||||
|
|
@ -2016,6 +2044,7 @@ namespace AgroBase.Models.Modules
|
|||
private S_Code DetectarComponenteDoID_Num(int ID_Num)
|
||||
{
|
||||
if (ID_Num == Variaveis.ID_Num_sMOD) return S_Code.sMOD;
|
||||
if (ID_Num == Variaveis.ID_Num_sLRA) return S_Code.sLRA;
|
||||
|
||||
var Disp = Variaveis.OperacaoEmAndamento.DispSen.Dados;
|
||||
var sensor = Disp.Sensores.FirstOrDefault(x => x.ID_Num == ID_Num);
|
||||
|
|
@ -2303,7 +2332,7 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
byte ID_Num = mensagem.DataRx[0];
|
||||
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)mensagem.DataRx[1];
|
||||
byte id = mensagem.Id;
|
||||
byte id = mensagem.IdTx;
|
||||
var data = mensagem.DataRx;
|
||||
|
||||
var item = Variaveis.OperacaoEmAndamento.DispSen.Dados.DadosLeitura;
|
||||
|
|
@ -2316,22 +2345,38 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
case CanMessagePosicaoDados.Status:
|
||||
{
|
||||
if (componente == S_Code.sMOD)
|
||||
switch (componente)
|
||||
{
|
||||
T_Code tipoModulo = (T_Code)data[2];
|
||||
bool conectado = data[3] == 1;
|
||||
int versao = data[4];
|
||||
case S_Code.sMOD:
|
||||
{
|
||||
T_Code tipoModulo = (T_Code)data[2];
|
||||
bool conectado = data[3] == 1;
|
||||
int versao = data[4];
|
||||
|
||||
Console.WriteLine($"[SEN] Resposta {ID_Num} recebida. Tipo={tipoModulo}, Conectado={conectado}, Versão={versao}");
|
||||
Console.WriteLine($"[SEN] Resposta {ID_Num} recebida. Tipo={tipoModulo}, Conectado={conectado}, Versão={versao}");
|
||||
|
||||
item.Conectado = conectado;
|
||||
item.Dispositivo = tipoModulo;
|
||||
item.Versao = versao;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool Iniciado = data[2] == 1;
|
||||
DefinirOuCriar(ID_Num, posicao, componente, new List<dynamic>() { Iniciado });
|
||||
item.Conectado = conectado;
|
||||
item.Dispositivo = tipoModulo;
|
||||
item.Versao = versao;
|
||||
break;
|
||||
}
|
||||
case S_Code.sLRA:
|
||||
{
|
||||
bool Conectado = data[2] == 1;
|
||||
bool Configurado = data[3] == 1;
|
||||
Console.WriteLine($"[LRA] Resposta {ID_Num} recebida. Conectado={Conectado}, Configurado={Configurado}");
|
||||
var loraService = Variaveis.OperacaoEmAndamento.DispSen.Dados.loRaService;
|
||||
loraService.Conectado = Conectado;
|
||||
loraService.Configurado = Configurado;
|
||||
loraService.Parametros.UltimaLeitura = DateTime.Now;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
bool Iniciado = data[2] == 1;
|
||||
DefinirOuCriar(ID_Num, posicao, componente, new List<dynamic>() { Iniciado });
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -2381,6 +2426,32 @@ namespace AgroBase.Models.Modules
|
|||
DefinirOuCriar(ID_Num, posicao, componente, new List<dynamic>() { roll, pitch, yaw });
|
||||
break;
|
||||
}
|
||||
case S_Code.sLRA:
|
||||
{
|
||||
byte address = data[2];
|
||||
byte baudAndAirRate = data[3];
|
||||
byte baud = (byte)(baudAndAirRate & 0b11100000);
|
||||
byte airRate = (byte)((baudAndAirRate & 0b00011100) >> 2);
|
||||
byte packetSizeAndPower = data[4];
|
||||
byte packetSize = (byte)((packetSizeAndPower & 0b11000000));
|
||||
byte power = (byte)(packetSizeAndPower & 0b00000011);
|
||||
byte channel = data[5];
|
||||
byte tranModeAndWorCycle = data[6];
|
||||
byte tranMode = (byte)(tranModeAndWorCycle & 0b01000000);
|
||||
byte worCycle = (byte)(tranModeAndWorCycle & 0b00000111);
|
||||
var loraService = Variaveis.OperacaoEmAndamento.DispSen.Dados.loRaService;
|
||||
Console.WriteLine($"[LRA] Resposta {ID_Num} recebida. Address={address.ToString("X")}, BaudRate={baud.ToString("X")}, AirRate={airRate.ToString("X")}, PacketSize={packetSize.ToString("X")}, Power={power.ToString("X")}, Channel={channel.ToString("X")}, transMode={tranMode.ToString("X")}, WorCycle={worCycle.ToString("X")}");
|
||||
loraService.Parametros.address = address;
|
||||
loraService.Parametros.baudRate = baud;
|
||||
loraService.Parametros.airRate = airRate;
|
||||
loraService.Parametros.packetSize = packetSize;
|
||||
loraService.Parametros.power = power;
|
||||
loraService.Parametros.channel = channel;
|
||||
loraService.Parametros.worCycle = worCycle;
|
||||
loraService.Parametros.tranMode = tranMode;
|
||||
loraService.Parametros.UltimaLeitura = DateTime.Now;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
|
|
@ -2410,7 +2481,11 @@ namespace AgroBase.Models.Modules
|
|||
DefinirOuCriar(ID_Num, posicao, componente, new List<dynamic>() { temperatura, pressao, altitude });
|
||||
break;
|
||||
}
|
||||
|
||||
case S_Code.sLRA:
|
||||
{
|
||||
Console.WriteLine("Dados LORA recebidos da base: " + FuncoesGlobais.ConverterComandoBytesParaTexto(data));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Console.WriteLine($"[SEN] Resposta de {ID_Num} na posicao {posicao.ToString()} recebida. Parametros nao mapeados: {FuncoesGlobais.ConverterComandoBytesParaTexto(data)}");
|
||||
|
|
|
|||
|
|
@ -55,6 +55,13 @@ namespace AgroBase.Models
|
|||
public static OperacaoModel OperacaoEmAndamento { get; set; } = new OperacaoModel(ModoOperacao.Manual);
|
||||
public static MqttService MqttService { get; set; } = new MqttService("localhost", 1883);
|
||||
public static LoRaService ConexaoLoRa { get; set; } = new LoRaService();
|
||||
public static LoRaEspService LoraService
|
||||
{
|
||||
get
|
||||
{
|
||||
return OperacaoEmAndamento.DispSen?.Dados?.loRaService;
|
||||
}
|
||||
}
|
||||
public static string FaixaIPwifi { get; set; } = "192.168.100";
|
||||
public static string FaixaIPethernet { get; set; } = "192.168.105";
|
||||
public static string IP_Host { get; set; } = FaixaIPwifi + ".105";
|
||||
|
|
@ -68,7 +75,9 @@ namespace AgroBase.Models
|
|||
}
|
||||
}
|
||||
|
||||
public static byte ID_Num_sMOD { get; } = 0x00;
|
||||
public static byte ID_Num_sMOD { get; } = 0;
|
||||
public static byte ID_Num_sTOD { get; } = 250;
|
||||
public static byte ID_Num_sLRA { get; } = 251;
|
||||
}
|
||||
|
||||
public static class VariaveisPortas
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ namespace AgroBase.Services
|
|||
{
|
||||
public DateTime Momento { get; set; }
|
||||
public T_Code Dispositivo { get; set; }
|
||||
public byte Id { get; set; }
|
||||
public byte IdTx { get; set; }
|
||||
public byte IdRx { get; set; }
|
||||
public byte funcCodeTx { get; set; }
|
||||
public byte funcCodeRx { get; set; }
|
||||
public byte[] DataTx { get; set; }
|
||||
|
|
@ -30,11 +31,13 @@ namespace AgroBase.Services
|
|||
public enum CanMessagePosicaoDados
|
||||
{
|
||||
Status = 0,
|
||||
Dados1 = 1,
|
||||
Dados2 = 2,
|
||||
Dados3 = 3,
|
||||
Dados4 = 4,
|
||||
Dados5 = 5,
|
||||
DadosAll = 1,
|
||||
Dados1 = 2,
|
||||
Dados2 = 3,
|
||||
Dados3 = 4,
|
||||
Dados4 = 5,
|
||||
Dados5 = 6,
|
||||
Dados6 = 7,
|
||||
Config1 = 51,
|
||||
Config2 = 52,
|
||||
Config3 = 53,
|
||||
|
|
@ -171,19 +174,19 @@ namespace AgroBase.Services
|
|||
|
||||
lock (Mensagens)
|
||||
{
|
||||
mensagem = Mensagens.Where(x => x.Id == id).OrderBy(x => x.EnviadoEm).FirstOrDefault(x => x.Enviado && !x.Respondido && x.funcCodeRx == funcCodeRx);
|
||||
mensagem = Mensagens.Where(x => x.IdRx == id).OrderBy(x => x.EnviadoEm).FirstOrDefault(x => x.Enviado && !x.Respondido && x.funcCodeRx == funcCodeRx);
|
||||
}
|
||||
|
||||
if (mensagem == null)
|
||||
{
|
||||
mensagem = new CanMessage { Id = (byte)id, DataRx = data };
|
||||
mensagem = new CanMessage { IdTx = (byte)id, IdRx = (byte)id, DataRx = data };
|
||||
}
|
||||
|
||||
mensagem.DataRx = data;
|
||||
mensagem.Respondido = true;
|
||||
mensagem.RespondidoEm = DateTime.Now;
|
||||
|
||||
if (_roteadores.TryGetValue(mensagem.Id, out var handler))
|
||||
if (_roteadores.TryGetValue(mensagem.IdRx, out var handler))
|
||||
{
|
||||
handler.ProcessarMensagem(mensagem);
|
||||
}
|
||||
|
|
@ -198,7 +201,7 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
|
||||
public static void AdicionarMensagemNaFila(T_Code Dispositivo, byte id, byte funcCode, byte[] payload = null, bool get = true, byte ID_Num = 0x00)
|
||||
public static void AdicionarMensagemNaFila(T_Code Dispositivo, byte idTx, byte idRx, byte funcCode, byte[] payload = null, bool get = true, byte ID_Num = 0x00)
|
||||
{
|
||||
if (_PortaCAN?.IsOpen != true) return;
|
||||
|
||||
|
|
@ -209,7 +212,7 @@ namespace AgroBase.Services
|
|||
|
||||
if (Dispositivo == T_Code.Mks)
|
||||
{
|
||||
byte crc = CalcularChecksum(id, dados.ToArray());
|
||||
byte crc = CalcularChecksum(idTx, dados.ToArray());
|
||||
dados.Add(crc);
|
||||
}
|
||||
|
||||
|
|
@ -218,7 +221,8 @@ namespace AgroBase.Services
|
|||
Momento = DateTime.Now,
|
||||
Dispositivo = Dispositivo,
|
||||
funcCodeTx = funcCode,
|
||||
Id = id,
|
||||
IdTx = idTx,
|
||||
IdRx = idRx,
|
||||
funcCodeRx = ID_Num,
|
||||
DataTx = dados.ToArray(),
|
||||
ComResposta = get
|
||||
|
|
@ -230,7 +234,7 @@ namespace AgroBase.Services
|
|||
LimparMensagensAntigas();
|
||||
|
||||
var existente = Mensagens.FirstOrDefault(x =>
|
||||
x.Id == novaMensagem.Id &&
|
||||
x.IdTx == novaMensagem.IdTx &&
|
||||
x.funcCodeTx == novaMensagem.funcCodeTx &&
|
||||
x.funcCodeRx == novaMensagem.funcCodeRx &&
|
||||
x.ComResposta == novaMensagem.ComResposta &&
|
||||
|
|
@ -256,7 +260,7 @@ namespace AgroBase.Services
|
|||
{
|
||||
var idsDisponiveis = Mensagens
|
||||
.Where(x => !x.Enviado)
|
||||
.Select(x => x.Id)
|
||||
.Select(x => x.IdTx)
|
||||
.Distinct()
|
||||
.OrderBy(id => id)
|
||||
.ToList();
|
||||
|
|
@ -282,7 +286,7 @@ namespace AgroBase.Services
|
|||
}
|
||||
|
||||
var msg = Mensagens
|
||||
.Where(x => !x.Enviado && x.Id == proximoId)
|
||||
.Where(x => !x.Enviado && x.IdTx == proximoId)
|
||||
.OrderBy(x => x.Momento)
|
||||
.FirstOrDefault();
|
||||
|
||||
|
|
@ -300,7 +304,7 @@ namespace AgroBase.Services
|
|||
{
|
||||
string frame = CriarFrame(msg);
|
||||
|
||||
if (_ultimoIdEnviado.HasValue && msg.Id == _ultimoIdEnviado.Value)
|
||||
if (_ultimoIdEnviado.HasValue && msg.IdTx == _ultimoIdEnviado.Value)
|
||||
{
|
||||
await Task.Delay(5);
|
||||
}
|
||||
|
|
@ -311,14 +315,14 @@ namespace AgroBase.Services
|
|||
|
||||
msg.Enviado = true;
|
||||
msg.EnviadoEm = DateTime.Now;
|
||||
_ultimoIdEnviado = msg.Id;
|
||||
_ultimoIdEnviado = msg.IdTx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string CriarFrame(CanMessage msg)
|
||||
{
|
||||
string frame = $"t{msg.Id:X3}{msg.DataTx.Length}";
|
||||
string frame = $"t{msg.IdTx:X3}{msg.DataTx.Length}";
|
||||
foreach (byte b in msg.DataTx)
|
||||
frame += b.ToString("X2");
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ namespace AgroBase.Comum
|
|||
{
|
||||
if ((int)funcao % 2 == 1)
|
||||
{
|
||||
CanService.AdicionarMensagemNaFila(Dispositivo, _EnderecoCAN, (byte)funcao, dados, get, ID_Num);
|
||||
CanService.AdicionarMensagemNaFila(Dispositivo, _EnderecoCAN, (byte)((int)_EnderecoCAN + 1), (byte)funcao, dados, get, ID_Num);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,205 @@
|
|||
using AgroBase.Models;
|
||||
using AgroBase.Models.Modules;
|
||||
using static AgroBase.Models.Enums;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AgroBase.Services
|
||||
{
|
||||
public class LoRaEspService
|
||||
{
|
||||
public string ID { get; set; }
|
||||
public int ID_Num { get; set; }
|
||||
public S_Code Componente { get; set; }
|
||||
public bool Conectado { get; set; }
|
||||
public bool Configurado { get; set; }
|
||||
public bool Iniciado
|
||||
{
|
||||
get
|
||||
{
|
||||
return Conectado && Configurado;
|
||||
}
|
||||
}
|
||||
public LoRaParametrosModel Parametros { get; set; }
|
||||
public List<FuncoesPinout> Funcoes { get; set; }
|
||||
|
||||
public List<byte[]> ProtocoloConfiguracao(PinoutDataModel Pinout, bool conectar)
|
||||
{
|
||||
List<byte> config1 = new List<byte>
|
||||
{
|
||||
(byte)ID_Num,
|
||||
(byte)CanMessagePosicaoDados.Config1,
|
||||
};
|
||||
|
||||
var _pinout = Pinout.Clone();
|
||||
_pinout.Pinos = _pinout.Pinos.Where(x => x.ComponenteID == ID).ToList();
|
||||
if (_pinout.Pinos.Count > 0)
|
||||
{
|
||||
byte[] pinos = PinoutModel.PinoProtocolo(_pinout, Funcoes, false);
|
||||
config1.AddRange(pinos);
|
||||
}
|
||||
|
||||
List<byte> config2 = new List<byte>
|
||||
{
|
||||
(byte)ID_Num,
|
||||
(byte)CanMessagePosicaoDados.Config2,
|
||||
Parametros.address,
|
||||
Parametros.baudAndAirRate,
|
||||
Parametros.packetSizeAndPower,
|
||||
Parametros.channel,
|
||||
Parametros.tranModeAndWorCycle
|
||||
};
|
||||
|
||||
return new List<byte[]>() { config1.ToArray(), config2.ToArray() };
|
||||
}
|
||||
|
||||
public async Task<(bool, LoRaParametrosModel)> RequisitarParametrosModulo()
|
||||
{
|
||||
DateTime inicio = DateTime.Now;
|
||||
bool sucesso = false;
|
||||
Variaveis.OperacaoEmAndamento.DispSen?.Dados?.RequisitarDadosModulo(F_Code.CmdTx, Variaveis.ID_Num_sLRA, CanMessagePosicaoDados.Command1);
|
||||
while (!sucesso && inicio.AddMilliseconds(2000) > DateTime.Now)
|
||||
{
|
||||
await Task.Delay(10);
|
||||
sucesso = Parametros.UltimaLeitura < inicio;
|
||||
}
|
||||
return (sucesso, Parametros);
|
||||
}
|
||||
|
||||
public async Task<(bool, LoRaParametrosModel)> RedefinirParametros(LoRaParametrosModel parametros)
|
||||
{
|
||||
Parametros = parametros.Clone();
|
||||
Configurado = false;
|
||||
|
||||
DateTime inicio = DateTime.Now;
|
||||
Variaveis.OperacaoEmAndamento.DispSen?.EnviarProtocolosConfiguracao(true);
|
||||
while (!Configurado && inicio.AddMilliseconds(2000) > DateTime.Now)
|
||||
{
|
||||
await Task.Delay(10);
|
||||
}
|
||||
return (Configurado, Parametros);
|
||||
}
|
||||
|
||||
public (bool, byte[]) EnviarDadosLoRa(DadosLoRaParse TipoDado)
|
||||
{
|
||||
(CanMessagePosicaoDados posicao, byte[] dados) = MontarMensagemLoRa(TipoDado);
|
||||
|
||||
bool sucesso = EnviarMensagem(posicao, dados);
|
||||
|
||||
return (sucesso, dados);
|
||||
}
|
||||
|
||||
|
||||
public bool EnviarMensagem(CanMessagePosicaoDados posicao, byte[] payload)
|
||||
{
|
||||
if (payload == null) return false;
|
||||
|
||||
if (payload.Length > 5) return false; // Payload muito grande para o nosso protocolo CAN-Style LoRa
|
||||
|
||||
List<byte> dados = new List<byte>
|
||||
{
|
||||
(byte)ID_Num,
|
||||
(byte)posicao
|
||||
};
|
||||
dados.AddRange(payload);
|
||||
|
||||
Variaveis.OperacaoEmAndamento.DispSen?.AdicionarMensagemFila(F_Code.CmdTx, dados.ToArray(), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private (CanMessagePosicaoDados, byte[]) MontarMensagemLoRa(DadosLoRaParse TipoDado)
|
||||
{
|
||||
List<byte> dados = new List<byte>();
|
||||
|
||||
switch (TipoDado)
|
||||
{
|
||||
case DadosLoRaParse.GpsQualidade:
|
||||
{
|
||||
var Gps = Variaveis.OperacaoEmAndamento.Sensoriamento.Gps;
|
||||
dados.Add((byte)(Gps.Inicializado ? 1 : 0)); // 1 byte
|
||||
dados.Add((byte)Gps.QualidadeFix); // 1 byte
|
||||
dados.Add((byte)Gps.NumeroSatelites); // 1 byte
|
||||
dados.AddRange(CompactarDecimal(Gps.PrecisaoCm)); // 2 bytes
|
||||
break;
|
||||
}
|
||||
case DadosLoRaParse.GpsLatitude:
|
||||
{
|
||||
var Gps = Variaveis.OperacaoEmAndamento.Sensoriamento.Gps;
|
||||
dados.AddRange(CompactarCoordenada(Gps.Latitude)); // 4 bytes
|
||||
break;
|
||||
}
|
||||
case DadosLoRaParse.GpsLongitude:
|
||||
{
|
||||
var Gps = Variaveis.OperacaoEmAndamento.Sensoriamento.Gps;
|
||||
dados.AddRange(CompactarCoordenada(Gps.Longitude)); // 4 bytes
|
||||
break;
|
||||
}
|
||||
case DadosLoRaParse.GpsGerais:
|
||||
{
|
||||
var Gps = Variaveis.OperacaoEmAndamento.Sensoriamento.Gps;
|
||||
dados.Add((byte)Gps.VariacaoMagnetica); // 1 byte
|
||||
dados.AddRange(CompactarDecimal(Gps.Altitude)); // 2 bytes
|
||||
dados.AddRange(CompactarDecimal(Gps.Velocidade)); // 2 bytes
|
||||
break;
|
||||
}
|
||||
case DadosLoRaParse.DadosControle:
|
||||
{
|
||||
var Controle = Variaveis.OperacaoEmAndamento.Sensoriamento.Controle;
|
||||
dados.Add((byte)Controle.Direcao); // 1 byte
|
||||
dados.Add((byte)Controle.PercentualVelocidadeSP); // 1 byte
|
||||
dados.Add((byte)Controle.TipoMovimento); // 1 byte
|
||||
dados.Add((byte)Controle.Angulo); // 1 byte
|
||||
dados.Add((byte)Controle.VelocidadeMP); // 1 byte
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ((CanMessagePosicaoDados)TipoDado, dados.ToArray());
|
||||
}
|
||||
|
||||
private byte[] CompactarCoordenada(double valor)
|
||||
{
|
||||
int inteiro = (int)(valor * 1e7); // Multiplica para preservar precisão
|
||||
byte[] bytes = BitConverter.GetBytes(inteiro); // 4 bytes (little endian)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private double DescompactarCoordenada(byte[] payload)
|
||||
{
|
||||
int inteiro = BitConverter.ToInt32(payload, 0); // 4 primeiros bytes
|
||||
return inteiro / 1e7; // Voltar para graus decimais
|
||||
}
|
||||
|
||||
private byte[] CompactarDecimal(double precisaoCm)
|
||||
{
|
||||
ushort valor = (ushort)Math.Round(precisaoCm); // Arredonda para inteiro
|
||||
return BitConverter.GetBytes(valor); // 2 bytes (little endian)
|
||||
}
|
||||
|
||||
private double DescompactarDecimal(byte[] payload, int offset)
|
||||
{
|
||||
ushort valor = BitConverter.ToUInt16(payload, offset); // Lê 2 bytes
|
||||
return (double)valor; // Volta para double
|
||||
}
|
||||
|
||||
public enum DadosLoRaParse
|
||||
{
|
||||
GpsQualidade = (int)CanMessagePosicaoDados.Dados2,
|
||||
GpsLatitude = (int)CanMessagePosicaoDados.Dados3,
|
||||
GpsLongitude = (int)CanMessagePosicaoDados.Dados4,
|
||||
GpsGerais = (int)CanMessagePosicaoDados.Dados5,
|
||||
DadosControle = (int)CanMessagePosicaoDados.Dados6,
|
||||
}
|
||||
|
||||
public enum LoRaCaracteresEspeciais
|
||||
{
|
||||
BeginMsg = 0xAA,
|
||||
BreakLine = 0xBB,
|
||||
EndMsg = 0x55
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using AgroBase.Models;
|
||||
using AgroMonitor;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
|
|
@ -8,6 +9,7 @@ using System.Text;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using static AgroBase.Models.Enums;
|
||||
using static AgroBase.Services.LoRaEspService;
|
||||
|
||||
namespace AgroBase.Services
|
||||
{
|
||||
|
|
@ -18,19 +20,26 @@ namespace AgroBase.Services
|
|||
{
|
||||
get
|
||||
{
|
||||
if (_PortaLoRa != null && !_PortaLoRa.IsOpen)
|
||||
if (!Variaveis.IsAgroMonitor)
|
||||
{
|
||||
try
|
||||
{
|
||||
_PortaLoRa.Open();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
_PortaLoRa = null;
|
||||
}
|
||||
return Variaveis.OperacaoEmAndamento.DispSen?.Dados?.loRaService?.Iniciado ?? false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_PortaLoRa != null && !_PortaLoRa.IsOpen)
|
||||
{
|
||||
try
|
||||
{
|
||||
_PortaLoRa.Open();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
_PortaLoRa = null;
|
||||
}
|
||||
}
|
||||
return _PortaLoRa != null && _PortaLoRa.IsOpen;
|
||||
}
|
||||
return _PortaLoRa != null && _PortaLoRa.IsOpen;
|
||||
}
|
||||
}
|
||||
private static Dictionary<DateTime, List<byte>> bufferRecebido = new Dictionary<DateTime, List<byte>>();
|
||||
|
|
@ -406,17 +415,39 @@ namespace AgroBase.Services
|
|||
int numBytes = resposta.Buffer[2];
|
||||
if (numBytes == 6 || numBytes == 11) // Set ou Get nos parametros
|
||||
{
|
||||
modelo.address = resposta.Buffer[4];
|
||||
string baudAndAirRates = Convert.ToInt32(resposta.Buffer[5].ToString("X")).ToString("00");
|
||||
modelo.baudRate = Convert.ToByte(baudAndAirRates[0] + "0", 16);
|
||||
modelo.airRate = Convert.ToByte("0" + baudAndAirRates[1], 16);
|
||||
string packetSizeAndPower = Convert.ToInt32(resposta.Buffer[6].ToString("X")).ToString("00");
|
||||
modelo.packetSize = Convert.ToByte(packetSizeAndPower[0] + "0", 16);
|
||||
modelo.power = Convert.ToByte("0" + packetSizeAndPower[1], 16);
|
||||
modelo.channel = resposta.Buffer[7];
|
||||
string tranModeAndWorCycle = Convert.ToInt32(resposta.Buffer[8].ToString("X")).ToString("00");
|
||||
modelo.tranMode = Convert.ToByte(tranModeAndWorCycle[0] + "0", 16);
|
||||
modelo.worCycle = Convert.ToByte("0" + tranModeAndWorCycle[1], 16);
|
||||
var data = resposta.Buffer;
|
||||
byte address = data[4];
|
||||
byte baudAndAirRate = data[5];
|
||||
byte baud = (byte)(baudAndAirRate & 0b11100000);
|
||||
byte airRate = (byte)((baudAndAirRate & 0b00011100) >> 2);
|
||||
byte packetSizeAndPower = data[6];
|
||||
byte packetSize = (byte)((packetSizeAndPower & 0b11000000));
|
||||
byte power = (byte)(packetSizeAndPower & 0b00000011);
|
||||
byte channel = data[7];
|
||||
byte tranModeAndWorCycle = data[8];
|
||||
byte tranMode = (byte)(tranModeAndWorCycle & 0b01000000);
|
||||
byte worCycle = (byte)(tranModeAndWorCycle & 0b00000111);
|
||||
|
||||
/*modelo.address = resposta.Buffer[4];
|
||||
string baudAndAirRates = Convert.ToInt32(resposta.Buffer[5].ToString("X")).ToString("00");
|
||||
modelo.baudRate = Convert.ToByte(baudAndAirRates[0] + "0", 16);
|
||||
modelo.airRate = Convert.ToByte("0" + baudAndAirRates[1], 16);
|
||||
string packetSizeAndPower = Convert.ToInt32(resposta.Buffer[6].ToString("X")).ToString("00");
|
||||
modelo.packetSize = Convert.ToByte(packetSizeAndPower[0] + "0", 16);
|
||||
modelo.power = Convert.ToByte("0" + packetSizeAndPower[1], 16);
|
||||
modelo.channel = resposta.Buffer[7];
|
||||
string tranModeAndWorCycle = Convert.ToInt32(resposta.Buffer[8].ToString("X")).ToString("00");
|
||||
modelo.tranMode = Convert.ToByte(tranModeAndWorCycle[0] + "0", 16);
|
||||
modelo.worCycle = Convert.ToByte("0" + tranModeAndWorCycle[1], 16);*/
|
||||
|
||||
modelo.address = address;
|
||||
modelo.baudRate = baud;
|
||||
modelo.airRate = airRate;
|
||||
modelo.packetSize = packetSize;
|
||||
modelo.power = power;
|
||||
modelo.channel = channel;
|
||||
modelo.tranMode = tranMode;
|
||||
modelo.worCycle = worCycle;
|
||||
|
||||
return modelo;
|
||||
}
|
||||
|
|
@ -551,7 +582,7 @@ namespace AgroBase.Services
|
|||
return true;
|
||||
}
|
||||
|
||||
public async Task<LoRaParametrosModel> ConfigurarModuloAsync(LoRaParametrosModel Parametros)
|
||||
public async Task<(bool, LoRaParametrosModel)> ConfigurarModuloAsync(LoRaParametrosModel Parametros)
|
||||
{
|
||||
parametrosModel = Parametros;
|
||||
|
||||
|
|
@ -568,7 +599,7 @@ namespace AgroBase.Services
|
|||
// Verifica se a última leitura foi atualizada nos últimos 3 segundos
|
||||
if (parametrosModel.UltimaLeitura >= DateTime.Now.AddSeconds(-3))
|
||||
{
|
||||
return parametrosModel;
|
||||
return (true, parametrosModel);
|
||||
}
|
||||
|
||||
// Aguarda de forma assíncrona por um curto período de tempo
|
||||
|
|
@ -576,7 +607,7 @@ namespace AgroBase.Services
|
|||
}
|
||||
|
||||
// Se o timeout for atingido, retorna o modelo com os dados disponíveis até o momento
|
||||
return null;
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -655,14 +686,13 @@ namespace AgroBase.Services
|
|||
{
|
||||
List<byte[]> Partes = new List<byte[]>();
|
||||
|
||||
byte[] Cabecalho = new byte[] { parametrosModel.address, targetAddress, (byte)tipo };
|
||||
byte addressHigh = 0x00;
|
||||
byte channel = parametrosModel.channel;
|
||||
byte remetente = parametrosModel.address;
|
||||
|
||||
int maxPacketSize = CodigosPacketSizes.FirstOrDefault(x => x.Value == parametrosModel.packetSize).Key - 10;
|
||||
int prefixLength = 5; // Tamanho fixo do cabeçalho (ajuste conforme necessário)
|
||||
int maxDataLength = maxPacketSize - prefixLength - 1; // Subtrai tamanho do cabeçalho e um byte para o sufixo
|
||||
|
||||
byte addressHigh = 0x00; // Endereço alto (pode ajustar conforme necessário)
|
||||
byte channel = parametrosModel.channel; // Canal do LoRa (ajuste conforme necessário)
|
||||
int overhead = 1 + 1 + 1 + 1 + 1 + 1; // Begin + Size + Remetente + Dest + Checksum + End
|
||||
int maxDataLength = maxPacketSize - overhead;
|
||||
|
||||
for (int i = 0; i < bytes.Length; i += maxDataLength)
|
||||
{
|
||||
|
|
@ -670,23 +700,25 @@ namespace AgroBase.Services
|
|||
byte[] dataFragment = new byte[length];
|
||||
Array.Copy(bytes, i, dataFragment, 0, length);
|
||||
|
||||
// Montar cabeçalho
|
||||
|
||||
string Prefixo = (i == 0 ? SerialService.BeginLine : SerialService.BreakLine);
|
||||
string Sulfixo = (i + length >= bytes.Length ? SerialService.EndLine : SerialService.BreakLine) + "\n";
|
||||
byte checksum = (byte)(dataFragment.Sum(b => b) % 256);
|
||||
|
||||
byte[] prefixoBytes = Encoding.UTF8.GetBytes(Prefixo);
|
||||
byte[] sulfixoBytes = Encoding.UTF8.GetBytes(Sulfixo);
|
||||
byte prefix = (byte)LoRaCaracteresEspeciais.BeginMsg;
|
||||
byte suffix = (byte)LoRaCaracteresEspeciais.EndMsg;
|
||||
byte dataSize = (byte)dataFragment.Length;
|
||||
|
||||
byte[] mensagem = new byte[prefixoBytes.Length + Cabecalho.Length + dataFragment.Length + sulfixoBytes.Length + 3];
|
||||
byte[] mensagem = new byte[3 + 1 + 1 + 1 + 1 + length + 1 + 1]; // 3 físicos + protocolo
|
||||
mensagem[0] = addressHigh;
|
||||
mensagem[1] = targetAddress;
|
||||
mensagem[2] = channel;
|
||||
|
||||
Array.Copy(prefixoBytes, 0, mensagem, 3, prefixoBytes.Length);
|
||||
Array.Copy(Cabecalho, 0, mensagem, 3 + prefixoBytes.Length, Cabecalho.Length);
|
||||
Array.Copy(dataFragment, 0, mensagem, 3 + prefixoBytes.Length + Cabecalho.Length, dataFragment.Length);
|
||||
Array.Copy(sulfixoBytes, 0, mensagem, 3 + prefixoBytes.Length + Cabecalho.Length + dataFragment.Length, sulfixoBytes.Length);
|
||||
mensagem[3] = prefix;
|
||||
mensagem[4] = dataSize;
|
||||
mensagem[5] = remetente;
|
||||
mensagem[6] = targetAddress;
|
||||
|
||||
Array.Copy(dataFragment, 0, mensagem, 7, length);
|
||||
mensagem[7 + length] = checksum;
|
||||
mensagem[8 + length] = suffix;
|
||||
|
||||
Partes.Add(mensagem);
|
||||
}
|
||||
|
|
@ -703,6 +735,8 @@ namespace AgroBase.Services
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static async Task tmrEnvio_Tick()
|
||||
{
|
||||
var mensagens = bufferEnvio.ToList();
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ namespace AgroBase.Services
|
|||
|
||||
private static void EnviarComando(T_Code Dispositivo, byte id, byte funcCode, byte[] payload = null, bool get = true)
|
||||
{
|
||||
CanService.AdicionarMensagemNaFila(Dispositivo, id, funcCode, payload, get, funcCode);
|
||||
CanService.AdicionarMensagemNaFila(Dispositivo, id, id, funcCode, payload, get, funcCode);
|
||||
}
|
||||
|
||||
public static void SetModoOperacao(byte id, byte modo)
|
||||
|
|
@ -294,7 +294,7 @@ namespace AgroBase.Services
|
|||
public void ProcessarMensagem(CanMessage mensagem)
|
||||
{
|
||||
MksFuncCode func = (MksFuncCode)mensagem.DataRx[0];
|
||||
byte id = mensagem.Id;
|
||||
byte id = mensagem.IdTx;
|
||||
var data = mensagem.DataRx;
|
||||
|
||||
var item = ObterOuCriar(id);
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ namespace AgroBase.Services
|
|||
AtualizarConsole("Procurando dispositivos conectados à porta " + PortaCom + "...");
|
||||
SerialPort _Porta = new SerialPort() { PortName = PortaCom, BaudRate = 115200 };
|
||||
|
||||
if (!LoRaService.Iniciado)
|
||||
if (Variaveis.IsAgroMonitor && !LoRaService.Iniciado)
|
||||
{
|
||||
bool LoRa = await ProcurarDispositivoLoRa(_Porta);
|
||||
if (LoRa)
|
||||
|
|
|
|||
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -1 +1 @@
|
|||
a916f4b7a3b8728703c8d007e50dd3f8c3f77e39a6c79a9f0061bc86cd1cd2f6
|
||||
94e5acd4ef698303080886ab8e455321319e02e14c31bfb9f4f93b4ef716322c
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -26,6 +26,8 @@
|
|||
"stdexcept": "cpp",
|
||||
"xmemory0": "cpp",
|
||||
"xstring": "cpp",
|
||||
"optional": "cpp"
|
||||
"optional": "cpp",
|
||||
"map": "cpp",
|
||||
"xtree": "cpp"
|
||||
}
|
||||
}
|
||||
|
|
@ -92,6 +92,8 @@ CanService* CanService::instance = nullptr;
|
|||
class CanService {
|
||||
public:
|
||||
uint8_t ID_Num_sMOD = 0;
|
||||
uint8_t ID_Num_sTOD = 250;
|
||||
uint8_t ID_Num_sLRA = 251;
|
||||
typedef void (*OnReceiveCallback)(int packetSize, int senderId, byte funcCode, byte* data, int dataLength);
|
||||
|
||||
CanService(uint8_t nodeId, uint32_t baudRate = 500000) {
|
||||
|
|
@ -126,53 +128,151 @@ class CanService {
|
|||
PrintTela(String(err));
|
||||
return;
|
||||
}
|
||||
|
||||
if (CanTaskRxHandle == NULL) {
|
||||
canQueueRx = xQueueCreate(50, sizeof(twai_message_t));
|
||||
if (canQueueRx == NULL) {
|
||||
PrintTela("[CAN] Falha ao criar fila RX!");
|
||||
}
|
||||
xTaskCreatePinnedToCore(CanService::CanTaskRxWrapper, "CanTaskRx", 4096, this, 3, &CanTaskRxHandle, APP_CPU_NUM);
|
||||
}
|
||||
|
||||
if (CanTaskTxHandle == NULL) {
|
||||
canQueueTx = xQueueCreate(50, sizeof(twai_message_t));
|
||||
if (canQueueTx == NULL) {
|
||||
PrintTela("[CAN] Falha ao criar fila TX!");
|
||||
}
|
||||
xTaskCreatePinnedToCore(CanService::CanTaskTxWrapper, "CanTaskTx", 4096, this, 3, &CanTaskTxHandle, APP_CPU_NUM);
|
||||
}
|
||||
|
||||
if (CanTaskProcessHandle == NULL) {
|
||||
xTaskCreatePinnedToCore(CanService::CanTaskProcessWrapper, "CanTaskProcess", 6144, this, 2, &CanTaskProcessHandle, APP_CPU_NUM);
|
||||
}
|
||||
|
||||
PrintTela("[CAN] TWAI iniciado com sucesso");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
QueueHandle_t canQueueRx;
|
||||
TaskHandle_t CanTaskRxHandle = NULL;
|
||||
static void CanTaskRxWrapper(void *pvParameters) {
|
||||
CanService *service = static_cast<CanService*>(pvParameters);
|
||||
service->CanTaskRx(pvParameters);
|
||||
}
|
||||
|
||||
void CanTaskRx(void* pvParameters) {
|
||||
twai_message_t message;
|
||||
if (twai_receive(&message, pdMS_TO_TICKS(10)) == ESP_OK) {
|
||||
while (true) {
|
||||
if (twai_receive(&message, pdMS_TO_TICKS(10)) == ESP_OK) {
|
||||
|
||||
if (message.data_length_code == 0 || message.extd) return;
|
||||
|
||||
byte funcCode = message.data[0];
|
||||
|
||||
bool MensagemTx = (int)funcCode % 2 == 0;
|
||||
|
||||
Serial.print("[CAN ");
|
||||
Serial.print(MensagemTx ? "TX" : "RX");
|
||||
Serial.print("] ID: 0x");
|
||||
Serial.print(message.identifier, HEX);
|
||||
Serial.print(" Len: ");
|
||||
Serial.print(message.data_length_code);
|
||||
Serial.print(" Data: ");
|
||||
for (int i = 0; i < message.data_length_code; i++) {
|
||||
Serial.print(message.data[i], HEX);
|
||||
Serial.print(" ");
|
||||
if (message.data_length_code == 0 || message.extd) continue;
|
||||
|
||||
bool MensagemTx = message.identifier == static_cast<uint32_t>(_nodeId + 1);
|
||||
|
||||
PrintTela("[CAN ", false);
|
||||
PrintTela(MensagemTx ? "TX" : "RX", false);
|
||||
PrintTela("] ID: 0x", false);
|
||||
PrintTela(String(message.identifier, HEX), false);
|
||||
PrintTela(" Len: ", false);
|
||||
PrintTela(String(message.data_length_code), false);
|
||||
PrintTela(" Data: ", false);
|
||||
for (int i = 0; i < message.data_length_code; i++) {
|
||||
PrintTela(String(message.data[i], HEX), false);
|
||||
PrintTela(" ", false);
|
||||
}
|
||||
PrintTela("");
|
||||
|
||||
if (MensagemTx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!mensagemJaNaFila(canQueueRx, message)) {
|
||||
if (xQueueSend(canQueueRx, &message, 0) != pdTRUE) {
|
||||
PrintTela("[CAN] Fila RX cheia! Mensagem descartada.");
|
||||
}
|
||||
else {
|
||||
//PrintTela("[CAN] Mensagem adicionada na fila RX");
|
||||
}
|
||||
} else {
|
||||
PrintTela("[CAN] Mensagem duplicada ignorada.");
|
||||
}
|
||||
}
|
||||
Serial.println();
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (MensagemTx) { // funcCodes impares sao enviados pelo esp, ignorar
|
||||
return;
|
||||
}
|
||||
QueueHandle_t canQueueTx;
|
||||
TaskHandle_t CanTaskTxHandle = NULL;
|
||||
static void CanTaskTxWrapper(void *pvParameters) {
|
||||
CanService *service = static_cast<CanService*>(pvParameters);
|
||||
service->CanTaskTx(pvParameters);
|
||||
}
|
||||
|
||||
if (_callback != nullptr) {
|
||||
//PrintTela("Entrar no callback...");
|
||||
_callback(message.data_length_code, message.identifier, funcCode, &message.data[1], message.data_length_code - 1);
|
||||
void CanTaskTx(void* pvParameters) {
|
||||
CanService *service = static_cast<CanService*>(pvParameters);
|
||||
twai_message_t msg;
|
||||
while (true) {
|
||||
if (xQueueReceive(service->canQueueTx, &msg, portMAX_DELAY) == pdTRUE) {
|
||||
//PrintTela("[CAN] Mensagem recebida da fila TX");
|
||||
if (service->enviarDadosCan(msg)) {
|
||||
//PrintTela("[CAN] Mensagem enviada via CAN com sucesso");
|
||||
}
|
||||
else {
|
||||
PrintTela("[CAN] Erro ao enviar mensagem via CAN");
|
||||
}
|
||||
}
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
TaskHandle_t CanTaskProcessHandle = NULL;
|
||||
static void CanTaskProcessWrapper(void *pvParameters) {
|
||||
CanService *service = static_cast<CanService*>(pvParameters);
|
||||
service->CanTaskProcess(pvParameters);
|
||||
}
|
||||
|
||||
void CanTaskProcess(void* pvParameters) {
|
||||
//PrintTela("[CAN] Task de Processamento iniciada");
|
||||
CanService *service = static_cast<CanService*>(pvParameters);
|
||||
twai_message_t msg;
|
||||
while (true) {
|
||||
if (xQueueReceive(service->canQueueRx, &msg, portMAX_DELAY) == pdTRUE) {
|
||||
//PrintTela("[CAN] Mensagem recebida da fila RX");
|
||||
uint8_t funcCode = msg.data[0];
|
||||
if (service->_callback) {
|
||||
service->_callback(msg.data_length_code, msg.identifier, funcCode, &msg.data[1], msg.data_length_code - 1);
|
||||
}
|
||||
}
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool mensagemJaNaFila(QueueHandle_t fila, const twai_message_t& novaMsg) {
|
||||
twai_message_t msgTmp;
|
||||
UBaseType_t items = uxQueueMessagesWaiting(fila);
|
||||
|
||||
for (UBaseType_t i = 0; i < items; i++) {
|
||||
if (xQueuePeek(fila, &msgTmp, 0) == pdTRUE) {
|
||||
if (msgTmp.identifier == novaMsg.identifier && msgTmp.data[0] == novaMsg.data[0] && msgTmp.data[1] == novaMsg.data[1] && msgTmp.data[2] == novaMsg.data[2]) {
|
||||
return true; // já tem uma igual
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void setReceiveCallback(OnReceiveCallback cb) {
|
||||
_callback = cb;
|
||||
}
|
||||
|
||||
bool enviarMensagem(const std::vector<uint8_t>& payload) {
|
||||
bool adicionarMensagemFila(const std::vector<uint8_t>& payload) {
|
||||
if (payload.size() > 8) return false;
|
||||
|
||||
twai_message_t msg;
|
||||
msg.identifier = _nodeId;
|
||||
msg.identifier = _nodeId + 1;
|
||||
msg.extd = 0;
|
||||
msg.rtr = 0;
|
||||
msg.ss = 1;
|
||||
|
|
@ -182,27 +282,26 @@ class CanService {
|
|||
msg.data[i] = payload[i]; // 👈 Preenche os dados corretamente
|
||||
}
|
||||
|
||||
return twai_transmit(&msg, pdMS_TO_TICKS(10)) == ESP_OK;
|
||||
}
|
||||
|
||||
bool enviarMensagem(byte funcCode, const std::vector<uint8_t>& payload) {
|
||||
if (payload.size() > 7) return false;
|
||||
|
||||
twai_message_t msg;
|
||||
msg.identifier = _nodeId;
|
||||
msg.extd = 0;
|
||||
msg.rtr = 0;
|
||||
msg.ss = 1;
|
||||
msg.data_length_code = payload.size() + 1;
|
||||
msg.data[0] = funcCode;
|
||||
|
||||
for (uint8_t i = 0; i < payload.size(); ++i) {
|
||||
msg.data[i + 1] = payload[i];
|
||||
if (!mensagemJaNaFila(canQueueTx, msg)) {
|
||||
if (xQueueSend(canQueueTx, &msg, 0) != pdTRUE) {
|
||||
PrintTela("[CAN] Fila TX cheia! Mensagem descartada.");
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
//PrintTela("[CAN] Mensagem adicionada na fila TX");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
PrintTela("[CAN] Mensagem duplicada ignorada.");
|
||||
}
|
||||
}
|
||||
|
||||
bool enviarDadosCan(twai_message_t msg) {
|
||||
return twai_transmit(&msg, pdMS_TO_TICKS(10)) == ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
std::vector<uint8_t> MontarFrameChk(T_Code D_Code, bool Conectado, int Versao) {
|
||||
std::vector<uint8_t> data;
|
||||
data.push_back(ID_Num_sMOD);
|
||||
|
|
@ -217,6 +316,8 @@ class CanService {
|
|||
uint8_t _nodeId;
|
||||
uint32_t _baudRate;
|
||||
OnReceiveCallback _callback = nullptr;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -66,6 +66,8 @@ enum S_Code {
|
|||
sPOT = 23,
|
||||
sMOD = 24,
|
||||
sIMU = 25,
|
||||
sLRA = 26,
|
||||
sTOD = 27,
|
||||
};
|
||||
|
||||
enum Estado {
|
||||
|
|
@ -120,11 +122,12 @@ enum TiposLeituras {
|
|||
enum CanMessagePosicaoDados
|
||||
{
|
||||
Status = 0,
|
||||
Dados1 = 1,
|
||||
Dados2 = 2,
|
||||
Dados3 = 3,
|
||||
Dados4 = 4,
|
||||
Dados5 = 5,
|
||||
DadosAll = 1,
|
||||
Dados1 = 2,
|
||||
Dados2 = 3,
|
||||
Dados3 = 4,
|
||||
Dados4 = 5,
|
||||
Dados5 = 6,
|
||||
Config1 = 51,
|
||||
Config2 = 52,
|
||||
Config3 = 53,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef MuxService_h
|
||||
#define MuxService_h
|
||||
#ifndef I2CService_h
|
||||
#define I2CService_h
|
||||
|
||||
#include "SerialService.h"
|
||||
#include <Wire.h>
|
||||
|
|
@ -42,6 +42,11 @@ class I2CService {
|
|||
}
|
||||
idAtualUsandoI2C = -1;
|
||||
tempoEntradaI2C = 0;
|
||||
|
||||
if (i2cTaskHandle == NULL) {
|
||||
xTaskCreatePinnedToCore(I2CService::i2cTaskWrapper, "i2cTask", 4096, nullptr, 5, &i2cTaskHandle, tskNO_AFFINITY);
|
||||
}
|
||||
|
||||
return I2CIniciado;
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +80,7 @@ class I2CService {
|
|||
}
|
||||
|
||||
// Troca canal se necessário
|
||||
if (canalMux >= 0 && !TrocarCanalMux(canalMux)) {
|
||||
if (canalMux >= 0 && !SelecionarSensor(canalMux)) {
|
||||
xSemaphoreGive(i2cMutex); // Libera caso a troca de canal falhe
|
||||
return false;
|
||||
}
|
||||
|
|
@ -97,14 +102,6 @@ class I2CService {
|
|||
return idAtualUsandoI2C;
|
||||
}
|
||||
|
||||
static void VerificarI2CPreso() {
|
||||
if (idAtualUsandoI2C >= 0 && (millis() - tempoEntradaI2C > LimiteTempoI2C)) {
|
||||
PrintTela("[I2C] Semáforo preso pelo ID %d — forçando liberação!\n", idAtualUsandoI2C);
|
||||
LiberarAcessoI2C(idAtualUsandoI2C);
|
||||
IniciarI2C();
|
||||
}
|
||||
}
|
||||
|
||||
static bool LeituraSegura(uint8_t addr, uint8_t* buffer, size_t qtd, uint8_t reg = 0xFF, uint32_t timeoutMs = 50) {
|
||||
//Wire.setTimeout(timeoutMs);
|
||||
unsigned long t0 = millis();
|
||||
|
|
@ -155,13 +152,20 @@ class I2CService {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool TrocarCanalMux(int canalMux) {
|
||||
|
||||
static bool SelecionarSensor(uint8_t canalGlobal) {
|
||||
uint8_t _enderecoMux = 0x70 + (canalGlobal / 10);
|
||||
uint8_t _canal = canalGlobal % 10;
|
||||
return TrocarCanalMux(_enderecoMux, _canal);
|
||||
}
|
||||
|
||||
static bool TrocarCanalMux(uint8_t endereco, uint8_t canalMux) {
|
||||
if (MuxIniciado && canalMux >= 0 && canalMux < 9) {
|
||||
if (canalMux != canalAtivo) {
|
||||
Wire.beginTransmission(enderecoMux);
|
||||
Wire.beginTransmission(endereco);
|
||||
Wire.write(1 << canalMux);
|
||||
Wire.endTransmission();
|
||||
PrintTela("[MUX] Canal alterado de " + String(canalAtivo) + " para " + String(canalMux));
|
||||
PrintTela("[MUX] 0x" + String(endereco) + " - Canal alterado de " + String(canalAtivo) + " para " + String(canalMux));
|
||||
canalAtivo = canalMux;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -179,8 +183,7 @@ class I2CService {
|
|||
PrintTela("[MUX] Iniciando TCA9548A...");
|
||||
if (!MuxIniciado) {
|
||||
SolicitarAcessoI2C();
|
||||
Wire.beginTransmission(endereco);
|
||||
MuxIniciado = (Wire.endTransmission() == 0);
|
||||
MuxIniciado = VerificaEnderecoBarramento(endereco);
|
||||
if (!MuxIniciado) {
|
||||
PrintTela("[MUX] TCA9548A nao Iniciado, endereco " + String(endereco) + " nao encontrado no barramento");
|
||||
if (Mandatorio) {
|
||||
|
|
@ -273,6 +276,26 @@ class I2CService {
|
|||
static unsigned long tempoEntradaI2C;
|
||||
static unsigned long LimiteTempoI2C;
|
||||
|
||||
static TaskHandle_t i2cTaskHandle;
|
||||
static void i2cTaskWrapper(void *pvParameters) {
|
||||
I2CService::i2cTask();
|
||||
}
|
||||
|
||||
static void i2cTask() {
|
||||
while (true) {
|
||||
VerificarI2CPreso();
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void VerificarI2CPreso() {
|
||||
if (idAtualUsandoI2C >= 0 && (millis() - tempoEntradaI2C > LimiteTempoI2C)) {
|
||||
PrintTela("[I2C] Semáforo preso pelo ID %d — forçando liberação!\n", idAtualUsandoI2C);
|
||||
LiberarAcessoI2C(idAtualUsandoI2C);
|
||||
IniciarI2C();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
int I2CService::_pinoSDA = 1;
|
||||
|
|
@ -288,5 +311,6 @@ int I2CService::canalAtivo = -1;
|
|||
int I2CService::idAtualUsandoI2C = -1;
|
||||
unsigned long I2CService::tempoEntradaI2C = 0;
|
||||
SemaphoreHandle_t I2CService::i2cMutex = nullptr;
|
||||
TaskHandle_t I2CService::i2cTaskHandle = NULL;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,706 @@
|
|||
// 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);
|
||||
|
||||
PrintTela("[LRA] Iniciando LoRa nos pinos TXD=" + String(_pinTXD) + ", RXD=" + String(_pinRXD) + ", baudRate=" + String(_baudRate) + "...");
|
||||
|
||||
// Inicializa GPIOs
|
||||
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) {
|
||||
PrintTela("[LRA] E220 detectado!");
|
||||
|
||||
if (LraTaskRxHandle == NULL) {
|
||||
lraQueueRx = xQueueCreate(50, sizeof(std::vector<uint8_t>));
|
||||
if (lraQueueRx == NULL) {
|
||||
PrintTela("[LRA] Falha ao criar fila RX!");
|
||||
}
|
||||
xTaskCreatePinnedToCore(LoRaService::LraTaskRxWrapper, "LraTaskRx", 4096, this, 3, &LraTaskRxHandle, APP_CPU_NUM);
|
||||
}
|
||||
|
||||
if (LraTaskTxHandle == NULL) {
|
||||
lraQueueTx = xQueueCreate(50, sizeof(std::vector<uint8_t>));
|
||||
if (lraQueueTx == NULL) {
|
||||
PrintTela("[LRA] Falha ao criar fila TX!");
|
||||
}
|
||||
xTaskCreatePinnedToCore(LoRaService::LraTaskTxWrapper, "LraTaskTx", 4096, this, 3, &LraTaskTxHandle, APP_CPU_NUM);
|
||||
}
|
||||
|
||||
if (LraTaskProcessHandle == NULL) {
|
||||
xTaskCreatePinnedToCore(LoRaService::LraTaskProcessWrapper, "LraTaskProcess", 4096, this, 2, &LraTaskProcessHandle, APP_CPU_NUM);
|
||||
}
|
||||
} else {
|
||||
PrintTela("[LRA] E220 não detectado.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<uint8_t> MontarMensagemCAN(CanMessagePosicaoDados posicao) {
|
||||
std::vector<uint8_t> data;
|
||||
data.push_back(_canService->ID_Num_sLRA);
|
||||
data.push_back(static_cast<uint8_t>(posicao));
|
||||
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() < 3) return;
|
||||
uint8_t idNum = dados[1];
|
||||
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)dados[2];
|
||||
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) {
|
||||
PrintTela("[LRA] Fila TX cheia! Mensagem descartada.");
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
PrintTela("[LRA] 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() < 3) return status;
|
||||
uint8_t idNum = data[1];
|
||||
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)data[2];
|
||||
PrintTela("[LRA] Recebido comando ConfigurarModulo: posicao = " + String((int)posicao));
|
||||
switch (posicao) {
|
||||
case CanMessagePosicaoDados::Config1: {
|
||||
if (data.size() < 8) return data;
|
||||
_pinM0 = data[3];
|
||||
_pinM1 = data[4];
|
||||
_pinAUX = data[5];
|
||||
_pinRXD = data[6];
|
||||
_pinTXD = data[7];
|
||||
break;
|
||||
}
|
||||
case CanMessagePosicaoDados::Config2: {
|
||||
if (data.size() < 8) return data;
|
||||
if (_pinRXD == 0 || _pinTXD == 0) {
|
||||
PrintTela("[LRA] Erro ao iniciar E220, pinout ainda nao definido!");
|
||||
return status;
|
||||
}
|
||||
uint8_t baudField = data[4] & 0xE0; // bits 5,6,7 (baudrate)
|
||||
Inicializar(baudField);
|
||||
EnviarDadosCAN(MontarMensagemCAN(CanMessagePosicaoDados::Status));
|
||||
if (Conectado) {
|
||||
if (requisitarParametrosLoRa()) {
|
||||
EnviarDadosCAN(MontarMensagemCAN(CanMessagePosicaoDados::Dados1));
|
||||
Configurado =
|
||||
parametrosAtual.address == data[3] &&
|
||||
parametrosAtual.baudAndAirRate == data[4] &&
|
||||
parametrosAtual.packetSizeAndPower == data[5] &&
|
||||
parametrosAtual.channel == data[6] &&
|
||||
parametrosAtual.tranModeAndWorCycle == data[7];
|
||||
if (!Configurado) {
|
||||
Configurado = configurarModuloLoRa(
|
||||
data[3], // address
|
||||
data[4], // baudAndAirRate
|
||||
data[5], // packetSizeAndPower
|
||||
data[6], // channel
|
||||
data[7] // tranModeAndWorCycle
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
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 = 100; // por exemplo, 100ms
|
||||
|
||||
void reiniciarEstado() {
|
||||
recebendo = 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* instance = (LoRaService*)pvParameters;
|
||||
while (true) {
|
||||
if (!Conectado || !Configurado) {
|
||||
vTaskDelay(1000);
|
||||
continue;
|
||||
}
|
||||
if (recebendo && (millis() - ultimoByteRecebido > timeoutRecebimentoMs)) {
|
||||
PrintTela("[LRA] Timeout de recebimento. Reiniciando estado.");
|
||||
reiniciarEstado();
|
||||
}
|
||||
while (currentMode == LoRaMode::NORMAL && instance->_serialLoRa->available()) {
|
||||
uint8_t byteRecebido = instance->_serialLoRa->read();
|
||||
ultimoByteRecebido = millis(); // <- atualiza o tempo
|
||||
PrintTela("byteRecebido=" + String(byteRecebido));
|
||||
|
||||
if (!recebendo) {
|
||||
if (byteRecebido == CodigosFuncoes::BeginMsg) {
|
||||
reiniciarEstado();
|
||||
PrintTela("[LRA] Iniciou o recebimento dos dados LoRa");
|
||||
recebendo = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (bytesEsperados == -1) {
|
||||
bytesEsperados = byteRecebido;
|
||||
//PrintTela("[LRA] Definindo bytesEsperados = " + String(bytesEsperados));
|
||||
}
|
||||
else if (addrRemetente == -1) {
|
||||
addrRemetente = byteRecebido;
|
||||
//PrintTela("[LRA] Definindo addrRemetente = " + String(addrRemetente));
|
||||
}
|
||||
else if (addrDestinatario == -1) {
|
||||
addrDestinatario = byteRecebido;
|
||||
//PrintTela("[LRA] Definindo addrDestinatario = " + String(addrDestinatario));
|
||||
if (addrDestinatario != parametrosAtual.address) {
|
||||
PrintTela("[LRA] Mensagem para outro ID, descartando");
|
||||
reiniciarEstado();
|
||||
}
|
||||
}
|
||||
else {
|
||||
buffer.push_back(byteRecebido);
|
||||
|
||||
bool protocoloCompleto = addrRemetente != -1 && addrDestinatario != -1 && buffer.size() == bytesEsperados + 2; // +2 = checksum + end
|
||||
PrintTela("[LRA] 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) {
|
||||
PrintTela("[LRA] Mensagem LoRa recebida de " + String(addrRemetente) + " com checksum válido");
|
||||
|
||||
if (buffer.size() >= 2) {
|
||||
if (xQueueSend(lraQueueRx, &buffer, 0) != pdTRUE) {
|
||||
PrintTela("[LRA] Fila RX cheia! Mensagem descartada.");
|
||||
}
|
||||
else {
|
||||
PrintTela("[LRA] Mensagem adicionada na fila RX");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PrintTela("[LRA] 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) {
|
||||
vTaskDelay(1000);
|
||||
continue;
|
||||
}
|
||||
if (xQueueReceive(service->lraQueueTx, &msg, portMAX_DELAY) == pdTRUE) {
|
||||
service->EnviarDadosLoRa(msg);
|
||||
}
|
||||
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 _pinTXD;
|
||||
uint8_t _pinRXD;
|
||||
uint8_t _pinM0;
|
||||
uint8_t _pinM1;
|
||||
uint8_t _pinAUX;
|
||||
int _baudRate = 9600;
|
||||
LoRaMode currentMode = NORMAL;
|
||||
LoRaParametrosModel parametrosAtual;
|
||||
int commandTimeout = 1000;
|
||||
|
||||
// Métodos internos
|
||||
// Define o modo do LoRa
|
||||
bool setLoRaMode(LoRaMode mode) {
|
||||
if (mode == currentMode) return true;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
PrintTela("[LRA] 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) {
|
||||
PrintTela("[LRA] Erro: Timeout aguardando AUX após mudança de modo.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(100); // Manual pede 2ms após AUX ficar HIGH
|
||||
return true;
|
||||
}
|
||||
|
||||
// Verifica se o E220 responde
|
||||
bool checkLoRaConnected() {
|
||||
PrintTela("[LRA] Verificando se o E220 esta conectado...");
|
||||
|
||||
if (!setLoRaMode(CONFIG)) {
|
||||
PrintTela("[LRA] Falha 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];
|
||||
PrintTela("[LRA] Tentando BaudRate: " + String(_baudRate));
|
||||
}
|
||||
|
||||
// Limpa buffers antes de testar
|
||||
limparBufferLoRa();
|
||||
|
||||
_serialLoRa->write(CodigosFuncoes::CfgRx);
|
||||
|
||||
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) {
|
||||
PrintTela("[LRA] E220 conectado com baudRate " + String(_baudRate));
|
||||
} else {
|
||||
PrintTela("[LRA] Falha ao conectar no E220!");
|
||||
}
|
||||
|
||||
return conectado;
|
||||
}
|
||||
|
||||
// Método que monta o comando de configuração
|
||||
bool configurarModuloLoRa(byte addr, byte baud, byte packet, byte channel, byte worCycle) {
|
||||
if (!setLoRaMode(CONFIG)) {
|
||||
PrintTela("[LRA] Falha ao mudar para CONFIG!");
|
||||
return false;
|
||||
}
|
||||
|
||||
PrintTela("[LRA] Configurando modulo...");
|
||||
|
||||
limparBufferLoRa();
|
||||
|
||||
uint8_t command[9] = {
|
||||
CodigosFuncoes::CfgTx, // Código para configurar
|
||||
0x00, // Registro inicial
|
||||
0x06, // Quantidade de registros
|
||||
0x00, // Reservado
|
||||
addr,
|
||||
baud,
|
||||
packet,
|
||||
channel,
|
||||
worCycle
|
||||
};
|
||||
|
||||
_serialLoRa->write(command, sizeof(command));
|
||||
PrintTela("[LRA] Comando de configuração enviado!");
|
||||
|
||||
unsigned long startTime = millis();
|
||||
bool sucesso = false;
|
||||
|
||||
while (millis() - startTime < commandTimeout) {
|
||||
if (_serialLoRa->available()) {
|
||||
uint8_t cabecalho = _serialLoRa->read();
|
||||
if (cabecalho == CodigosFuncoes::CfgRx) { // 0xC1 é Resposta de Configuração
|
||||
sucesso = true;
|
||||
PrintTela("[LRA] Confirmação de configuração recebida!");
|
||||
} else {
|
||||
PrintTela("[LRA] Resposta inesperada: 0x" + String(cabecalho));
|
||||
}
|
||||
break;
|
||||
}
|
||||
vTaskDelay(10);
|
||||
}
|
||||
|
||||
setLoRaMode(NORMAL);
|
||||
|
||||
return sucesso;
|
||||
}
|
||||
|
||||
// Método que requisita a leitura dos parâmetros atuais
|
||||
bool requisitarParametrosLoRa() {
|
||||
PrintTela("Entrou aqui 3");
|
||||
if (!setLoRaMode(CONFIG)) {
|
||||
PrintTela("[LRA] Falha ao mudar para CONFIG!");
|
||||
return false;
|
||||
}
|
||||
|
||||
PrintTela("[LRA] Requisitando parametros do modulo...");
|
||||
|
||||
limparBufferLoRa();
|
||||
|
||||
uint8_t command[3] = { CodigosFuncoes::CfgRx, 0x00, 0x0B };
|
||||
|
||||
_serialLoRa->write(command, sizeof(command));
|
||||
PrintTela("[LRA] Comando de requisição enviado!");
|
||||
|
||||
unsigned long startTime = millis();
|
||||
bool sucesso = false;
|
||||
|
||||
while (!sucesso && millis() - startTime < commandTimeout) {
|
||||
if (_serialLoRa->available()) {
|
||||
uint8_t cabecalho = _serialLoRa->read();
|
||||
if (cabecalho == CodigosFuncoes::CfgRx) { // Cabeçalho de resposta correta
|
||||
PrintTela("[LRA] Cabecalho recebido corretamente.");
|
||||
uint8_t startAddr = _serialLoRa->read(); // Deve ser 0x00
|
||||
uint8_t dataLen = _serialLoRa->read(); // Deve ser 0x0B
|
||||
|
||||
if (startAddr == command[1] && dataLen == command[2]) {
|
||||
PrintTela("[LRA] Parâmetros recebidos corretamente.");
|
||||
|
||||
// Agora ler os registros
|
||||
uint8_t addh = _serialLoRa->read(); // byte 0
|
||||
uint8_t addl = _serialLoRa->read(); // byte 1
|
||||
parametrosAtual.address = addl; // usa só o ADDL (0~255)
|
||||
|
||||
// Continua normalmente
|
||||
parametrosAtual.baudAndAirRate = _serialLoRa->read(); // byte 2
|
||||
parametrosAtual.packetSizeAndPower = _serialLoRa->read(); // byte 3
|
||||
parametrosAtual.channel = _serialLoRa->read(); // byte 4
|
||||
parametrosAtual.tranModeAndWorCycle = _serialLoRa->read(); // byte 5
|
||||
|
||||
// Lê e ignora os 5 bytes restantes (6~10)
|
||||
for (int i = 0; i < (0x0B - 6); i++) {
|
||||
_serialLoRa->read();
|
||||
}
|
||||
|
||||
sucesso = true;
|
||||
}
|
||||
else {
|
||||
PrintTela("[LRA] Dados Corrompidos: startAddr=" + String(startAddr) + ", dataLen=" + String(dataLen));
|
||||
}
|
||||
} else if (cabecalho == CodigosFuncoes::WrongMode) {
|
||||
PrintTela("[LRA] Modo invalido!");
|
||||
}
|
||||
}
|
||||
vTaskDelay(10);
|
||||
}
|
||||
|
||||
limparBufferLoRa();
|
||||
|
||||
setLoRaMode(NORMAL);
|
||||
|
||||
return sucesso;
|
||||
}
|
||||
|
||||
// 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)
|
||||
frame.push_back((destino >> 8) & 0xFF); // Addr High (geralmente 0x00)
|
||||
frame.push_back(destino & 0xFF); // Addr Low
|
||||
frame.push_back(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
|
||||
|
||||
PrintTela("Frame LoRa montado: ", false);
|
||||
for (int i = 0; i < frame.size(); i++) {
|
||||
PrintTela(String(frame[i]) + " ", false);
|
||||
}
|
||||
PrintTela("");
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
bool EnviarDadosCAN(std::vector<uint8_t> dados) {
|
||||
_canService->adicionarMensagemFila(dados);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EnviarDadosLoRa(std::vector<uint8_t> dados) {
|
||||
PrintTela("Enviando dados via LoRa para o endereco " + String(AddressBase) + " no canal " + String(parametrosAtual.channel));
|
||||
std::vector<uint8_t> dadosLora = MontarFrameLoRa(AddressBase, parametrosAtual.channel, dados);
|
||||
_serialLoRa->write(dadosLora.data(), dadosLora.size());
|
||||
_serialLoRa->flush();
|
||||
PrintTela("Dados enviados");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::map<String, uint8_t> CodigosTipoTransmissao = {
|
||||
{"Normal", 0x00},
|
||||
{"Fixed", 0x40},
|
||||
};
|
||||
|
||||
std::map<int, uint8_t> CodigosBaudRates = {
|
||||
{1200, 0x00},
|
||||
{2400, 0x20},
|
||||
{4800, 0x40},
|
||||
{9600, 0x60},
|
||||
{19200, 0x80},
|
||||
{38400, 0xA0},
|
||||
{57600, 0xC0},
|
||||
{115200, 0xE0},
|
||||
};
|
||||
|
||||
std::map<double, uint8_t> CodigosAirRates = {
|
||||
{2.4, 0x02},
|
||||
{4.8, 0x03},
|
||||
{9.6, 0x04},
|
||||
{19.2, 0x05},
|
||||
{38.4, 0x06},
|
||||
{62.5, 0x67},
|
||||
};
|
||||
|
||||
std::map<int, uint8_t> CodigosPacketSizes = {
|
||||
{200, 0x00},
|
||||
{128, 0x40},
|
||||
{64, 0x80},
|
||||
{32, 0xC0},
|
||||
};
|
||||
|
||||
std::map<int, uint8_t> CodigosWorCycles = {
|
||||
{500, 0x00},
|
||||
{1000, 0x01},
|
||||
{1500, 0x02},
|
||||
{2000, 0x03},
|
||||
{2500, 0x04},
|
||||
{3000, 0x05},
|
||||
{3500, 0x06},
|
||||
{4000, 0x07},
|
||||
};
|
||||
|
||||
std::map<int, uint8_t> CodigosPowers = {
|
||||
{22, 0x00},
|
||||
{17, 0x01},
|
||||
{13, 0x02},
|
||||
{10, 0x03},
|
||||
};
|
||||
|
||||
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
|
||||
|
|
@ -91,7 +91,7 @@ class SensorIMU {
|
|||
Iniciado = MpuIniciado || BmpIniciado;
|
||||
if (Iniciado) {
|
||||
filterDelay = 1000.0f / filterHz;
|
||||
xTaskCreatePinnedToCore(&SensorIMU::IMUTaskWrapper, "IMUTask", 5000, this, 20, &IMUTaskHandle, tskNO_AFFINITY);
|
||||
xTaskCreatePinnedToCore(&SensorIMU::IMUTaskWrapper, "IMUTask", 4096, this, 20, &IMUTaskHandle, tskNO_AFFINITY);
|
||||
PrintTela(_ID + " iniciado");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,431 @@
|
|||
// Dispositivo: Sensoriamento
|
||||
// Versão Firmware: 17
|
||||
// Ultima atualização: 28/04/2025
|
||||
// Atualização: Implementado LoRa
|
||||
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\SerialService.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\CanService.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\LoRaService.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\EepromService.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\I2CService.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\Utils.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\Pinout.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\SensorTemperaturaModel.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\SensorCorrenteModel.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\SensorIMUModel.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\SinaleiroModel.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\ReleModel.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\ServoFreioModel.h"
|
||||
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\SensorPotenciometroModel.h"
|
||||
|
||||
|
||||
// Configurações do CAN
|
||||
uint8_t CanID = 0x11;
|
||||
CanService canService(CanID);
|
||||
|
||||
LoRaService loraService(&canService);
|
||||
|
||||
#define D_Code Sen
|
||||
int VERSION = 17;
|
||||
String Mod_ID = "Sen";
|
||||
|
||||
#define _pinoLED RGB_BUILTIN
|
||||
|
||||
bool Conectado = false;
|
||||
bool Conectar = false;
|
||||
|
||||
int _TaxaAmostragem = 1000;
|
||||
volatile bool RequisitarDados = false;
|
||||
int latenciaLoop = 0;
|
||||
int qtdEnviosStatus = 0;
|
||||
int maxEnviosStatus = 5;
|
||||
|
||||
std::vector<SensorTemperatura*> listaSensoresTemperatura;
|
||||
std::vector<SensorCorrente*> listaSensoresCorrente;
|
||||
std::vector<Sinaleiro*> listaSinaleiros;
|
||||
std::vector<Rele*> listaReles;
|
||||
std::vector<ServoFreio*> listaServoFreios;
|
||||
std::vector<SensorIMU*> listaSensoresIMU;
|
||||
|
||||
void inicializarDependenciasI2C() {
|
||||
I2CService::IniciarI2C();
|
||||
I2CService::IniciarMUX();
|
||||
I2CService::IniciarMCP();
|
||||
I2CService::IniciarADS();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
delay(5000);
|
||||
|
||||
inicializarDependenciasI2C();
|
||||
|
||||
// Registrar callback para processar mensagens CAN recebidas
|
||||
canService.setReceiveCallback([](int packetSize, int senderId, byte funcCode, byte* data, int dataLength) {
|
||||
std::vector<uint8_t> fullData;
|
||||
fullData.push_back(funcCode); // Primeiro byte é sempre o funcCode
|
||||
for (int i = 0; i < dataLength; i++) {
|
||||
fullData.push_back(data[i]);
|
||||
}
|
||||
|
||||
ProcessarFrameCAN(fullData);
|
||||
});
|
||||
|
||||
canService.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!RequisitarDados) {
|
||||
enviarLogsPeriodicamente();
|
||||
}
|
||||
|
||||
delay(1);
|
||||
}
|
||||
|
||||
// Função para enviar logs do sensor periodicamente
|
||||
unsigned long ultimoEnvioLogs = 0;
|
||||
const unsigned long intervaloEnvioLogs = 1000; // 1 segundo
|
||||
void enviarLogsPeriodicamente() {
|
||||
unsigned long tempoAtual = millis();
|
||||
// Verifica se o intervalo de 5 segundos já passou
|
||||
if (tempoAtual - ultimoEnvioLogs >= intervaloEnvioLogs) {
|
||||
ultimoEnvioLogs = tempoAtual; // Atualiza o tempo do último envio
|
||||
|
||||
enviarDadosSensores(canService.ID_Num_sTOD, CanMessagePosicaoDados::DadosAll);
|
||||
}
|
||||
}
|
||||
|
||||
void enviarDadosSensores(int id_num, CanMessagePosicaoDados posicao) {
|
||||
unsigned long startLoop = millis();
|
||||
if (Conectado) {
|
||||
PrintTela("Atualizando dados dos sensores...");
|
||||
|
||||
bool enviarStatus = qtdEnviosStatus >= maxEnviosStatus && posicao != CanMessagePosicaoDados::Status;
|
||||
bool todosDados = posicao == CanMessagePosicaoDados::DadosAll;
|
||||
bool todosIDs = id_num == canService.ID_Num_sTOD;
|
||||
|
||||
if (id_num == canService.ID_Num_sLRA) {
|
||||
EnviarDadosCAN(loraService.MontarMensagemCAN(posicao));
|
||||
return;
|
||||
}
|
||||
|
||||
if (todosIDs && enviarStatus) {
|
||||
ProcessarChk();
|
||||
EnviarDadosCAN(loraService.MontarMensagemCAN(CanMessagePosicaoDados::Status));
|
||||
}
|
||||
|
||||
for (auto* sensor : listaSensoresTemperatura) {
|
||||
if (sensor != nullptr && (todosIDs || sensor->ID_Num == id_num)) {
|
||||
sensor->RequisitarDados();
|
||||
if (enviarStatus) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Status));
|
||||
}
|
||||
if (sensor->Iniciado) {
|
||||
if (todosDados) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Dados1));
|
||||
}
|
||||
else {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(posicao));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* sensor : listaSensoresCorrente) {
|
||||
if (sensor != nullptr && (todosIDs || sensor->ID_Num == id_num)) {
|
||||
sensor->RequisitarDados();
|
||||
if (enviarStatus) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Status));
|
||||
}
|
||||
if (sensor->Iniciado) {
|
||||
if (todosDados) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Dados1));
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Dados2));
|
||||
}
|
||||
else {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(posicao));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* sensor : listaSinaleiros) {
|
||||
if (sensor != nullptr && (todosIDs || sensor->ID_Num == id_num)) {
|
||||
sensor->RequisitarDados();
|
||||
if (enviarStatus) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Status));
|
||||
}
|
||||
if (sensor->Iniciado) {
|
||||
if (todosDados) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Dados1));
|
||||
}
|
||||
else {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(posicao));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* sensor : listaReles) {
|
||||
if (sensor != nullptr && (todosIDs || sensor->ID_Num == id_num)) {
|
||||
sensor->RequisitarDados();
|
||||
if (enviarStatus) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Status));
|
||||
}
|
||||
if (sensor->Iniciado) {
|
||||
if (todosDados) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Dados1));
|
||||
}
|
||||
else {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(posicao));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* sensor : listaServoFreios) {
|
||||
if (sensor != nullptr && (todosIDs || sensor->ID_Num == id_num)) {
|
||||
sensor->RequisitarDados();
|
||||
if (enviarStatus) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Status));
|
||||
}
|
||||
if (sensor->Iniciado) {
|
||||
if (todosDados) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Dados1));
|
||||
}
|
||||
else {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(posicao));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* sensor : listaSensoresIMU) {
|
||||
if (sensor != nullptr && (todosIDs || sensor->ID_Num == id_num)) {
|
||||
sensor->RequisitarDados();
|
||||
if (enviarStatus) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Status));
|
||||
}
|
||||
if (todosDados) {
|
||||
if (sensor->MpuIniciado) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Dados1));
|
||||
}
|
||||
if (sensor->BmpIniciado) {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(CanMessagePosicaoDados::Dados2));
|
||||
}
|
||||
}
|
||||
else {
|
||||
EnviarDadosCAN(sensor->MontarMensagemCAN(posicao));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enviarStatus) {
|
||||
qtdEnviosStatus = 0;
|
||||
}
|
||||
else {
|
||||
qtdEnviosStatus++;
|
||||
}
|
||||
}
|
||||
|
||||
latenciaLoop = millis() - startLoop;
|
||||
}
|
||||
|
||||
void EnviarDadosCAN(std::vector<uint8_t> data) {
|
||||
if (data.size() > 2) {
|
||||
canService.adicionarMensagemFila(data);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessarFrameCAN(std::vector<uint8_t> data) {
|
||||
if (data.size() < 1) return;
|
||||
|
||||
F_Code funcao = (F_Code)data[0];
|
||||
|
||||
switch (funcao) {
|
||||
case F_Code::ChkTx: ProcessarChk(); break;
|
||||
case F_Code::ReqTx: ProcessarReq(data); break;
|
||||
case F_Code::CmdTx: ProcessarCmd(data); break;
|
||||
case F_Code::CfgTx: ProcessarCfg(data); break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessarChk() {
|
||||
std::vector<uint8_t> data = canService.MontarFrameChk(D_Code, Conectado, VERSION);
|
||||
EnviarDadosCAN(data);
|
||||
}
|
||||
|
||||
void ProcessarReq(std::vector<uint8_t> data) {
|
||||
if (data.size() < 3) return;
|
||||
uint8_t id = data[1];
|
||||
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)data[2];
|
||||
enviarDadosSensores(id, posicao);
|
||||
}
|
||||
|
||||
void ProcessarCfg(std::vector<uint8_t> data) {
|
||||
if (data.size() < 4) return;
|
||||
std::vector<uint8_t> _chkRx;
|
||||
uint8_t id = data[1];
|
||||
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)data[2];
|
||||
S_Code componente = DetectarComponente(id);
|
||||
if (componente == S_Code::sMOD) {
|
||||
switch (posicao) {
|
||||
case CanMessagePosicaoDados::Config1: {
|
||||
if (data.size() < 8) return;
|
||||
bool conectar = data[4] == 1;
|
||||
int taxa = data[5] * 10;
|
||||
bool requisitar = data[6] == 1;
|
||||
loraService.AddressBase = data[7];
|
||||
_TaxaAmostragem = taxa;
|
||||
RequisitarDados = requisitar;
|
||||
Conectar = conectar;
|
||||
break;
|
||||
}
|
||||
case CanMessagePosicaoDados::Config2: {
|
||||
if (data.size() < 6) return;
|
||||
int pinoSDA = data[4];
|
||||
int pinoSCL = data[5];
|
||||
if (pinoSDA != PINO_INVALIDO && pinoSCL != PINO_INVALIDO) {
|
||||
I2CService::DefinirPinos(pinoSDA, pinoSCL);
|
||||
inicializarDependenciasI2C();
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
Conectado = Conectar;
|
||||
_chkRx = canService.MontarFrameChk(D_Code, Conectado, VERSION);
|
||||
LimparListasComponentes();
|
||||
PrintTela("[SEN] Configuracao do modulo concluida");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (componente == S_Code::sLRA) {
|
||||
_chkRx = loraService.ConfigurarModulo(data);
|
||||
}
|
||||
else {
|
||||
componente = (S_Code)data[3];
|
||||
switch (componente) {
|
||||
case sTMP: {
|
||||
_chkRx = SensorTemperatura::ConfigurarSensor(listaSensoresTemperatura, data, Mod_ID);
|
||||
break;
|
||||
}
|
||||
case sCOR: {
|
||||
_chkRx = SensorCorrente::ConfigurarSensor(listaSensoresCorrente, data, Mod_ID);
|
||||
break;
|
||||
}
|
||||
case sLED: {
|
||||
_chkRx = Sinaleiro::ConfigurarSinaleiro(listaSinaleiros, data, Mod_ID);
|
||||
break;
|
||||
}
|
||||
case sRLE: {
|
||||
_chkRx = Rele::ConfigurarRele(listaReles, data, Mod_ID);
|
||||
break;
|
||||
}
|
||||
case sFRO: {
|
||||
_chkRx = ServoFreio::ConfigurarServo(listaServoFreios, data, Mod_ID);
|
||||
break;
|
||||
|
||||
}
|
||||
case sIMU: {
|
||||
_chkRx = SensorIMU::ConfigurarSensor(listaSensoresIMU, data, Mod_ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EnviarDadosCAN(_chkRx);
|
||||
}
|
||||
|
||||
void ProcessarCmd(std::vector<uint8_t> data) {
|
||||
if (data.size() < 3) return;
|
||||
uint8_t id = data[1];
|
||||
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)data[2];
|
||||
S_Code componente = DetectarComponente(id);
|
||||
switch (componente) {
|
||||
case sLED: {
|
||||
Sinaleiro::ComandarSinaleiro(listaSinaleiros, data, id);
|
||||
break;
|
||||
}
|
||||
case sRLE: {
|
||||
Rele::ComandarRele(listaReles, data, id);
|
||||
break;
|
||||
}
|
||||
case sFRO: {
|
||||
ServoFreio::ComandarServo(listaServoFreios, data, id);
|
||||
break;
|
||||
}
|
||||
case sLRA: {
|
||||
loraService.EnviarComando(data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LimparListasComponentes() {
|
||||
for (auto* rele : listaReles) {
|
||||
rele->Desligar();
|
||||
PrintTela("Rele " + rele->_ID + " desligado.");
|
||||
delete rele;
|
||||
}
|
||||
listaReles.clear();
|
||||
|
||||
for (auto* sinaleiro : listaSinaleiros) {
|
||||
sinaleiro->Desligar();
|
||||
PrintTela("Sinaleiro " + sinaleiro->_ID + " desligado.");
|
||||
delete sinaleiro;
|
||||
}
|
||||
listaSinaleiros.clear();
|
||||
|
||||
for (auto* servo : listaServoFreios) {
|
||||
servo->Desligar();
|
||||
PrintTela("Servo " + servo->_ID + " desligado.");
|
||||
delete servo;
|
||||
}
|
||||
listaServoFreios.clear();
|
||||
|
||||
for (auto* sensor : listaSensoresCorrente) {
|
||||
sensor->Desligar();
|
||||
PrintTela("Sensor de Corrente " + sensor->_ID + " desligado.");
|
||||
delete sensor;
|
||||
}
|
||||
listaSensoresCorrente.clear();
|
||||
|
||||
for (auto* sensor : listaSensoresTemperatura) {
|
||||
sensor->Desligar();
|
||||
PrintTela("Sensor de Temperatura " + sensor->_ID + " desligado.");
|
||||
delete sensor;
|
||||
}
|
||||
listaSensoresTemperatura.clear();
|
||||
|
||||
for (auto* sensor : listaSensoresIMU) {
|
||||
sensor->Desligar();
|
||||
PrintTela("Sensor IMU " + sensor->_ID + " desligado.");
|
||||
delete sensor;
|
||||
}
|
||||
listaSensoresIMU.clear();
|
||||
}
|
||||
|
||||
S_Code DetectarComponente(uint8_t idNum) {
|
||||
if (idNum == canService.ID_Num_sMOD) return S_Code::sMOD;
|
||||
if (idNum == canService.ID_Num_sTOD) return S_Code::sTOD;
|
||||
if (idNum == canService.ID_Num_sLRA) return S_Code::sLRA;
|
||||
for (auto* s : listaSensoresTemperatura) {
|
||||
if (s->ID_Num == idNum) return S_Code::sTMP;
|
||||
}
|
||||
for (auto* s : listaSensoresCorrente) {
|
||||
if (s->ID_Num == idNum) return S_Code::sCOR;
|
||||
}
|
||||
for (auto* s : listaSinaleiros) {
|
||||
if (s->ID_Num == idNum) return S_Code::sLED;
|
||||
}
|
||||
for (auto* s : listaReles) {
|
||||
if (s->ID_Num == idNum) return S_Code::sRLE;
|
||||
}
|
||||
for (auto* s : listaServoFreios) {
|
||||
if (s->ID_Num == idNum) return S_Code::sFRO;
|
||||
}
|
||||
for (auto* s : listaSensoresIMU) {
|
||||
if (s->ID_Num == idNum) return S_Code::sIMU;
|
||||
}
|
||||
return S_Code::sVZO;
|
||||
}
|
||||
|
|
@ -0,0 +1,331 @@
|
|||
#include <HardwareSerial.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
// Definições de pinos
|
||||
#define PIN_M0 7
|
||||
#define PIN_M1 15
|
||||
#define PIN_AUX 18
|
||||
|
||||
// Definições de Serial
|
||||
HardwareSerial SerialLoRa(2); // UART2 para o E220
|
||||
HardwareSerial SerialCAN(1); // UART1 simulando o CAN
|
||||
|
||||
// Estado atual do módulo
|
||||
enum LoRaMode { NORMAL, WAKE_UP, POWER_SAVING, CONFIG };
|
||||
LoRaMode currentMode = NORMAL;
|
||||
|
||||
// Prototipagem
|
||||
bool setLoRaMode(LoRaMode mode);
|
||||
bool checkLoRaConnected();
|
||||
void taskBridge(void *pvParameters);
|
||||
void readParameters();
|
||||
void saveParameters();
|
||||
|
||||
// Config inicial
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Iniciando ESP32-S3 + E220...");
|
||||
|
||||
// Inicializa GPIOs
|
||||
pinMode(PIN_M0, OUTPUT);
|
||||
pinMode(PIN_M1, OUTPUT);
|
||||
pinMode(PIN_AUX, INPUT);
|
||||
|
||||
// Inicializa Seriais
|
||||
SerialLoRa.begin(9600, SERIAL_8N1, 17, 16); // RX=17, TX=16
|
||||
SerialCAN.begin(115200, SERIAL_8N1, 9, 10); // Simulação CAN (mude os pinos conforme seu setup)
|
||||
|
||||
// Coloca o módulo em modo normal
|
||||
setLoRaMode(NORMAL);
|
||||
|
||||
delay(500); // Aguarda estabilização
|
||||
|
||||
// Testa conexão
|
||||
if (checkLoRaConnected()) {
|
||||
requisitarParametrosLoRa();
|
||||
Serial.println("LoRa E220 detectado!");
|
||||
} else {
|
||||
Serial.println("Erro: LoRa E220 não detectado.");
|
||||
}
|
||||
|
||||
// Cria a task de ponte entre LoRa e CAN
|
||||
xTaskCreatePinnedToCore(
|
||||
taskBridge,
|
||||
"TaskBridge",
|
||||
4096,
|
||||
NULL,
|
||||
1,
|
||||
NULL,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Você pode colocar comandos de teste aqui se quiser.
|
||||
}
|
||||
|
||||
// Define o modo do LoRa
|
||||
bool setLoRaMode(LoRaMode mode) {
|
||||
// Ajusta os pinos M0 e M1 conforme o modo desejado
|
||||
switch (mode) {
|
||||
case NORMAL:
|
||||
digitalWrite(PIN_M0, LOW);
|
||||
digitalWrite(PIN_M1, LOW);
|
||||
break;
|
||||
case WAKE_UP:
|
||||
digitalWrite(PIN_M0, HIGH);
|
||||
digitalWrite(PIN_M1, LOW);
|
||||
break;
|
||||
case POWER_SAVING:
|
||||
digitalWrite(PIN_M0, LOW);
|
||||
digitalWrite(PIN_M1, HIGH);
|
||||
break;
|
||||
case CONFIG:
|
||||
digitalWrite(PIN_M0, HIGH);
|
||||
digitalWrite(PIN_M1, HIGH);
|
||||
break;
|
||||
}
|
||||
|
||||
currentMode = mode;
|
||||
|
||||
delay(100); // Aguarda sinalização inicial de troca
|
||||
|
||||
// ESPERA o AUX ir para HIGH, indicando que a troca completou
|
||||
unsigned long timeout = millis() + 1000;
|
||||
while (digitalRead(PIN_AUX) == LOW) {
|
||||
if (millis() > timeout) {
|
||||
Serial.println("Erro: Timeout aguardando AUX após mudança de modo.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delay(100); // Manual pede 2ms após AUX ficar HIGH
|
||||
return true;
|
||||
}
|
||||
|
||||
// Verifica se o E220 responde
|
||||
bool checkLoRaConnected() {
|
||||
if (!setLoRaMode(CONFIG)) {
|
||||
Serial.println("Falha ao mudar para CONFIG!");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool conectado = false;
|
||||
|
||||
SerialLoRa.write(0xC1);
|
||||
delay(100);
|
||||
while (SerialLoRa.available()) {
|
||||
uint8_t dado = SerialLoRa.read();
|
||||
if (dado == 0xC1) {
|
||||
conectado = true;
|
||||
}
|
||||
}
|
||||
setLoRaMode(NORMAL);
|
||||
|
||||
return conectado;
|
||||
}
|
||||
|
||||
String comandoRecebido = "";
|
||||
|
||||
// Task que faz o espelhamento Serial e interpreta comandos de texto
|
||||
void taskBridge(void *pvParameters) {
|
||||
while (true) {
|
||||
if (Serial.available()) {
|
||||
char c = (char)Serial.read();
|
||||
if (c == '\n' || c == '\r') {
|
||||
// Quando apertar ENTER, processa o comando recebido
|
||||
if (comandoRecebido.length() > 0) {
|
||||
if (comandoRecebido == "1") {
|
||||
Serial.println("Comando: Requisitar parâmetros do LoRa");
|
||||
requisitarParametrosLoRa();
|
||||
}
|
||||
else if (comandoRecebido.startsWith("2") && comandoRecebido.length() == 11) {
|
||||
// comando: 2 + 10 digitos (2 digitos por byte em hexadecimal)
|
||||
|
||||
Serial.println("Comando: Configurar LoRa com novos parâmetros");
|
||||
|
||||
// Parseia os números
|
||||
byte addr = strtol(comandoRecebido.substring(1, 3).c_str(), NULL, 16);
|
||||
byte baud = strtol(comandoRecebido.substring(3, 5).c_str(), NULL, 16);
|
||||
byte packet = strtol(comandoRecebido.substring(5, 7).c_str(), NULL, 16);
|
||||
byte channel = strtol(comandoRecebido.substring(7, 9).c_str(), NULL, 16);
|
||||
byte worCycle = strtol(comandoRecebido.substring(9, 11).c_str(), NULL, 16);
|
||||
|
||||
Serial.print("Addr: "); Serial.println(addr, HEX);
|
||||
Serial.print("Baud: "); Serial.println(baud, HEX);
|
||||
Serial.print("Packet: "); Serial.println(packet, HEX);
|
||||
Serial.print("Channel: "); Serial.println(channel, HEX);
|
||||
Serial.print("WorCycle: "); Serial.println(worCycle, HEX);
|
||||
|
||||
configurarModuloLoRa(addr, baud, packet, channel, worCycle);
|
||||
}
|
||||
else {
|
||||
Serial.println("Comando inválido.");
|
||||
}
|
||||
comandoRecebido = ""; // Limpa para próxima leitura
|
||||
}
|
||||
}
|
||||
else {
|
||||
comandoRecebido += c;
|
||||
}
|
||||
}
|
||||
|
||||
// Dados recebidos do LoRa -> envia para o CAN normalmente
|
||||
if (SerialLoRa.available()) {
|
||||
while (SerialLoRa.available()) {
|
||||
SerialCAN.write(SerialLoRa.read());
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct LoRaParametrosModel {
|
||||
uint8_t address;
|
||||
uint8_t baudAndAirRate;
|
||||
uint8_t packetSizeAndPower;
|
||||
uint8_t channel;
|
||||
uint8_t tranModeAndWorCycle;
|
||||
};
|
||||
|
||||
// Parâmetros globais
|
||||
LoRaParametrosModel parametrosAtual;
|
||||
|
||||
// Método que monta o comando de configuração
|
||||
bool configurarModuloLoRa(byte addr, byte baud, byte packet, byte channel, byte worCycle) {
|
||||
if (!setLoRaMode(CONFIG)) {
|
||||
Serial.println("Falha ao mudar para CONFIG!");
|
||||
return false;
|
||||
}
|
||||
|
||||
limparBufferLoRa();
|
||||
|
||||
uint8_t command[9] = {
|
||||
0xC0, // Código para configurar
|
||||
0x00, // Registro inicial
|
||||
0x06, // Quantidade de registros
|
||||
0x00, // Reservado
|
||||
addr,
|
||||
baud,
|
||||
packet,
|
||||
channel,
|
||||
worCycle
|
||||
};
|
||||
|
||||
SerialLoRa.write(command, sizeof(command));
|
||||
Serial.println("Comando de configuração enviado!");
|
||||
|
||||
unsigned long startTime = millis();
|
||||
bool sucesso = false;
|
||||
|
||||
while (millis() - startTime < 3000) { // timeout 3 segundos
|
||||
if (SerialLoRa.available()) {
|
||||
uint8_t cabecalho = SerialLoRa.read();
|
||||
if (cabecalho == 0xC1) { // 0xC1 é Resposta de Configuração
|
||||
sucesso = true;
|
||||
Serial.println("Confirmação de configuração recebida!");
|
||||
} else {
|
||||
Serial.print("Resposta inesperada: 0x");
|
||||
Serial.println(cabecalho, HEX);
|
||||
}
|
||||
break;
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
|
||||
setLoRaMode(NORMAL);
|
||||
|
||||
return sucesso;
|
||||
}
|
||||
|
||||
// Método que requisita a leitura dos parâmetros atuais
|
||||
bool requisitarParametrosLoRa() {
|
||||
if (!setLoRaMode(CONFIG)) {
|
||||
Serial.println("Falha ao mudar para CONFIG!");
|
||||
return false;
|
||||
}
|
||||
|
||||
limparBufferLoRa();
|
||||
|
||||
uint8_t command[3] = { 0xC1, 0x00, 0x0B };
|
||||
|
||||
SerialLoRa.write(command, sizeof(command));
|
||||
Serial.println("Comando de requisição enviado!");
|
||||
|
||||
unsigned long startTime = millis();
|
||||
bool sucesso = false;
|
||||
|
||||
while (!sucesso && millis() - startTime < 3000) { // timeout 3 segundos
|
||||
if (SerialLoRa.available()) {
|
||||
uint8_t cabecalho = SerialLoRa.read();
|
||||
if (cabecalho == 0xC1) { // Cabeçalho de resposta correta
|
||||
uint8_t startAddr = SerialLoRa.read(); // Deve ser 0x00
|
||||
uint8_t dataLen = SerialLoRa.read(); // Deve ser 0x0B
|
||||
|
||||
if (startAddr == 0x00 && dataLen == 0x0B) {
|
||||
Serial.println("Parâmetros recebidos corretamente.");
|
||||
|
||||
// Agora ler os registros
|
||||
parametrosAtual.address = SerialLoRa.read();
|
||||
parametrosAtual.baudAndAirRate = SerialLoRa.read();
|
||||
parametrosAtual.packetSizeAndPower = SerialLoRa.read();
|
||||
parametrosAtual.channel = SerialLoRa.read();
|
||||
parametrosAtual.tranModeAndWorCycle = SerialLoRa.read();
|
||||
|
||||
// Exibe os valores lidos
|
||||
Serial.println("Parametros lidos do módulo:");
|
||||
Serial.print("Address: 0x");
|
||||
Serial.println(parametrosAtual.address, HEX);
|
||||
|
||||
Serial.print("Baud and AirRate: 0x");
|
||||
Serial.println(parametrosAtual.baudAndAirRate, HEX);
|
||||
|
||||
Serial.print("Packet Size and Power: 0x");
|
||||
Serial.println(parametrosAtual.packetSizeAndPower, HEX);
|
||||
|
||||
Serial.print("Channel: 0x");
|
||||
Serial.println(parametrosAtual.channel, HEX);
|
||||
|
||||
Serial.print("TranMode and WorCycle: 0x");
|
||||
Serial.println(parametrosAtual.tranModeAndWorCycle, HEX);
|
||||
|
||||
// Ignorar os bytes restantes (Reservados, etc)
|
||||
for (int i = 0; i < (0x0B - 5); i++) {
|
||||
SerialLoRa.read();
|
||||
}
|
||||
|
||||
sucesso = true;
|
||||
}
|
||||
} else if (cabecalho == 0xFC) {
|
||||
Serial.println("Modo invalido!");
|
||||
}
|
||||
/*else {
|
||||
Serial.print("Resposta inesperada: 0x");
|
||||
Serial.println(cabecalho, HEX);
|
||||
Serial.println("Resposta recebida: ");
|
||||
while (SerialLoRa.available()) {
|
||||
uint8_t byteLido = SerialLoRa.read();
|
||||
Serial.print("0x");
|
||||
Serial.print(byteLido, HEX);
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println();
|
||||
}*/
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
|
||||
setLoRaMode(NORMAL);
|
||||
|
||||
return sucesso;
|
||||
}
|
||||
|
||||
// Limpa buffer anterior
|
||||
void limparBufferLoRa() {
|
||||
while (SerialLoRa.available()) {
|
||||
SerialLoRa.read();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue