Skip to content

Commit

Permalink
Vibration pattern with major code refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
theelims committed Jan 15, 2022
1 parent c956f87 commit 27063f4
Show file tree
Hide file tree
Showing 7 changed files with 496 additions and 290 deletions.
27 changes: 20 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
# Upcoming Release 0.2.1
# Release 0.3.0
- set and get functions for maximum speed and maximum acceleration. Allows to change these limits during runtime.
- Renamed `#define DEBUG_VERBOSE` to `#define DEBUG_TALKATIVE` to make StrokeEngine play nice with WifiManager.
- Removed state `ERROR` from state machine. After tests with the servo this has become pointless. `disable()` and homing will clear any error state.
- Fixed bug with missing implementation of inverted direction signal: https://github.com/theelims/StrokeEngine/issues/3
- Added an improved pause handling mechanism to pattern:
- removed `void patternDelay(int milliseconds)` function as unfit for the purpose
- removed `void patternDelay(int milliseconds)` function as unfit for the purpose.
- Pattern-Class has 3 private functions `void _startDelay()`, `void _updateDelay(int delayInMillis)` and `bool _isStillDelayed()` to add an internal delay function based on comparing `millis()`.
- A pattern can request StrokeEngine to skip the current motion command by returning `_nextMove.skip = true;`.
- Steps per mm, speed and acceleration limits are available inside pattern, now.
- Refactored code to improve thread safety:
- Pinned the working threads to the application core 1.
- During state `PATTERN` only the `_stroking()`-Thread communicates with the FastAccelStepper library. Mid-stroke parameter updates only set a flag for the `_stroking()`-Thread to query the pattern for an updated motion calculation.
- All accesses to a pattern that influence it's internal states (set-functions & `nextTarget()`) are made thread safe and guarded by a mutex.
- Function `applyNewSettingsNow()` had to disappear. Set-functions got an additional bool `applyNow` to apply the changes immediately. Value defaults to `false` which means changes take effect only with the next stroke.
- It is now possible to receive telemetry information's about each trapezoidal motion a pattern generates. You may register a callback function `void registerTelemetryCallback(void(*callbackTelemetry)(float, float, bool))` with the following signature `void callbackTelemetry(float position, float speed, bool clipping)`.
- New pattern available:
- Stop'n'Go
- Insist
- Jack Hammer

## Planned Features
- New Pattern:
- Closing Gap
- Stroke Nibble

## Update Notes
### Mid-Stroke Parameter Updates
The function `applyNewSettingsNow()` had to disappear to make the code thread safe. However, all set-functions got an additional parameter `applyNow` of type bool to apply the changes immediately. This parameter defaults to `false` which means changes take effect only with the next stroke. To get the same functionality as calling `applyNewSettingsNow()` append `true` as the last parameter in all set-function calls.
```cpp
Stroker.setSpeed(float speed, bool applyNow); // Speed in Cycles (in & out) per minute, constrained from 0.5 to 6000
Stroker.setDepth(float depth, bool applyNow); // Depth in mm, constrained to [0, _travel]
Stroker.setStroke(float stroke, bool applyNow); // Stroke length in mm, constrained to [0, _travel]
Stroker.setSensation(float sensation, bool applyNow); // Sensation (arbitrary value a pattern may use to alter its behavior),
// constrained to [-100, 100] with 0 being neutral.
Stroker.setPattern(int index, bool applyNow); // Pattern, index must be < Stroker.getNumberOfPattern()
```

# Release 0.2.0
- Stroking-task uses suspend and resume instead of deleting and recreation to prevent heap fragmentation.
Expand Down Expand Up @@ -58,7 +71,7 @@ Stroker.enableAndHome(&endstop);
# Release 0.1.0
- First "official" release
- Renamed function `Stroker.startMotion()` to `Stroker.startPattern()`
- Renamed all states of the statemachine
- Renamed all states of the state machine
- Provide set and get functions for maxSpeed and maxAcceleration
- Fancy adjustment mode
- Changed members of struct `motorProperties` to streamline interface:
Expand Down
18 changes: 13 additions & 5 deletions Pattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ Pauses between a series of strokes. The number of strokes ramps from 1 stroke to
Sensation reduces the effective stroke length while keeping the stroke speed constant to the full stroke. This creates interesting vibrational pattern at higher sensation values. With positive sensation the strokes will wander towards the front, with negative values towards the back.

### Jack Hammer
Vibrational pattern that works like a jack hammer. Vibrates on the way in and pulls out smoothly in one go. Sensation sets the vibration amplitude.
Vibrational pattern that works like a jack hammer. Vibrates on the way in and pulls out smoothly in one go. Sensation sets the vibration amplitude from 3mm to 25mm.

