Skip to content

Peripheral Execution Configuration ​

This document is intended for device configuration users / integrators and covers the configuration methods for Peripheral Execution Rules (PeriphExec Rule), usage differences between trigger and action types, field semantics, and typical scenarios.

If you are interested in low-level implementation architecture, task scheduling, and source code logic, please read periph-exec-flow.md.

If you are interested in the peripherals themselves (types, pins, compile switches), please read peripheral-configuration-guide.md.


Current Version Notes ​

  • Peripheral Exec is in the release baseline. Script actions require ruleScript and commandScript to be compiled, enabled and accepted by the runtime budget.
  • Event triggers now support local sensor data sources; event IDs use the ds:<sourceId> format, e.g. ds:dht_01_temperature.
  • The "Local Sensor" dropdown only shows data sources that have been configured via sensor read actions in peripheral execution rules; no invalid sensor sources are displayed when no read action is configured.
  • The default configuration no longer includes a dedicated buzzer peripheral or buzzer preset actions; actionType=20 is retained as a historical placeholder.
  • Display actions are grouped under the "Display" category in the Web UI, retaining four action types: Display Number, Display Text, Clear Display, and OLED Custom Display for easy selection.
  • For configuration backup and migration, use "Device Config > Advanced Config > Config Import/Export" to import/export periph_exec.json individually under "Peripheral Execution".

The Peripheral Execution page displays the rule list, enable status, trigger and action entries. When creating a new rule, keep it disabled first, then confirm action safety via "Run Once" and logs after saving.

NameStatusTriggerTarget PeripheralActionStatsOperations
ADC Voltage Read & ReportDisabledTimer TriggerADC Analog ConvertSensor Data Read -> ADC Analog ConvertTriggered: 1Run Once / Edit / Enable / Delete
ADC Voltage Below 2.30V Close RelayDisabledEvent Trigger-Set LOWTriggered: 1Run Once / Edit / Enable / Delete
ADC Voltage Above 2.50V Open RelayDisabledEvent Trigger-Set HIGHTriggered: 1Run Once / Edit / Enable / Delete
ADC Voltage Display on 7-SegmentDisabledTimer Trigger-Display NumberTriggered: 1Run Once / Edit / Enable / Delete
NTP Time SyncDisabledPlatform Trigger (MQTT)-NTP SyncTriggered: 1Run Once / Edit / Enable / Delete
OLED Temp & Humidity DisplayDisabledTimer Trigger-OLED Custom DisplayTriggered: 1Run Once / Edit / Enable / Delete
OLED Custom Display - Button Trigger Device InfoDisabledEvent Trigger-OLED Custom DisplayTriggered: 1Run Once / Edit / Enable / Delete
OLED Custom Display - Timer MixedDisabledTimer Trigger-OLED Custom DisplayTriggered: 1Run Once / Edit / Enable / Delete
OLED Custom Display - MQTT MessageDisabledPlatform Trigger (MQTT)-OLED Custom DisplayTriggered: 1Run Once / Edit / Enable / Delete
OTA UpgradeDisabledPlatform Trigger (MQTT)-OTA UpgradeTriggered: 1Run Once / Edit / Enable / Delete

When configuring, follow the chain diagram from left to right: first confirm the trigger source, then select the action target, and finally decide whether to report execution results; for complex rules, save as disabled first, then enable after manual execution confirms safety.

Before going live, follow the lifecycle diagram: save as disabled first, check trigger and action parameters, manually execute to confirm safety, then enable auto-trigger; in case of anomalies, disable the rule and restore backup first.

When configuring a rule, first answer "when to execute", then "what action to perform". Keep triggers single and clear; start actions with observable, reversible single actions.

TriggerInput SourceCheck TimingSuitable ScenariosCommon Risks
Platform TriggerMQTT platform commandMatch fields immediately on message arrivalRemote switch, parameter delivery, interlocking controlField name mismatch, messageId duplication, insufficient permissions
Timer TriggerLocal clock / cron / intervalExecute when time point is reachedPeriodic reporting, timed switching, night modeNTP not synced, first trigger semantics unclear
Event TriggerButtons, sensors, system eventsMatch eventId on event publishButton control, threshold alarm, state changeDebounce, event name typos, stale cache values
Poll TriggerActive reading of peripheral or protocol dataCheck conditions every pollIntervalModbus collection, analog threshold, distance alarmToo frequent, serial port busy, low-memory frequency reduction

Choosing the wrong trigger type will lead you astray in troubleshooting. For platform triggers, check platform messages and protocol connectivity; for timer triggers, check clock and interval; for event triggers, check event ID and debounce; for poll triggers, check sampling period, timeout, retries, and resource usage.

