Week 10: Output Devices

This week, the task was to add an output device to a microcontroller board.

Group assignment

Week 10

Introduction

In the architecture of embedded systems, an output device functions as a transducer that converts electrical energy from a microcontroller into a measurable physical phenomenon. While the microcontroller serves as the central processing unit, these peripheral components act as the system's effectors, translating binary logic into mechanical motion, electromagnetic radiation, or acoustic waves.

This week I’ll use the last week PCB with all the XIAO’s inputs and outputs, because I didn’t include them on the week 8 board. For the outputs, I will use two OLED displays of different sizes and Neopixels, because I want to see a visual response to my inputs from last week in order to observe how quickly the outputs respond.

Types of signals

Digital Signals: A digital signal is a type of signal that represents data as a sequence of discrete, distinct values—typically binary 0s and 1s—rather than a continuous waveform. These signals are implemented as fixed-width electrical or light pulses (0V or 5V), creating a square wave pattern that offers high noise immunity and better data integrity than analog signals.

Analog Signals: An analog signal is a continuous, time-varying electrical, mechanical, or physical signal that represents information by varying its amplitude, frequency, or phase. Unlike digital signals, which use discrete binary values (0s and 1s), analog signals can take on an infinite range of values, making them ideal for representing natural phenomena like sound, temperature, and light.

Type of communications

Communications

Low-Level Communication Methods

These methods are based on electrical signals and are typically used for direct control or simple data transfer between devices:

GPIO (Digital Communication)

Uses HIGH/LOW signals. While simple, it can still represent control logic between a main controller and peripheral devices.

PWM (Pulse Width Modulation)

Encodes information in the duty cycle of a signal. Although commonly used for control (like LED brightness or motor speed), it can also be interpreted as a communication method where the peripheral device decodes signal timing.

DAC (Digital-to-Analog Conversion)

Produces variable voltage levels. This can act as a form of analog communication where continuous signals represent data between devices.

Protocol-Based Communication

These methods define rules for data exchange and are designed for reliable communication between multiple devices:

I²C (Inter-Integrated Circuit)

Two-wire protocol (SDA, SCL) that allows multiple devices using addressing.

  • One main device + multiple peripherals
SPI (Serial Peripheral Interface)

High-speed communication with dedicated lines (MOSI, MISO, SCK, CS).

  • One main device + multiple peripherals (each with select line)
UART (Serial Communication)

Asynchronous communication using TX/RX.

  • Point-to-point (main ↔ peripheral)

Networking Communication (IoT & Wireless)

Beyond local communication, embedded systems can connect through networking technologies, enabling distributed systems:

WiFi

Enables devices to connect to local networks or the internet. Ideal for IoT systems with cloud integration.

Bluetooth / BLE

Short-range communication, commonly used for direct interaction with smartphones.

MQTT (Message Queuing Telemetry Transport)

Lightweight publish/subscribe protocol. Devices communicate through a broker, allowing scalable and decoupled systems.

HTTP/HTTPS

Request-response model used for web services and APIs.

WebSockets (WS/WSS)

Enables real-time bidirectional communication between clients and devices.

Potential Formulas

P = V × I

This equation calculates power consumption, where P is power (watts), V is voltage (volts), and I is current (amperes). It is essential for determining how much energy an output device will consume.

P = I² × R

This form is useful for calculating power dissipation in resistive elements, such as current limiting resistors, ensuring they can handle the thermal load safely.

I = V / R

Derived from Ohm’s Law, this formula is used to calculate the current flowing through a device such as an LED, allowing proper resistor selection to avoid damage.

PCB

Schematic

Fab

XIAO INPUTS AND OUTPUTS. For the Xiao, I'll just add pins for its inputs and outputs, and I'll also add pins for external power supply (3.3V, 5V, and GND).

NeoPixels. I connected them to D3 on my Xiao using a resistor and a label, then connected the LEDs in series and the first power pin to a capacitor of 100 nF for component safety. Finally, I left some holes so that more LEDs could be added if desired.

Display. For the display output I just added the corresponding pins to connect it later and a decoupling capacitor 10µF to 3.3V.

PCB design

Fab

1. Then I went to the PCB editor and with the Route single track I connected every component.

Calculator tool. Before defining the size, it is important to calculate it using the calculator tool given by KiCad. To do that we first have to go to the start menu and open the Calculator tool.
Then add the Current (I) and the Temperature rise we are expecting our PCB to have and look fo the result the calculator will give back to us in the right top side. The calculator works by using a formula explained at the bottom.
Fab Fab
Track thickness. To change the track thickness we must go to the top tool section and click on Track use netclass width. Subsequently, select Edit Pre-defined Sizes.
Inside that section we can add tracks sizes by clicking the + symbol located at the bottom of the window, and in the width section we can change the width of the new track we added. Then we'll just have to click Ok.
Fab Fab

MODS

This are the parameters for each process in Mods. If you want to learn more go to Week 8.

Parameters.
The outline width is 2 mm and its layer is Edge.Cuts.
The track’s width is 0.8 mm- 2 mm and its layer is F.Cu.
The Holes layer is User.1.
Drilling - MODS. • Tool width. 0.8 mm
• Speed. 0.5 mm/s
• Origin (x,y,z). (0,0,0)
• Offset number. 1
Cutting - MODS. • Tool width. 0.39 mm
• Speed. 4 mm/s
• Origin (x,y,z). (0,0,0)
•Offset number. 3
Outline - MODS. • Tool width. 2 mm
• Speed. 4 mm/s
• Origin (x,y,z). (0,0,0)
• Offset number. 1

Results

Fab

VPANEL

VPanel for the Roland DG Corporation SRM-20 is the dedicated, user-friendly computer software interface used to operate, control, and monitor the desktop milling machine. It acts as a virtual on-screen panel, enabling users to set the milling origin (XYZ base point), adjust feed rates and spindle speeds, and pause/resume jobs.

CS

Before Cutting

Fab Fab

1. First, we have to paste the tape in the back of the copper board.

2. Then, we have to paste the copper board to the Sacrifice Bed.

Materials:

Copper Board.

double-sided tape.

Sacrifice Bed. Is an MDF board designed to prevent the SMR-20 from being damaged in the event that the tool drills too deep.

Before Cutting

Fab

3. Subsequently, we have to place the bed inside the SMR-20. In my case, in my lab, our SMR-20 has a fitting to secure the sacrifice table with screws.

Before Cutting

4. Having secured the bed inside the SMR-20, we have to select the tool for each milling process (Holes, Tracks and Borders).

Fab

Drilling Tool. This tool is specifically for perforations because of its shape and width. To use it, we must set the speed between 0.1 and 0.5 in order to don't damage it.

Fab

Cutting tool. This tool is specifically designed for traces, as its point is sharp and very thin. The Speed of use can be higher but we must be careful about its deep.

Fab

Border tool. This tool is can be used for the border cutting because of its width, it can also be used for perforation, but the diameter of them will be bigger.

Cutting

Fab

1. We have to connect our computer to the SMR-20 and open VPANEL.

VPANEL.

X-Y Controls. Move the tool in the X-Y axis.

Z Controls.Move the tool in the Z axis.

Cursor Step and Shortcuts. The Step Cursor determines the speed at which the tool moves along all axes; Continue is the smoothest and x1 is the slowest setting, since each click corresponds to a single motor movement. The Shortcuts are to automatically move to an already saved point (Origin Point).

Speed and Spindle. Is to set the speed and to turn on or turn off the the spin of the tool.

Set Coordinates (Origin Point). By clicking the XY or the Z button we can set the Origin Point.

Process Controls. Cut is for adding our code and start the process. Pause, this allows you to pause the process and resume it from where it left off. Stop, stops the process.

Cutting

Fab

2. Then we have to click on Cut. A window will open, and we should click Add to add our milling code.

Fab

3. Finally we have to click Output and the machine will automatically start to cut.

Cutting

Fab

Soldering

Fab

1. To solder, we first need to set up our workspace and gather the necessary tools.

Necessary tools

  • Soldering station
  • Flux
  • Soldering Tin
  • Desoldering mesh
  • Silicone Tablecloth

