(3 Player) Lazer Pointer Mirror Shooter Game, Using TCS3472 Color Sensor's and Multiplexer
by belletje in Circuits > Arduino
83 Views, 0 Favorites, 0 Comments
(3 Player) Lazer Pointer Mirror Shooter Game, Using TCS3472 Color Sensor's and Multiplexer
I made this as a school project; it is a simple game for 3 players, with each player having their own color.
I liked the idea of shooting lasers via mirrors at targets.
The main goal of the game is to get all lamps on all 3 sides to display your color.
It is possible to make a DIY version using only cardboard, tape, and aluminum foil if you have the electronic parts for the Arduino.
The game works best and looks coolest in a dark room.
Supplies
- 3 x RGB LEDs
- 9 x 3.3 ohm resistors
- 3 x color sensors
- 1 x multiplexer
- 1 x step motor
- 1 x Arduino
- Old telephone cord for wiring
- Soldering tin and pin board for making connections between wires
- 1 x red laser pointer
- 1 x green laser pointer
- 1 x blue laser pointer
- Cardboard or thin plastic (Stanley knife or scissors)
- Tape
- Aluminum foil
Sketch
In my initial sketches, I decided to make the lasers handheld and not controlled by the Arduino due to the limited number of outputs available. Additionally, this approach makes the project more interactive and fun.
Make Box
With of each side: 30 cm
height: 15 cm
Make Mirror Hexagon
Height: 8cm
sides: 3cm
Wiring RGB Lights/ Soldering
Make wiring for lights around 1.5 the width of the box
Coding Servo Motors and Lights
This was my initial code to check if changing the light colors would work and if the motor could rotate randomly.
#include <Servo.h>
Servo myServo; // Create a servo object to control the servo motor
const int servoPin = 0; // D0 pin for the servo motor
void setup() {
// Initialize servo motor
myServo.attach(servoPin); // Attach the servo to pin D0
// Initialize RGB LED pins
for (int i = 1; i <= 9; i++) {
pinMode(i, OUTPUT);
digitalWrite(i, LOW); // Ensure all LEDs are off at start
}
}
void loop() {
// Toggle between red, green, and blue for each RGB LED with a delay of 0.5 second
toggleRGB(1, 1); // Red for first RGB LED
toggleRGB(4, 1); // Red for second RGB LED
toggleRGB(7, 1); // Red for third RGB LED
delay(500);
toggleRGB(1, 2); // Green for first RGB LED
toggleRGB(4, 2); // Green for second RGB LED
toggleRGB(7, 2); // Green for third RGB LED
delay(500);
toggleRGB(1, 3); // Blue for first RGB LED
toggleRGB(4, 3); // Blue for second RGB LED
toggleRGB(7, 3); // Blue for third RGB LED
delay(500);
// Move the servo motor randomly
moveRandom();
delay(500); // Adjust delay as needed
}
void toggleRGB(int led, int color) {
// Turn off all LEDs
digitalWrite(led, LOW); // r
digitalWrite(led + 1, LOW); // g
digitalWrite(led + 2, LOW); // b
// Depending on the color value passed, turn on the corresponding color for the specified LED
switch (color) {
case 1: // Red
digitalWrite(led, HIGH); // r
break;
case 2: // Green
digitalWrite(led + 1, HIGH); // g
break;
case 3: // Blue
digitalWrite(led + 2, HIGH); // b
break;
}
}
void moveRandom() {
int currentPosition = myServo.read(); // Get the current position of the servo
int newPosition = random(0, 180); // Generate a random position between 0 and 180 degrees
int speed = random(1, 4); // Generate a random speed between 1 and 3 (adjust as needed)
// Determine the direction (increment or decrement) to reach the new position
int direction = (newPosition > currentPosition) ? 1 : -1;
// Gradually move the servo to the new position with the specified speed
for (int pos = currentPosition; pos != newPosition; pos += direction) {
myServo.write(pos); // Move the servo to the current position
delay(speed * 10); // Wait for the specified speed (adjust as needed)
}
myServo.write(newPosition); // Set the final position after reaching the new position
}
Wiring Sensors
Coding Sensors
First, I researched how the color sensor works and how it communicates the detected color to the Arduino.
#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
/* Example code for the Adafruit TCS34725 breakout library */
/* 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) */
// Adafruit_TCS34725 tcs = Adafruit_TCS34725();
/* Initialise with specific int time and gain values */
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_614MS, TCS34725_GAIN_1X);
void setup(void) {
Serial.begin(9600);
if (tcs.begin()) {
Serial.println("Found sense");
} else {
Serial.println("No TCS34725 found ... check your connections");
while (1);
}
// Now we're ready to get readings!
}
void loop(void) {
uint16_t r, g, b, c, colorTemp, lux;
tcs.getRawData(&r, &g, &b, &c);
// colorTemp = tcs.calculateColorTemperature(r, g, b);
colorTemp = tcs.calculateColorTemperature_dn40(r, g, b, c);
lux = tcs.calculateLux(r, g, b);
Serial.print(WHITE);
Serial.println("ColorSense");
Serial.print("Lux:");
Serial.println(lux, DEC);
Serial.print("RED:");
Serial.println(r, DEC);
Serial.print("GREEN:");
Serial.println(g, DEC);
Serial.print("BLUE:");
Serial.println(b, DEC);
}
I encountered an unexpected problem while trying to get three TCS34725 color sensors to work on the same Arduino. These sensors can only be read on analog pins 4 and 5, which created a challenge since all three sensors have the same I2C address (0x29). This meant they couldn't be connected in parallel directly.
After extensive research, I discovered that using a multiplexer could solve this issue. A multiplexer can receive multiple analog inputs and assign different addresses to them, allowing each sensor to be read separately.
The multiplexer I used created the following addresses for the corresponding pins:
- 0x70 -> SD0/SC0
- 0x71 -> SD1/SC1
- 0x72 -> SD2/SC2
- 0x73 -> SD3/SC3
- 0x74 -> SD4/SC4
- 0x75 -> SD5/SC5
- 0x76 -> SD6/SC6
- 0x77 -> SD7/SC7
I found an example (https://www.hackster.io/sherwinchiu89/multiplexing-6-i2c-tcs34725-color-sensors-2a7272) which demonstrated how to use a multiplexer with TCS34725 sensors. Although my sensors and Arduino were different, the example provided valuable insights into addressing the sensors via the multiplexer.
Adapting the code required considerable trial and error. Debugging was challenging because errors could stem from the code, the sensors, or the wiring. Despite these difficulties, I managed to get the setup working by carefully adjusting the code and ensuring all connections were correct.
Taping Everything Together
I initially put some black tape over the lights from the color sensor to prevent interference with the laser colors. Later, I replaced the tape with black nail polish for a more permanent solution.
Prototype Full Code
My first not completely bug free semi working code (prototype):
//servo
#include <Servo.h>
Servo myServo; // Create a servo object to control the servo motor
const int servoPin = 0; // D0 pin for the servo motor
// colour sensor
#include <Wire.h>
#include "Adafruit_TCS34725.h"
#define MUX_ADDR 0x70 // Multiplexer address
#define SENSORS_COUNT 3 // Number of sensors connected to the multiplexer
Adafruit_TCS34725 tcs; // Create an object for TCS34725 sensor
//#define THRESHOLD 5000 // Adjust the threshold value as needed
int redThreshold = 500;
int greenThreshold = 500;
int blueThreshold = 500;
int player1_color = 0;
int player2_color = 0;
int player3_color = 0;
void setup() {
// servo motor
myServo.attach(servoPin); // Attach the servo to pin D0
// rgb let lights
pinMode(1, OUTPUT); // Pin connected to the red LED of the first RGB LED //D1=Red D2=Geen D3=Blue
pinMode(4, OUTPUT); // Pin connected to the red LED of the second RGB LED //D4=Red D5=Geen D6=Blue
pinMode(7, OUTPUT); // Pin connected to the red LED of the third RGB LED //D7=Red D8=Geen D9=Blue
// colour sensors
// Wire.begin(); // Initialize I2C communication
// // Initialize the TCS34725 sensor
// if (!tcs.begin()) {
// Serial.println("No TCS34725 found ... check your connections");
// while (1);
// }
}
void loop() {
// // Iterate through each sensor connected to the multiplexer
// for (int sensorNum = 0; sensorNum < 3; sensorNum++) {
// // Select the sensor using the multiplexer
// chooseBus(sensorNum);
// // Read data from the selected sensor
// uint16_t r, g, b, c;
// tcs.getRawData(&r, &g, &b, &c);
// // Print the RGB values of the selected sensor
// Serial.print("Sensor ");
// Serial.println(sensorNum + 1); // Sensor numbering starts from 1
// Serial.print("RED: ");
// Serial.println(r);
// Serial.print("GREEN: ");
// Serial.println(g);
// Serial.print("BLUE: ");
// Serial.println(b);
// Serial.println();
// checkColorThreshold(r, g, b, redThreshold, greenThreshold, blueThreshold, sensorNum + 1);
// }
//Toggle between red, green, and blue for each RGB LED with a delay of 1 second
toggleRGB(1, 1); // Red for first RGB LED
toggleRGB(4, 1); // Red for second RGB LED
toggleRGB(7, 1); // Red for third RGB LED
delay(500);
toggleRGB(1, 2); // Green for first RGB LED
toggleRGB(4, 2); // Green for second RGB LED
toggleRGB(7, 2); // Green for third RGB LED
delay(500);
toggleRGB(1, 3); // Blue for first RGB LED
toggleRGB(4, 3); // Blue for second RGB LED
toggleRGB(7, 3); // Blue for third RGB LED
delay(500);
moveRandom();
//delay(500); // Wait before moving again
}
void chooseBus(int busNum) {
Wire.beginTransmission(MUX_ADDR);
Wire.write(1 << (busNum + 2)); // Set the appropriate bit for selecting the sensor channel
Wire.endTransmission();
}
void toggleRGB(int led, int color)
{
// Turn off all LEDs
// Turn off all LEDs
digitalWrite(led, LOW); // r
digitalWrite(led + 1, LOW); // g
digitalWrite(led + 2, LOW); // b
//digitalWrite(led, LOW); // Turn off the specified LED
//delay(10);
// Depending on the color value passed, turn on the corresponding color for the specified LED
switch(color)
{
case 1: // Red
digitalWrite(led, HIGH);// r
// digitalWrite(led + 1, LOW); // g
// digitalWrite(led + 2, LOW); // b
break;
case 2: // Green
digitalWrite(led + 1, HIGH); // The green pin is one higher than the red pin
// digitalWrite(led, LOW);// r
// digitalWrite(led + 1, HIGH); // g
// digitalWrite(led + 2, LOW); // b
break;
case 3: // Blue
digitalWrite(led + 2, HIGH); // The blue pin is two higher than the red pin
// digitalWrite(led, LOW);// r
// digitalWrite(led + 1, LOW); // g
// digitalWrite(led + 2, HIGH); // b
break;
default:
break;
}
}
void moveRandom() {
int currentPosition = myServo.read(); // Get the current position of the servo
int newPosition = random(0, 180); // Generate a random position between 0 and 180 degrees
int speed = random(1, 4); // Generate a random speed between 1 and 3 (adjust as needed)
// Determine the direction (increment or decrement) to reach the new position
int direction = (newPosition > currentPosition) ? 1 : -1;
// Gradually move the servo to the new position with the specified speed
for (int pos = currentPosition; pos != newPosition; pos += direction) {
myServo.write(pos); // Move the servo to the current position
delay(speed * 10); // Wait for the specified speed (adjust as needed)
}
myServo.write(newPosition); // Set the final position after reaching the new position
}
void checkColorThreshold(uint16_t r, uint16_t g, uint16_t b, int redThreshold, int greenThreshold, int blueThreshold, int sensorNum)
{
// // Check if the red value is above its threshold
// if (r > redThreshold) {
// Serial.println("Red is above threshold.");
// }
// // Check if the green value is above its threshold
// if (g > greenThreshold) {
// Serial.println("Green is above threshold.");
// }
// // Check if the blue value is above its threshold
// if (b > blueThreshold) {
// Serial.println("Blue is above threshold.");
// }
// Determine which color is highest above its threshold
if (r > redThreshold && r >= g && r >= b) {
Serial.println("Red is highest above its threshold.");
player_colors(1, sensorNum);
} else if (g > greenThreshold && g >= r && g >= b) {
Serial.println("Green is highest above its threshold.");
player_colors(2, sensorNum);
} else if (b > blueThreshold && b >= r && b >= g) {
Serial.println("Blue is highest above its threshold.");
player_colors(3, sensorNum);
}
}
void player_colors(int color,int sensorNum)
{
if (sensorNum = 1)
{
player1_color = color;
toggleRGB(1, color);
}
if (sensorNum = 2)
{
player2_color = color;
toggleRGB(4, color);
}
if (sensorNum = 3)
{
player3_color = color;
toggleRGB(7, color);
}
}
The main issue with this code was that is suddenly did not want to work anymore, the color thresholds were not right, and the lamps appeared to be constantly active on certain pins.
Prototype and Coding Errors
These are some persistent bugs I encountered. The primary issue appeared to be related to the wiring of the color sensors, which were not being recognized through the multiplexer. A lot later, it became evident that the sensors could only be connected to 3.3V instead of 5V to function correctly.
Fixing Error's and Wiring
After disassembling and reassembling the setup, I addressed issues with the lights frequently disconnecting during testing. I found a more stable method to connect them securely to the Arduino.
Additionally, I revised the code to adjust the way color thresholds were processed. Previously, the red color detection was not functioning correctly because it was the first condition checked, even when the red value exceeded the threshold.
Full Game
I double-checked all connections for accuracy and opted for a neater finish by replacing cardboard with cut plastic. The cardboard cutting steps from earlier can also be used for this, mind that I now put the wiring of the lamps to the inside of the box for a cleaner look, enhancing both the game's appearance and playability. Additionally, I designed the walls to be movable and standalone, ensuring they do not disrupt the wiring below.
Now the sensors are also placed lower/beneath the laser locations to encourage pointing downward with lasers to make it saver and to reduce visible wires.
The game turned out to be surprisingly enjoyable and challenging. Despite the mirrors rotating slowly, hitting the sensors posed a significant challenge. The game can be played for extended periods because once you secure your color, others can still take over.
It offers a great test of hand-eye coordination in a completely novel way.
Working Code
This is my latest code, and it's fully operational now. You can adjust the thresholds to accommodate the ambient light conditions in the room. If the lamps illuminate before you shoot a laser at them, simply adjust the thresholds accordingly.
#include <Servo.h>
#include <Wire.h>
#include "Adafruit_TCS34725.h"
// Servo motor
Servo myServo;
const int servoPin = 9; // Connect the servo to digital pin 9
// Color sensors
Adafruit_TCS34725 tcs[] = {
Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_614MS, TCS34725_GAIN_1X),
Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_614MS, TCS34725_GAIN_1X),
Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_614MS, TCS34725_GAIN_1X)
};
byte gammatable[256];
// Define constants
#define MUX_ADDR 0x70 // Multiplexer address
#define SENSORS_COUNT 3 // Number of sensors connected to the multiplexer
#define RED_PIN1 2
#define GREEN_PIN1 3
#define BLUE_PIN1 4
#define RED_PIN2 5
#define GREEN_PIN2 6
#define BLUE_PIN2 7
#define RED_PIN3 10
#define GREEN_PIN3 11
#define BLUE_PIN3 12
// Color thresholds
#define RED_THRESHOLD 100
#define GREEN_THRESHOLD 100
#define BLUE_THRESHOLD 100
// Player colors
int playerColors[3] = {0, 0, 0}; // Array to store colors for each player
// Servo movement variables
int currentPosition = 90; // Start at mid-point
int targetPosition = 90;
unsigned long lastServoMoveTime = 0;
const int servoMoveInterval = 10; // Decrease milliseconds between servo moves (was 20)
const int servoStepSize = 5; // Increase the step size
void setup() {
Serial.begin(9600);
Serial.println("start setup");
// Initialize RGB LED pins
for (int i = 2; i <= 11; i++) {
pinMode(i, OUTPUT);
digitalWrite(i, LOW); // Ensure all LEDs are off at start
}
Serial.println("setup leds");
Wire.begin();
for (int i = 0; i < 256; i++) {
float x = i;
x /= 255;
x = pow(x, 2.5);
x *= 255;
gammatable[i] = x;
}
Serial.println("setup 1");
initColorSensors();
Serial.println("setup after initialisation color sensors");
// Initialize the servo motor
myServo.attach(servoPin);
myServo.write(currentPosition); // Move servo to start position
Serial.println("Servo attached and set to 90 degrees");
}
void loop()
{
for (int i = 0; i < SENSORS_COUNT; i++) {
readColors(i);
}
// Toggle LEDs based on player colors
for (int i = 0; i < SENSORS_COUNT; i++) {
int redPin, greenPin, bluePin;
switch (i) {
case 0:
redPin = RED_PIN1; greenPin = GREEN_PIN1; bluePin = BLUE_PIN1;
break;
case 1:
redPin = RED_PIN2; greenPin = GREEN_PIN2; bluePin = BLUE_PIN2;
break;
case 2:
redPin = RED_PIN3; greenPin = GREEN_PIN3; bluePin = BLUE_PIN3;
break;
}
toggleRGB(redPin, greenPin, bluePin, playerColors[i]);
}
moveRandom(); // Move the servo motor randomly
delay(1); // Adjust delay as needed
}
void initColorSensors() {
Serial.println("Sensor initialization");
for (int i = 0; i < SENSORS_COUNT; i++) {
chooseBus(i);
if (tcs[i].begin()) {
Serial.print("Found sensor "); Serial.println(i + 1);
} else {
Serial.println("No Sensor Found");
while (true);
}
}
}
void readColors(byte sensorNum) {
chooseBus(sensorNum);
uint16_t r, g, b, c;
tcs[sensorNum].getRawData(&r, &g, &b, &c);
Serial.print("Sensor "); Serial.print(sensorNum); Serial.println(":");
Serial.print("R: "); Serial.print(r, DEC); Serial.print(" ");
Serial.print("G: "); Serial.print(g, DEC); Serial.print(" ");
Serial.print("B: "); Serial.print(b, DEC); Serial.print(" ");
Serial.print("Clear: "); Serial.println(c);
checkColorThreshold(r, g, b, c, sensorNum);
}
void chooseBus(uint8_t bus) {
Wire.beginTransmission(MUX_ADDR);
Wire.write(1 << bus);
Wire.endTransmission();
}
void checkColorThreshold(uint16_t r, uint16_t g, uint16_t b, uint16_t c, int sensorNum) {
if (r > RED_THRESHOLD && r > g && r > b) {
Serial.print(sensorNum);
Serial.println(": Red detected.");
setPlayerColor(1, sensorNum);
} else if (g > GREEN_THRESHOLD && g > r && g > b) {
Serial.print(sensorNum);
Serial.println(": Green detected.");
setPlayerColor(2, sensorNum);
} else if (b > BLUE_THRESHOLD && b > r && b > g) {
Serial.print(sensorNum);
Serial.println(": Blue detected.");
setPlayerColor(3, sensorNum);
}
}
void setPlayerColor(int color, int sensorNum) {
if (sensorNum >= 0 && sensorNum < SENSORS_COUNT) {
playerColors[sensorNum] = color;
}
}
void toggleRGB(int redPin, int greenPin, int bluePin, int color) {
// Turn off all LEDs
digitalWrite(redPin, LOW);
digitalWrite(greenPin, LOW);
digitalWrite(bluePin, LOW);
// Depending on the color value passed, turn on the corresponding color for the specified LED
switch (color) {
case 1:
Serial.println("Turning on RED");
digitalWrite(redPin, HIGH);
break;
case 2:
Serial.println("Turning on GREEN");
digitalWrite(greenPin, HIGH);
break;
case 3:
Serial.println("Turning on BLUE");
digitalWrite(bluePin, HIGH);
break;
}
}
void moveRandom() {
unsigned long currentTime = millis();
if (currentTime - lastServoMoveTime >= servoMoveInterval) {
if (currentPosition == targetPosition) {
// Choose a new random target position
targetPosition = random(0, 180);
Serial.print("New target position: ");
Serial.println(targetPosition);
} else {
// Move the servo multiple steps towards the target position
int step = min(servoStepSize, abs(targetPosition - currentPosition));
currentPosition += (targetPosition > currentPosition) ? step : -step;
myServo.write(currentPosition);
Serial.print("Current position: ");
Serial.println(currentPosition);
}
lastServoMoveTime = currentTime;
}
}
Videos
Videos of a:
test (prototype testing)
play through (with 3 players)
unpacking (the insides)
all speed up since only less than 25 MB files are allowed...