Pinephone - A simple compass app (Kompass ? :°)
Rédigé par SuperGNU Aucun commentaireForeword : This article is a log of the process I followed to develop a simple compass app for Manjaro Plasma Mobile on the Pinephone (the app in itself should be compatible with more systems and devices but this is my primary target and test device incidentally). It should be considered as such and certainly not as a tutorial to develop a new app. Further disclaimer, I am not even a developer, I am just an electrical engineer playing with Linux and software, my knowledge is really lacking and I'm sure several steps are unnecessary and/or plain wrong
Genesis of the project
I was looking for a small project to put me back on track with my Braveheart smartphone since I haven't been playing much with it since the last article. On the plasma-mobile "random project assignor" I got this. The linked problem to solve is this one Seems like a perfect project, simple enough, small so that it doesn't takes up a ton of time, with a useful and concrete result. I'm sold !
Preliminary research
According to this list the Pinephone compas is a ST LIS3MDL sensor. Actually, according to :
STmicroelectronics LIS3MDL ultra-low-power three-axis magnetometer, LGA-12 2.0x2.0x1.0 mm [datasheet] Note: The LIS3MDL is currently unavailable, so it has been replaced in the PinePhone Beta Edition with the Voltafield AF8133L e-Compass, which is unlisted on the Voltafield web site, but the AF8133J is listed. Presumably U1200 will be unpopulated and U1203 will be populated in the Beta Edition, since they appear to be alternatives.
And :
U1203: Asahi Kasei Microdevices (AKM) AK09911 3-axis electronic compass IC with Hall sensor, 8-pin WL-CSP (BGA), 1.2×1.2×0.5 mm or Voltafield Technology Corp. (VTC) AF8133J 3-axis electronic compass with proprietary anisotropic magneto resistive (AMR) technology, 8-pin WLCSP 1.2x1.2x0.5 mm Note: These parts appear to be alternatives to be used if the LIS3MDL is unavailable, so U1203 was probably unpopulated in BraveHeart and the Community Editions, but will be populated in the Beta Edition.
It's a little bit more complicated but given that I have the BraveHeart edition I apparently have a LIS3MDL
Eventually, I suppose the default Plasma-mobile (let's dream :)) compass application (Kompass obviously :)) will have to support every Pinephone configuration (and every supported smartphone). I'm not sure about how an application is supposed to handle that but I have the feeling that if the magnetometer is correctly managed by the kernel as a device, it should be exposed in a generic way in the user space... I started to poke around a little bit and found this ST driver for ST mems (what about the other brands ??) following a link from this thread. Also this random repo, not sure what it does.
After flashing a fresh Manjaro Plasma-mobile on my Pinephone, i tried :
[sylvain@plasma-mobile /]$ sudo find . -name "*lis3mdl*"
[sudo] password for sylvain:
find: ‘./proc/28998’: No such file or directory
./sys/firmware/devicetree/base/__symbols__/lis3mdl
[sylvain@plasma-mobile /]$
This is great news ! Apparently, the magnetometer is in the device tree !
[sylvain@plasma-mobile /]$cat ./sys/firmware/devicetree/base/__symbols__/lis3mdl
/soc/i2c@1c2b000/magnetometer@1e^@
[sylvain@plasma-mobile /]$
And it's connected on an I2C bus ! (From the datasheet it could also have been SPI) This means if I'm not mistaken that the device is mapped in memory at the address 0x1c2b000. So I tried :
[sylvain@plasma-mobile /]$ sudo hexdump -C --skip 0x1c2b000 /dev/mem | head
hexdump: /dev/mem: Bad address
01c2b000
Without success... Same happens for the following addresses. According to this, it's apparently normal :
You should also notice that there is no ranges property in the i2c@1,0 node. The reason for this is that unlike the external bus, devices on the i2c bus are not memory mapped on the CPU's address domain. Instead, the CPU indirectly accesses the rtc@58 device via the i2c@1,0 device. The lack of a ranges property means that a device cannot be directly accessed by any device other than it's parent.
So let's install some tools to play with I2C devices. With
[sylvain@plasma-mobile /]$ pacman -Syu i2c-tools
Now we can scan the I2C bus to fincd which devices are connected:
[sylvain@plasma-mobile /]$ sudo i2cdetect 0
[sudo] password for sylvain:
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-0.
I will probe address range 0x08-0x77.
Continue? [Y/n] Y
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: 30 31 32 33 34 35 36 -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: 50 51 52 -- 54 55 -- 57 58 59 5a 5b 5c 5d -- 5f
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
[sylvain@plasma-mobile /]$ sudo i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x08-0x77.
Continue? [Y/n] Y
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- UU -- -- -- UU -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- UU -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
[sylvain@plasma-mobile /]$ sudo i2cdetect 2
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-2.
I will probe address range 0x08-0x77.
Continue? [Y/n] Y
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- UU --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
[sylvain@plasma-mobile /]$ sudo i2cdetect 3
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-3.
I will probe address range 0x08-0x77.
Continue? [Y/n] Y
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
[sylvain@plasma-mobile /]$ sudo i2cdetect 4
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-4.
I will probe address range 0x08-0x77.
Continue? [Y/n] Y
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- UU -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- UU -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
[sylvain@plasma-mobile /]$ sudo i2cdetect 5
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-5.
I will probe address range 0x08-0x77.
Continue? [Y/n] Y
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- UU --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
For all the I2C bus (apparently five of them) there are 11 devices managed by a driver and 20 connected devices but not managed by a driver according to this excerpt from there
"--". The address was probed but no chip answered. "UU". Probing was skipped, because this address is currently in use by a driver. This strongly suggests that there is a chip at this address. An address number in hexadecimal, e.g. "2d" or "4e". A chip was found at this address.
The default addresses for the lis3mdl (according to the datasheet) are marked as "UU" which means there's probably a driver already loaded for the magnetometer. Let's try to find which kernel modules are loaded to see if some of them can be identified to a magnetometer driver :
[sylvain@plasma-mobile ~]$ lsmod
Module Size Used by
rfcomm 53248 16
bnep 28672 2
zram 28672 2
zsmalloc 32768 1 zram
ov5640 32768 2
crct10dif_ce 20480 1
8723cs 1449984 0
sun50i_codec_analog 32768 1
sun8i_adda_pr_regmap 16384 1 sun50i_codec_analog
hci_uart 57344 0
btrtl 24576 1 hci_uart
btbcm 24576 1 hci_uart
st_magn_spi 16384 0
bluetooth 430080 44 btrtl,hci_uart,btbcm,bnep,rfcomm
st_sensors_spi 16384 1 st_magn_spi
st_magn_i2c 16384 0
panel_sitronix_st7703 16384 0
st_magn 20480 2 st_magn_i2c,st_magn_spi
st_sensors_i2c 16384 1 st_magn_i2c
inv_mpu6050_i2c 16384 0
st_sensors 20480 3 st_magn_i2c,st_magn,st_magn_spi
inv_mpu6050 32768 2 inv_mpu6050_i2c
industrialio_triggered_buffer 16384 2 inv_mpu6050,st_magn
stk3310 16384 0
kfifo_buf 16384 1 industrialio_triggered_buffer
cfg80211 315392 1 8723cs
anx7688 36864 0
ecdh_generic 16384 1 bluetooth
ecc 28672 1 ecdh_generic
rfkill 28672 11 bluetooth,cfg80211
sun8i_codec 53248 1
sun4i_i2s 24576 2
snd_soc_ec25 16384 1
snd_soc_bt_sco 16384 1
snd_soc_simple_card 20480 2
snd_soc_simple_amplifier 16384 1
snd_soc_simple_card_utils 20480 1 snd_soc_simple_card
snd_soc_core 151552 8 sun4i_i2s,snd_soc_bt_sco,sun50i_codec_analog,sun8i_codec,snd_soc_simple_amplifier,snd_soc_simple_card_utils,snd_soc_ec25,snd_soc_simple_card
snd_pcm_dmaengine 20480 1 snd_soc_core
snd_pcm 102400 4 sun4i_i2s,sun8i_codec,snd_soc_core,snd_pcm_dmaengine
snd_timer 40960 1 snd_pcm
snd 65536 8 snd_timer,sun8i_codec,snd_soc_core,snd_pcm
display_connector 16384 0
soundcore 16384 1 snd
leds_sgm3140 16384 0
i2c_gpio 16384 0
sun4i_lradc_keys 16384 0
[sylvain@plasma-mobile ~]$
We have st_magn_spi and st_magn (for I2C), this sound promising ! A little bit more research leads to libiio which is a library for interfacing with Linux IIO (Industrial Input Output) devices from Analog Devices. It's actually installed on my Manjaro image and seems to be used to interface with the magnetometer. The first iio command to see which sensors are managed by the lib already contains tons of information :
[sylvain@plasma-mobile ~]$ iio_info
Library version: 0.21 (git tag: v0.21)
Compiled with backends: local xml ip usb serial
IIO context created with local backend.
Backend version: 0.21 (git tag: v0.21)
Backend description string: Linux plasma-mobile 5.12.0-0-MANJARO-ARM #1 SMP Mon Apr 26 10:36:03 UTC 2021 aarch64
IIO context has 2 attributes:
local,kernel: 5.12.0-0-MANJARO-ARM
uri: local:
IIO context has 5 devices:
iio:device0:
5 channels found:
voltage2: (input)
2 channel-specific attributes found:
attr 0: raw value: 3801
attr 1: scale value: 1.100000
current1: (input)
2 channel-specific attributes found:
attr 0: raw value: 0
attr 1: scale value: 1
voltage1: (input)
2 channel-specific attributes found:
attr 0: raw value: 0
attr 1: scale value: 0.800000
current2: (input)
2 channel-specific attributes found:
attr 0: raw value: 110
attr 1: scale value: 1
temp: (input)
3 channel-specific attributes found:
attr 0: offset value: -2667
attr 1: raw value: 2864
attr 2: scale value: 100
No trigger on this device
iio:device1: stk3310
2 channels found:
proximity: (input)
5 channel-specific attributes found:
attr 0: integration_time value: 0.000370
attr 1: integration_time_available value: 0.000185 0.000370 0.000741 0.001480 0.002960 0.005920 0.011840 0.023680 0.047360 0.094720 0.189440 0.378880 0.757760 1.515520 3.031040 6.062080
attr 2: raw value: 10
attr 3: scale value: 0.100000
attr 4: scale_available value: 6.4 1.6 0.4 0.1
illuminance: (input)
5 channel-specific attributes found:
attr 0: integration_time value: 0.094720
attr 1: integration_time_available value: 0.000185 0.000370 0.000741 0.001480 0.002960 0.005920 0.011840 0.023680 0.047360 0.094720 0.189440 0.378880 0.757760 1.515520 3.031040 6.062080
attr 2: raw value: 0
attr 3: scale value: 0.100000
attr 4: scale_available value: 6.4 1.6 0.4 0.1
No trigger on this device
iio:device2: mpu6050 (buffer capable)
9 channels found:
accel_x: (input, index: 0, format: be:S16/16>>0)
6 channel-specific attributes found:
attr 0: calibbias value: 1052
attr 1: matrix value: 0, 0, 0; 0, 0, 0; 0, 0, 0
attr 2: mount_matrix value: 0, 1, 0; -1, 0, 0; 0, 0, -1
attr 3: raw value: -32
attr 4: scale value: 0.000598
attr 5: scale_available value: 0.000598 0.001196 0.002392 0.004785
accel_y: (input, index: 1, format: be:S16/16>>0)
6 channel-specific attributes found:
attr 0: calibbias value: 281
attr 1: matrix value: 0, 0, 0; 0, 0, 0; 0, 0, 0
attr 2: mount_matrix value: 0, 1, 0; -1, 0, 0; 0, 0, -1
attr 3: raw value: -1594
attr 4: scale value: 0.000598
attr 5: scale_available value: 0.000598 0.001196 0.002392 0.004785
accel_z: (input, index: 2, format: be:S16/16>>0)
6 channel-specific attributes found:
attr 0: calibbias value: 896
attr 1: matrix value: 0, 0, 0; 0, 0, 0; 0, 0, 0
attr 2: mount_matrix value: 0, 1, 0; -1, 0, 0; 0, 0, -1
attr 3: raw value: 15226
attr 4: scale value: 0.000598
attr 5: scale_available value: 0.000598 0.001196 0.002392 0.004785
temp: (input)
3 channel-specific attributes found:
attr 0: offset value: 12420
attr 1: raw value: -90
attr 2: scale value: 2.941176
anglvel_x: (input, index: 4, format: be:S16/16>>0)
5 channel-specific attributes found:
attr 0: calibbias value: 0
attr 1: mount_matrix value: 0, 1, 0; -1, 0, 0; 0, 0, -1
attr 2: raw value: -44
attr 3: scale value: 0.001064724
attr 4: scale_available value: 0.000133090 0.000266181 0.000532362 0.001064724
anglvel_y: (input, index: 5, format: be:S16/16>>0)
5 channel-specific attributes found:
attr 0: calibbias value: 0
attr 1: mount_matrix value: 0, 1, 0; -1, 0, 0; 0, 0, -1
attr 2: raw value: -8
attr 3: scale value: 0.001064724
attr 4: scale_available value: 0.000133090 0.000266181 0.000532362 0.001064724
anglvel_z: (input, index: 6, format: be:S16/16>>0)
5 channel-specific attributes found:
attr 0: calibbias value: 0
attr 1: mount_matrix value: 0, 1, 0; -1, 0, 0; 0, 0, -1
attr 2: raw value: -15
attr 3: scale value: 0.001064724
attr 4: scale_available value: 0.000133090 0.000266181 0.000532362 0.001064724
timestamp: (input, index: 7, format: le:S64/64>>0)
gyro: (input, WARN:iio_channel_get_type()=UNKNOWN)
1 channel-specific attributes found:
attr 0: matrix value: 0, 0, 0; 0, 0, 0; 0, 0, 0
3 device-specific attributes found:
attr 0: current_timestamp_clock value: realtime
attr 1: sampling_frequency value: 50
attr 2: sampling_frequency_available value: 10 20 50 100 200 500
2 buffer-specific attributes found:
attr 0: data_available value: 0
attr 1: watermark value: 1
Current trigger: trigger0(mpu6050-dev2)
iio:device3: lis3mdl (buffer capable)
4 channels found:
magn_x: (input, index: 0, format: le:S16/16>>0)
3 channel-specific attributes found:
attr 0: raw value: -5566
attr 1: scale value: 0.000146
attr 2: scale_available value: 0.000146 0.000292 0.000438 0.000584
magn_y: (input, index: 1, format: le:S16/16>>0)
3 channel-specific attributes found:
attr 0: raw value: 1332
attr 1: scale value: 0.000146
attr 2: scale_available value: 0.000146 0.000292 0.000438 0.000584
magn_z: (input, index: 2, format: le:S16/16>>0)
3 channel-specific attributes found:
attr 0: raw value: 1793
attr 1: scale value: 0.000146
attr 2: scale_available value: 0.000146 0.000292 0.000438 0.000584
timestamp: (input, index: 3, format: le:S64/64>>0)
3 device-specific attributes found:
attr 0: current_timestamp_clock value: realtime
attr 1: sampling_frequency value: 80
attr 2: sampling_frequency_available value: 1 2 3 5 10 20 40 80
2 buffer-specific attributes found:
attr 0: data_available value: 0
attr 1: watermark value: 1
ERROR: checking for trigger : Input/output error (-5)
trigger0: mpu6050-dev2
0 channels found:
No trigger on this device
Actually, the device interface is exposed as files :
[sylvain@plasma-mobile ~]$ cd /sys/bus/iio/devices/iio\:device3
[sylvain@plasma-mobile iio:device3]$ ls
buffer in_magn_x_raw in_magn_z_raw power subsystem
current_timestamp_clock in_magn_x_scale in_magn_z_scale sampling_frequency trigger
dev in_magn_y_raw name sampling_frequency_available uevent
in_magn_scale_available in_magn_y_scale of_node scan_elements
[sylvain@plasma-mobile iio:device3]$
And
[sylvain@plasma-mobile iio:device3]$ cat in_magn_x_raw
-5512
So we can read from the sensor easily ! Just by opening the file and reading the value. Time to start a basic app with Plasma mobile app dev guide
The guide specify to install :
git cmake make extra-cmake-modules g++ qt5-qmake qt5-default qtdeclarative5-dev libqt5svg5-dev qtquickcontrols2-5-dev libkf5config-dev kirigami2-dev qtmultimedia5-dev
But I only had to install :
gcc
qt5
Some usefull links :
https://www.balena.io/blog/sensors-and-data-logging-with-embedded-linux-the-ultimate-guide-part-1/
https://wiki.analog.com/resources/tools-software/linux-software/libiio/iio_readdev
https://stackoverflow.com/questions/18688763/why-is-istream-ostream-slow
https://sourceforge.net/projects/iioutils/files/iioutils/
I quickly put together a small example app : https://github.com/Gnu-Bricoleur/Kompass The code is disgusting, it's the first commit on this repository.
It's really slow because the default sampling frequency is 1Hz. With this, we can speed things up
[sylvain@plasma-mobile iio:device2]$ sudo su
[sudo] password for sylvain:
[root@plasma-mobile iio:device2]# echo 80 > sampling_frequency
(Yes, it's now device2, somehow the device number change between reboot ...)
A good source of information on I2C in general : https://learn.sparkfun.com/tutorials/raspberry-pi-spi-and-i2c-tutorial/all