Flutter GPIO控制插件flutter_gpiod的使用

Flutter GPIO控制插件flutter_gpiod的使用

📰 新闻

  • 迁移到了null-safety
  • SignalEventtime 属性不再那么准确,但提供了两个新的属性 timestampNanostimestamp,它们非常准确。(这是因为内核的变化)
  • 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

1 回复

更多关于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线路已控制'),
        ),
      ),
    );
  }
}

在这个示例中,我们做了以下几件事:

  1. initState方法中,我们打开了一个GPIO芯片并获取了特定的GPIO线路。
  2. 我们请求了这条线路的控制权,并将其方向设置为输出。
  3. 我们向这条线路写入了高电平和低电平。
  4. dispose方法中,我们释放了GPIO资源。

请注意,这个示例假设你的Flutter应用运行在支持gpiod接口的Linux系统上,并且你有足够的权限来访问GPIO设备。如果你的应用运行在受限的环境中(例如Android或iOS),你可能需要寻找其他方法来控制硬件。

此外,确保在实际部署时处理所有可能的异常和错误情况,以增强应用的健壮性。

回到顶部