Help Receiving MIDI clock for tempo LED

Status
Not open for further replies.
Could probably be causing issues, onClock shouldn’t have anything in its parenthesis and if you delete that it’ll clear up the error.
Code:
 void onClock([COLOR="#FF0000"]byte channel, byte note, byte velocity[/COLOR]) {
  if (!bankSelect){
    if (ClockCount<=3){
      digitalWrite(MODE_TAP_LED_PIN, LED_ON);
    }else{
      digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
    }
  }
  ++ClockCount = ClockCount%24;
}
 
I'm having no luck with the plan simple code from post 15. I am using teensy 2.0 - changing LED pin to 11.
Using windows, mac, Ableton, Logic Pro, Reaper, new Teensy etc. LED will only come on solid when clock starts - not flashing. :/
 
Thank you. I've installed and am able to control other functions so I know the teensy and miditools are both working fine. But sending clocks is doing nothing at all to my LED. :( I can't even make the LED flash with a completely brand new teensy using inbuilt LED.

EDIT: With both the full sketch and the simple sketch, I can use MIDI Tools to send a clock message which turns the LED on solid - but the led does not flash with multiple clocks and will not turn off regardless of any other clock messages it receives.

When I use midi monitor to see clocks coming in, it's a constant stream - about 24 per second. Is that correct? I can see clock start and clock stop events in the monitor as well. LED still just comes on initially and stays on infinitely.
 
Last edited:
I can confirm that it does function with the code from post #15, fresh upload to a Teensy 3.6 with MIDI Tools, the only modification I did the code is fixing the onClock parenthesis so it's actually defined correctly.
Screen Shot 2019-11-18 at 12.16.14 AM.jpg
 
Thank you for the confirmation. Could a clarify which buttons you're pressing in miditools? I pressed start and then started the clock. I might use another teensy.
 
Start on timing clock with 100 BPM, I also confirmed that I can do it manually by just sending timing clocks.
 
New teensy 2.0 - nothing connected - just using pin 11 for led. I press start and led just turns on solid. This is a brand new install on a different PC than I was using just to be sure. Is it a teensy 2,0 issue possibly?
 
I wouldn't think so, it's something that's pretty basic so it shouldn't be broken between devices.
 
I wouldn't think so, it's something that's pretty basic so it shouldn't be broken between devices.

Well, I've just tried with my original full function device (with all the switches etc), 2x brand new Teensy 2.0 and one Teensy 2.0++

All the same effect - just a solid led when clicking start or sending individual clocks. No change.
Code:
int ledPin = 6;
int ClockCount; 

void setup() {
  pinMode(ledPin, OUTPUT);
  usbMIDI.setHandleSongPosition(onSongPosition);
  usbMIDI.setHandleClock(onClock);
  usbMIDI.setHandleStart(onStart);
}

void loop() {
  usbMIDI.read();
}


void onClock() {
  if (ClockCount<=3){
    digitalWrite(ledPin, HIGH);
  }else{
    digitalWrite(ledPin, LOW);
  }
  ++ClockCount = ClockCount%24;
}

void onStart(){
  ClockCount = 0;
}

void onSongPosition(uint16_t semiQ){
  ClockCount= semiQ*6 ; 
}

Capture.PNG
 
Perhaps this line is causing an issue and not actually incrementing on the 2.0 hardware:
Code:
++ClockCount = ClockCount%24;
This is the only line that could stop it from working as far as I can tell, try replacing it with this and see if it works:
Code:
ClockCount++;
ClockCount %= 24;
 
I guess the compiler for that architecture doesn't interpret the syntax correctly.

Ummm, the syntax:
Code:
void onClock() {
  if (ClockCount<=3){
    digitalWrite(ledPin, HIGH);
  }else{
    digitalWrite(ledPin, LOW);
  }
  ++ClockCount = ClockCount%24;
}

Is incorrect C++, so you should have gotten an error with both compilers.

The correct way to write setting ClockCount would be:
Code:
  ClockCount = (ClockCount + 1) % 24;
 
So, Here is my code with VJ's little edit for the Teensy 2.0 Syntax - I've tested with DAW and I get no LED action at all. Simple sketch now working perfectly, but this large sketch has no mode flashing, and no clock flashing.
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******** 

//********** PIN DEFINITIONS
const int D_PINS = 5; // number of Digital PINS
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 MODE_TAP_PIN = 5;
const int MODE_TAP_LED_PIN = 11;


//********** MIDI DEFINITIONS
const int channel = 1; // MIDI channel
const int MODE_COUNT = 3; // number of rows of banks
//CC configuration matrix!!
const int MIDI_CC_NUMS[MODE_COUNT][D_PINS] = { //rows are banks up to MODE_COUNT
    {60,61,62,63,64},
    {65,66,67,68,69},
    {70,71,72,73,74}
};
const int TAP_CC = 15;
const int ON_Value = 127; // note-one velocity sent from buttons (should be 65 to  127)



//********** PHYSICAL DEFINITIONS 
const int BOUNCE_TIME = 30; // 5 ms is usually sufficient
const int modeThreshold = 600; // how long to hold before mode changes
const int flashOnTime = 150; // how long flashed LED is on
const int flashOffTime = 250; // how long flashed LED is off
const bool LED_ON = LOW; // LOW for active LOW wiring
const bool LED_OFF = HIGH; // HIGH for active LOW wiring


//******VARIABLES***********
// a data array to remember the current state of each switch
boolean state[MODE_COUNT][D_PINS];
elapsedMillis modeTimer,flashTimer;
boolean modeSelectActive = false;
int bank = 0 ; 
int flashcount;
int shiftUp; // keeps track of whether the mode change needs to be handled (true) or was (false)
int modeLED; // keeps track of whether LED is on without testing it..
int ClockCount; // for tempo tracking
boolean bankSelect = false;
//************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)
}; 
Bounce modeTap = Bounce(MODE_TAP_PIN, BOUNCE_TIME);

//************SETUP**************
void setup() {
  
  //'set a handle for returning MIDI messages'
  usbMIDI.setHandleControlChange(OnControlChange);
  usbMIDI.setHandleSongPosition(onSongPosition);
  usbMIDI.setHandleClock(onClock);
  usbMIDI.setHandleStart(onStart);
  //'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);
  }
  pinMode(MODE_TAP_PIN, INPUT_PULLUP);
  pinMode(MODE_TAP_LED_PIN, OUTPUT);
  digitalWrite(MODE_TAP_LED_PIN, LED_OFF);


  for (int i=0;i<D_PINS;i++){
    digitalWrite(GREEN_LED_PINS[i], LED_OFF);   // - GREEN OFF
    digitalWrite(RED_LED_PINS[i], LED_ON);   // - RED ON
  }
}

//************LOOP**************
void loop() {
  getDigitalData();
  getModeTap();
  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[bank][i]) {
        usbMIDI.sendControlChange(MIDI_CC_NUMS[bank][i], 0, channel);  
        digitalWrite(RED_LED_PINS[i], LED_ON);
        digitalWrite(GREEN_LED_PINS[i], LED_OFF);
      }else{
        usbMIDI.sendControlChange(MIDI_CC_NUMS[bank][i], ON_Value, channel);  
        digitalWrite(RED_LED_PINS[i], LED_OFF);
        digitalWrite(GREEN_LED_PINS[i], LED_ON);
      }
      state[bank][i] = !state[bank][i] ;
    }
  }
}

//************MODE/TAP SECTION**************
void getModeTap(){
  modeTap.update();
  if (modeTap.fallingEdge()) {
    usbMIDI.sendControlChange(TAP_CC, ON_Value, channel);  // can we send any D2 for Tap?
    modeTimer = 0;
    shiftUp = true;
  }    
  if (modeTap.risingEdge()){
    shiftUp = false;
  }
  if (modeTimer>modeThreshold && shiftUp) {
    shiftUp = false;
    bankSelect = true; 
    bank++;
    bank = bank%MODE_COUNT;
    for (int i = 0; i < D_PINS ; i++){
      digitalWrite(GREEN_LED_PINS[i], !state[bank][i]);
      digitalWrite(RED_LED_PINS[i], state[bank][i]); 
    }
    flashcount = bank + 1;
    flashTimer = 0 ;
    // set counter of flashes 'owed' -- count them down after main part
  }
  // if flashcount > 0 flash and start counter
  if (flashcount){
    if (flashTimer>(flashOnTime+flashOffTime)){ 
        flashcount-- ;// decrement flashcount
        flashTimer = 0;
        if (modeLED){
          modeLED = false;
          digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
        }
      }else{
        if (modeLED == false && flashTimer>flashOnTime){
          modeLED = true;
          digitalWrite(MODE_TAP_LED_PIN, LED_ON);
        }
    }
  }else{
    //bankSelect = false;
  }
}

void OnControlChange(byte rcvChannel, byte controller, byte value) {
  if (rcvChannel == channel){
    for (int i = 0; i < D_PINS ; i++){
      if (MIDI_CC_NUMS[bank][i] == controller) {
        if (value >= 64) {
          digitalWrite(GREEN_LED_PINS[i], LED_ON);
          digitalWrite(RED_LED_PINS[i], LED_OFF); //'receiving >64 turns green on and red off'
          state[bank][i] = true;
        }else{
          digitalWrite(GREEN_LED_PINS[i], LED_OFF);
          digitalWrite(RED_LED_PINS[i], LED_ON); //'receiving <64 turns red on and green off'
          state[bank][i] = false;
        } // 'if not the controller for i then skip this loop'    
      }
    }
  }
}

void onClock() {
  if (!bankSelect){
    if (ClockCount<=3){
      digitalWrite(MODE_TAP_LED_PIN, LED_ON);
    }else{
      digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
    }
  }
  ++ClockCount;
  ClockCount%= 24;
}

void onStart(){
  ClockCount = 0;
}


void onSongPosition(uint16_t semiQ){
  ClockCount= semiQ*6 ; 
}
 
I believe this should be uncommented:
Code:
//bankSelect = false;
And this:
Code:
++ClockCount;
Should be this:
Code:
 ClockCount++;

I believe that should get the tempo LED working, I’m not sure about the mode LED though.
 
Could probably be causing issues, onClock shouldn’t have anything in its parenthesis and if you delete that it’ll clear up the error.
Code:
 void onClock([COLOR="#FF0000"]byte channel, byte note, byte velocity[/COLOR]) {
  if (!bankSelect){
    if (ClockCount<=3){
      digitalWrite(MODE_TAP_LED_PIN, LED_ON);
    }else{
      digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
    }
  }
  ++ClockCount = ClockCount%24;
}
I've been downright dangerous lately.

I'd like to blame cut and paste programming but that's just admitting laziness.

And the increment thing is stupid... even if it works it's unnecessarily obtuse compared with ClockCount = (ClockCount + 1) % 24...

I didn't really expect others to debug this for me...

I'm not averse to starting over on the tap code... it felt a bit if a kludge and if there's a clearly better way to keep track the logic of the user interface I'm open to that.

I've not read all this yet but thanks to both of you for the help. Sorry for wasting everyone's time on the carelessness stuff.

@Aussie_CrocHunter
Great to see you thrashing through this... I'm sure you're learning a bunch even if it's frustrating.

I should have a few hours to work this out this week.

I recall at 2:30 am that I was unable to keep the potential sequence of state and pin changes in my addled noodle to identify where in the code one needs to supress flash for tempo and when can enable it again.

Perhaps with fresher brain-cells and judicious use of Serial.print() I can get a Boolean that supresses tempo flashes when appropriate -- which currently I'm assuming is after the tap button is held past the mode timing threshold and after the final flash indicating the bank selection (with a possible additional period to avoid obscuring the bank flash count with the next tempo flash).
 
Last edited:
Oddson, I think you've done very well and I'm very appreciative. You're exactly right about me learning lots! it's been fantastic.
I'll try the little edits VJ suggested and see how it works and what parameters might need changing.
 
I'm rethinking what's needed in flashing behaviour when Tap/Bank button is used and posting this to confirm whether my intuition on the user interface agrees with yours -- and of anyone else with two-cents (CND$0.03).



We have to turn tempo-flash off at the falling edge so that the user can see the flashes distinctly when the threshold is passed. Otherwise a flash right before the threshold was reached could read as having selected the bank above the actual.

When the user does pass the threshold we need it off until the final flash has been perceived as the end of as sequence before resuming the tempo. Otherwise a flash right after the threshold was reached could read as having selected the bank above the actual.

But we don't want to disrupt the tempo sequence if the threshold value is never passed. So as it needs to be suppressed from the fallingEdge we then restore it in time if you are setting the tempo.

So I think instead of a Boolean for bank selection it should be one for tempoFlashActive and swap the T/F back and avoid the negation in the test statement.

Set it false on fallingEdge and true again on rising if less than threshold. Then the tricky bit is if the threshold is passed.

For delaying resumption of the tempo I'm wondering if allowing the flash counter to decrement into the negatives by delaying the escape below zero (with a separate conditional for positive values for the LED display) and then restoring tempoFlashActive = true when the inequality goes below the negative limit. I think it will still work as it's set positive again next time the bank is selected.

If this works the values for how long the flash remains lighted and for how long to supress flashing after bank selection will be tempo dependant (which I think will feel correct) and won't need any additional timers.

But we have to watch for stopping the sequence in the ON setting unintentionally whenever we turn tempo updating off.

Go ahead with trying to get the existing code functioning but don't bang your head too much as it's still likely to contain some frustrations for you (and me). But if you DO get it working with the original behaviour you can still add the above change later.
 
Last edited:
That logic sounds good to me. I haven't been able to get the LED to light at all so far - even when commenting out all the clock-related lines and just leaving the bank select lines. The LED is definitely working with other sketches though so it's not hardware.
If you think the above suggestion will be a better implementation, then by all means I think go for it.
 
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******** 

//********** PIN DEFINITIONS
const int D_PINS = 5; // number of Digital PINS in main group (excl tap/bank)
const int DIGITAL_PINS[D_PINS] = {0,1,[COLOR="#FF0000"]2[/COLOR],3,4}; // pins to switchs 
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 MODE_TAP_PIN = [COLOR="#FF0000"]5[/COLOR];  // momentary switch to set tempo and select bank
const int MODE_TAP_LED_PIN = [COLOR="#FF0000"]11[/COLOR]; // pin to LED for tempo/bank indicator


//********** MIDI DEFINITIONS
const int channel = 1; // MIDI channel
const int MODE_COUNT = 3; // number of rows of banks
//CC configuration matrix!!
const int MIDI_CC_NUMS[MODE_COUNT][D_PINS] = { //rows are banks up to MODE_COUNT
    {60,61,62,63,64},
    {65,66,67,68,69},
    {70,71,72,73,74}
};
const int TAP_CC = 15;
const int ON_Value = 127; // note-one velocity sent from buttons (should be 65 to  127)



//********** PHYSICAL DEFINITIONS 
const int BOUNCE_TIME = 30; // 5 ms is usually sufficient
const int modeThreshold = 600; // how long to hold before mode changes
const int flashOnTime = 150; // how long flashed LED is on
const int flashOffTime = 250; // how long flashed LED is off
const bool LED_ON = [COLOR="#FF0000"]LOW[/COLOR]; // LOW for active LOW wiring
const bool LED_OFF = [COLOR="#FF0000"]HIGH[/COLOR]; // HIGH for active LOW wiring
const int onClocks = 4; // number of clock messages with LED on for tempo flash
const int waitClocks = 10; // number of clocks after bank change before tempo flash resumes

//******VARIABLES***********
// a data array to remember the current state of each switch
boolean state[MODE_COUNT][D_PINS];
elapsedMillis modeTimer,flashTimer;
boolean modeSelectActive = false;
int bank = 0 ; 
int flashcount;
int shiftUp; // keeps track of whether the mode change needs to be handled (true) or was (false)
int modeLED; // keeps track of whether LED is on without testing it..
int ClockCount; // for tempo tracking
boolean tempoFlashOn = true;// tempo flash defaults to on

//************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)
}; 
Bounce modeTap = Bounce(MODE_TAP_PIN, BOUNCE_TIME);

//************SETUP**************
void setup() {
  
  //'set a handle for returning MIDI messages'
  usbMIDI.setHandleControlChange(OnControlChange);
  usbMIDI.setHandleSongPosition(onSongPosition);
  usbMIDI.setHandleClock(onClock);
  usbMIDI.setHandleStart(onStart);
  //'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);
  }
  pinMode(MODE_TAP_PIN, INPUT_PULLUP);
  pinMode(MODE_TAP_LED_PIN, OUTPUT);
  digitalWrite(MODE_TAP_LED_PIN, LED_OFF);


  for (int i=0;i<D_PINS;i++){
    digitalWrite(GREEN_LED_PINS[i], LED_OFF);   // - GREEN OFF
    digitalWrite(RED_LED_PINS[i], LED_ON);   // - RED ON
  }
}

//************LOOP**************
void loop() {
  getDigitalData();
  getModeTap();
  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[bank][i]) {
        usbMIDI.sendControlChange(MIDI_CC_NUMS[bank][i], 0, channel);  
        digitalWrite(RED_LED_PINS[i], LED_ON);
        digitalWrite(GREEN_LED_PINS[i], LED_OFF);
      }else{
        usbMIDI.sendControlChange(MIDI_CC_NUMS[bank][i], ON_Value, channel);  
        digitalWrite(RED_LED_PINS[i], LED_OFF);
        digitalWrite(GREEN_LED_PINS[i], LED_ON);
      }
      state[bank][i] = !state[bank][i] ;
    }
  }
}

//************MODE/TAP SECTION**************
void getModeTap(){
  modeTap.update();
  if (modeTap.fallingEdge()) {
    usbMIDI.sendControlChange(TAP_CC, ON_Value, channel);  
    modeTimer = 0;
    shiftUp = true;
    tempoFlashOn = false; 
  }    
  if (modeTap.risingEdge()){
    shiftUp = false;
    tempoFlashOn = true; 
  }
  if (modeTimer>modeThreshold && shiftUp) {
    shiftUp = false;
    bank++;
    bank = bank%MODE_COUNT;
    for (int i = 0; i < D_PINS ; i++){
      digitalWrite(GREEN_LED_PINS[i], !state[bank][i]);
      digitalWrite(RED_LED_PINS[i], state[bank][i]); 
    }
    flashcount = bank + 1;
    flashTimer = 0 ;
    // set counter of flashes 'owed' -- count them down after main part
  }
  // if flashcount > 0 flash and start counter
  if (flashcount>= -1*waitClocks){
    if (flashcount> 0){
      if (flashTimer>(flashOnTime+flashOffTime)){ 
        flashcount-- ;// decrement flashcount
        flashTimer = 0;
        if (modeLED){
          modeLED = false;
          digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
        }
      }else{
        if (modeLED == false && flashTimer>flashOnTime){
          modeLED = true;
          digitalWrite(MODE_TAP_LED_PIN, LED_ON);
        }
      }
    }
  }else{
    tempoFlashOn = true;
  }
}

