303 lines
8.2 KiB
C++
303 lines
8.2 KiB
C++
#include "SerialService.h"
|
|
#include "Pinout.h"
|
|
#include "Utils.h"
|
|
#include "IComponenteCAN.h"
|
|
#include <SimpleKalmanFilter.h>
|
|
|
|
#ifndef SensorTemperaturaNtcModel
|
|
#define SensorTemperaturaNtcModel
|
|
|
|
class SensorTemperaturaNtc : public ComponenteCAN {
|
|
public:
|
|
String ID;
|
|
uint8_t ID_Num = 0;
|
|
PinoModel _pino;
|
|
bool _ntcGnd = false;
|
|
|
|
int leitura = 0;
|
|
double temperatura = -1;
|
|
double tensao = 0;
|
|
|
|
bool Iniciado = false;
|
|
|
|
float OffsetTemperatura = 0.0f;
|
|
|
|
SensorTemperaturaNtc() {}
|
|
|
|
SensorTemperaturaNtc(
|
|
const String& id,
|
|
uint8_t idNum,
|
|
int pino,
|
|
TiposBarramentos tipoBarramento,
|
|
int canalMux = -1,
|
|
float offsetTemperatura = 0.0f,
|
|
bool ntcGnd = false
|
|
) :
|
|
ID(id),
|
|
ID_Num(idNum),
|
|
_pino(PinoModel(pino, tipoBarramento, _INPUT, ANALOGICO, idNum, canalMux)),
|
|
OffsetTemperatura(offsetTemperatura),
|
|
_ntcGnd(ntcGnd)
|
|
{
|
|
}
|
|
|
|
void Inicializar() {
|
|
if (Iniciado) {
|
|
MostrarLog("Sensor ja inicializado");
|
|
return;
|
|
}
|
|
|
|
leitura = 0;
|
|
temperatura = -1;
|
|
tensao = 0;
|
|
|
|
_pino.Conectar();
|
|
_pino.ConfigurarLeituraAnalogica(12, ADC_11db);
|
|
|
|
Iniciado = _pino.Definido();
|
|
|
|
MostrarLog(Iniciado ? "Sensor iniciado" : "Sensor nao iniciado");
|
|
}
|
|
|
|
void Desligar() {
|
|
if (!Iniciado) {
|
|
MostrarLog("Sensor nao esta inicializado");
|
|
return;
|
|
}
|
|
|
|
_pino.Desconectar();
|
|
|
|
Iniciado = false;
|
|
|
|
MostrarLog("Sensor desligado");
|
|
}
|
|
|
|
void RequisitarDados() {
|
|
if (!Iniciado) return;
|
|
AferirTemperatura();
|
|
}
|
|
|
|
std::vector<uint8_t> MontarMensagemCAN(CanMessagePosicaoDados posicao) {
|
|
std::vector<uint8_t> data;
|
|
data.push_back(static_cast<uint8_t>(posicao));
|
|
data.push_back(ID_Num);
|
|
|
|
int snapLeitura;
|
|
double snapTemperatura;
|
|
double snapTensao;
|
|
portENTER_CRITICAL(&_snapshotLock);
|
|
snapLeitura = leitura;
|
|
snapTemperatura = temperatura;
|
|
snapTensao = tensao;
|
|
portEXIT_CRITICAL(&_snapshotLock);
|
|
|
|
switch (posicao) {
|
|
case CanMessagePosicaoDados::Status: {
|
|
data.push_back(Iniciado ? 1 : 0);
|
|
break;
|
|
}
|
|
|
|
case CanMessagePosicaoDados::Dados1: {
|
|
int16_t temperaturaInt =
|
|
isfinite(snapTemperatura)
|
|
? static_cast<int16_t>(round(snapTemperatura * 100.0f))
|
|
: INT16_MIN;
|
|
|
|
uint16_t tensaoMv =
|
|
isfinite(snapTensao) && snapTensao >= 0.0
|
|
? static_cast<uint16_t>(round(snapTensao * 1000.0))
|
|
: 0xFFFF;
|
|
|
|
uint16_t leituraAdc = static_cast<uint16_t>(snapLeitura);
|
|
|
|
data.push_back(static_cast<uint8_t>(leituraAdc >> 8));
|
|
data.push_back(static_cast<uint8_t>(leituraAdc & 0xFF));
|
|
|
|
data.push_back(static_cast<uint8_t>(temperaturaInt >> 8));
|
|
data.push_back(static_cast<uint8_t>(temperaturaInt & 0xFF));
|
|
|
|
data.push_back(static_cast<uint8_t>(tensaoMv >> 8));
|
|
data.push_back(static_cast<uint8_t>(tensaoMv & 0xFF));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
private:
|
|
bool DebugMode = false;
|
|
portMUX_TYPE _snapshotLock = portMUX_INITIALIZER_UNLOCKED;
|
|
// SimpleKalmanFilter filtro = SimpleKalmanFilter(5, 15, 0.01);
|
|
|
|
void MostrarLog(String mensagem) {
|
|
if (DebugMode) {
|
|
PrintTela("[TMP " + ID + "] " + mensagem);
|
|
}
|
|
}
|
|
|
|
void AferirTemperatura() {
|
|
constexpr int QuantidadeAmostras = 16;
|
|
|
|
int novaLeitura = _pino.get(QuantidadeAmostras);
|
|
const int tensaoMv = _pino.get(QuantidadeAmostras, FormatoLeituraAnalogica::Milivolts);
|
|
|
|
double novaTensao = NAN;
|
|
double novaTemperatura = NAN;
|
|
|
|
if (novaLeitura < 0 || tensaoMv < 0) {
|
|
novaLeitura = -1;
|
|
|
|
portENTER_CRITICAL(&_snapshotLock);
|
|
leitura = novaLeitura;
|
|
tensao = novaTensao;
|
|
temperatura = novaTemperatura;
|
|
portEXIT_CRITICAL(&_snapshotLock);
|
|
|
|
MostrarLog("Falha na leitura do sensor NTC");
|
|
return;
|
|
}
|
|
|
|
novaTensao = tensaoMv / 1000.0f;
|
|
|
|
// Detecta curto, pino flutuante, circuito aberto ou saturação.
|
|
if (novaLeitura <= 20 || novaLeitura >= 4080) {
|
|
portENTER_CRITICAL(&_snapshotLock);
|
|
leitura = novaLeitura;
|
|
tensao = novaTensao;
|
|
temperatura = novaTemperatura;
|
|
portEXIT_CRITICAL(&_snapshotLock);
|
|
|
|
MostrarLog("Leitura NTC fora da faixa | Raw: " + String(novaLeitura) + " | Tensao: " + String(novaTensao, 3) + " V");
|
|
return;
|
|
}
|
|
|
|
if (novaLeitura > 20 && tensaoMv < 0)
|
|
novaTemperatura = CalcularTemperatura(static_cast<float>(novaLeitura), OffsetTemperatura);
|
|
else
|
|
novaTemperatura = CalcularTemperaturaPorTensao(static_cast<float>(tensaoMv), _ntcGnd, OffsetTemperatura);
|
|
|
|
portENTER_CRITICAL(&_snapshotLock);
|
|
leitura = novaLeitura;
|
|
tensao = novaTensao;
|
|
temperatura = novaTemperatura;
|
|
portEXIT_CRITICAL(&_snapshotLock);
|
|
|
|
MostrarLog(
|
|
"Leitura: " + String(novaLeitura) +
|
|
" | Tensao: " + String(novaTensao, 3) + " V" +
|
|
" | Temperatura: " + String(novaTemperatura, 2) + " C"
|
|
);
|
|
}
|
|
|
|
float CalcularTemperatura(float leituraAdc, float offset = 0.0f) {
|
|
const float Vs = 5.0f;
|
|
const float Beta = 3950.0f;
|
|
const float To = 298.15f;
|
|
const float Ro = 10000.0f;
|
|
const float adcMax = 4095.0f;
|
|
static constexpr float divisor = 1.0f;
|
|
static constexpr float vOffset = (2.516f / 3.036f);
|
|
|
|
if (leituraAdc < 200.0f) return -1.0f;
|
|
|
|
float Vadc = leituraAdc / adcMax * Vs;
|
|
Vadc = Vadc * divisor * vOffset;
|
|
|
|
tensao = Vadc;
|
|
|
|
if (Vadc <= 0.001f) return -100.0f;
|
|
if (Vadc >= Vs) return NAN;
|
|
|
|
float Rt = Ro * (Vadc / (Vs - Vadc));
|
|
float T = 1.0f / (1.0f / To + log(Rt / Ro) / Beta);
|
|
float Tc = T - 273.15f + offset;
|
|
|
|
return roundf(Tc * 100.0f) / 100.0f;
|
|
}
|
|
|
|
float CalcularTemperaturaPorTensao(float tensaoAdcMv, bool ntcGnd, float offset = 0.0f) {
|
|
constexpr float Vs = 5.0f;
|
|
|
|
constexpr float Rfixo = 10000.0f; // Resistor interno do motor
|
|
|
|
// Valores reais da placa:
|
|
constexpr float Rserie = 4700.0f; // Retorno até o GPIO
|
|
constexpr float Rgnd = 6800.0f; // GPIO até o GND
|
|
constexpr float Rcarga = Rserie + Rgnd;
|
|
|
|
constexpr float Beta = 3950.0f;
|
|
constexpr float T0 = 298.15f; // 25 °C em kelvin
|
|
constexpr float Rntc25 = 10000.0f;
|
|
|
|
const float vAdc = tensaoAdcMv / 1000.0f;
|
|
|
|
if (!isfinite(vAdc) || vAdc <= 0.01f || vAdc >= 3.05f)
|
|
return NAN;
|
|
|
|
// Tensão no retorno do motor, antes do resistor de 4,7 kΩ.
|
|
const float vRetorno = vAdc * (Rserie + Rgnd) / Rgnd;
|
|
|
|
if (!isfinite(vRetorno) || vRetorno <= 0.01f || vRetorno >= Vs - 0.01f) {
|
|
return NAN;
|
|
}
|
|
|
|
float resistenciaNtc = NAN;
|
|
|
|
if (ntcGnd) {
|
|
/*
|
|
5 V
|
|
|
|
|
Rfixo
|
|
|
|
|
retorno
|
|
|
|
|
NTC
|
|
|
|
|
GND
|
|
A carga externa Rcarga fica em paralelo com o NTC.
|
|
*/
|
|
|
|
const float rEquivalente = Rfixo * vRetorno / (Vs - vRetorno);
|
|
const float inversoRntc = (1.0f / rEquivalente) - (1.0f / Rcarga);
|
|
if (!isfinite(inversoRntc) || inversoRntc <= 0.0f)
|
|
return NAN;
|
|
resistenciaNtc = 1.0f / inversoRntc;
|
|
}
|
|
else {
|
|
/*
|
|
5 V
|
|
|
|
|
NTC
|
|
|
|
|
retorno
|
|
|
|
|
Rfixo
|
|
|
|
|
GND
|
|
A carga externa Rcarga fica em paralelo com Rfixo.
|
|
*/
|
|
|
|
const float rInferior = (Rfixo * Rcarga) / (Rfixo + Rcarga);
|
|
resistenciaNtc = rInferior * (Vs - vRetorno) / vRetorno;
|
|
}
|
|
|
|
if (!isfinite(resistenciaNtc) || resistenciaNtc <= 0.0f)
|
|
return NAN;
|
|
|
|
const float temperaturaK = 1.0f / ((1.0f / T0) + logf(resistenciaNtc / Rntc25) / Beta);
|
|
const float temperaturaC = temperaturaK - 273.15f + offset;
|
|
|
|
if (!isfinite(temperaturaC) || temperaturaC < -40.0f || temperaturaC > 150.0f) {
|
|
return NAN;
|
|
}
|
|
|
|
return roundf(temperaturaC * 100.0f) / 100.0f;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|