Kabellose Datenübertragung muss nicht immer über Funk wie WLAN (Wireless Local Area Network, drahtloses lokales Netzwerk) oder Bluetooth laufen. Mit einem einfachen Laser-Modul und einem Lichtsensor lassen sich Daten direkt über sichtbares Licht durch den Raum schicken.
In Teil 1 habe ich gezeigt, das es möglich ist, mit einem einfachen Laserpointer und einem einfachen Fotowiderstand (LDR, Light Dependent Resistor), Daten direkt über sichtbares Licht durch den Raum zu übertragen. Das funktioniert gut (zwei Buchstaben pro Sekunde) – aber der LDR ist sehr träge und somit langsam.
In diesem zweiten Teil zeige ich, wie du die Übertragung deutlich beschleunigen kannst, in dem du den LDR durch den, in diesem Betrag vorgestellten, TEMT6000‑Fototransistor ersetzt.
Zusätzlich erweitern wir diesmal den Empfänger direkt mit einem OLED‑Display, das dann zeigt, das die empfangenen Zeichen deutlich schneller übertragen werden.
Es soll fortlaufend das vollständige Alphabet gesendet werden und oben rechts wird angezeigt wie viele Seiten schon vollständig gefüllt wurden, um zu testen wie schnell diese Werte ankommen.

Die wichtigsten Fakten im Vergleich
| Eigenschaft | LDR (Fotowiderstand) | TEMT6000 (Fototransistor-Modul) |
| Reaktionszeit | Träge (oft 10 bis 30 Millisekunden) | Sehr schnell (Reaktionszeiten im Mikrosekundenbereich) |
| Übertragungsrate | Meist unter 100 Baud (Zeichen pro Sekunde) | Problemlos 9600 Baud und mehr |
| Lichtspektrum | Reagiert breit, oft träge im Infrarotbereich | Angepasst an das menschliche Auge (Spitze bei etwa 570 Nanometern) |
| Signalform | Unscharfe, „verschmierte“ Signalflanken | Saubere Flanken für die digitale Auswertung |
Verwendete Hardware:
- Arduino Nano für Empfänger und Uno für Sender
- TEMT6000 Lichtsensor
- Laserpointer rot
- OLED‑Display (SSD1306, 128×64, I²C)
- 2x Steckbrett (Breadboard) mit Jumper‑Kabel
- Fischertechnik zum Ausrichten des Lasers (Siehe Teil1)
Aufbau und Verkabelung
Der Anschluss ist unkompliziert und lässt sich komplett auf einem Steckbrett ohne Löten aufbauen:
1. Sender (Laser-Modul)
- VCC (Versorgungsspannung) an 5V (oder 3.3V, je nach Modul)
- GND (Masse) an GND
- Signal/Pin an einen digitalen Ausgangspin (z. B. Pin D3 oder direkt an TX, den Sende-Pin der seriellen Schnittstelle)

