Arduino "simple" menu system a bit too complicated...

Hi! I've been trying to make a "simple" menu system for a 128 x 64 OLED with a rotary encoder (with pushbutton) since the ones online didn't really work or didn't fit my needs and were a bit complicated. So I wanted to create my own, way simpler. Or so I thought...

This is what I came up with after working on it for way too long:

#include <Bounce2.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "bitmaps.h"

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)

#define LOGO_HEIGHT   64
#define LOGO_WIDTH    128

#define sw      5
#define outputA 6
#define outputB 7

#define VCC 8

//////////////////////////////////////////
int noOfMenus = 3;
//////////////////////////////////////////

int counter = 0 ;
int aState      ;
int aLastState  ;
int lastSw      ;
int lastCounter ;
int currentSw   ;
int oldValue;
int x = 1;

bool drewDisplayMenu = false;
bool drewSettingsMenu = false;
bool drewPlaceholderMenu = false;
bool exittedSettingsMenu = false;
bool wentThruSubSettings = false;
bool firstTime = true;
bool pressedFirstTime = false;
bool releasedFirstTime = true;
bool ignoreEncoder = false;
//bool cleared = false;

Bounce debouncer = Bounce();

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
  pinMode (outputA,   INPUT);
  pinMode (outputB,   INPUT);
  pinMode (sw, INPUT_PULLUP);

  pinMode (VCC, OUTPUT);
  digitalWrite(VCC, HIGH);

  debouncer.attach(sw);
  debouncer.interval(5); // interval in ms

  Serial.begin (9600);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't proceed, loop forever
  }

  aLastState = digitalRead(outputA);

  display.display();
  display.clearDisplay();
  display.display();
}

void loop() {

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Button debouncing with Bounce2

  debouncer.update();
  int value = debouncer.read();

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Encoder algorythm

if (ignoreEncoder == false) {
  aState = digitalRead(outputA); // Reads the "current" state of the outputA
  // If the previous and the current state of the outputA are different, that means a Pulse has occured
  if (aState != aLastState) {
    // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
    if (digitalRead(outputB) != aState) {
      counter ++;
    } else {
      counter --;
    }
  }
}

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Calculating menu position

  int counterer = counter / 2;

  if (counterer % noOfMenus == noOfMenus - noOfMenus) {

    drewDisplayMenu = false;
    drewPlaceholderMenu = false;
    drawSettingsMenu();

    if (value == HIGH) {
      releasedFirstTime = true;
    }

    if (value == LOW && firstTime == true && releasedFirstTime == true) {
      drawSubSettings(counterer);
      pressedFirstTime = true;
    }

    if (value == HIGH && pressedFirstTime == true) {
      drawSubSettings(counterer);
      firstTime = false;
    }

    if (value == LOW && firstTime == false) {
      drewSettingsMenu = false;
      firstTime = true;
      pressedFirstTime = false;
      releasedFirstTime = false;
      ignoreEncoder = false;
    }

    /* if (value == 0) { //pressed
       drawSubSettings();
      }

      if (wentThruSubSettings == true) {

         drewSettingsMenu = false;

      }
    */
  }

  if (counterer % noOfMenus == noOfMenus - noOfMenus + 1 || counterer % noOfMenus == noOfMenus - noOfMenus - 1) {
    drewSettingsMenu = false;
    drewPlaceholderMenu = false;

    drawDataDisplayMenu();
  }

  if (counterer % noOfMenus == noOfMenus - noOfMenus + 2 || counterer % noOfMenus == noOfMenus - noOfMenus - 2) {
    drewSettingsMenu = false;
    drewDisplayMenu = false;

    drawPlaceholderMenu();
  }

  if (drewDisplayMenu == true && drewSettingsMenu == true) {
    drewDisplayMenu = false;
    drewSettingsMenu = false;
  }

  aLastState  =    aState; // Updates the previous state of the outputA with the current state
  lastCounter = counterer;
  lastSw      =     value;
  oldValue = value;

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Optional debug

  //Serial.println(value);

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Drawing functions

void drawSettingsMenu(void) {
  if (!drewSettingsMenu) {
    display.clearDisplay();
    display.drawBitmap((display.width() - LOGO_WIDTH ) / 2, (display.height() - LOGO_HEIGHT) / 2, settingsMenu, LOGO_WIDTH, LOGO_HEIGHT, 1);
    display.display();
    drewSettingsMenu = true;
  }
}

void drawSubSettings(int secondValue) {


  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println(analogRead(A0));
  display.println(secondValue);
  display.display();

  ignoreEncoder = true;


}

void drawDataDisplayMenu(void) {
  if (!drewDisplayMenu) {
    display.clearDisplay();
    display.drawBitmap((display.width() - LOGO_WIDTH ) / 2, (display.height() - LOGO_HEIGHT) / 2, dataDisplayMenu, LOGO_WIDTH, LOGO_HEIGHT, 1);
    display.display();
    drewDisplayMenu = true;
  }
}

void drawPlaceholderMenu(void) {
  if (!drewPlaceholderMenu) {
    display.clearDisplay();
    display.drawBitmap((display.width() - LOGO_WIDTH ) / 2, (display.height() - LOGO_HEIGHT) / 2, placeholderMenu, LOGO_WIDTH, LOGO_HEIGHT, 1);
    display.display();
    drewPlaceholderMenu = true;
  }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// End of program

(the bitmap.h didn't fit so it's attached)

And for some unknown reason, it works ;D . But I wanted to make it... maybe more compact?

Its purpose:

variable number of menus (atm only 3)
scroll through them with encoder
click encoder on "settings" menu
get analogRead(A0) and the current encoder count/cycle (also ignore encoder so things don't get messed up)
click again to exit submenu of settings
get back to scrolling through menus

How could I make the code less ugly and more compact?

Thanks.

bitmaps.h (18.4 KB)

  1. A variable minus itself will always be zero.
noOfMenus - noOfMenus
  1. Better use of parameters
    Both of these functions are identical except for 2 variables. By utilizing the input/output parameters you can do both with a single function.
void drawDataDisplayMenu(void) 
void drawPlaceholderMenu(void)
  1. Research "State machine"
    An if statement is a very basic state machine. More if statements = more complex state machine.
    It can be worthwhile to investigate other methods such as the switch statement.

What about this piece of code that uses so many bools?

if (value == HIGH) {
      releasedFirstTime = true;
    }

    if (value == LOW && firstTime == true && releasedFirstTime == true) {
      drawSubSettings(counterer);
      pressedFirstTime = true;
    }

    if (value == HIGH && pressedFirstTime == true) {
      drawSubSettings(counterer);
      firstTime = false;
    }

    if (value == LOW && firstTime == false) {
      drewSettingsMenu = false;
      firstTime = true;
      pressedFirstTime = false;
      releasedFirstTime = false;
      ignoreEncoder = false;
    }

Its purpose is to, when a main menu item is selected, you click and release the button to enter the submenu and then click and release again to exit it. Is there a better way? (i'm sure there is, i want to know what it is)

. Is there a better way? (i'm sure there is, i want to know what it is)

Yes, I'm sure I mentioned it in my last post.

  1. Research "State machine"
    An if statement is a very basic state machine. More if statements = more complex state machine.
    It can be worthwhile to investigate other methods such as the switch statement.