help with CRC check please

Greetings,

I am trying to reverse engineer an old dotmatrix LED display which accepts a serial input.
I have an arduino UNO sending test strings via the serial interface but currently I can only send known test commands because the CRC is already calculated and known. As soon as i try to implement variable text strings the CRC fails and the display rejects the command.

my issue is that i don't understand how the CRC calculation is done. please help me figure out how to send user defined strings to the device. Code examples below:

thanks in advance!

This basic code works but i cannot change any strings:

void setup()                          
{
  Serial.begin(1200,SERIAL_7E2);      // set up Serial 9600 bps, config 7E2
}

void loop()                           // run over and over again
{
ALL_LED_OFF();
delay(1000); 
ALL_LED_ON();
delay(1000); 
ALL_LED_OFF();
delay(1000); 
ALL_LED_ON();
delay(2000); 
Test_scroll();
delay(5000); 


}

void ALL_LED_OFF()
{
                       //all LEDS OFF
  Serial.write(0x76); //  v
  Serial.write(0x09); //  TAB
  Serial.write(0x50); //  P
  Serial.write(0x49); //  I
  Serial.write(0x44); //  D
  Serial.write(0x38); //  8 
  Serial.write(0x0d); //  CR
  Serial.write(0xe8); //  CRC value (known value not calculated)
 }

void ALL_LED_ON()
{
                         //all LEDS ON
  Serial.write(0x76); //  v
  Serial.write(0x09); //  TAB
  Serial.write(0x50); //  P
  Serial.write(0x49); //  I
  Serial.write(0x44); //  D
  Serial.write(0x37); //  7
  Serial.write(0x0d); //  CR 
  Serial.write(0xe7); //  CRC value (known value not calculated) 
}

void Test_scroll()
{
  Serial.write(0x76);   //  v
  Serial.write(0x18);   //  CAN
  Serial.write(0x06);   //  ACK
  Serial.write(0x35);   //  5
  Serial.write(0x54);   //  T
  Serial.write(0x65);   //  e
  Serial.write(0x73);   //  s 
  Serial.write(0x74);   //  t
  Serial.write(0x20);   //  SPACE
  Serial.write(0x6c);   //  l
  Serial.write(0x6f);   //  o
  Serial.write(0x6f);   //  o
  Serial.write(0x6b);   //  k
  Serial.write(0x73);   //  s
  Serial.write(0x20);   //  SPACE
  Serial.write(0x67);   //  g
  Serial.write(0x6f);   //  o
  Serial.write(0x6f);   //  o
  Serial.write(0x64);   //  d
  Serial.write(0x21);   //  !
  Serial.write(0x0d);   //  CR
  Serial.write(0xcf);   //  CRC value (known value not calculated)
}

I have some code from the LED display manufacturer showing the CRC calculation but i cannot get it to work with the arduino.

Here is the code straight from the manufacturer (not for arduino) to calculate the parity/CRC:

void WB_Parity_Calc (ubyte *str)
{

byte Par;                                                               
Par = 0x00;                                                                       
Par = Par ^ *str;                                 /* XOR aller Zeichen bis CR */
str++;

while (*(str-1) != 13);    /* bis CR */ 

                Par = Par | 0x80;                 // MSB setzen da parity bei 7 Bit Ascii nicht übertragen wird wegen Berechnung bei return wird das Parity bit dann negiert und ist so auf null 
                                                  // MSB set because parity with 7 bit ASCII is not transmitted due to calculation with return the parity bit is then negated and is therefore zero 
                return ~Par;      
                }
}

Here is my failed attempt to convert and add everything together and make it work for any user defined string:

char Test_Str[] = "TEST1";
char Add_Str[6];
char parity;



//////////////////////////////////////// 
void setup() {
Serial.begin(1200,SERIAL_7E2);       // 1200 baud, 7E2 config,  

}

