TCS3472 color recognition module can distinguish different colors of objects, and map the colors to pitch through algorithm, which is called "color controlled electronic piano".
In practice, the original expression of color is in the form of cie-rgb component, which is a three-dimensional space;
The brightness can be removed by transformation (see evaluation post ා (1)), and the chromaticity, cie-xyz, can be obtained, and the dimension can be reduced to two-dimensional space;
However, the actual pitch is one-dimensional linear, so it is necessary to reduce the cie-xyz dimension again through the algorithm to obtain the corresponding pitch in one-dimensional space.
After the hardware is built, software programming begins. The main framework of the program is Arduino UNO,
The Adafruit_TCS34725 class is used to access the sensor, and tone / ntone is used to generate pitch.
This paper focuses on the algorithm from cie-xyz space to pitch, as shown in the figure below.
In cie-xyz color space, the (x, y) coordinate relative to the white point is converted to the polar coordinate ρ ∠φ by me, that is
x= ρcosφy= ρsinφ
After conversion, ρ corresponds to the key strength, and φ corresponds to the key pitch. regulations:
When ρ is close to zero, it doesn't make a sound, that is, gray white corresponds to silent, and only when the color is bright enough can "play" the pitch.
When ρ is large enough, φ (0-360) will look up the table to correspond to one-dimensional pitch, and different colors will play different pitch notes.
Due to the smoothness of TCS3472 (see evaluation post 2), many different pitches can be called up.
The debugging information of serial port is embedded in the software. When working, the serial port can directly check the ρ and φ data for fine tuning.
// ColorTune: A TCS3472 based Musical Equipment
#include <M5Stack.h>
#include <Adafruit_TCS34725.h>
#include <math.h>
#include "CIE_Types.h"
#include "BUZ_Pitches.h"
/* Connect SCL to analog 5
Connect SDA to analog 4
Connect VDD to 3.3V DC
Connect GROUND to common ground */
/* Initialise with default values (int time = 2.4ms, gain = 1x) */
/* Initialise with specific int time and gain values */
// Available intervals:
// TCS34725_INTEGRATIONTIME_2_4MS
// TCS34725_INTEGRATIONTIME_24MS
// TCS34725_INTEGRATIONTIME_50MS
// TCS34725_INTEGRATIONTIME_101MS
// TCS34725_INTEGRATIONTIME_154MS
// TCS34725_INTEGRATIONTIME_700MS
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_24MS, TCS34725_GAIN_1X);
// Global Vars
static CIE_XYZ g_OrigXY(0, 0);
//---------------------------------------------------------
// Chromaticity Proc.
//---------------------------------------------------------
CIE_XYZ GetCieXY(const CIE_RGB & rgb)
{
// CIERGB to CIEXYZ
double X = (2.7688 * rgb.r) + (1.7517 * rgb.g) + (1.1301 * rgb.b);
double Y = (1.0000 * rgb.r) + (4.5906 * rgb.g) + (0.0601 * rgb.b);
double Z = /*(0 * r) + */ (0.0565 * rgb.g) + (5.5942 * rgb.b);
double S = X+Y+Z;
return CIE_XYZ(X/S, Y/S);
}
//---------------------------------------------------------
// Music Proc.
//---------------------------------------------------------
void PlayNote(float phi)
{
// Play a note, guided by phi(0~360)
uint16_t tmp= (uint16_t)(phi+0.5);
if(tmp<70) M5.Speaker.tone(NOTE_C5);
else if(tmp<90) M5.Speaker.tone(NOTE_D5);
else if(tmp<130) M5.Speaker.tone(NOTE_E5);
else if(tmp<180) M5.Speaker.tone(NOTE_F5);
else if(tmp<225) M5.Speaker.tone(NOTE_G5);
else if(tmp<250) M5.Speaker.tone(NOTE_A5);
else if(tmp<315) M5.Speaker.tone(NOTE_B5);
else M5.Speaker.tone(NOTE_C6);
}
void StopPlay(void)
{
M5.Speaker.mute();;
}
//---------------------------------------------------------
// Arduino Proc.
//---------------------------------------------------------
void setup(void)
{
M5.begin();
Serial.begin(115200);
M5.Lcd.setCursor(20, 0, 2);
// TCS3472 Init
if (!tcs.begin())
{
Serial.println("TCS34725: Failed ... check connections");
while (1);
}
Serial.println("TCS34725: OK");
delay(100);
// Get Average ORIG position
CIE_XYZ sum(0,0);
#define NUM_AVERAGE 10
for(uint8_t i=0; i<NUM_AVERAGE; i++)
{
CIE_RGB rgb;
tcs.getRawData(&rgb.r, &rgb.g, &rgb.b, &rgb.c);
sum+= GetCieXY(rgb);
}
sum/= NUM_AVERAGE;
g_OrigXY= sum;
M5.Lcd.print("ORIG: ");
M5.Lcd.print(g_OrigXY.x, 3);
M5.Lcd.print(", ");
M5.Lcd.print(g_OrigXY.y, 3);
M5.Lcd.println();
// Now we're ready to get readings!
}
void loop(void)
{
// Get Current Color (CIE_XYZ)
CIE_RGB rgb;
tcs.getRawData(&rgb.r, &rgb.g, &rgb.b, &rgb.c);
CIE_XYZ xy= GetCieXY(rgb);
xy-= g_OrigXY; // Abs -> Rel
// Convert (x,y) to Rho<Phi
float rho= sqrt(xy.x*xy.x +xy.y*xy.y); // Radius
float phi= atan2(xy.y, xy.x) *(180/M_PI); // Angle(-180~+180, Valid when Radius>0)
if(phi<0) phi+= 360; // Angle(0~+360)
// When it is Colorful, Do something
#define THR_COLORFUL 0.03 // Colorful Threshold
if(rho>THR_COLORFUL)
{
// It is colorful
PlayNote(phi); // Play a Note
M5.Lcd.setCursor(20, 20, 2);
M5.Lcd.print("(Colorful): ");
M5.Lcd.println(phi, 1);
}
else
{
// It is white or gray
StopPlay(); // Stop Play(=Mute)
M5.Lcd.setCursor(20, 40, 2);
M5.Lcd.print("(Gray)");
static uint8_t cnt= 0;
M5.Lcd.println(cnt++, HEX); // For Debug Effect
}
}
t3486784401
Comments