Skip to content

9. Wearables

Research

Wearable textiles, represent a revolutionary intersection of fashion, technology, and functionality. These garments integrate electronics, sensors, and conductive materials into fabrics, allowing them to perform functions beyond traditional clothing. The development of wearable textiles opens new possibilities in areas such as health monitoring, fitness tracking, and interactive fashion.

References & Inspiration

Sean Hodgins, known for his work under Idle Hands Dev., is a creative technologist, maker, and artist. He focuses on the intersection of technology, art, and design, with a special interest in digital fabrication, interactive installations, and innovative, functional designs. His projects often feature unique explorations of materials and combine programming with physical objects to create interactive or visually compelling pieces.

One of his notable areas of expertise is working with e-textiles and incorporating electronics into clothing and accessories. He has also delved into the world of open-source tools and platforms, contributing to the maker community by providing tutorials, workshops, and educational resources. His work typically challenges traditional boundaries, showcasing the capabilities of merging craftsmanship with cutting-edge technology.

Hodgins' projects frequently emphasize sustainability and the creative repurposing of everyday materials, contributing to the broader conversation about environmentally responsible design. His projects can be both aesthetically pleasing and practically oriented, appealing to tech enthusiasts, designers, and artists alike.

Process and workflow

For this project, I began by ensuring the design included an appropriate space for the microcontroller, carefully considering its placement and accessibility. I created a dedicated holder with outputs for the pins and an additional section intended for incorporating other input and output components. These sections, which I call "patches," were designed to be modular and adaptable for integration into various garments. My aim was to develop a versatile and functional solution that not only aligns aesthetically with textile design but also allows for easy customization and expandability.

Design and laser cut

I used SolidWorks to create the design. First, I imported and scaled an image of the XIAO RP2040 microcontroller to ensure accurate dimensions for the layout. The design is specifically tailored for use with natural leather of 1.1 mm thickness, chosen for its durability and flexibility, which complements the intended application. This approach allowed me to precisely align the microcontroller and ensure compatibility with the material specifications.

I had previously used this microcontroller during my E-textiles week. Feel free to visit that week to learn more about how I implemented it.

Suggested Settings:

  • Power: Between 50% and 70%.
  • Using too much power may burn the material or leave blackened edges.
  • Speed: Between 10 and 30 mm/s.
  • Slower speeds allow for cleaner cuts but can generate more heat, potentially burning the edges.

Once I confirmed that the cut was suitable for my Xiao, I proceeded to sew the microcontroller and the metallic buttons, which served as pin extensions, using conductive thread. I also ensured that the conductive thread did not overlap with other pins by testing with a multimeter. Since conductive thread tends to unravel easily, I recommend securing it with a final pass of regular thread to prevent it from coming undone.

OLED SSD1306

The OLED SSD1306 is a compact and versatile display module that uses OLED (Organic Light-Emitting Diode) technology and the SSD1306 controller, making it a popular choice for electronics projects due to its low power consumption and ability to display graphics and text. Below are its key features and applications:

Features: OLED Technology:

Does not require backlighting as each pixel emits its own light. Offers excellent contrast with deep blacks and vibrant colors. Energy-efficient, especially for images with many dark pixels. Common Sizes:

Typically available in 0.96-inch sizes with resolutions of 128x64 or 128x32 pixels. Interface:

Supports I²C and SPI protocols, allowing easy integration with microcontrollers like Arduino, ESP32, and Raspberry Pi. Compatibility:

Works with various microcontrollers and programming platforms such as Arduino IDE, MicroPython, and CircuitPython. Voltage:

Operates at 3.3V to 5V, making it suitable for diverse projects. Compact Design:

Ideal for portable or space-constrained projects. Applications: Wearables: Indicators, clocks, or portable gadgets. Prototyping: Graphical interfaces for tech prototypes. IoT Projects: Real-time data displays such as temperature, humidity, or internet stats. Control Panels: Displays for automation systems and robots. Typical Use: A common implementation with Arduino includes:

Connecting the display pins to the microcontroller (via I²C: VCC, GND, SCL, and SDA). Utilizing libraries like Adafruit_SSD1306 and Adafruit_GFX for text and graphics control. Writing code to initialize the display and render text or images.

