The touch screen and your first widgets
Module 2 — From Screen to Hardware · Slides: slides.md · Module overview · Course page
Understand how a finger on the glass reaches your Python code through the other core’s event queue, then build your first widgets at the right place and font size, and keep their id numbers so you can tell which one was tapped.
Objectives
Section titled “Objectives”By the end of this lesson you will be able to:
- Explain the four-hop path from a finger on the glass to Python (touch detector, CM55 finding the widget, the event queue, ui.poll() on CM33), how it differs from polling a real button, and what happens if the code never calls ui.poll()
- Create ui.Label, ui.Button and ui.Switch after ui.screen() with your own x, y, w, h and color, say when to let the screen lay things out (x=-1), and read value= on Label/Button/Dropdown/Textarea as a font size limited to 14/16/20/24/28, not a measured value
- Keep the .id() of several buttons in a list and turn an event carrying a handle number and type ‘clicked’ back into the button’s row with .index(), following the five steps of building a widget
Before you start
Section titled “Before you start”Lessons 2.4–2.6 build on the loop from lessons 2.1–2.3 (read → decide → command → wait → loop again); the only change is that the input now comes from a finger on the glass.
Teams who have not yet tried 07_button_to_broker.py from lesson 2.3 lose nothing — the main content does not depend on it.
Have your learning log ready to note the font sizes you try and the .id() numbers you get from the hands-on part at the end of the lesson.
- 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 2.3 — Hands-on: running lights, a button, and the broker
See it work first
Section titled “See it work first”On the Eva Kit, open the Controls menu you played with back in lesson 1.1, and touch a coloured circle again. Watch for the three things this set of lessons must build for itself: the button knowing it was touched · the real LED changing state · the text on screen changing to match. Teams with a Dev Kit have no such card, which is all the more reason to build it yourselves — look instead at the finished picture of the solution in the slides. Behind this ordinary-looking page is an event-handling circuit we are about to write ourselves.
Concepts
Section titled “Concepts”The spell for this set of lessons is: the screen is not the real thing, the screen reports the real thing, and the two must always agree. The reason you need a button on screen when a real button can already command a light is that once a machine ships, someone else uses it and only ever sees the screen. The board gives Python only one real button to touch, but one machine has more than one command, and a panel that disagrees with the real thing deceives the person operating the machine. Even so, a real button still has one advantage the screen does not: a finger can find it by feel without looking.
A button on screen is not a button on the board. A real button wires straight into a chip pin, which we can “read” ourselves whenever we like. A button on screen is an image the CM55 draws.
The touch screen does not measure pressure at all; it measures the closeness of a conductor (a finger increases capacitance at that spot), and the detector lives entirely on the CM55 side
while Python runs on CM33 — a different core. So the CM55 logs the event into a queue and waits for us to “collect what was left for us” with ui.poll().
If we never ask, the queue does not lose the event, but nothing happens either.
The five steps of a widget (the same order as LVGL in C): 1 create it, ui.Button("Red") · 2 place it at x, y, w, h
(or .pos(x, y)) · 3 its look: text, color · 4 remember its handle, btn.id() · 5 filter events, ev['type'] == 'clicked'.
Steps 1–3 are what you see; steps 4–5 are what responds. Step 4 differs most from C, because C hands a callback for the system to call,
while we keep our own id number to check ourselves in the loop. Always start a new page with ui.screen() to clear out the old one. x, y is the top-left corner in pixels;
leave out w, h and the screen fits the widget to the text. Colours are 0xRRGGBB, and x=-1 (the default) lets the screen lay widgets out in the order you created them —
good for quick experiments, but a real control panel must set its own positions, because “where the button is” is part of the design.
A trap every group falls into: on Label, Button, Dropdown and Textarea, value= is a font size, accepting only 14 / 16 / 20 / 24 / 28.
ui.Label("Temperature", value=24) therefore means text 24 pixels tall, not 24 degrees. min, max and value that mean their literal name
apply only to Slider, Arc and Bar. To change a Label’s text, always use .text("new text").
A handle is an id card. What you get back when creating a widget is a handle bound to the real widget on the CM55. An event from ui.poll()
does not say “the red button was pressed” — it only gives a number, for example {'handle': 4, 'type': 'clicked'}, with no name, no colour, no text.
With several buttons, the cleanest approach is on_ids = [b.id() for b in btn_on], then on_ids.index(ev['handle']) gives back the row number.
If you did not keep .id(), there is no way at all to tell which one sent the event when it arrives.
Worked example
Section titled “Worked example”The slides for this lesson also refer to files that live in other lessons:
- m02-ui-to-hardware/l03-led-button-lab/examples/07_button_to_broker.py — a button and a light on our desk end up on the broker, for a web page to read
- m02-ui-to-hardware/l03-led-button-lab/solution/s03_led_button.py — an LED chase over every LED plus a debounced button counter
- shared/web/mqtt_dashboard.html — a class-wide dashboard
Check your understanding
Section titled “Check your understanding”The same questions are in quiz.yaml for automatic marking.
-
A team creates three buttons and writes a loop that only sleeps, with no ui.poll() at all. The user taps the red button three times. What happens? (choose one · objective 1)
- A) The red LED lights on its own, because the CM55 already knows this button is named red
- B) The events are logged in the queue on the CM55 side and wait there, but the Python code does nothing at all, because nobody asked
- C) The program stops with an error, because the queue is full
- D) CM33 reads the finger position directly from the screen itself, so ui.poll() is not needed
Solution
B — The touch detector lives entirely on the CM55 side, while Python runs on CM33. The CM55 logs the event into a queue and waits for us to ask with ui.poll(). If we never ask, the event is not lost, but nothing happens either.
-
Which statement correctly explains how the board’s touch screen knows a finger has touched it? (choose one · objective 1)
- A) It measures pressure — the harder you press, the more clearly it registers
- B) There is a tiny mechanical switch under the glass at every point
- C) It measures the closeness of a conductor — a person’s finger approaching the glass increases the capacitance at that spot
- D) Python code on CM33 polls the screen’s pin one point at a time
Solution
C — The touch screen does not measure pressure at all; it measures the closeness of a conductor. A person’s finger is a conductor, so it changes the capacitance at that point with no mechanical switch needed. This same principle comes back in lessons 2.7–2.9 when reading CapSense.
-
What does the line
ui.Label("Temperature", value=24)mean? (choose one · objective 2)- A) Display a temperature value of 24 degrees on the label
- B) The text on the label is 24 pixels tall
- C) Place the label 24 pixels from the left edge
- D) The label updates every 24 ms
Solution
B — On Label, Button, Dropdown and Textarea, value= is the font size and only accepts 14, 16, 20, 24, 28. A value that means its literal name applies only to Slider, Arc and Bar. To change the text, use .text() instead.
-
Which statements about placing widgets on screen are correct? Choose every correct one. (choose all that apply · objective 2)
- A) x=-1 (the default) lets the screen lay widgets out in the order they were created; good for quick experiments, but you cannot control the position
- B) A real control panel should set its own x, y, because a button’s position is part of the design
- C) If w, h are left out, the screen fits the widget to the text automatically
- D) x, y are measured from the bottom-right corner of the screen
- E) You must create a new widget every time you want to change the text on a Label
Solution
A, B, C — x, y is the top-left corner in pixels, and text is changed with .text() on the same widget, with no need to recreate it. x=-1 lets the screen lay things out on its own, which does not suit a control panel that must design its layout.
-
Suppose
on_ids = [3, 4, 5]holds the ids for the red, green and blue buttons in that order, and ui.poll() returns{'handle': 4, 'type': 'clicked'}. What doeson_ids.index(ev['handle'])give? (choose one · objective 3)- A) 4, the button’s id number
- B) 1, the row of the green button
- C) “green”, because the event tells you the button’s name
- D) 2, the row of the blue button
Solution
B — The event only gives a number, with no name and no colour. .index() finds where the number 4 sits in the list, which is position 1 (counting from 0) — the row of the green button. Without keeping .id(), there would be no way to tell which one sent the event.
Hands-on (about 20 minutes). Write it in BENTO IDE and send it to a board or the Emulator with BENTO Playground kept open.
There is no event loop yet — lesson 2.5 will make the buttons respond. A widget you create stays on screen until someone calls ui.screen() again,
but end your script with ui.poll() once, because the CM55 hides the whole set of widgets until the first ui.poll() arrives.
If you never call it, the screen stays blank for about 2 seconds before things appear (rule 1 of the ui module, in lesson 2.5).
- Start with
ui.screen(), followed bytime.sleep_ms(200), then create the three lines from the slides (title,btn,sw); check that each one sits exactly at the x, y you commanded - Remove
x=andy=from the button, then run it again; note where the button ends up when the screen is left to lay it out on its own - Create five Labels with
value=14, 16, 20, 24 and 28, and pick the size that reads clearly from standing distance. Try one value outside these five once, and note what happens - Change the text of one Label with
.text()instead of creating a new one - Create three buttons — red, green, blue — keep their
.id()in a list, andlcd.printthem out. Record the numbers in your learning log, and notice they are pure numbers, with no name or colour
Going further
Section titled “Going further”Lesson 2.5 puts these stored id numbers to real use: write an event loop that calls ui.poll() every round, tell events apart by handle, and command gpio.led()
so the real LED lights up exactly when the screen reports it.
Next lesson: Lesson 2.5 — The event loop: touch the screen, light the real LED
Reflect
Section titled “Reflect”- What does a car gain and lose by moving every button onto one touch screen? If it were a control panel on factory machinery, which real buttons would you keep?
- If an event only gives a number, and one day you insert a new button into the middle of the list, which part of the code needs the most care?
Review questions
Answer on your own first, then open the answer.
-
A team creates three buttons, then writes a loop that only sleeps and never calls ui.poll(). A user taps the red button three times. What happens? (Objective 1)
- หลอดแดงติดเอง เพราะ CM55 รู้อยู่แล้วว่าปุ่มนี้ชื่อแดง
- เหตุการณ์ถูกจดไว้ในคิวฝั่ง CM55 และรออยู่ แต่โค้ด Python ไม่ทำอะไรเลย เพราะไม่มีใครมาถาม
- โปรแกรมหยุดด้วย error เพราะคิวเต็ม
- CM33 อ่านตำแหน่งนิ้วจากจอเองโดยตรง จึงไม่จำเป็นต้องมี ui.poll()
Show answer
Answer: B. เหตุการณ์ถูกจดไว้ในคิวฝั่ง CM55 และรออยู่ แต่โค้ด Python ไม่ทำอะไรเลย เพราะไม่มีใครมาถาม
ตัวตรวจจับสัมผัสอยู่ฝั่ง CM55 ทั้งหมด ส่วน Python รันบน CM33 CM55 จึงจดเหตุการณ์ใส่คิวแล้วรอให้เรามาถามด้วย ui.poll() ถ้าไม่ถาม เหตุการณ์ไม่หาย แต่ก็ไม่มีอะไรเกิดขึ้น
-
Which statement correctly explains how the board's touchscreen knows a finger has touched it? (Objective 1)
- วัดแรงกด ยิ่งกดแรงยิ่งรับรู้ได้ชัด
- มีสวิตช์กลขนาดเล็กอยู่ใต้กระจกทุกจุด
- วัดความใกล้ของตัวนำ นิ้วคนที่เข้าใกล้กระจกเพิ่มความจุไฟฟ้าให้จุดนั้น
- โค้ด Python บน CM33 อ่านขาของจอทีละจุดแบบ polling
Show answer
Answer: C. วัดความใกล้ของตัวนำ นิ้วคนที่เข้าใกล้กระจกเพิ่มความจุไฟฟ้าให้จุดนั้น
จอสัมผัสไม่ได้วัดแรงกด มันวัดความใกล้ของตัวนำ นิ้วคนเป็นตัวนำจึงเปลี่ยนความจุไฟฟ้าที่จุดนั้นโดยไม่ต้องมีสวิตช์กล หลักการเดียวกันนี้กลับมาอีกในบทเรียน 2.7–2.9 ตอนอ่าน CapSense
-
What does the line `ui.Label("อุณหภูมิ", value=24)` mean? (Objective 2)
- แสดงค่าอุณหภูมิ 24 องศาบนป้าย
- ตัวอักษรบนป้ายสูง 24 พิกเซล
- วางป้ายห่างจากขอบซ้าย 24 พิกเซล
- ป้ายจะอัปเดตทุก 24 ms
Show answer
Answer: B. ตัวอักษรบนป้ายสูง 24 พิกเซล
บน Label, Button, Dropdown และ Textarea value= คือขนาดฟอนต์ และรับแค่ 14, 16, 20, 24, 28 ส่วน value ที่เป็นค่าจริงใช้กับ Slider, Arc และ Bar เท่านั้น อยากเปลี่ยนข้อความให้ใช้ .text()
-
Which statements about placing widgets are true? Choose all that apply. (Objective 2)
- x=-1 (ค่าเริ่มต้น) ให้จอเรียงตามลำดับที่สร้าง เหมาะกับการลองของเร็ว ๆ แต่คุมตำแหน่งไม่ได้
- แผงควบคุมจริงควรกำหนด x, y เอง เพราะตำแหน่งของปุ่มเป็นส่วนหนึ่งของการออกแบบ
- ถ้าไม่ใส่ w, h จอเลือกขนาดพอดีข้อความให้เอง
- x, y วัดจากมุมขวาล่างของจอ
- ต้องสร้าง widget ใหม่ทุกครั้งที่อยากเปลี่ยนข้อความบน Label
Show answer
Answer: A. x=-1 (ค่าเริ่มต้น) ให้จอเรียงตามลำดับที่สร้าง เหมาะกับการลองของเร็ว ๆ แต่คุมตำแหน่งไม่ได้ · B. แผงควบคุมจริงควรกำหนด x, y เอง เพราะตำแหน่งของปุ่มเป็นส่วนหนึ่งของการออกแบบ · C. ถ้าไม่ใส่ w, h จอเลือกขนาดพอดีข้อความให้เอง
x, y คือมุมซ้ายบนเป็นพิกเซล และเปลี่ยนข้อความด้วย .text() บน widget เดิม ไม่ต้องสร้างใหม่ ส่วน x=-1 ปล่อยให้จอเรียงเอง ซึ่งไม่เหมาะกับแผงควบคุมที่ต้องออกแบบตำแหน่ง
-
Suppose `on_ids = [3, 4, 5]` holds the ids of the red, green and blue buttons in that order, and ui.poll() returns `{'handle': 4, 'type': 'clicked'}`. What does `on_ids.index(ev['handle'])` give? (Objective 3)
- 4 คือเบอร์ของปุ่ม
- 1 คือแถวของปุ่มเขียว
- "เขียว" เพราะเหตุการณ์บอกชื่อปุ่มมาด้วย
- 2 คือแถวของปุ่มน้ำเงิน
Show answer
Answer: B. 1 คือแถวของปุ่มเขียว
เหตุการณ์บอกแค่เบอร์ ไม่มีชื่อ ไม่มีสี .index() หาตำแหน่งของเบอร์ 4 ใน list ซึ่งอยู่ลำดับที่ 1 (นับจาก 0) คือแถวของปุ่มเขียว ถ้าไม่ได้เก็บ .id() ไว้ เราจะแยกไม่ออกว่าใครส่งมา
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.
"The touch screen and your first widgets" 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: "จอสัมผัสและ widget ตัวแรก" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
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-04.html (slides 1–14)
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