Проверка Arduino на Raspberry crc32

Я пытаюсь отправить сообщения через последовательный интерфейс USB моего Arduino (C++) на Raspberry Pi (Python).

На стороне Arduino я определяю структуру, которую затем копирую в char[]. Последняя часть структуры содержит контрольную сумму, которую я хочу вычислить, используя CRC32. Я копирую структуру во временный массив символов -4 байта, чтобы очистить поле контрольной суммы. Затем контрольная сумма вычисляется с использованием временного массива, и результат добавляется в структуру. Затем структура копируется в byteMsg, который отправляется по последовательному соединению.

На конце малины я делаю наоборот, я получаю строку байта и вычисляю контрольную сумму по сообщению - 4 байта. Затем распакуйте тестовую строку и сравните полученную и вычисленную контрольную сумму, но, к сожалению, это не удается.

Для отладки я сравнил проверку crc32 на python и arduino на наличие строки "Hello World", и они сгенерировали одинаковую контрольную сумму, поэтому проблем с библиотекой не возникает. Raspberry также может прекрасно декодировать оставшуюся часть сообщения, так что распаковка данных в переменные тоже выглядит нормально.

Любая помощь приветствуется.

Код Python:

   def unpackMessage(self, message):
        """ Processes a received byte string from the arduino """

        # Unpack the received message into struct
        (messageID, acknowledgeID, module, commandType,
         data, recvChecksum) = struct.unpack('<LLBBLL', message)

        # Calculate the checksum of the recv message minus the last 4
        # bytes that contain the sent checksum
        calcChecksum = crc32(message[:-4])
        if recvChecksum == calcChecksum:
            print "Checksum checks out"

Библиотека Aruino crc32 взята с http://excamera.com/sphinx/article-crc.html

crc32.h

#include <avr/pgmspace.h>

static PROGMEM prog_uint32_t crc_table[16] = {
    0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
    0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
    0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
    0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};

unsigned long crc_update(unsigned long crc, byte data)
{
    byte tbl_idx;
    tbl_idx = crc ^ (data >> (0 * 4));
    crc = pgm_read_dword_near(crc_table + (tbl_idx & 0x0f)) ^ (crc >> 4);
    tbl_idx = crc ^ (data >> (1 * 4));
    crc = pgm_read_dword_near(crc_table + (tbl_idx & 0x0f)) ^ (crc >> 4);
    return crc;
}

unsigned long crc_string(char *s)
{
  unsigned long crc = ~0L;
  while (*s)
    crc = crc_update(crc, *s++);
  crc = ~crc;
  return crc;
}

Главный Arduino Sketch

struct message_t {
    unsigned long messageID;
    unsigned long acknowledgeID;
    byte module;
    byte commandType;
    unsigned long data;
    unsigned long checksum;
};

void sendMessage(message_t &msg)
{
    // Set the messageID
    msg.messageID = 10;
    msg.checksum = 0;

    // Copy the message minus the checksum into a char*
    // Then perform the checksum on the message and copy
    // the full msg into byteMsg
    char byteMsgForCrc32[sizeof(msg)-4];
    memcpy(byteMsgForCrc32, &msg, sizeof(msg)-4);
    msg.checksum = crc_string(byteMsgForCrc32);

    char byteMsg[sizeof(msg)];
    memcpy(byteMsg, &msg, sizeof(msg));

    Serial.write(byteMsg, sizeof(byteMsg));

void loop() {
    message_t msg;
    msg.module = 0x31;
    msg.commandType = 0x64;
    msg.acknowledgeID = 0;
    msg.data = 10;

    sendMessage(msg);

С наилучшими пожеланиями, Thiezn

2 ответа

Решение

Вы совершаете классическую ошибку уровня связи структура / сеть / последовательный порт / вставка. Структуры имеют скрытое заполнение, чтобы выровнять элементы по подходящим границам памяти. Не гарантируется, что это будет одинаковым на разных компьютерах, не говоря уже о разных процессорах / микроконтроллерах.

Возьмите эту структуру в качестве примера:

struct Byte_Int
{
     int x;
     char y;
     int z;
}

Теперь на базовом 32-битном процессоре x86 у вас есть 4-байтовая граница памяти. Это означает, что переменные выровнены либо по 4 байта, либо по 2 байта, либо не совсем, в зависимости от типа переменной. Пример будет выглядеть следующим образом в памяти: int x для байтов 0,1,2,3, char y для байта 4, int z для байтов 8,9,10,11. Почему бы не использовать три байта во второй строке? Потому что тогда контроллер памяти должен был бы сделать две выборки, чтобы получить одно число! Контроллер может читать только одну строку за раз. Таким образом, структуры (и все другие виды данных) имеют скрытое заполнение для выравнивания переменных в памяти. Пример структуры имел бы sizeof() 12, а не 9!

Теперь, как это относится к вашей проблеме? Вы Memcpy() структура непосредственно в буфер, включая заполнение. Компьютер на другом конце не знает об этом заполнении и неверно интерпретирует данные. Для чего вам нужна функция сериализации, которая берет элементы ваших структур и вставляет их в буфер по одному, таким образом вы теряете заполнение и в итоге получаете что-то вроде этого: [0,1,2,3: int x][4: char y][5,6,7,8: int z]. Все как один длинный байтовый массив / строка, которую можно безопасно отправить с помощью Serial(). Конечно, на другом конце вам придется анализировать эту строку в понятные данные. Unpack() в Python сделает это за вас, если вы укажете правильную строку формата.

Наконец, int на Arduino имеет длину 16 бит. На пк вообще 4! Так что соберите вашу распакованную строку формата с осторожностью.

Массив char, который я передавал функции crc_string, содержал символы '\0'. Crc_string перебирал массив, пока не нашел '\0', что не должно происходить в этом случае, так как я использовал массив char в качестве потока байтов для отправки по последовательному соединению.

Я изменил функцию crc_string, чтобы принимать размер массива в качестве аргумента и перебирать массив, используя это значение. Это решило проблему.

Вот новая функция

unsigned long crc_string(char *s, size_t arraySize) 
{ 
  unsigned long crc = ~0L; 
  for (int i=0; i < arraySize; i++) { 
    crc = crc_update(crc, s[i]); 
  } 
  crc = ~crc; 
  return crc; 
}
Другие вопросы по тегам