C# Serial Receive Limit - c#

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.

Related

MSVS C# SerialPort Received Data Loss

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.

For serial port programming in C#, When I am in the middle of a DataRecived handler, how can I jump into another DataRecieved Handler

I have a serial port and a bunch of DataRecieved handler, I want one to be activated at some point if a certain condition is true in another handler.
For example
// write some message to StartCommHandler
private void StartCommHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
byte[] startComResp = new byte[7];
if (sp.BytesToRead < 7)
return;
else
sp.DataReceived -= StartCommHandler;
sp.Read(startComResp, 0, startComResp.Length);
if ( //true response)
{
// enable StartMeasureHandler and write to it:
sp.DataReceived += StartMeasureHandler;
// write some message to StartCommHandler
}
else
{
// enable StartCommHandler and write to it again:
sp.DataReceived += StartCommHandler;
// write the message to StartCommHandler
}
// some other code that should not be read if else is selected
}
private void StartMeasureHandler(object sender, SerialDataReceivedEventArgs e)
{
//some code
}
when I enable the "StartMeasureHandler" and write to it, I expect that I should jump to it and the rest of the code should be ignored. But it continues reading the rest of the code in the "StartCommHandler" .
In other words, how should I jump out of one handler to another handler
p.s.: I know that I can put codes in if else statement, but I still need to know a solution to my issue to solve a more complicated problem.
Thanks
TL;DR: There's only one good way to deal with serial stream -- with a state machine. Make the compiler write that state machine for you and be happy.
Even better than swapping event handler is to not use the DataReceived event at all. Use sp.BaseStream.ReadAsync. Then you can write all your data handling code in blocking style which is easy, and the compiler will convert it to continuation-passing style, which performs well.
No threading to mess with. And it bypasses the most buggy parts of the SerialPort class.
I've written a blog post on the subject, which you can read for more details on why to avoid DataReceived and BytesToRead.
You should just be able to call
if ( /*true response*/)
{
sp.DataReceived += StartMeasureHandler;
StartMeasureHandler(object sender, SerialDataReceivedEventArgs e);
return;
}
// ...
It is still just a function.

New to threading, need help making a static variable thread safe with background worker

As the title suggests, I'm new to the concept of threading and I'm learning as I go. I'm working on a program as part of a research project involving a 3D printer. I have two pieces of hardware I need to interface with: a small stage that moves up and down, and a force sensor that can detect when an object on the stage makes contact with the sensor which acts as the signal to stop moving the stage. As I said, I'm new to the whole threading concept, so I'm not even sure if I'm designing this correctly, if I should use backgroundworker or the Thread class, how to ensure thread-safety, etc.
I"m writing the program in C# using Windows Forms and decided to use two background worker controls in Visual Studio/.NET Framework, one to monitor the force sensor reading and the other to control the stage moving up and down.
It has to be fast and accurate; the stage needs to stop moving the moment a pre-defined sensor value is detected. I have two static classes, a ZStageControl static class and a ForceSensorControl static class.
ForceSensorControl has a method called UpdateSensor() which returns the current value of the force sensor, and the ZStageControl has a private static bool variable, forceDetected (set to false by default), a public property ForceDetected that gets/sets this variable, and a method called GoodToMove. GoodToMove is defined as follows:
public static bool GoodToMove()
{
if (forceDetected == true ||
Position() < MinHeight ||
Position() > MaxHeight)
return false;
else
return true;
}
My sensor update backgroundworker dowork code is defined as follows:
private void sensorUpdateBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
double currentForceValue;
while (ForceSensorControl.IsConnected() == true)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
try
{
currentForceValue = Double.Parse(ForceSensorControl.UpdateSensor());
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
disconnectForceSensor();
return;
}
if (currentForceValue >= forceSensorStopValue)
{
if (this.ForceDetected != null)
{
this.ForceDetected(this,
new ForceDetectedEventArgs(ZStageControl.Position(), currentForceValue));
}
}
else if (ZStageControl.ForceDetected == true)
ZStageControl.ForceDetected = false;
forceSensorValueLabel.Text = currentForceValue.ToString() + "g";
}
}
}
So as long as the sensor remains connected, continuously loop and update the force sensor vlaue. If it detects the proper force value, it fires the ForceDetected event. If it reads the value as less than the force sensor stop value, but ForceDetected is still set to true, it simply sets to false (since when the force is detected, it will stop the stage from moving and then return it to its default position, so it should reset the force detected variable).
The event code is defined as follows:
public void FormMain_ForceDetected(object sender, ForceDetectedEventArgs e)
{
if (ZStageControl.ForceDetected == true)
return;
ZStageControl.ForceDetected = true;
feedbackRichTextBox.Text += "\nForce Detected at position " +
e.ForceDetectedPosition.ToString() + " with a value of " +
e.ForceDetectedValue.ToString() + "\n";
ScrollToEndOfFeedbackBox(feedbackRichTextBox);
soundPlayer.Play();
}
The thread to move the Z-Stage up or down is defined as follows:
private void zStageMoveBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
ZStageMoveDirections direction = (ZStageMoveDirections)e.Argument;
while (ZStageControl.IsConnected == true)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
System.Threading.Thread.Sleep(zStageSpeed);
if (direction == ZStageMoveDirections.Down)
{
ZStageControl.MoveDown(true);
}
if (direction == ZStageMoveDirections.Up)
{
ZStageControl.MoveUp(true);
}
zStagePositionUpdateLabel.Text = ZStageControl.Position().ToString();
}
}
}
The code that calls the DoWork event for the Z-Stage move is controlled by an if statement that checks if ZStageControl.GoodToMove() is true. So while GoodToMove() returns true, the Z-Stage thread can fire.
The issue I'm having is that I'm not sure if I'm designing this right, if I'm using the backgroundworker properly, and I know my variables are not thread-safe because at certain points GoodToMove() returns true and other times it returns false, even though there is clearly no force being detected. It seems to have a mind of its own. I just know nothing about thread-safety. Should I simply use the THread class instead of the background worker, is there a certain way to ensure the forceDetected variable/GoodToMove() method operates properly across these threads?
I think your approach is inherently flawed.
You seem to be designing a system with a constantly looping monitor and then a check of that monitor when you want to execute a "move."
This is inherently problematic because you've created a race condition between your safety check operation and your move operation.
Consider this:
1 // check if move is ok -- PASS
(async) // move suddenly becomes not ok!
2 // move is executed
Instead you should think about this problem as entirely synchronous (as validation checks and executions should be entirely atomic). Whenever a move is requested you should check if it's permitted and then decide whether or not to execute.
1 // exclusive lock
2 // check if move is ok -- PASS
3 // execute move
4 // release lock

Checking device status, while receiving data from serial port c#

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;
}
`

Constantly check SerialPort object to see if any bytes exist and if so read bytes and perform function

I am interfacing with a device, and need a way to constantly check if any bytes are available to be read by the serialport, and if so I need to read them and perform different functions. I can't create a data_received event for a number of reasons due to the project I am working on. As of now I have an infinite for loop running in a new thread, but it is causing the computer to slow way down.
Does anyone have any other ideas? Any help is appreciated. Below is the for loop code I am currently using.
void bw_DoWork(object sender, DoWorkEventArgs e)
{
object[] parameters = (object[])e.Argument;
SerialPort port = (SerialPort)parameters[0];
agentRadio agentR = (agentRadio)parameters[1];
for (; ; )
{
if (port.IsOpen)
{
try
{
if (port.BytesToRead > 0)
{
byte value = (byte)port.ReadByte();
if (value == Global.OFF_CHAR)
{
port.Close();
Global.isConnected = false;
Form form = new Form();
form.deviceDisconnect(agentR);
MessageBox.Show("Your device has disconnected on its own, due to inactivity or because a key was switched off.", "Device Disconnect", MessageBoxButton.OK, MessageBoxImage.Information);
break;
}
else if (value == Global.WARNING_CHAR[0])
{
if (Global.activeClick)
{
port.BaseStream.Write(Global.POLL_CHAR, 0, Global.POLL_CHAR.Length);
}
}
}
}
catch { }
}
else
{
break;
}
}
}
Get rid of the horrible 'if (port.BytesToRead > 0)' polling loop! Why do you do that - you do nothing else if the check retuns false, so just let the read block in the normal way.
Use the DataReceived event. This event is called on a secondary thread, there's no need to create another, or event a BackgroundWorker.

Categories

Resources