0) ภาพรวม Digital I/O คืออะไร

  • Digital Output (OUT): เขียนค่า HIGH/LOW ออกไปเป็นแรงดันไฟ เพื่อสั่ง LED, รีเลย์, บัซเซอร์ ฯลฯ
  • Digital Input (IN): อ่านว่าสัญญาณภายนอกเป็น HIGH หรือ LOW (ปุ่มกด, สวิทช์, สัญญาณจากอุปกรณ์อื่น)

ความต่างที่สำคัญ: Arduino UNO ใช้ลอจิก 5V, ESP32 ใช้ลอจิก 3.3V


1) ไฟฟ้า & ข้อจำกัด (สำคัญมาก)

1.1 แรงดันลอจิก

บอร์ดLOW (ประมาณ)HIGH (ประมาณ)
Arduino UNO (5V)0–0.3×Vcc (≤1.5V)≥0.6×Vcc (≥3.0V)
ESP32 (3.3V)ใกล้ 0Vใกล้ 3.3V

ห้าม ป้อนสัญญาณ 5V เข้าขา ESP32 โดยตรง (ต้องลดแรงดันด้วย ตัวต้านทานแบ่งแรงดัน หรือ เลเวลชิฟเตอร์)

1.2 กระแสต่อขา (แนวทางปลอดภัย)

  • Arduino UNO: เอาต์พุตต่อขา ≤ 20 mA (อย่าแตะ 40 mA ซึ่งเป็นค่าสูงสุดเชิงสเป็ก), รวมทั้งชิปอย่าเกิน ~100 mA
  • ESP32: ต่อขาแนะนำ ≤ 12 mA ต่อเนื่อง (ใช้ทรานซิสเตอร์/รีเลย์โมดูลเมื่อโหลดกินกระแส)

1.3 Pull-up/Pull-down ภายใน

บอร์ดPull-upPull-down
Arduino UNOมี (~20–50 kΩ) → INPUT_PULLUPไม่มีในตัว
ESP32มี (~30–100 kΩ) → INPUT_PULLUPมี (INPUT_PULLDOWN)

ขาตั้งค่า INPUT เฉย ๆ มัก “ลอย” → ค่าเดี๋ยว 0 เดี๋ยว 1 → แก้ด้วย Pull-up/down


2) แผนผังขา & ขาที่ควรระวัง

2.1 Arduino UNO

  • D0/D1 ใช้กับ USB-Serial (อัปโหลด/Serial Monitor) → ระวังเมื่อต่ออุปกรณ์ภายนอก
  • LED บนบอร์ด อยู่ที่ D13
  • PWM ที่ analogWrite() ได้: D3, D5, D6, D9, D10, D11

2.2 ESP32 (เช่น DevKitC/NodeMCU-32S)

  • ห้ามใช้: GPIO 6–11 (ต่อแฟลชภายใน)
  • Input-only: GPIO 34–39 (ไม่มี pull-up/down ภายใน)
  • Strapping/Boot pins: 0, 2, 4, 5, 12, 15 (สถานะช่วงบูตมีผล)
  • UART0: 1(TX0), 3(RX0) ใช้กับ Serial Monitor
  • LED บนบอร์ด มักอยู่ GPIO2 (เปลี่ยนตามรุ่น)

3) การตั้งโหมดขา

โหมดใช้อย่างไรอธิบาย
OUTPUTpinMode(pin, OUTPUT)เขียน HIGH/LOW สั่งโหลด
INPUTpinMode(pin, INPUT)อ่านค่า แต่ ลอยได้ (floating) ถ้าไม่ใส่ R ภายนอก
INPUT_PULLUPpinMode(pin, INPUT_PULLUP)มี ตัวต้านทานดึงขึ้น ภายใน (≈30–100 kΩ)
INPUT_PULLDOWNpinMode(pin, INPUT_PULLDOWN)มี ตัวต้านทานดึงลง ภายใน (ESP32 รองรับ)
OUTPUT_OPEN_DRAIN (ESP32)pinMode(pin, OUTPUT_OPEN_DRAIN)ขา “ดึงลงหรือปล่อยลอย” ใช้กับบัสร่วม/เลเวลชิฟต์

ปัญหาคลาสสิก: ตั้ง INPUT แล้ว “ค่าสั่น/เดี๋ยว 0 เดี๋ยว 1” เพราะ ขาลอย → แก้ด้วย Pull-up/down (ภายในหรือภายนอก)


4) วงจรพื้นฐาน & โค้ด

4.1 LED (OUT) + R อนุกรม

ต่อ: GPIO → R (220–330Ω) → LED → GND
Arduino/ESP32 โค้ดเหมือนกัน:

C++
const int LED = 13; // UNO: D13 มี LED on board, ESP32 อาจใช้ 2
void setup(){ pinMode(LED, OUTPUT); }
void loop(){
  digitalWrite(LED, HIGH); delay(500);
  digitalWrite(LED, LOW);  delay(500);
}

4.2 ปุ่มกด (IN) + Pull-up ภายใน (แนะนำสุด)

ต่อ: GPIO → ปุ่ม → GND
ตั้งโหมด: INPUT_PULLUP (ปล่อย=HIGH, กด=LOW)

C++
const int BTN = 2; // UNO: D2 ดีสำหรับ interrupt; ESP32 ใช้ 15/4/0/… ตามบอร์ด
void setup(){ Serial.begin(115200); pinMode(BTN, INPUT_PULLUP); }
void loop(){
  bool pressed = (digitalRead(BTN) == LOW);
  if(pressed) Serial.println("PRESSED");
  delay(20);
}

5) Debounce (แก้ “เด้ง” ของปุ่ม)

5.1 แบบง่าย (มี delay)

C++
const int BTN=2; int last=HIGH;
void setup(){ pinMode(BTN, INPUT_PULLUP); Serial.begin(115200); }
void loop(){
  int r = digitalRead(BTN);
  if(r != last){ delay(30); r = digitalRead(BTN); } // กันเด้ง ~30 ms
  if(r != last){ last = r; if(r==LOW) Serial.println("PRESSED"); }
}

5.2 แบบไม่บล็อก (ใช้ millis)

C++
const int BTN=2; int stable=HIGH, prev=HIGH;
unsigned long tChange=0; const unsigned long DEB=30;
void setup(){ pinMode(BTN, INPUT_PULLUP); Serial.begin(115200); }
void loop(){
  int now = digitalRead(BTN);
  if(now != prev){ tChange=millis(); prev=now; }
  if((millis()-tChange)>DEB && stable!=now){
    stable=now; if(stable==LOW) Serial.println("PRESSED");
  }
}

6) Edge Detection (จับ “เหตุการณ์”)

ทำงานเมื่อ เปลี่ยนจาก HIGH → LOW (FALLING) เพียงครั้งเดียว:

C++
int last=HIGH; const int BTN=2;
void setup(){ pinMode(BTN, INPUT_PULLUP); Serial.begin(115200); }
void loop(){
  int r = digitalRead(BTN);
  if(last==HIGH && r==LOW) Serial.println("FALLING edge");
  last = r;
}

7) Interrupts (ตอบสนองทันที)

7.1 Arduino UNO

  • ดั้งเดิม: D2 (INT0), D3 (INT1)
  • ใช้รูปแบบสากล: attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)
C++
const int BTN=2;
volatile bool fired=false;
void isr(){ fired=true; }       // ISR ต้องสั้น ห้าม delay/Serial หนัก

void setup(){
  Serial.begin(115200);
  pinMode(BTN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BTN), isr, FALLING);
}
void loop(){
  if(fired){ fired=false; Serial.println("IRQ!"); }
}

7.2 ESP32

  • ผูก interrupt ได้กับหลายขา (ยกเว้นบางขาพิเศษ)
  • ใส่ IRAM_ATTR กับ ISR เพื่อวางไว้ใน IRAM
C++
const int BTN=15;
volatile bool fired=false;
void IRAM_ATTR isr(){ fired=true; }

void setup(){
  Serial.begin(115200);
  pinMode(BTN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BTN), isr, FALLING);
}
void loop(){ if(fired){ fired=false; Serial.println("IRQ!"); } }

สัญญาณมีเด้ง? ใช้ debounce ฮาร์ดแวร์ (RC) หรือกรองใน loop() ร่วมกัน


8) Open-Drain

8.1 ESP32 (มีโหมดจริง)

C++
const int OD=23;
void setup(){
  pinMode(OD, OUTPUT_OPEN_DRAIN);
  // ภายนอกต้องมีตัวต้านทาน pull-up ไป 3.3V
}
void loop(){
  digitalWrite(OD, LOW);  // ดึงสายลง 0V
  delay(500);
  digitalWrite(OD, HIGH); // ปล่อยลอย → ถูกดึงขึ้นด้วย pull-up ภายนอก
  delay(500);
}

