Enabling an Industrial I/O Driver on Raspberry Pi 3 running Arch Linux ARM

MPU9250 from TDK is a three in one Gyro + Accelerometer + Compass sensor with an I²C interface. Mainline Linux kernel has the source code for it's driver which reads values from the sensor via I²C and exposes it via the filesystem using the Industrial I/O subsystem. Arch Linux ARM for Raspberry Pi does not come with the driver for this pre-compiled and enabled. Here is how to compile and enable the driver on Raspberry Pi 3 Model B.


Raspberry Pi 3 Model B with MPU9250 connected to it's I²C interface
and a USB-Serial widget to access console over serial

Connections

Buy the MPU9250 Widget from Amazon and hook it up to your Raspberry Pi as per the table

RPi3 (2.54mm)

MPU9250 Module
(2.54mm)

3.3V PWR Pin 1

VCC

GND Pin 9

GND

I2C1_SCL GPIO 3 Pin 5

SCL

I2C1_SDA GPIO 2 Pin 3

SDA

GPIO4 Pin 7

INT

Preparations

  1. Prepare and boot Arch Linux ARM from SD Card: instructions are here.
  2. Connect the Raspberry Pi to your home network using an ethernet cable.
  3. Look up the DHCP assigned IP address from your router’s configuration web interface, it will show as a new device named alarmpi.
  4. Gain access to the console over SSH using PuTTY or MobaXterm or physically using keyboard/mouse/monitor.
    Login: alarm and Password: alarm
  5. While following the instructions below, switch to root user whenever required using:
    1. su root
    2. Password: root

Enable Console over USB-Serial [Optional]

Follow instructions over at https://gist.github.com/yeokm1/d6c3ca927919c61257cd

Install required packages on Arch Linux ARM running on RPi3B

  • pacman-key --init
  • pacman-key --populate archlinuxarm
  • pacman -Syu
  • pacman -S curl bash ca-certificates nano i2c-tools jq iio-utils
  • pacman -S base-devel
  • pacman -S dtc git
  • pacman -S linux-raspberrypi-headers
  • pacman -S python python-pip

Enable I²C on Raspberry Pi 3 Model B running Arch Linux ARM

  1. su root
  2. nano /etc/modules-load.d/raspberrypi.conf
  3. Append:
    1. i2c-dev
    2. Save and Reboot
  4. nano /boot/config.txt
  5. Append:
    1. dtparam=i2c=on
    2. Save and Reboot
  6. Test (0x68 is MPU9250):
    [root@alarmpi alarm]# i2cdetect -y 1
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- -- -- -- --
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
    70: -- -- -- -- -- -- -- --

 Python Library for reading from MPU9250 over I²C

  1. Install python packages (Latest version from its git repository, replace the version numbers with latest one)
    1. python -m pip install git+https://github.com/FaBoPlatform/FaBo9AXIS-MPU9250-Python.git@78e4da28e176a07cc1c22c2f61a5be7ffe6c2c28

  2. Clone the git repository to fetch the example code
    1. git clone https://github.com/FaBoPlatform/FaBo9AXIS-MPU9250-Python.git

  3. First test in text mode using example from FaBo9AXIS-MPU9250-Python repo (This uses raw I2C bus without the need for MPU9250 Industrial I/O Driver):
    [root@alarmpi ~]# python FaBo9AXIS-MPU9250-Python/example/read9axis.py
     ax =  -0.871
     ay =  0.39
     az =  0.31
     gx =  -0.793
     gy =  0.725
     gz =  -0.099
     mx =  30.289
     my =  44.197
     mz =  -24.791
     ax =  -0.872
     ay =  0.392
     az =  0.312
     gx =  -0.801
     gy =  0.71
     gz =  -0.069
     mx =  30.647
     my =  43.475
     mz =  -25.832

