Midi controller freezes after sitting for 6 to 8 hours



#include <MIDI.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # ((was 4) or -1 if sharing Arduino reset pin)
#define OLED_ADDR 0x3C  // address of my 128x64 OLED
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// error blink codes
#define ERR_DISP_ALLOC 3 // display allocation error


static const unsigned ledPin = LED_BUILTIN; // use onboard LED as activity indicator
static const byte switchPin[] = {2,3,4,5,6,7}; // pins for footswitch inputs
static const byte switchCount = 6; // number of footswitches used
static bool switchPressed[switchCount]; // current state of footswitches
static bool switchLastState[switchCount]; //previous state of footswitches (used for long press detection)
static unsigned long lastPressMillis[switchCount]; // when the last button press was detected
static unsigned long lastReleaseMillis[switchCount]; // when the last button was released

// Created and binds the MIDI interface to the default hardware Serial port
MIDI_CREATE_DEFAULT_INSTANCE();


void errBlink(int errCode) {
  byte blinkTime = 200; // duration for each blink
  byte blinkGap = 200; // gap between each blink
  int burstWait = 1000; // wait time between bursts 
  for (;;) { // loop forever
    for (int i = 1; i <= errCode; i++) {
      digitalWrite(ledPin,HIGH);
      delay(blinkTime);
      digitalWrite(ledPin,LOW);
      delay(blinkGap);
    }
    delay(burstWait);
  }
} // end of errBlink()

bool buttonState1 = false; // Example for button 1
bool buttonState2 = false; // Example for button 2
bool buttonState3 = false; // Example for button 3
bool buttonState4 = false; // Example for button 4
bool buttonState5 = false; // Example for button 5
bool buttonState6 = false; // Example for button 6
bool buttonStates[4] = {LOW, LOW, LOW, LOW}; // Current state of each button
bool ccStates[4] = {LOW, LOW, LOW, LOW}; // CC state (on/off) for each button

void setup() {
  pinMode(ledPin, OUTPUT);  // setup activity LED pin for output

  MIDI.begin(MIDI_CHANNEL_OMNI);  // Listen to all incoming messages

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
 if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) { // Address 0x3C for my 128x64 variant
   errBlink(ERR_DISP_ALLOC);
  }

  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  
 delay(500);

  display.setTextSize(3);      // Normal 1:1 pixel scale
  display.setTextColor(WHITE); // DraW white text
  display.setCursor(0,10);     // Start at top-left corner

  display.println(" TEST");
  display.cp437(true);         // Use full 256 char 'Code Page 437' font

  display.display();

  delay(500);
 
  // Initialise switches and related variable arrays
  for (int i=0;i<switchCount;i++) { 
    pinMode(switchPin[i], INPUT_PULLUP); // add pullup resistors to all footswitch input pins
    switchPressed[i] = false; //initialise switch state
    switchLastState[i] = false; //initialse last switch state
    lastReleaseMillis[i] = millis(); // initialise time switch last released
    lastPressMillis[i] = lastPressMillis[i] -1; // initialise time switch last pressed
  }

} // end of setup


void loop() {
  readButtons();
   
  midiSend();
} // end of loop

/*
 * ----------------------------------------
 * 
 * Input related declarations and functions
 * 
 * ----------------------------------------
 */


static const bool switchDown = LOW; // because we are using pullup resistor and switch to GND
static const byte debounceTime = 230; // after processing a button press, ignore further input for some milliseconds
static const byte debounceDelay = 3; // amount of time to wait before retesting debounceTime
static const int longPressTime = 2; // how long a switch has to be held to count as a long press
static int switchPressedCounter = 0; // how many switches are currently pressed
static byte nextCommand = -1; // most important pending action - the switch that was last pressed, or other command via multi or long press
static byte lastCommand = -1; // last command sent (used for display confirmation)
static unsigned long commandMillis = millis(); // the time that nextCommand was last set - ie the last switch to be pressed
static const byte pagePatchReset = 5*switchCount + 3; 
static const byte pageUp = 5*switchCount + 2; 
static const byte pageDn = 5*switchCount + 1;

static byte currentPage = 0; // the current page / bank to be displayed
static const byte pageCount =2; // how many pages we have configured

