Skip to content

Arduino Heap Space Workaround

I thought this GPS project would be done quickly, but found myself scratching my head for days on a weird issue. I still don’t know exactly what caused the problem, but I found a work around by moving some data to heap memory instead of using the stack. I can only guess that there is some memory stomping going on with the Adafruit SSD1306 library, but I don’t see anything obviously wrong with their source code.

My working source code is found here: https://github.com/benjohnemmett/GpsDisplay

The Project

The project is a simple DIY GPS made of an Arduino Uno, Beitian BN-220 GPS module, and an SSD1306 OLED display (AliExpress Affiliate links). I have already done Arduino-based projects that work with each component individually, so combining them seemed straightforward.

The Problem

However, I found that the code which parses the GPS data would not work after calling the begin() function for the SSD1306 display. Parsing would work immediately before calling begin(), but not after. After ruling out other alternatives, I believe that the string data was getting corrupted somehow by the begin() function.

The Solution

After poking at the problem periodically for days I took a shot it the dark and moved the GPS string data from the stack to the heap which solved the issue.

Original Stack-Based GPS Data Code

void setup() {
  ...
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  ...
  String gps_string = new String(Serial.readStringUntil('\n')); // String on the stack
  bool is_synchronized = gpsIsSynchronized(gps_string);
  while(!is_synchronized) {                                     // Never exits loop
    gps_string = String(Serial.readStringUntil('\n'));
    is_synchronized = gpsIsSynchronized(gps_string);
  }
  ...
}

Updated Heap-Based GPS Data Code

String *gps_string;

void setup() {
  ...
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  ...
  gps_string = new String(Serial.readStringUntil('\n')); // String on the heap
  bool is_synchronized = gpsIsSynchronized(gps_string);
  while(!is_synchronized) {                              // Works as expected!
    delete gps_string;
    gps_string = new String(Serial.readStringUntil('\n'));
    is_synchronized = gpsIsSynchronized(gps_string);
  }
  delete gps_string;
  ...
}

Other Notes & Investigations

Could it be that display.begin() takes too much time, causing Serial input buffer overrun and corrupt the data?

No, I was able to print out each line received by GPS and also print out the result of the call to the stringIsGgaData() function. I viewed all of this data in the Arduino IDE serial monitor. I could see that each line was being read correctly correctly. Also, each line was returning 0 (False) for stringIsGgaData(), even when the line started with “$GNGGA”.

Could it be a bug in the stringIsGgaData() function?

Nope. First of all the function worked fine until I added in code for the SSD1306. Also, I tested the function with a static string and found that it worked correctly. It even worked fine with the GPS data when it was called before the SSD1306 begin() function.

Maybe it has to do with the newline or carriage return sneaking in front of the “$GNGGA” header.

Nope, I added a call to the trim() method which would remove any whitespace, newline, or carriage return characters from the beginning and end of the gps data string and the problem persisted.

Sanity Check With other display

Sanity confirmed. I switched out the SSD1306 for an LCD1602 display with an I2C backpack and was quickly able to get GPS data to parse and display correctly. This narrowed my search down to the SSD1306 library specifically.

Leave a Reply

Your email address will not be published. Required fields are marked *