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
 
Schaltung
Lösung mit Servomotor
Lösung mit Steppermotor
Fazit
Update 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...

Update November 2025 - da der neue Sensor erst mal nur UART kann, habe ich PIN 17 und 16 zusätzlich angebunden..

 

von unten gesehen (OK - etwas unübersichtlich...)

 

Update November 2025 - ich hab's mal aufgemalt...

 

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.

LIDAR - Version 1

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...)

Startbildschirm des Programmes ( nach Click auf den Button1 )
ESP aktiv ?nur für zusätzliche Kontrolle, beim Start wird der ESP automatisch gesucht....
ESP anbindenhier wird die externe Batch-Datei (s.o.) ausgeführt
Messung startenwechselt dann zu Messung beenden....
ESP trennenhier 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...

hier als Platine mit Sicherung, Steuerungs- und Motorkabel sowie herausgeführten MS1-MS3-Polen (Jumperbank)

 

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.

die Variante mit der Spiegelfliese...
die Variante mit der Halterung für den Rundspiegel

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....

Fuss und Halterung wie die Rundspiegelversion - nur ein neuer Rotationskopf für den 30mm Laserspiegel...

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...)

hier die Version mit 360 Grad-Rundsicht - (nach Click auf Button1)

 

ESP aktiv ?nur für zusätzliche Kontrolle, beim Start wird der ESP automatisch gesucht....
ESP anbindenhier wird die externe Batch-Datei (s.o.) ausgeführt
Messung startenwechselt dann zu Messung beenden....
ESP trennenhier 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...

Grafikausschnitt www. waveshare.com...

 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...

 zurück zur Startseite