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.
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.
Preparations¶
Before trying to access the sensors as the purism
user, you need to
install the i2c-tools
package on the development board:
sudo apt install i2c-tools
Then add the purism
user to the i2c
group:
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.
Accessing the Module¶
The module is available via address 0x60
on I2C bus 1. This can be
verified by running the i2cdetect
tool:
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.
Reading the Device ID¶
We check that the device at address 0x60
on I2C bus 1 is the sensor by
querying its ID_L
and ID_M
registers, which should be readable at
address 0x0c
on the device. We can do this with the i2cget
tool:
/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.
Powering the Ambient Light Sensor¶
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 i2cset
tool:
/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.
Reading the Ambient Light Level¶
With the device powered, ambient light sensor readings can be obtained by
reading from the ALS_Data_L
and 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 White_Data_L
and White_Data_H
registers at 0x0a
on the device:
/usr/sbin/i2cget -y 1 0x60 0x0a w
These should vary as the ambient light level at the sensor changes.
Powering the Proximity Sensor¶
The state of the proximity sensor can be obtained by reading the PS_CONF1
and 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 0x00
:
/usr/sbin/i2cset -y 1 0x60 0x03 0 w
Setting the lowest bit to 1
again will shut down the sensor.
Reading Proximity Data¶
With the proximity sensor enabled, values can be obtained for the proximity of
object to the sensor by reading from the PS_Data_L
and PS_Data_M
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
bits.
Using Python to Access the Sensors¶
The python3-smbus
package can be used to read these levels. Install it on
the development board Python script with apt
:
sudo apt install python3-smbus
The following script will show the ambient light levels every second until it
is terminated: ambient.py
#!/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: proximity.py
#!/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)