Need newbie help PLEASE - Ultrasonic Sensor

Hello

i’m very new to Arduino and am trying to achieve a simple circuit whereby an ultrasonic sensor controls the brightness of an LED light.

I have been TRAWLING the internet for this very simple tutorial and have had no luck. I’m not inept with technology and the internet, I just have only found people who have set up the circuit but need help with the coding or the final stages or whatever.

Arduino has become incredibly infuriating for me because I know this is such a simple setup and I just can’t achieve it. I’m sorry if this has been asked and answered before but any previous posts i’ve found have not been helpful or i just havent been able to understand.

I have set up the arduino so that the ultrasonic sensor can measure how far away objects are.

The board is an Arduino Uno.
The sensor is a HC-SR04 ultrasonic sensor

Pic is the setup

Can you do the things separately?

I mean:
Can you make a sketch to control the brightness of a LED?
Can you make a sketch to read an ultrasonic sensor?

Once you can do what Rob suggests, you could get the US range reading, convert to 0-255 using map(), then use that value to control the LED brightness with PWM.
It'll be good practice if you do it yourself. Don't just look for someone else's code that does exactly what you want, or you'll learn very little.

i guess i'll have to learn (i've never written any form of code before so wish me luck)

im only after simple instructions and someone else's code because it's for an art project and the deadline is approaching fast

_buchan:
i guess i’ll have to learn (i’ve never written any form of code before so wish me luck)
im only after simple instructions and someone else’s code because it’s for an art project and the deadline is approaching fast

Taking an ultrasonic range reading and generating PWM from a pin are both very simple concepts, and there are examples of both provided with the IDE. The “Ping” sketch for ultrasonic ranging, (>Examples >Sensors >Ping) and the “Fading” sketch for controlling LED brightness, (>Examples >Analog >Fading).
Just check out those examples, then have a shot at it. If you run into trouble, post your attempt at the code here, and we’ll help you sort out the bugs to get it working.

You could maybe determine maximum and minimum ranges that you want the US range to affect the LED, set up an ‘if’ statement to reject values outside that range, then use ‘map()’ to convert values within that range to 0-255 for the PWM.

For instance, if you were to select the range of 10cm to 100cm, you could use:-

// Do ultrasonic range procedure here, then:-
if(rangeIncm >= 10 && rangeIncm <= 100)
{
    pwmVal = map(rangeIncm, 10,100,255,0);  // Convert 10-100 to 255-0.
    analogWrite(pwmPin, pwmVal);            // Generate PWM.
}

With those values, the LED would be at full brightness at 10cm, just off at 100cm, or somewhere in between.
(Similar thing for inches.)

Edit: Aside from the actual ranging, and selecting the values, I’ve just about written it for you. :smiley:

thank you, i have a code for measuring distance with the sensor, now I need to work out what your code actually means so then i can combine them (????)

Also, even if i did copy and paste your code underneath the measuring distance code, surely it wouldn't work because I havent defined which pin my LED light is going into?

_buchan:
thank you, i have a code for measuring distance with the sensor, now I need to work out what your code actually means so then i can combine them (???)

Also, even if i did copy and paste your code underneath the measuring distance code, surely it wouldn’t work because I havent defined which pin my LED light is going into?

I only intended to show you a possible method, not to write the whole thing. Just use what I wrote as a guide.
You need to declare the variables and pins, using names of your choice. Also, you’ll need to set up the range to suit your preference, and even the units. ie cm or inches.

if(rangeIncm >= 10 && rangeIncm <= 100)

This means “if the range is larger than or equal to 10cm AND smaller than or equal to 100cm”

For further info, look up ‘map()’ and ‘analogWrite()’ in the Arduino reference. ( >Help >Reference )

I think I’ve given you enough to get you going. The rest shouldn’t take very long.

Edit: And the LED would be connected to ‘pwmPin’. You could name it “ledPin” or similar maybe. :slight_smile:
And don’t forget to use a current-limiting resistor in series with the LED.

okay, so this is my code

const int trig = 10;
const int echo = 11;
#define LED 9
long duration, inches, cm;

