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.