Created
December 13, 2019 09:59
-
-
Save Electronza/5cdb2966112018feb0ac28b312bd4f6c to your computer and use it in GitHub Desktop.
Arduino: Color and UV sensing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************* | |
____ __ ____ ___ ____ ____ __ __ _ ____ __ | |
( __)( ) ( __)/ __)(_ _)( _ \ / \ ( ( \(__ ) / _\ | |
) _) / (_/\ ) _)( (__ )( ) /( 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