สวัสดีครับ หลังจากที่เราได้ติดตั้ง Software ที่จำเป็นแล้ว และสามารถสั่งการ Board ที่ตนเองได้รับเรียบร้อยแล้วนั้น เนื้อหาหลังจากนี้ไป เราจะได้เริ่มทำความรู้จักอุปกรณ์เริ่มต้นสำหรับการรับข้อมูลจากอุปกรณ์ และส่งออกข้อมูลไปให้อุปกรณ์อย่างง่ายก่อน โดยเริ่มต้นเราจะรับส่งข้อมูลในรูปแบบของดิจิตอล และต่อมาเราจะมาเรียนรู้การรับส่งข้อมูลในข้อมูลแบบอนาล็อคกันครับ
เนื่องจากคลิปวิดีโอในครั้งนี้มีเสียงที่เบา น้องๆ อาจใช้ extension ของ google chrome ช่วยเพิ่มเสียงได้ เช่น Volume Master
Intro
โครงสร้าง Project PlatformIO
ก่อนอื่นที่เราจะเริ่มเรียนรู้คำสั่งต่างๆ เราควรที่จะทำความเข้าใจโครงสร้างของโปรเจคเสียก่อน โดยเมื่อเราสร้าง Project ของ PlatformIO ขึ้นมาแล้วเนี่ย ระบบมักจะสร้างไฟล์พื้นฐานประมาณนี้
.
├── include
│ └── README
├── lib
│ └── README
├── platformio.ini
├── src
│ └── main.cpp
└── test
└── README
โดยไฟล์ที่เราสนใจนั้น มีเพียงสองส่วนคือ
.
├── platformio.ini
├── src
└── main.cpp
เริ่มจากไฟล์แรก platformio.ini ไฟล์นี้แสดงถึง config environment ต่างๆ เช่น บอร์ดที่ใช้ บอร์ดที่ใช้ ความเร็วในการ upload โค้ดลงบอร์ด ความเร็วของ Serial monitor และไลบราลี่เพิ่มเติมก็จะถูกระบุในไฟล์นี้เช่นเดียวกัน
ต่อมาพระเอกของเราก็คือ src/main.cpp ซึ่งเป็นพื้นที่ที่เราจะพัฒนาในการเขียนโค้ดหลักๆ โดยไฟล์ดังกล่าวมีโครงสร้างดังนี้
#include <Arduino.h>
void setup(){
}
void loop(){
}
ลักษณะของโครงสร้างก็เป็นเหมือนภาษา cpp ทั่วไป และมีความคล้ายคลึงกับไฟล์ .ino ของ arduino โดยส่วนหัวก็จะเป็นส่วนของ Header ที่จะ include libraries จากภายนอกเพื่อนำฟังก์ชั่นต่างๆ มาใช้งาน โดยในที่นี้เราได้ include header Arduino.h ซึ่งทำให้เราสามารถใช้คำสั่งพื้นฐานใน Arduino ใน PlatformIO ได้
โดยฟังก์ชั่นหลัก 2 ตัวมีความแตกต่างกันดังนี้
void setup()เป็นฟังก์ชั่นเริ่มต้น โดยคำสั่งที่อยู่ภายใต้ฟังก์ชั่นนี้ จะถูกทำเพียงครั้งเดียวvoid loop()เป็นฟังก์ชั่นที่เริ่มทำงานหลังจากvoid setup()โดยคำสั่งที่อยู่ภายใต้ฟังก์ชั่นนี้จะถูกทำไปเรื่อย ไม่มีวันสิ้นสุด (เหมือนถูกครอบด้วยwhile(1) {})
GPIO
GPIO ย่อมาจาก general purpose input/output เรียกเป็นภาษาไทยง่ายๆว่า พอร์ตเอนกประสงค์ คือเราสามารถควบคุม คอนโทรลให้เป็น ค่าต่างๆได้ และเรายังสามารถกำหนด GPIO เหล่านี้ให้เป็น INPUT หรือ OUTPUT ก็ได้
โดยบอร์ด LOLIN D32 PRO มี pinout ดังนี้

LOLIN D32 PRO Pinout
โดยพอร์ทที่เราเห็นเป็นตัวเลขสีเทา พอร์ทเหล่านั้นสามารนำมาใช้เป็นพอร์ท INPUT และ OUTPUT ได้หมดเลย
I/O Device
Serial Monitor
Serial Monitor เป็นการส่งข้อมูลผ่านทาง Serial ซึ่ง Serial ของเราในที่นี้ก็คือผ่านทาง USB (Universal Serial Bus) ที่เราเชื่อมต่อให้กับบอร์ดของเราอยู่ แล้วให้คอมพิวเตอร์ของเราอ่านข้อมูลที่ออกมาจาก Serial Bus อีกทีครับ โดยการส่งข้อมูลผ่าน Serial นั้น เราจำเป็นต้องกำหนดอัตราการส่งข้อส่ง/รับ ข้อมูล ให้เท่ากัน จึงจะสามารถตีความข้อมูลที่ส่งมาจากผู้ส่งได้อย่างถูกต้่อง โดยการส่งข้อมูลผ่าน Serial Monitor นั้นเป็นการรับส่งข้อมูลที่พื้นฐานที่สุด เพราะไม่ต้องมานั่งต่ออุปกรณ์เพิ่มเติม

ข้อมูลที่ส่งกันระหว่างอุปกรณ์และคอมพิวเตอร์ก็คือ bit นั่นเอง
ต่อไปนี้จะเป็น ตัวอย่างโค้ดสำหรับการส่งข้อมูลผ่าน Serial Monitor ทุกๆ 1 วินาที
#include <Arduino.h>
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.println("Hello World");
delay(1000);
}
อธิบายคำสั่งได้ดังนี้
| Syntax | Description |
|---|---|
Serial.begin(115200); | กำหนดความเร็วในส่งข้อมูลเป็น 115200 symbol/วินาที |
Serial.println("Hello World"); | ส่งข้อความ "Hello World\n" ผ่าน Serial port |
delay(1000); | หยุดการทำงานชั่วคราวเป็นเวลา 1000 ms |
ทำความรู้จักกับ Breadboard
เป็นบอร์ดที่ใช้ทดลองวงจรอิเล็กทรอนิกส์ ลักษณะเป็นแผ่นพลาสติกหนาสีขาว บนแผ่นมีรูเรียงกันจำนวนมาก ภายในรูมีตัวนำไฟฟ้าซึ่งเชื่อมต่อกัน

ลักษณะการเชื่อมต่อของรู Breadboard ขนาดเล็ก

ลักษณะการเชื่อมต่อของรู Breadboard ขนาดใหญ่
ต่อวงจรเพิ่มเติม
ต่อจากนี้ไป เราจะเริ่มติดต่อสื่อสารกับอุปกรณ์ Input Output เพิ่มเติม โดยให้น้องนำอุปกรณ์ที่ได้ทั้งหมด ตาม List ที่ได้ให้ไป ต่อตาม Pin ที่กำหนดไว้ให้ตามวงจรด้านล่าง
โปรดตรวจสอบความถูกต้องของวงจรก่อนที่จะเสียบ USB ให้กับตัวบอร์ด โดยจุดที่ต้องระวังจะมี 2 ส่วน
- ไฟ 5V (BAT) เข้ากับตัว LDR โดยระวังอย่าให้ไฟ 5V ต่อเข้ากับ GND โดยไม่ผ่านตัวต้านทาน
- ปุ่มต้องหันให้ถูกด้าน เพราะอาจทำให้เกิด Short Circuit ได้ โดยสามารถกระโดดไปอ่านเนื้อหา Switch ก่อนได้

