Code and schematic at bottom of post. Very low experience with programming. Using an esp32 wroom but I'm not sure the specific model
The design uses TCS34725 colour sensors to detect when an object (in this case, a simple colour filter) is placed in front of it. I built an enclosure for the sensor so that the light input can be controlled. This aspect works perfectly, I can get it to recognise the difference between a red, a blue and no filter, and even serial print "correct" when the red filter is put in (hence solving the puzzle)
The problem however is that when I daisy chain addressable PL9823 LEDs, they all seem to do random things. I want them to show white (255,255,255) when there is no filter (the white light underneath it), red when the red filter is there, blue for the blue filter etc etc, but it just seems to show blue for all the leds no matter what.
I downsized to 1 sensor and 1 LED to debug (the code and schematic example below) but I'm getting the same results. I've triple checked the wiring on the LED and I am getting the correct DIN leg connected to GPIO 15.
Eventually I want to increase the number of detected colours to green, purple, yellow and none (black)
But currently, I just want the LED to be red when the red filter is inserted, repeat for white and blue etc.
The multiplexer is required because there will be 5 colour sensors, the wiring is still in the schematic because I haven't actually disconnected anything yet, I've just changed NUM_SENSORS numSensors to 1 and it is sensing properly. The issue is with the LED
Hope anyone can help!
Edit: When the code checks for 'hasChanged = True' it doesn't seem to be printing (Serial.print(detectedObjects[i]) to the serial monitor the detected object (none, ruby, sapphire etc.), maybe this could be the issue?
Schematic:
Code;
/**
* "Crown Jewels" Escape Room Puzzle, (c) 2023 Playful Technology
* Object detection using multiple TCS34725 colour sensors
* via a TCA9548A I2C multiplexer
*/
// DEFINES
#define ARRAY_SIZE(x) ((sizeof x) / (sizeof *x))
// INCLUDES
// Built-in Arduino library for addressing I2C devices
#include <Wire.h>
// For addressing PL9823 progammable RGB LEDs. See http://fastled.io/
#include <FastLED.h>
// For interfacing with TCS34725 colour sensor. See https://github.com/playfultechnology/TCS34725
#include "TCS34725.h"
// STRUCTS AND ENUMS
// Enumerate each of the possible objects that we want to detect
enum Object {None, Ruby, Sapphire};
// Define a struct to record colour data about each object:
// - "input" RGB and lux component values as recorded by the sensor,
// - "output" RGB values that should be sent to the LED strip
struct Colour {
TCS34725::Color inRGB; // RGB values as detected by TCS34725
float lux; // Input lux as detected by TCS34725
CRGB outRGB; // RGB values to be emitted by PL9823 LEDs
};
// CONSTANTS
// Array to associate each object with its appropriate colour values
// The .inRGB input values were recorded by holding each item up to the sensor and averaging
// the values printed in the serial monitor
const Colour colours[] = {
[None] = { .inRGB={14,19,14}, .lux=550, .outRGB={255,255,255}},
[Ruby] = { .inRGB={141,1,2}, .lux=55, .outRGB={255,0,0} },
[Sapphire] = { .inRGB={1,24,44}, .lux=140, .outRGB={0,0,255}}
};
// Address of TCA9548A multiplexer set by setting A0-A2 lines HIGH/LOW
// LLL=0x70 (default), HLL=0x71, LHL=0x72, HHL=0x73, LLH=0x74, HLH=0x75, LHH=0x76, HHH=0x77
const byte multiplexAddress = 0x70;
const byte numSensors = 1;
const byte ledDataPin = 15; // GPIO pin connected to DataIn of first LED
const byte relayPin = 14; // GPIO pin connected to signal line of relay module
// The objects that should be placed in front of each sensor to solve the puzzle
const Object targetObjects[numSensors] = { Ruby};
// Defines how similar detected colour must be to target colour to be matched
const int threshold = 500;
// GLOBALS
// Initialise an array of TCS sensor objects
TCS34725 tcs[numSensors] = {TCS34725()};
// An array to record the detected colours from each sensor
Object detectedObjects[numSensors];
// Define the array of LED RGB values - one per sensor
CRGB leds[numSensors];
// FUNCTIONS
// Select the appropriate channel from the I2C multiplexer
void selectI2CBus(uint8_t channel){
Wire.beginTransmission(multiplexAddress);
Wire.write(1 << channel);
Wire.endTransmission();
}
// Test the similarity between two colours by the difference in their R,G,B, and Lux components
uint32_t getColourDistance(TCS34725::Color Col1, float lux, Colour Col2) {
return (abs(Col1.r-Col2.inRGB.r) + abs(Col1.g-Col2.inRGB.g) + abs(Col1.b-Col2.inRGB.b) + abs(Col2.lux-lux));
}
void setup(void) {
// Serial interface used for calibration and debugging
Serial.begin(115200);
Serial.println(__FILE__ __DATE__);
// Start the I2C interface
Wire.begin();
// Configure relay to maglock
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, HIGH);
// Initialise the array of colour sensors
for(int i = 0; i < numSensors; i++) {
Serial.print(F("Initialising sensor #"));
Serial.print(i);
// Set the multiplexer to the appropriate channel
selectI2CBus(i);
// Configure sensor
if (tcs[i].attach(Wire)) {
tcs[i].integrationTime(500); // ms
tcs[i].gain(TCS34725::Gain::X01);
Serial.println(F(" OK!"));
}
else {
Serial.println(F(" Failed to initialise TCS34725"));
}
delay(100);
}
// Initialise the LEDs
FastLED.addLeds<PL9823, ledDataPin, RGB>(leds, numSensors).setCorrection(TypicalLEDStrip);
}
void loop(void) {
// Loop over every sensor to see if a new object has been detected
bool hasChanged = false;
for(int i=0; i<numSensors; i++) {
// Select the appropriate channel on the I2C multiplexer
selectI2CBus(i);
// If there is data available to read
if (tcs[i].available()) {
// Retrieve the colour and lux values detected by the sensor
TCS34725::Color colour = tcs[i].color();
float lux = tcs[i].lux();
// DEBUG/CALIBRATION Print sensor values on the serial monitor
char buffer[50];
snprintf(buffer, 50, "Sensor #%d, %.f, %.f, %.f, %.f", i, colour.r, colour.g, colour.b, tcs[i].lux());
Serial.println(buffer);
// What was the last known object detected by this sensor?
Object previousDetectedObject = detectedObjects[i];
// Compare sensor readings to known colours
uint32_t currentMinDelta = threshold;
for(int j=0; j<ARRAY_SIZE(colours); j++){
// Calculate the difference between the detected colour and the correct target colour
uint32_t delta = getColourDistance(colour, lux, colours[j]);
// If this is more similar than the previous best match
if(delta < currentMinDelta) {
// Update the best score
currentMinDelta = delta;
// Update the array of detected objects
detectedObjects[i] = (Object)j;
delay(100);
}
}
// If the detected object is different to the one previously known
if(detectedObjects[i] != previousDetectedObject){
hasChanged = true;
Serial.println("Change detected");
}
}
}
// If the colour detected by at least one sensor has changed
if(hasChanged){
// Check to see if puzzle is solved
bool allCorrect = true;
for(int i=0; i<numSensors; i++) {
// Debug - print an array of the objects detected by each sensor
Serial.print(detectedObjects[i]);
if(i<numSensors-1) {
Serial.print(",");
}
else {
Serial.println("");
}
// Set corresponding LED colour for each sensor
leds[i] = colours[detectedObjects[i]].outRGB;
// If any sensor detects an object different from that in the targetObjects array, the puzzle is not solved
if(detectedObjects[i] != targetObjects[i]) {
allCorrect = false;
}
}
// If we have made it this far and allCorrect is still true, release the maglock
if(allCorrect) { digitalWrite(relayPin, LOW);
Serial.println ("correct"); }
else { digitalWrite(relayPin, HIGH);
}
// Update the LEDs
FastLED.show();
}
delay(500);
Serial.println("");
}