“Arduino” เป็นแพลตฟอร์มที่ใช้ภาษา C / C++ ในการเขียนโปรแกรมสั่งงาน แต่การเขียนโปรแกรมในแพลตฟอร์ม Arduino นั้นจะง่าย และสามารถเข้าใจได้ นอกจากนี้ แพลตฟอร์มยังมีฟังก์ชั่นทำงานหลาย ๆ ที่อำนวยความสะดวกไว้ให้แล้ว เช่น ฟังก์ชั่นหน่วงเวลา ฟังก์ชั่นอินเตอร์รัพท์ ฟังก์ชั่นอ่านค่า GPIO ซึ่งในแพลตฟอร์มอื่น ๆ จะไม่มีการเตรียมฟังก์ชั่นไว้ให้ใช้งานได้สะดวกเท่ากับ Arduino
จากในบทที่แล้ว ที่ได้เปิดตัวอย่างของโค้ดโปรแกรมไฟกระพริบ (หรือ Blink) ขึ้นมา แล้วอัพโหลดลงบอร์ด เราก็ได้ทราบผลลัพธ์กันไปแล้ว ในบทนี้เราจะนำโค้ดที่ทำให้เกิดผลลัพธ์มาดูกันว่า โค้ด หรือคำสั่ง ฟังก์ชั่นที่ใช้แต่ละตัวคืออะไร เมื่อเราทราบผลลัพธ์อยู่แล้ว ก็จะทำให้เรานึกภาพตามได้ง่ายมากยิ่งขึ้น
โค้ดด้านล่างนี้ เป็นโค้ดตัวอย่างโปรแกรมไฟกระพริบ
ภาษา C/C++ และภาษาอื่น ๆ ที่ใช้เขียนโปรแกรมสั่งงานคอมพิวเตอร์ จะทำงานจากบนลงล่างเสมอ ดังนั้นหากต้องการวิเคราะห์โค้ดโปรแกรม จะต้องเริ่มจากบรรทัดแรกสุดเสมอ
บรรทัดที่ 1 – 21 เป็นส่วนของคอมเม้น หรือส่วนระบุข้อความ สามารถสังเกตได้จากการใช้เครื่องหมาย /* และ */ ในการเปิด-ปิดชุดข้อความ ข้อความที่อยู่ระหว่างเครื่องหมายคอมเม้น (เปิดด้วย /* และปิดด้วย */) จะไม่ถูกนำมาประมวลผลเป็นคำสั่งโปรแกรม และใช้เพื่ออธิบาย Note รายละเอียดของโค้ด ชื่อผู้พัฒนา หรืออื่น ๆ ในส่วนของคอมเม้นสามารถลบทิ้งได้ หรือไม่ต้องวิเคราะห์ได้ เพราะไม่ถูกนำมาประมวลผลเป็นคำสั่งโปรแกรมอยู่แล้ว
บรรทัดที่ 22 – 23 เป็นบรรทัดว่าง จึงไม่ต้องวิเคราะห์ การวิเคราะห์โค้ดโปรแกรม จะวิเคราะห์เฉพาะบรรทัดที่มีข้อความอยู่
บรรทัดที่ 24 เป็นส่วนของคอมเม้น โดยดูได้จากการใช้เครื่องหมาย // นำหน้าชุดข้อความ การใช้เครื่องหมาย // ในการคอมเม้น จะใช้ได้เฉพาะการคอมเม้นแบบ 1 บรรทัดเท่านั้น และถือว่าจบคอมเม้นเมื่อขึ้นบรรทัดใหม่ แต่หากหน้าเครื่องหมาย // มีข้อความ จะถือว่าข้อความที่อยู่หน้าเครื่องหมาย // เป็นคำสั่งโปรแกรม จำเป็นจะต้องนำมาวิเคราะห์ด้วย
บรรทัดที่ 25 เป็นการสร้างฟังก์ชั่น setup() ขึ้นมา โดยการสร้างฟังก์ชั่นในภาษา C/C++ จะมีรูปแบบดังนี้
ชนิดของข้อมูลตอบกลับ ชื่อฟังก์ชั่น(พารามิเตอร์) {
ชุดคำสั่ง 1;
ชุดคำสั่ง 2;
….
}
ชนิดของข้อมูลตอบกลับ หมายถึง ชื่อของชนิดข้อมูล ที่ต้องการตอบกลับไป หลังทำงานในฟังก์ชั่นเสร็จแล้ว ซึ่งชนิดของข้อมูลในแพลตฟอร์ม Arduino มีดังนี้
ชื่อชนิด | คำอธิบาย | ตัวอย่าง |
void | ใช้เฉพาะการสร้างฟังก์ชั่นที่ไม่มีการตอบกลับข้อมูล | ค่าว่าง |
bool | ข้อมูลเชิงตรรกะ มีแค่จริง (True) และเท็จ (False) | True |
char | ข้อมูลตัวอักษร 1 ตัว | ‘A’ |
unsigned char | ข้อมูลตัวอักษร 1 ตัว หรือตัวเลข 0 ถึง 255 | 255 |
byte | ข้อมูลขนาด 1 ไบต์ หรือตัวเลข 0 ถึง 255 | 200 |
int | ข้อมูลไม่เกิน 2 ไบต์ หรือตัวเลขจำนวนเต็ม -32,768 ถึง 32,767 | -200 |
unsigned int | ข้อมูลไม่เกิน 2 ไบต์ หรือตัวเลขจำนวนเต็ม 0 ถึง 65,535 | 1023 |
word | ข้อมูลไม่เกิน 2 ไบต์ หรือตัวเลขจำนวนเต็ม 0 ถึง 65,535 | 1000 |
long | ข้อมูลไม่เกิน 4 ไบต์ หรือตัวเลขจำนวนเต็ม -2,147,483,648 ถึง 2,147,483,647 | -1000000 |
unsigned long | ข้อมูลไม่เกิน 4 ไบต์ หรือตัวเลขจำนวนเต็ม 0 ถึง 4,294,967,295 | 1000000 |
short | ข้อมูลไม่เกิน 2 ไบต์ หรือตัวเลขจำนวนเต็ม -32,768 ถึง 32,767 | -900 |
float | ข้อมูลไม่เกิน 4 ไบต์ หรือตัวเลขทศนิยม 6 – 7 ตำแหน่ง | 100.587 |
double | ข้อมูลไม่เกิน 4 ไบต์ หรือตัวเลขทศนิยม 6 – 7 ตำแหน่ง | 98.2104 |
String | ข้อมูลที่เป็นชุดข้อความ | “Hello” |
ฟังก์ชั่นที่สร้างแล้วต้องการตอบกลับข้อมูลกลับไป เช่น ฟังก์ชั่นยกกำลัง ที่จะต้องตอบกลับข้อมูลผลลัพธ์กลับไป ผลลัพธ์อยู่ในรูปของตัวเลขที่อาจะเป็นทศนิยม จงควรใช้ชนิดข้อมูลตอบกลับเป็น float
ส่วนฟังก์ชั่นที่สร้างแล้วไม่ต้องการผลลัพธ์ เช่น ฟังก์ชั่นสั่งให้หลอดไฟติด จะใช้ชนิดข้อมูลตอบกลับเป็น void อย่างการสร้างฟังก์ชั่น setup() นี้ เป็นการสร้างฟังก์ชั่นที่ไม่ต้องการตอบข้อมูลผลลัพธ์ที่ได้กลับไป จึงใช้ชนิดข้อมูลตอบกลับเป็น void
ชื่อฟังก์ชั่น หมายถึง ชื่อที่จะตั้งให้กับฟังก์ชั่น ควรสอดคล้องกับการทำงานภายในฟังก์ชั่น เช่น ฟังก์ชั่นยกกำลัง ควรตั้งชื่อว่า pow ฟังก์ชั่นหาค่าสูงสุด ควรตั้งว่า max ฟังก์ชั่นเปิดไฟ ควรตั้งว่า light_on เป็นต้น
การตั้งชื่อฟังก์ชั่น มีข้อกำหนดดังนี้
( ) { } เป็นรูปแบบคงที่ เครื่องหมาย ( ) { } จะต้องอยู่ให้ถูกที่ตามรูปแบบ เพื่อให้คอมไพล์เลอร์แปลความหมายได้ถูกต้อง
พารามิเตอร์ หมายถึง ค่าที่จะส่งเข้าไปในฟังก์ชั่น เช่น ฟังก์ชั่นยกกำลัง จะต้องส่งเลขฐาน และเลขชี้กำลังเข้าไป เพื่อให้ฟังก์ชั่นสามารถคำนวณได้ ในการสร้างฟังก์ชั่นที่มีพารามิเตอร์นั้น จะต้องมีการกำหนดชนิดของข้อมูลที่ต้องการรับเข้ามา และชื่อของตัวแปรที่จะรับเข้ามาด้วย และค่าพารามิเตอร์สามารถมีได้ไม่จำกัด
ชุดคำสั่งต่าง ๆ ที่อยู่ภายใต้ปีกกาเปิด และปีกกาปิด จะถูกกระทำเมื่อมีการเรียกใช้ฟังก์ชั่นเท่านั้น การเรียกใช้ฟังก์ชั่น หากฟังก์ชั่นมีพารามิเตอร์ จำเป็นจะต้องระบุข้อมูลต่าง ๆ ลงไปให้ตรงตำแหน่งของพารามิเตอร์ด้วย
ฟังก์ชั่น setup() เป็นฟังก์ชั่นที่จำเป็นต้องมีในโค้ดโปรแกรม โดยชุดคำสั่งที่อยู่ในฟังก์ชั่น setup() จะถูกเรียกขึ้นมาเพียงครั้งเดียว นิยมนำฟังก์ชั่น หรือชุดคำสั่งที่ใช้กำหนดค่าเริ่มต้นมาใส่ไว้ เช่น กำหนดให้ขา GPIO ใด ๆ มีสถานะรอรับค่า เรียกฟังก์ชั่นกำหนดว่าจะเริ่มใช้จอแสดงผล เรียกฟังก์ชั่นกำหนดว่าจะใช้ Serial วางชุดคำสั่งที่ใช้เชื่อมต่อ WiFi เป็นต้น
บรรทัดที่ 26 เป็นคอมเม้น
บรรทัดที่ 27 เรียกใช้ฟังก์ชั่น pinMode() ซึ่งใช้สำหรับกำหนดค่าของขา GPIO ว่าต้องการให้รับค่าเข้ามา (เช่น รับค่าการกดสวิตซ์) หรือเขียนค่าออกไป (เช่น ทำให้หลอด LED ติด) มีรูปแบบการใช้งานดังนี้
void pinMode(int pin, int mode);
มีค่าพารามิเตอร์ดังนี้
และไม่มีการส่งค่าใด ๆ กลับมา (void)
ในแพลตฟอร์ม Arduino มีค่าคงที่ ๆ ชื่อ LED_BUILTIN ซึ่งทำหน้าที่เก็บหมายเลขขาที่ต่ออยู่กับหลอด LED บนบอร์ดต่าง ๆ เมื่อคอมไพล์เลอร์เริ่มแปล จะเปลี่ยนคำว่า LED_BUILTIN เป็นตัวเลขหมายเลขขาที่ต่ออยู่กับหลอด LED ให้อัตโนมัติ ซึ่งในบอร์ด NodeMCU-32S ตัวหลอด LED ต่ออยู่กับ GPIO2 เมื่อคอมไพล์เลอร์เริ่มแปล จะเปลี่ยนคำว่า LED_BUILTIN เป็นเลข 2 แทน
เมื่อย้อนกลับไปดูที่ 27 อีกครั้ง จะพบว่าได้มีการส่งค่าพารามิเตอร์เข้าไป 2 ตัว คือ LED_BUILTIN และ OUTPUT หมายความว่า กำหนดให้ขา GPIO ที่ต่ออยู่กับหลอด LED บนบอร์ด มีสถานะเป็นเขียนค่าออกไปนั่นเอง
สังเกตว่า หลังการเรียกใช้ฟังก์ชั่น จะมีเครื่องหมาย ; ปิดเสมอ ซึ่งเครื่องหมายนี้จำเป็นจะต้องมีหลังเรียกใช้ฟังก์ชั่นเสมอ
หลังเครื่องหมาย ; จะเป็นคอมเม้น ไม่ต้องวิเคราะห์
บรรทัดที่ 28 เครื่องหมายปีกกาปิด ใช้บอกคอมไพล์เลอร์ว่า หมดชุดคำสั่งที่จะนำไปไว้ในฟังก์ชั่น setup() แล้ว
บรรทัดที่ 29 – 30 ไม่นำมาวิเคราะห์
บรรทัดที่ 31 เป็นการสร้างฟังก์ชั่น loop() ชุดคำสั่งที่อยู่ในฟังก์ชั่น loop() จะถูกเรียกขึ้นมาใช้งานตลอดเวลา และจะเริ่มทำงานหลังฟังก์ชั่น setup() เมื่อฟังก์ชั่น setup() ทำงานแล้ว คำสั่งแรก จนถึงคำสั่งสุดท้ายในฟังก์ชั่น loop() จะทำงานตามลำดับจากบนลงล่างไปจนจบ แล้วเริ่มกลับมาทำคำสั่งแรกถึงคำสั่งสุดท้ายอีกครั้ง วนไปแบบนี้ไม่มีวันจบ
บรรทัดที่ 32 เรียกใช้ฟังก์ชั่น digitalWrite() ซึ่งทำหน้าที่เขียนค่าแบบดิจิตอลออกไปที่ขา GPIO ซึ่งฟังก์ชั่น digitalWrite() มีรูปแบบการใช้งานดังนี้
void digitalWrite(int pin, int value);
มีค่าพารามิเตอร์ดังนี้
และไม่มีการส่งค่ากลับ (void)
บรรทัดที่ 32 ได้มีการส่งค่า LED_BUILTIN เป็นหมายเลขขา และส่งค่า HIGH ตามลำดับ จึงหมายความว่า สั่งให้ขา GPIO ที่ต่อกับหลอด LED บนบอร์ดมีสถานะเป็น HIGH หรืออาจจะอธิบายว่า บรรทัด 32 สั่งให้หลอด LED ติด
บรรทัดที่ 33 เรียกใช้ฟังก์ชั่น delay() ซึ่งทำหน้าที่หน่วงเวลา มีค่าพารามิเตอร์ตัวเดียว ใช้รับค่าเวลาที่ต้องการหน่วง โดยค่าเวลานั้น อยู่ในหน่วยมิลิวินาที หมายความว่า หากใส่ไว้ 1000 จะหมายถึง หน่วงเวลา 1 วินาที ดังนั้นในบรรทัดที่ 33 จึงอธิบายได้ว่า เป็นการสั่งให้หยุดอยู่เฉย ๆ หลังสั่งให้หลอด LED ติด เป็นเวลา 1 วินาที
บรรทัดที่ 34 เรียกใช้ฟังก์ชั่น digitalWrite() โดยค่าพารามิเตอร์ตัวแรกเป็น LED_BUILTIN แต่พารามิเตอร์ตัวที่สอง ได้ส่ง LOW ออกไป หมายความว่า สั่งให้ขาหมายเลข LED_BUILTIN มีสถานะเป็น LOW หรืออาจจะอธิบายว่า บรรทัด 34 สั่งให้หลอด LED ดับ
บรรทัดที่ 35 หน่วงเวลา 1 วินาที
บรรทัดที่ 36 เครื่องหมายปีกกาปิด บอกว่าจบคำสั่งในฟังก์ชั่น loop()
ผลที่ได้คือ หลอดไฟมีการติด 1 วินาที และดับ 1 วินาที หรือพริบ ไม่มีที่สิ้นสุดนั่นเอง
ในตัวอย่างนี้ เป็นตัวอย่างของการใช้คำสั่งอ่านค่าสถานะขา GPIO แล้วนำมาแสดงผลผ่าน Serial Moniter นอกจากตัวอย่างนี้จะใช้เพื่อเรียนรู้คำสั่งอ่านค่าต่าง ๆ แล้ว ยังใช้เรียนรู้คำสั่งแสดงผลผ่าน Serial Monitor ด้วย ซึ่งการใช้ Serial Monitor จะช่วยให้สามารถดีบัค และหาข้อผิดพลาดของโปรแกรมได้ง่าย
บนบอร์ด NodeMCU-32S มีสวิตซ์กดติดปล่อยดับอยู่ 2 ตัว หนึ่งในนั้น คือสวิตซ์ IO0 ที่ต่ออยู่กับขา GPIO0 ซึ่งสามารถนำมาใช้ในการทดลองนี้ได้ โดยไม่ต้องต่อวงจรใด ๆ เพิ่มเติม
ก่อนอื่น ให้กดไปที่เมนู File > Examples > 01.Basics เลือก DigitalReadSerial
ในบรรทัดที่ 9 ตัวแปร pushButton ให้แก้ไขเป็นเลข 0
จากนั้นกดอัพโหลดลงบอร์ด NodeMCU-32S ได้เลย
กดไปที่ไอค่อน เพื่อเปิด Serial Monitor ขึ้นมา แล้วจะพบเลข 1 ขึ้นมาจำนวนมาก
เมื่อกดสวิตซ์ IO0 บนบอร์ด NodeMCU-32S ค้างไว้ จะขึ้นเลข 0 ดังรูป
จะเห็นว่า เราสามารถอ่านค่าการกดสวิตซ์ได้แล้ว โดยตัวเลขที่แสดงผลหมายถึงสถานะทางลอจิกที่อ่านได้จากขา GPIO0 นั่นเอง เมื่อพิจารณาจากผลการทดลองจะพบว่า สวิตซ์ IO0 บนบอร์ด NodeMCU-32S ทำงานแบบ Active LOW หรือต่อวงจรไว้แบบ Pull-up
ต่อมา เรามาดูกันว่าโค้ดที่ใช้ในการทดลองนี้ ใช้ฟังก์ชั่น คำสั่งอะไร จึงได้ผลการทดลองเช่นนี้ โดยโค้ดด้านล่างนี้ เป็นโค้ดที่ยกมาจากในโปรแกรม Arduino IDE หลังจากแก้ไขตัวแปร pushButton เรียบร้อยแล้ว
บรรทัดที่ 1 – 8 เป็นคอมเม้น
บรรทัดที่ 9 ประกาศตัวแปรชื่อ pushButton โดยมีชนิดข้อมูลเป็น int กำหนดให้มีค่าเป็น 0 เมื่อพิจารณาจากชื่อ และการนำไปใช้ในบรรทัดถัด ๆ ไป จะพบว่าตัวแปร pushButton สร้างขึ้นมาเพื่อใช้เก็บหมายเลขขา GPIO ที่ต่ออยู่กับสวิตซ์
ตัวแปร หมายถึง สิ่งที่ไม่ทราบค่า สามารถมีค่า หรือไม่มีค่าก็ได้ และสามารถเปลี่ยนค่าได้ตลอดเวลา ในทางโปรแกรมมิ่ง นิยมใช้ในการเก็บค่าบางอย่างไว้ เพื่อรอการนำไปใช้งานในบรรทัดถัด ๆ ไป
การประกาศตัวแปร สามารถทำได้โดยใช้รูปแบบ
ชนิดของข้อมูล ชื่อตัวแปร; หรือ ชนิดของข้อมูล ชื่อตัวแปร = ค่าที่ต้องการจะเก็บ;
ข้อกำหนดของการตั้งชื่อตัวแปร จะเหมือนกับข้อกำหนดของการตั้งชื่อฟังก์ชั่น ส่วนการกำหนดค่าในตัวแปรภายหลังประกาศตัวแปรไปแล้ว สามารถกำหนดได้เลยโดยไม่ต้องระบุชนิดตัวแปรอีก ตามรูปแบบ
ชื่อตัวแปร = ค่าที่ต้องการจะเก็บ;
เช่น pushButton = 6;
บรรทัดที่ 10 – 11 ไม่นำมาวิเคราะห์
บรรทัดที่ 12 สร้างฟังก์ชั่น setup()
บรรทัดที่ 13 เป็นคอมเม้น
บรรทัดที่ 14 เรียกใช้ฟังก์ชั่น Serial.begin() ซึ่งฟังก์ชั่นนี้จะใช้กำหนดว่าจะเริ่มการสื่อสารผ่าน Serial แล้ว หรืออาจอธิบายได้ว่า เป็นการบอกว่าจะเริ่มใช้การดีบัคผ่าน Serial Monitor แล้ว โดยฟังก์ชั่น Serial.begin() มีค่าพารามิเตอร์อยู่ตัวเดียวคือความเร็วในการสื่อสาร ซึ่งสามารถกำหนดได้ดังนี้ 300 600 1200 2400 4800 9600 14400 19200 28800 38400 57600 หรือ 115200 ทั้งนี้ตัวเลขที่มาก จะหมายถึงการรับ – ส่งข้อมูลกับ Serial Monitor จะเร็วมากขึ้นนั่นเอง
บรรทัดที่ 15 เป็นคอมเม้น
บรรทัดที่ 16 เรียกใช้ฟังก์ชั่น pinMode() กำหนดให้หมายเลขขาที่อยู่ในตัวแปร pushButton มีสถานะเป็นรอรับค่าเข้ามา ซึ่งในบรรทัดที่ 9 ได้มีการกำหนดให้ตัวแปร pushButton มีค่าเป็น 0 ดังนั้นในบรรทัดนี้จึงเป็นการกำหนดให้ขา GPIO0 มีสถานะเป็นรอรับค่าเข้ามา
บรรทัดที่ 17 จบคำสั่งในฟังก์ชั่น setup()
บรรทัดที่ 18 – 19 ไม่นำมาวิเคราะห์
บรรทัดที่ 20 สร้างฟังก์ชั่น loop() ขึ้นมา (คำสั่งที่อยู่ภายในตั้งฟังก์ชั่นนี้จะวนทำงานไม่รู้จบ)
บรรทัดที่ 21 เป็นคอมเม้น
บรรทัดที่ 22 ประกาศตัวแปร เป็นชนิด int แล้วนำค่าที่ได้จากในฟังก์ชั่น digitalRead() มาเก็บไว้
เมื่อพบการใช้งานฟังก์ชั่นร่วมกับตัวแปรในบรรทัดเดียวกันแบบนี้ ให้วิเคราะห์ว่าโปรแกรมจะทำงานทางขวาก่อน เนื่องจากเครื่องหมาย = จะหมายถึงให้โปรแกรมทำงานทางขวา หรือนำค่าทางขวามาเก็บไว้ในชื่อตัวแปรทางซ้าย
ฟังก์ชั่น digitalRead() เป็นฟังก์ชันที่ใช้สำหรับอ่านค่าสถานะที่ขา GPIO จะตอบกลับมาในรูปของ int เป็นตัวเลข 0 หรือ 1 หรือ HIGH หรือ LOW โดยมีค่าพารามิเตอร์ตัวเดียวคือหมายเลขขา เมื่อดูจากพารามิเตอร์ที่ลงไว้ในโค้ดจะพบว่ามีการใส่ตัวแปร pushButton ไว้ในพารามิเตอร์ ย้อนกลับไปพิจารณาบรรทัดที่ 9 ที่กำหนดให้ pushButton = 0 ไว้ เมื่อนำค่าที่อยู่ในตัวแปร pushButton มาแทนลงในพารามิเตอร์ของฟังก์ชั่นจะได้ว่า int buttonState = digitalRead(0); ต่อมาคือค่าที่ได้จากฟังก์ชั่น digitalRead(0) จะเป็น 0 หรือ 1 ก็ได้ และสมมุติว่าขณะนี้อ่านค่าจาก GPIO ได้สถานะลอจิก 1 จึงแทนคำว่า digitalRead(0) ด้วยเลข 1 จะได้รูปแบบออกมาเป็น int buttonState = 1; ซึ่งจะไปตรงกับรูปแบบของการประกาศตัวแปรแบบกำหนดค่า ทำให้ตัวแปร buttonState สามารถเก็บค่าที่อ่านได้จากขา GPIO ไว้ได้นั่นเอง
บรรทัดที่ 23 เป็นคอมเม้น
บรรทัดที่ 24 เรียกใช้ฟังก์ชั่น Serial.println() ใช้ส่งข้อความ ค่าในตัวแปร ออกไปทาง Serial แบบเว้นบรรทัดให้อัตโนมัติ หรืออาจจะอธิบายได้ว่า เป็นคำสั่งที่ใช้ส่งสิ่งที่ต้องการให้แสดงผล ไปแสดงในหน้าต่าง Serial Monitor มีพารามิเตอร์ตัวเดียว รองรับข้อมูลทุกชนิด การใช้งานสามารถใช้ทั้งการวางค่าที่ต้องการให้แสดงลงไปตรง ๆ เช่น Serial.println(“Hello”); หรือให้แสดงผลค่าในตัวแปร เช่น Serial.println(buttonState);
ดังนั้นในบรรทัดนี้จึงอธิบายได้ว่า เป็นบรรทัดที่ใช้แสดงผลค่าสถานะที่อ่านจากขา GPIO โดยใช้ตัวแปร buttonState มาแสดงผลนั่นเอง
บรรทัดที่ 25 หน่วงเวลา 1 มิลิวินาที
บรรทัดที่ 26 ใช้เครื่องหมาย } เพื่อสิ้นสุดคำสั่งในฟังก์ชั่น loop()
ผลที่ได้คือ ในหน้าต่าง Serial Monitor มีการแสดงผลการอ่านค่า GPIO แบบต่อเนื่องไม่มีสิ้นสุดนั่นเอง
ตัวอย่างโค้ดอื่น ๆ ยังมีอีกมาก ท่านผู้อ่านสามารถเข้าไปลองเล่น ลองอัพโหลดแต่ละตัวอย่างได้ ซึ่งจะทำให้สามารถเรียนรู้พื้นฐานของแพลตฟอร์ม Arduino ได้เร็วมากขึ้น
ในชุดการเรียนรู้ด้วยตัวอย่างได้มีการอธิบายพื้นฐานของแพลตฟอร์ม Arduino ที่จะมีเฉพาะแพลตฟอร์ม Arduino เท่านั้นที่เขียนโปรแกรมได้แบบนี้ ซึ่งจุดประสงค์ของการเรียนรู้ด้วยตัวอย่างคือการศึกษาฟังก์ชั่น setup() และ loop() เพื่อให้เข้าใจว่าโค้ดโปรแกรมมีขั้นตอนการทำงานอย่างไร
ในหัวข้อพื้นฐานภาษา C/C++ นี้ จะนำโครงสร้างพื้นฐานของภาษา C/C++ มาอธิบาย เพื่อให้สามารถต่อยอดการเขียนโปรแกรมไปยังรูปแบบอื่น ๆ ได้ เช่นการเขียนโปรแกรมให้ทำงานตามเงื่อนไข การแยกชุดคำสั่งที่ทำงานบ่อย ๆ การประหยัดเมมโมรี่ และทฤษฎีโครงสร้างภาษา C/C++
ภาษา C/C++ หากจะอ้างอิงตามจริงแล้ว จะมีความแตกต่างจากโครงสร้างในแพลตฟอร์ม Arduino เล็กน้อย แต่เนื่องจากในชุดบทความนี้ใช้แพลตฟอร์ม Arduino จึงเลือกนำโครงสร้างของ Arduino ขึ้นมาอธิบายแทน ดังนี้
จะเห็นได้ว่าโครงสร้างของการเขียนโปรแกรมบนแพลตฟอร์ม Arduino จะแบ่งส่วนได้เป็น 3 ส่วนด้วยกัน
ส่วนหัว คือ ส่วนที่จะประกอบไปด้วยคำสั่งพรีโปรเซสเซอร์ (Preprocesssor) เป็นหลัก ชึ่งคำสั่งพรีโปรเซสเซอร์จะมีด้วยกันอยู่ 2 ตัว ดังนี้
นอกจากนี้ส่วนหัวยังเป็นส่วนที่ใช้ประกาศค่าคงที่ต่าง ๆ และตัวแปรแบบโกลบอล (Global Variable) ซึ่งเป็นตัวแปรที่มีขอบเขตการใช้งานอยู่สูงที่สุด ทุกฟังก์ชั่นสามารถเรียกใช้งานได้ ตัวแปรแบบโกลบอลนิยมประกาศเพื่อที่จะนำตัวแปรไปใช้งานในฟังก์ชั่น setup, loop และฟังก์ชั่นย่อยอื่น ๆ ต่อไป
ฟังก์ชั่น setup คือ ส่วนที่ใช้วางคำสั่งโปรแกรมขณะเริ่มต้น นิยมใช้ในการกำหนดค่าต่าง ๆ ก่อนใช้งาน เช่น ใช้ฟังก์ชั่น pinMode() กำหนดสถานะของขา GPIO ใช้คำสั่ง Serial.begin() เพื่อบอกว่าจะเริ่มใช้งาน Serial
ฟังก์ชั่น loop คือ ส่วนที่ใช้วางคำสั่งโปรแกรมที่จะถูกเรียกทำงานซ้ำ ๆ เช่น ใส่ชุดคำสั่งโปรแกรมไฟกระพริบ ใส่ชุดคำสั่งอ่านค่าสวิตซ์ ซึ่งผลที่ได้คือคำสั่งเหล่านั้นจะมีการวนกลับมาทำงานเรื่อย ๆ ไม่มีที่สิ้นสุด
คอมเม้น คือชุดข้อความที่ไม่ใช่โค้ดโปรแกรม และไม่ถูกนำมาประมวลผล ใช้เพื่ออธิบายการทำงานของโปรแกรม หรือบันทึกข้อความต่าง ๆ ไว้ การใช้งานคอมเม้นจะมี 2 รูปแบบ คือ
จะใช้เครื่องหมาย { (ปีกกาเปิด) และเครื่องหมาย } (ปีกกาปิด) ในการครอบชุดคำสั่งทั้งหมดที่ต้องการให้อยู่ภายใต้คำสั่งนั้น หรือฟังก์ชั่นนั้น ๆ
ทุกบรรทัดที่มีการใช้งานฟังก์ชั่น คำสั่งต่าง ๆ จะต้องมีเครื่องหมาย ; (Semicoln) ปิดท้ายบรรทัดเสมอ มิฉะนั้นจะคอมไพล์โปรแกรมไม่ผ่าน
ตัวแปร คือพื้นที่จัดเก็บข้อมูลชั่วคราว อยู่ในหน่วยความจำแรม เมื่อมีการหยุดจ่ายไฟ หรือรีเซ็ต ก็จะทำให้ค่าเหล่านั้นหายไป การใช้งานตัวแปรจะต้องมีการประกาศตัวแปรก่อน การประกาศตัวแปรจะแบ่งตามขอบเขตการใช้งานได้เป็น 2 แบบ คือ
การใช้งานตัวแปรจะขึ้นอยู่กับชนิดข้อมูลที่เก็บ และขนาดข้อมูลที่จะเก็บ เช่น หากต้องการจะเก็บข้อมูลที่เป็นตัวเลขจำนวนเต็ม สามารถเลือกใช้ได้ทั้ง unsigned int, int, unsigned long, long ทั้งนี้ต้องดูด้วยว่าข้อมูลตัวเลขที่จะเก็บมีจำนวนติดลบหรือไม่ หากไม่มีสามารถใช้ unsigned int, unsigned long ได้ หากตัวเลขที่จะเก็บมีค่ามากกว่า 65,535 หรือมากกว่า 2 ไบต์ จะต้องใช้ unsigned long เท่านั้น แต่หากข้อมูลไม่เกิน 65,535 หรือน้อยกว่า 2 ไบต์แต่ใช้ unsigned long จะทำให้สิ้นเปลืองแรม
การประกาศตัวแปร และใช้งานตัวแปร สามารถย้อนกลับไปดูได้ที่หัวข้อ เรียนรู้จากตัวอย่างโปรแกรมไฟกระพริบ
ค่าคงที่คือค่าที่ไม่สามารถเปลี่ยนแปลงได้ แตกต่างจากตัวแปรที่สามารถเปลี่ยนแปลงได้ตลอดเวลา ข้อดีของการใช้ค่าคงที่คือ ค่าที่ใส่ไว้จะถูกนำไปเก็บไว้บนรอมที่มีพื้นที่มากกว่าแรม ซึ่งการใช้ค่าคงที่จะทำให้ประหยัดการใช้พื้นที่แรมได้
รูปแบบการใช้งานของค่าคงที่ มีดังนี้
const ชนิดของข้อมูล ชื่อค่าคงที่ = ค่าที่ต้องการจัดเก็บ;
อาร์เรย์ คือการเก็บชุดข้อมูลที่เป็นชนิดเดียวกันไว้ในตัวแปรเดียวกัน เช่น หากเราต้องการเก็บค่าอายุของคน 10 คน แทนที่เราจะใช้การสร้างตัวแปร age1 age 2 age… ก็เปลี่ยนมาใช้ตัวแปรอาร์เรย์แทน ตัวแปรแบบอาร์เรย์มีคุณสมบัติที่สำคัญอยู่ 3 ประการ คือ
นอกจากนี้ในภาษา C/C++ จะใช้งานอาร์เรย์จำเป็นต้องมีการจองพื้นที่แรมก่อนใช้งาน มีรูปแบบการประกาศใช้งาน ดังนี้
ชนิดข้อมูล ชื่อตัวแปร[] = { ชุดข้อมูล }; หรือ ชนิดข้อมูล ชื่อตัวแปร[ขนาดของข้อมูล];
ให้สังเกตว่า การสร้างตัวแปรแบบอาร์เรย์จะแตกต่างจากการสร้างตัวแปรแบบปกติตรงที่การสร้างจะต้องมีเครื่องหมาย [ และ ] ต่อท้ายชื่อตัวแปรด้วย และการกำหนดข้อมูล จะใช้เครื่องหมาย { และ } ในการครอบชุดข้อมูล โดยข้อมูลต่อละชุดจะถูกคั่นด้วยเครื่องหมาย , (ลูกน้ำ)
หรือถ้าต้องการสร้างตัวแปรอาร์เรย์ที่ต้องการกำหนดค่าที่หลัง จะต้องมีการกำหนดขนาดของอาร์เรย์ด้วย โดยนำค่าของขนาด หรือจำนวนข้อมูลที่ต้องการเก็บไว้ในระหว่างเครื่องหมาย [ และ ]
การดึงข้อมูลออกมาจากอาร์เรย์จะใช้ดัชนี (index) ในการชี้ตำแหน่ง โดยใช้รูปแบบดังนี้
ชื่อตัวแปร[ดัชนี]
นอกจากนี้ หากต้องการเปลี่ยนแปลงค่าในอาร์เรย์ สามารถทำได้แบบเดียวกับการเปลี่ยนค่าตัวแปร แต่ต้องมีการชี้ดัชนีที่ต้องเปลี่ยนด้วย
ชื่อตัวแปร[ดัชนี] = ข้อมูลใหม่;
ตัวดำเนินการ (Operators) คือ การใช้สัญลักษณ์บางอย่างเพื่อกระทำกับข้อมูลแล้วทำให้เกิดผลลัพธ์ขึ้นมา เป็นไปตามชนิดข้อมูล และตัวดำเนินการที่ใช้อยู่
ตัวดำเนินการเปรียบเทียบ (Relational Operators) เมื่อใช้ตัวดำเนินการในกลุ่มนี้ จะให้ผลลัพธ์ออกมาในรูปจริง (True) หรือเท็จ (False) เท่านั้น
เครื่องหมาย | ความหมาย | ตัวอย่าง (กำหนด x = 1) |
> | มากกว่า | x > 5 // ผลลัพธ์ False |
< | น้อยกว่า | x < 5 // ผลลัพธ์ True |
== | เท่ากับ | x == 1 // ผลลัพธ์ True |
!= | ไม่เท่ากับ | x != 6 // ผลลัพธ์ True |
>= | มากกว่าหรือเท่ากับ | x >= 1 // ผลลัพธ์ True |
<= | น้อยกว่าหรือเท่ากับ | x <= 6 // ผลลัพธ์ True |
ตัวดำเนินการทางคณิตศาสตร์ (Arithmetic Operators) เป็นเครื่องหมายที่ใช้ในการคำนวณทางคณิตศาสตร์
เครื่องหมาย | ความหมาย | ตัวอย่าง (กำหนด x = 1) |
+ | บวก | x + 5 // ผลลัพธ์ 6 |
– | ลบ | x – 5 // ผลลัพธ์ 4 |
* | คูณ | x * 10 // ผลลัพธ์ 10 |
/ | หาร | x / 2 // ผลลัพธ์ 0.5 |
% | หารเอาเศษ | x % 2 // ผลลัพธ์ 1 |
++ | เพิ่มค่าขึ้นหนึ่ง | x++ // ผลลัพธ์ 2 |
— | ลดค่าลงหนึ่ง | x– // ผลลัพธ์ 0 |
ตัวดำเนินการทางลอจิก (Logical Operators) ใช้ดำเนินการทางลอจิกของผลลัพธ์ของเงื่อนไขการเปรียบเทียบตั้งแต่ 1 เงื่อนไขขึ้นไป จะให้ผลลัพธ์ออกมาเป็นจริง (True) หรือเท็จ (False)
เครื่องหมาย | ความหมาย | ตัวอย่าง (กำหนด x = 1) |
&& | AND | x > 5 && x == 1 // ผลลัพธ์ False |
|| | OR | x > 5 || x != 2 // ผลลัพธ์ True |
! | NOT | !True // ผลลัพธ์ False |
ตัวดำเนินการระดับบิต (Bitwise Operators) ใช้ดำเนินการเปรียบเทียบตามตำแหน่งบิต ผลลัพธ์ที่ได้จะอยู่จะอยู่ในรูปของบิตข้อมูล
เครื่องหมาย | ความหมาย | ตัวอย่าง (กำหนด x = B01001011) |
& | AND | x & B00000001 // ผลลัพธ์ B00000001 |
| | OR | x | B10000000 // ผลลัพธ์ B11001011 |
^ | XOR | x ^ B00100000 // ผลลัพธ์ B01101011 |
~ | คอมพลีเมนท์ (Complement) | ~x // ผลลัพธ์ B10110100 |
<< | เลื่อนบิตไปทางซ้าย | x << 4 // ผลลัพธ์ B10110000 |
>> | เลื่อนบิตไปทางขวา | x >> 4 // ผลลัพธ์ B00000100 |
การตรวจสอบเงื่อนไข คือ การเปรียบเทียบค่าต่าง ๆ ที่หากค่าที่เปรียบเทียบเป็นจริง หรือเป็นเท็จ จะทำให้มีการทำงานของโปรแกรมที่เปลี่ยนไป หรือยกตัวอย่างเรื่องของการจ่ายเงิน สมมุติให้ค่าสินค้า / บริการคือ 300 บาท เงื่อนไขคือ จะจ่ายด้วยธนบัตรอะไร ถ้ามีธนบัตร 100 บาทครบ 3 ใบ ให้จ่ายด้วยธนบัตร 100 จำนวน 3 ใบ แต่หากมีเฉพาะธนบัตรที่มากกว่า 100 บาท ให้จ่ายด้วยธนบัตรนั้น จากตัวอย่างจะเห็นว่าเงื่อนไขคือธนบัตรที่มี เงื่อนไขแรกตรวจสอบแล้วเป็นจริง (มีธนบัตร 100 บาทครบ 3 ใบ) จึงจะทำงานแบบหนึ่ง (จ่ายด้วยธนบัตร 100 จำนวน 3 ใบ) แต่หากว่าเงื่อนไขแรกไม่เป็นจริง (มีเฉพาะธนบัตรที่มากกว่า 100 บาท) ก็จะทำงานอีกแบบหนึ่ง (จ่ายด้วยธนบัตรนั้น)
คำสั่งที่เกี่ยวข้องกับการทำงานแบบมีเงื่อนไข มีดังนี้
if – เป็นการตรวจสอบเงื่อนไขแล้วเลือกทำงาน หรือไม่ทำงาน คือหากเป็นจริง (True) จะเข้าไปทำงานในปีก-กา หากเป็นเท็จ จะปล่อยผ่านไปทำในบรรทัดหลังจุดสิ้นสุด
รูปแบบการใช้งาน
if (เงื่อนไข) {
ชุดคำสั่ง 1;
ชุดคำสั่ง 2; หรือ if (เงื่อนไข) ชุดคำสั่ง 1;
ชุดคำสั่ง …;
}
เมื่อเงื่อนไข อาจใช้เครื่องหมายตัวดำเนินการเปรียบเทียบในการตรวจสอบแล้วให้ค่า True , False หรือจะใช้การวางตัวแปรแบบ bool เพื่อเลือกที่จะให้ทำหรือไม่ทำเลยก็ได้
if-else – เป็นการตรวจสอบเงื่อนไข แล้วเลือกที่จะทำงานไปในทางใดทางหนึ่ง หากเป็นจริง (True) จะเข้าไปทำงานในวงเล็บของ if แต่หากเป็นเท็จ (False) จะเข้าไปทำงานในวงเล็บของ else
รูปแบบการใช้งาน
if (เงื่อนไข) {
ชุดคำสั่ง 1;
ชุดคำสั่ง 2; หรือ if (เงื่อนไข) ชุดคำสั่ง 1;
ชุดคำสั่ง …; else ชุดคำสั่ง 1;
} else {
ชุดคำสั่ง 1;
ชุดคำสั่ง 2;
ชุดคำสั่ง …;
}
if-else if – เป็นการตรวจสอบเงื่อนไข แล้วเข้าไปทำงานในเงื่อนไขที่เป็นจริง
รูปแบบการใช้งาน
if (เงื่อนไข) {
ชุดคำสั่ง 1;
ชุดคำสั่ง 2; หรือ if (เงื่อนไข) ชุดคำสั่ง 1;
ชุดคำสั่ง …; else if (เงื่อนไข 2) ชุดคำสั่ง 1;
} else if (เงื่อนไข 2) { else if (เงื่อนไข 3) ชุดคำสั่ง 1;
ชุดคำสั่ง 1; else ชุดคำสั่ง 1;
ชุดคำสั่ง 2;
ชุดคำสั่ง …;
} else if (เงื่อนไข 3) {
ชุดคำสั่ง 1;
ชุดคำสั่ง 2;
ชุดคำสั่ง …;
} else {
ชุดคำสั่ง 1;
ชุดคำสั่ง 2;
ชุดคำสั่ง …;
}
ทั้งนี้รูปแบบการใช้งานจะสามารถปรับเปลี่ยนได้ โดยสามารถเพิ่ม else if (เงื่อนไข) ได้ไม่จำกัด และไม่จำเป็นต้องมี else { } ก็ได้ แต่หากจะให้มี else { } จะต้องเอาไว้ท้ายสุดเสมอ
Ternary Operators – เป็นคำสั่งเพิ่มเติมที่จะมาแนะนำให้รู้จัก ตัว Ternary Operators นิยมใช้งานร่วมกับการเซ็ตค่าตัวแปรที่มีเงื่อนไข เช่น โค้ดเดิมคือ
if (A > 5) x = 10;
else x = 5;
จะเห็นได้ว่า ไม่ว่าเงื่อนไขจะเป็นจริง หรือเท็จ ก็จะมีการเซ็ตค่าให้กับตัวแปร x เสมอ ด้วยเงื่อนไขแบบนี้ เราสามารถเลี่ยงไปใช้ Ternary Operators ได้
รูปแบบการใช้งาน
ตัวแปร = (เงื่อนไข) ? ค่าที่ต้องการเซ็ตหากเป็นจริง : ค่าที่ต้องการเซ็ตหากเป็นเท็จ;
ดังนั้นจากตัวอย่างที่แล้ว ทำให้เราสามารถใช้ Ternary Operators ซึ่งจะได้โค้ดออกมาเป็น
x = (A > 5) ? 10 : 5;
ซึ่งจะเห็นว่าโค้ดสั้นกว่ามาก แต่ก็ต้องแลกมาด้วยการที่โค้ดทำความเข้าใจได้ยากขึ้น
ในภาษา C/C++ จะมีคำสั่งทำงานวนรอบอยู่หลายตัว แต่ที่จะมาแนะนำจะยกมาเฉพาะตัวที่มีการใช้บ่อย ๆ เท่านั้น
while – เป็นคำสั่งใช้วนรอบทำงานแบบมีเงื่อนไข ตราบใดที่เงื่อนไขยังเป็นจริง โดยจะมีการวนกลับมาตรวจสอบเงื่อนไขทุกครั้งที่สิ้นสุดลูป จะมีการวนรอบอยู่อย่างนั้นไปเรื่อย ๆ หรืออาจอธิบายได้ว่า คำสั่ง while ใช้วนรอบเมื่อเงื่อนไขเป็นจริง
รูปแบบการใช้งาน
while(เงื่อนไข) {
ชุดคำสั่ง 1;
ชุดคำสั่ง 2; หรือ while(เงื่อนไข) ชุดคำสั่ง 1;
ชุดคำสั่ง …;
}
คำสั่ง while บางครั้งมีการกำหนดเงื่อนไขเป็น 1 หรือ True เพื่อให้วนรอบทำงานแบบไม่มีที่สิ้นสุด
for – เป็นคำสั่งที่ใช้วนรอบทำคำสั่งแบบมีจำนวนรอบที่แน่นอน
รูปแบบการใช้งาน
for(ชนิดข้อมูล ชื่อตัวแปร=ค่าเริ่มต้น;ชื่อตัวแปร[ตัวดำเนินการเปรียบเทียบ]ค่าสิ้นสุด;ชื่อตัวแปร[++ หรือ –]) {
ชุดคำสั่ง 1;
ชุดคำสั่ง 2;
ชุดคำสั่ง …;
}
หรือ
for(ชนิดข้อมูล ชื่อตัวแปร=ค่าเริ่มต้น;ชื่อตัวแปร[ตัวดำเนินการเปรียบเทียบ]ค่าสิ้นสุด;ชื่อตัวแปร[++ หรือ –]) ชุดคำสั่ง 1;
ตัวอย่างโปรแกรมวนรอบ 0 – 9
ผลลัพธ์
ตัวอย่างโปรแกรมวนรอบ 9 – 0
ผลลัพธ์
จากตัวอย่าง ให้สังเกตตัวดำเนินการเปรียบเทียบ และ ++ — ในตอนท้าย ที่จะใช้กำหนดว่าจะเริ่มจากตัวเลขน้อยไปมาก หรือมากไปน้อย ในขณะที่การกำหนดค่าเริ่มต้น และค่าสิ้นสุดยังกำหนดที่จุดเดิม
คำสั่งต่อไปนี้ เป็นคำสั่งที่ใช้ในการควบคุมการวนรอบที่เกิดจากคำสั่ง while และคำสั่ง for มักใช้ร่วมกับ if เพื่อกำหนดเงื่อนไขในการควบคุมการวนรอบ
continue – เป็นคำสั่งที่ใช้ข้ามลูปรอบนี้ไป โดยคำสั่งที่อยู่ใต้บรรทัด continue และอยู่ในปีกกาเดียวกันจะไม่ถูกทำต่อ และจะเริ่มไปทำงานในรอบใหม่ที่บรรทัดแรกของปีกกา
break – เป็นคำสั่งที่ใช้ออกจากการวนรอบ
ฟังก์ชั่น คือ การรวมโค้ดโปรแกรมที่ถูกใช้งานซ้ำ ๆ มารวมกันไว้เพื่อง่ายต่อการแก้ไขและเรียกใช้งาน ฟังก์ชั่นมีด้วยกันอยู่ 4 ประเภทคือ
ทั้งนี้การสร้างฟังก์ชั่นสามารถย้อนกลับไปดูได้ในหัวข้อ เรียนรู้จากตัวอย่างโปรแกรมไฟกระพริบ
ในฟังก์ชั่นที่มีการกำหนดให้มีการส่งค่ากลับ จะต้องมีการส่งค่ากลับด้วย การส่งค่ากลับจะใช้คำสั่ง return ซึ่งมีรูปแบบการใช้งานดังนี้
return ข้อมูลที่ต้องการส่งกลับ;
ทั้งนี้ข้อมูลที่ส่งกลับไม่จำเป็นต้องเป็นชนิดเดียวกับที่กำหนดไว้ในชนิดของข้อมูลที่ตอบกลับก็ได้ เนื่องจากหากชนิดข้อมูลที่ตอบกลับ กับชนิดข้อมูลส่งส่งผ่านคำสั่ง return เป็นคนละชนิดกัน จะมีการแปลงชนิดข้อมูลให้อัตโนมัติ (หากทำได้)
จากเนื้อหาทั้งหมดในบทนี้ อาจจะมีส่วนที่ผู้อ่านเข้าใจบ้าง ไม่เข้าใจบ้าง ขอให้ผู้อ่านพยายามศึกษาจากตัวอย่างให้มาก ๆ เพราะตัวอย่างจะมีคำสั่งต่าง ๆ ที่เป็น Keyword อยู่ในนั้น หากมีคำสั่งใดที่ไม่เข้าใจว่าใช้ทำอะไร สามารถนำไปวางใน Google เพื่อค้นหาได้ ทั้งนี้เนื้อหาในหัวข้อ พื้นฐานภาษา C/C++ เป็นการกล่าวถึงทฤษฎีแทบทั้งหมด ซึ่งสามารถนำไปใช้อ้างอิงกับการทดลองตัวอย่างต่าง ๆ เพื่อให้เข้าโค้ดได้ง่ายและรวดเร็วมากยิ่งขึ้น และผู้เขียนขอให้ผู้อ่านพิจารณารูปแบบการใช้งานคำสั่งต่าง ๆ ให้ดี เพราะหากเครื่องหมายบางอย่างหายไปแค่อันเดียวก็ทำให้โปรแกรมเกิด Error ขึ้นได้