Skip to content

Below is the standard integration document for connecting the Jingquan Instrument LXSY-15E cold water meter to ThinkLink using the CJ188 cold water meter protocol. In this solution, KC22 is used as the collector and connects to the water meter through M-Bus. Since this solution is essentially a template-based adaptation for CJ188 cold water meters, other brands of water meters that support or are compatible with the CJ/T 188 cold water meter protocol can also be adapted based on this template. The related basic information is derived from the device materials and CJ188 protocol documents you provided.

Model catalog

Search for these models in ThinkLink by name or ID.

Model typeNameid_namePlatform model ID
TemplateCJ188 COOL WATER92529888803688453

1. Sensor Overview

The sensor integrated in this project is the Jingquan Instrument LXSY-15E cold water meter, with the English name JQYB, business code 23103, and platform template name CJ188 COOL WATER.
This device is a cold water meter. On the protocol side, it belongs to the cold water meter category under the CJ/T 188-2004 system, and its meter type code is 0x10.

2. Product Features

  1. It uses the CJ188 cold water meter protocol, which is well standardized and convenient for standardized integration.
  2. It supports access to the collector through the M-Bus interface.
  3. It supports reading the meter address, reading water consumption data, setting the meter address, and valve open/close control.
  4. On the platform side, the corresponding EB logic, thing model, parameter model, and RPC template have already been organized, making it convenient to replicate in batches for other similar cold water meter projects.
  5. This template has good general applicability and is suitable for adapting other brands of water meters compatible with the CJ188 cold water meter protocol.

3. Application Scope

This solution is suitable for the following scenarios:

  • Remote meter reading for residential cold water meters
  • Cold water metering in campuses / buildings
  • Wireless retrofit of existing CJ188 cold water meters
  • Projects requiring unified thing model parsing, data subscription, and remote parameter configuration through ThinkLink

Note:
This solution uses LXSY-15E as an example, but in essence it is a general CJ188 cold water meter template. For other brands, as long as their protocol frame format, data identifiers, address encoding method, and status bit definitions are compatible with or close to the CJ188 cold water meter template, adaptation can be carried out on this basis. This conclusion is an engineering summary based on the template information and protocol structure you provided.

4. Collector Information

4.1 Hardware Information

The collector used is KC22.

Known hardware information:

  • Device model: KC22
  • Interface: M-Bus
  • Power supply: Battery-powered
  • Battery capacity: 10800mAh
  • External power supply: M-Bus

4.2 Wiring Information

Power Supply and Communication Interface

  • KC22 is powered by battery.
  • KC22 communicates with the cold water meter via M-Bus and provides bus-side driving/power capability to the slave device.
  • According to the protocol document, the M-Bus interface supports one master to one slave or one master to multiple slaves, and the slave connection is non-polarized. The master must provide power to the bus during operation. Communication is half-duplex. The supported transmission baud rate ranges from 300bps to 9600bps. The template used in this project actually uses 2400bps, 8 data bits, 1 stop bit, and even parity.

Sensor Interface

  • Sensor interface type: M-Bus
  • KC22 external power supply mode: M-Bus bus power supply
  • In this solution, the LXSY-15E cold water meter is connected to KC22 via M-Bus.

5. Data Collection

In this solution, water meter data is read through the CJ188 protocol, not Modbus.

Therefore, it should be described as follows:

The following data is read through the CJ188 protocol:

  • Meter address
  • Instantaneous flow
  • Total accumulated flow
  • Status word
  • Valve status
  • Reading fault status

According to the protocol document, meter reading uses:

  • CTR = 0x01
  • DI = 0x901F

When the meter replies:

  • CTR = 0x81
  • The data area contains instantaneous flow, accumulated flow, reserved time fields, status words, etc.

5.1 Register Definition

The CJ188 protocol is not based on the Modbus register model, so it is more accurate here to understand it as data identifier definitions.

  • Read meter data: DI0 DI1 = 90 1F
  • Read meter address: DI0 DI1 = 81 0A
  • Write meter address: DI0 DI1 = A0 18
  • Operate valve: DI0 DI1 = A0 17

