Serial connection update line with response - vb.net

I have a Serial connection to a device and am having trouble handling the response from the device.
If I use Hyperterminal and send the command TIME to the device, I get a response along the lines of;
TIME:13:30:30
which will keep updating on the same line. When I try and do this with my app, depending on whether I'm using a RTB or a TB to display the response, I get either;
RTB;
TIME:13:30:30
TIME:13:30:31
TIME:13:30:32
TIME:13:30:33
TIME:13:30:34
or TB;
TIME:13:30:30TIME:13:30:31TIME:13:30:32TIME:13:30:33TIME:13:30:34
Code is;
Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
ReceivedText(SerialPort1.ReadExisting())
End Sub
Private Sub ReceivedText(ByVal [text] As String)
If Me.RichTextBox1.InvokeRequired Then
Dim x As New SetTextCallback(AddressOf ReceivedText)
Me.Invoke(x, New Object() {(text)})
Else
RichTextBox1.AppendText([text])
TextBox1.AppendText([text])
End If
End Sub
Any way I can get the same response I get when using Hyperterminal?

My approach would be to accumulate data into a StringBuilder until you receive an event that contains the field separator. Then cut the StringBuilder in two at the field separator, set all of the data before the separator into the Text property of the message box, and then continue processing. Something like this should do (pseudocode follows)
StringBuilder builder;
SerialPort1_DataReceived()
receivedData = SerialPort1.ReadExisting()
builder.Append(receivedData)
if receivedData contains delimiter
timeData = builder.ToString().Split(delimiter)
ReceivedText(timedata[0])
builder = new StringBuilder(timedata[1])
end
end
So we read each dispatch from the serial port into our buffer. If we notice that it contains the indicator that a new time value is starting, we split on the indicator, set the text box to contain the beginning of the buffer, and clear the buffer and re-insert the rest.
And in ReceivedText, you would change
RichTextBox1.AppendText([text])
to
RichTextBox1.Text = [text]
Apologies for the lack of VB.NET code, I'm quite rusty on the language so this will probably be easier to understand. Let me know if you have any questions, or if this works at all.

Insure your textBox (TB) has
textBox1.Multiline = True
textBox1.AcceptsReturn = True
As a TB is often used for input, a return may instead of being displayed, is used to signal user input completion.
Less likely avenue of pursuit.
The incoming data TIME:13:30:30 has an EOLine, either a CR, LF or CR+LF. ReadExisting() appears to not have any trouble with the incoming data, hence this is not likely a serial data issue, other than an extra/unexpectented EOL character may be lurking in text (in the CR + LF) case) and TB/RTB is configured to handle them differently. Simple noting the expected length of text will (in)validate this concern.
Notes:
TB "the NewLine value is removed from the input buffer."
The value of NewLine in changeable. The default is a line feed, (NewLine).

Related

Reading Numbers from Webbrowser Vb.net

In my WebBrowser I got text like this: Remaining balance: 10$
I would like to convert it to another currency, I want just to read number from my browser, then after that I will send it to a Label or TextBox with the new converted currency. I am stuck here.
A screenshot of it
Private Sub LinkLabel2_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs) Handles LinkLabel2.LinkClicked
WebBrowser2.Navigate(TextBox9.Text + TextBox2.Text)
End Sub
Private Sub WebBrowser2_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles WebBrowser2.DocumentCompleted
Label1.Text = (WebBrowser2.Document.Body.InnerText)
End Sub
I have this suggestion (I know this is probably not the perfect solution):
First, try loading that same webpage in a normal web browser such as Google Chrome or Firefox or any browser which has the feature to "inspect elements", meaning to view their HTML code.
Find out the element which displays the price you want.
Note down the ID of the element (usually written something like id="someID")
Back to your program's code, include the following Function, which will get the text displayed and convert it to another currency:
Public Function ConvertDisplayedCurrency() As Decimal
Dim MyCurrencyElement As HtmlElement = WebBrowser2.Document.GetElementById("theIdYouGotInStep3") 'This will refer to the element you want.
Dim TheTextDisplayed As String = MyCurrencyElement.InnerText 'This will refer to the text that is displayed.
'Assuming that the text begins like "Remaining balance: ###", you need to strip off that first part, which is "Remaining balance: ".
Dim TheNumberDisplayed As String = TheTextDisplayed.Substring(19)
'The final variable TheNumberDisplayed will be resulting into a String like only the number.
Dim ParsedNumber As Decimal = 0 'A variable which will be used below.
Dim ParseSucceeded As Boolean = Decimal.TryParse(TheNumberDisplayed, ParsedNumber)
'The statement above will TRY converting the String TheNumberDisplayed to a Decimal.
'If it succeeds, the number will be set to the variable ParsedNumber and the variable
'ParseSucceeded will be True. If the conversion fails, the ParseSucceeded will be set
'to False.
If Not ParseSucceeded = True Then Return 0 : Exit Function 'This will return 0 and quit the Function if the parse was a failure.
'Now here comes your turn. Write your own statements to convert the number "ParsedNumber"
'to your new currency and finally write "Return MyFinalVariableName" in the end.
End Function
Call that Function probably when the document of the WebBrowser2 loads, that is, in the WebBrowser2_DocumentCompleted sub.
I hope it helps!

