HitLib 1.5.0
VEX V5 LED animation library for PROS
Loading...
Searching...
No Matches
Profiles & Modes

The profile system lets you define named animation modes with a priority level, then activate and deactivate them at runtime. The highest-priority currently-active mode wins and drives the strand.


Built-in Profiles

Three ready-made profiles are available in hitlib/profiles/classic.hpp.

Mode Index Table

Mode Classic Modern Showy
Showoff 0 rainbow 0 rainbow not in this profile
Idle 1 magenta flow 1 pink pulse 0 purple flow
Alliance Red 2 red pulse 2 red pulse + orange bg 1 white pulse / red bg
Alliance Blue 3 blue pulse 3 blue pulse + cyan bg 2 white pulse / blue bg
Scoring 4 green pulse 4 green flash 3 teal pulse
Matchloading 5 yellow pulse 5 yellow pulse not in this profile
Endgame 6 warn -> white -> cycle 6 solid green -> pulse 4 yellow -> rainbow

Classic and Modern have seven modes each and share their numbering. Showy has five, so its indices are its own: no Showoff or Matchloading, and Endgame is 4, not 6. activateMode() takes a raw index into the profile's mode array, so activateMode(6) against Showy is out of range and simply never wins.

strand.attachProfile(&hitlib::profiles::classic);
strand.activateMode(1); // Idle - persistent
strand.activateModeTimed(4, 1500); // Scoring - expires after 1.5 s
strand.deactivateMode(1); // remove Idle from stack
strand.detachProfile(); // detach and turn off
Ready-made Classic, Modern and Showy LED profiles.
const Profile classic
Classic profile instance.
Definition classic.hpp:111

Custom Profiles

1. Write setup functions

static void myIdle (hitlib::LedStrand& s) { s.rainbow(1); }
static void myAlert (hitlib::LedStrand& s) { s.flash(0xFF0000, 100, 100); }
Driver for a single WS2812B-compatible LED strip on a VEX ADI port.
Definition led_strand.hpp:53
void flash(uint32_t color, uint32_t onMs, uint32_t offMs, uint32_t bgColor=0x000000)
Blink the whole strip on and off.
void rainbow(Speed speed)
Scroll a full HSV rainbow across the strip.

2. Declare modes

static const hitlib::ProfileMode myModes[] = {
{"Idle", 10, myIdle, nullptr},
{"Alert", 80, myAlert, nullptr},
};
static const hitlib::Profile myProfile = {"MyRobot", myModes, 2};
Describes a single named animation mode within a Profile.
Definition led_profile.hpp:38
An immutable, statically-allocated collection of ProfileMode entries.
Definition led_profile.hpp:68

3. Attach and activate

strand.attachProfile(&myProfile);
strand.activateMode(0); // Idle

Exported from Pattern Studio

Pattern Studio writes the three steps above for you, and can put the result straight into your project.

Show it your PROS or VEXcode project once - Export > Choose Robot Project..., or drag the project folder onto the window - and Deploy writes include/hitlib_studio.hpp (hitlib_studio.h for VEXcode) every time you click it. Re-deploying overwrites that one file, so changing a port or adding a mode is: click Deploy, rebuild.

What you write once

#include "hitlib_studio.hpp"
namespace myRobot = hitlib::profiles::myRobot;
void initialize() {
hitlib::studio::begin();
}
void opcontrol() {
myRobot::strand.activateModeTimed(myRobot::mode::endgame, 30000);
}

begin() registers every strand in the design, starts its refresh task at the design's interval, attaches each profile and activates each strand's first mode. A second call does nothing, so a routine that re-runs its own init is safe.

The exported file opens with this same snippet as a comment, filled in with your design's real identifiers and mode names.

VEXcode

A VEXcode project gets the same file for VEXcode's C++11 compiler. The snippet uses the competition template's functions, and everything you call is spelled the same. The header is hitlib_studio.h, since VEXcode Pro V5 only shows .h headers in its file tree:

#include "hitlib_studio.h"
namespace myRobot = hitlib::profiles::myRobot;
void pre_auton() {
hitlib::studio::begin();
}
void usercontrol() {
myRobot::strand.activateModeTimed(myRobot::mode::endgame, 30000);
}

C++11 has no inline variables, so inside the file each shared object is a static member of a small class template, with a reference of the usual name bound to it:

template <typename = void> struct strand_ { static LedStrand value; };
template <typename T> LedStrand strand_<T>::value{adiPort, length, refreshMs};
static LedStrand& strand = strand_<>::value;

That keeps one strand however many files include the header. Use strand; strand_ is only where it lives.

Deploy picks the form from the project it writes into. File and clipboard exports use Export > Target Platform, which follows the last project you chose.

What the file contains

Each strand gets its own namespace under hitlib::profiles:

