Skip to content

9. Wearables

goals of the week

  • Create a swatch using an attiny with one input and one output of your choice, using hard-soft connection solutions and battery.
  • Create 2 actuator swatches and test them with the Arduino or ATtiny. Learn how to program and ATTiny, add the libraries and links you used for your code.
  • Document the schematic and the programming code, libraries you had to add and calculations.
  • EXTRA POINT Integrate it to a project.


Here are some inspiring projects developed by previous participants:


VISUAL ACTUATOR

LEDs

Different colors = different voltages

RGB LEDs - can combine three leads to create different colors.

NeoPixels (type of LED)

They have chips in them called drivers. Allows you to control each LED in specific.

You have to have them connected to a microcontroller, a battery is not enough.

Fiber Optics

Light shines in one end, travels down the strand, and emerges at the other end.

There is a clear internal core and an external coating called cladding.

LED intensity - depends on the intensity of the light source.

Type of Fiber - End Emitting (light only gets out at the end) / Side Emitting (thicker, cladding as not pronounce).

Taking the LED and attach to the Fiber. Connecting options:

  • Drilling a hole into the LED and placing the fiber
  • Tubing and glue
  • 3D printed enclosure and glue

Thermochromic Ink

Reacts to a heating surface.

Inks or pigments that changes in the presence of heat, the color of the ink goes away.

Heating elements:

  • Stainless steel conductive thread
  • Karl Grimm conductive thread (at leats 2.5 ohms)
  • Conductive Fabrics (tricky)
  • Nichrome wire or flexinol


SOUND ACTUATOR

Sound is a pressure wave created by an object when it vibrates.

Permanent Magnets - are objects that generate a magnetic field, which can attract and repel some metals. Every magnet as a north and a south pole.

Electromagnets - is a type of magnet that only works when electricity is flowing through it. The electromagnet force of a straight wire is not very strong at all but by coiling the wire, you can create a stronger field.

When you connect a battery and an audio signal to your electromagnet, it forms a magnetic field. The magnetic field fluctuates based on the electrical frequencies of the audio signal.

Variables:

  • Coil tightness - tighter the coil the louder it is
  • Material - stiff or less stiffer impact the sound
  • Magnet size - the bigger the louder
  • Magnet placement - the volume will be louder the closer the magnet is to the centre of the coil

Your speaker should:

  • Use conductive thread
  • Be a coil
  • Have a small space between the coil traces
  • Have two ends to connect to your circuit

You can make it by embroidery, weaving, folding, and vinyl cutting.


MOTION ACTUATOR

Shape Memory Alloy

Shape memory alloys, metals that change shapes when they are heated to a certain temperature.

  • Untrained flexinol will contract by 10% of its length when heat is applied.
  • Trained flexinol contracts by 10% but has been trained into a shape through a heating process. When heated, it will return to the shape you trained it to.

Variables:

  • Material Substrates — lighter material will yield best results, like cotton, paper, silk, (not polyester)
  • Diameter size — higher the diameter you will need more power
  • Length — the resistance increases when the length increases

You can make it by folding, curling, smoking

Connections: you cannot solder directly onto into, but you can use crimp beads to attach the flexinol to.

Flip Dots

Magnetic hematite bead flips back and forth when we run current through the coil. It creates an electromagnetic field that attracts or repeals the bead on the direction of the current.

  • Bead size - the bigger the bead the larger the EMF must be to flip it
  • Securing the bead - test the tension
  • Number of coils - at least 50
  • Gauge of wire - smaller gauge, more coils in less space
  • Coil wrapping material

Vibration Motors and Haptics

Haptic sensing involves both touch and kinesthesia (sometimes referred to as proprioception) which refers to sense of limb positioning and movement.

Unlike other senses, haptic sensing is bidirectional. The information we can extract about an object’s properties is linked to the movements made to perceive those properties

How it works:

  • Small motors that vibrate!
  • Voltage: 2V - 5V
  • 5V current draw: 100mA
  • 3V current draw: 60mA

There are two types of vibration motors: Eccentric Rotating Mass (ERM) and Linear Resonant Actuator (LRA). ERM vibe motors have an off-centre load that causes vibrations when it rotates.

You can use motor drivers to control the behaviour more precisely. You need one board for each motor.



research & ideation

