Go Down

Topic: Esp32 : deep sleep et SPIFFS  (Read 1 time) previous topic - next topic

lesept

Bonsoir

En étudiant le deep sleep de l'esp32 j'ai vu que les variables sont perdues si on ne prend pas la précaution de les stocker dans la RAM de la RTC, qui est le seul bloc du SoC qui reste alimenté. Mais qu'en est il de la mémoire SPIFFS ? Est ce que les fichiers stockés sont conservés durant ce deep sleep ?

Je crois que la RAM de la RTC fait 4ko ou 8ko. Le compilateur m'indique que les variables globales de mon code occupent près de 50000 octets, donc ça ne rentrera pas dans la RAM RTC, même si je pousse très fort... D'où l'idée de stocker ce dont je me sers dans un fichier dans la SPIFFS. Qu'en pensez vous ?
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

J-M-L

#1
Nov 18, 2018, 06:30 am Last Edit: Nov 18, 2018, 06:30 am by J-M-L
Le SPIFFS est conservé même si vous coupez le jus. c'est en mémoire flash, pas SRAM
Ce sera lent au réveil de tout charger.

50Ko de variables globales ? vous y faites conservez quoi ?
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

lesept

C'est vrai que le code est un peu long et fait appel à pas mal de bibliothèques. Il va chercher l'heure et la météo sur Internet, parse les données Json et les affiche sur deux écrans OLED. Il y a aussi un accéléromètre et un DHT22.

L'IDE m'indique ceci:
Quote
Le croquis utilise 876190 octets (66%) de l'espace de stockage de programmes. Le maximum est de 1310720 octets.
Les variables globales utilisent 45108 octets (13%) de mémoire dynamique, ce qui laisse 282572 octets pour les variables locales. Le maximum est de 327680 octets.
C'est bien 45 ko de variables globales ? C'est vrai que j'ai une douzaine de const String utilisés par la bibliothèque ArduinoJson, mais le reste de mes variables n'est pas grand chose, à part 5 tableaux de dimension 6...

Cette consommation vient-elle des bibliothèques ?
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

al1fch

#3
Nov 18, 2018, 09:57 am Last Edit: Nov 18, 2018, 10:01 am by al1fch
Bonjour

