Pass-through/Edit script

Updated at:

If a device cannot send data to the cloud in JSON format, it must transmit the data in binary format using pass-through mode. The cloud then runs a parsing script to convert this data into the standard ICA JSON format.

Prerequisites

You have created a product and set its Data format to Pass-through/Custom. Otherwise, the options described in this topic will not be available on the Product > Device Debugging page.

Pass-through parsing script

The parsing script is developed in JavaScript. The data protocol for devices and scripts can be either standard or custom.

  • For devices developed with the standard protocol, you can directly use the script that is automatically generated by the cloud.
  • If you use a custom protocol, you must develop the JavaScript (JS) script yourself.

The script must support the following two methods for communication with the cloud.

  • protocolToRawData (converts ICA protocol data to binary data)
  • rawDataToProtocol (converts binary data to ICA protocol data)
通信原理

Procedure

  1. Go to the Product > Device Debugging page.
  2. In the Data Parsing section, click Edit Script.
  3. Edit the script and debug it with simulated data.

Script definition

The script supports only JavaScript syntax that complies with ECMAScript 5.1.

The data upload process is as follows.

脚本上传数据流程
  1. The device reports data in pass-through mode.
  2. The cloud uses the script to parse the data from the device and convert it into the standard data format of the IoT Platform.
  3. The converted data is used for business logic processing.
  4. The script parses the result that is returned from the cloud.
  5. The converted result is pushed to the device.

Write a script

  • Product definition

    As an example, assume a test product has three properties: prop_float, prop_int16, and prop_bool.

  • Script example
    var COMMAND_REPORT = 0x00; // Report properties
    var COMMAND_SET = 0x01; // Set properties
    var COMMAND_REPORT_REPLY = 0x02; // Response to reported data
    var COMMAND_SET_REPLY = 0x03; // Response from the device to a property setting request
    var COMMAD_UNKOWN = 0xff;    // Unknown command
    var ALINK_PROP_REPORT_METHOD = 'thing.event.property.post'; // Standard ALink JSON topic for devices to upload property data to the cloud
    var ALINK_PROP_SET_METHOD = 'thing.service.property.set'; // Standard ALink JSON topic for the cloud to send property control commands to a device
    var ALINK_PROP_SET_REPLY_METHOD = 'thing.service.property.set'; // Standard ALink JSON topic for a device to report the result of a property setting to the cloud
    /*
    Sample data:
    Data reported by the device
    Input parameter ->
        0x000000000100320100000000
    Output result ->
        {"method":"thing.event.property.post","id":"1","params":{"prop_float":0,"prop_int16":50,"prop_bool":1},"version":"1.0"}
    
    Response to a property setting request
    Input parameter ->
        0x0300223344c8
    Output result ->
        {"code":"200","data":{},"id":"2241348","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; // ALink JSON format - Topic for reporting properties
            jsonMap['version'] = '1.0'; // ALink JSON format - Fixed field for the protocol version number
            jsonMap['id'] = '' + dataView.getInt32(1); // ALink JSON format - ID of the request
            var params = {};
            params['prop_int16'] = dataView.getInt16(5); // Corresponds to the prop_int16 property of the product
            params['prop_bool'] = uint8Array[7]; // Corresponds to the prop_bool property of the product
            params['prop_float'] = dataView.getFloat32(8); // Corresponds to the prop_float property of the product
            jsonMap['params'] = params; // ALink JSON format - Standard params field
        } else if(fHead == COMMAND_SET_REPLY) {
            jsonMap['version'] = '1.0'; // ALink JSON format - Fixed field for the protocol version number
            jsonMap['id'] = '' + dataView.getInt32(1); // ALink JSON format - ID of the request
            jsonMap['code'] = ''+ dataView.getUint8(5);
            jsonMap['data'] = {};
        }
    
        return jsonMap;
    }
    /*
    Sample data:
    Set properties
    Input parameter ->
        {"method":"thing.service.property.set","id":"12345","version":"1.0","params":{"prop_float":123.452, "prop_int16":333, "prop_bool":1}}
    Output result ->
        0x0100003039014d0142f6e76d
    
    Response to data reported by the device
    Input data ->
        {"method":"thing.event.property.post","id":"12345","version":"1.0","code":200,"data":{}}
    Output result ->
        0x0200003039c8
    */
    function protocolToRawData(json) {
        var method = json['method'];
        var id = json['id'];
        var version = json['version'];
        var payloadArray = [];
        if (method == ALINK_PROP_SET_METHOD) // Set properties
        {
            var params = json['params'];
            var prop_float = params['prop_float'];
            var prop_int16 = params['prop_int16'];
            var prop_bool = params['prop_bool'];
            // Concatenate rawData based on the custom protocol format
            payloadArray = payloadArray.concat(buffer_uint8(COMMAND_SET)); // command field
            payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); // ALink JSON format 'id'
            payloadArray = payloadArray.concat(buffer_int16(prop_int16)); // Value of the 'prop_int16' property
            payloadArray = payloadArray.concat(buffer_uint8(prop_bool)); // Value of the 'prop_bool' property
            payloadArray = payloadArray.concat(buffer_float32(prop_float)); // Value of the 'prop_float' property
        } else if (method ==  ALINK_PROP_REPORT_METHOD) { // Response to data reported by the device
            var code = json['code'];
            payloadArray = payloadArray.concat(buffer_uint8(COMMAND_REPORT_REPLY)); // command field
            payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); // ALink JSON format 'id'
            payloadArray = payloadArray.concat(buffer_uint8(code));
        } else { // Unknown command. Some commands are not processed.
            var code = json['code'];
            payloadArray = payloadArray.concat(buffer_uint8(COMMAD_UNKOWN)); // command field
            payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); // ALink JSON format 'id'
            payloadArray = payloadArray.concat(buffer_uint8(code));
        }
        return payloadArray;
    }
    // The following are helper functions
    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);
    }

