I have this simple program, where I am implementing a menu structure on an OLED display and a rotary encoder.
The code works functionally as intended, however when I add more menu items, then I get some artefacts on the display, in the lower right corner (see screenshots).
The code that I am referring to is inside the block comment in initMenu().
When I uncomment that block, the artefacts start appearing, every other time I turn the knob on the encoder. Sometimes it temporarily disappears, or slightly changes its shape.
As I said, other than that, the code behaves as expected.
Could anyone maybe shed some light on why this happens?
#define DEBUG 1
// Display
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display;
// Rotary encoder
#include "RotaryEncoder.h"
#include "YetAnotherPcInt.h"
#define RTRY_ENC_SW A1
#define RTRY_ENC_CLK A2
#define RTRY_ENC_DT A3
RotaryEncoder encoder(RTRY_ENC_CLK, RTRY_ENC_DT);
void onSwitch();
void onRotate();
typedef struct MenuItem {
String name;
byte id = 0;
boolean active = false;
MenuItem* prevSibling;
MenuItem* nextSibling;
MenuItem* currentChild;
};
MenuItem autoModes;
MenuItem manualModes;
MenuItem settings;
MenuItem peakAverage;
MenuItem peakToPeak;
MenuItem rms;
MenuItem* currentItem;
MenuItem* activeMode;
void setup() {
#ifdef DEBUG
Serial.begin(115200);
#endif
initEncoder();
initMenu();
initDisplay();
}
boolean hasInputs = false;
boolean switchPressed = false;
void loop() {
if (hasInputs == true) {
hasInputs = false;
handleButtonInputs();
}
}
void initEncoder() {
pinMode(RTRY_ENC_SW, INPUT_PULLUP);
PcInt::attachInterrupt(RTRY_ENC_SW, onSwitch, FALLING);
PcInt::attachInterrupt(RTRY_ENC_CLK, onRotate, CHANGE);
PcInt::attachInterrupt(RTRY_ENC_DT, onRotate, CHANGE);
}
void initMenu() {
autoModes.name = "Auto";
autoModes.id = 1;
autoModes.active = true;
autoModes.prevSibling = &settings;
autoModes.nextSibling = &manualModes;
autoModes.currentChild = &peakAverage;
manualModes.name = "Manual";
manualModes.id = 2;
manualModes.prevSibling = &autoModes;
manualModes.nextSibling = &settings;
settings.name = "Config";
settings.id = 3;
settings.prevSibling = &manualModes;
settings.nextSibling = &autoModes;
/*
peakAverage.name = "PAvg";
peakAverage.id = 4;
peakAverage.active = true;
peakAverage.prevSibling = &rms;
peakAverage.nextSibling = &peakToPeak;
peakToPeak.name = "P2P";
peakToPeak.id = 5;
peakToPeak.prevSibling = &peakAverage;
peakToPeak.nextSibling = &rms;
rms.name = "RMS";
rms.id = 6;
rms.prevSibling = &peakToPeak;
rms.nextSibling = &peakAverage;
activeMode = &peakAverage;
*/
currentItem = &autoModes;
}
void initDisplay() {
display = Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setCursor(36, 24);
display.setTextSize(2);
display.setTextColor(1);
display.print("Hello");
display.display();
}
void handleButtonInputs() {
if (switchPressed) {
switchPressed = false;
if (currentItem->id <= 3) {
currentItem = currentItem->currentChild;
printMenu();
} else if (4 <= currentItem->id && currentItem->id <= 6) {
if (activeMode->id != currentItem->id) {
activeMode->active = false;
currentItem->active = true;
activeMode = currentItem;
}
currentItem = &autoModes;
printMenu();
}
}
RotaryEncoder::Direction direction = encoder.getDirection();
if (direction == RotaryEncoder::Direction::CLOCKWISE) {
#ifdef DEBUG
Serial.println("clockwise");
#endif
currentItem = currentItem->nextSibling;
printMenu();
} else if (direction == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
#ifdef DEBUG
Serial.println("counterclockwise");
#endif
currentItem = currentItem->prevSibling;
printMenu();
} else if (direction == RotaryEncoder::Direction::NOROTATION ) {
#ifdef DEBUG
Serial.println("no rotation");
#endif
// ?
}
}
void printMenu() {
#ifdef DEBUG
Serial.println(currentItem->prevSibling->name);
#endif
display.clearDisplay();
display.setTextSize(2);
Serial.println(currentItem->prevSibling->name);
int x1 = 64 - currentItem->prevSibling->name.length() * 6;
if (currentItem->prevSibling->active) {
display.setTextColor(0, 1);
display.fillRect(x1 - 2, 0, 2, 16, 1);
} else {
display.setTextColor(1, 0);
}
display.setCursor(x1, 0);
display.print(currentItem->prevSibling->name);
#ifdef DEBUG
Serial.println(currentItem->name);
#endif
int x2 = 64 - currentItem->name.length() * 6;
int width = currentItem->name.length() * 12 + 10;
if (currentItem->active) {
display.setTextColor(0, 1);
display.fillRect(x2 - 6, 19, width, 24, 1);
} else {
display.setTextColor(1, 0);
display.drawRect(x2 - 6, 19, width, 24, 1);
}
display.setCursor(x2, 23);
display.print(currentItem->name);
#ifdef DEBUG
Serial.println(currentItem->nextSibling->name);
#endif
int x3 = 64 - currentItem->nextSibling->name.length() * 6;
if (currentItem->nextSibling->active) {
display.setTextColor(0, 1);
display.fillRect(x3 - 2, 47, 2, 16, 1);
} else {
display.setTextColor(1, 0);
}
display.setCursor(x3, 47);
display.print(currentItem->nextSibling->name);
display.display();
}
void onSwitch() {
hasInputs = true;
switchPressed = true;
}
void onRotate() {
hasInputs = true;
encoder.tick();
}
This is the output of the compilation process regarding program size:
Sketch uses 18198 bytes (59%) of program storage space. Maximum is 30720 bytes.
Global variables use 827 bytes (40%) of dynamic memory, leaving 1221 bytes for local variables. Maximum is 2048 bytes.


(4 <= currentItem->id <= 6)is parsed as((4 <= currentItem->id) <= 6)which is always true since comparisons evaluate to 0 or 1. You need to split it up((4 <= currentItem->id) && (currentItem->id <= 6)).printMenuinto smaller chunks? Put all constant Strings to PROGMEM?