ข้ามไปยังเนื้อหา

ออกแบบ HMI: การ์ด ลำดับสายตา สี และงบ widget

โมดูล 3 — แสดงผลเซนเซอร์บน HMI · สไลด์: slides.md · ภาพรวมโมดูล · หน้าหลักสูตร

ออกแบบแดชบอร์ดสี่การ์ดบนกระดาษก่อนพิมพ์โค้ด ทั้งการจัดกลุ่ม ลำดับสายตา ความหมายของสี พิกัดบนจอ 792x398 และตารางนับ widget ที่ต้องไม่เกินงบ 32 ที่เราตั้งเอง

เมื่อจบบทเรียนนี้ คุณจะ:

  1. อธิบายหลัก HMI สามข้อที่แผงควบคุมจริงใช้ (การ์ดหนึ่งใบตอบคำถามหนึ่งข้อ ลำดับสายตาสามชั้น และสีเขียว เหลือง แดงที่ถูกจองไว้ให้สถานะ) แล้วชี้ได้ว่าผังตัวอย่างละเมิดข้อไหน
  2. วาดผังสี่การ์ดบนกระดาษพร้อม x y w h ของ Panel ทุกใบ โดยทุกใบผ่านเงื่อนไข x + w ไม่เกิน 792 และ y + h ไม่เกิน 398 และไม่มีการ์ดใดทับกัน
  3. ทำตารางงบ widget รายการ์ดที่รวมแล้วไม่เกิน 32 โดยนับ Panel ทุกใบ และไม่นับ series ของ Chart
  4. เขียนคำสั่ง ui.Panel ที่ให้ color= เป็นสีพื้น min= เป็นสีขอบ max= เป็นรัศมีมุม และ value= เป็นความหนาขอบ ได้ถูกต้อง และบอกได้ว่าทำไมต้องสร้าง Panel ก่อน Label ที่วางทับ

บทเรียนนี้งานหลักอยู่บนกระดาษ เตรียมกระดาษ ดินสอ และบันทึกการเรียนไว้ ทบทวนของสามชิ้นที่จะมาอยู่ร่วมจอเดียวกัน คือ pot กับ ui.Arc ui.Seg7 และ CapSense กับ ui.Bar จากบทเรียน 2.7–2.9, sensors.bmi270.motion() ที่อ่านหกแกนในครั้งเดียวจากบทเรียน 3.1–3.3 และ ui.Chart หลาย series ที่ cadence 200 ms จากบทเรียน 3.4–3.6

เปิดเมนู Sensor Dashboard ของเฟิร์มแวร์บนบอร์ดแล้วปล่อยให้มันวิ่ง หน้าจอเดียว การ์ดหลายใบ เซนเซอร์หลายตัว อัปเดตพร้อมกัน ไม่มีเมนูให้เข้า มองแล้วรู้สถานะทั้งระบบภายในสองวินาที นั่นคือ HMI (Human-Machine Interface) สังเกตด้วยว่าการ์ดขึ้นกับบอร์ด บน Eva Kit มีการ์ด BMI270 · Controls · กราฟการเคลื่อนไหว · เข็มทิศ · จอยสติ๊ก และไม่มีการ์ดลูกบิด ส่วน Dev Kit มีการ์ด DPS368 SHT40 และเรดาร์เพิ่ม เพราะเฟิร์มแวร์ประกอบการ์ดตามชิปที่บอร์ดมี

ชุดบทเรียน 3.7–3.9 แทบไม่มีของใหม่ ความยากอยู่ที่ของทุกชิ้นต้องทำงานพร้อมกันบนจอเดียว แผงควบคุมจริงไม่ได้ ออกแบบให้สวย มันออกแบบให้ คนที่เหนื่อยและรีบ อ่านถูกในครั้งแรก หลักข้อแรกคือการจัดกลุ่ม ค่าที่มาจาก แหล่งเดียวกันหรือใช้ตัดสินใจเรื่องเดียวกันต้องอยู่ในกรอบเดียวกัน การ์ดหนึ่งใบตอบคำถามหนึ่งข้อ “การเคลื่อนไหว เป็นยังไง” “หันไปทางไหน” “มีใครแตะอยู่ไหม” “ลูกบิดตั้งไว้เท่าไร” ถ้าตอบไม่ได้ว่าการ์ดใบนี้ตอบคำถามอะไร ก็ยังไม่ควรมีการ์ดใบนี้

