Bauteile · 12 Min. Lesezeit

TCS3200 Farbsensor mit Arduino: Anschluss, Kalibrierung, Code

So funktioniert der TCS3200 Farbsensor: S0–S3, R, G, B mit pulseIn messen, Weiß-Schwarz-Kalibrierung, Farberkennung und Code für ESP32, Pico, TCS34725.

Der TCS3200 Farbsensor ist das meistgenutzte Modul zur Farberkennung in Arduino-Projekten: Er misst die Farbe der Oberfläche vor ihm, indem er sie in ihre Rot-, Grün- und Blauanteile zerlegt. Das von einem Fotodioden-Array eingefangene Licht wandelt er direkt in eine Frequenz um: Je mehr rotes Licht eine Oberfläche reflektiert, desto höher ist die Frequenz im Rotkanal. Du brauchst weder einen Analogeingang noch ein spezielles Kommunikationsprotokoll; mit dem Arduino-Befehl pulseIn() liest du die R-, G- und B-Werte in wenigen Zeilen aus. Sortiermaschinen für bunte Süßigkeiten, Roboter mit Farberkennung, Fahrzeuge, die an farbigen Markierungen auf dem Boden abbiegen, und Lampen, die die Farbe eines vorgehaltenen Gegenstands nachahmen, entstehen alle mit diesem Sensor.

In dieser Anleitung siehst du Schritt für Schritt, was im Sensor steckt, wofür die Pins S0–S3 da sind, wie du ihn an Arduino Uno, ESP32 und Raspberry Pi Pico anschließt, wie du Rohwerte liest, wie die Weiß-Schwarz-Kalibrierung funktioniert und mit welchem Algorithmus du aus einem Messwert die Farbe bestimmst. Danach bauen wir die Logik einer Maschine auf, die Objekte nach Farbe sortiert, und vergleichen den Sensor zum Schluss mit dem TCS34725, der über I2C arbeitet.

TCS3200 Farbsensor-Modul
TCS3200 Farbsensor-Modul: in der Mitte der TCS3200-Chip mit 8 Pins, an den vier Ecken weiße LEDs zur Beleuchtung; auf einer Seite die Pins S0, S1, OE, GND, auf der anderen VCC, OUT, S2, S3.

Was ist der TCS3200 Farbsensor und wie funktioniert er?

Auf dem TCS3200-Chip, dem Herzstück des Moduls (die ältere Version heißt TCS230), sitzen 64 Fotodioden in einem 8×8-Raster. Sechzehn davon liegen unter einem Rotfilter, 16 unter einem Grünfilter, 16 unter einem Blaufilter, die übrigen 16 haben keinen Filter (clear) und erfassen die gesamte Lichtmenge. Die 16 Fotodioden einer Farbe sind parallel geschaltet, und die Pins S2 und S3 wählen aus, welche Gruppe gemessen wird. Weil die Fotodioden der verschiedenen Farben im Array ineinander verschachtelt sind, wirkt sich Licht, das auf eine Ecke fällt, gleichmäßig auf alle Farbgruppen aus.

Der kleine Strom der gewählten Fotodiodengruppe gelangt zu einem Strom-Frequenz-Wandler im Chip. Diese Schaltung erzeugt ein Rechtecksignal mit 50 % Tastgrad, dessen Frequenz direkt proportional zur Lichtstärke ist. Du misst also keine Spannung, sondern die Zahl der Impulse pro Sekunde: in völliger Dunkelheit wenige Hertz, dicht vor einer weißen Fläche einige zehn Kilohertz. Das Schöne am Frequenzausgang ist, dass er unempfindlich gegen Störungen ist und an jeden digitalen Pin des Arduino passt. Willst du nur messen, wie hell es ist, ist ein LDR-Lichtsensor die einfachere Wahl; der TCS3200 macht dasselbe getrennt für drei Farben.

MerkmalWert
ChipTCS3200 (ältere Version TCS230, pinkompatibel)
Fotodioden-Array8×8 = 64: 16 Rot, 16 Grün, 16 Blau, 16 ohne Filter
AusgangRechtecksignal mit 50 % Tastgrad, Frequenz proportional zum Licht
Vollausschlag-Frequenz (typisch)600 kHz (100 %), 120 kHz (20 %), 12 kHz (2 %)
Versorgungsspannung2,7–5,5 V
Stromaufnahme des ChipsTypisch 1,4 mA (der Strom der LEDs auf dem Modul kommt hinzu)
SteuerpinsS0, S1 (Frequenzskalierung), S2, S3 (Farbfilter), OE (Ausgang aktiv, active LOW)
Empfohlener AbstandEtwa 1–3 cm, bei jeder Messung gleich
Beleuchtung4 weiße LEDs auf dem Modul

Pins S0–S3: Frequenzskalierung und Farbfilter wählen

Vier Steuerpins legen fest, wie sich der TCS3200 verhält. S0 und S1 teilen die Ausgangsfrequenz herunter: Bei 100 % schrumpfen die Impulse auf wenige Mikrosekunden und bringen die Messgenauigkeit des Arduino Uno an ihre Grenzen, bei 2 % werden die Impulse länger, dafür kommen in derselben Zeit weniger an. S2 und S3 wählen, welcher Farbfilter gelesen wird. Du verbindest diese Pins mit digitalen Ausgängen des Arduino und setzt sie vor jeder Messung im Code.

S0S1Skalierung der AusgangsfrequenzWann verwenden?
LOWLOWAbgeschaltet (Power-down)Um den Sensor in Batterieprojekten schlafen zu legen
LOWHIGH2 % (typisch bis 12 kHz)Langsame Prozessoren, MicroPython und Zählen per Interrupt
HIGHLOW20 % (typisch bis 120 kHz)Die beste Einstellung für Arduino und pulseIn
HIGHHIGH100 % (typisch bis 600 kHz)Schnelle Messungen mit Hardware-Zähler
S2S3Gelesene Fotodiodengruppe
LOWLOWMit Rotfilter
LOWHIGHMit Blaufilter
HIGHLOWOhne Filter (clear, gesamtes Licht)
HIGHHIGHMit Grünfilter
Der Zusammenhang zwischen Impulsdauer und Frequenz ist einfach: Da der Tastgrad 50 % beträgt, dauert der LOW-Teil eine halbe Periode. Mit der Formel f = 1 / (2 × t) rechnest du die Mikrosekunden aus pulseIn in eine Frequenz um. Bei t = 25 µs ergibt sich zum Beispiel f = 1 / (2 × 0,000025 s) = 20 kHz. Bei 20 % Skalierung und eingeschalteten LEDs sind Werte zwischen 10 und 200 µs normal.

TCS3200 anschließen: Arduino Uno, ESP32 und Pico

Modul-PinArduino UnoESP32Raspberry Pi Pico
VCC5V3V33V3(OUT), Pin 36
GNDGNDGNDGND
S0D4GPIO 32GP2
S1D5GPIO 33GP3
S2D6GPIO 25GP4
S3D7GPIO 26GP5
OUTD8GPIO 27GP6
OEGNDGNDGND
Lass den OE-Pin nicht offen. Er ist active LOW; liegt er auf HIGH oder hängt er in der Luft, wird der Ausgang hochohmig und pulseIn liefert ständig 0. Außerdem arbeiten die Pins von ESP32 und Pico mit 3,3 V: Versorgst du das Modul mit 5 V, gibt der OUT-Pin 5-V-Impulse aus und belastet den Eingang des Boards. Versorge das Modul an diesen Boards aus 3V3 oder setze einen Spannungsteiler oder Pegelwandler in die OUT-Leitung.

Die vier weißen LEDs auf dem Modul beleuchten die Oberfläche bei jeder Messung gleich, sodass die Ergebnisse auch bei wechselndem Raumlicht stabiler bleiben. Manche Boards haben einen eigenen LED-Pin zum Ein- und Ausschalten der LEDs, bei anderen leuchten sie, sobald Spannung anliegt; schau auf die Beschriftung deines Boards. Für die besten Ergebnisse umgibst du den Sensor mit einem schwarzen Rohr oder Karton, bringst das Objekt jedes Mal in denselben Abstand und meidest glänzende, spiegelnde Oberflächen. Die Pinbelegung deines Boards kannst du im Tool Pinout prüfen.

