modbus_client 1.2.1 copy "modbus_client: ^1.2.1" to clipboard
modbus_client: ^1.2.1 copied to clipboard

This is the base Modbus Client package sending requests to a remote device (i.e. Modbus Server) via Serial (ASCII and RTU) or TCP/IP connection.

Introduction #

This is a set of three packages implementing Modbus Client sending requests to a remote device (i.e. Modbus Server).

  • Modbus Client is the base implementation for the TCP and Serial packages.
  • Modbus Client TCP implements the TCP protocol to send requests via ethernet networks.
  • Modbus Client Serial implements the ASCII and RTU protocols to send requests via Serial Port

The split of the packages is done to minimize dependencies on your project.

Notes for serial implementation

  • For Flutter projects: you only need to add flutter_libserialport package to your own project (i.e. run flutter pub add flutter_libserialport)
  • For Dart projects: you need to build the serial library libserialport build instructions. As a workaround you can create a dummy Flutter project, add the flutter_libserialport package, build it and copy the auto-generated serialport library from the build folder into your project's root folder or wherever your binary code will run.

Features #

Usage #

Using modbus client is simple. You define your elements, create a read or write request out of them and use the client to send the request.

Read Request #

import 'package:modbus_client/modbus_client.dart';
import 'package:modbus_client_tcp/modbus_client_tcp.dart';

void main() async {
  // Create a modbus int16 register element
  var batteryTemperature = ModbusInt16Register(
      name: "BatteryTemperature",
      type: ModbusElementType.inputRegister,
      address: 22,
      uom: "°C",
      multiplier: 0.1,
      onUpdate: (self) => print(self));

  // Discover the Modbus server
  var serverIp = await ModbusClientTcp.discover("192.168.0.0");
  if (serverIp == null) {
    ModbusAppLogger.shout("No modbus server found!");
    return;
  }
  
  // Create the modbus client.
  var modbusClient = ModbusClientTcp(serverIp, unitId: 1);

  // Send a read request from the element
  await modbusClient.send(batteryTemperature.getReadRequest());

  // Ending here
  modbusClient.disconnect();
}

Write Request #

import 'package:modbus_client/modbus_client.dart';
import 'package:modbus_client_tcp/modbus_client_tcp.dart';

enum BatteryStatus implements ModbusIntEnum {
  offline(0),
  standby(1),
  running(2),
  fault(3),
  sleepMode(4);

  const BatteryStatus(this.intValue);

  @override
  final int intValue;

  @override
  String toString() {
    return name;
  }
}

void main() async {
  var batteryStatus = ModbusEnumRegister(
      name: "BatteryStatus",
      address: 11,
      type: ModbusElementType.holdingRegister,
      enumValues: BatteryStatus.values,
      onUpdate: (self) => print(self));

  // Discover the Modbus server
  var serverIp = await ModbusClientTcp.discover("192.168.0.0");
  if (serverIp == null) {
    ModbusAppLogger.shout("No modbus server found!");
    return;
  }
  
  // Create the modbus client.
  var modbusClient = ModbusClientTcp(serverIp, unitId: 1);

  var req = batteryStatus.getWriteRequest(BatteryStatus.running);
  var res = await modbusClient.send(req);
  print(res.name);

  modbusClient.disconnect();
}

Modbus Elements #

This library has a wide range of defined modbus elements having a name, a description, a modbus address and an update callback you can use in case the element value has been updated.

Bit and Numeric Elements #

Typical elements are simple bit and numeric values:

  • ModbusDiscreteInput
  • ModbusCoil
  • ModbusInt16Register
  • ModbusUint16Register
  • ModbusInt32Register
  • ModbusUint32Register

Numeric elements have an uom (i.e. unit of measure), a multiplier and offset to make conversion from raw to engineering values (i.e. value = read_value*multiplier + offset), and viewDecimalPlaces to print out only needed decimals.

Enum Element #

To read and write an enum as an element you can use a ModbusEnumRegister

