You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32 GPS解析:如何高效将uint8_t数组转换为单个float值?

问题

我是嵌入式开发新手,当前使用ESP32-S3-WROOM-1从NEO-6M GPS模块获取UART数据,开发环境为Visual Studio Code。目前通过逐字节循环的方式将uint8_t数组转换为float值(如纬度、经度),具体代码如下:

char* Post_Time_String = strstr(String_Input, ",");
F_Extract_Until_Comma(Post_Time_String, Lat_Val);
for(int itr=0; itr<sizeof(Lat_Val),itr++)
{
    if(Lat_Val[itr] != ".")
    {
        gps_info->GPS_POSITION->latitude = (10 * gps_info->GPS_POSITION->latitude) + Lat_Val[itr];
    }
}

但这种方式效率较低,希望找到无需循环的更优转换方案,比如是否可使用strtof函数实现?附上当前编写的NMEA解析相关完整C代码及头文件供参考:

NMEA_PARSE_CODE.c

#include <stdio.h>
#include <NMEA_PARSE_CODE.h>

void F_FIND_COMMA(char *String_Input, char* String_Output)
{
    char *String_Output = strstr(String_Input, ",");

    return String_Output;
}

void F_Extract_Until_Comma(char *String_Input, uint8_t* Extracted_Output[])
{
    uint8_t String_Length = strlen(String_Input);
    uint8_t* F_Buffer[20] = {NULL};
    char* comma_parse_string = F_FIND_COMMA(String_Input); //Get string after comma
    //Find comma and extract data until next comma

    uint8_t comma_parse_string_length = strlen(comma_parse_string); //Get length of string after comma

    for (uint8_t itr = 0; itr < comma_parse_string_length, itr++)
    {
        if(comma_parse_string[itr] == ",")
        {
            break;
        }
        F_Buffer[itr] = comma_parse_string[itr];
        Extracted_Output[itr] = comma_parse_string[itr];
        

    }

}

void F_U8_to_F(uint8_t Input_val, uint8_t pos, float* Output)
{
    float OutputHold;
    OutputHold = 10*Input_val[pos];
    OutputHold = OutputHold + Input_val[pos+1];
    Output = OutputHold;
}

void F_NMEA_TIME_CONVERT(uint8_t Input_Val, NMEA_GPS gps_info)
{
    F_U8_to_F(timeval, 0, gps_info->GPS_TIME->nmea_hours);
    F_U8_to_F(timeval, 2, gps_info->GPS_TIME->nmea_minutes);
    F_U8_to_F(timeval, 4, gps_info->GPS_TIME->nmea_seconds);
}


void F_GGA_Parse(uint8_t* String_Input, NMEA_GPS gps_info)
{
    //TIME, Lat, N/S, Long, E/W, Fix, Sat#, HDOP, Alt, unit, Geoid, unit,  .........
    strof();
    uint8_t time_value[12], Lat_Val[12]; //obtain from String_Input and to convert into float value
    F_Extract_Until_Comma(String_Input, time_value);
    F_NMEA_TIME_CONVERT(time_value, gps_info)

    //time end
    char* Post_Time_String = strstr(String_Input, ",");
    F_Extract_Until_Comma(Post_Time_String, Lat_Val);
    for(int itr=0; itr<sizeof(Lat_Val),itr++)
    {
        if(Lat_Val[itr] != ".")
        {
            gps_info->GPS_POSITION->latitude = (10 * gps_info->GPS_POSITION->latitude) + Lat_Val[itr];
        }
    }
    F_U8_to_F(Lat_Val,0,gps_info);


}


char F_Extract_GPS_Data(char * raw_string_input, NMEA_GPS* gps_info)
{
    char* GPS_ID_FOUND = strstr(raw_string_input, "$GP");
    uint8_t Comma_Rounds = 0;

    if(NULL == GPS_ID_FOUND)
    {
        return NULL;
    }
    else
    {
        if(strstr(GPS_ID_FOUND, "GGA"))
        {
            gps_info->GPS_TYPE = GGA;
            gps_info->GGA_Flag = YES;
            Comma_Rounds = 14;
        }
        else if(strstr(GPS_ID_FOUND,"RMC"))
        {
            gps_info->GPS_TYPE = RMC;
            gps_info->RMC_Flag = YES;
            Comma_Rounds = 11;
        }
        else if(strstr(GPS_ID_FOUND,"VTG"))
        {
            gps_info->GPS_TYPE = VTG;
            gps_info->VTG_Flag = YES;
            Comma_Rounds = 9;
        }

        if(gps_info->GPS_TYPE == 1)
        {
            F_GGA_Parse(GPS_ID_FOUND, gps_info);
        }


        // if(GPS_ID_FOUND[0])
    }
}

NMEA_PARSE_CODE.h

#include <stdio.h>


typedef enum
{
    NO=0,
    YES=1
} NMEA_ID_GGA;

typedef enum
{
    NO=0,
    YES=1
} NMEA_ID_RMC;

typedef enum
{
    NO=0,
    YES=1
} NMEA_ID_VTG;

typedef enum 
{
    GGA=1,
    RMC,
    VTG

} NMEA_ID;

typedef struct
{
    float nmea_hours;
    float nmea_minutes;
    float nmea_seconds;
    float nmea_thousands;
} NMEA_UTC_TIME;

typedef struct
{
    uint8_t nmea_year;
    uint8_t nmea_month;
    uint8_t nmea_day;
} NMEA_DATE;

typedef struct
{
    float longitude;
    float latitude;
} NMEA_POSITION;

typedef struct
{
    char NORTHSOUTH;
    char EASTWEST;
    float course; //In degrees
    float knot_speed; //In knots
    float meter_speed; //In meters per second
} NMEA_VELOCITY;

typedef struct
{
    NMEA_ID GPS_TYPE;
    NMEA_ID_GGA GGA_Flag;
    NMEA_ID_RMC RMC_Flag;
    NMEA_ID_VTG VTG_Flag;
    NMEA_UTC_TIME GPS_TIME;
    NMEA_DATE GPS_DATE;
    NMEA_POSITION GPS_POSITION;
    NMEA_VELOCITY GPS_VELOCITY;

} NMEA_GPS;

typedef struct
{
    NMEA_ID ID;
    NMEA_UTC_TIME TIME;

} NMEA_GGA;

当前代码虽可编译,但希望优化uint8_t数组转float的效率,避免逐字节循环操作。


优化方案

1. 使用strtof实现无循环转换

strtof是标准C库提供的字符串转float函数,底层经过编译器高度优化(部分平台会用硬件指令加速),效率远高于手动逐字节循环,完全满足你的需求。但需要先修正当前代码中的几个基础错误:

  • 单个字符比较必须用单引号('.'),双引号(".")表示字符串指针,会导致逻辑错误。
  • 提取的字符串必须以\0结尾,否则strtof无法识别为合法C字符串。
  • 修正函数参数的类型不匹配问题(比如F_U8_to_F中输入应为数组而非单个uint8_t)。

2. 关键函数修改示例

(1)修复F_Extract_Until_Comma,生成合法C字符串

// 修改参数为char数组,直接提取ASCII字符串,无需转uint8_t
void F_Extract_Until_Comma(char *String_Input, char* Extracted_Output, size_t max_len)
{
    char* comma_start = strstr(String_Input, ",");
    if (!comma_start) {
        Extracted_Output[0] = '\0';
        return;
    }
    comma_start++; // 跳过当前逗号,指向数据起始位置

    char* next_comma = strstr(comma_start, ",");
    if (!next_comma) {
        // 无下一个逗号,取到字符串末尾
        strncpy(Extracted_Output, comma_start, max_len - 1);
    } else {
        // 截取到下一个逗号前的内容
        size_t data_len = next_comma - comma_start;
        if (data_len >= max_len) data_len = max_len - 1;
        strncpy(Extracted_Output, comma_start, data_len);
    }
    Extracted_Output[max_len - 1] = '\0'; // 强制添加字符串终止符
}

(2)替换循环转换逻辑为strtof

在F_GGA_Parse中直接用strtof转换纬度:

// 修正参数为指针,避免结构体拷贝
void F_GGA_Parse(char* String_Input, NMEA_GPS* gps_info)
{
    char time_value[12] = {0}, Lat_Val[12] = {0};
    F_Extract_Until_Comma(String_Input, time_value, sizeof(time_value));
    
    // 提取并转换纬度
    char* Post_Time_String = strstr(String_Input, ",");
    F_Extract_Until_Comma(Post_Time_String, Lat_Val, sizeof(Lat_Val));
    // 一行代码完成转换,无需循环
    gps_info->GPS_POSITION.latitude = strtof(Lat_Val, NULL);

    // 时间转换也可采用类似逻辑,提取字符串后拆分转换
    // ... 其他解析逻辑
}

3. 其他代码修正建议

  • 修正F_FIND_COMMA的返回值错误:
    char* F_FIND_COMMA(char *String_Input)
    {
        return strstr(String_Input, ",");
    }
    
  • 优化时间转换函数,直接处理字符串:
    void F_NMEA_TIME_CONVERT(char* time_str, NMEA_UTC_TIME* time_info)
    {
        // NMEA时间格式如"123456.789",拆分时、分、秒
        char hour_str[3] = {time_str[0], time_str[1], '\0'};
        char min_str[3] = {time_str[2], time_str[3], '\0'};
        char sec_str[6] = {time_str[4], time_str[5], time_str[6], time_str[7], time_str[8], '\0'};
        
        time_info->nmea_hours = strtof(hour_str, NULL);
        time_info->nmea_minutes = strtof(min_str, NULL);
        time_info->nmea_seconds = strtof(sec_str, NULL);
    }
    

4. 效率说明

strtof作为标准库函数,其实现经过严格的性能优化,在嵌入式平台上的执行效率远高于手动编写的循环转换逻辑,同时代码的可读性和可维护性也会大幅提升,完全适配ESP32的开发场景。

内容的提问来源于stack exchange,提问作者John Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 23:27:02