void OnControlChange(byte rcvChannel, byte controller, byte value) {
  if (rcvChannel == channel){
    for (int i = 0; i < D_PINS ; i++){
      if (MIDI_CC_NUMS[bank][i] == controller) {
        if (value >= 64) {
          digitalWrite(GREEN_LED_PINS[i], LED_ON);
          digitalWrite(RED_LED_PINS[i], LED_OFF); //'receiving >64 turns green on and red off'
          state[bank][i] = true;
        }else{
          digitalWrite(GREEN_LED_PINS[i], LED_OFF);
          digitalWrite(RED_LED_PINS[i], LED_ON); //'receiving <64 turns red on and green off'
          state[bank][i] = false;
        } // 'if not the controller for i then skip this loop'    
      }
    }
  }
}

void onClock() {
    Serial.println(tempoFlashOn);
  if (tempoFlashOn){
    if (ClockCount<=onClocks){
      digitalWrite(MODE_TAP_LED_PIN, LED_ON);
    }else{
      digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
    }
  }
  ClockCount = (ClockCount+1)%24;
}

void onStart(){
  ClockCount = 0;
}


void onSongPosition(uint16_t semiQ){
  ClockCount= semiQ*6 ; 
}
Here are those changes or at least an attempt at them.

Make sure on any further code we exchange that you check the values in red to make sure they are changed to what you need as I need to have pin 2 as the Tap switch and the built-in as LED plus the active HIGH for LED.

I could only test sending F8 (clock) from a midi utility in pulses to check the step-trough logic but I could not test the interplay between the two-modes while a clock was running at speed as i have no DAW installed on this laptop.
 
Last edited:
Code:
  if (flashcount>= -1*waitClocks){ // counts into negative values to supress tempo flash longer
    if (flashcount> 0){ // 
      if (flashTimer>(flashOnTime+flashOffTime)){ 
[COLOR="#FF0000"]//was here??[/COLOR]
        flashTimer = 0;
        if (modeLED){
          modeLED = false;
          digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
        }
      }else{
        if (modeLED == false && flashTimer>flashOnTime){
          modeLED = true;
          digitalWrite(MODE_TAP_LED_PIN, LED_ON);
        }
      }
    }
[COLOR="#FF0000"]    flashcount-- ;// decrement flashcount [/COLOR]
  }else{
    tempoFlashOn = true;
  }


Just reviewing the code to add comments and notice the decrement is likely wrong. It looks like it won't keep decrementing past zero and so the suppression likely doesn't stop after a bank change in the previous code.

I think it belongs after outside the test for positive count values so that it keeps ticking downward for 10 (constant value stored in waitClocks) more clock signals.

I suspect the it has some other errors that effect the flash timing but don't show up unless the on and off times for mode flashes are less similar.

I think the logic got reversed during one of the swaps between active HIGH and LOW LED wiring during development.

With the change above the code might more-or-less work with just some timing oddities.


Here's the changes I've been making without a compiler that I hope to test/debug tonight
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******** 

//********** PIN DEFINITIONS
const int D_PINS = 5; // number of Digital PINS in main group (excl tap/bank)
const int DIGITAL_PINS[D_PINS] = {0,1,2,3,4}; // pins to switchs 
const int RED_LED_PINS[D_PINS] = {6,19,8,10,17}; // pins to RED LEDS
const int GREEN_LED_PINS[D_PINS] = {7,20,9,16,18}; // pins to GREEN LEDS
const int MODE_TAP_PIN = 5;  // momentary switch to set tempo and select bank
const int MODE_TAP_LED_PIN = 11; // pin to LED for tempo/bank indicator


//********** MIDI DEFINITIONS
const int channel = 1; // MIDI channel
const int MODE_COUNT = 3; // number of rows of banks
//CC configuration matrix - set the CC numbers (D1) for each bank/row
const int MIDI_CC_NUMS[MODE_COUNT][D_PINS] = { //rows are banks up to MODE_COUNT
    {60,61,62,63,64},
    {65,66,67,68,69},
    {70,71,72,73,74}
};
const int ON_Value = 127; // CC value (D2) sent for ON 
const int OFF_Value = 0; // CC value (D2) sent for OFF 
const int TAP_CC = 15; // CC number (D1) sent for tap events



