Skip to content

1. Sensor Overview

Model catalog

Search for these models in ThinkLink by name or ID.

Model typeNameid_namePlatform model 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

The MPPT1050 is an MPPT solar charge controller manufactured by Qingdao SKYWISE Technology Co., Ltd. under the Lumiax brand. The device employs MPPT (Maximum Power Point Tracking) technology for efficient solar energy harvesting and manages battery charging/discharging processes. It supports multiple battery types (Gel, AGM, Liquid, Lithium) and is ideal for small off-grid photovoltaic systems.

In this integration solution, the device model is MPPT1050, the business code is 22110, and the template name is MPPT1050.


2. Product Features

  • MPPT tracking efficiency >99.9%
  • Charge conversion efficiency 96.5%
  • Supports Gel, AGM, Liquid, and Lithium battery types
  • Aluminum housing, IP67 protection rating
  • Operating temperature range -35°C to +60°C
  • RS-485 Modbus-RTU communication interface
  • Max charge current 10A, system voltage 12V

3. Application Scope

  • Remote monitoring of solar street light systems
  • Battery charge/discharge status monitoring for off-grid PV systems
  • Real-time solar controller working status monitoring

4. Collector Information

4.1 Hardware Information

The data acquisition device used in this solution is KC21 (built-in battery version, CN470 frequency band).

  • Device model: KC21 (CN470 frequency band)
  • Interface: RS-485
  • Power supply: Built-in battery 10800mAH
  • LoRaWAN Class: A
  • Protection rating: IP65

4.2 Wiring Information

Power Supply and Communication Interface

The MPPT1050 connects to KC21 via RS-485 interface. KC21 is powered by its internal battery — no external power supply is needed.

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

The controller itself is powered by the solar panel or battery.


5. Data Acquisition

In this solution, controller data is read via Modbus-RTU (function code 0x04, read input registers). Each collection cycle performs two Modbus queries:

  • Query 1 (address 0x3033, 5 registers): Status and temperature data
  • Query 2 (address 0x3045, 17 registers): Power and energy data

All analog values use ×100 scale factor (display value = register value / 100). 32-bit power/energy values are combined from two consecutive 16-bit registers (L + H).

Platform-side parameter conventions:

  • Upload period parameter address: 70
  • Collection period parameter address: 74
  • Modbus address parameter address: 150

5.1 Register Definitions

Query 1 — Status + Temperature (5 registers, start address 0x3033):

Address (HEX)Address (DEC)Item DescriptionData TypeNotes
0x303312339battery_statusuint16BED7-D4: temp status; D3-D0: voltage status
0x303412340charge_statusuint16BED3-D2: charge state; D1: fault; D0: charging
0x303512341discharge_statusuint16BED13-D12: load level; D11: short circuit; D0: discharging
0x303612342temp_ambientuint16BEAmbient temperature, /100 (°C)
0x303712343temp_internaluint16BEInternal temperature, /100 (°C)

Query 2 — Power/Energy Data (17 registers, start address 0x3045):

Address (HEX)Address (DEC)Item DescriptionData TypeNotes
0x304512357battery_socuint16BEBattery state of charge (%)
0x304612358battery_voltageuint16BEBattery voltage, /100 (V)
0x304712359battery_currentint16BEBattery current, /100 (A), positive=charge, negative=discharge
0x304812360battery_power_luint16BEBattery power L-register (low 16 bits)
0x304912361battery_power_huint16BEBattery power H-register (high 16 bits)
0x304A12362load_voltageuint16BELoad voltage, /100 (V)
0x304B12363load_currentuint16BELoad current, /100 (A)
0x304C12364load_power_luint16BELoad power L-register
0x304D12365load_power_huint16BELoad power H-register
0x304E12366solar_voltageuint16BESolar panel voltage, /100 (V)
0x304F12367solar_currentuint16BESolar panel current, /100 (A)
0x305012368solar_power_luint16BESolar power L-register
0x305112369solar_power_huint16BESolar power H-register
0x305212370daily_charge_energyuint16BEDaily charge energy, /100 (kWh)
0x305312371total_charge_energy_luint16BETotal charge energy L-register
0x305412372total_charge_energy_huint16BETotal charge energy H-register
0x305512373daily_discharge_energyuint16BEDaily discharge energy, /100 (kWh)

32-bit power/energy calculation:

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)

Supported Modbus function codes:

  • 0x02: Read discrete inputs
  • 0x03: Read holding registers
  • 0x04: Read input registers
  • 0x05: Write single coil
  • 0x06: Write single register
  • 0x10: Write multiple registers

5.2 Status Bit Definitions

battery_status (0x3033):

BitsValueMeaning
D7-D400HTemperature normal
D7-D401HHigh temperature protection
D3-D000HVoltage normal
D3-D001HOvervoltage
D3-D002HUndervoltage
D3-D003HOver-discharge

charge_status (0x3034):

BitsValueMeaning
D60/1Manual charge disable (0=normal, 1=disabled)
D50/1Day/Night (0=day, 1=night)
D3-D200Not charging
D3-D201Float charge
D3-D202Boost charge
D3-D203Equalize charge
D10/1Fault (0=normal, 1=fault)
D00/1Charging (1=charging)

discharge_status (0x3035):