Meter Data Read Frame Definition

Request frame:

  • Start byte: 68h
  • Meter type T: 10h for cold water meter
  • Address: A0-A6
  • Control code: 01h
  • Data length: 03h
  • Data identifier: 90 1F
  • Sequence number: SER
  • Checksum: CS
  • End byte: 16h

Response frame:

  • Control code: 81h
  • Data length: 16h
  • Data identifier: 90 1F
  • The data area includes:
    • 14-17: Instantaneous flow reading
    • 18: Instantaneous flow unit, , corresponding to 2C
    • 19-22: Total accumulated flow reading
    • 23: Accumulated flow unit, , corresponding to 2C
    • 24-30: Reserved time bytes
    • 31: Status byte S0
    • 32: Status byte S1

5.2 Status Bit Definition

The CJ188 status word consists of 2 bytes, among which S0 is the commonly used one.

According to the protocol:

  • bit0-bit1: Valve status
    • 00: Open
    • 01: Closed
    • 11: Abnormal
  • bit2: Reading fault
    • 0: Normal
    • 1: Abnormal
  • Other bits: Reserved

6. EdgeBus Model

6.1 EB Configuration Parameters

The following parameters are extracted from the EB configuration you provided:

  • SwVersion: 31
  • BaudRate: 2400
  • StopBits: 1
  • DataBits: 8
  • Checkbit: EVEN
  • Battery: true
  • ConfirmDuty: 60
  • BzType: 23103
  • BzVersion: 17

6.2 EB Code

Below is the EB code you provided:

typescript
import { Buffer } from "buffer";
import { buildOtaFile } from "@EBSDK/run";
import {
    ActionAfertExpr, CalcData,
    CrcMode,
    CvtRule,
    EBBuffer,
    ExprCondition,
    LoraUpEvent,
    QueryEvent, SetUpCovDataType,QuItemBase,
    QuItemModBus,QuItemCJ188, UserConfQueryItem, EventConfig, Utils
} from "@EBSDK/EBCompiler/all_variable";
import { EBModel } from "@EBSDK/EBCompiler/EBModel/EBModel";
import { EventInfoItem } from "@EBSDK/EBCompiler/plugins/EBHelper";
import type { UserConfUPItem } from "@EBSDK/EBCompiler/plugins/EBHelper";
import { CheckbitEnum, getOtaConfig, HwTypeEnum, UpgrdTypeEnum } from "@EBSDK/otaConfig";

let otaConfig = getOtaConfig({
    SwVersion:31,
    BaudRate: 2400,
    StopBits: 1,
    DataBits: 8,
    Checkbit: CheckbitEnum.EVEN,
    Battery: true,
    ConfirmDuty: 60,
    BzType: 23103, // required ,2 bytes
    BzVersion: 18 // required,1 bytes
})
const eventInfo:UserConfUPItem[]=[
    {
        name:"cj188-coolwater",port:22,version:"0x87",dataType:"0x03",upPeriodIndex:70,
        indexAddr:5,addrSize:7,
        quInfo:[
            {   protocol:"CJ188",code:"0x0103",periodIndex:70,
                indexAPP:150,indexCMD:2,copySize:7,preamble:10,
                cmd:"0x68 10 AA AA AA AA AA AA AA 01 03 90 1F 12 0C 16",
                ack:"0x68 10 01 00 00 05 08 00 00 81 16 90 1F 00   00 23 01 00 2C 00 00 00 00 2C 00 00 00 00 00 00 00 00 00 48 16",
                listVal:[
                    { start:"14", end:"23"}
                ]
            }
        ]
    }
]
const MODBUS_TT = (ebModel: EBModel) => {
    for (let i=0; i<eventInfo.length; i++){
        let event=new EventInfoItem(eventInfo[i]);
        event.upEventSetup()
        event.eventInstall()
    }
    return JSON.stringify(ebModel, null, 2)
}
buildOtaFile(import.meta.url, otaConfig, MODBUS_TT)

6.3 Description

