Problème code pour LCD

Je pense que vous n’avez pas saisi le sens de la remarque : un bioreacteur n’est justement pas un petit projet jetable qui tourne 5 minutes sur un coin de table… donc quite à récrire un bout du code autant se débarrasser des Strings.

effectivement le projet va tourner sans arrêt minimum 48 à72h voir plus !

Bonsoir J-M-L

Oui, j'ai parfaitement saisi le sens du message de @hbachetti....
Mais, je me pose la question, ces gens qui on écrit et publié ce programme ne l'ont donc jamais essayé sur une longue durée, ce serait étonnant!

Cordialement
jpbbricole

Si à un instant donné le constructeur de String() renvoie une String incomplète par manque de mémoire, il n'y aura aucun moyen de le savoir. S'il trouve un bloc mémoire de 15 octets alors que la demande est de 20 octets, String() renverra une chaîne de 15 octets, si c'est le plus gros bloc à disposition.

L'allocation dynamique, soit on comprend, soit on ne comprend pas.

Rien qu'à lire le code d'origine, on voit bien qu'il a été écrit par un noob.

Leur papier montre un log sur 20h, ils ne parlent pas d’analyse de robustesse.

Le challenge de l’allocation dynamique c’est que ça fonctionne jusqu’à ce que ca ne fonctionne plus. Je ne dis pas que leur code plantera, je dis que ce n’est pas simple à évaluer car ils utilisent justement abondamment l’opérateur + ou l’instanciation par String() qui sont les plus gros facteurs de morcellement (et un mega dispose de plus de ram qu’un UNO)

Cela dit la durée de vie est celle d’une fonction de mise à jour de l’écran ponctuelle et il semble qu’elle est peu imbriquée avec d’autres parties dynamiques,

Comme vous faites le boulot de réécriture, c’est celui qui fait qui a raison :wink:

mais le conseil qui est partagé serait de se passer de toutes ces strings et de faire juste des lcd.print() par petits bouts pour construire l’affichage surtout qu’il n’a pas l’air super compliqué

(Et idem pour les logs SD ou série)

Je ne pense pas avoir de leçons à recevoir de toi.

Dans ce post, tu n'a pas compris que la première remarque t'était adressée.

J'ai la détestable habitude de conserver certains posts croustillants, une sorte de bêtisier, avec les noms des auteurs.

On peut citer :
https://forum.arduino.cc/t/conseil-comment-faier-communiquer-12-arduinos-et-un-rasperry/555577/20
Où tu te permets de contester les explications de deux éminents membres de ce forum, et de qualifier leur propos de "lithanie technique".

Celui-ci est pas mal non plus :
https://forum.arduino.cc/t/sequence-sur-le-portd/557356/79

Il faut te rendre à l'évidence. Tu as des efforts à faire pour considérer que certains ici ont des connaissances énormes par rapport à d'autres.

Bonjour hbachetti

Alors, pourquoi tu l'écris et tu ne le pratiques pas.

Excellent et j'assume et le fait que tu gardes ça va tout à fait dans le sens de ton comportement dans ce forum. Tu rentres tout à fait dans cette catégorie de gens cités ici:

Tu vois, je garde aussi certains trucs.

Dan ton énumération tu as oublié la modestie.

Donc je persiste, @hbachetti Comme d'habitude, tout dans l'élégance!

Sur ce, je vais arrêter de perdre mon temps avec toi et surtout cesser de polluer cette discussion

Cordialement
jpbricole

Bien, après ce déversement de fiel, nous allons donc pouvoir revenir au sujet tranquillement.

Le problème serait simple s'il n'y avait pas ceci :

   serial_string = String(LCD_string);  //we use this part of LCD string also for serial port log.
   LCD_string +=" OD600 H Tc Lt  PH   ";
   if (od >0) LCD_string +=" ";
   LCD_string = String(LCD_string +String(od,2));
   LCD_string +=" ";
   LCD_string = String(LCD_string +String(h_HEATER, DEC));
   LCD_string +=" ";
   LCD_string = String(LCD_string +String(t0, DEC));
   LCD_string +=" ";
   LCD_string = String(LCD_string + String(lt,0)); 
   LCD_string +=" ";
   LCD_string = String(LCD_string +String(ph_instant, 2));
   LCD_string +="    ";
   if (second(t) %10 == 0) {  // log once every 10 seconds on the serial port!
     serial_string += String(" Sensors_flow:"+String(l_hour,DEC)+" Light:"+String(lt,0)+" OD600:"+String(od,2)+" Heater:"+String(h_HEATER, DEC)+" Tc:"+String(t0, DEC)+" pH:"+String(ph_instant, 2)+" OHuL:"+String(total_OH_uL,DEC));
     web_string = serial_string;
     Serial.println(String(serial_string)); 
   } 

LCD_string est copié dans serial_string puis dans web_string pour envoi HTTP.
Donc, il faut bien fabriquer une string (mais une C string), avec les moyens habituels (sprintf, strcat, etc.).

Comme cherif27 est débutant, cela risque de ne pas être de la tarte.

Le début pourrait ressembler à ceci :

void refreshDisplay()  //update LCD display
{
  char LCD_string[100];    // cette taille est à ajuster en fonction du total (à estimer)

  int length = sprintf(LCD_string, "%d.%02d.%02d %02d:%02d:%02d\n", year(t), month(t), day(t), hour(t), minute(t), second(t));
  length += sprintf(LCD_string + length, "S%d", stage);
  switch (stage) {
    case 0:
      strcat(LCD_string, ":Ready  ");
      break;
    case 1:
      strcat(LCD_string, ":Growing");
      break;
    case 2:
      strcat(LCD_string, ":IPTG ON");
      break;
    case 3:
      strcat(LCD_string, ":Harvest");
      break;
    case 4:
      strcat(LCD_string, ":Done ! ");
      break;
  }
  length += 8;
  // etc, etc, etc.
}

