Componentes · 14 min de leitura

Sensor de cor TCS3200 com Arduino: ligações, calibração e código

Como funciona o sensor de cor TCS3200: ajustes de S0–S3, leitura de R, G e B com pulseIn, calibração de branco e preto, separação por cores e código para ESP32, Pico e TCS34725.

O sensor de cor TCS3200 é o módulo de detecção de cor mais usado em projetos Arduino: ele mede a cor da superfície à sua frente separando-a em componentes vermelho, verde e azul. Ele converte a luz captada por uma matriz de fotodiodos direto em uma frequência: quanto mais luz vermelha uma superfície reflete, maior a frequência no canal vermelho. Não há leitura analógica nem protocolo de comunicação especial; você lê os valores de R, G e B em poucas linhas com o comando pulseIn() do Arduino. Máquinas que separam balas, robôs que reconhecem cores, veículos que mudam de direção em marcas coloridas no chão e luminárias que copiam a cor de um objeto colocado na frente delas são todos feitos com este sensor.

Neste guia você vai ver, passo a passo, o que há dentro do sensor, para que servem os pinos S0–S3, como ligá-lo a um Arduino Uno, a um ESP32 e a um Raspberry Pi Pico, como fazer leituras brutas, como fazer uma calibração de branco e preto e um algoritmo que descobre a qual cor uma leitura pertence. Depois vamos montar a lógica de uma máquina que separa objetos por cor e, por fim, comparar com o TCS34725, um sensor que funciona por I2C.

Módulo sensor de cor TCS3200
Módulo sensor de cor TCS3200: o chip TCS3200 de 8 pinos no meio, LEDs brancos nos quatro cantos para iluminar a superfície, com os pinos S0, S1, OE, GND de um lado e VCC, OUT, S2, S3 do outro.

O que é o sensor de cor TCS3200 e como ele funciona?

O chip TCS3200 no coração do módulo (a versão mais antiga se chama TCS230) tem 64 fotodiodos em uma grade de 8×8 na superfície. Dezesseis deles ficam sob filtros vermelhos, 16 sob verdes, 16 sob azuis, e os outros 16 não têm filtro (clear) e veem a quantidade total de luz. Os 16 fotodiodos de cada cor são ligados em paralelo, e os pinos S2 e S3 escolhem qual grupo é medido. Como os fotodiodos de cores diferentes estão intercalados na matriz, a luz que cai em um canto da superfície afeta igualmente todos os grupos de cor.

A pequena corrente produzida pelo grupo de fotodiodos selecionado vai para um conversor de corrente em frequência dentro do chip. Esse circuito gera uma onda quadrada com ciclo de trabalho de 50%, e a frequência da onda é diretamente proporcional à intensidade da luz. Então o que você mede não é uma tensão, e sim o número de pulsos por segundo: alguns hertz no escuro total, dezenas de quilohertz perto de uma superfície branca. A vantagem de uma saída em frequência é que ela é robusta contra ruído e pode ser ligada a qualquer pino digital do Arduino. Se você só quer medir a intensidade da luz, um sensor de luz LDR é uma opção mais simples; o TCS3200 faz o mesmo trabalho separadamente para três cores.

CaracterísticaValor
ChipTCS3200 (versão antiga TCS230, compatível nos pinos)
Matriz de fotodiodos8×8 = 64: 16 vermelhos, 16 verdes, 16 azuis, 16 sem filtro
SaídaOnda quadrada com ciclo de trabalho de 50%, frequência proporcional à luz
Frequência de fundo de escala (típica)600 kHz (100%), 120 kHz (20%), 12 kHz (2%)
Tensão de alimentação2,7–5,5 V
Corrente do chip1,4 mA típica (a corrente dos LEDs do módulo vem por cima)
Pinos de controleS0, S1 (escala de frequência), S2, S3 (filtro de cor), OE (habilitação da saída, ativo em LOW)
Distância recomendadaCerca de 1–3 cm, a mesma em todas as leituras
Iluminação4 LEDs brancos no módulo

Pinos S0–S3: escala de frequência e escolha do filtro de cor

Quatro pinos de controle definem como o TCS3200 se comporta. S0 e S1 reduzem a frequência de saída: em 100% os pulsos encolhem para alguns microssegundos e forçam os limites de precisão de medição do Arduino Uno, enquanto em 2% os pulsos ficam mais longos, mas chegam menos deles no mesmo tempo. S2 e S3 escolhem qual filtro de cor é lido. Você liga esses pinos às saídas digitais do Arduino e os ajusta pelo código antes de cada leitura.

S0S1Escala da frequência de saídaQuando usar
LOWLOWDesligado (power down)Para pôr o sensor para dormir em projetos com bateria
LOWHIGH2% (tipicamente até 12 kHz)Processadores lentos, MicroPython e contagem por interrupção
HIGHLOW20% (tipicamente até 120 kHz)O melhor ajuste para Arduino e pulseIn
HIGHHIGH100% (tipicamente até 600 kHz)Medições rápidas com um contador de hardware
S2S3Grupo de fotodiodos lido
LOWLOWFiltro vermelho
LOWHIGHFiltro azul
HIGHLOWSem filtro (clear, luz total)
HIGHHIGHFiltro verde
A relação entre a largura do pulso e a frequência é simples: como o ciclo de trabalho é de 50%, a parte em LOW dura metade do período. Com a fórmula f = 1 / (2 × t), você converte em frequência os microssegundos do pulseIn. Por exemplo, se t = 25 µs, então f = 1 / (2 × 0,000025 s) = 20 kHz. Na escala de 20%, com os LEDs do módulo acesos, valores entre 10 e 200 µs são normais.

