Hi all,
I've built a MIDI floorboard using an Mega2560 Rev3. I'm using WIN10 and the latest Ardunio IDE. Everything works fine, apart from the names that I give to certain presets. Somehow they get displayed incorrectly and the error seems to be somehow correlated with speed of the script. I noticed the names get displayed correctly when adding a long Serial.print function right at the beginning of my void loop(), which slows the responsiveness of the device down so much, it basically becomes unusable. If I remove the Serial.print code lines, the pedal is super responsive but the issue returns.
I'll post all my code (it's somewhat comprehensive) because the issue might be somewhere I do not anticipate it.
Basically, there is a struct list called Presets that has all the names and MIDI values for the 10 sound Presets, and then, there is another struct list that contains all the amplifier presets with the names and their respective MIDI patch values.
One amp preset is called "Death", but the HD44780 (i2c'd) as well as the SerialMonitor write "Deathw". or "Deatho". Sometimes, the preset "SRV" gets misspelled as "SRVi" or "SRV(". And if all the amplifier presets are spelled correctly, the first effect preset called "obZen" is misspelled "ooZen".
Things I have tried that didn't fix the issue
- several lengths of names ("deathhhh" - gets misspelled "deathohh")
- different definitions of char sizes
- various USB cables
- various USB ports
- powering the floorboard over either USB or 9V or both
- various baud rates (MIDI only really works with one though but SerialMonitor has options)
- various delays in the void setup and / or void loop
- asking ChatGPT for help (which suggested null termination and displaying individual characters but couldn't reeally help
- asking my wife for female guidance
- coming back to it after a while
This is the serial monitor's output when the amlifier is misspelled
18:39:20.476 ->
18:39:20.476 -> 0 3 1
18:39:20.476 -> 06: Mirage
18:39:20.509 -> Deathw
18:39:20.509 -> print(int num)
18:39:20.541 -> print(long num,int digits)
18:39:20.541 ->
Here's the Serial monitor's output when I tell it to display all the characters of the preset individually... the effects preset becomes misspelled
19:02:56.180 ->
19:02:56.180 -> 0 3 1
19:02:56.180 -> Original ampPreset[activeAmp].name: Death
19:02:56.180 -> Character at index 0: D
19:02:56.180 -> Character at index 1: e
19:02:56.180 -> Character at index 2: a
19:02:56.180 -> Character at index 3: t
19:02:56.180 -> Character at index 4: h
19:02:56.180 -> Character at index 5:
19:02:56.180 -> Character at index 6:
19:02:56.213 -> Character at index 7: (here's a symbol missing - basically a square box)
19:02:56.213 -> 01: o�Zen
19:02:56.213 -> Death
19:02:56.213 -> print(int num)
19:02:56.213 -> print(long num,int digits)
19:02:56.247 ->
19:02:56.247 -> 0 3 1
Here's the device (just so you have a pic)
And all my code (the screensaver logic is in another tab, but it does not affect the error).
#include <ArduinoTapTempo.h>
#include <Arduino.h>
#include <MIDI.h>
#include <Wire.h> // not surte I really needf it, but meh...
#include <hd44780.h> // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header
//#include <serialMIDI.h> // not sure I need it
#include <EmSevenSegment.h>
#include <EEPROM.h>
EmSevenSegment disp(3, 'A', 4, 3, 2);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial3, MIDI3); // MIDI INPUT PORT
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); // MIDI OUTPUT PORT
MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, MIDI2); // MIDI OUTPUT CHECK LED PORT
hd44780_I2Cexp lcd; // declare lcd object: auto locate & auto config expander chip
ArduinoTapTempo tapTempo;
const int LCD_COLS = 20;
const int LCD_ROWS = 4;
const int brightness(A0);
const int led15(LED_BUILTIN);
const int numLed = 14;
const int numSw = 16;
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
const int led[] = { 52, 38, 40, 42, 26, 28, 30, 32, 34, 36, 44, 46, 48, 50 };
const int sw[] = { 27, 29, 31, 33, 39, 41, 43, 45, 47, 49, 35, 37, 25, 53, 23, 51 };
// 0 1 2 3 4 5 6 7 Cap Dry HOT TAP MID Rnd Dth Jzz
byte stateLed[numLed] = { 0 };
byte stateSw[numSw] = { 1 };
byte lastStateSw[numSw] = { 0 };
struct PresetList {
char name[21];
byte timeline, space, heavy, mid;
};
PresetList preset[] = {
{ "01: obZen", 0, 0, 1, 64 },
{ "02: Psycho Shred", 29, 22, 2, 127 },
{ "03: Third Eye Open", 22, 9, 2, 127 },
{ "04: Subtle Lead Rvb", 0, 16, 2, 127 },
{ "05: Master Reverb", 0, 19, 3, 64 },
{ "06: Mirage", 27, 1, 3, 127 },
{ "07: Dream Machine", 10, 21, 3, 127 },
{ "08: BLACKHOLE", 0, 5, 3, 127 },
{ "09: Random", 0, 0, 0, 64 },
{ "10: DRY", 0, 0, 3, 64 }
};
//
unsigned long previousMillis2 = 0, previousBpmMillis = 0, previousMillis = 0, previousMillis4 = 0, previousMillis5 = 0, previousMillis6 = 0;
int stateLcdWash = 0;
int long interval = 500;
int long timeout = 1800000; // 1h time before screensaver turns on
const int timelineCh = 1; // MIDI Channel for Timeline pedal
const int spaceCh = 2; // MIDI Channel for Space pedal
const int heavyCh = 3; // MIDI Channel for Heavy pedal
const int iridiumCh = 4; // MIDI Channel for Iridium pedal
const int capistanCh = 3; // MIDI CHannel for El Capistan pedal
const int elCapToggle = 10;
const int elCapBypass = 11;
const int midCC = 15; // CC for IR Mid control
const int timelineBypassCC = 102;
const int timelineBypassValue = 0;
const int timelineActiveValue = 127;
const int spaceBypassCC = 2;
const int spaceBypassValue = 127;
// const int spaceActiveValue = not needed, as next preset loads as active
int tap = 0, tap2 = 0, tap3 = 0, tap4 = 0, tap5 = 0;
const int timelineTapCC = 93;
const int spaceTapCC = 100;
const int tapDown = 127; // Tap pedal Pressed = 127
const int spaceHotSwitchCC = 101;
bool midBoost = 0;
int mid = 0;
int normLevel = 64;
int bpm = 0;
int rcvMid = 0;
byte activePreset;
byte activeAmp;
bool stateMenu = 0;
int stateScreenSaver = 0;
bool lastStateMidBoost = 0;
// Tml Banks Tml Preset Spc Preset Hvy Ch IR Preset Tml Active Spc Active
long randNumber, randNumber1, randNumber2, randNumber3, randNumber4, randNumber5, randNumber6;
struct AmpPresets {
char name[8];
byte value;
};
AmpPresets ampPreset[] = {
{"Death ", 0},
{"Jazz ", 1},
{"SRV ", 2},
{"Fusion ", 3},
{"JCM800 ", 4}
};
void setup() {
delay(2000);
MIDI.begin(MIDI_CHANNEL_OMNI), MIDI2.begin(MIDI_CHANNEL_OMNI), MIDI3.begin(MIDI_CHANNEL_OMNI);
Serial.begin(9600);
analogWrite(brightness, 200);
delay(10);
pinMode (18, OUTPUT);
int status;
status = lcd.begin(LCD_COLS, LCD_ROWS);
if (status) // non zero status means it was unsuccesful
{ hd44780::fatalError(status); }
for (int i = 0; i < numLed; i++) {
pinMode(led[i], OUTPUT);
}
pinMode(brightness, OUTPUT);
for (int i = 0; i <= numSw; i++) {
pinMode(sw[i], INPUT_PULLUP);
}
// LED FUNCTION CHECK ON STARTUP - Check LEDs
for (int i = 0; i < 14; i++) {
digitalWrite(led[i], 1);
}
disp.print("888");
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 20; col++) {
lcd.setCursor(col, row);
lcd.write(255); // Display character 255
}
}
delay(3000);
activeAmp = EEPROM.read(0);
activeAmp = constrain(activeAmp, 0, sizeof(ampPreset) - 1);
bpm = EEPROM.read(1);
tapTempo.setBPM(bpm);
MIDI.sendProgramChange(ampPreset[EEPROM.read(0)].value, iridiumCh); // activating last used preset
for (int i = 0; i < 14; i++) {
digitalWrite(led[i], 0);
}
lcdWash();
lcd.clear();
activePreset = EEPROM.read(2); // can be set to 9 alternatively
stateLed[activePreset] = !stateLed[activePreset];
stateLed[10] = 0;
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 2);
lcd.print(" ");
lcd.setCursor(14, 3);
lcd.print(" ");
MIDI.sendProgramChange(preset[activePreset].heavy, heavyCh);
if (preset[activePreset].timeline == 0) {
MIDI.sendControlChange(timelineBypassCC, timelineBypassValue, timelineCh); //bypass Timeline if value is == 0
}
if (preset[activePreset].space == 0) {
MIDI.sendControlChange(spaceBypassCC, spaceBypassValue, spaceCh); // bypass Space if value is == 0
}
if (preset[activePreset].timeline >= 1) {
MIDI.sendProgramChange(preset[activePreset].timeline - 1, timelineCh); // load Preset timelinePreset Timeline
MIDI.sendControlChange(timelineBypassCC, timelineActiveValue, timelineCh); // activate Preset on Timeline
}
delay(10);
if (preset[activePreset].space >= 1) {
MIDI.sendProgramChange(preset[activePreset].space - 1, spaceCh); // loadPreset spacePreset2 on Space (loading different preset also activates it)
}
long tapInit = tapTempo.getBeatLength();
MIDI.sendControlChange(timelineTapCC, tapDown, timelineCh); // TAP TEMPO TIMELINE
MIDI.sendControlChange(spaceTapCC, tapDown, spaceCh); // TAP TEMPO SPACE
MIDI.sendControlChange(timelineTapCC, tapDown, heavyCh);
delay(tapInit);
MIDI.sendControlChange(timelineTapCC, tapDown, timelineCh); // TAP TEMPO TIMELINE
MIDI.sendControlChange(spaceTapCC, tapDown, spaceCh); // TAP TEMPO SPACE
MIDI.sendControlChange(timelineTapCC, tapDown, heavyCh);
delay(tapInit);
MIDI.sendControlChange(timelineTapCC, tapDown, timelineCh); // TAP TEMPO TIMELINE
MIDI.sendControlChange(spaceTapCC, tapDown, spaceCh); // TAP TEMPO SPACE
MIDI.sendControlChange(timelineTapCC, tapDown, heavyCh);
}
void loop() {
Serial.println(preset[activePreset].name);
Serial.println(ampPreset[activeAmp].name);
unsigned long currentMillis = millis(), currentMillis2 = millis(), currentMillis3 = millis(), currentMillis4 = millis(), currentMillis5 = millis();
// display layout
lcd.setCursor(0, 0); lcd.print(preset[activePreset].name);
lcd.setCursor(0, 1); lcd.print("Tml1-"); lcd.print(preset[activePreset].timeline);
lcd.setCursor(0, 2); lcd.print("Spc2-"); lcd.print(preset[activePreset].space);
lcd.setCursor(0, 3); lcd.print("Hvy3-"); lcd.print(preset[activePreset].heavy);
lcd.setCursor(9, 1); lcd.print("Amp-"); lcd.print(ampPreset[activeAmp].name);
lcd.setCursor(9, 2); lcd.print("Mid-"); lcd.print(midBoost);
lcd.setCursor(9, 3); lcd.print("Lvl-"); lcd.print(mid);
randNumber = random(2); // choose between 0 - 1 (Timeline Banks A-B)
randNumber1 = random(127); // choose between 1 - 126 (Timeline Presets)
randNumber2 = random(100); // choose betweet 1 - 99 (Space Presets)
randNumber3 = random(1, 4); // choose between 1 - 3 (Heavy Channels)
randNumber4 = random(2); // choose between 0 - 1 (IR Preset)
randNumber5 = random(100); // choose between 0 - 99 (toggle Timeline Preset in/active - threshold in code below)
randNumber6 = random(100); // choose between 0 - 99 (toggle Space Preset in/active - threshold in code below)
// --- THIS IS WHERE THE MAGIC HAPPENS!! ---
for (int i = 8; i < 16; i++) { // cycles through the number of NON-PRESET switches specified in array
if (i == 9){
continue;
}
stateSw[i] = digitalRead(sw[i]); // assigns states to digitalRead value
if (!stateSw[i] == 0) {
}
}
for (int i = 0; i < 10; i++) { // cycles through all PRESET SWITCHES
stateSw[i] = digitalRead(sw[i]); // assigns states to digitalRead value
if (i == 8){
continue;
}
if (stateSw[i] == 0) { // if pressed INPUT_PULLUP
activePreset = i; // the number (array) of the pressed switch is stored as activePreset
}
}
for (int i = 0; i < 14; i++) { //cycles through ALL leds (scecified in array)
digitalWrite(led[i], stateLed[i]); // assings digitalWrite to the respective state
}
for (int i = 0; i < 10; i++) { // cycles through all PresetSwitchLEDs
if (i == 8){
continue;
}
if (i == activePreset) { // if it arrives at the one selected
continue; // skip the one
}
else { // if != seleted (all other LEDs)
stateLed[i] = 0; // state0 LOW
}
}
interval = tapTempo.getBeatLength();
boolean buttonDown = (stateSw[11]) == LOW;
tapTempo.update(buttonDown);
bpm = tapTempo.getBPM();
stateLed[12] = tapTempo.onBeat();
disp.print(bpm);
// MIDI OUT LED
if (MIDI2.read() || MIDI3.read()) // Is there a MIDI message incoming ?
{
previousMillis2 = currentMillis2;
if (currentMillis2 - previousMillis2 <= 50) {
stateLed[13] = 1;
stateLed[14] = 1;
}
}
if (currentMillis2 - previousMillis2 > 50) {
stateLed[13] = 0;
stateLed[14] = 0;
}
// PRESET SWITCH LOGIC 1-7 and 9
if (!stateSw[activePreset]) {
if (stateSw[activePreset] == LOW && stateLed[activePreset] == 0) {
stateLed[activePreset] = !stateLed[activePreset];
stateLed[10] = 0;
stateLed[8] = 0;
MIDI.sendProgramChange(elCapBypass, capistanCh);
if (preset[activePreset].timeline == 0) {
MIDI.sendControlChange(timelineBypassCC, timelineBypassValue, timelineCh); //bypass Timeline if value is == 0
}
if (preset[activePreset].space == 0) {
MIDI.sendControlChange(spaceBypassCC, spaceBypassValue, spaceCh); // bypass Space if value is == 0
}
if (preset[activePreset].timeline >= 1) {
MIDI.sendProgramChange(preset[activePreset].timeline - 1, timelineCh); // load Preset timelinePreset Timeline
MIDI.sendControlChange(timelineBypassCC, timelineActiveValue, timelineCh); // activate Preset on Timeline
}
delay(10);
if (preset[activePreset].space >= 1) {
MIDI.sendProgramChange(preset[activePreset].space - 1, spaceCh); // loadPreset spacePreset2 on Space (loading different preset also activates it)
}
if (midBoost == 1) {
MIDI.sendControlChange(midCC, (preset[activePreset].mid), iridiumCh);
mid = (preset[activePreset].mid);
}
//delay(40);
MIDI.sendProgramChange(preset[activePreset].heavy, heavyCh);
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 2);
lcd.print(" ");
lcd.setCursor(14, 3);
lcd.print(" ");
EEPROM.write(2, activePreset);
previousMillis4 = currentMillis4;
}
}
// ElCapSwitchLogic sw8
if (stateSw[8] == LOW) {
tap5++; // increases value while button is pressed
} else {
tap5 = 0; // resets value if button is releases
}
if (tap5 == 1) {
MIDI.sendProgramChange(elCapToggle, capistanCh); // send PG10 to Ch4 (el cap and Heavy)
delay(20);
stateLed[8] = !stateLed[8];
}
// SWITCH 10 // HOT SW FOOTSWITCH
if (stateSw[10] == LOW) {
tap2++; // increases value while button is pressed
} else {
tap2 = 0; // resets value if button is releases
}
if (tap2 == 1) {
MIDI.sendControlChange(spaceHotSwitchCC, 127, spaceCh); // send CC101 value 127 channel 3
delay(20);
stateLed[10] = !stateLed[10];
MIDI.sendControlChange(spaceHotSwitchCC, 0, spaceCh); // send CC101 value 0 channel3
}
// SWITCH 11 // TAP Footswitch
if (stateSw[11] == LOW) {
stateLed[11] = 1;
tap++; // increases value while button is pressed
delay(10);
} else {
stateLed[11] = 0;
delay(10);
tap = 0; // resets value if button is releases
}
if (tap == 1) {
MIDI.sendControlChange(timelineTapCC, tapDown, timelineCh); // TAP TEMPO TIMELINE
MIDI.sendControlChange(spaceTapCC, tapDown, spaceCh); // TAP TEMPO SPACE
MIDI.sendControlChange(timelineTapCC, tapDown, heavyCh);
EEPROM.write(1, bpm);
}
lastStateSw[11] = stateSw[11];
// SWITCH 12 MID BOOST SWITCH ACTIVE CODE
if (stateSw[12] != lastStateSw[12]) {
if (stateSw[12] == LOW) {
midBoost = !midBoost;
}
}
lastStateSw[12] = stateSw[12];
if (!stateSw[12] && midBoost == 1) {
MIDI.sendControlChange(midCC, preset[activePreset].mid, iridiumCh);
mid = preset[activePreset].mid;
}
if (!stateSw[12] && midBoost == 0) {
MIDI.sendProgramChange(ampPreset[EEPROM.read(0)].value, iridiumCh);
lcd.setCursor(13, 3);
lcd.write(" ");
mid = 0;
}
// SWITCH 13 Menu Button
if (stateSw[13] != lastStateSw[13]) {
if (stateSw[13] == LOW) {
activePreset = 8;
MIDI.sendControlChange(0, randNumber, timelineCh);
MIDI.sendProgramChange(randNumber1, timelineCh);
MIDI.sendProgramChange(randNumber2, spaceCh);
MIDI.sendProgramChange(randNumber3, heavyCh);
MIDI.sendProgramChange(randNumber4, iridiumCh);
preset[8].timeline = randNumber1;
preset[8].space = randNumber2;
preset[8].heavy = randNumber3;
MIDI.sendProgramChange(randNumber4, iridiumCh);
if (randNumber5 >= 75) {
MIDI.sendControlChange(timelineBypassCC, timelineBypassValue, timelineCh);
}
if (randNumber5 < 75) {
MIDI.sendControlChange(timelineBypassCC, timelineActiveValue, timelineCh);
}
if (randNumber6 >= 80) {
MIDI.sendControlChange(spaceBypassCC, spaceBypassValue, spaceCh);
}
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 2);
lcd.print(" ");
lcd.setCursor(14, 3);
lcd.print(" ");
}
}
lastStateSw[13] = stateSw[13];
// SWITCH 14 AMP SELECT
if (stateSw[14] != lastStateSw[14]) {
if (stateSw[14] == LOW) {
if (activeAmp < sizeof(ampPreset) / sizeof(ampPreset[0])) {
activeAmp++;
}
if (activeAmp == sizeof(ampPreset) / sizeof(ampPreset[0])) {
activeAmp = 0;
}
delay(20);
}
}
lastStateSw[14] = stateSw[14];
// SWITCH 15 AMP SEND
if (stateSw[15] != lastStateSw[15]) {
if (stateSw[15] == LOW) {
MIDI.sendProgramChange(ampPreset[activeAmp].value, iridiumCh);
EEPROM.write(0, ampPreset[activeAmp].value);
delay(20);
}
}
lastStateSw[15] = stateSw[15];
if (stateLcdWash == 1 && currentMillis4 - previousMillis4 > 50) {
lcdWash();
}
// SCREENSAVER LOGIC
if (currentMillis5 - previousMillis5 > timeout) {
stateScreenSaver = 1;
} else {
stateScreenSaver = 0;
}
while (stateScreenSaver == 1) {
unsigned long currentMillis6 = millis();
if (currentMillis6 - previousMillis6 > 30){
lcd.setCursor(random(21), random(4));
lcd.print(random(2));
previousMillis6 = currentMillis6;
}
for (int i = 0; i < 14; i++) {
digitalWrite(led[i], 0);
}
for (int i = 0; i < 16; i++) {
if (digitalRead(sw[i]) == 0) {
stateScreenSaver = 0;
stateLcdWash = 1;
}
}
}
for (int i = 0; i < 16; i++) {
if (digitalRead(sw[i]) == 0) {
previousMillis5 = currentMillis5;
}
}
}
If you have any (!!) ideas on how to make it work, please push me in the right direction.
Thanks in advance.
Pete