Current EB logic description:

  1. KC22 periodically initiates water meter data reading through M-Bus / CJ188.
  2. The core query message is the read meter data command, namely 68 10 ... 01 03 90 1F ... 16.
  3. In the EB logic, key checks are performed on the returned frame:
    • Start byte 0x68
    • DI check 0x90 0x1F
    • End byte 0x16
    • CRC uses the SUM method
  4. From the collected response frame, EB copies the following into the LoRaWAN uplink:
    • Address segment
    • Instantaneous flow and total flow data segments
    • Status word
  5. The LoRaWAN uplink port is 22.
  6. The core purpose of this logic is to map the raw CJ188 meter response frame into a standard uplink data format directly parseable by ThinkLink.
  7. In the parameter indexes, periodIndex=70 and addrIndex=150 correspond exactly to app_70 and app_150 in the subsequent parameter thing model, indicating that both the collection period and meter address can be dynamically configured through platform parameters / RPC downlink.

7. Thing Model

7.1 Basic Information of the Thing Model

Telemetry Thing Model

  • Name: [CJ188-COOLWATER]
  • ID Name: cj188_coolwater_23103

Parameter Thing Model

  • Name: [CJ188-COOLWATER-PARA]
  • ID Name: cj188_coolwater_para_23103

Uplink port:

  • port: 22

Frame definition:

javascript
let frameInfo = {
        port:22,
        dataLen:-1,
        rssi:true,
        battery:4,
        tagList:[
            { index:0, tag:0x87 },
            { index:1, tag:0x03 }
        ]
    }

Field definition:

javascript
let appInfo = [
        { name:"addr", field_name:"addr", unit:"", index:5, type:"hexLE7" },
        { name:"instantflow", field_name:"instantflow", unit:"m³", index:12, type:"bcdLE4", coefficient:0.01,decimal:2 },
        { name:"totalflow", field_name:"totalflow", unit:"m³", index:17, type:"bcdLE4", coefficient:0.01,decimal:2 },
        { name:"status", field_name:"status", unit:"", index:22, type:"uint8" }
    ]

Description:

  • addr: Meter address, 7 bytes, low byte first
  • instantflow: Instantaneous flow, 4-byte BCD little-endian, coefficient 0.01
  • totalflow: Total accumulated flow, 4-byte BCD little-endian, coefficient 0.01
  • status: Status byte S0, 1 byte

7.3 Thing Model Script

javascript
let frameInfo = {
    port: 22,
    dataLen: -1,
    rssi: true,
    battery: 4,
    tagList: [
        { index: 0, tag: 0x87 },
        { index: 1, tag: 0x03 }
    ]
}

let appInfo = [
    { name: "addr", field_name: "addr", unit: "", index: 5, type: "hexLE7" },
    { name: "instantflow", field_name: "instantflow", unit: "m³", index: 12, type: "bcdLE4", coefficient: 0.01, decimal: 2 },
    { name: "totalflow", field_name: "totalflow", unit: "m³", index: 17, type: "bcdLE4", coefficient: 0.01, decimal: 2 },
    { name: "status", field_name: "status", unit: "", index: 22, type: "uint8" }
]

let tdata = {}
let payParser = new PayloadParser({
    device: device,
    msg: msg,
    frameInfo: frameInfo,
    appInfo: appInfo,
})
tdata = payParser.telemetry()

return {
    telemetry_data: tdata,
    server_attrs: null,
    shared_attrs: null
}

8. Third-Party Platform Data Subscription

8.1 MQTT Topic

latex
/v32/{Organization Account}/tkl/up/telemetry/{eui}

8.2 Example Reported Data

According to the thing model fields of this template, the corrected example data is suggested as follows:

json
{
    "eui": "6353012af10a9331",
    "active_time": "2026-04-05T10:00:00.000Z",
    "thingModelId": "cj188_coolwater_23103",
    "thingModelIdName": "CJ188-COOLWATER",
    "telemetry_data": {
        "addr": "01000005080000",
        "snr": 13.5,
        "rssi": -51,
        "battery": 3.37,
        "instantflow": 123.45,
        "totalflow": 5657.00,
        "status": 1
    }
}

9. RPC

