| อาการ | สาเหตุที่แท้จริง | วิธีแก้ |
|---|---|---|
| จอขึ้นครบแต่ทุกค่าค้างที่ 0 | ยังไม่ได้เติมจุดอ่านค่าในลูป | เติม sensors.pot.percent() / capsense.slider() |
TypeError ตอนสร้างฟิลเตอร์ |
เขียน dsp.EMA(0.2) |
keyword-only: dsp.EMA(alpha=0.2) |
| บรรทัด EMA เท่ากับ RAW ทุกรอบ | สร้าง dsp.EMA() ไว้ในลูป ความจำถูกล้าง |
ย้ายไปสร้างครั้งเดียวนอกลูป |
| ปุ่มสัมผัสรายงานกลับด้าน | วางนิ้วค้างตอน บอร์ดบูต | ยกนิ้วออกให้หมดแล้ว ถอด USB เสียบกลับ (Dev Kit: ห้ามโยกสวิตช์บนฐาน นั่นคือสวิตช์ไฟ) |
OSError ที่บรรทัด sensors.init() |
บน Eva Kit เฟิร์มแวร์ปฏิเสธคำสั่งนี้ (บน Dev Kit ผ่านเงียบ ๆ แต่ไม่จำเป็น) | ลบบรรทัดนั้นทิ้ง ไม่ต้องมี init เลย ทั้งสองบอร์ด |
OSError: ... CM55 did not answer ที่บรรทัดแรก ๆ |
เพิ่งรีเซ็ต คอร์จอยังไม่ตอบสายเซนเซอร์ | รอสักครู่แล้วรันซ้ำ ถ้ายังซ้ำค่อยแจ้งผู้สอน |
| widget หายไปเป็นวินาที จอกระตุก | ลืม ui.poll() ในลูป |
ใส่ ui.poll() ทุกรอบก่อน sleep_ms |
ui.Bar ไม่ขยับทั้งที่ค่าเปลี่ยน |
ส่งค่าทศนิยมเข้าไป | ครอบด้วย int() และคุมช่วง 0-100 |
| อาการ | สาเหตุที่แท้จริง | วิธีแก้ |
|---|---|---|
ui.Scale ไม่ขยับเลยสักครั้ง |
มันคือไม้บรรทัด ไม่รับ .value() |
ตัวที่ต้องขยับคือ ui.Bar ที่วางทับ (แบบวงกลมมีเข็มจริงผ่าน .prop(ui.PROP_SCALE_NEEDLE, ...) — fw 2026-08-20 ขึ้นไป ดูบทเรียน 3.1–3.3) |
| ตัวเลขบนไม้บรรทัดเบียดกันจนอ่านไม่ออก | ขีดเยอะเกินไปสำหรับความกว้างที่มี | .ticks(ทั้งหมด, ใส่เลขทุกกี่ขีด) เช่น (11, 2) |
ui.Spinbox แตะแล้วค่าไม่เปลี่ยน |
จอสัมผัสไม่มีลูกบิดหมุน การแตะแค่เลือกตำแหน่งหลัก | ต้องมี ui.Button เพิ่ม/ลดข้าง ๆ เสมอ |
ช่องเกณฑ์ขึ้น 0070 ทั้งที่ตั้ง 70 |
ค่าตั้งต้นของ spinbox คือสี่หลัก | sp.digits(2, 0) = สองหลัก ไม่มีจุดทศนิยม |
| ไฟดับแล้วยังเห็นเป็นวงจาง ๆ | ตั้งใจ .value(0) คือหรี่ ไม่ใช่หาย |
ไฟที่หายไปทำให้แยกไม่ออกว่าดับหรือจอเสีย |
| เปลี่ยนสีแถบตอนเกินเกณฑ์แล้วไม่ได้ผล | .color() ของ ui.Bar ไปลงที่ "ราง" ไม่ใช่แถบค่า |
บอกสถานะด้วยไฟกับตัวหนังสือแทน |
| ค่าเซนเซอร์ค้างเป็นค่าเดียวตลอด | ใช้ ui.* แล้ว sensor auto-task หยุด |
อ่านเซนเซอร์เองในลูปทุกรอบ |
OSError ที่บรรทัด sensors.scan() |
บน Eva Kit เฟิร์มแวร์ปฏิเสธเพื่อกันบัสชนกัน (บน Dev Kit ผ่าน) | ห้ามใช้คำสั่งนี้ในคอร์สนี้ทั้งสองบอร์ด ใช้ sensors.snapshot() แทน |
| อาการ | สาเหตุที่แท้จริง | วิธีแก้ |
|---|---|---|
AttributeError ตอนอ่าน pot |
รันบน AI Kit เปล่า ๆ ที่ไม่มีฐาน | ลูกบิดและ CapSense มีบน Eva Kit และ Dev Kit |
AttributeError: sht40 / dps368 |
สองชิปนี้ไม่มีบน Eva Kit ธง BSP ตั้งไว้ 0 (Dev Kit มี) | บน Eva ไม่มีทางแก้ด้วยโค้ด ใช้ตัวเลขที่พิมพ์เองป้อน dsp.dew_point() แทน |
Median(window=4) แล้วได้ 5 |
หนีบ 3-15 และบังคับเป็นเลขคี่ เงียบ ๆ | ไม่ใช่บั๊ก · print(med) บอกค่าที่ได้จริง |
SMA(window=200) แล้วนิ่งน้อยกว่าที่คิด |
หนีบเพดานไว้ 64 เงียบ ๆ เหมือนกัน | ขอเกินได้ แต่ไม่ได้ตามขอ ตรวจด้วย print() |
HPF บรรทัดแรกได้ 0.0 ทุกครั้ง |
ยังไม่มีค่าก่อนหน้าให้ลบ | ปกติ ทิ้งค่าแรกไปหนึ่งรอบ |
LPF(cutoff=2, fs=100) แต่กรองไม่เหมือนที่คำนวณ |
ลูปจริงเดิน 5 Hz ไม่ใช่ 100 Hz | fs ต้องเท่ากับคาบลูปจริง ไม่ใช่ค่าตั้งต้น |
sensors.auto_rate(5) แล้วไม่มีอะไรเปลี่ยน |
หนีบไว้ 20 ms และบน Eva Kit งานเบื้องหลังก็ไม่ได้เดินอยู่แล้ว | "เรียกได้" ไม่ได้แปลว่า "มีผล" |
เจ็ดในยี่สิบสามข้อนี้ไม่ได้เกิดจากโค้ดผิด แต่เกิดจาก ลำดับการทำงานกับฮาร์ดแวร์ อีกสี่ข้อเกิดจาก ค่าที่ถูกหนีบเงียบ ๆ และแถวส่วนใหญ่ของตารางที่ 2 เกิดจาก การเข้าใจ widget ผิดตัว — ทั้งสามกลุ่มไม่มี error ให้เห็นสักบรรทัด
เปิด s05_pot_capsense.py มีช่องว่างให้เติม 6 จุด
# เติม: ema = dsp.EMA(alpha=0.2)
pass
while True:
try:
# เติม: pct = sensors.pot.percent()
pass
# เติม: slider = sensors.capsense.slider()
pass
except OSError:
fresh = False
# เติม: pot_bar.value(int(max(0, min(100, pct))))
pass
# เติม: ema_pct = ema.update(pct)
pass
# เติม: events = ui.poll()
events = []
pass
ไฟล์ฝึกตั้งค่าเริ่มต้นไว้ให้แล้ว (pct = 0.0, slider = 0, events = []) โปรแกรมจึง รันได้ตั้งแต่ยังไม่เติมอะไรเลย แต่ตัวเลขเปอร์เซ็นต์ แถบ และค่ากรองจะนิ่งสนิท (ค่าดิบกับโวลต์ขยับตั้งแต่แรก เพราะสองบรรทัดนั้นไม่มีช่องว่าง) ใช้ตรงนี้เป็นเครื่องมือ: เติมทีละจุด รันทีละครั้ง แล้วดูว่าจอ "ตื่น" ขึ้นทีละส่วน
หน้าจอทั้ง 33 ชิ้นเขียนไว้ให้ครบแล้ว ไม่ต้องแตะ งานของเราคือทำให้ค่าไหลเข้าไปในนั้น จุดที่ 6 คือจุดที่คนข้ามบ่อยที่สุด เพราะไฟล์รันได้อยู่แล้วโดยไม่มีมัน อาการที่ตามมาคือปุ่มเพิ่ม/ลดเกณฑ์กดไม่ติด ซึ่งไม่มี error ให้เห็นเลยสักบรรทัด
ต้องทำในบทเรียน · เปิดตามลำดับนี้ ทั้งชุดราว 40 นาที
| ลำดับ · เรื่อง · เวลา | ไฟล์ | ลงมือทำอะไร แล้วจะเข้าใจอะไร |
|---|---|---|
| 1 · เลขจาก ADC ไม่ใช่โวลต์ · 10 นาที | 05_adc_counts_to_volts.py |
ลดจำนวนบิตทีละท่าแล้วดูบันไดหยาบขึ้น · จะแปลงเลขดิบเป็นโวลต์ได้เอง เพราะรู้แล้วว่าต้องรู้แรงดันเต็มสเกลกับจำนวนบิตเสมอ |
| 2 · แถบสัมผัสคุมความสว่าง · 15 นาที | 01_capsense_dimmer.py |
ลากนิ้วแล้วปล่อย แล้วดูว่าเส้นแดงค้างอยู่ตอนไม่มีนิ้ว · จะรู้ว่า slider() เท่ากับ 0 ไม่ได้แปลว่าปล่อยนิ้ว ต้องดูว่าค่าหยุดเปลี่ยนแทน |
| 3 · alpha แปลเป็นวินาทีได้ · 15 นาที | 06_ema_time_constant.py |
เดินทีละท่าเปลี่ยน alpha แล้วอ่านค่า tau ที่ไฟล์คำนวณให้ · จะเลือก alpha ของ MVP จากตัวเลข ไม่ใช่จากการเดา |
ติดตรงไหน เปิดอันนี้
| อาการที่เจอ | ไฟล์ที่ตอบอาการนั้น |
|---|---|
| แตะแถบสัมผัสแล้วไม่แน่ใจว่าบอร์ดรับไปหรือยัง โดยเฉพาะตอนมือเปียก | 02_capsense_menu_wet_hand.py — ปุ่มสัมผัสไม่มีแรงต้านให้นิ้วรู้สึก จึงต้องตอบกลับด้วยจอ ไฟ หรือเสียงทุกครั้ง |
| กราฟที่กรองแล้วยังกระโดดตามค่าหลุดค่าเดียวอยู่ดี | 07_median_beats_mean.py — ค่าเฉลี่ยเอาค่าที่หลุดมาบวกด้วย ส่วน median เรียงแล้วหยิบตัวกลาง ค่าหลุดจึงไม่มีสิทธิ์ถูกหยิบ · หน้าต่าง N ทน spike ที่ติดกันได้ไม่เกิน (N-1)//2 ตัว |
| ปล่อยมือจากลูกบิดแล้ว ค่าที่รายงานยังกระดิกไม่หยุด | 03_pot_setpoint_deadband.py — dead-band ตัดการกระดิกทิ้งโดยผู้ใช้ไม่รู้สึกว่าเสียอะไร นิสัยนี้ใช้ยาวไปถึงบทเรียน 4.4–4.6 ตอนต้องลดจำนวนข้อความที่ส่งออก |
อ่านเสริมนอกเวลา — เรื่องนี้อยู่นอกเกณฑ์ผ่านของบทเรียน 2.7–2.9 แต่ตอบคำถามที่ทีมเสียงมักติด: 04_pot_taper_volume.py ครึ่งทางของลูกบิดไม่ใช่ครึ่งหนึ่งของความดังที่หูได้ยิน หูตอบสนองเป็นลอการิทึม การแมปตรง ๆ จึงรู้สึกว่าดังพรวดตั้งแต่ยังหมุนไม่ถึงไหน
โปรแกรมที่รันได้แต่ยังไม่ถูกต้อง ดีบักง่ายกว่าโปรแกรมที่รันไม่ขึ้นเสมอ
ฝั่งอิเล็กทรอนิกส์และการวัด
การแปลงอนาล็อกเป็นดิจิทัล · ความละเอียด (บิต) กับช่วงการวัด · แรงดันอ้างอิงและผลของมันต่อค่าที่อ่านได้ · การตรวจจับแบบ capacitive · แนวคิด baseline และการวัดแบบส่วนต่าง
ฝั่งการประมวลผลสัญญาณ
noise มาจากไหนและมีกี่ชั้น · ค่าเฉลี่ยถ่วงน้ำหนักแบบมีความจำ (EMA) · ตัวกลางแบบหน้าต่างเลื่อน (Median) · การแลกกันระหว่างความนิ่งกับความหน่วง
ฝั่ง Python และการออกแบบระบบ
ออบเจกต์ที่เก็บสถานะข้ามรอบลูป · keyword-only arguments · การตรวจขอบเขตค่าจากภายนอก · การออกแบบหน้าจอให้หนึ่งค่าเล่าได้หลายหน่วย
เรื่อง trade-off ระหว่าง "นิ่ง" กับ "ไว" จะกลับมาหาผู้เรียนอีกในบทเรียน 3.1–3.3 และ 3.4–3.6 แต่คราวนั้นข้อมูลจะมาจาก IMU
วันนี้เราได้:
อ่านค่าอนาล็อกจากลูกบิดครบสามหน่วยและอธิบายที่มาของแต่ละหน่วยได้ · เข้าใจว่า ADC ปัดค่าอย่างไรและทำไมบิตล่างถึงกระพริบ · อ่านปุ่มสัมผัสและแถบเลื่อนพร้อมรู้เรื่อง baseline · ใช้ dsp.EMA และ dsp.Median แล้วเห็นผลเทียบกันบนจอเดียว
การบ้านของทีม: เลือกทำ 1 ข้อจากสี่ข้อในสไลด์ถัดไป จดลงบันทึกการเรียน
ชุดบทเรียนถัดไป: เราจะเปลี่ยนจากลูกบิดที่มีคนหมุน ไปเป็นเซนเซอร์ที่วัดโลกจริง — sensors.bmi270.motion() กับ dsp.tilt() แล้วสร้างเครื่องวัดระดับดิจิทัลที่บอกองศาการเอียงของบอร์ดได้จริง ฟิลเตอร์ที่เรียนวันนี้จะได้ใช้ทันทีในชุดบทเรียนถัดไป
เก็บค่า
alphaที่ทีมชอบไว้ให้ดี ชุดบทเรียนถัดไปจะได้ไม่ต้องลองใหม่ตั้งแต่ศูนย์
s05_pot_capsense.py — ส่วนที่หนึ่ง: ตั้งเวทีอ่านให้เข้าใจ แล้วพิมพ์เอง อย่าคัดลอกวาง
import ui
ui.screen()
import time
import sensors
import dsp
ui.clear()
time.sleep_ms(200)
COL_TEXT, COL_DIM = 0xE8EAED, 0x9AA3AF
COL_CARD = 0x171B22
COL_OK, COL_WARN, COL_BAD = 0x30A46C, 0xF5A623, 0xE5484D
TH_MIN, TH_MAX, TH_STEP = 10, 95, 5
th = 70 # เกณฑ์ตั้งต้น หน่วยเปอร์เซ็นต์
# ไม่มี sensors.init() ในไฟล์นี้ และไม่ควรมี (ทั้งสองบอร์ด)
try:
sensors.pot.read() # อุ่นเครื่อง รอบแรกหลังรีเซ็ตอาจต้องรอคอร์จอตอบ
except OSError:
print("อ่านเซนเซอร์รอบแรกยังไม่ได้ - ลองใหม่ในลูป")
เฉลยรุ่นนี้เปลี่ยนจานสีทั้งชุด จากเดิมที่ให้ สีบอกหน่วย (เขียว = ค่าดิบ, ฟ้า = เปอร์เซ็นต์, เหลือง = โวลต์) มาเป็นจานสีของแผงควบคุมจริง ที่ สีบอกสถานะ เท่านั้น
เหตุผลไม่ใช่ความชอบ: สีเหลืองในหน้าจออุตสาหกรรมแปลว่า "เฝ้าระวัง" ตายตัว การเอาไปใช้กับบรรทัดโวลต์ที่ปกติดีทุกอย่าง คือการสอนให้ตาคนชินกับสีเตือน แล้ววันที่มันเตือนจริง จะไม่มีใครเห็น หน่วยของแต่ละบรรทัดบอกด้วย คำ ได้อยู่แล้ว ไม่ต้องยืมสีมาบอก
ค่าสีถูกตั้งเป็นค่าคงที่ชื่ออ่านรู้เรื่องไว้บนสุด ไม่ได้ใส่เลข 0x30A46C ลงไปกลางโค้ดตรง ๆ เพราะวันหนึ่งที่อยากเปลี่ยนธีมทั้งจอ เราจะแก้ที่เดียวจบ — ค่าทั้งชุดคือจานสีของหลักสูตร (พื้น 0x0E1116 · การ์ด 0x171B22 · ปุ่มรอง 0x3A4150 · ตัวหนังสือ 0xE8EAED · จาง 0x9AA3AF · ปกติ/เตือน/เสีย 0x30A46C 0xF5A623 0xE5484D) · การอุ่นเครื่องอยู่ใน try/except เพราะ OSError รอบแรกหลังรีเซ็ตเป็นเรื่องปกติทั้งสองบอร์ด ไม่ใช่ความผิดพลาด
เลขฐานสิบหกที่โผล่กลางโค้ดโดยไม่มีชื่อ คือหนี้ที่คนอ่านคนถัดไปต้องมาจ่าย
ui.Label("แผงคุมลูกบิดกับแถบสัมผัส", x=16, y=8, color=COL_TEXT, value=24)
# การ์ดบนซ้าย - ค่าที่วัดได้ พร้อมพิสัยของมันเอง
ui.Panel(x=12, y=40, w=468, h=200, color=COL_CARD, min=COL_DIM, max=12, value=1)
ui.Label("ลูกบิดเทียบพิสัย 0-100", x=28, y=48, color=COL_DIM, value=20)
pot_bar = ui.Bar(x=28, y=80, w=428, h=16, color=0x4A9EFF, min=0, max=100, value=0)
pot_scale = ui.Scale(x=28, y=92, w=428, h=44, color=COL_TEXT, min=0, max=100)
pot_scale.ticks(11, 2)
...
lbl_pct = ui.Label("0.0 %", x=28, y=180, color=COL_TEXT, value=28)
lbl_raw = ui.Label("ค่าดิบ 0", x=252, y=148, color=COL_DIM, value=16)
lbl_volt = ui.Label("โวลต์ 0.000 V", x=252, y=172, color=COL_DIM, value=16)
# การ์ดบนขวา - เกณฑ์ที่ผู้ใช้ตั้งเอง กับไฟสองดวง
ui.Panel(x=492, y=40, w=288, h=224, color=COL_CARD, min=COL_DIM, max=12, value=1)
...
sp_th = ui.Spinbox(x=508, y=76, w=256, h=88, color=COL_TEXT,
min=TH_MIN, max=TH_MAX, value=th)
sp_th.digits(2, 0)
btn_up = ui.Button("เพิ่ม", x=644, y=172, w=120, h=88, color=0x3A4150, value=16)
btn_dn = ui.Button("ลด", x=508, y=172, w=120, h=88, color=0x3A4150, value=16)
...
led_ok = ui.Led(x=492, y=304, w=48, h=48, color=COL_OK, value=1)
...
led_bad = ui.Led(x=492, y=348, w=48, h=48, color=COL_BAD, value=0)
นับ widget ให้ครบทุกครั้งก่อนรัน: 33 ชิ้น เกินงบของคอร์ส 32 ไปหนึ่งชิ้น (ยังต่ำกว่าเพดานเฟิร์มแวร์ 64) งานต่อยอดจึงต้องเอาชิ้นเดิมออกก่อนเพิ่มชิ้นใหม่ · ข้อเสนอ (ไม่บังคับ): ชิ้นที่ตัดได้ก่อนคือป้ายนิ่ง "อัลฟา 0.2" ทั้งในไฟล์ฝึกและเฉลย เพราะเป็นข้อความตายตัวที่ซ้ำกับ dsp.EMA(alpha=0.2) ในโค้ด และจะแสดงค่าผิดทันทีที่ลองเปลี่ยน alpha ตัดแล้วเหลือ 32 พอดีงบ · ui.Panel สี่ใบไม่ได้มีไว้ให้สวย มันคือเส้นที่บอกตาว่า "ของกลุ่มนี้เกี่ยวกันนะ" หน้าจอที่ของทุกชิ้นลอยอยู่บนพื้นเดียวกันหมด คนดูต้องจัดกลุ่มเองด้วยสายตาทุกครั้งที่มอง
ทุกตัวถูกสร้างด้วยข้อความตั้งต้นที่มีรูปแบบเหมือนตอนมีค่าจริง เช่น "โวลต์ 0.000 V" ไม่ใช่ "..." เพราะการจองความกว้างไว้ตั้งแต่แรกทำให้ข้อความไม่กระตุกเวลาความยาวเปลี่ยน
ข้อควรรู้เรื่องความยาวข้อความ: คำสั่งสร้าง widget มีท่อกว้าง 95 ไบต์ ส่วน .text() กว้าง 126 ไบต์ เฟิร์มแวร์ 2026-08-20 ขึ้นไปส่งส่วนที่เกิน 95 ไบต์ซ้ำทาง .text() ให้เอง (modui.c) ป้ายจึงพาได้ 126 ไบต์ทั้งตอนสร้างและตอน .text() ตรงกับบทเรียน 1.2 ภาษาไทยตัวละ 3 ไบต์ แปลว่าราว 42 ตัวอักษร ยาวกว่านั้น ถูกตัดเงียบ ๆ ไม่มี error ให้เห็น · เฟิร์มแวร์รุ่นก่อนตัดที่ 95 ไบต์ (ราว 31 ตัวอักษรไทย) ตั้งแต่ตอนสร้าง ถ้าป้ายยาวขาดกลางคำ ให้ตั้งสั้นตอนสร้างแล้วเขียนเต็มด้วย .text()
ออกแบบหน้าจอโดยคิดถึง "ตอนมีค่าจริง" เสมอ ไม่ใช่ตอนที่ยังว่างเปล่า
ema = dsp.EMA(alpha=0.2) # สร้างนอกลูป
last_sec = -1
while True:
fresh = True
try:
raw = sensors.pot.read()
volts = sensors.pot.voltage()
pct = sensors.pot.percent()
slider = sensors.capsense.slider()
b0, b1 = sensors.capsense.buttons()
except OSError:
fresh = False
b0, b1 = 0, 0
pot_bar.value(int(max(0, min(100, pct))))
touch_bar.value(max(0, min(100, slider)))
ema_pct = ema.update(pct)
over = ema_pct >= th # หนึ่งดวงติดเท่านั้น
led_ok.value(0 if over else 1)
led_bad.value(1 if over else 0)
led_b0.value(1 if b0 else 0)
led_b1.value(1 if b1 else 0)
for ev in ui.poll():
if ev.get("type") != "clicked":
continue
if ev.get("handle") == btn_up.id():
th = min(TH_MAX, th + TH_STEP)
sp_th.value(th)
elif ev.get("handle") == btn_dn.id():
th = max(TH_MIN, th - TH_STEP)
sp_th.value(th)
sec = time.ticks_ms() // 1000 # ตัวเลขเปลี่ยนวินาทีละครั้ง
if sec != last_sec:
last_sec = sec
lbl_pct.text("{:.1f} %".format(pct))
lbl_raw.text("ค่าดิบ {}".format(raw))
lbl_volt.text("โวลต์ {:.3f} V".format(volts))
raw_line.text("ดิบ {:.2f} %".format(pct))
ema_line.text("กรอง {:.2f} %".format(ema_pct))
time.sleep_ms(200)
try เดียว และเกณฑ์จากค่าที่กรองแล้วการอ่านทั้งห้าบรรทัดอยู่ใน try เดียวกันโดยตั้งใจ: ถ้ารอบนี้คอร์จอไม่ตอบ เราไม่อยากได้ครึ่งชุดเก่าครึ่งชุดใหม่ปนกันบนจอ ค่าทั้งหน้าจะเป็นชุดเดียวกันเสมอ และธง fresh จะไปบอกบรรทัดคุณภาพของค่าว่าตัวเลขที่เห็นตอนนี้เป็นของรอบก่อน
over = ema_pct >= th ใช้ค่าที่ กรองแล้ว ไม่ใช่ค่าดิบ ถ้าใช้ค่าดิบ ไฟจะกระพริบสลับดวงตอนค่าอยู่คาบเกี่ยวกับเกณฑ์พอดี ซึ่งเป็นอาการที่แผงควบคุมจริงเรียกว่า alarm chattering และเป็นเหตุผลหนึ่งที่คนหน้างานปิดเสียงเตือนทิ้ง
ลองแก้
alphaเป็น 0.05 แล้ว 0.8 ดู แล้วจ้องสองบรรทัดขวาล่างสิบวินาที — บทเรียนทั้งบทเรียนอยู่ในความต่างนั้น
ท่า 1 ตั้งจอ แล้วรอให้คอร์จอตอบ มาก่อน เพราะถ้าจอไม่ขึ้นหรือ sensors.pot.read() รอบอุ่นเครื่องยังขึ้น OSError ท่าที่เหลือไม่มีทางถูกต้องได้เลย
ท่า 2 ลูกบิดกับเกณฑ์ มาก่อน CapSense เพราะลูกบิดต่ออยู่กับ ADC ในชิปหลักโดยตรง ไม่ต้องพึ่งชิปตัวที่สองและไม่ต้องพึ่งบัส I2C — ถ้าท่านี้ผ่าน แปลว่าเส้นทาง sensor → Python → จอ ใช้ได้แล้วทั้งเส้น
ท่านี้พ่วง พิสัย (ui.Scale ใต้แถบ) กับ เกณฑ์ (ui.Spinbox และไฟสองดวง) ไว้ด้วยกัน เพราะมันคือคำถามเดียวกัน: ค่านี้เทียบกับอะไร — แยกไปคนละท่า ผู้เรียนจะจำได้แค่ว่า "มี widget อีกตัว" ไม่ใช่ว่า "ค่าที่วัดได้ห้ามอยู่ลำพัง"
ท่า 3 CapSense มาทีหลัง เพราะมันเพิ่มตัวแปรใหม่เข้ามาสองอย่าง (ชิป 4000T และบัส I2C) ถ้าพังตอนนี้ เรารู้ทันทีว่าพังที่ของใหม่ ไม่ใช่ที่ของเก่า
ท่า 4 ฟิลเตอร์กับบรรทัดคุณภาพของค่า ต้องมาหลังจากเห็นค่าดิบสั่นด้วยตาตัวเองแล้ว ไม่งั้นจะไม่มีวันรู้ว่ามันแก้ปัญหาอะไรให้ · สองอย่างนี้อยู่ท่าเดียวกันเพราะตอบคำถามเดียวกัน: "ตัวเลขที่เห็นเชื่อได้แค่ไหน"
ท่า 5 จังหวะลูป ปิดท้าย เพราะมันคือสิ่งที่ทำให้ทุกท่าข้างบนอยู่ร่วมกันได้อย่างเสถียรตลอดสิบนาที ไม่ใช่แค่สิบวินาทีแรก
คำถามคิดต่อ: ถ้าต้องส่งค่าลูกบิดขึ้นคลาวด์ทุก 5 วินาที จะส่งค่าดิบหรือค่าที่กรองแล้ว · การส่งค่าที่กรองแล้วทำให้เราสูญเสียข้อมูลอะไรไปบ้าง · ถ้าปลายทางเป็นระบบแจ้งเตือน ความหน่วงจากฟิลเตอร์มีราคาเท่าไร
ทั้งสี่มุมนี้ใช้ความรู้ชุดเดียวกับที่เพิ่งเขียนไป ต่างกันแค่ว่าค่าที่ผิดพลาดไปหนึ่งเปอร์เซ็นต์ ราคาของมันแพงแค่ไหน
ข้อ 1 · สนามทดลองค่า alpha — ทำสามบรรทัดเทียบกัน: RAW, EMA(0.05), EMA(0.5) บนจอเดียว หมุนเร็ว ๆ แล้วปล่อยนิ่ง จดว่าแต่ละเส้นตามทันต่างกันกี่วินาที
ข้อ 2 · Median ปะทะ spike — ใส่ dsp.Median(window=5) เพิ่มอีกบรรทัด แล้วจงใจสร้าง spike ด้วยการเคาะบอร์ด บันทึกว่า EMA กับ Median รับมือต่างกันอย่างไร
ข้อ 3 · ลูกบิดสั่งไฟจริง — ใช้ gpio.led() จากบทเรียน 2.1–2.3 คู่กับค่าเปอร์เซ็นต์: ยิ่งหมุนมากยิ่งติดหลายดวง — แบ่ง 0-100 % เป็นช่วงเท่ากันตาม gpio.num_leds() (Eva 3 ดวง = ดวงละ 33 % · Dev Kit 5 ดวง = ดวงละ 20 %) ไม่พิมพ์ 3 ตายตัว · กันกะพริบตรงรอยต่อด้วยช่วงเผื่อ ไม่ใช่จุดเดียว · ดวงที่ต้องเป็นสีให้หาตามชื่อใน gpio.board_info()["led_names"] แบบ led_named() ใน 01_imu_vibration_monitor.py
ข้อ 4 · ปุ่มสัมผัสสลับหน้าที่จอ — BTN0 สลับหน่วยของเกจ · BTN1 เปิด-ปิดฟิลเตอร์ · ตรวจขอบขาขึ้นแบบบทเรียน 2.1–2.3 กันแตะครั้งเดียวสลับหลายรอบ
เขียนคำตอบลงบันทึกการเรียน แล้วเอามาเล่าให้เพื่อนฟังต้นชุดบทเรียนถัดไป
ui.Roller
10_roller_picks_the_filter.py สร้าง สัญญาณบนกราฟไฟล์นั้นสร้างขึ้นเอง ไม่ได้มาจากลูกบิดหรือแผ่นสัมผัสชุดบทเรียนนี้มีตัวกรองหกตัวให้เลือก และวิธีเลือกก็เป็นบทเรียนของมันเอง — 08_six_filters_one_signal.py ใช้ปุ่ม "ตัวถัดไป" ซึ่งแปลว่าจะไปดู Kalman ต้องกดห้าครั้ง และตลอดเวลานั้นไม่มีใครเห็นว่ามีอะไรให้เลือกบ้าง
| ตัวเลือกยาว ๆ ทำด้วยอะไรได้บ้าง | ข้อเสียบนจอสัมผัส 4.3 นิ้ว |
|---|---|
| ปุ่ม "ตัวถัดไป" ที่กดวน | ซ่อนรายการทั้งชุด และไปตัวที่ห้าต้องกดห้าครั้ง |
ui.Dropdown |
ต้องแตะเปิดก่อนถึงจะเห็นตัวเลือก และรายการไปทับของอื่น |
ui.Roller |
กางตัวเลือกค้างไว้ เลื่อนด้วยนิ้วรวดเดียว ตัวที่เลือกอยู่ไฮไลต์ตลอดเวลา |
ui.Roller (ต่อ) — ใช้ที่ไหน และสองเรื่องที่วัดมาแล้วในงานจริงมันคือ widget ของ "ทางเลือกที่มีจำกัดและรู้ล่วงหน้า" — โหมดเครื่อง ช่วงพิสัยของหัววัด กะการทำงาน ปลายทางที่จะส่งข้อมูล ทั้งหมดนี้เป็นชุดที่ปิดแล้ว ไม่ใช่ของที่ผู้ใช้พิมพ์เอง
สองเรื่องที่วัดมาแล้วและต้องรู้ก่อนใช้
value= ตอนสร้างทำสองหน้าที่พร้อมกัน คือ บรรทัดที่เลือก และ ขนาดฟอนต์ไทย (ui_widget_mgr.c ส่ง cfg->init_val เข้า ui_apply_content_font() ตัวเดียวกัน) ตั้ง value=24 เพราะอยากได้ตัวหนังสือขนาด 24 จะได้บรรทัดที่ 24 แถมมาด้วย — ไฟล์ตัวอย่างจึงไม่ตั้ง value= เลย แล้วสั่งเลือกด้วย .value(n) ทีหลัง.prop(ui.PROP_VISIBLE_ROWS, n) เขียนทับความสูงที่ตั้งไว้ด้วย h= และคำนวณจากฟอนต์ธีม ไม่ใช่ฟอนต์ไทยที่วงล้อใช้จริง วัดบนตัวจำลอง: ขอ 5 บรรทัด ได้กล่องเตี้ยลงจนเห็นจริง 3 บรรทัด — เลือกอย่างใดอย่างหนึ่ง อย่าสั่งทั้งสองทางจุดที่ต้องมองตอนตรวจภาษาไทย — ดูที่แถบสีเน้น ไม่ใช่ที่บรรทัดอื่น เพราะ LVGL วาดบรรทัดที่เลือกจากส่วน
LV_PART_SELECTEDคนละส่วนกับบรรทัดที่เหลือ ตั้งฟอนต์ไทยที่MAINอย่างเดียวจะได้ตัวอักษรทุกบรรทัดยกเว้นบรรทัดที่เลือก



เอกสารและตำรา
ภาพ
ทุกตัวเลขบนสไลด์ชุดนี้สืบกลับไปที่เอกสารต้นทางหรือซอร์สโค้ดได้ ยกเว้นข้อที่เขียนกำกับไว้ว่ายังต้องวัด
ตัวอย่างโค้ด — โฟลเดอร์ examples/ ของบทเรียน 2.7–2.9 สิบไฟล์ (ใบล่าสุดคือ 10_roller_picks_the_filter.py) · 07_median_beats_mean.py ย้ายมาจาก โฟลเดอร์ examples/ ของบทเรียน 3.1–3.3 เมื่อ 14 ส.ค. เพราะเรื่องที่มันสอนคือการกรองสัญญาณ ซึ่งเป็นเนื้อหาของชุดบทเรียนนี้ · เพิ่มใหม่ 15 ส.ค. 08_six_filters_one_signal.py ครอบ SMA LPF HPF Kalman1D ที่เดิมไม่มีตัวอย่างเลยสักไฟล์ และ 09_sensors_api_tour.py เรียกเก้าฟังก์ชันของโมดูล sensors จริง ๆ เพื่อพิสูจน์ว่าใครตอบใครปฏิเสธ · ตัวอย่าง SMA ที่เคยอยู่ใน examples/dsp/ ถูกถอดออกจากคลังเมื่อ 14 ส.ค. โฟลเดอร์นั้นจึงว่างอยู่
ข้อที่ยังไม่ลงตัว — ต้องวัดก่อนสอน
แรงดันอ้างอิงของ pot: ผังวงจร Eva แสดง VDD_1V8 แต่ sensors.pot.voltage() ของเฟิร์มแวร์คูณด้วย 3.3 V ที่สมมติไว้ ทั้งสองบอร์ด — ห้ามใช้ตัวเลขใดตัวเลขหนึ่งเป็นข้อเท็จจริงจนกว่าจะวัดที่ขาลูกบิดของบอร์ดจริง (Eva: P15[1] · Dev Kit: VR1) · ทิศหมุนและ taper ของ VR1 บน Dev Kit ก็ยังไม่ได้วัด
เปลี่ยนจากเฟิร์มแวร์รุ่นก่อน — บน Eva Kit ถูกปฏิเสธด้วย OSError ทั้งหมด ห้าตัว ไม่ใช่สองตัว: init() scan() push() live_push() auto() · read_all() ไม่ถูกปฏิเสธ แต่บน Eva มันคืนผลของ snapshot() ตัวเดียวกัน · auto_rate() และ auto_status() เรียกได้ตามปกติ · sensors.pot.* / sensors.capsense.* / sensors.bmi270.* อ่านผ่าน snapshot ของคอร์จอ ส่วน sensors.bmm350.* อ่านตรงจากชิปได้ เพราะมันอยู่คนละบัส (I3C ขา P3[0]/P3[1]) ที่คอร์จอไม่ได้ถือไว้ · บน TESAIoT Dev Kit ห้าตัวนั้นทำงานจริง (live_push() วนจนกด Ctrl+C) · read_all() คืน pot เป็น float · snapshot() มีแล้วและคืน dict หน้าตาเดียวกับ Eva — IMU ในนั้นอ่านสดจาก CM33 ส่วนช่อง pot กับ capsense มาจากคอร์จอที่อ่านทุก 200 ms · sensors.pot.read() ต่างหากที่อ่าน SAR ตรง · มี dps368 sht40 radar เพิ่ม · ตรวจจากซอร์ส BENTO-TESAIoT-libraries/claw/common/mpy/modsensors.c และ moddsp*.c โดยตรง
ข้อที่เขียนกำกับว่ายังต้องวัด ให้วัดก่อนขึ้นสอน แล้วค่อยเขียนตัวเลขลงสไลด์
หลักเดิมจากชุดบทเรียนก่อน ๆ ยังใช้ได้: เพิ่มตัวแปรใหม่ทีละหนึ่ง แล้วทุกความผิดพลาดจะชี้กลับไปที่สิ่งที่เราเพิ่งเพิ่มเสมอ