Projekte - LIDAR mit ToF-Sensoren..(TimeofFlight)
Update Dezember 2025 - Basis dieses Projektes ist die Idee, sich mit einem Laser-Entfernungssensor, einem SERVO bzw. rotierenden Spiegel an einem Motor/Stepper-Motor einen einfaches kleines LIDAR-System für den Nahbereich zu bauen. Dabei werden verschiedene Versionen getestet.....
Aktuell gibt es bei mir zwar keine Verwendung dafür - aber die Idee erschien interessant.....
Übersicht
SchaltungLösung mit ServomotorLösung mit SteppermotorFazitUpdate November 2025 - ein neuer TOF-Sensor - Lösung mit Servomotor...
Vor Kurzem hatte ich mir ohnehin einen Laser-Entfernungssensor VLX53L0X besorgt, um damit eventuell die Drehzahl an meinem Windrad zu messen. Aber dafür hatte ich dann eine andere Lösung gefunden.
Die Verkabelung ist eigentlich recht einfach, ich habe sie in meinem Beitrag zu den Laser-Entfernungsmessern VLX53L..X , zum Servo SG90 bzw. zum Steppermotor mit A4988 bereits beschrieben.
Die Schaltung....
Da aufgrund der Bewegungen sich im Experimentierboard ständig die Kabel gelöst haben, habe ich mir eine kleine Platine gelötet - und zugleich die Stromversorgung des Sensors und des SERVO's aus einer Powerbank realisiert. ( beachte GND der Stromversorgung und GND am ESP müssen verbunden werden !)
Die Schaltung wurde später (s.u.) dann noch mit der Ansteuerung für den Stepper-Motor erweitert...



Und natürlich kam wieder mein Powerbank-Wächter zu Einsatz, da sich während der Ruhe- bzw. Programmtest-Phasen die Bank ständig abschaltete.
Ist mir zu kompliziert..... =>.... zurück zur Startseite
Erster Versuch...mit Servo-Motor...
Zuerst habe ich einen kleinen SERVO SG90 verwendet, den ich über den ESP32 angesteuert habe. Für den Sensor habe ich mir eine kleine Halterung in Sketchup konstruiert und gedruckt...und für den SERVO einen kleinen Fuss.

