/* Programme ESCLAVE pour tester le transmetteur Bluetooth HC-05.
   Le programme attend un TEXTE arrivant sur la ligne RX du module Bluetooth.
   Il affiche alors un texte sur la ligne du bas de l'afficheur OLED si c'est une commande.
   Puis il transmet l'accusé de réception "OK" sur la Ligne TX du module Bluetooth.
   Chaque ACR commence par le numéro d'ordre du texte reçu suivi de " OK!".
   Si la commande reçue est égal à 'Z' le compteur est remis à zéro.
   Puis le cycle recommence.
   Si le texte ne fait qu'un seul caractère, c'est une commande. Si la commande n'est pas
   valide, l'ACR retourne "Erreur !". Une erreur produit un BIP d'alerte si Silence est annulé.
   La commande 'A' pour Alarme force un BIP même si silence est actif. La commande 'H' pour "Help" fait
   afficher sur le Moniteur la LISTE des commandes. (H ou ? ou ,)

BRANCHEMENTS à effectuer :
--------------------------
   Le module HC-05 : GND -> GND    VCC -> +5V ou +3,3V   TX -> D10   RX -> D11
   D5 : BUZZER actif dont le coté - est sur GND. (Non obligatoire.)
                      
   
Compilateur v1.7.9.         PGM               RAM dynamique
                       16748 Octets (54%)    1386 Octets (67%)  */

//------------------------ Constantes du programme --------------------------
#define Version "31/07/2025." // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define Taille_PGM 16748 // Taille du programme Esclave @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define RAM_Dynamique 1386 // Taille de la RAM dynamique @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define Lgr_TEXTE 14 // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#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.
int Delai_voulu = 75; // Demi période du clignottement en mS.
unsigned long Temps_Ecoule_depuis_RESET = 0;

//----------------- Définition des broches Analogiques ----------------------

//------------------- Définition des broches binaires -----------------------
#define RX 2 // Va sur TX du module Bluetooth.
#define TX 3 // Va sur RX du module Bluetooth avec pont de résistances.
#define BUZZER 5 // D5 : BUZZER actif. (Broche choisie arbitrairement)
#define LED_Arduino 13

//---------------------- Déclarations pour le matériel ----------------------
// Cette section est placée après la déclaration des broches binaires qui doivent
// être définies lors de l'instruction SoftwareSerial mySerial(RX,TX);
#include <SoftwareSerial.h>
SoftwareSerial Module_HC05(RX,TX);
#include <U8g2lib.h>
#include <SPI.h>
#include <Wire.h>
U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0);

//------------------------- Variables du programme --------------------------
boolean Encadrement, Erreur, INFINI, Lumiere, Noir, Silence;
byte Index;
char CAR_sur_RX;
unsigned long Compteur;
char TEXTE[Lgr_TEXTE];

//========================= Procédures de servitude ============================
void BIP() {digitalWrite(BUZZER, HIGH); delay(100); digitalWrite(BUZZER, LOW);}

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 RESET() {Lumiere = false; Encadrement = true; Noir = false; Silence = true; Compteur = 0;}

void Aff_Nb_Echanges() {
  u8g2.print(F("Nb = ")); u8g2.print(Compteur); Serial.print(F(" Nb echanges = ")); Serial.print(Compteur);}

