From a27535e901fc7164b31b72eeb1463eec7608c7e4 Mon Sep 17 00:00:00 2001 From: Sam Hos Date: Tue, 6 May 2025 16:27:02 +0200 Subject: [PATCH] styling --- .../interface_application_programming.md | 1344 ++++++++--------- 1 file changed, 672 insertions(+), 672 deletions(-) diff --git a/docs/Assignments/week_14_interface_application_programming/interface_application_programming.md b/docs/Assignments/week_14_interface_application_programming/interface_application_programming.md index 72d852d..4de9a3b 100644 --- a/docs/Assignments/week_14_interface_application_programming/interface_application_programming.md +++ b/docs/Assignments/week_14_interface_application_programming/interface_application_programming.md @@ -426,723 +426,723 @@ This is the full code of the screen. The schematic for the pcb can be found in [ ## Turning it into a library So I asked Copilot (Claude 3.7 Sonnet) to turn this above into a library. Using this prompt "turn the screen thing into a library so i can easily create menus and stuff" and using the context from my files it came up with this broken and oversized mess. ??? Failure - ```cpp - #ifndef OLED_MENU_H - #define OLED_MENU_H + ```cpp + #ifndef OLED_MENU_H + #define OLED_MENU_H - #include - #include + #include + #include - // Maximum number of menu items and item name length - #define MAX_MENU_ITEMS 20 - #define MAX_ITEM_LENGTH 20 + // Maximum number of menu items and item name length + #define MAX_MENU_ITEMS 20 + #define MAX_ITEM_LENGTH 20 - // Button states - #define BUTTON_RELEASED 0 - #define BUTTON_PRESSED 1 - - class OLEDMenu { - public: - // Constructor - OLEDMenu(U8G2_SSD1306_128X64_NONAME_F_HW_I2C* display); - - // Initialize the menu - void begin(); - - // Set button states - void setButtons(bool up, bool select, bool down); - - // Add a menu item - bool addMenuItem(const char* item_name, const uint8_t* icon = nullptr); - - // Set custom bitmaps for menu items - void setItemBitmap(int item_index, const uint8_t* bitmap); - - // Set custom screen for a menu item - void setItemScreen(int item_index, const uint8_t* screen); - - // Update and draw the menu - void update(); - - // Get currently selected item - int getSelectedItem(); - - // Set selected item - void setSelectedItem(int item); - - // Change to specific screen - void setScreen(int screen_number); - - // Get current screen - int getCurrentScreen(); - - // Set scrollbar visibility - void showScrollbar(bool show); - - // Add custom screen drawing callback - typedef void (*DrawCallback)(U8G2_SSD1306_128X64_NONAME_F_HW_I2C* display, int item_index); - void setCustomScreenCallback(int item_index, DrawCallback callback); - - private: - U8G2_SSD1306_128X64_NONAME_F_HW_I2C* _display; - - // Menu items - char _menu_items[MAX_MENU_ITEMS][MAX_ITEM_LENGTH]; - const uint8_t* _item_icons[MAX_MENU_ITEMS]; - const uint8_t* _item_screens[MAX_MENU_ITEMS]; - DrawCallback _custom_callbacks[MAX_MENU_ITEMS]; - - int _item_count; - int _selected_item; - int _prev_item; - int _next_item; - - // Screen control - int _current_screen; - bool _show_scrollbar; - - // Button states - bool _button_up; - bool _button_select; - bool _button_down; - bool _button_up_clicked; - bool _button_select_clicked; - bool _button_down_clicked; - - // Internal methods - void drawMenuScreen(); - void handleButtonEvents(); - void updateNavigation(); - }; - - #endif // OLED_MENU_H - ``` - -??? Failure - ```cpp - #include "OLEDMenu.h" + // Button states + #define BUTTON_RELEASED 0 + #define BUTTON_PRESSED 1 + class OLEDMenu { + public: // Constructor - OLEDMenu::OLEDMenu(U8G2_SSD1306_128X64_NONAME_F_HW_I2C* display) { - _display = display; - _item_count = 0; - _selected_item = 0; - _current_screen = 0; - _show_scrollbar = true; + OLEDMenu(U8G2_SSD1306_128X64_NONAME_F_HW_I2C* display); - // Initialize button states - _button_up = false; - _button_select = false; - _button_down = false; - _button_up_clicked = false; - _button_select_clicked = false; - _button_down_clicked = false; - - // Initialize arrays - for (int i = 0; i < MAX_MENU_ITEMS; i++) { - _menu_items[i][0] = '\0'; - _item_icons[i] = nullptr; - _item_screens[i] = nullptr; - _custom_callbacks[i] = nullptr; - } - } - - void OLEDMenu::begin() { - updateNavigation(); - } - - void OLEDMenu::setButtons(bool up, bool select, bool down) { - _button_up = up; - _button_select = select; - _button_down = down; - } - - bool OLEDMenu::addMenuItem(const char* item_name, const uint8_t* icon) { - // Check if we have space for another item - if (_item_count >= MAX_MENU_ITEMS) { - return false; - } - - // Copy the item name - strncpy(_menu_items[_item_count], item_name, MAX_ITEM_LENGTH); - _menu_items[_item_count][MAX_ITEM_LENGTH - 1] = '\0'; // Ensure null termination - - // Store the icon - _item_icons[_item_count] = icon; - - // Increment the item count - _item_count++; - - // Update navigation - updateNavigation(); - - return true; - } - - void OLEDMenu::setItemBitmap(int item_index, const uint8_t* bitmap) { - if (item_index >= 0 && item_index < _item_count) { - _item_icons[item_index] = bitmap; - } - } - - void OLEDMenu::setItemScreen(int item_index, const uint8_t* screen) { - if (item_index >= 0 && item_index < _item_count) { - _item_screens[item_index] = screen; - } - } - - void OLEDMenu::setCustomScreenCallback(int item_index, DrawCallback callback) { - if (item_index >= 0 && item_index < _item_count) { - _custom_callbacks[item_index] = callback; - } - } - - void OLEDMenu::update() { - // Handle button events - handleButtonEvents(); - - // Draw the appropriate screen - _display->firstPage(); - do { - if (_current_screen == 0) { - // Draw the menu screen - drawMenuScreen(); - } else if (_current_screen == 1 && _item_screens[_selected_item] != nullptr) { - // Draw item screen - _display->drawBitmap(0, 0, 128/8, 64, _item_screens[_selected_item]); - } else if (_current_screen == 2 && _custom_callbacks[_selected_item] != nullptr) { - // Draw custom screen using callback - _custom_callbacks[_selected_item](_display, _selected_item); - } - } while (_display->nextPage()); - } - - int OLEDMenu::getSelectedItem() { - return _selected_item; - } - - void OLEDMenu::setSelectedItem(int item) { - if (item >= 0 && item < _item_count) { - _selected_item = item; - updateNavigation(); - } - } - - void OLEDMenu::setScreen(int screen_number) { - _current_screen = screen_number; - } - - int OLEDMenu::getCurrentScreen() { - return _current_screen; - } - - void OLEDMenu::showScrollbar(bool show) { - _show_scrollbar = show; - } - - void OLEDMenu::drawMenuScreen() { - // Draw menu background elements - // You might want to add the bitmap_item_sel_outline here - - // Draw previous item - _display->setFont(u8g_font_7x14); - _display->drawStr(25, 15, _menu_items[_prev_item]); - if (_item_icons[_prev_item] != nullptr) { - _display->drawBitmap(4, 2, 16/8, 16, _item_icons[_prev_item]); - } - - // Draw selected item - _display->setFont(u8g_font_7x14B); - _display->drawStr(25, 15+20+2, _menu_items[_selected_item]); - if (_item_icons[_selected_item] != nullptr) { - _display->drawBitmap(4, 24, 16/8, 16, _item_icons[_selected_item]); - } - - // Draw next item - _display->setFont(u8g_font_7x14); - _display->drawStr(25, 15+20+20+2+2, _menu_items[_next_item]); - if (_item_icons[_next_item] != nullptr) { - _display->drawBitmap(4, 46, 16/8, 16, _item_icons[_next_item]); - } - - // Draw scrollbar if enabled - if (_show_scrollbar && _item_count > 1) { - // Draw scrollbar background - // You might want to add bitmap_scrollbar_background here - - // Draw scrollbar handle - _display->drawBox(125, 64/_item_count * _selected_item, 3, 64/_item_count); - } - } - - void OLEDMenu::handleButtonEvents() { - if (_current_screen == 0) { - // Handle up button for menu navigation - if (_button_up && !_button_up_clicked) { - _selected_item = _selected_item - 1; - _button_up_clicked = true; - if (_selected_item < 0) { - _selected_item = _item_count - 1; - } - updateNavigation(); - } else if (!_button_up && _button_up_clicked) { - _button_up_clicked = false; - } - - // Handle down button for menu navigation - if (_button_down && !_button_down_clicked) { - _selected_item = _selected_item + 1; - _button_down_clicked = true; - if (_selected_item >= _item_count) { - _selected_item = 0; - } - updateNavigation(); - } else if (!_button_down && _button_down_clicked) { - _button_down_clicked = false; - } - } - - // Handle select button for screen switching - if (_button_select && !_button_select_clicked) { - _button_select_clicked = true; - - // Cycle through screens - _current_screen = (_current_screen + 1) % 3; - - // Skip screens that don't have content - if (_current_screen == 1 && _item_screens[_selected_item] == nullptr) { - _current_screen = 2; - } - if (_current_screen == 2 && _custom_callbacks[_selected_item] == nullptr) { - _current_screen = 0; - } - } else if (!_button_select && _button_select_clicked) { - _button_select_clicked = false; - } - } - - void OLEDMenu::updateNavigation() { - // Update previous and next item indices - _prev_item = _selected_item - 1; - if (_prev_item < 0) _prev_item = _item_count - 1; - - _next_item = _selected_item + 1; - if (_next_item >= _item_count) _next_item = 0; - } - ``` -Then I asked it to rewrite it and make it simpler and to add in a way to call functions using these menu tabs and to use the original graphics. - -Then it came up with this -??? AIgenerated - ```cpp - #include "MenuSystem.h" - #include "MenuGraphics.h" - - MenuSystem::MenuSystem(U8G2_SSD1306_128X64_NONAME_F_HW_I2C* display) { - _display = display; - _itemCount = 0; - _selectedItem = 0; - _screen = 0; - - _btnUp = false; - _btnSelect = false; - _btnDown = false; - _prevBtnUp = false; - _prevBtnSelect = false; - _prevBtnDown = false; - - // Initialize arrays - for (int i = 0; i < MAX_ITEMS; i++) { - _itemNames[i][0] = '\0'; - _itemIcons[i] = nullptr; - _itemScreens[i] = nullptr; - _itemCallbacks[i] = nullptr; - } - } - - void MenuSystem::begin() { - // Ensure display is properly initialized - _display->begin(); - _display->clearBuffer(); - _display->sendBuffer(); - delay(100); // Short delay to ensure display is ready - } - - void MenuSystem::addMenuItem(const char* name, const uint8_t* icon) { - if (_itemCount < MAX_ITEMS) { - strncpy(_itemNames[_itemCount], name, MAX_NAME_LEN-1); - _itemNames[_itemCount][MAX_NAME_LEN-1] = '\0'; // Ensure null termination - _itemIcons[_itemCount] = icon; - _itemCount++; - } - } - - void MenuSystem::setItemScreen(int index, const uint8_t* screen) { - if (index >= 0 && index < _itemCount) { - _itemScreens[index] = screen; - } - } - - void MenuSystem::setItemCallback(int index, void (*callback)()) { - if (index >= 0 && index < _itemCount) { - _itemCallbacks[index] = callback; - } - } - - void MenuSystem::setButtons(bool up, bool select, bool down) { - // Save previous button states - _prevBtnUp = _btnUp; - _prevBtnSelect = _btnSelect; - _prevBtnDown = _btnDown; - - // Set new button states - _btnUp = up; - _btnSelect = select; - _btnDown = down; - } - - void MenuSystem::update() { - // Process button inputs before drawing - handleButtons(); - - // Simple drawing approach (not using page buffer mode) - _display->clearBuffer(); - - if (_screen == 0) { - // Draw the main menu screen - drawMenu(); - } else if (_screen == 1 && _itemScreens[_selectedItem] != nullptr) { - // Draw the full-screen bitmap for the selected item - _display->drawXBM(0, 0, 128, 64, _itemScreens[_selectedItem]); - } - - _display->sendBuffer(); - } - - void MenuSystem::drawMenu() { - if (_itemCount == 0) return; // Nothing to draw - - // Calculate which items to show (previous, current, next) - int prevItem = (_selectedItem > 0) ? _selectedItem - 1 : _itemCount - 1; - int nextItem = (_selectedItem < _itemCount - 1) ? _selectedItem + 1 : 0; - - // Draw the background with the item selector outline (if enabled) - // _display->drawXBM(0, 0, 128, 64, bitmap_item_sel_outline); - - // Instead, draw a simple selection highlight - _display->drawFrame(0, 20, 128, 24); // Highlight the middle selection - - // Draw previous item (top position) - _display->setFont(u8g2_font_6x10_tf); // Use a different, simpler font - _display->drawStr(25, 15, _itemNames[prevItem]); - if (_itemIcons[prevItem] != nullptr) { - _display->drawXBM(4, 2, 16, 16, _itemIcons[prevItem]); - } - - // Draw selected item (middle position) - _display->setFont(u8g2_font_6x10_tf); // Use same font for consistency - _display->drawStr(25, 35, _itemNames[_selectedItem]); - if (_itemIcons[_selectedItem] != nullptr) { - _display->drawXBM(4, 24, 16, 16, _itemIcons[_selectedItem]); - } - - // Draw next item (bottom position) - _display->setFont(u8g2_font_6x10_tf); // Use same font for consistency - _display->drawStr(25, 55, _itemNames[nextItem]); - if (_itemIcons[nextItem] != nullptr) { - _display->drawXBM(4, 46, 16, 16, _itemIcons[nextItem]); - } - - // Draw a simple scrollbar instead of using bitmap - if (_itemCount > 1) { - _display->drawVLine(125, 0, 64); // Scrollbar background - int barHeight = 64 / _itemCount; - int barY = (_selectedItem * 64) / _itemCount; - _display->drawBox(123, barY, 4, barHeight); // Scrollbar handle - } - } - - void MenuSystem::handleButtons() { - // Only respond to button press events (not holds) - // Button rising edge detection for up - if (_btnUp && !_prevBtnUp) { - _selectedItem = (_selectedItem > 0) ? _selectedItem - 1 : _itemCount - 1; - Serial.print("Selected item: "); - Serial.println(_selectedItem); - } - - // Button rising edge detection for down - if (_btnDown && !_prevBtnDown) { - _selectedItem = (_selectedItem < _itemCount - 1) ? _selectedItem + 1 : 0; - Serial.print("Selected item: "); - Serial.println(_selectedItem); - } - - // Button rising edge detection for select - if (_btnSelect && !_prevBtnSelect) { - // Execute callback if it exists - if (_itemCallbacks[_selectedItem] != nullptr) { - _itemCallbacks[_selectedItem](); - } - - // Toggle between menu and item screen if screen exists - if (_itemScreens[_selectedItem] != nullptr) { - _screen = (_screen == 0) ? 1 : 0; - Serial.print("Screen changed to: "); - Serial.println(_screen); - } - } - } - ``` - - -??? AIgenerated - ```cpp - #ifndef MENU_SYSTEM_H - #define MENU_SYSTEM_H - - #include - #include - - class MenuSystem { - public: - // Constructor - MenuSystem(U8G2_SSD1306_128X64_NONAME_F_HW_I2C* display); - - // Initialize menu + // Initialize the menu void begin(); - // Add menu item with icon - void addMenuItem(const char* name, const uint8_t* icon); - - // Set callback function for item - void setItemCallback(int index, void (*callback)()); - // Set button states void setButtons(bool up, bool select, bool down); - // Update and draw menu + // Add a menu item + bool addMenuItem(const char* item_name, const uint8_t* icon = nullptr); + + // Set custom bitmaps for menu items + void setItemBitmap(int item_index, const uint8_t* bitmap); + + // Set custom screen for a menu item + void setItemScreen(int item_index, const uint8_t* screen); + + // Update and draw the menu void update(); - private: + // Get currently selected item + int getSelectedItem(); + + // Set selected item + void setSelectedItem(int item); + + // Change to specific screen + void setScreen(int screen_number); + + // Get current screen + int getCurrentScreen(); + + // Set scrollbar visibility + void showScrollbar(bool show); + + // Add custom screen drawing callback + typedef void (*DrawCallback)(U8G2_SSD1306_128X64_NONAME_F_HW_I2C* display, int item_index); + void setCustomScreenCallback(int item_index, DrawCallback callback); + + private: U8G2_SSD1306_128X64_NONAME_F_HW_I2C* _display; // Menu items - static const int MAX_ITEMS = 10; - static const int MAX_NAME_LEN = 20; - char _itemNames[MAX_ITEMS][MAX_NAME_LEN]; - const uint8_t* _itemIcons[MAX_ITEMS]; - const uint8_t* _itemScreens[MAX_ITEMS]; - void (*_itemCallbacks[MAX_ITEMS])(); + char _menu_items[MAX_MENU_ITEMS][MAX_ITEM_LENGTH]; + const uint8_t* _item_icons[MAX_MENU_ITEMS]; + const uint8_t* _item_screens[MAX_MENU_ITEMS]; + DrawCallback _custom_callbacks[MAX_MENU_ITEMS]; - // Menu state - int _itemCount; - int _selectedItem; - int _screen; // 0 = menu, 1 = item screen + int _item_count; + int _selected_item; + int _prev_item; + int _next_item; - // Button handling - bool _btnUp; - bool _btnSelect; - bool _btnDown; - bool _prevBtnUp; - bool _prevBtnSelect; - bool _prevBtnDown; + // Screen control + int _current_screen; + bool _show_scrollbar; - // Draw the menu - void drawMenu(); + // Button states + bool _button_up; + bool _button_select; + bool _button_down; + bool _button_up_clicked; + bool _button_select_clicked; + bool _button_down_clicked; - // Update button states - void handleButtons(); - }; + // Internal methods + void drawMenuScreen(); + void handleButtonEvents(); + void updateNavigation(); + }; - #endif - ``` + #endif // OLED_MENU_H + ``` + +??? Failure + ```cpp + #include "OLEDMenu.h" + + // Constructor + OLEDMenu::OLEDMenu(U8G2_SSD1306_128X64_NONAME_F_HW_I2C* display) { + _display = display; + _item_count = 0; + _selected_item = 0; + _current_screen = 0; + _show_scrollbar = true; + + // Initialize button states + _button_up = false; + _button_select = false; + _button_down = false; + _button_up_clicked = false; + _button_select_clicked = false; + _button_down_clicked = false; + + // Initialize arrays + for (int i = 0; i < MAX_MENU_ITEMS; i++) { + _menu_items[i][0] = '\0'; + _item_icons[i] = nullptr; + _item_screens[i] = nullptr; + _custom_callbacks[i] = nullptr; + } + } + + void OLEDMenu::begin() { + updateNavigation(); + } + + void OLEDMenu::setButtons(bool up, bool select, bool down) { + _button_up = up; + _button_select = select; + _button_down = down; + } + + bool OLEDMenu::addMenuItem(const char* item_name, const uint8_t* icon) { + // Check if we have space for another item + if (_item_count >= MAX_MENU_ITEMS) { + return false; + } + + // Copy the item name + strncpy(_menu_items[_item_count], item_name, MAX_ITEM_LENGTH); + _menu_items[_item_count][MAX_ITEM_LENGTH - 1] = '\0'; // Ensure null termination + + // Store the icon + _item_icons[_item_count] = icon; + + // Increment the item count + _item_count++; + + // Update navigation + updateNavigation(); + + return true; + } + + void OLEDMenu::setItemBitmap(int item_index, const uint8_t* bitmap) { + if (item_index >= 0 && item_index < _item_count) { + _item_icons[item_index] = bitmap; + } + } + + void OLEDMenu::setItemScreen(int item_index, const uint8_t* screen) { + if (item_index >= 0 && item_index < _item_count) { + _item_screens[item_index] = screen; + } + } + + void OLEDMenu::setCustomScreenCallback(int item_index, DrawCallback callback) { + if (item_index >= 0 && item_index < _item_count) { + _custom_callbacks[item_index] = callback; + } + } + + void OLEDMenu::update() { + // Handle button events + handleButtonEvents(); + + // Draw the appropriate screen + _display->firstPage(); + do { + if (_current_screen == 0) { + // Draw the menu screen + drawMenuScreen(); + } else if (_current_screen == 1 && _item_screens[_selected_item] != nullptr) { + // Draw item screen + _display->drawBitmap(0, 0, 128/8, 64, _item_screens[_selected_item]); + } else if (_current_screen == 2 && _custom_callbacks[_selected_item] != nullptr) { + // Draw custom screen using callback + _custom_callbacks[_selected_item](_display, _selected_item); + } + } while (_display->nextPage()); + } + + int OLEDMenu::getSelectedItem() { + return _selected_item; + } + + void OLEDMenu::setSelectedItem(int item) { + if (item >= 0 && item < _item_count) { + _selected_item = item; + updateNavigation(); + } + } + + void OLEDMenu::setScreen(int screen_number) { + _current_screen = screen_number; + } + + int OLEDMenu::getCurrentScreen() { + return _current_screen; + } + + void OLEDMenu::showScrollbar(bool show) { + _show_scrollbar = show; + } + + void OLEDMenu::drawMenuScreen() { + // Draw menu background elements + // You might want to add the bitmap_item_sel_outline here + + // Draw previous item + _display->setFont(u8g_font_7x14); + _display->drawStr(25, 15, _menu_items[_prev_item]); + if (_item_icons[_prev_item] != nullptr) { + _display->drawBitmap(4, 2, 16/8, 16, _item_icons[_prev_item]); + } + + // Draw selected item + _display->setFont(u8g_font_7x14B); + _display->drawStr(25, 15+20+2, _menu_items[_selected_item]); + if (_item_icons[_selected_item] != nullptr) { + _display->drawBitmap(4, 24, 16/8, 16, _item_icons[_selected_item]); + } + + // Draw next item + _display->setFont(u8g_font_7x14); + _display->drawStr(25, 15+20+20+2+2, _menu_items[_next_item]); + if (_item_icons[_next_item] != nullptr) { + _display->drawBitmap(4, 46, 16/8, 16, _item_icons[_next_item]); + } + + // Draw scrollbar if enabled + if (_show_scrollbar && _item_count > 1) { + // Draw scrollbar background + // You might want to add bitmap_scrollbar_background here + + // Draw scrollbar handle + _display->drawBox(125, 64/_item_count * _selected_item, 3, 64/_item_count); + } + } + + void OLEDMenu::handleButtonEvents() { + if (_current_screen == 0) { + // Handle up button for menu navigation + if (_button_up && !_button_up_clicked) { + _selected_item = _selected_item - 1; + _button_up_clicked = true; + if (_selected_item < 0) { + _selected_item = _item_count - 1; + } + updateNavigation(); + } else if (!_button_up && _button_up_clicked) { + _button_up_clicked = false; + } + + // Handle down button for menu navigation + if (_button_down && !_button_down_clicked) { + _selected_item = _selected_item + 1; + _button_down_clicked = true; + if (_selected_item >= _item_count) { + _selected_item = 0; + } + updateNavigation(); + } else if (!_button_down && _button_down_clicked) { + _button_down_clicked = false; + } + } + + // Handle select button for screen switching + if (_button_select && !_button_select_clicked) { + _button_select_clicked = true; + + // Cycle through screens + _current_screen = (_current_screen + 1) % 3; + + // Skip screens that don't have content + if (_current_screen == 1 && _item_screens[_selected_item] == nullptr) { + _current_screen = 2; + } + if (_current_screen == 2 && _custom_callbacks[_selected_item] == nullptr) { + _current_screen = 0; + } + } else if (!_button_select && _button_select_clicked) { + _button_select_clicked = false; + } + } + + void OLEDMenu::updateNavigation() { + // Update previous and next item indices + _prev_item = _selected_item - 1; + if (_prev_item < 0) _prev_item = _item_count - 1; + + _next_item = _selected_item + 1; + if (_next_item >= _item_count) _next_item = 0; + } + ``` +Then I asked it to rewrite it and make it simpler and to add in a way to call functions using these menu tabs and to use the original graphics. + +Then it came up with this +??? AIgenerated + ```cpp + #include "MenuSystem.h" + #include "MenuGraphics.h" + + MenuSystem::MenuSystem(U8G2_SSD1306_128X64_NONAME_F_HW_I2C* display) { + _display = display; + _itemCount = 0; + _selectedItem = 0; + _screen = 0; + + _btnUp = false; + _btnSelect = false; + _btnDown = false; + _prevBtnUp = false; + _prevBtnSelect = false; + _prevBtnDown = false; + + // Initialize arrays + for (int i = 0; i < MAX_ITEMS; i++) { + _itemNames[i][0] = '\0'; + _itemIcons[i] = nullptr; + _itemScreens[i] = nullptr; + _itemCallbacks[i] = nullptr; + } + } + + void MenuSystem::begin() { + // Ensure display is properly initialized + _display->begin(); + _display->clearBuffer(); + _display->sendBuffer(); + delay(100); // Short delay to ensure display is ready + } + + void MenuSystem::addMenuItem(const char* name, const uint8_t* icon) { + if (_itemCount < MAX_ITEMS) { + strncpy(_itemNames[_itemCount], name, MAX_NAME_LEN-1); + _itemNames[_itemCount][MAX_NAME_LEN-1] = '\0'; // Ensure null termination + _itemIcons[_itemCount] = icon; + _itemCount++; + } + } + + void MenuSystem::setItemScreen(int index, const uint8_t* screen) { + if (index >= 0 && index < _itemCount) { + _itemScreens[index] = screen; + } + } + + void MenuSystem::setItemCallback(int index, void (*callback)()) { + if (index >= 0 && index < _itemCount) { + _itemCallbacks[index] = callback; + } + } + + void MenuSystem::setButtons(bool up, bool select, bool down) { + // Save previous button states + _prevBtnUp = _btnUp; + _prevBtnSelect = _btnSelect; + _prevBtnDown = _btnDown; + + // Set new button states + _btnUp = up; + _btnSelect = select; + _btnDown = down; + } + + void MenuSystem::update() { + // Process button inputs before drawing + handleButtons(); + + // Simple drawing approach (not using page buffer mode) + _display->clearBuffer(); + + if (_screen == 0) { + // Draw the main menu screen + drawMenu(); + } else if (_screen == 1 && _itemScreens[_selectedItem] != nullptr) { + // Draw the full-screen bitmap for the selected item + _display->drawXBM(0, 0, 128, 64, _itemScreens[_selectedItem]); + } + + _display->sendBuffer(); + } + + void MenuSystem::drawMenu() { + if (_itemCount == 0) return; // Nothing to draw + + // Calculate which items to show (previous, current, next) + int prevItem = (_selectedItem > 0) ? _selectedItem - 1 : _itemCount - 1; + int nextItem = (_selectedItem < _itemCount - 1) ? _selectedItem + 1 : 0; + + // Draw the background with the item selector outline (if enabled) + // _display->drawXBM(0, 0, 128, 64, bitmap_item_sel_outline); + + // Instead, draw a simple selection highlight + _display->drawFrame(0, 20, 128, 24); // Highlight the middle selection + + // Draw previous item (top position) + _display->setFont(u8g2_font_6x10_tf); // Use a different, simpler font + _display->drawStr(25, 15, _itemNames[prevItem]); + if (_itemIcons[prevItem] != nullptr) { + _display->drawXBM(4, 2, 16, 16, _itemIcons[prevItem]); + } + + // Draw selected item (middle position) + _display->setFont(u8g2_font_6x10_tf); // Use same font for consistency + _display->drawStr(25, 35, _itemNames[_selectedItem]); + if (_itemIcons[_selectedItem] != nullptr) { + _display->drawXBM(4, 24, 16, 16, _itemIcons[_selectedItem]); + } + + // Draw next item (bottom position) + _display->setFont(u8g2_font_6x10_tf); // Use same font for consistency + _display->drawStr(25, 55, _itemNames[nextItem]); + if (_itemIcons[nextItem] != nullptr) { + _display->drawXBM(4, 46, 16, 16, _itemIcons[nextItem]); + } + + // Draw a simple scrollbar instead of using bitmap + if (_itemCount > 1) { + _display->drawVLine(125, 0, 64); // Scrollbar background + int barHeight = 64 / _itemCount; + int barY = (_selectedItem * 64) / _itemCount; + _display->drawBox(123, barY, 4, barHeight); // Scrollbar handle + } + } + + void MenuSystem::handleButtons() { + // Only respond to button press events (not holds) + // Button rising edge detection for up + if (_btnUp && !_prevBtnUp) { + _selectedItem = (_selectedItem > 0) ? _selectedItem - 1 : _itemCount - 1; + Serial.print("Selected item: "); + Serial.println(_selectedItem); + } + + // Button rising edge detection for down + if (_btnDown && !_prevBtnDown) { + _selectedItem = (_selectedItem < _itemCount - 1) ? _selectedItem + 1 : 0; + Serial.print("Selected item: "); + Serial.println(_selectedItem); + } + + // Button rising edge detection for select + if (_btnSelect && !_prevBtnSelect) { + // Execute callback if it exists + if (_itemCallbacks[_selectedItem] != nullptr) { + _itemCallbacks[_selectedItem](); + } + + // Toggle between menu and item screen if screen exists + if (_itemScreens[_selectedItem] != nullptr) { + _screen = (_screen == 0) ? 1 : 0; + Serial.print("Screen changed to: "); + Serial.println(_screen); + } + } + } + ``` + + +??? AIgenerated + ```cpp + #ifndef MENU_SYSTEM_H + #define MENU_SYSTEM_H + + #include + #include + + class MenuSystem { + public: + // Constructor + MenuSystem(U8G2_SSD1306_128X64_NONAME_F_HW_I2C* display); + + // Initialize menu + void begin(); + + // Add menu item with icon + void addMenuItem(const char* name, const uint8_t* icon); + + // Set callback function for item + void setItemCallback(int index, void (*callback)()); + + // Set button states + void setButtons(bool up, bool select, bool down); + + // Update and draw menu + void update(); + + private: + U8G2_SSD1306_128X64_NONAME_F_HW_I2C* _display; + + // Menu items + static const int MAX_ITEMS = 10; + static const int MAX_NAME_LEN = 20; + char _itemNames[MAX_ITEMS][MAX_NAME_LEN]; + const uint8_t* _itemIcons[MAX_ITEMS]; + const uint8_t* _itemScreens[MAX_ITEMS]; + void (*_itemCallbacks[MAX_ITEMS])(); + + // Menu state + int _itemCount; + int _selectedItem; + int _screen; // 0 = menu, 1 = item screen + + // Button handling + bool _btnUp; + bool _btnSelect; + bool _btnDown; + bool _prevBtnUp; + bool _prevBtnSelect; + bool _prevBtnDown; + + // Draw the menu + void drawMenu(); + + // Update button states + void handleButtons(); + }; + + #endif + ``` Which worked really well with my code ??? example - ```cpp - #include - #include - #include - #include "MenuSystem.h" - #include "Graphics.h" - U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); - MenuSystem menu(&u8g2); + ```cpp + #include + #include + #include + #include "MenuSystem.h" + #include "Graphics.h" + U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); + MenuSystem menu(&u8g2); - //=====================================================================================// - // Struct declarations - typedef struct struct_message - { - int PWMCH1; - int PWMCH2; - int PWMCH3; - int PWMCH4; - } struct_message; - struct_message JoystickData; // declare the struct as JoystickData + //=====================================================================================// + // Struct declarations + typedef struct struct_message + { + int PWMCH1; + int PWMCH2; + int PWMCH3; + int PWMCH4; + } struct_message; + struct_message JoystickData; // declare the struct as JoystickData - struct hardJoystickValues - { - int LXU; // Left joystick X axis up - int LXD; // Left joystick X axis down - int RXU; // Right joystick X axis up - int RXD; // Right joystick X axis down - }; - hardJoystickValues JoystickDataHard; // declare the struct as JoystickDataHard + struct hardJoystickValues + { + int LXU; // Left joystick X axis up + int LXD; // Left joystick X axis down + int RXU; // Right joystick X axis up + int RXD; // Right joystick X axis down + }; + hardJoystickValues JoystickDataHard; // declare the struct as JoystickDataHard - //=====================================================================================// - //declarations - void MUXSetup(); - hardJoystickValues GUIParser(); - int analogReadMultiPlexer(int addressA, int addressB, int addressC, int addressD, int pin); - int mapPot(int normalizedValue); + //=====================================================================================// + //declarations + void MUXSetup(); + hardJoystickValues GUIParser(); + int analogReadMultiPlexer(int addressA, int addressB, int addressC, int addressD, int pin); + int mapPot(int normalizedValue); - // Controller declarations - const int MAXPWMVALUE = 1000; - const int MINPWMVALUE = 2000; + // Controller declarations + const int MAXPWMVALUE = 1000; + const int MINPWMVALUE = 2000; - bool buttonRight; - bool buttonLeft; + bool buttonRight; + bool buttonLeft; - // Actions for menu items - void action3DCube() { - Serial.println("3D Cube selected!"); - // Your 3D cube function code here - } + // Actions for menu items + void action3DCube() { + Serial.println("3D Cube selected!"); + // Your 3D cube function code here + } - void actionBattery() { - Serial.println("Battery selected!"); - // Your battery function code here - } + void actionBattery() { + Serial.println("Battery selected!"); + // Your battery function code here + } - void actionDashboard() { - Serial.println("Dashboard selected!"); - // Your dashboard function code here - } + void actionDashboard() { + Serial.println("Dashboard selected!"); + // Your dashboard function code here + } - void actionFireworks() { - Serial.println("Fireworks selected!"); - // Your fireworks function code here - } + void actionFireworks() { + Serial.println("Fireworks selected!"); + // Your fireworks function code here + } - void setup() { - Serial.begin(9600); - delay(100); // Small delay to stabilize - - Wire.begin(); // Initialize I2C bus - Wire.setClock(400000); // Set to 400kHz (standard speed for most displays) - - u8g2.begin(); - u8g2.clearBuffer(); - u8g2.sendBuffer(); - - MUXSetup(); // Setup the multiplexer - - // Initialize the menu - menu.begin(); - - // Add menu items - menu.addMenuItem("3D Cube", bitmap_icons[0]); - menu.addMenuItem("Battery", bitmap_icons[1]); - menu.addMenuItem("Dashboard", bitmap_icons[2]); - menu.addMenuItem("Fireworks", bitmap_icons[3]); - menu.addMenuItem("WEEEEE", bitmap_icons[4]); + void setup() { + Serial.begin(9600); + delay(100); // Small delay to stabilize + + Wire.begin(); // Initialize I2C bus + Wire.setClock(400000); // Set to 400kHz (standard speed for most displays) + + u8g2.begin(); + u8g2.clearBuffer(); + u8g2.sendBuffer(); + + MUXSetup(); // Setup the multiplexer + + // Initialize the menu + menu.begin(); + + // Add menu items + menu.addMenuItem("3D Cube", bitmap_icons[0]); + menu.addMenuItem("Battery", bitmap_icons[1]); + menu.addMenuItem("Dashboard", bitmap_icons[2]); + menu.addMenuItem("Fireworks", bitmap_icons[3]); + menu.addMenuItem("WEEEEE", bitmap_icons[4]); - - // Add action callbacks for menu items - menu.setItemCallback(0, action3DCube); - menu.setItemCallback(1, actionBattery); - menu.setItemCallback(2, actionDashboard); - menu.setItemCallback(3, actionFireworks); - - // Clear the display once more - u8g2.clearBuffer(); - u8g2.sendBuffer(); - - Serial.println("Menu system initialized"); - } + + // Add action callbacks for menu items + menu.setItemCallback(0, action3DCube); + menu.setItemCallback(1, actionBattery); + menu.setItemCallback(2, actionDashboard); + menu.setItemCallback(3, actionFireworks); + + // Clear the display once more + u8g2.clearBuffer(); + u8g2.sendBuffer(); + + Serial.println("Menu system initialized"); + } - void loop() { - // Read joystick and button inputs - JoystickData.PWMCH1 = mapPot(analogReadMultiPlexer(0, 0, 0, 0, A0)); //Right joystick Y - JoystickData.PWMCH2 = mapPot(analogReadMultiPlexer(1, 0, 0, 0, A0)); // Right joystick X - JoystickData.PWMCH3 = mapPot(analogReadMultiPlexer(0, 0, 0, 1, A0)); // left joystick Y - JoystickData.PWMCH4 = mapPot(analogReadMultiPlexer(1, 0, 0, 1, A0)); // left joystick X - buttonRight = analogReadMultiPlexer(0, 0, 1, 0, A0) > 2000; // right button - buttonLeft = analogReadMultiPlexer(1, 0, 1, 0, A0) > 2000; // left button + void loop() { + // Read joystick and button inputs + JoystickData.PWMCH1 = mapPot(analogReadMultiPlexer(0, 0, 0, 0, A0)); //Right joystick Y + JoystickData.PWMCH2 = mapPot(analogReadMultiPlexer(1, 0, 0, 0, A0)); // Right joystick X + JoystickData.PWMCH3 = mapPot(analogReadMultiPlexer(0, 0, 0, 1, A0)); // left joystick Y + JoystickData.PWMCH4 = mapPot(analogReadMultiPlexer(1, 0, 0, 1, A0)); // left joystick X + buttonRight = analogReadMultiPlexer(0, 0, 1, 0, A0) > 2000; // right button + buttonLeft = analogReadMultiPlexer(1, 0, 1, 0, A0) > 2000; // left button - // Parse joystick data - JoystickDataHard = GUIParser(); - - // Update menu button states - menu.setButtons(JoystickDataHard.LXD, buttonRight, JoystickDataHard.LXU); - - // Update and draw the menu - menu.update(); - - // Small delay to debounce buttons - delay(50); - } + // Parse joystick data + JoystickDataHard = GUIParser(); + + // Update menu button states + menu.setButtons(JoystickDataHard.LXD, buttonRight, JoystickDataHard.LXU); + + // Update and draw the menu + menu.update(); + + // Small delay to debounce buttons + delay(50); + } - void MUXSetup() - { - pinMode(D3, OUTPUT); // MUX enable - pinMode(D6, OUTPUT); // MUX address A - pinMode(D7, OUTPUT); // MUX address B - pinMode(D9, OUTPUT); // MUX address C - pinMode(D8, OUTPUT); // MUX address D - pinMode(A0, INPUT); // MUX input - } + void MUXSetup() + { + pinMode(D3, OUTPUT); // MUX enable + pinMode(D6, OUTPUT); // MUX address A + pinMode(D7, OUTPUT); // MUX address B + pinMode(D9, OUTPUT); // MUX address C + pinMode(D8, OUTPUT); // MUX address D + pinMode(A0, INPUT); // MUX input + } - // Function to parse joystick data to hard values - hardJoystickValues GUIParser(){ - // Define joystick offsets (calibrated resting values) - const int offsetPWMCH1 = 1090; // Resting value for PWMCH1 (right joystick Y-axis) - const int offsetPWMCH2 = 1072; // Resting value for PWMCH2 (right joystick X-axis) - const int offsetPWMCH3 = 1043; // Resting value for PWMCH3 (left joystick X-axis) - const int offsetPWMCH4 = 1476; // Resting value for PWMCH4 (left joystick Y-axis) + // Function to parse joystick data to hard values + hardJoystickValues GUIParser(){ + // Define joystick offsets (calibrated resting values) + const int offsetPWMCH1 = 1090; // Resting value for PWMCH1 (right joystick Y-axis) + const int offsetPWMCH2 = 1072; // Resting value for PWMCH2 (right joystick X-axis) + const int offsetPWMCH3 = 1043; // Resting value for PWMCH3 (left joystick X-axis) + const int offsetPWMCH4 = 1476; // Resting value for PWMCH4 (left joystick Y-axis) - // Define deadzone threshold - const int deadzone = 120; + // Define deadzone threshold + const int deadzone = 120; - // Adjust joystick values by subtracting offsets - int adjustedPWMCH1 = JoystickData.PWMCH1 - offsetPWMCH1; - int adjustedPWMCH2 = JoystickData.PWMCH2 - offsetPWMCH2; - int adjustedPWMCH3 = JoystickData.PWMCH3 - offsetPWMCH3; - int adjustedPWMCH4 = JoystickData.PWMCH4 - offsetPWMCH4; + // Adjust joystick values by subtracting offsets + int adjustedPWMCH1 = JoystickData.PWMCH1 - offsetPWMCH1; + int adjustedPWMCH2 = JoystickData.PWMCH2 - offsetPWMCH2; + int adjustedPWMCH3 = JoystickData.PWMCH3 - offsetPWMCH3; + int adjustedPWMCH4 = JoystickData.PWMCH4 - offsetPWMCH4; - // Apply deadzone - if (abs(adjustedPWMCH1) < deadzone) adjustedPWMCH1 = 0; //abs to avoid negatives - if (abs(adjustedPWMCH2) < deadzone) adjustedPWMCH2 = 0; - if (abs(adjustedPWMCH3) < deadzone) adjustedPWMCH3 = 0; - if (abs(adjustedPWMCH4) < deadzone) adjustedPWMCH4 = 0; + // Apply deadzone + if (abs(adjustedPWMCH1) < deadzone) adjustedPWMCH1 = 0; //abs to avoid negatives + if (abs(adjustedPWMCH2) < deadzone) adjustedPWMCH2 = 0; + if (abs(adjustedPWMCH3) < deadzone) adjustedPWMCH3 = 0; + if (abs(adjustedPWMCH4) < deadzone) adjustedPWMCH4 = 0; - // Map joystick values to hard values - int LXU = 0; // Left joystick X axis up - int LXD = 0; // Left joystick X axis down - if (adjustedPWMCH1 > 0) { - LXU = 1; // Joystick is up - } else if (adjustedPWMCH1 < 0) { - LXD = 1; // Joystick is down - } - - return {LXU, LXD, 0, 0}; // Return the values as a struct - } + // Map joystick values to hard values + int LXU = 0; // Left joystick X axis up + int LXD = 0; // Left joystick X axis down + if (adjustedPWMCH1 > 0) { + LXU = 1; // Joystick is up + } else if (adjustedPWMCH1 < 0) { + LXD = 1; // Joystick is down + } + + return {LXU, LXD, 0, 0}; // Return the values as a struct + } - int analogReadMultiPlexer(int addressA, int addressB, int addressC, int addressD, int pin) - { - digitalWrite(D3, LOW); - digitalWrite(D6, addressA); - digitalWrite(D7, addressB); - digitalWrite(D9, addressC); - digitalWrite(D8, addressD); - return analogRead(pin); - } + int analogReadMultiPlexer(int addressA, int addressB, int addressC, int addressD, int pin) + { + digitalWrite(D3, LOW); + digitalWrite(D6, addressA); + digitalWrite(D7, addressB); + digitalWrite(D9, addressC); + digitalWrite(D8, addressD); + return analogRead(pin); + } - int mapPot(int normalizedValue) - { - return map(normalizedValue, 400, 2500, MINPWMVALUE, MAXPWMVALUE); // map the normalized value to the PWM range - } - ``` + int mapPot(int normalizedValue) + { + return map(normalizedValue, 400, 2500, MINPWMVALUE, MAXPWMVALUE); // map the normalized value to the PWM range + } + ``` Now I will turn this into a proper library and clean up what the AI made because there are still some things I want different. I first started off removing all of my code and reducing it down to just what's needed to run the screen with the library. Then I replaced all variables with generic ones instead of using my structs and bringing confusion ```cpp