Arduino ne démarre plus qd je rajoute du code

Bonjour, je suis entrain de réaliser une petite machine pour controler le ph de mon eau d'aquarium sur une mega2560. C'est plutot en bonne voie mais un ENORME probleme survient, que je n'arrive pas à résoudre.

J'ai pas mal de classes en c++, ci dessous une partie du code principal que j'upload. J'ai mis le .ino au complet en piece jointe.

Lorsque je rajoute la ligne Serial.println("------------------------------"); n'importe ou dans le loop, l'arduino ne démarre plus. Cela est vrai avec d'autre instructions, notamment des functions contenues dans mes libraries.

J'ai un autre problème, dans les menus menuTankCapacity et menuTime, qui sont pourtant de type différent, le texte ne s'affiche plus sur l'écran mais les boutons sont bien présent et actif. Pourtant ils marchaient très bien avant, et d'autres menus utilisent les mm classes et sont opérationels.

Je pense que ces 2 problemes sont liés et doivent venir d'un probleme d'allocation de mémoire qui viens écraser des bouts de codes ou je ne sais quoi. Je ne suis pas un programmeur pro, je fais sur mon temps libre alors mes connaissances sont un peu limitées. Apres compilation j'ai ca comme retour,

Binary sketch size: 55,138 bytes (of a 258,048 byte maximum)
Estimated used SRAM memory: 6,208 bytes (of a 8192 byte maximum)

apparement y'a encore de la place !

Si vous voulez voir mes classes je les rajouterai en piece jointe. Merci à la personne qui sera motivée pour foutre son nez dans mon code :slight_smile:

#include <Time.h>                // Time lib
#include <DS1307RTC.h>           // RTC lib
#include <Wire.h>                // RTC  lib
#include <EEPROM.h>
#include <SPI.h>
#include <SD.h>
#include <stdlib.h>
//#include <avr/wdt.h>               //reboot library

#include "Dbug.h"
#include "MyFunc.h"
#include "MyLiquidCrystal.h"
#include "MyLcd.h"
#include "Button.h"
#include "ButtonsManager.h"
#include "LcdMenu.h"
#include "LcdMenuInput01.h"
#include "LcdMenuCalib.h"
#include "Queue.h"
#include "Ph.h"
#include "PhValue.h"
#include "Ec.h"
#include "LcdMenuInputTime.h"
#include "TimerTime.h"
#include "Memory.h"
#include "Pump.h"

// =====================================================================================
// ==                                  VARIABLES                                      ==
// =====================================================================================

MyLiquidCrystal lcd(23,25,27,29,31,33);
MyLcd myLcd(lcd);

// General Variables
const int OnLedPin = 3;
Memory dataManager(myLcd,false);
boolean timeRTCAvailable ;
int loopCount;

// Menus Variables
int btnIndex;                            // btnIndex 1=Menu/Ok 2=Exit 3=Plus 4=Minus
ButtonsManager btnManager("Menu/Ok",28,"Exit",22,"Plus",26,"Minus",24);
int menuGIndex;
int menuIndex;
String menuGeneralText[] = { "PH settings", "EC settings", "Tank volume", "Time settings" };
LcdMenu menuGeneral(myLcd, btnManager, "General", menuGeneralText, sizeof(menuGeneralText)/sizeof(String),30);

// PH Variables
const int PhPin = A10;
Ph phManager(PhPin,10,5,8,9);          // pump ph- pin=8, pump ph+ pin=9
int phManagerReturnCode ;
String menuPhText[] = { "Ph7 calibration", "Ph4 calibration", "Goal settings", "Delta settings", "Ph- Sol", "Ph+ Sol" };
LcdMenu menuPh(myLcd, btnManager, "Ph", menuPhText, sizeof(menuPhText)/sizeof(String),180);
LcdMenuInput01 menuPhGoal(myLcd, btnManager, "PH Goal", "Exit", "-/+", "Ok", "Goal= ", 5, 100, 180);
LcdMenuInput01 menuPhDelta(myLcd, btnManager, "PH Delta", "Exit", "-/+", "Ok", "Delta= ", 1, 100, 180);
LcdMenuInput01 menuPhMinusSol(myLcd, btnManager, "PH- sol", "Exit", "-/+", "Ok", "Ph-= ", 5, 100, 180);
LcdMenuInput01 menuPhPlusSol(myLcd, btnManager, "PH+ sol", "Exit", "-/+", "Ok", "Ph+= ", 5, 100, 180);
LcdMenuCalib menuPh7Calib(myLcd, btnManager, phManager, "PH7 calibration", "Ph 7.0=", ph7CalibValueDefault, 1800);
LcdMenuCalib menuPh4Calib(myLcd, btnManager, phManager, "PH4 calibration", "Ph 4.0=", ph4CalibValueDefault, 1800);

// EC Variables
const int ecPin = A11;
Ec ecManager(ecPin);
String menuEcText[] = { "Ec calibration", "Ec Goal", "Ec delta", "Ec Sol"};
LcdMenu menuEc(myLcd, btnManager, "Ec", menuEcText, sizeof(menuEcText)/sizeof(String),180);
LcdMenuInput01 menuEcGoal(myLcd, btnManager, "EC Goal", "Exit", "-/+", "Ok", "Goal= ", 5, 100, 180);
LcdMenuInput01 menuEcDelta(myLcd, btnManager, "EC Delta", "Exit", "-/+", "Ok", "Delta= ", 5, 100, 180);
LcdMenuInput01 menuEcSol(myLcd, btnManager, "EC sol", "Exit", "-/+", "Ok", "Ec= ", 5, 100, 180);

// Tank Variables
int tankVolume = 10;
LcdMenuInput01 menuTankCapacity(myLcd, btnManager, "Tank capacity", "Exit", "-/+", "Ok", "Liters=  ", 1, 1, 180);

// TimeMenu Variables
TimerTime timer;
String menuTimeText[] = { "Date set", "Time set", "Timer programation" };
LcdMenu menuTime(myLcd, btnManager, "Time", menuTimeText, sizeof(menuTimeText)/sizeof(String),180);
LcdMenuInputTime menuTimeSet(myLcd, btnManager, "Time set menu", 180);
LcdMenuInputTime menuDateSet(myLcd, btnManager, "Date set menu", 180);
LcdMenuInputTimer menuTimerSet(myLcd, btnManager, "Timer set menu", 180);

// =====================================================================================
// ==                              FUNCTIONS                                          ==
// =====================================================================================

// Arduino functions ===================================================================
void setup() {
   Serial.begin(9600);
   myLcd.Clear();
   myLcd.ShowChar(".START.",6,1);
   Serial.println("----Start-----");
//   digitalWrite(OnLedPin,HIGH);
   delay(1000);
   timeRTCAvailable = TimeTest();
   dataManager.SetTimeAvailable(timeRTCAvailable);
   dataManager.SDInit();
   
   if (!dataManager.IsFirstStart()) {
     Serial.println("not first start");
      phManager.values = dataManager.ReadPh();
      timer = dataManager.ReadTimer();  
   } else {
     Serial.println("first start");
     dataManager.SavePh(phManager.values); 
     dataManager.SaveTimer(timer); 
     dataManager.SetFirstStart();     
   }
  
   //tankVolume = dataManager.ReadTank();
   if (dataManager.IsSDActive()){
     dataManager.Log("Event",1);
   }
   ShowMonitor();
   loopCount = 0;
}

void loop()
{

  Serial.println("------------------------------");   /// <--------- PROBLEME QD JE RAJOUTE CETTE LIGNE, ARDUINO NE VEUT PLUS DEMARRER

  loopCount++;
  btnIndex = btnManager.WaitForButtonRelease(1000);
  if (btnIndex == 1) {
    StartMenus();
    ShowMonitor();
    btnIndex=0;
  }
  
  phManager.ReadAndSet(500);
  // add inst ph value to the queue
  if (loopCount >1) {
    phManager.Add();
    loopCount = 0;
  }
  phManager.queue.Dbug();
  phManager.TestAndInject(tankVolume);
  RefreshMonitor();
}


