Trouble getting ultrasonic sensor to work how I want.

Hi guys
hope you are able to help I’m trying to get an ultrasonic sensor to work my outside lights so that if someone walks underneath it it turns the lights on for a set amount of time but if a car parks under it and stays there the light will stay on for a maximum amount of time then go off and wait till the distance goes above a set amount again before it would loop and turn the lights on again.

Here is my code:

// thank you to DroneBot Workshop for the newping library and parts of the code found in youtube and there web page.



#include <NewPing.h>

#define trigPin  10   // define trigger pin
#define echoPin     13   //define echo pin
#define MAX_DISTANCE 400   //set max distance for the sensor.


int bulb = 9; //Define the relay signal on pin 9 on the arduino (in my case using an led to test, in final version this will turn a relay on).
float distance;   //varablie for distance
float duration;   //variable for duration
float speedOfSound = 776.5; //Speed of sound in miles per hour when temp is 77 degrees.

int setDistance = 13; //varaible for detection distance (again this just for testing and will be changed when project finished).

const long interval = 5000;   // interval to keep lights on when set Distance has been tripped (will be about 25 seconds when finished project).
const long interval2 = 15000;   // interval to turn lights off even if the distance is below the set Distance (i want this incase a car is parked under the sensor so the lights dont stay on). 
unsigned long previousMillis = 0;        // will store last time LED was updated

NewPing sonar(trigPin, echoPin, MAX_DISTANCE);   //for the new ping library.
 
 
int iterations = 5;   //read the ping time a set amount of times for a more arcuate reading.
 


void setup()

{

  Serial.begin (9600); //Start the serial monitor

  pinMode(trigPin, OUTPUT); //set the trigpin to output

  pinMode(echoPin, INPUT); //set the echopin to input

  pinMode (bulb, OUTPUT); //set the bulb on pin 9 to output


}

void loop()
{

unsigned long currentMillis = millis();   //setting up the millis command


// all code below from DroneBot Workshop thank you again.
 
  duration = sonar.ping_median(iterations);
  
  // Determine distance from duration
  // Use 343 metres per second as speed of sound
  
  distance = (duration / 2) * 0.0343;
  
  // Send results to Serial Monitor
  Serial.print("Distance = ");
  if (distance >= 400 || distance <= 2) {
    Serial.println("Out of range");
  }
  else {
    Serial.print(distance);
    Serial.println(" cm");
      }


// this is my code

  if (distance <= setDistance) {  //if the distance is below the set distance turn relay (led for now) on.
    digitalWrite(bulb, HIGH);
  }
  else if (distance >= setDistance && currentMillis - previousMillis >= interval) { //if the distance is now above the set distance and the first inteval is reached (5 seconds for now) turn relay (led) off.
    previousMillis = currentMillis;
     digitalWrite(bulb, LOW);
  }
  else if (distance <= setDistance && currentMillis >= interval2 ){   // this is where i want it to count if the distance is still below the set distance to turn the led off untill the distance goes back to say above 20cm for now,
    previousMillis = currentMillis;                                   // but this is where im getting stuck. 
    digitalWrite(bulb, LOW);
  }



}

I’ve put as much comments as possible if there is any more information you need just ask.

I’ve spent a few days going over the blink without delay sketch and getting there understanding it but still not totally 100% understanding the millis() function.

Thanks Jay.

So what does this code actually do, and how is it different from what you want it to do?

Thanks for the reply. The code does constant measurements from the ultrasonic sensor and if a object comes within 13cm of the sensor it turns a led on then after 5 seconds turns it off again as long as the object has moved out of the way and the sensor reads above the 13cm set distance.

I want the led to light like it does for the 5 seconds but if the object stays in the way of the sensor for there to be a cut off time of a variable time and the led goes off anyways and doesn’t come back on till the sensor has measured past say 50cm so knows the object has moved out of the way.

Hope this makes sense.

Thanks Jay.

I have thrown my ultrasonic sensors away and replaced them with CJUL53LOX V2 laser sensors. Its an easy code for what you want. These sensors are very much more precise. Just go to manage libraries and search for the VL53L0X.h library. There are 2 easy to follow examples and several youtube videos that are easy to follow.

if sensor <=999, … then
output to your relay HIGH
and delay time on

you need a motion sensor, not a distance sensor. I use an ADNS 3080 Optical Flow Sensor. It looks for movement.

https://www.ebay.com/itm/Optical-Flow-Sensor-APM2-52-2-6-2-8-Multicopter-ADNS-3080-Detect-Level-Movement/352309147729?hash=item520742f851:g:Ip0AAOSw8LBar460

