/***** RECEPTEUR (STM32WB55 LORAE5 OLED128x64) *****/
                      /****** Ajout des librairies nécessaires ******/

#include <Arduino.h>
#include <SoftwareSerial.h>         // pour ouvrir une deuxième com série
#include <Wire.h>                  // pour l'I2C
#include <Adafruit_GFX.h>          // pour l'écran oled
#include <Adafruit_SSD1306.h>      // pour l'écran oled
#include "logo.h"                  // les logos (fait pour OLED 128x64) 

                      /****** Déclaration des constantes, variables et objets ******/
// pour notre écran OLED
#define SCREEN_WIDTH    128        // OLED largeur en pixel
#define SCREEN_HEIGHT   64         // OLED hauteur en pixel
#define OLED_RESET      -1         // GPIO ou -1 si vous partagez la broche de réinitialisation Arduino)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


// pour communiquer avec entre la WB55 et le LoraE5
SoftwareSerial LoraE5(A0, A1);       // on ouvre une 2ème com série sur A0 A1 (on y branche le LoraE5 RX TX)
String inputString, message = "";    // pour la réception global et pour l'extraction du message
bool stringComplete = false;         // un flag pour savoir quand le message reçu est complet

char recv_buf[512];                 // un buffer de 512 places    
bool is_exist = false;              // un flag pour valider que le module LoraE5 répond

                    /****** Déclaration des fonctions ******/
// fonction qui efface l'écran et l'affiche vide
void clear_display() {
  display.clearDisplay();            // on efface l'écran
  display.display();                 // on affiche l'écran vide
}

// fonction qui affiche le logo st choisi à l'écran 5s puis affiche le logo coeur 
void logos(int ID) {
  display.clearDisplay();             // on efface l'écran 
  // si ID vaut 0 on dessine le logo coeur (on calcul pour centrer sur x)
  if (ID == 0) { display.drawBitmap((SCREEN_WIDTH-LOGO_COEUR_WIDTH)/2, 0, COEUR, LOGO_COEUR_WIDTH, LOGO_COEUR_HEIGHT, WHITE);} 
  // si ID vaut 0 on dessine le 1er logo ST 
  else if (ID == 1) { display.drawBitmap((SCREEN_WIDTH-LOGO_ST_WIDTH)/2, 0, LOGO_ST, LOGO_ST_WIDTH, LOGO_ST_HEIGHT, WHITE);}
  // sinon (ID vaut 2) on dessine le 2ème logo ST 
  else         {display.drawBitmap(0, 0, LOGO_STM32, LOGO_STM32_WIDTH, LOGO_STM32_HEIGHT, WHITE);}
  display.display();                  // on affiche le logo à l'écran
}

              
// fonction qui sert a envoyer des commandes AT au module LoraE5 et retourne la réponse du module
int commande_AT_LoraE5(char *check_reponse, int timeout_ms, String at_commande) {
  int temps, nouveau_byte, place = 0;                             // on déclare 3 variables de type int 
  memset(recv_buf, 0, sizeof(recv_buf));                          // on rempli le buffer de 0
  Serial.print(at_commande);                                      // on affiche la commande AT qu'on envoie au module LoraE5
  LoraE5.print(at_commande);                                      // on envoie la commande au module LoraE5
  delay(100);                                                     // on attend un petit peu
  temps = millis();                                               // on récupère le temps du compteur millis
  if (check_reponse == NULL) { return 0; }                        // si le paramètre check_reponse est null on retourne 0 
  do {                                                            // on fait (tant qu'on est pas au timeout ou qu'on ne retourne pas 1)
      while (LoraE5.available() > 0) {                              // pour chaque byte qui arrive dans la com série avec le module LoraE5
          nouveau_byte = LoraE5.read();                               // on le récupère
          recv_buf[place++] = nouveau_byte;                           // on se décale dans le buffer et remplace le 0 par le nouveau byte 
          Serial.print((char)nouveau_byte);                           // on affiche dans la com série le byte reçu sous forme de char
          delay(2);                                                   // on attend un petit peu
      }
      if (strstr(recv_buf, check_reponse) != NULL) { return 1; }    // si recv_buf reçu correspond à check_reponse on retourne 1
  } while (millis() - temps < timeout_ms);                        // tant qu'on est pas au timeout  
  return 0;                                                       // si on est au timeout on retourne 0
}

// fonction pour extraire les datas du message et agir en fonction
void decode_message(char delimiter, String msg){      // deux paramètres: le délimiteur et le message a décoder
  int i1 = msg.indexOf(delimiter);                    // on récupère la place du premier délimiteur dans la string
  String nom = msg.substring(0, i1);                  // on extrait le contenu du début de la string au premier délimiteur (le nom)  
  if (nom == "LOGO") {                           // si le nom est LOGO ( msg sous forme: LOGO|0| ou LOGO|1| )
    int i2 = msg.indexOf(delimiter, i1+1);            // on récupère la place du deuxième délimiteur dans la string
    int choix = (msg.substring(i1 + 1, i2)).toInt();  // on extrait le contenu entre le premier et le deuxième délimiteur (0 ou 1) converti en int
    logos(choix);                                   // on affiche le logo correspondant au choix sur l'écran 
  }
}

// la fonction qui attend et traite les messages reçus
int attend_msg(void) {
  int nouveau_byte, place = 0;                                            // on déclare 2 variables de type int 
  memset(recv_buf, 0, sizeof(recv_buf));                                  // on rempli le buffer de 0
  while (LoraE5.available() > 0) {                                        // pour chaque byte qui arrive dans la com série avec le module LoraE5
    nouveau_byte = LoraE5.read();                                           // on le récupère
    recv_buf[place++] = nouveau_byte;                                       // on se décale dans le buffer et remplace le 0 par le nouveau byte 
    Serial.print((char)nouveau_byte);                                       // on affiche dans la com série le byte reçu sous forme de char
    delay(2);                                                               // on attend un petit peu
  }
  if (place) {                                                             // si place > 0, c'est qu'un message est arrivé
    String inputString = String(recv_buf);                                   // on récupère le buffer dans une string
    int first_place = inputString.indexOf('"');                              // on cherche la place du premier guillemet dans la string
    int second_place = inputString.lastIndexOf('"');                         // on cherche la place du dernier guillemet dans la string
    String msgHEX = inputString.substring(first_place + 1, second_place);    // on extrait ce qui est entre guillemet dans la string
    Serial.println(msgHEX);                                                  // on affiche dans la com série le message extrait (il est toujours en hexadécimale)
    String message = hexToString(msgHEX);                                    // on convertie en ASCII le message et le place dans une string    
    Serial.println(message);                                                 // on affiche dans la com série le message extrait en ASCII
    decode_message('|', message);
    return 1;                                                                // on retourne 1
  }
  return 0;                                                                // sinon on retourne 0
}

// fonction pour convertir une string en hexadécimale vers une string en ASCII
String hexToString(String hex_text) {                  
  String ascii_text = "";                                     // on déclare une string vide pour réceptionner les caractères convertis
  for (int i = 0; i < hex_text.length(); i++) {               // pour chaque caractère du message à convertir :
    if (i % 2 != 0) {                                           // si la place de ce caractère dans le message est impaire :
      char temp[3];                                               // on créé un tableau vide de 3 (FF = 255)
      sprintf(temp, "%c%c", hex_text[i - 1], hex_text[i]);        // on y place le caractère précédent et ce caractère
      int number = (int)strtol(temp, NULL, 16);                   // on converti cette partie hexadécimale en int
      ascii_text += char(number);                                 // on ajoute dans notre string de réception le char correspondant
    }
  }
  return ascii_text;                                          // on retourne notre string de réception en ASCII
}

void setup(void) {                
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c);                                      // initialise l'écran, adresse I2C : 0x3C 
  display.clearDisplay();                                                         // on efface l'écran   
  logos(0);delay(500);logos(1);       
  delay(1500);
  logos(0);delay(500);logos(2); 
  delay(1500);      
  clear_display();                
  Serial.begin(9600);                                                                             // on démarre la com série avec le pc
  LoraE5.begin(9600);                                                                             // on démarre la com série avec le module LoraE5
  Serial.println("****  Récepteur STM32WB55 LoraE5 ****");                                        // on écrit dans la com série avec le pc pour infos   
  Serial.println("Configuration du module LoraE5 en mode réception");                             // on écrit dans la com série avec le pc pour infos        
  if (commande_AT_LoraE5("+AT: OK", 1500, "AT\r\n")) {                                            // si la fonction commande_AT_LoraE5 retourne 1 :
    is_exist = true;
    delay(500);                                                                                     // on attend un petit peu
    commande_AT_LoraE5("+MODE: TEST", 1500, "AT+MODE=TEST\r\n");                                    // on passe le module LoraE5 en mode test
    delay(500);                                                                                     // on attend un petit peu
    commande_AT_LoraE5("+TEST: RFCFG", 1500, "AT+TEST=RFCFG,866,SF12,125,12,15,14,ON,OFF,OFF\r\n"); // on configure la partie RF du module LoraE5
    delay(500);                                                                                     // on attend un petit peu      
    commande_AT_LoraE5("+TEST: RXLRPKT", 1500, "AT+TEST=RXLRPKT\r\n");                              // on passe le module LoraE5 en mode réception
    delay(500);                                                                                     // on attend un petit peu 
    Serial.println();                                                                               // on espace d'une ligne       
    Serial.println("Module LoraE5 en mode récéption");                                              // on écrit dans la com série avec le pc pour infos 
  } else {                                                                                        // sinon (la fonction commande_AT_LoraE5 retourne 0) :
    is_exist = false;                                                                               // le module LoraE5 est non présent ou défaillant 
    Serial.println("Aucun module LoraE5 de trouvé");                                                // on écrit dans la com série avec le pc pour infos
  }
  Serial.println();
}

void loop(void) {
  if (is_exist)  {                                                      // si le module LoraE5 est présent et fonctionnel :
    if (attend_msg()) {                                                   // si la fonction attend_msg retourne 1
      Serial.println("**** réception, conversion et traitement ok ****");   // on écrit dans la com série
      Serial.println();                                                     // on espace d'une ligne 
    }
  }
}