Deep sleep arduino nano

Bonjour,

je suis en train de terminer un programme à base de arduino nano, un module RF, un écran OLED et le tout sur 2 batterie 18650 avec un BMS 2S.
Mon programme fonctionne bien mais je cherche dorénavant à passer mon arduinon en mode deep sleep. J'ai lu beaucoup de chose avant de poster mon message, sur ce forum mais aussi sur d'autres sites mais je suis perdu dans toutes les explications. Mon programme (et merci encore à hbachetti, al1fch et 68tjs de m'avoir aidé pour l'utilisation de mosfet P sur mon projet).
Le code est le suivant :

#include <Wire.h> 
#include <simpleBouton.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#define OLED_ADDR   0x3C
Adafruit_SSD1306 display(-1);

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

int compteurmenu  = 10; 

const int Down = 2; 
const int Enter = 3; 
const int Up = 4;

#define DEFAULT_DELAY 300

char buttonPressed = '0';

byte menuLevel = 0;  
byte menu = 1;
byte sub = 1;

//bool currState_B = HIGH;
bool currState_D = HIGH;
bool currState_U = HIGH;
bool currState_E = HIGH;
          
//bool prevState_B = HIGH; 
bool prevState_D = HIGH; 
bool prevState_U = HIGH; 
bool prevState_E = HIGH; 

//unsigned long prevTime_B = 0;
unsigned long prevTime_D = 0;
unsigned long prevTime_U = 0;
unsigned long prevTime_E = 0;

//unsigned long waitTime_B = 50;
unsigned long waitTime_D = 50;
unsigned long waitTime_U = 50;
unsigned long waitTime_E = 50;

const char * Niveau_Humidite;
const char * Niveau_Arrosage;
int percent = 0; 

volatile boolean arrosage = false;

const uint8_t pinFlotteur = A1;
const int SoilMoisture = A2;
int SensorValue = 0;
int ValHumidite = 0;
int ValArrosage = 1;

uint32_t T_Previous_Arrosage = 0;

enum t_etatSysteme : uint8_t {REPOS, ALERTE_VIDE} etat;

simpleBouton flotteurBas(pinFlotteur);

void majFlotteurs()
{
  flotteurBas.actualiser();
}

bool reservoirvide()
{
  return flotteurBas.estEnfonce();
}
 
bool pompeActive = false;
uint32_t t0Pompe;
const uint32_t dureeMaxPompe = 167000UL;
const byte RelaisPompe = 30; 
bool relaispompe = false;

void activerPompe()
{
  t0Pompe = millis();
  digitalWrite(RelaisPompe, LOW);
  pompeActive = true;
}

void desactiverPompe()
{
  digitalWrite(RelaisPompe, HIGH);
  pompeActive = false;
}

void setup() 
{
pinMode(Down, INPUT_PULLUP);
pinMode(Up, INPUT_PULLUP);
pinMode(Enter, INPUT_PULLUP);

Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.display();
display.clearDisplay();
etat = REPOS;  
updateLevel_0();
}

void loop()
{
arrosage();                      
checkButton();
}

void checkButton()
{
  // Button Debouncing
  bool currRead_U = digitalRead(Up);
  bool currRead_D = digitalRead(Down);
  bool currRead_E = digitalRead(Enter);

  if (currRead_U != prevState_U) {
    prevTime_U = millis();
  }
  if (currRead_D != prevState_D) {
    prevTime_D = millis();
  }

  if (currRead_E != prevState_E) {
    prevTime_E = millis();
  }

  if ((millis() - prevTime_U) > waitTime_U) {
    if (currRead_U != currState_U) {
      currState_U = currRead_U;
      if (currState_U == LOW) {
        buttonPressed = 'U';
          Serial.println("U");

      } 
    }
  } else buttonPressed = '0';
  if ((millis() - prevTime_D) > waitTime_D) {
    if (currRead_D != currState_D) {
      currState_D = currRead_D;
      if (currState_D == LOW) {
        buttonPressed = 'D';
          Serial.println("D");

      } 
    }
  } else buttonPressed = '0';

  if ((millis() - prevTime_E) > waitTime_E) {
    if (currRead_E != currState_E) {
      currState_E = currRead_E;
      if (currState_E == LOW) {
        buttonPressed = 'E';
          Serial.println("E");

      } 
    }
  } else buttonPressed = '0';

  prevState_U = currRead_U;
  prevState_D = currRead_D;
  prevState_E = currRead_E;

  processButton(buttonPressed);
}
  
