I have an Athena 16C controller that I am controlling via RS232. Its messaging protocol requires a proprietary checksum:
"CHKSUM: This is a two character Message Code Numbering System, representing the sum of all the ASCII values of all the characters (excluding the START, CHAR, the END CHAR, and the CHKSM themselves) in the message. The sum is computed using the following formula: CHKSM = SUM(All Message Characters)%256 where % represents the modulus operator."
An example message (from their documentation) is this:
$Ø1Ø1RØ5C1<CR>
and can be broken down as:
$ [START CHAR] 01 [ID] 01 [ZONE] R [TYPE] 05 [PARAM] C1 [CHKSM] <CR> [END CHAR]
I have sent this message to the controller and it works as expected.
I am writing my code in JS and have the following that is supposed to calculate the CHKSM to put at the end of the message:
var sum = 'Ø1Ø1RØ5'
.split('')
.map(function(char) {
return char.charCodeAt(0);
})
.reduce(function(current, previous) {
return previous + current;
});
var chksm = (sum % 256);
console.log(chksm.toString(16));
The checksum should be 'C1' according to the message format. But the calculated sum is 377 which results in a checksum of 121 which equals 79 in hex.
// 0 = 48, 1 = 49, R = 82, 5 = 53 (ASCII values)
// 48 + 49 + 48 + 49 + 82 + 48 + 53 = 377
// 377 % 256 = 121 (decimal) = 79 (hex)
An engineer from Athena sent me the following VB code but I cannot understand the logic, nor the syntax particularly. Is there something basic I am missing with this problem in general?
' Covert the mod % 256 checksum to the 2 chars:
' Will set First and Second chars for encoded value. Pass in the value (Checksum mod 256)
' and where to return the 1st and 2nd chars to.
Public Sub EncodeIt(ByVal Value As Integer, ByRef FirstChar As Integer, ByRef SecondChar As Integer)
If Value > 359 Then 'Z9 = 359, absolute max possible
Value = 359
End If
'Note: backslash '\' means integer divide, not floating point!!
If Value > 99 Then
FirstChar = (Value \ 10) + 65 - 10 '65 = ascii "A"
Else
FirstChar = (Value \ 10) + 48 '48 = ascii "0"
End If
SecondChar = (Value Mod 10) + 48
End Sub
' Convert the two chars received in a message back to normal integer.
' Take the 2 chars and return a decoded integer value
Public Function DecodeIt(ByVal FirstChar As Integer, ByVal SecondChar As Integer) As Integer
'65 = ascii "A", 48 = ascii "0"
If FirstChar > 57 Then '57 = ascii "9"
Return ((FirstChar - 65 + 10) * 10) + (SecondChar - 48)
Else
Return ((FirstChar - 48) * 10) + (SecondChar - 48)
End If
End Function
The encoding from decimal to string is cutom made and not base16. This is why (121).toString(16)
is not equal to C1
.
From the VBA of your post the encoding/decoding functions should be:
function compute_checksum(message) {
var sum = 0;
for(var i=0; i<message.length; i++)
sum += message.charCodeAt(i);
return sum % 256;
}
function encode_checksum(value) {
value = Math.min(value, 359);
var c1 = ((value / 10) | 0) + (value > 99 ? 55 : 48);
var c2 = (value % 10) + 48;
return String.fromCharCode(c1, c2);
}
function decode_checksum(text) {
var c1 = text.charCodeAt(0);
var c2 = text.charCodeAt(1);
return (c1 > 57 ? c1 - 55 : c1 - 48) * 10 + (c2 - 48)
}
Here is a usage example:
var checksum = compute_checksum('0101R05');
console.log('checksum: ' + checksum);
var CHKSM = encode_checksum(checksum);
console.log('encoded checksum: ' + CHKSM);
console.log('decoded checksum: ' + decode_checksum(CHKSM));