I am using C# to communicate via modbus rs485 rs232 to 2 phase meters that among other log the power voltage.
I have to send data over the bus so that i can receive the readings.
I have connected a normal wire and shorted the send and receive.
The data is recieved and this event is fired:
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
byte[] buff = new byte[sp.BytesToRead];
//Read the Serial Buffer
sp.Read(buff, 0, buff.Length);
string data= sp.ReadExisting();
foreach (byte b in buff)
{
AddBuffer(b); //Add byte to buffer
}
}
Then this buffer is sent to another function which is this one:
private void AddBuffer(byte b)
{
buffer.Add(b);
byte[] msg = buffer.ToArray();
//Make sure that the message integrity is correct
if (this.CheckDataIntegrity(msg))
{
if (DataReceived != null)
{
ModbusEventArgs args = new ModbusEventArgs();
GetValue(msg, args);
DataReceived(this, args);
}
buffer.RemoveRange(0, buffer.Count);
}
}
I think that the problem lies at the data integrity check:
public bool CheckDataIntegrity(byte[] data)
{
if (data.Length < 6)
return false;
//Perform a basic CRC check:
byte[] CRC = new byte[2];
GetCRC(data, ref CRC);
if (CRC[0] == data[data.Length - 2] && CRC[1] == data[data.Length - 1])
return true;
else
return false;
}
There is a CRC check and what is strange is that it never becomes true. The CRC calculation:
private void GetCRC(byte[] message, ref byte[] CRC)
{
ushort CRCFull = 0xFFFF;
byte CRCHigh = 0xFF, CRCLow = 0xFF;
char CRCLSB;
for (int i = 0; i < (message.Length) - 2; i++)
{
CRCFull = (ushort)(CRCFull ^ message[i]);
for (int j = 0; j < 8; j++)
{
CRCLSB = (char)(CRCFull & 0x0001);
CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF);
if (CRCLSB == 1)
CRCFull = (ushort)(CRCFull ^ 0xA001);
}
}
CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF);
CRC[0] = CRCLow = (byte)(CRCFull & 0xFF);
}
The problem is the use of ReadExisting(). It was not to be used in that manner as the buffer was being filled with useless data from the serial port. This problem was identified by #glace in the comments!
You first need to establish communication with your meters through some existing MODBUS master application like MODPOLL. Then, once you have communication working and having valid replies from your device, then and only then start testing your code. This way you make sure that problem can be only in your code and nothing else.
For example, to connect to two slave devices at the same time RS485 must be used instead of RS232, and this requests different wiring and RS485 to RS232 convertor on PC side.
Having RX and TX connected in RS232 for simulation purpose is not a good idea since each MODBUS message from a master (except broadcast messages) needs a reply which is different from just message echo. Also, each MODBUS message from a master has MODBUS client address embedded in it and only single client should reply to it (MODBUS is single master multiple slaves protocol).
As for a CRC calculation, this might help for MODBUS RTU protocol (ASCII is different):
function mb_CalcCRC16(ptr: pointer to byte; ByteCount: byte): word;
var
crc: word;
b, i, n: byte;
begin
crc := $FFFF;
for i := 0 to ByteCount do
if i = 0 then // device id is 1st byte in message, and it is not in the buffer
b := mb_GetMessageID; // so we have to calculate it and put it as 1st crc byte
else
b := ptr^;
Inc(ptr);
endif;
crc := crc xor word(b);
for n := 1 to 8 do
if (crc and 1) = 1 then
crc := (crc shr 1) xor $A001;
else
crc := crc shr 1;
endif;
endfor;
endfor;
Return(crc);
end;
function mb_CalcCRC: word; // Calculate CRC for message in mb_pdu
begin // this message can be one that is just received, or in a reply we have just composed
Return(mb_CalcCRC16(#mb_pdu[1], mb_GetEndOfData));
end;
That's a quote from a working embedded AVR device with implemented MODBUS RTU slave protocol.
Related
i read the article on https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_server and found it very interesting as i have not done anything with websockets before. I have already managed that my client connects and the handshake takes place (was also the easy part).
However, I can't manage to set the mask on my client so that the message arrives correctly at the server. Somehow I don't quite understand this yet....
At the server it looks like this:
bool fin = (bytes[0] & 0b10000000) != 0,
mask = (bytes[1] & 0b10000000) != 0; // must be true, "All messages from the client to the server have this bit set"
int opcode = bytes[0] & 0b00001111, // expecting 1 - text message
offset = 2;
ulong msglen = (ulong)(bytes[1] & 0b01111111);
if (msglen == 126)
{
// bytes are reversed because websocket will print them in Big-Endian, whereas
// BitConverter will want them arranged in little-endian on windows
msglen = BitConverter.ToUInt16(new byte[] { bytes[3], bytes[2] }, 0);
offset = 4;
}
else if (msglen == 127)
{
// To test the below code, we need to manually buffer larger messages — since the NIC's autobuffering
// may be too latency-friendly for this code to run (that is, we may have only some of the bytes in this
// websocket frame available through client.Available).
msglen = BitConverter.ToUInt64(new byte[] { bytes[9], bytes[8], bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2] }, 0);
offset = 10;
}
if (msglen == 0)
{
Console.WriteLine("msglen == 0");
}
else if (mask)
{
byte[] decoded = new byte[msglen];
byte[] masks = new byte[4] { bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3] };
offset += 4;
for (ulong i = 0; i < msglen; ++i)
decoded[i] = (byte)(bytes[offset + (int)i] ^ masks[i % 4]);
string text = Encoding.Default.GetString(decoded);
I have written the client in C# as TcpClient. But I just can't get it to encode the message so that it is decoded correctly at the server.
can someone of you help me? What is the inverse function to what is done at the server to decode this?
I only found Articles in Javascript or in C# without masks...
Thank u very much!
I'm currently setting up the communication between a controller for a step motor and a computer, coding an application in C# (it is the first time I use this programming language, and although I'm not a computer scientist but an industrial engineer, reason why I'm sure there are some ways of optimizing the function which I don't know, any recommendation on that matter would also be very appreciated). Therefore, I've been using the RS-485 that the controller has to communicate with it, and I've implemented an algorithm that generates the CRC(Cyclic Redundancy Check) bytes required.
And there is where my problem begins. I can't find the reason why my function doesn't generate the correct CRC value. I have checked with some online calculators of CRC and I've also used the example that appears in the Modbus Guide (where it also explains how is the code implemented).
Here is the code I've written for the calculus of the CRC:
class Program
{
static void Main(string[] args)
{
// 0x05, 0x06, 0x17, 0x70, 0x00, 0x01
byte[] prueba = new byte[] { 0x02, 0x07 };
byte[] result = Aux.CRC(prueba);
Console.WriteLine(result[0] + " " + result[1]);
}
}
class Aux{
public static byte[] CRC(byte[] to_evaluate)
{
byte[] CRC_Byte = new byte[2] { 0, 0 };
UInt16 CRC_Register = 0xFFFF; //16 bits 1111.1111.1111.1111
UInt16 CRC_pol = 0xa001; //16 bits 1010.0000.0000.0001
foreach (UInt16 byte_val in to_evaluate)
{
CRC_Register ^= byte_val;
Console.WriteLine("XOR inicial : {0:X}", CRC_Register);
for (byte i = 0; i < 8; i++)
{
CRC_Register >>= 1;
Console.WriteLine("Desplazamiento " + (i + 1) + ": {0:X}", CRC_Register);
if ((CRC_Register & 1) != 0)
{
CRC_Register ^= CRC_pol;
Console.WriteLine("XOR: {0:X}", CRC_Register);
}
}
}
Console.WriteLine("{0:X}",CRC_Register);
byte low_byte_CRC = (byte)((CRC_Register << 8) >> 8);
byte high_byte_CRC = (byte)(CRC_Register >> 8);
CRC_Byte[0] = low_byte_CRC;
CRC_Byte[1] = high_byte_CRC;
return CRC_Byte;
}
}
The expected result using the test array attached and the polinomial 0xa001 is 0x1241 for CRC_Register, and {0x41,0x12} for the CRC_Byte.
I had to implement a CRC check for PPP once in C# and it was absolutely no fun!
I found in this link the code that should correctly generate the CRC. It follows the CRC Generation procedure from section 6.2.2 on page 39 of the document you shared the link to.
// Compute the MODBUS RTU CRC
UInt16 ModRTU_CRC(byte[] buf, int len)
{
UInt16 crc = 0xFFFF;
for (int pos = 0; pos < len; pos++)
{
crc ^= (UInt16)buf[pos]; // XOR byte into least sig. byte of crc
for (int i = 8; i != 0; i--) // Loop over each bit
{
if ((crc & 0x0001) != 0) // If the LSB is set
{
crc >>= 1; // Shift right and XOR 0xA001
crc ^= 0xA001;
}
else // Else LSB is not set
{
crc >>= 1; // Just shift right
}
}
}
// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
return crc;
}
I have a device which i need to communicate to via host computer using C# and exchange frames between the device and host computer which are connected via Ethernet cable.The device has a port and ip which can be connected to.The following is sample code of frames communication between sender and receiver.
I have little knowledge in C# and i would like some help on how to establish the communication below using UDP protocol suite.Thanks
//Grab the data of the device
private byte[] Fetch_FrameID(bool bHostToDevice) //true:Host to Device false: Device to Host
{
return bHostToDevice ? new byte[] { 0x8F, 0xE1 } : new byte[] { 0x2D, 0x7A };
}
//Grab the Frame Sequence Number
private byte[] Fetch_FrameSequenceNumber(int iNumber)
{
return new byte[] { (byte)(iNumber & 0xFF), (byte)((iNumber >> 8) & 0xFF) };
}
//Grab the device status
private byte[] Fetch_HostToDeviceCommand(int iStatus) //status:0:Active 1:Sync 2:Request
{
switch (iStatus)
{
case 0: return new byte[] { 0x00, 0x00 }; //Active
case 1: return new byte[] { 0x11, 0x00 }; //Sync
case 2: return new byte[] { 0x21, 0x00 }; //Request
default: return null;
}
}
//Fetch Data Length
private byte[] Fetch_DataLen(byte[] dataArray)
{
int iLength = dataArray == null ? 0 : dataArray.Length;
return new byte[] { (byte)(iLength & 0xFF), (byte)((iLength >> 8) & 0xFF) };
}
//Fetch Data Check Sum
private byte[] Fetch_DataCheckSum(byte[] dataArray)
{
int iSum = 0;
if (dataArray != null)
{
for (int i = 0; i < dataArray.Length; i++)
{
iSum += (int)dataArray[i];
}
}
iSum += Convert.ToInt32("0xAA55", 16);
return new byte[] { (byte)(iSum & 0xFF), (byte)((iSum >> 8) & 0xFF) };
}
private byte[] Fetc_SendHeaderInfo(int iStatus, int iNumber, byte[] dataArray) //status:0:Active 1:Sync 2:Request
{
List<byte> result = new List<byte>();//TOTAL 12Bytes, each 2bytes
result.AddRange(Fetch_FrameID(true));//Grab FrameID
result.AddRange(Fetch_FrameSequenceNumber(iNumber));//Grab the FSN
result.AddRange(Fetch_HostToDeviceCommand(iStatus));//Grab host to device command
result.AddRange(Fetch_DataLen(dataArray));//Grab the data Length
result.AddRange(Fetch_DataCheckSum(dataArray)); //Grab the data Check sum
result.AddRange(Fetch_DataCheckSum(result.ToArray())); //Grab the headdata Check sum
return result.ToArray();
}
UDP and TCP are different protocols, in order to send UDP packets using .NET C# I suggest using UdpClient, here is a sample code for sending if that helps :
UdpClient udpClient = new UdpClient(ip, port);
try{
udpClient.Send(bytesToSend, bytesToSendLength);
}
catch ( Exception e ){
Console.WriteLine( e.ToString());
}
as for receiving you can use UdpClient.Receive
Here is Send & Receive documentation on MSDN
I need to transfer data from AT32 UC3 microcontroller ADC to PC via USB. I check work of ADC and PDCA in MCU of filling the buffer, and it works was perfect without data loosing. But when I send data from USB some bytes are lost. I do not know, why this happens.
I write simple programms to send some data from MCU to PC and check this data. In MCU I fill buffer with numbers from 0,1,2.. to 255 continuously, then send buffer via USB to PC, and check content of this buffer. So, some numbers are different from original data. Some bytes are lost. I using EVK1100 in CDC device mode.
AVR code:
#include <asf.h>
#include "conf_usb.h"
#define BUF_SIZE 32
int main(void){
irq_initialize_vectors();
cpu_irq_enable();
sysclk_init();
udc_start();
udc_attach();
char pbuf[BUF_SIZE];
for(int i=0; i<BUF_SIZE; i++){
pbuf[i] = (char)i;
}
while (true) {
udi_cdc_write_buf(pbuf, BUF_SIZE);
}
}
C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
namespace acc_tester
{
class Program
{
static void Main(string[] args) {
Console.WriteLine("Start");
int N = 32;
SerialPort serialPort = new SerialPort();
serialPort.PortName = "COM6";
serialPort.Open();
byte[] buf = new byte [N];
for (int n = 0; n < 10000; n++) {
serialPort.Read(buf, 0, N);
for (int i = 0; i < N; i++) {
if (buf[i] != (byte)(buf[0] + i)) {
Console.WriteLine("Data Lost. n =" + n.ToString() + " i=" + i.ToString());
return;
}
}
}
serialPort.Close();
Console.WriteLine("Stop");
return;
}
}
}
The output of my C# program is:
Data Lost. n =257 i=31
Data Lost. n =385 i=31
Data Lost. n =641 i=31
Data Lost. n =257 i=31
and etc.
Please, help me solve the problem.
SerialPort.Read reads at most N (32) bytes, it depends on how many bytes are in the input buffer (docs). Read returns the amount of bytes read.
To read chunk of data of length N you should buffer yourself the data and only check the content when you reach N bytes. Eg.
while (true) {
var bytesInBuffer = 0;
bytesInBuffer += serialPort.Read(buf, bytesInBuffer, N - bytesInBuffer);
if (bytesInBuffer == N) {
// Here the buffer is ready
bytesInBuffer = 0; // reset the counter
}
}
I've a sample pascal program, provided in a technical handbook of coin counter S350/360 machine, that enables communication between the machine and a computer using RS232C cable. I've came up with the following equivalent C# code and for some reason only the procedures that only need to write data to the serial port are executing and working successfully. I can only write data to the port but cannot read any data from it. I checked this using a Serial Sniffer and it turns out it only sniffs data written to the port, using the C# program, but cannot read any data back. Could you guys help me with what's going on here?
First here is the original Pascal code of the sample program.
uses crt;
const
{ COM1: RS232 port address }
RXTX = $3F8; { $2F8 if COM2: is used }
ACK = 6;
NAK = 21;
ESC = 27;
var
dummy,
checkSum : integer;
key : char;
protocol : integer;
procedure InitComm;
{ Set baudrate to 9600, 8 bits, no parity, 1 stop bit }
var i : integer;
begin
i := 1843200 div 9600 div 16;
port[RXTX + 3] := $80;
port[RXTX + 1] := hi(i);
port[RXTX]:= lo(i);
port[RXTX + 3] := 3;
port[RXTX + 4] := $A;
while odd(port[RXTX + 5]) do
begin
dummy := port[RXTX];
delay(10);
end;
end; { InitComm }
procedure Tx(data : integer);
{ Transmit a character on serial channel }
begin
while port[RXTX + 5] and $20 = 0 do;
port[RXTX] := data and $FF;
end; { Tx }
function RxWait : integer;
{ Waits for a character from serial channel }
begin
while not odd(port[RXTX + 5]) do;
RxWait := port[RXTX];
end; { RxWait }
procedure Tx2(data : integer);
{ Transmit a char on serial channel + Calculate check sum }
begin
Tx(data);
checkSum := (checkSum + data) and $FF;
end; { Tx2 }
procedure TxCommand(c1, c2 : char;
sendCheckSum : boolean);
{ Transmit command (no data) on serial channel }
begin
Tx(ESC);
checkSum := 0;
Tx2(ord(c1));
Tx2(ord(c2));
if sendCheckSum then
begin
Tx2(checkSum);
dummy := RxWait;
end;
end; { TxCommand }
function ReadNumber(n : integer) : real;
{ Read n bytes from serial channel }
var
number: real;
i : integer;
begin
number := 0;
checkSum := 0;
for i := 1 to n do
number := number * 256 + RxWait;
dummy := RxWait;
ReadNumber := number;
end; { ReadNumber }
procedure Revisions;
var
tmp : integer;
sw,
prot : real;
begin
TxCommand('P', 'R', FALSE);
checkSum := 0;
tmp := RxWait;
sw := tmp + RxWait / 100.0;
protocol := RxWait;
prot := protocol + RxWait / 100.0;
dummy := RxWait;
tmp := RxWait;
writeln('Software revision: ', sw:4:2);
writeln('Protocol revision: ', prot:4:2);
end; { Revisions }
procedure ReadCountReg;
begin
TxCommand('R', 'C', FALSE);
writeln(ReadNumber(4):11:0, ' coins counted.');
dummy := RxWait;
end; { ReadCountReg }
procedure ReadAccReg;
begin
TxCommand('R', 'A', FALSE);
writeln(ReadNumber(4):11:0, ' coins in accumulator.');
dummy := RxWait;
end; { ReadAccReg }
procedure Setbatch(limit : longint);
begin
TxCommand('W', 'L', FALSE);
case protocol of
1 : begin
Tx2(limit div 256);
Tx2(limit mod 256);
end;
2 : begin
Tx2( limit div 16777216);
Tx2((limit div 65536) mod 256);
Tx2((limit div 256) mod 256);
Tx2( limit mod 256);
end;
end; { case protocol }
Tx2(checkSum);
dummy := RxWait;
end; { Setbatch }
In the protocol descriptions two logical operators are used,
+ for arithmetic addition and ^ for logical AND.
All messages have the following principal structure:
Computer SC 350/360
–––––––> ESC (message start)
–––––––> Command
<––––––> Data (direction depends on command)
<––––––> Check sum (direction depends on command)
<––––––– Receipt:
- ACK (if check sum is correct) or
- NAK (if check sum is incorrect)
If the data is a binary number, the most significant byte (character) is
sent first. If the data is a text string, the first character of the string is
sent first. A text string always ends with a NULL character.
The check sum for received characters in a message is derived as
follows:
The addition of every hexadecimal character value, except those for
the message start (ESC), forms the sum. The check sum is then the
sum ^255.
If any of the fault conditions listed below occurs, the computer
should wait for at least 250 ms from the time that the last character
was received before re-sending the message:
The computer receives an incorrect check sum.
The machine has sent NAK.
The machine sends “invalid data”.
The machine does not respond within 250 ms.
The machine accepts all messages when counting except the message
that sets a new batch quantity.
For example; To start the machine 'TxCommand('M', '1', TRUE);' is issued as written in the handbook; I've implemented the pascal code for this in C#.Net and have successfully issued this command and the machine started right away. What I couldn't get working was the procedures 'ReadCountReg', 'ReadCountReg'and 'Setbatch'.
Below is the C# code I implemented:
public class CoinCounter : IDisposable
{
private const int RXTX = 0x3F8;
private const int ACK = 6;
private const int NAK = 21;
private const int ESC = 27;
private int dummy, checkSum;
private char key;
private int protocol;
public SerialPort port;
private bool _disposed = false;
public CoinCounter()
{
//Initialize Communication
port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
port.Handshake = Handshake.None;
port.Open();
port.DtrEnable = true;
port.RtsEnable = true;
}
public void ClosePort()
{
if (port.IsOpen)
port.Close();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
DisposeManagedResources();
}
DisposeUnmanagedResources();
_disposed = true;
}
}
private void DisposeManagedResources()
{
port.Dispose();
}
private void DisposeUnmanagedResources() { }
~CoinCounter()
{
Dispose(false);
}
public void Tx(int data)
{
port.Write(new byte[] { (byte)(data & 0xFF) }, 0, 1);
}
public int RxWait()
{
int readByte = 0;
CommTimer timer = new CommTimer();
timer.Start(250);
while ((port.BytesToRead == 0) && (timer.timedout == false))
{
}
if (port.BytesToRead > 0)
readByte = port.ReadByte();
return readByte;
}
public void Tx2(int data)
{
Tx(data);
checkSum = (checkSum + data) & 0xFF;
}
public void TxCommand(char c1, char c2, bool sendCheckSum)
{
Tx(ESC);
checkSum = 0;
Tx2((int)c1);
Tx2((int)c2);
if (sendCheckSum)
{
Tx2(checkSum);
dummy = RxWait();
}
}
public double ReadNumber(int n)
{
double number;
int i;
number = checkSum = 0;
for (i = 0; i < n; i++)
number = number * 256 + RxWait();
dummy = RxWait();
return number;
}
public void Revisions(out double softVersion, out double protocolVersion)
{
int tmp;
TxCommand('P', 'R', false);
checkSum = 0;
tmp = RxWait();
softVersion = tmp + RxWait() / 100.0;
protocol = RxWait();
protocolVersion = protocol + RxWait() / 100.0;
dummy = RxWait();
tmp = RxWait();
}
public double ReadCountReg()
{
TxCommand('R', 'C', false);
double coinsCounted = ReadNumber(4);
dummy = RxWait();
return coinsCounted;
}
public double ReadAccReg()
{
TxCommand('R', 'A', false);
double coinsInAccumulator = ReadNumber(4);
dummy = RxWait();
return coinsInAccumulator;
}
public void SetBatch(long limit)
{
TxCommand('W', 'L', false);
switch (protocol)
{
case 1:
Tx2((int)(limit / 256));
break;
case 2:
Tx2((int)(limit / 16777216));
Tx2((int)(limit / 65536) % 256);
Tx2((int)(limit / 256) % 256);
Tx2((int)(limit % 256));
break;
}
Tx2(checkSum);
dummy = RxWait();
}
public class CommTimer
{
public System.Timers.Timer tmrComm = new System.Timers.Timer();
public bool timedout = false;
public CommTimer()
{
timedout = false;
tmrComm.AutoReset = false;
tmrComm.Enabled = false;
tmrComm.Interval = 1000; //default to 1 second
tmrComm.Elapsed += new ElapsedEventHandler(OnTimedCommEvent);
}
public void OnTimedCommEvent(object source, ElapsedEventArgs e)
{
timedout = true;
tmrComm.Stop();
}
public void Start(double timeoutperiod)
{
tmrComm.Interval = timeoutperiod;
tmrComm.Stop();
timedout = false;
tmrComm.Start();
}
}
}
Note that the hand book states:
1. For simplicity the handshake lines are not used in this example.
2. No check sum implemented.
While most of the commands, with simple checksum calculation, can successfully communicate with the machine I couldn't get, for example, 'ReadCountReg' procedure to work and see the results of counted coins. The protocol for this procedure is;
Computer SC 350/360
–––––––> ESC
–––––––> “R”
–––––––> “C”
<––––––– CountRegister (CR)
<––––––– (CR ^ FF00,000016 + CR ^ FF,000016 + CR ^ FF0016 +CR ^ FF16) ^ FF16
<––––––– ACK
Could you guys help me get this working as I don't have much experience with the serial port communication and could help me resolve my issue.
SerialPort implements IDisposable. You are acquiring a serial port and you are never disposing it - this is an error. If your usage model is one wherein the client code will always make a call to you when it's done, you should Dispose the object then (this doesn't look like your model). Otherwise, you should implement IDisposable yourself (follow the code template. In your case, you will be implementing DisposeManagedResources() and disposing the serial port then) and ensure that your object is Disposed when finished.
A final option is to have the calling code allocate and dispose the serial port and pass it in to the constructor of your code.