Device Model: BGT-SMPS
Model catalog
Search for these models in ThinkLink by name or ID.
| Model type | Name | id_name | Platform model ID |
|---|---|---|---|
| RPC | [BGT-SMPS GET] para | bgt_smps_get_25101 | 102256370513874949 |
| RPC | [BGT-SMPS SET] para | bgt_smps_set_25101 | 102256081450831877 |
| Template | BGT-SMPS-25101 | 102256727507865605 | |
| Thing Model | [BGT-SMPS] | bgt_smps_25101 | 102254589725642757 |
| Thing Model | [BGT-25101-BGT-SMPS资产] 区域聚合 | bgt_smps_asset_25101 | 119677174411063296 |
| Parameter Thing Model | [BGT-SMPS-PARA] | bgt_smps_para_25101 | 102255300635004933 |
Template Name: BGT-SMPS-25101
Business Code: 25101
1. Sensor Introduction
BGT-SMPS is a multi-in-one soil parameter sensor developed by GXHY / Beijing Guoxinhuayuan Technology Co., Ltd. It supports the collection of soil temperature, soil moisture, soil EC, soil salinity, soil nitrogen, soil phosphorus, soil potassium, soil pH, and other parameters.
The sensor uses an RS-485 interface and communicates through the standard Modbus-RTU protocol. The default address is 01, and the default communication parameters are 9600, 8N1. The sensor can be directly inserted into or buried in soil, making it suitable for agriculture, environmental monitoring, greenhouses, soil testing, and other scenarios.
In this integration solution, BGT-SMPS is connected to the platform through KC21 + EdgeBus + LoRaWAN + ThinkLink. KC21 reads the Modbus data from the sensor through RS-485 and then reports the data to the ThinkLink platform via LoRaWAN.
2. Product Features
| Feature | Description |
|---|---|
| Multi-parameter integration | Supports soil temperature, moisture, EC, salinity, nitrogen, phosphorus, potassium, pH, and other parameters |
| Modbus-RTU communication | Standard RS-485 / Modbus-RTU protocol, easy to connect to data collectors |
| High measurement accuracy | High measurement accuracy for temperature, moisture, EC, pH, and other parameters |
| Fast response | Short stabilization time after power-on, enabling fast data collection |
| Good protection performance | Epoxy resin sealing, waterproof rating IP68 |
| Corrosion-resistant probe | Probe material: 316L stainless steel |
| Suitable for buried installation | The probe can be fully inserted into soil, or the entire device can be buried in soil |
3. Application Scope
BGT-SMPS is suitable for the following scenarios:
| Application Scenario | Description |
|---|---|
| Water-saving agricultural irrigation | Monitors soil temperature, moisture, and fertility parameters for irrigation strategy optimization |
| Meteorological monitoring | Works with agricultural weather stations for soil environment monitoring |
| Environmental monitoring | Used for soil environment data collection |
| Greenhouses | Monitors crop root-zone environmental parameters |
| Flower and vegetable cultivation | Used for refined planting management |
| Grassland and pasture | Monitors soil moisture and nutrient status |
| Rapid soil testing | Quickly obtains multi-parameter soil data on site |
| Scientific experiments | Used for data collection in agriculture, ecology, soil, and related experiments |
4. Collector Information
4.1 Hardware Information
| Item | Content |
|---|---|
| Collector model | KC21 |
| Communication method | LoRaWAN |
| Sensor interface | RS-485 |
| Collection protocol | Modbus-RTU |
| Power supply | Battery powered |
| Peripheral power supply | KC21 provides approximately 15.1 V to the sensor |
| Business code | 25101 |
| Template name | BGT-SMPS-25101 |
| Number of collected devices | Supports 3 soil sensors by default |
| Sensor address configuration | Addresses are stored in APP parameter areas 150, 151, and 152 |
| Period configuration | Period parameters are 70, 74, and 78 respectively |
4.2 Wiring Information
Power and Communication Interface
The BGT-SMPS sensor uses a 4-core cable. The wiring definition is as follows:
| Wire Color | Definition | Description |
|---|---|---|
| Red | Power+ | DC 9–24 V / the newer manual specifies DC 5–24 V |
| Black | GND | Power negative |
| Yellow | RS485_A | RS-485 A |
| Green | RS485_B | RS-485 B |
In this solution:
| KC21 Side | BGT-SMPS Side |
|---|---|
| Peripheral power output + | Red wire Power+ |
| GND | Black wire GND |
| RS485 A | Yellow wire RS485_A |
| RS485 B | Green wire RS485_B |
Sensor Interface
| Item | Content |
|---|---|
| Interface type | RS-485 |
| Communication protocol | Modbus-RTU |
| Default address | 0x01 |
| Baud rate | 9600 |
| Data bits | 8 |
| Stop bits | 1 |
| Parity | None |
5. Data Collection
In this solution, the following registers are read through Modbus:
| Parameter | Modbus Register Address | PLC Address | Data Type | Description |
|---|---|---|---|---|
| Soil temperature | 0x0000 | 40001 | int16 | 1 decimal place; negative values use two’s complement |
| Soil moisture | 0x0001 | 40002 | uint16 | 1 decimal place, 0–100% |
| Soil EC | 0x0002 | 40003 | uint16 | No decimal places, unit: μS/cm |
| Soil salinity | 0x0003 | 40004 | uint16 | No decimal places, unit: mg/L |
| Soil nitrogen | 0x0004 | 40005 | uint16 | No decimal places, unit: mg/kg |
| Soil phosphorus | 0x0005 | 40006 | uint16 | No decimal places, unit: mg/kg |
| Soil potassium | 0x0006 | 40007 | uint16 | No decimal places, unit: mg/kg |
| Soil pH | 0x0007 | 40008 | uint16 | 2 decimal places |
Example sensor read command:
01 03 00 00 00 08 44 0CMeaning:
| Byte | Content | Description |
|---|---|---|
| 01 | Slave address | Default address 1 |
| 03 | Function code | Read holding registers |
| 00 00 | Start register | Starts from 0x0000 |
| 00 08 | Number of registers | Reads 8 consecutive registers |
| 44 0C | CRC16 | Modbus CRC check |
5.1 Register Definition
| No. | Parameter Name | Field Name | Unit | Index | Type | Coefficient | Decimal Places |
|---|---|---|---|---|---|---|---|
| 1 | Soil temperature | soil_temperature | ℃ | 6 | int16be | 0.1 | 1 |
| 2 | Soil moisture | soil_moisture | % | 8 | uint16be | 0.1 | 1 |
| 3 | Soil EC | soil_ec | μS/cm | 10 | uint16be | 1 | 0 |
| 4 | Soil salinity | soil_salinity | mg/L | 12 | uint16be | 1 | 0 |
| 5 | Soil nitrogen | soil_nitrogen | mg/kg | 10 | uint16be | 0.088 | 3 |
| 6 | Soil phosphorus | soil_phosphorus | mg/kg | 10 | uint16be | 0.044 | 3 |
| 7 | Soil potassium | soil_potassium | mg/kg | 10 | uint16be | 0.066 | 3 |
| 8 | Soil pH | soil_ph | pH | 20 | uint16be | 0.01 | 2 |
Note: In this project’s thing model, the soil nitrogen, phosphorus, and potassium fields use the EC data position index: 10 and are calculated through coefficients: 0.55 × 0.16, 0.55 × 0.08, and 0.55 × 0.12, respectively.
5.2 Status Bit Definition
No separate sensor status bit is defined in this solution.
The thing model script handles collection timeout status as follows:
if((tdata?.status&0x02)===0x02) {
const status=tdata.status
tdata={ ...(device.telemetry_data?.[thingModelId] ?? {})}
tdata.status=status
}Logic description:
| Status Bit | Meaning | Processing Method |
|---|---|---|
| status & 0x02 | Collection timeout / time out | Keep the original telemetry data and only update the status value |
6. EdgeBus Model
This device itself is not a LoRaWAN sensor. It is an RS-485 / Modbus-RTU sensor, so the built-in EdgeBus of KC21 is required to execute Modbus collection logic.
In this solution, EdgeBus is configured with 3 sensor channels by default:
| Channel | Period Parameter Index | Address Parameter Index |
|---|---|---|
| Channel 1 | 70 | 150 |
| Channel 2 | 74 | 151 |
| Channel 3 | 78 | 152 |
6.1 EB Configuration Parameters
| Parameter | Value |
|---|---|
| SwVersion | 31 |
| BaudRate | 9600 |
| StopBits | 1 |
| DataBits | 8 |
| Checkbit | NONE |
| Battery | true |
| ConfirmDuty | 60 |
| BzType | 25101 |
| BzVersion | 11 |
| LoRaWAN Port | 22 |
| version | 0x88 |
| dataType | 0x01 |
| Protocol | Modbus |
| Modbus function code | 0x03 |
| Default sensor address | Read from APP parameter area |
| APP address parameters | 150, 151, 152 |
| Query period parameters | 70, 74, 78 |
6.2 EB Code
import {Buffer} from "buffer";
import {buildOtaFile} from "@EBSDK/run";
import {LoraUpEvent} 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} from "@EBSDK/otaConfig";
////////////////////////////////////////////////////////////////////////////////////////
let eventInfo: UserConfUPItem[] = [];
let otaConfig = getOtaConfig({
SwVersion:31,
BaudRate: 9600,
StopBits: 1,
DataBits: 8,
Checkbit: CheckbitEnum.NONE,
Battery: true,
ConfirmDuty: 60,
BzType: 25101,
BzVersion: 11
})
for(let i=0;i<3;i++) {
let userConf:UserConfUPItem={
name: "BGT_SMPS_6CH", version: "0x88",dataType: "0x01", port: 22, upPeriod: "10y",
quInfo: [
{
protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 70,
indexAPP: 150, indexCMD: 0, copySize: 1,
isLast:true,
listVal: [
{ start: "0", end: "7" }
]
}
]
}
userConf.name="BGT_SMPS_6CH_"+i.toString();
userConf.quInfo[0].periodIndex=70+i*4
userConf.quInfo[0].indexAPP = 150+i;
eventInfo.push(userConf)
}
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:
- KC21 connects to the BGT-SMPS soil multi-parameter sensor through RS-485.
- EdgeBus uses the Modbus-RTU protocol to read sensor data.
- Three collection events are generated by default, corresponding to 3 sensor channels.
- The 485 address of each sensor is read from the APP parameter area:
- Channel 1:
app_150 - Channel 2:
app_151 - Channel 3:
app_152
- Channel 1:
- The collection period of each sensor is controlled by the following parameters:
- Channel 1:
app_70 - Channel 2:
app_74 - Channel 3:
app_78
- Channel 1:
- Each collection uses Modbus function code
0x03to read 8 consecutive registers. - Uplink uses LoRaWAN port 22. The frame header tags are:
0x880x01
- The value at
subDevice.index = 5in the uplink data is used to distinguish different sub-devices / sensors.
7. Thing Model
7.1 Basic Thing Model Information
| Item | Content |
|---|---|
| Thing model name | BGT-SMPS-25101 |
| Template name | BGT-SMPS-25101 |
| Business code | 25101 |
| Device type | Multi-in-one soil sensor |
| Communication method | KC21 + LoRaWAN |
| Uplink port | 22 |
| Data length | 22 |
| Parse RSSI | Yes |
| Parse battery voltage | Yes, battery: 4 |
| Sub-device identification | index: 5, type: uint8 |
| Uplink frame identifier | 0x88 0x01 |
7.2 Uplink Frame Structure
| Byte Index | Content | Description |
|---|---|---|
| 0 | 0x88 | Business version / frame identifier |
| 1 | 0x01 | Data type |
| 4 | battery | Battery voltage field |
| 5 | subDevice | Sub-device number |
| 6–7 | soil_temperature | Soil temperature |
| 8–9 | soil_moisture | Soil moisture |
| 10–11 | soil_ec | Soil EC |
| 12–13 | soil_salinity | Soil salinity |
| 20–21 | soil_ph | Soil pH |
Note: According to the provided thing model script, soil nitrogen, phosphorus, and potassium are all calculated based on the data at index: 10 using different conversion coefficients.
7.3 Thing Model Script
let port = msg?.userdata?.port || null;
if(port != 22) return null
let frameInfo = {
port: 22,
dataLen: 22,
rssi: true,
battery: 4,
subDevice: { index: 5, type: "uint8" },
tagList: [
{ index: 0, tag: 0x88 },
{ index: 1, tag: 0x01 }
]
};
let appInfo = [
{ name: "Soil Temperature", field_name: "soil_temperature", unit: "℃", index: 6, type: "int16be", coefficient: 0.1, decimal: 1 },
{ name: "Soil Moisture", field_name: "soil_moisture", unit: "%", index: 8, type: "uint16be", coefficient: 0.1, decimal: 1 },
{ name: "Soil EC", field_name: "soil_ec", unit: "μS/cm", index: 10, type: "uint16be", coefficient: 1, decimal: 0 },
{ name: "Soil Salinity", field_name: "soil_salinity", unit: "mg/L", index: 12, type: "uint16be", coefficient: 1, decimal: 0 },
{ name: "Soil Nitrogen", field_name: "soil_nitrogen", unit: "mg/kg", index: 10, type: "uint16be", coefficient: 0.088, decimal: 3 },
{ name: "Soil Phosphorus", field_name: "soil_phosphorus", unit: "mg/kg", index: 10, type: "uint16be", coefficient: 0.044, decimal: 3 },
{ name: "Soil Potassium", field_name: "soil_potassium", unit: "mg/kg", index: 10, type: "uint16be", coefficient: 0.066, decimal: 3 },
{ name: "Soil pH", field_name: "soil_ph", unit: "pH", index: 20, type: "uint16be", coefficient: 0.01, decimal: 2 }
];
let payParser = new PayloadParser({
device: device,
msg: msg,
frameInfo: frameInfo,
appInfo: appInfo,
})
let tdata = payParser.telemetry()
if((tdata?.status & 0x02) === 0x02) {
const status = tdata.status
tdata = { ...(device.telemetry_data?.[thingModelId] ?? {}) }
tdata.status = status
}
return {
sub_device: tdata == undefined ? null : tdata.addr,
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
{
"eui": "6353012af10a9331",
"active_time": "2026-05-03T07:36:00.000Z",
"thingModelId": "BGT-SMPS-25101",
"thingModelIdName": "bgt_smps_25101",
"telemetry_data": {
"snr": 13.5,
"rssi": -51,
"battery": 3.37,
"addr": 1,
"soil_temperature": 23.6,
"soil_moisture": 35.6,
"soil_ec": 1234,
"soil_salinity": 1356,
"soil_nitrogen": 108.592,
"soil_phosphorus": 54.296,
"soil_potassium": 81.444,
"soil_ph": 6.86,
"status": 0
}
}9. RPC
9.1 RPC Name
| Type | Name |
|---|---|
| Parameter setting RPC | bgt_smps_set_25101 |
| Parameter validation name | bgt_smps_25101_set |
Note: During actual platform configuration, it is recommended to keep the RPC name and the parameter thing model validation name consistent to avoid call failures caused by naming inconsistency.
9.2 Parameter Definition
| Parameter Name | Field Name | APP Index | Type | Unit | Description |
|---|---|---|---|---|---|
| pwron_delay | pwron_delay | 38 | uint16le | ms | Peripheral power-on delay |
| period1 | period1 | 70 | uint32LE | s | Collection period of sensor channel 1 |
| period2 | period2 | 74 | uint32LE | s | Collection period of sensor channel 2 |
| period3 | period3 | 78 | uint32LE | s | Collection period of sensor channel 3 |
| addr1 | addr1 | 150 | uint8 | - | 485 address of sensor channel 1 |
| addr2 | addr2 | 151 | uint8 | - | 485 address of sensor channel 2 |
| addr3 | addr3 | 152 | uint8 | - | 485 address of sensor channel 3 |
9.3 RPC Code
9.3.1 Set Parameters
let classMode = (device && device.shared_attrs && device.shared_attrs.class_mode) || "ClassA";
let intervalms = classMode === "ClassA" ? 0 : 2000;
const rpcName = "bgt_smps_set_25101"
let paraDef = {
app_38: { name: "pwron_delay", field_name: "pwron_delay", unit: "ms", index: 38, type: "uint16le" },
app_70: { name: "period1", field_name: "period1", unit: "s", type: "uint32LE" },
app_74: { name: "period2", field_name: "period2", unit: "s", type: "uint32LE" },
app_78: { name: "period3", field_name: "period3", unit: "s", type: "uint32LE" },
app_150: { name: "addr1", field_name: "addr1", unit: "", type: "uint8" },
app_151: { name: "addr2", field_name: "addr2", unit: "", type: "uint8" },
app_152: { name: "addr3", field_name: "addr3", unit: "", type: "uint8" },
}
let frames = RPCHelper.buildFrame({
paraDef: paraDef,
params: params
});
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)
logger.info("set para")
let msgQue = Utils.makeParaSetMSG({
device: device,
classMode: classMode,
rpcName: rpcName,
params: params,
paraDownBuffer: dnBuffer,
extraAppBuffer: redoBuffer
})
if (msgQue.length == 0) return null
return msgQue9.3.2 Read Parameters
let classMode = (device && device.shared_attrs && device.shared_attrs.class_mode) || "ClassA";
let sleepMs = classMode === "ClassA" ? 500 : 5000;
let paraDef = {
app_38: { name: "pwron_delay", field_name: "pwron_delay", unit: "ms", index: 38, type: "uint16le" },
app_70: { name: "period1", field_name: "period1", unit: "s", type: "uint32LE" },
app_74: { name: "period2", field_name: "period2", unit: "s", type: "uint32LE" },
app_78: { name: "period3", field_name: "period3", unit: "s", type: "uint32LE" },
app_150: { name: "addr1", field_name: "addr1", unit: "", type: "uint8" },
app_151: { name: "addr2", field_name: "addr2", unit: "", type: "uint8" },
app_152: { name: "addr3", field_name: "addr3", unit: "", type: "uint8" },
}
let frames = RPCHelper.buildFrame({
paraDef: paraDef,
params: params
});
let msg = RPCHelper.makeMSG({
msgType: Utils.msgType.paras,
device: device,
dnBuffer: frames.readBuffer,
sleepTime: sleepMs,
})
return [msg]10. Template Selection
Search for the following template in the ThinkLink platform:
BGT-SMPS-25101Or search by business type:
25101Recommended search keywords:
| Search Method | Keyword |
|---|---|
| By template name | BGT-SMPS-25101 |
| By device model | BGT-SMPS |
| By business code | 25101 |
| By application type | Soil multi-parameter sensor |
| By collection method | KC21 / RS-485 / Modbus / LoRaWAN |