I2C Access from .NET SDK 8.0 Application : CLR/System.IO.IOException

torizon@verdin-imx8mp-15229850:~$ sudo tdx-info
Password:

Software summary
------------------------------------------------------------
Bootloader:               U-Boot
Kernel version:           5.15.129-6.5.0+git.6f8fd49366db #1-TorizonCore SMP PREEMPT Fri Dec 22 11:15:52 UTC 2023
Kernel command line:      root=LABEL=otaroot rootfstype=ext4 quiet logo.nologo vt.global_cursor_default=0 plymouth.ignore-serial-consoles splash fbcon=map:3 ostree=/ostree/boot.1/torizon/fe091cbe7b665ff6d9d5d618cb20c42c90c242fffeaceccf204eacd186b2f597/0
Distro name:              NAME="TorizonCore"
Distro version:           VERSION_ID=6.5.0-build.8
Distro variant:           VARIANT="Docker"
Hostname:                 verdin-imx8mp-15229850
------------------------------------------------------------

Hardware info
------------------------------------------------------------
HW model:                 Toradex Verdin iMX8M Plus WB on Verdin Development Board
Toradex version:          0058 V1.1B
Serial number:            15229850
Processor arch:           aarch64
------------------------------------------------------------
[Configuration]
Verdin iMX8M Plus Evaluation Kit with Touchscreen
with:
SOM i.MX8M Plus Quad 4GB WB IT v1.1B
Dahlia Carrier Board v1.1D
Verdin DSI to LVDS rev 1.1A
Capacitive Touch Display 10.1" v1.0A

I have created a .NET 8 Console application using the Torizon Wizard.

I have added the NuGet package System.Device.Gpio by issiing the command in my WSL Ubuntu project directory.

flepron@LAPTOP_FRANCOIS:~/dev/DemoGPIO$  dotnet add package System.Device.Gpio

I have launched my application in debug mode on my verdin-imx8mp-15229850, and received the exception as shown in the screen below :

But if I list the device, I have the /dev/i2c-1 which exists.

torizon@verdin-imx8mp-15229850:/dev$ ls -al
total 4
drwxr-xr-x 19 root root        4940 Apr  2 05:44 .
drwxr-xr-x 13 root root        4096 Jan 17 21:06 ..
crw-r--r--  1 root root     10, 235 Apr  2 05:44 autofs
drwxr-xr-x  2 root root         620 Apr  2 05:44 block
crw-------  1 root root     10, 234 Apr  2 05:44 btrfs-control
drwxr-xr-x  3 root root          60 Apr  2 05:44 bus
crw-------  1 root root     10, 126 Apr  2 05:44 caam-keygen
drwxr-xr-x  2 root root        3960 Apr  2 05:44 char
crw-------  1 root root      5,   1 Apr  2 05:44 console
crw-------  1 root root     10, 125 Apr  2 05:44 cpu_dma_latency
crw-------  1 root root     10, 203 Apr  2 05:44 cuse
drwxr-xr-x  7 root root         140 Apr  2 05:44 disk
drwxr-xr-x  2 root root         100 Jan  1  1970 dma_heap
drwxr-xr-x  3 root root         120 Apr  2 05:44 dri
lrwxrwxrwx  1 root root           7 Apr  2 05:44 emmc -> mmcblk2
lrwxrwxrwx  1 root root          12 Apr  2 05:44 emmc-boot0 -> mmcblk2boot0
lrwxrwxrwx  1 root root          12 Apr  2 05:44 emmc-boot1 -> mmcblk2boot1
lrwxrwxrwx  1 root root           9 Apr  2 05:44 emmc-part1 -> mmcblk2p1
crw-rw----  1 root video    29,   0 Apr  2 05:44 fb0
lrwxrwxrwx  1 root root          13 Apr  2 05:44 fd -> /proc/self/fd
crw-rw-rw-  1 root root      1,   7 Apr  2 05:44 full
crw-rw-rw-  1 root root     10, 229 Apr  2 05:44 fuse
crw-rw----  1 root video   199,   0 Apr  2 05:44 galcore
crw-rw-r--  1 root gpio    254,   0 Apr  2 05:44 gpiochip0
crw-rw-r--  1 root gpio    254,   1 Apr  2 05:44 gpiochip1
crw-rw-r--  1 root gpio    254,   2 Apr  2 05:44 gpiochip2
crw-rw-r--  1 root gpio    254,   3 Apr  2 05:44 gpiochip3
crw-rw-r--  1 root gpio    254,   4 Apr  2 05:44 gpiochip4
crw-------  1 root root    237,   0 Apr  2 05:44 hidraw0
crw-------  1 root root    237,   1 Apr  2 05:44 hidraw1
drwxr-xr-x  2 root root           0 Apr  2 05:44 hugepages
crw-------  1 root root     10, 183 Apr  2 05:44 hwrng
crw-rw-r--  1 root i2cdev   89,   0 Apr  2 05:44 i2c-0
crw-rw-r--  1 root i2cdev   89,   1 Apr  2 05:44 i2c-1
crw-rw-r--  1 root i2cdev   89,   2 Apr  2 05:44 i2c-2
crw-rw-r--  1 root i2cdev   89,   3 Apr  2 05:44 i2c-3
crw-------  1 root root    510,   0 Apr  2 05:44 iio:device0
lrwxrwxrwx  1 root root          12 Apr  2 05:44 initctl -> /run/initctl
drwxr-xr-x  4 root root         220 Apr  2 05:44 input
crw-r--r--  1 root root      1,  11 Apr  2 05:44 kmsg
crw-rw-rw-  1 root kvm      10, 232 Apr  2 05:44 kvm
lrwxrwxrwx  1 root root          28 Apr  2 05:44 log -> /run/systemd/journal/dev                                                                                                                                                                                                                                             -log
crw-rw----  1 root disk     10, 237 Apr  2 05:44 loop-control
brw-rw----  1 root disk      7,   0 Apr  2 05:44 loop0
brw-rw----  1 root disk      7,   1 Apr  2 05:44 loop1
brw-rw----  1 root disk      7,   2 Apr  2 05:44 loop2
brw-rw----  1 root disk      7,   3 Apr  2 05:44 loop3
brw-rw----  1 root disk      7,   4 Apr  2 05:44 loop4
brw-rw----  1 root disk      7,   5 Apr  2 05:44 loop5
brw-rw----  1 root disk      7,   6 Apr  2 05:44 loop6
brw-rw----  1 root disk      7,   7 Apr  2 05:44 loop7
drwxr-xr-x  2 root root          60 Apr  2 05:44 mapper
crw-r-----  1 root kmem      1,   1 Apr  2 05:44 mem
brw-rw----  1 root disk    179,   0 Apr  2 05:44 mmcblk2
brw-rw----  1 root disk    179,  32 Apr  2 05:44 mmcblk2boot0
brw-rw----  1 root disk    179,  64 Apr  2 05:44 mmcblk2boot1
brw-rw----  1 root disk    179,   1 Apr  2 05:44 mmcblk2p1
crw-------  1 root root    238,   0 Apr  2 05:44 mmcblk2rpmb
drwxrwxrwt  2 root root          40 Jan  1  1970 mqueue
crw-------  1 root root    235,   0 Apr  2 05:44 mxc_hantro
crw-------  1 root root    234,   0 Apr  2 05:44 mxc_hantro_vc8000e
drwxr-xr-x  2 root root          60 Apr  2 05:44 net
crw-rw-rw-  1 root root      1,   3 Apr  2 05:44 null
crw-r-----  1 root kmem      1,   4 Apr  2 05:44 port
crw-------  1 root root    108,   0 Apr  2 05:44 ppp
crw-rw-rw-  1 root tty       5,   2 Apr  2 09:10 ptmx
crw-------  1 root root    248,   0 Apr  2 05:44 ptp0
drwxr-xr-x  2 root root           0 Apr  2 05:44 pts
crw-------  1 root root      2,   0 Apr  2 05:44 ptyp0
crw-------  1 root root      2,   1 Apr  2 05:44 ptyp1
crw-------  1 root root      2,   2 Apr  2 05:44 ptyp2
crw-------  1 root root      2,   3 Apr  2 05:44 ptyp3
crw-------  1 root root      2,   4 Apr  2 05:44 ptyp4
crw-------  1 root root      2,   5 Apr  2 05:44 ptyp5
crw-------  1 root root      2,   6 Apr  2 05:44 ptyp6
crw-------  1 root root      2,   7 Apr  2 05:44 ptyp7
crw-------  1 root root      2,   8 Apr  2 05:44 ptyp8
crw-------  1 root root      2,   9 Apr  2 05:44 ptyp9
crw-------  1 root root      2,  10 Apr  2 05:44 ptypa
crw-------  1 root root      2,  11 Apr  2 05:44 ptypb
crw-------  1 root root      2,  12 Apr  2 05:44 ptypc
crw-------  1 root root      2,  13 Apr  2 05:44 ptypd
crw-------  1 root root      2,  14 Apr  2 05:44 ptype
crw-------  1 root root      2,  15 Apr  2 05:44 ptypf
brw-rw----  1 root disk      1,   0 Apr  2 05:44 ram0
brw-rw----  1 root disk      1,   1 Apr  2 05:44 ram1
brw-rw----  1 root disk      1,  10 Apr  2 05:44 ram10
brw-rw----  1 root disk      1,  11 Apr  2 05:44 ram11
brw-rw----  1 root disk      1,  12 Apr  2 05:44 ram12
brw-rw----  1 root disk      1,  13 Apr  2 05:44 ram13
brw-rw----  1 root disk      1,  14 Apr  2 05:44 ram14
brw-rw----  1 root disk      1,  15 Apr  2 05:44 ram15
brw-rw----  1 root disk      1,   2 Apr  2 05:44 ram2
brw-rw----  1 root disk      1,   3 Apr  2 05:44 ram3
brw-rw----  1 root disk      1,   4 Apr  2 05:44 ram4
brw-rw----  1 root disk      1,   5 Apr  2 05:44 ram5
brw-rw----  1 root disk      1,   6 Apr  2 05:44 ram6
brw-rw----  1 root disk      1,   7 Apr  2 05:44 ram7
brw-rw----  1 root disk      1,   8 Apr  2 05:44 ram8
brw-rw----  1 root disk      1,   9 Apr  2 05:44 ram9
crw-rw-rw-  1 root root      1,   8 Apr  2 05:44 random
crw-rw-r--  1 root root     10, 242 Apr  2 05:44 rfkill
lrwxrwxrwx  1 root root           4 Apr  2 05:44 rtc -> rtc0
crw-------  1 root root    252,   0 Apr  2 05:44 rtc0
crw-------  1 root root    252,   1 Apr  2 05:44 rtc1
drwxrwxrwt  2 root root          40 Apr  2 05:44 shm
drwxr-xr-x  3 root root         140 Apr  2 05:44 snd
lrwxrwxrwx  1 root root          15 Apr  2 05:44 stderr -> /proc/self/fd/2
lrwxrwxrwx  1 root root          15 Apr  2 05:44 stdin -> /proc/self/fd/0
lrwxrwxrwx  1 root root          15 Apr  2 05:44 stdout -> /proc/self/fd/1
crw-rw-rw-  1 root tty       5,   0 Apr  2 05:44 tty
crw--w----  1 root tty       4,   0 Apr  2 05:44 tty0
crw--w----  1 root tty       4,   1 Apr  2 05:44 tty1
crw--w----  1 root tty       4,  10 Apr  2 05:44 tty10
crw--w----  1 root tty       4,  11 Apr  2 05:44 tty11
crw--w----  1 root tty       4,  12 Apr  2 05:44 tty12
crw--w----  1 root tty       4,  13 Apr  2 05:44 tty13
crw--w----  1 root tty       4,  14 Apr  2 05:44 tty14
crw--w----  1 root tty       4,  15 Apr  2 05:44 tty15
crw--w----  1 root tty       4,  16 Apr  2 05:44 tty16
crw--w----  1 root tty       4,  17 Apr  2 05:44 tty17
crw--w----  1 root tty       4,  18 Apr  2 05:44 tty18
crw--w----  1 root tty       4,  19 Apr  2 05:44 tty19
crw--w----  1 root tty       4,   2 Apr  2 05:44 tty2
crw--w----  1 root tty       4,  20 Apr  2 05:44 tty20
crw--w----  1 root tty       4,  21 Apr  2 05:44 tty21
crw--w----  1 root tty       4,  22 Apr  2 05:44 tty22
crw--w----  1 root tty       4,  23 Apr  2 05:44 tty23
crw--w----  1 root tty       4,  24 Apr  2 05:44 tty24
crw--w----  1 root tty       4,  25 Apr  2 05:44 tty25
crw--w----  1 root tty       4,  26 Apr  2 05:44 tty26
crw--w----  1 root tty       4,  27 Apr  2 05:44 tty27
crw--w----  1 root tty       4,  28 Apr  2 05:44 tty28
crw--w----  1 root tty       4,  29 Apr  2 05:44 tty29
crw--w----  1 root tty       4,   3 Apr  2 05:44 tty3
crw--w----  1 root tty       4,  30 Apr  2 05:44 tty30
crw--w----  1 root tty       4,  31 Apr  2 05:44 tty31
crw--w----  1 root tty       4,  32 Apr  2 05:44 tty32
crw--w----  1 root tty       4,  33 Apr  2 05:44 tty33
crw--w----  1 root tty       4,  34 Apr  2 05:44 tty34
crw--w----  1 root tty       4,  35 Apr  2 05:44 tty35
crw--w----  1 root tty       4,  36 Apr  2 05:44 tty36
crw--w----  1 root tty       4,  37 Apr  2 05:44 tty37
crw--w----  1 root tty       4,  38 Apr  2 05:44 tty38
crw--w----  1 root tty       4,  39 Apr  2 05:44 tty39
crw--w----  1 root tty       4,   4 Apr  2 05:44 tty4
crw--w----  1 root tty       4,  40 Apr  2 05:44 tty40
crw--w----  1 root tty       4,  41 Apr  2 05:44 tty41
crw--w----  1 root tty       4,  42 Apr  2 05:44 tty42
crw--w----  1 root tty       4,  43 Apr  2 05:44 tty43
crw--w----  1 root tty       4,  44 Apr  2 05:44 tty44
crw--w----  1 root tty       4,  45 Apr  2 05:44 tty45
crw--w----  1 root tty       4,  46 Apr  2 05:44 tty46
crw--w----  1 root tty       4,  47 Apr  2 05:44 tty47
crw--w----  1 root tty       4,  48 Apr  2 05:44 tty48
crw--w----  1 root tty       4,  49 Apr  2 05:44 tty49
crw--w----  1 root tty       4,   5 Apr  2 05:44 tty5
crw--w----  1 root tty       4,  50 Apr  2 05:44 tty50
crw--w----  1 root tty       4,  51 Apr  2 05:44 tty51
crw--w----  1 root tty       4,  52 Apr  2 05:44 tty52
crw--w----  1 root tty       4,  53 Apr  2 05:44 tty53
crw--w----  1 root tty       4,  54 Apr  2 05:44 tty54
crw--w----  1 root tty       4,  55 Apr  2 05:44 tty55
crw--w----  1 root tty       4,  56 Apr  2 05:44 tty56
crw--w----  1 root tty       4,  57 Apr  2 05:44 tty57
crw--w----  1 root tty       4,  58 Apr  2 05:44 tty58
crw--w----  1 root tty       4,  59 Apr  2 05:44 tty59
crw--w----  1 root tty       4,   6 Apr  2 05:44 tty6
crw--w----  1 root tty       4,  60 Apr  2 05:44 tty60
crw--w----  1 root tty       4,  61 Apr  2 05:44 tty61
crw--w----  1 root tty       4,  62 Apr  2 05:44 tty62
crw--w----  1 root tty       4,  63 Apr  2 05:44 tty63
crw--w----  1 root tty       4,   7 Apr  2 05:44 tty7
crw--w----  1 root tty       4,   8 Apr  2 05:44 tty8
crw--w----  1 root tty       4,   9 Apr  2 05:44 tty9
crw-rw----  1 root dialout   4,  64 Apr  2 05:44 ttyS0
crw-rw----  1 root dialout   4,  65 Apr  2 05:44 ttyS1
crw-rw----  1 root dialout   4,  66 Apr  2 05:44 ttyS2
crw-rw----  1 root dialout   4,  67 Apr  2 05:44 ttyS3
crw-rw----  1 root dialout 207,  16 Apr  2 05:44 ttymxc0
crw-rw----  1 root dialout 207,  17 Apr  2 05:44 ttymxc1
crw-rw----  1 root dialout 207,  18 Apr  2 05:44 ttymxc2
crw-------  1 root root      3,   0 Apr  2 05:44 ttyp0
crw-------  1 root root      3,   1 Apr  2 05:44 ttyp1
crw-------  1 root root      3,   2 Apr  2 05:44 ttyp2
crw-------  1 root root      3,   3 Apr  2 05:44 ttyp3
crw-------  1 root root      3,   4 Apr  2 05:44 ttyp4
crw-------  1 root root      3,   5 Apr  2 05:44 ttyp5
crw-------  1 root root      3,   6 Apr  2 05:44 ttyp6
crw-------  1 root root      3,   7 Apr  2 05:44 ttyp7
crw-------  1 root root      3,   8 Apr  2 05:44 ttyp8
crw-------  1 root root      3,   9 Apr  2 05:44 ttyp9
crw-------  1 root root      3,  10 Apr  2 05:44 ttypa
crw-------  1 root root      3,  11 Apr  2 05:44 ttypb
crw-------  1 root root      3,  12 Apr  2 05:44 ttypc
crw-------  1 root root      3,  13 Apr  2 05:44 ttypd
crw-------  1 root root      3,  14 Apr  2 05:44 ttype
crw-------  1 root root      3,  15 Apr  2 05:44 ttypf
crw-------  1 root root     10, 124 Apr  2 05:44 ubi_ctrl
crw-------  1 root root     10, 223 Apr  2 05:44 uinput
crw-rw-rw-  1 root root      1,   9 Apr  2 05:44 urandom
drwxr-xr-x  2 root root          60 Apr  2 05:44 usb
drwxr-xr-x  3 root root          60 Apr  2 05:44 v4l
crw-rw----  1 root tty       7,   0 Apr  2 05:44 vcs
crw-rw----  1 root tty       7,   1 Apr  2 05:44 vcs1
crw-rw----  1 root tty       7,   2 Apr  2 05:44 vcs2
crw-rw----  1 root tty       7,   3 Apr  2 05:44 vcs3
crw-rw----  1 root tty       7,   4 Apr  2 05:44 vcs4
crw-rw----  1 root tty       7,   5 Apr  2 05:44 vcs5
crw-rw----  1 root tty       7,   6 Apr  2 05:44 vcs6
crw-rw----  1 root tty       7, 128 Apr  2 05:44 vcsa
crw-rw----  1 root tty       7, 129 Apr  2 05:44 vcsa1
crw-rw----  1 root tty       7, 130 Apr  2 05:44 vcsa2
crw-rw----  1 root tty       7, 131 Apr  2 05:44 vcsa3
crw-rw----  1 root tty       7, 132 Apr  2 05:44 vcsa4
crw-rw----  1 root tty       7, 133 Apr  2 05:44 vcsa5
crw-rw----  1 root tty       7, 134 Apr  2 05:44 vcsa6
crw-rw----  1 root tty       7,  64 Apr  2 05:44 vcsu
crw-rw----  1 root tty       7,  65 Apr  2 05:44 vcsu1
crw-rw----  1 root tty       7,  66 Apr  2 05:44 vcsu2
crw-rw----  1 root tty       7,  67 Apr  2 05:44 vcsu3
crw-rw----  1 root tty       7,  68 Apr  2 05:44 vcsu4
crw-rw----  1 root tty       7,  69 Apr  2 05:44 vcsu5
crw-rw----  1 root tty       7,  70 Apr  2 05:44 vcsu6
lrwxrwxrwx  1 root root          94 Apr  2 05:44 verdin-adc1 -> /sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0049/iio:device0/in_voltage3_raw
lrwxrwxrwx  1 root root          94 Apr  2 05:44 verdin-adc2 -> /sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0049/iio:device0/in_voltage2_raw
lrwxrwxrwx  1 root root          94 Apr  2 05:44 verdin-adc3 -> /sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0049/iio:device0/in_voltage1_raw
lrwxrwxrwx  1 root root          94 Apr  2 05:44 verdin-adc4 -> /sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0049/iio:device0/in_voltage0_raw
lrwxrwxrwx  1 root root           5 Apr  2 05:44 verdin-i2c-on-module -> i2c-0
lrwxrwxrwx  1 root root           5 Apr  2 05:44 verdin-i2c1 -> i2c-3
lrwxrwxrwx  1 root root           5 Apr  2 05:44 verdin-i2c2 -> i2c-1
lrwxrwxrwx  1 root root           5 Apr  2 05:44 verdin-i2c4 -> i2c-2
lrwxrwxrwx  1 root root           7 Apr  2 05:44 verdin-uart1 -> ttymxc0
lrwxrwxrwx  1 root root           7 Apr  2 05:44 verdin-uart2 -> ttymxc1
lrwxrwxrwx  1 root root           7 Apr  2 05:44 verdin-uart3 -> ttymxc2
lrwxrwxrwx  1 root root           8 Apr  2 05:44 verdin-watchdog -> watchdog
lrwxrwxrwx  1 root root           9 Apr  2 05:44 verdin-watchdog-soc -> watchdog                                                                                                                                                                                                                                             0
drwxr-xr-x  2 root root          60 Jan  1  1970 vfio
crw-------  1 root root     10, 127 Apr  2 05:44 vga_arbiter
crw-------  1 root root     10, 137 Apr  2 05:44 vhci
crw-rw----  1 root video    81,   0 Apr  2 05:44 video0
crw-rw----  1 root video    81,   1 Apr  2 05:44 video1
crw-------  1 root root    100,   0 Apr  2 05:44 vsi_daemon_ctrl
crw-------  1 root root     10, 130 Apr  2 05:44 watchdog
crw-------  1 root root    246,   0 Apr  2 05:44 watchdog0
crw-rw-rw-  1 root root      1,   5 Apr  2 05:44 zero
brw-rw----  1 root disk    253,   0 Apr  2 05:44 zram0