Check ItemConfirm Before EnablingRisk Note
Basic InfoName is identifiable, ID is unique, save as disabled first, note field conditionsMake the rule an auditable object before entering individual verification.
TriggersSource is clear, conditions are reproducible, poll interval is reasonable, event name is accurateDo not mix up platform, timer, event, and poll troubleshooting paths.
ActionsTarget peripheral exists, actionType is correct, actionValue is valid, strong actions have been confirmedManually execute relays, motors, and register writes once first.
Results & ReportingLogs are traceable, reporting fields are correct, failures have responses, screenshots availableCheck not only device actions but also what the platform receives.
RollbackCan disable, has backup, knows recovery entry, on-site power-off point is clearKeep a physical emergency stop for dangerous actions.

Before enabling rules, check basic info, triggers, actions, result reporting, and rollback methods against the diagram. When there is a real load on-site, strong-action rules must be saved as disabled and manually verified first.

Table of Contents ​


1. What Is a Peripheral Execution Rule ​

A Peripheral Execution Rule (PeriphExec Rule) is the local automation rule engine of FastBee devices, implementing the logic loop of "when a condition is met, execute a set of actions" without relying on cloud linkage. It works even when offline.

A rule consists of three parts:

┌─────────────┐     ┌────────────────┐     ┌─────────────┐
│ Triggers    │ ──> │ Condition /    │ ──> │ Actions     │
│ (multiple)  │     │ Received Value │     │ (multiple)  │
│  OR logic   │     │ Template parse │     │ Sequential  │
└─────────────┘     └────────────────┘     └─────────────┘
  • Triggers: Up to 3; any match triggers the rule (OR logic).
  • Actions: Up to 4; executed sequentially in array order (each action can have a delay syncDelayMs).
  • Execution Mode (execMode): 0=async (default, FreeRTOS task, non-blocking) / 1=sync (blocking execution, suitable for fast IO).

Configuration file path: /config/periph_exec.json, loaded by PeriphExecManager at runtime.


2. Rule Structure Overview ​

Single rule JSON structure (one item in the data/config/periph_exec.json array):

When reading JSON, cross-reference the data model: top-level fields describe rule identity and execution strategy, triggers describe source and conditions, actions describe target peripheral and action parameters, and reportAfterExec determines whether observable feedback is generated after execution.

json
{
  "id": "exec_1700000000001",
  "name": "High Temperature Alarm",
  "enabled": true,
  "execMode": 0,

  "triggers": [
    {
      "triggerType": 5,
      "triggerPeriphId": "dht_01",
      "operatorType": 2,
      "compareValue": "30"
    }
  ],

  "actions": [
    { "targetPeriphId": "fan_01", "actionType": 0, "execMode": 1 },
    { "targetPeriphId": "lcd_01",    "actionType": 27, "actionValue": "# High Temp Alarm\nTemp: ${dht_01.temperature}°C", "syncDelayMs": 200 }
  ],

  "protocolType": 0,
  "scriptContent": "",
  "reportAfterExec": true
}

2.1 Top-Level Fields ​

FieldTypeDefaultDescription
idstring—Unique ID (recommended exec_ prefix + timestamp)
namestring—Display name (used in UI / logs)
enabledbooltrueRule enable switch
execMode0|10Rule-level execution mode: 0=async, 1=sync; action-level execMode overrides if set
triggersarray—Trigger list, max 3, OR logic
actionsarray—Action list, max 4, sequential execution
protocolTypeuint80Data reporting preference: the release execution chain supports 0=MQTT and 1=Modbus RTU; 2=Modbus TCP, 3=HTTP, 4=CoAP, and 5=TCP are compatibility enums only
scriptContentstring""Optional text template for data reshaping after execution (${key} placeholders)
reportAfterExecbooltrueWhether to auto-report control results to the enabled protocol after execution

Compatibility Note: Older firmware may store unset scriptContent as the string "null"; the current version auto-corrects this to an empty string. No manual handling needed after upgrade.

Quota Limits: A single rule's triggers must not exceed 3 (MAX_TRIGGERS_PER_RULE), and actions must not exceed 4 (MAX_ACTIONS_PER_RULE). Exceeding these will be rejected on save.


3. Trigger Details ​

triggerType values: 0=Platform Trigger, 1=Timer Trigger, 4=Event Trigger, 5=Poll Trigger.

Note: Historical values 2=DATA_RECEIVE and 3=DATA_REPORT are deprecated and unified into 4=Event Trigger (use eventId to differentiate).

3.1 Platform Trigger (PLATFORM_TRIGGER=0) ​

Purpose: Respond to IoT platform MQTT property set / function call commands.

Fields:

FieldDescription
triggerPeriphIdRequired. Exact match with MQTT message item.id (usually peripheral ID)
operatorTypeComparison operator, special semantics see below
compareValueComparison value; when operatorType=1, means "set mode" with no comparison, any value matches

operatorType Special Convention (Platform Trigger Only):

  • 0 = Exact match: triggers only when item.value == compareValue (e.g., only responds to commands with value="ON")
  • 1 = Set mode: no value comparison, triggers as long as item.id == triggerPeriphId, and injects item.value as receivedValue into subsequent actions

Other operatorType values (2=GT/3=LT/4=GTE/5=LTE/6=BETWEEN/7=NOT_BETWEEN/8=CONTAIN/9=NOT_CONTAIN) are commonly used only with poll triggers; platform triggers typically use only 0/1.

Typical Configuration:

json
{ "triggerType": 0, "triggerPeriphId": "relay_01", "operatorType": 1, "compareValue": "" }

Meaning: Receiving platform commands {"id":"relay_01","value":"ON"} or {"id":"relay_01","value":"OFF"} will both trigger; actions can use the $value placeholder to read the delivered value.

3.2 Timer Trigger (TIMER_TRIGGER=1) ​

Purpose: Periodic execution by interval or daily time point.

Fields:

FieldDescription
timerMode0=Interval (every N seconds) / 1=Daily time point (HH:MM)
intervalSecInterval in seconds (effective when timerMode=0, minimum 1s, recommended ≥5s to avoid frequent wake-ups)
timePoint"HH:MM" (effective when timerMode=1, 24h format; requires NTP time sync)

Typical Configuration:

json
// Refresh OLED every 15 seconds
{ "triggerType": 1, "timerMode": 0, "intervalSec": 15 }

// Trigger watering every day at 6:30 AM
{ "triggerType": 1, "timerMode": 1, "timePoint": "06:30" }

Daily time point trigger depends on system time; NTP is strongly recommended. No trigger fires before time is synced (getLocalTime() fails).

3.3 Event Trigger (EVENT_TRIGGER=4) ​

Purpose: Respond to system events (WiFi/MQTT/NTP/buttons/OTA/peripheral execution completion/protocol data send/receive, etc.).

Fields:

FieldDescription
eventIdRequired. Event ID string (see table below)

Common Event ID Quick Reference (see the static event table in src/core/PeripheralExecution.cpp for the full list):

CategoryeventIdDescription
WiFiwifi_connected / wifi_disconnected / wifi_conn_failedWiFi connection state
MQTTmqtt_connected / mqtt_disconnected / mqtt_enabledMQTT protocol state
Network Modenet_mode_ap / net_mode_staAP/STA switch
Systemntp_synced / ota_start / ota_success / ota_failedSystem services
System Statesystem_boot / system_ready / system_error / factory_resetLifecycle
Buttonbutton_click / button_double_click / button_long_press_2s / button_long_press_5s / button_long_press_10s / button_press / button_releaseButton detection from digital input pull-up/pull-down pins (50ms debounce, 300ms double-click window)
Ruleperiph_exec_completedFires when any peripheral execution rule completes
Datadata_receive / data_reportProtocol data send/receive (replaces deprecated 2/3)
Device Eventsbreak_down / restart / device_alarm / low_powerCan be actively emitted by rules for MQTT reporting
Custom EventsIDs from DEVICE_EVENT peripherals (type=60)User-defined events, can be emitted by ACTION_TRIGGER_EVENT
Modbus Control Eventsmc:<deviceIndex>Emitted after a control-type Modbus sub-device is written successfully; compareValue stores the channel/action filter JSON generated by the Web UI

Typical Configuration:

json
// Light up LED after WiFi connects
{ "triggerType": 4, "eventId": "wifi_connected" }

// Toggle relay on button double-click
{ "triggerType": 4, "eventId": "button_double_click", "triggerPeriphId": "btn_01" }

Button events also support triggerPeriphId filtering: only button events from that peripheral ID will match. Leave empty to match all buttons.

3.4 Poll Trigger (POLL_TRIGGER=5) ​

Purpose: Locally and periodically read sensor / Modbus slave data; triggers when readings meet conditions.

Fields:

