Offline Codec Development
A codec can convert binary messages into JSON messages. The JSON format is defined in the profile. Therefore, before developing a codec, you must define the product model of the device.
Codec demo projects are provided to improve the integration efficiency of offline codec development. You are advised to perform secondary development based on a demo project.
Note: Offline codec development is complex and time-consuming. Therefore, graphical development is recommended.
Preparing the Development Environment
- Download the Eclipse installation package from the official website and decompress it to a local directory. You can use the software without installation.
- Download the Maven plug-in package (in .zip format) from the official website and decompress it to a local directory.
- Install the JDK and configure the Java development environment.
Maven configuration involves setting environment variables on Windows and setting Maven on Eclipse. For details on setting environment variables on Windows, see other online resources. Maven can be configured on Eclipse as follows:
- Start Eclipse and choose Windows > Preferences. In the Preferences window, choose Maven > Installations. On the right pane, click Add.

- Select the path where the Maven plug-in package is stored and click Finish to import the Maven plug-in.

- Select the imported Maven plug-in and click OK.

Importing the Demo Project of the Codec
- Download the demo project, obtain the codecDemo.zip file from the source_code folder, and decompress the file to a local directory.

- Open Eclipse, right-click the blank area in Project Explorer on the left of Eclipse, and choose Import > Import....

- Expand Maven, select Existing Maven Projects, and click Next.

- Click Browse, select the codecDemo folder obtained in step 1, select /pom.xml, and click Finish.

Implementation Sample Interpretation
The following figure shows the structure of the imported codec demo project.

This project is a Maven project. You can modify the following content based on this sample project to obtain the required codec.
- Modify the configuration files of the Maven project.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.thrid.party</groupId> <!-- Change it to the name of your codec. The naming rule is as follows: device type-manufacturer ID, for example: WaterMeter-Huawei.--> <artifactId>WaterMeter-Huawei</artifactId> <version>1.0.0</version> <!-- Check that the value is bundle. The value cannot be jar. --> <packaging>bundle</packaging> ...... <dependencies> ...... <!-- Codec interface provided by Huawei, which must be introduced. --> <!-- Replace systemPath with your local \codecDemo\lib\com.huawei.m2m.cig.tup-1.3.1.jar --> <dependency> <groupId>com.huawei</groupId> <artifactId>protocal-jar</artifactId> <version>1.3.1</version> <scope>system</scope> <systemPath>${basedir}/lib/com.huawei.m2m.cig.tup-1.3.1.jar</systemPath> </dependency> ...... </dependencies> <build> <plugins> <!-- OSGi packaging configuration --> <plugin> <configuration> <instructions> <!-- Change it to the name of your codec. The naming rule is as follows: device type-manufacturer ID, for example: WaterMeter-Huawei. --> <Bundle-SymbolicName>WaterMeter-Huawei</Bundle-SymbolicName> </instructions> </configuration> </plugin> </plugins> </build> </project>
- In the ProtocolAdapterImpl.java file, change the values of MANU_FACTURERID.
1 2 3
private static final Logger logger = LoggerFactory.getLogger(ProtocolAdapterImpl.class); //Manufacturer name private static final String MANU_FACTURERID = "Huawei";
- Modify the code in the CmdProcess.java file so that the codec can encode delivered commands and responses to reported data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
package com.Huawei.NBIoTDevice.WaterMeter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; public class CmdProcess { //private String identifier = "123"; private String msgType = "deviceReq"; private String serviceId = "Brightness"; private String cmd = "SET_DEVICE_LEVEL"; private int hasMore = 0; private int errcode = 0; private int mid = 0; private JsonNode paras; public CmdProcess() { } public CmdProcess(ObjectNode input) { try { // this.identifier = input.get("identifier").asText(); this.msgType = input.get("msgType").asText(); /* The IoT platform receives messages reported by the device and encodes the ACK message. { "identifier":"0", "msgType":"cloudRsp", "request": ***,//Stream reported by the device "errcode":0, "hasMore":0 } * */ if (msgType.equals("cloudRsp")) { //Assemble the values of fields in the ACK message. this.errcode = input.get("errcode").asInt(); this.hasMore = input.get("hasMore").asInt(); } else { /* The IoT platform delivers a command to the device with parameters specified as follows: { "identifier":0, "msgType":"cloudReq", "serviceId":"WaterMeter", "cmd":"SET_DEVICE_LEVEL", "paras":{"value":"20"}, "hasMore":0 } * */ //Compatibility must be considered. If the MID is not transferred, it is not encoded. if (input.get("mid") != null) { this.mid = input.get("mid").intValue(); } this.cmd = input.get("cmd").asText(); this.paras = input.get("paras"); this.hasMore = input.get("hasMore").asInt(); } } catch (Exception e) { e.printStackTrace(); } } public byte[] toByte() { try { if (this.msgType.equals("cloudReq")) { /* The NA delivers a control command. In this example, there is only one command: SET_DEVICE_LEVEL. If there are other commands, determine them. * */ if (this.cmd.equals("SET_DEVICE_LEVEL")) { int brightlevel = paras.get("value").asInt(); byte[] byteRead = new byte[5]; ByteBufUtils buf = new ByteBufUtils(byteRead); buf.writeByte((byte) 0xAA); buf.writeByte((byte) 0x72); buf.writeByte((byte) brightlevel); //Compatibility must be considered. If the MID is not transferred, it is not encoded. if (Utilty.getInstance().isValidofMid(mid)) { byte[] byteMid = new byte[2]; byteMid = Utilty.getInstance().int2Bytes(mid, 2); buf.writeByte(byteMid[0]); buf.writeByte(byteMid[1]); } return byteRead; } } /* After receiving the data reported by the device, the IoT platform encodes the ACK message as required and responds to the device. If null is returned, the IoT platform does not need to respond. * */ else if (this.msgType.equals("cloudRsp")) { byte[] ack = new byte[4]; ByteBufUtils buf = new ByteBufUtils(ack); buf.writeByte((byte) 0xAA); buf.writeByte((byte) 0xAA); buf.writeByte((byte) this.errcode); buf.writeByte((byte) this.hasMore) return ack; } return null; } catch (Exception e) { // TODO: handle exception e.printStackTrace(); return null; } } }
- Modify the code in the ReportProcess.java file so that the codec can decode data reported by devices and command execution results.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
package com.Huawei.NBIoTDevice.WaterMeter; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; public class ReportProcess { //private String identifier; private String msgType = "deviceReq"; private int hasMore = 0; private int errcode = 0; private byte bDeviceReq = 0x00; private byte bDeviceRsp = 0x01; //serviceId = Brightness private int brightness = 0; //serviceId = Electricity private double voltage = 0.0; private int current = 0; private double frequency = 0.0; private double powerfactor = 0.0; //serviceId = Temperature private int temperature = 0; private byte noMid = 0x00; private byte hasMid = 0x01; private boolean isContainMid = false; private int mid = 0; /** * @param binaryData: Payload of the CoAP packet sent by the device to the IoT platform * Input parameters in this example: AA 72 00 00 32 08 8D 03 20 62 33 99 * byte[0]--byte[1]: AA 72 command header * byte[2]: 00 mstType: 00 represents deviceReq, which indicates that data is reported by the device. * byte[3]: 00 hasMore: 0 indicates that there is no subsequent data and 1 indicates that there is subsequent data. If the hasMore field is not contained, the value 0 is used. * byte[4]--byte[11]: indicates service data, which is parsed as required.//If the service data is deviceRsp, byte[4] indicates whether the MID is carried and byte[5] to byte[6] indicate the short command ID. * @return */ public ReportProcess(byte[] binaryData) { //The identifier parameter can be obtained based on the input parameter stream. In this example, the default value is 123. // identifier = "123"; /* If the data is reported by the device, the return value is in the following format: { "identifier":"123", "msgType":"deviceReq", "hasMore":0, "data":[{"serviceId":"Brightness", "serviceData":{"brightness":50}, { "serviceId":"Electricity", "serviceData":{"voltage":218.9,"current":800,"frequency":50.1,"powerfactor":0.98}, { "serviceId":"Temperature", "serviceData":{"temperature":25}, ] } */ if (binaryData[2] == bDeviceReq) { msgType = "deviceReq"; hasMore = binaryData[3]; //serviceId = Brightness brightness = binaryData[4]; //serviceId = Electricity voltage = (double) (((binaryData[5] << 8) + (binaryData[6] & 0xFF)) * 0.1f); current = (binaryData[7] << 8) + binaryData[8]; powerfactor = (double) (binaryData[9] * 0.01); frequency = (double) binaryData[10] * 0.1f + 45; //serviceId = Temperature temperature = (int) binaryData[11] & 0xFF - 128; } /* If the data is a response sent by the device to a command of the IoT platform, the return value is in the following format: { "identifier":"123", "msgType":"deviceRsp", "errcode":0, "body" :{****} Note that the body is a JSON structure. } */ else if (binaryData[2] == bDeviceRsp) { msgType = "deviceRsp"; errcode = binaryData[3]; //Compatibility must be considered. If the MID is not transferred, it is not encoded. if (binaryData[4] == hasMid) { mid = Utilty.getInstance().bytes2Int(binaryData, 5, 2); if (Utilty.getInstance().isValidofMid(mid)) { isContainMid = true; } } } else { return; } } public ObjectNode toJsonNode() { try { //Assemble the body. ObjectMapper mapper = new ObjectMapper(); ObjectNode root = mapper.createObjectNode(); // root.put("identifier", this.identifier); root.put("msgType", this.msgType); //Assemble the message body based on the msgType field. if (this.msgType.equals("deviceReq")) { root.put("hasMore", this.hasMore); ArrayNode arrynode = mapper.createArrayNode(); //serviceId = Brightness ObjectNode brightNode = mapper.createObjectNode(); brightNode.put("serviceId", "Brightness"); ObjectNode brightData = mapper.createObjectNode(); brightData.put("brightness", this.brightness); brightNode.put("serviceData", brightData); arrynode.add(brightNode); //serviceId = Electricity ObjectNode electricityNode = mapper.createObjectNode(); electricityNode.put("serviceId", "Electricity"); ObjectNode electricityData = mapper.createObjectNode(); electricityData.put("voltage", this.voltage); electricityData.put("current", this.current); electricityData.put("frequency", this.frequency); electricityData.put("powerfactor", this.powerfactor); electricityNode.put("serviceData", electricityData); arrynode.add(electricityNode); //serviceId = Temperature ObjectNode temperatureNode = mapper.createObjectNode(); temperatureNode.put("serviceId", "Temperature"); ObjectNode temperatureData = mapper.createObjectNode(); temperatureData.put("temperature", this.temperature); temperatureNode.put("serviceData", temperatureData); arrynode.add(temperatureNode); //serviceId = Connectivity ObjectNode ConnectivityNode = mapper.createObjectNode(); ConnectivityNode.put("serviceId", "Connectivity"); ObjectNode ConnectivityData = mapper.createObjectNode(); ConnectivityData.put("signalStrength", 5); ConnectivityData.put("linkQuality", 10); ConnectivityData.put("cellId", 9); ConnectivityNode.put("serviceData", ConnectivityData); arrynode.add(ConnectivityNode); //serviceId = Battery ObjectNode batteryNode = mapper.createObjectNode(); batteryNode.put("serviceId", "battery"); ObjectNode batteryData = mapper.createObjectNode(); batteryData.put("batteryVoltage", 25); batteryData.put("battervLevel", 12); batteryNode.put("serviceData", batteryData); arrynode.add(batteryNode); root.put("data", arrynode); } else { root.put("errcode", this.errcode); //Compatibility must be considered. If the MID is not transferred, it is not decoded. if (isContainMid) { root.put("mid", this.mid);//mid } //Assemble the body. The body must be an ObjectNode object. ObjectNode body = mapper.createObjectNode(); body.put("result", 0); root.put("body", body); } return root; } catch (Exception e) { e.printStackTrace(); return null; } } }
Description of decode API
The input parameter binaryData over the decode API is the payload in the CoAP message sent by a device.

Upstream messages reported by the device need to be processed by the codec in the following two scenarios (message (4) is the protocol ACK message returned by the module and does not need to be processed by the codec):
- Reported device data (message (1) in the figure)
Parameter
Type
Mandatory or Optional
Description
identifier
String
No
Identifier of the device in the application protocol. The IoT platform obtains the parameter over the decode API, encodes the parameter over the encode API, and places the parameter in a stream.
msgType
String
Yes
This parameter has a fixed value of deviceReq, which indicates that the device reports data to the IoT platform.
hasMore
Int
No
Specifies whether the IoT platform has subsequent commands to deliver. 0: The IoT platform does not have subsequent commands to deliver. 1: The IoT platform has subsequent commands to deliver.
Subsequent data indicates that a piece of data reported by a device may be reported multiple times. After the data is reported the current time, the IoT platform determines whether there are subsequent messages using the hasMore field. The hasMore field is valid only in PSM mode. When the hasMore field of reported data is set to 1, the IoT platform does not deliver cached commands until it receives reported data whose hasMore field is set to 0. If the reported data does not contain the hasMore field, the IoT platform processes the data on the basis that the hasMore field is set to 0.
data
ArrayNode
Yes
Content of the data reported by the device.
Table 1 Definition of ArrayNode Parameter
Type
Mandatory or Optional
Description
serviceId
String
Yes
Service ID.
serviceData
ObjectNode
Yes
Data of a service. The detailed parameters are defined in the profile.
eventTime
String
No
Specifies the time when the device collects data. The format is yyyyMMddTHHmmssZ,
for example, 20161219T114920Z.
Example:
{ "identifier": "123", "msgType": "deviceReq", "hasMore": 0, "data": [{ "serviceId": "NBWaterMeterCommon", "serviceData": { "meterId": "xxxx", "dailyActivityTime": 120, "flow": "565656", "cellId": "5656", "signalStrength": "99", "batteryVoltage": "3.5" }, "eventTime": "20160503T121540Z" }, { "serviceId": "waterMeter", "serviceData": { "internalTemperature": 256 }, "eventTime": "20160503T121540Z" }] } - Device response to the command delivered by the IoT platform (message (5) in the figure)
Parameter
Type
Description
Mandatory or Optional
identifier
String
Identifier of the device in the application protocol. The IoT platform obtains the parameter over the decode API, encodes the parameter over the encode API, and places the parameter in a stream.
No
msgType
String
This parameter has a fixed value of deviceRsp, which indicates a response sent by a device to the IoT platform.
Yes
mid
Int
Specifies a 2-byte unsigned command ID. If the device must return the command execution result (deviceRsp), this field is used to associate the command execution result (deviceRsp) with the corresponding command.
When the IoT platform delivers a command over the encode API, the IoT platform places the MID allocated by the IoT platform into a stream and delivers the stream to the device together with the command. When the device reports the command execution result (deviceRsp), the device returns the MID to the IoT platform. In this way, the IoT platform associates the delivered command with the command execution result (deviceRsp) and updates the command delivery status accordingly.
Yes
errcode
Int
Request processing result code. The IoT platform determines the command delivery status based on this field.
The value 0 indicates success, and the value 1 indicates failure.
Yes
body
ObjectNode
Command response, whose fields are defined in the profile.
Note: The body is not an array.
No
Example:
{ "identifier": "123", "msgType": "deviceRsp", "mid": 2016, "errcode": 0, "body": { "result": 0 } }
Description of encode API
Input parameters of the encode API are commands or responses in JSON format delivered by the IoT platform.

The downstream messages of the IoT platform can be classified into two types:
- Response from the IoT platform to the data reported by the device (message (2) in the figure)
Table 2 Definition of input parameters of the encode API over which the IoT platform responds to data reported by a device Parameter
Type
Description
Mandatory or Optional
identifier
String
Identifier of the device in the application protocol. The IoT platform obtains the parameter over the decode API, encodes the parameter over the encode API, and places the parameter in a stream.
No
msgType
String
This field has a fixed value of cloudRsp, which indicates that the IoT platform sends a response to data reported by a device.
Yes
request
byte[]
Indicates the data reported by the device.
Yes
errcode
int
Request processing result code. The IoT platform determines the command delivery status based on this field.
The value 0 indicates success, and the value 1 indicates failure.
Yes
hasMore
int
Specifies whether the IoT platform has subsequent messages to be sent. The value 0 indicates that the IoT platform does not have subsequent messages to be sent. The value 1 indicates that the IoT platform has subsequent messages to be sent.
Subsequent messages indicate that the IoT platform still needs to deliver commands, and the hasMore field is used to tell the device not to sleep. The hasMore field is valid only in PSM mode with the downstream message indication function enabled.
Yes
Note: If msgType is set to cloudRsp and null is returned by the codec detection tool, the codec does not define the response to the reported data and the IoT platform does not need to respond.
Example:
{ "identifier": "123", "msgType": "cloudRsp", "request": [ 1, 2 ], "errcode": 0, "hasMore": 0 } - Commands delivered by the IoT platform (message (3) in the figure)
Table 3 Definition of input parameters of the encode API over which the IoT platform delivers commands Parameter
Type
Description
Mandatory or Optional
identifier
String
Identifier of the device in the application protocol. The IoT platform obtains the parameter over the decode API, encodes the parameter over the encode API, and places the parameter in a stream.
No
msgType
String
This parameter has a fixed value of cloudReq, which indicates a command delivered by the IoT platform.
Yes
serviceId
String
Service ID.
Yes
cmd
String
Command name. For details, see the profile.
Yes
paras
ObjectNode
Command parameters, which are defined in the profile.
Yes
hasMore
Int
Specifies whether the IoT platform has subsequent commands to deliver. 0: The IoT platform does not have subsequent commands to deliver. 1: The IoT platform has subsequent commands to deliver.
Subsequent commands indicate that the IoT platform still needs to deliver commands, and the hasMore field is used to tell the device not to sleep. The hasMore field is valid only in PSM mode with the downstream message indication function enabled.
Yes
mid
Int
A 2-byte unsigned command ID that is allocated by the IoT platform. (The value ranges from 1 to 65535.)
When the IoT platform delivers a command over the encode API, the IoT platform places the MID allocated by the IoT platform into a stream and delivers the stream to the device together with the command. When the device reports the command execution result (deviceRsp), the device returns the MID to the IoT platform. In this way, the IoT platform associates the delivered command with the command execution result (deviceRsp) and updates the command delivery status accordingly.
Yes
Example:
{ "identifier": "123", "msgType": "cloudReq", "serviceId": "NBWaterMeterCommon", "mid": 2016, "cmd": "SET_TEMPERATURE_READ_PERIOD", "paras": { "value": 4 }, "hasMore": 0} }
Description of getManufacturerId API
This API is used to return the manufacturer ID in the format of a character string. The IoT platform calls this API to obtain the manufacturer ID.
Example:
@Override
public String getManufacturerId() {
return "TestUtf8ManuId";
}
Precautions on Interface Implementation
Support for Thread Security Required
The decode and encode functions must ensure thread security. Therefore, member or static variables cannot be added to cache intermediate data.
- Incorrect example: When multiple threads are started at the same time, the status of thread A is set to Failed while the status of thread B is set to Success. As a result, the status is incorrect, and the program running is abnormal.
public class ProtocolAdapter { private String status; @Override public ObjectNode decode(finalbyte[] binaryData) throws Exception { if (binaryData == null) { status = "Failed"; return null; } ObjectNode node; ...; status = "Success";//The thread is insecure. return node; } } - Correct example: Encoding and decoding are performed based on the input parameters, and the encoding and decoding library does not process services.
Explanation of the mid Field
The IoT platform delivers orders in sequence. However, the IoT platform does not respond to the order execution results in the same sequence as the delivered orders. The MID is used to associate the order execution result response with the delivered order. On the IoT platform, whether the MID is implemented affects the message flow.
- When the MID is implemented:

If the MID is implemented and the order execution result is reported successfully:
- The status (SUCCESSFUL/FAILED) in the order execution result is updated to the record of the order in the IoT platform database.
- The order execution result notification sent by the IoT platform to the NA server contains commandId.
- The query result of the NA server indicates that the status of the order is SUCCESSFUL/FAILED.
- When the MID is not implemented:

If the MID is not implemented and the order execution result is reported successfully:
- The status (SUCCESSFUL/FAILED) in the order execution result is not updated to the record of the order in the IoT platform database.
- The order execution result notification sent by the IoT platform to the NA server does not contain commandId.
- The query result of the NA server indicates that the final status of the order is DELIVERED.
The preceding two message flows are used to explain the function of the mid field. Some message flows are simplified in the figures.
In scenarios where whether orders are sent to the device is of concern but the order execution is not, the device and codec do not need to implement the mid field.
- Match the response with the order according to the sequence in which orders are delivered. In this way, when the IoT platform delivers multiple orders to the same device at the same time, the order execution result is matched with the delivered order incorrectly if packet loss occurs. Therefore, it is recommended that the NA server deliver only one order to the same device each time. After receiving the order execution result response, the NA server delivers the next order.
- The codec can add order-related information, such as an order code, to the resultDetail field of the order response to help identify the order. The NA server identifies the mapping between the order execution result response and the delivered order according to the information in the resultDetail field.
Do Not Use DirectMemory
The DirectMemory field directly calls the OS interface to apply for memory and is not controlled by the JVM. Improper use of the DirectMemory field may cause insufficient memory of the OS. Therefore, the DirectMemory cannot be used in codec plug-in code.
Example of improper use: Use UNSAFE.allocateMemory to apply for direct memory.
if ((maybeDirectBufferConstructor instanceof Constructor))
{
address = UNSAFE.allocateMemory(1L);
Constructor<?> directBufferConstructor;
...
}
else
{
...
}
Codec Input and Output Examples
The following table describes the definition of a service supported by a kind of water meter.
|
Service Type |
Property Name |
Property Description |
Property Type (Data Type) |
|---|---|---|---|
|
Battery |
- |
- |
- |
|
- |
batteryLevel |
Specifies the battery level in the unit of percent. The value ranges from 0 to 100. |
int |
|
Meter |
- |
- |
- |
|
- |
signalStrength |
Indicates the signal strength. |
int |
|
- |
currentReading |
Specifies the current read value. |
int |
|
- |
dailyActivityTime |
Specifies the daily activated communication duration. |
string |
The following shows the decode interface output for data reported by a device to the IoT platform.
{
"identifier": "12345678",
"msgType": "deviceReq",
"data": [
{
"serviceId": "Meter",
"serviceData": {
"currentReading": "46.3",
"signalStrength": 16,
"dailyActivityTime": 5706
},
"eventTime": "20160503T121540Z"
},
{
"serviceId": "Battery",
"serviceData": {
"batteryLevel": 10
},
"eventTime": "20160503T121540Z"
}
]
}
The following shows the encode interface input when the IoT platform receives data reported by the device and sends a response to the device.
{
"identifier": "123",
"msgType": "cloudRsp",
"request":[
1,
2
],
"errcode": 0,
"hasMore": 0
}
The following table describes the commands supported by a kind of water meter.
|
Basic Function |
Category |
Name |
Command Parameter |
Data Type |
Enumerated Value |
|---|---|---|---|---|---|
|
WaterMeter |
Water meter |
- |
- |
- |
- |
|
- |
CMD |
SET_TEMPERATURE_READ_PERIOD |
- |
- |
- |
|
- |
- |
- |
value |
int |
- |
|
- |
RSP |
SET_TEMPERATURE_READ_PERIOD_RSP |
- |
- |
- |
|
- |
- |
- |
result |
int |
The value 0 indicates success. The value 1 indicates invalid input. The value 2 indicates execution failed. |
The following shows the input parameters of the encode interface when the IoT platform sends an order to the device.
{
"identifier": "12345678",
"msgType": "cloudReq",
"serviceId": "WaterMeter",
"cmd": "SET_TEMPERATURE_READ_PERIOD",
"paras": {
"value": 4
},
"hasMore": 0
}
After the IoT platform receives a response from the device, the IoT platform invokes the decode interface for decoding. The decode interface output is as follows:
{
"identifier": "123",
"msgType": "deviceRsp",
"errcode": 0,
"body": {
"result": 0
}
}
Packaging the Codec
After the codec is developed, use the Maven to pack the codec into a JAR package and create it as a codec package.
Maven Packaging
- Open the DOS window and access the directory where the pom.xml file is located.
- Run mvn package.
- After BUILD SUCCESS is displayed in the DOS window, open the target folder in the same directory as the pom.xml file to obtain the .jar package.
The naming rule of the .jar package is as follows: device type-manufacturer ID-device model-version.jar, for example: WaterMeter-Huawei-NBIoTDevice-version.jar.

- The com directory stores class files.
- The META-INF directory stores description files of .jar packages under the OSGi framework, which are generated based on configurations in the pom.xml file.
- The OSGI-INF directory stores service configuration files and is used to register the codec as a service for the platform to call (only one .xml file can be called).
- Other .jar packages are .jar packages referenced by codecs.
Preparing a Codec Package
- Create a folder named package, which contains the preload/ sub-folder.
- Place the packaged .jar package in the preload/ folder.

- In the package folder, create the package-info.json file. The fields and templates in this file are described as follows:
Note: The package-info.json file is encoded using UTF-8 without BOM. Only English characters are supported.
Table 4 Description of fields in the package-info.json file Parameter
Description
Mandatory or Optional
specVersion
Specifies the version of the description file. The value is fixed at 1.0.
Yes
fileName
Specifies the name of the software package. The value is fixed at codec-demo.
Yes
version
Specifies the version number of the software package. The version of the package.zip file must be the same as the value of bundleVersion.
Yes
deviceType
Specifies the device type, which must be the same as that defined in the profile.
Yes
manufacturerName
Specifies the manufacturer name, which must be the same as that defined in the profile. Otherwise, the package-info.json file cannot be uploaded to the IoT platform.
Yes
platform
Specifies the platform type, which is the operating system of the IoT platform on which the codec package runs. The value is fixed at linux.
Yes
packageType
Specifies the software package type. This field is used to describe the IoT platform module where the codec is deployed. The value is fixed at CIGPlugin.
Yes
date
Specifies the time when a packet is sent. The format is as follows: yyyy-MM-dd HH-mm-ss. For example, 2017-05-06 20:48:59.
No
description
Specifies the self-defined description about the software package.
No
ignoreList
Specifies the list of bundles to be ignored. The default value is null.
Yes
bundles
Specifies the description of a bundle.
Note: A bundle is a .jar package in a compressed package. Only one bundle needs to be described.
Yes
Table 5 Description of the bundles field Parameter
Description
Mandatory or Optional
bundleName
Specifies the bundle name, which is consistent with the value of Bundle-SymbolicName in the pom.xml file.
Yes
bundleVersion
Specifies the bundle version, which must be the same as the value of version.
Yes
priority
Specifies the bundle priority. This parameter can be set to the default value 5.
Yes
fileName
Specifies the codec file name.
Yes
bundleDesc
Describes the bundle function.
Yes
versionDesc
Describes the functions and features of different versions.
Yes
Template of the package-info.json file
{ "specVersion":"1.0", "fileName":"codec-demo", "version":"1.0.0", "deviceType":"WaterMeter", "manufacturerName":"Huawei", "description":"codec", "platform":"linux", "packageType":"CIGPlugin", "date":"2017-02-06 12:16:59", "ignoreList":[], "bundles":[ { "bundleName": "WaterMeter-Huawei", "bundleVersion": "1.0.0", "priority":5, "fileName": "WaterMeter-Huawei-1.0.0.jar", "bundleDesc":"", "versionDesc":"" }] } - Select all files in the package folder and compress them into a package.zip file.
Note: The package.zip file cannot contain the package directory.
Last Article: Developing a Codec Using JavaScript
Next Article: Downloading and Uploading a Codec
Did this article solve your problem?
Thank you for your score!Your feedback would help us improve the website.