void processButton(char buttonPressed)
{  
  
switch (menuLevel) {
case 0: 
     switch (buttonPressed) {
       case 'E':              
          menu = 1;
          menuLevel = 1;    
          updateLevel_1();    
          delay(DEFAULT_DELAY);
          break;
        case 'D':               
          break;
        case 'U':              
          menuLevel = 0;       
          updateLevel_0();               
          delay(DEFAULT_DELAY);
          break;
        default:
          break;   
               
}
break;
    case 1:                   
      switch ( buttonPressed ) {
        case 'E':              
          if(compteurmenu == 10)
          {
          menu = 1;
          sub = 1;
          compteurmenu = 10;
          menuLevel = 2;     
          updateLevel_2();      
          }

          
          if(compteurmenu == 20)
          {
          if (arrosage == false)
          {
          arrosage = true;
          activerPompe();
          Serial.println("yyyy");
          } 
          else if (arrosage == true)    
          {
          arrosage = false;
          desactiverPompe();
          Serial.println("xxxx");
          }
          updateLevel_1(); 
          }

          if(compteurmenu == 30)
          {
          Serial.println(" xxxx");
          menu = 1;
          sub = 2;
          compteurmenu = 10;
          menuLevel = 3;      
          updateLevel_2();     
          }
          if(compteurmenu == 40)
          {
          menu = 0;
          compteurmenu = 10;
          menuLevel = 0;       
          updateLevel_0();   
          }                    
          delay(DEFAULT_DELAY);
          
          break;
        case 'D':               // Down
          if(compteurmenu >= 40)
          {
          compteurmenu = 10;
          }
          else
          {
          compteurmenu = compteurmenu + 10;
          }
          updateLevel_1();  
          delay(DEFAULT_DELAY);
          break;
        case 'U':
          if(compteurmenu <= 10)
          {
          compteurmenu = 10;
          }
          else
          {                     
          compteurmenu = compteurmenu - 10;
          }
          updateLevel_1();                
delay(DEFAULT_DELAY);
          break;
        default:
          break;
          }
          break;

case 2:                    
      switch ( buttonPressed ) {
        case 'E':               // Enter
          if(compteurmenu == 40) 
          {
          menu = 1;
          compteurmenu = 10;
          menuLevel = 1;     
          updateLevel_1();    
          } 
          break;
        case 'D':              
          if(compteurmenu >= 40)
          {
          compteurmenu = 10;
          }
          else
          {
          compteurmenu = compteurmenu + 10;               
          }          
          updateLevel_2();      
          delay(DEFAULT_DELAY);
          break;
        case 'U': // Up
          if(compteurmenu <= 10)
          {
          compteurmenu = 10;
          }
          else
          {                     
          compteurmenu = compteurmenu - 10;               
          }             
          updateLevel_2();    
          delay(DEFAULT_DELAY);
          break;
        default:
          break;          
      }
          break;          
      
case 3:                   
      switch ( buttonPressed ) {
        case 'E':             
          if(compteurmenu == 10)
          {
          menu = 1;
          sub = 2;
          compteurmenu = 10;
          menuLevel = 4;       
          updateLevel_2();    
                    
          }
          if(compteurmenu == 20)
          {
          Serial.println("def humidite");
          menu = 1;
          sub = 2;
          compteurmenu = 20;
          menuLevel = 4;       
          updateLevel_2();  
          }
          if(compteurmenu == 30)
          {
          menu = 1;
          compteurmenu = 10;
          menuLevel = 1;       
          updateLevel_1();      
          }
          break;
        case 'D':           
          if(compteurmenu >= 30)
          {
          compteurmenu = 10;
          }
          else
          {
          compteurmenu = compteurmenu + 10;               
          }          
          updateLevel_2();     
          delay(DEFAULT_DELAY);
          break;
        case 'U': // Up
          if(compteurmenu <= 10)
          {
          compteurmenu = 10;
          }
          else
          {                     
          compteurmenu = compteurmenu - 10;               
          }             
          updateLevel_2();  
          delay(DEFAULT_DELAY);
          break;
        default:
          break;          
      } 
          break;          

case 4:                  
      switch ( buttonPressed ) {
        case 'E':           
          if(compteurmenu == 10 || compteurmenu == 20)
          {
          menu = 1;
          sub = 2;
          compteurmenu = 10;
          menuLevel = 3;        
          updateLevel_2();      
          }
          break;
        case 'D':            
          if(compteurmenu == 10 && ValArrosage < 30)
          {
          ValArrosage = ValArrosage + 1;
          }
          else if(compteurmenu == 20 && ValHumidite < 100)
          {
          ValHumidite = ValHumidite + 25;
          } 
                 
          updateLevel_2();  
          delay(DEFAULT_DELAY);
          break;
        case 'U': // Up
          if(compteurmenu == 10 && ValArrosage > 0)
          {
          ValArrosage = ValArrosage - 1;
          }
          else if(compteurmenu == 20 && ValHumidite > 0)
          {
          ValHumidite = ValHumidite - 25 ;
          }           
          updateLevel_2();     
          delay(DEFAULT_DELAY);
          break;
        default:
          break;          
      }           
          
          break;          
}
}


void updateLevel_0() 
{
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(30, 30);
    display.println("yyyyyy"); 
    display.display(); 
}
void updateLevel_1 () 
{
switch (menu) 
{
case 0 :
menu = 1;
break;

case 1:
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);    
    display.setCursor(10, 10);
    display.println("xxxx");
    display.setCursor(10, 20);
    display.println("xxxx");   
    if (arrosage == true)
    {
    display.setCursor(60, 20);
    display.println("ON");
    } 
    if (arrosage == false)    
    {
    display.setCursor(60, 20);
    display.println("OFF");
    }
    display.setCursor(10, 30);
    display.println("xxxx");   
    display.setCursor(10, 40);
    display.println("Back");       
    display.setCursor(2, compteurmenu);
    display.println(">");
    display.display();  

        break;
}
}

