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-up | Pull-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) การตั้งโหมดขา
| โหมด | ใช้อย่างไร | อธิบาย |
|---|---|---|
OUTPUT | pinMode(pin, OUTPUT) | เขียน HIGH/LOW สั่งโหลด |
INPUT | pinMode(pin, INPUT) | อ่านค่า แต่ ลอยได้ (floating) ถ้าไม่ใส่ R ภายนอก |
INPUT_PULLUP | pinMode(pin, INPUT_PULLUP) | มี ตัวต้านทานดึงขึ้น ภายใน (≈30–100 kΩ) |
INPUT_PULLDOWN | pinMode(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 UNO | ESP32 |
|---|---|---|
| API | analogWrite(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