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.

Most OpenMenuOS problems fall into four categories: display issues caused by TFT_eSPI misconfiguration, input issues from incorrect pin setup, memory issues that appear on ESP8266, and performance issues from blocking code or unoptimized rendering. Use the debug snippet at the bottom of this page as a first step with any new hardware setup.
Blank screen on startupThe most common cause is a mismatch between your hardware wiring and the pin definitions in your TFT_eSPI User_Setup.h file.
  1. Open libraries/TFT_eSPI/User_Setup.h and verify that TFT_MOSI, TFT_SCLK, TFT_CS, TFT_DC, and TFT_RST match your actual wiring.
  2. Make sure you have selected the correct driver (e.g. ILI9341_DRIVER, ST7789_DRIVER) for your display model.
  3. Confirm the display is receiving the correct supply voltage (3.3 V for most SPI TFT modules when driven by ESP32/ESP8266).
// Quick sanity check in setup()
Serial.println("TFT width:  " + String(menu.getTftWidth()));
Serial.println("TFT height: " + String(menu.getTftHeight()));
// If either prints 0, TFT_eSPI did not initialize correctly

Wrong colors / inverted colorsColor inversion or channel swapping is a TFT_eSPI color-order setting. In User_Setup.h, try toggling:
#define TFT_RGB_ORDER TFT_BGR  // or TFT_RGB — swap if colors look wrong

Flickering displayFlickering usually means the canvas is being pushed to the display on every frame, including frames where nothing changed. Enable frame comparison to skip redundant transfers:
menu.setOptimizeDisplayUpdates(true);
If flickering persists, check for conflicting SPI devices sharing the bus without proper CS management.
Buttons not responding
  1. Verify the pin numbers passed to the OpenMenuOS constructor or configured with setUpPin() / setDownPin() / setSelectPin() match the physical GPIO you wired.
  2. Check the active voltage level. By default the library expects buttons to pull the pin low when pressed. If your buttons pull high, call:
menu.setButtonsMode("High"); // "High" or "low"
  1. Confirm INPUT_PULLUP is appropriate for your circuit. External pull-down resistors require the "High" mode.

Rotary encoder not workingEncoder support requires both CLK and DT pins to be registered before menu.begin():
menu.setEncoderPin(5, 2);  // CLK pin, DT pin
menu.setSelectPin(19);     // encoder push-button
Check that your encoder’s CLK and DT are not swapped — swapped wires reverse the direction but otherwise work. If the encoder is connected to pins that also have a boot-mode function on ESP32 (GPIO 0, 2, 15), move it to a different pin pair.
Multiple triggers per press (debounce)If a single button press registers two or more actions, the cause is usually electrical noise or a button without hardware debouncing. Software options:
  • Add a small capacitor (100 nF) between the button pin and GND.
  • Use INPUT_PULLUP mode with an internal pull-up, which helps reject noise on floating lines.
  • Avoid running long wires to buttons without shielding.
Crashes or watchdog resets on ESP8266The most common ESP8266 crash pattern is caused by enabling setOptimizeDisplayUpdates(true) on a large display. Frame comparison buffers the entire previous frame in heap RAM. A 320×240 16-bit display requires ~150 KB of heap — more than ESP8266 has available. Disable it:
menu.setOptimizeDisplayUpdates(false); // required for large displays on ESP8266
Monitor free heap to catch other memory pressure early:
Serial.println("Free heap: " + String(ESP.getFreeHeap()));

Heap overflow / stack overflow
  • Keep SettingsScreen instances to 10 settings or fewer (MAX_ITEMS = 10). Exceeding this limit corrupts memory.
  • Avoid allocating String objects inside menu callbacks on every call. Use static local variables or const char* literals.
  • Reduce the number of simultaneously active screens in the navigation stack.

EEPROM / Preferences corruptionIf settings values read back as garbage after a firmware update or power loss, reset all stored settings to their defaults:
settingsScreen.resetSettings();
// Requires a restart to take effect
ESP.restart();
EEPROM IDs are generated from a hash of each setting’s name and type. Renaming a setting or changing its type invalidates the stored value for that entry. Call resetSettings() whenever you change setting names in your sketch.
Slow menu responseStart with the two most effective settings:
menu.setAnimation(false);   // remove scroll easing delay
menu.setTextScroll(false);  // stop per-frame horizontal scroll redraws

High CPU / SPI bus saturationIf the CPU usage is high even when the display is idle, enable frame comparison so unchanged frames are not pushed over SPI:
menu.setOptimizeDisplayUpdates(true); // skip SPI transfer when frame is unchanged

Jerky or stuttering animationsAnimations stutter when loop() takes too long on individual iterations. The usual cause is blocking code — delay(), slow I2C reads, or heavy computation — that prevents the menu from updating smoothly. Audit your callbacks and loop() body for any blocking calls and replace them with non-blocking millis() patterns.
// Replace this
delay(500);

// With this
static unsigned long lastAction = 0;
if (millis() - lastAction >= 500) {
  lastAction = millis();
  // do the work
}
Also ensure PopupManager::update() is called before menu.loop() on every iteration. Calling them out of order can cause missed frames.

Debug code snippet

Print key system information to Serial at startup to rule out configuration issues quickly:
void setup() {
  Serial.begin(115200);

  menu.begin(&mainMenu);

  // Library and display info
  Serial.println("Library version: " + String(menu.getLibraryVersion()));
  Serial.println("Display size: " + String(menu.getTftWidth()) + "x" + String(menu.getTftHeight()));

  // Memory
  Serial.println("Free heap: " + String(ESP.getFreeHeap()));
}
To dump all current settings values to Serial:
// Print every setting index and its current value
for (int i = 0; i < settingsScreen.totalSettings; i++) {
  Serial.println(
    settingsScreen.getSettingName(i) + ": " +
    String(settingsScreen.getSettingValue(i))
  );
}
getSettingValue(int index) returns a uint8_t. For BOOLEAN settings, 0 = off and 1 = on. For RANGE and OPTION settings, the raw numeric value is returned.

Build docs developers (and LLMs) love