Receiving and Processing HEX data from Serial - vb.net

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.

Related

Why am I getting error: BC30452 Operator '&' is not defined for types 'String' and 'Image', for this code?

I have an application which has a server and a client file. On the server side, the server's camera (in my case, this is my camera), get's streamed to the client file, and then the clients camera (in my case, my camera because I'm using this for educational purposes only) get's streamed to the server file.
Right now I'm in the process of writing this program, but I'm getting an error when I'm trying to stream the client's camera.
This is what the client program looks like:
Now, on the Form_Load sub, this code is declared
Public Class videocallfinal
Public xxx As Integer
Public Yy As String = "|U|"
Dim pic As New Drawing.Bitmap(500, 400)
Dim Touchless As New TouchlessLib.TouchlessMgr
Dim camera1 As TouchlessLib.Camera = Touchless.Cameras.ElementAt(0)
Private Sub videocallfinal_Load(sender As Object, e As EventArgs) Handles MyBase.Load
q.Comet.Send("camerastream" & Yy & PictureBox2.Image)
PictureBox2.Image = camera1.GetCurrentImage
Touchless.CurrentCamera = camera1
Touchless.CurrentCamera.CaptureHeight = 130
Touchless.CurrentCamera.CaptureWidth = 130
Me.BringToFront()
Me.Text = "You're currently in a video call with " + videocall.Label6.Text
Label6.Text = videocall.Label6.Text
End Sub
So, as you can see, I'm trying to send the PictureBox image to the server file on this line of code:
q.Comet.Send("camerastream" & Yy & PictureBox1.Image)
Under PictureBox1.Image, I get this error
BC30452 Operator '&' is not defined for types 'String' and 'Image'.
I've done some re-search on this issue, but it's usually with different controls, and I've never dealt with an error like this with a PictureBox.
Can someone explain what I'm doing wrong here?
Thanks!
You can convert the image to a Base64 string by using a MemoryStream to get the bytes. The following untested code, written from the docs for MemoryStream and Image, will do exactly this:
Dim s As New MemoryStream
PictureBox1.Image.Save(s, System.Drawing.Imaging.ImageFormat.Bmp)
Dim asBase64 = System.Convert.ToBase64String(s.ToArray())

Get file size before download it in vb

I have been working on a web browser in visual basic..Now,what I want to do is to the get file size before download it and when I click download I want to to get the number of the alrady downloaded Mbs(watch the picture)
Thank's for help!
I've done some research, and this would probably be the most simple and "cleanest" way of getting a download's size (in bytes):
Public Function GetDownloadSize(ByVal URL As String) As Long
Dim r As Net.WebRequest = Net.WebRequest.Create(URL)
r.Method = Net.WebRequestMethods.Http.Head
Using rsp = r.GetResponse()
Return rsp.ContentLength
End Using
End Function
Credit to Reed Kimble, who told me to dispose the WebResponse in my initial MSDN question.
The above code will read the response headers of the file, rather than reading the body of it. This means that the file does not require to get downloaded, just to check it's size.
This is the reason to why some codes requires the file to actually get downloaded at first; they're reading the file's body rather than it's headers.
Hope this helps!
Use the WebClient ResponseHeaders:
Public Shared Function GetFileSize(url As String) As Long
Using obj As New WebClient()
Using s As Stream = obj.OpenRead(url)
Return Long.Parse(obj.ResponseHeaders("Content-Length").ToString())
End Using
End Using
End Function
Request file size before download it
The WebClient's DownloadProgressChanged event's args contains the property TotalBytesToRecieve. That tells you how many bytes the file you're downloading is.
Not the prettiest way, but if you want to get the size of the file before downloading you can start downloading the file then immediately cancel it:
Dim DownloadSize As Long
Private Sub CheckDownloadSize(ByVal URL As String)
WebClient.DownloadFile(URL, IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.Temp, "tempdownload.tmp"))
End Sub
Private WithEvents WebClient As New WebClient
Private Sub WebClient_DownloadProgressChanged(ByVal sender As Object, ByVal e As System.Net.DownloadProgressChangedEventArgs) Handles WebClient.DownloadProgressChanged
DownloadSize = e.TotalBytesToReceive
WebClient.CancelAsync()
End Sub
Otherwise, just remove the .CancelAsync() line.

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.

Serial connection update line with response

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).

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.