Introduction: Colour Sensing Jewellery, a NeoPixel Wearable

About: eh? PhDork • #tinker #tailor #maker #breaker • I teach computer science, previously taught design innovation and Interactivity • assistive technologies • forgetfulness • wearables • physical computing • popcor…

The video shows a part of the final project!

This neopixel jewellery wearable tutorial takes inspiration from a tutorial on Adafruit to make a neopixel manicure. I loved this manicure idea. Previously, I incorporated NFC chips (with an led) into a manicure, so I wanted to take a new step in a slightly different direction.

I’ve modified items used, construction, and finishing touches.

The RGB LEDs will light up, and we play a sample neo light script or using a reset button, we can activate a colour sensor to display the colour that has been sensed.

Supplies

  • Gemma M0 board
  • Neopixels I’m using a strip of neopixels, and one ‘band’ for a finger
  • Colour sensor, I’m using a Flora sensor: 1356
  • Elasticated band, 2 sizes,
  • thicker on for the wrist for the battery and board
  • thinner for the finger bands but wider than the neopixel strip (I’m using 12mm)
  • Wire, your choice, based on the aesthetics you want. I’ve got some silicone thin black wire because I love the look, movement, and feel of it. I also have some braided 3 wire to try because I like how that looks as well!
  • Croc Clips for the prototyping part
  • Battery, a 3.7V with 150 mAh LiPo or larger but check the size as you’ll be wearing it!
  • (optional) I’m adding a charger circuit to this so I don’t have to remove the battery.
  • You’ll also need basic electronics tools, a soldering iron (I love my TS80 portable solder iron), maybe clamps or masking tape, or helping hands etc, solder, sewing kit or sewing machine with threads etc.
  • (optional) Button to reset the circuit (this was added at the end as I wanted easy access to the reset button)
  • (optional) Lace to complete the look

Step 1: The First Circuit, Gemma M0 and NeoPixels

Let’s start with the simple part of the circuit – we will be mapping:

  • the GND to GND on the NeoPixel strip,
  • Vout to +5V and D1 on the Gemma to Din on the Neo strip.

This was part of my initial planning.

I modified this to have 1 band and not 2 of LEDs. The 1 band had closer NeoPixels so more of them.

The final strip of NeoPixels I went with has a much closer distribution of Neos. It can be more difficult to solder though so keep that in mind.

Step 2: First Steps, Let’s Get Our NeoPixels to Work!

I like to build my circuits up slowly. Component by component. This way I know exactly when something stopped working.

It’s an easier way to find the errors.

For the first part of my circuit, I’m going to get my NeoPixels working in a strip. I can test the pinouts I’m using and the board.

I have a sample strip of 15 Neos that I typically use with croc clips.

The first step is to: hook up your NeoPixel strip to D1 on the Gemma M0, then hook up GND, and lastly the Vout.

Then upload sample code.


Wait – Do you have the Library for NeoPixels installed?


You’ll need to have the Adafruit NeoPixel library installed to get this examples folder with sketches. For a quick tutorial on how to add an item to the library, here is a quick guide to installing Libraries with Arduino to get you up and running. [Beta Arduino 2.0.0 guide]

If you know how to then just go ahead and add the library.

Step 3: Test Your NeoPixels; Run the Strandtest Sketch

Use the default strandtest sketch that you'll have with the installed library:

// A basic everyday NeoPixel strip test program.

// NEOPIXEL BEST PRACTICES for most reliable operation:
// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
//   connect GROUND (-) first, then +, then data.
// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
//   a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
// (Skipping these may work OK on your workbench but can fail in the field)

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN    11

// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 15

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)


// setup() function -- runs once at startup --------------------------------

void setup() {
  // These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
  // Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  // END of Trinket-specific code.

  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();            // Turn OFF all pixels ASAP
  strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
}


// loop() function -- runs repeatedly as long as board is on ---------------

