/* Le programme Configurer_le_module.ino est un outil logiciel qui permet de configurer à notre guise un
   module Bluetooth de type HC-05 et de consulter ses données internes.

 REMARQUE : Pour faciliter les manipulations les caractères alphabétiques minuscules sont transformés
            automatiquement en leur équivalent majuscule. Ce n'est pas possible de transposer ',' en
            '?' car on a besoin de la virgule dans certaines commandes AT.
 
 NOTE : Les deux tableaux TEXTE_USB[Lgr_TEXTE+1] et TEXTE_HC_05[Lgr_TEXTE+1] servent exactement à la
        même chose, c'est à dire mémoriser les messages échangés sur les deux lignes série. Comme ils
        ne sont jamais utilisés simultanément, il serait possible de n'utiliser qu'un seul tableau
        commun. C'est pour des raisons de lisibilité du programme que j'ai opté pour ce "gaspillage",
        car avec ce démonstrateur on peut se le permettre, on dispose de place à revendre.
        Idem pour les deux variables CAR_Moniteur et CAR_module_HC05.

Comportement du programme :   (Voir le didacticiel)
---------------------------
 • On frappe des commandes AT et on observe le retour du module.
 • Cette version intègre le mode Alternat INFINI décrit en fin de titoriel PROGRAMMATEUR de BLUETOOTH.pdf.
 
 BRANCHEMENTS à effectuer :
--------------------------
  • Insérer le HC-05 orienté le plus à l'extérieur possible du C.I. coté A pour le module à programmer.
  • Insérer le HC-05 orienté le plus à l'extérieur possible du C.I. coté B pour tester le dialogue avec A.
  
                                   PGM            RAM dynamique 
Compilateur v1.7.9 :        9922 Octets (32%)  504 Octets (24%)  */

//------------------------ Constantes du programme --------------------------
#define Version "12/09/2025" // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define Lgr_TEXTE 32 // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define Taille_PGM "9840 (32%)" // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define Taille_Dynamique "504 (24%)" // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define CR 13 // Code du Retour Chariot qui vaut 13 codé en ASCII sous forme décimale.
#define LF 10 // Code du passage à la ligne qui vaut 10 codé en ASCII sous forme décimale.
//-------------------------- Textes du programme ----------------------------
#define TXT_NAME "AT+NAME?" // Texte à transmettre à l'échantillon Blutooth pour lire un paramètre interne.
#define TXT_ROLE "AT+ROLE?" // Idem.
#define TXT_PSWD "AT+PSWD?" // Idem.
#define TXT_UART "AT+UART?" // Idem.
#define TXT_ADDR "AT+ADDR?" // Idem.
#define TXT_BIND "AT+BIND?" // Idem.
#define TXT_STATE "AT+STATE?" // Idem.
#define TXT_VERSION "AT+VERSION?" // Idem.
//------------------- Définition des broches binaires -----------------------
#define RX 6 // Va sur TX du module Bluetooth.
#define TX 11 // Va sur RX du module Bluetooth avec pont de résistances.
#define LED_Arduino 13
//----------------- Définition des broches analogiques ----------------------
#define BP 14 // Bouton poussoir sur A0.
//---------------------- Déclarations pour le matériel ----------------------
#include <SoftwareSerial.h>
SoftwareSerial Module_HC05(RX,TX);

//------------------------- Variables du programme --------------------------
boolean Clignote, Mode_question;
char CAR_Moniteur, CAR_module_HC05;
byte Index;
int Compteur_ERR, Compteur_OK;
unsigned long Temps_Ecoule_depuis_RESET = 0;
char TEXTE_USB[Lgr_TEXTE+1];
char TEXTE_HC_05[Lgr_TEXTE+1];

//========================= Procédures de servitude ============================
void Inverser_LED_Arduino() {
  if (Clignote) digitalWrite(LED_Arduino,!digitalRead(LED_Arduino));
  else digitalWrite(LED_Arduino,LOW);}

void Vider_le_tampon_USB() {while (Serial.available()) Serial.read();}

