Struct names randomly wrong ( maybe Serial / speedissue)

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

You should sanitize anything you read from EEPROM before using it as e.g. array indices. The value could be anything, resulting in an out of bounds write.

nice. this fixed an error I had forgotten about.. the random button works without sporadically freezing the device when I press another button afterwards.
Big thanks man. I also increased the NumSw by 1 (as there are 16 buttons but the const int was set to 16).
Id didn't fix the maning issue. Now it prints a funny chinese character instead of the "o" or whatever it likes to add.

Should I update my code (post the new version) everytime I make changes?

Gotcha

first off, some serial logger data

19:45:25.333 -> 
19:45:25.333 -> 8 6 1 
19:45:25.333 -> 01: obZen
19:45:25.333 -> Deat�  
19:45:25.374 -> print(int num)
19:45:25.374 -> print(long num,int digits)
19:45:25.374 -> 
19:45:25.374 -> 8 6 1 
19:45:25.374 -> 01: obZen

updated code

#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 = 15;
const int numSw = 17;
//                 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 };

const int MAX_EFFECTS_PRESET_INDEX = 9;
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 }
};

const int MAX_AMP_PRESET_INDEX = 4;
struct AmpPresets {
  char name[8];
  byte value;
};
AmpPresets ampPreset[] = {
  {"Death  ", 0},
  {"Jazz   ", 1},
  {"SRV    ", 2},
  {"Fusion ", 3},
  {"JCM800 ", 4}
};

//            
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;





void setup() {
  delay(1000);
  MIDI.begin(MIDI_CHANNEL_OMNI), MIDI2.begin(MIDI_CHANNEL_OMNI), MIDI3.begin(MIDI_CHANNEL_OMNI);
  Serial.begin(115200);
  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(1000);
  activeAmp = EEPROM.read(0);
  activeAmp = constrain(activeAmp, 0, sizeof(ampPreset) / sizeof(ampPreset[0]) - 1);
  activePreset = EEPROM.read(2);
  activePreset = constrain(activePreset, 0, MAX_EFFECTS_PRESET_INDEX);
  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();
  stateLed[activePreset] = !stateLed[activePreset];
  stateLed[10] = 0;
  lcd.setCursor(4, 1);
  lcd.print("STEALTH BYTE");
  lcd.setCursor(0, 2);
  lcd.print("to punish & enslave");
  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);

delay(2000);
lcd.clear();
}

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;
    }
  }
}

I'm learning how to use this forum as I go, so this reply might be misplaced. Below (or above) is an updated version of my code. Please let me know if I sanitized it correctly. THX

Facepalm...I have removed these functions now... they were not needed and had no physical led to illuminate :slight_smile:

stateLed[14] = 1;
stateLed[14] = 0;

It's my first time working with arrays and for-loops, so maybe I have misunderstood basic stuff.

How does this call index 14?

  for (int i = 0; i < numLed; i++) {
    pinMode(led[i], OUTPUT);
  }

I thought "i < numLed" ensures "i" is never higher than the size of the array

I'll shorten my millis once everything else works :slight_smile:

@peteccprogrammer Please read up on the use of the sizeof() macro. You need to,
declare your pin array with n elements, then
use sizeof to determine the value of numLed automagically. Then, when you make changes, you don't blow holes in your code, or your feet.

1 Like

You should either use a range-based for loop:

for (auto l : led)
  pinMode(l, OUTPUT);

Or if you need the index itself, use something like this:

template <class T, size_t N> constexpr size_t len(const T (&)[N]) { return N; }

for (size_t i = 0; i < len(led); ++i)
  pinMode(led[i], OUTPUT);

Do not use sizeof to determine the size of arrays, it is a low-level tool to get the size of the underlying object representation.

Ha! it all works now. But maybe you can tell me why. Was it the "=" or what am i missing?

This array is correct thisd way when it has 16 entries (0-15)

const int numSw = 16;
for (int i = 0; i < 16; i++) {
    pinMode(sw[i], INPUT_PULLUP);
  }

This caused the spelling issues

for (int i = 0; i <=numSw; i++) {
    pinMode(sw[i], INPUT_PULLUP);
  }

Do not hard-code the array size. Use the len() function I posted above to determine the size, based on the number of pins you specified.

This is never the right answer when indexing an array. Array indices are in the half-open interval [0, N), you always use (strictly) less than N, not less than or equal to N.

1 Like

Wow, so this

for (auto l : led)
  pinMode(l, OUTPUT);

is another way to get the correct result that this messed up a lilttle?
I tried it in my code... it works :slight_smile:

