C# Serial Communication to Arduino - c#

I'm working on a project that involves my client software sending data to a Arduino microcontroller, AtMega32U4, through serial communication. I've looked through many answered questions so far yet none of them were specific to my problem. However, I believe my problem may be limited to threading issues or Arduino autoreset problems.
Code 1:
public MainForm()
{
InitializeComponent();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
serialPort1.DtrEnable = true;
//serialPort1.RtsEnable = true;
}
private void button3_Click(object sender, EventArgs e)
{
// Disables button while processing
button3.Enabled = false;
GetDir dir = new App.GetDir();
dir.getCoords(Origin.Text, Destination.Text, Application.StartupPath + #"\temp2.html", "temp2.xml");
dataBrowser.Navigate(Application.StartupPath + #"\temp2.html");
dataBrowser.Update();
waypoints = dir.coordsLat.Length;
counter = dir.coordsLat.Length;
coords = new double[dir.coordsLat.Length, 2];
for (int i = 0; i < counter; i++)
{
coords[i, 0] = (Convert.ToDouble(dir.coordsLat[i]));
coords[i, 1] = (Convert.ToDouble(dir.coordsLon[i]));
}
//serialPort1.Close();
//System.Threading.Thread.Sleep(1000);
if (serialPort1.IsOpen && !doubleClick)
{
serialPort1.Close();
System.Threading.Thread.Sleep(2000);
try
{
serialPort1.Open();
}
catch (Exception exception)
{
MessageBox.Show(exception.Message, "Cannot open serial port");
}
System.Threading.Thread.Sleep(2000);
}
else
{
if (!serialPort1.IsOpen)
{
try
{
serialPort1.Open();
doubleClick = true;
}
catch (Exception exception)
{
MessageBox.Show(exception.Message, "Cannot open serial port");
}
System.Threading.Thread.Sleep(2000);
serialPort1.Write("^");
System.Threading.Thread.Sleep(1000);
Console.WriteLine('^');
//button3.Enabled = true;
}
}
}
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//System.Threading.Thread.Sleep(1000);
readData = serialPort1.ReadLine();
Console.WriteLine(readData);
// If microcontroller sends "&", it is ready to receive next piece of data
if (readData == "&")
{
sendRequest = true;
}
else
{
sendRequest = false;
}
// Write next piece of data to microcontroller if it is ready
if (sendRequest)
{
this.BeginInvoke( new EventHandler (write_serialPort1));
}
}
In during the debugging of code 1, the event handler (serialPort1_DataReceived) never gets called. In this process, somehow button3_click gets called twice as the console outputs '^' twice. Afterwards, the client stalls since there is nothing beind received. Keep in mind that the Arduino will respond with an ampersand ('&') once it has received the circumflex ('^'). The Arduino code has been tested on the Arduino IDE and appears to be working fine. I believe the problem with button3_click being called twice comes from the button3_down and button3_up.
However, I was able to bypass this issue with Code 2. But also hit another brick wall.
Code 2 :
private void button3_Click(object sender, EventArgs e)
{
// Disables button while processing
button3.Enabled = false;
GetDir dir = new App.GetDir();
dir.getCoords(Origin.Text, Destination.Text, Application.StartupPath + #"\temp2.html", "temp2.xml");
dataBrowser.Navigate(Application.StartupPath + #"\temp2.html");
dataBrowser.Update();
waypoints = dir.coordsLat.Length;
counter = dir.coordsLat.Length;
coords = new double[dir.coordsLat.Length, 2];
for (int i = 0; i < counter; i++)
{
coords[i, 0] = (Convert.ToDouble(dir.coordsLat[i]));
coords[i, 1] = (Convert.ToDouble(dir.coordsLon[i]));
}
serialPort1.Close();
try
{
serialPort1.Open();
}
catch (Exception exception)
{
MessageBox.Show(exception.Message, "Cannot open serial port");
}
if (serialPort1.IsOpen)
{
System.Threading.Thread.Sleep(2000);
using (serialPort1)
{
serialPort1.Write("^");
System.Threading.Thread.Sleep(1000);
Console.WriteLine("^");
serialPort1.Close();
System.Threading.Thread.Sleep(5000);
}
}
else
{
button3.Enabled = true;
}
}
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//SerialPort sp = (SerialPort)sender;
System.Threading.Thread.Sleep(10000);
/*if (!serialPort1.IsOpen)
{
serialPort1.Close();
System.Threading.Thread.Sleep(10000);
serialPort1.Open();
System.Threading.Thread.Sleep(10000);
}*/
//serialPort1.Open();
//using (sp)
using (serialPort1)
{
serialPort1.Open();
System.Threading.Thread.Sleep(5000);
readData = serialPort1.ReadExisting();
Console.WriteLine(readData);
// If microcontroller sends "&", it is ready to receive next piece of data
if (readData == "&")
{
sendRequest = true;
}
else
{
sendRequest = false;
}
// Write next piece of data to microcontroller if it is ready
if (sendRequest)
{
this.BeginInvoke(new EventHandler(write_serialPort1));
}
}
}
In Code 2, the event handler does get called and the button3_click only runs once. But when it tries to open the port, it returns the error 'Access to Port X denied'. Furthermore, I wish I didn't have to close and open the ports like this, but when the event handler is called (in an earlier code) it returned the error that the COM Port was not opened. In order to satisfy that error, I had to close it and reopen it during button3_click and event handling.
I've added a lot of delay in the code after I read about many problems dealing with the threading issues with serial communication. I had even tried a minute delay in hopes of a thread ending to solve the problem. However, no luck there.
I also specified my serial port in the MainForm designer instead of declaring it in the code (At first I did both and realized it was redundant). I'm not sure if this contributes to the problem, but I've seen examples of both being used.
Lastly, it could definitely deal with the Arduino auto resetting everytime a serial connection has been made (eg. opening and closing a port). In summary, it seems be sending data through serial, but unable to read the incoming data from serial.
Thank you for reading this and if someone could point me in the right direction, it would be very much appreciated.
Edit #1: Even after using BeginInvoke in Code 1, it still deadlocks because the event handler was never called.
Edit #2: Edits to Code 1 as per newbie's suggestions.
Edit #3: Added mainform initialization and updated Code 1 to current state.
Edit #4: Deleted (Commented out) the sleep at the event handler. I was sleeping during the event handler, thus I couldn't receive anything that the microcontroller would send to me. Code works fine as expected now.

Make sure you are using COM1, if you do not COM1 serial port,
change through Computer -> Device Manager -> Ports (COM & LPT) ->
Select the COM to be changed -> Port Settings -> Advanced -> ComPort Number -> select COM1.
Make sure that you have installed jumper / connect with a screwdriver
between pin2 and pin3 of COM1.
Add button1 and textBox1 to Form and run this program
using System;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.Text;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
const int MAX_BUFFER = 100;
int i = 0;
byte[] DataReceived = new byte[MAX_BUFFER];
SerialPort serialPort = new SerialPort();
public Form1() {
InitializeComponent();
serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);
}
void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) {
// wait data ready
Thread.Sleep(500);
// while data ready in buffer
while (serialPort.IsOpen && serialPort.BytesToRead > 0) {
// read data serial
DataReceived[i] = Convert.ToByte(serialPort.ReadByte());
// counter data
i++;
// reset conter if more then maxvalue
if (i >= MAX_BUFFER) {
i = 0;
}
}
if (i == 1 && DataReceived[0] == ASCIIEncoding.ASCII.GetBytes("^")[0]) {
this.textBox1.Invoke(new Action(() => {
this.textBox1.Text = ASCIIEncoding.ASCII.GetString(DataReceived, 0, 1);
}));
}
}
public void InitSerialPort() {
serialPort.PortName = "COM1";
serialPort.BaudRate = 9600;
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
serialPort.ReceivedBytesThreshold = 1;
}
private void Form1_Load(object sender, EventArgs e) {
// initialize serial port
InitSerialPort();
// assure port is closed before open it
if (serialPort != null && serialPort.IsOpen) {
serialPort.Close();
}
serialPort.Open();
}
private void button1_Click(object sender, EventArgs e) {
if (serialPort.IsOpen) {
serialPort.Write("^");
// wait data sent
Thread.Sleep(500);
}
}
}
}