//********** PHYSICAL DEFINITIONS 
const int BOUNCE_TIME = 30; // 5 ms is usually sufficient
const int modeThreshold = 600; // how long to hold before mode changes
const int flashOnTime = 150; // how long flashed LED is on
const int flashOffTime = 250; // how long flashed LED is off
const bool LED_ON = LOW; // LOW for active LOW wiring
const bool LED_OFF = HIGH; // HIGH for active LOW wiring
const int onClocks = 4; // number of clock messages with LED on for tempo flash
const int waitClocks = 10; // number of clocks after bank change before tempo flash resumes

//******VARIABLES***********
// a data array to remember the current state of each switch
boolean state[MODE_COUNT][D_PINS];
elapsedMillis modeTimer,flashTimer;
boolean modeSelectActive = false;
int bank = 0 ; 
int flashcount;
int shiftUp; // keeps track of whether the mode change needs to be handled (true) or was (false)
int modeLED; // keeps track of whether LED is on during mode flash without testing it..
int ClockCount; // for tempo tracking
boolean tempoFlashOn = true;// tempo flash defaults to on

//************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)
}; 
Bounce modeTap = Bounce(MODE_TAP_PIN, BOUNCE_TIME);

//************SETUP**************
void setup() {
  //'set a handle for returning MIDI messages'
  usbMIDI.setHandleControlChange(OnControlChange);
  usbMIDI.setHandleSongPosition(onSongPosition);
  usbMIDI.setHandleClock(onClock);
  usbMIDI.setHandleStart(onStart);
  //'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);
  }
  pinMode(MODE_TAP_PIN, INPUT_PULLUP); // configure tap/mode switch pin
  pinMode(MODE_TAP_LED_PIN, OUTPUT); // configure tap/mode LED pin
  digitalWrite(MODE_TAP_LED_PIN, LED_OFF); // set LED to off (needed here when active LOW)

  // 'loop to configure red and greed LED pins for default start'
  for (int i=0;i<D_PINS;i++){
    digitalWrite(GREEN_LED_PINS[i], LED_OFF);   // - GREEN OFF
    digitalWrite(RED_LED_PINS[i], LED_ON);   // - RED ON
  }
}

//************MAIN LOOP**************
void loop() {
  getMainMIDI(); // 'call for MIDI events from main switchs'
  getModeTap();  // 'call to send Tap CC and set bank selection'
  while (usbMIDI.read()) {
    //' controllers must call .read() to keep the queue clear even if they are not responding to MIDI'
  }
}


//************MAIN MIDI SECTION**************
void getMainMIDI(){
  for (int i=0;i<D_PINS;i++){
    digital[i].update();
    if (digital[i].fallingEdge() || digital[i].risingEdge()) {
      if (state[bank][i]) {
        usbMIDI.sendControlChange(MIDI_CC_NUMS[bank][i], OFF_Value, channel);  
        digitalWrite(RED_LED_PINS[i], LED_ON);
        digitalWrite(GREEN_LED_PINS[i], LED_OFF);
      }else{
        usbMIDI.sendControlChange(MIDI_CC_NUMS[bank][i], ON_Value, channel);  
        digitalWrite(RED_LED_PINS[i], LED_OFF);
        digitalWrite(GREEN_LED_PINS[i], LED_ON);
      }
      state[bank][i] = !state[bank][i] ;
    }
  }
}

//************MODE/TAP SECTION**************
void getModeTap(){
  modeTap.update();
  if (modeTap.fallingEdge()) {
    usbMIDI.sendControlChange(TAP_CC, ON_Value, channel);  
    modeTimer = 0;
    shiftUp = true;
    tempoFlashOn = false; 
  }    
  if (modeTap.risingEdge()){
    shiftUp = false;
    tempoFlashOn = true; 
  }
  if (modeTimer>modeThreshold && shiftUp) {
    shiftUp = false;
    bank++;
    bank = bank%MODE_COUNT;
    for (int i = 0; i < D_PINS ; i++){
      digitalWrite(GREEN_LED_PINS[i], !state[bank][i]);
      digitalWrite(RED_LED_PINS[i], state[bank][i]); 
    }
    flashcount = bank + 1;
    flashTimer = 0 ;
    // set counter of flashes 'owed' -- count them down after main part
  }
  // if flashcount > 0 flash and start counter
  if (flashcount>= -1*waitClocks){ // counts into negative values to supress tempo flash longer
    if (flashcount> 0){ // limit LED changes to positive values
      if (flashTimer>(flashOnTime+flashOffTime)){  // if time for next flash...
        flashTimer = 0; // reset timer
        if (!modeLED){ // ...don't set it over and over...
          modeLED = true; // update test variable
          digitalWrite(MODE_TAP_LED_PIN, LED_ON); //turn on the LED
        }
      }else{
        if (modeLED && flashTimer>flashOnTime){ // ...its on and has been on long enough
          modeLED = false;  // update test variable
          digitalWrite(MODE_TAP_LED_PIN, LED_OFF); // turn off the LED
        }
      }
    }
    flashcount-- ;// decrement flashcount (...to negative waitClocks)
  }else{ // ...when flashcount < negative waitclocks
    tempoFlashOn = true; // resume tempo flashes
  }
}


