I have a problem receiving a complet through the serial port. I have a device (Xbee) that is sending a frame which consists of 22 bytes. This frame is sent to the PC every 100ms at a baudrate of 4800bps.
I have made a class that uses asynchrone IO. When i use the class i create a instance and when i want to read the
newest value (frame) received i use "object.Temperatur" to get the string where the frame is stored. Temperatur is a string.
The big problem is to get a complete frame of 22 bytes stored in Temperatur (converted to HEX --> Temperatur = ConvertToHex(Encoding.ASCII.GetString(buffer, 0, bytesRead)); (ConvertToHex is a metod which i have defined in the class)). When i use this class i get only pieces and the pieces start from different bytes of the frame. Does anyone know what i could do to get a complete frame?
I have also shared my program. Feel free to take it if you can use something form it :)
I am sorry for my bad english.
////////////////////////////////////////////// MY CLASS //////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Windows.Forms;
namespace ThermoCouple
{
class AsynchIOThermo
{
SerialPort COMport = new SerialPort();
private Stream inputStream;
private AsyncCallback myCallBack;
private byte[] buffer;
private int BufferSize;
public string Temperatur { set; get; }
public AsynchIOThermo(string COMportName, int BaudRate, int DataBits, string Parity, string StopBits, int StreamBufferSize)
{
BufferSize = StreamBufferSize;
COMport.ReadBufferSize = 2200;
// Initial COMport
COMport.PortName = COMportName;
COMport.BaudRate = BaudRate;
COMport.DataBits = DataBits;
switch (Parity)
{
case "None":
COMport.Parity = System.IO.Ports.Parity.None;
break;
case "Mark":
COMport.Parity = System.IO.Ports.Parity.Mark;
break;
case "Even":
COMport.Parity = System.IO.Ports.Parity.Even;
break;
case "Odd":
COMport.Parity = System.IO.Ports.Parity.Odd;
break;
case "Space":
COMport.Parity = System.IO.Ports.Parity.Space;
break;
}
//StopBits Termo
switch (StopBits)
{
case "1":
COMport.StopBits = System.IO.Ports.StopBits.One;
break;
case "1.5":
COMport.StopBits = System.IO.Ports.StopBits.OnePointFive;
break;
case "2":
COMport.StopBits = System.IO.Ports.StopBits.Two;
break;
}
COMport.Open();
inputStream = COMport.BaseStream;
buffer = new byte[StreamBufferSize];
myCallBack = new AsyncCallback(OnCompletedRead);
}
/*abstract*/
public void Run()
{
inputStream.BeginRead(
buffer,
0,
buffer.Length,
myCallBack,
null);
}
public void OnCompletedRead(IAsyncResult asyncResult)
{
int bytesRead = inputStream.EndRead(asyncResult);
if (bytesRead > 0)
{
Temperatur = ConvertToHex(Encoding.ASCII.GetString(buffer, 0, bytesRead));
inputStream.BeginRead(buffer, 0, buffer.Length, myCallBack, null);
}
}
private string ConvertToHex(string asciiString)
{
string hex = "";
foreach (char c in asciiString)
{
int tmp = c;
hex += String.Format("{0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString()));
}
return hex;
}
public void Stop()
{
try
{
COMport.Close();
}
catch
{
MessageBox.Show("Unable to close the connection!");
}
}
}
}
////////////////////////////////////////////// END OF CLASS /////////////////////////////////////////////////
Loading
Karthik AgarwalPosted Nov 23, 2011, 9:46 AM
Sam HobbsPosted Nov 23, 2011, 4:13 PM
There is nothing wrong with asynchronous I/O; it is more complicated to do but it has very valid uses and for serial I/O asynchronous I/O is a very good thing.
I am very sorry I did not reply to this thread when I first saw it. My suggestion is to ensure that you are using flow control properly. Serial I/O uses two types of flow control; software and hardware. If the device at the other end uses one of the two, then you must also use it. It is the only way to reliably keep both ends synchronized.
I recommend that no one using serial I/O never use sleep!
Never!
HarisPosted Nov 23, 2011, 9:31 AM
It works now. Sleep was the solution.
Romba Nanri Sir
Karthik AgarwalPosted Nov 23, 2011, 9:13 AM
HarisPosted Nov 23, 2011, 9:06 AM
////////////////////////////////////////// CODE /////////////////////////////////////////
public void OnCompletedRead(IAsyncResult asyncResult)
{
//int bytesRead = inputStream.EndRead(asyncResult);
int bytes_to_read = this.COMport.BytesToRead;
//if (bytesRead > 0)
if (bytes_to_read > 0 )
{
this.COMport.Read(output_buffer, 0, bytes_to_read);
Temperatur = ConvertToHex(Encoding.ASCII.GetString(output_buffer, 0, bytes_to_read));
//Temperatur = ConvertToHex(Encoding.ASCII.GetString(buffer, 0, bytesRead));
Thread.Sleep(100);
inputStream.BeginRead(buffer, 0, buffer.Length, myCallBack, null);
}
}
/////////////////////////////////END ///////////////////////////////////////////////
When i used the old metod (shown downunder) i get continuesly data recived but i do not get the whole frame. I get:
Wanted frame: 7E 00 12 92 00 13 A2 00 40 71 03 75 A1 AB 01 01 00 00 01 00 24 1C
Received frame:
00 12 92 00 13 A2 00 40 71 03 75 A1 AB 01 01 00 00 01 00 24 1C
7E
00 12 92 00 13 A2 00 40 71 03 75 A1 AB 01 01 00 00 01 00 24 1C
7E
00 12 92 00 13 A2 00 40 71 03 75 A1 AB 01 01 00 00 01 00 24 1C
7E
00 12 92 00 13 A2 00 40 71 03 75 A1 AB 01 01 00 00 01 00 24 1C
7E
00 12 92 00 13 A2 00 40 71 03 75 A1 AB 01 01 00 00 01 00 24 1C
7E
........
So I get the start byte separaterd now.
//////////////////////////////////////CODE //////////////////////////////////////////
public void OnCompletedRead(IAsyncResult asyncResult)
{
int bytesRead = inputStream.EndRead(asyncResult);
//int bytes_to_read = this.COMport.BytesToRead;
if (bytesRead > 0)
// if (bytes_to_read > 0 )
{
// this.COMport.Read(output_buffer, 0, bytes_to_read);
//Temperatur = ConvertToHex(Encoding.ASCII.GetString(output_buffer, 0, bytes_to_read));
Temperatur = ConvertToHex(Encoding.ASCII.GetString(buffer, 0, bytesRead));
Thread.Sleep(50);
inputStream.BeginRead(buffer, 0, buffer.Length, myCallBack, null);
}
}
////////////////////////////////// END ////////////////////////////////////////
Karthik AgarwalPosted Nov 23, 2011, 8:39 AM
try writing
sleep(1000); // here 1000 says 1000ms which is nothing but 1 sec.
after every read cycle which may give some time for the port to read data. If that works fine then try reducing the time from 1000 to 500 and so on and settle down with the minimum time frame at which data is received perfectly.
After trying this let me know the result.
HarisPosted Nov 23, 2011, 8:33 AM
I have tested my hardware using docklight and it works (frame every 100ms).
I realy appreciate your help.
Karthik AgarwalPosted Nov 23, 2011, 8:15 AM
HarisPosted Nov 23, 2011, 8:09 AM
I have build a wireless temperaturesensor with two wireless modules (Xbee's) and i want the program to show the temperatur. It is nesessery that i run asynchronous IO because i will build more sensors later on and connect them.
The programs functionality:
Recive a frame with following format (Example):
0x7E 0x00 0x12 0x92 0x00 0x13 0xA2 0x00 0x40 0x71 0x03 0x75 0x79 0x86 0x01 0x01 0x00 0x00 0x01 0x00 0x2A 0x63
* The 1st byte (7E) is start-of-frame byte
* The folloing 11 bytes (0x00 0x12 0x92 0x00 0x13 0xA2 0x00 0x40 0x71 0x03 0x75) are observed to be konstant in every message.
* The last byte (0x63) is checksum
* The two bytes (0x00 0x2A) before the checksum are importent and needed to be isolated. This to bytes hold the value of the temperatur
When the value is isolated (Example: 0x00 0x2A) it needs to be converted to a temperatur value like this:
From hex to decimal: 0x00 0x2A = 0x002A --> Decimal: 42
Temperature in celsius = 42*0,732421875
////////////////////////////// Programs GUI ///////////////////////////////////
namespace ThermoCouple
{
public partial class ThermoCoupleViewer : Form
{
//Thermo Couple COMport Values
string ThermoCOMportName;
//Variables
byte[] ByteBuffer = new byte[30];
AsynchIOThermo TempSensor;
public ThermoCoupleViewer()
{
InitializeComponent();
string[] TermoPorts = SerialPort.GetPortNames();
TermoCoupleCOMComboBox.DataSource = TermoPorts;
}
//Buttons
private void ExitButton_Click(object sender, EventArgs e)
{Close();}
private void ConnectButton_Click(object sender, EventArgs e)
{
ThermoCOMportName = TermoCoupleCOMComboBox.Text;
TempSensor = new AsynchIOThermo(ThermoCOMportName, 22);
TempSensor.Run();
RefreshTime.Start();
DisconnectButton.Enabled = true;
ConnectButton.Enabled = false;
}
private void DisconnectButton_Click(object sender, EventArgs e)
{
TempSensor.Stop();
DisconnectButton.Enabled = false;
ConnectButton.Enabled = true;
}
private void TermoCoupleRefreshCOMButton_Click(object sender, EventArgs e)
{
string[] TermoPorts = SerialPort.GetPortNames();
TermoCoupleCOMComboBox.DataSource = TermoPorts;
}
private void RefreshTime_Tick(object sender, EventArgs e)
{TemperaturTextBox.Text = TempSensor.Temperatur;}
}
}
//////////////////////////////////////// END //////////////////////////////////////////////
Karthik AgarwalPosted Nov 23, 2011, 6:49 AM
COMport.PortName = "COM1";
COMport.BaudRate = 4800;
COMport.DataBits = 8;
COMport.Parity = System.IO.Ports.Parity.None;
COMport.StopBits = System.IO.Ports.StopBits.One;
COMport.Handshake = Handshake.None;
COMport.Encoding = Encoding.ASCII;
COMport.ReadTimeout = -1;
COMport.WriteTimeout = -1;
COMport.ReceivedBytesThreshold = 1;
COMport.ParityReplace = 0x3f;
COMport.NewLine = "\n";
COMport.ReadBufferSize = 0x1000;
COMport.WriteBufferSize = 0x800;
Karthik AgarwalPosted Nov 23, 2011, 6:30 AM
HarisPosted Nov 23, 2011, 6:00 AM
Karthik AgarwalPosted Nov 23, 2011, 5:38 AM
inputStream.BeginRead(buffer, 0, buffer.Length, myCallBack, null);// 1st line
You should have got the data already in output_buffer based on the following line
this.COMport.Read(output_buffer, 0, bytes_to_read);
then what for you wrote the 1st line?
HarisPosted Nov 23, 2011, 5:22 AM
//////////////////////////////////////////////////// CODE ///////////////////////////////////////
byte[] output_buffer = new byte[100]
public void OnCompletedRead(IAsyncResult asyncResult)
{
//int bytesRead = inputStream.EndRead(asyncResult);
int bytes_to_read = this.COMport.BytesToRead;
//if (bytesRead > 0)
if (bytes_to_read > 0 )
{
this.COMport.Read(output_buffer, 0, bytes_to_read);
Temperatur = ConvertToHex(Encoding.ASCII.GetString(output_buffer, 0, bytes_to_read));
//Temperatur = ConvertToHex(Encoding.ASCII.GetString(buffer, 0, bytesRead));
inputStream.BeginRead(buffer, 0, buffer.Length, myCallBack, null);
}
}
////////////////////////////////////// END OF CODE //////////////////////////////////////////////
Still no full frame and my COMport read buffer gets full. :(
Karthik AgarwalPosted Nov 23, 2011, 4:23 AM
int bytes_to_read = this.COMport.BytesToRead;
this.COMport.Read(output_buffer, 0, bytes_to_read);
try doing it this way and let me know the result.