Recents in Beach


Receba o meu conteúdo GRATUITAMENTE


Esp32 medindo potência AC e consumo KW/h Tensão e Corrente RMS com Arduino

 


INTENÇÃO DA AULA

Seguir com o desenvolvimento de uma aplicação para medida de potência e energia.

 

MATERIAIS UTILIZADOS

       Dispositivo montado da publicação anterior:
https://www.fernandok.com/2022/04/medidor-de-tensao-e-corrente-ac-com.html

       Uma carga resistiva para testes

       Multímetros e cabos para conexões

       Módulo codificador rotativo com chave KY-040

 


Sobre essa atualização

Nesta etapa, vamos explorar um pouco mais as possibilidades da montagem anterior, criando uma aplicação capaz de medir a tensão RMS, a corrente RMS, a potência e a energia em uma carga.

A carga utilizada será uma carga resistiva de 1000W. Muita atenção à adequação do cabeamento a corrente circulante, para evitar acidentes.

Desta vez utilizaremos as funções da biblioteca TFT_eSPI para criar uma interface um pouco mais refinada também.

Um outro cuidado é com a fonte de alimentação. O circuito utilizado é altamente dependente da qualidade e estabilidade da fonte de alimentação, por isso, utilize fontes estáveis e com pouco ruído.

Antes de chegarmos ao código-fonte, vamos revisitar o circuito para uma revisão rápida.

 

CIRCUITO – Rede AC e sensores

Abaixo vemos a interligação dos sensores à rede AC a ser monitorada:

 


       O sensor de tensão é conectado em paralelo com a carga.

       O sensor de corrente é conectado em série com a carga.

       Um interruptor é adicionado ao circuito para permitir o controle da alimentação da carga.

       Um led indicador também é incluindo, indicando quando o a carga está energizada.

 

Mais detalhes sobre o sensor de tensão baseado no Trafo ZMPT101B:

 


No esquema podemos observar que o ZMPT101B age como um isolador entre a rede e o circuito de medição.

O sinal detectado é da tensão induzida sobre o resisto R1, capturada por um amplificador de diferença com ganho 10.

O sinal resultante é aplicado em um divisor de tensão formado pelo trimpot R12 e em seguida, aplicado em uma outra etapa amplificadora de mesmo tipo e ganho.

 


Detalhes sobre o sensor de corrente baseado no ACS758LCB-050B:

 


O sinal resultante do sensor ACS758 pode ser obtido diretamente, passando apenas por um filtro passa-baixas formado por R1 e C1, bastando para isso conectar o ADC na saída OUT1.

Mas também pode ser obtido passando por um amp-op seguidor de tensão, formado por U2.

 


CIRCUITO – Filtros dos ADCs

Para garantir um sinal mais livre de ruídos nas entradas dos ADC e com níveis de tensão adequados aos 3V3 limite dos ADC, os sinais de cada sensor foram aplicados:

 


Primeiro a um divisor de tensão formado por dois resistores de 10kW / 1%, fazendo com que as tensões máximas de 5V das saídas sejam reduzidas a 2,5V. Não só adequado ao limites como também, dentro da faixa de maior linearidade dos ADCs do ESP32, o que resultará em ajustes de curva mais simples.

Em seguida, esses sinais, provenientes dos divisores de tensão passam por dois filtros passa-baixas sobrepostos, para frequências de 10 e 100x a frequência da rede AC (600Hz e 6kHz).

Um jumper foi adicionado ao filtro de 600Hz do sensor de tensão para desativação opcional, caso os ruídos sejam o objeto de estudo.

 

CIRCUITO – ESP32 e fonte

Depois de passar pelos divisores e filtros, os sinais são aplicados a suas respectivas entradas ADC no ESP32 (pinos 32 e 33 para o sinal de tensão e de corrente respectivamente).

Vemos também um detalhe dos capacitores da fonte de alimentação de 5V, que deve ser de boa qualidade, estável, livre de ruídos e com potência suficiente para manter o circuito.

 


