La mesure d'une tension à l'aide d'un Arduino UNO utilise une des entrées du convertisseur A/D. A lire les différents exemples, cette utilisation semble simple, mais je me suis vite rendu compte que le problème était plus ardu qu'il n'y paraissait !

Pour finir, j'ai obtenu de très bons résultats en prenant certaines précautions.

A la lecture de nombreux articles et exemples, il apparaît que beaucoup de bêtises sont écrites... A commencer par le calcul du pas de l'ADC. La plupart des exemples (même celui livré avec l'Arduino) divisent la tension de référence par 1023, alors qu'il y a 1024 pas qui vont de 0 à 1023 , d'où la confusion !! 

Ensuite, la tension de référence utilisée est souvent la tension d'alimentation théorique de 5V. Mauvaise idée, car cette tension est souvent différente des 5 V théoriques. Le port USB ne fournit qu'une tension approximative et le régulateur 5V n'est pas très précis.. Pour couronner le tout, la ligne 5V est fort "bruyante" à cause des remontées de signaux et commutations du processeur et tous les éléments connectés sur le 5V..
Pour pallier en partie ce problème, on peut utiliser la tension de référence interne du processeur qui est de 1,100V. Mais là aussi, il faut se méfier, car les caractéristiques du constructeur ATMEL donnent une précision de 10 % !! C'est à la limite pire que la référence Vcc 5V !

La solution "riche" est d'utiliser un composant externe pour générer une tension de référence précise et la plus stable possible. Je ne traiterai pas cette solution que je n'ai pas adoptée pour des raisons de coût et simplification.

Ma solution  est d'utiliser la référence interne après l'avoir mesurée avec précision. A noter que cette référence varie malheureusement en température...

J'ai donc utilisé ce petit sketch qui permet de la mesurer en positionnant la référence interne et en mettant le processeur en phase de mesure. J'ai trouvé 1,066V , 1,111V suivant le module UNO..

// mettre une capa de 0.1uF sur la pin AREF
// lancer le script et mesurer la tension AREF au bout de quelques cycles

const uint8_t PinLED = 13;

void setup( void ){

Serial.begin( 38400 );
Serial.println( "\r\n\r\n" );
pinMode( PinLED, OUTPUT );
digitalWrite( PinLED, LOW );
delay( 1000 );
analogReference( INTERNAL );
	}

void loop( void ) {
Serial.println( analogRead( 0 ) );
digitalWrite( PinLED, HIGH );
;delay( 1000 );
}

 

Autre problème, le pont diviseur de tension qui permet de diminuer la tension à mesurer de 12V en dessous de 1,1V qui est la tension maximum mesurable en utilisant la référence interne.

ATMEL recommande d'utiliser des résistances afin d'avoir environ 10 kOhms à l'entrée du convertisseur A/D. Pour pouvoir mesurer une tension maximale de 15V, il faut un rapport de division de 15V / 1,1V = 13,63 , on choisit 13 pour avoir des valeurs de résistances communes. Ce qui donne des résistances de 120k et 10k en série, soit une consommation de près de 10 uA.
Pour une utilisation très faible consommation sur batterie, toute consommation, même minime est à prendre en considération ! J'ai donc pour commencer, multiplié ces valeurs par 10, soit 1,2M et 100k.
Une autre solution serait de conserver des valeurs faibles de résistances afin de s'affranchir du bruit et d'isoler le pont diviseur et ne le raccorder qu'au moment de la mesure à l'aide d'un transistor Mosfet.  ( à tester )
 
Dans tous les cas, il faut utiliser des résistances de précision afin d'être certain du rapport de division et de garantir une stabilité dans le temps, et mettre un condensateur de 0.1uF entre la masse et les pins AREF et l'entrée analogique utilisée afin de filtrer le bruit présent sur ces pin.
 
Problème suivant, la précision de la mesure du convertisseur. La documentation ATMEL stipule qu'après la mise en service du convertisseur, la première mesure est "probablement" incohérente.
Mes essais m'ont montré que les 3 premières mesures ne sont pas stables !! 
Il existe plusieurs méthodes pour paller ce problème, soit faire une série de mesures  afin de "lisser" la moyenne et corriger les erreurs, ou de ne garder que la dernière mesure.
J'ai choisi la 2ème méthode qui permet d'éviter de faire trop de mesures.
 
Une fois la mesure analogique réalisée, il faut convertir cette valeur de 0 à 1023 issue du DAC en une valeur en Volts. Il faut alors tenir compte de la VRAIE valeur de la tension de référence et du rapport du diviseur de tension..
J'ai trouvé 2 méthodes :
  • La traditionnelle qui utilise la méthode du calcul d'un pas de l'AD
  • La plus originale qui utilise la fonction map() qui permet de faire une translation de valeurs.

Pour le moment, j'utilise(rai) par commodité la 1ère solution. Elle permet également de corriger l'imprécision du diviseur de tension en ajustant le facteur multiplicateur. ;-)

Au cours de mon développement, j'ai constaté que la 1ère mesure est toujours complètement erronée. Le problème vient du fait que le convertisseur DAC met un certain temps à se "réveiller". J'ai donc rajouté une commande pour le réveiller et un délai afin de stabiliser le DAC AVANT de faire les mesures.

const float seuilBatterie = 11.5;                   // seuil d'alerte batterie
boolean alerteBatterie;                             // flag alarme batterie

void readBatterie() {
  int analogBatt = analogRead(0);                   // on réveille le DAC sur la pin A0
  delay(100);                                       // on attend que le DAC se stabilise 
  
  for (byte i=0; i <= 4; i++) {
      analogBatt = analogRead(A0);                  // on fait 4 mesures et on garde la dernière
    }
  tensionBatterie = analogBatt * (1.100 / 1024);    // 1.100 = AREF
  tensionBatterie *= 11.275 ;                       // facteur de multiplication du pont diviseur de tension
  
  if ( tensionBatterie < seuilBatterie ) {
      alerteBatterie = 1;                           // flag d'alerte si la batterie escend en-dessous d'un certain niveau

#if defined(DEVMODE)                                // option du compilateur pour supprimer la commande suivante en mode production   
      Serial.println(F("ALARME BATTERIE"));
#endif      
  }
}