Possible memory conflict with OLED?

Hey everyone - I have an issue that is stumping me.

I have a ATMega328P project that uses a 128x64 OLED display. I am using the Adafruit_GFX and Adafruit_SSD1306 libraries for the display functions.

The main loop displays information, and I have another function that displays a menu. My program uses 63% of program space, and 42% of dynamic memory.

The problem I am having is that when I add code to the menu loop beyond a certain point, I start getting glitches in the lower right corner of the display while in the main loop. The size of the glitch is directly related to the amount of code added to the loop. The loop uses a switch statement to display a specific page of the menu. Right now my program has 20 menu pages. If I add a page 21, the glitch begins. If I comment out page 20, or 10 for example, the glitch goes away. That's why I say it doesn't matter what the code is - it is the size that matters. Once I hit a certain point, the glitch appears. It is like the program code is getting in a memory location that is used by one of the libraries to hold the content of the display.

The glitch is a series of changing pixels in a rectangular section of the lower right corner. I have attached a screenshot, but the actual glitch is a random moving series of pixels. One interesting fact is that it does not show the glitch in the menu mode.

Is there anyway to get some sort of debug output that shows what section of memory a library uses? Or someway to force my program to be stored in a different section of the memory?

I would appreciate any suggestions on how to debug this issue.
glitch

Here is a snippet of the switch statement that draws the menu. All the menu pages are similar to this.

void drawMenu(){
  
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(2);  
    switch(menuPage){
      case 0:
        display.setCursor(10,10);
        display.print("START/STOP");
        display.setCursor(10,25);
        display.print("PASS THRU");
        dispNext(40);
        display.setCursor(10,55);
        display.print("EXIT");
        break;
      case 1:
      case 6:
      case 16:
        display.setCursor(10,10);
        display.print("CHANNEL 1");
        display.setCursor(10,25);
        display.print("CHANNEL 2");
        display.setCursor(10,40);
        display.print("CHANNEL 3");
        display.setCursor(10,55);
        display.print("BACK");
        break;

A snippet isn't going to help.

This sounds like an array misbehaving somewhere, resulting in a memory location being overwritten, and it happens to be part of the memory that makes the display map. The problem can either be in the display routines/library (somewhat unlikely) or (more likely) you have a problem with an array going out of bounds elsewhere in your code. Apart from an array issue it can also be something more complex such as wrong use of a memory pointer or a problem with malloc(), but an out-of-bounds array operation is the more likely cause.

1 Like

Seems to be the day for this. See also this thread.

1 Like

Some display libraries make data buffers out of your available ram. You could be overwriting a display data buffer.

1 Like

Here is the complete code. I really don't use any arrays. All it does is listen to the serial port and make changes to the display based on messages it receives. The only array used is in the recvBytesWithStartEndMarkers() function which is used to process the incoming serial data. Thanks for taking the time to comment.

In this code, I just copied case 20 into case 21 and it causes the glitch. If I delete case 21, there is no glitch.

/**************************************************************************
Twin Midi Serial Display
 **************************************************************************/

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Fonts/Org_01.h>
#include <ReceiveOnlySoftwareSerial.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define debugByte 0x10
#define div1Byte 0x11
#define div2Byte 0x12
#define div3Byte 0x13
#define bpmByte  0x14
#define div1PlayByte 0x15
#define div2PlayByte 0x16
#define div3PlayByte 0x17
#define div1StopByte 0x18
#define div2StopByte 0x19
#define div3StopByte 0x20
#define intExtClockByte 0x21
#define menuModeByte 0x22
#define encoderDirByte 0x23
#define encoderPushByte 0x24
#define menuPageByte 0x25
#define menuCursorByte 0x26
#define div1FilterByte 0x27
#define div2FilterByte 0x28
#define div3FilterByte 0x29
#define div1PlaySyncByte 0x30
#define div2PlaySyncByte 0x31
#define div3PlaySyncByte 0x32
#define div1BeatSyncByte 0x33
#define div2BeatSyncByte 0x34
#define div3BeatSyncByte 0x35
#define settingsSavedByte 0x36
#define eurModeByte 0x37 
#define eurOnButtonByte 0x38

#define eur16_8_4 0x00
#define eur16_8_8tr 0x01
#define eurDivide 0x02
#define eurConstant 0x00
#define eurButton 0x01



bool debugMode = false;
bool firstBoot = true;
bool newPlayRecieved = false;
const byte numBytes = 64;
byte receivedBytes[numBytes];
unsigned int long newChangeOneReceivedMS = 0;
unsigned int long newChangeTwoReceivedMS = 0;
unsigned int long newChangeThreeReceivedMS = 0;
unsigned int long newPlayPulse = 500;
byte numReceived = 0;

int debugData = 1;

boolean newData = false;
boolean intClock = true;
boolean menuMode = false;
boolean encoderPush = false;

int div1 = 20;
int div2 = 20;
int div3 = 20;
bool channelOnePlayStatus = false;
bool channelTwoPlayStatus = false;
bool channelThreePlayStatus = false;
int bpm = 128;

long encPos = 0;
int menuPage = 0;
int menuCursor = 0;

int numMenuPages = 19;
int numMenuItems = 4;


byte inByte = 0x00;

bool div1Filter = false;
bool div2Filter = false;
bool div3Filter = false;

bool div1PlaySync = false;
bool div2PlaySync = false;
bool div3PlaySync = false;

bool div1BeatSync = false;
bool div2BeatSync = false;
bool div3BeatSync = false;

int eurOnButton = eurConstant; 
int eurMode = eur16_8_4;        

void setup() {
  //delay(1500);   
  pinMode(9,INPUT);
  pinMode(10, INPUT);
  pinMode(11, INPUT);
  Serial.begin(115200);

  display.setFont(&Org_01);
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.display();

  display.clearDisplay();
  display.display();
  drawDisplayBackground();
  updateDisplay();
  menuMode = false;
}

void loop() {
  recvBytesWithStartEndMarkers();
  processNewData();
  if (menuMode == false) updateDisplay();
  if (menuMode == true)  drawMenu();
  
}

void drawDisplayBackground(){
  display.clearDisplay();
  display.fillRect(0, 0, 83, 15, WHITE);
  display.fillRect(85, 0, 43, 15, WHITE);
  display.drawFastVLine(39,15,49,WHITE);
  display.drawFastVLine(40,15,49,WHITE);
  display.drawFastVLine(83,15,49,WHITE);
  display.drawFastVLine(84,15,49,WHITE);
  display.drawFastHLine(0,40,128,WHITE);
  display.drawFastHLine(0,41,128,WHITE);
  display.display();
}
bool itemBool(int x){
  switch(x){
    case 2:
      return div1Filter;
    case 3:
      return div2Filter;
    case 4:
      return div3Filter;
    case 7:
      return div1PlaySync;
    case 8:
      return div2PlaySync;
    case 9:
      return div3PlaySync;     
    case 17:
      return div1BeatSync;
    case 18:
      return div2BeatSync;
    case 19:
      return div3BeatSync;         
  }
}

void drawMenu(){
  
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(2);  
    switch(menuPage){
      case 0:
        display.setCursor(10,10);
        display.print("START/STOP");
        display.setCursor(10,25);
        display.print("PASS THRU");
        dispNext(40);
        display.setCursor(10,55);
        display.print("EXIT");
        break;
      case 1:
      case 6:
      case 16:
        display.setCursor(10,10);
        display.print("CHANNEL 1");
        display.setCursor(10,25);
        display.print("CHANNEL 2");
        display.setCursor(10,40);
        display.print("CHANNEL 3");
        display.setCursor(10,55);
        display.print("BACK");
        break;
      case 2: case 3: case 4: case 7: case 8: case 9: case 17: case 18: case 19:
        display.setCursor(10,10);
        if (itemBool(menuPage)==true)
          display.print("ON *");
        else display.print("ON");
        display.setCursor(10,25);
        if (itemBool(menuPage)==false)
          display.print("OFF *");
        else display.print("OFF");          
        display.setCursor(10,55);
        display.print("BACK");
        break;
      case 5:
        display.setCursor(10,10);
        display.print("QUANTIZE");
        dispNext(40);
        display.setCursor(10,55);
        display.print("BACK");
        break;
      case 10:
       display.setCursor(10,10);
        display.print("ANALOG OUT");
        display.setCursor(10,25);
        display.print(" CLK DIV");
        dispNext(40);
        display.setCursor(10,55);
        display.print("EXIT");
        break;                
      case 11:
        display.setCursor(10,10);
        if (eurMode == eur16_8_4)
          display.print("16-8-4 *");
        else display.print("16-8-4");
        
        display.setCursor(10,25);
        if (eurMode == eur16_8_8tr)
          display.print("16-8trip-8 *");
        else display.print("16-8trip-8");        
        
        display.setCursor(10,40);
        if (eurMode == eurDivide)
          display.print("DIVIDE *");
        else display.print("DIVIDE");          
        
        display.setCursor(10,55);
        display.print("BACK");
        break;
        
      case 12:
       display.setCursor(10,10);
        display.print("ANALOG OUT");
        display.setCursor(10,25);
        display.print("CLK MODE");
        dispNext(40);
        display.setCursor(10,55);
        display.print("EXIT");
        break;
      case 13:
        display.setCursor(10,10);
        display.print("BUTTON");
        if (eurOnButton==eurButton)   
          display.print(" *");
        display.setCursor(10,25);
        display.print("CONSTANT");
        if (eurOnButton==eurConstant)   
          display.print("*");
        display.setCursor(10,55);
        display.print("BACK");
        break;
      case 15:
        display.setCursor(10,10);
        display.print("BEATSYNC");
        dispNext(40);        
        display.setCursor(10,55);
        display.print("BACK");
        break;
      case 20:
        display.setCursor(10,10);
        display.print("SAVE");
        display.setCursor(10,25);
        display.print("SETTINGS?");
        display.setCursor(10,40);
        display.print("-SAVE");
        dispNext(55);
        break;
      case 21:
        display.setCursor(10,10);
        display.print("SAVE");
        display.setCursor(10,25);
        display.print("SETTINGS?");
        display.setCursor(10,40);
        display.print("-SAVE");
        dispNext(55);
        break;        
       default:
       break;
    }
  display.fillRect(1, (2 + (15*menuCursor)), 8, 8, WHITE);
  display.display(); 
}
void dispNext(int x){
  display.setCursor(10,x);
  display.print("NEXT");
}
void savedMessage()
{
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(2);
  display.setCursor(10,30);
  display.print("Saved");
  display.display();
  delay(1000);
}

void updateDisplay(){
  display.clearDisplay();
  display.fillRect(0, 0, 83, 15, WHITE);
  display.fillRect(85, 0, 43, 15, WHITE);
  display.drawFastVLine(39,15,49,WHITE);
  display.drawFastVLine(40,15,49,WHITE);
  display.drawFastVLine(83,15,49,WHITE);
  display.drawFastVLine(84,15,49,WHITE);
  display.drawFastHLine(0,40,128,WHITE);
  display.drawFastHLine(0,41,128,WHITE);  
    display.setTextColor(WHITE); 
    display.setTextSize(2);
    display.fillRect(0,17,37,22, BLACK);
    display.setCursor(8,28); 
    display.print(div1);

    if (div1Filter == true) {
      display.setTextSize(1);
      display.setCursor(32,20);
      display.print('S');
    }

    if (div1PlaySync == true) {
      display.setTextSize(1);
      display.setCursor(32,28);
      display.print('Q');
    }  
    
    if (div1BeatSync == true) {
      display.setTextSize(1);
      display.setCursor(32,36);
      display.print('B');
    }       

    display.fillRect(42,17,39,22, BLACK);
    display.setTextSize(2);
    display.setCursor(56,28); 
    display.print(div2);

    if (div2Filter == true) {
      display.setTextSize(1);
      display.setCursor(76,20);
      display.print('S');
    }    

    if (div2PlaySync == true) {
      display.setTextSize(1);
      display.setCursor(76,28);
      display.print('Q');
    }
    if (div2BeatSync == true) {
      display.setTextSize(1);
      display.setCursor(76,36);
      display.print('B');
    }     
    
    display.fillRect(86,17,39,22, BLACK);
    display.setTextSize(2);
    display.setCursor(102,28); 
    display.print(div3);

        if (div3Filter == true) {
      display.setTextSize(1);
      display.setCursor(122,20);
      display.print('S');
    }

    if (div3PlaySync == true) {
      display.setTextSize(1);
      display.setCursor(122,28);
      display.print('Q');
    }  
    if (div3BeatSync == true) {
      display.setTextSize(1);
      display.setCursor(122,36);
      display.print('B');
    }       

    display.fillRect(0, 0, 83, 15, WHITE);
    display.setTextColor(BLACK);   
    display.setTextSize(1);
    display.setCursor(16,8);//4
    if (intClock) {
      display.print("INTERNAL"); 
    }
    else {
      display.print("EXTERNAL");
    }
    if (debugMode==true) {
      display.setCursor(0,8);
      display.print(debugData);
    }
    
    if (channelOnePlayStatus) {
      if (millis() < (newChangeOneReceivedMS + newPlayPulse)){
        display.fillRect(0, 42, 37, 21, BLACK);
        display.fillTriangle(9, 44, 9, 60, 27, 52, WHITE); //play
      }
      else { 
        display.fillRect(0, 42, 37, 21, BLACK);;
        display.fillTriangle(12, 47, 12, 57, 24, 52, WHITE);
        }
    }
    else   {
      if (millis() < (newChangeOneReceivedMS + newPlayPulse)){
        display.fillRect(0, 42, 37, 21, BLACK); //Stop 
        display.fillRect(9, 44, 15, 15, WHITE);  //stop
      }
      else {
        display.fillRect(0, 42, 37, 21, BLACK); //Stop 
        display.fillRect(11, 46, 12, 12, WHITE);  //stop        
        
      }
    }

    if (channelTwoPlayStatus) {
      if (millis() < (newChangeTwoReceivedMS + newPlayPulse)){
        display.fillRect(41, 42, 40, 21, BLACK);
        display.fillTriangle(54, 44, 54, 60, 72, 52, WHITE); //play
      }
      else { 
        display.fillRect(41, 42, 40, 21, BLACK);
        display.fillTriangle(56, 47, 56, 57, 69, 52, WHITE);
        }
    }
    else   {
      if (millis() < (newChangeTwoReceivedMS + newPlayPulse)){
        display.fillRect(41, 42, 40, 21, BLACK);
        display.fillRect(54, 44, 15, 15, WHITE);  //stop
      }
      else {
        display.fillRect(41, 42, 40, 21, BLACK); //Stop 
        display.fillRect(54, 46, 12, 12, WHITE);  //stop  
        
      }
    }
    //CHANNEL THREE
    if (channelThreePlayStatus) {
      if (millis() < (newChangeThreeReceivedMS + newPlayPulse)){
        display.fillRect(85, 42, 40, 21, BLACK);
        display.fillTriangle(97, 44, 97, 60, 116, 52, WHITE); //play
      }
      else { 
        display.fillRect(85, 42, 40, 21, BLACK);
        display.fillTriangle(99, 47, 99, 57, 112, 52, WHITE);
        }
    }
    else   {
      if (millis() < (newChangeThreeReceivedMS + newPlayPulse)){
        display.fillRect(85, 42, 40, 21, BLACK);
        display.fillRect(95, 44, 15, 15, WHITE);  //stop
      }
      else {
        display.fillRect(85, 42, 40, 21, BLACK); //Stop 
        display.fillRect(97, 46, 12, 12, WHITE);  //stop  
        
      }
    }
    display.fillRect(85, 0, 43, 15, WHITE);
    display.setTextColor(BLACK); 
    display.setCursor(90,10);//85,4
    display.setTextSize(2);
    display.print(bpm);
             
    display.display();
}

void recvBytesWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    byte startMarker = 0x3C;
    byte endMarker = 0x3E;
    byte rb;
   

    while (Serial.available() > 0 && newData == false) {
        rb = Serial.read();

        if (recvInProgress == true) {
            if (rb != endMarker) {
                receivedBytes[ndx] = rb;
                ndx++;
                if (ndx >= numBytes) {
                    ndx = numBytes - 1;
                }
            }
            else {
                receivedBytes[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                numReceived = ndx;  // save the number for use when printing
                ndx = 0;
                newData = true;
            }
        }

        else if (rb == startMarker) {
            recvInProgress = true;
        }
    }
}
void processNewData() {
    if (newData == true) {
      switch(receivedBytes[0]){
        case debugByte:
            debugData = int(receivedBytes[1]);
            break;
        case div1Byte:
            div1 = int(receivedBytes[1]);
            break;
        case div2Byte:
            div2 = int(receivedBytes[1]);
            break;
        case div3Byte:
            div3 = int(receivedBytes[1]);
            break;
        case div1PlayByte:
            if ( int(receivedBytes[1]) == 0) {
              channelOnePlayStatus = false;
              newChangeOneReceivedMS = millis();
            }
            if ( int(receivedBytes[1]) == 1) {
              channelOnePlayStatus = true;
              newChangeOneReceivedMS = millis();
            }
            break;   
        case div2PlayByte:
            if ( int(receivedBytes[1]) == 0) {
              channelTwoPlayStatus = false;
              newChangeTwoReceivedMS = millis();
            }
            if ( int(receivedBytes[1]) == 1) {
              channelTwoPlayStatus = true;
              //newPlayReceived = true;
              newChangeTwoReceivedMS = millis();
            }
            break;   
        case div3PlayByte:
            if ( int(receivedBytes[1]) == 0) {
              channelThreePlayStatus = false;
              newChangeThreeReceivedMS = millis();
            }
            if ( int(receivedBytes[1]) == 1) {
              channelThreePlayStatus = true;
              //newPlayReceived = true;
              newChangeThreeReceivedMS = millis();
            }
            break;
        case intExtClockByte:
            if ( int(receivedBytes[1]) == 0) intClock = false;
            if ( int(receivedBytes[1]) == 1) intClock = true;
            break;          
                                           
        case bpmByte:
            bpm = int(receivedBytes[1]);
            break; 
        
        case menuModeByte:
            if ( int(receivedBytes[1]) == 0) {
              menuMode = false;
              debugData = 128;
            }
            if ( int(receivedBytes[1]) == 1) {
              menuCursor = 0;
              menuMode = true;
            }
            break;  
        case menuCursorByte:
            menuCursor = (int (receivedBytes[1]));
            break;
        case menuPageByte:
            menuPage = (int (receivedBytes[1]));
            break;            
        case encoderPushByte:
            if (int(receivedBytes[1] == 1)) encoderPush = true;
            break;
        case div1FilterByte:
            if (int(receivedBytes[1] == 0)) div1Filter = false;
            if (int(receivedBytes[1] == 1)) div1Filter = true;
            break;
        case div2FilterByte:
            if (int(receivedBytes[1] == 0)) div2Filter = false;
            if (int(receivedBytes[1] == 1)) div2Filter = true;
            break;
         case div3FilterByte:
            if (int(receivedBytes[1] == 0)) div3Filter = false;
            if (int(receivedBytes[1] == 1)) div3Filter = true;
            break;
        case div1PlaySyncByte:
            if (int(receivedBytes[1] == 0)) div1PlaySync = false;
            if (int(receivedBytes[1] == 1)) div1PlaySync = true;
            break;
        case div2PlaySyncByte:
            if (int(receivedBytes[1] == 0)) div2PlaySync = false;
            if (int(receivedBytes[1] == 1)) div2PlaySync = true;
            break;
         case div3PlaySyncByte:
            if (int(receivedBytes[1] == 0)) div3PlaySync = false;
            if (int(receivedBytes[1] == 1)) div3PlaySync = true;
            break;  
        case div1BeatSyncByte:
            if (int(receivedBytes[1] == 0)) div1BeatSync = false;
            if (int(receivedBytes[1] == 1)) div1BeatSync = true;
            break;
        case div2BeatSyncByte:
            if (int(receivedBytes[1] == 0)) div2BeatSync = false;
            if (int(receivedBytes[1] == 1)) div2BeatSync = true;
            break;
         case div3BeatSyncByte:
            if (int(receivedBytes[1] == 0)) div3BeatSync = false;
            if (int(receivedBytes[1] == 1)) div3BeatSync = true;
            break; 
         case settingsSavedByte:
            debugData = int(receivedBytes[1]);
            savedMessage();
            break;
         case eurModeByte:
            eurMode = int(receivedBytes[1]); 
            break;  
         case eurOnButtonByte:
            eurOnButton = int(receivedBytes[1]); 
            break;                              
      }
        newData = false;

        
    }
}

Yes, by the time you have allocated 1024 bytes for the runtime buffer it means 92% of dynamic memory.

Replace every display.print("anonymous string"); with display.print(F("anonymous string"));
Ditto for display.println("string");

This should give you a safety margin.

David.

David, thanks for the explanation! I'll try that right away.
Justin

David,

Thanks, that cut the dynamic size down to 29%.

Justin