Week 02 — Electronics & Embedded Programming
Inputs → code → outputs. From a heartbeat "tick" to my own touch-controlled plant on an OLED.
How to Survive Soldering & Embedded Programming
Welcome to my crash course in being confused, clicking too many things, and eventually making something!
This is my beginner-friendly guide to going from "I have no idea what's happening" to real, working code on hardware.
Note: We're all learning. It's new, and that's okay.
1) What is embedded programming
A microcontroller is a tiny computer that runs one focused program. The sketch has two parts:
setup()
runs once, and loop()
runs forever. The loop is basically
read → decide → act.
void setup() { // runs once at power-up
// pinMode(...), Serial.begin(...), etc.
}
void loop() { // runs forever
// read → map → output
}
Lecture notes (Anthony): stay at 3.3 V on RP2040 pins, prove life with a blink/serial test first, then add features gradually. Good soldering is part of debugging.
2) Soldering (intro + my process)
Goal: each castellated pad is fully wetted—joining the module edge pad and my PCB pad. I tinned the iron, used flux, tack-soldered corners, then ran a small bead across each edge. Final step: continuity checks.
Embed: Anthony's "Intro to Soldering"
Embed: my XIAO RP2040 soldering timelapse


3) Initial sanity tests (thanks, Quentin)
I validated the hardware using Quentin's minimal Arduino sketches. These prove three layers quickly: microcontroller runs code (blink), I²C display works (hello text), and touch pads respond (cap sense + LED example).
Testing
3.1 Blink (is the microcontroller alive?)
// Blink — basic liveness test (XIAO RP2040)
void setup(){
pinMode(LED_BUILTIN, OUTPUT);
}
void loop(){
digitalWrite(LED_BUILTIN, HIGH); delay(500);
digitalWrite(LED_BUILTIN, LOW); delay(500);
}
If this blinks, the RP2040 is running code and GPIO works.
Video: my blink test.
3.2 Display (SSD1306 over I²C)
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define SCREEN_ADDRESS 0x3C // sometimes 0x3D
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1, 1700000UL, 1700000UL);
void setup() {
Serial.begin(0); // RP2040 USB CDC (auto)
delay(50); // let the screen power up
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
display.clearDisplay();
display.display();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
}
void loop() {
display.clearDisplay();
display.setCursor(28, 25);
display.print("Hello world!");
display.display();
}
Wiring used: SDA→D4, SCL→D5, VCC→3V3, GND→GND. If you see "Hello world!" the I²C bus and OLED are good.
Video: OLED "Hello world!"
3.3 Touch (capacitive pads + LED example)
#define PIN_RED 17
#define PIN_GREEN 16
#define PIN_BLUE 25
#define N_TOUCH 6
#define THRESHOLD 30
int touch_pins[N_TOUCH] = {3, 4, 2, 27, 1, 26};
int touch_values[N_TOUCH] = {0, 0, 0, 0, 0, 0};
bool pin_touched_now[N_TOUCH] = {0};
bool pin_touched_past[N_TOUCH] = {0};
void update_touch() {
int t, t_max = 200, p;
for (int i = 0; i < N_TOUCH; i++) {
p = touch_pins[i];
pinMode(p, OUTPUT); digitalWriteFast(p, LOW);
delayMicroseconds(25);
noInterrupts();
pinMode(p, INPUT_PULLUP);
t = 0;
while (!digitalReadFast(p) && t < t_max) { t++; }
touch_values[i] = t;
interrupts();
pin_touched_past[i] = pin_touched_now[i];
pin_touched_now[i] = touch_values[i] > THRESHOLD;
}
}
void print_touch() {
char buf[30];
for (int i=0; i < N_TOUCH; i++) {
sprintf(buf, "%4d ", touch_values[i]);
Serial.print(buf);
}
Serial.println("");
}
void setup() {
Serial.begin(0); // RP2040 USB CDC
pinMode(PIN_RED, OUTPUT); pinMode(PIN_GREEN, OUTPUT); pinMode(PIN_BLUE, OUTPUT);
digitalWrite(PIN_RED, HIGH); digitalWrite(PIN_GREEN, HIGH); digitalWrite(PIN_BLUE, HIGH); // HIGH = off with this wiring
}
void loop() {
update_touch();
// example: button 0 press/release toggles GREEN LED
if (pin_touched_now[0] && !pin_touched_past[0]) { digitalWrite(PIN_GREEN, LOW); } // on
if (!pin_touched_now[0] && pin_touched_past[0]) { digitalWrite(PIN_GREEN, HIGH); } // off
print_touch();
delay(50);
}
On touch, numbers jump in Serial Monitor. This proves the capacitive pads + GPIO are behaving.
Video: touch readings + LED reaction.
Source sketches and more examples:
Quentin Bolsee — qpad-xiao / Arduino
.
Note: Serial.begin(0)
is valid on RP2040's USB CDC (auto-baud). On other boards you usually use 115200
.
4) Re-solder (robust) — the mistake that taught me fast
I powered the PCB while it rested on metal → OLED went blank. I swapped in a fresh XIAO and re-soldered carefully. Lesson: protect the back of the board, use a non-conductive mat, and sanity-test after rework.

5) My own testing phase (post re-solder)
With the fresh XIAO RP2040 soldered in, I began verifying that everything worked. I started with Quentin's base sketches to confirm the board was alive, then modified them myself. These tests show inputs, outputs, and communication all working.
5.1 Heartbeat → "tick" → "hello"
I combined Serial and LED in a heartbeat sketch. Originally it printed "tick" each second — I edited it to say "hello." This was my first authored modification to someone else's code.
// Heartbeat serial
static unsigned long t0 = millis();
if (millis() - t0 > 1000) {
t0 = millis();
Serial.println("hello"); // my edit
}
Microcontroller & Board (what I'm actually programming)
Microcontroller (chip): RP2040
- Dual-core Arm Cortex-M0+ up to 133 MHz; 264 KB SRAM; external flash
- USB device, GPIO (digital / PWM / analog), timers, I²C / SPI / UART
- Logic level: 3.3 V (not 5 V tolerant)
Board (module): Seeed Studio XIAO RP2040
- USB-C (power + programming), 3.3 V regulator, boot/reset (UF2), user LED
- Castellated pins break out the RP2040's GPIO so I can solder/use them
My understanding is: the RP2040 is the engine; the XIAO board is the car around it (USB, power, pins).
Why both matter: the datasheet tells me what the chip can do; the board docs tell me how those pins are wired so I can actually use them.
Summary: I am programming the RP2040 microcontroller via the XIAO RP2040 board on my PCB.
Group Assignment — Toolchains & Workflows (Reference)
For group assignment we were asked to compare embedded architectures and development workflows (Arduino C/C++, MicroPython/CircuitPython, toolchains, and bootloaders). See our group page and a classmate's write-up for details:
My program — touch-controlled plant on the OLED
I wanted something a bit poetic: a plant that "grows" and "sways" when I touch the pads.
Touch values are normalized to 0..1 and mapped to parameters (stem height, sway angle, bloom size).
OLED is SSD1306 128×64 over I²C: SDA → D4, SCL → D5, VCC → 3V3, GND → GND.
Update TOUCH_PINS
to match your board.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define I2C_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// === Update to YOUR touch pad pins ===
const int TOUCH_PINS[] = {2, 3, 4};
const int N = sizeof(TOUCH_PINS)/sizeof(TOUCH_PINS[0]);
uint32_t tMin[3] = {100000,100000,100000}, tMax[3] = {0,0,0};
// crude capacitive read: higher when touched
uint32_t readCapRaw(int pin, int samples=6){
uint32_t total=0;
for(int s=0; s<samples; s++){
pinMode(pin, OUTPUT); digitalWrite(pin, LOW); delayMicroseconds(5);
pinMode(pin, INPUT_PULLUP);
uint32_t t0 = micros();
while(digitalRead(pin)==LOW){ if(micros()-t0 > 3000) break; }
total += (micros()-t0);
}
return total / samples;
}
float clamp01(float v){ return v<0?0:v>1?1:v; }
float norm(float v, float lo, float hi){ return (hi<=lo+1)?0.0f: (v-lo)/(hi-lo); }
void setup(){
pinMode(LED_BUILTIN, OUTPUT);
Wire.begin();
if(!display.begin(SSD1306_SWITCHCAPVCC, I2C_ADDR)){
while(true){ digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); delay(100); }
}
display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE);
display.setCursor(0,0);
display.println("Touch plant: D4/D5=I2C");
display.println("Pad0=grow 1=sway 2=bloom");
display.display();
delay(600);
}
void loop(){
uint32_t cap[3] = {0,0,0};
for(int i=0; i<N && i<3; i++){
cap[i] = readCapRaw(TOUCH_PINS[i], 10);
if(cap[i] < tMin[i]) tMin[i] = cap[i];
if(cap[i] > tMax[i]) tMax[i] = cap[i];
}
float n0 = (N>0) ? clamp01(norm(cap[0], tMin[0], tMax[0])) : 0.0f; // height
float n1 = (N>1) ? clamp01(norm(cap[1], tMin[1], tMax[1])) : 0.5f; // sway
float n2 = (N>2) ? clamp01(norm(cap[2], tMin[2], tMax[2])) : 0.3f; // bloom
int stemLen = (int)(20 + n0 * 42);
float sway = (n1 - 0.5f) * 0.8f;
int bloomR = (int)(3 + n2 * 10);
display.clearDisplay();
int baseX=64, baseY=62;
display.drawLine(0, baseY, 127, baseY, SSD1306_WHITE);
int x0=baseX, y0=baseY;
int x1=baseX, y1=baseY - stemLen/3;
int x2=baseX + (int)(sway*18), y2=baseY - (2*stemLen)/3;
int x3=baseX, y3=baseY - stemLen;
display.drawLine(x0,y0, x1,y1, SSD1306_WHITE);
display.drawLine(x1,y1, x2,y2, SSD1306_WHITE);
display.drawLine(x2,y2, x3,y3, SSD1306_WHITE);
auto leaf=[&](int x,int y,float ang,int len){
int x2=x+(int)(cos(ang)*len), y2=y+(int)(sin(ang)*len);
display.drawLine(x,y,x2,y2,SSD1306_WHITE);
display.drawLine(x,y,x2,y2+3,SSD1306_WHITE);
display.drawLine(x2,y2,x2,y2+3,SSD1306_WHITE);
};
leaf(baseX-8, baseY - stemLen*2/5, -1.9f + sway*0.6f, 10 + (int)(n0*8));
leaf(baseX+8, baseY - stemLen*3/5, 1.9f + sway*0.6f, 10 + (int)(n0*8));
display.drawCircle(x3, y3, bloomR, SSD1306_WHITE);
display.fillCircle(x3, y3, bloomR/2, SSD1306_WHITE);
display.setTextSize(1); display.setCursor(0,0);
display.print("c0:"); display.print(cap[0]);
display.print(" c1:"); display.print(cap[1]);
display.print(" c2:"); display.print(cap[2]);
display.display();
delay(30);
}
Arduino Programming Cheat Card (XIAO RP2040)
void setup() { /* runs once at startup */ }
void loop() { /* runs forever */ }
// Pins
pinMode(pin, OUTPUT);
pinMode(pin, INPUT_PULLUP);
digitalWrite(pin, HIGH); // on
digitalWrite(pin, LOW); // off
int d = digitalRead(pin); // HIGH/LOW
// Analog & Serial
int a = analogRead(A0); // 0..1023
Serial.begin(115200);
Serial.println("Hello");
// Timing
delay(500); // 500 ms = 0.5 s
unsigned long t = millis(); // ms since boot
Troubleshooting (what actually happened)
- Serial gibberish → baud mismatch: set
115200
. - Blank OLED → check I²C wiring (SDA=D4, SCL=D5), try
0x3C
vs0x3D
, power cycle. - Touch flatline → wrong pins in
TOUCH_PINS
or a dry joint; reflow with flux. - After soldering → clean flux, continuity test, re-run "blink + hello" before complex code.
How this meets the assignment
- Datasheet: I read the RP2040 + XIAO docs (see Sources) and summarized chip vs board, voltage limits, and I²C pins.
- Program & interaction: I authored an Arduino sketch that reads local inputs (touch pads) and drives local outputs (OLED drawing, LED).
- Communication: I used USB Serial for the heartbeat "tick → hello" and raw touch prints. I also used a Processing sketch on my laptop as a remote visualization (serial → animation). [I'll add screenshots here]
- Extra credit (envs/languages): Primary in Arduino (C/C++). I experimented with Processing (host-side visualization toolchain). Next I may try CircuitPython to compare workflows.
Sources
- In-class & recitations; electronics presentation by Anthony.
- Quentin Bolsee — XIAO RP2040 sanity tests (touch/LED), notes & code references.
- Seeed Studio XIAO RP2040 docs (pinout, I²C), RP2040 datasheet.
- Group reference: EECS Week 03 page
- Example group section: Saleem's week 2
- ChatGPT (GPT-5 Thinking) — tutoring and code scaffolding. Verified on my hardware and adapted to my PCB.