Serial Port does not receive whole data in C# - c#

I am using C# serial port controlling gsm modem. Now in Mikroelectronia USART Terminal
after sending:
AT+CUSD=1,"*778#",15
It receives:
AT+CUSD=1,"*778#",15
OK
+CUSD: 0,"Balance: 0.00 TK. Validity: 29-Jul-13. Bonus: 0.00TK. Free Min: 0. Dial *121*2092# for 3 hit songs of Ashiqui-2 as ur Caller
Tunetk.10",64
But in C# after sending data
AT+CUSD=1,"*778#",15
it returns:
AT+CUSD=1,"*778#",15
OK
+CUSD: 0,"Balance: 0.00 TK. Validity: 29-Jul-13. Bonus: 0.00TK. Free Min: 0. Dial *121*2092# for 3 hit songs of Ashiqui-2 as ur Caller Tune
That means in C# it does not receive any data after "Caller Tune". Why is that happening? Part of C# code I have used is
private void Form1_Load(object sender, EventArgs e)
{
sp1.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
}
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var valueOfPort = sp1.ReadExisting();
textBox1.AppendText(valueOfPort);
}
private void button1_Click(object sender, EventArgs e)
{
TextBox.CheckForIllegalCrossThreadCalls = false;
try
{
if (!sp1.IsOpen)
{
sp1.Open();
}
sp1.Write(textBox2.Text+"\r");
}
catch(Exception ex)
{
MessageBox.Show(string.Format("Exception : {0}", ex.Message), "Port Error");
}
}

the TextBox.CheckForIllegalCrossThreadCalls = false; makes me very suspicious, I think that is what is breaking your program, simply just invoke to update the text box properly and I think it will work. Just change the event code to the following:
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new SerialDataReceivedEventHandler(sp_DataReceived), sender, e);
}
else
{
var valueOfPort = sp1.ReadExisting();
textBox1.AppendText(valueOfPort);
}
}
What this will do is check if you are running on the correct thread, if not it restarts the function again on the UI thread with the same arguments. Also be sure to remove the TextBox.CheckForIllegalCrossThreadCalls = false;
UPDATE: After re-reading the MSDN I found this remark
This method returns the contents of the stream and internal buffer of the SerialPort object as a string. This method does not use a time-out. Note that this method can leave trailing lead bytes in the internal buffer, which makes the BytesToRead value greater than zero.
Try checking to see if there is more data to read after calling ReadExisting.
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new SerialDataReceivedEventHandler(sp_DataReceived), sender, e);
}
else
{
while(sp1.BytesToRead > 0)
{
var valueOfPort = sp1.ReadExisting();
textBox1.AppendText(valueOfPort);
}
}
}

Have you defined your device's EOL and set it correctly in your program?
C# serial port does not necessarily fire up DataReceive at the start of the message nor does it know where the message ends. It may terminate anywhere in the message. You should try explicitly set the EOL which can be uniquely identified by the port in order for it to buffer incoming message and to return it once it is completed.
// e.g. I normally used (carriage return + newline) instead of just either one
sp1.NewLine = "\r\n";

Related

How to check output from the serial port match a message to continue write a command in C#?

