Arduino Forum

International => Français => Réalisations et Projets Finis => Topic started by: iutgeiisoissons on Aug 19, 2019, 07:34 pm

Title: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Aug 19, 2019, 07:34 pm
La gestion d'une batterie (BMS : Batterie Management System) numérique permet d'estimer l'état de charge et l'état de santé de la batterie assez facilement pour certaine technologie de batterie lithium. L'objectif final est de connaitre la distance parcourable restante d'un véhicule électrique malgré le vieillissement de la batterie ? Ainsi que de faire un diagnostic de la batterie en temps réel.
Mais de nombreuses questions sont en suspend pour faire l'instrumentation d'une batterie en temps réels : Comment faire le diagnostic d'une batterie lithium ? Quelles sont les ressources que doit avoir le processeur ? Quelles sont les méthodes pour connaitre l'état de charge et l'état de santé d'une batterie ou de chaque élément ? Quel doit le nombre d'échantillon de mesure pour les diagnostics précédents ?

L'article suivant est telechargeable sur le lien suivant
https://www.fichier-pdf.fr/2019/11/20/revue-3ei--battery-v6/
Une carte Méga ou DUE est utilisé  et explique les méthodes et le programme

Le fichier ISIS de la simulation pour la carte mega qui permet de tester le programme est téléchargeable sur le lien suivant
https://drive.google.com/open?id=1aOexJj5RCrA8S7_1kDYjw_IHRh9jP7xk


