Testing the Proximity and Ambient Light Sensors¶
The Librem 5 phone has a proximity sensor (PS) and ambient light sensor (ALS) provided by the VCNL4040M3OE-H5 module. These sensors can be accessed programmatically via messages sent to the relevant addresses on the appropriate I2C bus. It can be useful to test that these sensors are working before using the appropriate system D-Bus interface.
This guide provides a basic overview of the commands that can be used to obtain ambient light and proximity information from the device using the I2C bus.
The VCNL4040M3OE-H5 module is accessed via an I2C bus address and exposes a set of registers that can be accessed by their addresses in the device. This means that we need two addresses – a bus address and a device address – to access a register.
The kernel provides abstractions that allow the sensors to be accessed fairly simply without requiring knowledge of the message protocol used for I2C devices.
Before trying to access the sensors as the
purism user, you need to
i2c-tools package on the development board:
sudo apt install i2c-tools
Then add the
purism user to the
sudo usermod -a -G i2c purism
This user should now have permission to read and write the device files for the I2C devices and run the tools to access them.
The module is available via address
0x60 on I2C bus 1. This can be
verified by running the
purism@pureos:~$ /usr/sbin/i2cdetect -y 1
This should produce output like the following:
0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1e -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- UU -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: 60 -- -- -- -- -- -- -- -- -- UU -- -- -- -- -- 70: -- -- -- -- -- -- -- --
It should be clear that there is a device at address
0x60 on the I2C bus
– this should be the proximity and ambient light sensor.
We check that the device at address
0x60 on I2C bus 1 is the sensor by
ID_M registers, which should be readable at
0x0c on the device. We can do this with the
/usr/sbin/i2cget -y 1 0x60 0x0c w
The expected value is
0x0186 for the sensor module. If a different value is
returned then the device is not the one we were expecting.
Initially, the device will be powered down. To determine its state we read its
ALS_CONF register at address
0x00 on the device:
/usr/sbin/i2cget -y 1 0x60 0x00 w
This should return
0x0001, indicating that the shut down bit is enabled.
Set it to
0x0000 with the
/usr/sbin/i2cset -y 1 0x60 0x00 0 w
When we no longer need to read from the device, we set the shut down bit again:
/usr/sbin/i2cset -y 1 0x60 0x00 1 w
We can read the state of the device again to verify that it has powered down, if necessary.
With the device powered, ambient light sensor readings can be obtained by
reading from the
ALS_Data_H registers at address
0x09 on the device:
/usr/sbin/i2cget -y 1 0x60 0x09 w
White channel values can be read from the
0x0a on the device:
/usr/sbin/i2cget -y 1 0x60 0x0a w
These should vary as the ambient light level at the sensor changes.
The state of the proximity sensor can be obtained by reading the
PS_CONF2 registers at address
0x03 on the device:
/usr/sbin/i2cget -y 1 0x60 0x03 w
The lowest bit (
PS_SD) of the value returned is
1 if the sensor is
shut down. Set it to
0 to power it up. In the simplest case, we can set
both the registers to
/usr/sbin/i2cset -y 1 0x60 0x03 0 w
Setting the lowest bit to
1 again will shut down the sensor.
With the proximity sensor enabled, values can be obtained for the proximity of
object to the sensor by reading from the
registers at address
0x08 on the device:
/usr/sbin/i2cget -y 1 0x60 0x08 w
Closer objects should result in higher values, up to the maximum value set in
bit 3 (
PS_HD) of the
PS_CONF2 register. The default resolution is 12
python3-smbus package can be used to read these levels. Install it on
the development board Python script with
sudo apt install python3-smbus
The following script will show the ambient light levels every second until it
#!/usr/bin/env python3 import smbus import time # Bus number and i2c address: BUS = 1 ADDRESS = 0x60 # Register addresses: ALS_CONF = 0x00 ALS_Data = 0x09 White_Data = 0x0a # Masks and values: ALS_SD = 1 # Create an object to represent the appropriate bus. bus = smbus.SMBus(BUS) # Power up the ambient light sensor if not already powered. state = bus.read_word_data(ADDRESS, ALS_CONF) if state & ALS_SD == ALS_SD: bus.write_word_data(ADDRESS, ALS_CONF, state ^ ALS_SD) try: while True: print('Ambient:', bus.read_word_data(ADDRESS, ALS_Data)) print('White: ', bus.read_word_data(ADDRESS, White_Data)) time.sleep(1) finally: # Restore the initial state. bus.write_word_data(ADDRESS, ALS_CONF, state)
Similarly, this script will show the proximity sensor’s response to close
objects four times a second:
#!/usr/bin/env python3 import smbus import time # Bus number and i2c address: BUS = 1 ADDRESS = 0x60 # Register addresses: PS_CONF1 = 0x03 PS_Data = 0x08 # Masks and values: PS_SD = 1 # Create an object to represent the appropriate bus. bus = smbus.SMBus(BUS) # Power up the proximity sensor if not already powered. state = bus.read_word_data(ADDRESS, PS_CONF1) if state & PS_SD == PS_SD: bus.write_word_data(ADDRESS, PS_CONF1, state ^ PS_SD) try: while True: print('Proximity:', bus.read_word_data(ADDRESS, PS_Data)) time.sleep(0.25) finally: # Restore the initial state. bus.write_word_data(ADDRESS, PS_CONF1, state)