fivdi / pigpio

Fast GPIO, PWM, servo control, state change notification and interrupt handling with Node.js on the Raspberry Pi
MIT License
946 stars 89 forks source link

HELP with Measure distance with a HC-SR04 ultrasonic sensor #47

Closed Guttata closed 6 years ago

Guttata commented 6 years ago

Hi, i'm sorry it is not a issue but more an help request. I am new to the NODE JS and javascript world. I try the code for the "Measure distance with a HC-SR04 ultrasonic sensor" and it's working. Thanks for that.

Nevertheless, i don't understand the syntax of this part of code: We declare an async anonymous function with a callback function inside the echo.on ?

(function () {
  var startTick;

  echo.on('alert', function (level, tick) {
    var endTick,
      diff;

    if (level == 1) {
      startTick = tick;
    } else {
      endTick = tick;
      diff = (endTick >> 0) - (startTick >> 0); // Unsigned 32 bit arithmetic
      console.log(diff / 2 / MICROSECDONDS_PER_CM);
    }
  });
}());

How can i retrieve the distance from outside this callback function and anonymous function ? A struggle few hours on this. Thanks a lot.

fivdi commented 6 years ago

I try the code for the "Measure distance with a HC-SR04 ultrasonic sensor" and it's working. Thanks for that.

You're welcome :smile:

We declare an async anonymous function with a callback function inside the echo.on ?

There are two anonymous functions in the code posted above. Neither of them is async. echo.on is however async. The second parameter passed to echo.on is a callback function. On the screen it may look like this callback function is "inside the echo.on" but this isn't the case.

How can i retrieve the distance from outside this callback function and anonymous function ?

Below is a modified variant of the example program form the readme. It implements an async function called measureDistance which expects a callback function as a parameter. When called, measureDistance will measure the distance and call the callback with the measured distance. I hope this makes things a little clearer.

var Gpio = require('../').Gpio,
  trigger = new Gpio(23, {mode: Gpio.OUTPUT}),
  echo = new Gpio(24, {mode: Gpio.INPUT, alert: true});

// The number of microseconds it takes sound to travel 1cm at 20 degrees celcius
var MICROSECDONDS_PER_CM = 1e6/34321;

trigger.digitalWrite(0); // Make sure trigger is low

function measureDistance(callback) {
  var startTick;

  function alertHandler(level, tick) {
    var endTick,
      diff;

    if (level == 1) {
      startTick = tick;
    } else {
      endTick = tick;
      diff = (endTick >> 0) - (startTick >> 0); // Unsigned 32 bit arithmetic
      callback(diff / 2 / MICROSECDONDS_PER_CM);
      echo.removeListener('alert', alertHandler);
    }
  }

  echo.on('alert', alertHandler);

  trigger.trigger(10, 1); // Set trigger high for 10 microseconds
}

// Trigger a distance measurement once per second
setInterval(function () {
  measureDistance(function (distance) {
    console.log(distance + 'cm');
  });
}, 1000);
Guttata commented 6 years ago

Hi fivdi,

Thanks for the quick reply, it clarifies things.

I managed to do what a want just before your reply... but i'm still in trouble with this new paradigm...ASYNC.

I'am building an autonomous car. I manage without problem to pilot it in manual mode (no sensor measurement). But i'am facing a wall with the autoPilot mode.

function Car () {
    //Motor engine instanciation
...

    //Ultrason sensor instanciation
    this.FrontUltrasonicSensor = new UltrasonicSensor (constants.FRONT_ULTRASONIC_SENSOR_ECHO_GPIO, constants.FRONT_ULTRASONIC_SENSOR_TRIG_GPIO);
    this.FrontRightUltrasonicSensor = new UltrasonicSensor (constants.FRONT_RIGHT_ULTRASONIC_SENSOR_ECHO_GPIO, constants.FRONT_RIGHT_ULTRASONIC_SENSOR_TRIG_GPIO);
    this.FrontLeftUltrasonicSensor = new UltrasonicSensor (constants.FRONT_LEFT_ULTRASONIC_SENSOR_ECHO_GPIO, constants.FRONT_LEFT_ULTRASONIC_SENSOR_TRIG_GPIO);
    this.BackUltrasonicSensor = new UltrasonicSensor (constants.BACK_ULTRASONIC_SENSOR_ECHO_GPIO, constants.BACK_ULTRASONIC_SENSOR_TRIG_GPIO);

    this.autoPilotOn = false;
}

function UltrasonicSensor (echoGpio, trigGpio){
    this.echo = new Gpio(echoGpio, {mode: Gpio.INPUT, alert: true});
    this.trigger = new Gpio(trigGpio, {mode: Gpio.OUTPUT});
    this.distance = 0;
}

I manage to take the sensor value with this function. It stores the distance value inside the distance attribute of the sensor object passed in param.

Car.prototype.measureDistance = function (sensor){
    sensor.trigger.digitalWrite(0); // Make sure trigger is low
    var startTick;
    var distance;
    // The number of microseconds it takes sound to travel 1cm at 20 degrees celcius
    var MICROSECDONDS_PER_CM = 1e6/34321;

    sensor.echo.on('alert', function (level, tick) {
        var endTick,
        diff;

        if (level == 1) {
            startTick = tick;
        } else {
            endTick = tick;
            diff = (endTick >> 0) - (startTick >> 0); // Unsigned 32 bit arithmetic
            distance = diff / 2 / MICROSECDONDS_PER_CM;
            sensor.distance = distance;
            console.log("sensor distance:", sensor.distance);
        }
    });

    sensor.intervalObj = setInterval(function(){
        sensor.trigger.trigger(10, 1); // Set trigger high for 10 microseconds
    }, 1000);
}

I'm calling this function this way in my WebSocket Server:

client.on('AUTO PILOT', () => {
        myCar.measureDistance(myCar.FrontUltrasonicSensor);
        myCar.measureDistance(myCar.FrontRightUltrasonicSensor);
        myCar.measureDistance(myCar.FrontLeftUltrasonicSensor);
        myCar.measureDistance(myCar.BackUltrasonicSensor);
        myCar.autoPilot();
    });

So that it starts asynchronous measurement, and then i call my autoPilot function.

Car.prototype.autoPilot = function (){
   this.autoPilot = true;
   while (autoPilotOn) {
            while (!frontObstacleDetected) {
                   this.moveUp();
            } 
           // ....
    }
}

PROBLEM: As soon as i enter inside the first While loop. It seams that my async measure stops.

I tried to remove the While loop and calling the function this way:

client.on('AUTO PILOT', () => {
        myCar.measureDistance(myCar.FrontUltrasonicSensor);
        myCar.measureDistance(myCar.FrontRightUltrasonicSensor);
        myCar.measureDistance(myCar.FrontLeftUltrasonicSensor);
        myCar.measureDistance(myCar.BackUltrasonicSensor);
        //myCar.autoPilot();
        myCar.intervalObj = setInterval(function(){myCar.autoPilot();}, 1000);
    });

First advantage i manage to stop the autoPilot on socket event by clearing the intervalObj but now with this i get a lot more distance measurement. As if new myCar.measureDistance were called in addition to the previous one. Maybe because i don't have the echo.removeListener in my code ?

Thanks to help a complete beginner in the complexity of the multiple async paradigm

Guttata commented 6 years ago

Typically, does this can work (in term of callback ...) ?

Car.prototype.measureDistance = function (sensor, callback) {
    sensor.trigger.digitalWrite(0); // Make sure trigger is low
    var startTick;
    var distance;
    // The number of microseconds it takes sound to travel 1cm at 20 degrees celcius
    var MICROSECDONDS_PER_CM = 1e6/34321;

    sensor.echo.on('alert', function (level, tick) {
        var endTick,
        diff;

        if (level == 1) {
            startTick = tick;
        } else {
            endTick = tick;
            diff = (endTick >> 0) - (startTick >> 0); // Unsigned 32 bit arithmetic
            distance = diff / 2 / MICROSECDONDS_PER_CM;
            sensor.distance = distance;
            calback();
        }
    });
    sensor.trigger.trigger(10, 1); // Set trigger high for 10 microseconds
}

Car.prototype.measureDistances = function(callback) {
    this.measureDistance(this.FrontUltrasonicSensor, callback);
    this.measureDistance(this.FrontRightUltrasonicSensor, callback);
    this.measureDistance(this.FrontLeftUltrasonicSensor, callback);
    this.measureDistance(this.BackUltrasonicSensor, callback);
}

Car.prototype.autoPilot= function() {
    this.autoPilotOn = true;
        if (!this.frontObstacle())
    {
        this.moveUp();
    } else {
        var direction = this.chooseDirection();
        switch direction:
            //code
    }
}

//websocket.js
client.on('STOP', () => {
    if (myCar.autoPilotOn) {
        console.log("STOP AUTOPILOT");      
        clearInterval(myCar.intervalObj);
                myCar.autoPilotOn = false;
    }
    myCar.stop();
});

client.on('AUTO PILOT', () => {
    myCar.intervalObj = setInterval(function () {
      myCar.measureDistances(function () {
        myCar.autoPilot();
      });
    }, 1000);
});

Thanks a lot again.

fivdi commented 6 years ago

Code like the following is no-go as it will block all async code from executing.

Car.prototype.autoPilot = function (){
   this.autoPilot = true;
   while (autoPilotOn) {
            while (!frontObstacleDetected) {
                   this.moveUp();
            } 
           // ....
    }
}

In Node.js there is only one thread that executes JavaScript code and it will get stuck in the (potentially infinite) while loop above. None of the async code will get a chance to execute.

Maybe because i don't have the echo.removeListener in my code ?

The echo.removeListener is essential.

Code like the following isn't a good idea:

    this.measureDistance(this.FrontUltrasonicSensor, callback);
    this.measureDistance(this.FrontRightUltrasonicSensor, callback);
    this.measureDistance(this.FrontLeftUltrasonicSensor, callback);
    this.measureDistance(this.BackUltrasonicSensor, callback);

This code will start a measurement on four ultrasonic sensors at the same time. The ultrasonic sound pulses from the different sensors are likely to interfere with each other and give incorrect readings. The readings will need to be performed one after another.

I'm afraid there are too many questions above that I don't fully understand. To be honest, you may be trying to achieve too much in one step initially. The first thing is to understand how is Node.js is supposed to work. What async code is hand how it works. Without grasping this it's going to be difficult to impossible to achieve what you would like to achieve.

I'm going to go ahead and close this issue as there are no to-dos for pigpio. Good luck with the autonomous car.

Guttata commented 6 years ago

Hi,

Thanks again for your help. I will test step by test.

For your information, the "echo.removeListener" is not mentioned inside the readme and the published example.

fivdi commented 6 years ago

the "echo.removeListener" is not mentioned inside the readme and the published example.

Yes, it isn't needed for the example in the readme. It's needed for the example here.