void setup() { 
  pinMode(trig, OUTPUT);
  pinMode(echo, INPUT);
  pinMode(LED, OUTPUT);

  Serial.begin(9600);
}

void loop() 
{  
  // establish variables for duration of the ping, 
  // and the distance result in inches and centimeters:
  long duration, inches, cm;
  
  digitalWrite(trig, LOW);
  delayMicroseconds(2);

  digitalWrite(trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(trig, LOW);

  duration = pulseIn(echo, HIGH);
  
 // convert the time into a distance
  inches = microsecondsToInches(duration);
  cm = microsecondsToCentimeters(duration);
  
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();

  
  analogWrite(LED, duration);
  

  delay(100);

  if(cm >= 10 && cm <= 100)
{
    duration = map(cm, 10,100,255,0);  // Convert 10-100 to 255-0.
    analogWrite(LED, duration);            // Generate PWM.
}
}

long microsecondsToInches(long microseconds)
{
  return microseconds / 74 / 2;
}

long microsecondsToCentimeters(long microseconds)
{
  return microseconds / 29 / 2;
}

what i desired was for the measurements to be displayed on the serial monitor as well as changing the intensity of the LED light (the closer an object is, the more intense the light is)

However, with this code, the LED light just constantly fluctuates in regards to intensity, and doesn’t really change depending on how close an object is.

also, the readings from the sensor seem inaccurate at times. as i was testing the distance measurements as i walked closer to the sensor, it would sometimes read measurements like “942in, 2404cm”. I have tried a different HC-SR04 sensor and the same occurs. Is this normal for the sensor, and would there be any way to make the readings more accurate?

Further down the line (if i can get accurate measurement readings), I would like multiple sensors to detect humans, and the LED light would change depending on whichever sensor reads the closest measurement, but i guess we’ll cross that bridge when we come to it… :smiley:

Help sorting out the code above is the main priority atm.

You’re getting there…
You didn’t need to add an extra ‘analogWrite()’, directly writing the duration to the PWM pin. It makes the following conditional useless, as well as writing an out-of-range value to the PWM pin.

Think about exactly what’s happening -
1. You do a ping. (OK so far)
2. You measure the pulse. (Still OK)
3. You convert it to inches and cm. (OK, but one or the other would be better)
4. You spend 10 to 20 mS doing serial prints. (Serial prints are OK, but they do take 10-20mS at 9600 baud, so not now.)
5. You write the duration in microseconds directly to the PWM, (LED) pin. (This is bad, especially since that value is likely to be in the thousands, whereas a PWM pin expects a value between 0 and 255. A random value is being written when you don’t want to write anything at all to the PWM pin yet.)
6. Now you wait for 100mS before determining if the target is within range. (If the target is moving, during this total period of 110 to 120mS it will have moved closer or further away, so the LED brightness won’t end up corresponding exactly to the distance. Since the most important things are the operation of the ping sensor and the LED, give those operations priority for best results, and do them one immediately after the other.)
7. Finally, you now do the conditional which in turn determines whether or not you will even set a PWM value, and what that value will be (after mapping to 0-255). (Except that you’ve already written a wrong value to the PWM (LED) pin 100mS ago, so you’re seeing a blend of the right value and the wrong value in the LED brightness, flickering at a bit under 10Hz.)

While it doesn’t hurt, it’s probably a better idea to use a separate variable for the LED brightness, too, rather than ‘duration’. If nothing else, then it can be given a more meaningful name, like “brightness”. :slight_smile:

Generally, when programming, it’s not a good idea to use ‘delay()’, either. It’s far better to use ‘millis()’-based timing. There’s a good example of that basic method in the “Blink” example. For now, though, I’ll leave it in, but move it to a more approriate place.

Another point - do you really need the measurement in both inches and centimeters? It’s better to just work with one unit - whatever you usually use in your country. (Not a necessity, just a suggestion. :slight_smile: )

Anyway, having said all of that, (just to help you understand - I’m not having a shot at you), here’s a re-arranged and repaired version of the code. It’s 4am and I’m tired but couldn’t sleep, so hopefully I got it right.)

Oh, I just noticed. You declared these twice, both globally at the top and locally in the ‘loop()’ function.:-

long duration, inches, cm;

Once, in ‘loop()’, is enough, and I changed ‘cm’ and ‘inches’ to int, since they won’t be big enough to need a long.

Anyway, see how this goes:-

const byte led = 9;
const byte trig = 10;
const byte echo = 11;

void setup()
{
    pinMode(trig, OUTPUT);
    pinMode(echo, INPUT);
    pinMode(led, OUTPUT);
    Serial.begin(9600);
}

void loop()
{
    digitalWrite(trig, LOW);
    delayMicroseconds(2);
    digitalWrite(trig, HIGH);                       // Generate a 'ping'.
    delayMicroseconds(10);                          //     "    "    "
    digitalWrite(trig, LOW);                        //     "    "    "
    long duration = pulseIn(echo, HIGH);            // Measure the echo time.

    // Convert the time into a distance:-
    int inches = microsecondsToInches(duration);
    int cm = microsecondsToCentimeters(duration);
    if (cm >= 10 && cm <= 100)                      // Check if within range. (About 4 inches to 3 feet)
    {
        byte brightness = (byte)map(cm, 10, 100, 255, 0); // Convert 10-100 to 255-0.
        analogWrite(led, brightness);               // Generate PWM.
    }
    // Print the results:-
    Serial.print(inches);
    Serial.print("in, ");
    Serial.print(cm);
    Serial.print("cm");
    Serial.println();
    delay(100);                                     // Delay between readings.
}

int microsecondsToInches(long microseconds)
{
    return (int)microseconds / 74 / 2;
}

int microsecondsToCentimeters(long microseconds)
{
    return (int)microseconds / 29 / 2;
}

Now back to bed for me. :slight_smile:

Sorry, I forgot to address the ‘ping’ sensor’s inaccuracy.

Rather than the way you’re currently doing the ranging, take a look at the “NewPing” library. (Google “NewPing”)
It’s much nicer to use, will false-trigger less, and has a ‘ping_median()’ method that takes an average of several readings, default 5.

Edit: It also has methods to directly return the distance in cm or inches, to save you the trouble of doing the conversion(s) yourself. (But they don’t work with ‘ping_median()’. They’re just other alternatives.)

I appreciate you tidying up and re-writing the code for me so much, thank you. However, when i tested it out, the LED initially was off, but as I put an object in front of the sensor, the light turned on and stayed on, never dimming or anything.

Also, I looked at NewPing and found arduino’s example code for a sensor (Sample NewPing Sketch). I uploaded the code and the results are in the screenshot. :frowning:

This is some code I found including median, however, there is no “ping_median()” written in the code so i don’t think it’s correct. Plus, I got a compiling error when verifying the code anyway.

#include <newping.h>
#include <runningmedian.h>

//Define the pins for the ultrasonic sensor's echo and trigger pins 
#define DISTANCE_ECHO 11
#define DISTANCE_TRIGGER 10
#define MAX_ULTRASONIC_DISTANCE 200

NewPing sonar(DISTANCE_TRIGGER, DISTANCE_ECHO, MAX_ULTRASONIC_DISTANCE);

void setup()
{
    //Initialize serial communication to send the distance value  to the serial monitor
    Serial.begin(9600);
}

void loop()
{
  int distance_cm;
  distance_cm = getDistanceFromUltrasonicSensor();
  
  Serial.print(distance_cm);
  Serial.print("cm");
  Serial.println();
}

int getDistanceFromUltrasonicSensor(){
  //Create an instance of the RunningMedian class with the number of samples to use
  RunningMedian samples = RunningMedian(7);
  
  //Get the 7 samples
  for(int i= 0; i<5; i++){
      delay(50);
      int pingVal = sonar.ping();
      
      int distance = pingVal / US_ROUNDTRIP_CM;
      if(distance == 0){
        distance = 3000;
      }
     samples.add(distance);
   }   
   //Return the median value
   return samples.getMedian();  
}

Thanks for all your help so far :slight_smile: I’m now gonna go into uni and listen to my tutor tell me what little time i have left to complete this project

