A photoreceptor sensor outputs either an analog voltage (via a voltage divider) or a digital I2C data stream, depending on the specific module architecture. For the ubiquitous TEMT6000 analog phototransistor, the raw output is a current that a pull-down resistor converts into a voltage between 0V and 3.3V, which must be mathematically scaled to Lux. For digital ICs like the BH1750, the output is a direct 16-bit I2C integer representing Lux, bypassing the need for manual ADC math. This guide focuses on the analog TEMT6000, as it requires the most rigorous signal conditioning and raw-to-unit mathematical translation.
The Sensing Principle of a Photoreceptor Sensor
At the semiconductor junction level, a photoreceptor sensor relies on the internal photoelectric effect. When photons with sufficient energy strike the silicon (in a phototransistor like the TEMT6000) or cadmium sulfide (in an LDR like the GL5528), they excite electrons from the valence band into the conduction band. This generates electron-hole pairs, effectively increasing the material's conductivity. In a phototransistor, these photo-generated carriers act as base current, which the transistor's gain (hFE) amplifies into a much larger collector current.
Unlike a simple photodiode that generates a microamp-level current directly proportional to light, the phototransistor's internal amplification makes it highly sensitive to visible light without needing an external operational amplifier. However, this amplification comes at the cost of speed and linearity at extreme high-lux levels, meaning the relationship between incident light and output current is linear only up to roughly 1,000 Lux before beginning to compress.
Wiring and Pinout Specifications
Analog photoreceptor sensors cannot be wired directly to a microcontroller's GPIO pin. Because the sensor acts as a variable current source, you must use a pull-down resistor to convert that current into a measurable voltage. The table below outlines the wiring for the analog TEMT6000 and the digital BH1750 for contrast.
| Parameter | TEMT6000 (Analog) | BH1750 (Digital I2C) |
|---|---|---|
| Supply Range (VCC) | 3.3V to 5.0V | 3.0V to 5.0V |
| Output Type | Analog Voltage (0 - VCC) | Digital I2C (16-bit integer) |
| Signal Pin Wiring | OUT to ESP32 GPIO 34 (ADC1) | SDA to GPIO 21, SCL to GPIO 22 |
| Required Passives | 10kΩ pull-down resistor (usually on breakout) | 4.7kΩ I2C pull-ups (usually on breakout) |
| Current Draw | ~20 µA (dark) to ~1.5 mA (bright) | ~120 µA (active), ~1 µA (sleep) |
Output Signal Math: Raw ADC to Physical Lux
The most common mistake makers make with analog light sensors is using the raw 12-bit ADC value (0-4095) and applying a naive linear map. The ESP32's ADC is notoriously non-linear at the extremes (below 100mV and above 3.1V). Furthermore, the sensor outputs current, which the 10kΩ resistor converts to voltage. We must calculate the voltage first, then apply the datasheet's lux scaling factor.
According to the Vishay TEMT6000 datasheet, the collector current is approximately 10 µA at 1000 Lux. With a 10kΩ pull-down resistor, 10 µA generates 100mV (0.1V). However, empirical testing on standard breakout boards (which often use slightly different resistor values or silicon batches) shows a closer real-world mapping of roughly 1.25 Lux per millivolt when powered at 3.3V. To bypass the ESP32's raw ADC non-linearity, we use the analogReadMilliVolts() function introduced in the ESP32 Arduino Core v2.x, which utilizes the chip's factory-stored eFuse calibration data.
const int sensorPin = 34; // ADC1_CH6
const float LUX_PER_MV = 1.25; // Empirical scalar for TEMT6000 with 10k pull-down
void setup() {
Serial.begin(115200);
analogReadResolution(12); // Ensure 12-bit resolution
}
void loop() {
// Read calibrated voltage directly in millivolts (bypasses raw ADC non-linearity)
uint32_t mV = analogReadMilliVolts(sensorPin);
// Convert millivolts to Lux
float lux = mV * LUX_PER_MV;
Serial.print("Voltage: ");
Serial.print(mV);
Serial.print(" mV | Illuminance: ");
Serial.print(lux);
Serial.println(" Lux");
delay(500);
}
LUX_PER_MV constant is an approximation. For scientific or commercial applications, you must calibrate this scalar by placing the sensor next a reference lux meter (like an Extech LT300) under a known light source, reading the mV output, and dividing the reference Lux by your measured mV.
Calibration and Common Interference Sources
Even with perfect math, ambient light sensing in real-world environments introduces severe interference. Understanding these sources is critical for writing robust firmware.
1. 120Hz Mains Flicker
AC-powered lighting (LEDs, fluorescents, and incandescents) does not emit constant light. They pulse at twice the mains frequency (100Hz in 50Hz regions, 120Hz in 60Hz regions). Because the TEMT6000 has a fast response time (roughly 15 µs), it will capture these micro-fluctuations, causing your ESP32 readings to bounce wildly. The fix: Implement a software moving-average filter or integrate the readings over a full AC cycle (approx. 8.3ms for 60Hz). Taking 20 rapid samples and averaging them effectively smooths out the 120Hz ripple.
2. Infrared (IR) Bleed
While the TEMT6000 is designed for visible light (peaking at 570nm, similar to the human eye), its silicon junction remains sensitive up to 1000nm. If your project is placed near an IR heat source, a remote control, or in direct sunlight (which contains massive IR energy), your Lux readings will artificially inflate. If IR rejection is mandatory, you must place an IR-cut optical filter over the sensor dome, or switch to a dedicated digital IC like the BH1750 which has better internal IR rejection.
3. Temperature Drift
Semiconductor dark current increases with temperature. At 25°C, the TEMT6000 dark current is negligible (under 1 µA). At 60°C, it can rise enough to register as a false "dim light" reading in pitch-black environments. If your enclosure traps heat from a voltage regulator or motor driver, you must subtract a temperature-dependent offset from your final Lux calculation.
Frequently Asked Questions
Why is my photoreceptor sensor reading erratic under indoor LED lighting?
This is caused by Pulse Width Modulation (PWM) dimming and AC rectification. Modern LED drivers chop the DC current at high frequencies (often 1kHz to 20kHz) to dim the light, or they ripple at 120Hz from the AC mains. Because the analog phototransistor reacts in microseconds, it sees these micro-second flashes of darkness as rapid drops in voltage. To fix this, do not rely on a single analogRead(). Instead, sample the pin 50 times in a tight loop with a 1ms delay() between reads, sum the values, and divide by 50. This low-pass software filter averages out the flicker, yielding a stable Lux value.
How do I convert a photoreceptor sensor analog voltage to digital I2C?
You cannot convert the analog sensor itself; you must replace the module with a digital ambient light sensor IC, such as the ROHM BH1750 or the AMS TSL2561. These ICs contain an internal photodiode, an integrating ADC, and an I2C state machine. They output a direct 16-bit integer representing Lux over the SDA/SCL lines, entirely eliminating the need for pull-down resistors, ADC math, and flicker-averaging code. As detailed in the SparkFun sensor hookup guides, choose the analog TEMT6000 only when you need ultra-low cost or raw waveform data; choose the BH1750 when you need plug-and-play Lux accuracy.
What is the difference between a photoreceptor sensor and a photodiode?
A photodiode is a passive two-terminal semiconductor that generates a tiny current (nanoamps to microamps) strictly proportional to light. It requires an external transimpedance amplifier (op-amp) to convert that current into a usable voltage, but it offers incredibly fast response times (nanoseconds) and high linearity. A photoreceptor sensor (specifically a phototransistor like the TEMT6000) is a three-terminal device that uses the photoelectric effect to generate base current, which the transistor then amplifies by a factor of 100 to 500. This means a phototransistor outputs a much larger, easily readable signal without an op-amp, but it sacrifices response speed and high-lux linearity to achieve that sensitivity.






