Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/The-Young-Maker/OpenMenuOS/llms.txt

Use this file to discover all available pages before exploring further.

OpenMenuOS is designed to run on constrained microcontrollers where every millisecond and every kilobyte matters. By enabling the right options and avoiding a few common pitfalls, you can get fast, fluid menus even on an ESP8266 driving a mid-size TFT.

Frame comparison optimization

The most impactful single setting is setOptimizeDisplayUpdates(true). When enabled, the library compares the current canvas frame against the previous one before transferring it to the display over SPI. If nothing has changed, the transfer is skipped entirely — saving both CPU cycles and SPI bus time.
menu.setOptimizeDisplayUpdates(true);
You can also read the current state:
bool isOptimized = menu.getOptimizeDisplayUpdates();
Enable setOptimizeDisplayUpdates(true) on any project where the display is idle for more than a few frames per second. Static menus and confirmation popups benefit the most because their frames are identical between user interactions.
ESP8266 with large displays: disable this optimization. Frame comparison requires buffering a full copy of the previous frame in RAM. On ESP8266, a 320×240 16-bit framebuffer costs ~150 KB — more RAM than the chip has available for heap. Set setOptimizeDisplayUpdates(false) (the default) and rely on the other techniques below instead.

Animation control

Smooth scroll and selection animations look polished but cost CPU. Disabling them gives a snappier, instant-response feel that works well on lower-powered hardware or when your application is doing significant work elsewhere in loop().
menu.setAnimation(false); // disables scroll easing and transition animations
Re-enable when you want the visual polish back:
menu.setAnimation(true);
Button press animations are controlled separately with setButtonAnimation(bool). You can disable button animations while keeping menu scroll animations, or vice versa.
menu.setButtonAnimation(false); // disable only the button press animation

Text scroll control

Long menu item labels scroll horizontally by default. Each scroll step redraws the item, which increases the number of canvas writes per frame. If your labels are short enough to fit or you prefer static truncation, disable scrolling:
menu.setTextScroll(false);
Keeping menu labels short enough to fit without scrolling is the simplest optimization — it eliminates the scroll redraw entirely and makes the UI feel more immediate.

Memory tips

SettingsScreen item limit

SettingsScreen supports up to 10 settings items (MAX_ITEMS = 10). Exceeding this limit causes undefined behavior. Plan your settings hierarchy to stay within this bound per screen; use addSubscreenSetting() to link to a second SettingsScreen for overflow items.
// Good: split into two screens
settingsScreen.addSubscreenSetting("Advanced", &advancedSettingsScreen);

Avoid deep nesting

Each navigation level pushes the current screen onto ScreenManager’s history stack, which is a heap-allocated std::vector. Deeply nested menus — more than 4–5 levels — add both RAM pressure and code complexity. Flatten your hierarchy where possible.

Static variables in callbacks

Menu item action callbacks are called repeatedly whenever the item is selected. Avoid heap allocations inside them. Use static local variables for any state you need to persist across calls:
void toggleLED() {
  static bool ledState = false; // allocated once, not on every call
  ledState = !ledState;
  digitalWrite(LED_BUILTIN, ledState);
  PopupManager::showSuccess(ledState ? "LED On" : "LED Off");
}

Avoid blocking code in callbacks

Any delay() or long-running operation inside a callback blocks the entire loop(). PopupManager::update() and menu.loop() stop executing for the duration. Use non-blocking patterns with millis() timing instead:
// Avoid this in callbacks
void saveSettings() {
  delay(2000); // blocks loop() — don't do this
}

// Prefer this
unsigned long saveStarted = 0;

void saveSettings() {
  saveStarted = millis();
  PopupManager::showInfo("Saving...");
}

void loop() {
  PopupResult result = PopupManager::update();
  menu.loop();

  if (saveStarted > 0 && millis() - saveStarted > 2000) {
    saveStarted = 0;
    PopupManager::showSuccess("Saved!");
  }
}

Display tips

  • Solid colors over gradients: Anti-aliased and gradient draws cost more time than solid fills. Prefer flat color schemes for items and backgrounds.
  • Concise popup messages: Popup word-wrapping requires measuring and breaking text on each render. Short messages reduce that overhead.
  • Icons: If using menu item icons, keep them small (16×16 pixels or less). Larger images take more time to blit onto the canvas.

Summary of settings

void setup() {
  // Enable frame comparison (ESP32 / small displays only)
  menu.setOptimizeDisplayUpdates(true);

  // Disable animations for fastest response
  menu.setAnimation(false);

  // Disable button press animation
  menu.setButtonAnimation(false);

  // Disable horizontal text scrolling
  menu.setTextScroll(false);

  menu.begin(&mainMenu);
}

Build docs developers (and LLMs) love