Is it dark yet ? – Measuring brightness with LDR on Pi Pico (W) and ESP8266 (D1 Mini & NodeMCU v3) in MicroPython

Materials you need for this:

LDRs (I am using 5528), breadboard, jumper wires, resistor (I used 56k) and a 5V power supply.
The MicroPython code has been written for and tested on Pi Pico, Pi Pico W, D1 Mini and NodeMCU v3 ESP8266 .
I bought it at berrybase.de and az-delivery.de (not referral links). The microcontrollers are mostly cheaper at berrybase, but the other parts often cheaper at az-delivery (also at amazon.)

Please don’t put all the microcontrollers on one breadboard. The chance of short-circuiting something is too high. This is just to show which GPIOs I used. They are the same as used in the code as well.

The first code block shows a very simple way to read the voltage value which is in the value range 0 to 65535.
(machine.ADC also has a read() method which returns 0-1024, but this does not exist on the Pico, so I used the read_u16() as it is supported by all the boards I tried)

from machine import ADC
from time import sleep

# D1 mini and NodeMCU v3 -> 0, Pi Pico 26, 27 or 28
light_sensor = ADC(28)

while True:
    print(light_sensor.read_u16())
    sleep(0.5)

In this version I added a conversion to percent which reduces the accuracy, but is more easy to use. Using a constant avoids recalculation of a value that is not changing.
As I want to use the measured brightness level to adapt the intensity of a night light, this was good enough.

from machine import ADC
from time import sleep

VOLTAGE_TO_PERCENT_CONSTANT =  100 / 65535 # transforms measured voltage to percent

# D1 mini and NodeMCU v3 -> 0, Pi Pico 26, 27 or 28
light_sensor = ADC(28)


def voltage_to_percent(voltage: int) -> int:
    return round(voltage * VOLTAGE_TO_PERCENT_CONSTANT)


while True:
    print(voltage_to_percent(light_sensor.read_u16()))
    sleep(0.5)

The following script uses an LED stripe to compensate accordingly when it gets darker.
As the script is a little more complex a configuration section was added to remove any magic numbers in the code.
Functions were named to tell what they are doing and variables what they stand for. This always helps when reading code someone else (or yourself a while ago) has written.

The basic behavior stayed the same. The voltage is measured in an interval. But the value is now used to change the RGB values of the given color. The color stays the same because the RGB values are evenly changed with the same factor, but the brigthness is changed.
As you probably don’t want it to shine fully at night, a configuration value for maximum brightness percent was added.
As the LDR resistor is lower when it is bright, the measured value had to be inversed to compensate for darkness, not add to brightness.
Additionally the RGB value is only changed when the measured voltage percent has changed.

from machine import ADC, Pin
from time import sleep
from neopixel import NeoPixel

# CONFIG
LIGHT_SENSOR_PIN = 28  # D1 mini and NodeMCU v3 -> 0, Pi Pico 26, 27 or 28
MEASURE_INTERVAL_S = 0.5
PIXEL_PIN = 4
PIXEL_COUNT = 8
MAX_BRIGHTNESS = 100  # percentage: between 0 and 100
COLOR = (255, 247, 239)  # RGB values for warm white

# CONSTANTS
MAX_BRIGHTNESS_PERCENT = MAX_BRIGHTNESS / 100
# calculates the RGB color base values considering max brightness
RGB = tuple([round(v * MAX_BRIGHTNESS_PERCENT) for v in COLOR])
# rgb color value resolution considering max brightness
RGB_LED_RESOLUTION = round(max(RGB) * MAX_BRIGHTNESS_PERCENT)
VOLTAGE_RESOLUTION = 65535
# transforms voltage to RGB resulution - is rounded to int after transforming
VOLTAGE_TRANSFORM_FACTOR = round(RGB_LED_RESOLUTION / VOLTAGE_RESOLUTION, 6)

# USED PINS
light_sensor = ADC(LIGHT_SENSOR_PIN)
pixels = NeoPixel(Pin(PIXEL_PIN), PIXEL_COUNT)


def transform_resolution(voltage: int) -> int:
    return round(voltage * VOLTAGE_TRANSFORM_FACTOR)


def rgb_value(value: int, brightness: int) -> int:
    return round(value - ((value / RGB_LED_RESOLUTION) * brightness))


def rgb_values(brightness: int) -> [int, int, int]:
    return [rgb_value(v, brightness) for v in RGB]


previous_value = None
while True:
    measured_brightness = transform_resolution(light_sensor.read_u16())
    if measured_brightness != previous_value:
        pixels.fill(rgb_values(measured_brightness))
        pixels.write()
        previous_value = measured_brightness
    sleep(MEASURE_INTERVAL_S)

Have fun tinkering! If you have any questions, bug findings or just want to say hello – leave a comment!

Similar Posts

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.