So I'm trying to read data from a COM port that is connected to my laptop through USB. I know that the connection is successful and that the port is opened when calling on the Connect() method, however when I send a command to the port, it sends to the port, but once ReadLine() is called, it just hangs out in the terminal doing nothing. I've done some research on this issue and I see that many people have issues with SerialPort, but I can't seem to solve my own instance of the problem.
An example of a command I might send to the machine I'm working with is something as simple as "son" where I send the string to the port and the shaker device should simply turn on and start shaking. So I should send this command to the port and receive something back like "ok" to show that it is running correctly, but right now nothing happens once the code reaches the ReadLine().
Can someone please help me walk through where I might be going wrong in trying to read data from this port? Any help would be much appreciated.
private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = Port.ReadExisting();
}
public void Connect()
{
//MessageBox.Show(Port.IsOpen.ToString());
Port = new SerialPort(Comport, 9600, Parity.None, 8, StopBits.One);
Port.DtrEnable = true;
Port.DataReceived += SerialPortDataReceived;
Port.Open();
MessageBox.Show(Port.IsOpen.ToString());
Port.NewLine = Environment.NewLine;
}
public string SendCommand(string command)
{
Port.Write(command + "\r");
string result = Port.ReadLine().Trim();
if (result == "e")
{
string errorList = GetErrorList();
throw new InvalidOperationException("BioShake error: " + errorList);
}
return result;
}
I've been currently working on a .net project that sends SMS messages through a GSM Modem Device. I can manage to send a single SMS message just fine, but when I start to loop the AT Commands to send SMS to multiple recipients, the behaviour of the application gets very clunky. Sometimes it could only send an SMS to the first recipient it could find, most of the times none at all.
Below is the source code for my method on sending SMS to multiple recipients:
String MessageBody = String.Format("Everyone,\n\nEquipment Current Reading: {0} tph\nCurrent Status: {1}\n\n".Replace("\n",Environment.NewLine), CurrentValue, EqValue);
SerialPort SP = new SerialPort();
SP.PortName = "COM3";
using (TestEntities TE = new TestEntities())
{
List<vwRecipient_MasterList> RecipientList = TE.vwRecipient_MasterList.Where(r => r.Group_id == R_Group).Select(r => r).ToList();
foreach (vwRecipient_MasterList Recipient in RecipientList)
{
SP.Open();
String formattedRecipientNo = Char.ConvertFromUtf32(34) + Recipient.MobileNo + Char.ConvertFromUtf32(34);
SP.Write("AT+CMGF=1" + Char.ConvertFromUtf32(13));
SP.Write("AT+CMGS=" + formattedRecipientNo + Char.ConvertFromUtf32(13));
SP.Write(MessageBody + Char.ConvertFromUtf32(26) + Char.ConvertFromUtf32(13));
SP.Close();
}
}
So I did a bit more research on how to utilize the serial port class based on the MSDN reference (for serial ports) and a few articles around here at SO, and I've come up with this unorthodox solution that involves using the SerialDataReceivedEventHandler provisioned by the SerialPort class and an infinite while loop.
First of all, I created two properties in the class scope that is visible for both methods of the SendSMS(WebMethod) and the DataRecieved(Event):
// Holds the text output from the SerialPort
public string spReadMsg { get; set; }
// Used as a fail-safe terminator of the infinite loop used in the Web Method.
public DateTime executionTime { get; set; }
The following is the DataRecievedHandler event. Basically, all this event does is store the text response from the SerialPort in the spReadMsg property for the SendSMS method
private void DataRecievedHandler(object sender, SerialDataReceivedEventArgs e)
{
try
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
Debug.Print("Data Received:");
Debug.Print(indata);
// Store to class scope property spReadMsg for the sendSMS method to read.
spReadMsg = indata;
}
catch (Exception ex)
{
Debug.Print(ex.Message);
}
}
Finally, I added a few lines on my Web Method to listen to the DataRecieved event whenever the desired response for SMS Message has been sent successfully.
According to this article on using AT commands for Modem Devices: http://www.smssolutions.net/tutorials/gsm/sendsmsat.
The SerialPort should return a +CMGS: # response to identify that message sending has been completed.
So All I have to do is wait for a +CMGS: response which will let the program know that the message has been sent and is ready to send the next message to the next recipient. I made a makeshift listener for the web method using an infinite while loop that terminates once the response +CMGS: has been read from the serial port or when the it takes longer than 30 seconds to get the desired response.
[WebMethod]
public void sendSMSMessage_inHouse(String Recipients, String MessageBody)
{
String sanitizedRecipient = Recipients.Replace(" ", "");
var RecipientList = Recipients.Split(',').ToList();
String sanitizedMessage = MessageBody.Replace(#"\n", Environment.NewLine);
SerialPort SP = new SerialPort();
SP.PortName = "COM3";
SP.DataReceived += new SerialDataReceivedEventHandler(DataRecievedHandler);
SP.Open();
// Initally set property to the "condtion" value to allow first message to be
// run without the datarecieved response from the serial port
spReadMsg = "+CMGS:";
// Set executionTime inital value for comparison
executionTime = DateTime.Now;
foreach (String Recipient in RecipientList)
{
// Infinite Loop listens to the response from the serial port
while (true)
{
// If the +CMGS: response was recieved continue with the next message
// Use Contains comparison for substring check since some of the +CMGS: responses
// contain carriage return texts along with the repsonse
// Then empty the spReadMsg property to avoid the loop from prematurely
//sending the next message when the latest serial port response has not yet been
//updated from the '+CMGS:' response
if (!string.IsNullOrEmpty(spReadMsg) && spReadMsg.Contains("+CMGS:"))
{
spReadMsg = string.Empty;
break;
}
// If takes longer than 30 seconds to get the response since the last sent
//message - break.
if (DateTime.Now > executionTime.AddSeconds(30))
{
break;
}
}
// Once the while loop breaks proceed with sending the next message.
String formattedRecipientNo = Char.ConvertFromUtf32(34) + Recipient + Char.ConvertFromUtf32(34);
SP.Write("AT+CMGF=1" + Char.ConvertFromUtf32(13));
//Thread.Sleep(800);
SP.Write("AT+CMGS=" + formattedRecipientNo + Char.ConvertFromUtf32(13));
//Thread.Sleep(800);
SP.Write(sanitizedMessage + Char.ConvertFromUtf32(26) + Char.ConvertFromUtf32(13));
//Thread.Sleep(1000);
//Thread.Sleep(2000);
// Get the Datetime when the current message was sent for comparison in
// the next while loop
executionTime = DateTime.Now;
}
SP.Close();
}
I am writing a WPF/C# program that reads data from a weather station and will eventually put it in a MySQL database. The weather station's input to the computer is through a serial port so I am using System.IO.Ports to facilitate the communication.
This is set up so far where I click a button on the UI and it sends the commands to the serial port to have the weather station send data. The issue I am running into involves using the DataRecievedHandler method to handle when data is sent through the serial port to my program. When I get a response back, I get it in the form of a data packet containing 99 bytes, which I read into an array. The issue I am having is that when I try to send that array somewhere else to parse it out, it seems like the array doesn't get completely filled until I press another button on the UI... almost like it waits before finishing filling up the array.
This is the code from the button press in the UI: It sends a wakeup signal "\n"
waits for a bit then sends the signal to get data "LPS 2 1\n"
private void button_testSerial_Click(object sender, RoutedEventArgs e)
{
try
{
wxSerialPort.WriteLine("\n");
//printArrayList();
Thread.Sleep(3000);
wxSerialPort.Write("LPS 2 1\n");
}
catch (Exception C)
{
MessageBox.Show("Please ensure both DB connection and Serial Connections are set up and connected \n \n" + "Error text: \n" + C.ToString(), "Connection Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
When I send the wakeup I get first a response of [10], [13] which is correct, this signals the station is awake and returns a buffer with those values.
When I send the next one (LPS 2 1\n)
I can see data come in and if I run a for loop on the array it looks like it has data while the method is running from the button click. The issue is, if I try to send the array from the DataRecievedHandler method to another method or class or such it seems like it doesn't get filled or sent properly. So I end up running into null values in my Byte[] array.
This is my DataRecievedHandler method
public void DataRecievedHandler(object sender, SerialDataReceivedEventArgs e)
{
byte[] buffer = new byte[wxSerialPort.BytesToRead];
wxSerialPort.Read(buffer, 0, buffer.Length);
Console.WriteLine("Buffer length: " + buffer.Length); //test to see if data comes in
if (buffer.Length == 2) //wake up response is 2 values [10][13]
{
//Console.WriteLine("Wakeup Response");
return;
}
if (buffer.Length == 0)
{
//do nothing and skip
return;
}
else
{
string debugtext = "";
int counter = 0;
for (int i = 0; i < buffer.Length; i++) //loop prints out contents for testing
{
counter++;
Console.WriteLine("Buffer Contents " + buffer[i]);
// Console.WriteLine("ADDING To ArrayList Data: " + buffer[i] + " at position: " + i);
wxPacketData = buffer; //this is where I assign the Byte[] buffer to an array that can be sent off and parsed
}
counter = 0;
return;
}
}
Finally I send the wxPacketData array (byte array created when the DataReceivedHandler method runs) to another class to be parsed, but this only works if it is clicked from another button, this doesn't work when I have it under the same button as shown above:
private void button_parsePacket_Click(object sender, RoutedEventArgs e)
{
wxPacketParser wxPacket = new wxPacketParser(wxPacketData);
wxPacket.parseOutsideHumidity();
wxPacket.parseOutsideTemperature();
wxPacket.parseBarometricPressure();
wxPacket.parseDewPoint();
}
The question is, what am I doing wrong so that I have to run the parsing code under a different button click for this to work properly?
I am relatively new to this so if more information is needed please let me know. All suggestions or guidance are very welcome and I appreciate everyone's help in advance. I feel like I'm missing something very simple or basic, but probably need a push to see what that issue is.
Hello and thanks for considering this query:
I am sending a command from a PC over a virtual serial port to an embedded system which echos back the command when the embedded system has completed the command.
I can send the command fine and see the echo, when the command is completed by the embedded sytem, but I am having trouble finding a suitable way to wait or delay the program until the echoed command is received, so that I may proceed and send the next command. I suppose its a type of "high level" flow control that I'm trying to implement.
The code is in C#.
I'd like to wait fro the echo and have a timeout as well in case communication is lost between the PC and embedded system, so that the program does not freeze.
Any wizz kids out there that can suggest a neat way to do this?
I am not a great c# programmer, just learning.
This is the receive function that I have:
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// If the com port has been closed, do nothing
if (!comport.IsOpen) return;
// This method will be called when there is data waiting in the port's buffer
// Determain which mode (string or binary) the user is in
if (CurrentDataMode == DataMode.Text)
{
// Read all the data waiting in the buffer
string data = comport.ReadExisting();
// Display the text to the user in the terminal
Log(LogMsgType.Incoming, data);
}
else
{
// Obtain the number of bytes waiting in the port's buffer
int bytes = comport.BytesToRead;
// Create a byte array buffer to hold the incoming data
byte[] buffer = new byte[bytes];
// Read the data from the port and store it in our buffer
comport.Read(buffer, 0, bytes);
// Show the user the incoming data in hex format
Log(LogMsgType.Incoming, ByteArrayToHexString(buffer));
}
}
This is an example of a call to Transmitt a command:
text = "AR" + 50 + "\r"; //transmit a command to move
this.comport.Write(text);
Currently I'm using a time Delay [Thread.Sleep(TIMEGAP)] and assuming that the message is executed and that the echo back from the embedded system is fine BUT I do not check it and also wait a very long time to ensure that it is completed:
text = "AR" + 50 + "\r"; //transmit a command to move
this.comport.Write(text);
Thread.Sleep(TIMEGAP); //Timegap = 10000ms
I really would like to replace the time delay call [Thread.Sleep(TIMEGAP)] with a function/method that monitors the response on the serial port, checks to see that it is the same as the one sent and then allows the program code to proceed to the next command, AND if the correct echo [AR50\r in the above example] is not received in say for example 5 seconds, then the program reports an error.
Any suggestions?
Thank you!
The easiest way is not to use the DataReceived event, but by setting a ReadTimeout and using the Read method.
And since you're dealing with ASCII, you should check out the ReadLine method.
Both will throw a TimeoutException if ReadTimeout has elapsed without incoming data.
If, however, the embedded system can send unsolicited messages, the you'll need an other approach. Then you could put the echo you're expecting in a global string variable, and have the receive event set a ManualResetEvent when the echo has been received. Then you can wait for the ManualResetEvent with a timeout. This will also involve thread synchronization using the lock statement.
If GC is not an issue, I would probably start with something like this:
using System.Text;
using System.IO.Ports;
using System.Threading;
namespace ConsoleApplication2
{
class Program
{
static string serialBuffer = "";
static string expectedEcho = null;
static object expectedEchoLock = new object();
static ManualResetEvent expectedEchoReceived = new ManualResetEvent(false);
static SerialPort port = new SerialPort("COM1", 19200, Parity.None, 8, StopBits.One);
static void Main(string[] args)
{
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
}
static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
while (port.BytesToRead > 0)
{
byte[] buffer = new byte[port.BytesToRead];
int bytesRead = port.Read(buffer, 0, buffer.Length);
if (bytesRead <= 0) return;
serialBuffer += Encoding.UTF8.GetString(buffer, 0, bytesRead);
string[] lines = serialBuffer.Split('\r', '\n');
// Don't process the last part, because it's not terminated yet
for (int i = 0; i < (lines.Length - 1); i++)
{
if (lines[i].Length > 0)
ProcessLine(lines[i]);
}
serialBuffer = lines[lines.Length - 1]; // keep last part
}
}
static void ProcessLine(string line)
{
bool unsolicitedMessageReceived = false;
lock (expectedEchoLock)
{
if (line == expectedEcho)
{
expectedEchoReceived.Set();
}
else
{
unsolicitedMessageReceived = true;
}
}
if (unsolicitedMessageReceived)
{
// Process unsolicited/unexpected messages
}
}
/// <summary>
/// Send a command and wait for echo
/// </summary>
/// <param name="command">The command to send</param>
/// <returns>True when echo has been received, false on timeout.</returns>
static bool SendCommand(string command)
{
lock (expectedEchoLock)
{
expectedEchoReceived.Reset();
expectedEcho = command;
}
port.Write(command);
return expectedEchoReceived.WaitOne(5000); // timeout after 5 seconds
}
}
}
I am attempting to create a small application to collect data received from an external sensor attached to COM10. I have successfully created a small C# console object and application that opens the port and streams data to a file for a fixed period of time using a for-loop.
I would like to convert this application to use the dataReceived event to stream instead. After reading the Top 5 SerialPort Tips, I still can't seem to get this to work and don't know what I am missing. I rewrote the console application so that all the code is in Main and is pasted below. Can someone please help enlighten me as to why the event handler port_OnReceiveDatazz is not being called even though I know that there is data being sent to the port by the hardware?
Thanks
Thanks to #Gabe, #Jason Down, and #abatishchev for all the suggestions. I am stumped and can't seem to get the event handler to work. Perhaps it has something to do with the device. I can live with just reading the port in a thread and streaming the data straight to file.
Code
namespace serialPortCollection
{ class Program
{
static void Main(string[] args)
{
const int bufSize = 2048;
Byte[] buf = new Byte[bufSize]; //To store the received data.
SerialPort sp = new SerialPort("COM10", 115200);
sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler
sp.Open();
sp.WriteLine("$"); //Command to start Data Stream
// Wait for data or user input to continue.
Console.ReadLine();
sp.WriteLine("!"); //Stop Data Stream Command
sp.Close();
}
// My Event Handler Method
private static void port_OnReceiveDatazz(object sender,
SerialDataReceivedEventArgs e)
{
SerialPort spL = (SerialPort) sender;
const int bufSize = 12;
Byte[] buf = new Byte[bufSize];
Console.WriteLine("DATA RECEIVED!");
Console.WriteLine(spL.Read(buf, 0, bufSize));
}
}
}
I think your issue is the line:**
sp.DataReceived += port_OnReceiveDatazz;
Shouldn't it be:
sp.DataReceived += new SerialDataReceivedEventHandler (port_OnReceiveDatazz);
**Nevermind, the syntax is fine (didn't realize the shortcut at the time I originally answered this question).
I've also seen suggestions that you should turn the following options on for your serial port:
sp.DtrEnable = true; // Data-terminal-ready
sp.RtsEnable = true; // Request-to-send
You may also have to set the handshake to RequestToSend (via the handshake enumeration).
UPDATE:
Found a suggestion that says you should open your port first, then assign the event handler. Maybe it's a bug?
So instead of this:
sp.DataReceived += new SerialDataReceivedEventHandler (port_OnReceiveDatazz);
sp.Open();
Do this:
sp.Open();
sp.DataReceived += new SerialDataReceivedEventHandler (port_OnReceiveDatazz);
Let me know how that goes.
First off I recommend you use the following constructor instead of the one you currently use:
new SerialPort("COM10", 115200, Parity.None, 8, StopBits.One);
Next, you really should remove this code:
// Wait 10 Seconds for data...
for (int i = 0; i < 1000; i++)
{
Thread.Sleep(10);
Console.WriteLine(sp.Read(buf,0,bufSize)); //prints data directly to the Console
}
And instead just loop until the user presses a key or something, like so:
namespace serialPortCollection
{ class Program
{
static void Main(string[] args)
{
SerialPort sp = new SerialPort("COM10", 115200);
sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler
sp.Open();
sp.WriteLine("$"); //Command to start Data Stream
Console.ReadLine();
sp.WriteLine("!"); //Stop Data Stream Command
sp.Close();
}
// My Event Handler Method
private static void port_OnReceiveDatazz(object sender,
SerialDataReceivedEventArgs e)
{
SerialPort spL = (SerialPort) sender;
byte[] buf = new byte[spL.BytesToRead];
Console.WriteLine("DATA RECEIVED!");
spL.Read(buf, 0, buf.Length);
foreach (Byte b in buf)
{
Console.Write(b.ToString());
}
Console.WriteLine();
}
}
}
Also, note the revisions to the data received event handler, it should actually print the buffer now.
UPDATE 1
I just ran the following code successfully on my machine (using a null modem cable between COM33 and COM34)
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Thread writeThread = new Thread(new ThreadStart(WriteThread));
SerialPort sp = new SerialPort("COM33", 115200, Parity.None, 8, StopBits.One);
sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler
sp.Open();
sp.WriteLine("$"); //Command to start Data Stream
writeThread.Start();
Console.ReadLine();
sp.WriteLine("!"); //Stop Data Stream Command
sp.Close();
}
private static void port_OnReceiveDatazz(object sender,
SerialDataReceivedEventArgs e)
{
SerialPort spL = (SerialPort) sender;
byte[] buf = new byte[spL.BytesToRead];
Console.WriteLine("DATA RECEIVED!");
spL.Read(buf, 0, buf.Length);
foreach (Byte b in buf)
{
Console.Write(b.ToString() + " ");
}
Console.WriteLine();
}
private static void WriteThread()
{
SerialPort sp2 = new SerialPort("COM34", 115200, Parity.None, 8, StopBits.One);
sp2.Open();
byte[] buf = new byte[100];
for (byte i = 0; i < 100; i++)
{
buf[i] = i;
}
sp2.Write(buf, 0, buf.Length);
sp2.Close();
}
}
}
UPDATE 2
Given all of the traffic on this question recently. I'm beginning to suspect that either your serial port is not configured properly, or that the device is not responding.
I highly recommend you attempt to communicate with the device using some other means (I use hyperterminal frequently). You can then play around with all of these settings (bitrate, parity, data bits, stop bits, flow control) until you find the set that works. The documentation for the device should also specify these settings. Once I figured those out, I would make sure my .NET SerialPort is configured properly to use those settings.
Some tips on configuring the serial port:
Note that when I said you should use the following constructor, I meant that use that function, not necessarily those parameters! You should fill in the parameters for your device, the settings below are common, but may be different for your device.
new SerialPort("COM10", 115200, Parity.None, 8, StopBits.One);
It is also important that you setup the .NET SerialPort to use the same flow control as your device (as other people have stated earlier). You can find more info here:
http://www.lammertbies.nl/comm/info/RS-232_flow_control.html
By the way, you can use next code in you event handler:
switch(e.EventType)
{
case SerialData.Chars:
{
// means you receives something
break;
}
case SerialData.Eof:
{
// means receiving ended
break;
}
}
I was having the very same problem with a modem that had previously worked and then one day just stopped raising the DataReceived event.
The solution in my case, very randomly, was to enable RTS e.g.
sp.RtsEnable = true;
No idea why that worked on this particular bit of kit (not a comms man at all really), nor why it had worked and then stopped but it may help somebody else one day so just posting it just in case...
Might very well be the Console.ReadLine blocking your callback's Console.Writeline, in fact. The sample on MSDN looks ALMOST identical, except they use ReadKey (which doesn't lock the console).
I believe this won't work because you are using a console application and there is no Event Loop running. An Event Loop / Message Pump used for event handling is setup automatically when a Winforms application is created, but not for a console app.
Be aware that there are problems using .NET/C# and any COM port higher than COM9.
See: HOWTO: Specify Serial Ports Larger than COM9
There is a workaround in the format: "\\.\COM10" that is supported in the underlying CreateFile method, but .NET prevents using that workaround format; neither the SerialPort constructor nor the PortName property will allow a port name that begins with "\"
I've been struggling to get reliable communications to COM10 in C#/.NET. As an example, if I have a device on COM9 and COM10, traffic intended for COM10 goes to the device on COM9! If I remove the device on COM9, COM10 traffic goes to the device on COM10.
I still haven't figured how to use the handle returned by CreateFile to create a C#/.NET style SerialPort object, if I knew how to do that, then I think I could use COM10+ just fine from C#.
switch(e.EventType)
{
case SerilData.Chrs:
{
// means you receives something
break;
}
case SerialData.Eo:
{
// means reciving ended
break;
}
}
First of all change your line to:
sp.DataReceived += new SerialDataReceivedEventHandler(OnDataReceived);
Second, be aware that DataReceived is fired every time a byte arrives - so the data you read is likely to be a single character each time, and "buffer" will never hold an entire message if you overwrite it every time you handle the event.