FieldDescription
triggerPeriphIdRequired. Data source peripheral ID (can be DHT/DS18B20/Modbus slave/ADC, etc.)
operatorTypeCondition operator (0=EQ / 1=NEQ / 2=GT / 3=LT / 4=GTE / 5=LTE / 6=BETWEEN / 7=NOT_BETWEEN / 8=CONTAIN / 9=NOT_CONTAIN)
compareValueComparison threshold; BETWEEN/NOT_BETWEEN uses "min,max" format (e.g., "20,30")
intervalSecPoll period in seconds (recommended ≥5s; <5s is corrected by safety lower limit)
pollResponseTimeoutModbus response timeout (ms), default 1000
pollMaxRetriesModbus max retries, default 2
pollInterPollDelayMinimum interval between multiple slaves on the same bus (ms), default 100

Typical Configuration:

json
// Trigger when temperature > 30°C (DHT11 polls every 30 seconds)
{
  "triggerType": 5,
  "triggerPeriphId": "dht_01",
  "operatorType": 2,
  "compareValue": "30",
  "intervalSec": 30
}

// Alert when Modbus slave register value is outside [1000, 2000]
{
  "triggerType": 5,
  "triggerPeriphId": "mb_slave_01",
  "operatorType": 7,
  "compareValue": "1000,2000",
  "intervalSec": 10,
  "pollResponseTimeout": 1500,
  "pollMaxRetries": 3
}

Performance Tip: Poll triggers bind hardware tasks. Too many active poll tasks (>8) or too short intervals (<5s) will trigger safety correction logs. For Modbus polling, also note serial bus contention; recommend pollInterPollDelay ≥ 100ms between multiple slaves.

3.5 Trigger Type Comparison Table ​

DimensionPlatform (0)Timer (1)Event (4)Poll (5)
Trigger SourceIoT platform MQTT deliverySystem clockSystem/button/protocol eventsLocal sensor periodic reading
Works Offline❌ Requires MQTT online✅ Fully offline✅ All local events supported✅ Fully offline
receivedValue SourcePlatform-delivered valueNoneEvent-attached data (can be empty)Local reading
Key FieldstriggerPeriphId + operatorType+compareValuetimerMode + intervalSec / timePointeventId (optional triggerPeriphId)triggerPeriphId + operatorType + compareValue + intervalSec
Typical ScenariosRemote control, remote parameter settingTimed watering, timed display refreshWiFi disconnect alarm, button toggle, OTA completion alertTemperature control, threshold alarm, local interlocking
Recommended Period Lower Limit—≥1s (recommended ≥5s)Event-driven, no period≥5s (below is corrected)

4. Action Details ​

All actions are written into the same array and executed in index order. Each action can carry syncDelayMs (pre-execution delay, max 10000ms) for timing orchestration.

4.1 GPIO Output Actions (0/1/13/14) ​

actionTypeNameactionValueTargetDescription
0ACTION_HIGH—GPIO_DIGITAL_OUTPUTSet HIGH
1ACTION_LOW—GPIO_DIGITAL_OUTPUTSet LOW
13ACTION_HIGH_INVERTED—GPIO_DIGITAL_OUTPUT"Logic HIGH" but physical output LOW (for active-low relays)
14ACTION_LOW_INVERTED—GPIO_DIGITAL_OUTPUT"Logic LOW" but physical output HIGH

Purpose of inverted actions: Some relay modules are "active-low driven"; using ACTION_HIGH directly would turn them off. ACTION_HIGH_INVERTED maintains the "HIGH = ON" semantic intuition.

4.2 PWM / Analog Output Actions (2/3/4/5) ​

actionTypeNameactionValue FormatTarget
2ACTION_BLINKBlink period ms (e.g., "500")PWM/GPIO output
3ACTION_BREATHEBreathe period ms (e.g., "2000")PWM output
4ACTION_SET_PWMDuty cycle 0-255 or percentage 0-100GPIO_PWM_OUTPUT
5ACTION_SET_DACDAC value 0-255DAC (ESP32 DAC-supported pins only)

When useReceivedValue=true is enabled, actionValue is overridden by the trigger source's received value, suitable for platform-delivered brightness/duty cycle, etc.

4.3 System Management Actions (6/7/8/9) ​

actionTypeNametargetPeriphIdactionValue
6ACTION_SYS_RESTARTCan be empty—
7ACTION_SYS_FACTORY_RESETCan be empty—
8ACTION_SYS_NTP_SYNCCan be emptyOptional NTP server
9ACTION_SYS_OTACan be emptyFirmware URL

System actions do not require targetPeriphId as they affect the entire device. Place them at the end of the action list (to prevent subsequent actions from failing due to restart).

