How Photosensors Convert Light to Electrical Signals

Photosensors rely on the photoelectric effect or photoconductivity to translate photon strikes into measurable electrical signals. In analog phototransistors like the Vishay TEMT6000, incoming light hits the base-collector junction, generating electron-hole pairs that act as base current. This allows a proportional collector current to flow through an external load resistor, creating a variable voltage drop. In digital ambient light ICs like the Rohm BH1750FVI, an integrated photodiode array feeds an internal transimpedance amplifier and a precision ADC, outputting a pre-scaled binary lux value directly over an I2C bus.

The critical distinction for embedded builders is the output signal format. Analog photosensors output a continuous voltage (or current) that requires your microcontroller's ADC to digitize, meaning you must handle scaling, reference voltages, and noise filtering in software. Digital photosensors handle the analog-to-digital conversion internally, outputting discrete integer data. Conflating these two architectures—such as wiring an analog phototransistor output to a digital I2C pin, or expecting a raw voltage from a digital IC—will result in unreadable buses or damaged GPIO pins.

Hardware Specs and ESP32 Wiring Pinouts

Before wiring your breadboard, you must select the right sensor for your environment. Basic Cadmium Sulfide (CdS) LDRs like the GL5528 are cheap but suffer from severe memory effects and slow response times. Modern embedded designs default to silicon phototransistors or dedicated I2C ICs.

Table 1: Photosensor Component Specifications (2026 Market Data)
Part Number Type Supply Range Output Type Spectral Peak Lux Range Approx Cost
GL5528 CdS LDR N/A (Passive) Variable Resistance 540 nm (Green) 10 - 10,000 $0.15
TEMT6000 NPN Phototransistor 2.5V - 5.5V Analog Voltage 570 nm (Yellow-Green) 1 - 1,000 $1.20
BH1750FVI Digital I2C IC 2.4V - 3.6V Digital (I2C) 470 nm (Blue-Green) 1 - 65,535 $1.85
TSL2591 High-Dynamic I2C 2.7V - 3.6V Digital (I2C) Broadband (IR+Vis) 0.0001 - 88,000 $4.50

When interfacing with a 3.3V microcontroller like the ESP32 DevKit v1, you must respect the logic levels and supply ranges. The BH1750 is strictly a 3.3V device; feeding it 5V will destroy the internal I2C pull-ups. The TEMT6000 can tolerate 5V, but if you power it at 5V, you must use a voltage divider on the analog output to prevent feeding 5V into the ESP32's 3.3V-tolerant ADC pins.

Table 2: ESP32 DevKit v1 Wiring Pinout
Sensor Pin TEMT6000 (Analog) BH1750FVI (Digital I2C) Notes & Constraints
VCC / VIN ESP32 3V3 ESP32 3V3 Keep TEMT6000 at 3.3V to match ADC max input.
GND ESP32 GND ESP32 GND Ensure common ground plane; avoid daisy-chaining grounds.
OUT / SDA ESP32 GPIO 34 (ADC1_CH6) ESP32 GPIO 21 (SDA) GPIO 34 is input-only on ESP32. No internal pull-up needed.
N/C / SCL Not Connected ESP32 GPIO 22 (SCL) Add 4.7kΩ external pull-ups to 3.3V on SDA/SCL lines.
ADDR Not Connected GND (or VCC) Tie to GND for I2C address 0x23; tie to VCC for 0x5C.
Bench Tip: If you are using a pre-assembled TEMT6000 breakout board, verify the onboard load resistor. Most Chinese-market breakouts use a 10kΩ pull-down resistor. If yours uses a 100kΩ resistor, the math in the next section will be off by a factor of 10, and the sensor will saturate at much lower lux levels.

Raw ADC to Lux Math and Calibration

A raw ADC reading is useless for physical decision-making. You must convert the microcontroller's integer output into standard SI units (Lux). The math diverges entirely based on whether your photosensor outputs a voltage or a digital register value.

Analog Output Math (TEMT6000)

The TEMT6000 outputs a variable voltage based on the current flowing through the phototransistor and the onboard load resistor. According to the Vishay TEMT6000 datasheet, the typical light current responsivity is 0.02 µA per Lux.

Assuming a standard breakout board with a 10,000Ω (10kΩ) load resistor and a 3.3V supply:

  1. Calculate Voltage per Lux: $V_{out} = I_{current} \times R_{load}$. At 1 Lux, $I = 0.02 \mu A$. $V_{out} = (0.02 \times 10^{-6}) \times 10,000 = 0.0002V$ (or 0.2mV) per Lux.
  2. Invert for Lux: $\text{Lux} = \frac{V_{out}}{0.0002} = V_{out} \times 5000$.
  3. Convert to Millivolts: If your microcontroller reads in millivolts (mV), $\text{Lux} = \frac{mV}{1000} \times 5000 = mV \times 5$.

The ESP32's ADC is notoriously non-linear, particularly in the 0-0.1V and 3.1-3.3V ranges. Never use the raw analogRead() 12-bit integer (0-4095) with a simple voltage divider formula. Instead, use the ESP-IDF or Arduino core analogReadMilliVolts() function. This function reads the factory-calibrated eFuse data on your specific ESP32 silicon to correct the non-linear curve, returning a highly accurate millivolt reading.

// ESP32 Arduino Core Implementation
int adcPin = 34;
int millivolts = analogReadMilliVolts(adcPin);
float lux = millivolts * 5.0;
Serial.printf("Light Level: %.1f Lux\n", lux);

Digital Output Math (BH1750FVI)

The BH1750 handles the transimpedance amplification and ADC conversion internally. It outputs a 16-bit unsigned integer via I2C. However, the raw register value is not exactly Lux; it requires a scaling factor dictated by the Rohm BH1750FVI datasheet.

The standard resolution mode (Measurement Time = 69ms) requires dividing the raw I2C payload by 1.2 to yield true Lux.

// Wire.h I2C Read Implementation
Wire.requestFrom(0x23, 2);
uint16_t rawVal = (Wire.read() << 8) | Wire.read();
float lux = rawVal / 1.2;

Interference Sources and Signal Conditioning

Photosensors do not operate in a vacuum. Real-world environments introduce optical and electrical noise that will corrupt your readings if left unaddressed.

Table 3: Common Interference Sources and Mitigation Strategies
Interference Type Affected Sensors Physical Cause Mitigation Strategy
50/60Hz Mains Flicker All (Analog worse) AC driven LEDs and fluorescents pulse at 100/120Hz. Software: Average 20 samples over 20ms. Hardware: Increase BH1750 integration time.
IR Bleed (Sunlight) TEMT6000, LDRs Silicon phototransistors are naturally sensitive to near-IR (800-950nm). Use sensors with integrated IR-rejection filters (BH1750) or add an external IR-cut optical filter.
Analog Trace EMI TEMT6000 High-impedance analog traces act as antennas for switching regulators and WiFi antennas. Keep analog wires under 2 inches. Use twisted-pair wiring. Add a 100nF ceramic capacitor between OUT and GND.
I2C Bus Capacitance BH1750, TSL2591 Long wires add parasitic capacitance, rounding the I2C square waves and causing NACK errors. Use 4.7kΩ pull-ups for runs <10cm. Drop to 2.2kΩ pull-ups for runs up to 30cm. Keep SDA/SCL away from motor leads.

Handling the ESP32 ADC Deadzone

If your TEMT6000 is reading exactly 0 Lux in a dimly lit room, you have likely hit the ESP32 ADC deadzone. The ESP32 ADC cannot accurately resolve voltages below ~75mV. Because the TEMT6000 outputs 0.2mV per Lux, anything below ~375 Lux will read as 0 on the ESP32. If your application requires dim-light sensing (e.g., a nightlight trigger at 50 Lux), the TEMT6000 paired with an ESP32 is the wrong hardware choice. Switch to the BH1750, which can resolve down to 1 Lux digitally, or amplify the TEMT6000 signal using an op-amp circuit before feeding it to the microcontroller.

Safety & Code Caveat: When deploying photosensors for automated lighting control in hardwired home environments, the sensor circuit must be galvanically isolated from mains voltage. Never wire a 3.3V microcontroller GPIO directly to a 120V/240V relay coil. Use an optocoupler or a properly rated mechanical relay module with flyback diode protection to switch mains loads based on your Lux thresholds.