Flutter GPIO控制插件flutter_gpiod的使用
Flutter GPIO控制插件flutter_gpiod的使用
📰 新闻
- 迁移到了null-safety
SignalEvent
的time
属性不再那么准确,但提供了两个新的属性timestampNanos
和timestamp
,它们非常准确。(这是因为内核的变化)- Raspberry Pi 的主GPIO芯片在Pi 4上使用最新内核版本时不再是
pinctrl-bcm2835
,而是pinctrl-bcm2711
。
flutter_gpiod
这是一个用于Linux和Android(需要root权限)通过GPIO字符设备接口访问GPIO的dart包。已经在ARM32上测试并运行,应该也可以在其他32位和64位Linux平台上工作。
开始使用
首先,你可以使用 [FlutterGpiod.chips]
获取系统中连接的所有GPIO芯片列表。每个芯片都有一个名称、标签以及与其关联的一系列GPIO线路。
final chips = FlutterGpiod.instance.chips;
for (final chip in chips) {
print("chip name: ${chip.name}, chip label: ${chip.label}");
for (final line in chip.lines) {
print(" line: $line");
}
}
每条线路也有与之相关的信息,这些信息可以通过 [GpioLine.info]
获取。如果该线路未被你拥有或请求,则这些信息可能会随时改变。
// 获取主要的Raspberry Pi GPIO芯片。
// 在Raspberry Pi 4上,主要的GPIO芯片名为 `pinctrl-bcm2711`,
// 而在较旧的Pi或使用较旧内核版本的Pi 4上,它被称为 `pinctrl-bcm2835`。
final chip = FlutterGpiod.instance.chips.singleWhere(
(chip) => chip.label == 'pinctrl-bcm2711',
orElse: () => FlutterGpiod.instance.chips.singleWhere((chip) => chip.label == 'pinctrl-bcm2835'),
);
// 获取GPIO芯片上的第22号线路。
// 这是Raspberry Pi的BCM 22引脚。
final line = chip.lines[22];
print("line info: ${line.info}")
要控制一条线路(读取或写入值,或监听边缘信号),你需要使用 [GpioLine.requestInput]
或 [GpioLine.requestOutput]
请求该线路。
final chip = FlutterGpiod.instance.chips.singleWhere((chip) => chip.label == 'pinctrl-bcm2835');
final line = chip.lines[22];
// 将其请求为输入。
line.requestInput();
print("line value: ${line.getValue()}");
line.release();
// 现在我们将其请求为输出。
line.requestOutput(initialValue: true);
line.setValue(false);
line.release();
// 再次将其请求为输入,但这次我们还监听边缘信号;在这里两种信号都监听。
line.requestInput(triggers: {SignalEdge.falling, SignalEdge.rising});
print("line value: ${line.getValue()}");
// 如果没有请求任何触发器,line.onEvent 将不会发出任何事件。
// 这将无限循环
await for (final event in line.onEvent) {
print("got GPIO line signal event: $event");
}
line.release();
示例代码
import 'dart:async';
import 'package:async/async.dart';
import 'package:flutter_gpiod/flutter_gpiod.dart';
void main() async {
/// 获取所有GPIO芯片的列表。
final chips = FlutterGpiod.instance.chips;
/// 打印出所有GPIO芯片及其所有线路。
for (var chip in chips) {
print("$chip");
for (var line in chip.lines) {
print(" $line");
}
}
/// 获取第一个芯片的第23号线路。
/// 这是Raspberry Pi的BCM 23引脚。
///
/// 我建议基于芯片标签找到你想要的芯片,就像这里所做的那样。
///
/// 在这个例子中,我们搜索主要的Raspberry Pi GPIO芯片,然后获取它的第23号线路。因此,`line` 是GPIO引脚BCM 23。
///
/// 主GPIO芯片在Pi 4上称为 `pinctrl-bcm2711`,在较旧的Raspberry Pis上和带有较旧内核版本的Pi 4上也称为 `pinctrl-bcm2835`。
final chip = chips.singleWhere(
(chip) => chip.label == 'pinctrl-bcm2711',
orElse: () => chips.singleWhere((chip) => chip.label == 'pinctrl-bcm2835'),
);
final line1 = chip.lines[23];
final line2 = chip.lines[24];
/// 将BCM 23请求为输出。
line1.requestOutput(consumer: "flutter_gpiod test", initialValue: false);
/// 使线路脉冲两次。
line1.setValue(true);
await Future.delayed(Duration(milliseconds: 500));
line1.setValue(false);
await Future.delayed(Duration(milliseconds: 500));
line1.setValue(true);
await Future.delayed(Duration(milliseconds: 500));
// setValue(false) 不需要,因为我们正在释放它
line1.release();
/// 现在我们监听BCM 23和BCM 24的下降沿和上升沿事件。
line1.requestInput(consumer: "test 1", triggers: {SignalEdge.falling, SignalEdge.rising});
line2.requestInput(consumer: "test 2", triggers: {SignalEdge.falling, SignalEdge.rising});
print("line value: ${line1.getValue()}");
/// 打印所有接收到的线路事件。
final mergedEvents = StreamGroup.mergeBroadcast([
line1.onEvent.map((event) => "(pin 23) $event"),
line2.onEvent.map((event) => "(pin 24) $event"),
]);
var countEvents = 0;
await for (final event in mergedEvents) {
print("GPIO event: $event");
countEvents++;
if (countEvents > 100) {
break;
}
}
/// 如果你依赖于特定线路事件被触发,例如,如果你监听中断线路并在代码中阻塞直到接收到中断(=信号事件),请小心不要编写以下代码:
/// ```dart
/// final interruptLine = ...;
///
/// // 这里做一些触发中断的事情
///
/// // 等待中断
/// await for (final _ in interruptLine.onEvent) break;
///
/// // 继续这里
/// ```
/// `[GpioLine.onEvent]` 是广播流。如果事件添加到广播流中,但没有监听器存在,该事件将被丢弃。因此,如果你的芯片比Dart代码更快地触发中断,信号事件将被丢弃,并且Dart代码将在那里无限期地阻塞,等待信号事件。
/// 相反,做这样的事情:
/// 获取单订阅(缓冲流)。流将立即开始缓冲事件。如果你使用`listen`订阅此流(或映射/过滤等变体),重要的是取消订阅,否则内存将因缓冲的信号事件而填满。
/// `SingleSubscriptionTransformer` 包含在 `async` 包中。
final bufferingStream = line1.onEvent.transform(SingleSubscriptionTransformer<SignalEvent, SignalEvent>());
// 假设如果我们脉冲线路2(BCM 24),连接到它的某个设备会脉冲线路1(BCM 23)作为中断。
line2.release();
line2.requestOutput(consumer: 'some device that has interrupts', initialValue: false);
line2.setValue(true);
await Future.delayed(Duration(milliseconds: 500));
line2.setValue(false);
line2.release();
print("等待中断...");
/// `await for` 只是内部使用了 `[StreamSubscription]`,并且当 `await for` 循环结束时,将取消流订阅,因此没有内存泄漏。
await for (final _ in bufferingStream) break;
print("接收到中断!");
/// 你也不能在此时监听/使用 `bufferingStream`,因为单订阅流在取消后不可重用。
line1.release();
}
更多关于Flutter GPIO控制插件flutter_gpiod的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter GPIO控制插件flutter_gpiod的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中使用flutter_gpiod
插件来控制GPIO的示例代码。这个插件允许你通过Linux系统的gpiod接口来控制GPIO引脚。
首先,确保你的Flutter项目中已经添加了flutter_gpiod
依赖。在你的pubspec.yaml
文件中添加以下依赖:
dependencies:
flutter:
sdk: flutter
flutter_gpiod: ^最新版本号 # 请替换为实际的最新版本号
然后运行flutter pub get
来安装依赖。
接下来,你可以在你的Flutter应用中使用flutter_gpiod
来控制GPIO。以下是一个简单的示例代码,展示了如何打开一个GPIO引脚,设置其方向为输出,并写入高电平或低电平。
import 'package:flutter/material.dart';
import 'package:flutter_gpiod/flutter_gpiod.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late GpioChip chip;
late GpioLine line;
@override
void initState() {
super.initState();
// 初始化GPIO芯片和线路
_initGpio();
}
@override
void dispose() {
// 释放资源
line.close();
chip.close();
super.dispose();
}
Future<void> _initGpio() async {
try {
// 打开GPIO芯片,通常芯片编号为0
chip = await GpioChip.open(0);
// 获取GPIO线路,假设我们要控制的线路编号为17
line = chip.getLine(17);
// 设置线路方向为输出
await line.request(consumer: 'myapp', type: GpioLineRequestType.output);
// 写入高电平
await line.setValue(1);
// 可以添加一些延时或其他操作
await Future.delayed(Duration(seconds: 1));
// 写入低电平
await line.setValue(0);
} catch (e) {
print('GPIO操作失败: $e');
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter GPIO控制示例'),
),
body: Center(
child: Text('GPIO线路已控制'),
),
),
);
}
}
在这个示例中,我们做了以下几件事:
- 在
initState
方法中,我们打开了一个GPIO芯片并获取了特定的GPIO线路。 - 我们请求了这条线路的控制权,并将其方向设置为输出。
- 我们向这条线路写入了高电平和低电平。
- 在
dispose
方法中,我们释放了GPIO资源。
请注意,这个示例假设你的Flutter应用运行在支持gpiod接口的Linux系统上,并且你有足够的权限来访问GPIO设备。如果你的应用运行在受限的环境中(例如Android或iOS),你可能需要寻找其他方法来控制硬件。
此外,确保在实际部署时处理所有可能的异常和错误情况,以增强应用的健壮性。