// RTC Time init Test =================================================================
boolean TimeTest() {
   myLcd.Clear();
   tmElements_t tm;
....

HydroGuard_0_9.ino (10.3 KB)

tu n'as peut être plus assez de place pour l'exécution du programme (RAM) ...

au lieu de Serial.println("------------------------------");

écrit plutôt Serial.println(F("------------------------------")); (de même pour tous les serial.print avec du texte)

ça te fera gagner de la place pour l’exécution du programme

+1 y'a un sacré paquet de lib ... Les 2k de RAM ils doivent être submergé ... SD.h consomme déjà pas loin de 600 octets, avec tout tes "LcdMenuInput01 " contenant plein de chaines de char y'a au moins 500 byte de bouffés, plus tout le reste ...

le Serial.print(F("------------------------")); fonctionne effectivement, ( meme si je m'enfou de ce serial :stuck_out_tongue: ). Du coup y'a t il un moyen de connaitre la ram utilisée ?

oui y'a plein de lib, l'objet c'est tellement plus pratique et ca évite la redondance de code, surtout dans les menus.

cibine:
Binary sketch size: 55,138 bytes (of a 258,048 byte maximum)
Estimated used SRAM memory: 6,208 bytes (of a 8192 byte maximum)

apparement y'a encore de la place !

Ben non, probablement pas.
Ces chiffres concernent seulement ce que peut évaluer le compilateur, c'est-à-dire les variables globales statiques hors instances d'objet.

Tout est bouffé par tes chaînes de caractères, et il te reste déjà moins de 2ko de RAM dispo après compil.
Au démarrage du programme, il y a ensuite appel aux constructeurs de tous les objets permanents, ce qui prend encore de la RAM.

Donc au moment où tu entres dans ton setup(), il doit te rester trois fois rien en RAM dispo.
Pas de quoi allouer tes variables locales ou gérer la pile d'appels de fonctions.
Et là ton arduino part en sucette

Première chose à faire : afficher le résultat de la fonction freeRAM() dès les premières lignes du setup().

Ensuite et surtout : revoir la gestion de tes textes constants (tout ce qui est entre double guillements dans ton code)

int freeRam () {
   extern int __heap_start, *__brkval; 
   int v; 
   return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

bon j'ai trouvé un truc Arduino Playground - AvailableMemory

et effectivement je passe de 7438 deuis un .ino vierge, à 500 avec mon code :frowning: c'est 8ko de ram pour la mega d'ailleurs.

le serial.println(F ne fonctionne pas dans les classes, y'a un autre moyen pour optimiser ca, parce que clairement c'est dans les .cpp que j'ai le plus de serial.

Sinon c'est quoi les grand principes pour gagner de la ram ? parce que la j'en suis à la moitié des fonctions pour mon module :frowning:

merci bricoleau, pour la gestion des textes, y'a un moyen de tout regrouper pour que ca prenne moins de place ou il va falloir que je coupe dans le gras ?

edit: je viens d'avoir une idée, je peux les stocker sur l'eeprom. bonne solution ?

Je t'invite à prendre connaissance de ça (section arrays of strings) et ça

yes merci !

cibine:
parce que la j'en suis à la moitié des fonctions pour mon module :frowning:

Déjà, comprends et mets en œuvre PROGMEM.
Cela te permettra de continuer à avancer, mais c'est après que cela peut commencer à devenir vraiment drôle.

Par défaut, le nono gère l'adressage sur 16 bits.
Concrètement, cela signifie que tout pointeur est stocké sur deux octets, avec une valeur comprise entre 0 et 65536.

Sans qu'on en ait vraiment conscience au début, il y a des pointeurs qui pointent vers la RAM (adresses des variables / instances d'objets), d'autres qui pointent vers la flash (adresses de fonctions / méthodes, voire adresses de données si on utilise PROGMEM).

Sur une Mega, la flash de 256ko va au-delà de la capacité d'adressage sur 16 bits.

S'agissant des pointeurs de fonctions, la chose est proprement gérée à la compil / édition de liens.
Dans le code exécutable, une section trampoline est créée dans la zone des 64ko.
Comme son nom l'indique, elle permet de rebondir au-delà des 64ko.
Quand tu veux appeler une fonction qui se trouve au-delà des 64ko, tu passes en intermédiaire par cette section trampoline.
Cela permet de rester sur des pointeurs à 16 bits.

S'agissant des données constantes stockées en flash uniquement via PROGMEM, c'est plus délicat à gérer.
Mais pour avoir à s'en préoccuper, il faut déjà se retrouver avec des constantes stockées en partie haute de la flash.

je vais me jeter dessus c'est exactement ce qu'il me fallait !

qd tu dis partie haute c'est au dela d'un certain ko ? les 64?

Oui

Pour l'instant ton compilé ne fait que 55ko
C'est quand il dépassera les 64ko qu'il faudra (peut-être) s'en occuper.

Je n'ai pas creusé l'affaire jusqu'à étudier le mapping de la flash, pour savoir si les variables statiques sont plutôt au début (avant le code exécutable) ou à la fin.
Je sais juste qu'elles sont rangées dans l'ordre inverse de leur déclaration.

apres avoir remplacé toutes les strings en progmem, ca a libéré 2000bytes, j'aurais pensé plus mais bon ca tourne pour l'instant, c'est le principal, on verra avec les nouvelles fonctions plus tard ! thx !