Hello, so I am trying to create a simple menu to change two values using a rotary encoder (no breakout board). So far I've managed to get the home screen to display my two values and have two "menu screens" that I can cycle through where I should be able to change the values. For some reason the first menu screen works as intended, I rotate the encoder and the value goes up or down accordingly and is update when back on the home screen, the second menu screen does not display any change nor does the value in the home screen change. I'm honestly stumped and chatgpt says it should work so here I am. Any ideas or suggestions or critiques would be greatly appreciated.
Thanks!
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Encoder.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET 4
#define SCREEN_ADDRESS 0x3D
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Encoder encoder(5, 6);
const int BUTTON_PIN = 7;
enum Screen {
MAIN_SCREEN,
WARM_UP_SCREEN,
CUT_TIME_SCREEN
};
Screen currentScreen = MAIN_SCREEN;
Screen previousScreen = MAIN_SCREEN;
String warmUpTime = "4"; // Initialize warm-up time variable
String cutTime = "22"; // Initialize cut time variable
int encoderChange = 0; // Declare the encoderChange variable
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Initialize the OLED display
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
// Clear the display
display.clearDisplay();
// Set text size and color
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
}
void loop() {
int encoderValue = encoder.read();
if (digitalRead(BUTTON_PIN) == LOW) {
delay(250); // Debounce
switch (currentScreen) {
case MAIN_SCREEN:
currentScreen = WARM_UP_SCREEN;
break;
case WARM_UP_SCREEN:
currentScreen = CUT_TIME_SCREEN;
break;
case CUT_TIME_SCREEN:
currentScreen = MAIN_SCREEN;
break;
}
encoderChange = 0;
}
encoderChange = encoder.read();
if (encoderChange != 0) {
switch (currentScreen) {
case WARM_UP_SCREEN:
int newWarmUpTime = warmUpTime.toInt() + encoderChange;
// Ensure the new warm-up time is within a reasonable range
if (newWarmUpTime >= 0 && newWarmUpTime <= 30) {
warmUpTime = String(newWarmUpTime);
}
break;
case CUT_TIME_SCREEN:
int newCutTime = cutTime.toInt() + encoderChange;
// Ensure the new cut time is within a reasonable range
if (newCutTime >= 0 && newCutTime <= 60) {
cutTime = String(newCutTime);
}
break;
}
encoder.write(0); // Reset the encoder value
}
// Update the display within the loop
display.clearDisplay();
display.setCursor(0, 0);
switch (currentScreen) {
case MAIN_SCREEN:
display.setTextSize(1);
display.print("Warm Up Time: ");
display.print(warmUpTime);
display.println("s");
display.setCursor(0, 16);
display.print("Cut Time: ");
display.print(cutTime);
display.println("s");
break;
case WARM_UP_SCREEN:
display.setTextSize(2);
display.print("Warm Up");
display.setCursor(0, 24);
display.print("Time:");
display.setTextSize(2);
display.setCursor(60, 24);
display.print(warmUpTime);
display.println("s");
break;
case CUT_TIME_SCREEN:
display.setTextSize(2);
display.print("Cut Time:");
display.setTextSize(2);
display.setCursor(45, 24);
display.print(cutTime);
display.println("s");
break;
}
display.display();
// Update the previous screen
previousScreen = currentScreen;
}
Yes, as pointed out don't need the String objects, quite an easy change..
Encoder should be on pins that support an interrupt, moved them both to 2 and 3 which should be good for your board..
Updated the screen at loop speed, not so desirable..
Curious kind of looks like you knew this as you did already keep track of last screen..
So only clear screen when menu changes, added screen variable updating directly to the encoder switch..
oh, the encoder switch has locally declared vars in the cases, need to wrap entire case in {}..
seems to function better..
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Encoder.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET 4
#define SCREEN_ADDRESS 0x3D
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Encoder encoder(3, 2);
const int BUTTON_PIN = 7;
enum Screen {
MAIN_SCREEN,
WARM_UP_SCREEN,
CUT_TIME_SCREEN
};
Screen currentScreen = MAIN_SCREEN;
Screen previousScreen = WARM_UP_SCREEN;
int warmUpTime = 4; // Initialize warm-up time variable
int cutTime = 22; // Initialize cut time variable
int encoderChange = 0; // Declare the encoderChange variable
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Initialize the OLED display
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
// Clear the display
display.clearDisplay();
// Set text size and color
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
}
void loop() {
int encoderValue = encoder.read();
if (digitalRead(BUTTON_PIN) == LOW) {
delay(100); // Debounce
switch (currentScreen) {
case MAIN_SCREEN:
currentScreen = WARM_UP_SCREEN;
break;
case WARM_UP_SCREEN:
currentScreen = CUT_TIME_SCREEN;
break;
case CUT_TIME_SCREEN:
currentScreen = MAIN_SCREEN;
break;
}
encoderChange = 0;
}
encoderChange = encoder.read();
if (encoderChange != 0) {
switch (currentScreen) {
case WARM_UP_SCREEN: {
int newWarmUpTime = warmUpTime + encoderChange;
// Ensure the new warm-up time is within a reasonable range
if (newWarmUpTime >= 0 && newWarmUpTime <= 30) {
display.setCursor(60, 24);
display.setTextColor(SSD1306_BLACK);
display.print(warmUpTime);
display.println("s");
display.display();
display.setTextColor(SSD1306_WHITE);
warmUpTime = newWarmUpTime;
display.setCursor(60, 24);
display.print(warmUpTime);
display.println("s");
display.display();
}
break;
}
case CUT_TIME_SCREEN: {
int newCutTime = cutTime + encoderChange;
// Ensure the new cut time is within a reasonable range
if (newCutTime >= 0 && newCutTime <= 60) {
display.setCursor(45, 24);
display.setTextColor(SSD1306_BLACK);
display.print(cutTime);
display.println("s");
display.display();
display.setCursor(45, 24);
display.setTextColor(SSD1306_WHITE);
cutTime = newCutTime;
display.print(cutTime);
display.println("s");
display.display();
}
break;
}
}
encoder.write(0); // Reset the encoder value
}
if (currentScreen != previousScreen) {
// Update the display within the loop
display.clearDisplay();
display.setCursor(0, 0);
switch (currentScreen) {
case MAIN_SCREEN:
display.setTextSize(1);
display.print("Warm Up Time: ");
display.print(warmUpTime);
display.println("s");
display.setCursor(0, 16);
display.print("Cut Time: ");
display.print(cutTime);
display.println("s");
break;
case WARM_UP_SCREEN:
display.setTextSize(2);
display.print("Warm Up");
display.setCursor(0, 24);
display.print("Time:");
display.setTextSize(2);
display.setCursor(60, 24);
display.print(warmUpTime);
display.println("s");
break;
case CUT_TIME_SCREEN:
display.setTextSize(2);
display.print("Cut Time:");
display.setTextSize(2);
display.setCursor(45, 24);
display.print(cutTime);
display.println("s");
break;
}
display.display();
// Update the previous screen
previousScreen = currentScreen;
}
}
The fix was putting the encoder on interrupt pins, but sadly with the Micro I was already using those pins for data and clock on my screen. Switched to my mega2560 with additional interrupt pins and had no problems.