ความจำเป็นของพล็อตเตอร์เจ๋งๆ
Arduino สามารถใช้เป็นระบบเก็บข้อมูล (data acquisition) ในงานวิทยาศาสตร์ได้นะ น้องอยากเก็บข้อมูลลงไฟล์เพื่อเอาไปประมวลผลต่อ หรือดูกราฟแบบเรียลไทม์ใช่ไหม? Serial Plotter ใน Arduino IDE มันค่อนข้างจำกัดอะ ควบคุมยาก อยากแสดงอะไรก็ลำบาก แต่กับ Arduino Clever Plotter (ACP) นี่จัดไปวัยรุ่น! พล็อตข้อมูลได้เหมือนเดิม แต่เราคุมได้ทั้งแกน x และขอบเขตของแกน y ตั้งชื่อกราฟให้มันมีความหมาย เลือกการแสดงผลที่เหมาะกับงานได้ แถม ACP ยังเก็บข้อมูลลงไฟล์ให้อีกด้วย สบายมาก
สำหรับคนใจร้อน (Impatient)
- โหลดแอปพลิเคชันมาเลย (ลิงก์เดิมหายไปแล้ว น้องลองหาใน GitHub ดูนะ)
- อัพโหลดสเก็ตช์ลงบอร์ด Arduino ที่น้องชอบ โดยให้มันส่งข้อมูลออกมาเป็นค่าที่คั่นด้วยจุลภาค (comma separated values)
- รัน ACP ได้เลยจ้า
ด้านล่างนี้คือตัวอย่างที่เห็นได้เมื่อเก็บข้อมูลจากการทดลองวัดแรงดันไฟฟ้าตกคร่อมตัวเก็บประจุ (capacitor) ในวงจร RC
เตรียมสเก็ตช์ Arduino ของน้องให้พร้อม
ACP ทำงานคล้ายๆ กับ Serial Plotter ใน IDE นะ ถ้าน้องพิมพ์ค่าออกมาแค่ค่าเดียวแบบนี้
Serial.println(mydata)
ACP ก็จะพล็อตข้อมูลเป็นฟังก์ชันของจำนวนลูป (loop) แต่ถ้าน้องพิมพ์ค่าออกมาหลายค่า คั่นด้วยจุลภาค แบบนี้
Serial.print(x_data);
Serial.print(",");
Serial.print(y_data);
Serial.print(",");
Serial.println(z_data);
ACP จะสร้างกราฟสามอันในพล็อตเดียวกัน แบบที่เห็นด้านล่างนี้เลย:

แต่ละกราฟจะถูกตั้งชื่อให้อัตโนมัติ และจะมีคำอธิบาย (legend) ปรากฏที่มุมซ้ายบน แกน x จะตรงกับจำนวนลูป
ขอบอกไว้หน่อยนะ ถ้า ACP หาบอร์ด Arduino ไม่เจอ มันจะจำลองการมีอยู่ของบอร์ดด้วยการเขียนข้อมูลสุ่มลงพอร์ตให้ดูแทน เก๋ไก๋มาก
ถ้าน้องอยากพล็อตข้อมูลเป็นฟังก์ชันของปริมาณอื่นที่เก็บมาได้ระหว่างรัน (เช่น เวลา) น้องสามารถประกาศมันในฟังก์ชัน setup() ได้ โดยเขียนโครงสร้าง XML ลงพอร์ตอนุกรมแบบนี้
Serial.println("<header><x>t [$\\mu$s]</x></header>");
เมื่อ ACP ตรวจพบสตริงนี้ มันจะตีความตัวเลขตัวแรกในลิสต์ที่คั่นด้วยจุลภาคว่าเป็นตัวแปรที่จะพล็อตบนแกน x แกนก็จะถูกตั้งชื่อตามเนื้อหาในแท็ก <x> ด้วย ในตัวอย่างนี้คือ "t [𝜇s]" ($\\mu$ จะแสดงผลเป็น 𝜇)
น้องสามารถตั้งชื่อให้ข้อมูลบนแกน y ได้ด้วยการเพิ่มป้ายกำกับ (label) ที่ตรงกัน ตัวอย่างเช่น:
Serial.println("<header><x>t [$\\mu$s]</x><y>V_C [V]</y></header>");
ถ้ามีข้อมูลที่จะพล็อตในลิสต์ที่คั่นด้วยจุลภาคมากกว่าหนึ่งค่า น้องก็แค่ให้ลิสต์ป้ายกำกับที่คั่นด้วยจุลภาคใน XML ภายในแท็ก <y>:
Serial.println("<header><x>t [$\\mu$s]</x><y>V_C [V],V_R [V]</y></header>");
สเกลของแกน y จะถูกปรับอัตโนมัติเพื่อให้พอดีกับพล็อต ถ้าน้องอยากกำหนดมันตายตัวเลย ก็แค่เพิ่มค่าขอบล่างและ/หรือขอบบน โดยใช้แท็ก <ylim>:
Serial.println("<header><x>t [$\\mu$s]</x><y>V_C [V]</y><ylim>0,None</ylim></header>");
หรือ
Serial.println("<header><x>t [$\\mu$s]</x><y>V_C [V]</y><ylim>0,1023</ylim></header>");
หรือ อีกแบบ
Serial.println("<header><x>t [$\\mu$s]</x><y>V_C [V]</y><ylim>None,10</ylim></header>");
ตัว ACP มันจะเซฟข้อมูลลงไฟล์ CSV ชื่อ acp.csv ให้อัตโนมัติเป็นค่าเริ่มต้น วิธีนี้จะทำให้น้องสามารถเอาไปวิเคราะห์แบบออฟไลน์ได้ ถ้าต้องการ
โครงสร้างทางเทคนิค & โปรโตคอล Serial
- Python Backend: ACP ถูกเขียนด้วย Python ใช้ไลบรารีเทพๆ อย่าง
matplotlibสำหรับเรนเดอร์กราฟคุณภาพสูง และpyserialสำหรับคุยกับฮาร์ดแวร์ระดับลึก ทำให้มีฟีเจอร์เด็ดอย่างการเรนเดอร์ LaTeX สำหรับป้ายแกน (เช่น ใช้$\mu$แทนหน่วยไมโคร) - XML Metadata Handshake: ACP มีโปรโตคอลเมตาดาต้าที่ฉลาดมาก โดยการส่งสตริงพิเศษอย่าง
<header><x>t [$\mu$s]</x><y>V_C [V]</y></header>ระหว่างsetup()น้องสามารถตั้งค่ากราฟฟิกของโปรแกรมพล็อตเตอร์ได้จากโค้ด Arduino เลย เรียกว่าเป็น "self-describing" data stream ซึ่งเป็นแพตเทิร์นที่เจอในโปรโตคอล IoT แบบขั้นสูงในอุตสาหกรรม - Zero-Jitter Logging: ในขณะที่พล็อตแบบเรียลไทม์เป็นสิ่งสำคัญ ACP ก็ยังดันข้อมูลทุกแพ็กเก็ตที่เข้ามาทาง Serial ลงไฟล์ CSV (
acp.csv) พร้อมกันไปด้วย วิธีนี้รับประกันว่าแม้เธรดเรนเดอร์ของคอมจะล้าหลัง ความสมบูรณ์ของข้อมูลจะยังคงอยู่สำหรับเอาไปประมวลผลต่อใน Excel, MATLAB หรือ Pandas ของ Python ได้ - Virtual Hardware Simulation: ACP มีโหมดจำลองฮาร์ดแวร์ที่แข็งแกร่ง (ใช้สวิตช์
-sและ-p) ถ้าไม่เจอ Arduino สคริปต์จะสร้างข้อมูลสังเคราะห์ขึ้นมา (เช่น สัญญาณรบกวนสุ่ม หรือเส้นโค้งทางคณิตศาสตร์) ทำให้นักพัฒนาสามารถทดสอบลอจิกการแสดงผลและสภาพแวดล้อม Python ได้โดยไม่ต้องต่อฮาร์ดแวร์จริง
วิธีควบคุมแอปพลิเคชัน
เริ่มต้น ACP แค่รันไฟล์ acp.py ไม่ต้องใส่ argument อะไร สวิตช์ -h จะแสดงข้อความช่วยเหลือ
> python3 acp.py -h
น้องสามารถเลือกพอร์ตที่จะอ่านข้อมูลโดยใช้สวิตช์ -p ชื่อไฟล์ที่ใช้เก็บข้อมูลคือ acp.csv เป็นค่าเริ่มต้น แต่เปลี่ยนได้ด้วยสวิตช์ -f
ถ้าไม่เจอบอร์ด Arduino ตัว ACP จะจำลองว่ามีบอร์ดอยู่ น้องสามารถจำลองการส่ง header ได้ด้วยสวิตช์ -s ในกรณีนี้ข้อมูลจะถูกติดป้ายว่า "x (m)", "y (m/s$^2$)" และ "z (kg)" ถ้าอยากเพิ่มคอลัมน์ x แบบจำลอง ก็แค่เพิ่มสวิตช์ -t ไปในคำสั่งรัน acp.py
ACP จะรันไปเรื่อยๆ จนกว่าจะใช้สวิตช์ -n เพื่อบอกว่าจะเก็บข้อมูลกี่ลูป เมื่อจำนวนแถวที่อ่านจาก Arduino เท่ากับจำนวนนี้ ACP จะหยุดและรอให้น้องปิดหน้าต่างพล็อต
แกน x จะแสดงข้อมูล 100 ตัวล่าสุดที่เก็บมาเป็นค่าเริ่มต้น เปลี่ยนจำนวนนี้ได้ด้วยสวิตช์ -r
สวิตช์ -m ให้น้องเลือกมาร์กเกอร์สำหรับพล็อตข้อมูลที่ต่างออกไป ใช้ฟอร์แมตตามไลบรารี matplotlib.pyplot เช่น "-m o-" จะพล็อตข้อมูลด้วยวงกลมเล็กๆ เชื่อมด้วยเส้น
สวิตช์ -b ใช้ตั้งค่าอัตราบอด (baud rate) ซึ่งค่าเริ่มต้นคือ 9600 ACP จะพิมพ์ข้อมูลออกมาเยอะขึ้นถ้าเปิดสวิตช์ -v
วิศวกรรมและการใช้งาน
- Dynamic Scaling: การใช้แท็ก
<ylim>วิศวกรสามารถป้องกันปัญหากวนตีนของ Serial Plotter ทั่วไป ที่สัญญาณรบกวนเล็กๆ ทำให้กราฟซูมออกจนมองไม่เห็นสัญญาณจริง การตั้งลิมิต เช่น0, 1023(สำหรับ ADC 10-bit) จะช่วยให้ภาพกราฟคงที่ - Command Line Flexibility: เครื่องมือนี้ถูกออกแบบมาสำหรับ "ผู้ใช้ขั้นเทพ" ด้วยฟลัก CLI สำหรับควบคุมอัตราบอด (
-b), เลือกพอร์ต (-p), และกำหนดขนาดหน้าต่างแบบโรลลิ่ง (-r) ซึ่งควบคุมว่าข้อมูลย้อนหลังกี่จุดจะแสดงบนหน้าจอในเวลาเดียวกัน - Marker Customization: ผ่านสวิตช์
-mผู้ใช้สามารถเลือกระหว่างพล็อตแบบเส้น, แบบจุดกระจาย หรือมาร์กเกอร์ผสมกันได้ ช่วยให้แยกแยะระหว่างข้อมูลแบบไม่ต่อเนื่องกับคลื่นสัญญาณต่อเนื่องได้ง่ายขึ้น
สัญญาอนุญาต (Licensing)
ACP อยู่ภายใต้สัญญาอนุญาต GNU General Public License เวอร์ชัน 3 สำหรับรายละเอียดเพิ่มเติม ให้รันสคริปต์พร้อมกับสวิตช์ -l