Go Down

Topic: Problem with Stability due to low memory - how can I optimise code? (Read 472 times) previous topic - next topic

ITregear

Hi,

This is my first time posting, so I apologise if I miss any details. I also have very basic computing knowledge and am fairly new to Arduino in general.

I'm building a Voltmeter/Power Meter/Data-Logger device, and have finished all of the code for it. It uses a micro SD card breakout board, an I2C 0.96 OLED display and an INA219 breakout, as well as some buttons and resistors.

Throughout making it I have continually tested it, and everything works separately. As soon as they are combined I run out of space and the code does indeed upload, but nothing appears to work. If I remove some functionality (e.g: the data logging), the screen comes to life and the code seems to work.

I believe this is because I've run out of memory, as this is the message I receive after compiling:

Quote
Sketch uses 26066 bytes (84%) of program storage space. Maximum is 30720 bytes.
Global variables use 1936 bytes (94%) of dynamic memory, leaving 112 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.
I've tried to optimise the code by removing everything that seems unnecessary, but I don't think I can remove anything else without losing functionality. I'm aware that the libraries take up a lot of space, as well as all of the code that involves writing to the screen.

Here is the code I am using:

Code: [Select]
#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_INA219.h>

const int pinCS = 3;
File myFile;

int voltInput = A0;
float Vout = 0.0;
float Vin = 0.0;
float R1 = 100000.0;
float R2 = 10000.0;
int value = 0;

int resInput = A1;
int raw = 0;
int Rin = 5;
float Rout = 0;
float X1 = 1000;
float X2 = 0;
float buffer1 = 0;

const int numReadings2 = 10;
int readings2[numReadings2];
int readIndex2 = 0;
int total2 = 0;
int average2 = 0;
int resistance;

const int numReadings1 = 10;
int readings1[numReadings1];
int readIndex1 = 0;
int total1 = 0;
int average1 = 0;
int voltage;

int mode = 0;
int dataWrite = 0;
unsigned int fileNumber = 0;
int buttonState1;
int buttonState2;
int buttonState3;
int lastButtonState1;
int lastButtonState2;
int lastButtonState3;
int reading1;
int reading2;
int reading3;
unsigned long lastDebounceTime1 = 0;
unsigned long lastDebounceTime2 = 0;
unsigned long lastDebounceTime3 = 0;
unsigned long debounceDelay = 50;

float shuntVoltage = 0;
float busVoltage = 0;
float current_mA = 0;
float loadVoltage = 0;
float power_mW = 0;

unsigned long Time = 0;
unsigned long previousTime = 0;
unsigned long currentTime = 0;

unsigned long previousMillis = 0;
const long interval = 1000;
unsigned long currentMillis = 0;

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
Adafruit_INA219 ina219;

void setup(){             
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.display();
  ina219.begin();
  pinMode(pinCS, OUTPUT);

  for (int thisReading1 = 0; thisReading1 < numReadings1; thisReading1++) {
    readings1[thisReading1] = 0;
  }
  for (int thisReading2 = 0; thisReading2 < numReadings2; thisReading2++) {
    readings2[thisReading2] = 0;
  }

  if (!SD.begin(pinCS)){
    return;
  }
 
  delay(100);
}

void loop(){

reading1 = digitalRead(6);
if (reading1 != lastButtonState1){
  lastDebounceTime1 = millis();
  }
if ((millis() - lastDebounceTime1) > debounceDelay) {
  if (reading1 != buttonState1) {
    buttonState1 = reading1;
    if (buttonState1 == HIGH) {
    mode++;
      if (mode > 2){
        mode = 0;
     }}}}
   
lastButtonState1 = reading1;

reading2 = digitalRead(5);
if (reading2 != lastButtonState2){
  lastDebounceTime2 = millis();
}
if ((millis() - lastDebounceTime2) > debounceDelay) {
  if (reading2 != buttonState2){
    buttonState2 = reading2;
    if (buttonState2 == HIGH) {
    dataWrite = !dataWrite;
}}}
lastButtonState2 = reading2;

reading3 = digitalRead(7);
if (reading3 != lastButtonState3){
  lastDebounceTime3 = millis();
}
if ((millis() - lastDebounceTime3) > debounceDelay) {
  if (reading3 != buttonState3){
    buttonState3 = reading3;
    if (buttonState3 == HIGH) {
    fileNumber = ++fileNumber;
}}}
lastButtonState3 = reading3;
 
switch (mode) {
  case 0:
  power();
  break;

  case 1:
  volt();
  break;

  case 2:
  ohm();
  break;
}

shuntVoltage = ina219.getShuntVoltage_mV();
busVoltage = ina219.getBusVoltage_V();
current_mA = ina219.getCurrent_mA();
power_mW = ina219.getPower_mW();
loadVoltage = busVoltage + (shuntVoltage / 1000);

String filename = String();
  filename = "Exp_";
  filename += fileNumber;
  filename += ".txt";
  char charFileName[filename.length() + 1];
  filename.toCharArray(charFileName, sizeof(charFileName));

if (dataWrite == HIGH){

  currentMillis = millis();
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
 
    Time++;
     
    myFile = SD.open(charFileName, FILE_WRITE);
    if (myFile){
      myFile.print(shuntVoltage);
      myFile.print(" , ");
      myFile.print(busVoltage);
      myFile.print(" , ");
      myFile.print(current_mA);
      myFile.print(" , ");
      myFile.print(power_mW);
      myFile.print(" , ");
      myFile.print(loadVoltage);
      myFile.print(" , ");
      myFile.println(Time);
      myFile.close();
      }}} else {
      Time = 0;
      }
}

