Skip to content Skip to sidebar Skip to footer

Accessing Gpio Low Power Connector On Dragonboard 410c Running Android

I'm working with a recently purchased DragonBoard 410C running the Android 5.1 operating system and using Android Studio with Kotlin for generating a sample application that explor

Solution 1:

After reviewing a number of alternatives, it appears that the easiest approach for accessing the GPIO pins of the DragonBoard 410C running Android 5.1 was to use the legacy sysfs special device files method.

I'm not sure if this is the only workable solution. Using Android Things as well as using libgpiod both seem to require a more recent Linux kernel than Android 5.1 uses.

I have written an article on CodeProject.com providing details about working up this solution. See Using Windows 10 for Development with DragonBoard 410C and Android.

Which Android runs which Linux kernel? https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel

Android Version    |API Level  |Linux Version in AOSP                    |Header Version
----------------------------------------------------------------------------------------
4.4   Kit Kat      |19, 20     |(3.10)                                   |2.6.18
5.x   Lollipop     |21, 22     |(3.16.1)                                 |3.14.0
6.0   Marshmallow  |23         |(3.18.10)                                |3.18.10

This method also seems to be the easiest in that the library used is written in Kotlin as well.

Using the legacy sysfs special device GPIO interface

See this StackOverFlow post about Linux pseudo files and special device files and the legacy sysfs interface for GPIO pins, what is the /sys/class/gpio/export and `/sys/class/gpio/unexport mechanism and what is the underlying sysfs functionality? .

I found a simple GPIO library written in Java that provided necessary source code. Android Studio has a tool that converted the Java to Kotlin which I included into my project. The source code is below in files Gpio.kt and GpioProcessor.kt.

However in order for this to work I had to make a change to my DragonBoard startup scripts in order to make sure that the necessary special device files were created and available with the proper permissions allowing a user program to manipulate the GPIO pins.

The following procedure is from the Coursera class Internet of Things: Sensing and Actuation from Devices, Lesson 5a: Access GPIO through programs (Android) video #2, Modify boot script. The procedure was to:

  • use adb to pull a copy of /etc/init.qcom.post_boot.sh from the DragonBoard to my PC
  • use Notepad to modify the shell script to create the special device files
  • use adb to push the modified copy back to the Dragonboard
  • use adb to reboot the DragonBoard

The additional shell code to add to the bottom of /etc/init.qcom.post_boot.sh is as follows. However these special device files are for Android 5.1 only. Linux uses different GPIO pin names.

set -A pins 938 915 1017 926 937 930 914 971 901 936 935
for i in 0 1 2 3 4 5 6 7 8 9 10
doecho${pins[i]} > /sys/class/gpio/export;
    chmod 777 /sys/class/gpio/gpio${pins[i]};
    chmod 777 /sys/class/gpio/gpio${pins[i]}/value;
    chmod 777 /sys/class/gpio/gpio${pins[i]}/direction;
done

A Note on sysfs device attributes

Here is some documentation on the GPIO Sysfs Inferface for Userspace from kernel.org. In addition to the two attributes that I use, direction and value, there are several others such as edge and active_low.

“direction” … reads as either “in” or “out”. This value may normally be written. Writing as “out” defaults to initializing the value as low. To ensure glitch free operation, values “low” and “high” may be written to configure the GPIO as an output with that initial value.

Note that this attribute will not exist if the kernel doesn’t support changing the direction of a GPIO, or it was exported by kernel code that didn’t explicitly allow userspace to reconfigure this GPIO’s direction.

“value” … reads as either 0 (low) or 1 (high). If the GPIO is configured as an output, this value may be written; any nonzero value is treated as high.

If the pin can be configured as interrupt-generating interrupt and if it has been configured to generate interrupts (see the description of “edge”), you can poll(2) on that file and poll(2) will return whenever the interrupt was triggered. If you use poll(2), set the events POLLPRI and POLLERR. If you use select(2), set the file descriptor in exceptfds. After poll(2) returns, either lseek(2) to the beginning of the sysfs file and read the new value or close the file and re-open it to read the value.

“edge” … reads as either “none”, “rising”, “falling”, or “both”. Write these strings to select the signal edge(s) that will make poll(2) on the “value” file return.

This file exists only if the pin can be configured as an interrupt generating input pin.

“active_low” … reads as either 0 (false) or 1 (true). Write any nonzero value to invert the value attribute both for reading and writing. Existing and subsequent poll(2) support configuration via the edge attribute for “rising” and “falling” edges will follow this setting.

Kotlin source code for using sysfs

The complete testing application I am using to explore this topic of using the DragonBoard 410C with Android is in my GitHub repository, https://github.com/RichardChambers/dragonboard_410c

Source for file Gpio.kt

package com.example.myapplication

import java.io.*

/**
 * Created by Ara on 7/21/15.
 * From https://www.instructables.com/id/DragonBoard-How-to-Access-GPIOs-Using-Java/
 *   Java source from the article was converted to Kotlin using Android Studio.
 *
 * See as well https://github.com/IOT-410c/DragonBoard410c_GpioLibrary
 *
 */classGpio(pin: Int) {
    privateval pin: Int/*
     *  The GPIO pins are represented by folders in the Linux file system
     *  within the folder /sys/class/gpio. Each pin is represented by a folder
     *  whose name is the prefix "gpio" followed by the pin number.
     *  Within the folder representing the pin are two files, "value" used to
     *  set or get the value of the pin and "direction" used to set or get
     *  the direction of the pin.
     *
     *  This function creates the path to the Linux file which represents a particular
     *  GPIO pin function, "value" or "direction".
     */privatefunMakeFileName(pin: Int, op: String): String {
        return"/sys/class/gpio/gpio$pin$op"
    }

    /*
     * Get or set the current direction of a pin.
     * A pin may be either an Input pin or an Output pin.
     */var direction: String
        get() {
            println("Getting Direction")
            var line = ""try {
                val br = BufferedReader(FileReader(MakeFileName(pin, "/direction")))
                line = br.readLine()
                br.close()
            } catch (e: Exception) {
                println("Error: " + e.message)
            }
            return line
        }
        privateset(direction) {
            println("Setting Direction")
            try {
                valout = BufferedWriter(FileWriter(MakeFileName(pin, "/direction"), false))
                out.write(direction)
                out.close()
            } catch (e: IOException) {
                println("Error: " + e.message)
            }
        }

    /**
     * Get or Set pin value.
     * @param value Value of pin.
     * 0 -> Low Level.
     * 1 -> High Level
     */var value: Intget() {
            println("Getting Value")
            var line = ""try {
                val br = BufferedReader(FileReader(MakeFileName(pin, "/value")))
                line = br.readLine()
                br.close()
            } catch (e: Exception) {
                println("Error: " + e.message)
            }
            return line.toInt()
        }
        privateset(value) {
            println("Setting Value")
            try {
                valout = BufferedWriter(FileWriter(MakeFileName(pin, "/value"), false))
                out.write(Integer.toString(value))
                out.close()
            } catch (e: IOException) {
                println("Error: " + e.message)
            }
        }

    /**
     * Set pin as high.
     */funpinHigh() {
        value = HIGH
    }

    /**
     * Set pin as low.
     */funpinLow() {
        value = LOW
    }

    /**
     * Set pin as output.
     */funpinOut() {
        direction = "out"
    }

    /**
     * Set pin as input.
     * @param pin - Desirable pin.
     */funpinIn() {
        direction = "in"
    }

    funexportPin() {
        println("Exporting Ping")
        try {
            valout = BufferedWriter(FileWriter("$PATH/export", false))
            out.write(pin.toString())
            out.close()
        } catch (e: IOException) {
            println("Error: " + e.message)
        }
    }

    /**
     * Disable access to GPIO.
     * @param pin GPIO pin to disable access.
     */fununexportPin() {
        println("unExporting Ping")
        try {
            valout = BufferedWriter(FileWriter("$PATH/unexport", false))
            out.write(pin.toString())
            out.close()
        } catch (e: IOException) {
            println("Error: " + e.message)
        }
    }

    companionobject {
        constval HIGH = 1constval LOW = 0privateconstval PATH = "/sys/class/gpio"
    }

    /**
     * Set desirable pin for the GPIO class.
     */init {
        println("Initializing pin $pin")
        this.pin = pin
    }
}

Source for GpioProcessor.kt

package com.example.myapplication

import java.io.BufferedWriter
import java.io.FileWriter
import java.io.IOException
import java.util.*

/**
 * Created by Ara on 7/21/15.
 * From https://www.instructables.com/id/DragonBoard-How-to-Access-GPIOs-Using-Java/
 *   Java source from the article was converted to Kotlin using Android Studio.
 *
 * See as well https://github.com/IOT-410c/DragonBoard410c_GpioLibrary
 *
 * Simple example main()
 *
 * public class Main {
 *
 * public static void main(String[] args) {
 * int count = 0;
 * int buttonValue = 0;
 *
 * GpioProcessor gpioProcessor = new GpioProcessor();
 *
 * // Get reference of GPIO27 and GPIO29.
 *
 * Gpio gpioPin27 = gpioProcessor.getPin27();
 * Gpio gpioPin29 = gpioProcessor.getPin29();
 *
 * // Set GPIO27 as output.Set GPIO29 as input.
 * gpioPin27.pinOut();
 * gpioPin29.pinIn();
 *
 * while(count<20){
 * count++;
 * // Read value of GPIO29.
 * buttonValue=gpioPin29.getValue();
 *
 * if(buttonValue == 0){
 * // Set GPIO27 as low level.
 * gpioPin27.pinLow();
 * } else{
 * // Set GPIO27 as high level.
 * gpioPin27.pinHigh();
 * }
 *
 * try {
 * Thread.sleep(1000);
 * } catch(InterruptedException e){
 * // TODO Auto-generated catch block
 * e.printStackTrace();
 * }
 * }
 *
 * // Disable access GPIO27 and GPIO29.
 * gpioProcessor.closePins();
 * }
 * }
 *//*
 This class abstracts the use of the gpio pins. This class can be utilized on any linux operating
 system that has gpio pins defined in the /sys/class/gpio directory. It is required that the gpio
 pins themselves are available for access by the user of this application, and may require a
 change of permissions.
 */classGpioProcessor{
    privateval PATH = "/sys/class/gpio"privateval pins: MutableList<Int> = ArrayList()

    // mapping of physical pin number to GPIO file number.// the mapping varies depending on the operating systemprivateval  androidPin23 = 938privateval  androidPin24 = 914privateval  androidPin25 = 915privateval  androidPin26 = 971privateval  androidPin27 = 1017privateval  androidPin28 = 901// GPIO pin borrowed from MPP. supports PWM. support analog I/O.privateval  androidPin29 = 926// (input only)privateval  androidPin30 = 927privateval  androidPin31 = 937privateval  androidPin32 = 936privateval  androidPin33 = 930privateval  androidPin34 = 935privateval  linuxPin23 = 36privateval  linuxPin24 = 12privateval  linuxPin25 = 13privateval  linuxPin26 = 69privateval  linuxPin27 = 115privateval  linuxPin28 = 4// GPIO pin borrowed from MPP. supports PWM. support analog I/O.privateval  linuxPin29 = 24// (input only)privateval  linuxPin30 = 25privateval  linuxPin31 = 35privateval  linuxPin32 = 34privateval  linuxPin33 = 28privateval  linuxPin34 = 33privateval  physicalPin23 = androidPin23
    privateval  physicalPin24 = androidPin24
    privateval  physicalPin25 = androidPin25
    privateval  physicalPin26 = androidPin26
    privateval  physicalPin27 = androidPin27
    privateval  physicalPin28 = androidPin28    // GPIO pin borrowed from MPP. supports PWM. support analog I/O.privateval  physicalPin29 = androidPin29    // (input only)privateval  physicalPin30 = androidPin30
    privateval  physicalPin31 = androidPin31
    privateval  physicalPin32 = androidPin32
    privateval  physicalPin33 = androidPin33
    privateval  physicalPin34 = androidPin34

    /**
     * Get function of specific pin.
     * @param pin Desirable pin.
     */fungetPin(pin: Int): Gpio {
        exportPin(pin)
        pins.add(pin)
        return Gpio(pin)
    }

    /**
     * Get pin 23;
     * @returns {Gpio}
     */val pin23: Gpio
        get() = getPin(physicalPin23)

    /**
     * Get pin 24.
     * @returns {Gpio}
     */val pin24: Gpio
        get() = getPin(physicalPin24)

    /**
     * Get pin 25.
     * @returns {Gpio}
     */val pin25: Gpio
        get() = getPin(physicalPin25)

    /**
     * Get pin 26.
     * @returns {Gpio}
     */val pin26: Gpio
        get() = getPin(physicalPin26)

    /**
     * Get pin 27.
     * @returns {Gpio}
     */val pin27: Gpio
        get() = getPin(physicalPin27)

    /**
     * Get pin 28.
     * @returns {Gpio}
     */val pin28: Gpio
        get() = getPin(physicalPin28)

    /**
     * Get pin 29.
     * @returns {Gpio}
     */val pin29: Gpio
        get() = getPin(physicalPin29)

    /**
     * Get pin 30.
     * @returns {Gpio}
     */val pin30: Gpio
        get() = getPin(physicalPin30)

    /**
     * Get pin 31.
     * @returns {Gpio}
     */val pin31: Gpio
        get() = getPin(physicalPin31)

    /**
     * Get pin 32.
     * @returns {Gpio}
     */val pin32: Gpio
        get() = getPin(physicalPin32)

    /**
     * Get pin 33.
     * @returns {Gpio}
     */val pin33: Gpio
        get() = getPin(physicalPin33)

    /**
     * Get pin 34.
     * @returns {Gpio}
     */val pin34: Gpio
        get() = getPin(physicalPin34)

    /**
     * Get all GPIO's pins.
     * @return List of pins.
     */val allPins: Array<Gpio?>
        get() {
            val allPins = arrayOfNulls<Gpio>(12)   // android       linux
            allPins[0] = pin23                          // GPIO 938     GPIO 36
            allPins[1] = pin24                          // GPIO 914     GPIO 12
            allPins[2] = pin25                          // GPIO 915     GPIO 13
            allPins[3] = pin26                          // GPIO 971     GPIO 69
            allPins[4] = pin27                          // GPIO 1017    GPIO 115
            allPins[5] = pin28                          // Reserved
            allPins[6] = pin29                          // GPIO 926     GPIO 24 (input only)
            allPins[7] = pin30                          // GPIO 927     GPIO 25
            allPins[8] = pin31                          // GPIO 937     GPIO 35
            allPins[9] = pin32                          // GPIO 936     GPIO 34
            allPins[10] = pin33                         // GPIO 930     GPIO 28
            allPins[11] = pin34                         // GPIO 935     GPIO 33return allPins
        }

    /**
     * Enable access to GPIO.
     * @param pin GPIO pin to access.
     */privatefunexportPin(pin: Int) {
        println("Exporting Ping")
        try {
            valout = BufferedWriter(FileWriter("$PATH/export", false))
            out.write(pin.toString())
            out.close()
        } catch (e: IOException) {
            println("Error: " + e.message)
        }
    }

    /**
     * Disable access to GPIO.
     * @param pin GPIO pin to disable access.
     */privatefununexportPin(pin: Int) {
        println("unExporting Ping")
        try {
            valout = BufferedWriter(FileWriter("$PATH/unexport", false))
            out.write(pin.toString())
            out.close()
        } catch (e: IOException) {
            println("Error: " + e.message)
        }
    }

    funclosePins() {
        for (pin in pins) {
            unexportPin(pin)
        }
        pins.clear()
    }

    companionobject {
        constval TAG = "GpioProcessor"
    }
}

Example source using the GpioProcessor class

I used the GPIO sysfs interface library in an Android app within a fragment by linking a button press to a listener. I have two buttons, one to turn the LED on by driving a pin high and a second to turn the LED off by driving a pin low.

overridefunonViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    view.findViewById<Button>(R.id.button_second).setOnClickListener {
        findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment)
    }

    val txtScroll = view.findViewById(R.id.LedStatus) as TextView

    // find the button whose id is button_Location and then set an listener for// any clicks on that button. In the following listener we are going to have// the "Location" button, defined in the file fragment_first.xml, generate a// list of the GPS service providers by creatinga LocationManager object to// generate a list.val gpioProcessor_x =  GpioProcessor()
        // Get reference of GPIO23.val gpioPin23_x = gpioProcessor_x.pin23
        gpioPin23_x.exportPin()

    view.findViewById<Button>(R.id.button_led_off).setOnClickListener {
        val gpioProcessor =  GpioProcessor()
        // Get reference of GPIO27.val gpioPin23 = gpioProcessor.pin23

        // Set GPIO23 as output.
        gpioPin23.pinOut()
        gpioPin23.pinLow()    // drive pin low to turn off LED.
        txtScroll.append("LED Off\n")
    }

    view.findViewById<Button>(R.id.button_led_on).setOnClickListener {
        val gpioProcessor =  GpioProcessor()
        // Get reference of GPIO27.val gpioPin23 = gpioProcessor.pin23

        // Set GPIO23 as output.
        gpioPin23.pinOut()
        gpioPin23.pinHigh()    // drive pin high to turn on LED
        txtScroll.append("LED On\n")
    }
}

Post a Comment for "Accessing Gpio Low Power Connector On Dragonboard 410c Running Android"