DIY Arduino Foot Controller

This is how I write my code... Keep it modular and avoid mixing code for modes... Helps to write out a flow diagram that illustrates the hardware and software connection, and further a software specific flow diagram :)


Sent from my iPhone using Tapatalk
 
Feel free to embrace or ignore these comments as you see fit, it's up to you how you structure your code

Thanks - I really do want to embrace it - but think I'll need to wait for my MkII version to implement.
Its on the list for improvement ideas.
..and for when I have a better idea of coding in general.
 
SOrry if this is repeated - but not showing thtread as being updated the last 2 times I posted.

ANyone any idea how long the time window is to read a Sysex msg?

ie does it just sit there waiting t be read or do I need t try & read it as soon as its been sent?
 
It's taking 20+ loops of this function before is actually reads teh Preset Name.

Code:
void readPSName()
{ 
  Found_PSName_Sysex = false;
  while_counter = 0;

  MIDI.sendSysEx(8,data,true);
  MIDI.read();
  
  LCD_Print();   //update LCD 
  
  while (Found_PSName_Sysex == false)
    { 
     Serial.println("Send SYSEX ");
     delay(30);

     Serial.print("Mid Read : ");
     Serial.println(MIDI.read());
                
      if (MIDI.read() && Found_PSName_Sysex ==false)
          {
            const byte *pMsg = MIDI.getSysExArray();
            Serial.println("read a sysex");   
            if (pMsg[5]==0x0F)
              {
                Found_PSName_Sysex = true;
                lcd.setCursor(0, 2);
                lcd.write("           ");
                lcd.setCursor(0, 2);
                
                for (int n = 6; n < 26; n++)
                  {      
                    lcd.write(pMsg[n]);
                    Serial.print(pMsg[n]);
                    Serial.print("-");
                  }
                 Serial.println(); 
              }
          }
          
         while_counter = while_counter +1;
        
          Serial.println("End While");
     }

Serial.print(while_counter);
}
 
SOrry if this is repeated - but not showing thtread as being updated the last 2 times I posted.

ANyone any idea how long the time window is to read a Sysex msg?

ie does it just sit there waiting t be read or do I need t try & read it as soon as its been sent?

Bottom line is that, on average, you always want to be keeping up with MIDI data - which means reading it more or less as soon as it is sent. If you're not on average keeping up with the data rate, then you will lose data.

That said, apart from the Tuner and a sysex dump (not of use to a foot controller) most of the Axe FX MIDI data is small packets, sent either infrequently, or as requested, so you have some leeway over how quickly you process it because:

The MIDI library works on top of the core Arduino serial library, so it's all really dependent on the implementation of that in the Teensy 3.1. Reading:

https://forum.pjrc.com/threads/24827-Silly-question-re-UARTs

It looks like the internal buffers for Serial are 255 bytes - meaning that you can accumulate upto 255 bytes in the buffer before you'll start losing data. Whether the oldest data will be overwritten, or the latest data discarded when this happens, depends on how the library code was written. But either way you do not want it to happen.

So the "time window", is how long it will take to fill that buffer with newer messages if you aren't reading the sysex from those buffers for a period of time. In the worst case of the tuner, with a new (from memory) 8-byte tuner message coming in every 7ms - you have 255/8 * 7 = 223ms maximum before the buffer fills if you don't read anything.

That's the long way of repeating that you should at all costs keep up overall with the incoming data, but you have a little bit of slack if you slip behind temporarily. Also note, that even if the low-level buffers were bigger, it doesn't really help because you're only temporarily postponing the issue that basically you're not keeping up.

Hope this helps.

Jon
 
It's taking 20+ loops of this function before is actually reads teh Preset Name.

I was going to mention this earlier in the thread, but I left it, as I know it's easy to get swamped with detail when you're trying to learn a lot of new stuff.

If you take a step back, and (using MIDI-OX) study the general flow of midi messages which originate from the Axe Fx when you do things like:

1. Use the rotary control to change presets
2. Turn effects blocks on and off
3. You send a program change message to the Axe FX

What you will see is that there is a predictable, but fairly loosely coupled set of Sysex messages being sent. In an ideal world, your foot controller would respond to the Axe FX telling it that a new Preset has been activated due to the user changing the rotary control, just as it would a midi foot controller sending it a program change message. Equally, if the user went to the layout screen and used the FX bypass button on a particular module, you'd expect the foot controller to seamlessly sync the Instant Access (IA) state for that module. That's how the MFC101 works.

What this means, is that you are far better off decoupling your handling of MIDI messages so that your program is not quite so stateful. More or less, you are currently doing this:

1. User presses Footswitch for preset 1
2. Controller sends program change for preset 1
3. Controller sends Read Preset Name and waits for response
4. Controller updates Screen with preset name
5. Normal processing resumes

You'd be better off doing this:

1. User presses footswitch for preset 1
2. Controller sends program change for preset 1
3. Main processing loop resumes
.
.
.
4. Controller receives sysex packet from Axe Fx indicating a new preset has been loaded up
5. Controller sends "Read Preset Name" sysex packet
6. Normal processing loop resumes
.
.
.
7. Controller receives Preset name sysex from Axe Fx
8. Controller updates display
9. Normal processing loop resumes
.
.

Can you see the difference ? In the second scenario, the controller is always in the main loop polling the footswitches and seeing if there's any new data incoming from the Axe Fx - it's never stuck waiting for messages. It also responds equally to changes made on the Axe front panel as it does the foot controller itself.

Yes, there is the case where you need some kind of time check to make sure that you get a response before too much time has elapsed (pretty much what happens with an MFC101 when it gives the dreaded AxeFx name timeout) but this is not an inline delay where nothing else happens, it is a timer count running alongside the rest of the processing.

It's a bit late and I'm off to bed now, but I thought I'd drop this in as some food for thought. I know you're saving up a load of changes for V2 of your code, so as a simple suggestion for fixing what you have now, I would suggest that when you want to call ReadPSName() you should set a flag to indicate that you are waiting for a name, reset a counter, then send the Read PresetName sysex message. In your sysex handler, when you get a preset name message coming in, you update the screen and reset the flag. In your main loop, if the counter reaches a certain value, and the flag is set, then you know the read preset name has timed out. Ideally, you don't want a simple counter, rather you want to use the timer ticks - but my mind has gone blank and I can't think how to do this on Arduino right now.

Hopefully this makes some sense. Shout back if you think I'm talking nonsense !
 
As always - Makes a lot of sense mate. At least I think it does.
Thanks.
Get some sleep. You may need it with the questions that follow. :)

So amongst other things I think you are saying - don't send the get preset name sys ex until all of the std preset change data/msgs that get automatically sent have been sent.
Then send the get preset name.?

Also if/when the timer times out what then.
Accept I've ,missed it or resend get preset name request.
 
So amongst other things I think you are saying - don't send the get preset name sys ex until all of the std preset change data/msgs that get automatically sent have been sent.
Then send the get preset name.?

Yes. The packets involved should be (I had to refresh my memory, from some code I wrote a while back, but...):

1.Controller Sends Program change
2. Axe Fx sends sysex packet type 0x14 - new preset has been selected
3. Controller sends Read Preset Name packet
4. Axe FX sends preset name sysex reply

The key thing is not to get stuck outside of your main processing loop stuck waiting for the specific reply to any packet you send out, or any packet you might expect next.

When you receive packet 0x14, you know the preset is actually selected - and also this is the packet the Axe Fx sends out when you change presets from the front panel also. Whenever you see one of these, you would update the display with the new preset number, and send the read preset name packet.

Also if/when the timer times out what then.
Accept I've ,missed it or resend get preset name request.