| อุปกรณ์ | Pin ที่เชื่อมต่อ |
|---|---|
| LED สีแดง | 26 |
| LED สีเหลือง | 25 |
| LED สีเขียว | 33 |
| LDR | 34 |
| BUTTON (PULLUP) | 27 |
โดยตัวต้านทานที่ใช้กับ LED จะใช้ 330Ω (ส้ม-ส้ม-น้ำตาล) และ LDR ใช้ตัวต้านทาน 10kΩ (น้ำตาล-ดำ-ส้ม)
โดยน้องๆ อาจได้ตัวต้านทานที่มีแถบสี 4 สี หรือ 5 สีก็ได้
LED
LED หรือ ไดโอดเปล่งแสง (Light Emitting Diode) คืออุปกรณ์ไฟฟ้าแบบ Passive ที่เมื่อมีกระแสไหลผ่านแล้วจะเปล่งแสงออกมา ด้วยการที่เป็นไดโอด จึงยอมให้กระแสไหลผ่านทางเดียวคือจากขั้ว + ไปยังขั้ว - ถ้าต่อกลับทิศ ไฟจะไม่ติด โดยเราสามารถควบคุม LED ได้โดยการส่งสัญญาณแบบดิจิตอล (HIGH หรือ LOW)

วิธีการดูขั้วของ LED
การต่อ LED ควรต่ออนุกรมกับตัวต้านทานเสมอ เพื่อแบ่งแรงดันไฟฟ้า หากไม่ต่อตัวต้านทาน อาจทำให้ LED ไหม้ได้
โค้ดต่อไปนี้เป็นตัวอย่างแสดงการกระพริบ LED ติด-ดับสลับกัน 0.5 วินาที
#include <Arduino.h>
#define GREEN 33
void setup() {
pinMode(GREEN, OUTPUT);
digitalWrite(GREEN,0);
}
void loop() {
digitalWrite(GREEN,1);
delay(500);
digitalWrite(GREEN,0);
delay(500);
}
อธิบายคำสั่งได้ดังนี้
| Syntax | Description |
|---|---|
pinMode(pin, mode); | ระบุว่า pin ในบอร์ด เป็นโหมด INPUT OUTPUT หรือ INPUT_PULLUP |
digitalWrite(pin,0 or 1); | สั่ง pin ให้ส่ง logic 0 หรือ 1 |
Switch
ก่อนที่เราจะมาเรียนรู้การรับสัญญาณของปุ่ม เราควรเรียนรู้ลักษณะของวงจรของปุ่มเสียก่อน

ลักษณะการเชื่อมต่อของขาอุปกรณ์
จะเห็นว่าหากเราหันปุ่มในลักษณะเอาขาแบะไปทางบน-ล่าง ตามรูปนั้น จะเห็นว่า ขาที่ 1-3 และ ขาที่ 2-4 เชื่อมต่อถึงกัน ไม่ว่าจะตอนกดปุ่ม หรือไม่ได้กดปุ่ม ดังนั้น เราจึงใช้แค่ 2 ขา ในการใช้งาน
เช่น ใช้ขา 1 ต่อ GPIO ของบอร์ด และขาที่ 2 เชื่อม ground
หรือจะต่อแบบรูปด้านบนที่ใช้ ขาที่ 1 ต่อที่ของ GPIO27 และ ขาที่ 4 เชื่อมต่อกับ GND ของตัวบอร์ด
โปรดใช้ความระมัดระวังในการต่อ Switch โดยหากใช้ขาที่ Short ถึงกัน ได้แก่ขาที่ 1-3 และ ขาที่ 2-4 หากเชื่อมไฟตรงเข้า GND อาจทำให้อุปกรณ์เสียหายได้
ในการเรียนทั้งหมดของค่าย จะให้ต่อ switch ในรูปแบบ PULL-UP เพื่อลดการใช้ตัวต้านทานที่ไม่จำเป็น โดยน้องสามารถศึกษาเรื่องการต่อ Button ในรูปแบบต่างๆเพิ่มเติมได้ ที่นี่
#include <Arduino.h>
#define BUTTON 27
int cnt = 0;
void setup() {
Serial.begin(115200);
Serial.println("");
pinMode(BUTTON, INPUT_PULLUP);
}
void loop() {
if (!digitalRead(BUTTON)){
cnt++;
Serial.println(cnt);
}
}
หมายเหตุ การต่อสวิชต์แบบ PULLUP ค่าขณะที่กดจะเป็น LOW ตอนปล่อยจะเป็น HIGH
ถ้าลองทดลอง ดูค่า cnt จาก Serial Montitor เมื่อทำการกดปุ่ม 1 ครั้ง แต่ทำให้ค่า cnt เพิ่มขึ้นไปเป็นร้อยกว่า
ซึ่งตามความเข้าใจของเราค่าของ cnt ควรเพิ่มแค่ 1 ตามจำนวนครั้งในการกด
ปัญหาดังกล่าวเรียกว่า Bouncing เพราะสวิสต์นั้นภายในเป็นสปริง เมื่อเกิดการกดจะยังเกิดจังหวะที่หน้าสัมผัสของวงจรยังไม่ปิดสนิท ทำให้เกิดจังหวะที่ค่าของสวิสต์สวิงไปสวิงมา ต้องรอจนกว่าวงจรของสวิตส์ปิดสนิท

