Overview
This week focused on embedded programming with microcontrollers. The assignment required browsing through microcontroller datasheets and creating a program that interacts with local input/output devices and communicates with remote connections. I chose to work with the Xiao RP2040 microcontroller to create a capacitive touch-based game controller.
Assignment Goals
- Browse and understand microcontroller datasheets
- Write and test a program for an embedded system
- Implement local input/output device interaction
- Enable remote communication (USB HID in this case)
Hardware Setup & Soldering Challenges
The initial plan was to solder an LED screen to the Xiao RP2040 to provide visual feedback. However, I encountered significant challenges during the soldering process.
Initial Soldering Attempt
Failed soldering attempt - pins were difficult to solder and some copper traces were broken
Problem Analysis
- Difficult Pin Access: The original screen placement made it hard to reach all pins with the soldering iron
- Broken Copper Traces: Excessive heat and mechanical stress damaged some of the board traces
- Poor Joint Quality: The cramped space resulted in cold solder joints
Solution: Alternative Screen Placement
New screen placement strategy for easier soldering and better access to pins
I redesigned the screen placement to provide better access for soldering. This alternative configuration allowed for cleaner solder joints and reduced the risk of damaging the board.
Microcontroller Selection: Xiao RP2040
For this project, I selected the Seeed Studio XIAO RP2040 microcontroller for several reasons:
Key Specifications
| Feature | Specification | Benefits |
|---|---|---|
| Processor | Dual-core ARM Cortex-M0+ @ 133 MHz | Fast processing for real-time touch sensing |
| Memory | 264KB SRAM, 2MB Flash | Sufficient for complex programs |
| GPIO Pins | 11 digital pins | Plenty for multiple touch sensors |
| USB Support | Native USB 1.1 with HID support | Direct USB keyboard emulation |
| Built-in LED | RGB LED (Red, Green, Blue) | Visual feedback without external components |
| Power | 3.3V logic, 5V USB power | Standard USB-powered operation |
Why RP2040 for This Application?
- Programmable I/O (PIO): The RP2040's unique PIO feature allows for flexible peripheral implementation
- USB HID Native Support: Built-in USB stack makes it easy to implement keyboard/game controller functionality
- Fast ADC and GPIO: Excellent for responsive capacitive touch sensing
- Arduino Support: Well-documented with extensive community libraries
Software Implementation
Instead of just displaying information on the LED screen, I pivoted to create a more interactive project: a capacitive touch game controller that acts as a USB HID keyboard, allowing the board to control games directly.
Capacitive Touch Sensing Algorithm
The core of this project is a software-based capacitive touch sensing technique that doesn't require specialized hardware. The algorithm works by measuring the RC charging time of a pin:
- Discharge: Drive the pin LOW to discharge any capacitance
- Measure: Switch to INPUT_PULLUP and measure time for the pin to go HIGH
- Detect: When a finger approaches, capacitance increases, slowing the charging time
- Threshold: Compare timing against a calibrated threshold to detect touch
Key Features
- 6 Touch Sensors: Mapped to game controls (arrow keys, action buttons)
- USB HID Keyboard Emulation: Appears as a standard keyboard to any computer
- Visual Feedback: Built-in RGB LED flashes green on button press
- Low Latency: 10ms polling rate for responsive gaming
- Serial Debug Output: Real-time touch value monitoring
Pin Mapping
| Touch Pin | GPIO Pin | Game Button | Key Mapping |
|---|---|---|---|
| 0 | GPIO 3 | A Button | Space |
| 1 | GPIO 4 | B Button | X |
| 2 | GPIO 2 | D-Pad Up | Up Arrow |
| 3 | GPIO 26 | D-Pad Down | Down Arrow |
| 4 | GPIO 1 | D-Pad Left | Left Arrow |
| 5 | GPIO 27 | D-Pad Right | Right Arrow |
Complete Source Code
The full Arduino sketch implements capacitive touch sensing and USB HID keyboard functionality:
#include <Keyboard.h>
// Pin definitions for Xiao RP2040
#define PIN_RED 17 // Built-in RGB LED Red
#define PIN_GREEN 16 // Built-in RGB LED Green
#define PIN_BLUE 25 // Built-in RGB LED Blue
#define N_TOUCH 6
#define THRESHOLD 30
// Touch sensor pins - adjust these to your actual connections
int touch_pins[N_TOUCH] = {3, 4, 2, 26, 1, 27}; // GPIO pins on Xiao RP2040
int touch_values[N_TOUCH] = {0, 0, 0, 0, 0, 0};
bool pin_touched_now[N_TOUCH] = {false, false, false, false, false, false};
bool pin_touched_past[N_TOUCH] = {false, false, false, false, false, false};
// Game controller key mapping
char key_mappings[N_TOUCH] = {
' ', // Pin 0: A button (Space)
'x', // Pin 1: B button (X)
218, // Pin 2: Up Arrow (KEY_UP_ARROW)
217, // Pin 3: Down Arrow (KEY_DOWN_ARROW)
216, // Pin 4: Left Arrow (KEY_LEFT_ARROW)
215 // Pin 5: Right Arrow (KEY_RIGHT_ARROW)
};
void update_touch() {
int t;
int t_max = 200;
int p;
for (int i = 0; i < N_TOUCH; i++) {
p = touch_pins[i];
// Set pin to output and drive low
pinMode(p, OUTPUT);
digitalWrite(p, LOW);
// Short settle time
delayMicroseconds(25);
// Disable interrupts for accurate timing
noInterrupts();
// Switch to input with pullup
pinMode(p, INPUT_PULLUP);
// Measure rise time
t = 0;
while (!digitalRead(p) && t < t_max) {
t++;
}
touch_values[i] = t;
// Re-enable interrupts
interrupts();
// Update touch state
pin_touched_past[i] = pin_touched_now[i];
pin_touched_now[i] = touch_values[i] > THRESHOLD;
}
}
void handle_game_input() {
for (int i = 0; i < N_TOUCH; i++) {
// Button just pressed
if (pin_touched_now[i] && !pin_touched_past[i]) {
Keyboard.press(key_mappings[i]);
// LED feedback - flash green when any button is pressed
digitalWrite(PIN_GREEN, HIGH);
delay(50);
digitalWrite(PIN_GREEN, LOW);
Serial.print("Button ");
Serial.print(i);
Serial.println(" pressed");
}
// Button just released
if (!pin_touched_now[i] && pin_touched_past[i]) {
Keyboard.release(key_mappings[i]);
Serial.print("Button ");
Serial.print(i);
Serial.println(" released");
}
}
}
void print_touch() {
for (int i = 0; i < N_TOUCH; i++) {
Serial.print(touch_values[i]);
Serial.print("\t");
}
Serial.println();
}
void setup() {
// Initialize Serial
Serial.begin(115200);
while (!Serial) delay(100); // Wait for serial connection
// Initialize USB HID Keyboard
Keyboard.begin();
// Initialize LED pins
pinMode(PIN_RED, OUTPUT);
pinMode(PIN_GREEN, OUTPUT);
pinMode(PIN_BLUE, OUTPUT);
// Turn off LEDs initially
digitalWrite(PIN_RED, LOW);
digitalWrite(PIN_GREEN, LOW);
digitalWrite(PIN_BLUE, LOW);
Serial.println("Xiao RP2040 Touch Game Controller Ready!");
Serial.println("Touch mapping:");
Serial.println("0: A Button (Space)");
Serial.println("1: B Button (X)");
Serial.println("2: Up Arrow");
Serial.println("3: Down Arrow");
Serial.println("4: Left Arrow");
Serial.println("5: Right Arrow");
// LED startup indication
digitalWrite(PIN_BLUE, HIGH);
delay(500);
digitalWrite(PIN_BLUE, LOW);
}
void loop() {
// Update touch sensors
update_touch();
// Handle game input
handle_game_input();
// Print debug info (uncomment for debugging)
// print_touch();
// Small delay
delay(10);
}
Code Explanation
Capacitive Touch Sensing (update_touch()):
- Iterates through all 6 touch pins
- Discharges the pin by setting it to OUTPUT LOW
- Switches to INPUT_PULLUP and measures charging time
- Disables interrupts during measurement for accuracy
- Compares against threshold to determine touch state
- Tracks both current and previous states for edge detection
Game Input Handler (handle_game_input()):
- Detects button press events (transition from not-touched to touched)
- Sends USB HID keyboard press commands
- Provides visual feedback with green LED flash
- Detects button release events
- Sends USB HID keyboard release commands
- Outputs debug information to serial monitor
Setup and Configuration:
- Initializes serial communication at 115200 baud
- Starts USB HID keyboard emulation
- Configures RGB LED pins as outputs
- Displays startup message with key mappings
- Flashes blue LED to indicate successful initialization
Final Result: Game Controller in Action
Completed embedded system with capacitive touch sensors
Demonstration Video
Testing the touch-based game controller with a game - the Xiao RP2040 acts as a USB keyboard
Key Learnings
Hardware Lessons
Through this project, I learned several critical hardware design principles. Component placement significantly impacts soldering success - it's essential to plan for tool access before finalizing board layouts. I discovered that excessive heat can damage copper traces, requiring careful attention to soldering temperature and timing. The failed initial attempt taught me to consider rework and debugging accessibility when designing PCB component placement. Additionally, capacitive touch sensor performance varies dramatically with pin layout and ground plane proximity, requiring thoughtful routing decisions.
Software Lessons
The software implementation provided deep insights into embedded systems programming. Understanding USB HID descriptors and keyboard report formats was essential for creating a functional game controller. I learned that disabling interrupts during critical timing measurements prevents jitter and ensures accurate capacitive touch sensing. Implementing a state machine that tracks both current and previous touch states enabled reliable edge detection for button press and release events. Touch threshold calibration proved crucial, as sensitivity varies with environmental factors and sensor construction. Finally, I gained experience balancing responsiveness with stability in real-time embedded loops, achieving a 10ms polling rate that provides excellent gaming performance.
Microcontroller Architecture Insights
Working with the RP2040 revealed the power of modern embedded architectures. The chip's programmable I/O (PIO) blocks could enable even more sophisticated touch sensing algorithms with dedicated state machines. The dual-core architecture presents opportunities to separate sensing and USB communication on different cores for better performance and lower latency. The TinyUSB library provides excellent abstraction over the hardware USB peripheral, making HID implementation straightforward. Most importantly, the RP2040's fast GPIO response enables reliable software-based capacitive sensing without requiring dedicated touch-sensing hardware, demonstrating how capable modern microcontrollers have become.