หลักข้อที่สองคือลำดับสายตาสามชั้น ชั้นแรกคือค่าหลักที่ต้องอ่านจากระยะสองสามเมตร (ตัวใหญ่ สว่างบนพื้นเข้ม) ชั้นที่สองคือรายละเอียด กราฟ แถบ ที่อ่านตอนเดินเข้ามาใกล้ ชั้นที่สามคือชื่อการ์ด หน่วย ป้าย ที่อ่านเฉพาะตอนหา ใน ui เรามีเครื่องมือคุมเรื่องนี้แค่สองอย่าง คือ value= ของ Label (ขนาดฟอนต์ 14/16/20/24/28) กับ color= ถ้าใส่ 28 ให้ทุกตัว ทุกอย่างเด่นเท่ากัน แปลว่าไม่มีอะไรเด่น หลักข้อที่สามคือสีมีความหมาย เขียวคือปกติ เหลืองหรืออำพันคือเฝ้าดู แดงคือต้องลงมือ ห้ามใช้แดงกับของปกติ ไม่งั้นวันที่เกิดเรื่องจริงจะไม่มีใครสังเกต สีที่เหลือใช้แยกแหล่งข้อมูล เช่น การ์ด IMU ขอบม่วง COL_IMU และหัวข้อการ์ดก็ม่วงด้วย ให้คัดเลขสีจริงมาวาง ต้นไฟล์ของเราเอง อย่าสุ่มเลขสีเอง

ความเร็วอัปเดตก็เป็นเรื่องออกแบบ ตัวเลขที่เปลี่ยนทุก 20 ms คนอ่านไม่ทัน เห็นแต่เลขเบลอกระพริบ และฝั่งเครื่อง ลูปที่เร็วเกินจะส่งงานให้ CM55 ถี่กว่าที่วาดทัน คิวเต็ม แล้วเฟรมหายเงียบ ๆ ไม่มี error มีแต่จอที่ “รู้สึกกระตุก” จำไว้ว่าแดชบอร์ดหนักใช้ 200 ms ส่วนหน้าเบา ๆ ที่มีไม่กี่ widget อย่างต่ำ 50 ms

ui ไม่มี grid หรือ flex แบบเว็บ ทุก widget ต้องบอกพิกัดเอง พื้นที่ที่เราใช้ได้กว้าง 792 สูง 398 ทุกการ์ดต้องผ่าน x + w ≤ 792 และ y + h ≤ 398 วางทับกันหรือเกินขอบแล้วจอไม่ error มันแค่วาดทับ เลขที่ผิดจะเงียบจนกว่าเราจะ เห็นด้วยตา กรอบการ์ดคือ ui.Panel ซึ่งใช้ kwargs ชื่อเดิมแต่ความหมายไม่เหมือนใคร color คือสีพื้น min คือ สีขอบ max คือ รัศมีมุม และ value คือ ความหนาขอบ เช่น ui.Panel(x=24, y=100, w=368, h=136, color=BG_CARD, min=COL_IMU, max=12, value=2) (24 + 368 = 392, 100 + 136 = 236 ผ่านทั้งคู่) สร้าง Panel ก่อนเสมอ ของที่สร้างทีหลังอยู่ชั้นบน ถ้าสลับลำดับ การ์ดจะบังตัวหนังสือจนหาย

สุดท้ายคืองบ เฟิร์มแวร์รับได้ 64 widgets ต่อหน้าจอ (UI_MAX_WIDGETS เท่ากันทั้งสองบอร์ด) ตัวที่ 65 ไม่ขึ้นและ ไม่เตือนอะไรเลย เพราะโค้ด Python อยู่บน CM33 แต่ widget ถูกวาดโดย LVGL บน CM55 ทุกตัวกินหนึ่งช่องในตาราง ขนาดคงที่ที่จองไว้ตั้งแต่คอมไพล์ จองล่วงหน้าในขนาดที่รู้แน่ดีกว่ายืดหยุ่นแล้วพังตอนทำงาน ชุดบทเรียนนี้ตั้งงบ ให้ตัวเอง 32 เพื่อเหลือที่ให้บทเรียน 4.1–5.3 ต่อยอดบนหน้าจอเดิม ตารางในสไลด์จองไว้ 23 จาก 32 (Panel นับทุกใบ สี่การ์ดจึงกินไป 4 ตัวก่อนแสดงค่าอะไรเลย ส่วน add_series() สามครั้งยังเป็น Chart ตัวเดียว) ใครจะเพิ่มของ ต้องกลับมาแก้ตารางนี้ก่อน

