Home AMX User Forum AMX General Discussion

adding hex bytes to calc checksum

What is the easiest way to add a group of bytes together. The NEC projectors want you to add the command bytes together then append the lower byte of the total to the command. I did this long ago and forgot how. Any help or code sample would be great.

Comments

  • a_riot42a_riot42 Posts: 1,624
    samos wrote: »
    What is the easiest way to add a group of bytes together. The NEC projectors want you to add the command bytes together then append the lower byte of the total to the command. I did this long ago and forgot how. Any help or code sample would be great.

    I use the addition operator (+). Is there an easier way I don't know about?
    Paul
  • The "+" is used to add the bytes together:
    INTEGER nSum = byte1 + byte2 + byte3 etc.
    Next step would be to get the lower 8 bit of the sum
    CHAR cChk = TYPE_CAST(nSum % 256) // get checksum byte
    TYPE_CAST is required to prevent a compiler warning because an INTEGER is tried to write in a CHAR.
  • samossamos Posts: 106
    thanks

    Thanks Marc Scheibein I had everything right but the TYPE_CAST()
  • this is what I use:


    define_function char fChecksum(char cCommand[])
    {
    stack_var char nChkSum
    stack_var integer i
    for(i=1;i<=length_array(cCommand);i++)
    {
    nChkSum = nChkSum + cCommand
    if (nChkSum > 255) // if the checksum goes over 1 byte subtract by 255
    {
    nChkSum = nChkSum - 255 // don't know if I need this but it works
    }
    }
    return nChkSum
    }


    so I can do something like:

    send_string dvproj,"cProjCmd,fChecksum(cProjCmd)"
  • vegastechvegastech Posts: 369
    Can I ask a question on the checksum functions?

    When I want to send a command that needs a checksum calculated, what is the proper syntax? I was trying it like this:
    SEND_STRING dvPROJ, "cProjOn,fcnCheckSum(cProjOn)"
    
    But when trying to debug, I don't see the stack_var i, to indicate that the for loop ever ran. Is it simply running too fast for me to see, since the stack_var resets itself to 0 when it's done? I found it to be odd that when I dragged the stack_vars into the debug window, the value and display types never populated. Can I not debug these?

    I defined a constant above as:
    CHAR cProjOn[] = {"$02,$00,$00,$00,$00"}
    [code]
    here is my checksum function:
    [code]
    DEFINE_FUNCTION INTEGER fcnCheckSum(char sStringValue[])
    {
    	stack_var integer i
    	stack_var char ckSum
    	for(i=1;i<=length_string(sStringValue);i++)
    	{
    		ckSum = ckSum + sStringValue[i]
    	}
    	return ckSum
    }
    
    In the hope that I could simply refer to the constant when doing the SEND_STRING. I understand that the function is returning an integer, which is what I want, since the idea here is to simply add the hex values together to come up with the checksum. What am I doing wrong?
  • viningvining Posts: 4,368
    You won't see a stack var in debug unless you're stepping through the code using break points so for temp testing just change it to a local. Anytime your testing code include send_string 0's in as many places as you like to help you determine where you code is hitting or missing and include any value snapshots you might want to see at those points.

    You also should change your return data type of the function from INTEGER to CHAR and like the previous post or Marc's post do something in case your checksum total is over 1 byte.
  • TurnipTruckTurnipTruck Posts: 1,485
    Getting the lower eight bits of the sum can be accomplished by band'ing against FF. No type cast required.
  • vegastechvegastech Posts: 369
    Ok, I made the change from Integer to Char as recommended, and I also changed the stacks to locals for tracking. (I knew it, just didn't think of it!) When the function runs now, my integer i finishes at 20, instead of the actual number, which should be no longer than 5.
    DEFINE_FUNCTION CHAR fcnCheckSum(char sStringValue[])
    {
    	local_var integer i  //change back to stack
    	local_var char ckSum  //change back to stack
    	for(i=1;i<=length_string(sStringValue);i++)
    	{
    		ckSum = ckSum + sStringValue[i]
    	}
    	return ckSum
    }
    
    Correct me if I'm wrong here: When I define a function and I declare a parameter, that parameter ends up being (kind of) like a local variable, yes? Therefore, when I run that function in a button_event like so:
    SEND_STRING dvPROJ, "cProjOn,fcnCheckSum(cProjOn)"
    
    Shouldn't that parameter base itself on the constant that is used as its parameter? In this case, cProjOn, which is defined as:
    DEFINE_CONSTANT
    CHAR cProjOn[] = {"$02,$00,$00,$00,$00"} 
    
    Or am I defining my constant incorrectly?
  • PhreaKPhreaK Posts: 966
    When you changed those stack_var's to locals it important to remember that local_var's will hold their value when the code exits scope.

    i gets set to 1 every time in your for loop initialization, however ckSum will already be holding its previous value every time the function is entered. If you're going to keep ckSum as a local so that you can monitor it you will need to reset it back to 0 before you start using it:
    DEFINE_FUNCTION CHAR fcnCheckSum(char sStringValue[])
    {
    	local_var integer i  //change back to stack
    	local_var char ckSum  //change back to stack
            ckSum = 0    //reset ckSum - remove when changed back to stack
    	for(i=1;i<=length_string(sStringValue);i++)
    	{
    		ckSum = ckSum + sStringValue[i]
    	}
    	return ckSum
    }
    

    As TurnipTruck said it might be nice to use a binary and to just get the lower 8 bits:
    DEFINE_FUNCTION CHAR fcnCheckSum(char sStringValue[])
    {
    	local_var integer i  //change back to stack
    	local_var integer ckSum  //change back to stack
            ckSum = 0    //reset ckSum - remove when changed back to stack
    	for(i=1; i<=length_string(sStringValue) ;i++)
    	{
    		ckSum = ckSum + sStringValue[i]
    	}
    	return type_cast(ckSum & $FF)
    }
    
    note: ckSum has been changed to an integer to prevent any wrap around issues.
  • Joe HebertJoe Hebert Posts: 2,159
    vegastech wrote:
    When the function runs now, my integer i finishes at 20, instead of the actual number, which should be no longer than 5.
    ...
    DEFINE_CONSTANT
    CHAR cProjOn[] = {"$02,$00,$00,$00,$00"}
    

    Or am I defining my constant incorrectly?

    Yes, it’s incorrect. You’ve created a 19 character array which means i will equal 20 when the FOR loop ends.

    You’ll want to lose the double quotes and do this instead:
    DEFINE_CONSTANT
    CHAR cProjOn[] = {$02,$00,$00,$00,$00}
    

    Now you’ve got a 5 character array.

    The rest of your code looks fine but like PhreaK said you’ll need to do a reset of your local_var (ckSum=0) every time you enter the function.

    Personally I would just leave ckSum as a CHAR (as you have it) and just RETURN it as is when it exits the FOR loop.

    Have fun.
  • vegastechvegastech Posts: 369
    Stuck again...

    Thanks everyone for your posts. I've got just about everything together to make this work, but there is one more piece I'm unsure of - the protocol guide states that when the projector sends a reply, it also calculates its own checksum, which is different than the checksum I am sending. Awesome. It is calculated the same way, but with different starting hex bytes. I am trying to use the same checksum function as before, since it calcs the same, but I'm getting stuck on a few parts.
    In my data event, I am looking for the response string(s) from the projector:
    STRING:
    {
                    LOCAL_VAR CHAR locatedCkSum [20]                  
                    LOCAL_VAR CHAR sProjInput[20]  //copy data into string variable
                    LOCAL_VAR CHAR garbage [20]
    		local_var INTEGER startgarbage
    		sProjInput = DATA.TEXT
    SELECT
    		{
    			ACTIVE (FIND_STRING(sProjInput,"$22",1)):  //If an input response is sent
    			{
                            locatedCkSum = RIGHT_STRING(sProjInput,1)  //copies last value
    			startgarbage = length_string(sProjInput)
    			garbage = REMOVE_STRING(sProjInput,locatedCkSum,(length_string(startgarbage)-1))  //trashes last value, leaving cmd
    			
    			fcnCheckSum(sProjInput)
    			sProjInput = ''
    			startgarbage = 0
    
    I can copy the last bit from the return string, so that I can calculate that against what the checksum SHOULD be, by using Right_String. Since Right_String is not destructive, I needed a way to remove the checksum hex from the rest of the response, so I was trying something with the garbage = line, but it isn't working at all. As I try to debug it, nothing happens to the garbage variable. My thought was to use remove_string, looking for the value inserted into locatedCkSum previously, and starting at the end of the string, using a length_string minus 1 calculation. Can someone shed some light on this for me? Thanks.

    Update - I've been able to get the checksum function running in both the send_string and data_event portions of my code, so Happy Day! I do have a preference question on the data event, though. What would be my best option for updating my panel's button and variable text feedback, now that I have checksum values to compare? Something like this?
    SELECT
    		{
    			ACTIVE (FIND_STRING(sProjInput,"$22",1)):  //If a input response sent
    			{
    				locatedCkSum = RIGHT_STRING(sProjInput,1)  //copies last hex value
    				garbage = REMOVE_STRING(sProjInput,locatedCkSum,LENGTH_STRING(sProjInput))  //trashes last value, leaving cmd in sProjInput
    				fcnCheckSum(sProjInput)
    				
    			
    				IF (ckSum == locatedCkSum)
    				{
    					SELECT
    					{
    						ACTIVE (FIND_STRING(sProjInput,cProjHDMI,1)): //HDMI string sent
    						{
    							SEND_COMMAND dvTPproj, "'^TXT-15,0,HDMI'"
    							nProjinput = 1
    							nCurrentSource = 3  //will provide fb to sat source button
    						}
    						ACTIVE (FIND_STRING(sProjInput,cProjSvid,1)):  //SVideo string sent
    						{
    							SEND_COMMAND dvTPproj, "'^TXT-15,0,S-VIDEO'"
    							nProjinput = 2
    							nCurrentSource = 2  //will provide fb to camera source button
    						}
    						ACTIVE (FIND_STRING(sProjInput,cProjVid,1)): //video input feedback
    						{
    							SEND_COMMAND dvTPproj, "'^TXT-15,0,VIDEO'"
    							nProjinput = 3
    							nCurrentSource = 0  //deselect the input button on TP
    						}
    						ACTIVE (FIND_STRING(sProjInput,cProjComp,1)): //component input feedback
    						{
    							SEND_COMMAND dvTPproj, "'^TXT-15,0,COMPONENT'"
    							nProjinput = 4
    							nCurrentSource = 1  //will provide fb to dvd source button
    						}
    					}
    				}
    			}
    
    My IF statement will not compile because ckSum is a return value from the function call, and the compiler says it is not defined otherwise. Is the return value from a function like a local variable, where it can only be referenced inside that event? I tried getting around it by creating another global CHAR to copy the return value into, but that doesn't work. Is there another way to compare the two checksum values?
  • Same Problem

    I am hung up on the same issue. I've tried moving things around with no luck. Any hints?
  • My code

    STRING:
    {
    STACK_VAR CHAR cProjData [100]
    STACK_VAR CHAR cReplyCks [1]

    SELECT
    {

    ACTIVE(FIND_STRING(sProjBuf,"$22,$00,$00,$D0,$00",1)):
    {
    cProjData=calcChkSum(LEFT_STRING(sProjBuf, LENGTH_STRING(sProjBuf-1)))
    //set_length_string (sCmd, length_string (sCmd) - 1)
    //(LENGTH_STRING(STRING) > 0)
    cReplyCks=(RIGHT_STRING(sProjBuf,1)
    IF(cProjData == cReplyCks)
    {
    ON[nProjPwr]
    }
    }
    }
    }

    }
    BUTTON_EVENT[dvTP_Proj,255]
    {
    PUSH:
    {
    IF(!nProjPwr)
    {
    SEND_STRING dvProjector, "POWER_ON,calcChkSum(POWER_ON)"
    }
    ELSE
    {
    SEND_STRING dvProjector, "POWER_OFF,calcChkSum(POWER_OFF)"
    }
    }
    }
  • DHawthorneDHawthorne Posts: 4,584
    I'm lazy ... I entirely ignore incoming checksums. Most modern protocols are complex enough that it is easy enough to tell if the response itself is malformed and just throw it away. Back in the day when you had just a few bytes to work with, and they were entirely context dependent, it was important to check them against the checksum to make sure they were correct; serial chips have also advanced a lot so that errors are far, far less common. But when most protocols are sending you an ASCII string, with internal references to what it's about, I find checksums to be unnecessary.
  • DHawthorne wrote: »
    I'm lazy ... I entirely ignore incoming checksums. Most modern protocols are complex enough that it is easy enough to tell if the response itself is malformed and just throw it away. Back in the day when you had just a few bytes to work with, and they were entirely context dependent, it was important to check them against the checksum to make sure they were correct; serial chips have also advanced a lot so that errors are far, far less common. But when most protocols are sending you an ASCII string, with internal references to what it's about, I find checksums to be unnecessary.

    I agree entirely. About the only time I consider a checksum to be necessary is when you are using a less-than-perfect communications path. Examples would be PLC lighting controls, radio modems, etc.

    There is no reason why a device five feet from a controller with a hardwired connection would require a checksum.
  • a_riot42a_riot42 Posts: 1,624
    I agree entirely. About the only time I consider a checksum to be necessary is when you are using a less-than-perfect communications path. Examples would be PLC lighting controls, radio modems, etc.

    There is no reason why a device five feet from a controller with a hardwired connection would require a checksum.

    Why wouldn't you just always implement it so that it doesn't matter how good of a connection you have or any other issue at hand? At least then you know it will always work regardless of the installation.
    Paul
  • a_riot42 wrote: »
    Why wouldn't you just always implement it

    Simple answer... extra work.
  • PhreaKPhreaK Posts: 966
    Simple answer... extra work.
    Not when it saves a future site visit to diagnose a problem that your code could have emailed you about.
  • Generally, checksums are part of the third-party equipment that we are controlling. We don't have much say as to whether or not they are used.

    The only time I could think of that we would decide whether or not to implement a checksum would be in M2M communication. In that case, the AMX system handles message delivery accuarcy.
Sign In or Register to comment.