void Vider_le_tampon_HC05() {while (Module_HC05.available()) Module_HC05.read();}

void Enregistre_le_preambule() {TEXTE_USB[0] = 'A'; TEXTE_USB[1] = 'T'; TEXTE_USB[2] = '+';}

void Lecture_sur_le_Moniteur(){
  Enregistre_le_preambule(); Index = 3; 
  while (!Serial.available()) { // Attente d'une saisie sur le Moniteur.
             //================= Chronométrage pour le délai écoulé ================
             if (millis() - Temps_Ecoule_depuis_RESET > 75)  {
             Temps_Ecoule_depuis_RESET = millis(); Inverser_LED_Arduino();}}
             //=====================================================================
  while (Serial.available()) {CAR_Moniteur = Serial.read();
         //-------------------- Passer les lettre en MAJUSCULES -------------------
         if ((CAR_Moniteur >= 'a') && (CAR_Moniteur < char(123))) CAR_Moniteur = CAR_Moniteur & 223;
         // Bien moins couteux en octets que Chaine_Memorisee.toUpperCase() pour convertir les minuscules.
         //------------------------------------------------------------------------
         if ((Index < Lgr_TEXTE) && (CAR_Moniteur != CR) && (CAR_Moniteur != LF)) 
            {TEXTE_USB[Index] = CAR_Moniteur; Index++;}
         delay(5);} // Délai impératif.
  if (Mode_question) {TEXTE_USB[Index] = '?'; Index++;}
  Vider_le_tampon_USB();} // Vider le tampon série si Nb caractères > Lgr_TEXTE.

void Lecture_sur_le_Module_HC_05() {
  Index = 0; while (!Module_HC05.available()); // Attente du début de l'ACR.
  while (Module_HC05.available()) {CAR_module_HC05 = Module_HC05.read();
         if ((Index < Lgr_TEXTE) && (CAR_module_HC05 != CR) && (CAR_module_HC05 != LF))
            {TEXTE_HC_05[Index] = CAR_module_HC05; Index++;}
  delay(5);}} // Délai impératif.

void Afficher_ACR() {
  Serial.print(F(" ACR = "));
  if ((TEXTE_HC_05[0] == 'E') && (TEXTE_HC_05[1] == 'R') && (TEXTE_HC_05[1] == 'R') && (TEXTE_HC_05[1] == 'O'))
      for (byte I = 0; I < Index; I++) Serial.print(TEXTE_HC_05[I]);
  else {for (byte I = 0; I < Index-2; I++) Serial.print(TEXTE_HC_05[I]);
       Serial.print(' '); Serial.print(TEXTE_HC_05[Index-2]); Serial.print(TEXTE_HC_05[Index-1]);}
  Serial.println();}

void Souligner(byte Nb) {Serial.print(' '); for (byte I = 0; I < Nb; I++) Serial.print('-'); Serial.println();}

void AFF_texte_caracteristiques_echantillon() {
  Serial.print(F(" Caract")); Serial.print(char(233)); Serial.print(F("ristiques de l'"));
  Serial.print(char(233)); Serial.print(F("chantillon"));}

