Los sensores de color de la serie TCS3200 de AMS (la antigua TAOS) son todo un clásico para medir el color que todavía sigue utilizándose por su sencillez de uso y sobre todo por ser muy barato (pueden encontrarse módulos con iluminación por poco más de 2 €) pese a que hay en el mercado sensores que integran, además de color, detección de gestos y proximidad, diseñados pensando principalmente en su uso en dispositivos móviles.
Funcionamiento del TCS3200
Los TCS3200 son sensores que convierten en frecuencia la intensidad de luz medida por una matriz de fotodiodos. La frecuencia entregada por el sensor TCS3200 es mayor cuanta mayor luminosidad se detecte. La matriz de fotodiodos permite promediar el valor medido para compensar diferencias de color en la superficie muestreada así como disponer de filtros de color (en el modelo de color RGB) que, al ir alternando su estado, sirven para distinguir cada componente.
La matriz de los sensores TCS3200 está formada por 16 fotodiodos con un filtro rojo, 16 con un filtro verde, 16 con un filtro azul y otros 16 sin filtrar. Los 64 fotodiodos no funcionan de manera simultánea sino que se activan por grupos de color antes de realizar la medición de la intensidad de luz que incide en ellos. Para elegir qué filtros deben activarse en cada momento, los TCS3200 disponen de dos patillas, S2 y S3, con las que configurarlos.
Una vez obtenida la lectura de la iluminación, la corriente se convierte a frecuencia en forma de onda cuadrada con un ciclo de trabajo del 50% (mitad pulso alto, mitad pulso bajo). Enviar al microcontrolador una frecuencia en lugar de una corriente (intensidad) permite que sea más estable y soporte mejor las interferencias producidas por las pistas del circuito o los cables que unan el TCS3200 con el µC.
La máxima frecuencia que los TCS3200 puede generar (sin producir saturación) dependiendo de la intensidad de la luz medida y del color (longitud de onda de la luz) es de 600 KHz. Para poder utilizar µC (u otros componentes) a poca velocidad, es posible escalar la frecuencia en tres niveles, la normal (máxima) al 100%, una media al 20% y otra baja al 2%. Las patillas S0 y S1 de los TCS3200 sirven para establecer estos valores de frecuencia.
Configuración del TCS3200
Si S0 y S1 están a nivel alto la frecuencia del TCS3200 es del 100%. Si ambas se encuentran a nivel bajo se desconecta el conversor de corriente en frecuencia. Para conseguir una tasa de frecuencia del 20% S0 debe estar a nivel alto y S1 a nivel bajo. Al contrario, para conseguir una tasa de frecuencia del 2%, S0 debe estar a nivel bajo y S1 a nivel alto.
Para medir la luz que incide sobre los fotodiodos sin filtro de color se establece S2 a nivel alto y S3 a nivel bajo. El color rojo se consigue con S2 y S3 a nivel bajo, el verde con S2 y S3 a nivel alto y la medición usando los fotodiodos con filtro azul se activa con S2 a nivel bajo y S3 a nivel alto. El siguiente código para Arduino permite usar cómodamente los valores
1 2 3 4 5 6 7 |
String nombre_frecuencia[4]={"Apagada","Baja (2%)","Media (20%)","Alta (100%)"}; boolean S0[4]={LOW,LOW,HIGH,HIGH}; // Valor del pin S0 para conseguir el 0% de frecuencia, el 2%, el 20% y el 100% boolean S1[4]={LOW,HIGH,LOW,HIGH}; // Valor del pin S1 para conseguir el 0% de frecuencia, el 2%, el 20% y el 100% String nombre_color[4]={"Rojo","Verde","Azul","Blanco"}; boolean S2[4]={LOW,HIGH,LOW,HIGH}; // Valor del pin S2 para conseguir el rojo (0), verde (1) azul (2) y la luminosidad (3) boolean S3[4]={LOW,HIGH,HIGH,LOW}; // Valor del pin S3 para conseguir el rojo (0), verde (1) azul (2) y la luminosidad (3) |
Conexión del TCS3200
La conexión de los sensores de color TCS3200 es muy sencilla. La única consideración que hace el fabricante al respecto es utilizar un condensador de desacoplo entre VDD y GND con un valor comprendido entre 0,010 µF y 0,100 µF.
El dispositivo permanece inactivo hasta que se habilita a nivel bajo en la patilla OE. Para que siempre esté activo, se puede conectar a GND, o se puede utilizar (como en el esquema de abajo) una resistencia pull-down antes de la conexión al microcontrolador, que lo active siempre a no ser que se desactive expresamente desde el programa del µC que lo maneje.
Los TCS3200 pueden alimentarse entre 2,7 V y 5,5 V por lo que se pueden alimentar directamente desde un circuito que contenga una gran variedad de microcontroladores incluyendo las placas Arduino.
Implementación del TCS3200
Como la luz medida por los TCS3200 es la que incide en los fotodiodos, este sensor se debe usar con fuentes de luz o con objetos bien iluminados que la reflejen con suficiente intensidad. Por esta razón, muchas implementaciones de los TCS3200, como la mayoría de módulos para pruebas y prototipos, incluyen iluminación cercana al sensor y una carcasa para aislar al propio sensor de esta iluminación. Además de asegurarse una intensidad lumínica suficiente, esta disposición tiene la ventaja de que es posible trabajar con una luz controlada (conocida) en el dispositivo.
En muchos de los módulos que montan un TCS3200 como sensor de color e incluyen iluminación tienen una entrada que permite controlar, al menos, el encendido y apagado de los mismos. Esta entrada, que con frecuencia se denomina LED, no tiene nada que ver con el TCS3200 y dependerá, en su caso, del módulo.
Aunque los TCS3200 permiten hacer medidas genéricas de color-luz, lo más práctico es realizar un calibrado previo para establecer los parámetros de medida concretos en los que se va a usar. El resultado de esta fase previa permite definir una tabla de valores con los rangos buscados. Por ejemplo, si el sensor se utiliza en una imaginaria máquina de café con leche, lo más conveniente sería obtener los (rangos de) valores de los colores buscados para cada nivel de mezcla de café y leche. Para un calibrado genérico se puede utilizar una carta de color impresa, si se va a medir luz reflejada, o unos filtros de color si se va a medir emisión de luz.
Como el sensor TCS3200 entrega frecuencia en la salida OUT, obtener desde un microcontrolador el valor medido consiste en determinar dicha frecuencia. En general, para poder medir una frecuencia es necesario muestrear la señal a más del doble de dicha frecuencia, aunque en este caso concreto se sabe que se trata de una onda cuadrada con un ciclo de trabajo del 50%, lo que simplificará notablemente su estimación. El valor máximo de la frecuencia devuelto por el TCS3200 es de 600 KHz, así que será necesario usar un µC y un software que soporte, algo más de 1,2 MHz. Otra buena razón para establecer previamente el entorno en el que funcionará el TCS3200 es limitar el rango de frecuencias ya que medir una iluminación moderada centrada en el espectro visible apenas alcanzará los 20 KHz.
Control del TCS3200 desde Arduino
Para medir frecuencias, Arduino incluye la función pulseIn()
, que devuelve el tiempo que tarda en llegar un nivel (alto o bajo) a una entrada digital. Utilizando este recurso, una placa Arduino Uno es capaz de medir entorno a 50 KHz como máximo pero, usando el mismo hardware y midiendo la frecuencia con interrupciones, es posible medir algunos MHz. Usando interrupciones es necesario conectar la salida del TCS3200 a una de las patillas que las soporten. Las asignaciones de interrupciones en las distintas placas Arduino pueden resumirse en esta lista:
- Basadas en ATmega 328 (Arduino Uno, Arduino Nano, Arduino Mini…): patillas 2 y 3.
- Arduino Mega, Arduino Mega 2560 y Arduino Mega ADK: patillas 2, 3, 18, 19, 20 y 21.
- Basadas en ATmega 32U4 (Arduino Micro, Arduino Leonardo…): patillas 0, 1, 2, 3, 7.
- Arduino Due: todas las patillas.
- Arduino Zero (Genuino Zero fuera de USA): todas las patillas excepto en la 4.
- Arduino MKR1000 (Genuino MKR1000 fuera de USA): patillas 0, 1, 4, 5, 6, 7, 8, 9, A1 y A2.
Las interrupciones se asignan con attachInterrupt()
y se liberan con detachInterrupt()
. Para evitar usar el número de interrupción y permitir que el código funcione en varias placas, se hace referencia a la interrupción por la patilla utilizada, que es devuelta por digitalPinToInterrupt()
.
En el diagrama de conexión de más arriba se hace referencia a que la salida del TCS3200 se conecta a una entrada con una interrupción hardware disponible en el microcontrolador.
Medida de la frecuencia usando pulseIn()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#define PIN_OUT 3 // Número de pin al que se conecta la salida del pulso del TCS3200 #define PIN_OE 5 // Número de pin para activar el TCS3200 (se activa a nivel bajo) Para que siempre esté activo se puede conectar a GND #define PIN_S0 2 // Número de pin de Arduino que conecta a S0 del TCS3200 (escala de frecuencia) #define PIN_S1 4 // Número de pin de Arduino que conecta a S1 del TCS3200 (escala de frecuencia) #define PIN_S2 7 // Número de pin de Arduino que conecta a S2 del TCS3200 (filtro de color) #define PIN_S3 8 // Número de pin de Arduino que conecta a S3 del TCS3200 (filtro de color) #define TIEMPO_ENTRE_LECTURAS 1000 // Informar cada segundo (1000 milisegundos) unsigned long cronometro_lecturas=0; unsigned long duracion_ciclo; // Como no se sabe el tiempo a priori, se usa un tipo de datos que admita un valor muy alto void setup() { pinMode(PIN_S0,OUTPUT); digitalWrite(PIN_S0,LOW); // Modo de baja frecuencia (2%) 12 KHz máximo (con pulseIn la frecuencia máxima es de 50 KHz, tiempo mínimo 10 µs) pinMode(PIN_S1,OUTPUT); digitalWrite(PIN_S1,HIGH); // Modo de baja frecuencia (2%) 12 KHz máximo (con pulseIn la frecuencia máxima es de 50 KHz, tiempo mínimo 10 µs) pinMode(PIN_S2,OUTPUT); digitalWrite(PIN_S2,HIGH); // Usar los fotodiodos sin filtro de color pinMode(PIN_S3,OUTPUT); digitalWrite(PIN_S3,LOW); // Usar los fotodiodos sin filtro de color pinMode(PIN_OE,OUTPUT); digitalWrite(PIN_OE,LOW); // Activar el TCS3200 estableciendo un nivel bajo en OE pinMode(PIN_OUT,INPUT); Serial.begin(9600); } void loop() { if((unsigned long)(millis()-cronometro_lecturas)>TIEMPO_ENTRE_LECTURAS) { cronometro_lecturas=millis(); pulseIn(PIN_OUT,LOW); // Esperar a que cambie el estado a bajo duracion_ciclo=pulseIn(PIN_OUT,HIGH); // Tiempo transcurrido hasta que cambia el estado (medio ciclo) duracion_ciclo+=pulseIn(PIN_OUT,LOW); // Volver a medir para completar y ciclo y disminuir un poco el error Serial.print("Frecuencia "); Serial.print(1000.0/duracion_ciclo); // Convertir el tiempo en µs a frecuencia en KHz Serial.println(" KHz"); } } |
Para ofrecer un resultado realista al medir frecuencias es importante calcular el valor promediando el mayor número de lecturas posibles. En este ejemplo, que lo único que hace es mostrar, con poca precisión, la frecuencia medida con pulseIn()
, se espera a que el nivel pasa a un estado concreto (se ha elegido arbitrariamente el nivel bajo ya que el ciclo de trabajo es del 50%), se mide el tiempo hasta cambiar de nivel y se vuelve a medir el tiempo que tarda en llegar al estado inicial. Medir dos veces disminuye un poco el error de medida pero no es el promedio al que hago referencia, que debería ser muy superior en el número de muestras. La ventaja de esperar a que el nivel pase a un estado conocido usando pulseIn()
en lugar de un bucle con while()
que tenga como condición el estado que se busca es que la función pulseIn()
incluye por defecto un timeout (un tiempo máximo de espera) que evita que el programa se bloquee si nunca llegara a alcanzar el estado esperado.
Inicio de la lectura desde un evento externo
El código anterior sirve para leer de manera continua el sensor de color TCS3200. Para activar la lectura en un momento concreto se podría implementar la estructura clásica de inicio por un evento independiente, como un pulsador o fin de carrera. Para evitar rebotes en el proceso de activación-desactivación de la lectura pueden utilizarse recursos hardware (como un condensador, una puerta lógica, un conmutador bi-posición a dos entradas GPIO…) o software, imponiendo un tiempo mínimo para considerar un cambio en el estado. En el siguiente ejemplo se implementa la activación de la lectura por el nivel alto de una entrada con eliminación por software del rebote y con un retraso en el inicio para permitir que el objeto o la fuente de luz se estabilicen antes de medir con el sensor de color TCS3200.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
#define PIN_OUT 3 // Número de pin al que se conecta la salida del pulso del TCS3200 #define PIN_OE 5 // Número de pin para activar el TCS3200 (se activa a nivel bajo) Para que siempre esté activo se puede conectar a GND #define PIN_S0 2 // Número de pin de Arduino que conecta a S0 del TCS3200 (escala de frecuencia) #define PIN_S1 4 // Número de pin de Arduino que conecta a S1 del TCS3200 (escala de frecuencia) #define PIN_S2 7 // Número de pin de Arduino que conecta a S2 del TCS3200 (filtro de color) #define PIN_S3 8 // Número de pin de Arduino que conecta a S3 del TCS3200 (filtro de color) #define PIN_ACTIVAR_LECTURA 12 // Pin que (a nivel alto) inicia o detiene la lectura #define TIEMPO_REBOTE 150 // Ignorar los cambios del pulsador de activación/desactivación durante un intervalo de tiempo para evitar rebotes #define ESPERA_HASTA_LECTURA 250 // Esperar a leer el sensor hasta que el objeto y/o la luz sea estable boolean lectura_pendiente=false; unsigned long cronometro_rebotes=0; unsigned long cronometro_lecturas; unsigned long duracion_ciclo; // Como no se sabe el tiempo a priori, se usa un tipo de datos que admita un valor muy alto void setup() { pinMode(PIN_S0,OUTPUT); digitalWrite(PIN_S0,LOW); // Modo de baja frecuencia (2%) 12 KHz máximo (con pulseIn la frecuencia máxima es de 50 KHz, tiempo mínimo 10 µs) pinMode(PIN_S1,OUTPUT); digitalWrite(PIN_S1,HIGH); // Modo de baja frecuencia (2%) 12 KHz máximo (con pulseIn la frecuencia máxima es de 50 KHz, tiempo mínimo 10 µs) pinMode(PIN_S2,OUTPUT); digitalWrite(PIN_S2,HIGH); // Usar los fotodiodos sin filtro de color pinMode(PIN_S3,OUTPUT); digitalWrite(PIN_S3,LOW); // Usar los fotodiodos sin filtro de color pinMode(PIN_OE,OUTPUT); digitalWrite(PIN_OE,LOW); // Activar el TCS3200 estableciendo un nivel bajo en OE pinMode(PIN_OUT,INPUT); pinMode(PIN_ACTIVAR_LECTURA,INPUT); Serial.begin(9600); } void loop() { if(digitalRead(PIN_ACTIVAR_LECTURA)) // Pulsador de lectura activado { if(lectura_pendiente) // Lectura en espera { if((unsigned long)(millis()-cronometro_lecturas)>ESPERA_HASTA_LECTURA) // …y se considera estable la luz que se mide porque ha pasado el tiempo correspondiente { cronometro_rebotes=millis(); pulseIn(PIN_OUT,LOW); // Esperar a que cambie el estado a bajo duracion_ciclo=pulseIn(PIN_OUT,HIGH); // Tiempo transcurrido hasta que cambia el estado (medio ciclo) duracion_ciclo+=pulseIn(PIN_OUT,LOW); // Volver a medir para completar y ciclo y disminuir un poco el error Serial.print("Frecuencia "); Serial.print(1000.0/duracion_ciclo); // Convertir el tiempo en µs a frecuencia en KHz Serial.println(" KHz"); lectura_pendiente=false; } } } else // Si el pulsador de lectura está desactivado… { if((unsigned long)(millis()-cronometro_rebotes)>TIEMPO_REBOTE) // …y ha pasado el tiempo de rebote { lectura_pendiente=true; // Permitir la lectura del sensor cronometro_rebotes=millis(); cronometro_lecturas=cronometro_rebotes; } } } |
Una vez resuelta, con el anterior ejemplo, la forma de iniciar la lectura en lugar de realizar lecturas continuas, se puede omitir esta parte del código para hacer más sencilla su comprensión y así centrar mejor la atención en el muestreo del sensor de color TCS3200.
Medida de la frecuencia usando interrupciones
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#define PIN_OUT 3 // Número de pin al que se conecta la salida del pulso del TCS3200 #define PIN_OE 5 // Número de pin para activar el TCS3200 (se activa a nivel bajo) Para que siempre esté activo se puede conectar a GND #define PIN_S0 2 // Número de pin de Arduino que conecta a S0 del TCS3200 (escala de frecuencia) #define PIN_S1 4 // Número de pin de Arduino que conecta a S1 del TCS3200 (escala de frecuencia) #define PIN_S2 7 // Número de pin de Arduino que conecta a S2 del TCS3200 (filtro de color) #define PIN_S3 8 // Número de pin de Arduino que conecta a S3 del TCS3200 (filtro de color) #define TIEMPO_ENTRE_LECTURAS 1000 // Informar cada segundo (1000 milisegundos) unsigned long cronometro_lecturas; unsigned long numero_flancos=0; void setup() { pinMode(PIN_S0,OUTPUT); digitalWrite(PIN_S0,LOW); // Modo de baja frecuencia (2%) pinMode(PIN_S1,OUTPUT); digitalWrite(PIN_S1,HIGH); // Modo de baja frecuencia (2%) pinMode(PIN_S2,OUTPUT); digitalWrite(PIN_S2,HIGH); // Usar los fotodiodos sin filtro de color pinMode(PIN_S3,OUTPUT); digitalWrite(PIN_S3,LOW); // Usar los fotodiodos sin filtro de color pinMode(PIN_OE,OUTPUT); digitalWrite(PIN_OE,LOW); // Activar el TCS3200 estableciendo un nivel bajo en OE pinMode(PIN_OUT,INPUT); Serial.begin(9600); attachInterrupt(digitalPinToInterrupt(PIN_OUT),iniciar_medida,RISING); // Llamar a la función iniciar_medida cuando se alcance el flanco de subida (No se empieza a medir ya que no se sabe el estado al empezar) } void loop() { if((unsigned long)(millis()-cronometro_lecturas)>TIEMPO_ENTRE_LECTURAS) { detachInterrupt(digitalPinToInterrupt(PIN_OUT)); cronometro_lecturas=millis(); Serial.print("Frecuencia "); Serial.print(numero_flancos/1000.0); // Convertir el número de flancos a frecuencia en KHz Serial.println(" KHz"); numero_flancos=0; attachInterrupt(digitalPinToInterrupt(PIN_OUT),iniciar_medida,RISING); } } void iniciar_medida() { attachInterrupt(digitalPinToInterrupt(PIN_OUT),incrementar_numero_flancos,RISING); // Desde ahora, ya se conoce el estado, se llama a incrementar_numero_flancoss } void incrementar_numero_flancos() { detachInterrupt(digitalPinToInterrupt(PIN_OUT)); numero_flancos++; attachInterrupt(digitalPinToInterrupt(PIN_OUT),incrementar_numero_flancos,RISING); } |
Trabajando con interrupciones lo que se mide es el número de veces que vuelve a determinado flanco (al mismo estado). Para hacerlo se espera a que se alcance la primera vez y se cuentan los flancos alcanzados en un intervalo de tiempo conocido. En este caso, a una frecuencia muy baja, se ha usado un segundo para contar ciclos y para mostrar el resultado. Como decía antes, es muy importante al medir frecuencias promediar los valores para corregir en lo posible el error cometido.
Cabría preguntarse si merece la pena utilizar interrupciones para activar la lectura y la respuesta es que depende de las (interrupciones) que haya disponibles. Si la mayoría de las patillas GPIO del µC aceptan interrupciones, sí es una buena idea ya que es una forma limpia y legible de escribir el programa (entendido que se hace un uso correcto de la interrupción) Si el µC no dispone de muchas GPIO asociadas a interrupciones no merece la pena ya que la velocidad de reacción, principal ventaja, no es crítica en este caso.
Identificar el rango de trabajo del TCS3200
Una vez establecido el método más conveniente de cálculo de la frecuencia, el siguiente paso consiste en determinar el rango de valores en los que va a funcionar el TCS3200. Para hacer más legibles los ejemplos voy a usar pulseIn() para calcular la frecuencia (aunque es más preciso usar interrupciones y permite medir valores mayores) y solamente voy a buscar los valores mayor y menor del rango, que pueden entenderse sin necesidad de conocer los colores de una aplicación real.
Como el objetivo es establecer una tabla de valores entre los que interpolar, en este caso solamente el máximo y el mínimo, ya no es necesario calcular la frecuencia, y pueden almacenarse en la tabla usada como referencia los valores de tiempo obtenidos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
#define PIN_OUT 3 // Número de pin al que se conecta la salida del pulso del TCS3200 #define PIN_OE 5 // Número de pin para activar el TCS3200 (se activa a nivel bajo) Para que siempre esté activo se puede conectar a GND #define PIN_S0 2 // Número de pin de Arduino que conecta a S0 del TCS3200 (escala de frecuencia) #define PIN_S1 4 // Número de pin de Arduino que conecta a S1 del TCS3200 (escala de frecuencia) #define PIN_S2 7 // Número de pin de Arduino que conecta a S2 del TCS3200 (filtro de color) #define PIN_S3 8 // Número de pin de Arduino que conecta a S3 del TCS3200 (filtro de color) #define TOTAL_FILTROS 4 #define TIEMPO_ENTRE_RESULTADOS 1000 // Esperar 1 segundo para mostrar el estado actual de máximo y mínimo unsigned long cronometro_resultado=0; unsigned char contador_filtro; // Contador global del número de filtro que se está procesando float tiempo_pulso; unsigned long minimo[TOTAL_FILTROS]={-1,-1,-1,-1}; unsigned long maximo[TOTAL_FILTROS]={0,0,0,0}; bool s2[TOTAL_FILTROS]={LOW,HIGH,LOW,HIGH}; // Valor del pin S2 para conseguir el rojo (0), verde (1) azul (2) y la luminosidad (3) bool s3[TOTAL_FILTROS]={LOW,HIGH,HIGH,LOW}; // Valor del pin S3 para conseguir el rojo (0), verde (1) azul (2) y la luminosidad (3) String nombre[TOTAL_FILTROS]={"Rojo","Verde","Azul","Blanco"}; void setup() { pinMode(PIN_OUT,INPUT); pinMode(PIN_OE,OUTPUT); pinMode(PIN_S0,OUTPUT); pinMode(PIN_S1,OUTPUT); pinMode(PIN_S2,OUTPUT); pinMode(PIN_S3,OUTPUT); digitalWrite(PIN_S0,LOW); // Modo de baja frecuencia (2%) 12 KHz máximo (con pulseIn la frecuencia máxima es de 50 KHz, tiempo mínimo 10 µs) digitalWrite(PIN_S1,HIGH); // Modo de baja frecuencia (2%) 12 KHz máximo (con pulseIn la frecuencia máxima es de 50 KHz, tiempo mínimo 10 µs) digitalWrite(PIN_OE,LOW); // Activar el TCS3200 estableciendo un nivel bajo en OE Serial.begin(9600); } void loop() { for(contador_filtro=0;contador_filtro<TOTAL_FILTROS;contador_filtro++) { digitalWrite(PIN_S2,s2[contador_filtro]); digitalWrite(PIN_S3,s3[contador_filtro]); pulseIn(PIN_OUT,LOW); // Esperar a que cambie el estado a bajo tiempo_pulso=pulseIn(PIN_OUT,HIGH); // Tiempo transcurrido hasta que cambia el estado (medio ciclo) tiempo_pulso+=pulseIn(PIN_OUT,LOW); // Volver a medir para completar y ciclo y disminuir un poco el error if(tiempo_pulso>maximo[contador_filtro]) { maximo[contador_filtro]=tiempo_pulso; } if(tiempo_pulso<minimo[contador_filtro]) { minimo[contador_filtro]=tiempo_pulso; } } if((unsigned long)(millis()-cronometro_resultado)>TIEMPO_ENTRE_RESULTADOS) { cronometro_resultado=millis(); for(contador_filtro=0;contador_filtro<TOTAL_FILTROS;contador_filtro++) { Serial.println(nombre[contador_filtro]); Serial.print("Tiempo máximo "); Serial.print(maximo[contador_filtro]); Serial.println(" µs"); Serial.print("Tiempo mínimo "); Serial.print(minimo[contador_filtro]); Serial.println(" µs"); Serial.print("\n"); // Una línea en blanco como separador // Cambiar el máximo y el mínimo para ver el comportamiento en cada lectura /* minimo[contador_filtro]=-1; maximo[contador_filtro]=0; */ } Serial.print("\n"); // Una línea en blanco como separador } } |
Con los resultados obtenidos por el código anterior usado sobre luz u objetos de referencia, unos valores como los (arbitrarios) que se muestran en la captura de pantalla de abajo, se crea la tabla que permite interpolar los valores finales (en este caso solo los máximos y mínimos). Además se tiene el rango real en el que funciona el dispositivo, con lo que se pueden usar tipos de datos más ajustados (unsigned int
en lugar de unsigned long
, por ejemplo), establecer tiempos de espera máximos (timeout) para la función pulseIn()
y acotar el resultado interpretado usando, por ejemplo, constrain()
y map()
.
Medir el color con el TCS3200 y Arduino
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
#define PIN_OUT 3 // Número de pin al que se conecta la salida del pulso del TCS3200 #define PIN_OE 5 // Número de pin para activar el TCS3200 (se activa a nivel bajo) Para que siempre esté activo se puede conectar a GND #define PIN_S0 2 // Número de pin de Arduino que conecta a S0 del TCS3200 (escala de frecuencia) #define PIN_S1 4 // Número de pin de Arduino que conecta a S1 del TCS3200 (escala de frecuencia) #define PIN_S2 7 // Número de pin de Arduino que conecta a S2 del TCS3200 (filtro de color) #define PIN_S3 8 // Número de pin de Arduino que conecta a S3 del TCS3200 (filtro de color) #define TOTAL_FILTROS 4 #define TIMEOUT_TCS3200 10000 // Máximo tiempo de espera de lectura (en microsegundos) #define TIEMPO_ENTRE_RESULTADOS 3000 // Esperar 3 segundos para mostrar el resultado long cronometro_lecturas=0; unsigned int contador_medidas=0; unsigned char contador_filtro; // Contador global unsigned char valor_intensidad_normalizado; unsigned int pulso_medido; float tiempo_pulso[TOTAL_FILTROS]={0,0,0,0}; // Valores medios unsigned int minimo[TOTAL_FILTROS]={462,445,289,118}; unsigned int maximo[TOTAL_FILTROS]={7183,8558,6148,2446}; //unsigned int maximo[TOTAL_FILTROS]={7183,8558,6148,2446}; bool s2[TOTAL_FILTROS]={LOW,HIGH,LOW,HIGH}; // Valor del pin S2 para conseguir el rojo (0), verde (1) azul (2) y la luminosidad (3) bool s3[TOTAL_FILTROS]={LOW,HIGH,HIGH,LOW}; // Valor del pin S3 para conseguir el rojo (0), verde (1) azul (2) y la luminosidad (3) String nombre[TOTAL_FILTROS]={"Rojo","Verde","Azul","Blanco"}; void setup() { pinMode(PIN_OUT,INPUT); pinMode(PIN_OE,OUTPUT); pinMode(PIN_S0,OUTPUT); pinMode(PIN_S1,OUTPUT); pinMode(PIN_S2,OUTPUT); pinMode(PIN_S3,OUTPUT); digitalWrite(PIN_S0,LOW); // Modo de baja frecuencia (2%) 12 KHz máximo (con pulseIn la frecuencia máxima es de 50 KHz, tiempo mínimo 10 µs) digitalWrite(PIN_S1,HIGH); // Modo de baja frecuencia (2%) 12 KHz máximo (con pulseIn la frecuencia máxima es de 50 KHz, tiempo mínimo 10 µs) digitalWrite(PIN_OE,LOW); // Activar el TCS3200 estableciendo un nivel bajo en OE Serial.begin(9600); } void loop() { for(contador_filtro=0;contador_filtro<TOTAL_FILTROS;contador_filtro++) { digitalWrite(PIN_S2,s2[contador_filtro]); digitalWrite(PIN_S3,s3[contador_filtro]); contador_medidas++; pulseIn(PIN_OUT,LOW,TIMEOUT_TCS3200); // Esperar a que cambie el estado a bajo pulso_medido=pulseIn(PIN_OUT,HIGH,TIMEOUT_TCS3200); // Tiempo transcurrido hasta que cambia el estado (medio ciclo) pulso_medido+=pulseIn(PIN_OUT,LOW,TIMEOUT_TCS3200); // Volver a medir para completar y ciclo y disminuir un poco el error tiempo_pulso[contador_filtro]=tiempo_pulso[contador_filtro]*(contador_medidas-1)/contador_medidas+(float)pulso_medido/contador_medidas; } if((unsigned long)(millis()-cronometro_lecturas)>TIEMPO_ENTRE_RESULTADOS) { cronometro_lecturas=millis(); Serial.print(contador_medidas); Serial.println(" medidas tomadas"); contador_medidas=0; for(contador_filtro=0;contador_filtro<TOTAL_FILTROS;contador_filtro++) { valor_intensidad_normalizado=map(constrain(tiempo_pulso[contador_filtro],minimo[contador_filtro],maximo[contador_filtro]),minimo[contador_filtro],maximo[contador_filtro],255,0); Serial.print(nombre[contador_filtro]); Serial.print("="); Serial.print(tiempo_pulso[contador_filtro]); Serial.print(" "); Serial.print(valor_intensidad_normalizado); Serial.print("\n"); // Una línea en blanco como separador tiempo_pulso[contador_filtro]=0; } Serial.print("\n"); // Una línea en blanco como separador } } |
Como decía al principio, normalmente es muy importante promediar los valores obtenidos. En el caso del TCS3200 para calcular con más precisión la medida de la frecuencia, aunque el dispositivp incluya varios fotodiodos para medir cada color. En el código de ejemplo anterior se utiliza el intervalo entre presentaciones del resultado para realizar tantas medidas como sea posible y obtener el valor medio. Además de restringir los valores a los máximos y mínimos con constrain
, se convierten con map
al rango de 255 a 0 (invertido, más tiempo, menos luz) como ejemplo de aplicación.
Andreu
muchas grácias, este si funciona
Roberto Soteras
Buenas tardes Victor, me gustaría contactar contigo para ver como se podrían replicar las luces traseras de un coche con sensores de color.
Esperando tu respuesta, un saludo, Roberto.
Víctor Ventura
Hola, Roberto.
Si nos das más datos de tu proyecto a lo mejor podemos ayudarte en polaridad.es; si prefieres una consulta profesional, puedes encontrar un correo electrónico de contacto en mi página web victorventura.es.
¡Gracias por visitar polaridad.es!
Joan Francesc quetglas
Buenos dias,
En mi universidad hacemos un proyecto usando un sensor de color TCS3200 y antes nos funcionaba pero pasados unos dias solo nos da lecturas de color gris….que puede haber pasado?
Víctor Ventura
¡Hola! Sin más datos, tiene toda la pinta de haberse roto 🙁 ¿O ha pasado algo más entre que funcionaba y dejó de hacerlo?
Por cierto ¿No te gustaría contarnos en polaridad.es el uso que le dais al sensor? Puede ser muy interesante para otros lectores.
Joel Barrero Castro
Soy otro miembro del proyecto; éste se basa en un manipulador con dos grados de libertad que de un dispensador de bolas metálicas de colores detecta el color para trasladarla a su respectivo recipiente. con tu código nos devuelve valores RGB parecidos a los que tendría que dar pero más blancos. Por ejemplo: poniendo una superficie roja fuerte, nos devuelve un rojo muy poco intenso casi blanco. Al conectar el puerto del LED no se enciende ninguno de los 4 mientras está en funcionamiento, crees que es este el problema o qué puede suceder? Se debe calibrar antes el sensor?
Víctor Ventura
Hola, Joel.
Dependiendo del módulo que estés usando (no del sensor de color) puede que los LED se enciendan a nivel alto, bajo o hasta con la intensidad que determines por un PWM (con
analogWrite
). Si no tienes documentación del módulo que estás usando, puedes probar primero un nivel bajo, si no funciona, un nivel alto y si funciona a nivel alto, un pulso, por si es posible graduar el nivel.El sensor viene calibrado de fábrica, no hay que calibrarlo. Lo que sí hay que «calibrar» (determinar) son las condiciones ambientales; puede que al hablar de eso te haya inducido a error. Entiendo que estás midiendo luz reflejada (iluminas las bolas y mides la luz/color reflejada) dependiendo de como de intensa sea la luz ambiental y de su temperatura, la medida del TCS3200 de una superficie variará.
Creo que, si los colores de las bolas son suficientemente diferentes (es decir, no tienes que distinguir dos tipos de bolas rojas sino entre bolas rojas y verdes, por ejemplo) no tendrás problema aunque no consigas que el valor devuelto corresponda con uno equiparable al que representas (supongo que en una pantalla).
Dicho todo lo anterior, el código del artículo es de ejemplo, no de una aplicación en explotación así que lo revisaré por si se me ha escapado algún error que sea el que os produce la desviación de color que explicas.
¡Suerte con vuestro proyecto y gracias por participar en polaridad.es
P.D.
Entonces ¿Os ha funcionado como esparabais en algún momento y luego no o siempre no? 🙂
Joan Francesc quetglas
Podemos contactar contigo por email para explicarte mejor?
Gracias!
Juan Jose AC
Estimado! Excelente trabajo y muy bien explicado. Se te agradece el aporte. Por otro lado, me queda una pregunta: la frecuencia la mides con el timer 1 midiendo el tiempo que dura p.e. la señal en alto ö durante cierto tiempo especiificado en el timer cuentas las veces que se pone en alto la señal para despues hacer el calculo respectivo?. Saludos y gracias por tu tiempo.
Víctor Ventura
Hola, Juan Jose.
En este ejemplo no he trabajado (directamente o explícitamente) con los timers sino con la función
pulseIn
, para medir la duración del pulso o con interrupciones, asignadas conattachInterrupt
, para medir el número de pulsos en una unidad de tiempo.Gracias por participar en polaridad.es
Hernando Echeverry
Hola. Quería preguntar algo. Es posible medir otros colores diferentes al rojo, azul, verde o blanco? POr ejemplo un amarillo o un naranja? Si es así, qué debo tener en cuenta? Muchas gracias por la ayuda
Víctor Ventura
Hola, Hernando.
Claro, puedes medir cualquier color como la cantidad de luz que emite o refleja midiendo cada uno de sus componentes (además de la luminosidad)
Por ejemplo, sabrás que el color es amarillo cuando haya una cantidad similar de rojo y de verde y una cantidad muy inferior de azul.
Gracias por participar en polaridad.es ¡Vuelve pronto! 🙂
Ricardo
Hola, buenos días.
Tengo interés en desconectar la luz de los cuatro LED blancos para poder detectar una señal luminosa activa láser rojo.
¿Alguien podría decirme como hacerlo por software o hardware?
Muchas gracias y saludos.
Ricardo
Víctor Ventura
Hola, Ricardo.
Yo te puedo contar mi experiencia pero hay muchos módulos que utilizan este sensor y cada uno funciona de una manera un poco diferente. Será bueno que otros lectores te hablen de lo que ellos conocen.
Como decía en algún comentario anterior, cuando el módulo permite controlar los LED suele incluir una entrada (que normalmente se llama también LED) para activarlos a nivel alto o bajo, digamos por software, o para alimentarlos por separado del sensor (esto sería como activarlos a nivel alto pero tomando la alimentación de la señal).
Cuando se hace por hardware y, en el caso de algunos módulos, cuando se hace por software, es posible graduar la intensidad de la luz de los LED utilizando PWM.
El módulo más barato que he encontrado es el que aparece en la foto del principio del artículo y no se puede desactivar la iluminación. Para usar ese módulo sin luz (o uno de ese tipo) sería necesario desoldar los PWM. Como estos módulos son tan baratos, es cuestión de plantearse si buscar un modelo que se comporte como necesites o tomarse la molestia de «hackear» uno con la iluminación fija.
Por otro lado, sería interesante que nos hablaras del uso que piensas darle. Para detectar una luz láser hay sensores más baratos, más sencillos de usar y, sobre todo, que reaccionan más rápidamente a la señal ¿Estás trabajando con el color de la luz que llega? Seguro que a otros lectores, como a mí, les encantaría conocer tu proyecto 🙂
Suerte y gracias por participar en polaridad.es
Alejandro
amigo necesito de tu ayuda.
Soy nuevo con el uso de arduino y lo que quiero es mover un par de servos con el uso del sensor rgb ts3200, a modo que al marcar un color se mueva uno y al marcar otro el segundo, pero no se como hacerlo.
De antemano gracias
Hugo
Exelente trabajo Victor, me gustaria contartar contigo para un proyecto que estoy en proceso necesito un poco de ayuda, con lo que es el filtro «claro» aun no entiendo para que funciona ese filtro y tambien en, la parte de lalos iluminosidad yo cambie la frecuencia a un «100 %» pero creo que eso afecta en el rango del trabajo ojala leas el msj y puedas ayudarme te dejo mi correo por si pudiera tener una oportunidad de contactar contigo por algun tipo, de llamada por facebook o cualquier red social que puedas
Diego
Primero de todo, gran trabajo, muy útil. Segundo, no he visto ningún comentario que lo haya dicho pero creo que puedes tener un error, te cito: «una iluminación moderada centrada en el espectro visible apenas alcanzará los 20 KHz.» ¿Es posible que te hayas confundido con el rango de apreciación del sonido? porque el espectro visible de luz, si no me equivoco es 4-8 x10^14 Hz, muy lejos de los 20KH. un saludo y gracias.
Víctor Ventura
Hola, Diego.
El TCS3200 indica con una frecuencia la intensidad de cada uno de los colores medidos. Creo que estás confundiendo esta frecuencia, la de la señal del TCS3200, con la de la luz.
Gracias por participar en polaridad.es ¡ Vuelve pronto !
Diego
Hola.
Efectivamente esa fue mi interpretación, muchas gracias por aclararme la duda.
saludos.
Edi
Hola Victor. ¿Hay manera de que se programe el sensor de color junto con un carro seguidor de lineas pero esta vez no solo que detecte una sola linea si no algunas lineas de colores? De ser asi, ¿podrias apoyarme con la programacion? Es arduino uno el que deseo ocupar
Tomás
hola, ¿es necesario antes de la utilización del sensor , realizar alguna calibración?
Ricardo
Excelente aporte muchas gracias, solo una cuestion, estoy trabajando en un proyecto final, se trata de un sensor de color como el anterior agregado a una banda transportadora, me agradaria que me pudieras decir de que forma puedo hacer un programa (de pic 18f452) para que solo detecte rojo, o azul, o verde, y
a su vez de un pulso para hacer correr la banda con un motor CD?
AGUS
Hola Victor, una pregunta sobre una inquietud que tengo. Pensando en las utilidades de este sensor, podría utilizarse para sacar un color de un mueble, pared… para despues compararlo con la cartas de colores standar y pedir que te han una pintura igual al de la muestra «scaneada»?. Un cordial saludo
luis alberto luizaga
estoy haciendo un seleccionador de limmones pero en mi dificultad esta la programacion quiero que sense un color y que me active una carga. si me podes ayudar con esto.
william
hola estoy trabajando con el tcs 230 en muestreo de aguas turbias.si me podes ayudar con esto.
Jose Moscardó
Hola Víctor, un trabajo estupendo. He podido comprobar que muy exacto. He intentado con diferentes instrucciones que los datos RGBW puedan salir por una pantalla LCD, pero no he logrado ningún éxito. Solo se hace visible el dato para el color Blanco, es decir el último color que se imprime en pantalla. ¿Hay alguna posibilidad de ver los resultados en LCD? De esta manera podríamos disponer de un sensor portátil. Un cordial saludo.
Víctor Ventura
Hola, Jose.
Me alegro de que te haya gustado el artículo.
La respuesta corta a tu pregunta es «Sí». Lo estuve investigando hace tiempo (el artículo es de 2016) y malo es que hay muchos «peros» relacionados principalmente con el calibrado del sensor y, si quieres ver el color en un LCD, también del dispositivo de salida.
Por otro lado, no es el sensor de color más preciso que puedes conseguir hoy por poco precio. Si piensas invertir mucho tiempo en calibrar el dispositivo resultante (sensor + LCD) y no estás condicionado a utilizar precisamente ese (no sé si es que tienes cientos de ellos esperando a ser útiles) valora la posibilidad de elegir otro sensor .
Cuéntanos por aquí tus avances, seguro que son de ayuda para otros lectores.
Gracias por visitar el blog.
Jose
Hola Victor, no me he explicado bien. Me refería a poder visualizar en una pantalla LCD los datos que arroja el programa. Lo intento con comandos lcd.print para una con una LCD 20×4:
lcd.print (nombre[contador_filtro]);
lcd.print(«=»);
lcd..print(valor_intensidad_normalizado);
Al ejecutar solo se visualiza el «Blanco» y su «valor normalizado» pero no es posible ver los datos para rojo, verde y azul. Saludos
Jose Moscardó
Disculpa Victor, no debí de expresarme bien. Me refiero a ver los datos en bruto en una pantalla LCD:
lcd.print(nombre[contador_filtro]);
lcd..print(valor_intensidad_normalizado);
Al intentar que salgan en LCD solo podemos ver: «Blanco» y el valor del «color blanco» . El resto de colores y valores no aparecen en pantalla. (trabajo con una LCD 20×4 I2C
Cordial saludo
pd. He enviado antes una respuesta a tu comentario, per creo que no se ha publicado.
Víctor Ventura
Hola, Jose.
Me temo que el problema lo tienes entonces en la salida por la pantalla LCD. No puedo ayudarte mucho más pero así, a ojo, parece que no estés cambiando bien de línea al mostrar el valor y solo ves el último (que será el blanco)
Ojalá eso te dé una pista.
Un saludo.
Mauricio
Buen dia, disculpen estoy trabajando en un proyecto usando el sensor de color antes mencionado y en dos de los sensores se me desprendio el elemento en el punto c2, cual es la funcion de este elemento, tengo que reemplazar los dos sensores o intento soldarlos, la ausencia de este elemento me afecta en las lecturas, espero alguien me pueda ayudar a resolver mis dudas, saludos