j'ai plein de difficultés avec les concaténations de Strings. J'ai vu que ce sujet est fréquent dans les post, mais je n'ai pas vu de solutions stables.
Mon pb est de type aléatoire. Sur le même code, ça marche ..ou pas suivant les moments . CAD :
je veux faire une ligne qui sera enregistrée sur une SD, ligne qui est la concaténation de données, que j'exploite par ailleurs.
j'ai essayé : le "+", ".concat.".. dans les deux cas j'obtiens soit :
une chaine vide
soit la dernière valeur écrasée tout
soit certaines valeurs ne sont pas "concaténées"..
soit .. ce que je veux
..
comme je suppose que c'est un pb de gestion de mémoire dynamique, je voulais essayer une chaîne initiale de longueur fixe, dans laquelle je remplaçais les diverses zones mais je n'ai pas trouvé la fonction duale de .substring qui remplace un morceau de chaine (sans modifier la longueur)
Je suis sec .. Si quelqu'un peut m'aider ?? Merci d'avance.
PS: Le contexte : avant de mettre de nouveaux panneaux solaires sur mon toit, je veux faire des mesures d'ensoleillement car pas mal d'arbres autour du toit..=> Donc 10 capteurs enregistrés périodiquement sur une SD toutes les 15 mn du mati au soir ..
J'ai donc besoin de construire la ligne de données (mesurées = quelques moyennes à la volée)
Effectivement, ce serait bien de publier le code en question, mais si on réfléchit bien, pourquoi s’embêter à concaténer des String alors qu'il suffit de faire des print successifs?
Et sans avoir aucune idée de la plateforme utilisée, comment savoir si l'utilisation d'objets String sera possible (ESP8266 ou ESP32) ou potentiellement problématique (ARDUINO).
plateforme ARDUINO UNO (liée à un shield SD + RTC)
pour l'analyse ultérieure, je voulais regrouper sur l'enregistrement : 1. les données mesurées des 8 capteurs, PUIS 2. le % par rapport au capteur de référence , PUIS les valeurs corrigées des capteurs.
Je vous joint la partie de code.. (j'espère bien le faire sinon ..toutes mes excuses et merci d âme corriger....
Partie du code correspondant au pb ...
// 3 morceaux de l'enregistrement
String Pourcentage,Ligne,LigneC;
//
// message ecran - RFU
//
int trace_on = 0;
int halte_on = 0;
double t_clavier; //période entre 2 consultations du clavier
#include "dialogue_solaire.h" // gestion des BP et ecran LCD mini
// =================
// Procédure d'initialisation
// ------------------------------------------------------------------
void setup () {
// Initialisation de la liaison série (pour retourner les infos au moniteur série de l'ordi)
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Adressierung beachten, hier 0x3C!
// Démarage de l'écran LCD et ouverture ports BP
//-------------------------
init_ecran();
// initialisation des ports d'adress en écriture
pinMode (adr0, OUTPUT);
pinMode (adr1, OUTPUT);
pinMode (adr2, OUTPUT);
pinMode (Led_surveillance, OUTPUT);
pinMode (Led_alarme, OUTPUT);
digitalWrite (Led_surveillance, LOW);
digitalWrite (Led_alarme, LOW);
// Initialisation de la liaison série (pour retourner les infos au moniteur série de l'ordi)
Serial.begin(9600);
Serial.println();
Serial.println("Calibrage lancé");
Serial.println();
} // fin setup
// =================
// Boucle principale
// ------------------------------------------------------------------
void loop () {
//
// init tableaux du jour
//
for (int i =0 ; i< nb_capteur;i++)
{
lux_min[i] =500;
lux_max[i] =0;
lux_moy[i] =0;
lux_instant[i]=0;
}
int nb_mesure=0; // Nb de mesue du jour
//
// demarage des boucles du jour (Limité à 5 mesures ppur mise au point
// ----------------------------
for (nb_mesure=0;nb_mesure<5;nb_mesure++) {
Ligne = "M = ";
Pourcentage = " // % = ";
LigneC= " // corrigé :";
// debut d'une serie de mesures
//-----------------------------
int Moyenne = 0;
// Nb de mesure du jour
for (int no_capteur = 0; no_capteur <= nb_capteur-1 ; no_capteur ++) {
// pour chaque capteur : selection, lecture, traitement
digitalWrite (adr0, no_capteur & 1);
digitalWrite (adr1, (no_capteur >> 1) & 1);
digitalWrite (adr2, (no_capteur >> 2) & 1);
delay (100); // délai de positionnement du MUX
word soleil = analogRead (adr_lue);
lux_instant[no_capteur] = soleil;
Moyenne = Moyenne + soleil ;
if (no_capteur == 0) soleil0=soleil; // memorisation de la réference"PLEIN SOLEIL" : Capteur 0
// Valeur mesurée
// Ligne = Ligne + chiffres3(soleil) + "-";
Ligne.concat(chiffres3(soleil));
Ligne.concat("-");
// % age de correction à apporter
float r = soleil; r=soleil0/r ; r=r*100; byte i=r; // (pas élégant) : % / reférence
// Pourcentage = Pourcentage + chiffres3 (i) + "-";
Pourcentage.concat(chiffres3 (i));
Pourcentage.concat("-");
// valeur corrigée
double cor = soleil * lux_cor[no_capteur];
int j=cor/100;
LigneC.concat(chiffres3 (j));
LigneC.concat("-");
// MIN et MAX
if (soleil < lux_min[no_capteur]) lux_min[no_capteur] = soleil;
if (soleil > lux_max[no_capteur]) lux_max[no_capteur] = soleil;
lux_moy[no_capteur] = ((lux_moy[no_capteur] * nb_mesure ) + soleil) / (nb_mesure+1);
// solution transitoire... .. par capteur
Serial.print ("Val:" ); Serial.print (soleil);
Serial.print (" %:" ); Serial.print (chiffres3 (i) );
Serial.print (" Corrigée :" ); Serial.println (chiffres3(j)) ;
}
// solution qui ne marche pas - Globale sur tous les capteurs
// Serial.print (Ligne);Serial.print (Pourcentage);Serial.println (LigneC);
Pourcentage = " // % = ";
Moyenne = Moyenne / nb_capteur;
display.setCursor(55,40); display.println(Moyenne);
display.display();
attente (periode,2); // attente de la prochiane lecture sulaire + clignotement "court"
}
for (int i =0 ; i<nb_capteur;i++)
{
Serial.print ("Min =") ; Serial.print (chiffres3(lux_min[i]));
Serial.print ("/ Max ="); Serial.print (chiffres3(lux_max[i]));
int moy = lux_moy[i]; // conversion en int (pas trés beau ..ok)
Serial.print ("/ Moy =");Serial.println (chiffres3(moy));
}
Serial.println ("Écriture du jour terminée.");
} // fin de loop
Etant donné le nombre de concaténation en jeu les problèmes d'allocation mémoire peuvent apparaître. Autant ne pas utiliser String.
A noter également : moins le programme dispose de mémoire RAM, plus les problèmes sont probables, étant donné que le tas (heap) sera réduit d'autant, et que la pile et le tas peuvent se télescoper.
Etant donné les problèmes rencontrés (chaîne vide, non concaténation, etc.) le résultat est éloquent.
Une petite remarque, pour économiser la mémoire la plus "précieuse" ce qu'ii semble que tu manques dans certaines phases de ton programme, toutes les chaînes de caractères que tu utilises comme
le F("ABCD") déplace le stockage de la String en mémoire flash, économisant ainsi le mémoire de travail de l'Arduino.
L'effet est l'équivalent de:
const String texte = "ABCD";
Tu peux faire ça avec toutes les String entre guillemets ou presque, si pas, le compilateur de le dira bien assez vite.
Déjà, rien qu'avec ça, ton programme devrait mieux tourner
Logiquement oui, du moins je le pense, il faut bien que le compilateur stocke la valeur quelque part, ça serait bête d'encombrer la SRAM pour quelque chose qui ne change pas?
Je pense malheureusement que ça occupe une fois la flash (pour le binaire) et deux fois la SRAM, une pour l’affectation et une dans le tas lors de l’instanciation de la String. (Donc pas visible à la compilation).
le sujet que j'ai soumis autour du PB de l'allocation dynamique de mémoires, mais surtout vos interventions, m'amène à me poser la question de la 'map' de la mémoire dans l'ARDUINO.
Pour mon travail, j'avais développé pas mal d'application en assembleur sur des machines 'anciennes' que peu connaissent aujourd'hui (MITRA15, 125, SOLAR) ou des processeurs (80186, 80386, Z80,.) et j'avais un outil super utile qui était les sorties du linker ... (La map, les adresse de variable ..etc, ..etc)
Comment peut on obtenir qq chose d'analogue en fin de compilation ou par le loader?
J'avais déjà vu cette expression dans le langage arduino mais jusqu'ici je ne m'étais jamais vraiment posé la question de son utilité. Maintenant je comprends.
J'ai trouvé également des informations sur le mot-clé modificateur de variable :
Le fichier map apporte peu de choses lorsque l'on recherche des problèmes.
En fin de compilation les tailles sont affichées : FLASH, RAM
Le croquis utilise 28260 octets (91%) de l'espace de stockage de programmes. Le maximum est de 30720 octets.
Les variables globales utilisent 1582 octets (77%) de mémoire dynamique, ce qui laisse 466 octets pour les variables locales. Le maximum est de 2048 octets.
La mémoire disponible faible, des problèmes de stabilité pourraient survenir.
Dans cet exemple où il reste 466 octets de RAM disponible, il faut savoir que ces 466 octets sont occupés par la pile et le tas (heap).
Plus le tas sera sollicité (allocation dynamique), moins il restera de place pour la pile, ce qui peut être problématique.
Dans cet exemple, le message "des problèmes de stabilité pourraient survenir" est affiché, mais le logiciel fonctionne parfaitement bien. L'allocation dynamique n'étant pas utilisée, la pile dispose de 466 octets pour le stockage des variables locales, ce qui est suffisant dans ce cas.
Un peu plus d'info :
j'ai regardé rapidement. si vous compilez cela sur un UNO
const String texte = "CECI EST UN LONG TEXTE POUR VRAIMENT VOIR L'IMPACT EN SRAM DE CE QUE L'ON FAIT. BIEN SÛR NORMALEMENT ON FAIT ATTENTION A LA MEMOIRE :) !!!!";
void setup() {Serial.begin(115200); Serial.println(texte);}
void loop() {}
➜ vous avez
Le croquis utilise 2964 octets (9%) de l'espace de stockage de programmes. Le maximum est de 32256 octets.
Les variables globales utilisent 344 octets (16%) de mémoire dynamique, ce qui laisse 1704 octets pour les variables locales. Le maximum est de 2048 octets.
Si vous prenez le même code mais étendez un peu la chaîne de caractère (je rajoute 10 caractères)
const String texte = "0123456789CECI EST UN LONG TEXTE POUR VRAIMENT VOIR L'IMPACT EN SRAM DE CE QUE L'ON FAIT. BIEN SÛR NORMALEMENT ON FAIT ATTENTION A LA MEMOIRE :) !!!!";
void setup() {Serial.begin(115200); Serial.println(texte);}
void loop() {}
➜
Le croquis utilise 2974 octets (9%) de l'espace de stockage de programmes. Le maximum est de 32256 octets.
Les variables globales utilisent 354 octets (17%) de mémoire dynamique, ce qui laisse 1694 octets pour les variables locales. Le maximum est de 2048 octets.
On voit que la flash a augmenté de 10 octets, normal puisqu'il faut stocker le contenu de la variable dans le code, mais on voit que la SRAM a aussi augmenté de 10 octets et ça correspond au fait qu'il faut mettre le contenu de la variable en RAM avant l'affectation à la String.
cet exemple ne mesure pas la RAM allouée dynamiquement mais vu comme la classe String est construite, la surcharge de l'opérateur = fait une allocation sur le tas, donc on a aussi une demande équivalente à la longueur de la chaîne en SRAM en plus.
si vous compilez ça , le même texte mis en mémoire flash
void setup() {Serial.begin(115200); Serial.println(F("CECI EST UN LONG TEXTE POUR VRAIMENT VOIR L'IMPACT EN SRAM DE CE QUE L'ON FAIT. BIEN SÛR NORMALEMENT ON FAIT ATTENTION A LA MEMOIRE :) !!!!"));}
void loop() {}
➜ vous avez
Le croquis utilise 1626 octets (5%) de l'espace de stockage de programmes. Le maximum est de 32256 octets.
Les variables globales utilisent 188 octets (9%) de mémoire dynamique, ce qui laisse 1860 octets pour les variables locales. Le maximum est de 2048 octets.
et si vous rajoutez les 10 caractères
void setup() {Serial.begin(115200); Serial.println(F("0123456789CECI EST UN LONG TEXTE POUR VRAIMENT VOIR L'IMPACT EN SRAM DE CE QUE L'ON FAIT. BIEN SÛR NORMALEMENT ON FAIT ATTENTION A LA MEMOIRE :) !!!!"));}
void loop() {}
➜ vous avez
Le croquis utilise 1636 octets (5%) de l'espace de stockage de programmes. Le maximum est de 32256 octets.
Les variables globales utilisent 188 octets (9%) de mémoire dynamique, ce qui laisse 1860 octets pour les variables locales. Le maximum est de 2048 octets.
On voit que la Flash a augmenté de 10 octets, normal puisque l'on a allongé le texte et on voit que la mémoire SRAM n'a pas été impactée et reste à 188 octets.
ces 188 octets correspondent à l'empreinte mémoire minimal quand on utilise Serial et qu'on fait un seul println