From afdacd5c792c91cf692ac6ff198830980986a2eb Mon Sep 17 00:00:00 2001 From: Jeff Ward Date: Thu, 18 May 2023 12:41:58 -0400 Subject: [PATCH 1/5] Update readme.md --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 7f3eb76..8ffd180 100644 --- a/readme.md +++ b/readme.md @@ -41,7 +41,7 @@ PM> Install-Package Meadow.Modbus Create a ModbusRtuClient instance, passing in the SerialPort for COM4 and DigitalOutputPort used for the enable. -``` +```csharp var port = Device.CreateSerialPort(Device.SerialPortNames.Com4, 19200, 8, Meadow.Hardware.Parity.None, Meadow.Hardware.StopBits.One); port.WriteTimeout = port.ReadTimeout = TimeSpan.FromSeconds(5); var enable = Device.CreateDigitalOutputPort(Device.Pins.D02, false); @@ -51,7 +51,7 @@ The Temco TSTAT8 uses holding registers for all of its interfacing. It has a lot To read the current temperature and output it to the console every 5 seconds, we can use a loop like this: -``` +```csharp byte address = 201; ushort tempRegister = 121; // current temp, in tenths of a degree @@ -68,7 +68,7 @@ while (true) Writing to a holding register is similar to the read shown above. Below is code that reads the setpoint, changes it with a write, then re-reads to verify the change: -``` +```csharp byte address = 201; ushort setPointRegister = 345; // occupied setpoint, in tenths of a degree From 7d6e4f62c0b969fb7df51e1e4c91323a4b7dcd7a Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 26 Sep 2023 11:14:56 -0700 Subject: [PATCH 2/5] Release 1.3.4 --- src/Meadow.Modbus.sln | 9 --------- src/Meadow.Modbus/Meadow.Modbus.csproj | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Meadow.Modbus.sln b/src/Meadow.Modbus.sln index cdb0ef9..5e7504f 100644 --- a/src/Meadow.Modbus.sln +++ b/src/Meadow.Modbus.sln @@ -13,8 +13,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Modbus.Unit.Tests", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_refs", "_refs", "{F3A19D25-32FB-4420-B50D-220C86E5F8A0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Contracts", "..\..\Meadow.Contracts\Source\Meadow.Contracts\Meadow.Contracts.csproj", "{B2CE678E-2C35-490E-B311-8401567D8ABA}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -29,18 +27,11 @@ Global {E6F3EFB2-C8E9-4F7B-826D-79DC3AB5BDD2}.Debug|Any CPU.Build.0 = Debug|Any CPU {E6F3EFB2-C8E9-4F7B-826D-79DC3AB5BDD2}.Release|Any CPU.ActiveCfg = Release|Any CPU {E6F3EFB2-C8E9-4F7B-826D-79DC3AB5BDD2}.Release|Any CPU.Build.0 = Release|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Release|Any CPU.Build.0 = Release|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {B2CE678E-2C35-490E-B311-8401567D8ABA} = {F3A19D25-32FB-4420-B50D-220C86E5F8A0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5FCB68E9-82F3-44F9-BF93-D9A8F55D7F9E} diff --git a/src/Meadow.Modbus/Meadow.Modbus.csproj b/src/Meadow.Modbus/Meadow.Modbus.csproj index c649696..70921f2 100644 --- a/src/Meadow.Modbus/Meadow.Modbus.csproj +++ b/src/Meadow.Modbus/Meadow.Modbus.csproj @@ -22,6 +22,6 @@ - + From 010e0a57a77e98d220a6de4ed5ef7123c32e5126 Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Fri, 10 Nov 2023 16:54:10 -0800 Subject: [PATCH 3/5] Release 1.4.0.3 --- src/Meadow.Modbus.sln | 9 --------- src/Meadow.Modbus/Meadow.Modbus.csproj | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Meadow.Modbus.sln b/src/Meadow.Modbus.sln index cdb0ef9..5e7504f 100644 --- a/src/Meadow.Modbus.sln +++ b/src/Meadow.Modbus.sln @@ -13,8 +13,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Modbus.Unit.Tests", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_refs", "_refs", "{F3A19D25-32FB-4420-B50D-220C86E5F8A0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Contracts", "..\..\Meadow.Contracts\Source\Meadow.Contracts\Meadow.Contracts.csproj", "{B2CE678E-2C35-490E-B311-8401567D8ABA}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -29,18 +27,11 @@ Global {E6F3EFB2-C8E9-4F7B-826D-79DC3AB5BDD2}.Debug|Any CPU.Build.0 = Debug|Any CPU {E6F3EFB2-C8E9-4F7B-826D-79DC3AB5BDD2}.Release|Any CPU.ActiveCfg = Release|Any CPU {E6F3EFB2-C8E9-4F7B-826D-79DC3AB5BDD2}.Release|Any CPU.Build.0 = Release|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Release|Any CPU.Build.0 = Release|Any CPU - {B2CE678E-2C35-490E-B311-8401567D8ABA}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {B2CE678E-2C35-490E-B311-8401567D8ABA} = {F3A19D25-32FB-4420-B50D-220C86E5F8A0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5FCB68E9-82F3-44F9-BF93-D9A8F55D7F9E} diff --git a/src/Meadow.Modbus/Meadow.Modbus.csproj b/src/Meadow.Modbus/Meadow.Modbus.csproj index 249c12b..ad78740 100644 --- a/src/Meadow.Modbus/Meadow.Modbus.csproj +++ b/src/Meadow.Modbus/Meadow.Modbus.csproj @@ -23,6 +23,6 @@ - + From f97332baa205eca88c086db05d62be27037c0bf2 Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 28 Nov 2023 17:54:46 -0800 Subject: [PATCH 4/5] Release 1.5.0 --- src/Meadow.Modbus/Clients/ModbusClientBase.cs | 42 ++++++++++++------- src/Meadow.Modbus/Clients/ModbusTcpClient.cs | 9 ++++ src/Meadow.Modbus/Meadow.Modbus.csproj | 2 +- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/Meadow.Modbus/Clients/ModbusClientBase.cs b/src/Meadow.Modbus/Clients/ModbusClientBase.cs index 494f912..6ff22c3 100644 --- a/src/Meadow.Modbus/Clients/ModbusClientBase.cs +++ b/src/Meadow.Modbus/Clients/ModbusClientBase.cs @@ -13,7 +13,10 @@ namespace Meadow.Modbus; /// public abstract class ModbusClientBase : IModbusBusClient, IDisposable { - private const int MaxRegisterReadCount = 125; + private const int MaxRegisterReadCount = 0x7d; + private const int MaxCoilReadCount = 0x7d0; + private const int MaxRegisterWriteCount = 0x7b; + private const int MaxCoilWriteCount = 0x7b0; /// /// Event triggered when the client is disconnected. @@ -161,6 +164,11 @@ public async Task WriteHoldingRegister(byte modbusAddress, ushort register, usho /// The values to write to the registers. public async Task WriteHoldingRegisters(byte modbusAddress, ushort startRegister, IEnumerable values) { + if (values.Count() > MaxRegisterWriteCount) + { + throw new ArgumentException($"A maximum of {MaxRegisterWriteCount} registers can be written at one time"); + } + if (startRegister > 40000) { // holding registers are defined as starting at 40001, but the actual bus read doesn't use the address, but instead the offset @@ -205,7 +213,7 @@ public async Task ReadHoldingRegistersFloat(byte modbusAddress, ushort for (int i = 0; i < values.Length; i++) { - values[i] = ConvertUShortsToFloat(data[i * 2 + 1], data[i * 2]); + values[i] = ConvertUShortsToFloat(data[(i * 2) + 1], data[i * 2]); } return values; } @@ -219,6 +227,8 @@ public async Task ReadHoldingRegistersFloat(byte modbusAddress, ushort /// An array of ushort values representing the registers. public async Task ReadHoldingRegisters(byte modbusAddress, ushort startRegister, int registerCount) { + if (registerCount > MaxRegisterReadCount) throw new ArgumentException($"A maximum of {MaxRegisterReadCount} registers can be retrieved at one time"); + if (startRegister > 40000) { // holding registers are defined as starting at 40001, but the actual bus read doesn't use the address, but instead the offset @@ -226,8 +236,6 @@ public async Task ReadHoldingRegisters(byte modbusAddress, ushort star startRegister -= 40001; } - if (registerCount > MaxRegisterReadCount) throw new ArgumentException($"A maximum of {MaxRegisterReadCount} registers can be retrieved at one time"); - var message = GenerateReadMessage(modbusAddress, ModbusFunction.ReadHoldingRegister, startRegister, registerCount); await _syncRoot.WaitAsync(); @@ -237,6 +245,7 @@ public async Task ReadHoldingRegisters(byte modbusAddress, ushort star { await DeliverMessage(message); result = await ReadResult(ModbusFunction.ReadHoldingRegister); + if (result.Length == 0) return Array.Empty(); } finally { @@ -246,7 +255,7 @@ public async Task ReadHoldingRegisters(byte modbusAddress, ushort star var registers = new ushort[registerCount]; for (var i = 0; i < registerCount; i++) { - registers[i] = (ushort)((result[i * 2] << 8) | (result[i * 2 + 1])); + registers[i] = (ushort)((result[i * 2] << 8) | (result[(i * 2) + 1])); } return registers; } @@ -277,7 +286,7 @@ public async Task ReadInputRegisters(byte modbusAddress, ushort startR try { await DeliverMessage(message); - result = await ReadResult(ModbusFunction.ReadHoldingRegister); + result = await ReadResult(ModbusFunction.ReadInputRegister); } finally { @@ -287,7 +296,7 @@ public async Task ReadInputRegisters(byte modbusAddress, ushort startR var registers = new ushort[result.Length / 2]; for (var i = 0; i < registers.Length; i++) { - registers[i] = (ushort)((result[i * 2] << 8) | (result[i * 2 + 1])); + registers[i] = (ushort)((result[i * 2] << 8) | (result[(i * 2) + 1])); } return registers; } @@ -314,6 +323,11 @@ public async Task WriteCoil(byte modbusAddress, ushort register, bool value) /// public async Task WriteMultipleCoils(byte modbusAddress, ushort startRegister, IEnumerable values) { + if (values.Count() > MaxCoilWriteCount) + { + throw new ArgumentException($"A maximum of {MaxCoilWriteCount} coils can be written at one time"); + } + // Reduce bool value list to 8 bit byte array ushort byteArrayLength = (ushort)((values.Count() / 8) + (ushort)((values.Count() % 8) > 0 ? 1 : 0)); // Calc # 8 bit bytes needed to TX byte[] msgSegment = new byte[2 + 1 + byteArrayLength]; // StartAddr + coils + value bytes @@ -345,7 +359,7 @@ public async Task WriteMultipleCoils(byte modbusAddress, ushort startRegister, I /// public async Task ReadCoils(byte modbusAddress, ushort startCoil, int coilCount) { - if (coilCount > MaxRegisterReadCount) throw new ArgumentException($"A maximum of {MaxRegisterReadCount} coils can be retrieved at one time"); + if (coilCount > MaxCoilReadCount) throw new ArgumentException($"A maximum of {MaxCoilReadCount} coils can be retrieved at one time"); var message = GenerateReadMessage(modbusAddress, ModbusFunction.ReadCoil, startCoil, coilCount); await _syncRoot.WaitAsync(); @@ -355,7 +369,7 @@ public async Task ReadCoils(byte modbusAddress, ushort startCoil, int co try { await DeliverMessage(message); - result = await ReadResult(ModbusFunction.ReadHoldingRegister); + result = await ReadResult(ModbusFunction.ReadCoil); } finally { @@ -384,18 +398,18 @@ private float ConvertUShortsToFloat(ushort high, ushort low) // Combine the high and low values into a single uint uint input = (uint)(((high & 0x00FF) << 24) | ((high & 0xFF00) << 8) | - (low & 0x00FF) << 8 | - low >> 8); + ((low & 0x00FF) << 8) | + (low >> 8)); // Get the sign bit uint signBit = (input >> 31) & 1; int sign = 1 - (int)(2 * signBit); // Get the exponent bits - var exponentBits = ((input >> 23) & 0xFF); + var exponentBits = (input >> 23) & 0xFF; var exponent = exponentBits - 127; // Get the fraction - var fractionBits = (input & 0x7FFFFF); - var fraction = 1.0 + fractionBits / Math.Pow(2, 23); + var fractionBits = input & 0x7FFFFF; + var fraction = 1.0 + (fractionBits / Math.Pow(2, 23)); // get the value return (float)(sign * fraction * Math.Pow(2, exponent)); diff --git a/src/Meadow.Modbus/Clients/ModbusTcpClient.cs b/src/Meadow.Modbus/Clients/ModbusTcpClient.cs index b941335..d510001 100644 --- a/src/Meadow.Modbus/Clients/ModbusTcpClient.cs +++ b/src/Meadow.Modbus/Clients/ModbusTcpClient.cs @@ -30,6 +30,15 @@ public class ModbusTcpClient : ModbusClientBase, IDisposable private ushort _transaction = 0; private byte[] _responseBuffer = new byte[300]; // I think the max is 9 + 255, but this gives a little room + /// + /// Initializes a new instance of the class using the specified destination address and port. + /// + /// The destination address. + public ModbusTcpClient(IPEndPoint destination) + : this(destination.Address, (short)destination.Port) + { + } + /// /// Initializes a new instance of the class using the specified destination address and port. /// diff --git a/src/Meadow.Modbus/Meadow.Modbus.csproj b/src/Meadow.Modbus/Meadow.Modbus.csproj index ad78740..69c4582 100644 --- a/src/Meadow.Modbus/Meadow.Modbus.csproj +++ b/src/Meadow.Modbus/Meadow.Modbus.csproj @@ -23,6 +23,6 @@ - + From c6bd28d53d812b630cdc6661aff29e1d528e640d Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Wed, 6 Dec 2023 18:08:48 -0800 Subject: [PATCH 5/5] Release 1.6.0 --- src/Meadow.Modbus/Meadow.Modbus.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Meadow.Modbus/Meadow.Modbus.csproj b/src/Meadow.Modbus/Meadow.Modbus.csproj index 69c4582..157723e 100644 --- a/src/Meadow.Modbus/Meadow.Modbus.csproj +++ b/src/Meadow.Modbus/Meadow.Modbus.csproj @@ -23,6 +23,6 @@ - +