#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