4.4 Call / Script Actions (10/15) ​

actionTypeNameDescription
10ACTION_CALL_PERIPHERALForwards actionValue as a command to the targetPeriphId peripheral; stepper motors support JSON commands
15ACTION_SCRIPTExecutes a command sequence script; actionValue is the script content. See script-guide.md

Stepper motors (STEPPER_MOTOR, type=42) are recommended to use JSON format to avoid command parameter ambiguity:

actionValue ExampleDescription
{"periphId":"stepper","action":"forward"}Forward
{"periphId":"stepper","action":"reverse"}Reverse
{"periphId":"stepper","action":"stop"}Stop and release coils
{"periphId":"stepper","action":"faster","value":"2"}Speed up by 2 RPM
{"periphId":"stepper","action":"slower","value":"2"}Slow down by 2 RPM
{"periphId":"stepper","action":"setSpeed","value":"12"}Set speed to 12 RPM

4.5 Modbus Actions (16/17/18) ​

actionTypeNameactionValueDescription
16ACTION_MODBUS_COIL_WRITE"coilAddr:0/1" or standalone "0/1"Modbus FC05 write coil
17ACTION_MODBUS_REG_WRITE"regAddr:value" or standalone "value"Modbus FC06 write register
18ACTION_MODBUS_POLLNoneActively trigger one slave data collection (scheduled by PeriphExec)

targetPeriphId must be a configured Modbus slave device (MODBUS_DEVICE, type=51).

4.6 Data / Event Actions (19/21) ​

actionTypeNameactionValueTarget
19ACTION_SENSOR_READ—Sensors (DHT/DS18B20/ADC/pulse), used to actively trigger one collection and cache
21ACTION_TRIGGER_EVENTEvent extra data (can be empty)targetPeriphId = event ID (system built-in or DEVICE_EVENT peripheral ID)

ACTION_TRIGGER_EVENT is used for inter-rule linkage: one rule can act as an event source to wake other rules triggered by that event.

4.7 Rule Control Actions (22/23) ​

actionTypeNametargetPeriphIdDescription
22ACTION_ENABLE_EXEC_RULEid of another ruleEnable that rule
23ACTION_DISABLE_EXEC_RULEid of another ruleDisable that rule

Used for mode switching: e.g., button long-press 5s to toggle "Day Mode" / "Night Mode" (enable one set of rules while disabling another).

4.8 Display Actions (24/25/26/27) ​

actionTypeNameactionValueTarget Peripheral TypeCompile Switch
24ACTION_DISPLAY_NUMBERNumber: "12.34" / "12:34" / "1234" / "-12", supports ${id.field}SEVEN_SEGMENT_TM1637 (47)FASTBEE_ENABLE_SEVEN_SEGMENT
25ACTION_DISPLAY_TEXTText (max 4 chars, limited charset), supports ${id.field}SEVEN_SEGMENT_TM1637 (47)FASTBEE_ENABLE_SEVEN_SEGMENT
26ACTION_DISPLAY_CLEAR—SEVEN_SEGMENT_TM1637 (47)FASTBEE_ENABLE_SEVEN_SEGMENT
27ACTION_OLED_DISPLAYMulti-line text, supports ${id.field} + $value; first line # prefix = centered title + separator; \n line break; max 512 charsLCD (36)FASTBEE_ENABLE_LCD

Note: targetPeriphId must match the corresponding hardware type; mismatch (e.g., assigning 24 to an LCD peripheral) will be rejected at runtime with a warning log. See Chapter 6 Merge Assessment.

4.9 Complete Action Type Quick Reference ​

IDNameCategoryKey Parameters
0ACTION_HIGHGPIO—
1ACTION_LOWGPIO—
2ACTION_BLINKPWMPeriod ms
3ACTION_BREATHEPWMPeriod ms
4ACTION_SET_PWMPWM0-255 / 0-100%
5ACTION_SET_DACDAC0-255
6ACTION_SYS_RESTARTSystem—
7ACTION_SYS_FACTORY_RESETSystem—
8ACTION_SYS_NTP_SYNCSystemNTP server
9ACTION_SYS_OTASystemFirmware URL
10ACTION_CALL_PERIPHERALCommunicationCommand string
13ACTION_HIGH_INVERTEDGPIO—
14ACTION_LOW_INVERTEDGPIO—
15ACTION_SCRIPTScriptScript text
16ACTION_MODBUS_COIL_WRITEModbusaddr:val
17ACTION_MODBUS_REG_WRITEModbusaddr:val
18ACTION_MODBUS_POLLModbus—
19ACTION_SENSOR_READData—
20Reserved—Legacy buzzer preset removed
21ACTION_TRIGGER_EVENTEventEvent data
22ACTION_ENABLE_EXEC_RULERule ControlRule ID
23ACTION_DISABLE_EXEC_RULERule ControlRule ID
24ACTION_DISPLAY_NUMBERDisplayNumber string
25ACTION_DISPLAY_TEXTDisplay≤4 chars
26ACTION_DISPLAY_CLEARDisplay—
27ACTION_OLED_DISPLAYDisplayMulti-line + template

