Au cours de l'écriture de mon projet Apaguard sur l'Arduino, j'ai été confronté au problème d'espace disponible dans la SRAM...

L'arduino UNO ne possède "que" 2048 octets de SRAM, qui peuvent être rapidement remplis si l'on n'y prend pas garde. Je vous livre ici les quelques astuces que j'ai utilisées afin de mieux gérer la SRAM et gagner de précieux octets !

Règle numéro 1 : bien déclarer ses variables

1) Utilisez toujours le bon type de variable pour votre usage, à savoir TOUJOURS celui utilisant le moins d'octets. Le tableau comparatif ci-dessous donne l'espace occupé par type.

Type MinMaxSizePerf
byte uint8_t 0 255 8  :)
char   -128 127 8  :)
unsigned int uint16_t 0 65 535 16  :/
int   −32 768 32 767 16  :/
unsigned long uint32_t 0 4,294,967,295 32  :(
long   -2,147,483,648 2,147,483,647 32  :(
float / double   -3.4028235E+38 3.4028235E+38 32  :'(

 

Par exemple, si une variable x doit "varier" de 1 à 10, déclarez la en BYTE et non INT. Vous économiserez déjà 8 octets !

Autant que possible, évitez d'utiliser le type FLOAT qui est gourmand et dont la manipulation consommera beaucoup de ressources.

2) Toutes les variables qui ne seront jamais modifiées, donc en "lecture seule" doivent être déclarées avec CONST. Elles seront alors stockées dans la mémoire flash et non la SRAM.

Par exemple byte x = 10 doit être déclarée const byte x = 10 . Changez également byte pinPowerGSM = 13 en const byte pinPowerGSM = 13

3) Utilisez la fonction PROGMEM pour déplacer en mémoire flash les variables du type ARRAY ou BITMAPS. Vraiment utile uniquement  avec les gros tableaux car il y a quelques inconvénients à utiliser cette méthode.

PROGMEM est une fonction de la librairie pgmspace.h. Il faut donc charger cette librairie au prélable :en rajoutant :  #include <avr/pgmspace.h> au début du sketch.

const char apn[]  = "orange"  devient const char apn[] PROGMEM = "orange"

Pour lire les variables, il faudra alors utiliser une autre fonction de la librairie pgmspace.h,  pgm_read_xx. Consultez la documentation afin de connaitre la bonne fonction à utiliser par type de variable.

Règle numéro 2 : Variables globales et locales

De façon générale, l'utilisation de variables globales n'est pas recommandée. Utilisez des variables locales dés que possible.  Si une variable est utilisée dans une fonction, alors elle doit être déclarée uniquement dans cette fonction.

Si une variable globale est déclarée, son adresse sera stockée en SRAM. La manipulation d'une variable globale consomme également des octets pour accéder à cette adresse.

Règle numéro 3 : Utilisez la macro F !

Lors de l'utilisation des fonctions d'impression , print, println, Serial.print, etc... Le compilateur charge le texte à afficher en SRAM alors qu'il n'y a aucune utilité à le faire. La macro F a été écrite afin de charger ces textes dans la mémoire flash.

ATENTION, à n'utiliser qu'avec les impressions de texte pur, pas avec les variables !! Prenez l'habitude d'utilisez systématiquement cette macro si vous avez des sketch importants, l'économie réalisée sera substantielle !

Par exemple : Serial.println ("Ceci est un test") s'écrira Serial.println (F("Ceci est un test"))

Règle numéro 4 : optimisez les boucles !

Aussi étonnant que cela puisse paraître, les boucles qui décrémentent consomment moins de ressources que celles qui incrémentent ! Arrangez-vous pour utiliser i-- à la place de i++ .

Essayez d'intégrer les opérations et déclarations de différentes boucles en une seule.

Règle numéro 5 : Utilisez la compilation conditionnelle !

Bien souvent, lors du développement d'un sketch, on effectue des commandes, on envoie des indications sur le PC qui après développement ne servent plus et sont donc à supprimer pour un sketch en production afin de gagner de l'espace.

Il existe cependant une option bien utile du compilateur qui permet de gérer ce problème sans toucher aucune ligne de code ! J'ai  nommé la compilation conditionnelle.

Par exemple :

// décommenter pour passer en mode développement
//#define DEBUG_DISPLAY
void setup(){
}
void loop() {
#ifdef DEBUG_DISPLAY
    Serial.println(get_unixtime(now));
    Serial.println(F("Program started. Wait for wakeup !"));
#endif
}

 

En commentant ou décommentant la ligne dans la déclaration, le compilateur intègrera ou non les parties définies entre les #ifdef DEBUG_DISPLAY (ou tout autre texte comme DEBUG ou TEST) et #endif. On peut y mettre tout ce qu'on veut, commandes, fonctions, etc...

GENIAL, non ?

 

Références :