ถ้าเราพล็อต ตามเวลา จะเห็นว่าตอนเขย่ามันพุ่งทะลุเส้น 1.8g แค่ไม่กี่เฟรมแล้วตกกลับ ตอนตกอิสระก็ดิ่งใกล้ 0 ชั่วขณะเดียวกัน

COOLDOWN กันไม่ให้ส่งเตือนรัว ๆ จากเหตุเดียวบอร์ดที่ล้มทำหน้าที่ publisher, บอร์ดที่เหลือเป็น subscriber — แต่ในโค้ดของเรา บอร์ดเดียวเป็นทั้งคู่พร้อมกัน
payload สั้น ๆ: "SHAKE board01" หรือ "FALL board01" — คำแรกบอกชนิดเหตุ (เขย่า/ตกอิสระ) ตามด้วยชื่อบอร์ดที่เกิดเหตุ
ก่อนเข้าโค้ด ขอวางกฎให้เห็นภาพก่อน เรามีแค่เลขก้อนเดียว (mag) แล้วเทียบกับเส้นบน/ล่างสองเส้น

โค้ดในหน้าถัดไปคือกฎสามแถบนี้เป๊ะ ๆ: if mag > THRESHOLD (แดง) · elif mag < FALL_LOW (น้ำเงิน) · ที่เหลือปล่อยผ่าน (เขียว)
ขนาดรวมพุ่งสูง = เขย่า/กระแทก · ดิ่งใกล้ 0 = ตกอิสระ เราเลือกชนิดเหตุจาก mag แล้ว publish

โค้ดข้างล่างคือสายงานนี้เป๊ะ ๆ: อ่าน 3 แกน → หาร 9.81 เป็น g → รวมเป็น mag ก้อนเดียว → เทียบสองเส้น → ถ้ามีเหตุและพ้น COOLDOWN ก็ publish
ax, ay, az = sensors.bmi270.acceleration() # เซนเซอร์คืน m/s²
ax, ay, az = ax/9.81, ay/9.81, az/9.81 # แปลงเป็น g (วางนิ่ง mag รวม ≈ 1.0)
mag = math.sqrt(ax * ax + ay * ay + az * az)
now = time.time()
event = None # เลือกชนิดเหตุจากขนาดรวม
if mag > THRESHOLD:
event = "SHAKE" # พุ่งเกิน ~1.8g = เขย่า/กระแทก
elif mag < FALL_LOW:
event = "FALL" # ดิ่งใกล้ 0 = ตกอิสระ
if event is not None and (now - last_fire) > COOLDOWN:
payload = "%s %s" % (event, BOARD_ID) # "SHAKE board01" / "FALL board01"
mqtt.publish(TOPIC, payload)
print("ตรวจพบ %s mag=%.2f -> ส่ง '%s'" % (event, mag, payload))
last_fire = now
THRESHOLD = 1.8 / FALL_LOW = 0.4 ลองปรับดู เขย่าเบา ๆ ยังไม่ถึง แต่กระแทกแรงต้องเกินCOOLDOWN กันส่งซ้ำรัว ๆ — เกิดเหตุครั้งเดียวควรเตือนครั้งเดียว ไม่ใช่ทุกเฟรมget_message() ไม่บล็อก ไม่มีของก็คืน None เราจึง poll สลับกับการอ่านเซนเซอร์ในลูปเดียวได้

msg = mqtt.get_message() # คืน (topic, bytes) หรือ None
if msg is not None:
topic, data = msg
text = data.decode() # payload เป็น bytes ต้อง decode
print("ได้แจ้งเตือน:", text)
if text.startswith("SHAKE") or text.startswith("FALL"):
blink_alert() # game.sfx("deny") + led.toggle วน 6 รอบ
time.sleep_ms(50)
def blink_alert():
game.sfx("deny") # เสียงเตือนจากเอนจินเสียงบนบอร์ด
for _ in range(6):
led.toggle()
time.sleep_ms(120)
led.off()
โค้ดเดียวเป็นทั้ง publisher และ subscriber บอร์ดที่ล้มจึงได้ยินเสียงเตือนตัวเองด้วย
เสียงในคาบนี้ — คาบนี้ไม่ใช่เกม แต่
game.sfx("deny")ในตัวเตือน (blink_alert) คือเสียงui_denyตัวเดียวกับที่เกมใช้ตอนกดผิด เอามาทำ "เสียงเตือนภัย"; ถ้าต่อยอดเป็นหลายระดับความรุนแรงgame_overใช้เป็นเสียงเหตุหนักได้ (กดเล่นฟังได้จริง):
มินิโปรเจกต์ที่ดูเหมือนแค่ "เขย่าแล้วไฟกระพริบ" จริง ๆ คือต้นแบบย่อ ๆ ของระบบ condition monitoring ในงานอุตสาหกรรม ลองดูว่าแต่ละบรรทัดแตะรากฐานอะไร
ฝั่ง Embedded / สัญญาณ
ฝั่ง Python / Algorithm
COOLDOWN คือ debounce/hysteresis กันสัญญาณรบกวนสั่งงานซ้ำ ใช้ได้ทั้งปุ่มกด เซนเซอร์ และระบบควบคุมBosch MEMS sensors: Working principle of an accelerometer — Bosch Sensortec · Bosch คือผู้ผลิต BMI270 บนบอร์ดเรา คลิปนี้โชว์ว่าข้างในชิปมีโครงสร้างจิ๋วขยับยังไง ค่า ax/ay/az ถึงเกิดขึ้นได้
วิธีรัน: เปิด codes/ex_b3_alert.py ใน BENTO IDE แล้วกด Program to Device
TOPIC ตัวเดียวกัน เขย่าตัวหนึ่ง อีกตัวเตือนwifi.connect() จะผ่านทันที
ต่อยอดเลือก 1 อย่าง:
"FALL board01 2.7" แนบค่า mag แล้วฝั่งรับ split(" ") มาแสดงนี่คือแก่นของ IoT จริง ๆ: เซนเซอร์ตัวเล็ก ๆ คุยกันผ่านเครือข่าย แล้วกลายเป็นระบบที่ทำงานร่วมกันได้
fit-css