Binary communication with TCP/UDP binding?

Hi all,

I have a device that implements a binary communications protocol over a serial interface. Due to the lack of serial interfaces (and usb-to-serial converters don’t work reliably for this device) on my host, I have used a serial-to-ethernet device such as the MOXA N5110 with puts the serial messages into udp packets and sends them to my host.

I have set up the TCP/UDP binding to receive these packets and would like to implement the binary communications protocol based on rules processing.
The communications protocol requires me to parse the binary incoming packets to extract some status information (which I will put into corresponding items) and respond with a binary packet that basically translates into a “Confirm_ACK” message to the device so that it continues to send its status messages periodically.

So far I have successfully set up the UDP binding to get an input and pass it as a string item to my rule. As a first step, I would like to convert this string into a set of bytes[] representing the binary input data in order to do bitwise extractions.
To verify the working of this, I would like to display the extracted bytestream.

To explain the setup:

The configuration of the tcp/udp binding in openhab.cfg:

udp:port=4115
udp:buffersize=110
udp:itemsharedconnections=true
udp:bindingsharedconnections=true
udp:directionssharedconnections=false
udp:addressmask=false
udp:preamble=
udp:postamble=/r/n
udp:blocking=false
udp:updatewithresponse=false
udp:refreshinterval=250
udp:charset=ISO-8859-1

Item definition:

String C400_InputString “C400 Input String [%s]” {udp=„<[device_ip:source_port:’REGEX((.*))’]"}
String C400_decString „decoded Input String [%s]“

rule:
`import org.openhab.core.library.types.*

rule “parse_C400_InputString”
when
Item C400_InputString received update
then
if (C400_InputString.state instanceof StringType) {
var value = C400_InputString.state as StringType
var valueLength = value.toString.length() as Integer
val byte[] bytes = value.toString.getBytes(„ISO-8859-1“)
postUpdate(C400_decString, bytes.toString())
}
end

I was hoping that the bytes[] array contains the ASCII codes of the individual characters in the string, and wanted to output the ascii codes concatenated as a string for debugging purposes.

However:

I know that my device sends a binary stream of the following hex data:

„68 02 02 68 40 02 42 16“

What i get in the decString is:
„B@„ and then seemingly random data that varies from each input to the next, even tough the input string is always the same.
So clearly, the input parsing doesn’t work as planned. This could be due to the used charset, which I tried to put down as ISO-8859-1, so that I cover the full 8 bit per character. Or my rule ist simply wrong.

Any ideas? Or is the udp binding simply not capable of handling binary data?

Kind regards,

Thomas

Your problem isn’t with the binding I suspect but with the conversion to bytes and back to String.

If you want the Hex String sent to C400_decString you will want to do something like this:

val StringBuilder sb = new StringBuilder
bytes.forEach[b | sb.append(Integer::toHexString(b) + " ")]
postUpdate(C400_decString, sb.toString)

you’re right, the conversion was causing a problem. Most notably, it seems that my assumption that calling the toString() method on a byte[] array would simply convert everything back to a string was wrong. Instead this apparently returns the memory address of the byte array which is why I got “[B@” and the a different memory address on each call.

The method you suggested works, but only as long as the input bytes are less than 127 (7f). If they go higher, the initial getBytes method to put the string into a byte array is dependant on the charset. In my case with the “ISO-8859-1” I still seem to get UTF-8 multi-byte characters. A value on the input data stream of “ff” ends up being displayed as “ffffffff”.

Any Idea of how to work around this issue?

Cheers,

Thomas

I’ll preface that dealing with binary data in Java is a PAIN. I go to great lengths to avoid it when I can. And since the Rules DSL doesn’t offer us anything above and beyond what Java does we are stuck. Also, since the TCP/UDP Binding only handles Strings we are stuck trying to convert our way out of this mess.

Do you know what encoding, if any, the TCP Binding does with the data it reads off the socket? Maybe if you use the same one it will spit out the right values. Similarly maybe if you go with the flow and specify the encoding as UTF8 it might work better.

This site has an example of pulling bytes out of a String. Maybe it can help.

Beyond that I don’t know. I’ve not dealt with this sort of stuff in years.

Oh dear, it seems I’ve got my work cut out for me.

I guess i could look at the source of the comfoair binding. I know the comfoair send a binary data stream as well and the comfoair binding has successfully dealt with this, abeit using a direct serial interface rather than the tcp/udp binding.

I also remember seeing some code dealing with UTF-8 input when I was looking around. Maybe I can find it again.

Thanks for your help!

I tried a different approach to try and get rid of the encoding issues, changing the part of the code that put the string into a byte array using the get.Bytes() method to a char array with the encondig agnostic to.CharArray() method:

var char [] array = value.toString.toCharArray()
val StringBuilder sb = new StringBuilder
array.foreach (b | sb.append(Integer::toHexString(b) + " "))

This seems to works better. I will need to do more tests whether this represents the binary data correctly. For now I am hopeful :).

1 Like