PHP实现OTA固件发送:需以十六进制格式而非字符串写入?
问题
我正尝试通过空中(OTA)向设备发送固件,在其他设备上用以下代码通过fseek实现了分片传输:
$handle = fopen($uniquedetails['send_firmware'], "r"); fseek($handle, $uniquedetails['ota_bytes']); // 读取文件到最大字节限制 $data = bin2hex(fread ($handle , $sendbytes ));
之后通过fwrite($sock, hex2bin($hextosend));写入socket,设备能正常接收。本质是读取固件分片,连同偏移量(ota_bytes)发给设备拼接。
但当前设备要求的数据格式为:992, <OTA packet address offset (4 bytes)><OTA packet length (2 bytes)>,我的实现代码如下:
$cmd = '992'; //Send OTA Data $header = '$$'; $offset = str_pad(dechex($uniquedetails['ota_bytes']), 8, "0", STR_PAD_LEFT); $data = "," . $input_array[1] . "," . $cmd . "," . $offset . $datalength . $data; $length = strlen($data); // 计算随机包标志 $flag = randLetter(); // 构建用于校验和的数据包 $data = $header . $flag . $length . $data; $checksum = calcCheckSum($data); // 构建最终发送的数据包 $data = $data . $checksum . "\r\n"; fwrite($sock, $data);
设备能识别为OTA数据包但报错:
== GPRS Receive: 301 $$R289,861157040009546,992,0000000000808a664a7a0b2ed05849e0d22aa9d6b8701530a27a7dae50d8d1f0623a126232d43acada6a7a0a1aaaba4a5aeac97ea0a805e81aa2ba4a5aeacd86e82051b86a72316e10989be4766e5d7afc5487b022fae9e64880f3fc4ef6677b362e1d3abc144770e2ba2bcfaab271ae50d8db24b6ae9dba3c94c7a6aab271ae50d8db24b6aeE0 == OTA_RxIndex: 808464432 0 == OTA_RxDataLen: 12336 == OTA_RxData Err
联系厂商后得知,需要以十六进制格式而非十六进制字符串发送OTA数据(bin2hex生成的是字符串),请问PHP中如何实现?
解决方案
核心是直接传输原始二进制数据,而非将二进制转成十六进制字符串,同时把偏移量和长度按要求打包成指定字节数的二进制流。
1. 读取固件的二进制分片
去掉bin2hex转换,直接保留fread返回的二进制数据:
$handle = fopen($uniquedetails['send_firmware'], "r"); fseek($handle, $uniquedetails['ota_bytes']); // 直接读取二进制数据,不转十六进制字符串 $firmware_bin = fread($handle, $sendbytes); fclose($handle);
2. 将偏移量和长度打包为指定字节的二进制
设备要求偏移量为4字节、长度为2字节,使用PHP的pack函数处理:
// 偏移量:4字节大端模式(若设备用小端字节序则替换为'V') $offset_bin = pack('N', $uniquedetails['ota_bytes']); // 数据包长度:2字节大端模式(若设备用小端字节序则替换为'v') $length_bin = pack('n', strlen($firmware_bin));
注:
pack格式符说明:N对应32位无符号大端,n对应16位无符号大端;需和设备的字节序要求匹配,否则会导致偏移量或长度解析错误。
3. 拼接完整数据包
按照设备要求的格式,区分文本部分和二进制部分拼接:
$cmd = '992'; $header = '$$'; $flag = randLetter(); // 构建文本前缀部分 $text_part = "," . $input_array[1] . "," . $cmd . ","; // 计算数据包总长度:文本部分长度 + 偏移量(4) + 长度(2) + 固件二进制长度 $total_length = strlen($text_part) + 4 + 2 + strlen($firmware_bin); // 构建校验和计算的基础数据 $checksum_base = $header . $flag . $total_length . $text_part; // 拼接二进制部分:偏移量 + 长度 + 固件数据 $binary_part = $offset_bin . $length_bin . $firmware_bin; // 计算校验和(需确保calcCheckSum能处理二进制数据) $checksum = calcCheckSum($checksum_base . $binary_part); // 最终数据包:基础数据 + 二进制部分 + 校验和 + 换行符 $final_packet = $checksum_base . $binary_part . $checksum . "\r\n";
4. 发送数据包
直接用fwrite发送完整数据包,无需额外转换:
fwrite($sock, $final_packet);
关键注意点
- 之前的错误在于将固件二进制转成十六进制字符串发送,设备需要的是原始二进制字节流,因此要移除
bin2hex和hex2bin的转换步骤。 - 偏移量和长度不能用十六进制字符串(如
00000000),必须转换为对应字节数的二进制流,pack是PHP处理这类二进制打包的核心工具。 - 必须确认设备的字节序(大端/小端),格式符使用错误会导致设备解析偏移量或长度异常,进而触发OTA_RxData Err。
内容的提问来源于stack exchange,提问作者Jeff M
相关产品推荐
相关产品推荐

