diff --git a/src/Meadow.Modbus.Unit.Tests/ModbusSerialTStatTests.cs b/src/Meadow.Modbus.Unit.Tests/ModbusSerialTStatTests.cs index 643e5ec..834872a 100644 --- a/src/Meadow.Modbus.Unit.Tests/ModbusSerialTStatTests.cs +++ b/src/Meadow.Modbus.Unit.Tests/ModbusSerialTStatTests.cs @@ -89,7 +89,7 @@ public async void OverlappingAccessTest() { ushort sp = 600; - for(int i = 0; i < 10; i++) + for (int i = 0; i < 10; i++) { Debug.WriteLine("-->"); await client.WriteHoldingRegister(address, setpointRegister, sp); @@ -121,6 +121,10 @@ public async void ReadHoldingRegisterTest() var readCount = 1; var client = new ModbusRtuClient(port); + { + // force meadow to compile the serial stuff + }; + var r1 = await client.ReadHoldingRegisters(address, startRegister, readCount); Assert.Equal(readCount, r1.Length); diff --git a/src/Meadow.Modbus.Unit.Tests/ModbusTcpLoopbackTests.cs b/src/Meadow.Modbus.Unit.Tests/ModbusTcpLoopbackTests.cs index 3897228..37bdce3 100644 --- a/src/Meadow.Modbus.Unit.Tests/ModbusTcpLoopbackTests.cs +++ b/src/Meadow.Modbus.Unit.Tests/ModbusTcpLoopbackTests.cs @@ -124,7 +124,7 @@ public async void ReadMultipleHoldingRegistersLoopbackTest() { callbackOccurred = false; - testRegisterAddress = (ushort)r.Next(ushort.MaxValue); + testRegisterAddress = (ushort)r.Next(9999); var result = await client.ReadHoldingRegisters(255, testRegisterAddress, r.Next(2, 21)); Assert.True(callbackOccurred); @@ -225,5 +225,54 @@ public async void ReadCoilsLoopbackTest() } } } + + [Fact] + public async void ReadMultipleInputRegistersLoopbackTest() + { + ushort[]? testData = null; + ushort testRegisterAddress = 42; + bool callbackOccurred = false; + var r = new Random(); + + using (var server = new ModbusTcpServer(502)) + using (var client = new ModbusTcpClient("127.0.0.1")) + { + server.ReadInputRegisterRequest += (address, count) => + { + Assert.Equal(testRegisterAddress, address); + + // generate some new random data - the request is for registers, which are ushorts + testData = new ushort[count]; + for (int i = 0; i < testData.Length; i++) + { + testData[i] = (ushort)r.Next(ushort.MaxValue); + } + + callbackOccurred = true; + return new ModbusReadResult(testData); + }; + + server.Start(); + + await client.Connect(); + + // loop for 5 reads - reading 1-16 coils + // the event handler above checks the result + for (int i = 0; i < 5; i++) + { + testRegisterAddress = (ushort)r.Next(9999); // register range is only 10k + var result = await client.ReadInputRegisters(255, testRegisterAddress, r.Next(1, 17)); + Assert.True(callbackOccurred); + Assert.NotNull(testData); + Assert.NotNull(result); + Assert.Equal(testData?.Length, result.Length); + + for (int index = 0; index < testData?.Length; index++) + { + Assert.Equal(testData[index], result[index]); + } + } + } + } } } \ No newline at end of file diff --git a/src/Meadow.Modbus/Clients/ModbusRtuClient.cs b/src/Meadow.Modbus/Clients/ModbusRtuClient.cs index fc02960..187c744 100644 --- a/src/Meadow.Modbus/Clients/ModbusRtuClient.cs +++ b/src/Meadow.Modbus/Clients/ModbusRtuClient.cs @@ -41,6 +41,9 @@ private void SetEnable(bool state) } } + protected Action? PostOpenAction { get; set; } = null; + protected Action? PostWriteDelayAction { get; set; } = null; + public override Task Connect() { SetEnable(false); @@ -49,6 +52,8 @@ public override Task Connect() { _port.Open(); _port.ClearReceiveBuffer(); + + PostOpenAction?.Invoke(); } _byteTime = (1d / _port.BaudRate) * _port.DataBits * 1000d; @@ -125,7 +130,7 @@ protected override async Task ReadResult(ModbusFunction function) var actualCrc = buffer[buffer.Length - 2] | buffer[buffer.Length - 1] << 8; if (expectedCrc != actualCrc) { throw new CrcException(); } - if(resultLen == 0) + if (resultLen == 0) { //happens on write multiples return new byte[0]; } @@ -136,15 +141,19 @@ protected override async Task ReadResult(ModbusFunction function) return await Task.FromResult(result); } - protected override async Task DeliverMessage(byte[] message) + protected override Task DeliverMessage(byte[] message) { SetEnable(true); + _port.Write(message); // the above call to the OS transfers data to the serial buffer - it does *not* mean all data has gone out on the wire // we must wait for all data to get transmitted before lowering the enable line - var wait = (int)(_byteTime * message.Length) + 1; - await Task.Delay(wait); + + PostWriteDelayAction?.Invoke(message); + SetEnable(false); + + return Task.CompletedTask; } protected override byte[] GenerateReadMessage(byte modbusAddress, ModbusFunction function, ushort startRegister, int registerCount) diff --git a/src/Meadow.Modbus/Meadow.Modbus.csproj b/src/Meadow.Modbus/Meadow.Modbus.csproj index b3de19c..7607f5e 100644 --- a/src/Meadow.Modbus/Meadow.Modbus.csproj +++ b/src/Meadow.Modbus/Meadow.Modbus.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -16,10 +16,13 @@ - - + + + + + diff --git a/src/samples/F7v2RtuSample/MeadowApp.cs b/src/samples/F7v2RtuSample/MeadowApp.cs index 062b4ba..6c40d3e 100644 --- a/src/samples/F7v2RtuSample/MeadowApp.cs +++ b/src/samples/F7v2RtuSample/MeadowApp.cs @@ -1,6 +1,5 @@ using Meadow; using Meadow.Devices; -using Meadow.Hardware; using Meadow.Modbus; using System; using System.Threading.Tasks; @@ -10,30 +9,14 @@ namespace F7v2RtuSample public class MeadowApp : App { private ModbusRtuClient _client; + private ProjectLab _board; public override Task Initialize() { Resolver.Log.Info("Initialize hardware..."); - 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 projLab = new ProjectLab(); - IDigitalOutputPort serialEnable; - - if (projLab.IsV1Hardware()) - { - Resolver.Log.Info("ProjectLab v1 detected"); - serialEnable = Device.CreateDigitalOutputPort(Device.Pins.D09, false); // early ProjLab and Hack board - } - else - { - Resolver.Log.Info("ProjectLab v2 detected"); - serialEnable = projLab.Mcp_2.CreateDigitalOutputPort(projLab.Mcp_2.Pins.GP0, false); - } - - Resolver.Log.Info("Creating the Modbus RTU Enable port"); - _client = new ModbusRtuClient(port, serialEnable); + _board = new ProjectLab(); + _client = _board.GetModbusRtuClient(19200); return Task.CompletedTask; }