Pages: [1]   Go Down
Author Topic: TCS230 y Arduino, colorímetro  (Read 762 times)
0 Members and 1 Guest are viewing this topic.
España
Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hola gente.

Me fabriqué un colorímetro para comparar un color patrón con un segundo color y poder ver las diferencias entre los tres colores patrón, usando un sensor de color TCS230 y un Arduino Mega, aunque funciona con cualquiera.

He mirado por el foro y hay otro hilo con algo parecido http://arduino.cc/forum/index.php/topic,20309.0.html, pero he visto que utiliza la duración del pulso alto, por lo que si se usa una placa como una "nano" que el tiempo mínimo de conteo en microsegundos es de unos 4 microsegundos (y a 8MHz tienen resolución de 8uS), pues puede dar lecturas poco precisas.

He utilizado una interrupción para contar 10 veces y así obtener una muestra más grande y así esos 4uS no son tan importantes.
Luego para mejorar la precisión repito la operación 10 veces o más y hago una media. Con eso se obtiene muy buena precisión.

Otro punto que he utilizado es programarlo tipo "Grafcet" o como está utilizando Igor R en la máquina de estados, pero no sabía que había hecho una librería, gracias smiley-kitty  http://arduino.cc/forum/index.php/topic,75826.0.html

El código con los estados o pasos es este:
Code:
Paso 0:
  muestra inicio en pantalla y espera a botón "enter"
  Si botón "enter" apretado entonces salta al paso 1

Paso 1:
   Empieza con el sensor blanco (anulado)
   Va al paso 2

Paso 2:
   Captura color azul
   Si ha completado las X lecturas, entonces ve al paso 3

Paso 3:
   Captura color verde
   Si ha completado las X lecturas, entonces ve al paso 4

Paso 4:
   Captura color rojo
   Si ha completado las X lecturas, entonces ve al paso 5

Paso 5:
   Si se ha completado las XX muestras para hacer la media, entonces ve al paso 6
sino ve al paso 1

Paso 6:
   Realiza los cálculos y los guarda en variable patrón
Ve al paso 7

Paso 7:
   Muestra en pantalla la siguiente opción, "segundo color a comparar"
   Si apreta enter, entonces ve al paso 8, sino no hace nada

Paso 8:
   Empieza con el sensor blanco (anulado)
   Va al paso 9

Paso 9:
   Captura color azul
   Si ha completado las X lecturas, entonces ve al paso 10

Paso 10:
   Captura color verde
   Si ha completado las X lecturas, entonces ve al paso 11

Paso 11:
   Captura color rojo
   Si ha completado las X lecturas, entonces ve al paso 12

Paso 12:
   Si se ha completado las XX muestras para hacer la media, entonces ve al paso 13
sino ve al paso 8

Paso 13:
   Realiza los cálculos y los guarda en la segunda variable
   Muestra en pantalla los resultados
   Va al paso 14

Paso 14:
   Si botón enter entonces limpia pantalla y variables y va al paso 8,
Si botón abajo entonces limpia todas las variables y va al paso 0, empieza de nuevo.
   sino no hace nada

Y el código pasado a arduino:
---> Demasiado grande, en el siguiente post <---

El código está muy sucio, hay muchas lineas de depuración por serie que se podrían eliminar.  Además tampoco soy un experto, estoy aprendiendo.
Ahora que hay librería me lo miraré e intentaré hacer el código mejor.

Como mejoras que se pueden hacer con el sensor y con el código:
Con el sensor, si el color que se toma es brillante, la luz refleja y capta mucha luz por reflejo directo y no es bueno, hay que darle un cierto ángulo a la luz para que lo que refleja sea solo el color que ve.
Otro tema es que si varía la distancia de lectura, varían los tres colores y entonces puede dar lecturas falsas:
- Se puede solucionar asegurando una ventana para la lectura a la misma distancia.
- Otra forma de solucionarlo es en los cálculos tomar el % de cada color sobre la suma total, por lo que en teoría la proporción se mantendría aunque varie la intensidad de la fuente de luz. (Pero no estoy seguro, ya que cada sensor tiene una sensibilidad diferente, quizás habría que hacer alguna operación con sus parámetros, para que la proporción se mantuviera constante independientemente de la intensidad de la luz).
A parte de estos detalles que se pueden mejorar, el colorímetro funciona bastante bien.

Colgué el documento en mi página con una imagen: http://www.bitacoradealfon.es/TCS230_arduino.php


Saludos
« Last Edit: December 03, 2011, 03:28:37 pm by Alfon » Logged

El saber no ocupa lugar, pero sí tiempo

