/*
=== **Scrolling API Menu V2.0 ===
Reference: youtube channel: https://www.youtube.com/upir_upir
1. Create bitmaps with Photopea
a. Save images as .png file
2. Convert image to hex file using image2cpp
a. In image2cpp need to select 'Swap bits in byte: swap'
b. Use when working with u8g2 library
Also try using Gimp and saving as .XBM format
image2cpp does not work with .XBB format. Saved from Gimp as .png format and this
works with image2cpp. Remember to select 'Swap bits in byte: swap'
Notes:
9/14/24:
Completed scrolling menu with API selections.
Includes scrolling 'quote' test.
8/13/25:
Revisited, compared scrolling text to '**Scrolling Text Demo'.
** Verified as good running code.
*/
#include <U8g2lib.h>
// Constructor for U8g2lib
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0);
// === Start generated bitmaps from image2cpp ===
// 'Sunrise_Sunset_Icon', 16x16px
const unsigned char Sunrise_Sunset_Icon[] PROGMEM = { // 'PROGMEM" places this variable in Flash (32k) memory as opposed to SRAM (2k)
0x88, 0x00, 0x72, 0x02, 0x8c, 0x01, 0x05, 0x05, 0x02, 0x02, 0x02, 0x02, 0x02, 0x42, 0x05, 0x45,
0x8c, 0xc1, 0x72, 0xc2, 0x88, 0xe0, 0x00, 0xf0, 0x00, 0x78, 0x00, 0x7c, 0xc0, 0x3f, 0x00, 0x0f
};
// 'Dad_Jokes_Icon', 16x16px
const unsigned char Dad_Jokes_Icon[] PROGMEM = {
0xc0, 0x03, 0x30, 0x0c, 0x0c, 0x30, 0x04, 0x20, 0x72, 0x4e, 0x22, 0x44, 0x01, 0x80, 0x01, 0x80,
0x01, 0x80, 0x19, 0x98, 0xf2, 0x4f, 0xe2, 0x47, 0x84, 0x21, 0x0c, 0x30, 0x30, 0x0c, 0xc0, 0x03
};
// 'Useless_Facts_Icon', 16x16px
const unsigned char Useless_Facts_Icon[] PROGMEM = {
0x00, 0x00, 0x00, 0x40, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x07,
0x80, 0x03, 0x87, 0x03, 0xcf, 0x01, 0xde, 0x01, 0xfc, 0x00, 0xf8, 0x00, 0x70, 0x00, 0x20, 0x00
};
// 'Quote_Icon', 16x16px
const unsigned char Quote_Icon[] PROGMEM = {
0xcf, 0x03, 0xcf, 0x03, 0xcf, 0x03, 0xc3, 0x00, 0xc3, 0x00, 0x86, 0x01, 0x0c, 0x03, 0x00, 0x00,
0x00, 0x00, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0x00, 0xc3, 0x00, 0xc3, 0x80, 0x61, 0xc0, 0x30
};
// 'Chuck_Icon_new', 16x16px
const unsigned char Chuck_Icon[] PROGMEM = {
0x00, 0x00, 0x42, 0x42, 0xa2, 0x45, 0x16, 0x68, 0xfc, 0x3f, 0x08, 0x10, 0x68, 0x16, 0x08, 0x10,
0x08, 0x11, 0x88, 0x11, 0x08, 0x10, 0xc8, 0x13, 0x28, 0x14, 0x08, 0x10, 0x50, 0x0a, 0xa0, 0x05
};
// Array of all icon bitmaps.
const unsigned char* menu_icon[5] = {
Sunrise_Sunset_Icon,
Dad_Jokes_Icon,
Useless_Facts_Icon,
Quote_Icon,
Chuck_Icon
};
// 'Boarder', 128x20px
const unsigned char Boarder[] PROGMEM = {
0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01
};
// 'Scroll Bar', 8x64px
const unsigned char Scroll_Bar[] PROGMEM = {
0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40,
0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40,
0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40,
0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40
};
// 2D array of strings for menu items
const int NUM_ITEMS = 5;
char menu_item[NUM_ITEMS][20] = {
{"Sunrise Sunset"},
{"Dad Jokes"},
{"Useless Facts"},
{"Quotes"},
{"Chuck Jokes"}
};
// === end generated bitmaps from image2cpp ===
// === Start OLED menu variables ===
int item_sel_previous; // menu item before selected
int item_selected = 0; // selected menu item
int item_sel_next; // menu item after selected
#define BUTTON_UP_PIN 12 // pin for UP button
#define BUTTON_SELECT_PIN 8 // pin for SELECT button
#define BUTTON_DOWN_PIN 4 // pin for DOWN button
int button_up_clicked = 0; // only perform action when button is clicked, and wait until another press
int button_select_clicked = 0; // same as above
int button_down_clicked = 0; // same as above
int current_screen = 0; // 0 = menu, 1 = screenshot, 2 = qr code
// === End OLED menu variables ===
// === Start scrolling text variables ===
u8g2_uint_t offset; // Tracks the current horizontal position for scrolling animation
u8g2_uint_t width; // Stores the total pixel width of the text string
// this example text is 82 characters long
const char *text = "We could never learn to be brave and patient if there were only joy in the world. ";
// === End scrolling text variables ===
void setup() {
u8g2.begin();
u8g2.setBitmapMode(1); // background mode 0 solid, mode 1 transparent
u8g2.setColorIndex(1); // set the color to white
// === used for scrolling text ===
u8g2.setFont(u8g2_font_10x20_mr);
// width is a combination of 'text' length, which is 82
// and the pixel width of the font, which is 10.
width = u8g2.getUTF8Width(text); // width: 82 x 10 = 820
u8g2.setFontMode(0); // enable transparent mode, which is faster
// ---------- end ----------
pinMode(BUTTON_UP_PIN, INPUT_PULLUP); // up button
pinMode(BUTTON_SELECT_PIN, INPUT_PULLUP); // select button
pinMode(BUTTON_DOWN_PIN, INPUT_PULLUP); // down button
}
void loop() {
if (current_screen == 0) { // MENU SCREEN
if ((digitalRead(BUTTON_UP_PIN) == LOW) && (button_up_clicked == 0)) { // up button pressed
item_selected = item_selected - 1; // select previous item
button_up_clicked = 1; // set button to clicked to only perform the action once
if (item_selected < 0) { // if first item was selected, jump to last item
item_selected = NUM_ITEMS - 1;
}
}
if ((digitalRead(BUTTON_DOWN_PIN) == LOW) && (button_down_clicked == 0)) { // down button pressed
item_selected = item_selected + 1; // select next item
button_down_clicked = 1; // set button to clicked to only perform the action once
if (item_selected >= NUM_ITEMS) { // last item was selected, jump to first menu item
item_selected = 0;
}
}
if ((digitalRead(BUTTON_UP_PIN) == HIGH) && (button_up_clicked == 1)) { // unclick
button_up_clicked = 0;
}
if ((digitalRead(BUTTON_DOWN_PIN) == HIGH) && (button_down_clicked == 1)) { // unclick
button_down_clicked = 0;
}
}
// This section toggles through enter button selections by changing variable 'current_screen' value to 0, 1 or 2
if ((digitalRead(BUTTON_SELECT_PIN) == LOW) && (button_select_clicked == 0)) { // select button clicked, jump between screens
button_select_clicked = 1; // set button to clicked to only perform the action once
if (current_screen == 0) {current_screen = 1;} // menu items screen --> screenshots screen
else if (current_screen == 1) {current_screen = 2;} // screenshots screen --> qr codes screen
else {current_screen = 0;} // qr codes screen --> menu items screen
}
if ((digitalRead(BUTTON_SELECT_PIN) == HIGH) && (button_select_clicked == 1)) { // unclick
button_select_clicked = 0;
}
// set correct values for the previous and next items
item_sel_previous = item_selected - 1; // = 0 - 1 or -1
if (item_sel_previous < 0) { // Yes, -1 is < 0
item_sel_previous = NUM_ITEMS - 1; // NUM_ITEMS(4) - 1 = 3
}
item_sel_next = item_selected + 1; // = 0 + 1 or 1
if (item_sel_next >= NUM_ITEMS) { // No, 1 is not > 4
item_sel_next = 0; // next item would be after last = make it the first
}
u8g2.firstPage(); // required for page drawing, mode 1
do {
if (current_screen == 0) { // MENU SCREEN
// selected menu item boarder
u8g2.drawXBMP( 0, 22, 128, 20, Boarder);
// draw previous item as icon + label
u8g2.setFont(u8g2_font_7x14_tf);
u8g2.drawStr(26, 15, menu_item[item_sel_previous]);
// From top left corner - over, down, horz size, vert size)
// u8g2.drawXBMP(, over, down, horz, vert);
u8g2.drawXBMP(4, 2, 16, 16, menu_icon[item_sel_previous]);
// draw selected item as icon + label in bold font
u8g2.setFont(u8g2_font_7x14B_tf);
u8g2.drawStr(25, 37, menu_item[item_selected]);
u8g2.drawXBMP(4, 24, 16, 16, menu_icon[item_selected]);
// draw next item as icon + label
u8g2.setFont(u8g2_font_7x14_tf);
u8g2.drawStr(26, 59, menu_item[item_sel_next]);
u8g2.drawXBMP(4, 46, 16, 16, menu_icon[item_sel_next]);
// draw scrollbar background
u8g2.drawXBMP(120, 0, 8, 64, Scroll_Bar);
// draw scrollbar handle
u8g2.drawBox(125, 64/NUM_ITEMS * item_selected, 3, 64/NUM_ITEMS);
}
// === Run 'Scrolling Quote' Test ===
else if (current_screen == 1) { // MENU OPEN SCREEN
u8g2_uint_t x; // declare a local variable x for horizontal positioning
u8g2.firstPage();
do {
// draw the scrolling text at current offset
x = offset;
u8g2.setFont(u8g2_font_10x20_mr); // set the target font
do { // repeated drawing of the scrolling text...
u8g2.drawUTF8(x, 30, text); // draw the scolling text
x += width; // add the pixel width of the scrolling text
// draw again until the complete display is filled
// while x < 128
// getDisplayWidth() = 128
} while (x < u8g2.getDisplayWidth()); // draw again until the complete display is filled
u8g2.setFont(u8g2_font_10x20_mr); // draw the current pixel width
u8g2.setCursor(0, 58);
u8g2.print(width); // this value must be lesser than 128 unless U8G2_16BIT is set
} while (u8g2.nextPage());
offset -= 10; // speed adjust, lower number slower, higher number faster
if ((u8g2_uint_t) offset < (u8g2_uint_t) - width)
offset = 0; // start over again
}
else if (current_screen == 2) { // SUB MENU OPEN SCREEN
// Do something - "Display second item"
}
// === End 'Scrolling Quote' Test ===
} while (u8g2.nextPage()); // required for page drawing, mode 1
delay(0);
}