mbed-os github

Dependencies:   ADS1015 Faulhaber HTU21D_mod MS5837_potless Sensor_Head_RevB_3 USBDevice_dfu Utilsdfu beep

Fork of ARNSRS_testDFU by POTLESS

main.cpp

Committer:
POTLESS_2
Date:
2018-03-08
Revision:
36:1f5e2247b073
Parent:
35:a209a192f431
Child:
37:f9461c6592f2

File content as of revision 36:1f5e2247b073:

#include "mbed.h"
#include <string>
#include "Sensor_head_revB.h"
#include "HTU21D.h"
#include "PID.h"
#include "Faulhaber.h"
#include "Utils.h"

//Commandes  des servos
#define PWM_SERVO_POUMON PB_15
#define nSleep_SERVO_POUMON PC_6
#define FWD_SERVO_POUMON PB_14
#define REV_SERVO_POUMON PB_13
#define Channel_A_SERVO_POUMON PB_1
#define Channel_B_SERVO_POUMON PB_2
#define HOME_SERVO_POUMON 0

#define PWM_SERVO_FUITE PB_10
#define nSleep_SERVO_FUITE PC_5
#define FWD_SERVO_FUITE PB_4
#define REV_SERVO_FUITE PB_5
#define Channel_A_SERVO_FUITE PB_8
#define Channel_B_SERVO_FUITE PB_9
#define HOME_SERVO_FUITE 90

//Ecrit dans le moniteur série de la tablette à 115200 bds si sur 1, penser à mettre NEED_CONSOLE_OUTPUT à 0
#define NEED_ANDROID_OUTPUT 1

//Mode PID, STD à commenter / décommenter
#define STD_MODE
//#define PID_MODE

#ifdef STD_MODE
int MODE_FLAG = 0;
#endif

#ifdef PID_MODE
int MODE_FLAG = 2;
#endif

#if NEED_ANDROID_OUTPUT
#define ANDROID(...) { android.printf(__VA_ARGS__); }
#else
#define ANDROID(...)
#endif

#if DEBUG_MODE
#define DEBUG(...) { serialMonit.printf(__VA_ARGS__); fflush(stdout);}
#else
#define DEBUG(...)
#endif


//PinName pwm, PinName nSleep, PinName fwd, PinName rev, PinName channelA, PinName channelB, int pulsesPerRev, int Rapport, Encoding encoding = X2_ENCODING
Faulhaber Servo_Poumon("Servo_Poumon", PWM_SERVO_POUMON, nSleep_SERVO_POUMON, FWD_SERVO_POUMON, REV_SERVO_POUMON, Channel_A_SERVO_POUMON, Channel_B_SERVO_POUMON, 16, 207, Faulhaber::X2_ENCODING);
//Faulhaber Servo_Fuite("Servo_Fuite", PWM_SERVO_FUITE, nSleep_SERVO_FUITE, FWD_SERVO_FUITE, REV_SERVO_FUITE, 1, Channel_A_SERVO_FUITE, Channel_B_SERVO_FUITE, 16, 207, Faulhaber::X2_ENCODING);

//Moniteur série, Serial 2
Serial serialMonit(USBTX,USBRX,115200);

//COM Série vers Android, Serial 3
Serial android(PC_10,PC_11,115200);

//Init de la lib ARNSRS;
SENSOR_HEAD_REV_B sensors;

//pour Param Venant du PV
const int sizeParam = 30;
char  param[sizeParam];
volatile int indexParam = 0;
bool newParamFlag = false;

//pour Commandes Android
const int sizeAndroid = 20;
char  Android[sizeAndroid];
volatile int indexAndroid = 0;
bool newAndroidFlag = false;
char to_android[100];

//Flag pour interrompre l'affichage si on veut...
bool FLAG_AFF = true;

//Flag pour interrompre l'enregistrement si on veut...
bool FLAG_REC = true;

//Flag pour interrompre les demandes O2 en cours ed calibration...
bool FLAG_O2 = true;

//Variables de stockage des infos capteurs
int co2 = 0;
float pression = 0;
float Temp1 = 0;
int ppO2 = 0;
int CellO2_1 = 0;
int CellO2_2 = 0;

//Variables et constantes OTU
float OTU = 0;
float COEF_OTU = 0.83;