_buchan:
I appreciate you tidying up and re-writing the code for me so much, thank you. However, when i tested it out, the LED initially was off, but as I put an object in front of the sensor, the light turned on and stayed on, never dimming or anything.

Also, I looked at NewPing and found arduino’s example code for a sensor (Sample NewPing Sketch). I uploaded the code and the results are in the screenshot. :frowning:

This is some code I found including median, however, there is no “ping_median()” written in the code so i don’t think it’s correct. Plus, I got a compiling error when verifying the code anyway.

#include <newping.h>

#include <runningmedian.h>

//Define the pins for the ultrasonic sensor’s echo and trigger pins
#define DISTANCE_ECHO 11
#define DISTANCE_TRIGGER 10
#define MAX_ULTRASONIC_DISTANCE 200

NewPing sonar(DISTANCE_TRIGGER, DISTANCE_ECHO, MAX_ULTRASONIC_DISTANCE);

void setup()
{
   //Initialize serial communication to send the distance value  to the serial monitor
   Serial.begin(9600);
}

void loop()
{
 int distance_cm;
 distance_cm = getDistanceFromUltrasonicSensor();
 
 Serial.print(distance_cm);
 Serial.print(“cm”);
 Serial.println();
}

int getDistanceFromUltrasonicSensor(){
 //Create an instance of the RunningMedian class with the number of samples to use
 RunningMedian samples = RunningMedian(7);
 
 //Get the 7 samples
 for(int i= 0; i<5; i++){
     delay(50);
     int pingVal = sonar.ping();
     
     int distance = pingVal / US_ROUNDTRIP_CM;
     if(distance == 0){
       distance = 3000;
     }
    samples.add(distance);
  }  
  //Return the median value
  return samples.getMedian();  
}




Thanks for all your help so far :) I'm now gonna go into uni and listen to my tutor tell me what little time i have left to complete this project

That “RunningMedian” class isn’t what you wanted. As I said, “NewPing” has a method called ‘ping_median()’. A part of the “NewPing” class, not a separate library. You can’t just blindly download and install libraries. you need to take the time to read the documentation. It’s clearly mentioned on the “NewPing” page
Here’s a link to the “NewPing” page:-
NewPing

This is the prototype, copied from the “NewPing” page:-

sonar.ping_median(iterations) - Do multiple pings (default=5), discard out of range pings and return median in microseconds.

Here’s a simple example I wrote when I first got “NewPing”, to test the ‘ping_median()’ method:-

/* NewPingMedianTest.ino
*
*  Notes:
*  A test of the 'ping_median()' function of the 'New Ping' Library,
*  using the HC-SR04 ultrasonic ranging module.
*  In this test, the program will light a green LED initially, then
*  turn it off and light a red LED if an object comes within 20cm.
*  It will use 'pingMedian()' to average 5 US range readings each time.
*/

#include "NewPing.h"

#define TRIGGER_PIN     5               // HC-SR04 trigger pin.
#define ECHO_PIN        6               // HC-SR04 echo pin.
#define MAX_DISTANCE    200             // Maximum distance to ping for in cm, 400 max.

// Constants:-
const int THRESHOLD = 20;               // 20cm threshold.
const byte pGreenLED = 2;               // Green LED on pin D2.
const byte pRedLED = 3;                 // Red LED on D3.

// Variables:-
long lDistancecm;                       // Will hold the actual distance to the detected object in cm.
unsigned long mS;                       // Holds millis() value for delays.
unsigned long median_uS;                // Time for distance in microseconds.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void setup()
{
    pinMode(pGreenLED, OUTPUT);
    pinMode(pRedLED, OUTPUT);
}

void loop()
{
    median_uS = sonar.ping_median(5);  // Do 5 pings, discard out of range pings and return median in microseconds.
    lDistancecm = sonar.convert_cm(median_uS);  // Convert microseconds to distance in cm.

    // Light the LEDs according to distance. Green if over 20cm, red if closer:-
    if ((lDistancecm == 0) || (lDistancecm >= THRESHOLD))
    {
        digitalWrite(pGreenLED, 1);
        digitalWrite(pRedLED, 0);
    }
    else
    {
        digitalWrite(pGreenLED, 0);
        digitalWrite(pRedLED, 1);
    }
    delay(50);                                // 50mS between distance checks.
}

