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;
}
Related
I'm trying to communicate with the GC420t via serial port in c#.
I need to get the status of the printer.
I want to send the command ~HQES and receive the status.
I'm able to write commands: I've tryied to print succesfully some qrcodes.
But when I write the status info command I don't get any answer.
This is my test code:
//define serial port
static SerialPort _serialPort = new SerialPort("COM7", 9600, Parity.None, 8, StopBits.One);
public Serial()
{
try
{
_serialPort.Open();
WriteCommand();
ReadStatus();
}
catch (Exception ex)
{
_serialPort.Close();
}
}
public void WriteCommand()
{
string qrcode = "^XA^FO,20,20^BQ,2,10^FDD03048F,LM,N0123456789,A12AABB,B0006qrcode^FS^XZ";
string statusInfo = " ~HQES";
_serialPort.Write(statusInfo);
}
public void ReadStatus()
{
_serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
private static void DataReceivedHandler(
object sender,
SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
Console.WriteLine("Data Received:");
Console.Write(indata);
}
EDIT:
It will be fine for me also to be able to get that info on request:
WriteCommand();
int timeout = 3000;
while (timeout > 0)
{
Thread.Sleep(10);
timeout--;
// Timeout
string status = _serialPort.ReadExisting();
}
You should check ifs the printer configured to use any form of handshaking? Particularly XON/XOFF or DTR .
Ref page 24 of User Guide. https://www.uline.com/PDF/IH-7246VND.PDF
In the event the link breaks - the relevant text is
"The printer has a DCE serial communication port for hardware compatibility with legacy 888 printers. The required cable must have a nine-pin "D" type (DB-9P) male connector on one end which plugs into the mating (DB-9S) serial port located on the back of the printer. The other end of this signal interface cable connects to a serial port on the host computer. The cable is a Null-Modem (cross-over signal connections) cable. For pinout information, refer to Appendix A.
The serial port communication settings between the printer and host (typically a PC) must match for reliable communication. The Bits per second (or Baud rate) and Flow control are the most common settings that get changed. The host (typically a Windows PC) needs to have the data Flow control changed to match the printer's default communication method: Hardware and is noted by the Host Handshake setting DTR/Xon/Xoff. This combined hardware (DTR) and software (Xon/Xoff) mode may need to change depending{ upon use with non-Zebra application software or the serial cable variation in use."
You need to hook up the DataReceived handler before you make any calls, otherwise it might have answered before you hook it up.
Move ReadStatus up 1 line so that it comes straight after the constructor.
_serialPort.Open();
ReadStatus();
WriteCommand();
I am building a console application which will be scheduled in Task Scheduler of Windows to run my code every day at a set hour. Summing up, this application will read and write through SerialPort. When I send something to the Arduino, I need to receive something from it to finish up what I've sent and execute the command.
In other words, I will send something so see if the door is opened, if it is the application will run a code to close it. If the door is already closed I will send a bunch of characters to be displayed into the Arduino Led Display.
So I've developed a code but I am not sure if it's totally correct, if possible help me improve it. There is any changes I could make?
static void Main(string[] args)
{
SerialPort comport = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
comport.Open();
string start = "?";
string carriageReturn = "\r";
string text = string.Empty;
string mensage = "#" + "r" + "\r";
string mensage2 = "#" + "{" + texto + "\r";
try
{
while (true)
{
//Send to the Arduino
comport.Write(start+ "*" + carriageReturn);
//If the serial port have bytes to read
if (comport.BytesToRead > 0)
{
//Buffer with data
byte[] dados = HexStringToByteArray(mensage);
//Handle data
comport.Read(dados, 0, dados.Length);
//Send again to execute the the command
comport.Write(start + "*" + carriageReturn);
}
if (comport.BytesToRead > 0)
{
comport.Write(start + "*" + carriageReturn);
byte[] dados2 = HexStringToByteArray(mensage2);
comport.Read(dados2, 0, dados2.Length);
comport.Write(text);
}
comport.Close();
}
}
catch(Exception ex)
{
}
}
private static byte[] HexStringToByteArray(string s)
{
s = s.Replace(" ", "");
byte[] buffer = new byte[s.Length / 2];
for (int i = 0; i < s.Length; i += 2)
buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
return buffer;
}
EDIT:
My basic input output mapping/relationship is:
I will send ?*\r to the Arduino, then I will wait for a answer.
If the answer from Arduino is #r\r I will send the ?*\r again.
If the answer from the Arduino is #{/r I will send a string to him.
Here is a first attempt of my revision.
You should consider to use the DataReceived event and have an Exit-Strategy. Up to now your program never exits.
static void Main(string[] args)
{
SerialPort port = new SerialPort("COM1", 9600, Parity.Odd, 7, StopBits.One);
// register the event
port.DataReceived += Port_DataReceived;
//open the port
port.Open();
try
{
// start the communication
port.Write("?*\r");
Console.WriteLine("Waiting for response");
// Your manual exit strategy. Hit a kKeyboard-key to end this game
while (!Console.KeyAvailable)
{
}
}
catch (Exception ex)
{
Console.WriteLine("Writing failed! \nError: " + ex.Message);
}
}
The DataReceived event will be fired as soon as the Arduino sends you something. If it doesn't you can just quit your program manually.
Inside the event you can do your entire logic. As you already posted the input-output mapping you can just hack it into solid code as it is written in your post. If it is only 2 cases you could also use a switch/case construct:
private static void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort port = sender as SerialPort;
// read input
string incoming = port.ReadExisting();
switch (incoming)
{
case "#r\r":
// send the message back
port.Write("?*\r");
break;
case #"#{/r":
port.Write("Display this!");
break;
default:
Console.WriteLine("Unknown command from Arduino!\n Command: " + incoming);
break;
}
}
I hope it helps
If I understand your description properly (closing the door will be done by arduino, right?), this is the task:
A task scheduled console app triggers an arduino via Serial to check a status and execute something (if status tells thats necessary)
Arduino reports back status (and eventually that task was performed)
You could do it very simple:
"?" --> PC sends a trigger: Check door status and behave accordingly
Then PC waits for a response:
"1" <-- Arduino response ( door was already closed )
"2" <-- or alternate response ( I closed the door )
Arduino could send anything else to indicate an error. (feel free to invent as much as adequate)
Additionally, it's possible that arduino does not respond at all. So what would your scheduled console app do then?
You do not need to send/receive more than a byte, to have it simpler for Arduino and your little Serial PC code.
The PC code does not have a user interface, so there's no need for Arduino to respond immediately, if closing the door should take a while.
I'm on my laptop and unable to check this right now, I'm wondering if I open a COM2 connection, and add a receive event for COM2 port, then close the COM2 connection via "serial.Close()" in the program, will I still be able to receive a receive event on COM2 port? Let say if it can still receive, I think I will open the COM2 port connection at the receive event and read the data, can it be done this way?
SerialPort serial = new SerialPort()
{
PortName = "com2",
BaudRate = 9600,
Handshake = System.IO.Ports.Handshake.None,
Parity = Parity.None,
DataBits = 8,
StopBits = StopBits.One,
ReadTimeout = 400,
WriteTimeout = 200,
};
serial.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(Receive);
private void Receive(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
using (SerialPort serialPort = serial)
{
if (serialPort.IsOpen)
serialPort.Close();
try
{
serialPort.Open();
received_data = serialPort.ReadExisting();
Dispatcher.Invoke(DispatcherPriority.Send, new UpdateUiTextDelegate(WriteMyData), received_data);
}
catch (Exception ex)
{
}
finally
{
if (serialPort != null)
{
if (serialPort.IsOpen)
{
serialPort.Close();
}
Thread.Sleep(5);
//serialPort.Dispose();
}
Thread.Sleep(5);
}
}
}
public void SerialCmdSendByte(byte[] hexstring)
{
using (SerialPort serialPort = serial)
{
if (serialPort.IsOpen)
serialPort.Close();
try
{
serialPort.Open();
foreach (byte hexval in hexstring)
{
byte[] _hexval = new byte[] { hexval };
serialPort.Write(_hexval, 0, 1);
Thread.Sleep(3);
}
}
catch (IOException ex)
{
}
finally
{
if (serialPort != null)
{
if (serialPort.IsOpen)
{
serialPort.Close();
}
Thread.Sleep(5);
//serialPort.Dispose();
}
Thread.Sleep(5);
}
}
}
The idea is to only open a connection when I want to send from C# program and close it straight-away, but the same COM port is actually need to listen for communication from PIC-based microcontroller. Currently we are having issue where previously the program never try to close connection (unlike above code), but sometimes the receiving part from PIC-based microcontroller works but the sending part from program doesnt work. This only happens sometime, as normally the program work just fine...
Restarting the system seem to reset this OK. So I was thinking making the code like above will help in my situation(to be able to send, and listen on same COM port)?
Unfortunately, no. Only 1 connection to the COM port can be open at a time.
If you have a device listening on that port, then nothing else will be able to send on it.
If you have access to the code that the microcontroller uses to listen on the device, you could always write something that allows you to send a message to the microcontroller and have that device send your COM message.
Otherwise, the only thing you could do is have the microcontroller stop listening on the port, send your message from your code, receive any response that comes back from sending your message, and then reenable the microcontroller's listen features.
EDIT:
Just a note about your code. The using statement is designed to dispose of an object after closing all of its connections when it goes out of scope.
In your code, you are creating a reference to your serial port instance in a using block. If this works as defined, that serial port instance will need to be instantiated before you can call it again.
As a complement to jp2code answer and since imagination is the limit, you might want to try a more "out of the box" solution, something similar to Man-In-The-Middle.
THIS POST might give you a better idea about what I'm talking about.
You cannot communicate or listen on serial port when the port is closed.
So the code you have written is absolutely wrong.
Ideally you should not turn off the COM port.
Following can be the reasons why your communication fails.
1.This one seems to be the most probable answer: If the COM port is not closed properly in the previous run, the communication will not take place. In this case you have to reset the system. This takes place when the system gets crashed in between.
When the connectors are not tight, this can occur.
Check if the ground connections are proper.
Let me know if you need help in changing the code.
I am trying to read data from an RS-232 port. Does anyone have an example of how I get the data from the port/buffer and make sure that I have all the data as it can be multiline data.
Do I simply read it as follows ?
string Rxstring = port.ReadLine();
Console.WriteLine(Rxstring);
Q: how to get the date from the port/buffer, or input data from your connected device. AND make sure that you have all the data.
A: i have worked extensively with .net serial port class drivers where i was tasked to create reliable, robust code. this means that a connected device under test has to run and NOT fail over a LONG period of time. Serial port can AND does lose data! don't forget that.
//from top of the head;
using System.Port.IO;
using System.Port;
private class mywindowsForm: Form
{
StringBuilder sbReceived = new StringBuilder();
string Received = string.Empty;
int byteCOUNT = 0;
System.Windows.Timers.Timer serialTimer;
//Default constructor
myWindowsForm()
{
//assume that you clicked and dragged serial port in
serialPort1 = new SerialPort();//create new serial port instance
serialPort1.Baud = 9600;
serialPort1.DataReceived+=<Tab><Enter>
//serial port timer
serialTimer = new System.Windows.Timers.Timer(500);//set to 500ms time delay
serialTimer.Elapsed+=<TAB><ENTER>
}
void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//serial port has detected input data
//however, we only want to get serial data so,
if(e.EventType !=SerialData.Chars) return;
//good design practice dictates that we have an event handler that we invoke
this.BeginInvoke(new eventhandler(AddReceive));//beginInvoke is designed to deal with asynchronous processes like serial port data.
}
private void AddReceive(object s, EventArg e)
{
byteCOUNT=serialPort1.BytesToRead;//count the number of bytes in RX buffer
if(byteCOUNT > 0)
{
string ST = serialPort1.ReadTo("\n");//lets get one line at a time
sbReceived.Append(ST);//add whatever has been RX'd to our container.
serialPort1.Interval =100;
serialPort1.Start();//to be sure we have all data, check to see for stragglers.
}
}
void serialTimer(object Sender, TimerElapsedEventArgs e)
{
serialTimer.Stop();
this.BeginInvoke(new EventHandler(ReadData));
}
void ReadData(object Sender, EventArgs e)
{
//parse output for required data and output to terminal display (build one using rich text box)
Received = sbReceived.ToString();
//and if we have ANY MORE incoming data left over in serial buffer
if(Received.Length > 0)
{
//your data
}
}
}
this should be plenty to get you started. this is result of years of creating customized terminal emulators in c#. there are other things that can be done, particularly if you have large amount of i/o data you need to set up handshaking with device. you have to let the device handle at a rate that the device is happy with. in cases where larger data has to be transferred consider setting up a simple packet passing protocol and command semaphore construct - or use a protocol as defined that the controller / device is designed to work with.
Try this:
using System.IO.Ports;
...
private SerialPort port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
Console.WriteLine(port.ReadExisting());
Details can be found at Coad's Code.
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.