ดังนั้นเราจึงต้องทำการ Debouncing ปุ่ม โดยทำได้ 2 วิธี
- แก้ไขโดยใช้ Software โดยใช้การ
delay()ในระยะเวลาสั้นๆ เพื่อไปดูสถานะของปุ่มตอนวงจรคงที่แล้ว - แก้ไขโดยการต่อตัวเก็บประจุเพิ่มเพื่อสร้าง RC Circuit กรองความถี่ต่ำ (low-pass filter)
สามารถศึกษาเพิ่มเติมได้ ที่นี่
ด้วยเวลาอันจำกัดของเรา เราจะใช้ library ภายนอกเพิ่มเติมที่ชื่อ Bounce2 โดยให้ไปเพิ่มใน PlatformIO ให้เรียบร้อยก่อน
เมื่อทำการเพิ่มไลบราลี่ไปใน Project ของเราแล้วนั้น ให้เปลี่ยนโค้ดการรับสวิสต์เป็นแบบนี้
#include <Arduino.h>
#include <Bounce2.h>
#define BUTTON 27
int cnt = 0;
Bounce debouncer = Bounce();
void setup() {
Serial.begin(115200);
Serial.println("BUTTON");
debouncer.attach(BUTTON, INPUT_PULLUP);
debouncer.interval(25);
}
void loop() {
debouncer.update();
if ( debouncer.fell() ) {
cnt++;
Serial.println(cnt);
}
}
จะเห็นว่าค่า cnt จะขึ้นตามจำนวนครั้งในการกดจริงๆ แล้ว
LDR
แอลดีอาร์ (LDR) หรือชื่อเต็ม ๆ คือ Light Dependent Resistor หรือตัวต้านทานที่แปรค่าตามแสง คือ ตัวต้านทานชนิดที่เปลี่ยนสภาพความนำไฟฟ้า (Conductance) ได้เมื่อมีแสงมาตกกระทบ เซนเซอร์ โมดูลนี้ให้สัญญาณเอาต์พุตได้ ทั้งแบบแอนะล็อกที่ช่อง (A0) ซึ่งมีค่าระหว่าง 0 - 4095 และแบบดิจิทัลที่ช่อง (D0) ค่า 0 กับ 1 และจะต้องป้อนใช้ไฟเลี้ยง 3.3-5V ให้กับวงจร
โดยช่อง GPIO ที่จะใช้รับสัญญาณ analog ได้ของบอร์ด LOLIN D32 PRO มีดังนี้
| Analog Input Pins | 6 (VP, VN, 32, 33, 34, 35) |
|---|---|
| Analog Output Pins | 2 (25, 26) |
เราจะมาลองอ่านค่า LDR โดย LDR จะต้องใช้ร่วมกันกับตัวต้านทาน 10K โอห์ม
#include <Arduino.h>
#define LDR 32
void setup() {
Serial.begin(115200);
Serial.println("LDR");
}
void loop() {
Serial.println(analogRead(LDR));
delay(100);
}
เมื่อดู Serial Monitor จะเห็นว่าค่า LDR อยู่ในช่วง 0-4095 (12 bit) โดยหากเราต้องการให้ range ของค่า LDR เป็นค่าอื่นตามต้องการ เราสามารถใช้ฟังก์ชัน map() มาช่วยได้
เปลี่ยนแปลงโค้ดเก่าเล็กน้อย ดังนี้
#include <Arduino.h>
#define LDR 32
void setup() {
Serial.begin(115200);
Serial.println("LDR");
}
void loop() {
Serial.println(map(analogRead(LDR),0,4095,0,255));
delay(100);
}
โดยถ้าอยากสลับให้สว่างมาก ค่า x มากได้โดยการสลับค่า 0 กับ 255 ได้
และค่า 0 กับ 4095 ก็ควรเป็นค่าต่ำสุด สูงสุดที่สามารถอ่านได้ ณ ขณะที่ทำอยู่ เพราะเมื่อทำต่างสถานที่ ค่าของ LDR ก็จะเปลี่ยนแปลงตามสภาพของแสง ณ บริเวณนั้นๆ
PWM
PWM หมายถึง Pulse Width Modulation เป็นเทคนิคที่ใช้ในการควบคุมวงจร และ เขียนค่าแบบอะนาล๊อก (Analog) ด้วยพอร์ตดิจิตัล (Digital)
โดยปกติแล้ว พอร์ตดิจิตัล จะสามารถมีได้แค่ 2 สถานะ คือ HIGH (5 โวล์ท) กับ LOW (0 โวล์ท) เท่านั้น จึงทำให้สร้างค่าสัญญาณลอจิคได้เพียง เปิดหรือปิด (1 หรือ 0 , มีไฟหรือไม่มีไฟ) แค่นั้น ซึ่งการใช้เทคนิค PWM นั้น จะเป็นการทำให้พอร์ตดิจิตัล สามารถเขียนค่าได้มากกว่า HIGH หรือ LOW โดย ทำให้สามารถเขียนค่าเป็นแบบอะนาล๊อกได้ (อาจเป็น 0-255 หรือ 0-1023) โดยวิธีการนั้น จะใช้การปรับสถานะของสัญญาณลอจิค HIGH / LOW สลับกันไปมาด้วยคาบเวลาหนึ่งๆ โดยค่าที่ได้นั้นจะขึ้นอยู่กับ สัดส่วนเวลาของสัญญาณในช่วงเวลาที่มีสถานะเป็น HIGH กับช่วงเวลาที่เป็น LOW โดย ช่วงเวลาทั้งหมดที่สัญญาณมีสถานะเป็น HIGH นั้นเราจะเรียกว่าเป็น
"ความกว้าง Pulse (Pulse Width)"

