Flutter串口通信插件flutter_turbo_serialport的使用

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

Flutter串口通信插件flutter_turbo_serialport的使用

本库依赖于:felHR85/UsbSerial

变更日志

变更日志

使用说明

注意IDs在USB断开后不会保持不变。

默认参数类型

KEY VALUE
driver AUTO
portInterface -1
returnedDataType UTF8
baudRate 9600
dataBit DATA_BITS_8
stopBit STOP_BITS_1
parity PARITY_NONE
flowControl FLOW_CONTROL_OFF

可选配置

为了确保在Android上记住权限,需要在 android/app/src/main/AndroidManifest.xml 中添加以下意图:

<activity>
  <intent-filter>
    <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
  </intent-filter>

  <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
    android:resource="@xml/device_filter" />
</activity>

并在 android/app/src/main/res/xml/usb_device_filter.xml 文件中创建过滤器文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <!-- 0x0403 / 0x6001: FTDI FT232R UART -->
  <usb-device vendor-id="1027" product-id="24577" />

  <!-- 0x0403 / 0x6015: FTDI FT231X -->
  <usb-device vendor-id="1027" product-id="24597" />

  <!-- 0x2341 / Arduino -->
  <usb-device vendor-id="9025" />

  <!-- 0x16C0 / 0x0483: Teensyduino  -->
  <usb-device vendor-id="5824" product-id="1155" />

  <!-- 0x10C4 / 0xEA60: CP210x UART Bridge -->
  <usb-device vendor-id="4292" product-id="60000" />

  <!-- 0x067B / 0x2303: Prolific PL2303 -->
  <usb-device vendor-id="1659" product-id="8963" />

  <!-- 0x1366 / 0x0105: Segger JLink -->
  <usb-device vendor-id="4966" product-id="261" />

  <!-- 0x1366 / 0x0105: CH340 JLink -->
  <usb-device vendor-id="1A86" product-id="7523" />
</resources>

这里的 vendor-idproduct-id 需要以十进制形式给出,并且可以从 listDevices() 方法中获取。

示例

turboserialport_example

示例代码

import 'dart:async';
import 'dart:math';

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

void main() {
  runApp(
    const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    ),
  );
}

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

  [@override](/user/override)
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late Function _subscribe;
  List<Device> _allDevices = [];
  String _device = '';
  String _data = '';

  [@override](/user/override)
  void initState() {
    super.initState();

    // 此方法仅调用一次
    // 但它是可选的
    SerialportController().init(
      autoConnect: false,
      mode: Mode.$async,
      params: Params(
        driver: DriverType.$auto,
        portInterface: -1, // 所有端口
        returnedDataType: ReturnedDataType.$utf8,
        baudRate: BaudRate.$9600,
        dataBit: DataBit.$8,
        stopBit: StopBit.$1,
        parity: Parity.$none,
        flowControl: FlowControl.$off,
      ),
    );

    WidgetsBinding.instance.addPostFrameCallback((_) {
      _subscribe = SerialportController().addListeners(
        onReadData: onReadData,
        onError: onError,
        onConnected: onConnected,
        onDisconnected: onDisconnected,
        onDeviceAttached: onSearch,
        onDeviceDetached: onSearch,
      );
    });
  }

  [@override](/user/override)
  void dispose() {
    super.dispose();
    _subscribe();
  }

  void onReadData({
    int? deviceId,
    int? portInterface,
    dynamic data, // String | Uint8List
  }) {
    setState(() {
      _data += data as String;
    });
  }

  void onError({
    int? errorCode,
    String? errorMessage,
  }) {
    if (errorMessage != null) {
      _showMyDialog('Error', errorMessage);
    }
  }

  void onConnected({
    int? deviceId,
    int? portInterface,
  }) {
    setState(() {
      _device = 'id: $deviceId $portInterface +';
    });
  }

  void onDisconnected({
    int? deviceId,
    int? portInterface,
  }) {
    setState(() {
      _device = 'id: $deviceId $portInterface -';
    });
  }

  void onSearch({
    int? deviceId,
  }) {
    SerialportController().listDevices().then((List<Device> res) {
      setState(() {
        _allDevices = res;
      });
    });
  }

  Future<void> _showMyDialog(String title, String text) async {
    return showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text(title),
          content: SingleChildScrollView(
            child: ListBody(
              children: <Widget>[
                Text(text),
              ],
            ),
          ),
          actions: <Widget>[
            TextButton(
              child: const Text('Ok'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }

  connect({
    required Device device,
  }) {
    return () {
      device.isConnected().then((bool res) {
        if (res) {
          device.writeString(message: '${Random().nextDouble()}');
        } else {
          device.connect();
        }
      });
    };
  }

  write({
    int? deviceId,
  }) {
    return () {
      SerialportController().writeString(
        message: '${Random().nextDouble()}',
        deviceId: deviceId,
        portInterface: 0,
      );
    };
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Column(
          children: [
            TextButton(
              onPressed: onSearch,
              child: Center(
                child: Text(
                  'Search: ${_allDevices.length}\n$_device',
                  textAlign: TextAlign.center,
                ),
              ),
            ),
          ],
        ),
      ),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: _allDevices.length,
              itemBuilder: (BuildContext context, int index) {
                final Device device = _allDevices.elementAt(index);

                return ListTile(
                  title: TextButton(
                    onPressed: connect(device: device),
                    child: Center(
                      child: Text(
                        'Connect id: ${device.deviceId}',
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
          const SizedBox(height: 16),
          Expanded(
            child: SingleChildScrollView(
              child: Text(
                _data,
                textAlign: TextAlign.center,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

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

1 回复

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


当然,以下是一个使用Flutter串口通信插件flutter_turbo_serialport的示例代码。这个示例展示了如何初始化串口、打开串口、发送数据以及接收数据。

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

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

然后,运行flutter pub get来获取依赖。

接下来,是一个完整的Flutter应用示例,展示如何使用flutter_turbo_serialport进行串口通信:

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

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

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

class _MyAppState extends State<MyApp> {
  FlutterTurboSerialport? _serialPort;
  String _receivedData = "";

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

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

  void _initSerialPort() async {
    // 请求权限(Android特有)
    if (await FlutterTurboSerialport.requestPermission()) {
      // 初始化串口对象
      _serialPort = FlutterTurboSerialport();

      // 打开串口
      var result = await _serialPort!.openPort('/dev/ttyS0', 9600); // 根据实际情况修改串口路径和波特率
      if (result) {
        print("串口打开成功");

        // 设置数据接收回调
        _serialPort!.setDataReceivedCallback((Uint8List data) {
          setState(() {
            _receivedData += String.fromCharCodes(data);
          });
        });
      } else {
        print("串口打开失败");
      }
    } else {
      print("权限请求失败");
    }
  }

  void _closeSerialPort() async {
    if (_serialPort != null && _serialPort!.isOpen()) {
      await _serialPort!.closePort();
      print("串口关闭");
    }
  }

  void _sendData() async {
    if (_serialPort != null && _serialPort!.isOpen()) {
      String dataToSend = "Hello, Serial Port!";
      Uint8List data = Uint8List.fromList(dataToSend.codeUnits);
      await _serialPort!.writePort(data);
      print("数据发送: $dataToSend");
    } else {
      print("串口未打开");
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Serial Port Communication'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('接收到的数据:', style: TextStyle(fontSize: 18)),
              SizedBox(height: 8),
              Text(_receivedData, style: TextStyle(fontSize: 16)),
              SizedBox(height: 24),
              ElevatedButton(
                onPressed: _sendData,
                child: Text('发送数据'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

代码说明:

  1. 依赖引入:在pubspec.yaml文件中添加flutter_turbo_serialport依赖。
  2. 权限请求:在Android上,串口通信需要请求权限,使用FlutterTurboSerialport.requestPermission()方法。
  3. 初始化串口:使用FlutterTurboSerialport()创建串口对象,然后使用openPort(path, baudRate)方法打开串口。
  4. 设置数据接收回调:通过setDataReceivedCallback方法设置数据接收回调,接收到的数据会触发该回调。
  5. 发送数据:使用writePort(data)方法发送数据到串口。
  6. UI展示:使用Flutter的Material Design组件展示接收到的数据和发送数据的按钮。

注意事项:

  • 串口路径:在示例中,串口路径是'/dev/ttyS0',这通常在Linux或Android设备上使用。在Windows或iOS设备上,路径可能会有所不同。
  • 权限:在Android设备上,需要在AndroidManifest.xml中声明串口权限,但flutter_turbo_serialport插件通常会处理这些权限请求。
  • 错误处理:示例代码中省略了详细的错误处理,但在实际应用中,应该添加适当的错误处理逻辑。

希望这个示例代码能帮助你理解如何使用flutter_turbo_serialport插件进行串口通信。如果有更多问题,欢迎继续提问!

回到顶部