10. Output Devices¶
Assignment for Week10:¶
Group assignment:¶
- Measure the power consumption of an output device
Individual assignment:¶
- Add an output device to a microcontroller board you’ve designed, and program it to do something
Group Assignment Highlight:¶
Measuring Output Device Power Consumption
1. Lock-Style Solenoid (12V DC)
- Specs: 12V, 650mA (7.8W theoretical).
- Measurement: Oscilloscope showed 10.52W (12V, 0.86A) in practice due to startup surge.
- Demonstrated unlocking at 12V.
2. Mini Water Pump (6V DC)
- Specs: 6V, 220mA (1.32W theoretical).
- Measurement: Actual draw: 2.8W (5V, 0.56A) — higher due to load and efficiency losses.
- Showed water flow under power.
3. MG996R Servo Motor
- Specs: 5V, 2.5A stall current, 180° rotation.
- Test:
- Arduino code swept servo 0°→180°→0° (10ms/step).
- Power not measured but noted high current spikes during motion.
Key Takeaways¶
- Real-world power often exceeds theoretical values (startup surges, load effects).
- Oscilloscopes/multimeters are critical for validating specs.
- Servos/solenoids need robust power supplies to handle peak currents.
- Tools Used: Oscilloscope, Multimeter, Arduino, 12V/5V Power Supplies.
Here is the link to the group Assignment.
Individual assignment:¶
For this week, I decided to use neopixels and speaker as I will be using them in my final project.
I used my cutom XIAO Board with output devices.
i. Neopixels
The WS2812B is a popular individually addressable RGB LED strip.
WS2812B LED Strip Specifications:
- LED Type: WS2812B (Integrated RGB LED + Driver IC)
- Voltage: DC 5V
- Addressability: Individually addressable (Each LED can be controlled separately)
- PCB Color: White
- Protection Rating: IP30 (Non-waterproof, suitable for indoor use)
- LED Density: 60 LEDs/meter
- Some strips come in 30, 60, 96, or 144 LEDs/meter
- Color Depth: 24-bit (8 bits per color: R, G, B → 16.7 million colors)
- Data Protocol: Single-wire control (Uses a DIN input for data signal)
- Refresh Rate: 800Hz
- Power Consumption*:
- ~0.3W per LED at full brightness (white)
- ~60mA per LED
Programming neopixels
I found out that Neopixel requires a 5V power supply with sufficient current specially when driving a longer neopixels strips. I used external power supply how to I can drive long neopixel strip.
The library I used to program neopixel is Adafruit NeoPixel by Adafruit.
I tried to test the program that I wrote and simulated in embedded programming week. But a few random pixels were glowing but my program is not running correctly.
Then I used Arduino Uno to test one the examples program and my own program and it worked correctly and smoothly.
This is the example code I tested above. I changed the pin number to 6 and the number of neopixels to 121.
// A basic everyday NeoPixel strip test program.
// NEOPIXEL BEST PRACTICES for most reliable operation:
// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
// connect GROUND (-) first, then +, then data.
// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
// a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
// (Skipping these may work OK on your workbench but can fail in the field)
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN 6
// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 121
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
// setup() function -- runs once at startup --------------------------------
void setup() {
// These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
// Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
// END of Trinket-specific code.
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
}
// loop() function -- runs repeatedly as long as board is on ---------------
void loop() {
// Fill along the length of the strip in various colors...
colorWipe(strip.Color(255, 0, 0), 50); // Red
colorWipe(strip.Color( 0, 255, 0), 50); // Green
colorWipe(strip.Color( 0, 0, 255), 50); // Blue
// Do a theater marquee effect in various colors...
theaterChase(strip.Color(127, 127, 127), 50); // White, half brightness
theaterChase(strip.Color(127, 0, 0), 50); // Red, half brightness
theaterChase(strip.Color( 0, 0, 127), 50); // Blue, half brightness
rainbow(10); // Flowing rainbow cycle along the whole strip
theaterChaseRainbow(50); // Rainbow-enhanced theaterChase variant
}
// Some functions of our own for creating animated effects -----------------
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
for(int a=0; a<10; a++) { // Repeat 10 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this loop:
for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
// strip.rainbow() can take a single argument (first pixel hue) or
// optionally a few extras: number of rainbow repetitions (default 1),
// saturation and value (brightness) (both 0-255, similar to the
// ColorHSV() function, default 255), and a true/false flag for whether
// to apply gamma correction to provide 'truer' colors (default true).
strip.rainbow(firstPixelHue);
// Above line is equivalent to:
// strip.rainbow(firstPixelHue, 1, 255, 255, true);
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for(int a=0; a<30; a++) { // Repeat 30 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / strip.numPixels();
uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
Running the program that I wrote in Embedded programming week. You can find the code below.
Finally I realize the neopixel and xiao have different logic level ( Neopixel:5V, Xioa RP2040: 3.3V).
I then tested the same program I wrote in embedded programming week on 4 neopixels with my xioa and it worked correctly but when I tried on longer neopixels strip, it was again not working.
This is same code tested using Arduino Uno above.
#include <Adafruit_NeoPixel.h>
// Define the number of NeoPixels and the pin they are connected to
#define NUM_PIXELS 24
#define PIN 8
// Initialize the NeoPixel object
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, PIN, NEO_GRB + NEO_KHZ800);
// Define colors
uint32_t RED = pixels.Color(255, 0, 0); // Red
uint32_t BLUE = pixels.Color(0, 0, 255); // Blue
uint32_t GREEN = pixels.Color(0, 255, 0); // Green
// Function to clear all pixels
void clearPixels() {
for (int i = 0; i < NUM_PIXELS; i++) {
pixels.setPixelColor(i, 0); // Turn off all pixels
}
pixels.show();
}
// Function to smoothly blink all pixels with a specific color
void blinkColor(uint32_t color, int blinkDuration) {
// Fade in
for (int brightness = 0; brightness <= 255;brightness += 50) {
for (int i = -1; i < NUM_PIXELS; i++) {
pixels.setPixelColor(i, pixels.Color(
(color >> 16) & 0xFF, // Red component
(color >> 8) & 0xFF, // Green component
color & 0xFF // Blue component
));
pixels.setBrightness(brightness); // Adjust brightness
}
pixels.show();
delay(blinkDuration); // Smooth transition
}
}
// Function to turn on all pixel one by one
void snakeEffect(uint32_t color, int rounds) {
for (int r = 0; r < rounds; r++) { // Run for the specified number of rounds
for (int i = 0; i < NUM_PIXELS; i++) {
// Turn on the current pixel
pixels.setPixelColor(i, color);
// Update the NeoPixels
pixels.show();
// Wait a bit before moving to the next pixel
delay(100);
}
}
}
// Function to turn off all pixels one by one
void snakeEffectOff(uint32_t color, int rounds) {
for (int r = 0; r < rounds; r++) { // Run for the specified number of rounds
for (int i = 0; i < NUM_PIXELS; i++) {
// Turn off the current pixel
pixels.setPixelColor(i, 0);
// Update the NeoPixels
pixels.show();
// Wait a bit before moving to the next pixel
delay(100);
}
}
}
void setup() {
// Initialize the NeoPixel library
pixels.begin();
clearPixels(); // Ensure all pixels are off at the start
}
bool sequenceCompleted = false; // Flag to track if the sequence is done
void loop() {
if (!sequenceCompleted) {
// Blink red, blue, and green in sequence
snakeEffect(RED, 1);
clearPixels(); // Clear pixels after the red snake
blinkColor(RED, 300); // Blink red for 0.3 seconds
blinkColor(BLUE, 300); // Blink blue for 0.3 seconds
blinkColor(GREEN, 300); // Blink green for 0.3 seconds
snakeEffectOff(GREEN, 1);
clearPixels(); // Clear pixels after the green snake
sequenceCompleted = true; // Set the flag to true after the sequence
clearPixels(); // Ensure all pixels are off at the start
}
}
I think I have to use a logic shifter as I will be using longer neopixel strip in my final project.
Notes - 3.3V logic works for data line but only when driving a few numbers of neopixels. I observed that the xioa RP2040 could not drive longer neopixels strips even with external power supply provided to the neopixels potentially because of xiao’s logic level which is 3.3V. It is interesting to note that xiao can actually drive a few numbers of neopixels without shifting its logic to 5V.
ii. DFPlayer Mini and Speaker
I went through this site to check and note some details of DFPlayer Mini.
The DFPlayer Mini MP3 Player is a small and low cost MP3 module with an simplified output directly to the speaker. The module can be used as a stand alone module with attached battery, speaker and push buttons or used in combination with microcontrollers such as Arduino, ESP32, Raspberry Pi and any microcontrollers with Uart.
Pinout Diagram of DFPlayer Mini
Sepcifications of DFPlayer Mini
- Sampling rates (kHz): 8/11.025/12/16/22.05/24/32/44.1/48
- 24 -bit DAC output, support for dynamic range 90dB , SNR support 85dB
- Fully supports FAT16 , FAT32 file system, maximum support 32G of the TF card, support 32G of U disk, 64M bytes NORFLASH
- A variety of control modes, I/O control mode, serial mode, AD button control mode
- advertising sound waiting function, the music can be suspended. - when advertising is over in the music continue to play audio data sorted by folder, supports up to 100 folders, every folder can hold up to 255 songs
- 30 level adjustable volume, 6 -level EQ adjustable
***Speaker 8ohm 0.25W ***
This is a general-purpose 8 Ohm 0.25 Watt Speaker which is generally used for audio projects. It has a balanced frequency response and high efficiency. Very good high-range response. This is a transducer that converts electromagnetic energy into sound waves. It receives analog or digital inputs from computers or audio players and converts them into sound waves. The speaker has a resistance of 8 ohms and a power rating equal to 0.25W.
Specification
Testing the DFPlayer Mini and the speaker
-
I want to try it on Arduino Uno before I do it with my board because there is not much of a reference for xiao RP2040 and DFPlayer Mini.
-
I also prepared a SD card which will go inside DFPlayer Mini. The SD card must be formatted in FAT16 or FAT32 format and I also put some music in the SD Card.
-
Now I made circuit connection using Arduino Uno, DFPlayer Mini and the speaker reffering to this link.
-
I then go the Arduino IDE and install DFPlayer Mini library called DFRobotDFPlayerMini by DFRobot. And took one example code form that library, made the necessary pin number changes.
-
This is the example code I tried.
/***************************************************
DFPlayer - A Mini MP3 Player For Arduino
<https://www.dfrobot.com/product-1121.html>
***************************************************
This example shows the basic function of library for DFPlayer.
Created 2016-12-07
By [Angelo qiao](Angelo.qiao@dfrobot.com)
GNU Lesser General Public License.
See <http://www.gnu.org/licenses/> for details.
All above must be included in any redistribution
****************************************************/
/***********Notice and Trouble shooting***************
1.Connection and Diagram can be found here
<https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299#Connection_Diagram>
2.This code is tested on Arduino Uno, Leonardo, Mega boards.
****************************************************/
#include "Arduino.h"
#include "DFRobotDFPlayerMini.h"
#if (defined(ARDUINO_AVR_UNO) || defined(ESP8266)) // Using a soft serial port
#include <SoftwareSerial.h>
SoftwareSerial softSerial(/*rx =*/10, /*tx =*/11);
#define FPSerial softSerial
#else
#define FPSerial Serial1
#endif
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);
void setup()
{
#if (defined ESP32)
FPSerial.begin(9600, SERIAL_8N1, /*rx =*/D3, /*tx =*/D2);
#else
FPSerial.begin(9600);
#endif
Serial.begin(115200);
Serial.println();
Serial.println(F("DFRobot DFPlayer Mini Demo"));
Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
if (!myDFPlayer.begin(FPSerial, /*isACK = */true, /*doReset = */true)) { //Use serial to communicate with mp3.
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
while(true){
delay(0); // Code to compatible with ESP8266 watch dog.
}
}
Serial.println(F("DFPlayer Mini online."));
myDFPlayer.volume(10); //Set volume value. From 0 to 30
myDFPlayer.play(1); //Play the first mp3
}
void loop()
{
static unsigned long timer = millis();
if (millis() - timer > 3000) {
timer = millis();
myDFPlayer.next(); //Play next mp3 every 3 second.
}
if (myDFPlayer.available()) {
printDetail(myDFPlayer.readType(), myDFPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states.
}
}
void printDetail(uint8_t type, int value){
switch (type) {
case TimeOut:
Serial.println(F("Time Out!"));
break;
case WrongStack:
Serial.println(F("Stack Wrong!"));
break;
case DFPlayerCardInserted:
Serial.println(F("Card Inserted!"));
break;
case DFPlayerCardRemoved:
Serial.println(F("Card Removed!"));
break;
case DFPlayerCardOnline:
Serial.println(F("Card Online!"));
break;
case DFPlayerUSBInserted:
Serial.println("USB Inserted!");
break;
case DFPlayerUSBRemoved:
Serial.println("USB Removed!");
break;
case DFPlayerPlayFinished:
Serial.print(F("Number:"));
Serial.print(value);
Serial.println(F(" Play Finished!"));
break;
case DFPlayerError:
Serial.print(F("DFPlayerError:"));
switch (value) {
case Busy:
Serial.println(F("Card not found"));
break;
case Sleeping:
Serial.println(F("Sleeping"));
break;
case SerialWrongStack:
Serial.println(F("Get Wrong Stack"));
break;
case CheckSumNotMatch:
Serial.println(F("Check Sum Not Match"));
break;
case FileIndexOut:
Serial.println(F("File Index Out of Bound"));
break;
case FileMismatch:
Serial.println(F("Cannot Find File"));
break;
case Advertise:
Serial.println(F("In Advertise"));
break;
default:
break;
}
break;
default:
break;
}
}
-
But it didn’t work and I was so frustrated only to realize that the SD card that I used was broken and was not recognized by the DFPlayer Mini.
-
I got a new SD card from our lab and it worked. So I directly switch up the Arduino Uno to my xioa board and made the circuit. I tried using that example code on xiao as well but it didn’t work. So I edited the example code and use software serial library to convert D3 and D4 of Xiao to RX and TX which are required for communication with the DFPlayer.
-
After tweaking the code for a quite sometime, it worked with my xiao board. I learned that I can not really use the D6(TX) and D7(RX) as those seems gets utilized by the USB when we upload the code and it gets utilized by the serial monitor if your are using it in your program.
-
Here is the video of DFPlayer and speaker working happily with my Xiao board.
- This is the code used above.
// This is a program for Xioa RP2040 and DFPlayer Mini.
// DFPlayer Mini XIAO RP2040
// RX D4 (TX)
// TX D3 (RX)
// VCC 5V
// GND GND
#include <DFRobotDFPlayerMini.h>
#include <SoftwareSerial.h>
// Initialize software serial on pins D3 (RX) and D4 (TX)
SoftwareSerial mySerial(D3, D4);
// Create DFPlayer object
DFRobotDFPlayerMini myDFPlayer;
void setup() {
Serial.begin(115200); // Start hardware serial for debugging
mySerial.begin(9600); // Start software serial for DFPlayer
Serial.println(F("Initializing DFPlayer..."));
// Initialize DFPlayer
if (!myDFPlayer.begin(mySerial)) {
Serial.println(F("DFPlayer initialization failed!"));
Serial.println(F("1. Check RX/TX connections (must be crossed)"));
Serial.println(F("2. Insert SD card with MP3 files"));
while(true); // Halt if initialization fails
}
Serial.println(F("DFPlayer Mini ready!"));
myDFPlayer.volume(30); // Set volume (0-30)
// Play tracks in sequence
myDFPlayer.play(1); // Play track 0001.mp3
delay(5000); // Wait 5 seconds
myDFPlayer.play(2); // Play track 0002.mp3
delay(5000);
myDFPlayer.play(3); // Play track 0003.mp3
}
void loop() {
// Nothing needed here for basic playback
}
Program Files¶
i. Example Program for Addressable RGB Leds with Arduino Uno.
ii. My Program file for Addressable RGB Leds with XiaoRP2040.
iii. Example Program file for DFPlayer Mini with Arduinoi Uno .