Week 3

Embedded Programming

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

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

Soldering Failure

Failed soldering attempt - pins were difficult to solder and some copper traces were broken

Problem Analysis

Solution: Alternative Screen Placement

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?

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:

  1. Discharge: Drive the pin LOW to discharge any capacitance
  2. Measure: Switch to INPUT_PULLUP and measure time for the pin to go HIGH
  3. Detect: When a finger approaches, capacitance increases, slowing the charging time
  4. Threshold: Compare timing against a calibrated threshold to detect touch

Key Features

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()):

Game Input Handler (handle_game_input()):

Setup and Configuration:

Final Result: Game Controller in Action

Final Assembled Controller

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.

Files and Resources