(https://i89.servimg.com/u/f89/17/56/35/17/revue_25.png) (https://servimg.com/view/17563517/7393)
(https://i89.servimg.com/u/f89/17/56/35/17/revue_26.png) (https://servimg.com/view/17563517/7394)
(https://i89.servimg.com/u/f89/17/56/35/17/revue_27.png) (https://servimg.com/view/17563517/7395)
(https://i89.servimg.com/u/f89/17/56/35/17/revue_28.png) (https://servimg.com/view/17563517/7396)
(https://i89.servimg.com/u/f89/17/56/35/17/revue_21.png) (https://servimg.com/view/17563517/7389)
(https://i89.servimg.com/u/f89/17/56/35/17/revue_22.png) (https://servimg.com/view/17563517/7390)
(https://i89.servimg.com/u/f89/17/56/35/17/revue_23.png) (https://servimg.com/view/17563517/7391)
(https://i89.servimg.com/u/f89/17/56/35/17/revue_24.png) (https://servimg.com/view/17563517/7392)


L'algo est le suivant ;
Code: [Select]


Routine d'interruption timer1 tous les 0.1secondes

Mesure courant
Mesure coulomb mètre et Wattheure
Mesure tension chaque élément

Gestion de l'arrêt de la charge
Gestion de l'arrêt de la décharge avec un flag arrêt

Si 1 A.heure consommé alors calcul par régression linéaire de la pente de la diminution de la tension en fonction de la capacité énergétique pour chaque élément

Calcul de l'état de santé de chaque élément et donne l'état de santé de l'élément le plus faible

Si menu 0, test des résistances internes de chaque élément en décharge

Affichage des données sur l'afficheur LCD
Communication des données par la liaison série

Programme principal
Gestions des 4 boutons poussoirs
- BP1 test résistance en décharge
- BP2 lance la possibilité de décharge
- BP3 ré-autorise la décharge en réinitialisant le drapeau de l'arrêt
- BP4 gère les menus de l'afficheur LCD





Le programme Arduino est en PJ


Pour transférer les données du terminal du microcontrôleur dans Excel, il faut faire un copier-coller des données du terminal de Arduino. Coller dans un fichier doc
Faire un Ctrl+h   changer tous les points des chiffres en virgule pour qu'Excel
Refaire un Ctrl+h   changer tous les tabulation ^t   par un espace.
Enregistrer votre fichier .txt….puis renommer l'extention .txt en .csv
Ouvrir le fichier .csv dans Excel

Si vous utiliser un autre terminal que celui d'arduino, il est possible de copier directement les données en fichier .CSV


Remarques
Attention ouvrir le moniteur série Arduino avant de faire les essais sinon, il y a un reset du programme.
Attention appuyer sur BP5 pendant le off de la charge à cause du calibrage du capteur.




Conclusions et perspectives


Il suffit d'augmenter les variables pour avoir un BMS 13elements séries et la gestion des menus
Un compteur de vitesse (odomètre) à effet hall devrait être rajouté au programme pour mesurer la vitesse et la distance
Il faudrait mémoriser les données de l'état de santé pour chaque cycle
D'autres méthodes d'état de santé (SOH : state of health) de la batterie devrait être testé à partir de cette base de programmation en fonction de la chimie de la batterie
Un enregistrement des données dans une carte SD et une communication extérieur pour faire de la maintenance

on a fait the translate en english here
BMS , State of charge and estimate health battery lithium with Arduino
https://forum.arduino.cc/index.php?topic=651356.0
Title: Re: BMS, Etat de charge et de santé de batterie lithium NCM avec arduino
Post by: iutgeiisoissons on Nov 13, 2019, 09:15 am
Dans le BMS précédent, on voudrait ajouter un compteur de vitesse.

Sur le sujet suivant, le compteur de vélo, utilise des afficheurs 7 segments et un delays en scrutation pour compter le temps et définir la vitesse….et un ILS comme capteur.
« Compteur de vitesse avec mesure de puissance et de résistance au roulement »
https://forum.arduino.cc/index.php?topic=288800.0
Donc un afficheur LCD avec une routine d'interruption extérieure serait préférable avec un comptage par timer….

Dans le sujet precedent, Pour la résistance au roulement des pneus, il y a d'autres façons de la déterminer
http://velorizontal.1fr1.net/t18840-pneu-velo-adherence-et-coefficient-de-roulement
De plus, pour l'estimation de la puissance de pedalage, il faut prendre en compte le CX et la pente de la route ce n'est pas si simple.

Bref, il y a d'autres sujet, celui-ci affiche sur un smartphone avec le Bluetooth autant prendre le GPS du smarthphone pour connaitre sa vitesse, mais il a prix une cate arduino pro qui consomme peu.
https://create.arduino.cc/projecthub/alan_dewindt/bicycle-odometer-and-speedometer-with-99-lap-period-recorder-331d2b?ref=tag&ref_id=speedometer&offset=0
un autre sujet, bicycle-speedometer  qui détermine la vitesse pour chaque tour de roue avec la fonction millis() qui utilise le timer0
https://create.arduino.cc/projecthub/139683/arduino-lcd-display-as-a-bicycle-speedometer-6a6568?ref=tag&ref_id=speedometer&offset=3
un autre sujet qui détermine la vitesse toutes les secondes
Commande et instrumentation de trottinette électrique 500W avec Arduino méga
https://forum.arduino.cc/index.php?topic=473015.0

mais ils ont pris quoi comme capteur et comme aimant ????????
Il esiste de nombreux capteur a effet hall
- UGN35 10mV/Tesla
- KY003  tout ou rien, 0.7€ utilisant A3144  avec 10mT pour turn on, 1 mT turn off avec une hysteresis de 5.5mT
http://www.farnell.com/datasheets/11437.pdf
- KY035 lineaire mais avec une sensibilité faible, qui doit etre amplifié en fonction de la grosseur de l'aimant
- KY024 avec amplification,  6€
- 49E lineaire amplifié   3€,  (2,5V pour 0 tesla, 4V pour 100mT) donc un gain de 15V/tesla
- un capteur magnétique 3 axes tel que HMC5883 (GY-271) n'est pas très utile pour nous
https://www.instructables.com/id/Tutorial-to-Interface-HMC-5833L-With-Arduino-Uno/



Cela dépend de la forme de l'aimant, mais proche à l'aimant, le champ magnétique diminue  hyperboliquement en fonction de la distance. Donc, pour un aimant fournisant 70mT ou 700 Gaus, la distance est détection maximale est d'environ de  5mm
https://wiki.mchobby.be/index.php?title=Senseur_%C3%A0_Effet_Hall

remarque : Un aimant disque de  5mm*4mm en néodyme N35 fournit un champ de  900mT
http://www.farnell.com/datasheets/91199.pdf?_ga=2.85996127.699896235.1566289964-642331634.1566289964

Pour une roue et un pneu de 26x1.5 pouces, de circonférence de 2010mm. Il faut une précision en mm pour la circonférence.
La distance est donné par
Distancekm=nbrdetour*2010/1000000

Pour la vitesse, la précision au dixième près est un affichage à la seconde est seulement necessaire, 2 methodes sont possibles

• Méthode par détermination pour chaque tour de roue avec un comptage à la milliseconde près.
La vitesse est déterminée par l'équation suivante
V=(2010*3.6)/temps(ms)    pour 100ms=>72km/h
S'il y a une interruption extérieure toutes les secondes, la vitesse sera de 7.2km/h
Donc, il faut limiter la mesure à des temps supérieurs à 2 secondes donc à des vitesses supérieurs à 3.6km/h.
La précision de la vitesse est plus faible lorsque la vitesse est grande mais tres honorable
DeltaV=(2010*3.6)/tempsmini2       pour 100ms=>0.72km/h
A grande vitesse, l'affichage va changer à chaque tour, ce qui ne la rend pas lisible. Donc, un filtrage moyenneur numérique sur 5 valeurs est intéressant.

• précision avec la méthode de détermination pour 2 secondes
La vitesse est déterminée par l'équation suivante
V=(nbrdetour*2010*3.6)/temps constant       pour 2s=3.6(km/h)/tour
En effet, s'il y a plus ou moins 1 tour toutes les 2 secondes, la précision de la vitesse sera constante à 3.6km/h et va dépendre de la circonférence.
Un filtre passe bas numérique doit être utilisé pour moyenner cette erreur.

Mais à cause de la routine d'interruption timer tous les 0.1s ce n'est pas indiqué d'utiliser cette méthode

On va faire quelques tests, sur le capteur 3144 et 49E
Title: Re: BMS, Etat de charge et de santé de batterie lithium NCM avec arduino
Post by: roro_manche on Nov 14, 2019, 09:29 pm
Sujet sur le BMS super intéressant, mais pouvez vous développer ???

schéma arduino / cellules

programme / affichage sur lcd

les données stockées => comment les analyser ? les remèdes en cas de défaut ????

merci
Title: Re: BMS, Etat de charge et de santé de batterie lithium NCM avec arduino
Post by: MarsaMatruh on Nov 15, 2019, 09:57 am
C'est ambitieux comme projet. Toutes mes félicitations.  ;)

Même les fabricants de voiture électrique ont du mal à afficher en temps réel l'autonomie restante de leurs véhicules.

Sur la questions des contrôles/équilibrages individuels de cellule, il y a une starup qui s'est lancée dedans: enerstone (https://enerstone.fr/fr/).
Title: Re: BMS, Etat de charge et de santé de batterie lithium NCM avec arduino
Post by: iutgeiisoissons on Nov 21, 2019, 03:51 pm
Pas compris, comment enerstone fait de l'équilibrage actif ? Pas trouvé quelle était le courant d'équilibrage des cartes ?

L'article précèdent donne l'état de santé de la batterie et la consommation mais avec les valeurs moyennes précèdent qui peuvent provoquer des erreurs sur l'estimation futur

Pour connaitre la consommation en fonction de son parcours.
pour ma part, j'utilise l'application suivante qui en fonction du parcours m'indique ma consommation electrique a 5% d'erreur parfois majorée, parfois minorée
https://www.ebikemaps.com/

Pour avoir plus d'information sur les BMS et leurs rôles, il y a un lien suivant.
http://velorizontal.1fr1.net/t20802-bms-et-depannage-ou-maintenance-de-pack-de-batterie

Un lien pour télécharger l'article precedent  a été rajouté, le programme est en pièce jointe mais pour 2 cellules seulement pour savoir comment cela a programmé.
https://www.fichier-pdf.fr/2019/11/20/revue-3ei--battery-v6/
Title: Re: BANC de test de cyclage de batterie lithium avec arduino
Post by: iutgeiisoissons on Jan 02, 2020, 06:14 pm
L'article précèdent disait que l'on pouvait faire un banc de test de cyclage de batterie lithium pour connaitre les performances de vieillissement de batterie car les constructeurs sont flous à ce sujet.
Voici un programme qui permet de faire de cyclage de batterie LTO 18650 qui a une tension max de 2.7V, une tension mini de 1.5V avec une capacité énergétique de 1.45A

Difficile de trouver sur le net les caracteristiques des batteries LTO
Mais Voici celle des  GTKPower  TK18650 NT35 https://www.academia.edu/32374502/LTO_18650_datasheet_specs
Il y a des tests sur ce lien mais rien sur la durée de vie.
http://velorizontal.1fr1.net/t20802p50-bms-et-depannage-ou-maintenance-de-pack-de-batterie


La durée de vie à 80% du SOC nominal pour les LTO peut atteindre plus de 4000 cycles
Mais, en 24 heures avec le taux de décharge à 4C, il y aura 48 cycles de décharges, un diagnostic complet tous les 500 cycles sera effectuée avec le bouton poussoir BP1.
En 3 mois, les 4000 cycles seront effectués.

2 relais en séries sont utilisés qui permettent de charger la batterie ou de la décharger mais aussi d'arrêter le cyclage à 4C pour mesurer la résistance interne de l'élément.
Un capteur de courant ACS 712 20A est utilisé qui mesure un courant un courant positif et négatif.

Sachant que la tension du chargeur ne peut dépasser 2.7V pour charger l'element LTO, le programme attend que le courant soit inférieur à 0.5A pour arrêter la charge. Dans cette condition, la batterie est presque à 100% de la charge.

Lorsque la batterie LTO a atteint 1.5V, malgré le courant de décharge à 4C, on considéra que l'on est à 0% de décharge et l'on enregistra la valeur du SOC, ainsi que la résistance interne. Le nombre de cycle sera incrémenté.

Evidement avec le nombre de cycle, l'état de charge va diminuer ce qui correspondra à l'état de santé de la batterie.

Les données sont enregistrées dans l'EEPROM en cas de coupure d'electricité les données sont bien sauvegardé. Le petit programme permet d'initialiser correctement l'adresse ou sera enregistrées le cycle.
(https://i89.servimg.com/u/f89/17/56/35/17/a619.jpg) (https://servimg.com/view/17563517/7556)

le programme d'initialisation de l'eeprom en PJ
Code: [Select]

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <avr/wdt.h>   //chien de garde
#include <EEPROM.h>

#define BP1     30       // 30 BP1
#define BP2     31       // 31 BP2          
#define BP3     32       // 32 BP3
#define BP4     26       // 32 BP3
#define LEDV    33       // 33 led
#define LEDJ    34       // 34 led
#define LEDR    35       // 35 led
#define relays0 21          
#define relays1 20      
#define led13   13  
#define buzzer  25
#define BP5     12      

LiquidCrystal lcd(27, 28, 25, 24, 23, 22); // RS=27, Enable=28, D4=25, D5=24, D6= 23, D7=22, BPpoussoir=30
// Configuration des variables

unsigned int cycle=0;
float SOC1=-1450;


void setup() {
   lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
  Serial.begin(9600);
EEPROM.put(4000, cycle);    //le cycle est l'adresse de l'enregistrement à 4000 octets initialisé à 0
EEPROM.put(cycle, 1400);  //derneire mesure du SOC
}

void loop() {
lcd.setCursor(0,0);
lcd.print("init EEprom  ");

EEPROM.get(4000, cycle);    //verification des données
EEPROM.get(cycle, SOC1);  

lcd.setCursor(13,1);
lcd.print(cycle);
lcd.print("Cy  ");

lcd.setCursor(0,2);
lcd.print(SOC1,0);
lcd.print("mAh  ");

// faut-il reinitialiser toute l'eeprom pour chaque nouvelle batterie

}



L'algo simplifié du banc de test est le suivant avec programme en PJ

Code: [Select]

routine d'interruption toutes les 1s,
mesure et affichage du le courant, la tension, SOC, temps, cycle sont mesurés…
enregistrement eeprom après chaque décharge du SOC

arret de decharge à 1.5V
arret de la charge pour un courant de 0.5A et tension superieur à  2.65V


programme principal gestion des bouton
avec le bouton BP1, diagnostic totale de l'élément, avec tous les 30s, envoie au PC, tension, courant, SOC, résistance interne, la température….qui permet de faire un fichier CSV pour étude
avec le bouton BP2, on peut choisir de charger ou de décharger.
avec le bouton BP3, on, off
avec le bouton BP4, mettre en automatique le cyclage
BP5 lecture du nombre de cycle et du SOC effectué

Si la température est supérieure à 45°C alors off et attendre une remise en fonctionnement, activation du buzzer pour interpeller un problème



Pour vérifier notre programme, des captures d'écran suivant prouveront le bon fonctionnement de la programmation en simulations dans un premier temps puis une photo du banc de test de batterie

On peut observer que cela charge bien avec la bonne valeur de courant.
Etant donné que le capteur ACS 712 n'est pas simulable, nous avons utilisé une résistance ajustable. De même, pour faire varier la tension de la batterie.
Un simple Arduino nano suffit pour faire de nombreux bancs de test.
(https://i89.servimg.com/u/f89/17/56/35/17/a010.jpg) (https://servimg.com/view/17563517/7573)

Mais une carte Mega permet de gérer de nombreuses batteries en cyclage et le LCD était câblé.
De plus, la carte ATmega 2560 permet d'avoir un chien de garde.
BP3 actionné et BP2, il y a bien une charge, avec les bonnes valeurs de mesures, du temps
Voici le lien pour telecharger le fichier ISIS de ce banc de test
https://drive.google.com/open?id=1lKJQt0jddDAokAbMtMt_XU2v5ojnlbo6

(https://i89.servimg.com/u/f89/17/56/35/17/a1106.jpg) (https://servimg.com/view/17563517/7534)
Puis, il arrete la charge.
En décharge, il y a bien un courant negatif, et la capacité energetique et negatif
(https://i89.servimg.com/u/f89/17/56/35/17/a295.jpg) (https://servimg.com/view/17563517/7558)
Il y a bien un arrêt pour une tension inférieur à 1.5V
(https://i89.servimg.com/u/f89/17/56/35/17/a370.jpg) (https://servimg.com/view/17563517/7557)
En mode diagnostic, les mesures sont bien envoyées sur le terminal PC toutes les 30 secondes
(https://i89.servimg.com/u/f89/17/56/35/17/a438.jpg) (https://servimg.com/view/17563517/7559)
Lors de l'appuie sur BP5, les données des SOC sont bien récupérées
(https://i89.servimg.com/u/f89/17/56/35/17/a517.jpg) (https://servimg.com/view/17563517/7560)
En mode, auto, il y bien charge et décharge qui s'enchaine
En mode diagnostique, il y a bien un envoie des données tous les 30 secondes dont la valeur de la résitance

(https://i89.servimg.com/u/f89/17/56/35/17/a011.jpg) (https://servimg.com/view/17563517/7574)

Le cablage fait que cela ne est pas tres propre….mais le prototype tourne depuis 15 jours et  a permis de voir les petits problemes de programmation
(https://i89.servimg.com/u/f89/17/56/35/17/p1040115.jpg) (https://servimg.com/view/17563517/7576)

Remarques : avec les relais, il faut ne pas utiliser l'alimentation USB car lorsque les relais commutent un arret programme peut survenir.
Il faudrait mettre plus de données en eeprom s'il y a un arret programme et utiliser le chien de gardes.

Perspectives : l'ACS712 derive legerement lors de la mesure de courant. Un blindage est souhaitable et une remise à zero de temps en temps comme en mode diagnostic est pertinent. Il faudrait le faire aussi en mode charge et decharge.
l'estimation du SOC par regression lineaire devrait etre mis aussi dans ce banc de test

Conclusion :
il est très facile de faire un banc de test de cyclage pas cher avec des Arduino…..
De les multiplier, pour avoir des statistiques sur l'etat de santé de la batterie….

Des hacheurs abaisseurs et élévateurs permettrait de décharger une batterie pour en recharger une autre mais ceux-ci sera d'autres histoires.


Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jan 29, 2020, 03:52 pm
On a voulu refaire  un autre banc de test pour faire un cyclage à 6C
Voici un nouveau programme pour le cyclage de batterie des batteries LTO que l'on a amélioré
De plus, à cause des résistances parasites des relais et du câblage qui est de 0.028ohms+la résistance interne de la batterie qui est d'environ 0.04ohms, la valeur d'arrêt de décharge a été mis à 1.2V.
A cause de ces résistances parasites, il impossible de charger à plus de 4C l'élément en limitant la tension à 2.7V du chargeur.
Une solution est de mettre un troisième relais avec un chargeur à 3V, puis basculer sur le chargeur à 2.7V

Pour améliorer, la simulation le schéma ISIS a été changée à cause du capteur de courant ACS712, qui permet d'observer le calibrage de ce capteur,
https://drive.google.com/file/d/1gbkH9TQFWQYkuNvR86iMZmI24tou7c24/view?usp=sharing
(https://i89.servimg.com/u/f89/17/56/35/17/a1107.jpg) (https://servimg.com/view/17563517/7613)

L'afficheur LCD bug de temps en temps, mais en appuyant sur BP5, une réinitialisation de l'afficher LCD se fait.
Il faudrait vérifier les temps de programme de la routine d'interruption pour savoir pourquoi cela bug de temps en temps.

Perspectives est de multiplier le nombre de banc de test pour le cyclage avec des Arduino nano pour avoir une étude statistiques de défaillance d'elements
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jan 30, 2020, 12:42 pm
En fait, l'ACS712 est simulable dans ISIS, ce qui simplifie le schema precedent, il n'y a donc pas besoin de l'AOP
Pour faire de nouveau banc d'essais de cyclage d'element de battery, des afficheurs LCD avec bus I2C vont etre utilisés.
Il est aussi possible de simuler cet afficheur, pour ameliorer le programme et ne pas faire de betise sur la batterie.

On peut observer le schema simulation sous ISIS
(https://i89.servimg.com/u/f89/17/56/35/17/a1108.jpg) (https://servimg.com/view/17563517/7614)
On peut telecharger le fichier ISIS sur ce lien
https://drive.google.com/open?id=1M6x9hzKuSftT7Db3yvWfPXbY0gVtzYqb (https://drive.google.com/open?id=1M6x9hzKuSftT7Db3yvWfPXbY0gVtzYqb)

Voici le programme avec la library du LCD I2C avec le choix de l'adresse du LCD en fonction de A0, A1, A2
Code: [Select]

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
//https://github.com/johnrickman/LiquidCrystal_I2C/blob/master/examples/HelloWorld/HelloWorld.pde
//https://www.gotronic.fr/pj2-sbc-lcd16x2-fr-1441.pdf
//https://wiki.mchobby.be/index.php?title=LCD-I2C
//https://microcontrolere.wordpress.com/2019/04/01/arduino-and-i2c-lcd-in-proteus/
// indiquer (adresse i2c, nombre de colonnes, nombre de lignes)
// il existe des petits programmes pour verifier l'adresse
//LiquidCrystal_I2C lcd(0x27, 20, 4);    //en reel
LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter

#define led13   13  

void setup()
{  // initialise l'afficheur LCD
pinMode(led13, OUTPUT);
//pinMode(A4, OUTPUT);
//pinMode(A5, OUTPUT);


lcd.init();  //et pas lcd begin comme cetraine biblio
// activer l'affichage
  lcd.display();
// allumer retroeclairage
  lcd.backlight();
}

void loop()
{
 // ecrire à la position par defaut
  lcd.setCursor(0,0);
  lcd.print("IUT GENIE elect&info");
  lcd.setCursor(5,1);
  lcd.print("Soissons");
digitalWrite(led13,LOW);   //cligotement led 13
  // petite pause
  delay(2000);
  lcd.clear();
  digitalWrite(led13,HIGH);
  delay(1000);
}




(https://i89.servimg.com/u/f89/17/56/35/17/img_2106.jpg) (https://servimg.com/view/17563517/7615)


le code pour les caracteres speciaux "symbol" sur l'afficheur LCD
lcd.print((char)223);    //le degres de celcius
lcd.print((char)227);   //epsilon
lcd.print((char)228);    //micro
lcd.print((char)244);    //ohm
il n'y a qu'a faire un petit programme qui incrmente le numero des caracteres
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Feb 27, 2020, 01:59 pm
A force de mettre des choses dans la routine d'interruption, celle-ci a durée plus de 1ms.
De plus, pour ne plus avoir de bug de l'afficher LCD, entre des affichages que se faisait dans de la boucle principal et l'affichage dans la routine d'interruption.
Toutes les instructions du LCD se fait dans la routine d'interruption.
D'ailleurs à cause de la gestion de l'afficheur LCD, le temps de la durée de la routine est maintenant d'environ 17ms.

Par conséquent, le temps de la routine d'interruption est passé dans ce programme à 0.1secondes.
Mais, il a fallu changer de nombreuses valeurs qui dépendaient de ce temps d'interruption

de plusn Un temps  a été rajouté entre la décharge et la charge pour que la batterie ait le temps de redescendre légèrement en température. dans le programme, il est de 60s avec flagway


Le programme en piece jointe

Pour avoir un temps encore plus court de décharge, on a changé de résistance de décharge de 0.2Ω mais, avec l'élévation de température de la résistance de décharge, celle-ci est plutôt à 0.25 Ω.
(https://i89.servimg.com/u/f89/17/56/35/17/p1040223.jpg) (https://servimg.com/view/17563517/7713)

Sur la figure suivante, on peut observer la tension OCV d'un element LTO et température de la batterie en fonction du temps.
Le temps de la décharge est de 14 minutes et le temps de charge  est de 50.5minutes.
Donc un cycle de charge et de décharge est de 64.5minutes.
Pourtant Lors de la charge, il faut seulement 15minutes pour reprendre 1A.h, mais la charge à tension constante est relativement long mais permet à la batterie

(https://i89.servimg.com/u/f89/17/56/35/17/c712.jpg) (https://servimg.com/view/17563517/7723)
L'estimateur par régression linéaire donne une très bonne estimation comme on peut l'observer sur la figure suivante pour une tension 0CV mini de 2,1V.
(https://i89.servimg.com/u/f89/17/56/35/17/c811.jpg) (https://servimg.com/view/17563517/7724)

Sur la figure précédente, une erreur au début du SOC peut être observée à cause de la variation importante de la tension en début de décharge de 2.7V à 2.5V, puis l'erreur se minimise pour redevenir importante à la fin de la décharge.
Evidemment, une régression polynomiale pourrait minimiser ces erreurs.

La figure suivante donne la capacité energetique en fonction du cyclage. La variation de la capacité est du aux petites erreurs du calibrage du capteur de courant qui est de ±0.05A. D'ailleurs, à chaque cycle, le capteur est recalibrer pour minimiser l'erreur. D'ailleurs, c'est pour cela qu'il y a une plus grande d'erreur au début du cyclage.
(https://i89.servimg.com/u/f89/17/56/35/17/c910.jpg) (https://servimg.com/view/17563517/7725)

Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Mar 14, 2020, 07:50 am
suite à la discution du 13 nov sur les capteurs magnetiques, pour realiser un compteur de velo

On a voulu testé des aimants pour notre compteur de vélo et réalisé un tesla mètre avec un capteur SS49E pour savoir à quel distance l'aimant sera détecté.

Sur l'excellent article suivant, il dise que le champ magnétique et sur le devant du capteur alors qu'il est plus performant sur le dessus lors de nos essais ?

« Construction of a simple low-cost teslameter and its use with Arduino and MakerPlot software »
https://iopscience.iop.org/article/10.1088/0031-9120/51/2/024001/pdf

De plus, il faut aussi calibrer le capteur ce que ne fait pas le programme ci-dessus.
Sachant que Le champ magnétique terrestre varie de 30µT à 60µT, cela est négligeable par rapport aux champs d'un aimant. En France, il est  de 47µTesla , ou encore de 470 mGaus
Pour faire le calibrage, une bobine de diametre 1cm et 4cm de long alimentée en DC aurait pu être réalisé mais cela n'a pas été fait.

Voici le programme
Code: [Select]

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <TimerOne.h>
#include <SoftwareSerial.h>
//#include <math.h>

#define led13   13

LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter 20x4ligne

      signed int entree;
      signed int tesla=0;
      signed int zeromagnet;
      unsigned int TO;
      unsigned int Time;
      unsigned int kph;

void setup()
{  // initialise l'afficheur LCD
pinMode(led13, OUTPUT);
pinMode(3, OUTPUT);

  Timer1.initialize(1000000);         // initialize timer1, and set a 1 second period =>  1000 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  Serial.begin(9600);


lcd.init();               //et pas lcd begin comme cetraine biblio
lcd.display();            // activer l'affichage
lcd.backlight();          // allumer retroeclairage

zeromagnet=analogRead(A0);
attachInterrupt(0, interrup2, FALLING);   // il vaut mieux utiliser  KY003 tout ou rien   RISING, FALLING
TO = millis();
}//fin setup


void interrup2() // la fonction appelée par l'interruption externe n°0
{
Time=(millis()-TO);  //mesure du temps
TO = millis();
}




// Interruptions  tous les 1s fait par le timer1***********************************
void callback()  {
if (digitalRead(led13)== 1) {digitalWrite(led13,LOW);}  else {digitalWrite(led13,HIGH);}
}//callback



void loop()    
{
//digitalWrite(led13,HIGH);
entree=analogRead(A0);          //test du capteur 49E  teslametre, il faut passer l'aimant au dessus du capteur
tesla=entree-zeromagnet;
lcd.setCursor(0,1);  // (X,Y)  
//tesla=map(entree,0,1023, -167, 167);
tesla=(tesla*100)/307;    
lcd.print(tesla);
//lcd.print((char)228);    //micro
lcd.print("mTesla   ");  
//digitalWrite(led13,LOW);  

//pour faire une detection, du pole nord,
if (tesla<-10) {lcd.setCursor(0,0); lcd.print("aimant");} else  {lcd.setCursor(0,0); lcd.print("       ");}

//odometre ou compteur de velo en kilometre par heure
if (Time>=2000) {kph=0;   } else {kph=(2.010*1000*3.6)/Time;   }   //2010mm perimetre roue 26 pouces
  lcd.setCursor(0,3);  // (X,Y)
  lcd.print(kph,1);
  lcd.print("kph    ");  

    
}//fin loop




Le capteur magnétique n'est pas simulable dans Isis. Mais il est possible de le remplacer par un potentiomètre.
Donc, un essai avec l'aimant à 1.5cm du capteur donne -3mT en reel avec un de nos aimants.
(https://i80.servimg.com/u/f80/20/13/22/26/img_2021.jpg) (https://servimg.com/view/20132226/49)

Un autre essai avec l'aimant à 0.5cm, provoque une augmentation de la valeur à -21mT
(https://i80.servimg.com/u/f80/20/13/22/26/img_2022.jpg) (https://servimg.com/view/20132226/50)

Evidemment, si on retourne l'aimant, la valeur du champ magnétique change de signe
Etant donné que sur la plupart les téléphones Android, il y a un capteur magnétique 3 axes, il est aussi possible de vérifier les valeurs donnés par le capteur SSE49 donnée par l'aimant.

On peut observer en simulation qu'à la fréquence de 5Hz pour pneu de 26 pouces, la vitesse est bien de 34kph.
L'interruption n'est pas détecté, ni en falling, ni en rising avec le SS49E
Donc, seul un capteur tout ou rien KY03 ou 3144 pour faire un odometre et un compteur de velo est viable.
(https://i80.servimg.com/u/f80/20/13/22/26/img_2023.jpg) (https://servimg.com/view/20132226/51)
D'ailleurs avec l'aimant précedent, il faut 2 cm pour que le 3144 passe à 3.8V à 0.1V.
l'hysteresis fait que le capteur reste à l'etat "0" jusqu'a 3cm.
Attention, le 3144 n'est actionnable que pour un seul sens de champs magnetique, donc il faut choisir la bonne face de l'aimant. Sinon, il est possible de retourner le capteur
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: kobarose on Mar 27, 2020, 02:05 pm
Les interrupteurs d'éclairage vélo ont des interrupteurs pour les activés ou désactivés. L'interrupteur doit être le plus léger possible mais ne peut supporter un nombre de manœuvre faible ce qui donne une obsolescence du produit de quelques années.
Les éclairages de vélos commandés par microcontrôleur peuvent être réveiller (après mise en veille) par 2 technologie différente :
-   Interrupteur capacitif tactile
-   Interrupteur magnétique

L'interrupteur capacitif doit avoir une amplification qui consomme un courant de 40mA, bien trop important pour un système alimenté par batterie.

Un interrupteur à aimant est donc préférable
Mais quel type d'aimant faut-il utilisé ? quel sera son prix, sa masse, ces dimensions ?
A cause du boitier, quelle sera la distance minimale pour activer le capteur magnétique dans le boitier ? quel sont les types de capteurs magnétiques ? quel est le courant consommé par le capteur ?

1.Mesure avec smartphone du champs magnétique en fonction de la  distance

(https://i36.servimg.com/u/f36/20/18/53/15/courbe10.png) (https://servimg.com/view/20185315/1)
(https://i36.servimg.com/u/f36/20/18/53/15/courbe11.png) (https://servimg.com/view/20185315/2)
Etant donné que les mesures du smartphone sont peut-être erronées à cause des parties ferreuse, nous allons réaliser notre propre teslamètre avec le capteur SS49E.

12   Teslamètre avec capteur SS49E

Le capteur à effet Hall linéaire 49E est un petit dispositif à effet Hall linéaire sensible au le champ magnétique. Sa consommation est de 4.5mA pour 5V
La variation linéaire de ce capteur est de  2.5V  a 0T et pour 4V on a 100mT.
pour le mesure du champ magnétique en fonction de plusieurs magnets ,nous avons cette courbe suivante
(https://i36.servimg.com/u/f36/20/18/53/15/courbe12.png) (https://servimg.com/view/20185315/3)
Pour trois magnet on a cette courbe

(https://i36.servimg.com/u/f36/20/18/53/15/courbe13.png) (https://servimg.com/view/20185315/4)

pour une seule magnet on a celle-ci
(https://i36.servimg.com/u/f36/20/18/53/15/courbe14.png) (https://servimg.com/view/20185315/5)

D'après les courbes on peut dire que y'a pas de différences entre les mesures effectuées  avec le smartphone et le capteur SS49E car on voit bien que les courbes décroissent.

Programme du teslamètre avec Arduino


ça nous avons effectué un petit programme sous Arduino pour lire directement sur un afficheur LCD, la valeur du champs magnetique en fonction de la distance.
La fonction  map nous permettra de faire cette calcule comme suite
val=map(entree,0,1023,-167,167);

Dans ce cas, notre variable "val" contiendra donc le résultat de la fonction map() de la valeur de l'entrée, initialement comprise entre 0 et 1023 mais actuellement rééchantillonnée entre -167 et 167.



unsigned   int temps = 0;
unsigned   int entree = 0;
  signed   int val = 0;

         
void setup() {
  pinMode(Led, OUTPUT);   //led carte arduino
  pinMode(LEDV, OUTPUT);
  pinMode(LEDR, OUTPUT);
  pinMode(LEDJ, OUTPUT);

  Timer1.initialize(100000);         // initialize timer1, and set a 0,1 second period =>  100 000
  Timer1.attachInterrupt(callback);  // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4); 
//  Serial1.begin(9600);


Serial.begin(9600);
interrupts();
}


// Interruptions  tous les 0.1s
void callback()  {
temps++;
//toogle state ledv for check


//************************

 if (temps>=1  )  {        //
    if ( digitalRead(LEDV)== 1 ) {digitalWrite(LEDV,LOW);}  else {digitalWrite(LEDV,HIGH);}
  entree=analogRead(A0);       //


temps=0;
  } // fin temps

}//fin routine interruption


// Boucle correspondant à la fonction main
void loop() { 
 

  val=map(entree,0,1023,-167,167);
  lcd.setCursor(0,0);  // (X,Y)
  lcd.print(val);     //duree 10ms
  lcd.print("Tesla   ");
 
} // fin loop   



On a simuler notre programme et sur l'écran lcd nous avons bien pour 2.5V, la valeur de 0 Testla, et pour 4V, 100mT.
(https://i36.servimg.com/u/f36/20/18/53/15/isis10.png) (https://servimg.com/view/20185315/6)




Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: 22AS1999 on Apr 01, 2020, 11:41 pm
ETUDE DE BATTERIE NIMH
En moyenne il y aurait 106 piles et batteries présentes dans chaque foyer français et la plupart sont jetées alors qu'elles sont encore utilisables.
Pourquoi ??? la raison est toute simple les personnes lambda ont rarement accès à des outils de diagnostic de batterie. Une solution consiste à utiliser un Arduino avec le code libre accès et les schémas de câblage sur le forum sans oublier les tutos, tout le monde peut diagnostiquer sa batterie.

D'autres se sont tournés vers les batteries rechargeables espérant trouver une issue de secours mais se sont vite heurté à un problème. Mais lequel me direz-vous : les batteries rechargeables sont plus écologiques et sont réutilisables plusieurs fois sans oublier qu'elles ont une grande durée de vie. oui oui et oui mais la plupart des pub et des affiches sont mensongères et les fabricants vous disent rarement la vérité. La grande majorité des batteries n'atteignent même pas la durée affichée sur l'emballage. Saviez-vous par exemple que les batteries perdent 10% de leurs capacités dès qu'elles sortent d'usine ensuite 10 à 15% par mois. Cette étude-ci porte sur les batteries rechargeables Nimh (Nichel-Hydure métallique).
Voici quelques caractéristiques de batterie :
Tension nominale : 1.2v
Nombre de cycles : 500 à 1000
Rendement charge-décharge : 66%
Pour une charge optimale il faut le faire en I=0.1C  (capacité en A.h)

Pour la charge et la décharge il est plus judiciable d'utiliser des relays qui sont des interrupteurs commandés en courant donc directement commandé à partir de l'Arduino.
(https://i20.servimg.com/u/f20/20/18/88/59/image410.png) (https://servimg.com/view/20188859/1)

Voici le code :
Code: [Select]
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
//https://github.com/johnrickman/LiquidCrystal_I2C/blob/master/examples/HelloWorld/HelloWorld.pde
//https://www.gotronic.fr/pj2-sbc-lcd16x2-fr-1441.pdf
//https://wiki.mchobby.be/index.php?title=LCD-I2C
//https://microcontrolere.wordpress.com/2019/04/01/arduino-and-i2c-lcd-in-proteus/

LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter

#define led13   13
#define relays0 12         
#define relays1 11   
#define Charge    9     
#define Decharge  10       
           


    float tension,tension1=0;       // Initialisation
    float courant=0;
    float sensible=0.066;          // Sensibilité du capteur
    float offset=2.5;              // Valeur de tension lorsque le courant=0
    float zeroint=512;
    float var,var1=0; 

void setup()
{  // initialise l'afficheur LCD
  ////////// DEFINITION DES E/S //////////////////
pinMode(led13, OUTPUT);
//pinMode(A4, OUTPUT);
//pinMode(A5, OUTPUT);
pinMode(A0, INPUT); // On défini A0 en entrée
pinMode(relays1, OUTPUT);           // On défini relay1 en sortie
pinMode(relays0, OUTPUT);           // On défini relay0 en sortie
 
////////////////*********/////////////////////////

lcd.init();  //et pas lcd begin comme cetraine biblio
// activer l'affichage
  lcd.display();
// allumer retroeclairage
  lcd.backlight();
    lcd.clear();
}

void loop()
{
   digitalWrite(led13,LOW);   //cligotement led 13
   delay(100);
   digitalWrite(led13,HIGH);


  /////////////////*VERSION_2*///////////////////////
 tension=analogRead(A0);           // Lecture de la tension aux bornes de la batterie
 tension1=analogRead(A1);          // Lecture de la tension aux bornes du capteur de courant
 tension= (tension/204);           // Calcul de la tension batterie
 //tension1=(tension1*5.0)/1023;     // calcul de la tension capteur de courant
 courant= (tension1-zeroint)/20.4; // Calcul du courant

////////////////////////////////////////////
 

   //////////////FONCTIONNEL////////////////
 if (digitalRead(Charge)==1)  {digitalWrite(relays0,HIGH);}      // Charge on
 if (digitalRead(Decharge)==1)  {digitalWrite(relays1,HIGH);digitalWrite(relays0,HIGH);}   // décharge on 
 lcd.setCursor(0,0);                 
  lcd.print("Tension : ");         // Affichage de la tension batterie
  lcd.print(tension,2);
  lcd.setCursor(0,1);
  lcd.print("Courant : ");         // Affichage du courant
  lcd.print(courant,2);
  if (digitalRead(Charge)==0)  {digitalWrite(relays0,LOW);}      // Charge off
 if (digitalRead(Decharge)==0)  {digitalWrite(relays1,LOW);digitalWrite(relays0,LOW); }   // décharge off

 var=courant+var;            //etat de charge en mA.h
var1=var/36;          //division par 36 si tempsechatillon 100ms    //division par 3.6 pour 1s
lcd.setCursor(0,3);
lcd.print(var1,0);
lcd.print("mAh   ");

  ////////////////////////////////////////
 
    delay(100);
}

Voici quelques résultats de simulation :
(https://i20.servimg.com/u/f20/20/18/88/59/captur10.png) (https://servimg.com/view/20188859/2)

(https://i20.servimg.com/u/f20/20/18/88/59/captur11.png) (https://servimg.com/view/20188859/3)

(https://i20.servimg.com/u/f20/20/18/88/59/captur12.png) (https://servimg.com/view/20188859/4)
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Apr 02, 2020, 08:51 am
Belle introduction, mais quelle est la tension de charge ?

Expliquer pourquoi la valeur 36 pour la mesure de la capacité énergétique et le choix du delay de 0.1s
mais combien de temps dure votre programme ? car il va y avoir une erreur dans la capacité energetique

Ici, c'est les lithiums et pas les Ni-MH, mais ce n'est pas si grave

Ce serait bien de mettre le fichier ISIS.dsn    dans votre drive et mettre le lien pour qu'il soit téléchargeable

Vous allez détruire la batterie car :
Pour quelle tension ou courant, on arrête la charge ?
Pour quelle tension, on arrête la décharge ?
Il faudrait mettre à 0 la mesure de la capacité énergétique ma.h entre la charge et la décharge ?
La mesure de la résistance interne de la batterie de l'élément NIMH est un élément crucial pour savoir si la batterie est correcte ou pas.
Vous avez choisi un ACS712 de 20 ampère alors qu'avec un 5 ampère, la précision serait bien meilleur.
D'ailleurs, avec le 20A vous avez 0.05A de precison, don inutile de mettre 3 chiffres après la virgule

Ce n'est pas si mal mais vous êtes seulement à 35% d'un programme final et utile.
pourquoi une resistance R2 de 5ohms ????? il va y avoir des pertes lors de la charge ?



kobarose, elle confond, tesla et millitesla,  
pire ce quel croit des telsla, ce sont des volts??????     2.5V  correspond à 0Tesla sur le 3144

Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: g2gaston on Apr 03, 2020, 05:26 pm
L'idée initiale du projet était d'utiliser une carte Arduino Méga qui met à disposition un grand nombre d'entrée/sortie numérique et analogique. Il nous aurait été donc possible de tester la durée de vie de plusieurs éléments sur un même montage, malheureusement, on a pu observer que le montage pour un seul éléments été déjà très lourd en termes de câble. On s'est donc tourné vers une utilisation du même montage mais en utilisant un Arduino Nano qui possède bien moins d'entrer sortie ce qui entraine un cout bien moins élevé. L'idée est donc de reproduire le montage avec une multitude d'Arduino nano.
Pour limiter les entrée/sortie on avait à la base décider d'utiliser une liaison I2C pour relier l'écran et la carte Arduino Nano. Tout semblait bien marcher mais une fois qu'il fallait appliquer le programme au montage plus rien ne marchait. Il s'est avéré qu'une liaison I2C empêchait toute commande d'affichage dans l'interruption. 2 choix s'offrait alors à nous soit déplacer toutes les commandes d'affichage de l'interruption dans le programme principale ou tout simplement garder la liaison en parallèle. Nous sommes donc parties sur le choix numéros 2. Problème, il n'y avait pas assez de pin numérique, on a alors juste à utiliser des entrée/sortie analogique. Ces pins fonctionneront comme des Entrée/Sortie numérique.
C'est dans cette optique que j'ai modifier le câblage et l'es donc simuler, tout en veillant à modifier l'initialisation des PIN dans le programme.

(https://i61.servimg.com/u/f61/20/13/71/02/test_b10.png)
 
Une fois la simulation faite, on a pu commencer à mettre en place le projet dans le réel. Il m'a donc fallu réaliser le câblage de l'écran en parallèle.
 
(https://i61.servimg.com/u/f61/20/13/71/02/img_2024.jpg)

Malheureusement la carte des boutons que nous utilisons et sur laquelle on appose l'écran, ne possède pas les dimensions nécessaires, car c'est une carte fabriquer en série. Il me fallait donc faire 2 trous pour dimensionner la carte à l'afficheur. Même comme cela, il fallait que je relis deux pin à la masse et un pin au VCC.

(https://i61.servimg.com/u/f61/20/13/71/02/img_2025.jpg)
 
Après avoir réalisé la liaison entre l'Arduino et l'écran en parallèle j'ai joint un petit programme pour tester le fonctionnement.



[ Code]#include "LiquidCrystal.h" // on inclut la librairie
// initialise l'écran avec les bonnes broches
// ATTENTION, REMPLACER LES NOMBRES PAR VOS BRANCHEMENTS À VOUS !
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);
void setup() {
   lcd.begin(20, 4);
   lcd.print("Salut ca zeste ?");
}
void loop() {
}[ /Code]

(https://i61.servimg.com/u/f61/20/13/71/02/ecran_10.jpg)
 
Après avoir tester le bon fonctionnement de l'écran je me suis mis à réaliser le câblage du système notamment des 2 relaies.

(https://i61.servimg.com/u/f61/20/13/71/02/img_2026.jpg)
 
Malheureusement je n'ai pas pu finir du au confinement, il me reste donc à finir le câblage du système ainsi que la réalisation d'une deuxième liaison série pour relier les boutons de la carte avec l'Arduino nano. Une fois le tout réaliser il faudrait tester le programme et voire si l'intégralité de celui-ci fonctionne.
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: SALLMairame on Apr 03, 2020, 05:51 pm
 Etat de la charge et de la decharge de la batterie nimh.

Durée de vie
une durée de vie de 500 cycles de charge et de décharge et plus
Un modèle au Li-ion, par exemple, a une durée de vie de 300 à 500 cycles, soit environ deux ans de service dans un ordinateur portable. Bien utilisés, les

En fin de vie, l'impédance interne augmente, le chargeur doit donc être équipé de fonction de contrôle de la température et de la tension permettant d'éviter un échauffement anormal.


Le calcul de leur durée de charge exige de prendre en compte la capacité de la batterie, expri-mée en mAh et l'intensité du courant fourni par le chargeur, en mA. Il suffit ensuite de diviser la capacité par la valeur du courant puis de multiplier par 1,4 pour obtenir la durée requise pour recharger votre accumulateur.
Par exemple, pour un accu de 1600 milliampères par heure rechargée à l'aide d'un chargeur 600 mA, il faudra réaliser la formule suivante :
• 1600 / 600 x 1,4 = 3,73 heures = 3 h 45 minutes environ.
Certains joueurs font alors le choix de se procurer des chargeurs offrant une plus grande inten-sité, afin de réduire le temps de chargement. Cela peut s'avérer utile, mais il faut tout de même respecter certaines limites, sous peine d'endommager le produit et de réduire sa durée de vie.


https://i65.servimg.com/u/f65/20/18/97/94/a37010.jpg

https://i65.servimg.com/u/f65/20/18/97/94/a37010.jpg


#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <avr/wdt.h>   //chien de garde
#include <EEPROM.h>

#define BP1     30       // 30 BP1
#define BP2     31       // 31 BP2          
#define BP3     32       // 32 BP3
#define BP4     26       // 32 BP3
#define LEDV    33       // 33 led
#define LEDJ    34       // 34 led
#define LEDR    35       // 35 led
#define relays0 21          
#define relays1 20      
#define led13   13  
#define buzzer  25
#define BP5     12      

LiquidCrystal lcd(27, 28, 25, 24, 23, 22); // RS=27, Enable=28, D4=25, D5=24, D6= 23, D7=22, BPpoussoir=30
// Configuration des variables

unsigned int cycle=0;
float SOC1=-1450;


void setup() {
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
 Serial.begin(9600);
EEPROM.put(4000, cycle);    //le cycle est l'adresse de l'enregistrement à 4000 octets initialisé à 0
EEPROM.put(cycle, 1400);  //derneire mesure du SOC
}

void loop() {
lcd.setCursor(0,0);
lcd.print("init EEprom  ");

EEPROM.get(4000, cycle);    //verification des données
EEPROM.get(cycle, SOC1);  

lcd.setCursor(13,1);
lcd.print(cycle);
lcd.print("Cy  ");

lcd.setCursor(0,2);
lcd.print(SOC1,0);
lcd.print("mAh  ");

// faut-il reinitialiser toute l'eeprom pour chaque nouvelle batterie

}

(http://[url=https://servimg.com/view/20189794/2][img width=500 height=378]https://i65.servimg.com/u/f65/20/18/97/94/a37010.jpg)[/url][/img]
(https://i65.servimg.com/u/f65/20/18/97/94/a110610.jpg) (https://servimg.com/view/20189794/1)
(https://i65.servimg.com/u/f65/20/18/97/94/a37010.jpg) (https://servimg.com/view/20189794/2)
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: SALLMairame on Apr 03, 2020, 05:57 pm
Etat de la charge et de la decharge de la batterie nimh.

Durée de vie
une durée de vie de 500 cycles de charge et de décharge et plus
Un modèle au Li-ion, par exemple, a une durée de vie de 300 à 500 cycles, soit environ deux ans de service dans un ordinateur portable. Bien utilisés, les

En fin de vie, l'impédance interne augmente, le chargeur doit donc être équipé de fonction de contrôle de la température et de la tension permettant d'éviter un échauffement anormal.


Le calcul de leur durée de charge exige de prendre en compte la capacité de la batterie, expri-mée en mAh et l'intensité du courant fourni par le chargeur, en mA. Il suffit ensuite de diviser la capacité par la valeur du courant puis de multiplier par 1,4 pour obtenir la durée requise pour recharger votre accumulateur.
Par exemple, pour un accu de 1600 milliampères par heure rechargée à l'aide d'un chargeur 600 mA, il faudra réaliser la formule suivante :
•   1600 / 600 x 1,4 = 3,73 heures = 3 h 45 minutes environ.
Certains joueurs font alors le choix de se procurer des chargeurs offrant une plus grande inten-sité, afin de réduire le temps de chargement. Cela peut s'avérer utile, mais il faut tout de même respecter certaines limites, sous peine d'endommager le produit et de réduire sa durée de vie.


https://i65.servimg.com/u/f65/20/18/97/94/a37010.jpg

https://i65.servimg.com/u/f65/20/18/97/94/a37010.jpg


#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <avr/wdt.h>   //chien de garde
#include <EEPROM.h>

#define BP1     30       // 30 BP1
#define BP2     31       // 31 BP2         
#define BP3     32       // 32 BP3
#define BP4     26       // 32 BP3
#define LEDV    33       // 33 led
#define LEDJ    34       // 34 led
#define LEDR    35       // 35 led
#define relays0 21         
#define relays1 20     
#define led13   13 
#define buzzer  25
#define BP5     12     

LiquidCrystal lcd(27, 28, 25, 24, 23, 22); // RS=27, Enable=28, D4=25, D5=24, D6= 23, D7=22, BPpoussoir=30
// Configuration des variables

unsigned int cycle=0;
float SOC1=-1450;


void setup() {
   lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
  Serial.begin(9600);
EEPROM.put(4000, cycle);    //le cycle est l'adresse de l'enregistrement à 4000 octets initialisé à 0
EEPROM.put(cycle, 1400);  //derneire mesure du SOC
}

void loop() {
lcd.setCursor(0,0);
lcd.print("init EEprom  ");

EEPROM.get(4000, cycle);    //verification des données
EEPROM.get(cycle, SOC1); 

lcd.setCursor(13,1);
lcd.print(cycle);
lcd.print("Cy  ");

lcd.setCursor(0,2);
lcd.print(SOC1,0);
lcd.print("mAh  ");

// faut-il reinitialiser toute l'eeprom pour chaque nouvelle batterie

}



(http://[url=https://servimg.com/view/20189794/1][img]https://i65.servimg.com/u/f65/20/18/97/94/a110610.jpg)[/url][/img](http://[url=https://servimg.com/view/20189794/2][img]https://i65.servimg.com/u/f65/20/18/97/94/a37010.jpg)[/url][/img]

Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: modou_toure on Apr 20, 2020, 01:21 am
Bonsoir, j'ai continué le projet qu'avait commencé Adama(22AS99).

- J'ai ajouté une ligne de code pour le calcul de la résistance interne de la batterie. Cette dernière est obtenue en divisant la différence entre la tension mesurée de la batterie et sa tension électrochimique par l'intensité du courant délivré par le capteur de courant.

- Etant donné que les caractristiquse d'une batterie NIMH type AA ne
peut pas decharger à plus de 1A, du coup j'ai pris un capteur ACS712ELCTR-5A

- J'ai utilisé des leds (vert et rouge). Le led rouge clignote à une fréquence de 1/8s, si la tension d'arret (m=0.9v) et atteinte. Le vert clignote à la meme fréquence si la tension maximale de charge(1.5v) est atteinte.

- La charge charge et la décharge sont toujours gérées par le bouton poussoir mais il faut avoir un oeil sur
les leds pour toujours etre dans la plage de tension de batterie.  

Voici le code :

Code: [Select]
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define led13   13
#define relays1 12          
#define relays0 11  
#define charge_decharge    9    
#define on_off             10  
#define ledV 8  
#define ledR 7  
LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter        
/**********************************************************************************************/
float tension,tensionACS=0;       // Initialisation
float courant=0;
float sensible=0.185;           // Sensibilité du capteur
float ACSoffset=2.5;           // Valeur de tension de sortie du capteur lorsque le courant=0
float zeroint=512;
float var,var1=0;  
float R;
float m=0.9; // tension limite minimum
float M=1.5; // tension limite maximum
float E=1.2 ;
/********************************************************************************************/    
void setup()
{
pinMode(led13, OUTPUT);
pinMode(ledV,OUTPUT);
pinMode(ledR,OUTPUT);
pinMode(A0, INPUT); // On défini A0 en entrée
pinMode(relays1, OUTPUT);           // On défini relay1 en sortie
pinMode(relays0, OUTPUT);           // On défini relay0 en sortie
Serial.begin(9600);
lcd.init();         //et pas lcd begin comme cetraine biblio
lcd.display();     // activer l'affichage
lcd.backlight();   // allumer retroeclairage
lcd.clear();      // Effacer le l'écran
}
/******************************************************************************************/
void loop()
{/***********************************Cligotement LED************************************/
 
 digitalWrite(led13,LOW);   //cligotement led 13
 delay(100);
 digitalWrite(led13,HIGH);
/**********Mesure de la tension, du courant et de la capacité********/
 tension=analogRead(A0); // Lecture de la valeur récupèrée de A0
 tension=tension*(5.0/1023.0); // Conversion de cette valeur en une tension comprise entre 0 et 5V (Batterie)
 
 tensionACS=analogRead(A1); // Lecture de la valeur récupèrée de A1
 tensionACS=(tensionACS*5.0)/1023; // Conversion de cette valeur en une tension comprise entre 0 et 5V (Capteur ASC)
 courant=(tensionACS-ACSoffset)/sensible; // Calcul du courant
 var=courant+var;   //etat de charge en mA.h
 var1=var/360;  //division par 36 si tempsechatillon 100ms    //division par 3600 pour 1s
/**************************La gestion de la charge et décharge*************************/
 if(digitalRead(on_off)==0)  
 {digitalWrite(relays0,LOW);} // off
  else
 {digitalWrite(relays0,HIGH);} // on  
 if (digitalRead(charge_decharge)==0)  
 {digitalWrite(relays1,LOW); // charge
 R=(tension-E)/courant;  // Calcul de la résistance interne de la batterie état charge
 if(tension>=M){digitalWrite(ledV,HIGH);
                delay(125);
                digitalWrite(ledV,LOW);}}  
 else
 {digitalWrite(relays1,HIGH); // décharge
 R=(tension-E)/courant;  // Calcul de la résistance interne de la batterie état décharge
 if(tension<=m){digitalWrite(ledR,HIGH);
                delay(125);
                digitalWrite(ledR,LOW);}}
/*****************************Affichage sur l'écran LCD*******************************/
 lcd.setCursor(0,0);                
 lcd.print("Ubat : "); // Affichage de la tension batterie
 lcd.print(tension,2);
 lcd.print("V");
 lcd.setCursor(0,1);
 lcd.print("Ibat : "); // Affichage du courant
 lcd.print(courant,2);
 lcd.print("A");
 lcd.setCursor(0,2);
 lcd.print("Ri : "); // Affichage de la résistance interne
 lcd.print(R,2);
 lcd.print("ohm");
 lcd.setCursor(0,3);
 lcd.print(var1,0);
 lcd.print("mAh   ");
}



Voici le lien pour télécharger le schéma isis :

https://drive.google.com/drive/folders/11QPJlObjtTCaNw1ZXwGG7oc1HeF2KfU0

Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: modou_toure on Apr 29, 2020, 04:48 pm
Bonjour, j'apporte des modifications sur mon post du 19 avril
j'ai utilisé le TimerOne à une routine de 100ms.

Code: [Select]
#include <Wire.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <LiquidCrystal_I2C.h>
#define led13   13
#define relays1 12          
#define relays0 11  
#define charge_decharge    9    
#define on_off             10  
#define ledV 8  
#define ledR 7  
LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter        
/**********************************************************************************************/
float tension,tensionACS=0;       // Initialisation
float courant=0;
float sensible=0.185;           // Sensibilité du capteur
float ACSoffset=2.5;           // Valeur de tension de sortie du capteur lorsque le courant=0
float zeroint=512;
float var,var1=0;  
float R;
float m=0.9; // tension limite minimum
float M=1.5; // tension limite maximum
float E=1.2 ; // tension électrochimique
int n=0; // le nombre de fois de 1s
int Cbat=1500; // la capacité nominale de la batterie=1500mAh
float SOC;
int minute;
/********************************************************************************************/    
void setup()
{
  Timer1.initialize(100000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
pinMode(led13, OUTPUT);
pinMode(ledV,OUTPUT);
pinMode(ledR,OUTPUT);
pinMode(A0, INPUT); // On défini A0 en entrée
pinMode(relays1, OUTPUT);           // On défini relay1 en sortie
pinMode(relays0, OUTPUT);           // On défini relay0 en sortie
Serial.begin(9600);
lcd.init();         //et pas lcd begin comme cetraine biblio
//lcd.display();     // activer l'affichage
//lcd.backlight();   // allumer retroeclairage
lcd.clear();      // Effacer le l'écran
}
// Interruptions  tous les 0.1s fait par le timer1***********************************
void callback()  
{
  n++;
  if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
  minute++;
}
/******************************************************************************************/
void loop()
{
 /************************Mesure de la tension, du courant et de la capacité******************/
 if(n>=10){
 n=0;
 tension=analogRead(A0); // Lecture de la valeur récupèrée de A0
 tension=tension*(5.0/1023.0); // Conversion de cette valeur en une tension comprise entre 0 et 5V (Batterie)
 tensionACS=analogRead(A1); // Lecture de la valeur récupèrée de A1
 tensionACS=(tensionACS*5.0)/1023; // Conversion de cette valeur en une tension comprise entre 0 et 5V (Capteur ASC)
 courant=(tensionACS-ACSoffset)/sensible; // Calcul du courant
 var1=(courant/3.6)+var1;  //division par 3600 si tempsechatillon 1000ms,division par 3600 pour 1s
 //var1=var1*1000; // conversion de la quantité de charge en mAh
 //SOC=var1/Cbat; // état de charge
 //SOC=SOC*100; // état de charge en pourcentage
/******************************La gestion de la charge et décharge****************************/
 
if(digitalRead(on_off)==0) // interrupteur
 {digitalWrite(relays0,LOW);} // off
  else
 {digitalWrite(relays0,HIGH);} // on  
 if (digitalRead(charge_decharge)==0)  
 {digitalWrite(relays1,LOW); // charge
//if(minute>600){} // arret de la charge et decharge pendant 4s et mesure de E, calcul de la resistance
 
 R=(tension-E)/courant;  // Calcul de la résistance interne de la batterie état charge
 /*if(tension>=M){digitalWrite(ledV,HIGH);
                delay(125);
                digitalWrite(ledV,LOW);}*/}  
 else
 {digitalWrite(relays1,HIGH); // décharge
 R=(tension-E)/courant;  // Calcul de la résistance interne de la batterie état décharge
 /*if(tension<=m){digitalWrite(ledR,HIGH);
                delay(125);
                digitalWrite(ledR,LOW);}*/}
/*****************************Affichage sur l'écran LCD*******************************/
 lcd.setCursor(0,0);                
 //lcd.print("Ubat : "); // Affichage de la tension batterie
 lcd.print(tension,2);
 lcd.print("V");
 lcd.print("  "); // Affichage du courant
 lcd.print(courant,2);
 lcd.print("A");
 lcd.setCursor(0,1);
 //lcd.print("Ri : "); // Affichage de la résistance interne
 lcd.print(R,2);
 lcd.print("ohm");
 lcd.print("  ");
 lcd.print(var1,0);
 lcd.print("mAh   ");
 //lcd.setCursor(0,2);
 //lcd.print("SOC : ");
 //lcd.print(SOC,2);
 //lcd.print("%");
    }//fin if(n>=10)
    
} //fin loop ;




Voici le lien pour télécharger le schéma isis

https://drive.google.com/file/d/1jWBKTNUeDV3VpLqDrI_g2Me9MiE2X-4x/view?usp=sharing
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on May 02, 2020, 02:45 pm
Cela manque d'explication Mr Touré
Faire un cahier des charges et un algo permettrait d'etre plus explicite dans la gestion du testeur



Apres 1500 commutation du relais, charge et décharge, la résistance interne qui doit être  bien inférieur à 0.5 ohms a fortement augmenté et ne permettais plus ni de déchargé à 8A, mais surtout de rechargé à 4A le contact normalement ouvert
C'était un relais Songle

aliexpress (https://fr.aliexpress.com/item/32616081524.html?src=google&src=google&albch=shopping&acnt=494-037-6276&isdl=y&slnk=&plac=&mtctp=&albbt=Google_7_shopping&aff_platform=google&aff_short_key=UneMJZVf&&albagn=888888&albcp=6459793138&albag=77316928277&trgt=743612850714&crea=fr32616081524&netw=u&device=c&albpg=743612850714&albpd=fr32616081524&gclid=EAIaIQobChMI2-u_zo6V6QIVA9TeCh0OOgryEAQYAiABEgLpNvD_BwE&gclsrc=aw.ds)
(https://i89.servimg.com/u/f89/17/56/35/17/c145.jpg) (https://servimg.com/view/17563517/8081)

Je pensais que les contacts étaient charbonné mais pas du tout
(https://i89.servimg.com/u/f89/17/56/35/17/p1040351.jpg) (https://servimg.com/view/17563517/8082)

Donc plutôt, un problème de ressort

Je ne sais pas ce que vaut les autres relais chinois tel que BESTEP….meilleur ou pas ????
Est-ce qu'il y a des copies ?????
Est-ce que je n'ai pas eu de chance ? Ou est ce que cela manque de fiabilité ?

Ces relais ne sont  pas chers…mais je prefere payer 3 fois plus et que cela soit fiable
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on May 09, 2020, 04:57 pm
Ce serait bien que ton testeur niMH  soit valable pour les 2 types de batteries AA et AAA,
mais il va vous falloir regler le courant de charge et de decharge.....
https://fr.wikipedia.org/wiki/Format_des_piles_et_accumulateurs_%C3%A9lectriques
Mais il va falloir utiliser nombreux boutons poussoirs
- avec une entree analogique, comme pour  le shield keypad
http://domoticx.com/arduino-shield-lcd1602-display-met-keypad/
https://www.14core.com/wiring-the-lcd-16x2-keypad-shield-on-arduino/

Voila les tensions, avec les résistances precedentes du shield precedent
(https://i89.servimg.com/u/f89/17/56/35/17/arduin10.jpg) (https://servimg.com/view/17563517/8100)

- soit avec le clavier.
Evidemment, de nombreux lien sur internet, présente comment gérer un clavier.
Exemple :
http://www.mon-club-elec.fr/pmwiki_mon_club_elec/pmwiki.php?n=MAIN.ArduinoExpertLCDClavierAppuiTouche
Évidemment, il y a des librays qui gèrent le clavier.
https://playground.arduino.cc/Code/Keypad/
Voici le schéma électrique pour le code suivant
(https://i89.servimg.com/u/f89/17/56/35/17/b231.jpg) (https://servimg.com/view/17563517/8101)


Voici le code
Code: [Select]

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <Keypad.h>


#define PWM3       3      //   timer2
#define LED13     13      

LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables
//LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter 20x4ligne


unsigned    int    temps=0;
unsigned    int    temps1=0;
unsigned    int    Time=0;
unsigned    int    TO=0;
unsigned    int BoutonP;
String btnStr = "None";



const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}   };
            
byte rowPins[ROWS] = {A1, A2, A3, A4}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {12, 11, 10}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);


  Timer1.initialize(1000);           //  
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
  
  Serial.begin(9600);
  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

attachInterrupt(0, interrup2, FALLING);   // broche2
TO = millis();  

  lcd.setCursor(0,0);    
  lcd.print("IUT GEII Soissons");

//interrupts();  sei ();    //http://gammon.com.au/interrupts
//noInterrupts(); cli ();
}


void interrup2() // la fonction appelée par l'interruption externe n°0
{
Time=(millis()-TO);  //mesure du temps
TO = millis();
}

// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}

}//fin routine


///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {
  
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
delay(10);
BoutonP=analogRead(A7);

 if ((BoutonP <= 1000) ){
    if (BoutonP > 720 && BoutonP < 760){    
       btnStr="Select";                        //741
    } else if (BoutonP > 450 && BoutonP < 550){
      btnStr="Left";                           //506
    } else if (BoutonP < 10){
      btnStr="Right";                         //0            
    } else if (BoutonP > 100 && BoutonP < 200){
      btnStr="Up";
    } else if (BoutonP > 250 && BoutonP < 350){
      btnStr="Down";
    } else if (BoutonP > 820 && BoutonP < 870){
      btnStr="Efface";                        //850
    }
      //update button pressed
      lcd.setCursor(0,1);
      lcd.print(btnStr);
      lcd.print(" ");
//      lcd.print(BoutonP);
//      lcd.print("    ");  
            }

 //gestion du keypad
char key = keypad.getKey();

  if (key != NO_KEY){
      lcd.setCursor(0,2);
      lcd.print("keypad ");
      lcd.print(key);
  }  

                
} // fin loop  

Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: modou_toure on May 16, 2020, 04:26 pm
Bonjour,

J'ai apporté quelques modifications concernant mon dernier post du 29/04

Voici l'algorithme :

Code: [Select]

clignoter la led13
mesurer la tension
mesurer le courant
calculer la capacité
si temps = 1mn
                 alors désactiver le relai 0
                 mesurer la tension électrochimique E
                 si temps = 4s
                                  alors réactiver relai 0
si on appuie charge_decharge
                                          alors autoriser la charge ou la décharge
calcul de la résistance interne de la batterie
afficher la tension en volt
afficher le courant en ampère
afficher la capacité en mAh
afficher le temps


Le code est le suivant :

Code: [Select]

#include <Wire.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <LiquidCrystal.h>
#define led13   13
#define relays1 12         
#define relays0 11   
#define charge_decharge    9       
#define on_off 10
LiquidCrystal lcd(7,6,2,3,4,5); // (RS=7, E=6, D4=2, D5=3, D6=4, D7=5)     
/******************************************************************/
float tension,tensionACS=0;       // Initialisation
float courant=0;
float sensible=0.185;           // Sensibilité du capteur
float ACSoffset=2.5;           // Valeur de tension de sortie du capteur lorsque le courant=0
float capacite=0; 
float R;
float E=1.2 ; // tension électrochimique
int heure=0,minute=0,seconde=0; // le temps
/*****************************************************************/   
void setup()
{
  Timer1.initialize(1000000);          // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
pinMode(led13, OUTPUT);
pinMode(A0, INPUT); // On défini A0 en entrée
pinMode(relays1, OUTPUT);           // On défini relay1 en sortie
pinMode(relays0, OUTPUT);           // On défini relay0 en sortie
Serial.begin(9600);
lcd.begin(20,4);         //et pas lcd begin comme cetraine biblio
lcd.clear();      // Effacer le l'écran
digitalWrite(relays0,HIGH);
} // fin setup
// Interruptions  tous les 1s fait par le timer1****************
void callback() 
{
 seconde++;
 if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);} else {digitalWrite(13,HIGH);}
 tension=analogRead(A0); // Lecture de la valeur récupèrée de A0
 tension=tension*(5.0/1023.0); // Conversion de cette valeur en une tension comprise entre 0 et 5V (Batterie)
 tensionACS=analogRead(A1); // Lecture de la valeur récupèrée de A1
 tensionACS=(tensionACS*5.0)/1023; // Conversion de cette valeur en une tension comprise entre 0 et 5V (Capteur ASC)
 courant=(tensionACS-ACSoffset)/sensible; // Calcul du courant
 capacite=(courant/3.6)+capacite; // (I(A)*1000)/(1s/3600)+capacite   
} // fin void callback
void temps()
{ if(seconde>=60){seconde=0;
                  minute++;
                  digitalWrite(relays0,LOW);
                  if(minute>=60){minute=0;
                                 heure++;
                                }
                  if(heure>=24){heure=0;
                                lcd.clear();
                               }
                 }
  if(seconde==4){digitalWrite(relays0,HIGH);}           
} // fin void temps
/*********************************************************/
void loop()
{
 temps();
 
 if (digitalRead(charge_decharge)==0) 
 {digitalWrite(relays1,LOW);R=(tension-E)/courant;} // charge     
 else
 {digitalWrite(relays1,HIGH);R=E/courant;} // décharge
/*************Affichage sur l'écran LCD****************/
 lcd.setCursor(0,0);                 
 lcd.print(tension,2);
 lcd.print("V");
 lcd.print("  ");
 lcd.print(courant,2);
 lcd.print("A");
 lcd.setCursor(0,1);
 lcd.print(R,2);
 lcd.print("ohm");
 lcd.print("  ");
 lcd.print(capacite,0);
 lcd.print("mAh   ");
lcd.setCursor(5,2);
lcd.print(heure);
lcd.print("h ");
lcd.print(minute);
lcd.print("m ");
lcd.print(seconde);
lcd.print("s");
    }//fin loop ;


Vérification et simulation Isis :

En charge :
Pour être en charge, il faut désactiver l'interrupteur charge_decharge de l'entrée. On a alors l'alimentation de 1.5V de recharge la batterie à 1A.
En effet, le courant batterie correspond à l'équation suivante : I=(U-E)/R
Sur la figure suivante, on peut observer que le courant de charge est bien à 1A et que la tension de la batterie correspond bien à 1.3V. la mesure de la résistance interne affiche 0.09Ω presque égale à la valeur paramétrée dans la simulation.
Lorsque la tension de la batterie est à 1.5V et que le courant est de 0.1A alors il y a l'arrêt de la charge.
De plus, si le temps de charge est plus grand que 3 heures, la charge s'arrête.

(https://i42.servimg.com/u/f42/20/20/05/40/cc1_bm10.jpg) (https://servimg.com/view/20200540/8)

En décharge :
Pour la décharge, on active l'interrupteur charge_decharge. La batterie se décharge sur la résistance de décharge (0.9ohm). La tension de décharge est de 1.2V et le courant de décharge est 1A.
Le courant de batterie en décharge correspond à l'équation suivante : I=E/(R+r(0.9))
Dans la simulation Isis, représentée sur la figure ci-dessous, on observe que le courant de charge est égal à 1A et que la tension de la batterie correspond bien à 1.1V.
La résistance interne affichée est de 1.12Ω : ce qui est presque égale à la valeur paramétrée dans la simulation.

(https://i42.servimg.com/u/f42/20/20/05/40/cc2_bm10.jpg) (https://servimg.com/view/20200540/9)

Voici le lien pour télécharger le fichier Isis :
https://drive.google.com/file/d/1aHmKApdtJsQLtyKSUddmVvACPW5-L8TQ/view?usp=sharing

Pour plus d'information concernant le projet, je vous invite à télécharger le dossier dont le lien
est le suivant : https://www.fichier-doc.fr/2020/05/16/batterie/

Perspectives :
Notre testeur NiMH fonctionne bien, mais pour sécuriser le fonctionnement, un capteur de température LM35 va permettre de mesurer la température externe de la batterie.
Si celle-ci est supérieure à 50°C, alors la charge ou la déchargé sera bloquée.
Dans un deuxième temps, de réaliser un testeur de deux batteries NiMH de type AA et AAA à la fois en parallèle avec une Nano.

Merci.



Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: modou_toure on May 21, 2020, 02:29 pm
J'ai ajouté dans mon dernier post du 16/05 une autre batterie NiMH de type AAA.
J'ai changé les logicstates avec de vrais interrupteurs pour la gestion de la charge et de la décharge de chaque batterie, et un autre interrupteur pour imposer à l'afficheur écran LCD les informations sur quelle batterie sont à afficher.
J'ai aussi utilisé la liaison série pour pouvoir récupérer les informations concernant chaque batterie dans le but d'une étude ultérieure.
Deux thermomètres Lm35 (un pour chaque batterie) ont été rajoutés pour sécuriser le fonctionnement, il va permettre de mesurer la température externe des batteries.
Si celle-ci est supérieure à 50°C, alors la charge ou la déchargé sera bloquée.

Voici le code :
Code: [Select]

#include <Wire.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <LiquidCrystal.h>
#define led   13
#define R1 12          
#define R0 11  
#define R2 8
#define R3 9
//#define on_off 10  
LiquidCrystal lcd(7,6,2,3,4,5); // (RS=7, E=6, D4=2, D5=3, D6=4, D7=5)    
/******************************************************************/
float tension1=0,tension2=0;       // Initialisation
float courant1=0,courant2=0;
float sensible=0.185;           // Sensibilité du capteur
float ACSoffset=2.5;           // Valeur de tension de sortie du capteur lorsque le courant=0
float capacite1=0,capacite2=0;  
float Rb1,Rb2;
float E1=1.2,E2=1.2; // tension électrochimique
int heure=0,minute=0,seconde=0; // le temps
float temperature;
/*****************************************************************/    
void setup()
{
  Timer1.initialize(1000000);          // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
pinMode(led, OUTPUT);
pinMode(A0, INPUT); // On défini A0 en entrée
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(A3, INPUT);
pinMode(A4, INPUT);
pinMode(A5, INPUT_PULLUP);
pinMode(10, INPUT_PULLUP);
pinMode(0, INPUT_PULLUP);
pinMode(R0, OUTPUT);pinMode(R1, OUTPUT);pinMode(R2, OUTPUT);pinMode(R3, OUTPUT); // On définit les relaies en sortie
Serial.begin(9600);
lcd.begin(20,4);         //et pas lcd begin comme cetraine biblio
lcd.clear();      // Effacer l'écran
digitalWrite(R0,HIGH);
digitalWrite(R2,HIGH);
} // fin setup
// Interruptions  tous les 1s fait par le timer1****************
void callback()  
{
 seconde++;
 if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);} else {digitalWrite(13,HIGH);}
 tension1=analogRead(A0)*(5.0/1023.0); tension2=analogRead(A3)*(5.0/1023.0); // Récupération et conversion de cette valeur en une tension comprise entre 0 et 5V (Batterie)
 courant1=((analogRead(A1)*(5.0/1023.0))-ACSoffset)/sensible; courant2=((analogRead(A2)*(5.0/1023.0))-ACSoffset)/sensible; // Calcul du courant
 capacite1=(courant1/3.6)+capacite1; capacite2=(courant2/3.6)+capacite2; // (I(A)*1000)/(1s/3600)+capacite  
 temperature=((analogRead(A4)*5.0)/1023.0)*(1/0.01); // la tension de sortie est de : 10mV/°C
} // fin void callback
void temps()
{ if(seconde>=60){seconde=0;
                  minute++;
                  if(analogRead(A5)*(5.0/1023.0)>=4.99)
                  {digitalWrite(R0,LOW);}
                  else
                  {digitalWrite(R2,LOW);}
                  if(minute>=60){minute=0;
                                 heure++;
                                }
                  if(heure>=24){heure=0;
                                lcd.clear();
                               }
                 }
  if(seconde==4)
  {if(analogRead(A5)*(5.0/1023.0)>=4.99){digitalWrite(R0,HIGH);}
   else {digitalWrite(R2,HIGH);}
  }          
} // fin void temps
void affichage_temps()
{
 lcd.setCursor(5,2);
 lcd.print(heure);
 lcd.print("h ");
 lcd.print(minute);
 lcd.print("m ");
 lcd.print(seconde);
 lcd.print("s ");
}
/*********************************************************/
void affichage_bat1()
{
 lcd.setCursor(0,0);      
 lcd.print("Bat1 :");  
 lcd.setCursor(7,0);          
 lcd.print(tension1,2);
 lcd.print("V ");
 lcd.print(" ");
 lcd.print(courant1,2);
 lcd.print("A ");
 lcd.setCursor(0,1);
 lcd.print(Rb1,2);
 lcd.print("ohm");
 lcd.print("  ");
 lcd.print(capacite1,0);
 lcd.print("mAh   ");
 lcd.setCursor(0,3);
 lcd.print("temperature : ");
 lcd.print(temperature);
 Serial.println("Bat1 : ");
 Serial.print("Temps : ");Serial.print(heure);Serial.print("h ");Serial.print(minute);Serial.print("m ");Serial.print(seconde);Serial.println("s ");
 Serial.print(tension1,2);Serial.print("V   ");Serial.print(courant1,2);Serial.print("A   ");
 Serial.print(capacite1,0);Serial.print("mAh   ");Serial.print(temperature);Serial.println("deg   ");
}
void affichage_bat2()
{
 lcd.setCursor(0,0);      
 lcd.print("Bat2 :");  
 lcd.setCursor(7,0);                
 lcd.print(tension2,2);
 lcd.print("V ");
 lcd.print(" ");
 lcd.print(courant2,2);
 lcd.print("A ");
 lcd.setCursor(0,1);
 lcd.print(Rb2,2);
 lcd.print("ohm");
 lcd.print("  ");
 lcd.print(capacite2,0);
 lcd.print("mAh   ");
 lcd.setCursor(0,3);
 lcd.print("temperature : ");
 lcd.print(temperature);
 Serial.println("Bat2 : ");
 Serial.print("Temps : ");Serial.print(heure);Serial.print("h ");Serial.print(minute);Serial.print("m ");Serial.print(seconde);Serial.println("s ");
 Serial.print(tension2,2);Serial.print("V   ");Serial.print(courant2,2);Serial.print("A   ");
 Serial.print(capacite2,0);Serial.print("mAh   ");Serial.print(temperature);Serial.println("deg   ");
}
void loop()
{
 temps();
if (digitalRead(10)==1)  
{digitalWrite(R1,LOW);Rb1=(tension1-E1)/courant1;} // charge    
else
{digitalWrite(R1,HIGH);Rb1=E1/courant1;} // décharge
if (digitalRead(0)==1)  
{digitalWrite(R3,LOW);Rb2=(tension2-E2)/courant2;} // charge    
else
{digitalWrite(R3,HIGH);Rb2=E2/courant2;} // décharge
/*************Affichage sur l'écran LCD****************/
if(analogRead(A5)*(5.0/1023.0)>=4.99){affichage_bat1();}
else{affichage_bat2();}
affichage_temps();

    }//fin loop ;


Quelques captures d'écran :

Sur des deux images suivantes, on observe que l'écran affiche les valeurs de la batterie AAA (Bat1).
En charge :
On a alors l'alimentation de 1.5V et un courant de charge de à 1C avec C=1000mAh.
En effet, le courant batterie correspond à l'équation suivante : I=(U-E)/R
On peut observer que le courant de charge est bien à 1A et que la tension de la batterie correspond bien à 1.3V. La mesure de la résistance interne affiche 0.09Ω presque égale à la valeur paramétrée dans la simulation.
(https://i42.servimg.com/u/f42/20/20/05/40/captur17.png) (https://servimg.com/view/20200540/12)

En décharge :
La batterie se décharge sur la résistance de décharge (0.9ohm). La tension de décharge est de 1.2V et le courant de décharge est 1A.
Le courant de batterie en décharge correspond à l'équation suivante : I=E/(R+r(0.9))
Dans la simulation Isis, représentée sur la figure ci-dessous, on observe que le courant de charge est égal à 1A et que la tension de la batterie correspond bien à 1.1V.
La résistance interne affichée est de 1.12Ω : ce qui est presque égale à la valeur paramétrée dans la simulation.
(https://i42.servimg.com/u/f42/20/20/05/40/captur18.png) (https://servimg.com/view/20200540/13)

Sur des deux images suivantes, on observe que l'écran affiche les valeurs de la batterie AA (Bat2).
En charge :
On a une alimentation de 1.5V et un courant de charge de à C/2 avec C=2000mAh.
Comme la batterie AAA,  on observe bien que le courant de charge est bien à 1A et que la tension de la batterie correspond bien à 1.3V. La mesure de la résistance interne affiche 0.09Ω presque égale à la valeur paramétrée dans la simulation.
(https://i42.servimg.com/u/f42/20/20/05/40/captur19.png) (https://servimg.com/view/20200540/14)

En décharge :
La batterie se décharge avec un courant de décharge à C/5 sur la résistance de décharge (2.9ohm). La tension de décharge est de 1.2V et le courant de décharge est 0.4A.
Sur la figure suivante, on observe que le courant de charge est égal à 0.38A et que la tension de la batterie correspond bien à 1.16V.
La résistance interne affichée est de 3.13Ω : ce qui est presque égale à la valeur paramétrée dans la simulation.
(https://i42.servimg.com/u/f42/20/20/05/40/captur20.png) (https://servimg.com/view/20200540/15)

Voici le lien pour télécharger le schéma isis :
https://drive.google.com/file/d/1RHmqUQpsXPeSx0Wc8Ea8I2hkCKYlP26c/view?usp=sharing

Title: code I2C shields
Post by: iutgeiisoissons on May 23, 2020, 10:58 am
Quelles sont tes perspectives Mr Touré ?????


Lorsqu'on n'a pas beaucoup d'entrée et sortie comme sur la nano, on peut rajouter beaucoup d'esclave relier par 4 fils avec le bus de communication I2C comme sur la figure suivante si l'on a des systèmes assez lent.

Mais il y a un conflit entre la routine d'interruption timerOne et la communication de la bibliothèque « wire ».
Mais il est possible d'utiliser un real time clock qui ferrait la même chose que la routine d'interruption time par l'intermédiaire de la routine d'interruption extérieure de 1seconde

Hors la charge et décharge des batteries sont assez lente cela dure environ 1 heure, donc une routine d'interruption extérieur  1 seconde sur le Sout du DS1307 peut faire très bien l'affaire

Voici les adresses des composants utilisé par notre câblage
I2C device found at address 0x20    PCF8574
I2C device found at address 0x21    PCF8575
I2C device found at address 0x27    LCD PCF8574
I2C device found at address 0x60    DAC MCP4725   A0=0
I2C device found at address 0x68    DS1307
I2C device found at address 0x50    EEPROM   AT4C1024

Voici le code qui utilise différente bibliothèque juste pour le DS1307, le LCD, ainsi que les PCF857X
Code: [Select]

#include <Wire.h>     // // Join i2c bus
#include <LiquidCrystal_I2C.h>
#include "RTClib.h"      // Bibliothèque pour le module RTC


LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter
RTC_DS1307 rtc;   //minuscule    minuscule


#define led13   13
byte x;
unsigned int n;
byte data;


DateTime datetime;

void setup()
{  // initialise l'afficheur LCD
pinMode(led13, OUTPUT);
//pinMode(A4, OUTPUT); //resistance de pull-up inutile
//pinMode(A5, OUTPUT);


lcd.init();  //et pas lcd begin comme cetraine biblio
  lcd.display();     // activer l'affichage
  lcd.backlight();   // allumer retroeclairage

  lcd.setCursor(0,0);
  lcd.print("IUT GENIE elect&info");
  lcd.setCursor(5,1);
  lcd.print("Soissons");
  //Serial.begin(57600);

 Wire.begin();
   
rtc.begin();       //adress fixe 0x68
rtc.adjust(DateTime(2020, 05, 10, 23, 59, 35));    //annee, mois, date, heure

pf575_write(word(B00000000,B00000000));

//https://www.idreammicro.com/post/Utilisation-du-Square-Wave-Output-du-DS1307
    Wire.beginTransmission(0x68);   //dS1307  adress
    Wire.write(0x07);     //DS1307_CONTROL_Regitre
    Wire.write(B00010000);   //frequence 1Hz
    Wire.endTransmission();
 

  attachInterrupt(0, Rexterieur2, RISING);   // broche2
}

void Rexterieur2() // la fonction appelée par l'interruption externe n°0
{
//digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption                                   
//digitalWrite(LED13,LOW);
if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
}

void pf575_write(uint16_t data)
{
  Wire.beginTransmission(0x21); //adresse PCF8575
  Wire.write(lowByte(data));
  Wire.write(highByte(data));
  Wire.endTransmission();
}



void loop()
{

 DateTime now=rtc.now();
/*
lcd.setCursor(0,2);
lcd.print(now.day(),DEC);
lcd.print("/" );
lcd.print(now.month(), DEC);
lcd.print("/" );
lcd.print(now.year(), DEC);
lcd.print("   ");
*/
 
  lcd.setCursor(0,3);
   lcd.print(now.hour(), DEC);
   lcd.print(':');
   lcd.print(now.minute(), DEC);
   lcd.print(':');
   lcd.print(now.second(), DEC);
   lcd.print("   ");

x++;      //variable
Wire.beginTransmission(0x20);   // transmit to device #20  PCF8574  A0=0, A1=0, A2=0
Wire.write(x);                  // sends value byte
Wire.endTransmission();         // stop transmitting 

 Wire.requestFrom (0x20, 1);   //lecture information
 if (Wire.available ()) {
   data = Wire.read ();  }
   
pf575_write(word(x,x));

delay(100);

}



Une capture d'écran qui prouve que cela fonctionne
(https://i89.servimg.com/u/f89/17/56/35/17/b236.jpg) (https://servimg.com/view/17563517/8136)




Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: modou_toure on Jun 03, 2020, 06:05 pm
Suite à mon dernier post du 21 mai, vu que la carte n'a plus assez de pins disponibles, j'ai donc décidé d'utiliser la liaison I2C pour la gestion de l'écran LCD, prenant broches à l'Arduino.

Le composant principal de l'écran LCD I2C est l'extenseur d'E/S PCF8574, avec seulement deux broches SDA et SCL, nous obtenons un maximum de 8 broches de P0 à P7. Le PCF8574A peut également être utilisé mais il a une adresse différente.
Toutes les broches de données LCD sont connectées au PCF8574 où : RS, RW, E, D4, D5, D6 et D7 sont connectés à P0, P1, P2, P4, P5, P6 et P7 respectivement.


Un problème se pose : il y a un conflit entre la routine d'interruption TimerOne et la communication I2C.
Pour résoudre cette problématique, j'ai utilisé un Real Time Clock à la place du TimerOne.

Il fait la même chose que la routine d'interruption TimerOne par l'intermédiaire de la routine d'interruption extérieure de 1 seconde.

Je vous le lien de mon dossier pour plus d'explication sur la communication I2C et l'interruption extérieure : https://www.fichier-doc.fr/2020/06/03/batterie-toure-v4/ (https://www.fichier-doc.fr/2020/06/03/batterie-toure-v4/)

Dans la partie suivante, je vais récupérer les valeurs du Virtual Terminal pour l'estimation de l'état de santé de chaque batterie.

pourriez vous m'aider pour l'estimation de la regression linéaire pour programmation en Arduino et avec Excel??
et faire un exemple simple


Merci d'avance


Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jun 03, 2020, 09:11 pm
Pour estimer le SOH d'une batterie, la méthode par régression linéaire peut se faire  à partir de 2 points.
mais Plus, il y aura de points et plus l'estimation sera précise si la courbe se rapporche d'une droite ????
La régression linéaire est bien expliquée sur Excel ici
https://support.office.com/fr-fr/article/droitereg-droitereg-fonction-84d7d0d9-6e50-4101-977a-fa7abf772b6d (https://support.office.com/fr-fr/article/droitereg-droitereg-fonction-84d7d0d9-6e50-4101-977a-fa7abf772b6d)
https://support.microsoft.com/fr-fr/office/coefficient-determination-coefficient-determination-fonction-d7161715-250d-4a01-b80d-a8364f2be08f (https://support.microsoft.com/fr-fr/office/coefficient-determination-coefficient-determination-fonction-d7161715-250d-4a01-b80d-a8364f2be08f)
La méthode des moindres carres, régression, ajustement, équations ou courbes de tendances ont tous le même nom

Pour faire un exemple simple :

La tension de la batterie diminue =5-0,1*B2   donc a=-0.1  et b=5
A la place d'un nuage de points, la tension de la batterie oscillera =C2-0,2*SIN(B2)
L'estimation de la tension sera =a*capacité+b           avec a=pente de la droite
Pour avoir la valeur de la pente a de la régression linéaire E3 =INDEX(DROITEREG($D$2:D3;$B$2:B3;VRAI;FAUX);1)
Puis, à chaque mesure E4 une nouvelle pente est déterminée E4= =INDEX(DROITEREG($D$2:D4;$B$2:B4;VRAI;FAUX);1)

Pour avoir la valeur de la pente de la régression linéaire b =INDEX(DROITEREG($D$2:D3;$B$2:B3;VRAI;FAUX);2)

On peut observer sur la capture d'écran
Les données déterminées par les fonctions Excel avec le coefficient de dermination R^2 qui est assez proche de 1 mais la sinusoïde provoque des écarts de détermination. Heureusement, que la diminution de la tension des batteries
(https://i89.servimg.com/u/f89/17/56/35/17/captur47.jpg) (https://servimg.com/view/17563517/8182)

Mais comme tu as dit, il faut mieux faire le calcul par l'Arduino de la regression lineaire par tableau
D'ailleurs de nombreux softs (Matlab, C++, python, Mathcad), les fonctions de la regression lineaire existent qui peuvent donner directement ces données
ici regression polynomiale en C avec le code source
https://github.com/dani2442/Polynomial-Regression (https://github.com/dani2442/Polynomial-Regression)
Sur Arduino, des bibliothèques existent, mais il faudrait les tester
https://github.com/cubiwan/LinearRegressino/blob/master/README.md (https://github.com/cubiwan/LinearRegressino/blob/master/README.md)
https://github.com/Rotario/arduinoCurveFitting (https://github.com/Rotario/arduinoCurveFitting)

pour ma part, mon code est noyé avec tout le programme de test de batterie et ce n'est pas trop explicite


Le calcul des coefficients de  la regression lineaire sont les suivantes
(https://i89.servimg.com/u/f89/17/56/35/17/c326.jpg) (https://servimg.com/view/17563517/8181)
Ce qui  donne pour l'exemple
(https://i89.servimg.com/u/f89/17/56/35/17/c234.jpg) (https://servimg.com/view/17563517/8183)

Tu peux telecharger le fichier de l'exemple ci dessus Excel  ici
https://drive.google.com/file/d/1rWsyEtMK7Orjirq5xmjrkUy3sXB87jnx/view?usp=sharing (https://drive.google.com/file/d/1rWsyEtMK7Orjirq5xmjrkUy3sXB87jnx/view?usp=sharing)


Donc l'algo Arduino somme X, somme des Y, sommeX^2, Somme des X*Y  calcul de a et b

Attention, pour chaque chimie de batterie, la variation de la tension en fonction de la capactité energetique est différente donc l'estimation de la regression lineaire et viable ou pas ?
De plus, l'estimation du SOH est viable à partir d'une certaine capacité energetique dépensée.

pour le Ni mh, il faudrait faire des essais avant


Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jun 04, 2020, 08:08 am
Pour les lithiums LTO, LIPO l'estimation de la régression linéaire est très satisfaisante, mais seulement à partir d'une certaine valeur de capacité énergétique dépensée.
D'ailleurs, pour les batteries LTO, le 27/02/2020 la courbe expérimentale de l'estimation a été présenté sur ce post et elle est viable à partir de 15% de la capacité énergétique
Pour les éléments lithium NCM 18650 Samsung, on peut observer sur la figure suivante  que l'estimation est viable seulement à partir de 20% de la capacité énergétique qui est ici de 2.5A.h
(https://i89.servimg.com/u/f89/17/56/35/17/c327.jpg) (https://servimg.com/view/17563517/8184)

Est-ce qu'avec une régression polynomiale, on pourrait améliorer l'estimation ?
La, il faut me laisser du temps
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jun 04, 2020, 06:30 pm
Pas encore réaliser  le programme ????????

Est-ce qu'il faut des bibliothèques sur la régression linéaire alors que 7 lignes de programmes suffisent ?

Dans l'exemple ci-dessous et posté precedement, la pente est de -0.01 et b=5 et le programme la retrouve bien pour chaque mesure effectuée.
D'ailleurs la régression linéaire demande un temps de calcul et l'envoie des données en USB de 1.6ms.
L'estimation du SOH donne bien 20A.h pour une tension d'arrêt de décharge de 3V
(https://i89.servimg.com/u/f89/17/56/35/17/c329.jpg) (https://servimg.com/view/17563517/8186)

Voici le code qui mesure et calcul la régression linéaire toutes les sondes toutes les 1 secondes.
Code: [Select]

include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>

#define LED13    13      
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define PI 3.141592
unsigned    int    temps=0;
 
float  n=0;                 //nombre d'echnatillon
float  X=0;    //varaible abscisse
float Y=0;     //ordonnée image de l'abcisse
float SommeX=0;
float SommeXsquare=0;  
float SommeY=0;
float SommeXY=0;

float a=0;    //pende de la courbe  Y=a*X+b
float b=0;    //constante

float estimateur;
          
void setup() {
  pinMode(LED13, OUTPUT);

  Timer1.initialize(1000000);           //   pour 0.001s  1000   0.002s 2000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
  Serial.begin(9600);
//  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet
}




// Interruptions  tous les 1 fait par le timer1    fe=1Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
X=n;                   // capacity energetique batttery
Y=5-0.1*X-0.2*sin(n);            //Y=voltage=analogRead(A0);   for battery
SommeX=SommeX+X;     //for (byte i = 0; i < M; i++) {SommeX=X[i]+SommeX;}
SommeXsquare=SommeXsquare+(X*X);
SommeY=SommeY+Y;
SommeXY=SommeXY+(X*Y);
n++;
if(n>=2) {             // 2 echantillon pour une regression lineaire    
a=(((n*SommeXY)-(SommeX*SommeY))/((n*SommeXsquare)-(SommeX*SommeX)));
b=((SommeY-(a*SommeX))/n);
estimateur=(3-b)/a;          //estimateur x lorsque Y devrait atteontre  la valeur 3
}
/*
Serial.print(X,1);Serial.print(";");
Serial.print(Y,1);Serial.print(";");
Serial.print(SommeX,1);Serial.print(";");
Serial.print(SommeY,1);Serial.print(";");
Serial.print(SommeXsquare,1);Serial.print(";");
Serial.print(SommeXY,1);Serial.print(";  ");
*/
Serial.print(a,3);Serial.print(";");
Serial.print(b,3);Serial.print(";");
Serial.print(estimateur,3);Serial.println(";");

digitalWrite(LED13,LOW);      //calcul et envoie en 1.6ms
}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {
 
lcd.setCursor(0,0);    
lcd.print("Regression Lineaire");
lcd.setCursor(0,1);    
lcd.print("nbr echantillon=");
lcd.print(n);
lcd.setCursor(0,2);
lcd.print("a=");
lcd.print(a,3);                 //coefficient a
lcd.print(" b=");
lcd.print(b,3);                 //coefficient b
lcd.setCursor(0,3);
lcd.print("estimateur(3)=");
lcd.print(estimateur,3);

} // fin loop  



Avec le bruit du sinus de l'exemple donné par le fichier Excel précèdent, on peut observer que la régression linéaire donne les même valeurs que dans excel, et qu'il y a un écart entre la valeur théorique de 20A.h et  celle donné par l'estimateur.
(https://i89.servimg.com/u/f89/17/56/35/17/c424.jpg) (https://servimg.com/view/17563517/8187)


L'Arduino pourrait faire le calcul du coefficient de corrélation d'échantillonnage de Pearson, pour savoir si la courbe estimée peut être une droite ou pas.
Si c'est le cas la corrélation ou de détermination de Pearson doit être proche de -1 avec une pente negative
https://en.wikipedia.org/wiki/Pearson_correlation_coefficient
bizarre il y a des petites différence dans les données fournies par Excel si l'on prend  la fonction « Pearson » et « coeficient.determination »

j'ai oublié de le faire dans le code  précédent
Il est possible de le déterminer par les equations suivantes
(https://i89.servimg.com/u/f89/17/56/35/17/c235.jpg) (https://servimg.com/view/17563517/8189)
(https://i89.servimg.com/u/f89/17/56/35/17/captur48.jpg) (https://servimg.com/view/17563517/8190)
Il suffit de rajouter
Code: [Select]

SommeYsquare=SommeYsquare+(Y*Y);

Rpearson=(n*SommeXY-SommeX*SommeY)/((sqrt((n*SommeXsquare)-sq(SommeX))*sqrt((n*SommeYsquare)-sq(SommeY))));

Serial.print(Rpearson,3);Serial.print(";");


Le virtual terminal donne a, b, Rpearson, l'estimateur
(https://i89.servimg.com/u/f89/17/56/35/17/c330.jpg) (https://servimg.com/view/17563517/8188)




cela devrait t aider à comprendre


Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: modou_toure on Jun 04, 2020, 07:23 pm
Merci beaucoup iutgeiisoissons, c'est très clair.
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jun 07, 2020, 06:51 pm
Pendant que j'y suis autant que j'explique la régression polynomiale…
D'ailleurs, elle est plus adaptée pour estimer la capacité énergétique d'une batterie. Mais le coefficient du second ordre et troisième peuvent parfois être négligé pour certaine chimie.

Pas facile de comprendre comment on détermine les coefficients d'une régression polynomiale par les encyclopédies.
https://en.wikipedia.org/wiki/Local_regression (https://en.wikipedia.org/wiki/Local_regression)
https://fr.wikipedia.org/wiki/R%C3%A9gression_polynomiale (https://fr.wikipedia.org/wiki/R%C3%A9gression_polynomiale)
Le calcul des ajustements de courbes, n'est pas très explicite non plus
https://fr.wikipedia.org/wiki/Ajustement_de_courbe (https://fr.wikipedia.org/wiki/Ajustement_de_courbe)
https://fr.wikipedia.org/wiki/R%C3%A9gression_polynomiale (https://fr.wikipedia.org/wiki/R%C3%A9gression_polynomiale)

Pourtant, la programmation est relativement simple mais demande un calcul matricielle ou des équations à rallongent
La régression linéaire est une régression polynomiale d'ordre 1 avec x^1, puis l'on peut faire des polynomes ordre 2 avec x^2, et d'ordre supérieur…..
il y a une library pour les matrice pour arduino
https://github.com/eecharlie/MatrixMath (https://github.com/eecharlie/MatrixMath)
voici la méthode pour bien comprendre la programmation
(https://i89.servimg.com/u/f89/17/56/35/17/c236.jpg) (https://servimg.com/view/17563517/8191)
Voici le résultat des équations pour un polynome d'ordre 2 seulement
(https://i89.servimg.com/u/f89/17/56/35/17/captur49.jpg) (https://servimg.com/view/17563517/8192)

Pour vérifier que le programme Arduino fonctionne bien ou pour utiliser la méthode par l'intermédiaire d'Excel
En effet, à la place que ce soit le processeur qui fasse le calcul de la régression polynomiale, les données peuvent être envoyées dans Excel pour que le PC fasse les données.
http://electroniqueamateur.blogspot.com/2014/10/transmettre-les-donnees-darduino-vers.html (http://electroniqueamateur.blogspot.com/2014/10/transmettre-les-donnees-darduino-vers.html)

Mais comment avoir les données dans Excel ?

Dans Excel, pour avoir une régression polynomiale d'ordre 2  et fait par calcul en matriciel [3,3], donc va retourner 3 valeurs avec la fonction suivante. {1.2} demande un second ordre. {1.2.3} demande un troisième ordre.
DROITEREG($B$31:B33;$A$31:A33^{1.2})
C'est très bien expliqué et présenté ici pour avoir les données matricielles dans Excel
http://www.smart-metrology.com/blog/2015/03/exploitation-des-resultats-detalonnage-pour-evaluer-la-part-de-linstrument-dans-un-bilan-dincertitude/ (http://www.smart-metrology.com/blog/2015/03/exploitation-des-resultats-detalonnage-pour-evaluer-la-part-de-linstrument-dans-un-bilan-dincertitude/)
Sur la figure suivante, on peut observer les valeurs de la courbe de tendances du second ordre, qui est bien déterminée par Excel par la fonction précédente.
On peut observer que la pente  avec la régression linéaire de la courbe, va diminuer et fournir la tangente à la courbe. Donc, avec la regression linaire, si l'on prend les points au début de la courbe qui est d'ordre 2, l'estimation de la capacité énergétique sera complètement décalée.
(https://i89.servimg.com/u/f89/17/56/35/17/c425.jpg) (https://servimg.com/view/17563517/8195)

attention : dans excel, a0 est le poids forts alors que dans arduino, c'est le poids faible

Nous allons utiliser ces mêmes équations dans le programme Arduino
Sur le Virtual terminal, est affiché après 3 mesures, le vecteur a de la courbe de tendance…..
(https://i89.servimg.com/u/f89/17/56/35/17/c516.jpg) (https://servimg.com/view/17563517/8197)
Puis à chaque mesure, toutes les secondes, la régression polynomiale est recalculée…
L'estimation pour un seuil de tension de 3.1V est calculé, on retrouve bien les 7.2A.h qui est dans Excel precedement

Voici le code, ou les cases de la matrice 3*3 pour l'ordre 2, est  remplie valeur par valeur
Code: [Select]

//#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>
#include <MatrixMath.h>   //https://github.com/eecharlie/MatrixMath

#define LED13    13      
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define PI 3.141592
unsigned    int    temps=0;

float n=0;   //nombre d'echantillons
float X=0;    //varaible abscisse
float Y=0;     //ordonnée image de l'abcisse

#define N  (2+1)      //N ordre
float SommeX [N*2];    //sommeX0, sommeX1, sommeX2
float SommeXY [N];
mtx_type S[N][N];
mtx_type v[N];      // This is a colonn vector
mtx_type a[N];      // This is a colonn vector a[0]coeficient poids faible  X^0,  a[1]coeficient  X^1=X, a[2] constante   X^2  

float a0;        //valeur de a0 moins le seuil de la tension atteinte ou la battery est dechargé à 100%
float estimateur=0;
 
          
void setup() {
  pinMode(LED13, OUTPUT);

  Timer1.initialize(1000000);           //   pour 0.001s  1000   0.002s 2000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
  Serial.begin(9600);
//  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet
}




// Interruptions  tous les 1 fait par le timer1    fe=1Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
X=n;                   // capacity energetique batttery
Y=5-0.43*X+0.023*sq(X);            //Y=voltage=analogRead(A0);   for battery
SommeX [0]=SommeX [0]+1   ;        //SommeX [0]=SommeX [0]+pow(X, 0)
SommeX [1]=SommeX [1]+X   ;        //SommeX [1]=SommeX [1]+pow(X, 1)
SommeX [2]=SommeX [2]+X*X ;        //SommeX [2]=SommeX [2]+pow(X, 2)
SommeX [3]=SommeX [3]+pow(X, 3);
SommeX [4]=SommeX [4]+pow(X, 4);

SommeXY [0]=SommeXY [0]+Y;
SommeXY [1]=SommeXY [1]+Y*X;
SommeXY [2]=SommeXY [2]+Y*X*X;
n++;
if(n>=4)   {
 S[0][0] = SommeX [0];
 S[0][1] = SommeX [1];
 S[0][2] = SommeX [2];
  
 S[1][0] = SommeX [1];
 S[1][1] = SommeX [2];
 S[1][2] = SommeX [3];  

 S[2][0] = SommeX [2];
 S[2][1] = SommeX [3];
 S[2][2] = SommeX [4];  

 v[0]=SommeXY [0];
 v[1]=SommeXY [1];
 v[2]=SommeXY [2];

//Serial.println("\n S:");
//Matrix.Print((mtx_type*)S, N, N, "S");
//Serial.println("\n v:");
//Matrix.Print((mtx_type*)v, N, 1, "v");

 Matrix.Invert((mtx_type*)S, N);
// Serial.println("\nInverted S:");
// Matrix.Print((mtx_type*)S, N, N, "S");


Matrix.Multiply((mtx_type*)S, (mtx_type*)v, N, N, 1, (mtx_type*)a);
Matrix.Print((mtx_type*)a, N, 1, "a");
Serial.println("");
a0=a[0]-3.1;       //3.1
estimateur=((sqrt(sq (a[1])-(4*a[2]*a0))+a[1])/(2*a[2]));    //resolution second ordre premiere valeur de x=capcity energy
}

Serial.print(X,1);Serial.print(";");
Serial.print(Y,1);Serial.print(";");
Serial.print(estimateur,1);Serial.println(";");

digitalWrite(LED13,LOW);      //calcul et envoie en
}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {
 
lcd.setCursor(0,0);    
lcd.print("Regression polyno");
lcd.setCursor(0,1);    
lcd.print("ordre");
lcd.print(N-1);
lcd.setCursor(0,2);
lcd.print("a0=");
lcd.print(a[0],3);              
lcd.print(" a1=");
lcd.print(a[1],3);                
lcd.setCursor(0,3);
lcd.print("a2=");
lcd.print(a[2],3);    


} // fin loop  



Evidement, pour un ordre 3, ou la matrice est de 4.4, avec 2 boucles FOR, la matrice peut etre rempli automatiquement.
donc, une regression polynomiale est un recette qu'il faut juste savoir utiliser
Le temps de calcul et d'estimation est de 3.8ms avec l'envoie des données sur l'USB
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jun 08, 2020, 05:33 pm
Si l'on désire prendre en compte la décroissance rapide de la tension en fin de décharge, une régression polynomiale troisième ordre, voir du quatrième ordre devrait être utilisé pour certaine chimie de battery.
D'ailleurs, à chaque valeur supplémentaire, il est possible d'augmenter l'ordre.
Si le quatrième ordre est utilisé alors que la courbe de tendance ne correspond pas à cet ordre, alors le coefficient sera négligeable.
D'ailleurs, on peut l'observer sur la figure suivante sur Excel ou la variation de la tension est un troisième ordre comme on peut le voir sur la courbe de tendance
(https://i89.servimg.com/u/f89/17/56/35/17/c426.jpg) (https://servimg.com/view/17563517/8209)

On peut observer sur la figure suivante que l'Arduino retrouve bien l'équation précédente
(https://i89.servimg.com/u/f89/17/56/35/17/c237.jpg) (https://servimg.com/view/17563517/8210)


Voici le code ou il suffit juste de rentrer l'ordre N pour la régression polynomiale désirée

Code: [Select]

//#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>
#include <MatrixMath.h>   //https://github.com/eecharlie/MatrixMath

#define LED13    13      
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define PI 3.141592
unsigned    int    temps=0;

float n=0;   //nombre d'echantillons
float X=0;    //varaible abscisse
float Y=0;     //ordonnée image de l'abcisse
float z=0;
#define N  (3+1)      //polynome 3 ordre
float SommeX [2*N];    //2*ordre   ,sommeX0, sommeX1, sommeX2
float SommeXY [N];
mtx_type S[N][N];
mtx_type v[N];      // This is a colonn vector
mtx_type a[N];      // This is a colonn vector a[0]coeficient poids faible  X^0,  a[1]coeficient  X^1=X, a[2] constante   X^2  

float a0;        //valeur de a0 moins le seuil de la tension atteinte ou la battery est dechargé à 100%
float estimateur=0;

          
void setup() {
  pinMode(LED13, OUTPUT);

  Timer1.initialize(1000000);           //   pour 0.001s  1000   0.002s 2000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
  Serial.begin(9600);
//  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet
}

// Interruptions  tous les 1 fait par le timer1    fe=1Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
X=n;                   // capacity energetique batttery
Y=(5-0.71*X+0.145*pow(X, 2)-0.013*pow(X, 3));            //Y=voltage=analogRead(A0);   for battery

for (byte i = 0; i <= (N-1)*2; i++) {SommeX [i]=SommeX [i]+pow(X, i);}    
for (byte i = 0; i < (N); i++) {SommeXY [i]=SommeXY [i]+Y*pow(X, i);}


n++;
if(n>=4)   {
for (byte i = 0; i < N; i++) {
for (byte j = 0; j < N; j++)   { S[i][j] =SommeX [j+i] ;  }
                             }
for (byte i = 0; i < N; i++)  { v[i]=SommeXY [i];}

//Serial.println("\n S:");
//Matrix.Print((mtx_type*)S, N, N, "S");
//Serial.println("\n v:");
//Matrix.Print((mtx_type*)v, N, 1, "v");

 Matrix.Invert((mtx_type*)S, N);
// Serial.println("\nInverted S:");
// Matrix.Print((mtx_type*)S, N, N, "S");


Matrix.Multiply((mtx_type*)S, (mtx_type*)v, N, N, 1, (mtx_type*)a);
Matrix.Print((mtx_type*)a, N, 1, "a");
Serial.println("");
a0=a[0]-3.1;       //3.1
//estimateur=((sqrt(sq (a[1])-(4*a[2]*a0))+a[1])/(2*a[2]));    
}
Serial.print(X,1);Serial.print(";");
Serial.print(Y,1);Serial.println(";");
//Serial.print(estimateur,1);Serial.println(";");

digitalWrite(LED13,LOW);      //calcul et envoie en
}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {
 
lcd.setCursor(0,0);    
lcd.print("Regression polyno");
lcd.setCursor(0,1);    
lcd.print("ordre");
lcd.print(N-1);
lcd.setCursor(0,2);
for (byte i = 0; i < (N/2); i++) {lcd.print("a");lcd.print(i);lcd.print("=");lcd.print(a[i],3);lcd.print(i);lcd.print(" ");}
lcd.setCursor(0,3);
for (byte i = N/2; i < (N); i++) {lcd.print("a");lcd.print(i);lcd.print("=");lcd.print(a[i],3);lcd.print(i);lcd.print(" ");}
} // fin loop  



résoudre l'équation d'un troisième ordre pour faire l'estimation n'est pas si facile car l'équation est relativement grande.
(https://i89.servimg.com/u/f89/17/56/35/17/captur50.jpg) (https://servimg.com/view/17563517/8211)
https://www.lucaswillems.com/fr/articles/58/equations-troisieme-degre?cache=update (https://www.lucaswillems.com/fr/articles/58/equations-troisieme-degre?cache=update)
l'equation est encore plus grande, si l'on a des ordres supérieurs,
Mais il y a des méthodes par dichotomie, Newton…..par programmation pour faire la résolution d'une fonction
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jun 09, 2020, 11:44 am
Pour avoir l'estimateur avec une équation polynomiale
La méthode de la dichotomie ou « bissection method » est bien expliquée dans wikipedia avec meme un algo
https://fr.wikipedia.org/wiki/M%C3%A9thode_de_dichotomie
Pour la methode de Newton, des exemples bien fait ici
http://math.univ-lille1.fr/~bodin//fichiers/ch_zeros.pdf

Voici le code de l'estimateur avec la bissection method avec la régression polynomial
Code: [Select]

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>
#include <MatrixMath.h>   //https://github.com/eecharlie/MatrixMath

#define LED13    13      
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define PI 3.141592
unsigned    int    temps=0;

float n=0;   //nombre d'echantillons
float X=0;    //varaible abscisse
float Y=0;     //ordonnée image de l'abcisse

#define N  (3+1)      //polynome 3 ordre
float SommeX [2*N];    //2*ordre   ,sommeX0, sommeX1, sommeX2
float SommeXY [N];
mtx_type S[N][N];
mtx_type v[N];      // This is a colonn vector
mtx_type a[N];      // This is a colonn vector a[0]coeficient poids faible  X^0,  a[1]coeficient  X^1=X, a[2] constante   X^2  

float a0;        //valeur de a0 moins le seuil de la tension atteinte ou la battery est dechargé à 100%
float estimateur=0;
float x2=15;
float x1=0;
float Yxes, Yx1;
byte z=0;
          
void setup() {
  pinMode(LED13, OUTPUT);

  Timer1.initialize(1000000);           //   pour 0.001s  1000   0.002s 2000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
  Serial.begin(9600);
//  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet
}

// Interruptions  tous les 1 fait par le timer1    fe=1Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
X=n;                   // capacity energetique batttery
Y=(5-0.71*X+0.145*pow(X, 2)-0.013*pow(X, 3));            //Y=voltage=analogRead(A0);   for battery

//determination des coefficients de la courbe de tendance
for (byte i = 0; i <= (N-1)*2; i++) {SommeX [i]=SommeX [i]+pow(X, i);}    
for (byte i = 0; i < (N); i++) {SommeXY [i]=SommeXY [i]+Y*pow(X, i);}

n++;
if(n>=4)   {
for (byte i = 0; i < N; i++) {
for (byte j = 0; j < N; j++)   { S[i][j] =SommeX [j+i] ;  }
                             }
for (byte i = 0; i < N; i++)  { v[i]=SommeXY [i];}

Matrix.Invert((mtx_type*)S, N);

Matrix.Multiply((mtx_type*)S, (mtx_type*)v, N, N, 1, (mtx_type*)a);
//Serial.print(a[0],3);serial.print(";");Serial.print(a[1],3);serial.print(";");Serial.print(a[2],3);serial.print(";");Serial.println(a[3],3);

//bissection methode
a0=a[0]-3;       //3 seuil de tension que l'estimateur doit decouvrir, mais il est possible de prendre 2,5V
while ((x2-x1)>=0.01) {estimateur=(x2+x1)/2;
                      Yxes=(a0+a[1]*estimateur+a[2]*pow(estimateur, 2)+a[3]*pow(estimateur, 3));
                      Yx1=(a0+a[1]*x1+a[2]*pow(x1, 2)+a[3]*pow(x1, 3));
                      if ((Yxes*Yx1)<0) {x2=estimateur;}  else {x1=estimateur;}          
                    z++;
                    //if (z==1) {Serial.println("bissection methode");}
                    Serial.print(x1,2);Serial.print(";");Serial.print(x2,2);Serial.print(";");Serial.print(estimateur,2);Serial.println(";");
                      }
x2=15; x1=0; z=0; //remise à zero des plages de l'estimateurs
} //fin if n>4
//Serial.println("X,Y,estimation");
//Serial.print(X,1);Serial.print(";");
//Serial.print(Y,1);Serial.print(";");
//Serial.print(estimateur,1);Serial.println(";");

digitalWrite(LED13,LOW);      //calcul et envoie en
}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {
 
lcd.setCursor(0,0);    
lcd.print("Regression polyno");
lcd.setCursor(0,1);    
lcd.print("ordre");
lcd.print(N-1);
lcd.setCursor(0,2);
lcd.print("bissection methode");
lcd.setCursor(0,3);
lcd.print("estimation=");
lcd.print(estimateur,2);
} // fin loop  



avec une precision de 2 chiffres après la virgule, N=2 suivant les equations suivantes
(https://i89.servimg.com/u/f89/17/56/35/17/c427.jpg) (https://servimg.com/view/17563517/8212)
la convergence de la valeur se fait en 11 itérations, à partir de l'équation suivante
nbr iteration=(2+Log(15-0))/log2=11.28
la valeur 15 provient de la capacity energetique de la battery maximale initialisé dans le programme

avec une precision de 3 chiffres après la virgule, N=3 suivant les equations suivantes, le nombre d'iteration augmente legerement, mais ce n'est pas problematique
nbr iteration=(3+Log(15-0))/log2=14

Chaque ligne donne les A.h, la tension, et l'estimation, comme on peut l'observer sur la figure suivante avec le nombre d'iteration sur le virtual terminal. On retrouve le nombre d'iteration
(https://i89.servimg.com/u/f89/17/56/35/17/c239.jpg) (https://servimg.com/view/17563517/8215)

Le calcul de la regression lineaire du 3eme ordre et la methode de bissection dure 14ms sans l'envoie des données,
donc ce n'est pas un problème.

j'ai fait un maximum pour la transparence des codes et des methodes



Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: modou_toure on Jun 25, 2020, 04:20 pm
j'ai réalisé le montage du projet sur testeur et cycleur batterie NiMH,
et j'ai fait des tests sur une batterie de 2300mA.h pour tracer la courbe de décharge sur résistance de 1.8Ω, sur deux résistances de 1.8Ω en parallèle et sur deux résistances de 1.8Ω en série,
de la tension, du courant, de la résistance interne et de la température en fonction de la capacité énergétique, afin de faire des comparaisons.

Le programme est le suivant :

Code: [Select]

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#define PWM3       3      //   timer2
#define LED13     13      
#define R2     12
#define R1     11

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
float voltage;
float courant;
float ACSoffset=2.5; // tension pour un courant I=0A
float sensibilite=0.100;
float capacite; // capacité énergétique
float R; // résistance interne
float E; // tension électrochimique
float temperature;
int seconde=0;
int BP1,BP2,BP3,BP4;
int flag1,flag2;

  void setup() {
  Timer1.initialize(1000000);           //  
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  pinMode(A7,INPUT);pinMode(A1,INPUT);pinMode(A2,INPUT);
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);
  pinMode(R2, OUTPUT);
  pinMode(R1, OUTPUT);

  flag1=1; // OFF
  flag2=0; // CHARGE
  
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
  Serial.begin(9600);
  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

}

void callback()
{
if (digitalRead(13)== 1 )
{digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
voltage=analogRead(A7);  // valeur numérique de la tension
voltage=(voltage*5)/1024;// valeur analogique de la tension
courant=analogRead(A1);  
courant=(courant*5)/1024;
courant=courant-ACSoffset; // tension image du courant en valeur analogique - ACSoffset(2.5V)
courant=courant*10;
if(digitalRead(R2)==0){courant=courant*(-1);} // CHARGE
else{courant=courant;}                        // DECHARGE
capacite=(courant/3.6)+capacite;
temperature=analogRead(A3);
temperature=(temperature*500)/1024;

seconde++; // temps

if(seconde==292){ // si le temps=292s, récupèrer sur le terminal les valeurs suivantes
Serial.print(capacite,0);Serial.print(";");Serial.print(voltage,2);Serial.print(";");
Serial.print(courant,2);Serial.print(";");Serial.print(temperature,1);Serial.print(";");
}
if(seconde==294){flag1=1;} // OFF, si le temps=294s,desactiver le relais 1
if(seconde==296){E=analogRead(A7);E=(E*5)/1024;Serial.print(E,2);Serial.print(";");} // si le temps=296s, mesurer l'OCV
if(seconde==298){flag1=0;} // ON, si le temps=298s, réactiver le relais 1
if(seconde==300){if(flag2==1){R=(E-voltage)/courant;} // DECHARGE, si le temps=5mn, mesurer la résistance interne en décharge
                if(flag2==0){R=(voltage-E)/courant;} //  CHARGE, si le temps=5mn, mesurer la résistance interne en charge
                Serial.println(R,2);seconde=0;}

lcd.setCursor(0,0);
lcd.print("V:");
lcd.print(voltage,2);
lcd.print("  I:" );
lcd.print(courant,2);
lcd.setCursor(0,1);
lcd.print("Q:" );
lcd.print(capacite,0);
lcd.print("  T:" );
lcd.print(temperature,1);
lcd.setCursor(0,2);
lcd.print("R:" );
lcd.print(R,2);
lcd.print(" ");
lcd.print(seconde);
}//fin routine

void loop() {
BP1=digitalRead(A2);
BP2=digitalRead(A5);
BP3=digitalRead(A4);
BP4=digitalRead(10);
if(BP1==1 && BP2==0){flag1=0;if(BP1==0 && BP2==0){flag1=0;}}
if(BP1==0 && BP2==1){flag1=1;if(BP1==0 && BP2==0){flag1=1;}}
if(flag1==0){digitalWrite(R1,HIGH);} // ON
if(flag1==1){digitalWrite(R1,LOW);}  // OFF
if(BP3==1 && BP4==1){flag2=0;if(BP3==0 && BP4==1){flag2=0;}}
if(BP3==0 && BP4==0){flag2=1;if(BP3==0 && BP4==1){flag2=1;}}
if(flag2==0){digitalWrite(R2,LOW);} // CHARGE
if(flag2==1){digitalWrite(R2,HIGH);}  // DECHARGE            
} // fin loop


Vérification du fonctionnement du programme en simulation sur Isis:

Sur le fichier suivant, on décharge la batterie sur une résistance de 1.8Ω, j'ai ajouté en plus des 0.1Ω, résistance de chaque relais, un potentiomètre réglé à 81%*2Ω=1.62Ω, et du coup la résistance externe
r=0.1Ω+0.1Ω+1.62Ω+1.8Ω=3.62Ω et la résistance interne de la batterie (en simulation Isis) R=0.1Ω.
Le courant de décharge est : I=(E-V)/R=E/(R+r)=0.3A; V=r*I
On voit sur le fichier que le courant  calculé avec la formule ci-dessus dans le code (0.34A) est différent de celui mesuré par l'ampèremètre (0.32A) ; en pratique, cela pourrait être par les perturbations extérieures d'où la précision=((0.34-0.32)/0.32)*100=6.25%
On remarque que l'intensité du courant obtenue est quasiment égale à celle prévue, donc le programme fonctionne bien.

(https://i42.servimg.com/u/f42/20/20/05/40/captur21.png) (https://servimg.com/view/20200540/16)

Le fichier de simulation sur Isis est téléchargeable sur le lien suivant :
https://drive.google.com/file/d/1uUah3NojfsN6JGvbuIkGZ7pi1IPcg8ZH/view?usp=sharing

Voici le montage :
Sur cette image, le relais 1 est désactivé (état Off), le courant est nul, enfin presque.
J'alimente l'arduino avec le 5V de la source de tension car avec l'alim du PC, la tension de sortie est environ 4.2V et du coup les mesures ne seront pas exactes.

(https://i42.servimg.com/u/f42/20/20/05/40/whatsa10.jpg) (https://servimg.com/view/20200540/17)

Les courbes de décharge sont les suivantes :
Analyse et Comparaison:
On observe dans les figgures 1 et 2 que la résistance interne augmente considérablement plus la capacité énergetique diminue.
Cela fait chuter fortement le courant de sortie.
Si on observe bien les trois figures, on remarque que la chute de tension est plus rapide et plus importante en décharge sur les résistances en paralléle que sur une résistance et sur les deux résistances en série.
Cela s'explique par le fait que la résistance équivalente de décharge (0.9Ω) sur la figure 2 est plus petite que la résistance (1.8Ω) de décharge sur la figure 1 et encore plus petite que la somme des deux résistances en série (3.6Ω)  et que d'après la loi d'Ohm,
moins la résistance est petite et plus la demande de courant est importante, et cela fait chuter la tension.
Plus la tension diminue, plus le courant chute.

(https://i42.servimg.com/u/f42/20/20/05/40/ggg10.png) (https://servimg.com/view/20200540/19)
Le fichier Excel est téléchargeable sur le lien suivant:
https://drive.google.com/file/d/1jBz2tNE78Tyuw0IXYM3uPdZVeX59gwiQ/view?usp=sharing

Il faut préciser que le temps d'échantillonnage pour l'écriture des valeurs n'est pas le même.
Pour la décharge sur les deux résistances en parallèle c'est 30s puisque la décharge est très rapide, 1mn sur une résistance et 5mn pour deux résistance en série.


Title: test Etat de charge et santé de batterie NiMH, (arduino)
Post by: iutgeiisoissons on Jun 27, 2020, 08:31 am
Dans votre programme, je ne vois pas l'arrêt de la charge ????

Différentes possibilités de recharger des batteries NiMh sont possibles qui vont jouer sur la durée de vie de la batterie.
Voici quelques liens
https://lygte-info.dk/info/batteryChargingNiMH%20UK.html (https://lygte-info.dk/info/batteryChargingNiMH%20UK.html)
http://bricolsec.canalblog.com/archives/2012/11/02/25480956.html (http://bricolsec.canalblog.com/archives/2012/11/02/25480956.html)
https://espace.etsmtl.ca/id/eprint/120/1/DEKKICHE_Abdelillah.pdf (https://espace.etsmtl.ca/id/eprint/120/1/DEKKICHE_Abdelillah.pdf)

La charge à C/10 sans danger, pour un temps 10heures est bien trop longue.
La détection du dV/dt de la tension de la batterie n'est pas si facile à détecter car très faible.
Mais vous mesurez la température de la batterie.
Donc, avec un générateur qui limite le courant à une certaine valeur (exemble C/2 avec une limitation à 2V ; dans isis, c'est possible à configurer un courant constant) et lorsque la température atteint environ 38°C, alors  arrêter la charge avec un flag. En effet, en fin de charge, la batterie ne transforme plus l'énergie reçue en chimie mais en chaleur et cette énergie se transforme en chaleur (divergence)
Sur la figure suivante, on peut observer, ce mode de charge
(https://i89.servimg.com/u/f89/17/56/35/17/a1141.jpg) (https://servimg.com/view/17563517/8275)
Très peu de chargeur vendu utilise cette méthode à cause à des problèmes de contact entre la batterie et le capteur de température.

Mais, ces dernières années, le capteur de température IR  sans contact GY-906 MLX90614ESF est devenu bon marché à moins de 7€ peut être utilisé.

Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: modou_toure on Jun 29, 2020, 02:24 am
Voici en dessous le nouveau programme modifié qui prend en compte de l'arrêt de la charge et de la décharge.
La charge est arrêté si et seulement si la température de la batterie atteint 38°C ou
si la tension de la batterie V = E + r.I >= 1.6V.
Et on arrête la décharge si  la tension de la batterie V = E - r.I <= 0.5V.
Pour arrêter la charge ou la décharge, il suffit de mettre le relais 1 en état Off.
Code: [Select]

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#define PWM3       3      //   timer2
#define LED13     13       
#define R2     12
#define R1     11

float voltage;
float courant;
float ACSoffset=2.5; // tension pour un courant I=0A
float sensibilite=0.185;
float capacite; // capacité énergétique
float R; // résistance interne
float E; // tension électrochimique
float temperature;
int seconde=0;
int BP1,BP2,BP3,BP4;
int flag1,flag2;
LiquidCrystal lcd(8,9,4,5,6,7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
 
  void setup() {
  Timer1.initialize(1000000);           // 
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  pinMode(A7,INPUT);pinMode(A1,INPUT);pinMode(A2,INPUT);
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);
  pinMode(R2, OUTPUT);
  pinMode(R1, OUTPUT);
 
  flag1=0; // OFF
  flag2=1; // CHARGE
 
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
  Serial.begin(9600);
  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet
  ACSoffset=analogRead(A1);
  }// fin setup

void callback()
{
if (digitalRead(13)== 1 )
{digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
voltage=analogRead(A7);  // valeur numérique de la tension
voltage=(voltage*5)/1024;// valeur analogique de la tension
courant=analogRead(A1); 
courant=courant-ACSoffset; // tension image du courant - ACSoffset
courant=(courant*5)/1024; // Conversion en valeur analogique
courant=courant/sensibilite;
capacite=(courant/3.6)+capacite;
temperature=analogRead(A3);
temperature=(temperature*500)/1024;

seconde++; // temps
if(seconde==60){ 
Serial.print(capacite,0);Serial.print(";");Serial.print(voltage,2);Serial.print(";");
Serial.print(courant,2);Serial.print(";");Serial.print(temperature,1);Serial.print(";");
}
if(seconde==61){flag1=1;}
if(seconde==62){E=analogRead(A7);E=(E*5)/1024;Serial.print(E,2);Serial.print(";");}
if(seconde==63){flag1=0;}
if(seconde==64){if(flag2==1){R=(E-voltage)/courant;}
                if(flag2==0){R=(voltage-E)/courant;}
                Serial.println(R,2);
                if(voltage<=0.5){flag1=1;seconde=65;}
                else if(temperature>=38||E>=1.6){flag1=1;seconde=65;}
                else {seconde=0;}}

lcd.setCursor(0,0);
lcd.print("V:");
lcd.print(voltage,2);
lcd.print("  I:" );
lcd.print(courant,2);
lcd.setCursor(0,1);
lcd.print("Q:" );
lcd.print(capacite);
lcd.print("  T:" );
lcd.print(temperature,1);
lcd.setCursor(0,2);
lcd.print("R:" );
lcd.print(R,2);
lcd.print(" ");
lcd.print(seconde);
}//fin callback

void loop() {
BP1=digitalRead(A2);
BP2=digitalRead(A5);
BP3=digitalRead(A4);
BP4=digitalRead(10);
if(BP1==1 && BP2==0){flag1=0;if(BP1==0 && BP2==0){flag1=0;}}
if(BP1==0 && BP2==1){flag1=1;if(BP1==0 && BP2==0){flag1=1;}}
if(flag1==0){digitalWrite(R1,HIGH);} // ON
if(flag1==1){digitalWrite(R1,LOW);}  // OFF
if(BP3==1 && BP4==1){flag2=0;if(BP3==0 && BP4==1){flag2=0;}}
if(BP3==0 && BP4==0){flag2=1;if(BP3==0 && BP4==1){flag2=1;}}
if(flag2==0){digitalWrite(R2,LOW);} // CHARGE
if(flag2==1){digitalWrite(R2,HIGH);}  // DECHARGE         
}// fin loop
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jun 29, 2020, 08:22 pm
Encore un contact relais Songle (relais chinois)  défaillant, après 800 commutations sur le testeur LTO.

Le relais peut être remplacé par un Omron G5LE 1DC5 qui devrait supporter pour 8A 100 000 commutations suite à leurs docs
http://www.farnell.com/datasheets/2182434.pdf?_ga=2.136815667.2060745107.1593329096-627363803.1593329096&_gac=1.14804866.1593329117.EAIaIQobChMIz_WRtv2j6gIVycmyCh1ztQ15EAAYASAAEgJAWfD_BwE

Mais, il vaut mieux remplacer les relais par des transistors avec optocoupleur cela coute moins cher en plus.
En plus, les courants pour le NiMh sont faibles. les transistors sont surdimensionnés mais à ce prix, on s'en moque
les transistors canal P aurait pu etre utilisé mais ils sont moins abondants que les canals N

(https://i89.servimg.com/u/f89/17/56/35/17/a1142.jpg) (https://servimg.com/view/17563517/8292)
Mais avec les transistors, la configuration électrique change comme on peut le voir sur la figure suivante
(https://i89.servimg.com/u/f89/17/56/35/17/a1143.jpg) (https://servimg.com/view/17563517/8293)

En mettant un certain nombre de résistances en parallèles pour décharger la batterie, il est possible de commuter les différentes résistances et d'avoir une régulation de courant constant pour décharger.
Pourquoi, le TIP 122, car j'en ai toute une boite de chaussure de ce composant.


Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jul 13, 2020, 09:15 am
Voici un nouveau programme avec toutes les conditions possibles d'arrêt de charge et de décharge avec 2 relais indépendant pour la charge et la décharge.
Les nombreuses conditions avec la gestion de l'écran auraient pu être programmées avec un switch case pour bien les identifier.
Mais cela n'a pas été fait.
Avec un bouton charge ou décharge,  la résistance est testé au début de chaque mode
Mais, il y a un bouton test résistance qui fait la mesure en mode charge ou décharge quand on l'active.
Un bouton test qui réalise une près charge, puis une décharge à 100% pour tester la capacité énergétique jusque ce la tension de l'élément NiMH attient 0.5V et l'affiche Soc. Puis il y a une recharge à 100%.
On peut comparer la capacité en décharge et en charge.

On peut observer sur la capture d'écran le mode du test pour la dernière charge
(https://i89.servimg.com/u/f89/17/56/35/17/a2130.jpg) (https://servimg.com/view/17563517/8343)

La routine d'interruption pour l'affichage est de 1s et le programme de la routine dure 25ms, donc cela ne pose pas de problème
Le test de la résistance attend que le temps de commutation du  relais soit effectué d'où l'utilisation des délais de 10ms


Etant donné que les transistors vont permettre une meilleure durée de vie du système par rapport aux relais. Mais les transistors vont permettre  une régulation du courant constante de décharge ou de la charge grâce à l'inductance et une PWM de 32kHz.
En effet, en décharge la constante de temps L/R=1mH/1ohms est bien supérieure à 1/32kHz=31µs. Donc avec une résistance de batterie de 0.1ohm, le filtre L/R attenue encore plus la variation du courant.

La régulation de décharge à courant constant sera avec un correcteur « intégral unitaire »
if (I>0.51)  {PWM--; }   //erreur positive
if (I<0.49)  {PWM++; }
if (PWM>253) {PWM=253; }
if (PWM<1) {PWM=1; }

Cette régulation est très stable ; mais étant donné que, la PWM  s'incrémente ou se décrémente  de 1 toutes les secondes, la PWM est initialisé à une valeur proche de la valeur désirée.
Le courant de décharge correspond en l'équation suivante : Idecharge(A)=(Ubatt-VCEsat)*PWM/(Rdecharge*255)

On peut observer sur la figure suivante la régulation de la décharge
(https://i89.servimg.com/u/f89/17/56/35/17/a450.jpg) (https://servimg.com/view/17563517/8344)

le programme en PJ

De la même manière, il est possible de réguler le courant de charge
Mais, pour quelques euros il vaut mieux une alimentation avec CI XL4015 pour la charge qui permet de limiter le courant à 1A et limiter la tension à 2.5V
(https://i89.servimg.com/u/f89/17/56/35/17/a390.jpg) (https://servimg.com/view/17563517/8342)

Evidement pour un élément PP3 de 8.4V, il faut modifier les valeurs pour l'arrêt de la décharge, la valeur du courant de charge et de décharge. De même pour les boitiers type AAA.
Pour ma part, j'ai utilisé un vieux chargeur pour mettre l'Arduino à l'intérieur…..
J'ai gardé le transformateur à point milieu de 2*11.5V pour alimenter l'Arduino et l'alimentation à courant constant, mais rien que l'afficheur me mange tout l'intérieur du chargeur.
(https://i89.servimg.com/u/f89/17/56/35/17/p1040632.jpg) (https://servimg.com/view/17563517/8346)

Evidement un chargeur tel que le BTY V407 pour 17€ est bien plus compacte.
Mais si celui-ci donne les informations des capacités énergétiques de la charge. Il ne la test pas en décharge et ne donne pas l'info de la résistance interne.
Commercialement, il y a d'autres types de chargeur et testeur tel que le lien suivant pour environ 130 € depuis 2016

https://www.skyrc.com/Charger/MC3000_Charger (http://)
Ce chargeur testeur  permet d'avoir une analyse par bluetooth sur smartphone et permet d'éviter l'écran LCD
(https://i89.servimg.com/u/f89/17/56/35/17/a530.jpg) (https://servimg.com/view/17563517/8345)

Cela pourrait être une perspective pour ce chargeur analyseur avec Arduino qui est facile à réparer s'il y a un problème par rapport à l'électronique propriétaire précèdent.

Une publication d'environ 8 pages sur les chargeurs et les testeurs des NiMH arrive pour faire la synthèse du travail
mais voici le rapport
https://www.fichier-pdf.fr/2020/08/05/nimh-testeur-chargeur-arduino--sivert/ (http://)


Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jul 14, 2020, 09:45 pm
J'ai récupéré de vieux chargeurs chez Emmaüs exactement comme dans mon boitier chargeur précèdent.
Mais, l'afficheur LCD 4 lignes 20 caractères prend vraiment beaucoup de place.
Or, j'ai un ESP32 Heltec qui traine avec un écran oled 128x64 c'est petit comme afficheur (2.5cmx1.25cm) mais en plus, il peut afficher des courbes donc pourquoi pas.
voilà le programme juste pour faire une routine d'interruption toutes les 1 seconde et l'affichage d'une courbe.

Code: [Select]

#include "Arduino.h"
#include "heltec.h"
int Te=1000000  ; //periode d'echantillonnage micro seconde  1s

DRAM_ATTR  int Time;
DRAM_ATTR  float  Y[127];          //declaration des Y  
DRAM_ATTR  float  phase=0;
DRAM_ATTR byte a=0;

byte i=0;

hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
uint32_t cp0_regs[18];


void IRAM_ATTR onTimer() {      //IRAM et DRAM
digitalWrite(33,HIGH);
  portENTER_CRITICAL_ISR(&timerMux);

xthal_set_cpenable(1);  xthal_save_cp0(cp0_regs);   // // Save FPU registers
//if ( digitalRead(33)== 1 ) {digitalWrite(33,LOW);}  else {digitalWrite(33,HIGH);}

Time++;
Y[Time]=31+10*sin(1.5*2*PI*Time/127+phase);           //calcul la courbe
if (Time>127) {Time=0;phase=phase+0.78;}               //remet à zero, le calcul et cree un dephasage de PI/4
    
  xthal_restore_cp0(cp0_regs);  xthal_set_cpenable(0);  // and turn it back off
  portEXIT_CRITICAL_ISR(&timerMux);
  digitalWrite(33,LOW);
}
 
void setup() {
 
  Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/);
//  Serial.begin(9600);
  Serial.begin(115200);
//  Heltec.display->flipScreenVertically();
  Heltec.display->setFont(ArialMT_Plain_10); //22 caracteres possible
  Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT);
 
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, Te, true);     //200000 0.2s
  timerAlarmEnable(timer);

   Heltec.display->clear();  
   Heltec.display->drawString(3, 0, "IUT GEII Soissons");          // afficher 17 caractéres 5ms;  1 seul caractere 3.6ms      
 //   Heltec.display->drawString(0, 11, "Time "+String(Time));
 //   Heltec.display->setPixel(0, 0);  // (X,Y) en haut à gauche
 //   Heltec.display->setPixel(63,31 );  //milieu
 //   Heltec.display->setPixel(127,63);  // en bas à droite
 Heltec.display->drawHorizontalLine(0,31,120);  //(x,y,length)      //trace les axes
 Heltec.display->drawVerticalLine(0,0,63);      //(x,y,length)
 Heltec.display->display();  
}
 
void loop() {

Heltec.display->setPixel(Time, Y[Time]);
Heltec.display->display();
            }




(https://i89.servimg.com/u/f89/17/56/35/17/p1040647.jpg) (https://servimg.com/view/17563517/8362)
Mais impossible d'utiliser l'afficheur OLED dans la routine d'interruption car la bibliothèque de OLED  bloque les interruptions
Donc, il faut refaire tout le programme qui était valable du post précédent ou la gestion de l'afficheur est exclusivement dans la routine d'interruption.
Il y a le même problème si on veut utiliser un afficheur LCD I2C.
c'est là où on voit qu'un programme ne peut être utiliser d'un processeur à un autre, meme avec le meme compilateur, à cause du choix de  matos..

Il y avait une publication dans elecktor pour utiliser un afficheur OLED,
J'ai voulu m'en inspirer, mais c'est immangeable....
https://www.elektormagazine.fr/articles/enregistreur-de-temprature-avec-arduino-nano (http://)
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Jul 29, 2020, 08:51 am
Sur le OLED 128x64 (2.5cmx1.25cm) de l'ESP32, la gestion de l'affichage et des courbes demande de faire des translations et de gerer les echelles..
Avec la taille 10 et la police Arial d'écriture, il est possible de faire 6 lignes de 21 caractères donc 2 lignes supplémentaires par rapport aux LCD précèdent.
La tension est enregistrée toutes les minutes dans une table et elle est affichée dans le menu 2 correspondant pour le « case 2. »
Etant donné que le (x,y) zéro correspond, en haut à gauche de l'écran OLED, une translation des points doit se faire en fonction de l'échelle choisie.
Ici l'échelle est figée pour être habitué à sa lecture. Mais, une échelle automatique aurait pu être programmée en fonction des écarts de tension et du temps du tableau en identifiant les valeurs min et max.
L'effacement de l'écran se fera qu'une seule fois lors du changement de menu pour ne pas avoir l'effet de clignotement de l'écran.
De même toutes les données de la table des mesures seront envoyé qu'une seule fois sur le monitor
(https://i89.servimg.com/u/f89/17/56/35/17/p1040710.jpg) (https://servimg.com/view/17563517/8390)
(https://i89.servimg.com/u/f89/17/56/35/17/p1040711.jpg) (https://servimg.com/view/17563517/8391)

Pour que ce soit plus compréhensible, seul le programme de la gestion de l'écran et de la mesure de tension et présent dans le programme suivant.
Code: [Select]

#include "Arduino.h"
#include "heltec.h"   //use SSD1306
int Te=1000000  ; //periode d'echantillonnage micro seconde  1s

DRAM_ATTR  int Time;
DRAM_ATTR  float  tension;
DRAM_ATTR  float  courant=0.7;
DRAM_ATTR  float  temperature=35;
DRAM_ATTR  byte  heure=0;
DRAM_ATTR  byte  minute=0;
DRAM_ATTR  byte  seconde=0;
DRAM_ATTR  byte  capacity=200;
DRAM_ATTR  float  resist=0.15;
DRAM_ATTR  float  Y[127];          //declaration des tensions 
DRAM_ATTR byte  a=0;


byte i=0;
byte var=1;
bool flagenvoie=1;

hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
uint32_t cp0_regs[18];


void IRAM_ATTR onTimer() {      //IRAM et DRAM   routine d'interruption toutes les secondes
digitalWrite(33,HIGH);
  portENTER_CRITICAL_ISR(&timerMux);

xthal_set_cpenable(1);  xthal_save_cp0(cp0_regs);   // // Save FPU registers
//if ( digitalRead(33)== 1 ) {digitalWrite(33,LOW);}  else {digitalWrite(33,HIGH);}

//mesure toutes les secondes
tension=analogRead(36);
tension=(tension*3.3)/4096;     
//courant=courant-2048; // tension image du courant - ACSoffset
//courant=(courant*3.3)/4096; // Conversion en valeur analogique
//courant=courant/0.185;
resist=0.654;

//enregistrement toutes les minutes
seconde++;
if (seconde>=60) {seconde=0;                //60
minute++;
if (minute>127)   {minute=127;}
}//fin time>60
     
  xthal_restore_cp0(cp0_regs);  xthal_set_cpenable(0);  // and turn it back off
  portEXIT_CRITICAL_ISR(&timerMux);
  digitalWrite(33,LOW);
}//fin routine interrupt


 
void setup() {
pinMode(0, INPUT_PULLUP);   //bouton prg carte heltec ESP32

Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/);
Serial.begin(115200);
  Heltec.display->setFont(ArialMT_Plain_10);       //21 caracteres possible et 6 lignes
  Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT);
//  Heltec.display->display.setTextColor(WHITE);      // Draw white text
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, Te, true);     //200000 0.2s
  timerAlarmEnable(timer);
           
for (byte i = 0; i <= 127; i++) {     //tets initialisation des valeurs de la mesure tension
//tension=1.5-(0.5*i/120);
tension=-0.0000007*pow(i,3)+0.00008*pow(i,2)-0.005*i+1.3;
Y[i]=tension;                    }
 
}


 
void loop() {

if (digitalRead(0)==0)   {var++;delay(100); }    //utilisation du bouton prg de la carte ESP32 heltec
if (var>=3)   {var=1; }



//gestion de l'afficheur
switch (var) {
  case 1:        //affichage des données
Heltec.display->clear();
Heltec.display->drawString(0, 0, "U="+String(tension,2));   //max  21 chiffres   donc 6 points par lettres
Heltec.display->drawString(45,0, "I="+String(courant,2));
Heltec.display->drawString(80,0, "T="+String(temperature,0)+"°C");
Heltec.display->drawString(0,10, String(heure)+"h"+String(minute)+"m"+String(seconde)+"s");
Heltec.display->drawString(0,20, String(capacity)+"mah  R="+String(resist,3)  );     //   "Ω"
Heltec.display->drawString(0,30, "charge on");
Heltec.display->drawString(0,40, "T");
Heltec.display->drawString(0,50, "T");
Heltec.display->display();
flagenvoie=1;
    break;
 
 
 
  case 2:       //affichage curve

 if (flagenvoie==1)    {flagenvoie=0; Heltec.display->clear();
 for (byte i = 0; i <= 127; i++) {    // affichage de la courbe
 Heltec.display->setPixel(i, (94-Y[i]*63));   //a=(0-31.5)/(1.5V-1V)     b=-1.5*63=94
 Heltec.display->display();  }

for (byte i = 0; i <= 127; i++) {    // envoie la courbe de la mesure de tension sur le monitor
Serial.print( Y[i]);Serial.print(";");
 }
Serial.println(";");            }

 Heltec.display->drawHorizontalLine(0,63,127);  //(x,y,length)      //trace les axes
 Heltec.display->drawHorizontalLine(0,48,5);
  Heltec.display->drawHorizontalLine(0,32,5);
   Heltec.display->drawString(5, 27, "1V");     //ordonnées courbes de tension
  Heltec.display->drawHorizontalLine(0,16,5);
    Heltec.display->drawString(5, 42, "0.75V");   
 Heltec.display->drawVerticalLine(0,0,63);      //(x,y,length)
 Heltec.display->drawVerticalLine(15,58,5);
 Heltec.display->drawVerticalLine(30,58,5);
 Heltec.display->drawVerticalLine(45,58,5);
   Heltec.display->drawString(54, 48, "1h");
 Heltec.display->drawVerticalLine(60,58,5); 
  Heltec.display->drawVerticalLine(75,58,5);
   Heltec.display->drawVerticalLine(90,58,5);
    Heltec.display->drawVerticalLine(105,58,5);
    Heltec.display->drawVerticalLine(120,58,5);
     Heltec.display->drawString(116, 48, "2h");    //120 correspond à 120 minutes
     Heltec.display->display();

       
    break;  //case 2
}


} //fin loop



Évidemment, il est possible de tracer la tension en fonction de la capacité énergétique qui est plus représentative, que la tension en fonction du temps.
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Aug 02, 2020, 06:01 pm
Ayant remplacé les relais par des transistors M0SFET
D'ailleurs, j'ai testé la carte IRF540 avec optocoupleur
(https://i89.servimg.com/u/f89/17/56/35/17/a725.jpg) (https://servimg.com/view/17563517/8415)
(https://i89.servimg.com/u/f89/17/56/35/17/a623.jpg) (https://servimg.com/view/17563517/8416)
https://www.robotics.org.za/IRF540-MOD (http://)
IRF540  Rdson=0.07Ω  avec une résistance thermique sans dissipateur de 62.5°C/W
Imax=(TJ-Tab)/RTH*Rdson=(175-25)/(62.5*0.07)= 27A    
Ayant, besoin que de petit courant, je n'ai pas testé, l'échauffement du transistor.
Il faut l'alimenter pour une tension supérieure 5V car V threshold 2V à 4V pour avoir une saturation
Vgatesaturation mini=V threshold+Idrain/gfs=4+1A/8A/V=5V
Donc, il faut séparer l'alimentation de la commande est celle de la batterie, si l'on a une tension d'alimentation inferieure à 5V.
Donc pour remplacer les relais, cela fonctionne très bien

Puis, j'ai voulu programmé l'asservissement de décharge de courant avec une PWM à 20kHz, en 12 bits
pour une batterie en 12V.
Si le temps pour rendre passant  le transistor est de 1 µs, le temps de blocage du transistor est de 19µs. car la décharge de la capacité d'entrée du MOSFET, ce fait dans la résistance de 10KΩ, comme on peut l'observer sur la figure suivante
(https://i89.servimg.com/u/f89/17/56/35/17/img_2195.jpg) (https://servimg.com/view/17563517/8417)
l'0ptocoupleur peut supporter 50mA, donc la résistance de 10Kohms a été remplacée par 1KΩ donc un courant de 0.005A et le temps de blocage est passé à 5µs.
(https://i89.servimg.com/u/f89/17/56/35/17/img_2196.jpg) (https://servimg.com/view/17563517/8418)

Le programme de test de différentes sorties PWM sur ESP32 est très simple
Code: [Select]

//PWM     https://api.riot-os.org/group__boards__esp32__heltec-lora32-v2.html
const int ledPin22 = 22;       //32,2,17,22 ok   35,34,39 ne fonctionne pas     26 amplitude 1V ???
const int ledChannel = 2;      //de 0 à 15
ledcSetup(ledChannel, 20000, 12);           //ledcSetup(ledChannel, freq, resolution);
ledcAttachPin(ledPin22, ledChannel);        //ledcAttachPin(ledPin, ledChannel);
ledcWrite(ledChannel, 200);                    //ledcWrite(ledChannel, dutyCycle);



Pas facile d'avoir un shield compatible avec des tenions d'alimentation  de 5V jusque 100V
D'ailleurs, c'est grâce à la LED qui sature la tension de gate qu'il est possible d'avoir  des tensions d'alimentation de plus de 20V.
Par conséquent  pour faire de la PWM une alimentation à découpage  pour alimenter la gate serait préférable avec des  optocoupleurs genre Si8261.


Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: g2gaston on Aug 25, 2020, 11:12 am
J'ai voulu commandé la carte des transistors MOSFET précédente  avec un  PCF8574  mais cela ne marche pas ?
en plus, lorsque je fais la lecture des données sur le PCF8574, tout est à zero et losque je debranche la carte mosfet, la lecture fonctionne et au multimetre les tensions sont correctes.
Pourtant sous isis mon prgramme fonctionne
Pourriez-vous m'aider ?
Pas facile de reprendre votre programme de condition d'arrêt de charge de la batterie, il faudrait faire un fichier avec une table de condition via une fonction Case
J'ai appris plein de choses grâce à ce post et du coup j'ai réalisé aussi un chargeur avec votre programme
Je suis en train d'essayer de faire le programme avec une nano et 4 batteries a recharger en meme temps  en utilisant la liaison I2C.
merci d'avance
Title: Re: BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)
Post by: iutgeiisoissons on Aug 25, 2020, 07:45 pm
Avez-vous vous mis une résistance de pull up R2 de valeurs entre 820Ω à 470Ω entre le PCF8574 et l'optocoupleur ?
Comme sur la figure de simulation suivante
La valeur de la résistance R2 ne doit pas être trop importante, car il faut un courant pour saturer le transistor de l'optocoupleur PS2801.
En effet, suite au data sheet, PCF8574, celui-ci ne peut fournir qu'un courant de 0.1mA, insuffisant pour commander le PS2801.
https://www.ti.com/lit/ds/symlink/pcf8574.pdf?ts=1597942095825&ref_url=https%253A%252F%252Fwww.ti.com%252Fproduct%252FPCF8574 (http://)
http://jp79dsfr.free.fr/_Docs%20et%20infos/Elec%20_%20Arduino%20-%20Bus%20I2c.pdf (http://jp79dsfr.free.fr/_Docs%20et%20infos/Elec%20_%20Arduino%20-%20Bus%20I2c.pdf)
Par contre, PCF8574 peut supporter un courant rentrant de 25mA (10mA typique)
Donc, voici le schéma électrique et le programme que j'ai utilisé pour vérifier le bon fonctionnement PCF8574 avec la carte MOSFET.
Cela fonctionne, j'ai verifié.
(https://i89.servimg.com/u/f89/17/56/35/17/a2131.jpg) (https://servimg.com/view/17563517/8652)
Sur la figure précédente, il est vrai que la simulation ISIS allume la led alors que ce n'est pas possible en réalité.
La simulation d'ISIS a de nombreux petits bugs comme celui-ci.
Le programme qui ecrit toutes les 3 secondes sur les  2 PCF8574 et qui lit l'octet pour vérifier que l'écritrue se fait bien.
La led 13 est allumée et eteinte tous les 3 secondes pour verifier que le programme fonctionne bien
Code: [Select]

#include <Wire.h>     // // Join i2c bus
#include <LiquidCrystal_I2C.h>
#include "RTClib.h"      // Bibliothèque pour le module RTC

LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter
RTC_DS1307 rtc;   //minuscule    minuscule


#define led13   13
byte data;



DateTime datetime;

void setup()
{  // initialise l'afficheur LCD
pinMode(led13, OUTPUT);

 Wire.begin();
lcd.init();  //et pas lcd begin comme cetraine biblio
  lcd.display();     // activer l'affichage
  lcd.backlight();   // allumer retroeclairage
  lcd.setCursor(0,0);
  lcd.print("IUT GENIE elect&info");
  lcd.setCursor(5,1);
  lcd.print("Soissons");
  //Serial.begin(57600);

}




void loop()
{

if (digitalRead(13)== 1 )

{digitalWrite(13,LOW);  
Wire.beginTransmission(0x20);   // transmit to device #20  PCF8574  A0=0, A1=0, A2=0
Wire.write(00);                  // sends value byte
Wire.endTransmission();         // stop transmitting

Wire.beginTransmission(0x21);   // transmit to device #21  PCF8574  A0=1, A1=0, A2=0
Wire.write(0b10101010);         // sends value byte
Wire.endTransmission();

}


else {digitalWrite(13,HIGH);
Wire.beginTransmission(0x20);   // transmit to device #20  PCF8574  A0=0, A1=0, A2=0
Wire.write(03);                  // sends value byte
Wire.endTransmission();         // stop transmitting

Wire.beginTransmission(0x21);
Wire.write(0b01010101);         // sends value byte
Wire.endTransmission();
}  

 Wire.requestFrom (0x20, 1);   //lecture information
 if (Wire.available ()) {
   data = Wire.read ();  }
  lcd.setCursor(0,2);
  lcd.print(data);
  lcd.print("=adresse20  ");

   Wire.requestFrom (0x21, 1);   //lecture information
 if (Wire.available ()) {
   data = Wire.read ();  }
  lcd.setCursor(0,3);
  lcd.print(data);
  lcd.print("=adresse21  ");
delay(3000);
}


Les fabricants de shields pourraient mettre cette résistance de pull up sur leur carte PCF8574!
je ne sais pas pourquoi il ne le font pas. Je me serais fait avoir aussi dans un premier temps.
(https://i89.servimg.com/u/f89/17/56/35/17/a391.jpg) (https://servimg.com/view/17563517/8653)