From c9b96c78618079a56be276a86554bd4b30cc2702 Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Mon, 15 Jun 2026 10:02:20 -0300 Subject: [PATCH] implementado recuperacao de barramento i2c --- Firmware/Modulos/I2CService.h | 1737 +++++++++++++++-- .../Sensoriamento_v23/Sensoriamento_v23.ino | 419 ++++ 2 files changed, 1940 insertions(+), 216 deletions(-) create mode 100644 Firmware/Sensoriamento/Sensoriamento_v23/Sensoriamento_v23.ino diff --git a/Firmware/Modulos/I2CService.h b/Firmware/Modulos/I2CService.h index 212b3c4af..888ebb4f1 100644 --- a/Firmware/Modulos/I2CService.h +++ b/Firmware/Modulos/I2CService.h @@ -2,20 +2,41 @@ #define I2CService_h #include "SerialService.h" +#include #include #include #include +#include #include class I2CService { public: - static const constexpr uint8_t enderecoMux = 0x70; - static const constexpr uint8_t enderecoMcp = 0x20; - static const constexpr uint8_t enderecoAds = 0x48; + static constexpr uint8_t enderecoMux = 0x70; + static constexpr uint8_t enderecoMcp = 0x20; + static constexpr uint8_t enderecoAds = 0x48; + + struct EstadoQuarentena { + uint8_t recuperacoesConfirmadas = 0; + uint32_t inicioJanelaMs = 0; + uint32_t ultimaOcorrenciaMs = 0; + bool bloqueado = false; + }; + + struct ContextoBarramento { + bool valido = false; + int idSensor = -1; + int canalMuxSolicitado = -1; + int canalMuxAtivo = -1; + uint8_t enderecoADS = 0xFF; + uint32_t inicioMs = 0; + }; + static bool I2CIniciado; static bool MuxIniciado; static bool McpIniciado; static bool AdsIniciado; + + // Mantidos publicos para compatibilidade com as firmwares atuais. static int canalAtivo; static int idAtualUsandoI2C; static bool i2cMutexReiniciando; @@ -23,421 +44,1705 @@ class I2CService { static Adafruit_MCP23X17 mcp; static Adafruit_ADS1115 ads; - static bool DefinirPinos(int sda, int scl) { + /* + * Todos os pinos extras sao opcionais. + * + * tcaRdy : READY open-drain do TCA4307 (entrada) + * tcaEn : EN do TCA4307 (saida, ativo em HIGH) + * muxRst : RESET_N do TCA9548A (saida, ativo em LOW) + * adsPwrEn : enable do load switch que alimenta o ADS1115 + * (saida, considerado ativo em HIGH) + */ + static bool DefinirPinos( + int sda, + int scl, + int tcaRdy = -1, + int tcaEn = -1, + int muxRst = -1, + int adsPwrEn = -1 + ) { bool mudou = false; + if (_pinoSDA != sda) { _pinoSDA = sda; mudou = true; } + if (_pinoSCL != scl) { _pinoSCL = scl; mudou = true; } + + if (_pinoTcaRdy != tcaRdy) { + _pinoTcaRdy = tcaRdy; + mudou = true; + } + + if (_pinoTcaEn != tcaEn) { + _pinoTcaEn = tcaEn; + mudou = true; + } + + if (_pinoMuxRst != muxRst) { + _pinoMuxRst = muxRst; + mudou = true; + } + + if (_pinoAdsPwrEn != adsPwrEn) { + _pinoAdsPwrEn = adsPwrEn; + mudou = true; + } + return mudou; } - static bool IniciarI2C() { - bool wireLiberado = WirePodeFinalizar(); + static void DefinirDebug(bool ativo) { + DebugMode = ativo; + } + + static void DefinirLimiteTempoI2C(uint32_t limiteMs) { + LimiteTempoI2C = limiteMs; + } + + static bool IniciarI2C(bool forcar = false) { + GarantirMutex(); + ConfigurarPinosControle(); + + bool barramentoLivre = forcar || BarramentoFisicamenteLivre(); + + // Primeiro tenta as recuperacoes mais localizadas, sem atribuir culpa + // durante a inicializacao. + if (!barramentoLivre && _pinoMuxRst >= 0) { + MostrarLog("I2C", "Barramento preso no inicio; resetando MUX"); + ResetarMuxSePossivel(); + barramentoLivre = AguardarBarramentoLivre(150); + } + + if (!barramentoLivre && _pinoAdsPwrEn >= 0) { + MostrarLog("I2C", "Barramento ainda preso; executando power-cycle do ADS"); + ResetarADSSePossivel(); + barramentoLivre = AguardarBarramentoLivre(150); + } + + if (!barramentoLivre && _pinoTcaEn >= 0) { + MostrarLog("I2C", "Barramento ainda preso; resetando TCA4307"); + ResetarTCA4307(false); + barramentoLivre = AguardarBarramentoLivre(200); + } + if (I2CIniciado) { - MostrarLog("I2C", "Tentando finalizar Wire..."); - if (wireLiberado) { - Wire.end(); - vTaskDelay(10); - MostrarLog("I2C", "Wire finalizado com sucesso"); - } else { - MostrarLog("I2C", "⚠️ Wire travado (SDA/SCL em LOW), nao foi finalizado"); - } + Wire.end(); + I2CIniciado = false; + DelayMs(5); } - if (wireLiberado) { - MostrarLog("I2C", "Iniciando Wire..."); - I2CIniciado = Wire.begin(_pinoSDA, _pinoSCL); - //Wire.setTimeout(50); - MostrarLog("I2C", "I2C inicializado nos pinos SDA=" + String(_pinoSDA) + " e SCL=" + String(_pinoSCL) + " - Res " + I2CIniciado); + + if (!barramentoLivre && (_pinoTcaEn < 0 || TCAReadyOk())) { + MostrarLog("I2C", "Tentando destravamento manual SDA/SCL antes do Wire.begin"); + DestravarBarramentoI2C(_pinoSDA, _pinoSCL); + barramentoLivre = AguardarBarramentoLivre(100); } - if (i2cMutex == nullptr) { - i2cMutex = xSemaphoreCreateMutex(); + + MostrarLog("I2C", "Iniciando Wire..."); + I2CIniciado = Wire.begin(_pinoSDA, _pinoSCL); + + if (I2CIniciado) { + Wire.setTimeOut(TimeoutWireMs); } + idAtualUsandoI2C = -1; tempoEntradaI2C = 0; + _readyLowDesdeMs = 0; + _falhasTransacaoConsecutivas = 0; + _recoveryPendente = false; + _motivoRecoveryPendente = ""; + + LimparContextoAtual(); + LimparContextoFalha(); + MarcarDispositivosParaRevalidacao(); + + MostrarLog( + "I2C", + "I2C inicializado | SDA=" + String(_pinoSDA) + + " SCL=" + String(_pinoSCL) + + " RDY=" + String(_pinoTcaRdy) + + " EN=" + String(_pinoTcaEn) + + " MUX_RST=" + String(_pinoMuxRst) + + " ADS_PWR_EN=" + String(_pinoAdsPwrEn) + + " linhas_livres=" + String(barramentoLivre ? 1 : 0) + + " res=" + String(I2CIniciado ? 1 : 0) + ); if (i2cTaskHandle == NULL) { - xTaskCreatePinnedToCore(I2CService::i2cTaskWrapper, "i2cTask", 4096, nullptr, 2, &i2cTaskHandle, tskNO_AFFINITY); + xTaskCreatePinnedToCore( + I2CService::i2cTaskWrapper, + "i2cTask", + 4096, + nullptr, + 2, + &i2cTaskHandle, + tskNO_AFFINITY + ); } return I2CIniciado; } - static bool VerificaEnderecoBarramento(byte _Endereco, uint32_t timeoutMs = 50) { - if (!I2CIniciado) return false; - - //Wire.setTimeout(timeoutMs); - unsigned long t0 = millis(); - - Wire.beginTransmission(_Endereco); - byte error = Wire.endTransmission(); - - if ((millis() - t0) > timeoutMs) { - MostrarLog("I2C", "Timeout verificando endereco " + String(_Endereco)); - return false; - } - - return error == 0; + static void VerificarSaudeBarramento() { + VerificarTCAReady(); + VerificarI2CPreso(); + VerificarRecoveryPendente(); } - static bool SolicitarAcessoI2C(int idSensor = 0, int canalMux = -1, uint32_t timeoutMs = 200) { + static bool TCA4307Configurado() { + return _pinoTcaEn >= 0 || _pinoTcaRdy >= 0; + } + + static bool TCA4307Ready() { + return TCAReadyOk(); + } + + static uint32_t TotalRecoveries() { + return _totalRecoveries; + } + + static uint32_t FalhasRecoveryConsecutivas() { + return _falhasRecoveryConsecutivas; + } + + static bool CanalMuxEmQuarentena(uint8_t canalGlobal) { + uint8_t indiceMux = canalGlobal / 10; + uint8_t canal = canalGlobal % 10; + + if (indiceMux > 7 || canal > 7) { + return true; + } + + bool bloqueado; + + portENTER_CRITICAL(&_estadoLock); + bloqueado = _saudeCanaisMux[indiceMux][canal].bloqueado; + portEXIT_CRITICAL(&_estadoLock); + + return bloqueado; + } + + static uint8_t RecuperacoesConfirmadasCanalMux(uint8_t canalGlobal) { + uint8_t indiceMux = canalGlobal / 10; + uint8_t canal = canalGlobal % 10; + + if (indiceMux > 7 || canal > 7) { + return 0; + } + + uint8_t total; + + portENTER_CRITICAL(&_estadoLock); + total = _saudeCanaisMux[indiceMux][canal].recuperacoesConfirmadas; + portEXIT_CRITICAL(&_estadoLock); + + return total; + } + + static bool ADSEmQuarentena(uint8_t endereco = enderecoAds) { + int indice = IndiceADS(endereco); + + if (indice < 0) { + return true; + } + + bool bloqueado; + + portENTER_CRITICAL(&_estadoLock); + bloqueado = _saudeADS[indice].bloqueado; + portEXIT_CRITICAL(&_estadoLock); + + return bloqueado; + } + + static uint8_t RecuperacoesConfirmadasADS(uint8_t endereco = enderecoAds) { + int indice = IndiceADS(endereco); + + if (indice < 0) { + return 0; + } + + uint8_t total; + + portENTER_CRITICAL(&_estadoLock); + total = _saudeADS[indice].recuperacoesConfirmadas; + portEXIT_CRITICAL(&_estadoLock); + + return total; + } + + static void DesbloquearCanalMux(uint8_t canalGlobal) { + uint8_t indiceMux = canalGlobal / 10; + uint8_t canal = canalGlobal % 10; + + if (indiceMux > 7 || canal > 7) { + return; + } + + portENTER_CRITICAL(&_estadoLock); + _saudeCanaisMux[indiceMux][canal] = EstadoQuarentena(); + portEXIT_CRITICAL(&_estadoLock); + + MostrarLog("MUX", "Quarentena removida | canalGlobal=" + String(canalGlobal)); + } + + static void DesbloquearADS(uint8_t endereco = enderecoAds) { + int indice = IndiceADS(endereco); + + if (indice < 0) { + return; + } + + portENTER_CRITICAL(&_estadoLock); + _saudeADS[indice] = EstadoQuarentena(); + portEXIT_CRITICAL(&_estadoLock); + + MostrarLog("ADS", "Quarentena removida | endereco=0x" + String(endereco, HEX)); + } + + static void LimparTodasQuarentenas() { + portENTER_CRITICAL(&_estadoLock); + + for (int mux = 0; mux < 8; mux++) { + for (int canal = 0; canal < 8; canal++) { + _saudeCanaisMux[mux][canal] = EstadoQuarentena(); + } + } + + for (int i = 0; i < 4; i++) { + _saudeADS[i] = EstadoQuarentena(); + } + + portEXIT_CRITICAL(&_estadoLock); + + MostrarLog("I2C", "Todas as quarentenas foram removidas"); + } + + static String StatusBarramento() { + String s = ""; + + s += "i2c=" + String(I2CIniciado ? 1 : 0); + s += " usando=" + String(idAtualUsandoI2C); + s += " recovery=" + String(_recuperacaoEmAndamento ? 1 : 0); + s += " pendente=" + String(_recoveryPendente ? 1 : 0); + s += " fails_tx=" + String(_falhasTransacaoConsecutivas); + s += " total_recovery=" + String(_totalRecoveries); + s += " q_mux=" + String(QuantidadeCanaisMuxEmQuarentena()); + s += " q_ads=" + String(QuantidadeADSEmQuarentena()); + + if (_pinoTcaRdy >= 0) { + s += " tca_rdy=" + String(digitalRead(_pinoTcaRdy) == HIGH ? 1 : 0); + } + + return s; + } + + static bool VerificaEnderecoBarramento(byte endereco, uint32_t timeoutMs = 50) { if (!I2CIniciado) { - MostrarLog("I2C", "Solicitacao de acesso negada para o sensor " + String(idSensor) + " - I2C Nao inicializado"); return false; } - if (i2cMutexReiniciando) return false; + if (!TCAReadyOk()) { + RegistrarFalhaTransacao("READY baixo ao verificar endereco 0x" + String(endereco, HEX)); + return false; + } - TickType_t timeoutTicks = pdMS_TO_TICKS(timeoutMs); - if (xSemaphoreTake(i2cMutex, timeoutTicks) != pdTRUE) { - MostrarLog("I2C", "Timeout ao tentar acessar o I2C - ID: " + String(idSensor)); + unsigned long t0 = millis(); + + Wire.beginTransmission(endereco); + byte error = Wire.endTransmission(); + + if ((uint32_t)(millis() - t0) > timeoutMs) { + MostrarLog("I2C", "Timeout verificando endereco 0x" + String(endereco, HEX)); + RegistrarFalhaTransacao("Timeout endereco 0x" + String(endereco, HEX)); + return false; + } + + if (error != 0) { + RegistrarFalhaTransacao( + "Endereco 0x" + String(endereco, HEX) + + " respondeu erro " + String(error) + ); + return false; + } + + RegistrarSucessoTransacao(); + return true; + } + + static bool SolicitarAcessoI2C( + int idSensor = 0, + int canalMux = -1, + uint32_t timeoutMs = 200 + ) { + if (!I2CIniciado) { + MostrarLog("I2C", "Acesso negado ID " + String(idSensor) + " | I2C nao inicializado"); + return false; + } + + if (canalMux >= 0 && CanalMuxEmQuarentena((uint8_t)canalMux)) { + MostrarLog( + "I2C", + "Acesso negado ID " + String(idSensor) + + " | Canal MUX em quarentena: " + String(canalMux) + ); + return false; + } + + if (i2cMutexReiniciando || _recuperacaoEmAndamento) { + MostrarLog("I2C", "Acesso negado ID " + String(idSensor) + " | Recovery em andamento"); + return false; + } + + if (!TCAReadyOk()) { + MostrarLog("I2C", "Acesso negado ID " + String(idSensor) + " | TCA4307 READY baixo"); + AgendarRecovery("READY baixo antes do acesso do ID " + String(idSensor)); + return false; + } + + GarantirMutex(); + + if (xSemaphoreTake(i2cMutex, pdMS_TO_TICKS(timeoutMs)) != pdTRUE) { + // Contencao do mutex nao prova falha eletrica no I2C. O watchdog + // do proprietario atual e quem decide se o barramento ficou preso. + MostrarLog("I2C", "Timeout ao tentar acessar I2C | ID=" + String(idSensor)); + return false; + } + + // Uma recuperacao pode ter comecado entre as verificacoes acima e o + // recebimento do mutex. + if (i2cMutexReiniciando || _recuperacaoEmAndamento || !I2CIniciado) { + xSemaphoreGive(i2cMutex); return false; } idAtualUsandoI2C = idSensor; tempoEntradaI2C = millis(); + PrepararContextoAtual(idSensor, canalMux); - // Troca canal se necessário - if (canalMux > -1 && !SelecionarCanalMux(canalMux)) { + if (canalMux >= 0 && !SelecionarCanalMux((uint8_t)canalMux)) { + MostrarLog( + "I2C", + "Falha ao selecionar canal mux " + String(canalMux) + + " | ID=" + String(idSensor) + ); + + bool precisaRecuperar = _recoveryPendente; + String motivo = _motivoRecoveryPendente; + + if (precisaRecuperar) { + CapturarContextoFalha(); + } + + LimparContextoAtual(); idAtualUsandoI2C = -1; - xSemaphoreGive(i2cMutex); // Libera caso a troca de canal falhe + tempoEntradaI2C = 0; + xSemaphoreGive(i2cMutex); + + if (precisaRecuperar) { + RecuperarBarramento(motivo, true); + } + return false; } + if (canalMux >= 0) { + ConfirmarCanalMuxNoContexto(canalMux); + } + return true; } static void LiberarAcessoI2C(int idSensor = 0) { - if (idSensor == idAtualUsandoI2C) { - idAtualUsandoI2C = -1; - xSemaphoreGive(i2cMutex); + if (idSensor != idAtualUsandoI2C || i2cMutex == nullptr) { + return; + } + + bool precisaRecuperar = _recoveryPendente; + String motivo = _motivoRecoveryPendente; + + if (precisaRecuperar) { + CapturarContextoFalha(); + i2cMutexReiniciando = true; + } + + LimparContextoAtual(); + idAtualUsandoI2C = -1; + tempoEntradaI2C = 0; + xSemaphoreGive(i2cMutex); + + if (precisaRecuperar) { + RecuperarBarramento(motivo, true); } } static void RecriarMutex() { + if (idAtualUsandoI2C >= 0 || _recuperacaoEmAndamento) { + MostrarLog("I2C", "Mutex nao recriado porque o barramento esta em uso"); + return; + } + i2cMutexReiniciando = true; - vSemaphoreDelete(i2cMutex); - i2cMutex = xSemaphoreCreateMutex(); - xSemaphoreGive(i2cMutex); - idAtualUsandoI2C = -1; + RecriarMutexSeguro(); i2cMutexReiniciando = false; - MostrarLog("I2C", "Mutex Recriado"); + + MostrarLog("I2C", "Mutex recriado manualmente"); } static bool WirePodeFinalizar() { - pinMode(_pinoSCL, INPUT_PULLUP); - pinMode(_pinoSDA, INPUT_PULLUP); - vTaskDelay(1); // garante leitura estável - return (digitalRead(_pinoSCL) == HIGH && digitalRead(_pinoSDA) == HIGH); + if (!I2CIniciado) { + pinMode(_pinoSCL, INPUT_PULLUP); + pinMode(_pinoSDA, INPUT_PULLUP); + DelayMs(1); + } + + return digitalRead(_pinoSCL) == HIGH && digitalRead(_pinoSDA) == HIGH; } static void DestravarBarramentoI2C(int sda, int scl) { - pinMode(scl, OUTPUT_OPEN_DRAIN); - pinMode(sda, INPUT_PULLUP); // SDA como entrada para ler + MostrarLog("I2C", "Gerando pulsos manuais para destravar SDA/SCL"); - for (int i = 0; i < 9; i++) { + pinMode(scl, OUTPUT_OPEN_DRAIN); + pinMode(sda, INPUT_PULLUP); + + digitalWrite(scl, HIGH); + delayMicroseconds(5); + + // 16 pulsos cobrem dispositivos que ficaram no meio de uma palavra + // e acompanham a margem utilizada pelo TCA4307. + for (int i = 0; i < 16; i++) { digitalWrite(scl, LOW); delayMicroseconds(5); digitalWrite(scl, HIGH); delayMicroseconds(5); + + if (digitalRead(sda) == HIGH && i >= 8) { + break; + } } - // Gera uma condição de STOP manual (SDA sobe enquanto SCL está alto) + // Condicao STOP manual. pinMode(sda, OUTPUT_OPEN_DRAIN); digitalWrite(sda, LOW); delayMicroseconds(5); digitalWrite(scl, HIGH); delayMicroseconds(5); digitalWrite(sda, HIGH); + delayMicroseconds(5); - // Espera estabilizar - delay(5); - - // Retorna controle ao Wire pinMode(scl, INPUT_PULLUP); pinMode(sda, INPUT_PULLUP); + DelayMs(5); } static int QuemEstaUsando() { return idAtualUsandoI2C; } - static bool LeituraSegura(uint8_t addr, uint8_t* buffer, size_t qtd, uint8_t reg = 0xFF, uint32_t timeoutMs = 50) { - //Wire.setTimeout(timeoutMs); + static bool LeituraSegura( + uint8_t addr, + uint8_t* buffer, + size_t qtd, + uint8_t reg = 0xFF, + uint32_t timeoutMs = 50 + ) { + if (!I2CIniciado || buffer == nullptr || qtd == 0) { + return false; + } + + if (!TCAReadyOk()) { + RegistrarFalhaTransacao("READY baixo antes de leitura 0x" + String(addr, HEX)); + return false; + } + unsigned long t0 = millis(); - + if (reg != 0xFF) { Wire.beginTransmission(addr); Wire.write(reg); - if (Wire.endTransmission(false) != 0) { - MostrarLog("I2C", "Falha ao endTransmission (leitura)"); + + int err = Wire.endTransmission(false); + + if (err != 0) { + MostrarLog( + "I2C", + "Falha endTransmission leitura | addr=0x" + + String(addr, HEX) + " err=" + String(err) + ); + RegistrarFalhaTransacao("Falha endTransmission leitura 0x" + String(addr, HEX)); return false; } - vTaskDelay(1); // <----- ADICIONAR este delay aqui } - + size_t received = Wire.requestFrom((int)addr, (int)qtd); - if (received != qtd || (millis() - t0) > timeoutMs) { - MostrarLog("I2C", "Timeout ou leitura incompleta"); + + if (received != qtd || (uint32_t)(millis() - t0) > timeoutMs) { + MostrarLog("I2C", "Timeout ou leitura incompleta | addr=0x" + String(addr, HEX)); + RegistrarFalhaTransacao("Timeout leitura 0x" + String(addr, HEX)); return false; } - + for (size_t i = 0; i < qtd; i++) { - if (Wire.available()) { - buffer[i] = Wire.read(); - } else { - MostrarLog("I2C", "Dados insuficientes disponíveis"); + if (!Wire.available()) { + MostrarLog("I2C", "Dados insuficientes | addr=0x" + String(addr, HEX)); + RegistrarFalhaTransacao("Dados insuficientes leitura 0x" + String(addr, HEX)); return false; } + + buffer[i] = Wire.read(); } - + + RegistrarSucessoTransacao(); return true; - } - - static bool EscritaSegura(uint8_t addr, const uint8_t* dados, size_t qtd, uint32_t timeoutMs = 50) { - //Wire.setTimeout(timeoutMs); + } + + static bool EscritaSegura( + uint8_t addr, + const uint8_t* dados, + size_t qtd, + uint32_t timeoutMs = 50 + ) { + if (!I2CIniciado || dados == nullptr || qtd == 0) { + return false; + } + + if (!TCAReadyOk()) { + RegistrarFalhaTransacao("READY baixo antes de escrita 0x" + String(addr, HEX)); + return false; + } + unsigned long t0 = millis(); - Wire.beginTransmission(addr); + for (size_t i = 0; i < qtd; i++) { Wire.write(dados[i]); } - + int resultado = Wire.endTransmission(); - if ((millis() - t0) > timeoutMs || resultado != 0) { - MostrarLog("I2C", "Falha na escrita ou timeout"); + + if ((uint32_t)(millis() - t0) > timeoutMs || resultado != 0) { + MostrarLog( + "I2C", + "Falha escrita | addr=0x" + String(addr, HEX) + + " err=" + String(resultado) + ); + RegistrarFalhaTransacao("Falha escrita 0x" + String(addr, HEX)); return false; } - + + RegistrarSucessoTransacao(); return true; } - static bool SelecionarCanalMux(uint8_t canalGlobal) { - uint8_t _enderecoMux = enderecoMux + (canalGlobal / 10); - uint8_t _canal = canalGlobal % 10; - return TrocarCanalMux(_enderecoMux, _canal); + uint8_t indiceMux = canalGlobal / 10; + uint8_t canal = canalGlobal % 10; + + if (indiceMux > 7 || canal > 7) { + MostrarLog("MUX", "Canal global invalido: " + String(canalGlobal)); + return false; + } + + if (CanalMuxEmQuarentena(canalGlobal)) { + MostrarLog("MUX", "Canal em quarentena: " + String(canalGlobal)); + return false; + } + + return TrocarCanalMux(enderecoMux + indiceMux, canal); } static bool TrocarCanalMux(uint8_t endereco, uint8_t canalMux) { - if (MuxIniciado && canalMux >= 0 && canalMux < 9) { - if (canalMux != canalAtivo) { - Wire.beginTransmission(endereco); - Wire.write(1 << canalMux); - int err = Wire.endTransmission(); - if (err != 0) { - MostrarLog("MUX", "Falha ao trocar canal, err=" + String(err)); - return false; - } - MostrarLog("MUX", "0x" + String(endereco) + " - Canal alterado de " + String(canalAtivo) + " para " + String(canalMux)); - canalAtivo = canalMux; + int indiceMux = IndiceMux(endereco); + + if (indiceMux < 0 || canalMux >= 8) { + MostrarLog( + "MUX", + "Endereco ou canal invalido | endereco=0x" + + String(endereco, HEX) + " canal=" + String(canalMux) + ); + return false; + } + + uint8_t canalGlobal = (indiceMux * 10) + canalMux; + + if (CanalMuxEmQuarentena(canalGlobal)) { + MostrarLog("MUX", "Canal bloqueado por quarentena | canalGlobal=" + String(canalGlobal)); + return false; + } + + if (!_muxValidado[indiceMux]) { + MostrarLog("MUX", "Validando MUX em 0x" + String(endereco, HEX)); + + if (!VerificaEnderecoBarramento(endereco)) { + MostrarLog("MUX", "MUX nao encontrado em 0x" + String(endereco, HEX)); + return false; } + + _muxValidado[indiceMux] = true; + MuxIniciado = true; + } + + if (enderecoMuxAtivo == endereco && canalAtivo == canalMux) { return true; } - MostrarLog("MUX", "Nao foi possivel trocar o canal para " + String(canalMux)); - return false; + + // Evita deixar dois ramais conectados simultaneamente. + if (enderecoMuxAtivo != 0xFF && enderecoMuxAtivo != endereco) { + Wire.beginTransmission(enderecoMuxAtivo); + Wire.write(0x00); + int erroDesabilitar = Wire.endTransmission(); + + if (erroDesabilitar != 0) { + MostrarLog( + "MUX", + "Falha ao desabilitar MUX anterior 0x" + + String(enderecoMuxAtivo, HEX) + + " | erro=" + String(erroDesabilitar) + ); + RegistrarFalhaTransacao("Falha ao desabilitar MUX anterior"); + return false; + } + + enderecoMuxAtivo = 0xFF; + canalAtivo = -1; + } + + Wire.beginTransmission(endereco); + Wire.write((uint8_t)(1U << canalMux)); + int erro = Wire.endTransmission(); + + if (erro != 0) { + MostrarLog( + "MUX", + "Falha ao trocar canal | endereco=0x" + String(endereco, HEX) + + " canal=" + String(canalMux) + " erro=" + String(erro) + ); + RegistrarFalhaTransacao("Falha ao trocar canal MUX"); + return false; + } + + enderecoMuxAtivo = endereco; + canalAtivo = canalMux; + RegistrarSucessoTransacao(); + + MostrarLog( + "MUX", + "Canal ativo | endereco=0x" + String(endereco, HEX) + + " canal=" + String(canalMux) + ); + + return true; } - static int SelecionarCanalADS(uint8_t canalGlobal) { - if (canalGlobal > 33) return -1; - uint8_t endereco = enderecoAds + (canalGlobal / 10); - uint8_t canal = canalGlobal % 10; - if (IniciarADS(endereco)) { - return canal; - } - else { + if (canalGlobal > 33) { return -1; } + + uint8_t endereco = enderecoAds + (canalGlobal / 10); + uint8_t canal = canalGlobal % 10; + + if (canal > 3) { + return -1; + } + + if (ADSEmQuarentena(endereco)) { + MostrarLog("ADS", "ADS em quarentena | endereco=0x" + String(endereco, HEX)); + return -1; + } + + MarcarEnderecoADSNoContexto(endereco); + + return IniciarADS(endereco) ? canal : -1; } static int RealizarLeituraADS(int canalGlobal, int leituras, int idSensor, int canalMux) { + if (leituras <= 0) { + return -1; + } + if (!SolicitarAcessoI2C(idSensor, canalMux)) { return -1; } - int canal = SelecionarCanalADS(canalGlobal); - if (canal > -1) { - uint16_t l = 0; - for (int i = 0; i < leituras; i++) { - vTaskDelay(1); - int leituraADC = (int)(ads.readADC_SingleEnded(canal)); - int leitura12bits = map(leituraADC, 0, 32767, 0, 4095); - MostrarLog("ADS", "Leitura: " + String(leituraADC) + ", Leitura 12 bits: " + String(leitura12bits)); - l += leitura12bits; - } - LiberarAcessoI2C(idSensor); - return (int)(l / leituras); - } - LiberarAcessoI2C(idSensor); - return -1; - } + int canal = SelecionarCanalADS(canalGlobal); + + if (canal < 0) { + LiberarAcessoI2C(idSensor); + return -1; + } + + int64_t soma = 0; + int leiturasConcluidas = 0; + bool leituraFalhou = false; + + for (int i = 0; i < leituras; i++) { + uint32_t inicioLeitura = millis(); + int leituraADC = (int)ads.readADC_SingleEnded(canal); + uint32_t duracaoLeitura = (uint32_t)(millis() - inicioLeitura); + + if (!TCAReadyOk() || duracaoLeitura > LimiteTempoI2C) { + RegistrarFalhaTransacao( + !TCAReadyOk() + ? "READY caiu durante leitura ADS" + : "Leitura ADS excedeu o limite de tempo" + ); + leituraFalhou = true; + break; + } + + int leitura12bits = map(leituraADC, 0, 32767, 0, 4095); + soma += leitura12bits; + leiturasConcluidas++; + + MostrarLog( + "ADS", + "Canal=" + String(canal) + + " ADC=" + String(leituraADC) + + " 12bits=" + String(leitura12bits) + ); + } + + if (_recoveryPendente) { + leituraFalhou = true; + } + + LiberarAcessoI2C(idSensor); + + if (leituraFalhou || leiturasConcluidas != leituras || leiturasConcluidas == 0) { + return -1; + } + + return (int)(soma / leiturasConcluidas); + } static void IniciarMUX(byte endereco = enderecoMux, bool Mandatorio = false) { if (!I2CIniciado) { - MostrarLog("MUX", "Impossivel iniciar TCA9548A, I2C nao iniciado!"); + MostrarLog("MUX", "Impossivel iniciar TCA9548A, I2C nao iniciado"); return; } - MostrarLog("MUX", "Iniciando TCA9548A..."); - if (!MuxIniciado) { + + int indiceMux = IndiceMux(endereco); + + if (indiceMux < 0) { + MostrarLog("MUX", "Endereco de MUX invalido: 0x" + String(endereco, HEX)); + return; + } + + if (_muxValidado[indiceMux]) { + MostrarLog("MUX", "TCA9548A ja iniciado em 0x" + String(endereco, HEX)); + return; + } + + while (true) { + MostrarLog("MUX", "Iniciando TCA9548A em 0x" + String(endereco, HEX)); + if (!SolicitarAcessoI2C(idSensorMux)) { - MostrarLog("MUX", "TCA9548A nao Iniciado, falha ao solicitar acesso ao barramento I2C"); - if (Mandatorio) { - MostrarLog("MUX", "Conexão obrigatória, tentando novamente..."); - IniciarMUX(endereco, false); + MostrarLog("MUX", "Falha ao solicitar acesso ao I2C"); + + if (!Mandatorio) { + return; } + + DelayMs(5000); + continue; + } + + bool iniciado = VerificaEnderecoBarramento(endereco); + + if (iniciado) { + _muxValidado[indiceMux] = true; + MuxIniciado = true; + enderecoMuxAtivo = 0xFF; + canalAtivo = -1; + } + + LiberarAcessoI2C(idSensorMux); + + if (iniciado) { + MostrarLog("MUX", "TCA9548A iniciado em 0x" + String(endereco, HEX)); return; } - MuxIniciado = VerificaEnderecoBarramento(endereco); - if (!MuxIniciado) { - MostrarLog("MUX", "TCA9548A nao Iniciado, endereco " + String(endereco) + " nao encontrado no barramento"); - if (Mandatorio) { - MostrarLog("MUX", "Conexão obrigatória, tentando novamente em 5 segundos..."); - vTaskDelay(5000); - IniciarMUX(endereco, Mandatorio); - } - else { - MostrarLog("MUX", "TCA9548A nao mandatorio, continuando o fluxo..."); - } + + MostrarLog("MUX", "TCA9548A nao encontrado em 0x" + String(endereco, HEX)); + + if (!Mandatorio) { + MostrarLog("MUX", "TCA9548A nao mandatorio, seguindo fluxo"); + return; } - else { - canalAtivo = -1; // Nenhum canal ativo ainda - MostrarLog("MUX", "TCA9548A Iniciado"); - } - LiberarAcessoI2C(idSensorMux); - } - else { - MostrarLog("MUX", "TCA9548A ja Iniciado"); + + MostrarLog("MUX", "Conexao obrigatoria, tentando novamente em 5 segundos"); + DelayMs(5000); } } static void IniciarMCP(byte endereco = enderecoMcp, bool Mandatorio = false) { if (!I2CIniciado) { - MostrarLog("MCP", "Impossivel iniciar MCP23X17, I2C nao iniciado!"); + MostrarLog("MCP", "Impossivel iniciar MCP23X17, I2C nao iniciado"); return; } - MostrarLog("MCP", "Iniciando MCP23X17..."); - if (!McpIniciado) { + + if (McpIniciado) { + MostrarLog("MCP", "MCP23X17 ja iniciado"); + return; + } + + while (true) { + MostrarLog("MCP", "Iniciando MCP23X17 em 0x" + String(endereco, HEX)); + if (!SolicitarAcessoI2C(idSensorMcp)) { - MostrarLog("MCP", "MCP23C17 nao Iniciado, falha ao solicitar acesso ao barramento I2C"); - if (Mandatorio) { - MostrarLog("MCP", "Conexão obrigatória, tentando novamente..."); - IniciarMCP(endereco, false); + MostrarLog("MCP", "Falha ao solicitar acesso ao I2C"); + + if (!Mandatorio) { + return; } + + DelayMs(5000); + continue; + } + + McpIniciado = mcp.begin_I2C(endereco, &Wire); + + if (!McpIniciado) { + RegistrarFalhaTransacao("Falha iniciar MCP23X17"); + } else { + RegistrarSucessoTransacao(); + } + + LiberarAcessoI2C(idSensorMcp); + + if (McpIniciado) { + MostrarLog("MCP", "MCP23X17 iniciado"); return; } - McpIniciado = mcp.begin_I2C(endereco, &Wire); - if (!McpIniciado) { - MostrarLog("MCP", "Erro ao iniciar MCP23X17!"); - if (Mandatorio) { - MostrarLog("MCP", "Conexão obrigatória, tentando novamente em 5 segundos..."); - vTaskDelay(5000); - IniciarMCP(endereco, Mandatorio); - } - else { - MostrarLog("MCP", "MCP23X17 nao mandatorio, continuando o fluxo..."); - } + + MostrarLog("MCP", "Erro ao iniciar MCP23X17"); + + if (!Mandatorio) { + MostrarLog("MCP", "MCP23X17 nao mandatorio, seguindo fluxo"); + return; } - else { - MostrarLog("MCP", "MCP23X17 Iniciado"); - } - LiberarAcessoI2C(idSensorMcp); - } - else { - MostrarLog("MCP", "MCP23X17 ja Iniciado"); + + MostrarLog("MCP", "Conexao obrigatoria, tentando novamente em 5 segundos"); + DelayMs(5000); } } - + static bool IniciarADS(byte endereco = enderecoAds) { if (!I2CIniciado) { - MostrarLog("ADS", "Impossivel iniciar ADS1115, I2C nao iniciado!"); + MostrarLog("ADS", "Impossivel iniciar ADS1115, I2C nao iniciado"); return false; } + + if (ADSEmQuarentena(endereco)) { + MostrarLog( + "ADS", + "Inicializacao recusada, ADS em quarentena | endereco=0x" + + String(endereco, HEX) + ); + return false; + } + + MarcarEnderecoADSNoContexto(endereco); + if (AdsIniciado && endereco == ultimoEnderecoADS) { - MostrarLog("ADS", "ADS1115 já iniciado no endereco 0x" + String(endereco, HEX)); return true; } - MostrarLog("ADS", "Iniciando ADS1115 no endereco 0x" + String(endereco, HEX) + "..."); + + MostrarLog("ADS", "Iniciando ADS1115 em 0x" + String(endereco, HEX)); AdsIniciado = ads.begin(endereco, &Wire); + if (!AdsIniciado) { ultimoEnderecoADS = 0xFF; - MostrarLog("ADS", "Erro ao iniciar ADS1115 no endereco 0x" + String(endereco, HEX)); + MostrarLog("ADS", "Erro ao iniciar ADS1115 em 0x" + String(endereco, HEX)); + RegistrarFalhaTransacao("Falha iniciar ADS1115"); + return false; } - else { - ads.setGain(GAIN_ONE); - MostrarLog("ADS", "ADS1115 Iniciado no endereco 0x" + String(endereco, HEX)); - ultimoEnderecoADS = endereco; - } - return AdsIniciado; + + ads.setGain(GAIN_ONE); + ultimoEnderecoADS = endereco; + + MostrarLog("ADS", "ADS1115 iniciado em 0x" + String(endereco, HEX)); + RegistrarSucessoTransacao(); + return true; } - private: static bool DebugMode; - static void MostrarLog(String Componente, String mensagem) { - if (DebugMode) { - PrintTela("[" + Componente + "] " + mensagem); - } - } - static int idSensorMux; static int idSensorMcp; static int _pinoSDA; static int _pinoSCL; + static int _pinoTcaRdy; + static int _pinoTcaEn; + static int _pinoMuxRst; + static int _pinoAdsPwrEn; + static unsigned long tempoEntradaI2C; static unsigned long LimiteTempoI2C; static byte ultimoEnderecoADS; - static SemaphoreHandle_t i2cMutex; + static uint8_t enderecoMuxAtivo; + static bool _muxValidado[8]; + static SemaphoreHandle_t i2cMutex; static TaskHandle_t i2cTaskHandle; + + static bool _recuperacaoEmAndamento; + static bool _recoveryPendente; + static String _motivoRecoveryPendente; + + static uint32_t _falhasTransacaoConsecutivas; + static uint32_t _falhasRecoveryConsecutivas; + static uint32_t _totalRecoveries; + + static unsigned long _ultimoRecoveryMs; + static unsigned long _readyLowDesdeMs; + + static EstadoQuarentena _saudeCanaisMux[8][8]; + static EstadoQuarentena _saudeADS[4]; + + static ContextoBarramento _contextoAtual; + static ContextoBarramento _contextoFalha; + static portMUX_TYPE _estadoLock; + + static constexpr uint32_t TimeoutWireMs = 80; + static constexpr uint32_t LimiteReadyLowMs = 200; + static constexpr uint32_t CooldownRecoveryMs = 1000; + static constexpr uint32_t LimiteFalhasTransacaoRecovery = 3; + static constexpr uint32_t MaxFalhasRecoveryAntesRestart = 3; + static constexpr uint32_t JanelaQuarentenaMs = 120000; + static constexpr uint8_t LimiteRecuperacoesMesmoAlvo = 3; + static constexpr uint32_t MargemHardRestartMs = 1500; + + static void MostrarLog(const String& componente, const String& mensagem) { + if (DebugMode) { + PrintTela("[" + componente + "] " + mensagem); + } + } + + static void DelayMs(uint32_t ms) { + vTaskDelay(pdMS_TO_TICKS(ms)); + } + + static bool TempoPassou(unsigned long inicio, uint32_t limiteMs) { + return (uint32_t)(millis() - inicio) >= limiteMs; + } + + static void GarantirMutex() { + if (i2cMutex == nullptr) { + i2cMutex = xSemaphoreCreateMutex(); + } + } + + static void RecriarMutexSeguro() { + if (i2cMutex != nullptr) { + vSemaphoreDelete(i2cMutex); + i2cMutex = nullptr; + } + + i2cMutex = xSemaphoreCreateMutex(); + idAtualUsandoI2C = -1; + tempoEntradaI2C = 0; + } + + static void ConfigurarPinosControle() { + if (_pinoTcaEn >= 0) { + pinMode(_pinoTcaEn, OUTPUT); + digitalWrite(_pinoTcaEn, HIGH); + } + + if (_pinoTcaRdy >= 0) { + pinMode(_pinoTcaRdy, INPUT_PULLUP); + } + + if (_pinoMuxRst >= 0) { + pinMode(_pinoMuxRst, OUTPUT); + digitalWrite(_pinoMuxRst, HIGH); + } + + if (_pinoAdsPwrEn >= 0) { + pinMode(_pinoAdsPwrEn, OUTPUT); + digitalWrite(_pinoAdsPwrEn, HIGH); + DelayMs(10); + } + } + + static bool TCAReadyOk() { + return _pinoTcaRdy < 0 || digitalRead(_pinoTcaRdy) == HIGH; + } + + static bool EsperarTCAReady(uint32_t timeoutMs) { + if (_pinoTcaRdy < 0) { + return true; + } + + unsigned long inicio = millis(); + + while (!TempoPassou(inicio, timeoutMs)) { + if (digitalRead(_pinoTcaRdy) == HIGH) { + return true; + } + + DelayMs(2); + } + + return digitalRead(_pinoTcaRdy) == HIGH; + } + + static bool ResetarTCA4307(bool logDetalhado = true) { + if (_pinoTcaEn < 0) { + if (logDetalhado) { + MostrarLog("TCA4307", "EN nao configurado, reset indisponivel"); + } + return false; + } + + if (logDetalhado) { + MostrarLog("TCA4307", "Resetando TCA4307 via EN"); + } + + digitalWrite(_pinoTcaEn, LOW); + DelayMs(10); + digitalWrite(_pinoTcaEn, HIGH); + DelayMs(5); + + bool ready = EsperarTCAReady(200); + + if (logDetalhado) { + MostrarLog("TCA4307", String("READY apos reset: ") + (ready ? "HIGH" : "LOW")); + } + + return ready; + } + + static bool ResetarMuxSePossivel() { + enderecoMuxAtivo = 0xFF; + canalAtivo = -1; + + if (_pinoMuxRst < 0) { + return false; + } + + MostrarLog("MUX", "Resetando TCA9548A via pino RESET_N"); + + digitalWrite(_pinoMuxRst, LOW); + DelayMs(2); + digitalWrite(_pinoMuxRst, HIGH); + DelayMs(5); + + MuxIniciado = false; + + for (int i = 0; i < 8; i++) { + _muxValidado[i] = false; + } + + return true; + } + + static bool ResetarADSSePossivel() { + if (_pinoAdsPwrEn < 0) { + return false; + } + + MostrarLog("ADS", "Executando power-cycle do ADS1115"); + + digitalWrite(_pinoAdsPwrEn, LOW); + DelayMs(30); + digitalWrite(_pinoAdsPwrEn, HIGH); + DelayMs(15); + + AdsIniciado = false; + ultimoEnderecoADS = 0xFF; + return true; + } + + static void MarcarDispositivosParaRevalidacao() { + canalAtivo = -1; + enderecoMuxAtivo = 0xFF; + + MuxIniciado = false; + McpIniciado = false; + AdsIniciado = false; + ultimoEnderecoADS = 0xFF; + + for (int i = 0; i < 8; i++) { + _muxValidado[i] = false; + } + } + + static int IndiceMux(uint8_t endereco) { + if (endereco < enderecoMux || endereco > enderecoMux + 7) { + return -1; + } + + return endereco - enderecoMux; + } + + static int IndiceADS(uint8_t endereco) { + if (endereco < enderecoAds || endereco > enderecoAds + 3) { + return -1; + } + + return endereco - enderecoAds; + } + + static int CanalGlobalAtivoAtual() { + int indiceMux = IndiceMux(enderecoMuxAtivo); + + if (indiceMux < 0 || canalAtivo < 0 || canalAtivo > 7) { + return -1; + } + + return (indiceMux * 10) + canalAtivo; + } + + static void PrepararContextoAtual(int idSensor, int canalMuxSolicitado) { + ContextoBarramento contexto; + contexto.valido = true; + contexto.idSensor = idSensor; + contexto.canalMuxSolicitado = canalMuxSolicitado; + contexto.canalMuxAtivo = CanalGlobalAtivoAtual(); + contexto.enderecoADS = 0xFF; + contexto.inicioMs = millis(); + + portENTER_CRITICAL(&_estadoLock); + _contextoAtual = contexto; + portEXIT_CRITICAL(&_estadoLock); + } + + static void ConfirmarCanalMuxNoContexto(int canalGlobal) { + portENTER_CRITICAL(&_estadoLock); + + if (_contextoAtual.valido) { + _contextoAtual.canalMuxAtivo = canalGlobal; + } + + portEXIT_CRITICAL(&_estadoLock); + } + + static void MarcarEnderecoADSNoContexto(uint8_t endereco) { + portENTER_CRITICAL(&_estadoLock); + + if (_contextoAtual.valido) { + _contextoAtual.enderecoADS = endereco; + } + + portEXIT_CRITICAL(&_estadoLock); + } + + static void CapturarContextoFalha() { + portENTER_CRITICAL(&_estadoLock); + + if (_contextoAtual.valido) { + _contextoFalha = _contextoAtual; + } + + portEXIT_CRITICAL(&_estadoLock); + } + + static ContextoBarramento ObterContextoFalha() { + ContextoBarramento contexto; + + portENTER_CRITICAL(&_estadoLock); + + if (_contextoFalha.valido) { + contexto = _contextoFalha; + } else { + contexto = _contextoAtual; + } + + portEXIT_CRITICAL(&_estadoLock); + return contexto; + } + + static void LimparContextoAtual() { + portENTER_CRITICAL(&_estadoLock); + _contextoAtual = ContextoBarramento(); + portEXIT_CRITICAL(&_estadoLock); + } + + static void LimparContextoFalha() { + portENTER_CRITICAL(&_estadoLock); + _contextoFalha = ContextoBarramento(); + portEXIT_CRITICAL(&_estadoLock); + } + + static int CanalSuspeitoDoContexto(const ContextoBarramento& contexto) { + if (contexto.canalMuxAtivo >= 0) { + return contexto.canalMuxAtivo; + } + + return contexto.canalMuxSolicitado; + } + + static bool BarramentoFisicamenteLivre() { + return TCAReadyOk() && WirePodeFinalizar(); + } + + static bool AguardarBarramentoLivre(uint32_t timeoutMs) { + uint32_t inicio = millis(); + + while (!TempoPassou(inicio, timeoutMs)) { + if (BarramentoFisicamenteLivre()) { + return true; + } + + DelayMs(2); + } + + return BarramentoFisicamenteLivre(); + } + + static void AgendarRecovery(const String& motivo) { + CapturarContextoFalha(); + _recoveryPendente = true; + _motivoRecoveryPendente = motivo; + } + + static void RegistrarResponsavelCanalMux(uint8_t canalGlobal) { + uint8_t indiceMux = canalGlobal / 10; + uint8_t canal = canalGlobal % 10; + + if (indiceMux > 7 || canal > 7) { + return; + } + + uint32_t agora = millis(); + uint8_t total; + bool bloqueado; + + portENTER_CRITICAL(&_estadoLock); + EstadoQuarentena& estado = _saudeCanaisMux[indiceMux][canal]; + + if ( + estado.inicioJanelaMs == 0 || + (uint32_t)(agora - estado.inicioJanelaMs) > JanelaQuarentenaMs + ) { + estado.recuperacoesConfirmadas = 0; + estado.inicioJanelaMs = agora; + } + + if (estado.recuperacoesConfirmadas < 255) { + estado.recuperacoesConfirmadas++; + } + + estado.ultimaOcorrenciaMs = agora; + + if (estado.recuperacoesConfirmadas >= LimiteRecuperacoesMesmoAlvo) { + estado.bloqueado = true; + } + + total = estado.recuperacoesConfirmadas; + bloqueado = estado.bloqueado; + portEXIT_CRITICAL(&_estadoLock); + + MostrarLog( + "MUX", + "Recovery atribuido ao canal " + String(canalGlobal) + + " | ocorrencias=" + String(total) + + " | quarentena=" + String(bloqueado ? "SIM" : "NAO") + ); + } + + static void RegistrarResponsavelADS(uint8_t endereco) { + int indice = IndiceADS(endereco); + + if (indice < 0) { + return; + } + + uint32_t agora = millis(); + uint8_t total; + bool bloqueado; + + portENTER_CRITICAL(&_estadoLock); + EstadoQuarentena& estado = _saudeADS[indice]; + + if ( + estado.inicioJanelaMs == 0 || + (uint32_t)(agora - estado.inicioJanelaMs) > JanelaQuarentenaMs + ) { + estado.recuperacoesConfirmadas = 0; + estado.inicioJanelaMs = agora; + } + + if (estado.recuperacoesConfirmadas < 255) { + estado.recuperacoesConfirmadas++; + } + + estado.ultimaOcorrenciaMs = agora; + + if (estado.recuperacoesConfirmadas >= LimiteRecuperacoesMesmoAlvo) { + estado.bloqueado = true; + } + + total = estado.recuperacoesConfirmadas; + bloqueado = estado.bloqueado; + portEXIT_CRITICAL(&_estadoLock); + + MostrarLog( + "ADS", + "Recovery atribuido ao endereco 0x" + String(endereco, HEX) + + " | ocorrencias=" + String(total) + + " | quarentena=" + String(bloqueado ? "SIM" : "NAO") + ); + } + + static uint8_t QuantidadeCanaisMuxEmQuarentena() { + uint8_t total = 0; + + portENTER_CRITICAL(&_estadoLock); + + for (int mux = 0; mux < 8; mux++) { + for (int canal = 0; canal < 8; canal++) { + if (_saudeCanaisMux[mux][canal].bloqueado) { + total++; + } + } + } + + portEXIT_CRITICAL(&_estadoLock); + return total; + } + + static uint8_t QuantidadeADSEmQuarentena() { + uint8_t total = 0; + + portENTER_CRITICAL(&_estadoLock); + + for (int i = 0; i < 4; i++) { + if (_saudeADS[i].bloqueado) { + total++; + } + } + + portEXIT_CRITICAL(&_estadoLock); + return total; + } + + static void RegistrarFalhaTransacao(const String& motivo) { + _falhasTransacaoConsecutivas++; + + MostrarLog( + "I2C", + "Falha transacao #" + String(_falhasTransacaoConsecutivas) + + " | " + motivo + ); + + if (_falhasTransacaoConsecutivas >= LimiteFalhasTransacaoRecovery) { + AgendarRecovery("Falhas consecutivas no I2C: " + motivo); + } + } + + static void RegistrarSucessoTransacao() { + _falhasTransacaoConsecutivas = 0; + } + + static bool RecuperarBarramento(const String& motivo, bool ignorarCooldown = false) { + if (_recuperacaoEmAndamento) { + return false; + } + + if ( + !ignorarCooldown && + _ultimoRecoveryMs > 0 && + !TempoPassou(_ultimoRecoveryMs, CooldownRecoveryMs) + ) { + return false; + } + + _recuperacaoEmAndamento = true; + i2cMutexReiniciando = true; + _ultimoRecoveryMs = millis(); + _totalRecoveries++; + + MostrarLog("I2C", "Recovery iniciado | Motivo: " + motivo); + + GarantirMutex(); + + if (xSemaphoreTake(i2cMutex, pdMS_TO_TICKS(150)) != pdTRUE) { + MostrarLog("I2C", "Recovery adiado porque o mutex ainda esta ocupado"); + _recoveryPendente = true; + i2cMutexReiniciando = false; + _recuperacaoEmAndamento = false; + return false; + } + + ContextoBarramento contexto = ObterContextoFalha(); + int canalSuspeito = CanalSuspeitoDoContexto(contexto); + + bool travadoInicialmente = !BarramentoFisicamenteLivre(); + bool barramentoLivre = !travadoInicialmente; + bool responsavelConfirmado = false; + + // 1) Isola primeiro o ramal do MUX, que e a acao mais localizada. + if (travadoInicialmente && canalSuspeito >= 0 && _pinoMuxRst >= 0) { + ResetarMuxSePossivel(); + barramentoLivre = AguardarBarramentoLivre(150); + + if (barramentoLivre) { + RegistrarResponsavelCanalMux((uint8_t)canalSuspeito); + responsavelConfirmado = true; + } + } + + // 2) Se o MUX nao resolveu e o ADS estava envolvido, faz power-cycle. + if (!barramentoLivre && contexto.enderecoADS != 0xFF && _pinoAdsPwrEn >= 0) { + ResetarADSSePossivel(); + barramentoLivre = AguardarBarramentoLivre(150); + + if (barramentoLivre) { + RegistrarResponsavelADS(contexto.enderecoADS); + responsavelConfirmado = true; + } + } + + // Em falhas logicas, sem linha fisicamente presa, reinicia o alvo + // relacionado, mas nao soma ponto de quarentena. + if (!travadoInicialmente) { + if (canalSuspeito >= 0 && _pinoMuxRst >= 0) { + ResetarMuxSePossivel(); + } else if (contexto.enderecoADS != 0xFF && _pinoAdsPwrEn >= 0) { + ResetarADSSePossivel(); + } + + barramentoLivre = AguardarBarramentoLivre(100); + } + + // 3) Recuperacao global pelo TCA4307. + if (!barramentoLivre && _pinoTcaEn >= 0) { + ResetarTCA4307(true); + barramentoLivre = AguardarBarramentoLivre(200); + } + + // Para bit-bang, primeiro tira o periferico Wire do controle dos pinos. + if (I2CIniciado) { + Wire.end(); + I2CIniciado = false; + DelayMs(5); + } + + // 4) Fallback manual. Com TCA desconectado (READY baixo), pulsar o lado + // do ESP nao alcanca o lado OUT, portanto so tenta quando faz sentido. + if (!barramentoLivre && (_pinoTcaEn < 0 || TCAReadyOk())) { + DestravarBarramentoI2C(_pinoSDA, _pinoSCL); + barramentoLivre = AguardarBarramentoLivre(100); + } + + bool wireReiniciado = false; + + if (barramentoLivre) { + I2CIniciado = Wire.begin(_pinoSDA, _pinoSCL); + + if (I2CIniciado) { + Wire.setTimeOut(TimeoutWireMs); + wireReiniciado = true; + } + } + + MarcarDispositivosParaRevalidacao(); + + bool ok = wireReiniciado && TCAReadyOk() && BarramentoFisicamenteLivre(); + + _readyLowDesdeMs = 0; + _falhasTransacaoConsecutivas = 0; + idAtualUsandoI2C = -1; + tempoEntradaI2C = 0; + LimparContextoAtual(); + + if (ok) { + _recoveryPendente = false; + _motivoRecoveryPendente = ""; + _falhasRecoveryConsecutivas = 0; + LimparContextoFalha(); + } else { + _recoveryPendente = true; + _motivoRecoveryPendente = "Nova tentativa apos recovery sem sucesso"; + _falhasRecoveryConsecutivas++; + } + + xSemaphoreGive(i2cMutex); + i2cMutexReiniciando = false; + _recuperacaoEmAndamento = false; + + if (ok) { + MostrarLog( + "I2C", + String("Recovery concluido com sucesso") + + (responsavelConfirmado ? " | responsavel identificado" : " | responsavel nao confirmado") + ); + } else { + MostrarLog( + "I2C", + "Recovery falhou | falhas_consecutivas=" + + String(_falhasRecoveryConsecutivas) + ); + + if (_falhasRecoveryConsecutivas >= MaxFalhasRecoveryAntesRestart) { + MostrarLog("I2C", "Limite de recoveries excedido, reiniciando ESP"); + DelayMs(100); + ESP.restart(); + } + } + + return ok; + } + + static void VerificarTCAReady() { + if (_pinoTcaRdy < 0 || _recuperacaoEmAndamento) { + return; + } + + bool ready = digitalRead(_pinoTcaRdy) == HIGH; + + if (ready) { + _readyLowDesdeMs = 0; + return; + } + + if (_readyLowDesdeMs == 0) { + _readyLowDesdeMs = millis(); + return; + } + + if (!TempoPassou(_readyLowDesdeMs, LimiteReadyLowMs)) { + return; + } + + String motivo = + "TCA4307 READY baixo por " + + String(LimiteReadyLowMs) + "ms"; + + if (idAtualUsandoI2C >= 0) { + AgendarRecovery(motivo); + return; + } + + AgendarRecovery(motivo); + } + + static void VerificarI2CPreso() { + if (_recuperacaoEmAndamento || idAtualUsandoI2C < 0) { + return; + } + + uint32_t tempoPreso = (uint32_t)(millis() - tempoEntradaI2C); + + if (tempoPreso <= LimiteTempoI2C) { + return; + } + + if (!_recoveryPendente) { + String motivo = + "Acesso I2C preso pelo ID " + + String(idAtualUsandoI2C) + + " por " + String(tempoPreso) + "ms"; + + MostrarLog("I2C", motivo); + AgendarRecovery(motivo); + } + + // Nunca apaga o mutex enquanto outra task pode estar dentro do Wire. + // O timeout do Wire deve permitir o retorno. Se nem isso acontecer, + // o restart completo e o ultimo recurso seguro. + if (tempoPreso > LimiteTempoI2C + MargemHardRestartMs) { + MostrarLog( + "I2C", + "Task I2C nao retornou apos o timeout; reiniciando ESP por seguranca" + ); + DelayMs(100); + ESP.restart(); + } + } + + static void VerificarRecoveryPendente() { + if ( + !_recoveryPendente || + _recuperacaoEmAndamento || + idAtualUsandoI2C >= 0 + ) { + return; + } + + RecuperarBarramento(_motivoRecoveryPendente, false); + } + static void i2cTaskWrapper(void *pvParameters) { - I2CService::i2cTask(); + (void)pvParameters; + I2CService::i2cTask(); } static void i2cTask() { while (true) { - VerificarI2CPreso(); - vTaskDelay(1); + VerificarSaudeBarramento(); + DelayMs(50); } } - - static void VerificarI2CPreso() { - if (idAtualUsandoI2C >= 0 && (millis() - tempoEntradaI2C > LimiteTempoI2C)) { - MostrarLog("I2C", "Semáforo preso pelo ID " + String(idAtualUsandoI2C) + " — forçando liberação!"); - ESP.restart(); - //RecriarMutex(); - //DestravarBarramentoI2C(_pinoSDA, _pinoSCL); - //IniciarI2C(); - } - } - }; bool I2CService::DebugMode = false; + int I2CService::_pinoSDA = 1; int I2CService::_pinoSCL = 2; +int I2CService::_pinoTcaRdy = -1; +int I2CService::_pinoTcaEn = -1; +int I2CService::_pinoMuxRst = -1; +int I2CService::_pinoAdsPwrEn = -1; + unsigned long I2CService::LimiteTempoI2C = 2000; + bool I2CService::I2CIniciado = false; bool I2CService::MuxIniciado = false; bool I2CService::McpIniciado = false; +bool I2CService::AdsIniciado = false; + int I2CService::idSensorMux = 200; int I2CService::idSensorMcp = 201; + Adafruit_MCP23X17 I2CService::mcp; Adafruit_ADS1115 I2CService::ads; + byte I2CService::ultimoEnderecoADS = 0xFF; -bool I2CService::AdsIniciado = false; +uint8_t I2CService::enderecoMuxAtivo = 0xFF; + +bool I2CService::_muxValidado[8] = { + false, false, false, false, + false, false, false, false +}; + int I2CService::canalAtivo = -1; int I2CService::idAtualUsandoI2C = -1; unsigned long I2CService::tempoEntradaI2C = 0; + SemaphoreHandle_t I2CService::i2cMutex = nullptr; TaskHandle_t I2CService::i2cTaskHandle = NULL; + bool I2CService::i2cMutexReiniciando = false; +bool I2CService::_recuperacaoEmAndamento = false; +bool I2CService::_recoveryPendente = false; + +String I2CService::_motivoRecoveryPendente = ""; + +uint32_t I2CService::_falhasTransacaoConsecutivas = 0; +uint32_t I2CService::_falhasRecoveryConsecutivas = 0; +uint32_t I2CService::_totalRecoveries = 0; + +unsigned long I2CService::_ultimoRecoveryMs = 0; +unsigned long I2CService::_readyLowDesdeMs = 0; + +I2CService::EstadoQuarentena I2CService::_saudeCanaisMux[8][8] = {}; +I2CService::EstadoQuarentena I2CService::_saudeADS[4] = {}; + +I2CService::ContextoBarramento I2CService::_contextoAtual = {}; +I2CService::ContextoBarramento I2CService::_contextoFalha = {}; + +portMUX_TYPE I2CService::_estadoLock = portMUX_INITIALIZER_UNLOCKED; #endif diff --git a/Firmware/Sensoriamento/Sensoriamento_v23/Sensoriamento_v23.ino b/Firmware/Sensoriamento/Sensoriamento_v23/Sensoriamento_v23.ino new file mode 100644 index 000000000..7537d7812 --- /dev/null +++ b/Firmware/Sensoriamento/Sensoriamento_v23/Sensoriamento_v23.ino @@ -0,0 +1,419 @@ +// Dispositivo: Sensoriamento +// Versão Firmware: 23 +// Ultima atualização: 15/06/2026 +// Atualização: Implementado melhoria na recuperação do barramento I2C + +#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\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\ServoAgroModel_v2.h" +#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\ReleModel_v2.h" +#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\SinaleiroModel_v2.h" +#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\SensorTemperaturaNtcModel.h" +#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\SensorCorrenteModel_v2.h" +#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\SensorMagneticoModel_v2.h" + + +// Configurações do CAN +uint8_t CanID = 0x11; +CanService canService(CanID); + +//LoRaService loraService(&canService); + +#define D_Code Sen +int VERSION = 23; +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; + +#pragma region SINALEIROS +Sinaleiro_v2 sLED_Manual("LEDMN", 21, 37, TiposBarramentos::CONTROLADOR); +Sinaleiro_v2 sLED_Automatico("LEDAT", 22, 35, TiposBarramentos::CONTROLADOR); +Sinaleiro_v2 sLED_Operacao("LEDOP", 23, 36, TiposBarramentos::CONTROLADOR); + +Sinaleiro_v2* ObterSinaleiroPorId(uint8_t idNum) { + switch (idNum) { + case 21: return &sLED_Manual; + case 22: return &sLED_Automatico; + case 23: return &sLED_Operacao; + default: return nullptr; + } +} + +LedManager ledManager; +void LedTask(void* pv) { + while (true) { + ledManager.Atualizar(); + vTaskDelay(pdMS_TO_TICKS(10)); // 100 Hz + } +} +#pragma endregion + +#pragma region SERVOS +ServoAgro_v2 sSRV_FreioET("FROET", 41, true, 45, TiposBarramentos::CONTROLADOR); +ServoAgro_v2 sSRV_FreioDT("FRODT", 43, false, 47, TiposBarramentos::CONTROLADOR); + +ServoAgro_v2* ObterServoPorId(uint8_t idNum) { + switch (idNum) { + case 41: return &sSRV_FreioET; + case 43: return &sSRV_FreioDT; + default: return nullptr; + } +} +#pragma endregion + +#pragma region RELES +Rele_v2 sRLE_CoolerIn("RLCIN", 81, true, 19, TiposBarramentos::CONTROLADOR); +Rele_v2 sRLE_CoolerOut("RLCOUT", 82, true, 20, TiposBarramentos::CONTROLADOR); + +Rele_v2* ObterRelePorId(uint8_t idNum) { + switch (idNum) { + case 81: return &sRLE_CoolerIn; + case 82: return &sRLE_CoolerOut; + default: return nullptr; + } +} +#pragma endregion + +#pragma region TEMPERATURA NTC +SensorTemperaturaNtc sNTC_MotorET("TMVET", 1, 0, TiposBarramentos::EXPANSOR_ADS, -1, 0.0f); +SensorTemperaturaNtc sNTC_MotorDT("TMVDT", 3, 1, TiposBarramentos::EXPANSOR_ADS, -1, 0.0f); +SensorTemperaturaNtc sNTC_MotorEF("TMVEF", 2, 2, TiposBarramentos::EXPANSOR_ADS, -1, 0.0f); +SensorTemperaturaNtc sNTC_MotorDF("TMVDF", 4, 3, TiposBarramentos::EXPANSOR_ADS, -1, 0.0f); + +SensorTemperaturaNtc* ObterTempNtcPorId(uint8_t idNum) { + switch (idNum) { + case 1: return &sNTC_MotorET; + case 2: return &sNTC_MotorEF; + case 3: return &sNTC_MotorDT; + case 4: return &sNTC_MotorDF; + default: return nullptr; + } +} +#pragma endregion + +#pragma region CORRENTE +SensorCorrente_v2 sCOR_3V0 ("COR_AB3V", 11, 0x40, TiposShuntCorrente::INA219_3A2, 5); +SensorCorrente_v2 sCOR_5V0 ("COR_AB5V", 12, 0x40, TiposShuntCorrente::INA219_3A2, 4); +SensorCorrente_v2 sCOR_7V2 ("COR_AB7V2", 13, 0x40, TiposShuntCorrente::INA219_3A2, 2); +SensorCorrente_v2 sCOR_12V ("COR_AB12V", 14, 0x40, TiposShuntCorrente::INA228_50A, 3); +SensorCorrente_v2 sCOR_19V ("COR_AB19V", 15, 0x40, TiposShuntCorrente::INA228_10A, 1); +SensorCorrente_v2 sCOR_24V ("COR_AB24V", 16, 0x40, TiposShuntCorrente::INA228_50A, 6); +SensorCorrente_v2 sCOR_36V ("COR_AB36V", 17, 0x40, TiposShuntCorrente::INA228_100A, 7); + +SensorCorrente_v2* ObterSensorCorrentePorId(uint8_t idNum) { + switch (idNum) { + case 11: return &sCOR_3V0; + case 12: return &sCOR_5V0; + case 13: return &sCOR_7V2; + case 14: return &sCOR_12V; + case 15: return &sCOR_19V; + case 16: return &sCOR_24V; + case 17: return &sCOR_36V; + default: return nullptr; + } +} +#pragma endregion + +#pragma region MAGNETICOS +SensorMagnetico_v2 sMAG_MotorET("MAG_ET", 91, 0x36, 1); +SensorMagnetico_v2 sMAG_MotorDT("MAG_DT", 92, 0x36, 2); +SensorMagnetico_v2 sMAG_MotorEF("MAG_EF", 93, 0x36, 3); +SensorMagnetico_v2 sMAG_MotorDF("MAG_DF", 94, 0x36, 4); + +SensorMagnetico_v2* ObterSensorMagneticoPorId(uint8_t idNum) { + switch (idNum) { + case 91: return &sMAG_MotorET; + case 92: return &sMAG_MotorDT; + case 93: return &sMAG_MotorEF; + case 94: return &sMAG_MotorDF; + default: return nullptr; + } +} +#pragma endregion + +void MostrarLog(String mensagem) { + bool _debugMode = false; + if (_debugMode) { + PrintTela(String("[SEN] ") + mensagem); + } +} + +void inicializarDependenciasI2C() { + I2CService::DefinirPinos(1, 2, 41, 42, 39, 40); + I2CService::IniciarI2C(); + I2CService::IniciarADS(); + I2CService::IniciarMUX(); + I2CService::IniciarMCP(); +} + +void setup() { + delay(2000); + + inicializarDependenciasI2C(); + + sSRV_FreioET.Inicializar(); + sSRV_FreioDT.Inicializar(); + sRLE_CoolerIn.Inicializar(); + sRLE_CoolerOut.Inicializar(); + sLED_Manual.Inicializar(); + sLED_Automatico.Inicializar(); + sLED_Operacao.Inicializar(); + sNTC_MotorET.Inicializar(); + sNTC_MotorDT.Inicializar(); + sNTC_MotorEF.Inicializar(); + sNTC_MotorDF.Inicializar(); + sCOR_3V0.Inicializar(); + sCOR_5V0.Inicializar(); + sCOR_7V2.Inicializar(); + sCOR_12V.Inicializar(); + sCOR_19V.Inicializar(); + sCOR_24V.Inicializar(); + sCOR_36V.Inicializar(); + //sMAG_MotorET.Inicializar(); + //sMAG_MotorDT.Inicializar(); + //sMAG_MotorEF.Inicializar(); + //sMAG_MotorDF.Inicializar(); + + ledManager.Adicionar(&sLED_Manual); + ledManager.Adicionar(&sLED_Automatico); + ledManager.Adicionar(&sLED_Operacao); + xTaskCreatePinnedToCore(LedTask, "LedTask", 3000, NULL, 1, NULL, tskNO_AFFINITY); + + // Registrar callback para processar mensagens CAN recebidas + canService.setReceiveCallback([](int packetSize, int senderId, CanMessagePosicaoDados posicao, byte* data, int dataLength) { + std::vector fullData; + fullData.push_back(static_cast(posicao)); // Primeiro byte é sempre a posicao + for (int i = 0; i < dataLength; i++) { + fullData.push_back(data[i]); + } + + ProcessarFrameCAN(posicao, 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 >= _TaxaAmostragem) { + ultimoEnvioLogs = tempoAtual; // Atualiza o tempo do último envio + enviarDadosSensores(canService.ID_Num_sTOD, CanMessagePosicaoDados::DadosAll); + } +} + +void enviarDadosSensores(uint8_t id_num, CanMessagePosicaoDados posicao) { + if (Conectado || posicao == CanMessagePosicaoDados::Status) { + unsigned long startLoop = millis(); + + 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_sMOD) { + EnviarDadosCAN(canService.MontarFrameReqStatusMod(D_Code, Conectado, VERSION)); + return; + } + + if (todosIDs) + MostrarLog("Atualizando dados dos sensores..."); + + if (todosIDs && enviarStatus) { + EnviarDadosCAN(canService.MontarFrameReqStatusMod(D_Code, Conectado, VERSION)); + } + + ComponenteCAN::ProcessarComponenteCAN(sSRV_FreioET, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sSRV_FreioDT, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + + ComponenteCAN::ProcessarComponenteCAN(sRLE_CoolerIn, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sRLE_CoolerOut, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + + ComponenteCAN::ProcessarComponenteCAN(sLED_Manual, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sLED_Automatico, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sLED_Operacao, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + + ComponenteCAN::ProcessarComponenteCAN(sNTC_MotorET, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sNTC_MotorDT, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sNTC_MotorEF, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sNTC_MotorDF, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + + ComponenteCAN::ProcessarComponenteCAN(sCOR_3V0, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sCOR_5V0, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sCOR_7V2, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sCOR_12V, {CanMessagePosicaoDados::Dados1, CanMessagePosicaoDados::Dados2}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sCOR_19V, {CanMessagePosicaoDados::Dados1, CanMessagePosicaoDados::Dados2}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sCOR_24V, {CanMessagePosicaoDados::Dados1, CanMessagePosicaoDados::Dados2}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + ComponenteCAN::ProcessarComponenteCAN(sCOR_36V, {CanMessagePosicaoDados::Dados1, CanMessagePosicaoDados::Dados2}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + + //ComponenteCAN::ProcessarComponenteCAN(sMAG_MotorET, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + //ComponenteCAN::ProcessarComponenteCAN(sMAG_MotorDT, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + //ComponenteCAN::ProcessarComponenteCAN(sMAG_MotorEF, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + //ComponenteCAN::ProcessarComponenteCAN(sMAG_MotorDF, {CanMessagePosicaoDados::Dados1}, todosIDs, id_num, enviarStatus, todosDados, posicao, EnviarDadosCAN); + + if (enviarStatus) { + qtdEnviosStatus = 0; + } + else { + qtdEnviosStatus++; + } + + latenciaLoop = millis() - startLoop; + + if (posicao == CanMessagePosicaoDados::DadosAll) { + EnviarDadosCAN(canService.MontarFrameReqDadosFim(latenciaLoop)); + } + + if (todosIDs) + MostrarLog("Ciclo concluido, latencia de loop: " + String(latenciaLoop)); + } +} + +void EnviarDadosCAN(std::vector data) { + if (data.size() > 2) { + canService.adicionarMensagemFila(data); + } +} + +void ProcessarFrameCAN(CanMessagePosicaoDados posicao, std::vector data) { + if (data.size() < 1) return; + + F_Code funcao = canService.FuncaoPorPosicao(posicao); + + switch (funcao) { + case F_Code::ReqTx: ProcessarReq(data); break; + case F_Code::CmdTx: ProcessarCmd(data); break; + case F_Code::CfgTx: ProcessarCfg(data); break; + } +} + +void ProcessarReq(std::vector data) { + if (data.size() < 2) return; + CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)data[0]; + uint8_t id = data[1]; + enviarDadosSensores(id, posicao); +} + +void ProcessarCfg(std::vector data) { + if (data.size() < 4) return; + std::vector _chkRx; + CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)data[0]; + uint8_t id = data[1]; + S_Code componente = DetectarComponente(id); + if (componente == S_Code::sMOD) { + switch (posicao) { + case CanMessagePosicaoDados::Config1: { + if (data.size() < 8) return; + bool conectar = data[2] == 1; + int taxa = data[3] * 1000; + bool requisitar = data[4] == 1; + //loraService.AddressBase = data[5]; + int pinoSDA = data[6]; + int pinoSCL = data[7]; + + _TaxaAmostragem = taxa; + RequisitarDados = requisitar; + + //if (pinoSDA != PINO_INVALIDO && pinoSCL != PINO_INVALIDO) { + // if (I2CService::DefinirPinos(pinoSDA, pinoSCL) || !I2CService::I2CIniciado) { + // inicializarDependenciasI2C(); + // } + //} + + vTaskDelay(pdMS_TO_TICKS(100)); + + Conectado = conectar; + _chkRx = canService.MontarFrameReqStatusMod(D_Code, Conectado, VERSION); + + MostrarLog("Configuracao do modulo concluida"); + break; + } + } + } + + EnviarDadosCAN(_chkRx); +} + +void ProcessarCmd(std::vector data) { + if (data.size() < 2) return; + CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)data[0]; + uint8_t id = data[1]; + S_Code componente = DetectarComponente(id); + switch (componente) { + case sMOD: { + if (data.size() < 3) return; + switch (posicao) { + case CanMessagePosicaoDados::Command1: { + Estado estado = (Estado)data[2]; + if (estado == Estado::Reset) { + delay(100); + esp_restart(); + } + break; + } + } + break; + } + case sSRV: { + ServoAgro_v2* servo = ObterServoPorId(id); + if (servo == nullptr) + return; + servo->ProcessarComando(data); + break; + } + case sRLE: { + Rele_v2* rele = ObterRelePorId(id); + if (rele == nullptr) + return; + rele->ProcessarComando(data); + break; + } + case sLED: { + Sinaleiro_v2* sinaleiro = ObterSinaleiroPorId(id); + if (sinaleiro == nullptr) + return; + sinaleiro->ProcessarComando(data); + break; + } + default: + break; + } +} + +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; + if (idNum >= 01 && idNum <= 10) return S_Code::sNTC; + if (idNum >= 11 && idNum <= 20) return S_Code::sCOR; + if (idNum >= 21 && idNum <= 30) return S_Code::sLED; + if (idNum >= 41 && idNum <= 50) return S_Code::sSRV; + if (idNum >= 81 && idNum <= 90) return S_Code::sRLE; + if (idNum >= 91 && idNum <= 100) return S_Code::sMAG; + return S_Code::sVZO; +} + +