void updateLevel_2 () {
  switch (menu) {
    case 0:
      break;
    case 1:                                
      switch (sub) {
        case 0:
          break;
        case 1:
    display.clearDisplay();    
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(10, 10);
    display.println("xxx 1");   
    display.setCursor(10, 20);
    display.println("xxxx 2");
    display.setCursor(10, 30);
    display.println("xxxx 3"); 
    display.setCursor(10, 40);
    display.println("xxx"); 
    display.display();         
    display.setCursor(2, compteurmenu);
    display.println(">");
    display.display();         
          break;

        case 2:       
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(10, 10);
    display.println("xxxxx");  
    display.setCursor(80, 10);
    display.println(ValArrosage);    
    display.setCursor(10, 20);
    display.println("Seuil Hum");
    percent = (SensorValue - 229) * 0.43;
    display.setCursor(80, 20);
    display.println(ValHumidite);    
    display.setCursor(10, 30);
    display.println("Back");    
    display.setCursor(2, compteurmenu);
    display.println(">"); 
    display.display();         
    
          break;             
}
          break;
}
} 


void arrosage() 
{
majFlotteurs();
majLEDs();
SensorValue = analogRead(SoilMoisture);
uint32_t T_Current_Arrosage = millis();
uint32_t T_Arrosage = ValArrosage*1000UL;

switch (etat) 
       {
        case REPOS:         
                if (SensorValue < (((ValHumidite/0.33)+229)) && !reservoirvide())
                {                     
                    if(T_Current_Arrosage - T_Previous_Arrosage > T_Arrosage) 
                    {
                    Serial.println(ValArrosage);
                    T_Previous_Arrosage = T_Current_Arrosage;
                    }
                    
                }

                if (reservoirvide()) 
                {
                Serial.println(F("ALERTE RESERVOIR EAU PROPRE VIDE"));
                Serial.println(etat);
                etat = ALERTE_VIDE;
                }
                break;

        case ALERTE_VIDE:
                if (!reservoirvide())                 
                {
                etat = REPOS;
                }
                break;     
       }                       
}
                                                 

