RobArchi 1.XX
Ce tutoriel vient en complément du RobArchi X.xx qui était la base de départ.
Cette nouvelle version a pour objectif de proposer une surveillance du niveau de charge de la batterie.
Mots clefs :
Atmega 328p, Raspberry, I2C, batterie, NPN 2N2222, eeprom, 24LCXXX
Avertissements :
Comme pour la version X.xx, vous ne devez pas chercher, dans RobArchi, la solution à vos problèmes mais plus une approche de laboratoire de test et d'apprentissage.
En effet, multiplier les Atmegas comme je l'ai fait, n'ai pas la solution ultime. En terme de coût, ce n'est pas exorbitant, mais il serait difficile de tous les loger dans un gabarit de robot plus petit.
Objectifs :
Comme présenté ci-dessus la version 1.XX va apporter un contrôle régulier du niveau de charge de la batterie et cela à la demande de la carte Raspberry.
Il me semble qu’un robot d’exploration qui ne surveille pas la décharge de sa batterie prend beaucoup de risque.
Histoire de pousser le vice au maximum, j’ai décidé de jouer également avec une eeprom en I2C.
La carte du X.xx a donc subi une petite modification par l’implantation de l’eeprom 24LC128 juste au-dessus de la carte Pi mais toujours sur la ligne I2C coté 3V (annexe1 et 2 en bas de page).
Préambule :
Lors de mes tests sur la version X.xx je passais mon temps à monter et démonter mon Atméga à chaque modification du code… l’horreur.
Pour cette nouvelle étape je suis passé par un banc de tests : une plaque de test 840 points et Arduino Nano à la place d’un simple Atméga. Et une carte Raspberry avec un connecteur GPIO.
Déroulement du tuto :
Chapitre 1.1 : Création du shield accueillant la gestion de la batterie
Chapitre 1.2 : Mesure de la tension de la batterie arduino uniquement
Chapitre 1.3 : Mesure de la tension de la batterie à la demande du RaspberryChapitre 1.4 : Utilisation d’une eeprom entre l’Atmega et la Raspberry
Chapitre 1.5 : Mesure de la tension toutes les deux minutes via un job CRON
Annexes :
Annexe 1 - Mise à jour de la carte Version X.XX - Composants
Annexe 2 - Mise à jour de la carte Version X.XX - Stripboard
Annexe 3 – Création du shield batterie - Composants
Annexe 4 – Création du shield batterie - Stripboard
Chapitre 1.1 - Création du shield accueillant la gestion de la batterie
Je suis donc parti sur un deuxième shield fait maison qui sera ajusté à la carte de la version X.xx.
Les deux plaques sont mises en communication via un groupe de 2x6 « long header » pour envoyer sur le shield batterie l’alimentation et la connexion I2C.
La tension arrive directement sur la plaque 1.xx. Etant du genre distrait, j’ai mis un pont de diode afin de garantir la bonne polarité dans le circuit.
En regardant sur le web il y a beaucoup de référence sur le sujet.
J’ai choisi de suivre le blog suivant :
http://www.chicoree.fr/w/Mesurer_une_tension_avec_un_ATmega328P
Histoire d’éviter une consommation d’énergie trop important, j’ai associé un transistor 2N2222 dans mon circuit afin d’envoyer du 12V dans le pont diviseur uniquement au moment de la mesure.
Voici mon schéma de principe sans le pont de diode.
Chapitre 1.2 - Mesure de la tension de la batterie
La première étape sera la mesure de la batterie par « l’Atméga batterie ».
Le code arduino de l’Atmega
#include <stdlib.h> // Constants const int analogInPin = A0; // Analog pin the voltage divider is attached to const float Vcc = 5; // µC Vcc (V) const float R1 = 27000; // Voltage divider R1 (Ohm) const float R2 = 10000; // Voltage divider R2 (Ohm) const int pause = 60*1000; // delay between samples (ms) void setup() { // initialize serial communications at 9600 bps: Serial.begin(9600); } void loop() { // read the analog in value: int sensorValue = analogRead(analogInPin); // Serial.println(sensorValue); // for debug / calibration float v_pin = Vcc*sensorValue/1023.; float v_bat = v_pin/(R2/(R1+R2)); // Convert to string using the format ddd.dd char buffer[21]; dtostrf(v_bat, 6, 2, buffer); buffer[6] = 0; // Display result Serial.println(buffer); // wait before the next loop delay(pause); }
Chapitre 1.3 - Mesure de la tension de la batterie à la demande du Raspberry
En reprenant une bonne partie du code de la version X.xx, c’est déjà plus facile.
Les tests ont été effectué sur une carte Arduino Uno puis une Arduino Nano et à mon grand bonheur, pas de difficulté.
Maintenant une fois le code transféré sur Ateméga « baterrie » grosse déception, au lieu de 11.56 V (mesure multimètre) je n’avais que 3.49V. Après trois jours de recherche j’ai capitulé en multipliant par 3.4 la mesure en entrée A0.
int sensorValue = analogRead(analogInPin)*3.4;
Je hercherais un autre jour la solution avec AREF certainement.
Le code arduino de l’Atmega
#include <Wire.h> #include <stdlib.h> #define SLAVE_ADDRESS 0x14 String dataReceived; char dataSend[6] ; int index = 0; // Constants const int analogInPin = A0; const int T2N = 9; const float Vcc = 5; const float R1 = 26500; // Voltage divider R1 (Ohm) const float R2 = 9910; // Voltage divider R2 (Ohm) void setup() { Wire.begin(SLAVE_ADDRESS); Wire.onReceive(receiveData); Wire.onRequest(sendData); pinMode(T2N, OUTPUT); } void loop() { /* Il n'y a rien dans la boucle infinie */ } void receiveData(int byteCount){ dataReceived = NULL; int numOfBytes = Wire.available(); byte b = Wire.read(); //cmd for(int i=0; i<numOfBytes-1; i++){ char c = (char) Wire.read(); dataReceived += (char) c; } digitalWrite(T2N, HIGH); int sensorValue = analogRead(analogInPin)*3.4; float v_pin = Vcc*sensorValue/1023; float v_bat = v_pin/(R2/(R1+R2)); dtostrf(v_bat, 6, 2, dataSend); dataSend[6] = 0; digitalWrite(T2N, LOW); } void sendData(){ Wire.write(dataSend[index]); ++index; if (index >= 6) { index = 0; } }
Coté Raspberry
#!/usr/bin/env python import smbus import time import os bus = smbus.SMBus(1) # I2C address of Arduino Slave i2c_add_batterie = 0x14 i2c_cmd = 0x01 def ConvertStringToBytes(src): converted = [] for b in src: converted.append(ord(b)) return converted data = "" # loop to send message exit = False while not exit: r = raw_input('Enter something, "q" to quit"') print(r) bytesToSend = ConvertStringToBytes(r) bus.write_i2c_block_data(i2c_add_batterie, i2c_cmd, bytesToSend) time.sleep(0.1) for i in range(0, 5): data += chr(bus.read_byte(i2c_add_batterie)); print data time.sleep(1); data = "" if r=='q': exit=True
Après connexion au raspberry en SSH, il suffit de taper une lettre pour recevoir de l’atmega la tension de la batterie.Chapitre 1.4 - Utilisation d’une eeprom entre l’Atmega et la Raspberry
Ne fondez pas de grand espoir dans cette partie car si cela fonctionne presque sur la plaque d'essai, c'est une véritable perte de temps après mise en place sur le robot.
Je vous mets les codes pour info ou tout simplement si vous voulez tenter le coup.
Histoire de préparer la version 2.xx j’ai décidé de jouer avec une eeprom en I2C forcément.
Pour la mise en place il y a beaucoup de tuto sur le net, il suffit de taper dans un navigateur :
arduino eeprom I2C
L’eeprom doit être du côté 5v du réseau I2C. En mettant les pins 1 à 4 à la masse, l’eeprom aura l’adresse 0x50.
Contrairement au Raspberry, l’Atmega (ou simplement l’Arduino) peut accepter d’être aussi bien esclave que maître mais forcément pas en même temps.
La démarche et simple, au démarrage, l’Atmega est en esclave jusqu’au moment de recevoir la commande du Raspberry. A ce moment-là, il ouvre une connexion I2C en qualité de maître vers l’eeprom pour y déposer la valeur de la tension de batterie puis revient à l’écoute du Raspberry.
Le raspberry attend 5 secondes puis va voir sur l’eeprom ce qu’a déposé l’atméga.
Dans le pays à Oui-Oui c’est comme ça que ça marche. Maintenant, il y a toujours un problème qui vient contredire la théorie. Dans la pratique, lorsque la Rpi affiche une erreu de connexion avec l’eeprom pour une histoire de délai dépassé. A première vue, l’Atméga semble garder un lien avec l’eeprom même lorsque la connexion est terminée avec :
Wire.endTransmission(deviceaddress);
Une solution c’est de rebooter l’Atméga. Coté informatique je n’ai pas trouvé, j’ai donc choisi la méthode électronique. Un 2222 en parallèle du bouton RESET avec une commande depuis le PIN 5. C’est un peu brutal mais ça marche (Sur la plaque d'essai).
Le code arduino de l’Atmega
#include <Wire.h> #include <stdlib.h> #define SLAVE_ADDRESS 0x14 #define I2C_add_eeprom 0x50 String dataReceived; char dataSend[6] ; unsigned int address = 0; // Constants const int analogInPin = A0; const int T2N = 9; const int RESET = 13; const float Vcc = 5; const float R1 = 26500; // Voltage divider R1 (Ohm) const float R2 = 9910; // Voltage divider R2 (Ohm) void setup() { Wire.begin(SLAVE_ADDRESS); Wire.onReceive(receiveData); pinMode(T2N, OUTPUT); pinMode(RESET, OUTPUT); } void loop() { /* Il n'y a rien dans la boucle infinie */ } void receiveData(int byteCount){ dataReceived = NULL; int numOfBytes = Wire.available(); byte b = Wire.read(); //cmd for(int i=0; i<numOfBytes-1; i++){ char c = (char) Wire.read(); dataReceived += (char) c; } digitalWrite(T2N, HIGH); int sensorValue = analogRead(analogInPin)*3.4; float v_pin = Vcc*sensorValue/1023; float v_bat = v_pin/(R2/(R1+R2)); dtostrf(v_bat, 6, 2, dataSend); dataSend[6] = 0; digitalWrite(T2N, LOW); writeEEPROM(I2C_add_eeprom, address); delay(1); digitalWrite(RESET, HIGH); } void writeEEPROM(int deviceaddress, unsigned int eeaddress) { Wire.beginTransmission(deviceaddress); Wire.write((int)(eeaddress >> 8)); // MSB Wire.write((int)(eeaddress & 0xFF)); // LSB for(int i=0; i<strlen(dataSend); i++){ Wire.write(dataSend[i]); } Wire.endTransmission(deviceaddress); delay(1); }
Coté Raspberry
#!/usr/bin/env python import smbus import time import os bus = smbus.SMBus(1) # I2C address of Arduino Slave i2c_add_batterie = 0x14 i2c_add_eeprom = 0x50 i2c_cmd = 0x01 def ConvertStringToBytes(src): converted = [] for b in src: converted.append(ord(b)) return converted level = "" # loop to send message exit = False while not exit: r = raw_input('Enter something, "q" to quit"') print(r) bytesToSend = ConvertStringToBytes(r) bus.write_i2c_block_data(i2c_add_batterie, i2c_cmd, bytesToSend) time.sleep(5) memory_address = 0 data="" bus.write_byte(i2c_add_eeprom, memory_address >> 8) bus.write_byte(i2c_add_eeprom, memory_address & 0xff) for i in range(0,10): data =bus.read_byte(i2c_add_eeprom) if data>45 and data<58: level +=chr(data) print level time.sleep(1) data = "" level = „“ if r=='q': exit=True }
Chapitre 1.5 - Mesure de la tension toutes les deux minutes via un job CRON
Pour terminer cette version 1.xx je voudrais connaître le temps de décharge de la batterie sans surveillance de ma part.
Je vais donc créer un nouveau fichier Python au nom de « batterie.py » qui sera dans le répertoire : /home/pi/batterie.py
Je suis reparti sur la base du chapitre 1.3
Ne pas oublier de créer le répertoire data
Code Raspberry : fichier batterie.py
#!/usr/bin/env python import smbus import time import os import datetime bus = smbus.SMBus(1) # I2C address of Arduino Slave i2c_add_batterie = 0x14 i2c_cmd = 0x01 def ConvertStringToBytes(src): converted = [] for b in src: converted.append(ord(b)) return converted data = "" bytesToSend = ConvertStringToBytes("m") bus.write_i2c_block_data(i2c_add_batterie, i2c_cmd, bytesToSend) time.sleep(0.1) for i in range(0, 5): data += chr(bus.read_byte(i2c_add_batterie)); print data time.sleep(1); fichier = open("/home/pi/data/batterie.txt", "w") fichier.write("\n" + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + " " + data) fichier.close()
Avec la commande « crontab –e » dans le terminal SSH, nous avons accès à la liste des Job CRON.
Il faut mettre cette ligne à la fin du fichier
*/1 * * * * sudo python /home/pi/batterie.py
Lorsque le fichier batterie.txt ne sera plus rempli, nous pourrons en déduire la tension minimale à la batterie pour que le système fonctionne.
Histoire de compléter le système vous pouvez créer un serveur Samba pour accéder facilement au fichier batterie.txt
En annexe 5 le fichier batterie.txt avec comme unique consommation électrique le PI et les Atmegas. les moteurs étant débranchés.
Annexes
base_composants.pdf 121,37 Ko
790 téléchargement(s)
base_stripboard.pdf 123,46 Ko
983 téléchargement(s)
shield_composant.pdf 118,01 Ko
794 téléchargement(s)
shield_stripboard.pdf 117,03 Ko
851 téléchargement(s)
batterie.txt 4,19 Ko
771 téléchargement(s)