Skip to content

12. Skin Electronics

Portada

Skin electronics have become popular for several key reasons that are transforming both technology and the way we interact with the world around us. Some of the main reasons for their growing popularity include:

  • Comfort and Discretion: Devices designed to adhere to the skin are typically lightweight, flexible, and discreet. This means people can wear them all day without feeling uncomfortable or like they’re using a bulky device. Skin integration also makes them invisible to others, which adds to their appeal.

  • Continuous Health Monitoring: Advances in skin electronics technology allow for continuous monitoring of biometric signals (such as heart rate, glucose levels, body temperature, etc.), which is extremely useful in the healthcare field for both patients and healthcare professionals. This enables a more personalized and preventive approach to healthcare.

  • Applications in Well-being and Sports: Skin electronics devices are also popular among athletes and individuals interested in their health, as they can measure physical performance, muscle activity, or recovery in real-time. This can improve decision-making for optimizing training or healthy lifestyles.

  • Advances in Flexible Materials: Developments in materials such as conductive polymers, nanomaterials, and flexible circuits have allowed these devices to be not only functional but also able to adapt to the body’s variations. This makes them more comfortable, durable, and efficient.

  • Innovation in Interface Technology: These devices offer new ways to interact with technology. Some can detect gestures, touches, or even changes in the skin, opening up new forms of digital interaction that go beyond conventional touchscreens.


@crime.fraiche

NFC-activated LED nail encapsulation ✨

♬ One Wish - Ray J
@davidv.lab

♬ оригінальний звук - davidv.lab


First test (Pulse sensor)

The Schematic

Laser cut

This code uses a pulse sensor to measure the heart rate and makes a set of Neopixels blink according to the pulse rate. Each time a heartbeat is detected, the Neopixels turn red and blink at a speed corresponding to the heart rate (BPM).

Main Features

Define pins and parameters
- The pin where the Neopixels are connected (PIN_NEOPIXELS), the number of Neopixels (NUMPIXELS), and the pin where the pulse sensor is connected (PULSE_SENSOR_PIN) are defined.

Pulse sensor reading
- In the loop(), the pulse sensor value is constantly read to detect heartbeats.

Heartbeat detection
- If the sensor value exceeds a set threshold (threshold), a heartbeat is counted, and the heart rate (pulseRate) is incremented.

Blink interval calculation
- The heart rate (BPM) is mapped to a blink interval (from 1000 ms to 200 ms). This means that the higher the BPM, the faster the Neopixels will blink.

Control of the Neopixels
- When a heartbeat is detected, the Neopixels light up in red and blink at the speed determined by the BPM.
- It ensures that the blink interval stays within a reasonable range (between 200 ms and 1000 ms).
- After each blink, the Neopixels are turned off for the same duration.

State reset
- If the sensor value drops below the threshold, the pulse state is reset to detect the next heartbeat.


Arduino code

#include <Adafruit_NeoPixel.h>

// Define the pins and parameters
#define PIN_NEOPIXELS D10       // Pin where the Neopixels are connected
#define NUMPIXELS 4            // Number of Neopixels
#define PULSE_SENSOR_PIN D3    // Pin where the Pulse Sensor is connected

// Pulse sensor variables
int pulseValue = 0;         // Value of the pulse reading
int threshold = 550;        // Detection threshold for beats (adjustable)
bool lastPulseState = false; // Previous pulse state (detected/not detected)
int pulseRate = 0;          // Heart rate (BPM)

// Initialize the Neopixel strip
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN_NEOPIXELS, NEO_GRB + NEO_KHZ800);

void setup() {
  Serial.begin(115200);  // To monitor the pulse via the serial port
  strip.begin();
  strip.show();  // Initially turn off the Neopixels
}

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

  // If a heartbeat is detected (exceeds the threshold), turn on the Neopixels
  if (pulseValue > threshold && !lastPulseState) {
    pulseRate++;  // Count a heartbeat
    lastPulseState = true;  // Mark that a heartbeat has been detected
    Serial.print("Heartbeat detected! Pulse rate: ");
    Serial.println(pulseRate);

    // Map the heart rate (BPM) to a blink interval
    int blinkInterval = map(pulseRate, 60, 120, 1000, 200);  // Map the BPM to an interval from 1000ms to 200ms

    // Ensure the interval is within a reasonable range
    blinkInterval = constrain(blinkInterval, 200, 1000);  // Don't allow the interval to be less than 200ms or greater than 1000ms

    // Neopixel blink: Turn on the LEDs
    strip.fill(strip.Color(255, 0, 0));  // Turn the Neopixels red
    strip.show();
    delay(blinkInterval);  // Wait for the blink interval determined by the BPM

    // Turn off the Neopixels
    strip.clear();
    strip.show();
    delay(blinkInterval);  // Wait for the off interval determined by the BPM
  } 
  else if (pulseValue < threshold) {
    lastPulseState = false;  // Reset the state if the value falls below the threshold
  }

  // If there is no signal, keep the Neopixels off
  else {
    strip.clear();
    strip.show();  // Ensure the Neopixels are off
  }
}


