Skip to content

Commit

Permalink
Merge branch 'release/1.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
timhendriks93 committed Aug 7, 2022
2 parents 2cfb2eb + efc4d2c commit a993fb7
Show file tree
Hide file tree
Showing 15 changed files with 313 additions and 113 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ jobs:
run: |
find src test -regex '.*\.\(cpp\|h\)' -exec clang-format-13 --dry-run --Werror {} \;
find examples -regex '.*\.ino' -exec clang-format-13 --dry-run --Werror {} \;
- uses: arduino/arduino-lint-action@v1
- name: Lint with arduino-lint
uses: arduino/arduino-lint-action@v1
with:
library-manager: update
test:
name: "Test"
runs-on: ubuntu-latest
Expand Down
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,24 +153,26 @@ void loop() {

At first, an animation will be in the default mode. In this mode, the animation is simply not doing anything and waits until the mode has changed.

| Mode | Constant | Description |
|------|----------|-------------|
| default | MODE_DEFAULT | Not playing / waiting |
| play | MODE_PLAY | Playing back the animation |
| pause | MODE_PAUSE | Pausing the animation at the current frame |
| stop | MODE_STOP | Slowly moving the servos to their neutral position |
| live | MODE_LIVE | Reading serial commands to move the servos in real-time |
| Constant | Method | Description |
|----------|--------|-------------|
| MODE_DEFAULT | | Not playing / waiting |
| MODE_PLAY | play() | Start or resume playing the animation once |
| MODE_PAUSE | pause() | Pausing the animation at the current frame |
| MODE_STOP | stop() | Slowly moving the servos to their neutral position |
| MODE_LOOP | loop() | Start or resume playing the animation in a loop |
| MODE_LIVE | live(stream) | Reading serial commands to move the servos in real-time |

The modes can be changed or triggered via the following methods:
The modes can be changed or triggered by calling the above methods on the animation object:

```ino
myBlenderAnimation.play();
myBlenderAnimation.pause();
myBlenderAnimation.stop(stepDelay);
myBlenderAnimation.loop();
myBlenderAnimation.stop();
myBlenderAnimation.live(stream);
```

> Note: the default mode is only handled internally.
> Note: the default mode can not be triggered as it is only handled internally.
When calling the `stop` method, it is possible to pass a delay in milliseconds. This delay will be used when the servos are moved to their neutral position during the stop mode. To get to the neutral position, the current position will either be increased or decreased by 1. The delay therefore controls how fast or smooth this movement will take place. The default value for this parameter is `20`.

Expand Down
4 changes: 2 additions & 2 deletions examples/AdafruitPCA9685/AdafruitPCA9685.ino
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ void setup() {
animation.addServo(neckLeftServo);
animation.addServo(neckRightServo);

// Trigger the animation play mode
animation.play();
// Trigger the animation loop mode
animation.loop();

// Initialize servo driver
pwm.begin();
Expand Down
4 changes: 2 additions & 2 deletions examples/MultiplePCA9685/MultiplePCA9685.ino
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ void setup() {
animation.addServo(servoMaps[i].servo);
}

// Trigger the animation play mode
animation.play();
// Trigger the animation loop mode
animation.loop();

// Initialize servo drivers
pwmA.begin();
Expand Down
4 changes: 2 additions & 2 deletions examples/StandardServoLib/StandardServoLib.ino
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ void setup() {
// Add the Blender servo object to the animation
animation.addServo(myBlenderServo);

// Trigger the animation play mode
animation.play();
// Trigger the animation loop mode
animation.loop();
}

void loop() {
Expand Down
6 changes: 6 additions & 0 deletions examples/SwitchModeButton/SwitchModeButton.ino
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
changes. For example, this can be used to control the animation audio. See the
comments below to understand what happens on a short or long button press
based on the current mode.
Starting the animation will only play it once, so another button press is
required to play it again. Alternatively, you can also use MODE_LOOP instead
of MODE_PLAY and loop() instead of play(). This will keep the animation
running until the button is short or long pressed again, thus pausing or
stopping the animation.
*/

#include "simple.h"
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=BlenderServoAnimation
version=1.0.0
version=1.1.0
author=Tim Hendriks
maintainer=Tim Hendriks <admin@tim-hendriks.com>
sentence=Library to control servos based on an exported Blender animation.
Expand Down
16 changes: 15 additions & 1 deletion src/BlenderServoAnimation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ void Animation::changeMode(byte mode) {
void Animation::run(unsigned long currentMicros) {
switch (mode) {
case MODE_PLAY:
case MODE_LOOP:
this->handlePlayMode(currentMicros);
break;
case MODE_STOP:
Expand Down Expand Up @@ -74,7 +75,15 @@ void Animation::handlePlayMode(unsigned long currentMicros) {
Servo *servo = this->servos[i];

if (servo && servo->hasPositions()) {
servo->moveByStep(this->frame);
servo->moveByFrame(this->frame);
}
}

if (this->frame == 0) {
if (this->mode == Animation::MODE_LOOP) {
this->changeMode(Animation::MODE_LOOP);
} else {
this->changeMode(Animation::MODE_DEFAULT);
}
}
}
Expand Down Expand Up @@ -132,6 +141,11 @@ void Animation::pause() {
this->changeMode(Animation::MODE_PAUSE);
}

void Animation::loop() {
this->lastMicros = micros();
this->changeMode(Animation::MODE_LOOP);
}

void Animation::stop(byte stepDelay) {
this->stopStepDelay = stepDelay;
this->frame = 0;
Expand Down
10 changes: 6 additions & 4 deletions src/BlenderServoAnimation.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ class Animation {

public:
static const byte MODE_DEFAULT = 0;
static const byte MODE_PAUSE = 1;
static const byte MODE_PLAY = 2;
static const byte MODE_STOP = 3;
static const byte MODE_LIVE = 4;
static const byte MODE_PLAY = 1;
static const byte MODE_PAUSE = 2;
static const byte MODE_LOOP = 3;
static const byte MODE_STOP = 4;
static const byte MODE_LIVE = 5;

Animation();
Animation(byte fps, int frames);
Expand All @@ -50,6 +51,7 @@ class Animation {
void run(unsigned long currentMicros = micros());
void play();
void pause();
void loop();
void stop(byte stepDelay = 20);
void live(Stream &serial);
byte getMode();
Expand Down
4 changes: 2 additions & 2 deletions src/servo/Servo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ void Servo::move(int position, bool force) {
this->currentPosition = position;
}

void Servo::moveByStep(int step) {
int newPosition = pgm_read_word_near(this->positions + step);
void Servo::moveByFrame(int frame) {
int newPosition = pgm_read_word_near(this->positions + frame);
this->move(newPosition);
}

Expand Down
2 changes: 1 addition & 1 deletion src/servo/Servo.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Servo {
Servo(byte id, const int positions[], cb moveCallback, byte threshold = 20);
Servo(byte id, cb moveCallback, byte threshold = 20);
void move(int position, bool force = false);
void moveByStep(int step);
void moveByFrame(int frame);
void moveTowardsNeutral(bool inSteps = true);
bool isNeutral();
bool hasPositions();
Expand Down
93 changes: 93 additions & 0 deletions test/animation/test_live/test_live.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "BlenderServoAnimation.h"
#include "../../SerialMock.h"
#include <unity.h>

using namespace BlenderServoAnimation;

#define FPS 60
#define FRAMES 5

struct positionLog {
int index;
int positions[20];
};

positionLog lastPositions[16];

void setUp(void) {
for (int id = 0; id < 16; id++) {
lastPositions[id].index = 0;

for (int i = 0; i < 20; i++) {
lastPositions[id].positions[i] = 0;
}
}
}

void move(byte servoID, int position) {
int index = lastPositions[servoID].index;
lastPositions[servoID].positions[index] = position;
lastPositions[servoID].index++;
}

const int positions[5] PROGMEM = {350, 340, 330, 340, 330};

void test_multiple_servos(void) {
Animation animation;
SerialMock mock;
Servo servos[] = {
Servo(0, positions, move),
Servo(1, move),
};
animation.addServos(servos, 2);
animation.live(mock);
TEST_ASSERT_EQUAL(Animation::MODE_LIVE, animation.getMode());

byte values[20] = {60, 0, 1, 94, 62, 60, 1, 1, 94, 62,
60, 0, 1, 99, 62, 60, 1, 1, 99, 62};

for (int i = 0; i < 10; i++) {
mock.write(values[i]);
}

animation.run();

TEST_ASSERT_EQUAL(0, lastPositions[0].positions[0]);
TEST_ASSERT_EQUAL(350, lastPositions[1].positions[0]);

for (int i = 10; i < 20; i++) {
mock.write(values[i]);
}

animation.run();

TEST_ASSERT_EQUAL(355, lastPositions[0].positions[0]);
TEST_ASSERT_EQUAL(355, lastPositions[1].positions[1]);
}

void test_skip(void) {
Animation animation;
SerialMock mock;
Servo servo(0, move);
animation.addServo(servo);
animation.live(mock);
TEST_ASSERT_EQUAL(Animation::MODE_LIVE, animation.getMode());

byte values[15] = {60, 0, 1, 94, 62, 60, 0, 1, 99, 0, 60, 0, 1, 104, 62};

for (int i = 0; i < 15; i++) {
mock.write(values[i]);
}

animation.run();

TEST_ASSERT_EQUAL(350, lastPositions[0].positions[0]);
TEST_ASSERT_EQUAL(360, lastPositions[0].positions[1]);
}

int main(int argc, char **argv) {
UNITY_BEGIN();
RUN_TEST(test_multiple_servos);
RUN_TEST(test_skip);
UNITY_END();
}
91 changes: 91 additions & 0 deletions test/animation/test_mode_change/test_mode_change.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include "BlenderServoAnimation.h"
#include "../../SerialMock.h"
#include <unity.h>

using namespace BlenderServoAnimation;

#define FPS 60
#define FRAMES 5

struct modeLog {
byte prevMode;
byte newMode;
};

byte modeIndex = 0;
modeLog lastModes[10];

void setUp(void) {
for (int i = 0; i < 10; i++) {
lastModes[i].prevMode = 0;
lastModes[i].newMode = 0;
}

modeIndex = 0;
}

void onModeChange(byte prevMode, byte newMode) {
lastModes[modeIndex].prevMode = prevMode;
lastModes[modeIndex].newMode = newMode;
modeIndex++;
}

void test_different_mode(void) {
Animation animation(FPS, FRAMES);

animation.onModeChange(onModeChange);

animation.play();
TEST_ASSERT_EQUAL(Animation::MODE_DEFAULT, lastModes[0].prevMode);
TEST_ASSERT_EQUAL(Animation::MODE_PLAY, lastModes[0].newMode);
animation.pause();
TEST_ASSERT_EQUAL(Animation::MODE_PLAY, lastModes[1].prevMode);
TEST_ASSERT_EQUAL(Animation::MODE_PAUSE, lastModes[1].newMode);
}

void test_same_mode(void) {
Animation animation(FPS, FRAMES);

animation.onModeChange(onModeChange);

animation.loop();
TEST_ASSERT_EQUAL(Animation::MODE_DEFAULT, lastModes[0].prevMode);
TEST_ASSERT_EQUAL(Animation::MODE_LOOP, lastModes[0].newMode);
animation.loop();
TEST_ASSERT_EQUAL(Animation::MODE_LOOP, lastModes[1].prevMode);
TEST_ASSERT_EQUAL(Animation::MODE_LOOP, lastModes[1].newMode);
}

void test_all_modes(void) {
SerialMock mock;
Animation animation(FPS, FRAMES);

animation.onModeChange(onModeChange);

animation.play();
TEST_ASSERT_EQUAL(Animation::MODE_DEFAULT, lastModes[0].prevMode);
TEST_ASSERT_EQUAL(Animation::MODE_PLAY, lastModes[0].newMode);
animation.pause();
TEST_ASSERT_EQUAL(Animation::MODE_PLAY, lastModes[1].prevMode);
TEST_ASSERT_EQUAL(Animation::MODE_PAUSE, lastModes[1].newMode);
animation.loop();
TEST_ASSERT_EQUAL(Animation::MODE_PAUSE, lastModes[2].prevMode);
TEST_ASSERT_EQUAL(Animation::MODE_LOOP, lastModes[2].newMode);
animation.stop();
TEST_ASSERT_EQUAL(Animation::MODE_LOOP, lastModes[3].prevMode);
TEST_ASSERT_EQUAL(Animation::MODE_STOP, lastModes[3].newMode);
animation.run();
TEST_ASSERT_EQUAL(Animation::MODE_STOP, lastModes[4].prevMode);
TEST_ASSERT_EQUAL(Animation::MODE_DEFAULT, lastModes[4].newMode);
animation.live(mock);
TEST_ASSERT_EQUAL(Animation::MODE_DEFAULT, lastModes[5].prevMode);
TEST_ASSERT_EQUAL(Animation::MODE_LIVE, lastModes[5].newMode);
}

int main(int argc, char **argv) {
UNITY_BEGIN();
RUN_TEST(test_different_mode);
RUN_TEST(test_same_mode);
RUN_TEST(test_all_modes);
UNITY_END();
}
Loading

0 comments on commit a993fb7

Please sign in to comment.