Components

  • Pinheaders
  • 1 Jumper
  • 2 smd WS2812B

Soldering

Fab

2. First, we need to apply flux to our PCB so that the solder adheres better. I placed the Xiao over the pins so it would stand upright.

Soldering

Fab

3.Then, we have to turn on the soldering station and set the temperature. To solder tin it is recomendable to place the temperature above 300 °C (572 °F). I will use 375 °C (710 °F) because that works good with my materials.

4.To solder, we have to place the soldering iron over the copper board and heat it up, then we have to place the Tin on the surface and wait until it melts. It is important to place the Tin on the copper surfance and not on the soldering iron because the melted Tin flows toward hot surfaces, if we place it on the soldering iron, it won't adhere easily to the copper surface because it will be cooler.

Results

Fab

OLED Display

OLED Display

Fab

Is a compact, energy-efficient screen module (commonly 0.96 or 1.3 inches) that uses organic light-emitting diodes to display text, images, and graphics.

To operate the OLED display, a microcontroller is required . For this board, the driver is the SH1106, which comes with a library that allows to display text, bitmaps, pixels, rectangles, circles, and lines. In my case the microcontroller is the XIAO ESP32-C6 and I'll code in Arduino IDE.

Data Sheet: OLED 128×64 1.3 ” I2C SH1106

Specifications

Interface:I²C (I²C address: 0x3C).

Pins:4 (VDD, GND, SCK, and SDA).

Potential: 0.04W when all pixels are lit.

Voltage: 3V - 5.5V DC

Connection

Fab

1. First we have to locate the 12C pins of the XIAO. Those are D4 and D5. D4 is Serial Data (SDA). SDA is a bidirectional line used to transfer data bits between devices. D5 is Serial Clock (SCL). SCL is the clock signal, usually generated by a master device, which synchronizes the data transfer. A data bit on the SDA line is read on each rising edge of the SCL clock.

2. The OLED display's two I2C pins should be connected to D4 and D5, and the other two pins should be connected to 3.3V and GND, respectively, on my board.

XIAO RP2350 - OLED SSD1306 Display Setup

~ Protocol: I²C Communication (SDA/SCL).
~ Display: 128x64 Monochrome OLED.
~ Library: Adafruit SSD1306 & GFX for frame buffer management.


            #include <SPI.h>
            #include <Wire.h>
            #include <Adafruit_GFX.h>
            #include <Adafruit_SSD1306.h>

            #define SCREEN_WIDTH 128
            #define SCREEN_HEIGHT 64
            #define OLED_RESET -1
            #define SCREEN_ADDRESS 0x3C

            Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

            void setup() {
            display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
            display.clearDisplay();
            
            display.setTextSize(1);
            display.setTextColor(WHITE);
            display.setCursor(0,0);
            
            display.print("Helo World");
            display.display();
            }

            void loop() {
            // Main loop remains empty for static display
            }

FIRST CODE - EXPLANATION

This code provides a clean, standard implementation for driving a monochrome OLED display using the XIAO RP2350. It leverages the I2C protocol to send text data to an SSD1306 controller, providing a simple visual interface for your projects.

~ Library Imports and Hardware Definitions:
The script begins by including the necessary libraries for hardware communication. SPI.h and Wire.h manage the low-level data transfer, with Wire.h being the primary driver for I2C communication. The Adafruit_GFX.h library provides the graphical "vocabulary" (drawing lines, circles, and text), while Adafruit_SSD1306.h acts as the specific driver for this OLED hardware. The constants SCREEN_WIDTH (128) and SCREEN_HEIGHT (64) define the pixel resolution, and SCREEN_ADDRESS (0x3C) sets the unique I2C identifier for the display.

~ Object Creation and Memory Management:
To interface with the screen, the code instantiates an object called display using the Adafruit_SSD1306 class. This object is initialized with the screen dimensions, a reference to the I2C interface (&Wire), and a reset pin definition (OLED_RESET), which is set to -1 because these small displays typically share the microcontroller's reset line. This object essentially creates a "frame buffer"—a slice of memory in the RP2350 that acts as a digital canvas before the data is sent to the physical screen.

