Skip to content

1. Sensor Overview

The AQ3485 is a dual-channel temperature sensor manufactured by AoQing (奥青). It uses a Modbus-RTU / RS-485 interface to simultaneously read refrigerator temperature and defrost temperature from a cold-storage unit. This integration connects the sensor to the ThinkLink platform via a KC21 (battery-powered) DTU, with optional COV (change-of-value) triggered uplinks.

Integration details: model AQ3485, business code 21306, template name AQ3485.


2. Product Features

  • Dual-channel temperature acquisition: refrigerator temperature + defrost temperature
  • RS-485 interface, Modbus-RTU protocol
  • Default slave address 0x01, baud rate 9600, 8N1
  • Independent COV thresholds for each temperature channel
  • Low power design — battery-powered KC21 DTU

3. Applications

The AQ3485 is suited for the following scenarios:

  • Cold cabinet monitoring in supermarkets and convenience stores
  • Refrigeration room and defrost temperature monitoring in cold-storage warehouses
  • Cold chain logistics temperature management
  • Cold storage equipment monitoring in food and pharmaceutical industries

4. Data Collector

4.1 Hardware Specifications

This solution uses the KC21 (battery version) DTU.

  • Model: KC21
  • Interface: RS-485
  • Power supply: Battery
  • LoRaWAN Class: A

4.2 Wiring

Power and Communication Interface

The KC21 battery version supplies power to the sensor over the RS-485 bus:

  • KC21 A terminal → RS-485A (sensor)
  • KC21 B terminal → RS-485B (sensor)

Sensor Interface

The AQ3485 uses a standard RS-485 interface with default settings: 9600 baud, 8N1, no parity. Connect directly to the KC21 RS-485 port.


5. Data Acquisition

This solution reads the following Modbus registers:

  • 0x0000: Refrigerator temperature (Int16BE, × 0.1 °C)
  • 0x0001: Defrost temperature (Int16BE, × 0.1 °C)

Platform parameter address conventions:

  • Upload interval: App address 70
  • Acquisition interval: App address 74
  • Power-on delay: App address 38

5.1 Register Map

Address (hex)DescriptionData TypeNotes
0x0000Refrigerator temperatureInt16BE (signed, big-endian)Raw value × 0.1 = actual temperature (°C)
0x0001Defrost temperatureInt16BE (signed, big-endian)Raw value × 0.1 = actual temperature (°C)

6. EdgeBus Model

6.1 EB Configuration

ParameterValue
name"AQ3485"
port21
version"0x85"
dataType"0x03"
upPeriodIndex70
periodIndex (acquisition)74
BaudRate9600
StopBits1
DataBits8
CheckbitNONE
Batterytrue
BzType21306
BzVersion12
SwVersion31

6.2 EB Code

typescript
import {Buffer} from "buffer";
import {buildOtaFile} from "@EBSDK/run";
import {EBModel, EventInfoItem, LoraUpEvent, UserConfUPItem, CheckbitEnum, getOtaConfig, HwTypeEnum} from "@EBSDK/EBCompiler/all_variable";
////////////////////////////////////////////////////////////////////////////////////////
const eventInfo:UserConfUPItem[]=[
    {
        name:"AQ3485",port:21, version:"0x85",dataType:"0x03",upPeriodIndex:70,
        quInfo:[
            {
            protocol:"modbus",code:"0x03", periodIndex:74,//addr:"0x01",
            indexAPP:150, indexCMD:0, copySize:1,isLast:false,
            listVal:[
                { start: "1", end: "1" ,covType:"Int16BE",covAppIndex:110},  // refrigerator temp, coefficient=0.1
                { start: "2", end: "2" ,covType:"Int16BE",covAppIndex:112}   // defrost temp, coefficient=0.1
                ]
            }
        ]
    }
]
let otaConfig = getOtaConfig({
    SwVersion:31,
    BaudRate: 9600,
    StopBits: 1,
    DataBits: 8,
    Checkbit: CheckbitEnum.NONE,
    Battery: true,
    ConfirmDuty: 60,
    BzType: 21306,
    BzVersion: 12
})
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 Notes