//////////////////////////////////////// 
void loop() {
  
  Serial.write(Test_Str);                           //  send test string plus parity. 
  Serial.write(Parity_Calculate(Test_Str));
  
  // or 
  //Serial.write(Test_Str + Parity_Calculate(Test_Str));
  
  // or 
  //Add_Str = Test_Str + parity;
  //Serial.write(Add_Str);
  
}

//////////////////////////////////////// 
byte Parity_Calculate (byte *str)
{
  byte Par;                                                               
  Par = 0x00;                                                                       
  Par = Par ^ *str;                   /* XOR aller Zeichen bis CR */
  str++;
  
  while (*(str-1) != 13);             
    Par = Par | 0x80;                                                   
    return ~Par;                 
}

Are you certain you transcribed this correctly?:

void WB_Parity_Calc (ubyte *str)
{

byte Par;                                                               
Par = 0x00;                                                                       
Par = Par ^ *str;                                 /* XOR aller Zeichen bis CR */
str++;

while (*(str-1) != 13);    /* bis CR */

                Par = Par | 0x80;                 // MSB setzen da parity bei 7 Bit Ascii nicht übertragen wird wegen Berechnung bei return wird das Parity bit dann negiert und ist so auf null
                                                  // MSB set because parity with 7 bit ASCII is not transmitted due to calculation with return the parity bit is then negated and is therefore zero
                return ~Par;     
                }
}

This line:

while (*(str-1) != 13);    /* bis CR */

seems to be an infinite loop unless I'm missing something.

Hi,

Definitely copied that example code exactly.

I have attached a screenshot of the source for reference.

Unfortunately deciphering it is a bit beyond my programming experience so I am currently trying to understand how the *str pointer works in this code so i have made many variations trying to get it to work on Arduino.

That isn’t a CRC, it is a simple XOR of all the characters up to , then clears the upper bit of the result, which somehow reflects the parity. But where does the parity come from?

As posted the code can’t possibly work because of the problem Blackfin noted above, and a void function can’t return a parameter.

You could try something like this, but the comment about 7 bit ASCII makes it unclear exactly what the function (or the data protocol) expects. Do you have any examples of valid command strings?

char WB_Parity_Calc (char *str)
{

char Par = 0x00;

do {
Par = Par ^ *str;          /* XOR aller Zeichen bis CR */
str++;
} while (*(str-1) != 13);      /* bis CR */

Par = Par | 0x80;  
// MSB set because parity with 7 bit ASCII is not transmitted due to calculation with return 
// the parity bit is then negated and is therefore zero
return ~Par;     
}

Edit: I just noticed another problem. This test INCLUDES the in the calculation, in contradiction to the comment.while (*(str-1) != 13);
To ignore the , remove the “-1”. Oh, and make sure that your input strings are terminated with .

Please post a link to where you got this code, or ask the manufacturer for the correct code.

dannysprogis:
Hi,

Definitely copied that example code exactly.

Actually, you missed a do/while loop. Look again.

I experimented a bit and was able to get the correct "CRC" for the strings in your tested code:

unsigned char WB_Parity_Calc( char *str )
{
    unsigned char Par;

    Par = 0x00;
    do
    {
        Par = Par ^ *str;                                 /* XOR aller Zeichen bis CR */                        
        str++;
        
    }while( *(str-1) != 0x0d );
    
    //Par = Par | 0x80;                                                  
    Par = Par & 0x7F;                                                  
    return ~Par;         

}//

Do/while included and, instead of ORing Par with 0x80 before complimenting and returning, I AND it with 0x7F to clear that bit.

It seems to work. Give it a shot with your code.

Indeed, must be included in the calculation.
The following calculates E8 from the string below, which is supposedly the correct checksum.

char test[]={0x76,0x09,0x50,0x49,0x44,0x38,0x0d};
/*
Serial.write(0x76); //  v
  Serial.write(0x09); //  TAB
  Serial.write(0x50); //  P
  Serial.write(0x49); //  I
  Serial.write(0x44); //  D
  Serial.write(0x38); //  8
  Serial.write(0x0d); //  CR
  Serial.write(0xe8); //  CRC value (known value not calculated)
 */