~ Display Initialization (Setup):
Inside void setup(), the first critical step is calling display.begin(). This function takes the parameter SSD1306_SWITCHCAPVCC, which tells the display to generate its own internal high voltage from the 3.3V supply provided by the XIAO. Immediately following this, display.clearDisplay() is called. This is a mandatory step because OLED memory usually contains random "noise" (static pixels) upon power-up; clearing the buffer ensures a clean slate.

~ Text Formatting and Positioning:
Once the screen is ready, the code sets the typography parameters using the display object. setTextSize(1) sets the text to the standard 6x8 pixel font, while setTextColor(WHITE) ensures that the pixels will be lit up (on a monochrome screen, "white" simply means "on"). The function setCursor(0,0) acts like an invisible pen, placing the starting point for the text at the very top-left pixel coordinate of the screen.

~ Rendering and the Display Buffer:
The actual content is defined using display.print("Helo World"). However, a common point of confusion for beginners is that this function does not instantly show text on the glass. Instead, it only updates the internal memory buffer. To physically push that digital canvas to the OLED hardware, the function display.display() must be called. This final command transfers the entire 128x64 bitmask over the I2C wires, lighting up the corresponding pixels on the screen.

~ The Execution Loop:
In this specific program, the void loop() remains empty. Because the "Helo World" message is static and doesn't need to change, the code executes the drawing logic once in the setup phase and then effectively enters a holding pattern. The XIAO RP2350 continues to run, but since there are no instructions in the loop, the display simply maintains the last frame it received until the power is disconnected or the device is reset.

First Code Result

XIAO ESP32-C6 - Integrated Capacitive System

~ Sensors: Capacitive step response (TX: D10, RX: A2).
~ Visuals: 128x64 OLED Display & 2x WS2812B NeoPixels.
~ Action: Activates "LIMON" text and Green LEDs on specific touch thresholds.


            #include <SPI.h>
            #include <Wire.h>
            #include <Adafruit_GFX.h>
            #include <Adafruit_SSD1306.h>
            #include <Adafruit_NeoPixel.h>

            #define SCREEN_WIDTH 128 
            #define SCREEN_HEIGHT 64 
            #define OLED_RESET -1 
            #define SCREEN_ADDRESS 0x3C 
            Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

            #define PIN_NEO 3    
            #define NUM_PIXELS 2    
            Adafruit_NeoPixel pixels(NUM_PIXELS, PIN_NEO, NEO_GRB + NEO_KHZ800);

            long result;
            int analog_pin = A2;  
            int tx_pin = D10;     

            void setup() {
            pinMode(tx_pin, OUTPUT);      
            Serial.begin(115200);

            if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
                Serial.println(F("SSD1306 allocation failed"));
                for(;;); 
            }
            
            display.clearDisplay();
            display.display();

            pixels.begin(); 
            pixels.clear(); 
            pixels.show();

            delay(1000); 
            }

            long tx_rx() {
            int read_high, read_low, diff;
            long sum = 0;
            int N_samples = 100; 

            for (int i = 0; i < N_samples; i++) {
                digitalWrite(tx_pin, HIGH);
                read_high = analogRead(analog_pin);
                delayMicroseconds(100); 

                digitalWrite(tx_pin, LOW);
                read_low = analogRead(analog_pin);
                diff = read_high - read_low;
                sum += diff;
            }
            return sum;
            }

            void loop() {
            result = tx_rx();
            long mapped_result = map(result, 15000, 25000, 0, 1024); 

            if (mapped_result >= 30000 && mapped_result <= 32000) {
                display.clearDisplay();
                display.setTextSize(2);      
                display.setTextColor(SSD1306_WHITE);
                display.setCursor(35, 25);   
                display.print("LIMON");
                display.display();
            } else {
                display.clearDisplay();      
                display.display();
            }

            if (mapped_result >= 30000 && mapped_result <= 35000) {
                for(int i=0; i < NUM_PIXELS; i++) {
                pixels.setPixelColor(i, pixels.Color(0, 255, 0)); 
                }
                pixels.show();
            } else {
                pixels.clear();
                pixels.show();
            }

            delay(50); 
            }

