Twos Complement Calculation

I am trying to send a message to my security system. It uses an RS232 protocol message format, although I am sending it over ethernet. When sending a message, I need to include a checksum at the end of the message. Here is what the documentation says about the checksum:

4.1.6 Checksum
2 ASCII characters, 2-digit checksum. This is the hexadecimal two’s complement of the modulo-256 sum of the ASCII values of all characters in the message excluding the checksum itself and the CR-LF terminator at the end of the message. Permissible characters are ASCII 0-9 and upper case A-F. When all the characters are added to the Checksum, the value should equal 0.

Yikes. I have no idea what to do with that. I have the entire message as a string (not including the checksum, of course). I could feed it into a function to calculate the checksum, but I have no idea of what to put in that function.

They did also provide some sample c code, but it didn’t help me very much:

Is it a fixed message, or is there some variable component you want to include?

It will be a different message each time.

I figured it out. It most certainly ain’t pretty. There’s probably a one-liner that could do this, but I couldn’t find it. At the very least, there are probably ways to make this MUCH easier.

Anyway, here’s how I did it. This code works in a rule.

var String messageCommand="2Edm1100000Message Here^00"

var i = 0
var thisCharVal = 0
var stringTotalVal = 0

// total up the ASCII values of everything in the command. I have a transformation
// map for all of the "regular" characters
while (i < messageCommand.length) {
  thisCharVal = Integer::parseInt(transform("MAP", "", messageCommand.mid(i,1) ))
  stringTotalVal = stringTotalVal + thisCharVal
  i = i + 1

// calculate the MOD 256. We will have a number between 0 and 255 inclusive
stringTotalVal = stringTotalVal % 256

// convert to binary, 8 bits. for example, 0100 1101
var String bin_code = String.format("%08d", Integer::parseInt(Long.toBinaryString(stringTotalVal)))

// get the ones complement. Swap all the 0's to 1's and vice versa
bin_code = bin_code.replace("0", "X")
bin_code = bin_code.replace("1", "0")
bin_code = bin_code.replace("X", "1")

// convert the ones complement back to an integer
stringTotalVal = Integer.parseInt(bin_code, 2)

// add one
stringTotalVal = stringTotalVal + 1

// convert to hex
var String hex_code = String.format("%02X", stringTotalVal)
logInfo ("test", "Twos complement in Hex Code: " + hex_code)

Twos complement is just making it a negative number on just about every CPU, including ARM and Intel architecture. So stringTotalVal = (-stringTotalVal) % 256 should work without resorting to a string representation of the binary digits. so should (-stringTotalVal) & 0xFF.

Also without testing I can’t say for sure, but if, when you convert the binary back to integer, you end up with all ones, so FF in Hex, when you add 1 the result will be 100 Hex. What I’m uncertain about is what the %02X format would do with that, but it might cause 1 out 256 messages to fail. So in your version I think you should take the 256 remainder again after the +1.

1 Like

That was brilliant, and works perfectly! One step and it is done. Thanks :slight_smile: