Module VL6180x - Proximity Sensor

Posted on
  • Hi everyone, I am using this sensor for my project. I looked at the libraries in internet and it seems that there is nothing targeting this module, therefore I write it by my own, following the structures of the libraries of two modules of the same family (VL53L0X and VL53L1X). My code is available on GItHub: https://github.com/SalvoCas/EspruinoDocs­/blob/master/devices/VL6180X.js

    Right now, I am using puck.js as board, but later I will use the MDBT42Q board. I am able to use the sensor on Arduino, but it is not working using the puck, so I think the problem is on the code I wrote. Still, I could not figure the problem out, so I am asking if anyone knows where is the problem.

    I am having various issues. Firstly, the status of the sensor says that it goes on overflow, giving me 15 as error. Then, I tried to an if to control if the device is correctly read on the VL6180X function, but it is not.

    Thanks to everyone that will take a look at it!

  • Wed 2020.02.05

    @user109290, nicely written and tidy module Salvatore.

    While I'm not likely to provide sufficient insight to continue, for the others to assist, would you provide the links to the sensor datasheet(s), the Arduino tutorial and of course, the current version of Espruino. process.env

    A snippet of the calls made to your module, the errors and a simple wiring/hookup detail would be needed. Thanks

  • Hi Robin,

    Thank you for your kind reply. Here it is the link to the complete documentation regarding the VL6180X module. In particular, I focused my study on the general datasheet:
    https://www.st.com/resource/en/datasheet­/vl6180x.pdf
    and the technical notes about the range measurement: https://www.st.com/content/ccc/resource/­technical/document/application_note/d5/b­b/ec/94/7d/1e/40/a0/DM00122600.pdf/files­/DM00122600.pdf/jcr:content/translations­/en.DM00122600.pdf

    This is the main code I am currently using:

    var sensor1;
    var I2C1 = new I2C();

    function onTimer() {
    var l1 = sensor1.readRange();
    console.log("Sensor 1: "+ l1 +" mm");
    }

    function initHW(){
    digitalWrite(D31, 0);
    I2C1.setup({sda:D1, scl:D2, bitrate:400000});
    }

    function initSensor(){
    console.log('Initialazing sensor');
    digitalWrite(D31, 1);
    sensor1 = require("https://github.com/SalvoCas/EspruinoDocs­/blob/master/devices/VL6180X.js").connec­t(I2C1, {address:0x29});
    }

    function doStart(){
    initHW();
    initSensor();
    setTimeout(function() {}, 1);
    var status = sensor1.readRangeStatus();
    if(status == 0){

    setInterval(onTimer, 500);
    

    }
    else{

    console.log('Error number: '+ status);
    

    }
    }

    doStart();

    The error I am encountering is a overflow error, specified by the RangeStatus number 15.
    The espruino board I am using has the latest version on it.

    Thank you for the help!

  • Your issue is that all your code runs within the upload cycle and that messes most of the time with the actual upload and things just do not what they should do.

    Simplest thing, put your doStart() into the onInit(){} function. After upload, you enter onInit() in the console and your code will either just work straight from the start or reveal issues it has. For convenience while you get the code to get its act together and not having to type into the console for starting it after every upload, add this as a last line - which you remove for final upload for save():

    setTimeout(onInit,999); // while dev'ing; remove before upload for save()
    

    Adding the onInit(){...} gives you two birds w/ one stone:

    1. You get out of the bind that upload and execution bite/fight each other
    2. Your code will start automatically cleanly on each power cycle

    PS: JS is executed while uploaded. For the last thing you have doStart() which sets everything in motion before the upload has completed (except you have save on send checked in the IDE... which is not the point of (original) Espruino... it was first added to help to overcome Arduinism, and later, to increase memory saving by writing things directly into Flash and execute (most of) it from there.

  • Thr 2020.02.06

    A development conditional startup initialization solution is outlined in the following tutorial, along with the suggestion in post #4 @user109290

    Quick Start Best Practices - Proper way to call init() on start up

  • Thank you for your help! It's the first time I code for Espruino and I still need to get used to it. I had concern about the loop functions, since I thought that the way they work is quite different from the environment I used to work in (especially Arduino). I will try to change my code according to your suggestions, thank you!

  • Thank you! I will definitely look into it.

  • Thanks for the help. I changed my code in this way, but I still have the overflow error. I do not really know why there is still this error, since the module works perfectly fine with Arduino, whit a library having the same logic flow of the one I built for Espurino.

    var sensor1;
    var I2C1 = new I2C();
    
    function onTimer() {
      var l1 = sensor1.readRange();
      console.log("Sensor 1: "+ l1 +" mm");
    }
    
    function initHW(){
      digitalWrite(D31, 0);
      I2C1.setup({sda:D1, scl:D2, bitrate:400000});
    }
    
    function initSensor(){
      console.log('Initialazing sensor');
      digitalWrite(D31, 1);
      sensor1 = require("https://github.com/SalvoCas/Esp­ruinoDocs/blob/master/devices/VL6180X.js­").connect(I2C1);
    }
    
    function doStart(){
      initHW();
      initSensor();
      setTimeout(function() {}, 1);
      var status = sensor1.readRangeStatus();
      if(status == 0){
        setInterval(onTimer, 500);
      }
      else{
        console.log('Error number: '+ status);
      }
    }
    
    function onInit(){
      doStart();
    }
    
    setTimeout(onInit, 999);
    
  • @user109290

    are you talking about the

    • Error Value 15
    • Range overflow
    • Range value out of range. This occurs when the target is detected by the device but is placed at a high distance (> 200mm) resulting in internal variable overflow

    in Table 12. - Range error codes - as specified in datasheet https://www.st.com/resource/en/datasheet­/vl6180x.pdf V7 pages 27+28?

    If with same arrangement of object to detect works on different platform, I suspect some data is not correctly received / decoded or a different measurement is looked at rather than the expected one with the object in range.

  • Yes, I am talking about this error.

    I agree with you, but I could not find the error on the library I wrote. I will keep looking at it, thanks!

  • Took a quick look at the module, especially at lines beginning at https://github.com/SalvoCas/EspruinoDocs­/blob/master/devices/VL6180X.js#L147

    ...
    // Read range function
    VL6180X.prototype.readRange = function() {
      while (!(this.read8(C.VL6180X_REG_RESULT_RANGE­_STATUS) & 0x01));                // wait for device to be ready for range measurement
      this.write8(C.VL6180X_REG_SYSRANGE_START­, 0x01);                            // Start a range measurement
      while (!(this.read8(C.VL6180X_REG_RESULT_INTER­RUPT_STATUS_GPIO) & 0x04));        // Poll until bit 2 is set
      var range = this.read8(C.VL6180X_REG_RESULT_RANGE_VA­L);                          // read range in mm
      this.write8(C.VL6180X_REG_SYSTEM_INTERRU­PT_CLEAR, 0x07);                    // clear interrupt
      return range;
    };
    ...
    

    This - while / continuously polling in JS loop until condition met - is not flying well with Espruino... it is actually inhibiting Espruino to function properly. Espruino is event driven; think of JS code as interrupt service routines: they get invoked by a hardware event - pin state change or timer event - and have to finish - come to an end (or put into (another) timeout the continuation/completion) within reasonable time in order to release the (cpu) resource to be able to respond timely to the next event. (You can do 'logical while' / polling, but it has to be with a timeout.)

    You do not show any wiring info (nor what Espruino board you are using)... So I assume you do this - at this point in time - all in software... To get it done done all in software, use retries on timeouts, set overall timeout or maximum retries and make it with callback(s) or Promise.

    Since the sensor has an interrupt pin (GPIO1), you can simplify your code significantly and lighten the cpu work by connecting that pin to an Espruino pin and watch it by the module as necessary.

  • @user109290

    An implementation w/ retries w/ timeouts and callback

    ...not the most elegant solution, but it works... (logically... have no way to test since I do not have device at hand).

    Using Promises may yield more elegant, concise code.

    Number of tries and timeoutTimes are just sample values... Consult datasheet for reasonable values. Currently, they are inline hard coded, but they can be put into the C-onstants as well. I assuem that the correct status check registers and respective check value are used to determine readiness and completenes and retry condition.

    // Read range function - expects callback with two (2) args:
    // - err: error code of module: 32 not ready for | 64 not completed measurement
    // - val: range in mm if module err == 0 else device error code (see Table 12)
    
    VL6180X.prototype.readRange = function(cb) {
      var rc = this._readRangeRC.bind(this); // ready check as funct w/ obj context
      rc(5,10,rc,cb);        // 5 tries, each retry 10ms (rcTimeoutTime) deferred
    };
    VL6180X.prototype._readRangeRC(triesLeft­,rcTimeoutTime,rc,cb) {
      // ready check for measurement - and start measurement
      var s = this.read8(C.VL6180X_REG_RESULT_RANGE_ST­ATUS);
      if (s & 0x01) {        // ready for measurement
        this.write8(C.VL6180X_REG_SYSRANGE_START­, 0x01); // start measurement
        var cc = this._readRangeCC.bind(this); // check completion as f w/ obj ctx
        cc(6,12,cc,cb);      // 6 tries, each retry 12ms (ccTimeoutTime) deferred
      } else {               // not ready yet for measurement
        if (--triesLeft>0) { // retry ready check delayed/deferred
           setTimeout(rc,rcTimeoutTime,triesLeft,rc­TimeoutTime,rc,cb); // retry
        } else {
           cb(32,s); // err in module = 32 / 0x20: not ready within... 
        }            // ...tries-1 x rcTimeoutTime, device err code in val;
      }              // alternative: cb(32|s); // combined err and undefined for val
    };
    VL6180X.prototype._readRangeCC(triesLeft­,ccTimeoutTime,cc,cb) {
      // completion check for measurement - and read measurement and clear interrupt
      var s = this.read8(C.VL6180X_REG_RESULT_INTERRUP­T_STATUS_GPIO);
      if (s & 0x04) {        // completed measurement
        var range = this.read8(C.VL6180X_REG_RESULT_RANGE_VA­L); // read range in mm
        this.write8(C.VL6180X_REG_SYSTEM_INTERRU­PT_CLEAR, 0x07); // clear interrupt
        cb(0,range);         // module and device err = 0, measured range in val
      } else {               // not completed measurement yet
        if (--triesLeft>0) { // retry completion check delayed/deferred
           setTimeout(cc,ccTimeoutTime,triesLeft,cc­TimeoutTime,cc,cb); // retry
        } else {
           cb(64,s); // err in module = 64 / 0x40: not completed within...
        }            // ...tries-1 x ccTimeoutTime, device err code in val;
      }              // alternative: cb(64|s); // combined err and undefined for val
    };
    

    and usage in doStart():

    var sensor1IID; // clearInterval(sensor1IID) stops sensor1 measuring
    function doStart(){
      initHW();
      initSensor();
      setTimeout(function(){                   // initial delay/defer
        sensor1IID = setInterval(function(){   // every 500ms get range w/...
          sensor1.readRange(function(err,val){ // ...callback, logging...
            if (err) {                         // ...error - if there is -...
              console.log("ModuleError:", err, " - DeviceError:", val);
            } else {                           // ...and range else
              console.log("Sensor 1:",val,"mm");
            }
          })
        },500);
      },1);
    }
    

    The individual anonymous functions in setTimeout() and setInterval() can be put into named functions and just referred by by name.

    You can drop onTimer(){...} or implement it differently.

    Btw, your initial setTimeout(function() {}, 1); in doStart(){...} did nothing. It does not work as a delay of 1ms as you thought it would. It puts just an empty function in the list of timed things to do / to do after 1 ms. But the code flow keeps going... so no real controlled delay. You have to put the code that should be executed a ms later INTO the timeout function body to have that effect; but then it becomes async and you have to use callback / passed function).

    Setting up ready check and completion check as functions bound to object makes them easy to use in the timeout. Alternative solution is to pass this - the object context as _ parameter. With other 'trickery' applied, the code looks then like:

    // Read range function - expects callback w/ 2 mandatory and 1 optional args:
    // - err: error code of module: 32 not ready for | 64 not completed measurement
    // - val: range in mm if module err == 0 else device error code (see Table 12)
    // - _: (optional) this / context, useful to get evtl more details from sensor
    
    VL6180X.prototype.readRange = function(cb) {
      this._readRangeRCS(this,5,10,cb); };         // 5 tries, defer retries 10ms
    VL6180X.prototype._readRangeRCS(_,triesL­eft,rcTimeoutTime,cb,s) { // rdyC+strt
      if ((s=_.read8(C.VL6180X_REG_RESULT_RANGE_S­TATUS))&1) { // ready f/ measure
        _.write8(C.VL6180X_REG_SYSRANGE_START,1)­;  // start measurement
        _._readRangeCCR(_._readRangeCCR,6,12,cb)­;  // 6 tries, defer retries 12ms
      } else if (--triesLeft>0) { // not ready yet: retry ready check deferred
        setTimeout(_._readRangeRCS,rcTimeoutTime­,_,triesLeft,rcTimeoutTime,cb);
      } else { cb(32,s,_); } };   // err in module = 32 / 0x20: not ready f/ m/t
    VL6180X.prototype._readRangeCCR(_,triesL­eft,ccTimeoutTime,cb,s) { // complC+rd
      if ((s=_.read8(C.VL6180X_REG_RESULT_INTERRU­PT_STATUS_GPIO))&4) { // complete
        var range = _.read8(C.VL6180X_REG_RESULT_RANGE_VAL);­  // read range in mm
        _.write8(C.VL6180X_REG_SYSTEM_INTERRUPT_­CLEAR, 0x07); // clear interrupt
        cb(0,range,_);            // module & device err=0, measured range in val
      } else if (--triesLeft>0) { // not completed yet: retry compl/n chk deferred
        setTimeout(_._readRangeCCR,ccTimeoutTime­,_,triesLeft,ccTimeoutTime,cb);
      } else { cb(64,s,_); } };   // err in module = 64 / 0x40: not completed m/t
    
  • Tue 2020.02.11

    ref #11 'You do not show any wiring info (nor what Espruino board you are using)'

    To @allObjects, was mentioned in 2nd pp. post #1

    'Right now, I am using puck.js as board, but later I will use the MDBT42Q board'

    but, I too also notice the anomalies in code block from post #8


    Salvatore, @user109290 we asked back in post #2 for a bit of info. Would you please enter process.env in the Left-Hand console side of the WebIDE and post the results please. This will provide loads of detail, a quick snapshot overview, for us to provide the best solution for your project.


    ref post #10 'Yes, I am talking about this error.'

    Which of the three errors was this comment in reference to? Three were detailed in post #9



    Salvatore, @allObjects has spent a considerable amount of time fleshing out a solution which while entertaining at times, does become a big time sink. From the data sheet provided, while there may be a breakout board variant, I'm not easily grasping the wiring to the devices that have been specified either. Lets make sure we are all on the same page, okay? process.env first and then an image of the wiring would be nice.

  • @Robin, somehow noticed only Arduino... even though in same line / phrase PuckJS and MDBT42Q show. @user109290, sorry for that.

    @Robin, regarding the error: it's the 3 aspects of it: error value, name and description in bullet list.

    @user109290, what is the power supply setup for the sensor?

    I'm looking forward to the feedback about success / suitability of my solution examples.

  • Thank you so much! I really appreciate your help. In someway I made it working using the previous code I showed you, but it is quite unstable and it still yields to errors after a while. In the next days I will implement a code using your logic. I actually had no experience using promises, but I tried to study them yesterday, I will go into more details and try to implement a version of the code using this approach. Thank you again!

  • HI Robin, sorry for my lack in information. Right now I am using the breakout board of the module VL6180X by Adafruit (https://www.adafruit.com/product/3316). I am using the puck.js board, the wiring is as follow:

    • SDA to D1
    • SCL to D2
      -GPIO0 to D31
      -Power to 3V
      -Ground to GND
      They are directly connected, since the breakout board should allow it. The wiring seems to be correct, since I made it working and I checked them using the multimeter.

    The result of the command process.env is:

    >process.env
    ={
      "VERSION": "1v99",
      "GIT_COMMIT": "f0d66ba",
      "BOARD": "PUCKJS",
      "FLASH": 524288, "RAM": 65536,
      "SERIAL": "6ddd0f13-64ec2540",
      "CONSOLE": "Bluetooth",
      "MODULES": "Flash,Storage,net" ... "S,crypto,neopixel",
      "EXPTR": 536882352 }
    

    I believe that the hints given by allObjects can lead me to write a right code. It is my first project using the Espruino IDE and I am totally new to JavaScript, therefore I wrongly approached the problem as I was working with Arduino or any C-based processor.
    Thank you for your help and I again apologize if I have been shallow in my previous posts.

  • Hi allObject, I worked on it today and I made it working using your logic. I changed something on the code, but nothing relevant. I needed a bit of troubleshooting, but your help was essential. Thank you very much!

    I attach the final version of both library and main code, it may be helpful for someone else.

    var sensor1;
    var I2C1 = new I2C();
    var sensor1IID;
    
    function initHW(){
      digitalWrite(D31, 0);
      I2C1.setup({sda:D1, scl:D2, bitrate:400000});
    }
    
    function initSensor(){
      console.log('Initialazing sensor');
      digitalWrite(D31, 1);
      sensor1 = require("https://github.com/SalvoCas/Esp­ruinoDocs/blob/master/devices/VL6180X.js­").connect(I2C1, {address:0x29});
    }
    
    function doStart(){
      initHW();
      initHW();
      initSensor();
      var status = sensor1.readRangeStatus();
      if (status==0){
      setTimeout(function(){
        sensor1IID = setInterval(function(){
          sensor1.readRange(function(err,val){
            if(err){
              console.log("ModuleError:", err, " - DeviceError:", val);
            }
            else{
              console.log("Sensor 1: ",val,"mm");
            }
          }); },200);
        },1);
      }
      else{
        console.log("Err status: ", status);
      }
    }
    function onInit(){
      doStart();
    }
    
    setTimeout(onInit, 999);
    

    Library: https://github.com/SalvoCas/EspruinoDocs­/blob/master/devices/VL6180X.js

  • Wondering why you initialize the HW twice...

    Also looked at your coding and formatting stile in the module...

    • Long names / identifiers that do not get and cannot get (easily) minified (or for constants to get folded in), are a burden: they use memory / variables in Espruino (because interpretation from source code), therefore the shorter the better. Therefore, I would refactor/rename them.
    • Spacing related to (reserved) key words and parenthesis and curly braces feels odd to me as a compiler / parser guy... I know it works, but it is like reading text and making the breaks and taking breaths off-interpunctions...

    Last but not least, I'd take a look at the try counts and defer times: timeout times rcTimeoutTime and ccTimeoutTime (I'd like to see them anyway to be part of the C-onstants and not hard coded... ;/...). Make them match to hour intended measuring interval / prevent time overlapping. Worst case - or if you really want to get tight - you make an open interval constructed with comparing actual time and a defer with a timeout if needed to not measure more frequently that your intend maximum, but give time to complete a measurement in case it takes longer. Such a construct (self calling - by timeout -method/function as in the rc and cc defers in the module) you can use then also for firing right again a measurement after a failing one.

    All depends though on your context where you want to use the sensor. I assume you did this work not just for the sensors' sake.

    NB: Don't forget to remove the convenient setTimeout(onInit,... before you upload for saving (the code)

  • Wed 2020.02.12

    @user109290, in post #16, snippet L3, I notice a somewhat older version of Espruino. As you are well into module writing, one might benefit with newer ES6 concepts.

    http://www.espruino.com/Features

    While there was no need to, it is I that must also apologize for being nit-picky. My request for the detail was only as the sequence of needed info wasn't appearing timely, (I'm sure from your end, you were focused on gettin' it done) and things were starting to smell a bit rotten. This concern arose from a user that also appeared a bit sketchy from a post around a year ago, and both @allObjects and I spent hours, I know I spent twenty, and I'm sure allObjects had triple that, cranking out code snippets for every exception that popped up, then the user just disappeared!! Not a total loss however, as despite all that, allObjects soldiered on completing his new concept for a game solution for his grandkids! Even shot some videos:

    http://forum.espruino.com/comments/14641­317/

    Both of us have learned to now be more cautious, and a few items here seemed to me that we were going down that same path to no resolution.


    'final version of both library and main code, it may be helpful for someone else'

    Yes indeed, but even though this content is within the 'Projects' forum heading, it may get missed, when the inquisitive are searching from the main site.



    Salvatore, you have here an exceptional work that would benefit others. Once I saw the video, post #16, I now see the excitement you had, wanting to make this work. I applaud you!

    I know this would take yet another day of effort, but please do consider memoralizing your effort with a landing page, with your name in blazing glory, accessible from the main site,
    something like:

    MPU6050 accelerometer and gyro

    As I wrote a week ago back in post #2, 'nicely written and tidy module' and indeed exceptional at that.

    http://www.espruino.com/Writing+Modules

  • Hi Robin, thank you again for your help. I completely understand you, there is no need to apologize. I will try to make a landing page over the weekend.

  • Thank you for the hints. I will keep to work on it in the next weeks, since it is part of my research project at university. I will try to follow your advice. HW initializing twice is an error, I just copied it twice. Thank you again for your efforts!

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Module VL6180x - Proximity Sensor

Posted by Avatar for user109290 @user109290

Actions