System.IO.Ports.SerialPort and Multithreading - c#

I have some SerialPort code that constantly needs to read data from a serial interface (for example COM1). But this seems to be very CPU intensive and if the user moves the window or a lot of data is being displayed to the window (such as the bytes that are received over the serial line) then communication gets messed up.
Considering the following code:
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] buffer = new byte[port.ReadBufferSize];
var count = 0;
try
{
count = port.Read(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
if (count == 0)
return;
//Pass the data to the IDataCollector, if response != null an entire frame has been received
var response = collector.Collect(buffer.GetSubByteArray(0, count));
if (response != null)
{
this.OnDataReceived(response);
}
The code needs to be collected as the stream of data is constant
and the data has to be analyzed for (frames/packets).
port = new SerialPort();
//Port configuration code here...
this.collector = dataCollector;
//Event handlers
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
port.Open();
If there is no user interaction and nothing being added to the window,
this works fine but as soon as there is interaction communication really gets messed up.
Timeouts occur etc....
For example, this messes everything up:
Dispatcher.BeginInvoke(new Action(() =>
{
var builder = new StringBuilder();
foreach (var r in data)
{
builder.AppendFormat("0x{0:X} ", r);
}
builder.Append("\n\n");
txtHexDump.AppendText(builder.ToString());
txtHexDump.ScrollToEnd();
}),System.Windows.Threading.DispatcherPriority.ContextIdle);
});
But even simple calls to log4net cause problems.
Are there any best practices to optimize SerialPort communication
or can someone tell me what I'm doing wrong...
Update:
In case the above didn't make much sence. I made a very simple (and stupid) little example:
class Program
{
static void Main(string[] args)
{
var server = new BackgroundWorker();
server.DoWork += new DoWorkEventHandler(server_DoWork);
server.RunWorkerAsync();
var port = new SerialPort();
port.PortName = "COM2";
port.Open();
string input = "";
Console.WriteLine("Client on COM2: {0}", Thread.CurrentThread.ManagedThreadId);
while (input != "/quit")
{
input = Console.ReadLine();
if (input != "/quit")
{
var data = ASCIIEncoding.ASCII.GetBytes(input);
port.Write(data, 0, data.Length);
}
}
port.Close();
port.Dispose();
}
static void server_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("Listening on COM1: {0}", Thread.CurrentThread.ManagedThreadId);
var port = new SerialPort();
port.PortName = "COM1";
port.Open();
port.ReceivedBytesThreshold = 15;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
}
static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var port = (SerialPort)sender;
int count = 0;
byte[] buffer = new byte[port.ReadBufferSize];
count = ((SerialPort)sender).Read(buffer, 0, buffer.Length);
string echo = ASCIIEncoding.ASCII.GetString(buffer,0,count);
Console.WriteLine("-->{1} {0}", echo, Thread.CurrentThread.ManagedThreadId);
}
}
The result might look like this:
Listening on COM1: 6
Client on COM2: 10
This is some sample data that I send
---> 6 This is some sample data that I send
So reading the data from the port happens on the main thread....
Might this be part of what's causing my problems?

I am surprised no one caught this. The SerialPort class utilizes its own thread when using the DataReceived event. This means that if the subscriber, for example, is accessing any Form elements, then it must be done so with either an Invoke, or BeginInvoke method(s). Otherwise, you end up with a cross thread operation. In older versions of .net, this would go along unnoticed with unpredictable behaviour (depending on the CPU cores in the PC) and in later versions, should raise an exception.

Your last conclusion, that the event runs on the Main thread, may not be true for Windows App. Don't test this in a Console.
The proper way to tune this is:
set a large enough buffer, although the minimum 4096 is usually Ok
set the ReceivedBytesThreshold as high as tolerable (and do it before the Open())
do as little as possible in the Received event, pass the
data to a Queue or MemoryStream if you need more time

You should rewrite port_DataReceived procedure to read data until port.BytesToRead is greater then zero, like this:
private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
var port = (SerialPort)sender;
while (port.BytesToRead > 0)
{
int byte_count = port.BytesToRead;
byte[] buffer = new byte[byte_count];
int read_count = port.Read(buffer, 0, byte_count);
// PROCESS DATA HERE
}
}
Also I would recommend you to just insert data in queue list in procedure port_DataReceived, and perform data processing in separate thread.

The classic solution is to have a FIFO buffer. Ensure that the size of the FIFO is large enough to handle any critical case where there is a lot of input and the processor block is being taken up.
You could even have a 2-buffer system:
--->|Reader|-->FIFO-->|Processor|--->FIFO2--->|Displayer|

Related

How to pass a Variable to Lambda Expression

I was just starting to use C# last week and tried to create a simple serial monitor program. I want my program to read data from serialport continuously while displaying those data in the Form Application at the same time.
I use the code from here as reference https://github.com/ahelsayd/Serial-Lab
I use the same BeginInvoke() function. However I can not pass the variable that I want to write.
This is the original code
private void rx_data_event(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (mySerial.IsOpen)
{
try
{
//=====Only this part is different=======================
int dataLength = mySerial.BytesToRead;
byte[] dataReceived = new byte[dataLength];
int nbytes = mySerial.Read(dataReceived, 0, dataLength);
if (nbytes == 0) return;
//=====Only this part is different=======================
this.BeginInvoke((Action)(() =>
{
data = System.Text.Encoding.Default.GetString(dataReceived);
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
}));
}
catch { alert("Can't read form " + mySerial.PortName + " port it might be opennd in another program"); }
}
}
//And then update the UI
private void update_rxtextarea_event(object sender, DoWorkEventArgs e)
{
this.BeginInvoke((Action)(() =>
{
if (rx_textarea.Lines.Count() > 5000)
rx_textarea.ResetText();
rx_textarea.AppendText("[RX]> " + data);
}));
}
This code can read the Serialport and Write into the Form simultaneously. However, it does not receive all data from the serialport. So I modified the code to write the data into a buffer first until all data is received.
The modified code
private void rx_data_event(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (mySerial.IsOpen)
{
try
{
//=====Only this part is different=======================
string Data = mySerial.ReadExisting();
serialBuffer.Append(Data);
string bufferString = serialBuffer.ToString();
int index = -1;
do
{
index = bufferString.IndexOf(terminationSequence);
if (index > -1)
{
string message = bufferString.Substring(0, index);
bufferString = bufferString.Remove(0, index + terminationSequence.Length);
}
}
while (index > -1);
serialBuffer = new StringBuilder(bufferString);
byte[] bytes = new byte[30];
if (serialBuffer.Length == 15) {
Console.WriteLine("data:" + serialBuffer);
bytes = Encoding.ASCII.GetBytes(serialBuffer.ToString());
}
//=====Only this part is different=======================
this.BeginInvoke((Action)(() =>
{
data = Encoding.ASCII.GetString(bytes);
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
}));
}
catch { alert("Can't read form " + mySerial.PortName + " port it might be opennd in another program"); }
}
}
The problem is in the Form application the value of bytes is always null although when I checked by writing the output value to console window I can see the value of bytes updated.
I am very confused why the variable dataReceived value can be accessed by BeginInvoke while the variable bytes keep having null value. Is there something that I've missed that causing the value not get updated?
So I wasn't able to do it yesterday, but I promised the OP I would write something in an answer related to a comment of mine. From what I understand he/she is trying to update something in a Form based on received data from a serial port. Here's how I do it:
First off you need to declare a delegate and an event (which is basically a list of delegates):
public delegate void Data_Received_EventHandler(object sender, Data_Received_EventArgs e);
public event Data_Received_EventHandler Data_Received;
These are used to substitute the original "data received" event and event args from the serial port.
I usually define the Data_Received_EventArgs type as something based of an array of bytes, for simplicity:
public class Data_Received_EventArgs : EventArgs
{
public byte[] data;
internal Data_Received_EventArgs(int length)
{
data = new byte[length];
}
}
Then, in the original data reception event (let's say your serial port is called serialPort1):
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// check how many bytes you need to read:
int bytesToRead = serialPort1.BytesToRead;
// declare your arguments for the event based on that number of bytes (check the Data_Received_EventArgs constructor):
Data_Received_EventArgs args = new Data_Received_EventArgs(bytesToRead);
// copy the bytes from your serial port into the arguments:
for (int i = 0; i < bytesToRead; i++)
args.data[i] = (byte)serialPort1.ReadByte();
// verify if there are subscribers to the event (list not empty) and fire your event using BeginInvoke:
if (Data_Received != null)
BeginInvoke((MethodInvoker)delegate () { Data_Received(this, args); });
}
What we now have is an event which is guaranteed to execute its handlers on the UI thread.
To use it, you can subscribe to the event like so:
Data_Received += My_Data_Received_Handler;
Your handler should have the same signature as the delegate we declared in the first line of code (should be void and have specified parameters):
private void My_Data_Received_Handler(object sender, Data_Received_EventArgs e)
{
// read bytes from the arguments as you normally would from an array and do whatever e.g.:
some_Label.Text = e.data[0].ToString(); // without worrying about crossthreading
}
I know this is not the answer the OP wanted, but I hope it helps in simplifying what he/she was trying to do in the first place.

