Mémoire disponible faible, des problèmes de stabilité pourraient survenir

Bonjour à tous,

J'ai un projet de mesure du vent et de la température en cours qui fonctionne mais avec un message alarmant lors de la compilation : "Mémoire disponible faible, des problèmes de stabilité pourraient survenir".

Quelqu'un pourrait-il m'aider à optimiser le code et voir comment récupérer de la mémoire dynamique ?

J'ai bien quelques pistes :

  • Moyenne de la vitesse avec un tableau de 48 mesures pour une moyenne sur 2 mn (c'est court, j'aurais aimé 5 mn soit 120 mesures;

  • Moyenne de la direction du vent, idem ci dessus;

  • Bibliothèque "cactus_io_DS18B20.h" ?

Merci pour votre aide.

Fitness04

//Capteur anémomètre-Girouette DAVIS
//Inspiré du site ci dessous
//http://cactus.io/hookups/weather/anemometer/davis/hookup-arduino-to-davis-anemometer-software

#include <SPI.h>
#include <Ethernet.h>

#include "TimerOne.h"
#include <math.h>

#include "cactus_io_DS18B20.h"

#define TX_Pin 8
#define DS18B20_Pin 9
#define WindSensor_Pin (2)
#define WindVane_Pin (A2)
#define VaneOffset 0

volatile unsigned long tipCount;
volatile unsigned long contactTime;

volatile unsigned int  timerCount;
volatile unsigned long rotations;
volatile unsigned long contactBounceTime;

const float DEG2RAD = 3.14156 / 180.0; // convert degrees to radian
const float RAD2DEG = 180 / 3.14156; //convert radian to degrees
float winDir[48];
float sinSum = 0;
float cosSum = 0;

volatile float windSpeed;
volatile float xMin;
int vaneValue;
int vaneDirection;
int calDirection;
int lastDirValue;

float minTemp;
float maxTemp;

const int numReadings = 48;//2mn
const int numReadingsDir = 48;//2mn

int readings[numReadings];
int readIndex = 0;
int total = 0;
int windDirection = 0;
int windSpeedMax = 0;
int averageSpeed = 0;

int readingsDir[numReadingsDir];
int readIndexDir = 0;
int avg_windDirection = 0;
int avgWinDir = 0;
String girouette = "";

DS18B20 ds(DS18B20_Pin); // on digital pin 9

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 19);
EthernetServer server(80);

void setup() {

  lastDirValue = 0;
  rotations = 0;
  timerCount = 0;

  ds.readSensor();
  minTemp = ds.getTemperature_C();
  maxTemp = ds.getTemperature_C();

  // disable the SD card by switching pin 4 high
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);

  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();

  Serial.begin(9600);
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
  for (int readIndexDir = 0; readIndexDir < numReadingsDir; readIndexDir++) {
    winDir[readIndexDir] = 0;
  }

  pinMode(TX_Pin, OUTPUT);
  pinMode(WindSensor_Pin, INPUT);
  attachInterrupt(digitalPinToInterrupt(WindSensor_Pin), isr_rotation, FALLING);

  // Setup the timer interupt for 0.5 second (500 000 microSecondes)
  Timer1.initialize(500000);
  Timer1.attachInterrupt(isr_timer);

  sei();
}

