MQTT กับแพลตฟอร์มที่ติดตั้งเอง: telemetry และ command
โมดูล 4 — เชื่อมต่อแพลตฟอร์ม IoT · สไลด์: slides.md · ภาพรวมโมดูล · หน้าหลักสูตร
ใช้โมดูล mqtt หกชื่อส่ง JSON จากบอร์ดขึ้น TESAIoT CE ที่คุณติดตั้งเองและรับคำสั่งกลับมา โดยรู้ทันกับดักพอร์ต 1883 การขึ้นทะเบียนอุปกรณ์ และเพดานเงียบของแต่ละฟังก์ชัน
เป้าหมาย
หัวข้อที่มีชื่อว่า “เป้าหมาย”เมื่อจบบทเรียนนี้ คุณจะ:
- ส่ง JSON ใบแรกขึ้น broker ด้วย
03_connect_and_publish.pyโดยตรวจค่าที่mqtt.connect()คืนก่อนทำงานต่อ และบอกได้ว่าทางออกทั้งสามของmqtt.publish()(True · False · OSError) แต่ละทางแปลว่าอะไร - เตรียม TESAIoT CE ให้บอร์ดใน LAN เข้าถึงได้ โดยแก้การเผยแพร่พอร์ตเป็น
0.0.0.0:1883:1883ขึ้นทะเบียนอุปกรณ์ด้วยdevice_idสั้นไม่เกิน 31 ตัวอักษร และตั้งclient_id == username == device_idจน MQTT Explorer ที่ subscribedevice/#เห็นข้อความของทีม - รับคำสั่งจาก MQTT Explorer ด้วยการ subscribe ครั้งเดียวแล้วถาม
get_message()ทุกรอบลูป 100 ms ตรวจis not Noneแปลง bytes ด้วย.decode()และครอบjson.loads()ด้วยtry/except ValueErrorจนข้อความที่ไม่ใช่ JSON ไม่ทำให้โปรแกรมหยุด - อธิบายได้ว่าทำไมพอร์ต 1883 เป็นแค่สนามซ้อมที่ปิดล้อม และทำไมการเรียก
mqtt.disconnect()ตอนเลิกใช้ตามตั้งใจ ทำให้ต่อใหม่ด้วยclient_idเดิมได้ทันที
ก่อนเริ่ม
หัวข้อที่มีชื่อว่า “ก่อนเริ่ม”ทวนบทเรียน 4.4: topic สองรูป (bento/<รหัสของคุณ>/telemetry บน broker สาธารณะ และ device/<device_id>/telemetry บน TESAIoT CE)
เพดาน 31 ตัวอักษรของ client_id / username / password และช่องรับข้อความที่มีช่องเดียว
เตรียมคอมที่มี Docker และ RAM อย่างน้อย 8 GB สำหรับติดตั้ง TESAIoT Community Edition ติดตั้ง MQTT Explorer ไว้
และให้บอร์ดกับคอมอยู่บน WiFi วงเดียวกัน (WiFi บ้านหรือ Hotspot มือถือของคุณ) ถ้าเรียนเป็นกลุ่ม ผู้จัดอาจเตรียม CE หรือ broker ไว้ให้
- อุปกรณ์: บอร์ด Eva Kit หรือ TESAIoT Dev Kit ที่ลงเฟิร์มแวร์ MicroPython ของ BENTO แล้ว หรือ BENTO Emulator ใน BENTO IDE (ซ้อมโค้ดและหน้าจอบน Emulator ได้ ไฟล์ 05 ไม่ต่อเครือข่ายเลย แต่ข้อความไม่ออกไปถึง TESAIoT CE ในแลนของคุณ และเมื่อยังไม่ได้ต่อ
publish()บน Emulator คืน False แทนการโยน OSError การดูผลที่ MQTT Explorer จึงต้องใช้บอร์ดจริง) - เรียนมาก่อน: บทเรียน 4.4 — MQTT: pub/sub topic QoS และงบข้อมูล
โค้ดของชุดบทเรียนนี้สั้นกว่าบทเรียน 3.7–3.9 มาก แต่การตัดสินใจหนักกว่า สิ่งที่ทำให้แล้วคือ TCP/IP การเข้ารหัสแพ็กเก็ต MQTT
การส่ง keepalive และฝั่งแพลตฟอร์มทั้งหมด (broker EMQX บริดจ์ที่ subscribe device/+/telemetry รออยู่ ฐานข้อมูลอนุกรมเวลา
และกราฟที่สร้างจากชื่อคีย์ JSON) งานของเราคือสี่คำถาม: ส่งอะไร ตั้งชื่อว่าอะไร ถี่แค่ไหน และทำอะไรกับคำสั่งที่รับกลับมา
โมดูล mqtt มีหกชื่อเท่านั้น และแต่ละตัวล้มแบบเงียบได้ connect() คืน True/False ไม่โยน exception ถ้าไม่ตรวจ
โปรแกรมจะวิ่งต่อแล้ว publish ทุกใบหายเงียบ ชื่อคีย์เวิร์ดคือ keepalive ไม่ใช่ keep_alive และ username= ไม่ใช่ user=
(พิมพ์ผิดได้ TypeError ทันที) publish() มีสามทางออก: ยังไม่ได้ต่อได้ OSError · ต่ออยู่แต่ส่งไม่ผ่านได้ False · ส่งต่อให้ชั้นเครือข่ายแล้วได้ True
ซึ่งที่ QoS 0 ไม่ได้แปลว่า broker ได้รับ publish() รับแค่สามอาร์กิวเมนต์ ใส่ retain=True ได้ TypeError
subscribe() ที่ broker ปฏิเสธก็คืน False เฉย ๆ get_message() ไม่เคยบล็อก คืน None หรือ tuple (topic, payload)
โดย payload เป็น bytes ไม่เกิน 255 ไบต์ ส่วน disconnect() คืน None จึงห้ามใส่ใน if คีย์ port ตั้งได้
แต่โมดูลนี้ส่งข้อมูลรับรอง TLS เป็นค่าว่างเสมอ จึงต่อได้เฉพาะพอร์ตข้อความเปล่า
ครึ่งหลังของบทเรียนคือแพลตฟอร์มที่คุณเป็นเจ้าของเอง ติดตั้ง TESAIoT CE ด้วย make install (ธง PREBUILT=1) ราว 15–30 นาที
make up ต้องมาก่อน make init-pki เอกสารทุกฉบับเขียนว่าพอร์ต 1883 แต่ docker-compose.yml เผยแพร่จริงเป็น
127.0.0.1:11883:1883 บอร์ดใน LAN จึงเข้าไม่ถึงเลย ต้องแก้เป็น 0.0.0.0:1883:1883 แล้วใช้ docker compose up -d emqx
(restart ไม่อ่านพอร์ตใหม่) ไฟล์ตั้งค่าคือความจริง เพราะมันคือสิ่งที่เครื่องอ่าน แพลตฟอร์มนี้ไม่มีการลงทะเบียนอัตโนมัติ
ต้องเพิ่มอุปกรณ์ที่ Devices → Add device และตั้ง device_id เองให้สั้น เช่น team03 ถ้าปล่อยให้สุ่มจะได้ UUID ยาว 36 ตัว
ถูกตัดเงียบที่ 31 แล้วต่อไม่ติดโดยไม่บอกสาเหตุ ขอรหัสด้วย POST /api/v1/devices/<id>/reset-mqtt-password
(ไม่ใช่ /reset-password) ตรวจว่าอุปกรณ์เป็น active และโหมด server_tls แล้วกรอกให้ client_id == username == device_id ตรงกันเป๊ะ
โค้ดหลักมีสามท่า ท่าที่ 1 ต่อ WiFi แล้ว mqtt.connect(..., keepalive=60) ซึ่งแปลว่าเงียบเกิน 60 วินาทีจะโดนตัด ส่งทุก 5 วินาทีจึงปลอดภัย
ท่าที่ 2 อ่าน sensors.bmi270.motion() ครั้งเดียวได้หกค่าจากช่วงเวลาเดียวกัน ใน try ใช้ round() เพราะ 0.12 กิน 4 ไบต์
แทน 12 แล้ว json.dumps() แบบแบน บริดจ์ห่อและเติม device_id กับ timestamp ให้เอง ห่อซ้ำจะได้ชื่อวัดขึ้นต้น data_
ท่าที่ 3 subscribe ครั้งเดียว แล้วถาม get_message() ทุกรอบ 100 ms คำสั่งจากภายนอกคือข้อมูลที่เราไม่ได้เขียนเอง
จึง .decode() แล้วครอบ json.loads() ด้วย try และจำสถานะ LED ในตัวแปรเอง ส่วนดวงไฟเลือก ตามชื่อ ด้วย
led_named("RGB_GREEN", "LED2") เพราะเลขดัชนีต่างกันตามบอร์ด ท่าที่ 4 ที่ไม่มีในโครงคือ mqtt.disconnect()
ถ้ากด Ctrl-C หรือรันใหม่โดยไม่เรียก broker ยังนับว่าเราอยู่จนครบ keepalive และ client_id เดิมยังถูกจอง
พอร์ต 1883 ไม่มีการเข้ารหัส username, password และ payload ทุกไบต์เดินบน WiFi เป็นข้อความอ่านออก ใครดักได้ก็ปลอมเป็นอุปกรณ์เรา
ส่งข้อมูลปลอมเข้าแพลตฟอร์มได้ทันที CE เองระบุว่า 1883 มีไว้สำหรับ local/dev เท่านั้น วันนี้จึงเป็นการฝึกในสนามซ้อมที่ปิดล้อม
“ต่อได้” กับ “ต่อได้อย่างปลอดภัย” เป็นคนละคำถาม เวลาข้อมูลไม่ขึ้นกราฟ ให้หาว่ากล่องสุดท้ายที่ยังเห็นข้อมูลคือกล่องไหน
ถ้า MQTT Explorer เห็นข้อความ ปัญหาอยู่ที่ topic ผิดรูปหรือ JSON ไม่ถูกต้อง ถ้าไม่เห็น ปัญหายังอยู่ฝั่งบอร์ด
ตัวอย่างสมบูรณ์
หัวข้อที่มีชื่อว่า “ตัวอย่างสมบูรณ์”สามไฟล์แรกต้องทำในบทเรียน เปิดตามลำดับนี้ทั้งชุดราว 35 นาที ทุกไฟล์ให้ทายก่อนรันว่าจอจะเป็นอย่างไร
01_topic_design.py(8 นาที) ดูการ์ดเขียวที่ publish ได้กับการ์ดส้มที่เป็น wildcard แล้วตอบว่าทำไม+กับ#ใส่ใน publish ไม่ได้03_connect_and_publish.py(15 นาที) แก้ค่าบนหัวไฟล์ให้เป็นของคุณ (IP ของคอมที่รัน CE และ device_id กับรหัสผ่าน MQTT ที่ได้ตอนเพิ่มอุปกรณ์) ดูป้ายสามขั้น (WiFi · broker · publish) เปลี่ยนเป็นเขียวตามลำดับ แล้วนับใน MQTT Explorer ว่าครบสิบข้อความไหม จากนั้นลองใส่ IP ของ broker ผิดหนึ่งครั้ง เพื่อจำว่าขั้นไหนล้มและล้มหน้าตาอย่างไร04_subscribe_command.py(12 นาที) ส่ง{"cmd":"beep"}หรือ{"cmd":"count"}จาก MQTT Explorer แล้วลองส่งข้อความที่ไม่ใช่ JSON ดูว่าโปรแกรมไม่หยุด ส่วนการสลับ LED ด้วย{"cmd":"toggle"}ทำจริงในไฟล์ฝึกของบทเรียน 4.6
ติดตรงไหนเปิดไฟล์ที่ตอบอาการนั้น: ส่งได้แต่สั่งกลับไม่ตอบ เปิด 05_send_every_5s_still_listen.py (ไม่ต้องต่อเครือข่าย ลองแก้ LOOP_MS
กับ SEND_EVERY_MS แล้วรันซ้ำ) · ไม่แน่ใจว่าจะใส่ฟิลด์อะไร เปิด 02_payload_shape.py · โค้ดบอกว่าส่งแล้วแต่ MQTT Explorer ไม่เห็น
เปิด 06_sent_is_not_delivered.py ซึ่งนับที่ปลายทาง ไม่ใช่นับที่ต้นทาง · อยากเห็นว่า disconnect() คืนชื่อให้ว่างจริง เปิด 07_disconnect_frees_id.py
ไฟล์ 04, 06, 07 ตั้งไว้กับ broker ฝึกสาธารณะ broker.hivemq.com (สำรอง test.mosquitto.org) ซึ่งไม่ขอ username= กับ password=
ผู้เรียนคนอื่นก็ใช้ broker นี้ ก่อนรันให้แก้ team03 ทั้งใน DEVICE_ID และใน topic bento/team03/... เป็นรหัสที่ไม่ซ้ำใคร
เช่นชื่อเล่นภาษาอังกฤษตัวเล็กต่อด้วยเลขสุ่ม 4 หลัก (nok4821) ไม่งั้น client_id ที่ซ้ำจะเตะกันหลุด และข้อความจะปนกับของคนอื่น
ส่วน MQTT Explorer ให้ต่อ broker เดียวกันที่พอร์ต 1883 ถ้าจะรันสามไฟล์นี้กับ CE ของคุณ ต้องเติม username= กับ password=
(CE ปฏิเสธอุปกรณ์ที่ไม่รู้จักตั้งแต่ตอน CONNECT) และใช้ topic รูป device/<device_id>/...
| ไฟล์ | ไฟล์นี้สอน |
|---|---|
| examples/01_topic_design.py | ออกแบบชื่อ topic ก่อนเขียนโค้ดส่ง |
| examples/02_payload_shape.py | รูปร่างของ payload ตัดสินว่าฝั่งรับทำงานง่ายหรือยาก |
| examples/03_connect_and_publish.py | ต่อ broker แล้วส่งค่าขึ้นไปหนึ่งชุด |
| examples/04_subscribe_command.py | รับคำสั่งจากข้างนอก แล้วทำตาม |
| examples/05_send_every_5s_still_listen.py | ส่งทุก 5 วินาที แต่ยังรับคำสั่งได้ทุก 100 ms |
| examples/06_sent_is_not_delivered.py | publish คืน True แปลว่าอะไร และไม่แปลว่าอะไร |
| examples/07_disconnect_frees_id.py | บอกลา broker ให้ถูกวิธี แล้วต่อใหม่ด้วยชื่อเดิมได้ทันที |
สไลด์ของบทเรียนนี้อ้างถึงไฟล์ที่อยู่ในบทเรียนอื่นด้วย:
- m04-iot-connectivity/l06-mqtt-telemetry-lab/practice/s10_mqtt_telemetry.py — ส่ง telemetry ขึ้น broker และรับคำสั่งกลับ (ฉบับฝึกเติมโค้ด)
- m04-iot-connectivity/l08-tesaiot-module/examples/06_secure_publish_loop.py — ส่งขึ้นแพลตฟอร์มผ่าน TLS แล้วโชว์หลักฐานบนจอ
ภาพจอจาก BENTO Emulator ของตัวอย่างในบทนี้ (คลิกชื่อไฟล์เพื่อเปิดโค้ด)

01_topic_design.py ออกแบบชื่อ topic ก่อนเขียนโค้ดส่ง
02_payload_shape.py รูปร่างของ payload ตัดสินว่าฝั่งรับทำงานง่ายหรือยาก
03_connect_and_publish.py ต่อ broker แล้วส่งค่าขึ้นไปหนึ่งชุด
04_subscribe_command.py รับคำสั่งจากข้างนอก แล้วทำตาม
05_send_every_5s_still_listen.py ส่งทุก 5 วินาที แต่ยังรับคำสั่งได้ทุก 100 ms
06_sent_is_not_delivered.py publish คืน True แปลว่าอะไร และไม่แปลว่าอะไร
07_disconnect_frees_id.py บอกลา broker ให้ถูกวิธี แล้วต่อใหม่ด้วยชื่อเดิมได้ทันทีเช็กความเข้าใจ
หัวข้อที่มีชื่อว่า “เช็กความเข้าใจ”คำถามชุดเดียวกันอยู่ใน quiz.yaml สำหรับระบบที่ตรวจอัตโนมัติ
-
ข้อใดถูกเกี่ยวกับค่าที่
mqtt.publish(topic, payload)คืน เลือกทุกข้อที่ถูก (เลือกได้หลายข้อ · เป้าหมายข้อ 1)- ก) เรียกตอนยังไม่ได้ต่อ broker จะได้ OSError ไม่ใช่ False
- ข) ต่ออยู่แต่ส่งไม่ผ่านจะได้ False
- ค) ได้ True แปลว่า broker ได้รับข้อความแล้วแน่นอน
- ง) ใส่
retain=Trueเพิ่มได้ถ้าอยากให้ broker จำข้อความล่าสุด
เฉลย
ก, ข — publish() มีสามทางออก OSError เมื่อยังไม่ได้ต่อ False เมื่อต่ออยู่แต่ส่งไม่ผ่าน และ True ที่แปลแค่ว่าส่งต่อให้ชั้นเครือข่ายแล้ว ที่ QoS 0 ไม่มีการยืนยันจาก broker ส่วน retain ไม่ใช่พารามิเตอร์ ใส่ไปได้ TypeError
-
ทีมติดตั้ง TESAIoT CE เสร็จ เอกสารบอกพอร์ต 1883 แต่บอร์ดที่อยู่ใน LAN เดียวกันต่อ broker ไม่ได้เลย ควรแก้อย่างไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 2)
- ก) แก้
docker-compose.ymlจาก127.0.0.1:11883:1883เป็น0.0.0.0:1883:1883แล้วสั่งdocker compose up -d emqx - ข) แก้ไฟล์เดียวกันแล้วสั่ง
docker compose restartก็พอ - ค) ใส่
BROKER = "localhost"ในโค้ดบนบอร์ด - ง) เปลี่ยนไปต่อพอร์ต 8884 ด้วยโมดูล
mqtt
เฉลย
ก — ไฟล์ตั้งค่าเผยแพร่จริงเป็น 11883 และผูกกับ loopback บอร์ดจึงเข้าไม่ถึง ต้องแก้เป็น 0.0.0.0:1883 และใช้ up -d เพราะ restart ไม่อ่านพอร์ตใหม่ localhost ชี้ไปที่ตัวบอร์ดเอง และโมดูล mqtt ต่อพอร์ต TLS ไม่ได้
- ก) แก้
-
เรียงขั้นของการรับคำสั่ง toggle จาก broker มาสลับ LED ให้ถูกลำดับ (เรียงลำดับ · เป้าหมายข้อ 3)
- ก) แปลง
msg[1].decode()แล้วjson.loads()ภายในtry/except ValueError - ข) เรียก
mqtt.subscribe(TOPIC_CMD)ครั้งเดียวก่อนเข้าลูป - ค) ถ้า
cmd.get("cmd") == "toggle"สลับตัวแปรled_onที่จำไว้เอง แล้วสั่งlamp.value(...) - ง) เรียก
mqtt.get_message()ทุกรอบลูป 100 ms - จ) ตรวจว่า
msg is not None
เฉลย
ข → ง → จ → ก → ค — subscribe ครั้งเดียวแล้วถาม get_message() ทุกรอบ ซึ่งคืน None เมื่อไม่มีข้อความจึงต้องตรวจก่อน payload เป็น bytes ต้อง decode ก่อน json.loads และครอบ try เพราะคนส่งมั่วได้เสมอ สถานะ LED ต้องจำเอง เพราะค่าของขาบอกแค่ระดับ ณ วินาทีที่ถาม
- ก) แปลง
-
ทีมกด Ctrl-C แล้วรันไฟล์เดิมใหม่ทันที บอร์ดต่อติดแล้วหลุดสลับกันทั้งที่โค้ดไม่ผิด สาเหตุและทางแก้ที่ตรงที่สุดคืออะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 4)
- ก) broker ยังนับว่าตัวเก่าอยู่จนครบ keepalive และ client_id เดิมยังถูกจอง ให้เรียก
mqtt.disconnect()ในexcept KeyboardInterrupt: - ข) keepalive สั้นเกินไป ให้ตั้ง
keep_alive=600 - ค) ต้องเรียก
if mqtt.disconnect():ก่อน connect ทุกครั้ง - ง) บอร์ดเสีย ต้องลงเฟิร์มแวร์ใหม่
เฉลย
ก — ถ้าไม่บอกลา broker รอจนครบ keepalive กว่าจะยอมรับว่าเราไปแล้ว disconnect() คืนชื่อให้ว่างทันที แต่คืน None จึงใส่ใน if ไม่ได้ และคีย์เวิร์ดที่ถูกคือ keepalive ไม่ใช่ keep_alive
- ก) broker ยังนับว่าตัวเก่าอยู่จนครบ keepalive และ client_id เดิมยังถูกจอง ให้เรียก
-
คนที่ดักจับสัญญาณ WiFi วงเดียวกับบอร์ดได้ ทำอะไรได้บ้างเมื่อบอร์ดของเราส่งผ่านพอร์ต 1883 เลือกทุกข้อที่ถูก (เลือกได้หลายข้อ · เป้าหมายข้อ 4)
- ก) อ่าน username และ password ของอุปกรณ์ได้
- ข) อ่านค่าเซนเซอร์ใน payload ได้
- ค) ปลอมเป็นอุปกรณ์ของเราส่งข้อมูลปลอมเข้าแพลตฟอร์มได้
- ง) ไม่ได้อะไรเลย เพราะ broker ขอรหัสผ่านก่อนต่อ
เฉลย
ก, ข, ค — พอร์ต 1883 ไม่มีการเข้ารหัส ทุกไบต์รวมถึงรหัสผ่านเดินเป็นข้อความอ่านออก การขอรหัสผ่านไม่ช่วยเมื่อรหัสเองถูกอ่านได้ จึงใช้ 1883 ได้แค่ในสนามซ้อม ต่อได้กับต่อได้อย่างปลอดภัยเป็นคนละคำถาม
เตรียมแพลตฟอร์มของคุณ ทำบนคอมของคุณกับบอร์ดจริง แล้วจดผลลงบันทึกการเรียน
- ติดตั้ง TESAIoT CE ด้วย
make installโดยเรียกmake upก่อนmake init-pki - แก้
docker-compose.ymlจาก127.0.0.1:11883:1883เป็น0.0.0.0:1883:1883สั่งdocker compose up -d emqxแล้วดูdocker compose psว่าพอร์ตขึ้นเป็น0.0.0.0:1883จริง - Devices → Add device ด้วย
device_idสั้นที่ตั้งเอง ขอรหัสด้วยreset-mqtt-passwordและตรวจว่าเป็น active กับserver_tls - หา IP ของคอมใน LAN (ไม่ใช่ localhost) แล้ว
wifi.ping()จากบอร์ดให้ผ่านก่อนแตะ MQTT - ต่อ MQTT Explorer ไปที่ IP เดียวกัน พอร์ต 1883 ด้วยชื่อและรหัสของอุปกรณ์ แล้ว subscribe
device/#ไว้ - รัน
03_connect_and_publish.pyด้วยclient_id == username == device_idและ topicdevice/<device_id>/telemetryจนเห็นข้อความใน MQTT Explorer - เขียนหนึ่งประโยคในบันทึกการเรียนว่าคนดักฟังบน WiFi วงเดียวกันเห็นอะไรบ้างเมื่อเราใช้พอร์ต 1883
บทเรียน 4.6 ประกอบทุกท่าเป็นโปรแกรมเดียวใน s10_mqtt_telemetry.py ส่งค่าจริงทุก 5 วินาทีและรับ {"cmd":"toggle"} กลับมาสลับ LED
อ่านเสริมนอกเวลาได้ที่ 06_secure_publish_loop.py ของบทเรียน 4.8 ซึ่งเป็นลูปเดียวกันบนช่องที่เข้ารหัส เปิดอ่านเทียบโครงได้ แต่อย่าเพิ่งรัน
บทเรียนถัดไป: บทเรียน 4.6 — ลงมือทำ: telemetry สองทาง
สะท้อนคิด
หัวข้อที่มีชื่อว่า “สะท้อนคิด”- เอกสารของ CE กับ
docker-compose.ymlขัดกันเรื่องพอร์ต คุณจะเช็กอะไรก่อนเมื่อเจอระบบใหม่ครั้งหน้า - ถ้ามีคนบน WiFi วงเดียวกันดักรหัส MQTT ของอุปกรณ์เราไปได้ เขาทำอะไรกับแพลตฟอร์มของเราได้บ้าง
- ตัวนับ True จาก
publish()เป็นหลักฐานว่าส่งถึงได้หรือไม่ ถ้าไม่ได้ หลักฐานที่เชื่อได้คืออะไร
คำถามทบทวน
ลองตอบเองก่อน แล้วค่อยเปิดดูเฉลย
-
ข้อใดถูกเกี่ยวกับค่าที่ `mqtt.publish(topic, payload)` คืน เลือกทุกข้อที่ถูก (เป้าหมายข้อ 1)
- เรียกตอนยังไม่ได้ต่อ broker จะได้ OSError ไม่ใช่ False
- ต่ออยู่แต่ส่งไม่ผ่านจะได้ False
- ได้ True แปลว่า broker ได้รับข้อความแล้วแน่นอน
- ใส่ `retain=True` เพิ่มได้ถ้าอยากให้ broker จำข้อความล่าสุด
ดูเฉลย
คำตอบ: A. เรียกตอนยังไม่ได้ต่อ broker จะได้ OSError ไม่ใช่ False · B. ต่ออยู่แต่ส่งไม่ผ่านจะได้ False
publish() มีสามทางออก OSError เมื่อยังไม่ได้ต่อ False เมื่อต่ออยู่แต่ส่งไม่ผ่าน และ True ที่แปลแค่ว่าส่งต่อให้ชั้นเครือข่ายแล้ว ที่ QoS 0 ไม่มีการยืนยันจาก broker ส่วน retain ไม่ใช่พารามิเตอร์ ใส่ไปได้ TypeError
-
ทีมติดตั้ง TESAIoT CE เสร็จ เอกสารบอกพอร์ต 1883 แต่บอร์ดที่อยู่ใน LAN เดียวกันต่อ broker ไม่ได้เลย ควรแก้อย่างไร (เป้าหมายข้อ 2)
- แก้ `docker-compose.yml` จาก `127.0.0.1:11883:1883` เป็น `0.0.0.0:1883:1883` แล้วสั่ง `docker compose up -d emqx`
- แก้ไฟล์เดียวกันแล้วสั่ง `docker compose restart` ก็พอ
- ใส่ `BROKER = "localhost"` ในโค้ดบนบอร์ด
- เปลี่ยนไปต่อพอร์ต 8884 ด้วยโมดูล `mqtt`
ดูเฉลย
คำตอบ: A. แก้ `docker-compose.yml` จาก `127.0.0.1:11883:1883` เป็น `0.0.0.0:1883:1883` แล้วสั่ง `docker compose up -d emqx`
ไฟล์ตั้งค่าเผยแพร่จริงเป็น 11883 และผูกกับ loopback บอร์ดจึงเข้าไม่ถึง ต้องแก้เป็น 0.0.0.0:1883 และใช้ up -d เพราะ restart ไม่อ่านพอร์ตใหม่ localhost ชี้ไปที่ตัวบอร์ดเอง และโมดูล mqtt ต่อพอร์ต TLS ไม่ได้
-
เรียงขั้นของการรับคำสั่ง toggle จาก broker มาสลับ LED ให้ถูกลำดับ (เป้าหมายข้อ 3)
- แปลง `msg[1].decode()` แล้ว `json.loads()` ภายใน `try/except ValueError`
- เรียก `mqtt.subscribe(TOPIC_CMD)` ครั้งเดียวก่อนเข้าลูป
- ถ้า `cmd.get("cmd") == "toggle"` สลับตัวแปร `led_on` ที่จำไว้เอง แล้วสั่ง `lamp.value(...)`
- เรียก `mqtt.get_message()` ทุกรอบลูป 100 ms
- ตรวจว่า `msg is not None`
ดูเฉลย
ลำดับที่ถูก: B. เรียก `mqtt.subscribe(TOPIC_CMD)` ครั้งเดียวก่อนเข้าลูป → D. เรียก `mqtt.get_message()` ทุกรอบลูป 100 ms → E. ตรวจว่า `msg is not None` → A. แปลง `msg[1].decode()` แล้ว `json.loads()` ภายใน `try/except ValueError` → C. ถ้า `cmd.get("cmd") == "toggle"` สลับตัวแปร `led_on` ที่จำไว้เอง แล้วสั่ง `lamp.value(...)`
subscribe ครั้งเดียวแล้วถาม get_message() ทุกรอบ ซึ่งคืน None เมื่อไม่มีข้อความจึงต้องตรวจก่อน payload เป็น bytes ต้อง decode ก่อน json.loads และครอบ try เพราะคนส่งมั่วได้เสมอ สถานะ LED ต้องจำเอง เพราะค่าของขาบอกแค่ระดับ ณ วินาทีที่ถาม
-
ทีมกด Ctrl-C แล้วรันไฟล์เดิมใหม่ทันที บอร์ดต่อติดแล้วหลุดสลับกันทั้งที่โค้ดไม่ผิด สาเหตุและทางแก้ที่ตรงที่สุดคืออะไร (เป้าหมายข้อ 4)
- broker ยังนับว่าตัวเก่าอยู่จนครบ keepalive และ client_id เดิมยังถูกจอง ให้เรียก `mqtt.disconnect()` ใน `except KeyboardInterrupt:`
- keepalive สั้นเกินไป ให้ตั้ง `keep_alive=600`
- ต้องเรียก `if mqtt.disconnect():` ก่อน connect ทุกครั้ง
- บอร์ดเสีย ต้องลงเฟิร์มแวร์ใหม่
ดูเฉลย
คำตอบ: A. broker ยังนับว่าตัวเก่าอยู่จนครบ keepalive และ client_id เดิมยังถูกจอง ให้เรียก `mqtt.disconnect()` ใน `except KeyboardInterrupt:`
ถ้าไม่บอกลา broker รอจนครบ keepalive กว่าจะยอมรับว่าเราไปแล้ว disconnect() คืนชื่อให้ว่างทันที แต่คืน None จึงใส่ใน if ไม่ได้ และคีย์เวิร์ดที่ถูกคือ keepalive ไม่ใช่ keep_alive
-
คนที่ดักจับสัญญาณ WiFi วงเดียวกับบอร์ดได้ ทำอะไรได้บ้างเมื่อบอร์ดของเราส่งผ่านพอร์ต 1883 เลือกทุกข้อที่ถูก (เป้าหมายข้อ 4)
- อ่าน username และ password ของอุปกรณ์ได้
- อ่านค่าเซนเซอร์ใน payload ได้
- ปลอมเป็นอุปกรณ์ของเราส่งข้อมูลปลอมเข้าแพลตฟอร์มได้
- ไม่ได้อะไรเลย เพราะ broker ขอรหัสผ่านก่อนต่อ
ดูเฉลย
คำตอบ: A. อ่าน username และ password ของอุปกรณ์ได้ · B. อ่านค่าเซนเซอร์ใน payload ได้ · C. ปลอมเป็นอุปกรณ์ของเราส่งข้อมูลปลอมเข้าแพลตฟอร์มได้
พอร์ต 1883 ไม่มีการเข้ารหัส ทุกไบต์รวมถึงรหัสผ่านเดินเป็นข้อความอ่านออก การขอรหัสผ่านไม่ช่วยเมื่อรหัสเองถูกอ่านได้ จึงใช้ 1883 ได้แค่ในสนามซ้อม ต่อได้กับต่อได้อย่างปลอดภัยเป็นคนละคำถาม
อ้างอิงบทเรียนนี้
ถ้านำบทเรียนนี้ไปสอน ทำสไลด์ หรือทำเอกสารต่อ ให้อ้างอิงด้วยข้อความนี้ ถ้าดัดแปลงเนื้อหา ให้เติม (ดัดแปลง)ต่อท้ายชื่อบทเรียน
"MQTT กับแพลตฟอร์มที่ติดตั้งเอง: telemetry และ command" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
ข้อความอ้างอิงภาษาอังกฤษ: "MQTT with a self-hosted platform: telemetry and commands" 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/m04-iot-connectivity/l05-mqtt-platform/
บทเรียนนี้ดัดแปลงจากต้นฉบับด้านล่าง เมื่ออ้างอิงให้คงเครดิตต้นฉบับไว้ด้วย
https://github.com/Advance-Innovation-Centre-AIC/embedded-systems-for-aiot-developer/blob/a80bbe88a34bcb9bb8d991f42f9252b77cdab079/session-10.html (slides 16–27)
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0
เนื้อหาเผยแพร่ภายใต้ CC BY-NC 4.0 นำไปใช้ต่อในงานที่ไม่ใช่เพื่อการค้าได้ โปรดอ้างอิงสมาคมสมองกลฝังตัวไทย (TESA) ทุกครั้ง · วิธีอ้างอิง TESA