LCD Sub-Menu Functions with 4x20 OLED?

@ironmarvel
in post #35 I have asked you if and if yes how you have tried the default liquid crystal library.

I've just connected a default (HD44780) LCD with your code, modified the rs/rw pins and voila ... a HD44780 LCD works with the code from your starting post.

So it is VERY LIKELY that a LCD library will work with your OLED Display when you set up the pins correctly.

And yes ... I would have a "simple" Menu code for a LCD with standard LCD API...

@ironmarvel Here is the link to the software, the menu stuff in when it is in the setup mode not the play mode.
midi trill guitar

I would say it is very unlikely an LCD library would work with an OLED display.

An LCD library has no concept of a buffer that contains all the data written to the display. An LCD driver simply writes the ASCII characters directly into the LCD display at a character position set up or by default the position after the previous position.

Where as all OLEDs have a buffer that matches the display bit for pixel. The act of setting up the display is the act of writing into this buffer. The act of showing the display is to move this buffer into the display.

I think with this OLED-display it is indeed different.
Here is the low-level bitbanging of how the characters are transmitted into the OLED-display

  1. set port D to the 8bit-value of character
  2. toggle latch

transmitting character done

or do the same with low/high-nibble of a byte

void SendCharToLCD(char i) {
  switch (InterfaceMode) {

    case 4: PORTD = i;
      digitalWrite(R_S, HIGH);              // Data
      digitalWrite(R_W, LOW);               // Write
      latch();                              // Latch upper 4 bits
      i = i << 4;                           // shift 4 bits
      PORTD = i;                            // Take lower 4 bits
      latch();                              // Latch lower 4 bits
      break;

    case 8: PORTD = i;
      digitalWrite(R_S, HIGH);              // Data
      digitalWrite(R_W, LOW);               // Write
      latch();                              // Latch 8 bits
      break;
  }
}

Some displays have a default ASCII mode where you can print to them just like a text LCD. You use this to annotate graphs. Spec start location and print. A device might emulate an LCD for that level of functionality. You won't be able to dot plot using it, but it should simplify text only apps.

Remind me again what this OLED is. Did you post a link to it?

https://newhavendisplay.com/4x20-character-blue-oled-module/

correction

alert to @ironmarvel

the description says
This module has 4/8-bit Parallel, SPI or I2C interface compatibility.

Uh wow! If your display is really exactly this one this means there are more options to connect it to the Arduino
best regards Stefan

1 Like

Ahr yes the stupidly expensive one. The one where you are supposed to use Port D, the port that has the TX and RX pins on it.

Seriously you consider this a viable option?

The TO has bought this display and wants to use it.
finally I found the datasheet for this display

on page 4 the datasheet shows all interface options


this means there is a chance to use this US2066-library

There are three pins BS0, BS1, BS2 to select the interface type

Which are described here

best regards Stefan

Awesome! My son has it setup for 8-bit parallel. It would be great if that library can be used! Are you able to get one of these displays Stefan?

I haven't reaserched if I can buy one. But I don't want to. Too expensive.

usually I use these displays

1 Like

This is true!

Re-configuring the display for SPI should be quite easy

The demo-code of the library has the documentation which IO-pin to connect to which pin of the newhavendisplay

1 Like

We're back from holiday! Haven't had much time trying to switch over to the SPI interface but we made a few modifications to the keypad definitions:

#include <Keypad.h>

const byte ROWS = 2; // 2 rows
const byte COLS = 2; // 2 columns
char keys[ROWS][COLS] = {
  {'1','2'},
  {'4','5'},
};
byte rowPins[ROWS] = {0, 1}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {2, 3}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup(){
  Serial.begin(115200); // make sure to adjust the serial monitor to 115200 baud
  
}

void loop(){
  char key = keypad.getKey();

  if (key == '1'){
    Serial.println("UP Key Pressed");
  }
  if (key == '4'){
    Serial.println("DOWN Key Pressed");
  }
  if (key == '2'){
    Serial.println("SELECT Key Pressed");
  }
  if (key == '5'){
    Serial.println("MAIN MENU Key Pressed");
  }
}

This change happened as we only needed 4 total buttons, so to save 4 extra Arduino pins, we will leave the other keypad lines disconnected. Plus, we can connect more LEDs later!

