I'm trying to communicate back and forth with an Arduino via serial through a C# program. I've got a basic working example, but it's not pretty and not fault-less. I've included it at the end. I have done some research online, but it's difficult to look for something when you don't know all the details and intricacies of what you're looking for.
I'm under the impression that good serial communication protocol should include a handshake, send data and wait for response, receive data and send response, data clocking, etc. My question is how best to implement this. My existing code is one-way, from C# to the arduino's serial port, and it just sends the data and assumes that it was received. It's also very simple, sending just two bytes (a 1 or 0 to turn on or off an LED, and a 0-255 to control the LED's brightness via PWM). I'd like to be able to send sensor data or confirmation that the data was received and acted on, back from the arduino to the program.
I'm also curious about syncing. Does that go hand in hand with the handshake (no pun intended)? How can I reliably send data either way and be assured that nothing will be lost due to being out of sync?
Anywho, I've rambled, sorry. Hopefully someone can shed a little learning light on this subject. Here's my current messy code:
Arduino:
#define LEDPin 9
byte SerialCmd = 0;
byte SerialVal = 0;
void setup() {
Serial.begin(9600);
pinMode(LEDPin, OUTPUT);
digitalWrite(LEDPin, LOW);
}
void loop() {
while(Serial.available() > 1) {
SerialCmd = Serial.read();
SerialVal = Serial.read();
switch(SerialCmd) {
case 0:
digitalWrite(LEDPin, LOW);
break;
case 1:
analogWrite(LEDPin, SerialVal);
break;
}
}
}
C#:
using System;
using System.Windows.Forms;
namespace ArduinoTest2
{
public partial class Form1 : Form
{
byte[] SerialData = new byte[] { 0, 0 };
public Form1()
{
InitializeComponent();
serialPort1.PortName = "COM3";
serialPort1.BaudRate = 9600;
serialPort1.Open();
}
public void SerialWrite()
{
serialPort1.Write(SerialData, 0, 2);
}
private void button1_Click(object sender, EventArgs e)
{
SerialData[0] = 1;
SerialWrite();
}
private void button2_Click(object sender, EventArgs e)
{
SerialData[0] = 0;
SerialWrite();
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
SerialData[1] = Convert.ToByte(trackBar1.Value);
label1.Text = trackBar1.Value.ToString();
SerialWrite();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
serialPort1.Close();
}
}
}
Related
So, I am trying to read data from the serial port to communicate with an Arduino. Here is the code I am using:
public partial class Form1 : Form
{
SerialPort port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
public Form1()
{
InitializeComponent();
port.Open();
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
}
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
this.Invoke(new EventHandler(DoUpDate));
}
private void DoUpDate(object s, EventArgs e)
{
textBox1.AppendText(port.ReadLine() + "\r\n");
}
}
But the result I get in the textBox is (check the picture):
The value read should be 975 but I got separated values as well as many empty lines.
Any help is apperciated.
EDIT#1:
Here is the Arduino code:
int sensorPin=A0;
int sensorValue=0;
void setup()
{
Serial.begin(9600);
}
void loop()
{
sensorValue=analogRead(sensorPin);
Serial.print(sensorValue);
Serial.print("\n");
}
And here is the result when I click on the serial reader within arduino IDE (which what C# code should show)
EDIT#2
After thinking more about the problem, I think that the C# code is very fast that it is reading uncomplete data but I don't have a solution for it, do you know anything I can try to solve it?
I'm not fluent in C#, but maybe you should wait for data to be in the Serial buffer before trying to read them. A method for that is to use port.ReadExisting() as it can be seen here!
Hope it helps!
Insert a 20ms delay in your loop()
I'm trying to figure out, how to control two servos separately from WinForms C# desktop application.
In C# initialization:
myport.PortName = "COM6";
myport.BaudRate = 9600;
myport.Open();
Controlled by trackBar1:
private void trackBar1_Scroll(object sender, EventArgs e)
{
if (myport.IsOpen)
{
myport.WriteLine(trackBar1.Value.ToString());
}
}
Arduino code moves servos with pin 9 and 11, one goes to left, anther to right side synchronously :
#include <Servo.h>
Servo servo1;
Servo servo2;
int val;
void setup() {
Serial.begin(9600);
servo1.attach(9);
servo2.attach(11);
}
void loop() {
val = Serial.parseInt();
if(val != 0){
servo1.write(val);
servo2.write(val);
}
}
to control direction, I can create separate function void for servo1 and servo2 with different angle value, but I can't figure out, how to get separate control for each servo from C# application. I'm not sure, what I have to add in Arduino uploaded code and C#, for example, if I want control servo2 from pin 11 with trackBar2 and servo1 from pin 9 with trackBar1:
private void trackBar2_Scroll(object sender, EventArgs e)
{
if (myport.IsOpen)
{
myport.WriteLine(trackBar2.Value.ToString());
}
}
Any advice, guide or example would be very helpful
One option would be, instead of just reading ints on your Arduino, wait for character 'A' or 'B', then read an int. If the character is 'A' move servo1, if the character is 'B' move servo2. In your .net app, Write 'A' or 'B' before sending the trackbar value
Note: Untested
// in Arduino
void loop() {
switch(Serial.read())
{
case 'A':
servo1.write(Serial.parseInt());
break;
case 'B':
servo2.write(Serial.parseInt());
break;
}
}
// in c#
myport.Write('B')
myport.WriteLine(trackBar2.Value.ToString());
I have device that can connect to my laptop via blue-tooth as COM5. The device has a Pulse sensor. I want to draw data coming from sensor to graph. However when i connected to COM5 the serialport_Datarecieved event is not triggered. I tried device using matlab. It takes and draws data but i cant get data in c#. I checked the connection status of device and it is ok. I tried to change DtrEnabled and RtsEnapled properties but not worked.
private void Form1_Load(object sender, EventArgs e)
{
cmbPortList.Items.AddRange(SerialPort.GetPortNames());
cmbPortList.Sorted = true;
cmbPortList.SelectedIndex = 0;
this.serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(serialPort1_DataReceived);
}
private void btnOpenPort_Click(object sender, EventArgs e)
{
try
{
serialPort1.PortName = cmbPortList.Text;
serialPort1.BaudRate = 9600;
serialPort1.DataBits = 8;
serialPort1.ReadTimeout = 500;
serialPort1.WriteTimeout = 500;
serialPort1.Handshake = Handshake.None;
if (!serialPort1.IsOpen)
{
btnRun.Enabled = true;
serialPort1.Open();
}
}
catch (Exception ex)
{
serialPort1.Close();
}
}
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
while (serialPort1.BytesToRead > 0)
{
Thread.Sleep(50);
byte[] buffer = new byte[serialPort1.BytesToRead];
serialPort1.Read(buffer, 0, buffer.Length);
}
}
I cant read any data in buffer. There is led is flashing while device is not connected with via blue-tooth. So i am absolutely sure i connected to device.
Is problem about Bluetooth or code? Should i use another library to communicate blue tooth device?
I have read links below.
SerialPort fires DataReceived event after close
SerialPort not receiving any data
This may have less to do with the SerialPort and more to do with the way that Winforms threads are interacting with the serial port's background worker threads. See the solution to this for more info.
I think the designer of the circuit requests data from device with 's'. It must be about its protocol or hex code. I have found that code in matlab sample of circuit % Request Data fprintf(s,'s'); That's why i can read data when i use serialport.Write("Blast"); Also i tried all letters. Only 's' char triggers the event.
I am using C# serial port controlling gsm modem. Now in Mikroelectronia USART Terminal
after sending:
AT+CUSD=1,"*778#",15
It receives:
AT+CUSD=1,"*778#",15
OK
+CUSD: 0,"Balance: 0.00 TK. Validity: 29-Jul-13. Bonus: 0.00TK. Free Min: 0. Dial *121*2092# for 3 hit songs of Ashiqui-2 as ur Caller
Tunetk.10",64
But in C# after sending data
AT+CUSD=1,"*778#",15
it returns:
AT+CUSD=1,"*778#",15
OK
+CUSD: 0,"Balance: 0.00 TK. Validity: 29-Jul-13. Bonus: 0.00TK. Free Min: 0. Dial *121*2092# for 3 hit songs of Ashiqui-2 as ur Caller Tune
That means in C# it does not receive any data after "Caller Tune". Why is that happening? Part of C# code I have used is
private void Form1_Load(object sender, EventArgs e)
{
sp1.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
}
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var valueOfPort = sp1.ReadExisting();
textBox1.AppendText(valueOfPort);
}
private void button1_Click(object sender, EventArgs e)
{
TextBox.CheckForIllegalCrossThreadCalls = false;
try
{
if (!sp1.IsOpen)
{
sp1.Open();
}
sp1.Write(textBox2.Text+"\r");
}
catch(Exception ex)
{
MessageBox.Show(string.Format("Exception : {0}", ex.Message), "Port Error");
}
}
the TextBox.CheckForIllegalCrossThreadCalls = false; makes me very suspicious, I think that is what is breaking your program, simply just invoke to update the text box properly and I think it will work. Just change the event code to the following:
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new SerialDataReceivedEventHandler(sp_DataReceived), sender, e);
}
else
{
var valueOfPort = sp1.ReadExisting();
textBox1.AppendText(valueOfPort);
}
}
What this will do is check if you are running on the correct thread, if not it restarts the function again on the UI thread with the same arguments. Also be sure to remove the TextBox.CheckForIllegalCrossThreadCalls = false;
UPDATE: After re-reading the MSDN I found this remark
This method returns the contents of the stream and internal buffer of the SerialPort object as a string. This method does not use a time-out. Note that this method can leave trailing lead bytes in the internal buffer, which makes the BytesToRead value greater than zero.
Try checking to see if there is more data to read after calling ReadExisting.
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new SerialDataReceivedEventHandler(sp_DataReceived), sender, e);
}
else
{
while(sp1.BytesToRead > 0)
{
var valueOfPort = sp1.ReadExisting();
textBox1.AppendText(valueOfPort);
}
}
}
Have you defined your device's EOL and set it correctly in your program?
C# serial port does not necessarily fire up DataReceive at the start of the message nor does it know where the message ends. It may terminate anywhere in the message. You should try explicitly set the EOL which can be uniquely identified by the port in order for it to buffer incoming message and to return it once it is completed.
// e.g. I normally used (carriage return + newline) instead of just either one
sp1.NewLine = "\r\n";
Hi I'm trying to program a simple C# WPF that displays time information on a virtual scoreboard in real time from a timing system. I'm fairly new to programming so in depth explanation would be appreciated.
I have created a new thread to handle the incoming data from the COM port and as the app is developed this data will be interpreted. For now I just wanted to display the raw information (in hex) that is coming from the timer into a textbox. This works but not as intended. I am receiving tons of duplicate information, my only explanation is I am reading the data too slowly or its reading the same byte over and over. What I would like to happen is to take out each byte and display them, all controlled by one start/stop button.
Possible solutions include storing the entire buffer in a list or array which I'm not quite sure of yet, I don't want to add so many threads that the program freezes everything up.
Here is my code so far (I'm new to pretty much all the code I have written here, so if anything is bad practice please let me know):
public partial class MainWindow : Window
{
SerialPort comms;
Thread commThread;
bool flag;
string message;
public MainWindow()
{
InitializeComponent();
comms = new SerialPort();
}
private void PortControl_Click(object sender, RoutedEventArgs e)
{
if (!comms.IsOpen)
{
PortControl.Content = "Stop";
comms.PortName = "COM1";
comms.BaudRate = 9600;
comms.DataBits = 8;
comms.StopBits = StopBits.One;
comms.Parity = Parity.Even;
comms.ReadTimeout = 500;
comms.ReceivedBytesThreshold = 1;
commThread = new Thread(new ThreadStart(Handle));
comms.Open();
comms.DataReceived += new SerialDataReceivedEventHandler(ReadIn);
}
else
{
PortControl.Content = "Start";
flag = false;
comms.DataReceived -= ReadIn;
commThread.Join();
comms.Close();
}
}
private void ReadIn(object sender, SerialDataReceivedEventArgs e)
{
if (!commThread.IsAlive)
{
flag = true;
commThread.Start();
}
}
private void Handle()
{
while (flag)
{
if (comms.IsOpen)
{
try
{
message = comms.ReadByte().ToString("X2");
Dispatcher.BeginInvoke((Action)(() =>
{
ConsoleBox.Text += message + " ";
}));
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
}
Here is one solution.
The serial port is receiving the data in its own thread, and you should read the incoming bytes in the data received handler.
I propose to read the data and add it to a thread-safe FIFO list in the data received handler and read the data from the list in the main thread.
See my solution in post Serial port reading + Threads or something better?