• Skip to main content
  • Skip to header right navigation
  • Skip to site footer
CDP Studio logo

CDP Studio

The no-code and full-code software development tool for distributed control systems and HMI

  • Doc
  • Why CDP
    • Software developers
    • Automation engineers
    • Managers
  • Products
    • Automation Designer
    • HMI Designer
    • CDP Linux
  • Services
  • Use cases
  • Pricing
  • Try CDP

CDP Studio Documentation

  • Examples and Tutorials
  • Basic Function Block Tutorial - Motor Start/Stop and Ramp

Using Anti-Sway on a Crane Using Sequence Steps to Operate Doors

Basic Function Block Tutorial - Motor Start/Stop and Ramp

Why This Tutorial

This is a hands-on exercise for learning Basic Block configuration in Configure mode. You will create a practical control setup: a small motor controller state-machine and an external actuator path with start, stop, ramp-up, and ramp-down behavior.

The tutorial is progressive. Each step adds a small set of concepts and keeps the previous setup.

Prerequisites

  • Open a system in Configure mode.
  • Have one application component where you can add blocks (for example App.Controller).

Step 1 - Create a Minimal Two-State Motor Block

Goal

Build a block that switches between Stopped and Running using events.

Step-by-Step

  • In the Project tree, select the component where you want to add the controller block.
  • In the Resource tree, find Sequencer -> BasicBlockHelpers and right-click Sequencer.BasicBlock_2S, then select Add.

    Note: The Sequencer.BasicBlock_2S is a BasicFunctionBlock that is already set up with two states and transitions between them to make it easier to get started.

  • Rename the added block to MotorCtrl.
  • Double-click MotorCtrl to navigate into it.
  • In the Resource tree, right-click Sequencer.EventIn and select Add.

    Note: Sequencer.EventIn is used to receive messages as events and trigger state-machine processing. In addition to being pure triggers, the events can bundle Data that will be unpacked to all matching (by name) local Arguments in the object containing the Event.

  • Rename it to Start.
  • Add another Sequencer.EventIn to MotorCtrl, and name it Stop.
  • Rename states:
    • Null -> Stopped
    • Work -> Running
  • Rename transitions:
    • ToWork -> ToRunning
    • ToNull -> ToStopped

    Note: Renaming states and transitions early keeps the Execution Control Chart (ECC) self-explanatory and makes later routing and troubleshooting easier.

  • Connect events to transitions:
    • Start to ToRunning
    • Stop to ToStopped

    You can do this either by dragging a route in the block diagram or by setting the transition Routing property.

Run & Test

  • Make sure MotorCtrl.ProcessOn is set to Event.

    Note: ProcessOn can handle two processing modes: Event-based and/or Cyclic. Events are occasional and message-driven, while Cyclic triggers at a repeated deterministic interval.

  • Select the system in the Project tree and choose Run & Connect.
  • Navigate into MotorCtrl.
  • Select the Start event, and in the Messages table, right-click 0x20100 and select Add to Watch -> Add.
  • Do the same with the Stop event.
  • From the Watch list, you can now click Send to trigger Start and Stop events for the state-machine.

Expected Behavior

  • Sending Start transitions the state-machine to Running.

    Note: The active state is colored green, while the previously activated states are colored yellow.

  • Sending Stop transitions the state-machine back to Stopped.

What We Learned

  • Creating a simple state-machine.
  • Using events to control it.

Step 2 - Add Speed Arguments and Simple RunScript Actions

Goal

Make each state write a motor command value.

Step-by-Step

  • Stop the system if it is currently running.
  • Select MotorCtrl in the Project tree.
  • In the Resource tree, right-click Sequencer.Argument<double> and select Add. Rename it to SpeedRef.
  • Add a Sequencer.ArgumentOut<double> and rename it to SpeedCmd.

    Note: Sequencer.ArgumentOut<> can be routed from multiple sources. We can later route the SpeedCmd from multiple states, but only the active state will update it.

  • Navigate into MotorCtrl, then select state Running.
  • In the Resource tree, right-click Sequencer.RunScript and select Add. Rename it to SetRunCmd.
  • Set SetRunCmd.Script:
    SpeedCmd = SpeedRef
  • Navigate into state Stopped and add another Sequencer.RunScript. Rename it to SetStopCmd.
  • Set SetStopCmd.Script:
    SpeedCmd = 0.0

