软件升级协议

前言

本协议规定设备和天翼物联网平台(AIoT)(以下简称“平台”)之间的应用层升级协议(简称PCP协议),用于实现设备的升级。

通讯方式

1、PCP协议运行在应用层,底层可以是LWM2M/COAP/MQTT或者其他非流式协议

2、由于PCP协议消息没有使用单独的端口号,并且不依赖于底层协议,为了和设备业务消息区分,PCP协议固定以0XFFFE作为起始字节。因此要求设备的业务消息的前两个字节不能是0XFFFE,更多细节参考附录6.4

3、本协议消息采用一问一答模式,所有请求消息都有一个响应消息

数据类型

数据类型描述和要求
BYTE无符号一字节整数
WORD无符号二字节整数
DWORD无符号四字节整数
BYTE[n]n字节的十六进制数
STRING字符串


 Note

本协议采用网络序来传输WORD和DWORD

消息结构

字段名字段类型描述和要求
起始标识WORD起始标识,固定为0XFFFE
版本号BYTE高四位预留;低四位表示协议版本号,当前为1
消息码BYTE标识一个请求消息,应答消息的消息码和请求消息相同
校验码WORD从起始标识到数据区的最后一个字节的CRC16校验值,计算前先把校验码字段置为0,计算完成后把结果写到校验码字段。
数据区长度WORD数据区的长度
数据区BYTE[n]参见具体指令的定义

消息码定义:

消息码描述
0-18平台预留
19查询设备版本
20新版本通知
21请求升级包
22上报升级包下载状态
23执行升级
24上报升级结果
25-127平台预留

消息定义

i.查询设备版本

上报任意数据后触发,设备收到 NB查询设备现在版本号的消息码流方向:平台->设备
查询设备版本,例如此时设备端收到此消息:+NNMI:8,FFFE01134C9A0000

按照协议中的规定字段解读:FFFE为起始标识,此字段固定;01为版本号,当前为置为1;13为消息码,十进制即为19,协议中规定了消息码描述;4C9A即为校验码;0000为数据区长度;当设备接收到平台的查询软件版本后,根据协议此时设备上报一条当前版本号消息。方向:设备->平台。

AT+NMGS=25,FFFE0113164700110056322E31300000000000000000000000

协议中规定上报结果码以及当前版本号。按照例子,此时设备端应该回复的消息如下:

FFFE(起始标识)01(版本号)13(消息码)1647(校验码)0011(数据区长度)00(结果码) 56322E31300000000000000000000000(当前版本号, V2.10,规定长度为
byte[16])

字段数据类型描述及要求



无数据区

响应消息:

字段数据类型描述及要求
结果码BYTE0X00处理成功
当前版本号BYTE[16]当前版本号,由ASCII字符组成,位数不足时,后补“0X00”。

正常处理:平台根据版本号判断设备是否需要升级,如果需要,下发请求升级

异常处理:如果响应超时,平台中止升级任务

ii.新版本通知

平台对设备端发起新版本通知,方向:平台->设备。

假设此时在平台上上传的软件包,json 文件中定义的版本号为
V2.16,设备端收到消息码流FFFE011491B0001656322E3136000000000000000000000001F400813836,前面字段的解析方法与之前所介绍一致,01F4
为分片包大小(500 字节),0081 为分片总数, 3836 转换成字符串即为 json 文件中的
versioncheckcode 字段值。设备按照写中规定,返回允许升级:AT+NMGS=
9,FFFE0114D768000100,其中末尾的 0X00 为结果码,表示允许升级。

字段数据类型描述及要求
目的版本号BYTE[16]目的版本号,由ASCII字符组成,位数不足时,后补“0X00”。
升级包分片大小WORD每个分片的大小
升级包分片总数WORD升级包分片总数
升级包校验码WORD升级包校验码。用户上传升级包时,需要在升级包描述文件里填写校验码

应答消息:

字段数据类型描述及要求
结果码BYTE0X00 允许升级0X01设备使用中0X02信号质量差0X03已经是最新版本0X04电量不足0X05剩余空间不足0X09内存不足0X7F内部异常

正常处理:如果设备不允许升级,平台中止升级任务

异常处理:如果响应超时,而且没收到请求升级包消息,平台中止升级任务

iii.请求升级包

设备向平台一次请求升级的分片包,方向:设备->平台。

此时设备请求的第一个分片包,起始位0,发送码流:

FFFE0115A989001256322E313600000000000000000000000000,起始标识“FFFE”,版本号“01”,消息码为“15”对应10进制为21即为请求升级包,“A989”为CRC校验码,“0012”数据区长度,对应后面所加的数据为18个字节即为,其中的前16个字节为版本号V2.16,最后两字节“0000”为设备请求的第一个分片。