Enabling MPU9250 Industrial I/O interface on ArchLinux running on Raspberry Pi 3

  1. Fetch (from mainline kernel repository) the latest sources of inv-mpu6050 which has support for MPU9250 through shallow clone/sparse checkout, execute the following commands while in /root:
    1. su root
    2. mkdir linux_main_src
    3. cd linux_main_src
    4. git init
    5. git remote add origin https://github.com/torvalds/linux.git
    6. git config core.sparsecheckout true
    7. echo "drivers/iio/*" >> .git/info/sparse-checkout
    8. git pull --depth=3 origin master
  2. Create the Makefile for compiling the driver:
    1. Get into /root/linux_main_src/drivers/iio/imu/inv_mpu6050
    2. And modify the Makefile present there for the inv_mpu6080 driver to compile on and for Raspberry Pi 3 platform (lines in blue will be present already, lines in red are the ones that need to be appended, make sure to use a single tab and not spaces at the beginning of the lines below all: and clean:):
    3. Makefile:

      # SPDX-License-Identifier: GPL-2.0
      #
      # Makefile for Invensense MPU6050 device.
      #

      obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o
      inv-mpu6050-y := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o \
                       inv_mpu_aux.o inv_mpu_magn.o

      obj-$(CONFIG_INV_MPU6050_I2C) += inv-mpu6050-i2c.o
      inv-mpu6050-i2c-y := inv_mpu_i2c.o inv_mpu_acpi.o

      obj-$(CONFIG_INV_MPU6050_SPI) += inv-mpu6050-spi.o
      inv-mpu6050-spi-y := inv_mpu_spi.o

      all:
          make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
          gzip -f inv-mpu6050-i2c.ko
          gzip -f inv-mpu6050-spi.ko
          gzip -f inv-mpu6050.ko

      clean:
          make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
          rm inv-mpu6050-i2c.ko.gz
          rm inv-mpu6050-spi.ko.gz
          rm inv-mpu6050.ko.gz


  3. Change to /root
    cd /root

  4. Create an device tree overlay source file for MPU9250:
    nano /root/mpu9250.dts

  5. Add the contents and save:

    // Definitions for MPU9250
    // Combination of:
    // https://github.com/raspberrypi/linux/blob/rpi-4.9.y/arch/arm/boot/dts/overlays/mpu6050-overlay.dts
    // and https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt

    // Driver codes:
    // https://github.com/torvalds/linux/blob/master/drivers/iio/imu/inv_mpu6050/
    // and
    // https://github.com/torvalds/linux/tree/master/drivers/iio/magnetometer

    /dts-v1/;
    /plugin/;

    / {
        compatible = "brcm,bcm2708";

        fragment@0 {
                target = <&i2c1>;
                __overlay__ {
                        #address-cells = <1>;
                        #size-cells = <0>;
                        status = "okay";
                        clock-frequency = <400000>;

                        mpu9250: mpu9250@68 {
                                compatible = "invensense,mpu9250";
                                reg = <0x68>;
                                interrupt-parent = <&gpio>;
                                interrupts = <4 1>;
                                mount-matrix = "1",  /* x0 */
                                "0",  /* y0 */
                                "0",  /* z0 */
                                "0",  /* x1 */
                                "1",  /* y1 */
                                "0",  /* z1 */
                                "0",  /* x2 */
                                "0",  /* y2 */
                                "1";  /* z2 */

                                i2c-gate {
                                        #address-cells = <1>;
                                        #size-cells = <0>;
                                        ak8963@c {
                                                compatible = "asahi-kasei,ak8963";
                                                reg = <0x0c>;
                                        };
                                };
                        };
                };
        };

        __overrides__ {
                interrupt = <&mpu9250>,"interrupts:0";
        };
    };




  6. Compile the device tree source and place it in overlays folder:
    dtc -I dts -O dtb -o /boot/overlays/mpu9250.dtbo -b 0 -@ /root/mpu9250.dts

  7. Update /boot/config.txt and add the following lines to it, save it and reboot:
    dtoverlay=mpu9250

  8. Read the Accelerometer and Magnetometer values like so:
     
    [alarm@alarmpi ~]$ cat /sys/bus/iio/devices/iio\:device0/in_accel_x_raw
    -794
    [alarm@alarmpi ~]$ cat /sys/bus/iio/devices/iio\:device0/in_accel_y_raw
    271
    [alarm@alarmpi ~]$ cat /sys/bus/iio/devices/iio\:device0/in_accel_z_raw
    16950
    [alarm@alarmpi ~]$ cat /sys/bus/iio/devices/iio\:device1/in_magn_x_raw
    42
    [alarm@alarmpi ~]$ cat /sys/bus/iio/devices/iio\:device1/in_magn_y_raw
    -14
    [alarm@alarmpi ~]$ cat /sys/bus/iio/devices/iio\:device1/in_magn_z_raw
    43

