Bounce & Bounce2 Objects with Multiplexer 74HCT4051 / 4051

Status
Not open for further replies.

dr_d_nice

Member
I have a MIDI project using switches, potentiometers and piezo-electrics paired with Teensy-LC. Things are going well.
I am using 74HCT4051 multiplexers to read these components, and I have had great results.

Previously, using Teensy 2.0, switches and piezos, I was generating MIDI Control Change values of 0 or 1 using the Bounce library.
I need to understand how to use the Bounce/Bounce2 library with the 74HCT4051.

Here's my example code, it does not work. The idea is to generate "1" when the buttons are pressed or the piezos generate a rising voltage, and a "0" when the voltage is falling. But I am getting neither 0 or 1.
I also realize that I need more practice working with arrays in Arduino.

Code:
#include <Bounce2.h>
# define BOUNCE_LOCK_OUT


// variable definition for 4051
int r0 = 0;  // setting initial value of 4051 pin (s0)
int r1 = 0;  // setting initial value of 4051 pin (s1)
int r2 = 0;  // setting initial value of 4051 pin (s2)
int count = 0;  // which pin we are selecting while reading 4051 inputs
int old_time = 0; // time since powered on

// asssign Input pin
int digitalPin = 3;

// initiate Bounce
Bounce debouncer_piezos[8] = Bounce();

// define the s0, s1, s2 pins on Teensy
int s0 = 0;
int s1 = 1;
int s2 = 2;

// setup arduino pins
void setup() {
  
  pinMode(s0, OUTPUT);  // s0
  pinMode(s1, OUTPUT);  // s1
  pinMode(s2, OUTPUT);  // s2

  pinMode(digitalPin, INPUT);  // set digitalPin to INPUT
  debouncer_piezos[8].attach(digitalPin);
  debouncer_piezos[8].interval(5);
}


void loop() {
  for(count = 0; count <= 7; count++) {  // "count = 0" initializes for loop,
                                         // "count <= 7" tests,
                                         // "count++" increments / decrements 

    debouncer_piezos[count].update();
    
    // SELECT THE BIT
    // reads bit of a number, in this case "count", 
    // it reads the binary equivalent of count (count = 1 --> r2r1r0 = 001)
    // "0" tells bitRead to read the right most bit
    // "1" tells bitRead to read the first bit form the right
    // "2" tells bitRead to read the second bit form the right
    r0 = bitRead(count, 0);
    r1 = bitRead(count, 1);
    r2 = bitRead(count, 2);
    
    // WRITE TO DIGITAL PINS
    digitalWrite(s0, r0);  // teensy pin 0, s0 on 4051
    digitalWrite(s1, r1);  // teensy pin 1, s1 on 4051
    digitalWrite(s2, r2);  // teensy pin 2, s2 on 4051
    
    delayMicroseconds(75);  // once the pin is selected, allow some time
                            // for the signal to stabalize


    // READ MULTIPLEXED PIN
    if ( debouncer_piezos[count].rose() ) {
    usbMIDI.sendControlChange(count+1, 1, 1);
    }
    if ( debouncer_piezos[count].fell() ) {
    usbMIDI.sendControlChange(count+1, 0, 1);
    }
    
    // simple counter that confirms teensy is communicating with Pure Data
    int new_time = millis();
    if ( new_time - old_time >= 1000 ) {
      usbMIDI.sendControlChange(100, new_time/1000, 1);
    }


  }
}
 
I'm not actually sure.
My intention is to monitor each of the 8 multiplexer pins. Maybe I do need a separate Bounce object per pin.
 
Maybe I do need a separate Bounce object per pin.

Yes, you definitely need 1 per pin.

If each pin doesn't have its own pullup resistor, you'll also need many microseconds delay from the moment you change the mux before you update the Bounce object, to allow time for the weak pullup to slowly make the line high.
 
3 year bump.

Paul are you saying that each pin of each mux needs its own Bounce object? Seems like the same Bounce object could be used repeatedly on the same teensy input pin to which the mux is attached??

I am currently working on something with a LOT of knobs and buttons, and while my code is functioning......it is long because I am struggling with how to efficiently setup Bounce objects such that a large number of them can be accessed from within a nested loop.
 
You need separate bounce items because you will get different readings between each switch and you would mistake those for a bouncing switch....

So you need multiple objects with the same pin input from the MUX but different indexes based on the selector settings at the mux to apply the correct data from the actual switch.

I think that code only generates one bounce object. And I'm not sure bounce is the way to deal with piezo readings (implied by the name).

If you are trying to extract a triggering event from the signal of a piezo you normally would convert it to an envelop and test when the envelop passes some threshold value.


For how to initialize an array of bounce objects see the first post from this tread (notice the keyword 'new' in the setup lines on this and ResponsiveAnalogRead).
https://forum.pjrc.com/threads/52552-MIDI-DJ-Controller-using-Teensy-3-6-am-I-doing-this-right
 
For how to initialize an array of bounce objects see the first post from this tread (notice the keyword 'new' in the setup lines on this and ResponsiveAnalogRead).
https://forum.pjrc.com/threads/52552-MIDI-DJ-Controller-using-Teensy-3-6-am-I-doing-this-right

That link is very informative. Still trying to digest it. My experience with pointers and arrays is limited. LEARNING!

Luckily my project does not use any piezos. Just pushbuttons and knobs.

Currently I am reluctant to post my code because it --in my inexperienced style -- is super messy.
 
That post covers how to initialize the bounce and ResponsiveAnalogRead object. It's multiplex code uses a delay immediately after setting the mux controls to give the signal time to stabilize after switching, which would be fine in some circumstances but I believe having it on the inner loop means it's firing between every reading meaning you would be less and less responsive the more mux you have.

I have yet to write code for all the issues that arrise with lots of mux as I don't ever use up the pins on the T3.0s I usually use for my MIDI projects.

So my mux experience has been purely 'experimental' and so I can't truly comment on usability in the real world of MIDI.

This post is a decent index of what I do know on the subject: https://forum.pjrc.com/threads/52856-MIDI-and-MUX?p=181888&viewfull=1#post181888
 
That post covers how to initialize the bounce and ResponsiveAnalogRead object. It's multiplex code uses a delay immediately after setting the mux controls to give the signal time to stabilize after switching, which would be fine in some circumstances but I believe having it on the inner loop means it's firing between every reading meaning you would be less and less responsive the more mux you have.

I have yet to write code for all the issues that arrise with lots of mux as I don't ever use up the pins on the T3.0s I usually use for my MIDI projects.

So my mux experience has been purely 'experimental' and so I can't truly comment on usability in the real world of MIDI.

This post is a decent index of what I do know on the subject: https://forum.pjrc.com/threads/52856-MIDI-and-MUX?p=181888&viewfull=1#post181888

Awesome! Thanks again. Here is what I have been able to get working with the Teensy 3.5. This is a bare bones version of the code which does not include all the other stuff I have going on: display, encoder, interface buttons, and some other functionality that is in development.

The trick was setting up the large array of an array of Bounce and ResponsiveAnalogRead objects.

Not entirely relevant to this post, no piezos.

Code:
// I am sure there are more efficient ways to do some of the things here, but this is working fine for me
// this code is written for Teensy 3.5
// the pin numbers used here are how My circuit is wired. it will vary for your design
// I have pullup resistors on every single button, so I do not need any delay while reading through the mux pins


#include "Type4051Mux.h"    //calls a 4051 mux library
#include "Type4067Mux.h"    //calls a 4067 mux library
#include <ResponsiveAnalogRead.h> // include the ResponsiveAnalogRead library for analog smoothing
#include <Bounce.h>   // include the Bounce library for 'de-bouncing' switches -- removing electrical chatter as contacts settle
//usbMIDI.h library is added automatically when code is compiled as a MIDI device
#include <MIDI.h>                //include the midi library as I am also using  5pin Dins for MIDI in and out,
MIDI_CREATE_DEFAULT_INSTANCE();  // instantiate MIDI


//***************** define 4051 Selector pins

const int B4051_S[3] = {26,27,28};   // sets up 4053 S pins as an array

//***************** DEFINE MUX 4051 INPUT PINS

const int B4051_[8] = {29,30,31,32,33,34,35,36};   // sets up the digital pins used for the 8 4051s as an array

// creates 8 global 8ch mux  objects
// parameters: (input pin, type, digi/analog, s0, s1, s2)
// Need one of these for each 4051 on the board (8 total). they all share s0, s1, s2

Type4051Mux buttonMux_[8] = {Type4051Mux(B4051_[0], INPUT, DIGITAL, B4051_S[0], B4051_S[1], B4051_S[2]),     //sets up the 4051 mux's as an array
                             Type4051Mux(B4051_[1], INPUT, DIGITAL, B4051_S[0], B4051_S[1], B4051_S[2]),
                             Type4051Mux(B4051_[2], INPUT, DIGITAL, B4051_S[0], B4051_S[1], B4051_S[2]),
                             Type4051Mux(B4051_[3], INPUT, DIGITAL, B4051_S[0], B4051_S[1], B4051_S[2]),
                             Type4051Mux(B4051_[4], INPUT, DIGITAL, B4051_S[0], B4051_S[1], B4051_S[2]),
                             Type4051Mux(B4051_[5], INPUT, DIGITAL, B4051_S[0], B4051_S[1], B4051_S[2]),
                             Type4051Mux(B4051_[6], INPUT, DIGITAL, B4051_S[0], B4051_S[1], B4051_S[2]),
                             Type4051Mux(B4051_[7], INPUT, DIGITAL, B4051_S[0], B4051_S[1], B4051_S[2])};

