ESP8266异常在尝试从字节数组读取float时

ESP8266 Exception while trying to read float from byte array

本文关键字:数组 字节数 读取 float 字节 异常 ESP8266      更新时间:2023-10-16

我在这里有一个奇怪的问题(WEMOS D1 mini)。我正在旋转一个用于使用NRF24L01发射器的网络的库。

我定义了一个Gloabl Uint8_t Arry,它代表我的接收缓冲区。从另一个设备接收传输时,阵列会填充。填充数组后,我开始解析缓冲区,以确定是否有有效的标头/帧。标头包含一些典型数据,例如发件人,校验和...如果标题有效,则帧的数据将被解析。数据的结构始终相同。可以有x所谓的datatriples。DataTriph是密钥(CSTRING),值类型(UINT8_T)和值本身的组合。值类型可以是基本类型,例如int8,uint8或float或string。

现在是Stange Thing。

为了保存内存,我不将值从缓冲区复制到专用变量中。我一直指向缓冲区中的位置。当我想阅读一个值时,我会做一个简单的指针并读取值。如果值是字符串或简单的1个字节长UINT8_T,则这是非常有效的。但是,如果是浮点或uint32_t,我会得到一个告诉我的loadStorSorealignmentcause excetion,我想访问不访问的内存。怎么会这样?指针绝对指出了正确的缓冲液。我对此进行了检查,因为如果我创建一个新的浮点变量,并从缓冲区到变量地址做一个memcpy,那么一切都很好。但是我A创建了一个浮点指针,并指向缓冲区中的位置,我得到了一个例外。

有人可以告诉我原因或告诉我我做错了什么?

这是一些代码片段,可以更好地理解。

bool SBSmartHomeBasicDevice::parseDataTriples() {
if (_IncommingTransmission) {
    uint8_t* iCurrentPos;
    uint8_t iDataTripleCount = 0;
    // _LastReceivedFrame.Payload points to the receive buffer
    for (iCurrentPos = _LastReceivedFrame.Payload; iCurrentPos < (_LastReceivedFrame.Payload + _LastReceivedFrame.Header.PayloadSize); iCurrentPos) {
        // Catch the type of the triple
        uint8_t type = *((uint8_t*)iCurrentPos);
        // increase the pointer about uint8_t size
        iCurrentPos += sizeof(uint8_t);
        // Catch the key of the triple
        char* key;
        key = (char*)(iCurrentPos);
        // increase the pointer about strlen of key + 1 
        iCurrentPos += strlen((char*)iCurrentPos) + 1;
        // catch the value
        void* value = (void*)(iCurrentPos);
        _LastReceivedTriples[iDataTripleCount].setType(type);
        _LastReceivedTriples[iDataTripleCount].setKey(key);

        // ***
        // Here starts the interesting part 
        // ***
        float* fTmp = (float*)iCurrentPos;
        // The following works perfect
        // ****
        float f;
        memcpy(&f, fTmp, sizeof(float));
        Serial.println(f);
        // ****
        // The following causes an exception
        Serial.println(*fTmp);
        // *** EXCEPTION ***

        _LastReceivedTriples[iDataTripleCount].setValue(iCurrentPos);
        // now increase the pointer
        switch (type) {
        case SMART_HOME_VALUE_TYPE_STRING:
            iCurrentPos += strlen((char*)iCurrentPos) + 1;
            break;
        case SMART_HOME_VALUE_TYPE_INT8:
        case SMART_HOME_VALUE_TYPE_UINT8:
            iCurrentPos += sizeof(int8_t);
            break;
        case SMART_HOME_VALUE_TYPE_INT16:
        case SMART_HOME_VALUE_TYPE_UINT16:
            iCurrentPos += sizeof(int16_t);
            break;
        case SMART_HOME_VALUE_TYPE_FLOAT:
            iCurrentPos += sizeof(float);
            break;
        case SMART_HOME_VALUE_TYPE_INT32:
        case SMART_HOME_VALUE_TYPE_UINT32:
            iCurrentPos += sizeof(int32_t);
            break;
        default:
            Serial.println("parseDataTriples(): Unknown ValueType");
        }
        _LastReceivedTriples[iDataTripleCount].print();
        iDataTripleCount++;
        _LastReceivedTriplesCount = iDataTripleCount;
    } 
    return true;
}
return false;

}

非常感谢您抽出宝贵的时间并帮助我。

问候Schullebernd

ESP8266只能读取float S(和16位int S,32位int s,double s)从正确对齐的地址,即地址,即是数据类型的多个地址。由于 float是4个字节长,因此只能从4个倍数的地址读取4个字节。否则您会得到上述例外。

您正在使用uint8_t数组。uint8_t是1个字节长,因此不需要,并且可能不会以任何方式对齐。数组可能从地址170001开始。如果您现在在数组中的位置40处具有float值,则您的代码试图从地址170041读取float值,该值未正确对齐并导致例外。

>

解决方案是首先将float值的字节复制到局部,正确对齐的变量。那就是您要做的,但随后您在不对准的位置再次访问并获得例外。

    float* fTmp = (float*)iCurrentPos; // does not copy; takes a pointer to the orignal location
    float f;
    memcpy(&f, fTmp, sizeof(float)); // copies data to aligned variable
    Serial.println(f); // prints value from aligned variable
    Serial.println(*fTmp); // prints value for original, non-aligned location

只是摆脱了最后一行。