hier die Sketchup-Datei zum Download...
und so sieht die Sache dann aus.....
Angesteuert wird die Mess-Einheit selbst über den ESP32.. ferngesteuert über Bluetooth von meinem RasPi....
(Achtung - der Text [code].....[/code] gehört NICHT zum Programm..)
[code]
// Version für SERVO, VL53L0X und Bluetooth..
// ARBEITSVERSION OHNE ABSICHERUNGEN ETC.....
// Servo und Messteil mit externen 5v versorgen !!!
// ESP möglichst ebenfalls aus der Konserve versorgen..
// Teile des Codes wurden aus dem WEB bzw. den Beispielen
// kopiert und angepasst !!
// ACHTUNG, dieses Programm wird vom RasPi über Bluetooth
// angesteuert....
// Bibliotheken.....
#include "BluetoothSerial.h" // für Bluetooth
#include <Wire.h> // für I2C
#include <VL53L0X.h> // für sensor
#include <Servo.h> // für servo
// Definitionen......
BluetoothSerial SerialBT;
String Message_BT = "";
char incomingChar = 0;
String Order_BT = "Warten";
VL53L0X sensor; // sensorbezeichnung definieren
#define LONG_RANGE // besser im Dunkeln
#define HIGH_SPEED // Schnell ODER Korrekt !!!
//#define HIGH_ACCURACY
int diff = 0;
int diff1 = 0;
int diff2 = 0;
int diff3 = 0;
String Messreihe = "";
static const int servoPin = 19; // der servo-pin
Servo servo1; // servobezeichnung definieren
int posDegrees = 0;
//***********************************************************************
// erste Einstellungen....
void setup() {
Serial.begin(115200);
delay (2000);
// Bluetooth
SerialBT.begin("ESP32_Lidar"); //Name für den BT
Serial.println("Bereit zum Verbinden");
delay (5000); // für Verbindung und Pairing..
// für I2C....
Wire.begin();
// für den VL53L0X...
sensor.setTimeout(500);
if (!sensor.init())
{
Serial.println("Failed to detect and initialize sensor!");
while (1) {}
}
#if defined LONG_RANGE
// lower the return signal rate limit (default is 0.25 MCPS)
sensor.setSignalRateLimit(0.1);
// increase laser pulse periods (defaults are 14 and 10 PCLKs)
sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18);
sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14);
#endif
#if defined HIGH_SPEED
// reduce timing budget to 20 ms (default is about 33 ms)
sensor.setMeasurementTimingBudget(20000);
#elif defined HIGH_ACCURACY
// increase timing budget to 200 ms
sensor.setMeasurementTimingBudget(200000);
#endif
// Servo-Steuerpin zuordnen
servo1.attach(servoPin);
} // end setup
//***********************************************************************
// Dauerschleife.....
void loop() {
// ACHTUNG Order_BT wird in Bluetooth-Abfrage aus Message_BT gebildet !!! s.u.
if ( Order_BT == ("Start mit Servo") ) { // Startbefehl gesendet....
// VORWÄRTS....
// for(int posDegrees = 0; posDegrees <= 180; posDegrees++) {
posDegrees = 0;
while ( posDegrees <=180 ){
diff=0;
diff1=0;
diff2=0;
diff3=0;
servo1.write(posDegrees);
if (posDegrees == 0 ){
// nichts machen....
}else{
// 3x Messen für Mittelwert...
diff1=(sensor.readRangeSingleMillimeters());
diff2=(sensor.readRangeSingleMillimeters());
diff3=(sensor.readRangeSingleMillimeters());
// Mittelwert bilden - wenn Messungen erfolgreich...
if ( (diff1 > 0) && (diff2 > 0) && (diff3 > 0) ) { diff= round( diff1+diff2+diff3 ) / 3; }
// sonst Problem...hier erst mal ignoriert...
// if (sensor.timeoutOccurred()) { Serial.println(" TIMEOUT");}
// Einschränkung gültiger Messwerte für die Übergabe an den RasPi..
if ( (diff > 500 ) && (diff < 1500 )) {
Messreihe += String(posDegrees);
Messreihe += ("//");
Messreihe += String(diff);
Messreihe += ("//");
}
} // endif posDegrees = 0 oder...
delay(10); // normalerweise min. 20 für Servo, da aber Messungen auch dauern >= gewählt....
posDegrees=posDegrees +10;
} // endwhile <= 180..
// wir senden den gesammelten string des vorlaufs mit den Messwerten...
if (Messreihe != "") {
SerialBT.print(Messreihe);
SerialBT.println();
// Serial.println(Messreihe);
Messreihe = ""; // zurücksetzen...
}
// RÜCKWÄRTS...
// for(int posDegrees = 180; posDegrees >= 0; posDegrees--) {
posDegrees = 180;
while ( posDegrees >= 0){
diff=0;
diff1=0;
diff2=0;
diff3=0;
servo1.write(posDegrees);
if (posDegrees == 180 ){
// nichts machen...
}else{
// 3x Messen für Mittelwertbildung...
diff1=(sensor.readRangeSingleMillimeters());
diff2=(sensor.readRangeSingleMillimeters());
diff3=(sensor.readRangeSingleMillimeters());
// Mittelwert bilden...wenn Messung erfolgreich
if ( (diff1 > 0) && (diff2 > 0) && (diff3 > 0) ) { diff= round( diff1+diff2+diff3 ) / 3; }
// sonst Problem...hier erst mal ignoriert...
// if (sensor.timeoutOccurred()) { Serial.println(" TIMEOUT");}
// Einschränkung gültiger Messwerte für die Übergabe an den RasPi..
if ( (diff > 500 ) && (diff < 1500 )) {
Messreihe += String(posDegrees);
Messreihe += ("//");
Messreihe += String(diff);
Messreihe += ("//");
}
} // endif posDegrees = 0 oder...
delay(10); // normalerweise min. 20 für Servo, da aber Messungen auch dauern >= gewählt....
posDegrees=posDegrees -10;
} // endwhile >=0...
// wir senden den gesammelten string mit den Messwerten...
if (Messreihe != "") {
SerialBT.print(Messreihe);
SerialBT.println();
// Serial.println(Messreihe);
Messreihe = ""; // zurücksetzen...
}
}
else // ORDER_BT ist Start mit Servo
{
delay(500); // sonst würde er volle Hütte durchlaufen....da ja kein Job..
}
// Wir schauen auf Bluetooth - Nachrichten = Steuerbefehl vom RasPi....
if ( SerialBT.available() ){
Message_BT = "";
incomingChar = (SerialBT.read());
while ( incomingChar != '\n' ) {
Message_BT += String(incomingChar);
delay(50);
incomingChar = (SerialBT.read());
}
if (Message_BT == "Start mit Servo" ) { Order_BT = "Start mit Servo";}
if (Message_BT == "Warten" ) { Order_BT = "Warten";}
}
} // end loop
[/code]
Wie bereits erwähnt, wird das ESP-Programm durch meinen RasPi und ein Lazarus-Programm über Bluetooth ferngesteuert....
Um dies also nachzuvollziehen, benötigt Ihr einen RasPi mit Linux (Raspian oder eine aktuellere Variante) und installiertem Lazarus, da die beigefügte zip-Datei mit der Lazarus-Programmierung aktuell im Windows-Dateiformat ist, müsst Ihr sie dann mit "WinSCP" oder einem ähnlichem Programm auf den RasPi übertragen.
ACHTUNG: Die Programmierung ist ein Arbeitsstand, sie funktioniert grundlegend ist aber nicht gegen Fehlbedienung etc. abgesichert. Aber Ihr könnt ein "hängendes Programm" in der Lazarus-Oberfläche jederzeit mit "Halt" abbrechen...
hier die Lazarus-Dateien zum Download...
Das Projekt ist inklusive aller Dateien komplett, ich habe die Programmschritte hoffentlich ausreichend kommentiert. Ihr benötigt aber noch das LazSerial-Package.
Im Archiv sind neben den "üblichen" Lazarus-Projektdateien auch 2 spezielle LINUX-Batch-Dateien (*.sh) für die Anbindung bzw. das Trennen derselben zum ESP über Bluetooth, diese müsst Ihr editieren und die aktuelle Bluetooth-Adresse Eures ESP eintragen...
Dazu muss natürlich auf dem ESP das obige Programm laufen, damit er sich über Bluetooth überhaupt meldet....
Dann geht Ihr dazu in das Linux-Terminal-Fenster auf Eurem RasPi ( das kleine schwarze Fenstersymbol in der Statusleiste ), dort gebt Ihr ein....
sudo hcitool scan
Als Ergebniss solltet Ihr dann solch eine kryptische Antwort bekommen, dies ist die Bluetooth-Adresse Eures ESP.....
A0:A3:B3:2B:EB:BA ESP32_Lidar
Mit dieser Adresse müsst Ihr den Eintrag in den beiden Dateien anpassen, dies sollte mit dem Texteditor des Betriebssystem problemlos möglich sein.
Hinweis: Ein "#" vor der jeweiligen Zeile deaktiviert diese Zeile für die spätere Ausführung....
Die beiden Dateien sind eigentlich bereits als "ausführbare Dateien" definiert - sollte dies aber auf Eurem RasPi nicht funktionieren, müsst Ihr mal im Web nach den notwendigen Schritten für "Script ausführbar machen....." suchen. (ist aber nicht kompliziert)
Nachdem die Vorbereitungen abgeschlossen sind, könnt Ihr das Lazarus-Projekt laden und mit F9 kompilieren und starten......( der "Radarbildschirm" baut sich normalerweise erst nach Messbeginn auf...)

| ESP aktiv ? | nur für zusätzliche Kontrolle, beim Start wird der ESP automatisch gesucht.... |
| ESP anbinden | hier wird die externe Batch-Datei (s.o.) ausgeführt |
| Messung starten | wechselt dann zu Messung beenden.... |
| ESP trennen | hier wird die externe Batch-Datei (s.o.) ausgeführt |
| Button1 | (ist normalerweise deaktiviert und unsichtbar..) |
Ist mir zu kompliziert..... =>.... zurück zur Startseite
Zweiter Versuch...mit Stepper-Motor....
Beim zweiten Versuch habe ich einen kleinen Stepper-Motor NEMA17 verwendet, den ich ebenfalls über den ESP32 angesteuert habe. Zwischenzeitlich hatte ich es auch mit einem ungesteuerten Motor versucht, dieser drehte allerdings viel zu schnell.
Für den Steppermotor war eine zusätzliche Steuerungsplatine notwendig, dazu habe ich hier genauere Angaben gemacht...

Für den Sensor habe ich mir wieder eine kleine Halterung in Sketchup konstruiert und gedruckt...und für den NEMA-Stepper ebenfalls einen kleinen Fuss.
Als rotierenden Spiegel hatte ich zuerst eine 25x25mm Spiegelscheibe aus einer Spiegelfliese, dann so einen kleinen Rundspiegel ( Durchmesser ca. 40mm mit Teleskopstange für ca. 3€ im Handel erhältlich) genutzt.


Aber leider sind diese Spiegel mit einer Glasoberfläche mit zu grossen Verzerrungen behaftet und die Messergebnisse waren nicht brauchbar - die maximale Messentferung waren nur knapp 50cm, danach konnte kein Gegenstand mehr erkannt werden.
Auch ein zwischenzeitliches Hardware-Upgrade auf einen besseren Sensor VL53L1X brachte da keine Verbesserung....
Als letze Version habe ich dann in Fernost einen Laser-Spiegel mit Durchmesser 30mm bestellt (bei hiesigen Anbietern habe ich leider nur kleinere Durchmesser gefunden). Diese Spiegel werden für die Umlenkung von Laserstrahlen in technischen Geräten genutzt, sind aus hochglanzpolierten Stählen - und leider nicht ganz billig....
Dies nun ist die bisherig letzte Version....