void setup() {
  
Serial.begin(9600);
Serial.print( 0xff&WB_Parity_Calc(test),HEX);

}
char WB_Parity_Calc (char *str)
{

char Par = 0x00;

do {
Par = Par ^ *str;          /* XOR aller Zeichen bis CR */
str++;
} while (*(str-1) != 13);      /* bis CR */

Par = Par & 0x7F; 
return ~Par;     
}

void loop() {
}

Yep, is included:

unsigned char WB_Parity_Calc( char *str )
{
    unsigned char Par;

    Par = 0x00;
    while( *str )
        Par ^= *str++;
    
    Par = Par & 0x7F;                                                  
    return ~Par;         

}//

Hi guys,

Thanks for the help and sorry for the obvious error in transcribing!

I don't have the display with me right now so I cant test until tomorrow but already it makes a lot more sense. I will let you know how i get along.

Cheers!

Hi guys,

Im still having trouble getting anything to display when using any code other than the test code i mentioned in my first post. currently i am testing the code examples you have provided but no love so far.

Last night I have found some text explaining the telegram structure that i have translated from German below:

Calculation of the telegram length: A telegram length of 16 characters results from
16 x 4 = 64 = 0x40
Calculation of the longitudinal parity: An XOR between all characters starting at p of the identifier and ending before the two parity digits and the byte formed from the parity digits results in: 0xFF.
The user information is limited to 256 bytes for all telegrams

z I HH nC CR HH
z target number
I indoor displays
HH Length Number of characters of the user information [1 byte HEX] Calculation in 4 x useful bytes e.g. With 16 characters = 0x40
nC payload
CR end identifier [0x0D]
HH longitudinal parity

Example of a line that sends the text: "Weinweg" to the advertising module:
ASCII: z ; I ; 28; Weinweg; „CR“ ; xx (xx = parity)
Dezimal: 122; 73; 28; Weinweg; 13 ; xx (xx = parity)
Hexadezimal: 0x7A;0x49;0x1C;0x57;0x65;0x69;0x6E;0x77;0x65;0x67;0x0D; xx (xx = parity)

dannysprogis:
Hi guys,

Im still having trouble getting anything to display when using any code other than the test code i mentioned in my first post. currently i am testing the code examples you have provided but no love so far.

Please show your current, non-working code.

Hi,

here is the code exactly as i am working with it now.

char test[]={0x76,0x18,0x06,0x35,0x54,0x65,0x73,0x74,0x0d};
//char test[]={0x76,0x09,0x50,0x49,0x44,0x38,0x0d};
//char test[]={0x7A,0x49,0x1C,0x57,0x65,0x69,0x6E,0x77,0x65,0x67,0x0D}; //0x13

/*
  Serial.write(0x76); //  v
  Serial.write(0x09); //  TAB
  Serial.write(0x50); //  P
  Serial.write(0x49); //  I
  Serial.write(0x44); //  D
  Serial.write(0x38); //  8
  Serial.write(0x0d); //  CR
  Serial.write(0xe8); //  CRC value (known value not calculated)
 */

void setup() 
{
//Serial.begin(1200);  //SERIAL_7E2 
Serial.begin(1200,SERIAL_7E2);  //SERIAL_7E2
}

//////////////////////////////////////////////////////
//char WB_Parity_Calc (char *str)
//{
//
//char Par = 0x00;
//
//do {
//Par = Par ^ *str;          /* XOR aller Zeichen bis CR */
//str++;
//} while (*(str-1) != 13);      /* bis CR */
//
//Par = Par & 0x7F;
//return ~Par;     
//}
//////////////////////////////////////////////////////
unsigned char WB_Parity_Calc( char *str )
{
    unsigned char Par;

    Par = 0x00;
    while( *str )
      Par ^= *str++; 
      Par = Par & 0x7F;                                                 
    return ~Par;         

}
//////////////////////////////////////////////////////

