One-Button Menu
A whole menu UI driven by a single pushbutton, using the OneButton library’s event state machine instead of hand-rolled debounce timers: btn.tick() in loop() debounces GPIO 14 and dispatches the callbacks registered with attachClick, attachDoubleClick and attachLongPressStart. A single click cycles through four menu items on the SSD1306 OLED, a double-click toggles the highlighted item on/off (the green LED on GPIO 4 mirrors the state), and holding the button for more than 800 ms jumps back to the home entry. The callbacks just update state and set a redraw flag — loop() does the drawing — the same keep-callbacks-short discipline you’d use on real hardware. In the Lab, try quick clicks vs. a slow hold on the bench button to see the three event types fire in the serial monitor.
// One-Button Menu - click / double-click / long-press on a SINGLE button
// ESP32 DevKit v1. SSD1306 OLED @0x3C on I2C (SDA=21, SCL=22),
// menu button on GPIO 14 (INPUT_PULLUP, other leg to GND), green LED on GPIO 4.
//
// The OneButton library turns one pushbutton into three inputs: its tick()
// state machine debounces the pin and dispatches the callbacks you register
// with attachClick / attachDoubleClick / attachLongPressStart. Here a single
// click cycles through four menu items, a double-click toggles the selected
// item (LED mirrors the state), and a long press (>800 ms) jumps back home.
// Callbacks only update state + set a flag; loop() does the OLED redraw -
// keep ISR-adjacent callbacks short, exactly like on real hardware.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <OneButton.h>
Adafruit_SSD1306 display(128, 64, &Wire, -1);
// pin 14, active LOW, enable internal pull-up
OneButton btn(14, true, true);
const int LED = 4;
int item = 0; // which menu entry is highlighted (0..3)
bool selected = false; // is the highlighted item switched on?
bool needRedraw = false;
void drawScreen() {
display.clearDisplay();
display.setCursor(0, 0);
display.print("ONE-BUTTON MENU");
display.setTextSize(2);
display.setCursor(0, 20);
if (item == 0) display.print("Lights");
else if (item == 1) display.print("Fan");
else if (item == 2) display.print("Music");
else display.print("Lock");
display.setTextSize(1);
display.setCursor(0, 52);
display.print(selected ? "STATE: ON" : "STATE: off");
display.display();
}
// single click -> next menu item
void onClick() {
item = (item + 1) % 4;
selected = false;
digitalWrite(LED, LOW);
Serial.print("Click -> item ");
Serial.println(item);
needRedraw = true;
}
// double click -> toggle the highlighted item on/off
void onDouble() {
selected = !selected;
digitalWrite(LED, selected ? HIGH : LOW);
Serial.print("Double -> select ");
Serial.println(selected ? 1 : 0);
needRedraw = true;
}
// long press (>800 ms) -> back to home, everything off
void onLong() {
item = 0;
selected = false;
digitalWrite(LED, LOW);
Serial.println("Long -> HOME");
needRedraw = true;
}
void setup() {
Serial.begin(115200);
pinMode(LED, OUTPUT);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextColor(SSD1306_WHITE);
btn.attachClick(onClick);
btn.attachDoubleClick(onDouble);
btn.attachLongPressStart(onLong);
Serial.println("One-button menu ready");
drawScreen();
}
void loop() {
btn.tick(); // run the click/double/long-press state machine
if (needRedraw) {
needRedraw = false;
drawScreen();
}
delay(10);
}Built with FluxBench.AI — the AI workbench for embedded projects.