Flutter串口通信插件libserialport的使用

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

Flutter串口通信插件 libserialport 的使用

概述

libserialport 是一个基于 Dart FFI(Foreign Function Interface)的库,用于与 libserialport C 库进行交互。libserialport 是由 sigrok 项目创建的一个最小化的 C 库,适用于 Linux、macOS、Windows 和 Android 平台。

对于 Flutter 应用程序,推荐使用 flutter_libserialport 插件,它能够自动构建和部署 libserialport 到所有支持的平台。

使用步骤

添加依赖

首先,在你的 pubspec.yaml 文件中添加 libserialport 或者 flutter_libserialport 作为依赖:

dependencies:
  flutter_libserialport: ^latest_version

确保替换 ^latest_version 为实际的最新版本号。

示例代码

以下是一个简单的示例,展示了如何列出可用的串口设备,并打开其中一个端口进行读写操作。

列出所有可用串口

import 'package:libserialport/libserialport.dart';

void main() {
  print('Available ports:');
  var i = 0;
  for (final name in SerialPort.availablePorts) {
    final sp = SerialPort(name);
    print('${++i}) $name');
    print('\tDescription: ${sp.description}');
    print('\tManufacturer: ${sp.manufacturer}');
    print('\tSerial Number: ${sp.serialNumber}');
    print('\tProduct ID: 0x${sp.productId!.toRadixString(16)}');
    print('\tVendor ID: 0x${sp.vendorId!.toRadixString(16)}');
    sp.dispose();
  }
}

打开并使用串口

下面的例子展示了如何打开第一个可用的串口,并设置为读写模式,然后发送数据并监听接收的数据。

import 'dart:io';
import 'package:libserialport/libserialport.dart';

void main() async {
  // 获取第一个可用串口名称
  final name = SerialPort.availablePorts.firstOrNull;
  if (name == null) {
    print("No serial ports available.");
    exit(-1);
  }

  // 创建 SerialPort 实例
  final port = SerialPort(name);
  
  // 尝试以读写模式打开串口
  if (!port.openReadWrite()) {
    print(SerialPort.lastError);
    exit(-1);
  }

  // 写入一些数据到串口
  port.write([0x48, 0x65, 0x6c, 0x6c, 0x6f]); // 发送 "Hello"

  // 设置监听器来接收数据
  final reader = SerialPortReader(port);
  reader.stream.listen((data) {
    print('Received: ${String.fromCharCodes(data)}');
  });

  // 等待一段时间以便接收数据
  await Future.delayed(Duration(seconds: 5));

  // 关闭串口
  port.close();
}

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

1 回复

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


在Flutter应用中实现串口通信,你可以使用libserialport插件。libserialport是一个强大的C库,用于跨平台的串口通信。虽然Flutter本身并不直接支持C库,但你可以通过调用原生代码(如Android的Java/Kotlin或iOS的Objective-C/Swift)来实现这一功能。

以下是一个基本的步骤和代码示例,展示如何在Flutter项目中集成和使用libserialport进行串口通信。由于libserialport是一个C库,我们需要在原生代码中进行封装,然后通过MethodChannel与Flutter进行通信。

步骤 1: 设置Flutter项目

首先,创建一个新的Flutter项目(如果你还没有的话):

flutter create serial_port_app
cd serial_port_app

步骤 2: 添加原生依赖

Android

  1. android/app/src/main/cpp/目录下创建一个CMakeLists.txt文件(如果不存在),并添加以下内容:
cmake_minimum_required(VERSION 3.4.1)

add_library(libserialport SHARED
            src/main/cpp/libserialport/libserialport.c)

find_library(log-lib log)

target_link_libraries(libserialport ${log-lib})

注意:你需要将libserialport的源代码放在src/main/cpp/libserialport/目录下。

  1. android/app/build.gradle中添加对CMake的支持:
android {
    ...
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }
}
  1. 创建一个Java类来封装对libserialport的调用。在android/app/src/main/java/com/yourappid/目录下创建一个名为SerialPortHelper.java的文件,并添加以下内容:
package com.yourappid;

import android.content.Context;
import androidx.annotation.NonNull;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

