การโปรแกรม serial port ใน MATLAB เพื่อเชื่อมต่อพอร์ตอนุกรมแบบเรียลไทม์

Supachai Vorapojpisut
2 min readApr 23, 2022

--

MATLAB เป็นซอฟต์แวร์ที่มีจุดเด่นในแง่ของการประมวลผลข้อมูลด้วยอัลกอริธึมที่หลากหลายและครอบคลุมหลายด้านตั้งแต่ signal processing, math transform และ machine learning แต่ปัญหาของ MATLAB ตอนเอาไปใช้งานกับบอร์ดไมโครคอนโทรลเลอร์คือ การนำเข้าข้อมูลแบบเรียลไทม์ด้วย sample rate ที่มากกว่า 10 Hz ทำได้ยากในเกือบทุกช่องทางตั้งแต่พอร์ตอนุกรม การสื่อสารผ่าน WiFi ดังนั้น โจทย์ที่ต้องการใช้บอร์ดไมโครคอนโทรลเลอร์เป็น I/O จึงมักต้องพึ่งซอฟต์แวร์อื่น เช่น LabVIEW หรือเขียนโค้ดภาษาคอมพิวเตอร์ เพื่อนำเข้าข้อมูลแล้วมาบันทึกเป็นไฟล์ก่อนจะนำไปประมวลผลด้วย MATLAB

MATLAB รุ่น 2022a มีการเปลี่ยนแปลง API ในส่วนพอร์ตอนุกรมจากเดิมที่สร้าง serial object ด้วยคำสั่ง serial('port') เป็นการใช้คำสั่ง serialport('port') แทน ข้อแตกต่างระหว่าง object คือ ตัว serialportobject จะรองรับการใช้งาน callback function ด้วยคำสั่ง configureCallback(obj,trigger,@callback) ที่สามารถผูกฟังก์ชันเข้ากับการรับข้อมูลทีละไบต์และทีละบรรทัด (ขึ้นกับการกำหนด terminator) หรือกล่าวได้ว่าเราสามารถกระตุ้นส่วนโค้ด MATLAB ให้ขึ้นมาทำงานเมื่อได้รับข้อมูลใหม่ทางพอร์ตอนุกรม ฟีเจอร์นี้เมื่อรวมเข้ากับการเขียนโค้ดทางฝั่งบอร์ดไมโครคอนโทรลเลอร์ เช่น คำสั่ง Serial.println() ของ Arduino จะทำให้เราสามารถเขียนโค้ดฝั่ง MATLAB ให้รับและประมวลผลข้อมูลจากบอร์ด Arduino แบบเรียลไทม์ได้

M5Stack Core 2 ส่งข้อมูลความเร่ง 3 แกนทางพอร์ตอนุกรม

ผมทดลองฟีเจอร์ของ serialport object ด้วยการเขียนโค้ดสำหรับบอร์ด M5Stack Core 2 (หน่วยประมวลผล ESP32) เพื่ออ่านค่าความเร่ง 3 แกนด้วยความถี่ 100 Hz แล้วพิมพ์ค่าออกทางพอร์ตอนุกรม โดยทดลองทั้งแบบพอร์ตอนุกรม UART-USB และ ฺBluetooth serial ข้อสังเกตหนึ่งคือ ตอนใช้โปรแกรม serial terminal ดูข้อมูลที่ได้รับ พบว่าการรับข้อมูลผ่านพอร์ต USB จะมีช่วงที่ข้อความพิมพ์แบบกระตุกหลายครั้ง ในขณะที่ข้อความที่ส่งผ่าน Bluetooth ดูจะลื่นไหลกว่า ผมคาดว่าน่าจะเป็นปัญหาในส่วนของไดรเวอร์ USB serial บนฝั่งคอมพิวเตอร์ที่เป็น Windows

#include <M5Core2.h> 
#include <BluetoothSerial.h>
BluetoothSerial ESP32BT;
uint32_t t_start = 0;
void setup() {
M5.begin();
M5.IMU.Init();
ESP32BT.begin("M5core2");
Serial.begin(115200);
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextColor(GREEN , BLACK);
M5.Lcd.setTextSize(2);
t_start = millis();
}
void loop() {
static float accX = 0.0F;
static float accY = 0.0F;
static float accZ = 0.0F;
if (millis()%10 == 0) {
uint32_t t = millis();
M5.update();
M5.IMU.getAccelData(&accX,&accY,&accZ);
ESP32BT.print( (t - t_start)/1000.0 );
ESP32BT.print(',');
ESP32BT.print(accX);
ESP32BT.print(',');
ESP32BT.print(accY);
ESP32BT.print(',');
ESP32BT.print(accZ);
ESP32BT.println();
if (t%500 == 0) {
M5.Lcd.clearDisplay();
M5.Lcd.setCursor(0,0);
M5.Lcd.printf("%.1f, %.1f, %.1f", accX, accY, accZ);
}
Serial.println(millis() - t); // elapsed time
}
delay(1);
}

