/*
Projet IRP : SoC d'une batterie par Bluetooth
M1 ESET - Universite Toulouse 3 Paul Sabatier
*********************************************
IRP Project: Battery SoC via Bluetooth
M1 ESET - Toulouse 3 Paul Sabatier University

Anna Ernst
Thomas Dehee
Alexandre Barret

Encadre par / Supervised by Vincent Boitier
*/

#include "BluetoothSerial.h"
#include "esp_adc_cal.h"
#include "EEPROM.h"

#define tension_pin 13
#define courant_pin 14
#define batterie_em 15
#define led_pin 2
#define compa_bas 23
#define compa_haut 22

//Resistances pour ponts diviseurs de tension
//Tension batterie fauteuil
#define R1 99080.0
#define R2 9500.0
//Tension image courant batterie fauteuil
#define R3 4797.0
#define R4 9580.0
//Tension batterie systeme
#define R5 2170.0
#define R6 9590.0

//Nombre de mesures a faire pour moyenne
#define NbMesure 10
#define IntervalleMesure 5 //5 secondes entre chaque mesure
#define Q 12 //Ah
#define Size_TabCourant 20
#define offset 0.0
float TabCourant[Size_TabCourant];

// Vérifier si les configurations Bluetooth sont activées
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run make menuconfig to and enable it
#endif

// Bluetooth Serial object
BluetoothSerial SerialBT;

// Gérer les messages reçus et envoyés
String message = "";

int pourcentage_em = 0; //SoC systeme embarque

float tableau[1000];
float SOC=1;
int indice = 0;    //indice pour repérer la position dans le tableau de courant
unsigned long previousMillis = 0;

//Parametre memoire EEPROM ESP
int addr_EEPROM = 0;
#define EEPROM_SIZE 1

void setup() {
  Serial.begin(115200);
  // nom de l'appareil Bluetooth
  SerialBT.begin("ChargeMate");
  Serial.println("ChargeMate pret a etre connecte au Bluetooth");
  
  //régler la résolution sur 12 bits (0-4096)
  analogReadResolution(12);

  Serial.println("start...");
  if (!EEPROM.begin(EEPROM_SIZE))
  {
    //Echec de EEPROM
    Serial.println("Echec de l'initialisation de EEPROM");
    delay(1000000);
  }
  Serial.println("Lecture depuis EEPROM : ");
  Serial.print(EEPROM.read(addr_EEPROM));
  Serial.println();
  //Affectation de la valeur stockée dans EEPROM dans SOC
  SOC = float(EEPROM.read(addr_EEPROM));

}

//Calcul valeur moyenne d'un tableau
void moyenne_tab(float tab[], int taille, float * moyenne) {
  int i;
  for(i=0;i<taille;i++) {
    *moyenne += tab[i];
  }
  *moyenne /= taille;
}

//Faire la lecture en mV du pin correspondant
float ADC_Volts(int ADC_pin) {
  int ADC_raw = analogRead(ADC_pin);
  esp_adc_cal_characteristics_t ADC_chars;
  esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &ADC_chars);
  return(esp_adc_cal_raw_to_voltage(ADC_raw, &ADC_chars));
}

//Moyenne de plusieur mesure de courant pour avoir une valeur stable
float mesure_moyennee(int pin) {
  float * tableau = (float*)malloc(NbMesure * sizeof(float));
  float mesure_instant, moyenne;
  int i;
  if(tableau == NULL) {
    Serial.println("Erreur : memoire non allouee pour mesure_moyenne");
    moyenne = 0.0;    
  }
  else {
      for(i=0;i<NbMesure;i++) {
        tableau[i] = ADC_Volts(pin);
        delay(10); //Défini la fréquence d'échantillonnage à 100Hz
        //Serial.println(i);     
      }
      moyenne_tab(tableau, NbMesure, &moyenne);
  }
  free(tableau);
  return (moyenne/1000) - offset;
}

//Sotckage des courants moyen dans un tableau de courant à intervalle de 5s
void curr_meas_stor(float *tab){
  //check si t>5s... Si oui tab[indice]=mesuremoyenne(pin)
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis >= IntervalleMesure*1000){
    previousMillis = currentMillis;
    tab[indice] = (mesure_moyennee(courant_pin)*((R3+R4)/R4) - 2.5) * 10;  
    indice++;
  }
}

