← 返回二进制组件参考 · 二进制数据专题 · 全部使用指导
二进制组件使用指导
二进制组件帮你把「帧头、命令、温度、校验」这些内容和一串字节互相转换。它不收发数据, 收发交给蓝牙、Socket、WebSocket、MQTT 组件(见二进制数据专题)。 所有方法直接返回结果,不需要事件。
前四个案例不需要任何硬件,放一个按钮、一个标签就能在手机上看到结果,和文中写的对一下就知道做对没有。
先懂三件事
1. 字节列表:一串字节就是由 0~255 的数字组成的普通列表,[165, 1, 255]。
列表积木都能用:求长度、取第几个(从 1 数起)、追加。
2. 十六进制:协议文档里的 A5 01 FF 和 [165, 1, 255] 是同一串字节,两位十六进制是一个字节。
「十六进制转字节」「字节转十六进制」负责互转,调试时把收到的内容显示成十六进制最直观。
3. 多字节数字的顺序和符号:一个字节只能放 0~255,温度 23.5 这种要乘 10 变成 235 再放进 2 个字节。
| 数字 | 高位优先(默认) | 低位优先 | 说明 |
|---|---|---|---|
| 1500 | 05 DC |
DC 05 |
协议文档会写明「高字节在前」还是「低字节在前」 |
| -20 | FF EC |
EC FF |
负数按补码;读回时「有符号」要选真,否则得到 65516 |
单片机直接发内存里的数字(ESP32、STM32 常见)多是低字节在前,把高位优先(HighByteFirst)设为假。
快速上手:十六进制与字节互转
拖一个二进制组件、一个文本输入框、一个按钮和一个标签。在输入框里敲十六进制,点按钮看它有几个字节、第 1 个字节的十进制是多少:
when 按钮1.Click() {
var 字节 = 二进制1.FromHex(文本输入框1.Text)
if (length(字节) > 0) {
标签1.Text = join(length(字节), " 个字节,第 1 个是 ", 字节[1], ",规整后 ", 二进制1.ToHex(字节))
} else {
标签1.Text = "请输入至少一个有效字节"
}
}
跑起来看:输入 a5,01,ff,标签显示「3 个字节,第 1 个是 165,规整后 A5 01 FF」。输入 A5 1(单数位)会弹出错误提示「十六进制位数是单数」。
案例一:拼一帧并加上校验
协议:帧头 A5 + 命令 03 + 目标温度(2 字节,乘 10,有符号)+ CRC16-MODBUS 校验。设定温度 -2.5 ℃:
when 按钮1.Click() {
var 帧 = 二进制1.Build([165, 3, 二进制1.NumberToBytes(-25, 2)])
标签1.Text = 二进制1.ToHex(二进制1.AppendChecksum(帧, "CRC16-MODBUS"))
}
跑起来看:标签显示 A5 03 FF E7 D3 52。FF E7 是 -25 的补码,最后两个字节是 CRC(Modbus 规定低字节在前,已自动处理)。
「拼接字节」一块积木放下整帧的各部分:数字是一个字节,列表整段接上,文本按编码转字节。 不用它的话要连续「追加列表」好几次,还容易漏掉一个字节。
案例二:解析设备回复的一帧
设备回复:A5 83 + 温度(2 字节,乘 10,有符号)+ 湿度(1 字节)+ 状态(1 字节)+ 累加校验。
先把回复写死在十六进制文本里练习解析,接到真设备时把十六进制文本换成收到的字节列表即可:
when 按钮1.Click() {
var 帧 = 二进制1.FromHex("A5 83 00 EB 2D 05 45")
if (二进制1.VerifyChecksum(帧, "SUM8")) {
var 温度 = 二进制1.BytesToNumber(帧, 3, 2, true) / 10
var 湿度 = 帧[5]
var 报警 = 二进制1.GetBit(帧[6], 0)
标签1.Text = join("温度 ", 温度, " ℃,湿度 ", 湿度, " %,报警 ", 报警)
} else {
标签1.Text = "校验不对,丢弃这一帧"
}
}
跑起来看:标签显示「温度 23.5 ℃,湿度 45 %,报警 true」。把十六进制最后的 45 改成 46 再点,显示「校验不对,丢弃这一帧」。
帧[5] 就是列表的「选择列表项」,第 5 个字节;状态字节 05 的二进制是 0000 0101,第 0 位是 1,所以报警为真。
案例三:小数与 4 字节 float 互转
传感器常把小数按 IEEE754 单精度(4 字节)发送。把 23.5 转成字节再转回来:
when 按钮1.Click() {
var 大端 = 二进制1.FloatToBytes(23.5, 4)
二进制1.HighByteFirst = false
var 小端 = 二进制1.FloatToBytes(23.5, 4)
var 还原 = 二进制1.BytesToFloat(小端, 1, 4)
二进制1.HighByteFirst = true
标签1.Text = join("大端 ", 二进制1.ToHex(大端), " / 小端 ", 二进制1.ToHex(小端), " / 还原 ", 还原)
}
跑起来看:标签显示「大端 41 BC 00 00 / 小端 00 00 BC 41 / 还原 23.5」。 和单片机对不上时,先看它发来的是哪一种顺序。
案例四:文本命令与中文
有的设备协议里夹着文本命令,比如 A5 + "LED=1" + 换行;串口屏显示中文多用 GBK 编码:
when 按钮1.Click() {
var 命令 = 二进制1.Build([165, "LED=1", 10])
二进制1.TextEncoding = "GBK"
var 中文 = 二进制1.FromText("你好")
二进制1.TextEncoding = "UTF-8"
标签1.Text = join(二进制1.ToHex(命令), " / ", 二进制1.ToHex(中文))
}
跑起来看:标签显示「A5 4C 45 44 3D 31 0A / C4 E3 BA C3」。同样的「你好」按 UTF-8 是 6 个字节 E4 BD A0 E5 A5 BD,
所以设备显示乱码时,先确认两边用的是同一种编码。
案例五:和电脑上的网络调试助手联调
没有设备也能把收发跑通:电脑上的网络调试助手(NetAssist、SSCOM 等,网上免费下载)开一个 TCP 服务器扮演设备。
- 电脑和手机连同一个 Wi-Fi,查到电脑的局域网 IP(如
192.168.1.10); - 调试助手选「TCP Server」,端口
8888,勾选「十六进制显示」「十六进制发送」,点「打开」; - App 里拖一个 Socket客户端,服务器地址填电脑 IP、端口
8888,再放一个按钮、两个标签:
when Screen1.Initialize() {
Socket客户端1.Connect()
}
when 按钮1.Click() {
var 帧 = 二进制1.AppendChecksum(二进制1.Build([165, 1, 二进制1.NumberToBytes(1500, 2)]), "SUM8")
Socket客户端1.SendBytes(帧)
标签1.Text = join("已发送 ", 二进制1.ToHex(帧))
}
when Socket客户端1.BytesReceived(字节列表) {
标签2.Text = join("收到 ", 二进制1.ToHex(字节列表))
}
跑起来看:
- 点按钮,调试助手窗口里出现
A5 01 05 DC 87,和标签1 显示的一致; - 在调试助手的发送框里填
A5 83 00 EB 2D 05 45点发送,标签2 显示「收到 A5 83 00 EB 2D 05 45」。 把案例二的解析逻辑搬进「收到字节时」,就成了一个完整的上位机。
调试助手一次发出的内容,App 在局域网里通常一次收到。真实设备连续发送时可能拆开或粘在一起, 可靠的写法见专题页的拆帧。
案例六:收到的字节存成文件,再读回来分包发送
蓝牙、串口收到的整段数据(日志、图片、固件)要留底,或者要把一个文件分成小包发给单片机,用「保存字节到文件」和「读取文件字节」。
下面先把一段字节存成 log.bin,再读回来,按每包 4 个字节切开:
when 按钮1.Click() {
var 数据 = 二进制1.FromHex("A5 01 05 DC 87 00 FF 10 20 30")
var 路径 = 二进制1.SaveBytesToFile(数据, "log.bin")
var 读回 = 二进制1.ReadFileBytes("log.bin")
var 分包 = ""
for 起点 = 1 to length(读回) step 4 {
// 最后一包可能不足 4 个:截取长度不能超过剩下的字节数,否则「截取字节」会报错
分包 = join(分包, "[", 二进制1.ToHex(二进制1.SubBytes(读回, 起点, min(4, length(读回) - 起点 + 1))), "]")
}
标签1.Text = join("读回 ", length(读回), " 字节,和写入相同:", 读回 == 数据)
标签2.Text = join("分包:", 分包)
标签3.Text = 路径
}
跑起来看:标签1 显示「读回 10 字节,和写入相同:true」,标签2 显示「分包:[A5 01 05 DC][87 00 FF 10][20 30]」——三包分别是 4、4、2 个字节,最后一包按剩下的字节数截取;标签3 显示文件的完整路径,以 /log.bin 结尾。
这个路径可以直接交给 Web 客户端的「执行POST表单文件请求」上传,或交给分享组件发出去。
要点:
- 只写文件名时,文件存在本应用自己的目录,不需要存储权限,卸载应用时一起删除;同名文件会被覆盖;
- 「读取文件字节」只写文件名时,先找本应用目录里存过的文件,没有再找项目素材,所以也能直接读打包进项目的
.bin素材; - 读写都限 1MB:字节列表里每个字节是一个列表项,太大的文件会把内存撑满。分包发送时每包大小按设备的接收能力定(低功耗蓝牙常见 20 字节)。
常见问题
| 现象 | 原因 | 解决 |
|---|---|---|
| 温度读成 65516 这种大数 | 负数按无符号读了 | 「字节转整数」的「有符号」选真 |
| 数字差了很多倍、对不上 | 字节顺序和设备相反 | 改「高位优先」;先用「字节转十六进制」看原始字节 |
| 校验总是不对 | 算法、初值或覆盖范围和设备不一致 | 用参考页 123456789 的检验值核对设备端;确认校验包含哪几个字节 |
| 报错「超出一个字节的范围」 | 列表里放了大于 255 的数 | 多字节数字先用「整数转字节」 |
| 拼出来多了几个字节 | 文本框的 "12" 放在顶层按文本转成了两个字节 |
要当一个字节时放进列表,或先做一次数学运算 |
| 中文乱码 | 编码不一致 | 「文本编码」和设备保持一致(国内设备多为 GBK) |
| 「读取文件字节」报文件超过 1MB | 字节列表不适合太大的文件 | 大文件交给文件类组件或 Web 上传,不要整个转成字节列表 |
| 只写文件名读到的不是刚存的文件 | 应用目录里没有这个名字,读到了同名素材 | 用「保存字节到文件」返回的完整路径去读 |
平台说明
安卓与鸿蒙行为一致,结果逐字节相同,本页积木两边写法相同。
完整 .aia 源码与操作指导
不需要网络或硬件即可解析真实硬件协议格式。初始小端、UTF-8、空格分隔;默认帧是 Modbus 读取10个保持寄存器请求。字节下标从1开始,位下标从0开始。要连接硬件,将拼接结果传给通信组件的字节发送入口。
导入 BinaryToolsAllFeatures.aia,连接最新伴侣,或编译独立安装包。真机/伴侣运行尚未实测。
| API / 功能 | 操作入口 | 预期结果 |
|---|---|---|
| HighByteFirst / TextEncoding / HexSeparator 读取与设置 | 编码选择、大小端开关、分隔符输入、应用设置 | 属性区读取实际值;分隔符可空,选择 GBK 再试中文 |
| FromHex / ToHex / Checksum / AppendChecksum / VerifyChecksum | 默认输入点校验 | MODBUS 完整帧为 01 03 00 00 00 0A C5 CD;校验值 52677;核对为真 |
| VerifyChecksum / SubBytes / Build | 点损坏校验 | 最后字节变为 CE,校验为假,原帧为真;原始列表不变 |
| FromText / ToText / BytesToBase64 / Base64ToBytes | 点文本转换 | 字节、Base64 与还原文字同时展示;ASCII 使用英文 |
| NumberToBytes / BytesToNumber | 1500、小端点数值 | DC 05 还原 1500;负数可观察有符号和无符号差异 |
| FloatToBytes / BytesToFloat | 23.5、小端点数值 | 00 00 BC 41 还原 23.5;其他小数允许浮点舍入 |
| Build / SubBytes / IndexOf | 点拼接 | 小端为 A5 01 02 DC 05;负载起点 4;不存在返回 0 |
| SetBit / GetBit | 点位操作 | 第3位设后为 8/真,清后为 0/假;位编号从 0 开始 |
| SaveBytesToFile / ReadFileBytes | 先点校验,再点存文件 | 路径以 /frame.bin 结尾;读回 01 03 00 00 00 0A C5 CD;与原帧相同为真 |
| 空、越界、非法输入 | 空十六进制、非数字或点非法 | 有明确提示,修正后可继续 |
所有返回值均用于界面反馈,错误由底部错误区展示。设计器采用明确非默认配置,初始化与界面同步。API 覆盖检查按最终 AIA 检查方法、事件、属性读取与设置及设计器配置,不能替代真实设备验证。
二进制硬件报文
导入方法:项目 → 导入项目(.aia)→ 选择下载的 .aia 源码文件。 不需要网络或硬件即可解析真实硬件协议格式。初始小端、UTF-8、空格分隔;默认帧是 Modbus 读取10个保持寄存器请求。字节下标从1开始,位下标从0开始。要连接硬件,将拼接结果传给通信组件的字节发送入口。 覆盖检查 29/29。首次载入及保存重开均0个错误,真实后台导入、安装包编译通过;真机/伴侣/硬件联调尚未实测。全部属性读写、方法与事件的操作入口和预期结果见组件指导。
用 Python 独立核对默认报文
电脑安装 Python 3 后,把下面代码保存为 check_binary.py,执行 python3 check_binary.py。不需要第三方库或连接设备;输出可与手机案例逐项对比。
import struct
import base64
# Modbus RTU 的 CRC16 校验按低字节在前追加,与数值字节序开关无关。
def modbus_crc(data):
value = 0xFFFF
for byte in data:
value ^= byte
for _ in range(8):
value = (value >> 1) ^ (0xA001 if value & 1 else 0)
return value
request = bytes.fromhex("01 03 00 00 00 0A")
crc = modbus_crc(request)
frame = request + struct.pack("<H", crc)
assert crc == 52677
assert frame.hex(" ").upper() == "01 03 00 00 00 0A C5 CD"
assert struct.pack("<h", 1500).hex(" ").upper() == "DC 05"
assert struct.pack("<f", 23.5).hex(" ").upper() == "00 00 BC 41"
text_bytes = "设备就绪".encode("utf-8")
assert base64.b64decode(base64.b64encode(text_bytes)).decode("utf-8") == "设备就绪"
print("完整帧:", frame.hex(" ").upper())
print("校验值:", crc)
print("整数1500:", struct.pack("<h", 1500).hex(" ").upper())
print("浮点23.5:", struct.pack("<f", 23.5).hex(" ").upper())
print("文本Base64:", base64.b64encode(text_bytes).decode("ascii"))
手机字节列表下标从1开始,Python字节索引从0开始。例如手机从第4字节截取2字节,对应 Python frame[3:5]。发送 Modbus 帧前,还需按设备手册配置站号、寄存器范围和通信连接;本页核对编码与校验,不声称连接了实际设备。
扫码添加客服咨询