Manual

do

Maker

.

com

Eletronica digital - Sensor de cores

Eletronica digital - Sensor de cores

Recentemente adquiri um sensor de cores aqui no Brasil mesmo, para fazer parte da instrumentação da decoração do quarto de minha filha; um sensor da Parallax, o ColorPal. O problema seria que enquanto nos EUA o custo é de 20 dólares, aqui no Brasil o custo gira em torno de 120 reais, mas o problema não parou por ai.

O sensor é 1-wire, mas não consegui fazer a comunicação com ele utilizando o bus pirate para testar a comunicação utilizando o respectivo protocolo. Então, testei com UART a 4800kbps e consegui enviar comandos, mas não recuperá-los. Passei 2 dias a fazê-lo, ansiava por concluir essa etapa do projeto, mas aconteceu que desisti e resolvi fazer meu próprio sensor de cores.

O conceito mais simples de detecção de cores sem filtros é com a utilização de um LED RGB e um fotosensor; além de fazer o sensor de cores, decidi torná-lo open-hardware e open-source (mas se preferir, encomende comigo).

sensor de cores - como funciona

Para recolher amostras de cores o sensor testa cor a cor da base RGB. Acende o vermelho do LED e o fotosensor de encarrega de ler o retorno. A quantidade de luz absorvida pela superfície diante do sensor é a quantidade oposta de vermelho contida na superfície, o restante é refletido. O mesmo processo é feito para o verde e para o azul.

Nas cores pigmentos a base é amarelo, magenta e ciano, repare que as cores luz são diferentes. Outra coisa a levar em consideração - o sensor funciona apenas para detecção de cores de superfícies refletivas. Ele não é capaz de detectar por exemplo, a cor azul na tela de seu computador.

Precisão

Para que haja uma boa precisão, é necessário que se faça a calibração do sensor de cores. Nesse ponto, será necessário ler o datasheet do LED RGB utilizado, assim como o datasheet do fotosensor escolhido para obter as informações de curvas, influências externas, variação de temperatura, etc.

Não fiz o perfeito, apenas o satisfatório. Para tal, coloquei uma folha branca diante do sensor  de cores (o mais próximo possível), então liguei cor a cor do LED e anotei o quanto refletia. Dessa forma soube que 100% da cor vermelha estabiliza em 642 no sinal analógico de 10bits na composição do meu sensor. Logo, perceba que há uma perda muito grande que provavelmente pode ser retificada com ajuste da posição do led e/ou fotosensor. Em 10 bits,a resolução chega a 1024 valores.

Depois basta fazer uma regra de 3 quando estiver recolhendo uma amostra de cada cor. Para converter o resultado em valor para acender o LED, novamente uma regra de 3, sabendo dessa vez que 100% corresponde a 255 em todas as cores. Pegando a porcentagem, converte-se em valor de sinal e passa-se para o analogWrite() o valor PWM a escrever na saída digital. Funcionou bem, mesmo com a perda, porém para um bom resultado necessito deixar o objeto o mais próximo possível.

As provas de conceito em Arduino podem ser vistos nesse vídeo. Também nesse outro.

Mas a tarefa não acaba aqui.

ldr.webp

Componentes escolhidos e suas respectivas informações

Para esse projeto serão necessários 2 componentes principais:

  • Fotosensor

-LED RGB

Utilizei um LED RGB de alto-brilho, que possui 8 bits de resolução.

Além disso, claro que é possível aplicar tunnings do lado da controladora que recebe a informação, assim como é possível fazê-lo na própria MCU utilizada no sensor. Essa calibração pode ser aferida recolhendo as amostras RGB e acendendo um LED RGB com os valores detectados para comparação. Não se preocupe com a complexidade, pois mais adiante você terá a descrição de um código.

O sensor está gerando cores satisfatoriamente, mas terei que melhorá-lo (ou você o fará, se quiser participar).

Inicialmente fiz o código de prova no Arduino, para depois migrá-lo para o PIC designado para este projeto. Detalhes sobre LED RGB podem ser vistos nesse post. No Arduino fiz unicamente para testar a amostragem e não implementei o sensor de proximidade infra-vermelho. Se quiser fazê-lo, o procedimento pode ser visto nesse post.

caneta.webp

O sensor foi montado em uma porta de espelho, onde os furos fiz com uma Dremel. A ponta preta onde acoplei o fotosensor é a ponta de uma caneta de quadro branco.

Basicamente, o código para testes em Arduino é este:


//Maximas capturadas sobre fundo branco
int Rmax = 720;
int Gmax = 605;
int Bmax = 440;
int ACESO = 255;

int interf;

int incomingByte = '0';

int i;

int RGB[3];
int resultado;

int MAXIMUM = 0;

//cor da vez
int RED = 0;
int GREEN = 1;
int BLUE = 2;

