Compare commits

...

2 Commits

Author SHA1 Message Date
cb4a809c60 initial commit 2025-05-15 16:01:28 +02:00
140f58993b Initial commit 2025-05-06 15:15:17 +02:00
6 changed files with 403 additions and 0 deletions

BIN
EasyMenu.zip Normal file

Binary file not shown.

68
example/Graphics.h Normal file
View File

@@ -0,0 +1,68 @@
// all the arrays below are generated from images using image2cpp website
// PROGMEM is used to store the images in flash memory instead of SRAM
const unsigned char upir_logo [] PROGMEM = { // this is another way how to define images, using binary notation
B00010101, B11010111,
B00010101, B01000101,
B00010101, B10010110,
B00011001, B00010101
};
// 'icon_3dcube', 16x16px
const unsigned char bitmap_icon_3dcube [] PROGMEM = {
0x00, 0x00, 0x01, 0x80, 0x07, 0x60, 0x19, 0x18, 0x61, 0x06, 0x51, 0x0a, 0x45, 0xa2, 0x41, 0x02,
0x45, 0x22, 0x41, 0x02, 0x45, 0xa2, 0x51, 0x0a, 0x61, 0x06, 0x19, 0x18, 0x07, 0x60, 0x01, 0x80
};
// 'icon_battery', 16x16px
const unsigned char bitmap_icon_battery [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x40, 0x04, 0x5b, 0x66, 0x5b, 0x66,
0x5b, 0x66, 0x40, 0x04, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'icon_dashboard', 16x16px
const unsigned char bitmap_icon_dashboard [] PROGMEM = {
0x07, 0xe0, 0x18, 0x18, 0x21, 0x24, 0x50, 0x02, 0x48, 0x0a, 0x84, 0x01, 0x83, 0x81, 0xa2, 0x45,
0x82, 0x41, 0x81, 0x81, 0xa0, 0x05, 0x40, 0x02, 0x4b, 0xd2, 0x23, 0xc4, 0x18, 0x18, 0x07, 0xe0
};
// 'icon_fireworks', 16x16px
const unsigned char bitmap_icon_fireworks [] PROGMEM = {
0x00, 0x00, 0x00, 0x08, 0x00, 0x94, 0x10, 0x08, 0x10, 0x00, 0x6c, 0x00, 0x10, 0x10, 0x10, 0x10,
0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0x10, 0x04, 0x10, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00
};
// 'icon_gps_speed', 16x16px
const unsigned char bitmap_icon_gps_speed [] PROGMEM = {
0x00, 0x00, 0x03, 0xf0, 0x00, 0x08, 0x01, 0xe4, 0x00, 0x12, 0x00, 0xca, 0x06, 0x2a, 0x07, 0x2a,
0x07, 0x8a, 0x07, 0xc2, 0x07, 0xc0, 0x0a, 0x00, 0x1f, 0x00, 0x20, 0x80, 0x7f, 0xc0, 0x00, 0x00
};
// 'icon_knob_over_oled', 16x16px
const unsigned char bitmap_icon_knob_over_oled [] PROGMEM = {
0x00, 0x00, 0x1f, 0xf0, 0x13, 0x50, 0x1b, 0xb0, 0x11, 0x50, 0x1f, 0xf0, 0x03, 0x80, 0x01, 0x00,
0x00, 0x00, 0x09, 0x20, 0x49, 0x24, 0x20, 0x08, 0x00, 0x01, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00
};
// 'icon_parksensor', 16x16px
const unsigned char bitmap_icon_parksensor [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x22, 0x00, 0x25, 0x00, 0xf9, 0x00, 0x00, 0x81,
0x0c, 0x85, 0x12, 0x95, 0xd2, 0x95, 0x0c, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'icon_turbo', 16x16px
const unsigned char bitmap_icon_turbo [] PROGMEM = {
0x00, 0x0e, 0x07, 0xf1, 0x18, 0x01, 0x20, 0x01, 0x40, 0x01, 0x43, 0xf1, 0x84, 0x4e, 0x8a, 0xa0,
0x89, 0x22, 0x8a, 0xa2, 0x84, 0x42, 0x43, 0x84, 0x40, 0x04, 0x20, 0x08, 0x18, 0x30, 0x07, 0xc0
};
// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 384)
const unsigned char* bitmap_icons[8] = {
bitmap_icon_3dcube,
bitmap_icon_battery,
bitmap_icon_dashboard,
bitmap_icon_fireworks,
bitmap_icon_gps_speed,
bitmap_icon_knob_over_oled,
bitmap_icon_parksensor,
bitmap_icon_turbo
};
// ------------------ end generated bitmaps from image2cpp ---------------------------------

85
example/main.cpp Normal file
View File

@@ -0,0 +1,85 @@
//Graphics and idea from https://www.youtube.com/watch?v=HVHVkKt-ldc https://github.com/upiir/arduino_oled_menu
//
// This libary is made with vibes and Claude 3.7 sonnet
// Cleaned up by Smikkelbakje
#include <Arduino.h>
#include <Wire.h>
#include <U8g2lib.h>
#include "MenuSystem.h"
#include "Graphics.h"
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
MenuSystem menu(&u8g2);
bool buttonDown = false;
bool buttonRight = false;
bool buttonUp = false;
// Actions for menu items
void action3DCube() {
Serial.println("3D Cube selected!");
}
void actionBattery() {
Serial.println("Battery selected!");
}
void actionDashboard() {
Serial.println("Dashboard selected!");
}
void actionFireworks() {
Serial.println("Fireworks selected!");
}
void setup() {
// Initialize serial communication
Serial.begin(9600);
Wire.begin(); // Initialize I2C bus
Wire.setClock(400000); // Set to 400kHz (standard speed for most displays)
// Clear and initialize the display
u8g2.begin();
u8g2.clearBuffer();
u8g2.sendBuffer();
// 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();
}
void loop() {
//Set according to your button setup or use the booleans in another configuration
buttonUp = digitalRead(D0);
buttonUp = digitalRead(D1);
buttonUp = digitalRead(D2);
// Update menu button states
menu.setButtons(buttonDown, buttonRight, buttonUp);
// Update and draw the menu
menu.update();
}

33
src/MenuGraphics.h Normal file
View File

@@ -0,0 +1,33 @@
// 'scrollbar_background', 8x64px
const unsigned char bitmap_scrollbar_background [] PROGMEM = {
0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02,
0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02,
0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02,
0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00
};
// 'item_sel_outline', 128x21px
const unsigned char bitmap_item_sel_outline [] PROGMEM = {
0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0
};

152
src/MenuSystem.cpp Normal file
View File

@@ -0,0 +1,152 @@
#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::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);
}
}
}

65
src/MenuSystem.h Normal file
View File

@@ -0,0 +1,65 @@
#ifndef MENU_SYSTEM_H
#define MENU_SYSTEM_H
#include <Arduino.h>
#include <U8g2lib.h>
class MenuSystem {
public:
// Constructor
MenuSystem(U8G2_SSD1306_128X64_NONAME_F_HW_I2C* display);
// Initialize menu
void begin();
// @brief Add a menu item with an icon
// @param name The name of the menu item
// @param icon The icon associated with the menu item in a bitmap array
void addMenuItem(const char* name, const uint8_t* icon);
// @brief Set a callback function for a menu item
// @param index The index of the menu item
// @param callback The function to be called when the item is selected
void setItemCallback(int index, void (*callback)());
// @brief Set the button states
// @param up The state of the up button
// @param select The state of the select button
// @param down The state of the down button
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