Tableau de char (malloc ou progmem) et aide a la mise en place

Salut,

Dans mon projet, j'utilise plusieurs tableaux de char, maintenant que la fonctionnalité est la, je passe a l’étape d'optimisation.

Si j'ai bien compris:

  • PROGMEM permet de stocker mes tableaux de char dans la flash.
  • malloc, realloc, free, etc... pour la gestion de tableaux dynamique.

Donc le but du jeux est de privilégié des tableaux dont la taille est connu, pour limiter l'utilisation de la ram?

Un conseil : ne joue pas avec malloc, realloc, free sur un arduino.
Mets ça de côté pour l'instant, sauf si tu n'as vraiment pas le choix.

Concernant PROGMEM :

Prenons un truc comme

char mon_texte[] = "Le joli message que je veux afficher quelque part";

Ce truc est présent dans le code compilé, stocké dans la mémoire flash.
Lorsque l'arduino est sous tension, il est également présent dans la RAM, stocké comme une variable globale.
Il te bouffe donc 50 octets de RAM en permanences.

Et comme tu as plein de messages que tu veux pouvoir afficher, cela te fait une collection de trucs qui bouffent rapidement toute la RAM de l'arduino.

A noter que les conséquences sont les mêmes pour un simple

Serial.println("Le joli message que je veux afficher quelque part");

Le texte est stocké sous forme de tableau de caractères en RAM. L'adresse de sa première cellule est passée en paramètre d'appel de la méthode println().

PROGMEM va te permettre de ne pas avoir une image systématique de tes chaines de caractères dans la RAM de l'arduino.
Celles-ci restent uniquement stockées dans la mémoire flash.

La contrainte, c'est qu'elle ne peuvent être accédées directement par le programme.
Lorsque que tu veux récupérer une chaîne de caractères, il faut commencer par la recopier dans une variable (tableau de char) déclaré en RAM.

Pour gérer tes chaînes de caractères en mémoire flash, le principe est de les définir toutes en PROGMEM

Puis de déclarer également en progmem un tableau de pointeurs (attention la tête !) vers ces chaînes.
Ce tableau te servira d'index pour la recopie vers la ram

C'est bien expliqué là (en anglais) :

dans le paragraphe Arrays of strings

J'ai cerveau qui bou moi :smiley:

Le serial.print, je l'utilise seulement pour le debug et encadré par

#ifdef DEBUG_RAM
  Serial.println(F("La stack est fragmenté!"));
#endif

Mais juste pour mon info, lorsque l'on utilise la fonction F(), on utilise comme même 50 octets de RAM avec cette fonction?

Un autre exemple que je voudrais aborder, pour les tableaux déclarés dans une fonction en locale, la mémoire est libéré après utilisation on est d'accord, c'est comme pour toutes les variables locales?
Dans cette fonction, les 8 octets attribué a dataTmp sont ensuite libéré?

// Ajoute la convertion d'un type float a la chaine de caractere
void floatTo2TabChar(float number, char data[])
{
  int DATATMP_SIZE = 8; //8 //Valeur la plus longue (-127.00) 
  char dataTmp[DATATMP_SIZE];
  // On rempli notre tableau de caractere NULL
  memset(dataTmp,NULL,DATATMP_SIZE); 
  // On met la partie antiere du nombre dans le tableau
  itoa (int(number), dataTmp, 10);// radix: 2=binary "10=decimal" 16=hexadecimal
  strcat(data,dataTmp);
  // On recupere la partie decimal
  number -= int(number); // On soustrait le nombre par son entier
  number *= 100; // On multiplie *100 les décimales
  number = int(number); // On recupere la ou les decimale(s)
  // On ajoute le point de separation des antiers de la decimal
  strcat(data,"."); 
  // On met la partie decimal du nombre dans le tableauf
  memset(dataTmp,NULL,DATATMP_SIZE); 
  itoa (number, dataTmp, 10); // radix: 2=binary "10=decimal" 16=hexadecimal
  strcat(data,dataTmp);
}

Merci pepe

Je propose donc mon code sur lequel, je voudrais optimisé la RAM. (voir code en piece jointe)

Toute proposition d'amélioration est la bienvenue...

Fonctionnement:
Je scan dans mon loop() la fonction "receiveDataToServer()"
si j'ai une requette udp demandant l’envoie des données (voir switch case 1:) j'appelle la fonction: sendDataToServer(1);

sendDataToServer(1); prépare et envois les données au serveur via le shield wifi

