When opening a serial port, the device I am trying to access may not have a baudrate of 9600 set so I will have to go thru each baudrate until the device opens correctly.
What error, ArgumentOutOfRangeException or an IOException or some other, should I look for after performing the Port.Open ? Or do I perform this check using the PortOpen statement ?
Sorry to have to ask all of these simple questions, but I am unsure how else to get the information ?
Is there any way that I can tell how to use the PortOpen procedures correctly, including the error handling too, so that I dont have to keep asking everyone ?
Thanks, George.
IOException
InvalidOperationException
is what You should receive in case of errors.
Here You have great example in C#:
http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.open.aspx
You could find all exceptions explanation down there.
For more general advice, have a look at the System.IO.Ports namespace which has a more complete example. Personally I'd adapt what they have there to set your general port settings and then try a few different baud rates in debug mode (some bad, one known good). You'll very quickly see what a bad configuration gives vs a good one. I'm assuming you have access to test the device here?
It's worth noting that you won't see any problems with the call to open the port (just opening it will only test that you've set some parameters which the port supports). You'll start to see issues when you try and read/write to the device and it's there that you'll want to do the error checking for a valid baud rate.
[EDIT] Try something along these lines (NOTE: I've not tested this with any hardware but it at least compiles):
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO.Ports;
namespace SerialPortExperiments
{
class Program
{
public static void Main()
{
// Create a new SerialPort object with default settings.
SerialPort _serialPort = new SerialPort();
// Set some generic settings
SetBasicSettings(ref _serialPort);
// Try and find something valid
int baudRate = FindValidBaud(ref _serialPort);
if (baudRate > 0)
{
Console.WriteLine(String.Format("Found baudrate: {0}", baudRate));
}
else
{
Console.WriteLine("ERROR: Failed to identify baudrate");
}
}
public static void SetBasicSettings(ref SerialPort port)
{
port.PortName = "COM1";
port.Parity = Parity.None;
port.DataBits = 8;
port.StopBits = 0;
port.Handshake = Handshake.None;
port.ReadTimeout = 500;
port.WriteTimeout = 500;
}
public static int FindValidBaud(ref SerialPort port)
{
bool buadrateIdentified = false;
// Pick some baudrates to try
List<int> baudrates = new List<int>();
baudrates.Add(9600);
baudrates.Add(19200);
// Try and open the port at each baud rate in turn, trying one write/read to verify success
for (int i = 0; i < baudrates.Count; i++)
{
// Pick a baud rate
port.BaudRate = baudrates[i];
// Try opening a connection and exchanging some data
port.Open();
buadrateIdentified = AttemptValidExchange(ref port);
port.Close();
if (buadrateIdentified)
{
return port.BaudRate;
}
}
return -1;
}
public static bool AttemptValidExchange(ref SerialPort port)
{
try
{
// Send a test command
port.Write("SOME_TEST_COMMAND");
// Check to see what the device responded with
const int expectedReturnLength = 1024;
byte[] buffer = new byte[expectedReturnLength];
port.Read(buffer, 0, expectedReturnLength);
if (buffer.ToString().Equals("EXPECTED_RETURN_VALUE"))
{
return true;
}
}
catch (TimeoutException) // NOTE: You'll probably need to catch other exceptions like parity errors here
{
}
return false;
}
}
}
Related
I am trying to send the bytes (b'\x03\r') to a device on COM5. The result will be the micropython board on the other end crashing. The python code results in the board freezing (As intended). The C# code results in no changes on the device's end, and the serial port not working until it is replugged. How can I get the C# code to do the same thing that the python code does?
This python code works:
import serial # this is installed with 'pip install pyserial'
ser = serial.Serial(
port='COM5',
baudrate=115200,
)
ser.write(b'\x03\r')
I tried to make this C# code to do the same thing but it does not work
using System.IO.Ports;
public static class tester {
public static void main(/* String[] args */) {
SerialPort sport = new SerialPort("COM5", 115200);
sport.Open();
sport.Write(new byte[]{0x03, 0xD}, 0, 2);
sport.Close();
}
}
Thanks for trying to help me :)
The solution as #kunif and #Hans Passant said was that I needed to set certain parameters as their defaults are not the same on different implementations of serial port libraries. To use a serial device that works fine with the default settings of PySerial use the following code. You will likely have to change the baud rate based on your specific device.
SerialPort sport = new SerialPort("COM5", 115200);
// I love StackOverflow
sport.Handshake = Handshake.None;
sport.DtrEnable = true;
sport.RtsEnable = true;
sport.StopBits = StopBits.One;
sport.DataBits = 8;
sport.Parity = Parity.None;
sport.Open();
After much head scratching I managed to find the com port number of the USB to RS232 converters on my system. I have 15 in the present system. What I now need to do is detect when one is connected or disconnected so that I can update my table. I can work out how to detect a USB Storage device but the same does not work for USB to RS232 converters. Does anyone have any idea on how I can detect this please?
Here is the code snippet I use to work out the com ports in use by the converters
private void btn_tst2_Click(object sender, EventArgs e)
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_PnPEntity WHERE ClassGuid=\"{4d36e978-e325-11ce-bfc1-08002be10318}\"");
foreach (ManagementObject queryObj in searcher.Get())
{
rtxbx_output.AppendText(queryObj["Name"].ToString() +"\r");
}
}
You can make some sort of Thread or Task which queries the array returned from the method System.IO.Ports.SerialPort.GetPortNames(): when this array changes, or a new element is added, it means that a new serial port have been connected to the system.
Of course, it will return every serial port on your system, but then you can choose which of them are USB-RS232 converters with your code snippet.
You can make use of MSSerial_PortName Please find the snippet.
Note: This is not a working code, you have to make changes as per your requirement.
private void btn_tst2_Click(object sender, EventArgs e)
{
// connection to WMI rather than cimv2 namespace
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\WMI",
"select * from MSSerial_PortName");
foreach (ManagementObject queryObj in searcher.Get())
{
queryObj["Active"]; // status
queryObj["InstanceName"];
queryObj["PortName"]; // port number
}
}
After much frustration I finally worked out how to achieve what I needed. I post the answer in case it gives a pointer to anyone in the future stuck in the same area. I include comments in the code. What I get at the end is a list of all the Com Ports and this is refreshed when one leaves or joins. The file dbt.h is found on your PC.
To make the code below work just make a solution with a Rich text box and two buttons.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Management; //used to perform WMI queries
using System.Threading;
using System.Runtime.InteropServices; // needed for marshalling
namespace MissionControl
{
public partial class Control : Form
{
public Control()
{
InitializeComponent();
}
// WndProc = Window Procedures: Every window has an associated window procedure — a function that processes all messages sent
// or posted to all windows of the class. All aspects of a window's appearance and behavior depend on the window procedure's
// response to these messages. see https://msdn.microsoft.com/en-us/library/ms632593%28v=vs.85%29.aspx
protected override void WndProc(ref Message m)
{
//you may find these definitions in dbt.h
const int WM_DEVICECHANGE = 0x0219; // in dbt.h, BroadcastSpecialMessage constants.
const int DBT_DEVICEARRIVAL = 0x8000; // system detected a device arrived see dbt.h
const int DBT_DEVICEREMOVECOMPLETE = 0x8004; //system detected a device removal see dbt.h
const int DBT_DEVTYP_PORT = 0x00000003; // serial, parallel in dbt.h
switch (m.Msg)
{
case WM_DEVICECHANGE:
switch (m.WParam.ToInt32())
{
case DBT_DEVICEARRIVAL:
{
// Get the DBT_DEVTYP* as defined in dbt.h
// We are looking for DBT_DEVTYP_PORT value = 3 which is Serial port
int devTypeA = Marshal.ReadInt32(m.LParam, 4);
if (devTypeA == DBT_DEVTYP_PORT)
{
rchtxbx_output.SelectedText = string.Empty;
rchtxbx_output.SelectionFont = new Font(rchtxbx_output.SelectionFont, FontStyle.Bold);
rchtxbx_output.SelectionColor = Color.Lime;
rchtxbx_output.AppendText("\rSerial Port Connected\r\rList of Current Ports\r");
}
else
{
// We should never get in here but just in case do somethign rather than fall over
rchtxbx_output.SelectedText = string.Empty;
rchtxbx_output.SelectionFont = new Font(rchtxbx_output.SelectionFont, FontStyle.Bold);
rchtxbx_output.SelectionColor = Color.Red;
rchtxbx_output.AppendText("Non-Serial Port Connected\r");
}
//To prevent cross threading we will start the function call in its own thread
// Create the thread object, passing in GetPortNum
//ThreadA is the arrival thread (just connected)
Thread ThreadA = new Thread(new ThreadStart(GetPortNum));
// Start the thread via a ThreadStart delegate
ThreadA.Start();
}
break;
case DBT_DEVICEREMOVECOMPLETE:
{
// Get the DBT_DEVTYP* as defined in dbt.h
// We are looking for DBT_DEVTYP_PORT value = 3 which is Serial port
int devTypeD = Marshal.ReadInt32(m.LParam, 4);
if (devTypeD == DBT_DEVTYP_PORT)
{
rchtxbx_output.SelectedText = string.Empty;
rchtxbx_output.SelectionFont = new Font(rchtxbx_output.SelectionFont, FontStyle.Bold);
rchtxbx_output.SelectionColor = Color.Lime;
rchtxbx_output.AppendText("\rSerial Port Disconnected\r\rList of Current Ports\r");
}
else
{
// We should never get in here but just in case do something rather than fall over
rchtxbx_output.SelectedText = string.Empty;
rchtxbx_output.SelectionFont = new Font(rchtxbx_output.SelectionFont, FontStyle.Bold);
rchtxbx_output.SelectionColor = Color.Red;
rchtxbx_output.AppendText("Non-Serial Port Disconneted\r");
}
//To prevent cross threading we will start the function call in its own thread
// Create the thread object, passing in GetPortNum
//ThreadD is the departure thread (disconnected)
Thread ThreadD = new Thread(new ThreadStart(GetPortNum));
// Start the thread via a ThreadStart delegate
ThreadD.Start();
}
break;
}
break;
}
//we detect the media arrival event
base.WndProc(ref m);
}
private void btn_close_Click(object sender, EventArgs e)
{
this.Close();
}
private void btn_clr_Click(object sender, EventArgs e)
{
rchtxbx_output.Clear();
}
private void GetPortNum()
{
//Windows Management Instrumentation (WMI) consists of a set of extensions to the Windows Driver Model that provides an
//operating system interface through which instrumented components provide information and notification.
// To work out the WMI to use, get the tool https://www.microsoft.com/en-us/download/details.aspx?id=8572
//GUID (or UUID) is an acronym for 'Globally Unique Identifier' (or 'Universally Unique Identifier'). It is a 128-bit
//integer number used to identify resources. The term GUID is generally used by developers working with Microsoft
//technologies, while UUID is used everywhere else.
// Get the list of ClassGUID from https://msdn.microsoft.com/en-us/library/windows/hardware/ff553426%28v=vs.85%29.aspx
string comportnum = "";
int textStart = 0;
char[] textEnd = { ')' };
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_PnPEntity WHERE ClassGuid=\"{4d36e978-e325-11ce-bfc1-08002be10318}\"");
foreach (ManagementObject queryObj in searcher.Get())
{
comportnum = queryObj["Name"].ToString(); // Get the name of the comm port
//Format the string to extract the comport number only
textStart = comportnum.IndexOf("(COM");
comportnum = comportnum.Remove(0, textStart + 4).TrimEnd(textEnd);
//To prevent cross threading use Invoke. We are writing to a control created in another thread.
rchtxbx_output.Invoke(new EventHandler(delegate
{
rchtxbx_output.SelectedText = string.Empty;
rchtxbx_output.SelectionFont = new Font(rchtxbx_output.SelectionFont, FontStyle.Bold);
rchtxbx_output.SelectionColor = Color.Lime; //set font colour
rchtxbx_output.AppendText("Comm Port = " + comportnum + "\r"); //add some text
rchtxbx_output.ScrollToCaret(); // move cursor to the end
}));
}
}
}
}
We need to weigh a bunch of products, and store their weight in a DB.
We have a scale that can be plugged through a serial port, and then we want to scan the product's barcode with a barcode scanner.
I need to write a program that will read the scale data. I have read and followed a bunch of articles on how to do it, but I can't seem to make it work.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Weighting
{
public partial class Form1 : Form
{
SerialPort port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
public Form1()
{
this.InitializeComponent();
this.port.Open();
this.port.Encoding = Encoding.ASCII;
this.port.Handshake = Handshake.None;
this.port.RtsEnable = true;
this.port.ReceivedBytesThreshold = 1;
this.port.DataReceived += new SerialDataReceivedEventHandler(this.port_DataReceived);
}
~Form1()
{
this.port.Close();
this.port.Dispose();
}
private void button2_Click(object sender, EventArgs e)
{
try
{
char[] data = new char[] { 's', ' ', '\r', '\n' };
this.port.Write(data, 0, 1);
Thread.Sleep(500);
var InputData = this.port.ReadLine();
if (!string.IsNullOrEmpty(InputData))
{
MessageBox.Show(InputData);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var InputData = this.port.ReadLine();
this.BeginInvoke(new Action(() =>
{
MessageBox.Show(InputData);
}));
}
}
}
The DataReceived handler is never fired, and ReadLine() never returns anything.
The specs of the scale can be found here (chapter 10) : http://www.kern-sohn.com/manuals/files/English/FCB-BA-e-0911.pdf
Note that it's very possible that my serial port, or my cable don't work, or that the scale doesn't send data, or whatever (it's been about 15 years since I have used a serial port device). How can y test that everything works ?
Thank you!
Update
The connection parameters have been taken (and interpreted) from an old Excel macro:
With MSComm1
.CommPort = 1
.Handshaking = 0
.RThreshold = 1
.RTSEnable = True
.Settings = "9600,n,8,1"
.SThreshold = 1
.PortOpen = True
End With
This macro supposedly used to work a few years back, but I couldn't make it work myself (the MSComm1 object is not defined).
Your connection setup looks to match the documentation, but your code smells quite a bit (this is nothing against you, the .NET SerialPort is a nightmare to work with!).
You are not setting the SerialPort.NewLine property to Environment.NewLine (CRLF) and it is instead using the default value of \n (LF). This alone doesn't solve the problem, because you would just have a carriage return at end of the result of calling ReadLine().
The real issue is the DataReceived handler. A lot of people recommend using it but through my exhaustive testing it diminishes determinism of results greatly, particularly when communicating in a friendly encoding such as ASCII. Because you initiate the command by sending your s character, I would honestly get rid of it, and just call ReadLine() right after your Write. (You do not need to Thread.Sleep because ReadLine will simply block until the terminated string is read in or a timeout is reached).
Your ReceivedBytesThreshold is set to 1, and that specifies the buffer size to fill before raising DataReceived so you're trying to read a whole line for every byte received (give or take, DataReceived is very unpredictable).
Here's an example of how I would clean it up (in addition to removing the DataReceived handler). I can't be certain if this will clear your problem up, but making these corrections will lead to much easier debugging. As a temporary change to aid with debugging, you could bring your Sleep call back in and call ReadExisting instead of ReadLine and examine what it pulls (if anything) from the buffer.
private void button2_Click(object sender, EventArgs e)
{
try
{
this.port.Write("s");
var InputData = this.port.ReadLine();
if (!string.IsNullOrEmpty(InputData))
{
MessageBox.Show(InputData);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
I've just finished writing a library to sit on top of the .NET SerialPort for my company and have run into the same sort of issues (plus a million more) as you. If you would like further clarification feel free to ask.
As pointed out by #user3894601, the problem was solved by using the original cable, and using a USB adapter.
I'd like to know if there is a way to get the Baud Rate when it's connected on the RS232 port
BEFORE you initialize the SerialPort class and set it's values. Let me try to explain the reason for that...
Today, I'm working with two different RFID Reader devices, each one works on a different BaudRate, so if I set a wrong baudrate when I create the SerialPort class, it will read the card id all wrong, instead get the real card's id, it will get something like ????|W2???.
Also, there's a possibilite that the device have a USB port.
That's why I'd like to know the device's baud rate before I instantiate the SerialPort class.
I tried for my serial usb devices. Keep changing the baud rate and check. ComboBox contains series of possible baudrates.
public void initConfig(SerialPort serialPort)
{
// you can assign these values in a combo box
string[] ports= "{COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8"};
//you can assign these values in a combo box in a string format
int[] baudRate = { 4800, 9600, 19200, 38400, 57600, 115200, 230400 };
serialPort.PortName = ports[0]; //else get from combobox : portCombobox.SelectedItem
serialPort.BaudRate = baudRate[0];
//serialPort.BaudRate = Int32.Parse(speedComboBox.SelectedItem.ToString());
//you can have controls to store and change these values if required
serialPort.Handshake = System.IO.Ports.Handshake.None;
serialPort.Parity = System.IO.Ports.Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = System.IO.Ports.StopBits.One;
serialPort.ReadTimeout = 200;
serialPort.WriteTimeout = 50;
}
change the strings into respective types and call open.
finally:
public void callingMethod() //or your connect event attached control
{
SerialPort serialPort = new SerialPort();
initConfig(serialPort);
try
{
serialPort.Open();
}
catch
{
MessageBox.Show("Error: Unable to Open the serial interface !");
return;
}
}
Depending upon the details of what you connect to etc. You can loop through a list of baud rates, attempt to connect and then perform an echo test. If you connect at the wrong rate, your echo will be returned as garbage instead of the string you sent. This methodology is working for me.
I'm attempting to write a C# library which looks at all available USB serial ports on a Raspberry Pi so that I can enumerate, identify and communicate with a set of Arduinos connected to the Pi via a USB hub.
I am able to make this work on my windows machine (several Arduinos connected to my desktop computer) and have even been able to make it work on my Pi however, I am struggling to understand how to generalize the fix.
If I attempt to run the program by itself on the Pi, I am able to open the serial port and send data however, I cannot receive anything from the Arduinos: I get timeout exceptions. I understand that Mono's implementation of SerialPort is limited and I must use SerialPort.ReadByte() instead of Readline() and the data received events (my solution is based on code from HowToSystemIOPorts). My Serial port enumeration is using a method outlined in another stack exchange response here.
My timeout is currently set to 4 seconds, which is several orders of magnitude longer than I expect to receive the message.
After a lot of googling, I came across mention of using minicom to initialize the serial port here, which to my surprise allowed me to receive data from the Arduino. The biggest drawback is that I need to initialize the port using minicom and leave the process opening each time I boot the Pi. I also can't seem to figure out how to make this work with multiple Arduinos.
Here is what I have tried so far:
Updated the Pi firmware and software to their latest versions
Attempted to use both an Arduino MEGA 2560 R3 and Arduino UNO
Changed the owner of the tty* ports (ttyACM0 and ttyUSB0 in this case) to both my user and group
Successfully configured the port via minicom, left the process running and start the program and read/wrote data. A manual process which only seems to work for one Arduino at a time
Successfully run the program in Windows without fault
Verified the Arduinos are recognized by the Pi running "dmesg | grep tty"
Here is what I hope to solve:
Automatic setup/initialization of the Arduino serial ports. Whether through a shell script run before the main program or within Mono code so that the code below can run as intended.
Here is my connection code:
public bool StartArduinoComms()
{
string[] ports = GetPortNames();
foreach (string port in ports)
{
mLogger.LogMessage(ProsthesisCore.Utility.Logger.LoggerChannels.Arduino, string.Format("Found serial port {0}", port));
}
bool foundCorrectArduino = false;
var idPacket = new ArduinoMessageBase();
idPacket.ID = ArduinoMessageValues.kIdentifyValue;
string jsonOutput = Newtonsoft.Json.JsonConvert.SerializeObject(idPacket);
foreach (string port in ports)
{
SerialPort serialPort = new SerialPort(port, kArduinoCommsBaudRate);
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
//Only check unopened ports
if (!serialPort.IsOpen)
{
serialPort.Open();
//Disable telemtry just incase
var toggle = new { ID = ArduinoMessageValues.kTelemetryEnableValue, EN = false };
string disableTelem = Newtonsoft.Json.JsonConvert.SerializeObject(toggle);
serialPort.Write(disableTelem);
//Discard any built up data
serialPort.DiscardInBuffer();
serialPort.Write(jsonOutput);
serialPort.ReadTimeout = kIDTimeoutMilliseconds;
string response = string.Empty;
for (int i = 0; i < kNumRetries; ++i)
{
try
{
//This is guaranteed to timeout if not configured through minicom
response = ReadLine(serialPort);
break;
}
//Catch case where the serial port is unavailable. MOve to next port
catch (TimeoutException)
{
continue;
}
}
if (!string.IsNullOrEmpty(response))
{
//Perform response validation
}
else
{
//Got no response
}
if (!foundCorrectArduino)
{
serialPort.Close();
}
}
}
return foundCorrectArduino;
}
/// <summary>
/// From https://stackoverflow.com/questions/434494/serial-port-rs232-in-mono-for-multiple-platforms
/// </summary>
/// <returns></returns>
private static string[] GetPortNames()
{
int p = (int)Environment.OSVersion.Platform;
List<string> serial_ports = new List<string>();
// Are we on Unix?
if (p == 4 || p == 128 || p == 6)
{
string[] ttys = System.IO.Directory.GetFiles("/dev/", "tty*");
foreach (string dev in ttys)
{
//Arduino MEGAs show up as ttyACM due to their different USB<->RS232 chips
if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB") || dev.StartsWith("/dev/ttyACM"))
{
serial_ports.Add(dev);
}
}
}
else
{
serial_ports.AddRange(SerialPort.GetPortNames());
}
return serial_ports.ToArray();
}
Have a look at stty command. It will let you set/read teminal settings
http://linux.about.com/od/lna_guide/a/gdelna38t01.htm will give a rundown on it's use.
It would be easier to call out to than minicom, and the settings stay on the device.
I have done something like the same as you before.
I had to read and write data through USB Serial adapter, and didnt use minicom.
It may not be god code but i found that inorder to read the data I could create a new thread and have that check for data, my code include a lot of stuff but basicly i did this:
System.Threading.Thread newThread;
newThread = new System.Threading.Thread(this.check_get_data);
and the check_get_data method
public void check_get_data ()
{
byte tmpByte = 0;
while (m_objSerialPort.BytesToRead != 0) {
tmpByte = (byte)m_objSerialPort.ReadByte ();
DoSomethingWithByte(tmpByte);
Thread.Sleep(20);
}
}
this is currently running with two usbserials. dont know if it helps but hope you find your solution