12 KiB
Modbus Logic Language (mb-lang)
This document describes the design and usage of the simple logic engine configurable via Modbus TCP.
Purpose
The Modbus Logic Engine allows users to define simple conditional automation rules directly by writing to specific Modbus holding registers. This enables basic automation sequences like "if sensor value X exceeds Y, then turn on relay Z" without modifying the core firmware code.
Architecture
A dedicated component, ModbusLogicEngine, runs within the firmware.
- It exposes a block of Modbus Holding Registers for configuration and status.
- It periodically evaluates the enabled rules.
- It can read the state of other Modbus registers/coils.
- It can perform actions like writing to Modbus registers/coils or calling pre-defined methods on other firmware components.
- It updates status registers after each rule evaluation/action attempt.
Configuration and Status
The engine supports a fixed number of logic rules, defined by MAX_LOGIC_RULES (e.g., 8). Each rule is configured and monitored using a block of REGISTERS_PER_RULE (now 13) consecutive Holding Registers.
- Base Address:
MODBUS_LOGIC_RULES_START(Defined inconfig-modbus.h) - Rule N Address:
MODBUS_LOGIC_RULES_START + (N * REGISTERS_PER_RULE)whereNis the rule index (0 toMAX_LOGIC_RULES - 1).
Register Map per Rule (N)
| Offset | Register Name | Address Offset | R/W | Description | Notes |
|---|---|---|---|---|---|
| +0 | Rule_N_Enabled |
Base + (N*13) + 0 |
R/W | Enable/Disable Rule: 0 = Disabled, 1 = Enabled | Rules are ignored if disabled. |
| +1 | Rule_N_Cond_Src_Type |
Base + (N*13) + 1 |
R/W | Condition Source Type: 0 = Holding Register, 1 = Coil | Specifies what type of Modbus item the condition reads. |
| +2 | Rule_N_Cond_Src_Addr |
Base + (N*13) + 2 |
R/W | Condition Source Address: Modbus address of the register/coil to read for the condition. | The address of the data point to check. |
| +3 | Rule_N_Cond_Operator |
Base + (N*13) + 3 |
R/W | Condition Operator: 0===, 1=!=, 2=<, 3=<=, 4=>, 5=>= |
The comparison to perform. |
| +4 | Rule_N_Cond_Value |
Base + (N*13) + 4 |
R/W | Condition Value: The value to compare the source against. | For Coils, use 0 for OFF and 1 for ON. |
| +5 | Rule_N_Action_Type |
Base + (N*13) + 5 |
R/W | Action Type: 0=None, 1=Write Holding Reg, 2=Write Coil, 3=Call Component Method | Specifies what to do if the condition is true. |
| +6 | Rule_N_Action_Target |
Base + (N*13) + 6 |
R/W | Action Target: Modbus Address (for Write Reg/Coil) or Component ID (for Call Method) | Specifies what to act upon (Register Address, Coil Address, Component ID). |
| +7 | Rule_N_Action_Param1 |
Base + (N*13) + 7 |
R/W | Action Parameter 1: Value (for Write Reg), ON/OFF (for Write Coil, 0=OFF, 1=ON), Method ID (for Call) | Specifies how to act upon the target. |
| +8 | Rule_N_Action_Param2 |
Base + (N*13) + 8 |
R/W | Action Parameter 2: Argument 1 for Call Component Method |
First argument for the component method. Ignored for Write actions. |
| +9 | Rule_N_Action_Param3 |
Base + (N*13) + 9 |
R/W | Action Parameter 3: Argument 2 for Call Component Method |
Second argument for the component method. Ignored for Write actions. |
| +10 | Rule_N_Last_Status |
Base + (N*13) + 10 |
R | Last Action Status: 0=Idle/OK, 1=Err Cond Read, 2=Err Action Write/Call, 3=Invalid Action Params | Reports the outcome of the last trigger attempt. (See Status Codes below) |
| +11 | Rule_N_Last_Trigger_Timestamp |
Base + (N*13) + 11 |
R | Last Trigger Timestamp: System time (e.g., seconds since boot) when rule last triggered. | 0 if never triggered. Wraps eventually. |
| +12 | Rule_N_Trigger_Count |
Base + (N*13) + 12 |
R | Trigger Count: Number of times this rule's action has been successfully triggered. | Wraps eventually. Can be reset by writing 0 via Modbus. |
Note: Base refers to MODBUS_LOGIC_RULES_START. R=Read-Only, W=Writeable (from Modbus perspective). Status registers (+10 to +12) are updated internally but the count (+12) can potentially be reset via write.
Status Codes (Rule_N_Last_Status)
| Code | Meaning |
|---|---|
| 0 | Idle / Action OK |
| 1 | Error Reading Condition Src |
| 2 | Error Performing Action |
| 3 | Invalid Action Parameters |
| 4 | Component Method Call Failed |
| ... | (Other specific errors TBD) |
Actions
1. Write Holding Register
- Action Type: 1
- Action Target: Modbus address of the Holding Register.
- Action Parameter 1: Value to write.
- Action Parameters 2 & 3: Ignored.
2. Write Coil
- Action Type: 2
- Action Target: Modbus address of the Coil.
- Action Parameter 1: Value to write (0 for OFF, 1 for ON). Other non-zero values may also be interpreted as ON.
- Action Parameters 2 & 3: Ignored.
3. Call Component Method
- Action Type: 3
- Action Target: The numeric
Component ID. - Action Parameter 1: The numeric
Method ID. - Action Parameter 2: The first integer argument (
arg1). - Action Parameter 3: The second integer argument (
arg2).
Important: Only specific, pre-registered methods can be called. Available Component/Method IDs need separate documentation.
Execution Flow
- The
ModbusLogicEngineloops periodically. - For each rule
Nfrom 0 toMAX_LOGIC_RULES - 1: a. ReadRule_N_Enabled. If 0, skip. b. Read condition parameters. c. Attempt to read the current value fromCond_Src_Addr. d. If read fails, updateRule_N_Last_Status(e.g., to 1) and skip to the next rule. e. Evaluate the condition. f. If the condition is TRUE: i. Read action parameters. ii. Attempt to perform the specified action. iii. UpdateRule_N_Last_Statusbased on action success (0) or failure (e.g., 2, 3, 4). iv. If action was successful, incrementRule_N_Trigger_Countand updateRule_N_Last_Trigger_Timestamp. g. If the condition is FALSE, potentially resetRule_N_Last_Statusto 0 (Idle), unless it holds an error state.
Example Scenarios
(Addresses updated for REGISTERS_PER_RULE = 13)
Example 1: Turn on Relay 5 if Register 200 >= 100
Assume:
MODBUS_LOGIC_RULES_START= 1000- Rule Index
N = 0(Base = 1000) - Relay 5 is mapped to Coil address 5
- Register 200
Write:
| Register Address | Value | Meaning |
|---|---|---|
| 1000 | 1 | Rule 0: Enabled |
| 1001 | 0 | Cond Src Type: Reg |
| 1002 | 200 | Cond Src Addr: 200 |
| 1003 | 5 | Cond Operator: >= (5) |
| 1004 | 100 | Cond Value: 100 |
| 1005 | 2 | Action Type: Write Coil |
| 1006 | 5 | Action Target: Coil Addr 5 |
| 1007 | 1 | Action Param 1: Value ON |
| 1008 | 0 | Action Param 2: (Ignored) |
| 1009 | 0 | Action Param 3: (Ignored) |
Read Status (after trigger):
| Register Address | Value | Meaning |
|---|---|---|
| 1010 | 0 | Last Status: OK |
| 1011 | e.g., 12345 | Last Trigger: Timestamp |
| 1012 | e.g., 1 | Trigger Count: 1 |
Example 2: Call resetCounter() Method on Component StatsTracker if Coil 10 is ON
Assume:
- Rule Index
N = 1(Base = 1000 + 13 = 1013) - Coil 10
- Component
StatsTrackerID = 5 - Method
resetCounterID = 1 (takes no args)
Write:
| Register Address | Value | Meaning |
|---|---|---|
| 1013 | 1 | Rule 1: Enabled |
| 1014 | 1 | Cond Src Type: Coil |
| 1015 | 10 | Cond Src Addr: Coil 10 |
| 1016 | 0 | Cond Operator: == (0) |
| 1017 | 1 | Cond Value: ON (1) |
| 1018 | 3 | Action Type: Call Method |
| 1019 | 5 | Action Target: Component ID 5 |
| 1020 | 1 | Action Param 1: Method ID 1 |
| 1021 | 0 | Action Param 2: Arg1 = 0 |
| 1022 | 0 | Action Param 3: Arg2 = 0 |
Read Status (after trigger):
| Register Address | Value | Meaning |
|---|---|---|
| 1023 | 0 | Last Status: OK |
| 1024 | e.g., 12360 | Last Trigger: Timestamp |
| 1025 | e.g., 1 | Trigger Count: 1 |
Limitations & Considerations
- Complexity: Only simple, single conditions per rule. No
AND/OR/ELSE. - Execution Order: Rules evaluated sequentially.
- Performance: Rule evaluation takes time. Consider impact and execution frequency.
- Timestamp: The
Last_Trigger_Timestampresolution and potential for wrapping depend on the firmware implementation (e.g.,millis()overflow, using seconds since boot). - Error Handling: Status codes provide basic feedback. More detailed logging might be needed for complex debugging.
- Method Availability: Callable methods are fixed in firmware.
- Concurrency: Actions (especially method calls) might take time. The engine design needs to consider if rule evaluation should block or if actions run asynchronously (current design implies synchronous execution within the loop).
Limitations & Considerations
- Complexity: Only simple, single conditions per rule are supported. No
AND/ORorELSElogic. - Execution Order: Rules are evaluated sequentially. Be mindful of potential interactions if multiple rules modify the same target.
- Performance: Reading Modbus values and executing actions takes time. Complex rules or a large number of rules might impact overall system performance. Rule execution frequency should be considered.
- Error Handling: The current design doesn't explicitly define Modbus registers for rule execution status or errors. This could be added later.
- Method Availability: The list of callable methods (
Component ID,Method ID) is fixed in the firmware and needs to be documented for users.