8.2 Arduino (จำลอง)

  • “ดึงลง” → pinMode(pin, OUTPUT); digitalWrite(pin, LOW);
  • “ปล่อยลอย” → pinMode(pin, INPUT); (อย่าตั้ง HIGH เพราะจะ push-pull)
C++
const int OD=7; // ต่อ pull-up ภายนอกไป 5V
void pullDown(){ pinMode(OD, OUTPUT); digitalWrite(OD, LOW); }
void release(){  pinMode(OD, INPUT); } // high-Z

9) PWM (หรี่ไฟ/ควบคุมความเร็ว)

หัวข้อArduino UNOESP32
APIanalogWrite(pin, duty)ledcSetup/AttachPin/Write
ความละเอียด8 บิต (0–255)เลือกได้ (เช่น 8–12 บิต)
ความถี่คงที่ตามพิน/ไทเมอร์เลือกความถี่ได้ต่อช่อง

UNO:

C++
analogWrite(3, 128); // 50% duty

ESP32:

C++
const int LED=2, CH=0, FREQ=5000, RES=8;
void setup(){ ledcSetup(CH,FREQ,RES); ledcAttachPin(LED,CH); }
void loop(){ for(int d=0; d<=255; d++){ ledcWrite(CH,d); delay(5);} }

10) การเชื่อมกันระหว่าง 5V ↔ 3.3V (สำคัญ)

  • Arduino (5V) → ESP32 (3.3V Input): ใช้ ตัวต้านทานแบ่งแรงดัน (เช่น 10k/20k) หรือ เลเวลชิฟเตอร์
  • ESP32 (3.3V Output) → Arduino (5V Input): มักอ่านได้เป็น HIGH อยู่แล้ว (3.3V > Vih) แต่ถ้าขอบเขตไม่พอ ให้ใช้ เลเวลชิฟเตอร์ เพื่อความชัวร์

11) ทิป/เช็กลิสต์

  • ใส่ R อนุกรมกับ LED เสมอ (220–330 Ω)
  • ปุ่ม: ใช้ INPUT_PULLUP + ต่อไป GND ง่าย/นิ่งทั้งสองแพลตฟอร์ม
  • งานจริงหลีกเลี่ยง delay() ยาว ๆ → ใช้ โครงสร้าง non-blocking ด้วย millis()
  • โหลดกระแสสูง (รีเลย์, มอเตอร์) → ใช้ ทรานซิสเตอร์/MOSFET/รีเลย์โมดูล + ไดโอดกันกลับ
  • ESP32 ระวัง strapping pins ระหว่างบูต และ GPIO 34–39 เป็น input-only
  • Arduino ระวัง D0/D1 (ชนกับ Serial) ตอนอัปโหลด/Monitor

12) ชุดตัวอย่าง “พร้อมใช้”

12.1 Toggle LED ด้วยปุ่ม (Debounce ไม่บล็อก) — ใช้ได้ทั้งคู่

C++
const int LED = 
#if defined(ARDUINO_ARCH_AVR)
  13;          // UNO
#else
  2;           // ESP32 ส่วนใหญ่มักใช้ GPIO2 เป็น LED on-board
#endif

const int BTN = 
#if defined(ARDUINO_ARCH_AVR)
  2;           // UNO: D2 ดี (รองรับ int)
#else
  15;          // ESP32: เลือกขาที่สะดวก
#endif

int ledState = LOW;
int stable = HIGH, prev = HIGH;
unsigned long tChange=0;
const unsigned long DEB=30;

void setup(){
  pinMode(LED, OUTPUT);
  pinMode(BTN, INPUT_PULLUP);
  Serial.begin(115200);
}
void loop(){
  int now = digitalRead(BTN);
  if(now != prev){ tChange=millis(); prev=now; }
  if((millis()-tChange)>DEB && stable!=now){
    stable=now;
    if(stable==LOW){
      ledState = !ledState;
      digitalWrite(LED, ledState);
      Serial.println(ledState ? "LED=ON" : "LED=OFF");
    }
  }
}

12.2 “เลเวลชิฟต์ซอฟต์แวร์” (จำลอง open-drain บน Arduino)

C++
const int LINE=7; // ต่อ pull-up ภายนอก
void driveLow(){ pinMode(LINE, OUTPUT); digitalWrite(LINE, LOW); } // 0V
void release(){  pinMode(LINE, INPUT); }                           // high-Z