- Professional PopupManager with 5 distinct popup types: Info, Success, Warning, Error, and Question
- Advanced popup features: Auto-close functionality for success messages, custom colors, icons, and automatic word wrapping
- Non-blocking operation with proper button debouncing and input validation
- Encoder support for popup navigation: rotate to select buttons, press to confirm
- Visual feedback with button selection indicators and semi-transparent overlays
- Input blocking ensures clicks don't reach underlying screens when popups are active
- Improved button handling with proper press/release detection preventing false triggers
- Optimized display performance with frame comparison and efficient canvas management
- Better input isolation between popups and menu systems
- Bug fixes: Canvas sprite creation, encoder pin validation, self-assignment fixes, null pointer checks
- Performance optimizations: ScrollTextHorizontal functions enhanced with sprite caching, calculateMaxCharacters improved with binary search algorithm
- Memory efficiency: Enhanced drawCanvasOnTFT with intelligent frame comparison
- Robust error handling for edge cases and invalid inputs
- MenuScreen and SettingsScreen constructors updated to make the title optional
- CustomScreen constructor supports optional title definition
- Overloaded
addItem
method introduced to use title fromnextScreen
automatically - Added
navigateBack()
function to return to previous screen - Settings system improved to check for duplicate names and types before adding
- Function added to reset settings to default (restart required)
- Animations can be enabled/disabled via new function
- EEPROM saving fixes for global setting objects
- Toggle switch flickering bugs fixed
- Cleaned and reorganized header (.h) and source (.cpp) files for maintainability
- Fixed toggle switch positioning and display bugs
- Added function to get parameter value by name
- Added style presets setting by number
- Added display rotation function
- Improved selection rectangle drawing and removed unnecessary variables
- Changed default button pins to -1
- Settings now use vectors and non-random IDs for EEPROM consistency
- Sub-screen support added in settings
- Added smooth animations and encoder support throughout the library
- Added visual indication for locked settings and unit support for ranges
- Various small improvements and bug fixes related to UI and settings handling
Here are the significant updates and improvements:
- Variables moved to the private section of the class for better encapsulation and maintainability.
- Fixed issues with scrollbar height for submenus.
- Fixed scrolling text issues with non-black background colors.
- Menus (main, submenus, and settings) now dynamically adapt to the display size.
- Unlimited screens and menu levels—replacing the previous limitation of 3 levels.
- Fixed main menu item scrolling issues when returning from submenus.
- Replaced the old boolean-only settings system with object-based settings.
- Supports a variety of data types: options, ranges, and booleans.
- Includes default value support (overridden when values are changed in EEPROM).
- Full functionality for all setting types with proper EEPROM storage (v3.1.0).
- Added a function to retrieve the library version.
- Added smoother text scrolling.
- Added the ability to set a boot image.
- Added font customization options for menus.
- Improved storage handling by using
preferences.h
on ESP32 andEEPROM
on ESP8266.
- Resolved an issue where long-pressing the select button did not correctly return to the last screen.
- Removed the
tft_bl
argument from the menu instance (can now be set inTFT_eSPI
's config).
This demo was recorded using the "Screenshot" feature provided by TFT_eSPI.
The visible lines on the recording are not present on the actual display; they are likely caused by noise during data transmission from the display to the computer.
Additionally, while the demo may appear laggy, this is due to the low recording frame rate. The display could not refresh while the screenshot data was being transmitted.
- Introduction
- Features
- Hardware Requirements
- Installation
- Quick Start
- PopupManager System
- Advanced Usage
- API Reference
- Examples
- Troubleshooting
- Performance Tips
- Contributing
- License
- 📖 Complete Documentation - Arduino IDE compatible overview
- 🔧 Full API Reference - Detailed method documentation
- 💡 Code Examples - Practical implementation guides
OpenMenuOS is a comprehensive Arduino library designed to create professional, responsive menu systems on TFT displays using the TFT_eSPI library. Compatible with ESP32 and ESP8266 boards, it provides an intuitive API for building complex user interfaces with minimal code.
- 🎛️ Multi-level menus with unlimited depth
- 📱 Responsive design that adapts to any display size
- 🎨 Customizable themes and style presets
- 📊 Dynamic scrollbars with smooth animations
- 🖼️ Icon support for enhanced visual appeal
- ⚡ Optimized rendering with frame comparison
- 💬 5 popup types: Info, Success, Warning, Error, Question
- 🎭 Visual customization: Colors, icons, shadows, rounded corners
- 🔄 Auto-close functionality for temporary notifications
- 📝 Automatic word wrapping for long messages
- 🎮 Multiple input methods: Buttons and rotary encoder support
- 🚫 Input blocking prevents accidental interactions
- ⚙️ Multiple data types: Boolean, Range, Option, Subscreen
- 💾 EEPROM/Preferences storage with automatic persistence
- 🔧 Dynamic configuration with real-time updates
- 🎚️ Animated toggle switches and range sliders
- 📋 Option lists with custom values
- 🔘 Button support with debouncing and long-press detection
- 🔄 Rotary encoder support with interrupt-driven processing
- 📱 Touch support (when available on display)
- ⌨️ Configurable pin assignments and voltage levels
- 🔒 Input validation and error prevention
- 🚀 Optimized rendering with sprite caching
- 💾 Memory efficient with intelligent resource management
- 🔄 Smooth animations with easing functions
- 📊 Frame rate optimization and display update control
- ⚡ Fast response times with optimized algorithms
- ESP32 (all variants)
- ESP8266 (NodeMCU, Wemos D1, etc.)
- Arduino-compatible boards with sufficient memory
- TFT displays supported by TFT_eSPI library
- Recommended: 160x128 or higher for optimal experience
- Color depth: 16-bit color (RGB565)
- Buttons: Up, Down, Select (configurable pins)
- Rotary encoder: CLK, DT, SW pins (optional)
- Voltage levels: 3.3V or 5V compatible
- Flash: ~50KB for library code
- RAM: 2-8KB depending on display size and features
- EEPROM/Preferences: 1-2KB for settings storage
- Open Arduino IDE
- Go to
Tools
→Manage Libraries...
- Search for "OpenMenuOS"
- Click
Install
- Download the ZIP file from GitHub
- In Arduino IDE:
Sketch
→Include Library
→Add .ZIP Library...
- Select the downloaded ZIP file
The following libraries are required and will be installed automatically:
- TFT_eSPI (for display control)
- Arduino Core libraries
✅ Arduino IDE 1.8.x: Fully supported
✅ Arduino IDE 2.x: Fully supported
✅ Documentation: Available in Tools
→ Manage Libraries
→ OpenMenuOS
→ More info
✅ Examples: Accessible via File
→ Examples
→ OpenMenuOS
✅ Auto-complete: Full IntelliSense support for all methods
📁 Library Structure for Arduino IDE:
OpenMenuOS/
├── src/ # Core library files
│ ├── OpenMenuOS.h # Main header file
│ └── OpenMenuOS.cpp # Implementation
├── examples/ # Example sketches
│ └── OpenMenuOS_Simple/ # Basic usage example
├── docs/ # Documentation
│ ├── README.md # Arduino IDE overview
│ ├── api.md # Complete API reference
│ └── examples.md # Code examples
├── library.properties # Arduino library metadata
└── README.md # This comprehensive guide
#include "OpenMenuOS.h"
// Pin configuration
#define BTN_UP 19 // Up button pin
#define BTN_DOWN -1 // Down button pin (disabled)
#define BTN_SELECT 2 // Select button pin
// Create menu system instance
OpenMenuOS menu(BTN_UP, BTN_DOWN, BTN_SELECT);
// Create screens
MenuScreen mainMenu("Main Menu");
SettingsScreen settingsScreen("Settings");
void setup() {
Serial.begin(115200);
// Configure settings
settingsScreen.addBooleanSetting("WiFi", true);
settingsScreen.addRangeSetting("Brightness", 0, 100, 75, "%");
// Configure main menu
mainMenu.addItem("Settings", &settingsScreen);
mainMenu.addItem("About", nullptr, showAbout);
// Apply style and start
menu.useStylePreset("Rabbit_R1");
menu.begin(&mainMenu);
}
void loop() {
menu.loop();
}
void showAbout() {
PopupManager::showInfo("OpenMenuOS v3.1.0\nProfessional Menu System", "About");
}
// Enable rotary encoder support
menu.setEncoderPin(5, 2); // CLK, DT pins
menu.setSelectPin(19); // Encoder button pin
OpenMenuOS includes a professional popup system with 5 built-in types:
PopupManager::showInfo("Operation completed successfully!", "Information");
PopupManager::showSuccess("Settings saved!", "Success");
// Auto-closes after 3 seconds
PopupManager::showWarning("Low battery detected", "Warning");
PopupManager::showError("Connection failed", "Error");
PopupResult result = PopupManager::showQuestion("Save changes?", "Confirm");
// Returns PopupResult::OK or PopupResult::CANCEL
PopupConfig config;
config.message = "Custom popup message";
config.title = "Custom Title";
config.type = PopupType::INFO;
config.autoClose = true;
config.autoCloseDelay = 5000; // 5 seconds
config.showButtons = true;
config.showCancelButton = true;
config.customIcon = myIcon;
config.customIconWidth = 32;
config.customIconHeight = 32;
PopupResult result = PopupManager::show(config);
void loop() {
// Handle popup interactions first
PopupResult popupResult = PopupManager::update();
// Only process menu if no popup is active
if (!PopupManager::isActive()) {
menu.loop();
}
// Handle popup results
if (popupResult != PopupResult::NONE) {
switch (popupResult) {
case PopupResult::OK:
Serial.println("User confirmed");
break;
case PopupResult::CANCEL:
Serial.println("User cancelled");
break;
}
}
}
CustomScreen myScreen("Custom");
void setup() {
// Define custom drawing function
myScreen.customDraw = []() {
canvas.fillScreen(TFT_BLACK);
canvas.setTextColor(TFT_WHITE);
canvas.drawString("Custom Content", 10, 50);
// Draw graphics
canvas.fillCircle(100, 100, 20, TFT_BLUE);
canvas.drawRect(50, 50, 100, 50, TFT_GREEN);
};
mainMenu.addItem("Custom Screen", &myScreen);
}
void updateMenu() {
mainMenu.clearItems();
if (wifiConnected) {
mainMenu.addItem("Network Settings", &networkMenu);
mainMenu.addItem("Cloud Sync", nullptr, syncToCloud);
} else {
mainMenu.addItem("Connect WiFi", nullptr, connectWiFi);
}
mainMenu.addItem("Local Settings", &localSettings);
}
void onBrightnessChange(uint8_t newValue) {
analogWrite(BACKLIGHT_PIN, newValue * 255 / 100);
}
void setup() {
settingsScreen.addRangeSetting("Brightness", 0, 100, 75, "%");
settingsScreen.setSettingCallback("Brightness", onBrightnessChange);
}
// Use built-in presets
menu.useStylePreset("Rabbit_R1"); // Modern blue theme
menu.useStylePreset("Default"); // Classic theme
// Custom styling
menu.setMenuStyle(1); // Style mode
menu.setSelectionBorderColor(TFT_CYAN); // Selection border
menu.setSelectionFillColor(TFT_DARKGREY); // Selection fill
menu.setScrollbar(true); // Enable scrollbar
menu.setScrollbarStyle(1); // Modern scrollbar
menu.setScrollbarColor(TFT_WHITE); // Scrollbar color
// Enable display optimization (saves CPU/memory)
menu.setOptimizeDisplayUpdates(true);
// Control animations
menu.setAnimation(true); // Enable smooth animations
// Disable text scrolling for better performance
menu.setTextScroll(false);
OpenMenuOS(int btn_up, int btn_down, int btn_sel)
btn_up
: Pin for up button (-1 to disable)btn_down
: Pin for down button (-1 to disable)btn_sel
: Pin for select button (-1 to disable)
void begin(Screen* mainMenu) // Initialize with default rotation
void begin(int rotation, Screen* mainMenu) // Initialize with custom rotation
void loop() // Main update loop
void redirectToScreen(Screen* screen) // Navigate to specific screen
void navigateBack() // Go back to previous screen
const char* getLibraryVersion() // Get library version string
void setDisplayRotation(int rotation) // Set display rotation (0-3)
void setOptimizeDisplayUpdates(bool enabled) // Enable frame comparison
bool getOptimizeDisplayUpdates() // Get optimization state
void drawCanvasOnTFT() // Manually update display
void useStylePreset(char* preset) // Apply predefined style
void useStylePreset(int preset) // Apply style by number
void setMenuStyle(int style) // Set menu appearance style
void setSelectionBorderColor(uint16_t color) // Selection border color
void setSelectionFillColor(uint16_t color) // Selection fill color
void setScrollbar(bool enabled) // Enable/disable scrollbar
void setScrollbarStyle(int style) // Set scrollbar appearance
void setScrollbarColor(uint16_t color) // Set scrollbar color
void setMenuFont(const GFXfont* font) // Set regular menu font
void setMenuFontBold(const GFXfont* font) // Set bold menu font
void setButtonsMode(char* mode) // "High" or "Low" button logic
void setEncoderPin(uint8_t clk, uint8_t dt) // Configure rotary encoder
void setUpPin(uint8_t pin) // Set up button pin
void setDownPin(uint8_t pin) // Set down button pin
void setSelectPin(uint8_t pin) // Set select button pin
void setAnimation(bool enabled) // Enable/disable animations
void setTextScroll(bool enabled) // Enable/disable text scrolling
void setButtonAnimation(bool enabled) // Enable button press animation
void showBootImage(bool enabled) // Show boot image on startup
void setBootImage(uint16_t* image, uint16_t h, uint16_t w) // Set custom boot image
int getTftHeight() // Get display height
int getTftWidth() // Get display width
int UpButton() // Get up button pin
int DownButton() // Get down button pin
int SelectButton() // Get select button pin
MenuScreen() // Create menu without title
MenuScreen(const char* title) // Create menu with title
void addItem(const char* label, Screen* nextScreen = nullptr,
ActionCallback action = nullptr, const uint16_t* image = nullptr)
void addItem(Screen* nextScreen, ActionCallback action = nullptr,
const uint16_t* image = nullptr) // Auto-use screen title
void clearItems() // Remove all items
int getIndex() // Get current selection index
void draw() // Render the menu
void handleInput() // Process user input
const char* getTitle() // Get menu title
SettingsScreen() // Create without title
SettingsScreen(const char* title) // Create with title
void addBooleanSetting(const char* name, bool defaultValue)
void addRangeSetting(const char* name, uint8_t min, uint8_t max,
uint8_t defaultValue, const char* unit = nullptr)
void addOptionSetting(const char* name, const char** options,
uint8_t count, uint8_t defaultIndex = 0)
void addSubscreenSetting(const char* name, Screen* targetScreen)
uint8_t getSettingValue(int index) // Get value by index
uint8_t getSettingValue(const char* name) // Get value by name
String getSettingName(int index) // Get setting name
Setting::Type getSettingType(uint8_t index) // Get setting type
void saveToEEPROM() // Save all settings
void readFromEEPROM() // Load all settings
void resetSettings() // Reset to defaults
void modify(int8_t direction, int index) // Modify setting by index
void modify(int8_t direction, const char* name) // Modify setting by name
CustomScreen() // Create without title
CustomScreen(const char* title) // Create with title
std::function<void()> customDraw // Custom drawing function
void draw() // Render the screen
void handleInput() // Process user input
static PopupResult show(const PopupConfig& config) // Show custom popup
static PopupResult showInfo(const char* message, const char* title = nullptr)
static PopupResult showSuccess(const char* message, const char* title = nullptr)
static PopupResult showWarning(const char* message, const char* title = nullptr)
static PopupResult showError(const char* message, const char* title = nullptr)
static PopupResult showQuestion(const char* message, const char* title = nullptr)
static PopupResult update() // Update popup state
static bool isActive() // Check if popup visible
static void hide() // Hide current popup
enum class PopupType {
INFO, // Information popup (blue)
SUCCESS, // Success popup (green, auto-close)
WARNING, // Warning popup (orange)
ERROR, // Error popup (red)
QUESTION // Question popup (cyan, Yes/No buttons)
};
enum class PopupResult {
NONE, // No interaction
OK, // OK/Yes button pressed
CANCEL // Cancel/No button pressed
};
struct PopupConfig {
const char* message = nullptr; // Popup message text
const char* title = nullptr; // Popup title (optional)
PopupType type = PopupType::INFO; // Popup type
bool showButtons = true; // Show action buttons
bool showCancelButton = false; // Show cancel/no button
bool autoClose = false; // Auto-close popup
uint32_t autoCloseDelay = 3000; // Auto-close delay (ms)
const uint16_t* customIcon = nullptr; // Custom icon image
uint16_t customIconWidth = 0; // Custom icon width
uint16_t customIconHeight = 0; // Custom icon height
};
enum class Setting::Type {
BOOLEAN, // Toggle switch (true/false)
RANGE, // Numeric range with min/max
OPTION, // Selection from predefined options
SUBSCREEN // Navigation to another screen
};
#include "OpenMenuOS.h"
// Hardware configuration
#define BTN_UP 19
#define BTN_SELECT 2
#define ENCODER_CLK 5
#define ENCODER_DT 2
// Menu system
OpenMenuOS menu(BTN_UP, -1, BTN_SELECT);
// Screens
MenuScreen mainMenu("Smart Device");
MenuScreen deviceMenu("Device Control");
MenuScreen networkMenu("Network");
SettingsScreen settingsScreen("Settings");
CustomScreen aboutScreen("About");
// Device state
bool ledState = false;
uint8_t brightness = 75;
bool wifiEnabled = true;
void setup() {
Serial.begin(115200);
// Configure custom about screen
aboutScreen.customDraw = []() {
canvas.fillScreen(TFT_BLACK);
canvas.setTextColor(TFT_WHITE);
canvas.setFreeFont(&FreeMono9pt7b);
canvas.drawString("Smart Device v1.0", 10, 30);
canvas.drawString("OpenMenuOS v" + String(menu.getLibraryVersion()), 10, 50);
canvas.drawString("ESP32-based IoT Device", 10, 70);
canvas.setTextColor(TFT_CYAN);
canvas.drawString("Press SELECT to return", 10, 100);
};
// Configure settings
settingsScreen.addBooleanSetting("WiFi", wifiEnabled);
settingsScreen.addBooleanSetting("Bluetooth", false);
settingsScreen.addRangeSetting("Brightness", 0, 100, brightness, "%");
settingsScreen.addRangeSetting("Volume", 0, 10, 5);
const char* themes[] = {"Dark", "Light", "Auto"};
settingsScreen.addOptionSetting("Theme", themes, 3, 0);
settingsScreen.addSubscreenSetting("Network Settings", &networkMenu);
// Configure device menu
deviceMenu.addItem("Toggle LED", nullptr, toggleLED);
deviceMenu.addItem("Brightness Control", nullptr, showBrightnessControl);
deviceMenu.addItem("Device Status", nullptr, showDeviceStatus);
deviceMenu.addItem("Reset Device", nullptr, resetDevice);
// Configure network menu
networkMenu.addItem("WiFi Settings", nullptr, configureWiFi);
networkMenu.addItem("Network Status", nullptr, showNetworkStatus);
networkMenu.addItem("Reset Network", nullptr, resetNetwork);
// Configure main menu
mainMenu.addItem("Device Control", &deviceMenu);
mainMenu.addItem("Settings", &settingsScreen);
mainMenu.addItem("Network", &networkMenu);
mainMenu.addItem("About", &aboutScreen);
// Setup hardware
menu.setEncoderPin(ENCODER_CLK, ENCODER_DT);
menu.useStylePreset("Rabbit_R1");
menu.setScrollbar(true);
menu.setAnimation(true);
// Initialize
menu.begin(&mainMenu);
PopupManager::showSuccess("System initialized successfully!", "Welcome");
}
void loop() {
// Handle popup interactions first
PopupResult popupResult = PopupManager::update();
// Only process menu if no popup is active
if (!PopupManager::isActive()) {
menu.loop();
}
// Handle popup results
if (popupResult != PopupResult::NONE) {
handlePopupResult(popupResult);
}
// Update settings-based configurations
brightness = settingsScreen.getSettingValue("Brightness");
wifiEnabled = settingsScreen.getSettingValue("WiFi");
}
// Action callbacks
void toggleLED() {
ledState = !ledState;
digitalWrite(LED_BUILTIN, ledState);
if (ledState) {
PopupManager::showSuccess("LED turned ON", "Success");
} else {
PopupManager::showInfo("LED turned OFF", "Info");
}
}
void showBrightnessControl() {
PopupConfig config;
config.message = "Current brightness: " + String(brightness) + "%\nAdjust in Settings menu";
config.title = "Brightness Control";
config.type = PopupType::INFO;
PopupManager::show(config);
}
void showDeviceStatus() {
String status = "LED: " + String(ledState ? "ON" : "OFF") + "\n";
status += "Brightness: " + String(brightness) + "%\n";
status += "WiFi: " + String(wifiEnabled ? "Enabled" : "Disabled") + "\n";
status += "Free Memory: " + String(ESP.getFreeHeap()) + " bytes";
PopupManager::showInfo(status.c_str(), "Device Status");
}
void resetDevice() {
PopupResult result = PopupManager::showQuestion("This will restart the device.\nContinue?", "Reset Device");
if (result == PopupResult::OK) {
PopupManager::showWarning("Restarting device...", "Reset");
delay(2000);
ESP.restart();
}
}
void configureWiFi() {
if (wifiEnabled) {
PopupManager::showInfo("WiFi configuration would open here.\nNot implemented in this example.", "WiFi Setup");
} else {
PopupManager::showWarning("WiFi is disabled.\nEnable in Settings first.", "WiFi Disabled");
}
}
void showNetworkStatus() {
String status = "WiFi: " + String(wifiEnabled ? "Enabled" : "Disabled") + "\n";
if (wifiEnabled) {
status += "Status: Connected\n";
status += "IP: 192.168.1.100\n";
status += "RSSI: -45 dBm";
} else {
status += "Status: Disabled";
}
PopupManager::showInfo(status.c_str(), "Network Status");
}
void resetNetwork() {
PopupResult result = PopupManager::showQuestion("Reset all network settings?", "Reset Network");
if (result == PopupResult::OK) {
PopupManager::showSuccess("Network settings reset!", "Success");
}
}
void handlePopupResult(PopupResult result) {
switch (result) {
case PopupResult::OK:
Serial.println("User confirmed action");
break;
case PopupResult::CANCEL:
Serial.println("User cancelled action");
break;
default:
break;
}
}
- Blank screen: Check TFT_eSPI configuration and wiring
- Wrong colors: Verify color order in TFT_eSPI settings
- Flickering: Enable display optimization with
setOptimizeDisplayUpdates(true)
- Buttons not responding: Check pin numbers and button voltage logic
- Encoder not working: Verify CLK/DT pin connections and enable interrupts
- Multiple triggers: Increase debounce time or check wiring
- Crashes on ESP8266: Disable display optimization for large displays
- Heap overflow: Reduce number of menu items or use simpler graphics
- EEPROM corruption: Call
resetSettings()
to restore defaults
- Slow response: Disable animations or text scrolling
- High CPU usage: Enable frame comparison optimization
- Jerky animations: Check for blocking code in main loop
// Enable debug output
Serial.println("Library version: " + String(menu.getLibraryVersion()));
Serial.println("Display size: " + String(menu.getTftWidth()) + "x" + String(menu.getTftHeight()));
Serial.println("Free heap: " + String(ESP.getFreeHeap()));
// Check settings values
for (int i = 0; i < settingsScreen.getTotalSettings(); i++) {
Serial.println("Setting " + String(i) + ": " + String(settingsScreen.getSettingValue(i)));
}
- Use
setOptimizeDisplayUpdates(false)
on ESP8266 with large displays - Minimize the number of concurrent menu items
- Use simple icons (16x16 pixels or smaller)
- Avoid deeply nested menu structures
- Enable frame comparison:
setOptimizeDisplayUpdates(true)
- Disable unnecessary animations:
setAnimation(false)
- Use shorter text to avoid scrolling:
setTextScroll(false)
- Optimize custom drawing functions
- Choose appropriate display update frequency
- Use solid colors instead of gradients
- Minimize the use of anti-aliased graphics
- Keep popup messages concise
- Initialize heavy objects in
setup()
, not in menu callbacks - Use static variables in callbacks to avoid repeated allocations
- Handle long-running operations asynchronously
- Test on target hardware early in development
We welcome contributions to OpenMenuOS! Here's how you can help:
- 🐛 Bug Reports: Report issues with detailed reproduction steps
- 💡 Feature Requests: Suggest new features or improvements
- 📝 Documentation: Improve documentation and examples
- 🔧 Code Contributions: Submit bug fixes and new features
- 🧪 Testing: Test on different hardware configurations
- 🎨 Design: Create new themes and visual improvements
- Fork the repository on GitHub
- Clone your fork locally
- Create a new branch for your feature/fix
- Make your changes following the coding standards
- Test thoroughly on target hardware
- Submit a pull request with clear description
- Use descriptive variable and function names
- Follow existing code formatting and style
- Add comments for complex logic
- Include examples for new features
- Ensure compatibility with ESP32 and ESP8266
- Test with different display sizes
- One feature/fix per pull request
- Include updated documentation
- Add examples if introducing new features
- Ensure all examples compile successfully
- Update version history in README
**Hardware:**
- Board: ESP32/ESP8266
- Display: [Model and size]
- Library version: [Version]
**Issue Description:**
[Clear description of the problem]
**Steps to Reproduce:**
1. [Step 1]
2. [Step 2]
3. [Step 3]
**Expected Behavior:**
[What should happen]
**Actual Behavior:**
[What actually happens]
**Code:**
```cpp
[Minimal code that reproduces the issue]
Additional Information: [Any other relevant details]
## License
This library is licensed under the **MIT License**. This means you can:
✅ **Use** the library in personal and commercial projects
✅ **Modify** the source code to fit your needs
✅ **Distribute** modified versions
✅ **Include** in proprietary software
**Requirements:**
- Include the original license notice
- Include copyright notice
See the [LICENSE](https://github.com/The-Young-Maker/OpenMenuOS/blob/main/LICENSE) file for complete terms.
---
## Credits
**Author**: The Young Maker
**GitHub**: [https://github.com/The-Young-Maker/OpenMenuOS](https://github.com/The-Young-Maker/OpenMenuOS)
**Version**: 3.1.0
**Last Updated**: June 15, 2025
### **Acknowledgments**
- TFT_eSPI library by Bodmer for display handling
- Arduino community for inspiration and feedback
- Contributors who helped improve the library
### **Support**
- 📖 **Documentation**: Check this README and examples
- 💬 **Issues**: GitHub Issues for bug reports and questions
- 🌟 **Star** the repository if you find it useful!
- 🔔 **Watch** for updates and new releases
---
**Made with ❤️ for the Arduino community**