สไลด์ของบทเรียนนี้อ้างถึงไฟล์ที่อยู่ในบทเรียนอื่นด้วย:

คำถามชุดเดียวกันอยู่ใน quiz.yaml สำหรับระบบที่ตรวจอัตโนมัติ

  1. ทีมหนึ่งทำกรอบการ์ดเข็มทิศเป็นสีแดงสดเพราะชอบสีนี้ ทั้งที่เข็มทิศทำงานปกติ ข้อใดอธิบายปัญหาได้ถูกต้อง (เลือกหนึ่งข้อ · เป้าหมายข้อ 1)

    • ก) แดงถูกจองไว้ให้ “ต้องลงมือ” ใช้กับของปกติแล้ว วันที่เกิดเรื่องจริงจะไม่มีใครสังเกตเห็น
    • ข) ไม่มีปัญหา สีเป็นเรื่องความชอบของทีม
    • ค) ui.Panel รับสีแดงไม่ได้ จอจะไม่แสดงการ์ด
    • ง) แดงทำให้ CM55 วาดช้าลง จึงควรใช้สีเข้มแทน
    เฉลย

    ก — ในแผงควบคุมจริง เขียว เหลือง แดงมีความหมายที่ทั้งอุตสาหกรรมอ่านตรงกัน แดงคือต้องหยุดหรือแก้เดี๋ยวนี้ ถ้าใช้กับของปกติ สีนี้จะไม่เหลือความหมายตอนที่ต้องใช้จริง สีที่เหลือใช้แยกแหล่งข้อมูลแทน

  2. จอที่เราใช้ได้กว้าง 792 สูง 398 การ์ดใดต่อไปนี้ผิดเงื่อนไขพิกัด (เลือกหนึ่งข้อ · เป้าหมายข้อ 2)

    • ก) ui.Panel(x=408, y=100, w=400, h=136, …)
    • ข) ui.Panel(x=24, y=100, w=368, h=136, …)
    • ค) ui.Panel(x=24, y=252, w=320, h=136, …)
    • ง) ui.Panel(x=360, y=252, w=408, h=136, …)
    เฉลย

    ก — 408 + 400 = 808 เกิน 792 ส่วนข้ออื่นได้ 392, 344 และ 768 และ y + h ไม่เกิน 388 การ์ดที่เกินขอบไม่ทำให้เกิด error จอแค่วาดทับหรือตัดทิ้ง จึงต้องบวกเลขบนกระดาษก่อน

  3. การ์ด IMU มี Panel หนึ่งใบ Label หัวข้อ Chart ที่ add_series() เพิ่มอีกสองเส้นให้ครบสามแกน และ Label ค่าสามแกนหนึ่งตัว การ์ดนี้ใช้งบกี่ widget (เลือกหนึ่งข้อ · เป้าหมายข้อ 3)

    • ก) 4
    • ข) 6
    • ค) 3
    • ง) 5
    เฉลย

    ก — series ของ Chart ไม่ใช่ widget add_series() กี่ครั้งก็ยังเป็น Chart ตัวเดียว แต่ Panel นับทุกใบ จึงได้ Panel + หัวข้อ + Chart + Label ค่า = 4

  4. ข้อใดถูกต้องเกี่ยวกับเพดาน 64 widgets และงบ 32 ของชุดบทเรียนนี้ เลือกทุกข้อที่ถูก (เลือกได้หลายข้อ · เป้าหมายข้อ 3)

    • ก) widget ตัวที่ 65 ไม่ขึ้นจอ และไม่มีข้อความเตือนอะไรเลย
    • ข) เพดานมาจากตารางขนาดคงที่ฝั่ง CM55 ที่จองไว้ตั้งแต่คอมไพล์
    • ค) 32 คืองบที่ชุดบทเรียนนี้ตั้งเอง เพื่อเหลือที่ให้บทเรียน 4.1–5.3 ต่อยอด
    • ง) Dev Kit รับได้มากกว่า Eva Kit เพราะมีเซนเซอร์มากกว่า
    • จ) ถ้าเกินงบ เฟิร์มแวร์จะขยายตารางให้เองตอนรัน
    เฉลย

    ก, ข, ค — UI_MAX_WIDGETS เท่ากันทั้งสองบอร์ดเพราะใช้ ipc_ui ชุดเดียวกัน ตารางถูกจองขนาดตายตัวเพื่อไม่ต้องขอหน่วยความจำระหว่างวาดจอ ตัวที่เกินจึงหายเงียบ ๆ งบ 32 เป็นวินัยที่เราตั้งเอง

  5. ต้องการการ์ดพื้น BG_CARD ขอบสี COL_IMU มุมโค้ง 12 px และขอบหนา 2 px คำสั่งใดถูกต้อง (เลือกหนึ่งข้อ · เป้าหมายข้อ 4)

    • ก) ui.Panel(x=24, y=100, w=368, h=136, color=BG_CARD, min=COL_IMU, max=12, value=2)
    • ข) ui.Panel(x=24, y=100, w=368, h=136, color=COL_IMU, min=BG_CARD, max=2, value=12)
    • ค) ui.Panel(x=24, y=100, w=368, h=136, color=BG_CARD, min=0, max=100, value=COL_IMU)
    • ง) ui.Panel(x=24, y=100, w=368, h=136, color=BG_CARD, min=12, max=COL_IMU, value=2)
    เฉลย

    ก — สำหรับ Panel color คือสีพื้น min คือสีขอบ max คือรัศมีมุม value คือความหนาขอบ ชื่อเดียวกับ Slider แต่ไม่ใช่ค่าต่ำสุดสูงสุด อย่าเดาจากชื่อ และต้องสร้าง Panel ก่อน Label ที่วางทับ ไม่งั้นการ์ดจะบังตัวหนังสือ