//Mesure du temps d'éxecution du loop
Timer REAL_RATE;
float RATE = 0;
float RATE_TRUE = 0;
float Ref_Time = 2.0; //La durée de la boucle désirée...

//HTU21D sur l'I2C
HTU21D temphumid(PB_9, PB_8); //Temp humid sensor || SDA, SCL
float Temp2;
int Humid;

//Data LOG
char to_store[50];
time_t seconds;
char Log_File_Name[] = "                           ";

//Contrôle des servos
float Consigne_poumon = 0;
float volet_poumon_Position = 0;
float Consigne_fuite = 0;
float volet_fuite_Position = 0;
float Volets_Speed = 0.1;
float Volet_DeadBand = 5;

#ifdef PID_MODE
//Paramètre du PID
float Kc = 40;
float Ti = 0;
float Td = 0;
float RATE_PID = Ref_Time;
float Commande_PID;
float consigne = 210;
float Max_Input = 1000;
float Min_Input = 80;
float Max_Output = 85;//Vérifier la valeur pour angle à laisser ouvert...
float Min_Output = 5;

//Init PID
PID control_Servo(Kc, Ti, Td, RATE_PID);
#endif

//Boolean du status de l'appareil, en mode SECU ou nominal
bool EN_MODE_SECU = false;

//Test des alim
DigitalIn V_USB(PA_0);
DigitalIn V_PILES_2(PC_3);
AnalogIn V_PILES(PA_1);

int Vusb = 1;
int VPiles = 1;
float VPiles_2 = 1;

//Interruption user button
InterruptIn button(USER_BUTTON);
volatile int  GO = 0;

void Affichage()
{

    serialMonit.printf("\r\n");
    serialMonit.printf("  CO2             = %d ppm\r\n"  , co2);
    serialMonit.printf("  PPO2            = %d mb\r\n", ppO2);
    serialMonit.printf("  OTU             = %d \r\n", (int)OTU);
    serialMonit.printf("  Pression        = %f msw\r\n", pression);
    serialMonit.printf("  Temp MS5837     = %f C\r\n", Temp1);
    serialMonit.printf("  Temp HTU21D     = %f C\n\r", Temp2);
    serialMonit.printf("  Humidity        = %d %%\n\r", Humid);
    serialMonit.printf("\n\r");
    serialMonit.printf("  Cell O2 n 1     = %d\r\n"  , CellO2_1);
    serialMonit.printf("  Cell O2 n 2     = %d\r\n"  , CellO2_2);
    serialMonit.printf("\r\n");
    serialMonit.printf("  Volet Poumon    = %f\r\n"  , volet_poumon_Position);
    serialMonit.printf("  Volet Fuite     = %f\r\n"  , volet_fuite_Position);
    serialMonit.printf("\r\n");
    serialMonit.printf("  Temps d execution de la boucle = %f seconde(s)\n", (RATE + RATE_TRUE / 1000));
    serialMonit.printf("\r\n");
    if (FLAG_REC) serialMonit.printf("  Chaine enregistrée = %s\n", to_store);
    else serialMonit.printf("  Pas d'enregistrement en cours.");
    serialMonit.printf("\r\n");
    serialMonit.printf("  V_USB = %f", Vusb);
    serialMonit.printf("\r\n\r\n");
    fflush(stdout);
}

void pressed()
{
    GO = GO + 1;
    if (GO > 1) NVIC_SystemReset();
}

//Passage en mode SECU
void Mode_SECU()
{
#ifdef PID_MODE
    //Mise du PID en mode manuel (desactivation...)
    control_Servo.setMode(MANUAL_MODE);
#endif
    Consigne_poumon = HOME_SERVO_POUMON;
    Consigne_fuite = HOME_SERVO_FUITE;

    Volets_Speed = 0.1;
    Volet_DeadBand = 10;

    while(1) {
        wait_ms(100);
        if (Servo_Poumon.Pos_OK() == true) break;
        //if (Servo_Poumon.Pos_OK() == true && Servo_Fuite.Pos_OK() == true) break;
    }
    DEBUG("-------------- Appareil en mode SECU ---------------\r\n");

    wait_ms(100);

    EN_MODE_SECU = true;
    wait_ms(100);
    int Pos = Servo_Poumon.getPulses();
    UTILS::Store_A_Val((float)Pos, "Servo_Poumon");
    DEBUG("  position volet poumon sauvegardée = %d pulse(s)\r\n", Pos);
    //Pos = Servo_Fuite.getPulses();
    //UTILS::Store_A_Val((float)Pos, "Servo_Fuite");
    //DEBUG("  position volet fuite sauvegardée = %d pulse(s)\r\n", Pos);
    UTILS::UnMount_Flash();
    Servo_Poumon.Sleep();
}

