FluxBench.AIShared project · read-only

One-Button Menu

ESP32 DevKit (WROOM-32)·Arduino C++·Updated Jul 27, 2026·by FluxBench TeamIncludes Lab + Schematic

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.

sketch.ino
// 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.