Also, I looked at NewPing and found arduino’s example code for a sensor (Sample NewPing Sketch). I uploaded the code and the results are in the screenshot. :frowning:

So, did you actually run that code example?

I’m not exactly sure why the code I wrote yesterday fails to vary the LED brightness. The LED should be at full brightness at 10cm, right off at 100cm, and varying degrees of brightness in between.

My one and only ping sensor is permanently attached to my robot, but I’ll run some leads from it in the morning and see why the code fails. Maybe I’ve overlooked something.

For now, I just tested the maths - the 10cm to 100cm conditional and the ‘map()’ function. I replaced the ‘analogWrite()’ with a print to the serial monitor, displaying the “brightness” value, and got correct readings. I manually ‘plugged in’ values of 5cm, 10cm, 100cm and 55cm, and the values printed to the serial monitor were exactly as expected:-
5cm: no output
10cm: brightness = 255
100cm: brightness = 0
55cm: brightness = 128

So that part of the code’s OK, and doing what it should. I’ll do that test in the morning and see what’s going wrong. I can’t see anything in the rest of the code that should fail. I’m puzzled.

A screenshot of my test code and result:-
(Right-click and “View image” or similar to see it in full size.)

Math Test.JPG

Well, somethin’ ain’t right!

I just put together a test setup, loaded the exact code that I gave you yesterday, and it works perfectly.
(I decided not to wait until tomorrow morning, so did it tonight.)

The LED is right off to start, then as I move closer, at 100cm the LED begins to light, getting brighter as I get closer until it reaches full brightness at a range of 10cm.

Then, as I move further away, the LED gradually dims until it’s turned right off at 100cm.

I just wasted 1/2 hour, but at least now you know the code works fine.

Just how far did you test it to? If you only moved your hand back and forth a couple of inches, you wouldn’t see much change, because the range is currently set up for 10cm to 100cm. It’s up to you to decide what you want as an effective range, and change the code accordingly.

So there’s no doubt in your mind that it works, I posted a crude video of it on YouTube here:-
US PWM LED

And here’s a photo of the setup:-
(Ignore the UNO behind the ‘ping’ sensor’ It’s the robot car controller, not associated with this test.)

US PWM LED setup.JPG

So I think a clear photo of your setup is called for at this point.
Edit: The full setup as it stands now, including the LED.
(Your first photo only showed the ‘ping’ sensor.)

Okay, so i have this code below, and it enables me to look at the measurements on the serial monitor as well as controlling the LED light to an extent. Basically, what happens is that at 0cm, the LED light is at a low brightness. As i put an object in front of it, the sensor reads the measurement correctly but the light just becomes brighter in an instance, rather than gradually getting brighter.

#include <NewPing.h>

#define TRIGGER_PIN  8  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

const byte pRedLED = 9;                 // Red LED on D9

// Variables:-
long lDistancecm;                       // Will hold the actual distance to the detected object in cm.
unsigned long mS;                       // Holds millis() value for delays.
unsigned long median_uS;                // Time for distance in microseconds.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void setup() 
{
    pinMode(pRedLED, OUTPUT);

  Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results.
}

void loop() 
{
  delay(50);                      // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.
  unsigned int uS = sonar.ping(); // Send ping, get ping time in microseconds (uS).
  Serial.print("Ping: ");
  Serial.print(uS / US_ROUNDTRIP_CM); // Convert ping time to distance and print result (0 = outside set distance range, no ping echo)
  Serial.println("cm");

  median_uS = sonar.ping_median(5);  // Do 5 pings, discard out of range pings and return median in microseconds.
    lDistancecm = sonar.convert_cm(median_uS);  // Convert microseconds to distance in cm.
    
    {
        byte brightness = (byte)map(lDistancecm, 10, 100, 255, 0); // Convert 10-100 to 255-0.
        analogWrite(pRedLED, brightness);               // Generate PWM.
 
    }
    delay(50);                                // 50mS between distance checks.
}