CIRCUITO – Introdução de um codificador rotativo KY-040

Para permitir uma maior interação entre o usuário e o dispositivo, vamos introduzir no circuito, nesta etapa, um codificador rotativo com chave. Ele permitirá a seleção de estados da aplicação.

Em etapas futuras, poderemos introduzir também a função touch do display.

As conexões com o ESP32 estão descritas na tabela abaixo.

A detecção do sinal segue as orientações do manual, lembrando que a defasagem entre os sinais DT e CLK determinam, além da rotação, a direção.

 




Para detectar os sinais do codificador, uma tarefa no ESP32 ficará responsável por monitorar os pinos SW, DT e CLK.

SW é a chave e sempre que pressionada, vai a nível baixo. Assim, a tarefa deverá detectar uma borda de descida para SW para sabermos que o botão foi pressionado.

Já para a rotação, vamos monitorar toda vez que o sinal CLK vai de alto para baixo (borda de descida). Quando isto ocorrer, iniciaremos o monitoramento do sinal DT até que este apresente uma mudança.

Se a mudança for uma borda de subida (de baixo para alto) consideraremos um incremento (CW), caso contrário, consideraremos um decremento (CCW).

 



CALIBRAÇÃO – Offsets e coeficientes

Para auxiliar na calibração, introduzimos:

#define CAL 0 //1 para offset e 2 para coeficientes (0 para programa padrão)

 

Este #define permitirá alterar a rotina do programa para:

0 – Funcionar normalmente, como um monitor de energia.

1 – Enviar para a serial o valor médio dos ADCs de tensão e corrente. Útil para determinar os offsets, usando esta função com uma tensão de 0V e corrente de 0A.

2 – Enviar os valores RMS dos ADCs, sem a conversão linear para tensão RMS e corrente RMS. Útil para determinar os coeficientes angulares e lineares das retas de conversão, com ajuda do Excel por exemplo.

Como no exemplo do vídeo anterior, utilizaremos uma calibração simples. Métodos mais complexos e adequados a instrumentação podem ser aplicados a partir deste conceito.

A calibração basicamente se restringirá à determinação dos offsets das medidas e de um par de coeficientes (angular e linear) para retas de conversão, já que consideramos todos os sensores com saídas lineares.

 

CALIBRAÇÃO – Obtendo os offsets – CAL = 1

Abaixo vemos um exemplo de obtenção dos offsets dos ADCs de tensão e corrente. Para isso, definimos CAL = 1 e executamos o programa. Garantimos que uma tensão de 0V e uma corrente de 0A estivessem aplicadas ao dispositivo.

O resultado obtido no monitor serial do Arduíno IDE pode ser visto abaixo:

 


Aplicamos estes valores ao programa:

//valores de offset de cada sensor (determinados estatisticamente através de capturas)
const float offsetVAC = 1398.46;
const float offsetIAC = 1413.18;

 

CALIBRAÇÃO – Obtendo os coeficientes – CAL = 2

Para obter os coeficientes de ajuste para tensão e corrente, mudamos o valor de CAL para 2. Esta mudança anula o efeito dos coeficientes coefA_VAC, coefB_VAC, coefA_IAC e coefB_IAC nas funções de ajuste, permitindo que os valores sem correção sejam exibido na tela.

Para nosso exemplo, vamos aplicar uma tensão de 0V e uma corrente de 0A e então, registrar os valores obtidos no display. Estes pares são os primeiros pontos de suas respectivas retas de ajuste.

(0Vrms ; 59) para a tensão

(0,006Arms ; 48) para corrente

O próximo passo é aplicar valores de tensão e corrente, diferentes de zero, para a obtenção dos próximos pontos. Para isso, aplicamos a tensão da rede a uma carga e obtivemos:

(114,5Vrms ; 432,5) para tensão

(8,818Arms ; 90) para corrente

De posse destes pares de pontos, podemos determinar a reta de ajuste e consequentemente a função de ajuste. Para isso, basta aplicá-los em gráficos no Excel, por exemplo, e solicitar uma reta de regressão linear. Veja no recorte abaixo:

 


E fazemos o mesmo para a corrente:

 


Podemos agora registrar estes valores no código-fonte e retornar o valor de CAL para 0.

#define CAL 0 //1 para offset e 2 para coeficientes (0 para programa padrão)

const float coefA_VAC = 0.3066; //Coloque aqui os coeficientes encontrados na calibração
const float coefB_VAC = -18.087; //
const float coefA_IAC = 0.2098; //
const float coefB_IAC = -10.064;//

 

CÓDIGOS-FONTES

CÓDIGOS-FONTES – Declarações

#include  //Para a comunicação SPI com o display
#include  // Biblioteca de controle do display
#include  //Para acessar as funções do WD de tarefas 
#include "soc/timer_group_struct.h" //Para acessar o timer do WatchDog de hardware do núcleo 0
#include "soc/timer_group_reg.h" //Para acessar o timer do WatchDog de hardware do núcleo 0

//Definições gráficas (facilitam a leitura do código e o posicionamento)
#define LARGURA 480
#define ALTURA 320
#define SEPARADOR_CENTRAL 30
#define CENTRO_X ((LARGURA / 2) - SEPARADOR_CENTRAL)
#define CENTRO_Y (ALTURA / 2)
#define QUARTO_X (LARGURA / 4)
#define QUARTO_Y (ALTURA / 4)
#define ZERO_X 0
#define ZERO_Y 0
int32_t ALTURA_DA_LINHA = 0;

//Pinos de entrada utilizados
const uint8_t pinAdcTensao = 32;
const uint8_t pinAdcCorrente = 33;
const uint8_t pinSW = 25;
const uint8_t pinDT = 26;
const uint8_t pinCLK = 27;
#define CAL 0 //1 para offset e 2 para coeficientes (0 para programa padrão)

//valores de offset de cada sensor (determinados estatisticamente através de capturas)
const float offsetVAC = 1398.46;
const float offsetIAC = 1413.18;

//valores limiares de tensão e corrente RMS
const float limiarTensao = 2.5; //tensão RMS mínima medida
const float limiarCorrente = 0.25; //Corrente RMS mínima medida

#if CAL == 2//altera os coeficiente para serem ignorados
//coeficientes das regressões lineares. Usados para a conversão dos códigos
const float coefA_VAC = 1.0; //Valores que não influenciam na conversão
const float coefB_VAC = 0.0; //
const float coefA_IAC = 1.0; //
const float coefB_IAC = 0.0;//
#else
const float coefA_VAC = 0.3066; //Coloque aqui os coeficientes encontrados na calibração
const float coefB_VAC = -18.087; //
const float coefA_IAC = 0.2098; //
const float coefB_IAC = -10.064;//
#endif

//Período mínimo de captura de 16,7ms para garantir que ao menos um ciclo seja capturado
const int32_t duracaoDaAmostragem = (int32_t)((1000.0 / 60.0) * 30.0); //30 ciclos de 1000ms/60 (60Hz)

//Protótipos das funções das tarefas
void TaskEncoder(void *pvParamenters);
void TaskAplicacao1 (void *pvParamenters);

//Manipuladores das tarefas
TaskHandle_t hndTaskEncoder;
TaskHandle_t hndTaskAplicacao1;

//Protótipos das funções
void barrasDeTitulo();//Desenha a barra de título
void telaFixa();//Desenha os elementos fixos da tela
void atualizaMedidas(float _t, float _c, float _p, float _d, float _e);///Atualiza elementos variáveis (tensão, corrente,potência, tempo decorrido e energia)
void menu(int8_t _indice);

//enumerações
enum estadosDaAplicacao { INICIADA, PAUSADA, PARADA, MENU};