namespace hitlib::profiles {
namespace myRobot {
// --- Hardware: the strand this design was previewed against ---
constexpr uint8_t adiPort = 6;
constexpr uint8_t length = 63;
constexpr uint32_t refreshMs = 25;
constexpr uint8_t brightness = 80;
// --- Mode indices: pass these to activateMode() / activateModeTimed() ---
namespace mode {
constexpr uint8_t idle = 0; // "Idle", priority 10
constexpr uint8_t endgame = 1; // "Endgame", priority 100
} // namespace mode
namespace detail { /* one setup function per mode and per sequenced phase */ }
inline const ProfileMode modeTable[] = { ... };
inline const Profile profile = {"My Robot", modeTable, 2};
inline void apply(LedStrand& s); // setBrightness + attachProfile
inline void apply(LedGroup& g);
inline LedStrand strand{adiPort, length, refreshMs}; // the strand itself
} // namespace myRobot
} // namespace hitlib::profiles
namespace hitlib::studio {
inline LedGroup groups[1]; // one per refresh interval in the design
inline LedGroup& group = groups[0];
inline void begin(); // add, init, start, apply, activateMode
inline void begin(LedGroup& existing);
}
Definition classic.hpp:56

The constexpr hardware values are why the strand on the robot matches the one in the preview: the port, length and refresh interval come from the design rather than being retyped. strand is an ordinary hitlib::LedStrand built from those constants - every API call still works on it directly. The mode:: constants replace counting rows in modeTable to work out what index activateMode() wants.

Fitting it to your own code

The strand and the group are optional. Two ways to opt out, each one line:

hitlib::studio::begin(yourGroup);

puts the design's strands into a group you already own and attaches their profiles, leaving init(), start() and the refresh interval to you. Or:

#define HITLIB_STUDIO_NO_AUTOWIRE
#include "hitlib_studio.hpp"

and the file defines no strands and no group - only the profile and the constants:

namespace myRobot = hitlib::profiles::myRobot;
hitlib::LedStrand myRobotStrand(myRobot::adiPort, myRobot::length, myRobot::refreshMs);
void initialize() {
group.add(&myRobotStrand);
group.init(myRobot::refreshMs);
group.start();
myRobot::apply(group); // brightness + attachProfile
group.activateMode(myRobot::mode::idle);
}
Owns a set of LedStrand pointers and fans all animation commands out to each strand simultaneously.
Definition led_group.hpp:43
void add(LedStrand *strand)
Register a strand with this group.
void init(uint32_t refreshMs=20)
Initialize hardware on all registered strands.
void start()
Start the group refresh task.
void activateMode(uint8_t modeIdx)
Push a persistent mode onto the mode stack.

One deployed header per project: it defines hitlib::studio, so two in one build would define begin() twice. Keep every strand in one document, which is what Deploy exports.

Multiple strands

Deploy writes the whole document, so multi-strand designs need nothing extra. Two separately exported headers can collide: if both have an Idle mode, each defines its own setup function for it and they cannot share a .cpp.

namespace left = hitlib::profiles::left;
namespace right = hitlib::profiles::right;
void initialize() {
hitlib::studio::begin(); // both strands, both profiles, both started
}
void opcontrol() {
left::strand.activateMode(left::mode::idle);
right::strand.activateMode(right::mode::idle);
}

Strands designed at different refresh intervals get a group each, since a group ticks everything it owns at one rate. begin() handles that; it is still one call.

Strand names become namespace names, so each strand needs a distinct one. Pattern Studio blocks the export until they are unique.

Exporting to a file instead

Export > Export Current Strand as C++... and Export All Strands as C++... still save through a normal file dialog, for a project Pattern Studio cannot see or a header you want to check in by hand. The generated code is identical.


Mode Stack Rules

  • Modes are stored in a priority stack, the highest-priority active mode wins.
  • Multiple modes can be active simultaneously, the winner updates every tick.
  • Timed modes auto-expire, persistent modes stay until deactivateMode() is called.
  • Calling activateModeTimed() on an already-timed mode extends its deadline rather than creating a duplicate entry.

Sequencer

hitlib::Sequencer drives multi-phase endgame / event sequences inside onActivate and onTick profile callbacks.

static void phase1(hitlib::LedStrand& s) { s.flash(0xFFFF00, 150, 150); }
static void phase2(hitlib::LedStrand& s) { s.rainbow(2); }
static const hitlib::Sequencer::Phase phases[] = {
{2000, phase1}, // 2 s yellow flash
{8000, phase2}, // 8 s rainbow
};
static hitlib::Sequencer seq(phases, 2);
static void myEgActivate(hitlib::LedStrand& s) { seq.start(s); }
static void myEgTick (hitlib::LedStrand& s) { seq.update(s); }
Forward declaration.
Definition led_sequencer.hpp:49
One phase in the animation sequence.
Definition led_sequencer.hpp:59

Phases loop indefinitely until the mode is deactivated.

One Sequencer can run on several strands at once, each at its own phase, so a profile attached to a whole group (group.attachProfile(), or a generated apply(group)) sequences every strand in it. It keeps track of up to Sequencer::MAX_STRANDS (8) strands; starting it on a ninth takes over from the strand that has gone longest without an update(), normally one whose mode has since been replaced. stop() stops it everywhere and stop(strand) on one strand.