Files

307 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AvaloniaApplication1.ViewModels
{
public class ModBusMaster
{
public byte slaveId { get; set; } = 0x01; // Slave ID (your ESP slave ID)
//byte functionCode = 0x05; // Function code for Write Single Coil
//byte coilAddressHigh = 0x00; // Coil address (MSB)
//byte coilAddressLow = 0x00; // Coil address (LSB)
//byte coilValueHigh = 0x00; // Coil value (0xFF00 to turn on, 0x0000 to turn off)
//byte coilValueLow = 0x00;
// Function to read coils
public async Task<bool[]> ReadCoils(SerialPort port, ushort startAddress, ushort numberOfCoils)
{
try
{
byte functionCode = 0x01; // Function code for Read Coils
byte[] frame = new byte[6]; // Request frame size (without CRC)
// Construct the request frame
frame[0] = slaveId; // Slave address
frame[1] = functionCode; // Function code (0x01 for Read Coils)
frame[2] = (byte)(startAddress >> 8); // Start address high byte
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
frame[4] = (byte)(numberOfCoils >> 8); // Number of coils high byte
frame[5] = (byte)(numberOfCoils & 0xFF); // Number of coils low byte
// Calculate CRC and append it to the frame
byte[] crc = CalculateCRC(frame);
byte[] fullFrame = new byte[frame.Length + crc.Length];
Array.Copy(frame, fullFrame, frame.Length);
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
// Send the frame over the serial port
port.DiscardOutBuffer();
port.DiscardInBuffer();
port.Write(fullFrame, 0, fullFrame.Length);
Thread.Sleep(500);
Console.WriteLine("Read Coils request sent.");
// Buffer for the response (modbus slave response size can vary)
byte[] response = new byte[256];
int bytesRead = port.Read(response, 0, response.Length);
Console.WriteLine("Response Coils sent.");
// Ensure that the response is valid (check slave address and function code)
if (response[0] != slaveId || response[1] != functionCode)
{
Console.WriteLine("Invalid response or error in slave response.");
return null;
}
// Calculate the number of bytes based on the number of coils
int expectedByteCount = (numberOfCoils + 7) / 8;
if (response[2] != expectedByteCount)
{
Console.WriteLine("Incorrect byte count in the response.");
return null;
}
// The response bytes are in bit form, and we need to extract them
bool[] coilStates = new bool[numberOfCoils]; // Array to store the coil states
int byteIndex = 3; // Start reading from byte 3 (after slave ID and function code)
byte coilByte = response[byteIndex]; // The byte representing the coil states
// Loop over all coils and check the corresponding bit
for (int coilIndex = 0; coilIndex < numberOfCoils; coilIndex++)
{
// Check the state of the coil by checking the corresponding bit in coilByte
coilStates[coilIndex] = coilByte == 1 ? true : false; /*(coilByte & (1 << (7 - coilIndex))) != 0;*/
}
if (coilStates==null)
{
Console.WriteLine("status wael: null");
}
else
{
Console.WriteLine($"status: {coilStates[0]}");
}
return coilStates;
}
catch (Exception ex)
{
Console.WriteLine($"error wael: {ex.Message}");
return null;
}
}
// Function to write a single coil
public async Task<byte[]> WriteSingleCoil(ushort coilAddress, bool state)
{
byte functionCode = 0x05;
byte[] frame = new byte[6];
frame[0] = slaveId; // Slave address
frame[1] = functionCode; // Function code
frame[2] = (byte)(coilAddress >> 8); // Coil address high byte
frame[3] = (byte)(coilAddress & 0xFF); // Coil address low byte
frame[4] = (byte)(state ? 0xFF : 0x00); // Coil value (ON=0xFF, OFF=0x00)
frame[5] = (byte)(0x00); // Coil value (ON=0xFF, OFF=0x00)
// Calculate CRC and append it
byte[] crc = CalculateCRC(frame);
byte[] fullFrame = new byte[frame.Length + crc.Length];
Array.Copy(frame, fullFrame, frame.Length);
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
return fullFrame;
}
// Function to write multiple coils
public byte[] WriteMultipleCoils( ushort startAddress, bool[] coilValues)
{
byte functionCode = 0x0F;
int byteCount = (coilValues.Length + 7) / 8; // Calculate the number of bytes needed
byte[] frame = new byte[5 + byteCount]; // Basic frame without CRC
frame[0] = slaveId; // Slave address
frame[1] = functionCode; // Function code
frame[2] = (byte)(startAddress >> 8); // Start address high byte
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
frame[4] = (byte)(coilValues.Length); // Number of coils
frame[5] = (byte)byteCount; // Number of bytes to follow (coil values)
for (int i = 0; i < coilValues.Length; i++)
{
int byteIndex = 5 + 1 + (i / 8); // Start at byte 6, accounting for byte count
if (coilValues[i])
{
frame[byteIndex] |= (byte)(1 << (i % 8)); // Set the bit corresponding to the coil
}
}
// Calculate CRC and append it
byte[] crc = CalculateCRC(frame);
byte[] fullFrame = new byte[frame.Length + crc.Length];
Array.Copy(frame, fullFrame, frame.Length);
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
return fullFrame;
}
// Function to write multiple registers
public async Task<byte[]> WriteSingleRegister(ushort startAddress, int value)
{
byte functionCode = 0x06; // Function code for writing a single register
byte[] frame = new byte[6]; // Basic frame without CRC
frame[0] = slaveId; // Slave address
frame[1] = functionCode; // Function code
frame[2] = (byte)(startAddress >> 8); // Start address high byte
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
frame[4] = (byte)(value >> 8); // Register value high byte
frame[5] = (byte)(value & 0xFF); // Register value low byte
// Calculate CRC and append it
byte[] crc = CalculateCRC(frame);
byte[] fullFrame = new byte[frame.Length + crc.Length];
Array.Copy(frame, fullFrame, frame.Length);
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
return fullFrame;
}
public async Task<byte[]> WriteSingleRegister(ushort startAddress, ushort value)
{
byte functionCode = 0x06; // Function code for writing a single register
byte[] frame = new byte[6]; // Basic frame without CRC
frame[0] = slaveId; // Slave address
frame[1] = functionCode; // Function code
frame[2] = (byte)(startAddress >> 8); // Start address high byte
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
frame[4] = (byte)(value >> 8); // Register value high byte
frame[5] = (byte)(value & 0xFF); // Register value low byte
// Calculate CRC and append it
byte[] crc = CalculateCRC(frame);
byte[] fullFrame = new byte[frame.Length + crc.Length];
Array.Copy(frame, fullFrame, frame.Length);
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
return fullFrame;
}
public async Task<byte[]> ReadHoldingRegister(ushort startAddress, ushort numberOfRegisters)
{
byte functionCode = 0x03; // Function code for reading holding registers
byte[] frame = new byte[6]; // Basic frame without CRC
frame[0] = slaveId; // Slave address
frame[1] = functionCode; // Function code
frame[2] = (byte)(startAddress >> 8); // Start address high byte
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
frame[4] = (byte)(numberOfRegisters >> 8); // Number of registers high byte
frame[5] = (byte)(numberOfRegisters & 0xFF); // Number of registers low byte
// Calculate CRC and append it
byte[] crc = CalculateCRC(frame);
byte[] fullFrame = new byte[frame.Length + crc.Length];
Array.Copy(frame, fullFrame, frame.Length);
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
return fullFrame;
}
public async Task<byte[]> ReadInputRegisters(ushort startAddress, ushort numberOfRegisters)
{
byte functionCode = 0x04; // Function code for reading input registers
byte[] frame = new byte[6]; // Basic frame without CRC
frame[0] = slaveId; // Slave address
frame[1] = functionCode; // Function code
frame[2] = (byte)(startAddress >> 8); // Start address high byte
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
frame[4] = (byte)(numberOfRegisters >> 8); // Number of registers high byte
frame[5] = (byte)(numberOfRegisters & 0xFF); // Number of registers low byte
// Calculate CRC and append it
byte[] crc = CalculateCRC(frame);
byte[] fullFrame = new byte[frame.Length + crc.Length];
Array.Copy(frame, fullFrame, frame.Length);
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
return fullFrame;
}
public async Task<byte[]> WriteMultipleRegisters(ushort startAddress, float[] values)
{
byte functionCode = 0x10; // Function code for writing multiple registers
byte byteCount = (byte)(values.Length * 2); // Total number of bytes for values
byte[] frame = new byte[7 + byteCount]; // Frame without CRC
frame[0] = slaveId; // Slave address
frame[1] = functionCode; // Function code
frame[2] = (byte)(startAddress >> 8); // Start address high byte
frame[3] = (byte)(startAddress & 0xFF); // Start address low byte
frame[4] = (byte)(values.Length >> 8); // Number of registers high byte
frame[5] = (byte)(values.Length & 0xFF); // Number of registers low byte
frame[6] = byteCount; // Byte count
for (int i = 0; i < values.Length; i++)
{
short val = unchecked((short)values[i]);
frame[7 + i * 2] = (byte)(val >> 8); // Register value high byte
frame[8 + i * 2] = (byte)(val & 0xFF); // Register value low byte1
}
// Calculate CRC and append it
byte[] crc = CalculateCRC(frame);
byte[] fullFrame = new byte[frame.Length + crc.Length];
Array.Copy(frame, fullFrame, frame.Length);
Array.Copy(crc, 0, fullFrame, frame.Length, crc.Length);
return fullFrame;
}
// Function to calculate CRC16 for Modbus RTU frame
public byte[] CalculateCRC(byte[] data)
{
ushort crc = 0xFFFF;
foreach (byte byteData in data)
{
crc ^= byteData;
for (int i = 8; i > 0; i--)
{
if ((crc & 0x0001) == 0x0001)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
return new byte[] { (byte)(crc & 0xFF), (byte)((crc >> 8) & 0xFF) };
}
}
}