void readButtons() {
  switchPressedCounter = 0;
  for (int i=0;i<switchCount;i++) {
    switchPressed[i] = ( digitalRead(switchPin[i]) == switchDown ); // set array element to true if switch is currently pressed, or false if not
    if (switchPressed[i] != switchLastState[i]) { //potential press or release detected
      if (switchPressed[i]) { // potential new press detected
        if ( millis() > (lastPressMillis[i] + debounceTime) ) { // genuine press and not switch bounce
          lastPressMillis[i] = millis();
          switchLastState[i] = true;
          nextCommand = i;
          commandMillis = millis();
          
        }
      }
      else if (!switchPressed[i]) { //potential switch release detected
        if ( millis() > (lastReleaseMillis[i] + debounceTime ) ) { // genuine release and not switch bounce
          lastReleaseMillis[i] = millis();
          switchLastState[i] = false;
        }
      }
    }
    if (switchPressed[i]) {
      switchPressedCounter++;  //increment counter used to check multiple presses
      
      if (  millis() > (lastPressMillis[i] + longPressTime)  ) { // long press detected
        lastPressMillis[i] = millis(); // reset timer so it doesn't re-trigger every loop
        nextCommand = i + switchCount; // use the next n numbers as a second bank of commands representing long press actions
        
      }
    }
  }
   static bool comboActive = false; // remembers whether multiple presses were detected to avoid re-triggering every loop
  if (switchPressedCounter > 1 ) { // multiple presses detected
    if (!comboActive) {
      displayButtonPress(" BANK");
      comboActive = true;
      if ( switchPressed[2] && switchPressed[4]) { // first two switches -> Page Down
        nextCommand = pageDn;
        changePageDown();
        }
      else if ( switchPressed[3] && switchPressed[5]) { // second two switches -> Page Up
        nextCommand = pageUp;
        changePageUp();
        }
      
      }
    }
  else {
    comboActive = false; // we can reset this as no more than one switch currently pressed
  }
  lastCommand = nextCommand;
} // end of read_buttons()

void changePageUp() {
  currentPage++;
  if (currentPage >= pageCount) { // we have gone past the last page
    currentPage = 0; // reset to first page
  }
}

void changePageDown() {
  currentPage--;
  if (currentPage > pageCount) { // we have scrolled back past the first page
    currentPage = (pageCount -1); // reset to last page
  }
}




/*
 * 
 * Display related functions
 * 
 */

void displayButtonPress(const char* message) {
  display.clearDisplay();
  display.setCursor(0,20);
  display.println(message);
  display.display();
}
void invSelection(int i=(lastCommand+1)) {
  if (lastCommand == i) {
    display.setTextColor(BLACK, WHITE);
  }
  else {
    display.setTextColor(WHITE, BLACK);
  }
}


void displayLine(const __FlashStringHelper *str0, const __FlashStringHelper *str1, const __FlashStringHelper *str2, const __FlashStringHelper *str3, int startBtn) {
display.print(F(""));
invSelection(0+startBtn);
display.print(str0);
invSelection();
display.print(F(""));
invSelection(1+startBtn);
display.print(str1);
invSelection();
display.print(F(""));
invSelection(2+startBtn);
display.print(str2);
invSelection();
display.print(F(""));
invSelection(3+startBtn);
display.print(str3);
invSelection();
display.println(F(""));
}



/*
 * 
 * MIDI output related functions
 * 
 */


void midiSend() {
  // do something
  if (nextCommand >=0) {
    if (nextCommand == pagePatchReset) { // SW7 & SW8 should reset page and patch to 0 regardless of which page/patch currently active
      MIDI.sendControlChange(47,0,1);
    }
    switch(currentPage) {
      case 0: // menu page 0 (1 of 1)
       switch(nextCommand) {
        case 0:
        displayButtonPress(" ");
          MIDI.sendControlChange(47, 0, 1); 
          break;
          
        case 2:
        displayButtonPress(" ");
          MIDI.sendControlChange(47, 1, 1); 
          break;
          
        case 1:
        displayButtonPress(" ");
      if (!buttonState4) {
       MIDI.sendControlChange(46, 127, 1); //(toggle on)
       buttonState4 = true;
     } else {
        displayButtonPress(" ");
       MIDI.sendControlChange(46, 0, 1); //(toggle off)
       buttonState4 = false;
     }
     break;
     
        case 3:
        displayButtonPress(" ");
          MIDI.sendControlChange(47, 2, 1); 
          break;

        case 4:
        displayButtonPress("");
          MIDI.sendControlChange(44, 2, 1); 
          break;   

         case 5:
        displayButtonPress(" ");
      if (!buttonState4) {
       MIDI.sendControlChange(45, 127, 1); //(toggle on)
       buttonState4 = true;
     } else {
        displayButtonPress(" ");
       MIDI.sendControlChange(45, 0, 1); //(toggle off)
       buttonState4 = false;
     }
     break;
         
          } // end of menu page 0
        break;

        case 1: // menu page 1 (1 of 2)
       switch(nextCommand) {
        case 0:
        displayButtonPress(" A");
          MIDI.sendControlChange(44, 0, 1); 
          break;
          
        case 1:
        displayButtonPress(" B");
          MIDI.sendControlChange(44, 1, 1); 
          break;
          
       case 2:
        displayButtonPress(" C");
          MIDI.sendControlChange(44, 2, 1); 
          break;
     
        case 3:
        displayButtonPress(" D");
          MIDI.sendControlChange(44, 3, 1); 
          break;

        case 4:
        displayButtonPress(" E");
          MIDI.sendControlChange(44, 4, 1); 
          break;   

         case 5:
        displayButtonPress(" F");
          MIDI.sendControlChange(44, 5, 1); 
          break;
         
          } // end of menu page 1
         break;

    } // end of outer nested switch case

      
    nextCommand = -1; // re-initialise this so we don't send the same message repeatedly
  }
 } // end midisend