In accordance to my 4th edit, deleted (Commented out) the sleep at the event handler. I was sleeping during the event handler, thus I couldn't receive anything that the microcontroller would send to me. Code works fine as expected now. Nothing was wrong with the serial ports on either components.

Related

Serial Messages Issue

I'm receiving in my serial port the message "Hello World!CRLF" (no quotes) at every 1 second, and I'm using ReadExisting() to read the message, but I can't understand why I'm receiving lots of "\0" before every character.
PuTTy seems to handle the messages just fine, so my code must be the problem. Could someone please help me to figure this out?
Part of my code:
void button1_Click(object sender, EventArgs e)
{
try
{
_serialPort = new SerialPort(cbPort.Text);
_serialPort.BaudRate = Int32.Parse(cbBaudrate.SelectedItem.ToString());
_serialPort.Parity = Parity.None;
_serialPort.StopBits = StopBits.One;
_serialPort.DataBits = 8;
_serialPort.ReadTimeout = 500;
_serialPort.Open();
if (_serialPort.IsOpen)
{
try
{
ReadSerialData();
}
catch (TimeoutException) { }
}
}
catch (Exception er){}
}
private void ReadSerialData()
{
try
{
ReadSerialDataThread = new Thread(ReadSerial);
ReadSerialDataThread.Start();
}
catch (Exception e){}
}
private void ReadSerial()
{
try
{
while (_serialPort.BytesToRead >= 0)
{
readserialvalue = _serialPort.ReadExisting();
ShowSerialData(readserialvalue);
Thread.Sleep(1);
}
}
catch (Exception e){}
}
public delegate void ShowSerialDatadelegate(string r);
private void ShowSerialData(string s)
{
if (rtb_msg.InvokeRequired)
{
ShowSerialDatadelegate SSDD = ShowSerialData;
Invoke(SSDD, s);
}
else
{
rtb_msg.AppendText(readserialvalue);
}
}
As sugested by #Hans Passant changing the encoding to BigEndian solved the main issue.
Still getting lots of invalid unicode chars, but I think is best to open another thread. Thank you all for the support.
_serialPort.Encoding = System.Text.Encoding.BigEndianUnicode;
Are you sure that you want to read from the serial port even if there are 0 bytes to be read? If not, you might want to try changing:
while (_serialPort.BytesToRead >= 0)
to
while (_serialPort.BytesToRead > 0)
I suspect you may be reading from the serial port with 0 bytes to be read, which could return a null (\0) value