I chose to use the OLED1306 because I had never worked with one before, and its features intrigued me. I decided to incorporate it as the output device for this project. However, to utilize it effectively, I first needed to understand how it works. To achieve this, I developed a simple code to create a basic animation as a starting point for experimentation.

Arduino Code Viewer

Code Eye

Below is the Arduino IDE code:

#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 drawEye(int state) {
  display.clearDisplay();
  display.drawLine(24, 32, 104, 32, SSD1306_WHITE);
  display.drawLine(24, 32, 64, 12, SSD1306_WHITE);
  display.drawLine(64, 12, 104, 32, SSD1306_WHITE);

  if (state == 0) {
    display.fillCircle(64, 32, 8, SSD1306_WHITE);
  } else if (state == 1) {
    display.drawLine(24, 32, 104, 32, SSD1306_WHITE);
    display.drawLine(24, 33, 104, 33, SSD1306_WHITE);
  } else {
    display.drawLine(24, 32, 104, 32, SSD1306_WHITE);
  }

  display.display();
}

void setup() {
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("Failed to initialize the OLED screen"));
    for (;;);
  }
  display.clearDisplay();
  display.display();
}

void loop() {
  drawEye(0);
  delay(1000);
  drawEye(1);
  delay(200);
  drawEye(2);
  delay(300);
  drawEye(1);
  delay(200);
}
  

OLED SSD1306 + PULSE SENSOR + BUZZER

To complete my project for this week, I used an OLED screen and a buzzer as output devices, and a heart rate sensor as an input device. My goal was to make the heartbeats detected by the sensor trigger an animation of a beating heart on the OLED screen. To achieve this, I first needed to learn how to draw a heart shape on the OLED display.

OLED SSD1306 + PULSE SENSOR

To create the "visual wearable", I used an Arduino heart rate sensor as the INPUT and a 2.5" OLED display module with a 128x64 resolution for the MSP430 STM32 series as the OUTPUT. The purpose of this device is to display a real-time animation of a person's heartbeats after exercising, serving as a visual stimulus symbolizing the reward for physical activity. This tool will be very useful for my long working hours hehehe.

How to draw a heart?

The heart in the code is drawn using basic graphic primitives provided by the Adafruit GFX library, specifically circles and a triangle.

  • The pulse sensor reads the analog signal and returns a value corresponding to your heart rate.
  • The signal is smoothed to eliminate sudden fluctuations and provide a more stable output.
  • The smoothed value is used to adjust the size of the heart animation displayed on the OLED screen. A higher heart rate makes the heart larger, while a lower rate makes it smaller.
  • The heart animation is continuously updated to "pulse" based on changes in the heart rate, providing a visual representation of your heartbeat.

Top Circles: - Two circles are used to form the upper curves of the heart. - They are centered to the left and right of the heart's central point. - The radii of the circles are scaled according to the desired size of the heart.

display.fillCircle(x - size / 2, y - size / 2, size / 2, SSD1306_WHITE); display.fillCircle(x + size / 2, y - size / 2, size / 2, SSD1306_WHITE);

Details:

  • x - size / 2 and x + size / 2: Position the circles to the left and right of the x-center.
  • y - size / 2: Places both circles above the central y-point.
  • size / 2: Defines the circle's radius, which depends on the desired size.

Bottom Triangle:

??? A filled triangle is used to connect the bases of the circles and form the pointed lower part of the heart. "

display.fillTriangle(x - size, y, x + size, y, x, y + size * 1.5, SSD1306_WHITE);

Details:

  • (x - size, y): Defines the left vertex of the triangle.
  • (x + size, y): Defines the right vertex of the triangle.
  • (x, y + size * 1.5): Defines the bottom vertex of the triangle, located below the y-center.

Final Structure: By combining the two circles and the triangle:

  • The circles create the two curved upper halves.
  • The triangle forms the pointed lower part.
  • Together, they shape the silhouette of a heart.

Visual representation:

      ****    ****
    ******  ******
   ****************
    ****************
      ************
        ********
          ****
           **

These are the libraries you need to download and add to the code:

Wire.h:

Description: This library is used for I2C (Inter-Integrated Circuit) communication. It allows the microcontroller (like an Arduino) to communicate with other devices (such as displays or sensors) via this two-wire communication protocol.

Adafruit_GFX.h:

Description: This is a general graphic library that provides functions for drawing on displays. It supports basic graphics like lines, circles, text, etc. It is used alongside other libraries that control specific types of displays (in this case, the SSD1306 OLED display).

Adafruit_SSD1306.h:

Description: This library is specifically designed to control OLED displays based on the SSD1306 chip. It provides functions to initialize the display, draw images, text, and other graphics on the OLED screen, making it easy to create visual interfaces.

Definition of Constants

These lines define the size of the OLED screen (128x64 pixels) and initialize the display object using the Adafruit SSD1306 library.

Code

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

I2C Address

This is the I2C address of the OLED display. The address 0x3C is common in many SSD1306 displays, but it may vary depending on the hardware.

Code

#define SCREEN_ADDRESS 0x3C

Pulse Sensor Configuration

This is the pin where the pulse sensor is connected. The pulse sensor will generate an analog signal that we can read using analogRead().

Code

const int pulsePin = 26; // Pin A0 en Xiao RP2040

Pulse Sensor Variables
  • pulseValue: Stores the current value read from the pulse sensor.
  • smoothedValue: Used to store a smoothed version of the pulse data to make the heart animation more stable.
  • smoothingFactor: A value between 0 and 1 that controls the level of data smoothing. A higher value results in a smoother signal.
  • pulseRate: Stores the pulse rate, i.e., the number of beats per minute (bpm).

Code int pulseValue = 0; int smoothedValue = 0; float smoothingFactor = 0.1; int pulseRate = 60;

loop()

  • pulseValue = analogRead(pulsePin);: Reads the analog value from the pulse sensor.
  • Updates the smoothed value (smoothedValue) using a simple smoothing algorithm. It adjusts the previous value by a percentage determined by the smoothing factor (0.1 in this case).
  • The pulseRate is calculated by mapping the smoothed value to a range of 60 to 120 beats per minute. This is used to control the size of the pulsing heart.
  • drawPulsingHeart(64, 32, pulseRate);: Calls the function to draw the heart on the screen based on the calculated pulse rate.
  • Serial.println(smoothedValue);: Sends the smoothed value to the serial monitor for debugging or visualizing pulse data.
  • delay(50);: Adds a small delay between readings and screen updates to stabilize the animation and prevent overloading the display.

drawPulsingHeart()

This function is responsible for drawing the pulsing heart on the OLED display.

  • The heart's size is calculated based on the pulse rate, where a higher pulse rate results in a larger heart.
  • display.clearDisplay(): Clears the screen before drawing a new frame.
  • display.fillCircle(): Draws two circles (representing the upper parts of the heart).
  • display.fillTriangle(): Draws a triangle representing the lower part of the heart.
  • display.display(): Updates the screen to show the heart.
Arduino Code

Arduino Code

Below is the Arduino code that you can copy:


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

#define SCREEN_WIDTH 12812
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// I2C address
#define SCREEN_ADDRESS 0x3C

// Pulse sensor pin
const int pulsePin = 26; // Pin A0 on Xiao RP2040

// Sensor variables
int pulseValue = 0;
int smoothedValue = 0;
float smoothingFactor = 0.1;
int pulseRate = 60;

void setup() {
  // Initialize serial communication
  Serial.begin(115200);

  // Initialize OLED display
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }
  display.display();
  delay(2000);

  // Configure the pulse sensor pin
  pinMode(pulsePin, INPUT);
}

void loop() {
  // Read the analog value from the pulse sensor
  pulseValue = analogRead(pulsePin);

  // Smooth the readings
  smoothedValue = smoothedValue * (1.0 - smoothingFactor) + pulseValue * smoothingFactor;

  // Map the smoothed value to a pulse rate
  pulseRate = map(smoothedValue, 300, 420, 60, 120);
  pulseRate = constrain(pulseRate, 60, 120);

  // Draw the pulsing heart
  drawPulsingHeart(64, 32, pulseRate);

  // Send smoothed value to the Serial Plotter
  Serial.println(smoothedValue);

  delay(50); // Delay to stabilize readings and animation
}

// Function to draw the pulsing heart
void drawPulsingHeart(int x, int y, int rate) {
  int size = map(rate, 60, 120, 10, 30); // Map the pulse rate to heart size
  display.clearDisplay();
  display.fillCircle(x - size / 2, y - size / 2, size / 2, SSD1306_WHITE); // Left circle of the heart
  display.fillCircle(x + size / 2, y - size / 2, size / 2, SSD1306_WHITE); // Right circle of the heart
  display.fillTriangle(x - size, y, x + size, y, x, y + size * 1.5, SSD1306_WHITE); // Bottom triangle of the heart
  display.display(); // Update the display to show the heart
}
    

```

I had already integrated the buzzer into this code, but compilation errors arose. Therefore, I decided to modify the code.

Arduino Heart Pulse Animation Code

Arduino Heart Pulse Animation Code

This page provides the Arduino code for animating a pulsing heart on an OLED screen, using a pulse sensor and a buzzer for feedback.

Code for Heart Animation with Pulse Sensor


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

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

// I2C address
#define SCREEN_ADDRESS 0x3C

// Pulse sensor pin
const int pulsePin = 26; // A0 pin on Xiao RP2040

// Buzzer pin
const int buzzerPin = 4;

// Sensor variables
int pulseValue = 0;
int smoothedValue = 0;
float smoothingFactor = 0.1;
int pulseRate = 60;

// Buzzer control variables
bool buzzerState = false;
unsigned long lastBuzzerTime = 0;
const unsigned long buzzerInterval = 100; // Buzzer sound duration in milliseconds

void setup() {
  // Initialize Serial
  Serial.begin(115200);

  // Initialize OLED
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }
  display.display();
  delay(2000);

  // Configure pulse sensor pin
  pinMode(pulsePin, INPUT);

  // Configure buzzer pin as output
  pinMode(buzzerPin, OUTPUT);
  digitalWrite(buzzerPin, LOW); // Ensure the buzzer is off initially
}

void loop() {
  // Read analog value from the sensor
  pulseValue = analogRead(pulsePin);

  // Smooth the readings
  smoothedValue = smoothedValue * (1.0 - smoothingFactor) + pulseValue * smoothingFactor;

  // Map the value to a pulse rate
  pulseRate = map(smoothedValue, 300, 420, 60, 120);
  pulseRate = constrain(pulseRate, 60, 120);

  // Draw the pulsing heart
  drawPulsingHeart(64, 32, pulseRate);

  // Activate the buzzer when pulse reaches its peak
  if (pulseRate == 120) {
    playBuzzer();
  }

  // Send data to Serial Plotter
  Serial.println(smoothedValue);

  delay(50);
}

// Function to draw the pulsing heart
void drawPulsingHeart(int x, int y, int rate) {
  int size = map(rate, 60, 120, 10, 30);
  display.clearDisplay();
  display.fillCircle(x - size / 2, y - size / 2, size / 2, SSD1306_WHITE);
  display.fillCircle(x + size / 2, y - size / 2, size / 2, SSD1306_WHITE);
  display.fillTriangle(x - size, y, x + size, y, x, y + size * 1.5, SSD1306_WHITE);
  display.display();
}

// Function to activate the buzzer
void playBuzzer() {
  unsigned long currentTime = millis();

  // Emit a short tone only if enough time has passed since the last tone
  if (currentTime - lastBuzzerTime >= buzzerInterval) {
    digitalWrite(buzzerPin, HIGH); // Turn on the buzzer
    delay(100);                    // Keep the buzzer on for 100 ms
    digitalWrite(buzzerPin, LOW);  // Turn off the buzzer
    lastBuzzerTime = currentTime;  // Update the time of the last tone
  }
}
    

After several attempts, this is the code that turned out to be the most effective and responded best to the tests conducted.

Descripción general

Este código está diseñado para leer datos de un sensor de pulso conectado a un microcontrolador (en este caso, un pin analógico) y mostrar una animación de un corazón palpitante en una pantalla OLED. El tamaño del corazón cambia de acuerdo con la frecuencia del pulso, que se calcula y se mapea a un rango de 60 a 120 pulsaciones por minuto (BPM).

Explicación de las partes clave Inclusión de bibliotecas:

Wire.h: Se utiliza para la comunicación I2C, necesaria para manejar la pantalla OLED. Adafruit_GFX.h y Adafruit_SSD1306.h: Bibliotecas para gestionar la pantalla OLED y sus gráficos. Configuración de la pantalla OLED:

Se definen las dimensiones de la pantalla (SCREEN_WIDTH y SCREEN_HEIGHT). Se inicializa la pantalla y se establece la dirección I2C (SCREEN_ADDRESS). Configuración del sensor de pulso:

pulsePin es el pin analógico donde se conecta el sensor de pulso. pulseValue es la variable para almacenar el valor leído del sensor. pulseRate es la tasa de pulso calculada y mapeada. smoothedValue es la variable usada para almacenar el valor suavizado. smoothingFactor es el factor de suavización que ayuda a estabilizar las lecturas. Función setup():

Inicializa la comunicación serial para la depuración. Configura la pantalla OLED y muestra un mensaje de error si no se inicializa correctamente. Configura el pin del sensor como entrada. Función loop():

Lee el valor analógico del sensor de pulso y lo suaviza utilizando una fórmula de promedio ponderado. Mapea el valor suavizado a un rango de 60 a 120 BPM y lo restringe con constrain() para mantenerlo en un rango lógico. Llama a drawPulsingHeart() para dibujar un corazón palpitante en la pantalla. Imprime el valor suavizado en el monitor serial para la depuración. Introduce un retraso de 50 ms para estabilizar las lecturas y la animación. Función drawPulsingHeart():

Dibuja un corazón en la pantalla OLED, utilizando círculos y un triángulo para formar la figura. El tamaño del corazón se ajusta en función de la tasa de pulso mapeada, lo que crea un efecto de "latido".

Final code

Arduino OLED Heart Animation

Arduino OLED Heart Animation

This page showcases the latest three codes for animating a heart on an OLED screen using Arduino.

1. Original Code


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

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

// I2C address
#define SCREEN_ADDRESS 0x3C

// Pin connected to the pulse sensor
const int pulsePin = 26; // Make sure to use an appropriate analog pin

// Variables for reading the pulse sensor
int pulseValue = 0;
int pulseRate = 60;  // Default pulse rate (adjustable)

// For smoothing the values
int smoothedValue = 0; 
float smoothingFactor = 0.1; // Smoothing factor (0.0 to 1.0)

void setup() {
  // Initialize Serial for the Plotter
  Serial.begin(115200);

  // Initialize the OLED screen
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }

  display.display();
  delay(2000);
  pinMode(pulsePin, INPUT); // Configure the sensor pin as input
}

void loop() {
  // Read the pulse sensor value
  pulseValue = analogRead(pulsePin);

  // Smooth the readings for more stability
  smoothedValue = smoothedValue * (1.0 - smoothingFactor) + pulseValue * smoothingFactor;

  // Calculate the pulse rate (adjust based on real sensor values)
  pulseRate = map(smoothedValue, 300, 700, 60, 120); 
  pulseRate = constrain(pulseRate, 60, 120); // Constrain the pulse rate to a logical range

  // Draw the pulsing heart animation
  drawPulsingHeart(64, 32, pulseRate);

  // Send data to the Serial Plotter
  Serial.println(smoothedValue);

  delay(50); // Delay for stable readings and animation
}

// Function to draw the pulsing heart
void drawPulsingHeart(int x, int y, int rate) {
  int size = map(rate, 60, 120, 10, 30); // Adjust the heart size based on pulse rate

  display.clearDisplay();  // Clear the screen

  // Draw the two top circles
  display.fillCircle(x - size / 2, y - size / 2, size / 2, SSD1306_WHITE);
  display.fillCircle(x + size / 2, y - size / 2, size / 2, SSD1306_WHITE);

  // Draw the bottom part of the heart
  display.fillTriangle(x - size, y, x + size, y, x, y + size * 1.5, SSD1306_WHITE);

  display.display(); // Update the screen
}
    

2. Smooth Animation Code


[INSERT THE SECOND CODE HERE]
    

3. Adjusted for Maximum Pulse


[INSERT THE THIRD CODE HERE]
    

Results


  1. File: XIAO