domingo, 16 de dezembro de 2012

Arduino TCS3200D Sensor de cor RGB




O TCS3200D é um sensor estático de cor. Ele possui 16 foto transistores com filtro Vermelho (R), 16 com filtro Verde  (G), 16 com filtro para azul(B) e 16 sem filtro (branco). Os filtros impedem a passagem das demais cores permitido que o fototraisistor seja excitado apenas pela luz quista. Através de dois pinos é possível selecionar qual grupo de fototransistor estará ativo e consequentemente qual luz será captada pelo sensor. A saída do sinal no sensor em uma onda quadra com frequência proporciona a intensidade da cor medida R, G, B, W.

O sensor

Como mencionado na introdução o sensor é basicamente constituído por quatro grupos de fototrasistores três desses grupos com filtros.

Os fototransistores são simplificadamente, transistores cujas bases são expostas. A incidência de luz sobre a base faz com que o transistor conduza. A variação dessa luz provoca variação proporcional na corrente que circula no fototransistor até o limite de saturação. Colocando um filtro que só deixe passar luz vermelha, por exemplo, apenas a luz vermelha fara conduzir o fototransistor. Então a corrente que circula no fototransistor será função da luz vermelha sobre ele.
É isso que ocorre no sensor, Um grupo de sensores não tem filtro algum o que o torna sensível a toda luz que incida sobre ele e cada um dos três outros grupos de sensores é dotado de filtro para uma determinada cor de luz vermelho, verde e azul.


Através dos pinos S2 e S3 é possível selecionar qual grupo de fototransistores estará ativo e consequentemente qual luz o sensor estará medindo e mostrando em sua saída. A tabela ao lado mostra as combinações destes pinos e o respectivos sensores selecionados.


Outra possibilidade de configuração do sensor é escalonar a frequência de saída escolhendo através dos pinos S0 e S1. A tabela ao lado mostra as combinações destes pinos e as respectivas escalas.



Recebendo os dados do sensor



A saída do sensor e uma onda com frequência diretamente proporcional a intensidade da cor medida naquele instante. Queremos obter a frequência em ciclos por segundo. Como se trada de uma onda quadrada com "duty cicle" de 50% (metade do ciclo em nível alto e metade em nível baixo). Então o programa deve contar todos os ciclos que ocorrem em um segundo. Ou estimar contando por 1/2 segundo e multiplicando por 2.

Uma forma de contar os ciclos para determinar a frequência é usando uma interrupção externa e um timmer o timer é setado para um segundo, por exemplo, e durante esse tempo uma variável é incrementada toda vez que a saída do sensor ligada ao pino de interrupção correspondente sai do nível baixo para o alto.


Para receber  os dados do sensor deve-se selecionar o filtro através de S1, S2, S3 e S4 conforme a tabela abaixo e receber e decodificar a informação sobre a cor selecionada através do pino OUT.


O programa para o arduino mostrado abaixo foi adaptado do programa encontrado neste site. usa a solução timme e interrupção externa. O programa para processing foi escrito suando como base o exemplo que acompanha a IDE do arduino "Arduino call response". Na primeira leitura o programa ajusta o balanço de branco, por isso é importante expor o sensor a referência de branco ao iniciar o arduino. No vídeo eu coloco uma folha em branco sobre o sensor ao iniciar o arduino para ter esta referência.

Código para o Arduino:

// este codigo e uma adaptação do coodigo encontrado em:
//http://www.elecfreaks.com/1666.html

//é necessario a biblioteca Timer1 que pode ser baixada aqui:
//http://www.arduino.cc/playground/Code/Timer1
// baixe renomei-a para TimerOne e adcione-a na pasta libriries da ide do arduino

#include <TimerOne.h>

#define OUT  2
#define S2   5
#define S3   6
#define S0   3
#define S1   4

volatile int g_count =0;
int g_flag = 0;
int g_array[3] = {0};
float g_SF[3] = {0};

void setup()
{
  // Init TSC230 and setting Frequency.
  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  pinMode(OUT, INPUT);

  digitalWrite(S0, LOW);  // OUTPUT FREQUENCY SCALING 2%
  digitalWrite(S1, HIGH);
  
  Serial.begin(115200);
  Timer1.initialize();             // defaulte is 1s
  Timer1.attachInterrupt(TSC_Callback);
  attachInterrupt(0, TSC_Count, RISING);

  delay(4000);

  g_SF[0] = 255.0/ g_array[0];     //R Scale factor
  g_SF[1] = 255.0/ g_array[1] ;    //G Scale factor
  g_SF[2] = 255.0/ g_array[2] ;    //B Scale factor

  estabeleceContato(); // aguarda contato com o processing
}
void loop()
{
   g_flag = 0;
   for(int i=0; i<3; i++)
   {
   int val = int(g_array[i] * g_SF[i]);
   val = constrain(val, 0, 255);
   Serial.write(val);
   }
   delay(4000);

}

// seta o filtro
void TSC_FilterColor(int Level01, int Level02)
{
  if(Level01 != 0)
    Level01 = HIGH;

  if(Level02 != 0)
    Level02 = HIGH;

  digitalWrite(S2, Level01);
  digitalWrite(S3, Level02);
}
// incrementa g_cont cada vex que a sáida do sensor
// vai do nivel baixo ao alto
void TSC_Count()// finção chamada pela interrupção
{
  g_count ++ ;  // incrementa g_cont
}

void TSC_Callback()
{
  switch(g_flag)
  {
    case 0:
          TSC_WB(LOW, LOW);     //Red
         break;
    case 1:
         g_array[0] = g_count;
         TSC_WB(HIGH, HIGH);    //Green
         break;
    case 2:
         g_array[1] = g_count;
         TSC_WB(LOW, HIGH);     //Blue
         break;
    case 3:
         g_array[2] = g_count;
         TSC_WB(HIGH, LOW);     //Clear(no filter)
         break;
   default:
         g_count = 0;
         break;
  }
}

void TSC_WB(int Level0, int Level1) //balanceamento de branco
{
  g_count = 0;
  g_flag ++;
  TSC_FilterColor(Level0, Level1);
  Timer1.setPeriod(1000000);    // define periodo de 1 segundo
}

 void estabeleceContato() {
    while (Serial.available() <= 0) {
    Serial.write('A');   // envia A ate estabelecer contato
    delay(300);
  }
 }

Código para o Processing:

// Adaptado do exemplo SerialCallResponse que acompanha a IDE do Arduino

import processing.serial.*;

 int comando =0;
 int y_01 = 55;
 
Serial myPort; // porta serial
int[] serialInArray = new int[3]; // vetor para armazenar o valores recebidos
int serialCount = 0;           // usado para contar o numero de bytes recebidos

int[] buffer = new int[3];

boolean firstContact = false;  // Whether we've heard from the microcontroller

void setup() {
  // imprime a lista de portas seriais
  println(Serial.list());

  String portName = Serial.list()[0];
  // substituir COM9 pela porta onde esta o Arduino
  myPort = new Serial(this, "COM9", 115200); 
  size(800, 600);  //janela, largura, altura
  smooth();
  
}

void draw() {

 
    background(buffer[0],buffer[1],buffer[2]);      
    
}


void serialEvent(Serial myPort) {
  
  // le os bytes da porta serial
  int inByte = myPort.read();
  // se receber o primeiro byte e for 'A'
  if (firstContact == false) {
    if (inByte == 'A') { 
      myPort.clear();       // Limpa o buffer da porta serial
      firstContact = true;  // o primeiro contato ja ocorreu
      myPort.write('A');    // envia "A" para que o arduino 
                            // reconheça o primeiro contato
    } 
  } 
  else {
    // se não for o primeiro byte adiciona-o ao vetor:
    serialInArray[serialCount] = inByte;
    serialCount++;

    // quando receber os 12 bytes armazena-os em buffer[i]
    if (serialCount > 2) {
      for(int i =0; i<3; i++)
      {
      buffer[i] =  serialInArray[i];
      }       
        // envia comandos caso existam
      myPort.write(comando);
      // Reset serialCount:
      serialCount = 0;
    }
  }
}