Hands-on: build and ship an edge AI app
Module 8 — Capstone: your own Edge AI app · Slides: slides.md · Module overview · Course page
Fill the five backbone lines of s20_capstone.py — select, reading raw acceleration, result, EMA-filtered confidence, and fire_alert — then design, tune, and demo your own Guardian, proving with evidence that a real event alerts once while a decoy doesn’t, and explaining the reasoning behind the design values you chose.
Objectives
Section titled “Objectives”By the end of this lesson, you will:
- Fill the five points in practice/s20_capstone.py until the Guardian shows the motion context and a filtered verdict, and alerts once (sound, banner, counter) when the target class holds for HITS_NEEDED results.
- Design your own Guardian (model, action, and the values CONF_FLOOR, HITS_NEEDED, EMA_ALPHA), and record your reasoning with measured evidence — the number of alerts from real events versus from decoys, at least five trials each.
- Give a two-minute demo showing a real alert, a decoy that doesn’t alert, and the trade-off you chose, and propose one extension direction.
Before you start
Section titled “Before you start”You’ve been through lesson 8.1, and have a draft of your Guardian and starting values with reasons. Prepare a decoy in advance, such as a single brief shake or a borderline sound, to prove the decision layer works.
- Hardware: a TESAIoT Dev Kit board already flashed with BENTO’s MicroPython firmware, or the BENTO Emulator inside BENTO IDE — you can complete the whole capstone on the emulator with the Motion model and the Shake button. Watching for sound or radar needs the board.
- Prior lesson: lesson 8.1 — Designing the capstone: Guardian, three pillars in one file
Concepts
Section titled “Concepts”The whole file reads as one sentence: select a model → read context and the verdict → filter it steady → once the threshold is met, act → clean up on exit. The design knobs sit at the top of the file (MODEL_KEYWORD, HITS_NEEDED = 3, EMA_ALPHA = 0.4, ACTION_NOTE = 72). The five points to fill in are: (1) edge_ai.select(model['index']) when Load is pressed, inside a try, showing RUNNING only after select succeeds (2) ax, ay, az = sensors.bmi270.acceleration() — forget this, and motion stays stuck at 0.0 (3) r = edge_ai.result() — forget this, and the card never moves (4) conf_s = conf_ema.update(r['conf']) — forget this, and the status colour flickers with every peak, and (5) fire_alert(r['label'], conf_s) when streak >= HITS_NEEDED and not fired — forget this, and the threshold is met, but Guardian stays silent.
Filling it in isn’t the capstone yet. The fifth step the solution doesn’t hand you is your own design: choose a model and an action, set the design values, then measure real results — run the real event and the decoy several times each, count how many alerts each produces, then tune and record how the values change the result, so your design’s explanation stands on numbers, not on a feeling. Deliver it with a short demo showing both a real alert and a non-alert. There are four extension directions: plugging in a self-trained model from module 5, adding FFT-derived features from module 4, fusing with a raw sensor or radar as in lesson 6.5, and sending events to MQTT as in lesson 6.6.
Worked example
Section titled “Worked example”s20_capstone_full.py is the polished version of Guardian — selects a model live from a dropdown, shows latency, resets the filter when starting a fresh watch cycle, and counts the number of alerts.
| File | What this file teaches |
|---|---|
| examples/s20_capstone_full.py | Capstone: Edge AI Guardian (full version) |
This lesson’s slides also reference a file in another lesson or in shared/:
- shared/training/eval_pc.py — Run the exported int8 .tflite on the PC and report accuracy + confusion.
Practice
Section titled “Practice”The # TODO: comments are at lines 127 (select), 152 (raw acceleration), 159 (result), 166 (conf_ema.update), and 194 (fire_alert). If it shows RUNNING but the card never moves, check points 127 and 159. If the status colour flickers every frame, check point 166.
| Practice file | Topic |
|---|---|
| practice/s20_capstone.py | Capstone: Edge AI Guardian (the fill-in-the-code version) |
Solution
Section titled “Solution”Open the solution after trying on your own at least once, and read how to use the solutions first.
| Solution | Pairs with |
|---|---|
| solution/s20_capstone.py | practice/s20_capstone.py |
Check your understanding
Section titled “Check your understanding”The same questions are in quiz.yaml for automated checking.
-
Guardian shows RUNNING, but the result card never moves at all, while motion moves with the shaking. Which point is still empty? (single choice · objective 1)
- a) Point 2, reading acceleration
- b) Point 3, r = edge_ai.result() (or point 1, select)
- c) Point 4, EMA
- d) Point 5, fire_alert
Solution
b — motion moving means the DAQ pillar is working. The result card needs both the model correctly selected and result read in the loop.
-
You hold a shake past the threshold, and the banner shows up waiting to trigger the whole time, but there’s no sound or counter increase. Which point is still empty? (single choice · objective 1)
- a) Point 5, fire_alert(r[“label”], conf_s)
- b) Point 2
- c) Point 1
- d) Nothing is wrong
Solution
a — all three gates have passed, but the fire_alert action is never called. That’s the end of the pipeline that makes the sound, the banner, and the count.
-
What’s the best evidence for explaining why you chose HITS_NEEDED = 4? (single choice · objective 2)
- a) “It felt about right”
- b) A results table: at 4, real events alert 5/5 with decoys alerting 0/5, while at 2, decoys alert 3/5
- c) The same value your friend used
- d) The largest value you can enter
Solution
b — engineering decisions must stand on measurement. The count of alerts from real events versus decoys is the evidence for the trade-off.
-
What should a good capstone demo show, besides an alert that works? (single choice · objective 3)
- a) The whole file’s code on screen
- b) A decoy that Guardian doesn’t alert on, and the reasoning behind the trade-off chosen
- c) The number of lines of code
- d) Nothing more is needed
Solution
b — the hard, valuable part is rejecting a fake signal, and explaining why these values were chosen. That’s the difference between a product and a demo.
The MVP for lessons 8.1–8.2: deliver a Guardian with at least three working pillars (DAQ, Processing, Apps), watching for a target event, passing through a decision layer (a filtered conf with debounce), with an action that genuinely fires — and be able to explain the design.
- All five points in the practice file are filled in. Run it on the emulator or the board until you see a real alert.
- Design your own Guardian, set the design values, then test the real event and the decoy at least five times each, and note the results in your learning log.
- Try at least two sets of design values, and choose the one you’ll use with reasons from the numbers.
- Record or perform a two-minute demo: a real alert, a decoy that doesn’t alert, and the trade-off chosen, closing with one research question or extension direction.
Going further
Section titled “Going further”The course is now complete. Extend it in any of the four directions in this lesson, or go back to module 5 to train your own model and plug it into Guardian.
This is the course’s last lesson. Return to the course page to see where to go next.
Reflect
Section titled “Reflect”- How far did the design values you chose end up from the starting values, and which number changed your mind?
- If you had to ship this Guardian for a customer to actually use, what would still be missing the most?
Review questions
Answer on your own first, then open the answer.
-
The Guardian shows RUNNING, the result card never moves, but motion follows the shaking. Which point is empty? (Objective 1)
- จุดที่ 2 อ่านความเร่ง
- จุดที่ 3 r = edge_ai.result() (หรือจุดที่ 1 select)
- จุดที่ 4 EMA
- จุดที่ 5 fire_alert
Show answer
Answer: B. จุดที่ 3 r = edge_ai.result() (หรือจุดที่ 1 select)
motion ขยับแปลว่าเสา DAQ ทำงาน การ์ดผลต้องการทั้งโมเดลที่ถูก select และการอ่าน result ในลูป
-
You shake long enough but the banner stays on waiting and there is no sound or counter. Which point is empty? (Objective 1)
- จุดที่ 5 fire_alert(r["label"], conf_s)
- จุดที่ 2
- จุดที่ 1
- ไม่มีจุดใดผิด
Show answer
Answer: A. จุดที่ 5 fire_alert(r["label"], conf_s)
ด่านทั้งสามผ่านแล้วแต่ไม่มีการเรียกการกระทำ fire_alert คือปลายท่อที่ทำเสียง แบนเนอร์ และนับครั้ง
-
What is the best evidence for choosing HITS_NEEDED = 4? (Objective 2)
- "รู้สึกว่ากำลังดี"
- ตารางผลทดลอง: ที่ 4 เหตุการณ์จริงเตือนครบ 5/5 และเคสหลอกเตือน 0/5 ส่วนที่ 2 เคสหลอกเตือน 3/5
- ค่าเดียวกับเพื่อน
- ค่าที่มากที่สุดที่ใส่ได้
Show answer
Answer: B. ตารางผลทดลอง: ที่ 4 เหตุการณ์จริงเตือนครบ 5/5 และเคสหลอกเตือน 0/5 ส่วนที่ 2 เคสหลอกเตือน 3/5
การตัดสินใจเชิงวิศวกรรมต้องยืนบนการวัด จำนวนการเตือนจากเหตุการณ์จริงเทียบกับเคสหลอกคือหลักฐานของการแลกเปลี่ยน
-
Besides a working alert, what should a good capstone demo show? (Objective 3)
- โค้ดทั้งไฟล์บนจอ
- เคสหลอกที่ Guardian ไม่เตือน และเหตุผลของการแลกเปลี่ยนที่เลือก
- จำนวนบรรทัดของโค้ด
- ไม่ต้องมีอะไรเพิ่ม
Show answer
Answer: B. เคสหลอกที่ Guardian ไม่เตือน และเหตุผลของการแลกเปลี่ยนที่เลือก
ส่วนที่ยากและมีค่าคือการปฏิเสธสัญญาณปลอม และการอธิบายว่าทำไมตั้งค่าแบบนี้ นั่นคือความต่างระหว่างผลิตภัณฑ์กับเดโม
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.
"Hands-on: build and ship an edge AI app" 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: "ลงมือทำ: สร้างและส่งมอบแอป Edge AI" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
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