6. C++ พื้นฐาน: เงื่อนไขและลูป
if, else, switch, for, while, break และ continue พร้อมผังลูปรอความสูงนิ่งจากโค้ดจริงของชมรม
เงื่อนไขและลูป สรุปก่อนอ่าน
- คืออะไร
ifคือ "ถ้า...ก็ทำ..." และลูป (for,while) คือ "ทำซ้ำจนกว่า..." - ทำไมต้องมี ถ้ามีแค่ลำดับตายตัว โปรแกรมจะไม่ตอบสนองอะไรเลย ต้องมีทางเลือกและการทำซ้ำ เหมือนเราตัดสินใจ "ถ้าฝนตกให้พกร่ม" และ "ตักข้าวซ้ำจนอิ่ม"
- ในโค้ดโดรน ใช้ตอน "ถ้าสูงเกินไปให้ลง" และ "รอวนไปจนความสูงนิ่งแล้วค่อยไปต่อ"
โปรแกรมที่ทำแต่บรรทัดแรกไปบรรทัดสุดท้ายตามลำดับทำได้ไม่มาก โดรนต้องตัดสินใจ ("ถ้าสูงเกินไป ให้ลง") และทำซ้ำ ("รอจนความสูงนิ่ง") สองเครื่องมือนี้คือ เงื่อนไข (condition) และ ลูป (loop)
1. if / else
int height = 85;
if (height > 100) {
Serial.println("สูงมาก");
} else if (height >= 70) {
Serial.println("สูงพอบินได้");
} else {
Serial.println("ต่ำเกิน");
}คอมพิวเตอร์ตรวจเงื่อนไขจากบนลงล่าง ตัวแรกที่เป็นจริงทำงาน ที่เหลือข้ามหมด ตัวอย่างจริงจาก Drone_Mission.ino ที่เลือกโหมดของ KEY 1
// Drone_Mission.ino
uint8_t KEY1_MODE = 2;
void DroneMission_KEY_1_handle(void) {
if (KEY1_MODE == 1) {
KEY1_mode1();
} else if (KEY1_MODE == 2) {
KEY1_mode2();
} else {
// ถ้าใส่โหมดผิด ให้ไม่ทำงานอันตราย
drone.calibrateAndDelay(3000);
...
}
}else ตัวสุดท้ายดักค่าที่ไม่คาดคิด เป็นนิสัยที่ดี ถ้าใครพิมพ์ KEY1_MODE = 5 โดรนยังทำอะไรที่ปลอดภัย
ตัวอย่างชีวิตจริง: ป้ายไฟจราจร
ไฟแดงหยุด ไฟเหลืองชะลอ นอกนั้นไป เป็นลำดับ if ... else if ... else ตรงๆ
รวมเงื่อนไข
&& คือ "และ" || คือ "หรือ" ! คือ "ไม่"
// shooting.ino
if (h == 0 || h > 300) { ... } // ค่าเพี้ยนอย่างใดอย่างหนึ่ง
if (current_h < (HEIGHT_SHOOT - 10) || current_h > (HEIGHT_SHOOT + 10)) { ... }เงื่อนไขที่สองอ่านว่า "ต่ำกว่า 110 หรือสูงกว่า 130" ซึ่งคือ "อยู่นอกช่วง 120 ± 10"
2. switch
เมื่อเทียบตัวแปรตัวเดียวกับหลายค่า switch อ่านง่ายกว่า else if ต่อกันยาว โค้ดชมรมใช้กับรอบการปล่อยบอล
// Servo.ino
void drop_payload_by_round_all(int round) {
switch (round) {
case 0:
release_dual_servo_angle(PORT3, 1000, PORT6, 1000);
break;
case 1:
release_dual_servo_angle(PORT3, 2550, PORT6, 2550);
break;
}
}break สำคัญมาก ถ้าลืม โปรแกรมจะไหลต่อลงไปทำ case ถัดไปด้วย
3. while และ for
while ทำซ้ำตราบใดที่เงื่อนไขยังจริง ใช้เมื่อไม่รู้จำนวนรอบ
// Servo.ino
uint32_t t = micros();
while (micros() - t < 1500000) {
// ส่งพัลส์ซ้ำ นาน 1.5 วินาที
}for ใช้เมื่อรู้จำนวนรอบ ประกอบด้วยสามส่วนคือ เริ่ม; เงื่อนไข; ก้าวต่อไป
// protocol.cpp (smooth_set_height) — ขยับความสูงทีละ step
for (uint16_t h = current_height + step; h <= target_height; h += step) {
set_height(h);
delay(delay_ms);
}ถ้า current_height = 100, target_height = 140, step = 10 ลูปรันที่ h = 110, 120, 130, 140 รวม 4 รอบ แต่ละรอบสั่งความสูงใหม่ แล้วรอ จึงขึ้นเป็นขั้นบันไดนิ่มๆ แทนกระโดดทีเดียว
break และ continue
breakออกจากลูปทันทีcontinueข้ามส่วนที่เหลือของรอบนี้ ไปรอบถัดไป
4. ผังลูปจริง: รอความสูงนิ่ง
ฟังก์ชันที่ซับซ้อนที่สุดในหน้านี้คือ reach_height_shoot_stable() ใน shooting.ino ใช้ทุกอย่างที่เรียนมาพร้อมกัน
// shooting.ino (ตัดเฉพาะส่วนลูป)
uint32_t t0 = millis();
uint32_t stableSince = 0;
while (millis() - t0 < MAX_WAIT_MS) { // MAX_WAIT_MS = 3500
pArduino->set_height(TARGET);
delay(SAMPLE_DELAY); // 80
pArduino->poll();
uint16_t h = read_height_cm();
if (h == 0 || h > 300) {
stableSince = 0;
continue;
}
int diff = (int)h - (int)TARGET;
if (abs(diff) <= TOL) { // TOL = 4
if (stableSince == 0) stableSince = millis();
if (millis() - stableSince >= STABLE_NEED_MS) { // 500
break;
}
} else {
stableSince = 0;
}
}อ่านเป็นภาษาคน: "ภายใน 3.5 วินาที สั่งความสูง 120 รออ่านทุก 80 ms ถ้าอ่านค่าเพี้ยนให้ทิ้ง ถ้าอยู่ในช่วง 116–124 cm ต่อเนื่องครบ 0.5 วินาที ถือว่านิ่ง ออกได้" หมดเวลาก่อนก็ออกเหมือนกัน จะได้ไม่ค้างตลอดไปถ้าเซนเซอร์เสีย
สังเกตตัวแปร stableSince ที่ใช้เป็นธง: ค่า 0 แปลว่า "ยังไม่เริ่มนับ" นี่คือเทคนิคที่เจอบ่อย
ลองทำ
- เขียนลูป
forที่พิมพ์ความสูง 100, 110, ... 150 ลง Serial Monitor (คำตอบ:for (int h = 100; h <= 150; h += 10) Serial.println(h);) - ถ้า
TOLเปลี่ยนจาก 4 เป็น 10 ช่วงที่ถือว่านิ่งเป็นกี่ cm (คำตอบ: 110–130) - ในผังด้านบน ถ้าอ่านได้ 118, 0, 119, 121 ตามลำดับ
stableSinceถูกรีเซ็ตกี่ครั้ง (คำตอบ: 1 ครั้ง ตอนอ่านได้ 0)
พังตรงไหนบ่อย
- ลืม
breakในswitchโปรแกรมทำหลายcaseติดกัน - ลูป
whileไม่มีทางจบ ลืมปรับตัวแปรที่ใช้ในเงื่อนไข โค้ดชมรมกันด้วยเงื่อนไขเวลาmillis() - t0 < MAX_WAIT_MS forกับuint16_tนับลงfor (uint16_t h = 10; h >= 0; h--)ไม่มีวันจบ เพราะ unsigned ไม่เคยน้อยกว่า 0 โค้ดsmooth_set_heightจึงใส่if (h <= step) break;กันไว้- ใช้
=แทน==ดูหน้าตัวแปร - ลืมปีกกา
if (x) a(); b();b()ทำเสมอ ไม่ได้อยู่ในif