ออกแบบบนกระดาษ ก่อนแตะคีย์บอร์ด ทำลงบันทึกการเรียนให้ครบทุกข้อ

  • เขียนคำถามหนึ่งข้อที่การ์ดแต่ละใบตอบ ครบสี่การ์ด (IMU · เข็มทิศ · CapSense · ลูกบิด)
  • วาดผังสี่การ์ดพร้อม x y w h ของทุก Panel ตรวจทีละใบว่า x + w ≤ 792 และ y + h ≤ 398 และเว้นช่องไฟระหว่างการ์ด
  • เผื่อพื้นที่แถวหัวหรือแถบสถานะไว้สำหรับชื่อทีม เลขรอบ และเวลาลูป
  • ทำตารางงบ widget รายการ์ด แล้วรวมเลขให้เห็น ต้องไม่เกิน 32 (นับ Panel ทุกใบ ไม่นับ series)
  • ระบุลำดับสายตาของแต่ละการ์ด ตัวไหนคือค่าหลัก (ฟอนต์ใหญ่ สีขาว) ตัวไหนคือป้าย (เล็กกว่า สีจาง)
  • เลือกสีประจำเซนเซอร์ให้แต่ละการ์ด และเขียนกำกับว่าเขียว เหลือง แดงสงวนไว้ให้สถานะเท่านั้น
  • เขียนคำสั่ง ui.Panel ของการ์ดใบแรกด้วยมือ แล้วเทียบ kwargs ทีละตัวกับตารางความหมายของ Panel

บทเรียน 3.8 จะเปลี่ยนผังกระดาษนี้เป็นโค้ด อ่านเซนเซอร์ทุกตัวในลูปเดียวที่ cadence 200 ms และบทเรียน 3.9 จะพิสูจน์มันด้วยการรันต่อเนื่องสิบนาที เก็บผังกับตารางงบไว้ให้ดี

บทเรียนถัดไป: บทเรียน 3.8 — ประกอบแดชบอร์ด: สี่การ์ดในลูปเดียว

  • การ์ดใบไหนของคุณที่ตอบคำถามได้ไม่ชัด และถ้าต้องตัดทิ้งหนึ่งใบ คุณจะตัดใบไหน
  • ถ้าต้องเพิ่มไฟเตือนสีแดงหนึ่งดวง มันควรติดเมื่อไร และตอนที่ทุกอย่างปกติมันควรเป็นอย่างไร
  • ตอนเลือกว่าจะตัด Label ตัวไหนให้พองบ คุณใช้เกณฑ์อะไรตัดสิน

คำถามทบทวน

ลองตอบเองก่อน แล้วค่อยเปิดดูเฉลย

  1. ทีมหนึ่งทำกรอบการ์ดเข็มทิศเป็นสีแดงสดเพราะชอบสีนี้ ทั้งที่เข็มทิศทำงานปกติ ข้อใดอธิบายปัญหาได้ถูกต้อง (เป้าหมายข้อ 1)

    1. แดงถูกจองไว้ให้ "ต้องลงมือ" ใช้กับของปกติแล้ว วันที่เกิดเรื่องจริงจะไม่มีใครสังเกตเห็น
    2. ไม่มีปัญหา สีเป็นเรื่องความชอบของทีม
    3. ui.Panel รับสีแดงไม่ได้ จอจะไม่แสดงการ์ด
    4. แดงทำให้ CM55 วาดช้าลง จึงควรใช้สีเข้มแทน
    ดูเฉลย

    คำตอบ: A. แดงถูกจองไว้ให้ "ต้องลงมือ" ใช้กับของปกติแล้ว วันที่เกิดเรื่องจริงจะไม่มีใครสังเกตเห็น

    ในแผงควบคุมจริง เขียว เหลือง แดงมีความหมายที่ทั้งอุตสาหกรรมอ่านตรงกัน แดงคือต้องหยุดหรือแก้เดี๋ยวนี้ ถ้าใช้กับของปกติ สีนี้จะไม่เหลือความหมายตอนที่ต้องใช้จริง สีที่เหลือใช้แยกแหล่งข้อมูลแทน

  2. จอที่เราใช้ได้กว้าง 792 สูง 398 การ์ดใดต่อไปนี้ผิดเงื่อนไขพิกัด (เป้าหมายข้อ 2)

    1. ui.Panel(x=408, y=100, w=400, h=136, ...)
    2. ui.Panel(x=24, y=100, w=368, h=136, ...)
    3. ui.Panel(x=24, y=252, w=320, h=136, ...)
    4. ui.Panel(x=360, y=252, w=408, h=136, ...)
    ดูเฉลย

    คำตอบ: A. ui.Panel(x=408, y=100, w=400, h=136, ...)

    408 + 400 = 808 เกิน 792 ส่วนข้ออื่นได้ 392, 344 และ 768 และ y + h ไม่เกิน 388 การ์ดที่เกินขอบไม่ทำให้เกิด error จอแค่วาดทับหรือตัดทิ้ง จึงต้องบวกเลขบนกระดาษก่อน

  3. การ์ด IMU มี Panel หนึ่งใบ Label หัวข้อ Chart ที่ add_series() เพิ่มอีกสองเส้นให้ครบสามแกน และ Label ค่าสามแกนหนึ่งตัว การ์ดนี้ใช้งบกี่ widget (เป้าหมายข้อ 3)

    1. 4
    2. 6
    3. 3
    4. 5
    ดูเฉลย

    คำตอบ: A. 4

    series ของ Chart ไม่ใช่ widget add_series() กี่ครั้งก็ยังเป็น Chart ตัวเดียว แต่ Panel นับทุกใบ จึงได้ Panel + หัวข้อ + Chart + Label ค่า = 4

  4. ข้อใดถูกต้องเกี่ยวกับเพดาน 64 widgets และงบ 32 ของชุดบทเรียนนี้ เลือกทุกข้อที่ถูก (เป้าหมายข้อ 3)

    1. widget ตัวที่ 65 ไม่ขึ้นจอ และไม่มีข้อความเตือนอะไรเลย
    2. เพดานมาจากตารางขนาดคงที่ฝั่ง CM55 ที่จองไว้ตั้งแต่คอมไพล์
    3. 32 คืองบที่ชุดบทเรียนนี้ตั้งเอง เพื่อเหลือที่ให้บทเรียน 4.1–5.3 ต่อยอด
    4. Dev Kit รับได้มากกว่า Eva Kit เพราะมีเซนเซอร์มากกว่า
    5. ถ้าเกินงบ เฟิร์มแวร์จะขยายตารางให้เองตอนรัน
    ดูเฉลย

    คำตอบ: A. widget ตัวที่ 65 ไม่ขึ้นจอ และไม่มีข้อความเตือนอะไรเลย · B. เพดานมาจากตารางขนาดคงที่ฝั่ง CM55 ที่จองไว้ตั้งแต่คอมไพล์ · C. 32 คืองบที่ชุดบทเรียนนี้ตั้งเอง เพื่อเหลือที่ให้บทเรียน 4.1–5.3 ต่อยอด

    UI_MAX_WIDGETS เท่ากันทั้งสองบอร์ดเพราะใช้ ipc_ui ชุดเดียวกัน ตารางถูกจองขนาดตายตัวเพื่อไม่ต้องขอหน่วยความจำระหว่างวาดจอ ตัวที่เกินจึงหายเงียบ ๆ งบ 32 เป็นวินัยที่เราตั้งเอง

  5. ต้องการการ์ดพื้น BG_CARD ขอบสี COL_IMU มุมโค้ง 12 px และขอบหนา 2 px คำสั่งใดถูกต้อง (เป้าหมายข้อ 4)

    1. ui.Panel(x=24, y=100, w=368, h=136, color=BG_CARD, min=COL_IMU, max=12, value=2)
    2. ui.Panel(x=24, y=100, w=368, h=136, color=COL_IMU, min=BG_CARD, max=2, value=12)
    3. ui.Panel(x=24, y=100, w=368, h=136, color=BG_CARD, min=0, max=100, value=COL_IMU)
    4. ui.Panel(x=24, y=100, w=368, h=136, color=BG_CARD, min=12, max=COL_IMU, value=2)
    ดูเฉลย

    คำตอบ: A. ui.Panel(x=24, y=100, w=368, h=136, color=BG_CARD, min=COL_IMU, max=12, value=2)

    สำหรับ Panel color คือสีพื้น min คือสีขอบ max คือรัศมีมุม value คือความหนาขอบ ชื่อเดียวกับ Slider แต่ไม่ใช่ค่าต่ำสุดสูงสุด อย่าเดาจากชื่อ และต้องสร้าง Panel ก่อน Label ที่วางทับ ไม่งั้นการ์ดจะบังตัวหนังสือ

