From a real problem to a design: canvas, schema and designing for failure
Companion videos
Watch on YouTube (opens in a new tab)
-
Software Development Process - EP01 : Overall Software Development Process Thai Embedded Systems Association (TESA) -
Software Development Process - EP02 : User Needs Thai Embedded Systems Association (TESA) -
Software Development Process - EP03 : System Requirements Thai Embedded Systems Association (TESA)
Videos by Thai Embedded Systems Association (TESA) · The whole series in the playlist AIoT Foundation
Module 5 — Capstone: AIoT Mini-Product · Slides: slides.md · Module overview · Course page
Start the capstone from a real pain point and finish the design on paper before touching code, covering the five-box canvas, a schema that lasts, event-based sending and how the system behaves when things break.
Objectives
Section titled “Objectives”By the end of this lesson you will be able to:
- Write the team’s problem from three questions (who loses what every day, how they find out today, what changes if they knew thirty minutes sooner) and fill all five canvas boxes Sense → Decide → Show → Send → Act, with Act stating who does what within how many minutes
- Design a small, stable JSON payload under 1000 bytes with a unit and a device id, justify every field, and name any proxy field so that it declares itself a proxy
- Calculate messages per day for sending every reading versus sending events plus a heartbeat, and explain why the heartbeat is still needed
- Specify how the system behaves in five failure situations (WiFi drops, broker silent, network returns, a one-off spike, an alert nobody sees) and decide whether to drop or keep data while offline, with a reason tied to the team’s problem
Before you start
Section titled “Before you start”This lesson writes no code yet. Open a fresh page in your learning log for the capstone — the five-box canvas, the schema table, and your team’s failure-behaviour table will all live there, and will be used again in lessons 5.2 and 5.3. Review two things your team already has: the dashboard from lessons 3.7–3.9, and the telemetry sent to a broker in lessons 4.4–4.6 and 4.7–4.9. This set of lessons introduces not a single new API — only things your team has already run yourselves.
- Equipment: an Eva Kit or TESAIoT Dev Kit board with the BENTO MicroPython firmware installed, or the BENTO Emulator in BENTO IDE
- Before this: Lesson 4.9 — Hands-on: real readings over an encrypted channel
See it work first
Section titled “See it work first”Open two things at once: your team’s dashboard on the board, and the telemetry flowing into the broker. While both are running, have a teammate quietly turn off WiFi, and watch what the board’s screen does — does it hang, does an error appear in the middle of the screen, or does it keep working and report its state? Both of these already work, but neither is yet a product. What you see when the network vanishes is the distance from demo to product that this set of lessons will walk you through.
Concepts
Section titled “Concepts”A demo and a product differ in decisions made, not in the API used. A product must be able to answer why this thing needs to exist at all
(someone is losing money or time to this problem right now). The value on screen is a quantity with a unit that someone can actually decide from.
Only what the destination truly uses gets sent up. The screen keeps working when the network drops. And there is a device id to check against.
None of these five needs an API we have not learned yet. What is in our hands is nine modules (lcd, gpio, ui, sensors, dsp,
mic, wifi, mqtt, tesaiot) — check today whether your team’s problem needs a name that is not on this list.
Start from the problem, not from a list of sensors. Ask in order: who loses what every day · how do they find out today that there is a problem ·
what would change if they knew thirty minutes sooner (unable to answer question three means the problem is not yet worth doing). Only then ask what the board can measure
that speaks to that. If the board cannot measure what you want to know, use a proxy, and declare clearly that it is a stand-in, both in the slides and in the field name.
For example, the Eva Kit has no room-temperature sensor, so a cold-room problem instead measures how long the door has been left open, and names the field door_open_s, not temp_c
(the Dev Kit can read room temperature directly from sensors.sht40, but neither board has a gas or current sensor).
Fill all five canvas boxes before touching the keyboard. Sense (what to measure, how often, how filtered) · Decide (what threshold, how many rounds to confirm, decided on the board) · Show (what appears on screen, is it understood within two seconds, does it have a network status and an acknowledge button) · Send (what to send, where, what schema) · Act (who does what next, within how many minutes). You may fill it right to left, starting from Act, and this often gives a smaller, more focused system. If nobody does anything in the Act box, that data never needed to be sent in the first place. The example in the slides is a third-floor scaffold that tilts and nobody knows until morning — this is the problem lesson 5.3’s solution carries all the way through.
A schema that lasts, and event-based sending. The starter’s payload has six fields:
{"id": "team01", "v": 17.4, "unit": "deg", "state": "ALERT", "kind": "event", "t": 812340}
— the board’s identity, the decided value, its unit, the board’s own verdict, the message’s type, and the board’s own time for ordering.
The rule is: field names stay constant for the whole project; new fields can be added but the meaning of an existing field must never change, and the payload always stays under 1000 bytes.
Reading every 200 ms and sending every value gives 432,000 messages a day per board (about 39 MB), but sending only on a state change
plus a heartbeat every 30 seconds leaves about 2,883. The heartbeat is still needed because silence alone
cannot tell the destination “everything is fine” apart from “the board died yesterday”. MQTT itself was born for exactly this kind of poor signal
(designed in 1999, for oil pipelines connected by satellite).
Design for failure from the very start. A dropped network is the normal condition of a device installed in the real world; a product must keep drawing the screen and show the word offline, schedule a retry every 10 seconds, count failed sends and show them on screen, reconnect on its own once the network returns and tell the receiving side, require a value past the threshold for several rounds in a row before changing state, and hold an alert state until someone presses acknowledge. Another thing that must be decided clearly is whether to drop or keep for later while offline. The starter drops and counts, because a tilt reading from ten minutes ago is no use to someone standing under the scaffold. Either answer can be correct — the wrong thing is never deciding at all.
Worked example
Section titled “Worked example”The slides for this lesson also refer to files that live in another lesson:
- m05-capstone/l03-build-and-present/practice/s12_capstone_starter.py — a mini-product starter structure: Sense -> Decide -> Show -> Send
- m05-capstone/l03-build-and-present/solution/s12_capstone_starter.py — one finished example: a Tilt Alarm for a scaffold or shelf
Check your understanding
Section titled “Check your understanding”The same questions are in quiz.yaml for automatic marking.
-
In the slides’ tilting-scaffold problem, which is the correctly written Act box for the canvas? (choose one · objective 1)
- A) Send the tilt value to the cloud every 200 ms
- B) The site supervisor gets an alert and walks over to check within 15 minutes
- C) Show the word ALERT in large text on screen
- D) Store the data for a historical chart
Solution
B — The Act box is written as “who does what, within how many minutes”, not “send to the cloud”. If nobody at the destination does anything, that data never needed to be sent in the first place. Showing ALERT on screen is the Show box’s job.
-
A team using an Eva Kit builds a cold-room problem, but the Eva has no room-temperature sensor, so they measure how many seconds the door has been left open instead. How should this field be named in the schema? (choose one · objective 2)
- A) temp_c, because the real problem is about temperature
- B) door_open_s
- C) imu_temp_c, reading from sensors.bmi270.temperature() instead
- D) cold_room
Solution
B — A proxy must declare itself a stand-in, both on the presentation slides and in the field name — name it door_open_s, not temp_c. sensors.bmi270.temperature() on the Eva raises OSError every time, and even if it could be read, it is the IMU chip’s own temperature, not the room’s.
-
Which rules make a schema last a long time? Choose every correct one. (choose all that apply · objective 2)
- A) Field names stay constant for the whole project
- B) New fields can be added, but the meaning of an existing field must never change
- C) The outgoing payload always stays under 1000 bytes
- D) Send raw values for every axis too, in case the destination wants to compute its own
- E) Rename fields to be clearer as the team understands the problem better
Solution
A, B, C — The receiving side writes code to unpack the data once; if we rename a field later, they must fix the whole system. The v field is the value already decided, not a raw one — the destination should not need to recompute it and get an answer that disagrees with the board’s.
-
A board reads a value every 200 ms. Sending every value gives 432,000 messages a day; changing to send only on a state change plus a heartbeat every 30 seconds leaves about 2,883 messages. Why is the heartbeat still needed? (choose one · objective 3)
- A) So the destination can tell “everything is normal” apart from “the board has died”
- B) So the destination’s chart is as smooth as when every value was sent
- C) So the filtered-out raw values are all still sent in full
- D) So the number of messages per day stays the same as before
Solution
A — Silence alone cannot tell the destination “everything is fine” apart from “the board died yesterday”. A heartbeat every 30 seconds is 2,880 messages a day, plus a handful of real events — still over a hundred times fewer than sending every value.
-
A team builds a machine that counts parts produced. When the network drops, which approach makes the most sense? (choose one · objective 4)
- A) Drop every value, because the starter chose to drop
- B) Keep it to send later, because completeness matters more than freshness here, and record this decision in the learning log
- C) No decision needed — just let the program do whatever it already does
- D) Stop counting until the network returns
Solution
B — The starter drops and counts, because a tilt reading from ten minutes ago is no use to someone under the scaffold, but a parts counter’s answer may be the opposite, because completeness matters more than freshness. Either answer can be correct — the wrong thing is never deciding at all, and letting behaviour happen by accident.
Design the capstone on paper (about 30 minutes, done as a team). Record every item in your learning log.
- Answer the problem’s three questions, then tell another team your problem in under 30 seconds. If they ask “so what”, go back and fix question three
- Pick your own team’s problem, or one of the six field problems in the slides, and check the nine-module list to make sure every name you need really exists on your team’s board
- If a proxy is needed, write down what it can and cannot tell you, and name the field so it declares itself a stand-in
- Fill all five canvas boxes. Write the Act box as “who does what, within how many minutes”
- Write a two-column schema table (field · why it must exist). Every field has a reason, a unit, and a device id
- Work out your team’s messages per day two ways: sending every value versus sending events plus a heartbeat
- Write a five-row failure table (WiFi drops, broker silent, network returns, a one-off spike, an alert nobody sees) and decide “drop or keep for later” for each, with one sentence of reasoning
- Be able to answer: if your team’s machine hangs, how would someone walking past know?
Going further
Section titled “Going further”Lesson 5.2 walks through the starter structure s12_capstone_starter.py move by move — bring your filled-in canvas with you, because every “the team writes this” point in the structure
asks for an answer from it. If there is time, watch the extra slides videos on topic design and MQTT’s Last Will and Testament.
Next lesson: Lesson 5.2 — The starter: Sense, Decide, Show, Send
Reflect
Section titled “Reflect”- If your team’s problem cannot answer “what changes if they knew thirty minutes sooner”, would the team change the problem, or change who benefits?
- Which field in your team’s schema does nobody at the destination actually use yet — what would be lost by cutting it?
- If your team’s board went silent all night, could the receiving side tell whether that was normal or dead?
Review questions
Answer on your own first, then open the answer.
-
In the slides' scaffold-tilt problem, which entry is a correct Act box on the canvas? (Objective 1)
- ส่งค่าความเอียงขึ้นคลาวด์ทุก 200 ms
- หัวหน้าไซต์ได้แจ้งเตือน แล้วเดินไปดูภายใน 15 นาที
- แสดงคำว่า ALERT ตัวใหญ่บนจอ
- เก็บข้อมูลไว้ทำกราฟย้อนหลัง
Show answer
Answer: B. หัวหน้าไซต์ได้แจ้งเตือน แล้วเดินไปดูภายใน 15 นาที
ช่อง Act เขียนเป็น "คนทำอะไร ภายในกี่นาที" ไม่ใช่ "ส่งขึ้นคลาวด์" ถ้าปลายทางไม่มีใครทำอะไร ข้อมูลนั้นไม่ต้องส่งตั้งแต่แรก ส่วนคำว่า ALERT บนจอเป็นงานของช่อง Show
-
A team on the Eva Kit tackles the cold-room problem, but the Eva has no room-temperature sensor, so it measures how long the door stays open instead. How should this field be named in the schema? (Objective 2)
- temp_c เพราะโจทย์จริงคืออุณหภูมิ
- door_open_s
- imu_temp_c โดยอ่านจาก sensors.bmi270.temperature() แทน
- cold_room
Show answer
Answer: B. door_open_s
proxy ต้องประกาศตัวว่าเป็นตัวแทนทั้งบนสไลด์นำเสนอและในชื่อฟิลด์ ตั้งชื่อ door_open_s ไม่ใช่ temp_c ส่วน sensors.bmi270.temperature() บน Eva โยน OSError ทุกครั้ง และถึงอ่านได้ก็เป็นอุณหภูมิของชิป IMU ไม่ใช่ของห้อง
-
Which rules keep a schema usable for a long time? Choose all that apply. (Objective 2)
- ชื่อฟิลด์คงที่ตลอดโครงการ
- เพิ่มฟิลด์ใหม่ได้ แต่ห้ามเปลี่ยนความหมายของฟิลด์เดิม
- payload ขาออกต่ำกว่า 1000 ไบต์เสมอ
- ส่งค่าดิบทุกแกนไปด้วย เผื่อปลายทางอยากคำนวณเอง
- เปลี่ยนชื่อฟิลด์ให้สื่อความหมายขึ้นเมื่อทีมเข้าใจโจทย์มากขึ้น
Show answer
Answer: A. ชื่อฟิลด์คงที่ตลอดโครงการ · B. เพิ่มฟิลด์ใหม่ได้ แต่ห้ามเปลี่ยนความหมายของฟิลด์เดิม · C. payload ขาออกต่ำกว่า 1000 ไบต์เสมอ
ฝั่งรับเขียนโค้ดแกะข้อมูลครั้งเดียว ถ้าเราเปลี่ยนชื่อฟิลด์ทีหลัง เขาต้องแก้ทั้งระบบ และฟิลด์ v คือค่าที่ตัดสินใจแล้ว ไม่ใช่ค่าดิบ ปลายทางไม่ต้องคำนวณซ้ำจนได้คำตอบไม่ตรงกับบอร์ด
-
The board reads every 200 ms. Sending every reading gives 432,000 messages a day; sending on state change plus a heartbeat every 30 seconds leaves about 2,883. Why keep the heartbeat at all? (Objective 3)
- เพื่อให้ปลายทางแยกได้ว่า "ทุกอย่างปกติ" กับ "บอร์ดตายไปแล้ว"
- เพื่อให้กราฟฝั่งปลายทางลื่นเหมือนตอนส่งทุกค่า
- เพื่อส่งค่าดิบที่ถูกกรองทิ้งไปให้ครบ
- เพื่อให้จำนวนข้อความต่อวันเท่าเดิม
Show answer
Answer: A. เพื่อให้ปลายทางแยกได้ว่า "ทุกอย่างปกติ" กับ "บอร์ดตายไปแล้ว"
ถ้าเงียบอย่างเดียว ปลายทางแยกไม่ออกระหว่าง "ทุกอย่างปกติ" กับ "บอร์ดตายไปแล้วเมื่อวาน" heartbeat ทุก 30 วินาทีคือ 2,880 ข้อความต่อวัน บวกเหตุการณ์จริงอีกไม่กี่ครั้ง ยังน้อยกว่าการส่งทุกค่าร้อยกว่าเท่า
-
A team builds a counter for parts produced. What is the most reasonable choice while the network is down? (Objective 4)
- ทิ้งทุกค่า เพราะโครงตั้งต้นเลือกทิ้ง
- เก็บไว้ส่งทีหลัง เพราะความครบสำคัญกว่าความสด แล้วจดการตัดสินใจนี้ไว้ในบันทึกการเรียน
- ไม่ต้องตัดสินใจ ปล่อยให้เป็นไปตามที่โปรแกรมทำอยู่
- หยุดนับจนกว่าเน็ตจะกลับมา
Show answer
Answer: B. เก็บไว้ส่งทีหลัง เพราะความครบสำคัญกว่าความสด แล้วจดการตัดสินใจนี้ไว้ในบันทึกการเรียน
โครงตั้งต้นทิ้งแล้วนับไว้ เพราะค่าความเอียงเมื่อสิบนาทีก่อนไม่มีประโยชน์กับคนใต้นั่งร้าน แต่เครื่องนับชิ้นงานคำตอบอาจกลับกัน เพราะความครบสำคัญกว่าความสด ทั้งสองคำตอบถูกได้ ที่ผิดคือไม่เคยตัดสินใจ แล้วปล่อยให้พฤติกรรมเป็นไปตามบังเอิญ
Cite this lesson
If you teach from this lesson or reuse it in slides or documents, credit it with the text below. If you changed it, add (adapted) after the title.
"From a real problem to a design: canvas, schema and designing for failure" 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
Thai attribution: "จากโจทย์จริงสู่แบบ: canvas schema และการออกแบบตอนพัง" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
Lesson link: https://tesaiot.github.io/tesa-qualification-program/en/courses/aiot-micropython/m05-capstone/l01-problem-to-design/
This lesson adapts the source below; keep its credit too.
https://github.com/Advance-Innovation-Centre-AIC/embedded-systems-for-aiot-developer/blob/a80bbe88a34bcb9bb8d991f42f9252b77cdab079/session-12.html (slides 1–21)
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0
Content is licensed CC BY-NC 4.0. Reuse it non-commercially and credit the Thai Embedded Systems Association (TESA) every time. · How to cite TESA