Interface and 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 for the embedded board that you made. that interfaces a user with an input and/or output device(s)
PuffIN & Out Monitor
This week, I created a Processing application for my final project. It includes a counter, visual elements, and logs the last time a puffin entered or exited its burrow. It was a fun and productive week! The Microcontroller talkes to the Application Via Serial
Processing
I used Processing to build the application. First, I installed it using this Flatpak. After launching it, I encountered an issue where everything appeared very small due to custom scaling settings. I resolved this by setting the scaling to 200% and adjusting the editor and terminal font size to 24.
Arduino Code
Before starting with Processing, I updated my Arduino code to send serial data to the application. Below is the complete code for the microcontroller:
#include <Wire.h>
#include "rgb_lcd.h"
rgb_lcd lcd;
int analog_pin = A1; // A1 on XIAO ESP32-C3 = GPIO 1
int tx_pin = D2; // D2 on XIAO ESP32-C3 = GPIO 2
long threshold = 20000;
bool puffinIn = false;
int inCount = 0;
int outCount = 0;
bool pressState = 0;
void setup() {
pinMode(tx_pin, OUTPUT);
Serial.begin(9600);
lcd.begin(16, 2);
lcd.clear();
// Loading animation
lcd.setCursor(0, 0);
lcd.print("PuffIN & OUT!");
const char* dots[] = { "Loading. ", "Loading.. ", "Loading..." };
for (int i = 0; i < 5; i++) {
lcd.setCursor(0, 1);
lcd.print(dots[i % 3]);
delay(1000);
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("PuffIN & OUT!");
updateLCD();
}
void loop() {
long result = tx_rx();
result = constrain(result, 0, 60000);
// Uncomment to debug raw values
// Serial.print("Step: ");
// Serial.println(result);
if (!puffinIn && result < threshold) {
puffinIn = true;
pressState = 1 - pressState;
if (pressState == 1) {
inCount++;
} else {
outCount++;
}
updateLCD();
sendState();
delay(300); // debounce delay
}
if (puffinIn && result > threshold + 1000) {
puffinIn = false;
}
delay(100);
}
void updateLCD() {
lcd.setCursor(0, 1);
lcd.print(" "); // clear row
char buffer[17];
snprintf(buffer, sizeof(buffer), "IN: %-3d OUT: %-3d", inCount, outCount);
lcd.setCursor(0, 1);
lcd.print(buffer);
}
void sendState() {
Serial.print("IN:");
Serial.print(inCount);
Serial.print("|OUT:");
Serial.println(outCount);
}
long tx_rx() {
int read_high, read_low, diff;
long sum = 0;
const int N_samples = 100;
for (int i = 0; i < N_samples; i++) {
digitalWrite(tx_pin, HIGH);
read_high = analogRead(analog_pin);
delayMicroseconds(100);
digitalWrite(tx_pin, LOW);
read_low = analogRead(analog_pin);
diff = read_high - read_low;
sum += diff;
}
return sum;
}
Processing Application
With some help from ChatGPT, I completed the Processing application in a day. Below are the key challenges I faced and how they were resolved:
Key Challenges and Solutions
-
Serial Output from Arduino:
AddedSerial.print()
to logIN: X | OUT: Y
. -
Visualizing Puffin States:
- Read serial input.
- Display a red square for "IN" and a blue square for "OUT."
- Show IN/OUT counts at the bottom.
-
Layout Adjustments:
Moved the red square (IN) to the left and the blue square (OUT) to the right. -
Custom Puffin Images:
UsedPImage
to display:"puffin_in_burrow.png"
for IN."flying_puffin.png"
for OUT.
-
Persistent Burrow Display:
Added a burrow image or a dark ellipse if the image is unavailable. -
Puffin Artwork:
Created hand-drawn images for a flying puffin and a puffin in a burrow. -
Image Stretching:
Fixed by scaling images proportionally. -
Mirror Flying Puffin:
Usedscale(-1, 1)
to flip the image horizontally. -
Fade Transitions:
Added smooth fade effects usingtint()
. -
Last IN/OUT Time:
Displayed timestamps inMMM dd at HH:mm
format. -
Step Response Sensor:
Replaced button input with capacitive sensing. -
GPIO Mapping:
Remapped to valid GPIO pins on ESP32-C3. -
Serial Baud Rate:
Unified to 9600 for compatibility. -
LCD Updates:
Reintegrated LCD logic with step response detection. -
Merged Logic:
Combined step detection, count logic, and LCD updates.
import processing.serial.*;
import java.text.SimpleDateFormat;
import java.util.Date;
Serial myPort;
int inCount = 0;
int outCount = 0;
boolean isIn = true;
boolean prevIsIn = true;
float fadeAlpha = 255;
float fadeSpeed = 10;
String lastEntered = "Never";
String lastExited = "Never";
PImage flyingPuffin;
PImage puffinInBurrow;
void setup() {
size(1200, 800);
background(255);
flyingPuffin = loadImage("flying_puffin.png");
puffinInBurrow = loadImage("puffin_in_burrow.png");
println("Available serial ports:");
println(Serial.list());
myPort = new Serial(this, Serial.list()[0], 9600);
myPort.bufferUntil('\n');
}
void draw() {
background(255);
imageMode(CENTER);
// Detect state change for fade + timestamp
if (isIn != prevIsIn) {
fadeAlpha = 0;
updateTimestamp(isIn);
prevIsIn = isIn;
}
if (fadeAlpha < 255) {
fadeAlpha += fadeSpeed;
fadeAlpha = min(fadeAlpha, 255);
}
// Select image and scale
PImage currentImage = isIn ? puffinInBurrow : flyingPuffin;
float targetHeight = 300;
float aspect = currentImage.width / (float) currentImage.height;
float targetWidth = targetHeight * aspect;
float x = isIn ? width / 3 : 2 * width / 3;
float y = height / 2 - 40;
tint(255, fadeAlpha);
if (isIn) {
image(currentImage, x, y, targetWidth, targetHeight);
} else {
pushMatrix();
translate(x, y);
scale(-1, 1);
image(currentImage, 0, 0, targetWidth, targetHeight);
popMatrix();
}
noTint();
// === Draw Text ===
fill(0);
textAlign(CENTER);
// IN / OUT Count
textSize(36);
text("IN: " + inCount + " OUT: " + outCount, width / 2, height - 60);
// Timestamps below that
textSize(24);
text("Last IN: " + lastEntered, width / 2, height - 30);
text("Last OUT: " + lastExited, width / 2, height - 10);
}
void updateTimestamp(boolean nowIn) {
String timestamp = getTimeString();
if (nowIn) {
lastEntered = timestamp;
} else {
lastExited = timestamp;
}
}
String getTimeString() {
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("MMM dd 'at' HH:mm");
return sdf.format(now);
}
void serialEvent(Serial port) {
String input = trim(port.readStringUntil('\n'));
if (input != null && input.startsWith("IN:")) {
String[] parts = splitTokens(input, ":|");
if (parts.length >= 4) {
try {
inCount = int(trim(parts[1]));
outCount = int(trim(parts[3]));
isIn = (inCount + outCount) % 2 == 1;
} catch (Exception e) {
println("Error parsing input: " + input);
}
}
}
}
ChatGPT Chat Links
Puffin Images
To use the images, go to Sketch > Add File in Processing and select the images below. They were originally PNGs with transparent backgrounds but resized for compatibility.
Final Result
The application is functional, with some room for visual polish. It tracks puffin activity and provides a fun, interactive display!