Week 14 Interface and Application Programming
This is the group assignment Website Fab Lab Puebla
- Group assignment:
- Compare as many tool options as possible.
- Document your work on the group work page and reflect on your individual page what you learned.
- Individual assignment:
- Write an application that interfaces a user with an input and/or output device(s) on a board that you made.
UI Framework Comparison
1. Tkinter (Python)
Description: Tkinter is Python’s built-in library for creating desktop GUIs.
Advantages:
- Easy to learn and use.
- Lightweight and cross-platform.
- Ideal for rapid prototyping.
Disadvantages:
- Limited aesthetic appeal.
- Not ideal for large or complex applications.
2. PyQt or PySide (Python)
Description: Python wrappers for the Qt framework offering sophisticated GUI design capabilities.
Advantages:
- Extensive widget library.
- Modern and polished UIs.
- Highly scalable.
Disadvantages:
- Steeper learning curve.
- Resource intensive.
3. Qt (C++)
Description: A powerful C++ framework for developing advanced, cross-platform applications.
Advantages:
- High performance and customizable.
- Professional-grade UIs.
- Broad platform support.
Disadvantages:
- Steep learning curve.
- Slower development speed.
Comparison Summary Table
Feature | Tkinter | PyQt/PySide | Qt (C++) |
---|---|---|---|
Ease of Use | Easy | Medium | Hard |
Aesthetic Flexibility | Basic | High | Very High |
Cross-Platform Support | Yes | Yes | Yes |
Target Platforms | Desktop | Desktop | Desktop & Mobile |
Resource Usage | Low | Medium | Low |
Development Speed | Fast | Medium | Slower |
Best For | Simple GUIs | Professional GUIs | High-performance GUIs |
For this week assignment i decided yo use Tkinter, i saw one friend that did his week with this tool of phyton and i really like the simplicity
I watch this video on YouTube to understand some basic things about this software Tkinter i had to take more time that i expected, this assignment was really out of my knowledge area, so i had to dedicate more hours to understand how it works programming and i saw a several videos on YT of this guy. If you are new in programming interfaces on Tkinter take a look.
Learning Tkinter - Step-by-Step Guide
Step 1: Learn Basic Tkinter Elements
I started by watching YouTube tutorials to understand Tkinter’s functionality. Here's what I learned:
- Create a Button: Add a button to your interface.
- Create a Label: Use labels to display text or other information.
- Input and Output: Send data from the interface to the terminal by clicking a button and input data directly into the interface.
These foundational skills will be useful for building more complex applications.
Step 2: Connect the Interface to Hardware (OLED Screen)
After learning the basics, I decided to connect the interface to an OLED screen (SCD-306). My goal was to:
- Allow the user to select an image from their file library through the interface.
- Display the selected image on the OLED screen.
This builds on my previous work where I used the OLED screen to display sensor data.
Step 3: Install Python and Tkinter
Before you begin, ensure you have Python and Tkinter installed on your system.
Installing Python:
- Download Python: Go to the official Python website and download the latest version.
- Install Python: Follow the installation instructions and make sure to check the box for adding Python to your system's PATH.
PIP INSTALL PILLOW
The command pip install Pillow is used to install Pillow, a Python library for working with images. Pillow is an improved and actively maintained version of the old PIL (Python Imaging Library).
I made a few trials following the tutorial and i'll show you below
Here i made a button to printing a message in the terminal
This is the code to run the interface
CODE
import tkinter as tk
app = tk.Tk()
palabra = tk.StringVar(app)
entrada = tk.StringVar(app)
#dimensions height x width
app.geometry("300x540")
#TO CHANGE THE TITLE OF THE WINDOW
tk.Wm.wm_title(app, "hello roy")
tk.Button(
app,
text="click me!",
font=("Courier",14),
bg="#00a8e8",
fg="white",
command=lambda: print("WEEK INTERFACES"+ entrada.get()),
).pack(
fill=tk.BOTH,
expand=True,
)
tk.Entry(
fg="white",
bg="black",
justify="center",
textvariable= entrada
).pack(
fill=tk.BOTH,
expand=True,
)
app.mainloop()
Video
This week, I focused on creating a Tkinter interface that can load an image from my local drive and send the image data to an LCD connected via Arduino IDE. The LCD receives the data from the interface and displays the image.
Interface code
Image Processing and Arduino Communication
1. Image Upload
Function upload_image()
:
- The user selects an image from their system using a dialog box (
filedialog.askopenfilename
). - The uploaded image is resized to 128x64 pixels using
img.resize((128, 64))
. This is because Arduino typically works with small displays like OLED screens. - It is converted to a black-and-white format with
convert("1")
, meaning each pixel will be 0 (black) or 1 (white).
2. Image Processing
The converted image is transformed into a list of pixels with list(img_resized.getdata())
.
This list is reorganized into a 128x64 pixel matrix (pixel_matrix
), representing each row of the image as a list of values.
3. Preview
An enlarged version of the image (img_resized.resize((256, 128))
) is generated to display it in the graphical interface on the preview_panel
.
4. Sending to Arduino
Function send_to_arduino(pixel_matrix)
:
- For each row of the pixel matrix (
pixel_matrix
), a bit string (row_data
) is generated, where each pixel is converted to a1
or a0
depending on its value (black =1
, white =0
). - This bit string is sent to the Arduino via the serial port using
arduino.write
. - A small delay (
time.sleep(0.1)
) is added to avoid overloading the communication channel.
CODE OF Tkinter
import tkinter as tk
from tkinter import filedialog, ttk
from PIL import Image, ImageTk
import serial
import time
# Configurar conexión serial con Arduino
arduino = serial.Serial(port='COM3', baudrate=9600, timeout=1) # Cambia 'COM3' por tu puerto
time.sleep(2) # Espera a que Arduino se inicialice
def upload_image():
filepath = filedialog.askopenfilename(filetypes=[("Image Files", "*.png;*.jpg;*.jpeg;*.bmp")])
if filepath:
img_label.config(text=f"File uploaded: {filepath.split('/')[-1]}") # Mostrar nombre del archivo
img = Image.open(filepath)
img_resized = img.resize((128, 64)).convert("1") # Redimensionar y convertir a blanco y negro
pixel_data = list(img_resized.getdata())
pixel_matrix = [pixel_data[i:i+128] for i in range(0, len(pixel_data), 128)]
# Mostrar la imagen en el panel de previsualización
preview_img = ImageTk.PhotoImage(img_resized.resize((256, 128))) # Escalar para mejor visualización
preview_panel.config(image=preview_img)
preview_panel.image = preview_img
send_to_arduino(pixel_matrix)
def send_to_arduino(pixel_matrix):
for row in pixel_matrix:
row_data = ''.join(['1' if pixel == 0 else '0' for pixel in row])
arduino.write((row_data + '\n').encode())
time.sleep(0.1) # Pequeña pausa para evitar sobrecarga
status_label.config(text="Image sent ", foreground="green")
# Crear la interfaz gráfica
app = tk.Tk()
app.title("Week 14: Interface and Application Programming")
app.geometry("500x350")
app.resizable(False, False)
# Título
title_label = tk.Label(app, text="Fab Academy Rodrigo", font=("Arial", 12, "bold"))
title_label.pack(pady=10)
# Marco para contener los elementos
frame = ttk.Frame(app, padding=10)
frame.pack(fill=tk.BOTH, expand=True)
# Botón para cargar la imagen
boton = ttk.Button(frame, text="Upload Image", command=upload_image)
boton.pack(pady=5)
# Label para mostrar el archivo cargado
img_label = tk.Label(frame, text="nothing uploaded yet", font=("Arial", 10), foreground="blue")
img_label.pack(pady=5)
# Panel de previsualización de la imagen
preview_panel = tk.Label(frame, text="preview", bg="black", width=32, height=16)
preview_panel.pack(pady=10)
# Label para el estado de la operación
status_label = tk.Label(frame, text="", font=("Arial", 10))
status_label.pack(pady=5)
app.mainloop()
CODE of ARDUINO which receive data and show it in LCD
The Python interface sends image data row by row via serial communication. Each row is 128 characters long, representing the pixels of the OLED screen:
- '1': White pixel
- '0': Black pixel
The Arduino reads each row and updates the corresponding pixels on the OLED screen.
2. Key Arduino Functions
Serial.begin(9600)
: Initializes serial communication at 9600 baud.Serial.readStringUntil('\n')
: Reads a row of data until a newline character is received.display.drawPixel(x, y, color)
: Draws a pixel at position (x, y) with the specified color.display.display()
: Updates the OLED to show the drawn pixels.
3. Data Processing Loop
The Arduino processes data as follows:
- Wait for data from the serial connection.
- Read a row (128 characters).
- Draw each character as a pixel on the OLED.
- Repeat for all rows (64 rows for a 128x64 screen).
- Once all rows are processed, update the display to show the complete image.
CODE for Arduino
#include Wire.h
#include Adafruit_GFX.h
#include Adafruit_SSD1306.h
// Definición de ancho y alto de la pantalla
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1 // Reset pin # (o -1 si no se usa)
#define SCREEN_ADDRESS 0x3C // Dirección I2C estándar
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
Serial.begin(9600); // Inicializa comunicación serial con PC
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("Error: No se pudo inicializar la pantalla OLED"));
for (;;); // Detener si falla la inicialización de la pantalla
}
// Mostrar el mensaje inicial
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 10); // Posición vertical ajustada
display.println(F("Hola chiva hermano,"));
display.setCursor(0, 20); // Siguiente línea
display.println(F("sube tu imagen:"));
display.display(); // Mostrar el mensaje
Serial.println("Esperando imagen...");
}
void loop() {
static int y = 0; // Línea actual en la pantalla OLED
if (Serial.available() > 0) {
String row = Serial.readStringUntil('\n'); // Leer una fila completa hasta un salto de línea
if (row.length() == SCREEN_WIDTH) { // Verificar que la fila recibida tenga exactamente 128 caracteres
for (int x = 0; x < SCREEN_WIDTH; x++) {
if (row[x] == '1') {
display.drawPixel(x, y, SSD1306_WHITE); // Dibujar píxel blanco
} else {
display.drawPixel(x, y, SSD1306_BLACK); // Dibujar píxel negro
}
}
y++; // Avanzar a la siguiente fila
} else {
Serial.println("Error: Fila con longitud incorrecta.");
}
if (y >= SCREEN_HEIGHT) { // Si se recibieron todas las filas
display.display(); // Mostrar la imagen completa en la pantalla
Serial.println("Imagen completa mostrada.");
y = 0; // Reiniciar para una nueva imagen
display.clearDisplay(); // Limpiar la pantalla para la siguiente imagen
}
}
}
Video of the FINAL results
Video 2