Flutter Modbus通信插件modbus_master的使用

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

Flutter Modbus通信插件modbus_master的使用

modbus_master 是一个易于使用的包,它使得 Dart 程序可以作为一个 Modbus/TCP 主设备。

目前支持的功能

  • 离散输入
    • 读取单个离散输入
  • 线圈
    • 读取单个线圈
    • 写入单个线圈
  • 输入寄存器
    • 读取单个输入寄存器
  • 保持寄存器
    • 读取单个保持寄存器
    • 写入单个保持寄存器

当前实现

  • 目前只实现了 TCP IPv4。
  • 使用单一流对象来接收所有从站设备的响应。
  • 此包在单独的隔离区处理套接字网络部分,以使主线程可以自由处理其他任务,如 Flutter 的 UI 部分。
  • 此包可以在支持 dart:io 和 dart:isolate 的所有平台上使用,即 WINDOWS, LINUX, MACOS, ANDROID, IOS。

关于此包的重要信息

每个请求都会生成响应

  • 如果在超时时间内未从从站设备收到响应,则库将生成错误响应,并将其放入 responses() 流中。
  • 发送第一个请求到从站设备时,会建立连接并保持连接状态。
  • 每次发送请求到从站设备时,首先检查连接是否已建立。
    • 如果连接已建立,则发送请求。
    • 如果发现连接断开,则尝试重新建立连接。
  • 如果已有 247 个活动连接,并且向新地址发送请求,则最早建立的连接会被断开,新的连接会被建立。
  • 如果执行了 close 方法,则首先生成所有待处理请求的响应。之后,关闭所有活动连接。

超时

  • 如果从站设备在超时时间内未响应,包将生成错误响应。超时时间在两种不同场景下有所不同:
    • 场景 1:当连接已经建立时
      • 如果在超时时间内(默认为 1000 毫秒)未生成响应,则主设备生成错误响应。
      • 超时时间 = 请求超时(默认 1000 毫秒)
    • 场景 2:当连接尚未建立时
      • 主设备尝试建立连接。
      • 如果在套接字连接超时时间内(默认 2000 毫秒)未建立连接,则主设备生成错误响应。
      • 如果连接已建立,则如果在超时时间内(默认 1000 毫秒)未生成响应,则主设备生成错误响应。
      • 最大超时时间 = 套接字连接超时时间(默认 2000 毫秒)+ 请求超时时间(默认 1000 毫秒)

限制

  • 只支持 IPv4,不支持 IPv6。
  • 只能同时连接 247 个从站设备,超过 247 个时,最早的连接会被断开。
  • 只能一次读取单个元素,不支持读取多个线圈或多个寄存器。
  • 只能一次写入单个元素,不支持写入多个线圈或多个寄存器。
  • 需要 dart 3.0 及以上版本,因为它使用了 dart 记录。

使用此库的步骤

  1. 在需要创建对象的函数中添加 async 关键字。
  2. 创建 ModbusMaster 类的实例。
final modbusMaster = await ModbusMaster.start();
  1. 监听响应流。
modbusMaster.responses().listen(
  (response) {
    print(response);
  }
);
  1. 使用以下命令向从站发送请求:
  • 读取单个线圈
modbusMaster.readCoil(
  ipv4: '192.168.29.163',
  portNo: 502,
  elementNumberOneTo65536: 11,
);
  • 读取单个离散输入
modbusMaster.readDiscreteInput(
  ipv4: '192.168.1.5',
  elementNumberOneTo65536: 11,
);
  • 读取单个保持寄存器
modbusMaster.readHoldingRegister(
  ipv4: '192.168.1.5',
  elementNumberOneTo65536: 11,
);
  • 读取单个输入寄存器
modbusMaster.readInputRegister(
  ipv4: '192.168.1.5',
  elementNumberOneTo65536: 11,
);
  • 写入单个线圈
modbusMaster.writeCoil(
  ipv4: '192.168.1.5',
  elementNumberOneTo65536: 11,
  valueToBeWritten: true,
);
  • 写入单个保持寄存器
modbusMaster.writeHoldingRegister(
  ipv4: '192.168.1.5',
  elementNumberOneTo65536: 11,
  valueToBeWritten: 15525,
);
  1. 最后调用 close 方法以关闭所有 TCP 连接并停止 modbusMaster
modbusMaster.close();

示例代码

1. 首先启动 Modbus/TCP 服务器(即从站设备)使用 pyModbusTCP 库

from pyModbusTCP.server import ModbusServer
import time
import socket

# 获取 IPv4 地址
host_ip = socket.gethostbyname(socket.gethostname())
modbusServer = ModbusServer(host=host_ip, port=502, no_block=True)

try:
    print('正在尝试启动 MODBUS/TCP 从站服务器')
    modbusServer.start()
    print(f'MODBUS/TCP 从站服务器在线于 {host_ip}')
    
    set_coil = True
    while True:
        if set_coil:
            # 线圈地址是线圈编号减一,因此 11-1 写入
            modbusServer.data_bank.set_coils(11-1, bit_list=[True])
            print('线圈 11 已设置')
        else:
            modbusServer.data_bank.set_coils(11-1, bit_list=[False])
            print('线圈 11 已复位')

        print('等待 5 秒钟')
        time.sleep(5)
        set_coil = not set_coil

except Exception as error:
    print(error)
    print(f'尝试停止 MODBUS/TCP 从站服务器于 {host_ip}')
    modbusServer.stop()
    print('MODBUS/TCP 从站服务器已离线')

2. 检查 Modbus/TCP 服务器(从站)是否在线

  • 使用 Windows PowerShell 命令检查服务器端口是否打开
# 使用你的设备 IP 地址,该地址由 Python 脚本打印
netstat -na | Select-String "192.168.29.163:502"
  • 当套接字监听时,netstat 命令会显示
TCP    192.168.29.163:502   0.0.0.0:0    LISTENING
  • 当套接字关闭时,netstat 命令不会显示任何内容

3. 使用 Dart 代码与 ModbusMaster 库读取从站设备(pyModbusTCP 服务器)的线圈值

import 'modbus_master_isolate.dart';

void main() {
  testReadingCoil();
}

void testReadingCoil() async {
  final modbusMaster = await ModbusMaster.start();

  int countResponseReceived = 0;
  modbusMaster.responses().listen(
    (response) {
      ++countResponseReceived;
      print(response);
      if (countResponseReceived >= 3) {
        // 收到三个响应后关闭 modbusMaster
        modbusMaster.close();
      }
    },
  );

  for (int i = 1; i <= 5; ++i) {
    print('-> 请求 $i');
    try {
      modbusMaster.readCoil(
        ipv4: '192.168.29.163',
        portNo: 502,
        elementNumberOneTo65536: 11,
      );
    } catch (e) {
      // 执行读取请求后调用 close 方法时会抛出异常
      print('读取过程中出现异常 $e');
    }
    await Future.delayed(Duration(seconds: 5));
  }
}

4. 通过反复按 Ctrl+C 关闭 Python 脚本

  • 使用 netstat 命令检查套接字是否打开。
  • 如果 Python 脚本已结束但套接字仍然打开,使用 Python 语句关闭 modbusServer
modbusServer.stop()

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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用modbus_master插件进行Modbus通信的示例代码。这个示例将展示如何设置Modbus客户端,连接到Modbus服务器,并读取和写入寄存器。

首先,确保你的Flutter项目中已经添加了modbus_master依赖。在pubspec.yaml文件中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  modbus_master: ^1.0.0  # 请使用最新版本号

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

接下来是示例代码,展示如何使用modbus_master插件:

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

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

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

class _MyAppState extends State<MyApp> {
  ModbusTcpClient? _client;
  String _result = "";

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

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

  Future<void> _initModbusClient() async {
    _client = ModbusTcpClient('192.168.1.100', port: 502); // 替换为你的Modbus服务器地址和端口
    await _client!.connect();

    if (_client!.isConnected!) {
      setState(() {
        _result = "Connected to Modbus server";
      });
      
      // 读取保持寄存器示例(功能码03)
      Uint8List response = await _client!.readHoldingRegisters(slaveId: 1, startAddress: 0, numberOfPoints: 10);
      List<int> registers = Uint16List.view(response.buffer, response.byteOffset, response.length ~/ 2).toList();
      print("Holding Registers: $registers");

      // 写入单个保持寄存器示例(功能码06)
      Uint8List writeResponse = await _client!.writeSingleRegister(slaveId: 1, startAddress: 0, value: 12345);
      print("Write Single Register Response: $writeResponse");

      // 写入多个保持寄存器示例(功能码16)
      List<int> valuesToWrite = [67890, 111213];
      Uint8List multipleWriteResponse = await _client!.writeMultipleRegisters(slaveId: 1, startAddress: 10, values: valuesToWrite);
      print("Write Multiple Registers Response: $multipleWriteResponse");
    } else {
      setState(() {
        _result = "Failed to connect to Modbus server";
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Modbus Master Example'),
        ),
        body: Center(
          child: Text(_result),
        ),
      ),
    );
  }
}

代码解释:

  1. 依赖添加:在pubspec.yaml中添加modbus_master依赖。
  2. 客户端初始化:在_initModbusClient方法中创建并连接到Modbus TCP客户端。
  3. 读取保持寄存器:使用readHoldingRegisters方法读取保持寄存器。
  4. 写入单个保持寄存器:使用writeSingleRegister方法写入单个保持寄存器。
  5. 写入多个保持寄存器:使用writeMultipleRegisters方法写入多个保持寄存器。
  6. UI显示:在UI中显示连接结果。

注意事项:

  • 请确保Modbus服务器正在运行,并且IP地址和端口号正确。
  • 根据实际需要调整寄存器地址和值。
  • 在实际项目中,你可能需要添加更多的错误处理和状态管理。

这个示例代码展示了如何使用modbus_master插件进行基本的Modbus通信。根据你的需求,你可以进一步扩展和修改这个示例。

回到顶部