Skip to main content

MOS350 Modbus Integraion

Version 1.0

The MOS350 Control Config file is a JSON file that defines control configuration settings for device integrations. This document covers all elements of the configuration definition file and their usage. Control configuration files use the extension .cf.

tip

MOS350 includes a built-in Blockly visual editor that simplifies building config files without writing JSON by hand. The editor provides drag-and-drop blocks for all config elements and generates the .cf file automatically. Manual JSON editing is only needed for advanced or custom use cases.


Definition: Type 1 — Modbus

Top-Level Elements

ElementTypeDescription
VERstringFile version
TYPintegerIntegration type. 1 = Modbus
STYPintegerDevice sub-type. See Device Sub-Types
FLGintegerGlobal flags. 1 = Auto Load
DATA_FZintegerData processing frequency in seconds. Typical value: 10
CMDarrayArray of command definitions
DATAarrayArray of data definitions
EXECarrayArray of execute definitions
MAParrayData map definitions
CMAPobjectCustom map definitions
SCRarrayScript block definitions

Device Sub-Types

ValueDescription
10Storage / Hybrid Inverter
11PV Inverter
12Meter
13Custom Control
255Custom / Pass-through

Example

{
"VER": "1.0",
"TYP": 1,
"STYP": 10,
"FLG": 1,
"DATA_FZ": 10,
"CMD": [],
"DATA": [],
"EXEC": [],
"MAP": [],
"CMAP": {},
"SCR": []
}

CMD — Command Definition

Each command defines a single Modbus read or write operation. Read and write commands share most fields, with some fields only applicable to write operations.

ElementTypeRequiredDescription
IDintegerYesUnique command ID
NMstringNoHuman-readable name for the command
RSintegerYesModbus register start address
FTintegerYesModbus function type. See Read Function Types and Write Function Types
CFTintegerWrite onlyCustom function subtype. Used when FT is 0xF0 or 0xF1 to specify the device-specific subtype number
SZintegerYesNumber of registers to read or write
TYPintegerRead onlyData type of the register value. See Data Types
MBAintegerNoOverride Modbus address. Overrides the address provided during device pairing
VALstringWrite onlyValue to write in HEX. Use #### when the value is supplied by the automation engine at runtime
FLGintegerNoCommand-level flags bitmask. See CMD Flags

CMD Flags

Both read and write commands share the same FLG bitmask. Some bits are only relevant to specific command types.

BitApplies ToDescription
Bit 2Write onlyFloat representation — interpret the written value as a float. Only applicable when FT is 16 (Write Multiple Registers)
Bit 3Read and WriteLittle-endian byte order. When set, registers are read or written in little-endian format. Default (unset) is big-endian

Read Function Types

ValueDescription
1Read Coils (01)
2Read Discrete Inputs (02)
3Read Holding Registers (03)
4Read Input Registers (04)

Write Function Types

ValueDescription
5Write Single Coil (05)
6Write Single Register (06)
16Write Multiple Registers (16)
240 (0xF0)Write Single Register Custom — used with CFT to specify a device-specific subtype
241 (0xF1)Write Multiple Registers Custom — used with CFT to specify a device-specific subtype

Data Types

ValueType
1UINT_8
2INT_8
3UINT_16
4INT_16
5UINT_32
6INT_32
7UINT_64
8INT_64
9FLOAT

Examples

Read command — reads SOC from a single UINT_16 holding register, big-endian:

{
"NM": "SOC",
"ID": 0,
"RS": 1,
"FT": 3,
"SZ": 1,
"TYP": 3,
"FLG": 0
}

Read command — little-endian byte order (FLG bit 3 set = 8):

{
"NM": "Voltage",
"ID": 1,
"RS": 525,
"FT": 3,
"SZ": 4,
"TYP": 4,
"FLG": 8
}

Write command — writes a fixed HEX value using custom function type with subtype 73:

{
"NM": "HEX Write",
"ID": 2,
"RS": 8705,
"FT": 240,
"CFT": 73,
"SZ": 1,
"VAL": "2202",
"FLG": 0
}

