Skip to content

9. WEARABLES

In this week of WEARABLES, we are building on our knowledge of E-TEXTILES and using a micro controller to integrate electronics into a wearable device of some kind.

RESEARCH & INSPIRATION

There are some really cool Wearables being done by a few different fashion companies. I have a few examples below

Stone Island is ahead of the game with their Thermochromatic fabrics and garments

Junya Watanabe also has experimented with adding electronics into his work

I also looked into what is a growing industry of making garments or wearables that obfuscate surveillance systems.

one of which was a visor that disrupts AI ability to read faces.

TOOLS

  • FABRIXIAO (seeed studio XIAO RP2040)
  • NEOpixels
  • Arduino Big Sound Sensor
  • Gravity I2C 16x2 Arduino LCD with RGB Font Display (Black) V1.0
  • Arduino Joystick
  • ThermoChromatic pigment
  • Brother Digital Embroidery Machine

ELECTRONIC PIN OUTS

SOFTWARE

  • Arduino IDE
  • Inkscape
  • InkStich Extension

PROCESS AND WORKFLOW

I managed to fry my AdaFruit Flora somehow and so this week i switched to the FabriXiao

I first had to solder the micro controller to the board and learn the differences between the two different boards.

LED JOYSTICK

My First sample was a NEOpixel strip that I was controlling with a arduino joystick.

Depending on how I move the joystick it will change the Color and brightness of the LEDS.

If I click the button of the joystick it will also flash the LEDS and switch to a different mode where the LEDS blink while also still having the same color changing properties of the joystick.

CODE EXAMPLE 1

#include <Adafruit_NeoPixel.h>

// Define pin and number of pixels
#define PIN D5          // Pin for the 4-LED NeoPixel strip
#define NUMPIXELS 4     // Number of pixels in the strip

// Define joystick analog input pins
#define JOY_X A0        // X-axis pin of the joystick
#define JOY_Y A1        // Y-axis pin of the joystick
#define JOY_BTN D9       // Button pin of the joystick

// Create NeoPixel object
Adafruit_NeoPixel strip(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// Define color states for the button press
int colorState = 0;  // Used to cycle through colors when button is pressed

// Variables to store the actual center position of the joystick
int centerX = 512;
int centerY = 512;

// Flag to track the current mode
bool isMode1 = true;  // Default to Mode 1 (Joystick controls RGB)

// Deadzone value (tolerance for joystick drift)
int deadzone = 100; // You can adjust this value for higher tolerance

// Color cycling states (you can add more colors here)
unsigned long colors[] = {
  0xFF0000,  // Red
  0x00FF00,  // Green
  0x0000FF,  // Blue
  0xFFFF00,  // Yellow
  0xFF00FF,  // Magenta
  0x00FFFF   // Cyan
};

void setup() {
  // Initialize the NeoPixel strip
  strip.begin();
  strip.show();  // Clear the strip initially

  // Set the joystick button pin as input
  pinMode(JOY_BTN, INPUT_PULLUP);  // Use the internal pull-up resistor

  // Calibrate the joystick center
  calibrateJoystick();
}

void loop() {
  // Read the joystick X and Y axis values (0 to 1023)
  int xValue = analogRead(JOY_X);  // Read X-axis (left-right)
  int yValue = analogRead(JOY_Y);  // Read Y-axis (up-down)

  // Check if the joystick button is pressed (button is LOW when pressed)
  if (digitalRead(JOY_BTN) == LOW) {
    // Toggle between Mode 1 and Mode 2 when the button is pressed
    toggleMode();
    delay(500);  // Simple debounce to prevent multiple presses
  }

  if (isMode1) {
    // In Mode 1, control RGB based on joystick movement
    controlRGBWithJoystick(xValue, yValue);
  } else {
    // In Mode 2, control the color cycling with joystick
    controlColorCycling(xValue, yValue);
  }

  delay(50); // Smooth transition delay
}

// Function to set color and brightness for all LEDs in the strip
void setStripColor(int r, int g, int b, int brightness) {
  for (int i = 0; i < NUMPIXELS; i++) {
    // Apply brightness scaling
    int scaledR = r * brightness / 255;
    int scaledG = g * brightness / 255;
    int scaledB = b * brightness / 255;
    strip.setPixelColor(i, strip.Color(scaledR, scaledG, scaledB));
  }
  strip.show(); // Update the strip with the new colors
}

// Function to control RGB blending using joystick (Mode 1)
void controlRGBWithJoystick(int xValue, int yValue) {
  // Apply deadzone to eliminate small unwanted movements
  if (abs(xValue - centerX) < deadzone) xValue = centerX;
  if (abs(yValue - centerY) < deadzone) yValue = centerY;

  // Calculate joystick movement with dead zone tolerance
  int xMovement = abs(xValue - centerX); // Calculate absolute movement from center
  int yMovement = abs(yValue - centerY);

  // Calculate total movement (sum of X and Y) to determine brightness
  int totalMovement = xMovement + yMovement;

  // Map the total movement to brightness (0-255)
  int brightness = map(totalMovement, 0, 1023, 0, 255);
  brightness = constrain(brightness, 0, 255);  // Ensure brightness is within 0-255

  // Map X-axis to red and green channels, Y-axis to blue channel
  int red = map(xValue, 0, 1023, 0, 255);   // X-axis controls red
  int green = map(yValue, 0, 1023, 0, 255); // Y-axis controls green
  int blue = map(abs(xValue - yValue), 0, 1023, 0, 255); // Combined movement for blue

  // Set the color and brightness of the NeoPixel strip
  setStripColor(red, green, blue, brightness);
}

// Function to control color cycling using joystick (Mode 2)
void controlColorCycling(int xValue, int yValue) {
  // Use the joystick's X and Y axis to adjust the color state
  int xMovement = abs(xValue - centerX);
  int yMovement = abs(yValue - centerY);
  int totalMovement = xMovement + yMovement;

  // Map movement to switch color states
  int colorChangeRate = map(totalMovement, 0, 1023, 0, 5);  // Adjust rate of color change

  if (colorChangeRate > 0) {
    colorState = (colorState + colorChangeRate) % 6;  // Cycle through the colors
  }

  // Set all pixels to the color corresponding to the current state
  setStripColor(
    (colors[colorState] >> 16) & 0xFF,  // Red
    (colors[colorState] >> 8) & 0xFF,   // Green
    colors[colorState] & 0xFF,          // Blue
    255
  );
}

// Function to toggle between Mode 1 and Mode 2 when the button is pressed
void toggleMode() {
  isMode1 = !isMode1;  // Toggle the mode
  if (isMode1) {
    // Clear strip when switching back to Mode 1
    strip.clear();
    strip.show();
  }
}

// Function to calibrate the joystick center position
void calibrateJoystick() {
  // Read the joystick values when it's at rest (before beginning the loop)
  centerX = analogRead(JOY_X);
  centerY = analogRead(JOY_Y);

  // Add some delay to make sure we get the stable readings
  delay(500);  // Calibrate for half a second

  Serial.print("Calibrated Center - X: ");
  Serial.print(centerX);
  Serial.print(" Y: ");
  Serial.println(centerY);
}

DECIBLE COUNTER AND DISPLAY

My second sample was a decible counter connected to an LCD screen.

The big sound sensor is basically acting as a microphone to pick up noise from the surroundings.

The LCD screen then displays the decible level.

It also changes the backlight color at different decible levels.


CODE EXAMPLE 2

#include <Wire.h>
#include <DFRobot_RGBLCD1602.h>  // Library for Gravity I2C LCD

#define SOUND_SENSOR_PIN A0  // Analog pin for Big Sound Sensor
const int sampleWindow = 50; // Time window for sound measurement (ms)

// LCD setup: Set I2C address (default: 0x27)
DFRobot_RGBLCD1602 lcd(/*RGBAddr*/0x60 ,/*lcdCols*/16,/*lcdRows*/2);

void setup() {
  Serial.begin(9600);

  // Initialize the LCD
  lcd.init();
  lcd.setRGB(0, 0, 255);  // Set LCD backlight to blue (quiet)
  lcd.setCursor(0, 0);
  lcd.print("Decibel Meter");
  delay(2000);
  lcd.clear();
}

void loop() {
  unsigned long startMillis = millis();  // Start of sampling window
  unsigned int signalMax = 0;
  unsigned int signalMin = 4095;         // RP2040 ADC is 12-bit (0-4095)

  // Measure sound level
  while (millis() - startMillis < sampleWindow) {
    int sample = analogRead(SOUND_SENSOR_PIN);
    if (sample < 4096) {  // Ensure value is within ADC range
      if (sample > signalMax) signalMax = sample;
      if (sample < signalMin) signalMin = sample;
    }
  }

  unsigned int peakToPeak = signalMax - signalMin;
  double voltage = (peakToPeak * 3.3) / 4095.0;  // Convert ADC value to voltage
  double decibels = 20 * log10(voltage / 0.00631);  // Approximate dB calculation

  // Update LCD
  lcd.setCursor(0, 0);
  lcd.print("dB: ");
  lcd.print((int)decibels);  // Display decibels as an integer
  lcd.print("    ");         // Clear trailing characters

   // Change backlight based on decibel levels
  if (decibels < 0) {
    lcd.setRGB(0, 0, 255);    // Blue (Quiet)
  } else if (decibels < 10) {
    lcd.setRGB(0, 255, 0);    // Green (Moderate)
  } else if (decibels < 16) {
    lcd.setRGB(255, 105, 180); // Pink (Loud)
  } else if (decibels < 25) {
    lcd.setRGB(128, 0, 128);   // Purple (Very Loud)
  } else {
    lcd.setRGB(255, 0, 0);    // Red (Extremely Loud)
  }
  // Optional: Print to Serial Monitor
  Serial.print("dB: ");
  Serial.println(decibels);

  delay(100);  // Short delay for stability
}


THERMOCHROMATIC INK

For my third sample, I created a embroidery with conductive thread and coated it in thermochromatic ink.

Using the InkStich Extension for Inkscape I converted my logo into an embroidery file.


One issue I had was that I mistakenly used a fill function as inkscape was not treating my logo as a single line vector.

This caused my embroidery to be too densly packed with conductive thread, making the circut not complete / infinite.

With no clear start and end point the current takes the path of least resistance.

Even with the above problems I was able to get a pretty good effect with the thermocromatic ink

FUTURE

A quick sketch showing how I might iterate both electronic wearables into a garmet.

Left side: LED epaulette controlled by joystick integrated into breast pocket

Right side: decible counter LCD with electronics integrated into breast pocket