banner

Os profissionais sabem isso!



Pessoal, hoje vamos falar sobre “Calibração automatizada do ADC do ESP32”. Pode parecer um assunto muito técnico, mas acho bastante importante você saber um pouco sobre isso. Isso porque não se trata apenas do ESP32, ou mesmo somente de calibração do ADC, mas, sim, de tudo que envolve sensores analógicos que você possa querer ler. A maioria dos sensores não é linear, então, vamos introduzir um protótipo de calibrador automatizado para conversores analógicos digitais. Ainda, vamos experimentar realizando uma correção do AD de um ESP32.



Já tenho um vídeo no qual falo um pouco sobre esse assunto: Você não sabia? Ajuste de ADC do ESP32. Agora, vamos falar de uma maneira automatizada que evita que você faça todo o processo de regressão polinomial. Confere aí!



Recursos usados

·         Jumpers
·         1x Protoboard
·         1x ESP WROOM 32  DevKit
·         1x Cabo USB
·         2x Resistores de 10k
·         1x Resistor de 6k8 ou 1x potenciômetro mecânico de 10k para ajuste do divisor de tensão
·         1x X9C103 – potenciômetro digital de 10k
·         1x LM358 – Amplificador operacional




Circuito utilizado


Neste circuito, o LM358 é um amplificador operacional na configuração “buffer de tensão”, isolando os dois divisores de tensão para que um não influencie o outro. Isso permite a obtenção de uma expressão mais simples já que R1 e R2 podem, com uma boa aproximação, não ser mais considerados em paralelo com RB.




Tensão de saída em função da variação do potenciômetro digital X9C103

Baseada na expressão que obtivemos para o circuito, está é a curva de tensão em sua saída quando variamos o potenciômetro digital de 0 a 10k.





Controlando o X9C103

·         Para controlar nosso potenciômetro digital X9C103:
·         vamos alimentá-lo com os 5V, provenientes da mesma USB que alimenta o ESP32, conectando em Vcc.
·         Conectamos o pino UP/DOWN no GPIO12.
·         Conectamos o pino INCREMENTE no GPIO13.
·         Conectamos DEVICE SELECT (CS) e Vss no GND.
·         Conectamos VH/RH na alimentação de 5V
·         Conectamos VL/RL no GND.
·         Conectamos RW/VW na entrada do buffer de tensão.





Conexões






Captura no osciloscópio das rampas de subida e descida

Podemos observar as duas rampas geradas pelo código do ESP32.
Os valores da rampa de subida são capturados e enviados ao software em C# para avaliação e determinação da curva de correção.





Esperado versus lido






Correção

Vamos utilizar a curva de erro para corrigir o ADC. Para isso, vamos alimentar um programa feito em C#, com os valores do ADC. Ele calculará a diferença entre o valor lido e o esperado, criando assim uma curva de ERRO em função do valor do ADC.
Conhecendo o comportamento desta curva, conheceremos o erro e poderemos corrigi-lo.
Para conhecer esta curva, o programa em C# utilizará uma biblioteca que realizará uma regressão polinomial (como aquelas realizadas em vídeos anteriores).






Esperado versus lido após a correção






Execução do programa em C#


Fica aguardando a mensagem de INICIO da rampa






Código Fonte do ESP32 – exemplo de uma função de correção e sua utilização

double f(double x) //função de correção obtida no programa C#
 {  // OBS: TROCAR A VÍRGULAS POR PONTOS CASO SEJA NECESSÁRIO.
        return 108.441301852859
         + 0.406040363240515 * x
         + -0.000518376038637707 * x * x
         + 2.75596287776397E-07 * x * x * x
         + -5.11930314783709E-11 * x * x * x * x
         + -8.55999917386865E-16 * x * x * x * x * x
         + 6.96385006018707E-19 * x * x * x * x * x * x;
}


medida = medida + f(medida); 
//Basta usar o valor medido do ADC como argumento da função e somar o retorno a sua medida




Comparação com as técnicas anteriores





CÓDIGO FONTE DO ESP32

Declarações e Setup()

const int PinINC = 13; //pino responsável pelo incremento/decremento do contador interno do X9C103
const int PinUD = 12; //pino responsável pela direção de contagem (UP ou DOWN)
const int PinAD = 34; //pino de leitura do ADC
const int intervalo = 100; //intervalo entre cada coleta de dados
const int larguraDoPulso = 100; //duração do nível baixo e do nível alto do pulso de incremento/decremento
const int numeroDeAmostras = 200; //número de amostras coletadas para cálculo do valor médio (ameniza ruídos)

void setup()
{
  Serial.begin(250000); //inicial a serial
  pinMode(PinINC, OUTPUT);//pino de incremento/decremento como saída
  pinMode(PinUD, OUTPUT); //pino de direção da contagem como saída
  pinMode(PinAD, INPUT); //pino do ADC como entrada

  digitalWrite(PinINC, LOW); //inicia o pino de incremento/decremento em nível baixo
  digitalWrite(PinUD, HIGH); //inicia o pino de direção como alto (Alto para incremento)
  delay(5000); //aguarda 5 segundos
}



Loop()

