我正在使用最近购买的运行Android 5.1操作系统的DragonBoard 410C,并使用带有Kotlin的Android Studio生成一个示例应用程序,该应用程序探索一些硬件,如40针低功耗连接器。
我的问题是如何使用Kotlin和Android Studio访问带有GPIO引脚的40引脚低功耗连接器。
从我迄今为止的研究来看,mraa库似乎是通往成功的道路,但我一直找不到任何关于与Kotlin一起使用该库的文档。
如何开始使用Kotlin的mraa库来访问40针低功耗连接器?
还是有不同的方法?
我的第一个例子是一个简单的闪烁LED应用程序,但我不知道如何使用Kotlin访问低功率连接器的引脚。
票据和资源
mraa文档页面
Libmraa是一个绑定到Python、Javascript和Java与Galileo、Edison&其他平台,具有结构合理的API,其中端口名称/编号与使用libmraa不会将您与特定的在运行时完成板检测的硬件,您可以创建便携式将在支持的平台上工作的代码。
mraa GitHub存储库的upm库
UPM存储库为各种常用的传感器和致动器。这些软件驱动程序相互作用与底层硬件平台(或微控制器),以及通过对MRAA API的调用。
哪个Android运行哪个Linux内核?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
sysfs死了!libgpiod万岁!(适用于linux和CircuitPython的libgpiod)
这基本上是将取代我们的Python DHT驱动程序的代码,并且具有与任何其他Linux板向前兼容的优点运行4.8以上内核。我们将慢慢取代其他CircuitPython使用libgpiod的代码,这样我们就可以对树莓派、BeagleBone或洋葱上的CircuitPython。
没有太多的libgpiod代码,libgpiod也没有在Linux发行版上上市,这可能是它需要一点时间的原因同时继续。有C和Python的绑定。这是一个剧本它可以帮助您开始为自己编译https://github.com/adafruit/Raspberry-Pi-Installer-Scripts/blob/master/libgpiod.sh
GitHub上的DragonBoard 410C GPIO库https://github.com/IOT-410c/DragonBoard410c_GpioLibrary它是用Java编写的/sys/class/gpio";Linux访问GPIO引脚的方法。这看起来像是一组用于物联网Coursera课程的存储库中的一个存储库,其中一些使用DragonBoard 410C。
Linux内核:传统GPIO接口
这提供了Linux上GPIO访问约定的概述。
这些调用使用gpio_*命名前缀。不应使用其他呼叫该前缀或相关的_gpio*前缀。
Android Studio和adb
Android Studio是用于开发Android应用程序的应用程序。它可以从下载安装https://developer.android.com/studio/releases
此外,还有Android平台工具,它们是单独下载的。adb
shell应用程序就是这些工具的一部分。这些工具可以从SDK平台工具发布说明下载进行安装。选择操作系统(Windows、Linux、MacOS)所需的特定版本。
安卓系统
虽然Android Things看起来很有帮助,但实际上似乎只支持几个板,DragonBoard 410C不是其中之一。而且我也不确定Android Things是否能与Android 5.1配合使用。
https://developer.android.com/things/get-started
然而,DragonBoard 410C有一个Brillo(现在的Android Things)端口https://discuss.96boards.org/t/android-things-on-the-dragonboard/1128
安卓开发者>文档>安卓设备>辅助线>GPIO-
为了打开到GPIO端口的连接,您需要知道唯一的端口名。在开发的初始阶段,或将应用程序移植到新硬件上,发现所有使用
getGpioList()
:的PeripheralManager
的可用端口名
Android Things GitHub存储库https://github.com/androidthings/
另请参阅以下stackoverflow的帖子,这些帖子对这个主题有一些看法。请参阅stackoverflow中的标签[android things]。
GpioCallback如何注册";false";连续两次?
Android Things Pin命名约定
PeripheralManagerService抛出NoClassDefFoundError
在查看了许多替代方案后,访问运行Android 5.1的DragonBoard 410C的GPIO引脚的最简单方法似乎是使用传统的sysfs特殊设备文件方法。
我不确定这是否是唯一可行的解决方案。使用Android Things和使用libgpiod似乎都需要比Android 5.1使用的更新的Linux内核。
我在CodeProject.com上写了一篇文章,提供了有关开发此解决方案的详细信息。请参阅使用Windows 10与DragonBoard 410C和Android进行开发。
哪个Android运行哪个Linux内核?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
这种方法似乎也是最简单的,因为使用的库也是用Kotlin编写的。
使用旧式sysfs专用设备GPIO接口
请参阅StackOverFlow关于Linux伪文件和特殊设备文件以及GPIO引脚的传统sysfs接口的文章,/sys/class/GPIO/export和'sys/class/GPIO/unport机制是什么,底层sysfs功能是什么。
我发现了一个用Java编写的简单GPIO库,它提供了必要的源代码。安卓工作室有一个将Java转换为Kotlin的工具,我将其包含在我的项目中。源代码位于Gpio.kt和GpioProcessor.kt.文件中
然而,为了实现这一点,我不得不更改我的DragonBoard启动脚本,以确保创建了必要的特殊设备文件,并具有适当的权限,允许用户程序操作GPIO引脚。
以下程序来自Coursera课程物联网:从设备感知和驱动,第5a课:通过程序访问GPIO(Android)视频#2,修改引导脚本。程序是:
- 使用adb将/etc/init.qcom.post_boot.sh的副本从DragonBoard拉到我的电脑
- 使用记事本修改shell脚本以创建特殊的设备文件
- 使用adb将修改后的副本推回到Dragonboard
- 使用adb重新启动DragonBoard
要添加到/etc/init.qcom.post_boot.sh底部的附加shell代码如下然而,这些特殊设备文件仅适用于Android 5.1Linux使用不同的GPIO引脚名称。
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
do
echo ${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
关于sysfs设备属性的说明
以下是kernel.org中关于GPIO Sysfs Inferface for Userspace的一些文档。除了我使用的两个属性direction
和value
之外,还有其他几个属性,如edge
和active_low
。
"direction">…读作"in"或"out"。此值通常可能被书写。写入为"out"默认将值初始化为低的为确保无故障运行,值"低"one_answers"高"可能为被写入以将GPIO配置为具有该初始值的输出。
请注意,如果内核不支持,则此属性将不存在更改GPIO的方向,或者由内核代码导出没有明确允许用户空间重新配置这个GPIO方向
"value">…读取为0(低)或1(高)。如果GPIO该值被配置为输出,可以被写入;任何非零值被视为高。
如果引脚可以配置为产生中断的中断,并且它已配置为生成中断(请参阅"edge"),则可以对该文件进行轮询(2),并且每当中断被触发。如果使用轮询(2),请设置事件POLLPRI和POLLERR。如果使用select(2),请在中设置文件描述符例外。轮询(2)返回后,lseek(2)到打开sysfs文件并读取新值,或者关闭该文件并重新打开它以读取该值。
"edge">…读作"none"、"rising"、"falling"或"both"。写这些字符串选择将进行轮询(2)的信号边缘返回"value"文件。
只有当引脚可以配置为中断时,此文件才存在生成输入引脚。
"active_low">…读取为0(false)或1(true)。写任意非零值以反转读取和写通过"上升"one_answers"下降"边缘的边缘属性将遵循此背景
Kotlin使用sysfs的源代码
我用来探索将DragonBoard 410C与Android一起使用这一主题的完整测试应用程序在我的GitHub存储库中,https://github.com/RichardChambers/dragonboard_410c
文件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
*
*/
class Gpio(pin: Int) {
private val 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".
*/
private fun MakeFileName(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
}
private set(direction) {
println("Setting Direction")
try {
val out = 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: Int
get() {
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()
}
private set(value) {
println("Setting Value")
try {
val out = BufferedWriter(FileWriter(MakeFileName(pin, "/value"), false))
out.write(Integer.toString(value))
out.close()
} catch (e: IOException) {
println("Error: " + e.message)
}
}
/**
* Set pin as high.
*/
fun pinHigh() {
value = HIGH
}
/**
* Set pin as low.
*/
fun pinLow() {
value = LOW
}
/**
* Set pin as output.
*/
fun pinOut() {
direction = "out"
}
/**
* Set pin as input.
* @param pin - Desirable pin.
*/
fun pinIn() {
direction = "in"
}
fun exportPin() {
println("Exporting Ping")
try {
val out = 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.
*/
fun unexportPin() {
println("unExporting Ping")
try {
val out = BufferedWriter(FileWriter("$PATH/unexport", false))
out.write(pin.toString())
out.close()
} catch (e: IOException) {
println("Error: " + e.message)
}
}
companion object {
const val HIGH = 1
const val LOW = 0
private const val PATH = "/sys/class/gpio"
}
/**
* Set desirable pin for the GPIO class.
*/
init {
println("Initializing pin $pin")
this.pin = pin
}
}
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.
*/
class GpioProcessor {
private val PATH = "/sys/class/gpio"
private val pins: MutableList<Int> = ArrayList()
// mapping of physical pin number to GPIO file number.
// the mapping varies depending on the operating system
private val androidPin23 = 938
private val androidPin24 = 914
private val androidPin25 = 915
private val androidPin26 = 971
private val androidPin27 = 1017
private val androidPin28 = 901 // GPIO pin borrowed from MPP. supports PWM. support analog I/O.
private val androidPin29 = 926 // (input only)
private val androidPin30 = 927
private val androidPin31 = 937
private val androidPin32 = 936
private val androidPin33 = 930
private val androidPin34 = 935
private val linuxPin23 = 36
private val linuxPin24 = 12
private val linuxPin25 = 13
private val linuxPin26 = 69
private val linuxPin27 = 115
private val linuxPin28 = 4 // GPIO pin borrowed from MPP. supports PWM. support analog I/O.
private val linuxPin29 = 24 // (input only)
private val linuxPin30 = 25
private val linuxPin31 = 35
private val linuxPin32 = 34
private val linuxPin33 = 28
private val linuxPin34 = 33
private val physicalPin23 = androidPin23
private val physicalPin24 = androidPin24
private val physicalPin25 = androidPin25
private val physicalPin26 = androidPin26
private val physicalPin27 = androidPin27
private val physicalPin28 = androidPin28 // GPIO pin borrowed from MPP. supports PWM. support analog I/O.
private val physicalPin29 = androidPin29 // (input only)
private val physicalPin30 = androidPin30
private val physicalPin31 = androidPin31
private val physicalPin32 = androidPin32
private val physicalPin33 = androidPin33
private val physicalPin34 = androidPin34
/**
* Get function of specific pin.
* @param pin Desirable pin.
*/
fun getPin(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 33
return allPins
}
/**
* Enable access to GPIO.
* @param pin GPIO pin to access.
*/
private fun exportPin(pin: Int) {
println("Exporting Ping")
try {
val out = 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.
*/
private fun unexportPin(pin: Int) {
println("unExporting Ping")
try {
val out = BufferedWriter(FileWriter("$PATH/unexport", false))
out.write(pin.toString())
out.close()
} catch (e: IOException) {
println("Error: " + e.message)
}
}
fun closePins() {
for (pin in pins) {
unexportPin(pin)
}
pins.clear()
}
companion object {
const val TAG = "GpioProcessor"
}
}
使用GpioProcessor类的示例源
我在一个片段中使用了Android应用程序中的GPIO sysfs接口库,通过将按钮按下链接到侦听器。我有两个按钮,一个是通过驱动引脚高来打开LED,另一个是驱动引脚低来关闭LED。
override fun onViewCreated(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 Offn")
}
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 Onn")
}
}