อ้างอิงบทเรียนนี้

ถ้านำบทเรียนนี้ไปสอน ทำสไลด์ หรือทำเอกสารต่อ ให้อ้างอิงด้วยข้อความนี้ ถ้าดัดแปลงเนื้อหา ให้เติม (ดัดแปลง)ต่อท้ายชื่อบทเรียน

"ออกแบบ HMI: การ์ด ลำดับสายตา สี และงบ widget" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0

ข้อความอ้างอิงภาษาอังกฤษ: "HMI design: cards, visual order, colour and the widget budget" from TESA Open Knowledge by the Thai Embedded Systems Association (TESA), https://github.com/tesaiot/tesa-qualification-program, licensed under CC BY-NC 4.0

ลิงก์บทเรียน: https://tesaiot.github.io/tesa-qualification-program/courses/aiot-micropython/m03-sensor-hmi/l07-hmi-design/

บทเรียนนี้ดัดแปลงจากต้นฉบับด้านล่าง เมื่ออ้างอิงให้คงเครดิตต้นฉบับไว้ด้วย
https://github.com/Advance-Innovation-Centre-AIC/embedded-systems-for-aiot-developer/blob/a80bbe88a34bcb9bb8d991f42f9252b77cdab079/session-08.html (slides 1–17)

วิธีอ้างอิง TESA ฉบับเต็ม

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0

เนื้อหาเผยแพร่ภายใต้ CC BY-NC 4.0 นำไปใช้ต่อในงานที่ไม่ใช่เพื่อการค้าได้ โปรดอ้างอิงสมาคมสมองกลฝังตัวไทย (TESA) ทุกครั้ง · วิธีอ้างอิง TESA