-
Sep 13th, 2016, 03:16 AM
#1
Thread Starter
Junior Member
Parse arduino Serial data
Dear all.
I am working with arduino Uno, I have tracker application running on arduino Uno board. I am sending data in comma seprated value. Now i want to view data into Visual basic. I have code as below. The below code is working with fake values but as soon it live stream data split functions gives out error.
outout data serial look like this.
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:1,Local Date:0/0/0,Local Time:5:30:2,tracker_des_angle:0.00,tracker_actual_pos:57.22,Wind Speed :0.00,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:1,Local Date:0/0/0,Local Time:5:30:4,tracker_des_angle:43.00,tracker_actual_pos:69.20,Wind Speed :0.95,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:6,tracker_des_angle:43.00,tracker_actual_pos:16.34,Wind Speed :1.85,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:8,tracker_des_angle:43.00,tracker_actual_pos:11.89,Wind Speed :2.77,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:10,tracker_des_angle:43.00,tracker_actual_pos:14.11,Wind Speed :3.77,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:12,tracker_des_angle:43.00,tracker_actual_pos:13.77,Wind Speed :4.75,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:14,tracker_des_angle:43.00,tracker_actual_pos:13.77,Wind Speed :5.67,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:16,tracker_des_angle:43.00,tracker_actual_pos:9.67,Wind Speed :6.57,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:18,tracker_des_angle:43.00,tracker_actual_pos:9.67,Wind Speed :7.58,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:20,tracker_des_angle:43.00,tracker_actual_pos:13.43,Wind Speed :8.59,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:22,tracker_des_angle:43.00,tracker_actual_pos:15.82,Wind Speed :9.57,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:24,tracker_des_angle:43.00,tracker_actual_pos:12.23,Wind Speed :9.57,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:26,tracker_des_angle:43.00,tracker_actual_pos:12.40,Wind Speed :9.68,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:28,tracker_des_angle:43.00,tracker_actual_pos:13.26,Wind Speed :9.74,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:30,tracker_des_angle:43.00,tracker_actual_pos:16.34,Wind Speed :9.70,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:32,tracker_des_angle:43.00,tracker_actual_pos:12.06,Wind Speed :9.72,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:34,tracker_des_angle:43.00,tracker_actual_pos:12.23,Wind Speed :9.76,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:36,tracker_des_angle:43.00,tracker_actual_pos:15.14,Wind Speed :9.75,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:38,tracker_des_angle:43.00,tracker_actual_pos:15.99,Wind Speed :9.77,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:40,tracker_des_angle:43.00,tracker_actual_pos:12.40,Wind Speed :9.69,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:42,tracker_des_angle:43.00,tracker_actual_pos:9.49,Wind Speed :9.63,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:44,tracker_des_angle:43.00,tracker_actual_pos:11.55,Wind Speed :9.58,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:46,tracker_des_angle:43.00,tracker_actual_pos:9.32,Wind Speed :9.52,END
visual basic code:
Code:
Option Explicit On
Imports System
Imports System.IO.Ports
Imports System.Text
Public Class Solar_Track
Dim comPORT As String
Dim receivedData As String = ""
Dim strArray() As String
Dim Data As String
Dim latitude() As String
Dim longitude() As String
Dim Local_date() As String
Dim Local_Time() As String
Dim Mode_oper() As String
Dim Dir_motor() As String
Dim Act_pos() As String
Dim Dis_pos() As String
Dim wind_pos() As String
Sub Parse_receivedata(ByVal msg As String)
Data = msg
strArray = Split(Data, ",")
'' latitude = Split(strArray(1), ":")
strArray = Split(Data, ",")
latitude = Split(strArray(1), ":")
Text_Latitude.Text = latitude(1)
longitude = Split(strArray(2), ":")
Text_Longitude.Text = longitude(1)
Mode_oper = Split(strArray(3), ":")
Text_Mode.Text = Mode_oper(1)
Dir_motor = Split(strArray(4), ":")
Text_MotorDir.Text = Dir_motor(1)
Local_date = Split(strArray(5), ":")
Text_LocalDate.Text = Local_date(1)
Local_Time = Split(strArray(6), ":")
Label_hour.Text = Local_Time(1)
Label_min.Text = Local_Time(2)
Label_Sec.Text = Local_Time(3)
Dis_pos = Split(strArray(7), ":")
Text_TrackerDesire.Text = Dis_pos(1)
Act_pos = Split(strArray(8), ":")
Text_TrackerActual.Text = Act_pos(1)
wind_pos = Split(strArray(9), ":")
Text_Windspeed.Text = wind_pos(1)
End Sub
Sub data_parse()
Data = "Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:1,Local Date:0/0/0,Local Time:5:30:2,tracker_des_angle:0.00,tracker_actual_pos:54.48,Wind Speed :0.00,END"
strArray = Split(Data, ",")
latitude = Split(strArray(1), ":")
Text_Latitude.Text = latitude(1)
longitude = Split(strArray(2), ":")
Text_Longitude.Text = longitude(1)
Mode_oper = Split(strArray(3), ":")
Text_Mode.Text = Mode_oper(1)
Dir_motor = Split(strArray(4), ":")
Text_MotorDir.Text = Dir_motor(1)
Local_date = Split(strArray(5), ":")
Text_LocalDate.Text = Local_date(1)
Local_Time = Split(strArray(6), ":")
Label_hour.Text = Local_Time(1)
Label_min.Text = Local_Time(2)
Label_Sec.Text = Local_Time(3)
Dis_pos = Split(strArray(7), ":")
Text_TrackerDesire.Text = Dis_pos(1)
Act_pos = Split(strArray(8), ":")
Text_TrackerActual.Text = Act_pos(1)
wind_pos = Split(strArray(9), ":")
Text_Windspeed.Text = wind_pos(1)
End Sub
Private Sub Solar_Track_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Timer1.Enabled = False
comPORT = ""
For Each sp As String In My.Computer.Ports.SerialPortNames
comPort_ComboBox.Items.Add(sp)
Next
End Sub
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles comPort_ComboBox.SelectedIndexChanged
If (comPort_ComboBox.SelectedItem <> "") Then
comPORT = comPort_ComboBox.SelectedItem
End If
End Sub
Private Sub BtConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles connect_BTN.Click
If (connect_BTN.Text = "Connect") Then
If (comPORT <> "") Then
SerialPort1.Close()
SerialPort1.PortName = comPORT
SerialPort1.BaudRate = 9600
SerialPort1.DataBits = 8
SerialPort1.Parity = Parity.None
SerialPort1.StopBits = StopBits.One
SerialPort1.Handshake = Handshake.None
SerialPort1.Encoding = System.Text.Encoding.Default
SerialPort1.ReadTimeout = 10000
SerialPort1.Open()
connect_BTN.Text = "Dis-connect"
Timer1.Enabled = True
Timer_LBL.Text = "Timer: ON"
Else
MsgBox("Select a COM port first")
End If
Else
SerialPort1.Close()
connect_BTN.Text = "Connect"
Timer1.Enabled = False
Timer_LBL.Text = "Timer: OFF"
End If
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
receivedData = ReceiveSerialData()
RichTextBox1.Text &= receivedData
Parse_receivedata(receivedData)
'' data_parse()
End Sub
Function ReceiveSerialData() As String
Dim Incoming As String
Try
Incoming = SerialPort1.ReadExisting()
If Incoming Is Nothing Then
Return "nothing" & vbCrLf
Else
Return Incoming
End If
Catch ex As TimeoutException
Return "Error: Serial Port read timed out."
End Try
End Function
Private Sub BtClear_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtClear.Click
RichTextBox1.Text = ""
End Sub
End Class
Fake value function which is working fine.
Code:
Sub data_parse()
Data = "Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:1,Local Date:0/0/0,Local Time:5:30:2,tracker_des_angle:0.00,tracker_actual_pos:54.48,Wind Speed :0.00,END"
strArray = Split(Data, ",")
latitude = Split(strArray(1), ":")
Text_Latitude.Text = latitude(1)
longitude = Split(strArray(2), ":")
Text_Longitude.Text = longitude(1)
Mode_oper = Split(strArray(3), ":")
Text_Mode.Text = Mode_oper(1)
Dir_motor = Split(strArray(4), ":")
Text_MotorDir.Text = Dir_motor(1)
Local_date = Split(strArray(5), ":")
Text_LocalDate.Text = Local_date(1)
Local_Time = Split(strArray(6), ":")
Label_hour.Text = Local_Time(1)
Label_min.Text = Local_Time(2)
Label_Sec.Text = Local_Time(3)
Dis_pos = Split(strArray(7), ":")
Text_TrackerDesire.Text = Dis_pos(1)
Act_pos = Split(strArray(8), ":")
Text_TrackerActual.Text = Act_pos(1)
wind_pos = Split(strArray(9), ":")
Text_Windspeed.Text = wind_pos(1)
End Sub
The function which is not working with live data
Code:
Sub Parse_receivedata(ByVal msg As String)
Data = msg
strArray = Split(Data, ",")
'' latitude = Split(strArray(1), ":")
strArray = Split(Data, ",")
latitude = Split(strArray(1), ":")
Text_Latitude.Text = latitude(1)
longitude = Split(strArray(2), ":")
Text_Longitude.Text = longitude(1)
Mode_oper = Split(strArray(3), ":")
Text_Mode.Text = Mode_oper(1)
Dir_motor = Split(strArray(4), ":")
Text_MotorDir.Text = Dir_motor(1)
Local_date = Split(strArray(5), ":")
Text_LocalDate.Text = Local_date(1)
Local_Time = Split(strArray(6), ":")
Label_hour.Text = Local_Time(1)
Label_min.Text = Local_Time(2)
Label_Sec.Text = Local_Time(3)
Dis_pos = Split(strArray(7), ":")
Text_TrackerDesire.Text = Dis_pos(1)
Act_pos = Split(strArray(8), ":")
Text_TrackerActual.Text = Act_pos(1)
wind_pos = Split(strArray(9), ":")
Text_Windspeed.Text = wind_pos(1)
End Sub
error message
-
Sep 13th, 2016, 07:09 AM
#2
Re: Parse arduino Serial data
Too small to read on my screen but looks like index out of bounds. So what is the value of data? I suspect you are not sending the full line when you switched to using a COM port event to supply the Parse_receivedata routine. Make sure you have a full line (and probably drop any CR/LF characters) before trying to parse.
-
Sep 13th, 2016, 01:14 PM
#3
Re: Parse arduino Serial data
Ok... this is a common, common problem for first-time SerialPort users to run into.
The problem is you're reading the Serial Port using a timer. How on earth is it supposed to knows when there's data in the serial buffer? How on earth is it supposed to know there's one COMPLETE row of data being read off the buffer? For all you know, it could be grabbing this:
Code:
"me:5:30:2,tracker_des_angle:0.00,tracker_actual_pos:57.22,Wind Speed :0.00,END
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:1,Loc"
...and sending it to your routine.
You need an IRONCLAD way to read streaming, non-stop serial data from something like a weigh scale (where I learned this), GPS, Weather station (which looks like your problem) or other streaming device.
The trick is, all these devices usually send data in "frames". One "frame" of your data would be:
Code:
Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:20,tracker_des_angle:43.00,tracker_actual_pos:13.43,Wind Speed :8.59,END
Now, normally, "frames" are the same length in terms of number of bytes always, but yours are a little different because you'll notice it's not putting leading zeros on some of those values.
So, you need something on the serial port that can figure out where each frame starts and ends and computes this for each frame of data.
Here's a serial handler object that I use for my work. I tweaked it for your particular problem, so in theory it should work, but since the computer I'm on has neither a serial port, nor the thing your arduino is hooked to, I can't test it.
If you have any questions, give a shout!
Code:
Imports System.Text
Public Class clsSerialPort
Implements IDisposable
'Lots of global variables. Why? Speed. It takes time allocating and deallocating variables. It's not efficient use of your processing. These routines
'need to run FAST, in the low millisecond ranges as data is streaming in as fast as the serial port will allow.
Private WithEvents myPort As New System.IO.Ports.SerialPort
Private t As Threading.Thread 'A secondary thread which will periodically check to see if we're still receiving data.
Private rcvQLock As New Object 'An object to serve as a thread-safe lock
Private rcvQ As New StringBuilder 'My raw receiving queue. May contain multiple frames of data, not necessarilly starting at the top of a frame
Private booDetected As Boolean = False 'Checks if the computer detects something transmitting on the serial port
Private booShutDown As Boolean = False 'Set if I'm shutting down the system, alerts everything to close and cleanup
Private strData As String = String.Empty 'Final result, a string with 1 line of data
Private bytesToRead As Integer 'How many bytes of data to read off the serial port
Private booLockedFrame As Boolean 'Tells me if I know my receiving queue is sitting at the top of a data frame
Private i As Integer 'Just a counter
Private intRoughFrameLength = 160 'Roughly how big my data frame is, error on the side of larger than smaller.
Private eolChar As Byte = 13 'End of Line character in ASCII
Public Property ComPort As String 'Set my serial port name (COM1 for example)
Public ReadOnly Property PortOpen() As Boolean 'True or false if the serial port is open
Get
Return myPort.IsOpen
End Get
End Property
'Initialization routine. Call this to start the port reading.
Public Sub Initialize_SerialPort()
Open_SerialPort()
t = New Threading.Thread(AddressOf CheckDevice) 'Start the monitor thread.
'It makes sure what's attached to the serial port is active, still connected and still reading data
t.Name = "Check Disconnected Device"
t.IsBackground = True
t.Start()
End Sub
'Shutdown routine. Call this to stop everything and close out the port.
Public Sub Shutdown_SerialPort()
booShutDown = True 'Flag that the port is shutting down
t.Join(250) 'Wait for the monitor thread to stop, should stop in 100ms, but use 250ms just in case. After 250, it'll kill it forcefully.
Close_SerialPort()
End Sub
Private Function Open_SerialPort() As Boolean
Try
booShutDown = False
With myPort
'Example port settings
.BaudRate = 9600
.PortName = ComPort
.DataBits = 8
.Parity = IO.Ports.Parity.None
.StopBits = IO.Ports.StopBits.One
.Handshake = IO.Ports.Handshake.None
.Encoding = Encoding.Default
.ReadTimeout = 500
.WriteTimeout = 500
.DiscardNull = False
.ReadBufferSize = 16384
End With
If Not myPort.IsOpen Then
myPort.Open()
End If
Return True
Catch ex As Exception
'Do something
Return False
End Try
End Function
Private Sub Close_SerialPort()
Try
If myPort.IsOpen Then
myPort.Close() 'Close the port
End If
Catch ex As Exception
'Do sonething
End Try
End Sub
Private Sub CheckDevice()
'The monitor thread runs this loop which cycles every 5 seconds checking to see if the port is still streaming in data.
Dim i As Integer = 0
While Not booShutDown
For i = 0 To 50 'Pause every 5 seconds... but check every 100 ms if we're shutting down.
Threading.Thread.Sleep(100)
If booShutDown Then Exit While 'If we're shutting down, ditch out of this loop and end this thread.
Next
'So... 5 seconds have passed...
Try
If Not booDetected Then 'If the device is reading data, then this will get set to "true" every couple milliseconds.
'-=-=-=-=-==
'Do something, like raise an event that screams HEY! I'm NO LONGER detecting the device!
'-=-=-=-=-==
End If
Catch ex As Exception
'Do something
End Try
booDetected = False 'I'm going to set this to false here. If 5 more seconds pass, and this is STILL false, then the port isn't receiving any data and it'll get triggered as such on the next pass.
End While
End Sub
Private Sub myPort_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles myPort.DataReceived
'The "DataReceived" event. This happens every time the port receives a new chunk of data. It may happen sporadically. Lots of checks need to be in place.
Try
Dim c As Char = Convert.ToChar(eolChar) 'This is the character that my string ENDS with... in this case, carriage return
If myPort.BytesToRead = 0 Then Exit Sub 'Nothing to read... exit now, why was it triggered? Gremlins. They happen.
bytesToRead = myPort.BytesToRead
Dim buf(bytesToRead - 1) As Byte
bytesToRead = myPort.Read(buf, 0, bytesToRead) 'Read in the buffer
Threading.Monitor.Enter(rcvQLock) 'This is to prevent this from triggering again before the first one is done. Important!
Try
rcvQ.Append(Encoding.ASCII.GetString(buf)) 'Append what I've received onto my string builder.
Finally
Threading.Monitor.Exit(rcvQLock) 'Release the lock
End Try
If rcvQ.Length > (intRoughFrameLength * 2) Then
booDetected = True 'This is just a signal that my device is still connected and activly streaming data in!
If Not booLockedFrame Then 'Starting out, I don't know where the beginning of a line is.
'This is roughly one frame of data:
'Start,Latitude:13.00,longitude:60.00,MODE:1,DIR:0,Local Date:0/0/0,Local Time:5:30:26,tracker_des_angle:43.00,tracker_actual_pos:12.40,Wind Speed :9.68,END
'About 160 characters, since this isn't being kind about leading zeros
'so I've set intRoughFrameLength = 160. I need to wait until there's enough data for at least 2 frames before I can start safely processing it.
i = 0
Threading.Monitor.Enter(rcvQLock) 'Locking the queue again from other processes...
Try
Do Until rcvQ.ToString.StartsWith(c)
rcvQ.Remove(0, 1) 'I'm removing all leading characters one at a time until I get to the END of a line
Loop
booLockedFrame = True 'I'm sitting on the end of a line now. I know what comes after is a frame of data.
Finally
Threading.Monitor.Exit(rcvQLock) 'Release the lock
End Try
End If
'Ok, so I'm sitting on the top of a frame of data...
Threading.Monitor.Enter(rcvQLock) 'Locking the queue again from other processes...
Try
i = 1 'start my count on the second character
Do Until rcvQ.Chars(i) = c
i += 1 'I'm counting how many characters before I reach the end of the frame
Loop
rcvQ.Remove(0, 1) 'Remove the End of Line character at the start of the buffer. I'm at the beginning of the line
Dim charArray(i - 1) As Char 'Prepare where I'm copying it to, I want 1 less because I don't want the new End of Line character.
rcvQ.CopyTo(0, charArray, 0, i - 1) 'Copy my frame off.
rcvQ.Remove(0, i - 1) 'Remove the frame's data from my queue, but leave the End of Line character. This way, the NEXT time it runs, it 'll grab the next frame of data.
strData = New String(charArray) 'This is ONE LINE OF TEXT, FINALLY!
Catch ex As Exception
'If something is messed up, maybe we really don't know if we're at the top of the frame
booLockedFrame = False
Finally
Threading.Monitor.Exit(rcvQLock) 'Release the lock
End Try
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
'Do something with that text here... like send it to my parse function:
'Parse_receiveddata(strData)
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
End If
'If for some reason processing falls WAY behind... just blow away the queue and start over.
If rcvQ.Length > (10 * intRoughFrameLength) AndAlso rcvQ.Length > 500 Then
Threading.Monitor.Enter(rcvQLock) 'Always lock the receive queue when working with it. You never know when this is getting called again on another thread.
Try
rcvQ = New StringBuilder
booLockedFrame = False
Finally
Threading.Monitor.Exit(rcvQLock)
End Try
End If
Catch ex As Exception
'Do something - something really messed up
End Try
End Sub
#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls
' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
Shutdown_SerialPort()
myPort.Dispose()
End If
End If
disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
Dispose(True)
End Sub
#End Region
End Class
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|