Ideally, you want to do at least one retry, resending the read preset name. The comms could be a bit glitchy due to a dodgy cable or interference. How many retries you do before giving up, and whether you slightly increase the timeout each time, is personal choice really - if the foot controller is idle not sending any other data, you could even keep retrying every second until the user changes preset again. But honestly, if you haven't got a response after the first couple of tries, and you've given it a reasonable timeout (upto 0.5 second, say) then it seems like it's not going to reply at all.

For reference, here is some code I wrote a while back as part of some from-scratch new firmware for the FCB-1010 to achieve the equivalent. I wasn't interested in the Preset Name as the FCB1010 can't show it, only the number. I was however interested in the effect block states for IA switch use. I didn't do a timeout in this version of the code. The function I'm showing is the equivalent of your sysex handling function, though obviously I wasn't using Arduino, or their midi library.

Code:
void process_sysex(uint8_t *buffer, uint16_t length)
{
    uint16_t i;

    if (!is_axefx_message(buffer, length))
    {
        return;
    }

    switch(buffer[5])
    {
        case 0x10:
            // tempo
            break;

        case 0x0d:
            // tuner packet, no checksum
            //
            break;

        case 0x14:
            // new preset ?
            //
            if (is_valid_checksum(buffer, length))
            {
                i = (((buffer[6] & 0x7f) << 7) | (buffer[7] & 0x7f)) + 1;

                // update numerical display
                //
                LED_displayNumber(i);

                // Request effect block states for IA use
                //
                SYSEX_CMD_requestStates();
            }
            break;

        case 0x0e:
            // Effect block states
            //
            //if (is_valid_checksum(buffer, length))
            {
                IA_prepareUpdateStates();

                for (i = 6; i < length - 2; i+=5)
                {
                    IA_updateState(
                      ((buffer[i+3] >> 3) & 0b1111) | ((buffer[i+4] & 0b1111) << 4),
                      ((buffer[i+1] >> 1) & 0b00111111) | ((buffer[i+2] & 0b11) << 6),
                      (buffer[i] & 0x01) ? 0 : 1,
                      (buffer[i] & 0x02) ? 0 : 1);
                }
            }
            break;

        case 0x21:
            // An effect block state has changed
            //
            if (is_valid_checksum(buffer, length))
            {
                // Re-request effect states for IAs
                //
                SYSEX_CMD_requestStates();
            }
            break;
    }
}

Can you see how it's completely stateless ? It only ever updates things or sends out a new command in response to some new message coming in from the Axe Fx. I'm never actively waiting for anything.
 
If it helps, in my code, I have a readMidi() function at the end of each main loop, that responds to any incoming sysex based on whether what is read is valid. I do not read midi after sending any data for the risk of being stuck in a loop waiting for a message. The read midi function only responds to tuner messages or preset name messages.

As Jspyve mentions, it is best to have a callback or event handler function to responds to midi if valid midi is read, that is most logical. I like end-loop polling to keep the clutter low and possibility of errors down (because I dont like debugging my errors :) ).

That way, if my teensy is connected and the tuner is turned on on front panel, the teensy would read the sysex and realize that the tuner on button wasn't turned on, but will still display tuner data until the time between tuner messages reaches a threshold (and will assume axe stopped sending them), and then quit the display tuning data loop.
 
I've just come across this thread and I'm really interested in building something similar although a little simpler for myself.

What's the best place to go to learn how to program an arduino to send midi commands? I'm just looking to be able to change scenes/patches from probably 4 buttons and have an LED that corresponds with whichever switch was pressed last.
 
Hey, assuming no programming skills:

https://www.arduino.cc/en/Tutorial/Midi
https://www.arduino.cc/en/Tutorial/Button

that should be everything you need to make such a controller :) I'd suggest starting a separate thread though if you need advice specific to your project.

Edit: the arduino website has many examples and a lot of information about getting started with programming for arduino. If you feel more comfortable with programming you can look at direct AVR chip programming:

http://shop.oreilly.com/product/0636920028161.do

lots of options!

Good luck!
 
Thanks so much dude! :D

I understand the concepts of logic and have done some really basic coding before through codeacademy in the past. But definitely still a beginner.
 
