Datenübertragung per Laser zwischen zwei Arduinos

Das letzte, noch nicht vorstellte Modul In meiner 37-in-one-Sensoren-Bulk-Sammlung ist das Lasermodul KY-008 (roter Laser).

An dieser Stelle gleich eine Warnung: der Laser hat eine Licht-Leistung von 5 Milliwatt und ist kein Spielzeug. Es darf niemals direkt in den Laserstrahl geschaut werden. Das kann zu ernsten Augenschädigungen führen. Wer sicher gehen will, benutzt eine Laserschutzbrille (auf den richtigen Wellenbereich, z. B. 650nm achten).



Für dieses Experiment werden gleich zwei Arduinos Unos benötigt, eines als Sender und eines als Empfänger. Wir werden dann per Laserstrahl Daten von Arduino 1 durch die Luft zu Ardunio 2 senden.

Für den Sender-Arduino benutze ich das Mini Breadboard Prototype Shield, wie hier vorgestellt. Und als Empfänger kommt das Multi Function Shield, das ich bereits hier vorgestellt habe. Es bietet mit seinem 4-fach-7-Segment-Display die Möglichkeit zur einfachen Anzeige des empfangenden Textes an.

Hardware: Laser-Modul KY-008


Das Lasermodul KY-008 verfügt über einen roten Laser, der für die 5V des Arduinos ausgelegt ist, damit aber doch einen hellen Laserpunkt mit etwa 5 mW erzeugt. Das dürfte selbst für Strecken über ein paar hundert Meter ausreichen.

Wie eine LED kennt der Laser zwei Zustände: ein und aus. Entweder er leuchtet oder nicht. Entsprechend einfach ist der Anschluss an den Arduino: Sobald die Signal-Leitung auf HIGH gesetzt wird, geht der Laser an, bei LOW ist er aus.

Der Sender


Ich habe mir aus einem 4 poligen Buchsen-Header und einem 4-poligen Stecker-Header, die ich im 90° Winkel miteinander verlötet habe einen Adapter gebastelt, bei dem ich einmal das Modul einstecken kann und dann das Ganze auf ein Breadbaord. So lässt sich das Laser-Modul leicht und sicher befestigen.

Jetzt fehlt nur noch die Software, die Pin 2 so ein- und ausschaltet und damit den Laser blinken lässt, dass damit Daten übertragen werden können.

Dazu wird jedes Zeichen eines vorgegeben Textes (hier "Hallo 123456789 ") einzeln mit einer Funktion sendChar(char c) übertragen. Die Funktion zerlegt das Zeichen (0-255) in die einzelnen 8 Bits aus denen es besteht und schaltet den Laser dann für jedes 0-Bit kurz und für jeden 1-Bit lang ein. Zwischen jedem Bit gibt es eine kurze Pause und zwischen den Zeichen eine dreifach lange.

Die Defines KURZ und LANG können entsprechend im Source-Code verändert werden, um die Übertragung schneller oder langsamer zu machen.

Source-Code Sender

//////////////////////////////////////////////////////// // (C) 2019 by Oliver Kuhlemann // // Bei Verwendung freue ich mich über Namensnennung, // // Quellenangabe und Verlinkung // // Quelle: http://cool-web.de/arduino/ // //////////////////////////////////////////////////////// #define PinLaser 2 // wo ist der Laser KY-008 angeschlossen? #define KURZ 10 // wie lang ist ein kurzer Impuls (für 0) ? #define LANG 20 // wie lang ist ein langer Impuls (für 1) ? void setup() { pinMode(PinLaser, OUTPUT); } void sendChar(char c) { int dur; for (byte i=0; i<8;i++) { // 8 bits byte b = c & 128; // oberstes Bit if (b>0) { dur = LANG; } else { dur = KURZ; } digitalWrite(PinLaser, HIGH); delay (dur); digitalWrite(PinLaser, LOW); delay (KURZ); // Pause zwischen den Bits c = c << 1; // nächstes Bit //Serial.println(); } //Serial.println("\n"); delay (LANG*3); // Pause nach einem Byte } void loop() { String text = "Hallo 123456789 "; while (1) { for (int i=0; i < text.length(); i++) { sendChar (text[i]); } delay (1000); } } Damit ist die Sender-Seite fertig.

Der Empfänger


Fehlt noch der Empfänger. Irgendwie müssen wir jetzt den durch den Luft gesandten Laserstrahl wieder einfangen und auswerten. Am besten wäre dafür bestimmt ein Fototransistor geeignet. Doch leider hatte ich da nur einen in einer Gabel-Lichtschranke verbautem Modul da, das ich hätte zerstören müssen, weil in der vorliegenden Bauweise kein Lichteinfall möglich gewesen wäre.

Darum habe ich dem guten alten Fotowiderstand / Helligkeitssensor eine Chance gegeben. Die Frage dabei ist natürlich: "Ist so ein Fotowiderstand überhaupt schnell genug für so schnelle Wechsel zwischen hell und dunkel?". In diesem Experiment wollte ich auch die Antwort auf diese Frage stellen.

