MIDI Batch Preset Box

Hey guys, I'm new to Arduino and am hoping for some guidance on this project.

Starting off is a bit overwhelming. I've gone through the Arduino Project Book that comes with the starter kit. I understand the process and what certain things do, but I haven't quite fully grasped the language. I do have plenty of experience building analog electronics. I've built over 100 guitar effects pedals, lots of high end studio gear, some guitar amps, and quite a few other things. Most of which were clones of circuits that were already designed.

I really want to learn how to do this stuff, so I'm not looking for somebody to do the work for me. Mostly, I'm hoping for somebody to help me make the right choices for this. This will explain why I am writing out each step of my thought process in pretty thorough detail.

Here is the project goal:

[u]Midi Proccessor[/u]

A device that will send at least 65 midi messages out on at least 6 different channels. Batch midi messages will be triggered by an incoming PM or by a footswitch. Each midi batch will be stored in a bank. I will need at least 30 banks. It will include an LCD display with a menu system. I'll be using a rotary encoder to navigate the menu system. I don't need to edit the menu system externally, but will need to be able to change predefined parameters. I am planning on writing ever saved option from the menu to a text file on an SD card. This will enable me to make copies and quickly edit on a computer.

Here is a basic mockup of what it will look like:

|500x479

[u]For this project, I know I'll need the following hardware:[/u]

1 x Arduino Due 1 x LCD display (would prefer to eventually use 14-segment for readability, but I have 16 x 2 that came with the kit) 1 x Rotary encoder 3 x SPST momentary footswitches 2 x midi jacks 1 x SD card pcb

I'm unsure of exactly how many ins and outs I'll need from my Arduino. [u]I think I need the following digital ins and outs:[/u]

LCD Display - 6 digital Rotary Encoder - 3 digital Foot switches - 3 digital Midi In/Out - 2 digital SD Card - 4 digital

So, this gives me a total of 18 digital ins/outs needed. This leads me to believe it will be out of the Uno's scope and that I should look to use the Due.

[u]For the code I am planning on calling up the following libraries:[/u] Arduino Midi Menu LiquidCrystal SD

[u]The following is an example of the menu system:/u

MF-104MSD-L (Channel 1) - Output Level - 0-10 (CC 7(MSB), 39(LSB)) - 14-bit - Time - I would love to figure out a way to display MS (needs to edit CC 12(MSB), 39(LSB)) - 14-bit - Feedback - 0-10 (CC 13(MSB), 44(LSB)) - 14-bit 0-16383 - Mix - 0-10 (CC 14(MSB), 46(LSB)) - 14-bit 0-16383 - LFO Rate - .05-50 (CC 15(MSB), 47(LSB) - 14-bit 0-16383 - LFO Amount - 0-10 (CC 16(MSB), 48(LSB) - 14-bit 0-16383 - LFO Waveform (CC#102) 7-bit 0-127 - Sine (0) - Triangle (16) - Square (32) - Saw (48) - Ramp (64) - S&H (80) - Smooth S&H (96) - Range Switch (CC#86) - Slow (0) - Fast (64) - Bypass Switch - On (127) - Off (0) - Time Slew Rate - 0-10 (CC 5(MSB), 37(LSB)) 14-bit 0-16383 - Filter Bright/Dark (CC#83) - Bright (0) - Dark (64) - Delay Time Multiplier (CC#87) - Normal (0) - X2 (32) - X4 (64) - X8 (96) - LFO Phase Reset (CC#105) - Yes (Any value) - No (Send nothing) - LFO Clock Divisions - Midi Note Spillover - Midi Note Mode

Well, that's about all I have for today. I'll start compiling a basic list of the coding that I think I should use to accomplish this soon. If you have any input for me, please let me know!

1 x Arduino Due

I would not use the Due, it is a 3V3 system and a bit delicate in the I/O department. I would do this on a Uno, but you might find the extra pins and memory on the Mega simplify things .

1 x SD card pcb

Can't see the need for this. Although you say:-

I am planning on writing ever saved option from the menu to a text file on an SD card. This will enable me to make copies and quickly edit on a computer.

I am not too sure what that means.

Grumpy_Mike: I would not use the Due, it is a 3V3 system and a bit delicate in the I/O department. I would do this on a Uno, but you might find the extra pins and memory on the Mega simplify things .

Thanks. I guess the board I will use will depending on the route I have to go.

Can't see the need for this. Although you say:-

I am not too sure what that means.

I'm not sure if the data will be too much for the Arduino. The amount of settings I want to save and send is much higher than nost of what is out there. I've seen mention of others doing similar projects and they saved settings on an SD card. I'm guessing that it would be written and read using something like CSV?

I'm not completely sure, so it's a little guesswork at this point.

'm not sure if the data will be too much for the Arduino.

No the amount of data will be fine.

he amount of settings I want to save and send is much higher than most of what is out there.

Doesn't look like it to me. But you have the full spec you calculate how much data you need. CSV is fine but you have to plan what you are going to do and break the project into small steps and test each step before going onto the next one.

I found a project that is very similar to what I was hoping to achieve. I've uploaded it to my Arduino and have tested it. It is a more simplified version used to control just one Moogerfooger pedal. I was hoping to get some guidance on how to modify it to control 3 different pedals. I've been able to modify the library to include more midi controls as well as modify the button functions (more user friendly for me) and screen color.

I am using the Adafruit RGB display. The same as what is used in the code.

Right now, there is one level to the menu. That is to scroll through the options for the MF-104M. I would like to add a menu layer on top of this that will allow me to put the current options into a sublayer. This is beyond me. I've looked at other menu setups before, but am confused how I would implement it into this code. Right now the LCD display shows

updateLCD(GlobalBank, GlobalPreset, GlobalParameter, GlobalValue);

at startup. I'm assuming I would need to change it to something like

updateLCD(GlobalBank, GlobalPreset, GlobalMainMenu);

? I would like a main menu system that scrolls through "Name, MF-104M1, MF-104M2, and MF-108M". Once pressing the select button, it will enter into that particular item and show the GlobalParameter options that are customized for each individual pedal. With the exception of Name, where I would just like to edit the name of each preset. When in the GlobalParameter menu, pressing select would save the current patch and revert back to the preset Name GlobalMainMenu. Can anybody offer some help on the best way to achieve this?

The library will need to be customized. My thought was to edit each of the libraries for the three pedals. The only differences between the two MF-104Ms are that they will be sent on different midi channels. They will need separate settings sent. The MF-108M has different values and midi CC#s, but some of them do overlap. What would be the best route for this? Should I simply change each library name to reflect each different pedal? For example;

#define ccBypass 80
       attribute[18][0] = ccBypass;

Would be changed to:

#define ccBypassMF104M1 80
       attribute[18][0] = ccBypassMF104M1;
#define ccBypassMF104M2 80
       attribute[18][0] = ccBypassMF104M2;
#define ccBypassMF-108M 80
       attribute[18][0] = ccBypassMF-108M;

Bypass is the same midi CC# for both pedals in this instance, but not always the case for other parameters.

Or should I modify the currently library to include functions for all of the pedals?

The files can be found at: https://github.com/ryanjamesmcgill/Midi-Preset-Box

I will be posting the original code in the post below.

The ino file code is:

/*********************
Example code for the Adafruit RGB Character LCD Shield and Library
This code displays text on the shield, and also reads the buttons on the keypad.
When a button is pressed, the backlight changes color.
**********************/
// include the library code:
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
#include <MF104.h>
#include <EEPROM.h>
// These #defines make it easy to set the backlight color
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7
//decalre MF104 object
#define totalParameters 19
MF104 superDelay[3][3];
int GlobalBank = 1; //start on bank 1
int GlobalPreset = 1; //start on preset 1
int GlobalParameter = 1; //start on parameter 1
int GlobalValue;
// The shield uses the I2C SCL and SDA pins. On classic Arduinos
// this is Analog 4 and 5 so you can't use those for analogRead() anymore
// However, you can connect other I2C sensors to the I2C bus and share
// the I2C bus.
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
void setup() {
Serial.begin(31250);
lcd.begin(16, 2);
lcd.setBacklight(VIOLET);
initializePins(4,10,8);
loadPreSets();
GlobalValue = superDelay[GlobalBank][GlobalPreset].getValue(GlobalParameter);
updateLCD(GlobalBank, GlobalPreset, GlobalParameter, GlobalValue);
}
uint8_t i=0;
int prevMidiValue;
int prevBS1;
int prevBS2;
int prevBS3;
void loop() {
uint8_t buttons = lcd.readButtons();
GlobalValue = readPot(A0);
int buttonState1 = digitalRead(4);
int buttonState2 = digitalRead(10);
int buttonState3 = digitalRead(8);
if (GlobalValue != prevMidiValue) //Knob is changed
{
//lcd.clear();
updateLCDvalue(GlobalValue);
superDelay[GlobalBank][GlobalPreset].setValue(GlobalParameter,GlobalValue);
superDelay[GlobalBank][GlobalPreset].sendSettings();
prevMidiValue = GlobalValue;
delay(50);
}
if (buttons) //Selection Buttons are Changed
{
if (buttons == 0x12) //LEFT + RIGHT Pressed
{
GlobalBank++;
if(GlobalBank > 3)
GlobalBank = 1;
GlobalValue = superDelay[GlobalBank][GlobalPreset].getValue(GlobalParameter);
updateLCD(GlobalBank, GlobalPreset, GlobalParameter, GlobalValue);
delay(400);
}
else if (buttons & BUTTON_RIGHT) {
GlobalParameter--;
if(GlobalParameter < 1) //check for edge
GlobalParameter = totalParameters;
GlobalValue = superDelay[GlobalBank][GlobalPreset].getValue(GlobalParameter);
updateLCD(GlobalBank, GlobalPreset, GlobalParameter, GlobalValue);
delay(200);
}
else if (buttons & BUTTON_LEFT) {
GlobalParameter++;
if(GlobalParameter > totalParameters) //check for edge
GlobalParameter = 1;
GlobalValue = superDelay[GlobalBank][GlobalPreset].getValue(GlobalParameter);
updateLCD(GlobalBank, GlobalPreset, GlobalParameter, GlobalValue);
delay(200);
}
else if (buttons & BUTTON_SELECT) {
storePreset(GlobalBank, GlobalPreset);
//superDelay[GlobalBank][GlobalPreset].sendSettings();
lcd.clear();
lcd.setCursor(4,0); lcd.print("SAVED");
lcd.setCursor(3,1); lcd.print("SETTING!!!");
delay(1000);
updateLCD(GlobalBank, GlobalPreset, GlobalParameter, GlobalValue);
}
}
//store "previous" states so we can detect changes
prevBS1 = buttonState1;
prevBS2 = buttonState2;
prevBS3 = buttonState3;
}
void storePreset(int bank, int preset)
{
int address = (bank*100) + (preset*20);
superDelay[bank][preset].storeData(address);
}
void loadPreSets() //load all presets from EEPROM
{
superDelay[0][0].loadData(0);
superDelay[0][1].loadData(20);
superDelay[0][2].loadData(40);
superDelay[1][0].loadData(100);
superDelay[1][1].loadData(120);
superDelay[1][2].loadData(140);
superDelay[2][0].loadData(200);
superDelay[2][1].loadData(220);
superDelay[2][2].loadData(240);
}
void initializePins(int A, int B, int C)
{
pinMode(A, INPUT);
pinMode(B, INPUT);
pinMode(C, INPUT);
}
int readPot(int port)
{
int sensorValue = analogRead(port);
return sensorValue * (127.0/1023.0);
}
void updateLCD(int bank, int preset, int parameter, int value)
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print(bank); lcd.print("-");
lcd.print(preset); lcd.print(":");
printParameterName(parameter);
lcd.setCursor(0,1);
lcd.print("VALUE:"); lcd.print(value);
}
void updateLCDvalue(int value)
{
lcd.setCursor(6,1);
lcd.print(value);
lcd.print(" ");
}
void printParameterName(int parameter)
{
switch(parameter)
{
case 1:
lcd.print("Output Level");
break;
case 2:
lcd.print("Time");
break;
case 3:
lcd.print("Feedback");
break;
case 4:
lcd.print("Mix");
break;
case 5:
lcd.print("LFO Rate");
break;
case 6:
lcd.print("LFO Amount");
break;
case 7:
lcd.print("LFO Waveform");
break;
case 8:
lcd.print("Range");
break;
case 9:
lcd.print("Time Slew Rate");
break;
case 10:
lcd.print("EQ");
break;
case 11:
lcd.print("Dly Time Mult");
break;
case 12:
lcd.print("LFO Phase Reset");
break;
case 13:
lcd.print("LFO ClkDiv");
break;
case 14:
lcd.print("Time ClkDiv");
break;
case 15:
lcd.print("Spillover");
break;
case 16:
lcd.print("Triplets");
break;
case 17:
lcd.print("Tap Dest");
break;
case 18:
lcd.print("LED Div");
break;
case 19:
lcd.print("Bypass");
break;
}
}

It also includes a library for one of the Moogerfoogers:

.cpp file is:

/*
MF-104.cpp - Library for Moog MF-104 settings class
Created by Ryan J. McGill, January 16, 2015.
Released into the public domain.
*/
#include "Arduino.h"
#include "MF104.h"
#include <EEPROM.h>
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
#define totalAttributes 19
#define ccMIDI 0xB0
#define ccOutputLevel 7
#define ccTime 12
#define ccFeedback 13
#define ccMix 14
#define ccLFOrate 15
#define ccLFOamount 16
#define ccLFOwaveform 102
#define ccRange 86
#define ccSlew 5
#define ccEQ 85
#define ccDelayTimeMult 87
#define ccLFOphaseReset 105
#define ccLFOclockDiv 107
#define ccTimeClockDiv 90
#define ccSpillover 82
#define ccTriplets 88
#define ccTapDest 116
#define ccLEDdiv 118
#define ccBypass 80
MF104::MF104()
{
attribute[0][0] = ccOutputLevel;
attribute[1][0] = ccTime;
attribute[2][0] = ccFeedback;
attribute[3][0] = ccMix;
attribute[4][0] = ccLFOrate;
attribute[5][0] = ccLFOamount;
attribute[6][0] = ccLFOwaveform;
attribute[7][0] = ccRange;
attribute[8][0] = ccSlew;
attribute[9][0] = ccEQ;
attribute[10][0] = ccDelayTimeMult;
attribute[11][0] = ccLFOphaseReset;
attribute[12][0] = ccLFOclockDiv;
attribute[13][0] = ccTimeClockDiv;
attribute[14][0] = ccSpillover;
attribute[15][0] = ccTriplets;
attribute[16][0] = ccTapDest;
attribute[17][0] = ccLEDdiv;
attribute[18][0] = ccBypass;
}
void MF104::sendSettings()
{
for(int i = 0; i < totalAttributes; i++)
{
Serial.write(ccMIDI); Serial.write(attribute[i][0]); Serial.write(attribute[i][1]);
}
}
void MF104::setValue(int parameter, uint8_t value)
{
attribute[parameter-1][1] = value;
Serial.write(ccMIDI); Serial.write(attribute[parameter-1][0]); Serial.write(attribute[parameter-1][1]);
}
uint8_t MF104::getValue(int parameter)
{
return attribute[parameter-1][1];
}
void MF104::storeData(int address)
{
for(int i = 0; i < totalAttributes; i++)
{
EEPROM.write(address + i, getValue(i + 1)); //i+1 because we started with 0
}
}
void MF104::loadData(int address)
{
for(int i = 0; i < totalAttributes; i++)
{
setValue(i + 1, EEPROM.read(address + i));
}
}

.h file is:

/*
MF-104.h - Library for Moog MF-104 settings class
Created by Ryan J. McGill, January 16, 2015.
Released into the public domain.
*/
#ifndef MF104_h
#define MF104_h
#include "Arduino.h"
#include <EEPROM.h>
class MF104
{
public:
MF104();
void sendSettings();
void setValue(int parameter, uint8_t value);
uint8_t getValue(int parameter);
void storeData(int address);
void loadData(int address);
private:
uint8_t attribute[19][2];
};
#endif

I would like a main menu system that scrolls through .........

Multilevel menus are nothing special you just have to code them up. The technique I use is shown in this project of mine:- http://www.thebox.myzen.co.uk/Hardware/RFID_Sequencer.html It is a medium complex menu system but basically it involves a few numbers that tell you what menu and sub menu you are at. Then at the deepest level there is the code to do the options you want. The code for this project is available on a link on this page.

Man, I don't know how you guys do this stuff. After staring at your code for an hour, I'm much more confused than when I started.

Okay, so I would add something like this at the top?

String mainMenu [] = { "Name","MF-104M1","MF-104M2",MF-108M"};

It looks like this would define the names of the mainMenu items? Then I would somehow have to figure out the code to show this menu. The rest of the code kinda made some sense to me (as far as recognizing the words), but I don't understand why it's written like it is and how it ends up being a menu system like your diagram.

To be honest, I think I need to accept that this is way outside of my ability and that I don't have the time and resources to learn this. I can edit something that's already written, but to write something like what you do is just unbelievable. I've gone through the Arduino Project Book and a few others, but they only teach you how to copy and modify basic functions like LEDs blinking or basic sensors.

What would I need to do to get somebody to basically hold my hand through this? I can afford a little bit, but not much. I'm going to need much more help than hints if I'm going to accomplish this. And accomplishing this box would be something very important for me.

What would I need to do to get somebody to basically hold my hand through this?

You could post in the gigs and collaboration section.

Better would be to get some one local to mentor you. Do you have any hack spaces or such like close to you? Those are a great way for getting one to one tuition.

You could also google 'arduino menu tutorial' one such link is this:- http://playground.arduino.cc/Code/Menu

Thanks Grumpy_Mike. I'm going to follow those suggestions. The menu tutorial link is a helpful breakdown that makes a bit more sense. I can also see some similarities between that tutorial and what the author of the Moogerfooger code used. I'm going to spend some time trying to learn more about it before posting in gigs and collaborations.

It's going to be hard for me to find someone local to help since I only find random bits of time to try to learn this. With the day job, studio, band rehearsals, and session work, I'm booked solid for around 100 hours a week.