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.
- 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 } }