Android Question Convert HEX in ASCII

StarinschiAndrei

Active Member
Licensed User
Hi,
Is there any function that can convert hex 15e in 30 31 35 3E ?
I tried to use:
B4X:
dim val as string     '15e
Log(bc.HexFromBytes (bc.StringToBytes(val,"UTF8")))
but the result is : 313565
 

emexes

Well-Known Member
Licensed User
Hi, the input is 15e and output should be 30 31 35 3E
This will do what precisely what you've asked for (and more ;-) and might thus tease out whether that's actually what you want:

B4X:
Sub SingleDigitHexToDoubleDigitHex(SingleDigitHexString As String, DigitPrefix As String, MinDigits As Int) As String

    Dim HexChar As String
    Dim DoubleDigitHexString As String = ""
    Dim NumDigits As Int = 0

    For I = 0 To SingleDigitHexString.Length - 1
        HexChar = SingleDigitHexString.SubString2(I, I+1).ToUpperCase    'would prefer CharAt, if only it would cast as a string
        If "0123456789ABCDEF".Contains(HexChar) Then
            DoubleDigitHexString = DoubleDigitHexString & DigitPrefix & HexChar
            NumDigits = NumDigits + 1
        Else
            'not a valid hex digit, so do whatever seems appropropriate here
        End If
    Next

    Do While NumDigits < MinDigits
        DoubleDigitHexString = DigitPrefix & "0" & DoubleDigitHexString
        NumDigits = NumDigits + 1
    Loop
   
    Return DoubleDigitHexString.Trim    'removes leading space (if DigitPrefix includes one as a separator)

End Sub

Sub Tester
   
    Dim SingleDigitHexString As String
    Dim DoubleDigitHexString As String
   
    SingleDigitHexString = "15e"
    DoubleDigitHexString = SingleDigitHexToDoubleDigitHex(SingleDigitHexString, " 3", 4)    'note DigitPrefix includes separator space
   
    Log("input = " & SingleDigitHexString)
    Log("output = " & DoubleDigitHexString)
   
End Sub
 

StarinschiAndrei

Active Member
Licensed User
Dear Klaus ,
I'm try to communicate with a device via Bluetooth based on the below protocol. I have to calculate BCC (position 6).
upload_2019-2-25_11-13-23.png


B4X:
Dim bc As ByteConverter
    Dim bb As BytesBuilder
    Dim bcc As Int
    bb.Initialize
    Dim c As String=""
Dim CMD() As String=Regex.Split(",",TXTMSG.Text)
bb.Append(Array As Byte(0x01))
bb.Append(Array As Byte(0x25))
bb.Append(bc.HexToBytes(Bit.ToHexString(Rnd(32,255))))
bb.Append (bc.HexToBytes("45"))   
            
If CMD(4)="0" Then
    bb.Append(bc.StringToBytes("0","UTF8"))
End If
bb.Append (bc.HexToBytes("05"))

Bit.ParseInt(bc.StringFromBytes(bb.SubArray2(3,4),"UTF-8") ,16)))
    Dim pz1 As String=bc.HexFromBytes( bb.SubArray2(1,2))
    Dim pz2 As String=bc.HexFromBytes( bb.SUBARRAY2(2,3))
    Dim pz3 As String=bc.HexFromBytes( bb.SUBARRAY2(3,4))
    Dim pz4 As String=bc.HexFromBytes( bb.SUBARRAY2(4,5))
    Dim pz5 As String=bc.HexFromBytes( bb.SUBARRAY2(5,6))
    Dim val As String=Bit.ToHexString( Bit.ParseInt(pz1,16)+Bit.ParseInt(pz2,16)+Bit.ParseInt(pz3,16)+Bit.ParseInt(pz4,16)+Bit.ParseInt(pz5,16))
    Dim CF() As Byte=val.GetBytes("UTF8")
    Log(CF.Length)
    For I=0 To 3-CF.Length
        bb.Append(bc.HexToBytes("30"))
    Next
    For J=0 To CF.Length-1
        Log(CF(J))
        'bb.Append(bc.HexToBytes(bc.StringToBytes( Chr(3)&CF(J),"utf8")))
    Next
LBLCMD.Text=    bc.HexFromBytes(bb.ToArray)
 

emexes

