/* Programme contenant les routines de base pour développer des programmes sur la platine
   d'expérimentation initialement prévue pour programmer des modules BLUETUUTH. Ce noyau conditionne les
   Entrées/Sorties pour pouvoir exploiter directement :
   • L'afficheur OLED de la carte NANO étendue.
   • Le BUZZER actif.
   • Le bouton poussoir.
   • Éventuellement la LED Arduino.
   • Un dialogue via le Moniteur de l'IDE initialisé à 38400 baud.
   Si en développement un dialogue via la ligne USB du Moniteur de l'IDE n'est pas envisagé, toutes les
   procédures relatives à ce dernier sont regroupées pour pouvoir les retirer facilement.

BRANCHEMENTS à effectuer : Aucun si ce n'est plus tard des composants additionnels pour développer.
--------------------------
   ATTENTION : Si la carte Arduino utilisée est de type étendu avec écran OLED :
               A4 est réservé par la bibliothèque U8G2lib.h pour SDA.
               A5 est réservé par la bibliothèque U8G2lib.h pour SLC.
   
Compilateur v1.7.9.             PGM               RAM dynamique
Programme NOYAU :         14362 Octets (46%)    1201 Octets (58%) */

//------------------------ Constantes du programme --------------------------
#define Version "20/08/2025." // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define Taille_PGM 15204 // Taille du programme. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define Delai_voulu 75 // Demi période du clignottement en mS.

//------------------- Définition des broches binaires -----------------------
// Les broches choisies sont séparées pour les écarter et faciliter les branchements provisoires.
#define UT1 6 // Se trouve directement sur une broche du connecteur.
#define UT2 11 // Se trouve sur une broche du connecteur avec diviser de tension "3,3V.
#define BUZZER 12 // Va sur le bruiteur actif.
#define BP 14 // Bouton poussoir sur A0.
#define LED_Arduino 13
//---------------------- Déclarations pour le matériel ----------------------
#include <U8g2lib.h>   // L'afficheur est sur A4 et A5 pour SDA et SCL.
#include <SPI.h>
#include <Wire.h>
U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0);

//------------------------- Variables du programme --------------------------
unsigned long Temps_Ecoule_depuis_RESET = 0;

//--------- Variables si commandes par Moniteur de l'IDE conservées ---------
#define Lgr_TEXTE 22 // Longueur maximale d'un ACR Esclave @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#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.
boolean Encadrement, Noir, Silence;
char CAR_Moniteur;
byte Compteur, Index;
char TEXTE[Lgr_TEXTE];

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

void Inverser_LED_Arduino() {digitalWrite(LED_Arduino,!digitalRead(LED_Arduino));}

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 Affiche_page_accueil_sur_OLED() {
 u8g2.clearBuffer(); // Efface la mémoire interne.
    if (Encadrement) u8g2.drawFrame(0,0,128,32);
    u8g2.setFont(u8g2_font_shylock_nbp_tf); // Choisir la police de caractère affichée.
    // Chaque appel à un texte impose dans la procédure de redéfinir la police de caractères.
    u8g2.setCursor(14,14);u8g2.print(F("Pile - Tas = ")); u8g2.print(SRAM_LIBRE()); u8g2.print('.');
    u8g2.setCursor(14,28); u8g2.print(F("Rev ")); u8g2.print(Version);
  u8g2.sendBuffer();} // Transférer le texte sur l'afficheur.

//================= Procédures pour les commandes Moniteur ===================
void Saisir_une_commande_USB() { // Effectuer une SAISIE sur le Moniteur de l'IDE.
  if (Serial.available()) {Index = 0;
     while (Serial.available()) {CAR_Moniteur = Serial.read();
            if ((Index < Lgr_TEXTE) && (CAR_Moniteur != CR) && (CAR_Moniteur != LF)) {
                //---------- 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().
                //-----------------------------------------------------
                TEXTE[Index] = CAR_Moniteur; Index++;} delay(5);}}} // Délai impératif.

void Prompt_commande_USB() { Serial.println(); Serial.print(F(" Commande > "));}

void AFF_la_commande() {
  Serial.print('['); for (byte I = 0; I < Index; I++) Serial.print(TEXTE[I]); Serial.print(F("]  (Nb = "));
  if (TEXTE[0] != 'Z') Serial.print(Compteur+1); else Serial.print(0); Serial.println(')');}

void Ecran_commande() {
  u8g2.clearBuffer(); // Efface la mémoire interne.
    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.
      // Chaque appel à un texte impose dans la procédure de redéfinir la police de caractères.
      u8g2.setCursor(14,14);u8g2.print(F("Commande ")); u8g2.print(Compteur); u8g2.print(F(" = ")); 
      if (Index == 1) u8g2.print(TEXTE[0]);
      else {u8g2.setCursor(14,28); u8g2.print(F(" Lgr TEXTE > 1."));} u8g2.sendBuffer();}}

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

void Liste_les_commandes() {
   Serial.println(); Serial.println(F(" Rappel des commandes :")); Souligner(22);
   Serial.println(F(" a ou A : Alarme. (BIP)"));
   Serial.println(F(" d ou D : Discret. (BIPs NON.)"));
   Serial.println(F(" e ou E : Encadrement OUI/NON."));
   Serial.println(F(" l ou L : Liste les commandes."));
   Serial.println(F(" n ou N : Ecran noir OUI/NON."));
   Serial.println(F(" o ou O : Etat des options."));
   Serial.println(F(" r ou R : RESET des OPTIONS. >>>> BIP redevient actif !"));
   Serial.println(F(" p ou P : Presentation du logiciel."));
   Serial.println(F(" s ou S : Silence OUI/NON."));
   Serial.print(F(" z ou Z : Remise a zero du compteur de commandes.")); Serial.println();}

void RESET_OPTIONS() {Encadrement = true; Noir = false; Silence = false;}

void Aff_OUI_NON(boolean OUI) {if (OUI) Serial.print(F("OUI.")); else Serial.print(F("NON."));}

void Liste_les_options() {
  Serial.println(); Serial.print(F(" Encadrement = ")); Aff_OUI_NON(Encadrement);
  Serial.print(F(" Silence = ")); Aff_OUI_NON(Silence); Serial.print(F(" Ecran Noir = "));
  Aff_OUI_NON(Noir); Serial.println();}

void Traite_la_commande() {
  switch(TEXTE[0]) {
     case 'A' : {BIP(); break;}
     case 'D' : {Silence = true; Serial.println(); Serial.print(F(" Mode discret")); break;}
     case 'E' : {Encadrement = !Encadrement; Serial.println(); Serial.print(F(" Encadrement = "));
                 Aff_OUI_NON(Encadrement); Ecran_commande(); Serial.println(); break;}
     case 'L' : {Liste_les_commandes(); break;}
     case 'N' : {Noir = !Noir; if (Noir) {u8g2.clearBuffer();u8g2.sendBuffer();}
                               else Ecran_commande(); Serial.println(); Serial.print(F(" Ecran Noir = "));
                 Aff_OUI_NON(Noir); Serial.println(); break;}
     case 'O' : {Liste_les_options(); break;}
     case 'P' : {Affiche_page_accueil_sur_OLED(); Aff_presentation_sur_USB(); break;}
     case 'R' : {RESET_OPTIONS(); Liste_les_options(); Ecran_commande(); break;}
     case 'S' : {Silence = !Silence; Serial.println(); Serial.print(F(" Silence = "));
                 Aff_OUI_NON(Silence); Serial.println(); break;}
     case 'Z' : {Compteur = 0; Ecran_commande(); break;}
     default : {BIP(); Serial.print(F(" Caractere non valide !")); Serial.println();
               u8g2.setCursor(14,28); u8g2.print(F(" Car NON valide !")); u8g2.sendBuffer();}}}

void Aff_presentation_sur_USB() {
  Serial.println(); Serial.print(F(" NOYAU Programmateur : Version du "));
  Serial.println(Version); Serial.print(F(" Pile - Tas = ")); Serial.print(SRAM_LIBRE());
  Serial.println('.'); Serial.println();}

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

void setup() {
  Serial.begin(38400); Aff_presentation_sur_USB(); pinMode(LED_Arduino,OUTPUT);
  pinMode(UT1,OUTPUT); digitalWrite(UT1,LOW);
  pinMode(UT2,OUTPUT); digitalWrite(UT2,HIGH); // Éteindre la LED blanche en logique négative.
  pinMode(BUZZER,OUTPUT); digitalWrite(BUZZER,LOW); RESET_OPTIONS();
  u8g2.begin(); Affiche_page_accueil_sur_OLED(); BIP(); Liste_les_commandes();
  Prompt_commande_USB();}

void loop() {
  //--------------------- Attendre un clic sur le B.P. ----------------------
  while((analogRead(BP) > 511) && (Index == 0)) {
    //================= Chronométrage pour le délai écoulé ================
          if (millis() - Temps_Ecoule_depuis_RESET > Delai_voulu)  {
          Temps_Ecoule_depuis_RESET = millis(); Inverser_LED_Arduino(); Saisir_une_commande_USB();}}
    //=====================================================================
  //--------------- Inverse l'état des LEDs blanches et orange --------------
  if (Index == 0) {digitalWrite(UT1,!digitalRead(UT1)); digitalWrite(UT2,!digitalRead(UT2));
                   TEXTE[0] = 'B'; TEXTE[1] = '.'; TEXTE[2] = 'P'; TEXTE[3] = '.'; Index = 4;}
  //--------------------- Attendre le relâcher du  B.P. ---------------------
  digitalWrite(LED_Arduino,HIGH); while(analogRead(BP) < 511); // Ne rien faire.
  digitalWrite(LED_Arduino,LOW);
  //----------------- Traitement de la commande du Moniteur -----------------
  AFF_la_commande(); // Affiche la lettre de commande entre crochets.
  if (Index > 0) {Compteur++; Ecran_commande();
  if (Index == 1) Traite_la_commande(); Index = 0;} Prompt_commande_USB();}



