including light effects from different project

This commit is contained in:
2024-12-15 22:46:44 +01:00
parent e4621f8ad8
commit 6db9bd85a5
20 changed files with 1533 additions and 43 deletions
+4
View File
@@ -0,0 +1,4 @@
idf_component_register(
SRC_DIRS "src"
INCLUDE_DIRS "include"
)
+202
View File
@@ -0,0 +1,202 @@
/**
* @file color.hpp
* @author Gustice
* @brief
*
* @copyright Copyright (c) 2024
*/
#pragma once
#include <cassert>
#include <cstdint>
#include <cstring>
#include <type_traits>
/// No Waveform definition
extern uint8_t const *const noWave;
/**
* @brief Color class
*
* @details Defines color data and common operations with color objects
* \li Supports RGB with overlapping white channel (e.g. SK6812-LED)
* \li Supports Operator overloading with '+' and '*' (Combined operation with MixWith)
* \li Return formatted data stream that can be directly sent to an LED
*/
class Color {
private:
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t white;
public:
struct Rgbw {
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t white;
};
struct Rgb {
uint8_t red;
uint8_t green;
uint8_t blue;
};
/// @brief Construct a new Color object
/// @details Default color will be Black \ref _ctBlack
Color(void) {
set(0, 0, 0, 0);
};
/// @brief Copy-Construct a new Color object
Color(const Color & c) {
red = c.red;
green = c.green;
blue = c.blue;
white = c.white;
};
/// @brief Construct a new Color object according to rgb color-channel-values
/// @details This constructor also takes an overlapping white channel
/// @param r Red channel
/// @param g Green channel
/// @param b Blue channel
/// @param w White channel (zero by default if not assigned)
Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { set(r, g, b, w); };
/// @brief Set the Color object according to rgb color-channel values
/// @param r Red channel
/// @param g Green channel
/// @param b Blue channel
/// @param w White channel (zero by default if not assigned)
void set(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) {
red = r;
green = g;
blue = b;
white = w;
}
/// @brief Returns Color as Color_t structure
/// @return Color_t Color
template <typename T>
T getColor(void) const {
if constexpr (std::is_same<T, Rgbw>::value) {
return {red, green, blue, white};
} else if constexpr (std::is_same<T, Rgb>::value) {
return {red, green, blue};
} else {
throw "wrong type";
}
}
uint8_t getRed() { return red; };
uint8_t getGreen() { return green; };
uint8_t getBlue() { return blue; };
uint8_t getWhite() { return white; };
/// @brief Scaling and overlaying with given color.
/// @param c2 Color 2
/// @param k scaling factor
/// @return Overlayed Color
Color mixWith(const Color c2, uint8_t k = 0x7F) {
auto t(c2);
*this = (*this * (UINT8_MAX - k)) + (t * k);
return *this;
}
/// @brief Writes Color as formatted byte stream to location
/// @param color
template <typename T>
void writeByteStreamTo(uint8_t raw[sizeof(T)]) const {
if constexpr (std::is_same<T, Rgbw>::value) {
raw[0] = blue;
raw[1] = green;
raw[2] = red;
raw[3] = white;
} else if constexpr (std::is_same<T, Rgb>::value) {
raw[0] = blue;
raw[1] = green;
raw[2] = red;
} else {
throw "wrong type";
}
}
/// @brief Assign overload
Color operator=(const Color &c2) {
red = c2.red;
green = c2.green;
blue = c2.blue;
white = c2.white;
return *this;
}
/// @brief Checks if both color values are equal
bool operator==(Color const &c2) const {
return ((blue == c2.blue) && (green == c2.green) &&
(red == c2.red) && (white == c2.white));
}
/// @brief Checks if both color values are not equal
bool operator!=(Color const &c2) const {
return ((blue != c2.blue) || (green != c2.green) ||
(red != c2.red) || (white != c2.white));
}
/// @brief Scale color by scaling each color-channel by given factor
/// @param c1 Color
/// @param k Scale-factor uint8_t.Max is full-scale
/// @return Scaled Color
Color operator*(uint8_t k) {
red = (uint8_t)((uint16_t)red * k / 0xFF);
green = (uint8_t)((uint16_t)green * k / 0xFF);
blue = (uint8_t)((uint16_t)blue * k / 0xFF);
white = (uint8_t)((uint16_t)white * k / 0xFF);
return *this;
}
/// @brief Overlaying to colors my adding each color channel value
/// @note Colors channels should not overlap since there is no automatic ratio correction.
/// Use *-Operator first in order to scale each color value a a way that no overflow can occur
/// @param c2 Color 2
/// @return Overlayed Color
Color operator+(Color c2) {
red = red + c2.red;
green = green + c2.green;
blue = blue + c2.blue;
white = white + c2.white;
return *this;
}
};
static_assert(sizeof(Color) == 4 && "Color Type is to big");
/// Null color object
extern const Color *noColor;
/// Used old color agreement (if no color pointer)
extern const Color *oldColor;
/// Black color object constant
extern const Color CBlack;
/// Red color object constant
extern const Color CRed;
/// Green color object constant
extern const Color CGreen;
/// Blue color object constant
extern const Color CBlue;
/// Magenta color object constant
extern const Color CMagenta;
/// Cyan color object constant
extern const Color CCyan;
/// Yellow color object constant
extern const Color CYellow;
/// White color object constant
extern const Color CWhite;
/// Warm white color object constant (with higher red channel value)
extern const Color CWarmWhite;
/// Black color object constant (with higher green channel value)
extern const Color CColdWhite;
/// Black color object constant (with higher blue channel value)
extern const Color CSpookyWhite;
+202
View File
@@ -0,0 +1,202 @@
/**
* @file color.hpp
* @author Gustice
* @brief
*
* @copyright Copyright (c) 2024
*/
#pragma once
#include <cassert>
#include <cstdint>
#include <cstring>
#include <type_traits>
/// No Waveform definition
extern uint8_t const *const noWave;
/**
* @brief Color class
*
* @details Defines color data and common operations with color objects
* \li Supports RGB with overlapping white channel (e.g. SK6812-LED)
* \li Supports Operator overloading with '+' and '*' (Combined operation with MixWith)
* \li Return formatted data stream that can be directly sent to an LED
*/
class Color {
private:
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t white;
public:
struct Rgbw {
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t white;
};
struct Rgb {
uint8_t red;
uint8_t green;
uint8_t blue;
};
/// @brief Construct a new Color object
/// @details Default color will be Black \ref _ctBlack
Color(void) {
set(0, 0, 0, 0);
};
/// @brief Copy-Construct a new Color object
Color(const Color & c) {
red = c.red;
green = c.green;
blue = c.blue;
white = c.white;
};
/// @brief Construct a new Color object according to rgb color-channel-values
/// @details This constructor also takes an overlapping white channel
/// @param r Red channel
/// @param g Green channel
/// @param b Blue channel
/// @param w White channel (zero by default if not assigned)
Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { set(r, g, b, w); };
/// @brief Set the Color object according to rgb color-channel values
/// @param r Red channel
/// @param g Green channel
/// @param b Blue channel
/// @param w White channel (zero by default if not assigned)
void set(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) {
red = r;
green = g;
blue = b;
white = w;
}
/// @brief Returns Color as Color_t structure
/// @return Color_t Color
template <typename T>
T getColor(void) const {
if constexpr (std::is_same<T, Rgbw>::value) {
return {red, green, blue, white};
} else if constexpr (std::is_same<T, Rgb>::value) {
return {red, green, blue};
} else {
throw "wrong type";
}
}
uint8_t getRed() { return red; };
uint8_t getGreen() { return green; };
uint8_t getBlue() { return blue; };
uint8_t getWhite() { return white; };
/// @brief Scaling and overlaying with given color.
/// @param c2 Color 2
/// @param k scaling factor
/// @return Overlayed Color
Color mixWith(const Color c2, uint8_t k = 0x7F) {
auto t(c2);
*this = (*this * (UINT8_MAX - k)) + (t * k);
return *this;
}
/// @brief Writes Color as formatted byte stream to location
/// @param color
template <typename T>
void writeByteStreamTo(uint8_t raw[sizeof(T)]) const {
if constexpr (std::is_same<T, Rgbw>::value) {
raw[0] = blue;
raw[1] = green;
raw[2] = red;
raw[3] = white;
} else if constexpr (std::is_same<T, Rgb>::value) {
raw[0] = blue;
raw[1] = green;
raw[2] = red;
} else {
throw "wrong type";
}
}
/// @brief Assign overload
Color operator=(const Color &c2) {
red = c2.red;
green = c2.green;
blue = c2.blue;
white = c2.white;
return *this;
}
/// @brief Checks if both color values are equal
bool operator==(Color const &c2) const {
return ((blue == c2.blue) && (green == c2.green) &&
(red == c2.red) && (white == c2.white));
}
/// @brief Checks if both color values are not equal
bool operator!=(Color const &c2) const {
return ((blue != c2.blue) || (green != c2.green) ||
(red != c2.red) || (white != c2.white));
}
/// @brief Scale color by scaling each color-channel by given factor
/// @param c1 Color
/// @param k Scale-factor uint8_t.Max is full-scale
/// @return Scaled Color
Color operator*(uint8_t k) {
red = (uint8_t)((uint16_t)red * k / 0xFF);
green = (uint8_t)((uint16_t)green * k / 0xFF);
blue = (uint8_t)((uint16_t)blue * k / 0xFF);
white = (uint8_t)((uint16_t)white * k / 0xFF);
return *this;
}
/// @brief Overlaying to colors my adding each color channel value
/// @note Colors channels should not overlap since there is no automatic ratio correction.
/// Use *-Operator first in order to scale each color value a a way that no overflow can occur
/// @param c2 Color 2
/// @return Overlayed Color
Color operator+(Color c2) {
red = red + c2.red;
green = green + c2.green;
blue = blue + c2.blue;
white = white + c2.white;
return *this;
}
};
static_assert(sizeof(Color) == 4 && "Color Type is to big");
/// Null color object
extern const Color *noColor;
/// Used old color agreement (if no color pointer)
extern const Color *oldColor;
/// Black color object constant
extern const Color CBlack;
/// Red color object constant
extern const Color CRed;
/// Green color object constant
extern const Color CGreen;
/// Blue color object constant
extern const Color CBlue;
/// Magenta color object constant
extern const Color CMagenta;
/// Cyan color object constant
extern const Color CCyan;
/// Yellow color object constant
extern const Color CYellow;
/// White color object constant
extern const Color CWhite;
/// Warm white color object constant (with higher red channel value)
extern const Color CWarmWhite;
/// Black color object constant (with higher green channel value)
extern const Color CColdWhite;
/// Black color object constant (with higher blue channel value)
extern const Color CSpookyWhite;
@@ -0,0 +1,60 @@
/**
* @file ExactIndex.h
* @author Gustice
* @brief Index-Class with fix-point arithmetic
*/
#pragma once
#include <algorithm>
#include <cstdint>
/**
* @brief Index-Class with fix-point arithmetic for exact stepping though data-points
* @details Allows stepping through data-points by quotient step-sizes
*/
class ExactIndex {
public:
/// @brief Construct Index-object according to number of data points and steps to be taken
/// @param length
/// @param steps
ExactIndex(unsigned length, uint16_t steps) : Length(std::max(1u, length)), Steps(steps) {
_delta = Length << 8;
_delta = _delta / steps;
_index = unsigned(0u - _delta);
}
/// @brief Get current index-position as floor value to never pass upper bound
uint16_t GetIdx(void) { return ((_index) >> 8); };
/// @brief Move index by quotient step forward
ExactIndex &operator++() {
_index = _index + _delta;
return *this;
}
/// @brief Move index by quotient step backward
ExactIndex &operator--() {
_index = _index - _delta;
return *this;
}
/// @brief Get current index-position as floor value to never pass upper bound
operator int() const { return ((_index) >> 8); }
/// @brief Get size of data set (number of data points)
unsigned GetLength() { return Length; }
/// @brief Get number of steps that are to be taken though data set
uint16_t GetSteps() { return Steps; }
private:
/// @brief Number of data points
unsigned Length;
/// @brief Steps to be taken through data-set
uint16_t Steps;
/// @brief Current index in fixed-point representation
unsigned _index = 0;
/// @brief Step-with in fixed-point representation
unsigned _delta;
};
+95
View File
@@ -0,0 +1,95 @@
/**
* @file Macro.h
* @author Gustice
* @brief Predefined effect macros
*
* @copyright Copyright (c) 2024
*/
#pragma once
#include "Color.hpp"
#include "Waveform.hpp"
#include <cstdint>
#include <vector>
namespace Effect {
/// Collection of process values to maintain processing state machine
struct StepProcessVars {
/// @brief Reference of current color
const Color &pCurentColor;
/// @brief Number of color entries
uint8_t ColorLen;
/// @brief Idle value for intensity
uint8_t IdleIntensity;
/// @brief Full value for intensity
uint8_t FullIntensity;
/// @brief Dynamic range for effects
uint8_t DynamicRange;
/// @brief Index in current waveform
uint16_t waveIdx;
/// @brief Reference to processed waveform
const WaveForm_t *const pWave;
};
/**
* @brief Effect implementation for State-Machine
* @details State-Machine invokes referred effect-implementation during processing
*/
typedef Color (*EffectImplementation)(StepProcessVars &pcsParam);
/**
* @brief Definition of Effect-Macro
*/
struct Macro_t {
/// @brief Effect type
const EffectImplementation pImplementation;
/// @brief Effect duration waveforms will be compressed / stretched to fit the duration
const uint8_t Duration;
/// @brief Next SubEffect
const int8_t Next;
/// @brief waveform reference
const WaveForm_t *pWave = emptyWave;
/// @brief Full-Scale-intensity
const uint8_t FsIntensity;
/// @brief Num of Repeats
const int8_t Repeats;
Macro_t(uint8_t duration, int8_t next, EffectImplementation state, uint8_t intensity,
int8_t repeats = 0)
: pImplementation(state), Duration(duration), Next(next), FsIntensity(intensity),
Repeats(repeats) {}
Macro_t(uint8_t duration, int8_t next, WaveForm_t const *wave, EffectImplementation state,
uint8_t intensity, int8_t repeats = 0)
: pImplementation(state), Duration(duration), Next(next), pWave(wave),
FsIntensity(intensity), Repeats(repeats) {}
private:
/// @brief Null-pointer to represent no change in color
static constexpr Color *pOldColor = nullptr;
/// @brief Null-pointer to represent no actual waveform-representation
static constexpr WaveForm_t *pNoWave = nullptr;
/// @brief Default implemented Waveform with no profile
static const constexpr WaveForm_t *emptyWave = &EmptyWave;
};
using Macro = std::vector<Macro_t>;
/// @brief Dark - Nothing to see here
extern const Macro macDark;
/// @brief Continuous idle
extern const Macro macIdle;
/// @brief Continuous flicker
extern const Macro macStartFlicker;
/// @brief Smooth turn on and then remaining idle
extern const Macro macStartIdle;
/// @brief Smooth turn on to full color
extern const Macro macStartFull;
/// @brief Slow pulse starting from idle intensity. Appears like breathing
extern const Macro macStdPulse;
/// @brief Short pulses starting from idle intensity. Appears nervously.
extern const Macro macNervousPulse;
} // namespace Effect
@@ -0,0 +1,53 @@
/**
* @file EffectProcessor.h
* @author Gustice
* @brief Effect-Processor-Class for processing effect scenarios and switching actions
*
* @copyright Copyright (c) 2024
*/
#pragma once
#include "Color.hpp"
#include "Macro.hpp"
#include "StateMachine.hpp"
#include <array>
#include <cstdint>
namespace Effect {
/**
* @brief EffectProcessor Class
* @details Maintains State-Machines, Processes effect-switching, and invokes generation
* of raw data stream for LEDs.
* \li If effect scenarios are changed a cross-fading is executed
* \li Raw-data-stream for each Color object is generated
*/
class EffectProcessor {
public:
///@brief Constructor
///@param fadeSteps Steps count for cross fade
EffectProcessor(uint8_t const fadeSteps = 8);
/// @brief Set next effect sequence
/// @param sequence reference to sequence that has to be processed next
/// @param sColor Start color. Use noColor to start with default color
/// @param intens Idle intensity for effect. Use NULL to start with default intensity
void setEffect(const Macro &sequence, Color const *sColor = noColor, uint8_t delay = 0, uint8_t intens = gu8_idleIntensity);
/// @brief Execute step of effect processor
/// @return Reference to color result
Color tick(void);
private:
StateMachine::Parameter _parameter;
std::array<StateMachine, 2> _effectMachines;
uint8_t _fadeSteps;
uint8_t _fadingCnt;
StateMachine *_EffPV;
StateMachine *_EffPV_old;
Color crossFadeColors(Color &cNew, Color &cOld, uint8_t k);
EffectProcessor();
};
} // namespace Effect
@@ -0,0 +1,118 @@
/**
* @file EffectStateMachine.h
* @author Gustice
* @brief Effect-State-Machine-Class for processing Effect Macros
* @version 0.6
* @date 2019-10-01
*
* @copyright Copyright (c) 2019
*
*/
#pragma once
#include "Color.hpp"
#include "Macro.hpp"
#include <cstdint>
#include "ExactIndex.hpp"
namespace Effect {
/**
* @brief Effect State Machine Class
* @details Processes Effect macro array Macro entry subsequently
* Processing is executed in following manner:
* \li Index-incrementing each Tick
* \li ticks until duration \ref EffMacro is finished
* \li each tick the index incremented by templateLength/duration
* \li Repeats-processing
* \li Each repeat the tick and the index are set back to start
* \li Switches to next Macro enty
* \li Next entry is given by 'next' index
* \li Color changes can be executed optionally
*/
class StateMachine {
public:
/// @brief Collection of object configuration at startup
struct Parameter {
/// @brief Idle intensity as background color
uint8_t idleIntens;
/// @brief dynamic range as variation around background color
uint8_t dynamicRange;
/// @brief Fade steps for color changes
uint8_t fadeSteps;
/// @brief Count of target LEDs
uint8_t targetCount;
};
///@brief Construct a new Effect State Machine object
///@param templateLength Length of waveforms that are used to display
StateMachine(Parameter &parameter);
///@brief Sets the effect state machine to process given effect macro
///@param sequence reference to sequence
///@param startColor Start color. Use noColor to invoke with default color
///@param initialDelay Number of steps that are waited before effect is started
void setEffect(const Macro &sequence, Color const *startColor = noColor, uint8_t initialDelay = 0);
/// @brief Sets the effect state machine to process given effect macro
/// @param sequence reference to sequence
/// @param startColor Start color. Use noColor to invoke with default color
/// @param intens Idle intensity for effect. Use nullptr to start with default intensity
/// @param delayedStart Number of steps that are waited before effect is started
void setEffect(const Macro &sequence, Color const *startColor, const uint8_t *intens,
const uint8_t delayedStart);
/// @brief Execute step of state machine
/// @details Each tick the tick variable is decremented. After the limit is reached,
/// the current macro line is either repeated or the next macro line is started (with
/// an optional color change).\n
/// @note Tick must be called regularly. For standard light applications all 40 ms seems to be
/// a convenient values.
/// @return Color const* Reference to color result
virtual Color tick(void);
/// Black out lights
static Color UpdateBlank(StepProcessVars &pcsParam);
/// Continuous illumination in idle intensity
static Color UpdateIdle(StepProcessVars &pcsParam);
/// Keep latest state on hold
static Color UpdateFreeze(StepProcessVars &pcsParam);
/// Step through a defined waveform
static Color UpdateWave(StepProcessVars &pcsParam);
/// Step through a defined waveform backwards
static Color UpdateRevWave(StepProcessVars &pcsParam);
/// Show flicker effect with idle intensity as static part with dynamic flicker-range
static Color UpdateFlicker(StepProcessVars &pcsParam);
protected:
/// Collection of process values to maintain processing state machine
struct ProcessValues {
/// Decrementing tick, counting duration
uint8_t tick;
/// Decrementing repeats of current Effect-part
uint8_t repeats;
/// Index to macro element
uint8_t macroIdx;
/// Current index to waveform
uint16_t waveIdx;
/// Index step each tick according to duration and template length.
uint16_t waveStep;
};
/// @brief Concentrated parameter values of instance
Parameter &_params;
Color const * _curentColor;
ProcessValues _prcValues;
void SetIndexes(void);
Macro const *_p_effSeq;
Macro_t const *_p_effMac;
ExactIndex _idx;
// Color& (*apF_Effects)(StateMachine * SM);
typedef Color const *pEffPrc(StateMachine *);
};
} // namespace Effect
@@ -0,0 +1,27 @@
/**
* @file WaveForm.h
* @author Gustice
* @brief C++ encapsulated waveform definition
*/
#pragma once
#include "Waveforms.hpp"
#include <cstdint>
namespace Effect {
/**
* @brief Waveform definition based on c-definitions
*/
struct WaveForm_t {
/// @brief Reference to waveform
const uint8_t *const pWave;
/// @brief Length of waveform
const unsigned Length;
};
/// @brief Empty waveform with defined pointer to meaningless data to avoid problems
extern const WaveForm_t EmptyWave;
} // namespace Effect
@@ -0,0 +1,38 @@
/**
* @file EffectWaveforms.h
* @author Gustice
* @brief Predefined Waveforms that can be put together to a complex Waveform
* @version 0.6
* @date 2019-10-01
*
* @copyright Copyright (c) 2019
*
*/
#pragma once
#include <cstdint>
namespace Effect {
/// Length of all subsequent Template definitions
extern const uint16_t cu16_TemplateLength;
/// Idle intensity for Slopes and Pulses
extern const uint8_t gu8_idleIntensity;
/// Idle intensity for Slopes and Pulses
extern const uint8_t gu8_fullIntensity;
/// Headroom for Effects on top of idle brightness
extern const uint8_t gu8_dynamicRange;
/// Standard cross fade steps
extern const uint8_t gu8_fadeSteps;
/// Starts from idle \ref gu8_idleIntensity and represents a gaussian impulse \ref gu8_fullIntensity
extern const uint8_t gau8_offsetPulse[];
/// Starts at zero and rises to idle intensity \ref gu8_idleIntensity
extern const uint8_t gau8_initSlope[];
/// Starts from idle \ref gu8_idleIntensity and rises up to full intensity \ref gu8_fullIntensity
extern const uint8_t gau8_offsetSlope[];
/// Starts at zero and rises to full intensity \ref gu8_fullIntensity
extern const uint8_t gau8_fullSlope[];
/// Starts at zero and represents a gaussian impulse to full intensity \ref gu8_fullIntensity
extern const uint8_t gau8_fullPulse[];
} // namespace Effect
+27
View File
@@ -0,0 +1,27 @@
/**
* @file color.cpp
* @author Gustice
* @brief Implementation of Color-Class
*
* @copyright Copyright (c) 2024
*/
#include "Color.hpp"
uint8_t const * const noWave = nullptr;
const Color *noColor = nullptr;
const Color *oldColor = nullptr;
const Color CBlack(0x00, 0x00, 0x00, 0x00);
const Color CRed(0xFF, 0x00, 0x00, 0x00);
const Color CGreen(0x00, 0xFF, 0x00, 0x00);
const Color CBlue(0x00, 0x00, 0xFF, 0x00);
const Color CMagenta(0x80, 0x00, 0x80, 0x00);
const Color CCyan(0x00, 0x80, 0x80, 0x00);
const Color CYellow(0x80, 0x80, 0x00, 0x00);
const Color CWhite(0x55, 0x55, 0x55, 0x00);
const Color CWarmWhite(0x7F, 0x4F, 0x4F, 0x00);
const Color CColdWhite(0x4F, 0x4F, 0x7F, 0x00);
const Color CSpookyWhite(0x4F, 0x7F, 0x4F, 0x00);
+71
View File
@@ -0,0 +1,71 @@
/**
* @file EffectMacro.cpp
* @author Gustice
* @brief Predefined Macros
* @version 0.6
* @date 2021-03-23
*
* @copyright Copyright (c) 2021
*/
#include "Macro.hpp"
#include "Color.hpp"
#include "Waveforms.hpp"
#include "StateMachine.hpp"
namespace Effect {
const uint8_t Empty[4] = {0, 0, 0, 0};
const WaveForm_t EmptyWave{Empty, 4};
const WaveForm_t OffsetPulseWave{gau8_offsetPulse, cu16_TemplateLength};
const WaveForm_t InitSlopeWave{gau8_initSlope, cu16_TemplateLength};
const WaveForm_t OffsetSlopeWave{gau8_offsetSlope, cu16_TemplateLength};
const WaveForm_t FullSlopeWave{gau8_fullSlope, cu16_TemplateLength};
const WaveForm_t FullPulseWave{gau8_fullPulse, cu16_TemplateLength};
const Macro macDark = {
{32, 0, StateMachine::UpdateBlank, 0, 0}
};
const Macro macIdle = {
{32, 1, StateMachine::UpdateIdle, 0xFF},
{32, 1, StateMachine::UpdateIdle, 0xFF},
};
const Macro macStartIdle = {
{64, 1, &InitSlopeWave, StateMachine::UpdateWave, 0xFF},
{32, 1, StateMachine::UpdateIdle, 0xFF},
};
const Macro macStartFlicker = {
{64, 1, &InitSlopeWave, StateMachine::UpdateWave, 0xFF},
{32, 1, StateMachine::UpdateFlicker, 0xFF},
};
const Macro macStartFull = {
{24, 1, &FullSlopeWave, StateMachine::UpdateWave, 0xFF},
{32, 1, StateMachine::UpdateIdle, 0xFF},
};
const Macro macStdPulse = {
{32, 1, StateMachine::UpdateIdle, 0},
{32, 2, &OffsetPulseWave, StateMachine::UpdateWave, 0xFF},
{32, 0, StateMachine::UpdateIdle, 0},
};
const Macro macNervousPulse = {
{8, 1, StateMachine::UpdateIdle, 0},
{16, 2, &OffsetPulseWave, StateMachine::UpdateWave, 0xFF},
{8, 0, StateMachine::UpdateIdle, 0},
};
const Macro macStdWipe = {
{32, 1, StateMachine::UpdateIdle, 0},
{32, 2, &OffsetSlopeWave, StateMachine::UpdateWave, 0xFF},
{32, 3, StateMachine::UpdateFreeze, 0},
{32, 4, &OffsetSlopeWave, StateMachine::UpdateRevWave, 0xFF},
{32, 1, StateMachine::UpdateFreeze, 0},
};
} // namespace Effect
+58
View File
@@ -0,0 +1,58 @@
/**
* @file Processor.cpp
* @author Gustice
* @brief Implementation of Effect-Processor-Class EffectProcessor.h
* @version 0.6
* @date 2019-10-03
*
* @copyright Copyright (c) 2019
*/
#include "Processor.hpp"
#include "Macro.hpp"
#include "Waveforms.hpp"
#include <algorithm>
#include <math.h>
#include <stdlib.h>
namespace Effect {
EffectProcessor::EffectProcessor(uint8_t const fadeSteps)
: _parameter{0x20, 0x20, 0, 1},
_effectMachines{StateMachine{_parameter}, StateMachine{_parameter}} {
_EffPV = &_effectMachines[0];
_EffPV_old = &_effectMachines[1];
_EffPV->setEffect(macDark, noColor);
_fadeSteps = fadeSteps;
}
void EffectProcessor::setEffect(const Macro &sequence, Color const *sColor, uint8_t delay, uint8_t intens) {
_fadingCnt = _fadeSteps;
std::swap(_EffPV, _EffPV_old);
_EffPV->setEffect(sequence, sColor, &intens, delay);
}
Color EffectProcessor::tick(void) {
uint8_t k;
auto cN = (_EffPV->tick());
if (_fadingCnt > 0) { // always process soft cross dissolve between different macros
_fadingCnt--;
k = (uint16_t)_fadingCnt * 0xFF / _fadeSteps;
auto cO = (_EffPV_old->tick());
cN = crossFadeColors(cN, cO, k);
}
return cN;
}
Color EffectProcessor::crossFadeColors(Color &cNew, Color &cOld, uint8_t k) {
cOld = cOld * k;
cNew = cNew * (0xFF - k);
cNew = cNew + cOld;
return cNew;
}
} // namespace Effect
+117
View File
@@ -0,0 +1,117 @@
/**
* @file StateMachine.cpp
* @author Gustice
* @brief Implementation of Effect-State-Machine-Class EffectStateMachine.h
* @version 0.6
* @date 2019-10-01
*
* @copyright Copyright (c) 2019
*
*/
#include "StateMachine.hpp"
#include "Waveforms.hpp"
#include <math.h>
#include <stdlib.h>
namespace Effect {
/// Optional prequel for delayed start.
/// \li Duration must be != 0
/// \li Next entry will be first entry in given Effect sequence
Macro delayPrequel = {
{1, 0, StateMachine::UpdateBlank, 0, 0}
};
StateMachine::StateMachine(Parameter &parameter)
: _params(parameter), _idx(/*pseudo values*/ 1, 1) {}
void StateMachine::setEffect(const Macro &sequence, Color const *startColor, uint8_t initialDelay) {
setEffect(sequence, startColor, &_params.idleIntens, initialDelay);
}
void StateMachine::setEffect(const Macro &sequence, Color const *startColor, const uint8_t *intens,
const uint8_t delayedStart) {
if (startColor != noColor) {
_curentColor = startColor;
}
if (intens != nullptr) {
_params.idleIntens = *intens;
}
_p_effSeq = &sequence;
if (delayedStart > 0) {
_p_effMac = &delayPrequel[0];
_prcValues.tick = delayedStart;
} else {
_p_effMac = &sequence[0];
_prcValues.tick = _p_effMac->Duration;
}
_prcValues.tick++;
_prcValues.repeats = _p_effMac->Repeats;
_idx = ExactIndex(_p_effMac->pWave->Length, _p_effMac->Duration);
}
Color StateMachine::tick(void) {
// tick-increment
if (--_prcValues.tick == 0) {
// repeats-decrement
if (_prcValues.repeats-- == 0) {
_p_effMac = &(*_p_effSeq)[_p_effMac->Next];
_prcValues.repeats = _p_effMac->Repeats;
}
_prcValues.tick = _p_effMac->Duration;
_idx = ExactIndex(_p_effMac->pWave->Length, _p_effMac->Duration);
}
++_idx;
StepProcessVars pv{
.pCurentColor = *_curentColor,
.ColorLen = _params.targetCount,
.IdleIntensity = _params.idleIntens,
.FullIntensity = _p_effMac->FsIntensity,
.DynamicRange = _params.dynamicRange,
.waveIdx = _idx.GetIdx(),
.pWave = _p_effMac->pWave,
};
return _p_effMac->pImplementation(pv);
}
Color StateMachine::UpdateBlank(StepProcessVars &pcsParam) {
return CBlack;
}
Color StateMachine::UpdateIdle(StepProcessVars &pcsParam) {
auto c = pcsParam.pCurentColor;
return c * pcsParam.IdleIntensity;
}
Color StateMachine::UpdateFreeze(StepProcessVars &pcsParam) {
return pcsParam.pCurentColor;
}
Color StateMachine::UpdateWave(StepProcessVars &pcsParam) {
Color temp = pcsParam.pCurentColor;
temp = temp * ((float)(pcsParam.pWave->pWave[pcsParam.waveIdx]) / 0xFF * pcsParam.FullIntensity);
return temp;
}
Color StateMachine::UpdateRevWave(StepProcessVars &pcsParam) {
unsigned lastIndex = pcsParam.pWave->Length - 1;
Color temp = pcsParam.pCurentColor;
temp = temp * ((float)(pcsParam.pWave->pWave[lastIndex - pcsParam.waveIdx]) / 0xFF *
pcsParam.FullIntensity);
return temp;
}
Color StateMachine::UpdateFlicker(StepProcessVars &pcsParam) {
uint8_t k = rand();
k = pcsParam.IdleIntensity + ((int16_t)k * (pcsParam.DynamicRange) / 0xFF);
auto c = pcsParam.pCurentColor;
c = c * ((float)(k));
return c;
}
} // namespace Effect
+100
View File
@@ -0,0 +1,100 @@
/**
* @file Waveforms.cpp
* @author Gustice
* @brief Implementation of defined Waveforms
* @version 0.6
* @date 2019-10-01
*
* @copyright Copyright (c) 2019
* @note The defined waveforms are generated with a the WaveformGenerator
*/
#include "Waveforms.hpp"
namespace Effect {
const uint16_t cu16_TemplateLength = 128;
const uint8_t gu8_idleIntensity = 0x55;
const uint8_t gu8_fullIntensity = 0xFF;
const uint8_t gu8_dynamicRange = gu8_fullIntensity - gu8_idleIntensity;
const uint8_t gu8_fadeSteps = 10;
// clang-format off
// Offset Pulse: Gaussian pulse from background illumination with full scale intensity
const uint8_t gau8_offsetPulse[cu16_TemplateLength * 2] = {
0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x58, 0x58, 0x59, 0x59, 0x5A, 0x5B, 0x5C,
0x5D, 0x5E, 0x5F, 0x61, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6F, 0x71, 0x74, 0x77, 0x7B, 0x7E,
0x81, 0x85, 0x89, 0x8E, 0x92, 0x97, 0x9B, 0xA0, 0xA5, 0xAA, 0xAF, 0xB5, 0xBA, 0xBF, 0xC4, 0xC9,
0xCF, 0xD3, 0xD8, 0xDD, 0xE1, 0xE6, 0xEA, 0xED, 0xF1, 0xF4, 0xF7, 0xF9, 0xFB, 0xFD, 0xFD, 0xFE,
0xFF, 0xFE, 0xFD, 0xFD, 0xFB, 0xF9, 0xF7, 0xF4, 0xF1, 0xED, 0xEA, 0xE6, 0xE1, 0xDD, 0xD8, 0xD3,
0xCF, 0xC9, 0xC4, 0xBF, 0xBA, 0xB5, 0xAF, 0xAA, 0xA5, 0xA0, 0x9B, 0x97, 0x92, 0x8E, 0x89, 0x85,
0x81, 0x7E, 0x7B, 0x77, 0x74, 0x71, 0x6F, 0x6C, 0x6A, 0x68, 0x66, 0x64, 0x62, 0x61, 0x5F, 0x5E,
0x5D, 0x5C, 0x5B, 0x5A, 0x59, 0x59, 0x58, 0x58, 0x57, 0x57, 0x57, 0x56, 0x56, 0x56, 0x55, 0x55,
0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x58, 0x58, 0x59, 0x59, 0x5A, 0x5B, 0x5C,
0x5D, 0x5E, 0x5F, 0x61, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6F, 0x71, 0x74, 0x77, 0x7B, 0x7E,
0x81, 0x85, 0x89, 0x8E, 0x92, 0x97, 0x9B, 0xA0, 0xA5, 0xAA, 0xAF, 0xB5, 0xBA, 0xBF, 0xC4, 0xC9,
0xCF, 0xD3, 0xD8, 0xDD, 0xE1, 0xE6, 0xEA, 0xED, 0xF1, 0xF4, 0xF7, 0xF9, 0xFB, 0xFD, 0xFD, 0xFE,
0xFF, 0xFE, 0xFD, 0xFD, 0xFB, 0xF9, 0xF7, 0xF4, 0xF1, 0xED, 0xEA, 0xE6, 0xE1, 0xDD, 0xD8, 0xD3,
0xCF, 0xC9, 0xC4, 0xBF, 0xBA, 0xB5, 0xAF, 0xAA, 0xA5, 0xA0, 0x9B, 0x97, 0x92, 0x8E, 0x89, 0x85,
0x81, 0x7E, 0x7B, 0x77, 0x74, 0x71, 0x6F, 0x6C, 0x6A, 0x68, 0x66, 0x64, 0x62, 0x61, 0x5F, 0x5E,
0x5D, 0x5C, 0x5B, 0x5A, 0x59, 0x59, 0x58, 0x58, 0x57, 0x57, 0x57, 0x56, 0x56, 0x56, 0x55, 0x55,
};
// Full Pulse: Gaussian pulse from dark to full scale intensity
const uint8_t gau8_fullPulse[cu16_TemplateLength * 2] = {
0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B,
0x0D, 0x0E, 0x10, 0x12, 0x14, 0x17, 0x1A, 0x1D, 0x20, 0x23, 0x27, 0x2B, 0x2F, 0x34, 0x39, 0x3E,
0x43, 0x49, 0x4F, 0x56, 0x5C, 0x63, 0x6A, 0x71, 0x79, 0x80, 0x88, 0x90, 0x98, 0x9F, 0xA7, 0xAF,
0xB7, 0xBE, 0xC5, 0xCC, 0xD3, 0xDA, 0xE0, 0xE5, 0xEA, 0xEF, 0xF3, 0xF6, 0xF9, 0xFC, 0xFD, 0xFE,
0xFF, 0xFE, 0xFD, 0xFC, 0xF9, 0xF6, 0xF3, 0xEF, 0xEA, 0xE5, 0xE0, 0xDA, 0xD3, 0xCC, 0xC5, 0xBE,
0xB7, 0xAF, 0xA7, 0x9F, 0x98, 0x90, 0x88, 0x80, 0x79, 0x71, 0x6A, 0x63, 0x5C, 0x56, 0x4F, 0x49,
0x43, 0x3E, 0x39, 0x34, 0x2F, 0x2B, 0x27, 0x23, 0x20, 0x1D, 0x1A, 0x17, 0x14, 0x12, 0x10, 0x0E,
0x0D, 0x0B, 0x0A, 0x08, 0x07, 0x06, 0x05, 0x05, 0x04, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01,
0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B,
0x0D, 0x0E, 0x10, 0x12, 0x14, 0x17, 0x1A, 0x1D, 0x20, 0x23, 0x27, 0x2B, 0x2F, 0x34, 0x39, 0x3E,
0x43, 0x49, 0x4F, 0x56, 0x5C, 0x63, 0x6A, 0x71, 0x79, 0x80, 0x88, 0x90, 0x98, 0x9F, 0xA7, 0xAF,
0xB7, 0xBE, 0xC5, 0xCC, 0xD3, 0xDA, 0xE0, 0xE5, 0xEA, 0xEF, 0xF3, 0xF6, 0xF9, 0xFC, 0xFD, 0xFE,
0xFF, 0xFE, 0xFD, 0xFC, 0xF9, 0xF6, 0xF3, 0xEF, 0xEA, 0xE5, 0xE0, 0xDA, 0xD3, 0xCC, 0xC5, 0xBE,
0xB7, 0xAF, 0xA7, 0x9F, 0x98, 0x90, 0x88, 0x80, 0x79, 0x71, 0x6A, 0x63, 0x5C, 0x56, 0x4F, 0x49,
0x43, 0x3E, 0x39, 0x34, 0x2F, 0x2B, 0x27, 0x23, 0x20, 0x1D, 0x1A, 0x17, 0x14, 0x12, 0x10, 0x0E,
0x0D, 0x0B, 0x0A, 0x08, 0x07, 0x06, 0x05, 0x05, 0x04, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01,
};
// Init Slope: Gaussian slope from dark to background brightness
const uint8_t gau8_initSlope[cu16_TemplateLength * 2] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04,
0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x12, 0x13, 0x14, 0x16, 0x17, 0x19, 0x1A, 0x1C, 0x1D, 0x1F, 0x21, 0x22, 0x24, 0x26, 0x27, 0x29,
0x2B, 0x2D, 0x2E, 0x30, 0x32, 0x33, 0x35, 0x37, 0x38, 0x3A, 0x3B, 0x3D, 0x3E, 0x40, 0x41, 0x42,
0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4C, 0x4D, 0x4E, 0x4E, 0x4F, 0x4F, 0x50,
0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 0x54,
0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55,
};
// Offset Pulse: Gaussian slope from background illumination to full scale brightness
const uint8_t gau8_offsetSlope[cu16_TemplateLength] = {
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x58, 0x58, 0x59, 0x59, 0x5A, 0x5A, 0x5B, 0x5C, 0x5D,
0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x65, 0x66, 0x68, 0x6A, 0x6C, 0x6E, 0x70, 0x72, 0x74, 0x76,
0x79, 0x7B, 0x7E, 0x81, 0x84, 0x87, 0x8A, 0x8D, 0x90, 0x93, 0x97, 0x9A, 0x9D, 0xA1, 0xA4, 0xA8,
0xAB, 0xAF, 0xB2, 0xB6, 0xB9, 0xBC, 0xC0, 0xC3, 0xC6, 0xC9, 0xCC, 0xCF, 0xD2, 0xD5, 0xD8, 0xDA,
0xDD, 0xDF, 0xE1, 0xE3, 0xE5, 0xE7, 0xE9, 0xEB, 0xED, 0xEE, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
0xF6, 0xF7, 0xF8, 0xF9, 0xF9, 0xFA, 0xFA, 0xFB, 0xFB, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFD, 0xFD,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF,
};
// Full Slope: Gaussian slope from dark to full scale brightness
const uint8_t gau8_fullSlope[cu16_TemplateLength] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0C,
0x0D, 0x0F, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1D, 0x1F, 0x22, 0x25, 0x28, 0x2B, 0x2F, 0x32,
0x36, 0x3A, 0x3E, 0x42, 0x46, 0x4B, 0x50, 0x54, 0x59, 0x5E, 0x63, 0x68, 0x6D, 0x72, 0x77, 0x7C,
0x82, 0x87, 0x8C, 0x91, 0x96, 0x9B, 0xA0, 0xA5, 0xAA, 0xAE, 0xB3, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8,
0xCC, 0xCF, 0xD3, 0xD6, 0xD9, 0xDC, 0xDF, 0xE1, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, 0xEF, 0xF1,
0xF2, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF8, 0xF9, 0xFA, 0xFA, 0xFB, 0xFB, 0xFC, 0xFC, 0xFD, 0xFD,
0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF,
};
}// clang-format on
@@ -0,0 +1,5 @@
idf_component_register(
SRC_DIRS "."
INCLUDE_DIRS "."
REQUIRES cmock utils
)
+15
View File
@@ -0,0 +1,15 @@
/**
* @file testUtils.cpp
* @author Gustice
* @brief Hardware bound unit-tests
*
* @copyright Copyright (c) 2024
*/
#include "testable.h"
#include "unity.h"
#include <limits.h>
TEST_CASE("No relevant test case", "[utils]") {
TEST_ASSERT_EQUAL(0, 0);
}
@@ -0,0 +1 @@
unitTestsFrame()
@@ -0,0 +1,59 @@
/**
* @file TestableUTest.cpp
* @author Gustice
* @brief
*
* @copyright Copyright (c) 2024
*/
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "utils.hpp"
#include "doctest/doctest.h"
#include <iostream>
namespace SingleActiveTests {
class Foo : public SingleActive<Foo> {
public:
int test{};
Foo() : SingleActive() {}
};
template <>
bool Foo::SingleActive<Foo>::active{};
class Bar : public SingleActive<Bar> {
public:
int test{};
Bar() : SingleActive() {}
};
template <>
bool Bar::SingleActive<Bar>::active{};
TEST_CASE("Sweet path") {
{ // first instances
Foo dut;
} // destruction
{ // second instances
Foo dut;
} // destruction
{ // third instance
Foo dut1;
Bar dut2; // First instance of second class
} // destruction
CHECK(true);
}
TEST_CASE("Too many instances") {
{
Foo dut1;
Foo *pDut2;
CHECK_THROWS_WITH(pDut2 = new Foo, "Instance already active");
}
CHECK(true);
}
}