Zuerst musste die Schaltung vorbereitet werden.

Dazu wurde dem 4x3-Header-Block (normalerweise Stecker) mein selbstgebastelter Adapter aufgesteckt, um die Pins zu Buchsen-Löchern zu machen.

Der Apapter besteht einfach aus sich zwei gegenüberliegenden Blöcken mit je drei nebeneinander liegenden 4-Pin-Headern, die miteinander verlötet sind. Damit das Ganze nicht auseinanderfällt, kam noch ein Schrumpfschlauch drumherum zum Einsatz.

Hier soll nun der Fotowiderstand am analogen Eingang A5 angeschlossen werden.

Der Fotowiderstand allein reicht allerdings nicht, um an A5 einen Wert ablesen zu können. Dazu brauchen wir noch einen Widerstand mit 10 kΩ (bzw. ungefähr so viel, wie der Fotowiderstand bei Normallicht an Widerstand hat), mit dem wir eine Mini Spannungsteilungs-Schaltung aufbauen.


Wie das geht und was eine Spannungsteilungs-Schaltung genau bewirkt, ist hier näher erklärt.

Mit dem Wissen stecken wir den 10 kOhm Widerstand zwischen GND und A5 und den Fotowiderstand zwischen +5V und GND.

Dadruch erreichen wir, dass bei absoluter Dunkelheit der Wert an A5 0 (Null) ist und bei extremen Lichteinfach 1023. Der Laser ist sehr energiereiches Licht und bringt den Sensor auf Werte um die 1008, wenn er voll auftrifft.

Dazu müssen Sender und Empfänger natürlich korrekt ausgerichtet sein.

Am besten wird es außerdem sein, das Experiment bei abgedunkeltem Raum durchzuführen oder aber den Fotowiderstand vom Licht zu isolieren.

Zum Beispiel indem man ihn an das Ende eines Plastik-Trinkhalms steckt, den man mit schwarzem Isolierband umwickelt. Dann kann nur noch sehr wenig Streulicht einfallen.

Dann muss aber auch der Laser sehr genau und in gleicher Höhe ausgerichtet sein, damit das Laserlicht geradlinig durch den Strohhalm auf den Sensor auftreffen kann.












Die Hardware ist verdrahtet und die beiden Arduinos sind so ausgerichtet, dass der Laser mittig auf dem Sensor auftrifft. Der Sender arbeitet schon und man sieht es auf dem Fotowiderstand schnell blinken bzw. flackern (je nach Geschwindigkeit).

Nun fehlt noch die Emfänger-Software. Diese muss registrieren, wenn der Laser auftrifft und die Zeiten messen, wie lang dieser jeweils an gewesen ist.

Mit einer gewissen Toleranz muss er diese Zeiten dann auf auswerten und entscheiden, ob es sich um einen kurzen Impults für ein 0-Bit oder einen langen für ein 1-Bit handelt.

Sind 8 Bits zusammengekommen und sind diese auch zusammengehörig, also keine längere Pause zwischen ihnen, dann werden die 8 Bits zu einem Byte bzw. Zeichen zusammengefügt und das Zeichen wird auf dem 7-Segment-Display ausgegeben.

Als akustische Rückmeldung piept es, solange der Laser auftrifft und größer als der Schwellenwert ist. Der Schwellenwert ist die Helligkeit, die als Lasereintreffen gezählt wird. Um auch bei Tageslicht zu funktionieren, sollte er möglichst hoch gewählt werden, z. B. 1000. Dann muss allerdings auch der Laser entsprechend gut auftreffen, um diese Lichtleistung zu liefern. Das Piepen macht die Justage einfach. Außerdem ist der Rhythmus mit dem Ohr einfacher wahrzunehmen als das Flackern des Lasers mit dem Auge. Und bei Lasern sollte man sowieso nicht genau hinschauen. Das mit der Zeit nervige Piepen wird man einem Druck auf der Taster S1 wieder los.

Außerdem habe ich noch eingebaut, dass die einzelnen Bits durchlaufen. Wie die Zeichen auf dem 7-Segment-Display von rechts nach links durchlaufen, so laufen die letzten 4 Bits von unten nach oben durch. Bei langsameren Übertragung kann man den Bits dann quasi zuschauen.

Wichtig ist natürlich, dass der Takt beim Empfangen der selbe ist wie beim Senden. Die Werte KURZ und LANG müssen also bei Sender und Empfänger gleich sein, sonst sprechen diese nicht die gleiche Sprache und verstehen sich nicht.

Source-Code Empfänger

