Bonjour,
Enseignant de sciences physiques, je souhaiterais monter un petit projet de suivi de la charge d'un circuit RC par Arduino avec commande de la carte Arduino (Uno) par Python. Je ne souhaite pas utiliser pyfirmata et préfère interagir directement via Serial (dans un but pédagogique mais aussi pour découvrir le fonctionnement de Serial). La charge dure quelques millisecondes (capacité de 47nF et résistance de 100kohms) et la communication via le port Série étant trop lente, je stocke les mesures sur l'Arduino afin de les transmettre en un bloc à la fin des mesures. Chaque mesure code sur 2 octets l'instant de la mesure et la tension mesurée (codée sur 10 bits par l'Arduino).
Le programme Python lance la mesure et récupère les données pour les tracer. Je place ci-dessous les deux codes :
Code Arduino :
int voltage = 0; // tension mesurée
unsigned long start_time;
unsigned long time_actuel;
#define MESURE 101 //les informations envoyées par le programme Python pour lancer les subroutines
#define FLUSH_BUFFER 102
#define N 300 //le nombre de mesures
uint8_t buffer[4 * N]; //chaque mesure utilise 4 bytes
//===============================================================================
// Subroutines
//===============================================================================
void FlushBuffer() //vide le port Série
{
while (Serial.available()>0) Serial.read();
}
void Mesure()
{
int n;
int debutExperience;
unsigned long delta_t;
int delta_t_int;
debutExperience = 0;
digitalWrite(8,LOW); //le "générateur", pin8, est remis à zéro
delay(1000); //on attend 1 seconde la décharge éventuelle du condensateur
start_time = micros();
for (n=0; n<N; n++){
time_actuel = micros();
if ((time_actuel-start_time>3000) && debutExperience == 0 ){
digitalWrite(8,HIGH); //au bout de 3ms on allume le "générateur"
debutExperience = 1;
}
delta_t=micros()-start_time;
delta_t_int=(int) (delta_t);
voltage=analogRead(0);
buffer[4*n]=(delta_t_int>>8)&0xFF; //octet de poids fort pour l'instant t
buffer[4*n+1]=delta_t_int & 0xFF; //octet de poids faible pour l'instant t
buffer[4*n+2]=(voltage>>8)&0xFF; //octet de poids fort pour la tension
buffer[4*n+3]= voltage & 0xFF; //octet de poids faible pour la tension
delay(0.5); //on attend 0.5ms entre deux mesures
}
Serial.write(buffer,4*N); //une fois les N mesures faites, on envoie au programme Python
}
//===============================================================================
// Initialization
//===============================================================================
void setup()
{
Serial.begin (500000);
while (Serial.available()>0) Serial.read(); //on teste la communication via le port Série
char c;
c = 0;
Serial.write(c);
c = 255;
Serial.write(c);
c = 0;
Serial.write(c);
}
//===============================================================================
// Main
//===============================================================================
void loop()
{
char commande;
if (Serial.available()>0) {
commande = Serial.read();
if (commande == MESURE) {
Mesure();
}
else if (commande == FLUSH_BUFFER){
FlushBuffer();
}
}
delay(0.0001);
}
Et du côté de Python :
import matplotlib.pyplot as plt
import serial
class Arduino():
def __init__(self,port):
self.ser = serial.Serial(port,baudrate=500000)
c_recu = self.ser.read(1) #on teste la communication via le port Série
while ord(c_recu)!=0:
c_recu = self.ser.read(1)
c_recu = self.ser.read(1)
while ord(c_recu)!=255:
c_recu = self.ser.read(1)
c_recu = self.ser.read(1)
while ord(c_recu)!=0:
c_recu = self.ser.read(1)
print("Communication établie")
##############################################################################
#Création des variables de commande
##############################################################################
self.MESURE = chr(101).encode()
self.FLUSH_BUFFER = chr(102).encode()
def close(self):
#libération du port série
self.ser.close()
def flushBuffer(self):
#nettoyage du buffer
self.ser.write(self.FLUSH_BUFFER)
def mesure(self):
#mesure sur 300 points
self.ser.write(self.MESURE)
liste_recus = []
N=300
liste_recus = self.ser.read(4*N)
temps = []
voltage = []
for n in range(N):
temps_1n = (liste_recus[4*n])
temps_2n = (liste_recus[4*n+1])
volt_1n = (liste_recus[4*n+2])
volt_2n = (liste_recus[4*n+3])
temps.append(float(temps_1n*0x100+temps_2n)/1000)
voltage.append(float(volt_1n*0x100+volt_2n)*5/1024)
return temps,voltage
port="/dev/cu.usbmodem1421"
ard=Arduino(port)
ard.flushBuffer()
charge=ard.mesure()
ard.close()
plt.figure()
plt.plot(charge[0],charge[1],'+')
plt.show()
Le programme fonctionne très bien à la première exécution comme le montre la 1ère image attachée. La charge affichée correspond au comportement attendu. Par contre, quand je relance le programme dans la foulée, j'obtiens du bruit proche de 5V.
Je dois débrancher la connexion USB et la rebrancher pour que je puisse retrouver une belle charge de condensateur quand je relance le programme (mais à l'exécution suivante je retombe sur du bruit).
Voici les différentes modifications que j'ai essayées mais aucune n'a modifié le comportement :
- j'ai relié physiquement la masse de la carte Arduino à une masse EDF via un fil,
- j'ai testé les programmes avec une autre carte Arduino Uno et sur un autre ordinateur portable,
- j'ai modifié la durée entre deux mesures.
La seule modification qui a fonctionné était de brancher une led entre la sortie 8 et la masse (je voulais tester le bon fonctionnement de la commande digitalWrite) : dans ce cas j'obtiens à chaque exécution une bonne charge (mais la tension en régime stationnaire est de 2,5V et non 5V).
Je suspecte un comportement capacitif de la sortie digitale mais je vous avoue que c'est un peu flou. Est-ce que quelqu'un aurait une idée ou aurait déjà rencontré de tels problèmes ?
Je vous remercie par avance pour le temps que vous prendrez à réfléchir à ce problème (qui est peut-être très simple).