Ligações do TCS3200: Arduino Uno, ESP32 e Pico

Pino do móduloArduino UnoESP32Raspberry Pi Pico
VCC5V3V33V3(OUT), pino 36
GNDGNDGNDGND
S0D4GPIO 32GP2
S1D5GPIO 33GP3
S2D6GPIO 25GP4
S3D7GPIO 26GP5
OUTD8GPIO 27GP6
OEGNDGNDGND
Não deixe o pino OE solto. Esse pino é ativo em LOW; se estiver em HIGH ou desligado, a saída fica em alta impedância e o pulseIn retorna 0 o tempo todo. Além disso, os pinos do ESP32 e do Pico funcionam com 3,3 V: se você alimentar o módulo com 5 V, o pino OUT gera pulsos de 5 V e força a entrada da placa. Nessas placas, alimente o módulo pelo 3V3 ou coloque um divisor de tensão ou um conversor de nível lógico na linha OUT.

Os quatro LEDs brancos do módulo iluminam a superfície do mesmo jeito em toda leitura, então os resultados ficam mais consistentes mesmo quando a iluminação da sala muda. Algumas placas têm um pino LED separado para ligar e desligar os LEDs, enquanto em outras os LEDs acendem assim que a alimentação é ligada; confira as marcações da sua placa. Para os melhores resultados, envolva o sensor em um tubo preto ou em papelão, traga o objeto sempre à mesma distância e evite superfícies brilhantes e reflexivas. Você pode conferir a disposição dos pinos da sua placa na ferramenta Pinagem.

Passo a passo: lendo R, G e B com pulseIn

  1. Ligue o módulo como na tabela acima; ligue o OE ao GND e o VCC ao 5V.
  2. Escolha a escala de frequência de 20% pondo S0 em HIGH e S1 em LOW.
  3. Para o vermelho, ponha S2 e S3 em LOW e espere alguns milissegundos para a saída se estabilizar.
  4. Meça em microssegundos a duração da parte em LOW do pulso com pulseIn(OUT, LOW); faça a média de 10 leituras para reduzir o ruído.
  5. Repita o mesmo para o verde (S2 HIGH, S3 HIGH) e para o azul (S2 LOW, S3 HIGH).
  6. Acompanhe os resultados no Monitor Serial em 9600 baud e ponha folhas de papel de cores diferentes na frente do sensor.
C++ (Arduino IDE)
1// TCS3200 ile ham R, G, B okuma / Raw R, G, B reading with the TCS3200
2const int S0 = 4;
3const int S1 = 5;
4const int S2 = 6;
5const int S3 = 7;
6const int OUT_PIN = 8;
7
8// Filtreyi seçer, 10 darbenin ortalama LOW süresini döndürür (µs) / Selects a filter, returns the average LOW time of 10 pulses (µs)
9unsigned long readPulse(bool s2, bool s3) {
10  digitalWrite(S2, s2 ? HIGH : LOW);
11  digitalWrite(S3, s3 ? HIGH : LOW);
12  delay(5);                                           // çıkışın oturmasını bekle / let the output settle
13  unsigned long total = 0;
14  int count = 0;
15  for (int i = 0; i < 10; i++) {
16    unsigned long t = pulseIn(OUT_PIN, LOW, 50000UL); // 50 ms zaman aşımı / 50 ms timeout
17    if (t > 0) {
18      total += t;
19      count++;
20    }
21  }
22  return count > 0 ? total / count : 50000UL;         // darbe yoksa çok karanlık / no pulse means very dark
23}
24
25void setup() {
26  pinMode(S0, OUTPUT);
27  pinMode(S1, OUTPUT);
28  pinMode(S2, OUTPUT);
29  pinMode(S3, OUTPUT);
30  pinMode(OUT_PIN, INPUT);
31  digitalWrite(S0, HIGH);                             // S0=HIGH, S1=LOW: %20 ölçek / 20% scaling
32  digitalWrite(S1, LOW);
33  Serial.begin(9600);
34}
35
36void loop() {
37  unsigned long r = readPulse(false, false);          // kırmızı filtre / red filter
38  unsigned long g = readPulse(true, true);            // yeşil filtre / green filter
39  unsigned long b = readPulse(false, true);           // mavi filtre / blue filter
40  Serial.print("R: ");
41  Serial.print(r);
42  Serial.print("  G: ");
43  Serial.print(g);
44  Serial.print("  B: ");
45  Serial.println(b);
46  delay(500);
47}

O ponto mais importante deste código é que o número lido é inversamente proporcional à luz: quanto menor o valor, mais luz vem daquela cor. Quando você mostra uma folha vermelha, vai ver uma saída como “R: 24 G: 68 B: 52”; o menor número está no canal vermelho. O tempo limite de 50.000 µs passado ao pulseIn impede o código de esperar o 1 segundo padrão quando nenhum pulso chega; se nenhum pulso for captado, a função retorna 50.000, o que significa “muito escuro”. Se tiver dificuldade para ler a saída serial, dê uma olhada no guia do Monitor Serial do Arduino.

Calibração de branco e preto: transformando valores brutos em 0–255

Os valores brutos, sozinhos, enganam, porque os três grupos de fotodiodos não têm a mesma sensibilidade e os LEDs brancos do módulo não emitem todas as cores por igual. Por isso, até uma folha perfeitamente branca dá números diferentes nos três canais. A calibração elimina essas diferenças e coloca cada canal entre 0 (preto) e 255 (branco); é a mesma ideia do balanço de branco de uma câmera. Faça a calibração onde o sensor vai trabalhar de verdade, com a mesma luz e na mesma distância; uma calibração feita na sua mesa não vale dentro do gabinete da máquina.

  1. Monte o sensor em um suporte fixo e defina a distância de medição (por exemplo, 1,5 cm) de modo que ela não mude.
  2. Ponha uma folha branca lisa na frente dele, leia os valores de R, G e B várias vezes com o código acima e anote as médias como a referência de branco; estes são os menores valores.
  3. Ponha uma folha preta fosca ou um feltro no mesmo lugar e anote as leituras como a referência de preto; estes são os maiores valores.
  4. Ponha esses seis números nas constantes WHITE_ e BLACK_ no topo do código abaixo e carregue.
  5. Confira se os três canais marcam perto de 255 no papel branco e perto de 0 no preto; se não, repita a medição com a mesma luz e a mesma distância.

Cuidado com um erro presente em muitos exemplos: converter a largura do pulso direto para 0–255 com map() não dá o resultado certo, porque a luz é proporcional à frequência, não à largura do pulso. O jeito correto é converter primeiro a largura em frequência (1/t) e depois escalar: nível = (f − f_preto) / (f_branco − f_preto) × 255. Vejamos um exemplo: você mediu 20 µs no branco, 200 µs no preto, e o objeto deu 40 µs. O map() transforma isso em 226, enquanto o método da frequência dá 113. A frequência do objeto é exatamente metade da do branco e, descontado o nível do preto, isso corresponde a cerca de 44% da escala, ou 113; o map(), por outro lado, mostra as cores muito mais claras do que são e dificulta distinguir tons escuros.

Algoritmo de classificação de cores: qual cor é esta leitura?

Os valores calibrados de R, G e B ainda dependem da quantidade de luz: quando o mesmo objeto vermelho chega um pouco mais perto do sensor, os três valores sobem juntos. Para corrigir isso, dividimos cada valor pelo total: r = R / (R + G + B), e o mesmo para g e b. Essas proporções de cor ficam quase constantes mesmo quando a distância e o brilho mudam um pouco. Depois você as compara com proporções de referência medidas e salvas uma vez para cada cor e escolhe o vizinho mais próximo. Para a comparação, basta somar os quadrados das diferenças entre as proporções; quanto menor esse número, mais parecidas são as cores. Se até a referência mais próxima estiver mais longe que um certo limite, o resultado é “desconhecido”. Se a luz total for muito baixa, o objeto é tratado como preto e, se os três canais forem muito altos, como branco; esses dois casos não podem ser distinguidos pelas proporções, então são verificados primeiro.

C++ (Arduino IDE)
1// TCS3200 ile kalibrasyonlu renk tanıma / Calibrated color recognition with the TCS3200
2const int S0 = 4, S1 = 5, S2 = 6, S3 = 7, OUT_PIN = 8;
3
4// Kalibrasyonda ölçtüğün süreler (µs), kendi değerlerinle değiştir / Pulse widths from your calibration (µs), use your own
5const float WHITE_R = 16, WHITE_G = 20, WHITE_B = 18;     // beyaz kağıt / white paper
6const float BLACK_R = 140, BLACK_G = 170, BLACK_B = 150;  // siyah kağıt / black paper
7
8struct ColorRef {
9  const char *name;
10  float r, g, b;                                          // renk oranları, toplamı 1 / color ratios, sum is 1
11};
12
13// Örnek referanslar, kendi ölçümlerinle değiştir / Sample references, replace with your own measurements
14ColorRef refs[] = {
15  {"Kirmizi / Red",    0.58, 0.20, 0.22},
16  {"Turuncu / Orange", 0.55, 0.30, 0.15},
17  {"Sari / Yellow",    0.42, 0.40, 0.18},
18  {"Yesil / Green",    0.24, 0.46, 0.30},
19  {"Mavi / Blue",      0.18, 0.30, 0.52},
20  {"Mor / Purple",     0.40, 0.20, 0.40}
21};
22const int REF_COUNT = sizeof(refs) / sizeof(refs[0]);
23const float MAX_DIST = 0.02;                              // bundan uzaksa bilinmiyor / farther than this is unknown
24
25unsigned long readPulse(bool s2, bool s3) {
26  digitalWrite(S2, s2 ? HIGH : LOW);
27  digitalWrite(S3, s3 ? HIGH : LOW);
28  delay(5);
29  unsigned long total = 0;
30  int count = 0;
31  for (int i = 0; i < 10; i++) {
32    unsigned long t = pulseIn(OUT_PIN, LOW, 50000UL);
33    if (t > 0) {
34      total += t;
35      count++;
36    }
37  }
38  return count > 0 ? total / count : 50000UL;
39}
40
41// Süreyi frekansa çevirip 0-255 arasına ölçekler / Converts the pulse width to frequency and scales it to 0-255
42float toLevel(unsigned long t, float tWhite, float tBlack) {
43  float f = 1.0 / t;                                      // ışık frekansla orantılı / light is proportional to frequency
44  float fWhite = 1.0 / tWhite;
45  float fBlack = 1.0 / tBlack;
46  float level = (f - fBlack) / (fWhite - fBlack) * 255.0;
47  return constrain(level, 0.0, 255.0);
48}
49
50// En yakın referans rengi bulur / Finds the nearest reference color
51const char *classify(float R, float G, float B) {
52  float sum = R + G + B;
53  if (sum < 90) return "Siyah / Black";                  // çok az ışık / very little light
54  if (R > 200 && G > 200 && B > 200) return "Beyaz / White";
55  float r = R / sum, g = G / sum, b = B / sum;           // parlaklıktan bağımsız oranlar / brightness-independent ratios
56  int best = 0;
57  float bestDist = 1e9;
58  for (int i = 0; i < REF_COUNT; i++) {
59    float d = sq(r - refs[i].r) + sq(g - refs[i].g) + sq(b - refs[i].b);  // uzaklığın karesi / squared distance
60    if (d < bestDist) {
61      bestDist = d;
62      best = i;
63    }
64  }
65  if (bestDist > MAX_DIST) return "Bilinmiyor / Unknown";
66  return refs[best].name;
67}
68
69void setup() {
70  pinMode(S0, OUTPUT);
71  pinMode(S1, OUTPUT);
72  pinMode(S2, OUTPUT);
73  pinMode(S3, OUTPUT);
74  pinMode(OUT_PIN, INPUT);
75  digitalWrite(S0, HIGH);                                 // %20 ölçek / 20% scaling
76  digitalWrite(S1, LOW);
77  Serial.begin(9600);
78}
79
80void loop() {
81  float R = toLevel(readPulse(false, false), WHITE_R, BLACK_R);
82  float G = toLevel(readPulse(true, true), WHITE_G, BLACK_G);
83  float B = toLevel(readPulse(false, true), WHITE_B, BLACK_B);
84  float sum = R + G + B;
85  Serial.print("R=");
86  Serial.print(R, 0);
87  Serial.print(" G=");
88  Serial.print(G, 0);
89  Serial.print(" B=");
90  Serial.print(B, 0);
91  if (sum > 0) {                                          // referans için oranlar / ratios for your references
92    Serial.print("  r=");
93    Serial.print(R / sum, 2);
94    Serial.print(" g=");
95    Serial.print(G / sum, 2);
96    Serial.print(" b=");
97    Serial.print(B / sum, 2);
98  }
99  Serial.print("  -> ");
100  Serial.println(classify(R, G, B));
101  delay(500);
102}
Parte do códigoO que faz
WHITE_* e BLACK_*As larguras de pulso de branco e preto que você mediu na calibração (µs)
toLevel() (conversão para nível)Converte a largura do pulso em frequência e a escala para 0–255
refs[] (referências)Proporções r, g, b de referência de cada cor; troque pelas suas próprias medições
classify() (classificação)Separa primeiro o preto e o branco e depois acha a referência mais próxima
MAX_DIST (distância máxima)Leituras mais distantes que isso contam como “Unknown” (desconhecida)
Não chute os valores de referência, meça: ponha um objeto de cada cor na frente do sensor 5–10 vezes, faça a média das proporções r, g, b mostradas no Monitor Serial e escreva-as no array refs[]. Faça as medições com a luz e na distância em que a máquina vai trabalhar. Se as leituras tremerem, fazer a média de várias leituras ou aplicar um filtro de mediana ajuda; para os detalhes, veja o artigo sobre filtragem de dados de sensores.

Montando uma máquina que separa objetos por cor

O projeto mais popular com o TCS3200 é uma máquina que separa por cor balas coloridas, miçangas ou peças de LEGO. O mecanismo tem dois servos. O servo alimentador de cima gira um disco com um furo em que cabe exatamente um objeto: primeiro ele pega um objeto embaixo do tubo do funil, depois o leva para a frente do sensor e, depois da leitura, o solta na calha de baixo. O servo da calha de baixo gira uma calha inclinada para mandar o objeto para o recipiente da cor dele. Se é a primeira vez que você usa servos, leia antes O que é um servo motor?.

  1. Monte o funil e um tubo um pouco mais largo que os objetos, para que eles caiam um de cada vez no disco alimentador.
  2. Monte o sensor de modo que ele veja o objeto no disco a 1–2 cm de distância e feche-o para não entrar luz de fora.
  3. Escolha três ângulos para o servo alimentador: carga, sensor e descarga. Encontre-os por tentativa no seu próprio mecanismo; ligue o servo alimentador ao D9 e o servo da calha ao D10.
  4. Atribua um ângulo a cada recipiente de cor para o servo da calha (por exemplo, vermelho 30°, verde 75°, azul 120°, todo o resto 165°).
  5. Calibre dentro da máquina, com a iluminação da própria máquina, e teste com vários objetos de cada cor.