void loop()
{
  long medida = 0; //variável que armazenará a medida
  int contadorDeAmostras = 0; //variável para contagem de amostras

  while (contadorDeAmostras < numeroDeAmostras) //Enquanto a contagem de amostras for menor que o número de amostras definidas...
  {
    contadorDeAmostras++; //incrementa o número de amostras
    medida = medida + analogRead(PinAD); //Lê um valor e acumula na variável medidas
  }

  medida = medida / numeroDeAmostras; //encontra a média das medidas amostradas
  //medida = medida + f(medida); //usado quando a função de correção estivel pronta

  if ((medida == 0) && !(digitalRead(PinUD)))// se é o início da rampa...
  {
    digitalWrite(PinUD, HIGH);//define Alto para incrementar
    Serial.println("INICIO"); //envia uma mensagem indicando o início da rampa de subida

  }
  
  Serial.println(medida);//envia a medida
  
  if (medida >= 4095)//Se o máximo valor foi obtido ...
  {
    digitalWrite(PinUD, LOW);//define baixo para iniciar a curva de descida.
    Serial.println("FIM"); //envia uma mensagem de fim da rampa de subida
    int i = 0; //cria um indice temporário para decrementar o contador interno do X9C103
    while (i <= 100)//enquanto o número de pulsos for menor ou igual a 100...
    {
      i++;//incrementa o contador de pulsos
      pulso();//executa um pulso
    }
  }
  pulso(); //executa um pulso
  delay(intervalo); //aguarda o intervalo entre as medidas
}


Pulso()

void pulso() //executa um pulso no pino de incremento/decremento
{
  digitalWrite(PinINC, HIGH);//põe o pino em nível alto
  delay(larguraDoPulso);//aguarda a duração determinada para este estado
  digitalWrite(PinINC, LOW);//põe o pino em nível baixo
  delay(larguraDoPulso);//aguarda a duração determinada para este estado
}


CÓDIGO FONTE DO PROGRAMA EM C#

Execução do programa em C#



Bibliotecas


//Declarações das bibliotecas
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO.Ports;
using MathNet.Numerics;//Mais informações sobre esta biblioteca, visite:
     https://www.mathdotnet.com/

Namespace, Classe e globais


namespace Regressao_polinomial //definição do namespace
{
     public class PortChat//definição da classe
     {
          static bool _continue; //variável sinalizadora de continuidade do programa
          static SerialPort _serialPort; //variável que receberá o objeto que representa a porta serial
          static List<double> medidas = new List<double>(); //lista para recebimento das medidas enviadas pelo ESP32


regPol()

static void regPol() //função que realiza a regressão polinomial usando os valores recebidos
{
       const int degree = 6; //define o grau para o polinômio resultante
       
       int tamanhoY = medidas.Count();//determina o número de medidas recebidas pelo tamanho da lista 'medidas'
       if (tamanhoY < 1) return;//se o tamanho for menor que 1, algo deu errado, saia . . .

       double[] x = new double[tamanhoY];//cria uma matriz para os valores x da função, como o tamanho da lista
       double[] y = new double[tamanhoY];//cria uma matriz para os valores y da função, como o tamanho da lista

       int indice = 0; //cria um indice temporário
       double incremento = 4095.0 / tamanhoY; //divide o intervalo do ADC pelo número de medidas realizadas
           //esse valor e o índice determinarão a reta de referência.
       foreach (double item in medidas) //para cada valor na lista 'medidas'...
       {

             x[indice] = item; //...armazene a medida no matriz x...
             y[indice] = (indice * incremento) - item;//...e determine o seu ERRO para armazenar na matriz y
             indice++; //incremente o índice

       }



       //usando as duas matrizes com as medidas e seus respetivos erros, usamos a função 
       //de regressão polinomial disponível em MathNet.Numerics
       //Mais informações sobre esta biblioteca, visite:
       // https://www.mathdotnet.com/
       var p = Fit.Polynomial(x, y, degree); //a variável 'p' contém os coeficientes do polinômio

       Console.WriteLine();//salta uma linha

       //monta a string com a função e o polinomio já para ser copiada para o código do usuário
       string str = "double f(double x)\n {\n\treturn " +
       p[0].ToString() + "\n\t + " +
       p[1].ToString() + " * x\n\t + " +
       p[2].ToString() + " * x * x\n\t + " +
       p[3].ToString() + " * x * x * x\n\t + " +
       p[4].ToString() + " * x * x * x * x\n\t + " +
       p[5].ToString() + " * x * x * x * x * x\n\t + " +
       p[6].ToString() + " * x * x * x * x * x * x;\n}";

       //imprime a string montada, substituindo as virgulas por pontos 
       Console.WriteLine(str.Replace(",", "."));

       medidas.Clear(); //limpa a lista medidas
       Console.ReadKey(true); //aguarda pressionar de uma tecla
}







FAÇA O DOWNLOAD DOS ARQUIVOS

PDF

OUTROS




2 comentários:

  1. Parabéns pelo ótimo trabalho de divulgação tecnológica que você vem fazendo =] estou acompanhando sempre de perto. E estou cada vez mais imerso nesse mundo. Só que sinto que quanto mais aprendo mais longe fico de aprender tudo rs. E tanta coisa, tantas possibilidades, tantos recursos e desafios. Estou montando um sistema para monitoramento de consumo de energia e este recurso irá ajudar muito a precisão dos equipamentos =] No caso, estou pensando em comprar um osciloscópio para fazer analise das ondas em AC para afinar os equipamentos de medição. Também quero montar uma impressora 3D para fazer os cases, haja dinheiro não é mesmo? Mas como você disse é um investimento em nós. Se tiver alguma comissão com algum fornecedor me avisa que faço questão de comprar usando seu link. (poderia criar uma loja virtual com links para sites parceiros e assim ajudar nos recursos para o site e pesquisa =]

    ResponderExcluir
  2. Coloque seu código no GitHub, a comunidade vai pode colaborar.

    ResponderExcluir

Tecnologia do Blogger.