Skip to content

1. 传感器简介

模型清单

可使用下列名称或 ID 在 ThinkLink 中搜索对应模型。

模型类型名称id_name平台模型 ID
RPC[MPPT1050 GET] 22110mppt1050_get_22110107130783759495171
RPC[MPPT1050 SET] 22110mppt1050_set_22110107130783759495170
TemplateMPPT1050107130783759495172
Thing Model[MPPT1050]mppt1050_22110107130783759495168
Thing Model[LUMIAX-22110-MPPT1050资产] 区域聚合mppt1050_asset_22110119677174411063309
Parameter Thing Model[MPPT1050-PARA]mppt1050_para_22110107130783759495169

MPPT1050 是青岛天盈华智科技(SKYWISE)旗下 Lumiax 品牌生产的 MPPT 太阳能充放电控制器。设备采用 MPPT(最大功率点跟踪)技术,实现对太阳能电池板的高效能量采集,并管理蓄电池的充放电过程。设备支持多种蓄电池类型(Gel、AGM、Liquid、Lithium),适合小型离网光伏系统。

本次对接方案中,设备型号为 MPPT1050,业务代码为 22110,模板名称为 MPPT1050


2. 产品特点

  • MPPT 追踪效率 >99.9%
  • 充电转换效率 96.5%
  • 支持 Gel、AGM、Liquid、Lithium 多种电池类型
  • 铝合金外壳,IP67 防护等级
  • 工作温度范围 -35°C ~ +60°C
  • RS-485 Modbus-RTU 通讯接口
  • 最大充电电流 10A,系统电压 12V

3. 适用范围

  • 太阳能路灯系统远程监测
  • 离网光伏系统蓄电池充放电状态监控
  • 太阳能控制器工作状态实时查看

4. 采集器信息

4.1 硬件信息

本方案采集设备采用 KC21(内置电池版,CN470 频段)。

  • 设备型号:KC21(CN470 频段)
  • 接口:RS-485
  • 供电方式:内置电池 10800mAH
  • LoRaWAN Class:A
  • 防护等级:IP65

4.2 接线信息

电源与通讯接口

MPPT1050 通过 RS-485 接口与 KC21 连接。KC21 使用内置电池供电,无需外部电源。

  • MPPT1050 RS-485 A → KC21 RS-485 A
  • MPPT1050 RS-485 B → KC21 RS-485 B

控制器本体由太阳能电池板或蓄电池供电。


5. 数据采集

本方案中,通过 Modbus-RTU(功能码 0x04,读输入寄存器)读取控制器数据。每次采集周期执行 两次 Modbus 查询

  • 查询 1(地址 0x3033,5 个寄存器):状态 + 温度数据
  • 查询 2(地址 0x3045,17 个寄存器):功率/电能数据

所有模拟量采用 ×100 倍率(显示值 = 寄存器值 / 100),32 位功率/电能值由两个连续 16 位寄存器组合(L + H)。

平台侧约定:

  • 上传周期参数地址:70
  • 采集周期参数地址:74
  • Modbus 地址参数地址:150

5.1 寄存器定义

查询 1 — 状态 + 温度(5 个寄存器,起始地址 0x3033):

地址(HEX)地址(DEC)项目描述数据类型说明
0x303312339battery_statusuint16BED7-D4: 温度状态; D3-D0: 电压状态
0x303412340charge_statusuint16BED3-D2: 充电状态; D1: 故障; D0: 充电中
0x303512341discharge_statusuint16BED13-D12: 负载等级; D11: 短路; D0: 放电中
0x303612342temp_ambientuint16BE环境温度,/100 (°C)
0x303712343temp_internaluint16BE机内温度,/100 (°C)

查询 2 — 功率/电能数据(17 个寄存器,起始地址 0x3045):

