I'm trying to create a Serial Communication tool on MSVS using C#. it communicates with the Photon MCU and a bluetooth dongle.
When the "start" button is pressed, the UI sends a "1" to the Photon which it first sends the current time stamp and starts streaming data from the function generator. When the "stop" button is pressed, It first sends 10 "2"s (due to the timer issue on the photon's end) which the when the Photon receives, it stops transmitting the function generator's data. Then it sleeps for a second and sends a "3" which it sends another current time stamp. Then the UI discards data in the InBuffer and stops reading data.
connectBT is connected with the start button and the disconnectBT is connected with the stop button.
This is the code that I have right now:
SerialPort serial = new SerialPort();
string recieved_data;
int startBuffer = 0;
private void connectBT(object sender, RoutedEventArgs e)
{
startBuffer++; // keep track of BT open counter
if (serial.IsOpen) Debug.WriteLine("BT Open");
// first time BT is open and BT is not open
if (!serial.IsOpen)
{
if (startBuffer == 1)
{
// COM port properties
serial.PortName = "COM7";
serial.BaudRate = 38400;
serial.Handshake = Handshake.None;
serial.Parity = Parity.None;
serial.DataBits = 8;
serial.StopBits = StopBits.One;
serial.ReadTimeout = 200;
serial.WriteTimeout = 50;
serial.Open();
}
startButton.Content = "Recording";
Send_Data("1"); // tell Photon to start sending data
serial.DiscardInBuffer(); // discard whatever is in inbuffer
serial.DataReceived += new SerialDataReceivedEventHandler(Recieve); // start receiving data
}
// after BT has been opened and start button has been pressed again
else if (serial.IsOpen && startBuffer > 1)
{
startButton.Content = "Recording";
Send_Data("1");
serial.DiscardInBuffer();
serial.DataReceived += new SerialDataReceivedEventHandler(Recieve);
}
}
// stop button is pressed
private void disconnectBT(object sender, RoutedEventArgs e)
{
// send "2" ten times to tell photon to stop transmitting function generator data
int i = 0;
while (i < 10)
{
Send_Data("2");
Thread.Sleep(1);
i++;
}
Thread.Sleep(1000);
Send_Data("3"); // send a 3 to tell photon to send the last time stamp
Thread.Sleep(1000);
serial.DiscardInBuffer(); // discard in buffer
serial.DataReceived -= Recieve; // stop receiving data
//serial.Close(); // close BT
startButton.Content = "Start";
}
private void Recieve(object sender, SerialDataReceivedEventArgs e)
{
recieved_data = serial.ReadLine();
Debug.WriteLine(recieved_data);
}
I'm running into an issue where when I press the "stop" button, the last chunk of data that was sent from the bluetooth is lost. I never receive the last time stamp that I'm supposed to have received when the stop button is pressed. According to our math, we're supposed to be receiving 500 points per second (500Hz) but I only receive about 100 of them.
My theory is that the UI is receiving data at a slower (or a delayed) rate and the serial.DiscardInBuffer discard the received data even before that the data can be printed to the Debug output. I know for a fact that all the data between the first and last I receive are all there because of counter values associated with the data packets. Basically if I were to receive 1~500 data points, I only receive 1~100. I've also tried it with just termite with sending 1,2, and 3 as the UI is supposed to be and I get all the data as I need them. I don't close BT on purpose.
What can I do to prevent this data loss? What am I doing wrong in my code that I shouldn't be doing or be doing for the correct bluetooth protocol? This is my first time writing bluetooth code so I'm fairly unfamiliar with it.
Not sure if that's the cause of your problem, but your Receive has a very big pitfall.
You only read one line per Receive event, and on one event there can be more than one line to read, then they are being accumulated and discarded at the end.
ReadLine is meant to be used in a synchronous way like an stream where you read one line, process it then you write, not to be used with the DataReceived event.
You have two options: spin a new thread in a continuous loop reading with serial.ReadLine() (it will block until a new line is available) or the better approach, read the serial buffer on each Receive event.
To do it like that you can do smething like this:
List<byte> tmpBuffer = new List<byte>();
static byte newLineB = Encoding.ASCII.GetBytes("\n")[0];
void Receive(object sender, SerialDataReceivedEventArgs e)
{
lock (tmpBuffer)
{
while (serial.BytesToRead > 0)
{
byte[] segment = new byte[serial.BytesToRead];
serial.Read(segment, 0, segment.Length);
tmpBuffer.AddRange(segment);
ProcessBuffer();
}
}
}
private void ProcessBuffer()
{
int index = 0;
while ((index = tmpBuffer.IndexOf(newLineB)) > -1)
{
string line = Encoding.ASCII.GetString(tmpBuffer.Take(index + 1).ToArray());
//Do whatever you need to do with the line data
Debug.WriteLine(line);
tmpBuffer.RemoveRange(0, index + 1);
}
}
As you can see, the received data is being stored on a temporal list used as a buffer (yes, an array and using Buffer functions would be faster, but for small messages and for simplicity a list is enough for most cases), then the received data is added to the buffer and when there are no more bytes left the list is processed in search of string lines.
Also note the read is in a loop, I have run in cases where there were data received while the function was being executed and no receive event was fired, so the better for this is to create a loop to read while there is still data.
Thank you all for your response, they all helped me reaching the solution for my issue, but in the end what fixed it was delaying the time between sending the "3" and discarding my inBuffer and closing the Receive connection.
async Task DelayBT()
{
await Task.Delay(100);
}
Thread.Sleep() didn't work because of its nature disabling all action within the thread (which I still needed) so this method worked like a charm. I just called await DelayBT where I needed the delay.
Hope this helps anyone running into the same issue as me.
Related
I am working on a project where I need to be able to communicate between C# and Arduino. I am stuck on an issue where I need to be able to start a timer in c# when data is received from the Arduino, then if the timer runs out, it triggers a relay to turn off power. The code looks something like this.
Arduino
char incoming_char=0;
#define RECEIVER_PIN A0
#define THRESHOLD 80
void setup() {
Serial.begin(9600); //for debug
pinMode(13, OUTPUT);
}
void loop() {
incoming_char=Serial.read();
// read the input on RECEIVER_PIN
int sensorValue = analogRead(RECEIVER_PIN);
if( sensorValue > THRESHOLD ) {
Serial.print("A"); //Send a letter back to VS
}
}
C#
public void TimerTick(object source, ElapsedEventArgs e)
{
Tick++;
System.Diagnostics.Debug.WriteLine("Ticking! " + Tick);
if (Tick > 10)
{
UPCTimer.Stop();
}
}
private void ArduinoSerial_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("We GOT DATA!");
if (UPCTimer.Enabled == false)
{
UPCTimer.Start();
System.Diagnostics.Debug.WriteLine("We started the timer!");
Tick = 0;
}
Right now when the laser is triggered, it just spams WE GOT DATA endlessly until it the item passes through. What I would like to be able to do, is only act on the first character that's sent to VS from the Arduino, then ignore all the other input until the timer is disabled again. is there anyway to do this?
You can add code to the arduino firmware to wait for the analogue signal to drop below the threshold, possibly with hysteresis before allowing for data to be sent again.
Ie add a tripped variable when threshold is tripped to true or 1. Check this value when checking the threshold:
if( tripped == 0 && sensorValue > THRESHOLD )
{
tripped = 1;
//send A to serial port
}
else if( sensorValue < THRESHOLD - hysteresis )
{
tripped = 0;
}
Arduino_DataReceived is an event handler. Something has to subscribe to the event in order for it to do anything. Somewhere in your setup code, you have a line like:
ArduinoSerial.DataReceived += ArduinoSerial_DataReceived;
If you want to ignore this data until the timer is disabled, then immediately after you say "We GOT DATA!" you can unsubscribe by adding a line like:
ArduinoSerial.DataReceived -= ArduinoSerial_DataReceived;
And then you can subscribe again once you are ready to start receiving again.
An alternate approach is to use a class-level boolean to flag when you are ignoring input. Wrap all of your c# code in something like if (!Ignoring) { } and set Ignoring to true inside that block so that all subsequent calls are ignored. When you are ready to start responding again, just set Ignoring back to false.
I have one device "installed" on a users desk (a desk is nothing but a chair or table on which user will sit), and I will be supporting thousands of desks.
A user will have one "chip" and the user will scan this chip on the device which is installed on their desk.
The device will read the data off the chip and will send it to my laptop which will also have one of the devices installed, except this device is the main device responsible for collecting all user scan chip data.
All the data will be routed to my device via a wifi router and I will listen to this from my Main device and read data from this device from my laptop via serial port connection.
This data sending will happen as each user number scans his/her chip.
I have created a windows form application which will continuously run in the background on my laptop, and will be listening to my serial port on which main device is connected.
This is my code taken from here: Source Code Reference:
public partial class MainUI : Form
{
SerialPortManager _spManager;
public MainUI()
{
InitializeComponent();
UserInitialization();
}
}
private void UserInitialization()
{
_spManager = new SerialPortManager();
_spManager.NewSerialDataRecieved += new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved);
this.FormClosing += new FormClosingEventHandler(MainUI_FormClosing);
}
private void MainUI_Load(object sender, EventArgs e)
{
_spManager.StartListening()
}
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int dataLength = _serialPort.BytesToRead;
byte[] data = new byte[dataLength];
int nbrDataRead = _serialPort.Read(data, 0, dataLength);
if (nbrDataRead == 0)
return;
// Send data to whom ever interested
if (NewSerialDataRecieved != null)
{
NewSerialDataRecieved(this, new SerialDataEventArgs(data));
}
}
void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
{
if (this.InvokeRequired)
{
// Using this.Invoke causes deadlock when closing serial port, and BeginInvoke is good practice anyway.
this.BeginInvoke(new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved), new object[] { sender, e });
return;
}
//data is converted to text
string str = Encoding.ASCII.GetString(e.Data);
if (!string.IsNullOrEmpty(str))
{
//Here i will store that data in to my database through web service.
//What i should use whether WCF service or Web Api because data will be continuos like at a
//time more than 10 or 100 user can scan data at the same time so this event will be fired continuously.
//I am using entity framework to store data in to my database and how to ansynchornously call web service to store my data
//so that my call doesnt block incoming data to serial port
}
}
My main concern is I will have numerous users who will scan data at the same time and how I will handle when more than 10 or 100 user scan the data at the same time.
How can I mitigate this potential issue?
Ok, if i got the question right you need to do something like this ...
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int dataLength = _serialPort.BytesToRead;
byte[] data = new byte[dataLength];
int nbrDataRead = _serialPort.Read(data, 0, dataLength);
if (nbrDataRead == 0)
return;
// Send data to api
string str = Encoding.ASCII.GetString(e.Data);
if (!string.IsNullOrEmpty(str))
{
var api = new HttpClient();
api.BaseUrl("http://somewhere.com");
api.PostAsJsonAsync("api/Something", str)
}
if (this.InvokeRequired)
{
// Using this.Invoke causes deadlock when closing serial port,
// and BeginInvoke is good practice anyway.
this.BeginInvoke(new EventHandler<SerialDataEventArgs>(
_spManager_NewSerialDataRecieved), new object[] { sender, e
});
return;
}
}
// i think this can go completely ...
void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
That posts the data to webapi but whilst that post is taking place on another thread the serial port can carry on receiving data
close your serial port and load every some-amount-of-time. After that some-amount-of-time open the port and scan all devices, then close it again.
public void MainUI.Load(Object sender, Eventargs e)
{
if (_spmanager != null && !_spManager.IsOpen)
//*write the code here where it opens and starts listening
_spmanager.StartListening();
//*write the code here where it waits a little bit then
_spmanager.Close();
}
Therefore everytime it loads it starts when the port is closed, it opens for a little bit, scans whatever values are true and then closes again.
I am not very sure about this but it is just an idea of how to handle it. The code might not be accurate or currect I just wrote it quickly. Take the idea from this
I am taking over a C# code from someone who implemented a desktop application to read real time data from the Serial Port and displaying it in charts (using the Chart Class).
The code seems to be working, but is very slow. It seems to be updating the chart around once in 4 seconds (0.25Hz). However, I can see that it is multi-threaded and has no delay commands, so I don't understand why it is running so slow. Could the updating of charts slow it down? Ideally I would like to achieve 1000 data points per second (1kHz), displaying it in real time and saving it to the hard disk, where the size of each data point is about 30 bytes.
I spent few days understanding the code, but it's too cumbersome to follow, all written in a single file, with no comments. Is my goal of reading 1000 data points per second realistic/achievable?
I'm also considering to re-write the code (as opposed to trying to fix it), considering it's only about 2,500 lines long. Any tips would be appreciated. Also, if I rewrite the code, what language might be better for this application?
I developed some code where I got significant performance improvement, it may work for you. Here is what I did-
Step 1: I would first identify, which one is the bottle neck, drawing/rendering of the chart
or serial port
Step 2: If you find its the rendering-- then add this in your form/chart setup, it will draw much faster. But first double check to make sure you're not in remote desktop mode.
<!-- language: c# -->
// No double buffering for remote, only local
public bool OptimizeOfLocalFormsOnly(System.Windows.Forms.Control chartControlForm)
{
if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
{
SetUpDoubleBuffer(chartControlForm);
return true;
}
return false;
}
public static void SetUpDoubleBuffer(System.Windows.Forms.Control chartControlForm)
{
System.Reflection.PropertyInfo formProp =
typeof(System.Windows.Forms.Control).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
formProp.SetValue(chartControlForm, true, null);
}
I assume you using winform application :
Use serialPort component :
Configure its properties : (BaudRate, DataBits, StopBits, Parity ...)
Make use of its event (DataReceived) to collect your inputs.
You can send commands in a loop and collect the inputs/drawing them on chart component roughly like :
while(/*some condition*/)
{
serialPort.Write(/* your command */);
// you have to think of response time
// so implement something that waits a bit before calling the port again
// I would do it using timers
int tick= Environment.TickCount;
while(Environment.TickCount - tick <= 100) // wait 100 milliseconds
{
Application.DoEvents();
}
}
// collect the data as:
private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// use according to your needs, for example
string data = "";
int readByte;
int available;
available = serialPort.BytesToRead;
if(available >= 30) // 30 bytes as stated in your question
{
for(int i=0; i<30; i++)
{
readByte = serialPort.ReadByte();
data += String.Format("{0:2X}", readByte);
}
// you can call some methods to save/display your collected data from here
save_to_file(data);
display_to_chart(data);
}
}
I developed a similar app where I was displaying 16charts * 256 samples/sec. Storing the data in a buffer and creating a separate thread for updating the charts worked for me.
When new data is read, data is stored in a list or array. Since it is real-time data, the timestamps are also generated here. Using the sample rate of the data acquired: timeStamp = timeStamp + sampleIdx/sampleRate;
public void OnDataRead(object source, EEGEventArgs e)
{
if ((e.rawData.Length > 0) && (!_shouldStop))
{
lock (_bufferRawData)
{
for (int sampleIdx = 0; sampleIdx < e.rawData.Length; sampleIdx++)
{
// Append data
_bufferRawData.Add(e.rawData[sampleIdx]);
// Calculate corresponding timestamp
secondsToAdd = (float) sampleIdx/e.sampleRate;
// Append corresponding timestamp
_bufferXValues.Add( e.timeStamp.AddSeconds(secondsToAdd));
}
}
Then, create a thread that sleeps every N ms (100ms is suitable for me for a 2 seconds display of data, but if I wanna display 10 seconds, I need to increase to 500ms of sleep time for the thread)
//Create thread
//define a thread to add values into chart
ThreadStart addDataThreadObj = new ThreadStart(AddDataThreadLoop);
_addDataRunner = new Thread(addDataThreadObj);
addDataDel += new AddDataDelegate(AddData);
//Start thread
_addDataRunner.Start();
And finally, update the charts and make the thread sleep every N ms
private void AddDataThreadLoop()
{
while (!_shouldStop)
{
chChannels[1].Invoke(addDataDel);
// Sleeep thread for 100ms
Thread.Sleep(100);
}
}
The AddData method, simply reads the X (timestamp) and Y values stored in the buffer and add its to the charts using ptSeries.Points.AddXY(xValue, yValue)
I am reading data values from a serial port and plotting the data in realtime on a zedgraph control using invoke and delegate. I would like to click a disconnect button that will stop the plotting and when a connect button is pressed, the plotting will resume. My problem is when I reconnect, the plotting will not be displayed at 1 point per second. There will be unexpected behavior that sometimes includes a delay that will not plot for 2 seconds and the next second it will plot 2 points at the same time. Sometimes it will not plot for 3 seconds and the next second, it will plot 3 points at the same time and sometimes it will work fine plotting 1 point per second. Sometimes it will plot twice or three times per second. This problem occurs only when I disconnect and then reconnect. My GUI response also begins to get worse with every time I disconnect and reconnect until finally not responding.
When I click disconnect, I clear the zedgraph but don't close the serial port. When I re-click connect again, the serial port will begin sending data again and continue to plot, calling control.invoke every time a new data point is plotted onto the zedgraph. Is the problem that I am calling invoke too many times and how would I get around this? I need to plot in realtime, so each time I receive a data point, plot it on the zedgraph right away.
Here is how I connect to the serial port:
private void btnConnect_Click_1(object sender, EventArgs e)
{
curveUSB = myPaneUSB.AddCurve("Load", listUSB, Color.Black, SymbolType.Circle);
isConnected = true;
DateTime startTime = DateTime.Now;
txtStartTime.Text = startTime.ToString();
sw.Start();
createCSVFile(startTime);
try
{
if (!serialPort1.IsOpen)
{
serialPort1.PortName = cmbPort.Items[cmbPort.SelectedIndex].ToString();
//Other Serial Port Property
serialPort1.Parity = Parity.None;
serialPort1.StopBits = StopBits.One;
serialPort1.DataBits = 8;
serialPort1.BaudRate = 9600;
//Open our serial port
serialPort1.Open();
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
Thread.Sleep(100); //Always sleep before reading
serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceivedHandler);
// Save the beginning time for reference
tickStart = Environment.TickCount; //used the calculate the time
setGraphAxis(myPaneUSB, zgControlUSB);
//Disable Connect button
btnConnect.Enabled = false;
//Enable Disconnect button
btnDisconnect.Enabled = true;
}
Invoke and serialPort1_DataReceivedHandler:
private void serialPort1_DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
if (isConnected == true)
{
txtUSBLoad.Invoke(new EventHandler(delegate
{
strRawData = serialPort1.ReadLine();
txtUSBLoad.Text = strRawData + loadUnit;
Display_Data(strRawData, curveUSB, listUSB, zgControlUSB);
}));
}
}
Display_Data function to display the plot:
private void Display_Data(String data, LineItem curve, IPointListEdit list, ZedGraphControl zgControl)
{
ts = sw.Elapsed;
tsBT = swBT.Elapsed;
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}",tsBT.Hours, tsBT.Minutes, tsBT.Seconds);
elapsedTimeBT = String.Format("{0:00}:{1:00}:{2:00}", tsBT.Hours, tsBT.Minutes, tsBT.Seconds);
txtElapsedTime.Text = elapsedTime;
txtBTElapsedTime.Text = elapsedTimeBT;
if (zgControl.GraphPane.CurveList.Count <= 0) //Make sure that the curvelist has at least one curve
return;
curve = zgControl.GraphPane.CurveList[0] as LineItem; //Get the first CurveItem in the graph
if (curve == null)
return;
list = curve.Points as IPointListEdit; //Get the PointPairList
if (list == null) //If this is null, it means the reference at curve.Points does not
return; //support IPointListEdit, so we won't be able to modify it
time = (Environment.TickCount - tickStart) / 1000.0; //Time is measured in seconds
//Get current time
now = DateTime.Now;
timestamp = now.ToOADate();
list.Add(timestamp, Convert.ToDouble(data));
if(fileWriter.BaseStream != null){ //If fileWriter is open, then write to file, else don't write
fileWriter.WriteLine(String.Format("{0:yyyy-MM-dd_HH-mm-ss}", now) + "," + data); //writes the timestamp and data to the CSV file
}
if (Convert.ToDouble(data) > tempLoad) //Checks if the current load is the maximum load
{
tempLoad = Convert.ToDouble(data);
txtMaxLoad.Text = tempLoad.ToString() + " " + loadUnit;
txtBTMaxLoad.Text = tempLoad.ToString() + " " + loadUnit;
}
//if ((Convert.ToDouble(data) > dblThreshold) && (emailAlerts == true)) {
if ((Convert.ToDouble(data) > 50) && (emailAlerts == true))
{
sendEmail(Convert.ToDouble(data));
}
XDate dateTime = curve[0].X;
zgControl.AxisChange(); //changes the x-axis
zgControl.Invalidate(); //Force a redraw
}
Disconnect button:
private void btnDisconnect_Click_1(object sender, EventArgs e)
{
zgControlUSB.GraphPane.CurveList.Clear();
if (fileWriter != null)
{
fileWriter.Close();
}
isConnected = false;
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
txtUSBLoad.Text = initText;
DateTime endTime = DateTime.Now;
txtEndTime.Text = endTime.ToString(); //displays the time stamp when the plotting stops
sw.Stop(); //stops the stop watch
}
When I disconnect, I do not close the serial port because the problem is worse when I do and I read that it is not good to always close and reopen a serial port. Instead, I used an isConnected flag to start and stop plotting.
Any help is appreciated! Thanks in advance!
Calling txtUSBLoad.Invoke() will switch processing onto the GUI thread (the same thread that is used to handle user input and draw the controls). By putting all the processing inside the call to Invoke() you are effectively swamping the GUI thread.
If your serial updates are frequent enough this will result in delayed interactions/re-drawing of the GUI.
You have a few options here, the first thing is to keep all the processing in the thread that receives the messages from the serial port. Look at all the visual controls that you are updating and the determine what data they need to be updated and process as much of that before calling Invoke(). Create a class to hold the data.
This might be too slow which will mean that you'll have a backlog of serial data building up which you can't process in time. If so, you can conflate the incoming data if you're ok with losing updates. You'll have to see if this applies to your situation.
If you want to try and parallelise the processing of the incoming data look into TPL Dataflow for an example of how to create a processing pipeline for the data.
Life status of device connected via serial port.
Hello everyone.
How can I check if the device responds to the request? I'm googling this for couple days and tried lot of solutions also from SO, but nothing gave me results that I've expected. After lot of tries I'm in point described below. I think I'm very close but now I need little help, so thanks for every answer in advance.
The current situation
What am I doing right now is very simple. First of all I'm opening serial port serialPort.Open() at very beggining of app (data is receiving almost all the application running time).
As this is just an example in my form is only one label called labelStatus and labelStatus.Text = "Not connected"
Next I'm adding a timer and it's tick method, that contains execute of serialPort.Write(). Timer Interval is set to 100 if that matters.
private void timer_Tick(object sender, EventArgs e)
{
if (serialPort.IsOpen)
{
serialPort.WriteLine("r"); //I'm sending "r" message and device send data back
}
}
Next step is create DataReceived event like below (very simplified version, in my app received data is parsing to floats and storing in array, but it's just to show the problem)
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string someVariable = serialPort.ReadLine();
labelStatus.Invoke((MethodInvoker)(() => labelStatus.Text = "Connected"));
//If i received something that means the device is plugged in and connection is correct (still very simplified)
}
One last thing is create ErrorReceived method.
private void serialPort_ErrorReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
labelStatus.Invoke((MethodInvoker)(() => labelStatus.Text = "Not connected"));
}
Untill now everything works brilliant. Sending data works. DataReceived event is executig each 100 miliseconds when data is send. My data is received properly with no problems. When I start application labelStatus text is "Not connected" (device cable is not plugged in). When I plugged in device labelStatus text changing to "Connected". But now when I plugged of cable ErrorReceived event is not executing and labelStatus text is still "Connected". So as I've asked before: How can I check is device still connected to computer? (Or maybe: how to execute ErrorReceived event, when data is not receiving?).
Note: Serial port ReadTimeout is set to 300 miliseconds.
What have I tried
I've tried lot of things but this one in my head seems to should work but doesn't.
I've modified DataReceived event and I've put serialPort.ReadLine() into try/catch block with TimeoutException where I've tried to manually execute ErrorReceived method like below
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
try
{
string someVariable = serialPort.ReadLine();
labelStatus.Invoke((MethodInvoker)(() => labelStatus.Text = "Connected"));
//If i received something that means the device is plugged in and connection is correct (still very simplified)
}
catch (TimeoutException)
{
serialPort_ErrorReceived(null, null);
}
}
I was hoping that will work like I want.
BTW. Sorry for my English. It's not perfect, but I do my best. Cheers!
Listen to the WM_DEVICECHANGE event that will be fired when a device is removed or inserted.
Here is an example of a implementation and some more information:
Detect serial port insertion/removal
http://www.codemiles.com/download/file.php?id=719 (USB Sample)
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v=vs.85).aspx
This is solution in my case
Regarding to Martjin's answer i need to further explain my situation. First of all I want to say that I'm not installing any hardware into my computer, so in my opinion WM_DEVICECHANGE event was not what i need (but of course thanks for information, I've learned something new). Application is reading data from scale. Scale after plug into com port is not sending any data and actually there's no communication between it and computer at all. The only way to read data is to send request to scale, so I have to rely on that.
First try
The plan:
Add two static int fields (flags) checkOld and checkNew,
increment checkNew in DataReceived, check in timer Tick method
is checkOld is equal to checkNew. If true that means checkNew
was not increment, and that means DataReceived was not executed.
`
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
checkNew++;
string someVariable = serialPort.ReadLine();
labelStatus.Invoke((MethodInvoker)(() => labelStatus.Text = "Connected"));
//If i received something that means the device is plugged in and connection is correct (still very simplified)
}
private void timer_Tick(object sender, EventArgs e)
{
if (serialPort.IsOpen)
{
serialPort.WriteLine("r"); //I'm sending "r" message and device send data back
}
if (checkOld == checkNew)
{
labelStatus.Invoke((MethodInvoker)(() => labelStatus.Text = "Not connected"));
}
}
`
The plan was good but when I've tested it result was not even good. What happened? Actually device status was blinking connected-not connected-connected-not connected etc. I've wrote some data to output and get answer. The timer was looping so fast that DataReceived event could not always increment checkNew value.
Final solution
Based on what I had at the moment I've decided to add some little changes. Instead of comparing two integers values try to collect couple last values ad check if all were the sem or not.
The plan:
Add three static fields: first six elements array of integers
statusArray, second integer index with value equals to 6 (last
element of array + 1), third integer checkNew,
increment checkNew in
DataReceived event,
in timer Tick event fill array to index,
decrement index value untill whole array is filled, and if index == 0 reset index value to
6,
and last check if last six values of checkNew, stored in
statusArray are the same. If true that means DataReceived did not
executed six times in a row, and now I can be sure that connection is
lost.
`
static int index = 6;
static int checkNew = 0;
static int[] statusArray = {0,0,0,0,0,0};
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
checkNew++;
string someVariable = serialPort.ReadLine();
labelStatus.Invoke((MethodInvoker)(() => labelStatus.Text = "Connected"));
//If i received something that means the device is plugged in and connection is correct (still very simplified)
}
private void timer_Tick(object sender, EventArgs e)
{
if (serialPort.IsOpen)
{
serialPort.WriteLine("r"); //I'm sending "r" message and device send data back
}
if (index == 0)
index = 6;
index--;
int value = statusArray[index] = checkNew;
}
`