SECOND CODE - EXPLANATION

This code integrates three main systems: a capacitive-style sensor, an OLED display, and NeoPixel LEDs. It functions as a detection device that monitors a signal, processes the data, and provides both visual text and color-coded light feedback when a specific threshold is reached.

~ Library Imports and Hardware Configuration:
The program starts by including the necessary libraries for communication and peripheral control: SPI.h and Wire.h for data protocols, Adafruit_GFX.h and Adafruit_SSD1306.h for the display, and Adafruit_NeoPixel.h for the LEDs. The OLED configuration defines the screen dimensions (128x64) and the I2C address (0x3C). The NeoPixel configuration sets up two pixels on digital pin 3. Finally, the sensor variables define analog_pin (A2) for reading and tx_pin (D10) for transmitting pulses, along with a long result variable to store the sensor data.

~ System Initialization (Setup):
The void setup() function prepares the hardware for operation. It sets the tx_pin as an OUTPUT and opens the Serial Monitor at 115200 baud. It then attempts to initialize the OLED using display.begin(); if the allocation fails, the code enters an infinite loop as a safety measure. Once initialized, the screen is cleared. The NeoPixels are also started and cleared using pixels.begin(), and a one-second delay ensures all components have stabilized their voltages before the main loop begins.

~ The Sensor Mechanism: tx_rx Function
The long tx_rx() function implements a differential sensing technique to detect proximity or touch. It runs a loop 100 times (N_samples), toggling the tx_pin between HIGH and LOW. When the pin is high, it records read_high; when low, it records read_low. The variable diff calculates the difference between these states, and this value is added to a cumulative sum. This method is highly effective for filtering out ambient electrical noise, providing a much more stable result than a single analog read would allow.

~ Data Processing and Mapping:
Inside the void loop(), the sensor value is retrieved via tx_rx() and stored in result. The code then uses the map() function to create mapped_result. This scales the expected raw sensor range (15,000 to 25,000) to a value between 0 and 1,024. Both the raw and mapped values are printed to the Serial Monitor for debugging. It is important to note that the triggers later in the code look for mapped_result values above 30,000, which suggests the sensor expects values significantly higher than the 15k–25k range defined in the mapping function.

~ OLED Display Feedback Logic:
The display logic is governed by an if-else structure. If mapped_result falls within the narrow window of 30,000 to 32,000, the OLED activates. It clears the previous frame, sets the text size to 2 for readability, and uses display.setCursor(35, 25) to center the word "LIMON" on the screen. If the value is outside this specific range, the screen is cleared and remains blank. This provides a clear, textual confirmation when a specific detection threshold is hit.

~ NeoPixel Visual Feedback and Timing:
Simultaneously, the code manages the NeoPixels for color-based feedback. If mapped_result is between 30,000 and 35,000, the pixels are set to Green (0, 255, 0) using a for loop that iterates through NUM_PIXELS. Outside this range, the pixels are cleared. The loop concludes with a delay(50), which keeps the system responsive while preventing the processor from over-cycling. This combination of text on the OLED and a green light provides a redundant, high-visibility notification system.

Second Code Result

OLED Display

Fab

Is a compact, energy-efficient screen module (commonly 0.96 or 1.3 inches) that uses organic light-emitting diodes to display text, images, and graphics.

To operate the OLED display, a microcontroller is required . For this board, the driver is the SH1106, which comes with a library that allows to display text, bitmaps, pixels, rectangles, circles, and lines. In my case the microcontroller is the XIAO ESP32-C6 and I'll code in Arduino IDE.

Data Sheet: OLED 128×32 0.91 I2C SSD1306

Specifications

Interface:I²C (I²C address: 0x3C).

Pins:4 (VDD, GND, SCK, and SDA).

Potential: 0.04W when all pixels are lit.

Voltage: 3V - 5.5V DC

Connection

Fab

1. First we have to locate the 1²C pins of the XIAO. Those are D4 and D5. D4 is Serial Data (SDA). SDA is a bidirectional line used to transfer data bits between devices. D5 is Serial Clock (SCL). SCL is the clock signal, usually generated by a master device, which synchronizes the data transfer. A data bit on the SDA line is read on each rising edge of the SCL clock.

2. The OLED display's two I²C pins should be connected to D4 and D5, and the other two pins should be connected to 3.3V and GND, respectively, on my board.

XIAO RP2350 - OLED SSD1306 Display Setup

~ Protocol: I²C Communication (SDA/SCL).
~ Display: 128x32 Monochrome OLED.
~ Library: Adafruit SSD1306 & GFX for frame buffer management.


            #include <SPI.h>
            #include <Wire.h>
            #include <Adafruit_GFX.h>
            #include <Adafruit_SSD1306.h>

            #define SCREEN_WIDTH 128
            #define SCREEN_HEIGHT 32
            #define OLED_RESET -1
            #define SCREEN_ADDRESS 0x3C

            Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

            void setup() {
            display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
            display.clearDisplay();
            
            display.setTextSize(1);
            display.setTextColor(WHITE);
            display.setCursor(0,0);
            
            display.print("Helo World");
            display.display();
            }

            void loop() {
            // Main loop remains empty for static display
            }

FIRST CODE - EXPLANATION

This code provides a clean, standard implementation for driving a monochrome OLED display using the XIAO RP2350. It leverages the I2C protocol to send text data to an SSD1306 controller, providing a simple visual interface for your projects.

~ Library Imports and Hardware Definitions:
The script begins by including the necessary libraries for hardware communication. SPI.h and Wire.h manage the low-level data transfer, with Wire.h being the primary driver for I2C communication. The Adafruit_GFX.h library provides the graphical "vocabulary" (drawing lines, circles, and text), while Adafruit_SSD1306.h acts as the specific driver for this OLED hardware. The constants SCREEN_WIDTH (128) and SCREEN_HEIGHT (32) define the pixel resolution, and SCREEN_ADDRESS (0x3C) sets the unique I2C identifier for the display.

~ Object Creation and Memory Management:
To interface with the screen, the code instantiates an object called display using the Adafruit_SSD1306 class. This object is initialized with the screen dimensions, a reference to the I2C interface (&Wire), and a reset pin definition (OLED_RESET), which is set to -1 because these small displays typically share the microcontroller's reset line. This object essentially creates a "frame buffer"—a slice of memory in the RP2350 that acts as a digital canvas before the data is sent to the physical screen.

~ Display Initialization (Setup):
Inside void setup(), the first critical step is calling display.begin(). This function takes the parameter SSD1306_SWITCHCAPVCC, which tells the display to generate its own internal high voltage from the 3.3V supply provided by the XIAO. Immediately following this, display.clearDisplay() is called. This is a mandatory step because OLED memory usually contains random "noise" (static pixels) upon power-up; clearing the buffer ensures a clean slate.

~ Text Formatting and Positioning:
Once the screen is ready, the code sets the typography parameters using the display object. setTextSize(1) sets the text to the standard 6x8 pixel font, while setTextColor(WHITE) ensures that the pixels will be lit up (on a monochrome screen, "white" simply means "on"). The function setCursor(0,0) acts like an invisible pen, placing the starting point for the text at the very top-left pixel coordinate of the screen.

~ Rendering and the Display Buffer:
The actual content is defined using display.print("Helo World"). However, a common point of confusion for beginners is that this function does not instantly show text on the glass. Instead, it only updates the internal memory buffer. To physically push that digital canvas to the OLED hardware, the function display.display() must be called. This final command transfers the entire 128x64 bitmask over the I2C wires, lighting up the corresponding pixels on the screen.

~ The Execution Loop:
In this specific program, the void loop() remains empty. Because the "Helo World" message is static and doesn't need to change, the code executes the drawing logic once in the setup phase and then effectively enters a holding pattern. The XIAO RP2350 continues to run, but since there are no instructions in the loop, the display simply maintains the last frame it received until the power is disconnected or the device is reset.

Neopixels example

For this part Í will use the neopixels code of my week 9.

Neopixels

Fab

NeoPixels are individually addressable RGB LEDs developed by Adafruit that allow for independent control of color and brightness for each LED on a strip or array, using only a single microcontroller pin.

Datasheet: Neopixel

Specifications

Interface:Serial.

Pins:4 (VDD, GND, DIN, DOUT).

Potential per component: 0.052W when tje light has medium untensity.

Voltage: 5V

How to connect. Neopixels have 4 pins (VDD, GND, DIN, DOUT). To make it work, you must connect the DIN pin through a 100nF resistor to a digital pin on the microcontroller. DIN is the data input. DOUT can be left disconnected or connected to another Neopixel; it is the data output that transmits the signal from DIN. VDD is connected to 5V and GND to its respective pin.

XIAO RP2350 - Sharp Distance Sensor & Visual Feedback

~ Hardware: Sharp GP2Y0A41SK0F (Analog) & 2x WS2812B LEDs.
~ Resolution: 12-bit ADC configuration for Raspberry Pi Silicon.
~ Threshold: Binary color feedback (Red ≤ 10cm, Blue > 10cm).


            #include <Adafruit_NeoPixel.h>

            const int PIN_SHARP = A2; 
            const int PIN_NEO = D3;
            const int NUM_LEDS = 2;

            Adafruit_NeoPixel strip(NUM_LEDS, PIN_NEO, NEO_GRB + NEO_KHZ800);

            void setup() {
            Serial.begin(115200); 
            
            strip.begin();
            strip.setBrightness(50);
            strip.show();

            analogReadResolution(12);
            Serial.println("--- SISTEMA DE PROXIMIDAD LISTO ---");
            }

            void loop() {
            int lectura = analogRead(PIN_SHARP);
            float voltaje = lectura * (3.3 / 4095.0);

            float distancia_cm = 0;
            if (voltaje > 0.4) {
                distancia_cm = 13.0 / (voltaje - 0.1);
            }

            if (distancia_cm > 0 && distancia_cm <= 10) { 
                strip.setPixelColor(0, strip.Color(255, 0, 0));
                strip.setPixelColor(1, strip.Color(255, 0, 0));
            } else {
                strip.setPixelColor(0, strip.Color(0, 0, 255));
                strip.setPixelColor(1, strip.Color(0, 0, 255));
            }

            strip.show();
            delay(50);
            }

CODE - EXPLANATION

This code establishes a proximity-sensing system using an infrared (IR) distance sensor and NeoPixel LEDs. It is specifically configured for the XIAO RP2350 microcontroller, providing real-time visual feedback by changing the LED colors based on how close an object is to the sensor.

~ Hardware Configuration and Variable Definitions:
The program begins by including the Adafruit_NeoPixel.h library to manage the addressable LEDs. It defines three main constants for hardware mapping: PIN_SHARP (A2) for the sensor input, PIN_NEO (D3) for the LED data line, and NUM_LEDS (2) for the quantity of LEDs. An object named strip is created to control the NeoPixels using the NEO_GRB protocol. These variables ensure the code knows exactly where the sensor is sending data and where the light signals are being sent.

~ System Initialization (Setup):
In the void setup() function, the code prepares the hardware for operation. It initializes serial communication at 115200 baud for debugging purposes. The strip.begin() and strip.show() commands ensure the LEDs start in an "off" state, while strip.setBrightness(50) limits the intensity to roughly 20% to save power and protect the eyes. Crucially, the function analogReadResolution(12) is called to set the Analog-to-Digital Converter (ADC) to 12-bit mode, allowing the microcontroller to read the sensor with a high precision range of 0 to 4095.

~ Data Acquisition and Voltage Conversion:
The void loop() starts by taking a raw reading from the sensor using analogRead(PIN_SHARP), storing it in the integer variable lectura. Because the microcontroller operates on 3.3V logic and the ADC is set to 12-bit, the code calculates the variable voltaje using the formula lectura * (3.3 / 4095.0). This converts the raw digital number back into a meaningful voltage level, which is necessary to calculate the physical distance.

