/* Utilisation de la carte Arduino avec OLED associée à un codeur incrémental de type KY-040.
   • La rotation du codeur change le n° de la fonction supposée.
   • le BP central inverse le booléen OUI.

BRANCHEMENTS à effectuer :
  • Le codeur incrémental : GND -> GND   + -> +5V     SW -> D4     DT   -> D2    CLK  -> D3
    >>>>>> ATTENTION : En fonction du codeur KY-40 approvisionné il
    >>>>>>             faudra éventuellement inverser D2 et D3 pour
    >>>>>>             obtenir un sens de variation correct.
  
                              PGM             RAM dynamique 
Compilateur v1.7.9 :   11366 Octets (36%)  1009 Octets (49%) */

//------------------------ Constantes du programme --------------------------
#define Version "23/04/2025" // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define Hiver true // Précise si l'on est en heure d'hiver ou en heure d'été. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define MAX 8 // Nombre MAXimum de "fonctions" + 1. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//------------------- Définition des broches binaires -----------------------
#define Codeur_A 2   // DT sur D2
#define Codeur_B 3   // CLK sur D3
#define BP_central 4 // SW : BP central sur D4.
#define LED_Arduino 13

//---------------------- Déclarations pour le matériel ----------------------
#include <Arduino.h>
#include <U8g2lib.h>
#include <SPI.h>
#include <Wire.h>
U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0);

//------------------------- Variables du programme --------------------------
boolean  BP_central_a_traiter, OUI;
boolean Interruption; // Gestion anti-rebonds.
boolean A_set = false;              
boolean B_set = false;
volatile unsigned int Fonction = 0; // Gestion des affichages.

//======================= Procédures d'interruptions ===========================
void Traite_Aff_Pages_A() {if (A_set && !B_set) Fonction += 1; // Incrémenter le compteur si A avant B.
     if (Fonction == MAX) Fonction = 1;}

void Traite_Aff_Pages_B() {
  if (Fonction == 0) return;
  if( B_set && !A_set ) Fonction -= 1; // Décrémenter le compteur si B avant A.
  if (Fonction == 0) Fonction = MAX - 1;}

// Interruption quand A change d'état avant B.
void doEncoderA(){
  if (Interruption) delay(5);  // Attendre un peu la fin du rebond.
  // Test pour savoir si l'état a changé :
  if( digitalRead(Codeur_A) != A_set ) {// Attendre l'état inverse.
    A_set = !A_set; Traite_Aff_Pages_A();
    Interruption = false;}} // Interruption prise en compte.

// Interruption quand B change d'état avant A.
void doEncoderB(){
  if (Interruption) delay(5);  // Attendre un peu la fin du rebond.
  // Test pour savoir si l'état a changé : 
  if(digitalRead(Codeur_B) != B_set) {// Attendre l'état inverse.
    B_set = !B_set; Traite_Aff_Pages_B();
    Interruption = false;}}

//========================= Procédures de servitude ============================

void Teste_activation_BP_central() {
  BP_central_a_traiter = false;
  if (!digitalRead(BP_central)) {// Si le B.P. central est activé :
     digitalWrite(LED_Arduino, HIGH); // Allume la LED.
     // ========== Attendre le relacher du B.P. central. ==========
     while (!digitalRead(BP_central)); // Attendre le relâché.
     digitalWrite(LED_Arduino, LOW);
     // ===========================================================
     BP_central_a_traiter = true;}}

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

void setup(void) {pinMode(LED_Arduino, OUTPUT);
  //--------------------- Initialisation de l'afficheur OLED ----------------------
  u8g2.begin(); 
  u8g2.setFont(u8g2_font_shylock_nbp_tf); // Choisir la police de caractère affichée.
  //------------------- Déclarations pour le codeur incrémental -------------------
  pinMode(Codeur_A, INPUT); pinMode(Codeur_B, INPUT); pinMode(BP_central, INPUT_PULLUP);
  attachInterrupt(0, doEncoderA, CHANGE); // Broche de l'interruption 0 (E/S 2)
  attachInterrupt(1, doEncoderB, CHANGE);} // Broche de l'interruption 1 (E/S 3)
  //------------------------ Initialisation des veriables -------------------------
  // Néant dans ce démonstrateur.

void loop(void) {
  Teste_activation_BP_central();
  if (BP_central_a_traiter) {BP_central_a_traiter = false; OUI = !OUI;}
  //--------------------------------------------------------------
  u8g2.clearBuffer(); // Efface la mémoire interne.
  u8g2.drawFrame(0,0,128,32);
  u8g2.drawStr(10,14,"Fonction active"); // Écrire le texte dans la mémoire de l'afficheur.
  u8g2.setCursor(30,28); u8g2.print(Fonction);
  u8g2.setCursor(64,28); if (OUI) u8g2.print("OUI"); else u8g2.print("NON");
  u8g2.sendBuffer();} // Transférer le texte sur l'afficheur.