5. Templates & Dynamic Values ​

Action actionValue supports three dynamic value mechanisms:

5.1 ${periphId.field} Sensor Template ​

Reads the latest value from the local sensor data cache and substitutes it.

Supported fields (by sensor type):

  • DHT11 / DHT22: temperature / humidity
  • DS18B20: temperature
  • Analog input (ADC / GPIO_ANALOG_INPUT): value / voltage
  • Digital input: value (0/1)
  • Modbus slave: reg_<N> / coil_<N>

Example:

text
"# Environment Monitor\nTemp: ${dht_01.temperature}°C\nHumidity: ${dht_01.humidity}%"

5.2 $value Received Value Placeholder (OLED Action 27 Only) ​

Replaced with the original value from the trigger source:

  • Platform trigger: MQTT-delivered item.value
  • Poll trigger: Current peripheral reading
  • Event trigger: Event-attached data

Example:

text
"# Platform Delivery\nValue: $value"

When MQTT delivers {"id":"lcd_01","value":"Hello"}, it displays Value: Hello.

5.3 useReceivedValue=true (Universal) ​

When this flag is set on an ExecAction, the entire actionValue is completely replaced by the "received value" (suitable for PWM/DAC/relays directly using platform-delivered numbers).


6. Should Display Actions Be Merged? Assessment ​

The community has proposed "merging Display Number (24) / Display Text (25) / Clear Display (26) / OLED Custom Display (27) into a single universal action to simplify configuration". After assessment, the current 4 independent actions are retained for the following reasons:

6.1 Fundamental Hardware Differences ​

DimensionTM1637 (24/25/26)OLED/LCD (27)
Driver LayerSevenSegmentDriver (bit-bang)LCDManager (U8g2 graphics library)
Display Capability4-digit 7-segment (limited charset)128×64 graphics matrix, multi-line text
Parameter FormatSimple stringMulti-line + template + # title + \n
Compile SwitchFASTBEE_ENABLE_SEVEN_SEGMENTFASTBEE_ENABLE_LCD
UI Control<input><textarea rows=6 maxlength=512>

6.2 Problems Caused by Merging ​

  • Parameter validation complexity: After merging, the executor must implicitly determine field semantics based on target peripheral type, making error messages hard to pinpoint.
  • Breaking compatibility: Existing configurations with actionType=24/25/26/27 would need migration scripts.
  • Frontend forms would be more complex: A "universal display" action would require extensive conditional rendering (target type → parameter controls), harder to maintain than the current 4 independent actions.

6.3 Current Compromise Solution ​

"Frontend categorical merge + backend remains independent":

  • The frontend UI (web-src/modules/runtime/periph-exec-form.js) groups all four actions under the "Display" category (periph-exec-action-cat-display); users see them in the same dropdown category.
  • Parameter controls automatically switch based on actionType: 24/25 use input, 26 has no parameters, 27 uses textarea.
  • Backend enums remain independent; logs are clear; runtime type validation is strict (peripheral type mismatch is rejected immediately).

6.4 Optional Further Simplification (Future Optimization) ​

If reducing user selection cost is still desired, the recommended approach is to enhance the frontend only (no backend changes):

User first selects targetPeriphId → frontend only shows corresponding actions based on target peripheral type:

  • Selected SEVEN_SEGMENT_TM1637 → only show 24/25/26
  • Selected LCD → only show 27

With this approach, from the user's perspective it feels like "there's only one display action", while keeping the backend unchanged. This is the recommended progressive optimization path.


7. Typical Configuration Examples ​

7.1 Periodic OLED Environment Monitor Refresh ​

json
{
  "id": "exec_oled_env",
  "name": "OLED Environment Monitor (15s)",
  "enabled": true,
  "triggers": [
    { "triggerType": 1, "timerMode": 0, "intervalSec": 15 }
  ],
  "actions": [
    {
      "targetPeriphId": "lcd_01",
      "actionType": 27,
      "actionValue": "# Environment Monitor\nTemp: ${dht_01.temperature}°C\nHumidity: ${dht_01.humidity}%\nIP: ${sys.ip}"
    }
  ]
}

