The flicker under room lights
Many LED and fluorescent lamps on the mains brighten and dim 100 times a second, or 120 where the mains is 60 Hz. This sensor is fast enough to follow that, so single reads scatter. Average everything read over 50 ms, five whole cycles at 100 Hz and six at 120 Hz, and the ripple cancels.
Faster than it looks
The phototransistor switches in about 15 µs. After it, the 100 nF and the 4.7 kΩ make a filter, 4.7 kΩ times 100 nF, which is 0.47 ms, with a corner near 340 Hz. Anything slower than that corner passes straight through to SIGNAL. The ripple of a mains lamp is slower than that.
Many LED and fluorescent lamps run from the mains get a little brighter and
dimmer twice every mains cycle: 100 times a second where the mains is 50 Hz,
120 where it is 60 Hz. You do not see it. How deep the ripple is depends
entirely on the lamp: some barely ripple, some a lot. This sensor sees it,
and a single analogRead catches the lamp wherever it happens to be in its
wave.
Leave the reading on One analogRead and run it. Eight readings, taken at different moments, scatter across tens of counts on an Uno. The ripple in the figure is a picture of a lamp, ±25 %, not a measurement of any particular one; yours may be smaller or larger. The 100 nF takes only 4 % off it at 100 Hz, and 6 % at 120 Hz.
Average whole cycles
Pick The mean of 50 ms. Each reading is now the average of everything read over 50 ms, and 50 ms holds exactly five cycles of 100 Hz. Whole waves average to the same value wherever they start, so the eight readings land on top of each other. Switch to 60 Hz: 50 ms is six whole cycles of 120 Hz, and it still works.
Try The mean of 10 ms. At 50 Hz mains it is one whole cycle and works perfectly. At 60 Hz it is 1.2 cycles, the leftover fifth of a wave depends on where the reading started, and the scatter comes back. That is why the sketches use 50 ms: it is whole cycles in every country.
The sketch
readSteadyMilliVolts reads as fast as the board can for WINDOW_MS and
returns the mean. It is not the number of reads that matters but the time
they cover. The sketch prints a single read beside the average, so you can
watch one column wander and the other hold still under the lamp in your room.
The code
Two readings side by side: one analogRead, and the mean of everything read over 50 ms. Under a lamp that ripples, the first column wanders and the second holds still.
/*
Ambient Light Sensor - a steady reading TK20 / /p/tk20
Wiring. Count from the square pad on the TinkerBlock board, parts
up, header at the bottom:
GND -> GND
VCC -> 5V on an Uno; 3V3 on an ESP32, ESP32-S3 or Pico
NC -> nothing (unconnected on the board)
SIGNAL -> A0 on an Uno, GPIO 34 on an ESP32, GPIO 4 on an
ESP32-S3, GP26 on a Raspberry Pi Pico
Arduino IDE
Tools > Board your board, e.g. Arduino Uno
Tools > Port the one that appears when you plug in
Tools > USB CDC On Boot Enabled (ESP32-S3 only)
No library needed.
*/
// The analog pin SIGNAL is wired to.
// Uno: A0. ESP32: 34. ESP32-S3: 4. Pico: 26.
const int LIGHT_PIN = A0;
// Uno and Pico only: the ADC's full scale, in mV.
// Uno: 5000. Pico: 3300.
const float FULL_SCALE_MV = 5000.0;
// 5 cycles of a 100 Hz ripple, 6 of a 120 Hz one.
const unsigned long WINDOW_MS = 50;
float readMilliVolts() {
#if defined(ARDUINO_ARCH_ESP32)
return analogReadMilliVolts(LIGHT_PIN); // calibrated in the chip
#else
return analogRead(LIGHT_PIN) * FULL_SCALE_MV / 1023.0;
#endif
}
// The mean of everything read in WINDOW_MS.
float readSteadyMilliVolts() {
float sum = 0;
long n = 0;
unsigned long start = millis();
while (millis() - start < WINDOW_MS) {
sum += readMilliVolts();
n++;
}
return sum / n;
}
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.print("one read ");
Serial.print(readMilliVolts(), 0);
Serial.print(" mV 50 ms mean ");
Serial.print(readSteadyMilliVolts(), 0);
Serial.println(" mV");
delay(250);
}WINDOW_MS is 50 because 50 ms is five cycles of a 100 Hz ripple and six of a 120 Hz one, so the average contains only whole waves wherever it starts. The loop reads as fast as the board can for that long, which is a few hundred reads on an Uno.
The same comparison in MicroPython: one read, and the mean of everything read over 50 ms, in millivolts.
"""
Ambient Light Sensor - a steady reading, MicroPython TK20 / /p/tk20
Wiring. Count from the square pad on the TinkerBlock board, parts
up, header at the bottom:
GND -> GND
VCC -> 3V3 (never 5V: bright light takes SIGNAL towards VCC)
NC -> nothing (unconnected on the board)
SIGNAL -> GPIO 34 on an ESP32, GPIO 4 on an ESP32-S3,
GP26 on a Raspberry Pi Pico
Thonny
Run > Configure interpreter MicroPython (ESP32) or
MicroPython (Raspberry Pi Pico)
Save it to the board as main.py to run it on every power-up.
Nothing to install: machine, sys and time are built in.
"""
import sys
import time
from machine import ADC, Pin
# The GPIO number SIGNAL is wired to. ESP32: 34. ESP32-S3: 4. Pico: 26.
LIGHT_PIN = 4
WINDOW_MS = 50 # 5 cycles of a 100 Hz ripple, 6 of a 120 Hz one
adc = ADC(Pin(LIGHT_PIN))
ESP = sys.platform == "esp32" # ESP32 and ESP32-S3
if ESP:
adc.atten(ADC.ATTN_11DB) # the full range, to about 3.1 V
def read_millivolts():
if ESP:
return adc.read_uv() / 1000 # calibrated in the chip
return adc.read_u16() * 3300 / 65535
def read_steady_millivolts():
total = 0
n = 0
start = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), start) < WINDOW_MS:
total += read_millivolts()
n += 1
return total / n
while True:
one = read_millivolts()
mean = read_steady_millivolts()
print("one read %.0f mV 50 ms mean %.0f mV" % (one, mean))
time.sleep_ms(250)There is no Uno here: an Uno cannot run MicroPython. MicroPython reads more slowly than C, so fewer reads fit in the window, but they still spread across whole cycles and the ripple still cancels. Stop it with Ctrl-C.
When it does not work
That is the lamp, not the block. Run the sketch here and compare the single read with the 50 ms average: the average should hold within a count or two. Under daylight or a battery torch both columns should be steady, which confirms it.
10 ms is exactly one cycle of the ripple where the mains is 50 Hz, so it works in Europe, Australia and much of Asia. Where the mains is 60 Hz the ripple is 120 Hz, and 10 ms is 1.2 cycles, which leaves part of a wave in every reading. 50 ms is whole cycles of both, so one sketch works everywhere.
The 100 nF with the 4.7 kΩ makes a filter with a corner near 340 Hz, which takes only a few per cent off a 100 Hz ripple. Hiding it would take a capacitor tens of times larger, soldered to a surface-mount pad, and the reading would lag too. Averaging in the sketch does the same job with no soldering.
Each reading takes 50 ms, so at most 20 readings a second. For light in a room that is far more than enough. If the sketch has other work to do, take one reading every second or so rather than one after another.
A TK01 that comes on at dusk, calibrated to your room, and does not flicker while it decides.
A night light →Edit this page — content/books/ambient-light-sensor/the-flicker-under-room-lights.mdx
Questions about this product
See what other owners have asked, and read their solutions.
Ambient Light Sensor
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.