void loop() {
  //Serial.print( 0xff&WB_Parity_Calc(test),HEX);
  Serial.print( WB_Parity_Calc(test),HEX);
  delay(1000); 
  
}

The serial data for the device is specified as:

Baudrate : 1200
Data Bits: 7
Stop Bits: 2
Parity: no

hence

Serial.begin(1200,SERIAL_7E2);

which works with my test code.

Post complete code that is supposed to send a different command to the display (not one of the test strings).

Hi,

there is no other test code available.

I created the test code myself based on the hex strings in the example.

Example of a line that sends the text: "Weinweg" to the advertising module:
ASCII: z ; I ; 28; Weinweg; „CR" ; xx (xx = parity)
Dezimal: 122; 73; 28; Weinweg; 13 ; xx (xx = parity)
Hexadezimal: 0x7A;0x49;0x1C;0x57;0x65;0x69;0x6E;0x77;0x65;0x67;0x0D; xx (xx = parity)

The code you most recently posted will not work to send data to the display.

What is your plan for that?

I think you might be misinterpreting the nature of the second byte of the packet. I can't make sense of the instruction, or account for the value of 9 in the example I looked at.

Hi,

my understanding is that i can calculate the parity and add it to the test string and then send to the device.

This is the code i am working with now but the device ignores it completely:

char test[]={0x76,0x18,0x06,0x35,0x54,0x65,0x73,0x74,0x0d};
//char test[]={0x76,0x09,0x50,0x49,0x44,0x38,0x0d};
//char test[]={0x7A,0x49,0x1C,0x57,0x65,0x69,0x6E,0x77,0x65,0x67,0x0D}; //0x13

/*
  Serial.write(0x76); //  v
  Serial.write(0x09); //  TAB
  Serial.write(0x50); //  P
  Serial.write(0x49); //  I
  Serial.write(0x44); //  D
  Serial.write(0x38); //  8
  Serial.write(0x0d); //  CR
  Serial.write(0xe8); //  CRC value (known value not calculated)
 */

void setup() 
{
//Serial.begin(1200);  //SERIAL_7E2 
Serial.begin(1200,SERIAL_7E2);  //SERIAL_7E2
}

//////////////////////////////////////////////////////
//char WB_Parity_Calc (char *str)
//{
//
//char Par = 0x00;
//
//do {
//Par = Par ^ *str;          /* XOR aller Zeichen bis CR */
//str++;
//} while (*(str-1) != 13);      /* bis CR */
//
//Par = Par & 0x7F;
//return ~Par;     
//}
//////////////////////////////////////////////////////
unsigned char WB_Parity_Calc( char *str )
{
    unsigned char Par;

    Par = 0x00;
    while( *str )
      Par ^= *str++; 
      Par = Par & 0x7F;                                                 
    return ~Par;         

}
//////////////////////////////////////////////////////

void loop() {
  //Serial.print( 0xff&WB_Parity_Calc(test),HEX);
  Serial.print(test+WB_Parity_Calc(test));
  delay(1000); 
  
}

This won't work. You can't concatentate C-strings with "+". Populate an array and use Serial.write().

  Serial.print(test+WB_Parity_Calc(test));

Or try something like this (NOTE required zero termination of test).

char test[]={0x76,0x18,0x06,0x35,0x54,0x65,0x73,0x74,0x0d,0}; //zero terminated

void setup()
{
Serial.begin(1200,SERIAL_7E2);  //SERIAL_7E2
}

unsigned char WB_Parity_Calc( char *str )
{
    unsigned char Par;

    Par = 0x00;
    while( *str )
      Par ^= *str++;
      Par = Par & 0x7F;                                                 
    return ~Par;         

}

void loop() {
 char s[2]={0,0};
 s[0]=WB_Parity_Calc(test); //make a 1 character C-string with checksum
  Serial.print(test);
  Serial.print(s);
  delay(1000);
}

thanks,

trying now....

GENIUS!!!

I now have a scrolling text! "test"

That works a treat!

Now the next stage to make it work for any text string input.

Yaay!