Note: RunScripts (and also RunOperations, which we introduce later) are only executed when a state-transition occurs, in this case when a Start or Stop Event triggers it. If there are extra conditions on transitions so the transition is not taken, then the current state RunScript (or RunOperation) will not run.

Run & Test

  • Select the system in the Project tree and choose Run & Connect.
  • Add SpeedRef and SpeedCmd to the Watch list.
  • Set SpeedRef to a non-zero value.
  • Trigger Start from the Watch list, then trigger Stop.

Expected Behavior

  • When entering Running, SpeedCmd is set to SpeedRef because the state-transition triggers the RunScript.

    Note: Changing SpeedRef during Running does not update SpeedCmd, because Running is only executed on events in this step.

  • When returning to Stopped state, SpeedCmd returns to 0.

What We Learned

  • Adding input and output Arguments to the state-machine.
  • Using RunScripts to control the Arguments.
  • RunScript is excellent for short state-local logic. If logic starts growing, move it to operators for easier debugging, and use RunOperation instead to run those operators.
  • Since we run the state-machine on Event, the RunScripts are only executed when the event causes a state-transition.

Step 3 - Enable Cyclic Behavior (E+C with O+P)

Goal

Run state logic on every cycle while the state remains active.

Note: Without Cyclic running, properties in the state, such as Time, are only updated on received Events.

Step-by-Step

  • Stop the system if it is currently running.
  • Select MotorCtrl in the Project tree.
  • In the property table, set ProcessOn = E+C, meaning:
    • E - Process state-machine on Event
    • C - Process state-machine Cyclically (based on closest parent CDPComponent fs)
  • In the property table, set TransitionMode = O+P, which has the following meanings:
    • O - One transition per execution
    • P - Re-Process current state when not able to transition

    Note: Setting O+P makes the state-machine run similarly to the CDPComponent scheduler: select a new state (if any) and run it once, otherwise run the current state once.

Run & Test

  • Select the system in the Project tree and choose Run & Connect.
  • Trigger Start.
  • Change SpeedRef a few times while staying in Running.

Expected Behavior

  • SpeedCmd updates every cycle while Running is active.
  • You do not need a new event for each SpeedRef adjustment.

What We Learned

  • How to set the state-machine to run both on event and cyclically.
  • O allows one transition per process call and gives predictable Execution Control Chart (ECC) stepping.
  • P runs current state logic also when no transition is taken.
  • This is the same transition-mode combination used in Sequencer.CyclicStateMachine and gives behavior similar to traditional cyclic CDP control blocks.
  • State-properties, such as Time, are only updated on received Events in E mode but on every cycle in C mode.

Step 4 - Replace Scripts With RunOperation

Goal

Implement speed changes using OperatorContainers instead of scripts. This allows us to later add more complex operator logic.

Step-by-Step

  • Stop the system if it is running.
  • Select MotorCtrl and delete Stopped.SetStopCmd.
  • Delete Running.SetRunCmd.
  • In state Running, add Sequencer.RunOperation named RunningCmd.
  • In state Stopped, add Sequencer.RunOperation named StopCmd.
  • Click into the background of the MotorCtrl object to change the selected object.
  • From the CDPCore resource, add an OperatorContainer and name it SetStopSpeed.
  • Select SetStopSpeed, and add a Sequencer.Argument<double> named StopSpeed.
  • Select the StopSpeed Argument and make sure the Input property is not checked.
  • Make sure StopSpeed.Value is 0.
  • Copy SetStopSpeed and right-click Paste it into MotorCtrl (select Add in the dialog box when prompted).

    Note: Copying SetStopSpeed and then specializing the copy to SetRunSpeed ensures both operator containers start with identical structure, which reduces setup mistakes.

  • Rename the pasted object to SetRunSpeed.
  • Inside SetRunSpeed, rename StopSpeed to RunSpeed.
  • Select SetRunSpeed and add a Sequencer.Argument<double>, name it Ref.
  • Navigate into SetRunSpeed, connect (by drag and drop) Ref to RunSpeed.
  • Navigate to MotorCtrl and connect SpeedRef to SetRunSpeed.Ref.
  • Connect Stopped.StopCmd to SetStopSpeed. This makes the Stopped state invoke this operator.
  • Connect Running.RunningCmd to SetRunSpeed.
  • Connect SetRunSpeed.RunSpeed to SpeedCmd.
  • Connect SetStopSpeed.StopSpeed to SpeedCmd.

