Search code examples
c++checksumcrc

Appending CRC (Checksum) to data stream serial port communication C++


I am appending CRC to a data stream. I am using a serial port libary from RS-232 for Linux and Windows.

I want to get the same value as the one displayed on the comport tool picture:

Serialport communcation from a comport tool

I am using const char[4086]="123456789"; as buffer.

CRC returned is bb3d which I then convert from unsigned short to char *.

#define SERIALPORT_BUFSIZE       4088

char *im_buf = (char *)malloc(SERIALPORT_BUFSIZE  + 8);

char *tx_buf = (char *)malloc(SERIALPORT_RCV_BUFSIZE  + 8);

const char buf[SERIALPORT_BUFSIZE]="123456789";

strcpy(im_buf, buf);
WriteToSerialPort(); // call to function

void SerialPortCom::WriteToSerialPort(void)
{
    if(!port_open) return;

    int i,n, tmp, tmp2;

    if(newline == 1)
    {
        strcat(im_buf, "0a");
    }
    else if(newline == 2)
    {
        strcat(im_buf, "0d");
    }
    else if(newline == 3)
    {
        strcat(im_buf, "0d0a");
    }

    n = strlen(im_buf) / 2;

    if(n < 1) return;

    for(i=0; i< n; i++)
    {
        tmp = im_buf[i*2];

        if((tmp >= '0') && (tmp <= '9'))
        {
            tmp -= '0';
        }
        else if((tmp >= 'a') && (tmp <= 'f'))
        {
            tmp -= ('a' - 10);
        }
        else if((tmp >= 'A') && (tmp <= 'F'))
        {
            tmp -= ('A' - 10);
        }

        tmp *= 16;

        tmp2 = im_buf[(i*2)+1];

        if((tmp2 >= '0') && (tmp2 <= '9'))
        {
            tmp2 -= '0';
        }
        else if((tmp2 >= 'a') && (tmp2 <= 'f'))
        {
            tmp2 -= ('a' - 10);
        }
        else if((tmp2 >= 'A') && (tmp2 <= 'F'))
        {
            tmp2 -= ('A' - 10);
        }

        tmp += tmp2;

        tx_buf[i] = tmp;

    }
    //generate crc
    unsigned short crc  = GetCrc16(im_buf, strlen(im_buf));
    //append crc
    memcpy( tx_buf + n, &crc, 2 );
    //send to serial port
    RS232_SendBuf(comport_nr, (unsigned char *)tx_buf, n+2);

}

What I get:   (12 34 56 78 70 11 4A 3D)
What I want:(12 34 56 78 7B 34 FD FD FD FD)

How to achieve the expected result?

unsigned short SerialPortCom::GetCrc16(const char* InStr,unsigned int len)
{
    //Crc16
    unsigned short Crc16Table[256];
    unsigned int i,j;
    unsigned short Crc;      
    unsigned char CRCHigh = 0xFF, CRCLow = 0xFF;

    for (i = 0; i < 256; i++)
    {
        Crc = i;
        for (j = 0; j < 8; j++)
        {
            if(Crc & 0x1)
                Crc = (Crc >> 1) ^ 0xA001;
            else
                Crc >>= 1;
        }
        Crc16Table[i] = Crc;
    }
    //CRC16
    Crc=0x0000;
    for(i=0; i<len; i++)
    {
        Crc = (Crc >> 8) ^ Crc16Table[(Crc & 0xFF) ^(uint8_t) InStr[i]];

    }
    //Crc ^= 0x0000;

    return Crc;
}

Solution

  • First, some notes :

    • Your inputs are ASCII hex. Each character is a half-byte, but your input string has an odd number of characters : you discard the last character.
    • What you expect is 12 34 56 78 7B 34 : 0x347B is the CRC for 0x12 0x34 0x56 0x78. The additional FDs looks like some irrelevant end of buffer (or another bug ?).

    The problems with your program :

    You need a data buffer to send with RS232_SendBuf. You convert your ASCII to data and put it in tx_buf and then you take its CRC. That's fine, but you use strcat to append the CRC to the buffer ; you can't do that, strcat is for text. strcat expect two null terminated strings, but you have two data buffers.

    You CRC function has a problem too : you have a superfluous mask & 0x7F that removes the last bit of each CRC byte. The 0xFF mask is sufficient (and not even needed actually).

    You also allocate a buffer with new char[2], but never delete it. That will work but it's ugly.

    Note that your CRC is just two bytes ; just return Crc directly as an unsigned short.

    After that, just append your CRC to your buffer with memcpy.
    In your case, that would be memcpy( tx_buf + n, &my_crc_short, 2 );