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);
}






















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