Pour nos applications on dipose de 8k en Ram RTC (c'est la RTC Slow  Memory)

Une seconde RAM RTC de 8k (= RTC Fast Memory)  , elle ausi préservée en 'sommeil profond', est résevée au coeur 'protocole' par opposition au coeur 'application'.


-> Délimiter les informations essentielles qu'il faut conserver,  pas  besoin de sauver tout le contexte qui a permis de les produire. Pourquoi sauvegarder le buffer json avant parsing , par exemple ?

8K de Ram préservée ça parait déjà conséquent pour l'application décrite !

lesept

-> Délimiter les informations essentielles qu'il faut conserver,  pas  besoin de sauver tout le contexte qui a permis de les produire. Pourquoi sauvegarder le buffer json avant parsing , par exemple ?

8K de Ram préservée ça parait déjà conséquent pour l'application décrite !
Je suis d'accord avec toi, c'est pourquoi je posais cette question. Je pense que le buffer json est local à la fonction qui le parse. Je peux gagner de la mémoire en mettant mes const String là où je les utilise.

Le reste j'en ai besoin car typiquement les données sont produites dans une fonction et affichées dans d'autres.
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

J-M-L

Si vous pouvez régénérer les données (nouvelle lecture capteur) ce n'est pas nécessaire de les conserver non plus
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

lesept

J'ai fait la chasse au gaspi, mais le résultat n'est pas fameux :

Quote
Le croquis utilise 876242 octets (66%) de l'espace de stockage de programmes. Le maximum est de 1310720 octets.
Les variables globales utilisent 44848 octets (13%) de mémoire dynamique, ce qui laisse 282832 octets pour les variables locales. Le maximum est de 327680 octets.
Voila mes déclarations de bibliothèques :
Code: [Select]
#include <Arduino.h>
#include <Wire.h>
#include "I2Cdev.h"
#include "MPU6050.h"
#include <U8g2lib.h>
#include <dummy.h>  //for esp32
#include <WiFi.h>
#include <HTTPClient.h>
#include "time.h"
#include "DHTesp.h"
#include "ArduinoJson.h"

// Bus 1 : les 2 écrans
#define SDA1 19
#define SCL1 23

// Bus 2 : l'accéléro
#define SDA2 5
#define SCL2 4

#define LED_PIN 22
#define DHT_PIN 26

U8G2_SH1106_128X64_NONAME_F_SW_I2C display1(U8G2_R0, SCL1, SDA1);
U8G2_SH1106_128X64_NONAME_F_SW_I2C display2(U8G2_R0, SCL1, SDA1);

// U8G2_SSD1306_128X64_NONAME_F_SW_I2C display1(U8G2_R0, SCL1, SDA1);
// U8G2_SSD1306_128X64_NONAME_F_SW_I2C display2(U8G2_R0, SCL2, SDA2);
MPU6050 accelgyro;
DHTesp dht;

#include "Variables.h"
#include "Internet.h"
#include "Affichage.h"
#include "Accelero.h"


et mes variables :
Code: [Select]
// Accelerometer
byte Orientation;
bool AcceleroSuccess = false;
enum Direction {Xplus, Xmoins, Yplus, Ymoins, Zplus, Zmoins, Unknown};

// Time
int second;
int minute;
int hour;
int day;
int month;
int year;
int weekday;

// Second interval fine adjustment
#define ADJUST_SECONDS 0 // 0 pour ne pas rechercher l'optimum sinon 1
unsigned long nmicro = 1000;
int oldsec;

// Meteo
// Current weather
const char* current_weather_description;
float current_temp;
float current_pressure;
const char* current_PressureTendency_Code;
const char* current_weather_icon;
float current_precip_mm;
int current_humidity;
float current_uv;
// Hourly forecast
#define nbHourForecast 6
byte hourlyHours[nbHourForecast] = {0,1,2,5,8,11};
const char* hourly_weather_description[nbHourForecast];
float hourly_temp[nbHourForecast];
int hourly_weather_icon[nbHourForecast];
int hourly_PreciProba[nbHourForecast];
// Daily forecast
#define nbDayForecast 3
const char* daily_weather_description[nbDayForecast];
const char* daily_weather_icon[nbDayForecast];
const char* daily_date[nbDayForecast];
float daily_mintemp[nbDayForecast];
float daily_maxtemp[nbDayForecast];
float daily_Precip[nbDayForecast];
const char* daily_sunrise[nbDayForecast];
const char* daily_sunset[nbDayForecast];

// Program
bool blinkState = false;
unsigned long chronoD1;
unsigned long chronoAcc;
bool Aff2 = false;


Au passage, quand je compile un code vide (setup et loop vides, pas de variables ni de bibliothèques), j'obtiens quand même :
Quote
Le croquis utilise 173740 octets (13%) de l'espace de stockage de programmes. Le maximum est de 1310720 octets.
Les variables globales utilisent 13272 octets (4%) de mémoire dynamique, ce qui laisse 314408 octets pour les variables locales. Le maximum est de 327680 octets.
De 13000 à 45000, ça me fait 32000 octets : je ne vois pas où ils sont dans mes variables !
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

al1fch

#7
Nov 18, 2018, 10:36 am Last Edit: Nov 18, 2018, 10:50 am by al1fch
l'exemple 'Bare Minimum' donne 13 272 ko  en  'variables globales' (en gros les besoins du 'firmware embarqué
l'exemple 'WiFi Client' donne 41 504 ko

plus peut être les buffers des afficheurs selon les librairies (écriture directe dans l'afficheur ou via un buffer en ram ?)

lesept

Où vois-tu ça ?

A terme, je compte remplacer les OLED (0.96") par des e-paper. Mais ils seront plus grands...
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

J-M-L

Une façon simple de traiter le problème est de vous concentrer uniquement sur vos données et traiter le réveil du deep sleep comme un reboot (voir carrément rebooter)

c'est un peu le même principe de la reprise après panne / démarrage à froid -> vous avez stocké dans une mémoire permanente les valeurs clés et vous initialisez vos variables avec cela

Quand j'ai ce besoin généralement je mets toutes mes variables d'états dans une grosse structure globale (cf un exemple dans mon petit projet de gestion te T°) en utilisant une "séquence magique" pour vérifier que la mémoire a bien été initialisée. L'avantage de la structure c'est que vous pouvez écrire ça comme un BLOB  (binary large object) et tout dumper ou lire d'un seul coup en EEPROM ou sur un fichier

Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

lesept

Merci J-M-L, je vais étudier ça
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

J-M-L

#11
Nov 18, 2018, 11:11 am Last Edit: Nov 18, 2018, 11:16 am by J-M-L
l'avantage de "forcer" le reboot c'est que vous repassez par le setup() et donc réinitialisez toutes vos classes comme il faut (faut prêter attention à toutes celles qui ont un begin() ou un init() sinon)


j'ai des montages ou je ne m'ennuie pas à gérer le sleep car il y a aussi l'alimentation de tout le reste donc j'éteins carrément le système en utilisant un Pololu Power Switches. ce switch c'est ce que l'utilisateur va appuyer pour démarrer manuellement le système mais en le connectant à une RTC ou un autre petit système vous pouvez aussi déclencher le réveil quand vous voulez en envoyant un front au power switch
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

al1fch

#12
Nov 18, 2018, 11:16 am Last Edit: Nov 18, 2018, 11:47 am by al1fch
Faire l'inventaire des donnnées minimales dont il faut absolument disposer  au redémarrage
(les données à afficher ? d'autres ?)

SI le cumul tient dans les 8k et s'il n'est pas indispensable de conserver les données en cas de coupure de l'alimentation utiliser la Ram RTC

Sinon utiliser la mémoire Flash SPI par SPIFFS comme indiqué par J-M-L
(L'EEPROM émulée peut également servir mais il faut tenir compte de la taille des pages et sans doute faire un partitionnement adapté)

J-M-L, le réveil  partir d'un deepsleep(ESP8266 ou ESP32) est un redémarrage  avec exécution du setup() !

En deep sleep tout est 'éteint' dans les ESP sauf la partie dite RTC (timer et ram)
C'est un redémarrage total  avec le bénéfice de la  conservation  du  contenu de la RAM RTC.  
Il me semble d'ailleurs  qu'on retrouve même les états initiaux des GPIO. Les valeurs en RAM normale sont aléatoires.
L'ESP doit recharger le code de la Flash vers la Ram. (en gros 100ms)
Bien entendu millis() repart à zéro , je sauvegarde en Ram RTC sa valeur juste avant l'appel du deepsleep , ça me permet de suivre la durée des phases d'eveil  que je cherche à réduire.

Bien entendu en deep sleep il reste une petite  consommation , mais avec les 5µA de l'ESP32 j'ai renoncé à faire des coupures d'alimentation. (5µA obtenus sans interface série/USB et sous alimentation directe 3,2V donc sans régulateur)

lesept

Bon, merci de votre aide. Pour l'instant ça marche...

Il me reste à tester le réveil par interruption de l'accéléromètre et par les touchs de l'ESP32.
Et à peaufiner le code. Mais je ne vois toujours pas d'où viennent ces 45ko de variables globales. Lorsque je passe mes variables à moi dans la RAM RTC, le nombre diminue un peu : en gros j'utilise réellement moins d'un ko.
Le reste c'est en effet les buffers des écrans et ... quoi ? Les buffers des json ?
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

al1fch

#14
Nov 18, 2018, 01:59 pm Last Edit: Nov 18, 2018, 02:28 pm by al1fch
Ton apllication repose sur un socle de fonctionnalités dont RTOS qui est en service d'office en arrière plan pour l'ESP32.(même si tu ne t'y réfères pas explicitement)

La sur-couche d'abstraction  Arduino par dessus l'IDF Espressif masque tout cela.

Regardes l'exemple 'Bare Minimum' : setup() et main() vide -> 13272 octets de RAM
Ajoutes la pile de protocole réseau , le WIFI et un client -> 41 504 octets
Le complément vient avec des librairies....... et enfin..... 'peanuts' tes variables.

Voir par exemple cette analyse d'un simple Hello World



Go Up