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.
Related
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();
}
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)
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
}
}
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.
This question already has answers here:
Problem with serial port data receive in C#
(6 answers)
Closed 5 years ago.
I'm attempting to write a c# program in Visual Studio 2010 that communicates with a micro controller via a serial connection. I can read and write to the port just fine, I am just unsure of how to have the send method wait until all of the data from the previous send command has been received before it executes. I have implemented the data received event handler such that it determines when the proper amount of data that had been requested has been received on the serial port. I just need to know how to cause that to tell the send method the port is free.
I had planned on using a mutex, but I believe the problem is not due to multi-threading. The same thread is sending strings out on the serial port one after another and the data being received in response to the first request is conflicting with the second request.
Also, if the communication is being done by one thread, would having that thread wait cause the data received event handler to not execute?
(both methods are in the same class)
My send data method:
//method for sending data
public void send(String s)
{
sp.Write(s + "\r");
}
My data received event handler:
//data received event handler
private void dataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string tmp;
tmp = sp.ReadExisting();
//put all received data into the read buffer
readBuffer += tmp;
//add various flag checks to determine end of transmission
if (firmwareFlag)
{
if (readBuffer.EndsWith("\n"))
{
firmwareFlag = false;
//trigger event to tell object owner data is ready
dataReady(this, new CustomEventArgs(readBuffer, "firmware"));
readBuffer = "";
}
}
else if (parameterFlag)
{
if (System.Text.RegularExpressions.Regex.IsMatch(readBuffer, "K90", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
{
parameterFlag = false;
dataReady(this, new CustomEventArgs(readBuffer, "parameters"));
readBuffer = "";
}
}
else
{
dataReady(this, new CustomEventArgs(readBuffer, null));
readBuffer = "";
}
}
I would use just a bool variable in the class, and a timer. When timer ticks, you check that bool and send the data if it is allowed.
boolean isAllowed;
DispatcherTimer tmr1;
//...etc
I have the same problem on the past and I solved this way:
I added SendAndWaitResponse method which receives the buffer to be sent by the port, the expected lenght of the response, a timeout in seconds(just in case), and a callback, when the expected response has been received:
//System.IO.Ports.SerialPort port;
//port.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived);
void SendAndWaitResponse(String buffer, int expectedLenght, int timeoutInSeconds, Action<String> callback)
{
TimeSpan _timeout = new TimeSpan(0, 0, 0, timeoutInSeconds);
//may not be necesary
if (!string.IsNullOrEmpty(buffer) && buffer != "" && port != null)
{
//Remove current dataReceived event handler, as we will manually manage it for now, will restore later!
this.port.DataReceived -= port_DataReceived;
if (!this.port.IsOpen) this.port.Open(); // open the port if it was closed
this.send(buffer); // send buffer, changed port.write(buffer) so it now usses your own send
DateTime startToWait = DateTime.Now; //start timeout
bool isTimeout = false;
int totalRead = 0;
String read = "";
while (!(isTimeout) && totalRead < expectedLenght)
{
do
{
if (port.BytesToRead > 0)
{
read += (char)this.port.ReadByte();
totalRead++;
//you can add a System.Threading.Thread.Sleep(int); if all bytes come in parts
}
} while (port.BytesToRead > 0);
isTimeout = (DateTime.Now.Subtract(_timeout) >= startToWait);
}
this.port.DataReceived += port_DataReceived; // restore listener
callback(read); //invoke callback!!!
}
}
If you want to check if there are bytes with out send to the device (bytes in write buffer), so you can read BytesToWrite property.
SerialPort.BytesToWrite Property