// Create Bounce objects for each button.  The Bounce object
// automatically deals with contact chatter or "bounce", and
// it makes detecting changes very simple.   
// We need a Bounce object for every single PIN of every single mux
const int BOUNCE_TIME = 7;              // the bounce time. I have 4.7kohm pull-up resistors on each of my buttons, and so am not using INPUT_PULLUP
const int D_PINS = 8;                   //the number of digital pins used on the teensy for the 8 4051 Muxs
const int D_MUX_PINS = 8;               //the number of  pins on each 4051 

//Declares and sets pointer to an 8x8 array of bounce objects --> set this up in setup()

//**************this part is super important!******************//
Bounce *buttons[D_PINS][D_MUX_PINS];                    //this totally works     

  
int noteOnVelocity = 99;  //--edit this later via the input buttons and encoder

//define 4067 Selector pins

const int K4067_S[4] = {2, 3, 4, 5};

//DEFINE 4067 INPUT pins

const int K4067_[7] = {16, 17, 18, 19, 20, 21, 22};

const int A_PINS = 7;                         //the number of digital pins used on the teensy for the 7 4067 Muxs
const int A_MUX_PINS = 16;                    // number of Multiplexer Analog PINS

//creates 7 global instances of the 4067 mux 36ch (input pin, type, digital/analog, s0, s3, s2, s3)
//I will need one of these for each 4067 on the board (7 total)
Type4067Mux knobMux_[A_PINS] = {Type4067Mux(K4067_[0], INPUT, ANALOG, K4067_S[0], K4067_S[1], K4067_S[2], K4067_S[3]),    
                                Type4067Mux(K4067_[1], INPUT, ANALOG, K4067_S[0], K4067_S[1], K4067_S[2], K4067_S[3]),
                                Type4067Mux(K4067_[2], INPUT, ANALOG, K4067_S[0], K4067_S[1], K4067_S[2], K4067_S[3]),
                                Type4067Mux(K4067_[3], INPUT, ANALOG, K4067_S[0], K4067_S[1], K4067_S[2], K4067_S[3]),
                                Type4067Mux(K4067_[4], INPUT, ANALOG, K4067_S[0], K4067_S[1], K4067_S[2], K4067_S[3]),
                                Type4067Mux(K4067_[5], INPUT, ANALOG, K4067_S[0], K4067_S[1], K4067_S[2], K4067_S[3]),
                                Type4067Mux(K4067_[6], INPUT, ANALOG, K4067_S[0], K4067_S[1], K4067_S[2], K4067_S[3])};

                       

// This creates a pointer to a declared array of responsiveAnalogRead objects

//******************this part is super important**************//
ResponsiveAnalogRead *knobMux[A_PINS][A_MUX_PINS];               //holy shit this totally works



// set sup midi I/O pin definitions
#define MIDI_In   0       //rx0
#define MIDI_Out  1       //tx0

int noteMap[8][8] = {{36,37,38,39,40,41,42,43},           //this array will be useful later when using the interface encoder and buttons to assign cutom mappings
                     {44,45,46,47,48,49,50,51},
                     {52,53,54,55,56,57,58,59},
                     {60,61,62,63,64,65,66,67},
                     {68,69,70,71,72,73,74,75},
                     {76,77,78,79,80,81,82,83},
                     {84,85,86,87,88,89,90,91},
                     {92,93,94,95,96,97,98,99}};

int buttonChannelMap[8][8] = {{1,1,1,1,1,1,1,1},         //this array will be useful later when using the interface encoder and buttons to assign cutom mappings
                              {1,1,1,1,1,1,1,1},
                              {1,1,1,1,1,1,1,1},
                              {1,1,1,1,1,1,1,1},
                              {1,1,1,1,1,1,1,1},
                              {1,1,1,1,1,1,1,1},
                              {1,1,1,1,1,1,1,1},
                              {1,1,1,1,1,1,1,1}};
 
int knobCcMap[7][16] = {{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16},
                        {17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32},                      //this array will be useful later when using the interface encoder and buttons to assign cutom mappings
                        {33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48},
                        {49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64},
                        {65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80},
                        {81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96},
                        {97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112}};

int knobChannelMap[7][16] = {{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},            //this array will be useful later when using the interface encoder and buttons to assign cutom mappings
                             {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
                             {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
                             {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
                             {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
                             {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
                             {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
           
// a data array and a lagged copy to tell when MIDI Knob changes are required
byte data[A_PINS][A_MUX_PINS];

byte dataLag[A_PINS][A_MUX_PINS];



void setup() { 
  //setup all the pins. might be redundant for 4067, 4051,but whatevs
//pins will vary based on YOUR design and wiring
  pinMode(MIDI_In, INPUT);
  pinMode(MIDI_Out, OUTPUT);
  
  pinMode(K4067_S[0], OUTPUT);
  pinMode(K4067_S[1], OUTPUT);
  pinMode(K4067_S[2], OUTPUT);
  pinMode(K4067_S[3], OUTPUT);

  pinMode(B4051_S[0], OUTPUT);
  pinMode(B4051_S[1], OUTPUT);
  pinMode(B4051_S[2], OUTPUT);
  
  pinMode(B4051_[0], INPUT);
  pinMode(B4051_[1], INPUT);
  pinMode(B4051_[2], INPUT);
  pinMode(B4051_[3], INPUT);
  pinMode(B4051_[4], INPUT);
  pinMode(B4051_[5], INPUT);
  pinMode(B4051_[6], INPUT);
  pinMode(B4051_[7], INPUT);

  pinMode(K4067_[0], INPUT);
  pinMode(K4067_[1], INPUT);
  pinMode(K4067_[2], INPUT);
  pinMode(K4067_[3], INPUT);
  pinMode(K4067_[4], INPUT);
  pinMode(K4067_[5], INPUT);
  pinMode(K4067_[6], INPUT);
  
  pinMode(23, OUTPUT);    // this pin is not used in the controller and is free for scoping slash debugging


  //this sets up the analog responsive read object array
//******************this part is super important**************//
// it populates the RAR array with the correct stuff
  for (int j = 0; j < A_PINS; j++){
    for (int i = 0; i < A_MUX_PINS; i++){
      knobMux[j][i] = new ResponsiveAnalogRead(K4067_[j], true); // initialize
    }
  }


  //this sets up the buttons bounce object array
//******************this part is super important**************//
// it populates the Bounce array with the correct stuff
  for(int j = 0; j < D_PINS; j++){
    for (int i = 0; i < D_MUX_PINS; i++){
      buttons[j][i] = new Bounce(B4051_[j], BOUNCE_TIME);
    }
  }

  //start an instance of MIDI I/O
  MIDI.begin();

  //starts serial monitor, use for debug, 
  //Serial.begin(115200);    
  //higher speed than 31250 (MIDI), 57600 will also work
  
}


void loop(){

  //******************** call the readKnobs() function***************//
  digitalWrite(23, HIGH);         //this pin is for debugging with a scope to test timing
  readKnobs();

  //******************** call the readButtons() function***************//
  digitalWrite(23, LOW);          //this pin is for debugging with a scope to test timing
  readButtons();  

  // MIDI Controllers should discard incoming MIDI messages.
  // http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash
  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}



                      
void readKnobs(){
  //************this section reads the knob values through the 4067*****************//
  //************reads and smooths knob input****************//
   for(int j = 0; j < A_PINS; j++){ 
    for (int i= 0; i < A_MUX_PINS; i++){
      knobMux_[j].setChannel(i);                       //THIS Totally WORKS
      knobMux[j][i]->update();
      if(knobMux[j][i]->hasChanged()){
        data[j][i] = knobMux[j][i]->getValue()>>3;
        if (data[j][i] != dataLag[j][i]){
          dataLag[j][i] = data[j][i];
          usbMIDI.sendControlChange(knobCcMap[j][i], data[j][i], knobChannelMap[j][i]);
          //MIDI.sendControlChange(knobCcMap[j][i], data[j][i], knobChannelMap[j][i]);
        }
      }
     }
   } 
  // ****************************ends loop for reading knobs **************// 
}


void readButtons(){
    for(int j = 0; j < D_PINS; j++){
      for(int i = 0; i < D_MUX_PINS; i++){
        buttonMux_[j].setChannel(i);
        buttons[j][i]->update();
        if (buttons[j][i]->fallingEdge()) {
          usbMIDI.sendNoteOn(noteMap[j][i], noteOnVelocity, buttonChannelMap[j][i]);   //************IT WORKS!!************//
          //MIDI.sendNoteOn(noteMap[j][i], noteOnVelocity, buttonChannelMap[j][i]);    // Send a Note (pitch , velo , on channel)
        } 
        if (buttons[j][i]->risingEdge()) {
          usbMIDI.sendNoteOff(noteMap[j][i], 0, buttonChannelMap[j][i]); 
         // MIDI.sendNoteOff(noteMap[j][i], 0, buttonChannelMap[j][i]);
        }
      }
    }
}
 
Status
Not open for further replies.
Back
Top