rs77can
rs77can

Reputation: 61

NModbus C# .Net 8.0 - TCP Slave - 1st parameter (float) at Holding Address 40001 only has resolution of only 0.125, rest work OK

Trying to get NMOdbus to work on a .Net 8.0 Console app, and all works OK. I am able to set the data in the dataStore, and then using Modbus Poll, I am able to read the data. I am new to .Net, so excuse me if there are any glaring errors.

The only issue I am finding is that the first parameter at address 40001 only changes by increments of 0.125, while the rest of the parameters have the full resolution. If I start the data at address 40003, then that behavior is still the same (so I don't think the issue is the Modbus Poll software)

Here is a sample of the data that should be present

Param 0 = 25.671875
Param 0 = 25.671875
Param 0 = 25.671875
Param 0 = 25.671875
Param 0 = 25.664062
Param 0 = 25.664062
Param 0 = 25.671875
Param 0 = 25.671875
Param 0 = 25.664062
Param 0 = 25.664062
Param 0 = 25.671875
Param 0 = 25.671875
Param 0 = 25.671875
Param 0 = 25.671875
Param 0 = 25.671875
Param 0 = 25.664062
Param 0 = 25.664062
Param 0 = 25.664062
Param 0 = 25.664062
Param 0 = 25.664062
Param 0 = 25.664062
Param 0 = 25.671875
Param 0 = 25.664062
Param 0 = 25.671875
Param 0 = 25.671875
Param 0 = 25.664062

However, the modbus poll will only read 25.625 (and will only change as stated by increments of 0.125)

My code is listed below

using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Text.Json;
using System.IO;
using System.Reflection.Emit;
using System.Net.Mail;
using System.Configuration;
using NModbus;
using System.Net.Sockets;
using Newtonsoft.Json.Linq;
using System.Numerics;



//
// Modbus
//  Rgisters
//      Float
//          2 registers per device parameter
//          starting register =     40001 -  parameter 0
//                                  40003 - parameter 1
//                                  40005 - parameter 2
//                                  ..
//                                  40099 - parameter 49
//

namespace CommIoT
{
    public partial class Program
    {
        private IModbusFactory _modbusFactory;
        private IModbusSlaveNetwork _slaveNetwork;
        private TcpListener _tcpListener;
        private int numParams = 28;


        public double GetParamResult(int param)
        {
            return deviceData.parameters[param].result;
        }


        public void ModbusServer()
        {
            _modbusFactory = new ModbusFactory();
        }
        public void CreateServer(IPAddress ip, int port)
        {
            _tcpListener = new TcpListener(ip, port);
            _tcpListener.Start();
            _slaveNetwork = _modbusFactory.CreateSlaveNetwork(_tcpListener);
        }
        public void AddSlave(byte unitId)
        {
            var slave = _modbusFactory.CreateSlave(unitId);

            _slaveNetwork.AddSlave(slave);
            ISlaveDataStore dataStore = _slaveNetwork.GetSlave(unitId).DataStore;
            ReadData(dataStore);
        }
        public void StartServer()
        {
            _slaveNetwork.ListenAsync();
        }

        public void EWModbusServer()
        {
            // create the modbus server
            ModbusServer();
            CreateServer(IPAddress.Any, 502);
            AddSlave(1);
            StartServer();
        }

        public async void ReadData(ISlaveDataStore _dataStore)
        {
            while (numParams == 0)
            {
                await Task.Delay(5000);     // wait for stability of the data
            }


            ushort[] a1 = new ushort[numParams * 2];
            ushort[] sArr = new ushort[2];


            while (true)
            {
                for (int i = 0; i < numParams; i++)
                {
                    float f = (float)GetParamResult(i);

                    if (i == 0)
                        Console.WriteLine("Param 0 = " + f.ToString());

                    ModbusToUnsignedShortArray((float)f, ref sArr);

                    a1[i * 2] = sArr[0];
                    a1[(i * 2) + 1] = sArr[1];
                }
                _dataStore.HoldingRegisters.WritePoints(1, a1);

                // set the Holding registers
                // enter all the parameters into the data store
                await Task.Delay(1000);
            }
        }

        public void Dispose()
        {
            _slaveNetwork?.Dispose();
            _slaveNetwork = null;
            _tcpListener?.Stop();
            _tcpListener = null;
        }



        public float ModbusToFloat(ushort[] args)
        {
            if (args.Length != 2)
                throw new ArgumentException("Input Array length invalid - Array langth must be '2'");

            var highValue = BitConverter.IsLittleEndian ? args[1] : args[0];
            var lowValue = BitConverter.IsLittleEndian ? args[0] : args[1];


            byte[] highRegisterBytes = BitConverter.GetBytes(highValue);
            byte[] lowRegisterBytes = BitConverter.GetBytes(lowValue);

            byte[] doubleBytes = {
                         highRegisterBytes[0],
                         highRegisterBytes[1],
                         lowRegisterBytes[0],
                         lowRegisterBytes[1],
         };

            return BitConverter.ToSingle(doubleBytes, 0);
        }

        public void ModbusToUnsignedShortArray(float fValue, ref ushort[] reg)
        {
            var value = BitConverter.SingleToInt32Bits(fValue);
            ushort low = (ushort)(value & 0x0000ffff);
            ushort high = (ushort)((value & 0xffff0000) >> 16);

            var highRegister = BitConverter.IsLittleEndian ? low : high;
            var lowRegister = BitConverter.IsLittleEndian ? high : low;

            reg[0] = lowRegister;
            reg[1] = highRegister;
        }

    }




}

As stated,

I tried changing the start address of the Holding registers

    _dataStore.HoldingRegisters.WritePoints(3, a1);

That did not work - same 0.125 inc/dec - but all the registers now start at 3,5,7 etc

The printed data in

                   if (i == 0)
                        Console.WriteLine("Param 0 = " + f.ToString());

Shows that the resolution should be about 0.01, not 0.125

(Modbus poll is defined as LittleEndian ByteSwap to get the correct visualization)

Upvotes: 0

Views: 661

Answers (0)

Related Questions