An IR obstacle sensor on an Arduino: a touchless soap dispenser
Beginner
Or see every lesson.
- 455parts on the bench
- 43boards running now
- 1.00xreal time, on every board
The FC-51 is the little IR obstacle sensor in robot car kits: a clear infrared LED, a dark receiver beside it, a trimmer and one output pin. Infrared bounces off anything close, and once enough comes back the output goes LOW. It cannot tell you how far away something is, only whether it is there, and that is all a lot of projects need.
The circuit above is an Uno driving a touchless soap dispenser: an FC-51 beside the spout and a small pump under the bottle, on a transistor. Drag the slider on the dispenser to bring a hand in under the spout: the sensor’s lamp lights, the pump runs for 0.7 seconds, soap drops into the hand and the serial monitor counts the dose. Leave the hand there and it does not pump again until you take it away.
What you need
- An Arduino Uno
- An FC-51 IR obstacle sensor (also sold as an IR proximity sensor)
- A small 5 V pump or DC motor
- A 2N2222 transistor, a 1k resistor and a 1N4007 diode
- A breadboard and jumper wires
Wiring
| Part and pin | Goes to | Note |
|---|---|---|
| FC-51 OUT | Pin 2 | LOW while something is in range |
| FC-51 VCC and GND | 5 V and GND | Runs on 3.3 to 5 V |
| Pin 9, through 1k | Transistor base | The pump switch |
| Transistor emitter | GND | |
| Pump | 5 V to the collector | Diode across it, band toward 5 V |
How it sees, and why LOW means yes
The clear LED shines infrared forward. The receiver measures how much comes back, and a comparator chip on the board, an LM393, compares that with the trimmer’s setting. Enough light back and it pulls OUT down to ground and lights the obstacle lamp. The rest of the time a 10k resistor on the board holds OUT high.
So the code reads it the "wrong" way around: digitalRead(2) == LOW means something is in front. No library, no pull-up from the Uno, just pinMode(2, INPUT).
Range, and the black-object problem
Turn the trimmer clockwise and it reaches further. With it in the middle, as here, a white card trips it at about 13 cm. Skin sends back about two thirds as much infrared as white card, so a bare hand has to come to about 11 cm: drag the hand to 10 cm and the pump runs, at 12 cm nothing happens. Listings give 2 to 30 cm for the whole trimmer.
A black object reflects far less infrared than a white one, so it has to come much closer. Click the swatch beside the slider to put a black glove on the hand: now 5 cm is not close enough, and it trips at 3 cm. A robot with FC-51s runs into black chair legs for this reason, and it is why a soap dispenser sets its sensor to see a hand only when it is right under the spout.
The sketch: one dose per hand
A dispenser that pumps for as long as a hand is there empties the bottle. So when OUT goes low, the sketch prints "hand: dose 1", turns the pump on for 700 milliseconds and off again. Then it waits until OUT has stayed high for half a second, meaning the hand has gone, and prints "ready". Only then will it pump again.
The pump is a motor, which needs more current than a pin can give, so pin 9 switches it through a 2N2222 transistor. The diode across the motor catches the voltage spike a motor makes when its current stops.
const int SENSOR = 2;
const int PUMP = 9;
const unsigned long DOSE_MS = 700;
const unsigned long AWAY_MS = 500;
int doses = 0;
bool handThere() {
return digitalRead(SENSOR) == LOW;
}
void loop() {
if (!handThere()) {
delay(20);
return;
}
doses++;
Serial.print("hand: dose ");
Serial.println(doses);
digitalWrite(PUMP, HIGH);
delay(DOSE_MS);
digitalWrite(PUMP, LOW);
// Wait for the hand to leave, and stay gone for AWAY_MS.
unsigned long clear = millis();
while (millis() - clear < AWAY_MS) {
if (handThere()) clear = millis();
delay(10);
}
Serial.println("ready");
}What the simulation leaves out
Sunlight is full of infrared, and a real FC-51 in a sunny window sees obstacles that are not there. The sensor also looks out in a cone of about 35 degrees; here the hand on the slider is straight in front of it. Neither of those is modeled, so test a real one where it will live.
The comparator has no hysteresis, here or on the real board, so with something right at the edge of the range OUT can flicker. The half-second wait in this sketch is what stops that from pumping twice.
Common mistakes
Reading it backward. LOW means something is there.
Expecting a distance. The FC-51 says yes or no. For centimeters use an ultrasonic sensor or a laser distance sensor.
Driving a pump or motor straight from a pin. Use a transistor or a driver, and a diode across the motor.
Testing in sunlight, where a real one triggers on its own.
Forgetting that the trimmer moves the range. If it triggers all the time or never, turn it until the lamp just goes out with nothing in front.
Questions
How do I connect an IR obstacle sensor to an Arduino?
VCC to 5 V, GND to GND and OUT to any digital pin. Read it with digitalRead(): LOW means an obstacle is in range, HIGH means nothing is.
What is the range of the FC-51?
About 2 to 30 cm on a white surface, set by the trimmer. A black object has to come several times closer. In the middle of the trimmer it sees a white card at about 13 cm.
Can an IR obstacle sensor measure distance?
No. It only reports whether something is closer than its set range. Use an HC-SR04 ultrasonic sensor or a VL53L0X for a distance in centimeters or millimeters.
Is the FC-51 the same as a line tracking sensor?
They are close cousins. A line sensor such as the TCRT5000 points the same kind of LED and receiver down at the floor to tell black tape from white, and a 5-channel board puts five in a row.
Keep going
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.