~ Distance Calculation Formula:
The code converts the voltage into a distance measurement in centimeters, stored in the variable distancia_cm. Sharp IR sensors like the GP2Y0A41SK0F have a non-linear output, so the code uses a specific linearization formula: 13.0 / (voltaje - 0.1). An if (voltaje > 0.4) condition is used to ensure the sensor is within its reliable operating range before performing the math; if the voltage is too low, the distance remains at zero to avoid mathematical errors or "ghost" readings.

~ Logic and Visual Feedback:
After calculating the distance, the code uses an if-else structure to provide visual feedback. If distancia_cm is between 0 and 10 cm, the code calls strip.setPixelColor to turn both LEDs RED (255, 0, 0), signaling that an object is very close. If the distance is greater than 10 cm or no object is detected, the LEDs are set to BLUE (0, 0, 255). The strip.show() function is then called to push these color changes to the physical LEDs.

~ Debugging and Timing:
To help the user monitor the system, Serial.print and Serial.println are used to output the distancia_cm value to the Serial Monitor in real-time. Finally, a delay(50) is included at the end of the loop. This short 50-millisecond pause ensures the system remains highly responsive to movement while preventing the Serial Monitor from being flooded with data too quickly, striking a balance between performance and readability.

Results

How does it work?

Fab Fab

1. I made this sensor with the help of Adrian Torres' documentation and Neil Gershenfeld's examples.

2. Then, we have to paste the copper board to the Sacrifice Bed.

Materials:

Copper Board.

double-sided tape.

Sacrifice Bed. Is an MDF board designed to prevent the SMR-20 from being damaged in the event that the tool drills too deep.

Before Cutting

Fab

3. Subsequently, we have to place the bed inside the SMR-20. In my case, in my lab, our SMR-20 has a fitting to secure the sacrifice table with screws.

Before Cutting

4. Having secured the bed inside the SMR-20, we have to select the tool for each milling process (Holes, Tracks and Borders).

Fab

Drilling Tool. This tool is specifically for perforations because of its shape and width. To use it, we must set the speed between 0.1 and 0.5 in order to don't damage it.

Fab

Cutting tool. This tool is specifically designed for traces, as its point is sharp and very thin. The Speed of use can be higher but we must be careful about its deep.

Fab

Border tool. This tool is can be used for the border cutting because of its width, it can also be used for perforation, but the diameter of them will be bigger.

Cutting

Fab

1. We have to connect our computer to the SMR-20 and open VPANEL.

VPANEL.

X-Y Controls. Move the tool in the X-Y axis.

Z Controls.Move the tool in the Z axis.

Cursor Step and Shortcuts. The Step Cursor determines the speed at which the tool moves along all axes; Continue is the smoothest and x1 is the slowest setting, since each click corresponds to a single motor movement. The Shortcuts are to automatically move to an already saved point (Origin Point).

Speed and Spindle. Is to set the speed and to turn on or turn off the the spin of the tool.

Set Coordinates (Origin Point). By clicking the XY or the Z button we can set the Origin Point.

Process Controls. Cut is for adding our code and start the process. Pause, this allows you to pause the process and resume it from where it left off. Stop, stops the process.

Cutting

Fab

2. Then we have to click on Cut. A window will open, and we should click Add to add our milling code.

Fab

3. Finally we have to click Output and the machine will automatically start to cut.

Cutting

Fab

Learning outcomes

This week, I learned how to use and program an OLED display, which helped me better understand how displays communicate with microcontrollers. One of the most important aspects I discovered was configuring the display size correctly. In my opinion, this is a critical step because if the dimensions are not set properly, the text may appear distorted, incorrectly scaled, or the display may not show anything at all. Understanding this configuration process helped me troubleshoot display issues more effectively.

I also learned more about the response speed involved in signal transmission and how timing can affect communication between components. This gave me a better understanding of how electronic devices react to incoming signals and how important synchronization is in embedded systems.

Additionally, I expanded my knowledge about NeoPixels and how they can be programmed to create different lighting effects and visual feedback. Working with them helped me better understand concepts such as digital communication, timing control, and LED addressing. Overall, this week allowed me to strengthen both my programming and electronics skills through practical experimentation.

Files