KY-040 with 3V Teensy

Hello all, I'm trying to use this:

with a 3v Teensy 4.1 microcontroller. According to some serial prints it seems to be grounding SW, CLK and DT when I press the button switch and also behaving strangely by registering turns of both directions many many times on one 'rotation notch'.

I've had them working fine with 5v on the mega before and according to the Google, people claim to have them working with 3v.

Are there any special requirements for using this with 3v? Or can it be connected directly to the pins?

Wiring is simply (via a breadboard) 3v to 3v, GND to GND and SW, CLK, DT to digital pins.

SW is INPUT_PULLUP and CLK and DT are INPUT

Post your test code.

Are you using debounce? Those encoders use mechanical switches that can bounce. I use hardware debounce on mine. A 0.1 uF ceramic cap from DT to ground, from CLK to ground and SW to ground. That can help a lot.

1 Like

This is my own test code.

#define SW 2
#define DT 3
#define CLK 4

#include <Arduino.h>
enum ActionType {
  NOTHING = 0,
  SINGLE_PRESS,
  LONG_PRESS,
  DOUBLE_PRESS,
  TRIPLE_PRESS,
  INCREASE, 
  DECREASE
};

class Encoder {
  public:
    uint8_t clockPin;
    uint8_t dataPin;
    uint8_t switchPin;
    bool clockLastState = HIGH;
    bool clockState;

    ActionType read() {
      clockState = digitalRead(clockPin);
      if (clockState != clockLastState) {
        clockLastState = clockState;
        bool dataCurrentState = digitalRead(dataPin);
        if (!clockState)
          return dataCurrentState ? INCREASE : DECREASE;
      }
      return NOTHING;
    }

    Encoder(uint8_t clockPin, uint8_t dataPin, uint8_t switchPin)
      : clockPin(clockPin), dataPin(dataPin), switchPin(switchPin) {
      
      pinMode(clockPin, INPUT);
      pinMode(dataPin, INPUT);
      pinMode(switchPin, INPUT_PULLUP);
    }
};

Encoder e(CLK, DT, SW);

void setup() {
  Serial.begin(115200);
}

void loop() {
  static int x;

  switch(e.read()) {
    case INCREASE:
      Serial.print("INCREASED TO: ");
      Serial.println(++x);
    break;

    case DECREASE:
      Serial.print("DECREASED TO: ");
      Serial.println(--x);
    break;

    default:
    break;
  }
}

Twisting one 'notch' on the encoder clockwise (i.e attempting to INCREASE) gave me:

INCREASED TO: 1
DECREASED TO: 0
DECREASED TO: -1
DECREASED TO: -2
DECREASED TO: -3
DECREASED TO: -4
DECREASED TO: -5
DECREASED TO: -6

then a second clockwise notch (again going for INCREASE) gave me:

INCREASED TO: -5
DECREASED TO: -6
DECREASED TO: -7
DECREASED TO: -8
DECREASED TO: -9
DECREASED TO: -10
DECREASED TO: -11
DECREASED TO: -12
DECREASED TO: -13
DECREASED TO: -14
DECREASED TO: -15
DECREASED TO: -16
DECREASED TO: -17
DECREASED TO: -18
DECREASED TO: -19
DECREASED TO: -20

Of course, real hardware and simulations are quite different but hopefully, this Wokwi version of the same code suggests that my encoder logic isn't completely idiotic. (Although let's see!)

If this unravels and turns out to be a software and not hardware issue, can an admin/mod kindly move this to programming, please?

So it turns out the wiring and 3V were both perfectly fine and it was in fact a software problem! Feel free to move this post if wanted. Or delete it entirely :smiley:

Essentially, after a lot of trial and error, I think it was just trying to read the encoder way too fast. I'm used to the Mega speeds and the Teensy is certainly zippy in comparison.

The solution was to do a non-blocking millis() check and only do the encoder reading every (in my case) 4ms. Anything below that gave me jittering and anything above 4ms behaved perfectly and felt nice and responsive for my needs.

I also changed the Encoder::read() code slightly to something I prefer:

#include <Arduino.h>

#define SW 2
#define DT 3
#define CLK 4

enum ActionType
{
  NOTHING = 0,
  SINGLE_PRESS,
  LONG_PRESS,
  DOUBLE_PRESS,
  TRIPLE_PRESS,
  INCREASE,
  DECREASE
};

class Encoder
{
public:
  uint8_t clockPin;
  uint8_t dataPin;
  uint8_t switchPin;
  uint16_t debounce;
  
  ActionType read() {
    static uint32_t timeCapture;
    static bool clockLastState = HIGH;
    bool clockState = digitalRead(clockPin);

    if ((millis() - timeCapture) >= debounce && clockState != clockLastState) {
      timeCapture = millis();
      clockLastState = clockState;
      if (!clockState)
        return digitalRead(dataPin) ? INCREASE : DECREASE;
    }
    return NOTHING;
  }

  Encoder(uint8_t clockPin, uint8_t dataPin, uint8_t switchPin, uint16_t debounce = 4)
      : clockPin(clockPin), dataPin(dataPin), switchPin(switchPin), debounce(debounce) {
    pinMode(clockPin, INPUT);
    pinMode(dataPin, INPUT);
    pinMode(switchPin, INPUT_PULLUP);
  }
};

Encoder e(CLK, DT, SW);

void setup() { Serial.begin(115200); }

void loop() {
  static unsigned int x;

  switch (e.read()) {
  case INCREASE:
    Serial.print("INCREASED TO: ");
    Serial.println(++x);
    break;

  case DECREASE:
    Serial.print("DECREASED TO: ");
    Serial.println(--x);
    break;

  default:
    break;
  }
}

By the way... If anyone likes this code and wants to use it... Have at it.

Please add some comments to the code, so it's easier to understand.

Which bits tripping you up? I'm happy to explain

I don't see how any of these would work in your code;
LONG_PRESS,DOUBLE_PRESS,TRIPLE_PRESS

Am I missing something here or are they just not implemented?

Ah yes, that 'actions' enum was taken from the button library I wrote/use. This was just a test sketch to get the encoder working so it was just pasted in. In the real deal I'm using this code, but I also have an instance of the button class (which can do the SINGLE, LONG, DOUBLE, TRIPLE presses) as a member variable inside the encoder class, which is watching the SW pin of the encoder.