mercredi 7 août 2019

ESP32: Utilisation des entrées analogiques

La mesure d'une tension analogique a toujours été un point faible de l'ESP8266: le premier module (ESP-01) ne donnait accès à aucune entrée analogique, alors que ses successeurs n'en comportent qu'une seule.

Le moins qu'on puisse dire, c'est que les choses sont différentes avec l'ESP32, qui comporte en principe un grand total de 18 broches pouvant être utilisées comme entrée analogique.

Mais avant de s'enthousiasmer outre mesure, faisons tout de suite remarquer que, sur la plupart des cartes de développement ESP32, 3 des entrées analogiques ne sont pas disponibles car elles sont réservées à un autre usage. Plus important encore, les 10 broches reliées à l'ADC2 ne peuvent pas être utilisées lorsque le WiFi est actif!

Le schéma ci-dessous montre la disposition des broches pouvant être utilisées comme entrées analogiques sur la carte ESP32 que j'utilise. J'ai mis en vert le numéro des 6 broches qui peuvent toujours être utilisées comme entrée analogique, alors que les numéros en rouge sont pour les 9 broches qui peuvent être utilisées comme entrée analogique à la condition que le WiFi soit inactif.
Notez les deux façons possibles de numéroter chaque broche. Pour lire l'état de la broche GPIO 33, je peux tout aussi bien écrire analogRead(33) ou analogRead(A5).

Sur ma carte, les broches GPIO 0, 37 et 38 ne sont pas disponibles. Les broches GPIO 36 et GPIO 39 sont disponibles, par contre, à la condition de savoir les reconnaître: elles portent respectivement la mention "VP" et "VN" sur la carte (attention: n'utilisez pas les broches GPIO 36 et GPIO 39 pendant que vous utilisez le capteur à effet Hall intégré de l'ESP32).

Essai avec un potentiomètre

J'ai branché un potentiomètre à ma carte ESP32 de façon à faire varier la tension de la broche GPIO 33 entre 0 V et 3,3 V.

J'ai ensuite utilisé un sketch minimaliste pour afficher la valeur mesurée dans le moniteur série: mis à part le numéro de broche, le même sketch conviendrait à une carte Arduino conventionnelle.

(Au besoin, vous pouvez vous référer à ce précédent billet pour apprendre comment programmer l'ESP32 au moyen de l'IDE Arduino)


En tournant le bouton du potentiomètre d'une extrémité à l'autre afin de faire passer la tension de 0 V à 3,3 V, on obtient des valeurs variant entre 0 et 4095, puisque la résolution de l'ADC est de 12 bits.


Conversion des valeurs en volts

Les choses se corsent un peu si on désire connaître la tension exacte en volts (ce qui est rarement nécessaire, faut-il le préciser). On pourrait croire qu'il s'agit de multiplier notre mesure par 3,3 , puis de la diviser par 4095. Mais si on procède de cette façon, nous obtiendrons systématiquement une tension plus faible que la tension réelle.  La raison, c'est que la réponse de l'ADC de l'ESP32 n'est pas parfaitement linéaire.

Pour le vérifier, j'ai branché un voltmètre au potentiomètre, et j'ai relevé la tension réelle en volts et le résultat d'analogRead() pour plusieurs positions différentes du potentiomètre.


Le problème principal se situe aux deux extrémités du graphique: la résolution de 12 bits devrait permettre de distinguer deux valeurs séparées d'environ un millivolt. Pourtant, toutes les valeurs inférieures à environ 125 mV retournent une mesure de 0. À l'autre extrémité, la valeur maximale de 4095 est atteinte dès 3,1 V, ce fait que nous obtenons le même résultat peu importe que la tension soit de 3,1, 3,2 ou 3,3 V...

Sur le graphique ci-dessous, j'ai ajouté, en orange, la droite correspondant au calcul analogRead() * 3,3 / 4095... ça donne un résultat presque toujours légèrement plus faible que ce qu'il devrait être:


Dans mon cas, la conversion sera plus fidèle si j'utilise l'équation 7,68E-4 * analogRead() + 0,1734 (sauf aux deux extrémités de la courbe). Je ne sais pas si cette courbe d'étalonnage s'applique à tous les exemplaires de l'ESP32, par contre.



Yves Pelletier   (TwitterFacebook)

8 commentaires:

  1. Bonjour, merci beaucoup pour votre article.
    J'essaie de lire la tension en sortie d'un panneau photovoltaIque, et donc avec une tension extérieure à l'ESP32.
    Du coup, quel serait la modification pour lire du 3V maximum sur le GPIO33 ?
    Cordialement,
    JCF

    RépondreSupprimer
  2. Très bon article. Effectivement le CAN de l'ESP32 est moins précis que celui de l'arduino (10 bits)
    Pour le calcul, je chipote un peu mais je fais : analogRead() * 3,3 / 4096

    RépondreSupprimer
    Réponses
    1. Merci!
      (Je chipote un peu, mais pourquoi remplacer le bon calcul (analogRead()*3.3/4095) par une version légèrement erronée?)

      Supprimer
  3. Merci pour cet excellent article, une question me vient lorsqu'on mesure une tension total étrangère à l'esp, qu'elle référence a le 3 volts et quelques issus d'un pont diviseur sur une alimentation ou une batterie par exemple. Car sur un diviseur le bon potentiel 3.2v sur une batterie de 13v s'obtient entre le + 13 et le milieu du pont diviseur on ne peut donc pas mettre le gnd sur le plus batterie ?
    Bref quel est la référence du plus 3 volt qui est lu sur l'entrée ? Mer i de vos éclairages bienveillants

    RépondreSupprimer
  4. Bonjour, vous pouvez réaliser votre pont diviseur avec 2 résistances, l'une de 3,3k reliée à +13v, en série avec l'autre de 1k qui donne sur GND (le - de la batterie).
    En appliquant le diviseur de tension vous aurez bien vos 3v sur le point milieu

    RépondreSupprimer
  5. Article intéressant, je vous recommande une application sur ce sujet via ce lien

    https://youtu.be/I24Em0m0r5s

    RépondreSupprimer
  6. Bonjour, et merci pour ce travail.
    Mes tests confirment que l'adc est inutilisable sous 30 mV et au dessus de 3130 (troncage).
    Et même entre les 2, il est fortement non linéaire: écart jusqu'à plus de 100mV par rapport à la droite de régression!! Si l'on reste entre 100 et 2500 mV la non linéarité reste inférieure à 10 mV, ce qui reste beaucoup par rapport à la résolution de l'adc (+ petit écart mesurable) qui vaut 3300/4096, donc de l'ordre du mV. Dans tous les cas, une correction linéaire restera nécessaire (erreur de gain de 5% et offset de 100 mV!)
    Et c'est pas tout! l'ADC est extrêmement bruité: de l'ordre de 50 mV (pour le port 36, mais 10 pour le 13...), qui doivent pouvoir se réduire à l'aide d'un condensateur.
    Autre méthode sympa pour réduire le bruit: le moyennage, ou "dithering", si vous n'êtes pas "pressé" car la mesure devient plus lente: si certaines conditions stat. sont réunies, vous pouvez réduire le bruit d'un facteur racine(N) si vous moyennez sur N valeurs. Ainsi, vous pouvez réduire le bruit de 50 à 1 mV en moyennant 50x50=2500 fois. J'ai vérifié, ça marche. En moyennant 10000 fois, la mesure dure environ 1 seconde et le bruit descend sous la résolution initiale, on peut ainsi obtenir une "pseudo-résolution" plus petite que l'initiale (paradoxe du dithering) à condition de mettre des float où il faut:

    float ADCmoy() // mesure ADC moyennée
    {
    #define NSum 10000.0 // moyennage NSum fois, le mesure durera environ NSum fois plus longtemps, de l'ordre de la seconde pour 10000
    long sum=0; // initialisation somme
    for(int i=0; i<NSum;i++){
    sum+=analogRead(36);
    }
    return (float)sum/NSum; // resultat, attention, en float pour ne pas perdre une partie du benefice du dithering...
    }

    RépondreSupprimer