Week 4 : Embedded Programming

Overview

This week I programmed a microcontroller to both interact with a physical output device and communicate data over a wired connection. In my final project I hope to be able to communicate wirelessly. For my individual assignment I will use a Seeed Studio XIAO RP2040 running MicroPython to drive a vibrating motor while simultaneously transmitting live telemetry back to my computer over USB serial.

I chose a vibrating motor as my output device because it is a core component of my final project. This week gave me the opportunity to understand how to control it programmatically before integrating it into a larger system.

Notes on Interaction vs Communication:The vibrating motor is my local output device because it physically responds to the board's GPIO pins. The USB serial stream (visible in Thonny's shell) is my communication. My RP2040 board transmits structured text data to my computer over a wired connection in real time. I'm hoping by doing it this why this week's requirements are satisfied in a single program.

Group Assignment

Group assignment: Demonstrate and compare the toolchains and development workflows for available embedded architectures Document your work to the group work page and reflect on your individual page what you learned

Individual Assignment

Browse through the datasheet for a microcontroller Write and test a program for an embedded system using a microcontroller to interact (with local input &/or output devices) and communicate (with remote wired or wireless connections)

Group Assignment Workflow: Comparing Toolchains and Development Workflows

Note: I am a remote student so I worked on this portion alone.

Notes on comparing toolchains

Comparing the RP2040 and the ATmega328 side by side made the differences between interpreted and compiled workflows very concrete. With the RP2040 and MicroPython, I can run a line of code instantly in the REPL and see the motor respond in real time. With the ATmega328 and Arduino IDE, the C++ code must be compiled into a binary on my computer first, then flashed to the board. That extra step is a meaningful difference in how quickly I can iterate and debug. The voltage difference (3.3V vs 5V logic) also matters practically. There are components that work safely with one board that may not be safe with the others.

What is architecture in electronics?

In the context of microcontrollers, architecture is the "blueprint" of how the chip's internal brain is wired. It defines how the CPU (core processing unit), memory, and peripherals talk to each other and how the chip executes those instructions.

For this week's assignment, I am comparing two distinct types of architecture (blueprints): AVR and ARM (i'll describe more later)

First, there are a few important things to consider within the blueprint.

  1. Bit Width (8-bit vs 32-bit): This is the size of the "data highway" inside the chip.
  2. Instruction Set: The lamguage and instructions being carried. In my case, both of my chips are RISC (Reduced Instruction Set Computer), but they speak different "languages" (I'll describe this later).
  3. Memory Layout (Harvard vs Von Neumann): This defines how the chip stores and accesses its "to-do list" (code) versus its "work bench" (data).
  4. Register Sets: Think of registers as the chip's "short-term memory" where it holds the values it is currently working on.

I decided to explore two MCU families: ARM and AVR. Next I'll explain their architecture:

AVR architecture:
  1. Bit Width: The bit Width is 8 bit. The CPU processes data in 8-bit chunks (from 0 to 255). If someone is trying to program a large math calculation for example, the CPU has to break it into pretty small pieces which means this is pretty slow but very power efficient for simple tasks.
  2. Instruction Set: As stated above, the AVR chip is RISC; however, the AVR chip speaks a language that uses proprietary instruction sets owned by Microchip. Its designed such that almost every instruction takes exactly one clock cycle, making timing extremely predictable.
  3. Memory Layout: Harvard memory layout phycically seperates paths for instructions and data. Basically, the chip can read and fetch data simultaniously which prevents bottlenecking.
  4. Register Sets: AVR has 32 general use registers
ARM architecture:
  1. Bit Width:The highway for an ARM chip is a LOT wider than an AVR and processes a LOT faster. 32-bit chunks with numbers up to 4 billlion can be processed at a time. It can handle pretty complex tasks quickly (for example, running a Python interpreter)
  2. Instruction Set:ARM licenses the Cortex-M language to other companies who then build the physical hardware around it.
  3. Memory Layout:The Von Neumann matrix is complex where everything lives in one giant map which allows for more flexability. For example, the chip can run code directly out of RAM for extra speed if needed.
  4. Register Sets: AVR is equipt with 13-16 different set of registers that are a lot wider, ARM chips are able to handle the 32 bit numbers more easily.

Comparing toolchains and Development Workflows

I am a remote student and will have to complete the group assignment on my own. I currently have an Adafruit Metro and FabriXiao (from the Czech Republic and my Fabricademy course) available on hand for comparisons. I am concerned about the use of my FabriXiao as I used it heavily in the workshop I attended at FAB 25 in the Czech Republic and also used it extensively during my time as a student in the 2025/2026 Fabricademy course. I hope that I will be able to program and utilize it for this project as well, otherwise, I will need to source another chip. The FabriXiao utilizes the Seeed Studio XIAO-RP2040 board.

I decided to source another chip, I purchased a Xiao RP2040 and will us it below in my comparissons.

I analyzed and compared the toolchains, development workflows, and electrical differences between my available embedded architectures. Specifically, I contrasted the Seeed Studio XIAO RP2040 (running MicroPython via Thonny IDE) with the Adafruit Metro 328 (running C++ via Arduino IDE).

Microcontroller Specs Comparissons

Hardware Feature Seeed Studio Xiao RP2040 Adafruit Metro 328 (ATmega328)
Processor Architecture 32-bit Dual ARM Cortex-M0+ 8-bit AVR RISC
Max Clock Speed 133 MHz 16 MHz
On-Chip SRAM 264 KB 2 KB
Flash Memory 2 MB (External QSPI) 32 KB
Operating Voltage 3.3V 5.0V
Form Factor Style Ultra-compact SMT / Castellated Classic Arduino UNO Revision-3 Footprint
Native Python Runtime Yes(MicroPython / CircuitPython) No(Insufficient SRAM footprint)
Primary Toolchain Thonny IDE / VS Code (PlatformIO) Arduino IDE (C++)
Upload Mechanism Mass Storage (UF2 drag-and-drop via Native USB) USB-to-Serial (Optiboot Bootloader via CP2104 / CH340 chip)
Unique Hardware Blocks Programmable I/O (PIO Blocks) Direct high-current I/O pin drive capabilities (up to 40mA)

Toolchain & Workflow Comparissons

Board Compilation vs. Interpretation Voltage and Electrical Safety Bootloader Triggering
Xiao RP 2040 The Thonny toolchain sends my raw .py text script directly to an active, on-chip Python interpreter. Operates strictly on 3.3V logic (PLEASE view the safety update under this table) Requires holding a physical "BOOT" button while connecting USB to accept a new file system runtime
Adafruit Metro 328 (ATmega328) Requires C++ code to be compiled into a static binary image on my computer before upload. Operates on 5V logic Utilizes an automated onboard USB-to-UART bridge to auto-reset and flash code instantly when hitting "Upload".
A note and update on the table above learned during the System Integraton weekly review: I chose to change my board for my final project from a RP2040 to a RP2350 because of voltage and electrical safety concersn related to the RP2040. The RP2040 can be completely destroyed by electrical faults. To understand these more fully please visit this link: Xiao Charging Situation

Microcontroller Implementation

Microcontroller Exploration & Datasheet Review

I reviewed the official Raspberry Pi RP2040 datasheet to verify how its registers map to the physical layout of the Seeed Studio XIAO footprint.

Core Architectural Features

  1. Processor Core:32-bit Dual-core ARM Cortex-M0+ processing at a native clock speed of 133MHz.
  2. On-Chip Memory:264KB of embedded SRAM divided into six independent banks. This provides the massive memory headroom required to host an on-chip Python runtime interpreter layer.
  3. Storage Execution:2MB of external Flash memory mapped through a high-speed QSPI interface cache block.

Hardware Registers & GPIO Control

  1. SIO Registers: The Single-cycle IO (SIO) block manages high-speed pin manipulation. The GPIO_OUT register drives logic states, and the GPIO_IN register samples incoming voltages.
  2. Direction Registers:The GPIO_OE (Output Enable) register controls whether a pin buffer drives an output or listens as an input.
  3. Pad Control:Individual internal registers handle pull-up or pull-down configuration, allowing me to enable software-defined resistors without hardware modifications.

What I took away from learning about toolchains and develpment workflows

The RP2040's mass storage upload mechanism (drag-and-drop UF2) is convenient for CircuitPython but Arduino IDE's direct COM port upload is faster for iterative testing. For my final project on the nRF52840 which requires BLE, SD card, and display libraries, Arduino IDE will be what I work with to complete the program. I made the decision to use Arduino for this week's individual work to build that muscle memory now.

Connection to My Final Project

My final project is a two-part wearable journaling system: a handheld console running a prompted journal loaded from an SD card, and a fabric wristband that delivers haptic feedback to the user via a vibration motor. The wristband communicates with the console over BLE using a Seeed Studio XIAO nRF52840. This week is allowing me to begin prototyping the embeded workflows necessary to pull my final project off.

Individual Workflow

Imagining the System Hardware Configuration

I configured a local input device, local output device, and telemetry communication connection using the XIAO RP2040 Pinout Map:

  1. Local Input Device:A tactile push button wired between physical board pin D0 and Ground (GND).
  2. Local Output Device:A motor that will serve as haptic feedback for users
  3. Communication Interface:Currently, Arduino IDE via USBc

Drawing The Schematic to Make a PCB

KiCad

After creating a new project in KiCad, I placed symbols for the XIAO RP2040. Since KiCad does not natively store this symbol I did have to download the Seeed Studio XIAO Series library and the associated footprint libraries etc in order to complete this PCB. After loading the library I:

KiCad Symbols Placed

Working towards getting 0 errors in the ERC

This was an extremely tedious step.

Running the Electrical Rules Checker revealed two issues I had to work through. First, the diode wasn't annotated yet. This was a pretty easy fix. I ran the annotate schematic function under the tools in KiCad to auto-number everything

Next I was getting errors because I forgot to show that the pins I was not connecting to were not supposed to have any connections. I went back and added no-connect flags on all of the unused GPIO pins

The part that took me a bit of work was linking the actual XIAO RP2040 to the schematic as a symbol and as a footprint. The library that I download wasnt loading initially. I kept getting a foorprint library warning for U1 no matter how many times I reinstalled and closed the program and restarted it. Eventually I was able to instal the library through KiCad's plugin content manager after realizing that I was downloading the file to a folder that was not routing to the actual KiCad program file in my file explorer. After installation the ERC passed with 0 errors and 0 warnings.

ERC No Errors

PCB Layout, Placement, and Routing

I used the "PCB from Schematic" funcction to open the PCB editor and start moving things around in a way that I thought would most resemble placement for my eventual final project board

All of the footprints came in nowhere near where I wanted them so I took a few minutes to move them to spaces that I hoped would make sense when I run the DRC and actually start soldering later.

The ratsnest is extremely annoying, honestly, and really takes some patience to ensure that none of the wires are crossing and that components that have shared functionality (like the mosfe and resistor) are close to avoid making my board too chunky. Keeping in mind that this is eventually going to translate into a wearable, I'm really trying to keep space tight on the board.

Once I had the components placed I drew the board outline on the Edge Cuts layer with plans to round the corners out in Inkscape later. Personally, I love using inkscape to make corrections to trace width, pad width, and any other changes that will make the board function better. I was trained using inkscape years ago so I just prefer incorporating it for these steps.

Finally, I was ready to run the DRC (though I was EXTREMELY nervous), thankfully I had 0 errors.

KiCad PCB DRC no errors

Moving to Inkscape

I exported two separate SVG files and also a PDF (just because, I technically only needed the SVGs for Inkscape)

  1. F.CU only
  2. Edge.Cuts only

Iopened both files simultaneously in Inkscape and renamed pieces accordingly in the layers panel.

I like to go stroke by stroke between pads and combine them to make sure my traces are fully connecting. There are faster ways to do this but I feel like this helps me to ensure that I know for sure everything is connecting in the way I intended in KiCad.

Next I went in and changed the stroke widths from .200 to .300 (Previously I'd noticed that 200 is too thin for traces, in my opinion)

Then, I saved the whole thing as my trace layer to be routed on the SRM-20 through Mods CE. I like to keep the exterior cut in during the trace routing just in case I've made any spacing issues or If I can actually make the edge cut closer to the boarders of the pads and traces.

Finally, I hide everything except the edge cuts and save just the edge cut to be routed alone in the SRM-20

Keep in mind, I save these as plain SVGs NOT Inkscape SVGs for use in Mods CE

Inkscape Traces

Working Through Mods CE

I use Chrome to open and operate Mods CE. I do this because Neil mentioned it in a class once and I noticed it "behaves" better than using microsoft Edge. I'm not sure of the reasons why it moves more quickly and seemlessly but it does.

Using the Navigation Menu on the top left side, I go down to the SRM-20 and choose PCB milling

I cut my traces using a 1/64 flat endmill and I like to choose this before I even inport my file. I just would HATE to have the wrong tool chosen and break a bit because this setting was off.

Next, I go ahead and bring in my trace file. I double check that I have the correct bit chosen before moving to the next step (yes I double check this because I really don't want to waste any bits).

I make sure to set my machine X, Y, and Z to zero because I choose my XYZ height and location based on the spoilboard I am currently using. I use different spoil boards based on other projects that are simultaneously milling. I use the VPanel to make all of these adjustments live.

Finally I create the file and use the Mods' preview to make sure it's looking how I expect before exporting the file

MODS CE Example

Moving to Mill on the SRM 20

I already had a wax sacrificial board taped down to the work plane on the SRM-20 from a previous project. It's on there with a LOT of scotch double sided (permenant) tape. The top of the wax has already been surface planed to make sure it's flat. I double check using a small level that I keep handy thats usually used for woodworking

Once I have the file ready, I move back into Vpanel, load the output and start the cut.

The mills take a few minutes depending on the project. Once it's done I grab the vaccuum and remove all particles before starting the edge cut. I do this so I can examine the board and make any corrections before doing the edge cuts. I feel like this is important because I'd rather remill while it's still physically connected to the rest of the copper board than when it is shifting a little here and there because it's no longer connected to the rest of the well adheared copper board.

Milled before edgecut

Next, I moved on to the edge cut following the same procedure as abov; however, this time I double check that my 1/32 flat endmill is the bit that is chosen.

Keep in mind, before starting the milling process I had to change the bits out and rezero the Z axis ONLY to ensure I actually am in the exact same place as my original cut. I make sure to jog to the previous X/Y zero and then zero my Z because I've already removed the copper since my trace cut INCLUDES my edges. I only rezero from the very top of the copper if i've adjust the edge cut at all.

I actually use a sewing Pin to remove the board. I find a sewing pin is small enough to get inbetween the sides and also doest scratch the surface of the PCB as much as the flat head screwdriver I used to use.

Board Completely Milled

Soldering and Assembly

Order of Soldering

First, I wipe down the PCB with some isopropyl alcohol to make sure theres no residue on top that would prefent good soldering.

  1. 1N4148W diode
  2. AO3400 MOSFET
  3. 100 ohm resistor
  4. 2 pin motor header
  5. XIAO RP2040

I set my iron to 350 and went to work. It took me some time because I have some extremly tiny components and the joints arent gorgeous, but they have continuity.

Continuity Checks

Before plugging in anythign I made sure to check all traces and pads for continuity. I use a multimeter for this step and mine produces a beep when there is continuity

Components Soldered

Horrific Problems after Soldering (lol)

Not actually milled well

After milling, a continuity check revealed a beep between a signal trace and an adjacent XIAO pad. I pulled out a magnifing glass and noticed that a copper bridge from a trace routed too close to the pad.

I was extremely annoyed because i'd already removed the board and also connected pieces so I was immediately worried that I might accidently mess up a joint or component when trying to fix this. initially I considered using the dremel to clear it but decided that was probably too extreme. I grabbed an Xacto knife that has lost it's point (its square at the tip now) and slowly lightly scored it until I isolated the channel.

Lesson Learned

Increase clearances in KiCad before exporting.

Lifted copper pad on the diode

While soldering the 1N4148W diode I held the iron on the cathode pad too long and lifted the copper. The pad came up with the diode leg and i lost continuity completely in this area.

I grabbed some wire from what I'd clipped from the diode earlier to make it shorter and made a bridge. Thankfully this restored continuity; however, the beauty of my board was decreasing lol.

lesson learned

Keep the iron off of the board and move quickly when soldering.

TWO defetive tactile buttons

I wont name the brand but I tried two different 6mm tactile buttons as my loacal input devices. Both failed continuity testing. On both buttons all four legs beeped to eachother without pressing the button meaning the button was somehow permanently closed. Those were the only buttons I had that would fit the board

I had to take a break for lunch at this point because I was extremely annoyed at the thought of possibly having to stop for days to find a new button or create a completely new PCB. I was not trying to do either.

While eating it occured to me that this would be a great opportunity to go back to something I learned in Fabricademy a few weeks ago. A fabric based button.

I ended up making a velostat fabric pressure sensor as a buttton. This also works a lot better towards my final project concept as well. A win is a win. (I added the steps to this below)

Ghost PCB

Eventually I was ready to plug in the board to see if I could get something going through the IDE. Unfortunatley nothing appeard in my Device Manager.

After some trial and error I realized that I'd grabbed a USB c cable that goes to my portable charger and was not data capable.

i went and grabbed my iphone USBc charger and it immediately showed up as COM7 in the IDE

Input Device - Textile Pressure Button

My pivot to buiding a textile based pressure sensor was a better input device for my wearable for my final project. Despite arriving to this conclusion through a mountain of frustration, I'm glad about it.

What is velostat? Velostat is a pressure sensitive condiuctive material that provides resistance decreases as presure increase. I learned about this during Fabricademy. Check out my Fabricademy page for more info on how this works.

Materials

  1. Conductive Fabric
  2. Velostat
  3. Conductive Thread
Textile Pressure Button

Serial Output

I chose Arduino IDE over CircuitPython for this week because my final project runs on the Seeed Studio XIAO nRF52840, which requires Arduino for full BLE, SD card, and display library support. Starting with Arduino now builds the muscle memory I need for the final project rather than having to switch toolchains later.

Workflow

  1. Install the Raspberry Pi Pico/RP2040 by Earle Philhower board package via the Arduino Board manager
  2. From the drop down menu, I selected the Seeed Xiao RP2040 as the board and COM7 as the port. The board entered bootloader mode automatically when I clicked upload. Initially the board was blinking but the bliinking stoped and the sketch flashed succesfully.
      // ─────────────────────────────────────────────────────────────
      // Fab Academy Week 4 — Embedded Programming
      // Board: Seeed Studio XIAO RP2040
      // Toolchain: Arduino IDE + Raspberry Pi Pico/RP2040 board package
      // Input: Velostat fabric pressure sensor on A0
      // Output: DC coreless vibration motor via AO3400 MOSFET on D2
      // Communication: Bidirectional USB serial at 115200 baud
      // ─────────────────────────────────────────────────────────────
       
      const int buttonPin = A0;  // velostat pressure sensor — analog input
      const int motorPin  = D2;  // MOSFET gate — digital output
       
      // Threshold calibrated from live Serial Monitor testing:
      // At rest:  sensor reads 100–200 (high resistance, no pressure)
      // Pressed:  sensor reads as low as 5 (low resistance, pressure applied)
      // Threshold set to 20 — only genuine press triggers
      int threshold = 20;
       
      void setup() {
        Serial.begin(115200);
        pinMode(motorPin, OUTPUT);
        digitalWrite(motorPin, LOW);
        Serial.println("Week 4 ready");
      }
       
      void loop() {
        // Read analog pressure value from velostat sensor
        int pressure = analogRead(buttonPin);
        Serial.println(pressure);  // live telemetry for monitoring and calibration
       
        // Pressure DECREASES under compression (velostat behavior)
        // Trigger when value drops below threshold
        if (pressure < threshold) {
          Serial.println("NEXT");   // signal to computer application
          pulseMotor();             // haptic confirmation
          delay(1000);              // prevent repeat triggers while held
        }
       
        // Listen for incoming serial command from computer
        if (Serial.available()) {
          String cmd = Serial.readStringUntil('\n');
          cmd.trim();
          if (cmd == "MOTOR_ON") {
            pulseMotor();
            Serial.println("MOTOR_DONE");  // confirm completion to computer
          }
        }
       
        delay(50);  // 50ms loop tick
      }
       
      // Motor pulse function — fires motor for 500ms
      void pulseMotor() {
        digitalWrite(motorPin, HIGH);
        delay(500);
        digitalWrite(motorPin, LOW);
      }
      
IDE with code

Testing and Calibration

I opened the Arduino Serial Monitor at 115200 baud and watched the live pressure values stream. At rest with no pressure on the sensor, values fluctuated between 100 and 200. When I pressed the velostat sandwich firmly together, the value dropped as low as 5. Within the code, I set the threshold to 20 which is below the resting noise floor and well above the pressed value.

Action on PCB Serial Monitor Showing Real World Result
At rest (no pressure) 100-200 Nothing
Pressed Firmly 5-15 Next Shows on Screen, Motor Activates

Bidirectional Communication Verification

With the Arduino Serial Monitor still at at 115200 baud and watched the live pressure values stream. At rest with no pressure on the sensor, values fluctuated between 100 and 200.

Action on PCB Serial Monitor Showing Real World Result
Sitting on Table 100-200 Nothing
typed "MOTOR_ON" NO numbers, the words MOTOR_DONE The motor activates then once it stops, the words MOTOR_DONE shows on the screen

Video

Reflection

What I learned this week

I had never thought about the difference between interaction and communication as separate requirements before. Running both in the same loop (the motor buzzing and serial printing in sync) made the connection very clear. The board is doing two distinct things at once: physically affecting the world and transmitting data about it.

Why the vibrating motor matters for my final project

My final project relies on haptice feedback for users. This motor functioning is a highlight of the entire two piece final project.

What I would do differently

Check my components before starting anything in KiCAD

Files

File Description
Wearable Wristband — Inkscape Traces IDE Code - Motor controller for SEEED Xiao RP2040