inspiration

  1. ?
  2. GIVENCHY FALL 1999 RTW COLLECTION by ALEXANDER MCQUEEN
  3. AURORA LED NECKLACE
  4. MOVING TARGET 2007 by MAGGIE ORTH
  5. ?
  6. PANGOLIN SCALES PROJECT by ANOUK WIPPRECHT
  7. ?
  8. Frison, PHILIPS DESIGN


neopixels

neopixel

We began the practical classes with some Arduino work to refresh our programming skills from E-Textiles week. It was a great refresher, and we had a lot of fun exploring various coding possibilities with Neopixels strips, including integrating an LDR sensor.

NeoPixels are RGB LEDs with built-in microcontrollers, allowing each light to display unique colors and brightness.

Before proceeding, we needed to install two essential libraries: one for controlling the NeoPixels and another for interfacing with the Servo Motor (which you'll see later on). Libraries are pre-written code collections designed to simplify hardware interfacing and specific tasks. They’re incredibly helpful, not only for beginners but for experienced developers as well.

In the image below, you’ll find a detailed view of the libraries we installed, including their versions and the initial code examples we used. Adafruit_NeoPixel.h 1.12.3 / ESP32Servo.h 1.2.1


arduino


In the image below, we have a breadboard circuit setup featuring a Seeed Studio XIAO ESP32S3, jumper wires, a NeoPixel strip with two LEDs, a 10k resistor, a 390-ohm resistor, and an LDR. This serves as a base circuit for the different approaches we made.

breadboardneopixel


schematic

Schematic by Michelle Vossen with Fritzing.


Building this Circuit on a Breadboard:

  • Connect the GND pin on the NeoPixel board to the GND pin on the microcontroller, and the 5V pin on the NeoPixel board to the 5V pin on the microcontroller.

  • Attach a 390Ω resistor to the Din pin on the NeoPixel board.

  • Connect the other end of the 390Ω resistor to pin A0 on the microcontroller.

  • Connect one leg of the LDR to GND through a 10KΩ resistor.

  • Connect the other leg of the LDR to pin A1 on the microcontroller.


BASE CODE:

/*
Map LDR input to neopixel output

Reads an analog input on pin A1, and maps it to neopixels. 
Also prints the result to the Serial Monitor.

Connect the data wire of the neopixel strip through a 300-500 Ohm resistor to pin A0
Connect the red wire of the neopixel strip to +3.3V (this is because when using battery power, there is no power on the 5V pin)
Connect the black wire of the neopixel strip to GND

Connect one leg of the LDR to pin A1. Also connect A1 through a 4.7k Ohm resistor to +3.3V (this way, 
we're making a voltage divider). Depending on the LDR value range you may need to increase 
or decrease the resistor value.
Connect the other leg of the LDR to ground

Other options for this code:
- Attach the center pin of a potentiometer to pin A0, and the outside pins to +3.3V and ground.

This example code is in the public domain.
20 Oct 2024
by Michelle Vossen

https://v0ss3n.github.io/projects/education/wearables 
*/

#include <Adafruit_NeoPixel.h>

// Pin Definitions
const int sensor_pin = A1;      // LDR connected to A0
const int neopixel_pin = A0;      // NeoPixel strip connected to A1

// NeoPixel Setup
const int numPixels = 2;   // Number of NeoPixels on the strip. Change according to the amount of NeoPixels. Keep the power budget in mind, see https://learn.adafruit.com/adafruit-neopixel-uberguide/powering-neopixels
Adafruit_NeoPixel strip = Adafruit_NeoPixel(numPixels, neopixel_pin, NEO_GRB + NEO_KHZ800);

int ldrValue;           // Variable to store LDR value

void setup() {
// Initialize the NeoPixel strip
strip.begin();
strip.show();  // Initialize all pixels to 'off'

// Initialize Serial Monitor for debugging
Serial.begin(115200);
}

void loop() {
// Read LDR value (range: 0 to 4095 on ESP32S3's 12-bit ADC)
ldrValue = analogRead(sensor_pin);
Serial.print("LDR Value before constraining and filtering: ");
Serial.println(ldrValue);
// Scale the LDR value down (because the values range from 650 to 1000 in my case). Change according to the values that you need.
ldrValue = constrain(ldrValue, 600, 2500);  // Constrain to the expected range
ldrValue = ldrValue / 10; // very rough filter, to remove some jitter

int mappedLdr = map(ldrValue, 60, 250, 0, 100);  // Map to percentage (0-100)

// Light up more NeoPixels based on the mapped LDR value (the closer, the more pixels)
int numLitPixels = map(mappedLdr, 100, 0, 0, numPixels);  // Map LDR to the number of NeoPixels

int ldrColor = map(mappedLdr, 0, 100, 0, 255);

// Light up the corresponding number of NeoPixels
for (int i = 0; i < numPixels; i++) {
    if (i < numLitPixels) {
    strip.setPixelColor(i, strip.Color(ldrColor, 0, 0));  // Set lit pixels to blue
    } else {
    strip.setPixelColor(i, strip.Color(0, 0, 0));    // Turn off the remaining pixels
    }
}

// Update the NeoPixel strip
strip.show();

// Output values to Serial Monitor for debugging
Serial.print("LDR Value: ");
Serial.print(ldrValue);
Serial.print(" | Lit Pixels: ");
Serial.println(numLitPixels);

// Add a small delay to avoid overwhelming the serial monitor and NeoPixel updates
delay(100);
}


CODE ALTERATIONS:

1ST Brighter to Darker

    int ldrColor = map(mappedLdr, 0, 100, 0, 255);

    // Light up the corresponding number of NeoPixels
    for (int i = 0; i < numPixels; i++) {
        if (i < numLitPixels) {
        strip.setPixelColor(i, strip.Color(ldrColor, 0, 0));  // Set lit pixels to blue
        } else {
        strip.setPixelColor(i, strip.Color(0, 0, 0));    // Turn off the remaining pixels
        }
    }

2ND Blue to Red

    int ldrBlue = map(mappedLdr, 0, 100, 0, 255); 
    int ldrRed = map(mappedLdr, 0, 100, 255, 0);

    // Light up the corresponding number of NeoPixels
    for (int i = 0; i < numPixels; i++) {
        if (i < numLitPixels) {
        strip.setPixelColor(i, strip.Color(ldrRed, 0, ldrBlue));  // Set lit pixels to blue
        } else {
        strip.setPixelColor(i, strip.Color(0, 0, 0));    // Turn off the remaining pixels
        }
    }

3RD Blue pair to Red pair

    int ldrBlue = map(mappedLdr, 0, 100, 0, 255); 
    int ldrRed = map(mappedLdr, 0, 100, 255, 0);

    strip.setPixelColor(0, strip.Color(ldrRed, 0, ldrBlue));
    strip.setPixelColor(1, strip.Color(ldrRed, 0, ldrBlue));

    (take OFF)  // // Light up the corresponding number of NeoPixels
    // for (int i = 0; i < numPixels; i++) {
    //   if (i < numLitPixels) {
    //     strip.setPixelColor(i, strip.Color(ldrRed, 0, ldrBlue));  // Set lit pixels to blue
    //   } else {
    //     strip.setPixelColor(i, strip.Color(0, 0, 0));    // Turn off the remaining pixels
    //   }
    // }

4TH 1 Red 1 Blue Reverse

    int ldrBlue = map(mappedLdr, 0, 100, 0, 255);
    int ldrRed = map(mappedLdr, 0, 100, 255, 0);

    strip.setPixelColor(0, strip.Color(ldrRed, 0, ldrBlue));
    strip.setPixelColor(1, strip.Color(ldrBlue, 0, ldrRed));

    (take OFF)  // // Light up the corresponding number of NeoPixels
    // for (int i = 0; i < numPixels; i++) {
    //   if (i < numLitPixels) {
    //     strip.setPixelColor(i, strip.Color(ldrRed, 0, ldrBlue));  // Set lit pixels to blue
    //   } else {
    //     strip.setPixelColor(i, strip.Color(0, 0, 0));    // Turn off the remaining pixels
    //   }
    // }

5TH 1 Red 1 Blue to Green pair

    int ldrBlue = map(mappedLdr, 0, 100, 0, 255); 
    int ldrRed = map(mappedLdr, 0, 100, 255, 0);


    strip.setPixelColor(0, strip.Color(ldrRed, ldrBlue, 0));
    strip.setPixelColor(1, strip.Color(0, ldrBlue, ldrRed));

    (take OFF) // // Light up the corresponding number of NeoPixels
    // for (int i = 0; i < numPixels; i++) {
    //   if (i < numLitPixels) {
    //     strip.setPixelColor(i, strip.Color(ldrRed, 0, ldrBlue));  // Set lit pixels to blue
    //   } else {
    //     strip.setPixelColor(i, strip.Color(0, 0, 0));    // Turn off the remaining pixels
    //   }
    // }


felt touchSensor

Issy and I decided to create a felt touch sensor that would change the colors of two NeoPixels. Initially, one NeoPixel would be red and the other blue, and upon touch, both would turn green. To start, we made a quick needle-felted sample with conductive fiber. You can find the step-by-step process for needle felting in my E-textiles week documentation.

We intentionally kept the sample soft and not too dense, allowing the NeoPixel lights to shine through the wool.

Since we already had both codes, one for controlling the NeoPixels and another for the felt touch sensor that I developed during E-textiles week, we focused on combining them and building the circuit on the breadboard. Using ChatGPT, we merged the two codes by providing all the necessary details, including our goal and the tools we had available.


touchsensor


Building this Circuit on a Breadboard:

  • Connect the GND pin on the NeoPixel board to the GND pin on the microcontroller, and the 5V pin on the NeoPixel board to the 5V pin on the microcontroller.

  • Attach a 390Ω resistor to the Din pin on the NeoPixel board.

  • Connect the other end of the 390Ω resistor to pin A0 on the microcontroller.

  • Use a jumper wire with an alligator clip to connect the touch sensor (felt sample) to Touch2 on the microcontroller.


CODE:

#include <Adafruit_NeoPixel.h>

// Pin Definitions
const int neopixel_pin = A0;       // NeoPixel strip connected to A0
const int touch_pin = T2;          // Pin that we're going to touch

// NeoPixel Setup
const int numPixels = 2;           // Number of NeoPixels on the strip
Adafruit_NeoPixel strip = Adafruit_NeoPixel(numPixels, neopixel_pin, NEO_GRB + NEO_KHZ800);

// Variables for touch sensor
int touchValue;                    // Variable to store touch sensor value
int threshold = 75000;             // Threshold for lighting up NeoPixels

void setup() {
// Initialize the NeoPixel strip
strip.begin();
strip.show();  // Initialize all pixels to 'off'

// Initialize Serial Monitor for debugging
Serial.begin(115200);
}

void loop() {
// Read touch sensor value
touchValue = touchRead(touch_pin);
Serial.print("Touch Value before constraining: ");
Serial.println(touchValue);

// Constrain the touch value range
touchValue = constrain(touchValue, 35000, 110000);

// Map the touch value to a percentage (0-100) for NeoPixel control
int mappedTouch = map(touchValue, 35000, 110000, 0, 100);

// Calculate color intensity based on mapped touch value
int touchBlue = map(mappedTouch, 0, 100, 0, 255);
int touchRed = map(mappedTouch, 0, 100, 255, 0);

// Set colors for each NeoPixel
strip.setPixelColor(0, strip.Color(touchRed, touchBlue, 0));  // First pixel fades red to blue
strip.setPixelColor(1, strip.Color(0, touchBlue, touchRed));  // Second pixel fades blue to red

// Update the NeoPixel strip
strip.show();

// Output values to Serial Monitor for debugging
Serial.print("Mapped Touch Value: ");
Serial.println(mappedTouch);

// Add a small delay to avoid overwhelming the serial monitor and NeoPixel updates
delay(100);
}



textile speaker

A textile speaker transforms electrical energy into sound waves using three key components: a permanent magnet, an electromagnet, and a vibrating membrane. These elements work together to move air and create sound.

Electromagnetism forms the basis of this process. When an electric current flows through a wire, it generates a magnetic field. If you coil the wire, the magnetic field intensifies due to the increased surface area, resulting in a stronger electromagnetic force.

To produce the sound, the electromagnet (a coiled wire) is connected to a circuit powered by a battery and an audio signal. The audio signal carries varying electrical frequencies, causing the magnetic field of the coil to fluctuate in sync with the signal. When placed near a strong permanent magnet, the changing electromagnetic field interacts with the permanent magnet’s field, creating forces of attraction and repulsion. As the membrane vibrates, it pushes and pulls the surrounding air, generating sound waves that correspond to the original audio signal.


speakers

Issy and I teamed up to explore various speaker prototypes, experimenting with coils of different shapes, sizes, and materials. The goal was to evaluate how these variations affect performance and identify the most effective designs.

One critical aspect of the process was ensuring that the coil traces didn’t overlap or touch each other, as this could cause a short circuit and disrupt the electromagnetic field.

To power and test each speaker, we used the Adafruit PAM8302 Mono 2.5W Class D Audio Amplifier, connected to three AA batteries (providing 4.5V). An audio jack linked the setup to a laptop to deliver the audio signal.

This experiment allowed us to study how different parameters, such as coil geometry and material properties, influence the vibration of the speaker membrane and, ultimately, the sound quality.

  1. (circular coil) conductive thread hand sewn with non condcutive thread - less coil tightness; small diameter. Results: the sound was only audible when held close to the ears.
  2. (circular coil) conductive thread sewn with a sewing machine - medium coil tightness. Results: Both the circular and square coils produced similar results, delivering clear and audible sound.
  3. (square coil) conductive thread sewn with a sewing machine - medium coil tightness.
  4. (circular coil) conductive thread (copper wire) hand sewn - less coil tightness; small diameter. Results: Audible from a distance very well.
  5. (square coil) conductive tape - high coil tightness. Results: The sound quality was great, mainly due to the large conductive surface area.
  6. (circular coil) condcutive thread (copper wire) hand sewn with non condcutive thread; high coil tightness; large diameter. Results: The loudest design, not only projecting sound from afar but also handling bass frequencies very well.



servo motor

Servomotors are compact rotary or linear actuators commonly used for precise control of position, angle, or speed. They operate using a pulse-width modulation (PWM) signal, where the length of each pulse determines the motor's target movement. This makes them highly versatile for applications requiring accurate and repeatable motion.

The servomotors we worked with fell into two categories: fixed-range (180 degrees) and continuous rotation (360 degrees). Fixed servos are ideal for tasks requiring controlled angular movement, while continuous servos are better suited for full rotation and speed control.

To control these servos, we used the Servo library in Arduino, that I showed above. This version was compatible with the Xiao ESP32S3 microcontroller, as newer versions of the library presented compatibility issues. We began our experimentation by uploading a basic example sketch called Sweep, which demonstrated how to smoothly rotate a servo back and forth across its range.

This exercise allowed us to better understand the nuances of servo control and how the PWM signal interacts with the motor.

servomotorbreadboard

Building this Circuit on a Breadboard:

  • Connect the GND pin on the NeoPixel board to the GND pin on the microcontroller, and the 5V pin on the NeoPixel board to the 5V pin on the microcontroller.

  • Attach a 390Ω resistor to the Din pin on the NeoPixel board.

  • Connect the other end of the 390Ω resistor to pin A0 on the microcontroller.

  • Use a jumper wire with an alligator clip to connect the touch sensor (felt sample) to Touch2 on the microcontroller.


CODE:

/* Sweep
by BARRAGAN <http://barraganstudio.com>
This example code is in the public domain.

modified 8 Nov 2013
by Scott Fitzgerald

modified for the ESP32 on March 2017
by John Bennett

see http://www.arduino.cc/en/Tutorial/Sweep for a description of the original code

* Different servos require different pulse widths to vary servo angle, but the range is 
* an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos
* sweep 180 degrees, so the lowest number in the published range for a particular servo
* represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top
* of the range represents 180 degrees. So for example, if the range is 1000us to 2000us,
* 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 1800
* degrees.
* 
* Circuit: (using an ESP32 Thing from Sparkfun)
* Servo motors have three wires: power, ground, and signal. The power wire is typically red,
* the ground wire is typically black or brown, and the signal wire is typically yellow,
* orange or white. Since the ESP32 can supply limited current at only 3.3V, and servos draw
* considerable power, we will connect servo power to the VBat pin of the ESP32 (located
* near the USB connector). THIS IS ONLY APPROPRIATE FOR SMALL SERVOS. 
* 
* We could also connect servo power to a separate external
* power source (as long as we connect all of the grounds (ESP32, servo, and external power).
* In this example, we just connect ESP32 ground to servo ground. The servo signal pins
* connect to any available GPIO pins on the ESP32 (in this example, we use pin 18.
* 
* In this example, we assume a Tower Pro MG995 large servo connected to an external power source.
* The published min and max for this servo is 1000 and 2000, respectively, so the defaults are fine.
* These values actually drive the servos a little past 0 and 180, so
* if you are particular, adjust the min and max values to match your needs.
*/

#include <ESP32Servo.h>

Servo myservo;  // create servo object to control a servo
// 16 servo objects can be created on the ESP32

int pos = 0;    // variable to store the servo position

int servoPin = A0;

void setup() {
    // Allow allocation of all timers
    ESP32PWM::allocateTimer(0);
    ESP32PWM::allocateTimer(1);
    ESP32PWM::allocateTimer(2);
    ESP32PWM::allocateTimer(3);
    myservo.setPeriodHertz(50);    // standard 50 hz servo
    myservo.attach(servoPin, 1000, 2000); // attaches the servo on pin 18 to the servo object 
    // using default min/max of 1000us and 2000us
    // different servos may require different min/max settings
    // for an accurate 0 to 180 sweep
}

void loop() {

    for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
        // in steps of 1 degree
        myservo.write(pos);    // tell servo to go to position in variable 'pos'
        delay(15);             // waits 15ms for the servo to reach the position
    }
    for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
        myservo.write(pos);    // tell servo to go to position in variable 'pos'
        delay(15);             // waits 15ms for the servo to reach the position
    }
}



Next, we integrated an LDR into the circuit, adding more interactivity. The LDR measures light intensity, and its readings are processed and mapped to control the servo motor's movements through Pulse Width Modulation (PWM).

By converting the light levels into PWM signals, the servo motor’s angle is dynamically adjusted in response to changes in ambient light. For instance, brighter light results in one range of motion, while dimmer light produces a different movement.

servomotorbreadboard

Building this Circuit on a Breadboard:

  • Connect the GND pin on the NeoPixel board to the GND pin on the microcontroller, and the 5V pin on the NeoPixel board to the 5V pin on the microcontroller.

  • Attach a 390Ω resistor to the Din pin on the NeoPixel board.

  • Connect the other end of the 390Ω resistor to pin A0 on the microcontroller.

  • Use a jumper wire with an alligator clip to connect the touch sensor (felt sample) to Touch2 on the microcontroller.


CODE:

/*
Map LDR input to servo motor output

Reads an analog input on pin A1, and maps it to servo motor angle (fixed angle versions) or speed (continuous rotation versions). 
Also prints the result to the Serial Monitor.

Ideally, we're powering a micro servo with a stand-alone power supply, but
with a micro servo and not too much weight, we can hopefully get away with powering it
like this :-)
Connect the orange wire of the servo motor to pin A0
Connect the red wire of the servo motor to +3.3V
Connect the black wire of the servo motor to GND

Connect one leg of the LDR to pin A1. Also connect A1 through a 4.7k Ohm resistor to +3.3V (this way, 
we're making a voltage divider). Depending on the LDR value range you may need to increase 
or decrease the resistor value.
Connect the other leg of the LDR to ground

Other options for this code:
- Attach the center pin of a potentiometer to pin A0, and the outside pins to +3.3V and ground.

This example code is in the public domain.
20 Oct 2024
by Michelle Vossen

https://v0ss3n.github.io/projects/education/wearables 
*/

// Include the servo library. If you're using a microcontroller with an AVR, SAM, SAMD, NRF52 or STM32F4 processor, use this library:
// #include <Servo.h>
// Use this one for ESP32 processors:
#include <ESP32Servo.h>


// Pin Definitions
const int servo_pin = A0;  // Servo connected to A1
const int sensor_pin = A1;    // LDR connected to A0

// Variables
Servo myServo;       // Create a Servo object
int ldrValue = 0;    // Variable to store LDR value
int servoAngle = 0;  // Variable to store servo angle

void setup() {
// Attach servo to the specified pin
myServo.attach(servo_pin);

// Initialize Serial Monitor for debugging
Serial.begin(115200);
}

void loop() {
// Read LDR value (0 to 4095 on RP2040 Xiao's 12-bit ADC)
ldrValue = analogRead(sensor_pin);
Serial.println(ldrValue);

// Scale the LDR value down (because the values range from 650 to 1000 in my case). Change according to the values that you need.
ldrValue = constrain(ldrValue, 3000, 4500);  // Constrain to the expected range
ldrValue = ldrValue / 10; // very rough filter, to remove some jitter

// Map LDR value to a range of 180 degrees for the servo (servoAngle: 0 to 180)
// Since I divided the values by 10, the min/max are divided by 10 too. This is not very elegant code :-)
servoAngle = map(ldrValue, 300, 450, 180, 0);
servoAngle = constrain(servoAngle, 0, 180);  // Ensure the angle stays between 0 and 180

// Set the servo to the mapped angle
myServo.write(servoAngle);

// Output values to Serial Monitor for debugging
Serial.print("LDR Value: ");
Serial.print(ldrValue);
Serial.print(" | Servo Angle: ");
Serial.println(servoAngle);

// Add a small delay to avoid overwhelming the serial monitor
delay(100);
}



final proposal

final


Fabrication files


  1. File: xxx 

  2. File: xxx