HC-12 Changements de canal et envoi de données float et string

Bonjour, désolé de vous solliciter derechef.
Suite à mon montage anémomètre qui marche très bien avec un nano et un mega 2560, je tente de passer au même projet, mais avec trois nanos. (Un seul nano récepteur ne peut encaisser suffisamment de mémoire pour un LCD, une horloge temps réel et une carte SD). Ci-joint le schéma global. Je choisirai ensuite celui des deux montages fonctionnant le mieux.

HC-12_ANEMO_61_Emetteur-Récepteur-LCD_Récepeur-DS3231-Carte SD.zip (73.5 KB)

Le HC-12(1) du nano émetteur envoie les signaux de l’anémomètre donnant ainsi la fréquence qui est reçue par le HC-12(2) du nano récepteur A. Ce dernier effectue des calculs (void « CALCULATEUR() ») pour différentes données affichées sur un LCD I2c 20X4 avec le void « AFFICHEUR ()». Cette étape fonctionne très bien. Voici le code du recepteur A :

/*
  LE PROGRAMME UTILISE LE HC-12 B du Namo 2 POUR RECEVOIR TOUTES LES SECONDES LA VALEUR DE LA fREQUENCE
  LE CALCULATEUR PASSE LES DONNEES A L'AFFICHEUR POUR L'ECRAN LCD I2C 20x4
  TOUTES LES MIN LE HC-12 B PASSE AU CANAL 2 POUR ENVOYER LES DONNEES AU HC-12 C PUIS REPREND LE CANAL 1
*/


// INCLUSION LIBRAIRIES ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <Arduino.h>
#include <LiquidCrystal_I2C.h>  //LCD_TAG
#include <Wire.h>
LiquidCrystal_I2C lcd(0x27,20,4);  // LCD_TAG set the LCD address to 0x27 for a 20 chars and 4 line display

// Déclaratiion HC-12
#include <SoftwareSerial.h>
SoftwareSerial hc12(8,9);     // OK pour le NANO !

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*************************************************************************************************************************************************************************************************************************
// COMMENTAIRES CONFIGURATION HARDWARE  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// D3 = INPUT logic pour remise à zero v max par interruption
// D4 = OUTPUT logic anemo pour diode lumineuse signal anemometre
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*************************************************************************************************************************************************************************************************************************
// ALLOCATION INPUT/OUTPUT ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

const byte ResetVmaxPin = 3;    // D3 = INPUT, remise à zero v max par front changeant
const byte pinSDA = 4;          // D4 pour le SDA de l"écran LCD
const byte pinSCL = 5;          // D5 pour le SCL de l"écran LCD

const byte pinSet_HC12 = 7;     // Pour mise à 0 pour changer de canal

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*************************************************************************************************************************************************************************************************************************
// DECLARATION VARIABLES //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Données enregistrements à envoyer par HC-12 B à HC-12 C du 3ème Nano
int x = 0;  //rang enregistrement
int data0;
float data1;
float data2;
float data3;
float data4;
String data5 = "";
String data6 = "";
String typeRafale;
String typeVent;

// Timing 1 min période d'envoi des données pour enregistrements sur carte SD du Nano 3
unsigned long tpsAct_1MIN = 0;
static unsigned long  tpsPrec_1MIN = 0;
unsigned long delta_T = 0;
const unsigned long duree = 20000UL;


// Grandeurs Vinst, Vmax et Vmoy
boolean affiche = 1;                    // si le v_max_inst (v instantanée max obtenue depuis dernier affichage) a été affiché, il ne l'affichera plus et passera à la prochaine valeur obtenue suite nouveau front montant
byte grandeur_v_max_inst = 0;           // entre deux affichages, prend la valeur max des vitesse instantanées obtenues et l'affiche comme vitesse instantanée
byte grandeur_v_max = 0;                // vitesse max relevée depuis lancement du programme, se remet à zero manuellement via pin d'interruption D3
byte grandeur_v_moy = 0;

//
static unsigned long temps_precedent = 0;      // temps précédent (µs) est une mémoire qui permet le calcul le delta t entre deux fronts desc. (avec "nouveau_temps"
float v = 0;                                   // vitesse calculée entre deux fronts descendants
float v_precedente = 0;                 // vitesse calculée entre deux fronts descendants
byte nb_v_calc = 0;
byte depassement = 0; 
float v_max = 0;                        // vitesse max obtenue depuis le lancement du programme, sera mise à zéro manuellement par une interruption.
float v_max_inst = 0;                   // vitesse max instantanée obtenue depuis dernier affichage des valeurs
float v_max_inst_bargraph = 0;
static unsigned long duree_v_nulle = 0; // durée pendant laquelle aucun front montant anemomètre => sert à mettre à 0 V_max_inst et donc pour afficher vitesse nulle
boolean memoire_vmax_zero = 0;
static unsigned long TEMPS_milli = 0;   // variable temps en ms pour les besoins de la cadence d'affichage
static unsigned long TEMPS_micros = 0;  // variable temps en 
unsigned long t_debut_affichage = 0;    // pour gérer cadence affichage
unsigned long t_ecoule_affichage = 0;   // pour gérer cadence affichage

unsigned long t_debut_bargraph = 0;     // pour gérer cadence affichage
unsigned long t_ecoule_bargraph = 0;    // pour gérer cadence affichage

//////// CONSTANTES POUR VITESSE MOYENNE GLISSANTE SUR 10s  //////////////////////////
////          ///////  int NB_PULSE = 0                                      // incrément du nombre de fronts descendants pour 10s
static unsigned int NB_PULSE_10s_INCREMENT = 0;       // incrément du nombre de fronts descendants vus en 10s
int MOY_temps_ecoule = 0;                             // temps écoulé sur l'observation du nombre de fronts descendants
unsigned long MOY_temps_debut = 0;                    // temps de départ fenêtre de moyennage 10s
unsigned int NB_PULSE_10s_TOTAL = 0;                  // nb fronts descendants obtenus  sur 10s
int MOY_temps_ecoule_10s = 0;                         // temps écoulé de 10s

int i = 0;
unsigned int PULSE[60];             // tableau de 60 valeurs (chacune des valeurs est le nb de fronts descendants observés en 10s. 60x10s=600s=10mn
float nb_blocs_10s_calcules = 0;    // nombre de valeurs rentrées dans le tableau PULSE (sert quand le tableau n'est pas complètement rempli de ne calculer la V moy que sur les valeurs entrées 
float nb_pulses_10mn = 0;           // somme des 60 valeurs du tableau PULSE
float MOY_VMOY = 0;                 // vitesse moyenne obtenue sur 10mn , donc moyenne des 60 valeurs du tableau PULSE

/// assignation durée précise (10s) sur chacun des blocs de 10s dans le tableau MOY_DUREE_BLOC
unsigned int DUREE_BLOC_10s[60];    // durée exacte des blocs de 10s (60 blocs de 10 s donne 10mn)
float DUREE_BLOC_10s_SOMME = 0;     // somme durée exacte des blocs de 10s (60 blocs de 10 s donne 10mn)
float DUREE_BLOC_10s_MOY = 0;       // moyenne de durée exacte des blocs de 10s

float RAFALES_MAX_10_DERNIERES_MN = 0;
byte grandeur_M_10_D_MN = 0;
//// CONSTANTES POUR tableau des écarts (rafales)

float v_max_bloc_10s = 0;           // vmax observée sur un bloc de 10s, même bloc que pour calcul moy sur bloc 10s
float vitesse_relevee = 0;    
float vitesse_max_10mn = 0;

float V_max_bloc[60];
byte grandeur_vitesse_max_10mn = 0;

byte classification_vent = 0;
byte classification_rafales = 0;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************
// CONSTANTES DE REGLAGE /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

unsigned long cadence_affichage = 1000;                        // cadence affichage (ms)
unsigned long cadence_bargraph = 200;
volatile unsigned long duree_remise_zero = 2500000; // valeur en µs, durée 2.5s à partir de laquelle sans signal anemomètre, la vitesse instantannée est mise à zero

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************
// CONSTANTES BARRE DE PROGRESSION ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// /* Constantes pour la taille de l'écran LCD */
const int LCD_NB_ROWS = 4;
const int LCD_NB_COLUMNS = 20;
// Valeur en pourcentage de la barre de progression */
static byte percent = 0;
static float pourcentage = 0;

/* Caractères personnalisés */
byte DIV_0_OF_5[8] = {
  B00000, 
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
}; // 0 / 5

byte DIV_1_OF_5[8] = {
  B00000, 
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000
}; // 1 / 5

byte DIV_2_OF_5[8] = {
  B00000, 
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000
}; // 2 / 5 

byte DIV_3_OF_5[8] = {
  B00000, 
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100
}; // 3 / 5

byte DIV_4_OF_5[8] = {
  B00000, 
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110
}; // 4 / 5

byte DIV_5_OF_5[8] = {
  B00000, 
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111
}; // 5 / 5

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************************************
// SETUP PROGRESS BAR //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 // Fonction de configuration de l'écran LCD pour la barre de progression.
 // Utilise les caractères personnalisés de 0 à 5 (6 et 7 restent disponibles).
 
