微信咨询
产品经理为您提供专业解答
手机扫码加我微信
OR
手机号码:
15920323068
简电云 | 充电桩协议云快充1.6充电枪二维码扩展协议的兼容设计与实现
优匠科技 · Sep 22, 2026

云快充 1.6 主要解决充电桩与运营平台之间的通信,二维码显示并没有统一命令。实际项目中,不同设备厂商会在基础协议上增加私有扩展:有的只下发 URL 前缀,由充电桩拼接桩号和枪号;有的按枪下发完整 URL;还有的使用固定长度字段保存二维码内容。

本文根据三组项目材料,对这些扩展进行统一设计。由于材料没有标注公司归属,文中以“扩展 A、B、C”代称。生产实施仍应以设备厂商确认的协议版本和真机联调结果为准。

一、三种扩展有什么区别

对比项

扩展 A:前缀拼接

扩展 B:完整 URL

扩展 C:固定内容

下发/应答命令

0xF0 / 0xF1

0x9C / 0x9B

0x5A / 0x59

下发范围

整桩

单枪

单枪

桩编号

BCD 7 字节

下发数据域不携带

BCD 7 字节

枪号

由格式决定是否拼接

BIN 1 字节

BCD 1 字节

二维码内容

ASCII 变长前缀

ASCII 变长 URL

ASCII 固定 150 字节

长度字段

BIN 1 字节

BIN 2 字节

无

最大长度

200 字节

200 字节

固定 150 字节

成功码

0x00

0x01

0x01

最容易出错的地方有三个:

  • 0xF1 的 0x00 表示成功,而 0x9B、0x59 的 0x01 才表示成功;

  • 扩展 B 的枪号是 BIN,扩展 C 的枪号是 BCD;

  • 长度是编码后的字节数,不是 Java 字符串长度。

二、三种协议的数据域

扩展 A:0xF0/0xF1

0xF0 下发数据域:

字段

类型

长度

说明

桩编号

BCD

7 字节

14 位编号,不足补零

二维码格式

BIN

1 字节

0x00 或 0x01

前缀长度

BIN

1 字节

不超过 200

二维码前缀

ASCII

变长

URL 前缀

两种格式的拼接规则为:

代码语言:JavaScript
自动换行
AI代码解释
格式 0:前缀 + 14 位桩编号
格式 1:前缀 + 14 位桩编号 + 2 位枪编号

例如:

代码语言:JavaScript
自动换行
AI代码解释
前缀:https://q.example.com/?No=桩号:34220001000233格式 0:https://q.example.com/?No=34220001000233格式 1:https://q.example.com/?No=3422000100023301

0xF1 应答由 7 字节 BCD 桩编号和 1 字节结果组成:0x00 成功,0x01 失败。

扩展 B:0x9C/0x9B

0x9C 按枪下发完整 URL:

字段

类型

长度

说明

枪号

BIN

1 字节

充电枪号

URL 长度

BIN

2 字节

ASCII 字节数

URL

ASCII

变长

最大 200 字节

该命令不携带桩编号,目标设备由当前 TCP 会话确定。URL 长度字段的端序在材料中没有明确说明,必须通过厂商黄金报文或真机测试确认,不能根据 Java 默认端序猜测。

0x9B 应答由 7 字节 BCD 桩编号、1 字节 BIN 枪号和 1 字节结果组成:0x00 失败,0x01 成功。

扩展 C:0x5A/0x59

0x5A 下发固定长度二维码内容:

字段

类型

长度

说明

桩编号

BCD

7 字节

14 位编号,不足补零

枪号

BCD

1 字节

0x01 表示 1 号枪

二维码内容

ASCII

固定 150 字节

不足补 0x00

二维码内容可以采用:

代码语言:JavaScript
自动换行
AI代码解释
https://q.example.com/qr?c=<设备编号><枪号>

0x59 应答由桩编号、BCD 枪号和设置结果组成:0x00 失败,0x01 成功。固定内容域必须补足 150 字节,不能省略尾部的 0x00。

三、兼容层设计

三种扩展应复用现有云快充 1.6 基础帧,只负责命令数据域:

代码语言:JavaScript
自动换行
AI代码解释
业务配置
  -> 二维码扩展适配器
  -> 数据域 byte[]
  -> 云快充基础帧编码器
  -> TCP 会话

通过设备型号和固件版本选择适配器,不要只根据品牌名称判断。同一厂商的不同固件也可能使用不同命令。

展开
代码语言:JavaScript
自动换行
AI代码解释
public enum QrProtocol {
    PREFIX_F0_F1,
    URL_9C_9B,
    FIXED_5A_59}

public interface QrExtensionCodec {
    QrProtocol protocol();

    QrCommand encode(QrSetting setting, QrCapability capability);

    QrAck decodeAck(byte frameType, ByteBuffer payload);
}

public record QrCommand(byte frameType, byte[] payload) {}

public record QrAck(
    QrProtocol protocol,
    String deviceNo,
    Integer connectorNo,
    boolean success,
    int rawResult
) {}

能力配置至少包含协议类型、最大长度、长度字段端序、断电是否保存以及登录后是否需要重发。找不到明确配置时,不要轮流尝试三个命令,以免旧固件错误处理未知帧。

四、Java 编码实现

先把 14 位桩号转换为 7 字节 BCD:

展开
代码语言:JavaScript
自动换行
AI代码解释
static byte[] encodeDeviceNo(String value) {
    if (value == null || !value.matches("\\d{1,14}")) {
        throw new IllegalArgumentException("invalid deviceNo");
    }
    String digits = "0".repeat(14 - value.length()) + value;
    byte[] result = new byte[7];
    for (int i = 0; i < result.length; i++) {
        int high = digits.charAt(i * 2) - '0';
        int low = digits.charAt(i * 2 + 1) - '0';
        result[i] = (byte) ((high << 4) | low);
    }
    return result;
}static byte encodeBcd(int value) {
    if (value < 0 || value > 99) {
        throw new IllegalArgumentException("BCD value out of range");
    }
    return (byte) (((value / 10) << 4) | value % 10);
}

三种数据域的核心编码如下:

展开
代码语言:JavaScript
自动换行
AI代码解释
// 0xF0:桩号 + 格式 + 一字节长度 + 前缀static byte[] encodeF0(String deviceNo, int format, byte[] prefix) {
    if (prefix.length > 200) throw new IllegalArgumentException("prefix too long");
    ByteBuffer out = ByteBuffer.allocate(9 + prefix.length);
    return out.put(encodeDeviceNo(deviceNo))
        .put((byte) format)
        .put((byte) prefix.length)
        .put(prefix)
        .array();
}// 0x9C:BIN 枪号 + 两字节长度 + 完整 URLstatic byte[] encode9c(int connector, byte[] url, ByteOrder byteOrder) {
    if (url.length > 200) throw new IllegalArgumentException("url too long");
    ByteBuffer out = ByteBuffer.allocate(3 + url.length).order(byteOrder);
    return out.put((byte) connector)
        .putShort((short) url.length)
        .put(url)
        .array();
}// 0x5A:桩号 + BCD 枪号 + 固定 150 字节内容static byte[] encode5a(String deviceNo, int connector, byte[] content) {
    if (content.length > 150) throw new IllegalArgumentException("content too long");
    ByteBuffer out = ByteBuffer.allocate(158);
    out.put(encodeDeviceNo(deviceNo)).put(encodeBcd(connector)).put(content);
    while (out.hasRemaining()) out.put((byte) 0x00);
    return out.array();
}

调用前还要严格校验 ASCII。不要直接用字符数作为长度,也不要让不可编码字符被静默替换为问号。若 URL 中包含中文,应先完成百分号编码,再检查最终 ASCII 字节数。

应答解析也必须归属具体适配器:

代码语言:JavaScript
自动换行
AI代码解释
boolean success = switch (protocol) {
    case PREFIX_F0_F1 -> rawResult == 0x00;
    case URL_9C_9B, FIXED_5A_59 -> rawResult == 0x01;
};

五、下发与重试

二维码设置应作为异步任务处理:

代码语言:JavaScript
自动换行
AI代码解释
PENDING -> SENT -> ACK_SUCCESS
                -> ACK_FAILED
                -> TIMEOUT

扩展 A 的应答不带枪号,可按设备会话和协议序列号关联;扩展 B、C 还应校验枪号。收到重复应答时保持幂等。

设备断电后是否保存二维码,各材料没有统一结论。平台可以维护“期望配置版本”和“设备最后确认版本”,在设备登录后按能力配置决定是否重发。扩展 A 下发一次,扩展 B、C 按枪下发。

超时不代表设备一定没有写入,因此重试必须可幂等。同一枪存在多条待处理任务时,只发送最新配置;格式错误、长度超限和能力不匹配不应重试。

六、二维码内容设计

二维码尽量使用 HTTPS 短链接:

代码语言:JavaScript
自动换行
AI代码解释
https://q.example.com/c/4Yp7kN2m

不要在 URL 中放入手机号、用户令牌、价格或内部数据库主键。扫码后由服务端把公开 ID 映射到设备和枪口。

协议允许 150 或 200 字节,不代表应该用满。URL 越长,二维码越密,在小尺寸或低分辨率屏幕上的识别率越差。优先使用短域名、短路径和服务端映射。

协议日志记录设备号、枪号、命令码、序列号、内容哈希、长度和结果码即可,默认不要打印完整 URL。

七、测试重点

上线前至少覆盖以下用例:

用例

预期结果

14 位桩号

编码为 7 字节 BCD

扩展 B 的 12 号枪

BIN 0x0C

扩展 C 的 12 号枪

BCD 0x12

URL 为 200 字节

允许

URL 为 201 字节

拒绝

扩展 C 内容不足 150 字节

补 0x00

0xF1 返回 00

成功

0x9B、0x59 返回 01

成功

应答长度不正确

拒绝解析

每种扩展还需要一组厂商确认的完整十六进制报文,逐字节验证编码结果。真机测试不能只看应答成功,还要确认二维码是否显示在正确枪口、扫码内容是否完全一致,以及断电重启后是否需要重新下发。

总结

云快充二维码扩展的难点不在生成二维码,而在兼容协议差异:0xF0/0xF1 下发前缀,0x9C/0x9B 按枪下发变长 URL,0x5A/0x59 下发固定 150 字节内容。三者的枪号编码、长度字段和成功码并不统一。

实现时应保留统一的云快充基础帧,为每种扩展建立独立适配器,并根据设备型号和固件能力选择协议。端序、BCD 补零方向和断电保存策略等材料未明确的内容,必须通过厂商确认和真机测试后再上线。

本文中的域名、桩号和二维码内容均为演示数据,命令定义来自项目提供的三份扩展材料,不代表公开行业标准。

共享充电 选择优匠
共享充电整体解决方案服务商
在线咨询
微信咨询