### Stroke Nibbler
Simple vibrational overlay pattern. Vibrates on the way in and out. Sensation sets the vibration amplitude from 3mm to 25mm.

## Contribute a Pattern
Making your own pattern is not that hard. They can be found in the header only [pattern.h](./src/pattern.h) and easily extended.
Expand All @@ -49,18 +52,18 @@ Reimplement the core function `motionParameter nextTarget(unsigned int index)`.
```cpp
motionParameter nextTarget(unsigned int index) {
// maximum speed of the trapezoidal motion
_nextMove.speed = int(1.5 * _stroke/_timeOfStroke);
_nextMove.speed = int(1.5 * _stroke/_timeOfStroke);

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

// odd stroke is moving out
if (index % 2) {
_nextMove.stroke = 0;
_nextMove.stroke = _depth - _stroke;
// even stroke is moving in
} else {
_nextMove.stroke = _stroke;
_nextMove.stroke = _depth;
}

_index = index;
Expand All @@ -78,7 +81,6 @@ For debugging and verifying the math it can be handy to have something on the Se
#endif
```

It is possible for a pattern to insert pauses between strokes. The main stroking-thread of StrokeEngine will poll a new set of motion commands every few milliseconds once the target position of the last stroke is reached. If a pattern returns the motion parameter `_nextMove.skip = true;` inside the costume implementation of the `nextTarget()`-function no new motion is started. Instead it is polled again later. This allows to compare `millis()` inside a pattern. To make this more convenient the `Pattern` base class implements 3 private functions: `void _startDelay()`, `void _updateDelay(int delayInMillis)` and `bool _isStillDelayed()`. `_startDelay()` will start the delay and `_updateDelay(int delayInMillis)` will set the desired pause in milliseconds. `_updateDelay()` can be updated any time with a new value. If a stroke becomes overdue it is executed immediately. `bool _isStillDelayed()` is just a wrapper for comparing the current time with the scheduled time. Can be used inside the `nextTarget()`-function to indicate whether StrokeEngine should be advised to skip this step by returning `_nextMove.skip = true;`. See the pattern Stop'n'Go for an example on how to use this mechanism.

Don't forget to add an instance of your new pattern class at the very bottom of the file to the `*patternTable[]`-Array.
```cpp
Expand All @@ -88,6 +90,12 @@ static Pattern *patternTable[] = {
// <-- insert your new pattern class here!
};
```
#### Graceful Behavior & Error Proofing
Pattern are responsible that they behave gracefully on parameter changes. They return the absolute position and must therefore ensure internally, that they adhere to the interval [depth, depth-stroke] at all times. Test your code against parameter changes. Especially changes in depth and stroke may cause additional stroke distances which must be thought of. A good practice is to have these transfer moves executed at the same speed as the regular move. Erratic behavior on parameter changes must be avoided by all means.
#### Pauses
It is possible for a pattern to insert pauses between strokes. The main stroking-thread of StrokeEngine will poll a new set of motion commands every few milliseconds once the target position of the last stroke is reached. If a pattern returns the motion parameter `_nextMove.skip = true;` inside the costume implementation of the `nextTarget()`-function no new motion is started. Instead it is polled again later. This allows to compare `millis()` inside a pattern. To make this more convenient the `Pattern` base class implements 3 private functions: `void _startDelay()`, `void _updateDelay(int delayInMillis)` and `bool _isStillDelayed()`. `_startDelay()` will start the delay and `_updateDelay(int delayInMillis)` will set the desired pause in milliseconds. `_updateDelay()` can be updated any time with a new value. If a stroke becomes overdue it is executed immediately. `bool _isStillDelayed()` is just a wrapper for comparing the current time with the scheduled time. Can be used inside the `nextTarget()`-function to indicate whether StrokeEngine should be advised to skip this step by returning `_nextMove.skip = true;`. See the pattern Stop'n'Go for an example on how to use this mechanism.
### Expected Behavior
#### Adhere to Depth & Stroke at All Times
Depth and Stroke set in StrokeEngine are axiomatic. StrokeEngine closely monitors the returned `motionParameter` and ensures no violation against the machines physics were returned. Pattern only return a stroke information which is offset by depth in the StrokeEngine. Your return value may be anywhere in the interval [0, stroke]. Positions outside the interval [depth - stroke, depth] will be truncated, leading to a distortion of your intended stroke profile. This is an integral safety feature to prevent injuries. This sets the envelope the pattern may use. Similar for speed a.k.a. timeOfStroke.
Expand Down
Loading

0 comments on commit 27063f4

Please sign in to comment.