Skip to content

Instantly share code, notes, and snippets.

@Electronza
Created December 13, 2019 09:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Electronza/5cdb2966112018feb0ac28b312bd4f6c to your computer and use it in GitHub Desktop.
Save Electronza/5cdb2966112018feb0ac28b312bd4f6c to your computer and use it in GitHub Desktop.
Arduino: Color and UV sensing
/*******************************************************************
____ __ ____ ___ ____ ____ __ __ _ ____ __
( __)( ) ( __)/ __)(_ _)( _ \ / \ ( ( \(__ ) / _\
) _) / (_/\ ) _)( (__ )( ) /( O )/ / / _/ / \
(____)\____/(____)\___) (__) (__\_) \__/ \_)__)(____)\_/\_/
Project name: Arduino: Color and UV sensing
Project page: https://electronza.com/arduino-color-and-uv-sensing/
********************************************************************/
#include <Wire.h>
#include <TCS3471.h>
#include <SPI.h>
// Since library stays away from hardware, user sketch has to take care about
// actual communications. Please note that functions need to be exactly like this,
// return void and accept 3 parameters, 1st - i2c address of slave chip,
// 2nd number of bytes to be written or read and 3rd - pointer to a byte array
// from where to read or where to write respective bytes
// Since TCS3471 chips have 12c fixed address, this can come in handy if you want to
// put multiple chips behind multiplexer or do something like that
void i2cWrite(byte address, byte count, byte* buffer);
void i2cRead(byte address, byte count, byte* buffer);
// This is where TCS3471 object gets created
// See how functions declared get passed to constructor as parameters
TCS3471 TCS3471(i2cWrite, i2cRead);
// Just a variable to tell main loop that interrupt was triggered
volatile bool colorAvailable = false;
//double X;
double Y;
//double Z;
//double x;
//double y;
double n;
double ctemp;
// variables for UV sensing
const int chipSelectPinADC = 9;
const int enablePinUV = 7;
unsigned int adc_h;
unsigned int adc_l;
unsigned int tmp1;
unsigned int tmp2;
unsigned int avrg = 0;
unsigned int count = 0;
float UV_val;
// counter to check when to finish printing
int printXtimes = 0;
void setup()
{
Serial.begin(9600);
// starting i2c interface in master mode
Wire.begin();
// TCS3471 can generate interrupt when color/light detection cycle finishes and/or is over or under set threshold
// To free resources of Arduino connect INT pin of TCS3471 to pin 2 of Arduino and use interrupts!
// NB! Open drain logic in use, which means high voltage = 0, low voltage = 1, thus FALLING edge trigger
attachInterrupt(0,TCS3471Int,FALLING);
// No need to pound the chip if there is no chip, detect() function checks if chip responds and returns true
// if it is and false if it does not
if(!TCS3471.detect())
{
Serial.println("Something is not right. Check your wiring, sensor needs at least VCC, GND, SCL and SDA.");
while (1);
}
// tell TCS3471 to generate interrupts on INT pin
TCS3471.enableInterrupt();
// integration time is time chip takes to measure RGBC values, the longer, the higher precision
// range is from 2.4ms up to 614.4ms, parameter is float and in milliseconds,
// step is 2.4ms, but you can put in any number you like, library will take care of rounding
TCS3471.setIntegrationTime(700.0);
// set wait time between measurements, chip just sits there and waits for approximately that many
// milliseconds and conserves power
// range is from 2.4ms up to 7400ms, from 2.4ms up to 614.4ms step is 2.4ms, from 614.4ms up step is 28.8ms
// library takes care of rounding in this case too
// if set to anything less than 2.4ms, wait time is disabled
TCS3471.setWaitTime(2000.0);
// chip has 4 different analog gain settings - TCS3471_GAIN_1X, TCS3471_GAIN_4X, TCS3471_GAIN_16X and TCS3471_GAIN_60X
// naked chip under regular ambient lighting works ok with 1x gain
TCS3471.setGain(TCS3471_GAIN_1X);
// if C(lear) channel goes above this value, interrupt will be generated
// range is from 0-65535, 16 full bits
// just for demonstration purposes, setting it right above half of full range
TCS3471.interruptHighThreshold(32768);
// similar as above, only C channel has to go below this value
// again, range is from 0-65535
// setting it right below half of full range
// this will ensure that interrupt is generated all the time
TCS3471.interruptLowThreshold(32767);
// interrupt persistence determines how many times in row C channel has to go above high threshold
// or below low threshold for interrupt to be generated
// range is from 1 to 60, from 1-3 step is 1, from 5-60 step is 5
// library takes care of finding closest valid value
TCS3471.interruptPersistence(2);
// once interrupt flag is set in chip, it must be cleared from host
// in case it was left on from previous runs, we clear it here
// otherwise we are not going to get new interrupts
TCS3471.clearInterrupt();
// and finally, when all the parameters are set, let's start measuring color and light
TCS3471.enable();
// UV Sensor
SPI.begin();
SPI.setBitOrder(MSBFIRST);
//SPI.setBitOrder(LSBFIRST);
// Clock frequency is maximum 0.8 MHz @ 2.7V
SPI.setClockDivider(SPI_CLOCK_DIV32);
SPI.setDataMode(SPI_MODE0);
pinMode(chipSelectPinADC, OUTPUT);
// If the device was powered up with the CS pin low,
// it must be brought high and back low to initiate communication.
digitalWrite(chipSelectPinADC, HIGH);
// We set the pin to enable the UV sensor as output
pinMode(enablePinUV, OUTPUT);
// And we enable the UV sensor
digitalWrite(enablePinUV, HIGH);
delay(500);
}
void loop()
{
if (colorAvailable)
{
// check if valid RGBC data is available
// in this scenario this call is redundant
// but for demonstration purposes, let's have it here
if (TCS3471.rgbcValid())
{
// as mentioned previously, interrupt flag has to be cleared from host
TCS3471.clearInterrupt();
// reset Arduino flag too
colorAvailable = false;
// read C(lear) channel data
// range from 0-65535
double clearVal = TCS3471.readCData();
// read R(ed) channel data
// range 0-65535
double redVal = TCS3471.readRData();
// read G(reen) channel data
// range 0-65535
double greenVal = TCS3471.readGData();
// read B(lue) channel data
// range 0-65535
double blueVal = TCS3471.readBData();
// and print it all out
Serial.print("Light is ");
Serial.print(clearVal);
Serial.print(" overall and red is ");
Serial.print(redVal);
Serial.print(" while green is ");
Serial.print(greenVal);
Serial.print(" and blue is ");
Serial.println(blueVal);
// now we compute the color temperature
// First we go to XYX
//X = -0.14282*redVal + 1.54924*greenVal -0.95641*blueVal;
Y = -0.32466*redVal + 1.57837*greenVal -0.73191*blueVal;
//Z = -0.68202*redVal + 0.77073*greenVal + 0.56332*blueVal;
Serial.print("Brightness is ");
Serial.println(Y);
n = (0.23881*redVal + 0.25499*greenVal -0.58291*blueVal) / (0.11109*redVal - 0.85406*greenVal +0.52289 * blueVal);
ctemp = 449*n*n*n + 3525*n*n + 6823.3* n + 5520.33;
// Just for debugging
// Serial.print("n is ");
// Serial.println(n);
Serial.print("Color temperature is ");
Serial.println(ctemp);
for(int i=0; i<16; i++ ){ // Get 12 ADC values
digitalWrite(chipSelectPinADC, LOW); // Select MCP3201
adc_h = SPI.transfer(0x00); // Get first 8 bits of ADC value
adc_l = SPI.transfer(0x00); // Get second 8 bits of ADC value
digitalWrite(chipSelectPinADC, HIGH); // Deselect MCP3201
// Format the acquired value
tmp1 = ((adc_h & 0x1F) << 8); // Store the first 8 bits of the ADC value in temporary variable
tmp2 = ((tmp1 | adc_l) >> 1); // Store the rest of the ADC value bits in temporary variable
avrg += tmp2; // Sum the acquired values
}
avrg >>= 4; // Calculate the average value
// Returns 12-bit ADC result
if ((avrg > 1100) && (avrg < 4095)) {
UV_val = avrg * 3300.0 / 4096.0; // Vref=3300 ; 12bit=4096 ;
UV_val=(UV_val-1000)*8.333; // see figure on page 4 in datasheet
Serial.print("UV sensor reading = ");
Serial.println(UV_val);
// Add New line CR+LF
}
else{
Serial.println("Error. No UV measurement!"); // Add New line CR+LF
}
printXtimes++;
}
}
if (printXtimes > 9)
{
// disable interrupt on Arduino
detachInterrupt(0);
// disable interrupt generation on TCS3471
TCS3471.disableInterrupt();
// and finally disable the chip itself, saves power!
TCS3471.disable();
}
}
// this is function that gets called on interrupt from TCS3471
void TCS3471Int()
{
colorAvailable = true;
}
// implementation of i2cWrite and i2cRead functions for simplest case when there is only one
// TCS2471 chip attached to Arduino's two wire bus
void i2cWrite(byte address, byte count, byte* buffer)
{
Wire.beginTransmission(address);
byte i;
for (i = 0; i < count; i++)
{
#if ARDUINO >= 100
Wire.write(buffer[i]);
#else
Wire.send(buffer[i]);
#endif
}
Wire.endTransmission();
}
void i2cRead(byte address, byte count, byte* buffer)
{
Wire.requestFrom(address, count);
byte i;
for (i = 0; i < count; i++)
{
#if ARDUINO >= 100
buffer[i]= Wire.read();
#else
buffer[i]= Wire.receive();
#endif
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment