CHKSUM comparison fails although recomputed CHKSUM looks correct?

I am sending this string "Forum" with computed CHKSUM (0xF7) from UNO to NANO using SUART (Software UART) Port (Fig-1). At the receiver side, the string arrives alright and the recomputed CHKSUM looks correct. However, the following comparison statement fails, and it prevents me from showing this message: "Valid string received." Would appreciate to know what is happening here? (Sender/Receiver sketches are posted below.)

if (CHKSUM == myMsg[5])  //myMsg[5] holds the arrived CHKSUM
{
      Serial.println("Valid string received.");
}

1. The connection diagram between UNO and NNAO using SUART Port.
uartUnoNano-1
Figure-1:

2. Sender Sketch

#include<SoftwareSerial.h> //Hedaer file that includes ready-made functons
SoftwareSerial SUART(2, 3); //SUART port is created with SRX = DPin-2, STX = DPin-3
#define STX 0x02  //STX = Start of Text
#define ETX 0x03  //ETX = End of Text

char myMsg[] = "Forum"; //Message/sting to be sent
byte CHKSUM = 0;

void setup()
{
  Serial.begin(9600); //UART Port is enabled
  SUART.begin(9600);  //SUART port is enabled
}

void loop()
{
  SUART.write(STX); //StartMarker is end
  SUART.write(myMsg, sizeof myMsg - 1); //bytes of the message are sent
  //--------------------------
  for (int i = 0; i < sizeof myMsg - 1; i++)
  {
    CHKSUM += myMsg[i];
  }
  CHKSUM = ~CHKSUM + 1;  //computing check sum = F7
  Serial.println(CHKSUM, HEX);   //shows: F7
  SUART.write(CHKSUM);  //sending CHKSUM to NANOCHKSUM = 0;
  CHKSUM = 0;
  //-----------------------------
  SUART.write(ETX); //EndMarker is sent
  delay(1000);  //test interval
}

3. Receiver Sketch

#include<SoftwareSerial.h>
SoftwareSerial SUART(2, 3);
#define STX 0x02  //STX = Start of Text
#define ETX 0x03  //ETX = End of Text
char myMsg[10];
byte CHKSUM = 0;

void setup()
{
  Serial.begin(9600);
  SUART.begin(9600);
  pinMode(4, OUTPUT);
}

void loop()
{
  byte n = SUART.available();
  if ((n == 1) && (SUART.read() == STX))//StartMarker is detected
  {
    byte m = SUART.readBytesUntil(ETX, myMsg, 10); //save all except ETX
    myMsg[m] = '\0';  //inserting Null-character 
    Serial.println(myMsg); //wild charcater after Forum is due to CHKSUM
    //-----------------------
    for (int i = 0; i < m - 1; i++)
    {
      CHKSUM += myMsg[i]; //add all the bytes of received message = 9
    }
    CHKSUM = ~CHKSUM + 1; //9 = 0000 1001 ==> 1111 0110 + 1 = 11110111 = 0xF7
    Serial.println(CHKSUM, HEX); //shows: F7
    if (CHKSUM == myMsg[5])  //myMsg[5] holds the arrived CHKSUM
    {
      Serial.println("Valid string received.");
      digitalWrite(4, HIGH);  //flash LED1 at DPin-4
      delay(200);
      digitalWrite(4, LOW);
      delay(200);
    }
    CHKSUM = 0;  //reset CHKSUM variable
  }
}
char myMsg[10];
   :
if (CHKSUM == myMsg[5]) 

