Mit Arduino per LASER-Licht Daten übertragen

In der Welt der Datenübertragung gibt es viele verschiedene Technologien, die verwendet werden können. Eine davon ist die Übertragung von Daten mittels gebündelten Licht – dem LASER. LASER ist eine Abkürzung und steht für Light Amplification by Stimulated Emission of Radiation

In diesem kleinen Projekt werde ich mich damit beschäftigen, wie man Zeichencode (ASCII-Code) mithilfe von Lasern und einem Licht-abhängigen Widerstand (LDR) zwischen zwei voneinander getrennten Arduino Boards übertragen kann. Der darin auch ablesbare Dezimalwert (0 bis 127) möchte ich als Leuchtdauer der Lichtquelle in Millisekunden nutzen.

Zunächst einmal ist es wichtig zu verstehen, was ASCII-Code ist.
ASCII steht für American Standard Code for Information Interchange und bezieht sich auf einen Standard, der jedem Buchstaben, Zahl und Sonderzeichen einen eindeutigen 7-Bit-Code zuweist. Dies ermöglicht es Computern, Text und andere Daten in einer gemeinsamen Sprache auszutauschen.

ASCII Tabelle (Auszug) und die zugewiesenen binären Werte:

Quelle: Wikipedia (Tabelle erweitert mit einer „0“ auf 8 Bit)

Um für ein Zeichen, die benötigte Zeichenkodierung zu finden, suche zuerst oben den entsprechenden Spaltennamen für die ersten 4 Bit und hänge dann den entsprechende Zeilenname hinten dran.
Beispiel Fragezeichen: ? = 0011xxxx + xxxx1111 = 00111111 (Binär in Dezimal 32+16+8+4+2+1) = 63

Bei den hier zu sehenden 128 Zeichensätze, ist der erste (0000000) und der letzte Wert (1111111) ein Sonderzeichen. Die nicht druckbaren Steuerzeichen (Wert 2 bis 31) enthalten Ausgabezeichen wie Zeilenvorschub oder Tabulatorzeichen, Protokollzeichen wie Übertragungsende oder Bestätigung und Trennzeichen wie Datensatztrennzeichen. Und welche darüber hinaus sonst noch existieren, sowie weitere Details ist hier bei Wiki nachzulesen.

Hardware:
2 x Microcontroller Entwicklungsboard oder Arduino Uno
Laser Modul KY-008 650nm 3,3V bis 5V
IS0203 Laser/Infrared Receiver Sensor

Verdrahtung:

Entwicklungsumgebung:

Um ASCII-Code mithilfe von Lasern und LDRs zu übertragen, benötigen wir zwei Arduino Boards, einen 5Volt 650nm Laser und einen LDR. Der Laser wird verwendet, um die ASCII-Codes in Form von Lichtimpulsen zu senden, während der LDR verwendet wird, um diese Impulse zu empfangen, die Dauer in Millisekunden zu messen und in die ursprünglichen ASCII-Codes zurück zu wandeln.

Das erste, was wir tun müssen, ist das Sendesketch auf dem Arduino Board zu programmieren, das die Nachricht senden soll. Das Sketch verwendet die digitalWrite()-Funktion, um den Laser ein- und auszuschalten und die delayMicroseconds()-Funktion, um die Dauer des Lichtimpulses zu steuern. Die Dauer des Impulses entspricht dem dezimalen Wert ASCII-Code des jeweiligen Buchstabens oder Zeichens.

Auf dem Empfänger-Arduino Board programmieren wir das Empfängersketch. Es verwendet die pulseIn()-Funktion, um die Dauer des Lichtimpulses zu messen, die von dem LDR empfangen wird. Dieser Wert wird dann in den entsprechenden ASCII-Code umgewandelt und das Zeichen wird über die serielle Kommunikation ausgegeben.

Bevor wir jedoch die Übertragung starten, müssen wir die Laser-Empfänger-Kombination aufeinander abstimmen. Anfangs keine große Entfernung zwischen den beiden Arduino Boards wählen.
Das Laserlicht sollte sichtbar auf dem Empfänger auftreffen und das Umgebungslicht keinen störenden Einfluss haben.

Dann im Sketch einwenig mit dem Multiplikator Wert experimentieren, bis die umgerechneten Dezimalzahlen mit den tatsächlichen ASCII-Werten übereinstimmen. Je kürzer die Impulsdauer, um so schneller die Übertragung, aber auch Fehleranfälliger.
Zum ersten Kalibrieren habe ich mit nur einem Zeichen begonnen, das im Sekundentakt gesendet wurde.
Beispiel für das Zeichen „?“ = 63
Erhöhung der Sendedauer mal 10 = 630ms

Danach habe ich einfach ein Teil des Alphabet genommen, so konnte ich nicht nur prüfen ob die Zeichen korrekt übertragen wurden, sondern auch ob ein Zeichen fehlte. Hatte dabei bei mir die Sendedauer noch etwas erhöht und die Sendepause verringert.

Ausrichtung des Lasers:
Um mir die Höhenausrichtung des Lasers zu erleichtern, wurde nach dieser präzisen Skizze 😀 mit Fischertechnik ein mit einer Drehschraube verstellbarer Ständer gebaut.