//Globais
estadosDaAplicacao estado = PARADA; //define o estado da aplicação (parado, pausado, iniciado, menu)
estadosDaAplicacao estadoAnterior = PARADA; //Guarda o estado antes do MENU ser acionado (inicia a aplicação PARADA)
String informaEstado = "parada"; //String para impressão do estado na tela
float tensaoRMS = 0.0; //Tensao detectada
float correnteRMS = 0.0; //Corrente detectada
float potencia = 0.0; // Potencia calculada
float energia = 0.0; //Energia consumida
float decorrido = 0.0; //Intervalo decorrido
uint32_t inicio = 0; //início das medições
int32_t t1 = 0; //para o cálculo do delta t
int32_t t2 = 0; //para o cálculo do delta t
float delta = 0.0; //variação do tempo (t2-t1)

//Cria o objeto que representa o display
TFT_eSPI tft = TFT_eSPI();

 

CÓDIGOS-FONTES – Setup e loop

void setup()
{
  Serial.begin(115200); //Iniciando a comunicação serial (para debug)
  
  //define as funções dos pinos
  pinMode(pinAdcTensao, INPUT);
  pinMode(pinAdcCorrente, INPUT);
  pinMode(pinSW, INPUT);
  pinMode(pinDT, INPUT);
  pinMode(pinCLK, INPUT);
  
  tft.init(); //inicia o display
  tft.setRotation(1); //Ajusta a rotação do display
  
#if CAL == 1 //chama a função auxiliar de calibração 1
  CAL1();
  return; // e finaliza
#endif

  criaTarefas();//cria as tarefas
}
void loop()
{
  //não utilizaremos a tarefa do loop
  vTaskDelete(NULL);
}


 

CÓDIGOS-FONTES – CAL1()

void CAL1()
{
  /*****************************************************************************************
     Execute para determinar o valor médio dos ADC para tensão e corrente zero (OFFSETs)
   *****************************************************************************************/
  delay(5000);
  Serial.println("Inciando a medida dos offsets");
  //Variáveis
  int32_t tensao = 0;
  int32_t corrente = 0;
  uint32_t qtdDeAmostras = 0;
  float acumuladorTensao = 0.0;
  float acumuladorCorrente = 0.0;
  
  for (int32_t amostra = 0; amostra < 100000; amostra++)
  {
    tensao = analogRead(pinAdcTensao);
    corrente = analogRead(pinAdcCorrente);
    
    acumuladorTensao = acumuladorTensao + tensao;
    acumuladorCorrente = acumuladorCorrente + corrente;
    qtdDeAmostras++;
  }
  acumuladorTensao = acumuladorTensao / float(qtdDeAmostras);
  acumuladorCorrente = acumuladorCorrente / float(qtdDeAmostras);
  
  Serial.println("offsetVAC = " + String(acumuladorTensao));
  Serial.println("offsetIAC = " + String(acumuladorCorrente));
}

 

CÓDIGOS-FONTES – criaTarefas()

/*ATENÇÃO:
   Lembre-se de que a versão do xTaskCreate()ESP IDF é diferente da versão original do FreeRTOS.
   No FreeRTOS original, a profundidade da pilha é especificada em palavras.
   No ESP IDF, é especificado em bytes . Uma distinção muito importante!
*/
void criaTarefas()
{
  /*****************************************************************
        TaskEncoder
  *******************************************************************/
  xTaskCreatePinnedToCore
  (
    TaskEncoder,         //nome da função que implementa a tarefa
    "TaskEncoder",       //Nome da tarefa para exibição
    2048,                //tamanho do stack em bytes
    NULL,                //Parâmetro de entrada da tarefa
    1,                   //Prioridade acima do IDLE0
    &hndTaskEncoder,     //manipulador da tarefa
    0                    //Executar no core 0
  );
  
  /*****************************************************************
        TaskAplicacao1
  *******************************************************************/
  xTaskCreatePinnedToCore
  (
    TaskAplicacao1,         //nome da função que implementa a tarefa
    "TaskAplicacao1",       //Nome da tarefa para exibição
    2048,                   //tamanho do stack em bytes
    NULL,                   //Parâmetro de entrada da tarefa
    1,                      //Prioridade acima do IDLE1
    &hndTaskAplicacao1,     //manipulador da tarefa
    1                       //Executar no core 1
  );
}

 

CÓDIGOS-FONTES – TaskEncoder()

void TaskEncoder (void *pvParameters)
{
  (void) pvParameters;
  
  int8_t indice = 0; //Registra o índice selecionado do menu
  bool CLKanterior = digitalRead(pinCLK); //Estado anterior do CLK do Encoder
  bool CLKatual = digitalRead(pinCLK); //Estado atual do CLK do Encoder
  bool SWanterior = digitalRead(pinSW); //Estado anterior do SW do Encoder
  bool SWatual = digitalRead(pinSW);// Estado Atual do SW do Encoder
  bool DTatual = digitalRead(pinDT); //Estado atual do DT do Encoder
  bool menuAtivo = false; //Registra se o menu está ou não ativo
  
  telaFixa();
   
  for (;;)
  {
    TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; //Habilita a escrita no WD
    TIMERG0.wdt_feed = 1; //alimentamos o WD de hardware do Núcleo 0

////Verifica se o SW foi pressionado para entrar no menu
    SWatual = digitalRead(pinSW);
    if ((SWanterior == HIGH) && (SWatual == LOW))//detecta uma borda de descida do SW no encoder
    {
      estadoAnterior = estado; //Salva o estado antes de acessar o MENU
      estado = MENU; //altera o estado
      barrasDeTitulo();//carrega barra de título
      menu(indice);//carrega o menu com indice atualizado
      menuAtivo = true;//registra que o menu está ativo
    }
    SWanterior = SWatual;//salva o estado atual
    
        ////Se o menu foi  ativado, executa o menu até uma entrada
    while (menuAtivo)
    {
      TIMERG0.wdt_feed = 1; //alimentamos o WD de hardware do Núcleo 0
      esp_task_wdt_reset();//Alimentamos o WD de software (WD de tarefas)
      //////Detecta a rotação do encoder
      CLKanterior = CLKatual; //salva o estado do CLK
      CLKatual = digitalRead(pinCLK);//Atualiza o estado do CLK
      if ((CLKanterior == HIGH) && (CLKatual == LOW)) //detecta uma borda de descida do CLK no encoder
      {
        while (digitalRead(pinCLK) == LOW) //enquanto o CLK estiver baixo, aguarda mudança do DT
        {
          DTatual = digitalRead(pinDT);//detecta o estado de DT
          if (DTatual == HIGH) //verifica uma borda de subida do DT no encoder
          {
            indice++;//incrementa o índice
            if (indice > 3) indice = 3;//se o índice ultrapassar o limite, corrige
            break; //finaliza a observação do pino DT
          }
          else if (DTatual == LOW) //verifica uma borda de descida do DT no encoder
          {
            indice--;//decrementa o índice
            if (indice < 0) indice = 0;//se o índice ultrapassar o limite, corrige
            break; //finaliza a observação do pino DT
          }
            int8_t contador = 0;
          while (contador < 10)
          { //Aguarda 100ms para evitar falsas detecções, mas sem deixar de alimentar os WD
            TIMERG0.wdt_feed = 1; //alimentamos o WD de hardware do Núcleo 0
            esp_task_wdt_reset();//Alimentamos o WD de software (WD de tarefas)
            vTaskDelay(10 / portTICK_PERIOD_MS);
          }
        }
        menu(indice);//Atualiza o menu
      }

      ////Verifica se o SW foi pressionado para sair do menu
      SWatual = digitalRead(pinSW);
      if ((SWanterior == HIGH) && (SWatual == LOW))//detecta uma borda de descida do SW no encoder
      {
        telaFixa();
        menuAtivo = false;
        switch (indice)
        {
          case 0:
            inicio = millis();//Marca o inicio das medições
            t1 = inicio;
            energia = 0.0;
            estado = INICIADA;
            informaEstado = "iniciada";
            break;
          case 1:
            estado = PAUSADA;
            informaEstado = "pausada";
            break;
          case 2:
            estado = PARADA;
            informaEstado = "parada";
            break;
          case 3: //Continua com as medições (mesmo que iniciar)
            estado = INICIADA;
            informaEstado = "continuando";
            break;
          default: break;
        }
      }
      SWanterior = SWatual;
    }//sai do menu

 //Envia os valores para atualização da tela
    atualizaMedidas(tensaoRMS, correnteRMS, potencia, decorrido, energia);
    TIMERG0.wdt_wprotect = 0; //desabilita alterações nos registradores do WD (caso a task seja suspensa)
  }
}

 