7.2 Button Double-Click Toggle Relay ​

json
{
  "id": "exec_btn_relay_toggle",
  "name": "Button Double-Click Toggle Relay",
  "enabled": true,
  "triggers": [
    { "triggerType": 4, "eventId": "button_double_click", "triggerPeriphId": "btn_01" }
  ],
  "actions": [
    { "targetPeriphId": "relay_01", "actionType": 15, "actionValue": "toggle relay_01" }
  ]
}

7.3 High Temperature Alarm (Fan + OLED + MQTT Event Report) ​

json
{
  "id": "exec_high_temp_alarm",
  "name": "High Temp Alarm",
  "enabled": true,
  "triggers": [
    { "triggerType": 5, "triggerPeriphId": "dht_01", "operatorType": 2, "compareValue": "32", "intervalSec": 10 }
  ],
  "actions": [
    { "targetPeriphId": "fan_01", "actionType": 0, "execMode": 1 },
    { "targetPeriphId": "lcd_01", "actionType": 27, "actionValue": "# High Temp Alarm\nTemp: ${dht_01.temperature}°C\nPlease ventilate", "syncDelayMs": 200 },
    { "targetPeriphId": "device_alarm", "actionType": 21, "actionValue": "temp_high" }
  ]
}

7.4 Platform-Delivered PWM Brightness Control ​

json
{
  "id": "exec_platform_pwm",
  "name": "Platform Light Strip Brightness",
  "enabled": true,
  "triggers": [
    { "triggerType": 0, "triggerPeriphId": "pwm_led", "operatorType": 1, "compareValue": "" }
  ],
  "actions": [
    { "targetPeriphId": "pwm_led", "actionType": 4, "actionValue": "128", "useReceivedValue": true }
  ]
}

When platform delivers {"id":"pwm_led","value":"200"}, PWM duty cycle is set to 200 (useReceivedValue causes actionValue to be overridden by the received value).

7.5 WiFi Disconnect Alarm + OLED Display ​

json
{
  "id": "exec_wifi_lost",
  "name": "WiFi Disconnect Alarm",
  "enabled": true,
  "triggers": [
    { "triggerType": 4, "eventId": "wifi_disconnected" }
  ],
  "actions": [
    { "targetPeriphId": "status_led", "actionType": 2, "actionValue": "500" },
    { "targetPeriphId": "lcd_01", "actionType": 27, "actionValue": "# Network Error\nWiFi Disconnected\nSystem will auto-reconnect", "syncDelayMs": 300 }
  ]
}

7.6 Periodic 7-Segment Display Time (TM1637) ​

json
{
  "id": "exec_tm1637_time",
  "name": "7-Segment Display Time",
  "enabled": true,
  "triggers": [
    { "triggerType": 1, "timerMode": 0, "intervalSec": 30 }
  ],
  "actions": [
    { "targetPeriphId": "tm1637_01", "actionType": 24, "actionValue": "${sys.time_hhmm}" }
  ]
}

7.7 Button Long-Press 5s Toggle Day/Night Mode (Rule Linkage) ​

json
{
  "id": "exec_mode_switch",
  "name": "Long-Press Mode Switch",
  "enabled": true,
  "triggers": [
    { "triggerType": 4, "eventId": "button_long_press_5s", "triggerPeriphId": "btn_01" }
  ],
  "actions": [
    { "targetPeriphId": "exec_day_mode",   "actionType": 23 },
    { "targetPeriphId": "exec_night_mode", "actionType": 22 }
  ]
}

8. Best Practices ​

8.1 Trigger Type Selection ​

ScenarioRecommended Trigger
Platform remote controlPlatform Trigger (0)
Periodic display refresh / collection reportingTimer Trigger (1)
WiFi/MQTT/button system state changesEvent Trigger (4)
Local sensor threshold alarmPoll Trigger (5)

8.2 Performance & Resources ​

  • Poll interval ≥ 5 seconds: Below 5s is corrected by validateRuntimeConfig; Modbus polling recommended ≥ 10s.
  • Active async tasks ≤ 3: MAX_ASYNC_TASKS=3; excess is queued. For dense rules, consider using execMode=1 synchronous execution for simple GPIO actions.
  • Avoid rules waking each other in loops: When using ACTION_TRIGGER_EVENT with EVENT_PERIPH_EXEC_COMPLETED, add guard conditions to prevent infinite loops.

