Forum Rule: Always post complete source code & details to reproduce any issue!
Page 2 of 3 FirstFirst 1 2 3 LastLast
Results 26 to 50 of 51

Thread: Help Receiving MIDI clock for tempo LED

  1. #26
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Location
    Florida
    Posts
    420
    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(byte channel, byte note, byte velocity) {
      if (!bankSelect){
        if (ClockCount<=3){
          digitalWrite(MODE_TAP_LED_PIN, LED_ON);
        }else{
          digitalWrite(MODE_TAP_LED_PIN, LED_OFF);
        }
      }
      ++ClockCount = ClockCount%24;
    }

  2. #27
    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. :/

  3. #28
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Location
    Florida
    Posts
    420
    If you install this program: https://mountainutilities.eu/miditools
    You can use it to manually send MIDI commands so it’s easy to troubleshoot certain things and one thing you can do it send the clocks yourself.

  4. #29
    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 by Aussie_CrocHunter; 11-18-2019 at 05:09 AM.

  5. #30
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Location
    Florida
    Posts
    420
    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.
    Click image for larger version. 

Name:	Screen Shot 2019-11-18 at 12.16.14 AM.jpg 
Views:	5 
Size:	161.9 KB 
ID:	18203

  6. #31
    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.

  7. #32
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Location
    Florida
    Posts
    420
    Start on timing clock with 100 BPM, I also confirmed that I can do it manually by just sending timing clocks.

  8. #33
    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?

  9. #34
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Location
    Florida
    Posts
    420
    I wouldn't think so, it's something that's pretty basic so it shouldn't be broken between devices.

  10. #35
    Quote Originally Posted by vjmuzik View Post
    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 ; 
    }
    Click image for larger version. 

Name:	Capture.PNG 
Views:	1 
Size:	47.5 KB 
ID:	18204

  11. #36
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Location
    Florida
    Posts
    420
    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;

  12. #37
    Great stuff! It works now You're a legend!

    EDIT: now to test the full function version which Oddson posted earlier.

  13. #38
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Location
    Florida
    Posts
    420
    I guess the compiler for that architecture doesn't interpret the syntax correctly.

  14. #39
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,365
    Quote Originally Posted by vjmuzik View Post
    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;

  15. #40
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Location
    Florida
    Posts
    420
    Well it did work for the 3.6 so I guess it’s undefined but happens to work out correctly.

  16. #41
    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 ; 
    }

  17. #42
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Location
    Florida
    Posts
    420
    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.

  18. #43
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,237
    Quote Originally Posted by vjmuzik View Post
    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(byte channel, byte note, byte velocity) {
      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 by oddson; 11-18-2019 at 06:28 PM.

  19. #44
    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.

  20. #45
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,237
    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 by oddson; 11-18-2019 at 10:21 PM.

  21. #46
    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.

  22. #47
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,237
    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};
    const int GREEN_LED_PINS[D_PINS] = {7,20,9,16,18};
    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!!
    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
    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 by oddson; 11-19-2019 at 07:00 AM.

  23. #48
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,237
    Code:
      if (flashcount>= -1*waitClocks){ // counts into negative values to supress tempo flash longer
        if (flashcount> 0){ // 
          if (flashTimer>(flashOnTime+flashOffTime)){ 
    //was here??
            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);
            }
          }
        }
        flashcount-- ;// decrement flashcount 
      }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 by oddson; 11-19-2019 at 07:17 PM.

  24. #49
    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

  25. #50
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,237
    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.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •