From 6242d8b3dca53198b8ad84c86d2c4b1a8fc0d524 Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Tue, 4 Aug 2026 17:20:50 -0300 Subject: [PATCH] pequenos ajustes no sensor de temperatura e pinout --- Firmware/Modulos/Pinout.h | 118 +++++++++++++---- Firmware/Modulos/SensorTemperaturaNtcModel.h | 124 ++++++++++++++++-- .../Sensoriamento_v23/Sensoriamento_v23.ino | 8 +- 3 files changed, 212 insertions(+), 38 deletions(-) diff --git a/Firmware/Modulos/Pinout.h b/Firmware/Modulos/Pinout.h index 5e9c05c7a..10a8b57f8 100644 --- a/Firmware/Modulos/Pinout.h +++ b/Firmware/Modulos/Pinout.h @@ -7,6 +7,11 @@ #define PINO_INVALIDO 0xFF +enum class FormatoLeituraAnalogica : uint8_t { + Bruta, + Milivolts +}; + class PinoModel { public: int num; @@ -20,6 +25,35 @@ class PinoModel { PinoModel(int numero = PINO_INVALIDO, TiposBarramentos barr = CONTROLADOR, TiposPinos tp = _INPUT, TiposLeituras lt = DIGITAL, int _idSensor = 0, int canalMux = -1) : num(numero), barramento(barr), tipo(tp), leitura(lt), idSensor(_idSensor), _CanalMUX(canalMux) { } + bool ConfigurarLeituraAnalogica(uint8_t resolucaoBits = 12, adc_attenuation_t atenuacao = ADC_11db) { + if (!Conectado || barramento != TiposBarramentos::CONTROLADOR || leitura != TiposLeituras::ANALOGICO) { + return false; + } + + if (resolucaoBits < 9 || resolucaoBits > 12) { + MostrarLog( + "Resolucao ADC invalida no GPIO " + + String(num) + + ": " + + String(resolucaoBits) + + " bits" + ); + + return false; + } + + analogReadResolution(resolucaoBits); + analogSetPinAttenuation(num, atenuacao); + + MostrarLog( + "ADC configurado | GPIO: " + String(num) + + " | Resolucao: " + String(resolucaoBits) + + " bits | Atenuacao: " + String(static_cast(atenuacao)) + ); + + return true; + } + bool Definido() { bool barramentoIniciado = (barramento == TiposBarramentos::CONTROLADOR) || @@ -90,38 +124,72 @@ class PinoModel { Conectado = false; } - int get(int leituras = 1) { - if (Conectado) { - if (barramento == TiposBarramentos::CONTROLADOR) { + int get(int quantidadeLeituras = 1, FormatoLeituraAnalogica formato = FormatoLeituraAnalogica::Bruta) { + if (!Conectado || quantidadeLeituras <= 0) { + return -1; + } + + bool retornarMilivolts = formato == FormatoLeituraAnalogica::Milivolts; + + switch (barramento) { + case TiposBarramentos::CONTROLADOR: { if (leitura == TiposLeituras::DIGITAL) { - return digitalRead(num); - } - else if (leitura == TiposLeituras::ANALOGICO) { - double l = 0; - for (int i = 0; i < leituras; i++) { - vTaskDelay(1); - int leitura = analogRead(num); - MostrarLog("Leitura Controlador: " + String(leitura)); - l += leitura; - } - return l / leituras; - } - } - else if (barramento == TiposBarramentos::EXPANSOR_GPIO) { - if (I2CService::McpIniciado) { - if (!I2CService::SolicitarAcessoI2C(idSensor, _CanalMUX)) { + if (retornarMilivolts) { + MostrarLog("Leitura em mV solicitada para pino digital: " + String(num)); return -1; } - int leitura = I2CService::mcp.digitalRead(num); - I2CService::LiberarAcessoI2C(idSensor); - return leitura; + return digitalRead(num); } + + if (leitura == TiposLeituras::ANALOGICO) { + uint64_t soma = 0; + for (int i = 0; i < quantidadeLeituras; i++) { + if (i > 0) { + vTaskDelay(pdMS_TO_TICKS(1)); + } + const uint32_t valor = retornarMilivolts ? analogReadMilliVolts(num) : analogRead(num); + soma += valor; + } + const uint32_t media = static_cast(soma / static_cast(quantidadeLeituras)); + MostrarLog( + "Leitura Controlador | GPIO: " + String(num) + + " | Valor: " + String(media) + + (retornarMilivolts ? " mV" : " raw") + + " | Amostras: " + String(quantidadeLeituras) + ); + return media <= static_cast(INT_MAX) ? static_cast(media) : -1; + } + + return -1; } - else if (barramento == TiposBarramentos::EXPANSOR_ADS) { - return I2CService::RealizarLeituraADS(num, leituras, idSensor, _CanalMUX); + + case TiposBarramentos::EXPANSOR_GPIO: { + if (retornarMilivolts) { + MostrarLog("Leitura em mV nao suportada pelo expansor GPIO"); + return -1; + } + if (!I2CService::McpIniciado) { + return -1; + } + if (!I2CService::SolicitarAcessoI2C(idSensor, _CanalMUX)) { + return -1; + } + const int valor = I2CService::mcp.digitalRead(num); + I2CService::LiberarAcessoI2C(idSensor); + return valor; } + + case TiposBarramentos::EXPANSOR_ADS: { + if (retornarMilivolts) { + MostrarLog("Modo milivolts ainda nao definido para o ADS"); + return -1; + } + return I2CService::RealizarLeituraADS(num, quantidadeLeituras, idSensor, _CanalMUX); + } + + default: + return -1; } - return -1; } void set(bool status, bool reqMutex = true) { diff --git a/Firmware/Modulos/SensorTemperaturaNtcModel.h b/Firmware/Modulos/SensorTemperaturaNtcModel.h index 4ed9d663e..9d329a09b 100644 --- a/Firmware/Modulos/SensorTemperaturaNtcModel.h +++ b/Firmware/Modulos/SensorTemperaturaNtcModel.h @@ -12,6 +12,7 @@ class SensorTemperaturaNtc : public ComponenteCAN { String ID; uint8_t ID_Num = 0; PinoModel _pino; + bool _ntcGnd = false; int leitura = 0; double temperatura = -1; @@ -29,12 +30,14 @@ class SensorTemperaturaNtc : public ComponenteCAN { int pino, TiposBarramentos tipoBarramento, int canalMux = -1, - float offsetTemperatura = 0.0f + float offsetTemperatura = 0.0f, + bool ntcGnd = false ) : ID(id), ID_Num(idNum), _pino(PinoModel(pino, tipoBarramento, _INPUT, ANALOGICO, idNum, canalMux)), - OffsetTemperatura(offsetTemperatura) + OffsetTemperatura(offsetTemperatura), + _ntcGnd(ntcGnd) { } @@ -49,6 +52,7 @@ class SensorTemperaturaNtc : public ComponenteCAN { tensao = 0; _pino.Conectar(); + _pino.ConfigurarLeituraAnalogica(12, ADC_11db); Iniciado = _pino.Definido(); @@ -123,14 +127,36 @@ class SensorTemperaturaNtc : public ComponenteCAN { } void AferirTemperatura() { - leitura = _pino.get(1); - // leitura = filtro.updateEstimate(leitura); + constexpr int QuantidadeAmostras = 16; - temperatura = CalcularTemperatura((float)leitura, OffsetTemperatura); + leitura = _pino.get(QuantidadeAmostras); + const int tensaoMv = _pino.get(QuantidadeAmostras, FormatoLeituraAnalogica::Milivolts); - MostrarLog("Leitura: " + String(leitura) + - ", Tensao: " + String(tensao) + - ", Temperatura: " + String(temperatura)); + if (leitura < 0 || tensaoMv < 0) { + leitura = -1; + tensao = NAN; + temperatura = NAN; + + MostrarLog("Falha na leitura do sensor NTC"); + return; + } + + tensao = tensaoMv / 1000.0f; + + // Detecta curto, pino flutuante, circuito aberto ou saturação. + if (leitura <= 20 || leitura >= 4080) { + temperatura = NAN; + MostrarLog("Leitura NTC fora da faixa | Raw: " + String(leitura) + " | Tensao: " + String(tensao, 3) + " V"); + return; + } + + temperatura = CalcularTemperaturaPorTensao(static_cast(tensaoMv), _ntcGnd, OffsetTemperatura); + + MostrarLog( + "Leitura: " + String(leitura) + + " | Tensao: " + String(tensao, 3) + " V" + + " | Temperatura: " + String(temperatura, 2) + " C" + ); } float CalcularTemperatura(float leituraAdc, float offset = 0.0f) { @@ -158,7 +184,87 @@ class SensorTemperaturaNtc : public ComponenteCAN { 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; + } + + }; diff --git a/Firmware/Sensoriamento/Sensoriamento_v23/Sensoriamento_v23.ino b/Firmware/Sensoriamento/Sensoriamento_v23/Sensoriamento_v23.ino index adaab2053..e7976a054 100644 --- a/Firmware/Sensoriamento/Sensoriamento_v23/Sensoriamento_v23.ino +++ b/Firmware/Sensoriamento/Sensoriamento_v23/Sensoriamento_v23.ino @@ -88,10 +88,10 @@ Rele_v2* ObterRelePorId(uint8_t idNum) { #pragma endregion #pragma region TEMPERATURA NTC -SensorTemperaturaNtc sNTC_MotorET("TMVET", 1, 6, TiposBarramentos::CONTROLADOR, -1, 0.0f); -SensorTemperaturaNtc sNTC_MotorDT("TMVDT", 3, 7, TiposBarramentos::CONTROLADOR, -1, 0.0f); -SensorTemperaturaNtc sNTC_MotorEF("TMVEF", 2, 8, TiposBarramentos::CONTROLADOR, -1, 0.0f); -SensorTemperaturaNtc sNTC_MotorDF("TMVDF", 4, 9, TiposBarramentos::CONTROLADOR, -1, 0.0f); +SensorTemperaturaNtc sNTC_MotorET("TMVET", 1, 6, TiposBarramentos::CONTROLADOR, -1, 0.0f, false); +SensorTemperaturaNtc sNTC_MotorDT("TMVDT", 3, 7, TiposBarramentos::CONTROLADOR, -1, 0.0f, true); +SensorTemperaturaNtc sNTC_MotorEF("TMVEF", 2, 8, TiposBarramentos::CONTROLADOR, -1, 0.0f, false); +SensorTemperaturaNtc sNTC_MotorDF("TMVDF", 4, 9, TiposBarramentos::CONTROLADOR, -1, 0.0f, true); SensorTemperaturaNtc* ObterTempNtcPorId(uint8_t idNum) { switch (idNum) {