in Arduino, Tutorial

How to build a distance sensor with Arduino

You can safely say that when it comes to electronics, there are countless ways to measure distances. This tutorial will explain how to build an inexpensive IR distance sensor under $8, perfect for close measurements  and motion detectors.

You will need:

You can download the final Arduino code here.

altctrlThis post is part of a longer series of tutorial about alternative games controllers.

If you are going to create an alternative game controller yourself, you should definitely look into ALT.CTRL.GDC. It’s one of the most fresh and intriguing exhibitions at GDC, and is all dedicated to innovative ways to interact with games.

Step 0: The theory

dodefaceAt the heart of this sensor there are two IR LEDs. One emits light, the other receives it. When an object is close to the sensor, it will inevitably reflect some of the IR light. This is detected by the Arduino, and translated into a distance measurement. The Human eye does not pick up infrared light; this sensor will look invisible, even though it can be extremely bright in the IR spectrum. There are many things that can go wrong with this approach; IR light can be erroneously picked up from another source, or the objects is not reflective enough. This picture shows a face of the DodecaLEDron, an alternative game controller which is based on the same principle.

Step 1: The circuit

This circuit has three LEDs. The bottom ones are IR emitters; the top one is an IR photodiode receiver. It is important to use photodiodes, and not phototransistors. Both types are IR receiver which change their resistance according to how much light they sense. However, while the latter works like a digital switch: it either sense IR or it doesn’t. For this project we need a photodiode because we actually need to measure the quantity of light received.

ir distance_bb

To measure the amount of light sensed from the IR receiver, we measure the drop of voltage on its resistor. When the photodiode receives light, it allows current to flow from the 5V to the GND pin; little to no current then flows back to A0. On the other hand, when the diode is in darkness and doesn’t let current flow, A0 reads a high voltage. For this project you have to use an analog input (A0-A5).

For this circuit to work properly, you have to find the right resistors to use. I’ve used LED calculator for this. You need to know the forward current (V) and forward voltage (If) of your LEDs, which are always indicated in their datasheets.

Step 2: The calibration

The moist naive way you can read data from the circuit is the following:

void setup () {
	pinMode(A0, INPUT);
}

void loop () {
	int distance = analogRead(A0)
	Serial.println(distance);
}

Most of the tutorials I’ve found which explains how to do IR distance sensors stop here. As a software engineer, I believe we can do much, much more on the software side. First of all, the distance readings of this sensor are likely to be affected by the IR background noise in your room. Almost every artificial light source is visible in the IR spectrum, causing interferences with our sensor. A more sophisticated approach to solve this problem is to calibrate the light background in your environment. To do this, we can sample the input from the sensor when (1) no object is nearby and (2) an object is very close. This provides a baseline calibration which depends on the light in the room.

int sensorPin = A0;
int duration = 1000;

int calibrationZero;
int calibrationOne;

void setup () {
	pinMode(sensorPin, INPUT);	

	// Far
	Serial.print("Far calibration...");
	delay(duration);

	int sensorCumulativeValue = 0;
	int reads = 0;
	unsigned long timeStart = millis();
	do {
		delay(20);
		sensorCumulativeValue += analogRead(sensorPin);
		reads ++;
	} while (millis() <= timeStart + duration);
	calibrationZero = sensorCumulativeValue / reads;

	Serial.print(calibrationZero);
	Serial.println();
	
	// Close
	Serial.print("Close calibration...");
	delay(duration);

	sensorCumulativeValue = 0;
	reads = 0;
	timeStart = millis();
	do {
		delay(20);
		sensorCumulativeValue += analogRead(sensorPin);
		reads ++;
	} while (millis() <= timeStart + duration);
	calibrationOne = sensorCumulativeValue / reads;
	
	Serial.print(calibrationOne);
	Serial.println();
}

This new setup function is divided in two seconds, to calibrate the sensor for far and nearby objects, respectively. The variable duration determines how long each calibration phase lasts. This is important because single readings are prone to error, but averaging them over a longer time interval provides more reliable data. The result of the calibration is stored in calibrationZero and calibrationOne, which will be used later to correct our readings.

Step 3: The reading

Reading from the sensor works in a similar fashion. We repeat a certain numbers of readings over a long time interval, and average them out to make sure about their reliability.

int getDistanceReading (int readings = 5) {
	int sensorCumulativeValue = 0;
	for (int i = 0; i < readings; i ++) {
		delay(20);
		sensorCumulativeValue += analogRead(sensorPin);
	}

	int distance = map
      (
        sensorCumulativeValue / readings,
        sensorCalibrationZero, sensorCalibrationOne,
        0, 255
      );
	return constrain(distance, 0, 255);
}

Lines 8-13 use the function map to rescale the readings between 0 and 255. These two values corresponds to the amount of IR light received during the two calibration steps.

Finally, to use this code:

void loop () {
	int distance = getDistanceReading ();
	Serial.println(distance);
}

It’s worth noticing that your code can be quite jumpy and unreliable. An interesting approach to remove noise from your readings is by adopting a Kalman filter, which has been discussed in details in a previous post.

Conclusion

This post explored how to use IR light to sense short distances. I have used this technique to create an omni-direction controller called DodecaLEDron. It features 11 distance sensors, all fitting in a £10 budget. Since I required so many analog inputs, I had to use an Arduino Mega rather than an Arduino One.

Further projects…

There are many other techniques which are more reliable and fully Arduino-compatible.  If you only to detect motion, you can use a PIR sensor. For more advanced projects, my favourite component is the ultrasonic module which is, de-facto, a small sonar. By using the Doppler effect you can also calculate the speed of the object it detects. For large scale applications, such as localisation within a building, you should use Bluetooth iBeacons; I’ll explain how to use them in a future post.


💖 Support this blog

This website exists thanks to the contribution of patrons on Patreon. If you think these posts have either helped or inspired you, please consider supporting this blog.

Patreon Patreon_button
Twitter_logo

YouTube_logo
📧 Stay updated

You will be notified when a new tutorial is released!

📝 Licensing

You are free to use, adapt and build upon this tutorial for your own projects (even commercially) as long as you credit me.

You are not allowed to redistribute the content of this tutorial on other platforms, especially the parts that are only available on Patreon.

If the knowledge you have gained had a significant impact on your project, a mention in the credit would be very appreciated. ❤️🧔🏻

Write a Comment

Comment

17 Comments

  1. Hi Alan.
    I wanna ask you, is the Arduino overheating? Couse I read, that the maximum current draw is about 500mA on Arduino Uno and 1500mA on Mega. If my calculations are right, one of your sensor draws 300mA, you are using Arduino Mega that can supply only 1500mA (I read it there if I understood it right http://stackoverflow.com/questions/27032926/5-volt-output-max-current-for-arduino-mega-2560-rev3 ) and 1500/300=5 so you should be able to use safely only 5 sensors but you are using 11. How did you achieved this? The second thing I want to ask you is: Can I use Arduino Leonardo for this purpose? What do you thing? I know it has less analog pins than Mega, so I will be able to make less sensors. However iťs a lot cheaper.

    • Hey! Yes, that’s too much power for Arduino Uno! I powered the sensors and LEDs of the DodecaLEDron with an external 5V power supply! I never really had any problems with Arduino overhearting, also because it doesn’t provide really enough current by itself. Arduino Leonardo should be fine, as far as I know! 😀

  2. Hi Alan,
    Congratulations for your amazing works, and tutorials you share.
    I’m trying to make a video interactive with public and to make it, i need to activate animations on screen when people reach a specific distance with the screen hooked on the wall:
    – When people enters in a zone near the screen > activation new state.
    – When people leaves this zone > back to the original state.
    I begin my project with a PIR sensor but it unable to detect no-moving bodies, and I don’t know to calculate precisely the distance between people and PIR.
    I don’t want the detection stops before people leaves the entire zone.
    Maybe ultrasonic sensor is the sensor I need.
    I ask you, which the best way (easily way ) to make this project? ( full money ).
    Special condition: I want to get the sensor value in Unity 3D.
    #sensor#arduino#opencv#kinect#…
    Thanks Alan,

    • Hey!

      I think there are really a lot of ways in which this project can be built.
      If you have those kind of constraints, perhaps using a Kinect is the best way. It can detect people even when they are not moving, and it works with multiple people. The problem is that when it comes to counting people in a real environment, sensors can be tricked very easily.

  3. Hey ALAN,

    I like the way you guys make the program and use it for different purposes like games. It is really cool. Umm…I need to clear this doubt.

    You have declared “int sensorPin=A0”. But you have given the read part in the above program as “analogRead(pinSensor)”. It is actually “sensorPin” right?

    I just have doubt in this. Thanks for the code 😀

  4. Hey Alan. In your code, you did not declare the variable “sensorCalibrationZero” and “sensorCalibrationOne”. Should I amek these equal to zero?

  5. Thanks for the tutorial, Alan! It just took me a while to find out that the receiver diode’s longer leg should be connected to GND, while the longer leg of the transmitter should be connected to VCC.

  6. Can you do it with IR Blaster? As it requires no separate hardware, many android devices have it inbuilt.

  7. Hey there Alan,
    just a quick question, this doesn’t actually tell you the distance between the sensor and the object, now does it?
    Im a bit confused because we are printing the distance, but I don’t know the physical meaning of the variable!

    • Hi Francisco,

      No, it does not print the distance in metres.
      What it does, is indicating the amount of IR light that is reflected back into the sensor.
      That is, loosely speaking, a metric of how much IR reflection there is around the sensor.
      Which could indicate the presence of obstacles nearby.

      You can theoretically calculate how much light should be reflected by a source at a certain distance, but that is very inaccurate. This is because each environment and each material has different properties, and there is plenty of background IR radiation to mess up with your sensor.

      The best way is to calibrate it based on your environment. Check the value you get when you have your hand at different distances, and use those values accordingly.

Webmentions

  • I create custom controller "Project Notouch" - Petr Marek May 15, 2019

    […] you can create your own type of controller. We decided to create our own controller when I found an article by Alan Zucconi about his […]

  • Everything You Need to Know About LEDs - Alan Zucconi May 15, 2019

    […] How to build a distance sensor […]