Could you please help me on this topic ?

Many Thanks.
Francois.

Hello @flepron,

The issue you are running into is likely related to the container permissions to access the I2C controller.
You can read more about this on the Expose Hardware to Containers section on the developer website.

Regarding your specific use case of I2C, you will need to make sure that the docker-compose.yml file gives the container the necessary permissions. To do that, both the debug and production services will need to have the necessary access.

There are two ways to do this, as follows:

1- Add the device to the docker compose file. This method allows for access to only the specified I2C controllers on the container.

version: "3.9"
services:
  <debug-container-name>:
    build:
      context: .
      dockerfile: Dockerfile.debug
    ...
    devices:
      - "/dev/i2c-1"

  <container-name>:
    build:
      context: .
      dockerfile: Dockerfile
    ...
    devices:
      - "/dev/i2c-1"

2- Add a bind mount of the /dev directory and corresponding I2C cgroup rules to the docker compose file. This method allows for access to all I2C controllers on the container.

version: "3.9"
services:
    <debug-container-name>:
    build:
      context: .
      dockerfile: Dockerfile.debug
    ...
    volumes:
      - type: bind
        source: /dev
        target: /dev
    device_cgroup_rules:
      # For I2C
      - 'c 89:* rmw'

  <container-name>:
    build:
      context: .
      dockerfile: Dockerfile
    ...
    volumes:
      - type: bind
        source: /dev
        target: /dev
    device_cgroup_rules:
      # For I2C
      - 'c 89:* rmw'