//************INCOMING CC SECTION (SET LEDS)**************
void OnControlChange(byte rcvChannel, byte controller, byte value) {
  if (rcvChannel == channel){ // filter for selected channel
    for (int i = 0; i < D_PINS ; i++){
      if (MIDI_CC_NUMS[bank][i] == controller) {
        if (value >= 64) {
          digitalWrite(GREEN_LED_PINS[i], LED_ON);
          digitalWrite(RED_LED_PINS[i], LED_OFF); //'receiving >64 turns green on and red off'
          state[bank][i] = true;
        }else{
          digitalWrite(GREEN_LED_PINS[i], LED_OFF);
          digitalWrite(RED_LED_PINS[i], LED_ON); //'receiving <64 turns red on and green off'
          state[bank][i] = false;
        } // 'if not the controller for i then skip this loop'    
      }
    }
  }
}

//************CLOCK MESSAGE SECTION**************
void onClock() {
  if (tempoFlashOn){ // if the tempo flash is not supressed...
    if (ClockCount<=onClocks){ //... then set the LED based on count
      digitalWrite(MODE_TAP_LED_PIN, LED_ON);
    }else{
      digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
    }
  }
  ClockCount = (ClockCount+1)%24; // increment count and truncate to zero on quarter notes
}

//************START MESSAGE SECTION**************
void onStart(){
  ClockCount = 0; // reset the counter when the song is restarted
}


//************START MESSAGE SECTION**************
void onSongPosition(uint16_t semiQ){
  ClockCount= semiQ*6 ; // override count with explicit position data (truncate on next clock)
}
 
Last edited:
Clock is working in this latest sketch. No mode flash though. No change at all in flash behaviour when changing modes.
Mode is still functionally switching - just not changing flash behaviour.

No errors compiling either :)
 
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******** 

//********** PIN DEFINITIONS
const int D_PINS = 5; // number of Digital PINS in main group (excl tap/bank)
const int DIGITAL_PINS[D_PINS] = {0,1,5,3,4}; // pins to switchs 
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 MODE_TAP_PIN = 2;  // momentary switch to set tempo and select bank
const int MODE_TAP_LED_PIN = 13; // pin to LED for tempo/bank indicator


//********** MIDI DEFINITIONS
const int channel = 1; // MIDI channel
const int MODE_COUNT = 3; // number of rows of banks
//CC configuration matrix!!
const int MIDI_CC_NUMS[MODE_COUNT][D_PINS] = { //rows are banks up to MODE_COUNT
    {60,61,62,63,64},
    {65,66,67,68,69},
    {70,71,72,73,74}
};
const int TAP_CC = 15;
const int ON_Value = 127; // note-one velocity sent from buttons (should be 65 to  127)



//********** PHYSICAL DEFINITIONS 
const int BOUNCE_TIME = 30; // 5 ms is usually sufficient
const int modeThreshold = 800; // how long to hold before mode changes
const int flashOnTime = 150; // how long flashed LED is on
const int flashOffTime = 250; // how long flashed LED is off
const bool LED_ON = HIGH; // LOW for active LOW wiring
const bool LED_OFF = LOW; // HIGH for active LOW wiring
const int onClocks = 4; // number of clock messages with LED on for tempo flash
const int waitflash = 2; // number of dark flash cylces after bank change before tempo flash resumes

//******VARIABLES***********
// a data array to remember the current state of each switch
boolean state[MODE_COUNT][D_PINS];
elapsedMillis modeTimer,flashTimer;
boolean modeSelectActive = false;
int bank = 0 ; 
int flashcount= (-1*waitflash); // default is effective sequence stop of flash counter
int shiftUp; // keeps track of whether the mode change needs to be handled (true) or was (false)
int modeLED; // keeps track of whether LED is on without testing it..
int ClockCount; // for tempo tracking
boolean tempoFlashOn = true;// tempo flash defaults to on

//************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)
}; 
Bounce modeTap = Bounce(MODE_TAP_PIN, BOUNCE_TIME);

//************SETUP**************
void setup() {
  
  //'set a handle for returning MIDI messages'
  usbMIDI.setHandleControlChange(OnControlChange);
  usbMIDI.setHandleSongPosition(onSongPosition);
  usbMIDI.setHandleClock(onClock);
  usbMIDI.setHandleStart(onStart);
  //'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);
  }
  pinMode(MODE_TAP_PIN, INPUT_PULLUP);
  pinMode(MODE_TAP_LED_PIN, OUTPUT);
  digitalWrite(MODE_TAP_LED_PIN, LED_OFF);


  for (int i=0;i<D_PINS;i++){
    digitalWrite(GREEN_LED_PINS[i], LED_OFF);   // - GREEN OFF
    digitalWrite(RED_LED_PINS[i], LED_ON);   // - RED ON
  }
}

