Voltmètre de précision avec Arduino, ADS1115, bluetooth HC05 et Processing Datalogging
Le schield pour Arduino UNO
HC06 est l'émetteur récepteur bluetooth qui assure la liaison série entre Arduino Uno et l'autre correspondant bluetooth comme un portable, une tablette ou un smartphone. Ses connexions à l'Arduino UNO sont les suivantes: Tx sur Rx (UNO) et Tx (UNO) par l'intermédiare d'un pont de 2 résistances (1K et 2K) pour ajuster la tension de 5V (UNO) vers 3,3V (HC05).
La précision est toujours inférieure au mV sur la plus grande échelle de mesure c'est-à-dire 5V. En théorie on devrait avoir 5000mV/32767 = 0,153 mV ce qui est très intéressant. Mais en pratique il se peut que du bruit electromagnétique influence les mesures. Toutefois on peut compter sur une bonne précision et comme on peut le constater sur les enregistrements plus bas, les courbes se présentent avec un taux de bruit pratiquement imperceptible. Les résultats sont donc encourageant pour un système à transmission bluetooth, c'est-à-dire sans fil.
Exemple de résultats acquis par le programme Processing
Ce graphe de mesures de tension en fonction du temps est obtenu sur mon portable avec le sketch Processing ci-dessous selon le montage décrit dans le schéma ci-dessous. L'enregistrement comprend 1 mesure par seconde sur chacun des deux canaux soit au maximum 2000 points en 2000 secondes de gauche à droite. Les valeurs courantes en volt sont affichées en directe dans les rectangle en grisé. La précision et la stabilité des mesures sont excellentes.
Sketch Processing pour l'acquisition de mesures ADS1115 en mode différentiel
/***** ReadADS1115_Differential_DUAL_SL33.pde *******************************************
* Convertisseur ADS1115 donne les valeurs différentielles de ADC0-ADC1 et ADC2-ADC3 dans le buffer "communication série" et
* les enregistre 1 fois par 2 seconde dans le fichier xxxxxx.txt selon les réglages
* Sketch à mettre dans Arduino "**** UNO_ADS1115_Differential_DUAL_Bluetooth_SL01.ino *****"
* ça fait un voltmètre bluetooth très précis
* Paramètres ajustables de la fenêtre Vmin= 9V et Vmax= 15V par exemple
* validé slazare 2020 03 13
*/
import processing.serial.*;
PrintWriter output;
Serial myPort;
float val =20;
float val2 =10;
float voltage = 0 ;
float voltage2 = 0;
int tictime=0; //arrêt du temps à chaque enregistrement
int period = 999; //détermine la fréquences des points de mesure et d'affichage
int xPos = 1; // horizontal position of the graph
float scalefactor = 0.1875F; // This is the scale factor for the default +/- 6.144 Volt Range we will use
float Vmin = 0; // en V
float Vmax = 5; // en V
//Variables to draw a continuous line.
int lastxPos=1;
int lastheight=500;
int lastheight2=500;
void setup() {
size(2000,1000); //peut être adapté à 200, 1023 par exemple
myPort = new Serial(this, Serial.list() [2], 9600); //HC05-2 c'est COM8 donc faut mettre [2] ou [3] ESP32-GOUU COM19 c'est 11 ESP32-WROOMCOM20 c'est 12
printArray(Serial.list());
output = createWriter("ReadADS1115_Differential_DUAL_SL02.txt"); //fichier et nom à changer
stroke(0,0,0);
strokeWeight(1);
line(0, height-height*(10-Vmin)/(Vmax-Vmin), width, height-height*(10-Vmin)/(Vmax-Vmin)); //ligne des 10V
stroke(255,0,0);
line(0, height-height*(12-Vmin)/(Vmax-Vmin), width, height-height*(12-Vmin)/(Vmax-Vmin)); //ligne des 12V
stroke(0,0,255);
line(0, height-height*(14-Vmin)/(Vmax-Vmin), width, height-height*(14-Vmin)/(Vmax-Vmin)); //ligne des 14V
//Date et heure imprimé en haut du fichier de sauvegarde
int s = second(); // Values from 0 - 59
int m = minute(); // Values from 0 - 59
int h = hour(); // Values from 0 - 23
int d = day();
int mo = month();
int y = year();
output.print(y); output.print(" ");
output.print(mo); output.print(" ");
output.print(d); output.print(" ");
output.print(h);
output.print('h');
output.print(m);
output.print('m');
output.print(s);
output.println('s');
//rect(800, 1000, 300, 1000);
}
void draw() {
while (myPort.available() > 0) {
String sb = myPort.readStringUntil('\n');
if (sb != null)
{
String A0A1 = sb.substring(0,5);
String A2A3 = sb.substring(5,10);
val = float(A0A1)-50000; //j'enlève 50000 aux valeurs transférées qui avait été ajouté pour que les 5 digits restent constants
val2 = float(A2A3)-50000; //idem
//voltage = 3.53501*(val*scalefactor)/1000; //le coeff 3.53501* c'est pour l'échelle 0-17V seulement
voltage = (val*scalefactor)/1000;
voltage2 = (val2*scalefactor)/1000;
}
}
textSize(32);
if (millis()-tictime>period) { //à chaque échéance "tick" reçoit, calcule, enregistre, trace et affiche
String sf2 = nf(voltage, 0, 5); // transforme variable en string avec 5 après la virgule
String sf3 = nf(voltage2, 0, 5);
//String sf2 = nf(val, 0, 0);
//Ecriture dans le fichier dont le nom est défini ci-dessus
output.print(sf2); // Prints "2.51200" for example
output.print(" ");
output.println(sf3);
//output.println(voltage);
tictime=millis();
//Draw a red line from Last voltage point to the new one.
stroke(255,0,0); //stroke color
strokeWeight(4); //stroke wider
line(lastxPos, lastheight, xPos, height - height*(voltage-Vmin)/(Vmax-Vmin));
lastheight= int(height-height*(voltage-Vmin)/(Vmax-Vmin));
//Draw a green line from Last voltage point to the new one.
stroke(0,200,100); //stroke color green
line(lastxPos, lastheight2, xPos, height - height*(voltage2-Vmin)/(Vmax-Vmin));
lastxPos= xPos;
lastheight2= int(height-height*(voltage2-Vmin)/(Vmax-Vmin));
//renouvelle l'affichage des valeurs mesurées dans un rectangle
fill(#C1C1C1); //255, 255, 255
noStroke();
rect(100, 130, 260, 90); //rectangle d'affichage des deux mesures 280x90
rect(1000, 130, 260, 90);
textSize(64);
fill(0, 100, 153); //définit la couleur des caractères RGB par défaut
text(nf(voltage, 0, 4), 100, 200); //la fonction nf(val, 0, 4) format le float à 2 après virgule
text(nf(voltage2, 0, 4), 1000, 200);
//text(int(val), 100, 200); //l
//text(int(val2), 1000, 200);
xPos++;
}
}
void keyPressed() { // press any key or spacebar
output.print("FIN");
output.flush(); // Writes the remaining data to the file
output.close(); // Finishes the file
exit(); // Stops the program
}
//*******************************************************************************************************
Quelques explications sur le sketch Processing
ADS1115 est un convertisseur 16-bit (15-bit utiles ou 32767 points), bon marché, qui offre bien plus de précision que celui de l'ATMega 328 de l'Arduino UNO qui lui est limité à 12-bit et est mono-canal. La lecture de la datasheet montre qu'il possède 4 canaux de mesures de tension (A0, A1, A2, A3) par rapport à la masse et que ces canaux peuvent fonctionner en mode différentiel, c'est-à-dire positionnés entre 4 points quelconque du circuit. Les 4 points de mesure A0-A3 doivent avoir un potentiel positif par rapport à la masse, sous peine d'endommagement du circuit. Le mode différentiel de mesure donne deux valeurs V1-V2 et V3-V4, il est intéressant pour, par exemple acquérir simultanément tension et intensité dans un circuit. De plus on peut le paramétrer au choix pour privilégier soit la précision soit la fréquence des mesures et ajuster l'échelle à l'aide du gain programmable. On peut utiliser la configuration par défaut du chip ADS1115 ou bien opter pour des valeurs différentes des paramètres de configuration, contenus dans les 16 bits (2 bytes) du registre de configuration, qu'il convient de programmer dans le programme d'utilisation Arduino. On se référera à la datasheet tableau 8 pour la signification des paramètres de configuration et pour les fonctions de configuration aux différentes libraries I2C disponibles pour l'ADS1115 (https://github.com/adafruit/Adafruit_ADS1X15, https://github.com/jrowberg/i2cdevlib) .
HC06 est l'émetteur récepteur bluetooth qui assure la liaison série entre Arduino Uno et l'autre correspondant bluetooth comme un portable, une tablette ou un smartphone. Ses connexions à l'Arduino UNO sont les suivantes: Tx sur Rx (UNO) et Tx (UNO) par l'intermédiare d'un pont de 2 résistances (1K et 2K) pour ajuster la tension de 5V (UNO) vers 3,3V (HC05).
La précision est toujours inférieure au mV sur la plus grande échelle de mesure c'est-à-dire 5V. En théorie on devrait avoir 5000mV/32767 = 0,153 mV ce qui est très intéressant. Mais en pratique il se peut que du bruit electromagnétique influence les mesures. Toutefois on peut compter sur une bonne précision et comme on peut le constater sur les enregistrements plus bas, les courbes se présentent avec un taux de bruit pratiquement imperceptible. Les résultats sont donc encourageant pour un système à transmission bluetooth, c'est-à-dire sans fil.
Sketch Arduino
/**** UNO_ADS1115_Differential_DUAL_Bluetooth_SL01.ino *****************************
* celui-ci est pour la liaison série-Bluetooth seulement
* slazare 2020 02 20
*/
#include <Wire.h>
#include <Adafruit_ADS1015.h>
Adafruit_ADS1115 ads; // Declare an instance of the ADS1115
int16_t rawADCvalue[2]; // The is where we store the value we receive from the ADS1115
float scalefactor = 0.1875F; // This is the scale factor for the default +/- 6.144 Volt Range we will use
float volts = 0.0; // The result of applying the scale factor to the raw value
void setup(void)
{
Serial.begin(9600);
ads.begin();
ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV 0.1875mV (default)
//ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV
// ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV 0.0625mV
// ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5mV 0.03125mV
// ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25mV 0.015625mV
// ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125mV 0.0078125mV
}
void loop(void)
{
rawADCvalue[0] = ads.readADC_Differential_0_1();
rawADCvalue[1] = ads.readADC_Differential_2_3();
//volts = (rawADCvalue * scalefactor)/1000.0;
//Serial.print("Raw ADC Value = ");
//Serial.print(rawADCvalue);
Serial.print(50000+rawADCvalue[0]); //50000 ajouté pour que le nombre ait constamment 5 digit et toujours positif
//Serial.print(" ");
Serial.print(10000+rawADCvalue[1]); //idem
// Serial.print("\tVoltage Measured = ");
//Serial.println(volts,6);
Serial.println();
delay(1000);
}
//************************************************************************************************************ celui-ci est pour la liaison série-Bluetooth seulement
* slazare 2020 02 20
*/
#include <Wire.h>
#include <Adafruit_ADS1015.h>
Adafruit_ADS1115 ads; // Declare an instance of the ADS1115
int16_t rawADCvalue[2]; // The is where we store the value we receive from the ADS1115
float scalefactor = 0.1875F; // This is the scale factor for the default +/- 6.144 Volt Range we will use
float volts = 0.0; // The result of applying the scale factor to the raw value
void setup(void)
{
Serial.begin(9600);
ads.begin();
ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV 0.1875mV (default)
//ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV
// ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV 0.0625mV
// ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5mV 0.03125mV
// ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25mV 0.015625mV
// ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125mV 0.0078125mV
}
void loop(void)
{
rawADCvalue[0] = ads.readADC_Differential_0_1();
rawADCvalue[1] = ads.readADC_Differential_2_3();
//volts = (rawADCvalue * scalefactor)/1000.0;
//Serial.print("Raw ADC Value = ");
//Serial.print(rawADCvalue);
Serial.print(50000+rawADCvalue[0]); //50000 ajouté pour que le nombre ait constamment 5 digit et toujours positif
//Serial.print(" ");
Serial.print(10000+rawADCvalue[1]); //idem
// Serial.print("\tVoltage Measured = ");
//Serial.println(volts,6);
Serial.println();
delay(1000);
}
Exemple de résultats acquis par le programme Processing
La variation programmée de la tension a été faite avec un module MCP4725 comme décrit plus bas. Le graphe a été enregistré à une mesure par seconde et il contient 2000 mesures de la gauche vers la droite, ce qui fait une durée d'enregistrement de 33 minutes. La fréquence des mesures et la durée d'enregistrement peut être ajustée par l'utilisateur.
Ci-dessous en mode différentiel avec des mesures négatives sur l'une des voies. Par-contre les 4 broches de mesure doivent avoir un potentiel positif au dessus de la masse sous peine d'endommagement du chip ADS1115.
Ce graphe de mesures de tension en fonction du temps est obtenu sur mon portable avec le sketch Processing ci-dessous selon le montage décrit dans le schéma ci-dessous. L'enregistrement comprend 1 mesure par seconde sur chacun des deux canaux soit au maximum 2000 points en 2000 secondes de gauche à droite. Les valeurs courantes en volt sont affichées en directe dans les rectangle en grisé. La précision et la stabilité des mesures sont excellentes.
Sketch Processing pour l'acquisition de mesures ADS1115 en mode différentiel
/***** ReadADS1115_Differential_DUAL_SL33.pde *******************************************
* Convertisseur ADS1115 donne les valeurs différentielles de ADC0-ADC1 et ADC2-ADC3 dans le buffer "communication série" et
* les enregistre 1 fois par 2 seconde dans le fichier xxxxxx.txt selon les réglages
* Sketch à mettre dans Arduino "**** UNO_ADS1115_Differential_DUAL_Bluetooth_SL01.ino *****"
* ça fait un voltmètre bluetooth très précis
* Paramètres ajustables de la fenêtre Vmin= 9V et Vmax= 15V par exemple
* validé slazare 2020 03 13
*/
import processing.serial.*;
PrintWriter output;
Serial myPort;
float val =20;
float val2 =10;
float voltage = 0 ;
float voltage2 = 0;
int tictime=0; //arrêt du temps à chaque enregistrement
int period = 999; //détermine la fréquences des points de mesure et d'affichage
int xPos = 1; // horizontal position of the graph
float scalefactor = 0.1875F; // This is the scale factor for the default +/- 6.144 Volt Range we will use
float Vmin = 0; // en V
float Vmax = 5; // en V
//Variables to draw a continuous line.
int lastxPos=1;
int lastheight=500;
int lastheight2=500;
void setup() {
size(2000,1000); //peut être adapté à 200, 1023 par exemple
myPort = new Serial(this, Serial.list() [2], 9600); //HC05-2 c'est COM8 donc faut mettre [2] ou [3] ESP32-GOUU COM19 c'est 11 ESP32-WROOMCOM20 c'est 12
printArray(Serial.list());
output = createWriter("ReadADS1115_Differential_DUAL_SL02.txt"); //fichier et nom à changer
stroke(0,0,0);
strokeWeight(1);
line(0, height-height*(10-Vmin)/(Vmax-Vmin), width, height-height*(10-Vmin)/(Vmax-Vmin)); //ligne des 10V
stroke(255,0,0);
line(0, height-height*(12-Vmin)/(Vmax-Vmin), width, height-height*(12-Vmin)/(Vmax-Vmin)); //ligne des 12V
stroke(0,0,255);
line(0, height-height*(14-Vmin)/(Vmax-Vmin), width, height-height*(14-Vmin)/(Vmax-Vmin)); //ligne des 14V
//Date et heure imprimé en haut du fichier de sauvegarde
int s = second(); // Values from 0 - 59
int m = minute(); // Values from 0 - 59
int h = hour(); // Values from 0 - 23
int d = day();
int mo = month();
int y = year();
output.print(y); output.print(" ");
output.print(mo); output.print(" ");
output.print(d); output.print(" ");
output.print(h);
output.print('h');
output.print(m);
output.print('m');
output.print(s);
output.println('s');
//rect(800, 1000, 300, 1000);
}
void draw() {
while (myPort.available() > 0) {
String sb = myPort.readStringUntil('\n');
if (sb != null)
{
String A0A1 = sb.substring(0,5);
String A2A3 = sb.substring(5,10);
val = float(A0A1)-50000; //j'enlève 50000 aux valeurs transférées qui avait été ajouté pour que les 5 digits restent constants
val2 = float(A2A3)-50000; //idem
//voltage = 3.53501*(val*scalefactor)/1000; //le coeff 3.53501* c'est pour l'échelle 0-17V seulement
voltage = (val*scalefactor)/1000;
voltage2 = (val2*scalefactor)/1000;
}
}
textSize(32);
if (millis()-tictime>period) { //à chaque échéance "tick" reçoit, calcule, enregistre, trace et affiche
String sf2 = nf(voltage, 0, 5); // transforme variable en string avec 5 après la virgule
String sf3 = nf(voltage2, 0, 5);
//String sf2 = nf(val, 0, 0);
//Ecriture dans le fichier dont le nom est défini ci-dessus
output.print(sf2); // Prints "2.51200" for example
output.print(" ");
output.println(sf3);
//output.println(voltage);
tictime=millis();
//Draw a red line from Last voltage point to the new one.
stroke(255,0,0); //stroke color
strokeWeight(4); //stroke wider
line(lastxPos, lastheight, xPos, height - height*(voltage-Vmin)/(Vmax-Vmin));
lastheight= int(height-height*(voltage-Vmin)/(Vmax-Vmin));
//Draw a green line from Last voltage point to the new one.
stroke(0,200,100); //stroke color green
line(lastxPos, lastheight2, xPos, height - height*(voltage2-Vmin)/(Vmax-Vmin));
lastxPos= xPos;
lastheight2= int(height-height*(voltage2-Vmin)/(Vmax-Vmin));
//renouvelle l'affichage des valeurs mesurées dans un rectangle
fill(#C1C1C1); //255, 255, 255
noStroke();
rect(100, 130, 260, 90); //rectangle d'affichage des deux mesures 280x90
rect(1000, 130, 260, 90);
textSize(64);
fill(0, 100, 153); //définit la couleur des caractères RGB par défaut
text(nf(voltage, 0, 4), 100, 200); //la fonction nf(val, 0, 4) format le float à 2 après virgule
text(nf(voltage2, 0, 4), 1000, 200);
//text(int(val), 100, 200); //l
//text(int(val2), 1000, 200);
xPos++;
}
}
void keyPressed() { // press any key or spacebar
output.print("FIN");
output.flush(); // Writes the remaining data to the file
output.close(); // Finishes the file
exit(); // Stops the program
}
//*******************************************************************************************************
Quelques explications sur le sketch Processing
Le buffer recevant les données transmises par la communication série de l'arduino est lu par le sketch Processing comme une chaîne de caractères, ce qui veut dire que les nombres flottants ou entiers ne sont pas reconnus simplement. Il convient donc de lire octet par octet avec la fonction readStringUntil(\n) jusqu'à la fin signalée par la présence de \n et de définir les sous-chaînes de 5 digits qui vont définir les valeurs communiquées et de les convertir en nombres entiers avant calcul des tensions mesurées. Le convertisseur ADS1115 transmet des nombres compris entre 1 et 2^16=65536 (outre les valeurs de tension négatives qui sont possibles). C'est pour cela que les 5 digits transférés sont suffisants pour une bonne précision. Sur une échelle de 5V cela fait une imprécision inférieure au mV, ce qui très acceptable pour beaucoup d'applications. Les données transmises sont lues à la cadence de une fois par seconde dans l'exemple ci-dessus mais des fréquences plus rapides ou plus lentes sont possibles. Si on regarde de plus près, on voit que les deux nombres fournis par ADS1115 sont concaténés dans le buffer de communication série. Un problème surviendrait lorsque ces valeurs d'entier deviennent inférieures à 10000, 1000, 100 et 10 puisque le nombre de digits à ce moment-là passe de 5 à 4, puis à 3, 3 et 1. Alors on aurait un problème à la lecture des digits qui ne seraient pas en nombres constants. C'est pour cela que le sketch Arduino ajoute 50000 à chaque valeur avant la communication de manière à ne jamais avoir de valeur inférieure à 10000 et de maintenir le nombre de digit à 5 pour chaque nombre. Le buffer est donc lu en entier sur ses dix digits (variable sb), qui contiennent une valeur sur les 5 premiers et la deuxième sur les 5 autres. Avec les fonctions substring() les deux nombres sont séparés. Ensuite il suffit de retirer 50000 aux valeurs lues par le sketch Processing après transmission. Une fois les deux valeurs entières obtenues, on obtient les tensions en les multipliant par le facteur d'échelle appropriée qui dépend du gain de l'ADS1115. La fenêtre de traçage peut être ajustée à volonté ainsi que la fréquence des mesures. Les valeurs sont stockées dans un fichier text pour une éventuelle analyse ultérieure.
Exemple de mesures de tension "programmée" avec un DAC MCP4725
Exemple de mesures de tension "programmée" avec un DAC MCP4725
Commentaires
Enregistrer un commentaire