Would the SPI interface be easier to implement a menu with? @stefanl38tmp @Grumpy_Mike @noiasca @madmark2150

No. When the LCD library is based on the LCD API it doesnt matter much which hardware interface is used.

Cool. Any ideas on how to setup a menu like we explained further up?

There are many many ways to implement a menu. The more complex you get the less intuitive it becomes. A lot depends on your final application.

The big thing in my opinion is being consistent. That is always have the same way of getting to a parameter, and always the same way of changing a number.

For example when changing a parameter you have to choice of restricting the value between two limits, or letting it change without limits. If you hit a limit do you stick at that limit or do you wrap round to the beginning. Once you have reached the right number what happens? Does it require some sort of "that's it" confirmation or do you carry on and move back to another screen? Do you want an abandon message?

Do you climb out of a deep menu, level by level or do you go back to the top?

The possibility are endless. So don't try and make up the structure of the menu as you code. Make sure you have a plan and if you have not coded many menus before make that plan on a piece of paper before you begin to code. Once you do that you will find that the code is simple to implement. Have a function for each screen, don't try and do it all in one function or you will end up with a unfathomable mess.

We would climb out of the menu with one dedicated button. For the time being, we are only interested in turning LEDs on and off

How can the code below be modified to use a 4x20 LCD for a sub-menu system? For instance, my son and I would like a system like this:

START MENU/MAIN MENU will have up to 4 options:

1. //
2. //
3. LED LIGHTS!
4. //

These options will be user selectable using the UP, DOWN, SELECT, and MAIN MENU keypad buttons to navigate. '1' for UP, '4' for DOWN, '2' for SELECT, and '5' for MAIN MENU, like this:

LCD

When the 3. LED LIGHTS! option is selected, the LCD changes to a sub menu that has:

>LED 1 ON
 LED 2 OFF
 LED 3 OFF
 LED 4 OFF

Again, the user can select which LED to control using the UP/DOWN keys and pressing the SELECT key to toggle them on and off, similar to how this GIF shows:

LCD2

Template Code:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

int upButton = 10;
int downButton = 11;
int selectButton = 12;
int menu = 1;

void setup() {
  lcd.begin();
  lcd.backlight();
  pinMode(upButton, INPUT_PULLUP);
  pinMode(downButton, INPUT_PULLUP);
  pinMode(selectButton, INPUT_PULLUP);
  updateMenu();
}

void loop() {
  if (!digitalRead(downButton)){
    menu++;
    updateMenu();
    delay(100);
    while (!digitalRead(downButton));
  }
  if (!digitalRead(upButton)){
    menu--;
    updateMenu();
    delay(100);
    while(!digitalRead(upButton));
  }
  if (!digitalRead(selectButton)){
    executeAction();
    updateMenu();
    delay(100);
    while (!digitalRead(selectButton));
  }
}

void updateMenu() {
  switch (menu) {
    case 0:
      menu = 1;
      break;
    case 1:
      lcd.clear();
      lcd.print(">MenuItem1");
      lcd.setCursor(0, 1);
      lcd.print(" MenuItem2");
      break;
    case 2:
      lcd.clear();
      lcd.print(" MenuItem1");
      lcd.setCursor(0, 1);
      lcd.print(">MenuItem2");
      break;
    case 3:
      lcd.clear();
      lcd.print(">MenuItem3");
      lcd.setCursor(0, 1);
      lcd.print(" MenuItem4");
      break;
    case 4:
      lcd.clear();
      lcd.print(" MenuItem3");
      lcd.setCursor(0, 1);
      lcd.print(">MenuItem4");
      break;
    case 5:
      menu = 4;
      break;
  }
}

void executeAction() {
  switch (menu) {
    case 1:
      action1();
      break;
    case 2:
      action2();
      break;
    case 3:
      action3();
      break;
    case 4:
      action4();
      break;
  }
}

void action1() {
  lcd.clear();
  lcd.print(">Executing #1");
  delay(1500);
}
void action2() {
  lcd.clear();
  lcd.print(">Executing #2");
  delay(1500);
}
void action3() {
  lcd.clear();
  lcd.print(">Executing #3");
  delay(1500);
}
void action4() {
  lcd.clear();
  lcd.print(">Executing #4");
  delay(1500);
}

Do not cross post.