C# - Events and delegates when receiving data

I want to display a string when I receive data from a server. For that I was thinking on using delegates and events. I'm new to this topic (Delegates and Events) so I'm have not been able to set this up.
Here is what I've done:
public delegate void ClientHandleData(byte[] data, int bytesRead);
public event ClientHandleData OnDataReceived;
public void ConnectToServer(string ipAddress, int port)
{
this.port = port;
tcpClient = new TcpClient(ipAddress, port);
clientStream = tcpClient.GetStream();
Thread t = new Thread(new ThreadStart(ListenForData));
started = true;
t.Start();
}
private void ListenForData()
{
int bytesRead;
while (started)
{
bytesRead = 0;
try
{
bytesRead = clientStream.Read(buffer.ReadBuffer, 0, readBufferSize);
}
catch
{
//A socket error has occurred
MessageBox.Show("A socket error has occurred);
break;
}
if (OnDataReceived != null)
{
// display string to a textbox on the UI
}
Thread.Sleep(15);
}
started = false;
Disconnect();
}
You can just write
OnDataReceived?.Invoke(buffer.ReadBuffer, bytesRead);
If you want to be sure that your event will not be set to null after the if statement you can do this:
var handler = OnDataReceived;
handler?.Invoke(buffer.ReadBuffer, bytesRead);
Be careful when updating UI, because you can only update UI from UI thread. If you are using WPF you can do this:
Dispatcher.Invoke(() => {
// Update your UI.
});
And also make sure that someone actually subscribed to the event:
public void Foo()
{
objectWithTheEvent.OnDataReceived += OnOnDataReceived;
}
private void OnOnDataReceived(byte[] data, int count)
{
}
Let us have a look at your TcpClient listening code. When you call stream.Read() you can not be sure how much data will be read from your socket so you have to read until the end of the stream or you have to know how much date you are supposed to read from socket. Let us assume that you know how much data you are supposed to read
var readSofar = 0;
var iNeedToRead = 500;//500Bytes
try{
while(readSoFar<iNeedToRead){
var readFromSocket = clientStream.Read(buffer.ReadBuffer, readSofar, readBufferSize-readSofar);
if(readFromSocket==0){
//Remote server ended your connection or timed out etc
//Do your error handling may be stop reading
}
readSofar += readFromSocket;
}
}
catch {
//A socket error has occurred
MessageBox.Show("A socket error has occurred);
break;
}
if (OnDataReceived != null){
// display string to a textbox on the UI
}
You can use null propogation operator like this.
OnDataReceived?.Invoke(buffer.ReadBuffer, bytesRead);
If you are using WindowsForm each controller has to be updated from UI thread therefore you have to call from the subscriber method
private void IReceivedData(byte[] data, int count){
this.Invoke(()=>{...Your code});
}

Implementing threading while making a GUI that reads Serial Data and plots Graphs

I'm in the middle of a project wherein I have to read Serial Data in CSV format from the sensors of an Arduino , parse the obtained values using C#, and display real time graphs.
I am new to the concept of multithreading and I'm confused about how many threads should I make, and what task each thread should be assigned.
Any Suggestions?
This is an initial sample code, so it may have errors.
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
RxString = serialPort1.ReadExisting();
RxString = RxString.Replace("$", "");
this.Invoke(new EventHandler(DisplayText));
}
//display the parsed string List
private void DisplayText(object sender, EventArgs e)
{
richTextBox1.AppendText(RxString);
GlobalList.AddRange(parsed());
richTextBox2.Text = String.Join(Environment.NewLine, GlobalList);
}
//set the input rate
private void Start_Click(object sender, EventArgs e)
{
serialPort1.PortName = "COM32";
serialPort1.BaudRate = 9600;
serialPort1.DtrEnable=true;
serialPort1.Open();
if (serialPort1.IsOpen)
{
Start.Enabled = false;
Stop.Enabled = true;
richTextBox1.ReadOnly = false;
}
}
public List<String> parsed()
{
string line;
int loc = 0;
List<string> stringList;
line = richTextBox1.Text;
stringList = new List<string>(line.Split(','));
richTextBox3.AppendText("\n Pressure:" + stringList[loc]);
loc++;
richTextBox3.AppendText("\n Accelerometer:" + stringList[loc]);
loc++;
richTextBox3.AppendText("\n Temperature:" + stringList[loc]);
loc++;
richTextBox3.AppendText("\n Height:" + stringList[loc]);
loc++;
return stringList;
}
//plot an elementary graph from the values obtained
public void displayglobal()
{
for (int i = 0; i < GlobalList.Count; i++)
{
if (i % 3 == 0)
{
rtxtConsole.AppendText("\nPressure: " + GlobalList[i]);
chart1.Series["tempvspressure"].Points.AddXY(GlobalList[i], GlobalList[i + 2]);
}
}
}
I would strongly advise using a worker thread on anything that can block, since this freezes up the UI.
I would spin off a single thread to read the incoming stream and parse units of data. If the stream is CSV then your row delimiter, usually a new line, can be used by the stream parser to delimit units.
If the workload per row is very low, the stream reading thread can just invoke the UI thread for handling each time it has a complete unit of data. If the number of rows being handled causes the UI to lock up then another worker thread may be desired to batch insert them into the UI. For this you would require a thread safe queue.
I've done this many times in logging applications which listen to a network stream and the problem doesn't seem that different for a serial port.
With the new async support in C#, you shouldn't need multiple threads at all.
You can use port.BaseStream.ReadAsync() which will cooperate with UI message processing.

TCP Sending Simple Flood Control And Sync - P2P Webcam

With the help of some nice people from SO I have slowly built a small P2P app that sends and receives a stream of images of about 4kb each.
On 127.0.0.1 the receive keeps up with the sending, but when I try it on a remote machine it seems to me the receiving cannot keep up, I have perhaps sent 6 images but the receiver has only received one so far... and as time passes the difference gets bigger, until you see yourself a whole minute ago on the other screen. Its worth noting I would like this to work well on a connection that is about 64kbps-100kbps in another country where the ping times might be very big like 250ms or more.
What are the synchronisation options I have?
I have been advised by my brother of a simple solution which is to implement a 1:1 send/receive. So I only send an image when I receive one.
As I am a total beginner in network programming, any other tips are most welcome, here is my complete code :
namespace MyPrivateChat
{
using System;
using System.Drawing;
using System.Windows.Forms;
using AForge.Video;
using AForge.Video.DirectShow;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Net.Sockets;
using System.Diagnostics;
using AForge.Imaging.Filters;
public partial class fChat : Form
{
public fChat()
{
InitializeComponent();
}
private void fChat_Load(object sender, EventArgs e)
{
// get ip
_ownExternalIp = GetPublicIP();
Text = "My Private Chat - IP: " + _ownExternalIp;
// get video cam
var _videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (_videoDevices.Count != 0)
{
_videoDevice = new VideoCaptureDevice(_videoDevices[0].MonikerString);
btnStart.Enabled = true;
}
// fire up listener
listeningThread.RunWorkerAsync();
}
public string GetPublicIP()
{
string ip = "";
using (WebClient wc = new WebClient())
{
Match m = Regex.Match(wc.DownloadString("http://checkip.dyndns.org/"), #"(?<IP>\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})");
if (m.Success)
{
ip = m.Groups["IP"].Value;
}
}
return ip;
}
private void mnuPasteOwnIP_Click(object sender, EventArgs e)
{
txtPartnerIP.Text = _ownExternalIp;
}
private void btnStart_Click(object sender, EventArgs e)
{
if (_tcpOut == null)
{
// tcp server setup
_tcpOut = new TcpClient();
_tcpOut.Connect(txtPartnerIP.Text, 54321);
tmrLive.Enabled = true;
}
else
{
tmrLive.Enabled = false;
_tcpOut.Client.Disconnect(true);
_tcpOut.Close();
_tcpOut = null;
}
if (!_videoDevice.IsRunning)
{
_videoDevice.NewFrame += new NewFrameEventHandler(NewFrameReceived);
_videoDevice.DesiredFrameSize = new Size(640, 480);
_videoDevice.DesiredFrameRate = 100;
_videoDevice.Start();
btnStart.Text = "Stop";
}
else
{
_videoDevice.SignalToStop();
btnStart.Text = "Start";
}
}
private void NewFrameReceived(object sender, NewFrameEventArgs e)
{
Bitmap img = (Bitmap)e.Frame.Clone();
byte[] imgBytes = EncodeToJpeg(img, 25).ToArray();
if (_tcpOut.Connected)
{
NetworkStream ns = _tcpOut.GetStream();
if (ns.CanWrite)
{
ns.Write(BitConverter.GetBytes(imgBytes.Length), 0, 4);
ns.Write(imgBytes, 0, imgBytes.Length);
_totalFramesSent++;
}
}
}
private void listeningThread_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
_tcpIn = new TcpListener(IPAddress.Any, 54321);
_tcpIn.Start();
TcpClient _inClient = _tcpIn.AcceptTcpClient();
lblStatus.Text = "Connected - Receiving Broadcast";
tmrLive.Enabled = true;
NetworkStream ns = _inClient.GetStream();
while (true)
{
// read image size.
Byte[] imgSizeBytes = new Byte[4];
int totalBytesRead = 0;
do
{
int bytesRead = ns.Read(imgSizeBytes, totalBytesRead, 4 - totalBytesRead);
if (bytesRead == 0)
{
break; // problem
}
totalBytesRead += bytesRead;
} while (totalBytesRead < 4);
// read image
int imgSize = BitConverter.ToInt32(imgSizeBytes, 0);
Byte[] imgBytes = new Byte[imgSize];
totalBytesRead = 0;
do
{
int bytesRead = ns.Read(imgBytes, totalBytesRead, imgSize - totalBytesRead);
if (bytesRead == 0)
{
break; // problem
}
totalBytesRead += bytesRead;
} while (totalBytesRead < imgSize);
picVideo.Image = Image.FromStream(new MemoryStream(imgBytes));
_totalFramesReceived++;
}
}
private void CloseVideoDevice()
{
if (_videoDevice != null)
{
if (_videoDevice.IsRunning)
{
_videoDevice.SignalToStop();
}
_videoDevice = null;
}
}
private void fChat_FormClosing(object sender, FormClosingEventArgs e)
{
CloseVideoDevice();
}
private void btnClose_Click(object sender, EventArgs e)
{
Close();
}
private void tmrLive_Tick(object sender, EventArgs e)
{
_totalSecondsLive++;
lblStats.Text = "S:"+_totalFramesSent + " R:" + _totalFramesReceived + " T:"+ _totalSecondsLive;
if (_totalSecondsLive == 60)
{
MessageBox.Show("Total Frames : " + _totalFramesSent);
}
}
#region ENCODING JPEG
private MemoryStream EncodeToJpeg(Bitmap img, long quality)
{
using (EncoderParameters myEncoderParameters = new EncoderParameters(1))
{
MemoryStream ms = new MemoryStream();
myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
img.Save(ms, GetEncoder(ImageFormat.Jpeg), myEncoderParameters);
return ms;
}
}
private ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
#endregion
VideoCaptureDevice _videoDevice;
TcpClient _tcpOut;
TcpListener _tcpIn;
string _ownExternalIp;
int _totalFramesSent;
int _totalFramesReceived;
int _totalSecondsLive;
}
}
Well this is not a TCP specific problem. You are producing faster that you are consuming. Therefore you need to throttle your producer.
I would change the producer to wait for an acknowledgement from the consumer before I send the next image. I would discard all new images in the meantime.
On the producer you would keep a status flag which allows you to track if a frame has been sent and it has not been acknowledged. While this flag is true you discard new images as they appear. When it is false you send an image and set it to true. When the acknowledgement comes you set the flag to false.
Edit: I would implement the acknowledgement as a "bool" (a byte on the network) because this will acknowledge much faster than sending an image as a response. I would define two "messages": MessageType.Image and MessageType.Acknowledgement. The receiver can then see which MessageType arrived and either update the screen or start sending the next image.
Edit 2: You do not need to just discard images. You can have a variable Image latestUnsentImage. When the cam produces an image you unconditionally overwrite this variable. When you need to send an image you just access this variable. This will send always the latest available and unsent image.
What happens here, is that you fill up the buffers on the sending machine by generating data to send much faster, than it can be sent over the network. My approach would be
create two boolean flags newImageAvailable and readyToSend on the class level, both starting at false
In NewFrameReceived create a new image, but don't yet send it. Atomically store the byte[] in a class vairable instead and set newImageAvailable to true, then call new function TrySendImage() (see below for info)
On Tcp connect set readyToSend to true and call TrySendImage()
In TrySendImage()
.... check if BOTH newImageAvailable and readyToSend are true, if not do
nothing
.... set newImageAvailable and readyToSend to false
.... Send image (Size + data) asynchronously (BeginSend())
In the completion notification for BeginSend() set readyToSend to true
While this is a bit complicated, it makes sure, that allways the newest image is sent, and only if the image before is "on the wire".
I consider this superior to a "1:1 send/receive" solution, as you often have cases, where bandwidth is different in the two directions - in this case the 1:1 solution would degrade performance of the good direction to performance of the bad direction.
An optimization I have used on a similar project is, to dynamically adapt the quality parameter of the JPEG encoding: If framerate falls below a threshold reduce quality by a factor, until a minimum is reached, if framerate climbs above another threshold increase quality by a factor, again until a mmaximum.
Well as for what "usr" has said about throttling, instead of discarding your images, you might want to add these captured images to a queue. From te queue you should be able to pick them up one by one and via another thread and transport them over the network. You might even add a sequence number to the images being transported apart from the boolean acknowledgement to ensure a transport of images. Anything that is not transported should be enqueued again. Sequencing should allow you to sort the correct order of images and produce a lesser loss of data.
Sender
create sequence number
assign bool flag
Enqueue in transport queue
Dequeue and transport (separate thread)
Re queue if no acknowledgement
Receiver (possibly what you can do)
Receive from network
Send acknowledgement
Check sequence number and sort (may be storage required to sequence
numbering check)

serial communication, read the 9th bit

I have an application which connects with an external protocol
using serial communication.
I need know if the wakeup bit is set on each packet it sends to me (the 9 bit), and as communication rates must be below 40ms, and response must be sent under 20 ms.
The framework, encapsulates the bits read from the port, and only send back the 8 bits of data to me. Also, I cannot wait for the parity error event, because of timing issues.
I need to know how can I read the 9 bit, or if there is a free alternative to http://www.wcscnet.com/CdrvLBro.htm
Did you try to put your serial read function right in the parity error event handler? Depending on the driver, this might be fast enough.
This wouldn't happen to be for a certain slot machine protocol, would it? I did this for fun for you. Maybe it will work?
{
public Form1()
{
InitializeComponent();
}
SerialPort sp;
private void Form1_Load(object sender, EventArgs e)
{
sp = new SerialPort("COM1", 19200, Parity.Space, 8, StopBits.One);
sp.ParityReplace = 0;
sp.ErrorReceived += new SerialErrorReceivedEventHandler(sp_SerialErrorReceivedEventHandler);
sp.ReadTimeout = 5;
sp.ReadBufferSize = 256;
sp.Open();
}
object msgsLock = new object();
Queue<byte[]> msgs = new Queue<byte[]>();
public void sp_SerialErrorReceivedEventHandler(Object sender, SerialErrorReceivedEventArgs e)
{
if (e.EventType == SerialError.RXParity)
{
byte[] buffer = new byte[256];
try
{
int cnt = sp.Read(buffer, 0, 256);
byte[] msg = new byte[cnt];
Array.Copy(buffer, msg, cnt);
if (cnt > 0)
{
lock (msgsLock)
{
msgs.Enqueue(msg);
}
}
}
catch
{
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if (msgs.Count > 0)
{
lock (msgsLock)
{
listBox1.Items.Insert(0, BitConverter.ToString(msgs.Dequeue()));
}
}
}
}
}
Anyways, for more control over the serial port I suggest using the win32 calls to get what you want.
http://msdn.microsoft.com/en-us/magazine/cc301786.aspx

Categories

Resources