jay748:

  else if (distance >= setDistance && currentMillis - previousMillis >= interval) {

[…]

else if (distance <= setDistance && currentMillis >= interval2 ){

First line looks good, second line not. You don’t take the difference, and currentMillis will almost always ben greater than interval2.

Next you need to do state checking: record that the distance goes below a certain number, not that it is below that number. So keep track of what the distance was.

Limit the number of times you measure the distance, maybe a few times a second (use the millis() timer for that as well). It’s enough, and less risk of echos messing with your measurements.

TechnoMonkeys: you need a motion sensor, not a distance sensor. I use an ADNS 3080 Optical Flow Sensor. It looks for movement.

https://www.ebay.com/itm/Optical-Flow-Sensor-APM2-52-2-6-2-8-Multicopter-ADNS-3080-Detect-Level-Movement/352309147729?hash=item520742f851:g:Ip0AAOSw8LBar460

Thanks but dont want to detect all movement through the sensor only from a certain height so cats and other things can't turn the lights on only humans (or that's the plan).

wvmarle: First line looks good, second line not. You don't take the difference, and currentMillis will almost always ben greater than interval2.

Next you need to do state checking: record that the distance goes below a certain number, not that it is below that number. So keep track of what the distance was.

Limit the number of times you measure the distance, maybe a few times a second (use the millis() timer for that as well). It's enough, and less risk of echos messing with your measurements.

Ideal thats what im looking for will look into how to set the state up and go from there, thank you for your help.

I’ve now got this:

  if (distanceRead==1 && distance<=carDistance){
    
  }
  state=lastState;
  distanceRead=lastDistanceRead;

  if (distance <= setDistance) {  //if the distance is below the set distance turn relay (led for now) on.
    digitalWrite(bulb, HIGH);
    state = !state;
    distanceRead = 1;
  }
  else if (distance >= setDistance && currentMillis1 - startMillis1 >= interval) { //if the distance is now above the set distance and the first inteval is reached (5 seconds for now) turn relay (led) off.
    startMillis1 = currentMillis1;
    digitalWrite(bulb, LOW);
  }
  while (state == 1 && currentMillis2 - startMillis2 >= interval2){
    startMillis2 = currentMillis2;
    digitalWrite(bulb, LOW);
  }

this is now working how i want but is only turning the bulb off for a fraction of a second, how would i get it to stay off till the distance has reset, in my mind i need something in the top if statement but probably wrong, if someone can just point me in the right direction as want to learn how but dont want to just be given the code.

Thanks for all your help.
Jay.

Add a boolean variable that keeps track of this.

Switch on the lights based on distance and the state being false, and upon switching on the lights set it to true.

Then when the distance drops below a certain level again set that variable to false, so it's possible to switch on the lights again.

Thanks for that, I’ve been playing about and got it to work how i wanted but all the timings dont seem to be right so played about with that and now lost the plot :o

here is the code I have now I’ve added an lcd screen so I can see the millis and distance without having to open the serial monitor.

// thank you to DroneBot Workshop for the newping library and parts of the code found in youtube and there web page.
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);



int trigPin = 8;   // define trigger pin
int echoPin = 9;  //define echo pin
int bulb = 13;   //Define the relay signal on pin 9 on the arduino (in my case using an led to test, in final version this will turn a relay on).
int setDistance = 13;   //varaible for detection distance (again this just for testing and will be changed when project finished).
int carDistance = 20;   //varaible for detection distance (again this just for testing and will be changed when project finished).

float distance = 400;   //varablie for distance
float duration;   //variable for duration
float pingTime;   //variable for pingtime

const long interval = 5000;   // interval to keep lights on when set Distance has been tripped (will be about 25 seconds when finished project).
const long interval2 = 15000;   // interval to keep lights on when set Distance has been tripped (will be about 25 seconds when finished project).
const long check = 500;   //set a delay between distance checks

unsigned long startMillis = 0;   // set zero to be for first time throught the loop
unsigned long startMillis1 = 0;   // set zero to be for first time throught the loop
unsigned long startMillis2 = 0;   // set zero to be for first time throught the loop

boolean state = false;

void setup()

{
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  //Print a message to the LCD.
  //lcd.print("Distance");
    //delay(1000);

  Serial.begin (9600); //Start the serial monitor

  pinMode(trigPin, OUTPUT); //set the trigpin to output

  pinMode(echoPin, INPUT); //set the echopin to input

  pinMode (bulb, OUTPUT); //set the bulb on pin 9 to output


}