8.3 Orchestration & Timing ​

  • Multiple actions execute sequentially in array order; use syncDelayMs for simple orchestration (e.g., turn on fan first, then refresh screen).
  • syncDelayMs max is 10000ms; for longer waits, split into two rules + event bridging.

8.4 Template Usage ​

  • OLED multi-line templates with # at the start of the first line produce a centered title + separator line, improving readability.
  • For sensor data field names, refer to each sensor chapter in peripheral-configuration-guide.md.
  • $value is only enabled in OLED action 27; for other actions needing received values, use useReceivedValue=true.

8.5 Persistence & Backup ​

  • Configuration file /config/periph_exec.json is on LittleFS; recommend periodic backup export via the UI's "Import/Export" feature.
  • When batch adding/removing rules, keep an eye on file size (long-term observation suggests ≤ 32KB is safe, depending on LittleFS partition).

9. FAQ ​

Q1: Why isn't my rule triggering? ​

Check each item:

  1. Is the rule enabled set to true?
  2. Does the trigger type match (e.g., event trigger missing eventId)?
  3. Is the corresponding compile switch enabled (for example, Modbus RTU actions need FASTBEE_ENABLE_MODBUS_RTU)?
  4. Are there [PeriphExec] related warnings in device logs (data/logs/system.log)?
  5. For poll triggers, can the sensor read correctly (test manually in UI)?

Q2: What happens if OLED action (27) targets a TM1637 peripheral? ​

The runtime executor logs warning: "OLED_DISPLAY target '%s' is not LCD" and returns failure immediately. The UI form also filters available targetPeriphId based on actionType. Always match action type to target peripheral type.

Q3: How to display MQTT-delivered values on OLED? ​

Two steps:

  1. Use platform trigger with operatorType=1 (set mode, no comparison).
  2. Include $value placeholder in action 27's actionValue.

When {"id":"lcd_01","value":"Hello"} is delivered, $value in the display text is replaced with Hello.

Q4: Can I set poll trigger period to 1 second? ​

Not recommended. The system corrects <5s periods to the absolute lower limit of 5s. For Modbus slave polling with multiple devices on the bus, recommend intervalSec ≥ 10s and set pollInterPollDelay ≥ 100ms to avoid bus congestion.

Q5: How to do both "local alarm" and "platform report" for the same sensor? ​

Two rules:

  • Rule A: Poll trigger temperature > threshold → Fan + OLED (local alarm)
  • Rule B: Timer trigger every 60s → ACTION_SENSOR_READ (trigger collection) + reportAfterExec=true (report to MQTT)

Or chain in one rule's action array: collection → alarm → report, with reportAfterExec enabled.

Q6: Why aren't button events triggering? ​

Button events depend on GPIO_DIGITAL_INPUT_PULLUP / GPIO_DIGITAL_INPUT_PULLDOWN peripheral types (see supportsButtonEvent). Check:

  1. Is the peripheral type correct (type=12 or 13)?
  2. Debounce and double-click window: pressing too fast may be recognized as a single click instead of double-click (double-click window is 200ms).
  3. Does triggerPeriphId match the button peripheral ID (leave empty to match all buttons)?

Q7: What events can ACTION_TRIGGER_EVENT emit? ​

  • System built-in event IDs: e.g., break_down/device_alarm/low_power/restart (see section 3.3).
  • Custom events: First create a DEVICE_EVENT (type=60) peripheral in peripheral management; its id can then be used as targetPeriphId.

Q8: Does executing one rule affect other rules? ​

By default, async execution (independent FreeRTOS tasks) does not block each other. However, shared hardware (e.g., shared I²C bus, serial port) still needs to avoid concurrent contention. With many rules, prefer async; for short GPIO actions, consider sync (execMode=1) to reduce task creation overhead.

Q9: What is reportAfterExec? ​

After execution completes, the action execution results (including targetPeriphId, actualValue, and success/failure reason) are reported through the selected active protocol path. The release execution chain supports MQTT and Modbus RTU; compatibility values 2-5 do not create protocol clients. Reporting is enabled by default; disable it for local-only interlocking (for example, a local alarm) to reduce upstream traffic.

Q10: How to debug rules? ​

  1. Enable serial logging (115200), look for [PeriphExec] prefix.
  2. Use the UI "Run Once" button to manually trigger once.
  3. Use ACTION_TRIGGER_EVENT to emit device_alarm events and observe whether the MQTT subscriber receives the corresponding topic.
  4. Temporarily change execMode to sync; the logs will show serial execution order, helping locate failure steps.

FastBee | fastbee.cn