I use this code to initialize a serial port in C# :
serialPort.PortName = cboCOMPort.Text;
serialPort.BaudRate = Convert.ToInt32(cboBaudRate.Text);
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
serialPort.Handshake = Handshake.None;
serialPort.WriteTimeout = 500;
serialPort.ReadTimeout = 500;
serialPort.DataReceived += new SerialDataReceivedEventHandler(SerialDataReceived);
serialPort.Open();
then use this code to read data from serial port :
private void SerialDataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort = (SerialPort)sender;
strDataReceived = serialPort.ReadExisting();
ShowSerialOutput(strDataReceived);
}
and write :
private void SerialDataSend(string strCommand)
{
serialPort.WriteLine(strCommand);
}
My problem : when a device (example a switch) connects to the serial port and executes a command I have written, my program has to wait for the switch finishes executing this command before writing a new command to the serial port. How to check if the switch is finished? (when finished, the switch will send some message contain the keywords like 'completed', 'finished',...). I have tried to use this code but not work :
while(true)
{
if(serialPort.ReadLine().Contains("completed"))
serialDataSend(nextCommand);
}
Sorry for not clearly explaining my problem.
**Example my problem : **
I connect to the switch using serial port and use my program to read/write data. I want to copy a large file from server to the switch using this command : cp tftp://10.0.0.1/file.tgz /var/tmp/file.tgz. I use my program to write this command to the serial port and the switch executes this command. The file is very large so the program need to wait the file copied completely before sending the next command. When finished, the switch show message "Completed". That is my problem : how to check the copy process completed to write the new command.
I will try to approach your problem. Probably will need to rephrase as more information might come from you :)
As I understand it you have more than 1 command that you are sending to the device. Something like a command-list.
As you already have the event for data reception you could also use it to verify whether the completed keyword has been send and set a flag.
EDIT: The flag you can use for the while-wait-loop as you already do.
Your program will wait there until your device confirms the completion and jump out of the loop. Then you need to reset the flag for the next waiting loop.
public class DeviceCommunication
{
bool FlagToProceed = false;
private void MainJob()
{
// send command 1...
SerialDataSend("cp tftp://10.0.0.1/file.tgz /var/tmp/file.tgz.");
// wait
while(!FlagToProceed)
{}
// reset the flag
FlagToProceed = false;
// send command 2 ...
SerialDataSend("WhatEverComesNext");
// wait
while(!FlagToProceed)
{}
// reset the flag
FlagToProceed = false;
}
private void SerialDataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort = (SerialPort)sender;
strDataReceived = serialPort.ReadExisting();
ShowSerialOutput(strDataReceived);
// check whether affirmation has been received and open the gates
if(strDataReceived.Contains("completed"))
{
FlagToProceed = true;
}
}
private void SerialDataSend(string strCommand)
{
serialPort.WriteLine(strCommand);
}
}
This waiting technique is not a very good one! It is just the closest to your posted code. If you want to use it, I would suggest to have a timer running and break out of the while - loop if timeout is reached. Otherwise you might keep stuck in the while loop if the device decides not to confirm with "complete" or data just gets lost during transmission.
Another possibility to solve your problem could be to apply an asynch / await approach.
EDIT: be ware of case sensitivity! "Complete" & "complete" is not the same for the Contains method!
You need to use a state machine and delegates to achieve what you are trying to do. See the code below, I recommend doing all this in a separate thread other then Main. You keep track of the state you're in, and when you get a response you parse it with the correct callback function and if it is what you are expecting you move onto the next send command state.
private delegate void CallbackFunction(String Response); //our generic Delegate
private CallbackFunction CallbackResponse; //instantiate our delegate
private StateMachine currentState = ATRHBPCalStateMachine.Waiting;
SerialPort sp; //our serial port
private enum StateMachine
{
Waiting,
SendCmd1,
Cmd1Response,
SendCmd2,
Cmd2Response,
Error
}
private void do_State_Machine()
{
switch (StateMachine)
{
case StateMachine.Waiting:
//do nothing
break;
case StateMachine.SendCmd1:
CallbackResponse = Cmd1Response; //set our delegate to the first response
sp.Write("Send first command1"); //send our command through the serial port
currentState = StateMachine.Cmd1Response; //change to cmd1 response state
break;
case StateMachine.Cmd1Response:
//waiting for a response....you can put a timeout here
break;
case StateMachine.SendCmd2:
CallbackResponse = Cmd2Response; //set our delegate to the second response
sp.Write("Send command2"); //send our command through the serial port
currentState = StateMachine.Cmd2Response; //change to cmd1 response state
break;
case StateMachine.Cmd2Response:
//waiting for a response....you can put a timeout here
break;
case StateMachine.Error:
//error occurred do something
break;
}
}
private void Cmd1Response(string s)
{
//Parse the string, make sure its what you expect
//if it is, then set the next state to run the next command
if(s.contains("expected"))
{
currentState = StateMachine.SendCmd2;
}
else
{
currentState = StateMachine.Error;
}
}
private void Cmd2Response(string s)
{
//Parse the string, make sure its what you expect
//if it is, then set the next state to run the next command
if(s.contains("expected"))
{
currentState = StateMachine.Waiting;
backgroundWorker1.CancelAsync();
}
else
{
currentState = StateMachine.Error;
}
}
//In my case, I build a string builder until I get a carriage return or a colon character. This tells me
//I got all the characters I want for the response. Now we call my delegate which calls the correct response
//function. The datareceived event can fire mid response, so you need someway to know when you have the whole
//message.
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string CurrentLine = "";
string Data = serialPortSensor.ReadExisting();
Data.Replace("\n", "");
foreach (char c in Data)
{
if (c == '\r' || c == ':')
{
sb.Append(c);
CurrentLine = sb.ToString();
sb.Clear();
CallbackResponse(CurrentLine); //calls our correct response function depending on the current delegate assigned
}
else
{
sb.Append(c);
}
}
}
I would put this in a background worker, and when you press a button or something you can set the current state to SendCmd1.
Button press
private void buttonStart_Click(object sender, EventArgs e)
{
if(!backgroundWorker1.IsBusy)
{
currentState = StateMachine.SendCmd1;
backgroundWorker1.RunWorkerAsync();
}
}
Background worker do work event
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
if (backgroundWorker1.CancellationPending)
break;
do_State_Machine();
Thread.Sleep(100);
}
}

