15. Interface and Application Programming

This week I have to create an interface.

I decided to try something with the sensor that I’m going to use for my final project. I think it’s perfect as an input; I only need to create an output. So, I decided to make an interface where, if the distance is short, the circle becomes smaller, but if the distance is large, the circle becomes big. The size of the circle depends on the distance of a movement.

I decided to use Visual Studio Code to try my interface in Python, because in the Embedded Programming week I used Python.

Group Task:

Group task

First, I need to learn about important modules to create my interface.

Visual Elements (PyQt5 Widgets)

Command Description
QApplication Creates the main application
QWidget Creates a window for other elements
QLabel Displays text on screen
QVBoxLayout Arranges elements vertically
QHBoxLayout Arranges elements horizontally
setLayout(layout) Adds the layout to the design
setWindowTitle("text") Sets the window title
resize(width, height) Changes the window size

Important Libraries in the Code

Command What It Does Why It's Important
import sys Lets your program interact with the system Required to use sys.exit() to close the program properly
import serial Imports the PySerial library to read data from a serial port (USB/COM) Needed to receive sensor data (like distance)
from PyQt5.QtWidgets import ... Imports GUI elements like windows (QWidget), labels (QLabel), and layouts Used to build the visual interface
from PyQt5.QtGui import QPainter, QBrush Imports tools for custom drawing (shapes and colors) Used to draw different shapes
from PyQt5.QtCore import QTimer, Qt QTimer allows repeated actions; Qt provides constants like colors Used to update shapes/text regularly and set styles

Doign tries

First i start with a code of a basic window

Base code

from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout
import sys

# Create application
app = QApplication(sys.argv)  # QApplication is the main application in PyQt5

# Create main window
ventana = QWidget()  # Create a window using QWidget()
ventana.setWindowTitle("My First PyQt5 App")  # Set the window title
ventana.resize(1000, 300)  # Set the window size (width, height)

# Create layout and welcome label
layout = QVBoxLayout()  # Vertical layout
mensaje_bienvenida = QLabel("Welcome to PyQt5")  # Create a label with text
mensaje_bienvenida.setStyleSheet("font-size: 30px; color: blue;")  # Set style for the label
mensaje_adios = QLabel("Goodbye")  # Create a label with text
mensaje_3 = QLabel("Message 3")  # Create a label with text
mensaje_4 = QLabel("Message 4")  # Create a label with text

# Add labels to the layout
layout.addWidget(mensaje_bienvenida)  # Add the label to the layout
layout.addWidget(mensaje_adios)  # Add the label to the layout
layout.addWidget(mensaje_3)  # Add the label to the layout
layout.addWidget(mensaje_4)  # Add the label to the layout

ventana.setLayout(layout)  # Set the layout on the window

# Show window
ventana.show()  # Display the window on screen
sys.exit(app.exec_())  # Run the application's main loop



I only added the titles and the text.

interfaces

Then I added the function class to define the type of object, and I used the QWidget library, which is a base class. def __init__(self): is used to assign values to the object. In this case, we assign the distance and the size.

class Circulo_en_movimiento(QWidget):
def __init__(self):
	super().__init__()
	self.distancia = 50  # initial distance
	self.setFixedSize(220, 220)

def set_distancia(self, valor):
	self.distancia = valor
	self.update()

Drawing the Circle

To draw the circle, I imported the following library:

from PyQt5.QtGui import QPainter, QBrush

The function paintEvent(self, event) is automatically called when the window needs to be redrawn. This is important because I need a circle that constantly changes size.

Then, I added some parameters to the circle so it adjusts within the window.


	def paintEvent(self, event):
		painter = QPainter(self)
		painter.setBrush(QBrush(Qt.red, Qt.SolidPattern))
		tamaño = max(10, min(200, self.distancia))  # Limits the size between 10 and 200
		tamaño = int(tamaño)  # Make sure the size is an integer
		x = (self.width() - tamaño) // 2
		y = (self.height() - tamaño) // 2
		painter.drawEllipse(x, y, tamaño, tamaño)
	
	ventana.show()  # Show the window on screen
	sys.exit(app.exec_())  # Run the main application loop

Setting Up the Main Window

In this part, I added some properties like layout and style.

