Pourquoi la déclaration d'un String me fait planter mon programe

Bonjour à tous

J'utilise depuis ce soir une nouvelle librairie qui va envoyer des données à Ubidots.com

Au moment de l'envoi mon programme plante. Et je constate que c'est juste au moment de la déclaration d'une variable de type String

A l'appel de ce code, il m'affiche que 0

bool Ubidots::sendAll() {

    int i;
Serial.println(F("0"));
    String all;
    String str;
Serial.println(USER_AGENT);
    all = USER_AGENT;
    all += "/";
Serial.println(VERSION);
    all += VERSION;
    all += "|POST|";

#ifdef DEBUG_UBIDOTS
       Serial.println(_token);
#endif
    all += _token;
    all += "|";
    all += _dsTag;

[.. suite du code ..]

}

J'ai regardé le reste de mon code, et je constate que je n'utilise jamais une variable de type String. En d'autres mots, la variable 'all' est le premier String de mon code.

Est-ce qu'il y a un truc de particulier quand on déclare un String?
Avez-vous une idée de ce qu'il fait planter mon code??

Milles mercis pour vos lumières!

le type String, si je ne me trompe pas, fait de l'allocation dynamique par blocs des chaines stockées. Si t'es déjà un peu limite en termes d'utilisation mémoire de ta carte, le problème vient peut-être de là (?)

Hello, merci pour ta réponse.

Heu ben j'ai 256k

Sketch uses 102,000 bytes (38%) of program storage space. Maximum is 262,144 bytes.

Y-a-t-il d'autres moyens pour vérifier ceci?

ah en effet, à moins de la moitié de 256k, je ne pense pas que le problème vienne de l'hypothèse que j'ai émis.
Après, par expérience et sans avoir vu le reste de ton code, parfois des problèmes de "jardinage" (débordements/écrasements mémoire) peuvent donner naissance à des comportements aléatoires du genre "si j'ajoute une ligne de code à l'autre bout de mon code ca ne plante plus, mais si je l'enlève ca marche"... Ce sont, à mon sens, les pires bugs à trouver...

pierrot10:
Heu ben j'ai 256k
...

Bonjour,

Ce que tu nous donnes c'est la mémoire programme.
Ce qu'il faut vérifier dans ce cas c'est la ram (mémoire dynamique).

Arg ! j'ai pas vu que c'était l'espace programme, en effet, il faut que tu regardes au niveau de l'occupation de la "mémoire dynamique", mais si tu fais beaucoup d'allocations dynamiques (new) son utilisation ne sera pas facile à voir au moment de la compilation...

:slight_smile: :slight_smile: http://snippets-r-us.com :slight_smile: :slight_smile:

on aurait besoin de voir le code (et effectivement comprendre ce que dit le compilateur pour la partie mémoire RAM, pas de programme)

c'est quoi cette indentation bizarre de votre code? appuyez sur ctrl-T dans l'IDE pour bien aligner tout...

vraiment pas la peine d'utiliser cette classe String, elle crée plus de pb qu'elle n'en résout...

au lieu de faire

    all = USER_AGENT;
    all += "/";
    all += VERSION;
    all += "|POST|";
    all += _token;
    all += "|";
    all += _dsTag;

et j'imagine ensuite imprimer all d'un coup pourquoi ne pas balancer simplement chacun des bouts séparément sur le port série qui va bien

unPortSerie.print(USER_AGENT);
unPortSerie.print(F("/"));
unPortSerie.print(VERSION);
unPortSerie.print(F("|POST|"));
unPortSerie.print(token);
unPortSerie.print(F("|"));
unPortSerie.print(_dsTag);

vous gagnerez plein de mémoire parce que pas besoin d'importer le code de la classe String et surtout vous ne morcellerez plus votre mémoire avec des ajouts de petits bouts de Strings... pour bien comprendre ce qu'il se passe avec la classe String quand vous faites ce genre d'opérations allez lire The-evils-of-arduino-strings - c'est en anglais mais ça vous donnera une bonne explication de pourquoi ce n'est pas une bonne idée d'utiliser cette classe sur nos petits micro-contrôleurs

