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.
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
| Element | Type | Description |
|---|---|---|
VER | string | File version |
TYP | integer | Integration type. 1 = Modbus |
STYP | integer | Device sub-type. See Device Sub-Types |
FLG | integer | Global flags. 1 = Auto Load |
DATA_FZ | integer | Data processing frequency in seconds. Typical value: 10 |
CMD | array | Array of command definitions |
DATA | array | Array of data definitions |
EXEC | array | Array of execute definitions |
MAP | array | Data map definitions |
CMAP | object | Custom map definitions |
SCR | array | Script block definitions |
Device Sub-Types
| Value | Description |
|---|---|
10 | Storage / Hybrid Inverter |
11 | PV Inverter |
12 | Meter |
13 | Custom Control |
255 | Custom / 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.
| Element | Type | Required | Description |
|---|---|---|---|
ID | integer | Yes | Unique command ID |
NM | string | No | Human-readable name for the command |
RS | integer | Yes | Modbus register start address |
FT | integer | Yes | Modbus function type. See Read Function Types and Write Function Types |
CFT | integer | Write only | Custom function subtype. Used when FT is 0xF0 or 0xF1 to specify the device-specific subtype number |
SZ | integer | Yes | Number of registers to read or write |
TYP | integer | Read only | Data type of the register value. See Data Types |
MBA | integer | No | Override Modbus address. Overrides the address provided during device pairing |
VAL | string | Write only | Value to write in HEX. Use #### when the value is supplied by the automation engine at runtime |
FLG | integer | No | Command-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.
| Bit | Applies To | Description |
|---|---|---|
| Bit 2 | Write only | Float representation — interpret the written value as a float. Only applicable when FT is 16 (Write Multiple Registers) |
| Bit 3 | Read and Write | Little-endian byte order. When set, registers are read or written in little-endian format. Default (unset) is big-endian |
Read Function Types
| Value | Description |
|---|---|
1 | Read Coils (01) |
2 | Read Discrete Inputs (02) |
3 | Read Holding Registers (03) |
4 | Read Input Registers (04) |
Write Function Types
| Value | Description |
|---|---|
5 | Write Single Coil (05) |
6 | Write Single Register (06) |
16 | Write 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
| Value | Type |
|---|---|
1 | UINT_8 |
2 | INT_8 |
3 | UINT_16 |
4 | INT_16 |
5 | UINT_32 |
6 | INT_32 |
7 | UINT_64 |
8 | INT_64 |
9 | FLOAT |
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
}
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.
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.
| Element | Type | Description |
|---|---|---|
ID | integer | Unique data element ID |
EXP | string | Mathematical expression referencing command outputs. See Expression Syntax |
SZ | integer | Number 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:
| Reference | Meaning |
|---|---|
{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.
| Element | Type | Description |
|---|---|---|
ID | integer | Unique execute element ID |
NM | string | Human-readable name (optional) |
FZ | integer | Execution frequency in seconds |
SEQ | array | Ordered array of command IDs to execute in sequence |
SF | number | Scale factor to apply to the value |
FLG | integer | Execution flag bitmask. See Exec Flags |
FTYP | integer | Function type. See Exec Function Types |
Exec Function Types
| Value | Description |
|---|---|
0 | None |
1 | Reset / Standby |
2 | Charge |
3 | Discharge |
4 | Charge / Discharge — the same execution sequence handles both; value sign changes based on function |
5 | Power Limit |
6 | Power Limit Reset |
7 | Grid Form |
8 | Grid Reset |
9 | Grid Sense |
Exec Flags
| Bit | Description |
|---|---|
| Bit 0 | Value specified in watts |
| Bit 1 | Value specified in percentage. Mutually exclusive with Bit 0 |
| Bit 2 | Scale factor (SF) is used as a divisor |
| Bit 3 | Change 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.
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.
| Element | Type | Description |
|---|---|---|
TYP | integer | 1 |
LEG | integer | Measurement leg number (1–30) |
KEY | string | Standard automation variable key. See Power / Energy Keys |
EXP | string | Mathematical expression referencing data element outputs using the format {DataID_Index} |
Power / Energy Keys
| Key | Description |
|---|---|
I | Current |
V | Voltage |
FZ | Frequency |
PF | Power Factor |
PWR | Active Power |
PWR-VA | Apparent Power |
PWR-VAR | Reactive Power |
E-N | Energy Negative (Export, kWh) |
E-P | Energy Positive (Import, kWh) |
E-VA-N | Apparent Energy Negative (Export, kVAh) |
E-VA-P | Apparent Energy Positive (Import, kVAh) |
E-VAR-N | Reactive Energy Negative (Export, kVARh) |
E-VAR-P | Reactive Energy Positive (Import, kVARh) |
MAP Type 2: Device Parameters
Maps a data element to a device-specific automation variable.
| Element | Type | Description |
|---|---|---|
TYP | integer | 2 |
KEY | string | Device automation variable key. See Device Keys |
EXP | string | Mathematical expression referencing data element outputs using the format {DataID_Index} |
Device Keys
| Key | Description |
|---|---|
O-PWR | Output Power |
B-SOC | Battery State of Charge |
B-PWR | Battery Power |
S-PWR | Solar Power |
OP-MODE | Operational 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).
| Element | Type | Description |
|---|---|---|
TYP | integer | 3 |
TAG | string | Base tag name prefix applied to each mapped bit (e.g. STATUS_) |
EXP | string | Mathematical expression referencing data element outputs |
MASK | integer | Bitmask 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.
| Element | Type | Description |
|---|---|---|
TYP | integer | 4 |
DBNM | string | Database name |
TBNM | string | Table name |
COL | string | Column name |
CTYP | integer | Column data type. 1 = Int, 2 = Float, 3 = Text |
EXP | string | Mathematical expression referencing data element outputs |
MAP Type 5: Custom Expression
Maps a named expression to a custom variable for use in application-specific logic.
| Element | Type | Description |
|---|---|---|
TYP | integer | 5 |
NM | string | Map variable name |
EXP | string | Mathematical 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"
}
}
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.
| Element | Type | Description |
|---|---|---|
FTYP | integer | Function type that triggers this script block. See Script Function Types |
FZ | integer | Execution interval in seconds. Only used when FTYP is 0 (Interval) |
EXP | string | The Lua script source, provided as a JSON string |
Script Function Types
| Value | Name | Description |
|---|---|---|
0 | Interval | Script runs repeatedly at the interval specified by FZ in seconds |
1 | Reset | Called by the automation engine when a reset function is performed |
2 | Charge | Called by the automation engine when a charge function is performed |
3 | Discharge | Called 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"
}
]
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"
}
]
}