The final MotorCtrl should look something like this:

Run & Test

  • Select the system in the Project tree and choose Run & Connect.
  • Trigger Start, then trigger Stop.

Expected Behavior

  • SpeedCmd changes according to Stopped and Running state, just like it did in Step 3.

What We Learned

  • How to replace state-local scripts with RunOperation nodes.
  • How each state can invoke a dedicated OperatorContainer for clearer structure.
  • How Sequencer.ArgumentOut<double> can receive values from multiple sources, while active state logic decides what is written.
  • Why this keeps MotorCtrl focused on decisions, while actuator dynamics are moved out in Step 5.

Step 5 - Move Ramping to an External Actuator Path

Goal

Keep MotorCtrl focused on state-machine decisions and move ramp/filter dynamics outside.

Step-by-Step

  • Stop the system if it is running.
  • In the same parent component as MotorCtrl, add a CDPCore.OperatorContainer and name it MotorActuator.
  • Navigate into MotorActuator.
  • Add Sequencer.Argument<double> for input and output:
    • TargetSpeed (Input=1)
    • ActualSpeed (Input=0)
  • Add Automation.RampFilter<double> named SpeedRamp.
  • Set SpeedRamp.MaxRate value to 0.5.
  • Connect routes in MotorActuator:
    • TargetSpeed -> SpeedRamp.In
    • SpeedRamp.Out -> ActualSpeed
  • Navigate back to the parent component and select MotorCtrl.
  • Add a Sequencer.Argument<double> named SpeedFeedback.

    Note: SpeedFeedback is introduced here so Step 6 can use measured speed in transition conditions, instead of relying only on commanded values.

  • Navigate back to the parent component (e.g. double-click the background) and route:
    • MotorCtrl.SpeedCmd -> MotorActuator.TargetSpeed
    • MotorActuator.ActualSpeed -> MotorCtrl.SpeedFeedback
  • Keep MotorCtrl state logic from Step 4 unchanged.

Run & Test

  • Select the system in the Project tree and choose Run & Connect.
  • Add MotorActuator.TargetSpeed, MotorActuator.ActualSpeed, and SpeedRamp.MaxRate to the Watch list.
  • Right-click MotorActuator.ActualSpeed and add it to a Plot.
  • Right-click MotorActuator.TargetSpeed and add it to a Plot.
  • Go into Analyze mode.
  • Trigger Start and Stop from the Watch list as before.
  • Watch the target and actual speed response.

Expected Behavior

  • MotorCtrl.SpeedCmd changes by state, just like in Step 4.
  • MotorActuator.ActualSpeed follows TargetSpeed with ramp-limited dynamics.
  • State logic and physical response are now separated and easier to maintain.

What We Learned

  • The state-machine should express control intent, not physical dynamics.
  • Ramp/filter/simulation belongs naturally in an external actuator path.
  • You can experiment with e.g. IIRFilter and other operators to simulate different motor output behaviors.
  • This separation makes MotorCtrl reusable for different actuator models.

Step 6 - Expand to a Practical Motor State-Machine

Goal

Make the controller robust with startup, stopping, and fault handling.