@J-M-L : visiblement les Serial.println() sont des simples traces de debug, la chaîne "all" doit certainement être utilisée plus loin dans la même fonction.

@pepe : super ! je sens que je vais utiliser souvent "SP - __brkval". Mais pour être sur d'avoir compris, je supposde que SP est le registre de pile (Stack Pointer), mais __brkval correspond à quoi ?

Zorro_X:
@J-M-L : visiblement les Serial.println() sont des simples traces de debug, la chaîne "all" doit certainement être utilisée plus loin dans la même fonction.

oui si vous regardez bien je ne les ai pas mis et je n'ai pas envoyé ma chaîne sur le même Serial / SowftareSerial... ça sent la requête POST pour un service web...

faire du code pour bâtir petit bouts par petits bouts la chaîne "Bonjour il fait beau" et ensuite l'envoyer sur le port série c'est la même chose que d'envoyer les petits bouts les uns après les autres sur ce même port série. Donc pourquoi s'ennuyer à mettre tout ça en mémoire...

Bonjour,
En complément aux informations de pepe
ATmega memory use

@pepe et @icare : merci beaucoup, très utiles et intéressantes vos infos ! Ca va bien me servir pour déboguer mes applis qui "rentrent de justesse" ! :slight_smile:

Bonjour,
Merci pour ces échanges très intéressant.

Voici la soucre du code que j'ai partiellement collé
https://github.com/ubidots/Ubidots-FONA/blob/master/UbidotsFONA.cpp#L193

Et voic la déclaratin de USER_AGENT et de VERSION comme demandé
https://github.com/ubidots/Ubidots-FONA/blob/master/UbidotsFONA.h#L41

Je penasi suivre l'idée de J-M-L
http://forum.arduino.cc/index.php?topic=441080.msg3037166#msg3037166

et de faire ma fonction.

Je suis conctré sur deux fonction.
Celle-ci
https://github.com/ubidots/Ubidots-FONA/blob/master/UbidotsFONA.cpp#L91
pour collecté les informations (variable+value) à envoyé à Ubidots

et donc celle-ci pour les envoyé
https://github.com/ubidots/Ubidots-FONA/blob/master/UbidotsFONA.cpp#L193

J'ai donc créé deux nouvelles fonctions dans ma libraire (je ne l'ai pas encore testé

// CONSTRUCTEUR
Adafruit_FONA::Adafruit_FONA(int8_t rst)
{
  _rstpin = rst;

  apn = F("FONAnet");
  apnusername = 0;
  apnpassword = 0;
  mySerial = 0;
  httpsredirect = false;
  useragent = F("FONA");
  ok_reply = F("OK");

  // Ubidots
  currentValue = 0;
  val = (Value *)malloc(MAX_VALUES*sizeof(Value));
    _dsName = NULL;
    _dsTag = "FONA";

}

void Adafruit_FONA::ubidotsAdd(char *variable_id, float value, char *ctext1) {
    #ifdef DEBUG_UBIDOTS
            Serial.print(F("Ubidots: "));
            Serial.print(variable_id);
            Serial.print(F(", ")); Serial.print(value);
            Serial.print(F(", ")); Serial.println(ctext1);
    #endif
    (val+currentValue)->varName = variable_id;
    (val+currentValue)->ctext = ctext1;
    (val+currentValue)->varValue = value;
    currentValue++;
    if (currentValue > MAX_VALUES) {
        currentValue = MAX_VALUES;
    }

}