Stream from TCPClient catching data from next packet

I'm developing some software that listens for events from another program via TCP.
The events come in a string formatted like this: "value1;value2;value3"
The problem is that sometimes a single character from value1 gets read onto the previous event so i get something like this:
value1;value2;value3v
alue1;value2;value3
How can i make it know where each message begins and ends?
Here is the code:
Dim client As New TcpClient()
Sub listen()
Dim networkStream As Stream = client.GetStream()
While True
Dim bytes(client.ReceiveBufferSize) As Byte
Dim size As Integer = 0
size = networkStream.Read(bytes, 0, CInt(client.ReceiveBufferSize))
'process event here
End While
End Sub
This is all done on a thread of it's own.
Sender has to included the length of each data its sending so at receiver you will receive upto the provided length. Easier option would be to include NULL termination so that you can read it as individual string at receiver end.

vb.net: listbox.items.add() throws exception in same class

I'm not even sure I understand this situation enough to come up with a proper title. I come from a modest understanding of VB6 and having to climb a steep learning curve for VB 2010.
I am trying to create a multi-client server program that will communicate with my Enterprise iPhone app. I found a relatively simple example to build upon here: http://www.strokenine.com/blog/?p=218. I have been able to modify the code enough to make it work with my app, but with one glitch: I can't get access to the controls on the form to add items, even though the method is invoked within the form's class. (I tried this on the original code too, and it does the same thing. I don't know how the author managed to get it to work.)
Here's the code segment in question:
Public Class Server 'The form with the controls is on/in this class.
Dim clients As New Hashtable 'new database (hashtable) to hold the clients
Sub recieved(ByVal msg As String, ByVal client As ConnectedClient)
Dim message() As String = msg.Split("|") 'make an array with elements of the message recieved
Select Case message(0) 'process by the first element in the array
Case "CHAT" 'if it's CHAT
TextBox3.Text &= client.name & " says: " & " " & message(1) & vbNewLine 'add the message to the chatbox
sendallbutone(message(1), client.name) 'this will update all clients with the new message
' and it will not send the message to the client it recieved it from :)
Case "LOGIN" 'A client has connected
clients.Add(client, client.name) 'add the client to our database (a hashtable)
ListBox1.Items.Add(client.name) 'add the client to the listbox to display the new user
End Select
End Sub
Under Case "LOGIN" the code tries to add the login ID to the listbox. It throws an exception: "A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll" The listbox (all controls, for that matter) is in the same class, Server.vb and Server.vb [Design].
The data comes in from another class that is created whenever a client logs on, which raises the event that switches back to the Server class:
Public Class ConnectedClient
Public Event gotmessage(ByVal message As String, ByVal client As ConnectedClient) 'this is raised when we get a message from the client
Public Event disconnected(ByVal client As ConnectedClient) 'this is raised when we get the client disconnects
Sub read(ByVal ar As IAsyncResult) 'this will process all messages being recieved
Try
Dim sr As New StreamReader(cli.GetStream) 'initialize a new streamreader which will read from the client's stream
Dim msg As String = sr.ReadLine() 'create a new variable which will be used to hold the message being read
RaiseEvent gotmessage(msg, Me) 'tell the server a message has been recieved. Me is passed as an argument which represents
' the current client which it has recieved the message from to perform any client specific
' tasks if needed
cli.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf read, Nothing) 'continue reading from the stream
Catch ex As Exception
Try 'if an error occurs in the reading purpose, we will try to read again to see if we still can read
Dim sr As New StreamReader(cli.GetStream) 'initialize a new streamreader which will read from the client's stream
Dim msg As String = sr.ReadLine() 'create a new variable which will be used to hold the message being read
RaiseEvent gotmessage(msg, Me) 'tell the server a message has been recieved. Me is passed as an argument which represents
' the current client which it has recieved the message from to perform any client specific
' tasks if needed
cli.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf read, Nothing) 'continue reading from the stream
Catch ' IF WE STILL CANNOT READ
RaiseEvent disconnected(Me) 'WE CAN ASSUME THE CLIENT HAS DISCONNECTED
End Try
End Try
End Sub
I hope I am making sense with all this. It all seems to bounce back and forth, it seems so convoluted.
I've tried using Me.listbox1 and Server.listbox1 and several other similar structures, but to no avail.
I'm reading a lot about Invoke and Delegates, but would that be necessary if the method and the control are in the same class? Or do I have a fundamental misperception of what a class is?
Many thanks for any help I can get.
Private Delegate Sub UpdateListDelegate(byval itemName as string)
Private Sub UpdateList(byval itemName as string)
If Me.InvokeRequired Then
Me.Invoke(New UpdateListDelegate(AddressOf UpdateList), itemName)
Else
' UpdateList
' add list add code
ListBox1.Items.Add(itemName)
End If
End Sub
Add above, then replace:
ListBox1.Items.Add(client.name)
to
UpdateList(client.name)
Does it work? check the syntax, may have typo as I type it.

Receiving and Processing HEX data from Serial

I'm new to this forum so I'm not sure if this is really the place to put this - feel free to move to a more appropriate forum if required.
Okay so I'm writing a simple application in VB 2010 that sends and receives HEX data to a serial device. Now I have the "sending" part down pat, however, I'm having difficulty with the receiving part. The device sends a constant stream of HEX data back to the application every second; I need the application to look at certain bytes of the received HEX string and do things accordingly within IF Statements (e.g. If byte 10 of the string is equal to "&10" then do ... ).
I'm not sure how to get this data, parse the correct byte out of it and then do something with it. :/ The code I've attempted to use for this (but does not work) is as follows:-
Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
Dim statusget() As String
Dim str1 As String
str1 = SerialPort1.ReadExisting()
statusget = str1.Split(",")
If statusget(10) = "&01" Then
btn_tx1.BackColor = Color.Red
btn_tx2.BackColor = SystemColors.Control
btn_tx3.BackColor = SystemColors.Control
btn_tx4.BackColor = SystemColors.Control
btn_tx5.BackColor = SystemColors.Control
btn_tx6.BackColor = SystemColors.Control
btn_tx7.BackColor = SystemColors.Control
btn_tx8.BackColor = SystemColors.Control
End If
End Sub
Additionally, I have a Rich-Text box that displays all data received from the device so I can monitor it. It works fine, except for the fact that it returns in a weird encoding (not HEX) such as "uZ!!!??".
Thank you for any help you can provide. :)
Edit - Code for Sending data:
Private Sub btn_tx1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_tx1.Click
Dim buftx1() As Byte = {&HF0, &H5A, &HF, &H5, &H1, &H11, &H0, &H0, &H0, &H0, &H0, &H0, &H30, &H37, &HFF}
SerialPort1.Write(buftx1, 0, buftx1.Length)
End Sub
Avoid thinking about "hex" data, as hex is merely a way to format binary data into a string that's easy to read by humans.
Clearly, your device is using a binary protocol; your Click event handler is proof of that. Which invariably means that it also returns binary data, not text. Which makes using SerialPort.ReadExisting() wrong, that reads text. The result of that is the garbage that you are now seeing, particularly the question marks. Those are produced because it doesn't know how to convert the bytes it receives to ASCII characters.
You must instead use SerialPort.Read() to fill a Byte(). Be sure to pay attention to the return value of Read(); that tells you how many bytes you got. The result will be less than the number of bytes you asked for. Keep calling Read() until you get the full response from the device according to the protocol rules.
Once you got the full response you can then process it using the data that the device returned. Displaying it in a RichTextBox is not terribly useful, other than for debugging purposes. You can use the BitConverter.ToString(Byte()) overload to generate a hex string from the bytes. Be sure to use Control.BeginInvoke() in your DataReceived event handler, as you cannot update the UI directly since the event runs on a worker thread.
If you need help with the protocol rules then contact the vendor of the device for support.

