14. Interface and Application Programming

This week's assignment was to write an application for a embedded board that interfaces a user with an input and/or output device. I decided to build a pill dispenser desktop app that communicates over Serial with a XIAO ESP32-C6. The app shows the real time read from a DS3231 RTC directly on the screen, letting the user set an alarm with a specific time and choose which days of the week it should trigger. And when that alarm fires it activates a passive buzzer and a servo motor on the board to physically dispense a pill. I built the interface in Qt Designer and wrote the logic in Python using PySerial for the Serial connection.

The general workflow this week was:

Key Concepts

Backend Code

This is the Python side of the project. It loads the interface generated from Qt Designer, opens the Serial connection, listens for messages from the XIAO ESP32-C6, and sends commands when the user sets an alarm or confirms that the pill was taken. In this section I will share the full code of backend.py and slides explaining the most important parts.

Learning Outcomes

This week helped me connect interface design, Python programming, Serial communication, and embedded hardware into one complete pill dispenser system. These were my main takeaways:

  • Qt Designer makes interface building much faster: Designing the pill dispenser window visually helped me organize the time display, alarm controls, day checkboxes, port selector, and action buttons before writing the Python logic. Renaming each widget with a clear objectName was essential because those names are what backend.py uses to control the interface.
  • Keeping the interface and backend separate makes the project easier to manage: I used Qt Designer to create pastillero.ui, converted it with pyuic6 into frontend.py, and kept the behavior in backend.py. This structure made it easier to update the visual design, connect buttons, read values from the widgets, and debug the app without mixing layout code with logic.
  • A virtual environment keeps the Python project reproducible: Creating a venv and installing PyQt6 and pyserial inside it helped keep the app's dependencies organized. Saving the installed packages in requirements.txt also makes the project easier to recreate on another computer.
  • Serial communication needs a clear protocol: Defining messages such as TIME, SET_ALARM, PILL_TAKEN, ALARM_TRIGGERED, and PILL_CONFIRMED made communication between Python and the XIAO ESP32-C6 predictable. Because every message had a specific format, both sides could parse commands and respond without confusion.
  • The desktop app and firmware have to be tested as one system: The final behavior depended on many parts working together: the DS3231 RTC sending the time, Python displaying it, the user setting an alarm, the XIAO ESP32-C6 activating the servo and buzzer, and the confirmation message returning to the app. Testing the full chain helped me find problems that would not appear when testing each component alone.
  • Threading is important for responsive interfaces: Reading Serial data continuously can block a program, so using a worker thread allowed the app to keep receiving messages from the board while the PyQt6 interface stayed usable and responsive.

Files