Best Regards,
Bruno

Hi Bruno,

We have made the modification that you’ve suggested, and this has fixed our problem in creating the device object to access the I2C 1 at address 0x57 (87d), but now we have problem either in reading of in writing to this I2C interface.

Before the modification you’ve advised to us, our docker-compose.yml was:

version: "3.9"
services:
  demo-gpio-debug:
    build:
      context: .
      dockerfile: Dockerfile.debug
    image: ${LOCAL_REGISTRY}:5002/demo-gpio-debug:${TAG}
    ports:
      - 2222:2222

  demo-gpio:
    build:
      context: .
      dockerfile: Dockerfile
    image: ${DOCKER_LOGIN}/demo-gpio:${TAG}

And after, docker-compose.yml is like that:

version: "3.9"
services:
  demo-gpio-debug:
    build:
      context: .
      dockerfile: Dockerfile.debug
    image: ${LOCAL_REGISTRY}:5002/demo-gpio-debug:${TAG}
    ports:
      - 2222:2222
    volumes:
      - type: bind
        source: /dev
        target: /dev
    device_cgroup_rules:
      # For I2C
      - 'c 89:* rmw'

  demo-gpio:
    build:
      context: .
      dockerfile: Dockerfile
    image: ${DOCKER_LOGIN}/demo-gpio:${TAG}
    volumes:
      - type: bind
        source: /dev
        target: /dev

