2 * @file animationQueueDemo.ino
3 * @brief Demonstrates queued animations, live logic inversion, and wait().
5 * This example demonstrates:
6 * - Capturing four animations in a fixed-storage queue
7 * - Inspecting the active queue index
8 * - Modifying a running animation without losing its block positions
9 * - Stopping block emission and allowing active blocks to flow out
10 * - Adding a non-blocking pause with wait()
13 * - Supported driver with compatible library (SBK_MAX72xx or SBK_HT16K33)
14 * - Bar meter display or LED array wired to the selected driver
17 * Samuel Barabé (Smart Builds & Kits)
25// -----------------------------------------------------------------------------
26// SBK BarDrive Configuration Flags
27// -----------------------------------------------------------------------------
28#define SBK_BARDRIVE_WITH_ANIM // Enable preset animations and queue controls.
30// This example uses four entries. The library default is already four.
31// If you change the global queue capacity, keep it at four or greater here.
32// #define SBK_BARDRIVE_QUEUE_CAPACITY 4
34// -----------------------------------------------------------------------------
35// SELECT YOUR DRIVER SETUP
36// Enable one driver configuration and comment out the other two.
37// -----------------------------------------------------------------------------
39/* === [A] MAX7219/MAX7221 via SOFTWARE SPI (any 3 digital pins) === */
40#define DIN_PIN A4 // Software SPI Data In
41#define CLK_PIN A5 // Software SPI Clock
42#define CS_PIN A3 // Chip Select / LOAD
43#include <SBK_MAX72xxSoft.h>
44SBK_MAX72xxSoft driver(DIN_PIN, CLK_PIN, CS_PIN, 1);
45#include <SBK_BarDrive.h>
46SBK_BarDrive<SBK_MAX72xxSoft> bar(
47 &driver, 0, MatrixPreset::SBK_BarMeter_SK28);
49/* === [B] MAX7219/MAX7221 via HARDWARE SPI (dedicated MCU SPI pins) === */
50// #define CS_PIN A3 // Chip Select / LOAD
51// #include <SBK_MAX72xxHard.h>
52// SBK_MAX72xxHard driver(CS_PIN, 1);
53// #include <SBK_BarDrive.h>
54// SBK_BarDrive<SBK_MAX72xxHard> bar(
55// &driver, 0, MatrixPreset::SBK_BarMeter_SK28);
57/* === [C] HT16K33 via I2C === */
58// #include <SBK_HT16K33.h>
59// const uint8_t NUM_DEV = 1;
60// const uint8_t DEV0_IDX = 0;
61// const uint8_t DEV0_ADD = 0x70;
62// const uint8_t DEV0_NUM_ROWS = 8; // 20-SOP = 8, 24-SOP = 12, 28-SOP = 16
63// SBK_HT16K33 driver(NUM_DEV);
64// #include <SBK_BarDrive.h>
65// SBK_BarDrive<SBK_HT16K33> bar(
66// &driver, 0, MatrixPreset::SBK_BarMeter_SK28);
69 * The default setup uses the SBK BarMeter SK28 28-segment preset.
70 * Change the MatrixPreset or constructor to match your display and wiring.
73uint8_t previousQueueIndex = 0xFF; // Same value as NO_QUEUE_INDEX.
74uint32_t queueStepStartedAt = 0;
75bool blockLogicInverted = false;
77void startAnimationQueue()
80 * enqueue() captures the animation and its modifiers in the next queue slot.
81 * The captured animations do not start until startQueue() is called.
83 * Queue index 0: fill the display.
84 * Queue index 1: empty the display.
85 * Queue index 2: emit colliding blocks indefinitely (numBlocks defaults to 0).
86 * Queue index 3: preserve the empty display for 1000 ms without blocking.
88 * wait() is different from Arduino delay(): loop() and animations.update()
89 * continue to run, so the rest of the application remains responsive.
91 bar.animations().stop()
92 .fillUpIntv(35).noLoop().enqueue()
93 .emptyDownIntv(35).noLoop().enqueue()
94 .collidingBlocks(45, 4, 2).enqueue()
101#ifdef SBK_HT16K33_IS_DEFINED
102 // HT16K33 setup is required only when configuration [C] is enabled.
103 driver.setAddress(DEV0_IDX, DEV0_ADD);
104 driver.setDriverRows(DEV0_IDX, DEV0_NUM_ROWS);
108 driver.setBrightness(0, 10);
109 bar.setDirection(BarDirection::FORWARD);
110 startAnimationQueue();
115 // update() advances timing and queue transitions; show() sends pixels to the driver.
116 bar.animations().update();
120 * Detect entry into a new queue slot so each timed action is measured from
121 * the start of that animation, rather than from the start of the sketch.
123 const uint8_t currentQueueIndex = bar.animations().currentQueueIndex();
124 if (currentQueueIndex != previousQueueIndex)
126 previousQueueIndex = currentQueueIndex;
127 queueStepStartedAt = millis();
128 blockLogicInverted = false;
131 const uint32_t queueStepElapsed = millis() - queueStepStartedAt;
134 * invertLogic() is intentionally called outside the queue. Loading another
135 * queued animation would reinitialize the animation and lose all current
136 * block positions. Modifying the active index-2 animation in place keeps
137 * the same blocks moving while changing how they are rendered.
139 * The boolean prevents invertLogic() from being called on every loop pass.
141 if (bar.animations().isQueueIndexPlaying(2) &&
142 queueStepElapsed >= 3000 &&
145 bar.animations().invertLogic();
146 blockLogicInverted = true;
150 * After three more seconds, stop creating blocks. Active blocks continue
151 * moving until they leave the display. The animation then completes
152 * naturally and the queue advances to wait(1000).
154 if (bar.animations().isQueueIndexPlaying(2) &&
155 queueStepElapsed >= 6000 &&
156 bar.animations().isBlockEmissionEnabled())
158 bar.animations().stopBlockEmission();
161 // After the final wait entry completes, rebuild and replay the demonstration.
162 if (!bar.animations().isRunning())
163 startAnimationQueue();