//************LOOP**************
void loop() {
  getMain();
  getModeTap();
  flasher();
  while (usbMIDI.read()) {
    //' controllers must call .read() to keep the queue clear even if they are not responding to MIDI'
  }
}


//************DIGITAL SECTION**************
void getMain(){
  for (int i=0;i<D_PINS;i++){
    digital[i].update();
    if (digital[i].fallingEdge() || digital[i].risingEdge()) {
      if (state[bank][i]) {
        usbMIDI.sendControlChange(MIDI_CC_NUMS[bank][i], 0, channel);  
        digitalWrite(RED_LED_PINS[i], LED_ON);
        digitalWrite(GREEN_LED_PINS[i], LED_OFF);
      }else{
        usbMIDI.sendControlChange(MIDI_CC_NUMS[bank][i], ON_Value, channel);  
        digitalWrite(RED_LED_PINS[i], LED_OFF);
        digitalWrite(GREEN_LED_PINS[i], LED_ON);
      }
      state[bank][i] = !state[bank][i] ;
    }
  }
}

//************MODE/TAP SECTION**************
void getModeTap(){
  modeTap.update();
  if (modeTap.fallingEdge()) {
    usbMIDI.sendControlChange(TAP_CC, ON_Value, channel);  
    modeTimer = 0;
    shiftUp = true;
    tempoFlashOn = false; 
    flashcount = 0; // is this error or magic?
    Serial.println("suspend tempo on tap");
  }    
  if (modeTap.risingEdge()){
    shiftUp = false;
    if (modeTimer<modeThreshold){
      tempoFlashOn = true; 
    Serial.println("resume tempo no mode change");
    }
  }
  if (modeTimer>modeThreshold && shiftUp) {
    shiftUp = false;
    bank++;
    bank = bank%MODE_COUNT;
    for (int i = 0; i < D_PINS ; i++){
      digitalWrite(GREEN_LED_PINS[i], !state[bank][i]);
      digitalWrite(RED_LED_PINS[i], state[bank][i]); 
    }
    // set counter of flashes 'owed' -- count them down after main part
    flashcount = bank + 1;
    flashTimer = 0 ; // is this needed?
  }
}

void flasher(){
  if (flashcount>= -1*waitflash){
    if (flashcount> 0){
      if (flashTimer>(flashOnTime+flashOffTime)){ 
        flashcount-- ;// decrement flashcount
        flashTimer = 0;
        if (modeLED){
          modeLED = false;
          digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
        }
      }else{
        if (modeLED == false && flashTimer>flashOnTime){
          modeLED = true;
          digitalWrite(MODE_TAP_LED_PIN, LED_ON);
        }
      }
    }else{
      if (flashTimer>(flashOnTime+flashOffTime)){ 
        flashcount-- ;// decrement flashcount
        flashTimer = 0;
      }
    }
  }else{
    if (!tempoFlashOn){
      tempoFlashOn = true;
      Serial.println(flashcount);
      Serial.println("resume tempo after bank select");
    }
  }
}

void OnControlChange(byte rcvChannel, byte controller, byte value) {
  if (rcvChannel == channel){
    for (int i = 0; i < D_PINS ; i++){
      if (MIDI_CC_NUMS[bank][i] == controller) {
        if (value >= 64) {
          digitalWrite(GREEN_LED_PINS[i], LED_ON);
          digitalWrite(RED_LED_PINS[i], LED_OFF); //'receiving >64 turns green on and red off'
          state[bank][i] = true;
        }else{
          digitalWrite(GREEN_LED_PINS[i], LED_OFF);
          digitalWrite(RED_LED_PINS[i], LED_ON); //'receiving <64 turns red on and green off'
          state[bank][i] = false;
        } // 'if not the controller for i then skip this loop'    
      }
    }
  }
}

void onClock() {
  if (tempoFlashOn){
    if (ClockCount<=onClocks){
      digitalWrite(MODE_TAP_LED_PIN, LED_ON);
    }else{
      digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
    }
  }
  ClockCount = (ClockCount+1)%24;
}

void onStart(){
  ClockCount = 0;
}


void onSongPosition(uint16_t semiQ){
  ClockCount= semiQ*6 ; 
}
This sometimes worked... you will have to reset the constant values. Oh.. is the super long bounce time for me or you? I like it since I'm connecting wire to pin to test the tap switch but you may want to dial that back to 10 or less if you are able. That's as close as I'm getting tonight.
 
Status
Not open for further replies.
Back
Top