void Lister_les_protocoles_en_EXPLOITATION() {
  Serial.println(); Serial.println(F(" Protocoles d'EXPLOITATION du programme.")); Souligner(39);
  Serial.println(F(" '&' est une directive qui alterne entre QUESTIONNEMENT et INITIALISATION."));
  Serial.print(F(" Possibilit")); Serial.print(char(233));
  Serial.println(F(" de frapper les commandes en minuscules."));
  Serial.println(F(" Le rappel des commandes avec 'H' est valable dans les deux modes."));
  Serial.println(F(" En mode INITIALISATION terminer par un '?' transforme en QUESTIONNEMENT."));
  Serial.print(F(" En INITIALISATION on s")); Serial.print(char(233)); Serial.print(F("pare les donn"));
  Serial.print(char(233)); Serial.println(F("es avec la virgule."));
  Serial.print(F(" Pour avoir le format d'une donn")); Serial.print(char(233));
  Serial.println(F("e il suffit d'interroger avec son code AT et"));
  Serial.print(F("      de remplacer les ':' par des virgules,"));
  Serial.print(F(" sans oublier le = apr")); Serial.print(char(232)); Serial.println(F("s la consigne."));
  Serial.println(F(" Pour UART les vitesses possibles sont :"));
  Serial.print(F(" 4800    9600    19200   38400   57600"));
  Serial.println(F("   115200  230400  460800  921600  1382400"));
  Serial.println(F(" CONSEIL : Commencer les initialisations par ORGL."));
  Serial.print(F(" >>>>> 'X' ou 'L' :")); AFF_texte_caracteristiques_echantillon();  Serial.println('.');
  Serial.println(F(" 'M' inverse le clignottement et liste les tailles du PGM."));
  Serial.print(F(" 'I' impose le mode INFINI apr")); Serial.print(char(232)); 
  Serial.println(F("s confirmation."));}

void Lister_les_commandes_AT_sur_USB() {
  Serial.println(); Serial.println(F(" Liste des commandes AT. (Fiche 11/16)")); Souligner(37);
  Serial.print(F(" '*' pour une question et '")); Serial.print(char(149));
  Serial.println(F("' pour une consigne."));
  Serial.println(F(" Lister les protocoles d'EXPLOITATION avec la directive 'E'."));
  Serial.println(F(" Passer en mode Alternats INFINIs avec la directive 'I'."));
  Serial.print(F(" (Le protocole est comment")); Serial.print(char(233));
  Serial.println(F(" en confirmation avec 'O'.)")); Serial.println();
  Serial.print(' '); Serial.print(char(149)); Serial.print(F(" ORGL : Restore les valeurs par d"));
  Serial.print(char(233)); Serial.println(F("faut."));
  Serial.println(F(" * VERSION? : Version du module."));
  Serial.println(F(" * NAME? : Nom arbitraire du module."));
  Serial.println(F(" * ADDR? : Adresse du module."));
  Serial.println(F(" * ROLE? : 0 pour Esclave et 1 por Maitre."));
  Serial.print(F(" * PSWD? : Mot de passe. (1234 par d"));
  Serial.print(char(233)); Serial.println(F("faut.)"));
  Serial.println(F(" * UART? : Format des transmissions."));
  Serial.println(F(" * BIND? : Adresse pour associer l'Esclave."));
  Serial.print(F(" * STATE? : Etat de fonctionnement du module test")); Serial.print(char(233));
  Serial.println('.');
  Serial.print(' '); Serial.print(char(149)); Serial.println(F(" RESET : Passer en mode utilisation."));}

boolean Mode_AT() {return ((TEXTE_HC_05[0] == 'O') && (TEXTE_HC_05[1] == 'K'));}

void Tester_la_presence_du_HC05() {
  Serial.println(); Serial.println(F(" Attente d'un HC-05 en mode AT."));
  Serial.print(F(" Penser ")); Serial.print(char(224)); Serial.print(F(" passer le Strap sur 3,3V."));
  while (!Mode_AT()) {delay(500); Module_HC05.println(F("AT")); Lecture_sur_le_Module_HC_05();}
  Serial.println(); Serial.print(F(" Module en mode AT pr")); Serial.print(char(233)); 
  Serial.println(F("sent.")); Lister_les_commandes_AT_sur_USB(); Serial.println(); 
  Serial.println(F(" Rappel des commandes AT ci-dessus avec directive 'H' ou ',' ou '?'.")); Souligner(67);
  Serial.println(F(" 'H' pour HELP et '?' car on se pose une question."));}

int SRAM_LIBRE() { // Fonction qui retourne la taille de SRAM disponible.
    extern int __heap_start, *__brkval; // Déclaration des deux pointeurs dédiés.
    byte BIDON; // Dernière variable allouée, donc occupe le "haut" du TAS.
    if (__brkval == 0) {return (int) &BIDON -(int) &__heap_start;}
    else {return (int) &BIDON -(int) __brkval;}}