j'ai essayé d'utiliser la librairie "LowPower.h" mais en vain d'autant qu'elle rentre en conflit avec celle de Mysensors pour information (j'ai trouvé une solution ici : Compilation error when using LowPower.h library with MySensors | MySensors Forum).

Ma question, comment intégrer une fonction deep sleep dans mon programme pour permettre simplement la lecture des capteurs 1 fois par jour et hop au dodo le reste du temps?
Merci d'avance pour votre aide.

La librairie MySensors possède sa propre fonction, qui endort l'ARDUINO et le module RF :

Voir tout en bas.

La NANO n'est certainement pas la meilleure plateforme pour ce type de projet, idem pour l'écran OLED.
Rien que la NANO consommera à elle-seule presque 9mA, et 4mA si tu vires le LED POWER, et à cela il faut ajouter l'écran.

Voir 6. ARDUINO NANO

Bonjour et merci de ce retour,

un arduino pro mini serait plus adapté je suppose?
J'avais pensé à supprimer le régulateur de tension (en plus de la led power) du nano pour passer par la broche 5v directement afin de gagner encore en consommation, serait un choix judicieux?
Pour le oled, je pensais justement à couper l'alimentation de ce dernier en plus du mode deep sleep de l'arduino mais je ne sais pas si cela est possible à moins de passer par un mosfet ou relais directement. Car ce dernier n'est vraiment utile pour la phase de démarrage nécessaire aux réglages de l'interface.

La réponse se trouve dans mon article.

Oui, avec un MOSFET P.

EDIT : le SSD1306 a un sleep mode :

Merci, ç'est clair, j'ai vu cela dans votre article mais j'ignorais que vous étiez l'éditeur de "RitonDuino", site que j'ai très souvent consulté pour mes projets alors merci pour tous ces milliers de précieux conseils!
Je vais regarder le Github de l'oled.
Je vais dans un premier temps supprimer le convertisseur et la led sur mon nano à défaut d'avoir un pro mini sous la main :wink:
Un Mofset N pourrait aussi convenir et ainsi utiliser la sortie de contrôle de l'arduino en LOW, me trompes je? Sauf que l'oled va consommer probablement plus en coupant le neutre que la simple sortie de l'arduino allez vous peut être me dire?
Merci

Le problème est ensuite d'alimenter en 5V à partir d'une batterie.
La tension d'une 18650 ne suffira pas. Seule une PRO MINI 8MHz 3.3V démarrera à 3.7V et même à 3V. On peut bien sûr élever cette tension à 5V à l'aide d'un bon convertisseur STEP-UP, mais cela ajoute de la consommation :

  • XR2981 : 120µA
  • MT3608 : 170µA

La tension de deux 18650 en série sera trop importante. On pourrait l'abaisser à l'aide d'un bon régulateur LDO : un HT7333-1 par exemple, qui ajoutera seulement 4µA.

A la réflexion, couper l'OLED par le rail négatif serait OK, étant donné que les lignes SDA et SCL seront à HIGH au repos, aucun courant ne devrait circuler dans les résistances PULLUP.
Mais il serait bien d'essayer quand même les commandes ON et OFF de l'afficheur.
Dans le mode OFF l'OLED consomme seulement 10µA. Ce n'est pas grand chose. A mon avis le courant d'auto-décharge de la batterie devrait être supérieur à cette valeur, surtout si ce sont des batteries chinoises NONAME.

Ce n'est pas si simple.
A moins de bien connaitre le sujet, je conseille de lire :

J'ai effectivement branché 2 batteries LG presque neuves pour avoir entre 8,4V et 6,4V en pleine décharge que j'ai d'abord déchargé comme conseillé sur ton site. Je l'alimente avec un convertisseur Boost à 8,4V (recommandé par le constructeur) à travers une alimentation 5V de 2A.
J'ai donc un LDO de 5V (BA50BC0FP-E2 LDO) en sortie pour mon arduino mais je ne sais à quel paramètre correspond sa consommation max.
Je vais d'abord essayer la partie commande avant de passer par un mosfet et mesurer le courant consommé même si ma pince amperemétrique doit manquer de précision sur de si faibles consommation, je peux tenter.

https://www.mouser.com/datasheet/2/348/baxxbc0-e-1018445.pdf
Avec 80µA de courant de repos, ça commence à faire beaucoup.
Et un courant maxi de 1A n'est pas forcément utile. Il serait étonnant qu'un 250mA ne soit pas suffisant. Et dans la catégorie 250-500mA on trouve des régulateurs exceptionnels.

Je suppose qu'il s'agit de la ligne "CTL Input Current " dans la data sheet ? Je ne savais pas à quoi cela correspondait, merci. Il faudrait que je regarde pour un nouveau régulateur moins gourmand alors :frowning: mince sur les sites ou je suis allé (farnell et mouser), les frais de ports sont très importants pour mes toutes petites commandes. Peut être sur ebay avec un peu de chance.

Pour information, les commandes du site que vous m'avez préconisé ne fonctionnaient pas pour moi, peut être une question de librairie mais ces commandes fonctionnent pour la librairie "Adafruit" :

display.ssd1306_command(SSD1306_DISPLAYOFF);
  delay(5000);
  display.ssd1306_command(SSD1306_DISPLAYON);
  display.println("Awake!");
  display.display();

Déjà cela de trouvé :slight_smile:

Je suis extrêmement satisfait des HT7333-1 (250mA).
Je les commande sur AliExpress. Ils existent en CMS ou traversant.

Ceci me rappelle les déboires de leg2027.
Il vaudrait mieux faire certaines vérifications, en particulier la tension batterie en fin de charge.

Bonsoir,
je comprends mieux, je dois utiliser une alimentation 5v 1A max pour mon cas (C/2) avec mes 18650 2500mAh. J en ai une de 500mA, ca sera plus long mais plus prudent. Pour le reste, mon montage semble fonctionner mais je ne trouve pas de carte d equilibrage séparée 2S comme préconisé dans votre tutoriel. Je continue mes recherches.
Pour le code du deep sleep, ca fonctionne mais je ne suis pas sur du calcul pour avoir un reveil toutes les 15 minutes.
Je comprends que 8s sont le max de la mise en sommeil.

void lowPowerSleep(int minutes)
{
  int seconds = minutes * 60;
  int sleeps = seconds / 8;
  for (int i = 0 ; i < sleeps ; i++) {
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
  }

De ce que j en comprends, sleeps = 112,5 à i=0. À i=1, 8 secondes de sommeil se sont ecoulées et il recommence jusque i=112,5 avec 8s de sommeil à chaque fois, soit 112,5 x 8 = 15 minutes. Donc il s agit de micor sommeil de 8s successifs?

A l.issue de cela, l arduino effectue la boucle pendant 1000ms et recommence son sommeil de 15min, c est bien cela?

void loop() {
  lowPowerSleep(15);
  Serial.println("HELLO");
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
}

Un autre probleme, la librairie mysensors.h semble etre en conflit avec celle du lowpower.h mais je ne sais pas encore pourquoi.

A savoir si l'alimentation à découpage coupe sa sortie à 1A ou si elle limite son courant à 1A.
Il serait bien de vérifier ceci.

  • mesurer la tension de l'alimentation et de la batterie pendant la charge
  • en début de charge la tension doit croître
  • en fin de charge le courant doit décroître

Oui. Mais comme je disais plus haut la librairie MySensors possède une fonction sleep() :

unsigned long SLEEP_TIME = 900000L; // Sleep time between reads (15 minutes)
// ...
  sleep(SLEEP_TIME);

lowpower est donc parfaitement inutile.

Bonjour,

merci, je vais faire l'exercice pour la batterie. C'est en effet cette partie que je n'ai pas bien saisi dans le post que tu as mis en lien. La différence générée entre la limitation de courant à max C/2 ou une coupure de l 'alimentation au delà de 1A. Je ne comprends pas bien en quoi la conséquence serait différente.
Pour la librairie mysensors, je pensais que cela ne concernait que le module Bluetooth et non pas l'arduino d'ou ma question redondante.

Bonjour, comme le déclenchement de la pompe se fait par le contact du flotteur, j'utiliserai plutôt le réveil par interruption, ce qui éviterait un réveil inutile toute les 8 secondes Low-Power/powerDownWakeExternalInterrupt.ino at master · rocketscream/Low-Power · GitHub

Non, avec MySensors, le réveil par PIR, bouton, capteur, etc. se fait comme ceci :

1 Like

Bonjour à la communauté,

je reviens vers vous dans la continuité du sujet initial.
Afin d'intégrer le deep sleep Mysensors conseillé par hbachetti, j'ai d'abord déclaré mon module 433Mhz. Il est bien reconnu et transmets correctement de la data.
Cependant, un problème survient dans mon programme qui est structuré en 3 parties :

  1. Déclaration des fonctions boutons
  2. Définition des menus sur l'écran et navigation
  3. Void nécessaire aux déclenchements de fonctions.

Lorsque j'intègre mon module 433Mhz, une partie du programme n'est pas "lu" :

  1. Le "void checkButton()" fonctionne
  2. "arrosage_plantes();" également
  3. void processButton(char buttonPressed) n'est pas "lu" apparemment

J'ai intégré de nombreux println pour le voir à travers la console et je ne comprends pas ce qui peut bloquer. Je poste mon programme si l'un d'entre vous a un peu de temps pour jeter un oeil, ça serait top :slight_smile:
Merci

//$$$$$$$$$$$$$$$$$$$$$$$$$$$ LIBRAIRIES $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
#include <Wire.h> 
#include <simpleBouton.h> 
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#define OLED_ADDR   0x3C
Adafruit_SSD1306 display(-1);

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

//$$$$$$$$$$$$$$$$$$$$$$$$$$$ CONFIG RFX $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

#define MY_DEBUG 
#define MY_RADIO_RF24
#include <MySensors.h>
#define CHILD_ID_pinFlotteur_Bas 110

#define MY_PARENT_NODE_ID 0
#define MY_PARENT_NODE_IS_STATIC

MyMessage msgBas(CHILD_ID_pinFlotteur_Bas,V_TRIPPED);

#define SLEEP_TIME 50000 // 15 min
uint32_t sleepTime = SLEEP_TIME;
int8_t wakeupReason = MY_WAKE_UP_BY_TIMER;


//$$$$$$$$$$$$$$$$$$$$$$$$$$$ VARIABLES MENU $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

int compteurmenu  = 10; 

const int Down = 6; 
const int Enter = 7; 
const int Up = 8; 

#define DEFAULT_DELAY 300

char buttonPressed = '0';

byte menuLevel = 0;
                      
byte menu = 1;
byte sub = 1;

//bool currState_B = HIGH;
bool currState_D = HIGH;
bool currState_U = HIGH;
bool currState_E = HIGH;
          
//bool prevState_B = HIGH; 
bool prevState_D = HIGH; 
bool prevState_U = HIGH; 
bool prevState_E = HIGH; 

unsigned long prevTime_D = 0;
unsigned long prevTime_U = 0;
unsigned long prevTime_E = 0;

unsigned long waitTime_D = 50;
unsigned long waitTime_U = 50;
unsigned long waitTime_E = 50;

const char * Niveau_Humidite;
const char * Niveau_Arrosage;
int percent = 0; 

volatile boolean arrosage = false;

volatile boolean ecran = false;

//$$$$$$$$$$$$$$$$$$$$$$$$$$$ VARIABLES CAPTEURS $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

#define pinFlotteur 3

const int SoilMoisture = A2;
int SensorValue = 0;
int ValHumidite = 0;
int ValArrosage = 1;


enum t_etatSysteme : uint8_t {REPOS, ALERTE_VIDE, POMPE_ACTIVE, POMPE_DESACTIVE} etat;

simpleBouton flotteurBas(pinFlotteur);

void majFlotteurs()
{
  flotteurBas.actualiser();
}

bool reservoirvide()
{
  return flotteurBas.estEnfonce();
}
 
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ POMPES $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
// état de la pompe
bool pompeActive = false;

uint32_t t0Pompe;

const uint32_t dureeMaxPompe = 167000UL; 

//Numéro du pin pour les capteurs de niveau
const byte RelaisPompe = 2; 

// état du relais 
bool relaispompe = HIGH;

void activerPompe()
{
  t0Pompe = millis();
  Serial.println(F("Activation pompe"));
  digitalWrite(RelaisPompe, LOW);
  pompeActive = true;
}

void desactiverPompe()
{
  Serial.println(F("Desactivation pompe"));
  digitalWrite(RelaisPompe, HIGH);
  pompeActive = false;
}

//$$$$$$$$$$$$$$$$$$ GESTION DES LEDS $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
// LES LEDS (pin --- R 220Ω ----- Anode [LED] --- Cathode --- GND
const uint8_t pinLedAlerteReservoirVide = 38;

bool clignoteVide = false;  // mémoire état clignotement LED réservoir eau propre

void majLEDs()
{
  static uint32_t t0Vide = 0;
  const uint32_t demiPeriode = 500UL; // clignote une fois par seconde 500UL = 50ms

   if (clignoteVide) 
   {
      if (millis() - t0Vide > demiPeriode) 
          {
          if (digitalRead(pinLedAlerteReservoirVide) == HIGH) 
          digitalWrite(pinLedAlerteReservoirVide, LOW);
          else digitalWrite(pinLedAlerteReservoirVide, HIGH);
          t0Vide = millis();
          }
    } 
    else digitalWrite(pinLedAlerteReservoirVide, LOW);  
}

//$$$$$$$$$$$$$$$$$$ PRESENTATION $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
void presentation() 
{
//sendSketchInfo("Sleep + Interrupts", "1.0");
present(CHILD_ID_pinFlotteur_Bas, S_DOOR);
}

//$$$$$$$$$$$$$$$$$$ SETUP $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
void setup() 
{
pinMode(Down, INPUT_PULLUP); // met la broche en entrée
pinMode(Up, INPUT_PULLUP); // met la broche en entrée
pinMode(Enter, INPUT_PULLUP); // met la broche en entrée

//pinMode(pinFlotteur, INPUT); // met la broche en entrée

Serial.begin(115200);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);

display.display();
display.clearDisplay();
Serial.println(F("SYSTEME PRET"));

digitalWrite(RelaisPompe, HIGH);

etat = REPOS;  
updateLevel_0();
}

//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ LOOP $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
void loop()
{
arrosage_plantes();                      
checkButton();
}

//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ STATUS ETAT BOUTON $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
void checkButton()
{
  // Button Debouncing
  bool currRead_U = digitalRead(Up);
  bool currRead_D = digitalRead(Down);
  bool currRead_E = digitalRead(Enter);

  if (currRead_U != prevState_U) {
    prevTime_U = millis();
  }
  if (currRead_D != prevState_D) {
    prevTime_D = millis();
  }

  if (currRead_E != prevState_E) {
    prevTime_E = millis();
  }

  if ((millis() - prevTime_U) > waitTime_U) {
    if (currRead_U != currState_U) {
      currState_U = currRead_U;
      if (currState_U == LOW) {
        buttonPressed = 'U';
          Serial.println("U");

      } 
    }
  } else buttonPressed = '0';
  if ((millis() - prevTime_D) > waitTime_D) {
    if (currRead_D != currState_D) {
      currState_D = currRead_D;
      if (currState_D == LOW) {
        buttonPressed = 'D';
          Serial.println("D");

      } 
    }
  } else buttonPressed = '0';

  if ((millis() - prevTime_E) > waitTime_E) {
    if (currRead_E != currState_E) {
      currState_E = currRead_E;
      if (currState_E == LOW) {
        buttonPressed = 'E';
          Serial.println("E");

      } 
    }
  } else buttonPressed = '0';

  prevState_U = currRead_U;
  prevState_D = currRead_D;
  prevState_E = currRead_E;

  processButton(buttonPressed);
}
  
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ MENU $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
void processButton(char buttonPressed)
{  
  
switch (menuLevel) {
case 0: //page 1, home screen
     switch (buttonPressed) {
       case 'E':                // Enter
       
          if (ecran == true)    
          {
          ecran = false;
          display.ssd1306_command(SSD1306_DISPLAYON);          
          Serial.println("ECRAN ON");
          }
                    
          menu = 1;
          menuLevel = 1;        // go to main menu
          updateLevel_1();      // show main menu
          delay(DEFAULT_DELAY);
          Serial.println("CA MARCHE BORDEL"); 
          break;
        case 'D':               // Down
          break;
        case 'U':               // Back
          menuLevel = 0;        // go to home screen
          updateLevel_0();      // show home screen            
          delay(DEFAULT_DELAY);
          break;
        default:
          break;   
               
}
break;
    case 1:                     // Level 1, main menu
      switch ( buttonPressed ) {
        case 'E':               // Enter

          //Serial.println("retour case départ");

          if(compteurmenu == 10)
          {
          Serial.println("PLANTES");
          menu = 1;
          sub = 1;
          compteurmenu = 10;
          menuLevel = 2;        // go to sub menu
          updateLevel_2();      // show sub menu
          }

          
          if(compteurmenu == 20)
          {
          if (arrosage == false)
          {
          arrosage = true;
          activerPompe();
          Serial.println("ARROSAGE ON");
          } 
          else if (arrosage == true)    
          {
          arrosage = false;
          desactiverPompe();
          Serial.println("ARROSAGE OFF");
          }
          updateLevel_1();      // show sub menu
          }

          
          if(compteurmenu == 30)
          {
          Serial.println(" REGLAGES");
          menu = 1;
          sub = 2;
          compteurmenu = 10;
          menuLevel = 3;        // go to sub menu reglages
          updateLevel_2();      // show sub menu reglages
          }

          if(compteurmenu == 40)
          {
          if (ecran == false)
          {
          ecran = true;
          display.ssd1306_command(SSD1306_DISPLAYOFF);          
          Serial.println("ECRAN OFF");
          menu = 0;
          compteurmenu = 10;
          menuLevel = 0;        // go to home screen
          updateLevel_0();      // show home screen          
          } 
          //else if (ecran == true)    
          //{
          //ecran = false;
          //display.ssd1306_command(SSD1306_DISPLAYON);          
          //Serial.println("ECRAN ON");
          //}
         // updateLevel_1();      // show sub menu
          }
 
          if(compteurmenu == 50)
          {
          menu = 0;
          compteurmenu = 10;
          menuLevel = 0;        // go to home screen
          updateLevel_0();      // show home screen
          }                    
          delay(DEFAULT_DELAY);
          
          break;
        case 'D':               // Down
          if(compteurmenu >= 40)
          {
          compteurmenu = 10;
          }
          else
          {
          compteurmenu = compteurmenu + 10;
          // decrease level menu (move ">" symbol)
          }
          updateLevel_1();      // show main menu
          delay(DEFAULT_DELAY);
          break;
        case 'U': // Up
          if(compteurmenu <= 10)
          {
          compteurmenu = 10;
          }
          else
          {                     
          compteurmenu = compteurmenu - 10;
          //increase level menu (move ">" symbol)
          }
          updateLevel_1();      // show main menu
          delay(DEFAULT_DELAY);
          break;
        default:
          break;
          }
          break;

case 2:                     // Level 2, sub menu
      switch ( buttonPressed ) {
        case 'E':               // Enter
          Serial.println(" NIVEAU PLANTES");
          if(compteurmenu == 40) //Back
          {
          menu = 1;
          compteurmenu = 10;
          menuLevel = 1;        // go to home screen
          updateLevel_1();      // show home screen
          }
          
          //if(compteurmenu == 10) //Paramétres de la première plante (exemple Orchidée
          // Arrosage à 75% toutes les 3 semaines
          //{
          

          
          //intégrer un plantes() issu de void plantes pour lancer une séquence          
          break;
        case 'D':               // Down
          if(compteurmenu >= 40)
          {
          compteurmenu = 10;
          }
          else
          {
          compteurmenu = compteurmenu + 10;               
          // decrease level menu (move ">" symbol)
          }          
          updateLevel_2();      // show sub menu plantes
          delay(DEFAULT_DELAY);
          break;
        case 'U': // Up
          if(compteurmenu <= 10)
          {
          compteurmenu = 10;
          }
          else
          {                     
          compteurmenu = compteurmenu - 10;               
          //increase level menu (move ">" symbol)
          }             
          updateLevel_2();      // show main menu
          delay(DEFAULT_DELAY);
          break;
        default:
          break;          
      }
          break;          
      
case 3:                     // Level 2, sub menu
      switch ( buttonPressed ) {
        case 'E':               // Enter
          Serial.println(" NIVEAU REGLAGES");
          if(compteurmenu == 10)
          {
          Serial.println("def Eau");            
          menu = 1;
          sub = 2;
          compteurmenu = 10;
          menuLevel = 4;        // go to home screen
          updateLevel_2();      // show home screen
                    
          }
          if(compteurmenu == 20)
          {
          Serial.println("def humidite");
          menu = 1;
          sub = 2;
          compteurmenu = 20;
          menuLevel = 4;        // go to home screen
          updateLevel_2();      // show home screen
          }
          if(compteurmenu == 30)
          {
          menu = 1;
          compteurmenu = 10;
          menuLevel = 1;        // go to home screen
          updateLevel_1();      // show home screen
          }
          break;
        case 'D':               // Down
          if(compteurmenu >= 30)
          {
          compteurmenu = 10;
          }
          else
          {
          compteurmenu = compteurmenu + 10;               
          // decrease level menu (move ">" symbol)
          }          
          updateLevel_2();      // show sub menu plantes
          delay(DEFAULT_DELAY);
          break;
        case 'U': // Up
          if(compteurmenu <= 10)
          {
          compteurmenu = 10;
          }
          else
          {                     
          compteurmenu = compteurmenu - 10;               
          //increase level menu (move ">" symbol)
          }             
          updateLevel_2();      // show main menu
          delay(DEFAULT_DELAY);
          break;
        default:
          break;          
      } 
          break;          

case 4:                     // Level 2, sub menu
      switch ( buttonPressed ) {
        case 'E':               // Enter
          Serial.println(" NIVEAU DEFINITION NIVEAU");
          if(compteurmenu == 10 || compteurmenu == 20)
          {
          Serial.println("sortie");
          menu = 1;
          sub = 2;
          compteurmenu = 10;
          menuLevel = 3;        // go to home screen
          updateLevel_2();      // show home screen
          }
          //intégrer un plantes() issu de void plantes pour lancer une séquence          
          break;
        case 'D':               // Down
          if(compteurmenu == 10 && ValArrosage < 30)
          {
          ValArrosage = ValArrosage + 1;
          }
          else if(compteurmenu == 20 && ValHumidite < 100)
          {
          ValHumidite = ValHumidite + 25;
          } 
                 
          updateLevel_2();      // show sub menu plantes
          delay(DEFAULT_DELAY);
          break;
        case 'U': // Up
          if(compteurmenu == 10 && ValArrosage > 0)
          {
          ValArrosage = ValArrosage - 1;
          }
          else if(compteurmenu == 20 && ValHumidite > 0)
          {
          ValHumidite = ValHumidite - 25 ;
          }           
          updateLevel_2();      // show main menu
          delay(DEFAULT_DELAY);
          break;
        default:
          break;          
      }           
          
          break;          
}
}


//$$$$$$$$$$$$$$$$$$$$$$$$$$$$ NIVEAU MENU ZERO $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
void updateLevel_0() 
{
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(30, 30);
    display.println("Shhhh It Grows !"); // MAIN MENU
    display.display(); 
}
//$$$$$$$$$$$$$$$$$$$$$$$$$$$ NIVEAU MENU 1 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
void updateLevel_1 () 
{
switch (menu) 
{
case 0 :
menu = 1;
break;

case 1:
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);    
    display.setCursor(10, 10);
    display.println("Plantes");
    display.setCursor(10, 20);
    display.println("Arrosage");   
    if (arrosage == true)
    {
    display.setCursor(60, 20);
    display.println("ON");
    } 
    if (arrosage == false)    
    {
    display.setCursor(60, 20);
    display.println("OFF");
    }
    display.setCursor(10, 30);
    display.println("Reglages");   
    display.setCursor(10, 40);
    display.println("Extinction ecran"); 
    display.setCursor(10, 50);
    display.println("Back");           
    display.setCursor(2, compteurmenu);
    display.println(">");
    display.display();  

        break;
}
}

