ESP8266解析RGB十六进制字符串时B值非零崩溃问题求助
ESP8266解析RGB十六进制码时B值非零崩溃的修复
问题描述
开发ESP8266项目时,设备接收格式如$3#FF00FF的RGB十六进制码控制LED灯带。当前代码在B值非零时(如$3#FF00FF、$3#FF0012)会崩溃,B值为0时(如$3#FF0000)运行正常,且崩溃时自动看门狗未触发,推测是内存相关问题。
核心问题分析
崩溃根源在于b1 = strtol(data.substring(7, 9).c_str(), NULL, 16);这行代码:
- Arduino的
String.substring()会返回一个临时String对象,调用.c_str()后得到的是该临时对象内部的字符指针。 - 当这条语句执行完毕,临时String对象会被立即销毁,其内部的字符数组内存也会被释放,此时
strtol可能还在使用这个已经失效的野指针。 - 当B值为0时,释放的内存恰好还保留着
00的残留数据,strtol能解析出正确结果,不会触发明显错误;但B值非零时,后续内存操作会覆盖这段区域,导致strtol访问无效内存,引发崩溃。
另外,代码中dataLen变量未定义,需要确保它是data.length()的正确取值,否则可能导致错误进入解析分支。
修复方案
方案1:延长临时字符串生命周期
将substring的结果存入临时String变量,确保在strtol解析完成前内存不被释放:
void loop() { String data = // 从服务器获取数据,格式如"$3#FF0000" int dataLen = data.length(); // 补充dataLen的定义 if (data.charAt(0) == '$' && dataLen == 9) { char ledStrip = data.charAt(1); char buffer[10]; data.toCharArray(buffer, sizeof(buffer)); r1 = strtol(data.substring(3, 5).c_str(), NULL, 16); g1 = strtol(data.substring(5, 7).c_str(), NULL, 16); // 先将子串存入临时变量,避免野指针 String bSub = data.substring(7, 9); b1 = strtol(bSub.c_str(), NULL, 16); Serial.print("Received RGB values for Strip 1 & 2: R="); Serial.print(r1); Serial.print(", G="); Serial.print(g1); Serial.print(", B="); Serial.println(b1); } }
方案2:直接操作字符计算数值(更高效)
跳过substring和strtol,直接通过字符转换计算RGB值,避免临时对象和内存分配:
// 新增十六进制字符转整数的辅助函数 int hexCharToInt(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'A' && c <= 'F') return 10 + (c - 'A'); if (c >= 'a' && c <= 'f') return 10 + (c - 'a'); return 0; // 非法字符返回0 } void loop() { String data = // 从服务器获取数据,格式如"$3#FF0000" int dataLen = data.length(); if (data.charAt(0) == '$' && dataLen == 9) { char ledStrip = data.charAt(1); // 直接通过字符计算RGB值 r1 = (hexCharToInt(data.charAt(3)) << 4) | hexCharToInt(data.charAt(4)); g1 = (hexCharToInt(data.charAt(5)) << 4) | hexCharToInt(data.charAt(6)); b1 = (hexCharToInt(data.charAt(7)) << 4) | hexCharToInt(data.charAt(8)); Serial.print("Received RGB values for Strip 1 & 2: R="); Serial.print(r1); Serial.print(", G="); Serial.print(g1); Serial.print(", B="); Serial.println(b1); } }
方案3:利用已有的char数组解析
代码中已经将data转成了char数组buffer,直接用该数组解析更安全:
void loop() { String data = // 从服务器获取数据,格式如"$3#FF0000" int dataLen = data.length(); if (data.charAt(0) == '$' && dataLen == 9) { char ledStrip = data.charAt(1); char buffer[10]; data.toCharArray(buffer, sizeof(buffer)); // 构造两位的十六进制字符串解析 char rBuf[3] = {buffer[3], buffer[4], '\0'}; r1 = strtol(rBuf, NULL, 16); char gBuf[3] = {buffer[5], buffer[6], '\0'}; g1 = strtol(gBuf, NULL, 16); char bBuf[3] = {buffer[7], buffer[8], '\0'}; b1 = strtol(bBuf, NULL, 16); Serial.print("Received RGB values for Strip 1 & 2: R="); Serial.print(r1); Serial.print(", G="); Serial.print(g1); Serial.print(", B="); Serial.println(b1); } }
内容的提问来源于stack exchange,提问作者kazar4
相关产品推荐
相关产品推荐

