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.
- Step 0: The theory
- Step 1: The circuit
- Step 2: The calibration
- Step 3: The reading
- Conclusion
- Further projects…
You will need:
- Arduino Uno
- IR photodiode receiver
- IR emitters
- Resistors: 18Ω, 39Ω.
You can download the final Arduino code here.
This post is part of a longer series of tutorial about alternative games controllers.
- Part 1. How to integrate Arduino with Unity
- Part 2. How to hack any IR controller
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
At 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.
🪛 Recommended Components
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.
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.
🪛 Recommended Components
Building alternative game controllers intimidating. If you are new to Electronics, Makey Makey and BBC micro:bit are perfect for beginners!
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 (Passive InfraRed). 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 could use Bluetooth iBeacons; I’ll explain how to use them in a future post.
Leave a Reply