1. 传感器简介
模型清单
可使用下列名称或 ID 在 ThinkLink 中搜索对应模型。
| 模型类型 | 名称 | id_name | 平台模型 ID |
|---|---|---|---|
| RPC | [MPPT1050 GET] 22110 | mppt1050_get_22110 | 107130783759495171 |
| RPC | [MPPT1050 SET] 22110 | mppt1050_set_22110 | 107130783759495170 |
| Template | MPPT1050 | 107130783759495172 | |
| Thing Model | [MPPT1050] | mppt1050_22110 | 107130783759495168 |
| Thing Model | [LUMIAX-22110-MPPT1050资产] 区域聚合 | mppt1050_asset_22110 | 119677174411063309 |
| Parameter Thing Model | [MPPT1050-PARA] | mppt1050_para_22110 | 107130783759495169 |
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) | 项目描述 | 数据类型 | 说明 |
|---|---|---|---|---|
| 0x3033 | 12339 | battery_status | uint16BE | D7-D4: 温度状态; D3-D0: 电压状态 |
| 0x3034 | 12340 | charge_status | uint16BE | D3-D2: 充电状态; D1: 故障; D0: 充电中 |
| 0x3035 | 12341 | discharge_status | uint16BE | D13-D12: 负载等级; D11: 短路; D0: 放电中 |
| 0x3036 | 12342 | temp_ambient | uint16BE | 环境温度,/100 (°C) |
| 0x3037 | 12343 | temp_internal | uint16BE | 机内温度,/100 (°C) |
查询 2 — 功率/电能数据(17 个寄存器,起始地址 0x3045):
| 地址(HEX) | 地址(DEC) | 项目描述 | 数据类型 | 说明 |
|---|---|---|---|---|
| 0x3045 | 12357 | battery_soc | uint16BE | 蓄电池电量 (%) |
| 0x3046 | 12358 | battery_voltage | uint16BE | 蓄电池电压,/100 (V) |
| 0x3047 | 12359 | battery_current | int16BE | 蓄电池电流,/100 (A),正=充电,负=放电 |
| 0x3048 | 12360 | battery_power_l | uint16BE | 蓄电池功率 L 寄存器(低 16 位) |
| 0x3049 | 12361 | battery_power_h | uint16BE | 蓄电池功率 H 寄存器(高 16 位) |
| 0x304A | 12362 | load_voltage | uint16BE | 负载电压,/100 (V) |
| 0x304B | 12363 | load_current | uint16BE | 负载电流,/100 (A) |
| 0x304C | 12364 | load_power_l | uint16BE | 负载功率 L 寄存器 |
| 0x304D | 12365 | load_power_h | uint16BE | 负载功率 H 寄存器 |
| 0x304E | 12366 | solar_voltage | uint16BE | 太阳能电压,/100 (V) |
| 0x304F | 12367 | solar_current | uint16BE | 太阳能电流,/100 (A) |
| 0x3050 | 12368 | solar_power_l | uint16BE | 太阳能功率 L 寄存器 |
| 0x3051 | 12369 | solar_power_h | uint16BE | 太阳能功率 H 寄存器 |
| 0x3052 | 12370 | daily_charge_energy | uint16BE | 日充电量,/100 (kWh) |
| 0x3053 | 12371 | total_charge_energy_l | uint16BE | 总充电量 L 寄存器 |
| 0x3054 | 12372 | total_charge_energy_h | uint16BE | 总充电量 H 寄存器 |
| 0x3055 | 12373 | daily_discharge_energy | uint16BE | 日用电量,/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-D4 | 00H | 温度正常 |
| D7-D4 | 01H | 高温保护 |
| D3-D0 | 00H | 电压正常 |
| D3-D0 | 01H | 过压 |
| D3-D0 | 02H | 欠压 |
| D3-D0 | 03H | 过放 |
charge_status(0x3034):
| 位 | 值 | 含义 |
|---|---|---|
| D6 | 0/1 | 手动充电禁止(0=正常,1=禁止) |
| D5 | 0/1 | 昼/夜(0=白天,1=夜晚) |
| D3-D2 | 00 | 未充电 |
| D3-D2 | 01 | 浮充 |
| D3-D2 | 02 | 升压充电 |
| D3-D2 | 03 | 均衡充电 |
| D1 | 0/1 | 故障(0=正常,1=故障) |
| D0 | 0/1 | 充电中(1=充电中) |
discharge_status(0x3035):
| 位 | 值 | 含义 |
|---|---|---|
| D13-D12 | 00 | 轻载 |
| D13-D12 | 01 | 中载 |
| D13-D12 | 02 | 额定负载 |
| D13-D12 | 03 | 过载 |
| D11 | 0/1 | 短路保护(1=动作) |
| D0 | 0/1 | 放电中(1=放电中) |
6. EdgeBus 模型
6.1 EB 配置参数
主要配置参数如下:
name: "mppt1050"port: 22version: "0x87"dataType: "0x10"upPeriodIndex: 70- Modbus 默认串口参数:
BaudRate: 9600StopBits: 1DataBits: 8Checkbit: NONE
BzType: 22110BzVersion: 1Battery: true(内置电池,Class A)
6.2 EB 代码
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 上行帧结构
数据上行帧结构如下:
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 | 字段名 | 类型 | 说明 |
|---|---|---|---|
| 6 | battery_status | uint16BE | 蓄电池状态(位掩码) |
| 8 | charge_status | uint16BE | 充电状态(位掩码) |
| 10 | discharge_status | uint16BE | 放电状态(位掩码) |
| 12 | temp_ambient | uint16BE | 环境温度,decimal:2 (°C) |
| 14 | temp_internal | uint16BE | 机内温度,decimal:2 (°C) |
| 16 | battery_soc | uint16BE | 蓄电池电量 (%) |
| 18 | battery_voltage | uint16BE | 蓄电池电压,decimal:2 (V) |
| 20 | battery_current | int16BE | 蓄电池电流,decimal:2 (A) |
| 22 | battery_power_l | uint16BE | 蓄电池功率 L 寄存器 |
| 24 | battery_power_h | uint16BE | 蓄电池功率 H 寄存器 |
| 26 | load_voltage | uint16BE | 负载电压,decimal:2 (V) |
| 28 | load_current | uint16BE | 负载电流,decimal:2 (A) |
| 30 | load_power_l | uint16BE | 负载功率 L 寄存器 |
| 32 | load_power_h | uint16BE | 负载功率 H 寄存器 |
| 34 | solar_voltage | uint16BE | 太阳能电压,decimal:2 (V) |
| 36 | solar_current | uint16BE | 太阳能电流,decimal:2 (A) |
| 38 | solar_power_l | uint16BE | 太阳能功率 L 寄存器 |
| 40 | solar_power_h | uint16BE | 太阳能功率 H 寄存器 |
| 42 | daily_charge_energy | uint16BE | 日充电量,decimal:2 (kWh) |
| 44 | total_charge_energy_l | uint16BE | 总充电量 L 寄存器 |
| 46 | total_charge_energy_h | uint16BE | 总充电量 H 寄存器 |
| 48 | daily_discharge_energy | uint16BE | 日用电量,decimal:2 (kWh) |
7.3 物模型脚本
7.3.1 数据物模型脚本
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 参数物模型脚本
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 上报示例数据
根据本模板的物模型字段,整理后的示例数据如下:
{
"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_power、load_power、solar_power、total_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_70 | period_up | period_up | s | uint32LE | 上传周期 |
| app_150 | addr_modbus | addr_modbus | | uint8 | Modbus 地址 |
9.3 RPC 代码
配置参数 RPC
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
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 光伏监控
补充说明
- 厂家默认 Modbus 参数为:
- 地址:1
- 波特率:9600
- 数据位:8
- 停止位:1
- 校验:无校验
- 蓄电池电流为有符号整型(int16BE):正值表示充电,负值表示放电。
- 32 位功率/电能值由 L 寄存器(低 16 位)和 H 寄存器(高 16 位)组合,公式:
(H << 16) | L,结果再除以 100。 - 蓄电池 SOC 无需缩放(/1),与其它模拟量不同。
- DTU 采用内置电池供电(KC21,10800mAH),Class A 模式,默认上传周期 300 秒。
- 控制器默认支持 Gel、AGM、Liquid、Lithium 四种电池类型。