EB logic summary:

  • Uplinks are sent on port 21.
  • A single query event reads registers 0x0000 and 0x0001 in one Modbus FC03 request (2 contiguous registers, 4 bytes total).
  • Upload interval and acquisition interval are separated:
    • App address 70 = upload interval
    • App address 74 = acquisition interval
  • COV triggers: refrigerator temperature (App 110) and defrost temperature (App 112) each have independent COV thresholds. An uplink is triggered immediately when a temperature change exceeds its threshold.
  • The Modbus slave address is managed by App parameter 150 and can be changed at runtime.
  • Data format: Int16BE (signed, big-endian); divide by 10 to get temperature in °C.

7. Thing Model

7.1 Thing Model Information

Telemetry Thing Model

  • Name: [AQ3485]
  • id Name: aq3485_21306
frameInfo: { port: 21, dataLen: -1, rssi: true, battery: 4 }
tagList:   [{ index: 0, tag: 0x85 }, { index: 1, tag: 0x03 }]

Uplink frame field layout (business data starts at byte 6):

Offset (index)FieldTypeUnitDescription
0versionuint8Protocol version, fixed 0x85
1dataTypeuint8Business type, fixed 0x03
2covStatusuint8COV trigger status
3statusuint8Query event status
4batteryuint8Battery level (4-byte encoded)
5addruint8Modbus slave address
6–7humidityInt16BE°C × 0.1Refrigerator temperature (field_name: humidity)
8–9temperatureInt16BE°C × 0.1Defrost temperature (field_name: temperature)

Field naming note: The humidity field contains refrigerator temperature, and temperature contains defrost temperature. These names reflect legacy platform registration and do not affect functionality.

7.3 Thing Model Script

7.3.1 Telemetry Thing Model Script

javascript
import {PayloadParser} from '#tklHelper';

function payload_parser({device, msg, thingModelId, noticeAttrs}) {
let    port=msg?.userdata?.port || null;
    let frameInfo={
        port:21, dataLen:-1,rssi:true,battery:4,
        tagList:[{   index:0, tag:0x85}, {   index:1, tag:0x03}]
    }
    let appInfo = [
        {   name: "humidity", field_name: "humidity", unit:"℃", index: 6, type: "int16BE",coefficient:0.1,decimal:1},
        {   name: "temperature", field_name: "temperature", unit:"℃", index: 8, type: "int16BE",coefficient:0.1,decimal:1},
    ]
    let paraInfo= {
        app_70: {name: "periodUP", field_name: "periodUP", unit: "s",  type: "uint32LE"},
        app_74: {name: "periodRead", field_name: "periodRead", unit: "s", type: "uint32LE"},
        app_110: {name: "cov_temperature", field_name: "cov_temperature", unit: "℃", type: "int16BE",coefficient:0.1,decimal:1},
        app_112: {name: "cov_humidity", field_name: "cov_humidity", unit: "℃", type: "int16BE",coefficient:0.1,decimal:1},
        app_150: {name: "addr", field_name: "addr", unit: "", type: "uint8"},
    }
    let pdata={}
    let tdata={}
    let payParser=new PayloadParser({
        device:device,
        msg:msg,
        frameInfo:frameInfo,
        appInfo:appInfo,
        paraInfo:paraInfo,
    })
    if (port===214) {
        pdata=payParser.paras()
        tdata=null
    }else  {
        tdata= payParser.telemetry()
        pdata=null
    }
    return {
        telemetry_data: tdata,
        server_attrs: null,
        shared_attrs: pdata
    }
}

8. Third-Party Platform Data Subscription

8.1 MQTT Topic

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

8.2 Example Payload

json
{
    "eui": "6353012af10a9331",
    "active_time": "2026-02-05T08:35:48.000Z",
    "thingModelId": "87554295540486149",
    "thingModelIdName": "aq3485_21306",
    "telemetry_data": {
        "snr": 10.5,
        "rssi": -65,
        "battery": 3.21,
        "humidity": -5.2,
        "temperature": 18.3
    }
}

Notes:

  • thingModelId uses the platform-assigned value (87554295540486149).
  • thingModelIdName maps to the telemetry thing model aq3485_21306.
  • humidity represents refrigerator temperature (°C); example value -5.2 °C is a typical cold-storage reading.
  • temperature represents defrost temperature (°C); example value 18.3 °C represents ambient temperature during a defrost cycle.

