/ Flora_Piano_Glove / Flora_Pianoglove / Flora_Pianoglove.ino
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  }