Well-Known Member
Licensed User
Hey Andrei, turns out we're a lot closer to your solution than I thought. Bonus! In fact, the solution should be even simpler the one above, especially if we're allowed to assume that BCC is always 4 ASCII bytes long.
 

StarinschiAndrei

Active Member
Licensed User
Hey Andrei, turns out we're a lot closer to your solution than I thought. Bonus! In fact, the solution should be even simpler the one above, especially if we're allowed to assume that BCC is always 4 ASCII bytes long.
Hey emexes, thank you ! ! ! . I will try your code.
 

emexes

Well-Known Member
Licensed User
No, no... that's not code... THIS is code!

B4X:
'convert unsigned number 0..255 to signed byte -128..127 (sometimes I hate Java)
Sub UnsignedToSignedByte(X As Int) As Byte
   
    If X > 0 And X < 255 Then
        If X < 128 Then
            Return X
        Else
            Return X - 256
        End If
    Else
        'out of range, do whatever you have to do
    End If
   
End Sub

'convert signed byte -128..127 to unsigned number 0..255
Sub SignedByteToUnsigned(X As Byte) As Int
   
    'no need to check input range since all results fit within int
   
    If X < 0 Then
        Return X + 256
    Else
        Return X
    End If
   
End Sub

Sub PacketForAndrei(Seq As Byte, Cmd As Byte, DataBytes() As Byte) As Byte()
   
    Dim PacketLength As Int = 4 + DataBytes.Length + 6
   
    Dim PacketBytes(PacketLength) As Byte
   
    Dim PutPtr As Int = 0
   
    'Position 1 = SOH
    PacketBytes(PutPtr) = 0x01 
    PutPtr = PutPtr + 1
   
    'Position 2 = LEN
    PacketBytes(PutPtr) = UnsignedToSignedByte(0x20 + 3 + DataBytes.Length + 1)    'note offset by 0x20
    PutPtr = PutPtr + 1
   
    'Position 3 = SEQ
    PacketBytes(PutPtr) = Seq
    PutPtr = PutPtr + 1
   
    'Position 4 = CMD
    PacketBytes(PutPtr) = Cmd
    PutPtr = PutPtr + 1
   
    'Position 5 = DATA
    For I = 0 To DataBytes.Length - 1    'sometimes I miss traditional BASIC's array indexing too...
        PacketBytes(PutPtr) = DataBytes(I)   
        PutPtr = PutPtr + 1
    Next
   
    'Position 6 = post-amble
    PacketBytes(PutPtr) = 0x05
    PutPtr = PutPtr + 1
   
    'Position 7 = BCC
    Dim ControlSum As Int = 0
    For I = 1 To PutPtr - 1    'Position 2 to Position 6
        ControlSum = ControlSum + SignedByteToUnsigned(PacketBytes(I))   
    Next
    'zero operations left in because computer time is cheap and programmer time is not
    PacketBytes(PutPtr + 0) = 0x30 + Bit.And(Bit.ShiftRight(ControlSum, 12), 0x000F)
    PacketBytes(PutPtr + 1) = 0x30 + Bit.And(Bit.ShiftRight(ControlSum,  8), 0x000F)
    PacketBytes(PutPtr + 2) = 0x30 + Bit.And(Bit.ShiftRight(ControlSum,  4), 0x000F)
    PacketBytes(PutPtr + 3) = 0x30 + Bit.And(Bit.ShiftRight(ControlSum,  0), 0x000F)
    PutPtr = PutPtr + 4
   
    'Position 8 = ETX
    PacketBytes(PutPtr) = 0x03
    PutPtr = PutPtr + 1
   
    If PutPtr <> PacketBytes.Length Then
        'well, this is embarrassing
    End If
   
    Return PacketBytes
   
End Sub

Sub Tester

    Dim DataBytes(10) As Byte
    For I = 0 To DataBytes.Length - 1
        DataBytes(I) = 100 + I    'easy-to-see sample data
    Next
   
    Dim X() As Byte = PacketForAndrei(0x44, 0x55, DataBytes)
   
    Dim Temp As String = "Returned " & X.Length & " bytes ="
    For I = 0 To X.Length - 1
        Temp = Temp & " " & X(I)
    Next
    Log(Temp)
   
End Sub
 

emexes

Well-Known Member
Licensed User
Returned 20 bytes = 1 46 68 85 100 101 102 103 104 105 106 107 108 109 5 48 52 62 49 3

I think we could be on the home stretch ;-)
 

emexes

Well-Known Member
Licensed User
BCC definition is a bit vague: "Sum of data bytes from position 2 to position 6."

which had me wondering:

(a) data bytes? as opposed to non-data bytes?? does that include the LEN, SEQ and CMD bytes???

(b) position 1 is unambiguously not included, but... position 6 is of fixed value like position 1, and thus also of no usefulness in BCC, so why include position 6 but not position 1?

If your BCC values aren't matching the manufacturer's examples (what do you mean, they haven't supplied any??? ;-/ ) then those would be the areas to first check.
 

StarinschiAndrei

Active Member
Licensed User
BCC definition is a bit vague: "Sum of data bytes from position 2 to position 6."

which had me wondering:

(a) data bytes? as opposed to non-data bytes?? does that include the LEN, SEQ and CMD bytes???

(b) position 1 is unambiguously not included, but... position 6 is of fixed value like position 1, and thus also of no usefulness in BCC, so why include position 6 but not position 1?

If your BCC values aren't matching the manufacturer's examples (what do you mean, they haven't supplied any??? ;-/ ) then those would be the areas to first check.
Thank you ... i will give you some details regarding the communication (unfortunately i can't upload the entire doc because is confidential). I monitored the serial port (manufacturer app + device under windows) the sent command was : 01 25 F8 45 30 05 30 31 39 37 03 ; 01 25 D1 45 30 05 30 31 37 30 03, etc.
I created an array manually
B4X:
 AStream.Write(Array As Byte(0x01, 0x25, 0xD1, 0x45, 0x30, 0x05, 0x30, 0x31, 0x37, 0x30, 0x03 ))
worked fine. Now i should reproduce the same array programmatically. The array looks like this, now : 01253E4530053030. i thik the first code that you post is much better, the second one i couldn't test it.
 

emexes

Well-Known Member
Licensed User
The second code is better, because by then I had a bigger-picture view of where you were headed. And the reason I jumped on to it is: I have done a ton of serial interfacing in the past with protocols that look eerily similar to what you posted.

When you say you couldn't test it, does that mean it doesn't run on your computer, or that you're away from your workbench, or... ? I ran it here, and it generated a reasonable-looking packet. I cheerfully admit that I could easily be off-by-one somewhere with the length or something, but hey, I'm sure you love a good puzzle to keep you entertained at work, don't want to keep all the fun to myself ;-)

In fact, I've just had a brilliant thought, if the above sample you posted is an actual real correct packet. Will be back soon.
 
Last edited:

emexes

Well-Known Member
Licensed User
First the good news:

** Activity (main) Create, isFirst = true **
Returned 11 bytes = 01 25 F8 45 30 05 30 31 39 37 03
Returned 11 bytes = 01 25 D1 45 30 05 30 31 37 30 03
** Activity (main) Resume **
 

emexes

Well-Known Member
Licensed User
There is no bad news ;-)

The PacketForAndrei routine is unchanged. The test routine did change, (i) to test against the logged packets you gave, and (ii) to dump the results in hex rather than decimal, to make it easier to compare the regenerated packets against the original logs.

B4X:
Sub PacketDumper(X() As Byte)
   
    Dim Temp As String = "Returned " & X.Length & " bytes ="
   
    For I = 0 To X.Length - 1
        Temp = Temp & " " & bc.HexFromBytes(Array As Byte(X(I)))
    Next
    Log(Temp)
   
End Sub

Sub Tester
   
    'using actual recorded packets - let's see if we can reproduce it
    '01 25 F8 45 30 05 30 31 39 37 03 ; 01 25 D1 45 30 05 30 31 37 30 03
   
    'first packet is SEQ = F8, CMD = 45, DATA = 30
   
    Dim DataBytes() As Byte = Array As Byte (0x30)
    Dim X() As Byte = PacketForAndrei(0xF8, 0x45, DataBytes)
    PacketDumper(X)
   
    'second packet is SEQ = D1, CMD = 45, DATA = 30
   
    'same data as previous = no need to create again
    Dim X() As Byte = PacketForAndrei(0xD1, 0x45, DataBytes)
    PacketDumper(X)
   
End Sub
 

StarinschiAndrei

Active Member
Licensed User
There is no bad news ;-)

The PacketForAndrei routine is unchanged. The test routine did change, (i) to test against the logged packets you gave, and (ii) to dump the results in hex rather than decimal, to make it easier to compare the regenerated packets against the original logs.

B4X:
Sub PacketDumper(X() As Byte)
  
    Dim Temp As String = "Returned " & X.Length & " bytes ="
  
    For I = 0 To X.Length - 1
        Temp = Temp & " " & bc.HexFromBytes(Array As Byte(X(I)))
    Next
    Log(Temp)
  
End Sub

Sub Tester
  
    'using actual recorded packets - let's see if we can reproduce it
    '01 25 F8 45 30 05 30 31 39 37 03 ; 01 25 D1 45 30 05 30 31 37 30 03
  
    'first packet is SEQ = F8, CMD = 45, DATA = 30
  
    Dim DataBytes() As Byte = Array As Byte (0x30)
    Dim X() As Byte = PacketForAndrei(0xF8, 0x45, DataBytes)
    PacketDumper(X)
  
    'second packet is SEQ = D1, CMD = 45, DATA = 30
  
    'same data as previous = no need to create again
    Dim X() As Byte = PacketForAndrei(0xD1, 0x45, DataBytes)
    PacketDumper(X)
  
End Sub
THANK YOU ! ! ! YOU ARE AMAZING ! ! ! ... I will test in an hour when i arrive home.
 

emexes

Well-Known Member
Licensed User
No worries, it was kinda fun to do that stuff again. I probably spent a couple of days massaging my first packet send/receive routines into shape the first time I did it, so if I've saved you a bit of that same time and heartache, then we're good.

I was mulling over that you might be about to enter the packet-receive swamp (if you're not already knee-deep with those alligators ;-) and the first thing I'd do is pull out the checksum calculation into its own sub so that you can use it for both generating outgoing checksums and checking incoming checksums, eg:

B4X:
Sub CalculateChecksum(PacketBytes() As Byte, I1 As Int, I2 As Int) As Byte()
 
    Dim ControlSum As Int = 0
    For I = I1 To I2
        ControlSum = ControlSum + SignedByteToUnsigned(PacketBytes(I))
    Next
 
    Dim ChecksumBytes(4) As Byte
    'zero shift right left in because computer time is cheap and programmer time is not
    ChecksumBytes(0) = 0x30 + Bit.And(Bit.ShiftRight(ControlSum, 12), 0x000F)
    ChecksumBytes(1) = 0x30 + Bit.And(Bit.ShiftRight(ControlSum,  8), 0x000F)
    ChecksumBytes(2) = 0x30 + Bit.And(Bit.ShiftRight(ControlSum,  4), 0x000F)
    ChecksumBytes(3) = 0x30 + Bit.And(Bit.ShiftRight(ControlSum,  0), 0x000F)

    Return ChecksumBytes
 
End Sub
and then the packet assembly bit collapses down to:

B4X:
'Position 7 = BCC
Dim ChecksumBytes() As Byte = CalculateChecksum(PacketBytes, 1, PutPtr - 1)    'Position 2 to Position 6
For I = 0 To 3
    PacketBytes(PutPtr) = ChecksumBytes(I)
    PutPtr = PutPtr + 1
Next
and on the disassembly side you'd have something like:

B4X:
'Position 7 = BCC
Dim ChecksumBytes() As Byte = CalculateChecksum(PacketBytes, 1, ChecksumPosition - 1)    'Position 2 to Position 6
Dim ChecksumOkFlag As Boolean = True    'optimism reigns supreme
For I = 0 To 3
    If PacketBytes(ChecksumPosition + I) <> ChecksumBytes(I) Then
        ChecksumOkFlag = False
        Exit
    End If
Next
BTW I did see you were using strings originally, which I can understand as a sin committed in the heat of the coding battle ;-) but the performance hit is like 100-fold or more. If you want to make it out of this low-level swamp alive, best avoid strings like the plague. Use bytes instead, or if the signed-vs-unsigned stuff gets too annoying, use shorts instead (still signed, but able to handle full "proper" 8-bit range 0..255) and convert them at handover to/from the serial stream. The maximum packet size was under 255 bytes, so worst-case is you waste maybe 500 bytes to have your packet assembly/disassembly buffers as easy-to-use shorts instead of pita bytes. Cheap insurance if you ask me, and you probably even reclaim some of that space through smaller/simpler code in that area.

What could possibly go wrong?!?!
 
Last edited:
Top