void volt(){
display.clearDisplay();

total1 = total1 - readings1[readIndex1];
readings1[readIndex1] = analogRead(voltInput);
total1 = total1 + readings1[readIndex1];
readIndex1 = readIndex1 + 1;

if (readIndex1 >= numReadings1) {
  readIndex1 = 0;
}
value = total1 / numReadings1;

Vout = (value * 5) / 1024.0;
Vin = Vout / (R2/(R1+R2));
if (Vin < 0.09) {
  Vin = 0.0;
}

display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(36,0);
display.print("VOLTMETER");
display.setTextSize(2);
display.setCursor(38,15);
display.print(Vin);

display.display();

}

void ohm(){
display.clearDisplay();
 
total2 = total2 - readings2[readIndex2];
readings2[readIndex2] = analogRead(resInput);
total2 = total2 + readings2[readIndex2];
readIndex2 = readIndex2 + 1;

if (readIndex2 >= numReadings2) {
  readIndex2 = 0;

  raw = total2 / numReadings2;
 
  buffer1 = raw * Rin;
  Rout = (buffer1)/1024;
  buffer1 = (Rin/Rout) - 1;
  X2 = X1 * buffer1;
  if (X2 > 1000000){
    X2 = 0;
  }
}

display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(36,0);
display.print("OHM-METER");
display.setTextSize(2);
display.setCursor(38,15);
display.print(X2);
display.display();

}


void power(){

display.clearDisplay();     
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(0,0);
display.print("V: ");
display.setCursor(20,0);
display.print(loadVoltage);
display.setCursor(60,0);
display.print(" V");

display.setCursor(0,12);
display.print("I: ");
display.setCursor(20,12);
display.print(current_mA);
display.setCursor(60,12);
display.print("mA");

display.setCursor(0,24);
display.print("P: ");
display.setCursor(20,24);
display.print(power_mW);
display.setCursor(60,24);
display.print("mW");
display.display();

}


Any tips or advice on how to free up some space and get the code working would be greatly appreciated. Thank you!


AWOL

Code: [Select]
  for (int thisReading1 = 0; thisReading1 < numReadings1; thisReading1++) {
    readings1[thisReading1] = 0;
  }
  for (int thisReading2 = 0; thisReading2 < numReadings2; thisReading2++) {
    readings2[thisReading2] = 0;
  }
They're already zero. crt0 sees to that.
You can grab back a few bytes of RAM by using the F macro for your prints, at the expense of a little more flash.
A lot of variables could have smaller scope, but overall, that may not save all that much.
"Pete, it's a fool (who) looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

Unsigned_Arduino

  First of all...

 I would like to congratulate you for using that much flash. That is a lot of code.

 But anyway,

 If some variables are constant, for example:

Code: [Select]
int ledPin = 13;

 Change them to:

Code: [Select]
const int ledPin = 13;

 If your range of variables is under a byte, for example:

Code: [Select]
const int ledPin = 13;

 Change it to:

Code: [Select]
const byte ledPin = 13;

And if possible, use local variables.
Do not touch the String class.

Ever.

ITregear

Thank you for the quick replies,

I tried to use the F() macro for all of the prints I could, but that only freed up another 50ish byes, leaving me with 156 for local variables, and 1892 used (92%) by global variables. This still doesn't work.

How many bytes need to be free to allow to the code to function? And is it possible to use the F() macro around print commands that have variables within them, without causing problems?

E.g: I know this can be done:

Code: [Select]
display.print(F("VOLTMETER"));

But could this be done?

Code: [Select]
display.print(F(loadVoltage));

Thanks

AWOL

Quote
But could this be done?
You already tried it, right?
"Pete, it's a fool (who) looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

AWOL

If the OLED library has a full frame buffer, that could be a big chunk of RAM. Even 128x64 monochrome is 1024 bytes, which may not be necessary.
You could write your own simpler library, and lose the frame buffer (assuming there is one).

And ditch the String class. (That shouldn't need to be said)
"Pete, it's a fool (who) looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

DrAzzy

All the places where you read from digital pins, you store the result in an int. You can gain a byte each by switching to byte.

Because you are using String class, it is hard to predict how much extra memory is needed; Get rid of the String class and build the file name in a fixed length character array. Losing String will also save you some flash.

Also - can you rework your math to not use floats? Usually you can - and in that case, you'll save some more flash (floating point math is all software implementations), and a little bit of ram from switching to smaller datatypes.
ATtiny core for 841+1634+828 and x313/x4/x5/x61/x7/x8 series Board Manager:
http://drazzy.com/package_drazzy.com_index.json
ATtiny breakouts (some assembled), mosfets and awesome prototyping board in my store http://tindie.com/stores/DrAzzy

ITregear

Thank you for the replies all,

I solved it and am just posting in case it is useful for others.

I simply used the SdFat library instead of the SD library which required very minor changes to the code and gave me around 10% more memory - enough to work.

Cheers

Unsigned_Arduino

Do not touch the String class.

Ever.

hammy

You have an aweful lot of variables -  if reading analog inputs "int" is sufficient and then do your maths in integers , that saves space.
I have for example read an analog voltage (?0-1023) representing 0-5v ;  Work with the 1023 instead of generating 5.0volts.  You will never get better than 1 part in 1024 , so using float for a R value or mA would then be pointless as you get no more resolution.

Const int R =1000


Go Up