我编写了一些拦截数据包并修改它们的代码。数据包中的 4 个字节代表一个浮点值,但以相反的字节顺序(小端)。这给我带来了很多问题。让我用一段简化的代码来解释一下:
#include <Arduino.h>
typedef struct {
uint8_t start_seq[2];
uint8_t body_size;
uint8_t type;
uint8_t unknown;
uint8_t entity_id;
uint8_t reserved[3];
float value;
uint8_t end_seq[2];
} Packet;
void printRawPacket(const char* message, const uint8_t* packet, size_t length) {
Serial.print(message);
for (int i = 0; i < length; i++) {
Serial.printf("%02X ", packet[i]);
}
Serial.println();
}
// Packet interceptor function
void handlePacket(uint8_t* raw_packet, size_t length) {
// Create a pointer to the packet structure
Packet* packet = (Packet*)raw_packet;
printRawPacket("Original bytes: ", raw_packet, sizeof(raw_packet));
// Correctly Prints: "Original bytes: BE EF 08 03 00 09 00 00 00 00 00 C0 40 EF BE"
// Access the float value without creating a copy of the 4 bytes
Serial.print("Original value: ");
Serial.println(packet->value);
// Incorrectly prints a random value or "ovf" instead of 6.00
// Modify the float value without creating a copy of the 4 bytes
packet->value = -120.0f; // little endian would be 00 00 F0 C2; big endian would be C2 F0 00 00
Serial.print("Modified value: ");
Serial.println(packet->value); // Prints -120.0 correctly
printRawPacket("Modified bytes: ", raw_packet, sizeof(raw_packet));
// Incorrectly Prints: "Modified bytes: BE EF 08 03 00 09 00 00 00 00 00 C0 00 00 F0"
// Showing the float value was not stored correctly and the footer was overwritten
// Expected output would have been: "Modified bytes: BE EF 08 03 00 09 00 00 00 00 00 00 00 F0 C2 EF BE"
}
void setup() {
Serial.begin(115200);
// Example packet
uint8_t raw_packet[] = {
// ------ HEADER ------
0xBE, 0xEF, // start sequence
0x08, // body size uint8_t
0x03, // type uint8_t
0x00, // unknown
// ------ HEADER ------
// ------ BODY ------
0x09, // entity id uint8_t
0x00, 0x00, 0x00, // reserved
0x00, 0x00, 0xC0, 0x40, // value (float, little-endian) // 6.0f
// ------ BODY ------
// ------ FOOTER ------
0xEF, 0xBE // end sequence
// ------ FOOTER ------
};
handlePacket(raw_packet, sizeof(raw_packet));
}
void loop() {
// Nothing to do here
}
正如评论中所解释的,这在多个级别上不起作用。
我可以通过将值字段设为
uint8_t value[4]
,然后使用如下所示的 getter 和 setter 函数来轻松解决该问题:
typedef struct {
uint8_t start_seq[2];
uint8_t body_size;
uint8_t type;
uint8_t unknown;
uint8_t entity_id;
uint8_t reserved[3];
uint8_t value[4];
uint8_t end_seq[2];
// Function to get the float value from the raw packet in little-endian format
float getFloatLeValue() const {
// reverse byte order and return as float
uint32_t temp = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0];
float result;
memcpy(&result, &temp, sizeof(result)); // Copy uint32_t to float
return result;
}
// Function to set the float value into the raw packet in little-endian format
void setFloatLeValue(float newValue) {
// Copy float to uint32_t
uint32_t temp;
memcpy(&temp, &newValue, sizeof(temp));
// Copy uint32_t to value in little-endian format
value[0] = temp & 0xFF;
value[1] = (temp >> 8) & 0xFF;
value[2] = (temp >> 16) & 0xFF;
value[3] = (temp >> 24) & 0xFF;
}
} Packet;
但是当我想做的只是反向解释 4 个字节时,这似乎显得不必要的复杂和复杂。
有没有更简单更优雅的方式?
(我正在使用 Arduino 框架在 ESP32 的 PlatformIO 上用 C++ 编写代码。)
让我们看看@PasserBy 建议的
bit_cast
会是什么样子。我们可以使用来自 https://en.cppreference.com/w/cpp/numeric/bit_cast 的 memcpy 进行示例实现
我们还可以使用
htonl
/ntohl
进行实际交换。或者还有bswap_32
。
template<class To, class From>
To my_bit_cast(const From& src) noexcept
{
To dst;
memcpy(&dst, &src, sizeof(To));
return dst;
}
float getFloatLeValue() const {
return my_bit_cast<float, uint32_t>(
ntohl(
my_bit_cast<uint32_t, uint8_t[4]>(value)));
}
void setFloatLeValue(float newValue) {
uint32_t temp = htonl(my_bit_cast<uint32_t, float>(newValue));
memcpy(&value, &temp, sizeof(value));
}
呃...不太好?