I have a sensor (a Quartzonix pressure transducer, actually) that spits out data via the serial port, around 3 times a second. I'd like to set up some code to give me an average reading based on x-amount of samples.
The output looks something like this:
01+ 1.502347091823e01
01+ 1.501987234092e01
01+ 1.50234524524e01
01+ 1.502123412341e01
01+ 1.502236234523e01
01+ 1.50198345e01
01+ 1.502346234523e01
.. and will keep going on forever until the com port is closed or the transducer gets another command.
This is what code I have so far, and the code works to show me what the transducer is actually outputting:
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
Dim a As String
a = "$01MC" & Chr(13)
MyComPort.WriteLine(a)
Do
Dim Incoming As String = MyComPort.ReadLine()
Dim incomingtext As String = Incoming.Remove(0, 3)
If Incoming Is Nothing Then
Exit Do
Else
txtRawData.Text = Incoming
boxPSIA.Text = Format(Val(incomingtext), "##0.000")
End If
Application.DoEvents()
Loop
End Sub
The "$01MC" is the command the transducer needs to start spitting out the data. I've got some wierd timeout thing happening when i click the start button, but that's another show (maybe a .readtimeout adjustment is needed, not sure).
I have a text box txtReadingsToAvg for input of how many readings to average.. I'm just not wrapping my head around how to actually get it to caluclate the average (on, say, a button click and then spitting it out into a msgbox, or even into another text box).
Not sure how your code even works. You said you get values @ about 3 Hz? Then a straight up Do...Loop
would be too fast. There is an event which is raised when the serial port receives data. Make use of that.
You will probably need to change this around a little to suit your needs
' WithEvents allows events to be handled with "Handles" keyword
Private WithEvents myComPort As SerialPort
Private dataQueue As Queue(Of Double)
Private numReadingsToAvg As Integer = 0
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
' make a new queue here to initialize or clear an old queue
dataQueue = New Queue(Of Double)()
' read the num avgs text box. you may want to change on the fly also
numReadingsToAvg = Integer.Parse(Me.txtReadingsToAvg.Text)
myComPort.WriteLine("$01MC" & Chr(13))
End Sub
Private Sub myComPort_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles myComPort.DataReceived
Dim incomingLine As String = myComPort.ReadLine()
' DataReceived event happens on its own thread, not the UI
' must invoke call back to UI to change properties of controls
txtRawData.Invoke(Sub() txtRawData.Text = incomingLine)
Dim incomingValue As String = incomingLine.Remove(0, 3).Trim()
If Not String.IsNullOrWhiteSpace(incomingValue) Then
Exit Sub
Else
Dim measurement As Double = Double.Parse(incomingValue)
boxPSIA.Invoke(Sub() boxPSIA.Text = Format(measurement, "##0.000"))
dataQueue.Enqueue(measurement)
' if there are too many items, remove the last one
If dataQueue.Count > numReadingsToAvg Then
dataQueue.Dequeue()
End If
Dim average As Double = dataQueue.Average()
' you need to add this textbox
anotherTextBox.Invoke(Sub() anotherTextBox.Text = Format(average, "##0.000"))
End If
End Sub
By the way, Application.DoEvents() should rarely (never) be used, as there's always a better way to remedy whatever problem you are bandaging with DoEvents. Your original example suffered from clogging up the UI thread with a never-ending loop running on the UI. If you ever need to run a loop like that, it should almost always run on a different thread than the UI thread. In my example, there is no loop, and the timing is determined by the port itself. No need for any of this to happen on the UI thread.