Step-by-Step

  • Stop the system.
  • Navigate to MotorCtrl.
  • Add one more Sequencer.EventIn named Reset.
  • Add Sequencer.InternalArgument<double> nodes needed for transition conditions: StartTimeoutSec (5 seconds) and StopTimeoutSec (5 seconds)

    Note: Explicit timeout arguments make fault escalation deterministic and easy to tune without changing transition expressions. Using InternalArgument keeps these tuning values local to MotorCtrl and out of the block's external API.

  • Add states Starting, Stopping, and Fault.
  • In Stopped state, rename ToRunning to ToStarting, and connect it to Starting state.
  • Add a Sequencer.StateTransition Starting.ToRunning and connect it to Running state.
  • Add a Sequencer.RunOperation to Starting, and name it RampRun.
  • Connect it to SetRunSpeed.
  • Select Starting.ToRunning, and in Value, put
    SpeedFeedback >= SpeedRef - 0.05
  • In Running state, rename ToStopped to ToStopping, and connect it to Stopping state.
  • Add a Sequencer.StateTransition Stopping.ToStopped.
  • Select Stopping.ToStopped, and in Value, put
    SpeedFeedback <= 0.05
  • Connect Stopping.ToStopped to Stopped state.
  • Add a Sequencer.RunOperation to Stopping, and name it RampStop.
  • Connect it to SetStopSpeed.
  • In Starting state, add a Sequencer.StateTransition ToFault.
  • Select Starting.ToFault, and in Value, put
    Starting.Time > StartTimeoutSec
  • Connect Starting.ToFault to Fault state.
  • In Stopping state, add a Sequencer.StateTransition ToFault.
  • Select Stopping.ToFault, and in Value, put
    Stopping.Time > StopTimeoutSec
  • Connect Stopping.ToFault to Fault state.
  • In Fault state, add a Sequencer.StateTransition ToStopped, and connect it to Stopped state.
  • Connect Reset to Fault.ToStopped.

Note: Make sure to rearrange the state nodes in the MotorCtrl Block Diagram ExecutionControl table so they flow nicely from left to right. The state order should be: Stopped, Starting, Running, Stopping, Fault.

The final block-diagram for MotorCtrl should look something like this:

Run & Test

  • Select the system in the Project tree and choose Run & Connect.
  • Verify normal start and stop.
  • Force a startup timeout condition (e.g. adjust ramp filter) and verify transition to Fault.
  • Force a stopping timeout condition and verify transition to Fault.
  • Select the Reset event, and add 0x20100 to the Watch list.
  • Trigger Reset from the Watch list and verify return to Stopped.

Expected Behavior

  • Normal operation reaches Running only when feedback confirms target speed.
  • Normal stop reaches Stopped only when feedback confirms near-zero speed.
  • Fault handling is deterministic and recoverable.

What We Learned

  • How to use both event-triggered and condition-triggered transitions in one state-machine.
  • How to supervise start/stop behavior with <State>.Time timeout conditions.
  • How to recover from Fault state via explicit Reset event.

Troubleshooting

  • If transitions do not trigger, confirm event routing and transition Value expressions.
  • If time-based transitions never fire, verify ProcessOn includes C.
  • If state logic runs only on transitions, verify TransitionMode includes P.
  • If output jumps instead of ramps, verify actuator path routing through MotorActuator.SpeedRamp.

Summary

You now have one complete pattern that scales:

  • Start from event-driven two-state ECC.
  • Add state-local scripts for fast iteration.
  • Enable cyclic behavior with E+C and O+P.
  • Move state output dynamics to an external actuator path for better separation.
  • Extend to production-style states with timeout and fault handling.

For full element and property reference, see Basic Function Block.

Using Anti-Sway on a Crane Using Sequence Steps to Operate Doors

The content of this document is confidential information not to be published without the consent of CDP Technologies AS.

CDP Technologies AS, www.cdpstudio.com

Get started with CDP Studio today

Let us help you take your great ideas and turn them into the products your customer will love.

Try CDP Studio for free
Why CDP Studio?

CDP Technologies AS
Hundsværgata 8,
P.O. Box 144
6001 Ålesund, Norway

Tel: +47 990 80 900
E-mail: info@cdptech.com

Company

About CDP

Contact us

Services

Partners

Blog

Developers

Get started

User manuals

Support

Document download

Release notes

My account

Follow CDP

  • LinkedIn
  • YouTube
  • GitHub

© Copyright 2026 CDP Technologies. Privacy and cookie policy.

Return to top