RAD Developer Docs
OSSMSoftwareMotionStrokeEngine Library

Patterns

Learn how patterns create variety in StrokeEngine motion by generating trapezoidal motion parameters

Patterns are what set StrokeEngine apart from other motion systems. Each pattern is a small program that generates the next set of trapezoidal motion parameters: target position, speed, and acceleration.

Available patterns

The OSSM firmware exposes 7 patterns (indices 0-6): Simple Stroke, Teasing or Pounding, Robo Stroke, Half'n'Half, Deeper, Stop'n'Go, and Insist. Jack Hammer and Stroke Nibbler are available in the StrokeEngine library but are not currently implemented in the OSSM firmware.

OSSM patterns (0-6)

Library-only patterns

These patterns are available in the StrokeEngine library but are not currently implemented in the OSSM firmware. They're documented here for developers building custom firmware or other StrokeEngine-based projects.

Creating custom patterns

You can create your own patterns by subclassing the Pattern class in the header-only pattern.h file.

Subclass the Pattern class

Create a new class that extends Pattern. See SimpleStroke for a minimal implementation:

class SimpleStroke : public Pattern {
    public:
        SimpleStroke(const char *str) : Pattern(str) {}

The constructor stores the pattern's display name string.

Override set-functions if needed

Reimplement set-functions when you need custom calculations:

void setTimeOfStroke(float speed = 0) {
    // In & Out have same time, so divide by 2
    _timeOfStroke = 0.5 * speed;
}

Implement the nextTarget function

This is the core function that StrokeEngine calls after each stroke to get the next motion parameters.

motionParameter nextTarget(unsigned int index) {
    // Maximum speed of the trapezoidal motion
    _nextMove.speed = int(1.5 * _stroke/_timeOfStroke);

    // Acceleration to meet the profile
    _nextMove.acceleration = int(3.0 * _nextMove.speed/_timeOfStroke);

    // Odd stroke moves out
    if (index % 2) {
        _nextMove.stroke = _depth - _stroke;

    // Even stroke moves in
    } else {
        _nextMove.stroke = _depth;
    }

    _index = index;
    return _nextMove;
}

The index parameter starts at 0 when the pattern is first called and increments by 1 after each stroke. Use this to create patterns that vary over time.

Add debugging output

Encapsulate Serial.print() statements with preprocessor directives so they can be toggled:

#ifdef DEBUG_PATTERN
    Serial.println("TimeOfInStroke: " + String(_timeOfInStroke));
    Serial.println("TimeOfOutStroke: " + String(_timeOfOutStroke));
#endif

Register the pattern

Add an instance of your pattern class to the patternTable[] array at the bottom of the file:

static Pattern *patternTable[] = {
    new SimpleStroke("Simple Stroke"),
    new TeasingPounding("Teasing or Pounding"),
    new YourNewPattern("Your Pattern Name")  // Add your pattern here
};

Pattern requirements

Depth and stroke boundaries

Patterns must return stroke positions within the interval [0, stroke]. StrokeEngine monitors all returned motionParameter values and truncates positions outside [depth - stroke, depth] to prevent injuries.

Depth and stroke values set in StrokeEngine are axiomatic boundaries. Your pattern defines the envelope it uses within these limits. The same safety constraints apply to speed via timeOfStroke.

Graceful parameter changes

Your pattern must handle parameter changes gracefully. When depth or stroke values change mid-operation, the pattern must:

  • Stay within the interval [depth, depth - stroke] at all times
  • Execute transfer moves at the same speed as regular moves
  • Avoid erratic behavior

Test your pattern thoroughly against parameter changes, especially depth and stroke modifications that may require additional stroke distances.

Using the index parameter

The index parameter provides important state information:

  • Resets to 0 when StrokeEngine.setPattern(int) or StrokeEngine.startMotion() is called
  • Increments after each successfully executed move
  • Comparing index == _index determines if this is an update to the current stroke rather than a new stroke

Store the last index in _index before returning to track stroke state in time-varying patterns.

Implementing pauses

Patterns can insert pauses between strokes. When the target position is reached, StrokeEngine polls for new motion commands every few milliseconds.

To implement a pause, return _nextMove.skip = true from your nextTarget() function. StrokeEngine will poll again later instead of starting a new motion.

The Pattern base class provides three helper functions for pause management:

FunctionDescription
_startDelay()Starts the delay timer
_updateDelay(int delayInMillis)Sets the pause duration in milliseconds (can be updated anytime)
_isStillDelayed()Returns true if the scheduled time hasn't been reached

If a stroke becomes overdue, it executes immediately. See the Stop'n'Go pattern for an example implementation.

Contributing patterns

After thoroughly testing your pattern, submit a pull request with your updated pattern.h file.

On this page