Hey there, I'm trying to get this little rotary encoder to not be so glitchy and I'm at a loss. I've had the "best" luck through basic wiring (only pullup resistors on A/B), but it's still pretty finicky getting it to land on a number you want.
Has anyone had any luck with them and what method did you use to get it to increase/decrease smoothly?
I read about a hardware solution with something called a Schmitt Trigger, but could only find that available in a giant 14 pin chip and I'd like to try and keep my project small. Seemed a little overkill to debug one small component.
I've tried several coding debounces I found on the internet and haven't had much luck with them, though I'll admit I felt a little in over my head, and again, the regular encoder read code was the one that gave "best" results.
I did try this circuit and, like the software solutions, it didn't seem to have much positive effect.
Here is my code and I included both the regular encoder read code(//commented out) and the debounce code. Which doesn't decrease and actually slowly increases as the encoder glitches upward.
Thank you to anyone with experience with these little guys or anyone willing to offer suggestions!
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
int Buzzer = 7;
int minutes_change=0;
int minutes=0;
int Current_Frame=1;
int buttonPin = 3;
//encoder pins and variables
int clkPin = 2;
int dtPin = 8;
int encoderCount = 0;
int clkPinLast = LOW;
int clkPinCurrent = LOW;
//////////////////
//States: //
//0 =reset //
//1 =setup //
//2 =countdown //
///////////////////////
int State=0;
long Timestamp_Button_Pressed;
unsigned long Countdown_start;
// Declaration for SSD1306 display connected using software SPI
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
//Images:
// 'TIME CHECK', 128x32px
const unsigned char TIMES_UP [] PROGMEM = {
0xdf, 0xff, 0xff, 0xef, 0xef, 0xef, 0xff, 0xf7, 0xf7, 0xf7, 0xf7, 0xff, 0xfb, 0xfb, 0xfb, 0xfb,
0x8f, 0xcf, 0xcf, 0xc7, 0xc7, 0xc7, 0xe7, 0xe7, 0xe3, 0xe3, 0xe3, 0xf3, 0xf3, 0xf1, 0xf1, 0xf1,
0xcf, 0xcf, 0xcf, 0xcf, 0xc7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf1, 0xf9,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0x38, 0x7e, 0x3c, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0x38, 0x7c, 0x3c, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0x38, 0x7c, 0x3c, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0x38, 0x3c, 0x3c, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x38, 0x38, 0x1c, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x38, 0x38, 0x1c, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x38, 0x38, 0x1c, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x38, 0x38, 0x1c, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x38, 0x10, 0x1c, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x38, 0x10, 0x1c, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x30, 0x10, 0x1c, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x30, 0x10, 0x1c, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x30, 0x00, 0x1c, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x30, 0x80, 0x1c, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x30, 0x82, 0x1c, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x30, 0x82, 0x1c, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x30, 0xc2, 0x1c, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x30, 0xc6, 0x1c, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0x78, 0xe7, 0x1c, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xcf, 0xcf, 0xcf, 0xcf, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf1, 0xf9,
0x8f, 0xcf, 0xcf, 0xc7, 0xc7, 0xc7, 0xe7, 0xe7, 0xe3, 0xe3, 0xe3, 0xf3, 0xf3, 0xf1, 0xf1, 0xf1,
0xdf, 0xdf, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xfb, 0xfb, 0xfb, 0xfb
};
//Functions:
////////////////////////////START BUTTON////////////////////////////
void Press_B_Button(){
if (millis() - Timestamp_Button_Pressed>200){
if (State==1){//add minutes mode
Countdown_start=millis();
State=2; //start countdown mode
}
else{//if state is 2(count) then go to state 1(add minute) and "minutes"=0
State=0;//reset mode
delay(2000);
}
}
}
///////////////BUTTON DEBOUNCE///////////////
long debouncing_time = 15; //Debouncing Time in Milliseconds
volatile unsigned long last_micros;
void debounceInterruptB() {
if((long)(micros() - last_micros) >= debouncing_time * 1000) {
Press_B_Button();
last_micros = micros();
}
}
////////////////////////////RING BELL////////////////////////////
void Ring_Bell(int Frame =0){
display.clearDisplay();
int dimmerValue = analogRead(A6);
if (dimmerValue<=50){
display.clearDisplay();
display.invertDisplay(true);
}
else{
display.clearDisplay();
display.invertDisplay(false);
}
if (Frame==1||2||3||4)display.drawBitmap(0,0,TIMES_UP,128,32,1);
display.display();
if (Frame>1){
delay(200);
digitalWrite(Buzzer,LOW);
}
else {
digitalWrite(Buzzer,HIGH);
delay(1000);
}
}
////////////////////////////CURRENT TIME////////////////////////////
String CurrentTime(unsigned long MsLeft){
String Result;
int M;
int S;
M=(long)MsLeft/60000;
if (M<10) Result=(String)"0"+ M + ":";else Result=(String)M+":";
S=(long)((MsLeft-M*60000)/1000);
if (S<10) Result=(String)Result + "0"+ S ;else Result=(String)Result +S;
return Result;
}
////////////////////////////BRIGHTNESS////////////////////////////
void setBrightness(int contr){
int prech;
int brigh;
switch (contr){
case 000 ... 255: prech= 0; brigh= contr; break;
//case 401 ... 411: prech=16; brigh= contr; break;
default: prech= 16; brigh= 255; break;}
display.ssd1306_command(SSD1306_SETPRECHARGE);
display.ssd1306_command(prech);
display.ssd1306_command(SSD1306_SETCONTRAST);
display.ssd1306_command(brigh);
}
//////////////////////////////////////////////////////////
// SETUP //
//////////////////////////////////////////////////////////
void setup() {
Timestamp_Button_Pressed=millis();
//Encoder pin setup:
pinMode(clkPin, INPUT);
pinMode(dtPin, INPUT);
//Button pin setup:
pinMode(buttonPin, INPUT_PULLUP); //init button A pin to input
digitalWrite(buttonPin, HIGH);
attachInterrupt(digitalPinToInterrupt(buttonPin), debounceInterruptB,RISING);
//Buzzer pin setup:
pinMode(Buzzer, OUTPUT);
digitalWrite(Buzzer, HIGH);
int Frame =0;
//OLED Pin setup:
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3c
display.clearDisplay();
display.display();
}
/////////////variables for debounced rotary
static uint8_t prevNextCode = 0;
static uint16_t store=0;
/////////////////////////////////////////////////////////////////////////////////
// LOOP //
/////////////////////////////////////////////////////////////////////////////////
void loop() {
//Set contrast level
int dimmerValue = analogRead(A6);
if (dimmerValue <=600){
setBrightness(dimmerValue);
}
///////////////////RESET/////////////////////////
if (State==0){//reset state
digitalWrite(Buzzer,HIGH);
minutes = 0;
display.invertDisplay(false);
display.clearDisplay();
display.setTextSize(2);
display.setCursor(5,0);
display.setTextColor(SSD1306_WHITE);
display.println((String)minutes+" min");
display.display();
State=1;
}
////////////////////////////////// SET TIME STATE ///////////////////////////
//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBOUNCED Encoder vvvvvvvvvvvvvvvvvvvvvvvvvvvv
static int8_t minutes,val;
if( val=read_rotary() ) {
minutes +=val;
if(minutes<0){
minutes=1;
}
Serial.print(minutes);Serial.print(" ");
display.clearDisplay();
display.setTextSize(2);
display.setCursor(5,0);
display.setTextColor(SSD1306_WHITE);
display.println((String)minutes+" min");
display.display();
}
///^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////// SET TIME STATE /////////////////////////////
//vvvvvvvvvvvvvvvvvvvvvvvvvvvvv *NOT* DEBOUNCED Encoder vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
/* if (State==1){//set time mode
digitalWrite(Buzzer,HIGH);
clkPinCurrent = digitalRead(clkPin);
if ((clkPinLast == LOW) && (clkPinCurrent == HIGH)) {
if (digitalRead(dtPin) == HIGH) {
if(minutes<=0){// Minutes-- unless minutes is 0, then don't count below 0
minutes=0;
}
else {
minutes--;
}
}
else {
minutes++;
}
display.clearDisplay();
display.setTextSize(2);
display.setCursor(5,0);
display.setTextColor(SSD1306_WHITE);
display.println((String)minutes+" min");
display.display();
}
clkPinLast = clkPinCurrent;
}
*/
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//////////////////////////////////////////////////////////////////////////////////
///////////////////////Countdown State/////////////////////////////
if(State==2){
if(minutes*60000 >(millis()-Countdown_start)){
display.clearDisplay();
display.setCursor(5,0);
display.setTextSize(4);
display.setTextColor(SSD1306_WHITE);
display.println(CurrentTime((minutes*60000-(millis()-Countdown_start))));
display.display();
}
else {
Ring_Bell(Current_Frame);
Current_Frame=Current_Frame+1;
if (Current_Frame==5) Current_Frame=1;
}
}
}
////////////////////////DEBOUNCED READ ROTARY FUNCTION/////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
// A vald CW or CCW move returns 1, invalid returns 0.
int8_t read_rotary() {
static int8_t rot_enc_table[] = {0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0};
prevNextCode <<= 2;
if (digitalRead(dtPin)) prevNextCode |= 0x02;
if (digitalRead(clkPin)) prevNextCode |= 0x01;
prevNextCode &= 0x0f;
// If valid then store as 16 bit data.
if (rot_enc_table[prevNextCode] ) {
store <<= 4;
store |= prevNextCode;
if ((store&0xff)==0x2b) return -1;
if ((store&0xff)==0x17) return 1;
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////