public class SerialPortHelper implements FlutterPlugin, MethodCallHandler, ActivityAware {
    private MethodChannel channel;
    private Context applicationContext;

    // Native method declarations
    public native void openPort(String path, int baudrate);
    public native void closePort();
    public native byte[] readPort(int size);
    public native void writePort(byte[] data);

    static {
        System.loadLibrary("serialport");
    }

    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
        applicationContext = flutterPluginBinding.getApplicationContext();
        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "serial_port_channel");
        channel.setMethodCallHandler(this);
    }

    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
        if (call.method.equals("openPort")) {
            String path = call.argument("path");
            int baudrate = call.argument("baudrate");
            openPort(path, baudrate);
            result.success(null);
        } else if (call.method.equals("closePort")) {
            closePort();
            result.success(null);
        } else if (call.method.equals("readPort")) {
            int size = call.argument("size");
            byte[] data = readPort(size);
            result.success(data);
        } else if (call.method.equals("writePort")) {
            byte[] data = call.argument("data");
            writePort(data);
            result.success(null);
        } else {
            result.notImplemented();
        }
    }

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        channel.setMethodCallHandler(null);
    }

    @Override
    public void onAttachedToActivity(ActivityPluginBinding binding) {
        // No-op
    }

    @Override
    public void onDetachedFromActivityForConfigChanges() {
        // No-op
    }

    @Override
    public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
        // No-op
    }

    @Override
    public void onDetachedFromActivity() {
        // No-op
    }
}

注意:你需要将libserialport的C代码进行适当的封装,以提供上述的openPortclosePortreadPortwritePort方法。

iOS

对于iOS,你需要使用Objective-C或Swift封装对libserialport的调用,但这个过程相对复杂,因为iOS通常不直接支持串口通信。你可能需要寻找一个现有的iOS串口通信库,或者使用外部硬件(如USB转串口适配器)并通过IOKit进行访问。

步骤 3: 在Flutter中调用原生方法

在你的Flutter项目中,创建一个Dart文件来与原生代码进行通信。例如,在lib/目录下创建一个名为serial_port.dart的文件,并添加以下内容:

import 'package:flutter/services.dart';

class SerialPort {
  static const MethodChannel _channel = MethodChannel('serial_port_channel');

  static Future<void> openPort(String path, int baudrate) async {
    try {
      await _channel.invokeMethod('openPort', {'path': path, 'baudrate': baudrate});
    } on PlatformException catch (e) {
      throw e;
    }
  }

  static Future<void> closePort() async {
    try {
      await _channel.invokeMethod('closePort');
    } on PlatformException catch (e) {
      throw e;
    }
  }

  static Future<Uint8List> readPort(int size) async {
    try {
      final Uint8List result = await _channel.invokeMethod('readPort', {'size': size});
      return result;
    } on PlatformException catch (e) {
      throw e;
    }
  }

  static Future<void> writePort(Uint8List data) async {
    try {
      await _channel.invokeMethod('writePort', {'data': data});
    } on PlatformException catch (e) {
      throw e;
    }
  }
}

步骤 4: 使用SerialPort类

现在,你可以在你的Flutter应用中使用SerialPort类来进行串口通信。例如:

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

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

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

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    // 打开串口
    SerialPort.openPort('/dev/ttyUSB0', 9600);

    // 读取数据(在实际应用中,你可能需要在另一个线程或定时器中执行此操作)
    SerialPort.readPort(10).then((data) {
      print('Received data: ${data.map((byte) => byte.toRadixString(16)).join(', ')}');
    });

    // 写入数据
    SerialPort.writePort(Uint8List.fromList([0x01, 0x02, 0x03]));

    // 关闭串口(在适当的时机调用)
    // SerialPort.closePort();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Serial Port Communication'),
        ),
        body: Center(
          child: Text('Check console for serial port communication output'),
        ),
      ),
    );
  }
}

注意

  1. 权限:在Android上,你需要请求USB权限或使用特定的串口权限。
  2. 线程:串口通信是阻塞操作,因此你应该在单独的线程或Isolate中执行这些操作。
  3. 错误处理:在实际应用中,添加更多的错误处理和日志记录。
  4. iOS:iOS上的串口通信需要更多的工作,因为iOS没有内置的串口API。你可能需要使用
回到顶部