España
Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Y el código:
Code:
/*LiquidCrystal Library - Hello World

Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.

The circuit:
* LCD RS pin to digital pin 8
* LCD Enable pin to digital pin 9
* LCD D4 pin to digital pin 4
* LCD D5 pin to digital pin 5
* LCD D6 pin to digital pin 6
* LCD D7 pin to digital pin 7
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)

Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
*/

// include the library code:
#include <LiquidCrystal.h>

int akey = 0; // Valor entrada teclado analógico
int key_val[5] ={0, 142, 327, 503, 740 }; //"Right Key OK ", "Up Key OK ", "Down Key OK ", "Left Key OK ", "Select Key OK"
int pinput = 2;
int S2 = 11;
int S3 = 12;
int i=0;
float periodo = 0;
float frecuencia = 0;
float tiempoactual = 0;
int media = 0;
float valorazul = 0;
float valorverde = 0;
float valorrojo = 0;
float valorazul2 = 0;
float valorverde2 = 0;
float valorrojo2 = 0;
int paso = 0;
int s; // Variables para For
int muestra = 10; // Valor para hacer media, mientras más es más preciso, pero tarda mucho más (0 incluido).
int nmuestra = 9; // Numero de ciclos para saber la frecuencia, a más es más fiable, aunque arduino tiene un tiempo mínimo de 4uS u 8uS según modelo (0 incluido).
float azul[11], verde[11], rojo[11]; // IMPORTANTE: Poner el tamaño del array igual al valor de "muestra" + 1

/* Bits de configuración del sensor, se descarga el sensor blanco y se usarán los de diferentes colores
S2 | S3 | Sensor
----------------------
L L Red
L H Blue
H L Clear (no filter
H H Green
*/

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
pinMode(pinput, INPUT); // Pin entrada frecuencia sensor color
pinMode(S2, OUTPUT); // Pin S2 de configuración
pinMode(S3, OUTPUT); // Pin S3 de configuración
Serial.begin(57600); // Para debug, comunicación serie con PC
Serial.println("Sensor color:");
Serial.println("Blanco | Azul | verde | rojo");

}

