//********************************************************************************************************************************************************************************** // Fichier BATTERIES.C // // Contient toutes les fonctions liées aux batteries de ShipHeart //**********************************************************************************************************************************************************************************/ //#define __DEBUG_BATTERIES // Includes : #include "main.h" // Prototypes : int8_t init_batteries(void); void gestion_batteries(void); int16_t batterie_cle_primaire(uint16_t cle_primaire); int16_t variable_batterie_cle_primaire(uint16_t cle_primaire); // Variables globales : struct Struct_batteries batteries; /********************************************************************************************************************************************************************************** Fonction : Initialise les différents paramètres des batteries au démarrage de l'application Reçoit : Rien Retour : -1 si erreur, 0 sinon. **********************************************************************************************************************************************************************************/ int8_t init_batteries(void) { //------------------------------------------------------------------------------------------------------ // Init des variables générales : batteries.sequenceur.etat = ETAT_SEQUENCEUR_BATTERIES_INIT; batteries.sequenceur.compteur = BATTERIES_TEMPO_INIT; // Affichage du message de débug : affichage_debug(__FILE__, __LINE__, "Initialisation des batteries\t: OK", TYPE_MESSAGE_DEBUG_OK); return(0); } /********************************************************************************************************************************************************************************** Fonction : Fonction de plus haut niveau de gestion batteries Reçoit : Rien Retour : Rien **********************************************************************************************************************************************************************************/ void gestion_batteries(void) { uint16_t i, j; float soc; float tension_a_vide; float resistance_interne; float efficacite; float delta_energie; float capacite_reelle; float duree_decharge_corrigee; float courant_differentiel_corrige; bool flag_erreur; char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; if (!systeme.flag_cm3p_ready) return; // Evite que le traitement soit fait au milieu d'une reconfiguration... switch(batteries.sequenceur.etat) { //-------------------------------------------------------- case ETAT_SEQUENCEUR_BATTERIES_INIT : { if ((systeme.flag_cm3p_ready) && // Permet de s'assurer que le cycle d'init système est terminé. En particulier, on est sûr que 'init_bdd()' puis 'annuaire_k_bus()' ont été traités. (batteries.sequenceur.compteur == 0)) { affichage_debug(__FILE__, __LINE__, "------------------BATTEREIES INIT", TYPE_MESSAGE_DEBUG_WARNING); batteries.sequenceur.etat = ETAT_SEQUENCEUR_BATTERIES_REPOS; } break; } //-------------------------------------------------------- case ETAT_SEQUENCEUR_BATTERIES_REPOS : { if (batteries.sequenceur.compteur == 0) { batteries.sequenceur.compteur = BATTERIES_PERIODE_MESURES; #ifdef __DEBUG_BATTERIES batteries.sequenceur.compteur = 10; // Valeur passée de 300ms à 1s pour les tests (cf batteries.h) #endif for (i = 0; i < batteries.nb; i ++) { if (batteries.batteries[i].flag_validite == FALSE) continue; flag_erreur = FALSE; // Acquisition des dernières valeurs et mémorisation du nouveau niveau : switch(batteries.batteries[i].type_entrees) { //-------------------------------------------------------------------------- case TYPE_ENTREES_VOLTAGE : { // Récupération de l'état de l'entrées Voltage configurée sur cette batterie et calcul de la nouvelle moyenne de tension à partir de l'ancienne et de la valeur lue : batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante = (((batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante * (batteries.batteries[i].tension_coeff_filtrage - 1))) + entrees.volt.entrees[batteries.batteries[i].no_entrees].valeur) / batteries.batteries[i].tension_coeff_filtrage; break; } //-------------------------------------------------------------------------- case TYPE_ENTREES_PREDEFINIES : { // Récupération de l'état de l'entrées Prédéfinie configurée sur cette batterie et calcul de la nouvelle moyenne de tension à partir de l'ancienne et de la valeur lue : batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante = (((batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante * (batteries.batteries[i].tension_coeff_filtrage - 1))) + entrees.predefinies.entrees[batteries.batteries[i].no_entrees].valeur) / batteries.batteries[i].tension_coeff_filtrage; break; } //-------------------------------------------------------------------------- case TYPE_ENTREES_IBS : { break; } default : { sprintf(tmp, "Le 'Type_entrées' de la batterie #%i est invalide [%i]. La batterie est dévalidée", (i+1), batteries.batteries[i].type_entrees); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); batteries.batteries[i].flag_validite = FALSE; flag_erreur = TRUE; break; } } // Traitement : if (flag_erreur == FALSE) { //------------------------------------- // Tension : //------------------------------------- // Calcul de l'impact de Gain_tension sur la tension courante : batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante *= batteries.batteries[i].tension_gain; // Mise à jour du cumul : batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante; //------------------------------------- // Courant de décharge : //------------------------------------- batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante = 0; for (j = 0; j < circuits.nb; j ++) { if (! circuits.circuits[j].flag_validite) continue; if (circuits.circuits[j].id_batterie != batteries.batteries[i].cle_primaire) continue; batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante -= circuits.circuits[j].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante; // Cumul du courant dans chaque circuit. En négatif car le courant de chaque circuit est comptabilisé de façon positive alors que le courant de décharge est forcément négatif. } // Mise à jour du cumul : batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante; //------------------------------------- // Puissance : //------------------------------------- // SDIR #199 : suppression du (-1) dans le produit (tension x courant) afin de laisser la puissance consommée toujours négative : batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante * batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante; // Mise à jour du cumul : batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante; //================================================ // Entrée de type Entrée Prédéfinie ou Voltage : //================================================ if (batteries.batteries[i].type_entrees != TYPE_ENTREES_IBS) { //------------------------------------- // SOC : //------------------------------------- // Calcul du SOC : soc = calcul_niveau(batteries.batteries[i].points_tension_soc, MAX_NOMBRE_POINTS_TENSION_SOC_BATTERIE, batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante); if (soc >= 0) batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = soc; else { sprintf(tmp, "Erreur de calcul de SOC de la batterie #%i avec tension=%.3fV. La valeur lue n'est pas exploitée.\n", i, batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); } // Mise à jour du cumul : batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante; //------------------------------------- // Mise à jour BDD : //------------------------------------- // Mise à jour dans la BDD si les valeurs ont suffisemment évolué : // if ((fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_TENSION].marge) || // (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_COURANT_DECHARGE].marge) || // (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_SOC].marge) || // (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_PUISSANCE].marge)) if ((fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante) > 0) || (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante) > 0) || (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante) > 0) || (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante) > 0)) { batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD } /*#ifdef __DEBUG_BATTERIES sprintf(tmp, "Batterie #%i : Tension = %.3fV, courant de décharge = %.3fA (donc puissance = %.3fW) et SOC = %.2f%% (coeff filtrage tension = %i)", i, batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante, (batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante * (-1) * batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante), batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante, batteries.batteries[i].tension_coeff_filtrage); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif */ } //================================================ // Entrée de type IBS : //================================================ else { //------------------------------------- // Température : //------------------------------------- // Mise à jour du cumul : batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante; //------------------------------------- // Courant différentiel : //------------------------------------- // Mise à jour du cumul : batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante; //------------------------------------- // Calcul du courant de charge : //------------------------------------- batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante; if (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante < 0) // Normalement, impossible car cela induirait un courantd e charge négatif... { #ifdef __DEBUG_BATTERIES // sprintf(tmp, "Batterie #%i : Le courant de charge est calculé avec une valeur négative [%.3f A], ce qui n'est pas possible. Il est ramené à 0 mA", i, batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante); // affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); #endif batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante = 0; // Par défaut, on écrête... } // Mise à jour du cumul : batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante; //------------------------------------- // Calcul du SOC : //------------------------------------- // Calcul de la résistance interne en fonction de la température : resistance_interne = calcul_niveau(batteries.batteries[i].points_temperature_resistance_interne, MAX_NOMBRE_POINTS_TEMPERATURE_RESISTANCE_INTERNE_BATTERIE, batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante); if (resistance_interne != -1) resistance_interne = resistance_interne * batteries.batteries[i].resistance_interne / 1000; // Le '/1000' permet de passer de valeurs de résistance interne en mOhms à Ohms. else resistance_interne = batteries.batteries[i].resistance_interne / 1000; // Le '/1000' permet de passer de valeurs de résistance interne en mOhms à Ohms. #ifdef __DEBUG_BATTERIES // sprintf(tmp, "Valeur brute de résistance interne = %.2f mOhms, température batterie = %.2f °C, soit résistance interne corrigée = %.2f mOhms", batteries.batteries[i].resistance_interne, batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante, (resistance_interne * 1000)); // affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif // Estimation de la tension à vide (en tenant compte de la chute de tension due à la résistance interne de la batterie) : tension_a_vide = batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante - (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante * resistance_interne); if ((batteries.batteries[i].sequenceur.etat != ETAT_SEQUENCEUR_BATTERIE_CHARGE_NULLE) && // Le calcul de l'EOD, et, in fine, du SOC n'est pas effectué tant qu'on est à l'état de charge nulle... (batteries.batteries[i].sequenceur.etat != ETAT_SEQUENCEUR_BATTERIE_PLEINE_CHARGE)) // ... ou de pleine charge. { // Calcul de l'EOD : // Calculs fonction d'une charge ou d'une décharge : if (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante < 0) // Il est important de ne PAS prendre la valeur 0 (sinon le calcul de 'duree_decharge_corrigee' va planter via division par 0) { efficacite = 1 / batteries.batteries[i].efficacite_decharge; // Effet Peukert : duree_decharge_corrigee = batteries.batteries[i].heures_capacite * powf(((batteries.batteries[i].capacite / batteries.batteries[i].heures_capacite) / (-batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante)), batteries.batteries[i].coeff_peukert); // Le signe - devant le courant permet d'avoir une valeur positive (sinon POWF va générer une erreur) courant_differentiel_corrige = -(float)batteries.batteries[i].capacite / duree_decharge_corrigee; // Le signe '-' permet de revenir à un courant négatif en cas de décharge } else { efficacite = batteries.batteries[i].efficacite_charge; courant_differentiel_corrige = batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante; // Pas de correction de Peukert en charge... } // Calculs intermédiaires (facilite la lecture et le debug) : delta_energie = courant_differentiel_corrige * efficacite * (BATTERIES_PERIODE_MESURES * 0.1 / 3600); // La dernière () permet de passer en heures. // SDIR #337 : capacite_reelle = batteries.batteries[i].capacite * batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante / 100; // Le '100' permet de tenir compte du fait que le SOH est en pourcents capacite_reelle = batteries.batteries[i].capacite; // SDIR #337 // Calcul de l'EOD proprement dit : batteries.batteries[i].eod += delta_energie; if (batteries.batteries[i].eod > 0) batteries.batteries[i].eod = 0; // SDIR #337 //printf("Courant diff = %.3f, efficacité = %.3f, Delta T = %.6f secondes donc Delta Energie = %.6fAh => energie_dechargee = %.6fAh. Capacite_reelle = %.2fAh\n", batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante, efficacite, (BATTERIES_PERIODE_MESURES * 0.1 / 3600), delta_energie, batteries.batteries[i].eod, capacite_reelle); // Mise à jour du SOC à partir de l'EOD : batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = (1 + (batteries.batteries[i].eod / capacite_reelle)) * 100; // EOD est négative par nature. Le 'x100' permet de revenir d'une fraction en pourcents } else { efficacite = 0; // Permet d'éviter un warning losqu'il n'est pas calculé (état de pleine charge ou de charge nulle) mais utilisé par le message de debug courant_differentiel_corrige = 0; // Idem } //-------------------------------- // Mode repos : //-------------------------------- /* if (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante) < batteries.batteries[i].courant_repos) { soc = calcul_niveau(batteries.batteries[i].points_tension_soc, MAX_NOMBRE_POINTS_TENSION_SOC_BATTERIE, tension_a_vide); if (soc >= 0) { // batteries.batteries[i].soc_coeff_filtrage = 50; // float old = batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante; // float new; // new = moyenne_mobile(old, soc, batteries.batteries[i].soc_coeff_filtrage); // sprintf(tmp, "Old = %.3f%%, Add=%.3f%%, New = %.3f%% et le coeff %i. ", old, soc, new, batteries.batteries[i].soc_coeff_filtrage); // affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = moyenne_mobile(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante, soc, batteries.batteries[i].soc_coeff_filtrage); sprintf(tmp, "La batterie #%i est en mode REPOS. Son nouveau SOC = %.1f%%, il a été mis à jour via moyenne mobile avec la nouvelle valeur %.1f%% et le coeff %i. ", i, batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante, soc, batteries.batteries[i].soc_coeff_filtrage); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); } // Sinon, on n'utilise pas la valeur calculée car il ne sert à rien de corriger avec une valeur erronée... } */ //-------------------------------- // Mode Décharge : //-------------------------------- if (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante < 0) { // Vérification du passage en "charge nulle" : switch(batteries.batteries[i].sequenceur.etat) { //...................................... case ETAT_SEQUENCEUR_BATTERIE_NORMAL: { if (tension_a_vide <= batteries.batteries[i].tension_charge_nulle) { batteries.batteries[i].sequenceur.compteur = batteries.batteries[i].temporisation_charge_nulle; batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_CHARGE_NULLE; } break; } //...................................... case ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_CHARGE_NULLE: { if (tension_a_vide > batteries.batteries[i].tension_charge_nulle) // Cas où on n'a pas eu le temps d'attendre la tempo de confirmation avant remontée de la tension. { batteries.batteries[i].sequenceur.compteur = 0; batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; break; } if (batteries.batteries[i].sequenceur.compteur == 0) // Cas où le passage à charge nulle est confirmé : { batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = 0; batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante = -(batteries.batteries[i].eod * 100) / batteries.batteries[i].capacite; sprintf(tmp, "La Batterie [%i] est confirmée en CHARGE NULLE. EOD = %.3fAh et Capacité = %.2fAh donc SOH = %.3f%%", (i+1), batteries.batteries[i].eod, batteries.batteries[i].capacite, batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_CHARGE_NULLE; } break; } //...................................... case ETAT_SEQUENCEUR_BATTERIE_CHARGE_NULLE: { if (tension_a_vide > (batteries.batteries[i].tension_charge_nulle + BATTERIES_HYSTERESIS_CHARGE_NULLE)) { batteries.batteries[i].sequenceur.compteur = 0; batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; break; } break; } //...................................... default : { #ifdef __DEBUG_BATTERIES sprintf(tmp, "L'état du séquenceur de la Batterie [%i] est inconnu pour ce mode [%i]. Il est repositionné à NORMAL", (i+1), batteries.batteries[i].sequenceur.etat); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); #endif batteries.batteries[i].sequenceur.compteur = 0; batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; break; } } } //-------------------------------- // Mode Charge : //-------------------------------- if (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante > 0) { switch(batteries.batteries[i].sequenceur.etat) { //...................................... case ETAT_SEQUENCEUR_BATTERIE_NORMAL: { // Détection d'un passage à l'état "Pleine charge" : if ((tension_a_vide >= batteries.batteries[i].tension_pleine_charge) && (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante <= batteries.batteries[i].courant_pleine_charge)) { #ifdef __DEBUG_BATTERIES sprintf(tmp, "Les conditions du mode PLEINE CHARGE ont été détectées sur la Batterie [%i]. La temporisation de vérification (%is) est lancée ", (i+1), batteries.batteries[i].temporisation_pleine_charge); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif batteries.batteries[i].sequenceur.compteur = batteries.batteries[i].temporisation_pleine_charge; batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_PLEINE_CHARGE; } break; } //...................................... case ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_PLEINE_CHARGE: { // Détection de sortie de l'état "Pleine charge" avant d'avoir été confirmé : if ((tension_a_vide < batteries.batteries[i].tension_pleine_charge) || // Si la tension est redescendue... (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante > batteries.batteries[i].courant_pleine_charge)) // ... ou le courant remonté avant l'expiration de la tempo : { sprintf(tmp, "Les conditions du mode PLEINE CHARGE ont disparu sur la Batterie [%i]. La batterie est repositionnée en mode NORMAL ", (i+1)); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); batteries.batteries[i].sequenceur.compteur = 0; batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; break; } // Confirmation du passage à l'état "Pleine charge" : if (batteries.batteries[i].sequenceur.compteur == 0) // Cas où le passage à pleine charge est confirmé : { sprintf(tmp, "La Batterie [%i] est confirmée en PLEINE CHARGE ", (i+1)); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = 100; batteries.batteries[i].eod = 0; // batteries.batteries[i].tension_pleine_charge = tension_a_vide - BATTERIES_DELTA_TENSION_PLEINE_CHARGE_AUTO; // Ajustement auto de la tension pleine charge batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_PLEINE_CHARGE; } break; } //...................................... case ETAT_SEQUENCEUR_BATTERIE_PLEINE_CHARGE: { // Détection de sortie de l'état "Pleine charge" : if ((tension_a_vide < batteries.batteries[i].tension_pleine_charge) || // Si la tension est redescendue... (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante > batteries.batteries[i].courant_pleine_charge)) // ... ou le courant remonté : { batteries.batteries[i].sequenceur.compteur = 0; batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; break; } // Etat "pleine charge" : // batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = 100; // batteries.batteries[i].eod = 0; break; } //...................................... default : { #ifdef __DEBUG_BATTERIES sprintf(tmp, "L'état du séquenceur de la Batterie [%i] est inconnu pour ce mode [%i]. Il est repositionné à NORMAL", (i+1), batteries.batteries[i].sequenceur.etat); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); #endif batteries.batteries[i].sequenceur.compteur = 0; batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; break; } } } // Vérification du SOC : if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante > 100) batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = 100; if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante < 0) batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = 0; // Mise à jour du cumul : batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante; //-------------------------------------------- // Calcul des nombres de cycles : //-------------------------------------------- if (batteries.batteries[i].flag_increment_cycles == 0) // Le compteur est armé et n'a pas encore été incrémenté : { if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante <= BATTERIES_SEUIL_SOC_BAS_NOMBRE_CYCLES) { batteries.batteries[i].flag_increment_cycles = 1; // Blocage du flag jusqu'au prochain passage par 100% batteries.batteries[i].nombre_cycles ++; // Incrémentation du compteur batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD } } else // Le compteur "Nombre_cylces" a été incrémenté, on attend maintenant de revenir à 100% pour le réarmer : { if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante == 100) { batteries.batteries[i].flag_increment_cycles = 0; // Réarmement du flag pour le prochain cycle batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD } } //-------------------------------------------- // Calcul du nombre de décharges complètes : //-------------------------------------------- if (batteries.batteries[i].flag_increment_decharges_completes == 0) // Le compteur est armé et n'a pas encore été incrémenté : { if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante == 0) { batteries.batteries[i].flag_increment_decharges_completes = 1; // Blocage du flag jusqu'au prochain passage par 100% batteries.batteries[i].nombre_decharges_completes ++; // Incrémentation du compteur batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD } } else // Le compteur "Nombre_cylces" a été incrémenté, on attend maintenant de revenir à 100% pour le réarmer : { if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante == 100) { batteries.batteries[i].flag_increment_decharges_completes = 0; // Réarmement du flag pour le prochain cycle batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD } } //------------------------------------- // Calcul du SOH : //------------------------------------- // Vérification du SOH : // if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante > 100) batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante = 100; if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante < BATTERIES_SEUIL_MINI_SOH) batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante = BATTERIES_SEUIL_MINI_SOH; // Mise à jour du cumul : batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante; //------------------------------------- // Calcul de l'autonomie : //------------------------------------- // Mise à jour du cumul : batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_courante; //------------------------------------- // Mise à jour BDD : //------------------------------------- // Mise à jour dans la BDD si les valeurs ont suffisemment évolué : // if ((fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_TEMPERATURE].marge) || // (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_TENSION].marge) || // (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].marge) || // (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_COURANT_CHARGE].marge) || // (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_COURANT_DECHARGE].marge) || // (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_SOH].marge) || // (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_SOC].marge) || // (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_AUTONOMIE].marge) || // (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_PUISSANCE].marge)) if ((fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante) > 0) || (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante) > 0) || (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante) > 0) || (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante) > 0) || (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante) > 0) || (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante) > 0) || (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante) > 0) || (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_courante) > 0) || (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante) > 0)) { batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD } #ifdef __DEBUG_BATTERIES char texte_etat[200]; if (++ batteries.compteur_affichage_debug == 6) // Comme BATTERIES_PERIODE_MESURES = 300ms, cela donne un affichage toutes les 1.8s { batteries.compteur_affichage_debug = 0; switch(batteries.batteries[i].sequenceur.etat) { case ETAT_SEQUENCEUR_BATTERIE_NORMAL: { strcpy(texte_etat, "NORMAL"); break; } case ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_CHARGE_NULLE: { strcpy(texte_etat, "ATTENTE_CONFIRMATION_CHARGE_NULLE"); break; } case ETAT_SEQUENCEUR_BATTERIE_CHARGE_NULLE: { strcpy(texte_etat, "CHARGE_NULLE"); break; } case ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_PLEINE_CHARGE: { strcpy(texte_etat, "ATTENTE_CONFIRMATION_PLEINE_CHARGE"); break; } case ETAT_SEQUENCEUR_BATTERIE_PLEINE_CHARGE: { strcpy(texte_etat, "PLEINE_CHARGE"); break; } default: { strcpy(texte_etat, "Inconnu"); break; } } sprintf(tmp, "Batterie #%i : Etat = %s, Température = %.2f°C, Tension = %.3fV, Courant diff = %.3fA, courant charge = %.3fA, Courant décharge = %.3fA, SOH = %.2f%%, SOC = %.2f%%, Autonomie = %.2fh et puissance = %.3fW. Tension à vide = %.3fV, résistance interne corrigée = %.2fmOhms, efficacité = %.3f, courant_differentiel_corrigé = %.3fA, capacité = %.2fAh, EOD (énergie déchargée depuis démarrage) = %.5fAh", i, texte_etat, batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante, tension_a_vide, (resistance_interne * 1000), // Pour passer de Ohms à mOhms efficacite, courant_differentiel_corrige, batteries.batteries[i].capacite, batteries.batteries[i].eod ); /*sprintf(tmp, "Batterie #%i : Etat = %s, Tension = %.3fV, Courant diff = %.3fA. Tension à vide = %.3fV, efficacité = %.3f, courant_differentiel_corrigé = %.3fA, capacité = %.2fAh, duree_decharge_corrigee = %.2fh, EOD = %.5fAh", i, texte_etat, batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante, tension_a_vide, efficacite, courant_differentiel_corrige, batteries.batteries[i].capacite, duree_decharge_corrigee, batteries.batteries[i].eod ); */ affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); } #endif } // Fin du traitement pour batterie sur IBS batteries.batteries[i].cumuls_nb_val ++; // MàJ du nombre d'échantillons dans le cumul // Mise à jour des valeurs BDD des variables : if (batteries.batteries[i].flag_dde_maj_bdd) { for (j = 0; j < MAX_NOMBRE_VARIABLES_BATTERIES; j ++) batteries.batteries[i].variables[j].valeur_bdd = batteries.batteries[i].variables[j].valeur_courante; } } } } break; } //-------------------------------------------------------- default : { sprintf(tmp, "L'état du séquenceur Batteries est inconnu [%i]. Il est repositionné au REPOS", batteries.sequenceur.etat); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); batteries.sequenceur.etat = ETAT_SEQUENCEUR_BATTERIES_REPOS; break; } } } //********************************************************************************************************************************************************************************** // Fonction : Recherche la batterie dont la Clé_primaire est reçue en paramètre // Reçoit : La Cle_primaire de la batterie à rechercher // Retourne : le numéro de batterie correspondant (ou -1 si aucun circuit valide ne correspond). La numérotation allant de 0 à MAX_NOMBRE_BATTERIES - 1. //********************************************************************************************************************************************************************************** int16_t batterie_cle_primaire(uint16_t cle_primaire) { uint16_t i; if (cle_primaire == 0) return (-1); // Inutile de chercher si le n° à chercher est nul... for (i = 0; i < batteries.nb; i ++) { if (batteries.batteries[i].flag_validite == FALSE) continue; if (batteries.batteries[i].cle_primaire == cle_primaire) return(i); } return (-1); // Si on est arrivé au bout de la boucle, on renvoi le code de 'non trouvé'. } //********************************************************************************************************************************************************************************** // Fonction VARIABLE_BATTERIE_CLE_PRIMAIRE // Reçoit en paramètre : // - La Cle_primaire de la variable à rechercher // Retourne : // - le numéro de variable correspondant (ou -1 si aucune variable valide ne correspond). La numérotation allant de 0 à MAX_NOMBRE_VARIABLES_BATTERIES-1. //********************************************************************************************************************************************************************************** int16_t variable_batterie_cle_primaire(uint16_t cle_primaire) { uint16_t i; for (i=0; i