ทางฝั่ง MATLAB ผมเขียนโค้ดด้วย App Designer เพื่อสร้างเป็น GUI app โดยใช้ฟีเจอร์animatedline เพื่ออัพเดทเส้นกราฟแบบต่อเนื่อง การเชื่อมต่อทางพอร์ต USB และ Bluetooth ไม่ต้องปรับแก้โค้ดเป็นพิเศษ แค่ระบุหมายเลข COM port ให้ตรง การเขียนโค้ดรับข้อมูลจากบอร์ด M5Stack Core 2 อาศัยการผูก method ของตัว App เป็น callback ของการรับ ‘\n’ ด้วย configureCallback(app.serObj, "terminator", @app.readSerialFcn) ทำให้สามารถประมวลผลทันทีเมื่อได้รับครบ 1 บรรทัด

โค้ดสำหรับสร้าง serialport object และผูก callback
app.serPort = serialport(port, 115200);
configureCallback(app.serPort, "terminator", @app.readSerialFcn);
app.anLine1 = animatedline(app.UIAxes, 'Color', 'b', 'MaximumNumPoints', 1000);
app.anLine2 = animatedline(app.UIAxes, 'Color', 'r', 'MaximumNumPoints', 1000);
app.anLine3 = animatedline(app.UIAxes, 'Color', 'g', 'MaximumNumPoints', 1000);

การอ่านข้อความในกรณีนี้จะใช้คำสั่ง readline() เพราะข้อความ 1 บรรทัดพร้อมแล้วตามเงื่อนไข terminator จากนั้นการแปลงข้อความเป็น vector ทำได้ง่ายๆด้วยคำสั่ง str2num() ซึ่ง App ตัวอย่างจะเอาไปต่อท้ายในบัฟเฟอร์ของ animatedline object

โค้ดของ callback สำหรับรับและแสดงผล
function readSerialFcn(app, src, ~)
txt = readline(src);
new_data = str2num(txt);
if ~isempty(new_data)
addpoints(app.anLine1, new_data(1), new_data(2));
addpoints(app.anLine2, new_data(1), new_data(3));
addpoints(app.anLine3, new_data(1), new_data(4));
drawnow limitrate;
end
end

การทดลองใช้ App พบว่ารับข้อมูลและแสดงผลได้ต่อเนื่อง (การใช้ drawnow limitrate ทำให้อัพเดททุก 20 fps) และเมื่อใช้คู่คำสั่ง tic toc ภายใน callback พบว่า การเพิ่มข้อมูลเข้าไปแต่ยังไม่อัพเดทส่วนกราฟจะใช้เวลาประมาณ 2–3 มิลลิวินาที แต่จังหวะที่อัพเดทกราฟ เวลาประมวลผลจะกระโดดไปที่ 30–50 มิลลิวินาที

หน้าจอ MATLAB App แสดงกราฟความเร่ง 3 แกนตอนเขย่า

終わりに (ในตอนท้าย)

การเปลี่ยนแปลง API ในการเชื่อมต่อพอร์ตอนุกรมของ MATLAB 2022a ถือเป็นเรื่องดี เพราะทำให้การเขียนโค้ดประมวลผลข้อมูลของเซ็นเซอร์จากบอร์ดไมโครคอนโทรลเลอร์ทำได้ง่ายและเป็นเรียลไทม์มากขึ้น ข้อเสียตอนนี้น่าจะเป็นที่ตัว App จะกิน performance เครื่องมากกว่าที่คิด (run จากใน MATLAB ใช้ไป 9x % หาก package เป็น standalone app จะใช้ประมาณ 6x %) ซึ่งอาจจะเกิดจากส่วนการอัพเดทกราฟที่ใช้กลไก animatedline

--

--

Supachai Vorapojpisut
Supachai Vorapojpisut

Written by Supachai Vorapojpisut

Assistant Professor at Thammasat University

No responses yet