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.

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.
| Merkmal | Wert |
|---|---|
| Chip | TCS3200 (ältere Version TCS230, pinkompatibel) |
| Fotodioden-Array | 8×8 = 64: 16 Rot, 16 Grün, 16 Blau, 16 ohne Filter |
| Ausgang | Rechtecksignal mit 50 % Tastgrad, Frequenz proportional zum Licht |
| Vollausschlag-Frequenz (typisch) | 600 kHz (100 %), 120 kHz (20 %), 12 kHz (2 %) |
| Versorgungsspannung | 2,7–5,5 V |
| Stromaufnahme des Chips | Typisch 1,4 mA (der Strom der LEDs auf dem Modul kommt hinzu) |
| Steuerpins | S0, S1 (Frequenzskalierung), S2, S3 (Farbfilter), OE (Ausgang aktiv, active LOW) |
| Empfohlener Abstand | Etwa 1–3 cm, bei jeder Messung gleich |
| Beleuchtung | 4 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.
| S0 | S1 | Skalierung der Ausgangsfrequenz | Wann verwenden? |
|---|---|---|---|
| LOW | LOW | Abgeschaltet (Power-down) | Um den Sensor in Batterieprojekten schlafen zu legen |
| LOW | HIGH | 2 % (typisch bis 12 kHz) | Langsame Prozessoren, MicroPython und Zählen per Interrupt |
| HIGH | LOW | 20 % (typisch bis 120 kHz) | Die beste Einstellung für Arduino und pulseIn |
| HIGH | HIGH | 100 % (typisch bis 600 kHz) | Schnelle Messungen mit Hardware-Zähler |
| S2 | S3 | Gelesene Fotodiodengruppe |
|---|---|---|
| LOW | LOW | Mit Rotfilter |
| LOW | HIGH | Mit Blaufilter |
| HIGH | LOW | Ohne Filter (clear, gesamtes Licht) |
| HIGH | HIGH | Mit Grünfilter |
TCS3200 anschließen: Arduino Uno, ESP32 und Pico
| Modul-Pin | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| VCC | 5V | 3V3 | 3V3(OUT), Pin 36 |
| GND | GND | GND | GND |
| S0 | D4 | GPIO 32 | GP2 |
| S1 | D5 | GPIO 33 | GP3 |
| S2 | D6 | GPIO 25 | GP4 |
| S3 | D7 | GPIO 26 | GP5 |
| OUT | D8 | GPIO 27 | GP6 |
| OE | GND | GND | GND |
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
- Schließe das Modul wie in der Tabelle oben an; OE kommt an GND, VCC an 5V.
- Wähle die 20-%-Skalierung, indem du S0 auf HIGH und S1 auf LOW setzt.
- Setze für Rot S2 und S3 auf LOW und warte ein paar Millisekunden, bis sich der Ausgang eingeschwungen hat.
- Miss mit pulseIn(OUT, LOW) die Dauer des LOW-Teils in Mikrosekunden; bilde den Mittelwert aus 10 Messungen, um das Rauschen zu verringern.
- Wiederhole das für Grün (S2 HIGH, S3 HIGH) und Blau (S2 LOW, S3 HIGH).
- Verfolge die Ergebnisse im Seriellen Monitor bei 9600 Baud und halte verschiedenfarbige Blätter vor den Sensor.
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.
- Befestige den Sensor in einer festen Halterung und stelle den Messabstand (zum Beispiel 1,5 cm) so ein, dass er sich nicht ändert.
- 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.
- Lege an dieselbe Stelle ein mattschwarzes Blatt oder Filz und notiere die Werte als Schwarzreferenz; das sind die größten Werte.
- Trage diese sechs Zahlen in die Konstanten WHITE_ und BLACK_ am Anfang des folgenden Codes ein und lade ihn hoch.
- 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.
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}| Codeteil | Aufgabe |
|---|---|
| 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_DIST | Messungen, die weiter entfernt liegen, gelten als "Unbekannt" |
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?.
- Baue den Trichter und ein Rohr, das etwas breiter als die Objekte ist, so auf, dass die Objekte einzeln auf die Zuführscheibe fallen.
- Befestige den Sensor so, dass er das Objekt auf der Scheibe aus 1–2 cm Abstand sieht, und schirme ihn gegen Fremdlicht ab.
- 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.
- Ordne jedem Farbbehälter einen Winkel des Rutschenservos zu (zum Beispiel Rot 30°, Grün 75°, Blau 120°, alles andere 165°).
- Kalibriere in der Maschine mit ihrer eigenen Beleuchtung und teste mit mehreren Objekten jeder Farbe.
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}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.
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.
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?.

| Merkmal | TCS3200 | TCS34725 |
|---|---|---|
| Ausgang | Frequenz (Rechtecksignal) | I2C, Adresse 0x29 |
| Signalleitungen | S0–S3, OUT und OE | SDA und SCL (optional INT und LED) |
| Auflösung | Abhängig von Messmethode und -dauer | 16 Bit pro Kanal |
| IR-Filter | Nein | Ja (auf dem Chip) |
| Einstellungen | 3 Frequenzstufen und Abschaltung | 4 Verstärkungen, 2,4–614 ms Integrationszeit |
| Versorgung | 2,7–5,5 V | Chip 3,3 V; Module mit Spannungsregler 3–5 V |
| Beleuchtung | Meist 4 weiße LEDs | Meist 1 weiße LED |
| Prozessorlast | Beschäftigt, solange Impulse gemessen werden | Der Chip misst, der Arduino liest nur |
| Geeignet für | Lernen, einfache Farbsortierer | Genaue Farbe, Farbtemperatur und Lux-Messung |
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
| Symptom | Mögliche Ursache | Lösung |
|---|---|---|
| pulseIn liefert immer 0 oder den Timeout-Wert | OE offen oder HIGH, S0 und S1 beide LOW (abgeschaltet) oder OUT am falschen Pin | OE an GND legen, S0 auf HIGH und S1 auf LOW setzen, OUT-Pin mit dem Code vergleichen |
| Werte springen ständig | Raumlicht, flackernde Lampe oder wechselnder Abstand | Sensor mit schwarzem Rohr abschirmen, Abstand fixieren, mehrere Messungen mitteln |
| Jedes Objekt wird als dieselbe Farbe erkannt | Keine Kalibrierung; die Kanäle sind unterschiedlich empfindlich | Weiß-Schwarz-Kalibrierung durchführen, Referenzen neu messen |
| Farben driften bei Tageslicht | Der TCS3200 hat keinen IR-Filter | In einem geschlossenen Kasten messen oder einen TCS34725 verwenden |
| Stufen kleben an 0 oder 255 | Objekt in anderem Abstand als bei der Kalibrierung | Denselben Abstand wie bei der Kalibrierung einhalten, bei Bedarf neu kalibrieren |
| ESP32 oder Pico verhält sich seltsam | Modul mit 5 V versorgt, OUT liefert 5 V | Modul aus 3V3 versorgen oder einen Pegelwandler verwenden |
| Messwerte kippen, wenn der Servo sich dreht | Der Servostrom zieht die Versorgungsspannung herunter | Eigene 5-V-Quelle für die Servos, GNDs verbinden |
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.
Projekte, die du danach bauen kannst
Alle Projekte ansehen
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

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.

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

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

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

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.

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

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
Weitere Artikel
BlueGrays-App
Diese Schaltung Schritt für Schritt auf dem Handy nachbauen
Schaltplan, Code und Teileliste auf einem Bildschirm. Über 200 Arduino-, ESP32- und Pico-Projekte zum Nachbauen.
Mit dem Handy scannen
Richte die Kamera auf den Code, dann öffnet sich der App-Store deines Handys.
oder im Store öffnen