9.1 RPC Names

Parameter Configuration RPC

  • Name: [CJ188 SET] para 23103
  • ID Name: cj188_coolwater_set_23103

Parameter Reading RPC

  • Name: [CJ188 GET] para 23103
  • ID Name: cj188_coolwater_get_23103

9.2 Parameter Definition

The parameters are defined as follows:

  • retries
    • Field name: retries
    • Type: uint8
  • pwron_delay
    • Field name: pwron_delay
    • Unit: ms
    • Index: 38
    • Type: uint16le
  • period
    • Field name: period
    • Unit: m
    • Index: 70
    • Type: uint32LE
  • addr
    • Field name: addr
    • Index: 150
    • Type: hexLE7

Additional notes:

  • In the configuration RPC, period is converted to seconds, i.e. params.period * 60
  • During parameter report parsing, it is divided by 60 again and restored for display in minutes

This indicates that the platform expects to use minutes uniformly as the external configuration unit for the sampling period.

9.3 RPC Code

Parameter Configuration RPC

javascript
let classMode = (device && device.shared_attrs && device.shared_attrs.class_mode) || "ClassA";
const rpcName="cj188_coolwater_set_23103"
let paraDef= {
    app_34: {name: "retries", field_name: "retries", unit: "",  type: "uint8"},
    app_38: { name: "pwron_delay", field_name: "pwron_delay", unit: "ms", index: 38, type: "uint16le" },
    app_70: {name: "period", field_name: "period", unit: "m",  type: "uint32LE"},
    app_150: {name: "addr", field_name: "addr", unit: "", type: "hexLE7"},
}
logger.info("set paras")
let paraInput = {...params}
paraInput.retries=2
paraInput.pwron_delay=1000
paraInput.period=params.period*60
let frames=RPCHelper.buildFrame({
    paraDef:paraDef,
    params:paraInput});
let redoBuffer=RPCHelper.redo()
let dnBuffer=Buffer.alloc(frames.writeBuffer.length+frames.readBuffer.length);
frames.writeBuffer.copy(dnBuffer,0)
frames.readBuffer.copy(dnBuffer,frames.writeBuffer.length)
let msgQue=Utils.makeParaSetMSG({
    device:device,
    classMode:classMode,
    rpcName:rpcName,
    params:params,
    paraDownBuffer:dnBuffer,
    extraAppBuffer:redoBuffer
})
if (msgQue.length==0)  return null
return msgQue

Parameter Reading RPC

javascript
let paraDef= {
    app_34: {name: "retries", field_name: "retries", unit: "",  type: "uint8"},
    app_38: { name: "pwron_delay", field_name: "pwron_delay", unit: "ms", index: 38, type: "uint16le" },
    app_70: {name: "period", field_name: "period", unit: "m",  type: "uint32LE"},
    app_150: {name: "addr", field_name: "addr", unit: "", type: "hexLE7"},
}
let frames=RPCHelper.buildFrame({
    paraDef:paraDef,
    params:params});
let msg=RPCHelper.makeMSG({
    msgType:Utils.msgType.paras,
    device:device,
    dnBuffer:frames.readBuffer,
    sleepTime:0,
})
return [msg]

10. Template Selection

In the ThinkLink platform, search for the following templates:

  • CJ188 COOL WATER
  • cj188_coolwater_23103
  • cj188_coolwater_para_23103
  • cj188_coolwater_set_23103
  • cj188_coolwater_get_23103

Or search by business type:

  • Cold water meter
  • CJ188 cold water meter
  • Business code 23103

Additional Notes

  1. At the protocol layer, this solution uses CJ188, not Modbus. Therefore, the term “register” in this document should be understood as CJ188 data identifiers and data area definitions.
  2. This template is highly suitable as a general CJ188 cold water meter template. In actual projects, if other brands of cold water meters have the following characteristics:

Then in most cases, this template can be reused with only minor adjustments for address or vendor-specific differences.

- Meter type remains **cold water meter 0x10**
- Reading DI is **90 1F**
- Address encoding is **7-byte BCD little-endian**
- Status word rules are compatible