Console does not show the entire answar from serial port

I am working with BC66 modem from Quectel. On my program when I type some AT commands to the modem it can not show the entire response from the modem, but instead the reply of the AT command. see figur below:
Instead using an terminal program and type the AT commands i got the status of my request. See figure below:
This is what I am expecting in my program, but unfortunately not happened.
The code snip:
static void Main(string[] args)
{
if (InitPort() == true)
{
Console.WriteLine("Port initialized");
TestAT();
}
}
private static void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] data = new byte[serialPort.BytesToRead];
serialPort.Read(data, 0, data.Length);
Console.WriteLine(Encoding.ASCII.GetString(data));
//Console.WriteLine(serialPort.ReadExisting());
}
private static void TestAT()
{
for (int i = 0; i < 10; i++)
{
serialPort.Write("at");
Thread.Sleep(2000);
serialPort.Write("at+cereg");
Thread.Sleep(2000);
}
}
private static bool InitPort()
{
serialPort.PortName = "COM51";
serialPort.BaudRate = 115200;
serialPort.DataBits = 8;
serialPort.Parity = Parity.None;
serialPort.StopBits = StopBits.One;
serialPort.DataReceived += SerialPort_DataReceived;
try
{
serialPort.Open();
return true;
}
catch
{
return false;
}
}
Any suggestion on what is wrong?
As you can see here on page 7 you need to finish your commands with <CR><LF>.
The “AT” or “at” prefix must be set at the beginning of each command
line. Entering <CR> will terminate a command line.
in C# the equivalent is \r\n
As #Mong Zhu pointed out, you can achieve that by using
serialPort.WriteLine("at");