void loop() {

  ds.readSensor();
  // update min and max temp values
  if (ds.getTemperature_C() < minTemp) {
    minTemp = ds.getTemperature_C();
  }
  if (ds.getTemperature_C() > maxTemp) {
    maxTemp = ds.getTemperature_C();
  }

  getWindDirection();

  // Only update the display if change greater than 5 degrees.
  if (abs(calDirection - lastDirValue) > 5) {
    lastDirValue = calDirection;
  }

  EthernetClient client = server.available();
  if (client) {
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          digitalWrite(TX_Pin, HIGH);
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");
          client.println("Refresh: 10");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html><body>");
          client.print("<span style=\"font-size: 15px\";>");
          client.print("
&nbsp;&nbsp;Vent instantane : ");
          client.print(windSpeed);
          client.println(" Km/h");
          client.print("

&nbsp;&nbsp;Vent moyen (sur 2 mn) ");
          client.print(averageSpeed);
          client.println(" Km/h");
          client.print("

&nbsp;&nbsp;Vent maxi ");
          client.print(windSpeedMax);
          client.println(" Km/h");
          getWindDirection();
          client.print("

&nbsp;&nbsp;Direction instantannee  : ");
          getHeading(calDirection);
          client.print(girouette);
          client.print("  -  ");
          client.print(calDirection);
          client.print(" &deg ");
          getWindDirection();
          client.print("

&nbsp;&nbsp;Direction moyenne (sur 2 mn) : ");
          getHeading(avg_windDirection);
          client.print(girouette);
          client.print("  -  ");
          client.print(avg_windDirection);
          client.print(" &deg ");
          client.print("

&nbsp;&nbsp; Temperature  : ");
          client.print(ds.getTemperature_C());
          client.println(char(176));
          client.println(" C");          
          client.print("

&nbsp;&nbsp; Temperature mini : ");
          client.print(minTemp);
          client.println(char(176));
          client.println(" C");
          client.print("

&nbsp;&nbsp; Temperature maxi : ");
          client.print(maxTemp);
          client.println(char(176));
          client.println(" C </span>");
          client.println("</html>");
          digitalWrite(TX_Pin, LOW);
          break;
        }
        if (c == '\n') {
          currentLineIsBlank = true;
        } else if (c != '\r') {
          currentLineIsBlank = false;
        }

      }
    }
  }
  delay(1);
  client.stop();
  Ethernet.maintain();

}//Fin loop

// isr routine for timer interrupt
void isr_timer() {

  timerCount++;
  if (timerCount == 5)//5 x .5 = 2.5 s
  {
    // convert to mp/h using the formula V=P(2.25/T)- Davis Anemometer
    // V = P(2.25/2.5) = P * 0.9
    //en Km/h = P * 0.9 * 1.60934
    windSpeed = rotations * 0.9 * 1.60934;

    // ************Calcul moyenne sur 2 mn************
    total = total - readings[readIndex];
    readings[readIndex] = windSpeed;
    total = total + readings[readIndex];
    readIndex = readIndex + 1;
    if (readIndex >= numReadings) {
      readIndex = 0;
    }
    averageSpeed = total / numReadings;
    if (windSpeed >= windSpeedMax ) {
      windSpeedMax = windSpeed;
    }
    rotations = 0;
    timerCount = 0;
  }
}

// interrupt handler to increment the rotation count for wind speed
void isr_rotation ()   {

  if ((millis() - contactBounceTime) > 15 ) {
    rotations++;
    contactBounceTime = millis();
  }
}

// Get Wind Direction
void getWindDirection() {

  vaneValue = analogRead(WindVane_Pin);
  //  Serial.print(vaneValue);
  //  Serial.println();
  vaneDirection = map(vaneValue, 0, 1023, 0, 360);
  calDirection = vaneDirection + VaneOffset;
  if (calDirection > 360)
    calDirection = calDirection - 360;
  if (calDirection < 0)
    calDirection = calDirection + 360;
  //*********Calcul Moyenne Direction**************
  winDir[readIndexDir] = calDirection;
  readIndexDir = readIndexDir + 1;
  if (readIndexDir == numReadingsDir ) {
    for (readIndexDir = 0; readIndexDir < numReadingsDir; readIndexDir++)
    {
      sinSum += sin((winDir[readIndexDir]) * DEG2RAD);
      cosSum += cos((winDir[readIndexDir]) * DEG2RAD);
    }
    float avgWinDir = atan2(sinSum, cosSum);
    avgWinDir = avgWinDir / DEG2RAD;
    if ( avgWinDir < 0 )
    {
      avgWinDir += 360;
    }
    int windDirection = (int)avgWinDir % 360;
    avg_windDirection = windDirection;
    //    Serial.print (windDirection);
    //    Serial.println ("; ");
    sinSum = 0;
    cosSum = 0;
    readIndexDir = 0;
  }
}
// Converts compass direction to heading
void getHeading(int direction) {
  if (direction < 22)
    girouette = (" N");
  else if (direction < 67)
    girouette = (" NE");
  else if (direction < 112)
    girouette = (" E");
  else if (direction < 157)
    girouette = (" SE");
  else if (direction < 202)
    girouette = (" S");
  else if (direction < 247)
    girouette = (" SO");
  else if (direction < 292)
    girouette = (" O");
  else if (direction < 337)
    girouette = (" NO");
  else
    girouette = (" N");
}

Voir ici.

Un rappel :

Théorème de Lesept : "Si tu as une erreur de compilation, quelqu'un l'a eue avant toi"

Donc, tu copies le texte de l'erreur (ou les termes les plus informatifs) dans ton browser et Google te trouvera (c'est un corollaire du théorème) un message sur un forum qui en parle. Avec un peu de bol, le problème sera résolu dans ce message...

Bonjour lesept,

Merci pour ton aide et le lien. J'ai récupéré de la mémoire rien qu'avec les :

client.println(F("Bla,bla,bla"));

Toutefois, si je passe à une moyenne de mesures sur 5 mn (120 mesures), la mémoire est utilisée à 80 %.

Existe t'il une astuce pour gagner encore de la mémoire ?

Ou plus généralement comment bien utiliser les tableaux ?

Merci

float winDir[48];

Bonjour,
il te faut 4 bytes pour stocker un élément. Ne peux-tu pas remaper en byte et économiser 75% ?

Effectivement

Le tableau float winDIR[] occupe 4x48 = 192 octets de RAM
Tout ça pour y stocker quoi ? le contenu de la variable calDirection, de type int avec une valeur qui varie de 0 à 360

beurk beurk :slight_smile:

int winDIR[48];//et hop 96 octets facilement gagnés

Et encore, 2 octets pour stocker une valeur comprise entre 0 et 360 c'est luxueux.

En se cassant un peu la tête, le tableau tomberait à 54 octets en utilisant 9 bits par mesure
Et se cassant un peu plus la tête, le tableau tomberait à 51 octets en utilisant 17 bits par paire de mesures (valeur 0 à 360x360)
Tout ça sans rien perdre en précision d'information
Edit : log(361)/log(2)*48/8=50,975 donc le mini est bien à 51 octets

Il gagnera de la mémoire pour stocker les données mais en perdra sur son code, qui devra coder et décoder les informations stockées... Compromis.

Oui mais le code n'a pas d'empreinte RAM, il reste en flash.
Là il est question d'optimiser la RAM pour augmenter la durée de stockage des mesures.

C'est vrai que je prenais comme hypothèse qu'il restait de la marge par rapport aux 32 ko de flash d'un arduino.

idem mes 2 camarades pour optimiser la vitesse du vent :

  • tu détermines a partir du vent max que tu veux détecter, le nombre d'impulsions en 2.5s et tu travail en int (ou éventuellement long)
  • tu stockes les valeurs d'impulsions
  • tu fais la moyenne sur les impulsions
  • tu calcules la vitesse que lorsque tu veux faire une restitution

Oui

L'erreur souvent commise est de transformer tout de suite la donnée issue d'un capteur pour la mémoriser dans un format plus lourd (par exemple float).

Il est plus efficace de conserver un historique des valeurs brutes retournées par les capteurs, et de les convertir uniquement au moment où on veut les restituer, pour les rendre compréhensibles par un humain.

Bonjour et merci à tous les contributeurs pour toutes ces réponses.

Je vais approfondir vos recommandations.

Je ne maîtrise pas encore tout à fait bien les différents types de variables (float, int,...)

Il faut que je m'y mette sérieusement...

Heureusement, il y a des cours en ligne...

J'optimise mon code et vous tiens au courant.

Fitness04

Bonjour à tous,

Voici le code optimisé suivant vos suggestions et surtout celles de rjnc38 pour la gestion de la vitesse max avec un travail sur la variable "rotations" et une application de la formule de vitesse à la fin du traitement, juste avant la publication.

Encore merci à tous !

//Capteur anémomètre-Girouette DAVIS
//Inspiré du site ci dessous
//http://cactus.io/hookups/weather/anemometer/davis/hookup-arduino-to-davis-anemometer-software

#include <SPI.h>
#include <Ethernet.h>

#include "TimerOne.h"
#include <math.h>

#include "cactus_io_DS18B20.h"

#define TX_Pin 8
#define DS18B20_Pin 9
#define WindSensor_Pin (2)
#define WindVane_Pin (A2)
#define VaneOffset 0

volatile unsigned long tipCount;
volatile unsigned long contactTime;

volatile unsigned int  timerCount;
//volatile unsigned long rotations;
volatile unsigned int rotations;
volatile unsigned int averageRotations;
volatile unsigned long contactBounceTime;

const float DEG2RAD = 3.14156 / 180.0; // convert degrees to radian
const float RAD2DEG = 180 / 3.14156; //convert radian to degrees
int winDir[48];
float sinSum = 0;
float cosSum = 0;

volatile float windSpeed;
volatile float xMin;
int vaneValue;
int vaneDirection;
int calDirection;
int lastDirValue;

float minTemp = 0;
float maxTemp = 0;

const int numReadings = 48;//5mn
//const int numReadings = 48;//2mn

int readings[numReadings];
int readIndex = 0;
int totalRotations = 0;
int windDirection = 0;
int windSpeedMax = 0;
int rotationsMax = 0;
int averageSpeed = 0;

int readingsDir[numReadings];
int readIndexDir = 0;
int avg_windDirection = 0;
int avgWinDir = 0;
String girouette = "";

DS18B20 ds(DS18B20_Pin); // on digital pin 9

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 19);
EthernetServer server(80);

void setup() {

  lastDirValue = 0;
  rotations = 0;
  timerCount = 0;

  ds.readSensor();
  minTemp = ds.getTemperature_C();
  maxTemp = ds.getTemperature_C();

  // disable the SD card by switching pin 4 high
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);

  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();

  Serial.begin(9600);
  for (int readIndex = 0; readIndex < numReadings; readIndex++) {
    readings[readIndex] = 0;
  }

  for (int readIndexDir = 0; readIndexDir < numReadings; readIndexDir++) {
    winDir[readIndexDir] = 0;
  }

  pinMode(TX_Pin, OUTPUT);
  pinMode(WindSensor_Pin, INPUT);
  attachInterrupt(digitalPinToInterrupt(WindSensor_Pin), isr_rotation, FALLING);

  // Setup the timer interupt for 0.5 second (500 000 microSecondes)
  Timer1.initialize(500000);
  Timer1.attachInterrupt(isr_timer);

  sei();
}

void loop() {

  ds.readSensor();
  // update min and max temp values
  if (ds.getTemperature_C() < minTemp) {
    minTemp = ds.getTemperature_C();
  }
  if (ds.getTemperature_C() > maxTemp) {
    maxTemp = ds.getTemperature_C();
  }

  getWindDirection();

  // Only update the display if change greater than 5 degrees.
  if (abs(calDirection - lastDirValue) > 5) {
    lastDirValue = calDirection;
  }

  EthernetClient client = server.available();
  if (client) {
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          digitalWrite(TX_Pin, HIGH);
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Content-Type: text/html"));
          client.println(F("Connection: close"));
          client.println(F("Refresh: 10"));
          client.println();
          client.println(F("<!DOCTYPE HTML>"));
          client.println(F("<html><body>"));
          client.print(F("<span style=\"font-size: 15px\";>"));
          client.print(F("
&nbsp;&nbsp;Vent instantane : "));
          client.print(windSpeed);
          client.println(F(" Km/h"));
          client.print(F("

&nbsp;&nbsp;Vent moyen (sur 2 mn) "));
          client.print(averageSpeed);
          client.println(F(" Km/h"));
          client.print(F("

&nbsp;&nbsp;Vent maxi "));
          client.print(windSpeedMax);
          client.println(F(" Km/h"));
          getWindDirection();
          client.print(F("

&nbsp;&nbsp;Direction instantannee  : "));
          getHeading(calDirection);
          client.print(girouette);
          client.print(F("  -  "));
          client.print(calDirection);
          client.print(F(" &deg "));
          getWindDirection();
          client.print(F("

&nbsp;&nbsp;Direction moyenne (sur 2 mn) : "));
          getHeading(avg_windDirection);
          client.print(girouette);
          client.print(F("  -  "));
          client.print(avg_windDirection);
          client.print(F(" &deg "));
          client.print(F("

&nbsp;&nbsp; Temperature  : "));
          client.print(ds.getTemperature_C());
          client.println(char(176));
          client.println(F(" C"));
          client.print(F("

&nbsp;&nbsp; Temperature mini : "));
          client.print(minTemp);
          client.println(char(176));
          client.println(F(" C"));
          client.print(F("

&nbsp;&nbsp; Temperature maxi : "));
          client.print(maxTemp);
          client.println(char(176));
          client.println(F(" C </span>"));
          client.println(F("</html>"));
          digitalWrite(TX_Pin, LOW);
          break;
        }
        if (c == '\n') {
          currentLineIsBlank = true;
        } else if (c != '\r') {
          currentLineIsBlank = false;
        }

      }
    }
  }
  delay(1);
  client.stop();
  Ethernet.maintain();

}//Fin loop

// isr routine for timer interrupt
void isr_timer() {

  timerCount++;
  if (timerCount == 5)//5 x .5 = 2.5 s
  {
    // convert to mp/h using the formula V=P(2.25/T)- Davis Anemometer
    // V = P(2.25/2.5) = P * 0.9
    //en Km/h = P * 0.9 * 1.60934
    windSpeed = rotations * 0.9 * 1.60934;
    // ************Calcul moyenne sur 2 mn avec les rotations************
    totalRotations = totalRotations - readings[readIndex];
    readings[readIndex] = rotations;
    totalRotations = totalRotations + readings[readIndex];
    readIndex = readIndex + 1;
    if (readIndex >= numReadings) {
      readIndex = 0;
    }
    averageRotations = totalRotations / numReadings;
    if (rotations >= rotationsMax) {
      rotationsMax = rotations;
    }

    averageSpeed = averageRotations * 0.9 * 1.60934;
    windSpeedMax = rotationsMax * 0.9 * 1.60934;

    rotations = 0;
    timerCount = 0;
  }
}


// interrupt handler to increment the rotation count for wind speed
void isr_rotation ()   {

  if ((millis() - contactBounceTime) > 15 ) {
    rotations++;
    contactBounceTime = millis();
  }
}

// Get Wind Direction
void getWindDirection() {

  vaneValue = analogRead(WindVane_Pin);
  //  Serial.print(vaneValue);
  //  Serial.println();
  vaneDirection = map(vaneValue, 0, 1023, 0, 360);
  calDirection = vaneDirection + VaneOffset;
  if (calDirection > 360)
    calDirection = calDirection - 360;
  if (calDirection < 0)
    calDirection = calDirection + 360;
  //*********Calcul Moyenne Direction**************
  winDir[readIndexDir] = calDirection;
  readIndexDir = readIndexDir + 1;
  if (readIndexDir == numReadings ) {
    for (readIndexDir = 0; readIndexDir < numReadings; readIndexDir++)
    {
      sinSum += sin((winDir[readIndexDir]) * DEG2RAD);
      cosSum += cos((winDir[readIndexDir]) * DEG2RAD);
    }
    float avgWinDir = atan2(sinSum, cosSum);
    avgWinDir = avgWinDir / DEG2RAD;
    if ( avgWinDir < 0 )
    {
      avgWinDir += 360;
    }
    int windDirection = (int)avgWinDir % 360;
    avg_windDirection = windDirection;
    sinSum = 0;
    cosSum = 0;
    readIndexDir = 0;
  }
}
// Converts compass direction to heading
void getHeading(int direction) {
  if (direction < 22)
    girouette = (" N");
  else if (direction < 67)
    girouette = (" NE");
  else if (direction < 112)
    girouette = (" E");
  else if (direction < 157)
    girouette = (" SE");
  else if (direction < 202)
    girouette = (" S");
  else if (direction < 247)
    girouette = (" SO");
  else if (direction < 292)
    girouette = (" O");
  else if (direction < 337)
    girouette = (" NO");
  else
    girouette = (" N");
}

int winDir[48];

Bonjour,
pour éviter les incidents, mettre à la place
int winDir[numReadings];