Skip to content

Hands-on: your first model menu

Module 1 — Getting started: run the real thing, then take it apart · Slides: slides.md · Module overview · Course page

Walk through the s01_first_inference.py file along the four-beat MicroPython program skeleton, then fill its four blanks with the select, result, verdict.text and stop calls until the edge AI menu works end to end and shuts down cleanly.

By the end of this lesson, you will:

  1. Point out which parts of s01_first_inference.py belong to the import, create-once, loop and ui.poll beats, and explain why widgets are created outside the loop.
  2. Fill the four blanks in practice/s01_first_inference.py until Load shows the winning class and every class’s confidence bar changing with motion or sound, and Stop really stops the model.
  3. Explain what checking r[‘seq’] before redrawing and the finally block that calls edge_ai.stop() protect against.
  4. Find a motion or sound that keeps conf below 50% and explain why the scores spread out.

You’ve been through lesson 1.2, and know what the four calls models, select, result, stop do. Open the practice file in BENTO IDE, and keep your notes ready to record what motion or sound each model needs to win.

Nearly every MicroPython program on BENTO follows four beats: import → create widgets once → a loop that reads values and updates the screen → ui.poll, which handles buttons and touches, then loops back. Creating widgets outside the loop, and only changing their values inside it, keeps the screen from flickering and saves memory. The s01_first_inference.py file reads as a single sentence: ask what models exist → select one → loop reading results → put the winning class on screen → stop on exit.

The four blanks are exactly the four core calls: the Load button calls edge_ai.select(sel) inside a try, and only shows RUNNING after select succeeds. Inside the loop, it reads r = edge_ai.result() and only redraws when r['seq'] changes, using verdict.text(r['label'] or '-') and a bar for every class from r['scores'] (the class where i == top is coloured green). The Stop button calls edge_ai.stop(), and the finally block, already given, stops the engine every time you exit — whether that’s the back button or an error. This is an embedded systems habit: always leave the machine in a state you know for certain.

Success in this lesson isn’t just “the text moves” — you should be able to say what conf 92% actually means, and why the model isn’t sure yet for some motions. A motion halfway between two classes, or a sound that resembles several classes, spreads the score out, with no class winning by much.

Use the help ladder in order: the # TODO: hints in the practice file → the fill-in table in the slides → the solution → the full version. s01_first_inference_full.py is a polished version that adds latency and uses CONF_FLOOR to colour “confident” apart from “not sure yet”. Open it once you’ve passed the practice file, and find what’s different from your own file.

File What this file teaches
examples/s01_first_inference_full.py A 6-model edge AI menu (full version)

The file has 4 blanks, each marked with a # TODO: comment. Fill them in this order, and test after each one: 1) the Load button → edge_ai.select(sel) 2) inside the loop → r = edge_ai.result() 3) on a new result → verdict.text(r['label'] or '-') 4) the Stop button → edge_ai.stop(). If you forget blank 1, it will show RUNNING with no results at all. If you forget blank 3, the bars move but the large winning-class text never changes. If nothing shows up, check your indentation and call names first.

Practice file Topic
practice/s01_first_inference.py Running our first edge AI model (the fill-in-the-code version)

Open the solution after trying on your own at least once, and read how to use the solutions first.

Solution Pairs with
solution/s01_first_inference.py practice/s01_first_inference.py

The same questions are in quiz.yaml for automated checking.

  1. Put the four beats of a MicroPython program on BENTO in order (ordering · objective 1)

    • a) Loop: read results then update the screen
    • b) import: edge_ai, ui, lcd, time
    • c) ui.poll: handle buttons and touches
    • d) create-once: read models() and create widgets
    Solution

    b → d → a → c — import → create-once → loop → ui.poll, then loop back. Widgets are created outside the loop, and only their values change inside it, so the screen doesn’t flicker.

  2. You filled in the practice file. Pressing Load shows RUNNING, but there’s never any inference result at all. Which blank was most likely left unfilled? (single choice · objective 2)

    • a) Blank 1: edge_ai.select(sel) in the Load button
    • b) Blank 3: verdict.text(…)
    • c) Blank 4: edge_ai.stop()
    • d) The import lcd line
    Solution

    a — if select() is never called, the model is never actually told to run, yet the code still shows RUNNING on the following line. The fill-in table in the slides notes this symptom directly.

  3. The confidence bars move for every class, but the large winning-class text never changes. Which blank is still unfilled? (single choice · objective 2)

    • a) r = edge_ai.result()
    • b) verdict.text(r[‘label’] or ‘-’)
    • c) edge_ai.stop()
    • d) edge_ai.select(sel)
    Solution

    b — the bars come from r[‘scores’], which is already given, proving result() is working. The large text still needs verdict.text filled in yourself.

  4. Which of these correctly explain the reasoning behind checking r[‘seq’] and the finally block? (select every correct answer) (multiple choice · objective 3)

    • a) Checking seq means only redrawing when there’s a new result, instead of redrawing every round
    • b) finally means edge_ai.stop() is called no matter how you exit, so the engine never keeps running unattended
    • c) seq is the model’s confidence, as a percentage
    • d) finally means the program can never have an error
    Solution

    a, b — seq increases every time there’s a new result, telling you whether to redraw. finally doesn’t prevent errors — it guarantees that the engine is always stopped on the way out.

  5. If you make a motion halfway between circle and shaking, what do you typically see on screen? (single choice · objective 4)

    • a) One class wins outright at 100%
    • b) The score is split between the two classes; the winner’s conf is low, possibly below 50%
    • c) The model stops working
    • d) latency_ms becomes zero
    Solution

    b — when the data resembles several classes, softmax splits the score across them, so the winner only wins narrowly. This is exactly why CONF_FLOOR exists.