Second Test (with thread)

The components

To give the components greater flexibility when attaching them to the skin, I modified the pieces so that the connections were lightweight but effective in conductivity.

  • I soldered pins to the microcontroller to allow for quicker connections and disconnections of the entire system, and I used the inside of a jumper to attach conductive wire.

  • Perhaps the most time-consuming part was creating these small pieces that resemble a kind of shoelace, as I needed a tip that could be soldered to the copper points.

  • The conductive wire couldn’t be soldered on its own, but if you coat it with adhesive copper tape, the solder adheres very well, and the conductivity is excellent.

Laser cut


This code is designed to detect heartbeats using an analog sensor (ARD 366) and display the heart rate through a NeoPixel (RGB LED). The goal of this exercise is to create a system that detects heartbeats using a pulse sensor and visualizes the rhythm through a NeoPixel LED. The LED changes to red and blinks in sync with the heartbeat, and turns off when no heartbeat is detected.

Sensor reading: The code reads the value from the sensor connected to the analog pin A0 using the analogRead() function.

Heartbeat detection: If the read value exceeds a predefined threshold (THRESHOLD), the program assumes a heartbeat has been detected. The LED lights up red (setPixelColor(0, strip.Color(255, 0, 0))) and starts blinking to show the pulse rhythm.

LED blinking: When a heartbeat is detected (i.e., isPulsing is true), the NeoPixel blinks at regular intervals (500 ms), synchronized with the heartbeat, alternating between turning on and off.

End of heartbeat: When the sensor value drops below the threshold, the system stops detecting the heartbeat and the LED returns to its initial color, blue.

Arduino code

#include <Adafruit_NeoPixel.h>

#define PIN        0  // Data pin to which the NeoPixel is connected
#define NUMPIXELS 1  // Number of NeoPixels (in this case 1)
#define SENSOR_PIN A0  // Analog pin to read the ARD 366 sensor
#define THRESHOLD 600  // Detection threshold for the heartbeat (adjust it based on your sensor)

Adafruit_NeoPixel strip(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int lastSensorValue = 0;  // Previous sensor value
bool isPulsing = false;  // State of whether we are detecting the pulse (red blinking)
unsigned long lastPulseTime = 0;  // Time of the last pulse change
unsigned long pulseInterval = 500;  // Blink interval in milliseconds
bool ledState = false;  // LED state (on/off)

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

  // Initialize the NeoPixel
  strip.begin();
  strip.show();  // Ensure the NeoPixel is off at the beginning

  pinMode(SENSOR_PIN, INPUT);  // Set the sensor pin as input

  // At startup, the NeoPixel will be blue
  strip.setPixelColor(0, strip.Color(0, 0, 255));  // Blue
  strip.show();
}

void loop() {
  int sensorValue = analogRead(SENSOR_PIN);  // Read the analog value from the sensor
  Serial.print("Sensor value: ");
  Serial.println(sensorValue);  // Display the sensor value on the serial monitor

  // If the sensor value exceeds the threshold and enough time has passed since the last pulse, we detect a heartbeat.
  if (sensorValue > THRESHOLD && lastSensorValue <= THRESHOLD) {
    isPulsing = true;  // Indicate that we are detecting the pulse

    // Change the color to red and start blinking
    strip.setPixelColor(0, strip.Color(255, 0, 0));  // Red
    strip.show();
  }

  // If the heartbeat is not detected (when the sensor value falls below the threshold)
  if (sensorValue < THRESHOLD && lastSensorValue >= THRESHOLD) {
    isPulsing = false;  // Stop detecting the pulse
    strip.setPixelColor(0, strip.Color(0, 0, 255));  // Blue
    strip.show();
  }

  // If we are detecting the pulse, the NeoPixel should blink
  if (isPulsing) {
    unsigned long currentMillis = millis();

    // Blink in sync with the pulse (every half second)
    if (currentMillis - lastPulseTime >= pulseInterval) {
      lastPulseTime = currentMillis;

      // Toggle the LED state between on and off
      ledState = !ledState;
      if (ledState) {
        strip.setPixelColor(0, strip.Color(255, 0, 0));  // Red when turned on
      } else {
        strip.setPixelColor(0, strip.Color(0, 0, 0));  // Turn off the NeoPixel
      }
      strip.show();  // Update the NeoPixel
    }
  }

  // Update the previous sensor value for the next comparison
  lastSensorValue = sensorValue;

  delay(10);  // Small delay to avoid overly rapid readings
}


