Memory limitation Uno Rev3

Aloha,

I would like to create a think that :

  1. Record T° and humidity (bme280) on SD Card on the Ethernet shield
  2. Publish those data through mqtt online
  3. Be able to access to sd file on local ethernet network (Ethernet server)

I’m able to make work each task individually on the board.

When I merge the code to join function 2 and 3 I can’t upload to the board due to memory limitation (110%) . (I think the web server is taking a lot)

I know I can publish data on a local database using mqtt but I do not want that.

I’’m only a newbie and I presume my code is optimized as I get it from examples from the Arduino IDE.

Hardware is : Arduino Uno Rev 3 + Ethernet shield + Bme 280

Arduino Mega are a bit pricey ; feather looks interesting (1MB flash and 256KB SRAM) but the Ethernet FeatherWing can’t run web server :frowning:.

Any idea ?

Thanks.

It might be a benefit to you if you publish your code. Did you use the "F" macro for all print statements? If not I have a feeling that will get you a lot more free RAM. Take a look at one of the ESP32 parts, they are not expensive and have a lot of memory. Does the code actually compile?

Which memory is it running out of, program memory or dynamic memory?
If it's program memory, the only fix would be to reduce the size of your code or use a computer with more program space.

If it's dynamic memory that is running out, you may be able to move any constants into the program memory using PROGMEM and the F() macro that makes strings easy to move.

Both are full :confused: so your proposition looks interesting but unsure it can help...
I will review again the code and post it later.

The esp32 option looks very interesting but two questions :

  1. Esp32 is not feather ? Adafruit ESP32 Feather V2 - 8MB Flash + 2 MB PSRAM [STEMMA QT] : ID 5400 : $19.95 : Adafruit Industries, Unique & fun DIY electronics and kits ? If so the Ethernet wing will not support webserver. Correct ? Adafruit Ethernet FeatherWing : ID 3201 : $19.95 : Adafruit Industries, Unique & fun DIY electronics and kits

  2. If i'm wront, i assume as the hardware will be different i will have to modify library and therefore the code. Correct ?

The feather is not a microcontroller. It's an entire family of development boards all with the same physical size and common USB and battery support features. The rest depends on what microcontroller is on the particular feather board you are using.
There are feather boards with and ESP32 on them, or an Atmega 32u4 or a RP2040...

Hello,

Here is the code a,d the error message, in French but i'm sure you will understand :wink:

Le croquis utilise 35628 octets (110%) de l'espace de stockage de programmes. Le maximum est de 32256 octets.
Les variables globales utilisent 2238 octets (109%) de mémoire dynamique, ce qui laisse -190 octets pour les variables locales. Le maximum est de 2048 octets.
Croquis trop gros; vois https://support.arduino.cc/hc/en-us/articles/360013825179 pour des conseils de réduction.
text section exceeds available space in board

Compilation error: text section exceeds available space in board

/************************* Library ***************************/

#include <SPI.h>
#include "Adafruit_MQTT_Client.h"
#include <Ethernet.h>
#include <Adafruit_BME280.h>
#include <SD.h>    

/************************* Ethernet Client Setup *****************************/

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEC};
IPAddress myDns(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
EthernetServer server(80);

/************************* SD Card *********************************/

Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;

/************************* Adafruit.io Setup *********************************/

#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  1883
#define AIO_USERNAME    "xxxx"
#define AIO_KEY         "xxxxx"

/************ Global State ******************/


EthernetClient client;
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
#define halt(s) { Serial.println(F( s )); while(1);  }

/************************* Input Setup *********************************/

#define adresseI2CduBME280                0x76          
Adafruit_BME280 bme;

/****************************** Feeds ***************************************/

Adafruit_MQTT_Publish publi_temperature = Adafruit_MQTT_Publish(&mqtt,  AIO_USERNAME "/feeds/temperature");
Adafruit_MQTT_Publish publi_humidite = Adafruit_MQTT_Publish(&mqtt,  AIO_USERNAME "/feeds/humidite");

/****************************** Other ***************************************/

#define error(s) error_P(PSTR(s))
#define BUFSIZ 100

void error_P(const char* str) {
  PgmPrint("error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) 
  {
    PgmPrint("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
  while(1);
}

char* strupper( char* s )
// helper function char array to uppercase letters
{
  for (char* p = s; *p; ++p)
    *p = toupper( *p );
  return s;
}

char* strlower( char* s )
// helper function char array to lowercase letters
{
  for (char* p = s; *p; ++p)
    *p = tolower( *p );
  return s;
}

/*------------------------------------ Setup ------------------------------------*/


void setup() 
{
  Serial.begin(115200);
  Serial.println(F("Debut programme"));
  Serial.print(F("\nInit the Client..."));
  Ethernet.begin(mac);
  delay(1000); //give the ethernet a second to initialize
  bme.begin(adresseI2CduBME280);

  PgmPrint("Free RAM: ");
  Serial.println(FreeRam());  
  
  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
  pinMode(10, OUTPUT);                       // set the SS pin as an output (necessary!)
  digitalWrite(10, HIGH);                    // but turn off the W5100 chip!

  if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed!");
  
  // initialize a FAT volume
  if (!volume.init(&card)) error("vol.init failed!");

  PgmPrint("Volume is FAT");
  Serial.println(volume.fatType(),DEC);
  Serial.println();
  
  if (!root.openRoot(&volume)) error("openRoot failed");

  // list file in root with date and size
  PgmPrintln("Files found in root:");
  root.ls(LS_DATE | LS_SIZE);
  Serial.println();
  
  // Recursive list of all directories
  PgmPrintln("Files found in all dirs:");
  root.ls(LS_R);
  
  Serial.println();
  PgmPrintln("Done");

  server.begin();

}

/*------------------------------------ Loop ------------------------------------*/

void loop() 
{
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();

  //Serial.print(F(bme.readTemperature() ));
  //Serial.println("C°");
  //Serial.print(bme.readHumidity());
  //Serial.println("%");
  publi_temperature.publish(bme.readTemperature()) ;
  publi_humidite.publish(bme.readHumidity()) ;

  // ping the server to keep the mqtt connection alive
  if(! mqtt.ping()) 
  {
    mqtt.disconnect();
  }

{ //Loop file server 
  char clientline[BUFSIZ];
  int index = 0;
  
  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean current_line_is_blank = true;
    
    // reset the input buffer
    index = 0;
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        
        // If it isn't a new line, add the character to the buffer
        if (c != '\n' && c != '\r') {
          clientline[index] = c;
          index++;
          // are we too big for the buffer? start tossing out data
          if (index >= BUFSIZ) 
            index = BUFSIZ -1;
          
          // continue to read more data!
          continue;
        }
        
        // got a \n or \r new line, which means the string is done
        clientline[index] = 0;
        
        // Print it out for debugging
        Serial.println(clientline);
        
        // Look for substring such as a request to get the root file
        if (strstr(clientline, "GET / ") != 0) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          
          // print all the files, use a helper to keep it clean
          client.println("<h2>Files:</h2>");
          ListFiles(client, LS_SIZE);
        } else if (strstr(clientline, "GET /") != 0) {
          // this time no space after the /, so a sub-file!
          char *filename;
          
          filename = clientline + 5; // look after the "GET /" (5 chars)
          // a little trick, look for the " HTTP/1.1" string and 
          // turn the first character of the substring into a 0 to clear it out.
          (strstr(clientline, " HTTP"))[0] = 0;
          
          // print the file we want
          Serial.println(filename);

          if (! file.open(&root, filename, O_READ)) {
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println();
            client.println("<h2>File Not Found!</h2>");
            break;
          }
          
          Serial.println("Opened!");
          strlower(filename);          
          client.println("HTTP/1.1 200 OK");
          if (strstr(filename,".htm")!=NULL)
            client.println("Content-Type: text/html");
          else if (strstr(filename,".jpg")!=NULL)
            client.println("Content-Type: image/jpg");
          else  
            client.println("Content-Type: text/plain");
          client.println();
          
          int16_t c;
          while ((c = file.read()) >= 0) {
              // uncomment the serial to debug (slow!)
              //Serial.print((char)c);
              client.print((char)c);
          }
          file.close();
        } else {
          // everything else is a 404
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<h2>File Not Found!</h2>");
        }
        break;
      }
    }
    // give the web browser time to receive the data
    delay(1);
    client.stop();
  }
}

delay(3000);

}



/************************* Void *****************************/



// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.

void MQTT_connect() 
{
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
       Serial.println(mqtt.connectErrorString(ret));
       Serial.println("Retrying MQTT connection in 5 seconds...");
       mqtt.disconnect();
       delay(5000);  // wait 5 seconds
  }
  Serial.println("MQTT Connected!");
}

void ListFiles(EthernetClient client, uint8_t flags) 
{
  // This code is just copied from SdFile.cpp in the SDFat library
  // and tweaked to print to the client output in html!
  dir_t p;
  
  root.rewind();
  client.println("<ul>");
  while (root.readDir(p) > 0) {
    // done if past last used entry
    if (p.name[0] == DIR_NAME_FREE) break;

    // skip deleted entry and entries for . and  ..
    if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') continue;

    // only list subdirectories and files
    if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;

    // print any indent spaces
    client.print("<li><a href=\"");
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print((char)p.name[i]);
    }
    client.print("\">");
    
    // print file name with possible blank fill
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print((char)p.name[i]);
    }
    
    client.print("</a>");
    
    if (DIR_IS_SUBDIR(&p)) {
      client.print('/');
    }

    // print modify date/time if requested
    if (flags & LS_DATE) {
       root.printFatDate(p.lastWriteDate);
       client.print(' ');
       root.printFatTime(p.lastWriteTime);
    }
    // print size if requested
    if (!DIR_IS_SUBDIR(&p) && (flags & LS_SIZE)) {
      client.print(' ');
      client.print(p.fileSize);
    }
    client.println("</li>");
  }
  client.println("</ul>");
}

The AVR microcontrollers are built using a modified Harvard Architecture. This means the Flash Program Memory space is on a separate address bus than the "Static Random Access Memory (SRAM)". There are two data bus, one that can access all data and the In/Out data bus with limited access to a small section of memory.

Many also have EEPROM (electrically erasable programmable read-only memory) is organized as a separate data space, in which single bytes can be read and written. Access from the CPU to EEPROM is done through the EEPROM Address Registers , the EEPROM Data Register , and the EEPROM Control Register.

The reason the "F" macro works is the "C_Start" does not copy the data to RAM upon startup. If it is using the standard print the data is copied to RAM. For an experiment write a simple program that types hello world (Serial.print("Hello world!");. Then compile it and note the ram usage. Then convert the print statement to the "F" macro (Serial.print(F("Hello world!"));. and try again.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.