pics attached show my setup

_buchan:
Okay, so i have this code below, and it enables me to look at the measurements on the serial monitor as well as controlling the LED light to an extent. Basically, what happens is that at 0cm, the LED light is at a low brightness. As i put an object in front of it, the sensor reads the measurement correctly but the light just becomes brighter in an instance, rather than gradually getting brighter.

Yes, that's exactly what the code tells it to do. As I said yesterday, you need to set your own values for range.
I just gave it nominal values of 10cm to 100cm, so of course it starts working at 10cm. Below 10cm absolutely nothing will happen. And 'ping' sensors don't work right down to 0cm anyway.
Also, don't set the minimum to 0, because that's the value that both methods return for "out of range", since it's not a valid value for a ping sensor.

Have you read the 'pulseIn()' documentation? Or thoroughly read the HC-SR04 datasheet?
Or the "NewPing" documentation?
These all cover the case of 0 microseconds/0cm/HC-SR04 minimum range. (I just looked for the exact value - the HC-SR04 has an absolute minimum of 2cm, but is actually only reliable to slightly higher than that.)

If you want to to start operating at less than 10cm, then write that into the code. If you want the LED to be right off at less than 100cm, write that into the code.

I never set out to do your whole project for you. You seem to expect me to hand the whole thing to you on a platter.
I've now put in all of this extra time and effort just because you haven't properly read everything I've said, and all of the documentation.

In post #6 I said "Also, you'll need to set up the range to suit your preference" and "This means if the range is larger than or equal to 10cm AND smaller than or equal to 100cm"

I made it extremely clear exactly how the code works.

Anyway, it's now 12:30am and I'm exhausted. I don't want to waste any more time on this.

I understand that the light will only work between 10-100cm, however, inbetween 10-100cm, the brightness doesn't change at all, the brightness just stays constant.

Because we're using NewPing.h, we don't need pulseIn().

I tested the code that I wrote yesterday, and it works perfectly, as the video illustrates. I have no intention of wasting further time also getting the "NewPing" version working for you.

I mentioned 'pulseIn()', because that was the method used in the code that I wrote, and that I tested.
And "NewPing" also returns 0 for an out-of-range reading. If you bothered to carefully read the 'ping_median()' example I gave you, you would have seen that. In that code I handle the case of "NewPing" returning 0.

For the last time, start reading the @#$% documentation, learning how the code works, and how to do things for yourself. I set out to help, not to do your whole project for you.
I won't be doing anything more on this.

OldSteve:
Then, as I move further away, the LED gradually dims until it's turned right off at 100cm.

I just wasted 1/2 hour, but at least now you know the code works fine.

I hope not a complete waste of time. We all got a nice little video out of it.

Thanks for sharing the video. I thought it was fun to watch. I hope you make more videos of your experiments (I haven't watched the robot video yet but I plan to do so).

Something to consider when doing LED fading projects is to use a logarithmic scale when fading a LED. Apparently our eyes judge brightness similar to the way we judge loudness (logarithmically).

DuaneDegn:
I hope not a complete waste of time. We all got a nice little video out of it.

Thanks for sharing the video. I thought it was fun to watch. I hope you make more videos of your experiments (I haven't watched the robot video yet but I plan to do so).

The robot car video is nothing special - just the first test of it's swinging ultrasonic 'head'. The car turns to the side with the longest clear path. At that time, it just turned about 90 degrees. I've improved that a bit now, so it doesn't always do a sharp turn. Now it turns at 90 degrees to the detected object, rather than an absolute 90 degree turn.
I might make more videos in the future, if I have something interesting enough to show, but most of my stuff is boring and not worth filming.
When I get back onto the robot car project, I'll be completing a homing system to allow the car to find it's charger 'ramp', so I might film that.

Something to consider when doing LED fading projects is to use a logarithmic scale when fading a LED. Apparently our eyes judge brightness similar to the way we judge loudness (logarithmically).

I didn't want to complicate things that much, (and wasn't thinking that far ahead), so just kept it simple. He was having enough trouble just understanding the basics.
You're right, though, a linear fade doesn't look quite right.