Accessing Gpio Low Power Connector On Dragonboard 410c Running Android
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"