SerialDataReceivedEventHandler not working c#

I'm trying to connect to arduino with C# program in windows forms.
I can send data from c# to the arduino but I want to get data from the arduino to the C# program.
I've tried the SerialDataReceivedEventHandler, but I don't get the data after the form is built...
what can I do?
public Form1()
{
InitializeComponent();
Init();
}//end form 1
private void Init()
{
try
{
arduinoPort = new SerialPort();
arduinoPort.BaudRate = 9600;
arduinoPort.PortName = "COM4";
arduinoPort.Handshake = Handshake.None;
arduinoPort.RtsEnable = true;//request to send true
arduinoPort.DtrEnable = true;//arduino can send messages to the c# program
arduinoPort.DataReceived += new SerialDataReceivedEventHandler(GetFromArduino);
arduinoPort.Open();
}//end try
catch (Exception ex) { MessageBox.Show(ex.Message); }
}//end init
private void GetFromArduino(object sender, SerialDataReceivedEventArgs e)
{
//string arduinoInputString = arduinoPort.ReadLine();
//Invoke(new Action(() => label1.Text = arduinoInputString));
MessageBox.Show("does it work?");
}//end get from arduino
MessageBox.Show isn't going to work from the DataReceived event
SerialPort.DataReceived Event
The DataReceived event is raised on a secondary thread when data is
received from the SerialPort object. Because this event is raised on a
secondary thread, and not the main thread, attempting to modify some
elements in the main thread, such as UI elements, could raise a
threading exception. If it is necessary to modify elements in the main
Form or Control, post change requests back using Invoke, which will do
the work on the proper thread
at best you need to do something like this
this.Invoke(new Action(() => { MessageBox.Show(this, "text"); }));
However, if you really want to know if that event is fired, then use a break-point
Using Breakpoints
Lastly, if the event isnt fired, then you will have to consult the documentation for the device for the appropriate configuration of the device and the serial port.
Make some handler method, for example
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort comm = (SerialPort)sender;
string incoming_Data = comm.ReadExisting();
this.BeginInvoke((MethodInvoker)delegate()
{
Console.WriteLine(incoming_Data + "\n");
});
}
Then, make this method to the subscriber of DataRecieved event
arduinoPort.DataReceived += DataReceivedHandler;
Source: https://social.msdn.microsoft.com/Forums/vstudio/en-US/bd8f7ac8-67d5-4eb5-b679-c595f9c7536d/how-to-print-out-text-from-serialport-datareceived-event?forum=netfxbcl
I don't know if it matters, but this what i did and it solved:
1. in the setup function of the arduino program i had a loop that ran on all the pins, now i specified it to the pins i really use.
2. i don't open the port in the form.cs, but in the program.cs and run a while loop that just check if the port is open. then, it gets the data without disturbing the form to run...
**in the Program.cs:**
public static SerialPort arduinoPort { get; set; }
public static string arduinoInputString { get; set; }
[STAThread]
static void Main()
{
arduinoPort = new SerialPort();
try
{
arduinoPort = new SerialPort();
arduinoPort.BaudRate = 250000;
arduinoPort.PortName = "COM4";
//arduinoPort.Handshake = Handshake.None;
//arduinoPort.RtsEnable = true;//request to send true
//arduinoPort.DtrEnable = true;//arduino can send messages to the c# program
////arduinoPort.DataReceived += new SerialDataReceivedEventHandler(GetFromArduino);
arduinoPort.DataReceived += DataReceivedHandler;
arduinoPort.Open();
}//end try
catch (Exception ex) { Console.WriteLine((ex.Message)); }
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
while (arduinoPort.IsOpen)//read data if the port is open
{
}
}//end main
static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort comm = (SerialPort)sender;
arduinoInputString = comm.ReadExisting();
Form1.label1.Text = arduinoInputString;//label to show the input string from arduino
}//end get data from arduino

