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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 15:39:23