Quellcode:
Hier ist ein Beispiel für das Sender Sketch zum Kalibrieren:

void setup() {
   Serial.begin(9600); 
   pinMode(3, OUTPUT); // Pin 3 für den Laser
}

void loop() {
   char data = '?'; //Beispiel mit dem Fragezeichen
   int asciiValue = data; // umwandeln in Dezimalen Wert
   digitalWrite(3, HIGH);  // Schalte den Laser ein
   delayMicroseconds(asciiValue*10); // Dez Wert mal 10 als Leuchtzeit
   digitalWrite(3, LOW);  // Schalte den Laser aus
   Serial.print(asciiValue*10); //in diesem Beispiel Ausgabe im Seriellen Monitor: 630
   delay(1000); 
}

Sketch für Sender:

const int laserPin = 3; // Pin, an dem der Laser angeschlossen ist

void setup() {
  Serial.begin(9600); // Starte die serielle Kommunikation
  pinMode(laserPin,OUTPUT);  // LaserPin auf Ausgang stellen
}

void loop() {
  String message = "abcdefghijklm"; // Nachricht, die gesendet werden soll
  Serial.print("Folgende Zeichen werden gesendet: "); 
  Serial.println(message); 
  
  for (int i = 0; i < message.length(); i++) { // Arbeitet mit der Länge der Zeichenfolge 
    char c = message.charAt(i); // Greift auf ein bestimmtes Zeichen des Strings zu
    int asciiValue = int(c); // Konvertiere das Zeichen in seinen ASCII-Code
    Serial.print(c); 
    Serial.print(" = "); 
    Serial.println(asciiValue); 
    digitalWrite(laserPin, HIGH); // Schalte den Laser ein
    delayMicroseconds(asciiValue*20); // Warte die Dauer des ASCII-Codes
    digitalWrite(laserPin, LOW); // Schalte den Laser aus
    delay(200); // Warte eine kurze Pause damit ich auch was sehen kann
  }
}

Sketch für Empfänger:

int empfPin = 4; // Pin, an dem das EmpfängerModul angeschlossen ist
unsigned long dauer;

void setup() {
  Serial.begin(9600); // Starte die serielle Kommunikation
  pinMode(empfPin,INPUT);
}

void loop() {
  int dauer = pulseIn(empfPin, HIGH); // Messe die Dauer des Laserimpulses
  int asciiwert = (dauer/20)-2; // Setze die Dauer als ASCII-Code (bei mir minus 2ms Kalibrierkorrektur)
  Serial.print("Dauer: "); 
  Serial.print(dauer/20); 
  Serial.println(" ms"); // Gib das empfangene Zeichen aus
  Serial.print("Echte Dauer: "); 
  Serial.print(dauer); 
  Serial.println(" ms"); // Gib das empfangene Zeichen aus
  char c = char(asciiwert); // Konvertiere den ASCII-Code in ein Zeichen
  Serial.print("Zeichen: "); 
  Serial.println(c); // Gib das empfangene Zeichen aus
  Serial.println("------------"); // Gib das empfangene Zeichen aus
}

Video:

Info:
Der Link zur im Video vorgestellter DLR Animation zum Thema Da­ten­über­tra­gung in Höchst­ge­schwin­dig­keit – La­ser­kom­mu­ni­ka­ti­on im All

Nachtrag:
Nicht im Video zu sehen, die OLED Display Erweiterung, um auch ohne seriellen Monitor die empfangenen Buchstaben zu sehen.

Empfänger Erweiterung mit einem OLED Display:

Hardware:
OLED 0,86 Zoll Display I2C

Verdrahtung:
SDA = A4
SCL = A5
VCC = 3,3 Volt
GND = GND

Quellcode:

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET  -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

int empfPin = 4; // Pin, an dem das EmpfängerModul angeschlossen ist
unsigned long dauer;

void setup() {
  Serial.begin(9600); // Starte die serielle Kommunikation
  pinMode(empfPin,INPUT);
  display.display();
  delay(2000); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
}

void loop() {
  int dauer = pulseIn(empfPin, HIGH); // Messe die Dauer des Laserimpulses
  int asciiwert = (dauer/20)-2; // Setze die Dauer als ASCII-Code (bei mir minus 2ms Kalibrierkorrektur)
  Serial.print("Dauer: "); 
  Serial.print(dauer/20); 
  Serial.println(" ms"); // Gib das empfangene Zeichen aus
  Serial.print("Echte Dauer: "); 
  Serial.print(dauer); 
  Serial.println(" ms"); // Gib das empfangene Zeichen aus
  char c = char(asciiwert); // Konvertiere den ASCII-Code in ein Zeichen
  Serial.print("Zeichen: "); 
  Serial.println(c); // Gib das empfangene Zeichen aus
  Serial.println("------------"); // Gib das empfangene Zeichen aus

  display.setTextSize(5);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(40,20);   //Erster Wert = Anzahl Pixel von links, bis Schrift beginnt 
                              // Zweiter Wert = Anzahl Pixel von oben, bis Schrift beginnt
  display.println(c);
  display.display();
  delay(100);
  display.clearDisplay();
}
, ,