Este proyecto nace de la idea de hacer una lámpara para niños, en la que ellos puedan elegir, mediante una simple cartulina, el color que quieren que tenga su lámpara por la noche. El sistema, basado en Arduino, leerá el color de la cartulina y lo reproducirá en los LED que iluminan la habitación.
Como luz LED se ha usado un anillo que da un resultado espectacular y divertido cuando cambia de color, pues lo hace en secuencia.
Este proyecto es para niños y, por lo tanto, hemos intentado hacer todo lo posible para que participaran en la realización del mismo. Por eso usamos cartón, goma EVA y la pistola de pegamento caliente, para que ellos puedan ayudar en lo máximo posible.
- Detección del color
Para la detección del color se utiliza un sensor de color TCS3200. Es muy importante que el sensor esté a una distancia fija de la zona de lectura del color y que evitemos en lo posible la entrada de luz del exterior, pues las condiciones variables de iluminación dificultan la interpretación en la lectura del sensor y pueden dar lugar a confusión.
En nuestro proyecto hemos realizado un compartimento con ayuda de una caja de cartón (genial si ayudan los niños), donde el TCS3200 se ha ubicado en la tapa y la ranura por donde se inserta la tarjeta de color en la parte inferior. A menor distancia, la detección del color sería más compleja. Para reducir la influencia de la luz externa, así como los reflejos, se ha colocado un tubo de cartón alrededor del sensor, pintado de negro por la parte interior.
2. Identificación del color
En este ejemplo hemos intentado simplificar el proceso de identificación del color. Para ello limitamos el número de colores que el sensor va a ser capaz de leer a los colores escogidos para nuestras tarjetas, y hacemos un proceso manual de calibración que nos permitirá ajustar, en las condiciones de iluminación controladas que tenemos, la lectura que corresponde a cada color.
Sobre el material de la tarjeta de color, a nosotros nos ha funcionado muy bien la goma EVA, que se puede comprar en cualquier papelería. Es un material con pocos reflejos, por lo que leer su color ha resultado bastante sencillo.
3. Iluminación LED
La iluminación LED se ha realizado con un anillo WS2812 que permite, por un lado, hacer efectos (recorrido del anillo refrescando el color) y, por otro, obtener una cantidad de luz aceptable como lámpara nocturna (de hecho, bastante aceptable, se puede ajustar si resulta excesiva).
4. Alimentación
La alimentación, para que los niños no se anden con enchufes, la hemos solucionado con un PowerBank, alimentando el Arduino desde él. Próximamente disponibles en nuestra tienda.
5. Material utilizado
- Sensor de color TCS3200
- Anillo LED WS2812 (24 LEDs)
- Arduino UNO
- Sensor shield (¡qué fáciles con las conexiones con él!)
- Cable USB
- Cables macho-macho
- Cables macho-hembra
6. Vídeo de funcionamiento
7. Conexionado y código
El código de Arduino, donde se indica el conexionado, así como con comentarios que te ayudarán a entenderlo y modificarlo, es el siguiente:
/* Lámpara nocturna
Este código permite crear una lámpara noctura cuyo color se pueden elegir
mediante una cartulina de color. Se lee el color de la cartulina y se encienden
los LED reproduciendo su color.
www.electronperdido.com
¿Problemas con este código? Escribe a contacto@electronperdido.com
*/
// En primer lugar, se incluye la librería NeoPixel de Adafruit.
// ¿No la tienes? Descárgala: https://github.com/adafruit/Adafruit_NeoPixel
#include <Adafruit_NeoPixel.h>
// Definición de las conexiones del sensor TCS3200
// Debes conectar los pines S0 a S3 al pin indicado
// El pin SensorOut al pin número 8 del Arduino
// Además, VCC a 5V y GND a masa (GND) del Arduino
#define S0 4
#define S1 5
#define S2 6
#define S3 7
#define sensorOut 8
// Definición de los parámetros del anillo LED
#define LED_PIN 12 // Número del pin que conectaremos a la señal data (DI) del anillo LED
#define LED_COUNT 24 // Nümero de LEDs de nuestro anillo
#define BRIGHTNESS 255 // Nivel de brillo máximo del anillo
// Esto es la definición de un objeto, que se usa para manejar el anillo LED.
// No lo toques a no ser que sepas muy bien lo que haces
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// El código dentro de "SETUP" se ejectua una única vez, nada más encender el Arduino
// Ponemos aquí dentro el código que configura nuestro Arduino
void setup() {
pinMode(S0, OUTPUT); // Configura el pin S0 como salida
pinMode(S1, OUTPUT); // Configura el pin S1 como salida
pinMode(S2, OUTPUT); // Configura el pin S2 como salida
pinMode(S3, OUTPUT); // Configura el pin S3 como salida
pinMode(sensorOut, INPUT); // Configura el pin SensorOUT como entrada
// Ajusta el escalado en frecuencia a un 20% (parámetro del sensor de color)
// Esto se consigue escribiendo un '1' en S0 y un '0' en S1
digitalWrite(S0,HIGH);
digitalWrite(S1,LOW);
// Inicia el puerto serie a 9600bps
// Lo usaremos para ver en el monitor serie del PC los datos que lee el sensor
// Es útil para poder calibrarlo
Serial.begin(9600);
// Se inicializa el anillo LED
strip.begin(); // Esta orden inicializa el objeto creado antes (Déjala tal cual)
strip.show(); // Apaga todos los LED
strip.setBrightness(50); // Ajusta el nivel de brillo inicial a 50 (sobre un máximo de 255)
}
// Los valores que tenemos aquí son la lectura mínima y máxima que aceptaremos para cada color
// Cualquier color se puede dividir en la suma de Rojo(R) + Verde(G) + Azul(B)
// Aquí ponemos, para cada color, el máximo y mínimo que hemos leído en el monitor serie
// Para detectar esos máximos y mínimos lo hemos ensayado con todas las cartulinas que tenemos previsto usar
#define minR 336
#define maxR 727
#define minG 695
#define maxG 730
unsigned int minB=30;
unsigned int maxB=195;
// Nos creamos unas variables, llamadas ROJO, VERDE Y AZUL que usaremos luego
unsigned int ROJO, VERDE, AZUL;
int red, green, blue;
// Creamos también una variable necesaria para leer el sensor
int frequency = 0;
// El código dentro de LOOP se ejecutará siempre de manera cíclica
void loop() {
// Ajustamos el sensor TCS3200 para que lea el color rojo gracias a estas dos órdenes
digitalWrite(S2,LOW);
digitalWrite(S3,LOW);
// Leemos la señal SensorOut, que nos da la "frecuencia" correspondiente al color leído
frequency = pulseIn(sensorOut, LOW);
// Mostramos el valor de frecuencia que estamos leyendo a través del monitor serie.
// Este valor es el que, en el proceso de calibración, tendremos que poner en minR ó maxR
Serial.print("R= "); // Escribir este texto
Serial.print(frequency); // Escribe valor de frecuencia
Serial.print(" "); // Deja unos espacios en blanco
ROJO = frequency; // Guardo el valor en "ROJO"
if(frequency<minR){ frequency = minR; } // Si el valor leído es muy pequeño, pongo el mínimo
if(frequency>maxR){frequency = maxR;} // Si el valor leído es muy grande, pongo el máximo
// Ajusto el valor de entre minR y maxR para que tome un valor, de manera proporcional, entre 0 y 255
// Esto lo hace aplicando una regla de 3
red = map(frequency, minR,maxR,255,0);
// Con nuestras cartulinas hemos tenido problemas para detectar algunos colores, y nos hemos visto obligados
// a ajustar el mínimo y máximo de azul (B) no con un valor fijo, sino en función del valor de rojo (R) que
// leemos. Ese ajuste lo hacemos con las siguientes ecuaciones.
// Es posible que lo puedas evitar en tu proyecto. ¡Pero ves co cuidado!
float temp;
temp = frequency;
minB = 0.49*temp-133.0;
if (minB<30) minB = 30;
maxB = 0.12*temp+156.0;
if (maxB>240) maxB = 240;
delay(100); // Retardo de 100ms. No es estrictamente necesario, pero a veces ayuda que el programa vaya un poco lento.
// Ajustamos el sensor TCS3200 para que lea el color verde gracias a estas dos órdenes
digitalWrite(S2,HIGH);
digitalWrite(S3,HIGH);
// Repetimos el proceso, pero ahora con el color verde
frequency = pulseIn(sensorOut, LOW);
Serial.print("G= ");
Serial.print(frequency);
Serial.print(" ");
VERDE = frequency;
if(frequency<minG){ frequency = minG; }
if(frequency>maxG){frequency = maxG;}
green = map(frequency, minG,maxG,255,0);
delay(100); // Retardo de 100ms. No es estrictamente necesario, pero a veces ayuda que el programa vaya un poco lento.
// Ajustamos el sensor TCS3200 para que lea el color azul gracias a estas dos órdenes
digitalWrite(S2,LOW);
digitalWrite(S3,HIGH);
frequency = pulseIn(sensorOut, LOW);
Serial.print("B= ");
Serial.print(frequency);
Serial.println(" ");
AZUL = frequency;
if(frequency<minB){ frequency = minB; } // Recuerda que los valores minB y maxB, en este ejemplo, dependen del valor de Rojo.
if(frequency>maxB){frequency = maxB;}
blue = map(frequency, minB,maxB,255,0);
// Para decidir qué color está leyendo el sensor hacemos un proceso de calibración a mano con
// las cartulinas, apuntando el valor leído en cada canal (R, G, B) para cada una de ellas.
// Cartulina VERDE
#define VERDE_R 780
#define VERDE_G 625
#define VERDE_B 242
// Cartulina AZUL
#define AZUL_R 847
#define AZUL_G 715
#define AZUL_B 210
// Cartulina NARANJA
#define NARANJA_R 236
#define NARANJA_G 543
#define NARANJA_B 140
// Cartulina AMARILLA
#define AMARILLO_R 200
#define AMARILLO_G 266
#define AMARILLO_B 104
// Cartulina ROSA
#define ROSA_R 273
#define ROSA_G 478
#define ROSA_B 126
// Cartulina VIOLETA
#define VIOLETA_R 380
#define VIOLETA_G 481
#define VIOLETA_B 130
// Cartilina ROJA
#define ROJO_G 320
#define ROJO_G 780
#define ROJO_B 189
// Cartilina OSCURA (APAGADO)
#define APAGADO_R 650
#define APAGADO_G 750
#define APAGADO_B 190
// Tolerancia (desviación) que aceptamos en la lectura de cada color
#define OFFSET_R 50
#define OFFSET_G 55
#define OFFSET_B 25
// Árbol de decisión de color leído
// La instrucción "colorWipe" enciende los LED del anillo con el color indicado con las tres primeras cifras (ROJO, VERDE, AZUL)
// Este valor puede ir entre 0 y 255. El color se consigue con la proporción entre ambos, la intensidad con la suma de los tres valores
// El tercer valor, ajustado a 50, son los milisegundos que tarda, tras encender un LED, en encender el siguiente. Si quieres que
// el anillo cambie de color más rápido, bájalo, puede ir realmente rápido. Si quieres que sea más lento, pon un valor más elevado.
if ((ROJO > ROJO_R-OFFSET_R) && (ROJO < ROJO_R+OFFSET_R) && (VERDE > ROJO_G-OFFSET_G) && (VERDE < ROJO_G+OFFSET_G) && (AZUL > ROJO_B-OFFSET_B) && (AZUL < ROJO_B+OFFSET_B)) colorWipe(strip.Color(250, 0, 0) , 50); // ROJO
else if ((ROJO > VERDE_R-OFFSET_R) && (ROJO < VERDE_R+OFFSET_R) && (VERDE > VERDE_G-OFFSET_G) && (VERDE < VERDE_G+OFFSET_G) && (AZUL > VERDE_B-OFFSET_B) && (AZUL < VERDE_B+OFFSET_B)) colorWipe(strip.Color(0, 128, 0) , 50); // VERDE
else if ((ROJO > AZUL_R-OFFSET_R) && (ROJO < AZUL_R+OFFSET_R) && (VERDE > AZUL_G-OFFSET_G) && (VERDE < AZUL_G+OFFSET_G) && (AZUL > AZUL_B-OFFSET_B) && (AZUL < AZUL_B+OFFSET_B)) colorWipe(strip.Color(0, 0, 128) , 50); // AZUL
else if ((ROJO > NARANJA_R-OFFSET_R) && (ROJO < NARANJA_R+OFFSET_R) && (VERDE > NARANJA_G-OFFSET_G) && (VERDE < NARANJA_G+OFFSET_G) && (AZUL > NARANJA_B-OFFSET_B) && (AZUL < NARANJA_B+OFFSET_B)) colorWipe(strip.Color(128, 15, 0), 50); // NARANJA
else if ((ROJO > AMARILLO_R-OFFSET_R)&& (ROJO < AMARILLO_R+OFFSET_R)&& (VERDE > AMARILLO_G-OFFSET_G)&& (VERDE < AMARILLO_G+OFFSET_G)&& (AZUL > AMARILLO_B-OFFSET_B)&& (AZUL < AMARILLO_B+OFFSET_B)) colorWipe(strip.Color(128, 84, 0), 50);// AMARILLO
else if ((ROJO > ROSA_R-OFFSET_R) && (ROJO < ROSA_R+OFFSET_R) && (VERDE > ROSA_G-OFFSET_G) && (VERDE < ROSA_G+OFFSET_G) && (AZUL > ROSA_B-OFFSET_B) && (AZUL < ROSA_B+OFFSET_B)) colorWipe(strip.Color(128, 0, 32), 50); // ROSA
else if ((ROJO > VIOLETA_R-OFFSET_R) && (ROJO < VIOLETA_R+OFFSET_R) && (VERDE > VIOLETA_G-OFFSET_G) && (VERDE < VIOLETA_G+OFFSET_G) && (AZUL > VIOLETA_B-OFFSET_B) && (AZUL < VIOLETA_B+OFFSET_B)) colorWipe(strip.Color(6, 0, 12) , 50);// VIOLETA
else if ((ROJO > 400) && (VERDE > 550) && (AZUL > 175)) colorWipe(strip.Color(0, 0, 0) , 50); // APAGADO
delay(200); // Espera 200ms
}
// El código de aquí debajo es el que se encarga de encender los LED.
// Cada vez que llamas a la función "colorWipe" se ejecuta esto.
// No deberías necesitar tocarlo
void colorWipe(uint32_t color, int wait) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}