void setup_progressbar() 
{
  /* Enregistre les caractères personnalisés dans la mémoire de l'écran LCD */
  lcd.createChar(0, DIV_0_OF_5);
  lcd.createChar(1, DIV_1_OF_5);
  lcd.createChar(2, DIV_2_OF_5);
  lcd.createChar(3, DIV_3_OF_5);
  lcd.createChar(4, DIV_4_OF_5);
  lcd.createChar(5, DIV_5_OF_5);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************************************
// VOID CALCULATEUR_10mn ////// VOID CALCULATEUR_10mn //////// VOID CALCULATEUR_10mn //////// VOID CALCULATEUR_10mn ////////// VOID CALCULATEUR_10mn //////

void CALCULATEUR_10mn()
{
  MOY_temps_ecoule = TEMPS_milli - MOY_temps_debut;  // pour faire des paquets d'observation de 10s (sert à remplir le tableau PULSE)

  if (MOY_temps_ecoule >= 10000)      // toutes les 10s, crée un bloc, total de 60 blocs. 
  {
    if(nb_blocs_10s_calcules < 61)    // une fois atteint le nombre de 60 valeurs, on n'incrémente plus le nombre de valeurs présente dans le tableau PULSE
    {
      nb_blocs_10s_calcules++;      // pour première valeur erronnée "NB_PULSE_10s_TOTAL",  nb_blocs_10s_calcules vaut 1. 
      //Vaut 2 quand première valeur bonne. Donc nb valeurs bonne dans PULSE[] vaut (nb_blocs_10s_calcules-1)
    }

    NB_PULSE_10s_TOTAL = NB_PULSE_10s_INCREMENT;              // durant 10s, NB_PULSE_10s_INCREMENT a été incrémenté (nombre de front descendants observés. Cette valeur alimentera une case du tableau PULSE (10s par case)
    NB_PULSE_10s_INCREMENT = 0;                       // compteur de fronts descendants remis à 0 en vu de sa nouvelle incrémentation à venir pour le prochain paquet de 10s
    MOY_temps_debut = TEMPS_milli;    // relève l'instant de départ de la nouvelle fenêtre d'observation de 10s
    MOY_temps_ecoule_10s = MOY_temps_ecoule; 

    /// ETAPE 1 - 1/3 => DECALER TABLEAU PULSE///
    if(nb_blocs_10s_calcules > 2)    // à la première moy suite aux 10s, la valeur est faussée et nb_blocs_10s_calcules est à 1. 
    {
      //Donc c'est à partir de > 2 que je décale vers le bas les valeurs afin de faire entrer les nouvelles 
      for(i = 59;i > 0;i = i-1)
      {
        PULSE[i] = PULSE[i-1];
      }
    }

    /// ETAPE 1 - 2/3 => DECALER TABLEAU MOY_DUREE_BLOC///
    if(nb_blocs_10s_calcules > 2)    // à la première moy suite aux 10s, la valeur est faussée et nb_blocs_10s_calcules est à 1. Donc c'est à partir de > 2 que je décale vers le bas les valeurs afin de faire entrer les nouvelles 
    {
      for(i = 59;i > 0;i = i-1)
      {
        DUREE_BLOC_10s[i] = DUREE_BLOC_10s[i-1];
      }
    }

    /// ETAPE 1 - 3/3 => DECALER TABLEAU v_max_bloc_10s ///
    if(nb_blocs_10s_calcules>2)      // à la première moy suite aux 10s, la valeur est faussée et nb_blocs_10s_calcules est à 1. Donc c'est à partir de > 2 que je décale vers le bas les valeurs afin de faire entrer les nouvelles 
    {    
      for(i = 59 ;i > 0 ;i = i-1){
        V_max_bloc[i] = V_max_bloc[i-1];
      }
    }

    /// ETAPE 2 1/3 => FAIRE ENTRER NOUVELLE VALEUR DANS L'INDICE 0 DE PULSE ///
      PULSE[0] = NB_PULSE_10s_TOTAL;
    //////////////////////////////////

    /// ETAPE 2 2/3 => FAIRE ENTRER NOUVELLE VALEUR DANS L'INDICE 0 MOY DUREE BLOC ///
      DUREE_BLOC_10s[0] = MOY_temps_ecoule_10s;
    //////////////////////////////////

    /// ETAPE 3 1/3 => CALCUL SOMME du nb de pulse par bloc de 10s (somme de PULSE) ///
    nb_pulses_10mn = 0;
    for(i = 0;i < 60;i++)
    {
      nb_pulses_10mn = nb_pulses_10mn + PULSE[i];
    }
    //////////////////////////////////

    /// ETAPE 3 2/3 => CALCUL SOMME DES DUREES DE BLOC sur tout le tableau MOY_DUREE_BLOC ///
    DUREE_BLOC_10s_SOMME = 0;  // cette somme de DUREE de tous les 60 blocs sera en ms, 10s=10000ms, 10mn=600s = 600000ms
    for(i = 0;i < 60;i++)
    {
      DUREE_BLOC_10s_SOMME = DUREE_BLOC_10s_SOMME + DUREE_BLOC_10s[i];
    }
    //////////////////////////////////
     
     DUREE_BLOC_10s_MOY = (DUREE_BLOC_10s_SOMME / (nb_blocs_10s_calcules - 1));
     //MOY_VMOY=(nb_pulses_10mn/(nb_blocs_10s_calcules-1)*180/192)/(DUREE_BLOC_10s_MOY*0.001);   // V=F*(180/192), cette v moyenne est sur un bloc de 10s moyen, vitesse par 10s en fait
     MOY_VMOY = 3 + (nb_pulses_10mn / (nb_blocs_10s_calcules-1) * 0.8) / (DUREE_BLOC_10s_MOY * 0.001);       // V=0.8 x F+3

    /// ETAPE 2 3/3 => FAIRE ENTRER NOUVELLE VALEUR DANS tableau V_max_bloc ///
      V_max_bloc[0] = v_max_bloc_10s;
      v_max_bloc_10s = 0;
    //////////////////////////////////

    /// ETAPE 3 3/3 => CALCUL MAX sur 10 dernières mn / sur chacun des blocs de 10s 
    vitesse_max_10mn = 0;  // initialisation à 0 de la vitesse max avant analyse des 60 blocs de 10s ci-dessous
    for(i = 0;i < 60;i++)
    {
      vitesse_relevee = V_max_bloc[i];
      if(vitesse_relevee > vitesse_max_10mn)
      {
        vitesse_max_10mn = vitesse_relevee;  // DU COUP EN FAIT vitesse_max représente V MAX sur les 10 dernières MN
      }
    }
    //////////////////////////////////

    ////////////////// CALCUL DES RAFALES MAX sur les 10 dernières minutes  //////////////////////

  RAFALES_MAX_10_DERNIERES_MN = vitesse_max_10mn - MOY_VMOY; // EN FAIT MAX_10_DERNIERES_MN représente RAFALES sur 10 dernières mn (max ecart par rapport moyenne)

    /////////////////////////////////////////////////////////////////////////////////////////////////////////
    
  }  //////////// FIN SI MOY temps écoulé > 10s //////////////////////// FIN SI MOY temps écoulé > 10s ///////////
  
} //// FIN VOID "CALCULATEUR_10mn"//// FIN VOID "CALCULATEUR_10mn"//// FIN VOID "CALCULATEUR_10mn"//// FIN VOID "CALCULATEUR_10mn"//// FIN VOID "CALCULATEUR_10mn"
 

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************************************
// VOID AFFICHEUR //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void AFFICHEUR(){

//////////////////////////////LIGNE 1 - AFFICHAGE V instananée ////////////////////////////////////////// 

if (v_max_inst < 9.95)  // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
{
        grandeur_v_max_inst = 1;
}

if (v_max_inst < 99.95 && v_max_inst >= 9.95) // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
{
        grandeur_v_max_inst = 2;
}

if (v_max_inst <999.95 && v_max_inst >= 99.95) // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
{
        grandeur_v_max_inst = 3;
}

 lcd.setCursor (0,0);                           // LCD_TAG go to start of 1er line
  switch(grandeur_v_max_inst){
    case 1: lcd.print("V / max:   ");break;     // LCD_TAG
    case 2: lcd.print("V / max:  ");break;
    case 3: lcd.print("V / max: ");break;
  }
  lcd.print(v_max_inst, 1);      // LCD_TAG
  
/////////////////////////////// LIGNE 1 -  AFFICHAGE v max //////////////////////////////////////

if (v_max < 9.95) // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
{
        grandeur_v_max = 1;
}

if (v_max < 99.95 && v_max >= 9.95) // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
{
        grandeur_v_max = 2;
}

if (v_max < 999.95 && v_max >= 99.95) // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
{
        grandeur_v_max = 3;
}

  switch(grandeur_v_max)
  {
    case 1: lcd.print("   ");break;           // LCD_TAG
    case 2: lcd.print("  ");break;
    case 3: lcd.print(" ");break;
  }
lcd.print(v_max,1);           // LCD_TAG

///////////////////////////// LIGNE 2 -  AFFICHAGE v moy //////////////////////////////////////////

if (MOY_VMOY <= 3.1)     //// AJOUT pour passer à 0 la vitesse moyenne si elle est < 3.1 (calcul dit v=3.0 si aucun vent)
{
        grandeur_v_moy = 0;
        MOY_VMOY = 0;
}
if (MOY_VMOY < 9.5 && MOY_VMOY > 3.1) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
{
        grandeur_v_moy = 1;
}

if (MOY_VMOY < 99.5 && MOY_VMOY >= 9.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
{
        grandeur_v_moy = 2;
}

if (MOY_VMOY < 999.5 && MOY_VMOY >= 99.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
{
        grandeur_v_moy = 3;
}

lcd.setCursor (0,1);          // LCD_TAG go to start of 2nd line

  switch(grandeur_v_moy)
  {
    case 0: lcd.print("moy/R/M:  <3   /   /");break;
    case 1: lcd.print("moy/R/M:   ");break;           // LCD_TAG
    case 2: lcd.print("moy/R/M:  ");break;
    case 3: lcd.print("moy/R/M: ");break;
  }

if (grandeur_v_moy > 0)  //// AJOUT pour passer à 0 la vitesse moyenne si elle est < 3.1 (calcul dit v=3.0 si aucun vent)
{
  lcd.print(MOY_VMOY,0);          
}    

//////////////////////////////LIGNE 2 -  AFFICHAGE RAFALES MAX SUR LES 10 DERNIERES MINUTES //////////////////////

if (grandeur_v_moy == 0)
{
  grandeur_M_10_D_MN = 0;
}
if (RAFALES_MAX_10_DERNIERES_MN < 9.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
{
        grandeur_M_10_D_MN = 1;
}
if (RAFALES_MAX_10_DERNIERES_MN < 99.5 && RAFALES_MAX_10_DERNIERES_MN >= 9.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
{
        grandeur_M_10_D_MN = 2;
}
if (RAFALES_MAX_10_DERNIERES_MN < 999.5 && RAFALES_MAX_10_DERNIERES_MN >= 99.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
{
        grandeur_M_10_D_MN = 3;
}
  switch(grandeur_M_10_D_MN)
  {
    case 0: break;
    case 1: lcd.print("   ");break;          
    case 2: lcd.print("  ");break;
    case 3: lcd.print(" ");break;
  }
if (grandeur_v_moy>0)
{
  lcd.print(RAFALES_MAX_10_DERNIERES_MN, 0);
}
/////////////////////  LIGNE 2 -  AFFICHAGE vitesse max sur les 10 dernières mn (60 blocs de 10s analyses) /////////////

if (grandeur_v_moy == 0)
{
  grandeur_vitesse_max_10mn = 0;
}
if (vitesse_max_10mn < 9.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
{
        grandeur_vitesse_max_10mn = 1;
}

if (vitesse_max_10mn<99.5 && vitesse_max_10mn >= 9.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
{
        grandeur_vitesse_max_10mn = 2;
}

if (vitesse_max_10mn < 999.5 && vitesse_max_10mn >= 99.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
{
        grandeur_vitesse_max_10mn = 3;
}
       
  switch(grandeur_vitesse_max_10mn)
  {
    case 0: break; 
    case 1: lcd.print("   ");break;          
    case 2: lcd.print("  ");break;
    case 3: lcd.print(" ");break;
  }
if (grandeur_v_moy>0)
{
  lcd.print(vitesse_max_10mn,0);
}

///////////// LIGNE 3 -  AFFICHAGE denominations vents + rafales  //////////////////////

if (MOY_VMOY < 1)
{
        classification_vent = 1;
}
if (MOY_VMOY >= 1 && MOY_VMOY < 6)
{
        classification_vent = 2;
}
if (MOY_VMOY >= 6 && MOY_VMOY < 12)
{
        classification_vent = 3;
}
if (MOY_VMOY >= 12 && MOY_VMOY <20)
{
        classification_vent = 4;
}
if (MOY_VMOY >= 20 && MOY_VMOY < 29)
{
        classification_vent=5;
}
if (MOY_VMOY >= 29 && MOY_VMOY < 39)
{
        classification_vent = 6;
}
if (MOY_VMOY >= 39 && MOY_VMOY < 50)
{
        classification_vent = 7;
}
if (MOY_VMOY >= 50 && MOY_VMOY < 62)
{
        classification_vent = 8;
}
if (MOY_VMOY >= 62 && MOY_VMOY < 75)
{
        classification_vent = 9;
}
if (MOY_VMOY >= 75 && MOY_VMOY < 89)
{
        classification_vent = 10;
}
if (MOY_VMOY >= 89 && MOY_VMOY < 103)
{
        classification_vent = 11;
}
if (MOY_VMOY >= 103 && MOY_VMOY < 117)
{
        classification_vent = 12;
}
if (MOY_VMOY >= 117)
{
        classification_vent = 13;
}

// Serial.print("classification_vent : ");
// Serial.println(classification_vent);
// Serial.print("MOY_VMOY : ");
// Serial.println(MOY_VMOY);

  lcd.setCursor (0,2);          
  switch(classification_vent)
  {
    case 1: lcd.print("CALME             "); typeVent = "CALME"; break;        
    case 2: lcd.print("TRES LEGERE BRISE "); typeVent = "TRES LEGERE BRISE"; break; 
    case 3: lcd.print("LEGERE BRISE      "); typeVent = "LEGERE BRISE"; break; 
    case 4: lcd.print("PETITE BRISE      "); typeVent = "PETITE BRISE"; break;          
    case 5: lcd.print("JOLIE BRISE       "); typeVent = "JOLIE BRISE"; break; 
    case 6: lcd.print("BONNE BRISE       "); typeVent = "BONNE BRISE"; break; 
    case 7: lcd.print("VENT FRAIS        "); typeVent = "VENT FRAIS"; break;           
    case 8: lcd.print("GRAND FRAIS       "); typeVent = "GRAND FRAIS"; break; 
    case 9: lcd.print("COUP DE VENT      "); typeVent = "COUP DE VENT"; break; 
    case 10: lcd.print("FORT COUP DE VENT"); typeVent = "FORT COUP DE VENT"; break;           
    case 11: lcd.print("TEMPETE          "); typeVent = "TEMPETE"; break; 
    case 12: lcd.print("VIOLENTE TEMPETE "); typeVent = "VIOLENTE TEMPETE"; break; 
    case 13: lcd.print(" !!! OURAGAN !!! "); typeVent = "!!! OURAGAN !!!"; break; 

  }

///////////////////////////  LIGNE 3 - DENOMINATION RAFALES  ///////////////////////////////// 

if (RAFALES_MAX_10_DERNIERES_MN < 18)
{
        classification_rafales = 1;  // aucune rafales
}
if (RAFALES_MAX_10_DERNIERES_MN >= 18 && RAFALES_MAX_10_DERNIERES_MN < 28)
{
        classification_rafales = 2;  // rafales
}
if (RAFALES_MAX_10_DERNIERES_MN >= 28 && RAFALES_MAX_10_DERNIERES_MN < 46)
{
        classification_rafales = 3;  // fortes rafales
}
if (RAFALES_MAX_10_DERNIERES_MN >= 46)
{
        classification_rafales = 4;  // violentes rafales
}
  lcd.setCursor (18,2);          
  switch(classification_rafales){
    case 1: lcd.print("--"); typeRafale = "--"; break;
    case 2: lcd.print("-R"); typeRafale = "-R"; break;
    case 3: lcd.print("FR"); typeRafale = "FR"; break; 
    case 4: lcd.print("VR"); typeRafale = "VR"; break; 
  }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  t_debut_affichage = TEMPS_milli;
  affiche = 1;          // si le v_max_inst (v instantanée max obtenue depuis dernier affichage) a été affiché, il ne l'affichera plus et passera à la prochaine valeur obtenue suite nouveau front montant

}// FIN VOID AFFICHEUR////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************************************
//VOID REMISE A ZERO V MAX  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void REMISE_ZERO_VMAX ()    // Déclenchée par ISR D3
{
  v_max = 0;
}  // FIN VOID REMISE_ZERO_VMAX

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************************************
//VOID  PROGRESS BAR - LIGNE 4 -   ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void draw_progressbar(byte percent) 
{
  /* Déplace le curseur sur la ligne 4 */
  lcd.setCursor(0, 3);  // 3  au lieu 1

  /* Map la plage (0 ~ 100) vers la plage (0 ~ LCD_NB_COLUMNS * 5) */
  byte nb_columns = map(percent, 0, 100, 0, LCD_NB_COLUMNS * 5);

  /* Dessine chaque caractère de la ligne */
  for (byte i = 0; i < LCD_NB_COLUMNS; ++i) 
  {
    /* En fonction du nombre de colonnes restant à afficher */
    if (nb_columns == 0)  // Case vide
    {
      lcd.write((byte) 0);

    } else if (nb_columns >= 5)  // Case pleine
    {
      lcd.write(5);  // il en remplie 5 sur 100
      nb_columns -= 5; // il retranche 5 au total de colonnes à afficher

    } else  // Derniére case non vide
    {
      lcd.write(nb_columns);
      nb_columns = 0;
    }
  }
  v_max_inst_bargraph = 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************************************
////////  BARGRAPH ZERO ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 

//////////////***********************************  VOID CREATION CHAÏNE DES DONNEES A ENVOYER  ************************************** //////////////////////////////////////////////////////////////////////////////////////////////////////////// 
void createDataString(String &data)
{
  data0 = x++;
  data1 = v_max_inst;
  data2 = v_max;
  data3 = MOY_VMOY;
  data4 = vitesse_max_10mn;
  data5 = typeRafale;
  data6 = typeVent;

  data = "*" + String(data0) + "*" + String(data1) + "*" + String(data2) + "*" + String(data3) + "*" + String(data4) + "*" + String(data5) + "*" + String(data6); 
  
  Serial.println(data);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************************************
// SETUP /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup()                           
{
  Serial.begin(9600);
  
  // D3 = INPUT interruption pin pour remise à zero v max
  pinMode(ResetVmaxPin, INPUT);  
  // Déclaration de l'ISR DE REMISE 0 ZERO DE VMAX AVEC BOUTON POUSSOIR
  attachInterrupt(digitalPinToInterrupt(3),REMISE_ZERO_VMAX,CHANGE);  

  // initialize the lcd 20x4
  lcd.init();                         
  lcd.backlight();                    // LCD_TAG      // retroeclairage lcd 20x4
  lcd.begin(LCD_NB_COLUMNS, LCD_NB_ROWS);  
  setup_progressbar();                  // Mémorise les séries de caractères spéciaux
  
  lcd.setCursor (0,0);                  // LCD_TAG go to start of 1er line
  lcd.print("ANEMOMETRE V.58");
  lcd.setCursor (0,1);                  // LCD_TAG go to start of 1er line
  lcd.print("03 Mars 2024");
  lcd.setCursor (0,2);                  // LCD_TAG go to start of 1er line
  lcd.print("Thomas CAUNES R.P.");
  lcd.setCursor (0,3);                  // LCD_TAG go to start of 1er line
  lcd.print("V = 3 + 0.8 F km/h");
  delay(5000);                          // 2s insuffisant !
  lcd.clear();

  hc12.begin(9600);                     // Initialisation du HC-12
  // // *******************  Declaration du canal 1 par defaut  ***************************************
  // pinMode(pinSet_HC12, OUTPUT);         // Déclaration du pin du SET pour changement de canal
  // digitalWrite(pinSet_HC12, LOW); // Mettre le SET à la masse pour entrer en mode commande AT
  // Serial.println("Configuration du canal du HC-12");
  // delay(100);                 // Attente de la stabilisation de la connexion
  // hc12.print("AT+C001");      // Commande AT pour le canal 2 du HC-12 C
  // ///////delay(1000);                // Attente pour s'assurer que la commande est bien prise en compte
  // digitalWrite(pinSet_HC12, HIGH); // Mettre le SET à la masse pour entrer en mode commande AT


}  // FIN SETUP

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************************************
// BOUCLE LOOP ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop()           
{
  float freq = 0;                        // Déclaration de la donnée reçue

  if (hc12.available())                  // Si réception OK DE LA FREQUENCE
  { 
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
    freq = hc12.parseFloat();      //Décodage de la réception : freq = NB_PULSE reçus de l'émetteur en 1s
    temps_precedent = micros();    // Pour la mesure d'absence d'affichage en fin de loop
    NB_PULSE_10s_INCREMENT = NB_PULSE_10s_INCREMENT + freq;     // incrémente le nombre de front descendants observés en 10s (remis à 0 après 10s dans "Calculateur 10 min)
   
    ///////Serial.println(NB_PULSE_10s_INCREMENT);

    memoire_vmax_zero = 0; // permet de ne mettre à 0 la v instantannée max qu'une seule fois lorsque plus de 2.5s sans signal anemomètre

    // Conversion fréquence en vitesse
    if (freq == 0.0) 
    {   
        v = 0;
    }
    else if (freq <= 3.0)
    {
        v = 0.8 * freq ;
    }
    else
    {
        v = 3 + 0.8 * freq ;  
    }

    // Calcul v_max_inst
      if (affiche == 1)                   // s'il y a eu affichage de la v_max_inst, elle est remise à zer
      {                   
        v_max_inst = 0;                   // remise à zero v_max_inst, de ce fait elle évolura entre l'affichage qui vient d'avoir lieu et le nouvel affichage à venir (toutes les 500ms)
        affiche = 0;                      // on déclare que l'affichage est à faire pour la prochaine v max inst
      }
      
      if (v == 0)
      {
        v_max_inst = 0;
      }

      if (v > v_max_inst && v < 200)
      {      
       v_max_inst = v;                    // évolution de la vitesse instantannée (je prends la vitesse instantanée maximale obtenue entre 2 affichages (affichage toutes les 500ms)
      }

      if (v > v_max_inst_bargraph && v < 200)
      {      
       v_max_inst_bargraph = v;          // évolution de la vitesse instantannée (je prends la vitesse instantanée maximale obtenue entre 2 affichages (affichage toutes les 500ms)
      }

      if (v > v_max && v < 200)
      {
       v_max = v;                        // évolution vitesse max. 
      }
      //(1.8*v_precedente)) était 1.5 // calcul v max obtenue sur durée bloc de 10s, le coeff 
      //1.5 évite les aberrations de vitesse doublée qu'il y avait parfois
      if (v > v_max_bloc_10s && v < 200)
      { 
       v_max_bloc_10s = v;               // évolution vitesse max. sert AUX CALCULS DES RAFALES
      }

    ////////////////  ??????????????????  /////////////
    //pourcentage=(v);
    // pourcentage = (v_max_inst_bargraph);
    // percent = byte(pourcentage);

    t_ecoule_bargraph = TEMPS_milli - t_debut_bargraph;

    if (t_ecoule_bargraph > cadence_bargraph)
    { 
      //pourcentage=(v);
      pourcentage = (v_max_inst_bargraph);
      if (pourcentage > 0)          // Suppression du flash dû au "0" reçu avant la valeur
      {
        percent = byte(pourcentage);
      }
      draw_progressbar(percent);
      t_debut_bargraph = millis();
    }

   if (v < 200)
   { 
      v_precedente = v;
   }
  }
  //FIN IF hc12.available() //////////////////    FIN IF HC12 AVAILABLE   /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  TEMPS_milli = millis();         // instant (ms) nécessaires pour cadence d'affichage
  TEMPS_micros = micros();        // instant (µs) nécessaires remise à zero v instantanée "V_max_inst" si plus de 2 s sans signal

  CALCULATEUR_10mn();                       // appelle fonction de calcul des éléments sur 10mn d'observation
  
  t_ecoule_affichage = TEMPS_milli - t_debut_affichage;  // pour gérer cadence affichage 

//////////////////////////////////////////////////////////////////////////////////

  if (t_ecoule_affichage > cadence_affichage)      // affichage selon cadence choisie mais au début, tant qu'il n'y a pas eu assez de front descendants, n'affiche rien
  {  
    AFFICHEUR();                              // appelle fonction d'affichage (appelée tts les 1s)
    
    duree_v_nulle = TEMPS_micros - temps_precedent;  // temps écoulé depuis le dernier front montant (sert à la mise à zero de la vitesse si pas de signal pendant plus de 2s
    
    if ((duree_v_nulle > duree_remise_zero) && memoire_vmax_zero == 0)  // si plus de 2 s sans signal anemo, v max est mise à 0 et affiche une seule fois
    {
         v_max_inst = 0;                              
         memoire_vmax_zero = 1;     // variable mémoire qui permet de ne mettre à zero la vitesse instantannée qu'une seule fois. Est remise à 0 quand nouveau front montant
         percent = 0;
         draw_progressbar(percent);
    }
  }  //// FIN IF temps d'affichage dépassé ////////////////////////////////////////


  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  /////////////                        ENVOI DES DONNEES RECUPEREES DE L'AFFICHEUR                       /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  /////////******************************** TIMING TOUTES LES MINUTES  ************************************///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  // Calcul durée pour 1 min
  tpsAct_1MIN =  millis();

  // Serial.print("tpsAct = ");
  // Serial.print(tpsAct_10MIN);
  // Serial.print("- tpsPrec = ");
  // Serial.println(tpsPrec_10MIN);

  delta_T = tpsAct_1MIN - tpsPrec_1MIN;

  Serial.print("delta_T = ");
  Serial.println(delta_T);

  if (delta_T >= duree)
  {
      /////////**************************  CHANGEMENT DE CANAL DU HC-12 (DU 1 A 2)  *******************************///////////////////////////////////////////////////////////////////////////////////////////////////////////////
      // digitalWrite(pinSet_HC12, LOW); // Mettre le SET à la masse pour entrer en mode commande AT
      // Serial.println("Configuration du canal 2 ");
      // //delay(100);                 // Attente de la stabilisation de la connexion
      // hc12.print("AT+C002");      // Commande AT pour le canal 2 du HC-12 C
      // delay(100);                // Attente pour s'assurer que la commande est bien prise en compte
      // digitalWrite(pinSet_HC12, HIGH); // Mettre le SET à la masse pour entrer en mode commande AT

      ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      /////////************************************  ENVOI DES DATAS  *******************************/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      String dataString = "";
      createDataString(dataString);
      hc12.print(dataString);
      hc12.write('\n'); 
      Serial.println(dataString);

  Serial.println(data0);   //x++;
  Serial.println(data1);
  Serial.println(data2);
  Serial.println(data3);
  Serial.println(data4);
  Serial.println(data5);
  Serial.println(data6);

      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      /////////**************************  CHANGEMENT DE CANAL DU HC-12 (DU 2 A 1)  *******************************//////////////////////////////////////////////////////////////////////////////////////////////////////////////
      // digitalWrite(pinSet_HC12, LOW); // Mettre le SET à la masse pour entrer en mode commande AT
      // Serial.println("Configuration du canal 1");
      // //delay(100);                 // Attente de la stabilisation de la connexion
      // hc12.print("AT+C001");      // Commande AT pour le canal 2 du HC-12 C
      // delay(100);                // Attente pour s'assurer que la commande est bien prise en compte
      // digitalWrite(pinSet_HC12, HIGH); // Mettre le SET à la masse pour entrer en mode commande AT

      tpsPrec_1MIN =  millis();

  } //////////  FIN TRAVAIL 1 MINUTE   //////////////////////////  FIN TRAVAIL 1 MINUTE   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    
}    
///// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ////////// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ////////////////////////

Ces données doivent être alors envoyées au HC-12(3) du nano récepteur B pour les enregistrements des données sur carte SD avec date et heure.

J’ai pour cette dernière étape deux problèmes.

  • Je suis parti sur l’idée de changer le canal du HC-12(2) (canal 1 à 2) pour l’envoi des données au HC-12(3) paramétré sur le canal 2, puis revenir au canal 1. Cela ne fonctionne pas.
  • De plus, l’utilisation d’un envoi des données par concaténation de strings dysfonctionne (le « createDataString »). Comment envoyer les données float et string autrement ? En plusieurs fois ?

Ci-joint le code du récepteur B (je n'active pas les déclarations de l'horloge et de la carte SD. Seules les données reçues m'intéressent en priorité avant d'aller plus loin.)


/*
  LE PROGRAMME UTILISE LE HC-12 C POUR RECEVOIR TOUTES LES MINUTES LES DONNEES ISSUES DE L'AFFICHEUR DU RECEPTEUR A
  LE RECEPTEUR B EST SUR LE CANAL 2 DU HC-12 C. IL RECOIT LES DONNEES DU RECEPTEUR A VIA LE CANAL 2
  LA DATE ET L'HEURE SONT RECUPEREES AVANT LA RECEPTION



*/

///////////////// INCLUSION LIBRAIRIES DS3231 - Carte SD - HC-12  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <Arduino.h>
// Déclaratiion HC-12
#include <SoftwareSerial.h>
SoftwareSerial hc12(8,9);     // OK pour le NANO !

// Déclaration des bibliothèques de la DS3231 et de laCarte SD
/*#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <RTClib.h>     // Declaration de DS3231 (0x68)
RTC_DS3231 rtc;
*/

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*************************************************************************************************************************************************************************************************************************
// DECLARATION VARIABLES PAPOU  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// /////// HC-12 : pin SET
#define pinSet_HC12 6    // LOW pour définition du canal de communication

/*
// /////// DS3231 : 
// Declaration des types de variables à concatener pour afficher la date et l'heure
String stringPoints = ":";
String stringSlash = "/";
///////String dateActuelle;
String heureActuelle = "";
volatile int an, mois, jour, heure, minut;

// /////// CARTE SD : 
// Déclaration du pin de détection de présence de la carte SD en pin 48
#define SDmissing 7
boolean testCarteInseree;
// Déclaration du fichier d'enregistrements
File fichier;
File root;
int tailleFichier=0;
String nomFichier;

// Timing 1 min période enregistrements
unsigned long tpsAct_1MIN = 0;
static unsigned long  tpsPrec_1MIN = 0;
unsigned long delta_T = 0;
const unsigned long duree = 60000UL;

*/

// Données enregistrements
int x = 0;  //rang enregistrement
int data0;
float data1;
float data2;
float data3;
float data4;
String data5 = "";
String data6 = "";
String typeRafale;
String typeVent;


/*
//-------------------------------------------------------------------------------------------------------------------
//                  ROUTINE RECUPERATION DATE ET HEURE DE LA DS3231
//-------------------------------------------------------------------------------------------------------------------
void DateEtHeure()
{
  DateTime now = rtc.now();   //recuperation date et heure
  // Recuperation date et heure
  //////int jour = now.day(), mois = now.month(), an = now.year();
  jour = now.day(), mois = now.month(), an = now.year();
  ////// dateActuelle = jour + stringSlash + mois + stringSlash + an;
  ////// Serial.print(dateActuelle);
  ////// Serial.print("  ");
  ////// int heure = now.hour() , minut = now.minute();
  heure = now.hour() , minut = now.minute();
  heureActuelle = heure + stringPoints + minut;
  // Serial.println(heureActuelle);
  // Serial.println();
}

//--------------------------------------------------------------------------------------------------------------------
//                         ROUTINE REINITIALISATION DE LA CARTE
//--------------------------------------------------------------------------------------------------------------------
void initSDCard()
{
   while (!SD.begin())
   {
     Serial.print("---");
     Serial.print("Carte SD non disponible !");
     Serial.println("---");
     SD.end();           // Fonction non incluse dans la bib de l'IDE
     delay(100);
   }
   return;
}

*/



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************************************
// SETUP /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup()                      //fonction d'initialisation de la carte
{
  Serial.begin(9600);
  
  /*
  // ******************** INITIALISATION DS3231 **********************************************************************
  if (!rtc.begin()) 
  {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    //while (1);
  }
  else
  {
    Serial.println("DS3231 OK !");
  }

  // ******************** INITIALISATION CARTE SD **********************************************************************
  pinMode(SDmissing, INPUT_PULLUP);   // Pin détection Carte insérée noInterrupts()
  initSDCard();                       // Routine de détection de présence de la carte
  Serial.println("Communication etablie avec la carte SD !"); // Quand initSDCard est ok
  */
  
  // ******************** INITIALISATION CANAL 2 DE COMMUNICATION DU HC-12C  **********************************************************************
  hc12.begin(9600);              // Initialisation du HC-12 C
  pinMode(pinSet_HC12, OUTPUT);
  digitalWrite(pinSet_HC12, LOW); // Mettre le SET à la masse pour entrer en mode commande AT
  Serial.println("Configuration du canal du HC-12");
  delay(10);                 // Attente de la stabilisation de la connexion
  hc12.print("AT+C002");      // Commande AT pour le canal 2 du HC-12 C
  ///////delay(1000);                // Attente pour s'assurer que la commande est bien prise en compte
  digitalWrite(pinSet_HC12, HIGH); // Mettre le SET à la masse pour entrer en mode commande AT

}  // FIN SETUP


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//************************************************************************************************************************************************************************************************************************************************
// BOUCLE LOOP ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop()           
{


  if (hc12.available() >0)
  {
    data0 = x++;
    data1 = hc12.parseFloat();
    data2 = hc12.parseFloat();
    data3 = hc12.parseFloat();
    data4 = hc12.parseFloat();
    data5 = hc12.readString();
    data6 = hc12.readString(); 
    //sensorData4 = hc12.Serial.readStringUntil('\n'); 

    Serial.print("data0 : ");
    Serial.println(data0); 
    Serial.print("data1 : ");
    Serial.println(data1);
    Serial.print("data2 : ");
    Serial.println(data2);
    Serial.print("data3 : ");
    Serial.println(data3);
    Serial.print("data4 : ");
    Serial.println(data4);
    Serial.print("data5 : ");
    Serial.println(data5);
    Serial.print("data6: ");
    Serial.println(data6);
  }

    
}    ///// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ////////// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP

Merci pour vos avis et votre aide éventuelle.
Bébert

pouvez vous publier une image du circuit ? peu de participants vont télécharger un zip et l'ouvrir et c'est compliqué depuis un smartphone.

Comme je l'ai déjà dit, je suis surpris par cela - d'autant plus qu'en production le HC-12 pourrait être connecté aux pins 0 et 1 (Serial) ce qui éviterait d'avoir la bibliothèque SoftwareSerial dans le code et sa lourdeur.

Pour optimiser la communication entre vos HC-12, surtout que vous avez les même cartes des 2 côtés, vous pourriez faire une émission en binaire en envoyant une struct avec un code de validation (checksum par exemple)

Bien sûr se passer des Strings serait aussi un bon moyen d'économiser de la mémoire (RAM et flash).

Le programme complet occupait 121% de RAM avec un nano récepteur. Je réessaierai sans la bibliothèque SoftWareSerial. Avec cela, l'utilisation du Serial 0, 1 m'imposera de déconnecter le HC-12 pour le téléchargement, n'est-ce pas ?

Désolé mais je ne connais absolument pas ces procédures-là. J'ai cherché ce qu'est le cheksum sur le net. Je comprends à peu près le principe de calcul et vérification de la cohérence des envois de données mais pour l'instant, c'est du charabia Y-a-t-il des tutos sur ce sujet, des exemples pour débutants ? Les exemples vus sur les forum ne m'ont été utiles. J'aimerais bien acquérir de nouvelles compétences.
Merci J-M-L
Bébert

Je viens de créer le projet avec un seul NANO (le même qu'avec le MEGA 2560.
Voici le résultat de la compilation :

Pour la gestion de l'écran, de l'horloge et de la carte SD avec le détecteur de présence, voici les déclarations :

// INCLUSION LIBRAIRIES //////////////// INCLUSION LIBRAIRIES /////////////////// INCLUSION LIBRAIRIES /////////////
#include <Arduino.h>
// Déclaration de la bibliothèques de l'écran LCD
#include <LiquidCrystal_I2C.h>  //LCD_TAG
LiquidCrystal_I2C lcd(0x27,20,4);  // LCD_TAG set the LCD address to 0x27 for a 20 chars and 4 line display

// Déclaration des bibliothèques de la DS3231 et de laCarte SD
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <RTClib.h>     // Declaration de DS3231 (0x68)
RTC_DS3231 rtc;

// /////// DS3231 : 
// Declaration des types de variables à concatener pour afficher la date et l'heure
String stringPoints = ":";
String stringSlash = "/";
///////String dateActuelle;
String heureActuelle = "";
volatile int an, mois, jour, heure, minut;

// /////// CARTE SD : 
// Déclaration du pin de détection de présence de la carte SD en pin 48
#define SDmissing 48
boolean testCarteInseree;
// Déclaration du fichier d'enregistrements
File fichier;
File root;
int tailleFichier=0;
String nomFichier;

// Données enregistrements
int x = 0;  //rang enregistrement
int data1;
int data2;
int data3;
int data4;
String data5 = "";
String data6 = "";
String typeRafale;
String typeVent;

// Timing 1 min période enregistrements
unsigned long tpsAct_1MIN = 0;
static unsigned long  tpsPrec_1MIN = 0;
unsigned long delta_T = 0;
const unsigned long duree = 60000UL;

// /////// Déclaratiion HC-12 sur le port Serial1 de l'Arduino MEGA 2560
#define pinTX 1        // Serial1 pour le MEGA
#define pinRX 0

oui - mais ça vaut le coup (et vous avez un LCD si besoin d'afficher un peu de debug)

si je compile ce truc "de base" qui inclus la RTCLib, la carte SD (la bibliothèque SDFat est meilleure que SD), une très bonne bibliothèque pour l'écran (plus efficace) et le HC12 sur Serial pour une Nano

#include <Wire.h>
#include <hd44780.h>                        // main hd44780 header https://www.arduino.cc/reference/en/libraries/hd44780
#include <hd44780ioClass/hd44780_I2Cexp.h>  // i2c expander i/o class header
#include <RTClib.h>     // Declaration de DS3231 (0x68)

const uint8_t nbCols = 20;
const uint8_t nbRows = 4;
const uint8_t SD_CS_PIN = SS;
#include <SdFat.h>              // https://github.com/greiman/SdFat

SdFat SD;
RTC_DS3231 rtc;
hd44780_I2Cexp lcd;
#define hc12Serial Serial

void erreur() {
  while (true) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(250);
    digitalWrite(LED_BUILTIN, LOW);
    delay(250);
  }
}

void setup() {
  hc12Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);

  if (!rtc.begin()) erreur();
  if (!SD.begin(SD_CS_PIN)) erreur();
  if (lcd.begin(nbCols, nbRows)) erreur();
}

void loop() {}

ça me dit

Le croquis utilise 10678 octets (34%) de l'espace de stockage de programmes. Le maximum est de 30720 octets.
Les variables globales utilisent 1110 octets (54%) de mémoire dynamique, ce qui laisse 938 octets pour les variables locales. Le maximum est de 2048 octets.

ça devrait laisser quand même pas mal de place pour le code

OUF !... v'là du lourd !

  1. Comment intégrer ces bibliothèques dans VSCode ? Avec Gitub ? Je n'ai jamais essayé.
    Si je comprends bien, ces bibliothèques gèrent LCD, DS3231 et la Carte.
  2. Dois-je garder la bib SPI.h ?
  3. Que devient ma void "initCard()" qui repère si la carte est présente ou non. Cela m'est indispensable pour la poursuite des enregistrements après l'avoir enlevée ?
//--------------------------------------------------------------------------------------------------------------------
//                         ROUTINE REINITIALISATION DE LA CARTE
//--------------------------------------------------------------------------------------------------------------------
void initSDCard()
{
   while (!SD.begin())
   {
     Serial.print("---");
     Serial.print("Carte SD non disponible !");
     Serial.println("---");
     SD.end();           // Fonction non incluse dans la bib de l'IDE
     delay(100);
   }
   return;
}

La Bib SDFat possède-t-elle la fonction "end()" utilisée ici ?

C'est la pin CS de la carte. Mais que signifie "SS" ?

Désolé J-M, cela fait beaucoup ! Merci, merci. J'aborde avec tout cela des contrées inconnues mais excitantes.
Bébert

il y a une fonction end(), oui

il peut y avoir de nombreuses raisons même avec une carte insérée pour que SD.begin() répondre false...


je n'utilise pas vscode.. je suppose comme les autres bibliothèques :slight_smile:

@Bebert je crois que VSCode va chercher les bibliothèques dans le répertoire d'installation de Arduino IDE, qui est nécessaire à son bon fonctionnement.

Bonjour terval,
Merci pour ta réponse.
J'ai trouvé dans VSCODE les librairies dans la plateformIO. C'est OK.
b2BERT

J'ai "nettoyé" mon programme en enlevant tout ce qui concernait la gestion des périphériques (en les commentant).
Voici le résultat de la compilation :

Compiling .pio\build\nanoatmega328\src\8-HC12_ANEMO_INTERRUPT_61_F-Def-1s_NANO_Serial_Recepteur.cpp.o
Linking .pio\build\nanoatmega328\firmware.elf
Checking size .pio\build\nanoatmega328\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [=====Warning! The data size (2594 bytes) is greater than maximum allowed (2048 bytes)
=====]  **126.7%** (used 2594 bytes from 2048 bytes)
Flash: [==========]  **96.0%** (used 29504 bytes from 30720 bytes)
============================================================ [SUCCESS] Took 4.22 seconds ============================================================ *  Le terminal sera réutilisé par les tâches, appuyez sur une touche pour le fermer. 

Voici le programme :

/*
  LE PROGRAMME UTILISE LE hc-12 POUR RECEVOIR TOUTES LES SECONDES LA VALEUR DE LA fREQUENCE
  Utilisation du NANO avec le port SERIAL 0. TX en 1 et RX en 0 (Pas de librairie "AltSoftSerial.h")
  Le HC-12 reçoit les données String qui sont concaténées pour obtenir la freq
  
  La DS3231 donne l'heure et an, mois et jour pour la carte SD
  La carte SD crée un fichier avec la date du jour au format "anmoisjour.txt" (8 caractères max)
  La fonction Serial.end(); est utilisée dans la SD.init(). Le SDmissing permet de détecter la présence de la Carte.
  on peut la retirer sans risque et les enregistrements reprennent à la suite.
  La carte enregistre toutes les minutes : 
    - le nombre d'enregistrements
    - l'heure
    - v_max_inst
    - v_max
    - MOY_VMOY
    - vitesse_max_10mn
    - RAFALES_MAX_10_DERNIERES_MN
    - classification_vent
*/

// INCLUSION LIBRAIRIES //////////////// INCLUSION LIBRAIRIES////// INCLUSION LIBRAIRIES /////////////
#include <Arduino.h>


///////***********************  PG JP ************************/////////////////////////////////////////
// // Déclaration de la bibliothèques de l'écran LCD
// #include <LiquidCrystal_I2C.h>  //LCD_TAG
// LiquidCrystal_I2C lcd(0x27,20,4);  // LCD_TAG set the LCD address to 0x27 for a 20 chars and 4 line display

// // Déclaration des bibliothèques de la DS3231 et de laCarte SD
// #include <SPI.h>
// #include <SD.h>
// #include <Wire.h>
// #include <RTClib.h>     // Declaration de DS3231 (0x68)
// RTC_DS3231 rtc;



///////**************  FORUM **********  FORUM ****************  FORUM *********////////////////////////
///////**************  DECLARATION DE LCD HORLOGE ET CARTE SD   *********////////////////////////
#include <Wire.h>
#include <hd44780.h>                        // main hd44780 header https://www.arduino.cc/reference/en/libraries/hd44780
#include <hd44780ioClass/hd44780_I2Cexp.h>  // i2c expander i/o class header
#include <RTClib.h>                         // Declaration de DS3231 (0x68)


// /* Constantes pour la taille de l'écran LCD */ ????????????????????????????????
const int LCD_NB_ROWS = 4;
const int LCD_NB_COLUMNS = 20;
const uint8_t SD_CS_PIN = SS;
#include <SdFat.h>              // https://github.com/greiman/SdFat

SdFat SD;
RTC_DS3231 rtc;
hd44780_I2Cexp lcd;
#define hc12Serial Serial       // pour déclarer HC-12 avec le port Serial



// /////// DS3231 : 
// Declaration des types de variables à concatener pour afficher la date et l'heure
String stringPoints = ":";
String stringSlash = "/";
///////String dateActuelle;
String heureActuelle = "";
volatile int an, mois, jour, heure, minut;

// // // /////// CARTE SD : ??????????????????????????????????
// // Déclaration du pin de détection de présence de la carte SD en pin 48
// #define SDmissing 7
// boolean testCarteInseree;

// Déclaration du fichier d'enregistrements
File fichier;
File root;
int tailleFichier=0;
String nomFichier;

// Données enregistrements
int x = 0;  //rang enregistrement
float data1;
float data2;
float data3;
float data4;
String data5 = "";
String data6 = "";
String typeRafale;
String typeVent;

// Timing 1 min période enregistrements
unsigned long tpsAct_1MIN = 0;
static unsigned long  tpsPrec_1MIN = 0;
unsigned long delta_T = 0;
const unsigned long duree = 60000UL;

// /////// Déclaratiion HC-12 sur le port Serial1 de l'Arduino MEGA 2560
#define pinTX 1        // Serial1 pour le MEGA
#define pinRX 0

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//******************************************************************************************************************
// COMMENTAIRES CONFIGURATION HARDWARE  ////////////////////////////////////////////////////////////////////////////
// D3 = INPUT logic pour remise à zero v max par interruption
// D4 = OUTPUT logic anemo pour diode lumineuse signal anemometre
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//******************************************************************************************************************
// ALLOCATION INPUT/OUTPUT /////////////////////////////////////////////////////////////////////////////////////////

const byte ResetVmaxPin = 3;    // D3 = INPUT, remise à zero v max par front changeant
// const byte pinSDA = 4;         // A20 pour le MEGA    ??????????????????????????
// const byte pinSCL = 5;         // A21

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*******************************************************************************************************************
// DECLARATION VARIABLES ////////////////////////////////////////////////////////////////////////////////////////////

////SUPPR////volatile boolean front_descendant=0;  // utilisé dans la VOID ANEMO lors de l'interruption sur front montant en pin D2 (affichage que si cette variable passe à 1
boolean affiche = 1;                    // si le v_max_inst (v instantanée max obtenue depuis dernier affichage) a été affiché, il ne l'affichera plus et passera à la prochaine valeur obtenue suite nouveau front montant
byte grandeur_v_max_inst = 0;           // entre deux affichages, prend la valeur max des vitesse instantanées obtenues et l'affiche comme vitesse instantanée
byte grandeur_v_max = 0;                // vitesse max relevée depuis lancement du programme, se remet à zero manuellement via pin d'interruption D3
byte grandeur_v_moy = 0;

// SUPPR /////
//static unsigned long delta_t=5000;           // delta t évoqué au dessus
//static unsigned long nouveau_temps = 0;      // temps (µs) prélevé sur l'instant d'un front montant anemomètre (par interruption en D2)
/////////////

static unsigned long temps_precedent = 0;      // temps précédent (µs) est une mémoire qui permet le calcul le delta t entre deux fronts desc. (avec "nouveau_temps"
float v = 0;                                   // vitesse calculée entre deux fronts descendants

// SUPPR  ///// Déplacé au départ du loop !
//float freq=0;
///////////////////

float v_precedente = 0;                 // vitesse calculée entre deux fronts descendants
byte nb_v_calc = 0;
byte depassement = 0; 
float v_max = 0;                        // vitesse max obtenue depuis le lancement du programme, sera mise à zéro manuellement par une interruption.
float v_max_inst = 0;                   // vitesse max instantanée obtenue depuis dernier affichage des valeurs
float v_max_inst_bargraph = 0;
static unsigned long duree_v_nulle = 0; // durée pendant laquelle aucun front montant anemomètre => sert à mettre à 0 V_max_inst et donc pour afficher vitesse nulle
boolean memoire_vmax_zero = 0;
static unsigned long TEMPS_milli = 0;   // variable temps en ms pour les besoins de la cadence d'affichage
static unsigned long TEMPS_micros = 0;  // variable temps en s pour les besoins de la cadence d'affichage
unsigned long t_debut_affichage = 0;    // pour gérer cadence affichage
unsigned long t_ecoule_affichage = 0;   // pour gérer cadence affichage

unsigned long t_debut_bargraph = 0;     // pour gérer cadence affichage
unsigned long t_ecoule_bargraph = 0;    // pour gérer cadence affichage

//////// CONSTANTES POUR VITESSE MOYENNE GLISSANTE SUR 10s  /////////////////////////////////////////////////////////
////          ///////  int NB_PULSE = 0                                      // incrément du nombre de fronts descendants pour 10s
static unsigned int NB_PULSE_10s_INCREMENT = 0;       // incrément du nombre de fronts descendants vus en 10s
int MOY_temps_ecoule = 0;                             // temps écoulé sur l'observation du nombre de fronts descendants
unsigned long MOY_temps_debut = 0;                    // temps de départ fenêtre de moyennage 10s
unsigned int NB_PULSE_10s_TOTAL = 0;                  // nb fronts descendants obtenus  sur 10s
int MOY_temps_ecoule_10s = 0;                         // temps écoulé de 10s

int i = 0;
unsigned int PULSE[60];             // tableau de 60 valeurs (chacune des valeurs est le nb de fronts descendants observés en 10s. 60x10s=600s=10mn
float nb_blocs_10s_calcules = 0;    // nombre de valeurs rentrées dans le tableau PULSE (sert quand le tableau n'est pas complètement rempli de ne calculer la V moy que sur les valeurs entrées 
float nb_pulses_10mn = 0;           // somme des 60 valeurs du tableau PULSE
float MOY_VMOY = 0;                 // vitesse moyenne obtenue sur 10mn , donc moyenne des 60 valeurs du tableau PULSE

/// assignation durée précise (10s) sur chacun des blocs de 10s dans le tableau MOY_DUREE_BLOC
unsigned int DUREE_BLOC_10s[60];    // durée exacte des blocs de 10s (60 blocs de 10 s donne 10mn)
float DUREE_BLOC_10s_SOMME = 0;     // somme durée exacte des blocs de 10s (60 blocs de 10 s donne 10mn)
float DUREE_BLOC_10s_MOY = 0;       // moyenne de durée exacte des blocs de 10s

float RAFALES_MAX_10_DERNIERES_MN = 0;
byte grandeur_M_10_D_MN = 0;
//// CONSTANTES POUR tableau des écarts (rafales)

float v_max_bloc_10s = 0;           // vmax observée sur un bloc de 10s, même bloc que pour calcul moy sur bloc 10s
float vitesse_relevee = 0;    
float vitesse_max_10mn = 0;

float V_max_bloc[60];
byte grandeur_vitesse_max_10mn = 0;

byte classification_vent = 0;
byte classification_rafales = 0;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//********************************************************************************************************************
// CONSTANTES DE REGLAGE /////////////////////////////////////////////////////////////////////////////////////////////

unsigned long cadence_affichage = 1000;                        // cadence affichage (ms)
unsigned long cadence_bargraph = 200;
volatile unsigned long duree_remise_zero = 2500000; // valeur en µs, durée 2.5s à partir de laquelle sans signal anemomètre, la vitesse instantannée est mise à zero

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//********************************************************************************************************************
// CONSTANTES BARRE DE PROGRESSION ///////////////////////////////////////////////////////////////////////////////////

// Valeur en pourcentage de la barre de progression */
static byte percent = 0;
static float pourcentage = 0;

/* Caractères personnalisés */
byte DIV_0_OF_5[8] = {
  B00000, 
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
}; // 0 / 5

byte DIV_1_OF_5[8] = {
  B00000, 
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000
}; // 1 / 5

byte DIV_2_OF_5[8] = {
  B00000, 
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000
}; // 2 / 5 

byte DIV_3_OF_5[8] = {
  B00000, 
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100
}; // 3 / 5

byte DIV_4_OF_5[8] = {
  B00000, 
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110
}; // 4 / 5

byte DIV_5_OF_5[8] = {
  B00000, 
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111
}; // 5 / 5

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*******************************************************************************************************************
// SETUP PROGRESS BAR ///////////////////////////////////////////////////////////////////////////////////////////////

 // Fonction de configuration de l'écran LCD pour la barre de progression.
 // Utilise les caractères personnalisés de 0 à 5 (6 et 7 restent disponibles).
 
void setup_progressbar() 
{
  /* Enregistre les caractères personnalisés dans la mémoire de l'écran LCD */
  lcd.createChar(0, DIV_0_OF_5);
  lcd.createChar(1, DIV_1_OF_5);
  lcd.createChar(2, DIV_2_OF_5);
  lcd.createChar(3, DIV_3_OF_5);
  lcd.createChar(4, DIV_4_OF_5);
  lcd.createChar(5, DIV_5_OF_5);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*********************************************************************************************************************
// VOID CALCULATEUR_10mn ////// VOID CALCULATEUR_10mn //////// VOID CALCULATEUR_10mn //////// VOID CALCULATEUR_10mn ///

void CALCULATEUR_10mn()
{
  MOY_temps_ecoule = TEMPS_milli - MOY_temps_debut;  // pour faire des paquets d'observation de 10s (sert à remplir le tableau PULSE)

  if (MOY_temps_ecoule >= 10000)      // toutes les 10s, crée un bloc, total de 60 blocs. 
  {
    if(nb_blocs_10s_calcules < 61)    // une fois atteint le nombre de 60 valeurs, on n'incrémente plus le nombre de valeurs présente dans le tableau PULSE
    {
      nb_blocs_10s_calcules++;      // pour première valeur erronnée "NB_PULSE_10s_TOTAL",  nb_blocs_10s_calcules vaut 1. 
      //Vaut 2 quand première valeur bonne. Donc nb valeurs bonne dans PULSE[] vaut (nb_blocs_10s_calcules-1)
    }
    NB_PULSE_10s_TOTAL = NB_PULSE_10s_INCREMENT;              // durant 10s, NB_PULSE_10s_INCREMENT a été incrémenté (nombre de front descendants observés. Cette valeur alimentera une case du tableau PULSE (10s par case)
    NB_PULSE_10s_INCREMENT = 0;                       // compteur de fronts descendants remis à 0 en vu de sa nouvelle incrémentation à venir pour le prochain paquet de 10s
    MOY_temps_debut = TEMPS_milli;    // relève l'instant de départ de la nouvelle fenêtre d'observation de 10s
    MOY_temps_ecoule_10s = MOY_temps_ecoule; 

    /// ETAPE 1 - 1/3 => DECALER TABLEAU PULSE///
    if(nb_blocs_10s_calcules > 2)    // à la première moy suite aux 10s, la valeur est faussée et nb_blocs_10s_calcules est à 1. 
    {
      //Donc c'est à partir de > 2 que je décale vers le bas les valeurs afin de faire entrer les nouvelles 
      for(i = 59;i > 0;i = i-1)
      {
        PULSE[i] = PULSE[i-1];
      }
    }

    /// ETAPE 1 - 2/3 => DECALER TABLEAU MOY_DUREE_BLOC///
    if(nb_blocs_10s_calcules > 2)    // à la première moy suite aux 10s, la valeur est faussée et nb_blocs_10s_calcules est à 1. Donc c'est à partir de > 2 que je décale vers le bas les valeurs afin de faire entrer les nouvelles 
    {
      for(i = 59;i > 0;i = i-1)
      {
        DUREE_BLOC_10s[i] = DUREE_BLOC_10s[i-1];
      }
    }

    /// ETAPE 1 - 3/3 => DECALER TABLEAU v_max_bloc_10s ///
    if(nb_blocs_10s_calcules>2)      // à la première moy suite aux 10s, la valeur est faussée et nb_blocs_10s_calcules est à 1. Donc c'est à partir de > 2 que je décale vers le bas les valeurs afin de faire entrer les nouvelles 
    {    
      for(i = 59 ;i > 0 ;i = i-1)
      {
        V_max_bloc[i] = V_max_bloc[i-1];
      }
    }

    /// ETAPE 2 1/3 => FAIRE ENTRER NOUVELLE VALEUR DANS L'INDICE 0 DE PULSE ///
      PULSE[0] = NB_PULSE_10s_TOTAL;
    //////////////////////////////////

    /// ETAPE 2 2/3 => FAIRE ENTRER NOUVELLE VALEUR DANS L'INDICE 0 MOY DUREE BLOC ///
      DUREE_BLOC_10s[0] = MOY_temps_ecoule_10s;
    //////////////////////////////////

    /// ETAPE 3 1/3 => CALCUL SOMME du nb de pulse par bloc de 10s (somme de PULSE) ///
    nb_pulses_10mn = 0;
    for(i = 0;i < 60;i++)
    {
      nb_pulses_10mn = nb_pulses_10mn + PULSE[i];
    }
    //////////////////////////////////

    /// ETAPE 3 2/3 => CALCUL SOMME DES DUREES DE BLOC sur tout le tableau MOY_DUREE_BLOC ///
    DUREE_BLOC_10s_SOMME = 0;  // cette somme de DUREE de tous les 60 blocs sera en ms, 10s=10000ms, 10mn=600s = 600000ms
    for(i = 0;i < 60;i++)
    {
      DUREE_BLOC_10s_SOMME = DUREE_BLOC_10s_SOMME + DUREE_BLOC_10s[i];
    }
    //////////////////////////////////
     
    DUREE_BLOC_10s_MOY = (DUREE_BLOC_10s_SOMME / (nb_blocs_10s_calcules - 1));
    //MOY_VMOY=(nb_pulses_10mn/(nb_blocs_10s_calcules-1)*180/192)/(DUREE_BLOC_10s_MOY*0.001);   // V=F*(180/192), cette v moyenne est sur un bloc de 10s moyen, vitesse par 10s en fait
    MOY_VMOY = 3 + (nb_pulses_10mn / (nb_blocs_10s_calcules-1) * 0.8) / (DUREE_BLOC_10s_MOY * 0.001);       // V=0.8 x F+3

    /// ETAPE 2 3/3 => FAIRE ENTRER NOUVELLE VALEUR DANS tableau V_max_bloc ///
    V_max_bloc[0] = v_max_bloc_10s;
    v_max_bloc_10s = 0;
    //////////////////////////////////

    /// ETAPE 3 3/3 => CALCUL MAX sur 10 dernières mn / sur chacun des blocs de 10s 
    vitesse_max_10mn = 0;  // initialisation à 0 de la vitesse max avant analyse des 60 blocs de 10s ci-dessous
    for(i = 0;i < 60;i++)
    {
      vitesse_relevee = V_max_bloc[i];
      if(vitesse_relevee > vitesse_max_10mn)
      {
        vitesse_max_10mn = vitesse_relevee;  // DU COUP EN FAIT vitesse_max représente V MAX sur les 10 dernières MN
      }
    }
    //////////////////////////////////

    ////////////////// CALCUL DES RAFALES MAX sur les 10 dernières minutes  ////////////////////////////////////////

  RAFALES_MAX_10_DERNIERES_MN = vitesse_max_10mn - MOY_VMOY; // EN FAIT MAX_10_DERNIERES_MN représente RAFALES sur 10 dernières mn (max ecart par rapport moyenne)

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
  }  
  //////////// FIN SI MOY temps écoulé > 10s //////////////////////// FIN SI MOY temps écoulé > 10s /////////////
  
} //// FIN VOID "CALCULATEUR_10mn"//// FIN VOID "CALCULATEUR_10mn"//// FIN VOID "CALCULATEUR_10mn"//// FIN VOID "CALCULATEUR_10mn"//// FIN VOID "CALCULATEUR_10mn"
 

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*******************************************************************************************************************
// VOID AFFICHEUR ///////////////////////////////////////////////////////////////////////////////////////////////////

void AFFICHEUR()
{

  //////////////////////////////LIGNE 1 - AFFICHAGE V instananée ////////////////////////////////////////////////////// 

  if (v_max_inst < 9.95)  // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
  {
          grandeur_v_max_inst = 1;
  }

  if (v_max_inst < 99.95 && v_max_inst >= 9.95) // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
  {
          grandeur_v_max_inst = 2;
  }

  if (v_max_inst <999.95 && v_max_inst >= 99.95) // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
  {
          grandeur_v_max_inst = 3;
  }

  lcd.setCursor (0,0);                           // LCD_TAG go to start of 1er line
  switch(grandeur_v_max_inst)
  {
    case 1: lcd.print("V / max:   ");break;     // LCD_TAG
    case 2: lcd.print("V / max:  ");break;
    case 3: lcd.print("V / max: ");break;
  }
  // if (v_max_inst !=0)
  // {
  lcd.print(v_max_inst,1);      // LCD_TAG
    // Serial.print("V instantanee = ");
    // Serial.println(v_max_inst);
  //}
  
  /////////////////////////////// LIGNE 1 -  AFFICHAGE v max /////////////////////////////////////////////////////////

  if (v_max < 9.95) // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
  {
          grandeur_v_max = 1;
  }

  if (v_max < 99.95 && v_max >= 9.95) // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
  {
          grandeur_v_max = 2;
  }

  if (v_max < 999.95 && v_max >= 99.95) // je vais à 2 chiffres après virgule car j'affiche 1 chiffre après virgule
  {
          grandeur_v_max = 3;
  }

    switch(grandeur_v_max)
    {
      case 1: lcd.print("   ");break;           // LCD_TAG
      case 2: lcd.print("  ");break;
      case 3: lcd.print(" ");break;
    }
  lcd.print(v_max,1);           // LCD_TAG

  ///////////////////////////// LIGNE 2 -  AFFICHAGE v moy ///////////////////////////////////////////////////////////

  if (MOY_VMOY <= 3.1)     //// AJOUT pour passer à 0 la vitesse moyenne si elle est < 3.1 (calcul dit v=3.0 si aucun vent)
  {
          grandeur_v_moy = 0;
          MOY_VMOY = 0;
  }
  if (MOY_VMOY < 9.5 && MOY_VMOY > 3.1)     // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
  {
          grandeur_v_moy = 1;
  }

  if (MOY_VMOY < 99.5 && MOY_VMOY >= 9.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
  {
          grandeur_v_moy = 2;
  }

  if (MOY_VMOY < 999.5 && MOY_VMOY >= 99.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
  {
          grandeur_v_moy = 3;
  }

  lcd.setCursor (0,1);          // LCD_TAG go to start of 2nd line

    switch(grandeur_v_moy)
    {
      case 0: lcd.print("moy/R/M:  <3   /   /");break;
      case 1: lcd.print("moy/R/M:   ");break;           // LCD_TAG
      case 2: lcd.print("moy/R/M:  ");break;
      case 3: lcd.print("moy/R/M: ");break;
    }

  if (grandeur_v_moy > 0)  //// AJOUT pour passer à 0 la vitesse moyenne si elle est < 3.1 (calcul dit v=3.0 si aucun vent)
  {
    lcd.print(MOY_VMOY,0);          
  }    

  //////////////////////////////LIGNE 2 -  AFFICHAGE RAFALES MAX SUR LES 10 DERNIERES MINUTES //////////////////////////

  if (grandeur_v_moy == 0)
  {
    grandeur_M_10_D_MN = 0;
  }
  if (RAFALES_MAX_10_DERNIERES_MN < 9.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
  {
          grandeur_M_10_D_MN = 1;
  }
  if (RAFALES_MAX_10_DERNIERES_MN < 99.5 && RAFALES_MAX_10_DERNIERES_MN >= 9.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
  {
          grandeur_M_10_D_MN = 2;
  }
  if (RAFALES_MAX_10_DERNIERES_MN < 999.5 && RAFALES_MAX_10_DERNIERES_MN >= 99.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
  {
          grandeur_M_10_D_MN = 3;
  }
  switch(grandeur_M_10_D_MN)
    {
      case 0: break;
      case 1: lcd.print("   ");break;          
      case 2: lcd.print("  ");break;
      case 3: lcd.print(" ");break;
    }
  if (grandeur_v_moy>0)
  {
    lcd.print(RAFALES_MAX_10_DERNIERES_MN, 0);
  }
  /////////////////////  LIGNE 2 -  AFFICHAGE vitesse max sur les 10 dernières mn (60 blocs de 10s analyses) /////////////

  if (grandeur_v_moy == 0)
  {
    grandeur_vitesse_max_10mn = 0;
  }
  if (vitesse_max_10mn < 9.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
  {
          grandeur_vitesse_max_10mn = 1;
  }

  if (vitesse_max_10mn<99.5 && vitesse_max_10mn >= 9.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
  {
          grandeur_vitesse_max_10mn = 2;
  }

  if (vitesse_max_10mn < 999.5 && vitesse_max_10mn >= 99.5) // je vais à 1 chiffre après virgule car j'affiche 0 chiffre après virgule
  {
          grandeur_vitesse_max_10mn = 3;
  }
        
    switch(grandeur_vitesse_max_10mn)
    {
      case 0: break; 
      case 1: lcd.print("   ");break;          
      case 2: lcd.print("  ");break;
      case 3: lcd.print(" ");break;
    }
  if (grandeur_v_moy>0)
  {
    lcd.print(vitesse_max_10mn,0);
  }

  ///////////// LIGNE 3 -  AFFICHAGE denominations vents + rafales  ///////////////////////////////////////////////////

  if (MOY_VMOY < 1)
  {
          classification_vent = 1;
  }
  if (MOY_VMOY >= 1 && MOY_VMOY < 6)
  {
          classification_vent = 2;
  }
  if (MOY_VMOY >= 6 && MOY_VMOY < 12)
  {
          classification_vent = 3;
  }
  if (MOY_VMOY >= 12 && MOY_VMOY <20)
  {
          classification_vent = 4;
  }
  if (MOY_VMOY >= 20 && MOY_VMOY < 29)
  {
          classification_vent=5;
  }
  if (MOY_VMOY >= 29 && MOY_VMOY < 39)
  {
          classification_vent = 6;
  }
  if (MOY_VMOY >= 39 && MOY_VMOY < 50)
  {
          classification_vent = 7;
  }
  if (MOY_VMOY >= 50 && MOY_VMOY < 62)
  {
          classification_vent = 8;
  }
  if (MOY_VMOY >= 62 && MOY_VMOY < 75)
  {
          classification_vent = 9;
  }
  if (MOY_VMOY >= 75 && MOY_VMOY < 89)
  {
          classification_vent = 10;
  }
  if (MOY_VMOY >= 89 && MOY_VMOY < 103)
  {
          classification_vent = 11;
  }
  if (MOY_VMOY >= 103 && MOY_VMOY < 117)
  {
          classification_vent = 12;
  }
  if (MOY_VMOY >= 117)
  {
          classification_vent = 13;
  }

  // Serial.print("classification_vent : ");
  // Serial.println(classification_vent);
  // Serial.print("MOY_VMOY : ");
  // Serial.println(MOY_VMOY);

  lcd.setCursor (0,2);          
  switch(classification_vent)
  {
    case 1: lcd.print("CALME             "); typeVent = "CALME"; break;        
    case 2: lcd.print("TRES LEGERE BRISE "); typeVent = "TRES LEGERE BRISE"; break; 
    case 3: lcd.print("LEGERE BRISE      "); typeVent = "LEGERE BRISE"; break; 
    case 4: lcd.print("PETITE BRISE      "); typeVent = "PETITE BRISE"; break;          
    case 5: lcd.print("JOLIE BRISE       "); typeVent = "JOLIE BRISE"; break; 
    case 6: lcd.print("BONNE BRISE       "); typeVent = "BONNE BRISE"; break; 
    case 7: lcd.print("VENT FRAIS        "); typeVent = "VENT FRAIS"; break;           
    case 8: lcd.print("GRAND FRAIS       "); typeVent = "GRAND FRAIS"; break; 
    case 9: lcd.print("COUP DE VENT      "); typeVent = "COUP DE VENT"; break; 
    case 10: lcd.print("FORT COUP DE VENT"); typeVent = "FORT COUP DE VENT"; break;           
    case 11: lcd.print("TEMPETE          "); typeVent = "TEMPETE"; break; 
    case 12: lcd.print("VIOLENTE TEMPETE "); typeVent = "VIOLENTE TEMPETE"; break; 
    case 13: lcd.print(" !!! OURAGAN !!! "); typeVent = "!!! OURAGAN !!!"; break; 
  }

  /////////////////////////////////////  LIGNE 3 - DENOMINATION RAFALES  //////////////////////////////////////////////

  if (RAFALES_MAX_10_DERNIERES_MN < 18)
  {
          classification_rafales = 1;  // aucune rafales
  }
  if (RAFALES_MAX_10_DERNIERES_MN >= 18 && RAFALES_MAX_10_DERNIERES_MN < 28)
  {
          classification_rafales = 2;  // rafales
  }
  if (RAFALES_MAX_10_DERNIERES_MN >= 28 && RAFALES_MAX_10_DERNIERES_MN < 46)
  {
          classification_rafales = 3;  // fortes rafales
  }
  if (RAFALES_MAX_10_DERNIERES_MN >= 46)
  {
          classification_rafales = 4;  // violentes rafales
  }
  lcd.setCursor (18,2);          
  switch(classification_rafales)
  {
    case 1: lcd.print("--"); typeRafale = "--"; break;
    case 2: lcd.print("-R"); typeRafale = "-R"; break;
    case 3: lcd.print("FR"); typeRafale = "FR"; break; 
    case 4: lcd.print("VR"); typeRafale = "VR"; break; 
  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  t_debut_affichage = TEMPS_milli;
  affiche = 1;          // si le v_max_inst (v instantanée max obtenue depuis dernier affichage) a été affiché, il ne l'affichera plus et passera à la prochaine valeur obtenue suite nouveau front montant

}
//////////////////////////// FIN VOID AFFICHEUR   /////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//******************************************************************************************************************
//VOID REMISE A ZERO V MAX  ////////////////////////////////////////////////////////////////////////////////////////

void REMISE_ZERO_VMAX ()    // Déclenchée par ISR D3
{
  v_max = 0;
}  // FIN VOID REMISE_ZERO_VMAX

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//******************************************************************************************************************
//VOID  PROGRESS BAR - LIGNE 4 -   /////////////////////////////////////////////////////////////////////////////////

void draw_progressbar(byte percent) 
{
  /* Déplace le curseur sur la ligne 4 */
  lcd.setCursor(0, 3);  // 3  au lieu 1

  /* Map la plage (0 ~ 100) vers la plage (0 ~ LCD_NB_COLUMNS * 5) */
  byte nb_columns = map(percent, 0, 100, 0, LCD_NB_COLUMNS * 5);

  /* Dessine chaque caractère de la ligne */
  for (byte i = 0; i < LCD_NB_COLUMNS; ++i) 
  {
    /* En fonction du nombre de colonnes restant à afficher */
    if (nb_columns == 0)  // Case vide
    {
      lcd.write((byte) 0);
    } 
    else if (nb_columns >= 5)  // Case pleine
    {
      lcd.write(5);  // il en remplie 5 sur 100
      nb_columns -= 5; // il retranche 5 au total de colonnes à afficher

    } 
    else  // Derniére case non vide
    {
      lcd.write(nb_columns);
      nb_columns = 0;
    }
    }
    v_max_inst_bargraph = 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*****************************************************************************************************************
////////  BARGRAPH ZERO ////////////////////////////////////////////////////////////////////////////////////////////


//-------------------------------------------------------------------------------------------------------------------
//                  ROUTINE RECUPERATION DATE ET HEURE DE LA DS3231
//-------------------------------------------------------------------------------------------------------------------
void DateEtHeure()
{
  DateTime now = rtc.now();   //recuperation date et heure
  // Recuperation date et heure
  //////int jour = now.day(), mois = now.month(), an = now.year();
  jour = now.day(), mois = now.month(), an = now.year();
  ////// dateActuelle = jour + stringSlash + mois + stringSlash + an;
  ////// Serial.print(dateActuelle);
  ////// Serial.print("  ");
  ////// int heure = now.hour() , minut = now.minute();
  heure = now.hour() , minut = now.minute();
  heureActuelle = heure + stringPoints + minut;
  // Serial.println(heureActuelle);
  // Serial.println();
}

// //--------------------------------------------------------------------------------------------------------------------
// //                         ROUTINE REINITIALISATION DE LA CARTE
// //--------------------------------------------------------------------------------------------------------------------
// void initSDCard()      // ?????????????????????????????????????????????
// {
//    while (!SD.begin())
//    {
//      Serial.print("---");
//      Serial.print("Carte SD non disponible !");
//      Serial.println("---");
//      SD.end();           // Fonction non incluse dans la bib de l'IDE
//      delay(100);
//    }
//    return;
// }

//--------------------------------------------------------------------------------------------------------------------
//           FORUM   FORUM  FORUM   ROUTINE ERREUR D'INITIALISATION DU LCD, RTC ET SD
//--------------------------------------------------------------------------------------------------------------------
void erreur() {
  while (true) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(250);
    digitalWrite(LED_BUILTIN, LOW);
    delay(250);
  }
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*********************************************************************************************************************
// SETUP //////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup()                      //fonction d'initialisation de la carte
{
  //Serial.begin(9600);	        

  hc12Serial.begin(9600);         // Le port Serial 0 est utilisé par le HC-12 (défini en tant que "Serial" ligne 54)
  pinMode(LED_BUILTIN, OUTPUT);

  // D3 = INPUT interruption pin pour remise à zero v max
  pinMode(ResetVmaxPin, INPUT);
  // Déclaration de l'ISR DE REMISE 0 ZERO DE VMAX AVEC BOUTON POUSSOIR
  attachInterrupt(digitalPinToInterrupt(3),REMISE_ZERO_VMAX,CHANGE);  

  // ************** Départ communication avec le LCD 20x4 *************************************************************
  Wire.beginTransmission(0x27);  
  lcd.init();                    // initialize the lcd 20x4
  lcd.backlight();               //LCD_TAG      // retroeclairage lcd 20x4

  lcd.begin(LCD_NB_COLUMNS, LCD_NB_ROWS);  
  setup_progressbar();     // Mémorise les séries de caractères spéciaux
  
  lcd.setCursor (0,0);          // LCD_TAG go to start of 1er line
  lcd.print("ANEMOMETRE V.58");
  lcd.setCursor (0,1);          // LCD_TAG go to start of 1er line
  lcd.print("03 Mars 2024");
  lcd.setCursor (0,2);          // LCD_TAG go to start of 1er line
  lcd.print("Thomas CAUNES R.P.");
  lcd.setCursor (0,3);          // LCD_TAG go to start of 1er line
  lcd.print("V = 3 + 0.8 F km/h");
  delay(5000);       // 2s insuffisant !
  lcd.clear();

  // // ******************** INITIALISATION DS3231 **********************************************************************
  // if (!rtc.begin()) 
  // {
  //   Serial.println("Couldn't find RTC");
  //   Serial.flush();
  //   //while (1);
  // }
  // else
  // {
  //   Serial.println("DS3231 OK !");
  // }

  // // ******************** INITIALISATION CARTE SD *******************  ??????????????????   ***************************************************
  // pinMode(SDmissing, INPUT_PULLUP);   // Pin détection Carte insérée noInterrupts()
  // initSDCard();                       // Routine de détection de présence de la carte
  // Serial.println("Communication etablie avec la carte SD !"); // Quand initSDCard est ok

// ****************** PAS d'Initialisation du HC-12 avec le port Serial1  **********************************************

}  
/////  FIN SETUP  ///////////// FIN SETUP ///////////// FIN SETUP ///////////// FIN SETUP ///////////// FIN SETUP ///////

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//***********************************************************************************************************************
// BOUCLE LOOP ////////////////// BOUCLE LOOP ///////////////////// BOUCLE LOOP //////////////////// BOUCLE LOOP ////////

void loop()           
{
  
  ////////////////**************** Réception de la frequence ******************//////////////////////////////////////////
  float freq = 0;    // Déclaration de la donnée reçue

  String receivedData = "";

  ///////////////  VERSION OK  /////////////////  VERSION OK  //////////////   VERSION OK  //////////////////////////////
  if (Serial.available() > 0)       // Vérifie si des données sont disponibles
  {
    String receivedData = "";         // Initialise une chaîne pour stocker les données reçues
    while (Serial.available() > 0) // Lire toutes les données disponibles
    {
      char c = Serial.read();
      receivedData += String(c);
      delay(10);                 // A SUPPRIMER avec l'utilisation du port Serie car communication asynchrone
                                 // Mais SANS, le bargraph ne fonctionne plus ! 
    }
     // delay(10);                 // A SUPPRIMER avec l'utilisation du port Serie car communication asynchrone
    
    freq = receivedData.toFloat();
    // Serial.print("Frequence = ");
    // Serial.println(freq);
  }

  temps_precedent = micros();    // Pour la mesure d'absence d'affichage en fin de loop

  NB_PULSE_10s_INCREMENT = NB_PULSE_10s_INCREMENT + freq;     // incrémente le nombre de front descendants observés en 10s (remis à 0 après 10s dans "Calculateur 10 min)

  memoire_vmax_zero = 0; // permet de ne mettre à 0 la v instantannée max qu'une seule fois lorsque plus de 2.5s sans signal anemomètre

  // Conversion fréquence en vitesse
  if (freq == 0.0) 
  {   
      v = 0;
  }
  else if (freq <= 3.0)
  {
      v = 0.8 * freq ;
  }
  else
  {
      v = 3 + 0.8 * freq ;  
  }

  // Calcul v_max_inst
  if (affiche == 1)
  {                  // s'il y a eu affichage de la v_max_inst, elle est remise à zero 
    v_max_inst = 0;                   // remise à zero v_max_inst, de ce fait elle évolura entre l'affichage qui vient d'avoir lieu et le nouvel affichage à venir (toutes les 500ms)
    affiche = 0;                      // on déclare que l'affichage est à faire pour la prochaine v max inst
  }
      
  if (v == 0)
  {
    v_max_inst = 0;
  }

  if (v > v_max_inst && v < 200)
  {      
    v_max_inst = v;          // évolution de la vitesse instantannée (je prends la vitesse instantanée maximale obtenue entre 2 affichages (affichage toutes les 500ms)
  }

  if (v > v_max_inst_bargraph && v < 200)
  {      
    v_max_inst_bargraph = v;          // évolution de la vitesse instantannée (je prends la vitesse instantanée maximale obtenue entre 2 affichages (affichage toutes les 500ms)
  }

  if (v > v_max && v < 200)
  {
    v_max = v;                        // évolution vitesse max. 
  }
  //(1.8*v_precedente)) était 1.5 // calcul v max obtenue sur durée bloc de 10s, le coeff 
  //1.5 évite les aberrations de vitesse doublée qu'il y avait parfois
  if (v > v_max_bloc_10s && v < 200)
  { 
    v_max_bloc_10s = v;               // évolution vitesse max. sert AUX CALCULS DES RAFALES
  }

    // //pourcentage=(v);
    // pourcentage = (v_max_inst_bargraph);
    // percent = byte(pourcentage);

  t_ecoule_bargraph = TEMPS_milli - t_debut_bargraph;

  if (t_ecoule_bargraph > cadence_bargraph)
  { 
    //pourcentage=(v);
    pourcentage = (v_max_inst_bargraph);
    if (pourcentage > 0)          // Suppression du flash dû au "0" reçu avant la valeur
    {
      percent = byte(pourcentage);
    }
    draw_progressbar(percent);
    t_debut_bargraph = millis();
  }

  if (v < 200)
  { 
    v_precedente = v;
  }

  TEMPS_milli = millis();         // instant (ms) nécessaires pour cadence d'affichage
  TEMPS_micros = micros();        // instant (µs) nécessaires remise à zero v instantanée "V_max_inst" si plus de 2 s sans signal

  CALCULATEUR_10mn();             // appelle fonction de calcul des éléments sur 10mn d'observation
      
  t_ecoule_affichage = TEMPS_milli - t_debut_affichage;  // pour gérer cadence affichage 

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  if (t_ecoule_affichage > cadence_affichage)      // affichage selon cadence choisie mais au début, tant qu'il n'y a pas eu assez de front descendants, n'affiche rien
  {  
    AFFICHEUR();                              // appelle fonction d'affichage (appelée tts les 1s)

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /////////////  ************** PROGRAMME RECUPERATION DE L'HEURE SUR DS3231  ***************  ////////////////////
    // Arrêt affichage sur LCD 20x4
    Wire.endTransmission(0x27);
        
    // Bascule sur la DS3231
    Wire.beginTransmission(0x68);
    // Récupération de la date et de l'heure
    DateEtHeure();
    // Arrêt holrloge
    Wire.endTransmission(0x68);

    // Réactivation de l'écran
    Wire.beginTransmission(0x27);
    /////////////////// ************* FIN PROGRAMME RECUPERATION HEURE ************* ////////////////////////////////

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /////////////  ************** PROGRAMME ENREGISTREMENTS TOUTES LES MINUTES  ***************  ////////////////////
    // Calcul durée pour 1 min
    tpsAct_1MIN =  millis();

    // Serial.print("tpsAct = ");
    // Serial.print(tpsAct_10MIN);
    // Serial.print("- tpsPrec = ");
    // Serial.println(tpsPrec_10MIN);

    delta_T = tpsAct_1MIN - tpsPrec_1MIN;
                  
    // Serial.print("delta_T = ");
    // Serial.println(delta_T);

    if (delta_T >= duree)
    {
        // //Si la carte est insérée (pin SDmissing à 0)
        // if(!digitalRead(SDmissing))    //    ????????????????????????????????????????
        // {   
            char datafile[14];

            //************************** CREATION FICHIER ***********************************************************
            sprintf(datafile,"/%04d%02d%02d.txt",an,mois,jour);  //  %d pour un int

            delay(10);
            fichier = SD.open(datafile, FILE_WRITE);

            // Si le fichier existe
            if (fichier)
            {
                nomFichier = fichier.name();
                Serial.print("nomFichier : ");
                Serial.println(nomFichier);

                tailleFichier = fichier.size();
                if (tailleFichier == 0)   // Ecriture des en-tête
                {
                    fichier.println("Rang;Heure;Vint;Vmax;Vmoy;Vmax_10min;Rafales;Vent");
                    x = 0;
                    fichier.close();
                    delay(10); 
                }
                else
                {
                  // Récupération données du void "AFFICHEUR"
                  x++;
                  data1 = v_max_inst;
                  data2 = v_max;
                  data3 = MOY_VMOY;
                  data4 = vitesse_max_10mn;
                  data5 = typeRafale;
                  data6 = typeVent;

                  fichier.print(x),fichier.print(";"),fichier.print(heureActuelle),fichier.print(";"),fichier.print(data1),
                  fichier.print(";"),fichier.print(data2),fichier.print(";"),fichier.print(data3),fichier.print(";"),
                  fichier.print(data4),fichier.print(";"),fichier.print(data5),fichier.print(";"),fichier.print(data6);
                  
                  fichier.println();
                  fichier.close();
                  tpsPrec_1MIN = millis(); 
                }
            }
        //}
        // else
        // {
        //     initSDCard();
        // }

    }
  /////////////  ************** FIN PROGRAMME ENREGISTREMENTS TOUTES LES MINUTES  ***************  //////////////////

  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  /////////////  ************** REINITIALISATION DES VARIABLES  ***************  ////////////////////////////////////

    duree_v_nulle = TEMPS_micros - temps_precedent;  // temps écoulé depuis le dernier front montant (sert à la mise à zero de la vitesse si pas de signal pendant plus de 2s
        
    if ((duree_v_nulle > duree_remise_zero) && memoire_vmax_zero == 0)  // si plus de 2 s sans signal anemo, v max est mise à 0 et affiche une seule fois
    {
      v_max_inst = 0;                              
      memoire_vmax_zero = 1;     // variable mémoire qui permet de ne mettre à zero la vitesse instantannée qu'une seule fois. Est remise à 0 quand nouveau front montant
      percent = 0;
      draw_progressbar(percent);
    }
  }  //////////////////////////////////// FIN IF temps d'affichage dépassé ///////////////////////////////////////////

}    ///// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP ///////////// FIN LOOP //////

Je ne vois pas quoi supprimer de plus ! Misère !...
Ce dépassement de la RAM est-il rédhibitoire pour un fonctionnement efficace ?
Peut-on gérer autrement les mémoires ?
Merci J-M-L.

Vous avez des pas mal de texte dans des print() où vous n'utilisez pas la macro F(). il y a sans doute un peu de mémoire à gratter de ce côté là.

par exemple changez

  switch(classification_rafales){
    case 1: lcd.print("--"); typeRafale = "--"; break;
    case 2: lcd.print("-R"); typeRafale = "-R"; break;
    case 3: lcd.print("FR"); typeRafale = "FR"; break; 
    case 4: lcd.print("VR"); typeRafale = "VR"; break; 
  }

en

  switch (classification_rafales)
  {
    case 1: lcd.print(F("--")); typeRafale = F("--"); break;
    case 2: lcd.print(F("-R")); typeRafale = F("-R"); break;
    case 3: lcd.print(F("FR")); typeRafale = F("FR"); break;
    case 4: lcd.print(F("VR")); typeRafale = F("VR"); break;
  }

la macro F("bla bla bla") va mettre bla bla bla en mémoire flash au lieu de la RAM

vous devriez tomber sous 100% mais ce ne sera pas assez

vous pouvez sans doute aussi mettre vos caractères personnalisés sans doute en mémoire flash , ça fait gagner 48 octets de plus

vous avez 3 tableaux de 60 éléments

unsigned int PULSE[60];             // tableau de 60 valeurs (chacune des valeurs est le nb de fronts descendants observés en 10s. 60x10s=600s=10mn
unsigned int DUREE_BLOC_10s[60];    // durée exacte des blocs de 10s (60 blocs de 10 s donne 10mn)
float V_max_bloc[60];

ça fait 120 + 120 + 240 = 480 octets... pouvez vous réduire cela significativement ? est-ce que le tableau ne sert qu'à calculer une moyenne ? (on peut le faire en ne conservant que la somme et le nombre d'entrées et si vous voulez une moyenne glissante, vous pouvez faire une approximation en soustrayant la moyenne avant d'ajouter un nouvel élément. Si la variabilité pendant l'enregistrement est assez faible, la moyenne que vous enlevez sera assez proche de la plus ancienne valeur qu'il aurait fallu remplacer)

vous avez aussi de nombreuses variables globales, êtes vous sûr d'avoir besoin de toutes ces variables (il n'y a pas de petites économies).

Merci J-M-L pour toutes ces petites économies de mémoire !
C'est quand on est un peu serré que l'on est obligé d'y regarder de près.
A suivre.
Bébert

J'en suis là avec la fonction "F" :

Compiling .pio\build\nanoatmega328\src\8-HC12_ANEMO_INTERRUPT_61_F-Def-1s_NANO_Serial_Recepteur.cpp.o
Linking .pio\build\nanoatmega328\firmware.elf
Checking size .pio\build\nanoatmega328\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [==========]  97.8% (used 2002 bytes from 2048 bytes)
Flash: [==========]  95.4% (used 29294 bytes from 30720 bytes)
Building .pio\build\nanoatmega328\firmware.hex
================================================================= [SUCCESS] Took 5.09 seconds =================================================================
 *  Le terminal sera réutilisé par les tâches, appuyez sur une touche pour le fermer. 

Cela suffira-t-il ? Je verrai mon fils qui a créé ce programme au départ avec la moyenne glissante de la moyenne sur 10 min (voilà l'origine des blocs de 60). Mon rôle a été de créer la communication entre l'anémomètre émetteur et le récepteur en introduisant les HC-12. C'est un travail d'équipe ! Reste à tester.

Merci, merci... comme je ne le dirai jamais assez !
Bébert

c'est déjà mieux mais Non - 97.8% pour la RAM c'est trop. il y en a forcément qui est utilisé dynamiquement pour la pile et pour le tas vu que vous utilisez aussi beaucoup la classe String (faudrait virer toutes les Strings)

le plus gros à gagner c'est vraiment sur les 3 tableaux. vous pourriez sans doute récupérer plus de 400 octets

J'ai 4 occurrences de Strings.

1)
String stringPoints = ":"; 
String stringSlash = "/";
String heureActuelle = "";

2) nomFichier est de la forme « 20240803 » 
String nomFichier;

3)  Dans le loop() :
  if (Serial.available() > 0)       // Vérifie si des données sont disponibles
  {
    String receivedData = "";         // Initialise une chaîne pour stocker les données reçues
    while (Serial.available() > 0) // Lire toutes les données disponibles
    {
      char c = Serial.read();
      receivedData += String(c);
      delay(10);                 // A SUPPRIMER avec l'utilisation du port Serie car communication asynchrone
                                 // Mais SANS, le bargraph ne fonctionne plus ! 
    }
     // delay(10);                 // A SUPPRIMER avec l'utilisation du port Serie car communication asynchrone
    
    freq = receivedData.toFloat();

4) Dans le void dateEtHeure() :
heureActuelle = heure + stringPoints + minut;

Désolé, mais il me faut m'astreindre à étudier de plus près les variables char, strings, leurs poids et leur gestion.

  1. l'utilisation du char est-elle pertinente ?
  2. dois-je utiliser un tableau "char nomFichier[8]" ?
  3. la freq récupérée est un float. Ne peut-on déclarer "String receivedData = ""; " autrement ?
  4. comment déclarer "heureActuelle " autrement qu'en string ?
    Je vais essayer de voir de mon côté.

A suivre...
Bébert

Quand vous faites

String toto = "Hello";

Vous payez le prix fort. Hello va être en flash pour le code, puis mis en RAM lors de l’initialisation puis dupliqué dans la Strung par allocation dynamique. Bref vous avez trois fois le texte sur votre arduino…A la compilation vous voyez l’impact FLASH wet RAM mais pas la duplication aussi sur le tas.

La classe String est aussi gourmande en flash, Virer les String vous fera gagner 1.5ko .

Le gain en ram sera peu important car vous n’en avez pas trop.

Bonjour,

Pour fouiller cette question, j'ai étudié le cours que j'avais récupéré il y a quelque temps mais que je n'avais pas lu car je n'avais pas eu les problèmes actuels de gestion de mémoire. Fort instructif et clair :
https://riton-duino.blogspot.com/2019/12/commander-un-arduino-par-la-ligne-serie_21.html

Si j'ai bien compris, lorsqu'une variable est déclarée dans un void (ex : dateEtHeure en string), sa gestion peut impacter la zone BSS des variables globales si sa taille est trop importante pour la place disponible du tas ou de la pile... Bref, c'est un peu compliqué pour moi.

Bref, après avoir tenté de réduire la taille de mon programme nano récepteur sans succès, je reviens à mon plan B ; à savoir 3 nanos.
L'idée actuelle est d'utiliser pour le récepteur A du nano 2 la bibliothèque SoftwareSerial avec RX et Tx en 8 et 9 qui fonctionne bien, et d'utiliser le port série (0 et 1) du nano 2 pour la transmission des données à enregistrer au récepteur B du nano 3.
J'ai étudié le cours suivant :
https://riton-duino.blogspot.com/2019/12/commander-un-arduino-par-la-ligne-serie_21.html

Je vais essayer ainsi d'envoyer 4 float et 3 int du récepteur A au récepteur B (avec creatString ou pas ?)...
Merci pour vos remarques et suggestions.
A suivre...
Bébert

Et un plan C ? A savoir un ESP32. Même un ESP8266 sera plus puissant que 3 NANOs.

[quote="Bebert, post:18, topic:1287653"]
lorsqu'une variable est déclarée dans un void (ex : dateEtHeure en string), sa gestion peut impacter la zone BSS des variables globales si sa taille est trop importante pour la place disponible du tas ou de la pile[/quote]

on dit "dans une fonction".
➜ void c'est le type retourné par la fonction, ici on dit que la fonction ne retourne "rien du tout" (du vide).

la variable déclarée en local est toujours sur la pile.

Si c'est une String cependant (ou d'autres types qui ont besoin de mémoire supplémentaire), la zone de stockage du texte est allouée dynamiquement et donc dans le tas.

Comme on n'a qu'une seule mémoire, c'est organisé au deux bout de la mémoire. la pile est en haut et "descend" et le tas commence juste au dessus des variables globales et "monte"


(cf l'article adafruit)

Un des enjeux du développement c'est de s'assurer que le tas ne rencontre pas la pile (c'est ce qu'on appelle le stack overflow).


envisagez un protocole binaire. Vous mettez vos données dans une structure et vous émettez les octets de la structure.

Sinon si vous passez par du texte ASCII sur le port série vous pouvez jeter un oeil à mon petit tuto sur le sujet