hier die Sketchup-Datei zum Download....
Und ein kleines Video ( was da am Ende so knallt - das "Ziel" ist umgefallen....)
Angesteuert wird die Mess-Einheit selbst wieder über den ESP32.. ferngesteuert ebenfalls über Bluetooth von meinem RasPi....
(Achtung - der Text [code].....[/code] gehört NICHT zum Programm..)
[code]
// Version für NEMA mit A4988, VL53L1X und Bluetooth..
// NEMA mit externen 8-12V und Messteil mit externen 5v versorgen !!!
// ESP möglichst ebenfalls aus der Konserve versorgen..
// Teile des Codes wurden aus dem WEB bzw. den Beispielen
// kopiert und angepasst !!
// ACHTUNG: keine Nullpunktsuche - vor dem ersten Start NEMA auf NULL drehen !!!!
// ACHTUNG...
// in der aktuellen Konfiguration ist der DIR mit ENABLED verbunden
// d.h. DIR = HIGH => ENABLED = HIGH ( und somit ist der Treiber AUS geschaltet )
// Bibliotheken.....
#include "BluetoothSerial.h" // für Bluetooth
#include <Wire.h> // für I2C
#include "SparkFun_VL53L1X.h" // für den 4m Sensor..
// Definitionen......
BluetoothSerial SerialBT;
String Message_BT = "";
char incomingChar = 0;
String Order_BT = "Warten";
// getestet...
// der Sensor misst ca. 40x in der Sekunde (max. wenn allein am Board)
// das sind 1.000.000 mues / 40 = 25.000 mues....
SFEVL53L1X distanceSensor; // Sensorbezeichnung definieren
uint8_t STEP_Pin = 18; // der Pin für die Ansteuerung
uint8_t DIR_Pin = 19; // der Pin für die Drehrichtung UND AN/AUS ENABLED...
// Der Motor braucht im Vollschrittmodus 200 Steuersequenzen / 360 Grad
// das sind 1.8 Grad je Steuersequenz.. sprich 5 Steuersequenzen sind 9 Grad
// Der Motor braucht im 1/4-Schrittmodus 800 Steuersequenzen / 360 Grad
// das sind 0.45 Grad je Steuersequenz.. sprich 20 Steuersequenzen sind 9 Grad
// in jedem Modi werden andere Verhältnisse massgebend...
// AKTUELL ist Viertelschrittmodus eingestellt - MS2-Pin = HIGH...
int alpha_Mess_seq = 40 ; // wären dann ... Grad bis zur Mess-Stelle s.o
float alpha_360_seq = 800.0; // .... Sequenzen für 360 Grad s.o.
// MUSS fürs Rechnen float sein int und float => float sonst bleibt
// das Ergebniss int....
// die Umdrehung/min kann man über die mues_Delay einstellen...
// 950 sind die minimale Verzögerungszeit im Vollschrittmodus ( wenn
// KEINER der 3 Modi-Pins gesetzt ist - es sind intern alle auf LOW
// 218 sind die minimale Verzögerungszeit im 1/4-Schrittmodus ( wenn
// MS2-Pin HIGH gesetzt ist....
// andere Modi brauchen eine andere min. Verzögerung !!! siehe Handbuch...
int mues_Delay = 400; // ... Verzögerung in mues/Schritt - iterativ...
int diff = 0;
int diff1 = 0;
int diff2 = 0;
int diff3 = 0;
String Messreihe = "";
int posDegrees = 0;
//***********************************************************************
// erste Einstellungen....
void setup() {
// zuerst für den NEMA...damit er stromlos geschaltet ist...
pinMode(STEP_Pin, OUTPUT); // Modus einstellen
pinMode(DIR_Pin, OUTPUT);
// ACHTUNG...
// in der aktuellen Konfiguration ist der DIR mit ENABLED verbunden
// d.h. DIR = HIGH => ENABLED = HIGH ( und somit ist der Treiber AUS geschaltet )
digitalWrite(STEP_Pin, LOW); // generell erst mal Status definieren
digitalWrite(DIR_Pin, HIGH); // damit der Pin nicht im "Leeren" hängt...
// damit auch DIR=ENABLED = HIGH = AUS...
// normaler Programmstart...
Serial.begin(115200);
delay (2000);
// Bluetooth
SerialBT.begin("ESP32_Lidar"); //Name für den BT
Serial.println("Bereit zum Verbinden");
delay (5000);
// für I2C....
Wire.begin();
// für den VL53L1X...
if (distanceSensor.begin() != 0) //Begin returns 0 on a good init
{
Serial.println("Sensor failed to begin. Please check wiring. Freezing...");
while (1);
}
} // Ende setup
//***********************************************************************
// Dauerschleife.....
void loop() {
// ACHTUNG Order_BT wird in Bluetooth-Abfrage aus Maessage_BT gebildet !!! s.u.
// ggf. andere Befehle können z.B. Messen ohne Motorbewegung realisieren - aktuell
// aber nicht implementiert...
if ( Order_BT == ("Start mit Motor") ) { // Startbefehl vom RasPi gesendet....
posDegrees = 0;
digitalWrite(DIR_Pin, LOW);
// damit auch DIR=ENABLED = LOW = AN...
int i = 1;
int k = 1;
while ( i <= alpha_360_seq ){ // notwendige Sequenzen für 360 Grad
diff=0;
diff1=0;
diff2=0;
diff3=0;
k = 1;
while ( k <= alpha_Mess_seq ) { // Zählvariable...
if ( alpha_Mess_seq > 15 ){ // Anlauf- und Bremssteuerung
// || heisst ODER... && heisst UND...
if ( ( k <=5) || ( k >= (alpha_Mess_seq - 5 )) ) {
// Motoranlauf/-bremsung mit psch. 300 % des Regeldelays - 5 Sequenzen...
digitalWrite(STEP_Pin, HIGH);
delayMicroseconds( 3 * mues_Delay );
digitalWrite(STEP_Pin, LOW);
delayMicroseconds( 3 * mues_Delay );
} // endif...k im 5er-Bereich
else
{
// dazwischen normale Motoransteuerung - restliche Sequenzen...
digitalWrite(STEP_Pin, HIGH);
delayMicroseconds (mues_Delay);
digitalWrite(STEP_Pin, LOW);
delayMicroseconds (mues_Delay);
} // endif...k NICHT im 5er-Bereich
}
else // wenn k nicht mind. 15...
{
// sonst normale Motoransteuerung - eine Sequenz...
digitalWrite(STEP_Pin, HIGH);
delayMicroseconds (mues_Delay);
digitalWrite(STEP_Pin, LOW);
delayMicroseconds (mues_Delay);
}
// gezählt wird immer...
k=k+1; // wird NACH ersten Durchlauf 2 => erster Abschnitt überwunden
i=i+1; // wird NACH letztem Durchlauf 6x => bei Ansteuerung 5x 1.8 GRad = 9 Grad
// (im Vollschrittmodus)
} // endwhile..
// (akt_i / Anz_i für 360 ) * 360 Grad...
// i-1, da wir für 5 Durchläufe bereits auf 6 sind...
// 360 * (6-1) / 200 = 9 Grad (im Vollschrittmodus)
posDegrees = round( 360 * ((i-1)/alpha_360_seq));
// Messvorgang...
distanceSensor.startOneshotRanging(); // Initialisierung
while (!distanceSensor.checkForDataReady())
{
delay(1); // Warten bis bereit....
}
diff = distanceSensor.getDistance(); // Messergebniss
// diff1 = distanceSensor.getDistance(); // etc. diff2/diff3 - aktuell nicht aktiv...
// zur Kontrolle auf dem seriellen Monitor in der Arduino - USB-Verbindung
Serial.print(posDegrees);
Serial.print("/");
Serial.print(diff);
Serial.println();
// Mittelwertbildung - aktuell nicht aktiv...
// if ( (diff1 > 0) && (diff2 > 0) && (diff3 > 0) ) { diff= round( diff1+diff2+diff3 ) / 3; }
// unbrauchbare Messwerte rausfiltern....
if ( (diff > 100 ) && (diff < 2500 )) {
Messreihe += String(posDegrees);
Messreihe += ("//");
Messreihe += String(diff);
Messreihe += ("//");
}
} // endwhile i < alpha_360_seq..(aktuell 200)
// wir senden den gesammelten string des Durchlaufs mit den Messwerten...
// sofern Daten vorhanden sind...an den RasPi...
if (Messreihe != "") {
SerialBT.print(Messreihe);
SerialBT.println();
Messreihe = ""; // zurücksetzen...
delay(20); // warten bis gesendet....
}
} // Ende "Start mit Motor"
else
{
digitalWrite(STEP_Pin, LOW);
digitalWrite(DIR_Pin, HIGH);
// damit auch DIR=ENABLED = HIGH = LOW...
delay(500); // sonst würde er volle Hütte durchlaufen....da ja kein Job..
}
//************************************************************************
// wir schauen auf Nachrichten = ob Steuerbefehl vom RasPi kommt....
if ( SerialBT.available() ){
Message_BT = "";
// der Text vom RasPi kommt buchstabenweise an...EndeZeichen ist dann "/n"...
incomingChar = (SerialBT.read());
while ( incomingChar != '\n' ) {
Message_BT += String(incomingChar);
delay(50);
incomingChar = (SerialBT.read());
}
if (Message_BT == "Start mit Motor" ) { Order_BT = "Start mit Motor";}
// if (Message_BT == "Start ohne Motor" ) { Order_BT = "Start ohne Motor";} // nicht implementiert...
if (Message_BT == "Warten" ) { Order_BT = "Warten";}
}
} // Ende loop
[/code]Wie bereits erwähnt, wird das ESP-Programm durch meinen RasPi und ein Lazarus-Programm über Bluetooth ferngesteuert....
ACHTUNG: Die technischen Voraussetzungen wie auch die notwendigen Schritte der Einrichtung sind analog der vorhergehenden Variante mit dem Servo ( siehe dort...).
ACHTUNG: Bitte an die Reihenfolge der Stromversorgung des Steppermotors denken
ACHTUNG: Die Programmierung ist ebenfalls ein Arbeitsstand, sie funktioniert grundlegend ist aber nicht gegen Fehlbedienung etc. abgesichert. Aber Ihr könnt ein "hängendes Programm" in der Lazarus-Oberfläche jederzeit mit "Halt" abbrechen...
hier die Lazarus-Dateien zum Download...
Nachdem die Vorbereitungen abgeschlossen sind, könnt Ihr das Lazarus-Projekt laden und mit F9 kompilieren und starten......( der "Radarbildschirm" baut sich normalerweise erst nach Messbeginn auf...)

