Working with SerialPort´s DataReceived Event - c#

Im trying to read the output of a device on a COM port on my PC.
Im wrote a C# program to do so.
Using PuTTY, I can see the output Im expecting from my device.
The problem is that the function SerialPort.ReadExisting(); in my DataReceived function gives my a completely different string.
What is the proper way to read from a COM port using SerialPort?
Also, the strings I get from SerialPort.ReadExisting(); are fragments of the string I sent to the device.
Below is the code that initializes the SerialPort.
SerialPort port;
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = port.ReadExisting();
}
void init_serialport(object sender, EventArgs e)
{
if (port != null)
{
port.Close();
}
port = new SerialPort( /* ... */ );
port.BaudRate = 9600;
port.Parity = Parity.None;
port.DataBits = 8;
port.StopBits = StopBits.One;
port.Handshake = Handshake.RequestToSend;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
try
{
port.Open();
}
catch (Exception ex)
{
// ...
}
}

the strings I get from SerialPort.ReadExisting(); are fragments of the string I sent to the device.
I'd have a look at SerialPort.ReceivedBytesThreshold.
"Gets or sets the number of bytes in the internal input buffer before a DataReceived event occurs."

I would first take a look at the Read method of the port object, look at the raw bytes and verify that they match your expectations, which would then narrow down the problem to the encoding on the conversion to string.
More information on this is provided here.

You received fragments because SerialPort.Existing() executes and completes in less time then it takes for your sending device to send the entire string.
You need to repeat the call continuously or until you received the end of string character if the string has one.

Related

Problem with sending data from c# to arduino for SIM800L

I think I missed something in my code.
Below is my code in C#
public Form1()
{
InitializeComponent();
serialPort1.PortName = "COM2";
serialPort1.BaudRate = 9600;
serialPort1.Parity = Parity.None;
serialPort1.DataBits = 8;
serialPort1.StopBits = StopBits.One;
serialPort1.Handshake = Handshake.RequestToSend;
serialPort1.DtrEnable = true;
serialPort1.RtsEnable = true;
serialPort1.NewLine = System.Environment.NewLine;
}
private void btnSend_Click(object sender, EventArgs e)
{
serialPort1.Open();
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
try
{
string num = "+639952006630\n";
serialPort1.Write(num);
string message = "Your child arrived at our school at " + DateTime.Now + ".";
serialPort1.Write(message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Below is my code in Arduino for sending data using GSM module SIM800L
#include <SoftwareSerial.h>
//SIM800 TX is connected to Arduino D8
#define SIM800_TX_PIN 8
//SIM800 RX is connected to Arduino D7
#define SIM800_RX_PIN 7
//Create software serial object to communicate with SIM800
SoftwareSerial serialSIM800(SIM800_TX_PIN,SIM800_RX_PIN);
void setup() {
//Begin serial comunication with Arduino and Arduino IDE (Serial Monitor)
Serial.begin(9600);
while(!Serial);
//Being serial communication with Arduino and SIM800
serialSIM800.begin(9600);
delay(1000);
//Set SMS format to ASCII
serialSIM800.write("AT+CMGF=1\r\n");
delay(1000);
//getting the number
char remoteNum[20]; // telephone number to send sms
readSerial(remoteNum);
//Send new SMS command and message number
serialSIM800.print("AT+CMGS=\"");
serialSIM800.print(remoteNum);
serialSIM800.print("\"\r\n");
delay(1000);
// getting sms text
char txtMsg[200];
readSerial(txtMsg);
//Send SMS content
serialSIM800.print(txtMsg);
delay(1000);
//Send Ctrl+Z / ESC to denote SMS message is complete
serialSIM800.write((char)26);
delay(1000);
Serial.println("SMS Sent!");
}
/*
Read input serial
*/
char readSerial(char result[]) {
int i = 0;
while (1) {
while (Serial.available() > 0) {
char inChar = Serial.read();
if (inChar == '\n') {
result[i] = '\0';
Serial.flush();
return 0;
}
if (inChar != '\r') {
result[i] = inChar;
i++;
}
}
}
}
void loop() {
}
My confusion/question here is
Whenever I test it using Serial Monitor in Arduino, the code in Arduino sends message to the cellular number successful. But when I use a form in Visual Studio using C#, nothing happens. There are also no errors that appear. I tried F11 also to know if I am missing an error, still I see nothing. But the application does not send SMS to the number.
Help from you guys is very much appreciated. Thank you in advance.
The solution is as obvious as simple. You have all your routines in the Arduino setup which runs only once. So sending one sms from Arduino directly works exactly 1 time.
When connecting via c code, the Arduino is already booted and in the EMPTY loop. So the chance to hit the one time run code is zero.
Put your routines especially
readSerial(remoteNum);
in the loop and it should work as expected.
The first thing you should do before sending the commands with your Visual Studio is that the serialport is correctly configured, you simply have the serial port closed loop, everything you send should receive, If you do not receive anything you have a serial port configuration problem

AT Command delivery message c#

I have created winform application to send SMS using USB modem, it working properly but I want to get the delivery message and confirm the message has been sent correctly.
Here is my program
private void button1_Click(object sender, EventArgs e)
{
try
{
SerialPort sp = new SerialPort();
sp.PortName = textBox1.Text;
sp.Open();
sp.WriteLine("AT" + Environment.NewLine);
Thread.Sleep(100);
sp.WriteLine("AT+CMGF=1" + Environment.NewLine);
Thread.Sleep(100);
sp.WriteLine("AT+CSCS=\"GSM\"" + Environment.NewLine);
Thread.Sleep(100);
sp.WriteLine("AT+CMGS=\"" + mobile + "\"" + Environment.NewLine);
Thread.Sleep(100);
sp.Write(message);
Thread.Sleep(100);
sp.Write(new byte[] { 26 }, 0, 1);
Thread.Sleep(100);
var response = sp.ReadExisting();
if (response.Contains("ERROR: 500"))
{
MessageBox.Show("Please check credits");
}
sp.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
Please help me how to read the delivery status with above code
This question is not C# specific; rather it's an AT commands question.
After sending a SMS, you'll get a response from modem, something likee this:
+CMGS: {sms id, 0 to 255}
OK
In this case, if Service Center has delivered the SMS successfully, modem will return this response:
+cds: {some id which does not matter} {PDU status report}
You need just decode this PDU to obtain status report, ID of original SMS and other useful data. If the ID of the sent SMS and the one from status report are equal, you have status report exactly for your message.
Note: if you remove message from modem's storage before receiving of delivery report, you'll get report which will be containing all usual information, but status of delivery will be most likely 71 instead of 0.
I used this approach myself, based on this answer, and it works.
Edit 1 :
you are handling the RS232 reading synchronously, which i dont really recomand,the reading function should be fired automatically when the data are available in the port,something like that :
private string SerialDataReceived = string.Empty
private void button1_Click(object sender, EventArgs e)
{
// new instance of the COM port
port = new SerialPort(ComPort, 115200, Parity.None, 8, StopBits.One);
// start port lessener
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
// Begin communications and wait for nad to reboot completly
port.Open();
//send your AT Commands
port.Write("ATE0\r\n");
// check the response 'if Command is successfull it reply with something +Ok'
if(SerialDataReceived.ToLower().Contains("ok"))
}
//event will be fired each time a new data are available in the port
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// Show all the incoming data in the port's buffer
SerialDataReceived += port.ReadExisting();
}
now in your send function you should check if you have in the end a response containing +CMGS:,
var response = sp.ReadExisting();
while (!response.Contains("OK") && !response.Contains("ERROR"))
{
// check if message has been sent before exiting
response = sp.ReadExisting();
}

Event handler for read serial port never triggers

Goal:
Trying to write a string to a serial port, read it, then print it to console
Code:
// for waiting until event is detected
private static ManualResetEvent waitHandle = new ManualResetEvent(false);
public Driver()
{
// create new serial port
comPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
// add event handler
comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
// configure port
comPort.DtrEnable = true;
comPort.RtsEnable = true;
comPort.ReadTimeout = 3000;
// open port
comPort.Open();
// send string through port
string command = "test \n";
byte[] MyMessage = System.Text.Encoding.UTF8.GetBytes(command);
comPort.Write(MyMessage, 0, MyMessage.Length);
// wait until event is detected
waitHandle.WaitOne();
}
private void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// Write data to buffer and stop wait
Console.WriteLine(comPort.ReadExisting());
waitHandle.Set();
}
Issue:
Write to serial seems to work fine (confirmed by using Serial Port Monitor) but "comPort_DataReceived" never gets called
If I change my code and add
while(true)
{
Console.WriteLine(comPort.ReadExisting());
}
Right after the "comPort.Write(MyMessage, 0, MyMessage.Length);" line so that I'm polling instead of waiting for the event handler then a whole lot of nothing gets written
If I try polling this way
while (true)
{
Byte[] buf = new Byte[2048];
comPort.Read(buf, 0, 2048);
Console.WriteLine(buf.ToString());
}
It just times out (System.TimeoutException: 'The operation has timed out.'
).
I'm not sure where I am going wrong/why I am unable to read from the serial port
Ok, from what I see it looks like there is no device listening on serial port. Then, if you write something to serial port it does not mean that the same data will occur as a received data. This data is outgoing data. If you want to receive data there must be another device connected to that serial port and sending data as a response to your data written.
Turns out it was a hardware issue (no device was writing to serial port) coupled with a misunderstanding (thinking I could write to a serial port and then read what I wrote from within the same program)

C# - Example for Handshaking with SerialPort class

I'm currently working on a project which requires me to communicate with an MBED through serial ports.
I've read the msdn site but I don't understand.
Can anyone teach me or show examples of how to use SerialPort.Handshake (XonXoff or RequestToSend)?
My main request how to set: If C# reads a certain string, then send data.
First of all i think that Handshaking is not what you need. Handshaking is a low level communication, to control when to send data or not. more about handshaking
My main request how to set: If C# reads a certain string, then send
data.
What you need is a continuous data reading or an event, and after certain string is read - to send some data.
Let me find some examples.
void connect(){
_port = new SerialPort();
// set port parameters
_port.DataReceived += portDataReceived; //set the data received event
_port.Open();
}
void portDataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = _port.ReadExisting();
if(data.Contains("data-start")) //check for the start data
{
CreateReply(); // your reply
}
}
You can read the data in a while loop, but take a note that an infinite loop executed in the main thread will block your program from responding.
void connectAndRead(){
_port = new SerialPort();
// set port parameters
_port.Open();
_stopReading = false;
var dataBuffer = "";
while(!_stopReading)
{
dataBuffer += _serialPort.ReadLine();
if(!dataBuffer.Contains("data-start")) //check for the start data
continue;
CreateReply(); // your reply
_stopReading = true; // exit loop
}
}

Serial port reading

I am trying to read 3 temperature devices using WinForms and the Modbus 485 protocol.
Basically I have to periodically write a command to each device, wait for response and when I get the response, process it. Each device has a unique communication adress. In order to periodically send the command I am using a timer.Timer1.interval=100;
This is how I am sending the command and where I am processing the response:
private void ProcessTimer_Tick(object sender, EventArgs e)
{
switch (tempState)
{
case TempTimerState.sendCommDevice1:
if (!tempSerial.IsOpen)
{
tempSerial.Open();
}
tempSerial.DiscardInBuffer();
communication.tempCommand[0] = 0x01; //device adress
communication.tempCommand[6] = 0xA5; //CRC
communication.tempCommand[7] = 0xC2; //CRC
tempSerial.Write(communication.tempCommand, 0, 8);
tempState = TempTimerState.recievedDevice1;
communication.waitTime = 0; //time to wait before throw a timeout exception
communication.dataRecievedTemp = false; //flag for response recieved
break;
case TempTimerState.recievedDevice1:
communication.waitTime++;
if (communication.dataRecievedTemp)
{
communication.waitTime = 0;
if(CheckCRC(communication.tempResponse)) //CRC checking
{
//process response
}
else
{
//handle CRC Failure error
}
}
if(commcommunication.waitTime>=maxWaitTime)
{
//handle Timeout exception
}
tempState=TempTimerState.sendCommDevice2;
break;
}
}
and so on for each device. This is my serialport data recieved event:
private void tempSerial_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
sp.Read(communication.tempResponse, 0, sp.BytesToRead);
communication.dataRecievedTemp = true; //flag for data recieved
}
So my communication should be :
send command device1
recieve response device1
send command device2
recieve command device2
send command device3
recieve command device3
and then send command device1 again. The problem is that I get sometimes get communication timeout error and I know for sure that all the devices are responding very quick and every time. Since I had preset the sp.ReceivedBytesThreshold=8I started to get CRC errors too. My response should always be 8 bytes long.
I think the problem is in the serial port data recieved event, but I can't see what's the problem.
P.S. I have also tried to set the timer interval to 1000 miliseconds, but that didn't solve my problem
Relying on ReceivedBytesThreshold is very brittle, the show is over when you get out of sync once. Your code is also very vulnerable to other reasons that DataReceived may fire, you are not checking the e.EventType property. Which certainly can be SerialData.Eof for a binary protocol.
Just write robust code that doesn't depend on the EventType nor the number of available bytes. Like this:
private byte[] rcveBuf = new byte[8];
private int rcveLen;
private void tempSerial_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
rcveLen += sp.Read(rcvebuf, rcveLen, rcveBuf.Length - rcveLen);
if (rcveLen == rcveBuf.Length) {
Array.Copy(rcveBuf, communication.tempResponse, rcveBuf.Length);
communication.dataRecievedTemp = true;
rcveLen = 0;
}
}
And reset rcveLen back to zero on a timeout. And make sure that the timeout isn't too low, you can lose many seconds if your program got swapped out, use 10 seconds to be safe.

Categories