Python Bar Graph with Industrial I/O Driver

  1. Install python packages (Latest versions from its git repository, replace the version number with latest one)
    1. pacman -S libjpeg
    2. python -m pip install git+https://github.com/peterbrittain/asciimatics.git@476159c95c5b6e34a782ea97503785ad95683805

  2. Clone the git repository to fetch the example code
    1. git clone https://github.com/peterbrittain/asciimatics.git

  3. Then run following code using python for live bar graph:

    from asciimatics.effects import Print

    from asciimatics.renderers import BarChart, FigletText, SpeechBubble, Box

    from asciimatics.scene import Scene

    from asciimatics.screen import Screen

    from asciimatics.exceptions import ResizeScreenError

    import os

    import sys

    import math

    import time


    accel_x = open('/sys/bus/iio/devices/iio:device0/in_accel_x_raw', 'r')

    accel_y = open('/sys/bus/iio/devices/iio:device0/in_accel_y_raw', 'r')

    accel_z = open('/sys/bus/iio/devices/iio:device0/in_accel_z_raw', 'r')


    gyro_x = open('/sys/bus/iio/devices/iio:device0/in_anglvel_x_raw', 'r')

    gyro_y = open('/sys/bus/iio/devices/iio:device0/in_anglvel_y_raw', 'r')

    gyro_z = open('/sys/bus/iio/devices/iio:device0/in_anglvel_z_raw', 'r')


    mag_x = open('/sys/bus/iio/devices/iio:device1/in_magn_x_raw', 'r')

    mag_y = open('/sys/bus/iio/devices/iio:device1/in_magn_y_raw', 'r')

    mag_z = open('/sys/bus/iio/devices/iio:device1/in_magn_z_raw', 'r')


    def _speak(screen, text, pos, start):

      return Print(

        screen,

        SpeechBubble(text, None, uni=screen.unicode_aware),

        x=pos[0] + 4, y=pos[1] - 4,

        colour=Screen.COLOUR_WHITE,

        clear=True,

        start_frame=start,

        stop_frame=start+50)


    def accelx():

      accel_x.seek(0)

      accel = int(accel_x.readline())

      accel = accel/16000 + 1

      return accel if accel < 2 else 2


    def accely():

      accel_y.seek(0)

      accel = int(accel_y.readline())

      accel = accel/16000 + 1

      return accel if accel < 2 else 2


    def accelz():

      accel_z.seek(0)

      accel = int(accel_z.readline())

      accel = accel/16000 + 1

      return accel if accel < 2 else 2


    def gyrox():

      gyro_x.seek(0)

      gyro = int(gyro_x.readline())

      gyro = gyro/300 + 1

      return gyro if gyro < 2 else 2


    def gyroy():

      gyro_y.seek(0)

      gyro = int(gyro_y.readline())

      gyro = gyro/300 + 1

      return gyro if gyro < 2 else 2


    def gyroz():

      gyro_z.seek(0)

      gyro = int(gyro_z.readline())

      gyro = gyro/300 + 1

      return gyro if gyro < 2 else 2


    def magx():

      mag_x.seek(0)

      mag = int(mag_x.readline())

      mag = mag/100 + 1

      return mag if mag < 2 else 2


    def magy():

      mag_y.seek(0)

      mag = int(mag_y.readline())

      mag = mag/100 + 1

      return mag if mag < 2 else 2


    def magz():

      mag_z.seek(0)

      mag = int(mag_z.readline())

      mag = mag/100 + 1

      return mag if mag < 2 else 2


    def demo(screen):

        scenes = []

        effects = [

            Print(screen,

                  BarChart(

                      10, 40,

                      [accelx, accely, accelz],

                      colour=[c for c in range(1, 4)],

                      bg=[c for c in range(1, 4)],

                      scale=2.0,

                      axes=BarChart.X_AXIS,

                      intervals=0.5,

                      labels=True,

                      border=False),

                  x=0, y=0, transparent=False, speed=2),

              _speak(screen, "Accelerometer", (0, 14), 0),

              Print(screen,

                  BarChart(

                      10, 40,

                      [gyrox, gyroy, gyroz],

                      colour=[c for c in range(7, 10)],

                      bg=[c for c in range(7, 10)],

                      scale=2.0,

                      axes=BarChart.X_AXIS,

                      intervals=0.5,

                      labels=True,

                      border=False),

                  x=0, y=15, transparent=False, speed=2),

              _speak(screen, "Gyroscope", (0, 29), 0),

              Print(screen,

                  BarChart(

                      10, 40,

                      [magx, magy, magz],

                      colour=[c for c in range(4, 8)],

                      bg=[c for c in range(4, 8)],

                      scale=2.0,

                      axes=BarChart.X_AXIS,

                      intervals=0.5,

                      labels=True,

                      border=False),

                  x=44, y=0, transparent=False, speed=2),

              _speak(screen, "Magnetometer", (50, 14), 0),

        ]

        scenes.append(Scene(effects, -1))

        screen.play(scenes, stop_on_resize=True)




    while True:

        try:

            Screen.wrapper(demo)

            sys.exit(0)

        except ResizeScreenError:

            pass

 


Try rotating the MPU9250 widget and watch the bars change.
 

Enable WiFi on your Raspberry Pi 3 Running Arch Linux ARM [Optional]

  1. su root
  2. sudo pacman -S netctl
  3. nano /etc/netctl/wireless-home
  4. Copy and paste the following contents and modify the SSID and passphrase to match your WiFi network
    Description='A simple WPA encrypted wireless connection'
    Interface=wlan0
    Connection=wireless
    Security=wpa

    IP=dhcp
    ESSID='MyNetwork'

    # Prepend hexadecimal keys with \"
    # If your key starts with ", write it as '""<key>"'
    # See also: the section on special quoting rules in netctl.profile(5)
    Key='WirelessKey'
    # Uncomment this if your ssid is hidden
    #Hidden=yes
    # Set a priority for automatic profile selection
    #Priority=10
  5. netctl enable /etc/netctl/wireless-home
  6. reboot 

Comments

  1. hello sir, please I have a problem on my raspberry I'm trying to enable iio but when I enter into sys/buy/iio/devices I didn't get iio file inside devices please help me thanks .

    ReplyDelete
  2. Hi,
    I did the same you for dtover mpu9250 on raspberry pi4, but I could not find the path /sys/bus/iio/devices/iio\:device1
    what linux version are you using? I'm user version 5.15.26
    Thanks

    ReplyDelete

Post a Comment

Popular posts from this blog

Replacing the current sense resistor in Portable Chargers/Power Banks for powering low power DIY projects.

Interface USB Mouse to your Arduino using CH375B

Simple TCP client server sockets application using IPv6 and IPv6