Hex string to byte array
-
Saturday, August 04, 2012 3:05 AM
I need to convert a string of HEX values into a byte array which I then use as an argument for the Write() of System.IO.Ports SerialPort
I.E. my hex values are: 04 30 30 30 30 50 56 05
As I am sending HEX numbers to a serial device (Temperature controller) I guess they need to look like:
&H04&H32&H30&H30&H30&H30&H50&H56&H05 '&H32 being the space character
and my port.Write method needs to look like port.Write(myByteArray, 0, myByteArray.length) ' port being my instance of a serialPort object
I have no idea how to do this as my VB skills are very limited so any help greatefully accepted
All Replies
-
Saturday, August 04, 2012 4:54 AM
Hi, this is the method that will convert hex to byte[] :
Public Shared Function stringToByteArray(text As String) As Byte() Dim bytes As Byte() = New Byte(text.Length \ 2 - 1) {} For i As Integer = 0 To text.Length - 1 Step 2 bytes(i \ 2) = Byte.Parse(text(i).ToString() & text(i + 1).ToString(), System.Globalization.NumberStyles.HexNumber) Next Return bytes End Function
Mitja
-
Saturday, August 04, 2012 6:08 AM
Does this help?
Public Class Form1
Dim InsertedErrorCharacter As String = ""
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
Dim SampleHexString As String = "&H04&H32&H30&H" & InsertedErrorCharacter & "30&H30&H30&H50&H56&H05"
MsgBox("Original: " & SampleHexString & vbCrLf & "Tested : " & BytesToHexString(HexToBytes(SampleHexString)))
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Function HexToBytes(ByVal Hex As String) As Byte()
Dim Result As New List(Of Byte)
If Not HexIsValid(FormatHex(Hex)) Then Throw New Exception("Invalid hexidecimal number cannot be converted!")
For I = 0 To FormatHex(Hex).Length - 1 Step 2
Result.Add(Convert.ToByte(FormatHex(Hex).Substring(I, 2), 16))
Next : Return Result.ToArray()
End Function
Function BytesToHexString(ByVal Bytes() As Byte) As String
Return "&H" & Replace(BitConverter.ToString(Bytes, 0), "-", "&H")
End Function
Function HexIsValid(ByVal Hex As String) As Boolean
For Each Nibble As String In Hex
If "0123456789ABCDEFabcdef".IndexOf(Nibble, 0) = -1 Then Return False
Next : Return True
End Function
Function FormatHex(ByVal Hex As String) As String
Hex = Replace(Hex, "&h", "") : Hex = Replace(Hex, "&H", "")
If (Len(Hex) Mod 2) = 1 Then Hex = "0" & Hex
Return Hex
End Function
End Class
If you want something you've never had, you need to do something you've never done. If you believe something to be true, then one day you will be called upon to demonstrate that truth.
- Edited by Paul IshakMicrosoft Community Contributor Saturday, August 04, 2012 6:56 AM
-
Saturday, August 04, 2012 4:08 PM
'&H32 being the space character
Just in case you mean the usual ASCII space character, it's &H20, i.e. 32 in decimal.
How do you determine that a space is to be inserted in your list of values, as it doesn't appear in your first list?
--
Andrew- Proposed As Answer by Reed KimbleMicrosoft Community Contributor, Moderator Monday, August 06, 2012 5:03 PM
-
Monday, August 06, 2012 10:46 AM
Hi Paul,
Thanks for the effort you put into your answer...
I am a photographer/photo-processor and I am renovating an OLD film processor. I have removed all the electronic components which were useless and replaced them with a PC controlled relay board for switching pumps, motors, solenoids, heaters etc on/off. The relay board came with a VB library and is super easy to programme in VB... I now have x2 temperature controllers that use RS485 comms. I can use some free software that lets me send the HEX values and I get a responce ok, so the hardware is all fine... the problem is when I try to send the same HEX values from a VB app, I can't get the formatting correct as I don't know enough VB to understand what your code is trying to do.
So... is it possible for you to write your code as a separate "class" that I can instantiate in my app? Something like...
DIM obByteArrray AS stringHexToByteArray
obByteArrray = New stringHexToByteArray()
myByteArray = obMyByteArrray.Convert("string to parse")Where your class would have a "Convert(ByVal hexvalues AS String )" function.
I could then simply use the returned ByteArray as the argument for my IO.Ports.SerialPort Write method...
serialPort1.Write(myByteArray, 0, myByteArray.length)
Am I making sense? I'm really not good with this VB coding stuff and I just need to get on with fixing the processor and the temp controllers are the last item to fix.
Many Thanks
Mark
-
Monday, August 06, 2012 12:03 PM
Your hex values are bytes. A byte array of them looks like:
Dim B() as byte = {04, 30, 30, 30, 30, 50, 56, 05}
- Proposed As Answer by Reed KimbleMicrosoft Community Contributor, Moderator Monday, August 06, 2012 5:03 PM
-
Monday, August 06, 2012 2:06 PM
Here you go:
Public Class MyStringConversions ' a shared method does not require instantiation of its containing class Public Shared Function StringToByteArray(s As String) As Byte() ' remove any spaces from, e.g. "A0 20 34 34" s = s.Replace(" "c, "") ' make sure we have an even number of digits If (s.Length And 1) = 1 Then Throw New FormatException("Odd string length when even string length is required.") End If ' calculate the length of the byte array and dim an array to that Dim nBytes = s.Length \ 2 Dim a(nBytes - 1) As Byte ' pick out every two bytes and convert them from hex representation For i = 0 To nBytes - 1 a(i) = Convert.ToByte(s.Substring(i * 2, 2), 16) Next Return a End Function End Classand to test it:
Module Module1 Sub Main() ' test it with some bytes, with some optional spaces in the string Dim myBytes = MyStringConversions.StringToByteArray("4865 6C 6C 6F2E") ' confirm it outputs "Hello." For i = 0 To myBytes.Length - 1 Console.Write(Chr(myBytes(i))) Next Console.ReadLine() End Sub End ModuleHTH,
Andrew
- Marked As Answer by Lingoer Tuesday, August 07, 2012 9:09 AM
-
Monday, August 06, 2012 4:01 PM
Hi Lingoer, try this:
Example Usage:
Option Strict On
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'Your Hex String
Dim SampleHexString As String = "&H04&H32&H30&H30&H30&H30&H50&H56&H05"
'Create a new Hex Converter
Dim HexConverter1 As New HexConverter
'Change the value of the Hexconverter by setting the string
HexConverter1.HexString = SampleHexString
'Get the conversion result from the hexconverter
Dim ResultBytes As Byte() = HexConverter1.HexBytes
'Create a new hexconverter to test that it works
Dim HexConverter2 As New HexConverter
'This time change the value of the hex converter by setting its bytes instead of
'setting its string
HexConverter2.HexBytes = HexConverter1.HexBytes
'check if the conversion matches the original string
If SampleHexString = HexConverter2.ToString And SampleHexString = HexConverter2.HexString Then
MsgBox("The class works.")
Else
MsgBox("The class does not work")
End If
End Sub
End ClassHex Converter Class:
Public Class HexConverter
Private Property _HexString As String
Private Property _HexBytes As Byte()
Public Property HexString As String
Get
Return _HexString
End Get
Set(ByVal value As String)
_HexString = value
_HexBytes = HexToBytes(value)
End Set
End Property
Public Property HexBytes As Byte()
Get
Return _HexBytes
End Get
Set(ByVal value As Byte())
_HexBytes = HexBytes
_HexString = BytesToHexString(value)
End Set
End Property
Private Function HexToBytes(ByVal Hex As String) As Byte()
Dim Result As New List(Of Byte)
If Not HexIsValid(FormatHex(Hex)) Then Throw New Exception("Invalid hexidecimal number cannot be converted!")
For I = 0 To FormatHex(Hex).Length - 1 Step 2
Result.Add(Convert.ToByte(FormatHex(Hex).Substring(I, 2), 16))
Next : Return Result.ToArray()
End Function
Private Function BytesToHexString(ByVal Bytes() As Byte) As String
Return "&H" & Replace(BitConverter.ToString(Bytes, 0), "-", "&H")
End Function
Private Function HexIsValid(ByVal Hex As String) As Boolean
For Each Nibble As String In Hex
If "0123456789ABCDEFabcdef".IndexOf(Nibble, 0) = -1 Then Return False
Next : Return True
End Function
Private Function FormatHex(ByVal Hex As String) As String
Hex = Replace(Hex, "&h", "") : Hex = Replace(Hex, "&H", "")
If (Len(Hex) Mod 2) = 1 Then Hex = "0" & Hex
Return Hex
End Function
Public Shadows Function ToString() As String
Return _HexString
End Function
End Class
If you want something you've never had, you need to do something you've never done. If you believe something to be true, then one day you will be called upon to demonstrate that truth.
- Edited by Paul IshakMicrosoft Community Contributor Monday, August 06, 2012 4:17 PM
-
Monday, August 06, 2012 4:48 PM
Try this one as well, see which you like better:
Option Strict On
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'Create a new hexconverter1 with your Hex string
Dim HexConverter1 As New HexConverter("&H04&H32&H30&H30&H30&H30&H50&H56&H05")
'Create a new hexconverter2 using the resultant bytes of hexconverter1
Dim HexConverter2 As New HexConverter(HexConverter1.HexBytes)
'Check the conversion result.
Select Case HexConverter1.HexString = HexConverter2.HexString
Case True : MsgBox("Success!")
Case Else : MsgBox("Failure!")
End Select
End Sub
End Class
Public Class HexConverter
Private Property _HexString As String
Private Property _HexBytes As Byte()
Public Property HexString As String
Get
Return _HexString
End Get
Set(ByVal value As String)
_HexString = value
_HexBytes = HexToBytes(value)
End Set
End Property
Public Property HexBytes As Byte()
Get
Return _HexBytes
End Get
Set(ByVal value As Byte())
_HexBytes = HexBytes
_HexString = BytesToHexString(value)
End Set
End Property
Private Function HexToBytes(ByVal Hex As String) As Byte()
Dim Result As New List(Of Byte)
If Not HexIsValid(FormatHex(Hex)) Then Throw New Exception("Invalid hexidecimal number cannot be converted!")
For I = 0 To FormatHex(Hex).Length - 1 Step 2
Result.Add(Convert.ToByte(FormatHex(Hex).Substring(I, 2), 16))
Next : Return Result.ToArray()
End Function
Private Function BytesToHexString(ByVal Bytes() As Byte) As String
Return "&H" & Replace(BitConverter.ToString(Bytes, 0), "-", "&H")
End Function
Private Function HexIsValid(ByVal Hex As String) As Boolean
For Each Nibble As String In Hex
If "0123456789ABCDEFabcdef".IndexOf(Nibble, 0) = -1 Then Return False
Next : Return True
End Function
Private Function FormatHex(ByVal Hex As String) As String
Hex = Replace(Hex, "&h", "") : Hex = Replace(Hex, "&H", "")
If (Len(Hex) Mod 2) = 1 Then Hex = "0" & Hex
Return Hex
End Function
Public Shadows Function ToString() As String
Return _HexString
End Function
Sub New(Optional ByVal Value As Object = Nothing)
Select Case Value.GetType
Case GetType(Byte()) : HexBytes = DirectCast(Value, Byte())
Case GetType(String) : HexString = DirectCast(Value, String)
Case Else
If Not Value Is Nothing Then
Throw New ArgumentException("The " & Value.GetType.ToString & _
" Object type is invalid for this function. Use objects of the type Byte() or String.")
End If
End Select
End Sub
End Class
If you want something you've never had, you need to do something you've never done. If you believe something to be true, then one day you will be called upon to demonstrate that truth.
- Edited by Paul IshakMicrosoft Community Contributor Monday, August 06, 2012 4:54 PM
-
Monday, August 06, 2012 5:02 PMModerator
It looks like you've got your hex and decimal representations mixed up; I think the numbers you listed are in decimal.
An ASCII Space is decimal 32, which is &H20 in hex... &H32 is the "2" character (decimal 50). You can refer to an ASCII table that lists multiple base values: http://www.asciitable.com/
To make the code easy to use during development, consider starting with a simple hard-coded array of values, something like:
Dim dataToSend(8) As Byte '9-byte array dataToSend(0) = 4 dataToSend(1) = Asc(" ") 'or could be entered as 32 or &H20 dataToSend(2) = 30 dataToSend(3) = 30 dataToSend(4) = 30 dataToSend(5) = 30 dataToSend(6) = Asc("2") 'or could be entered as 50 or &H32 dataToSend(7) = Asc("8") 'or could be entered as 56 or &H38 dataToSend(8) = 5 SerialPort1.Write(dataToSend, 0, dataToSend.Length)
I think you'll be ok though once you are providing the characters in the correct base.Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
-
Monday, August 06, 2012 10:22 PM
Hi Lingoer,
Give this function a try.
Add one Button to a Form to try this code please.
'
Option Strict On Option Explicit On Option Infer Off Public Class Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim testString As String = "&H04&H32&H30&H30&H30&H30&H50&H56&H05" Dim byteArray() As Byte byteArray = ToByteArray(testString) 'This section just shows some output as number values.>> Dim sb As New System.Text.StringBuilder If byteArray.Length > 0 Then For index As Integer = 0 To byteArray.GetUpperBound(0) sb.Append(byteArray.ToString().PadLeft(2, Convert.ToChar("0")) & System.Environment.NewLine) Next End If MessageBox.Show(sb.ToString) End Sub Private Function ToByteArray(ByVal someString As String) As Byte() Dim byteList As New List(Of Byte) Try Dim tempString As String = someString.Replace("&H", "") If tempString.Length Mod 2 = 1 Then tempString = "0" & tempString For index As Integer = 0 To tempString.Length Step 2 byteList.Add(Convert.ToByte(Val("&H" & tempString.Substring(index, 2)))) Next Catch ex As Exception MessageBox.Show(ex.ToString) End Try Return byteList.ToArray End Function End Class
Regards,
Click this link to see the NEW way of how to insert a picture into a forum post.
Installing VB6 on Windows 7
App Hub for Windows Phone & XBOX 360 developers.
-
Monday, August 06, 2012 11:11 PM
As this turns out to be a hex-to-array competition, here's another one: ;-)
Public Shared Function FromHexString(ByVal Hex As String) As Byte() Dim result As Byte() Dim Index As Integer If Hex Is Nothing Then Throw New ArgumentNullException("Hex") End If If (Hex.Length And 1) = 1 Then Throw New ArgumentException("Number of characters must be even", "Hex") End If ReDim result((Hex.Length \ 2) - 1) For i = 0 To Hex.Length - 1 Step 2 Dim nibble As UInteger Dim value As Byte nibble = Convert.ToUInt32(Hex(i)) - 48UI If (nibble And &H10UI) <> 0 Then nibble = (nibble And &HFUI) + 9UI End If value = CByte(nibble << 4) nibble = Convert.ToUInt32(Hex(i + 1)) - 48UI If (nibble And &H10UI) <> 0 Then nibble = (nibble And &HFUI) + 9UI End If result(Index) = value Or CByte(nibble) Index += 1 Next Return result End Function
I think it's quick.
Armin
-
Tuesday, August 07, 2012 1:54 AM
Thanks Andrew... and everyone else who contributed... This class did the trick... so now all I need to do is send:
"04 30 30 30 30 50 56 05" as an argument and put the returned byteArray into my serialPort1.Write method... and it WORKS! Awesome. this allows me to query my temp controller to get the current temperature. Fantastic.
I now need to Write a "Set Point" to the temp controller so I can use different photographic process temperatures... I.E: E6 slide film is process at 36.8 C and colour neg is processed at 36.2 C, so I want to be able to set this from software.
The specs to Set a value on the temp controller are:
[EOT](addr_H)(addr_H)(addr_L)(addr_L)[STX](C1)(C2)<DATA>[ETX](BCC)
So in theory, to set a "set-point" to 450 C I should be able to send the follow string of HEX values to your class:
"04 30 30 30 30 02 53 4C 34 35 30 2D"
Only, this doesn't work... no responce at all from the temp controller... which means a formatting error and I suspect the BCC (block checksum) which is generated by a XOR'ing all the characters After and Excluding the STX (02) and Including the ETX (03) - in this case that would be: "53 4C 34 35 3 03"
I have no idea how to perform an XOr on these values.
Any ideas?
thanks
Mark... BTW: I'm happy to pay you for you time... just let me know how.
-
Tuesday, August 07, 2012 4:22 AM
There is an XOR operation available in VB. Because your string is being converted to bytes you can use XOR directly to calculate the checksum, but you will have to manipulat thhbyte array in order to calculate it and insert it.
You should include a dummy value for the checksum in your source string so that the returned array is the correct size.
If you know the relevant string positions you can do it like this. Assuming that the STX is at position 5 in byte array B() and the ETX is at array position 12, the code to insert the checksum at array position 13 is:
Dim X As Byte = B(6) For I As Integer = 7 To 12 X = X Xor B(I) Next B(13) = XWhether or not you could conveniently build that into the class depends on whether you can rely on either the positions or the values of the start and end of the checksummed sequence - if you had to nominate them for each case it would hardly be worth the effort of making that calculation a mthod of the class. -
Tuesday, August 07, 2012 9:07 AM
Thanks Acamar,
I have amended your snippet to;
Dim myLength As Int16 = myBytes.Length
Dim X As Byte = myBytes(6) For I As Integer = 7 To myLength - 2 X = X Xor myBytes(I) Next myBytes(myLength - 1) = X
Positions 0~ 6 in the array are always the same... so all I need to do is XOR the bytes at pos 6 through to the second-to-last byte, then put the ensuing values into the last byte position of the array.
I can see the mechanics working in the "locals" window of a debug session... but I have no idea what the XOr is supposed to be doing (absolute beginner here)... anyway, the temp controller still will not accept the argument and won't respond :((
- Marked As Answer by Lingoer Monday, August 20, 2012 11:17 PM
-
Tuesday, August 07, 2012 10:05 AM
Do you have a sample string that you know is correctly calculated so you can can process it through the method to confirm that the result is correct?
XOR is described here:
http://msdn.microsoft.com/en-us/library/csw1x2a6(v=vs.100).aspx
It is the bitwise (numeric) version, not the boolean.If you still can't make it work then I would guess that's because the data portion is not set up correctly. If you have a reference to the www site that describes the command protocol for this device I can check your interpretation of the format.
-
Tuesday, August 07, 2012 11:11 AM
Thanks Acamar,
The protocol documentation is called: AL808 communication protocol (PDF)
It can be found here: http://www.altec.cc/english/downloads.htm
well, I am now getting a responce from the AL808 Temperature controller at least... always a "nak" unfortunately... but better than nothing... which really makes me think it is the BCC that is wrong.
I'll read up some more on XOr... but I find this stuff difficult... give me some photographic chemistry issues any day... computers are not my thing to be honest.
Cheers Mark
-
Tuesday, August 07, 2012 11:42 AM
Ok Guys... Scratch all that last post... ALL FIXED!
I can now write to the temp controller AND read the current temperature!
FANTASTIC... Thanks so much for everyones assistance.
Cheers
Mark
-
Tuesday, August 07, 2012 11:45 AM
Ah, it may be asking a bit much to try to set a temperature to 450C (unless the processor includes an air dryer with a heater that will go up to that). I think you may have better luck using 45.0 - i.e 34 35 2E 30 as that is what it appears to mean in section 5 where it says "The value of a parameter in a given display format. e.g. 99.2, 1.2, -999, >1234 etc." And the NAK would be compliant with section 6 where it says that NAK is the reply for attempting to set a parameter outside the limits, well it actually says "read", but it would make more sense if it said "write".
Also, I wonder if you have to make sure that the high setpoint is always higher than the low setpoint, so you can't have
low=30, high=32 then change to low=33 high=34.
Instead, you'd have to do
low=30, high=32 then change to high=34, low=33
HTH,
Andrew
-
Tuesday, August 07, 2012 11:54 AM
That code calculates a correct checksum. The example from the manual can be passed as
"04 34 34 33 33 02 53 4C 34 35 30 03 FF"
and when the byte array is returned the BCC calculation adjusts it to the byte equivalent of
"04 34 34 33 33 02 53 4C 34 35 30 03 2D"
which agrees to the example. That suggests a parameter error. What is your hex format of the ASCII command you are trying to send? -
Tuesday, August 07, 2012 12:00 PM
Hi Andrew... yep, completely correct... I hadn't gotten around to checking/setting other params, but I started playing around with 20, 21, 23 Deg C and it started to work...
I also put a small routine in that looped the execution until the write buffer was finished (just in case)
If SerialPort1.IsOpen then Do While (SerialPort1.BytesToWrite > 0) Loop End If
That also helped as I wasn't getting a the Comms LED on the temp controllers front panel to "flash" each time I clicked the calling button on my form... was intermittent.
All good now though... getting "06" returned.... ACK and the display changes to what I set it to... yipppeee.
Time to ge this damn machine working and paying me back some of the $$$ I have spent on it.
Many Thanks Again!
Mark
-
Tuesday, August 07, 2012 12:08 PM
Thanks Acamar,
Yep, your Xor routine works perfectly...
See my responce to Andrew (below)... may have been a timing issue with the write/read methods... or I might have been "out of bounds" with the values I was trying to write to the unit (Local Set Point higher than Hi Alarm set point etc).
Thanks for your help on this... I can now get on with what I AM good at.
Cheers
Mark

