Skip to main content
The twin command requires the iot feature to be enabled during installation.
The twin command runs digital twin simulations for IoT devices with support for multiple protocols and physics strategies.

run

Start a digital twin simulation from a device definition.
apicentric twin run --device <path> --library <path> [flags]
--device
string
required
Path to the device definition YAML file
--library
string
required
Path to the device library or resources directory
--override-config
string
Path to a configuration override file

Device definition structure

A device definition file describes the twin configuration:
twin:
  name: temperature-sensor
  transports:
    - adapter_type: mqtt
      params:
        broker: mqtt://localhost:1883
        client_id: sensor-001
        subscriptions:
          - "commands/sensor-001"
  physics:
    - strategy: sine
      variable: temperature
      params:
        min: 18.0
        max: 26.0
    - strategy: script
      variable: humidity
      params:
        code: |
          let base = 50.0;
          let variation = 10.0 * (time / 60.0).sin();
          base + variation

Supported protocols

MQTT

Message Queue Telemetry Transport for lightweight pub/sub messaging. Configuration:
- adapter_type: mqtt
  params:
    broker: mqtt://localhost:1883
    client_id: device-001
    subscriptions:
      - "commands/+"
      - "updates/#"
Parameters:
  • broker: MQTT broker URL
  • client_id: Unique client identifier
  • subscriptions: Topics to subscribe to (supports wildcards)

Modbus

Modbus TCP/RTU protocol for industrial equipment. Configuration:
- adapter_type: modbus
  params:
    host: localhost
    port: 502
    unit_id: 1
Parameters:
  • host: Modbus server host
  • port: Modbus server port
  • unit_id: Modbus unit/slave ID

Physics strategies

Physics strategies simulate sensor behavior and data generation.

Sine wave

Generate smooth oscillating values.
- strategy: sine
  variable: temperature
  params:
    min: 15.0
    max: 30.0
Parameters:
  • min: Minimum value
  • max: Maximum value
  • Frequency: 0.1 Hz (default)
Use cases:
  • Temperature cycles
  • Pressure variations
  • Voltage fluctuations

Noise sine

Sine wave with added randomness.
- strategy: noise_sine
  variable: temperature
  params:
    min: 15.0
    max: 30.0
In the current implementation, noise_sine behaves identically to the sine strategy. Use the script strategy for custom noise patterns.

Script (Rhai)

Custom behavior using Rhai scripting language.
- strategy: script
  variable: rpm
  params:
    code: |
      let base_rpm = 1000.0;
      let load_factor = state.get("load").unwrap_or(0.5);
      base_rpm * (1.0 + load_factor)
Available in scripts:
  • time: Simulation time in seconds
  • state: Access to other twin variables
  • Math functions: sin(), cos(), sqrt(), etc.
Use cases:
  • Complex sensor relationships
  • State machines
  • Conditional logic
  • Custom algorithms

Replay

Replay historical data from CSV files.
- strategy: replay
  variable: power_consumption
  params:
    file: data/power-data.csv
    column: power_kw
    loop: true
Parameters:
  • file: Path to CSV file (relative to device definition)
  • column: Column name to read (optional, uses first column if omitted)
  • loop: Restart from beginning when file ends (default: true)
CSV format:
timestamp,power_kw,voltage
2024-01-01 00:00:00,2.5,220
2024-01-01 00:01:00,2.8,221
2024-01-01 00:02:00,2.3,219

Usage examples

Simple temperature sensor

apicentric twin run --device devices/temp-sensor.yaml --library ./device-lib
Device definition (temp-sensor.yaml):
twin:
  name: temp-sensor
  transports:
    - adapter_type: mqtt
      params:
        broker: mqtt://localhost:1883
        client_id: temp-001
  physics:
    - strategy: sine
      variable: temperature
      params:
        min: 20.0
        max: 25.0

Industrial pump with Modbus

apicentric twin run --device devices/pump.yaml --library ./lib
Device definition (pump.yaml):
twin:
  name: industrial-pump
  transports:
    - adapter_type: modbus
      params:
        host: 192.168.1.100
        port: 502
        unit_id: 1
  physics:
    - strategy: script
      variable: flow_rate
      params:
        code: |
          let base = 100.0;
          let noise = 5.0 * (time * 3.14).sin();
          base + noise
    - strategy: script
      variable: pressure
      params:
        code: |
          let flow = state.get("flow_rate").unwrap_or(100.0);
          2.5 * flow / 100.0

Data replay simulation

apicentric twin run --device devices/replay-device.yaml --library ./lib
Device definition (replay-device.yaml):
twin:
  name: historical-sensor
  transports:
    - adapter_type: mqtt
      params:
        broker: mqtt://localhost:1883
        client_id: replay-001
  physics:
    - strategy: replay
      variable: energy
      params:
        file: historical-data.csv
        column: energy_kwh
        loop: true

Simulation loop

The twin runs in a continuous loop:
  1. Poll adapters: Check for incoming messages from protocols
  2. Update state: Apply received data to twin state variables
  3. Tick physics: Execute all physics strategies
  4. Publish state: Send updated variables to protocol adapters
  5. Precise timing: Wait for next tick (1 second interval)

State management

The twin maintains a state object with all variables:
twin.state.variables = {
  "temperature": 22.5,
  "humidity": 55.0,
  "pressure": 1013.25
}
Variables can:
  • Be set by physics strategies
  • Be updated by protocol messages
  • Be read by other strategies
  • Be published to protocols

Error handling

Device file not found

❌ Failed to read device file 'devices/sensor.yaml': No such file or directory
💡 Check if the file exists and is readable

Invalid YAML

❌ Failed to parse device config: invalid type: string "true", expected a boolean
💡 Check YAML syntax

Unknown adapter

error: Unknown transport type: http
Supported adapters: mqtt, modbus

Missing replay file

❌ Missing 'file' parameter for replay strategy
💡 Add 'file: path/to/data.csv' to params

Performance

  • Tick rate: 1 second (1 Hz)
  • Timing: Precise, compensates for processing time
  • Lag handling: Catches up if tick processing is slow
  • Async: All I/O operations are non-blocking

Monitoring

The twin logs activity to stdout:
INFO Starting Digital Twin simulation...
INFO Loaded twin definition: temperature-sensor
INFO Entering simulation loop...
Errors are logged but don’t crash the simulation:
ERROR Simulation tick error: Failed to publish to MQTT

Use cases

Testing IoT backends

Simulate hundreds of devices without physical hardware

Protocol development

Develop and test protocol handlers with realistic data

Training and demos

Demonstrate IoT systems without real devices

Data generation

Generate test data for analytics and ML models

Advanced features

Config overrides

Override device configuration at runtime:
apicentric twin run --device device.yaml --library ./lib --override-config overrides.yaml
overrides.yaml:
twin:
  transports:
    - adapter_type: mqtt
      params:
        broker: mqtt://production-broker:1883

Multiple devices

Run multiple twins in parallel (requires separate processes):
apicentric twin run --device device1.yaml --library ./lib &
apicentric twin run --device device2.yaml --library ./lib &
apicentric twin run --device device3.yaml --library ./lib &

Dry run

Test configuration without connecting to protocols:
apicentric --dry-run twin run --device device.yaml --library ./lib

Next steps

Build docs developers (and LLMs) love