Flutter串口通信插件embedded_serialport的使用

发布于 1周前 作者 ionicwang 来自 Flutter

Flutter串口通信插件embedded_serialport的使用

embedded_serialport简介

embedded_serialport 是一个用于串口通信的Flutter包,它使用Dart FFI(Foreign Function Interface)与Rust和C共享库进行交互。这个包提供了简单的API来列出可用的串口,并执行读写操作。你可以参考这篇文章 Building a Flutter application for serial port communication with embedded system 和 示例演示视频 Demo Video 来了解更多。

平台支持

  • Linux

功能特性

  • 列出可用的串口
  • 打开并配置串口
  • 从串口读取数据和向串口写入数据
  • 设置串口通信超时时间

安装

要安装 embedded_serialport,可以使用以下命令:

flutter pub add embedded_serialport

依赖项

对于GNU/Linux系统,需要安装 pkg-configlibudev 头文件:

安装 pkg-config

  • Ubuntu: sudo apt install pkg-config
  • Fedora: sudo dnf install pkgconf-pkg-config

安装 libudev (除非禁用默认的 libudev 特性)

  • Ubuntu: sudo apt install libudev-dev
  • Fedora: sudo dnf install systemd-devel

检查权限

确保你的用户属于 dialout 组或等效组:

sudo usermod -aG dialout $USER

然后注销并重新登录。否则,你会收到 SerialErrorCode.serialErrorOpen 错误。

准备Linux应用程序以供分发

要构建Flutter应用程序作为发布版本,请运行以下命令:

flutter build linux --release

之后,进入 build/linux/release/bundle/ 目录并使用以下命令运行应用程序:

./projectname

这将自动复制一个名为 librust_serialport.so 的文件到 lib 文件夹中,该文件负责库在应用程序中的工作。

使用方法

在Dart代码中导入该包:

import 'package:embedded_serialport/embedded_serialport.dart';

示例1:列出所有可用的串口

import 'package:embedded_serialport/embedded_serialport.dart';

void main() {
  List<String> ports = Serial.getPorts();
  print(ports);
  for (int i = 0; i < ports.length; i++) {
    print(ports[i]);
  }
}

示例2:与串行设备通信

import 'package:embedded_serialport/embedded_serialport.dart';

void main() {
  List<String> ports = Serial.getPorts();

  var s = Serial(ports.first, Baudrate.b115200);
  s.timeout(2);

  try {
    s.writeString('led,0');
    var event = s.read(20);
    print(event.toString());
    s.writeString('led');
    var eventt = s.read(20);
    print(eventt.toString());
  } finally {
    s.dispose();
  }
}

示例3:完整的Flutter应用示例

import 'package:flutter/material.dart';
import 'package:embedded_serialport/embedded_serialport.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Serial Port Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SerialPortPage(),
    );
  }
}

class SerialPortPage extends StatefulWidget {
  const SerialPortPage({super.key});

  @override
  // ignore: library_private_types_in_public_api
  _SerialPortPageState createState() => _SerialPortPageState();
}

class _SerialPortPageState extends State<SerialPortPage> {
  late Serial _serial;
  List<String> _ports = [];
  String _output = "";

  @override
  void initState() {
    super.initState();
    _initializeSerialPort();
  }

  void _initializeSerialPort() {
    _ports = Serial.getPorts();
    if (_ports.isNotEmpty) {
      _serial = Serial(_ports.first, Baudrate.b115200);
      _serial.timeout(2);
    }
  }

  void _writeString(String command) async {
    _serial.writeString(command);
    await Future.delayed(const Duration(milliseconds: 100));
    var event = _serial.read(0);
    setState(() {
      _output += "Hardware says -> ${event.toString()}\n";
    });
  }

  void _disposeSerialPort() {
    _serial.dispose();
  }

  @override
  void dispose() {
    _disposeSerialPort();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Serial Port Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Available Ports: ${_ports.join(", ")}'),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => _writeString('on'),
              child: const Text('Send "on"'),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => _writeString('off'),
              child: const Text('Send "off"'),
            ),
            const SizedBox(height: 20),
            Expanded(
              child: SingleChildScrollView(
                child: Text(_output),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

API说明

Serial 类

静态方法

  • List<String> getPorts():返回可用串口列表。

构造函数

  • Serial(String port, Baudrate baudrate):创建一个新的Serial实例,指定端口和波特率。

方法

  • void timeout(int seconds):设置读写操作的超时时间。
  • void writeString(String data):向串口写入字符串。
  • Uint8List read(int length):从串口读取指定数量的字节。
  • void dispose():关闭串口并释放资源。

Baudrate 枚举

常见的波特率枚举值包括:

  • Baudrate.b9600
  • Baudrate.b19200
  • Baudrate.b38400
  • Baudrate.b57600
  • Baudrate.b115200
  • 更多…

作者

致谢


更多关于Flutter串口通信插件embedded_serialport的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter串口通信插件embedded_serialport的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter应用中使用embedded_serialport插件来实现串口通信的示例代码。请注意,这个插件主要面向Android平台,并且需要一些额外的配置才能在iOS上工作(但iOS上串口通信的支持较为有限,通常需要MFi认证的硬件)。

首先,确保你已经在pubspec.yaml文件中添加了embedded_serialport依赖:

dependencies:
  flutter:
    sdk: flutter
  embedded_serialport: ^x.y.z  # 请替换为最新版本号

然后,运行flutter pub get来安装依赖。

接下来,你需要在Android的AndroidManifest.xml中添加权限声明,以便应用能够访问串口设备:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yourapp">

    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <!-- 其他权限和配置 -->

</manifest>

由于串口通信通常涉及底层硬件访问,你可能还需要请求运行时权限。以下是一个请求权限的简单示例:

import 'package:permission_handler/permission_handler.dart';

Future<void> requestPermissions() async {
  var status = await Permission.storage.status;
  if (!status.isGranted) {
    Map<Permission, PermissionStatus> statuses = await [
      Permission.storage,
    ].request();
  }
}

现在,让我们编写一些代码来初始化串口并进行通信。下面是一个完整的示例,展示如何打开串口、写入数据并读取数据:

import 'package:flutter/material.dart';
import 'package:embedded_serialport/embedded_serialport.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  SerialPort? _serialPort;
  String _output = '';

  @override
  void initState() {
    super.initState();
    initSerialPort();
  }

  Future<void> initSerialPort() async {
    // 初始化串口
    _serialPort = await SerialPort.open('/dev/ttyS0'); // 根据你的设备调整路径
    if (_serialPort != null) {
      // 设置波特率等参数
      _serialPort!.setPortParameters(9600, 8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

      // 监听串口数据
      _serialPort!.inputStream.listen((Uint8List data) {
        setState(() {
          _output += String.fromCharCodes(data);
        });
      });
    }
  }

  Future<void> sendData(String data) async {
    if (_serialPort != null && _serialPort!.isOpen) {
      _serialPort!.write(data.codeUnits);
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Serial Port Communication'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              TextField(
                decoration: InputDecoration(labelText: 'Send Data'),
                onEditingComplete: () {
                  sendData(textFieldController.text);
                  textFieldController.clear();
                },
                controller: textFieldController,
              ),
              SizedBox(height: 16),
              Text('Received Data:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              Text(_output, style: TextStyle(fontSize: 16)),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            // 示例:发送 "Hello, Serial Port!"
            sendData('Hello, Serial Port!');
          },
          tooltip: 'Send Data',
          child: Icon(Icons.send),
        ),
      ),
    );
  }

  final TextEditingController textFieldController = TextEditingController();

  @override
  void dispose() {
    textFieldController.dispose();
    _serialPort?.close();
    super.dispose();
  }
}

注意

  1. 串口路径(如/dev/ttyS0)在Android上通常是虚拟设备或特定硬件的路径,你需要根据实际情况调整。
  2. embedded_serialport插件在iOS上的支持有限,如果你需要在iOS上实现串口通信,可能需要考虑其他方法或硬件。
  3. 确保你的设备或模拟器支持串口通信,并且已经正确连接了串口设备。

这个示例展示了基本的串口初始化、数据发送和接收功能。根据你的具体需求,你可能需要添加更多的错误处理、配置参数调整等。

回到顶部