We have now an execption raised when reading or writing in I2C 1 at adresss 0x57.

In both case, the exception is still a System.IO.IOException, where the exception message is “Error 6 performing I2C data transfer”.

We have listed on our Dahlia board and with our Verdin SOM i.MX8M Plus, the I2C interfaces which are available, as well as the addresses for each of them.

Here is the result:

torizon@verdin-imx8mp-15229850:~$ ls -la /dev | grep i2c*
crw-rw-r--  1 root i2cdev   89,   0 Apr  2 05:44 i2c-0
crw-rw-r--  1 root i2cdev   89,   1 Apr  2 05:44 i2c-1
crw-rw-r--  1 root i2cdev   89,   2 Apr  2 05:44 i2c-2
crw-rw-r--  1 root i2cdev   89,   3 Apr  2 05:44 i2c-3
lrwxrwxrwx  1 root root          94 Apr  2 05:44 verdin-adc1 -> /sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0049/iio:device0/in_voltage3_raw
lrwxrwxrwx  1 root root          94 Apr  2 05:44 verdin-adc2 -> /sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0049/iio:device0/in_voltage2_raw
lrwxrwxrwx  1 root root          94 Apr  2 05:44 verdin-adc3 -> /sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0049/iio:device0/in_voltage1_raw
lrwxrwxrwx  1 root root          94 Apr  2 05:44 verdin-adc4 -> /sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0049/iio:device0/in_voltage0_raw
lrwxrwxrwx  1 root root           5 Apr  2 05:44 verdin-i2c-on-module -> i2c-0
lrwxrwxrwx  1 root root           5 Apr  2 05:44 verdin-i2c1 -> i2c-3
lrwxrwxrwx  1 root root           5 Apr  2 05:44 verdin-i2c2 -> i2c-1
lrwxrwxrwx  1 root root           5 Apr  2 05:44 verdin-i2c4 -> i2c-2

torizon@verdin-imx8mp-15229850:~$ ls -l /dev/verdin-i2c*
lrwxrwxrwx 1 root root 5 Apr  2 05:44 /dev/verdin-i2c-on-module -> i2c-0
lrwxrwxrwx 1 root root 5 Apr  2 05:44 /dev/verdin-i2c1 -> i2c-3
lrwxrwxrwx 1 root root 5 Apr  2 05:44 /dev/verdin-i2c2 -> i2c-1
lrwxrwxrwx 1 root root 5 Apr  2 05:44 /dev/verdin-i2c4 -> i2c-2

torizon@verdin-imx8mp-15229850:~$ i2cdetect -l
i2c-0   i2c             30a20000.i2c                            I2C adapter
i2c-1   i2c             30a30000.i2c                            I2C adapter
i2c-2   i2c             30a40000.i2c                            I2C adapter
i2c-3   i2c             30a50000.i2c                            I2C adapter

#this checks the MPU6050 configuration availability on Torizon OS
torizon@verdin-imx8mp-15229850:~$ zcat /proc/config.gz | grep -i 6050
CONFIG_INV_MPU6050_IIO=m
CONFIG_INV_MPU6050_I2C=m
CONFIG_INV_MPU6050_SPI=m

torizon@verdin-imx8mp-15229850:~$ i2cdetect -y -r 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- UU -- -- -- -- -- -- -- -- -- --
30: -- -- UU -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- UU UU -- -- -- -- -- --
50: UU -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

torizon@verdin-imx8mp-15229850:~$ i2cdetect -y -r 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

torizon@verdin-imx8mp-15229850:~$ i2cdetect -y -r 2
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

torizon@verdin-imx8mp-15229850:~$ i2cdetect -y -r 3
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- UU -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: UU -- -- -- -- -- -- -- -- -- UU -- -- -- -- UU
50: UU -- -- -- -- -- -- UU -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

By this, we conclude, but this can be a misunderstanding from our side, that the /dev/verdin-i2c1 points on i2c-3, and at address 0x57 on i2c-3, there is UU which means that this is a sensitive address where read or write operations can be performed.

This is our understanding from the page : How to use I²C on Torizon OS, but I prefer to be honest in saying that my linux knowledge and know-how are super low, and I just start with Torizon.

We have heard that there are utilities like ic2get or i2ctransfer which could help us to know what’s going on i2c interfaces, but we do not know how to install these utilities on Torizon and how to use them.

Could please help us on fixing this new issue on reading and writing on I2C in our C# program, please ?

Sincerely,
François.

Hello @flepron,

The modifications to the docker-compose.yml file are mostly correct, the only problem is the missing cgroup rules entry on the release container.
This will not affect the issue you are facing now, as you are running the debug container.
To avoid problems in the future I would advise that you also add the following lines to the demo-gpio service on the docker-compose file:

    device_cgroup_rules:
      # For I2C
      - 'c 89:* rmw'

This conclusion is correct.


The UU at address 0x57 usually means that there is a device at that address and a driver is loaded and using that device.
I think that is the case here, which would actually mean that it should not be read or written directly via the application.

The following picture on the How to use I²C on Torizon OS article illustrates the two different ways which I2C devices can be accessed on Linux:

  • Using the generic I2C Userspace API or
  • Using the API for the device-specific driver

These approaches are interchangeable and cannot be used at the same time for the same device.


Address 0x57 on /dev/i2c-3 on the Dahlia Carrier Board is used for an EEPROM.
Is this the device which you are trying to use?

If so, using the driver you can write and read from it as if it was a normal file at /sys/class/i2c-dev/i2c-3/device/3-0057/eeprom.
To do this, remember to bind mount this file in your docker-compose file, similar to how you did for the /dev directory.

Another possible path is to disable the eeprom driver and use the I2C interface directly, as you are trying to do. If you decide to go this route, you will need to disable the eeprom node with a devicetree overlay.
We have documentation on how to do that on Device Tree Overlays on Torizon | Toradex Developer Center.

Best Regards,
Bruno

Hi Bruno,

Sorry for my late answer, and many thanks for your additional information on the rights for the release mode, that I have forgotten to integrate in my docker-compose.yml file which is now equal to:

version: "3.9"
services:
  i2c-read-eeprom-debug:
    build:
      context: .
      dockerfile: Dockerfile.debug
    image: ${LOCAL_REGISTRY}:5002/i2c-read-eeprom-debug:${TAG}
    ports:
      - 2230:2230
    volumes:
      - type: bind
        source: /dev
        target: /dev
    device_cgroup_rules:
      # For I2C
      - 'c 89:* rmw'

  i2c-read-eeprom:
    build:
      context: .
      dockerfile: Dockerfile
    image: ${DOCKER_LOGIN}/i2c-read-eeprom:${TAG}
    volumes:
      - type: bind
        source: /dev
        target: /dev
    device_cgroup_rules:
      # For I2C
      - 'c 89:* rmw'    

Here are the results of what I have been able to do so far, in order to explain you what I want to do with my the application coded in C, but first I did some tests using the shell and i2ctools.

I have used the i2cset and i2cget to write and read in the 24C02 EEPROM which is located at the address 0x57 on the I2C-3 bus, knowing that the I2C-1 of the Verdin i.MX8M Plus SODIMM is pointing on the I2C-3 as you can see here:

torizon@verdin-imx8mp-15229850:~$ ls -l /dev/verdin-i2c*
lrwxrwxrwx 1 root root 5 Apr  2 05:44 /dev/verdin-i2c-on-module -> i2c-0
lrwxrwxrwx 1 root root 5 Apr  2 05:44 /dev/verdin-i2c1 -> i2c-3
lrwxrwxrwx 1 root root 5 Apr  2 05:44 /dev/verdin-i2c2 -> i2c-1
lrwxrwxrwx 1 root root 5 Apr  2 05:44 /dev/verdin-i2c4 -> i2c-2
#write at @90 of the EEPROM the byte value 0x24
torizon@verdin-imx8mp-15229850:~$ i2cset -y -f 3 0x57 0x90 0x24
#read at @90 of the EEPROM : it returns me the value I have previously written 0x24
torizon@verdin-imx8mp-15229850:~$ i2cget -y -f 3 0x57 0x90
0x24
#write at @90 of the EEPROM the byte value 0xF1
torizon@verdin-imx8mp-15229850:~$ i2cset -y -f 3 0x57 0x90 0xF1
#read at @90 of the EEPROM : it returns me the value I have previously written 0xF1
torizon@verdin-imx8mp-15229850:~$ i2cget -y -f 3 0x57 0x90
0xf1
#here, I make a dump of the content of the EEPROM, and I am able to see that at @90, there is 0xF1 so the dump works.
torizon@verdin-imx8mp-15229850:~$ i2cdump -y -f 3 0x57
No size specified (using byte-data access)
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 00 40 01 cf 02 40 08 00 01 00 01 00 03 00 9b 00    .@???@?.?.?.?.?.
10: 01 40 21 00 86 82 ac 00 ff ff ff ff ff ff ff ff    ?@!.???.........
20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
90: f1 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ?...............
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................

On the Dahlia board v1.1D, there is also a temperature sensor TMP75 on the I2C-3 bus at address 0x4F.

So to read the temperature, I have processed the following commands:

torizon@verdin-imx8mp-15229850:~$ valhex=`i2cget -f -y 3 0x4f 0 b`
torizon@verdin-imx8mp-15229850:~$ printf "temp:%d DegC\n" $valhex
temp:28 DegC

So, knowing that I was able to write and read in the EEPROM using i2cset, and i2sget, I have tried to reproduce the same thing in a C code application, which is bellow:

#include <stdio.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

#define EEPROM_ADDR 0x57  // I2C address of your EEPROM
#define I2C_FILE_NAME "/dev/i2c-1"  // I2C device file, adjust for your system


int main() {
    int file;
    char buffer[256] = {0};

    // Open the I2C device file
    if ((file = open(I2C_FILE_NAME, O_RDWR)) < 0) {
        perror("Failed to open the i2c bus");
        return 1;
    }

    // Specify the I2C slave address
    if (ioctl(file, I2C_SLAVE, EEPROM_ADDR) < 0) {
        perror("Failed to acquire bus access and/or talk to slave");
        return 1;
    }

   // Read from the EEPROM
    int result ;
    if ((result = read(file, buffer, sizeof(buffer))) != sizeof(buffer)) {
        perror("Failed to read from the i2c bus");
        return 1;
    }

    // Print the read data
    for (int i = 0; i < sizeof(buffer); i++) {
        printf("buffer[%d] = %d\n", i, buffer[i]);
    }

    return 0;
}

The problem I am facing is when I call the function read here :

if ((result = read(file, buffer, sizeof(buffer))) != sizeof(buffer)) {

the result is equal to -1 which means that the read failed.

The call to the functions open and ioctl work, but the call to read fails.

I do not why.

Could you please help me on this , please?

Sincerely,
François.

Hello @flepron,

You cannot read from the 0x57 address on your application because the device is being used by the driver.

The i2cget command uses ioctl directly to access the I2C devices.
This seems to bypass the fact that the I2C device at address 0x57 (and 0x4F) are being used by drivers.

The best path forward is to not try to read and write to the EEPROM and read the temperature from the temperature sensor by using I2C directly. The drivers which are enabled by default take care of this task, and you just need to use the driver interface:

  • For the EEPROM: /sys/class/i2c-dev/i2c-3/device/3-0057/eeprom
  • For the TMP75C temperature sensor: /sys/class/i2c-dev/i2c-3/device/3-004f/hwmon/hwmon2/temp1_input

Note that the temperature is in thousands of a ºC (30125 → 30.125ºC).


If you want to access the I2C devices directly via I2C, you will need to disable their nodes on the devicetree. This can be done with a devicetree overlay.
For more information on how to do this, I recommend the following pages:

Best Regards,
Bruno

Hi Bruno,

I do not want to disable the drivers for the EEPROM neither for the the TMP75C, but I do not know how to use ioct for reading and writing.

I see that in the code you’ve advised to have a look at:

static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command,
				     int size, union i2c_smbus_data *data)
{
	struct i2c_smbus_ioctl_data args;

	args.read_write = read_write;
	args.command = command;
	args.size = size;
	args.data = data;
	return ioctl(file, I2C_SMBUS, &args);
}

Do you have a sample code in C which does the read or the write in the EEPROM using the driver ?

Many thanks.

Sincerely,
François.

Hello @flepron,

The i2cget code I mentioned was just to show how it uses ioctl, this would not be a recommended approach.

I made a quick sample showing how to work with the EEPROM driver and the TMP75C driver.
It requires some configuration changes to your project:

Add Bind Mounts and user argument to docker-compose.yml file:

...
    user: 0:0
    volumes:
      - type: bind
        source: /sys/class/i2c-dev/i2c-3/device/3-0057/eeprom
        target: /carrier_board_eeprom
      - type: bind
        source: /sys/class/i2c-dev/i2c-3/device/3-004f/hwmon/hwmon2/temp1_input
        target: /carrier_board_temp

Change the user within the container to be root, by editing the .vscode/settings.json file:

...
"torizon_run_as": "root",
...

The sample file only reads the first 32 bytes of the EEPROM and gets the temperature from the temperature sensor. It opens the EEPROM in rwb mode, so data can be written to it:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[]){
    printf("Hello Torizon!\n");

    FILE *eepromFile = fopen("/carrier_board_eeprom", "rwb");

    if(eepromFile == NULL){
        printf("Failed to open eeprom\n");
        return -1;
    }

    FILE *temperatureFile = fopen("/carrier_board_temp", "r");

    if(temperatureFile == NULL){
        printf("Failed to open temperature\n");
        fclose(eepromFile);
        return -1;
    }

    uint8_t eepromData[32];
    char temperatureString[32];

    // Run 10 cycles
    for(int i = 0; i < 10; i++){
        // Read first 32 bytes of EEPROM
        fseek(eepromFile, 0, SEEK_SET);
        fread(eepromData, 1, sizeof(eepromData), eepromFile);
        // Print Read Data
        printf("EEPROM:");
        for(int j = 0; j < sizeof(eepromData); j++){
            printf(" %X", eepromData[j]);
        }
        printf("\n");

        // Read temperature
        fseek(temperatureFile, 0, SEEK_END);
        // Get the current file pointer position, which is the file size
        size_t fileSize = ftell(temperatureFile);
        // Reset the file pointer back to the beginning of the file
        fseek(temperatureFile, 0, SEEK_SET);
        
        // Read temperature string
        fread(temperatureString, 1, fileSize, temperatureFile);
        // Convert to floating point
        float temperature = atoi(temperatureString)/1000.0;

        printf("Temperature: %f ÂşC\n", temperature);
        fflush(stdout);

        // Wait for 1 second
        usleep(1000000);
    }

    // Close files
    fclose(temperatureFile);
    fclose(eepromFile);

    return 0;
}

In this code I used fopen, but open could also be used.

Best Regards,
Bruno

1 Like

Hi Bruno,

Many thanks for this example for devices where there are drivers in the system for the interfaces that we want to use, because behind the communication interface, you have a device.

I have 2 questions.

I am a completely new on Torizon and how to access devices through a Linux system because I am much more an embedded engineer coding directly in a MCU like the Cortex-M7 under FreeRTOS, but as I have searched a lot on developer.toradex.com for complete instructions and code samples in C or C#, and unfortunately, I did not find a complete example as you did in all your posts on this thread, when you specify that the developer has to bind the peripherals in the docker-compose.yml and specify that in .vscode/settings.json file: that the user must be the root user.

So, my first question, is: Is there a git repository or another location on developer.toradex.com where there are full instructions (step by step) and samples as you gave me along this thread ?

If so, I would be really happy to know the location or how speed up my application development which uses a lot communication interfaces like I2C and ADC, because we plan to customize our SBC through Linear Computing, and we just need communication interfaces, and not so much integrated devices like an EEPROM or a TMP75C. This is really interesting to know how to handle these devices for the future, but for our POC we first need to know how to handle communication interfaces in application written in C or C#.

Our device are mainly switches to turn on or off relays and these switches are reachable via I2C. We use I2C switches where there are 8 outputs, and each output drive a relay.

We have connected our I2C switch located on a breadboard to the connector X20 of our Dahlia board using the pin J5 (GND), J6 (SDA), J7 (SCL) through a level shifter (1.8 V to 5V), and when we try to know if the I2C slave peripheral is detected, there is no problem with i2cdetect.

torizon@verdin-imx8mp-15229850:~$ i2cdetect -y -r 3
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- UU -- -- --
30: -- -- -- -- -- -- -- -- 1B -- -- -- -- -- -- --
40: UU -- -- -- -- -- -- -- -- -- UU -- -- -- -- UU
50: UU -- -- -- -- -- -- UU -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

We have used the tool i2cset and it works when I issue the command i2cset -y -f 3 0x38 0xFF for closing all relays, and i2cset -y -f 3 0x38 0x00 for opening all relays.

So, my second question is : what is the configuration I must make in docker-compose.yml, and in .vscode/settings.json file:, and maybe somewhere else in order to drive my I2C device which does not need a driver because we only write a byte which is a bit mask for each relay, and would it be possible if you can provide me a C code sample for driving my relays in an application instead of using a command line utility such as i2cset ?

Many thanks for your help.

This is really appreciated because Toradex’s SOMs is a wonderful technology but this is a bit like the Everest for me because I am a newbie.

Sincerely,
François.

Hello @flepron,

Yes, we have a section on the developer website that is dedicated to this, but not yet with the specific example I sent you.
The documentation is available on Peripheral Access Documentation Overview | Toradex Developer Center.

There is also the Torizon Samples repository:

  • bookworm branch: Contains some useful samples but most of them are not yet ported to use the Torizon IDE extension.
  • bookworm-new branch: Contains fewer samples, but they are all based on the Torizon IDE extension.

If you don’t need a driver, then one of the following will work:

While we do have a sample with direct I2C access, this sample is in Python.
The sample is still useful to see the necessary permissions for the container, which I already covered above.

For a c implementation, the following documentation is a good starting point: Implementing I2C device drivers in userspace — The Linux Kernel documentation

Best Regrads,
Bruno

Hello @bruno.tx ,

Thank you very much for all these links.

I’ve have been able thanks to your precious links to code my application in C.
So I share it with the community if this can be helpful for other newbies like me.

#include <stdlib.h>
#include <stdio.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define DEVICE_I2C_BUS 3
#define DEVICE_I2C_ADDRESS 0x38

int main() {
    int file;
    char filename[20];

    snprintf(filename, 19, "/dev/i2c-%d", DEVICE_I2C_BUS);
    file = open(filename, O_RDWR);
    if (file < 0) {
        /* ERROR HANDLING */
        exit(1);
    }

    //Set device I2C as slave device and 7bits address
    if (ioctl(file, I2C_SLAVE, DEVICE_I2C_ADDRESS) < 0) {
        /* ERROR HANDLING */
        exit(1);
    }

    __uint8_t buf[1] = {0x00};

    for(int loop=0;loop < 256;loop++) {
        
        printf("Relays behind PCF8574AT %0X\n",buf[0]);
        fflush(stdout);
        if (write(file, buf, 1) != 1) {
            /* ERROR HANDLING */
            exit(1);
        }

        usleep(100000); // 100 ms
        
        buf[0] += 0x01  ;

    }

    return 0;
}

And docker-compose.yml file:

version: "3.9"
services:
  drive-i2c-3-address-0x38-debug:
    build:
      context: .
      dockerfile: Dockerfile.debug
    image: ${LOCAL_REGISTRY}:5002/drive-i2c-3-address-0x38-debug:${TAG}
    ports:
      - 2230:2230
    volumes:
      - type: bind
        source: /dev
        target: /dev
    device_cgroup_rules:
      # For I2C
      - 'c 89:* rmw'

  drive-i2c-3-address-0x38:
    build:
      context: .
      dockerfile: Dockerfile
    image: ${DOCKER_LOGIN}/drive-i2c-3-address-0x38:${TAG}
    volumes:
      - type: bind
        source: /dev
        target: /dev
    device_cgroup_rules:
      # For I2C
      - 'c 89:* rmw'

Many thanks for all. :+1:t4: :100:

Sincerely,
François.

Hello @flepron,

Thanks for sharing your implementation!

Best Regards,
Bruno