如何向ESP8266/Arduino通过UART串口正确传输包含0值的JPEG文件
问题根因
你遇到的问题本质是把二进制JPEG数据当做C风格字符串处理导致的截断:C字符串默认以\0(即0值)作为终止标识,因此只要数据中出现0,所有字符串处理逻辑都会自动截断该位置之后的内容。另外你当前的收发逻辑设计也存在漏洞,不适合二进制数据传输场景。
发送端修改方案
- 完全删除替换0值为ASCII字符'0'的逻辑,原有代码中
sprintf(Storage_ReadWrite_Buffer[i],"%c", Storage_ReadWrite_Buffer[i]);是错误写法,sprintf第一个参数需要传入字符数组地址,传入单字节字符会导致内存访问越界,必须删除。 - 不要使用
\0作为传输结束标识,二进制内容中本身就可能存在\0,会导致提前截断。 - 推荐改用「起始标记 + 数据长度 + 原始二进制数据」的帧结构,避免特殊字符冲突,修改后的参考代码如下:
// 帧结构:1字节起始符< + 2字节数据长度 + N字节原始数据 uint16_t data_len = 244; RS485_Send_CHAN_A("<",1); RS485_Send_CHAN_A((uint8_t*)&data_len, 2); // 发送数据长度 RS485_Send_CHAN_A(Storage_ReadWrite_Buffer, data_len); // 直接传输原始二进制数据,不需要做任何转换
修改后不会篡改原始JPEG的任何字节,也不会出现0值截断问题。
接收端修改方案
- 不要对接收缓冲区做字符串终止符赋值,也不要用
Serial.println()这类字符串处理函数操作二进制数据,这类函数遇到0值会自动截断。 - 配合发送端的帧结构修改接收逻辑,根据指定长度接收数据,完全不需要判断内容中的特殊字符,参考代码如下:
const int numChars = 6000; uint8_t receivedChars[numChars]; boolean newData = false; int expectedLen = 0; void recvWithLengthMarker() { static boolean recvInProgress = false; static int ndx = 0; static int lenReceived = 0; char startMarker = '<'; char rc; while (Serial.available() > 0 && newData == false) { rc = Serial.read(); if (!recvInProgress) { if (rc == startMarker) { recvInProgress = true; ndx = 0; lenReceived = 0; expectedLen = 0; memset(receivedChars, 0, numChars); } } else { // 先接收2字节的长度信息 if (lenReceived < 2) { ((uint8_t*)&expectedLen)[lenReceived] = rc; lenReceived++; } else { // 按长度接收payload receivedChars[ndx++] = rc; if (ndx >= expectedLen) { // 接收完成 recvInProgress = false; newData = true; } } } } } void showNewData() { if (newData == true) { // 不要用Serial.println打印二进制数据,需要逐字节处理或者写入文件/上传 // 示例:逐字节打印十六进制 Serial.print("Received "); Serial.print(expectedLen); Serial.println(" bytes:"); for (int i=0; i<expectedLen; i++) { Serial.print(receivedChars[i], HEX); Serial.print(" "); } Serial.println(); newData = false; publish_image(); } } void loop() { recvWithLengthMarker(); showNewData(); }
修改完成后收发的JPEG数据和原始文件完全一致,不会出现字节替换或者截断的问题。
内容的提问来源于stack exchange,提问作者Hicham
相关产品推荐
相关产品推荐

