【问题标题】:Arduino to Raspberry crc32 checkArduino 到 Raspberry crc32 检查
【发布时间】:2015-04-26 03:13:11
【问题描述】:

我正在尝试通过我的 Arduino (C++) 的串行 USB 接口向 Raspberry Pi (Python) 发送消息。

在 Arduino 方面,我定义了一个结构,然后将其复制到一个 char[] 中。结构的最后一部分包含我想使用 CRC32 计算的校验和。我将结构复制到临时 char 数组 -4 字节中以去除校验和字段。然后使用临时数组计算校验和,并将结果添加到结构中。然后将该结构复制到通过串行连接发送的 byteMsg 中。

在树莓端我做相反的事情,我接收字节串并计算消息的校验和 - 4 个字节。然后解压字节串并比较接收和计算的校验和,但不幸的是失败了。

为了调试,我比较了 python 和 arduino 上的 crc32 检查字符串“Hello World”,它们生成了相同的校验和,所以库似乎没有问题。覆盆子还能够很好地解码消息的其余部分,因此将数据解包到变量中似乎也可以。

任何帮助将不胜感激。

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 草图

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);

亲切的问候, 蒂兹恩

【问题讨论】:

    标签: python c++ arduino raspberry-pi crc32


    【解决方案1】:

    您正在犯经典的结构到网络/串行/插入通信层错误。结构具有隐藏的填充,以便将成员对齐到合适的内存边界。这不能保证在不同的计算机之间是相同的,更不用说不同的 CPU/微控制器了。

    以这个结构体为例:

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

    现在在基本的 32 位 x86 CPU 上,您有一个 4 字节的内存边界。这意味着根据变量的类型,变量对齐到 4 个字节、2 个字节或根本不对齐。该示例在内存中看起来像这样:字节 0、1、2、3 上的 int x,字节 4 上的 char y,字节 8、9、10、11 上的 int z。为什么不使用第二行的三个字节?因为那时内存控制器必须进行两次提取才能获得一个数字!控制器一次只能读取一行。因此,结构(和所有其他类型的数据)具有隐藏的填充,以便使变量在内存中对齐。示例结构的 sizeof() 为 12,而不是 9!

    现在,这与您的问题有何关系?您正在 memcpy() 将结构直接放入缓冲区,包括填充。另一端的计算机不知道此填充并误解了数据。您需要一个序列化函数,该函数将结构的成员一次传递到缓冲区中,这样您就会丢失填充并最终得到如下结果: [0,1,2,3: int x][4: char y][5,6,7,8: int z]。全部作为一个冗长的字节数组/字符串,可以使用 Serial() 安全地发送。当然,在另一端,您必须将此字符串解析为可理解的数据。只要您提供正确的格式字符串,Python 的 unpack() 就会为您执行此操作。

    最后,Arduino 上的 int 是 16 位长。在PC上一般4!所以要小心组装你的解压格式字符串。

    【讨论】:

    • 感谢您的快速回复!一个明确的解释,今晚将实施并报告我的结果
    • 我已经尝试序列化结构(获取每个元素并将它们复制到一个 char* 中,但结果是相同的大小(在这种情况下为 18 个字节)。我注意到有时我会得到一个校验和结果为 0,而我确实在用数据填充结构。这给了我一种预感,即当 crc32 函数在字符串中找到一个 NULL '\0' 字符时,它正在退出,经过一些测试,它似乎确实如此。现在我不得不承认 crc32 函数有点超出我的范围,有人知道如何让它也处理 NULL 终止的字符吗?
    • 在仔细检查 crc_string() 后,我注意到了你的 while(s)。现在一个 '\0' 确实是一个 0 所以你的循环*会在遇到一个时停止。这是使用字符串而不是字节数组的缺点!因此,解决方案是停止将其视为字符串。不要检查( *s ),而是传递“字符串”的长度并根据此长度运行 for 循环。最后:CRC 为 0 是完全有效的。这实际上只是添加和移位位,因此您最终可以得到一个 0。尤其是较短的 CRC 更容易溢出。
    【解决方案2】:

    我传递给 crc_string 函数的 char 数组包含 '\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; 
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-28
      • 2017-12-19
      • 2018-04-16
      • 2015-12-17
      • 1970-01-01
      • 2013-06-07
      • 1970-01-01
      相关资源
      最近更新 更多