1
votes

I am reading data in bytes from .bin file and split the whole byte data into 16-16 bytes frames, so I want to 16 bytes frame one by one and wait until the first frame finished its cycle.

Callback method of SerialPort class:

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{

    // Read data from serial port:
    byte[] buffer = new byte[serialPort.BytesToRead];
    serialPort.Read(buffer, 0, buffer.Length);
    StringBuilder sb = new StringBuilder();
    List<string> response = new List<string>();
    for (int i = 0; i < buffer.Length; i++)
    {
        string currentByte = string.Format("{0:X2}", buffer[i]);
        response.Add(currentByte);

        sb.AppendFormat("{0:X2}", buffer[i]);
    }

    string responesCode = response[1].ToString();
    if (responesCode == "44")
    {
        // Wait until the first response is not received
        foreach (var packet in packetList.Skip(1))
        {
            // This method which sending the the data
            this.ReadByteDataFromFile(packet);
        }
    }
}

FdBrowseFile_Click button click:

private void FdBrowseFile_Click(object sender, RoutedEventArgs e)
{
    Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
    Nullable<bool> result = dlg.ShowDialog();
    if (result == true)
    {
        byte[] fileBytes = File.ReadAllBytes(filename);

        foreach (byte[] copySlice in fileBytes.Slices(16))
        {
            var splitedByteArray = copySlice;
            if (splitedByteArray.Length != 16)
            {
                byte[] padd = new byte[16];
                var startAt = 0;
                Array.Copy(splitedByteArray, 0, padd, startAt, splitedByteArray.Length);
                packetList.Add(padd);
            }
            else
            {
                packetList.Add(splitedByteArray);
            }
        }
        ReadByteDataFromFile(packetList[0]);
    }
}

ReadByteDataFromFile method:

public void ReadByteDataFromFile(byte[] packet) {
 try {
  byte[] mBuffer = new byte[24];
  byte[] payload = new byte[16];
  int i = 0;
  foreach(var bytes in packet) {
   payload[i++] = bytes;
  }
  CheckSumHelper checkSumHelper = new CheckSumHelper();
  var ckSum = checkSumHelper.GetCheckSum(payload);
  mBuffer[0] = 0x02;
  mBuffer[1] = 0x10;
  mBuffer[2] = CheckSumHelper.GetBytesFromDecimal(packet[0]);
  mBuffer[3] = CheckSumHelper.GetBytesFromDecimal(packet[1]);
  mBuffer[4] = CheckSumHelper.GetBytesFromDecimal(packet[2]);
  mBuffer[5] = CheckSumHelper.GetBytesFromDecimal(packet[3]);
  mBuffer[6] = CheckSumHelper.GetBytesFromDecimal(packet[4]);
  mBuffer[7] = CheckSumHelper.GetBytesFromDecimal(packet[5]);
  mBuffer[8] = CheckSumHelper.GetBytesFromDecimal(packet[6]);
  mBuffer[9] = CheckSumHelper.GetBytesFromDecimal(packet[7]);
  mBuffer[10] = CheckSumHelper.GetBytesFromDecimal(packet[8]);
  mBuffer[11] = CheckSumHelper.GetBytesFromDecimal(packet[9]);
  mBuffer[12] = CheckSumHelper.GetBytesFromDecimal(packet[10]);
  mBuffer[13] = CheckSumHelper.GetBytesFromDecimal(packet[11]);
  mBuffer[14] = CheckSumHelper.GetBytesFromDecimal(packet[12]);
  mBuffer[15] = CheckSumHelper.GetBytesFromDecimal(packet[13]);
  mBuffer[16] = CheckSumHelper.GetBytesFromDecimal(packet[14]);
  mBuffer[17] = CheckSumHelper.GetBytesFromDecimal(packet[15]);
  mBuffer[18] = 0x17;
  mBuffer[19] = 0x00;
  mBuffer[20] = 0x00;
  mBuffer[21] = 0x00;
  mBuffer[22] = Convert.ToByte(int.Parse(ckSum, System.Globalization.NumberStyles.HexNumber));
  mBuffer[23] = 0x03;
  serialPort.Write(mBuffer, 0, mBuffer.Length);
 } catch (Exception ex) {
  ExceptionHandler exceptionHandler = new ExceptionHandler();
  exceptionHandler.HandleException(ex);
 }
}

How I can add a delay for ReadByteDataFromFile method?

2
Please provide a minimal reproducible example. From your code, it's unclear what your question is. What does not work? At which line? What do you want to achieve? Furthermore, your code is incomplete: e.g. missing ReadByteDataFromFile and packetList.dymanoid
@dymanoid I want to send data to the serial port i.e to this method:ReadByteDataFromFilePrashant Pimpale
@dymanoid Thanks for the reply! I added the codePrashant Pimpale
My advice would be to first write readable code and split things up. F.e. payload is only used to calculate the checksum, why even copy the data? Also there are better ways to copy data from one array to the other. Also, I'm pretty sure GetBytesFromDecimal has nothing to do with the checksum, or does it? Also, why do you need to parse the checksum, if it's a number, it should probably return an int instead of a string. Also, the method named ReadByteDataFromFile doesn't read any thing from a file...huysentruitw
@huysentruitw totally agreed with you!Prashant Pimpale

2 Answers

5
votes

What you need is a way to block the execution of some code, until something else has happened, or - how to get things running on two threads synchronously. .NET has quite a few classes in the System.Threading namespace for synchronization. We'll use AutoResetEvent here.

Think of AutoResetEvent as a turnstile.

turnstile

You can't move forward if the the person on the other side stops. When you move forward, you call Wait - and it blocks you from moving, until someone calls Set on it.

Now if we apply that to our problem: We need to stop sending data until we get an acceptable response. So call Wait when sending data, and let the response handling code call Set to let it move forward.

Here's an example which simulates a modem. You send some AT commands, it responds, but the responses always end with \r\n.

var port = new SerialPort("COM2");
port.Open();

var mre = new AutoResetEvent(false);
var buffer = new StringBuilder();

port.DataReceived += (s, e) =>
{
    buffer.Append(port.ReadExisting());
    if (buffer.ToString().IndexOf("\r\n") >= 0)
    {
        Console.WriteLine("Got response: {0}", buffer);

        mre.Set(); //allow loop to continue
        buffer.Clear();
    }
};


var commandsToSend = new string[] { "AT", "AT", "AT+CSQ" };
var responseTimeout = TimeSpan.FromSeconds(10);

foreach (var command in commandsToSend)
{
    try
    {
        Console.WriteLine("Write '{0}' to {1}", command, port.PortName);
        port.WriteLine(command);

        Console.WriteLine("Waiting for response...");

        //this is where we block
        if (!mre.WaitOne(responseTimeout))
        {
            Console.WriteLine("Did not receive response");
            //do something
        }
    }
    catch (TimeoutException)
    {
        Console.WriteLine("Write took longer than expected");
    }
    catch
    {
        Console.WriteLine("Failed to write to port");
    }
}

Console.ReadLine();

Sample output when tested through virtual serial port: (I just reply with OK<CR><LF>)

Write 'AT' to COM2
Waiting for response...
Got response: OK

Write 'AT' to COM2
Waiting for response...
Got response: OK

Write 'AT+CSQ' to COM2
Waiting for response...
Did not receive response
2
votes

Wait in a loop for a full response after you write a first frame.

// Set read timeout to value recommended in the communication protocol specification 
// so serial port operations don't stuck.
_port.WriteTimeout = 200;
_port.ReadTimeout = 200;

public void OnClick()
{
    // Write first frame.
    _port.Write(...);
    // Now wait for the full response.

    // Expected response length. Look for the constant value from the device communication 
    // protocol specification or extract from the response header (first response bytes) if  
    // there is any specified in the protocol.
    int count = ...; 
    var buffer = new byte[count];
    var offset = 0;
    while (count > 0)
    {
        var readCount = _port.Read(buffer, offset, count);                 
        offset += readCount;
        count -= readCount;
    }
    // Now buffer contains full response or TimeoutException instance is thrown by SerialPort.
    // Check response status code and write other frames.
}

In order to not block UI thread you most probably still need to utilize synchronous API and Task.Run(). See C# await event and timeout in serial port communication discussion on StackOverflow.

For more information check Top 5 SerialPort Tips article by Kim Hamilton.