> ## Documentation Index
> Fetch the complete documentation index at: https://docs.directenergypartners.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Modbus TCP/IP

Each entry in the JSON-formatted address map corresponds to a Modbus register on the actual device. Every register in the address map must contain several required and optional parameters, which are described below. The final address map should cover all relevant Modbus registers, including both read and write registers.

### Understanding Modbus Registers

Modbus registers or addresses have two key distinctions: read operations and write operations. Modbus read operations correspond to function codes FC1, FC2, FC3, and FC4. Write operations, used for sending commands to devices, are associated with function codes FC5, FC6, FC15, and FC16. This distinction is crucial when adding Modbus registers to the device address map.

<Note>
  Please note that for most devices, the most commonly used function codes are FC3, FC6, and FC16. However, it is important to know that the PPL controller supports all Modbus function codes, despite the frequent use of these particular ones.
</Note>

### Naming Conventions

While not mandatory, it is recommended to adopt the following naming conventions for clarity and usability:

* **Write Operations:** Prefix register names with `control.` to indicate that the register is used for controlling the device.
* **Read Operations:** Prefix register names with `measure.` to signify that the register is used for measurement purposes.
* **Fault and Warning Registers:** Use the prefixes "fault." or "warning." for registers that monitor faults or warnings. This helps in generating appropriate Telegram notifications and alerts for the system.

See the example Modbus address map below for register naming conventions.

### Required Parameters

Each register in the address map must include the following parameters, regardless of whether it is for reading or writing Modbus registers:

<ParamField path="address" type="int" required>
  The Modbus device address from which to read a register or to which to write a value.

  **Example:** `0`
</ParamField>

<ParamField path="numberOfRegisters" type="int" required>
  Specifies the number of registers to read or write. This should be 1 for (u)int16 data types and 2 for (u)int32, float32 or string32 data types and 4 for (u)int64 or float64 data types.

  **Example:** `1`
</ParamField>

<ParamField path="name" type="string" required>
  A unique name for the register, representing the measurement point read from or written to the Modbus device.

  **Example:** `measure.ports.port1.voltage`
</ParamField>

<ParamField path="datatype" type="string" required>
  The data type of the value.

  **Options:** `int16 | uint16 | int32 | uint32 | float32 | string32 | int64 | uint64 | float64`
</ParamField>

<ParamField path="functionCode" type="int" required>
  The Modbus function code required to read from or write to the device.

  **Options:** `1 | 2 | 3 | 4 | 5 | 6 | 15 | 16`
</ParamField>

### Optional Parameters

These parameters are not necessary for every register but can be included as needed:

<ParamField path="scaling" type="int | float | string">
  The scaling factor to apply to the Modbus register's return value or the value being written.

  **Example:** `0.1`
</ParamField>

<ParamField path="offset" type="int | float">
  The offset to apply to the Modbus register's return value or the value being written.

  **Example:** `10`
</ParamField>

<ParamField path="direction" type="string">
  The direction of the power or current flow on a port of the device. When the direction is set to `output`,
  the register value will be inverted.

  **Options:** `input | output`
</ParamField>

<ParamField path="minimum" type="int | float">
  The minimum value of the register. For write register, setting the minimum value prevents you from
  writing a value lower than the minimum value. The control command will be blocked by the Power Platform.

  **Example:** `0`
</ParamField>

<ParamField path="maximum" type="int, float">
  The maximum value of the register. For write register, setting the maximum value prevents you from
  writing a value higher than the maximum value. The control command will be blocked by the Power Platform.

  **Example:** `10`
</ParamField>

<ParamField path="map" type="object">
  Indicates if mapping should be applied to the return value or the value being written.

  <Expandable title="properties">
    <ParamField path="type" type="string">
      Indicates the type of mapping to be applied.

      **Options:** `bitwise | value`
    </ParamField>

    * **bitwise:** Each bit in a number corresponds to a specific state (e.g., fault or status).
    * **value:** The entire return value corresponds to a state.
  </Expandable>

  **Example:**

  ```json icon="code" theme={null}
  {
    "map": {
      "type": "bitwise"
    }
  }
  ```
