Skip to main content

Interface & Application Programming

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 input and/or output device(s) on a board that you made.

Tkinter and pyserial

As I restarted my final project from scratch, I won't have too much time this week to try a lot of APIs. I don't need them for my final project. Even, as a former web developer, it's a topic that interests me a lot and can be really fun.

I already discover Processing in the Input devices' week, so I thought trying something else will be cool. With the help of Marcello and Ahmed, I discovered the Python Library Tkinter.

I'm not a real Python developer, but I know a bit. I used Jupyter to compile the Python code.

Windows with label

So first let's create an instance of Tkinter and add a Label.

Here is the code:

from tkinter import *

root = Tk()

# Creating a Label Widget
myLabel = Label(root, text="Hello World!")

# Showing it onto the screen
myLabel.pack()

root.mainloop()

Then we can play with the grid function and add a Title to the window.

from tkinter import *

root = Tk()
root.title("fab Lab Kamp-lintfort")

# creating a label widget

myLabel1 = Label(root, text="Hello Word1")
myLabel2 = Label(root, text="Hello Word2")

myLabel1.grid(row=0, column=0)
myLabel2.grid(row=1, column=0)

root.mainloop()

Buttons and interaction

Now we can create a Button, and add a function to it. It just shows a label once it has been clicked.

from tkinter import *

root = Tk()
root.title("fab Lab Kamp-lintfort")

def myClick():
myLabel= Label(root, text="look I clicked")
myLabel.pack()

# creating a label widget

myButton = Button(root, text="Click me", command=myClick, padx=50, pady=50)

myButton.pack()

Another interaction can be with input fields.

from tkinter import *

root = Tk()
root.title("fab Lab Kamp-lintfort")

def myClick():
myLabel= Label(root, text=e.get()) # Get the value of the input and show it
myLabel.pack()

# creating a label widget

myButton = Button(root, text="Click me", command=myClick, padx=50, pady=50)

# Create an input field

e = Entry(root)
e.pack()

myButton.pack()
root.mainloop()

Canvas

The tkinter library is useful to draw some "structures" in the window with the help of the Canvas class. We can then create rectangles, lines, etc.

from tkinter import *

window = 200 #window size

root = Tk()
myCanvas = Canvas(root, width=2*window, height=window, background='white')
myCanvas.create_rectangle(window, 0, 2*window, window, tags='rest', fill='#b00000')
myCanvas.pack()

root.mainloop()

So now We can make the canvas interactive. When I click on the button, it changes the color of the rectangle.

from tkinter import *

window = 200 #window size

root = Tk()
color_state = 0

# Function that changes the color of the rectangle between red and blue

def myClick():
global color_state
if(color_state == 0):
myCanvas.itemconfig('rect', fill="#b00000")
color_state = 1
else:
myCanvas.itemconfig('rect', fill="#0000b0")
color_state = 0
myCanvas.pack

myCanvas = Canvas(root, width=window, height=window, background='white')
myCanvas.create_rectangle(0, 0, window, window, tags='rect', fill='#b00000')
myCanvas.pack()

# creating a Button that call *myClick* function

myButton = Button(root, text="Click me", command=myClick, padx=50, pady=50)

myButton.pack()
root.mainloop()

Read and write on an MCU

It's nice to be able to create a quick and nice interface, but we are at the Fab Academy, so we need to talk to an MCU. I have an XIAO SAMD21 with a built-in LED and a touch sensor. So let's talk to it.

First, I need to initiate a Serial communication. For that, there is the pyserial library. I want to write but also read the serial communication with a loop. But I did some first tries, and the loop had some conflicts with the interface loop of tkinter. So I decided to use a separate thread for the serial.

I create a SerialThread class, that I'll instantiate later. So first I need to create the constructor that will connect to my SAMD21. I ask to ChatGPT for some help here.

import threading
import serial

# Serial port settings
PORT = 'COM10' # The port of my SAMD21
BAUDRATE = 9600

# A Class for serial communication
class SerialThread(threading.Thread):
"""Thread to read data from the serial port"""

def __init__(self):
super().__init__()
self.running = True
self.ser = serial.Serial(PORT, BAUDRATE, timeout=1)
self.ser.bytesize = 8 # Number of data bits = 8
self.ser.parity ='N' # No parity
self.ser.stopbits = 1 # Number of Stop bits = 1ser.setDTR()
self.ser.flush