void Aff_les_textes_relatifs_aux_commandes(byte Commande) {
  switch(Commande) {
         case 1 : {u8g2.print(F("BIP d'alerte.")); Serial.print(F(" BIP d'alerte.")); break;}
         case 2 : {u8g2.print(F("BONJOUR.")); Serial.print(F(" BONJOUR.")); break;}
         case 3 : {u8g2.print(F("Compilateur 1.7.9.")); Serial.print(F(" Compilateur 1.7.9.")); break;}
         case 4 : {u8g2.print(F("SILENCE !")); Serial.print(F(" SILENCE !")); break;}
         case 5 : {u8g2.print(F("Encadrement."));  Serial.print(F(" Encadrement.")); break;}
         case 6 : {u8g2.print(F("Fenetre."));  Serial.print(F(" Fenetre.")); break;}
         case 7 : {u8g2.print(F("Liste commandes.")); Serial.print(F(" Liste commandes.")); break;}
         case 8 : {u8g2.print(F("Dialogue INFINI.")); Serial.print(F(" Dialogue INFINI.")); break;}
         case 9 : {Aff_Nb_Echanges(); break;}
         case 10 : {u8g2.print(Taille_PGM); u8g2.print(F(" octets"));
                   Serial.print(F(" Taille_PGM = ")); Serial.print(Taille_PGM);
                   Serial.print(F(" octets")); break;}
         case 11 : {u8g2.print(F("LED Arduino."));  Serial.print(F(" LED Arduino.")); break;}
         case 12 : {u8g2.print(F("Pile - Tas = ")); u8g2.print(SRAM_LIBRE());
                   Serial.print(F(" Pile - Tas = ")); Serial.print(SRAM_LIBRE()); break;}
         case 13 : {u8g2.print(F("FIN ecran noir.")); Serial.print(F(" FIN ecran noir.")); break;}
         case 14 : {u8g2.print(F("Etat des Options.")); Serial.print(F(" Etat des Options.")); break;}
         case 15 : {u8g2.print(F("PGM 54% Dyn; 67%.")); Serial.print(F(" PGM 54% Dyn; 67%.")); break;}
         case 16 : {u8g2.print(F("Au revoir.")); Serial.print(F(" Au revoir.")); break;}        
         case 17 : {u8g2.print(F("RESET options.")); Serial.print(F(" RESET options.")); break;}
         case 18 : {u8g2.print(F("Silence = "));
                   if (Silence) u8g2.print(F("OUI")); else u8g2.print(F("NON"));
                   Serial.print(F(" Silence = "));
                   if (Silence) Serial.print(F("OUI")); else Serial.print(F("NON")); break;}
         case 19 : {u8g2.print(F("TEST dialogue.")); Serial.print(F(" TEST dialogue.")); break;}
         case 20 : {u8g2.print(F("M Dynamique ")); u8g2.print(RAM_Dynamique);
                    Serial.print(F(" RAM Dynamique  = ")); Serial.print(RAM_Dynamique); break;}
         case 21 : {u8g2.print(F("Rev : ")); u8g2.print(Version);
                   Serial.print(F(" Rev : ")); Serial.print(Version); Serial.print('.'); break;}
         case 22 : {u8g2.print(F("RAZ du compteur.")); Serial.print(F(" RAZ du compteur."));}}}

void Affiche_la_commande(byte Commande) {
  u8g2.clearBuffer(); // Efface la mémoire interne.
    u8g2.setFont(u8g2_font_shylock_nbp_tf); // Choisir la police de caractère affichée.
    if ((Commande == 13) && (Noir)) {
       u8g2.setCursor(10,28); u8g2.print("Ecran noir."); u8g2.sendBuffer();
       if (!INFINI) delay(2000); u8g2.clearBuffer(); u8g2.sendBuffer();}
    if (Noir) Serial.print(F(" Ecran noir."));
    if (!Noir) {
        if (Encadrement) u8g2.drawFrame(0,0,128,32);
        u8g2.setFont(u8g2_font_shylock_nbp_tf); // Choisir la police de caractère affichée.
        u8g2.drawStr(10,14," Commande : "); u8g2.setCursor(86,14); u8g2.print(TEXTE[0]);
        u8g2.setCursor(10,28); Aff_les_textes_relatifs_aux_commandes((Commande));  
  u8g2.sendBuffer();}} // Transférer le texte sur l'afficheur.

void Afficher_attente_sur_OLED() {
  u8g2.clearBuffer(); // Efface la mémoire interne.
  u8g2.drawFrame(0,0,128,32);
  u8g2.setFont(u8g2_font_shylock_nbp_tf); // Choisir la police de caractère affichée.
  u8g2.drawStr(26,14,"Attente de"); u8g2.drawStr(12,28,"SYNCHRONISATION");
  u8g2.sendBuffer();} // Transférer le texte sur l'afficheur.

void Affiche_TEXTE_sur_Moniteur() {
  Serial.println(); Serial.println(); Serial.print(' '); Serial.print(Compteur);
  Serial.print(F(" TEXTE = ["));
  for (byte I = 0; I < Index; I++) Serial.print(TEXTE[I]); Serial.println(']');}  

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