void Aff_les_tailles_PGM() {
  Serial.print(F(" Taille programme = ")); Serial.println(Taille_PGM);
  Serial.print(F(" Donn")); Serial.print(char(233)); Serial.print(F("es dynamiques = "));
  Serial.println(Taille_Dynamique); Serial.println(F(" Compilateur version 1.7.9."));
  Serial.print(F(" Pile - Tas sur le Maitre = ")); Serial.println(SRAM_LIBRE());}

void Lister_les_caracteristique_du_Hc_05() {
  Serial.println(); AFF_texte_caracteristiques_echantillon(); Serial.println(" :"); Souligner(35);
  Serial.print(char(149)); Module_HC05.println(TXT_NAME); Lecture_sur_le_Module_HC_05(); Afficher_ACR();
  Serial.print(char(149)); Module_HC05.println(TXT_ROLE); Lecture_sur_le_Module_HC_05(); Afficher_ACR();
  Serial.print(char(149)); Module_HC05.println(TXT_PSWD); Lecture_sur_le_Module_HC_05(); Afficher_ACR();
  Serial.print(char(149)); Module_HC05.println(TXT_UART); Lecture_sur_le_Module_HC_05(); Afficher_ACR();
  Serial.print(char(149)); Module_HC05.println(TXT_ADDR); Lecture_sur_le_Module_HC_05(); Afficher_ACR();
  Serial.print(char(149)); Module_HC05.println(TXT_BIND); Lecture_sur_le_Module_HC_05(); Afficher_ACR();
  Serial.print(char(149)); Module_HC05.println(TXT_VERSION); Lecture_sur_le_Module_HC_05(); Afficher_ACR();
  Serial.print(char(149)); Module_HC05.println(TXT_STATE); Lecture_sur_le_Module_HC_05(); Afficher_ACR();}

void Reception_sur_le_Moniteur(){
  Index = 0; 
  while (!Serial.available()); // Attente d'une saisie sur le Moniteur.
  while (Serial.available()) {CAR_Moniteur = Serial.read();
         if ((Index < Lgr_TEXTE) && (CAR_Moniteur != CR) && (CAR_Moniteur != LF)) 
            {TEXTE_USB[Index] = CAR_Moniteur; Index++;}
         delay(5);}
  Vider_le_tampon_USB();} // Vider le tampon série si Nb caractères > Lgr_TEXTE.

void Traiter_la_directive_I() {// Alternat automatique en mode INFINI.
   if (TEXTE_USB[3] == 'I') {Serial.println(); Serial.print(F(" Passer en mode alternat INFINI ? (O/N) >>> "));
     Lecture_sur_le_Moniteur(); Serial.print(TEXTE_USB[3]);
     if (TEXTE_USB[3] != 'O') {
        Serial.println(); Serial.println(F(" On abandonne le mode Alternats."));
        Enregistre_le_preambule(); TEXTE_USB[3] = 'L'; Index = 4;}
     else {Serial.println(); Serial.println(F(" Mode Alternats INFINI :")); Souligner(23);
           Serial.println(F(" > Passer le Strap du 3,3v sur GND,"));
           Serial.println(F(" > Relier D1 sur TXD et Placer l'Esclave en A"));
           Serial.println(F(" > Retirer le Maitre de A et l'y replacer pour :"));
           Serial.println(F(" > Attendre que les deux LEDs Oranges s'illuminent,"));
           Serial.println(F(" >>>> Valider sur le bouton poussoir."));
           Module_HC05.begin(9600); Compteur_ERR = 0; Compteur_OK = 0; digitalWrite(LED_Arduino,LOW); 
           //--------------------- Attendre un clic sur le B.P. ----------------------
           while(analogRead(BP) > 511); // Lire jusqu'au clic.
           //--------------------- Attendre le relâcher du  B.P. ---------------------
           while(analogRead(BP) < 511); // Ne rien faire.
           while (true) {
              Module_HC05.println("TEST"); // Transmet le texte au Maître qui l'envoie à l'Esclave.
              delay(100); Reception_sur_le_Moniteur(); // Réception du texte envoyé par l'Esclave.
              if ((TEXTE_USB[0] == 'T') && (TEXTE_USB[1] == 'E')
                 && (TEXTE_USB[2] == 'S') && (TEXTE_USB[3] == 'T')) Compteur_OK++;
              else Compteur_ERR++;
              Serial.println(); Serial.print(F(" Nb OK = ")); Serial.print(Compteur_OK);
              Serial.print(F(" Nb ERR = ")); Serial.print(Compteur_ERR);}}}}