I then added a run function to read and change the color of the canvas (see later), a stop function to close the serial and a sednState function to send the state to the SAMD21.

    def run(self):
global color_state
while self.running:
# Read data from the serial port
state = self.ser.readline().decode().strip()
if ((state == '0' or state == '1') and color_state != int(state)):
color_state = int(state)
changeColor()
print(color_state)

def stop(self):
self.running = False
self.ser.close()

def sendState(self):
global color_state
self.ser.write(str(color_state).encode())

I then create a simple canvas with a rectangle that will change color and a button.

from tkinter import *
import time

# Create a tkinter window and some buttons to control the variable state
root = Tk()
color_state = 0
window = 200 #window size

myCanvas = Canvas(root, width=window, height=window, background='white')
myCanvas.create_rectangle(0, 0, window, window, tags='rect', fill='#b00000')
myCanvas.pack()

myButton = Button(root, text="Click me", command=myClick, padx=50, pady=50)

myButton.pack()

I then create 3 functions to sendState, changeColor and myClick (to handle a click).

# Define a function to send the variable state to the Arduino
def sendState():
serial_com.sendState()

# Define a function to change the color of the canvas
def changeColor():
global color_state
if(color_state == 0):
myCanvas.itemconfig('rect', fill="#b00000")
else:
myCanvas.itemconfig('rect', fill="#0000b0")
myCanvas.pack()

# A function to handle a click
def myClick():
global color_state
if (color_state == 0):
color_state = 1
else:
color_state = 0
changeColor()
sendState()
print(color_state)
time.sleep(0.1)

So I'm almost done. I only need to initialize a serial communication, handle the closing of the windows and launch tkinter.

# Create the serial reader thread and start it
serial_com = SerialThread()
serial_com.start()

def on_closing():
# Stop the serial reader thread and close the serial port
serial_com.stop()
root.destroy()


# Bind the close event to stop the serial reader thread and close the serial port
root.protocol('WM_DELETE_WINDOW', on_closing)

# Start GUI main loop
root.mainloop()

Handle the serial on Arduino IDE

Ok, I have a Python program to write and read on the serial port of the SAMD21. But I need to push some code on the board to handle this communication and push some information too in the serial port.

So here I work with colorState variable of 0 or 1. The idea is to be able to both change the color in the canvas and also to light the built-in LED on the board. Either by clicking on the button in the interface or by touching the touch sensor.

So first I need to include the Bounce library to handle the noise that a touch sensor can have, so I can properly read it. Then I instantiate the colorState, set the pins for the touch button and the built-in LED and start the Serial communication.

#include <Bounce2.h>

const int button_pin = 0;
int colorState = 0; //red serial button
Bounce button_debouncer = Bounce();

void setup()
{
Serial.begin(9600);
pinMode(button_pin, INPUT);
pinMode(LED_BUILTIN, OUTPUT);
button_debouncer.attach(button_pin);
button_debouncer.interval(5);
}

Then I create a simple function to toggle the built-in LED state.

void toggleLED() {
switch(colorState)
{
case 1:
digitalWrite(LED_BUILTIN,HIGH);
colorState = 0;
break;
case 0: //your code
digitalWrite(LED_BUILTIN,LOW);
colorState = 1;
break;
default:
digitalWrite(LED_BUILTIN,LOW);
break;
}//end of switch()
}

Finally, I can set the loop function to read the serial when the button is clicked and write on the serial when the touch sensor when it's touched.

void loop()
{

if (Serial.available())
{
str state = Serial.read();
colorState = = state.toInt();
toggleLED();
}

button_debouncer.update();
if (button_debouncer.rose() == true)
{
toggleLED();
Serial.println(colorState);
}
}

Here is the result, we can see both the built-in LED turning on and off and the rectangle changing color by clicking or touching the sensor.

I also retry to do the exact same exercise on my board made during Week 8, it's an based on an XIAO ESP32-C3. The only thing that change is that ESP32 doesn't have a built-in LED. But I added an addressable LED on the board, so I just had to change LED_BUILTIN fo D3 in the Arduino code and chose the correct port in the Python programme. And here is a video of the result:

Files

Here are the files of the week: