The goal of this group assignment is to research and compare various development tools suitable for “human-device interaction.” These tools help visualize sensor data read by microcontrollers or send control commands to devices through a user interface. We aim to select an interface development tool suitable for individual assignments, based on factors such as usability, scalability, platform support, learning curve, and functionality.
We gathered a list of commonly used interface development tools from the internet and community resources, then compiled the following tools for testing:
No. | Tool Name | Type | Required Language/Platform |
---|---|---|---|
1 | Processing | Graphics Programming | Java-like, cross-platform |
2 | p5.js | Web Graphics Library | JavaScript, browser |
3 | Python + Tkinter + PySerial | Scripting + GUI | Python, local desktop |
4 | Arduino Serial Plotter | Serial Visualization | Arduino IDE |
Component | Wiring Description |
---|---|
Button | One end connected to XIAO’s D2 (GPIO2), the other end to GND |
Microcontroller | Connected to the computer via Type-C USB cable |
Power Supply | Powered through USB connection |
In the Arduino IDE, select the board as XIAO_ESP32S3, and upload the following code:
#define XIAO_ESP32S3_D2
const int buttonPin = 3;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
Serial.begin(9600);
}
void loop() {
int state = digitalRead(buttonPin);
if (state == LOW) {
Serial.println("ON");
} else {
Serial.println("OFF");
}
delay(200);
}
⚠️ Note: Make sure the serial port is not occupied by the Arduino Serial Monitor, otherwise Processing will fail to connect.
Open Processing and enter the following code (make sure to check and select the correct serial port — it's often not the first one in the list):
import processing.serial.*;
Serial myPort;
String receivedData = "";
void setup() {
size(400, 200);
println(Serial.list()); // Print all serial ports; manually select the one for XIAO ESP32S3
myPort = new Serial(this, Serial.list()[0], 9600); // Change to the correct port
myPort.bufferUntil('\n');
}
void draw() {
background(255);
fill(0);
textSize(32);
textAlign(CENTER, CENTER);
text("Button: " + receivedData, width / 2, height / 2);
}
void serialEvent(Serial p) {
receivedData = trim(p.readStringUntil('\n'));
}
p5.js is an open-source JavaScript graphics programming framework evolved from Processing. It is designed for web-based interactive development, targeting artists, designers, and makers who want to easily create interactive programs. It inherits Processing’s simple and intuitive syntax and runs entirely in the browser. Its main features include:
Button Module Pin | Connects to XIAO ESP32S3 |
---|---|
VCC | 3.3V |
GND | GND |
SIG (Signal) | GPIO3 (i.e., D2) |
Note: The button module has a default state of HIGH. When pressed, it goes LOW.
The following code reads the state of the button and sends the result to p5.js via the serial port:
#define XIAO_ESP32S3_D2
#define BUTTON_PIN 3
void setup() {
Serial.begin(115200);
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void loop() {
int buttonState = digitalRead(BUTTON_PIN);
if (buttonState == LOW) {
Serial.println("PRESSED");
} else {
Serial.println("RELEASED");
}
delay(50);
}
The following is the complete p5.js code that receives serial data and controls the size of a circle based on button state:
let serial;
let latestData = "waiting for data";
let circleSize = 50;
function setup() {
createCanvas(400, 400);
serial = new p5.SerialPort();
serial.list();
serial.open('/dev/tty.usbmodemXXXX'); // Replace with your actual serial port
serial.on('connected', serverConnected);
serial.on('list', gotList);
serial.on('data', gotData);
serial.on('error', gotError);
serial.on('open', gotOpen);
serial.on('close', gotClose);
}
function draw() {
background(220);
if (latestData.trim() === "0") {
circleSize = min(circleSize + 5, 200);
} else {
circleSize = max(circleSize - 5, 50);
}
ellipse(width / 2, height / 2, circleSize, circleSize);
}
function serverConnected() {
print("Connected to Server");
}
function gotList(thelist) {
print("List of Serial Ports:");
for (let i = 0; i < thelist.length; i++) {
print(i + " " + thelist[i]);
}
}
function gotOpen() {
print("Serial Port is Open");
}
function gotClose() {
print("Serial Port is Closed");
}
function gotError(theerror) {
print(theerror);
}
function gotData() {
let currentString = serial.readLine();
trim(currentString);
if (!currentString) return;
latestData = currentString;
}
⚠️ Note: Replace '/dev/tty.usbmodemXXXX'
with your actual serial port name. You can find it from the printed serial port list in the console.
Problem | Cause | Solution |
---|---|---|
Serial connection fails | Arduino Serial Monitor is occupying the port | Close the Arduino Serial Monitor |
Browser unresponsive | Browser does not support Web Serial API | Use the latest version of Chrome or Edge |
Data not updating on page | Arduino data format not parsed correctly | Send data line-by-line to avoid confusion |
In this experiment, I explored the use of p5.js — a web-based framework derived from Processing — to build an interactive system where a physical button controls a web animation. By using the Web Serial API, I successfully achieved real-time data interaction between a browser and a physical sensor device for the first time.
During the process, I became familiar with:
navigator.serial
mechanism for serial communication in browsers;Compared to Processing, p5.js is better suited for directly showcasing interactive projects on the web. It is more aligned with front-end web development but also requires a deeper understanding of JavaScript.
In this experiment, I explored how to use Python together with the Tkinter graphical user interface (GUI) library and the PySerial serial communication library to create a desktop-based interactive application. This method does not rely on the browser, making it more suitable for developing locally running programs with high compatibility and flexibility.
I installed Python 3.12 on a Windows system. The official download page is: https://www.python.org/downloads/
⚠️ Note: During installation, be sure to check “Add Python to PATH”; otherwise, Python will not be recognized in the command prompt.
Open the Command Prompt (CMD), and enter the following command to install PySerial:
pip install pyserial
Tkinter is included as part of Python's standard library. It is automatically installed with Python, so no additional installation is required.
My goal was to achieve the same visual effect as the previous p5.js example: to read the serial data sent from the Arduino (indicating whether the button is pressed or released), and reflect that state by animating the size of a circle in a graphical interface.
This updated Arduino code reads the state of a button connected to GPIO3 (D2) on the XIAO ESP32S3 and sends "1"
when the button is pressed, and "0"
when released.
This is used by both the p5.js and Python receiver programs to control the animation logic.
#define XIAO_ESP32S3_D2
#define BUTTON_PIN 3
void setup() {
Serial.begin(115200);
pinMode(3, INPUT_PULLUP); // Button connected to GPIO3 (D2)
}
void loop() {
if (digitalRead(3) == LOW) {
Serial.println("1");
} else {
Serial.println("0");
}
delay(100);
}
This version of the Python program receives serial data from the Arduino,
and uses Tkinter to display a circle whose size changes based on the button state.
It also shows a label below the circle to indicate the status ("PRESSED" or "RELEASED").
The serial data is "1"
when the button is pressed, and "0"
when released.
.py
extension, for example: button_gui.py
python button_gui.py
import serial
import tkinter as tk
import threading
# Serial port configuration
ser = serial.Serial("COM3", 115200)
# Initial value
circle_radius = 50
# GUI setup
window = tk.Tk()
window.title("Button-Controlled Circle Animation")
canvas = tk.Canvas(window, width=400, height=400, bg="white")
canvas.pack()
# Create visual elements
circle = canvas.create_oval(175, 175, 225, 225, fill="skyblue")
text = canvas.create_text(200, 300, text="Button State: UNKNOWN", font=("Arial", 16))
# Update circle and label
def update_circle(radius, status_text):
canvas.coords(circle, 200 - radius, 200 - radius, 200 + radius, 200 + radius)
canvas.itemconfig(text, text="Button State: " + status_text)
# Serial reader thread
def read_serial():
global circle_radius
while True:
try:
data = ser.readline().decode().strip()
if data == "1":
update_circle(80, "PRESSED")
elif data == "0":
update_circle(40, "RELEASED")
except Exception as e:
print("Read error:", e)
continue
# Start thread
thread = threading.Thread(target=read_serial)
thread.daemon = True
thread.start()
# Run GUI
window.mainloop()
# (Insert your Python code here)
circle.py
python circle.py
# (Insert your Python code here)
circle.py
python circle.py
cd "D:\Users\YourUsername\Desktop"
- Then run the Python file:
python circle.py
If everything is configured correctly, a white window will appear with a blue circle in the center. The circle’s size will change dynamically based on the button state.
Button State | Serial Output | Circle Radius |
---|---|---|
Pressed | 0 | 80 px |
Released | 1 | 40 px |
Visually, you will see the circle grow and shrink with smooth animation feedback.
Issue Description | Possible Cause | Solution |
---|---|---|
Cannot run .py file in PowerShell |
Wrong path, file doesn't exist, or Python not set up | Use cd to navigate to the .py file’s directory, then run python circle.py |
Error: ModuleNotFoundError: No module named 'serial' |
PySerial is not installed | Run pip install pyserial in PowerShell |
Circle does not change | Arduino serial is still open, or Python didn't open the port | After uploading Arduino code, close the Serial Monitor to release the COM port |
Python window crashes or turns white/unresponsive | Code error or parsing issue | Check if port and baud rate match; ensure Arduino is sending correct data |
Upload failed: port busy | Port is in use by another program (e.g., Serial Monitor) | Close all other serial tools before uploading |
Cannot open COM3 | Incorrect port, USB driver issue, or board not connected | Confirm Arduino is connected to COM3; try rebooting or using another cable |
This experiment marks my first time combining Python GUI programming with Arduino hardware communication, creating an interactive system where a physical button controls the size of a graphical circle.
Throughout the process, I:
tkinter
in Python;PySerial
+ threading
to receive data and refresh the display in real-time;This hands-on practice helped me understand:
Compared to Processing or p5.js:
Python offers more freedom and control in GUI logic;
Although it lacks the visual appeal of web-based animation,
it is a very solid way to learn serial communication and desktop GUI programming.
Arduino Serial Plotter is a built-in visualization tool in the Arduino IDE. It allows users to plot real-time data directly from the Serial output of their Arduino boards. It's especially useful for debugging sensor values, signal trends, or visualizing simple numeric output.
Serial.println()
Serial.println()
(e.g., sensor values or button states).Example Code (Button state visualization):
#define XIAO_ESP32S3_D2
#define BUTTON_PIN 3
void setup() {
Serial.begin(115200);
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void loop() {
int state = digitalRead(BUTTON_PIN);
Serial.println(state);
delay(100);
}
Button State | Serial Output | Plot Behavior |
---|---|---|
Pressed | 0 | Line drops to bottom |
Released | 1 | Line rises to top |
Advantages:
Limitations:
In this experiment, I aimed to create a serial interactive animation using Processing.
The goal is to use a potentiometer (knob sensor) connected to the XIAO ESP32S3 board as an input device.
The analog value will be sent to the computer via serial communication, and Processing will visualize this data
with a "cool-looking" real-time dynamic animation to enhance the interactive experience.
Tool | Purpose |
---|---|
XIAO ESP32S3 | Microcontroller for reading analog input |
Potentiometer (knob module) | Input device that adjusts analog voltage |
Arduino IDE | For writing Arduino code to read and send sensor data |
Processing 4 | To receive serial data and display interactive animation |
USB Type-C Cable | Connects the board to the computer via serial port |
Component Pin | Connected To |
---|---|
Potentiometer VCC | XIAO 3.3V |
Potentiometer GND | XIAO GND |
Potentiometer OUT | XIAO A0 (GPIO1) |
Note: The XIAO ESP32S3 supports multiple analog input pins. In this project, GPIO1 (A0) was selected as the analog input.
Upload the following code to the XIAO ESP32S3. The program will:
#define XIAO_ESP32S3_A0
#define POTENTIOMETER_PIN 1
void setup() {
Serial.begin(115200);
}
void loop() {
int sensorValue = analogRead(POTENTIOMETER_PIN);
Serial.println(sensorValue);
delay(50);
}
Once the code is uploaded successfully, you can open the Arduino Serial Monitor to verify whether the analog output changes as expected.
⚠️ Important: When using Processing, make sure to close the Arduino Serial Monitor first. Otherwise, the serial port will be occupied and Processing won’t be able to connect.
To make the animation more “cool” and visually appealing, I designed a multi-ring pulsing effect. The potentiometer value controls both the background color and the dynamic ripple size.
The following is the Processing program:
import processing.serial.*;
Serial myPort;
int sensorValue = 0;
void setup() {
size(600, 600);
println(Serial.list()); // List available ports
myPort = new Serial(this, "COM3", 115200); // Replace with your actual port
myPort.bufferUntil('\n');
}
void draw() {
background(map(sensorValue, 0, 4095, 0, 255), 100, 200);
translate(width / 2, height / 2);
noFill();
stroke(255);
int rings = 10;
for (int i = 0; i < rings; i++) {
float radius = i * 20 + map(sensorValue, 0, 4095, 0, 100);
ellipse(0, 0, radius, radius);
}
}
void serialEvent(Serial myPort) {
String inString = myPort.readStringUntil('\n');
if (inString != null) {
inString = trim(inString);
sensorValue = int(inString);
}
}
⚠️ Reminder: Replace "COM3"
with your actual serial port name from the Serial.list()
output.
"COM3"
), the animation window will open;Problem | Possible Cause | Diagnosis and Solution |
---|---|---|
Processing cannot open serial port; error: "port busy" | Arduino Serial Monitor is still open, COM3 is occupied |
I ran Processing right after uploading the Arduino code, and got a port access error.
It was due to the Serial Monitor being open. After closing it, the issue was resolved. Lesson: Only one program can use the port — close the other first. |
Processing window opens but no animation appears | 1. Wrong port number 2. Serial data not read properly |
Used println(Serial.list()) to confirm port, verified baud rate was 115200.
Found that sensorValue stayed 0 because myPort.bufferUntil('\n') was missing.
After adding it, the issue was fixed.
|
UI is laggy or unresponsive | Serial data sent too frequently; or data too large | Delay was set to 10ms in Arduino, sending over 100 messages/sec. Increased delay to 20–50ms and achieved stable 60 fps in Processing. |
Animation is not visually responsive enough | Potentiometer value range not visually intuitive |
Potentiometer outputs 0–4095. Used map() to scale values for color and size,
which significantly improved visual feedback.
|
Other Insights:
serialEvent()
in Processing is asynchronous. bufferUntil('\n')
is required to trigger it.This experiment — combining Processing, Arduino, and a potentiometer — was a complete journey from "sensor input" to "artistic output."
Used a potentiometer to collect analog input, transmitted via serial.
Learned analogRead()
and Serial.println()
on the ESP32.
Chose Processing for visualization. Its graphics capability is superior to Tkinter and p5.js. Mapped knob values to color and size, creating a center-expanding animation that felt intuitive and smooth.
\n
for clean parsingellipse()
, translate()
, map()