ESP8266 Photo Interrupter Sensor Project
This project demonstrates how to use an ESP8266 microcontroller to interface with a photo interrupter sensor. The photo interrupter detects the presence of an object by interrupting an infrared light beam, which the ESP8266 reads as a digital signal.
How It Works
A photo interrupter — also called a slotted optical switch or optocoupler interrupter — places an infrared LED on one side of a narrow gap and a phototransistor directly opposite. When the slot is clear, light reaches the phototransistor and it conducts. When something opaque enters the slot, the light path is cut and conduction stops.
Unlike a reflective sensor, the geometry is fixed and the detection point is mechanically precise — typically within a fraction of a millimetre at the slot edge. That repeatability is the reason photo interrupters dominate position sensing in printers, scanners, disk drives and 3D printers.
Because the LED and detector face each other across a small, shaded gap, ambient light rarely interferes. There is also nothing to wear out, unlike a mechanical limit switch whose contacts degrade over tens of thousands of operations.
The phototransistor output is usually open-collector, so it needs a pull-up resistor. Many carrier boards such as the KY-010 already include one along with an indicator LED.
Components Needed
- ESP8266 (NodeMCU)
- Photo Interrupter Sensor module
- ESP8266 (NodeMCU)
- USB cable for programming and power
- ESP8266 (NodeMCU)
Wiring to the ESP8266 (NodeMCU)
Connect S to D5, the supply pin to the 3.3 V rail and GND to ground. If you are using a bare slotted sensor rather than a carrier board, you must add both the LED's series resistor (around 220 Ω) and a 10 kΩ pull-up on the phototransistor collector.
Output polarity varies between carriers — some read HIGH when blocked, others LOW. Rather than trusting a datasheet, run the sketch below once with the slot clear and once blocked, and note which way round yours behaves.
Mount the sensor so the interrupting vane passes cleanly through the slot without touching either side. A vane that rubs will eventually shift the alignment and cause missed counts.
| Module pin | ESP8266 (NodeMCU) pin | Function |
|---|---|---|
| S / OUT | D5 | HIGH when the slot is blocked (carrier dependent) |
| Middle / + | 3V3 | Supply |
| − / GND | GND | Common ground |
Build and Upload
After uploading the code, open the Serial Monitor (baud rate: 9600).
The Serial Monitor will display whether an object is detected ("Object detected!") or not ("No object detected.").
Interrupt the infrared beam of the photo interrupter sensor with an object to observe changes in detection status.
Example Code
Counting slot interruptions, the basis of an encoder or tachometer. Upload it with the board set to ESP8266 (NodeMCU) and open the Serial Monitor at 115200 baud.
const int SENSOR_PIN = 5;
const int SLOTS_PER_REV = 20; // matches a typical encoder wheel
volatile unsigned long pulses = 0;
unsigned long lastReport = 0;
void countPulse() { pulses++; } // keep ISRs short
void setup() {
Serial.begin(115200);
pinMode(SENSOR_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(SENSOR_PIN), countPulse, FALLING);
}
void loop() {
if (millis() - lastReport >= 1000) {
noInterrupts();
unsigned long count = pulses;
pulses = 0;
interrupts();
float rpm = (count * 60.0) / SLOTS_PER_REV;
Serial.print("pulses/s: ");
Serial.print(count);
Serial.print(" speed: ");
Serial.print(rpm, 1);
Serial.println(" RPM");
lastReport = millis();
}
}
Applications
A photo interrupter sensor turns up in a wide range of projects. These are the uses where it is the right choice rather than a compromise:
- Rotary encoders measuring shaft speed and direction
- End stops and homing switches on 3D printers and CNC machines
- Paper presence and jam detection in printers and feeders
- Counting items dropping through a chute
- Tamper detection, where removing a cover clears the slot
Working with the ESP8266 (NodeMCU)
The ESP8266 (NodeMCU) is built around the ESP8266 and runs on 3.3 V logic with roughly 80 KB usable of SRAM and 4 MB on most NodeMCU boards of program flash. These details change how this circuit is wired and what the sketch can do, so they are worth stating plainly before you build.
The ESP8266 is a 3.3 V part and is not 5 V tolerant — feeding a 5 V sensor output straight into a GPIO can damage the chip. Use a divider or level shifter.
There is only one ADC channel, so reading several analog sensors needs an external multiplexer such as a CD4051 or an ADS1115.
D3 (GPIO0), D4 (GPIO2) and D8 (GPIO15) are strapping pins sampled at boot; holding them at the wrong level stops the board starting.
Built-in WiFi means the same sketch can publish readings to a dashboard without extra hardware.
| ESP8266 (NodeMCU) characteristic | Value | Why it matters here |
|---|---|---|
| Logic voltage | 3.3 V | Sensor outputs above this level need a divider or level shifter |
| ADC resolution | 10-bit (0–1023) | Sets how finely an analog reading can be resolved |
| Analog inputs | a single analog channel, A0 | Determines how many analog sensors can share the board |
| PWM outputs | any GPIO via software PWM | Needed for brightness, speed and tone control |
| I²C pins | D2 (SDA, GPIO4) and D1 (SCL, GPIO5) by default | Fixed by hardware — wiring copied from another board may not match |
| Interrupt pins | any GPIO except D0 (GPIO16) | Required for counting fast or asynchronous events |
| Serial | one hardware UART plus a transmit-only second port | Monitor runs at 115200 baud by default |
Troubleshooting
Most problems with this module fall into a handful of categories. Work through these before suspecting the part itself:
- The output never changes — the vane is missing the slot, or the LED side has no current-limiting resistor and has failed.
- Counts are double what they should be — the sketch is triggering on both edges. Use
FALLINGrather thanCHANGE. - Counts are missed at speed — polling cannot keep up. Use the interrupt-driven approach shown here.
- The reading is inverted compared with the tutorial — carrier boards differ; swap the comparison and continue.
- The sketch compiles but the board resets or behaves erratically — a 5 V module output is being driven into a 3.3 V pin. Measure the signal before connecting it.
- Readings differ from an Arduino tutorial for the same part — the 10-bit ADC returns 0–1023, not 0–1023, so any constant copied from an Uno example needs rescaling.
Taking It Further on the ESP8266 (NodeMCU)
Once the basic reading works, where you go next depends very much on which board you are using. These are the directions that suit the ESP8266 (NodeMCU) specifically:
The obvious extension on an ESP8266 is to stop printing to Serial and publish instead. A few lines using ESP8266WiFi and an HTTP client will push each reading to a dashboard such as ThingSpeak, or to an MQTT broker for home automation.
Running an onboard web server turns the board into its own display: serve a small HTML page that fetches the latest reading over AJAX, and any phone on the network becomes the instrument panel.
For battery operation, deep sleep is essential — the ESP8266 draws around 70 mA with WiFi active but under 20 µA asleep. Wire D0 (GPIO16) to RST so the board can wake itself, take a reading, publish and sleep again.
Notes and Practical Limits
On the ESP8266 (NodeMCU), interrupt-capable pins are any GPIO except D0 (GPIO16). Attaching an encoder to a non-interrupt pin is the most common reason counts go missing once a motor spins up.
Keep the ISR to a single increment, as above. Printing to Serial inside an interrupt handler will corrupt timing and can hang the board.