C++ (Arduino IDE)
1// TCS3200 ve iki servoyla renk ayırıcı / Color sorter with a TCS3200 and two servos
2#include <Servo.h>
3
4const int S0 = 4, S1 = 5, S2 = 6, S3 = 7, OUT_PIN = 8;
5const int FEEDER_PIN = 9;                 // besleme servosu / feeder servo
6const int CHUTE_PIN = 10;                 // yönlendirme servosu / chute servo
7
8const int LOAD_ANGLE = 160;               // huninin altı / under the hopper
9const int SENSOR_ANGLE = 95;              // sensörün önü / in front of the sensor
10const int DROP_ANGLE = 30;                // bırakma deliği / drop hole
11
12// Kalibrasyon süreleri (µs) / Calibration pulse widths (µs)
13const float WHITE_R = 16, WHITE_G = 20, WHITE_B = 18;
14const float BLACK_R = 140, BLACK_G = 170, BLACK_B = 150;
15
16Servo feeder;
17Servo chute;
18
19unsigned long readPulse(bool s2, bool s3) {
20  digitalWrite(S2, s2 ? HIGH : LOW);
21  digitalWrite(S3, s3 ? HIGH : LOW);
22  delay(5);
23  unsigned long total = 0;
24  int count = 0;
25  for (int i = 0; i < 10; i++) {
26    unsigned long t = pulseIn(OUT_PIN, LOW, 50000UL);
27    if (t > 0) {
28      total += t;
29      count++;
30    }
31  }
32  return count > 0 ? total / count : 50000UL;
33}
34
35float toLevel(unsigned long t, float tWhite, float tBlack) {
36  float f = 1.0 / t;
37  float level = (f - 1.0 / tBlack) / (1.0 / tWhite - 1.0 / tBlack) * 255.0;
38  return constrain(level, 0.0, 255.0);
39}
40
41// Baskın kanala göre kutu açısını seçer / Picks the bin angle from the dominant channel
42int binAngle(float R, float G, float B) {
43  if (R + G + B < 90) return 165;                     // çok koyu: diğer kutusu / too dark: "other" bin
44  if (R > G * 1.3 && R > B * 1.3) return 30;          // kırmızı kutusu / red bin
45  if (G > R * 1.3 && G > B * 1.3) return 75;          // yeşil kutusu / green bin
46  if (B > R * 1.3 && B > G * 1.3) return 120;         // mavi kutusu / blue bin
47  return 165;                                         // karar verilemedi / undecided
48}
49
50void setup() {
51  pinMode(S0, OUTPUT);
52  pinMode(S1, OUTPUT);
53  pinMode(S2, OUTPUT);
54  pinMode(S3, OUTPUT);
55  pinMode(OUT_PIN, INPUT);
56  digitalWrite(S0, HIGH);                             // %20 ölçek / 20% scaling
57  digitalWrite(S1, LOW);
58  feeder.attach(FEEDER_PIN);
59  chute.attach(CHUTE_PIN);
60  feeder.write(LOAD_ANGLE);
61  delay(500);
62}
63
64void loop() {
65  feeder.write(LOAD_ANGLE);                           // huniden bir nesne al / take one object from the hopper
66  delay(600);
67  feeder.write(SENSOR_ANGLE);                         // sensörün önüne getir / bring it to the sensor
68  delay(600);                                         // servo dursun, titreşim geçsin / let the servo settle
69  float R = toLevel(readPulse(false, false), WHITE_R, BLACK_R);
70  float G = toLevel(readPulse(true, true), WHITE_G, BLACK_G);
71  float B = toLevel(readPulse(false, true), WHITE_B, BLACK_B);
72  chute.write(binAngle(R, G, B));                     // kanalı doğru kutuya çevir / aim the chute at the right bin
73  delay(400);
74  feeder.write(DROP_ANGLE);                           // nesneyi kanala bırak / drop the object into the chute
75  delay(600);
76}
Alimentar os servos pelo pino 5V do Arduino pode fazer a tensão cair quando eles se movem e reiniciar a placa; essa queda também corrompe as leituras de cor. Alimente os dois servos por uma fonte de 5 V separada e ligue o GND dela ao GND do Arduino. Não faça leituras enquanto um servo está se movendo; como no código, acrescente uma pausa curta depois que o servo chega à posição.

TCS3200 com ESP32: medindo a frequência com interrupção

O mesmo código com pulseIn roda no ESP32, mas há um método mais estável: em vez de cronometrar os pulsos um por um, conte quantos pulsos chegam em um tempo fixo. Uma interrupção incrementa um contador a cada borda de subida; multiplique a contagem depois de 50 ms por 20 e você tem a frequência em hertz. Como esse método faz a média de centenas de pulsos, um único pulso ruim não o afeta. O processador de 240 MHz do ESP32 aguenta com folga dezenas de milhares de interrupções por segundo na escala de 20%; se a contagem ficar grande demais com luz muito forte, você pode baixar a escala para 2%. Você também pode usar o mesmo método no Arduino Uno passando o OUT para o D2, que suporta interrupções. No ESP32, não esqueça de alimentar o módulo pelo pino 3V3.

C++ (Arduino IDE)
1// ESP32: kesmeyle TCS3200 frekans ölçümü / ESP32: TCS3200 frequency measurement with an interrupt
2const int S0 = 32, S1 = 33, S2 = 25, S3 = 26, OUT_PIN = 27;
3
4volatile uint32_t pulseCount = 0;
5
6void IRAM_ATTR onPulse() {
7  pulseCount++;                                       // her yükselen kenarda say / count every rising edge
8}
9
10// Seçilen filtrenin frekansını Hz olarak döndürür / Returns the frequency of the selected filter in Hz
11uint32_t readHz(bool s2, bool s3) {
12  digitalWrite(S2, s2 ? HIGH : LOW);
13  digitalWrite(S3, s3 ? HIGH : LOW);
14  delay(5);                                           // çıkış otursun / let the output settle
15  pulseCount = 0;
16  delay(50);                                          // 50 ms boyunca say / count for 50 ms
17  return pulseCount * 20;                             // 50 ms x 20 = 1 saniye / 1 second
18}
19
20void setup() {
21  Serial.begin(115200);
22  pinMode(S0, OUTPUT);
23  pinMode(S1, OUTPUT);
24  pinMode(S2, OUTPUT);
25  pinMode(S3, OUTPUT);
26  pinMode(OUT_PIN, INPUT);
27  digitalWrite(S0, HIGH);                             // %20 ölçek / 20% scaling
28  digitalWrite(S1, LOW);
29  attachInterrupt(digitalPinToInterrupt(OUT_PIN), onPulse, RISING);
30}
31
32void loop() {
33  uint32_t r = readHz(false, false);                  // kırmızı / red
34  uint32_t g = readHz(true, true);                    // yeşil / green
35  uint32_t b = readHz(false, true);                   // mavi / blue
36  Serial.print("R: ");
37  Serial.print(r);
38  Serial.print(" Hz  G: ");
39  Serial.print(g);
40  Serial.print(" Hz  B: ");
41  Serial.print(b);
42  Serial.println(" Hz");
43  delay(300);
44}

TCS3200 com Raspberry Pi Pico e MicroPython

No Pico, o equivalente ao pulseIn do Arduino é a função machine.time_pulse_us(): ela retorna em microssegundos a duração de um pulso no nível pedido e um número negativo em caso de tempo esgotado. Se for chamada com o pino já nesse nível, ela começa a cronometrar na hora e pode medir meio pulso; por isso o código primeiro espera a parte em HIGH terminar e depois mede o tempo em LOW. Como cada chamada de função no MicroPython leva alguns microssegundos, escolhemos aqui a escala de 2% (S0=0, S1=1): os pulsos ficam 10 vezes mais longos e o efeito desse atraso diminui. Os valores de calibração são escritos em Hz; na escala de 2%, é normal ver alguns kHz no papel branco e algumas centenas de Hz no preto. Alimente o módulo pelo pino 3V3(OUT) do Pico; se você é novo no Pico, O que é o Raspberry Pi Pico? explica como instalar o MicroPython.

MicroPython
1# Pico + TCS3200: süreden frekansa ve 0-255 seviyesine / Pico + TCS3200: from pulse width to frequency and a 0-255 level
2from machine import Pin, time_pulse_us
3import time
4
5s0 = Pin(2, Pin.OUT)
6s1 = Pin(3, Pin.OUT)
7s2 = Pin(4, Pin.OUT)
8s3 = Pin(5, Pin.OUT)
9out = Pin(6, Pin.IN)
10
11s0.value(0)                                    # S0=0, S1=1: %2 ölçek / 2% scaling
12s1.value(1)
13
14FILTERS = {"R": (0, 0), "G": (1, 1), "B": (0, 1)}   # S2, S3 değerleri / S2, S3 values
15WHITE_HZ = {"R": 3125, "G": 2500, "B": 2778}        # beyaz kağıtta Hz / Hz on white paper
16BLACK_HZ = {"R": 357, "G": 294, "B": 333}           # siyah kağıtta Hz / Hz on black paper
17
18def read_hz(channel, samples=10):
19    s2.value(FILTERS[channel][0])
20    s3.value(FILTERS[channel][1])
21    time.sleep_ms(5)                           # çıkış otursun / let the output settle
22    total = 0
23    count = 0
24    for _ in range(samples):
25        time_pulse_us(out, 1, 50000)           # HIGH bitsin, LOW başında ol / wait for HIGH to end
26        t = time_pulse_us(out, 0, 50000)       # LOW süresi, µs / LOW time, µs
27        if t > 0:                              # negatif = zaman aşımı / negative = timeout
28            total += t
29            count += 1
30    if count == 0:
31        return 0
32    return 1000000 / (2 * total / count)       # f = 1 / (2t)
33
34def level(channel, hz):
35    v = (hz - BLACK_HZ[channel]) / (WHITE_HZ[channel] - BLACK_HZ[channel]) * 255
36    return max(0, min(255, int(v)))
37
38while True:
39    r = level("R", read_hz("R"))
40    g = level("G", read_hz("G"))
41    b = level("B", read_hz("B"))
42    print("R:", r, "G:", g, "B:", b)
43    time.sleep(0.5)

TCS3200 ou TCS34725?

O TCS34725 é um sensor de cor mais novo que faz o mesmo trabalho por I2C. Ele faz a medição dentro do chip e devolve os resultados como números de 16 bits por canal, então o Arduino não precisa cronometrar pulsos. A diferença mais importante é o filtro de bloqueio de infravermelho (IR) sobre os fotodiodos. O TCS3200 não tem esse filtro; como os fotodiodos de silício também são sensíveis ao infravermelho, as leituras se desviam com luz rica em IR, como a do sol ou de lâmpadas incandescentes. Como o ganho (1x, 4x, 16x, 60x) e o tempo de integração (2,4–614 ms) do TCS34725 podem ser ajustados por software, ele funciona em uma faixa ampla, de luz muito fraca a forte. Se tem curiosidade sobre como o I2C funciona, veja o artigo O que é I2C?.

Módulo sensor de cor I2C TCS34725
Módulo sensor de cor TCS34725: o chip sensor de cor com filtro IR no meio, um LED branco ao lado e os pinos LED, INT, SDA, SCL, 3V3, GND e VIN.
CaracterísticaTCS3200TCS34725
SaídaFrequência (onda quadrada)I2C, endereço 0x29
Fios de sinalS0–S3, OUT e OESDA e SCL (INT e LED opcionais)
ResoluçãoDepende do método e do tempo de medição16 bits por canal
Filtro IRNãoSim (no chip)
Ajustes3 escalas de frequência e desligamento4 ganhos, tempo de integração de 2,4–614 ms
Alimentação2,7–5,5 VChip de 3,3 V; módulos com regulador de 3–5 V
IluminaçãoGeralmente 4 LEDs brancosGeralmente 1 LED branco
Carga no processadorOcupado enquanto cronometra os pulsosO chip mede, o Arduino só lê
Ideal paraAprender, separadores de cor simplesMedição precisa de cor, temperatura de cor e lux
C++ (Arduino IDE)
1// TCS34725 ile ham RGBC, renk sıcaklığı ve lüks / Raw RGBC, color temperature and lux with the TCS34725
2#include <Wire.h>
3#include <Adafruit_TCS34725.h>
4
5// 50 ms entegrasyon süresi, 4x kazanç / 50 ms integration time, 4x gain
6Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X);
7
8void setup() {
9  Serial.begin(9600);
10  if (!tcs.begin()) {                                 // 0x29 adresinde ara / look for it at 0x29
11    Serial.println("TCS34725 bulunamadi / not found");
12    while (true) {
13      delay(1000);
14    }
15  }
16}
17
18void loop() {
19  uint16_t r, g, b, c;
20  tcs.getRawData(&r, &g, &b, &c);                     // 16 bit ham değerler / 16-bit raw values
21  uint16_t kelvin = tcs.calculateColorTemperature_dn40(r, g, b, c);
22  uint16_t lux = tcs.calculateLux(r, g, b);
23  Serial.print("R: ");
24  Serial.print(r);
25  Serial.print("  G: ");
26  Serial.print(g);
27  Serial.print("  B: ");
28  Serial.print(b);
29  Serial.print("  C: ");
30  Serial.print(c);
31  Serial.print("  K: ");
32  Serial.print(kelvin);
33  Serial.print("  Lux: ");
34  Serial.println(lux);
35  delay(500);
36}

Antes de carregar o código, instale a biblioteca Adafruit TCS34725 pelo Gerenciador de Bibliotecas; quando ela perguntar sobre as dependências, escolha instalar todas. No Arduino Uno, ligue o SDA ao A4 e o SCL ao A5; no ESP32, use os GPIO 21 e 22. getRawData() espera o tempo de integração e retorna os valores brutos de vermelho, verde, azul e sem filtro (C); calculateColorTemperature_dn40() dá a temperatura de cor em kelvin, e calculateLux() um nível de luz aproximado. A calibração e a lógica da cor mais próxima valem do mesmo jeito para este sensor. Se você quer um sensor que detecte gestos e proximidade além da cor, o APDS-9960 é uma boa alternativa.

Ideias de projetos com o TCS3200

  • Máquina separadora de cores: separe balas, miçangas ou peças de LEGO em recipientes diferentes por cor.
  • Luminária que copia cores: reproduza a cor do objeto que você põe na frente do sensor com um LED RGB ou uma fita WS2812B.
  • Robô que lê cores: ensine o seu robô seguidor de linha a parar em uma marca vermelha no chão e seguir em uma verde.
  • Braço robótico que reconhece cores: use um braço robótico para pegar cubos e soltar cada cor no próprio recipiente.
  • Detector de cores falante: anuncie a cor detectada com um som pré-gravado em um DFPlayer Mini; é uma ajuda prática para pessoas com deficiência visual.
  • Monitor da cor da água: acompanhe ao longo do tempo as mudanças de cor da água em um recipiente transparente e dispare um aviso para um aquário ou um reservatório de irrigação.

Erros comuns e como corrigir

SintomaCausa provávelSolução
O pulseIn sempre retorna 0 ou o valor do tempo limiteOE solto ou em HIGH, S0 e S1 ambos em LOW (desligado) ou OUT no pino erradoLigue o OE ao GND, ponha S0 em HIGH e S1 em LOW, compare o pino OUT com o código
Os valores ficam pulandoLuz do ambiente, uma lâmpada que pisca ou uma distância que mudaProteja o sensor com um tubo preto, fixe a distância, faça a média de várias leituras
Todo objeto aparece com a mesma corSem calibração; os canais têm sensibilidades diferentesFaça a calibração de branco e preto, meça as referências de novo
As cores se desviam com a luz do diaO TCS3200 não tem filtro IRMeça dentro de uma caixa fechada ou use um TCS34725
Os níveis ficam presos em 0 ou 255O objeto está em uma distância diferente da usada na calibraçãoMantenha a mesma distância da calibração, recalibre se preciso
O ESP32 ou o Pico se comporta de forma estranhaMódulo alimentado com 5 V, o OUT sai com 5 VAlimente o módulo pelo 3V3 ou use um conversor de nível
As leituras erram quando o servo se moveA corrente do servo derruba a tensão de alimentaçãoUse uma fonte de 5 V separada para os servos e una os GNDs
Superfícies brilhantes e reflexivas (metal, plástico brilhante, papel brilhante) devolvem a luz dos LEDs direto para o sensor e fazem as cores parecerem mais pálidas do que são. Segurar o sensor um pouco inclinado em vez de exatamente perpendicular, ou trabalhar com objetos foscos, reduz muito esse problema. Se você só precisa distinguir preto de branco, o TCRT5000 é uma escolha mais simples e rápida.

O TCS3200 é um ótimo sensor para aprender como a luz vira frequência, por que a calibração importa e como montar um algoritmo de classificação simples. Passar para o TCS34725 quando você precisar de mais precisão também é fácil, porque a calibração e a lógica da cor mais próxima que você aprendeu continuam iguais. No app BlueGrays você encontra projetos de mistura de cores com LED RGB, luminária misturadora de cores com ESP32, medição de luz com LDR e servo motor, com esquemas de circuito e código passo a passo, e pode combiná-los com o sensor de cor para montar a sua própria máquina.

Perguntas frequentes

A que distância o sensor de cor TCS3200 detecta a cor?

Os resultados mais estáveis vêm a cerca de 1–3 cm, porque a luz dos LEDs do módulo enfraquece rápido com a distância. Mais importante que a distância em si é mantê-la igual em todas as leituras, e você também deve calibrar nessa distância. Este sensor não serve para ler a cor de superfícies grandes ao longe.

Qual é a diferença entre o TCS230 e o TCS3200?

O TCS230 é o sensor de cor mais antigo do mesmo fabricante, e o TCS3200 é a versão melhorada. Os dois usam uma matriz de fotodiodos de 8×8, saída em frequência e a mesma lógica de controle S0–S3, então as ligações e o código são os mesmos. A maioria dos módulos vendidos hoje traz o TCS3200.

Por que o pulseIn retorna 0 com o TCS3200?

O pulseIn retorna 0 quando não vê um pulso dentro do tempo limite dado. As causas mais comuns são o pino OE não estar ligado ao GND, S0 e S1 deixados ambos em LOW (o que põe o sensor no modo desligado) e o OUT ligado a um pino diferente do que está no código. Se a frente do sensor estiver totalmente escura, a frequência cai para alguns hertz e nenhum pulso é captado em um tempo limite curto.

O TCS3200 funciona com o ESP32 e o Raspberry Pi Pico?

Sim. Como o TCS3200 funciona com 2,7–5,5 V, você pode alimentar o módulo pelo pino 3V3 da placa, e assim o pino OUT também gera pulsos de 3,3 V. Se alimentar o módulo com 5 V, você precisa de um divisor de tensão ou de um conversor de nível na linha OUT. Ao medir com MicroPython no Pico, a escala de frequência de 2% dá resultados mais precisos.

Qual é melhor, o TCS3200 ou o TCS34725?

Para medir cor com precisão, o TCS34725 é melhor: ele tem filtro de bloqueio de IR, resolução de 16 bits por canal e ganho ajustável, e se liga com só dois fios I2C. O TCS3200 é uma escolha comum e perfeitamente adequada para aprender medição de frequência e calibração e para montar separadores de cor simples.

Quantas cores o TCS3200 consegue distinguir?

Com iluminação controlada e distância fixa, o TCS3200 separa com segurança 6–8 cores claramente diferentes, como vermelho, laranja, amarelo, verde, azul, roxo, preto e branco. Distinguir tons próximos, como verde-claro e verde-escuro, também é possível, mas exige uma calibração mais cuidadosa, muitas leituras de referência para cada tom e um limite apertado.

Ver todos os projetos
Medição da intensidade da luz com LDR
#11Iniciante

Medição da intensidade da luz com LDR

Quer ver a luminosidade do seu quarto em números? O LDR, um pequeno componente sensível à luz, mede a luz ambiente, e o Arduino exibe o valor na tela do computador a cada meio segundo. Cubra o sensor

4.6(81)
139
Leitura da intensidade da luz com módulo LDR e ESP32
#113Iniciante

Leitura da intensidade da luz com módulo LDR e ESP32

Acompanhe em tempo real no Monitor Serial o valor bruto do ADC do módulo LDR e a porcentagem relativa de luz calculada a partir de referências de escuridão e claridade. Essa porcentagem não é uma medida em lux. O projeto ensina a lógica de calibração do módulo. É ideal fazê-lo antes do projeto 10, pois ajuda a determinar o valor do limiar.

4.6(36)
55
Uso de LED RGB
#39Iniciante
Premium

Uso de LED RGB

Três cores se escondem em um único LED: o LED RGB acende primeiro em vermelho, depois em verde e, por fim, em azul. Cada cor fica acesa por meio segundo e apagada por meio segundo antes de o ciclo rec

4.9(73)
98
Transição suave de cores com LED RGB
#40Iniciante
Premium

Transição suave de cores com LED RGB

Um único LED RGB muda lentamente do vermelho para o verde, do verde para o azul e de volta para o vermelho, repetindo esse ciclo de arco-íris sem parar. Cada transição leva cerca de dois segundos e me

4.8(58)
76
Luminária com mistura de cores e ESP32 (3 potenciômetros + LED RGB + OLED)
#138Avançado
Premium

Luminária com mistura de cores e ESP32 (3 potenciômetros + LED RGB + OLED)

Uma luminária inteligente que permite misturar a cor que você quiser girando três potenciômetros com o ESP32! Ao girar os potenciômetros de vermelho, verde e azul, o LED RGB de cátodo comum muda de cor em tempo real. O display OLED SSD1306 mostra os valores de cada cor na faixa de 0–255, barras de preenchimento e o código HEX da cor para a web, como #FF8040.

4.7(6)
10
Controle de LED RGB com ESP32
#111Iniciante

Controle de LED RGB com ESP32

Gere inúmeras cores com um único LED RGB em vez de 3 LEDs separados. Como no Arduino, ajustamos os valores R/G/B com analogWrite() para criar uma transição de arco-íris. Você vê a teoria das cores na prática, como vermelho + verde = amarelo.

4.6(29)
51
Uso de servo motor
#35Iniciante
Premium

Uso de servo motor

O braço de um pequeno servo motor gira para três posições em sequência: o início, o meio e o fim do percurso, esperando dois segundos em cada parada. O Arduino informa ao motor em qual ângulo ficar, e

4.7(48)
67
Detecção de superfícies pretas e brancas com TCRT5000
#78Intermediário
Premium

Detecção de superfícies pretas e brancas com TCRT5000

Com o pequeno sensor que funciona como o olho dos robôs seguidores de linha, você identifica se o piso é preto ou branco. O TCRT5000 emite luz invisível em direção ao piso e verifica quanto dela é ref

4.7(40)
49