Vu le nombre de String dans ce programme je me demande comment il a pu tourner plus de qq minutes.

Comme dit plus haut, tout dépend de l’imbrication des allocations et libérations. Si elles sont FILO alors on ne morcelle pas le tas, de même si globalement toutes les allocations sont effectuées dans une fonction à courte durée de vie sur des variables locales sans appel à d’autres fonctions imbriquées qui utilisent le tas de manière plus longue (ce qui semble le cas ici).

Sur un UNO ce serait peut être aussi plus challenging mais la MEGA a plus de SRAM

Le challenge bien sûr c’est qu’on ne sait pas si on est juste à la limite ou s’il y a une grosse marge.

Si c'est le cas, remplacer simplement la partie envoi vers le LCD :

Wire.beginTransmission
// ...
Wire.endTransmission(false);

Par un lcd.print().
Ensuite tester sur la durée.

Bonjour

C'est déjà pas mal ?

Cordialement
jpbbricole

Le principal problème reste la connaissance en matière de programmation.
S'il s'agit d'un projet dans le monde professionnel, une formation en langage C de quelques jours est à prévoir par l'employeur.
Sinon, si c'est un projet personnel, le temps n'a pas d'importance et les tutoriels sont nombreux.

C’est mieux que rien, mais Le besoin semble sur 3 jours ou plus. Le challenge de l’allocation dynamique comme mentionné c’est qu’on ne contrôle pas ce qu’il se passe. Donc peut être que à 21h ça plante ou 75h… ou pas du tout.

Ce que l’on essaye de vous dire c’est que pour un programme un peu sérieux pour lequel il y’a des conséquences (financières, risque, ressources perdues,….) en cas de dysfonctionnement il vaut mieux dès le départ prendre l’habitude d’utiliser des techniques saines de codage pour microcontroller plutôt que d’avoir a tout reprendre ensuite si l’on se rend compte que ça plante au bout de 3 jours à 10 minutes de l’obtention du résultat.
La complexification du code est négligeable une fois qu’on a investi 30 minutes pour comprendre les cStrings et les quelques fonctions associées (et qu’on teste les débordements memoire et cas limites. La gestion des erreurs représente souvent le gros du code pour ce genre de programme)

Pour mes tests je suis parti de ceci :

L'exécution ne semble montrer aucun problème.
Par contre lorsque j'ai modifié la fonction generateRandomString() :

String generateRandomString()
{
  static int counter = 0;
  String result;

  int len = random(SMALLEST_STRING, LARGEST_STRING);
  for (int i = 0 ; i < len ; i++) {
    result += '?';
// AJOUT de la verification de la Ŝtring allouée
    if (result.length() != i+1) {
      Serial.print("count :"); Serial.println(counter);
      Serial.print("wanted :"); Serial.println(len);
      Serial.print("got :"); Serial.println(i);
      Serial.println("TOO BAD");
      while (1);
    }
  }
  counter++;
  return result;
}

Sur une UNO (échec à la 97ème String :

LARGEST_STRING 61
SMALLEST_STRING 12
834 809 3.00
666 542 18.62
612 481 21.41
588 362 38.44
count :97
wanted :52
got :50
TOO BAD

Sur une MEGA c'est un peu plus long (195 String) :

LARGEST_STRING 291
SMALLEST_STRING 58
4043 3963 1.98
3564 3055 14.28
3357 2313 31.10
3085 1777 42.40
2687 1487 44.66
2490 939 62.29
2391 667 72.10
2376 667 71.93
2363 667 71.77
count :195
wanted :286
got :245
TOO BAD

Cela montre bien que String() renvoie une chaîne plus courte que celle espérée si la mémoire manque.

sur UNO et Mega2560, l'allocation de mémoire du tas conserve toujours un __malloc_margin de 128 octets, il y a donc toujours au moins cette quantité d'espace à utiliser pour la pile ce qui permet généralement de garder en vie le programme en cours d'exécution.

Si vous avez beaucoup d'appels de méthodes imbriqués avec beaucoup de variables locales, vous pouvez faire planter le sketch, mais c'est plus rare..

Mais si on ne traite pas les cas de mauvaise allocation (les cas limites) alors on est dans le flou, la string que l’on construit n’est pas celle que l’on pense et le log sera faux par exemple dans le cas de ce programme

Tout à fait.
J'ai rectifié : la UNO s'arrête au bout de 97 String, la MEGA 195.

Comme LCD_string est construite par concaténation, si l'allocateur ne parvient pas à obtenir la mémoire nécessaire (par realloc()), la String restera en l'état.
Donc il n'y aura pas de plantage mais par exemple les infos renvoyées par le serveur WEB risquent d'être tronquées.

https://github.com/arduino/ArduinoCore-API/blob/master/api/String.cpp

Apparemment on a affaire à 3 String :
LCD_string : 4x20 caractères
serial_string : copie de LCD_string + une 40aine de caractères
web_string : copie de LCD_string (on se demande pourquoi)
Le total est de ~ 280 octets.

Dans mon test sur une MEGA j'alloue entre 58 et 291 octets, et le test s'arrête à 195 allocations.
Leur log montre 117 lignes sur 20H.

Effectivement, sur 20H, la limite n'est peut-être pas atteinte, mais sur 40H ?

Bonsoir hbachetti,
es que je remplace la partie cité tout simplement ?:

Je n'ai écrit qu'une partie.
La première ligne du LCD : date et heure
Deuxième ligne du LCD : S0:Ready (ou autre)

A toi de jouer pour le reste.

ok si j'ai bien compris je doit refaire la même chose dans le même style pour la suite