I am working with a DragonBoard 410C using Android 5.1 and Kotlin to experiment with the GPIO pins on the 40 pin low power connector. The library I'm using is using the sysfs interface for interacting with the GPIO pins which requires opening various pseudo files in /sys/class/gpio/
directory tree and reading and writing values to those files, see accessing GPIO low power connector on DragonBoard 410C running Android
My understanding is that I can provision a GPIO pin as Input and Edge triggered which would allow me to wire up a simple circuit with a momentary contact switch and be able to detect when the switch is pressed.
However the documentation I've found indicates that I need to use the poll(2) system service or the select(2) system service on a file descriptor for the /value
pseudo file of the GPIO pin I am using in order to detect when the Edge is detected, e.g. /sys/class/gpio/gpio910/value
.
How do I use the poll(2)
or select(2)
system services with a file descriptor in Kotlin? Is poll(2)
the same as ready()
method of FileReader
?
Perhaps something similar to the Java WatchService
functionality is needed? http://www.java2s.com/Tutorials/Java/java.nio.file/WatchService/0060__WatchService.poll_.htm
What I am planning, unless this is the wrong approach, is to have a utility function, something like:
// pollPseudoFile() polls the specified GPIO pin using the sysfs interface in order
// to wait for a pin value due to an external event such as a momentary switch press
//
// Returns:
// - 0 -> an error of some kind
// - 1 -> time out expired with no value
// - 2 -> value change detected
public fun pollPseudoFile (pinPathFull : String, timeOut : Int) : Int {
println(" pollPseudoFile - String")
var iStatus : Int = 0 // status return indicating if poll() completed or not.
try {
val br = FileReader(pinPathFull)
br.poll(timeOut) // wait for data to be available or time out. how to do poll?
iStatus = 2 // indicate value change unless the poll() timed out
if (br.pollExpired) iStatus = 1 // poll timed out. how to check for poll status?
br.close()
} catch (e: Exception) {
println("Error: " + e.message)
}
return iStatus;
}
public fun pollGetValue (pinPathFull : String) : Int {
println(" pollGetValue - String")
var line = ""
try {
val br = BufferedReader(FileReader(pinPathFull))
line = br.readLine()
br.close()
} catch (e: Exception) {
println("Error: " + e.message)
}
return line.toInt()
}
https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
"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.
Additional notes
Note 1: Using the adb
utility I was able to shell
into my DragonBoard 410C and tested configuring physical pin 26, GPIO971, with direction
set to in
and edge
set to rising
. Using a simple LED circuit on a breadboard that was tied to physical pin 23, GPIO938, and adding a wire from physical pin 26 to the LED managed by physical pin 23, I was able to turn on the LED with echo 1 > gpio938/value
and then cat gpio971/value
to see that the value for physical pin 26 had gone high and was reading as 1
. I then turned the LED connected to physical pin 23 off with echo 0 > gpio938/value
and then cat gpio971/value
returned a value of 0
as expected.
However this experiment does not tell me if a poll(2)
would indicate a change on gpio971/value
when the LED was turned on and turned off or not.
Note 1a: I have a first version of a Native C++ JNI function to implement the poll(2)
service call and have been testing it with my DragonBoard 410C. What I am seeing is that the poll(2)
function is immediately returning with both POLLIN
and POLLERR
set in the revents
member of the struct pollfd
array.
The test is using physical pin 26 connected to a breadboard row with one leg of an LED connected to physical pin 23 which I am able to turn on and off. When I attempt to turn on polling with a 10000 ms time out, the call returns immediately whether the LED is lit (pin 26 value is 1) or not lit (pin 26 value is 0) with both indicators set.
My expectation is that since I have edge
set to rising
, I should see the poll(2)
return only when the LED is unlit and I then turn it on or 10 seconds have elapsed.
Am continuing my investigation as it strikes me there may a problem with how I'm using the Native C++ function I wrote in the Kotlin side of the app.
Note 2: I attempted to use WatchService
with my Kotlin application and ran into an error that WatchService
required an API level 26 and my minimum target in Android Studio is API level 22. It looks like WatchService
requires Android 8.0 (Oreo) while the DragonBoard is at Android 5.1 (Lollipop) so using WatchService
to monitor file status is not available to me.
poll(2)
service call on the indicated pseudo file. Once I have something that works, I will post an answer. – Armourer