Hello again,
I’m in the process of testing this approach and currently am unaware of any unwanted side effects this might introduce. I’m aware of heap fragmentation and pretty much the concepts of optimizing an Arduino program based on what I’ve read here: Optimizing SRAM | Memories of an Arduino | Adafruit Learning System
So I basically took the “Think Globally, Allocate Locally” thought to the next level and rather than instantiate library variables in global scope, I’ve moved them to their own functions and with their library return type.
As an example:
#include <Wire.h>
#include <Adafruit_RGBLCDShield.h>
#include <Adafruit_MCP23017.h>
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
void setup()
{
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Loading...");
}
void loop()
{
}
// Binary sketch size: 4,776 bytes (used 15% of a 32,256 byte maximum) (0.71 secs)
// Minimum Memory Usage: 273 bytes (13% of a 2048 byte maximum)
Now compare this to the following:
#include <Wire.h>
#include <Adafruit_RGBLCDShield.h>
#include <Adafruit_MCP23017.h>
// Experimental self-disposing pattern (avoids declaring global variables permanently in SRAM)
Adafruit_RGBLCDShield getLcd()
{
const uint8_t
LCD_COLS = 16,
LCD_ROWS = 2;
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
lcd.begin(LCD_COLS, LCD_ROWS);
return lcd;
}
void setup()
{
Adafruit_RGBLCDShield lcd = getLcd();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Loading...");
}
void loop()
{
}
// Binary sketch size: 4,828 bytes (used 15% of a 32,256 byte maximum) (0.75 secs)
// Minimum Memory Usage: 245 bytes (12% of a 2048 byte maximum)
Here is a more complete context:
#include <Wire.h>
#include <Adafruit_NFCShield_I2C.h>
#include <Adafruit_RGBLCDShield.h>
#include <Adafruit_MCP23017.h>
// Experimental self-disposing pattern (avoids declaring global variables permanently in SRAM)
Adafruit_RGBLCDShield getLcd()
{
const uint8_t
LCD_COLS = 16,
LCD_ROWS = 2;
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
lcd.begin(LCD_COLS, LCD_ROWS);
return lcd;
}
Adafruit_NFCShield_I2C getNfc()
{
const uint8_t
IRQ = 2,
RESET = 3,
MAX_TRIES = 3;
const uint16_t RETRY_DELAY = 1000;
Adafruit_NFCShield_I2C nfc(IRQ, RESET);
for (uint8_t i = 1; i < MAX_TRIES; i++)
{
if (nfc.getFirmwareVersion())
{
nfc.SAMConfig();
break;
}
delay(RETRY_DELAY);
}
return nfc;
}
void lcdPrint(const char *str)
{
Adafruit_RGBLCDShield lcd = getLcd();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(str);
}
void scanTag(uint8_t *dst, uint8_t len)
{
// ...
}
void setup()
{
lcdPrint("Loading!");
}
void loop()
{
}
// Binary sketch size: 5,636 bytes (used 17% of a 32,256 byte maximum) (0.73 secs)
// Minimum Memory Usage: 360 bytes (18% of a 2048 byte maximum)
This follows more of a “single responsibility” approach to coding but it seems this is really the way we should be using these huge libraries when instantiating their variables that take up so much space at boot and are left in SRAM forever even when not in use (SdFat anyone?). If local variables are truly 100% reclaimed when the function exits, self-disposing seems like an added incentive for simple yet optimal code. I’m curious to know what the side effects might be other than slower performance perhaps?
Your thoughts?