//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ NIVEAU MENU 2 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
void updateLevel_2 () {
  switch (menu) {
    case 0:
      break;
    case 1:                                 // Plantes
      switch (sub) {
        case 0:
          break;
        case 1:
    Serial.println(" MENU PLANTES");
    display.clearDisplay();    
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(10, 10);
    display.println("Plante 1");   
    display.setCursor(10, 20);
    display.println("Plante 2");
    display.setCursor(10, 30);
    display.println("Plante 3"); 
    display.setCursor(10, 40);
    display.println("Back"); 
    display.display();         
    display.setCursor(2, compteurmenu);
    display.println(">");
    display.display();         
          break;

        case 2:       
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(10, 10);
    display.println("Eau/Sem");  
    display.setCursor(80, 10);
    display.println(ValArrosage);    
    display.setCursor(10, 20);
    display.println("Seuil Hum");
    percent = (SensorValue - 229) * 0.43;
    display.setCursor(80, 20);
    display.println(ValHumidite);    
    display.setCursor(10, 30);
    display.println("Back");    
    display.setCursor(2, compteurmenu);
    display.println(">"); 
    display.display();         
    
          break;             
}
          break;
}
} 

//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ CONDITIONS ARROSAGE $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

uint32_t T_Previous_Duree_Arrosage = 0;
uint32_t T_Previous_Freq_Arrosage = 0;

uint32_t T_Current_Duree_Arrosage = 0;
uint32_t T_Duree_Arrosage = 6000UL;

uint32_t T_Current_Freq_Arrosage = 0;
uint32_t T_Freq_Arrosage = ValArrosage*10000UL;


void arrosage_plantes() 
{
T_Current_Freq_Arrosage = millis();
T_Current_Duree_Arrosage = millis();
  
majFlotteurs();  // suivi de l'état des flotteurs
majLEDs();
SensorValue = analogRead(SoilMoisture);

switch (etat) 
       {
        case REPOS: 
           
                if (SensorValue < (((ValHumidite/0.33)+229)) && !reservoirvide())
                {                     
                    if(T_Current_Freq_Arrosage - T_Previous_Freq_Arrosage > T_Freq_Arrosage) 
                    {
                    T_Previous_Duree_Arrosage = T_Current_Duree_Arrosage;                                      
                    activerPompe();
                    etat = POMPE_ACTIVE;                  
                    }
                }

                if (reservoirvide()) 
                {
                Serial.println(F("ALERTE RESERVOIR EAU PROPRE VIDE"));
                desactiverPompe();                
                etat = ALERTE_VIDE;
                }
                break;


        case POMPE_ACTIVE:

                if(T_Current_Duree_Arrosage - T_Previous_Duree_Arrosage > T_Duree_Arrosage) 
                {
                desactiverPompe();
                T_Previous_Freq_Arrosage = T_Current_Freq_Arrosage;
                etat = REPOS; 
                }
                break;                                                               
                        
        case ALERTE_VIDE:
                if (!reservoirvide())                 
                {
                Serial.println(F("RESERVOIR EAU OK"));
                etat = REPOS;
                }
                break;     
       }                       
}

                                                  

Je crains qu'il s'agisse d'un problème mémoire.

Les variables globales utilisent 1090 octets (53%) de mémoire dynamique, ce qui laisse 958 octets pour les variables locales. Le maximum est de 2048 octets.

Pour un écran 128*64, la librairie Adafruit va allouer 128 * ((64 + 7) / 8) = 1136 octets de RAM.
Or il t'en reste 968.
Je te conseillerais de jeter un œil ici :
https://riton-duino.blogspot.com/2019/11/afficheur-oled-ssd1306-comparons.html
Cet article t'aidera à comprendre les différences entre Adafruit_SSD1306 et SSD1306Ascii.

Merci beaucoup hbachetti, je n'aurais effectivement jamais pensé à cela. J'ai vu rapidement grâce à lien (mais je vais me pencher dessus), qu'une autre librairie serait plus adaptée. Quand on pense être au bout d'un problème, un autre pointe son nez...