void loop() {

if(paso == 0){
lcd.setCursor(0,0);
lcd.print("Colorimetro ->Enter");
lcd.setCursor(0,1);
lcd.print(analogRead(akey));
lcd.print(" ");
if(analogRead(akey)==key_val[4]){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Capturando color...");
paso++;
delay(200);

}
}

// ---------------------- Para blanco --------------------------
if(paso == 1){

/* --------->> Parte para el sensore blanco
attachInterrupt(0, cuenta, RISING); // Activa la interrupción cuando hay un cambio de estado de subida
if(i>=nmuestra){
i=0;
detachInterrupt(0); // Desactiva la interrupción, mientras hace los cálculos y envia a pantalla
periodo = micros() - tiempoactual; // Periodo en milisegundos
blanco[media] = (nmuestra+1)*1000000 / (periodo);
Serial.println(media);
Serial.print(blanco[media]); Serial.print(" ");
lcd.setCursor(0, 0);
lcd.print(frecuencia); lcd.print(" ");
paso++;

}
*/
paso++; // Este paso se quedó dentro del IF anulado
}


// ------------------------ Para azul ---------------------------
if(paso == 2){

attachInterrupt(0, cuenta, RISING); // Activa la interrupción cuando hay un cambio de estado de subida
if(i>=nmuestra){ // Cuando ha contado "nmuestra" entonces procede a lo siguiente:
i=0; // Reinicia el contador para el próximo color
detachInterrupt(0); // Desactiva la interrupción, mientras hace los cálculos y envia a pantalla
periodo = micros() - tiempoactual; // Periodo en microsegundos que ha tardado en contar los "nmuestra" veces (contando el 0)
azul[media] = (nmuestra+1)*1000000 / (periodo); // Pasa el periodo a frecuencia.
Serial.print(azul[media]); Serial.print(" "); // Imprime por serial para comprobaciones
lcd.print(azul[media]); lcd.print(" "); // Imprime por LCD aunque va muy rápido, se puede omitir
paso++; // incrementa el paso para el siguiente y el actual no se repetirá más.
lcd.setCursor(0, 1); // Se posiciona al inicio del LCD

}
}
// ------------------------ Para verde ---------------------------
if(paso == 3){

attachInterrupt(0, cuenta, RISING); // Activa la interrupción cuando hay un cambio de estado de subida
if(i>=nmuestra){
i=0;
detachInterrupt(0); // Desactiva la interrupción, mientras hace los cálculos y envia a pantalla
periodo = micros() - tiempoactual; // Periodo en microsegundos
verde[media] = (nmuestra+1)*1000000 / (periodo);
Serial.print(verde[media]); Serial.print(" ");
lcd.print(verde[media]); lcd.print(" ");
paso++;

}
}
// ------------------------ Para rojo ---------------------------
if(paso == 4){

attachInterrupt(0, cuenta, RISING); // Activa la interrupción cuando hay un cambio de estado de subida
if(i>=nmuestra){
i=0;
detachInterrupt(0); // Desactiva la interrupción, mientras hace los cálculos y envia a pantalla
periodo = micros() - tiempoactual; // Periodo en microsegundos
rojo[media] = (nmuestra+1)*1000000 / (periodo);
Serial.print(rojo[media]); Serial.println(" ");
lcd.print(rojo[media]); lcd.print(" ");
paso++;

}
}
if(paso == 5){
// Media de 10 tomas de muestras
lcd.clear();
lcd.setCursor(0,1);
lcd.print("muestra ");
lcd.print(media);
if(media >=muestra ){
paso = 6;
}else{
paso = 1;
media++;
}
}
if(paso == 6){
Serial.println("Valores de lectura para hacer media:");
for(s=0;s<media+1;s++){
Serial.println(s);
// valorblanco = valorblanco + blanco[s];
valorazul = valorazul + azul[s];
valorverde = valorverde + verde[s];
valorrojo = valorrojo + rojo[s];
}
// Serial.print("Total valorblanco = ");Serial.println(valorblanco);
// valorblanco = valorblanco / (media+1);
Serial.println("-------- Calculo para comprobación: -----------");
Serial.print("valorblanco = valorblanco / (media+1)");
Serial.print(valorblanco); Serial.print(" = "); Serial.print(valorblanco); Serial.print(" / "); Serial.println(media+1);
Serial.println("-------------------------------------------------");
Serial.print("Total valorazul = ");Serial.println(valorazul);
valorazul = valorazul / (media+1);
Serial.print("Total valorverde = ");Serial.println(valorverde);
valorverde = valorverde / (media+1);
Serial.print("Total valorrojo = ");Serial.println(valorrojo);
valorrojo = valorrojo / (media+1);
Serial.println("Valores de calibración: ");
Serial.print(valorblanco);
Serial.print(" ");
Serial.print(valorazul);
Serial.print(" ");
Serial.print(valorverde);
Serial.print(" ");
Serial.println(valorrojo);
Serial.println("-----------------------------------------------------------");
paso++;
}
if(paso == 7){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Segunda muestra ->");
lcd.setCursor(0,1);
lcd.print("Presione enter...");
if(analogRead(akey)==key_val[4]){
paso++;
media=0;
Serial.print("Valor media puesta a cero =");Serial.println(media);
}
delay(200);
}

// ------------------------------------ Segunda muestra ---------------------------------------------------------
if(paso == 8){
/*
attachInterrupt(0, cuenta, RISING); // Activa la interrupción cuando hay un cambio de estado de subida
if(i>=nmuestra){
i=0;
detachInterrupt(0); // Desactiva la interrupción, mientras hace los cálculos y envia a pantalla
periodo = micros() - tiempoactual; // Periodo en milisegundos
blanco2[media] = (nmuestra+1)*1000000 / (periodo);
Serial.println(media);
Serial.print(blanco2[media]); Serial.print(" ");
lcd.setCursor(0, 0);
lcd.print(blanco2[media]); lcd.print(" ");
paso++;

}
*/
paso++; // Este paso se quedó dentro del IF anulado
}


...sigue...
Logged

El saber no ocupa lugar, pero sí tiempo

España
Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
// ------------------------ Para azul ---------------------------
if(paso == 9){

attachInterrupt(0, cuenta, RISING); // Activa la interrupción cuando hay un cambio de estado de subida
if(i>=nmuestra){
i=0;
detachInterrupt(0); // Desactiva la interrupción, mientras hace los cálculos y envia a pantalla
periodo = micros() - tiempoactual; // Periodo en milisegundos
azul[media] = (nmuestra+1)*1000000 / (periodo);
Serial.print(azul[media]); Serial.print(" ");
lcd.print(azul[media]); lcd.print(" ");
paso++;
lcd.setCursor(0, 1);

}
}
// ------------------------ Para verde ---------------------------
if(paso == 10){

attachInterrupt(0, cuenta, RISING); // Activa la interrupción cuando hay un cambio de estado de subida
if(i>=nmuestra){
i=0;
detachInterrupt(0); // Desactiva la interrupción, mientras hace los cálculos y envia a pantalla
periodo = micros() - tiempoactual; // Periodo en milisegundos
verde[media] = (nmuestra+1)*1000000 / (periodo);
Serial.print(verde[media]); Serial.print(" ");
lcd.print(verde[media]); lcd.print(" ");
paso++;

}
}
// ------------------------ Para rojo ---------------------------
if(paso == 11){

attachInterrupt(0, cuenta, RISING); // Activa la interrupción cuando hay un cambio de estado de subida
if(i>=nmuestra){
i=0;
detachInterrupt(0); // Desactiva la interrupción, mientras hace los cálculos y envia a pantalla
periodo = micros() - tiempoactual; // Periodo en milisegundos
rojo[media] = (nmuestra+1)*1000000 / (periodo);
Serial.print(rojo[media]); Serial.println(" ");
lcd.print(rojo[media]); lcd.print(" ");
paso++;

}
}
if(paso == 12){
// Media de 10 tomas de muestras
lcd.clear();
lcd.setCursor(0,1);
lcd.print("muestra ");
lcd.print(media);
if(media >=muestra ){
paso = 13;
}else{
paso = 8;
media++;
}
}

if(paso == 13){
for(s=0;s<media+1;s++){
Serial.println(s);
// valorblanco2 = valorblanco2 + blanco2[s];
valorazul2 = valorazul2 + azul[s];
valorverde2 = valorverde2 + verde[s];
valorrojo2 = valorrojo2 + rojo[s];
}
Serial.print("Valor media final , comprobar para operación correcta: "); Serial.println(media);
valorblanco2 = valorblanco2 / (media+1);
Serial.println("-------- Calculo para comprobación: -----------");
Serial.print("valorblanco2 = valorblanco2 / (media+1)");
Serial.print(valorblanco); Serial.print(" = "); Serial.print(valorblanco); Serial.print(" / "); Serial.println(media+1);
Serial.println("-------------------------------------------------");
valorazul2 = valorazul2 / (media+1);
valorverde2 = valorverde2 / (media+1);
valorrojo2 = valorrojo2 / (media+1);
Serial.println("Valores de resultado: ");
Serial.print(valorblanco2);
Serial.print(" ");
Serial.print(valorazul2);
Serial.print(" ");
Serial.print(valorverde2);
Serial.print(" ");
Serial.println(valorrojo2);
Serial.println("---------------------------");
Serial.println("Diferencia en % respecto calibracion:");
Serial.print("Blanco: ");
Serial.println((valorblanco2 / valorblanco)*100);
Serial.print("Azul: ");
Serial.println((valorazul2 / valorazul)*100);
Serial.print("Verde: ");
Serial.println((valorverde2 / valorverde)*100);
Serial.print("Rojo: ");
Serial.println((valorrojo2 / valorrojo)*100);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("B,G,R:");
lcd.print((valorazul2 / valorazul)*100);
lcd.setCursor(0,1);
lcd.print((valorverde2 / valorverde)*100);
lcd.print(" ");
lcd.print((valorrojo2 / valorrojo)*100);
paso++;
}
if(paso == 14){
switch(analogRead(akey)>1000){
//No hace nada hasta que apreta el botón
delay(100);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Press Enter 2a mu");
lcd.setCursor(0,1);
lcd.print("abajo para reiniciar");
}
if(analogRead(akey)==key_val[4]){ // Si presiona enter, vuelve a ver el segundo color para comparar igual con el primero. Pone a 0 los valores del segundo color
paso=8;
valorblanco2=0;
valorazul2=0;
valorverde2=0;
valorrojo2=0;
media= 0;
}else if(analogRead(akey)==key_val[2]){ // Si presiona abajo, reinicia contadores a 0 y empieza de 0
paso = 0;
valorblanco=0;
valorazul=0;
valorverde=0;
valorrojo=0;
valorblanco2=0;
valorazul2=0;
valorverde2=0;
valorrojo2=0;
media = 0;

}
}
}

