I want to Combine a Oled-Display with a rotary encoder.
I have 1 Value i want to Change with the Rotary Encoder and i want the Value beeing displayed on the oled. I Ran into problems. The Value is glitching and it is not clearly changing. I narrowed it down do "display.display();". As soon as i remove this line it works. When i add it it is glitching again. Any ideas?
//SCreen
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
//Rotary Encoder
// Rotary Encoder Inputs
#define inputCLK 4
#define inputDT 5
int counter = 0;
int currentStateCLK;
int previousStateCLK;
String encdir = ""; //String to be populated with either ClockWise or CounterClockWise
//Variables
int Length = 20;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
//RotaryEncoder
// Set encoder pins as inputs
pinMode(inputCLK, INPUT);
pinMode(inputDT, INPUT);
// Read the initial state of inputCLK
// Assign to previousStateCLK variable
previousStateCLK = digitalRead(inputCLK);
}
void loop() {
//Rotary Encoder
// Read the current state of inputCLK
currentStateCLK = digitalRead(inputCLK);
// If the previous and the current state of the inputCLK are different then a pulse has occured
if (currentStateCLK != previousStateCLK){
// If the inputDT state is different than the inputCLK state then
// the encoder is rotating counterclockwise
if (digitalRead(inputDT) != currentStateCLK) {
counter --;
Length --;
encdir ="CCW";
} else {
// Encoder is rotating clockwise
counter ++;
encdir ="CW";
Length ++;
}
if (Length <= 0){
Length = 0;}
Serial.print("Direction: ");
Serial.println(encdir);
Serial.print(" -- Value: ");
Serial.println(counter);
Serial.print(" Length ");
Serial.println(Length);
}
// Update previousStateCLK with the current state
previousStateCLK = currentStateCLK;
//Oled
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
//Timer
display.setCursor(0, 10);
display.print("Timer - ");
display.print(Length);
display.println(" Minutes");
display.display();
}
Your sketch is running fast, and the display.display() is refreshing the data placed in its buffer. For an experiment, just after display.display(); put a delay to slow the refresh:
delay(200); // 200 ms delay
If this delay helps resolve the "flicker"... consider replacing the delay(); with a timing loop using the millis() function to only refresh your display at your preferred interval. An example of how to use it is blink without delay
A Delay is not Solving the issue. It just...well...delays it. Like i said. It is not a problem of the display. I have the Values printed out in the Serial.Monitor. It is allready glitching in the Monitor. But Thx
You got a very good solution from @qubits-us already using an interrupt pin!
The main problems of your sketch has already been mentioned above, redrawing the display all the time delays loop() so that it cannot read the encoder pins in due time.
Just as an example here (almost) your code but with a few changes:
/*
Forum: https://forum.arduino.cc/t/oled-rotary-encoder/1175366
Wokwi: https://wokwi.com/projects/377762218537644033
*/
//Screen
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
//Rotary Encoder
// Rotary Encoder Inputs
#define inputCLK 4
#define inputDT 5
String encdir = ""; //String to be populated with either ClockWise or CounterClockWise
//Variables
int Length = 20;
int oldLength = 0;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
//RotaryEncoder
// Set encoder pins as inputs
pinMode(inputCLK, INPUT);
pinMode(inputDT, INPUT);
}
void loop() {
readEncoder();
printToDisplay();
}
void readEncoder() {
static int previousStateCLK = HIGH; // Store the previous state for the next call of readEncoder()
//Rotary Encoder
// Read the current state of inputCLK
int currentStateCLK = digitalRead(inputCLK);
// If the previous and the current state of the inputCLK are different then a pulse has occured
if (currentStateCLK != previousStateCLK) {
int dtValue = digitalRead(inputDT);
if (currentStateCLK == LOW && dtValue == HIGH) {
// Encoder is rotating clockwise
Length++;
}
if (currentStateCLK == LOW && dtValue == LOW) {
// Encoder is rotating counterclockwise
Length--;
if (Length < 0) {
Length = 0;
}
};
}
// Update previousStateCLK with the current state
previousStateCLK = currentStateCLK;
}
void printToDisplay() {
if (Length != oldLength) {
//Oled
oldLength = Length;
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
//Timer
display.setCursor(0, 10);
display.print("Timer - ");
display.print(Length);
display.println(" Minutes");
display.display();
}
}
The display is only rewritten if the data have changed.
So in case you come into a situation where you cannot effort to "sacrifice" one of the two interrupt pins of your UNO you might use an Encoder also without that.
But the main goal is to show you that if you can avoid delaying functions microcontrollers may handle things with an amazing speed ...