Serial COM read returns null [duplicate]

I am developing program which need to interact with COM ports.
By learning from this Q&A: .NET SerialPort DataReceived event not firing, I make my code like that.
namespace ConsoleApplication1
{
class Program
{
static SerialPort ComPort;
public static void OnSerialDataReceived(object sender, SerialDataReceivedEventArgs args)
{
string data = ComPort.ReadExisting();
Console.Write(data.Replace("\r", "\n"));
}
static void Main(string[] args)
{
string port = "COM4";
int baud = 9600;
if (args.Length >= 1)
{
port = args[0];
}
if (args.Length >= 2)
{
baud = int.Parse(args[1]);
}
InitializeComPort(port, baud);
string text;
do
{
String[] mystring = System.IO.Ports.SerialPort.GetPortNames();
text = Console.ReadLine();
int STX = 0x2;
int ETX = 0x3;
ComPort.Write(Char.ConvertFromUtf32(STX) + text + Char.ConvertFromUtf32(ETX));
} while (text.ToLower() != "q");
}
private static void InitializeComPort(string port, int baud)
{
ComPort = new SerialPort(port, baud);
ComPort.PortName = port;
ComPort.BaudRate = baud;
ComPort.Parity = Parity.None;
ComPort.StopBits = StopBits.One;
ComPort.DataBits = 8;
ComPort.ReceivedBytesThreshold = 9;
ComPort.RtsEnable = true;
ComPort.DtrEnable = true;
ComPort.Handshake = System.IO.Ports.Handshake.XOnXOff;
ComPort.DataReceived += OnSerialDataReceived;
OpenPort(ComPort);
}
public static void OpenPort(SerialPort ComPort)
{
try
{
if (!ComPort.IsOpen)
{
ComPort.Open();
}
}
catch (Exception e)
{
throw e;
}
}
}
}
My problem is DataReceived event never gets fired.
My program specifications are:
Just .net console programming
I use VSPE from http://www.eterlogic.com
My computer has COM1 and COM2 ports already.
I created COM2 and COM4 by using VSPE.
I get output result from mystring array (COM1, COM2, COM3, COM4)
But I still don't know why DataReceived event is not fired.
Updated
Unfortunately, I still could not make to fire DataReceived event in any way.
So, I created new project by hoping that I will face a way to solve.
At that new project [just console application], I created a class...
public class MyTest
{
public SerialPort SPCOM4;
public MyTest()
{
SPCOM4 = new SerialPort();
if(this.SerialPortOpen(SPCOM4, "4"))
{
this.SendToPort(SPCOM4, "com test...");
}
}
private bool SerialPortOpen(System.IO.Ports.SerialPort objCom, string portName)
{
bool blnOpenStatus = false;
try
{
objCom.PortName = "COM" + portName;
objCom.BaudRate = 9600;
objCom.DataBits = 8;
int SerParity = 2;
int SerStop = 0;
switch (SerParity)
{
case 0:
objCom.Parity = System.IO.Ports.Parity.Even;
break;
case 1:
objCom.Parity = System.IO.Ports.Parity.Odd;
break;
case 2:
objCom.Parity = System.IO.Ports.Parity.None;
break;
case 3:
objCom.Parity = System.IO.Ports.Parity.Mark;
break;
}
switch (SerStop)
{
case 0:
objCom.StopBits = System.IO.Ports.StopBits.One;
break;
case 1:
objCom.StopBits = System.IO.Ports.StopBits.Two;
break;
}
objCom.RtsEnable = false;
objCom.DtrEnable = false;
objCom.Handshake = System.IO.Ports.Handshake.XOnXOff;
objCom.Open();
blnOpenStatus = true;
}
catch (Exception ex)
{
throw ex;
}
return blnOpenStatus;
}
private bool SendToPort(System.IO.Ports.SerialPort objCom, string strText)
{
try
{
int STX = 0x2;
int ETX = 0x3;
if (objCom.IsOpen && strText != "")
{
objCom.Write(Char.ConvertFromUtf32(STX) + strText + Char.ConvertFromUtf32(ETX));
}
}
catch (Exception ex)
{
throw ex;
}
return true;
}
}
I am not sure that I face good luck or bad luck because this new class could make fire DataReceived event which is from older console application that is still running. It is miracle to me which I have no idea how this happen.
Let me tell you more detail so that you could give me suggestion for better way.
Finally I created 2 console projects.
First project is the class which I posted as a question yesterday.
Second project is the class called MyTest which could make fire DataReceived event from First project, at the same time when two of the project is running.
Could anyone give me suggestions on how could I combine these two projects as a single project?
ComPort.Handshake = Handshake.None;
The problem is not that the DataReceived event doesn't fire, the problem is that the serial port isn't receiving any data. There are very, very few serial devices that use no handshaking at all. If you set it to None then the driver won't turn on the DTR (Data Terminal Ready) and RTS (Request To Send) signals. Which a serial port device interprets as "the machine is turned off (DTR)" or "the machine isn't ready to receive data (RTS)". So it won't send anything and your DataReceived event won't fire.
If you really want None then set the DTREnable and RTSEnable properties to true. But it is likely you want HandShake.RequestToSend since the device appears to be paying attention to the handshake signals.
If you still have trouble then use another serial port program like Putty or HyperTerminal to ensure the connection and communication parameters are good and the device is responsive. SysInternals' PortMon utility gives a low-level view of the driver interaction so you can compare good vs bad.
I have never worked with VSPE so I'm not sure if that causes the problem. I have worked with a COM port before and I looked up my code. The only main difference is the way you declare the event. You have:
ComPort.DataReceived += OnSerialDataReceived;
I have it like this:
ComPort.DataReceived += new SerialDataReceivedEventHandler(OnSerialDataReceived);
OnSerialDataReceived is your eventhandler. I'm not sure if this will make any difference, but you can try it. I hope this helps!
I had a quite similar problem. In a graphical application (C# win form) I had a class which encapsulate a SerialPort component. The DataReceived event was firing only one time, but then any following data received didn't fire any event. I solved the problem by calling the Close method in my principal form Closed event function.
No idea of why that changes anything, but now it's working.

application hangs when closing a listening port

I'm using com0com to create a part of virtual ports comA/comB, typing the input to comA from hyperterminal and listening on comB in a wpf application. When I run the following code (by triggering Connect), the application successfully connects and is able to get the data from comA, but hangs when I do Disconnect.
public void Connect()
{
readPort = new SerialPort("COMB");
readPort.WriteTimeout = 500;
readPort.Handshake = Handshake.None;
readPort.Open();
readThread = new Thread(Read);
readRunning = true;
readThread.Start();
System.Diagnostics.Debug.Print("connected");
}
public void Disconnect()
{
if (!readRunning)
{
readPort.Close();
}
else
{
readRunning = false;
readThread.Join();
readPort.Close();
}
System.Diagnostics.Debug.Print("disconnected");
}
public void Read()
{
while (readRunning)
{
try
{
int readData = 0;
readData = readPort.ReadByte();
System.Diagnostics.Debug.Print("message: " + readData.ToString());
}
catch (TimeoutException)
{
}
}
}
I tried changing the Read function to a write by using
byte[] writeData = { 1, 2, 3 };
readPort.Write(writeData, 0, 3);
instead of port.readbyte, and it starts working perfectly when disconnecting. Does anyone know if there is anything different about readbyte that could have caused the freeze? Or is it possibly related to com0com?
Just checking back, in case anyone runs into the same issue, I found an alternative way overriding SerialPort.DataReceived like this:
public override void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
byte[] buf = new byte[sp.BytesToRead];
sp.Read(buf, 0, buf.Length);
receivedDataDel(buf);
}

Categories