14. Interface and Application Programming
Assignment
Write an application that interfaces a user with an input and/or output device that you made. Compare as many tool options as possible and document your work on the group assignment page.
For this week I built a desktop application using Python and Qt Designer to communicate with the ESP32 board I designed in previous weeks. The interface allows a user to set a medication alarm with day-of-week repetition, and when the alarm fires the app confirms pill intake and triggers a servo motor that opens the physical pill dispenser.
Group AssignmentSystem Overview
The project is divided into two main sides: the PC application (Python + Qt) and the ESP32 firmware (Arduino). They talk to each other through a USB serial connection at 115200 baud using plain text commands — simple enough to read by eye in the Serial Monitor.
Hardware used
| Component | Pin | Function |
|---|---|---|
| OLED SH1106 128×64 | SDA=GPIO0, SCL=GPIO1 | Displays current time and alarm status |
| RTC DS3231 | Same I2C bus | Keeps real time even without power |
| Buzzer | GPIO 25 | Audible alarm when it is time to take the pill |
| LED | GPIO 26 | Visual indicator — blinks with the buzzer |
| Servo motor | GPIO 27 | Rotates 90° to open the pill compartment |
| Push button (PCB) | GPIO 34 | Physical confirm button on the board |
Serial protocol
| PC → ESP32 | ESP32 → PC |
|---|---|
SET_ALARM:HH:MM:LMMJVSD — sets alarm time and days | OK_ALARM — alarm saved confirmation |
CONFIRM — user confirmed pill was taken | ALARM_ON — it is time to take the pill |
ALARM_OFF — alarm has been dismissed | |
TIME:HH:MM:SS — current time (sent every second) |
About this tab
Everything you need to install and organize before touching any code. Follow every step in order — skipping one will usually break the next.
Step 1 — Install Python
Python is the programming language the application runs on. You only need to install it once.
- Go to python.org/downloads and download the latest Python 3.11+ installer for Windows.
- Run the installer. On the first screen, check the box that says "Add Python to PATH" — this is critical, do not skip it.
- Click Install Now and wait for it to finish.
- Open a terminal (search cmd in the Windows start menu) and type the command below to verify it worked. You should see a version number printed.
python --version
Step 2 — Organize your project folder
Create a dedicated folder for the project so all files stay together. A good location is your Desktop or Documents folder. The folder should contain exactly these files:
File structure
| File | What it does |
|---|---|
pastillero.ui | Visual layout — edit this in Qt Designer |
frontend.py | Generated automatically from the .ui file — do not edit by hand |
backend.py | All the application logic goes here |
base_serial.py | Handles the serial connection in a background thread so the window does not freeze |
requirements.txt | List of Python libraries the project needs |
pastillero_esp32.ino | Arduino sketch that runs on the ESP32 |
Step 3 — Install the Python libraries
Open a terminal, navigate to your project folder using the cd command, and run:
python -m pip install -r requirements.txt
This installs PyQt6 (the graphical interface library) and pyserial (for serial communication with the ESP32). Wait until the terminal shows "Successfully installed" before continuing.
Key terminal commands for Windows
| Command | What it does |
|---|---|
cd nombre_carpeta | Enters a folder. Example: cd Desktop\pastillero |
cd .. | Goes back one folder level |
dir | Lists all files in the current folder |
python backend.py | Runs the application |
python -m pip install pyserial | Installs the pyserial library manually |
python -m PyQt6.uic.pyuic pastillero.ui -o frontend.py | Converts the Qt Designer file into Python code |
Ctrl + C | Stops a running program in the terminal |
What is Qt Designer?
Qt Designer is a visual drag-and-drop editor that lets you build the graphical interface without writing code. You place buttons, labels and text boxes on a canvas, and it generates the Python file automatically. Think of it like Canva but for app windows.
File structure — who does what
The Python side is split into three files following the same template the professor gave us. Each file has one clear job so the code stays readable and easy to modify.
base_serial.py — the serial worker
This file runs the serial port in a background thread. A thread is like a second worker running at the same time as the main program. Without it, the window would freeze every time it waits for data from the ESP32. You do not need to edit this file — just keep it in the folder.
The three things it does: detect available COM ports, open or close the connection,
and continuously read incoming lines and pass them to a callback function in
backend.py.
backend.py — the application logic
This is the only file you write yourself. It imports the interface from
frontend.py, connects each button to a function, and defines what happens
when data arrives from the ESP32. The three key functions to understand:
enviarAlarma() — sends the alarm to the ESP32
Reads the time selected in the Time Edit widget and which day checkboxes are checked, then builds a text command and sends it through serial. The format is:
SET_ALARM:08:30:1110100Hour:Minute followed by 7 digits (1=active, 0=inactive) for Mon through Sun.
confirmarToma() — confirms pill was taken
Sends the text CONFIRM to the ESP32. The board then turns off the buzzer
and LED, rotates the servo to open the pill compartment, and sends back
ALARM_OFF to update the interface.
procesarDato() — handles messages from the ESP32
This function is called automatically every time the ESP32 sends a line.
It checks what the message says and updates the interface accordingly —
enabling the Pill Taken button when ALARM_ON arrives,
and updating the clock display every second when TIME:HH:MM:SS arrives.
Running the application
Open a terminal in the project folder and run:
python backend.py
The window opens immediately. Select the COM port of your ESP32 in the dropdown,
click Connect, set a time and days, and press Send Alarm.
The ESP32 will confirm with OK_ALARM in the status bar.
What the ESP32 does
The Arduino sketch runs a continuous loop that does four things simultaneously: reads serial commands from Qt, checks if it is alarm time by comparing the RTC with the stored alarm, drives the buzzer and LED when the alarm is active, and sends the current time to Qt every second.
Required Arduino libraries
Install all of these from Arduino IDE → Sketch → Include Library → Manage Libraries:
| Library | What it does |
|---|---|
| RTClib by Adafruit | Communicates with the DS3231 real-time clock |
| Adafruit SH110X | Drives the SH1106 OLED display |
| Adafruit GFX Library | Required by SH110X for drawing text and shapes |
| ESP32Servo | Controls the servo motor on ESP32 (replaces the standard Servo library) |
First time setup — syncing the RTC
The DS3231 keeps time even without power, but the first time you use it you need
to set the correct time. In the sketch find this line and uncomment it
(remove the //):
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
Upload the sketch once with this line active. The RTC will be set to your computer's clock at compile time. Then comment it back out and upload again — otherwise the time resets every time the board restarts.
How the alarm logic works
Every second the sketch reads the RTC and compares the current hour and minute with
the stored alarm values. It also checks the day of the week against the
diasActivos[] array that was set by the SET_ALARM command.
If all three match (hour, minute, and day) and the alarm has not already fired this
minute, it calls activarAlarma() which sends ALARM_ON to Qt
and starts the buzzer and LED blink cycle.
When the user confirms (either from the Qt button or the physical PCB button),
confirmarToma() turns everything off, rotates the servo 90° for 2 seconds
to open the pill compartment, then closes it and sends ALARM_OFF back to Qt.
Wire.begin(SDA, SCL)
call at the top of setup().
Final result
The complete system: Qt interface on the PC communicating with the ESP32 in real time. The OLED and the Qt clock display show the same time, both synchronized from the RTC.
Interface
The final interface has four sections: the real-time clock pulled from the RTC, the alarm configurator with time and day selection, the state card that changes color when the alarm fires, and the serial connection controls.
Physical hardware
Reflections
The biggest challenge was understanding how Qt's threading model works. The serial port runs in a background thread, but Qt only allows the main thread to touch the interface. The solution was a Bridge object with a pyqtSignal that safely passes messages from the serial thread to the main thread — a pattern that is common in Qt applications but not obvious at first.
Another learning was that regenerating frontend.py with
pyuic6 overwrites any manual edits, so all styling logic lives in
backend.py using setStyleSheet(). This keeps the workflow
clean: design in Qt Designer, logic in Python.