// OMG so broekn. capricious saveOn / progOn?
//+ fixed. array out of bounds 99.44 percetn sure
// https://wokwi.com/projects/387207919743535105
//+ starting from the moving target
// https://wokwi.com/projects/387121472215461889
//+ grabbed off on 17 DEC 2024 22:06 Zulu
//+ most additions and chnages marked with //+
// https://forum.arduino.cc/t/effect-switcher-and-relays-eeprom-question/1209660
# include <EEPROM.h>
# include <LiquidCrystal.h>
# include <Wire.h>
# include <Keypad.h>
//+ let's some virtual LEDs
# include <Adafruit_NeoPixel.h>
# define NLEDS 9
# define PIN 33
Adafruit_NeoPixel vLED(NLEDS, PIN, NEO_GRB + NEO_KHZ800);
//...
# include <ezButton.h>
//...
ezButton ezBankButton(A6), ezProgButton(A7), ezSaveButton(A9), ezDownButton(A12);
LiquidCrystal lcd(A5, A4, A3, A2, A1, A0);
#define rHot LOW
#define rGnd HIGH
void setupRelays();
void switchRelay(int, int);
//const int bankButton = A6; //blue button
//const int downButton = A12; //red button
//int progButton = A7; //program button (green) - matches LED
//int saveButton = A9; //save button (yellow) - matches LED
const byte rows = 9;
const byte cols = 1;
//... we only need to get 9 keys. subtrat 'a' for 0..8 later
char keys[rows][cols] = {
{'a'},
{'b'},
{'c'},
{'d'},
{'e'},
{'f'},
{'g'},
{'h'},
{'i'},
};
byte rowPins[rows] = { 34, 35, 36, 37, 38, 39, 40, 41, 42 }; /* buttons or momentary switches */
//byte colPins[cols] = {5,6,7}; /* rotary switch - pin 7 is read/use, pin 5 is program/choose effects, pin 6 is save to a preset. */
//... if they never change, let us know!
// ------ I'm not sure what you mean by "never change"? What aspect - the read/preset/save or the rows/switches?
const byte colPins[cols] = { A15}; // just 1 column a..i
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);
const int numberOfPedal = 9;
const int bankVariable = 9;
const int numberOfBanks = 9;
const int OneRelayPin[numberOfPedal] = { 11, 14, 15, 16, 17, 18, 19, 20, 21 }; //+? pin 1 on relay - reset/default has pin 1 at 0v - RED LED */
const int TenRelayPin[numberOfPedal] = { 10, 9, 8, 7, 6, 5, 4, 3, 2 }; //+? pin 10 on relay - reset/default has pin 10 at 5v - BLUE LED
const int presetLEDPin[numberOfPedal] = { 44, 45, 46, 47, 48, 49, 50, 51, 52 }; /* shows which preset is engaged - GREEN LED */
const int effectLEDPin[numberOfPedal] = { 22, 23, 24, 25, 26, 27, 28, 29, 30 }; /* shows which effects are engaged - YELLOW LED */
const int progLED = A8; // green
const int saveLED = A10; // red
byte midiChannel = 0; //+ thanks for leaving MIDI out of this for the moment
int bank = 0; //Starting Bank Number - illegal, gets fixed tight away
// so initial mode is "Press Any Preset", mode 0 READ
boolean saveOn = false;
boolean progOn = false;
/******************************************************/
void setup() {
Serial.begin(115200);
Serial.println("first things first!\n");
lcd.begin(16, 2);
lcd.print("I am a");
lcd.setCursor(0, 1);
lcd.print("real cowboy.");
Serial.print(" "); Serial.print(kModeF()); Serial.println(" initial");
//+
setupVLEDs();
//+ just to see LEDs are wired and operating for (; ; ); // hang it up here
setupRelays(); // pins and initial state
pinMode(progLED, OUTPUT);
pinMode(saveLED, OUTPUT);
for (int ii = 0; ii < numberOfPedal; ii++) /* setup device */
{
pinMode(presetLEDPin[ii], OUTPUT);
pinMode(effectLEDPin[ii], OUTPUT);
digitalWrite(presetLEDPin[ii], LOW);
digitalWrite(effectLEDPin[ii], LOW);
delay(10);
}
delay(10);
for (int ii = 0; ii < numberOfPedal; ii++) {
digitalWrite(presetLEDPin[ii], HIGH);
digitalWrite(effectLEDPin[ii], HIGH);
delay(10);
}
digitalWrite(progLED, LOW);
digitalWrite(saveLED, LOW);
delay(10);
digitalWrite(progLED, HIGH);
digitalWrite(saveLED, HIGH);
delay(10);
//...
ezBankButton.setDebounceTime(20);
ezProgButton.setDebounceTime(20);
ezSaveButton.setDebounceTime(20);
ezDownButton.setDebounceTime(20);
delay (1500); // ------ adding this ONLY because I want the initial message to show a bit longer. Definitely open to a better method, though.
}
/* Button Debounce should have had the pin as a parameter and not four functions
//...
boolean debounce(int thePin, boolean last) {
boolean current = digitalRead(thePin);
if (last != current) {
delay(5);
current = digitalRead(thePin);
}
return current;
}
*/
/*********************************************************/
void midiProg(byte status, int data) {
Serial.write(status);
Serial.write(data);
}
/*********************************************************/
void memory(int addr, int led) {
for (int ii = 0; ii < numberOfPedal; ii++)
{
EEPROM.write((addr) + ii, getEffectLED(ii));
// digital Write(presetLEDPin[ii], HIGH); // turns off all preset LEDs
setPresetLED(ii, HIGH);
}
lcd.clear();
lcd.setCursor(0, 0); //+ it may be already. belt and suspenders let's be sure
lcd.print("Program saved to");
lcd.setCursor(0, 1);
lcd.print("Bank ");
lcd.print(bank);
lcd.print(" Preset ");
lcd.print(led + 1);
/* Preset LED will flash when saving effects loops */
//+ let's fake that better while we see if this is messing with me some day
for (int ii = 0; ii < 6; ii++) {
// digital Write(presetLEDPin[led], (ii & 1) ? HIGH : LOW);
setPresetLED(led, (ii & 1) ? HIGH : LOW); // ledON, ledOFF for this upside down issue
delay(250); vLED.show(); // sorry Mom!
}
// saveOn = !saveOn;
// progOn = !progOn;
Serial.print(" "); Serial.print(kModeF()); Serial.println(" after both are toggled");
for (int ii = 0; ii < numberOfPedal; ii++) {
//+ digitalWrite(effectLEDPin[ii], HIGH); // turns off all effect LEDs
setEffectLED(ii, HIGH); // low? off
}
// delay(1500); please!
// ------ fine! hehe - but I'm adding more LED flashes so the screen shows the Bank/Preset info a little longer.
//+ nope, this should not be responsible for this message
// lcd.clear();
// lcd.print("Program Again Or");
// lcd.setCursor(0, 1);
// lcd.print("Press Any Preset");
//+ instead, let us just exit programming mode
// next loop pass will publish LCD message
progOn = false;
saveOn = false;
}
/*********************************************************/
void resetAllRelays() {
for (int ii = 0; ii < numberOfPedal; ii++)
switchRelay(ii, LOW); //... whatever the reset state is
}
/*********************************************************/
void resetAllLeds() {
for (int ii = 0; ii < numberOfPedal; ii++) {
//+ digitalWrite(presetLEDPin[ii], HIGH);
setPresetLED(ii, HIGH); // low? off
}
}
/*********************************************************/
void writeOut(int relay) {
//+ resetAllLeds(); - this messes with the yellow bank when we share a vLED
//+ digital Write(effectLEDPin[relay], !digitalRead(effectLEDPin[relay]));
setEffectLED(relay, getEffectLED(relay) ? LOW : HIGH); // opposite. toggle, you know
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Current Bank: ");
lcd.print(bank);
lcd.setCursor(0, 1);
lcd.print("Select Loops: ");
lcd.print(relay + 1);
}
/*********************************************************/
void readPreset(int addr, int pcNum, int led) {
//+
for (int ii = 0; ii < numberOfPedal; ii++)
{
setEffectLED(ii, EEPROM.read(addr + ii));
//+ digital Write(ii], HIGH);
setPresetLED(ii, HIGH);
int kPreset = EEPROM.read(addr + ii);
switchRelay(ii, kPreset ? HIGH : LOW);
}
//+ digitalWrite(presetLEDPin[led], LOW);
setPresetLED(led, LOW); // low? off
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" B-");
lcd.print(bank);
lcd.setCursor(0, 1);
lcd.print("Preset ");
lcd.print(led + 1);
delay(100);
}
//+ too may *s ;-)
// kludge a real state out of the saveOn/progOn booleans
# define READ 0
# define WRITE 1
# define MEMORY 3
char *modeTags[] = {"READ (0)", "WRITE (1)", "ERROR/UNUSED (2)", "MEMORY (3)"};
void loop()
{
vLED.show(); // until this is in the machinery
// print the mode if it is different this time through
static unsigned char lastMode = 0xff;
if (kModeF() != lastMode) {
Serial.print("mode: "); Serial.println(modeTags[kModeF()]);
lastMode = kModeF();
}
//... do the UI buttons
ezBankButton.loop();
ezProgButton.loop();
ezSaveButton.loop();
ezDownButton.loop();
bool doBank = ezBankButton.isPressed();
bool doProg = ezProgButton.isPressed();
bool doSave = ezSaveButton.isPressed();
bool doDown = ezDownButton.isPressed(); // down will be easier to get working now ;-)
//... somehow do the bank thing unprovoked
static bool firstTime = true;
doBank |= firstTime; firstTime = false;
if (doBank) {
//...
bank++;
if (bank >= numberOfBanks + 1) bank = 1;
//+
for (int ii = 0; ii < numberOfPedal; ii++) {
setPresetLED(ii, HIGH);
setEffectLED(ii, HIGH);
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" B-");
lcd.print(bank);
lcd.setCursor(0, 1);
lcd.print("Press Any Preset");
}
if (doProg) {
saveOn = false; //+ amIRight?
progOn = !progOn;
Serial.print(" "); Serial.print(kModeF()); Serial.println(" (doProg)");
if (progOn) {
for (int ii = 0; ii < numberOfPedal; ii++) {
//+ digitalWrite(presetLEDPin[ii], HIGH); //turn OFF all preset LEDs
//+ digitalWrite(effectLEDPin[ii], HIGH);
setPresetLED(ii, HIGH); // low? off
setEffectLED(ii, HIGH); // low? off
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Program Mode:");
lcd.setCursor(0, 1);
lcd.print("Select Loops");
}
else {
//+ this path still surprises me
Serial.println(" w.t.f.");
lcd.setCursor(0, 0); //+ here and elsewhere we don't need to clear, and it will not flicker if done again
lcd.print(" B-");
lcd.print(bank);
lcd.setCursor(0, 1);
lcd.print("Press Any Preset");
}
}
//+
if (!saveOn && !progOn) {
// lcd.clear(); //+ trick - no need to clear if you write in every character position
lcd.setCursor(0, 0); // but do need to set the cursor
lcd.print(" B-");
//+ "123456789012345"); // 15 chars
lcd.print(bank); // and one more
lcd.setCursor(0, 1);
lcd.print("Press Any Preset");
//+ "1234567890123456"); // 16 chars
}
digitalWrite(progLED, progOn);
//... digitalWrite(progRelay, progOn);
if (doSave) {
Serial.print(" "); Serial.print(kModeF()); Serial.println(" (doSave)");
if (!progOn) {
Serial.println("SAVE button - not in program mode, what would you like that to mean?");
}
else {
//+
if (saveOn) {
Serial.println("Imma ignore that, you are in save mode, select preset or leave program mode.");
} else
saveOn = true;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Save Mode:");
lcd.setCursor(0, 1);
lcd.print("Select Preset");
}
}
//... digitalWrite(progRelay, progOn); //+ remember the Alamo
digitalWrite(progLED, progOn);
digitalWrite(saveLED, saveOn); // never hurts. So these can be at this level in the loop, execute every time.
//+
unsigned char kMode = kModeF(); //... figure kMode from two mode booleans
// check for single column keypad input
char key = keypad.getKey();
if (!key) return; //+ no valid key we done - careful, this skips the remainder of the loop
char mKey = key - 'a'; // mKey is therefore 0..8
// kMode = 3; just testing
//... to see mode and argument
if (1) {
Serial.print(kMode); Serial.print(" mode ");
Serial.print((int) mKey); Serial.print(" token ");
Serial.println("");
}
switch (kMode) {
case WRITE :
writeOut(mKey); // relay
break;
case MEMORY :
Serial.print("memory bank is "); Serial.println(bank);
memory(bank * bankVariable + 100 * (mKey), mKey); //addr, led
break;
case READ :
Serial.print("read bank is "); Serial.print(bank); Serial.print(" - "); Serial.println(bank * bankVariable + 100 * (mKey));
readPreset(bank * bankVariable + 100 * (mKey), mKey + 1, mKey);
break;
default :
Serial.print(kMode); Serial.println(" error");
}
}
//+ argh! I keep fixing this, someone keeps breaking it, maybe me. function to help
unsigned char kModeF()
{
unsigned char theMode = 0;
if (saveOn) theMode += 2;
if (progOn) theMode += 1;
return theMode;
}
void setupRelays() {
for (int ii = 0; ii < numberOfPedal; ii++) {
digitalWrite(OneRelayPin[ii], LOW);
digitalWrite(TenRelayPin[ii], LOW); // they will be, but here explicitly so no one wonders
pinMode(OneRelayPin[ii], OUTPUT);
pinMode(TenRelayPin[ii], OUTPUT);
initRelay(ii, LOW); // HIGH or LOW as desired
}
}
// this driver
// gives him a little jolt left to right, or right to left. careful how you fix things, think it out
void switchRelay(int relayNumber, int onOff) {
if (onOff) { // close the contacts current flows 1 to 10
digitalWrite(OneRelayPin[relayNumber], rGnd);
digitalWrite(TenRelayPin[relayNumber], rHot);
} else { // open the contacts
digitalWrite(TenRelayPin[relayNumber], rGnd);
digitalWrite(OneRelayPin[relayNumber], rHot);
}
// if we have additional tell-tale lamps for the relays
// digitalWrite(tellTale[relayNumber], onOff ? HIGH : LOW);
//... you will need a delay accordian to the relay specifications
// ------ I turned off the delay here - when I'm switching presets, I need them all the switch at the same time
delay(30); // give peace a chance. implement the "stab" machine or switch to 7 ms
//delay(75); // let us see the pulse for now
digitalWrite(OneRelayPin[relayNumber], rGnd);
digitalWrite(TenRelayPin[relayNumber], rGnd); // always return to
}
void initRelay(int relayNumber, int onOff) {
if (onOff) { // close the contacts current flows 1 to 10
digitalWrite(OneRelayPin[relayNumber], rHot);
// digitalWrite(TenRelayPin[relayNumber], rGnd);
} else { // open the contacts
digitalWrite(TenRelayPin[relayNumber], rHot);
// digitalWrite(OneRelayPin[relayNumber], rGnd);
}
//delay(700);
// if we have additional tell-tale lamps for the relays
// digitalWrite(tellTale[relayNumber], onOff ? HIGH : LOW);
//delay(700); // give peace a chance. implement the "stab" function
//delay(500); // let us see the pulse for now
digitalWrite(OneRelayPin[relayNumber], rGnd);
delay(50);
digitalWrite(TenRelayPin[relayNumber], rGnd); // always return to
delay(50); // let us see the pulse for now
}
//+
//+ add 8 smart LEDs to mimic the green bank adding an indicator for non-empty slots
void setupVLEDs()
{
vLED.begin();
vLED.setPixelColor(1, 0xff0000);
vLED.setPixelColor(2, 0x00ff00);
vLED.setPixelColor(3, 0x0000ff);
vLED.setPixelColor(NLEDS - 1, 0xffffff);
vLED.show();
delay(777); vLED.clear(); vLED.show();
}
// get rid of digital Read hack. store the state properly
char kStoreEffectLED[9];
const unsigned long kDIM1 = 0x008080; // CYAN/2
const unsigned long kDIM2 = 0x800080; // MAGENTA/2
void setEffectLED(int theLED, int theValue)
{
// yellow LEDs toggle, vLED turns off other vLEDs ??? //+ remains an defect
Serial.print(" setEffect "); Serial.print(theLED); Serial.print(" "); Serial.println(theValue);
digitalWrite(effectLEDPin[theLED], theValue ? HIGH : LOW);
vLED.setPixelColor(theLED, theValue ? kDIM1 : 0xffff00);
// vLED.show();
kStoreEffectLED[theLED] = theValue;
}
int getEffectLED(int theLED)
{
//Serial.print(kStoreEffectLED[theLED] ? 1 : 0); Serial.println(" get effect");
// return digital Read(effectLEDPin[theLED]) == HIGH ? 1 : 0; // true or false
return kStoreEffectLED[theLED] ? 1 : 0;
}
void setPresetLED(int theLED, int theValue)
{
digitalWrite(presetLEDPin[theLED], theValue ? HIGH : LOW);
vLED.setPixelColor(theLED, theValue ? kDIM2 : 0x00ff00); // kDIM, kGREEN
vLED.show();
}
BANK
PROG
SAVE
UA Version w/ some smart LEDs