//=========================== Corps du programme ===============================

void setup(){
  pinMode(RX,INPUT); pinMode(TX,OUTPUT); pinMode(LED_Arduino, OUTPUT);  
  Serial.begin(9600); Vider_le_tampon_USB(); // Vider le tampon série.
  Serial.println(); Serial.print(F(" CONFIGURATEUR : Version du ")); Serial.println(Version);
  Module_HC05.begin(38400); Mode_question = true; Tester_la_presence_du_HC05(); Clignote = true;
  Serial.println(); Serial.println(F(" Dialogue en mode QUESTIONNEMENT."));}

void loop(){
  //--------------------- Saisir une commande sur le Moniteur ---------------------
  Serial.println(); 
  if (Mode_question) Serial.print(F(" Question ?"));
  else {Serial.print(F(" INITIALISATION param")); Serial.print(char(232)); Serial.print(F("tre ?"));}
  // Charger dans TEXTE les caractères disponibles sur le port Série USB du Moniteur :
  Lecture_sur_le_Moniteur();
  //------------------------------ Traiter la commande ----------------------------
  if (TEXTE_USB[3] == '&') {Mode_question = !Mode_question;
     Serial.println(); if (Mode_question) Serial.println(F(" Mode QUESTIONNEMENT."));
                       else Serial.println(F(" ATTENTION Mode ECRITURE."));} 
  //----------------------------- Afficher la commande ----------------------------
  if ((TEXTE_USB[3] == ',') || (TEXTE_USB[3] == '?')) TEXTE_USB[3] = 'H';
  if (TEXTE_USB[3] == 'X') TEXTE_USB[3] = 'L';
  if (TEXTE_USB[3] != '&') {Serial.print(F(" [")); for (byte I = 0; I < Index; I++) Serial.print(TEXTE_USB[I]);
                            Serial.println(']');}
  //---------------------- Traiter le rappel des commandes AT ---------------------
  if (TEXTE_USB[3] == 'H') Lister_les_commandes_AT_sur_USB();
  //-------------------- Afficher les protocoles d'EXPLOITATION -------------------
  if (TEXTE_USB[3] == 'E') Lister_les_protocoles_en_EXPLOITATION();
  //----------------------- Afficher les paramètres mémoire -----------------------
  if (TEXTE_USB[3] == 'M') {Aff_les_tailles_PGM(); Clignote = !Clignote;} // Inverse l'état de clignotement.
  //----------------- Liste les caractéristiques de l'échantillon -----------------
  if (TEXTE_USB[3] == 'L') Lister_les_caracteristique_du_Hc_05();
  //-------------------- Passe en mode INFINI si confirmation ---------------------
  if (TEXTE_USB[3] == 'I') Traiter_la_directive_I();
  //---------------------- Transmettre le texte vers le module --------------------
  if ((TEXTE_USB[3] != '&') &&  (TEXTE_USB[3] != 'H') &&  (TEXTE_USB[3] != 'E') && 
      (TEXTE_USB[3] != 'M') &&  (TEXTE_USB[3] != 'L')) {
      for (byte I = 0; I < Index; I++) Module_HC05.write(TEXTE_USB[I]);
      Module_HC05.write(CR); Module_HC05.write(LF);
      //------------------- Affichage de l'accusé de réception --------------------
      Lecture_sur_le_Module_HC_05(); Afficher_ACR();}}