Note 1:
sendDataToServer(1); // Pour l'affichage des données sur le site web
sendDataToServer(2); // Pour remplir une base de données toute les minutes (affichage de graphique d’évolution)

Note 2:
Dans wifiSendData(data, DATA_SIZE); Je découpe mes données en 64 octets avant envois, je souhaite cela, car cela améliore mon transfert, de 7s je passe à 2 secondes, non négligeable pour le confort d'utilisation du système.

  • Et d'ailleurs, je comprend pas pourquoi l’envoi des données plante lorsque j'augmente la valeur du tampon au dessus de 64 (96 ou 128)?

Pour le moment le tableau data atteint un maxi de 366 octets, mais c'est que le début de mon travail, il devrait facilement doubler.

Il ne faut pas négligé non plus la taille du tableau "reply" dans "receiveDataToServer()"

Le retour du DEBUG via serial.print:

-> Ram disponible: 3737 octets 

 -> function:1
 -> to:0
 -> val:0
 -> valFloat:0.00
 -> date:0

-> Connection au serveur: 192.168.1.211:80
-> Ram disponible: 3218 octets 

 |-> Envoi des donnees pour affichage sur le serveur
 |-> S U C C E S de la connection au serveur
 |-> Nombre de caractere disponible dans le buffer: 34
 |-> Nombre de caractere envoye: 366
 |-> Caractere envoye: do=data&func=0&to=0&val=0&valfloat=0&date=0&data=1420844424,1,192,168,1,240,8080,192,168,1,211,8888,80,19.62,19.62,19.62,19.62,44,24.0,23.0,22.0,21.50,0,1,2,0,1,1,1,1,8.0,33.0,13.0,25.0,10.50,24.0,6.50,27.50,255,255,0,255,480,600,660,720,720,480,240,120,240,180,120,60,255,255,255,255,26,26,26,26,0,1,1,1,1,0,0,0,0,6,7.0,6.50,7.50,1,1,390,420,1,1,0,0,2,2,1,1,1,1,0,0
 |-> Envois de donnees au serveur effectues en: 1913ms

-> Envoi une confirmation UDP de la reception des packets: func=1&to=0

-> Ram disponible: 3701 octets

En attendant vos retours, je test l'utilisation de progmem dans mon programme...

z_sendReceiveData.ino (30 KB)

Salut,
Petit retour de test de PROGMEM: ça fonctionne pour les petits tests que j'ai effectué...
Mais je ne voit pas comment le mettre en place dans mon programme, puisque j'ai un tableau de chaine de caractère (data[]) est de taille fixe, mais que ma chaine de caractères, elle n'est pas constante (relevé de mesure)?

bricoleau:
La contrainte, c'est qu'elle ne peuvent être accédées directement par le programme.
Lorsque que tu veux récupérer une chaîne de caractères, il faut commencer par la recopier dans une variable (tableau de char) déclaré en RAM.

Donc si je comprend bien, vu que ma chaine est déclaré en local et que si je veux la récupéré, je devrais créer un autre chaine en RAM qui me prendra autant d'octets, alors j'ai aucun intérêt a utilisé PROGMEM. Sauf si je peux lire les 64 premiers octects, les envoyés et lire les 64 octets suivant et ainsi de suite?

Je suis bloqué pour réduire la taille de ma chaine data, car je dois avant tout déclaré la taille finale du data dans le header du POST avant envois des données.

Il existe bien le mode chunked dans apache, mais il y a des problèmes d'implémentation dans la version 2.4 d'apache.

dudux2:
Salut,
Petit retour de test de PROGMEM: ça fonctionne pour les petits tests que j'ai effectué...
Mais je ne voit pas comment le mettre en place dans mon programme, puisque j'ai un tableau de chaine de caractère (data[]) est de taille fixe, mais que ma chaine de caractères, elle n'est pas constante (relevé de mesure)?

Flash = invariable

L'utilisation de progmem est réservée à des valeurs fixes.

Globalement, ce que tu émets est une chaine de caractères, comportant des parties constantes et des parties variables.

Progmem ne te servira que pour les parties constantes.

Merci les gars!

J'ai fais fumé le cerveau pour rien. :smiley:

La partie header qui est constante, je l'ai passé sous cette forme par simplicité:
client.print(F("CONSTANTES"));

Par contre la partie, gestion udp, j'ai 200 octets de constantes (confirmation udp) que je vais passé sous progmem.

Merci encore pour votre aide précieuse.