//Sequence d'arrêt
void Stop_Sequence()
{
    Mode_SECU();
    DEBUG("----------------ARRET DE L'APPAREIL--------------------\r\n");

    DEBUG("  Deep sleep autorisé : %i\r\n", sleep_manager_can_deep_sleep());
    wait(1.0);
    sleep_manager_sleep_auto();
}

//Fonction test de valeur d'entrée digitale
int Power_Test(DigitalIn& pin)
{
    pin.mode(PullDown);

    if(!pin) {
        Stop_Sequence();
        return 0;
    } else {
        return 1;
    }
}

//Fonction test de valeur d'entrée analogique
float Power_Test(AnalogIn& pin)
{
    float Val = pin.read();
    return Val;
}

//Contrôle du status de l'appareil / des constantes
bool Check()
{
    if (ppO2 > 100)
        return true;//Situation OK
    else
        return false;//Situation dégradée
}

//Calcul des OTU
float Calcul_OTU()
{
    /*
    La formule suivante permet de calculer la quantité d' OTU accumulée
    OTU = T * (2* PpO2 -1)0,83
    avec :
    T = temps de plongée en minutes
    PpO2 = pression partielle d’ oxygène en bars
    */

    if (ppO2 > 500) {
        float val = (2 * (float)ppO2/1000 - 1);//je divise par 1000 car la PP est en mb...
        OTU += Ref_Time * pow(val, COEF_OTU);
    }
}

//Thread d'intérogation des capteurs, positions servo
void SENSORS_thread()
{
    while (true) {
        //DEEP_DEBUG("  SENSORS_thread\r\n");

        //CO2 sur Cozir
        co2 = sensors.requestCO2();
        //P / T sur MS5837
        pression = sensors.requestPress();
        Temp1 =  sensors.requestTemp();
        //PPO2 sur ADS1015
        if (FLAG_O2) ppO2 = sensors.requestPpO2();
        //Cell O2 en mV
        if (FLAG_O2) CellO2_1 = sensors.requestCellO2_1();
        if (FLAG_O2) CellO2_2 = sensors.requestCellO2_2();

        //HTU21D
        Temp2 = temphumid.sample_ctemp();
        Humid = temphumid.sample_humid();

        //Retour position des servos
        volet_poumon_Position = Servo_Poumon.Get_Position();
        //volet_fuite_Position = Servo_Fuite.Get_Position();

    }
}

void GO_TO_thread()
{
    while (true) {
        //DEEP_DEBUG("  GO_TO_Thread\r\n");

        //Servo_Poumon.Go_To_Prop(Consigne_poumon);
        //Servo_Poumon.Go_To_PID(Consigne_poumon, Volet_DeadBand);
        Servo_Poumon.Go_To(Consigne_poumon, Volets_Speed, Volet_DeadBand);

        //Servo_Fuite.Go_To_Prop(Consigne_fuite);
        //Servo_Fuite.Go_To_PID(Consigne_fuite);
        //Servo_Fuite.Go_To(Consigne_fuite, Volets_Speed, Volet_DeadBand );
    }
}

void SECU_thread()
{
    while (true) {
        //DEEP_DEBUG("  SECU_Thread\r\n");

        //Alim USB
        //Vusb = Power_Test(V_USB);
        //VPiles = Power_Test(V_PILES);
        //Divers problèmes à implémenter
        //if(!Check()) Mode_SECU();
    }
}

//Callback de l'intérruption des envois de commandes depuis le terminal
void callbackParam()
{
    while(serialMonit.readable()) {
        if ((indexParam  == sizeParam) || newParamFlag  == true) //éviter la saturation du buffer
            char char_flush = serialMonit.getc();
        else
            param [indexParam ++] = serialMonit.getc();//chargement du buffer dans le message

        if ((indexParam == sizeParam) || (param[indexParam - 1] == '\n')) {//le message est complet ou nouvelle ligne ou autre si on veut...
            param[indexParam] = 0;
            newParamFlag  = true;
        }
    }
}

