How to elegantly read/write a struct field as little endian float?

I’ve written some code that intercepts data packets and modifies them. 4 bytes in the data packets represent a float value, but in reverse byte order (little-endian). And that creates a lot of issues for me. Let me explain with a simplified piece of code:

#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
}

As explained in the comments, this doesn’t work on multiple levels.

I can easily work around that issue by making the value field a uint8_t value[4] and then using a getter and setter function like this:

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;

But this just seems so unnecessarily complex and complicated when all I wanna do is interpret 4 bytes in reverse.

Is there a simpler more elegant way?

(I’m writing my code in C++ on PlatformIO for an ESP32 using the Arduino Framework.)

7

Let’s see how this would look with bit_cast suggested by @PasserBy. We can take an example implementation using memcpy from https://en.cppreference.com/w/cpp/numeric/bit_cast

Also let’s use htonl/ntohl for the actual swap. Alternatively there is 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));
}

It’s uh… Not that great?

About the float_le wrapper, the suggestion is:

class float_le {
    uint8_t value[4];
    public:
    float_le(float v) {
        // same as setFloatLeValue
    }
    operator float() const {
        // same as getFloatLeValue
    }
}

This is trivially copyable (e.g by memcpy) and is assignable/castable to/from ordinary float. Use inside the Packet struct as float_le value;. Could be useful if you need the flipped float in multiple places.

May have nasty alignment/padding issues. Let’s see what comments point out.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật