#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include "LCDIC2.h"
/* Connect SCL to analog 5
Connect SDA to analog 4
Connect VDD to 3.3V DC
Connect GROUND to common ground */
/* Initialise with specific int time and gain values */
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_240MS, TCS34725_GAIN_16X);
LCDIC2 lcd(0x27, 20, 4);
const int buttonPin = 2; // the number of the pushbutton pin
int buttonState = 0;
//Defining some global variables
uint16_t rMa, gMa, bMa, cMa, rMi, gMi, bMi, cMi;
uint16_t rA, gA, bA, cA;
//Light source white point chromaticity coordinates values found on the following data sheet: https://www.farnell.com/datasheets/3195184.pdf
//Has a CCT of 6000k
float Xn_c = 0.313;
float Yn_c = 0.337;
float Zn_c = 1.0 - Xn_c - Yn_c;
//Normalised tristimulus white point values: https://www.opticsthewebsite.com/CIE1931
float Xn = Xn_c / Yn_c;
float Yn = 1.0;
float Zn = 1.0 - Xn_c - Yn_c;
// float Xn = 0.948;
// float Yn = 1;
// float Zn = 1.073;
//Function AveRGBValue has the sensor take 25 readings then average them out
void AveRGBValue(uint16_t *rAve, uint16_t *gAve, uint16_t *bAve, uint16_t *cAve) {
uint32_t count = 25;
uint32_t rT, gT, bT, cT;
uint16_t r, g, b, c;
rT = gT = bT = cT = 0;
for (int i=0; i< count; i++) {
tcs.getRawData(&r, &g, &b, &c);
rT += r;
gT += g;
bT += b;
cT += c;
}
*rAve = rT / count;
*gAve = gT / count;
*bAve = bT / count;
*cAve = cT / count;
}
/* Function turning raw sensor values to RGB values from 0-255
Max values are white RGB values, min are black RGB values*/
void rawTOrgb(uint8_t *r,uint8_t *g,uint8_t *b, uint16_t *RedMax, uint16_t *GreenMax, uint16_t *BlueMax, uint16_t *RedMin, uint16_t *GreenMin, uint16_t *BlueMin) {
uint16_t red_M, green_M, blue_M;
uint16_t red, green, blue, clear;
// tcs.getRawData(&red, &green, &blue, &clear);
AveRGBValue(&rA, &gA, &bA, &cA);
uint32_t sum = cA;
// Avoid divide by zero errors ... if clear = 0 return black
if (cA == 0) {
*r = *g = *b = 0;
return;
}
red_M = map(rA, *RedMin, *RedMax, 0, 65535);
green_M = map(gA, *GreenMin, *GreenMax, 0, 65535);
blue_M = map(bA, *BlueMin, *BlueMax, 0, 65535);
*r = ((uint32_t) red_M << 8) / sum;
*g = ((uint32_t) green_M << 8) / sum;
*b = ((uint32_t) blue_M << 8) / sum;
}
void TwoPointCalibrate(uint16_t *RedMax, uint16_t *GreenMax, uint16_t *BlueMax, uint16_t *ClearMax, uint16_t *RedMin, uint16_t *GreenMin, uint16_t *BlueMin, uint16_t *ClearMin) {
uint16_t r,g,b,c;
//Calibration begins
//Calibrate for white
lcd.print("Place white object");
lcd.setCursor(0, 1);
lcd.print("in front of sensor");
lcd.setCursor(0, 2);
lcd.print("then press a button:");
//Waiting for user go ahead
while (buttonState == LOW) {
// Do Nothing, wait for button press
buttonState = digitalRead(buttonPin);
}
//Collecting white RGB values
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Calibrating...");
AveRGBValue(&rA, &gA, &bA, &cA);
*RedMax = rA;
*GreenMax = gA;
*BlueMax = bA;
*ClearMax = cA;
Serial.print("R: "); Serial.print(*RedMax); Serial.print(" ");
Serial.print("G: "); Serial.print(*GreenMax); Serial.print(" ");
Serial.print("B: "); Serial.print(*BlueMax); Serial.print(" ");
Serial.print("C: "); Serial.print(*ClearMax); Serial.print(" ");
Serial.println(" ");
//Calibrate for black
lcd.clear();
lcd.print("Place black object");
lcd.setCursor(0, 1);
lcd.print("in front of sensor");
lcd.setCursor(0, 2);
lcd.print("then press a button:");
// Removes serial data so program will wait until it recieves user input, will change this out once I have a button
//Waiting for user go ahead
while (buttonState == LOW) {
// Do Nothing, wait for press of button
buttonState = digitalRead(buttonPin);
}
//Collecting white RGB values
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Calibrating...");
// while(Serial.available() > 0) {
// char t = Serial.read();
// }
// while (Serial.available() == 0 ) {
// // Do Nothing
// }
// lcd.clear();
//Collecting Black RGB values
AveRGBValue(&rA, &gA, &bA, &cA);
*RedMin = rA;
*GreenMin = gA;
*BlueMin = bA;
*ClearMin = cA;
Serial.print("R: "); Serial.print(*RedMin); Serial.print(" ");
Serial.print("G: "); Serial.print(*GreenMin); Serial.print(" ");
Serial.print("B: "); Serial.print(*BlueMin); Serial.print(" ");
Serial.print("C: "); Serial.print(*ClearMin); Serial.print(" ");
Serial.println(" ");
}
/*Colour is evaluated through the CIElab system as a higher a* value was an indication of better masticatory performance
deltaE and l*,a*,b* values have a highly linear relationship, so gums with the same deltaE value will have the same LAB values and will be visually the same*/
void RGBtoCIElab(float *X, float *Y, float *Z, float *CIEL, float *CIEa, float *CIEb, float *DeltaE) {
//Converting the RGB values to XYZ trimstimulus counterparts
float fracX, fracY, fracZ;
float Fx, Fy, Fz;
float x, y, z, R, G, B;
uint8_t r, g, b;
float dL, da, db;
float OriL = 75.10;
float Oria = -1.36;
float Orib = -36.00;
rawTOrgb(&r, &g, &b, &rMa, &gMa, &bMa, &rMi, &gMi, &bMi);
lcd.setCursor(0, 0);
lcd.print("R:"); lcd.print((r); lcd.print(" ");
lcd.print("G:"); lcd.print(g); lcd.print(" ");
lcd.print("B:"); lcd.print(b); lcd.print(" ");
lcd.print(" ");
float RAv = r;
float GAv = g;
float BAv = b;
// turning the 0-255 values to 0-1
R = RAv / 255.0;
G = GAv / 255.0;
B = BAv / 255.0;
//Continuity check
Serial.print("value between 0 - 1 R: "); Serial.print(R); Serial.print(" ");
Serial.print("G: "); Serial.print(G); Serial.print(" ");
Serial.print("B: "); Serial.print(B); Serial.print(" ");
Serial.println(" ");
// Using the RGB to XYZ matrix calculation found in the manufacturer datasheet to find the XYZ trimstimulus values
// Manufacturer given
// *X = (-0.14282F * R) + (1.54924F * G) + (-0.95641F * B);
// *Y = (-0.32466F * R) + (1.57837F * G) + (-0.73191F * B);
// *Z = (-0.68202F * R) + (0.77073F * G) + (0.56332F * B);
//Matlab Calc #1
// *X = (1.8057F *R) + (-1.8763F * G) + (0.3264F * B);
// *Y = (0.9311F *R) + (-0.3753F * G) + (0.1306F * B);
// *Z = (0.0846F *R) + (-4.0028F * G) + (1.7191F * B);
//D65 2 degree standard observer matrix
*X = (0.412453 * R) + (0.35758 * G) + (0.180423 * B);
*Y = (0.212671 * R) + (0.715160 * G) + (0.072169 * B);
*Z = (0.019334 * R) + (0.119193 * G) + (0.950227 * B);
Serial.print("Tristimulus Values --- X: "); Serial.print(*X); Serial.print(" ");
Serial.print("Y: "); Serial.print(*Y); Serial.print(" ");
Serial.print("Z: "); Serial.print(*Z); Serial.print(" ");
Serial.println(" ");
// Converting the XYZ trimstimulus values to xyz chromacities values
float Total = *X + *Y + *Z;
x = *X / Total;
y = *Y / Total;
z = *Z / Total;
lcd.setCursor(0, 1);
lcd.print("X:"); lcd.print(x); lcd.print(" ");
lcd.print("Y:"); lcd.print(y); lcd.print(" ");
lcd.print("Z:"); lcd.print(z); lcd.print(" ");
lcd.print(" ");
// Going from CIExyz to CIELab
fracX = *X/Xn;
fracY = *Y/Yn;
fracZ = *Z/Zn;
// if (fracY > 0.008856) {
// *CIEL = pow( 116.0 * (fracY), 0.33333) - 16.0;
// *CIEa = 500 * (pow(fracX, 0.3333) - pow(fracY, 0.3333));
// *CIEb = 200 * (pow(fracY, 0.3333) - pow(fracZ, 0.3333));
// }
// else {
// *CIEL = 903.3 * fracY;
// *CIEa = 500 * (pow(fracX, 0.3333) - pow(fracY, 0.3333));
// *CIEb = 200 * (pow(fracY, 0.3333) - pow(fracZ, 0.3333));
// }
if (fracX > 0.008856) {
Fx = pow(fracX, 0.3333);
}
else {
Fx = ((7.787)*(fracX)) + (0.1379);
}
if (fracY > 0.008856) {
Fy = pow(fracY, 0.3333);
}
else {
Fy = ((7.787)*(fracY)) + (0.1379);
}
if (fracZ > 0.008856) {
Fz = pow(fracZ, 0.3333);
}
else {
Fz = ((7.787)*(fracZ)) + (0.1379);
}
*CIEL = (116.0 * Fy) - 16.0;
*CIEa = 500.0 * (Fx - Fy);
*CIEb = 200.0 * (Fy - Fz);
// Calculating the colour difference
dL = *CIEL - OriL;
da = *CIEa - Oria;
db = *CIEb - Orib;
*DeltaE = pow( (pow(dL,2) + pow(da,2) + pow(db,2) ), 0.5);
}
void setup(void) {
Serial.begin(9600);
pinMode(buttonPin, INPUT);
Serial.println("");
if (tcs.begin() & lcd.begin()) {
lcd.print("Found sensor");
delay(5000);
lcd.clear();
}
else {
lcd.print("No TCS34725 found ... check your connections");
while (1);
}
TwoPointCalibrate(&rMa, &gMa, &bMa, &cMa, &rMi, &gMi, &bMi, &cMi);
}
void loop(void) {
float X, Y, Z;
float CIEL, CIEa, CIEb, dE;
RGBtoCIElab(&X, &Y, &Z, &CIEL, &CIEa, &CIEb, &dE);
lcd.setCursor(0, 2);
lcd.print("L:"); lcd.print(CIEL); lcd.print(" ");
lcd.print("a:"); lcd.print(CIEa); lcd.print(" ");
lcd.print("b:"); lcd.print(CIEb); lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print("dE:"); lcd.print(dE); lcd.print(" ");
lcd.print(" ");
delay(5000);
lcd.clear();
}