โปรแกรมเมนูแบบรายการ (Vertical List) ที่ใช้งานจริงร่วมกับปุ่มกด 3 ปุ่ม (Up, Down, Select)
🔌 การต่อวงจร (Wiring)
เนื่องจากเราใช้ INPUT_PULLUP ในโค้ด ไม่ต้องต่อตัวต้านทานเพิ่ม ต่อตามนี้ได้เลย:
- จอ OLED (I2C):
- VCC -> 3.3V
- GND -> GND
- SDA -> A4
- SCL -> A5
- ปุ่มกด (Push Buttons):
- ปุ่ม UP: ขาหนึ่งต่อ D2, อีกขาต่อ GND
- ปุ่ม DOWN: ขาหนึ่งต่อ D3, อีกขาต่อ GND
- ปุ่ม SELECT: ขาหนึ่งต่อ D4, อีกขาต่อ GND
💻 โค้ดโปรแกรม
C++
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// --- ตั้งค่าจอ OLED ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// --- กำหนดขาปุ่มกด (Nano ESP32) ---
#define BTN_UP 2
#define BTN_DOWN 3
#define BTN_SELECT 4
// --- ข้อมูลเมนู ---
const char* menuItems[] = {
"DASHBOARD",
"SETTINGS",
"WIFI SETUP",
"SYSTEM INFO",
"EXIT"
};
int menuLength = 5; // จำนวนเมนู
int selectedItem = 0; // เมนูที่เลือกอยู่ปัจจุบัน (เริ่มที่ 0)
bool inAction = false; // ตัวแปรเช็คว่ากดเลือกเมนูไปหรือยัง
void setup() {
Serial.begin(115200);
// 1. ตั้งค่าปุ่มกดเป็น INPUT_PULLUP (ไม่ต้องต่อ R ภายนอก)
// ปกติสถานะจะเป็น HIGH (1), พอกดปุ่มจะเป็น LOW (0)
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
pinMode(BTN_SELECT, INPUT_PULLUP);
// 2. เริ่มต้นจอ OLED
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();
display.display();
}
void loop() {
// --- A. ตรวจจับการกดปุ่ม (Input Handling) ---
// 1. ปุ่ม UP (เลื่อนขึ้น)
if (digitalRead(BTN_UP) == LOW) {
if (!inAction) { // ถ้ายังไม่ได้เข้าหน้าย่อย ให้เลื่อนเมนูได้
selectedItem--;
if (selectedItem < 0) selectedItem = menuLength - 1; // วนไปตัวล่างสุด
}
delay(200); // หน่วงเวลากันปุ่มเบิ้ล (Debounce)
}
// 2. ปุ่ม DOWN (เลื่อนลง)
if (digitalRead(BTN_DOWN) == LOW) {
if (!inAction) {
selectedItem++;
if (selectedItem >= menuLength) selectedItem = 0; // วนไปตัวบนสุด
}
delay(200);
}
// 3. ปุ่ม SELECT (ตกลง / ยกเลิก)
if (digitalRead(BTN_SELECT) == LOW) {
inAction = !inAction; // สลับสถานะ (กดทีนึงเข้า, กดอีกทีออก)
delay(200);
}
// --- B. การวาดหน้าจอ (Render Logic) ---
display.clearDisplay();
if (inAction) {
// === หน้าแสดงผลเมื่อกดเลือกเมนู (Sub-Menu / Action) ===
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(10, 20);
display.println(F("SELECTED:"));
display.setTextSize(1);
display.setCursor(10, 45);
display.print(F("> "));
display.println(menuItems[selectedItem]);
display.setCursor(10, 55);
display.println(F("(Press SEL to Back)"));
} else {
// === หน้าเมนูหลัก (Main Menu) ===
// หัวข้อ
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(25, 0);
display.println(F("MAIN MENU"));
display.drawLine(0, 10, 128, 10, WHITE); // ขีดเส้นใต้
// วาดรายการเมนู
int startY = 15; // ตำแหน่ง Y เริ่มต้นของรายการแรก
for(int i=0; i<menuLength; i++) {
int yPos = startY + (i * 10); // ระยะห่างบรรทัดละ 10 pixel
if(i == selectedItem) {
// [ไฮไลท์] วาดสี่เหลี่ยมทึบสีขาว + ตัวหนังสือสีดำ
display.fillRect(0, yPos - 1, 128, 11, WHITE);
display.setTextColor(BLACK);
} else {
// [ปกติ] พื้นดำ + ตัวหนังสือสีขาว
display.setTextColor(WHITE);
}
display.setCursor(5, yPos);
display.println(menuItems[i]);
}
}
display.display(); // สั่งแสดงผลขึ้นจอ
}
คำอธิบายการทำงาน
- Input_Pullup: สังเกตว่าเราเช็คปุ่มด้วย
if (digitalRead(...) == LOW)เพราะเมื่อเรากดปุ่มที่ต่อลงกราวด์ สถานะทางไฟฟ้าจะกลายเป็น 0 (LOW) - Loop Menu: ให้เมนูวนลูปได้ (กดลงที่ตัวสุดท้าย จะเด้งไปตัวแรก) ด้วยคำสั่ง
if (selectedItem >= menuLength) selectedItem = 0; - Action State: ใช้ตัวแปร
inActionเพื่อจำลองการทำงานจริง- กด Select ครั้งแรก ->
inAction = true(หน้าจอจะเปลี่ยนไปโชว์ว่า “คุณเลือกเมนูอะไร”) - กด Select อีกครั้ง ->
inAction = false(กลับมาหน้าเมนูหลัก)
- กด Select ครั้งแรก ->
