So I'm reading data from the serial port, so far so good, but the data coming from the serial port is chunked, I've this protocol that states that every messages begin with SOH (\u0001 byte) and ends with EOT(\u0004), I tried to split the message by the SOH byte, but still having issues with that.
There's a more elegant and safe way to do this?
private void RecebendoDados(object sender, SerialDataReceivedEventArgs e)
{
try
{
var id_prova = Form1._Form1.IDPROVA;
var serie = Form1._Form1.SERIE;
var fase = Form1._Form1.FASE;
var http = new ComunicacaoWeb();
var sp = (SerialPort)sender;
var indata = sp.ReadExisting();
Console.WriteLine(indata+"\n\r");
if (!sp.IsOpen) {
sp.Open();
}
var pacotes = indata.Split(new[] { "\u0004" }, StringSplitOptions.None);
1
Theres a more elegant and safe way to do this?
If your message end with the EOT control code then SerialPort allows you to read exactly up to the first occurrence of this char.
SerialPort sp = (SerialPort)sender;
// this lines converts the hex-code of EOT to a char and read the incoming
// message only up to this point
string indata = sp.ReadTo(Convert.ToChar(0x04));
now you need only to get rid of the SOT:
indata = indata.TrimStart(Convert.ToChar(0x01));
at this point you should have on clean message.
The property BytesToRead will tell you whether there is still data in the buffer:
int stillToBeRead = sp.BytesToRead;
you can check it and repeat the reading procedure if necessary.
2.
Theres a more ellegant and safe way to do this?
if you try to read from a closed port then it will fail with a System.InvalidOperationException. By this logic the if-condition in your code will never be entered to reopen the port.
var sp = (SerialPort)sender;
var indata = sp.ReadExisting();
if (!sp.IsOpen) {
// this will never be executed
sp.Open();
}
you should check whether the port is open and there is data to read before attempting to read from it. I guess RecebendoDados is the DataReceived event, so the probability of having a closed port and no data to read is low, but still: the devil sleeps in the detail ;)
Related
I created a wpf application to control and monitor a sensor invented at my university. I connect to the device with 2 comports of which one is exclusively for sending data from the device. The device encodes each dataset to 5 bytes and starts sending them as soon as I give it the order via the other comport. Internally the sensor has a buffer of ~40 kilobytes.
Problem:
It works very will until a sampling rate above 25kHz (25000 times 5 bytes each second) is chosen. Then data seems to be lost and the device sends an error, that the internal buffer ran full (which explains the lost data)
I tried several approaches without success yet, the latest was trying to apply this solution: https://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport
I fail to understand what determines how often I receive an event that grabs the data from the serialport. The problem doesn't change whether my program is fast or slow .. always ~25kHz = ~200kBytes/s
Code:
private SerialPort _comPortData;
_comPortData.DataReceived += new SerialDataReceivedEventHandler(PortDataReceived);
The Connect() function, stripped-down:
void connect()
{
comPort.BaudRate = 115200;
comPort.DataBits = 8;
comPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), "One");
comPort.Parity = (Parity)Enum.Parse(typeof(Parity), "None");
comPort.PortName = port;
comPort.Open();
Console.WriteLine(comPort.PortName + " opened | Baud Rate: " + comPort.BaudRate);
}
The (edited) PortDataReceived function:
private void PortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (!_comPortData.IsOpen) return;
// Obtain the number of bytes waiting in the port's buffer
int numberOfBytes = _comPortData.BytesToRead;
// Create a byte array buffer to hold the incoming data
byte[] buffer = new byte[numberOfBytes];
// Read the data from the port asynchronously and store it in the buffer
await _comPortData.BaseStream.ReadAsync(buffer, 0, numberOfBytes);
}
I am new to C# and programming in general.
I am trying to communicate with a ohmmeter which is conneted via usb to my computer.
I am able to configurate the device and even recieve data.
But i can not acces this data. I can just print it on to the console.
(Was inspired by the code on the microsoft site)
Here is the constructer of my "communication"-class where i configurate the port:
public SCPI_Commands()
{
_SerialPort.PortName = SetPortName(_SerialPort.PortName);
_SerialPort.BaudRate = 115200;
_SerialPort.Parity = Parity.None;
_SerialPort.DataBits = 8;
_SerialPort.StopBits = StopBits.One;
_SerialPort.Handshake = Handshake.None;
_SerialPort.ReadTimeout = 500;
_SerialPort.WriteTimeout = 500;
_SerialPort.Open();
_SerialPort.DataReceived += _serialPort_DataReceived;
}
Here is my function which sends a query to the device (The constant Measurment_Value represents a scpi command which is understood by my ohmmeter) :
public void get_measurement()
{
_SerialPort.WriteLine(Measurment_Value);
}
And here is the private function which checks if the device is sending data and printing it on the console (not sure how this function works) :
private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Console.WriteLine(_SerialPort.ReadLine());
}
Unfortunately i am not able to return the data as a string. My goal is to do some calculations with the received data.
Does someone has any ideas ?
Greetings from Germany.
Luke
You can read from buffer to temp byte array and then get it as string, see the below example. Put this in _serialPort_DataReceived
// this the read buffer
byte[] buff = new byte[9600];
int readByteCount = _serialPort.BaseStream.Read(buff, 0, _serialPort.BytesToRead);
// you can specify other encodings, or use default
string response = System.Text.Encoding.UTF8.GetString(buff);
Side Note
If you want to keep your sanity while working with SerialPort, always send and receive as byte array. Then, get the equivalent string using Encoding.
The problem is, that I can trigger scanner using Serial Port software "Hercules" sending command <SYN>T<CR><LF>, in datasheet is said to use command [SYN]T[CR] to trigger scanner, but I cant trigger it (both commands) using my serial port comunication bellow.
I get input when use scanner manually but can't trigger it... What is a problem?
(The port is virtual)
private static SerialPort port;
private static bool _continue = false;
public static void Main(string[] args)
{
port = new SerialPort();
port.PortName = "COM8";
port.BaudRate = 115200;
port.Parity = Parity.None;
port.DataBits = 8;
port.StopBits = StopBits.One;
port.Handshake = Handshake.None;
port.RtsEnable = true;
port.DtrEnable = true;
port.ReadTimeout = 500;
port.WriteTimeout = 500;
port.Open();
_continue = true;
Thread thr = new Thread(SerialPortProgram);
thr.Start();
}
private static void SerialPortProgram()
{
Console.WriteLine("Writing to port: <SYN>T<CR><LF>");
string command = "<SYN>T<CR><LF>";
port.WriteLine(command);
while (_continue)
{
try
{
string input = port.ReadLine();
Console.WriteLine("Input is - " + input);
}
catch (TimeoutException) { }
}
}
Python barcode scanner serial trigger is an article that I answered similar Python question.
The contents are shown below.
This happens because you coded the abstract expression written in the document as raw output data.
The document represents 3 bytes of data transmission.
'SYN' and 'CR' are the following hexadecimal numbers.
'SYN' = \x16
'CR' = \x0d or escape sequence \r
'T' is an ordinary ASCII character.
Whitespace and < > [ ] { } are used to delimit the data in the document, not the data to send.
And, even you need to command prefix it.
Also use Write instead of WriteLine as written by #Turbofant.
You should write like this. Please try it.
string command = "\x16M\x0d\x16T\x0d";
port.Write(command);
I guess the problem is, that you are sending the wrong command string.
The <Syn>, <CR> and <LF> stands for the special, non printable ascii characters synchronous idle, Carriage return and line feed.
You need to encode them correctly in the string
Try sending:
string command = "\x16t\r\n";
port.Write(command);
\x16is <Syn> (Because Syn is ascii character 0x16, or 22 in decimal )
\r is <CR>
\n is <LN>
And use port.Write instead of port.WriteLine, because WriteLine automatically adds the \r\n at the end of the string.
I'm using the following code to receive hex data over the serial port, it seems that some of these transmissions are broken up into 2 lines when they are parts of the same transmission. How do I make sure that each transmission is received properly?
public void Receiver(object sender, SerialDataReceivedEventArgs e)
{
string data;
do
{
data = COMPort.ReadExisting();
} while (COMPort.BytesToRead != 0);
RxARR = data.ToCharArray().ToList();
Dispatcher.Invoke(new MethodInvoker(Display)); // Start "Display" on the UI thread
}
You should never assume that data from a serial port is provided to you in one go (the same is true for network communication by the way). You need to make sure that your code accepts a message only if you have received everything.
Everything is hard to define. This can either be all characters until a defined termination sequence (for example \r\n or EOL byte) or until a fixed number of bytes is read. Generally one can say, as every message can be fragmented, reliable communcation is not possible without a defined end of message signal.
What we do is:
Create a StringBuilder
Read everything into that StringBuilder
Search for termination sequence
Remove everything from the StringBuilder up to and including the termination sequence
Process that chunk of data
Repeat from 3 until termination sequence is not found
Keep the remaining characters in StringBuilder, as this is the start of a new message
Pseudo code:
private StringBuilder serialBuffer = new StringBuilder();
private string terminationSequence = "\r\n"; // Anything that can't be part of a message
public void Receiver(object sender, SerialDataReceivedEventArgs e)
{
string data = COMPort.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);
HandleMessage(message);
}
}
while (index > -1)
serialBuffer = new StringBuilder(bufferString);
}
By the way: Looping within your DataReceived event is not desired, as this event is called by the SerialPort whenever something new is ready to be read. Reading in a loop may interfere with what SerialPort is doing by default. So: Don't read in a loop within that event! The "loop" is the event being fired in sequence by SerialPort.
I Had almost the same situation.
Like Thorsten said the data sometimes does not come at once..
Maybe because of Hardware or BaundRate..
What really works for me was:
var end = '\r';
int dataLength = _serialPort.BytesToRead;
byte[] data = new byte[dataLength];
int nbrDataRead = _serialPort.Read(data, 0, dataLength);
RxString = Encoding.ASCII.GetString(data);
txtCod.AppendText(RxString);
if (RxString.IndexOf((char)end) > -1)
{
this.Invoke(new EventHandler(Final));
}
I create small test app to test connection between app and some device which measure temperature. When I write some command to device it's ok, but when device return me response, a string, for that I use ReadExisting() method. But app reads just first character of the string. If I send command again, it's same situation. I try to test connection with program called Terminal.exe, it's happens the same. But when I change BaudRate at some value and return BaudRate on 9600 ( it's ok rate ), then it's worked fine. Also I try to change BaudRate in my app, but it give me the same, just first character of string. Also I have an app written in LabView which works fine.
Also I tested my app with another PC with Terminal.exe , and it's worked fine.
private void SetUpPort()
{
port = new SerialPort();
port.PortName = port_name;
port.BaudRate = 9600;
port.Parity = Parity.None;
port.DataBits = 8;
port.StopBits = StopBits.One;
port.Handshake = Handshake.None;
port.ReadTimeout = 1000;
p.DataReceived += new SerialDataReceivedEventHandler(PortDataReceived);
}
private void PortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
recived_data += port.ReadExisting();
}
I would be very thankful for any help.
That is not how to use the SerialPort component. You need to attach the DataReceived event handler, which is called every time data comes in on the serial port. An example is found on the page I linked.
You need to append data until you know you're done! You can't just call ReadExisting and assume you get all the information. Do something like this:
private string inBuffer = String.Empty;
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
inBuffer += indata;
if (<inBuffer fulfills some criteria that tells you it's a complete message>)
{
ProcessInBuffer(inBuffer);
inBuffer = String.Empty;
}
}
It is your task to determine the criteria to fulfill. This may be that the received data as a certain length (if the records are fixed length), or maybe they end with newline characters or something else.
you will use datareceived event to store all the data in a array or string and after you can use every data of that string indata += sp.ReadExisting();