void Attente_de_la_synchronisation() {
  Afficher_attente_sur_OLED(); Serial.println(F(" Attente de synchronisation."));
  TEXTE[0] = '@'; // caractère quelconque pour entrer dans la boucle.
  while (TEXTE[0] != 'S') {Module_HC05.println('E'); Attente_texte_sur_Bluetooth();}
  Module_HC05.print('O'); Module_HC05.println('K');
  //-----------------------------------------------------------------------
  u8g2.clearBuffer(); // Efface la mémoire interne.
  u8g2.drawFrame(0,0,128,32);
  u8g2.setFont(u8g2_font_shylock_nbp_tf); // Choisir la police de caractère affichée.
  u8g2.drawStr(34,14,"DEBUT des"); u8g2.drawStr(34,28,"ECHANGES");
  u8g2.sendBuffer(); // Transférer le texte sur l'afficheur.
  //-----------------------------------------------------------------------
  Serial.println(); Serial.println(F(" DIALOGUE 2,4GHz :"));}

void Traiter_les_consignes() {
  if (Index == 1) {
     switch (CAR_sur_RX) {
        case 'A' : {BIP(); Affiche_la_commande(1); break;}
        case 'B' : {Affiche_la_commande(2); break;}
        case 'C' : {Affiche_la_commande(3); break;}
        case 'D' : {Silence = true; Affiche_la_commande(4); break;}
        case 'E' : {Encadrement = !Encadrement; Affiche_la_commande(5); break;}
        case 'F' : {Affiche_la_commande(6); break;}
        case 'H' : {Affiche_la_commande(7); break;}
        case 'I' : {INFINI = true; Affiche_la_commande(8); break;} // I pour "dialogue INFINI".
        case 'J' : {Affiche_la_commande(9); break;}
        case 'K' : {Affiche_la_commande(10); break;}
        case 'L' : {Lumiere = !Lumiere; Affiche_la_commande(11); break;}
        case 'M' : {Affiche_la_commande(12); break;}
        case 'N' : {Noir = !Noir; Affiche_la_commande(13); break;}
        case 'O' : {Affiche_la_commande(14); break;}
        case 'P' : {Affiche_la_commande(15); break;}
        case 'Q' : {Affiche_la_commande(16); break;}
        case 'R' : {RESET(); Affiche_la_commande(17); break; }
        case 'S' : {Silence = !Silence; Affiche_la_commande(18); break;}
        case 'T' : {Affiche_la_commande(19); break;}
        case 'U' : {Affiche_la_commande(20); break;}
        case 'V' : {Affiche_la_commande(21); break;}
        case 'Z' : {Compteur = 0; Affiche_la_commande(22); break;}
        default : {Erreur = true; if (!Silence) BIP();}}
     if (Erreur) Serial.print(F(" Caractere non valide."));}
   else Serial.print(F(" Non commande car > 1 caractere;"));}

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

void setup() {
  u8g2.begin(); Serial.begin(38400); Serial.println(); Serial.print(F(" ESCLAVE-G : Version du "));
  Serial.println(Version); Serial.println();
  pinMode (RX,INPUT); pinMode (TX,OUTPUT); pinMode(LED_Arduino, OUTPUT);
  pinMode(BUZZER, OUTPUT); digitalWrite(BUZZER, LOW); Module_HC05.begin(9600);
  while(Module_HC05.available()) Module_HC05.read(); // Vider le tampon de RX.
  Attente_de_la_synchronisation(); INFINI = false;
  Lumiere = false;  Compteur = 0; Encadrement = true; Index = 0, Noir = false; Silence = false;}
  // Silence = false car la synchronisation provoque un 'S' de commande parasite qui rétablit
  // la variable Silence à true.

void loop() {
  if (Lumiere) digitalWrite(LED_Arduino,HIGH); else digitalWrite(LED_Arduino,LOW);
  //======================== Réception sur la ligne Bluetooth. =======================
  Attente_texte_sur_Bluetooth(); Compteur++;
  //--------------- Affichage de la consigne reçue --------------
  Affiche_TEXTE_sur_Moniteur();
  //------------- Traiter les caractères de commande ------------
  CAR_sur_RX = TEXTE[0]; Erreur = false; Traiter_les_consignes();
  //---------------- Traite l'accusé de réception ---------------
  delay(50); Module_HC05.print(Compteur); Module_HC05.print(" : >");
  Module_HC05.print(TEXTE[0]); Module_HC05.print('<');
  Serial.println(); Serial.print(F(" ACR = ")); 
  if (Erreur) {Module_HC05.println(F(" Erreur !")); Serial.print(F(" Erreur !"));}  
  else {Module_HC05.println(F(" OK !")); Serial.print(F(" OK !"));}}


