Über den Beitrag
In meiner Reihe über Licht-, Gestik-, Bewegungs- und Abstandssensoren, die ich mit einer Gesamtübersicht abschließen werde, möchte ich diesmal über den VL6180X bzw. das VL6180X Modul berichten. Manchmal stößt man auch auf die Bezeichnung VL6180, also ohne „X“. Dabei handelt sich aber immer um dasselbe Bauteil.
Zunächst gehe ich auf die Eigenschaften des eigentlichen VL6180X ein, dann auf die Module. Schließlich werde ich eine Bibliothek vorstellen, mit der man das Teil bequem ansteuern kann. Ich habe als Grundlage eine Bibliothek von Sparkfun genommen, die ich um Interruptfunktionen erweitert habe.
Grundlegendes
Das Messprinzip
Der VL6180X misst Abstände über die ToF (Time-of-Flight) Methode. Das soll übrigens auch das Beitragsbild andeuten. Der VL6180X benutzt dazu einen Infrarot Laser, dessen reflektierte Strahlen hinsichtlich ihrer Flugzeit ausgewertet werden um so den Abstand zu berechnen. Im Prinzip also ähnlich wie der bekannte Ultraschallsensor HC-SR04. Da wir beim VL6180X aber über Strahlung mit Lichtgeschwindigkeit sprechen, stellt sich natürlich die Frage, wie man mit einem kostengünstigen Bauteil so kurze Flugzeiten exakt messen kann – es ist schon erstaunlich, aber es geht. Wer sich für die Details der Methode interessiert, der kann hier noch einen Artikel dazu lesen oder sich hier auf Wikipedia schlau machen.
Darüber hinaus kann der VL6180X das Umgebungslicht (ALS) über eine Photodiode messen. Die Messwerte können dann in Lux-Werte umgerechnet werden.
Ihr bekommt die Module z.B. hier bei Amazon in einer recht großen Preisspanne. Die günstigsten Modelle liegen bei 6 – 7 Euro einschließlich Versand.
Wie immer empfehle ich auch einen Blick ins Datenblatt. Denn oftmals – und auch hier ist das der Fall – entdeckt man, dass Bauteile viel mehr können, als in Bibliotheken implementiert ist.
Eigenschaften des VL6180X
Der VL6180X hat sehr viele Einstellparameter. Ich werde hier nur die nach meiner Ansicht wichtigsten Parameter nennen. Viele der Parameter sind nicht als öffentliche Funktionen in der Sparkfun bzw. meiner modifizierten Bibliothek implementiert. Wollt ihr sie ändern, müsst ihr die voreingestellten Werte direkt in der Bibliothek modifizieren.
Reichweite und Messmodi
Laut Datenblatt misst der VL6180X Entfernungen millimetergenau bis 10 cm. Die Module, die ich ausprobiert habe, hatten eine Reichweite von knapp 20 cm. Das deckt sich mit Angaben aus anderen Quellen, die ich gefunden habe. Wenn ihr größere Reichweiten bis 200 cm benötigt, dann könnt ihr zu den „größeren Brüdern“ VL53L0X oder VL53L1X greifen. Diese werde ich in einem weiteren Beitrag behandeln. Die Kommunikation erfolgt mittels I2C.
Sowohl die Bestimmung des Lichts (ALS) wie auch des Abstandes (Range) kann als Einfachmessung (single shot) oder in einem kontinuierlichen Modus erfolgen. Der Hersteller empfiehlt, Licht- und Abstand parallel nicht kontinuierlich zu messen, sondern bei Bedarf auf den sogenannten interleaved Mode auszuweichen. Im interleaved Mode werden ALS- und Rangemessungen sequentiell in regelmäßigen Abständen durchgeführt. Messungen im kontinuierlichen und interleaved Mode bieten sich vor allem dann an, wenn man Licht oder Abstand mittels der Interruptfunktion kontrollieren möchte.
Bei der Abstandsmessung lässt sich die Messzeit (max convergence time) einstellen. Je weiter die Entfernung und je schlechter die Reflektion, desto länger braucht eine akkurate Messung. Das Datenblatt hat dazu Tabellen. Nach meiner Erfahrung fahrt ihr aber mit den Voreinstellungen schon sehr gut. Letzten Endes hängen die Anforderungen natürlich von eurer spezifischen Anwendung ab.
Lichtmessung
Auch bei der Lichtmessung könnt ihr die Messzeit (integration period) einstellen. Das ist ähnlich wie bei dem in meinem letzten Beitrag vorgestellten TSL2561. Der Default Wert ist 100 ms, was dem oberen Ende der lt. Datenblatt empfohlenen Range von 50 – 100 ms entspricht. Darüber hinaus könnt ihr bei Bedarf auch verschiedene Verstärkungsfaktoren einstellen (GAIN_x, siehe Beispielsketch).
Der History Buffer
Ein sogenannter History Buffer speichert, sofern die Funktion aktiviert wurde, die letzten acht Lichtmesswerte (16 Bit) oder die letzten sechzehn Abstandswerte (8 Bit). Der History Buffer kann ein- und ausgeschaltet, gelesen und manuell geleert werden. Den History Buffer habe ich für die Interruptfunktion verwendet. Ihr könnt die Daten aus dem History Buffer natürlich auch dazu benutzen um sehr einfache Gesten auszuwerten (Annäherung vs. Entfernung).
Cross Talk Compensation
Verbaut man den VL6180X hinter einer Glasscheibe, können die Messergebnisse dadurch verfälscht werden (Cross Talk). Man kann diesen Effekt aber kompensieren. Die Prozedur dazu ist im Datenblatt beschrieben. Diese Funktion ist allerdings nicht in der Bibliothek implementiert.
Interrupts
Es lassen sich Interrupts für die Licht- und Abstandsmessung einstellen. Dabei kann man ein unteres Limit, ein oberes Limit oder ein Fenster definieren. Darüber hinaus kann man für jede abgeschlossene Messung einen Interrupt auslösen lassen.
I2C Adresse
Die voreingestellte I2C Adresse des VL6180X ist 0x29. Ihr könnt die Adresse interessanterweise über entsprechende Registereinträge frei ändern. Üblicherweise funktioniert das ja über Adresspins. Die Änderungsfunktion changeAddress(alt,neu)
ist in der Bibliothek öffentlich zugänglich implementiert. Eigentlich sollte die neue Adresse fest gespeichert werden. Das Ändern klappte bei mir, allerdings „vergaß“ mein Modul seine neue Adresse wieder, wenn ich die Stromversorgung kappte. Schade. Einen I2C Adressscanner findet ihr hier, falls Ihr auch mit der Funktion herumspielen wollt. Vielleicht funktioniert es ja bei euren Modulen.
VL6180X Module
Ihr bekommt VL6180X Module in verschiedenen Formen und Größen, aber die meisten haben zumindest die gleichen Pins:
- VCC/GND: im Gegensatz zu den blanken VL6180X Bausteinen, die zwischen 2.7 und 2.9 Volt betrieben werden sollten, vertragen die meisten Module (nicht alle, bitte prüfen!) zwischen 2.8 und 5.5 Volt.
- SDA/SCL: I2C Leitungen, ggf. werden Pull-Up Widerstände benötigt. Einige Module, wie z. B. das abgebildete Modell, haben Pull-Ups integriert. Um sie zu aktivieren müsst ihr die Kontakte im Jumperbereich „PU EN“ verbinden.
- GPIO0 (oder „IO0“ oder nur „0“):
- lässt sich als „Ausschalter“ konfigurieren
- lässt sich als Interruptpin konfigurieren
- Einstellen der Signalpolarität (active-low / active-high)
- GPIO1 (oder „IO1“ oder „1“):
- lässt sich als Interruptpin konfigurieren
- Einstellen der Signalpolarität
Ansteuerung mit der (modifizierten) Sparkfun Bibliothek
Es gibt eine Reihe von Bibliotheken auf Github, z.B. von Sparkfun, Adafruit, Pololu und stm32duino. Letztere erscheint sehr vollständig, allerdings ist sie – jedenfalls für mich – recht schwer „verdaulich“, da auch keine Beispiele dabei sind. Ihr findet sie hier und könnt euch ja euer eigenes Bild machen.
Leider habe ich keine andere gefunden, die die Interruptfunktion implementiert hatte. Ich habe dann die Bibliothek von Sparkfun genommen und erweitert. Das Original findet ihr hier auf Github. Meine erweiterte Version VL6180X_WE mit Interruptfunktion bekommt ihr hier oder auch direkt über die Bibliotheksverwaltung der Arduino IDE.
Die Schaltung
Die hier abgebildete Schaltung funktioniert für beide Beispielsketche. Sie setzt voraus, dass das Modul 5 Volt verträgt und dass das Modul Pull-Up Widerstände für die I2C Leitungen bereitstellt.
Beispielsketch für Abstands- und Lichtmessung
Der erste Sketch zeigt euch wie einfache Licht- und Abstandsmessungen im single shot Modus (auch Polling Modus genannt) durchgeführt werden. Zunächst werden die benötigten Bibliotheken eingebunden, dann wird das VL6180X Objekt „sensor“ erzeugt.
Den Teil, der die Informationen über das Modul liefert, könnt ihr auch weglassen. Ich habe ihn vom originalen Sparkfun Beispielsketch übernommen. Die Funktionen VL6180xInit()
und VL6180xDefaultSettings()
nehmen Einstellungen vor, so wie vom Hersteller STMicroelectronics in dem Application Note Document AN4545 empfohlen werden. Die Liste mit den Einstellungen findet sich dort ab Seite 24 (für die Interessierten).
Die Lichtmessung nehmt ihr mit der Funktion getAmbientLight(
gain
)
vor. Dabei ist nur die Verstärkung öffentlich als Parameter zugänglich. Wollt ihr mehr ändern, müsst ihr das direkt in der Bibliothek tun. Abstände fragt ihr über die Funktion getDistance()
ab.
/****************************************************************************** * VL6180X_WE_demo.ino * Based on SparkFun's example * Only Changes: * - Baud rate changed to 9600 * - Name of the library * * Original library and examples: * https://github.com/sparkfun/SparkFun_ToF_Range_Finder-VL6180_Arduino_Library * *******************************************************************************/ #include <Wire.h> #include <VL6180X_WE.h> #define VL6180X_ADDRESS 0x29 VL6180xIdentification identification; VL6180x sensor(VL6180X_ADDRESS); void setup() { Serial.begin(9600); //Start Serial at 9600bps Wire.begin(); //Start I2C library delay(100); // delay .1s sensor.getIdentification(&identification); // Retrieve manufacture info from device memory printIdentification(&identification); // Helper function to print all the Module information if(sensor.VL6180xInit() != 0){ Serial.println("FAILED TO INITALIZE"); //Initialize device and check for errors } sensor.VL6180xDefaultSettings(); //Load default settings to get started. delay(100); // delay 0.1s } void loop() { //Get Ambient Light level and report in LUX Serial.print("Ambient Light Level (Lux) = "); //Input GAIN for light levels, // GAIN_20 // Actual ALS Gain of 20 // GAIN_10 // Actual ALS Gain of 10.32 // GAIN_5 // Actual ALS Gain of 5.21 // GAIN_2_5 // Actual ALS Gain of 2.60 // GAIN_1_67 // Actual ALS Gain of 1.72 // GAIN_1_25 // Actual ALS Gain of 1.28 // GAIN_1 // Actual ALS Gain of 1.01 // GAIN_40 // Actual ALS Gain of 40 Serial.println( sensor.getAmbientLight(GAIN_1) ); //Get Distance and report in mm Serial.print("Distance measured (mm) = "); Serial.println( sensor.getDistance() ); delay(500); }; void printIdentification(struct VL6180xIdentification *temp){ Serial.print("Model ID = "); Serial.println(temp->idModel); Serial.print("Model Rev = "); Serial.print(temp->idModelRevMajor); Serial.print("."); Serial.println(temp->idModelRevMinor); Serial.print("Module Rev = "); Serial.print(temp->idModuleRevMajor); Serial.print("."); Serial.println(temp->idModuleRevMinor); Serial.print("Manufacture Date = "); Serial.print((temp->idDate >> 3) & 0x001F); Serial.print("/"); Serial.print((temp->idDate >> 8) & 0x000F); Serial.print("/1"); Serial.print((temp->idDate >> 12) & 0x000F); Serial.print(" Phase: "); Serial.println(temp->idDate & 0x0007); Serial.print("Manufacture Time (s)= "); Serial.println(temp->idTime * 2); Serial.println(); Serial.println(); }
Beispielsketch für Interruptfunktion
Um Interrupts zu implementieren, habe ich der Bibliothek einige zusätzliche Funktionen spendieren müssen. In VL6180X_WE.h habe ich die neuen Funktionen hervorgehoben, falls jemanden die Details interessieren.
Abstandsinterrupt
Wenn ihr die Interruptfunktion für Abstände nutzen wollt, dann müsst ihr zunächst ein Fenster definieren, außerhalb dessen ein Interrupt ausgelöst wird. Die Funktion dazu heißt VL6180xSetDistInt(
lower limit, upper limit
)
. Die Limits gebt ihr in Millimetern an. Wenn ihr nur ein einzelnes Limit setzen wollt, dann setzt als das andere 0 oder 255, da Werte außerhalb nicht erreicht werden.
Dann müsst ihr die Abstandsmessung im kontinuierlichen Modus starten und zwar mit getDistanceContinously()
. Mit dieser Funktion könnt ihr auch zwischendurch Abstandswerte abfragen. Eine Abfrage mittels getDistance()
hingegen beendet den kontinuierlichen Modus.
Den Interruptpin GPIO1 habe ich active-low eingestellt. Im Falle eines Interrupts:
- gibt es eine „Interrupt!“ Meldung auf dem seriellen Monitor
- leuchtet die LED an Pin 13 für eine Sekunde
- liest der Sketch den letzten Wert aus dem History Buffer aus
Den Interrupt müsst ihr manuell löschen.
Es ist zu beachten, dass der VL6180X die Messungen trotz des Interrupts weiter fortsetzt. Zwischen dem Interruptereignis und dem Auslesen des History Buffers dürft ihr also nicht zu viel Zeit verstreichen lassen.
Die Aktivierung des History Buffers erfolgt im Hintergrund mit der Einstellung des kontinuierlichen Modus.
Umgebungslichtinterrupt
Die Interrupts für die Lichtmessung funktionieren im Prinzip wie Abstandsinterrupts. Zunächst kommentiert ihr die Zeilen 50, 51, 63 und 64 aus und entkommentiert deren Entsprechungen für die Lichtinterrupts. Wie schon oben beschrieben, solltet ihr immer nur Licht oder Abstand im kontinuierlichen Modus messen.
Ihr definiert nun ein Fenster in Lux-Werten jenseits derer ein Interrupt ausgelöst wird. Dann startet ihr den kontinuierlichen Modus für die Lichtmessung.
Beim Auslesen des History Buffers für die Lichtwerte müsst ihr auch wieder den Verstärkungsfaktor übergeben. Ihr müsst dafür Sorge tragen, dass ihr bei allen Funktionen denselben Verstärkungsfaktor übergebt.
Hinweis: wenn ihr von Licht- auf Abstandinterrupts „umschaltet“, also den Sketch verändert und neu hochladet, müsst ihr den VL6180X ggf. einmal vom Strom nehmen. Den genauen Grund kenne ich nicht, es scheint jedoch als ob er nicht immer sauber resettet.
/****************************************************************************** * Modified by Wolfgang (Wolle) Ewald * https://wolles-elektronikkiste.de/en/vl6180x-tof-proximity-and-ambient-light-sensor * * **************************************************************************** * Based on the Sparkfun library example for the VL6180X: * https://github.com/sparkfun/SparkFun_ToF_Range_Finder-VL6180_Arduino_Library * ******************************************************************************/ #include <Wire.h> #include <VL6180X_WE.h> #define VL6180X_ADDRESS 0x29 VL6180xIdentification identification; VL6180x sensor(VL6180X_ADDRESS); int interruptPin = 2; int ledPin = 13; volatile bool event = false; int gain; #if defined(ESP8266) || defined(ESP32) void IRAM_ATTR blink(){ #else void blink(){ #endif event = true; } void setup() { pinMode(ledPin, OUTPUT); pinMode(interruptPin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(interruptPin), blink, FALLING); Serial.begin(9600); Wire.begin(); //Start I2C library if(sensor.VL6180xInit() != 0){ Serial.println("FAILED TO INITALIZE"); //Initialize device and check for errors } sensor.VL6180xDefaultSettings(); //Load default settings to get started. delay(100); // delay 0.1s //Input GAIN for light levels, // GAIN_20 // Actual ALS Gain of 20 // GAIN_10 // Actual ALS Gain of 10.32 // GAIN_5 // Actual ALS Gain of 5.21 // GAIN_2_5 // Actual ALS Gain of 2.60 // GAIN_1_67 // Actual ALS Gain of 1.72 // GAIN_1_25 // Actual ALS Gain of 1.28 // GAIN_1 // Actual ALS Gain of 1.01 // GAIN_40 // ActualALS Gain of 40 /* Range Threshold Interrupt: * The interrupt is set up with VL6180xSetDistInt(low limit / high limit); * The interrupt is triggered if the measured distance value is OUTSIDE these * limits. Keep in mind that the VL6180x will return a distance of 255 if * nothing is in the measuring range. * Examples: * low limit = 50, high limit = 150 => interrupt is triggered at < 50 and > 150 * low limit = 50, high limit = 255 => interrupt is triggered at < 50 * low limit = 0, high limit = 50 => interrupts is triggered at > 50 */ sensor.VL6180xSetDistInt(50,150); sensor.getDistanceContinously(); // ALS Threshold Interrupt: // sensor.VL6180xSetALSInt(GAIN_1,30,200); // sensor.getAmbientLightContinously(GAIN_1); } void loop() { if(event){ Serial.println("Interrupt!"); // Serial.print("Last ALS Value: "); // Serial.println(sensor.getLastAmbientLightFromHistory(GAIN_1)); Serial.print("Last Distance Value: "); Serial.println(sensor.getLastDistanceFromHistory()); digitalWrite(ledPin, HIGH); delay(1000); digitalWrite(ledPin, LOW); event = false; sensor.VL6180xClearInterrupt(); } }
Danksagung
Die schicke Stoppuhr auf dem Beitragsbild stammt von Sadia auf Pixabay. Danke!
Danke auch an Sparkfun für die (Basis-) Bibliothek und für das Fritzing VL6180 Bauteil.
Hallo Gerd,
ich glaube nicht, dass Wasser Infrarot wirklich gut reflektiert, ausprobiert habe ich es aber nicht. Also die Antwort ist: vielleicht. Spritzwassergeschützt ist der VL6180X auch nicht. Ich hätte ja einen JSB-SR04T Sensor empfohlen, aber der passt nicht für den Bereich 0-20 cm.
Vielleicht kommt so etwas in Betracht:
https://www.amazon.de/F%C3%BCllstandssensor-Wasserstandssensor-F%C3%BCllstandsmessumformer-Wasserstandsmelder-Signalausgang/dp/B087QG667Y/ref=asc_df_B087QG667Y/?tag=bingshoppin0b-21&linkCode=df0&hvadid=&hvpos=&hvnetw=o&hvrand=&hvpone=&hvptwo=&hvqmt=e&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=&hvtargid=pla-4584207585422938&th=1
VG, Wolfgang
Hallo Wolfgang,
ich habe den Benewake TFminiPlus im Einsatz. Schönes Teil mit Reichweite bis zu 6m.
Das Problem mit „Wasser“ ist heimtückisch:
Der Sensor war 6 Monate anstandslos im Außeneinsatz-dann kam Nebel. Da ich keine Werte auslese sondern den Ausgang nur als Kontakt für nah/fern benutze, weiß ich nicht so ganz genau, was da passiert ist. Auf jeden Fall wurde eine geringere Entfernung gesehen als normal, was zum Auslösen des Kontaktes führte.
Gruß
Detlef
Vielen Dank für das Teilen deiner Erfahrungen. Hilft vielleicht anderen, die auch so ein Teil im Einsatz haben.
VG, Wolfgang
Hallo Wolfgang,
mal wieder ein sehr interessanter Bericht :-).
Denkst Du das der Sensor für mein Wohnmobil Projekt (Wasserstand Pegel) geeignet ist. Möchte gerne im Bereich 0-20 cm den Wasserspegel mit einem Sensor erfassen und mit kleinem Display ausgeben.
Der Tank besteht aus Kunststoff, aber das tut nichts zur Sache, denke ich.
Wäre schön wenn Du mir da Deine Gedanken mitteilen könntest.
vg
Gerd