Third Test (Thread & adhesive tape)

The purpose of this exercise is to detect the pulse using the pulse sensor and display the detection status through colors on a set of NeoPixels. When a heartbeat is detected, the LEDs light up green, and after 10 seconds of pulse detection, the LED color cycles between green, blue, and yellow.

HOW IT WORKS KEY ELEMENTS
Sensor reading: The pulse sensor connected to pin A0 is read using analogRead(). If the sensor value exceeds the threshold (THRESHOLD), a heartbeat is detected. Color cycle: One of the key aspects of this code is that after detecting a pulse for 10 seconds, the color of the LEDs changes cyclically between green, blue, and yellow. This provides a dynamic visualization of how long the pulse has been detected.
Heartbeat detection: When a heartbeat is detected (i.e., the sensor value exceeds the threshold), the LED turns green to indicate that the heartbeat has been detected. The system also calculates the duration of the heartbeat, and after detecting the pulse for 10 seconds, it changes the color of the NeoPixels. Use of multiple NeoPixels: Although the code uses 3 NeoPixels, they all behave in a synchronized manner to show the same color, reinforcing the visualization as a whole
Color change: Every time a 10-second pulse detection cycle has passed, the system cycles the color of the NeoPixels between green, blue, and yellow in a repetitive sequence. Adjustable threshold: The THRESHOLD value can be adjusted according to the sensitivity of the pulse sensor, allowing the system to be adapted to different measurement conditions.
End of pulse: If the sensor value drops below the threshold, the pulse is considered finished, and the LEDs return to their initial red state.

Arduino code

#include <Adafruit_NeoPixel.h>

#define PIN        D9  // Data pin connected to the NeoPixel strip
#define NUMPIXELS 3  // Number of NeoPixels
#define SENSOR_PIN A0  // Analog pin for reading the pulse sensor
#define THRESHOLD 520  // Pulse detection threshold (adjust it according to your sensor)

Adafruit_NeoPixel strip(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int lastSensorValue = 0;  // Previous sensor value
bool isPulsing = false;  // State indicating if we are detecting the pulse (green when detected)
unsigned long lastPulseTime = 0;  // Time of the last pulse change
unsigned long pulseStartTime = 0;  // Pulse start time
unsigned long pulseDuration = 0;  // Duration of the detected pulse (in milliseconds)
unsigned long colorChangeInterval = 10000;  // Color change interval in milliseconds (10 seconds)

bool ledState = false;  // LED state (on/off)

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

  // Initialize the NeoPixel strip
  strip.begin();
  strip.show();  // Ensure the NeoPixels are off at the beginning

  pinMode(SENSOR_PIN, INPUT);  // Set the sensor pin as input

  // Initially, the NeoPixels will be red
  setRed();  
}

void loop() {
  int sensorValue = analogRead(SENSOR_PIN);  // Read the pulse sensor value
  Serial.print("Sensor value: ");
  Serial.println(sensorValue);  // Print the sensor value to the serial monitor

  // If the sensor value exceeds the threshold and we are not currently detecting a pulse
  if (sensorValue > THRESHOLD && lastSensorValue <= THRESHOLD) {
    isPulsing = true;  // Indicate that a pulse has been detected
    pulseStartTime = millis();  // Record the start time of the pulse
    Serial.println("Pulse detected!");
  }

  // If the sensor value drops below the threshold (end of the pulse)
  if (sensorValue < THRESHOLD && lastSensorValue >= THRESHOLD) {
    isPulsing = false;  // Stop detecting the pulse
    setRed();  // Change to red when no pulse is detected
    Serial.println("Pulse ended");
  }

  // If we are detecting a pulse, calculate the pulse duration
  if (isPulsing) {
    pulseDuration = millis() - pulseStartTime;  // Calculate how long the pulse has been going on

    // If more than 10 seconds have passed since the start of the pulse, change the color
    if (pulseDuration >= colorChangeInterval) {
      changeColor();  // Change the color of the NeoPixels
      pulseStartTime = millis();  // Reset the timer
    }
  }

  // Save the sensor value for the next iteration
  lastSensorValue = sensorValue;

  delay(10);  // Small delay to prevent too rapid readings
}