9. RPC

9.1 RPC Names

Set Parameters RPC

  • Name: [AQ3485 SET] para
  • id Name: AQ3485_21306_set

Get Parameters RPC

  • Name: [AQ3485 GET] para
  • id Name: aq3485_21306_get

9.2 Parameter Definition

App AddressParameter Namefield_nameUnitTypeDescription
app_38pwron_delaypwron_delaymsuint16lePower-on delay before querying the sensor
app_70periodUPperiodUPsuint32LEUpload interval
app_74periodReadperiodReadsuint32LEAcquisition interval
app_110cov_temperaturecov_temperature°Cint16BEDefrost temp COV threshold (× 0.1 °C)
app_112cov_humiditycov_humidity°Cint16BERefrigerator temp COV threshold (× 0.1 °C)
app_150addraddruint8Modbus slave address (default: 1)

9.3 RPC Scripts

Set Parameters RPC

javascript
import {RPCHelper, Utils} from '#tklHelper';
import {Buffer} from "buffer";

function rpc_script({device, params, alarms, logger}) {
let paraDef= {
        app_38: { name: "pwron_delay", field_name: "pwron_delay", unit: "ms", index: 38, type: "uint16le" },
        app_70: {name: "periodUP", field_name: "periodUP", unit: "s",  type: "uint32LE"},
        app_74: {name: "periodRead", field_name: "periodRead", unit: "s", type: "uint32LE"},
        app_110: {name: "cov_temperature", field_name: "cov_temperature", unit: "℃", type: "int16BE",coefficient:0.1,decimal:1},
        app_112: {name: "cov_humidity", field_name: "cov_humidity", unit: "℃", type: "int16BE",coefficient:0.1,decimal:1},
        app_150: {name: "addr", field_name: "addr", 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+redoBuffer.length);
    frames.writeBuffer.copy(dnBuffer,0)
    frames.readBuffer.copy(dnBuffer,frames.writeBuffer.length)
    redoBuffer.copy(dnBuffer,frames.writeBuffer.length+frames.readBuffer.length);
    logger.info("set para")
    let msg=RPCHelper.makeMSG({
        msgType:Utils.msgType.paras,
        device:device,
        dnBuffer:dnBuffer,
        sleepTime:0,
    })
    return [msg]
}

Get Parameters RPC

javascript
import {RPCHelper, Utils} from '#tklHelper';

function rpc_script({device, params, alarms, logger}) {
let paraDef= {
        app_38: { name: "pwron_delay", field_name: "pwron_delay", unit: "ms", index: 38, type: "uint16le" },
        app_70: {name: "periodUP", field_name: "periodUP", unit: "s",  type: "uint32LE"},
        app_74: {name: "periodRead", field_name: "periodRead", unit: "s", type: "uint32LE"},
        app_110: {name: "cov_temperature", field_name: "cov_temperature", unit: "℃", type: "int16BE",coefficient:0.1,decimal:1},
        app_112: {name: "cov_humidity", field_name: "cov_humidity", unit: "℃", type: "int16BE",coefficient:0.1,decimal:1},
        app_150: {name: "addr", field_name: "addr", 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:0,
    })
    return [msg]
}

10. Template Selection

Search for the following template in the ThinkLink platform: AQ3485

Or browse by business type: 21306 / Dual-Channel Temperature Sensor / RS-485 Modbus Cold-Storage Monitoring


11. Additional Notes

  1. Field naming: The humidity field contains refrigerator temperature; temperature contains defrost temperature. These names stem from legacy platform registration.
  2. Manufacturer default communication settings: address 1, baud rate 9600, 8 data bits, no parity, 1 stop bit.
  3. COV thresholds: cov_temperature (app_110) applies to defrost temperature; cov_humidity (app_112) applies to refrigerator temperature. Both are in units of 0.1 °C — a threshold of 10 means 1.0 °C.
  4. Power-on delay: pwron_delay (app_38) is the wait time in milliseconds after KC21 powers on before the first Modbus query is issued.