/// Implement [ModbusIntEnum] to use it with a [ModbusEnumRegister]
enum BatteryStatus implements ModbusIntEnum {
  offline(0),
  standby(1),
  running(2),
  fault(3),
  sleepMode(4);

  const BatteryStatus(this.intValue);

  @override
  final int intValue;

  @override
  String toString() {
    return name;
  }
}


void main() async {
  ModbusAppLogger(Level.FINEST);

  var batteryStatus = ModbusEnumRegister(
      name: "BatteryStatus",
      address: 11,
      type: ModbusElementType.holdingRegister,
      enumValues: BatteryStatus.values);

  var modbusClient = ModbusClientTcp("127.0.0.1", unitId: 1);
  await modbusClient.send(batteryStatus.getReadRequest());
  modbusClient.disconnect();
}

Status Element #

Similar to enum is a status element. Within a ModbusStatusRegister you can define all the possible statues (i.e. numeric <-> string pairs) for the element.

ModbusStatusRegister(
    name: "Device status",
    type: ModbusElementType.holdingRegister,
    address: 32089,
    statusValues: [
      ModbusStatus(0x0000, "Standby: initializing"),
      ModbusStatus(0x0001, "Standby: detecting insulation resistance"),
      ModbusStatus(0x0002, "Standby: detecting irradiation"),
      ModbusStatus(0x0003, "Standby: drid detecting"),
      ModbusStatus(0x0100, "Starting"),
      ModbusStatus(0x0200, "On-grid (Off-grid mode: running)"),
      ModbusStatus(0x0201,
          "Grid connection: power limited (Off-grid mode: running: power limited)"),
      ModbusStatus(0x0202,
          "Grid connection: selfderating (Off-grid mode: running: selfderating)"),
      ModbusStatus(0x0203, "Off-grid Running"),
      ModbusStatus(0x0300, "Shutdown: fault"),
      ModbusStatus(0x0301, "Shutdown: command"),
      ModbusStatus(0x0302, "Shutdown: OVGR"),
      ModbusStatus(0x0303, "Shutdown: communication disconnected"),
      ModbusStatus(0x0304, "Shutdown: power limited"),
      ModbusStatus(0x0305, "Shutdown: manual startup required"),
      ModbusStatus(0x0306, "Shutdown: DC switches disconnected"),
      ModbusStatus(0x0307, "Shutdown: rapid cutoff"),
      ModbusStatus(0x0308, "Shutdown: input underpower"),
      ModbusStatus(0x0401, "Grid scheduling: cosφ-P curve"),
      ModbusStatus(0x0402, "Grid scheduling: Q-U curve"),
      ModbusStatus(0x0403, "Grid scheduling: PF-U curve"),
      ModbusStatus(0x0404, "Grid scheduling: dry contact"),
      ModbusStatus(0x0405, "Grid scheduling: Q-P curve"),
      ModbusStatus(0x0500, "Spotcheck ready"),
      ModbusStatus(0x0501, "Spotchecking"),
      ModbusStatus(0x0600, "Inspecting"),
      ModbusStatus(0X0700, "AFCI self check"),
      ModbusStatus(0X0800, "I-V scanning"),
      ModbusStatus(0X0900, "DC input detection"),
      ModbusStatus(0X0A00, "Running: off-grid charging"),
      ModbusStatus(0xA000, "Standby: no irradiation"),
    ]),

Bit Mask Element #

Use ModbusBitMaskRegister if your device has registers where each bit value has a special meaning. You can define both an active and inactive value for each ModbusBitMask object.

ModbusBitMaskRegister(
    name: "Alarm 1",
    type: ModbusElementType.holdingRegister,
    address: 32008,
    bitMasks: [
      ModbusBitMask(0, "High String Input Voltage [2001 Major]"),
      ModbusBitMask(1, "DC Arc Fault [2002 Major]"),
      ModbusBitMask(2, "String Reverse Connection [2011 Major]"),
      ModbusBitMask(3, "String Current Backfeed [2012 Warning]"),
      ModbusBitMask(4, "Abnormal String Power [2013 Warning]"),
      ModbusBitMask(5, "AFCI Self-Check Fail. [2021 Major]"),
      ModbusBitMask(6, "Phase Wire Short-Circuited to PE [2031 Major]"),
      ModbusBitMask(7, "Grid Loss [2032 Major]"),
      ModbusBitMask(8, "Grid Undervoltage [2033 Major]"),
      ModbusBitMask(9, "Grid Overvoltage [2034 Major]"),
      ModbusBitMask(10, "Grid Volt. Imbalance [2035 Major]"),
      ModbusBitMask(11, "Grid Overfrequency [2036 Major]"),
      ModbusBitMask(12, "Grid Underfrequency [2037 Major]"),
      ModbusBitMask(13, "Unstable Grid Frequency [2038 Major]"),
      ModbusBitMask(14, "Output Overcurrent [2039 Major]"),
      ModbusBitMask(15, "Output DC Component Overhigh [2040 Major]"),
    ]),

Epoch/DateTime Element #

Use ModbusEpochRegister if your device holds timestamp values as Epoch/Unix time in seconds.

ModbusEpochRegister(
    name: "Startup time",
    type: ModbusElementType.holdingRegister,
    address: 32091,
    isUtc: false);

Epoch/DateTime Element #

Use ModbusEpochRegister if your device holds timestamp values as Epoch/Unix time in seconds.

ModbusEpochRegister(
    name: "Startup time",
    type: ModbusElementType.holdingRegister,
    address: 32091,
    isUtc: false);

Bytes Array Element #

Use ModbusBytesRegister if you want to read and write multiple bytes/registers at a time. Note that byteCount cannot exceed 250 bytes which is the multiple read bytes limit for Modbus/RTU. Note that the protocol limit depends on multiple factors:

  • Read & Write have different limits
  • Modbus RTU and TCP have different limits
  • Device dependent limits To get the right limit please refer to Modbus specs and your device manual.
var bytesRegister = ModbusBytesRegister(
    name: "BytesArray",
    address: 4,
    byteCount: 10,
    onUpdate: (self) => print(self));

// Create the modbus client.
var modbusClient = ModbusClientTcp("127.0.0.1", unitId: 1);

var req1 = bytesRegister.getWriteRequest(Uint8List.fromList(
    [0x01, 0x02, 0x03, 0x04, 0x05, 0x66, 0x07, 0x08, 0x09, 0x0A]));
var res = await modbusClient.send(req1);
print(res);

var req2 = bytesRegister.getReadRequest();
res = await modbusClient.send(req2);
print(bytesRegister.value);

modbusClient.disconnect();

Element Group #

You can define a ModbusElementsGroup to optimize the elements reading. The most the element addresses are contiguous the most performant is the request. The address range limit for bits is 2000 and 125 for registers. You can use the ModbusElementsGroup object as a kind of list of element.

import 'package:modbus_client/modbus_client.dart';
import 'package:modbus_client_tcp/modbus_client_tcp.dart';

enum BatteryStatus implements ModbusIntEnum {
  offline(0),
  standby(1),
  running(2),
  fault(3),
  sleepMode(4);

  const BatteryStatus(this.intValue);

  @override
  final int intValue;
}

void main() async {
  // Create a modbus elements group
  var batteryRegs = ModbusElementsGroup([
    ModbusEnumRegister(
        name: "BatteryStatus",
        type: ModbusElementType.holdingRegister,
        address: 37000,
        enumValues: BatteryStatus.values),
    ModbusInt32Register(
        name: "BatteryChargingPower",
        type: ModbusElementType.holdingRegister,
        address: 37001,
        uom: "W",
        description: "> 0: charging - < 0: discharging"),
    ModbusUint16Register(
        name: "BatteryCharge",
        type: ModbusElementType.holdingRegister,
        address: 37004,
        uom: "%",
        multiplier: 0.1),
    ModbusUint16Register(
        name: "BatteryTemperature",
        type: ModbusElementType.holdingRegister,
        address: 37022,
        uom: "°C",
        multiplier: 0.1),
  ]);

  // Create the modbus client.
  var modbusClient = ModbusClientTcp("127.0.0.1", unitId: 1);

  // Send a read request from the group
  await modbusClient.send(batteryRegs.getReadRequest());
  print(batteryRegs[0]);
  print(batteryRegs[1]);
  print(batteryRegs[2]);
  print(batteryRegs[3]);

  // Ending here
  modbusClient.disconnect();
}

Modbus File Records #

This library supports function codes 0x14 and 0x15 to read and write different types of numeric records.

File Record Types #

  • ModbusFileInt16Record
  • ModbusFileUint16Record
  • ModbusFileInt32Record
  • ModbusFileUint32Record
  • ModbusFileFloatRecord
  • ModbusFileDoubleRecord
  • ModbusFileMultipleRecord
import 'dart:typed_data';

import 'package:logging/logging.dart';
import 'package:modbus_client/modbus_client.dart';
import 'package:modbus_client_tcp/modbus_client_tcp.dart';

void main() async {
  // Simple modbus logging
  ModbusAppLogger(Level.FINE);

  // Create the modbus client.
  var modbusClient = ModbusClientTcp("127.0.0.1", unitId: 1);

  // Write two file records
  var r1 = ModbusFileUint16Record(
      fileNumber: 4,
      recordNumber: 1,
      recordData: Uint16List.fromList([12573, 56312]));
  var r2 = ModbusFileDoubleRecord(
      fileNumber: 3,
      recordNumber: 9,
      recordData: Float64List.fromList([123.5634, 125756782.8492]));
  await modbusClient.send(ModbusFileRecordsWriteRequest([r1, r2]));

  // Read two file records
  r1 = ModbusFileUint16Record.empty(
      fileNumber: 4, recordNumber: 1, recordDataCount: 2);
  r2 = ModbusFileDoubleRecord.empty(
      fileNumber: 3, recordNumber: 9, recordDataCount: 2);
  await modbusClient.send(ModbusFileRecordsReadRequest([r1, r2]));

  // Write multiple records
  var multipleRecords =
      ModbusFileMultipleRecord(fileNumber: 4, recordNumber: 1);
  multipleRecords.addNext(ModbusRecordType.int16, -123);
  multipleRecords.addNext(ModbusRecordType.uint16, 5000);
  multipleRecords.addNext(ModbusRecordType.int32, -1234567890);
  multipleRecords.addNext(ModbusRecordType.uint32, 1234567890);
  multipleRecords.addNext(ModbusRecordType.float, 123.45);
  multipleRecords.addNext(ModbusRecordType.double, 12345.6789);
  await modbusClient.send(multipleRecords.getWriteRequest());

  multipleRecords = ModbusFileMultipleRecord.empty(
      fileNumber: 4, recordNumber: 1, recordDataByteLength: 24);
  await modbusClient.send(multipleRecords.getReadRequest());
  multipleRecords.start();
  print(multipleRecords.getNext(ModbusRecordType.int16));
  print(multipleRecords.getNext(ModbusRecordType.uint16));
  print(multipleRecords.getNext(ModbusRecordType.int32));
  print(multipleRecords.getNext(ModbusRecordType.uint32));
  print(multipleRecords.getNext(ModbusRecordType.float));
  print(multipleRecords.getNext(ModbusRecordType.double));

  // Ending here
  modbusClient.disconnect();
}
8
likes
140
pub points
78%
popularity

Publisher

verified publishercabella.net

This is the base Modbus Client package sending requests to a remote device (i.e. Modbus Server) via Serial (ASCII and RTU) or TCP/IP connection.

Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD-3-Clause (LICENSE)

Dependencies

collection, logging

More

Packages that depend on modbus_client