serial port myPort.Datareceived C#

Hello friends have a form in C # that reads data from a serial device connected, my problem is that I even changing form of the method myPort.DataReceived still running and receiving data. There's no way I close the connection with the serial port because the method does not stop excutar. I've tried a command to zip it when I change my form but it crashes when you try to run the myPort.Close, I believe that is why the myPort.DataReceived still running, so I removed the code and it continues myPort.Close open in another form. I think my solution would be to stop the myPort.DataReceived to then close connection, but can not find way to do this.Below is an excerpt from my code:
namespace EntradaFinalCliente
{
public partial class ConsultaSerial : Form
{
string SerialString;
private SerialPort myport;
public ConsultaSerial()
{
InitializeComponent();
abrirSerial();
lerDados();
}
public void abrirSerial()
{
myport = new SerialPort();
myport.BaudRate = 9600;
myport.PortName = SerialPort1;
myport.DataReceived += myport_DataReceived;
}
private void lerDados()
{
if (myport.IsOpen == false)
{
try
{
myport.Open();
}
catch
{
return;
}
}
}
private void myport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
SerialString = myport.ReadExisting();
this.Invoke(new EventHandler(Analisa));
}
private void Analisa(object sender, EventArgs e)
{
checarSerial();
}
And this is my closing the form button:
private void button1_Click (object sender, EventArgs e)
{
myPort.Close ();
this.Hide ();
var form1 = new Form1 ();
form1.Closed + = (s, args) => this.Close ();
Form1.Show ();
}
The issue you have it that once the event has been triggered, your application would have entered the function myport_DataReceived. The function will continue to execute regardless of whether the port has been closed. If the port has been closed, the function would execute for the last time. Waiting for 100ms makes it worse. So my advice is to remove the wait and put a try catch statement around the code to make the thread terminate cleanly.
Furthermore, it is better if you use the sender to read the incoming data than using the member myPort because the sender is the one that fires the event. It also helps to remove confusion when you open two or more ports.
It is also advised that the body of DataReceived event handler function should be kept to minimum. Only do what you need to get the data out. You can then store the data in memory and do more complicated handling somewhere else using the stored data.
private void myport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100); // Why do you need to wait for 100 ms? If the event reaches here, it will have the data to read. Can remove?
try
{
SerialPort sp = (SerialPort)sender;
SerialString = sp.ReadExisting();
this.Invoke(new EventHandler(Analisa));
}
catch (Exception ex)
{
// Do something else
Console.WriteLine(ex.Message);
}
}

Why I get data after closing COM port?

I have methos that recieve data from opening COM port:
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
try
{
if (sp.IsOpen)
{
if (sp.BytesToRead > 0)
{
// Get data
}
}
}
}
Also I have method that does connection to COM port:
private void connectPort()
{
SerialPort mySerialPort = new SerialPort(port);
...
}
When I call method that closes port:
mySerialPort.DiscardInBuffer();
mySerialPort.DiscardOutBuffer();
mySerialPort.Close();
After I get data from device still. What is wrong?
I don't know for sure, but from the docs it sounds like the fact that the data is being raised from another thread may be buffering and/or lagging a bit behind the actual data (plus it's possible for you to receive data between when you've discarded the buffer and when you close it).
I'd probably unhooking the DataReceivedHandler first, then close the connection, finally discard the data, ex.
mySerialPort.DataReceived -= new SerialDataReceivedEventHandler(DataReceivedHandler);

Serial port auto close hangs

I'm trying to open and close a serial port with one button click event. But it always hangs whenever it hits the serialport.close part. Why?
private void btn_auto_Click(object sender, EventArgs e)
{
try
{
myport = new SerialPort();
myport.BaudRate = 9600;
myport.PortName = cb_portname.Text;
myport.Open();
myport.DataReceived += new SerialDataReceivedEventHandler(myport_DataReceived2);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error");
}
}
}
void myport_DataReceived2(object sender, SerialDataReceivedEventArgs e)
{
try
{
in_data = myport.ReadLine();
this.Invoke(new EventHandler(displaydata_event2));
}
catch (Exception)
{
}
}
private void displaydata_event2(object sender, EventArgs e)
{
string inStr;
inStr = in_data;
if (inStr.Length == 18)
{
int indexOfSpace = inStr.IndexOf(':');
string Temp = inStr.Substring(indexOfSpace + 1);
txtData2.Text = Temp;
}
if (txtData2.Text != "")
{
myport.Close(); //===== ALWAYS HANGS AT THIS PART =====
MessageBox.Show("STOPPED");
}
}
So, it always hangs under the if txtData2 not equals part.
Is it due to it requires a button action for a serialport to close and it cannot auto close? Thanks in advance.
Looking at the source code for the SerialPort class, and in particular for its associated SerialStream class, it appears that the Close() method will block waiting for handlers of any raised events to complete.
Your handling of the received data seems a bit suspect in any case, in that you only even bother to look at the received data if the received line is exactly 18 characters long, as well as in that you are using an instance field to pass data between two methods (very bad idea).
But most likely the biggest issue here, the one causing the deadlock, is that you are calling the SerialPort.Close() method before the DataReceived event handler has completed. Don't do that. Fix your code so that handling received data is a completely independent operation from actually closing the serial port, so that the former can complete before you attempt the latter.

C# - How to read from a SerialPort for an infinite amount of time

I have an arduino connected to my serialport, which generates numbers from 0 to 64 all the time.
I wanted to read these signals in c# and managed to attach them to a richtextbox.
Unfortunately at some point they stop being written in the box and i have to open the port again to append the text again to the box.
Here is a sample of the code:
private void btnOpenPort_Click(object sender, EventArgs e)
{
if (Arduino.IsOpen == false)
{
Arduino.BaudRate = 115200;
Arduino.PortName = cBPortWaehlen.SelectedItem.ToString();
Arduino.Open();
}
while (Arduino.BytesToRead != 0)
{
richTextBox1.AppendText(Arduino.ReadExisting());
}
}
I assumed the statement Arduino.BytesToRead would never turn false, as long as my arduino sends signals, but this seems not to be the case. How can I achieve that instead?
First of all, about any serial connection made in C# has a default event handler, called DataReceived. I believe that you can use it, and delete the while code block you have there.
Second, I think that the while block is too operation-intensive, so if you don't go with my first suggestion, try and place a Thread.Sleep(1000) inside your while, so it won't execute that many times. If you want to refresh the data every few milliseconds, replace Thread.Sleep(1000) by giving the amount of milliseconds that your prefer.
Hope this answered your question.
Later Edit:
The code you can have looks like this:
public void OpenArduinoConnection()
{
if(!arduinoBoard.IsOpen)
{
arduinoBoard.DataReceived += arduinoBoard_DataReceived;
arduinoBoard.PortName = "yourportname";
arduinoBoard.Open();
}
else
{
throw new InvalidOperationException("The Serial Port is already open!");
}
}
void arduinoBoard_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// your code here
}

Categories