Schritt für Schritt: R, G und B mit pulseIn messen

  1. Schließe das Modul wie in der Tabelle oben an; OE kommt an GND, VCC an 5V.
  2. Wähle die 20-%-Skalierung, indem du S0 auf HIGH und S1 auf LOW setzt.
  3. Setze für Rot S2 und S3 auf LOW und warte ein paar Millisekunden, bis sich der Ausgang eingeschwungen hat.
  4. Miss mit pulseIn(OUT, LOW) die Dauer des LOW-Teils in Mikrosekunden; bilde den Mittelwert aus 10 Messungen, um das Rauschen zu verringern.
  5. Wiederhole das für Grün (S2 HIGH, S3 HIGH) und Blau (S2 LOW, S3 HIGH).
  6. Verfolge die Ergebnisse im Seriellen Monitor bei 9600 Baud und halte verschiedenfarbige Blätter vor den 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}

Der wichtigste Punkt in diesem Code: Die gelesene Zahl ist umgekehrt proportional zum Licht. Je kleiner der Wert, desto mehr Licht kommt von dieser Farbe. Hältst du ein rotes Blatt davor, siehst du etwa "R: 24 G: 68 B: 52"; die kleinste Zahl steht im Rotkanal. Der Timeout von 50.000 µs für pulseIn verhindert, dass der Code ohne Impuls die standardmäßige 1 Sekunde wartet; wird gar kein Impuls erkannt, gibt die Funktion 50.000 zurück, was "sehr dunkel" bedeutet. Wenn du mit der seriellen Ausgabe Probleme hast, wirf einen Blick in die Anleitung zum Arduino Serial Monitor.

Weiß-Schwarz-Kalibrierung: Rohwerte in 0–255 umrechnen

Rohwerte allein führen in die Irre, weil die drei Fotodiodengruppen unterschiedlich empfindlich sind und die weißen LEDs des Moduls nicht alle Farben gleich stark abstrahlen. Deshalb liefert selbst ein strahlend weißes Blatt auf den drei Kanälen unterschiedliche Zahlen. Die Kalibrierung gleicht diese Unterschiede aus und legt jeden Kanal zwischen 0 (Schwarz) und 255 (Weiß); das ist dieselbe Idee wie der Weißabgleich einer Kamera. Kalibriere dort, wo der Sensor später wirklich arbeitet, bei gleichem Licht und gleichem Abstand; eine Kalibrierung auf dem Schreibtisch gilt nicht im Gehäuse der Maschine.

  1. Befestige den Sensor in einer festen Halterung und stelle den Messabstand (zum Beispiel 1,5 cm) so ein, dass er sich nicht ändert.
  2. Lege ein glattes weißes Blatt davor, lies die R-, G- und B-Werte mit dem Code oben mehrmals aus und notiere die Mittelwerte als Weißreferenz; das sind die kleinsten Werte.
  3. Lege an dieselbe Stelle ein mattschwarzes Blatt oder Filz und notiere die Werte als Schwarzreferenz; das sind die größten Werte.
  4. Trage diese sechs Zahlen in die Konstanten WHITE_ und BLACK_ am Anfang des folgenden Codes ein und lade ihn hoch.
  5. Prüfe, ob alle drei Kanäle auf Weiß nahe 255 und auf Schwarz nahe 0 liegen; falls nicht, wiederhole die Messung bei gleichem Licht und Abstand.

Achte auf einen Fehler, der in vielen Beispielen steckt: Die Impulsdauer mit map() direkt auf 0–255 umzurechnen, liefert kein korrektes Ergebnis, denn das Licht ist proportional zur Frequenz, nicht zur Impulsdauer. Richtig ist es, die Dauer zuerst in eine Frequenz (1/t) umzurechnen und dann zu skalieren: Stufe = (f − f_schwarz) / (f_weiß − f_schwarz) × 255. Ein Beispiel: Du hast auf Weiß 20 µs, auf Schwarz 200 µs gemessen, und das Objekt ergibt 40 µs. map() macht daraus 226, die Frequenzmethode dagegen 113. Die Frequenz des Objekts ist genau halb so hoch wie bei Weiß; zieht man den Schwarzwert ab, entspricht das etwa 44 % der Skala, also 113. map() lässt Farben dagegen deutlich heller erscheinen, als sie sind, und erschwert die Unterscheidung dunkler Töne.

Algorithmus zur Farberkennung: Welche Farbe ist das?

Die kalibrierten R-, G-, B-Werte hängen noch von der Lichtmenge ab: Kommt dasselbe rote Objekt etwas näher an den Sensor, steigen alle drei Werte gemeinsam. Um das zu lösen, teilen wir jeden Wert durch die Summe: r = R / (R + G + B), ebenso g und b. Diese Farbanteile bleiben fast konstant, auch wenn sich Abstand und Helligkeit etwas ändern. Dann vergleichst du sie mit Referenzanteilen, die du für jede Farbe einmal gemessen und gespeichert hast, und wählst den nächsten Nachbarn. Für den Vergleich genügt es, die Quadrate der Differenzen zu addieren; je kleiner diese Zahl, desto ähnlicher die Farben. Liegt selbst die nächste Referenz weiter als eine bestimmte Schwelle entfernt, lautet das Ergebnis "unbekannt". Ist das Gesamtlicht sehr gering, gilt das Objekt als schwarz, sind alle drei Kanäle sehr hoch, als weiß; diese beiden Fälle lassen sich über die Anteile nicht unterscheiden und werden deshalb zuerst geprüft.

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}
CodeteilAufgabe
WHITE_* und BLACK_*Die bei der Kalibrierung gemessenen Impulsdauern für Weiß und Schwarz (µs)
toLevel()Rechnet die Impulsdauer in eine Frequenz um und skaliert sie auf 0–255
refs[]Referenzanteile r, g, b jeder Farbe; ersetze sie durch deine eigenen Messungen
classify()Trennt zuerst Schwarz und Weiß ab und sucht dann die nächste Referenz
MAX_DISTMessungen, die weiter entfernt liegen, gelten als "Unbekannt"
Rate die Referenzwerte nicht, sondern miss sie: Halte ein Objekt jeder Farbe 5- bis 10-mal vor den Sensor, mittele die im Seriellen Monitor angezeigten Anteile r, g, b und trage sie in das Array refs[] ein. Miss bei dem Licht und in dem Abstand, in dem die Maschine arbeiten wird. Wenn die Werte zittern, hilft es, mehrere Messungen zu mitteln oder einen Medianfilter anzuwenden; mehr dazu im Artikel Sensordaten filtern.

Eine Maschine bauen, die Objekte nach Farbe sortiert

Das beliebteste TCS3200-Projekt ist eine Maschine, die bunte Süßigkeiten, Perlen oder LEGO-Teile nach Farbe sortiert. Die Mechanik besteht aus zwei Servos. Der obere Zuführservo dreht eine Scheibe mit einem Loch, in das genau ein Objekt passt: Zuerst nimmt er unter dem Trichterrohr ein Objekt auf, bringt es dann vor den Sensor und lässt es nach der Messung in die Rutsche darunter fallen. Der untere Rutschenservo dreht eine schräge Rutsche und schickt das Objekt in den Behälter seiner Farbe. Wenn du zum ersten Mal mit Servos arbeitest, lies vorher Was ist ein Servomotor?.

  1. Baue den Trichter und ein Rohr, das etwas breiter als die Objekte ist, so auf, dass die Objekte einzeln auf die Zuführscheibe fallen.
  2. Befestige den Sensor so, dass er das Objekt auf der Scheibe aus 1–2 cm Abstand sieht, und schirme ihn gegen Fremdlicht ab.
  3. Lege drei Winkel für den Zuführservo fest: Aufnahme, Sensor und Abwurf. Ermittle sie durch Ausprobieren an deiner Mechanik; schließe den Zuführservo an D9 und den Rutschenservo an D10 an.
  4. Ordne jedem Farbbehälter einen Winkel des Rutschenservos zu (zum Beispiel Rot 30°, Grün 75°, Blau 120°, alles andere 165°).
  5. Kalibriere in der Maschine mit ihrer eigenen Beleuchtung und teste mit mehreren Objekten jeder Farbe.
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}
Versorgst du die Servos über den 5V-Pin des Arduino, kann die Spannung bei jeder Bewegung einbrechen und das Board neu starten; dieser Einbruch verfälscht auch die Farbmessungen. Versorge beide Servos aus einer separaten 5-V-Quelle und verbinde deren GND mit dem GND des Arduino. Miss nicht, während sich ein Servo bewegt; füge wie im Code eine kurze Pause ein, nachdem der Servo seine Position erreicht hat.