The MVP for lessons 1.1–1.3: run the edge_ai menu and read live results — both the winning class (label) and the confidence (conf) change with real motion or sound.

  • All four blanks in the practice file are filled in, and it runs on the emulator or the board.
  • Switch between at least three models, and note in your learning log what each one needs to win.
  • Find a motion or sound that keeps conf below 50%, and explain why.
  • Be able to explain where in the code models, select, result and stop are called, and what each one does.

In the next pair of lessons (1.4–1.5), we’ll take the sensor app apart piece by piece, nail down the four-beat structure completely, and remix it into something of our own.

Next lesson: lesson 1.4 — Taking apart the sensor app: the shared four-beat structure of every program

  • If you removed the seq check, how would the screen’s behaviour change, and who pays the price for that?
  • Which model was hardest for you to make win, and do you think that’s because of the model, or because of how we fed it data?

Review questions

Answer on your own first, then open the answer.

  1. Order the four beats of a BENTO MicroPython program. (Objective 1)

    1. ลูป: อ่านผลแล้วอัปเดตจอ
    2. import: edge_ai, ui, lcd, time
    3. ui.poll: รับปุ่มและการแตะจอ
    4. สร้างครั้งเดียว: อ่าน models() และสร้าง widget
    Show answer

    Correct order: B. import: edge_ai, ui, lcd, time → D. สร้างครั้งเดียว: อ่าน models() และสร้าง widget → A. ลูป: อ่านผลแล้วอัปเดตจอ → C. ui.poll: รับปุ่มและการแตะจอ

    import → สร้างครั้งเดียว → ลูป → ui.poll แล้ววนกลับ widget สร้างนอกลูป ในลูปแค่เปลี่ยนค่า จอจึงไม่กระพริบ

  2. After filling the practice file, Load shows RUNNING but no result ever appears. Which blank was most likely missed? (Objective 2)

    1. จุดที่ 1: edge_ai.select(sel) ในปุ่ม Load
    2. จุดที่ 3: verdict.text(...)
    3. จุดที่ 4: edge_ai.stop()
    4. บรรทัด import lcd
    Show answer

    Answer: A. จุดที่ 1: edge_ai.select(sel) ในปุ่ม Load

    ถ้าไม่เรียก select() โมเดลไม่ถูกสั่งให้รันเลย แต่โค้ดยังขึ้นข้อความ RUNNING ตามบรรทัดถัดไป ตารางช่องเติมในสไลด์บอกอาการนี้ไว้ตรง ๆ

  3. Every confidence bar moves but the big winning-class text never changes. Which blank is still empty? (Objective 2)

    1. r = edge_ai.result()
    2. verdict.text(r['label'] or '-')
    3. edge_ai.stop()
    4. edge_ai.select(sel)
    Show answer

    Answer: B. verdict.text(r['label'] or '-')

    แถบมาจาก r['scores'] ที่ให้ไว้แล้ว แสดงว่า result() ทำงาน ส่วนตัวอักษรใหญ่ต้องเติม verdict.text เอง

  4. Which statements correctly give the reasons for checking r['seq'] and for the finally block? (select all that apply) (Objective 3)

    1. เช็ก seq เพื่อวาดจอเฉพาะเมื่อมีผลใหม่ ไม่วาดซ้ำทุกรอบ
    2. finally ทำให้ออกยังไงก็เรียก edge_ai.stop() เครื่องยนต์ไม่รันค้าง
    3. seq คือความมั่นใจของโมเดลเป็นเปอร์เซ็นต์
    4. finally ทำให้โปรแกรมไม่มีวันเกิด error
    Show answer

    Answer: A. เช็ก seq เพื่อวาดจอเฉพาะเมื่อมีผลใหม่ ไม่วาดซ้ำทุกรอบ · B. finally ทำให้ออกยังไงก็เรียก edge_ai.stop() เครื่องยนต์ไม่รันค้าง

    seq เพิ่มขึ้นทุกครั้งที่มีผลใหม่ จึงบอกได้ว่าควรวาดไหม ส่วน finally ไม่ได้กัน error แต่รับประกันว่าตอนออกจะหยุดเครื่องยนต์เสมอ

  5. If you make a motion halfway between circle and shaking, what do you usually see? (Objective 4)

    1. คลาสใดคลาสหนึ่งชนะขาดที่ 100%
    2. คะแนนกระจายระหว่างสองคลาส conf ของผู้ชนะต่ำ อาจต่ำกว่า 50%
    3. โมเดลหยุดทำงาน
    4. latency_ms กลายเป็นศูนย์
    Show answer

    Answer: B. คะแนนกระจายระหว่างสองคลาส conf ของผู้ชนะต่ำ อาจต่ำกว่า 50%

    เมื่อข้อมูลคล้ายหลายคลาส softmax จะแบ่งคะแนนไปหลายคลาส ผู้ชนะจึงชนะแบบเฉียด นี่คือเหตุผลที่ต้องมี CONF_FLOOR

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: your first model menu" 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: "ลงมือทำ: เมนูโมเดลตัวแรกของเรา" จาก 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/edge-ai-developer/m01-onboarding/l03-first-inference-lab/

Full guide: how to cite TESA

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