Week 10 – Output Devices

Fab Academy – Week 10
Date range: 25 - 31 Mar
Instructor: Neil Gershenfeld
🧠 Learning Objectives
- Demonstrate workflows used in controlling an output device(s) with MCU board you have designed.
📋 Assignments
Individual Assignment
- Add an output device to a microcontroller board you've designed and program it to do something.
Group Assignment
- Measure the power consumption of an output device.
- Document your work on the group work page and reflect on your individual page what you learned.
🛠️ Tools & Materials
- Software (Fusion360 for Electronics, Arduino IDE)
- Machines
- Materials (Circuit from last week, WS2813 LED strip, 100 μF capacitor, 330Ω resistor)
👥 Group Assignment
The objective of the group assignment was to read and understand the output of an output device. For this assignment we tested a digital output on the oscilloscope for a fading led using an Arduino UNO.

The code used to fade the led in and out:
/*
Fade
This example shows how to fade an LED on pin 9 using the analogWrite()
function.
The analogWrite() function uses PWM, so if you want to change the pin you're
using, be sure to use another PWM capable pin. On most Arduino, the PWM pins
are identified with a "~" sign, like ~3, ~5, ~6, ~9, ~10 and ~11.
This example code is in the public domain.
https://docs.arduino.cc/built-in-examples/basics/Fade/
*/
int led = 9; // the PWM pin the LED is attached to
int brightness = 0; // how bright the LED is
int fadeAmount = 5; // how many points to fade the LED by
// the setup routine runs once when you press reset:
void setup() {
// declare pin 9 to be an output:
pinMode(led, OUTPUT);
}
// the loop routine runs over and over again forever:
void loop() {
// set the brightness of pin 9:
analogWrite(led, brightness);
// change the brightness for next time through the loop:
brightness = brightness + fadeAmount;
// reverse the direction of the fading at the ends of the fade:
if (brightness <= 0 || brightness >= 255) {
fadeAmount = -fadeAmount;
}
// wait for 30 milliseconds to see the dimming effect
delay(30);
}
We then connected the oscilloscope’s wires in place of the led on the Arduino to see the signal playing:
[video]
We measured the power consumption of the Arduino Fade example to determine how much power is consumed by the LED as its brightness changes using PWM (Pulse Width Modulation).
The Arduino Fade example uses PWM to control LED brightness. Instead of reducing voltage, PWM rapidly switches the LED on and off (like a very fast light switch), this switching happens so fast our eyes can’t see it, but as PWM changes how long the LED stays ON or OFF, we can see the fading effect (50% duty cycle =ON half the time, OFF half the time → medium brightness).

Using PWM allow us to to output analog behavior using digital output as most Microcontrollers don’t have analog output pins.
So what matters for us when measuring power consumption is:
- The LED is not actually getting less voltage
- It’s getting full voltage, but for shorter time
As a result of this:
- The current measured by the multimeter represents the average current.
- Higher PWM values correspond to longer ON times, increasing average current and power.
We observed that power consumption increases proportionally with brightness.
1. Measuring Current
We connected the multimeter in series with the LED circuit to measure the current flowing through it.
[picture goes here]
Connection setup:
Arduino pin → Resistor → Multimeter → LED → GND
This allowed us to measure the LED current (( I_{LED} )).
2. Measuring Voltage
We then used the multimeter in voltage mode to measure the voltage drop across the LED terminals (( V_{LED} )).
3. Calculating Power Consumption
We calculated the LED power consumption using the formula:
( P ) = power (Watts) = ( V ) = voltage across the LED (Volts) x ( I ) = current through the LED (Amperes)
The result is: (?????)
🧪 Process & Workflow
Step 1 – Studying the output device
For this week’s assignment I’m interfacing a 10-LED WS2813 addressable strip, which I will be using for my final project. Hopefully this week’s assignment will help me understand using the LED strip and its power and data requirements.
Unlike standard LEDs, addressable LEDs require a specific data protocol to control each pixel's color and brightness individually over a single data line. I used the AdaFruit NeoPixel library to manage the light signal required for the WS2813 chips.
The WS2813 is different from the WS2812 as it has 4 lines instead of 3:
- +5V (Power) – supplies voltage
- GND (Ground) – common ground
- DI (Data In) – main control signal
- BI (Backup Data In) – backup signal line (keeps LEDs working if one fails) this is the advantage over the WS2812
Power: 9W/m (30LED/m)

I am using my circuit from last week, where I connected a piezo sensor. In this week I am taking that assignment a step further, instead of using the small led as an indicator for the single and double tap, I want to send a wave through the LED strip.
Step 2 – The Circuit
I am expanding on my flexible circuit from last week which I’ve already added a 3 pin header to (I originally thought I had WS2812b), so I can connect the LED strip to.

I didn't get enough time to solder this week, so I will be using a breadboard to continue the assignment (I’ll update later when I solder)
The Connections:
- Data In (DI) to D2 (via a 330Ω resistor).
- Backup In (BI) to GND (Required for WS2813 functionality).
- A 100μF capacitor placed across the 5V/GND to prevent voltage spikes from damaging the LEDs.
- I initially connected the +5V of the WS2813 to the Xiao SAMD21’s 5v output, then to the 3.3 (explained down)

On the messy breadboard:

I asked Gemini to generate the code with this prompt:
I want to generate a code for controlling a WS2813 LED Strip. So that when I tap a piezo once one color wave will be sent to the LED strip and when double taps is detected a different color will be sent. I am using Piezo as input on A0, WS2813 on pin D2, I have 27 led but I only need to test on 10 as I’m currently connecting WS2813 on the 3.3 v pin on the Xiao SAMD21.
The code:
#include <Adafruit_NeoPixel.h>
// --- Hardware Pins ---
#define PIEZO_PIN A0
#define STRIP_PIN D2
#define NUM_LEDS 27
#define TEST_LEDS 10 // Safety limit for 3.3V
#define BRIGHTNESS 45
// --- Logic Constants ---
#define THRESHOLD 250
#define TAP_WINDOW 350
#define DEBOUNCE 50
#define OFF_DELAY 3000 // 3 seconds of silence before total "Off"
Adafruit_NeoPixel strip(NUM_LEDS, STRIP_PIN, NEO_GRB + NEO_KHZ800);
// --- Animation State ---
enum AnimMode { NONE, SINGLE_WAVE, DOUBLE_WAVE };
AnimMode currentAnim = NONE;
unsigned long lastTapTime = 0;
unsigned long animFinishTime = 0; // When the wave reached the end
int tapCount = 0;
float head = -1.0;
unsigned long lastUpdate = 0;
bool isShowing = false; // Tracks if the strip is currently active
void setup() {
pinMode(PIEZO_PIN, INPUT);
strip.begin();
strip.setBrightness(BRIGHTNESS);
strip.clear();
strip.show();
}
void loop() {
checkInput();
// Run animations at ~50 FPS
if (millis() - lastUpdate > 20) {
runAnimations();
lastUpdate = millis();
}
// AUTO-OFF LOGIC:
// If an animation just finished and 3 seconds have passed without a new tap...
if (!isShowing && currentAnim == NONE && (millis() - animFinishTime > OFF_DELAY)) {
strip.clear();
strip.show();
}
}
void checkInput() {
int val = analogRead(PIEZO_PIN);
unsigned long now = millis();
if (val > THRESHOLD && (now - lastTapTime) > DEBOUNCE) {
tapCount++;
lastTapTime = now;
isShowing = true; // We are officially "active"
}
if (tapCount > 0 && (now - lastTapTime) > TAP_WINDOW) {
head = 0;
if (tapCount == 1) currentAnim = SINGLE_WAVE;
else currentAnim = DOUBLE_WAVE;
tapCount = 0;
}
}
void runAnimations() {
if (currentAnim == NONE) return;
// Apply a smooth fade
for (int i = 0; i < NUM_LEDS; i++) {
uint32_t c = strip.getPixelColor(i);
uint8_t r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c;
strip.setPixelColor(i, strip.Color(r * 0.75, g * 0.75, b * 0.75));
}
if (currentAnim == SINGLE_WAVE) {
if (head < TEST_LEDS) {
strip.setPixelColor((int)head, strip.Color(255, 20, 147)); // Pink
head += 0.5;
} else {
finishAnimation();
}
}
else if (currentAnim == DOUBLE_WAVE) {
if (head < TEST_LEDS) {
// Rainbow effect for double tap
uint32_t rainbow = strip.gamma32(strip.ColorHSV((uint16_t)(head * 6500)));
strip.setPixelColor((int)head, rainbow);
head += 0.7;
} else {
finishAnimation();
}
}
strip.show();
}
void finishAnimation() {
currentAnim = NONE;
head = -1;
isShowing = false;
animFinishTime = millis(); // Start the 3-second countdown
}
Step 3 – Testing
After uploading the code, the LED strip was acting up, one time only one led was on, sometimes nothing happens. At first, I thought the LED was faulty, but upon investigation and iterative testing I learned with help from Gemini:
The Logic Voltage Gap (3.3V vs. 5.0V): The most significant discovery was the mismatch between the XIAO’s 3.3V logic and the WS2813’s 5V power.
- The Physics: Addressable LEDs typically require a "High" data signal around 70% of their VCC.
- The Math: 0.7×5.0V=3.5V.
- The Conflict: The XIAO’s maximum output is 3.3V, which falls below the 3.5V threshold required by the strip. The first LED output simply could not "see" the signal.
To verify the logic gap theory, I temporarily moved the strip's power wire from the 5V pin to the 3.3V pin on the XIAO.
- Result: The strip immediately began responding to the code.
- Reasoning: At 3.3V power, the strip’s threshold drops to ≈2.3V. The XIAO’s 3.3V signal was now "tall enough" to cross the logic fence.
- Caution: I had to limit the number of LEDs to 10 and lower the brightness to avoid overloading the XIAO’s 3.3V regulator and protect the microcontroller from heating up.
In real setups, the proper fix was to use a logic level shifter (3.3V → 5V), or I could’ve used a diode (e.g. 1N400x) in series with the strip’s VCC which drops ~0.6–0.7V → effectively getting to ~4.3V.
⚠️ Problems & Solutions
- Nothing was lighting up, because I was connecting to a 5v but because the xiao samd21 wasn’t able to supply that voltage, I moved it to 3.3 and limited the brightness and number of LEDs.
- I accidently soldered the header pins to the DO and BO side of the strip, which will not work because data moves from DI to DO between LEDs, I realized that mistake during wiring so I soldered another header pins to the correct side.
🧩 Files
- Design files
- Downloads
📝 Reflection
- What you learned
- What you'd improve