boolean Adafruit_FONA::ubidotsSendAll(char* token, char* server){
  int i;
  char alls[255];
  char tmp[255];
 //  String all;
 //   String str;


  /*  
    all = USER_AGENT;
    all += "/";
    all += VERSION;
    all += "|POST|";
    all += token;
    all += "|";
    all += _dsTag;
*/
    if (_dsName != NULL) {
       // all += ":";
       // all += _dsName;
        sprintf(alls,"%s%s%s%s%s%s%s=>",USER_AGENT,"/",VERSION,"|POST|",token,"|",_dsTag,_dsNam3);
    }
//    all += "=>";
    sprintf(alls,"%s%s%s%s%s%s%s=>",USER_AGENT,"/",VERSION,"|POST|",token,"|",_dsTag,_dsNam3);

// Dessous je dois encore trouver comment faire pour encapsuler ceci dans le tmp ou alls

    for (i = 0; i < currentValue; ) {
        str = String(((val + i)->varValue), 2); // Ici je dois trouver comment convertir de uint8_t en char, mais printf pourrais m'aider
        all += String((val + i)->varName); // Ici, je mets le résultat dans tmp
        all += ":";
        all += str;
        if ((val + i)->ctext != NULL) {
            all += "$";
            all += String((val + i)->ctext);
        }
        i++;
        if (i >= currentValue) {
            break;
        } else {
            all += ",";
        }
    }
    all += "|end";
    Serial.println(all.c_str());


// ENCUITE J'ENVOI. Je n'ai pas encore développé ce code, mais je ne pense pas que j'aurai de problème
  
}

Ma difficulté est ici, je mets mes commentaire en majuscule

boolean Adafruit_FONA::ubidotsSendAll(char* token, char* server){
  int i;
  
// JE CREE UNE VARIALE alls QUI REMPLACERA all. POUR LE MOMENT J AI MIS LE MAXIMU 255
  char alls[255];
// JE CREE UNE VARIABLE tmp QUI REMPLACE str. POUR LE MOEMENT J'AI MIS LE MAX 255
  char tmp[255];
 //  String all;
 //   String str;


  /*  PLUS BESOIN DE CECI CAR TOUT VAS ETRE MIS DANS ALLS GRACE A SPRINTF
    all = USER_AGENT;
    all += "/";
    all += VERSION;
    all += "|POST|";
    all += token;
    all += "|";
    all += _dsTag;
*/
    if (_dsName != NULL) {
     //   all += ":";
      //  all += _dsName;
// JE CONSTRUIS MA CHAINE AVEC LE dsName ET J AJOUTE dsName et =>
        sprintf(alls,"%s%s%s%s%s%s%s=>",USER_AGENT,"/",VERSION,"|POST|",token,"|",_dsTag,_dsName);
    }
//    all += "=>";
// JE CONSTRUIT MA CHAINE SANS dsName ET J AJOUTE =>
    sprintf(alls,"%s%s%s%s%s%s=>",USER_AGENT,"/",VERSION,"|POST|",token,"|",_dsTag);




/* DESSOUS, JE DOIS ENCORE REFLAICHIR COMMENT BOUCLé currentValue et val.
* A SAVOIR QUE J AI CECI dans fichier.h (Structure). VOIR DANS LE CONSTRUCTEUR, DESSUS
*
*  typedef struct Value {
*  char  *varName;
*  char  *ctext;
*  float varValue;
*  } Value; 
//


// ICI COMMENT MODIFIER CETTE BOUCLE POUR QUE LES RESULTATS SOIENT ENCAPSULER DANS tmp ET NON DANS str
// et alls ET NON DANS all
    for (i = 0; i < currentValue; ) {
        str = String(((val + i)->varValue), 2); // Ici je dois trouver comment convertir de uint8_t en char, mais printf pourrais m'aider
        all += String((val + i)->varName); // Ici, je mets le résultat dans tmp
        all += ":";
        all += str;
        if ((val + i)->ctext != NULL) {
            all += "$";
            all += String((val + i)->ctext);
        }
        i++;
        if (i >= currentValue) {
            break;
        } else {
            all += ",";
        }
    }
    all += "|end";
    Serial.println(all.c_str());


// ENCUITE J'ENVOI. Je n'ai pas encore développé ce code, mais je ne pense pas que j'aurai de problème
  
}

Ma diffculté est de faire l'équivalence de sendAll() mais avec des char à la place des String.

Pourriez-vous me guider?

Milles mercis pour votre contribution!! :slight_smile:

simplifiez: à quoi voulez vous que ressemble la string finale envoyée? et où sont stockées les informations qui servent à la constituer?

Bonjour et encore merci.

Voilà, j'ai mon code mais ily a des bug, probablement en terme de formatage.

D'abord voici comment j'ai modifié ma la librairie Adfruit_Fona.
(NB: Mon code compile bien :slight_smile: )

Dans mon fichier Adafruit_fona.h, j'ai ajouté ceci:

// Ubidots
#define SERVER "translate.ubidots.com"
#define PORT "9010"
#define USER_AGENT "FONA"
#define VERSION "2.0"
#define MAX_VALUES 5
typedef struct Value {
    char  *varName;
    char  *ctext;
    float varValue;
} Value;

et encore ceci:

 private:
    // Ubidots
    char* _dsName;
    char* _dsTag;
    uint8_t currentValue;
    Value * val;

et finalement encore ceci

void ubidotsAdd(char *variable_id, float value, char *ctext1 = NULL);
    boolean ubidotsSendAll(char* token, char* server = SERVER);

Dans mon fichier Adafruit_Fona.ccp, j'ai ajouté ceci dans le constructuer

Adafruit_FONA::Adafruit_FONA(int8_t rst)
{
  _rstpin = rst;

  apn = F("FONAnet");
  apnusername = 0;
  apnpassword = 0;
  mySerial = 0;
  httpsredirect = false;
  useragent = F("FONA");
  ok_reply = F("OK");

  /*
  * Ubidots
  */
  _dsName = NULL;
  _dsTag = "FONA";
  currentValue = 0;
  val = (Value *)malloc(MAX_VALUES*sizeof(Value));

}