Does this overflow 3.9 times?

Not sure what you mean

Think about the largest value you can put in a byte. Hint: 1000 is way more than that.

So what would be a more likely number

Perfect I’ll work on that thank you

a de-bounce of 50 to 100 ms is already quite a lot, but if you want to use a larger value you should put it in a larger variable.
For the rest your code is not easy to read. Please correct the formatting using the code tag

would that cause the arduino to stop working though?

Overflow of a variable holding "millis()" is known to cause problems when the variable is the wrong type (like; int). "1000" in a byte type variable is 232 with the 'v' flag set. I do not know if that makes it negative to the Arduino.

What about the Longpress time being 600 is that an issue?

as long as the code looks like the way it does in this thread i can not tell.

It does not. byte is unsigned so it can hold only non-negative values.

not really sure what it is you need me to change

looks a lot better now..

Ok, well in your code you define a lot of variables as static, but since these are global variables to begin with, this means nothing. The compiler will have ignored this. A static variable is a local variable that holds it's value even though the function has finished. No harm done there.

GPIO pin numbers are defined in the core as 'int' so you may as well stick to that. Mind you as a byte they are also within the variable range. Again no harm done.

This

if ( millis() > (lastPressMillis[i] + debounceTime) ) { // genuine press and not switch bounce

is not the right way to calculate an elapsed time passing. Like this it will roll-over incorrectly.
it should be

if ( millis() - lastPressMillis[i]  > debounceTime ) { // genuine press and not switch bounce

current time - recorded time = elapsed time. Like that it will roll-over and still work just fine. Of course a 32-bit millis() variable rolls over after 50 days, so that doesn't account for your issue.

Actually i haven't found anything that would account for your issue. There are 2 possible causes that i can think of. Either memory fragmentation, but you do not use anything that could fragment, so i have to conclude this is not the case. Which leaves the possibility of a hardware issue. If you draw 3.3v for the screen from the Arduino 3.3v , that may be a bit much for the regulator of the Arduino, which may overheat and switch itself off momentarily.
This is what i did to your code, but it shouldn't make any difference unfortunately.

#include <MIDI.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # ((was 4) or -1 if sharing Arduino reset pin)
#define OLED_ADDR 0x3C  // address of my 128x64 OLED
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// error blink codes
#define ERR_DISP_ALLOC 3 // display allocation error


const int ledPin = LED_BUILTIN; // use onboard LED as activity indicator
const int switchPin[] = {2, 3, 4, 5, 6, 7}; // pins for footswitch inputs
const byte switchCount = 6; // number of footswitches used
bool switchPressed[switchCount]; // current state of footswitches
bool switchLastState[switchCount]; //previous state of footswitches (used for long press detection)
unsigned long lastPressMillis[switchCount]; // when the last button press was detected
unsigned long lastReleaseMillis[switchCount]; // when the last button was released

const bool switchDown = LOW; // because we are using pullup resistor and switch to GND
const byte debounceTime = 230; // after processing a button press, ignore further input for some milliseconds
const byte debounceDelay = 3; // amount of time to wait before retesting debounceTime
const int longPressTime = 2; // how long a switch has to be held to count as a long press
int switchPressedCounter = 0; // how many switches are currently pressed
byte nextCommand = -1; // most important pending action - the switch that was last pressed, or other command via multi or long press
byte lastCommand = -1; // last command sent (used for display confirmation)
unsigned long commandMillis = millis(); // the time that nextCommand was last set - ie the last switch to be pressed
const byte pagePatchReset = 5 * switchCount + 3;
const byte pageUp = 5 * switchCount + 2;
const byte pageDn = 5 * switchCount + 1;
byte currentPage = 0; // the current page / bank to be displayed
const byte pageCount = 2; // how many pages we have configured

// Created and binds the MIDI interface to the default hardware Serial port
MIDI_CREATE_DEFAULT_INSTANCE();

void errBlink(int errCode) {
  uint8_t blinkTime = 200; // duration for each blink
  uint8_t  blinkGap = 200; // gap between each blink
  uint16_t burstWait = 1000; // wait time between 
  for (;;) { // loop forever
    for (int i = 1; i <= errCode; i++) {
      digitalWrite(ledPin, HIGH);
      delay(blinkTime);
      digitalWrite(ledPin, LOW);
      delay(blinkGap);
    }
    delay(burstWait);
  }
} // end of errBlink()

bool buttonState1 = false; // Example for button 1
bool buttonState2 = false; // Example for button 2
bool buttonState3 = false; // Example for button 3
bool buttonState4 = false; // Example for button 4
bool buttonState5 = false; // Example for button 5
bool buttonState6 = false; // Example for button 6
/*int buttonStates[4] = {LOW, LOW, LOW, LOW}; // Current state of each button
int ccStates[4] = {LOW, LOW, LOW, LOW}; // CC state (on/off) for each button*/

void setup() {
  pinMode(ledPin, OUTPUT);  // setup activity LED pin for output
  MIDI.begin(MIDI_CHANNEL_OMNI);  // Listen to all incoming messages
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) { // Address 0x3C for my 128x64 variant
    errBlink(ERR_DISP_ALLOC);
  }
  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(500);
  display.setTextSize(3);      // Normal 1:1 pixel scale
  display.setTextColor(WHITE); // DraW white text
  display.setCursor(0, 10);    // Start at top-left corner
  display.println(" TEST");
  display.cp437(true);         // Use full 256 char 'Code Page 437' font
  display.display();
  delay(500);

  // Initialise switches and related variable arrays
  for (int i = 0; i < switchCount; i++) {
    pinMode(switchPin[i], INPUT_PULLUP); // add pullup resistors to all footswitch input pins
    switchPressed[i] = false; //initialise switch state
    switchLastState[i] = false; //initialse last switch state
    lastReleaseMillis[i] = millis(); // initialise time switch last released
    lastPressMillis[i] = lastPressMillis[i] - 1; // initialise time switch last pressed
  }

} // end of setup


void loop() {
  readButtons();
  midiSend();
} // end of loop

void readButtons() {
  static bool comboActive = false; // remembers whether multiple presses were detected to avoid re-triggering every loop
  switchPressedCounter = 0;
  for (int i = 0; i < switchCount; i++) {
    switchPressed[i] = (digitalRead(switchPin[i]) == switchDown); // set array element to true if switch is currently pressed, or false if not
    if (switchPressed[i] != switchLastState[i]) { //potential press or release detected
      if (switchPressed[i]) { // potential new press detected
        if (millis() -lastPressMillis[i] > debounceTime) { // genuine press and not switch bounce
          lastPressMillis[i] = millis();
          switchLastState[i] = true;
          nextCommand = i;
          commandMillis = millis();
        }
      }
      else if (!switchPressed[i]) { //potential switch release detected
        if ( millis()- lastReleaseMillis[i] > debounceTime) { // genuine release and not switch bounce
          lastReleaseMillis[i] = millis();
          switchLastState[i] = false;
        }
      }
    }
    if (switchPressed[i]) {
      switchPressedCounter++;  //increment counter used to check multiple presses
      if (millis() - lastPressMillis[i] > longPressTime) { // long press detected
        lastPressMillis[i] = millis(); // reset timer so it doesn't re-trigger every loop
        nextCommand = i + switchCount; // use the next n numbers as a second bank of commands representing long press actions
      }
    }
  }
  
  if (switchPressedCounter > 1 ) { // multiple presses detected
    if (!comboActive) {
      displayButtonPress(" BANK");
      comboActive = true;
      if ( switchPressed[2] && switchPressed[4]) { // first two switches -> Page Down
        nextCommand = pageDn;
        changePageDown();
      }
      else if ( switchPressed[3] && switchPressed[5]) { // second two switches -> Page Up
        nextCommand = pageUp;
        changePageUp();
      }
    }
  }
  else {
    comboActive = false; // we can reset this as no more than one switch currently pressed
  }
  lastCommand = nextCommand;
} // end of read_buttons()

void changePageUp() {
  currentPage++;
  if (currentPage >= pageCount) { // we have gone past the last page
    currentPage = 0; // reset to first page
  }
}

void changePageDown() {
  currentPage--;
  if (currentPage > pageCount) { // we have scrolled back past the first page
    currentPage = (pageCount - 1); // reset to last page
  }
}

void displayButtonPress(const char* message) {
  display.clearDisplay();
  display.setCursor(0, 20);
  display.println(message);
  display.display();
}
void invSelection(int i = (lastCommand + 1)) {
  if (lastCommand == i) {
    display.setTextColor(BLACK, WHITE);
  }
  else {
    display.setTextColor(WHITE, BLACK);
  }
}

void displayLine(const __FlashStringHelper *str0, const __FlashStringHelper *str1, const __FlashStringHelper *str2, const __FlashStringHelper *str3, int startBtn) {
  display.print(F(""));
  invSelection(0 + startBtn);
  display.print(str0);
  invSelection();
  display.print(F(""));
  invSelection(1 + startBtn);
  display.print(str1);
  invSelection();
  display.print(F(""));
  invSelection(2 + startBtn);
  display.print(str2);
  invSelection();
  display.print(F(""));
  invSelection(3 + startBtn);
  display.print(str3);
  invSelection();
  display.println(F(""));
}

void midiSend() {
  // do something
  if (nextCommand >= 0) {
    if (nextCommand == pagePatchReset) { // SW7 & SW8 should reset page and patch to 0 regardless of which page/patch currently active
      MIDI.sendControlChange(47, 0, 1);
    }
    switch (currentPage) {
      case 0: // menu page 0 (1 of 1)
        switch (nextCommand) {
          case 0:
            displayButtonPress(" ");
            MIDI.sendControlChange(47, 0, 1);
            break;

          case 2:
            displayButtonPress(" ");
            MIDI.sendControlChange(47, 1, 1);
            break;

          case 1:
            displayButtonPress(" ");
            if (!buttonState4) {
              MIDI.sendControlChange(46, 127, 1); //(toggle on)
              buttonState4 = true;
            } else {
              displayButtonPress(" ");
              MIDI.sendControlChange(46, 0, 1); //(toggle off)
              buttonState4 = false;
            }
            break;

          case 3:
            displayButtonPress(" ");
            MIDI.sendControlChange(47, 2, 1);
            break;

          case 4:
            displayButtonPress("");
            MIDI.sendControlChange(44, 2, 1);
            break;

          case 5:
            displayButtonPress(" ");
            if (!buttonState4) {
              MIDI.sendControlChange(45, 127, 1); //(toggle on)
              buttonState4 = true;
            } else {
              displayButtonPress(" ");
              MIDI.sendControlChange(45, 0, 1); //(toggle off)
              buttonState4 = false;
            }
            break;

        } // end of menu page 0
        break;

      case 1: // menu page 1 (1 of 2)
        switch (nextCommand) {
          case 0:
            displayButtonPress(" A");
            MIDI.sendControlChange(44, 0, 1);
            break;

          case 1:
            displayButtonPress(" B");
            MIDI.sendControlChange(44, 1, 1);
            break;

          case 2:
            displayButtonPress(" C");
            MIDI.sendControlChange(44, 2, 1);
            break;

          case 3:
            displayButtonPress(" D");
            MIDI.sendControlChange(44, 3, 1);
            break;

          case 4:
            displayButtonPress(" E");
            MIDI.sendControlChange(44, 4, 1);
            break;

          case 5:
            displayButtonPress(" F");
            MIDI.sendControlChange(44, 5, 1);
            break;

        } // end of menu page 1
        break;

    } // end of outer nested switch case
    nextCommand = -1; // re-initialise this so we don't send the same message repeatedly
  }
} // end midisend
type or paste code here

edit:
These variables should be of an unsigned type, they will be treated as such regardless because they are cast into an unsigned value by delay() anyway. It is anyway just the error function.
so

  uint8_t blinkTime = 200; // duration for each blink
  uint8_t  blinkGap = 200; // gap between each blink
  uint16_t burstWait = 1000; // wait time between bursts

I tend to use the unix type definitions because they make it clearer what is actually their size and if they are signed (the 'u' specifies the size, and the number of bits is represented by the number.

I appreciate all the help .. I think the overflow on the de life was the main issue . Although I will look deeper at all the suggestions.. My pedal has now been running for about 20 straight hours with no issues.. Thank you