Overwrite Visual Basic label text

I am reading a temperature value from a transceiver into the serial port and I want this value to change the value of a label in my Visual Basic Form. This value changes every few seconds. I am using the code below:
Me.dataReceived.Text &= [text]
where dataReceived is the label I am using and [text] is the data I am reading from the serial port. This results in the data being displayed but instead of overwriting the label, it writes the value after each other. (The data is appended). I tried to remove the & before the = but this did not work as nothing showed up. Any ideas on what I can do?
The code I am using is the following:
'To receive data into the text field
Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
While (SerialPort1.IsOpen)
ReceivedText(SerialPort1.ReadExisting()) 'This is called automatically every time data is received at the Serial Port
End While
End Sub
Private Sub ReceivedText(ByVal [text] As String)
Dim temperature As String
'This function compares the creating Thread's ID with the calling Thread's ID
If Me.dataReceived.InvokeRequired Then
Dim x As New SetTextCallback(AddressOf ReceivedText)
Me.Invoke(x, New Object() {(text)})
Else
'To output to the text box:
Me.dataReceived.Text = text
'To output to the label
temperature = text
Me.temp.Text = text
hmd.Text = temperature
End If
End Sub
If you want to overwrite the old value, you should not use &= but just = to assign the new value:
Me.dataReceived.Text = newText
&= is the same as Me.dataReceived.Text = Me.dataReceived.Text & newText
From MSDN:
The &= operator concatenates the String expression on its right to
the String variable or property on its left, and assigns the result to
the variable or property on its left.
Try this:
If (text IsNot Nothing) AndAlso (text.Trim().Length <> 0) Then
Me.dataReceived.Text = text
End If
I'm not that familiar with the SerialPort class, but I'll do my best to explain what's going on. The serial port raises the data received event when new data comes in through the serial port. Your code then reads all the existing data that has been received. When you call the ReadExisting method, it only returns what has been received up to that point. It does not wait for all the data to come in before it returns. It then calls the ReceivedText method which sets the value of some controls to the text that was received. The InvokeRequired and Invoke calls are only there to get you back on the UI thread. Apparently the SerialPort's DataReceived event may be raised on a separate thread, so you need to get back to the UI thread before you can do anything to any of the controls on the form. The change I suggested simply checked to make sure that the text recieved was not null or empty before you changed the value of the text box. The odd thing about the code is that it continues to read from the serial port until it is no longer open. I wouldn't think you'd want to do that. Rather, I would think that in each DataReceived event, you would just call ReadExisting once and then just expect it to be raised again the next time more data is received. By continuously calling ReadExisting in a loop, it must either return a null string or an empty string if there is no more data to read, which was why the text box was being blanked out.
&= is concatinating your text. use = to over write lable.
Me.dataReceived.Text = text
I'm not sure what is happening, but its likely that text does not have a value when setting the dataReceived.Text. Try this,
Private Sub ReceivedText(ByVal datatext As String)
Me.Invoke(Sub()
'To output to the text box:
Me.dataReceived.Text = datatext
Me.temp.Text = datatext
hmd.Text = datatext
End Sub)
End If
End Sub
I changed text to datatext since text could be confused with the forms local text property. I also moved the setting of the text properties of the labels/textboxes to an invoke using a lambda (note this syntax is new and only will work in VB 2010). I feel like somehow your invoke statement might not have been passing the string value properly. Also I removed the check for InvokeRequired, since the doing an invoke will work in both situations and it looks like you might just be doing threaded calls each time anyways.
If that compiles for you, it should work. If not, its likely that RecievedText is never being called. Set some breakpoints and step through the code to see that datatext has a value and that ReceivedText actually gets called.