//Callback de l'intérruption des envois de commandes depuis Android
void callbackAndroid()
{
    while(android.readable()) {
        if (indexAndroid  == sizeAndroid) //éviter la saturation du buffer
            android.getc();
        else
            Android [indexAndroid++] = android.getc();//chargement du buffer dans le message
        if ((indexAndroid == sizeAndroid) || (Android[indexAndroid -1] == '\n')) {//le message est complet ou nouvelle ligne ou autre si on veut...
            Android[indexAndroid] = 0;
            newAndroidFlag  = true;
        }
    }
}

void Decoding_Message(char message [])
{

    char com[sizeParam] = "";
    char numb[] = "";

    sscanf(message,"%s %s",&com , &numb);

    DEEP_DEBUG("\r\n  Commande = %s       Valeur = %s \r\n\r\n", com, numb);

    if (0 == strcmp(com, "secu")) {
        Mode_SECU();
    } else if (0 == strcmp(com, "ARNSRS_ID")) {
        UTILS::Store_A_Val(atoi(numb), "ARNSRS_ID");
    } else if (0 == strcmp(com, "Head_ID")) {
        //On l'enregistre dans l'eeprom
        UTILS::write_EEPROM(numb, HEAD_ID);
    } else if (0 == strcmp(com, "O2_1_ID")) {
        //On l'enregistre dans l'eeprom
        UTILS::write_EEPROM(numb, CELL_O2_1_ID);
    } else if (0 == strcmp(com, "O2_2_ID")) {
        //On l'enregistre dans l'eeprom
        UTILS::write_EEPROM(numb, CELL_O2_2_ID);
    } else if (0 == strcmp(com, "CO2_ID")) {
        //On l'enregistre dans l'eeprom
        UTILS::write_EEPROM(numb, CO2_ID);
    } else if (0 == strcmp(com, "calib_O2")) {
        FLAG_O2 = false;
        wait_ms(100);
        sensors.Calibrate_O2(true, atoi(numb));
        wait_ms(100);
        FLAG_O2 = true;
    } else if (0 == strcmp(com, "flash_i")) {
        FLAG_REC = false;
        UTILS::Flash_Infos();
        FLAG_REC = true;
    } else if (0 == strcmp(com, "flash_u")) {
        FLAG_REC = false;
        UTILS::UnMount_Flash();
    } else if (0 == strcmp(com, "flash_m")) {
        UTILS::Mount_Flash();        
    } else if (0 == strcmp(com, "check_F")) {
        FLAG_REC = false;
        serialMonit.printf("  ARNSRS_ID.txt   = %d\r\n", (int)UTILS::Read_A_Val("ARNSRS_ID"));
        //serialMonit.printf("  Calibration_O2.txt = %f\r\n", UTILS::Read_A_Val("Calibration_O2"));
        serialMonit.printf("  Servo_Poumon.txt   = %d\r\n", (int)UTILS::Read_A_Val("Servo_Poumon"));
        //serialMonit.printf("Servo_Fuite.txt    = %d\r\n", (int)UTILS::Read_A_Val("Servo_Fuite");
        FLAG_REC = true;
    } else if (0 == strcmp(com, "check_E")) {
        sensors.Sensor_head_check();
    } else if (0 == strcmp(com, "rec")) {
        if (FLAG_REC) {
            FLAG_REC = false;
            serialMonit.printf("Arrêt du Data Logging.");
        } else {
            FLAG_REC = true;
            serialMonit.printf("Démarrage Data Logging dans %s", Log_File_Name);
        }
    } else if (0 == strcmp(com, "help")) {
        FLAG_AFF = false;
        UTILS::Help();
    } else if (0 == strcmp(com, "start")) {
        FLAG_AFF = true;
    } else if (0 == strcmp(com, "stop")) {
        FLAG_AFF = false;
        UTILS::Help();
    } else if (0 == strcmp(com, "flash_c")) {
        FLAG_REC = false;
        UTILS::Clean_Flash();
    } else if (0 == strcmp(com, "dir")) {
        FLAG_REC = false;
        UTILS::Dir_Flash();
        FLAG_REC = true;
    } else if (0 == strcmp(com, "get")) {
        FLAG_REC = false;
        wait_ms(100);
        char filename[20];
        sprintf(filename, "LOG_%d.txt", atoi(numb));
        UTILS::Read_Flash_File(filename);
        wait_ms(100);
        FLAG_REC = true;
    } else if (0 == strcmp(com, "del")) {
        FLAG_REC = false;
        char filename[20];
        sprintf(filename, "LOG_%d.txt", atoi(numb));
        UTILS::Delete_Flash_File(filename);
        UTILS::Dir_Flash();
        FLAG_REC = true;
    } else if (0 == strcmp(com, "file_s")) {
        FLAG_REC = false;
        char filename[20];
        sprintf(filename, "LOG_%d.txt", atoi(numb));
        UTILS::Get_File_Size(filename);
        FLAG_REC = true;
    } else if (0 == strcmp(com, "calib_p")) {
        Consigne_poumon = 0;
        volet_poumon_Position = 0;
        Servo_Poumon.reset();
    } else if (0 == strcmp(com, "calib_f")) {
        Consigne_fuite = 0;
        volet_fuite_Position = 0;
        //Servo_Fuite.reset();
    } else if (0 == strcmp(com, "sleep")) {
        Stop_Sequence();
    } else if (0 == strcmp(com, "time")) {//Depuis terminal MAC taper : " date +%s "
        set_time(atoi(numb));
    } else if (0 == strcmp(com, "c_pou")) {
        Consigne_poumon = atof(numb);
        DEBUG("  Servo Poumon    = %f\r\n", Consigne_poumon);
    } else if (0 == strcmp(com, "c_fui")) {
        Consigne_fuite = atof(numb);
        DEBUG("  Servo Fuite     =  %f\r\n", Consigne_fuite);
    } else if (0 == strcmp(com, "reset")) {
        UTILS::UnMount_Flash();
        NVIC_SystemReset();
    } else {
        sensors.cozirSend(message);
    }

    strcpy(param," ");
    indexParam = 0;
    newParamFlag = false;
}

