How to convert an ASCII "Hex" string to an integer

I've been playing with this for about 4 days.

I have a string: from logs echo = 313538

where 313538 ASCII numbers:
0x31 = 1
0x35 = 5
0x38 = 8

I'm at a complete loss on how to make this conversion to an integer.

My next approach is to try and separate each hex, convert to string, concatenate then convert to integer.

My current code does not show all the variations I've tried but here it is:


metadata {
    definition (name: "Parsing UART test", namespace: "johnrob", author: "johnrob") {
        capability "Actuator"       // our device has commands...
        capability "Sensor"         // our device has attributes...
        
        command "measRequest"
    }

    preferences {
    }
}

// ===================================================================================================
def parse(String description) {
    def logEnable = true
    if (logEnable) log.debug " Parse description=  {$description}"

    Map map = [:]

    def event = zigbee.getEvent(description)
    if (event) {
        if (txtEnable) log.debug " parsed zigbee event = '${event}"
        sendEvent(event)
    }

    if (description?.startsWith("read attr -")) {
        def descMap = zigbee.parseDescriptionAsMap(description)
        if (logEnable) log.debug " Desc Map (1): $descMap"
        if (descMap.clusterInt == 20) {

            def echo = descMap.value        // this extraction seems to remove the leading "byte count"
            log.info " echo = $echo"     //  <---- OK to here

            if (echo.startsWith("21")) {
                log.info " sensed starts with 21"
                echo = echo.replace("21","").trim()  //or echo = echo.replace("21","")
                log.info " echo 38 = $echo"   // <---- OK to here
                
                    if(echo instanceof String) { log.info "is string"}
                echostrval = Long.parseLong(echo, 16);     // results in a number higher that 313538 by almost 2x 

                //log.info "is string"              int echoValue = Integer.parseInt(echo,16)
                log.info " echostrVal = $echostrval"
                
                int echoint = echo as Integer
                
                log.info "int echoInt = $echo"
                //log.debug " Integer.parseInt(echo) = $echoInt"

            }
        }
    }        
}   // --- parse ---
// ===================================================================================================


def measRequest() {
    delayBetween([
        "he cmd 0x${device.deviceNetworkId} 2 6 1 {}",
        "he cmd 0x${device.deviceNetworkId} 2 6 0 {}"
    ], 100)
}

def on() {
"he cmd 0x${device.deviceNetworkId} 2 6 1 {}"
}

def off() {
"he cmd 0x${device.deviceNetworkId} 2 6 0 {}"
}

Are the ASCII characters in the response always decimal integers?

Try something like this:

def demo()
{
    log.debug asciiToInt("313538")
    log.debug asciiToInt("313538313538")
}

def asciiToInt(String val)
{
    log.debug "val = ${val}"
    
    def sum = 0
    
    def bytes = hubitat.helper.HexUtils.hexStringToByteArray(val)
    log.debug "bytes = ${bytes}"
    
    bytes?.eachWithIndex
    { it, idx ->
        sum += ((it - 0x30) * (Math.pow(10, (bytes.size() - 1) - idx)))
    }
    
    log.debug "sum = ${sum}"
    
    return sum.toInteger()
}

There may be a cleaner way to do this but I got it to work by converting to a Int List > Hex list > then a char list.

	def ascii = 313538
	def intList = hubitat.helper.HexUtils.hexStringToIntArray(ascii as String)
	log.debug "intList ${intList}"

	def hexList = []
	intList.each {
		hexList.add(Integer.toHexString(it).padLeft(2, "0").toUpperCase())
	}
	log.debug "hexList ${hexList}"

	def charList = []
	hexList.each {
		charList.add((char)Integer.parseInt(it as String, 16))
	}
	log.debug "charList ${charList}"

Output:

charList [1, 5, 8]
hexList [31, 35, 38]
intList [49, 53, 56]

Hmm.. just realized the hex list is the same as the original just in a list. So could probably just break the first thing up directly into a list of strings to make it more efficient.

I think your ASCII values are meant to represent a single integer, correct @JohnRob?

For example - the string "313538" should convert to a single integer value of 158. If so, my code above works as long as the resulting value doesn't overflow the Integer max value (2147483647).

image

1 Like

Wow, you guys are great.

Yes the goal is an integer = 158 It will be limited to 255 by the sending end.

I will use both, if only to understand the manipulations. However they seem straight forward when reading them.

Thanks
John

No problem.

In case it helps, here's what I would do based on your last couple of posts on this project, @JohnRob .

def parse(String message)
{
    byte[] bytes = hubitat.helper.HexUtils.hexStringToByteArray(message)
    if(null == bytes) { return }

    def respVal
    
    switch(bytes[0])
    {
        case 0x21:
        case 0x22:
        case 0x23:
            log.debug "integer message type"
            respVal = parseInt(bytes.getAt(1..<bytes.size()) as byte[])
            break
        
        //case 0x24:
        //    do something for other message types
        //    break
        
        default:
            log.error "unrecognized message type"
            return
    }
    
    log.debug "parsed message with respVal = ${respVal}"    
}

Integer parseInt(byte[] bytes)
{
    def sum = 0
    
    bytes?.eachWithIndex
    { it, idx ->
        sum += ((it - 0x30) * (Math.pow(10, (bytes.size() - 1) - idx)))
    }
    
    return sum.toInteger()
}

Ah ok, you are trying to get a single integer back with the values combined. I was thinking it was 3 separate integers so I went with the list. The solution from @tomw looks more efficient than mine for that purpose.

1 Like

I have your suggestion from post 2 running successfully.
I'll have to look more closely at this code.

Thanks
John

Yes Sorry, I should have written that in the initial post.

What I really think I need to do is to change how I send the data from the ultrasonic sensor. Its all under my control.

Hardware is:

Garden Variety ultrasonic sensor board --> Arduino --> CC2530 Zigbee board --> Hubitat hub