CÓDIGOS-FONTES – TaskAplicacao1()

//Implementação das tarefas do núcleo 1
void TaskAplicacao1 (void *pvParameters)
{
  (void) pvParameters;
  
  for (;;)
  {
    //Variáveis
    int32_t tensao = 0;
    int32_t corrente = 0;
    uint32_t qtdDeAmostras = 0;
    float acumuladorTensao = 0.0;
    float acumuladorCorrente = 0.0;
    
    //Captura
    unsigned long inicioDaCaptura = millis();
    do
    {
      tensao = analogRead(pinAdcTensao) - offsetVAC;
      corrente = analogRead(pinAdcCorrente) - offsetIAC;
      
      acumuladorTensao = acumuladorTensao + (tensao * tensao); //Soma dos quadrados
      acumuladorCorrente = acumuladorCorrente + (corrente * corrente);//Soma dos quadrados
      
      qtdDeAmostras++;
    } while ((millis() - inicioDaCaptura) < duracaoDaAmostragem);

    //Cálculo dos valores RMS
    acumuladorTensao = acumuladorTensao / float(qtdDeAmostras); //Média dos quadrados
    acumuladorCorrente = acumuladorCorrente / float(qtdDeAmostras); //Média dos quadrados
    
    acumuladorTensao = sqrt(acumuladorTensao); //RMS da captura
    acumuladorCorrente = sqrt(acumuladorCorrente); //RMS da captura
    
    estadosDaAplicacao estadoAvaliado;
    if (estado == MENU)
    {
      estadoAvaliado = estadoAnterior;
    }
    else
    {
      estadoAvaliado = estado;
    }
    
    switch (estadoAvaliado)
    {
      case INICIADA: //mede a quantidade de energia
        //Converte o tempo decorrido para segundos
        t2 = millis();
        decorrido = float(t2 - inicio) / 1000.0; //tempo decorrido em switch
        delta = (t2 - t1) / 1000.0;
        t1 = t2;

        //Calcula a tensão RMS
        tensaoRMS = acumuladorTensao * coefA_VAC + coefB_VAC; //em volts
        if (tensaoRMS < limiarTensao) tensaoRMS = 0.0; //elimina valor residual
        
        //Calcula a corrente RMS
        correnteRMS = acumuladorCorrente * coefA_IAC + coefB_IAC; //Conversão em ampères
        if (correnteRMS < limiarCorrente) correnteRMS = 0.0; //elimina valor residual
        
        //Calcula a potência
        potencia = tensaoRMS * correnteRMS; //em watts
        
        //Calcula a quantidade de energia
        energia = energia + (potencia * delta) / 3600.0; //em Wh
        break;
        
      case PAUSADA: //Pausa a medida de energia
        decorrido = decorrido;
        break;

      case PARADA: //para de medir a energia e zera o valor
        //Calcula a tensão RMS
        tensaoRMS = acumuladorTensao * coefA_VAC + coefB_VAC; //em volts
        if (tensaoRMS < limiarTensao) tensaoRMS = 0.0; //elimina valor residual
        
        //Calcula a corrente RMS
        correnteRMS = acumuladorCorrente * coefA_IAC + coefB_IAC; //Conversão em ampères
        if (correnteRMS < limiarCorrente) correnteRMS = 0.0; //elimina valor residual
        
        decorrido = 0.0;
        potencia = 0.0;
        energia = 0.0;
        break;
        
      default:
        break;
    }
    esp_task_wdt_reset();//Alimentamos o WD de software (WD de tarefas)
  }
}


 

CÓDIGOS-FONTES – Partes do desenho do display



CÓDIGOS-FONTES – barrasDeTitulo()

//Desenha a barra de título
void barrasDeTitulo()
{
  tft.fillScreen(TFT_BLACK);
  //Imprime a barra de título
  tft.setTextFont(4);
  tft.setTextSize(1);
  tft.setTextDatum(TC_DATUM); //Ancora no TOP CENTER
  tft.setTextPadding(LARGURA); //Define o comprimento do PADDING do texto
  tft.setTextColor(TFT_YELLOW, TFT_NAVY);
  String texto = "Monitor de Consumo";
  ALTURA_DA_LINHA = tft.fontHeight(); //altura da linha para fonte 4
  tft.fillRect(0, 0, LARGURA, 2 * ALTURA_DA_LINHA, TFT_NAVY);
  tft.drawString(texto, CENTRO_X, ZERO_Y);
  
  //Imprime sub título
  tft.setTextFont(2);
  texto = "https://www.fernandok.com";
  tft.setTextColor(TFT_WHITE, TFT_NAVY);
  tft.drawString(texto, CENTRO_X, ALTURA_DA_LINHA);
}


 

CÓDIGOS-FONTES – Partes do desenho do display


CÓDIGOS-FONTES – telaFixa()

// Desenha a parte fixa da tela
void telaFixa()
{
  barrasDeTitulo();
  //Imprime etiquetas
  tft.setTextFont(4);
  tft.setTextPadding(LARGURA / 2); //Define o comprimento do PADDING do texto
  
  String texto = "Tensao";
  tft.setTextColor(TFT_YELLOW, TFT_NAVY);
  tft.drawString(texto, QUARTO_X, ALTURA_DA_LINHA * 2);
  
  texto = "Corrente";
  tft.setTextColor(TFT_YELLOW, TFT_NAVY);
  tft.drawString(texto, 3 * QUARTO_X, ALTURA_DA_LINHA * 2);
  
  texto = "Potencia";
  tft.setTextColor(TFT_YELLOW, TFT_NAVY);
  tft.drawString(texto, QUARTO_X, ALTURA_DA_LINHA * 7);
  
  texto = "Energia";
  tft.setTextColor(TFT_YELLOW, TFT_NAVY);
  tft.drawString(texto, 3 * QUARTO_X, ALTURA_DA_LINHA * 7);
}

 

CÓDIGOS-FONTES – Partes do desenho do display


Códigos-Fonte: atualizaMedidas()