void Decoding_Message_Android(char message [])
{
    char *commande = 0;
    float valeur = 0;

    sscanf(message,"%s %f",&commande , &valeur);

    if ((char)commande == 'T') {
        set_time(valeur);
    } else if ((char)commande == 'I') {
        Consigne_poumon = (float)valeur;
        DEBUG("  Servo Poumon    = %f\r\n", Consigne_poumon);
    } else if ((char)commande == 'O') {
        Consigne_fuite = (float)valeur;
        DEBUG("  Servo Fuite     =  %f\r\n", Consigne_fuite);
        /////////////////////////////////////////
        //Pour rajouter une commande
        //} else if ((char)commande == 'X') {
        //  attribuer à une VARIABLE = valeur;
        //  ou une action, avec ou sans valeur
        /////////////////////////////////////////
    } else if ((char)commande == 'R') {
        NVIC_SystemReset();
    }
#ifdef PID_MODE
    else if ((char)commande == 'p') {
        Kc = (float)valeur;
        control_Servo.reset();
        control_Servo.setTunings(Kc, Ti, Td);
        DEBUG"  UPDATE PID -->  Kc = %f   Ti = %f   Td = %f\r\n\n", Kc, Ti, Td);
    } else if ((char)commande == 'i') {
        Ti = (float)valeur;
        control_Servo.reset();
        control_Servo.setTunings(Kc, Ti, Td);
        DEBUG("  UPDATE PID -->  Kc = %f   Ti = %f   Td = %f\r\n\n", Kc, Ti, Td);
    } else if ((char)commande == 'd') {
        Td = (float)valeur;
        control_Servo.reset();
        control_Servo.setTunings(Kc, Ti, Td);
        DEBUG("  UPDATE PID -->  Kc = %f   Ti = %f   Td = %f\r\n\n", Kc, Ti, Td);
    } else if ((char)commande == 'c') {
        consigne = valeur;
        control_Servo.setSetPoint(consigne);
        DEBUG("  UPDATE CONSIGNE PID -->  Consigne = %d\r\n\n", consigne);
    }
#endif

    strcpy(Android," ");
    indexAndroid = 0;
    newAndroidFlag = false;
}