Debug with simulated data in the console

Note Note: To avoid errors, pay attention to the read/write permissions for product properties during simulation.
  • Simulate data reported by a device

    Set the simulation type to **Device Reported Data** and enter the test data. The simulated input data in the console must be in hexadecimal format.

    0x00002233441232013fa00000

    Click Run to view the output of the reported data.

    {
        "method": "thing.event.property.post", 
        "id": "2241348", 
        "params": {
            "prop_float": 1.25, 
            "prop_int16": 4658, 
            "prop_bool": 1
        }, 
        "version": "1.0"
    }
  • Simulate a response to data reported by the device

    Set the simulation type to **Data Received by Device** and enter the test data.

    {
      "id": "12345",
      "version": "1.0",
      "code": 200,
      "method": "thing.event.property.post",
      "data": {}
    }

    Click **Run** to view the output of the received data. The output is the result of the script conversion in hexadecimal format.

    0x0100003039014d0142f6e76d
  • Simulate a device response to a property setting request

    Simulate the device returning the result of a property setting. Enter the test data.

    0x0300223344c8

    Click **Run** to view the data reported by the device.

    {
      "code": "200",
      "data": {},
      "id": "2241348",
      "version": "1.0"
    }

Local debug script

This method is for local testing only. To test in the console, see Debug with simulated data in the console. To simplify script development and debugging, you can run the script in your local environment. The following example shows how to do this.

// Place the implementations of rawDataToProtocol and protocolToRawData here

// Test Demo
function Test()
{
    //0x001232013fa00000
    var rawdata_report_prop = new Buffer([
        0x00, // Fixed command header. 0 indicates property reporting.
        0x00, 0x22, 0x33, 0x44, // Corresponds to the id field, which marks the sequence number of the request.
        0x12, 0x32, // Two-byte int16, corresponding to the prop_int16 property.
        0x01, // One-byte bool, corresponding to the prop_bool property.
        0x3f, 0xa0, 0x00, 0x00 // Four-byte float, corresponding to the prop_float property.
    ]);

    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();

Simple data pass-through protocol

To save you from developing scripts and to reduce the computational load on the Microcontroller Unit (MCU), we have created a simple data protocol. The core data transmission uses the Type-Length-Value (TLV) format.

数据格式
  • Data is transmitted in big-endian byte order (high byte first).
  • To ensure reliable transmission, implement acknowledgement, timeout, and retransmission mechanisms.
Note This document does not specify the communication method or physical parameters. For example, for Universal Asynchronous Receiver-Transmitter (UART) communication, you must specify parameters such as baud rate: 115200, data bits: 8, parity: none, and stop bits: 1.
  • Protocol frame type definition协议帧类型
  • Payload format definitionpayload格式
    • method: The operation method. The following table describes the definitions.method定义
    • id: A frame identifier that distinguishes requests. The response frame must have the same id as the request frame.
    • data: The data domain. The specific format is determined by the method.
      • Data domain format for the Get methodGet方法

        attrid is the numbered ID of the property that the cloud reads.

      • Data domain format for the Set/Report methodSet/Report方法

        The protocol uses numbers to represent types and properties. The len field is optional and is required only for array and text types to indicate the length.

      • Data domain format for the Service/Event methodService/Event方法

        Services and events are identified by number for transmission. ID is the number of the service or event. parameters are the parameters for the service or event.

      • Data domain format for the Ack methoderrcode indicates the error code.

MCU SDK

For the standard binary protocol, we provide an MCU SDK that encapsulates the protocol. Based on your product's feature definitions, we also generate the corresponding code and logic for reporting and processing data in the MCU SDK. When you use the MCU SDK for development, you do not need to implement the communication protocol or define product features. You can directly call the provided API operations and add your business logic.

For example, when the cloud sends a request to turn off a light, which corresponds to the Switch property identifier, you must implement the Switch handling within a specific API. The MCU SDK handles the reply to the cloud by default. When your program detects a change in the local light's on/off state, it calls the API to modify the Switch property. The MCU SDK then reports the change to the cloud. This lets you focus on developing the device's business features.

The MCU SDK can generate development projects for the following chip models. You can develop your applications directly based on these projects. If you choose other platforms, we provide an SDK and a simple demo that you can compile and run in Linux.

  • STM8S207

    The development Integrated Development Environment (IDE) is IAR for STM8 (EWSTM8).

  • STM32L053R8

    The development IDE is Keil MDK5.

  • Other platforms

    Only a simple example is provided. You can compile and run it in Linux.

The core code for the MCU SDK is in the sdk-core directory. The directory structure, which includes the inc header file directory and the src source code directory, is shown in the following figure.

目录结构

The header files are described as follows.

  • common.h

    The common header file for the software development kit (SDK). It includes type definitions, global object definitions for the SDK, and public API operations. We recommend that you review this file.

  • platform.h

    Defines the functions that you need to implement or handle. We recommend that you review this file.

  • protocol.h

    Defines the interfaces used for the communication protocol with the cloud. You can ignore this file.

  • thing.h

    Contains definitions related to product features, such as properties, services, and events. We recommend that you review this file.

The source files are described as follows.

  • common.c

    Implements the common code for the SDK.

  • protocol.c

    Defines and implements the communication protocol with the cloud.

  • thing.c

    Implements the interfaces related to product features.

Parts of the code in thing.h and thing.c are automatically generated based on the product's Thing Specification Language (TSL).

API reference

The following interfaces are available for you to call.
  • Public interfaces
    • SDK initializer function: void boneSdkInit(void)
    • SDK runtime function, which is called in a while loop: void boneSdkRun(void)
    • Receives byte data from the serial port. This function is called in the serial port interrupt service routine: Int32_t boneRcvFromUart(Uint8_t *data, Uint16_t length);
    • System runtime calculation, which is called by a 1 ms timer. You can leave this unimplemented for now: void boneSystimeInc(void);
  • Product feature interfaces
    / Property value range definition /
    #define  ATTR_XXX
    
    / Set and get property values. AttrName represents the property name. The actual interface name corresponds to the property identifier. AttrVal represents the property value. ValueType represents the property type. /
    void boneSet_AttrName(AttrVal);
    ValueType boneGet_AttrName(void);
    
    / Report an event. EventName represents the event name. The actual interface name corresponds to the event identifier. Params represents the output parameters of the event. /
    void boneEvntPost_EventName(Params);
    Note The interfaces and parameters defined above are only a template. The specific APIs depend on the product features that you define. The following APIs are automatically generated for the sample product.
    • Property value range definition
      #define RANG_PROPINT8_R_MIN   -100
      #define RANG_PROPINT8_R_MAX   100
    • Set and retrieve property values
      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);
    • Report an event
      void boneEvntPost_EventInfo(eo_EventInfo_t *arg);
      
      void boneEvntPost_EventAlarm(void);
  • Interfaces for you to implement
    / Send protocol data over the serial port /
    Int32_t boneUartSend(Uint8_t *buffer, Uint16_t length);
    
    /* Lock-related interfaces. Can be left empty if no OS is used. */
    void *boneMutexCreate(void);
    void boneMutexDestroy(void *mutex);
    void boneMutexLock(void *mutex);
    void boneMutexUnlock(void *mutex);
  • Interfaces where you need to add handler methods
    /* Property change handler function */
    void bonePropChangeHandler(Int32_t index);
    
    /* Service handler function. Can be ignored if there are no services. ServiceName in the interface name represents the service name. The actual interface name corresponds to the service identifier. */
    static Int32_t boneServCall_ServiceName(Uint32_t id, Uint8_t *data, Uint16_t length)

    The following is an example.

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