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 type | Name | id_name | Platform model ID |
|---|---|---|---|
| Template | CJ188 COOL WATER | 92529888803688453 |
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
- It uses the CJ188 cold water meter protocol, which is well standardized and convenient for standardized integration.
- It supports access to the collector through the M-Bus interface.
- It supports reading the meter address, reading water consumption data, setting the meter address, and valve open/close control.
- 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.
- 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.
Reading-related items
- 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, m³, corresponding to 2C
- 19-22: Total accumulated flow reading
- 23: Accumulated flow unit, m³, 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:
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:
- KC22 periodically initiates water meter data reading through M-Bus / CJ188.
- The core query message is the read meter data command, namely
68 10 ... 01 03 90 1F ... 16. - 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
- Start byte
- From the collected response frame, EB copies the following into the LoRaWAN uplink:
- Address segment
- Instantaneous flow and total flow data segments
- Status word
- The LoRaWAN uplink port is 22.
- 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.
- In the parameter indexes,
periodIndex=70andaddrIndex=150correspond exactly toapp_70andapp_150in 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
7.2 Uplink Frame Structure
Uplink port:
port: 22
Frame definition:
let frameInfo = {
port:22,
dataLen:-1,
rssi:true,
battery:4,
tagList:[
{ index:0, tag:0x87 },
{ index:1, tag:0x03 }
]
}Field definition:
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 firstinstantflow: Instantaneous flow, 4-byte BCD little-endian, coefficient0.01totalflow: Total accumulated flow, 4-byte BCD little-endian, coefficient0.01status: Status byteS0, 1 byte
7.3 Thing Model Script
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
/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:
{
"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
- Field name:
pwron_delay- Field name:
pwron_delay - Unit:
ms - Index:
38 - Type:
uint16le
- Field name:
period- Field name:
period - Unit:
m - Index:
70 - Type:
uint32LE
- Field name:
addr- Field name:
addr - Index:
150 - Type:
hexLE7
- Field name:
Additional notes:
- In the configuration RPC,
periodis 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
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 msgQueParameter Reading RPC
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
- 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.
- 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