void Create_File_Name_Date()
{
    //Du nom du fichier Date / heure
    seconds = time(NULL);
    char Time[40];
    strftime(Time, 40, "%a_%d_%m_%Y_%H%M", localtime(&seconds));
    sprintf(Log_File_Name, "%s_LOG.txt", Time);
    DEBUG("Nouveau fichier LOG = %s \r\n", Log_File_Name);
}

void Create_File_Name_Index()
{
    //Du nom du fichier par Index
    sprintf(Log_File_Name, "LOG_%d.txt", UTILS::File_Index());
    DEBUG("  Nouveau fichier LOG = %s \r\n", Log_File_Name);
}

int main()
{

    button.fall(&pressed);

    int count = 0;

    while (1) {

        if (count == 0) serialMonit.printf("\r\n  Appuyez sur le User Button pour commencer...\r\n\r\n");

        count = 1;

        if (GO == 1) {

            wait(1);

            serialMonit.attach(&callbackParam, Serial::RxIrq);

            android.attach(&callbackAndroid, Serial::RxIrq);

            //Ci-dessous commande pour formater une nouvelle carte
            //UTILS::Format_Flash();

            //Montage Flash
            UTILS::Mount_Flash();

            //Liste des fichiers sur la Flash
            UTILS::Dir_Flash();

            //Vérification RTC, si on est le 01/01/70, c'est qu'il y a un problème
            seconds = time(NULL);
            char YEAR[10];
            strftime(YEAR, 10, "%D", localtime(&seconds));
            if (0 == strcmp(YEAR, "01/01/70")) serialMonit.printf("  Vous devez régler la RTC...\r\n");
             
            if (UTILS::File_Exist("ARNSRS_ID") == false) {
                UTILS::Store_A_Val(000, "ARNSRS_ID");
                DEBUG("ARNSRS ID forcée à 000\r\n");
            }

            bool calib_O2 = true;
            bool calib_CO2 = true;

            /*
            Par défaut les valeur en cas de calibration sur true sont les suivant

               nbCalibO2 = 5
               Mode = SPOOLING
               Filtre = DIGI_FILTER32
               CalibrationCO2 = "CALIB_AIR"

               Parfois la calibration du Cozir coince...faire reset et relancer...

               Pour calibrer avec ces paramètres :

               sensors.Sensors_INIT(true, true);

               Pour changer utiliser la syntaxe suivante :

               sensors.Sensors_INIT(true, true, true, 5, SPOOLING, DIGI_FILTER32, CALIB_AIR);

            */

            sensors.Sensors_INIT(calib_O2, calib_CO2);
            wait(1);

            //Création du nouveau fichier LOG par index / par date.
            Create_File_Name_Index();
            //Create_File_Name_Date()

            //Création et écriture du header du fichier LOG
            sensors.Create_Header(Log_File_Name);

            Servo_Poumon.Init("Servo_Poumon");
            //Servo_Fuite.Init("Servo_Fuite");

            DEBUG("  Demarrage des threads...\r\n\r\n");

            /*

            Pour mémoire, les réglage de priorité des thread

                  osPriorityIdle          = -3,          ///< priority: idle (lowest)
                  osPriorityLow           = -2,          ///< priority: low
                  osPriorityBelowNormal   = -1,          ///< priority: below normal
                  osPriorityNormal        =  0,          ///< priority: normal (default)
                  osPriorityAboveNormal   = +1,          ///< priority: above normal
                  osPriorityHigh          = +2,          ///< priority: high
                  osPriorityRealtime      = +3,          ///< priority: realtime (highest)
                  osPriorityError         =  0x84        ///< system cannot determine priority or thread has illegal priority
            */
            wait(1);


            Thread thread_Volets(osPriorityNormal);

            thread_Volets.start(callback(GO_TO_thread));

            DEBUG("  Volets thread démarré\r\n\r\n");

            wait(1);

            Thread thread_Secu(osPriorityNormal);

            thread_Secu.start(callback(SECU_thread));

            DEBUG("  Secu thread démarré\r\n\r\n");

            wait(1);

            Thread thread_Head(osPriorityNormal);

            thread_Head.start(callback(SENSORS_thread));

            DEBUG("  Info thread démarré\r\n\r\n");

            wait(1);


#ifdef PID_MODE
            //Init PID
            //Entrée PPO2 entre 100 et 1000 mb
            control_Servo.setInputLimits(Min_Input, Max_Input);
            //Sortie servo entre 0 et 100 %
            control_Servo.setOutputLimits(Min_Output, Max_Output);
            //Mode auto au démarrage
            control_Servo.setMode(AUTO_MODE);
            //Consigne à x mb
            control_Servo.setSetPoint(consigne);
#endif

            DEBUG("  Threads démarrés.....\r\n\r\n  Tapez help pour voir la liste des commandes disponibles.\r\n ");

            while (true) {

                //Démarrage du Timer mesurant le temps d'éxecution du code
                REAL_RATE.start();

                if (newParamFlag) {
                    DEBUG("  From PC = %s\r\n", param);
                    Decoding_Message(param);
                }

                if (newAndroidFlag) {
                    DEBUG("  From Android = %s\r\n", Android);
                    Decoding_Message_Android(Android);
                }

                //Fabrication de la chaine Date / heure
                seconds = time(NULL);
                char Time_buf[32];
                strftime(Time_buf, 32, "%D %I-%M-%S ", localtime(&seconds));

#ifdef PID_MODE
                //Fabrication de la chaine à enregistrer
                sprintf(to_store,"%s:%d:%d:%.2f:%.2f:%.2f:%d:%d:%d:%.2f:%.2f:%d:%.3f:%.3f:%.3f:%d",
                        Time_buf,
                        co2,
                        ppO2,
                        pression,
                        Temp1,
                        Temp2,
                        Humid,
                        CellO2_1,
                        CellO2_2,
                        volet_poumon_Position,
                        volet_fuite_Position,
                        MODE_FLAG,
                        Kc,
                        Ti,
                        Td,
                        (int)consigne
                       );
#endif
#ifndef PID_MODE
                //Fabrication de la chaine à enregistrer sans les variables du PID
                sprintf(to_store,"%s:%d:%d:%.2f:%.2f:%.2f:%d:%d:%d:%.2f:%.2f:%d:%.3f:%.3f:%.3f:%d",
                        Time_buf,
                        co2,
                        ppO2,
                        pression,
                        Temp1,
                        Temp2,
                        Humid,
                        CellO2_1,
                        CellO2_2,
                        volet_poumon_Position,
                        volet_fuite_Position,
                        MODE_FLAG,
                        0.0,
                        0.0,
                        0.0,
                        0
                       );
#endif
                char to_slave[50];
                //Fabrication de la chaine pour l'IHM
                sprintf(to_slave,"%d:%d:%d:%.2f:%.2f:%.2f:%d:%d:%d:%.2f:%.2f\r\n",
                        co2,
                        ppO2,
                        (int)OTU,
                        pression,
                        Temp1,
                        Temp2,
                        Humid,
                        CellO2_1,
                        CellO2_2,
                        volet_poumon_Position,
                        volet_fuite_Position);

                //Pour Android on ajoute < et > pour décoder l'arrivée du message
                if (NEED_ANDROID_OUTPUT == 1) {
                    //sprintf(to_android,"<%s>",to_store);
                    ANDROID(to_slave);
                }

                //Calcul des OTU
                Calcul_OTU();

                //Vers le moniteur série
                if (FLAG_AFF) Affichage();

                //Enregistrement de la chaine
                if (FLAG_REC) UTILS::Write_Flash_File(to_store, Log_File_Name);


#ifdef PID_MODE
                //Update du PID
                control_Servo.setProcessValue(ppO2);
                //Nouvelle sortie servo fuite si on est pas en mode SECU
                if(!EN_MODE_SECU) Consigne_fuite = control_Servo.compute();
#endif

                //Arrêt du Timer mesurant le temps d'éxecution du code
                REAL_RATE.stop();
                //Définition de la nouvelle valeur du temps d'échantillonage du PID.
                RATE = REAL_RATE.read();
                //Reset du Timer
                REAL_RATE.reset();

                //Pour ralentir le code à Ref_Time seconde fixe quelque soit les intéruptions du loop....
                if (Ref_Time > RATE) {
                    RATE_TRUE = (Ref_Time - RATE) * 1000;
                } else {
                    RATE_TRUE = 0;
#ifdef PID_MODE
                    control_Servo.setInterval(RATE);
#endif
                    DEBUG("Pour ralentir le code, Ref_Time doit être supérieur à %f seconde(s)\r\n\n", RATE);
                }

                wait_ms(RATE_TRUE);
            }
        }
    }
}