// Function to set the NeoPixels to red
void setRed() {
  for (int i = 0; i < NUMPIXELS; i++) {
    strip.setPixelColor(i, strip.Color(255, 0, 0));  // Red
  }
  strip.show();
}

// Function to set the NeoPixels to green
void setGreen() {
  for (int i = 0; i < NUMPIXELS; i++) {
    strip.setPixelColor(i, strip.Color(0, 255, 0));  // Green
  }
  strip.show();
}

// Function to turn off the NeoPixels
void setOff() {
  for (int i = 0; i < NUMPIXELS; i++) {
    strip.setPixelColor(i, strip.Color(0, 0, 0));  // Turn off
  }
  strip.show();
}

// Function to change the color of the NeoPixels every 10 seconds of detected pulse
void changeColor() {
  static int colorCycle = 0;  // Color cycle

  // Change the color every time 10 seconds of pulse are detected
  if (colorCycle == 0) {
    strip.setPixelColor(0, strip.Color(0, 255, 0));  // Green
    strip.setPixelColor(1, strip.Color(0, 255, 0));  // Green
    strip.setPixelColor(2, strip.Color(0, 255, 0));  // Green
    colorCycle++;
  } else if (colorCycle == 1) {
    strip.setPixelColor(0, strip.Color(0, 0, 255));  // Blue
    strip.setPixelColor(1, strip.Color(0, 0, 255));  // Blue
    strip.setPixelColor(2, strip.Color(0, 0, 255));  // Blue
    colorCycle++;
  } else if (colorCycle == 2) {
    strip.setPixelColor(0, strip.Color(255, 255, 0));  // Yellow
    strip.setPixelColor(1, strip.Color(255, 255, 0));  // Yellow
    strip.setPixelColor(2, strip.Color(255, 255, 0));  // Yellow
    colorCycle = 0;  // Reset the color cycle
  }
  strip.show();
}



FINAL RESULT

In this final exercise, the goal is to detect the heartbeat and, based on its presence or absence, control a set of RGB LEDs (NeoPixels) to create a dynamic visualization. When a pulse is detected, the LEDs turn red and change state over time. If no pulse is detected for a specific time (800 ms), the LEDs turn green, and after a waiting period (1000 ms), the system resets the detection.

HOW IT WORKS KEY ELEMENTS
Pulse sensor reading: The sensor value is read through an analog pin (SENSOR_PIN), and if the value exceeds the threshold (THRESHOLD), a heartbeat is detected. Management of time without pulse: The code implements explicit management of time without detecting a heartbeat, where the system transitions from a red on-cycle to a green state to indicate the absence of pulses. This can be useful in applications where a visual alert is desired to show that no pulse is being detected.
Pulse detection: When a heartbeat is detected, the LEDs light up in red, changing position every 3 seconds to turn on the next LED in the sequence. The LEDs continue lighting up sequentially until all LEDs are lit, at which point the cycle restarts. LED lighting sequence: The cycle of lighting up the LEDs one by one in red when a heartbeat is detected is a striking visual feature that clearly displays the pulse rhythm. This sequential pattern can help interpret the duration or continuity of the pulse.
Time without pulse: If no pulse is detected within the defined interval (NO_PULSE_TIMEOUT, which is 800 ms), all LEDs turn green. This behavior is maintained for a waiting time (GREEN_WAIT_TIME of 1000 ms). Wait intervals: The implementation of wait times (NO_PULSE_TIMEOUT and GREEN_WAIT_TIME) provides a smooth transition between pulse detection and inactivity state, which is useful for preventing false readings or rapid fluctuations of the LEDs.
Restart after green wait time: After the green waiting time, the system turns off the LEDs and restarts the heartbeat detection cycle. This allows the pulse sequence to restart after a defined waiting period.

Arduino code

#include <Adafruit_NeoPixel.h>

#define PIN        D9  // Data pin connected to the NeoPixel strip
#define NUMPIXELS 3  // Number of NeoPixels
#define SENSOR_PIN A0  // Analog pin for reading the pulse sensor
#define THRESHOLD 515 // Pulse detection threshold (set to 400)
#define NO_PULSE_TIMEOUT 800  // Time without detecting a pulse before turning LEDs green (in milliseconds)
#define GREEN_WAIT_TIME 1000  // Time to wait in green before detecting pulses again (in milliseconds)

Adafruit_NeoPixel strip(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int lastSensorValue = 0;  // Previous sensor value
bool isPulsing = false;  // State indicating if a pulse is being detected
unsigned long lastColorChangeTime = 0;  // Time of the last color change
unsigned long lastPulseTime = 0;  // Time of the last detected pulse
unsigned long lastGreenTime = 0;  // Time of the last green color change
unsigned long colorChangeInterval = 900;  // Interval for changing color (in milliseconds)
int currentPixelIndex = 0;  // Index of the current NeoPixel to light up
bool waitingForGreen = false;  // Flag indicating if we are waiting for the green wait time

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

  // Initialize the NeoPixel strip
  strip.begin();
  strip.show();  // Ensure the NeoPixels are off at the beginning

  pinMode(SENSOR_PIN, INPUT);  // Set the sensor pin as input

  // Initially, turn off all the NeoPixels
  for (int i = 0; i < NUMPIXELS; i++) {
    strip.setPixelColor(i, strip.Color(0, 0, 0));  // Turn off LEDs
  }
  strip.show();
}

void loop() {
  int sensorValue = analogRead(SENSOR_PIN);  // Read the pulse sensor value
  Serial.print("Sensor value: ");
  Serial.println(sensorValue);  // Print the sensor value to the serial monitor

  // If the sensor value exceeds the threshold and we are not already detecting a pulse
  if (sensorValue > THRESHOLD && lastSensorValue <= THRESHOLD && !waitingForGreen) {
    isPulsing = true;  // Indicate that a pulse has been detected
    lastPulseTime = millis();  // Record the time of the last pulse
    Serial.println("Pulse detected!");
  }

  // If the sensor value drops below the threshold (end of pulse)
  if (sensorValue < THRESHOLD && lastSensorValue >= THRESHOLD) {
    isPulsing = false;  // Stop detecting the pulse
    Serial.println("Pulse ended");
  }

  // If we are detecting a pulse, change the color of the LEDs every 3 seconds
  if (isPulsing && !waitingForGreen) {
    unsigned long currentMillis = millis();  // Get the current time

    // Check if the color change interval has passed
    if (currentMillis - lastColorChangeTime >= colorChangeInterval) {
      lastColorChangeTime = currentMillis;  // Update the time of the last color change

      // Light up the next NeoPixel in the sequence
      if (currentPixelIndex < NUMPIXELS) {
        strip.setPixelColor(currentPixelIndex, strip.Color(255, 0, 0));  // Red
        strip.show();  // Apply the changes
        currentPixelIndex++;  // Increment the index for the next LED
      } else {
        // If all LEDs are lit, turn them off and restart the cycle
        for (int i = 0; i < NUMPIXELS; i++) {
          strip.setPixelColor(i, strip.Color(0, 0, 0));  // Turn off LEDs
        }
        strip.show();  // Apply the changes
        currentPixelIndex = 0;  // Restart the lighting cycle
      }
    }
  } else {
    // If no pulse is being detected and the no-pulse timeout has passed, turn the LEDs green
    unsigned long currentMillis = millis();
    if (currentMillis - lastPulseTime >= NO_PULSE_TIMEOUT && !waitingForGreen) {
      // If more than NO_PULSE_TIMEOUT (5 seconds) have passed without detecting a pulse, turn the LEDs green
      for (int i = 0; i < NUMPIXELS; i++) {
        strip.setPixelColor(i, strip.Color(0, 255, 0));  // Green
      }
      strip.show();  // Apply the changes
      lastGreenTime = currentMillis;  // Record the time when the LEDs turned green
      waitingForGreen = true;  // Start the green wait time
    }

    // Wait for the necessary green time before detecting pulses again
    if (waitingForGreen && (millis() - lastGreenTime >= GREEN_WAIT_TIME)) {
      waitingForGreen = false;  // End the green wait time
      currentPixelIndex = 0;  // Reset the LED index
      for (int i = 0; i < NUMPIXELS; i++) {
        strip.setPixelColor(i, strip.Color(0, 0, 0));  // Turn off LEDs
      }
      strip.show();  // Apply the changes
    }
  }

  // Save the sensor value for the next iteration
  lastSensorValue = sensorValue;

  delay(10);  // Small delay to prevent too rapid readings
}