Je rencontre un soucis avec mon code. Je dois changer un string enregistré dans l'EEPROM en char* pour faire une publication MQTT.
En gros, je stock le topic MQTT dans l'EEPROM en fonction de 2 éléments : lieu et nom du capteur.
ensuite je lui ajoute le nom de la variable et cela donnee:
temperature_topic = cheminMQTT + nomcpt + "/temperature";
dans mon cas : temperature_topic = maison/bureau/capt/temperature
puis je fais un temperature_topic.c_str() mais la je n'obtiens que : maison/bureau
peu importe les variables utilisées, j'obtiens toujours que la première partie a savoir cheminMQTT
Voici le code très simplifié pour illustrer ce que je dis (pas besoin de charger les capteurs et librairies anexes)
#include <EEPROM.h>
float temp = 0.0;
//Config MQTT
String nomcpt,cheminMQTT;
//Chemin MQTT
String temperature_topic;
//MQTT
long lastMsg = 0;
void readEeprom(){
Serial.println("Lecture de l'EEPROM");
for (int i = 0; i < 32; ++i) { nomcpt += char(EEPROM.read(i)); }
Serial.print("nom capteur: ");
Serial.println(nomcpt);
for (int i = 128; i < 256; ++i){ cheminMQTT += char(EEPROM.read(i)); }
Serial.print("cheminMQTT: ");
Serial.println(cheminMQTT);
temperature_topic = cheminMQTT + nomcpt + "/temperature";
Serial.println(temperature_topic);
}
void setup() {
Serial.begin(115200);
EEPROM.begin(512);
readEeprom();
}
void loop(){
long now = millis();
if (now - lastMsg > 1000 * 5) {
lastMsg = now;
temp += 1.1;
Serial.println(temperature_topic);
Serial.print(temperature_topic.c_str());
Serial.print(" : ");
Serial.println(String(temp).c_str());
delay(25);
}
}
et la réponse du moniteur série :
maison/bureau/capt/temperature
maison/bureau : 1.10
for (int i = 0; i < 32; ++i) { nomcpt += char(EEPROM.read(i)); }
for (int i = 128; i < 256; ++i){ cheminMQTT += char(EEPROM.read(i)); }
Et elles placent des informations invalides dans les String.
Il faudrait sortir de la boucle lorsque le terminateur de chaine est lu. Pour autant que tu ais écrit une c_string dans l'EEPROM.
Ce qui donnerait quelque chose comme ça
for (int i = 0; i < 32 && EEPROM.read(i)!=0 ; ++i) { nomcpt += char(EEPROM.read(i)); }
for (int i = 128; i < 256 && EEPROM.read(i)!=0; ++i){ cheminMQTT += char(EEPROM.read(i)); }
J'ai simulé la lecture en EEPROM par des tableaux de char. Si je ne mets pas la sortie des 2 boucles sur la lecture du 0 j'ai un comportement semblable à ce que tu observes.
//#include <EEPROM.h>
float temp = 0.0;
char tabnomcpt[32] = {'c','p','t','\0'};
char tabnomqtt[128] = {'m','a','i','s','o','n','/','b','u','r','e','a','u','/','\0'};
//Config MQTT
String nomcpt,cheminMQTT;
//Chemin MQTT
String temperature_topic;
//MQTT
long lastMsg = 0;
void readEeprom(){
Serial.println("Lecture de l'EEPROM");
// for (int i = 0; i < 32; ++i) { nomcpt += char(EEPROM.read(i)); }
for (int i = 0; i < 32 && tabnomcpt[i]!=0; ++i) { nomcpt += tabnomcpt[i]; }
Serial.print("nom capteur: ");
Serial.println(nomcpt);
// for (int i = 128; i < 256; ++i){ cheminMQTT += char(EEPROM.read(i)); }
for (int i = 0; i < 128 && tabnomqtt[i]!=0; ++i){ cheminMQTT += tabnomqtt[i]; }
Serial.print("cheminMQTT: ");
Serial.println(cheminMQTT);
temperature_topic = cheminMQTT + nomcpt + "/temperature";
Serial.println(temperature_topic);
}
void setup() {
Serial.begin(115200);
// EEPROM.begin(512);
readEeprom();
}
void loop(){
long now = millis();
if (now - lastMsg > 1000 * 5) {
lastMsg = now;
temp += 1.1;
Serial.println(temperature_topic);
Serial.print(temperature_topic.c_str());
Serial.print(" : ");
Serial.println(String(temp).c_str());
delay(25);
}
}
et j'obtiens ça
Lecture de l'EEPROM
nom capteur: cpt
cheminMQTT: maison/bureau/
maison/bureau/cpt/temperature
maison/bureau/cpt/temperature
maison/bureau/cpt/temperature : 1.10
maison/bureau/cpt/temperature
maison/bureau/cpt/temperature : 2.20
Je pense qu'il y a vraiment des c_string dans l'EEPROM ce qui expliquerait que print affiche correctement la chaine. Mais String doit se planter si on lui met n'importe quoi au-delà du terminateur de chaine.
A noter, on dit assez souvent sur le forum que les String sont à éviter car elles morcellent la mémoire et sont la cause de pas mal de problèmes.
On peut généralement s'en passer. Dans ton cas tu les utilises pour concaténer des chaines, tu pourrais le faire autrement:
soit en utilisant des c_string, c'est un peu plus contraignant mais c'est plus sur en utilisant strcat par exemple
soit en écrivant les morceaux les uns après les autres
Au lieu de faire
String toto, titi, tutu, tata;
.....
toto = titi + tutu + tata;
Serial.print(toto);
Effectivement, la lecture des 128 octet de l'EEPROM avec une grosse majorité de 0 posait problème.
à la lecture de l'EEPROM j'ai fait un
for (int i = 128; i < 256; ++i)
{
if (EEPROM.read(i) != 0){
cheminMQTT += char(EEPROM.read(i));
}}
par contre il ne faut pas que j'utilise de 0 dans le champ la, mais ça ne devrait pas me poser de soucis. Est ce qu'il y a un character de fin de ligne? ou alors je le saisie mal dans l'écriture de l'EEPROM
for ( i = 0; i < fcheminMQTT.length(); ++i)
{
EEPROM.write(128+i, fcheminMQTT[i]);
Serial.print("Wrote: ");
Serial.println(fcheminMQTT[i]);
j=i+128;
}
EEPROM.write(j+1, '\0');
EEPROM.commit();
En tout cas un grand merci à toi, c'est le dernier point qui me bloquait, le reste ne sera que de l'amélioration du code et du bonhomme
fcheminMQTTcontient du texte donc il ne devrait pas y avoir 0, sauf comme terminateur. Le caractère 0 n'est pas codé par la valeur 0 dans une chaine de caractères.
Ta fonction qui relit l'EEPROM ne fonctionnera plus si la chaine en EEPROM change de longueur.
Exemple:
J'utilise \0 pour indiquer la fin de la chaine
tu écris en EEPROM
ceciestletreslongchemin\0
Si plus tard tu remplaces la chaine en question par celle-ci
ceciestlachaine\0
tu auras en EEPROM
ceciestlachaine\0ngchemin\0
et tu liras
ceciestlachainengchemin
puisque tu ignore les 0
Il faut sortir de la boucle de lecture lorsque tu rencontres le premier 0
for (int i = 128; i < 256 && EEPROM.read(i) != 0; ++i)
{
cheminMQTT += char(EEPROM.read(i));
}