透传数据协议--解析脚本及MCU SDK说明

更新时间:2018-05-24 23:21:15

解析脚本

透传解析脚本功能介绍

由于低配置且资源受限或者对网络流量有要求的设备不适合直接和云端传输Json格式的数据,因此设备通过二进制格式将数据透传到云端,由云端运行一段解析脚本将透传的数据转换成标准ICA格式的JSON数据。目前解析脚本通过JavaScript开发。</span>
设备和脚本的数据协议格式支持标准和自定义两种方式。使用标准协议开发的设备可以直接使用云端自动生成的脚本,若协议自定义则需要开发者自行开发JS脚本。</span>
脚本需要支持以下两个方法即可和云端上下行通讯:(1) ICA协议数据转二进制数据(protocolToRawData) (2)二进制数据转ICA协议数据(rawDataToProtocol)。

image.png | center | 435x355

脚本定义

语言定义

目前脚本仅支持符合ECMAScript 5.1的JavaScript语法。

方法定义

ICA协议数据转二进制数据

  • 方法名如下:

    // 解析服务端发送的ICA Json数据,并转换为二进制数据返回
    function protocalToRawData(jsonObj) {
      return rawdata;
    }
    
  • 参数定义:
    输入参数jsonObj :符合产品TSL定义的ICA协议格式数据, 例如:

    {
      "method":"thing.service.property.set",
      "id":"12345",
      "version":"1.0",
      "params": {
          "prop_float":123.452,
          "prop_int16":333,
          "prop_bool":1
      }
    }
    
  • 返回参数 :
    返回二进制byte数组,例如:

    0100003039014d0142f6e76d
    

二进制数据转ICA协议数据

  • 方法名如下:

    // 解析设备端发送的二进制数据,并转换为ICA Json数据返回
    function rawDataToProtocal(rawData) {
      return jsonObj;
    }
    
  • 参数定义:
    输入参数rawData :二进制byte数组, 如

    00002233441232013fa00000
    
  • 返回参数:
    符合产品TSL定义的ICA协议格式数据, 如:

    {
      "method":"thing.event.property.post",
      "id":"2241348",
      "params":{
          "prop_float":1.25,
          "prop_int16":4658,
          "prop_bool":1
      },
      "version":"1.0"
    }
    

自动生成脚本

通过获取产品功能我们会直接读取TSL数据, 并使用云平台制定的标准协议直接生成对应的脚本文件。但需要注意的是, 使用该功能时需设备按标准协议发送或接收解析,或者配套使用为了设备端开发的MCUSDK。

脚本开发示例

开发步骤

  • 添加产品功能

  • 编写解析脚本

  • 输入数据测试

  • 提交脚本

  • 上报/下发数据验证脚本解析是否正确

注意:如果使用标准协议,产品功能修改之后需要重新生成脚本并提交之后新功能才能被正常解析。

示例脚本

实现了数据上下行的协议(二进制转ICA和ICA转二进制):

var COMMAND_REPORT = 0x00;
var COMMAND_SET = 0x01;

var ALINK_PROP_REPORT_METHOD = 'thing.event.property.post'; // 标准ICA协议method, 设备 上传数据到 云端
var ALINK_PROP_SET_METHOD = 'thing.service.property.set';   // 标准ICA协议method, 云端 下发数据到 设备

// 上行示例数据:
//   传入参数 ->
//        0x00002233441232013fa00000
//   输出结果 ->
//        {"method":"thing.event.property.post","id":"2241348","params":{"prop_float":1.25,"prop_int16":4658,"prop_bool":1},"version":"1.0"}
function rawDataToProtocol(bytes)
{
    var uint8Array = new Uint8Array(bytes.length);
    for (var i = 0; i < bytes.length; i++)
    {
        uint8Array[i] = bytes[i] & 0xff;
    }
    var dataView = new DataView(uint8Array.buffer, 0);

    var jsonMap = new Object();

    var fHead = uint8Array[0]; // command
    if (fHead == COMMAND_REPORT)
    {
        jsonMap['method'] = ALINK_PROP_REPORT_METHOD; //ICA协议 - 属性上报topic
        jsonMap['version'] = '1.0'; //ICA协议 - 协议版本号固定字段
        jsonMap['id'] = '' + dataView.getInt32(1); //ICA协议 - 标示该次请求id值
        var params = {};
        params['prop_int16']  = dataView.getInt16(5); //对应产品属性中 prop_int16
        params['prop_bool'] = uint8Array[7];//对应产品属性中 prop_bool
        params['prop_float'] = dataView.getFloat32(8);//对应产品属性中 prop_float
        jsonMap['params'] = params;//ICA协议 - params标准字段
    }

    return jsonMap;
}

// 示例数据:
//   传入参数 ->
//       {"method":"thing.service.property.set","id":"12345","version":"1.0","params":{"prop_float":123.452, "prop_int16":333, "prop_bool":1}}
//   输出结果 ->
//      0x0100003039014d0142f6e76d
function protocolToRawData(json)
{
    var method = json['method'];
    var id = json['id'];
    var version = json['version'];

    var payloadArray = [];
    if (method == ALINK_PROP_SET_METHOD)    // 属性设置
    {
        var params = json['params'];
        var prop_float = params['prop_float'];
        var prop_int16 = params['prop_int16'];
        var prop_bool = params['prop_bool'];

        //按照自定义协议格式拼接 rawdata
        payloadArray = payloadArray.concat(buffer_uint8(COMMAND_SET));  // command字段
        payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); // ICA协议 'id'
        payloadArray = payloadArray.concat(buffer_int16(prop_int16));   // 属性'prop_int16'的值
        payloadArray = payloadArray.concat(buffer_uint8(prop_bool));    // 属性'prop_bool'的值
        payloadArray = payloadArray.concat(buffer_float32(prop_float)); // 属性'prop_float'的值
    }

    return payloadArray;
}

// 以下是部分辅助函数
function buffer_uint8(value)
{
    var uint8Array = new Uint8Array(1);
    var dv = new DataView(uint8Array.buffer, 0);
    dv.setUint8(0, value);
    return [].slice.call(uint8Array);
}

function buffer_int16(value)
{
    var uint8Array = new Uint8Array(2);
    var dv = new DataView(uint8Array.buffer, 0);
    dv.setInt16(0, value);
    return [].slice.call(uint8Array);
}

function buffer_int32(value)
{
    var uint8Array = new Uint8Array(4);
    var dv = new DataView(uint8Array.buffer, 0);
    dv.setInt32(0, value);
    return [].slice.call(uint8Array);
}

function buffer_float32(value)
{
    var uint8Array = new Uint8Array(4);
    var dv = new DataView(uint8Array.buffer, 0);
    dv.setFloat32(0, value);
    return [].slice.call(uint8Array);
}

示例测试数据

设备上报数据测试数据

0x00002233441232013fa00000

上报数据输出结果

{"method":"thing.event.property.post","id":"2241348",
"params":{"prop_float":1.25,"prop_int16":4658,"prop_bool":1},
"version":"1.0"}

设备接收数据测试数据

{"method":"thing.service.property.set","id":"12345","version":"1.0","params":{"prop_float":123.452, "prop_int16":333, "prop_bool":1}}

接收数据输出结果

0x0100003039014d0142f6e76d

本地调试脚本

为了方便开发及调试脚本,可将脚本放在本地环境中进行调用,参考如下:

// rawDataToProtocol和protocolToRawData的实现放在这里

// Test Demo
function Test()
{
    //0x001232013fa00000
    var rawdata_report_prop = new Buffer([
        0x00, //固定command头, 0代表是上报属性
        0x00, 0x22, 0x33, 0x44, //对应id字段, 标记请求的序号
        0x12, 0x32, //两字节 int16, 对应属性 prop_int16
        0x01, //一字节 bool, 对应属性 prop_bool
        0x3f, 0xa0, 0x00, 0x00 //四字节 float, 对应属性 prop_float
    ]);

    rawDataToProtocol(rawdata_report_prop);

    var setString = new String('{"method":"thing.service.property.set","id":"12345","version":"1.0","params":{"prop_float":123.452, "prop_int16":333, "prop_bool":1}}');
    protocolToRawData(JSON.parse(setString));
}

Test();

透传协议

为了让开发者免去脚本的开发,以及考虑减轻MCU的运算,我们制定了一套简易的数据协议(就是上章提到的标准协议。这里需要说明,此标准非彼标准),核心数据传输采用TLV格式。

image.png | center | 495x214

使用说明</span>

  • 本文档对通信方式和物理参数不做要求

比如:UART通信,需要指定如下参数:

波特率:115200

数据位:8

奇偶校验:无

停止位:1

数据流控:无

  • 数据传输统一使用</span>大端</span>(高字节在前,低字节在后)字节序</span>

  • 为保证传输可靠性,通信需要实现应答、超时及重传机制</span>

    协议帧类型定义

image.png | center | 396x193

payload格式定义

image.png | center | 509x81

各字段说明如下:</span>

method

操作的方法。定义如下:</span>

image.png | center | 491x125

id

帧标识符,用于区分不同的请求。回复帧与请求帧的id必须相同,表示对该帧的回复。</span>

data

数据域,具体格式根据method来确定。</span>

  • Get方法的data域格式

image.png | center | 179x65

说明:attrid即云端需要读取属性的ID,通过编号表示。

  • Set/Report方法的data域格式

image.png | center | 343x66

说明:

(1)协议中将类型、属性进行编号表示。

(2)len非必须,仅在类型为数组文本(text)的情况下需要,表示长度。

  • Service/Event方法的data域格式

image.png | center | 180x58

说明:对服务和事件进行编号传输,ID表示服务或者事件的编号,parameters表示服务或者事件携带的参数。

  • Ack方法的data域格式

image.png | center | 94x55

说明:errcode表示错误码。
上述提到的属性、类型、服务、事件的编号是通过定义的产品TSL自动生成的,具体可以参考自动生成的脚本或者MCUSDK。

MCU SDK

针对上述提到的二进制的标准协议,我们提供了MCUSDK实现了协议的封装。此外,我们还会根据开发者的产品功能定义在MCU SDK中生成与之对应的代码和上报、接收处理逻辑。开发者使用MCUSDK开发就不用实现通讯协议和产品功能的定义,直接按照提供的API接口调用以及添加自己的业务逻辑即可。</span>
例如,从云端下发一个关灯(对应属性标识Switch)的请求,需要在开发者在特定的API内部实现Switch的处理,MCUSDK默认实现了对云端的回复;例如,设备本地灯的开关状态变化,开发者的程序识别到后调用修改Switch属性的API后,MCUSDK会将变化上报到云端。通过这样开发者就只需要关注设备业务功能的开发即可。</span>
目前MCUSDK支持如下几种芯片型号生成对应开发工程,开发者可以直接基于此工程直接开发自己的应用。如果选择其他平台,我们会提供sdk和简单的示例demo,开发者可以在Linux进行编译运行。

  • STM8S207
    开发IDE使用IAR for STM8(EWSTM8)。

  • STM32L053R8
    开发IDE使用Keil MDK5。

  • 其他平台
    仅提供简单的示例。可以在Linux中编译运行。

    目录结构

    MCUSDK的核心代码位于sdk-core目录,目录结构如下所示,包括了头文件目录inc和源代码目录src。</span>

image.png | center | 180x206

头文件

  • common.h
    SDK共用的头文件,包括了类型定义、SDK全局对象定义以及公共的API。需要开发者关注

  • platform.h
    定义了需要开发者实现或者处理的函数。需要开发者关注

  • protocol.h
    定义了和云端通讯协议的使用的接口。开发者可以不用关注。

  • thing.h
    包含了产品功能(属性、服务、事件)相关定义。需要开发者关注

    源文件

  • common.c
    SDK公共代码的实现。

  • protocol.c
    和云端通信协议的定义及接口实现。

  • thing.c
    产品功能相关的接口实现。

说明:thing.h和thing.c部分代码会根据产品的TSL自动生成。

API说明

需要用户调用的接口

公共接口

/ SDK初始化函数 /
void boneSdkInit(void);

/ SDK运行函数,在while中调用 /
void boneSdkRun(void);

/ 接收串口的字节数据,在串口中断服务程序中调用 /
Int32_t boneRcvFromUart(Uint8_t *data, Uint16_t length);

/ 系统运行时间计算(定时1ms调用),暂时可以不用实现 /
void boneSystimeInc(void);

产品功能相关接口

/ 属性值范围定义 /
#define  ATTR_XXX

/ 属性值的设置和获取,AttrName表示的是属性名称,实际的接口名称和属性标识对应,AttrVal表示的是属性的值, ValueType表示的是属性的类型 /
void boneSet_AttrName(AttrVal);
ValueType boneGet_AttrName(void);

/ 事件上报,EventName表示的是事件名称,实际的接口名称和事件标识对应,Params表示事件的输出参数 /
void boneEvntPost_EventName(Params);

注意:以上接口及参数定义的只是一个模版,具体的API要视自己定义的产品功能而定。以下是示例产品自动生成的API。</span>

  • 属性值范围定义:</span>

    #define RANG_PROPINT8_R_MIN   -100
    #define RANG_PROPINT8_R_MAX   100
    
  • 属性值的设置和获取</span>
    ```c
    void boneSet_PropInt8_r(Int8_t data);
    Int8_t boneGet_PropInt8_r(void);

void boneSet_PropUint8_r(Uint8_t data);
Uint8_t boneGet_PropUint8_r(void);


* <span data-type="color" style="color:rgb(51, 51, 51)"><span data-type="background" style="background-color:rgb(255, 255, 255)">事件上报</span></span>
```c
void boneEvntPost_EventInfo(eo_EventInfo_t *arg);

void boneEvntPost_EventAlarm(void);

需要用户实现的接口

/ 串口发送协议数据 /
Int32_t boneUartSend(Uint8_t *buffer, Uint16_t length);

/* 锁相关接口,无os可空实现 */
void *boneMutexCreate(void);
void boneMutexDestroy(void *mutex);
void boneMutexLock(void *mutex);
void boneMutexUnlock(void *mutex);

需要用户添加处理方法的接口

/* 属性变化处理函数 */
void bonePropChangeHandler(Int32_t index);

/* 服务处理函数,如果没有服务可忽略。接口名称中的ServiceName表示的是服务名称,实际的接口名称和服务标识对应 */
static Int32_t boneServCall_ServiceName(Uint32_t id, Uint8_t *data, Uint16_t length)

示例:

static Int32_t boneServCall_SrvAsync1(Uint32_t id, Uint8_t *data, Uint16_t length);

static Int32_t boneServCall_SrvAsync2(Uint32_t id)

results matching ""

    No results matching ""