EncoderTool Library with both multiplexed and direct encoders

guitarflake

New member
Hi Everyone,

I am working on a project that requires more than 8 encoders (and other peripherals on other Teensy pins). I'm using the EncoderTool library to multiplex 8 of them using a 74hc165 shift register as well as to read values from 2 other encoders (not connected to the shift register). I have set up everything according to the examples on the EncoderTool website, but for some reason the non-multiplexed Encoders are not working (it appears their valueChanged() is returning false even when I turn the knob().

I am using a Teensy 4.0 with the Arduino IDE.

Code:
//Multiplexed Encoder defs
constexpr int multiplexedEncoderCount = 8;
EncoderTool::EncPlex74165 encoders(multiplexedEncoderCount, shiftReg_LD, shiftReg_CLK, QH_A, QH_B); //move this to setup()?
//enum ENCPLEX_CONTROLS { OSC_VOL, GLIDE, VIBRATO, DECAY, SUSTAIN, RELEASE, FC_LOW, FC_HIGH};
enum ENCPLEX_CONTROLS {FC_HIGH, FC_LOW, RELEASE, SUSTAIN, DECAY, VIBRATO, GLIDE, OSC_VOL};

//Other Encoder defs
EncoderTool::Encoder enc_q;

//Button/LED Defs
Bounce2::Button envFilterBtn = Bounce2::Button();
Bounce2::Button envVolBtn = Bounce2::Button();

//Parameters
int glideTime = 50; //ms
bool gliding = false;

void setup() {
  //... other button, SPI pin, and MIDI setup here ...
  
  // Set up multiplexed encoders
  encoders.begin(EncoderTool::CountMode::quarterInv);
  for (int i = 0; i < multiplexedEncoderCount; i++)
    encoders[i].setLimits(0, 127);

  // Set up other encoders
  enc_q.begin(Q_ENC_A, Q_ENC_B);
  
  // Set up serial (for debugging)
  Serial.begin(9600);
}

void loop() {

  usbMIDI.read();

  // ... Button update code here ...

  encoders.tick();
  for (int i = 0; i < multiplexedEncoderCount; i++) {
    if (encoders[i].valueChanged()) {
      Serial.print("\nencoder val changed: ");
      Serial.print(i);
      switch (i) {
        case OSC_VOL:
          usbMIDI.sendControlChange(OSCGAIN_CC, encoders[i].getValue(), channel);
          break;
        case GLIDE:
          usbMIDI.sendControlChange(GLIDE_CC, encoders[i].getValue(), channel);
          break;
        case VIBRATO:
          usbMIDI.sendControlChange(VIB_CC, encoders[i].getValue(), channel);
          break;
        case DECAY:
          usbMIDI.sendControlChange(DECAY_CC, encoders[i].getValue(), channel);
          break;
        case SUSTAIN:
          usbMIDI.sendControlChange(SUSTAIN_CC, encoders[i].getValue(), channel);
          break;
        case RELEASE:
          usbMIDI.sendControlChange(RELEASE_CC, encoders[i].getValue(), channel);
          break;
        case FC_LOW:
          usbMIDI.sendControlChange(FC_LOW_CC, encoders[i].getValue(), channel);
          break;
        case FC_HIGH:
          usbMIDI.sendControlChange(FC_HI_CC, encoders[i].getValue(), channel);
          break;
      }
    }
  }

  //enc_q.tick();
  if (enc_q.valueChanged()) usbMIDI.sendControlChange(FILTER_Q_CC, enc_q.getValue(), channel);

}

The multiplexed encoders all work fine, but the enc_q encoder does not. I've tried this both using PolledEncoder and interrupt based Encoder class. Any ideas?

Thanks!
Lucas
 
Looks fine on a first glance.
I can have a closer look but it would make things easier if you could prepare a minimal sketch which compiles and shows the issue without the midi stuff, buttons and other hardware
 
Hi, thanks so much for the response! I have narrowed it down to a minimal example, and double-checked the electrical connections. The two data pins of the encoder are connected to pins 0 and 1 of the Teensy, and the middle (ground) pin of the encoder is connected to the Teensy's GND pin. My entire sketch is below:

Code:
#include <vector>
#include <EncoderTool.h>
#include <Bounce2.h>
#include "SPI.h"
#include "usb_midi.h"



//Pin definitions - Multiplexed Encoders
#define QH_A 9 //Output pin QH from shift register A (closer to end of breadboard)
#define QH_B 8 //Output pin QH from shift register B (closer to middle of breadboard)
#define shiftReg_LD 6 //load pin for all shift registers (SH when high, LD when low)
#define shiftReg_CLK 7 //CLK pin for all shift registers

#define Q_ENC_A 1
#define Q_ENC_B 0


//Other variables

bool envControlsVol = false, envControlsFilter = false;

//MIDI OUT SETUP
const int channel = 1;

enum MIDI_CC {
  OSCGAIN_CC  = 102,
  GLIDE_CC = 103,
  ENV_TARGET_FILTER_CC = 104,
  ENV_TARGET_VOL_CC = 105,
  VIB_CC = 106,
  ATTACK_CC = 107,
  DECAY_CC = 108,
  SUSTAIN_CC = 109,
  RELEASE_CC = 110,
  FC_LOW_CC = 111,
  FC_HI_CC = 112,
  FILTER_Q_CC = 113,
  CHORUS_ON_CC = 114
};

//Multiplexed Encoder defs
constexpr int multiplexedEncoderCount = 8;
EncoderTool::EncPlex74165 encoders(multiplexedEncoderCount, shiftReg_LD, shiftReg_CLK, QH_A, QH_B); //move this to setup()?
enum ENCPLEX_CONTROLS {FC_HIGH, FC_LOW, RELEASE, SUSTAIN, DECAY, VIBRATO, GLIDE, OSC_VOL};

//Other Encoder defs
EncoderTool::Encoder enc_q;

void setup() {

  // Set up multiplexed encoders
  encoders.begin(EncoderTool::CountMode::quarterInv);
  for (int i = 0; i < multiplexedEncoderCount; i++)
    encoders[i].setLimits(0, 127);

  // Set up other encoders
  enc_q.begin(Q_ENC_A, Q_ENC_B);
  
  // Set up serial (for debugging)
  Serial.begin(9600);
}

void loop() {

  encoders.tick();
  for (int i = 0; i < multiplexedEncoderCount; i++) {
    if (encoders[i].valueChanged()) {
      Serial.print("\nencoder val changed: ");
      Serial.print(i);
      switch (i) {
        case OSC_VOL:
          usbMIDI.sendControlChange(OSCGAIN_CC, encoders[i].getValue(), channel);
          break;
        case GLIDE:
          usbMIDI.sendControlChange(GLIDE_CC, encoders[i].getValue(), channel);
          break;
        case VIBRATO:
          usbMIDI.sendControlChange(VIB_CC, encoders[i].getValue(), channel);
          break;
        case DECAY:
          usbMIDI.sendControlChange(DECAY_CC, encoders[i].getValue(), channel);
          break;
        case SUSTAIN:
          usbMIDI.sendControlChange(SUSTAIN_CC, encoders[i].getValue(), channel);
          break;
        case RELEASE:
          usbMIDI.sendControlChange(RELEASE_CC, encoders[i].getValue(), channel);
          break;
        case FC_LOW:
          usbMIDI.sendControlChange(FC_LOW_CC, encoders[i].getValue(), channel);
          break;
        case FC_HIGH:
          usbMIDI.sendControlChange(FC_HI_CC, encoders[i].getValue(), channel);
          break;
      }
    }
  }

  if (enc_q.valueChanged()) {
    Serial.println("encoder value changed: Q");
    usbMIDI.sendControlChange(FILTER_Q_CC, enc_q.getValue(), channel);}

}

When I compile and load this code on the Teensy, it all compiles fine and the multiplexed encoders work almost perfectly without glitch, but the enc_q encoder does not. It neither sends the MIDI CC message nor prints the "encoder value changed" message to the serial monitor.

Do you have any ideas? Is there something I'm missing? Or could there be something weird going on in the library where individual encoders can't be used with multiplexed ones?

Let me know, and thanks so much!
 
I compiled your sketch using USB type midi, connected an encoder to pins 0/1 and uploaded it. It prints "encoder value changed: Q" whenever I turn the encoder. So, looks like it works here.
 
Huh that's weird, did you try it with multiplexed encoders connected as well? I had other encoders connected via 74hc165 shift registers and the multiplexed ones worked but the direct one didn't. Could there be something going on involving the polling/interrupts on the multiplexed encoders that is interfering with the directly connected one?
 
I did not connect other hardware than the encoder on pins 0/1. I don't think that this will change anything because '165 code only generates some pin patterns and reads in the state of the '165 output pin. This will not change whether you actually connect the shift register or not.

I suspect some hardware issue. Can you try to disconnect everything but the encoder on 0/1 and check if it works then? Make sure to run exactly the code you posted above.

You can also do the following simple check
Code:
void setup()
{
    pinMode(0, INPUT_PULLUP);
    pinMode(1, INPUT_PULLUP);
}

void loop()
{
    static int oldA = 0;
    static int oldB = 0;

    int A = digitalRead(0);
    int B = digitalRead(1);

    if(oldA != A || oldB != B)
    {
        Serial.printf("%d %d\n", A, B);
        oldA = A;
        oldB = B;
    }
}

Turning the encoder this should generate something like
Code:
1 0
1 1
1 0
1 1
1 0
1 1
1 0
1 1
1 0
1 1
1 0
1 1
1 0
0 0
1 0
0 0
0 1
1 1
 
Thanks so much. I tried running your simple check code and it showed me that the input to the Teensy on one of the pins was never changing, which (after a surprising amount of confusing debugging) led me to find that I hadn't soldered the header onto that pin well enough. I added more solder and the issue is fixed.

So sorry about the dumb question! I could've sworn it was something happening with the library since I had checked literally every solder joint EXCEPT for that one that was the problem.

Thanks for all your help and for making this library! It has really saved me on this project.
 
Back
Top