Sketch Code für Sender:
Warum BIT_TIME_US = 500 µs?
- Der TEMT6000 reagiert im Mikrosekundenbereich → perfekt für schnelle Bitwechsel.
- 2000 Baud ist schnell genug, um ganze Displayseiten in unter einer Sekunde zu übertragen.
- Gleichzeitig ist die Geschwindigkeit robust genug, dass kleine Ausrichtungsfehler oder Raumlicht das Signal nicht zerstören.
Bei Veränderung: Kleinere Werte = schneller, aber empfindlicher. Größere Werte = langsamer, aber stabiler. Probiert es mal aus!
Warum Start-Bit, Daten-Bit und Stop-Bit:
Der Laser sendet kurze Licht‑Impulse. Der Fototransistor erkennt diese Impulse als „Licht an“ (1) oder „Licht aus“ (0).
Ein Startbit kündigt ein neues Zeichen an, danach folgen 8 Bits für den Buchstaben, und ein Stoppbit beendet das Zeichen.
So kann der Arduino ganz einfach Buchstaben aus Licht lesen — ohne Kabel, nur mit einem Laserstrahl.
Nennt sich Software‑UART
/*
Laser-Sender (Transmitter) - Fortlaufendes Alphabet
*/
const int LASER_PIN = 7;
const unsigned int BIT_TIME_US = 500; // 500 us = 2000 Baud
// Zeichen pro Display-Füllung (5 Zeilen à 25 Zeichen)
const int BLOCK_SIZE = 125;
char currentChar = 'A';
void setup() {
pinMode(LASER_PIN, OUTPUT);
digitalWrite(LASER_PIN, LOW); // Ruhezustand (Idle): Laser AUS
Serial.begin(115200);
Serial.println(F("Sender: Starte Alphabet-Stream..."));
}
// Sendet ein einzelnes Byte (8 Bit, LSB - Least Significant Bit zuerst, also Binär Code rückwärts)
void sendByte(byte b) {
// Start-Bit
digitalWrite(LASER_PIN, HIGH);
delayMicroseconds(BIT_TIME_US);
// 8 Datenbits
for (int i = 0; i < 8; i++) {
if (b & (1 << i)) {
digitalWrite(LASER_PIN, HIGH);
} else {
digitalWrite(LASER_PIN, LOW);
}
delayMicroseconds(BIT_TIME_US);
}
// Stopp-Bit
digitalWrite(LASER_PIN, LOW);
delayMicroseconds(BIT_TIME_US * 2);
}
void loop() {
// Genau einen Bildschirm voll Zeichen senden
for (int i = 0; i < BLOCK_SIZE; i++) {
sendByte((byte)currentChar);
// Nächster Buchstabe (nach Z wieder A)
currentChar++;
if (currentChar > 'Z') {
currentChar = 'A';
}
}
// Zeilenumbruch als Signal für "Bildschirm voll"
sendByte('\n');
// Pause für Display-Aktualisierung und Betrachtung
delay(1200);
}2. Empfänger (TEMT6000-Modul)
- VCC an 5V (oder 3.3V)
- GND an GND
- OUT/Signal an einen analogen Eingang (z. B. A0) oder an einen digitalen Pin mit Pegelwandler
Sketch Code für Empfänger:
Warum Empfänger an dem analogen Pin angeschlossen wird, aber der Ablesewert mit digitalRead():
Warum funktioniert das?
- Der Laser erzeugt einen sehr starken Helligkeitsunterschied.
- Dadurch liegt der analoge Wert bei Laser‑Treffer deutlich über dem Ruhewert.
- Der Arduino interpretiert Werte über ca. 2.5 V automatisch als HIGH.
/*
Laser-Empfaenger (Receiver) mit SSD1306 OLED (organische Leuchtdiode)
- Nutzt I2C (Inter-Integrated Circuit)
- 5 Zeilen à 25 Zeichen = 125 Zeichen pro Frame
- Zähler oben rechts zeigt jeden Neuaufbau
*/
#include <Wire.h>
#include <U8g2lib.h>
// SSD1306 128x64 OLED über I2C
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
const int SENSOR_PIN = A0;
const unsigned int BIT_TIME_US = 500; // Muss zum Sender passen
// Display-Raster: 5 Zeilen à 25 Zeichen = 125 Zeichen
const byte MAX_LINES = 5;
const byte CHARS_PER_LINE = 25;
char screenBuffer[MAX_LINES][CHARS_PER_LINE + 1];
byte curRow = 0;
byte curCol = 0;
unsigned int packetCount = 0;
void clearScreen() {
for (byte r = 0; r < MAX_LINES; r++) {
for (byte c = 0; c <= CHARS_PER_LINE; c++) {
screenBuffer[r][c] = '\0';
}
}
curRow = 0;
curCol = 0;
}
void addCharToScreen(char ch) {
if (curRow >= MAX_LINES) {
return; // Voll
}
screenBuffer[curRow][curCol] = ch;
curCol++;
screenBuffer[curRow][curCol] = '\0';
if (curCol >= CHARS_PER_LINE) {
curCol = 0;
curRow++;
}
}
void renderScreen() {
packetCount++;
u8g2.firstPage();
do {
// 1. Statusleiste oben
u8g2.setFont(u8g2_font_5x7_tr);
u8g2.drawStr(0, 8, "TEMT6000 RX");
char counterStr[12];
snprintf(counterStr, sizeof(counterStr), "#%u", packetCount);
u8g2.drawStr(100, 8, counterStr);
u8g2.drawHLine(0, 10, 128);
// 2. 5 Zeilen Alphabet ausgeben (Y: 20, 30, 40, 50, 60)
for (byte r = 0; r < MAX_LINES; r++) {
u8g2.drawStr(0, 20 + (r * 10), screenBuffer[r]);
}
} while (u8g2.nextPage());
}
// Liest 1 Byte per Software-UART (Universal Asynchronous Receiver-Transmitter)
bool receiveByte(byte &result) {
delayMicroseconds(BIT_TIME_US + (BIT_TIME_US / 2));
byte data = 0;
for (int i = 0; i < 8; i++) {
if (digitalRead(SENSOR_PIN) == HIGH) {
data |= (1 << i);
}
delayMicroseconds(BIT_TIME_US);
}
bool valid = (digitalRead(SENSOR_PIN) == LOW);
delayMicroseconds(BIT_TIME_US / 2);
result = data;
return valid;
}
void setup() {
pinMode(SENSOR_PIN, INPUT);
Serial.begin(115200);
Serial.println(F("Empfaenger bereit..."));
u8g2.begin();
clearScreen();
renderScreen();
}
void loop() {
// Ruhezustand ist LOW; bei Laser-Treffer (HIGH) startet das Byte
if (digitalRead(SENSOR_PIN) == HIGH) {
byte b = 0;
if (receiveByte(b)) {
if (b == '\n') {
// Block fertig: Display aktualisieren
renderScreen();
Serial.println();
// Puffer leeren für nächsten Frame
clearScreen();
}
else if (b >= 32 && b <= 126) {
addCharToScreen((char)b);
Serial.write((char)b);
}
}
}
}Tipps zur Problembehebung:
- Fremdlicht abschirmen: Baue aus einem kleinen Röhrchen (z. B. ein Stück schwarzer Schrumpfschlauch oder ein 3D-Druck-Zylinder) eine Blende um den TEMT6000. So verhinderst du, dass Lampen im Raum das Signal stören.
- Schwellenwert kalibrieren: Lies zuerst den Ruhewert ohne Laser ein. Setze den Schwellenwert im Code etwa in die Mitte zwischen Raumhelligkeit und vollem Lasereinfall.
- Ausrichtung: Da der Laserstrahl sehr dünn ist, muss die Diode exakt auf die kleine Sensorfläche des TEMT6000 ausgerichtet sein.
Und nicht in das Laserlicht schauen!
Fazit:
Mit dem Wechsel vom langsamen LDR zum schnellen TEMT6000 wird aus einer einfachen Lichtschranke eine richtig flotte Laser‑Datenverbindung. Der Fototransistor reagiert so schnell, dass der Arduino die Lichtimpulse sauber als digitale Bits auswerten kann – und dadurch steigt die Übertragungsrate von wenigen Zeichen pro Sekunde auf ganze Displayseiten in unter einer Sekunde.
Durch die Software‑UART bleibt der Aufbau trotzdem simpel: Ein Laser sendet kurze Lichtimpulse, der Sensor erkennt sie, und der Arduino setzt daraus wieder Buchstaben zusammen.
Trotz des analogen Ausgangs funktioniert der TEMT6000 dank des klaren Helligkeitsunterschieds zuverlässig als digitales Signal. Mit einem passenden BIT_TIME_US kannst du die Geschwindigkeit sogar flexibel anpassen.
Damit wird die Laserübertragung nicht nur schneller, sondern auch stabiler – und zeigt, wie viel Potenzial selbst einfache Bauteile haben, wenn man sie clever kombiniert.