//////////////////////////////////////////////////////// // (C) 2019 by Oliver Kuhlemann // // Bei Verwendung freue ich mich über Namensnennung, // // Quellenangabe und Verlinkung // // Quelle: http://cool-web.de/arduino/ // //////////////////////////////////////////////////////// #include <TimerOne.h> // für Timings und Interrupts #include <MultiFuncShield.h> // API für das Multi Function Shield #define PinFoto A5 // wo ist der Foto-Widerstand angeschlossen ? #define KURZ 10 // wie lang ist ein kurzer Impuls (für 0) ? #define LANG 20 // wie lang ist ein langer Impuls (für 1) ? #define Schwellwert 1000 // ab wann gilt der Laser als erkannt? void setup() { Timer1.initialize(); MFS.initialize(&Timer1); // initialize multi-function shield library pinMode (PinFoto, INPUT); } void loop() { boolean laser=false; boolean laserBefore=false; long laserStart=0; long laserStop=0; int laserDur=0; int laserPause=0; boolean silent=false; char c=32; byte bitpos=0; byte bit[8]; int dur=0; char seg[4]={32,32,32,32}; byte led[4]={0,0,0,0}; while (1) { int wert = analogRead(PinFoto); if (MFS.getButton() == BUTTON_1_PRESSED) { digitalWrite(3,HIGH); silent=true; } laser = (wert >= Schwellwert); // akustische Rückmeldung, Beeper ist Pin 3 if (!silent) digitalWrite(3,!laser); if (!laserBefore && laser) { // Laser AN gegangen laserStart=millis (); laserPause = millis() - laserStop; if (laserPause > LANG*2) { // Zeichen zuende oder keine Übertragung bitpos=0; } } if (laserBefore && !laser) { // Laser AUS gegangen laserStop=millis (); laserDur = laserStop - laserStart; Serial.println(laserDur); if (laserDur > KURZ * 0.8 && laserDur < KURZ * 1.2) { dur = KURZ; bit [bitpos]=0; bitpos++; } else if (laserDur > LANG * 0.8 && laserDur < LANG * 1.2) { dur = LANG; bit [bitpos]=1; bitpos++; } else { dur=0; bitpos=0; } // Bits auf LEDs ausgeben (die letzten 4) led[0]=led[1]; led[1]=led[2]; led[2]=led[3]; if (dur == KURZ) led [3]=0; if (dur == LANG) led [3]=1; MFS.writeLeds(LED_1, led[0]); MFS.writeLeds(LED_2, led[1]); MFS.writeLeds(LED_3, led[2]); MFS.writeLeds(LED_4, led[3]); if (bitpos >= 8) { // Byte ist voll c=0; for (int i=0; i < 8; i++) { Serial.print(bit[i]); c = c << 1; if (bit[i] >0) c = c | 1; } Serial.println(); Serial.println(c); Serial.println(); //Buchstabe auf MFS ausgeben, dabei ggf. scrollen seg[0]=seg[1]; seg[1]=seg[2]; seg[2]=seg[3]; seg[3]=c; MFS.write(seg); } } delayMicroseconds(100); laserBefore=laser; } } Das im Sourcecode implementierte Timing mit 10 und 20 Millsekunden ist das schnellste was stabil unter den vorherrschenden Bedingungen möglich ist. Bei Dunkelheit, oder wenn sich der Empfänger in einem abgedunkeltem Gehäuse befindet, dann ist sicher noch etwas mehr möglich.

Ich habe in mehrere Versuchsphasen ausprobiert, ob und inwiefern der Fotowiderstand als Empfänger taugt.

Zuerst ging es nur darum, ein Lichtsignal im halbsekündigen Wechsel auseinanderzuhalten, was mittels eines einfachen Blink-Sketches und der Anzeige des A5-Wertes auf der 7-Segment-Anzeige realisiert wurde.

Im zweiten Aufbau ging es dann um eine echte Datenübertragung, allerdings noch auf langsamen Niveau. Das war noch beobachtbar und erleichterte so die Programmierung und das Debugging.

Danach wurde das Tempo weiter gesteigert bis auf 8 und 16 ms. Hier wurden allerdings bei zuviel Streulicht oder nicht ungenauen Ausrichtung hin und wieder ein Zeichen verschluckt, so dass ich ein wenig zurückgegangen bin mit dem Tempo.

Ich habe meine Versuche in einem Video dokumentiert:



Von der Geschwindigkeit ist sicher noch mehr drin. Ich glaube nicht, dass die Geschwindigkeit des Fotowiderstandes schon ausgereizt ist. Und dann könnte man als nächstes natürlich noch einen Fototransistor ausprobieren, der nochmal eine Ecke schneller sein dürfte.

Mir ging es aber diesmal mehr darum, die Machbarkeit zu testen und eine halbwegs praktische Aufgabe für das Laser-Modul zu finden.

Zwischenzeitlich habe ich die Übertragung beschleunigen können, indem ich statt des Fotowiderstandes eine Fotodiode benutze. Mehr dazu im Nachfolge-Artikel Optimierung der Datenübertragung per Laser zwischen zwei Arduinos.