void loop(){
  
  unsigned long currentMillis = millis();   //setting up the millis command
  unsigned long currentMillis1 = millis();   //setting up the millis command
  unsigned long currentMillis2 = millis();   //setting up the millis command
  
if (currentMillis - startMillis >= check){
  startMillis = currentMillis;
  digitalWrite(trigPin, LOW); //Set trigger pin low
  delayMicroseconds(2000); //Let signal settle
  digitalWrite(trigPin, HIGH); //Set trigPin high
  delayMicroseconds(10); //Delay in high state
  digitalWrite(trigPin, LOW); //ping has now been sent

  pingTime = pulseIn(echoPin, HIGH);  //pingTime is presented in microceconds
  distance = (pingTime / 2) * 0.0343;
}
  lcd.setCursor(0, 1); //Set cursor to first column of second row
  lcd.print("                "); //Print blanks to clear the row
  lcd.setCursor(0, 1);  //Set Cursor again to first column of second row
  lcd.print(distance); //Print measured distance
  lcd.print(" cm's");  //Print your units.
  //delay(250); //pause to let things settle



  state=false;

  if (distance <= setDistance && state == false) {   //if the distance is below the set distance turn relay (led for now) on.
    digitalWrite(bulb, HIGH);
    state = !state;
  }
  else if (currentMillis1 - startMillis1 >= interval) {   //if the distance is now above the set distance and the first inteval is reached (5 seconds for now) turn relay (led) off.
    startMillis1 = currentMillis;   //did have this set to currentMillis1 but thought i would try this.
    digitalWrite(bulb, LOW);
    state = !state;
  }
  else if (state == true && currentMillis2 - startMillis2 >= interval2){
    startMillis2 = currentMillis;   //did have this set to currentMillis2 but thought i would try this.
    digitalWrite(bulb, LOW);
    state = !state;
    
  }
  if (state == true && distance >= carDistance){
    state = !state;
  }


  
  lcd.setCursor(1, 0); //Set cursor to first column of first row
  lcd.print("                "); //Print blanks to clear the row
  lcd.setCursor(1, 0);  //Set Cursor again to first column of first row
  lcd.print(state); //Print measured distance
  lcd.setCursor(8, 0);  //Set Cursor again to first column of first row
  lcd.print(millis()/1000); //Print measured distance
  if (state==1){
  lcd.setCursor(4, 0);  //Set Cursor again to first column of first row
  lcd.print(millis()/1000); //Print measured distance
  
  }
}

I’m still researching the boolean variable and getting my head around it but working night shift at the moment.

Thanks for the continued help.

Jay.

There’s no need for three currentMillis variables, as they’ll have the same value most of the time anyway. No need for this at all.

void lightsState;
uint32_t lightsSwitchOnTime;
const uint16_t setDistance = 13;

void loop() {
  digitalWrite(trigPin, LOW); //Set trigger pin low
  delayMicroseconds(2); //Let signal settle
  digitalWrite(trigPin, HIGH); //Set trigPin high
  delayMicroseconds(10); //Trigger needs to be 10 us or more.
  digitalWrite(trigPin, LOW); //Ping will be sent now.

  uint16_t pingTime = pulseIn(echoPin, HIGH, 30000);  //pingTime is presented in microceconds - time out after 30,000 us (= 5 meter distance, out of range).
  float distance = (pingTime / 2) * 0.0343;

  if (distance < setDistance) {
    if (lightsState == false) {
      lightsState = true;
      digitalWrite(bulb, HIGH); // Lights on.
      lightsSwitchOnTime = millis(); // Record when it happened.
    }
  }
  else {  // More distance again, allow light to trigger again, regardless of whether the current lit time was up already.
    lightsState = false; 
  }

  if (lightsSwitchOnTime - millis() > interval) { // If time's up,
    digitalWrite(bulb, LOW); // lights off.
  }
}

This should work better - untested, may contain typos.

Note that now repeated triggers (someone walks away, then comes back within setDistance from the sensor) during the light on, will reset the on time.

That’s brilliant thank you. I’m off to work again now so won’t get chance to try till tomorrow.

Can you just explain what a uint32_t is as I’ve not seen one before and would like to understand.

Thanks again Jay.

uint32_t = (u)nsigned (int)eger (32)-bit (t)ype aka unsigned long uint16_t aka unsigned int

