Wemos8266RelaysLedDisplay/src/MD_RobotEyes.h

217 lines
7.5 KiB
C++

// Implements a class to draw and animate a pair of 'emotive' eyes for a robot
//
#pragma once
#include <MD_MAX72xx.h>
// Misc defines
#if !defined(ARRAY_SIZE)
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) ///< number of elements in an array
#endif
#define EYE_COL_SIZE 8 ///< number of columns in one eye
// Module offsets from first module specified
#define LEFT_MODULE_OFFSET 1 ///< offset from the base LED module for the left eye
#define RIGHT_MODULE_OFFSET 0 ///< offset from the base LED module for the right eye
// Array references for eyeData array below
#define LEFT_EYE_INDEX 1 ///< array reference in the eye data for the left eye
#define RIGHT_EYE_INDEX 0 ///< array reference in the eye data for the right eye
// Basic unit of time a frame is displayed
#define FRAME_TIME 100 ///< minimum animation time
/**
* Robot Eyes Class.
* This class manages the displayed of animated eyes using LED matrices using the functions
* provided by the MD_MAX72xx library.
*/
class MD_RobotEyes
{
public:
/**
* Emotions enumerated type.
*
* This enumerated type defines the emotion animations
* available in the class for the eyes display
*/
//
typedef enum
{
E_NONE, ///< placeholder for no emotions, not user selectable
E_NEUTRAL, ///< eyes in neutral position (no animation)
E_BLINK, ///< both eyes blink
E_WINK, ///< one eye blink
E_LOOK_L, ///< both eyes look left
E_LOOK_R, ///< both eyes look right
E_LOOK_U, ///< both eyes look up
E_LOOK_D, ///< both eyes look down
E_ANGRY, ///< eyes look angry (symmetrical)
E_SAD, ///< eyes look sad (symmetrical)
E_EVIL, ///< eyes look evil (symmetrical)
E_EVIL2, ///< eyes look evil (asymmetrical)
E_SQUINT, ///< both eye squint
E_DEAD, ///< eyes indicate dead (different)
E_SCAN_UD, ///< both eyes scanning Up/Down
E_SCAN_LR, ///< both eyes scanning Left/Right
} emotion_t;
/**
* Class Constructor.
*
* Instantiate a new instance of the class.
*/
MD_RobotEyes(void):
_timeBlinkMinimum(5000), _animState(S_IDLE), _autoBlink(true), _nextEmotion(E_NEUTRAL)
{};
/**
* Class Destructor.
*
* Released any allocated memory and does the necessary to clean
* up once the object is no longer required.
*/
~MD_RobotEyes(void) { };
/**
* Initialize the object.
*
* Initialize the object data. This needs to be called during setup() to initialize new
* data for the class that cannot be done during the object creation.
*
* Outside of the class, the MD_MAX72xx library should be initialized and the pointer
* to the MD_MAX72xx object passed to the parameter. Also, as the eyes could be in the
* middle of a string of LED modules, the first 'eye' module can be specified.
*
* /param M pointer to the MD_MAX72xx library object.
* /param moduleStart the first 'eye' LED module. Defaults to 0 if not specified.
*/
void begin(MD_MAX72XX *M, uint8_t moduleStart = 0);
/**
* Set the animation type and parameters.
*
* Set the next animations to the specified. Additionally, set whether the animation should
* auto reverse the action (eg, blink down then back up again) and whether the animation
* should be run in reverse.
*
* Animations are generally symmetric, so only half the animation needs to be specified.
* If an animated expression needs to be held, the animation should be run without auto
* reverse, which holds the animation at the end point, and then later run the animation
* in reverse from the last position to return to the idle state.
*
* \param e the type of emotion to be displayed, one of the emotion_T enumerated values.
* \param r if true, run auto reverse.
* \param b if true, start the animation from the end of the sequence.
*/
inline void setAnimation(emotion_t e, bool r, bool b = false) { _nextEmotion = e; _autoReverse = r; _animReverse = b; };
/**
* Set the blink time.
*
* When no animation is running and AutoBlink is set, the eyes will occasionally blink.
* Set the minimum time period between blinks. A blink will occur a random time after this.
*
* \param t the minimum time between blinking actions in milliseconds.
*/
inline void setBlinkTime(uint16_t t) { _timeBlinkMinimum = t; };
/**
* Set or reset auto blink mode.
*
* When no animation is running and AutoBlink is set, the eyes will occasionally blink.
*
* \param b set auto blink if true, reset auto blink if false.
*/
inline void setAutoBlink(bool b) { _autoBlink = b; };
/**
* Display a text message.
*
* At the end of the current animation, the text will be scrolled across the 'eyes'
* and then the eyes are returned to the neutral expression
*
* \param p a pointer to a char array containing a nul terminated string.
The string must remain in scope while the message is being displayed.
*/
inline bool setText(const char *pText) { if (_pText != nullptr) return(false); else _pText = pText; return(true); };
/**
* Animate the display.
*
* This method needs to be invoked as often as possible to ensure smooth animation.
*
* The calling program should monitor the return value for 'true' in order to know when
* the animation has concluded. A 'true' return value means that the animation is complete.
*
* \return bool true if the animation has completed, false otherwise.
*/
bool runAnimation(void);
protected:
// Animations FSM state
typedef enum
{
S_IDLE,
S_RESTART,
S_ANIMATE,
S_PAUSE,
S_TEXT,
} animState_t;
// Define an animation frame
typedef struct
{
uint8_t eyeData[2]; // [LEFT_MODULE_OFFSET] and [RIGHT_MODULE_OFFSET] eye character from font data
uint16_t timeFrame; // time for this frame in milliseconds
} animFrame_t;
// Define an entry in the animation sequence lookup table
typedef struct
{
emotion_t e;
const animFrame_t *seq;
uint8_t size;
} animTable_t;
// Display parameters
MD_MAX72XX *_M;
uint16_t _sd; // start module for the display
// Animation parameters
uint32_t _timeStartPause;
uint32_t _timeLastAnimation;
uint16_t _timeBlinkMinimum;
animState_t _animState;
bool _autoBlink;
uint16_t _scrollDelay;
// Animation control data
animTable_t _animEntry; // record with animation sequence parameters
int8_t _animIndex; // current index in the animation sequence
bool _animReverse; // true = reverse sequence, false = normal sequence
bool _autoReverse; // true = always play the reverse, false = selected direction only
emotion_t _nextEmotion; // the next emotion to display
const char * _pText; // pointer to text data in user code. Not null means there is text to print
// Methods
void loadEye(uint8_t module, uint8_t ch);
void drawEyes(uint8_t L, uint8_t R);
uint8_t loadSequence(emotion_t e); // return the size of the sequence
void loadFrame(animFrame_t* pBuf);
void showText(bool bInit = false);
void dumpSequence(const animFrame_t* pBuf, uint8_t numElements); // debugging routine only
// Static data tables
static const animFrame_t seqBlink[], seqWink[];
static const animFrame_t seqLeft[], seqRight[], seqUp[], seqDown[];
static const animFrame_t seqAngry[], seqSad[], seqEvil[], seqEvil2[];
static const animFrame_t seqSquint[], seqDead[];
static const animFrame_t seqScanUpDown[], seqScanLeftRight[];
// Lookup table to find animation
static const animTable_t lookupTable[];
};