//Calcul SoC de la batterie chariot
void State_of_charge(){
  int t = 0, i;
  float inte_ech = 0;
  for(i = 0; i < indice; i++){ //somme des intregrales par echantillons
    inte_ech = inte_ech + (((t+IntervalleMesure) - t) * (TabCourant[i] + TabCourant[i+1]) / 2); //integration methode des trapezes
    t += IntervalleMesure;
    Serial.print("val tableau :");
    Serial.println(TabCourant[i]);
    Serial.print("val integrale :");    
    Serial.println(inte_ech);
  }
  //Calcul SoC
  SOC = SOC - ((inte_ech/(Q * 3600 * IntervalleMesure * Size_TabCourant)) * 100);  

  //sauvegarde du nouveau SoC dans EEPROM
  EEPROM.write(addr_EEPROM, SOC);
  EEPROM.commit();
  Serial.println("Sauvegarde SoC dans EEPROM");
}

//remise a zero du tableau de courant
void RAZ_TAB(float* tab){ 
  int i;
  for(i=0; i<Size_TabCourant; i++){
    tab[i]=0;
  }  
}

void loop() {

  float tension_moyenne = 0.0, SOCf;
  
  // Lire les messages reçus (commande)
  if (SerialBT.available()){
    char incoming_char = SerialBT.read();
    if (incoming_char != '\n'){
      message += String(incoming_char);
    }
    else{
      message = "";
    }
    Serial.write(incoming_char);  
  }

  // Vérifier le message reçu et contrôler la sortie en conséquence
  if (message == "data") {
    String donnees = ""; //chaine de caractere contenant les 3 donnees
    //SoC
    donnees += SOC;
    donnees += ":";
    //Tension
    donnees += (mesure_moyennee(tension_pin)*((R1+R2)/R2));
    donnees += ":";
    //Pourcentage systeme embarque
    tension_moyenne = mesure_moyennee(batterie_em)*((R5+R6)/R5);
    if(tension_moyenne <= 3.5) {
      pourcentage_em = 141.79 * tension_moyenne - 491.27; //determination du pourcentage avec la tension
    }
    else if(tension_moyenne > 3.5) {
      pourcentage_em = 100;
    }
    else {
      pourcentage_em = 0;
    }
    donnees += pourcentage_em;

    
    SerialBT.print(donnees);
    
    //Affiche les logs
    Serial.println("requete data");
    Serial.println(donnees);
    Serial.print("Tension batterie = ");
    Serial.println(mesure_moyennee(tension_pin)*((R1+R2)/R2));
    Serial.print("Tension batterie embarquee = ");
    Serial.println(mesure_moyennee(batterie_em)*((R5+R6)/R5));
    Serial.print("Tension pin courant = ");
    Serial.println(mesure_moyennee(courant_pin));

    message = "";
  }
  else if (message == "reset") {
    
    //Soc a 100%
    SOC = 100;
    EEPROM.write(addr_EEPROM, SOC);
    EEPROM.commit();
    Serial.println("Sauvegarde SoC dans EEPROM");

    Serial.println("Lecture depuis EEPROM : ");
    Serial.print(EEPROM.read(addr_EEPROM));

    
    message = "";
  }
  
  delay(20); //temporisation entre les 2 taches

  if(digitalRead(compa_bas) == LOW) {
    Serial.println("Seuil Bas");
    curr_meas_stor(TabCourant);
    if(indice >= Size_TabCourant - 1){
      State_of_charge();    //courant negatif on charge la batterie
      indice = 0;
      RAZ_TAB(TabCourant);
      Serial.println(SOC);
      Serial.println("--------");
    }
  }
  if(digitalRead(compa_haut) == HIGH) {
    Serial.println("Seuil Haut");
    curr_meas_stor(TabCourant);
    if(indice >= Size_TabCourant - 1){
      State_of_charge();     //courant positif on décharge la batterie
      indice = 0;
      Serial.println(SOC);
      Serial.println("--------");      
    }
  }
  delay(100); //temporisation avant rebouclage
}