void cuenta(){ // Función configuración de los bits para seleccionar los 4 sensores, va asociado al paso
if(i==0){
switch(paso){
case 1: // Para blanco
digitalWrite(S2, HIGH);
digitalWrite(S3, LOW);
break;
case 2: // Para azúl
digitalWrite(S2,LOW);
digitalWrite(S3,HIGH);
break;
case 3: // Para verde
digitalWrite(S2,HIGH);
digitalWrite(S3,HIGH);
break;
case 4: // Para rojo
digitalWrite(S2, LOW);
digitalWrite(S3, LOW);
break;
case 8: // Para blanco
digitalWrite(S2, HIGH);
digitalWrite(S3, LOW);
break;
case 9: // Para azúl
digitalWrite(S2,LOW);
digitalWrite(S3,HIGH);
break;
case 10: // Para verde
digitalWrite(S2,HIGH);
digitalWrite(S3,HIGH);
break;
case 11: // Para rojo
digitalWrite(S2, LOW);
digitalWrite(S3, LOW);
break;
}
tiempoactual=micros(); // Guarda el inicio del contador de tiempo de microsegundos.
}
i++;

}

Saludos
« Last Edit: December 03, 2011, 03:23:35 pm by Alfon » Logged

El saber no ocupa lugar, pero sí tiempo

Pages: [1]   Go Up
Jump to: