Exported Playback Logic
When you export animations, you set some basic triggering logic that is run for you “code-free.” That logic gives you options to trigger animations
- As a starting animation
- As an idle animation to play when no other animation is running
- Based on a pin state (high / low / some analog range)
However, you may likely have use cases that aren’t covered by these built-in options. In this case, you can add code to the callback file to implement your own animation triggering logic.
Available Methods
Section titled “Available Methods”There are three convenient methods you can use to write your own animation triggering logic:
// returns TRUE if an animation is currently playing, FALSE if notbool isPlaying = BottangoCore::commandStreamProvider->streamIsInProgress()You can use streamIsInProgress to detect if anything is currently playing.
// Start an animation playing by index, and if it should loop or not// pass a byte for animation index and a bool for if should loopBottangoCore::commandStreamProvider->startCommandStream(animationIndex, looping)You can use startCommandStream to start playing an animation by index value, and loop it or not.
// stop playing the current animation, and if you should also fully shut down the firmware// pass true to shutdown the firmware, and false to only stop playing the current animation.BottangoCore::stop(bool shutDownFirmware)You can use stop to stop playing an animation if one is playing. If you pass true it will also shut down the firmware and prevent any more movement until the hardware restarts. If you just want to stop the animation, pass false.
Putting it together
Section titled “Putting it together”The normal recommended place to put your animation triggering logic is in the onEarlyLoop callback. You don’t have to put it there, but it’s a good spot if you don’t have a reason to put it somewhere else.
Here’s a few examples of putting it all together. Pin initialization and defines are placed in the callback or near it for formatting reasons for the documentation. Better practice would be to move the defines to a more appropriate location, and to do pinMode initialization in the onThisControllerStarted callback rather than every loop callback.
Play animation on button press
Section titled “Play animation on button press”This is a very basic example of starting an animation index 2 when a button is pressed and an electrical state on a pin is detected. Note that you can achieve this same result “code-free” using the built-in export triggering features, but the custom code version is given below as a teaching tool.
void onEarlyLoop() { // is a button on pin 6 pressed pinMode(6, INPUT); bool startPressed = digitalRead(6) == LOW;
// if button is pressed, and we're not playing an animation if (startPressed && BottangoCore::commandStreamProvider->streamIsInProgress() == false) { // start animation 2 without looping BottangoCore::commandStreamProvider->startCommandStream(2, false); } }Playlist of animations in order
Section titled “Playlist of animations in order”In this example, we play 4 exported animations in order, starting the next immediately after the previous ends. We wrap back to the first animation after the last one plays.
#define NUM_ANIMATIONS 4 byte currentAnimIndex = 0;
void onEarlyLoop() { // check if anything is playing // if the firmware just started or // the last animation finished, we'll get false if (BottangoCore::commandStreamProvider->streamIsInProgress() == false) { // start the current animation BottangoCore::commandStreamProvider->startCommandStream(currentAnimIndex, false);
// increment the counter currentAnimIndex++;
// wrap it back to the start if we're at the end if (currentAnimIndex == NUM_ANIMATIONS) { currentAnimIndex = 0; } } }Random Animation on Button Press
Section titled “Random Animation on Button Press”Here’s a more advanced example putting these methods together that plays a random animation each time a button on pin 5 is pressed while nothing is currently playing, and stops playing if a button on pin 6 is pressed.
#define BUTTON_START_PLAYING 5#define BUTTON_STOP_PLAYING 6#define NUM_ANIMATIONS 3#define STOP_DEBOUNCE 500 unsigned long lastStopButtonTime = 0;
void onEarlyLoop() { pinMode(BUTTON_START_PLAYING, INPUT); pinMode(BUTTON_STOP_PLAYING, INPUT);
// is stop button pressed (accounting for debounce timing?) bool stopPressed = digitalRead(BUTTON_STOP_PLAYING) == LOW && millis() - lastStopButtonTime >= STOP_DEBOUNCE;
// if stop is pressed and we're playing an animation if (stopPressed && BottangoCore::commandStreamProvider->streamIsInProgress() == true) { // stop playing BottangoCore::stop(false); // note time to debounce the next press lastStopButtonTime = millis(); }
// is start button pressed bool startPressed = digitalRead(BUTTON_START_PLAYING) == LOW;
// if start is pressed, and we're not playing an animation if (startPressed && BottangoCore::commandStreamProvider->streamIsInProgress() == false) { // generate a random number between 0 (inclusive) and 3 (exclusive ) (IE 0, 1, or 2) byte animationIndexToPlay = random(0, NUM_ANIMATIONS); // start that animation without looping BottangoCore::commandStreamProvider->startCommandStream(animationIndexToPlay, false); } }Simple debouncing is in this example on the stop button to only allow the stop button press to be triggered once every 500ms.