TCS3200 am ESP32: Frequenz per Interrupt messen

Derselbe pulseIn-Code läuft auch auf dem ESP32, es gibt aber eine stabilere Methode: Statt Impulse einzeln zu messen, zählst du, wie viele Impulse in einer festen Zeit ankommen. Ein Interrupt erhöht bei jeder steigenden Flanke einen Zähler; multiplizierst du den Zählerstand nach 50 ms mit 20, hast du die Frequenz in Hertz. Da diese Methode über Hunderte Impulse mittelt, bringt sie ein einzelner gestörter Impuls nicht aus dem Tritt. Der 240-MHz-Prozessor des ESP32 verkraftet bei 20 % Skalierung problemlos Zehntausende Interrupts pro Sekunde; wird die Zahl bei sehr hellem Licht zu groß, kannst du auf 2 % heruntergehen. Dieselbe Methode funktioniert auch am Arduino Uno, wenn du OUT auf den interruptfähigen Pin D2 legst. Vergiss nicht, das Modul am ESP32 aus dem 3V3-Pin zu versorgen.

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 mit Raspberry Pi Pico und MicroPython

Auf dem Pico entspricht machine.time_pulse_us() dem pulseIn des Arduino: Die Funktion liefert die Dauer eines Impulses mit dem gewünschten Pegel in Mikrosekunden und bei Zeitüberschreitung eine negative Zahl. Wird sie aufgerufen, während der Pin schon diesen Pegel hat, startet die Messung sofort und erfasst womöglich nur einen halben Impuls; deshalb wartet der Code zuerst, bis der HIGH-Teil endet, und misst dann die LOW-Dauer. Da jeder Funktionsaufruf in MicroPython einige Mikrosekunden dauert, haben wir hier die 2-%-Skalierung gewählt (S0=0, S1=1): Die Impulse werden zehnmal länger, und der Einfluss dieser Verzögerung schrumpft. Die Kalibrierwerte werden in Hz eingetragen; bei 2 % sind auf weißem Papier einige kHz und auf schwarzem einige hundert Hz normal. Versorge das Modul aus dem Pin 3V3(OUT) des Pico; wenn der Pico neu für dich ist, erklärt Was ist der Raspberry Pi Pico? die Installation von 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 oder TCS34725?

Der TCS34725 ist ein neuerer Farbsensor, der dieselbe Aufgabe über I2C erledigt. Er misst im Chip selbst und liefert die Ergebnisse als 16-Bit-Werte pro Kanal; der Arduino muss keine Impulse messen. Der wichtigste Unterschied ist der Infrarot-Sperrfilter (IR-Filter) über seinen Fotodioden. Der TCS3200 hat keinen solchen Filter; da Silizium-Fotodioden auch auf Infrarot reagieren, driften die Messwerte bei IR-reichem Licht wie Sonnenlicht oder Glühlampen. Weil sich Verstärkung (1x, 4x, 16x, 60x) und Integrationszeit (2,4–614 ms) des TCS34725 per Software einstellen lassen, arbeitet er in einem weiten Bereich von sehr schwachem bis hellem Licht. Wenn du wissen willst, wie I2C funktioniert, lies den Artikel Was ist I2C?.

