Alright, lets start a longer post ![]()
First, if you have a problem which is hard make it easy to look at it. For starters, format the code nicely. The Arduino IDE does a great deal helping you with that while you type. And even if you mess it up a bit it can help you format it better (at least indentation) by pressing Ctrl + T. And after deleting some blank pages you end up with the much more readable bit of code:
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <avr/pgmspace.h>
int itemsPerScreen;
const int fontSize = 8;
static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
static int enSW = 9;
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile uint16_t encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile uint16_t
oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
PROGMEM const char Messaging[] = "Messaging";
PROGMEM const char UVScanner[] = "UV Scanner";
PROGMEM const char SecuritySystem[] = "Security";
PROGMEM const char BACK[] = "Main Menu";
int cnt = 0;
int itemSelected, subMenuSelected;
int itemsToDisplay = 0;
int curPos, startPos, endPos;
const char* menu_table[] = {Messaging, UVScanner, SecuritySystem, BACK};
void setup() {
 display.begin();
 pinMode(enSW, INPUT_PULLUP);
 pinMode(pinA, INPUT_PULLUP);
 pinMode(pinB, INPUT_PULLUP);
 attachInterrupt(0, PinA, RISING);
 attachInterrupt(1, PinB, RISING);
 display.clearDisplay();
 display.setTextSize(fontSize / 8);
 display.setTextColor(WHITE);
 display.println(F("Initialising...."));
 display.display();
 MySerial.begin(1200);
 Serial.begin(1200);
 while (!Serial); //wait for serial to connect
}
void loop() {
 display.setTextSize(1);
 display.clearDisplay();
 display.setCursor(25, 0);
 display.println(F("MENU SYSTEM"));
 display.setCursor(20, 25);
 display.println(F("Press control to enter the menu system"));
 display.setTextSize(fontSize / 8);
 display.display();
Â
 // Build the top level menu if select Switch is pressed
 if (digitalRead(enSW) == 0) {
  while (digitalRead(enSW) == 0); //wait till switch is released.
  Buildmenu();
 }
}
/*******************************Utility Functions *******************************/
void PinA() {
 cli(); //stop interrupts happening before we read pin values
 reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
 if (reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  encoderPos --; //decrement the encoder's position count
  bFlag = 0; //reset flags for the next turn
  aFlag = 0; //reset flags for the next turn
 }
 else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
 sei(); //restart interrupts
}
void PinB() {
 cli(); //stop interrupts happening before we read pin values
 reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
 if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  encoderPos ++; //increment the encoder's position count
  bFlag = 0; //reset flags for the next turn
  aFlag = 0; //reset flags for the next turn
 }
 else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
 sei(); //restart interrupts
}
void Buildmenu() {
 int itemsPerScreen = 4; //CHANGING THIS INT WILL ADJUST THE NUMBER OF REPEATED PRINTS OF THE
 MENU OPTIONS ?? ?? ?? ?
 int menuLength = 4;
 do {
  startPos = encoderPos % menuLength;
  display.clearDisplay();
  endPos = itemsPerScreen;
  if (menuLength < itemsPerScreen)
  {
   endPos = menuLength - startPos;
  }
  if ((menuLength - startPos) < itemsPerScreen)
  {
   endPos = menuLength - startPos;
  }
  for (cnt = 0; cnt <= (endPos - 1); cnt++) {
   if (cnt == 0)
   {
    display.setCursor(0, 0);
    display.print(F("Scroll to select"));
    display.drawFastHLine(0, 10, 200, WHITE);
    display.setCursor(0, 16 + cnt * fontSize); // sets the selection pointer
    display.print(F("->"));
   }
   for (int i = 0; i < 1; i++) {
    display.setCursor(16, 16 + cnt * fontSize); // sets the height of the submenu text
    display.println((__FlashStringHelper*)menu_table[i]);
   }
  }
  display.display();
  cnt = 0;
  if (oldEncPos != encoderPos) {
   oldEncPos = encoderPos;
  }
 } while (digitalRead(enSW));
Â
 while (digitalRead(enSW) == 0); //wait till switch is reseleased
 cnt = 0;
 return startPos;
}
First off, the code is still blocking. Might not be terrible now or even in your project but remember it. If you want to be able to "multiple things" you need non-blocking code. But that will not contain delays() or while()'s of undefined length. And you still printing the menu over and over again because it's in a do-while.
Next up, at least he PROGMEM text to display problem is solved. You're left with other problems. Main reason, why do you think you loop with cnt? Isn't that already to go through all menu items that fit on the screen? Now for each menu item you want on the screen you have a for loop which will loop exactly once and thus only prints item number 0....
I would say, start by just trying to print a variable on screen that's the first menu item that you want to display. Aka a number which you can change between 0 and 3. If that works, try to print that menu item (just one item). And last, try to add the next items that still fit.
And if you want a challenge, try to make it non-blocking.
and for which items to display I once did:
 //Calculate first item to display based on menu position
 byte firstItemToDisplay;
 if(menuPosition == 0){
  firstItemToDisplay = 0;
 }
 else if((numberOf(Menu) - menuPosition) <= (MaxMenuItems - 2)){
  firstItemToDisplay = numberOf(Menu) - MaxMenuItems;
 }
 else{
  firstItemToDisplay = menuPosition - 1;
 }[/code
With numberOf(Menu) the total number of menu items. MenuPosition the position / selected item in that menu and MaxMenuItems the number of items on screen. This makes the selected item always the second item on screen accept if you selected the first item or an item which is not followed by enough items to fill the screen. You get it like [url=https://www.youtube.com/watch?v=6YWsU6Scc8c]this[/url] only I change items by pressing the switch and rotating changes the value. But that's just implementation. And yes, it's also on a SSD1306 but I use the U8G2 library.
And something that can cause weird bugs. If you read a variable that's also used by interrupts (and thus volatile) but it's longer then the word length (8-bit on a Arduino Uno/Nano/Mega etc) you need to disable interrupts before you read. Because reading it will be more then a single step it's possible for the interrupt to trigger in the middle not copying the right value. In this case, you use "volatile uint16_t encoderPos" and I assume a Uno (or at least 8-bit).
Also, if I'm crrect on the Uno (or variant) doing
[code]Â while (!Serial); //wait for serial to connect
is useless ![]()