Perhaps due to integer promotion, myMsg[5] is getting sign extended to FFF7 (since it's a signed char) and CHKSUM is getting promoted to 00F7 since it's unsigned?

1 Like

1. Then should I do the following?

if ((byte)CHKSUM == (byte)myMsg[5])  //myMsg[5] holds the arrived CHKSUM
{
      Serial.println("Valid string received.");
}

2. I have done the above and it works.

3. Even though I declare variables as 8-bit (byte, uint8_t, int8_t, char, unsigned char), are they always be promoted to 16-bit (in UNO) during processing?

4. If I change the array declaration (at the receiver side) from char myMsg[10] to unsigned char myMsg[10], then the 16-bit promotion will still be there?

char s = 0xF7; // negative
// byte u = 0xF7; // positive
byte u = s; // EDIT: no problem, still positive
if (s != u) Serial.println("They're not equal"); 
if ((byte)s == u) Serial.println("cast helps"); 

not sure if thinking of int promotion helps

1 Like

You are comparing variables with unequal type. Therefore the compiler will promote one (or maybe even both) to a type that can accommodate all info in both variables. In this case that would be int as that can fit both 255 and -128.
If you would declare CHECKSUM as char, both sides of your comparison have the same type and can be compared without promotion.
The other way, that you already successfully tried, is a cast from char to byte. The cast to byte is not needed for CHECKSUM as it is already of type byte.

1 Like

The way I learned to think of it is that expressions are required to behave AS IF the arguments were promoted to integers. If you compare two uint8_t or two int8_t, the compiler will optimize to simple comparisons of the 8bit values. If the types are mixed, then it has no choice but to actually do the conversion.
(But you'll notice I wasn't very certain that this was the problem in your code...)

For example, consider this simple program:

#include <avr/io.h>

int main() {
  uint8_t u8, v8;
  int8_t s8, t8;

  u8 = PINB;    // get random values
  v8 = PINB;  s8 = PINB;  t8 = PINB;

  if (u8 == v8)  // unsigned to unsigned
    PORTD |= 1;
  if (s8 == t8)  // signed to signed
    PORTD |= 2;
  if (u8 == s8)  // signed to unsigned
    PORTD |= 4;
  PORTD |= 128;
}

This yields code like:

  if (u8 == v8)  // unsigned to unsigned
   8:	23 13       	cpse	r18, r19   ;; simple 8-bit compare
   a:	00 c0       	rjmp	.+2
    PORTD |= 1;
   c:	58 9a       	sbi	0x0b, 0	; 11
  if (s8 == t8)  // signed to signed
   e:	89 13       	cpse	r24, r25   ;; simple 8-bit compare
  10:	00 c0       	rjmp	.+2
    PORTD |= 2;
  12:	59 9a       	sbi	0x0b, 1	; 11
  if (u8 == s8)  // signed to unsigned
  14:	30 e0       	ldi	r19, 0x00	;; extend uint8_t to int.
  16:	08 2e       	mov r0, r24     ;; signed extend int8_t
  18:	00 0c       	add	r0, r0      ;; move high bit into carry (LSL)
  1a:	99 0b       	sbc	r25, r25    ;; (0 - <carry>) bit extension
  1c:	28 17       	cp	r18, r24    ;; compare 16 bits
  1e:	39 07       	cpc	r19, r25
  20:	01 f4       	brne	.+2      	; 0x22 <main+0x22>
    PORTD |= 4;
  22:	5a 9a       	sbi	0x0b, 2	; 11
  PORTD |= 128;
  24:	5f 9a       	sbi	0x0b, 7	; 11

BTW, It'll manage to avoid the unnecessary promotion even with -O0

1 Like

typically the checksum posted is the complement of the checksum so that when it is included in the checksum computation at the receiver the result is zero. no need to make a computed and received checksum comparison. just check for zero

1 Like

@gcjr
Yes! The information bytes could be added together, discard the carry and then add it with the received CHSUM; the result should be zero for the received frame to be valid.

However, the task is to re-compute the CHKSUM from the received frame; so, information bytes have to be added together, discard carry, invert all the bits, add 1 and then compare the result with received CHKSUM (which is within myMsg[5]) for equality.

I think you've also run into why "char" is also "unsigned" by default on many newer CPU/Compiler combinations. There's no reason for the items in your myMsg[] buffer to be signed, or in general for character strings (in either 7bit ascii, 8bit "extended" character sets, or more complex schemes) to have signed components.

@westfw
I am trying to understand the reason why your hint in post #2 has solved my problem when I did byte-cast on myMsg[5].

So, the fundamental question to myself --
Does the content of myMsg[5] (0xF7) bear any meaning in terms of sign and magnitude until it is processed?

I think that the answer to the above question becomes available only when the variable is processed using some operators. In my case, during comparison test, the compiler looks at data type (char which is signed) and MSBit (which is 1 in F7) and it finds -9.

The other variable CHKSUM also contains 0xF7; but, its data type is byte (always positive); as result, the compiler finds +247. There is a mismatch both in sign and magnitude of the two variables. Naturally, the comparison test has failed.

The compiler is not helping the programmer here. It is the programmer who needs to understand what is going on and then he writes the codes properly.

From your hint of post #2, I conceived the idea of doing something to convince the compiler for taking the content of myMsg[5] (0xF7) as positive number though the data type is signed and accordingly, I did byte-cast. As a result, my comparison test for equality has passed successfully.

Content of myMsg[5] ==> 0xFFF7 (-9) in tepm1 because of signed data type (char)
Content of CHKSUM ==> 0x00F7 (247) in temp2 because of unsigned data type (byte)

For obvious reason, the following comparison test has failed:

if(temp1 == temp2)
{
   .........

Whether char is "signed" or not depends on the compiler; it's not defined by the C standard. With AVR-GCC, "char" is signed. With ARM-GCC, "char" is unsigned. (isn't that FUN!)
See https://gcc.gnu.org/wiki/avr-gcc and ARM Linux - Documentation - FAQs - Signed Char

You can work around compiler differences by always explicitly including the signed-ness:

char buffer[5];           // Signed?  compiler dependent!
unsigned char buffer[5];  // always unsigned.
signed char buffer[5];    // always signed.
1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.