Something important is that to avoid repeating code, I used the super() function. This calls the characteristics of the "parent" class — in this case, the parent is QWidget and the child is MiVentana.


class MiVentana(QWidget):
	def __init__(self):
		super().__init__()
		self.setWindowTitle("Distancia con Círculo")
		self.resize(500, 300)

		self.texto = QLabel("Esperando distancia...")
		self.texto.setStyleSheet("font-size: 20px; color: purple;")
		self.circulo = Circulo_en_movimiento()

		layout_horizontal = QHBoxLayout()
		layout_horizontal.addWidget(self.circulo)
		layout_horizontal.addWidget(self.texto)

		layout_principal = QVBoxLayout()
		layout_principal.addLayout(layout_horizontal)
		self.setLayout(layout_principal)

Serial Communication with XIAO

Now I need to communicate with the XIAO — but how?

I have a XIAO RP2040, and I’m going to use serial communication to receive sensor data.

I imported these libraries:

  • PyQt or PySide: These help to display the measured distance.
  • QTimer: This is useful for applications that require continuous updating.

line = self.serial.readline().decode('utf-8').strip()

This line reads data from the serial port (in this case, COM5, baud rate 9600).

To confirm that data is received, I added:

print(f"Recibido: {line}")

If the console receives an invalid value, the program won’t do anything because of this try/except block:

except ValueError:
	pass  # Ignores invalid values

	# Serial communication

	self.serial = serial.Serial('COM5', 9600)  # Set the correct port (shown in Arduino IDE)
	self.timer = QTimer(self)
	self.timer.timeout.connect(self.actualizar_distancia)
	self.timer.start(200)
		
def actualizar_distancia(self):
	if self.serial.in_waiting > 0:
		line = self.serial.readline().decode('utf-8').strip()
		print(f"Recibido: {line}")  # See what is received
		try:
			distancia = float(line)  # Decimal format
			self.texto.setText(f"Distancia: {distancia:.2f} cm")

			# Scale the value for the circle
			tamaño = max(10, min(200, distancia))  # Limits size between 10 and 200
			self.circulo.set_distancia(tamaño)
		except ValueError:
			pass  # Ignore if not a valid value

Doing a test

I have been trying to read the sensor using the analog method, considering that the XIAO has a 12-bit ADC. However, when I tried this approach, I didn’t get accurate distance readings. Then I tested the same code using the ADC from an Arduino, and after applying it to my XIAO, it gave more realistic proximity values without false readings.

First Code considering the ADC of 12-bit

int analogPin = A2;  // Pin analógico conectado a la salida AN del sensor
float voltage;       // Variable para almacenar el voltaje leído
float distance;      // Distancia en pulgadas

void setup() {
  Serial.begin(9600);  // Inicia la comunicación serial
}

void loop() {
  // Leer el valor analógico de 12 bits (0–4095)
  int reading = analogRead(analogPin);

  // Convertir la lectura a voltaje (usando referencia de 3.3V)
  voltage = reading * (3.3 / 4095.0);

  // Convertir voltaje a distancia en pulgadas (según hoja de datos del sensor)
  distance = voltage / (3.3 / 512.0);

  // Convertir a centímetros y mostrar en el monitor serial
  Serial.println(distance * 2.54);

  // Esperar medio segundo
  delay(500);
}




Microcontroller code

int analogPin = A2;  // Analog pin connected to the sensor's AN output
float voltage;       // Variable to store the voltage read from the sensor
float distance;      // Distance in inches

void setup() {
  Serial.begin(9600);  // Start serial communication to print values to the monitor
}

void loop() {
  // Read the analog value from pin A2
  int reading = analogRead(analogPin);

  // Convert the analog reading to actual voltage (assuming 3.3V reference)
  voltage = reading * (3.3 / 1023.0);

  // According to the sensor's datasheet: distance in inches = voltage / (Vcc / 512)
  distance = voltage / (3.3 / 512.0);

  // Print the distance in centimeters (1 inch = 2.54 cm)
  Serial.println(distance * 2.54);

  // Wait for half a second before the next reading
  delay(500);
}


My interface working🛸

Imagen

Files

Conclusion🛸🛸

This week, I liked it because I was able to try my sensor for my final project. With it, I learned how to create an interface, which gives more possibilities to control and receive data from the microcontrollers