ตัวอย่างการส่งสัญญาณ PWM เพื่อส่งค่า analog ขนาด 8 bit
โดยเราจะนำสัญญาณ PWM มาลองใช้ในการควบคุมความสว่างของหลอด LED
#include <Arduino.h>
#define GREEN 33
void setup()
{
ledcSetup(0, 5000, 8);
ledcAttachPin(GREEN, 0);
}
void loop()
{
for (int i = 0; i < 255; i++) {
ledcWrite(0, i);
delay(5);
}
for (int i = 255; i > 0; i--) {
ledcWrite(0, i);
delay(5);
}
delay(1000);
}
โค้ดดังกล่าวทำให้หลอด LED สีเขียวหรี่-ติด แบบไฟหิ่งห้อย
| รูปแบบ | คำอธิบาย |
|---|---|
ledcSetup(channel,freq,resolution); | ฟังก์ชั่นกำหนดค่าใน Timer -channel หมายเลขช่องของ Timer ใช้งานได้ 16 ช่องค่า 0-15 -freq ค่าความถี่ที่ใช้สร้างสัญญาณ PWM -resolution ค่าความละเอียดของ Duty cycle 1-16 bit เช่นถ้าใช้ 8 bit ค่า Duty cycle ที่กำหนดจะมีค่า 0-255 หมายถึง 0-100% |
ledcAttachPin(GPIO, channel); | ฟังก์ชั่นกำหนดขาพอร์ตที่ใช้งานกับช่องของ Timer -GPIO หมายเลขขาพอร์ตที่ใช้งาน -channel หมายเลขช่องของ Timer ที่เลือกใช้งานกับขาพอร์ตที่ระบุ |
ledcWrite(channel, dutycycle); | ฟังก์ชั่นสั่งการให้ Timer สร้างสัญญาณ PWM -channel หมายเลขช่องของ Timer ที่ต้องการสั่งการ -dutycycle ค่า Duty cycle ที่ต้องการให้ Timer สร้างขึ้น |
