Arduino 到 Raspberry crc32 检查
Arduino to Raspberry crc32 check
我正在尝试通过我的 Arduino (C++) 的串行 USB 接口将消息发送到 Raspberry Pi (Python)。
在 Arduino 方面,我定义了一个结构,然后将其复制到一个 char[] 中。该结构的最后一部分包含我想使用 CRC32 计算的校验和。我将结构复制到临时字符数组 -4 字节中以去除校验和字段。然后使用临时数组计算校验和,并将结果添加到结构中。然后将该结构复制到 byteMsg 中,后者通过串行连接发送。
在覆盆子端我做相反的事情,我收到字节串并计算消息的校验和 - 4 个字节。然后解压字节串并比较接收到的和计算出的校验和,但不幸的是,这失败了。
为了调试,我比较了 python 和 arduino 上字符串 "Hello World" 的 crc32 检查,它们生成了相同的校验和,所以库似乎没有问题。树莓派还能够很好地解码消息的其余部分,因此将数据解包到变量中似乎也可以。
如有任何帮助,我们将不胜感激。
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);
亲切的问候,
蒂森
您犯了典型的 struct-to-network/serial/insert 通信层错误。结构具有隐藏的填充,以便将成员对齐到合适的内存边界。这不能保证在不同的计算机上是相同的,更不用说不同的 CPUs/microcontrollers.
以这个结构为例:
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: 整数 x][4: 字符 y][5,6,7,8: 整数 z]。全部作为一个冗长的 bytearray/string,可以使用 Serial() 安全地发送。当然,在另一端,您必须将此字符串解析为可理解的数据。只要您提供正确的格式字符串,Python 的 unpack() 就会为您完成此操作。
最后,Arduino 上的 int 是 16 位长。一台pc上一般4个!所以 assemble 请小心解压格式字符串。
我传递给 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;
}
我正在尝试通过我的 Arduino (C++) 的串行 USB 接口将消息发送到 Raspberry Pi (Python)。
在 Arduino 方面,我定义了一个结构,然后将其复制到一个 char[] 中。该结构的最后一部分包含我想使用 CRC32 计算的校验和。我将结构复制到临时字符数组 -4 字节中以去除校验和字段。然后使用临时数组计算校验和,并将结果添加到结构中。然后将该结构复制到 byteMsg 中,后者通过串行连接发送。
在覆盆子端我做相反的事情,我收到字节串并计算消息的校验和 - 4 个字节。然后解压字节串并比较接收到的和计算出的校验和,但不幸的是,这失败了。
为了调试,我比较了 python 和 arduino 上字符串 "Hello World" 的 crc32 检查,它们生成了相同的校验和,所以库似乎没有问题。树莓派还能够很好地解码消息的其余部分,因此将数据解包到变量中似乎也可以。
如有任何帮助,我们将不胜感激。
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);
亲切的问候, 蒂森
您犯了典型的 struct-to-network/serial/insert 通信层错误。结构具有隐藏的填充,以便将成员对齐到合适的内存边界。这不能保证在不同的计算机上是相同的,更不用说不同的 CPUs/microcontrollers.
以这个结构为例:
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: 整数 x][4: 字符 y][5,6,7,8: 整数 z]。全部作为一个冗长的 bytearray/string,可以使用 Serial() 安全地发送。当然,在另一端,您必须将此字符串解析为可理解的数据。只要您提供正确的格式字符串,Python 的 unpack() 就会为您完成此操作。
最后,Arduino 上的 int 是 16 位长。一台pc上一般4个!所以 assemble 请小心解压格式字符串。
我传递给 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;
}