สวัสดีครับนักศึกษาทุกคน จากบทที่แล้วที่เราอ่านค่า Analog (0-4095) เข้ามาได้แล้ว คำถามคือ… ถ้าเราอยากจะส่งค่า Analog ออกไปบ้างล่ะ? เช่น อยากหรี่ไฟ LED ให้สว่าง 50% หรืออยากขับมอเตอร์พัดลมให้หมุนเบาๆ เราจะทำอย่างไร?
ในเมื่อขา Digital ของ ESP32 มีแค่ “เปิด” (3.3V) กับ “ปิด” (0V) มันจ่ายไฟ 1.5V ออกมาตรงๆ ไม่ได้ … วิศวกรจึงคิดค้นเทคนิคที่เรียกว่า PWM มาหลอกสายตาเราและอุปกรณ์ครับ บทความนี้จะพาทุกคนไปทำความเข้าใจและเขียนโปรแกรมกันครับ
1. หลักการของ PWM (Pulse Width Modulation)
PWM คือเทคนิคการ “สับสวิตช์” เปิด-ปิด ไฟให้เร็วมากๆ จนสายตาเรามองไม่ทันครับ
- ถ้าเปิดนานกว่าปิด: ไฟเฉลี่ยจะดูสว่างมาก
- ถ้าปิดนานกว่าเปิด: ไฟเฉลี่ยจะดูสว่างน้อย
เราเรียกสัดส่วนเวลาที่เปิดไฟว่า Duty Cycle (ดิวตี้ ไซเคิล) หน่วยเป็นเปอร์เซ็นต์ (%)
- Duty Cycle 0%: ปิดตลอดเวลา (ไฟดับสนิท = 0V)
- Duty Cycle 50%: เปิดครึ่งหนึ่ง ปิดครึ่งหนึ่ง (สว่างปานกลาง ≈ 1.65V)
- Duty Cycle 100%: เปิดตลอดเวลา (สว่างสุด = 3.3V)
2. LEDC: พระเอกขี่ม้าขาวของ ESP32
ถ้าเป็นบอร์ด Arduino รุ่นเก่า เราจะใช้คำสั่ง analogWrite() ง่ายๆ แต่สำหรับ ESP32 นั้น ภายในชิปมีวงจรพิเศษที่ชื่อว่า LEDC (LED Control) ซึ่งทำงานได้เก่งกว่ามากครับ โดยเราต้องกำหนดค่า 3 อย่างคือ:
- Channel (ช่องสัญญาณ): ESP32 มีให้ใช้ 16 ช่อง (0-15) เราเลือกใช้ช่องไหนก็ได้
- Frequency (ความถี่): ความเร็วในการสับสวิตช์ ปกติ LED ใช้ 5000 Hz (5000 ครั้งต่อวินาที)
- Resolution (ความละเอียด): นิยมใช้ 8-bit (ปรับได้ 0-255 ระดับ) หรือ 10-bit (0-1023 ระดับ)
หมายเหตุ: ใน Arduino ESP32 Core เวอร์ชันใหม่ๆ (v3.0 ขึ้นไป) สามารถใช้
analogWrite()ได้เลย แต่ในการเรียนรู้นี้ ครูขอสอนแบบมาตรฐานเดิม (LEDC) เพื่อให้เข้าใจการตั้งค่าความถี่ซึ่งจำเป็นมากเวลาไปขับมอเตอร์หรือ Servo ครับ
3. ปฏิบัติการ: สร้างไฟหายใจ (Breathing LED)
เราจะเขียนโปรแกรมให้ไฟ LED ค่อยๆ สว่างขึ้นและค่อยๆ มืดลง คล้ายจังหวะการหายใจ
วงจร: ใช้ LED ต่อกับขา GPIO 23 เหมือนเดิม (ต่อผ่าน R 220Ω)
โค้ดโปรแกรม:
const int ledPin = 23; // ขา LED
// การตั้งค่า PWM
const int freq = 5000; // ความถี่ 5000 Hz
const int ledChannel = 0; // ใช้ Channel 0
const int resolution = 8; // ความละเอียด 8-bit (ค่า 0-255)
void setup() {
// 1. กำหนดค่าให้ Channel ของ PWM
ledcSetup(ledChannel, freq, resolution);
// 2. ผูกขา GPIO เข้ากับ Channel นั้น
ledcAttachPin(ledPin, ledChannel);
}
void loop() {
// วนลูปเพิ่มความสว่าง (Fade In)
// i คือค่าความสว่าง (Duty Cycle) ตั้งแต่ 0 ถึง 255
for (int i = 0; i <= 255; i++) {
ledcWrite(ledChannel, i); // สั่งงาน PWM
delay(10); // รอ 10ms เพื่อให้เห็นการเปลี่ยนแปลงทัน
}
// วนลูปดลดความสว่าง (Fade Out)
for (int i = 255; i >= 0; i--) {
ledcWrite(ledChannel, i);
delay(10);
}
}
4. ภารกิจท้าทาย (Challenge): วอลุ่มหรี่ไฟ
เพื่อเป็นการรวบยอดความรู้ บทที่ 6 (Analog Input) และ บทที่ 7 (PWM) เข้าด้วยกัน ให้นักศึกษาลองเขียนโปรแกรม:
- อ่านค่าจากตัวต้านทานปรับค่าได้ (Potentiometer) -> ค่าที่ได้คือ 0 – 4095
- แปลงค่านั้นให้เป็นค่า PWM (0 – 255) -> ใบ้ให้ว่าใช้ฟังก์ชัน
map() - ส่งค่าไปควบคุมความสว่างของ LED
- ผลลัพธ์: หมุนวอลุ่มแล้วไฟต้องหรี่-สว่างตามมือหมุน!
เฉลยแนวทาง (Snippet):
int adcVal = analogRead(potPin); // อ่านค่า 0-4095
// แปลงย่านข้อมูล: map(ค่าดิบ, ต่ำสุดเดิม, สูงสุดเดิม, ต่ำสุดใหม่, สูงสุดใหม่)
int pwmVal = map(adcVal, 0, 4095, 0, 255);
ledcWrite(ledChannel, pwmVal); // ส่งออกไป
สรุปท้ายบท
การเข้าใจ PWM คือพื้นฐานสำคัญมาก เพราะในโลกของ IoT เราไม่ได้แค่สั่งเปิด-ปิด แต่เราต้อง “ควบคุมปริมาณ” ได้ด้วย ไม่ว่าจะเป็นความเร็วโดรน, แขนหุ่นยนต์ หรือความสว่างของไฟ Smart Home
ภารกิจต่อไป: ตอนนี้เรามีพื้นฐาน Hardware และ Coding (Input/Output/Analog/PWM) ครบถ้วนแล้วครับ พร้อมหรือยังที่จะก้าวเข้าสู่หัวใจของวิชานี้? ในบทความหน้า เราจะมาเริ่มต้น “การเชื่อมต่อไร้สาย” โดยเราจะมาดูวิธีทำให้ ESP32 เชื่อมต่อ WiFi บ้าน และดูว่าเราจะเช็ค IP Address ได้อย่างไรครับ
หมายเหตุ:
- โค้ดที่ให้ไปเป็นแบบ Core v2 (
ledcSetup) ซึ่งเป็นมาตรฐานที่ใช้กันแพร่หลายที่สุดในหนังสือเรียนปัจจุบันครับ หากนักศึกษาคนไหนลงโปรแกรมใหม่ล่าสุดแล้ว Code Error ให้เปลี่ยนไปใช้ledcAttach(ledPin, freq, resolution)แทนครับ - Challenge เรื่อง
map()เป็นจุดที่เด็ก ปวส. ควรทำได้ครับ เป็นคณิตศาสตร์ประยุกต์ง่ายๆ