| ESP aktiv ? | nur für zusätzliche Kontrolle, beim Start wird der ESP automatisch gesucht.... |
| ESP anbinden | hier wird die externe Batch-Datei (s.o.) ausgeführt |
| Messung starten | wechselt dann zu Messung beenden.... |
| ESP trennen | hier wird die externe Batch-Datei (s.o.) ausgeführt |
| Button1 | (ist normalerweise deaktiviert und unsichtbar..) |
FAZIT....
Tja..... das Endergebniss ist leider unbefriedigend, auch mit dem hochwertigen Spiegel und dem besseren Entfernungs-Messmodul VLX53L1X ergeben sich leider nur Reichweiten bis zu max. 1.30m, weiter entfernte Teile werden nicht mehr erkannt.
Die gemesse Entfernung liegt desweiteren zwischen 20 - 40% abweichend (kürzer) als die reale Entfernung - und variiert zusätzlich.
Da als Ursache der - jetzt hochwertige - Spiegel ausfallen dürfte, muss sie im Sensor-Modul selbst liegen. Dieses kommt leider ohne optische Linsen und somit Ausrichtung der gesendeten Strahlen etc. daher und streut daher als "punktuelle Laserquelle" stark (angeblich ca. 27 Grad, beim L1X soll die Streuung durch Reduzierung der Sende-Auflösung verkleinert werden können)
Da ich aktuell hier keine bessere Idee - oder Hardware - habe, liegt die Fortführung dieses Projektes vorerst "auf Eis".......
Ist mir zu kompliziert..... =>.... zurück zur Startseite
Update November 2025 - ein neuer TOF-Sensor - Lösung mit Servo....
Beim Stöbern auf einer bekannten OnLine-Plattform habe ich einen neuen TOF-Sensor gefunden und gleich bestellt. Lt. Datenblatt soll dieser Sensor bis zu 8m mit einer Genauigkeit von 4mm messen können - und - er hat im Gegensatz zu den bisherig verwendeten Sensoren eine Optik und angeblich einen Blickwinkel von lediglich 2 Grad...

Nun.. die Platine ist klein - sehr klein - und ich musste mit erst mal einen Adapter drucken, um sie an meiner bisherigen Mechanik zu befestigen. Da ich keine 1.6mm-Schrauben gefunden habe, wurde es eine Klemm-Mechanik.
Der Sensor kann in der Grundeinstellung lediglich über UART (RX/TX) kommunizieren, er soll sich aber mit einer speziellen Software des Herstellers und einem FTDI-Board auch auf I2C-Kommunikation umstellen lassen.
Der Hersteller "waveshare" stellt auf seiner Webseite im "WiKi"-Bereich auch entsprechende Ansteuerungs-Software für RasPi bzw. ESP32 zur Verfügung, allerdings sind die Programmdateien auf 3 Teildateien verteilt und unübersichtlich - ich habe es mir daher mal als Basis-Grundprogramm in einer eigenen Datei zusammenkopiert und mein bisheriges Testprogramm mit Servoansteuerung entsprechend modifiziert..
/*
* 25_12_06 aus Zusammenstellung der notwendigen Funktionen für Messung
* auf Anfrage aus dem UART-TOF-Beispiel von Waveshare...mein_MiniTOF_RXTX.ino
* sowie mein_SimpleServor_VL53LOX.ino - jetzt aber auf RXTX-UART Pin 17/16
* abgefragt...
*
* ARBEITSVERSION OHNE ABSICHERUNGEN ETC.....
*
* Servo und Messteil mit externen 5v versorgen !!!
* ESP möglichst ebenfalls aus der Konserve versorgen..
* Teile des Codes wurden aus dem WEB bzw. den Beispielen
* kopiert und angepasst !!
* ACHTUNG, dieses Programm wird vom RasPi über Bluetooth
* angesteuert....
*/
// Bibliotheken.....
#include "BluetoothSerial.h" // für Bluetooth
#include <Servo.h> // für servo
//****************Deklarationen TOF
//#include "Arduino.h"
// #ifndef _TOF_SENSE_H_
// #define _TOF_SENSE_H_
//Define uart communication pins
#define TOF_RX_PIN 17
#define TOF_TX_PIN 16
// ??
#define TOF_FRAME_HEADER 0x57//TOFSense-F
#define TOF_FUNCTION_MARK 0x00//TOFSense-F
//Define serial port name
#define TOF_UART Serial1
// Speicherblock für die Mess-Daten definieren..
typedef struct {
uint8_t id;//TOF id
uint32_t system_time;//TOF ms
uint32_t dis;//TOF mm
uint8_t dis_status;//TOF
uint16_t signal_strength;//TOF
uint8_t range_precision;//TOF
} TOF_Parameter;//
// Speicherblock für die Daten zuordnen/erzeugen/reservieren..
TOF_Parameter TOF_0; // Define a structure to store decoded data
// diverse globale Variablen definieren...
uint8_t count_i = 0, count_j = 0; // Loop count variable
uint8_t check_sum = 0; // Checksum
uint8_t rx_buf[16]; // Serial port receiving array
uint8_t tx_buf[8] = {0x57,0x10,0xff,0xff,0x00,0xff,0xff,0x63};//Query the command with ID 0
// Definitionen Programmablauf
BluetoothSerial SerialBT;
String Message_BT = "";
char incomingChar = 0;
String Order_BT = "Warten";
int diff = 0;
int diff1 = 0;
int diff2 = 0;
int diff3 = 0;
String Messreihe = "";
static const int servoPin = 19; // der servo-pin
Servo servo1; // servobezeichnung definieren
int posDegrees = 0;
// ************** Startprozedur
void setup() {
Serial.begin(115200);//Initialize the USB serial port baud rate to 115200
TOF_UART.begin(921600, SERIAL_8N1, TOF_RX_PIN, TOF_TX_PIN);//Initialize the TOF serial port baud rate to 921600 and specify the pin
// TOF_UART.begin(921600, SERIAL_8N1, 17, 16);
// Bluetooth
SerialBT.begin("ESP32_Lidar"); //Name für den BT
Serial.println("Bereit zum Verbinden");
delay (1000); // für Verbindung und Pairing..
// Servo-Steuerpin zuordnen
servo1.attach(servoPin);
}
// *****************Dauerlaufprozedur
void loop() {
// ACHTUNG Order_BT wird in Bluetooth-Abfrage aus Message_BT gebildet !!! s.u.
if ( Order_BT == ("Start mit Servo") ) { // Startbefehl gesendet....
// VORWÄRTS....
// for(int posDegrees = 0; posDegrees <= 180; posDegrees++) {
posDegrees = 0;
while ( posDegrees <=180 ){
diff=0;
diff1=0;
diff2=0;
diff3=0;
servo1.write(posDegrees);
if (posDegrees == 0 ){
// nichts machen....
}else{
// 3x Messen für Mittelwert...
TOF_Inquire_Decoding(0);//Query and decode TOF data
diff1=(TOF_0.dis);
TOF_Inquire_Decoding(0);//Query and decode TOF data
diff2=(TOF_0.dis);
TOF_Inquire_Decoding(0);//Query and decode TOF data
diff3=(TOF_0.dis);
// Mittelwert bilden - wenn Messungen erfolgreich...
if ( (diff1 > 0) && (diff2 > 0) && (diff3 > 0) ) { diff= round( diff1+diff2+diff3 ) / 3; }
// sonst Problem...hier erst mal ignoriert...
// if (sensor.timeoutOccurred()) { Serial.println(" TIMEOUT");}
// Einschränkung gültiger Messwerte für die Übergabe an den RasPi..
if ( (diff > 500 ) && (diff < 5000 )) {
Messreihe += String(posDegrees);
Messreihe += ("//");
Messreihe += String(diff);
Messreihe += ("//");
}
} // endif posDegrees = 0 oder...
delay(1); // normalerweise min. 20 für Servo, da aber Messungen auch dauern >= gewählt....
posDegrees=posDegrees +2;
} // endwhile <= 180..
// wir senden den gesammelten string des vorlaufs mit den Messwerten...
if (Messreihe != "") {
SerialBT.print(Messreihe);
SerialBT.println();
// Serial.println(Messreihe);
Messreihe = ""; // zurücksetzen...
}
// RÜCKWÄRTS...
// for(int posDegrees = 180; posDegrees >= 0; posDegrees--) {
posDegrees = 180;
while ( posDegrees >= 0){
diff=0;
diff1=0;
diff2=0;
diff3=0;
servo1.write(posDegrees);
if (posDegrees == 180 ){
// nichts machen...
}else{
// 3x Messen für Mittelwert...
TOF_Inquire_Decoding(0);//Query and decode TOF data
diff1=(TOF_0.dis);
TOF_Inquire_Decoding(0);//Query and decode TOF data
diff2=(TOF_0.dis);
TOF_Inquire_Decoding(0);//Query and decode TOF data
diff3=(TOF_0.dis);
// Mittelwert bilden...wenn Messung erfolgreich
if ( (diff1 > 0) && (diff2 > 0) && (diff3 > 0) ) { diff= round( diff1+diff2+diff3 ) / 3; }
// sonst Problem...hier erst mal ignoriert...
// if (sensor.timeoutOccurred()) { Serial.println(" TIMEOUT");}
// Einschränkung gültiger Messwerte für die Übergabe an den RasPi..
if ( (diff > 500 ) && (diff < 5000 )) {
Messreihe += String(posDegrees);
Messreihe += ("//");
Messreihe += String(diff);
Messreihe += ("//");
}
} // endif posDegrees = 0 oder...
delay(1); // normalerweise min. 20 für Servo, da aber Messungen auch dauern >= gewählt....
posDegrees=posDegrees -2;
} // endwhile >=0...
// wir senden den gesammelten string mit den Messwerten...
if (Messreihe != "") {
SerialBT.print(Messreihe);
SerialBT.println();
// Serial.println(Messreihe);
Messreihe = ""; // zurücksetzen...
}
}
else // ORDER_BT ist Start mit Servo
{
delay(500); // sonst würde er volle Hütte durchlaufen....da ja kein Job..
}
// Wir schauen auf Bluetooth - Nachrichten = Steuerbefehl vom RasPi....
if ( SerialBT.available() ){
Message_BT = "";
incomingChar = (SerialBT.read());
while ( incomingChar != '\n' ) {
Message_BT += String(incomingChar);
delay(50);
incomingChar = (SerialBT.read());
}
if (Message_BT == "Start mit Servo" ) { Order_BT = "Start mit Servo";}
if (Message_BT == "Warten" ) { Order_BT = "Warten";}
}
// nur aufgehobene Befehle...Sicherheitskopie
// TOF_Active_Decoding();//Query and decode TOF data
// TOF_Inquire_Decoding(0);//Query and decode TOF data
// printf("TOF distance is:%d mm\r\n", TOF_0.dis);
// delay(500);//The refresh rate defaults to 50HZ. If the refresh rate is set to 100HZ, the time here is 1/100=0.01s
}
//***************TOF Prozeduren
/*
// wird aktuell nicht genutzt...
void TOF_Active_Decoding()
{
if(TOF_UART.available()>0)//wenn vorhanden
{
if (TOF_UART.peek() == TOF_FRAME_HEADER) // If it is a frame header, restart the loop count
{
count_i = 0;
rx_buf[count_i] = TOF_UART.read(); // Store the read data into a tuple for later decoding
}
else
{
rx_buf[count_i] = TOF_UART.read(); // Store the read data into a tuple for later decoding
}
count_i++;//Loop count +1 循环计数+1
if (count_i > 15)//If the number of received data is greater than 15, the count variable can be cleared and a decoding can be performed.
{
count_i = 0;
for (count_j = 0; count_j < 15; count_j++)
{
check_sum += rx_buf[count_j]; // Calculate the checksum and take the lowest byte
}
//Determine whether the decoding is correct
if ((rx_buf[0] == TOF_FRAME_HEADER) && (rx_buf[1] == TOF_FUNCTION_MARK) && (check_sum == rx_buf[15])) // TOF_FRAME_HEADER/TOF_FUNCTION_MARK
{
TOF_0.id = rx_buf[3]; // ID of the TOF module TOF
TOF_0.system_time = (unsigned long)(((unsigned long)rx_buf[7]) << 24 | ((unsigned long)rx_buf[6]) << 16 | ((unsigned long)rx_buf[5]) << 8 | (unsigned long)rx_buf[4]); // The time after the TOF module is powered on
TOF_0.dis = ((float)(((long)(((unsigned long)rx_buf[10] << 24) | ((unsigned long)rx_buf[9] << 16) | ((unsigned long)rx_buf[8] << 8))) / 256)); // The distance output by the TOF module
TOF_0.dis_status = rx_buf[11]; // Distance status indication output by TOF module
TOF_0.signal_strength = (unsigned int)(((unsigned int)rx_buf[13] << 8) | (unsigned int)rx_buf[12]); // The signal strength output by the TOF module
TOF_0.range_precision = rx_buf[14]; // The repeatability accuracy reference value output by the TOF module is invalid for Type C, Type D and Mini.
//Print data through the terminal
printf("TOF id is:%d\r\n", TOF_0.id);
printf("TOF system time is:%d ms\r\n", TOF_0.system_time);
printf("TOF distance is:%d mm\r\n", TOF_0.dis);
printf("TOF status is:%d\r\n", TOF_0.dis_status);
printf("TOF signal strength is:%d\r\n", TOF_0.signal_strength);
printf("TOF range precision is:%d\r\n\n", TOF_0.range_precision);
while (TOF_UART.read()>= 0);//Clear the serial port buffer
}
else
{
printf("Verification failed.\r\n");
}
}
check_sum = 0; //Clear Checksum
}
}
*/
void TOF_Inquire_Decoding(uint8_t id)
{
tx_buf[4] = id;//Add the ID you want to query to the command
tx_buf[7] = id + 0x63;//Update Checksum
while (TOF_UART.read()>= 0);//Clear the serial port buffer
TOF_UART.write(tx_buf,8);//Start query
delay(10);//Waiting for the sensor to return dat
TOF_UART.read(rx_buf,16);//Reading sensor data
for (count_j = 0; count_j < 15; count_j++)
{
check_sum += rx_buf[count_j]; // Calculate the checksum and take the lowest byte
}
//Determine whether the decoding is correct
if ((rx_buf[0] == TOF_FRAME_HEADER) && (rx_buf[1] == TOF_FUNCTION_MARK) && (check_sum == rx_buf[15])) //TOF_FRAME_HEADER/TOF_FUNCTION_MARK
{
TOF_0.id = rx_buf[3]; // ID of the TOF module TOF
TOF_0.system_time = (unsigned long)(((unsigned long)rx_buf[7]) << 24 | ((unsigned long)rx_buf[6]) << 16 | ((unsigned long)rx_buf[5]) << 8 | (unsigned long)rx_buf[4]); // The time after the TOF module is powered on
TOF_0.dis = ((float)(((long)(((unsigned long)rx_buf[10] << 24) | ((unsigned long)rx_buf[9] << 16) | ((unsigned long)rx_buf[8] << 8))) / 256)); // The distance output by the TOF module
TOF_0.dis_status = rx_buf[11]; // Distance status indication output by TOF module
TOF_0.signal_strength = (unsigned int)(((unsigned int)rx_buf[13] << 8) | (unsigned int)rx_buf[12]); // The signal strength output by the TOF module
TOF_0.range_precision = rx_buf[14]; // The repeatability accuracy reference value output by the TOF module is invalid for Type C, Type D and Mini.
//Print data through the terminal
printf("TOF id is:%d\r\n", TOF_0.id);
printf("TOF system time is:%d ms\r\n", TOF_0.system_time);
printf("TOF distance is:%d mm\r\n", TOF_0.dis);
printf("TOF status is:%d\r\n", TOF_0.dis_status);
if (TOF_0.dis_status == 0)
printf("Measurement distance is invalid.\r\n");
else
printf("Measurement distance is valid.\r\n");
printf("TOF signal strength is:%d\r\n", TOF_0.signal_strength);
printf("TOF range precision is:%d\r\n\n", TOF_0.range_precision);
}
else
{
printf("Verification failed.\r\n");
}
check_sum = 0; // Clear Checksum
}
Auch das Auswerteprogramm auf dem RasPi wurde noch etwas angepasst, der Auswerteradius wurde auf 5000mm erweitert und statt einzelner Messpunkte stelle ich jetzt eine Grundrisslinie dar...
ACHTUNG: Die Programmierung ist ebenfalls ein Arbeitsstand, sie funktioniert grundlegend ist aber nicht gegen Fehlbedienung etc. abgesichert. Aber Ihr könnt ein "hängendes Programm" in der Lazarus-Oberfläche jederzeit mit "Halt" abbrechen...
hier die Lazarus-Dateien zum Download...

Was soll ich sagen - dies sieht doch schon SEHR VIEL BESSER AUS...
Probiert es doch einfach aus...