TCS34725 I2C-Farbsensor-Modul
TCS34725 Farbsensor-Modul: in der Mitte der Farbsensor-Chip mit IR-Filter, daneben eine weiße LED; Pins LED, INT, SDA, SCL, 3V3, GND und VIN.
MerkmalTCS3200TCS34725
AusgangFrequenz (Rechtecksignal)I2C, Adresse 0x29
SignalleitungenS0–S3, OUT und OESDA und SCL (optional INT und LED)
AuflösungAbhängig von Messmethode und -dauer16 Bit pro Kanal
IR-FilterNeinJa (auf dem Chip)
Einstellungen3 Frequenzstufen und Abschaltung4 Verstärkungen, 2,4–614 ms Integrationszeit
Versorgung2,7–5,5 VChip 3,3 V; Module mit Spannungsregler 3–5 V
BeleuchtungMeist 4 weiße LEDsMeist 1 weiße LED
ProzessorlastBeschäftigt, solange Impulse gemessen werdenDer Chip misst, der Arduino liest nur
Geeignet fürLernen, einfache FarbsortiererGenaue Farbe, Farbtemperatur und Lux-Messung
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}

Installiere vor dem Hochladen die Bibliothek Adafruit TCS34725 über den Bibliotheksverwalter; wenn nach Abhängigkeiten gefragt wird, installiere alle. Am Arduino Uno kommt SDA an A4 und SCL an A5, am ESP32 an GPIO 21 und 22. getRawData() wartet die Integrationszeit ab und liefert die rohen Werte für Rot, Grün, Blau und ungefiltert (C); calculateColorTemperature_dn40() gibt die Farbtemperatur in Kelvin aus, calculateLux() die ungefähre Beleuchtungsstärke. Kalibrierung und Nächste-Farbe-Logik gelten bei diesem Sensor genauso. Suchst du einen Sensor, der neben der Farbe auch Handgesten und Annäherung erkennt, ist der APDS-9960 eine gute Alternative.

Projektideen mit dem TCS3200

  • Farbsortiermaschine: Sortiere Süßigkeiten, Perlen oder LEGO-Teile nach Farbe in getrennte Behälter.
  • Farbkopierende Lampe: Gib die Farbe des Gegenstands, den du vor den Sensor hältst, mit einer RGB-LED oder einem WS2812B-Streifen wieder.
  • Farbe lesender Roboter: Bring deinem Linienfolger-Roboter bei, an einer roten Markierung anzuhalten und bei Grün weiterzufahren.
  • Roboterarm mit Farberkennung: Lass einen Roboterarm Würfel aufnehmen und jede Farbe in ihrem eigenen Behälter ablegen.
  • Sprechender Farbdetektor: Sag die erkannte Farbe mit einer vorab aufgenommenen Ansage über einen DFPlayer Mini an; ein praktisches Hilfsmittel für sehbehinderte Menschen.
  • Wasserfarbe überwachen: Verfolge, wie sich die Farbe des Wassers in einem durchsichtigen Behälter mit der Zeit ändert, und gib eine Warnung für ein Aquarium oder einen Bewässerungstank aus.

Häufige Fehler und ihre Lösungen

SymptomMögliche UrsacheLösung
pulseIn liefert immer 0 oder den Timeout-WertOE offen oder HIGH, S0 und S1 beide LOW (abgeschaltet) oder OUT am falschen PinOE an GND legen, S0 auf HIGH und S1 auf LOW setzen, OUT-Pin mit dem Code vergleichen
Werte springen ständigRaumlicht, flackernde Lampe oder wechselnder AbstandSensor mit schwarzem Rohr abschirmen, Abstand fixieren, mehrere Messungen mitteln
Jedes Objekt wird als dieselbe Farbe erkanntKeine Kalibrierung; die Kanäle sind unterschiedlich empfindlichWeiß-Schwarz-Kalibrierung durchführen, Referenzen neu messen
Farben driften bei TageslichtDer TCS3200 hat keinen IR-FilterIn einem geschlossenen Kasten messen oder einen TCS34725 verwenden
Stufen kleben an 0 oder 255Objekt in anderem Abstand als bei der KalibrierungDenselben Abstand wie bei der Kalibrierung einhalten, bei Bedarf neu kalibrieren
ESP32 oder Pico verhält sich seltsamModul mit 5 V versorgt, OUT liefert 5 VModul aus 3V3 versorgen oder einen Pegelwandler verwenden
Messwerte kippen, wenn der Servo sich drehtDer Servostrom zieht die Versorgungsspannung herunterEigene 5-V-Quelle für die Servos, GNDs verbinden
Glänzende, spiegelnde Oberflächen (Metall, Hochglanzkunststoff, Glanzpapier) werfen das LED-Licht direkt in den Sensor zurück und lassen Farben blasser wirken, als sie sind. Hältst du den Sensor leicht schräg statt genau senkrecht oder arbeitest du mit matten Objekten, verringert sich das Problem deutlich. Musst du nur Schwarz und Weiß unterscheiden, ist der TCRT5000 die einfachere und schnellere Wahl.

Der TCS3200 ist ein großartiger Sensor, um zu lernen, wie Licht zu einer Frequenz wird, warum Kalibrierung wichtig ist und wie ein einfacher Klassifizierungsalgorithmus entsteht. Brauchst du mehr Genauigkeit, ist der Umstieg auf den TCS34725 leicht, denn die gelernte Kalibrierung und die Nächste-Farbe-Logik gelten unverändert weiter. In der BlueGrays-App findest du Projekte zum Farbmischen mit RGB-LEDs, eine ESP32-Farbmischerlampe, Lichtmessung mit dem LDR und Servomotor-Projekte mit Schaltplänen und Schritt-für-Schritt-Code; kombiniere sie mit dem Farbsensor und bau deine eigene Maschine.

Häufige Fragen

Aus welcher Entfernung erkennt der TCS3200 Farben?

Die stabilsten Ergebnisse erhältst du bei etwa 1–3 cm, weil das Licht der LEDs auf dem Modul mit zunehmendem Abstand schnell schwächer wird. Wichtiger als der Abstand selbst ist, dass er bei jeder Messung gleich bleibt; auch die Kalibrierung solltest du in diesem Abstand machen. Für die Farbe großer, weit entfernter Flächen ist dieser Sensor nicht geeignet.

Was ist der Unterschied zwischen TCS230 und TCS3200?

Der TCS230 ist der ältere Farbsensor desselben Herstellers, der TCS3200 seine verbesserte Version. Beide nutzen ein 8×8-Fotodioden-Array, einen Frequenzausgang und dieselbe S0–S3-Steuerlogik, daher sind Anschluss und Code identisch. Die meisten heute verkauften Module tragen einen TCS3200.

Warum liefert pulseIn beim TCS3200 nur 0?

pulseIn gibt 0 zurück, wenn innerhalb des Timeouts kein Impuls erkannt wird. Die häufigsten Ursachen sind ein nicht mit GND verbundener OE-Pin, S0 und S1 beide auf LOW (der Sensor schaltet sich dann ab) und ein OUT-Anschluss an einem anderen Pin als im Code. Ist es vor dem Sensor völlig dunkel, sinkt die Frequenz auf wenige Hertz, und bei kurzem Timeout wird kein Impuls erfasst.

Funktioniert der TCS3200 mit ESP32 und Raspberry Pi Pico?

Ja. Da der TCS3200 mit 2,7–5,5 V läuft, kannst du das Modul aus dem 3V3-Pin des Boards versorgen; dann gibt auch der OUT-Pin 3,3-V-Impulse aus. Versorgst du das Modul mit 5 V, brauchst du in der OUT-Leitung einen Spannungsteiler oder Pegelwandler. Bei Messungen mit MicroPython auf dem Pico liefert die 2-%-Skalierung genauere Ergebnisse.

Was ist besser: TCS3200 oder TCS34725?

Für genaue Farbmessung ist der TCS34725 besser: Er hat einen IR-Sperrfilter, 16 Bit Auflösung pro Kanal und eine einstellbare Verstärkung und braucht nur zwei I2C-Leitungen. Der TCS3200 ist eine verbreitete und völlig ausreichende Wahl, um Frequenzmessung und Kalibrierung zu lernen und einfache Farbsortierer zu bauen.

Wie viele Farben kann der TCS3200 unterscheiden?

Bei kontrolliertem Licht und festem Abstand trennt der TCS3200 zuverlässig 6–8 deutlich verschiedene Farben wie Rot, Orange, Gelb, Grün, Blau, Violett, Schwarz und Weiß. Auch ähnliche Töne wie Hell- und Dunkelgrün lassen sich unterscheiden, das erfordert aber eine sorgfältigere Kalibrierung, viele Referenzmessungen pro Farbton und eine enge Schwelle.