puis en bas de ce fichier j'ai finallement ajouté ceci:

/*
* UBIDOT
*/
void Adafruit_FONA::ubidotsAdd(char *variable_id, float value, char *ctext1) {
  
    Serial.print(F("Ubidots: "));
    Serial.print(variable_id);
    Serial.print(F(", ")); Serial.print(value);
    Serial.print(F(", ")); Serial.println(ctext1);
    
    (val+currentValue)->varName = variable_id;
    (val+currentValue)->ctext = ctext1;
    (val+currentValue)->varValue = value;
    currentValue++;
    if (currentValue > MAX_VALUES) {
        currentValue = MAX_VALUES;
    }

}

boolean Adafruit_FONA::ubidotsSendAll(char* token, char* server){
  int i;
  char alls[255];
  char tmp[255];
 //  String all;
  //  String str;


  /*  
    all = USER_AGENT;
    all += "/";
    all += VERSION;
    all += "|POST|";
    all += token;
    all += "|";
    all += _dsTag;
*/
    if (_dsName != NULL) {
        //all += ":";
        //all += _dsName;
        sprintf(alls,"%s%s%s%s%s%s%s=>",USER_AGENT,"/",VERSION,"|POST|",token,"|",_dsTag,_dsName);
    }
//    all += "=>";
    sprintf(alls,"%s%s%s%s%s%s%s=>",USER_AGENT,"/",VERSION,"|POST|",token,"|",_dsTag,_dsName);


Serial.print(F("ALLS: ")); Serial.println(alls);

Serial.print(F("VAR: "));

// Dessous je dois encore trouver comment faire pour encapsuler ceci dans le tmp ou alls

    for (i = 0; i < currentValue; ) {
        // str = String(((val + i)->varValue), 2); // Ici je dois trouver comment convertir de uint8_t en char, mais printf pourrais m'aider
        //Serial.print(((val + i)->varValue), 2);
        
        //all += String((val + i)->varName); // Ici, je mets le résultat dans tmp
        Serial.print((val + i)->varName);
        
        Serial.print(":");
        //all += ":";
        
        //all += str;
          Serial.print(((val + i)->varValue), 2);

        if ((val + i)->ctext != NULL) {
            //all += "$";
            Serial.print("$");

            //all += String((val + i)->ctext);
            Serial.print((val + i)->ctext);
        }
        i++;
        if (i >= currentValue) {
            break;
        } else {
            //all += ",";
            Serial.print(",");
        }
    }
  //  all += "|end";
    Serial.print("|end");

//    Serial.println(all.c_str());

}

Dans mon sketch, j'ai ces ligne qui font appel a ubidotsAdd() pour mettre dans constructeur les variable et valeurs à envoyer (je ne met pas les valeurs des variable, ca fonctionne bien)

fona.ubidotsAdd("imei", 123, imei);
fona.ubidotsAdd("valid", 1);
fona.ubidotsAdd("latitude", latitude);
fona.ubidotsAdd("longitude", longitude);
fona.ubidotsAdd("battery", battery);
fona.ubidotsAdd("localisation",0, "GSM");
fona.ubidotsAdd("tower", 0, celltower0);
fona.ubidotsAdd("timestamp", 0, timestamp);

Serial.println(F("Sending to Ubidots"));
fona.ubidotsSendAll(TOKEN);

Ca semble relativement fonctionner. Du moins, ubidotsAdd() semble bien sauver les valeurs, sauf pour les trois dernières. A encore vérifier.
A noter que le deuxième paramettes de ubidotsAdd() doit etre un float. Etant donné que celltower0 et timestamp ne soit pas des float (contrairement aux autre) mais des char, je les passe dans le troisème parametre. Mais il semble qu'elles n'ont pas été sauvées. Je verrai ca plus tard, pourquoi?

Voici ce que m'affiche le terminal et je viendrai sur la problématique que je vois actuellement.

Sending to Ubidots
ALLS: FONA/2.0|POST|tTPyJRD21PsM0q|FONA=>
VAR: imei:123.00$8650670207500000,valid:0.00,latitude:46.22,longitude:6.14,speed:0.00|end

Ce que je constate, c'est que ALLS m'affihe un résultat qui me semble est correct.

Pour rappel, c'est cette partie de ubidotsSendAll(TOKEN):

  int i;
  char alls[255];
  char tmp[255];
 //  String all;
  //  String str;


  /*  
    all = USER_AGENT;
    all += "/";
    all += VERSION;
    all += "|POST|";
    all += token;
    all += "|";
    all += _dsTag;
*/
    if (_dsName != NULL) {
        //all += ":";
        //all += _dsName;
        sprintf(alls,"%s%s%s%s%s%s%s=>",USER_AGENT,"/",VERSION,"|POST|",token,"|",_dsTag,_dsName);
    }
//    all += "=>";
    sprintf(alls,"%s%s%s%s%s%s%s=>",USER_AGENT,"/",VERSION,"|POST|",token,"|",_dsTag,_dsName);


Serial.print(F("ALLS: ")); Serial.println(alls);

Parcontre l'extraction de valeurs enregistré dans la Structure, me semble voler en kakaouette :o) et c'est cette partie ou j'ai mis des Serial.print() pour voir au moin ce que m'affiche, approximativement la boucle

Serial.print(F("VAR: "));

// Dessous je dois encore trouver comment faire pour encapsuler ceci dans le tmp ou alls

    for (i = 0; i < currentValue; ) {
        // str = String(((val + i)->varValue), 2); // Ici je dois trouver comment convertir de uint8_t en char, mais printf pourrais m'aider
        //Serial.print(((val + i)->varValue), 2);
        
        //all += String((val + i)->varName); // Ici, je mets le résultat dans tmp
        Serial.print((val + i)->varName);
        
        Serial.print(":");
        //all += ":";
        
        //all += str;
          Serial.print(((val + i)->varValue), 2);

        if ((val + i)->ctext != NULL) {
            //all += "$";
            Serial.print("$");

            //all += String((val + i)->ctext);
            Serial.print((val + i)->ctext);
        }
        i++;
        if (i >= currentValue) {
            break;
        } else {
            //all += ",";
            Serial.print(",");
        }
    }
  //  all += "|end";
    Serial.print("|end");

Et c'est sur cette partie que je nage. J'essaye donc que le résultat doit empiler dans 'alls' qui est de type char, à la place de 'all', qui est de type String.

Et d'empiler les résultat de 'tmp' qui est de type char, à la place de 'str' qui est de type String.

Et c'est là dessus, ou j'aimerais bien un coup de pource.

Le format final, je ne le sais pas, car c'est la premiere fois que je fais ceci mais je pense qu'il doit etre proche de

imei:123.00$865067020750000,valid:0.00,latitude:46.22,longitude:6.14,speed:0.00|end

saud que là il manque 'localisation', 'tower','timestamp'

Je pense que si on arrive bien refaire cette boucle, j'arriverais à aller chercher les problème suivant.

Miles et miles merci pour vos aides!!!!!

(J'espère avoir bien fourni les informations nécessaires)

(J'espère avoir bien fourni les informations nécessaires)

euh.. :slight_smile: peut-être mais je ne sais pas où... je ne crois pas que vous ayez simplifié...

je ne sais même plus où est la question :slight_smile:

Sérieusement, au lieu de vous casser la tête à convertir des int, des long, des char* etc dans un gros buffer qui prend plein de mémoire, faites vraiment juste des print par petits bouts vers le port série de contrôle. Ce port série n'attend pas de tout recevoir en une seule chaîne, en fait il ne sait pas si vous faites un deux ou trois printf... il reçoit juste un flot de caractères et en fait quelque chose...

Salut,
je comrends.

En fait ma question elle se trouve ici

Serial.print(F("VAR: "));

// Dessous je dois encore trouver comment faire pour encapsuler ceci dans le tmp ou alls

    for (i = 0; i < currentValue; ) {
        // str = String(((val + i)->varValue), 2); // Ici je dois trouver comment convertir de uint8_t en char, mais printf pourrais m'aider
        //Serial.print(((val + i)->varValue), 2);
        
        //all += String((val + i)->varName); // Ici, je mets le résultat dans tmp
        Serial.print((val + i)->varName);
        
        Serial.print(":");
        //all += ":";
        
        //all += str;
          Serial.print(((val + i)->varValue), 2);

        if ((val + i)->ctext != NULL) {
            //all += "$";
            Serial.print("$");

            //all += String((val + i)->ctext);
            Serial.print((val + i)->ctext);
        }
        i++;
        if (i >= currentValue) {
            break;
        } else {
            //all += ",";
            Serial.print(",");
        }
    }
  //  all += "|end";
    Serial.print("|end");

Tout est stoké dans 'all' et 'str' qui sont des String et j'aimerais qu'ils soient stoké dans quelque chose d'autre, pour qu'à la fin, j'ai le résultat de l'exécution de cette boucle, qui se met à la suite de

FONA/2.0|POST|tTPyJRD21PsM0q|FONA=>

et soit terminé par

|end

// Valeur de 'alls' :
FONA/2.0|POST|tTPyJRD21PsM0q|FONA=>{Résulta de l'exécution de la boucle}|end

mais une fois que c'est dans une variable, vous voulez en faire quoi? l'envoyer en sortie non? donc pourquoi ne pas l'envoyer en sortie petit à petit?

y'a un truc que je ne capte pas.

(sinon pour bâtir la chaîne il faut un buffer, des strcat() des itoa(), etc --> cf les fonctions de string.h de C/C++ ou dtostrf() de stdlib.h AVR)

Bonjour
J'ai finalement réglé mon problème.
Avant

for (i = 0; i < currentValue; ) {
        str = String(((val + i)->varValue), 2);
        all += String((val + i)->varName);
        all += ":";
        all += str;
        if ((val + i)->ctext != NULL) {
            all += "$";
            all += String((val + i)->ctext);
        }
        i++;
        if (i >= currentValue) {
            break;
        } else {
            all += ",";
        }
    }
    all += "|end";
    Serial.println(all.c_str());

Après

  for (i = 0; i < currentValue; )
  {
    strcat(alls2,(val + i)->varName); 
    strcat(alls2,":");
    sprintf(tmp,"%g",(val + i)->varValue);
    strcat(alls2,tmp); 
    if ((val + i)->ctext != NULL)
    {
      strcat(alls2,"$");
      strcat(alls2,(val + i)->ctext);
    }
    
    i++;
    if (i >= currentValue)
    {
      break;
    }
    else
    {
      //all += ",";
      strcat(alls2,",");
    }
  }
  strcat(alls2,"|end");
  Serial.println(alls2);

Merci

du coup je rejoins J-M-L, si c'était pour envoyer la chaine construite sur un port série, ca ne sert pas à grand chose de "bufferiser" la chaine avant de l'envoyer, il suffit d'envoyer direct sur le lien série en faisant des "print()", puis le "println()" lors de la dernière ("|end") comme le suggérait sagement J-M-L. Sinon, c'est manger de la RAM inutilement...