void loop() {
  // Fill along the length of the strip in various colors...
  colorWipe(strip.Color(255,   0,   0), 50); // Red
  colorWipe(strip.Color(  0, 255,   0), 50); // Green
  colorWipe(strip.Color(  0,   0, 255), 50); // Blue

  // Do a theater marquee effect in various colors...
  theaterChase(strip.Color(127, 127, 127), 50); // White, half brightness
  theaterChase(strip.Color(127,   0,   0), 50); // Red, half brightness
  theaterChase(strip.Color(  0,   0, 127), 50); // Blue, half brightness

  rainbow(10);             // Flowing rainbow cycle along the whole strip
  theaterChaseRainbow(50); // Rainbow-enhanced theaterChase variant
}


// Some functions of our own for creating animated effects -----------------

// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
  for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    delay(wait);                           //  Pause for a moment
  }
}

// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
  for(int a=0; a<10; a++) {  // Repeat 10 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      strip.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in steps of 3...
      for(int c=b; c<strip.numPixels(); c += 3) {
        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      strip.show(); // Update strip with new contents
      delay(wait);  // Pause for a moment
    }
  }
}

// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  // Hue of first pixel runs 5 complete loops through the color wheel.
  // Color wheel has a range of 65536 but it's OK if we roll over, so
  // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
  // means we'll make 5*65536/256 = 1280 passes through this outer loop:
  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
      // Offset pixel hue by an amount to make one full revolution of the
      // color wheel (range of 65536) along the length of the strip
      // (strip.numPixels() steps):
      int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
      // strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
      // optionally add saturation and value (brightness) (each 0 to 255).
      // Here we're using just the single-argument hue variant. The result
      // is passed through strip.gamma32() to provide 'truer' colors
      // before assigning to each pixel:
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
    }
    strip.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
  }
}

// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
  int firstPixelHue = 0;     // First pixel starts at red (hue 0)
  for(int a=0; a<30; a++) {  // Repeat 30 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      strip.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in increments of 3...
      for(int c=b; c<strip.numPixels(); c += 3) {
        // hue of pixel 'c' is offset by an amount to make one full
        // revolution of the color wheel (range 65536) along the length
        // of the strip (strip.numPixels() steps):
        int      hue   = firstPixelHue + c * 65536L / strip.numPixels();
        uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      strip.show();                // Update strip with new contents
      delay(wait);                 // Pause for a moment
      firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
    }
  }
}


You’ll need to change 2 items in the code. What pin is your NeoPixel on, and how many pixels are you using?

Which pin on the Arduino is connected to the NeoPixels?

On a Trinket or Gemma we suggest changing this to 1: #define LED_PIN 11

How many NeoPixels are attached to the Arduino? #define LED_COUNT 15

My sample Neopixel string and board with croc clips to test the board and code. I tried several boards, this shows a Lilypad.

Step 4: Next Steps: Adding the Colour Sensor

Again, using the croc clips you’ll add the colour sensor to your circuit. Connect SDA, SCL, GND, and Power.

SDA should go to (A2)D0

SCL to (A1)D2 on your Gemma M0 board

Adding to your circuit. The colour sensor should be simple, with 2 data pins and Power and Ground. (note; this photo is with my older Gemma board, and not the M0)

Again, for this step, I tested just the code to check that my wiring and connection worked, as well as the sensor data. The code is in the example files…

Grab the Library: “Adafruit_TCS34725.h”

Then go to the example files.

You’ll want to choose colorview sketch at the top.

Upload this code to your board. Then run the sketch – you’ll want to open the Serial monitor to view the output.

This is where you can see if the code and sensor are working.


The Colorview Sketch
#include <Wire.h>
#include "Adafruit_TCS34725.h"

// Pick analog outputs, for the UNO these three work well
// use ~560  ohm resistor between Red & Blue, ~1K for green (its brighter)
#define redpin 3
#define greenpin 5
#define bluepin 6
// for a common anode LED, connect the common pin to +5V
// for common cathode, connect the common to ground

// set to false if using a common cathode LED
#define commonAnode true

// our RGB -> eye-recognized gamma color
byte gammatable[256];


Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X);

void setup() {
  Serial.begin(9600);
  //Serial.println("Color View Test!");

  if (tcs.begin()) {
    //Serial.println("Found sensor");
  } else {
    Serial.println("No TCS34725 found ... check your connections");
    while (1); // halt!
  }

  // use these three pins to drive an LED
#if defined(ARDUINO_ARCH_ESP32)
  ledcAttachPin(redpin, 1);
  ledcSetup(1, 12000, 8);
  ledcAttachPin(greenpin, 2);
  ledcSetup(2, 12000, 8);
  ledcAttachPin(bluepin, 3);
  ledcSetup(3, 12000, 8);
#else
  pinMode(redpin, OUTPUT);
  pinMode(greenpin, OUTPUT);
  pinMode(bluepin, OUTPUT);
#endif

  // thanks PhilB for this gamma table!
  // it helps convert RGB colors to what humans see
  for (int i=0; i<256; i++) {
    float x = i;
    x /= 255;
    x = pow(x, 2.5);
    x *= 255;

    if (commonAnode) {
      gammatable[i] = 255 - x;
    } else {
      gammatable[i] = x;
    }
    //Serial.println(gammatable[i]);
  }
}

// The commented out code in loop is example of getRawData with clear value.
// Processing example colorview.pde can work with this kind of data too, but It requires manual conversion to 
// [0-255] RGB value. You can still uncomments parts of colorview.pde and play with clear value.
void loop() {
  float red, green, blue;
  
  tcs.setInterrupt(false);  // turn on LED

  delay(60);  // takes 50ms to read

  tcs.getRGB(&red, &green, &blue);
  
  tcs.setInterrupt(true);  // turn off LED

  Serial.print("R:\t"); Serial.print(int(red)); 
  Serial.print("\tG:\t"); Serial.print(int(green)); 
  Serial.print("\tB:\t"); Serial.print(int(blue));

//  Serial.print("\t");
//  Serial.print((int)red, HEX); Serial.print((int)green, HEX); Serial.print((int)blue, HEX);
  Serial.print("\n");

//  uint16_t red, green, blue, clear;
//  
//  tcs.setInterrupt(false);  // turn on LED
//
//  delay(60);  // takes 50ms to read
//
//  tcs.getRawData(&red, &green, &blue, &clear);
//  
//  tcs.setInterrupt(true);  // turn off LED
//
//  Serial.print("C:\t"); Serial.print(int(clear)); 
//  Serial.print("R:\t"); Serial.print(int(red)); 
//  Serial.print("\tG:\t"); Serial.print(int(green)); 
//  Serial.print("\tB:\t"); Serial.print(int(blue));
//  Serial.println();


#if defined(ARDUINO_ARCH_ESP32)
  ledcWrite(1, gammatable[(int)red]);
  ledcWrite(2, gammatable[(int)green]);
  ledcWrite(3, gammatable[(int)blue]);
#else
  analogWrite(redpin, gammatable[(int)red]);
  analogWrite(greenpin, gammatable[(int)green]);
  analogWrite(bluepin, gammatable[(int)blue]);
#endif
}

Step 5: Output From Serial Monitor If the Sketch Is Working…

When you open the monitor, you will have something similar to the following… your values will differ.

The working sketch is viewed in the Serial Monitor.

Step 6: What Next? Let’s Put It All Together!

If you’ve got both the NeoPixels and the sensor to work separately, then now’s the time to add them in the same code.

#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include <Adafruit_NeoPixel.h>

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN     11

// How many NeoPixels are attached to the Arduino?
#define LED_COUNT  15

// NeoPixel brightness, 0 (min) to 255 (max)
#define BRIGHTNESS 50 // Set BRIGHTNESS to about 1/5 (max = 255)

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// our RGB -> eye-recognized gamma color
byte gammatable[256];


Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X);

void setup() {
  Serial.begin(115200);
  Serial.println("Color View Test!");
  
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  
  if (tcs.begin()) {
    Serial.println("Found sensor");
  } else {
   Serial.println("No TCS34725 found ... check your connections");
    while (1); // halt!
  }
  
  // thanks PhilB for this gamma table!
  // it helps convert RGB colors to what humans see
  for (int i=0; i<256; i++) {
    float x = i;
    x /= 255;
    x = pow(x, 2.5);
    x *= 255;
      
    gammatable[i] = x;      
    Serial.println(gammatable[i]);
  }
  
  for (int i=0; i<3; i++){ //this sequence flashes the first pixel three times as a countdown to the color reading.
    strip.setPixelColor (0, strip.Color(188, 188, 188)); //white, but dimmer-- 255 for all three values makes it blinding!
    strip.show();
    delay(1000);
    strip.setPixelColor (0, strip.Color(0, 0, 0));
    strip.show();
    delay(500);
  }
  
  uint16_t clear, red, green, blue;

  tcs.setInterrupt(false);      // turn on LED

  delay(60);  // takes 50ms to read 
  
  tcs.getRawData(&red, &green, &blue, &clear);

  tcs.setInterrupt(true);  // turn off LED
  
  Serial.print("C:\t"); Serial.print(clear);
  Serial.print("\tR:\t"); Serial.print(red);
  Serial.print("\tG:\t"); Serial.print(green);
  Serial.print("\tB:\t"); Serial.print(blue);

  // Figure out some basic hex code for visualization
  uint32_t sum = red;
  sum += green;
  sum += blue;
  //sum += clear; // clear contains RGB already so no need to re-add it
  
  float r, g, b;
  r = red; r /= sum;
  g = green; g /= sum;
  b = blue; b /= sum;
  r *= 256; g *= 256; b *= 256;
  //Serial.print("\t");
 // Serial.print((int)r, HEX); Serial.print((int)g, HEX); Serial.print((int)b, HEX);
 // Serial.println();

 // Serial.print((int)r ); Serial.print(" "); Serial.print((int)g);Serial.print(" ");  Serial.println((int)b );
  colorWipe(strip.Color(gammatable[(int)r], gammatable[(int)g], gammatable[(int)b]), 0);
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
}

void loop() {

  //loop is empty because it only takes the color reading once on power up. Turn the circuit off and on again to change the color.
    
}

With this code you should now have a NeoPixel that flashes 3 times, indicating it is taking the reading from your colour sensor. Then it will display the colour that was read into the sensor.

Some work better than others so you’ll have to play around with it a little.

Step 7: – Troubleshooting –

I had a few issues with programming the Gemma M0 board…

I couldn’t get anything uploaded, here are a few things you might want to check.

  • What is the board version of your Gemma? The M0 has been updated and works differently than older Gemma boards you might have lying around. (I initially had an old Gemma!)
  • Grab the drivers if you are on Windows. You may need to add a driver to your computer, read about it here: https://learn.adafruit.com/adafruit-gemma-m0/windows-driver-installation
  • Also, sometimes when using a new board, we need to add additional boards through Arduino, using Settings > additional boards manager, there is a place where you can add more boards, you’ll want to copy and paste this text there: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
  • Check in the Boards manager if you have Adafruit AVR boardsAdafruit SAMD boards
  • Lastly – I ended up switching out my USB cable. This actually was what worked in the end. The board got recognised immediately and then I was able to upload.

In the newer version of Arduino, the boards manager is easily accessible by clicking the ‘chip’ icon in the left-hand area. Just search for the board packs.

Hopefully, one or more of these fixed any issues you may have had uploading code to your board. As always, run a small test script to see if it’s all working.

Step 8: Soldering & Sewing

Once your circuit works, it’s time to solder & sew. Sew let’s jump in! <— hehe

I began by checking the connections, how many are on GND, Power etc. For my circuit, I have 4 wires to GND, 4 wires to PWR and 3 wires to Data D1 – for the NeoPixels.

The other connections are 1 wire each for A1 and A2 for the SCL / SDA connections, so 2 more wires.

I measured out a length of thick elastic for a wrist piece. I then added Velcro so it can also work for other people.

Now solder the wires to the Gemma M0 board.

I used generic white silicon wires so it will look cleaner for this as it is a wrist piece and I like the colour against the black band.

Soldering the wires to Gemma M0.

Once it was soldered, I sewed it to the wrist piece. I also sewed around some of the wires to be sure they will stay in place. Invisible thread is great to use so it isn’t very obvious on the other side.

I used a ribbon wire. Silicone cover stranded-core ribbon cable, 28 AWG. I chose silicone because it is soft and looks good too. I separated it into 3 for the 3 ground connections I need, then I did the same for the 3 Data and 3 Power. These are soldered with the white wires from the colour sensor.

Once this board was soldered with the connections, I sewed it to the wrist piece as well.

Now I’ll need to solder it to the other components. Measure out how long you might need for the different parts and just trim the wires. Then I usually ‘tin’ the tips. So once I’ve tinned the wires and put solder on the pads, I solder them together. Test it all works once you’ve soldered the connections.

Note: after doing this, I think in future I’d make it a little bigger than I need to be sure it can expand if thingschange. I added a charging circuit and a larger battery at one point, and it made it a little tighter than it should have been.

Step 9: CHANGES & ADDITIONS

That’s it – you should have a working wearable circuit now. You can sew it to the specification that you’d like. I decided to make some more changes which I’ve detailed below…

Adding more code, modifying, fun times!

I wanted to add more functionality to this circuit. I added a delay after it grabs the colour and then in the loop, it launches code to loop through a whole bunch of fun effects.

I’ve added some links at the end of the post for where you can grab the code, based on what effects you’d like!

Lace added to make it wearable

I’ve added some lace to this to make it more wearable. This just involved buying a piece of lace, I bought one that has several layers, so it covers it like a ‘cuff’. I then used this lace to create 2 cuffs, so that both wrists will be the same. Then one wrist will have the wearable with NeoPixels.

The finished cuff, with a matching cuff for my other arm. Now you can wear it with your other clothes!

Step 10: Adding a Reset Button to the Front

The reset button is hidden a little in my circuit. It’s on the backside because I didn’t want it visible from the front. However, I can’t easily access it. So I added a reset button. This is easily done and there is a pad on the back of the Gemma that you can solder to.

If you want to add a reset button to the front of your wearable, I made a small guide. The short post [adding a reset button] is available through that link – because this post is very big already.

Reset button added – near the colour sensor on the right-hand side.

Step 11: Securing My Solder Joins and Small Wires – Using Nail Resin.

Other additions… I also added some resin to my solder joins as they are very delicate and I don’t want this to break too easily.

I wrote that up as a short post [using resing and UV] to explain the simple process of covering the solder areas with some nail resin and then curing it with a UV light. I'll write these extra posts as an instructable soon!

Step 12: Extras..

My full, altered code with different neopixel effectshttps://christinefarion.com/full-code-for-my-neopixel-jewellery/

http://fritzenmaker.blogspot.com/2017/05/tcs230-rgb-color-sensor.html The colour sensor information with sample code that you might find useful.

https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#HelpfultoolColorPicker A colour picker and instructions on how to get the colours in your code.

https://adrianotiger.github.io/Neopixel-Effect-Generator/ a great webapp that generates the Arduino code for you as you create the effects you want! Love this so much! <3

https://learn.adafruit.com/chameleon-scarf/code Code for using color sensor.

Thanks!

I hope you’ve enjoyed the post – please let me know if you’ve done something similar if you have issues, what projects you’re working on or what you’ve used the Gemma M0 for!

To see more of my projects and wearables, visit http://christinefarion.com, follow me on Instagram, and Twitter is great if you have any questions or comments!

Wearable Electronics items purchased from Tinker Tailor.