</ParamField>

<ParamField path="multiRead" type="bool">
  Indicates if the register is a multi-read register.
</ParamField>

<ParamField path="wordOrder" type="string">
  Specifies the word order notation.

  **Options:** `little | big`
</ParamField>

<ParamField path="byteOrder" type="string">
  Specifies the byte order notation.

  **Options:** `little | big`
</ParamField>

### `communicationCheck` Register

There is a specific register that is **required** in every Modbus address map, known as `communicationCheck`. This register allows the PPL controller to verify communication with the Modbus device. The controller pings the device and attempts to read the value from the register specified as communicationCheck. A successful read confirms the connection between the PPL controller and the Modbus device.

This register only needs the required parameters to be filled in; optional parameters are not necessary. However, the register must be a valid Modbus register on the device. Therefore, it can be a duplicate of another Modbus register in your address map, but with a different name. For example:

```json icon="code" theme={null}
{
  "address": 0,
  "numberOfRegisters": 1,
  "name": "communicationCheck",
  "datatype": "int16",
  "functionCode": 3
}
```

### Expression Registers

In addition to standard Modbus registers, you can define **optional** expression registers. An expression register computes a value from other registers in the address map using a mathematical expression, rather than reading directly from the device.

Each expression register requires the following parameters:

<ParamField path="name" type="string" required>
  A unique name for the expression register.

  **Example:** `measure.string1.port1.power`
</ParamField>

<ParamField path="expression" type="string" required>
  A mathematical expression that references other registers in the address map by their name, enclosed in curly braces `{}`.

  **Example:** `{measure.string1.port1.voltage} * {measure.string1.port1.current}`
</ParamField>

<Note>
  All registers referenced in the expression must exist in the address map itself.
</Note>

```json icon="code" theme={null}
{
    "name": "measure.string1.port1.power",
    "expression": "{measure.string1.port1.voltage} * {measure.string1.port1.current}"
}
```

### Saving the Address Map

By following the naming conventions and adding all the required and optional parameters to each Modbus register, you have created a complete address map for the controller
to communicate and interact with the device. The final JSON file should be saved with a unique name and a `.json` extension. For example, `modbus_address_map.json`.

### Example

```json modbus_address_map.json icon="code" theme={null}
[
  {
    "address": 0,
    "numberOfRegisters": 1,
    "name": "measure.ports.port1.voltage",
    "datatype": "int16",
    "functionCode": 3
  },
  {
    "address": 1,
    "numberOfRegisters": 1,
    "name": "measure.ports.port2.current",
    "datatype": "int16",
    "functionCode": 3,
    "scaling": 0.1,
    "offset": 0,
    "direction": "output"
  },
  {
    "address": 2,
    "numberOfRegisters": 1,
    "name": "control.ports.port1.method",
    "datatype": "uint16",
    "functionCode": 6,
    "map": {
      "type": "value",
      "0": "idle",
      "1": "net",
      "1026": "GPWR",
      "1282": "FPWR"
    }
  },
  {
    "address": 3,
    "numberOfRegisters": 1,
    "name": "fault.active.0",
    "datatype": "uint16",
    "functionCode": 3,
    "map": {
      "type": "bitwise",
      "0": "GFDI fault",
      "1": "IMI fault",
      "2": "PM heatsink temp",
      "3": "Control board temp",
      "4": "24V undervoltage",
      "5": "Ext fan lockd",
      "6": "DC OV",
      "7": "DC UV",
      "8": "Link OV",
      "9": "Link starve",
      "10": "Link OI",
      "11": "Link Prim Pos 1200V",
      "12": "Link Prim Neg 1200V",
      "13": "Link Sec Pos 1200V",
      "14": "Link Sec Neg 1200V",
      "15": "AC1 A-B Hard Sw Cond"
    }
  },
  {
    "address": 0,
    "numberOfRegisters": 1,
    "name": "communicationCheck",
    "datatype": "int16",
    "functionCode": 3
  }
]
```
