Sending MIDI CC value based on last received value with midi read

Status
Not open for further replies.

Aussie_CrocHunter

Well-known member
Hi there people who know way more than me! *wave*

I'm building a midi controller (isn't everyone?). I am using footswitches with built-in LED rings. They are two-color rings (red and green).

The footswitches are latching switches (push on, then push off again).

I have successfully written a sketch where each button sends a different midi CC and then the LEDs read the midi state from the software. when the switch is taken LOW, the controller sends value of 127 (fully on) and then the software will send a message back to the LED saying that it should change to green - the same happens when sending a value of "0" - the software will acknowledge and send back a "0" value which the teensy will read and turn the green LEDs off and the red LEDs on.

This is also fairly simple. But, what I'd like, is for the CC value that the button sends to be based on a midi read or at least, the state of the LEDs. That would mean that the it doesn't matter if the switch is made low or high, it will simply do the opposite of the last message it received. (if the leds are green, changing the state of the footswitch would send a midi CC value of "0", and if the leds are red, then changing the switch would send "127")

I haven't studied programming, but through my googling I assume I'm going to have to store a variable. But unfortunately I have no idea what that means or how to write it!

any help would be greatly appreciated (and also any refining of my current code - if i won't have to completely re-write it for this sort of function that is)

Code:
#include <Bounce2.h>

//BUTTONS SETUP
const int NUM_OF_BUTTONS = 6;
const int DEBOUNCE_TIME = 5;

Bounce buttons[NUM_OF_BUTTONS];

int MIDI_CC_NUMS[NUM_OF_BUTTONS];
int MIDI_CC_VALS[NUM_OF_BUTTONS];
int MIDI_CHAN = 1;

//Change LED red to green when receiving MIDI CC

int redLED1 = 6;
int greenLED1 = 7;
int redLED2 = 8;
int greenLED2 = 9;
int redLED3 = 10;
int greenLED3 = 16;
int redLED4 = 17;
int greenLED4 = 18;
int redLED5 = 19;
int greenLED5 = 20;
int singleLED = 21;

void OnControlChange(byte channel, byte controller, byte value) {
  if (controller == 99 && value == 127) {
  digitalWrite(greenLED1, LOW);
  digitalWrite(redLED1, HIGH); //receiving 127 turns green on and red off
}
  if (controller == 99 && value == 0) {
  digitalWrite(greenLED1, HIGH);
  digitalWrite(redLED1, LOW); //receiving 127 turns green off and red on
} 
  if (controller == 100 && value == 127) {
  digitalWrite(greenLED5, LOW);
  digitalWrite(redLED5, HIGH); //receiving 127 turns green on and red off
}
  if (controller == 100 && value == 0) {
  digitalWrite(greenLED5, HIGH);
  digitalWrite(redLED5, LOW); //receiving 127 turns green off and red on
} 
  if (controller == 101 && value == 127) {
  digitalWrite(greenLED2, LOW);
  digitalWrite(redLED2, HIGH); //receiving 127 turns green on and red off
}
  if (controller == 101 && value == 0) {
  digitalWrite(greenLED2, HIGH);
  digitalWrite(redLED2, LOW); //receiving 127 turns green off and red on
} 
  if (controller == 102 && value == 127) {
  digitalWrite(greenLED3, LOW);
  digitalWrite(redLED3, HIGH); //receiving 127 turns green on and red off
}
  if (controller == 102 && value == 0) {
  digitalWrite(greenLED3, HIGH);
  digitalWrite(redLED3, LOW); //receiving 127 turns green off and red on
} 
  if (controller == 103 && value == 127) {
  digitalWrite(greenLED4, LOW);
  digitalWrite(redLED4, HIGH); //receiving 127 turns green on and red off
}
  if (controller == 103 && value == 0) {
  digitalWrite(greenLED4, HIGH);
  digitalWrite(redLED4, LOW); //receiving 127 turns green off and red on
} 
}

void setup() {
  for (int i= 0; i < NUM_OF_BUTTONS; i++)
  {
    Bounce bounce = Bounce();
    bounce.attach(i);
    bounce.interval(DEBOUNCE_TIME);
    buttons[i] = bounce;

    pinMode (i, INPUT_PULLUP);

    MIDI_CC_NUMS[i] = 99 + i;
    MIDI_CC_VALS[i] = 127;
  }
  pinMode(redLED1, OUTPUT);
  pinMode(greenLED1, OUTPUT);
  pinMode(redLED2, OUTPUT);
  pinMode(greenLED2, OUTPUT);
  pinMode(redLED3, OUTPUT);
  pinMode(greenLED3, OUTPUT);
  pinMode(redLED4, OUTPUT);
  pinMode(greenLED4, OUTPUT);
  pinMode(redLED5, OUTPUT);
  pinMode(greenLED5, OUTPUT);
  digitalWrite(redLED1, HIGH);
  digitalWrite(greenLED1, HIGH);
  digitalWrite(redLED2, HIGH);
  digitalWrite(greenLED2, HIGH);
  digitalWrite(redLED3, HIGH);
  digitalWrite(greenLED3, HIGH);
  digitalWrite(redLED4, HIGH);
  digitalWrite(greenLED4, HIGH);
  digitalWrite(redLED5, HIGH);
  digitalWrite(greenLED5, HIGH);
  usbMIDI.setHandleControlChange(OnControlChange);

}

void loop() {
  for (int i = 0; i < NUM_OF_BUTTONS + 1; i++) {
  buttons[i].update();
  }
  for (int i = 0; i < NUM_OF_BUTTONS; i++)
  {
    if (buttons[i].fallingEdge())
    { 
      usbMIDI.sendControlChange (MIDI_CC_NUMS[i], MIDI_CC_VALS[i], MIDI_CHAN);
    }
    else if (buttons[i].risingEdge())
    {
      usbMIDI.sendControlChange (MIDI_CC_NUMS[i], 0, MIDI_CHAN);
    }
    while (usbMIDI.read());
    }
  }
 
You might want to ignore the LEDs at first and when you go to add them perhaps you will want to use arrays since you have them for other elements of your sketch.

Usually one would use momentary switches for this.

But you can process both edges the same and keep track of a virtual state separately from the actual physical state of the switch but it's a bit unusual to do with latching switches.

Code:
If (buttons[i].fallingEdge() || buttons[i].risingEdge()) {
  if (state[i]) {
      usbMIDI.sendControlChange (MIDI_CC_NUMS[i], 0, MIDI_CHAN);
  }else{
      usbMIDI.sendControlChange (MIDI_CC_NUMS[i], MIDI_CC_VALS[i], MIDI_CHAN);
  }
state[i] = !state[i]
}

So if either edge change is detected the same thing happens depending on what state it is currently in and afterwards reverse the Boolean value in the state array for the next change.

Later you can try to use the returned state instead of just toggling each time.

There are a number of problems with your sketch including the arrays used for CC data are not populated with data. (You might want to replace MIDI_CC_VALS with a single scalar value (at 127?) and to use the same value in in checking the returning messages.)

I'm concerned you're coding skills don't yet match your ambition. ;)

BTW - latching switches feel different in their two physical states, you might find it odd with this when it doesn't match the virtual state.
 
So are you committed to the latching switches?

Regardless the LED/DAW feedback feature (assuming your DAW is echoing back the received CC messages??) should be easy to convert to arrays and to integrate back into the above approach once you have that working.

Here's a rough 'sketch' (fragment) of how you could use two arrays to hold the PIN numbers for the LEDs and set them HIGH or LOW along with a state variable to track the current state so the trigger mechanism knows whether to send ON or OFF CC values based on what it got back from the DAW.

Code:
void OnControlChange(byte channel, byte controller, byte value) {
  for (int i = 0; i < NUM_OF_BUTTONS; i++){
    if (MIDI_CC_NUMS[i] = controller) {
      if (value < 64) {
        digitalWrite(greenLED[i], HIGH);
        digitalWrite(redLED[i], LOW); //receiving <64 turns green off and red on
        state[i] = true;
      }else{
        digitalWrite(greenLED[i], LOW);
        digitalWrite(redLED[i], HIGH); //receiving >=64 turns green on and red off
        state[i] = false;
      } // if not the controller for i then skip this loop    
    }
  }
}


Note that your code didn't handle CC values other than end points. I've set a middle threshold for on and off which could be switched check for not equal 127 if that's what you prefer.

I'm happy to help if you're in over your head provided you're interested in learning and not just in getting finished code.
 
Last edited:
Hey, thanks so much for replying. I'm sorry I didn't respond more quickly. I have used latching switches because they are the ones which have the built in LED-ring. The switches are footswitches as used on guitar pedals (3pdt). So they don't actually feel different being on or off. My coding skills indeed do not match my ambition! I'll send you a pm with a couple of other questions not related to this topic. Thanks!
 
Some latching footswitches don't sit as high when latched and releasing it feels different from setting it.

Personally I would make the DAW listen to the switch instead. If the switch is on, set the parameter in the DAW to high and the reverse... but I get why you don't want that.
 
Last edited:
Code:
//************LIBRARIES USED**************
  // 'include the Bounce library for 'de-bouncing' switches -- removing electrical chatter as contacts settle'
#include <Bounce.h> 
//'usbMIDI.h library is added automatically when code is compiled as a MIDI device'

// ******CONSTANT VALUES******** 
  // customize code behaviour here!
  const int channel = 1; // MIDI channel
  const int D_PINS = 5; // number of Digital PINS
  const int ON_Value = 127; // note-one velocity sent from buttons (should be 65 to  127)
  
  
  // 'define the pins and notes for digital events'
  const int DIGITAL_PINS[D_PINS] = {0,1,2,3,4};
  const int RED_LED_PINS[D_PINS] = {5,6,7,8,9};
  const int GREEN_LED_PINS[D_PINS] = {10,11,12,13,14};
  const int note[D_PINS] = {60,61,62,63,64};
  const int BOUNCE_TIME = 7; // 5 ms is usually sufficient
  const boolean toggled = true;
  
  
  //******VARIABLES***********
    // a data array to remember the current state of each switch
  boolean state[D_PINS];
  
  
  //************INITIALIZE LIBRARY OBJECTS**************
    
    // initialize the bounce objects 
  Bounce digital[] =   {
    Bounce(DIGITAL_PINS[0],BOUNCE_TIME), 
    Bounce(DIGITAL_PINS[1], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[2], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[3], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[4], BOUNCE_TIME)
  }; 
  
  //************SETUP**************
    void setup() {
      
      //'set a handle for returning control change messages'
      usbMIDI.setHandleControlChange(OnControlChange)
    
      //'loop to configure input pins and internal pullup resisters for digital section'
      for (int i=0;i<D_PINS;i++){
        pinMode(DIGITAL_PINS[i], INPUT_PULLUP);
      }
    }
  
  //************LOOP**************
    void loop() {
      getDigitalData();
      while (usbMIDI.read()) {
        //' controllers must call .read() to keep the queue clear even if they are not responding to MIDI'
      }
    }
  
  
  //************DIGITAL SECTION**************
    void getDigitalData(){
      for (int i=0;i<D_PINS;i++){
        digital[i].update();
        if (digital[i].fallingEdge() || digital[i].risingEdge()) {
          if (state[i]) {
            usbMIDI.sendControlChange(note[i], 0, channel);  
          }else{
            usbMIDI.sendControlChange(note[i], ON_Value, channel);  
          }
          // state[i] = !state[i] // 'commented out when using control change messaeges from DAW to set current state'
        }
      }
    } 
  
  
  void OnControlChange(byte channel, byte controller, byte value) {
    // add channel check?
      for (int i = 0; i < D_PINS ; i++){
        if (MIDI_CC_NUMS[i] = controller) {
          if (value >= 64) {
            digitalWrite(GREEN_LED_PINS[i], HIGH);
            digitalWrite(RED_LED_PINS[i], LOW); //'receiving >64 turns green on and red off'
            state[i] = true;
          }else{
            digitalWrite(GREEN_LED_PINS[i], LOW);
            digitalWrite(RED_LED_PINS[i], HIGH); //'receiving <64 turns red on and green off'
            state[i] = false;
          } // 'if not the controller for i then skip this loop'    
        }
      }
  }

This is code I put together yesterday without a compiler so it will likely have errors and may not work at all.

I was going to test it at last night but something came up and it could be a couple days before I'm anywhere I can test this out easily. So perhaps you could try it for me and report back if it compiled and whether it work or what's not working about it.

If I've not made any fundamental errors this should toggle between two CC values (one of which is a hard zero in this code) without caring what state the switch was in last.

In this configuration it will only work if your DAW is echoing CC messages and then only if I didn't muck up the OnControlChange() function.

If I did, comment out where it's handle is called and remove the comment tag ('//') in front of this line:

// state = !state // 'commented out when using control change messaeges from DAW to set current state'

This is the implementation of my first suggestion where the value is toggled by the code that switches rather than by the DAW's confirmation.
 
...In this configuration it will only work if your DAW is echoing CC messages and then only if I didn't muck up the OnControlChange() function.

If I did [muck up the DAW check thing], comment out where it's handle is called...

Here's the bit you need to comment out if the DAW thing doesn't work to set the toggle state.


// usbMIDI.setHandleControlChange(OnControlChange)

Alternately you can leave the handler run but comment out the two lines where state is set true or false.
 
Last edited:
A few problems I found, some small syntax errors which I fixed,

but overall, I changed the pin numbers to match the pins my device uses. I also had to define MIDI_CC_NUMS which runs near the bottom of void loop.
It doesn't seem to be mentioned anywhere else.

The switches respond to the DAW once, and then the value stays the same and doesn't even alternate, so I commented in and out the lines you suggested, and it functions but doesn't respond to midiRead.

The LEDs are not working at all. I will try and see if i can tinker with the code to see if I can get them to work.
 
The LEDs are at least in part from failing to put the pins in output mode.

It was optimistic to think it would be close but it was in part to make it easier to get at from a machine I can get Teensyduino on...

I'm looking at it now...

...later...


The bulk of the problem was leaving notes[] as the array to store D1 values. ...hopefully it's getting close
Code:
//************LIBRARIES USED**************
  // 'include the Bounce library for 'de-bouncing' switches -- removing electrical chatter as contacts settle'
#include <Bounce.h> 
//'usbMIDI.h library is added automatically when code is compiled as a MIDI device'

// ******CONSTANT VALUES******** 
  // customize code behaviour here!
  const int channel = 1; // MIDI channel
  const int D_PINS = 5; // number of Digital PINS
  const int ON_Value = 127; // note-one velocity sent from buttons (should be 65 to  127)
  
  
  // 'define the pins and notes for digital events'
  const int DIGITAL_PINS[D_PINS] = {0,1,2,3,4};
  const int RED_LED_PINS[D_PINS] = {5,6,7,8,9};
  const int GREEN_LED_PINS[D_PINS] = {10,11,12,13,14};
  const int MIDI_CC_NUMS[D_PINS] = {60,61,62,63,64};
  const int BOUNCE_TIME = 7; // 5 ms is usually sufficient
  const boolean toggled = true;
  
  
  //******VARIABLES***********
    // a data array to remember the current state of each switch
  boolean state[D_PINS];
  
  
  //************INITIALIZE LIBRARY OBJECTS**************
    
    // initialize the bounce objects 
  Bounce digital[] =   {
    Bounce(DIGITAL_PINS[0],BOUNCE_TIME), 
    Bounce(DIGITAL_PINS[1], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[2], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[3], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[4], BOUNCE_TIME)
  }; 
  
  //************SETUP**************
    void setup() {
      
      //'set a handle for returning control change messages'
      usbMIDI.setHandleControlChange(OnControlChange);
    
      //'loop to configure input pins and internal pullup resisters for digital section'
      for (int i=0;i<D_PINS;i++){
        pinMode(DIGITAL_PINS[i], INPUT_PULLUP);
        pinMode(RED_LED_PINS[i], OUTPUT);
        pinMode(GREEN_LED_PINS[i], OUTPUT);
      }
    }
  
  //************LOOP**************
    void loop() {
      getDigitalData();
      while (usbMIDI.read()) {
        //' controllers must call .read() to keep the queue clear even if they are not responding to MIDI'
      }
    }
  
  
  //************DIGITAL SECTION**************
    void getDigitalData(){
      for (int i=0;i<D_PINS;i++){
        digital[i].update();
        if (digital[i].fallingEdge() || digital[i].risingEdge()) {
          if (state[i]) {
            usbMIDI.sendControlChange(MIDI_CC_NUMS[i], 0, channel);  
          }else{
            usbMIDI.sendControlChange(MIDI_CC_NUMS[i], ON_Value, channel);  
          }
          // state[i] = !state[i] // 'commented out when using control change messaeges from DAW to set current state'
        }
      }
    } 
  
  
  void OnControlChange(byte channel, byte controller, byte value) {
    // add channel check?
      for (int i = 0; i < D_PINS ; i++){
        if (MIDI_CC_NUMS[i] == controller) {
          if (value >= 64) {
            digitalWrite(GREEN_LED_PINS[i], HIGH);
            digitalWrite(RED_LED_PINS[i], LOW); //'receiving >64 turns green on and red off'
            state[i] = true;
          }else{
            digitalWrite(GREEN_LED_PINS[i], LOW);
            digitalWrite(RED_LED_PINS[i], HIGH); //'receiving <64 turns red on and green off'
            state[i] = false;
          } // 'if not the controller for i then skip this loop'    
        }
      }
  }
 
Last edited:
Tested with a ground wire on pins 0 to 3 and the toggle worked with the negated assignment uncommented (and missing semicolon added and single equal sign changed to assignment double).

Don't have hardware for LED testing ...

The LEDs will not work if the CC feedback system isn't working as that's where the output pins are set for now.
 
Last edited:
Hey, this is really close now. The LEDs are working (mine are wired back to front, so swapping "HIGH" and "LOW" in this code gives the correct result). The switches and LEDs are responding to midiRead and the switch now sends the opposite of what it was last sent by the DAW (which means one switch can send 127 twice in a row :D )

However, the led's do not change when the switch has been pressed, only when they receive midi. So I can click the on-screen control on and off, and the lights will change. but when I click the physical button, the control does change on the screen, but the led doesn't change. weird.
 
Well its all based on the assumption that the daw sends back confirmation... the whole point of not just letting the switch state determine off and on.

But it could be I've messed up the logic on state values. Not sure what your saying about wiring them wrong. You assign which pins are which in the code.
 
However, the led's do not change when the switch has been pressed, only when they receive midi. So I can click the on-screen control on and off, and the lights will change. but when I click the physical button, the control does change on the screen, but the led doesn't change. weird.
I assumed you have MIDI feedback available and active in your software.

But I think I see how it should work if feedback is not running... but I have to work now...
 
To have it read changes from software but not be dependant on feedback otherwise we just need to leave the toggle code active but still allow the state to be set in the onControlChange().


I think that means we just take the comments off and put the semicolon where it belongs
Code:
//************DIGITAL SECTION**************
    void getDigitalData(){
      for (int i=0;i<D_PINS;i++){
        digital[i].update();
        if (digital[i].fallingEdge() || digital[i].risingEdge()) {
          if (state[i]) {
            usbMIDI.sendControlChange(MIDI_CC_NUMS[i], 0, channel);  
          }else{
            usbMIDI.sendControlChange(MIDI_CC_NUMS[i], ON_Value, channel);  
          }
          [COLOR="#FF0000"]state[i] = !state[i] ;[/COLOR] // ' I guess you want this to run even with the OnControlChange() code also setting the state!
        }
      }
    }
The only real weakness is that if the CC on/off message is missed by the software the display on the board will be out of sync whereas if you are getting feedback it would only change if the message is received but then you have to not change the state in until the message is returned.



I'm concerned about your statement " ...mine are wired back to front, so swapping "HIGH" and "LOW" in this code gives the correct result." I'm not sure about how that could be.

If you want them to light the opposite way ('Red=On' and 'Green=Off') then go ahead and swap HIGH and LOW and change the comments while you're there.

But if the lights are displaying reversed it can't be wiring as you just have to have the pin assignments correct.
Code:
  // 'define the pins and [COLOR="#FF0000"]CC numbers [/COLOR]for digital events'
  const int DIGITAL_PINS[D_PINS] = {0,1,2,3,4};
[COLOR="#FF0000"]  const int RED_LED_PINS[D_PINS] = {5,6,7,8,9};
  const int GREEN_LED_PINS[D_PINS] = {10,11,12,13,14};[/COLOR]
  const int MIDI_CC_NUMS[D_PINS] = {60,61,62,63,64};
  const int BOUNCE_TIME = 7; // 5 ms is usually sufficient
  [I]// const boolean toggled = true;// delete this line it's leftover garbage![/I]

-Edit- do you mean you have them wired to hot and are sinking to turn them on? I didn't consider that!
 
Last edited:
Yes the leds are wired hot. It's an unfortunate side effect of the switches I'm using with the led rings. They have a common anode.

I did have midi feedback running, and it was working when clicking on screen controls, but there seemed to be a problem when pressing the physical button. I can play around with it again if you think there is something I am missing? I will try activating the state !state concurrently.
 
I don't imagine the LED thing is a problem but is worth a note in the code so anyone working on the code down the road understands why.

Have you tried the code with state = !state ; active as well as the OnControlChange() call left active? It should toggle regardless of whether and when it hears back from the software.

There may be an issue at first use as the software will only have sync'd to the hardware if it's sent a CC. So you might want the setup code to fire CC messages with D2 = 0 so that if you plug the pedal in with the software running it will turn off any effect matched to one of its CCs but at least it would be in sync.
 
Forgot to switch the LEDs with the toggle...

Something like this
Code:
  //************DIGITAL SECTION**************
    void getDigitalData(){
      for (int i=0;i<D_PINS;i++){
        digital[i].update();
        if (digital[i].fallingEdge() || digital[i].risingEdge()) {
          if (state[i]) {
            usbMIDI.sendControlChange(MIDI_CC_NUMS[i], 0, channel);  
          }else{
            usbMIDI.sendControlChange(MIDI_CC_NUMS[i], ON_Value, channel);  
          }
           state[i] = !state[i] ;
           
            digitalWrite(RED_LED_PINS[i], !state[i]);
            digitalWrite(GREEN_LED_PINS[i], state[i]);
        }
      }
    }

Move the negation (!) from red to green if I have this backwards (which would not be surprising).
 
Ok, so now the CCs are being sent perfectly. No issues. The LEDs are almost right. They are working when the onscreen control changes or when pressing the button, but sometimes the LED won't change until a second press, which then makes the LEDs opposite to their desired state (red when it should be green etc).

Here's what happens:
-Program initialises
-Midi sent to LEDs and all lights change to correct colours
-Press Button
One of two things happen
a. The light changed from red to green immediately as it should.
b. The light stays the same despite the correct midi being sent, and then changes when pressed a second time, making the colours opposite to what they should be from then until a patch/song change which corrects the midi value. It gets stuck in an alternation rather than midi read. I assume that's because we included a state change rather than sticking with midi read. It's very curious.
 
Code:
//************LIBRARIES USED**************
  // 'include the Bounce library for 'de-bouncing' switches -- removing electrical chatter as contacts settle'
#include <Bounce.h> 
//'usbMIDI.h library is added automatically when code is compiled as a MIDI device'

// ******CONSTANT VALUES******** 
  // customize code behaviour here!
  const int channel = 1; // MIDI channel
  const int D_PINS = 5; // number of Digital PINS
  const int ON_Value = 127; // note-one velocity sent from buttons (should be 65 to  127)
  
  
  // 'define the pins and notes for digital events'
  const int DIGITAL_PINS[D_PINS] = {0,1,2,3,4};
  const int RED_LED_PINS[D_PINS] = {6,19,8,10,17};
  const int GREEN_LED_PINS[D_PINS] = {7,20,9,16,18};
  const int MIDI_CC_NUMS[D_PINS] = {60,61,62,63,64};
  const int BOUNCE_TIME = 7; // 5 ms is usually sufficient
  const boolean toggled = true;
  
  
  //******VARIABLES***********
    // a data array to remember the current state of each switch
  boolean state[D_PINS];
  
  
  //************INITIALIZE LIBRARY OBJECTS**************
    
    // initialize the bounce objects 
  Bounce digital[] =   {
    Bounce(DIGITAL_PINS[0],BOUNCE_TIME), 
    Bounce(DIGITAL_PINS[1], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[2], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[3], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[4], BOUNCE_TIME)
  }; 
  
  //************SETUP**************
    void setup() {
      
      //'set a handle for returning control change messages'
      usbMIDI.setHandleControlChange(OnControlChange);
    
      //'loop to configure input pins and internal pullup resisters for digital section'
      for (int i=0;i<D_PINS;i++){
        pinMode(DIGITAL_PINS[i], INPUT_PULLUP);
        pinMode(RED_LED_PINS[i], OUTPUT);
        pinMode(GREEN_LED_PINS[i], OUTPUT);
      }
    }
  
  //************LOOP**************
    void loop() {
      getDigitalData();
      while (usbMIDI.read()) {
        //' controllers must call .read() to keep the queue clear even if they are not responding to MIDI'
      }
    }
  
  
  //************DIGITAL SECTION**************
    void getDigitalData(){
      for (int i=0;i<D_PINS;i++){
        digital[i].update();
        if (digital[i].fallingEdge() || digital[i].risingEdge()) {
          if (state[i]) {
            usbMIDI.sendControlChange(MIDI_CC_NUMS[i], 0, channel);  
          }else{
            usbMIDI.sendControlChange(MIDI_CC_NUMS[i], ON_Value, channel);  
          }
           state[i] = !state[i] ;
           
            digitalWrite(RED_LED_PINS[i], !state[i]);
            digitalWrite(GREEN_LED_PINS[i], state[i]);
        }
      }
    }
  
  
  void OnControlChange(byte channel, byte controller, byte value) {
    // add channel check?
      for (int i = 0; i < D_PINS ; i++){
        if (MIDI_CC_NUMS[i] == controller) {
          if (value >= 64) {
            digitalWrite(GREEN_LED_PINS[i], LOW);
            digitalWrite(RED_LED_PINS[i], HIGH); //'receiving >64 turns green on and red off'
            state[i] = true;
          }else{
            digitalWrite(GREEN_LED_PINS[i], HIGH);
            digitalWrite(RED_LED_PINS[i], LOW); //'receiving <64 turns red on and green off'
            state[i] = false;
          } // 'if not the controller for i then skip this loop'    
        }
      }
  }

Here's what my code is looking like now with pin assignments etc. (I have re-designed by PCB and the next one will have the pins in counting order instead of all mixed up!)
 
After testing in mainstage and logic, it seems that mainstage's midi out funtion is simply broken after the cataline update. It is working perfectly well in logic pro now with this code:

Code:
//************LIBRARIES USED**************
  // 'include the Bounce library for 'de-bouncing' switches -- removing electrical chatter as contacts settle'
#include <Bounce.h> 
//'usbMIDI.h library is added automatically when code is compiled as a MIDI device'

// ******CONSTANT VALUES******** 
  // customize code behaviour here!
  const int channel = 1; // MIDI channel
  const int D_PINS = 5; // number of Digital PINS
  const int ON_Value = 127; // note-one velocity sent from buttons (should be 65 to  127)
  
  
  // 'define the pins and notes for digital events'
  const int DIGITAL_PINS[D_PINS] = {0,1,2,3,4};
  const int RED_LED_PINS[D_PINS] = {6,19,8,10,17};
  const int GREEN_LED_PINS[D_PINS] = {7,20,9,16,18};
  const int MIDI_CC_NUMS[D_PINS] = {60,61,62,63,64};
  const int BOUNCE_TIME = 7; // 5 ms is usually sufficient
  const boolean toggled = true;
  
  
  //******VARIABLES***********
    // a data array to remember the current state of each switch
  boolean state[D_PINS];
  
  
  //************INITIALIZE LIBRARY OBJECTS**************
    
    // initialize the bounce objects 
  Bounce digital[] =   {
    Bounce(DIGITAL_PINS[0],BOUNCE_TIME), 
    Bounce(DIGITAL_PINS[1], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[2], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[3], BOUNCE_TIME),
    Bounce(DIGITAL_PINS[4], BOUNCE_TIME)
  }; 
  
  //************SETUP**************
    void setup() {
      
      //'set a handle for returning control change messages'
      usbMIDI.setHandleControlChange(OnControlChange);
    
      //'loop to configure input pins and internal pullup resisters for digital section'
      for (int i=0;i<D_PINS;i++){
        pinMode(DIGITAL_PINS[i], INPUT_PULLUP);
        pinMode(RED_LED_PINS[i], OUTPUT);
        pinMode(GREEN_LED_PINS[i], OUTPUT);
      }
    }
  
  //************LOOP**************
    void loop() {
      getDigitalData();
      while (usbMIDI.read()) {
        //' controllers must call .read() to keep the queue clear even if they are not responding to MIDI'
      }
    }
  
  
  //************DIGITAL SECTION**************
    void getDigitalData(){
      for (int i=0;i<D_PINS;i++){
        digital[i].update();
        if (digital[i].fallingEdge() || digital[i].risingEdge()) {
          if (state[i]) {
            usbMIDI.sendControlChange(MIDI_CC_NUMS[i], 0, channel);  
          }else{
            usbMIDI.sendControlChange(MIDI_CC_NUMS[i], ON_Value, channel);  
          }
           state[i] = !state[i] ;
           
           // digitalWrite(RED_LED_PINS[i], !state[i]);
            //digitalWrite(GREEN_LED_PINS[i], state[i]);
        }
      }
    }
  
  
  void OnControlChange(byte channel, byte controller, byte value) {
    // add channel check?
      for (int i = 0; i < D_PINS ; i++){
        if (MIDI_CC_NUMS[i] == controller) {
          if (value >= 64) {
            digitalWrite(GREEN_LED_PINS[i], LOW);
            digitalWrite(RED_LED_PINS[i], HIGH); //'receiving >64 turns green on and red off'
            state[i] = true;
          }else{
            digitalWrite(GREEN_LED_PINS[i], HIGH);
            digitalWrite(RED_LED_PINS[i], LOW); //'receiving <64 turns red on and green off'
            state[i] = false;
          } // 'if not the controller for i then skip this loop'    
        }
      }
  }

And now how do we use pin 5 as a CC controller as well as a press-and-hold to change modes? also the single LED for the pin 5 switch is on pin 21
 
Ok, so now the CCs are being sent perfectly. No issues. The LEDs are almost right. They are working when the onscreen control changes or when pressing the button, but sometimes the LED won't change until a second press, which then makes the LEDs opposite to their desired state (red when it should be green etc).

Here's what happens:
-Program initialises
-Midi sent to LEDs and all lights change to correct colours
-Press Button
One of two things happen
a. The light changed from red to green immediately as it should.
b. The light stays the same despite the correct midi being sent, and then changes when pressed a second time, making the colours opposite to what they should be from then until a patch/song change which corrects the midi value. It gets stuck in an alternation rather than midi read. I assume that's because we included a state change rather than sticking with midi read. It's very curious.

This sound's like I have the HIGH/LOW backwards as I thought I might. Try this:
Code:
            digitalWrite(RED_LED_PINS[i], state[i]);
            digitalWrite(GREEN_LED_PINS[i], !state[i]);

The exclamation point means negate or flip the truth values... I flipped the wrong one
 
...And now how do we use pin 5 as a CC controller as well as a press-and-hold to change modes? also the single LED for the pin 5 switch is on pin 21

Ah... well that's outside this thread topic isn't it ;)

The logic isn't all that hard. If there is a risingEdge within x milliseconds after the fallingEdge change the mode (recall with monentary-on switch and a pullup the fallingEdge is the downstroke)
If the risingEdge is within the threshold listen for the next fallingEdge and the elapsed time will be the period of one beat. 1/24 of that is the time between clocks.

A clock signal can be sent long enough for the DAW to sync to it or you can send CC values for start and stop if that's what you want.

Reading the beat from the clock signal coming back is also mostly easy except for finding the beat amount 24 clock signals every quarter note...
 
Last edited:
This sound's like I have the HIGH/LOW backwards as I thought I might. Try this:
Code:
            digitalWrite(RED_LED_PINS[i], state[i]);
            digitalWrite(GREEN_LED_PINS[i], !state[i]);

The exclamation point means negate or flip the truth values... I flipped the wrong one

Works perfectly :cool:
 
Status
Not open for further replies.
Back
Top