Android IoT, Automotive, & Smart TV Customizations

Adding New Peripherals: Customizing the Linux Kernel for SPI/I2C Devices on Android Things

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction

Android Things provides a robust, Google-backed platform for developing IoT devices, offering the convenience of Android APIs coupled with the underlying power of the Linux kernel. However, many specialized IoT applications require interaction with custom hardware components, such as sensors, actuators, or displays, often communicating via Serial Peripheral Interface (SPI) or Inter-Integrated Circuit (I2C) buses. While Android Things offers peripheral I/O APIs, direct kernel customization is often necessary for advanced scenarios: supporting new, complex devices with dedicated Linux drivers, optimizing performance, or integrating peripherals that require specific kernel-level handling.

This expert-level guide will walk you through the intricate process of customizing the Linux kernel for an Android Things device to natively support new SPI or I2C peripherals. We’ll cover environment setup, kernel source acquisition, driver integration, Device Tree Blob (DTB) modification, kernel compilation, and flashing.

Understanding the Android Things Kernel Environment

At its core, Android Things runs on a standard Linux kernel, enhanced with Android’s userspace framework. This means that many principles of embedded Linux development apply directly. For custom hardware, the kernel needs to be aware of the peripheral’s existence, its bus type (SPI, I2C), its address, and any specific configuration parameters.

The Role of Device Trees

On ARM-based systems, which Android Things predominantly targets (e.g., Raspberry Pi 3, NXP i.MX7D), the hardware configuration is described using Device Trees (DT). A Device Tree is a data structure that describes the hardware components of a system, passing this information to the kernel at boot time. This eliminates the need for hardcoding peripheral addresses and configurations directly into the kernel source code, promoting greater flexibility and portability.

A typical Device Tree node defines properties like compatible (which helps the kernel find the correct driver), reg (the device’s address on the bus), and other specific parameters. For instance, an I2C device node might look like this:

i2c_bus_node { status = "okay"; my_sensor@68 { compatible = "vendor,my-sensor-driver"; reg = <0x68>; interrupts = <0 100 4>; // Example interrupt line };};

Setting Up Your Development Environment

Before diving into kernel modifications, you need a properly configured Linux-based host machine (Ubuntu or Debian is recommended) and specific tools.

  1. Operating System: A modern Linux distribution.
  2. Build Tools: Essential utilities for compiling the kernel.
  3. Cross-Compilation Toolchain: Since you’re compiling for an ARM target from an x86/x64 host, a cross-compiler is mandatory.
  4. Android Things Platform Tools: ADB (Android Debug Bridge) and Fastboot for interacting with your device.
  5. Kernel Source Code: The exact kernel source for your specific Android Things device.

Install the necessary packages:

sudo apt update && sudo apt upgrade sudo apt install git make gcc flex bison libssl-dev bc dwarves python3-pip sudo apt install crossbuild-essential-armhf # For 32-bit ARM (e.g., RPi3) sudo apt install crossbuild-essential-arm64 # For 64-bit ARM (e.g., some i.MX devices)

Obtaining and Preparing the Kernel Source

The first critical step is to obtain the exact kernel source code matching your Android Things device and its platform version. Google typically provides kernel sources through AOSP (Android Open Source Project). For a Raspberry Pi 3, you might find specific Android Things branches on the Raspberry Pi kernel GitHub.

Example: NXP i.MX7D Pico

mkdir android-things-kernel cd android-things-kernel repo init -u https://android.googlesource.com/kernel/manifest -b android-things-4.9-imx_pico-dev --depth=1 repo sync ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make imx_pico_android_defconfig

Example: Raspberry Pi 3 (Hypothetical branch)

git clone --depth 1 https://github.com/raspberrypi/linux.git -b rpi-4.9.y-android-things-build cd linux ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make bcm2709_defconfig

After running `make defconfig`, you’ll have a base configuration for your kernel.

Integrating Your SPI/I2C Device Driver

Most common SPI/I2C peripherals already have existing Linux kernel drivers. If your device is supported, you might only need to enable its driver. If not, you’ll need to port or write one and integrate it into the kernel build system.

Locating or Writing the Driver

Kernel drivers for SPI devices are typically found under `drivers/spi/`, and I2C drivers under `drivers/i2c/`. For this example, let’s assume we’re integrating an Analog Devices ADXL345 accelerometer via SPI.

Modifying Kconfig and Makefiles

You’ll need to inform the kernel build system about your new driver. This involves editing two types of files: `Kconfig` (for configuration options) and `Makefile` (for compiling the source).

  1. Kconfig: Add an entry to `drivers/spi/Kconfig` (or `drivers/i2c/Kconfig`):
config SPI_ADXL345 boolean "ADXL345 SPI Accelerometer" depends on SPI help This enables support for the Analog Devices ADXL345 three-axis accelerometer connected via SPI.
  1. Makefile: Add your driver’s source file to `drivers/spi/Makefile` (or `drivers/i2c/Makefile`):
obj-$(CONFIG_SPI_ADXL345) += adxl345.o

Place your `adxl345.c` (or equivalent) source file in the `drivers/spi/` directory.

Modifying the Device Tree Blob (DTB)

This is arguably the most crucial step for hardware enablement. You need to declare your specific peripheral within the Device Tree source (`.dts`) file. Navigate to `arch/arm/boot/dts/` (or `arch/arm64/boot/dts/` for 64-bit platforms). Find the `.dts` file corresponding to your board (e.g., `imx7d-pico-android-things.dts` or `bcm2709-rpi3-android-things.dts`). You might also need to modify an included `.dtsi` file.

Adding an SPI Device Node Example

Assuming your ADXL345 is connected to SPI bus 0, chip select 0, you would add a node within the SPI bus definition:

&ecspi1 { /* For i.MX7D; might be &spi0 for RPi */ status = "okay"; adxl345@0 { compatible = "adi,adxl345"; reg = <0>; /* Chip Select 0 */ spi-max-frequency = <5000000>; // Max clock frequency interrupts = <GIC_SPI 100 IRQ_TYPE_EDGE_FALLING>; /* Example interrupt */ };};

Adding an I2C Device Node Example

For an I2C device, such as an MPU6050 gyroscope/accelerometer at address 0x68 on I2C bus 1:

&i2c1 { status = "okay"; mpu6050@68 { compatible = "invensense,mpu6050"; reg = <0x68>; interrupts = <GIC_SPI 101 IRQ_TYPE_EDGE_FALLING>; // Example interrupt };};

The `compatible` string is vital; it links the device node to the correct kernel driver.

Building the Customized Kernel

With the driver integrated and the Device Tree modified, it’s time to build your new kernel.

  1. Configure Kernel Options: Run `menuconfig` to enable your newly added driver (and ensure SPI/I2C support is built-in or as a module).
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make menuconfig
  1. Compile Kernel Image and DTBs:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make -j$(nproc) zImage dtbs

This command compiles the kernel image (`zImage` or `Image` in `arch/arm/boot/`) and the Device Tree Blobs (`.dtb` files in `arch/arm/boot/dts/`).

  1. Compile Modules (Optional): If your driver is built as a module, compile them.
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make modules

Flashing the New Kernel to Your Android Things Device

The process of flashing varies slightly by device, but generally involves using `fastboot`.

  1. Connect Device: Connect your Android Things device to your host machine via USB.
  2. Reboot to Bootloader:
adb reboot bootloader
  1. Flash Kernel and DTB: Locate your `zImage` (or `Image`) and the relevant `.dtb` file in the `out/arch/arm/boot/` and `out/arch/arm/boot/dts/` directories, respectively.
fastboot flash boot <path_to_kernel_source>/arch/arm/boot/zImage fastboot flash dtb <path_to_kernel_source>/arch/arm/boot/dts/<your_board>.dtb # Note: Some devices might combine DTB into the boot image or bootloader partition. Consult your device's flashing guide.
  1. Reboot Device:
fastboot reboot

Verifying the Peripheral

Once your Android Things device reboots with the custom kernel, you need to verify that your new peripheral is recognized.

  1. Check Kernel Logs: Use `dmesg` to inspect the kernel boot logs for messages from your driver.
adb shell dmesg | grep adxl345adb shell dmesg | grep mpu6050
  1. Inspect Device Files: Look for corresponding device nodes in `/dev/`.
adb shell ls -l /dev/spidevadb shell ls -l /dev/i2c

If you see your device registered (e.g., `spidev0.0` for an SPI device, or a driver message indicating successful probe), your kernel customization was successful. You can then develop an Android Things application using the Peripheral I/O API to interact with the device through its standard Linux driver interfaces.

Conclusion

Customizing the Linux kernel for Android Things devices empowers developers to integrate a vast array of specialized SPI and I2C peripherals, extending the platform’s capabilities far beyond its out-of-the-box offerings. While requiring a deep understanding of embedded Linux, Device Trees, and kernel build processes, the ability to tailor the kernel opens doors to highly optimized and unique IoT solutions. By meticulously following these steps, you can successfully add new hardware, paving the way for advanced Android Things applications in various domains, from industrial automation to sophisticated smart home devices.

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner