3. โหมดฝูงบินและ CRC-16
เฟรมที่ขึ้นต้นด้วย 0xFA รหัสคำสั่งฝูง และการอ่านข้อมูลสถานะของโดรนแต่ละลำ จากบันทึกโปรโตคอล RCSA
บินโดรนลำเดียว ถ้าข้อความเพี้ยนไปหนึ่งไบต์ โดรนก็แค่ทำผิดหนึ่งครั้ง แต่ถ้าสั่งโดรน 20 ลำพร้อมกันแล้วข้อมูลเพี้ยน ทุกลำอาจทำผิดพร้อมกัน โหมดฝูงบินจึงเพิ่ม CRC-16 ท้ายเฟรมไว้ตรวจว่าข้อความมาครบ
เหมือนใบเสร็จที่มีเลขตรวจสอบท้ายใบ ผู้รับคำนวณเลขนั้นจากเนื้อความใหม่ ถ้าไม่ตรงกัน ก็รู้ทันทีว่าใบเสร็จผิด
หน้านี้อิงบันทึกเท่านั้น
โฟลเดอร์โค้ดของชมรม (drone_sanam5) ไม่มีโค้ดโหมดฝูงบิน ข้อมูลทั้งหมดมาจาก RCSA_PROTOCOL_FULL_NOTES.md ที่สกัดจากแอป RCSA 1.0.0 บันทึกไม่ได้ระบุรูปแบบพหุนามของ CRC จึงไม่เขียนสูตรให้ ข้อมูลระดับบิตของฝั่ง Flight Controller ก็ไม่มี
เฟรม
ตามบันทึก ฟังก์ชันส่งทั่วไปทำงานดังนี้
ส่ง = 0xFA + data[] + CRC16(data)
CRC16 เป็นแบบอิงตาราง (table-based) คืนค่า [crc_low, crc_high]- ใช้ได้ทั้งช่องทาง TCP และ UDP
- ฝั่งรับ (ข้อมูลสถานะจากโดรน) ก็ขึ้นต้น
0xFAแล้วตามด้วยไบต์ความยาว ไบต์ชนิดข้อมูล payload และ CRC 2 ไบต์ - รองรับโดรนสูงสุด 20 ลำ
รูปร่างของเฟรม
- FAหัว
- dataคำสั่ง+พารามิเตอร์
- loCRC ต่ำ
- hiCRC สูง
รหัสคำสั่งของโหมดฝูง
บันทึกแบ่งเป็นสองชุดตาราง ชุดแรกคือรหัสที่แอปส่งไปที่รีโมท (ชั้น 1 ในบท คำสั่งเดินทางยังไง) ชุดที่สองคือรหัสภายใน "ภารกิจ" ของโดรนแต่ละลำ
คำสั่งฝูง (ชุดรีโมท)
| รหัส | ชื่อ |
|---|---|
100 (0x64) | formation_flightNum |
101 (0x65) | formation_calibration |
102 (0x66) | formation_takeOff |
103 (0x67) | formation_autoland |
104 (0x68) | formation_move |
105 (0x69) | formation_setZ |
150 (0x96) | formation_sendMission |
151 (0x97) | formation_start |
152 (0x98) | formation_stop |
153 (0x99) | formation_moveXYZ |
162 (0xA2) | formation_clear |
255 (0xFF) | all_autoLand |
ยังมีอีกหลายตัว (วงกลม สามเหลี่ยม สี่เหลี่ยม ไฟ RGB) ดูตารางเต็มในบันทึก
คำสั่งในภารกิจของแต่ละลำ
| รหัส | ชื่อ | รหัส | ชื่อ |
|---|---|---|---|
| 0 | calibrate | 8 | flip |
| 1 | takeOff | 9 | jump |
| 2 | autoLand | 10 | moveXYZ |
| 3 | moveXY | 11 | atti |
| 4 | speed | 12 | circle |
| 5 | moveZ | 13 | circleAlt |
| 6 | yaw | 50 | RGB |
| 7 | local360 | 51 | int |
รหัส takeOff = 1 ในตารางนี้ ไม่ใช่ takeOff = 16 ของรีโมท และไม่ใช่ F_ARM = 1 ในแพ็กเก็ต I2C ทั้งสามเป็นตารางคนละชุด
ข้อมูลสถานะที่โดรนส่งกลับ
สำหรับข้อมูลชนิดหนึ่ง (บันทึกระบุ u=1, f=8) payload ถอดรหัสได้ดังนี้
payload สถานะของโดรน 1 ลำ
- X[0–1] int16
- Y[2–3] int16
- Z[4–5] int16
- V[6] แบต ×0.1
- R[7] กำลังรัน
- CS[8–9] checksum ภารกิจ
- ID[12–15] รหัสลำ
- T[16–19] เวลา uint32
ไบต์ [10]–[11] ไม่ได้ระบุไว้ในบันทึก และตำแหน่ง X Y ถูกแปลงเป็นช่องตาราง (grid/code index) ด้วยเซลล์ละ 40 หน่วย
นอกจากนี้บันทึกยังพบสตรีมเซนเซอร์แยกต่างหากที่ขึ้นต้น 0xFA เช่นกัน ถอดรหัสได้ความสูง ความดัน อุณหภูมิ ความเร่ง ไจโร มุม roll/pitch/yaw ระยะจากเรนจ์ไฟน์เดอร์ ความเร็ว แรงดันแบต และข้อมูลกล้อง (รายชื่อฟิลด์เท่านั้น ไม่มีตำแหน่งไบต์)
แนวคิดที่เอาไปใช้เองได้
แม้ชมรมไม่ได้ใช้โหมดฝูง แนวคิด "หัวเฟรม + ความยาว + ข้อมูล + CRC" ใช้ได้กับทุกการสื่อสาร เช่นส่งข้อมูลจาก Arduino ไปคอมพิวเตอร์เพื่อบันทึกการบิน แต่ต้องเลือกสูตร CRC เอง และตกลงกับฝั่งรับให้ตรงกัน อย่าเดาว่า CRC-16 ของ RCSA คือสูตรไหน
ลองทำ
- เขียนเฟรมสมมติ
FA 01 02 lo hiแล้วอธิบายว่าส่วนไหนคือหัว ข้อมูล และ CRC (ไม่ต้องคำนวณ CRC) - จากตาราง payload คำนวณว่าโดรนที่ส่งไบต์ [6] =
0x4Aมีแรงดันแบตเท่าไร (74 × 0.1 = 7.4) - ตอบ: ทำไมต้องระบุจำนวนโดรนสูงสุด 20 ลำในตัวรับ (คำใบ้: ขนาดบัฟเฟอร์ที่ต้องเตรียม)
พังตรงไหนบ่อย
- เดาสูตร CRC CRC-16 มีหลายแบบ ค่าเริ่มต้น ทิศบิต และพหุนามต่างกันให้ผลต่างกัน บันทึกไม่ระบุ ต้องสกัดจากแอปหรือถามผู้ผลิต
- สลับลำดับไบต์ CRC บันทึกบอกคืน
[crc_low, crc_high]ต่ำก่อน - ใช้รหัสข้ามตาราง รหัสในภารกิจกับรหัสรีโมทคนละตาราง
- คิดว่าอ่าน payload ทุกไบต์ได้ บางตำแหน่ง (เช่น [10]–[11]) ยังไม่ทราบความหมาย
- ลืมว่าไม่มีโค้ดฝูงในโฟลเดอร์ชมรม ถ้าจะใช้ ต้องเขียนใหม่ทั้งหมดและทดสอบกับโดรนจริง