地址(HEX)地址(DEC)项目描述数据类型说明
0x304512357battery_socuint16BE蓄电池电量 (%)
0x304612358battery_voltageuint16BE蓄电池电压,/100 (V)
0x304712359battery_currentint16BE蓄电池电流,/100 (A),正=充电,负=放电
0x304812360battery_power_luint16BE蓄电池功率 L 寄存器(低 16 位)
0x304912361battery_power_huint16BE蓄电池功率 H 寄存器(高 16 位)
0x304A12362load_voltageuint16BE负载电压,/100 (V)
0x304B12363load_currentuint16BE负载电流,/100 (A)
0x304C12364load_power_luint16BE负载功率 L 寄存器
0x304D12365load_power_huint16BE负载功率 H 寄存器
0x304E12366solar_voltageuint16BE太阳能电压,/100 (V)
0x304F12367solar_currentuint16BE太阳能电流,/100 (A)
0x305012368solar_power_luint16BE太阳能功率 L 寄存器
0x305112369solar_power_huint16BE太阳能功率 H 寄存器
0x305212370daily_charge_energyuint16BE日充电量,/100 (kWh)
0x305312371total_charge_energy_luint16BE总充电量 L 寄存器
0x305412372total_charge_energy_huint16BE总充电量 H 寄存器
0x305512373daily_discharge_energyuint16BE日用电量,/100 (kWh)

32 位功率/电能计算:

battery_power = ((battery_power_h << 16) | battery_power_l) / 100    (W)
load_power    = ((load_power_h << 16) | load_power_l) / 100           (W)
solar_power   = ((solar_power_h << 16) | solar_power_l) / 100         (W)
total_charge_energy = ((total_charge_energy_h << 16) | total_charge_energy_l) / 100  (kWh)

支持的 Modbus 功能码:

  • 0x02:读离散输入
  • 0x03:读保持寄存器
  • 0x04:读输入寄存器
  • 0x05:写单线圈
  • 0x06:写单寄存器
  • 0x10:写多寄存器

5.2 状态位定义

battery_status(0x3033):

含义
D7-D400H温度正常
D7-D401H高温保护
D3-D000H电压正常
D3-D001H过压
D3-D002H欠压
D3-D003H过放

charge_status(0x3034):

含义
D60/1手动充电禁止(0=正常,1=禁止)
D50/1昼/夜(0=白天,1=夜晚)
D3-D200未充电
D3-D201浮充
D3-D202升压充电
D3-D203均衡充电
D10/1故障(0=正常,1=故障)
D00/1充电中(1=充电中)

discharge_status(0x3035):

含义
D13-D1200轻载
D13-D1201中载
D13-D1202额定负载
D13-D1203过载
D110/1短路保护(1=动作)
D00/1放电中(1=放电中)

6. EdgeBus 模型

6.1 EB 配置参数

主要配置参数如下:

  • name: "mppt1050"
  • port: 22
  • version: "0x87"
  • dataType: "0x10"
  • upPeriodIndex: 70
  • Modbus 默认串口参数:
    • BaudRate: 9600
    • StopBits: 1
    • DataBits: 8
    • Checkbit: NONE
  • BzType: 22110
  • BzVersion: 1
  • Battery: true(内置电池,Class A)

6.2 EB 代码

typescript
import { Buffer } from "buffer";
import { buildOtaFile } from "@EBSDK/run";
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 } from "@EBSDK/otaConfig";

const eventInfo: UserConfUPItem[] = [
  {
    name: "mppt1050", port: 22, version: "0x87", dataType: "0x10", upPeriodIndex: 70,
    quInfo: [
      {
        protocol: "modbus", addr: "0x01", code: "0x04", periodIndex: 70,
        indexAPP: 150, indexCMD: 0, copySize: 1, isLast: false,
        listVal: [
          { start: "12339", end: "12343" }
        ]
      },
      {
        protocol: "modbus", addr: "0x01", code: "0x04", periodIndex: 70,
        indexAPP: 150, indexCMD: 0, copySize: 1, isLast: true,
        listVal: [
          { start: "12357", end: "12373" }
        ]
      }
    ]
  }
];

let otaConfig = getOtaConfig({
  BaudRate: 9600,
  StopBits: 1,
  DataBits: 8,
  Checkbit: CheckbitEnum.NONE,
  Battery: true,
  ConfirmDuty: 60,
  BzType: 22110,
  BzVersion: 1
});

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 说明

当前 EB 逻辑说明:

  • EdgeBus 通过 Port 22 上报业务数据。
  • 先执行查询 1(0x04,地址 0x3033,5 个寄存器),读取状态和温度数据。
  • 再执行查询 2(0x04,地址 0x3045,17 个寄存器),读取功率/电能数据。查询 2 的 isLast: true 标记触发上行。
  • 上传周期与采集周期分离:
    • 70 为上报周期(upPeriodIndex
    • 74 为采集周期(periodIndex
  • Modbus 地址由 APP 参数中的 150 管理。
  • 模拟量采用 uint16BE/int16BE 类型,decimal: 2 表示 /100 倍率。
  • 32 位功率/电能值由 L + H 寄存器组合后在物模型中运算。

7. 物模型

7.1 物模型基本信息

数据物模型

  • 名称:[MPPT1050]
  • id Name:mppt1050_22110

参数物模型

  • 名称:[MPPT1050-PARA]
  • id Name:mppt1050_para_22110

7.2 上行帧结构

数据上行帧结构如下:

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

帧结构说明:

  • Byte 0: tag[0] = 0x87(version)
  • Byte 1: tag[1] = 0x10(dataType)
  • Byte 2: 状态字节
  • Byte 3: 保留
  • Byte 4: 电池电压
  • Byte 5: 保留
  • Byte 6-15: 查询 1 结果(5 个寄存器 × 2 字节 = 10 字节)
  • Byte 16-49: 查询 2 结果(17 个寄存器 × 2 字节 = 34 字节)

字段定义如下:

Index字段名类型说明
6battery_statusuint16BE蓄电池状态(位掩码)
8charge_statusuint16BE充电状态(位掩码)
10discharge_statusuint16BE放电状态(位掩码)
12temp_ambientuint16BE环境温度,decimal:2 (°C)
14temp_internaluint16BE机内温度,decimal:2 (°C)
16battery_socuint16BE蓄电池电量 (%)
18battery_voltageuint16BE蓄电池电压,decimal:2 (V)
20battery_currentint16BE蓄电池电流,decimal:2 (A)
22battery_power_luint16BE蓄电池功率 L 寄存器
24battery_power_huint16BE蓄电池功率 H 寄存器
26load_voltageuint16BE负载电压,decimal:2 (V)
28load_currentuint16BE负载电流,decimal:2 (A)
30load_power_luint16BE负载功率 L 寄存器
32load_power_huint16BE负载功率 H 寄存器
34solar_voltageuint16BE太阳能电压,decimal:2 (V)
36solar_currentuint16BE太阳能电流,decimal:2 (A)
38solar_power_luint16BE太阳能功率 L 寄存器
40solar_power_huint16BE太阳能功率 H 寄存器
42daily_charge_energyuint16BE日充电量,decimal:2 (kWh)
44total_charge_energy_luint16BE总充电量 L 寄存器
46total_charge_energy_huint16BE总充电量 H 寄存器
48daily_discharge_energyuint16BE日用电量,decimal:2 (kWh)

7.3 物模型脚本

7.3.1 数据物模型脚本

javascript
let    port=msg?.userdata?.port || null;
if(port!=22) return null
let frameInfo={
    port:22, dataLen:50,rssi:true,battery:4,
    tagList:[{   index:0, tag:0x87}, {   index:1, tag:0x10}]
}
let appInfo = [
    {   name: "battery_status", field_name: "battery_status", unit:"", index: 6, type: "uint16BE"},
    {   name: "charge_status", field_name: "charge_status", unit:"", index: 8, type: "uint16BE"},
    {   name: "discharge_status", field_name: "discharge_status", unit:"", index: 10, type: "uint16BE"},
    {   name: "temp_ambient", field_name: "temp_ambient", unit:"°C", index: 12, type: "uint16BE",decimal:2},
    {   name: "temp_internal", field_name: "temp_internal", unit:"°C", index: 14, type: "uint16BE",decimal:2},
    {   name: "battery_soc", field_name: "battery_soc", unit:"%", index: 16, type: "uint16BE"},
    {   name: "battery_voltage", field_name: "battery_voltage", unit:"V", index: 18, type: "uint16BE",decimal:2},
    {   name: "battery_current", field_name: "battery_current", unit:"A", index: 20, type: "int16BE",decimal:2},
    {   name: "battery_power_l", field_name: "battery_power_l", unit:"", index: 22, type: "uint16BE"},
    {   name: "battery_power_h", field_name: "battery_power_h", unit:"", index: 24, type: "uint16BE"},
    {   name: "load_voltage", field_name: "load_voltage", unit:"V", index: 26, type: "uint16BE",decimal:2},
    {   name: "load_current", field_name: "load_current", unit:"A", index: 28, type: "uint16BE",decimal:2},
    {   name: "load_power_l", field_name: "load_power_l", unit:"", index: 30, type: "uint16BE"},
    {   name: "load_power_h", field_name: "load_power_h", unit:"", index: 32, type: "uint16BE"},
    {   name: "solar_voltage", field_name: "solar_voltage", unit:"V", index: 34, type: "uint16BE",decimal:2},
    {   name: "solar_current", field_name: "solar_current", unit:"A", index: 36, type: "uint16BE",decimal:2},
    {   name: "solar_power_l", field_name: "solar_power_l", unit:"", index: 38, type: "uint16BE"},
    {   name: "solar_power_h", field_name: "solar_power_h", unit:"", index: 40, type: "uint16BE"},
    {   name: "daily_charge_energy", field_name: "daily_charge_energy", unit:"kWh", index: 42, type: "uint16BE",decimal:2},
    {   name: "total_charge_energy_l", field_name: "total_charge_energy_l", unit:"", index: 44, type: "uint16BE"},
    {   name: "total_charge_energy_h", field_name: "total_charge_energy_h", unit:"", index: 46, type: "uint16BE"},
    {   name: "daily_discharge_energy", field_name: "daily_discharge_energy", unit:"kWh", index: 48, type: "uint16BE",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 {
        telemetry_data: tdata,
        server_attrs: null,
        shared_attrs: null
    }
}
tdata.battery_power = ((tdata.battery_power_h << 16) | tdata.battery_power_l) / 100
tdata.load_power = ((tdata.load_power_h << 16) | tdata.load_power_l) / 100
tdata.solar_power = ((tdata.solar_power_h << 16) | tdata.solar_power_l) / 100
tdata.total_charge_energy = ((tdata.total_charge_energy_h << 16) | tdata.total_charge_energy_l) / 100
delete tdata.battery_power_l; delete tdata.battery_power_h
delete tdata.load_power_l; delete tdata.load_power_h
delete tdata.solar_power_l; delete tdata.solar_power_h
delete tdata.total_charge_energy_l; delete tdata.total_charge_energy_h
return {
    telemetry_data: tdata,
    server_attrs: null,
    shared_attrs: null
}

7.3.2 参数物模型脚本

javascript
let    port=msg?.userdata?.port || null;
const rpcName="mppt1050_set_22110"
let paraDef= {
    app_70: {name: "period_up", field_name: "period_up", unit: "s", type: "uint32LE"},

    app_150: {name: "addr_modbus", field_name: "addr_modbus", unit: "", type: "uint8"}
}
if (port!==214) {
    let checkData=Utils.paraCheck(rpcName,device.server_attrs,device.shared_attrs)
    return {
        server_attrs: checkData.sdata,
        actions:checkData.actions,
    }
}
let pdata=(new PayloadParser({
    device:device,
    msg:msg,
    paraInfo:paraDef,
})).paras()
let checkData= Utils.paraCheck(rpcName,device.server_attrs,pdata)
return {
    telemetry_data: pdata,
    server_attrs: checkData.sdata,
    shared_attrs: pdata,
    actions: checkData.actions,
}

8. 第三方平台数据订阅

8.1 MQTT Topic

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

8.2 上报示例数据

根据本模板的物模型字段,整理后的示例数据如下:

json
{
    "eui": "6353012af10a9429",
    "active_time": "2026-05-12T08:35:48.000Z",
    "thingModelId": "22110",
    "thingModelIdName": "mppt1050_22110",
    "telemetry_data": {
        "snr": 13.5,
        "rssi": -51,
        "battery": 3.37,
        "battery_status": 0,
        "charge_status": 1,
        "discharge_status": 1,
        "temp_ambient": 25.5,
        "temp_internal": 28.3,
        "battery_soc": 85,
        "battery_voltage": 13.2,
        "battery_current": 2.5,
        "battery_power": 33.0,
        "load_voltage": 13.1,
        "load_current": 0.5,
        "load_power": 6.55,
        "solar_voltage": 18.5,
        "solar_current": 2.1,
        "solar_power": 38.85,
        "daily_charge_energy": 0.25,
        "total_charge_energy": 12.5,
        "daily_discharge_energy": 0.08
    }
}

说明:

  • thingModelId 实际应以平台生成值为准,这里用业务代码 22110 作示例。
  • thingModelIdName 对应数据物模型 mppt1050_22110
  • battery_current 为正表示充电,为负表示放电。
  • battery_powerload_powersolar_powertotal_charge_energy 为 32 位组合值(L + H 寄存器组合后 /100)。

9. RPC

9.1 RPC 名称

配置参数 RPC

  • 名称:[MPPT1050 SET] 22110
  • id Name:mppt1050_set_22110

读取参数 RPC

  • 名称:[MPPT1050 GET] 22110
  • id Name:mppt1050_get_22110

9.2 参数定义

参数地址名称field_name单位类型说明
app_70period_upperiod_upsuint32LE上传周期

| app_150 | addr_modbus | addr_modbus | | uint8 | Modbus 地址 |

9.3 RPC 代码

配置参数 RPC

javascript
let classMode = (device && device.shared_attrs && device.shared_attrs.class_mode) || "ClassA";
let intervalms = classMode === "ClassA" ? 0 : 2000;
const rpcName="mppt1050_set_22110"
let paraDef= {
    app_70: {name: "period_up", field_name: "period_up", unit: "s", type: "uint32LE"},

    app_150: {name: "addr_modbus", field_name: "addr_modbus", 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 msgQue

读取参数 RPC

javascript
let classMode = (device && device.shared_attrs && device.shared_attrs.class_mode) || "ClassA";
let sleepMs = classMode === "ClassA" ? 500 : 5000;
let paraDef= {
    app_70: {name: "period_up", field_name: "period_up", unit: "s", type: "uint32LE"},

    app_150: {name: "addr_modbus", field_name: "addr_modbus", 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. 模板选择

在 ThinkLink 平台中搜索模板: MPPT1050

或按业务类型查找: 22110 / MPPT 太阳能充放电控制器 / RS485 Modbus 光伏监控


补充说明

  1. 厂家默认 Modbus 参数为:
    • 地址:1
    • 波特率:9600
    • 数据位:8
    • 停止位:1
    • 校验:无校验
  2. 蓄电池电流为有符号整型(int16BE):正值表示充电,负值表示放电。
  3. 32 位功率/电能值由 L 寄存器(低 16 位)和 H 寄存器(高 16 位)组合,公式:(H << 16) | L,结果再除以 100。
  4. 蓄电池 SOC 无需缩放(/1),与其它模拟量不同。
  5. DTU 采用内置电池供电(KC21,10800mAH),Class A 模式,默认上传周期 300 秒。
  6. 控制器默认支持 Gel、AGM、Liquid、Lithium 四种电池类型。