I have written code to read data as a byte array from a serial port and show it in a textbox. The code compiles fine, but doesn't work properly:
private void button2_Click(object sender, EventArgs e)
{
if (serialPort1.IsOpen == false)
serialPort1.Open();
serialPort1.WriteLine(textBox1.Text);
int bytes = serialPort1.BytesToRead;
byte[] byte_buffer = new byte[bytes];
serialPort1.Read(byte_buffer, 0, bytes);
//textBox2.Text = " ";
for (int t = 0; t < bytes; t++)
{
textBox2.Text += (byte_buffer[t]).ToString();
}
}
serialPort1.WriteLine(textBox1.Text);
int bytes = serialPort1.BytesToRead;
The bytes value will always be zero. Unless you debug this code and single-step it to slow it down. It takes time for the bytes you've written with WriteLine() to be transmitted. And it takes time for the device to process them. And it takes time for the response to be received. This adds up to many milliseconds.
You'll need to fix this by looping, repeated calling the Read() method until you get the full response. If you set the SerialPort.NewLine property correctly then you'll have some odds that simply calling ReadLine() is enough to solve your problem.
You are going about this the wrong way.
Clicking a button will open serialPort1; sure. It will then try to read the buffer. But you only opened the port in the same method!
Take a look at this tutorial: http://www.dreamincode.net/forums/topic/35775-serial-port-communication-in-c%23/
It takes you through the entirety of serial communications in C#. You certainly don't want to be opening and reading the port only on a button press event handler.
Use button2 event to send the data to the port. Put the needed code (for sending the data) into a SynchronizationContext (use SynchronizationContext.Post method).
Next, register on the DataReceived event of the SerialPort class and do the reading there (again enclosed into the same SynchronicationContext object, otherwise you'll get a timeout on serial port reading/writing)
Cheers,
Related
I am trying to write a program that updates a windows form every time new data comes in on a serial port, but am struggling with understanding how the serial port works, and how I can use it in a way I want it.
I have an external device sending 8 bytes at 1Hz to my serial port, and wish to use the DataReceived event from the SerialPort Class. When I debug my code, the event is more or less triggered randomly based on what the program is doing at a certain time. The code as is is below:
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//byte[] rxbyte = new byte[1];
byte[] rxbyte = new byte[8];
byte currentbyte;
port.Read(rxbyte, 0, port.BytesToRead);
currentbyte = rxbyte[0];
int channel = (currentbyte >> 6) & 3; //3 = binary 11, ANDS the last 2 bits
int msb_2bit = (currentbyte >> 0) & 255; //AND compare all bits in a byte
currentbyte = rxbyte[1];
int val = ((msb_2bit << 8) | (currentbyte << 0));
//Extra stuff
SetText_tmp1(val.ToString());
}
I want to be able to have exactly 8 bytes in the receive buffer before I call the Read function, but I am not sure how to do this (never used SerialPort class before), and want to do all manipulation of data only when I have the entire 8 bytes. Is there a built in way to toggle the event only when a certain amount of bytes are in the buffer? Or is there another way to obtain only 8 bytes, but not more, and leave the remaining bytes to the next instance?
Yeah, you are not coding this correctly. You cannot predict how many bytes you are going to receive. So just don't process the received bytes until you've got them all. Like this:
private byte[] rxbyte = new byte[8];
private int rxcount = 0;
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
rxcount += port.Read(rxbyte, rxcount, 8 - rxcount);
if (rxcount < 8) return;
rxcount = 0;
// Process rxbyte content
//...
}
Set the ReceivedBytesThreshold property to 8. As in port.ReceivedBytesThreshold = 8;
An effective way to handle this is to add a timer to the class that ticks along at maybe 9 times a second. Eliminate the serial port event handler completely.
At each timer tick have the code check the serial port for bytes received from the serial port. If there are some there then grab them out of the serial port and append them to the end of buffer maintained in the class as a data member.
When the buffer has eight or more characters in it then timer tick logic would take the first 8 bytes out of the buffer and use them to update the user interface window. Any remaining bytes in the buffer can be moved up to the head of the buffer.
The timer tick routine can also maintain a counter value that increments each time the tick comes in and there is no data ready at the serial port at this tick. When this counter reaches a value of say 3 or 4 the code would reset the data buffer to empty and reset the counter back to zero. When data is actually seen from the serial port this counter is reset to zero. The purpose of this counter mechanism is to synchronize the data receive buffer with the 1Hz data stream coming in so the receive process does not get out of sync with what data represents the start of the 8-byte message.
Note that this method is superior to the serial port received data event because it allows your program to stay in control of things. I've already described the ability to synchronize with the data stream bursts - which is not possible to do with trying to set the serial port received data threshold to a count like 8. Another advantage is that the timer tick code can include additional handling functions such as signalling a timeout if no data arrives from the serial port in say 2 or 3 seconds.
I'm handling a caller id serial device and write the following program:
serialPort = new SerialPort("COM7", 19200, Parity.None, 8, StopBits.One);
serialPort.DataReceived += serialPort_DataReceived;
serialPort.RtsEnable = true;
serialPort.Encoding = Encoding.ASCII;
serialPort.Open();
void serialPort_DataReceived(object s, SerialDataReceivedEventArgs e)
{
byte[] data = new byte[serialPort.BytesToRead];
serialPort.Read(data, 0, data.Length);
Console.WriteLine(Encoding.ASCII.GetString(data));
}
At first that I receive a call, the event fires perfectly and the result is: "A0101181456926E"
The problem is the subsequent events... next time that I make a call, the event serialPort_DataReceived fires a lot of times each one with 1 char.
Is there any property to set or method to invoke to solve this behaviour?
ps. If I comment the line serialPort.RtsEnable = true;, I don't receive any subsequent event.
As Henk mentioned, you can set the amount of bytes to be received before the DataReceived event is triggered with the property ReceivedBytesThreshold.
But in any case you have to deal with any number of bytes to be received at a time. You have to design your protocol in a way that you are able to recognize when a message is fully received.
It is normal behavior. You can change the ReceivedBytesThreshold property, but doing so means that you have to receive at least that amount, and if there are any errors in transmission, getting in sync again can be difficult.
My advice is to leave ReceivedBytesThreshold=1, and queue the received data up until you have what you need.
I have done just that what is suggested by building in a buffering mechanism to detect when I have received a full input string based on a vendors protocol. works fine. (Must say, not all protocol designs provides clear start and end sequences to work with) What I couldn't understand was why the DataReceived eventhandler was still firing although when I read the input buffer with the Port.ReadExisting() method, the string length was zero. Reading up on the SerialPort.ReceivedBytesThreshold Property on https://msdn.microsoft.com/en-us/library/system.io.ports.serialport.receivedbytesthreshold(v=vs.110).aspx I realized that even though the default of the property is 1 the handler will still fire "if an Eof character is received, regardless of the number of bytes in the internal input buffer" Maybe this is the reason why the handler fires what seems to be unnecessarily
Ok, this should be dirt simple. I'm trying to read charactes from a serial device. It's such that if I send a space character, it echos back a string of numbers and EOL. That's it.
I'm using Unity 3.3 (.Net 2.0 support), and the 'serial port' is a Prolific serial-to-USB adaptor. BTW: Using Hyperterminal, it all works perfectly, so I know it's not driver nor hardware.
I can open the port ok. It seems I can send my space with port.Write(" "); But if I even TRY to call ReadChar, ReadByte, or ReadLine (like polling), it freezes up until I unplug the USB, and my console output shows nothing (exceptions were caught).
So instead I set up a DataReceviedHandler, but it's never called.
I've read some posts where people have done just this type of thing with Arduinos etc. (this is not an Arduino but hey), using nothing more than ReadLine. Their code does not work for me (and no answers thus far from those authors).
So, any tips? Do I need to use a different thread? If you know any Unity (Mono) coding, any tips along those lines greatly appreciated.
This code a mashup from http://plikker.com/?p=163 and http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx#Y537
using UnityEngine;
using System.Collections;
using System.IO.Ports;
using System;
public class SerialTest : MonoBehaviour {
SerialPort stream;
void Start () {
try {
stream = new SerialPort("COM3", 9600);
stream.Parity = Parity.None;
stream.StopBits = StopBits.One;
stream.DataBits = 8;
stream.Handshake = Handshake.None;
stream.DataReceived += new SerialDataReceivedEventHandler(DataReceviedHandler);
stream.Open();
Debug.Log("opened ok"); // it DOES open ok!
} catch (Exception e){
Debug.Log("Error opening port "+e.ToString()); // I never see this message
}
}
void Update () { // called about 60 times/second
try {
// Read serialinput from COM3
// if this next line is here, it will hang, I don't even see the startup message
Debug.Log(stream.ReadLine());
// Note: I've also tried ReadByte and ReadChar and the same problem, it hangs
} catch (Exception e){
Debug.Log("Error reading input "+e.ToString());
}
}
private static void DataReceviedHandler(
object sender,
SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender; // It never gets here!
string indata = sp.ReadExisting();
Debug.Log("Data Received:");
Debug.Log(indata);
}
void OnGUI() // simple GUI
{
// Create a button that, when pressed, sends the 'ping'
if (GUI.Button (new Rect(10,10,100,20), "Send"))
stream.Write(" ");
}
}
Events are not implemented in Mono SerialPort class, so you won't get any notifications, you have to perform (blocking) read explicitly. Other possible problem - I'm not sure how Unity Behaviours work, are you certain all methods accessing the SerialPort are invoked on the same thread? And you are not disposing you port object, this will also cause problems.
Make sure that you are opening the right port, using correct settings. Here is an example of how you could configure it:
serial = new SerialPort();
serial.ReadBufferSize = 8192;
serial.WriteBufferSize = 128;
serial.PortName = "COM1";
serial.BaudRate = 115200;
serial.Parity = Parity.None;
serial.StopBits = StopBits.One;
// attach handlers
// (appears to be broken in some Mono versions?)
serial.DataReceived += SerialPort_DataReceived;
serial.Disposed += SerialPort_Disposed;
serial.Open();
I recommend the open source RealTerm terminal, it has a rich set of features and can help you debug. Try writing a byte manually using such software, and if it works, then the problem is in your program. Otherwise it might be a driver problem (but more likely it isn't).
[Edit]
Calling SerialPort.ReadLine is actually supposed to block the thread until SerialPort.NewLine is received. Also ReadChar and ReadByte will hang until at least one byte is received. You need to make sure that you are actually receiving characters from the other side, and you won't be receiving them if your app is stuck and cannot send the space.
Since I never used Unity, I am not sure how Update is called, but I am presuming it's fired on a foreground thread in regular intervals (otherwise your app wouldn't freeze).
The example that you linked (Arduino and Unity example) shows that Arduino is sending the data continuously, and that is why their Update method is constantly receiving data (no space character needs to be sent towards the device). If they unplug the device, their app will hang just as well.
Well, maybe not, because in .NET 1.1, default value for ReadTimeout was not infinite, like it is in .NET 2.0.
So, what you can do is:
a. Set the ReadTimeout property to a reasonable value. Default in .NET 2.0 is InfiniteTimeout, which doesn't suit your needs. Cons: your update method will still hang for a while on each call, but not infinitely.
b. Someone said that events are not implemented in MONO SerialPort, so I guess using DataReceived only is not an option.
c. Move your sending logic to the Update method also, so that you don't read data at all, until it's time to read it:
private volatile bool _shouldCommunicate = false;
void Update ()
{
if (_shouldCommunicate) // this is a flag you set in "OnGui"
{
try {
stream.Write(" ");
Debug.Log(stream.ReadLine());
} catch (Exception e){
Debug.Log("Error reading input "+e.ToString());
}
}
}
void OnGUI() // simple GUI
{
if (GUI.Button (new Rect(10,10,100,20), "Send"))
_shouldCommunicate = true;
}
Note that, if your device is not sending data, it will also block at stream.ReadLine(), so make sure your ReadTimeout is set to a reasonable value. You will also want to stop sending at some point, but I leave that to you.
d. Send the space in OnGui like you are doing now, but always check if there is data in your buffer before reading it:
void Update () { // called about 60 times/second
try {
// call our new method
Debug.Log(ReadLineNonBlocking());
} catch (Exception e){
Debug.Log("Error reading input "+e.ToString());
}
}
private StringBuilder sb = new StringBuilder();
string ReadLineNonBlocking()
{
int len = stream.BytesToRead;
if (len == 0)
return "";
// read the buffer
byte[] buffer = new byte[len];
stream.Read(buffer, 0, len);
sb.Append(ASCIIEncoding.ASCII.GetString(buffer));
// got EOL?
if (sb.Length < 2 ||
sb[sb.Length-2] != '\r' ||
sb[sb.Length-1] != '\n')
return "";
// if we are here, we got both EOL chars
string entireLine = sb.ToString();
sb.Length = 0;
return entireLine;
}
Disclaimer: this is directly out of my head, untested, so there may be some syntax errors which I am sure you will handle.
Maybe your problem is the configuration of the serial port. It is important not only to check for BaudRate or StopBits. Also you should configure DTR, RTS, Handshake, everything. This is important cause maybe another program set some ugly values and the configuration must be explicitly set at every start or some settings of the old connection can run you into trouble.
Also maybe take a look into one of these tools:
com0com
Serial Port Monitor
They can help you to stub your serial interface or to take a deeper look into the connection. Also maybe try to talk to your serial device by using HyperTerminal or some similar tool that's proven to work.
Had similar problem with Mono, upgrading to 2.6.7 helped.
Do not mix data event and blocking read. What do you expect to happen if data arrives? That both the read method and the event should get the same received data?
You should also read about:
CTS: http://en.wikipedia.org/wiki/RS-232_RTS/CTS#RTS.2FCTS_handshaking
DTR: http://en.wikipedia.org/wiki/Data_Terminal_Ready
Small serial port tutorial describing all states: http://www.wcscnet.com/Tutorials/SerialComm/Page1.htm
The standard c# System.IO.Pots.SerialPort sucks big time. I suggest to give RJCP.DLL.SerialPortStream library a try. Synchronous read/write is super easy with this delightful library too, no need to jump through the loops with delegate and listeners.
From outside SerialPort object, it seems to make no difference what the size of the write buffer is, and whether or not it is full.
Using synchronous writing, the write method blocks until all the data has been sent and the buffer is empty.
Using async writing, the data is queued and the program continues on going. The callback method is not called until the write operation is completed and the data is out of the buffer.
The behavior of the serialport object seems to be the same regardless of how much data is in the buffer and whether or not the buffer is full. No errors seem to happen when the write buffer is full.
So, why be able to check BytesToWrite and WriteBufferSize at all? Is there any way that SerialPort behaves differently when the write buffer is full?
Buffers are a mechanism designed to allow whoever processes the buffer to do it in their own way, at their own time.
When i send data, i want it to be pushed at the maximal rate of the port, but i don't want to busywait on the port and wait for each byte to be sent before i push the next one. So you have a processing buffer that feeds the hardware, and can be passed in chunks.
As for the why you'd want to check the BytesToWrite - you are often interested to know if all your data was sent before moving on to the next thing you want to do, you might expect a response after a given period of time, you might want to know what the actual transfer rate is etc'.
The C# SerialPort.BytesToWrite property corresponds to the unmanaged Win32 COMSTAT.cbOutQue field which is described as:
The number of bytes of user data remaining to be transmitted for all write operations. This value will be zero for a nonoverlapped write.
This seems to suggest you could observe the write buffer being consumed as it is sent with async writing before the write complete callback is called.
I wanted to create a test utility that constantly sends out 0xAA out the serial port, with no gaps, forever. I don't care about RX.
I used a timer to keep the buffer full, and monitored BytesToWrite to wait below it was below threshold, before I wrote more data into the buffer.
I could have alternatively not used a timer, but refreshed the serial port in the AsyncCallback, but I wanted to do it this way for fun. You can view the label11 to see the buffer fill and empty.
Note you can get away with BeginWrite without EndWrite for a short time, but eventually you will run out resources. I am basically just putting in a dummy EndWrite.
private void checkBox2_CheckedChanged(object sender, EventArgs e)
{
timerFill.Enabled = checkBox2.Checked;
}
private void timerFill_Tick(object sender, EventArgs e)
{
GenerateSquareWave();
}
int const bufferSize = 256;
void GenerateSquareWave()
{
int k = serialPort1.BytesToWrite;
label11.Text = k.ToString();
if (k < bufferSize)
{
byte[] data = new byte[bufferSize];
for (int i = 0; i < bufferSize; i++)
{
data[i] = 0xAA;
}
serialPort1.BaseStream.BeginWrite(data, 0, data.Length, new AsyncCallback((IAsyncResult ar) => serialPort1.BaseStream.EndWrite(ar)), null);
}
}
I'm using the pretty nifty nmeasharp project to decipher an NMEA stream I'm receiving on a serial port in c#. It all works fine out of the box, but I want to mirror the data to an IP address, and am getting stuck.
The nmeasharp package gets it's data from any stream, so I used to hook it up to the serial port stream, which worked fine:
_nmeaParser.Source = serialport.BaseStream;
Now, I want to use the serial port event to trigger my own routine, where I can redirect the data, so i remove the assignment above, and set:
_serialport.DataReceived += new serialDataReceivedEventHandler(HandleNewSerialPortData);
This event is triggered, and the method gets called. All good at this point, but the nmeasharp code is still looking for a stream to listen to, since I haven't assigned it to a stream anymore.
The following method is where I need to set up a stream to nmeasharp, and write whatever new data the serial port has just received out to that stream.
private void HandleNewSerialPortData(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
this.serialDataAvailable(indata); // raise event that writes string to IP
if (_nmeaParser.Source == null) _nmeaParser.Source = new MemoryStream(840);
if (_nmeaParser.Source.CanWrite) _nmeaParser.Source.Write(ASCIIEncoding.UTF8.GetBytes(indata), 0, indata.Length);
// Unsuccessful attempts
// MemoryStream s = new MemoryStream(ASCIIEncoding.UTF8.GetBytes(indata));
// s.CopyTo(_nmeaParser.Source);
// sp.BaseStream.CopyTo(this.NmeaDataStreamFromSerialPort);
}
I've tried several variations of trying to write to the nmeasharp stream, but none work. One that showed promise was initialising a new stream every time, but that meant that the stream was closed after every DataReceived event, which truncated and missed out serial messages. The (unsuccessful) code was:
_nmeaParser.Source = new MemoryStream(ASCIIEncoding.UTF8.GetBytes(indata));
I've read lots of tutorials, read all the msdn documentation I could find, and still can't get this simple thing working. This has to be easy, right...?
Edit: I would like to keep the nmeasharp code stock if possible, as it works fine, and as the serial data isn't always ASCII, would like to keep it binary (streams) rather than sending it the data as a string. I can fix up the IP redirection for binary later.
Thanks.
I would try to create two streams. Read from the serial stream manually, and copy to both streams.
Set the nmeaParser to use one of them, and have the IP handler read from the second one.
You can look here for a good solution on how to copy streams.