Hi Jon/Evz - thanks - I'll properly read through both your replies at home tonight & try & digest.

A quick question in mean time though pls - what exactly does MIDI.read() actually do?

I got this code from somewhere which I'm using (including the comment) in the VOID_Setup().
Code:
MIDI.setHandleSystemExclusive(handleSysex);  //tells arduino to run the "handleSysex" function every time a midi sysex message is received and the MIDI.read() command is executed

Is the comment strictly true - ie will the HandleSysex function run everytime I issue a MIDI.read?

Reason I ask is if that's correct - will my tuner function (HandleSysex) run even when I'm issuing MIDI.read as part of my Get Preset Name function (readPSName()).
 
Hello!

From my understanding in the comments of the 4.2 Midi Library, and the call back example:

handleSysEx function will only run AFTER a MIDI.read() call which reads in a Sysex message. HandleSysex will not run if the message type is not sysex, as MIDI.read() should place the incoming midi message into a local buffer, and builds a data struct of the message. Having a callback without a read would result in the handle code never being executed.

MIDI.read() runs a parsing algorithm on the serial input port defined, as described here (in MIDI.hpp):

Code:
// Parsing algorithm:
// Get a byte from the serial buffer.
// If there is no pending message to be recomposed, start a new one.
//  - Find type and channel (if pertinent)
//  - Look for other bytes in buffer, call parser recursively,
//    until the message is assembled or the buffer is empty.
// Else, add the extracted byte to the pending message, and check validity.
// When the message is done, store it.

If I'm wrong please let me know! I'll delete my silly post!
 
Hello!

From my understanding in the comments of the 4.2 Midi Library, and the call back example:

handleSysEx function will only run AFTER a MIDI.read() call which reads in a Sysex message. HandleSysex will not run if the message type is not sysex, as MIDI.read() should place the incoming midi message into a local buffer, and builds a data struct of the message. Having a callback without a read would result in the handle code never being executed.

MIDI.read() runs a parsing algorithm on the serial input port defined, as described here (in MIDI.hpp):

Code:
// Parsing algorithm:
// Get a byte from the serial buffer.
// If there is no pending message to be recomposed, start a new one.
//  - Find type and channel (if pertinent)
//  - Look for other bytes in buffer, call parser recursively,
//    until the message is assembled or the buffer is empty.
// Else, add the extracted byte to the pending message, and check validity.
// When the message is done, store it.

If I'm wrong please let me know! I'll delete my silly post!
Correct. So the best way is to handle everything in callbacks (handleSysex etc...) or after the midi.read() call with NO callbacks, but mixing both will lead to problems...
 
Made some progress last night following the suggestions from several folks above (Jon/Evz/AlGrenadine) - cheers.

OK so next questions (pls) - sorry they just keep on coming:

1 - Is calling a function from within another function bad practice or inherently slow?

2 - Also anyidea how I can concatenate the individual pMsg[n] array values into a single value that my current code below writes 1 at a time.
I'd like to be able to use the final "string" later.
I get various conversion errors.
The individual values can be numbers, letters, characters.

Code:
            const byte *pMsg = MIDI.getSysExArray();  
            if (pMsg[5]==0x0F)
              {
                for (int n = 6; n < 26; n++)
                  {      
                    lcd.write(pMsg[n]);
                  }
 
Best to store preset name into a dedicated char array, I use this process:

Code:
// This is defined in a header file
char pname[MAX_PRESET_N_LENGTH];
//

 if (MIDI.getSysExArray()[5] == 0x0f){
          for(int i=0;i<31;++i){
            pname[i] = MIDI.getSysExArray()[i+6];
          }
          for(int i=31;i!=0;--i){ //backwards removal of space
            if (pname[i] == 0x20){
              pname[i] = 0x00;
            }
            else { break;}
          }
          Serial.println(pname);
        }


No conversion problems in this code.

And yes, you can call function from function (that is what I do in my code, helps keep it compact).
 
Back
Top Bottom