for (int i = 0; i < numLed; i++) {
    pinMode(led[i], OUTPUT);

I need to be more careful when posting replies and code, especially when i refer to the code that I posted as code. Sorry mate, I'm a beginner in this forum. I thought it would read correctly, but now I see how ambiguous my replies can be.

Unfortunately, I can only award one solution...

Then award @PieterP , he's pointed you in the right direction.
@PieterP Thanks for len(), hadn't spotted that. Although sizeof(array)/sizeof(first element of array) has always worked just fine.

Sigh. No wonder "The Arduino Language" goes to such lengths to hide C++ from users. That's ... clear as mud, and I haven't seen anything like it in assorted "intro to C++" classes/tutorials (which, after all, rarely get very far into templates. (and why isn't there some standard function for determining array size? Just because "you should be using a more complex data structure than C arrays, anyway!"?)

Do not use sizeof to determine the size of arrays, it is a low-level tool to get the size of the underlying object representation.

You'll have to explain that, too. Why is your template function better than

#define arraysize(a) ((sizeof a)/sizeof(*a))

Do not hard-code the array size.

I'm not sure I agree with that, either. I don't quite understand why an otherwise "strongly typed" language is so free with unspecified array lengths.

It may be confusing to have everything on a single line (this is to avoid the Arduino pre-processor from messing it up when collecting function signatures). With appropriate comments:

// The following is a function template. It can be called with any element
// type T and any size N. These template arguments are automatically
// deduced and filled in by the compiler.
template <typename T, size_t N>
// The function can be evaluated at compile time, so its result can be
// used as a compile-time constant (e.g. as another array size).
constexpr
// The function returns a size, and accepts an immutable reference to an
// array of N elements of type T as an (unused and unnamed) argument.
// It is passed by reference, because C-style arrays cannot be passed by
// value (they silently decay to a pointer).
size_t len(const T (&)[N]) {
    // It simply returns the number of elements of the array that was
    // passed as an argument. The correct value for the template argument
    // N is determined automatically by the compiler.
    return N;
}

Getting the number of elements of a C++ array a is as simple as using a.size().
In C++17 and up, you can also use std::size(a), which will even work for C-style arrays (as well as any other container or range with a size). std::size, std::ssize - cppreference.com

If you search for sizeof on this forum, you'll come across dozens of threads where people used it incorrectly. They either forget the /sizeof(*a) part, or they apply it to an array that has already decayed to a pointer (e.g. a function argument that looks like an array, but is actually a pointer). This results in them silently getting the wrong answer, often causing a crash at run time.

The len() function I provided either returns the correct answer, or it causes a compile-time error when you apply it to something that's not an array (e.g. a pointer).

The definition of len() may look somewhat complicated, but it's use is foolproof, as it prevents common mistakes.

Sizeof is great in low-level code when you need to know how much space to reserve to store an object in a buffer, for example, but it has no place in high-level code.

What I meant is that you should not store the hard-coded array size in a separate variable (or even worse: as a hardcoded bound in a for loop), because they are bound to get out of sync when someone adds or removes elements from the array.

int my_array[] {1, 2, 3};
const size_t my_array_size = 3; // Bad,
// don't do this. Just use len(my_array) if you need the size.

To be clear, I have no issues with specifying the size of an array in its declaration:

int my_array[3]; // Okay

This has nothing to do with strong typing. An array with an unspecified length still has a well-defined length (that is part of the type), it's just that the compiler does the counting of the number of elements in the initializer for you.

1 Like
  1. Indeed.
    As long as you're not compiling with something like -O0, of course, because then you don't allow the compiler to inline any functions.
    In C++20, you can use consteval instead of constexpr: then you force it to be evaluated at compile time (even if inlining is disabled).

  2. The T (&)[N] syntax is confusing, let's use a C++ array passed by value for clarity:

    template <size_t N>
    size_t foo(std::array<int, N> my_array) { return N; }
    

    This is simply a function template named foo that accepts one argument named my_array whose type is std::array<int, N>.
    If you were to call foo as follows:

    std::array<int, 3> some_array {1, 2, 3};
    foo(some_array);
    

    then it is obvious to the compiler that it should substitute N=3. For any other value of N, the function call foo(some_array) would be invalid, because the type of the argument some_array would not match the type of the parameter my_array. The complete rules for template argument deduction are of course a bit more involved, but usually there is a clear and obvious answer to the question of what the values of the template parameters should be for the code to make sense. In the rare case of an ambiguity, you may need to explicitly specify the argument (e.g. foo<3>(some_array)).

    To get back to T (&)[N], the issue is that the designers of C decided that a function parameter int my_array[N] should be exactly the same thing as a parameter int *my_array (which is useless to us here, because such a pointer carries no information about the size of the original array). To avoid this annoying edge case, you can pass the array by reference instead.
    So you might think that the parameter would then be something like T &my_array[N]. However, this is parsed as (T &) my_array[N] (i.e. my_array is an array where each element is a reference to T). To force it to be parsed correctly, you need to explicitly add parentheses, T (&my_array)[N].
    Finally, since the actual parameter my_array is never referenced inside of the len() function, there is no need to name it, and you can just shorten it to T (&)[N].

See also Anderson’s Clockwise/Spiral Rule:

                     +----------+
                     |    +---+ |
                     |    ^   | |
                 T  (&my_array)[N]
                 ^   ^        | |
                 |   +--------+ |
                 +--------------+
my_array is ...
  a reference to ...
  an array of N elements ...
  of type T.

Ah. Now THAT's a nice feature!

I have no issues with specifying the size of an array in its declaration

I was advocating for something like:

const size_t NLEDS 6;
uint8_t leds[NLEDS] = {1,2,3};

(Sigh. Why is there such a gulf between beginner-readable code and "advance, safe, proper" code? Even the use of "const", "size_t", and "uint8_t" interfere with simplicity of reading the code.)

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.