I prefer to use the explicit types as int on ATmega is a 16-bit signed value, but on ESP8266 it's a 32-bit signed value. I've been bitten by that before. This way you can always be sure how many bits you use for that variable - 8, 16, 32, 64 or 128.

Ideal makes sense when you break it down. There is a lot to learn with coding in finding that much out lol and more than one way to do things but just patience and keep reading and I’m sure I will get to a ok standard.

Thanks again and I will let you know how it goes.

Cheers Jay.

Just to finish off this post and a big thank you to wvmarle, the code is working like a charm and exactly how I want it to.

I couldn’t get the code you sent me to work but it allowed me to think about it in a different way and now working great.

I’ve cleaned up the code and put comments on it all and here it is if it is any help to anyone else.

/* This code will run a ultrasonic sensor and when the distance is below a set distance it will turn a relay on, if the distance is then above the set distance 
it will turn the relay off after a set time.  If the object doesnt move out of the sensors path it will still shut the relay off after a set time and then wait
till the distance has exceded another set distance before turning the relay on again.*/ 

int trigPin = 8;   // define trigger pin.
int echoPin = 9;  //define echo pin.
int relay = 13;   //Define the relay signal on pin 13 on the arduino.
int set_Distance = 13;   //varaible for detection distance.
int constant_Object_Distance = 20;   //varaible for detection distance of a object in constant way of sensor.

float distance;   //variable for distance.
float duration;   //variable for duration.
float pingTime;   //variable for pingtime.

const long lights_On = 30;   // interval to keep lights on when set_Distance has been tripped.
const long lights_On_1 = 90;   // interval to keep lights on when set Distance has been tripped and object doesnt move out of set_distance.
const long check = 150;   //set a delay between distance checks.

unsigned long start_Millis;   // set zero to be for first time throught the loop.
unsigned long relay_On;   // variable to keep track of when relay was last switched on.
unsigned long relay_Off;   // Variable for keeping track of how long before the relay should be switched off even if set_distence still tripped.

boolean range;   // Variable for the range of the measurment (true/false).

void setup()

{
  Serial.begin (9600); //Start the serial monitor
  pinMode(trigPin, OUTPUT); //set the trigpin to output
  pinMode(echoPin, INPUT); //set the echopin to input
  pinMode (relay, OUTPUT); //set the bulb on pin 9 to output


}

void loop() {

  unsigned long current_Millis = millis();   //setting up the millis command

  if (current_Millis - start_Millis >= check) {   // Set up a timer to limit how many time the distance is checked per second.
    start_Millis = current_Millis;   // Change start_millis varaible to the current Millis.
    digitalWrite(trigPin, LOW); //Set trigger pin low
    delayMicroseconds(2000); //Let signal settle
    digitalWrite(trigPin, HIGH); //Set trigPin high
    delayMicroseconds(10); //Delay in high state
    digitalWrite(trigPin, LOW); //ping has now been sent

    pingTime = pulseIn(echoPin, HIGH);  //pingTime is presented in microceconds
    distance = (pingTime / 2) * 0.0343;   // Convert pint time into cm's.
  }
  if (distance >= 5000 || distance <= 3) {  // If the distance is higher than 5M or lower then 3cm,
    distance = 4000;   // Read the distance as 4M
  }

  if (distance < set_Distance) {   // See if the distance is lower than set_distance variable
    relay_On = millis();   // Record when the distance dropped below.
    if (range == false) {   // If the relay is off run this loop.
      range = true;   // Set the state variable to true.
      digitalWrite(relay, HIGH);   // Turn the relay on.
      relay_Off = millis();   // Record when the relsy was turned on.
    }
    else {
      range = false;   // If distance is now above set_distance range is false.
    }
  }

  if (range == false) {   // If range is equal to false run loop.
    if  (millis() - relay_On > lights_On * 1000) {   // If relay has been on for set time from lights_On variable:
      digitalWrite(relay, LOW);   // Turn relay off
    }
    else {
      range = true;   // If the relay is still on and the distance is below set_Distance range is true.
    }
  }


  if (range == true) {   // If the range is equal to true:
    if (millis() - relay_Off > lights_On_1 * 1000) {   // And the relay have been on for the lights_On_1 variable set time:
      digitalWrite(relay, LOW);   // Turn relay off.


    }
  }
  if (range == true) {   // If range is still equal to true.
    if (distance >= constant_Object_Distance) {   // Dont return a false range distance till it has gone hight than the constant_Object_Distance Variable.
      range = false;  
    }
  }
}

Thanks again for the help.

Jay.