//captura pelo fotosensor (an0)
int freq[3];

int Me;
int nA;

//proximo de zero
int desvio = 7;
//maximo no led
int LEDmax = 255;

int teste;

//levanta a cor final no led
void colorOn(){
   analogWrite(9,RGB[RED]);
   analogWrite(5,RGB[GREEN]);
   analogWrite(3,RGB[BLUE]);
}

void colorOff(){
   analogWrite(9,0);
   analogWrite(5,0);
   analogWrite(3,0);
}

//devolve a resposta da cor solicitada
int freq2rgb(int cor,int media){
   if (cor == RED){
      MAXIMUM = Rmax;
   }
   else if (cor == GREEN){
      MAXIMUM = Gmax;
   }
   else if(cor == BLUE){
      MAXIMUM = Bmax;
   }
   resultado = ((media-desvio+interf) * 100)/MAXIMUM;
   Serial.println("resultado = ((media-(desvio+interf)) *  100)/MAXIMUM;");
   resultado = (resultado * 255)/100;
   return resultado;
}

void run(){
   interf = analogRead(0)*3;

   analogWrite(9,ACESO);
   delay(200);
   freq[0] = analogRead(0);
   freq[1] = analogRead(0);
   freq[2] = analogRead(0);
   Me = freq[0] + freq[1] + freq[2]/nA;

   RGB[RED] = freq2rgb(RED,Me);
   analogWrite(9,0);
   analogWrite(5,ACESO);
   delay(200);

   freq[0] = analogRead(0);
   freq[1] = analogRead(0);
   freq[2] = analogRead(0);
   Me = freq[0] + freq[1] + freq[2]/nA;
   RGB[GREEN] = freq2rgb(GREEN,Me);
   analogWrite(5,0);
   analogWrite(3,ACESO);
   delay(200);

   freq[0] = analogRead(0);
   freq[1] = analogRead(0);
   freq[2] = analogRead(0);
   Me = freq[0] + freq[1] + freq[2]/nA;
   RGB[BLUE] = freq2rgb(BLUE,Me);
   analogWrite(3,0);

   colorOn();

   teste = analogRead(0);
}

void setup(){
   pinMode(3,OUTPUT);//BLUE
   pinMode(5,OUTPUT);//GREEN
   pinMode(9,OUTPUT);//RED
   Serial.begin(9600);
}

void loop(){
   if (Serial.available() > 0) {
      incomingByte = Serial.read();
   }
   if (incomingByte == 'r'){
      run();
   }
   else if (incomingByte == '-'){
      colorOff();
   }
   incomingByte = '0';
   delay(1000);
   }

MCU

pic16f827.webp

Para esse projeto, escolhi como controlador o PIC16F1827, comprado na MicroManiacs. Ele tem muitos recursos e apenas 18 pinos, mas para utilizá-lo com o PICKit2, siga as orientações desse post.

Informações sobre o PIC16F1827

Algumas informações dessa MCU:

Memoria de programa (KB)     7 (Flash)
Velocidade da CPU (MIPS)      8
 RAM Bytes    384
 Data EEPROM (bytes)      256
 Periféricos de comunicação   1-A/E/USART, 2-MSSP(SPI/I2C)
 Capture/Compare/PWM      2 CCP, 2 ECCP
 Timers   4 x 8-bit, 1 x 16-bit
 ADC      12 ch, 10-bit
 Comparadores     2
Faixa de temperatura (C)      -40 to 125
 Operating Voltage Range (V)      1.8 to 5.5
Embalagem     18  PDIP
 XLP     sim
 Cap Touch    12

Ainda faltam algumas partes para finalizar, mas a prova de conceito em PIC foi executada com esse código:


/*
          U
     RA2 -|1.18|- RA1 LDR (analog)
PWMG RA3 -|2.17|- RA0 IR (analog)
PWMB RA4 -|3.16|- RA7 RSensor
     RA5 -|4.15|- RA6
     Vss -|5.14|- Vcc
     RB0 -|6.13|- RB7
  RX RB1 -|7.12|- RB6
  TX RB2 -|8.11|- RB5 Gsensor
 PWMR RB3 -|9.10|- RB4 Bsensor
*/

//sensor
sbit TRIS_RED at TRISA7_bit;
sbit TRIS_BLUE at TRISB4_bit;
sbit TRIS_GREEN at TRISB5_bit;

//pinos digital para o sensor
sbit RED_PINO at RA7_bit;
sbit GREEN_PINO at RB5_bit;
sbit BLUE_PINO at RB4_bit;

//LDR
//sbit TRIS_LDR at TRISA1_bit;

//Maximas do R,G,B capturadas pelo LDR em Arduino
//TODO: RECALIBRAR - tentar equiparar os valores com pwm
unsigned short int Rmax = 720;
unsigned short int Gmax = 605;
unsigned short int Bmax = 440;

unsigned short int MAXIMUM;

//interferência externa, capturada previamente à medição RGB.
unsigned short interf;

//Que convenção definiu 'i' como variável para loop?
short int i;

//Guarda os Valores RGB
unsigned short int RGB[3];

//Definição das cores
short int RED = 0;
short int GREEN = 1;
short int BLUE = 2;

//captura pelo fotosensor (RA1)
short unsigned int freq[3];

//Variaveis para regra de 3
short unsigned int Me;
short unsigned int nA;
short unsigned int resultado;

//valor de correção (esse deve ser calibrado para cada sensor pela variação
int desvio = 7;

unsigned short current_duty , old_duty,
current_duty1, old_duty1,
current_duty2, old_duty2,
current_duty3, old_duty3;

short int ESTADO = 1;
short int val = 0;
short unsigned int objectNear;

//identificadores dos sensores analogicos
short int LDR = 18;
short int IR = 17;

void colorOn(){
PWM1_Set_Duty(RGB[RED]);
PWM3_Set_Duty(RGB[GREEN]);
PWM4_Set_Duty(RGB[BLUE]);
}

//desliga a cor no led RGB baixando os pinos pwm e pull-up no pino terra do RGB.
void colorOff(){
PWM1_Set_Duty(0);
PWM3_Set_Duty(0);
PWM4_Set_Duty(0);
}

//devolve a resposta da cor. essa função é chamada a cada amostragem do RGB.
unsigned short int freq2rgb(int cor){
if (cor == RED){
MAXIMUM = Rmax;
}
else if (cor == GREEN){
MAXIMUM = Gmax;
}
else if(cor == BLUE){
MAXIMUM = Bmax;
}
resultado = ((Me-desvio+interf) * 100)/MAXIMUM;
resultado = (resultado * 255)/100;
return resultado;
}

//se o sensor de proximidade detectar um objeto, essa função será chamada
void run(){
//TODO: ler o LDR para testar a calibraçao

interf = ADC_Read(LDR)*3;

RED_PINO = 1;
Delay_ms(30);
freq[0] = ADC_Read(LDR);
freq[1] = ADC_Read(LDR);
freq[2] = ADC_Read(LDR);
Me = freq[0] + freq[1] + freq[2]/nA;

RGB[RED] = freq2rgb(RED);
RED_PINO = 0;
GREEN_PINO = 1;
Delay_ms(30);

freq[0] = ADC_Read(LDR);
freq[1] = ADC_Read(LDR);
freq[2] = ADC_Read(LDR);

Me = freq[0] + freq[1] + freq[2]/nA;
RGB[GREEN] = freq2rgb(GREEN);
GREEN_PINO = 0;
BLUE_PINO = 1;
Delay_ms(30);

freq[0] = ADC_Read(LDR);
freq[1] = ADC_Read(LDR);
freq[2] = ADC_Read(LDR);

Me = freq[0] + freq[1] + freq[2]/nA;
RGB[BLUE] = freq2rgb(BLUE);
BLUE_PINO = 0;

//levanta os valores RGB
colorOn();
RED_PINO = 0;
BLUE_PINO = 0;
GREEN_PINO = 0;
}

/*
void interrupt(){
GIE_bit = 0;
if (RCIF_bit == 1){
run();
}
RCIF_bit = 0;
GIE_bit = 1;
}
*/

/*
void colorUp(short int COLOR){
if (COLOR == RED){
PWM1_Set_Duty(80);
}
else if (COLOR == GREEN){
PWM3_Set_Duty(80);
}
else if (COLOR == BLUE){
PWM4_Set_Duty(80);
}
}
*/

void setup(){
void ADC_Init();
//RCIE_bit = 1; // Ativa interrução no RX
//TXIE_bit = 0; // Desativa no TX
//PEIE_bit = 1; // Ativa interrupção na chave de periféricos
//GIE_bit = 1; // Ativa a chave geral

ANSA0_bit = 1; //ativa analog na porta RA0 (IR)
ANSA1_bit = 1; //ativa analog na porta RA1 (LDR)

//INTCON = 0;
C1ON_bit = 0; // sem comparadores
TRISA.B0 = 1; //porta AN0 em input
TRISA.B1 = 1; //porta AN1 em input

//pinos pwm
TRIS_RED = 0;
TRIS_BLUE = 0;
TRIS_GREEN = 0;

PWM1_Init(4000);
PWM1_Set_Duty(0);
PWM1_Start();

PWM3_Init(4000);
PWM3_Set_Duty(0);
PWM3_Start();

PWM4_Init(4000);
PWM4_Set_Duty(0);
PWM4_Start();
}

