โปรแกรมเมนูแบบรายการ (Vertical List) ที่ใช้งานจริงร่วมกับปุ่มกด 3 ปุ่ม (Up, Down, Select)

🔌 การต่อวงจร (Wiring)

เนื่องจากเราใช้ INPUT_PULLUP ในโค้ด ไม่ต้องต่อตัวต้านทานเพิ่ม ต่อตามนี้ได้เลย:

  1. จอ OLED (I2C):
    • VCC -> 3.3V
    • GND -> GND
    • SDA -> A4
    • SCL -> A5
  2. ปุ่มกด (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(); // สั่งแสดงผลขึ้นจอ
}

คำอธิบายการทำงาน

  1. Input_Pullup: สังเกตว่าเราเช็คปุ่มด้วย if (digitalRead(...) == LOW) เพราะเมื่อเรากดปุ่มที่ต่อลงกราวด์ สถานะทางไฟฟ้าจะกลายเป็น 0 (LOW)
  2. Loop Menu: ให้เมนูวนลูปได้ (กดลงที่ตัวสุดท้าย จะเด้งไปตัวแรก) ด้วยคำสั่ง if (selectedItem >= menuLength) selectedItem = 0;
  3. Action State: ใช้ตัวแปร inAction เพื่อจำลองการทำงานจริง
    • กด Select ครั้งแรก -> inAction = true (หน้าจอจะเปลี่ยนไปโชว์ว่า “คุณเลือกเมนูอะไร”)
    • กด Select อีกครั้ง -> inAction = false (กลับมาหน้าเมนูหลัก)