平台收到设备第一个分片包的请求后,回复的响应消息中携带结果吗、分片序号、分片数据,此例子中的第一个分片数据大小应该是byte[500]。则此时平台回复的消息样式应该为“FFFE0115xxxx01F7000000byte[500]”。码流中的xxxx为crc校验码,01F7为503字节的数据区长度,00是处理成功的结果码,0000是第一个分片包序号,byte[500]为具体的分片包数据。之后的升级包请求按照升级包序号进一步操作即可。

字段数据类型描述及要求
目的版本号BYTE[16]目的版本号,由ASCII字符组成,位数不足时,后补“0X00”。
分片序号WORD表示请求获取的分片序号,从0开始计算。设备可以保存已经收到的分片,下次直接从缺失的分片开始请求,达到断点续传的效果。

响应消息:

字段数据类型描述及要求
结果码BYTE0X00处理成功0X80升级任务不存在0X81指定的分片不存在
分片序号WORD表示返回的分片序号
分片数据BYTE[n]分片的内容,n为实际的分片大小。如果错误码不为0,则不带此字段

iv.上报升级包下载状态

当设备请求完分片包后,按照协议设备需上传一条消息码流查询设备的升级包下载状态,让平台知道升级包是否已经都下载完成。方向:设备->平台。

按照正确的升级流程,升级包下载后,设备在数据区回复“00”通知平台升级包下载成功,此时发的消息码流为:“FFFE0116850e000100”。平台也也回复相应的数据码流通知设备处理成功,之后步骤v中设备会进行升级。

字段数据类型描述及要求
下载状态BYTE0X00下载成功0X05剩余空间不足0X06下载超时0X07升级包校验失败0X08升级包类型不支持

响应消息:

字段数据类型描述及要求
结果码BYTE0X00处理成功0X80升级任务不存在

v.执行升级

设备上报完升级包下载状态后,平台通知设备执行升级。方向:平台->设备。

下发码流“FFFE0117CF900000”通知设备执行升级操作。设备收到后回复“FFFE0117B725000100”。

字段数据类型描述及要求



无数据区

响应消息:

字段数据类型描述及要求
结果码BYTE0X00处理成功0X01设备使用中0X04电量不足0X05剩余空间不足0X09内存不足

vi.上报升级结果

设备完成升级后,上报升级的结果和当前版本号告知平台,平台上更新设备当前所使用的软件版本。方向:设备->平台

按照此用例中,设备升级的目的版本号为V2.16,协议中规定了消息区中升级完后当前版本号的数据类型与长度。方向:设备->平台设备上报消息码流为:“FFFE0118AD2600110056322E31360000000000000000000000”告知平台升级成功,当前版本号已经更新为
V2.16。平台收到后会回复一个应答消息,数据区为空,回复码流:“FFFE01182AD50000”

字段数据类型描述及要求
结果码BYTE0X00升级成功0X01设备使用中0X04电量不足0X05剩余空间不足0X09内存不足0X0A安装升级包失败0X7F内部异常
当前版本号BYTE[16]设备当前版本号

响应消息:

字段数据类型描述及要求
结果码BYTE0X00处理成功0X80升级任务不存在

附录

i.结果码定义

结果码描述备注
0X00处理成功
0X01设备使用中
0X02信号质量差
0X03已经是最新版本
0X04电量不足
0X05剩余空间不足
0X06下载超时
0X07升级包校验失败
0X08升级包类型不支持
0X09内存不足
0X0A安装升级包失败
0X0B-0X7E预留错误码
0X7F内部异常表示无法识别的异常
0X80升级任务不存在0X80开始的为平台返回的错误码
0X81指定的分片不存在

ii.升级流程

iii.断点续传

iv.PCP协议消息的识别

由于PCP协议消息和设备业务消息共用一个端口和URL通讯,平台收到设备的消息时,按照如下步骤判断是PCP协议消息还是业务消息:

1、检查设备是否支持软件升级(根据设备profile的omCapability.upgradeCapability定义),如果不支持,则认为是业务消息

2、检查设备软件升级协议是否是PCP,如果不是,则认为是业务消息

3、检查消息前两个字节是否为0XFFFE,如果不是,则认为是业务消息

4、检查版本号是否合法,如果不合法,则认为是业务消息

5、检查消息码是否合法,如果不合法,则认为是业务消息

6、检查校验码是否正确,如果不正确,则认为是业务消息

7、检查数据区长度是否正确,如果不正确,则认为是业务消息

8、如果以上检查都通过,认为是PCP协议消息

对设备的要求:需要设备保证业务消息的起始字节不是0XFFFE。

v.CRC16算法

【软件升级】CCITT标准CRC16(1021)算法Java代码:

public class CRC16Util {
private static int crc16_ccitt_table[] = {
 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a,
                    0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b,
                    0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528,
                    0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719,
                    0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e,
                    0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf,
                    0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec,
                    0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd,
                    0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2,
                    0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3,
                    0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0,
                    0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691,
                    0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806,
                    0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37,
                    0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64,
                    0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55,
                    0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 
};


    /**
     *
     * @param reg_init CRC校验时初值
     * @param message 校验值
     * @return
     */
    public static int do_crc(int reg_init, byte[] message) {
        int crc_reg = reg_init;
        for (int i = 0; i 
<
 message.length; i++) {
            crc_reg = (crc_reg 
>
>
 8) ^ crc16_ccitt_table[(crc_reg ^ message[i]) 
&
 0xff];
        }
        return crc_reg;
    }

    /**
     * 根据数据生成CRC校验码
     *
     * @param message
     *            byte数据
     *
     * @return int 返校验码
     */
    public static int do_crc(byte[] message) {
        // 计算CRC校验时初值从0x0000开始。
        int crc_reg = 0x0000;
        return do_crc(crc_reg, message);
    }
    /**
     * db44检验方法
     *
     * @param message 消息内容
     * @param crc 检验码值
     * @return
     */
    private static boolean do_crc(byte[] message,byte[] crc) {
        // 计算CRC校验时初值从0x0000开始。
        int crc_reg = 0x0000;
        int crc_value = (crc[0]
&
0xff)*256+(crc[1]
&
0xff);
        return crc_value ==do_crc(crc_reg, message);
    }
    /**
     * 供db44结构代码使用,数组后两位为CRC-校验码值
     * @param messages
     * @return
     */
    public static boolean do_crc_db44(byte[] messages) {
        // 计算CRC校验时初值从0x0000开始。
        byte[] messageArray = new byte[messages.length-2];
        byte[] crcArray = new byte[2];
        System.arraycopy(messages, 0, messageArray, 0, messageArray.length);
        System.arraycopy(messages, messages.length-2, crcArray, 0, 2);
        return do_crc(messageArray, crcArray);
    }

    public static void main(String[] args) {
        //以FFFE011300000000为例
        byte[] bytes = new byte[]{(byte)0xFF,(byte)0xFE,0x01,0x13,0x00,0x00,0x00,0x00};
        ByteBuffer clone = ByteBuffer.wrap(bytes);
        //需要把校验码的值置0再进行计算
        //clone.put(4, (byte)0).put(5, (byte)0);
        int calcChecksum = CRC16Util.do_crc(clone.array());
        System.out.println("calcChecksum:"+Integer.toHexString(calcChecksum));
    }
}复制

例如使用“FFFE011300000000”计算出来的校验码是4C9A,把校验码替换到软件升级码流规定的位置得到:FFFE011300000000->FFFE01134C9A0000,这个就是平台下发的查询设备软件版本的码流了

【软件升级】CCITT标准CRC16(1021)算法C语言代码:

#include 
<
stdio.h
>

typedef unsigned char byte;

const unsigned short crc16_table[256] = {
        0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
        0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
        0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
        0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
        0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
        0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
        0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
        0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
        0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
        0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
        0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
        0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
        0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
        0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
        0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
        0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
        0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
        0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
        0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
        0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
        0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
        0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
        0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
        0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
        0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
        0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
        0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
        0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
        0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
        0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
        0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
        0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};

int do_crc(int reg_init, byte* data, int length)
{
    int cnt;
    int crc_reg = reg_init;
    for (cnt = 0; cnt 
<
 length; cnt++)
    {
        crc_reg = (crc_reg 
>
>
 8) ^ crc16_table[(crc_reg ^ *(data++)) 
&
 0xFF];
    }
    return crc_reg;
}

int main()
{
    // FFFE011300000000用byte数组表示:
    byte message[8] = {0xFF,0xFE,0x01,0x13,0x00,0x00,0x00,0x00};
    // 计算CRC校验时初值从0x0000开始
    int a = do_crc(0x0000, message,sizeof(message));
    printf("a ==
>
 %x\n", a);
}复制

例如使用“FFFE011300000000”计算出来的校验码是4C9A,把校验码替换到软件升级码流规定的位置得到:FFFE011300000000->FFFE01134C9A0000,这个就是平台下发的查询设备软件版本的码流了


这篇文档是否帮助您解决了问题?
如果您愿意进一步帮助我们改进文档 ,请留下您的联系方式。

专属客户经理为您服务

客服热线

400-8285-656

合作咨询

微信扫码 获取VIP服务

在线时间: 工作日9:00-18:00

CTWing真诚期待您的宝贵建议