Guide available http://christinefarion.com/2021/10/neopixel-colour-sensing-jewellery-piece/
/**************************************************
* NeoPixel jewellery, colour sensor, Gemma Mo, Charge board, battery
* based on NeoPixel libraries
* Author: Christine Farion
* Site: www.christinefarion.com
* Twitter: @cmoz
* Date: October 2021
* License: CC BY-NC-SAi
*
* using Adafruit NeoPixel Library
*
* Pinout for a Gemma MO board that I used...
* PINOUTS : NEOPIXELS PIN 1
* : SCL/SCK D2
* : SDA D0
*/
/************************************************
#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include <Adafruit_NeoPixel.h>
#include <NeoPatterns.h>
#define INFO // if not defined, no Serial related code should be linked
#define NUM_LEDS 18
//#define TEST_USER_PATTERNS // Activate this to test your own pattern implementation on line 74 ff.
// Which pin on the Arduino is connected to the NeoPixels?
#define PIN_NEOPIXEL_BAR_16 1
// onComplete callback functions
void allPatterns(NeoPatterns *aLedsPtr);
// construct the NeoPatterns instances
#ifdef TEST_USER_PATTERNS
void ownPatterns(NeoPatterns *aLedsPtr);
NeoPatterns bar16 = NeoPatterns(18, PIN_NEOPIXEL_BAR_16, NEO_GRB + NEO_KHZ800, &ownPatterns);
#else
NeoPatterns bar16 = NeoPatterns(18, PIN_NEOPIXEL_BAR_16, NEO_GRB + NEO_KHZ800, &allPatterns);
#endif
#define PIN_NEOPIXEL_BAR_16 1
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN 1
// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 18
// NeoPixel brightness, 0 (min) to 255 (max)
#define BRIGHTNESS 40 // 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!
}
pinMode(LED_BUILTIN, OUTPUT);
#ifdef INFO
Serial.begin(115200);
#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) || defined(ARDUINO_attiny3217)
delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor!
#endif
// Just to know which program is running on my Arduino
Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_NEOPATTERNS));
#endif
bar16.begin(); // This initializes the NeoPixel library.
bar16.ColorWipe(COLOR32(0, 0, 02), 50, 0, REVERSE); // light Blue
#ifdef INFO
Serial.println("started");
#endif
delay(500);
// 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);
delay(15000);
}
// 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);
}
}
// ************************************************************************* LOOP ****************
void loop() {
//TwinkleRandom(15, 100, false);
//FadeInOut(0xB7, 0x00, 0xFE);
//CylonBounce(0xB7, 0x00, 0xFE, 4, 10, 50);
//strip.Color( 0xB7, 0x00, 0xFE );
//delay(150);
bar16.update();
delay(10);
//BouncingBalls(0x00,0x85,0xC7, 3);
//meteorRain(0xff,0xff,0xff,10, 64, true, 30);
/*
colorWipe(strip.Color(255, 0, 0), 20); // Red
colorWipe(strip.Color( 0, 255, 0), 20); // Green
colorWipe(strip.Color( 0, 0, 255), 20); // Blue
rainbow(10);
*/
}
#ifdef TEST_USER_PATTERNS
/************************************************************************************************************
* Put your own pattern code here
* Provided are sample implementation not supporting initial direction DIRECTION_DOWN
* Full implementation of this functions can be found in the NeoPatterns.cpp file of the NeoPatterns library
************************************************************************************************************/
/*
* set all pixel to aColor1 and let a pixel of color2 move through
* Starts with all pixel aColor1 and also ends with it.
*/
void UserPattern1(NeoPatterns *aNeoPatterns, color32_t aPixelColor, color32_t aBackgroundColor, uint16_t aIntervalMillis,
uint8_t aDirection) {
/*
* Sample implementation not supporting DIRECTION_DOWN
*/
aNeoPatterns->ActivePattern = PATTERN_USER_PATTERN1;
aNeoPatterns->Interval = aIntervalMillis;
aNeoPatterns->Color1 = aPixelColor;
aNeoPatterns->LongValue1.BackgroundColor = aBackgroundColor;
aNeoPatterns->Direction = aDirection;
aNeoPatterns->TotalStepCounter = aNeoPatterns->numPixels() + 1;
aNeoPatterns->ColorSet(aBackgroundColor);
aNeoPatterns->show();
aNeoPatterns->lastUpdate = millis();
}
/*
* @return - true if pattern has ended, false if pattern has NOT ended
*/
bool UserPattern1Update(NeoPatterns *aNeoPatterns, bool aDoUpdate) {
/*
* Sample implementation not supporting initial direction DIRECTION_DOWN
*/
if (aDoUpdate) {
if (aNeoPatterns->decrementTotalStepCounterAndSetNextIndex()) {
return true;
}
}
for (uint16_t i = 0; i < aNeoPatterns->numPixels(); i++) {
if (i == aNeoPatterns->Index) {
aNeoPatterns->setPixelColor(i, aNeoPatterns->Color1);
} else {
aNeoPatterns->setPixelColor(i, aNeoPatterns->LongValue1.BackgroundColor);
}
}
return false;
}
/*
* let a pixel of aColor move up and down
* starts and ends with all pixel cleared
*/
void UserPattern2(NeoPatterns *aNeoPatterns, color32_t aColor, uint16_t aIntervalMillis, uint16_t aRepetitions,
uint8_t aDirection) {
/*
* Sample implementation not supporting DIRECTION_DOWN
*/
aNeoPatterns->ActivePattern = PATTERN_USER_PATTERN2;
aNeoPatterns->Interval = aIntervalMillis;
aNeoPatterns->Color1 = aColor;
aNeoPatterns->Direction = aDirection;
aNeoPatterns->Index = 0;
// *2 for up and down. (aNeoPatterns->numPixels() - 1) do not use end pixel twice.
// +1 for the initial pattern with end pixel. + 2 for the first and last clear pattern.
aNeoPatterns->TotalStepCounter = ((aRepetitions + 1) * 2 * (aNeoPatterns->numPixels() - 1)) + 1 + 2;
aNeoPatterns->clear();
aNeoPatterns->show();
aNeoPatterns->lastUpdate = millis();
}
/*
* @return - true if pattern has ended, false if pattern has NOT ended
*/
bool UserPattern2Update(NeoPatterns *aNeoPatterns, bool aDoUpdate) {
/*
* Sample implementation
*/
if (aDoUpdate) {
// clear old pixel
aNeoPatterns->setPixelColor(aNeoPatterns->Index, COLOR32_BLACK);
if (aNeoPatterns->decrementTotalStepCounterAndSetNextIndex()) {
return true;
}
/*
* Next index
*/
if (aNeoPatterns->Direction == DIRECTION_UP) {
// do not use top pixel twice
if (aNeoPatterns->Index == (aNeoPatterns->numPixels() - 1)) {
aNeoPatterns->Direction = DIRECTION_DOWN;
}
} else {
// do not use bottom pixel twice
if (aNeoPatterns->Index == 0) {
aNeoPatterns->Direction = DIRECTION_UP;
}
}
}
/*
* Refresh pattern
*/
if (aNeoPatterns->TotalStepCounter != 1) {
// last pattern is clear
aNeoPatterns->setPixelColor(aNeoPatterns->Index, aNeoPatterns->Color1);
}
return false;
}
/*
* Handler for testing your own patterns
*/
void ownPatterns(NeoPatterns *aLedsPtr) {
static int8_t sState = 0;
uint8_t tDuration = random(20, 120);
uint8_t tColor = random(255);
uint8_t tRepetitions = random(2);
switch (sState) {
case 0:
UserPattern1(aLedsPtr, COLOR32_RED_HALF, NeoPatterns::Wheel(tColor), tDuration, FORWARD);
break;
case 1:
UserPattern2(aLedsPtr, NeoPatterns::Wheel(tColor), tDuration, tRepetitions, FORWARD);
sState = -1; // Start from beginning
break;
default:
Serial.println("ERROR");
break;
}
sState++;
}
#endif // TEST_USER_PATTERNS
/*
* Handler for all pattern
*/
void allPatterns(NeoPatterns *aLedsPtr) {
static int8_t sState = 0;
uint8_t tDuration = random(40, 81);
uint8_t tColor = random(255);
#ifdef INFO
Serial.print("Pin=");
Serial.print(aLedsPtr->getPin());
Serial.print(" Length=");
Serial.print(aLedsPtr->numPixels());
Serial.print(" State=");
Serial.print(sState);
#endif
switch (sState) {
case 0:
// Cylon
aLedsPtr->ScannerExtended(NeoPatterns::Wheel(tColor), 5, tDuration, 2, FLAG_SCANNER_EXT_CYLON);
break;
case 1:
// Heartbeat
aLedsPtr->RainbowCycle(tDuration / 4, (tDuration & DIRECTION_DOWN));
break;
case 2:
// rocket and falling star - 2 times bouncing
// Cylon
aLedsPtr->ScannerExtended(NeoPatterns::Wheel(tColor), 5, tDuration, 2, FLAG_SCANNER_EXT_CYLON);
/*
aLedsPtr->ScannerExtended(NeoPatterns::Wheel(tColor), 7, tDuration, 2,
FLAG_SCANNER_EXT_ROCKET | FLAG_SCANNER_EXT_START_AT_BOTH_ENDS, (tDuration & DIRECTION_DOWN));
*/
break;
case 3:
aLedsPtr->RainbowCycle(tDuration / 4, (tDuration & DIRECTION_DOWN));
break;
case 4:
aLedsPtr->RainbowCycle(tDuration / 4, (tDuration & DIRECTION_DOWN));
break;
case 5:
// Cylon
aLedsPtr->ScannerExtended(NeoPatterns::Wheel(tColor), 5, tDuration, 2, FLAG_SCANNER_EXT_CYLON);
/*
// old TheaterChase
aLedsPtr->Stripes(NeoPatterns::Wheel(tColor), 1, NeoPatterns::Wheel(tColor + 0x80), 2, 2 * aLedsPtr->numPixels(),
tDuration * 2, (tDuration & DIRECTION_DOWN));
*/
break;
case 6:
aLedsPtr->Fade(NeoPatterns::Wheel(tColor), NeoPatterns::Wheel(tColor + 0x80), 64, tDuration);
break;
case 7:
aLedsPtr->ColorWipe(NeoPatterns::Wheel(tColor), tDuration);
break;
case 8:
// clear existing color wipe
aLedsPtr->ColorWipe(COLOR32_BLACK, tDuration, FLAG_DO_NOT_CLEAR, DIRECTION_DOWN);
break;
case 9:
// Multiple falling star
initMultipleFallingStars(aLedsPtr, COLOR32_WHITE_HALF, 7, tDuration / 2, 3, &allPatterns);
break;
case 10:
if ((aLedsPtr->PixelFlags & PIXEL_FLAG_GEOMETRY_CIRCLE) == 0) {
//Fire
aLedsPtr->Fire(tDuration * 2, tDuration / 2);
} else {
// start at both end
aLedsPtr->ScannerExtended(NeoPatterns::Wheel(tColor), 5, tDuration, 0,
FLAG_SCANNER_EXT_START_AT_BOTH_ENDS | FLAG_SCANNER_EXT_VANISH_COMPLETE);
}
sState = -1; // Start from beginning
break;
default:
#ifdef INFO
Serial.println("ERROR");
#endif
break;
}
#ifdef INFO
Serial.print(" ActivePattern=");
aLedsPtr->printPatternName(aLedsPtr->ActivePattern, &Serial);
Serial.print("|");
Serial.print(aLedsPtr->ActivePattern);
Serial.println();
#endif
sState++;
}
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(100); // 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
}
}
void TwinkleRandom(int Count, int SpeedDelay, boolean OnlyOne) {
setAll(0,0,0);
for (int i=0; i<Count; i++) {
setPixel(random(NUM_LEDS),random(0,255),random(0,255),random(0,255));
showStrip();
delay(SpeedDelay);
if(OnlyOne) {
setAll(0,0,0);
}
}
delay(SpeedDelay);
}
void setPixel(int Pixel, byte red, byte green, byte blue) {
#ifdef ADAFRUIT_NEOPIXEL_H
// NeoPixel
strip.setPixelColor(Pixel, strip.Color(red, green, blue));
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
// FastLED
leds[Pixel].r = red;
leds[Pixel].g = green;
leds[Pixel].b = blue;
#endif
}
void setAll(byte red, byte green, byte blue) {
for(int i = 0; i < NUM_LEDS; i++ ) {
setPixel(i, red, green, blue);
}
showStrip();
}
void showStrip() {
#ifdef ADAFRUIT_NEOPIXEL_H
// NeoPixel
strip.show();
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
// FastLED
FastLED.show();
#endif
}
void FadeInOut(byte red, byte green, byte blue){
float r, g, b;
for(int k = 0; k < 256; k=k+1) {
r = (k/256.0)*red;
g = (k/256.0)*green;
b = (k/256.0)*blue;
setAll(r,g,b);
showStrip();
}
for(int k = 255; k >= 0; k=k-2) {
r = (k/256.0)*red;
g = (k/256.0)*green;
b = (k/256.0)*blue;
setAll(r,g,b);
showStrip();
}
}
void CylonBounce(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay){
for(int i = 0; i < NUM_LEDS-EyeSize-2; i++) {
setAll(0,0,0);
setPixel(i, red/10, green/10, blue/10);
for(int j = 1; j <= EyeSize; j++) {
setPixel(i+j, red, green, blue);
}
setPixel(i+EyeSize+1, red/10, green/10, blue/10);
showStrip();
delay(SpeedDelay);
}
delay(ReturnDelay);
for(int i = NUM_LEDS-EyeSize-2; i > 0; i--) {
setAll(0,0,0);
setPixel(i, red/10, green/10, blue/10);
for(int j = 1; j <= EyeSize; j++) {
setPixel(i+j, red, green, blue);
}
setPixel(i+EyeSize+1, red/10, green/10, blue/10);
showStrip();
delay(SpeedDelay);
}
delay(ReturnDelay);
}
void BouncingBalls(byte red, byte green, byte blue, int BallCount) {
float Gravity = -9.81;
int StartHeight = 1;
float Height[BallCount];
float ImpactVelocityStart = sqrt( -2 * Gravity * StartHeight );
float ImpactVelocity[BallCount];
float TimeSinceLastBounce[BallCount];
int Position[BallCount];
long ClockTimeSinceLastBounce[BallCount];
float Dampening[BallCount];
for (int i = 0 ; i < BallCount ; i++) {
ClockTimeSinceLastBounce[i] = millis();
Height[i] = StartHeight;
Position[i] = 0;
ImpactVelocity[i] = ImpactVelocityStart;
TimeSinceLastBounce[i] = 0;
Dampening[i] = 0.90 - float(i)/pow(BallCount,2);
}
while (true) {
for (int i = 0 ; i < BallCount ; i++) {
TimeSinceLastBounce[i] = millis() - ClockTimeSinceLastBounce[i];
Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000;
if ( Height[i] < 0 ) {
Height[i] = 0;
ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i];
ClockTimeSinceLastBounce[i] = millis();
if ( ImpactVelocity[i] < 0.01 ) {
ImpactVelocity[i] = ImpactVelocityStart;
}
}
Position[i] = round( Height[i] * (NUM_LEDS - 1) / StartHeight);
}
for (int i = 0 ; i < BallCount ; i++) {
setPixel(Position[i],red,green,blue);
}
showStrip();
setAll(0,0,0);
}
}
void meteorRain(byte red, byte green, byte blue, byte meteorSize, byte meteorTrailDecay, boolean meteorRandomDecay, int SpeedDelay) {
setAll(0,0,0);
for (int x = 0; x < 5; x++) {
for(int i = 0; i < NUM_LEDS+NUM_LEDS; i++) {
// fade brightness all LEDs one step
for(int j=0; j<NUM_LEDS; j++) {
if( (!meteorRandomDecay) || (random(10)>5) ) {
fadeToBlack(j, meteorTrailDecay );
}
}
// draw meteor
for(int j = 0; j < meteorSize; j++) {
if( ( i-j <NUM_LEDS) && (i-j>=0) ) {
setPixel(i-j, red, green, blue);
}
}
showStrip();
delay(SpeedDelay);
}
}
}
void fadeToBlack(int ledNo, byte fadeValue) {
#ifdef ADAFRUIT_NEOPIXEL_H
// NeoPixel
uint32_t oldColor;
uint8_t r, g, b;
int value;
oldColor = strip.getPixelColor(ledNo);
r = (oldColor & 0x00ff0000UL) >> 16;
g = (oldColor & 0x0000ff00UL) >> 8;
b = (oldColor & 0x000000ffUL);
r=(r<=10)? 0 : (int) r-(r*fadeValue/256);
g=(g<=10)? 0 : (int) g-(g*fadeValue/256);
b=(b<=10)? 0 : (int) b-(b*fadeValue/256);
strip.setPixelColor(ledNo, r,g,b);
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
// FastLED
leds[ledNo].fadeToBlackBy( fadeValue );
#endif
}