Write command — writes a discharge value supplied by the automation engine at runtime:

{
"NM": "Discharge",
"ID": 3,
"RS": 257,
"FT": 240,
"CFT": 66,
"SZ": 1,
"VAL": "####",
"FLG": 0
}
note

When VAL is set to ####, the automation engine substitutes the runtime value at execution time — equivalent to AutoData.SetValue on the Lua scripting side. This is required for any write command driven by automation logic such as charge or discharge targets. Fixed HEX values (e.g. "2202") are written as-is and are not substituted.

tip

When FT is 240 (0xF0) or 241 (0xF1), the CFT field specifies the device-specific subtype number as defined by the device manufacturer. Fixed HEX values in VAL (e.g. "2202") are written as-is; use #### for runtime-computed values.


DATA — Data Definition

Data elements are the final computed representation of values retrieved by one or more commands. Each data element evaluates a mathematical expression to produce a float result.

ElementTypeDescription
IDintegerUnique data element ID
EXPstringMathematical expression referencing command outputs. See Expression Syntax
SZintegerNumber of data elements to produce. Use with x in the expression to iterate over a command's result set

Expression Syntax

Data expressions reference command outputs using the format {CommandID_Index}, where CommandID is the ID of the command and Index is the zero-based position of the value within that command's result set.

If a command reads multiple registers (i.e. SZ > 1), each register result is accessible by its index:

ReferenceMeaning
{0_0}Command 0, first value
{0_1}Command 0, second value
{1_0}Command 1, first value

Expressions support standard mathematical operators and can combine multiple command outputs:

{ "ID": 0, "EXP": "{0_0} / 10", "SZ": 1 }

If command 0 returned 1234, the result is 123.4.

{ "ID": 1, "EXP": "{0_0} * {0_1}", "SZ": 1 }

Multiplies the first and second values from command 0 — useful for deriving power from voltage and current readings in the same register block.

{ "ID": 2, "EXP": "{0_0} + {1_0}", "SZ": 1 }

Adds the first value of command 0 and command 1 — useful for summing readings across two separate registers.

To iterate over all values in a multi-register command, replace the index with x and set SZ to the number of values:

{ "ID": 3, "EXP": "{0_x} / 10", "SZ": 3 }

This produces three data elements, each dividing the corresponding register value from command 0 by 10. All expressions evaluate to a float.


EXEC — Execute Definition

Execute elements define how and when command sequences are triggered, and what automation function they serve.

ElementTypeDescription
IDintegerUnique execute element ID
NMstringHuman-readable name (optional)
FZintegerExecution frequency in seconds
SEQarrayOrdered array of command IDs to execute in sequence
SFnumberScale factor to apply to the value
FLGintegerExecution flag bitmask. See Exec Flags
FTYPintegerFunction type. See Exec Function Types

Exec Function Types

ValueDescription
0None
1Reset / Standby
2Charge
3Discharge
4Charge / Discharge — the same execution sequence handles both; value sign changes based on function
5Power Limit
6Power Limit Reset
7Grid Form
8Grid Reset
9Grid Sense

Exec Flags

BitDescription
Bit 0Value specified in watts
Bit 1Value specified in percentage. Mutually exclusive with Bit 0
Bit 2Scale factor (SF) is used as a divisor
Bit 3Change sign of value in write function

Example

[
{
"ID": 0,
"NM": "Data",
"FZ": 10,
"SEQ": [0],
"SF": 1,
"FLG": 0,
"FTYP": 0
},
{
"ID": 1,
"NM": "Charge/Discharge",
"FZ": 10,
"SEQ": [1],
"SF": 1,
"FLG": 1,
"FTYP": 4
}
]

MAP — Data Map

MAP is an array of map objects that link data elements to automation variables. Each map object has a TYP field that identifies the map type.

note

In all MAP expressions, {DataID_Index} refers to the output of a DATA element, not a command. {0_0} means data element 0, first value. {0_1} means data element 0, second value (when that element produced multiple outputs via SZ). Mathematical operators are supported, e.g. {0_0} * {1_0}.


MAP Type 1: Power / Energy Parameters

Maps a data element to a standard power or energy automation variable for a specific measurement leg.

ElementTypeDescription
TYPinteger1
LEGintegerMeasurement leg number (1–30)
KEYstringStandard automation variable key. See Power / Energy Keys
EXPstringMathematical expression referencing data element outputs using the format {DataID_Index}
Power / Energy Keys
KeyDescription
ICurrent
VVoltage
FZFrequency
PFPower Factor
PWRActive Power
PWR-VAApparent Power
PWR-VARReactive Power
E-NEnergy Negative (Export, kWh)
E-PEnergy Positive (Import, kWh)
E-VA-NApparent Energy Negative (Export, kVAh)
E-VA-PApparent Energy Positive (Import, kVAh)
E-VAR-NReactive Energy Negative (Export, kVARh)
E-VAR-PReactive Energy Positive (Import, kVARh)

MAP Type 2: Device Parameters

Maps a data element to a device-specific automation variable.

ElementTypeDescription
TYPinteger2
KEYstringDevice automation variable key. See Device Keys
EXPstringMathematical expression referencing data element outputs using the format {DataID_Index}
Device Keys
KeyDescription
O-PWROutput Power
B-SOCBattery State of Charge
B-PWRBattery Power
S-PWRSolar Power
OP-MODEOperational Mode

MAP Type 3: Status Bitmask

Maps a data element to named status tags derived from individual bits of the register value. Supports up to 16 bits (B-00 through B-15).

ElementTypeDescription
TYPinteger3
TAGstringBase tag name prefix applied to each mapped bit (e.g. STATUS_)
EXPstringMathematical expression referencing data element outputs
MASKintegerBitmask specifying which bits (B-00 to B-15) to expose as status tags

MAP Type 4: Database

Maps a data element directly to a named database table column for persistent storage.

ElementTypeDescription
TYPinteger4
DBNMstringDatabase name
TBNMstringTable name
COLstringColumn name
CTYPintegerColumn data type. 1 = Int, 2 = Float, 3 = Text
EXPstringMathematical expression referencing data element outputs

MAP Type 5: Custom Expression

Maps a named expression to a custom variable for use in application-specific logic.

ElementTypeDescription
TYPinteger5
NMstringMap variable name
EXPstringMathematical expression referencing data element outputs using the format {DataID_Index}

MAP Example

"MAP": [
{ "TYP": 1, "LEG": 1, "KEY": "V", "EXP": "{0_0}" },
{ "TYP": 1, "LEG": 1, "KEY": "PWR", "EXP": "{0_0} * {0_1}" },
{ "TYP": 2, "KEY": "B-SOC", "EXP": "{1_0}" },
{ "TYP": 5, "NM": "MyCustomValue", "EXP": "{2_0} / 100" }
]

CMAP — Custom Map

CMAP provides arbitrary key-value mappings for use by custom applications built on top of the MOS350 engine. CMAP entries are not processed or interpreted by the MOS350 automation libraries — they are passed through as-is for consumption by the calling application.

CMAP can be used alongside a full CMD/DATA/EXEC/MAP configuration, or on its own as the sole content of a config file. When used on its own with STYP: 255, set DATA_FZ to 0 and leave CMD, DATA, EXEC, and MAP as empty arrays since no polling is required.

All keys and values are strings. Values may represent labels, aliases, or numeric thresholds — the interpretation is entirely application-defined.

CMAP values are read in Lua scripts via readModbusCMap(). See the Lua Scripting documentation for full API details.

Example — eGauge Meter label mapping for a PDP reporting application