void rgbUp(){
RED_PINO = 1;
Delay_ms(20);
RED_PINO = 0;
GREEN_PINO = 1;
Delay_ms(20);
GREEN_PINO = 0;
BLUE_PINO = 1;
Delay_ms(20);
BLUE_PINO = 0;
}

void main() {
setup();
//rgbUp();
run();
colorOn();
while (1){
objectNear = ADC_Get_Sample(IR); //17 é o pino que corresponde a AN0
// if (objectNear < 900){
// run();
// colorOn();
// }
Delay_ms(40);
}
}
/// PWM3_Set_Duty(val);
/*
PORTA.B0 = ESTADO;
ESTADO = !ESTADO;
Delay_ms(4);
*/
/// val += 1;
/// if (val == 256){
/// val = 0;
/// }

sensorcompic.webp

Para esse código (que ainda está sendo debugado e certamente sofrerá alterações), deixei a protoboard nesse estado:

Foi necessário recalibrar o sensor por diversos motivos; fiz um segundo sensor utilizando LED RGB difuso, o que sensibilizou a calibração. Além disso, acoplei o sensor LDR de uma maneira diferente, então tudo influenciou no resultado. Nesse modelo tive que colocar o objeto a ser capturado em ângulo. Não pude certificar o porque ainda, mas suspeito que seja porque o fundo está aberto diretamente com a protoboard e isso influencia o resultado também.

Claramente é mais complicado em relação ao código do Arduino, mas no Arduino fiz apenas a prova de conceito. No PIC as diferenças são consideráveis; o LED RGB do sensor é ligado a pinos digitais sem PWM, pois cada cor é ligada em modo full. Além disso, depois que a cor RGB for aplicada aos LEDs de decoração, o LED do sensor é apagado. No PIC, quando a cor é capturada, após 3 segundos a cor é gravada na EEPROM e é automaticamente carregada no caso de reset do PIC. Isso é, exceto o objeto ainda esteja diante do sensor. Nesse caso, o processo de captura é reiniciado e assim indefinidamente ae que o sensor de proximidade não perceba mais um objeto diante de si.

Como ainda não está 100% concluído, não vou colocar o desenho em Fritzing, mas farei um post com o resultado, mostrando uma bela decoração de quarto utilizando esse sensor, apesar de já ter também outros propósitos para ele.

Repare que a prova de conceito utiliza apenas 3 pinos pwm, um analógico, GND e +5v:

poc-cores.webp

Como sobraram pinos no PIC, pode-se incrementar um pouco mais o projeto. Por exemplo, colocando um buzzer para fazer o 'bip' dos 3 segundos antes de gravar a EEPROM. Mas enfim, estou há tantos dias trabalhando nesse projeto que a ansiedade não me deixa fazer mais por agora.

Para quem está acostumado com Arduino, é bastante complicado depurar PIC, afinal não se tem à mão uma saida serial. Por esse motivo montei o display no circuito, onde deveria ter feito uma comunicação UART, porém algumas implicações me obrigaram a tomar outro rumo e acabei fazendo os debugs em outra MCU do mesmo modelo, repassando o código posteriormente para o PIC principal.

Artesanato à parte...

Terminada essa parte técnica, ainda falta a parte artística. Olhei algumas silhuetas de madeira na C&C, Telha Norte e Leroy Merlin. O preço da silhueta (nem era tão grande) era de quase NoVeNtA ReAiS ! Vou fazer o recorte em madeira eu mesmo, porque a madeira para isso custa em torno de 4 reais. Diferente o preço, não?

Para o teste, recortei uma silhueta de gato em papel Paraná. Para quem não conhece, é um papel de gramatura extremamente alta, parecendo bastante com aquelas madeiras finas de colocar atrás do guarda-roupa. A silhueta não ficou bem acabada, mas servirá para o exemplo.

Uma recomendação é que a silhueta seja da mesma cor da parede, de forma que só terá destaque quando ligado o LED. O efeito valoriza muito o tema, veja a silhueta de teste:

gato.webp

O acabamento é feito com contact branco, para refletir e 'esparramar' a cor projetada na parte traseira do desenho.

O hardware poderia ficar completamente na traseira do desenho, saindo apenas 2 fios para a fonte, mas nesse caso pensei em colocar papel de parede por sobre os fios (que são do tipo mais fino e portanto não salientarão sob o papel de parede).

Para prender o desenho a parede, duas formas muito simples; fita dupla-face ou a fita da 3M para quadros.

Assim que estiver 100% concluído, libero um video em outro post, pois como pode ser visto, esse é um projeto de baixo custo, porém trabalhoso. Se você considerar que o importante é o resultado, então terá diversão garantida!

Se gostou, não deixe de compartilhar; dê seu like no video e inscreva-se no nosso canal Manual do Maker Brasil no YouTube.

Próximo post a caminho!

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.