Alle Projekte ansehen
Lichtstärke messen mit LDR
#11Anfänger

Lichtstärke messen mit LDR

Willst du sehen, wie hell dein Zimmer ist, und zwar als Zahl? Der LDR, ein kleines lichtempfindliches Bauteil, misst das Umgebungslicht, und der Arduino schreibt den Wert jede halbe Sekunde auf deinen

4.6(81)
139
Messung der Lichtintensität mit dem ESP32-LDR-Modul
#113Anfänger

Messung der Lichtintensität mit dem ESP32-LDR-Modul

Echtzeit-Anzeige des ADC-Rohwerts des LDR-Moduls und des anhand von Dunkel- und Hellreferenzwerten berechneten relativen Lichtwerts in Prozent im Serial Monitor. Dieser Prozentwert ist keine Lux-Messung. Vermittelt die Kalibrierungslogik des Moduls. Sollte idealerweise vor Projekt 10 durchgeführt werden, da es zur Festlegung des Schwellenwerts dient.

4.6(36)
55
RGB-LED verwenden
#39Anfänger
Premium

RGB-LED verwenden

In einer einzigen LED stecken drei Farben: Die RGB-LED leuchtet erst rot, dann grün, dann blau, jede Farbe eine halbe Sekunde an und eine halbe Sekunde aus, und dann geht es von vorne los. Du brauchst

4.9(73)
98
Sanfte Farbübergänge mit RGB-Led
#40Anfänger
Premium

Sanfte Farbübergänge mit RGB-Led

Eine einzige RGB-LED wandert langsam von Rot zu Grün, von Grün zu Blau und wieder zurück zu Rot, eine Regenbogenschleife ohne Ende. Jeder Übergang dauert etwa zweieinhalb Sekunden, und Gelb, Türkis un

4.8(58)
76
ESP32-Farbmischerlampe (3 Potentiometer + RGB-LED + OLED)
#138Fortgeschritten
Premium

ESP32-Farbmischerlampe (3 Potentiometer + RGB-LED + OLED)

Eine intelligente Farbmischer-Lampe mit ESP32, die durch Drehen von drei Potentiometern die gewünschte Farbe mischt! Wenn Sie die Potentiometer für Rot, Grün und Blau drehen, ändert die RGB-LED mit gemeinsamer Kathode sofort ihre Farbe. Auf dem SSD1306-OLED-Display werden für jede Farbe Werte im Bereich von 0 bis 255, visuelle Füllbalken und der HEX-Webfarbcode (z. B. #FF8040) in Echtzeit angezeigt. Ein professionelles Mini-Farbstudio – vermittelt in einem einzigen Projekt Kenntnisse über die RG

4.7(6)
10
ESP32 RGB-LED-Steuerung
#111Anfänger

ESP32 RGB-LED-Steuerung

Unbegrenzte Farberzeugung mit einer einzigen RGB-LED anstelle von 3 separaten LEDs. Wie in Arduino machen wir Regenbogenübergänge, indem wir R/G/B-Werte mit analogWrite() setzen. Die Farbtheorie (z. B. Rot+Grün=Gelb) wird praktisch umgesetzt.

4.6(29)
51
Verwendung von Servomotoren
#35Anfänger
Premium

Verwendung von Servomotoren

Der Arm eines kleinen Servomotors dreht sich nacheinander in drei Stellungen: ganz an den Anfang, genau in die Mitte und ganz ans Ende, mit jeweils zwei Sekunden Pause. Der Arduino sagt dem Motor, bei

4.7(48)
67
TCRT5000 Schwarz-Weiß-Untergrunderkennung
#78Mittel
Premium

TCRT5000 Schwarz-Weiß-Untergrunderkennung

Mit dem kleinen Sensor, der bei Linienfolger-Robotern als Auge dient, erkennst du, ob der Boden schwarz oder weiß ist. Der TCRT5000 schickt unsichtbares Licht auf den Boden und prüft, wie viel davon z

4.7(40)
49