Skip to content

9. E-Textiles and Wearables II

For this week I decided to make a Warm Scarf. When it is less than 5 Celsius degrees ( Temperature sensor: Input)
it turns ON and warms up changing the color from blue to white.(Thermochromic ink: actuator 1). It also has Neopixels led lights to advice when it is warming and when is not (actuator 2).

The logic of the circuit and coding is: The temperature sensor measures the environment and if it is less than a certain number allows the current flows warming up the Stainless steel wire that is recovered with cotton. The cotton is painted with blue thermochromic ink changing its color to white. Also the neopixels turns on and off depending on the sensor data. (I explain this very detailed .. just keep reading)

To understand the process I divided the project in two. First in Arduino and then Attiny.

Arduino

  • Step 1. Warm up the wire and change the thermochromic ink

I used the following:

  • Hardware
  • 9V power
  • Capacitor 100uf
  • Transistor IRF520N
  • Diode 1N1007
  • Bredboard
  • 100 Kohm Resistance
  • Arduino UNO hardware and Arduino 1.8 software
  • Stainless Steel wire previously recovered with cotton ( using Aurora spinning machine in past classes )
  • Blue thermochromic ink.

Why and how warm up the wire? Well, basically this happens because we are going to use the wire as a resistance. When current goes through and it is difficult for electrons to pass the friction generates heat. This heat is going to be responsible to change the color of the thermochromic ink and warm the fabric of the scarf. To do this the resistance of the stainless steel wire to use must be enough to reach the temperature needed to activate the ink to change, warm the scarf and not burn the wire itself or a component or a person if anyone touches it.

So, what is Ohm´s law? The way I see it, is the relation that exists between the current (I) is flowing in a conductive medium (Resistanc) with a certain power (Voltage) from one point to another.

V (Voltage) = Capacity (the power is needed to move the electrons) I (Current) = Quantity (the amount of electrons moving) R (Resistance) = Difficulty (the hardness for electrons to pass)

Reed more here: What is electric current? Electric basic theory

For my circuit I had to caculate the resistance of the wire to have a current between 500mA and 1A. My voltage is 9V so: Using ohm's law ( V = I x R ) then, ( R = V / I ) wich in my case is ( R = 9V / 0.8 A ) = ( R = 11.25 Ohms ) So I needed a Wire with a resistance of 11.25 Ohms.

I tested different wires. To keep in mind the resistance of a wire depends on its lenght and in its conductive properties. The longest > current. The shortest < current. A tip that I learnt (apart of everything in this class) is: if a wire is too long and its resistance is too much (wich means it does't pass current enough wich also means not heat enough) is possible to cut the wire in half and connect them in parallel so the current amount is duplicated reaching more heat. This happens because the lenght is shorter (the current doesnt have to travel a long way) and also the current has now 4 wires to pass through.

I selected VN 12.8.2.100S from BEKART because its lenght was enough, the resistance was perfect and the thickness is quite good to circuits (I think). Also it was covered with cotton and is so soft at tact.

  • Step 2. Create the circuit.

To understand the conections and every component is better to draw the circuit and ( have the more questions you can ) research.

My drawing

Having all this answers I could start to connecting things.

Identify the PWM pin in Arduino (I used 3). You find them with wave signal. The transistor Gate side conects with the PWM pin because it recieves the digital signal of the duty cycle (this means the period were the current is goint to pass. For example On and Off every percentage value. 255 is 100% always ON and 127 is 50% half ON and half OFF) and the transistor in the Drain side opens the current fluent from the Source side depending on the percentage is received.

That's why the code is just a line for this. It says something like: Give current to pin 3 every 200 frequency. So the Transistor drains current warming up the wire not all the time.. just a little more than 80% of the duty cycle.

analogWrite (3 - 200) 
  • Step 4. Try

Before trying check the circuit again. Check resistances and everything is its right place. Ok.. I conected the arduino and uploaded the code. After 40Sec the wire started feeling warm. It Worked¡¡

  • Step 5. Paint the wire

Using blue thermochromic ink I mixed it with screen base paintig. Usually the ink powder should be 10% of screen base. I used a thin brush and my fingers because the cotton is so delicate. Let it dry for 40 minutes aprox.

  • Then try again the circuit to watch the ink change effect.
  • After 30sec the ink started to change.
  • 50 sec and changed to light blue
  • 70 sec and getting white blue
  • 90 sec and stibilized the color
  • 120 sec and getting clearer
  • 140 sec and the wire was almost white.

Just a few things to know about the thermochromic ink are that changes the color in a temperature of 27 Celsius degrees. So if the the project is to use the paint over the skin keep this in mind because its going to react just by tact. Otherwise the ink must be isolated from the skin touch to don't affect the performance of the project. Also this ink was created to hide images. The idea is to paint over an image so the sun or heat makes it react getting transparent and discovering the image underneath.

Read more about Thermochromic ink and how to make it

  • Step 6. The temperature sensor

I used a LM35 sensor See datasheet. It should be connected with 5V Power, Ground and in a Digital PIN of the Arduino. As we are using a 9V battery we can use Voltage regulator 7805 see datasheet that reduces 9 to 5volts. Now you can use the sensor connected to the same powersource. Also will help with the Attiny.

  • Step 7. Coding the temperature sensor

Define the sensor name and pin. Then in Void Setup define what kind of variable is (INPUT). Use serialPrint (9600) to see the reading data. What happened to me was.. when I connected the sensor I had numbers in the serial but not for the real temperature. So I had to configure it in the coding to read the real Celsius degrees numbers. I found the code sample here I didn't used Farenheit so I deleted it.

Now that we have the sensor data we are ready to configure when the wire should get warm depending on the Celsius degrees. Note: To make it work I used the values the sensor was reading. For this I used the ¨if condition¨ and ¨else¨. It means If the data is this then do something. I used if the temperature is less than 20 then pass current to warm the wire. Else means that other different stop passing current.

const int sensor=A0; 
float tempc; //variable to store temperature in degree Celsius
float vout;  //temporary variable to hold sensor reading


void setup() {

pinMode(sensor,INPUT); // Configuring sensor pin as input
Serial.begin(9600);   

}

void loop() {


 vout=analogRead(sensor); //Reading the value from sensor

vout=(vout*500)/1023;

tempc=vout; // Storing value in Degree Celsius


Serial.print("in DegreeC=");

Serial.print("\t");

Serial.println(tempc);

delay(1000);

  if (tempc <= 29) {
  analogWrite(3, 180); 


  }
else { 
  analogWrite(3, 0 );
}

}

Check the code before upload it. What I learnt was... sometimes it says ¨the variable X is nos defined¨ and this happen rather because is not in the library you are using or that part is written outside the key symbol or the functions are open. ( I was stopped for 1 hour trying to understand what happened and I missed a key symbol to close the function)

¡The coding worked!

After keeping the sensor data less than 20 celsius degrees the wire started warming and changing the color. And when the data was more than 20 the wire was cooling up and the ink was going back to blue.

  • Step 8. Sew the wire in the Scarf.

After the wire works and changes the color I sew it in the white scarf. I choose white background and a word design to have a contrast during the performance. So the change is visually notorious.

  • Step 9. Connect and code Neopixels

For this I connected it in a digital PIN (9) and works with 5V power and ground. To use Neopixels is necessary to use a library. In my case I used FastLed.

At the end.. my ARDUINO code was this one.

 #include <FastLED.h>

const int sensor=A0; 
float tempc; //variable to store temperature in degree Celsius
float vout;  //temporary variable to hold sensor reading

#define LED_PIN     9
#define NUM_LEDS    57
#define BRIGHTNESS  64
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

#define UPDATES_PER_SECOND 100

CRGBPalette16 currentPalette;
TBlendType    currentBlending;

void setup() {

pinMode(sensor,INPUT); // Configuring sensor pin as input
Serial.begin(9600);   

   delay( 3000 ); // power-up safety delay
    FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
    FastLED.setBrightness(  BRIGHTNESS );

    currentPalette = RainbowColors_p;
    currentBlending = LINEARBLEND;

    fill_solid( currentPalette, 16, CRGB::White);

}

void loop() {

//Serial.println("Analog reading = ");
// Serial.println(tsensorReading);  

 vout=analogRead(sensor); //Reading the value from sensor

vout=(vout*500)/1023;

tempc=vout; // Storing value in Degree Celsius

Serial.print("in DegreeC=");

Serial.print("\t");

Serial.println(tempc);

delay(1000);

  if (tempc <= 29) {
  analogWrite(3, 180); 

    fill_solid( currentPalette, 16, CRGB::White);

  }
else { 
  analogWrite(3, 0 );

  fill_solid( currentPalette, 16, CRGB::Black);

}

static uint8_t startIndex = 0;
    startIndex = startIndex + 1; /* motion speed */

    FillLEDsFromPaletteColors( startIndex);

    FastLED.show();
    FastLED.delay(1000 / UPDATES_PER_SECOND);

}

void FillLEDsFromPaletteColors( uint8_t colorIndex)

{
    uint8_t brightness = 255;

    for( int i = 0; i < NUM_LEDS; i++) {
        leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
        colorIndex += 3;
    }
}

and here is a video of how it works.

Wearables - Warm scarf from Ana Correa on Vimeo.

ATtiny85

First I have to say programming this part broke my brain for about 2 days. But I could make it (with our Lab teachers) and this feels like I can do anything from now. At the end.. is not that difficult.

  • Hardware: Tiny AVR Programmer
  • Software: Arduino software
  • Attiny core for Arduino 1.8

Ok. Lets understand what is this. Is a microcontroler like a tiny arduino that's able to be programmable and repeat the orders saved in its 8k memory.

  • Step 1. Connect components to ATtiny85

The thing is it has 8 legs with Power - Ground - and the rest Input and Output. You have to understand first what is what and the names depending on wich INPUTS and what OUTPUTS are you going to use. So, I needed 1 PWM OUTPUT / 1 digital OUTPUT / 1 DIGITAL INPUT. (One of my mistakes were I didn´t connect the ground from the AVR to bredboard). I drew it many times till I understood. Literally.

Now I changed from Arduino Pins to ATtiny85 pins.

  • Sensor: From Arduino PIN A0 to ATtiny85 A2
  • Transistor: From Arduino PIN 3 to ATtiny 1
  • Neopixels: From Arduion PIN 9 to ATtiny 0

  • Step 2. Translate the code from Arduino to ATtiny

For the sensor coding the ATtiny understands the same code as Arduino. The only problem is it doesn't work with serial print so is almost imposible to read the values. I tried with Software serial but the code didn't run. So there was only needed to change the pin to A2.

For the warming wire code in this case is necessary to declare the variable.

pinMode(1, OUTPUT); // Configuring transistor pin as output

For the Neopixels I had to use the Library for ATtiny. I followed this tutorial It costed me to understan the patterns and include it in the condition coding line. I create a pattern with no colors to use it when the scarf is more than X celsius degrees.

At the end my code is this one.

#include <Adafruit_NeoPixel.h> 
/*
 WS2811/Neopixel pattern switcher for ATtiny85 (and Arduino)
 Requires Adafruit NeoPixel Library
 WS2811 Signal, Digital Pin 4
 Button, Digital Pin 0
 GPL v3
*/

// Define
#define NUM_LEDS 6
#define DATA_PIN 0
#define BTN_PIN 0
#define BTN_DELAY 250
#define NUM_PATTERNS 10
#define CTR_THRESH 16

// Init Vars
uint8_t j = 0;
uint8_t pattern=5;
uint8_t buttonState=0;
uint8_t lastPix=0; 
uint8_t myPix=0;
uint8_t direction=1;
uint8_t counter=0;
uint8_t colors[3];
uint32_t setColor=0;
unsigned long mark;

// Start Strip
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);

int sensor = A2; 
float tempc; //variable to store temperature in degree Celsius
float vout;  //temporary variable to hold sensor reading

void setup() {

  pinMode(sensor,INPUT); 
  pinMode(1, OUTPUT); // Configuring transistor pin as output

    pinMode(BTN_PIN, INPUT);     
    strip.begin();
    strip.show(); // Initialize all pixels to 'off'
}


void loop() {

 vout=analogRead(sensor); //Reading the value from sensor

vout=(vout*500)/1023;

tempc=vout; // Storing value in Degree Celsius

if (tempc <= 30) {
 analogWrite(1, 255); 

  pickPattern(3);

}
//    fill_solid( currentPalette, 16, CRGB::White);


else { 
  analogWrite(1, 0 );

 pickPattern(8);

}

}

void pickPattern(uint8_t var) {
      switch (var) {
        case 1:
          // scanner, color and delay - RGB
          scanner(strip.Color(255,0,0),50);
          scanner(strip.Color(200,0,100),50);
          scanner(strip.Color(64,0,200),50);
        break;
        case 2:
          // color wipe random RGB
          colorWipe(strip.Color(random(255), random(255), random(255)),70);
        break;
        case 3:
          // color wave - Hue/Sat/Bright
          // hue low (0-359), high (0-359),rate,extra delay
          wavey(200,240,0.06,0);
       break;
        case 4:
          // rainbow firefly, 1px at random
          colorFirefly(60);
          counter++;
        break;
        case 5:
          // rainbow solid
          rainbow(10);
         counter++;
        break;
        case 6:
           // bounce in and out
           // tail len, counter, delay
           bounceInOut(4,counter,20);
           counter++;
        break;
        case 7:
           // color wipe from center
           colorWipeCenter(strip.Color(255,0,0),100);
           colorWipeCenter(strip.Color(255,64,0),100);
        break;
        case 8:
           // solid color
           colorFast(strip.Color(0,0,0),100);
        break;
        case 9:
          // fade even or odd
          // 0-359 Hue value, even/odd, delay
          fadeEveOdd(200,0,20);
          fadeEveOdd(300,1,20);
break;
        case 10:
          // show rainbow
          rainbowCycle(10);
         break; 
      }
}


/* check button state */
boolean chkBtn(int buttonState) {
   if (buttonState == HIGH && (millis() - mark) > BTN_DELAY) {
       j = 0;
       mark = millis();
       pattern++;
       return true;
    } 
    else { return false; }
}

void colorFirefly(int wait) {
        if(myPix != lastPix) {
          if(counter<CTR_THRESH) {
            float colorV = sin((6.28/30)*(float)(counter)) *255;
            HSVtoRGB((359/CTR_THRESH)*counter, 255, colorV, colors);
            strip.setPixelColor(myPix, colors[0], colors[1], colors[2]);
           strip.show();
           delay(wait);
          } else {
            lastPix=myPix;
            counter=0;
            colorFast(0,0);
          }
        } else {
          myPix=random(0,strip.numPixels());
        }

}

// Fill the dots one after the other with a color
// Modified from Neopixel sketch to break on button press

void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
      if(chkBtn(digitalRead(BTN_PIN))) { break; }
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
}

// color wipe from center
void colorWipeCenter(uint32_t c, uint8_t wait) {
  uint8_t mid=strip.numPixels()/2;
  strip.setPixelColor(mid,c);
  for(uint16_t i=0; i<=strip.numPixels()/2; i++) {
      if(chkBtn(digitalRead(BTN_PIN))) { break; }
      strip.setPixelColor(mid+i, c);
      strip.setPixelColor(mid-i, c);
      strip.show();
      delay(wait);
  }
}

// fast version 
void colorFast(uint32_t c, uint8_t wait) {
    for (uint16_t i = 0; i < strip.numPixels(); i++) {
        strip.setPixelColor(i, c);
    }
    strip.show();
    delay(wait);
}

// Rainbow Cycle, modified from Neopixel sketch to break on button press
void rainbowCycle(uint8_t wait) {
    uint16_t i;

    //  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for (i = 0; i < strip.numPixels(); i++) {
        strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
    // }
}

void rainbow(uint8_t wait) {
    uint16_t i;

    //for(j=0; j<256; j++) {
    for (i = 0; i < strip.numPixels(); i++) {
        strip.setPixelColor(i, Wheel((i + j) & 255));
    }
    strip.show();
    delay(wait);
    // }
}

// scanner

void scanner(uint32_t c,uint8_t wait) {
        for(int i=0; i< strip.numPixels(); i++) {
            if(chkBtn(digitalRead(BTN_PIN))) { break; }

            colorFast(0,0);
            strip.setPixelColor(i,c);
            strip.show();
            delay(wait);
        }
        for(int i=strip.numPixels(); i>0; i--) {
           if(chkBtn(digitalRead(BTN_PIN))) { break; }
            colorFast(0,0);
            strip.setPixelColor(i,c);
            strip.show();
            delay(wait);
        }    
}


// scanner to midpoint
void bounceInOut(uint8_t num, uint8_t start,uint8_t wait) {
  colorFast(0,0);
  uint8_t color=200;
  for(int q=0; q < num; q++) {
    if(strip.numPixels()-start >= 0 && start < NUM_LEDS) {
          strip.setPixelColor(start+q, strip.Color(0,color,0));
          strip.setPixelColor(strip.numPixels()-start-q, strip.Color(0,color,0));
      }   
    color=round(color/2.0);
    strip.show();
    delay(wait);
  }
  if(counter > strip.numPixels()) { counter=0; }
}

void fadeEveOdd(int c1,byte rem,uint8_t wait) {
              for(int j=0; j < CTR_THRESH; j++) {
                      for(int i=0; i< strip.numPixels(); i++) {
                        if(i % 2== rem) {
                           HSVtoRGB(c1,255,(255/CTR_THRESH)*j,colors);
                           strip.setPixelColor(i,colors[0],colors[1],colors[2]);
                         }
                      }           
                      if(chkBtn(digitalRead(BTN_PIN))) { break; }
                      strip.show();
                      delay(wait);
                }
                for(int j=CTR_THRESH; j >= 0; j--) {
                      for(int i=0; i< strip.numPixels(); i++) {
                        if(i % 2== rem) {
                           HSVtoRGB(c1,255,(255/CTR_THRESH)*j,colors);
                           strip.setPixelColor(i,colors[0],colors[1],colors[2]);
                         }
                      }             
                     if(chkBtn(digitalRead(BTN_PIN))) { break; }
                      strip.show();
                      delay(wait);
                } 
}

// twinkle random number of pixels
void twinkleRand(int num,uint32_t c,uint32_t bg,int wait) {
  // set background
   colorFast(bg,0);
   // for each num
   for (int i=0; i<num; i++) {
     strip.setPixelColor(random(strip.numPixels()),c);
   }
  strip.show();
  delay(wait);
}

// sine wave, low (0-359),high (0-359), rate of change, wait
void wavey(int low,int high,float rt,uint8_t wait) {
  float in,out;
  int diff=high-low;
  int step = diff/strip.numPixels();
  for (in = 0; in < 6.283; in = in + rt) {
       for(int i=0; i< strip.numPixels(); i++) {
           out=sin(in+i*(6.283/strip.numPixels())) * diff + low;
           HSVtoRGB(out,255,255,colors);
           strip.setPixelColor(i,colors[0],colors[1],colors[2]);
       }
           strip.show();
           delay(wait);
           if(chkBtn(digitalRead(BTN_PIN))) { break; }
  }
}

// sine wave, intensity
void waveIntensity(float rt,uint8_t wait) {
  float in,level;
  for (in = 0; in < 6.283; in = in + rt) {
       for(int i=0; i< strip.numPixels(); i++) {
         // sine wave, 3 offset waves make a rainbow!
        level = sin(i+in) * 127 + 128;
        // set color level 
        strip.setPixelColor(i,(int)level,0,0);
       }
           strip.show();
           delay(wait);
           if(chkBtn(digitalRead(BTN_PIN))) { break; }
  }
}

// helpers 

uint32_t Wheel(byte WheelPos) {
    if (WheelPos < 85) {
        return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
    } else if (WheelPos < 170) {
        WheelPos -= 85;
        return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
    } else {
        WheelPos -= 170;
        return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
    }
}


// HSV to RGB colors
// hue: 0-359, sat: 0-255, val (lightness): 0-255
// adapted from http://funkboxing.com/wordpress/?p=1366
void HSVtoRGB(int hue, int sat, int val, uint8_t * colors) {
    int r, g, b, base;
    if (sat == 0) { // Achromatic color (gray).
        colors[0] = val;
        colors[1] = val;
        colors[2] = val;
    } else {
        base = ((255 - sat) * val) >> 8;
        switch (hue / 60) {
        case 0:
            colors[0] = val;
            colors[1] = (((val - base) * hue) / 60) + base;
            colors[2] = base;
            break;
        case 1:
            colors[0] = (((val - base) * (60 - (hue % 60))) / 60) + base;
            colors[1] = val;
            colors[2] = base;
            break;
        case 2:
            colors[0] = base;
            colors[1] = val;
            colors[2] = (((val - base) * (hue % 60)) / 60) + base;
            break;
        case 3:
            colors[0] = base;
            colors[1] = (((val - base) * (60 - (hue % 60))) / 60) + base;
            colors[2] = val;
            break;
        case 4:
            colors[0] = (((val - base) * (hue % 60)) / 60) + base;
            colors[1] = base;
            colors[2] = val;
            break;
        case 5:
            colors[0] = val;
            colors[1] = base;
            colors[2] = (((val - base) * (60 - (hue % 60))) / 60) + base;
            break;
        }

    }
}

It is long because of the Neopixels library.

Step 3. Try it

Well. I connected the circuit and suddendly everything started to work. The wire changed its color and the pixels were on and off when we manipulate the temperature. After days of trying it worked finally.

Step 4. Sew the circuit.