BitsValueMeaning
D13-D1200Light load
D13-D1201Medium load
D13-D1202Rated load
D13-D1203Overload
D110/1Short circuit protection (1=active)
D00/1Discharging (1=discharging)

6. EdgeBus Model

6.1 EB Configuration Parameters

Main configuration parameters:

  • name: "mppt1050"
  • port: 22
  • version: "0x87"
  • dataType: "0x10"
  • upPeriodIndex: 70
  • Modbus default serial parameters:
    • BaudRate: 9600
    • StopBits: 1
    • DataBits: 8
    • Checkbit: NONE
  • BzType: 22110
  • BzVersion: 1
  • Battery: true (built-in battery, Class A)

6.2 EB Code

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 Explanation

Current EB logic:

  • EdgeBus reports service data via Port 22.
  • First executes Query 1 (0x04, address 0x3033, 5 registers) to read status and temperature data.
  • Then executes Query 2 (0x04, address 0x3045, 17 registers) to read power/energy data. Query 2 has isLast: true which triggers the uplink.
  • Upload and collection periods are separated:
    • 70 is the upload period (upPeriodIndex)
    • 74 is the collection period (periodIndex)
  • Modbus address managed by APP parameter 150.
  • Analog values use uint16BE/int16BE types with decimal: 2 for /100 scaling.
  • 32-bit power/energy values are combined from L + H registers in the thing model after parsing.

7. Thing Model

7.1 Basic Information

Telemetry Thing Model

  • Name: [MPPT1050]
  • id Name: mppt1050_22110

Parameter Thing Model

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

The data uplink frame structure is as follows:

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

Frame structure:

  • Byte 0: tag[0] = 0x87 (version)
  • Byte 1: tag[1] = 0x10 (dataType)
  • Byte 2: Status byte
  • Byte 3: Reserved
  • Byte 4: Battery voltage
  • Byte 5: Reserved
  • Bytes 6-15: Query 1 results (5 registers × 2 bytes = 10 bytes)
  • Bytes 16-49: Query 2 results (17 registers × 2 bytes = 34 bytes)

Field definitions:

IndexField NameTypeDescription
6battery_statusuint16BEBattery status (bitmask)
8charge_statusuint16BECharge status (bitmask)
10discharge_statusuint16BEDischarge status (bitmask)
12temp_ambientuint16BEAmbient temperature, decimal:2 (°C)
14temp_internaluint16BEInternal temperature, decimal:2 (°C)
16battery_socuint16BEBattery SOC (%)
18battery_voltageuint16BEBattery voltage, decimal:2 (V)
20battery_currentint16BEBattery current, decimal:2 (A)
22battery_power_luint16BEBattery power L-register
24battery_power_huint16BEBattery power H-register
26load_voltageuint16BELoad voltage, decimal:2 (V)
28load_currentuint16BELoad current, decimal:2 (A)
30load_power_luint16BELoad power L-register
32load_power_huint16BELoad power H-register
34solar_voltageuint16BESolar voltage, decimal:2 (V)
36solar_currentuint16BESolar current, decimal:2 (A)
38solar_power_luint16BESolar power L-register
40solar_power_huint16BESolar power H-register
42daily_charge_energyuint16BEDaily charge energy, decimal:2 (kWh)
44total_charge_energy_luint16BETotal charge energy L-register
46total_charge_energy_huint16BETotal charge energy H-register
48daily_discharge_energyuint16BEDaily discharge energy, decimal:2 (kWh)

7.3 Thing Model Scripts

7.3.1 Telemetry Thing Model Script

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 Parameter Thing Model Script

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. Third-Party Data Subscription

8.1 MQTT Topic

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

Based on the template's thing model fields, example data is as follows:

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
    }
}

Notes:

  • thingModelId should use the platform-generated value; 22110 is shown as an example.
  • thingModelIdName corresponds to the telemetry thing model mppt1050_22110.
  • battery_current positive = charging, negative = discharging.
  • battery_power, load_power, solar_power, total_charge_energy are 32-bit combined values (L + H registers combined, then /100).

9. RPC

9.1 RPC Names

Set Parameters RPC

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

Get Parameters RPC

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

9.2 Parameter Definitions

Parameter AddressNamefield_nameUnitTypeDescription
app_70period_upperiod_upsuint32LEUpload period

| app_150 | addr_modbus | addr_modbus | | uint8 | Modbus address |

9.3 RPC Code

Set Parameters 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

Get Parameters 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. Template Selection

Search for the template in the ThinkLink platform: MPPT1050

Or search by business type: 22110 / MPPT Solar Charge Controller / RS485 Modbus PV Monitoring


Supplementary Notes

  1. Factory default Modbus parameters:
    • Address: 1
    • Baud rate: 9600
    • Data bits: 8
    • Stop bits: 1
    • Parity: None
  2. Battery current is signed (int16BE): positive = charging, negative = discharging.
  3. 32-bit power/energy values are combined from L-register (low 16 bits) and H-register (high 16 bits) using formula: (H << 16) | L, then divided by 100.
  4. Battery SOC uses no scaling (/1), unlike other analog values.
  5. DTU is powered by internal battery (KC21, 10800mAH), Class A mode, default upload period 300 seconds.
  6. The controller supports four battery types by default: Gel, AGM, Liquid, Lithium.