Gesture Controller: Arduino-Based Hand Movement Car Control
Gesture Controller: Arduino-Based Hand Movement Car Control
In this post, I’ll share insights from my Gesture Controller project, which demonstrates innovative embedded systems programming through an Arduino-based gesture control system that enables car control using hand movements.
Project Overview
The Gesture Controller project represents a creative approach to human-machine interaction, using Arduino microcontrollers and various sensors to detect hand gestures and translate them into car control commands. This project showcases embedded systems programming, sensor integration, and real-time signal processing.
Technical Architecture
Hardware Components
- Arduino Uno: Main microcontroller for processing
- Accelerometer/Gyroscope: MPU6050 for motion detection
- Ultrasonic Sensors: HC-SR04 for distance measurement
- Servo Motors: For steering control
- DC Motors: For forward/backward movement
- Motor Driver: L298N for motor control
- Bluetooth Module: HC-05 for wireless communication
- LED Indicators: Visual feedback system
System Design
Gesture Controller System:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Hand Gesture │───▶│ Sensor Array │───▶│ Arduino Uno │
│ Detection │ │ (MPU6050) │ │ Processing │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
┌─────────────────┐ │
│ Motor Control │◀────────────┘
│ (L298N) │
└─────────────────┘
│
┌─────────────────┐
│ Car Movement │
│ (Motors) │
└─────────────────┘Core Implementation
Sensor Integration
// GestureController.ino
#include <Wire.h>
#include <MPU6050.h>
#include <Servo.h>
#include <SoftwareSerial.h>
// Pin definitions
#define SERVO_PIN 9
#define MOTOR_ENA 5
#define MOTOR_ENB 6
#define MOTOR_IN1 7
#define MOTOR_IN2 8
#define MOTOR_IN3 10
#define MOTOR_IN4 11
#define LED_PIN 13
#define BUZZER_PIN 12
// Sensor objects
MPU6050 mpu;
Servo steeringServo;
SoftwareSerial bluetooth(2, 3); // RX, TX
// Global variables
int steeringAngle = 90; // Center position
int motorSpeed = 0;
bool isMoving = false;
unsigned long lastGestureTime = 0;
const unsigned long GESTURE_TIMEOUT = 1000; // 1 second timeout
// Gesture detection thresholds
const float TILT_THRESHOLD = 15.0; // degrees
const float SHAKE_THRESHOLD = 2.0; // g-force
const int GESTURE_HOLD_TIME = 500; // milliseconds
void setup() {
Serial.begin(9600);
bluetooth.begin(9600);
// Initialize MPU6050
Wire.begin();
mpu.initialize();
if (!mpu.testConnection()) {
Serial.println("MPU6050 connection failed!");
while(1);
}
// Initialize servo
steeringServo.attach(SERVO_PIN);
steeringServo.write(steeringAngle);
// Initialize motor pins
pinMode(MOTOR_ENA, OUTPUT);
pinMode(MOTOR_ENB, OUTPUT);
pinMode(MOTOR_IN1, OUTPUT);
pinMode(MOTOR_IN2, OUTPUT);
pinMode(MOTOR_IN3, OUTPUT);
pinMode(MOTOR_IN4, OUTPUT);
// Initialize LED and buzzer
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
Serial.println("Gesture Controller initialized!");
digitalWrite(LED_PIN, HIGH);
delay(1000);
digitalWrite(LED_PIN, LOW);
}
void loop() {
// Read sensor data
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
// Convert raw data to meaningful values
float accelX = ax / 16384.0; // Convert to g-force
float accelY = ay / 16384.0;
float accelZ = az / 16384.0;
float gyroX = gx / 131.0; // Convert to degrees/second
float gyroY = gy / 131.0;
float gyroZ = gz / 131.0;
// Detect gestures
GestureType gesture = detectGesture(accelX, accelY, accelZ, gyroX, gyroY, gyroZ);
// Process gesture and control car
processGesture(gesture);
// Update motor control
updateMotorControl();
// Send status via Bluetooth
sendStatusUpdate();
delay(50); // 20Hz update rate
}
enum GestureType {
NONE,
FORWARD,
BACKWARD,
LEFT,
RIGHT,
STOP,
EMERGENCY_STOP,
SPEED_UP,
SPEED_DOWN
};
GestureType detectGesture(float ax, float ay, float az, float gx, float gy, float gz) {
static unsigned long gestureStartTime = 0;
static GestureType currentGesture = NONE;
static bool gestureDetected = false;
// Calculate tilt angles
float pitch = atan2(ax, sqrt(ay * ay + az * az)) * 180.0 / PI;
float roll = atan2(ay, sqrt(ax * ax + az * az)) * 180.0 / PI;
// Calculate total acceleration magnitude
float totalAccel = sqrt(ax * ax + ay * ay + az * az);
// Detect shake gesture (emergency stop)
if (totalAccel > SHAKE_THRESHOLD) {
if (!gestureDetected) {
gestureDetected = true;
gestureStartTime = millis();
currentGesture = EMERGENCY_STOP;
}
return EMERGENCY_STOP;
}
// Detect tilt gestures
if (abs(pitch) > TILT_THRESHOLD || abs(roll) > TILT_THRESHOLD) {
if (!gestureDetected) {
gestureDetected = true;
gestureStartTime = millis();
if (pitch > TILT_THRESHOLD) {
currentGesture = FORWARD;
} else if (pitch < -TILT_THRESHOLD) {
currentGesture = BACKWARD;
} else if (roll > TILT_THRESHOLD) {
currentGesture = RIGHT;
} else if (roll < -TILT_THRESHOLD) {
currentGesture = LEFT;
}
}
// Check if gesture is held long enough
if (millis() - gestureStartTime > GESTURE_HOLD_TIME) {
return currentGesture;
}
} else {
// Reset gesture detection
gestureDetected = false;
currentGesture = NONE;
}
return NONE;
}
void processGesture(GestureType gesture) {
switch (gesture) {
case FORWARD:
motorSpeed = min(motorSpeed + 20, 255);
isMoving = true;
Serial.println("Gesture: FORWARD");
break;
case BACKWARD:
motorSpeed = max(motorSpeed - 20, -255);
isMoving = true;
Serial.println("Gesture: BACKWARD");
break;
case LEFT:
steeringAngle = max(steeringAngle - 10, 45);
Serial.println("Gesture: LEFT");
break;
case RIGHT:
steeringAngle = min(steeringAngle + 10, 135);
Serial.println("Gesture: RIGHT");
break;
case STOP:
motorSpeed = 0;
isMoving = false;
Serial.println("Gesture: STOP");
break;
case EMERGENCY_STOP:
motorSpeed = 0;
isMoving = false;
steeringAngle = 90; // Center steering
Serial.println("Gesture: EMERGENCY STOP");
emergencyStop();
break;
case SPEED_UP:
motorSpeed = min(motorSpeed + 30, 255);
Serial.println("Gesture: SPEED UP");
break;
case SPEED_DOWN:
motorSpeed = max(motorSpeed - 30, -255);
Serial.println("Gesture: SPEED DOWN");
break;
default:
// Auto-stop if no gesture for timeout period
if (millis() - lastGestureTime > GESTURE_TIMEOUT) {
motorSpeed = 0;
isMoving = false;
}
break;
}
if (gesture != NONE) {
lastGestureTime = millis();
}
}
void updateMotorControl() {
// Update steering servo
steeringServo.write(steeringAngle);
// Update motor speed and direction
if (motorSpeed > 0) {
// Forward
digitalWrite(MOTOR_IN1, HIGH);
digitalWrite(MOTOR_IN2, LOW);
digitalWrite(MOTOR_IN3, HIGH);
digitalWrite(MOTOR_IN4, LOW);
analogWrite(MOTOR_ENA, motorSpeed);
analogWrite(MOTOR_ENB, motorSpeed);
} else if (motorSpeed < 0) {
// Backward
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, HIGH);
digitalWrite(MOTOR_IN3, LOW);
digitalWrite(MOTOR_IN4, HIGH);
analogWrite(MOTOR_ENA, -motorSpeed);
analogWrite(MOTOR_ENB, -motorSpeed);
} else {
// Stop
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
digitalWrite(MOTOR_IN3, LOW);
digitalWrite(MOTOR_IN4, LOW);
analogWrite(MOTOR_ENA, 0);
analogWrite(MOTOR_ENB, 0);
}
// Update LED indicator
digitalWrite(LED_PIN, isMoving ? HIGH : LOW);
}
void emergencyStop() {
// Immediate stop
motorSpeed = 0;
isMoving = false;
// Sound buzzer
for (int i = 0; i < 3; i++) {
digitalWrite(BUZZER_PIN, HIGH);
delay(200);
digitalWrite(BUZZER_PIN, LOW);
delay(200);
}
// Flash LED
for (int i = 0; i < 5; i++) {
digitalWrite(LED_PIN, HIGH);
delay(100);
digitalWrite(LED_PIN, LOW);
delay(100);
}
}
void sendStatusUpdate() {
// Send status via Bluetooth
String status = "S:" + String(steeringAngle) +
",M:" + String(motorSpeed) +
",I:" + String(isMoving ? 1 : 0);
bluetooth.println(status);
// Send to serial for debugging
Serial.println(status);
}Advanced Gesture Recognition
// AdvancedGestureRecognition.h
#ifndef ADVANCED_GESTURE_RECOGNITION_H
#define ADVANCED_GESTURE_RECOGNITION_H
#include <Arduino.h>
#include <MPU6050.h>
class AdvancedGestureRecognition {
private:
MPU6050* mpu;
// Gesture history for pattern recognition
struct GestureHistory {
float accelX[10];
float accelY[10];
float accelZ[10];
float gyroX[10];
float gyroY[10];
float gyroZ[10];
unsigned long timestamps[10];
int index;
bool isFull;
} gestureHistory;
// Gesture patterns
struct GesturePattern {
float* accelPattern;
float* gyroPattern;
int patternLength;
float threshold;
GestureType gestureType;
};
GesturePattern patterns[5];
int patternCount;
// Kalman filter for sensor fusion
struct KalmanFilter {
float q; // Process noise
float r; // Measurement noise
float x; // State estimate
float p; // Error covariance
float k; // Kalman gain
} kalmanX, kalmanY, kalmanZ;
public:
AdvancedGestureRecognition(MPU6050* sensor);
void initialize();
GestureType recognizeGesture();
void addPattern(GestureType type, float* accelPattern, float* gyroPattern,
int length, float threshold);
float calculateSimilarity(float* pattern1, float* pattern2, int length);
float kalmanFilter(KalmanFilter& filter, float measurement);
void updateGestureHistory(float ax, float ay, float az, float gx, float gy, float gz);
bool detectPattern(GesturePattern& pattern);
};
AdvancedGestureRecognition::AdvancedGestureRecognition(MPU6050* sensor) {
mpu = sensor;
patternCount = 0;
// Initialize gesture history
gestureHistory.index = 0;
gestureHistory.isFull = false;
// Initialize Kalman filters
kalmanX.q = 0.1; kalmanX.r = 0.1; kalmanX.x = 0; kalmanX.p = 1;
kalmanY.q = 0.1; kalmanY.r = 0.1; kalmanY.x = 0; kalmanY.p = 1;
kalmanZ.q = 0.1; kalmanZ.r = 0.1; kalmanZ.x = 0; kalmanZ.p = 1;
}
void AdvancedGestureRecognition::initialize() {
// Define gesture patterns
// Pattern 1: Circle gesture (clockwise)
float circleAccel[] = {0, 0.5, 1, 0.5, 0, -0.5, -1, -0.5};
float circleGyro[] = {0, 0.5, 0, -0.5, 0, 0.5, 0, -0.5};
addPattern(SPEED_UP, circleAccel, circleGyro, 8, 0.7);
// Pattern 2: Figure-8 gesture
float figure8Accel[] = {0, 0.5, 1, 0.5, 0, -0.5, -1, -0.5, 0, 0.5, 1, 0.5, 0, -0.5, -1, -0.5};
float figure8Gyro[] = {0, 0.5, 0, -0.5, 0, 0.5, 0, -0.5, 0, -0.5, 0, 0.5, 0, -0.5, 0, 0.5};
addPattern(SPEED_DOWN, figure8Accel, figure8Gyro, 16, 0.6);
// Pattern 3: Double tap
float doubleTapAccel[] = {0, 2, 0, 0, 2, 0};
float doubleTapGyro[] = {0, 0, 0, 0, 0, 0};
addPattern(STOP, doubleTapAccel, doubleTapGyro, 6, 0.8);
}
void AdvancedGestureRecognition::addPattern(GestureType type, float* accelPattern,
float* gyroPattern, int length, float threshold) {
if (patternCount < 5) {
patterns[patternCount].gestureType = type;
patterns[patternCount].patternLength = length;
patterns[patternCount].threshold = threshold;
patterns[patternCount].accelPattern = new float[length];
patterns[patternCount].gyroPattern = new float[length];
for (int i = 0; i < length; i++) {
patterns[patternCount].accelPattern[i] = accelPattern[i];
patterns[patternCount].gyroPattern[i] = gyroPattern[i];
}
patternCount++;
}
}
GestureType AdvancedGestureRecognition::recognizeGesture() {
int16_t ax, ay, az, gx, gy, gz;
mpu->getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
// Convert to g-force and degrees/second
float accelX = ax / 16384.0;
float accelY = ay / 16384.0;
float accelZ = az / 16384.0;
float gyroX = gx / 131.0;
float gyroY = gy / 131.0;
float gyroZ = gz / 131.0;
// Apply Kalman filtering
accelX = kalmanFilter(kalmanX, accelX);
accelY = kalmanFilter(kalmanY, accelY);
accelZ = kalmanFilter(kalmanZ, accelZ);
// Update gesture history
updateGestureHistory(accelX, accelY, accelZ, gyroX, gyroY, gyroZ);
// Check for pattern matches
for (int i = 0; i < patternCount; i++) {
if (detectPattern(patterns[i])) {
return patterns[i].gestureType;
}
}
return NONE;
}
float AdvancedGestureRecognition::calculateSimilarity(float* pattern1, float* pattern2, int length) {
float similarity = 0;
float sum1 = 0, sum2 = 0, sum12 = 0;
for (int i = 0; i < length; i++) {
sum1 += pattern1[i] * pattern1[i];
sum2 += pattern2[i] * pattern2[i];
sum12 += pattern1[i] * pattern2[i];
}
if (sum1 == 0 || sum2 == 0) return 0;
similarity = sum12 / (sqrt(sum1) * sqrt(sum2));
return similarity;
}
float AdvancedGestureRecognition::kalmanFilter(KalmanFilter& filter, float measurement) {
// Prediction step
filter.p = filter.p + filter.q;
// Update step
filter.k = filter.p / (filter.p + filter.r);
filter.x = filter.x + filter.k * (measurement - filter.x);
filter.p = (1 - filter.k) * filter.p;
return filter.x;
}
void AdvancedGestureRecognition::updateGestureHistory(float ax, float ay, float az,
float gx, float gy, float gz) {
gestureHistory.accelX[gestureHistory.index] = ax;
gestureHistory.accelY[gestureHistory.index] = ay;
gestureHistory.accelZ[gestureHistory.index] = az;
gestureHistory.gyroX[gestureHistory.index] = gx;
gestureHistory.gyroY[gestureHistory.index] = gy;
gestureHistory.gyroZ[gestureHistory.index] = gz;
gestureHistory.timestamps[gestureHistory.index] = millis();
gestureHistory.index++;
if (gestureHistory.index >= 10) {
gestureHistory.index = 0;
gestureHistory.isFull = true;
}
}
bool AdvancedGestureRecognition::detectPattern(GesturePattern& pattern) {
if (!gestureHistory.isFull) return false;
// Calculate similarity for accelerometer data
float accelSimilarity = calculateSimilarity(
gestureHistory.accelX, pattern.accelPattern, pattern.patternLength);
// Calculate similarity for gyroscope data
float gyroSimilarity = calculateSimilarity(
gestureHistory.gyroX, pattern.gyroPattern, pattern.patternLength);
// Check if both similarities exceed threshold
return (accelSimilarity > pattern.threshold && gyroSimilarity > pattern.threshold);
}
#endifBluetooth Communication
// BluetoothController.h
#ifndef BLUETOOTH_CONTROLLER_H
#define BLUETOOTH_CONTROLLER_H
#include <SoftwareSerial.h>
class BluetoothController {
private:
SoftwareSerial* bluetooth;
String receivedCommand;
bool commandReady;
public:
BluetoothController(int rxPin, int txPin);
void initialize();
void sendCommand(String command);
String getReceivedCommand();
bool isCommandReady();
void processReceivedData();
void sendStatus(String status);
void sendError(String error);
};
BluetoothController::BluetoothController(int rxPin, int txPin) {
bluetooth = new SoftwareSerial(rxPin, txPin);
commandReady = false;
receivedCommand = "";
}
void BluetoothController::initialize() {
bluetooth->begin(9600);
Serial.println("Bluetooth initialized");
}
void BluetoothController::sendCommand(String command) {
bluetooth->println(command);
Serial.println("Sent: " + command);
}
String BluetoothController::getReceivedCommand() {
if (commandReady) {
commandReady = false;
return receivedCommand;
}
return "";
}
bool BluetoothController::isCommandReady() {
return commandReady;
}
void BluetoothController::processReceivedData() {
while (bluetooth->available()) {
char c = bluetooth->read();
if (c == '\n' || c == '\r') {
if (receivedCommand.length() > 0) {
commandReady = true;
Serial.println("Received: " + receivedCommand);
}
} else {
receivedCommand += c;
}
}
}
void BluetoothController::sendStatus(String status) {
String message = "STATUS:" + status;
sendCommand(message);
}
void BluetoothController::sendError(String error) {
String message = "ERROR:" + error;
sendCommand(message);
}
#endifSafety Features
Collision Avoidance
// CollisionAvoidance.h
#ifndef COLLISION_AVOIDANCE_H
#define COLLISION_AVOIDANCE_H
#include <Arduino.h>
class CollisionAvoidance {
private:
int triggerPin;
int echoPin;
float maxDistance;
float safeDistance;
public:
CollisionAvoidance(int trigger, int echo, float maxDist = 400.0, float safeDist = 50.0);
float getDistance();
bool isSafeToMove();
void emergencyStop();
void updateSafetyStatus();
};
CollisionAvoidance::CollisionAvoidance(int trigger, int echo, float maxDist, float safeDist) {
triggerPin = trigger;
echoPin = echo;
maxDistance = maxDist;
safeDistance = safeDist;
pinMode(triggerPin, OUTPUT);
pinMode(echoPin, INPUT);
}
float CollisionAvoidance::getDistance() {
// Send ultrasonic pulse
digitalWrite(triggerPin, LOW);
delayMicroseconds(2);
digitalWrite(triggerPin, HIGH);
delayMicroseconds(10);
digitalWrite(triggerPin, LOW);
// Read echo
long duration = pulseIn(echoPin, HIGH);
float distance = duration * 0.034 / 2; // Convert to cm
return (distance > maxDistance) ? maxDistance : distance;
}
bool CollisionAvoidance::isSafeToMove() {
float distance = getDistance();
return distance > safeDistance;
}
void CollisionAvoidance::emergencyStop() {
// Stop all motors immediately
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
digitalWrite(MOTOR_IN3, LOW);
digitalWrite(MOTOR_IN4, LOW);
analogWrite(MOTOR_ENA, 0);
analogWrite(MOTOR_ENB, 0);
// Sound alarm
digitalWrite(BUZZER_PIN, HIGH);
delay(500);
digitalWrite(BUZZER_PIN, LOW);
}
void CollisionAvoidance::updateSafetyStatus() {
if (!isSafeToMove()) {
emergencyStop();
Serial.println("EMERGENCY STOP: Obstacle detected!");
}
}
#endifTesting and Calibration
Sensor Calibration
// SensorCalibration.h
#ifndef SENSOR_CALIBRATION_H
#define SENSOR_CALIBRATION_H
#include <MPU6050.h>
class SensorCalibration {
private:
MPU6050* mpu;
float accelOffsetX, accelOffsetY, accelOffsetZ;
float gyroOffsetX, gyroOffsetY, gyroOffsetZ;
public:
SensorCalibration(MPU6050* sensor);
void calibrateSensors();
void applyCalibration();
void saveCalibration();
void loadCalibration();
};
SensorCalibration::SensorCalibration(MPU6050* sensor) {
mpu = sensor;
accelOffsetX = accelOffsetY = accelOffsetZ = 0;
gyroOffsetX = gyroOffsetY = gyroOffsetZ = 0;
}
void SensorCalibration::calibrateSensors() {
Serial.println("Calibrating sensors... Keep device still!");
delay(2000);
float sumAccelX = 0, sumAccelY = 0, sumAccelZ = 0;
float sumGyroX = 0, sumGyroY = 0, sumGyroZ = 0;
int samples = 1000;
for (int i = 0; i < samples; i++) {
int16_t ax, ay, az, gx, gy, gz;
mpu->getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
sumAccelX += ax;
sumAccelY += ay;
sumAccelZ += az;
sumGyroX += gx;
sumGyroY += gy;
sumGyroZ += gz;
delay(1);
}
// Calculate offsets
accelOffsetX = sumAccelX / samples;
accelOffsetY = sumAccelY / samples;
accelOffsetZ = sumAccelZ / samples - 16384; // Account for gravity
gyroOffsetX = sumGyroX / samples;
gyroOffsetY = sumGyroY / samples;
gyroOffsetZ = sumGyroZ / samples;
Serial.println("Calibration complete!");
Serial.println("Accel offsets: " + String(accelOffsetX) + ", " +
String(accelOffsetY) + ", " + String(accelOffsetZ));
Serial.println("Gyro offsets: " + String(gyroOffsetX) + ", " +
String(gyroOffsetY) + ", " + String(gyroOffsetZ));
}
void SensorCalibration::applyCalibration() {
mpu->setXAccelOffset(accelOffsetX);
mpu->setYAccelOffset(accelOffsetY);
mpu->setZAccelOffset(accelOffsetZ);
mpu->setXGyroOffset(gyroOffsetX);
mpu->setYGyroOffset(gyroOffsetY);
mpu->setZGyroOffset(gyroOffsetZ);
}
#endifLessons Learned
Embedded Systems Programming
- Real-time Processing: Efficient real-time sensor data processing
- Hardware Integration: Proper integration of multiple sensors and actuators
- Power Management: Optimizing power consumption for battery operation
- Interrupt Handling: Proper use of interrupts for time-critical operations
Sensor Technology
- Sensor Fusion: Combining multiple sensors for accurate gesture recognition
- Calibration: Proper sensor calibration for accurate measurements
- Noise Filtering: Implementing filters to reduce sensor noise
- Threshold Tuning: Optimizing detection thresholds for reliable operation
Safety and Reliability
- Fail-safe Design: Implementing safety mechanisms for autonomous operation
- Error Handling: Robust error handling and recovery mechanisms
- Testing: Comprehensive testing of all system components
- Documentation: Clear documentation for maintenance and troubleshooting
Future Enhancements
Advanced Features
- Machine Learning: Implement ML algorithms for gesture recognition
- Computer Vision: Add camera-based gesture recognition
- Voice Control: Integrate voice commands for additional control
- Mobile App: Develop mobile app for remote control and monitoring
Technical Improvements
- Wireless Communication: Implement WiFi or cellular connectivity
- Data Logging: Add data logging for analysis and optimization
- Remote Monitoring: Real-time monitoring and control via web interface
- Predictive Maintenance: Implement predictive maintenance algorithms
Conclusion
The Gesture Controller project demonstrates innovative embedded systems programming and human-machine interaction. Key achievements include:
- Innovative Design: Creative approach to car control using hand gestures
- Hardware Integration: Successful integration of multiple sensors and actuators
- Real-time Processing: Efficient real-time sensor data processing and gesture recognition
- Safety Features: Comprehensive safety mechanisms and collision avoidance
- User Experience: Intuitive gesture-based control system
- Technical Excellence: Clean, well-documented, and maintainable code
The project is available on GitHub and serves as a comprehensive example of embedded systems development and IoT applications.
This project represents my exploration into embedded systems programming and demonstrates how creative thinking can lead to innovative solutions in human-machine interaction. The lessons learned here continue to influence my approach to hardware programming and IoT development.