Flora_Pianoglove.ino
1 // SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries 2 // 3 // SPDX-License-Identifier: MIT 4 5 #include <Wire.h> 6 #include "Adafruit_TCS34725.h" 7 #include <Adafruit_NeoPixel.h> 8 #include "Flora_Pianoglove.h" 9 10 11 // we only play a note when the clear response is higher than a certain number 12 #define CLEARTHRESHHOLD 2000 13 14 #define LOWTONE 1000 15 #define HIGHTONE 2000 16 17 // high C 18 #define LOWKEY 64 19 // double high C 20 #define HIGHKEY 76 21 22 // our RGB -> eye-recognized gamma color 23 byte gammatable[256]; 24 25 // color sensor 26 Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X); 27 // one pixel on pin 6 28 Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, 6, NEO_GRB + NEO_KHZ800); 29 30 31 void setup() { 32 Serial.begin(9600); 33 Serial.println("Color Piano!"); 34 35 if (tcs.begin()) { 36 Serial.println("Found sensor"); 37 } else { 38 Serial.println("No TCS34725 found ... check your connections"); 39 while (1); // halt! 40 } 41 42 strip.begin(); 43 strip.show(); // Initialize all pixels to 'off' 44 45 // thanks PhilB for this gamma table! 46 // it helps convert RGB colors to what humans see 47 for (int i=0; i<256; i++) { 48 float x = i; 49 x /= 255; 50 x = pow(x, 2.5); 51 x *= 255; 52 53 gammatable[i] = x; 54 55 //Serial.println(gammatable[i]); 56 } 57 58 // tone output on OC1A / D9 59 pinMode(9, OUTPUT); 60 // toggle output 61 TCCR1A = _BV(COM1A1); 62 // PWM phase and freq correct 63 TCCR1B = _BV(WGM13) | _BV(CS11); // div by 8 64 setFreq(880); 65 } 66 67 void setFreq(uint16_t f) { 68 if (f == 0) { 69 TCCR1B = 0; // turn off 70 } else { 71 TCCR1B = _BV(WGM13) | _BV(CS11); // div by 8 72 } 73 uint32_t i = F_CPU / 16; // div by 8 and 2 74 i /= f; 75 ICR1 = i; 76 OCR1A = i/2; 77 } 78 79 void loop() { 80 uint16_t clear, red, green, blue; 81 82 tcs.setInterrupt(false); // turn on LED 83 84 delay(60); // takes 50ms to read 85 86 tcs.getRawData(&red, &green, &blue, &clear); 87 88 tcs.setInterrupt(true); // turn off LED 89 90 // not close enough to colorful item 91 if (clear < CLEARTHRESHHOLD) { 92 setFreq(0); 93 strip.setPixelColor(0, strip.Color(0, 0, 0)); // turn off the LED 94 strip.show(); 95 return; 96 } 97 98 Serial.print("C:\t"); Serial.print(clear); 99 Serial.print("\tR:\t"); Serial.print(red); 100 Serial.print("\tG:\t"); Serial.print(green); 101 Serial.print("\tB:\t"); Serial.print(blue); 102 103 // Figure out some basic hex code for visualization 104 uint32_t sum = red; 105 sum += green; 106 sum += blue; 107 sum = clear; 108 float r, g, b; 109 r = red; r /= sum; 110 g = green; g /= sum; 111 b = blue; b /= sum; 112 r *= 256; g *= 256; b *= 256; 113 if (r > 255) r = 255; 114 if (g > 255) g = 255; 115 if (b > 255) b = 255; 116 117 Serial.print("\t"); 118 Serial.print((int)r, HEX); Serial.print((int)g, HEX); Serial.print((int)b, HEX); 119 Serial.println(); 120 121 122 // OK we have to find the two primary colors 123 // check if blue is smallest. MEME: fix for 'white' 124 float remove, normalize; 125 if ((b < g) && (b < r)) { 126 remove = b; 127 normalize = max(r-b, g-b); 128 } else if ((g < b) && (g < r)) { 129 remove = g; 130 normalize = max(r-g, b-g); 131 } else { 132 remove = r; 133 normalize = max(b-r, g-r); 134 } 135 // get rid of minority report 136 float rednorm = r - remove; 137 float greennorm = g - remove; 138 float bluenorm = b - remove; 139 // now normalize for the highest number 140 rednorm /= normalize; 141 greennorm /= normalize; 142 bluenorm /= normalize; 143 144 Serial.println(); 145 strip.setPixelColor(0, strip.Color(gammatable[(int)r], gammatable[(int)g], gammatable[(int)b])); 146 strip.show(); 147 148 Serial.print(rednorm); Serial.print(", "); 149 Serial.print(greennorm); Serial.print(", "); 150 Serial.print(bluenorm); Serial.print(" "); 151 Serial.println(); 152 153 float rainbowtone = 0; 154 155 if (bluenorm <= 0.1) { 156 // between red and green 157 if (rednorm >= 0.99) { 158 // between red and yellow 159 rainbowtone = 0 + 0.2 * greennorm; 160 } else { 161 // between yellow and green 162 rainbowtone = 0.2 + 0.2 * (1.0 - rednorm); 163 } 164 } else if (rednorm <= 0.1) { 165 // between green and blue 166 if (greennorm >= 0.99) { 167 // between green and teal 168 rainbowtone = 0.4 + 0.2 * bluenorm; 169 } else { 170 // between teal and blue 171 rainbowtone = 0.6 + 0.2 * (1.0 - greennorm); 172 } 173 } else { 174 // between blue and violet 175 if (bluenorm >= 0.99) { 176 // between blue and violet 177 rainbowtone = 0.8 + 0.2 * rednorm; 178 } else { 179 // between teal and blue 180 rainbowtone = 0; 181 } 182 } 183 184 Serial.print("Scalar "); Serial.println(rainbowtone); 185 float keynum = LOWKEY + (HIGHKEY - LOWKEY) * rainbowtone; 186 Serial.print("Key #"); Serial.println(keynum); 187 float freq = pow(2, (keynum - 49) / 12.0) * 440; 188 Serial.print("Freq = "); Serial.println(freq); 189 //Serial.print((int)r ); Serial.print(" "); Serial.print((int)g);Serial.print(" "); Serial.println((int)b ); 190 setFreq(freq); 191 } 192 193 194 195 RgbColor HsvToRgb(HsvColor hsv) 196 { 197 RgbColor rgb; 198 unsigned char region, remainder, p, q, t; 199 200 if (hsv.s == 0) 201 { 202 rgb.r = hsv.v; 203 rgb.g = hsv.v; 204 rgb.b = hsv.v; 205 return rgb; 206 } 207 208 region = hsv.h / 43; 209 remainder = (hsv.h - (region * 43)) * 6; 210 211 p = (hsv.v * (255 - hsv.s)) >> 8; 212 q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8; 213 t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8; 214 215 switch (region) 216 { 217 case 0: 218 rgb.r = hsv.v; rgb.g = t; rgb.b = p; 219 break; 220 case 1: 221 rgb.r = q; rgb.g = hsv.v; rgb.b = p; 222 break; 223 case 2: 224 rgb.r = p; rgb.g = hsv.v; rgb.b = t; 225 break; 226 case 3: 227 rgb.r = p; rgb.g = q; rgb.b = hsv.v; 228 break; 229 case 4: 230 rgb.r = t; rgb.g = p; rgb.b = hsv.v; 231 break; 232 default: 233 rgb.r = hsv.v; rgb.g = p; rgb.b = q; 234 break; 235 } 236 237 return rgb; 238 } 239 240 HsvColor RgbToHsv(RgbColor rgb) 241 { 242 HsvColor hsv; 243 unsigned char rgbMin, rgbMax; 244 245 rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b); 246 rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b); 247 248 hsv.v = rgbMax; 249 if (hsv.v == 0) 250 { 251 hsv.h = 0; 252 hsv.s = 0; 253 return hsv; 254 } 255 256 hsv.s = 255 * long(rgbMax - rgbMin) / hsv.v; 257 if (hsv.s == 0) 258 { 259 hsv.h = 0; 260 return hsv; 261 } 262 263 if (rgbMax == rgb.r) 264 hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin); 265 else if (rgbMax == rgb.g) 266 hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin); 267 else 268 hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin); 269 270 return hsv; 271 }