{
"VER": "1.0",
"TYP": 1,
"STYP": 255,
"FLG": 0,
"DATA_FZ": 0,
"CMD": [],
"DATA": [],
"EXEC": [],
"MAP": [],
"CMAP": {
"CHAR": "Charge",
"DCHAR": "Discharge",
"V1": "V-L1",
"V2": "V-L2",
"V3": "V-L3",
"PWR-P": "kW-P",
"PWR-N": "kW-N",
"PWR-VA": "kVA",
"PWR-VAR": "kVAR",
"TOL": "0.05"
}
}
note

STYP: 255 is the official Custom / Pass-through sub-type. When used, it signals to the engine that this config exists solely to carry CMAP data, with no polling or automation processing required.


SCR — Script Blocks

SCR is an array of embedded Lua script blocks. Each block defines a script that the automation engine executes according to its function type.

ElementTypeDescription
FTYPintegerFunction type that triggers this script block. See Script Function Types
FZintegerExecution interval in seconds. Only used when FTYP is 0 (Interval)
EXPstringThe Lua script source, provided as a JSON string

Script Function Types

ValueNameDescription
0IntervalScript runs repeatedly at the interval specified by FZ in seconds
1ResetCalled by the automation engine when a reset function is performed
2ChargeCalled by the automation engine when a charge function is performed
3DischargeCalled by the automation engine when a discharge function is performed

Script Lifecycle

Each script block follows the standard Lua automation lifecycle — a setup() function called once on start, and a loop() function called on each execution. For FTYP 0 (Interval), loop() is called at the frequency defined by FZ. For other function types, loop() is called each time the automation engine triggers that function.

See the Lua Scripting documentation for the full API reference including AutoData, writeModbus(), pushRealTime(), and all other available functions.

Example

"SCR": [
{
"FTYP": 2,
"FZ": 1,
"EXP": "function setup()\n print(\"Charge script ready\")\nend\n\nfunction loop()\n writeModbus(AutoData.Id, 1, 6, 8, 1, AutoData.SetValue)\nend"
},
{
"FTYP": 0,
"FZ": 2,
"EXP": "counter = 0\nfunction setup()\n print(\"Interval script ready\")\nend\n\nfunction loop()\n counter = counter + 1\n pushRealTime(\"CNT\", \"Counter\", counter)\nend"
}
]
tip

Script source in EXP uses \n for newlines. Write and test scripts in a Lua editor first, then escape newlines before inserting into the JSON.


Full Type 1 Example

{
"VER": "1.0",
"TYP": 1,
"STYP": 10,
"FLG": 1,
"DATA_FZ": 10,
"CMD": [
{
"NM": "SOC",
"ID": 0,
"RS": 1,
"FT": 3,
"SZ": 1,
"TYP": 3,
"FLG": 0
},
{
"NM": "Charge/Discharge",
"ID": 1,
"RS": 3,
"FT": 6,
"CFT": 0,
"SZ": 1,
"VAL": "####",
"FLG": 0
}
],
"DATA": [
{ "ID": 0, "EXP": "{0_0}", "SZ": 1 }
],
"EXEC": [
{
"ID": 0,
"NM": "Data",
"FZ": 10,
"SEQ": [0],
"SF": 1,
"FLG": 0,
"FTYP": 0
},
{
"ID": 1,
"NM": "Charge/Discharge",
"FZ": 10,
"SEQ": [1],
"SF": 1,
"FLG": 1,
"FTYP": 4
}
],
"MAP": [
{ "TYP": 2, "KEY": "B-SOC", "EXP": "{0_0}" }
],
"CMAP": {},
"SCR": [
{
"FTYP": 2,
"FZ": 1,
"EXP": "function setup()\n print(\"Charge script ready\")\nend\n\nfunction loop()\n writeModbus(AutoData.Id, 1, 6, 8, 1, AutoData.SetValue)\nend"
},
{
"FTYP": 0,
"FZ": 2,
"EXP": "counter = 0\nfunction setup()\n print(\"Interval script ready\")\nend\n\nfunction loop()\n counter = counter + 1\n pushRealTime(\"CNT\", \"Counter\", counter)\nend"
}
]
}