//Atualiza as medidas na tela
void atualizaMedidas(float _tensao, float _corrente, float _potencia, float _decorrido, float _energia)
{
  //Imprime unidades
  tft.setTextFont(4);
  tft.setTextSize(1);
  tft.setTextDatum(BR_DATUM); //Ancora no Bottom Right
  const int32_t UM_CARACTER = tft.textWidth("W"); //tamanho de um caracter
  //...tensão
  String texto = "V";
  tft.setTextPadding(UM_CARACTER); //determina o padding para unidade
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawString(texto, CENTRO_X, ALTURA_DA_LINHA * 5.5);
  //...corrente
  texto = "A";
  tft.setTextPadding(UM_CARACTER); //determina o padding para unidade
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawString(texto, LARGURA, ALTURA_DA_LINHA * 5.5);

  //...potência
  int8_t decimalPotencia = 1; // define a quantidade de decimais para potência
  if (_potencia > 1000.0)
  {
    _potencia = _potencia / 1000.0;
    texto = "kW";
    decimalPotencia = 2;
  }
  else
  {
    texto = "W";
  }
  tft.setTextPadding(UM_CARACTER * 2); //determina o padding para unidade
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawString(texto, CENTRO_X, ALTURA_DA_LINHA * 10.5);

 //...energia
  if ((_energia >= 1.0E3) && (_energia < 1.0E6))
  {
    _energia = _energia / 1.0E3;
    texto = "kWh";
  }
  else if (_energia >= 1.0E6)
  {
    _energia = _energia / 1.0E6;
    texto = "MWh";
  }
  else
  {
    texto = "Wh";
  }
  tft.setTextPadding(UM_CARACTER * 3); //determina o padding para unidade
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawString(texto, LARGURA, ALTURA_DA_LINHA * 10.5);

  //Imprime a barra inferior com o tempo decorrido
  tft.setTextFont(4);
  tft.setTextSize(1);
  tft.setTextPadding(LARGURA);
  tft.setTextDatum(BL_DATUM);  //Ancora no BOTTOM LEFT
  //converte o tempo decorrido para string
  int32_t dias = int32_t(_decorrido / 86400.0);
  _decorrido = _decorrido - (dias * 86400.0);
  int32_t horas = int32_t((_decorrido / 3600));
  _decorrido = _decorrido - (horas * 3600.0);
  int32_t minutos = int32_t(_decorrido / 60.0);
  _decorrido = _decorrido - (minutos * 60.0);
  int32_t segundos = int32_t(_decorrido);
  texto = "Decorrido: " +
          String(dias) + "d " +
          String(horas) + "h " +
          String(minutos) + "min " +
          String(segundos) + "s " +
          informaEstado;
  //  texto = "Decorrido: " + String(_decorrido);
  tft.setTextColor(TFT_WHITE, TFT_NAVY);
  tft.drawString(texto, 0, ALTURA);

  //Imprime as medidas
  tft.setTextFont(7);
  tft.setTextSize(1);
  tft.setTextDatum(BR_DATUM); //Ancora no BOTTOM RIGHT
  tft.setTextPadding(tft.textWidth("999.99")); //Define o comprimento do PADDING do texto
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawFloat(_tensao, 1, CENTRO_X - UM_CARACTER * 1, ALTURA_DA_LINHA * 5.5);
  tft.drawFloat(_corrente, 1, LARGURA - UM_CARACTER * 1, ALTURA_DA_LINHA * 5.5);
  tft.drawFloat(_potencia, decimalPotencia, CENTRO_X - UM_CARACTER * 2, ALTURA_DA_LINHA * 10.5);
  tft.drawFloat(_energia, 2, LARGURA - UM_CARACTER * 3, ALTURA_DA_LINHA * 10.5);
}

 

CÓDIGOS-FONTES – Partes do desenho do display


Códigos-Fonte: menu()

//Atualiza o menu
void menu(int8_t _indice)
{
  //Imprime opções
  tft.setTextFont(4);
  tft.setTextSize(1);
  tft.setTextDatum(CC_DATUM); //Ancora no CENTER CENTER
  tft.setTextPadding(LARGURA); //Define o comprimento do PADDING do texto
  int32_t ALTURA_DO_TEXTO = tft.fontHeight();
  
  String texto = "INICIAR";
  (_indice == 0) ? tft.setTextColor(TFT_BLACK, TFT_YELLOW) : tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawString(texto, CENTRO_X, QUARTO_Y + 1 * ALTURA_DO_TEXTO);
  
  texto = "PAUSAR";
  (_indice == 1) ? tft.setTextColor(TFT_BLACK, TFT_YELLOW) : tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawString(texto, CENTRO_X, QUARTO_Y + 2.5 * ALTURA_DO_TEXTO);
  
  texto = "PARAR";
  (_indice == 2) ? tft.setTextColor(TFT_BLACK, TFT_YELLOW) : tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawString(texto, CENTRO_X, QUARTO_Y + 4 * ALTURA_DO_TEXTO);
  
  texto = "CONTINUAR";
  (_indice == 3) ? tft.setTextColor(TFT_BLACK, TFT_YELLOW) : tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawString(texto, CENTRO_X, QUARTO_Y + 5.5 * ALTURA_DO_TEXTO);
}

Postar um comentário

1 Comentários

  1. Preciso de um apoio, o meu Esp32 não roda o código, após gravar fica mostrando a mensagem de reseting RST pin
    E não roda o código.

    ResponderExcluir