From e6b35e1467c6b4ddca398dcdf0afd097f2d18a30 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 17 Apr 2023 20:18:41 -0700 Subject: [PATCH 01/85] Initial changes and separate handle branch --- VortexEngine/src/Infrared/IRConfig.h | 2 +- VortexEngine/src/Leds/LedTypes.h | 26 ++++--- VortexEngine/src/Leds/Leds.cpp | 27 +++++++ .../src/Menus/MenuList/ColorSelect.cpp | 22 +++--- .../src/Menus/MenuList/FactoryReset.cpp | 3 +- VortexEngine/src/Menus/Menus.cpp | 12 +-- VortexEngine/src/Modes/DefaultModes.cpp | 74 +------------------ VortexEngine/src/Storage/Storage.cpp | 24 +++++- VortexEngine/src/Time/Timings.h | 4 + VortexEngine/src/VortexConfig.h | 18 ++--- 10 files changed, 103 insertions(+), 109 deletions(-) diff --git a/VortexEngine/src/Infrared/IRConfig.h b/VortexEngine/src/Infrared/IRConfig.h index 0d0fa4b9fc..2555f67ac1 100644 --- a/VortexEngine/src/Infrared/IRConfig.h +++ b/VortexEngine/src/Infrared/IRConfig.h @@ -5,7 +5,7 @@ // // Whether to enable the IR system as a whole // -#define IR_ENABLE 0 +#define IR_ENABLE 1 #define IR_TEST_BLOCK_SIZE 16 diff --git a/VortexEngine/src/Leds/LedTypes.h b/VortexEngine/src/Leds/LedTypes.h index a6ec1c79bb..15ae8e00df 100644 --- a/VortexEngine/src/Leds/LedTypes.h +++ b/VortexEngine/src/Leds/LedTypes.h @@ -16,13 +16,6 @@ enum LedPos : uint8_t LED_0 = LED_FIRST, LED_1, LED_2, - LED_3, - LED_4, - LED_5, - LED_6, - LED_7, - LED_8, - LED_9, // the number of entries above LED_COUNT, @@ -31,6 +24,10 @@ enum LedPos : uint8_t LED_LAST = (LED_COUNT - 1) }; +// some helpers for microlight code +#define LED_TIP LED_0 +#define LED_TOP LED_1 + enum Pair : uint8_t { PAIR_FIRST = 0, @@ -38,14 +35,23 @@ enum Pair : uint8_t // one pair for each pair of leds, adjust this to be 2x the LED_COUNT PAIR_0 = PAIR_FIRST, PAIR_1, - PAIR_2, - PAIR_3, - PAIR_4, PAIR_COUNT, PAIR_LAST = (PAIR_COUNT - 1), }; +// backwards compatibility for multi led patterns +#define LED_3 LED_0 +#define LED_4 LED_1 +#define LED_5 LED_2 +#define LED_6 LED_0 +#define LED_7 LED_1 +#define LED_8 LED_2 +#define LED_9 LED_0 +#define PAIR_2 PAIR_0 +#define PAIR_3 PAIR_1 +#define PAIR_4 PAIR_0 + // check if an led is even or odd #define isEven(pos) ((pos % 2) == 0) #define isOdd(pos) ((pos % 2) != 0) diff --git a/VortexEngine/src/Leds/Leds.cpp b/VortexEngine/src/Leds/Leds.cpp index d8901999d0..7ecbcc76b4 100644 --- a/VortexEngine/src/Leds/Leds.cpp +++ b/VortexEngine/src/Leds/Leds.cpp @@ -12,6 +12,21 @@ #include "../../VortexLib/VortexLib.h" #endif +#ifdef VORTEX_ARDUINO +#include +#include + +#define LED_DATA_PIN 4 +#define CLOCK_PIN 3 + +// TODO: remove this and just set the pins to INPUT +// onboard LED on adafruit +#include +#define POWER_LED_PIN 7 +#define POWER_LED_CLK 8 +Adafruit_DotStar onboardLED(1, POWER_LED_PIN, POWER_LED_CLK, DOTSTAR_BGR); +#endif + // array of led color values RGBColor Leds::m_ledColors[LED_COUNT] = { RGB_OFF }; // global brightness @@ -19,6 +34,15 @@ uint32_t Leds::m_brightness = DEFAULT_BRIGHTNESS; bool Leds::init() { +#ifdef VORTEX_ARDUINO + // setup leds on data pin 4 + FastLED.addLeds((CRGB *)m_ledColors, LED_COUNT); + // get screwed fastled, don't throttle us! + FastLED.setMaxRefreshRate(0, false); + // clear the onboard led so it displays nothing + onboardLED.begin(); + onboardLED.show(); +#endif #ifdef VORTEX_LIB Vortex::vcallbacks()->ledsInit(m_ledColors, LED_COUNT); #endif @@ -233,6 +257,9 @@ void Leds::breathIndexVal(LedPos target, uint32_t hue, uint32_t variance, uint32 void Leds::update() { +#ifdef VORTEX_ARDUINO + FastLED.show(m_brightness); +#endif #ifdef VORTEX_LIB Vortex::vcallbacks()->ledsShow(); #endif diff --git a/VortexEngine/src/Menus/MenuList/ColorSelect.cpp b/VortexEngine/src/Menus/MenuList/ColorSelect.cpp index db4b98aa3c..48f1d7947a 100644 --- a/VortexEngine/src/Menus/MenuList/ColorSelect.cpp +++ b/VortexEngine/src/Menus/MenuList/ColorSelect.cpp @@ -197,13 +197,14 @@ void ColorSelect::showSlotSelection() if (g_pButton->isPressed() && g_pButton->holdDuration() >= DELETE_THRESHOLD_TICKS) { if ((g_pButton->holdDuration() % (DELETE_CYCLE_TICKS * 2)) > DELETE_CYCLE_TICKS) { // breath red instead of white blink - Leds::breathIndex(LED_COUNT, 0, g_pButton->holdDuration()); + Leds::breathIndex(LED_0, 0, g_pButton->holdDuration()); return; } } // just render that led Leds::clearAll(); - Leds::blinkIndex(LED_COUNT, Time::getCurtime(), 150, 650, m_colorset[m_curSelection]); + Leds::blinkIndex(LED_0, Time::getCurtime(), 150, 650, m_colorset[m_curSelection]); + Leds::setIndex(LED_1, g_pButton->isPressed() ? RGB_OFF : 0x050505); } // exit index is num colors unless we have less than 8 colors uint8_t exitIndex = m_colorset.numColors(); @@ -220,7 +221,7 @@ void ColorSelect::showSlotSelection() } // selecting the exit if (m_curSelection == exitIndex) { - showFullSet(LED_COUNT, Time::getCurtime(), 50, 100); + showFullSet(LED_0, Time::getCurtime(), 50, 100); } } @@ -230,8 +231,9 @@ void ColorSelect::showHueSelection1() showExit(); return; } - uint8_t hue1 = m_curSelection * (255 / 4); - Leds::breathIndex(LED_COUNT, hue1, (uint32_t)(Time::getCurtime() / 2) + 62, 22, 255, 180); + uint8_t hue = m_curSelection * (255 / 4); + Leds::breathIndex(LED_0, hue, (uint32_t)(Time::getCurtime() / 2), 22, 255, 180); + Leds::breathIndex(LED_1, hue, (uint32_t)(Time::getCurtime() / 2) + 125, 22, 255, 180); } void ColorSelect::showHueSelection2() @@ -250,8 +252,8 @@ void ColorSelect::showSatSelection() showExit(); return; } - - Leds::setIndex(LED_COUNT, HSVColor(m_newColor.hue, sats[m_curSelection], 255)); + Leds::setIndex(LED_0, HSVColor(m_newColor.hue, sats[m_curSelection], 255)); + Leds::breathIndexSat(LED_1, m_newColor.hue, (uint32_t)(Time::getCurtime() / 3), 100, 30, 30); } void ColorSelect::showValSelection() @@ -260,13 +262,15 @@ void ColorSelect::showValSelection() showExit(); return; } - Leds::setIndex(LED_COUNT, HSVColor(m_newColor.hue, m_newColor.sat, vals[m_curSelection])); + Leds::setIndex(LED_0, HSVColor(m_newColor.hue, m_newColor.sat, vals[m_curSelection])); + Leds::breathIndexVal(LED_1, m_newColor.hue, (uint32_t)(Time::getCurtime() / 3), 100, m_newColor.sat, 30); } void ColorSelect::showFullSet(LedPos target, uint64_t time, uint32_t offMs, uint32_t onMs) { Leds::clearAll(); if ((time % Time::msToTicks(offMs + onMs)) < Time::msToTicks(onMs)) { - Leds::setIndex(LED_COUNT, m_colorset.get(((time / Time::msToTicks(offMs + onMs))) % m_colorset.numColors())); + Leds::setIndex(target, m_colorset.get(((time / Time::msToTicks(offMs + onMs))) % m_colorset.numColors())); } + Leds::setIndex(LED_1, 0x001000); } diff --git a/VortexEngine/src/Menus/MenuList/FactoryReset.cpp b/VortexEngine/src/Menus/MenuList/FactoryReset.cpp index c6b05c99cf..ff5bebdf2e 100644 --- a/VortexEngine/src/Menus/MenuList/FactoryReset.cpp +++ b/VortexEngine/src/Menus/MenuList/FactoryReset.cpp @@ -107,5 +107,6 @@ void FactoryReset::showReset() int8_t sat = (int8_t)(2.5 * progress); Leds::clearAll(); - Leds::blinkAll(Time::getCurtime(), offMs, onMs, HSVColor(0, 255 - sat, 180)); + Leds::blinkIndex(LED_0, Time::getCurtime(), offMs, onMs, HSVColor(0, 255 - sat, 180)); + Leds::blinkIndex(LED_1, Time::getCurtime(), offMs, onMs, RGB_BLANK); } diff --git a/VortexEngine/src/Menus/Menus.cpp b/VortexEngine/src/Menus/Menus.cpp index d9e5213ea8..7a1c58d5e4 100644 --- a/VortexEngine/src/Menus/Menus.cpp +++ b/VortexEngine/src/Menus/Menus.cpp @@ -53,13 +53,13 @@ Menu *initMenu(const RGBColor &col) { return new T(col); } const MenuEntry menuList[] = { // ========================= // Default menu setup: - ENTRY(Randomizer, RGB_DIM_WHITE1), // 0 - ENTRY(ColorSelect, RGB_ORANGE), // 1 - ENTRY(PatternSelect, RGB_BLUE), // 2 + ENTRY(Randomizer, 0x101010), // 0 (dim white) + ENTRY(ColorSelect, 0x401200), // 1 (dim orange) + ENTRY(PatternSelect, 0x000010), // 2 (dim blue) ENTRY(GlobalBrightness, RGB_YELLOW), // 3 - ENTRY(FactoryReset, RGB_RED), // 4 - ENTRY(ModeSharing, RGB_TEAL), // 5 - ENTRY(EditorConnection, RGB_PURPLE), // 6 + ENTRY(FactoryReset, 0x100000), // 4 (dim red) + ENTRY(ModeSharing, RGB_TEAL), // 5 + ENTRY(EditorConnection, RGB_PURPLE), // 6 }; // the number of menus in the above array diff --git a/VortexEngine/src/Modes/DefaultModes.cpp b/VortexEngine/src/Modes/DefaultModes.cpp index 8ed36d7c2c..dd8fea98ce 100644 --- a/VortexEngine/src/Modes/DefaultModes.cpp +++ b/VortexEngine/src/Modes/DefaultModes.cpp @@ -6,7 +6,7 @@ // the gloveset upon factory reset const default_mode_entry default_modes[] = { { - PATTERN_DOPS, 5, { + PATTERN_STROBE, 5, { RGB_RED, RGB_GREEN, RGB_BLUE, @@ -24,20 +24,7 @@ const default_mode_entry default_modes[] = { } }, { - PATTERN_WARPWORM, 2, { - RGB_GREEN, - 0x26004B, - } - }, - { - PATTERN_PULSISH, 3, { - 0x00AB55, - 0x8D1C55, - 0x00001C - } - }, - { - PATTERN_ZIGZAG, 6, { + PATTERN_BRACKETS, 6, { RGB_OFF, 0x56D400, 0x5500AB, @@ -46,25 +33,6 @@ const default_mode_entry default_modes[] = { 0x700000 } }, - { - PATTERN_STROBE, 8, { - 0xD4002B, - RGB_OFF, - 0x0056AA, - RGB_OFF, - 0x8E711C, - RGB_OFF, - 0x0056AA, - RGB_OFF - } - }, - { - PATTERN_SNOWBALL, 3, { - 0x170600, - 0x00840A, - 0x12002A - } - }, { PATTERN_ULTRADOPS, 8, { 0x1C0000, @@ -77,44 +45,6 @@ const default_mode_entry default_modes[] = { 0x13000A } }, - { - PATTERN_MATERIA, 4, { - 0xAA0055, - 0x7070C5, - 0x0A0013, - 0x1C8E55, - } - }, - { - PATTERN_VORTEXWIPE, 8, { - RGB_RED, - 0x00001C, - 0x00001C, - 0x00001C, - 0x00001C, - 0x00001C, - 0x00001C, - 0x00001C, - } - }, - { - PATTERN_GHOSTCRUSH, 7, { - 0x26004B, - RGB_OFF, - RGB_GREEN, - RGB_WHITE, - RGB_GREEN, - RGB_OFF, - 0x26004B, - } - }, - { - PATTERN_VORTEXWIPE, 3, { - 0x00AB55, - 0x7F0081, - 0xAA381C, - } - }, { PATTERN_COMPLEMENTARY_BLEND, 3, { RGB_RED, diff --git a/VortexEngine/src/Storage/Storage.cpp b/VortexEngine/src/Storage/Storage.cpp index 5a34562a20..c6bac7e210 100644 --- a/VortexEngine/src/Storage/Storage.cpp +++ b/VortexEngine/src/Storage/Storage.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "../VortexConfig.h" #include "../Memory/Memory.h" @@ -15,7 +16,14 @@ #endif // only arduino needs const I guess? -static const uint8_t _storagedata[STORAGE_SIZE] = { }; +__attribute__((__aligned__(256))) +#ifndef VORTEX_ARDUINO +uint8_t _storagedata[(STORAGE_SIZE+255)/256*256] = { }; +#else +// only arduino needs const I guess? +const uint8_t _storagedata[(STORAGE_SIZE+255)/256*256] = { }; +#endif +FlashClass storage(_storagedata, STORAGE_SIZE); uint32_t Storage::m_lastSaveSize = 0; @@ -49,6 +57,12 @@ bool Storage::write(ByteStream &buffer) ERROR_LOG("Buffer too big for storage space"); return false; } + // clear existing storage, this is necessary even if it's empty + storage.erase(); + // set the last save size + m_lastSaveSize = buffer.size(); + // write out the buffer to storage + storage.write(_storagedata, buffer.rawData(), buffer.rawSize()); DEBUG_LOGF("Wrote %u bytes to storage (max: %u)", m_lastSaveSize, STORAGE_SIZE); return true; } @@ -61,6 +75,14 @@ bool Storage::read(ByteStream &buffer) ERROR_LOGF("Could not initialize buffer with %u bytes", STORAGE_SIZE); return false; } + // read directly into the rawdata of the byte array, this will + // include the crc, size, flags and entire buffer of data + storage.read(buffer.rawData()); + m_lastSaveSize = buffer.size(); + // immediately check the CRC + if (!buffer.checkCRC()) { + return false; + } DEBUG_LOGF("Loaded savedata (Size: %u)", m_lastSaveSize); return true; } diff --git a/VortexEngine/src/Time/Timings.h b/VortexEngine/src/Time/Timings.h index ca324383b2..ca5da1bbc9 100644 --- a/VortexEngine/src/Time/Timings.h +++ b/VortexEngine/src/Time/Timings.h @@ -14,6 +14,10 @@ // how long after startup before the button starts responding #define IGNORE_BUTTON_TICKS Time::msToTicks(IGNORE_BUTTON_TIME) +// the number of ticks to enter sleep mode +#define SLEEP_ENTER_THRESHOLD_TICKS Time::msToTicks(SLEEP_TRIGGER_TIME) +#define SLEEP_WINDOW_THRESHOLD_TICKS Time::msToTicks(SLEEP_WINDOW_TIME) + // Color delete threshold (in milliseconds) #define DELETE_THRESHOLD_TICKS Time::msToTicks(COL_DELETE_THRESHOLD) // Color delete cycle time (in milliseconds) diff --git a/VortexEngine/src/VortexConfig.h b/VortexEngine/src/VortexConfig.h index 74c18988dc..75d0d93715 100644 --- a/VortexEngine/src/VortexConfig.h +++ b/VortexEngine/src/VortexConfig.h @@ -42,9 +42,9 @@ // Menu Trigger Threshold (in milliseconds) // -// How long the button must be held to trigger menu selection and -// begin blinking the first menu color -#define MENU_TRIGGER_TIME 1000 +// How long the button must be held to trigger ring menu and begin +// filling the first menu color +#define MENU_TRIGGER_TIME 300 // Short Click Threshold (in milliseconds) // @@ -63,8 +63,8 @@ // Color delete threshold (in milliseconds) // // How long you must hold down on a color in the color select menu to -// trigger the delete option to start flashing -#define COL_DELETE_THRESHOLD 2000 +// trigger the delete option to start flashing on the tip +#define COL_DELETE_THRESHOLD 500 // Color delete cycle time (in milliseconds) // @@ -375,21 +375,21 @@ // These are the various storage space constants of the vortex device // maximum storage space in bytes -#define MAX_STORAGE_SPACE 32000 +#define MAX_STORAGE_SPACE 262000 // the size of the compiled engine -#define ENGINE_SIZE 28000 +#define ENGINE_SIZE 70000 // the raw amount of available space #define RAW_AVAILABLE_SPACE (MAX_STORAGE_SPACE - ENGINE_SIZE) // usable flash space is one eighth of what we have left idk why I // just kept picking numbers till it worked -#define USABLE_SPACE (RAW_AVAILABLE_SPACE / 2) +#define USABLE_SPACE (RAW_AVAILABLE_SPACE / 8) // the space available for storing modes is the usable space rounded // down to nearest 4096 -#define STORAGE_SIZE (USABLE_SPACE - (USABLE_SPACE % 256)) +#define STORAGE_SIZE 4096 // (USABLE_SPACE - (USABLE_SPACE % 4096)) // =================================================================== // Test Framework configurations From 10f36b88873958cb9fd20f38f8932d18d8b8678e Mon Sep 17 00:00:00 2001 From: Unreal-Dan <72595612+Unreal-Dan@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:44:54 -0700 Subject: [PATCH 02/85] Mode Refactor Reintegration of the mode refactor from core --- VortexEngine/VortexLib/VortexLib.cpp | 211 ++- VortexEngine/VortexLib/VortexLib.h | 53 +- VortexEngine/src/Buttons/Buttons.cpp | 6 + VortexEngine/src/Colors/Colorset.cpp | 104 +- VortexEngine/src/Colors/Colorset.h | 4 +- VortexEngine/src/Leds/LedTypes.h | 40 +- VortexEngine/src/Menus/Menu.cpp | 76 +- VortexEngine/src/Menus/Menu.h | 3 + .../src/Menus/MenuList/ColorSelect.cpp | 146 +- VortexEngine/src/Menus/MenuList/ColorSelect.h | 5 +- .../src/Menus/MenuList/EditorConnection.cpp | 2 +- .../src/Menus/MenuList/FactoryReset.cpp | 12 +- .../src/Menus/MenuList/GlobalBrightness.cpp | 2 + .../src/Menus/MenuList/PatternSelect.cpp | 27 +- .../src/Menus/MenuList/Randomizer.cpp | 61 +- VortexEngine/src/Menus/MenuList/Randomizer.h | 5 +- VortexEngine/src/Menus/Menus.cpp | 7 +- VortexEngine/src/Modes/Mode.cpp | 1106 ++++++----- VortexEngine/src/Modes/Mode.h | 96 +- VortexEngine/src/Modes/Modes.cpp | 22 +- .../src/Patterns/Multi/CompoundPattern.cpp | 5 +- .../Multi/Sequencer/SequencedPattern.cpp | 4 +- VortexEngine/src/Patterns/Pattern.cpp | 26 +- VortexEngine/src/Patterns/Pattern.h | 17 +- VortexEngine/src/Patterns/Patterns.h | 8 +- .../src/Patterns/Single/SolidPattern.cpp | 14 - .../src/Patterns/Single/SolidPattern.h | 5 - VortexEngine/src/Serial/Serial.cpp | 16 +- VortexEngine/src/Serial/Serial.h | 3 - VortexEngine/src/Time/TimeControl.cpp | 15 +- VortexEngine/src/Time/TimeControl.h | 7 + VortexEngine/src/VortexConfig.h | 6 + VortexEngine/src/VortexEngine.cpp | 26 +- example_modes.json | 1623 ----------------- 34 files changed, 1332 insertions(+), 2431 deletions(-) delete mode 100644 example_modes.json diff --git a/VortexEngine/VortexLib/VortexLib.cpp b/VortexEngine/VortexLib/VortexLib.cpp index 4c3399bb6a..dc4628e77a 100644 --- a/VortexEngine/VortexLib/VortexLib.cpp +++ b/VortexEngine/VortexLib/VortexLib.cpp @@ -56,6 +56,7 @@ EMSCRIPTEN_BINDINGS(vortex_engine) { using namespace std; // static vortex data +char Vortex::m_lastCommand = 0; deque Vortex::m_undoBuffer; uint32_t Vortex::m_undoLimit = 0; uint32_t Vortex::m_undoIndex = 0; @@ -65,9 +66,11 @@ FILE *Vortex::m_consoleHandle = nullptr; #if LOG_TO_FILE == 1 FILE *Vortex::m_logHandle = nullptr; #endif -queue Vortex::m_buttonEventQueue; +deque Vortex::m_buttonEventQueue; bool Vortex::m_initialized = false; uint32_t Vortex::m_buttonsPressed = 0; +std::string Vortex::m_commandLog; +bool Vortex::m_commandLogEnabled = false; #ifdef _MSC_VER #include @@ -170,6 +173,101 @@ void Vortex::cleanup() m_initialized = false; } +void Vortex::handleRepeat(char c) +{ + if (!isdigit(c) || !m_lastCommand) { + return; + } + int repeatAmount = c - '0'; + char newc = 0; + // read the digits into the repeatAmount + while (1) { + newc = getchar(); + if (!isdigit(newc)) { + // stop once we reach a non digit + break; + } + // accumulate the digits into the repeat amount + repeatAmount = (repeatAmount * 10) + (newc - '0'); + } + if (repeatAmount > 0) { + // offset repeat amount by exactly 1 because it's already done + repeatAmount--; + } + // shove the last non-digit back into the stream + ungetc(newc, stdin); + DEBUG_LOGF("Repeating last command (%c) x%u times", m_lastCommand, repeatAmount); + m_commandLog += to_string(repeatAmount); + // repeat the last command that many times + while (repeatAmount > 0) { + doCommand(m_lastCommand); + repeatAmount--; + } +} + +// injects a command into the engine, the engine will parse one command +// per tick so multiple commands will be queued up +void Vortex::doCommand(char c) +{ + if (!isprint(c)) { + return; + } + + m_commandLog += c; + + switch (c) { + case 'a': + if (m_lastCommand != c) { + DEBUG_LOG("Injecting short click"); + } + Vortex::shortClick(); + break; + case 's': + if (m_lastCommand != c) { + DEBUG_LOG("Injecting long click"); + } + Vortex::longClick(); + break; + case 'd': + if (m_lastCommand != c) { + DEBUG_LOG("Injecting menu enter click"); + } + Vortex::menuEnterClick(); + break; + case 'q': + //case '\n': + if (m_lastCommand != c) { + DEBUG_LOG("Injecting quit click"); + } + Vortex::quitClick(); + break; + case 'f': + if (Vortex::isButtonPressed()) { + if (m_lastCommand != c) { + DEBUG_LOG("Injecting release"); + } + Vortex::releaseButton(); + } else { + if (m_lastCommand != c) { + DEBUG_LOG("Injecting press"); + } + Vortex::pressButton(); + } + break; + case 'w': + if (m_lastCommand != c) { + DEBUG_LOG("Injecting wait"); + } + Vortex::sendWait(); + break; + default: + handleRepeat(c); + // return instead of break because this isn't a command + return; + } + m_lastCommand = c; +} + bool Vortex::tick() { if (!m_initialized) { @@ -183,31 +281,11 @@ bool Vortex::tick() uint32_t numInputs = 0; ioctl(STDIN_FILENO, FIONREAD, &numInputs); // iterate the number of inputs on stdin and parse each letter - // into a command for the engine + // into a command for the engine, this will inject all of the commands + // that are available into the engine but that doesn't necessarily + // mean that the engine will do anything with them right away for (uint32_t i = 0; i < numInputs; ++i) { - switch (getchar()) { - case 'a': - Vortex::shortClick(); - break; - case 's': - Vortex::longClick(); - break; - case 'd': - Vortex::menuEnterClick(); - break; - case 'q': - Vortex::quitClick(); - break; - case 'f': - if (Vortex::isButtonPressed()) { - Vortex::releaseButton(); - } else { - Vortex::pressButton(); - } - break; - default: - break; - } + doCommand(getchar()); } #endif // tick the vortex engine forward @@ -215,25 +293,37 @@ bool Vortex::tick() return true; } + void Vortex::installCallbacks(VortexCallbacks *callbacks) { m_storedCallbacks = callbacks; } +void Vortex::setInstantTimestep(bool timestep) +{ + Time::setInstantTimestep(timestep); +} + // send various clicks void Vortex::shortClick(uint32_t buttonIndex) { - m_buttonEventQueue.push(VortexButtonEvent(buttonIndex, EVENT_SHORT_CLICK)); + m_buttonEventQueue.push_back(VortexButtonEvent(buttonIndex, EVENT_SHORT_CLICK)); } void Vortex::longClick(uint32_t buttonIndex) { - m_buttonEventQueue.push(VortexButtonEvent(buttonIndex, EVENT_LONG_CLICK)); + m_buttonEventQueue.push_back(VortexButtonEvent(buttonIndex, EVENT_LONG_CLICK)); } void Vortex::menuEnterClick(uint32_t buttonIndex) { - m_buttonEventQueue.push(VortexButtonEvent(buttonIndex, EVENT_MENU_ENTER_CLICK)); + m_buttonEventQueue.push_back(VortexButtonEvent(buttonIndex, EVENT_MENU_ENTER_CLICK)); +} + +void Vortex::sendWait(uint32_t amount) +{ + // reusing the button index as the wait amount + m_buttonEventQueue.push_back(VortexButtonEvent(amount, EVENT_WAIT)); } void Vortex::pressButton(uint32_t buttonIndex) @@ -267,7 +357,7 @@ bool Vortex::isButtonPressed(uint32_t buttonIndex) void Vortex::quitClick() { - m_buttonEventQueue.push(VortexButtonEvent(0, EVENT_QUIT_CLICK)); + m_buttonEventQueue.push_back(VortexButtonEvent(0, EVENT_QUIT_CLICK)); } void Vortex::IRDeliver(uint32_t timing) @@ -443,7 +533,7 @@ bool Vortex::setPattern(PatternID id, const PatternArgs *args, const Colorset *s if (!pMode) { return false; } - if (!pMode->setPattern(id, args, set)) { + if (!pMode->setPattern(id, LED_ANY, args, set)) { return false; } return !save || doSave(); @@ -470,7 +560,7 @@ string Vortex::getModeName() return patternToString(PATTERN_NONE); } if (pMode->isMultiLed()) { - return patternToString(getPatternID(LED_FIRST)); + return patternToString(pMode->getPatternID(LED_MULTI)); } // can't use isSampleSingleLed because that will compare the entire // pattern for differences in any single led pattern, we only care @@ -499,7 +589,7 @@ bool Vortex::setPatternAt(LedPos pos, PatternID id, if (!pMode) { return false; } - if (!pMode->setPatternAt(pos, id, args, set)) { + if (!pMode->setPattern(id, pos, args, set)) { return false; } return !save || doSave(); @@ -511,11 +601,7 @@ bool Vortex::getColorset(LedPos pos, Colorset &set) if (!pMode) { return false; } - const Colorset *pSet = pMode->getColorset(pos); - if (!pSet) { - return false; - } - set = *pSet; + set = pMode->getColorset(pos); return true; } @@ -525,11 +611,9 @@ bool Vortex::setColorset(LedPos pos, const Colorset &set, bool save) if (!pMode) { return false; } - Pattern *pat = pMode->getPattern(pos); - if (!pat) { + if (!pMode->setColorset(set, pos)) { return false; } - pat->setColorset(&set); return !save || doSave(); } @@ -564,6 +648,15 @@ bool Vortex::setPatternArgs(LedPos pos, PatternArgs &args, bool save) return !save || doSave(); } +bool Vortex::isCurModeMulti() +{ + Mode *pMode = Modes::curMode(); + if (!pMode) { + return false; + } + return pMode->isMultiLed(); +} + string Vortex::patternToString(PatternID id) { // I wish there was a way to do this automatically but it would be @@ -741,13 +834,17 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) } // pop the event from the front of the queue VortexButtonEvent buttonEvent = m_buttonEventQueue.front(); - m_buttonEventQueue.pop(); - // make sure the button that is targeted is actually a valid index - if (buttonEvent.target >= numButtons) { - return; + m_buttonEventQueue.pop_front(); + // the target button for this event (might be nullptr if event is just 'wait') + Button *pButton = nullptr; + if (buttonEvent.type != EVENT_WAIT) { + // make sure the button that is targeted is actually a valid index + if (buttonEvent.target >= numButtons) { + return; + } + // assigned the button based on the array index target + pButton = buttons + buttonEvent.target; } - // grab the target button for this event - Button *pButton = buttons + buttonEvent.target; // switch on the type of event and then run the operation switch (buttonEvent.type) { case EVENT_NONE: @@ -758,12 +855,14 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) pButton->m_shortClick = true; pButton->m_pressTime = Time::getCurtime(); pButton->m_holdDuration = 200; + DEBUG_LOG("Injecting short click"); break; case EVENT_LONG_CLICK: pButton->m_newRelease = true; pButton->m_longClick = true; pButton->m_pressTime = Time::getCurtime(); pButton->m_holdDuration = SHORT_CLICK_THRESHOLD_TICKS + 1; + DEBUG_LOG("Injecting long click"); break; case EVENT_MENU_ENTER_CLICK: // to do this we simply press the button and set the press time @@ -773,7 +872,22 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) pButton->m_pressTime = Time::getCurtime(); pButton->m_holdDuration = MENU_TRIGGER_THRESHOLD_TICKS + 1; pButton->m_isPressed = true; - m_buttonEventQueue.push(VortexButtonEvent(0, EVENT_RESET_CLICK)); + m_buttonEventQueue.push_front(VortexButtonEvent(0, EVENT_RESET_CLICK)); + DEBUG_LOG("Injecting menu enter click"); + break; + case EVENT_WAIT: + if (buttonEvent.target) { + // backup the event queue and clear it + deque backup; + std::swap(backup, m_buttonEventQueue); + // ticks the engine forward some number of ticks, the event queue is empty + // so the engine won't process any input events while doing this + for (uint32_t i = 0; i < buttonEvent.target; ++i) { + VortexEngine::tick(); + } + // then restore the event queue so that events are processed like normal + std::swap(backup, m_buttonEventQueue); + } break; case EVENT_TOGGLE_CLICK: if (pButton->isPressed()) { @@ -786,17 +900,20 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) pButton->m_newRelease = true; pButton->m_shortClick = (pButton->m_holdDuration <= SHORT_CLICK_THRESHOLD_TICKS); pButton->m_longClick = !pButton->m_shortClick; + DEBUG_LOG("Injecting release"); } else { pButton->m_buttonState = LOW; pButton->m_isPressed = true; pButton->m_releaseDuration = (uint32_t)(Time::getCurtime() - pButton->m_releaseTime); pButton->m_pressTime = Time::getCurtime(); pButton->m_newPress = true; + DEBUG_LOG("Injecting press"); } break; case EVENT_QUIT_CLICK: // just uninitialize so tick returns false m_initialized = false; + DEBUG_LOG("Injecting quit"); break; case EVENT_RESET_CLICK: pButton->m_isPressed = false; diff --git a/VortexEngine/VortexLib/VortexLib.h b/VortexEngine/VortexLib/VortexLib.h index 5ba2b2067e..8bdb623953 100644 --- a/VortexEngine/VortexLib/VortexLib.h +++ b/VortexEngine/VortexLib/VortexLib.h @@ -11,7 +11,7 @@ #include #include -#include +#include #include // ============================================================================ @@ -108,6 +108,9 @@ class Vortex // install a callback for digital reads (button press) static void installCallbacks(VortexCallbacks *callbacks); + // control whether the engine will tick instantly or not + static void setInstantTimestep(bool timestep); + // send various clicks static void shortClick(uint32_t buttonIndex = 0); static void longClick(uint32_t buttonIndex = 0); @@ -116,6 +119,10 @@ class Vortex static void releaseButton(uint32_t buttonIndex = 0); static bool isButtonPressed(uint32_t buttonIndex = 0); + // send a wait event, will let the engine run a tick if running in lockstep + // for example when running the testing system + static void sendWait(uint32_t amount = 0); + // get the current menu demo mode static Mode *getMenuDemoMode(); @@ -158,8 +165,8 @@ class Vortex // functions to operate on the current Mode static bool setPattern(PatternID id, const PatternArgs *args = nullptr, const Colorset *set = nullptr, bool save = true); - static PatternID getPatternID(LedPos pos = LED_FIRST); - static std::string getPatternName(LedPos pos = LED_FIRST); + static PatternID getPatternID(LedPos pos = LED_ANY); + static std::string getPatternName(LedPos pos = LED_ANY); static std::string getModeName(); static bool setPatternAt(LedPos pos, PatternID id, const PatternArgs *args = nullptr, const Colorset *set = nullptr, @@ -169,6 +176,9 @@ class Vortex static bool getPatternArgs(LedPos pos, PatternArgs &args); static bool setPatternArgs(LedPos pos, PatternArgs &args, bool save = true); + // whether the current mode is a multi-led pattern + static bool isCurModeMulti(); + // Helpers for converting pattern id and led id to string static std::string patternToString(PatternID id = PATTERN_NONE); static std::string ledToString(LedPos pos); @@ -191,14 +201,33 @@ class Vortex // access stored callbacks static VortexCallbacks *vcallbacks() { return m_storedCallbacks; } + // printing to log system + static void printlog(const char *file, const char *func, int line, const char *msg, va_list list); + + // injects a command into the engine, the engine will parse one command + // per tick so multiple commands will be queued up + static void doCommand(char c); + + // enable, fetch and clear the internal command log + static void enableCommandLog(bool enable) { m_commandLogEnabled = enable; } + static const std::string &getCommandLog() { return m_commandLog; } + static void clearCommandLog() { m_commandLog.clear(); } + +private: + // the last command to have been executed + static char m_lastCommand; + + // internal function to handle repeating commands + static void handleRepeat(char c); + + // so that the buttons class can call handleInputQueue + friend class Buttons; + // called by the engine right after all buttons are checked, this will process - // the input queue that is fed by the apis like shortClick() above and translate + // the input deque that is fed by the apis like shortClick() above and translate // those messages into actual button events by overwriting button data that tick static void handleInputQueue(Button *buttons, uint32_t numButtons); - // printing to log system - static void printlog(const char *file, const char *func, int line, const char *msg, va_list list); -private: // The various different button events that can be injected into vortex enum VortexButtonEventType { @@ -211,6 +240,8 @@ class Vortex EVENT_LONG_CLICK, // a press that is just long enough to open the ring menu EVENT_MENU_ENTER_CLICK, + // just wait around a tick (mainly used for testing) + EVENT_WAIT, // toggle the button (press or unpress it) EVENT_TOGGLE_CLICK, // quit the engine (not really a 'click') @@ -253,11 +284,15 @@ class Vortex #if LOG_TO_FILE == 1 static FILE *m_logHandle; #endif - // queue of button events - static std::queue m_buttonEventQueue; + // queue of button events, deque so can push to front and back + static std::deque m_buttonEventQueue; // whether initialized static bool m_initialized; // whether each button is pressed (bitflags) so technically this only // supports 32 buttons but idc whoever adds 33 buttons can fix this static uint32_t m_buttonsPressed; + // keeps a log of all the commands issued + static std::string m_commandLog; + // whether to record commands + static bool m_commandLogEnabled; }; diff --git a/VortexEngine/src/Buttons/Buttons.cpp b/VortexEngine/src/Buttons/Buttons.cpp index e0a7f69318..ec112a245c 100644 --- a/VortexEngine/src/Buttons/Buttons.cpp +++ b/VortexEngine/src/Buttons/Buttons.cpp @@ -31,6 +31,12 @@ void Buttons::cleanup() void Buttons::check() { +#ifndef VORTEX_LIB + // on real devices ignore button presses at startup + if (Time::getCurtime() <= IGNORE_BUTTON_TICKS) { + return; + } +#endif // would iterate all buttons and check them here // but there's only one button so for (uint32_t i = 0; i < NUM_BUTTONS; ++i) { diff --git a/VortexEngine/src/Colors/Colorset.cpp b/VortexEngine/src/Colors/Colorset.cpp index 4694029049..43224189d9 100644 --- a/VortexEngine/src/Colors/Colorset.cpp +++ b/VortexEngine/src/Colors/Colorset.cpp @@ -110,6 +110,11 @@ void Colorset::clear() resetIndex(); } +bool Colorset::equals(const Colorset &set) const +{ + return equals(&set); +} + bool Colorset::equals(const Colorset *set) const { if (!set) { @@ -159,43 +164,44 @@ bool Colorset::addColorHSV(uint8_t hue, uint8_t sat, uint8_t val) return addColor(HSVColor(hue, sat, val)); } -void Colorset::addColorWithValueStyle(Random &ctx, uint8_t hue, uint8_t sat, uint8_t valStyle, uint8_t numColors, uint8_t index) +void Colorset::addColorWithValueStyle(Random &ctx, uint8_t hue, uint8_t sat, ValueStyle valStyle, uint8_t numColors) { - if (numColors != 1) { - switch (valStyle) { - default: - case VAL_STYLE_RANDOM: + if (numColors == 1) { + addColorHSV(hue, sat, ctx.next8(16, 255)); + return; + } + switch (valStyle) { + default: + case VAL_STYLE_RANDOM: + addColorHSV(hue, sat, 85 * ctx.next8(1, 4)); + break; + case VAL_STYLE_LOW_FIRST_COLOR: + if (m_numColors == 0) { + addColorHSV(hue, sat, ctx.next8(0, 86)); + } else { addColorHSV(hue, sat, 85 * ctx.next8(1, 4)); - break; - case VAL_STYLE_LOW_FIRST_COLOR: - if (index == 0) { - addColorHSV(hue, sat, ctx.next8(0, 86)); - } else { - addColorHSV(hue, sat, 85 * ctx.next8(1, 4)); - } - break; - case VAL_STYLE_HIGH_FIRST_COLOR: - if (index == 0) { - addColorHSV(hue, sat, 255); - } else { - addColorHSV(hue, sat, ctx.next8(0, 86)); - } - break; - case VAL_STYLE_ALTERNATING: - if (index % 2 == 0) { - addColorHSV(hue, sat, 255); - } else { - addColorHSV(hue, sat, 85); - } - break; - case VAL_STYLE_ASCENDING: - addColorHSV(hue, sat, index * (256 / numColors)); - break; - case VAL_STYLE_DESCENDING: - addColorHSV(hue, sat, 255 - (index * (256 / numColors))); } - } else { - addColorHSV(hue, sat, ctx.next8(16, 255)); + break; + case VAL_STYLE_HIGH_FIRST_COLOR: + if (m_numColors == 0) { + addColorHSV(hue, sat, 255); + } else { + addColorHSV(hue, sat, ctx.next8(0, 86)); + } + break; + case VAL_STYLE_ALTERNATING: + if (m_numColors % 2 == 0) { + addColorHSV(hue, sat, 255); + } else { + addColorHSV(hue, sat, 85); + } + break; + case VAL_STYLE_ASCENDING: + addColorHSV(hue, sat, m_numColors * (256 / numColors)); + break; + case VAL_STYLE_DESCENDING: + addColorHSV(hue, sat, 255 - (m_numColors * (256 / numColors))); + break; } } @@ -223,7 +229,7 @@ void Colorset::randomize(Random &ctx, uint32_t numColors) } ValueStyle valStyle = (ValueStyle)ctx.next8(0, VAL_STYLE_COUNT); for (uint32_t i = 0; i < numColors; ++i) { - addColorWithValueStyle(ctx, ctx.next8(), ctx.next8(), valStyle, numColors, i); + addColorWithValueStyle(ctx, ctx.next8(), ctx.next8(), valStyle, numColors); } } @@ -240,7 +246,7 @@ void Colorset::randomizeColorTheory(Random &ctx, uint32_t numColors) ValueStyle valStyle = (ValueStyle)ctx.next8(0, VAL_STYLE_COUNT); for (uint32_t i = 0; i < numColors; i++) { uint8_t nextHue = (randomizedHue + (i * colorGap)) % 256; - addColorWithValueStyle(ctx, nextHue, 255, valStyle, numColors, i); + addColorWithValueStyle(ctx, nextHue, 255, valStyle, numColors); } } @@ -255,7 +261,7 @@ void Colorset::randomizeMonochromatic(Random &ctx, uint32_t numColors) ValueStyle valStyle = (ValueStyle)ctx.next8(0, VAL_STYLE_COUNT); for (uint32_t i = 0; i < numColors; i++) { uint8_t decrement = 255 - (i * (256 / numColors)); - addColorWithValueStyle(ctx, randomizedHue, decrement, valStyle, numColors, i); + addColorWithValueStyle(ctx, randomizedHue, decrement, valStyle, numColors); } } @@ -263,14 +269,14 @@ void Colorset::randomizeMonochromatic(Random &ctx, uint32_t numColors) void Colorset::randomizeDoubleSplitComplimentary(Random &ctx) { clear(); - uint8_t randomizedHue = ctx.next8(); - uint8_t splitComplimentaryGap = ctx.next8(1, 64); + uint8_t rHue = ctx.next8(); + uint8_t splitGap = ctx.next8(1, 64); ValueStyle valStyle = (ValueStyle)ctx.next8(0, VAL_STYLE_COUNT); - addColorWithValueStyle(ctx, (randomizedHue + splitComplimentaryGap + 128) % 256, 255, valStyle, 5, 0); - addColorWithValueStyle(ctx, (randomizedHue - splitComplimentaryGap) % 256, 255, valStyle, 5, 1); - addColorWithValueStyle(ctx, randomizedHue, 255, valStyle, 5, 2); - addColorWithValueStyle(ctx, (randomizedHue + splitComplimentaryGap) % 256, 255, valStyle, 5, 3); - addColorWithValueStyle(ctx, (randomizedHue - splitComplimentaryGap + 128) % 256, 255, valStyle, 5, 4); + addColorWithValueStyle(ctx, (rHue + splitGap + 128) % 256, 255, valStyle, 5); + addColorWithValueStyle(ctx, (rHue - splitGap) % 256, 255, valStyle, 5); + addColorWithValueStyle(ctx, rHue, 255, valStyle, 5); + addColorWithValueStyle(ctx, (rHue + splitGap) % 256, 255, valStyle, 5); + addColorWithValueStyle(ctx, (rHue - splitGap + 128) % 256, 255, valStyle, 5); } // create a set of 2 pairs of oposing colors @@ -280,10 +286,10 @@ void Colorset::randomizeTetradic(Random &ctx) uint8_t randomizedHue = ctx.next8(); uint8_t randomizedHue2 = ctx.next8(); ValueStyle valStyle = (ValueStyle)ctx.next8(0, VAL_STYLE_COUNT); - addColorWithValueStyle(ctx, randomizedHue, 255, valStyle, 4, 0); - addColorWithValueStyle(ctx, randomizedHue2, 255, valStyle, 4, 1); - addColorWithValueStyle(ctx, (randomizedHue + 128) % 256, 255, valStyle, 4, 2); - addColorWithValueStyle(ctx, (randomizedHue2 + 128) % 256, 255, valStyle, 4, 3); + addColorWithValueStyle(ctx, randomizedHue, 255, valStyle, 4); + addColorWithValueStyle(ctx, randomizedHue2, 255, valStyle, 4); + addColorWithValueStyle(ctx, (randomizedHue + 128) % 256, 255, valStyle, 4); + addColorWithValueStyle(ctx, (randomizedHue2 + 128) % 256, 255, valStyle, 4); } void Colorset::randomizeEvenlySpaced(Random &ctx, uint32_t spaces) @@ -296,7 +302,7 @@ void Colorset::randomizeEvenlySpaced(Random &ctx, uint32_t spaces) ValueStyle valStyle = (ValueStyle)ctx.next8(0, VAL_STYLE_COUNT); for (uint32_t i = 0; i < spaces; i++) { uint8_t nextHue = (randomizedHue + (256 / spaces) * i) % 256; - addColorWithValueStyle(ctx, nextHue, 255, valStyle, spaces, i); + addColorWithValueStyle(ctx, nextHue, 255, valStyle, spaces); } } diff --git a/VortexEngine/src/Colors/Colorset.h b/VortexEngine/src/Colors/Colorset.h index bcef024132..54e908d0cb 100644 --- a/VortexEngine/src/Colors/Colorset.h +++ b/VortexEngine/src/Colors/Colorset.h @@ -40,6 +40,7 @@ class Colorset void clear(); // pointer comparison + bool equals(const Colorset &set) const; bool equals(const Colorset *set) const; // index operator to access color index @@ -66,7 +67,8 @@ class Colorset // add a single color bool addColor(RGBColor col); bool addColorHSV(uint8_t hue, uint8_t sat, uint8_t val); - void addColorWithValueStyle(Random &ctx, uint8_t hue, uint8_t sat, uint8_t valStyle, uint8_t numColors, uint8_t index); + void addColorWithValueStyle(Random &ctx, uint8_t hue, uint8_t sat, + ValueStyle valStyle, uint8_t numColors); void removeColor(uint32_t index); // randomize a colorset with a specific number of colors with diff --git a/VortexEngine/src/Leds/LedTypes.h b/VortexEngine/src/Leds/LedTypes.h index 15ae8e00df..50ca8d5614 100644 --- a/VortexEngine/src/Leds/LedTypes.h +++ b/VortexEngine/src/Leds/LedTypes.h @@ -21,7 +21,39 @@ enum LedPos : uint8_t LED_COUNT, // the last LED index - LED_LAST = (LED_COUNT - 1) + LED_LAST = (LED_COUNT - 1), + + // target all leds (multi and single) + // When fetching this the same as LED_ANY + // When setting this will set all of the leds + LED_ALL = LED_COUNT, + + // target the multi led slot + // + // When fetching this will return the multi led slot + // When setting this will set the multi led slot + LED_MULTI = (LED_COUNT + 1), + + // target all single led slots + // + // When fetching this will return the first single led slot + // When setting this will set all single led slots + LED_ALL_SINGLE = (LED_COUNT + 2), + + // Target the 'effective' led slot (any slot) + // + // When fetching this will: + // 1. return the multi led slot if it exists + // 2. otherwise the first single led slot + // + // When setting this will: + // 1. if setting single led pattern will set all + // 2. if setting multi led pattern will set multi + LED_ANY = (LED_COUNT + 3), + + // other customs? + // LED_EVENS = (LED_COUNT + 2), + // LED_ODDS = (LED_COUNT + 3), }; // some helpers for microlight code @@ -76,7 +108,7 @@ typedef uint64_t LedMap; // check if a map is purely just 1 led or not #define MAP_IS_ONE_LED(map) (map && !(map & (map-1))) -// foreach led macro +// foreach led macro (only iterates singles) #define MAP_FOREACH_LED(map) for (LedPos pos = mapGetFirstLed(map); pos < LED_COUNT; pos = mapGetNextLed(map, pos)) // convert a map to the first Led position in the map @@ -114,8 +146,8 @@ inline LedPos mapGetNextLed(LedMap map, LedPos pos) #define MAP_INVERSE(map) ((~map) & MAP_LED_ALL) // macro for all evens and odds -#define MAP_PAIR_EVENS (MAP_PAIR_EVEN(PAIR_0) | MAP_PAIR_EVEN(PAIR_1) | MAP_PAIR_EVEN(PAIR_2) | MAP_PAIR_EVEN(PAIR_3) | MAP_PAIR_EVEN(PAIR_4)) -#define MAP_PAIR_ODDS (MAP_PAIR_ODD(PAIR_0) | MAP_PAIR_ODD(PAIR_1) | MAP_PAIR_ODD(PAIR_2) | MAP_PAIR_ODD(PAIR_3) | MAP_PAIR_ODD(PAIR_4)) +#define MAP_PAIR_EVENS (((1 << LED_COUNT) - 1) & 0xAAAAAAAA) +#define MAP_PAIR_ODDS (((1 << LED_COUNT) - 1) & 0x55555555) // Some preset bitmaps for pair groupings #define MAP_PAIR_ODD_EVENS (MAP_PAIR_EVEN(PAIR_0) | MAP_PAIR_EVEN(PAIR_2) | MAP_PAIR_EVEN(PAIR_4)) diff --git a/VortexEngine/src/Menus/Menu.cpp b/VortexEngine/src/Menus/Menu.cpp index e536f359f8..34985cd821 100644 --- a/VortexEngine/src/Menus/Menu.cpp +++ b/VortexEngine/src/Menus/Menu.cpp @@ -29,19 +29,16 @@ bool Menu::init() if (!m_pCurMode) { // if you enter a menu and there's no modes, it will add an empty one if (Modes::numModes() > 0) { - Leds::setAll(RGB_PURPLE); // some kind of serious error return false; } if (!Modes::addMode(PATTERN_BASIC, RGBColor(RGB_OFF))) { - Leds::setAll(RGB_YELLOW); // some kind of serious error return false; } // get the mode m_pCurMode = Modes::curMode(); if (!m_pCurMode) { - Leds::setAll(RGB_ORANGE); // serious error again return false; } @@ -72,35 +69,7 @@ Menu::MenuAction Menu::run() // every time the button is clicked, change the target led if (g_pButton->onShortClick()) { - // The target led can be 0 through LED_COUNT to represent any led or all leds - // modulo by LED_COUNT + 1 to include LED_COUNT (all) as a target - switch (m_targetLeds) { - case MAP_LED_ALL: - if (m_pCurMode->isMultiLed()) { - // do not allow multi led to select anything else - break; - } - m_targetLeds = MAP_LED(LED_FIRST); - break; - case MAP_LED(LED_LAST): - m_targetLeds = MAP_PAIR_EVENS; - break; - case MAP_PAIR_EVENS: - m_targetLeds = MAP_PAIR_ODDS; - break; - case MAP_PAIR_ODDS: - m_targetLeds = MAP_LED_ALL; - break; - default: // LED_FIRST through LED_LAST - // do not allow multi led to select anything else - if (m_pCurMode->isMultiLed()) { - m_targetLeds = MAP_LED_ALL; - break; - } - // iterate as normal - m_targetLeds = MAP_LED(((mapGetFirstLed(m_targetLeds) + 1) % (LED_COUNT + 1))); - break; - } + nextBulbSelection(); } // on a long press of the button, lock in the target led if (g_pButton->onLongClick()) { @@ -119,7 +88,12 @@ Menu::MenuAction Menu::run() void Menu::showBulbSelection() { Leds::clearAll(); - Leds::blinkMap(m_targetLeds, Time::getCurtime(), 250, 500, m_menuColor); + if (m_targetLeds == MAP_LED(LED_MULTI)) { + LedPos pos = (LedPos)((Time::getCurtime() / 30) % LED_COUNT); + Leds::blinkIndex(pos, Time::getCurtime() + (pos * 10), 50, 500, m_menuColor); + } else { + Leds::blinkMap(m_targetLeds, Time::getCurtime(), 250, 500, m_menuColor); + } // blink when selecting showSelect(); } @@ -145,6 +119,42 @@ void Menu::showExit() Leds::blinkAll(Time::getCurtime(), 250, 500, RGB_DARK_RED); } +void Menu::nextBulbSelection() +{ + // The target led can be 0 through LED_COUNT to represent any led or all leds + // modulo by LED_COUNT + 1 to include LED_COUNT (all) as a target + switch (m_targetLeds) { + case MAP_LED_ALL: + if (m_pCurMode->isMultiLed()) { + // do not allow multi led to select anything else + //break; + } + m_targetLeds = MAP_LED(LED_FIRST); + break; + case MAP_LED(LED_LAST): + m_targetLeds = MAP_PAIR_EVENS; + break; + case MAP_PAIR_EVENS: + m_targetLeds = MAP_PAIR_ODDS; + break; + case MAP_PAIR_ODDS: + m_targetLeds = MAP_LED(LED_MULTI); + break; + case MAP_LED(LED_MULTI): + m_targetLeds = MAP_LED_ALL; + break; + default: // LED_FIRST through LED_LAST + // do not allow multi led to select anything else + if (m_pCurMode->isMultiLed()) { + //m_targetLeds = MAP_LED_ALL; + //break; + } + // iterate as normal + m_targetLeds = MAP_LED(((mapGetFirstLed(m_targetLeds) + 1) % (LED_COUNT + 1))); + break; + } +} + void Menu::onShortClick() { } diff --git a/VortexEngine/src/Menus/Menu.h b/VortexEngine/src/Menus/Menu.h index 1ae237a296..012358b284 100644 --- a/VortexEngine/src/Menus/Menu.h +++ b/VortexEngine/src/Menus/Menu.h @@ -40,6 +40,9 @@ class Menu void showSelect(); void showExit(); + // iterate to next bulb selection + void nextBulbSelection(); + // the current mode that was selected Mode *m_pCurMode; // the color of this menu diff --git a/VortexEngine/src/Menus/MenuList/ColorSelect.cpp b/VortexEngine/src/Menus/MenuList/ColorSelect.cpp index 48f1d7947a..e7eabe76cb 100644 --- a/VortexEngine/src/Menus/MenuList/ColorSelect.cpp +++ b/VortexEngine/src/Menus/MenuList/ColorSelect.cpp @@ -37,6 +37,9 @@ bool ColorSelect::init() if (!Menu::init()) { return false; } + if (m_pCurMode->isMultiLed()) { + m_ledSelected = true; + } m_state = STATE_INIT; DEBUG_LOG("Entered color select"); return true; @@ -49,17 +52,25 @@ Menu::MenuAction ColorSelect::run() return result; } + // all states start with a blank slate + Leds::clearAll(); + switch (m_state) { case STATE_INIT: // this is separate from the init function because the target led // hasn't been chosen yet at the time of the init function running // where as this will run after the target led has been chosen and // we can fetch the correct colorset to work with - m_newColor.clear(); + //m_newColor.clear(); + m_newColor = HSVColor(0, 255, 255); m_curSelection = 0; m_targetSlot = 0; // grab the colorset from our selected target led - m_colorset = *m_pCurMode->getColorset(mapGetFirstLed(m_targetLeds)); + if (m_targetLeds == MAP_LED_ALL) { + m_colorset = m_pCurMode->getColorset(); + } else { + m_colorset = m_pCurMode->getColorset(mapGetFirstLed(m_targetLeds)); + } // move on to picking slot m_state = STATE_PICK_SLOT; break; @@ -67,16 +78,10 @@ Menu::MenuAction ColorSelect::run() showSlotSelection(); break; case STATE_PICK_HUE1: - showHueSelection1(); - break; case STATE_PICK_HUE2: - showHueSelection2(); - break; case STATE_PICK_SAT: - showSatSelection(); - break; case STATE_PICK_VAL: - showValSelection(); + showSelection(m_state); break; } @@ -136,17 +141,11 @@ void ColorSelect::onLongClick() // if we're targetting more than one led then screw // checking if the colorset has changed because it's // not worth the effort - if (!MAP_IS_ONE_LED(m_targetLeds)) { - leaveMenu(true); - return; - } - // if our selection is on the exit index then check if the - // colorset has been changed and save if necessary - Pattern *pat = m_pCurMode->getPattern(mapGetFirstLed(m_targetLeds)); - if (pat && !m_colorset.equals(pat->getColorset())) { - m_pCurMode->setColorsetAt(mapGetFirstLed(m_targetLeds), &m_colorset); + needsSave = (!MAP_IS_ONE_LED(m_targetLeds) || + !m_colorset.equals(m_pCurMode->getColorset(mapGetFirstLed(m_targetLeds)))); + if (needsSave) { + m_pCurMode->setColorsetMap(m_targetLeds, m_colorset); m_pCurMode->init(); - needsSave = true; } leaveMenu(needsSave); return; @@ -164,19 +163,15 @@ void ColorSelect::onLongClick() } // otherwise store the target slot continue onto the hue selection m_targetSlot = m_curSelection; - m_state = STATE_PICK_HUE1; break; case STATE_PICK_HUE1: m_newColor.hue = m_curSelection * (255 / 4); - m_state = STATE_PICK_HUE2; break; case STATE_PICK_HUE2: m_newColor.hue += m_curSelection * (255 / 16); - m_state = STATE_PICK_SAT; break; case STATE_PICK_SAT: m_newColor.sat = sats[m_curSelection]; - m_state = STATE_PICK_VAL; break; case STATE_PICK_VAL: m_newColor.val = vals[m_curSelection]; @@ -187,90 +182,91 @@ void ColorSelect::onLongClick() m_state = STATE_PICK_SLOT; return; } + m_state = (ColorSelectState)(m_state + 1); m_curSelection = 0; } void ColorSelect::showSlotSelection() { - // selecting an led regular - if (m_curSelection < m_colorset.numColors()) { - if (g_pButton->isPressed() && g_pButton->holdDuration() >= DELETE_THRESHOLD_TICKS) { - if ((g_pButton->holdDuration() % (DELETE_CYCLE_TICKS * 2)) > DELETE_CYCLE_TICKS) { - // breath red instead of white blink - Leds::breathIndex(LED_0, 0, g_pButton->holdDuration()); - return; - } - } - // just render that led - Leds::clearAll(); - Leds::blinkIndex(LED_0, Time::getCurtime(), 150, 650, m_colorset[m_curSelection]); - Leds::setIndex(LED_1, g_pButton->isPressed() ? RGB_OFF : 0x050505); - } - // exit index is num colors unless we have less than 8 colors uint8_t exitIndex = m_colorset.numColors(); - // otherwise check if we're on add-color or exit - if (m_colorset.numColors() < MAX_COLOR_SLOTS) { - // selecting an led to add a new color + uint32_t holdDur = g_pButton->holdDuration(); + + bool withinNumColors = m_curSelection < m_colorset.numColors(); + bool holdDurationCheck = g_pButton->isPressed() && holdDur >= DELETE_THRESHOLD_TICKS; + bool holdDurationModCheck = (holdDur % (DELETE_CYCLE_TICKS * 2)) > DELETE_CYCLE_TICKS; + + if (withinNumColors && holdDurationCheck && holdDurationModCheck) { + // breath red for delete slot + Leds::breathIndex(LED_COUNT, 0, holdDur); + } else if (withinNumColors) { + // blink the selected slot color + Leds::blinkIndex(LED_COUNT, Time::getCurtime(), 150, 650, m_colorset[m_curSelection]); + } else if (m_colorset.numColors() < MAX_COLOR_SLOTS) { if (m_curSelection == m_colorset.numColors()) { - // blinks to indicate this will be a new color added here - Leds::clearAll(); + // blink faster to indicate 'add' new color Leds::blinkAll(Time::getCurtime(), 100, 150, RGB_BLANK); } - // if less than 8 colors the exit index is the num colors plus 1 exitIndex = m_colorset.numColors() + 1; } - // selecting the exit + if (m_curSelection == exitIndex) { showFullSet(LED_0, Time::getCurtime(), 50, 100); } } -void ColorSelect::showHueSelection1() +void ColorSelect::showSelection(ColorSelectState mode) { if (m_curSelection >= 4) { showExit(); return; } - uint8_t hue = m_curSelection * (255 / 4); - Leds::breathIndex(LED_0, hue, (uint32_t)(Time::getCurtime() / 2), 22, 255, 180); - Leds::breathIndex(LED_1, hue, (uint32_t)(Time::getCurtime() / 2) + 125, 22, 255, 180); -} -void ColorSelect::showHueSelection2() -{ - if (m_curSelection >= 4) { - showExit(); - return; - } - uint8_t hue = m_newColor.hue + (m_curSelection * (255 / 16)); - Leds::setAll(HSVColor(hue, 255, 255)); -} + uint8_t hue = m_newColor.hue; + uint8_t sat = m_newColor.sat; + uint8_t val = 255; -void ColorSelect::showSatSelection() -{ - if (m_curSelection >= 4) { - showExit(); + switch (mode) { + default: + return; + case STATE_PICK_HUE1: + hue = m_curSelection * (255 / 4); + MAP_FOREACH_LED(MAP_PAIR_EVENS) { + Leds::breathIndex(pos, hue, (uint32_t)(Time::getCurtime() / 2), 22, 255, 180); + } + MAP_FOREACH_LED(MAP_PAIR_ODDS) { + Leds::breathIndex(pos, hue, (uint32_t)(Time::getCurtime() / 2) + 125, 22, 255, 180); + } return; + case STATE_PICK_HUE2: + hue += (m_curSelection * (255 / 16)); + break; + case STATE_PICK_SAT: + sat = sats[m_curSelection]; + break; + case STATE_PICK_VAL: + val = vals[m_curSelection]; + break; } - Leds::setIndex(LED_0, HSVColor(m_newColor.hue, sats[m_curSelection], 255)); - Leds::breathIndexSat(LED_1, m_newColor.hue, (uint32_t)(Time::getCurtime() / 3), 100, 30, 30); -} -void ColorSelect::showValSelection() -{ - if (m_curSelection >= 4) { - showExit(); - return; + Leds::setMap(MAP_PAIR_ODDS, HSVColor(hue, sat, val)); + uint8_t satt = (mode == STATE_PICK_SAT) ? m_newColor.sat : 30; + MAP_FOREACH_LED(MAP_PAIR_EVENS) { + Leds::breathIndex(pos, hue, (uint32_t)(Time::getCurtime() / 3), 100, satt, 30); } - Leds::setIndex(LED_0, HSVColor(m_newColor.hue, m_newColor.sat, vals[m_curSelection])); - Leds::breathIndexVal(LED_1, m_newColor.hue, (uint32_t)(Time::getCurtime() / 3), 100, m_newColor.sat, 30); } void ColorSelect::showFullSet(LedPos target, uint64_t time, uint32_t offMs, uint32_t onMs) { - Leds::clearAll(); + if (!m_colorset.numColors()) { + // wat do? + return; + } if ((time % Time::msToTicks(offMs + onMs)) < Time::msToTicks(onMs)) { - Leds::setIndex(target, m_colorset.get(((time / Time::msToTicks(offMs + onMs))) % m_colorset.numColors())); + int divisor = Time::msToTicks(offMs + onMs); + if (!divisor) { + divisor = 1; + } + Leds::setIndex(LED_COUNT, m_colorset.get(((time / divisor)) % m_colorset.numColors())); } Leds::setIndex(LED_1, 0x001000); } diff --git a/VortexEngine/src/Menus/MenuList/ColorSelect.h b/VortexEngine/src/Menus/MenuList/ColorSelect.h index 059e09c419..038f9a8934 100644 --- a/VortexEngine/src/Menus/MenuList/ColorSelect.h +++ b/VortexEngine/src/Menus/MenuList/ColorSelect.h @@ -31,10 +31,7 @@ class ColorSelect : public Menu }; void showSlotSelection(); - void showHueSelection1(); - void showHueSelection2(); - void showSatSelection(); - void showValSelection(); + void showSelection(ColorSelectState mode); void showFullSet(LedPos target, uint64_t time, uint32_t offMs, uint32_t onMs); // the options for saturations diff --git a/VortexEngine/src/Menus/MenuList/EditorConnection.cpp b/VortexEngine/src/Menus/MenuList/EditorConnection.cpp index f1bc8dae12..f89c6df42f 100644 --- a/VortexEngine/src/Menus/MenuList/EditorConnection.cpp +++ b/VortexEngine/src/Menus/MenuList/EditorConnection.cpp @@ -62,7 +62,7 @@ void EditorConnection::clearDemo() { Colorset set(RGB_BLANK); PatternArgs args(1, 0, 0); - m_demoMode.setPattern(PATTERN_BASIC, &args, &set); + m_demoMode.setPattern(PATTERN_BASIC, LED_ALL, &args, &set); m_demoMode.init(); } diff --git a/VortexEngine/src/Menus/MenuList/FactoryReset.cpp b/VortexEngine/src/Menus/MenuList/FactoryReset.cpp index ff5bebdf2e..123523d0d2 100644 --- a/VortexEngine/src/Menus/MenuList/FactoryReset.cpp +++ b/VortexEngine/src/Menus/MenuList/FactoryReset.cpp @@ -39,6 +39,12 @@ Menu::MenuAction FactoryReset::run() if (result != MENU_CONTINUE) { return result; } + // bypass led selection for fac reset if a multi was set on + // the current slot because it doesn't make sense to pick + if (m_pCurMode->isMultiLed()) { + m_ledSelected = true; + m_targetLeds = MAP_LED(LED_MULTI); + } showReset(); return MENU_CONTINUE; } @@ -64,7 +70,7 @@ void FactoryReset::onLongClick() // reset the target mode slot on the target led const default_mode_entry &def = default_modes[curModeIndex]; Colorset set(def.numColors, def.cols); - m_pCurMode->setPatternAt(m_targetLeds, def.patternID, nullptr, &set); + m_pCurMode->setPatternMap(m_targetLeds, def.patternID, nullptr, &set); // re-initialize the current mode m_pCurMode->init(); // save and leave the menu @@ -76,14 +82,12 @@ void FactoryReset::showReset() if (m_curSelection == 0) { Leds::clearAll(); Leds::blinkAll(Time::getCurtime(), 350, 350, RGB_BLANK); - DEBUG_LOGF("select 0"); return; } if (!g_pButton->isPressed()) { Leds::clearAll(); Leds::blinkAll(Time::getCurtime(), 150, 150, RGB_DIM_RED); - DEBUG_LOGF("not pressed"); return; } @@ -95,7 +99,7 @@ void FactoryReset::showReset() uint16_t progress = ((holdDur * 100) / FACTORY_RESET_THRESHOLD_TICKS); - DEBUG_LOGF("progress: %f %u", progress, ledProgress); + DEBUG_LOGF("progress: %f", progress); if (progress >= 100) { Leds::setAll(RGB_WHITE); diff --git a/VortexEngine/src/Menus/MenuList/GlobalBrightness.cpp b/VortexEngine/src/Menus/MenuList/GlobalBrightness.cpp index 9c69772972..c0aa1de509 100644 --- a/VortexEngine/src/Menus/MenuList/GlobalBrightness.cpp +++ b/VortexEngine/src/Menus/MenuList/GlobalBrightness.cpp @@ -20,6 +20,8 @@ bool GlobalBrightness::init() if (!Menu::init()) { return false; } + // bypass led selection + m_ledSelected = true; // would be nice if there was a more elegant way to do this for (uint8_t i = 0; i < NUM_BRIGHTNESS_OPTIONS; ++i) { if (m_brightnessOptions[i] == Leds::getBrightness()) { diff --git a/VortexEngine/src/Menus/MenuList/PatternSelect.cpp b/VortexEngine/src/Menus/MenuList/PatternSelect.cpp index a3c77bddd0..485d8c28b1 100644 --- a/VortexEngine/src/Menus/MenuList/PatternSelect.cpp +++ b/VortexEngine/src/Menus/MenuList/PatternSelect.cpp @@ -37,16 +37,33 @@ Menu::MenuAction PatternSelect::run() void PatternSelect::onShortClick() { - PatternID newID = (PatternID)(m_pCurMode->getPatternID(mapGetFirstLed(m_targetLeds)) + 1); + LedPos srcLed = LED_MULTI; + if (!m_pCurMode->isMultiLed()) { + srcLed = mapGetFirstLed(m_targetLeds); + } + PatternID newID = (PatternID)(m_pCurMode->getPatternID(srcLed) + 1); if (newID == PATTERN_SOLID) { ++newID; } - if (newID > PATTERN_SINGLE_LAST) { - newID = PATTERN_SINGLE_FIRST; + PatternID endList = PATTERN_SINGLE_LAST; + PatternID beginList = PATTERN_SINGLE_FIRST; + if (m_targetLeds == MAP_LED_ALL || m_targetLeds == MAP_LED(LED_MULTI)) { + endList = PATTERN_MULTI_LAST; + } + if (m_targetLeds == MAP_LED(LED_MULTI)) { + beginList = PATTERN_MULTI_FIRST; + } + if (newID > endList || newID < beginList) { + newID = beginList; + } + // set the new pattern id + if (isMultiLedPatternID(newID)) { + m_pCurMode->setPattern(newID); + } else { + m_pCurMode->setPatternMap(m_targetLeds, newID); } - // iterate the pattern forward on current mode - m_pCurMode->setPatternAt(m_targetLeds, newID); m_pCurMode->init(); + DEBUG_LOGF("Iterated to pattern id %d", newID); } void PatternSelect::onLongClick() diff --git a/VortexEngine/src/Menus/MenuList/Randomizer.cpp b/VortexEngine/src/Menus/MenuList/Randomizer.cpp index 8b890d215f..a0cffea912 100644 --- a/VortexEngine/src/Menus/MenuList/Randomizer.cpp +++ b/VortexEngine/src/Menus/MenuList/Randomizer.cpp @@ -30,20 +30,23 @@ bool Randomizer::init() return false; } + if (!m_pCurMode) { + return false; + } + + // grab the multi ld pattern colorset crc if it's present + if (m_pCurMode->hasMultiLed()) { + ByteStream ledData; + m_pCurMode->getColorset(LED_MULTI).serialize(ledData); + m_multiRandCtx.seed(ledData.recalcCRC()); + } + // initialize the randomseed of each led with the // CRC of the colorset on the respective LED for (LedPos l = LED_FIRST; l < LED_COUNT; ++l) { ByteStream ledData; - Pattern *pat = m_pCurMode->getPattern(m_pCurMode->isMultiLed() ? LED_FIRST : l); - if (!pat) { - continue; - } - Colorset *set = pat->getColorset(); - if (!set) { - continue; - } - set->serialize(ledData); - m_randCtx[l].seed(ledData.recalcCRC()); + m_pCurMode->getColorset(l).serialize(ledData); + m_singlesRandCtx[l].seed(ledData.recalcCRC()); } DEBUG_LOG("Entered randomizer"); @@ -58,6 +61,20 @@ Menu::MenuAction Randomizer::run() return result; } + // if they are trying to randomize a multi-led pattern just convert + // the pattern to all singles with the same colorset upon entry + if (m_pCurMode->isMultiLed() && m_targetLeds != MAP_LED(LED_MULTI)) { + // convert the pattern to a single led pattern, this will map + // all multi led patterns to single led patterns using modulo + // so no matter which multi-led pattern they have selected it + // will convert to a single led pattern of some kind + PatternID newID = (PatternID)((m_pCurMode->getPatternID() - PATTERN_MULTI_FIRST) % PATTERN_SINGLE_COUNT); + // solid sucks + if (newID == PATTERN_SOLID) ++newID; + m_pCurMode->setPattern(newID); + m_pCurMode->init(); + } + // display the randomized mode if (m_pCurMode) { m_pCurMode->play(); @@ -88,7 +105,7 @@ bool Randomizer::reRoll(LedPos pos) // colorset that will be filled with random colors Colorset randomSet; // grab local reference to the target random context - Random &ctx = m_randCtx[pos]; + Random &ctx = (pos < LED_COUNT) ? m_singlesRandCtx[pos] : m_multiRandCtx; // pick a random type of randomizer to use then use // the randomizer to generate a random colorset uint32_t randType = ctx.next(0, 9); @@ -122,23 +139,37 @@ bool Randomizer::reRoll(LedPos pos) randomSet.randomizeEvenlySpaced(ctx, 3); break; } + // the random range begin/end + PatternID rbegin = PATTERN_SINGLE_FIRST; + PatternID rend = PATTERN_SINGLE_LAST; + // is the multi led present in the target led map + if (m_targetLeds & MAP_LED(LED_MULTI)) { + // if so enable that one + rend = PATTERN_MULTI_LAST; + if (m_targetLeds == MAP_LED(LED_MULTI)) { + rbegin = PATTERN_MULTI_FIRST; + } + } // create a random pattern ID from all patterns PatternID newPat; do { // continuously re-randomize the pattern so we don't get undesirable patterns - newPat = (PatternID)ctx.next(PATTERN_FIRST, PATTERN_SINGLE_LAST); + newPat = (PatternID)ctx.next(rbegin, rend); } while (newPat == PATTERN_SOLID || newPat == PATTERN_RIBBON || newPat == PATTERN_MINIRIBBON); // update the led with the new random - m_pCurMode->setPatternAt(pos, newPat, nullptr, &randomSet); + m_pCurMode->setPattern(newPat, pos, nullptr, &randomSet); // initialize the mode with the new pattern and colorset m_pCurMode->init(); - DEBUG_LOGF("Randomized set with randomization technique %u, %u colors, and Pattern number %u", - randType, randomSet.numColors(), newPat); + DEBUG_LOGF("Randomized Led %u set with randomization technique %u, %u colors, and Pattern number %u", + pos, randType, randomSet.numColors(), newPat); return true; } bool Randomizer::reRoll() { + if (m_targetLeds == MAP_LED(LED_MULTI)) { + return reRoll(LED_MULTI); + } MAP_FOREACH_LED(m_targetLeds) { if (!reRoll(pos)) { return false; diff --git a/VortexEngine/src/Menus/MenuList/Randomizer.h b/VortexEngine/src/Menus/MenuList/Randomizer.h index 8bca369d87..6206d1c3ec 100644 --- a/VortexEngine/src/Menus/MenuList/Randomizer.h +++ b/VortexEngine/src/Menus/MenuList/Randomizer.h @@ -22,8 +22,9 @@ class Randomizer : public Menu void onLongClick() override; private: - // random context for each led - Random m_randCtx[LED_COUNT]; + // random context for each led and led multi (LED_COUNT + 1) + Random m_singlesRandCtx[LED_COUNT]; + Random m_multiRandCtx; // re-roll a new randomization with a given context on an led bool reRoll(LedPos pos); diff --git a/VortexEngine/src/Menus/Menus.cpp b/VortexEngine/src/Menus/Menus.cpp index 7a1c58d5e4..f8413df506 100644 --- a/VortexEngine/src/Menus/Menus.cpp +++ b/VortexEngine/src/Menus/Menus.cpp @@ -74,12 +74,11 @@ bool Menus::init() void Menus::cleanup() { - // close any open menu - if (m_menuState == MENU_STATE_IN_MENU) { - m_pCurMenu->leaveMenu(); + if (m_pCurMenu) { + delete m_pCurMenu; m_pCurMenu = nullptr; - m_menuState = MENU_STATE_NOT_OPEN; } + m_menuState = MENU_STATE_NOT_OPEN; m_selection = 0; m_openTime = 0; } diff --git a/VortexEngine/src/Modes/Mode.cpp b/VortexEngine/src/Modes/Mode.cpp index ff3f4349b7..eeea7cfb13 100644 --- a/VortexEngine/src/Modes/Mode.cpp +++ b/VortexEngine/src/Modes/Mode.cpp @@ -16,8 +16,11 @@ // for internal reference to the led count #define MODE_LEDCOUNT m_numLeds Mode::Mode(uint32_t numLeds) : +#if VORTEX_SLIM == 0 + m_multiPat(nullptr), +#endif m_numLeds(numLeds), - m_ledEntries(nullptr) + m_singlePats(nullptr) { setLedCount(m_numLeds); } @@ -29,10 +32,13 @@ Mode::Mode() : // for internal reference to the led count #define MODE_LEDCOUNT LED_COUNT Mode::Mode() : - m_ledEntries() +#if VORTEX_SLIM == 0 + m_multiPat(nullptr), +#endif + m_singlePats() { for (uint32_t i = 0; i < LED_COUNT; ++i) { - m_ledEntries[i] = nullptr; + m_singlePats[i] = nullptr; } } #endif @@ -40,19 +46,19 @@ Mode::Mode() : Mode::Mode(PatternID id, const Colorset &set) : Mode() { - setPattern(id, nullptr, &set); + setPattern(id, LED_ANY, nullptr, &set); } Mode::Mode(PatternID id, const PatternArgs &args, const Colorset &set) : Mode() { - setPattern(id, &args, &set); + setPattern(id, LED_ANY, &args, &set); } Mode::Mode(PatternID id, const PatternArgs *args, const Colorset *set) : Mode() { - setPattern(id, args, set); + setPattern(id, LED_ANY, args, set); } Mode::Mode(const Mode *other) : @@ -66,9 +72,9 @@ Mode::Mode(const Mode *other) : Mode::~Mode() { - clearPatterns(); + clearPattern(LED_ALL); #if FIXED_LED_COUNT == 0 - free(m_ledEntries); + free(m_singlePats); #endif } @@ -83,13 +89,20 @@ void Mode::operator=(const Mode &other) { #if FIXED_LED_COUNT == 0 setLedCount(other.getLedCount()); +#endif + clearPattern(LED_ALL); +#if VORTEX_SLIM == 0 + if (other.m_multiPat) { + m_multiPat = PatternBuilder::dupe(other.m_multiPat); + } #endif for (uint32_t i = 0; i < other.getLedCount(); ++i) { - Pattern *otherPat = other.m_ledEntries[i]; + Pattern *otherPat = other.m_singlePats[i]; if (!otherPat) { continue; } - m_ledEntries[i] = PatternBuilder::dupe(otherPat); + // todo: dupeSingle? + m_singlePats[i] = PatternBuilder::dupe(otherPat); } } @@ -106,10 +119,16 @@ bool Mode::operator!=(const Mode &other) const void Mode::init() { +#if VORTEX_SLIM == 0 + // initialize the multi pattern if it's present + if (m_multiPat) { + m_multiPat->init(); + } +#endif // otherwise regular init for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; ++pos) { // grab the entry for this led and initialize it - Pattern *entry = m_ledEntries[pos]; + Pattern *entry = m_singlePats[pos]; if (!entry) { continue; } @@ -119,19 +138,22 @@ void Mode::init() void Mode::play() { +#if VORTEX_SLIM == 0 + // play multi pattern first so that the singles can override + if (m_multiPat) { + m_multiPat->play(); + } +#endif + // now iterate all singles and play for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; ++pos) { // grab the entry for this led - Pattern *entry = m_ledEntries[pos]; + Pattern *entry = m_singlePats[pos]; if (!entry) { // incomplete pattern/set or empty slot continue; } // play the current pattern with current color set on the current finger entry->play(); - // if either of these flags are present only play the first pattern - if (isMultiLed()) { - //break; - } } } @@ -177,26 +199,50 @@ void Mode::serialize(ByteStream &buffer) const { // serialize the number of leds buffer.serialize((uint8_t)MODE_LEDCOUNT); +#if FIXED_LED_COUNT == 0 + // empty mode? + if (!MODE_LEDCOUNT) { + return; + } +#endif // serialize the flags uint32_t flags = getFlags(); buffer.serialize(flags); +#if VORTEX_SLIM == 0 + // serialiaze the multi led? + if ((flags & MODE_FLAG_MULTI_LED) && m_multiPat) { + // serialize the multi led + m_multiPat->serialize(buffer); + } +#endif + // if no single leds then just stop here + if (!(flags & MODE_FLAG_SINGLE_LED)) { + return; + } + // if there are any sparse singles (spaces) then we need to + // serialize an led map of which singles are set + if (flags & MODE_FLAG_SPARSE_SINGLES) { + buffer.serialize((uint32_t)getSingleLedMap()); + } + // then iterate each single led and serialize it for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; ++pos) { - const Pattern *entry = m_ledEntries[pos]; + const Pattern *entry = m_singlePats[pos]; if (!entry) { continue; } // just serialize the pattern then colorset entry->serialize(buffer); - // if either of these flags are present only serialize the first pattern - if (flags & (MODE_FLAG_MULTI_LED | MODE_FLAG_ALL_SAME_SINGLE)) { + // if they are all same single then only serialize one + if (flags & MODE_FLAG_ALL_SAME_SINGLE) { break; } } } +// this is a hairy function, but a bit of a necessary complexity bool Mode::unserialize(ByteStream &buffer) { - clearPatterns(); + clearPattern(LED_ALL); uint8_t ledCount = 0; // unserialize the number of leds buffer.unserialize(&ledCount); @@ -217,89 +263,123 @@ bool Mode::unserialize(ByteStream &buffer) // unserialize the flags value uint32_t flags = 0; buffer.unserialize(&flags); - // unserialize the first pattern - m_ledEntries[0] = PatternBuilder::unserialize(buffer); - // if there is no first pattern, or the flags indicate it's a multi-led - // pattern then there's nothing more to unserialize and we're done - if (!m_ledEntries[0] || (flags & MODE_FLAG_MULTI_LED)) { + // if there is a multi led pattern then unserialize it + if (flags & MODE_FLAG_MULTI_LED) { +#if VORTEX_SLIM == 1 + // unserialize the multi pattern but just discard it + PatternBuilder::unserialize(buffer); +#else + // otherwise in normal build actually unserialize it + m_multiPat = PatternBuilder::unserialize(buffer); + m_multiPat->init(); +#endif + } + // if there is no single led patterns just stop here + if (!(flags & MODE_FLAG_SINGLE_LED)) { return true; } - // if it's an 'all same single' pattern where it's the same single led pattern - // repeated across all leds then we can just re-apply the first led that was - // just unserialized to each of the other leds - if (flags & MODE_FLAG_ALL_SAME_SINGLE) { - Pattern *firstPat = m_ledEntries[0]; - PatternID firstID = firstPat->getPatternID(); - const Colorset *firstSet = firstPat->getColorset(); - PatternArgs firstArgs; - firstPat->getArgs(firstArgs); - for (LedPos pos = (LedPos)(LED_FIRST + 1); pos < MODE_LEDCOUNT; ++pos) { - if (!setPatternAt(pos, firstID, &firstArgs, firstSet)) { - // fail? - continue; - } + // is there an led map to unserialize? if not default to all + LedMap map = (1 << ledCount) - 1; + if (flags & MODE_FLAG_SPARSE_SINGLES) { + buffer.unserialize((uint32_t *)&map); + } + // unserialize all singleled patterns into their positions + Pattern *firstPat = nullptr; + MAP_FOREACH_LED(map) { + if (pos >= LED_COUNT) { + // in case the map encodes led positions this device doesn't support + break; + } + if (!firstPat) { + // save the first pattern so that it can be duped if this is 'all same' + m_singlePats[pos] = firstPat = PatternBuilder::unserialize(buffer); + } else if (flags & MODE_FLAG_ALL_SAME_SINGLE) { + // if all same then just dupe first + m_singlePats[pos] = PatternBuilder::dupe(firstPat); + } else { + // otherwise unserialize the pattern like normal + m_singlePats[pos] = PatternBuilder::unserialize(buffer); } + m_singlePats[pos]->bind(pos); + } + // there is a few different possibilities here: + // 1. The provided ledCount is less than our current LED_COUNT + // -> if this happens we need to repeat the first ledCount leds + // into the remaining LED_COUNT - ledCount + // 2. The provided ledCount is more than our current LED_COUNT + // -> if this happens then we can just chop the leds and continue + // 3. The provided ledCount is the same as our current LED_COUNT + // -> if this happens all is good we can continue + if (ledCount >= LED_COUNT) { + // in this case we either chopped some off or have the exact amount return true; } - // we already loaded led 0 so the led position starts at 1 - LedPos pos = (LedPos)(LED_FIRST + 1); - // unserialize the rest of the leds out of the savefile which - // is ledCount - 1 because we already loaded the first led - uint32_t ledsLeft = ledCount ? ledCount - 1 : 0; - for (uint32_t i = 0; i < ledsLeft; ++i) { - Pattern *pat = PatternBuilder::unserialize(buffer); - // if we have loaded all of our available leds - if (pos >= MODE_LEDCOUNT) { - // then just discard this pattern we cannot apply it - delete pat; + // in this case we have to repeat them so we loop LED_COUNT - ledCount + // times and walk the first ledCount that are already set and copy them + LedPos src = LED_FIRST; + // start from ledCount (the first index we didn't load) and loop till + // LED_COUNT and dupe the pattern in the src position, but wrap the src + // around at ledCount so that we repeat the first ledCount over again + for (LedPos pos = (LedPos)ledCount; pos < LED_COUNT; ++pos) { + m_singlePats[pos] = PatternBuilder::dupe(m_singlePats[src]); + m_singlePats[pos]->bind(pos); + src = (LedPos)((src + 1) % ledCount); + } + return true; +} + +bool Mode::equals(const Mode *other) const +{ + if (!other) { + return false; + } + // compare the led count + if (other->getLedCount() != MODE_LEDCOUNT) { + return false; + } +#if VORTEX_SLIM == 0 + // compare the multi pattern, either: + // have a local pat and it's not equal to other (other could be null) + // or have no local pat and other has one + if ((m_multiPat && !m_multiPat->equals(other->m_multiPat)) || + (!m_multiPat && other->m_multiPat)) { + return false; + } +#endif + // compare all the singles + for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; ++pos) { + // if entry is valid, do a comparison + if (m_singlePats[pos]) { + // checks if other is not null and equal + if (!m_singlePats[pos]->equals(other->m_singlePats[pos])) { + return false; + } continue; } - if (!pat) { - ERROR_LOG("Failed to unserialize pattern from buffer"); + // current is null, check if other is valid + if (other->m_singlePats[pos]) { return false; } - // must bind the pattern to position so the pattern knows which led - pat->bind(pos); - // then store the pattern in the leds array at the right position - m_ledEntries[pos] = pat; - // move forward to next position - pos++; - } - // at this point if our pos isn't our LED_LAST then that means the - // savefile had less entries in it than we can support and we need - // to repeat those entries to fill up our slots - if (pos < MODE_LEDCOUNT) { - LedPos src = LED_FIRST; - for(;pos < MODE_LEDCOUNT; ++pos) { - Pattern *pat = m_ledEntries[src]; - if (!pat) { - continue; - } - PatternArgs args; - pat->getArgs(args); - setPatternAt(pos, pat->getPatternID(), &args, pat->getColorset()); - // increment the src led but wrap at the ledcount so for example - // a savefile with only 3 leds saved will come out as ABCABCABCA - src = (LedPos)((src + 1) % ledCount); - } + // both are null } return true; } #if FIXED_LED_COUNT == 0 // change the internal pattern count in the mode object -void Mode::setLedCount(uint8_t numLeds) +bool Mode::setLedCount(uint8_t numLeds) { - if (m_ledEntries) { - clearPatterns(); - free(m_ledEntries); + if (m_singlePats) { + clearPattern(LED_ALL); + free(m_singlePats); } m_numLeds = numLeds; - m_ledEntries = (Pattern **)vcalloc(m_numLeds, sizeof(Pattern *)); - if (!m_ledEntries) { - // big error - return; + m_singlePats = (Pattern **)vcalloc(m_numLeds, sizeof(Pattern *)); + if (!m_singlePats) { + ERROR_OUT_OF_MEMORY(); + return false; } + return true; } #endif @@ -314,490 +394,652 @@ uint8_t Mode::getLedCount() const const Pattern *Mode::getPattern(LedPos pos) const { - // if fetching for 'all' leds just return the first pattern - if (pos >= MODE_LEDCOUNT) { - pos = LED_FIRST; - } - return m_ledEntries[pos]; + return ((Mode *)this)->getPattern(pos); } Pattern *Mode::getPattern(LedPos pos) { - // if fetching for 'all' leds just return the first pattern - if (pos >= MODE_LEDCOUNT) { - pos = LED_FIRST; + switch (pos) { + case LED_ALL: + // makes no sense + return nullptr; + case LED_ANY: + // fallthrough +#if VORTEX_SLIM == 0 + case LED_MULTI: + if (m_multiPat) { + return m_multiPat; + } + if (pos == LED_MULTI) { + // don't fallthrough if actually multi, it's possible + // we got here by falling through from LED_ALL + break; + } + // fall through if LED_ALL and delete the single leds +#endif + case LED_ALL_SINGLE: + // count as 'any' single +#if FIXED_LED_COUNT == 0 + if (!m_singlePats) { + return nullptr; + } +#endif + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + if (m_singlePats[pos]) { + return m_singlePats[pos]; + } + } + // actually break here + break; + default: +#if FIXED_LED_COUNT == 0 + if (!m_singlePats) { + return nullptr; + } +#endif + if (pos < LED_COUNT && m_singlePats[pos]) { + return m_singlePats[pos]; + } + break; } - return m_ledEntries[pos]; + return nullptr; } -const Colorset *Mode::getColorset(LedPos pos) const +const Colorset Mode::getColorset(LedPos pos) const { - // if fetching for 'all' leds just return the first - if (pos >= MODE_LEDCOUNT) { - pos = LED_FIRST; - } - if (!m_ledEntries[pos]) { - return nullptr; - } - return m_ledEntries[pos]->getColorset(); + return ((Mode *)this)->getColorset(pos); } -Colorset *Mode::getColorset(LedPos pos) +Colorset Mode::getColorset(LedPos pos) { - // if fetching for 'all' leds just return the first - if (pos >= MODE_LEDCOUNT) { - pos = LED_FIRST; - } - if (!m_ledEntries[pos]) { - return nullptr; + Pattern *pat = getPattern(pos); + if (!pat) { + return Colorset(); } - return m_ledEntries[pos]->getColorset(); + return pat->getColorset(); } PatternID Mode::getPatternID(LedPos pos) const { - // if fetching for 'all' leds just return the first - if (pos >= MODE_LEDCOUNT) { - pos = LED_FIRST; - } - if (!m_ledEntries[pos]) { + const Pattern *pat = getPattern(pos); + if (!pat) { return PATTERN_NONE; } - return m_ledEntries[pos]->getPatternID(); + return pat->getPatternID(); } -bool Mode::equals(const Mode *other) const +bool Mode::setPattern(PatternID pat, LedPos pos, const PatternArgs *args, const Colorset *set) { - if (!other) { - return false; - } - if (other->getLedCount() != MODE_LEDCOUNT) { - return false; - } - for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; ++pos) { - // if entry is valid, do a comparison - if (m_ledEntries[pos]) { - // checks if other is not null and equal - if (!m_ledEntries[pos]->equals(other->m_ledEntries[pos])) { + // Use provided colorset, or colorset from pos if valid, otherwise use effective colorset + Colorset newSet = set ? *set : getColorset(((pos < LED_COUNT) && m_singlePats[pos]) ? pos : LED_ANY); + switch (pos) { + case LED_ANY: + case LED_ALL: + // fallthrough +#if VORTEX_SLIM == 0 + case LED_MULTI: + if (m_multiPat) { + delete m_multiPat; + m_multiPat = nullptr; + } + if (isMultiLedPatternID(pat)) { + m_multiPat = PatternBuilder::makeMulti(pat, args); + if (m_multiPat) { + // they could set PATTERN_NONE to clear + m_multiPat->setColorset(newSet); + } + // TODO: don't clear single leds if we're setting multi + // but for now it's easier to just clear them + clearPattern(LED_ALL_SINGLE); + return true; + } + if (pos == LED_MULTI) { + // don't fallthrough if actually multi, it's possible + // we got here by falling through from LED_ALL + return false; + } + // fall through if LED_ALL and delete the single leds +#endif + case LED_ALL_SINGLE: +#if FIXED_LED_COUNT == 0 + if (!m_singlePats) { + return false; + } +#endif + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + if (!setPattern(pat, pos, args, &newSet)) { return false; } - continue; } - // current is null, check if other is valid - if (other->m_ledEntries[pos]) { + // actually break here + return true; + default: +#if FIXED_LED_COUNT == 0 + if (!m_singlePats) { return false; } - // both are null +#endif + if (pos >= LED_COUNT) { + return false; + } + if (m_singlePats[pos]) { + delete m_singlePats[pos]; + } + m_singlePats[pos] = PatternBuilder::makeSingle(pat, args); + // they could set PATTERN_NONE to clear + if (m_singlePats[pos]) { + m_singlePats[pos]->setColorset(newSet); + m_singlePats[pos]->bind(pos); + } + return true; } - return true; + return false; } -bool Mode::setPattern(PatternID pat, const PatternArgs *args, const Colorset *set) +bool Mode::setPatternMap(LedMap map, PatternID pat, const PatternArgs *args, const Colorset *set) { - // if it's a multi pattern ID then just set the multi pattern slot - if (isMultiLedPatternID(pat)) { - return setMultiPat(pat, args, set); - } - // otherwise iterate all of the LEDs and set single led patterns - for (LedPos p = LED_FIRST; p < MODE_LEDCOUNT; ++p) { - if (!setPatternAt(p, pat, args, set)) { - ERROR_LOGF("Failed to set single pattern %u", p); + MAP_FOREACH_LED(map) { + if (!setPattern(pat, pos, args, set)) { return false; } } return true; } -bool Mode::setColorset(const Colorset *set) +// set colorset at a specific position +bool Mode::setColorset(const Colorset &set, LedPos pos) { - if (isMultiLed()) { - m_ledEntries[0]->setColorset(set); + switch (pos) { + case LED_ANY: + case LED_ALL: + // fallthrough +#if VORTEX_SLIM == 0 + case LED_MULTI: + if (m_multiPat) { + m_multiPat->setColorset(set); + } + if (pos == LED_MULTI) { + // don't fallthrough if actually multi, it's possible + // we got here by falling through from LED_ALL + return true; + } + // fall through if LED_ALL and delete the single leds +#endif + case LED_ALL_SINGLE: +#if FIXED_LED_COUNT == 0 + if (!m_singlePats) { + return false; + } +#endif + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + if (m_singlePats[pos]) { + m_singlePats[pos]->setColorset(set); + } + } + // actually break here return true; - } - // otherwise set all of the colorsets - for (LedPos p = LED_FIRST; p < MODE_LEDCOUNT; ++p) { - if (!m_ledEntries[p]) { - continue; + default: +#if FIXED_LED_COUNT == 0 + if (!m_singlePats) { + return false; } - m_ledEntries[p]->setColorset(set); - } - return true; -} - -bool Mode::setColorsetAt(LedPos pos, const Colorset *set) -{ - if (pos >= MODE_LEDCOUNT) { - return setColorset(set); - } - if (!m_ledEntries[pos]) { - return false; +#endif + if (pos < LED_COUNT && m_singlePats[pos]) { + m_singlePats[pos]->setColorset(set); + return true; + } + break; } - m_ledEntries[pos]->setColorset(set); - return true; + return false; } -bool Mode::setColorsetAt(LedMap map, const Colorset *set) +// set colorset at each position in a map +bool Mode::setColorsetMap(LedMap map, const Colorset &set) { MAP_FOREACH_LED(map) { - if (!setColorsetAt(pos, set)) { + if (!setColorset(set, pos)) { return false; } } return true; } -bool Mode::setPatternAt(LedPos pos, PatternID pat, const PatternArgs *args, const Colorset *set) +void Mode::clearPattern(LedPos pos) { - if (pos >= MODE_LEDCOUNT) { - return setPattern(pat, args, set); - } - SingleLedPattern *newPat = PatternBuilder::makeSingle(pat, args); - if (!newPat) { - // failed to build new pattern, user gave multiled pattern id? - return false; - } - return setPatternAt(pos, newPat, set); + setPattern(PATTERN_NONE, pos); } -bool Mode::setPatternAt(LedPos pos, SingleLedPattern *pat, const Colorset *set) -{ - if (!pat) { - return false; - } - if (pos >= MODE_LEDCOUNT) { - PatternArgs args; - pat->getArgs(args); - return setPattern(pat->getPatternID(), &args, set); - } - // bind the position and colorset, if the colorset is missing then just - // bind the previously assigned colorset, if that is also missing then - // try to grab the colorset from the first led. This could happen for - // example if a multi led pattern was set and we're not setting single - // led patterns on all the fingers - if (!set) { - set = getColorset(pos); - if (!set) { - set = getColorset(LED_FIRST); - } - } - pat->bind(pos); - pat->setColorset(set); - clearPatternAt(pos); - m_ledEntries[pos] = pat; - return true; -} - -bool Mode::setPatternAt(LedMap map, PatternID pat, const PatternArgs *args, const Colorset *set) -{ - MAP_FOREACH_LED(map) { - if (!setPatternAt(pos, pat, args, set)) { - return false; - } - } - return true; -} - -bool Mode::setPatternAt(LedMap map, SingleLedPattern *pat, const Colorset *set) +void Mode::clearPatternMap(LedMap map) { MAP_FOREACH_LED(map) { - if (!setPatternAt(pos, pat, set)) { - return false; - } + clearPattern(pos); } - return true; } -bool Mode::setMultiPat(PatternID pat, const PatternArgs *args, const Colorset *set) +void Mode::clearColorset(LedPos pos) { - MultiLedPattern *newPat = PatternBuilder::makeMulti(pat, args); - if (!newPat) { - return false; - } - return setMultiPat(newPat, set); + Colorset empty; + setColorset(empty, pos); } -bool Mode::setMultiPat(MultiLedPattern *pat, const Colorset *set) +void Mode::clearColorsetMap(LedMap map) { - if (!pat) { - return false; + MAP_FOREACH_LED(map) { + clearColorset(pos); } - // initialize the new pattern with the old colorset - // if there isn't already a pattern - pat->setColorset(set ? set : getColorset(LED_FIRST)); - // clear any stored patterns - clearPatterns(); - // update the multi pattern - m_ledEntries[0] = pat; - return true; } uint32_t Mode::getFlags() const { uint32_t flags = 0; - if (isMultiLed()) { - flags |= MODE_FLAG_MULTI_LED; - } else if (isSameSingleLed()) { - flags |= MODE_FLAG_ALL_SAME_SINGLE; - } + if (hasMultiLed()) flags |= MODE_FLAG_MULTI_LED; + if (hasSingleLed()) flags |= MODE_FLAG_SINGLE_LED; + if (hasSameSingleLed()) flags |= MODE_FLAG_ALL_SAME_SINGLE; + if (hasSparseSingleLed()) flags |= MODE_FLAG_SPARSE_SINGLES; return flags; } -// is this a multi-led pattern in the mode? -bool Mode::isMultiLed() const +// whether a multi-led pattern is present in the mode +bool Mode::hasMultiLed() const { - if (!m_ledEntries[0]) { - return false; - } - return m_ledEntries[0]->hasFlags(PATTERN_FLAG_MULTI); +#if VORTEX_SLIM == 0 + return (m_multiPat != nullptr); +#else + return false; +#endif } -// are all the single led patterns and colorsets equal? -bool Mode::isSameSingleLed() const +// whether at least one single-led pattern is present in the mode +bool Mode::hasSingleLed() const { - if (!m_ledEntries[0]) { - return false; - } - if (isMultiLed()) { - return false; - } - for (uint32_t i = LED_FIRST + 1; i < MODE_LEDCOUNT; ++i) { - // if any don't match 0 then no good - if (!m_ledEntries[i] || !m_ledEntries[i]->equals(m_ledEntries[0])) { - return false; + for (uint32_t i = LED_FIRST; i < MODE_LEDCOUNT; ++i) { + if (m_singlePats[i]) { + return true; } } - // all the same - return true; -} - -void Mode::clearPatterns() -{ -#if FIXED_LED_COUNT == 0 - if (!m_ledEntries) { - return; - } -#endif - for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; ++pos) { - clearPatternAt(pos); - } + return false; } -void Mode::clearPatternAt(LedPos pos) +// are all the single led patterns and colorsets equal? +bool Mode::hasSameSingleLed() const { -#if FIXED_LED_COUNT == 0 - if (!m_ledEntries) { - return; - } -#endif - if (!m_ledEntries[pos]) { - return; + Pattern *firstPat = nullptr; + for (uint32_t i = LED_FIRST; i < MODE_LEDCOUNT; ++i) { + if (!m_singlePats[i]) { + continue; + } + if (!firstPat) { + firstPat = m_singlePats[i]; + continue; + } + if (!firstPat->equals(m_singlePats[i])) { + return false; + } } - delete m_ledEntries[pos]; - m_ledEntries[pos] = nullptr; + // all the same if at least one pattern was found + return (firstPat != nullptr); } -void Mode::clearPatternAt(LedMap map) +bool Mode::hasSparseSingleLed() const { - MAP_FOREACH_LED(map) { - clearPatternAt(pos); + switch (getSingleLedMap()) { + case MAP_LED_ALL: + case 0: + // if all or none are set it's not sparse + return false; + default: + // if anything else is set it's sparse + return true; } } -void Mode::clearColorsets() +LedMap Mode::getSingleLedMap() const { - for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; ++pos) { - if (!m_ledEntries[pos]) { - continue; + LedMap map = 0; + for (LedPos pos = LED_FIRST; pos < MODE_LEDCOUNT; pos++) { + if (m_singlePats[pos]) { + setLed(map, pos); } - m_ledEntries[pos]->clearColorset(); } + return map; } -void Mode::clearColorsetAt(LedPos pos) +// is this a multi-led pattern in the mode? +bool Mode::isMultiLed() const { -#if FIXED_LED_COUNT == 0 - if (!m_ledEntries) { - return; - } +#if VORTEX_SLIM == 0 + return hasMultiLed() && !hasSingleLed(); +#else + return false; #endif - if (!m_ledEntries[pos]) { - return; - } - m_ledEntries[pos]->clearColorset(); -} - -void Mode::clearColorsetAt(LedMap map) -{ - MAP_FOREACH_LED(map) { - clearColorsetAt(pos); - } } #if MODES_TEST == 1 +#include #include // Mode::Test function void Mode::test() { - // Test setPattern + INFO_LOG("== Beginning Mode Tests ==\n"); + + INFO_LOG("= Testing Mode::setPattern =\n"); + Mode modeTest; + + assert(modeTest.getMultiPatID() == PATTERN_NONE); + assert(modeTest.getMultiColorset() == nullptr); + assert(modeTest.getMultiPat() == nullptr); + + // Check if default constructor initializes Mode object correctly + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + assert(modeTest.getPatternIDAt(pos) == PATTERN_NONE); + assert(modeTest.getColorsetAt(pos) == nullptr); + assert(modeTest.getPatternAt(pos) == nullptr); + } + + INFO_LOG("Test Mode default constructor passed.\n"); + + // Test setPattern PatternID testPatternID = PATTERN_SOLID; PatternArgs testPatternArgs = { 100, 255, 0, 10 }; Colorset testColorset(0, 0, 255); modeTest.setPattern(testPatternID, &testPatternArgs, &testColorset); - // Check if the pattern and colorset were set correctly + // Check if the pattern and colorset were set correctly for all LEDs for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - assert(modeTest.getPatternID(pos) == testPatternID); - assert(modeTest.getColorset(pos)->equals(&testColorset)); + assert(modeTest.getPatternIDAt(pos) == testPatternID); + assert(modeTest.getColorsetAt(pos)->equals(&testColorset)); } - DEBUG_LOG("Test setPattern passed."); + INFO_LOG("Test setPattern passed.\n"); + + // Test setPattern with multi-led pattern + Mode modeMultiLedTest; + PatternID multiPatternID = PATTERN_MULTI_FIRST; + PatternArgs multiPatternArgs = { 200, 200, 200, 5 }; + Colorset multiColorset(255, 0, 0); + + modeMultiLedTest.setPattern(multiPatternID, &multiPatternArgs, &multiColorset); + + // Check if the multi-led pattern and colorset were set correctly + assert(modeMultiLedTest.getPatternID() == multiPatternID); + assert(modeMultiLedTest.getColorset()->equals(&multiColorset)); + INFO_LOG("Test setPattern with multi-led pattern passed.\n"); + + // Test setPattern with single-led pattern and multi-led pattern simultaneously + Mode modeBothTest; + PatternID singlePatternID = PATTERN_SOLID; + PatternArgs singlePatternArgs = { 100, 255, 0, 10 }; + Colorset singleColorset(0, 0, 255); + + modeBothTest.setPattern(singlePatternID, &singlePatternArgs, &singleColorset); + modeBothTest.setPattern(multiPatternID, &multiPatternArgs, &multiColorset); + + // Check if the single-led and multi-led patterns coexist + assert(modeBothTest.hasMultiLed() && modeBothTest.hasSingleLed()); + INFO_LOG("Test setPattern with single-led and multi-led patterns simultaneously passed.\n"); + + // Test setPatternAt + Mode modeSetPatternAtTest; + LedPos setPatternAtPosition = LED_5; + modeSetPatternAtTest.setPatternAt(setPatternAtPosition, testPatternID, &testPatternArgs, &testColorset); + + // Check if the pattern and colorset were set correctly at the specified position + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + if (pos == setPatternAtPosition) { + assert(modeSetPatternAtTest.getPatternIDAt(pos) == testPatternID); + assert(modeSetPatternAtTest.getColorsetAt(pos)->equals(&testColorset)); + } else { + assert(modeSetPatternAtTest.getPatternIDAt(pos) == PATTERN_NONE); + } + } + INFO_LOG("Test setPatternAt passed.\n"); // Test setColorsetAt - Colorset newColorset(255, 255, 0); + Colorset newColorset(RGB_YELLOW, RGB_ORANGE, RGB_GREEN); LedPos setColorsetPosition = LED_3; - modeTest.setColorsetAt(&newColorset, setColorsetPosition); + modeTest.setColorsetAt(setColorsetPosition, &newColorset); // Check if the colorset was set correctly at the specified position for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { if (pos == setColorsetPosition) { - assert(modeTest.getColorset(pos)->equals(&newColorset)); + assert(modeTest.getColorsetAt(pos)->equals(&newColorset)); } else { - assert(modeTest.getColorset(pos)->equals(&testColorset)); + assert(modeTest.getColorsetAt(pos)->equals(&testColorset)); } } - DEBUG_LOG("Test setColorsetAt passed."); + INFO_LOG("Test setColorsetAt passed.\n"); + + // Test clearPattern with single-led pattern + Mode modeClearSingleLedTest; + modeClearSingleLedTest.setPattern(testPatternID, &testPatternArgs, &testColorset); + modeClearSingleLedTest.clearPatterns(); - // Add more test cases as needed + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + assert(modeClearSingleLedTest.getPatternIDAt(pos) == PATTERN_NONE); + } + INFO_LOG("Test clearPattern with single-led pattern passed.\n"); - // Test clearPattern - Mode modeClearTest; - modeClearTest.setPattern(testPatternID, &testPatternArgs, &testColorset); - modeClearTest.clearPattern(); + // Test clearPattern with multi-led pattern + Mode modeClearMultiLedTest; + modeClearMultiLedTest.setPattern(multiPatternID, &multiPatternArgs, &multiColorset); + modeClearMultiLedTest.clearPatterns(); + assert(modeClearMultiLedTest.getPatternID() == PATTERN_NONE); + INFO_LOG("Test clearPattern with multi-led pattern passed.\n"); + + // Test clearPattern with both single-led and multi-led patterns + modeBothTest.clearPatterns(); + + assert(modeBothTest.getPatternID() == PATTERN_NONE); for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - assert(modeClearTest.getPatternID(pos) == PATTERN_NONE); + assert(modeBothTest.getPatternIDAt(pos) == PATTERN_NONE); } - DEBUG_LOG("Test clearPattern passed."); + INFO_LOG("Test clearPattern with both single-led and multi-led patterns passed.\n"); - // Test setPatternAt - Mode modeSetPatternAtTest; - LedPos setPatternAtPosition = LED_5; - modeSetPatternAtTest.setPatternAt(setPatternAtPosition, testPatternID, &testPatternArgs, &testColorset); + // Test clearColorset + Mode modeClearColorsetTest; + modeClearColorsetTest.setPattern(testPatternID, &testPatternArgs, &testColorset); + modeClearColorsetTest.clearColorsets(); for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - if (pos == setPatternAtPosition) { - assert(modeSetPatternAtTest.getPatternID(pos) == testPatternID); + assert(modeClearColorsetTest.getColorsetAt(pos)->numColors() == 0); + } + INFO_LOG("Test clearColorset passed.\n"); + + // Test clearColorsetAt for a specific position + Mode modeClearColorsetAtTest; + modeClearColorsetAtTest.setPattern(testPatternID, &testPatternArgs, &testColorset); + LedPos clearColorsetAtPosition = LED_3; + modeClearColorsetAtTest.clearColorsetAt(clearColorsetAtPosition); + + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + if (pos == clearColorsetAtPosition) { + assert(modeClearColorsetAtTest.getColorsetAt(pos)->numColors() == 0); } else { - assert(modeSetPatternAtTest.getPatternID(pos) == PATTERN_NONE); + assert(modeClearColorsetAtTest.getColorsetAt(pos)->numColors() == testColorset.numColors()); } } - DEBUG_LOG("Test setPatternAt passed."); + INFO_LOG("Test clearColorsetAt passed.\n"); - // Test clearPatternAt - Mode modeClearPatternAtTest; - LedPos clearPatternAtPosition = LED_7; - modeClearPatternAtTest.setPattern(testPatternID, &testPatternArgs, &testColorset); - modeClearPatternAtTest.clearPatternAt(clearPatternAtPosition); + // Test clearColorsets with multi-led pattern + Mode modeClearMultiColorsetsTest; + modeClearMultiColorsetsTest.setPattern(multiPatternID, &multiPatternArgs, &multiColorset); + modeClearMultiColorsetsTest.clearColorsets(); + assert(modeClearMultiColorsetsTest.getColorset()->numColors() == 0); + INFO_LOG("Test clearColorsets with multi-led pattern passed.\n"); + + // Test clearColorsets with both single-led and multi-led patterns + modeBothTest.setPattern(PATTERN_BASIC); + modeBothTest.clearColorsets(); + + assert(modeBothTest.getColorset()->numColors() == 0); for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { - if (pos == clearPatternAtPosition) { - assert(modeClearPatternAtTest.getPatternID(pos) == PATTERN_NONE); - } else { - assert(modeClearPatternAtTest.getPatternID(pos) == testPatternID); - } - } - DEBUG_LOG("Test clearPatternAt passed."); - - // Test setPatternAt for a specific position - Mode modePatternAtTest; - Colorset colorset(0xFF0000, 0x00FF00, 0x0000FF); - PatternID patternId = PATTERN_SOLID; - assert(modePatternAtTest.setPatternAt(LED_FIRST, patternId, nullptr, &colorset)); - assert(modePatternAtTest.getPatternID(LED_FIRST) == patternId); - assert(modePatternAtTest.getColorset(LED_FIRST)->equals(&colorset)); - DEBUG_LOG("Test clearPatternAt passed."); - - // Test setColorsetAt for a specific position - Mode modeSetColorsetAtTest; - assert(modeSetColorsetAtTest.setPattern(PATTERN_BASIC)); - assert(modeSetColorsetAtTest.setColorsetAt(&colorset, LED_FIRST)); - assert(modeSetColorsetAtTest.getColorset(LED_FIRST)); - assert(modeSetColorsetAtTest.getColorset(LED_FIRST)->equals(&colorset)); - DEBUG_LOG("Test setColorsetAt passed."); - - // Test setMultiPat - Mode modeSetMultiPatTest; - assert(modeSetMultiPatTest.setMultiPat(PATTERN_MULTI_FIRST, nullptr, &colorset)); - assert(modeSetMultiPatTest.isMultiLed()); - assert(modeSetMultiPatTest.getPatternID() == PATTERN_MULTI_FIRST); - assert(modeSetMultiPatTest.getColorset()->equals(&colorset)); - DEBUG_LOG("Test setMultiPat passed."); + assert(modeBothTest.getColorsetAt(pos)->numColors() == 0); + } + INFO_LOG("Test clearColorsets with both single-led and multi-led patterns passed.\n"); + + // Test hasSparseSingleLed with sparse single-led patterns + Mode modeSparseTest; + modeSparseTest.setPatternAt(LED_3, testPatternID, &testPatternArgs, &testColorset); + modeSparseTest.setPatternAt(LED_5, testPatternID, &testPatternArgs, &testColorset); + modeSparseTest.setPatternAt(LED_7, testPatternID, &testPatternArgs, &testColorset); + + assert(modeSparseTest.hasSparseSingleLed()); + INFO_LOG("Test hasSparseSingleLed with sparse single-led patterns passed.\n"); + + // Test getSingleLedMap with no single-led patterns + Mode modeNoSingleLedTest; + assert(modeNoSingleLedTest.getSingleLedMap() == 0); + INFO_LOG("Test getSingleLedMap with no single-led patterns passed.\n"); + + // Test getSingleLedMap with all single-led patterns + Mode modeAllSingleLedTest; + modeAllSingleLedTest.setPattern(testPatternID, &testPatternArgs, &testColorset); + assert(modeAllSingleLedTest.getSingleLedMap() == MAP_LED_ALL); + INFO_LOG("Test getSingleLedMap with all single-led patterns passed.\n"); + + // Test getSingleLedMap with sparse single-led patterns + assert(modeSparseTest.getSingleLedMap() == (1 << LED_3 | 1 << LED_5 | 1 << LED_7)); + INFO_LOG("Test getSingleLedMap with sparse single-led patterns passed.\n"); + + // Test isMultiLed with only multi-led pattern + Mode modeOnlyMultiTest; + modeOnlyMultiTest.setPattern(multiPatternID, &multiPatternArgs, &multiColorset); + assert(modeOnlyMultiTest.isMultiLed()); + INFO_LOG("Test isMultiLed with only multi-led pattern passed.\n"); + + // Test isMultiLed with both single-led and multi-led patterns + assert(!modeBothTest.isMultiLed()); + INFO_LOG("Test isMultiLed with both single-led and multi-led patterns passed.\n"); + + // Test setPatternAt for specific position and clearPatternAt for a specific position + Mode modeSetClearAtTest; + modeSetClearAtTest.setPatternAt(LED_5, testPatternID, &testPatternArgs, &testColorset); + modeSetClearAtTest.clearPatternAt(LED_5); + + assert(modeSetClearAtTest.getPatternIDAt(LED_5) == PATTERN_NONE); + assert(modeSetClearAtTest.getColorsetAt(LED_5) == nullptr); + INFO_LOG("Test setPatternAt and clearPatternAt for specific position passed.\n"); + + // Test setColorsetAt for specific position and clearColorsetAt for a specific position + Mode modeSetClearColorsetAtTest; + modeSetClearColorsetAtTest.setPatternAt(LED_5, testPatternID, &testPatternArgs, &testColorset); + modeSetClearColorsetAtTest.clearColorsetAt(LED_5); + + assert(modeSetClearColorsetAtTest.getColorsetAt(LED_5)->numColors() == 0); + INFO_LOG("Test setColorsetAt and clearColorsetAt for specific position passed.\n"); + + // Test clearMultiColorset with multi-led pattern + Mode modeMultiClearTest; + modeMultiClearTest.setPattern(multiPatternID, &multiPatternArgs, &multiColorset); + modeMultiClearTest.clearMultiColorset(); + + assert(modeMultiClearTest.getPatternID() == multiPatternID); + assert(modeMultiClearTest.getColorset()->numColors() == 0); + INFO_LOG("Test clearMultiColorset with multi-led pattern passed.\n"); + + PatternID invalidID = (PatternID)(PATTERN_COUNT + 1); + + // Test setPattern with an invalid pattern ID + Mode modeInvalidPatternIDTest; + bool success = modeInvalidPatternIDTest.setPattern(invalidID, &testPatternArgs, &testColorset); + assert(!success); + INFO_LOG("Test setPattern with invalid pattern ID passed.\n"); + + // Test setPatternAt with an invalid pattern ID + Mode modeInvalidPatternIDAtTest; + success = modeInvalidPatternIDAtTest.setPatternAt(LED_3, invalidID, &testPatternArgs, &testColorset); + assert(!success); + INFO_LOG("Test setPatternAt with invalid pattern ID passed.\n"); + + // Test setMultiPat with an invalid pattern ID + Mode modeInvalidMultiPatternIDTest; + success = modeInvalidMultiPatternIDTest.setMultiPat(PATTERN_SINGLE_LAST, &testPatternArgs, &testColorset); + assert(!success); + INFO_LOG("Test setMultiPat with invalid pattern ID passed.\n"); + + LedPos invalidPos = (LedPos)(LED_COUNT + 1); - // Test clearColorset - Mode modeClearColorsetTest; - assert(modeClearColorsetTest.setPatternAt(LED_FIRST, patternId, nullptr, &colorset)); - modeClearColorsetTest.clearColorset(); - assert(modeClearColorsetTest.getColorset(LED_FIRST)->numColors() == 0); - DEBUG_LOG("Test clearColorset passed."); - - // Test clearPatternAt for a specific position - modeClearPatternAtTest.init(); - assert(modeClearPatternAtTest.setPatternAt(LED_FIRST, patternId, nullptr, &colorset)); - modeClearPatternAtTest.clearPatternAt(LED_FIRST); - assert(modeClearPatternAtTest.getPattern(LED_FIRST) == nullptr); - assert(modeClearPatternAtTest.getColorset(LED_FIRST) == nullptr); - DEBUG_LOG("Test clearColorsetAt passed."); - - // Test copy constructor - Mode modeCopyConstructorTest; - assert(modeCopyConstructorTest.setPatternAt(LED_FIRST, patternId, nullptr, &colorset)); - Mode modeCopyConstructorTestCopy(modeCopyConstructorTest); - assert(modeCopyConstructorTestCopy.getPatternID(LED_FIRST) == patternId); - assert(modeCopyConstructorTestCopy.getColorset(LED_FIRST)->equals(&colorset)); - DEBUG_LOG("Test copy constructor passed."); - - // Test assignment operator - Mode modeAssignmentOperatorTest; - assert(modeAssignmentOperatorTest.setPatternAt(LED_FIRST, patternId, nullptr, &colorset)); - Mode modeAssignmentOperatorTestCopy; - modeAssignmentOperatorTestCopy = modeAssignmentOperatorTest; - assert(modeAssignmentOperatorTestCopy.getPatternID(LED_FIRST) == patternId); - assert(modeAssignmentOperatorTestCopy.getColorset(LED_FIRST)->equals(&colorset)); - DEBUG_LOG("Test assignment operator passed."); - - // Test equality operator - Mode modeEqualityOperatorTest1; - Mode modeEqualityOperatorTest2; - assert(modeEqualityOperatorTest1.setPatternAt(LED_FIRST, patternId, nullptr, &colorset)); - assert(modeEqualityOperatorTest2.setPatternAt(LED_FIRST, patternId, nullptr, &colorset)); - assert(modeEqualityOperatorTest1 == modeEqualityOperatorTest2); - DEBUG_LOG("Test equality operator passed."); - - // Test inequality operator - Mode modeInequalityOperatorTest1; - Mode modeInequalityOperatorTest2; - assert(modeInequalityOperatorTest1.setPatternAt(LED_FIRST, patternId, nullptr, &colorset)); - assert(modeInequalityOperatorTest2.setPatternAt(LED_LAST, patternId, nullptr, &colorset)); - assert(modeInequalityOperatorTest1 != modeInequalityOperatorTest2); - DEBUG_LOG("Test inequality operator passed."); - - // Test isSameSingleLed - Mode modeSameSingleLedTest; - assert(modeSameSingleLedTest.setPattern(patternId, nullptr, &colorset)); - assert(modeSameSingleLedTest.isSameSingleLed()); - DEBUG_LOG("Test isSameSingleLed passed."); - - // Test getLedCount -#if FIXED_LED_COUNT == 0 - Mode modeGetLedCountTest(5); - assert(modeGetLedCountTest.getLedCount() == 5); - Mode modeSetLedCountTest2(3); - modeSetLedCountTest2.setLedCount(7); - assert(modeSetLedCountTest2.getLedCount() == 7); - DEBUG_LOG("Test getLedCount passed."); -#endif + // Test setPatternAt + Mode modeInvalidLEDPositionTest; + success = modeInvalidLEDPositionTest.setPatternAt(invalidPos, testPatternID, &testPatternArgs, &testColorset); + assert(success); + INFO_LOG("Test setPattern with invalid LED position passed.\n"); + + // Test setColorsetAt with an invalid LED position + Mode modeInvalidLEDPositionSetColorsetTest; + success = modeInvalidLEDPositionSetColorsetTest.setColorsetAt(invalidPos, &testColorset); + assert(success); + INFO_LOG("Test setColorsetAt with invalid LED position passed.\n"); + + // Test clearPatternAt with an invalid LED position + Mode modeInvalidLEDPositionClearPatternTest; + modeInvalidLEDPositionClearPatternTest.setPattern(PATTERN_BLENDSTROBE); + modeInvalidLEDPositionClearPatternTest.clearPatternAt(invalidPos); + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + assert(modeInvalidLEDPositionClearPatternTest.getPatternAt(pos) != nullptr); + } + INFO_LOG("Test clearPatternAt with invalid LED position passed.\n"); + // Test clearColorsetAt with an invalid LED position + Mode modeInvalidLEDPositionClearColorsetTest; + modeInvalidLEDPositionClearColorsetTest.setPattern(PATTERN_BLEND); + modeInvalidLEDPositionClearColorsetTest.clearColorsetAt(invalidPos); + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + assert(modeInvalidLEDPositionClearColorsetTest.getColorsetAt(pos) != nullptr); + } + INFO_LOG("Test clearColorsetAt with invalid LED position passed.\n"); + + // Test setPatternAt with PATTERN_NONE + Mode modePatternNoneTest; + modePatternNoneTest.setPatternAt(LED_2, PATTERN_NONE, &testPatternArgs, &testColorset); + assert(modePatternNoneTest.getPatternIDAt(LED_2) == PATTERN_NONE); + INFO_LOG("Test setPatternAt with PATTERN_NONE passed.\n"); + + // Test setPattern with PATTERN_NONE + Mode modeSetPatternNoneTest; + modeSetPatternNoneTest.setPattern(PATTERN_NONE, &testPatternArgs, &testColorset); + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + assert(modeSetPatternNoneTest.getPatternIDAt(pos) == PATTERN_NONE); + } + INFO_LOG("Test setPattern with PATTERN_NONE passed.\n"); + + // Test setPattern with PATTERN_MULTI_LAST + Mode modeSetPatternMultiLastTest; + modeSetPatternMultiLastTest.setPattern(PATTERN_MULTI_LAST, &testPatternArgs, &testColorset); + assert(modeSetPatternMultiLastTest.getPatternID() == PATTERN_MULTI_LAST); + assert(modeSetPatternMultiLastTest.getMultiPatID() == PATTERN_MULTI_LAST); + for (LedPos pos = LED_FIRST; pos < LED_COUNT; ++pos) { + assert(modeSetPatternNoneTest.getPatternIDAt(pos) == PATTERN_NONE); + } + INFO_LOG("Test setPattern with PATTERN_MULTI_LAST passed.\n"); + + // Test setMultiPat with PATTERN_MULTI_FIRST + Mode modeSetMultiPatMultiFirstTest; + modeSetMultiPatMultiFirstTest.setMultiPat(PATTERN_MULTI_FIRST, &testPatternArgs, &testColorset); + assert(modeSetMultiPatMultiFirstTest.getPatternID() == PATTERN_MULTI_FIRST); + INFO_LOG("Test setMultiPat with PATTERN_MULTI_FIRST passed.\n"); + + // Test setMultiPat with PATTERN_MULTI_LAST + Mode modeSetMultiPatMultiLastTest; + modeSetMultiPatMultiLastTest.setMultiPat(PATTERN_MULTI_LAST, &testPatternArgs, &testColorset); + assert(modeSetMultiPatMultiLastTest.getPatternID() == PATTERN_MULTI_LAST); + INFO_LOG("Test setMultiPat with PATTERN_MULTI_LAST passed.\n"); + + // Test setMultiPat with PATTERN_SINGLE_FIRST + Mode modeSetMultiPatSingleFirstTest; + bool setMultiPatSingleFirstSuccess = modeSetMultiPatSingleFirstTest.setMultiPat(PATTERN_SINGLE_FIRST, &testPatternArgs, &testColorset); + assert(!setMultiPatSingleFirstSuccess); + INFO_LOG("Test setMultiPat with PATTERN_SINGLE_FIRST passed.\n"); + + // Test setMultiPat with PATTERN_SINGLE_LAST + Mode modeSetMultiPatSingleLastTest; + bool setMultiPatSingleLastSuccess = modeSetMultiPatSingleLastTest.setMultiPat(PATTERN_SINGLE_LAST, &testPatternArgs, &testColorset); + assert(!setMultiPatSingleLastSuccess); + INFO_LOG("Test setMultiPat with PATTERN_SINGLE_LAST passed.\n"); } + #endif diff --git a/VortexEngine/src/Modes/Mode.h b/VortexEngine/src/Modes/Mode.h index cadd2c5579..eb07b9b360 100644 --- a/VortexEngine/src/Modes/Mode.h +++ b/VortexEngine/src/Modes/Mode.h @@ -16,8 +16,14 @@ class Pattern; #define MODE_FLAG_NONE 0 // the mode is utilizing a multi-led pattern #define MODE_FLAG_MULTI_LED (1 << 0) +// the mode is utilizing at least one single-led pattern +#define MODE_FLAG_SINGLE_LED (1 << 1) // the mode is utilizing the same single-led pattern on each finger -#define MODE_FLAG_ALL_SAME_SINGLE (1 << 1) +// optimization will be done when saving and only one led is saved +#define MODE_FLAG_ALL_SAME_SINGLE (1 << 2) +// the mode is missing leds on some single slots, an led map is +// present in savefile in order to convey which leds are missing +#define MODE_FLAG_SPARSE_SINGLES (1 << 3) // A mode is the container for instances of patterns. A pattern // must be bound to a colorset and led position with bind() and @@ -63,73 +69,79 @@ class Mode // load the mode from serial (optional led count) virtual bool unserialize(ByteStream &buffer); + // mode comparison + bool equals(const Mode *other) const; + #if FIXED_LED_COUNT == 0 // change the internal pattern count in the mode object - void setLedCount(uint8_t numLeds); + bool setLedCount(uint8_t numLeds); #endif uint8_t getLedCount() const; - // Get pointer to an individual pattern/colorset - const Pattern *getPattern(LedPos pos = LED_FIRST) const; - Pattern *getPattern(LedPos pos = LED_FIRST); - // get a pointer to a colorset - const Colorset *getColorset(LedPos pos = LED_FIRST) const; - Colorset *getColorset(LedPos pos = LED_FIRST); + // get the pattern at a position + const Pattern *getPattern(LedPos pos = LED_ANY) const; + Pattern *getPattern(LedPos pos = LED_ANY); + + // get the colorset at a position + const Colorset getColorset(LedPos pos = LED_ANY) const; + Colorset getColorset(LedPos pos = LED_ANY); + // get the pattern ID of the given pattern - PatternID getPatternID(LedPos pos = LED_FIRST) const; + PatternID getPatternID(LedPos pos = LED_ANY) const; - // mode comparison - bool equals(const Mode *other) const; + // change a single led pattern + bool setPattern(PatternID pat, LedPos pos = LED_ANY, const PatternArgs *args = nullptr, const Colorset *set = nullptr); + bool setPatternMap(LedMap pos, PatternID pat, const PatternArgs *args = nullptr, const Colorset *set = nullptr); - // set the pattern/colorset of the mode, if a multi-led pattern is provided then the pos - // is ignored. If a single led pattern is provided then it will be applied to all LEDS - // unless a specific LED is provided - bool setPattern(PatternID pat, const PatternArgs *args = nullptr, const Colorset *set = nullptr); - bool setColorset(const Colorset *set); + // set colorset at a specific position + bool setColorset(const Colorset &set, LedPos pos = LED_ANY); + // set colorset at each position in a map + bool setColorsetMap(LedMap map, const Colorset &set); - bool setColorsetAt(LedPos pos, const Colorset *set); - bool setColorsetAt(LedMap map, const Colorset *set); + // clear stored patterns in various ways + void clearPattern(LedPos pos = LED_ALL); + void clearPatternMap(LedMap map); - // change a single or multi pattern - bool setPatternAt(LedPos pos, PatternID pat, const PatternArgs *args = nullptr, const Colorset *set = nullptr); - bool setPatternAt(LedPos pos, SingleLedPattern *pat, const Colorset *set = nullptr); - bool setPatternAt(LedMap pos, PatternID pat, const PatternArgs *args = nullptr, const Colorset *set = nullptr); - bool setPatternAt(LedMap pos, SingleLedPattern *pat, const Colorset *set = nullptr); - bool setMultiPat(PatternID pat, const PatternArgs *args = nullptr, const Colorset *set = nullptr); - bool setMultiPat(MultiLedPattern *pat, const Colorset *set = nullptr); + // clear colorset in various ways + void clearColorset(LedPos pos = LED_ALL); + void clearColorsetMap(LedMap map); // get the flags associated with this mode uint32_t getFlags() const; - // is this a multi-led pattern in the mode? - bool isMultiLed() const; - // are all the single led patterns and colorsets equal? - bool isSameSingleLed() const; + // whether a multi-led pattern is present in the mode + bool hasMultiLed() const; + // whether at least one single-led pattern is present in the mode + bool hasSingleLed() const; + // whether this mode has all same single-led patterns + bool hasSameSingleLed() const; + // whether this mode has sparse single-led patterns (missing some slots) + bool hasSparseSingleLed() const; - // erase any stored patterns or colorsets - void clearPatterns(); - void clearPatternAt(LedPos pos); - void clearPatternAt(LedMap map); - void clearColorsets(); - void clearColorsetAt(LedPos pos); - void clearColorsetAt(LedMap map); + // get the led map of which singles are set + LedMap getSingleLedMap() const; + + // whether this mode purely a multi-led pattern with no singles + bool isMultiLed() const; #if MODES_TEST == 1 static void test(); #endif private: +#if VORTEX_SLIM == 0 + // the multi-led pattern slot is only present in non-slim builds + Pattern *m_multiPat; +#endif + // a list of slots for each single led pattern #if FIXED_LED_COUNT == 0 // the number of leds the mode is targetting uint8_t m_numLeds; - // list of pointers to Patterns, one for each led or if it - // is a multi-led pattern then there is only one total - Pattern **m_ledEntries; + // list of pointers to Patterns, one for each led + Pattern **m_singlePats; #else - Pattern *m_ledEntries[LED_COUNT]; + Pattern *m_singlePats[LED_COUNT]; #endif - // TODO: separate multi pattern - //MultiLedPattern *m_multiPat; }; #endif diff --git a/VortexEngine/src/Modes/Modes.cpp b/VortexEngine/src/Modes/Modes.cpp index 3ede052e68..a851b2e7ab 100644 --- a/VortexEngine/src/Modes/Modes.cpp +++ b/VortexEngine/src/Modes/Modes.cpp @@ -368,6 +368,7 @@ bool Modes::addMode(const Mode *mode) } // replace current mode with new one, destroying existing one +// TODO: is this api necessary? bool Modes::updateCurMode(PatternID id, const Colorset *set) { if (id > PATTERN_LAST) { @@ -382,7 +383,7 @@ bool Modes::updateCurMode(PatternID id, const Colorset *set) DEBUG_LOG("Failed to set pattern of current mode"); // failed to set pattern? } - if (set && !pCur->setColorset(set)) { + if (set && !pCur->setColorset(*set)) { DEBUG_LOG("Failed to set colorset of current mode"); } // initialize the mode with new pattern and colorset @@ -394,7 +395,8 @@ bool Modes::updateCurMode(PatternID id, const Colorset *set) bool Modes::updateCurMode(const Mode *mode) { - return updateCurMode(mode->getPatternID(), mode->getColorset()); + Colorset set = mode->getColorset(); + return updateCurMode(mode->getPatternID(), &set); } // set the current active mode by index @@ -722,11 +724,11 @@ void Modes::ModeLink::save() #include #include -#include "Patterns/PatternBuilder.h" +#include "../Patterns/PatternBuilder.h" void Modes::test() { - printf("== Beginning Modes Test ==\n"); + INFO_LOG("== Beginning Modes Test ==\n"); RGBColor col = RGB_RED; assert(!addMode(PATTERN_COUNT, col)); @@ -757,7 +759,7 @@ void Modes::test() clearModes(); assert(numModes() == 0); - printf("addMode(): success\n"); + INFO_LOG("addMode(): success\n"); ByteStream modebuf; ByteStream modesave; @@ -776,7 +778,7 @@ void Modes::test() assert(mode2 != nullptr); assert(mode1->equals(mode2)); - printf("addSerializedMode(): success\n"); + INFO_LOG("addSerializedMode(): success\n"); Colorset newset(RGB_BLUE, RGB_RED, RGB_GREEN); assert(updateCurMode(PATTERN_HYPERSTROBE, nullptr)); @@ -788,7 +790,7 @@ void Modes::test() Mode newTmp(PATTERN_BLEND, PatternBuilder::getDefaultArgs(PATTERN_BLEND), Colorset(RGB_YELLOW, RGB_ORANGE, RGB_CYAN, RGB_BLUE, RGB_WHITE, RGB_RED)); - printf("updateCurMode(): success\n"); + INFO_LOG("updateCurMode(): success\n"); assert(shiftCurMode(-1)); assert(m_curMode == 0); @@ -803,7 +805,7 @@ void Modes::test() assert(getModeLink(0)->instantiate()->getPatternID() == PATTERN_HYPERSTROBE); assert(getModeLink(1)->instantiate()->getPatternID() == PATTERN_DOPS); - printf("shiftCurMode(): success\n"); + INFO_LOG("shiftCurMode(): success\n"); deleteCurMode(); assert(m_numModes == 1); @@ -812,8 +814,8 @@ void Modes::test() assert(m_numModes == 0); assert(m_curMode == 0); - printf("deleteCurMode(): success\n"); + INFO_LOG("deleteCurMode(): success\n"); - printf("== Success Running Modes Test ==\n"); + INFO_LOG("== Success Running Modes Test ==\n"); } #endif diff --git a/VortexEngine/src/Patterns/Multi/CompoundPattern.cpp b/VortexEngine/src/Patterns/Multi/CompoundPattern.cpp index 718d7a1acf..82370a5ce1 100644 --- a/VortexEngine/src/Patterns/Multi/CompoundPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/CompoundPattern.cpp @@ -59,11 +59,8 @@ void CompoundPattern::setPatternAt(LedPos pos, SingleLedPattern *pat, if (!pat || pos >= LED_COUNT) { return; } - if (!set) { - set = &m_colorset; - } pat->bind(pos); - pat->setColorset(set); + pat->setColorset(set ? *set : m_colorset); pat->init(); // handle re-initialization and prevent leaks if (m_ledPatterns[pos]) { diff --git a/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.cpp b/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.cpp index 1917573900..5a42cbc06b 100644 --- a/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.cpp +++ b/VortexEngine/src/Patterns/Multi/Sequencer/SequencedPattern.cpp @@ -69,7 +69,7 @@ void SequencedPattern::play() curSet = &step.m_colorsetMap[pos]; } // if there's no pattern, or pattern is wrong colorset or pattern ID - if (!curPat || !curPat->getColorset()->equals(curSet) || curPat->getPatternID() != stepPattern) { + if (!curPat || !curPat->getColorset().equals(curSet) || curPat->getPatternID() != stepPattern) { // delete any existing pattern and re-create it delete m_ledPatterns[pos]; // create whichever pattern this step is (maybe PATTERN_NONE) @@ -77,7 +77,7 @@ void SequencedPattern::play() // if a pattern was created then bind and init it if (curPat) { curPat->bind(pos); - curPat->setColorset(curSet); + curPat->setColorset(*curSet); curPat->init(); } } diff --git a/VortexEngine/src/Patterns/Pattern.cpp b/VortexEngine/src/Patterns/Pattern.cpp index 6624cc6f82..640a5fbd47 100644 --- a/VortexEngine/src/Patterns/Pattern.cpp +++ b/VortexEngine/src/Patterns/Pattern.cpp @@ -112,32 +112,30 @@ bool Pattern::equals(const Pattern *other) return false; } // compare pattern id - if (m_patternID != other->getPatternID()) { + if (m_patternID != other->m_patternID) { return false; } // then colorset - if (!m_colorset.equals(other->getColorset())) { + if (!m_colorset.equals(&other->m_colorset)) { return false; } - // then compare the extra params - PatternArgs myArgs; - PatternArgs otherArgs; - getArgs(myArgs); - other->getArgs(otherArgs); - if (myArgs != otherArgs) { + // number of args + if (m_numArgs != other->m_numArgs) { return false; } + // compare each arg + for (uint8_t i = 0; i < m_numArgs; ++i) { + if (getArg(i) != other->getArg(i)) { + return false; + } + } return true; } // change the colorset -void Pattern::setColorset(const Colorset *set) +void Pattern::setColorset(const Colorset &set) { - if (!set) { - clearColorset(); - } else { - m_colorset = *set; - } + m_colorset = set; } void Pattern::clearColorset() diff --git a/VortexEngine/src/Patterns/Pattern.h b/VortexEngine/src/Patterns/Pattern.h index 6745bc7d3d..dd7cee3854 100644 --- a/VortexEngine/src/Patterns/Pattern.h +++ b/VortexEngine/src/Patterns/Pattern.h @@ -61,13 +61,12 @@ class Pattern #ifdef VORTEX_LIB // skip the pattern ahead some ticks - virtual void skip(uint32_t ticks); + void skip(uint32_t ticks); #endif - // must override the serialize routine to save the pattern - // must override unserialize to load patterns - virtual void serialize(ByteStream &buffer) const; - virtual void unserialize(ByteStream &buffer); + // serialize and unserialize a pattern to a bytestream + void serialize(ByteStream &buffer) const; + void unserialize(ByteStream &buffer); // get or set a single arg void setArg(uint8_t index, uint8_t value); @@ -92,8 +91,10 @@ class Pattern bool equals(const Pattern *other); // change the colorset - virtual void setColorset(const Colorset *set); - virtual void clearColorset(); + const Colorset getColorset() const { return m_colorset; } + Colorset getColorset() { return m_colorset; } + void setColorset(const Colorset &set); + void clearColorset(); // change the led position void setLedPos(LedPos pos) { m_ledPos = pos; } @@ -102,8 +103,6 @@ class Pattern PatternID getPatternID() const { return m_patternID; } // get a pointer to the colorset that is bound to the pattern - const Colorset *getColorset() const { return &m_colorset; } - Colorset *getColorset() { return &m_colorset; } LedPos getLedPos() const { return m_ledPos; } // get the pattern flags diff --git a/VortexEngine/src/Patterns/Patterns.h b/VortexEngine/src/Patterns/Patterns.h index a5fa9bac27..3e29fc8c82 100644 --- a/VortexEngine/src/Patterns/Patterns.h +++ b/VortexEngine/src/Patterns/Patterns.h @@ -100,8 +100,12 @@ enum PatternID : uint8_t }; // some helper functions to improve readability -inline bool isMultiLedPatternID(PatternID id) { return id >= PATTERN_MULTI_FIRST; } -inline bool isSingleLedPatternID(PatternID id) { return id < PATTERN_MULTI_FIRST; } +inline bool isMultiLedPatternID(PatternID id) { + return id >= PATTERN_MULTI_FIRST && id <= PATTERN_MULTI_LAST; +} +inline bool isSingleLedPatternID(PatternID id) { + return id < PATTERN_MULTI_FIRST; +} // PatternID operators inline PatternID &operator++(PatternID &c) diff --git a/VortexEngine/src/Patterns/Single/SolidPattern.cpp b/VortexEngine/src/Patterns/Single/SolidPattern.cpp index ce337d73c3..ef59cf7894 100644 --- a/VortexEngine/src/Patterns/Single/SolidPattern.cpp +++ b/VortexEngine/src/Patterns/Single/SolidPattern.cpp @@ -9,20 +9,6 @@ SolidPattern::SolidPattern(const PatternArgs &args) : setArgs(args); } -SolidPattern::~SolidPattern() -{ -} - -void SolidPattern::init() -{ - BasicPattern::init(); -} - -void SolidPattern::play() -{ - BasicPattern::play(); -} - // callbacks for blinking on/off, can be overridden by derived classes void SolidPattern::onBlinkOn() { diff --git a/VortexEngine/src/Patterns/Single/SolidPattern.h b/VortexEngine/src/Patterns/Single/SolidPattern.h index 3684c3f79b..ac50b71da9 100644 --- a/VortexEngine/src/Patterns/Single/SolidPattern.h +++ b/VortexEngine/src/Patterns/Single/SolidPattern.h @@ -9,11 +9,6 @@ class SolidPattern : public BasicPattern { public: SolidPattern(const PatternArgs &args); - virtual ~SolidPattern(); - - virtual void init() override; - - virtual void play() override; protected: // callbacks for blinking on/off, can be overridden by derived classes diff --git a/VortexEngine/src/Serial/Serial.cpp b/VortexEngine/src/Serial/Serial.cpp index 489f568fd3..9d7a204602 100644 --- a/VortexEngine/src/Serial/Serial.cpp +++ b/VortexEngine/src/Serial/Serial.cpp @@ -33,6 +33,7 @@ bool SerialComs::isConnected() // check for any serial connection or messages bool SerialComs::checkSerial() { +#if VORTEX_SLIM == 0 if (isConnected()) { // already connected return true; @@ -49,15 +50,17 @@ bool SerialComs::checkSerial() // serial is not connected return false; } - // serial is now connected - m_serialConnected = true; // Begin serial communications Serial.begin(9600); +#endif + // serial is now connected + m_serialConnected = true; return true; } void SerialComs::write(const char *msg, ...) { +#if VORTEX_SLIM == 0 if (!isConnected()) { return; } @@ -67,10 +70,12 @@ void SerialComs::write(const char *msg, ...) int len = vsnprintf((char *)buf, sizeof(buf), msg, list); Serial.write(buf, len); va_end(list); +#endif } void SerialComs::write(ByteStream &byteStream) { +#if VORTEX_SLIM == 0 if (!isConnected()) { return; } @@ -78,10 +83,12 @@ void SerialComs::write(ByteStream &byteStream) uint32_t size = byteStream.rawSize(); Serial.write((const uint8_t *)&size, sizeof(size)); Serial.write((const uint8_t *)byteStream.rawData(), byteStream.rawSize()); +#endif } void SerialComs::read(ByteStream &byteStream) { +#if VORTEX_SLIM == 0 if (!isConnected()) { return; } @@ -93,12 +100,17 @@ void SerialComs::read(ByteStream &byteStream) uint8_t byte = Serial.read(); byteStream.serialize(byte); } while (--amt > 0); +#endif } bool SerialComs::dataReady() { +#if VORTEX_SLIM == 0 if (!isConnected()) { return false; } return (Serial.available() > 0); +#else + return false; +#endif } diff --git a/VortexEngine/src/Serial/Serial.h b/VortexEngine/src/Serial/Serial.h index 6ab3c186da..af1ee279f1 100644 --- a/VortexEngine/src/Serial/Serial.h +++ b/VortexEngine/src/Serial/Serial.h @@ -8,9 +8,6 @@ class ByteStream; // Really wish I could name this Serial but arduino ruined that for me class SerialComs { - // private constructor - SerialComs(); - public: // init serial diff --git a/VortexEngine/src/Time/TimeControl.cpp b/VortexEngine/src/Time/TimeControl.cpp index 514389c8ed..af1110db9c 100644 --- a/VortexEngine/src/Time/TimeControl.cpp +++ b/VortexEngine/src/Time/TimeControl.cpp @@ -24,6 +24,7 @@ uint32_t Time::m_tickrate = DEFAULT_TICKRATE; #ifdef VORTEX_LIB uint32_t Time::m_simulationTick = 0; bool Time::m_isSimulation = false; +bool Time::m_instantTimestep = false; #endif // Within this file TICKRATE may refer to the variable member @@ -44,6 +45,7 @@ bool Time::init() #ifdef VORTEX_LIB m_simulationTick = 0; m_isSimulation = false; + m_instantTimestep = false; #endif return true; } @@ -57,10 +59,10 @@ void Time::tickClock() // tick clock forward m_curTick++; -#if TIMER_TEST == 1 - // just return immediately when testing the time system, this prevents the actual - // delay from occurring and allows us to tick forward as fast as we want - return; +#ifdef VORTEX_LIB + if (m_instantTimestep) { + return; + } #endif #if DEBUG_ALLOCATIONS == 1 @@ -209,6 +211,11 @@ void Time::test() { DEBUG_LOG("Starting Time class tests..."); + // just return immediately when testing the time system, + // this prevents the actual delay from occurring and + // allows us to tick forward as fast as we want + setInstantTimestep(true); + // Test init function assert(init()); DEBUG_LOG("Init test passed"); diff --git a/VortexEngine/src/Time/TimeControl.h b/VortexEngine/src/Time/TimeControl.h index 12fcfadb99..158a7b2b5c 100644 --- a/VortexEngine/src/Time/TimeControl.h +++ b/VortexEngine/src/Time/TimeControl.h @@ -64,6 +64,10 @@ class Time // Finish a time simulation static uint32_t endSimulation(); + + // Toggle instant step + static void setInstantTimestep(bool instant) { m_instantTimestep = instant; } + static bool isInstantStepping() { return m_instantTimestep; } #endif #if TIMER_TEST == 1 @@ -94,6 +98,9 @@ class Time // whether the timer is running a simulation static bool m_isSimulation; + + // whether to disable timestep and instantly tick + static bool m_instantTimestep; #endif }; diff --git a/VortexEngine/src/VortexConfig.h b/VortexEngine/src/VortexConfig.h index 75d0d93715..d470ae9140 100644 --- a/VortexEngine/src/VortexConfig.h +++ b/VortexEngine/src/VortexConfig.h @@ -455,4 +455,10 @@ #define LOGGING_LEVEL 3 #endif +// no ignore button time when running vortex lib +#ifdef VORTEX_LIB +#undef IGNORE_BUTTON_TIME +#define IGNORE_BUTTON_TIME 0 +#endif + #endif // VORTEX_CONFIG_H diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index 0e5d3a7b0e..e45af3d522 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -103,9 +103,7 @@ void VortexEngine::tick() // don't poll the button till some cycles have passed, this prevents // the wakeup from cycling to the next mode - if (Time::getCurtime() > IGNORE_BUTTON_TICKS) { - Buttons::check(); - } + Buttons::check(); // run the main logic for the engine runMainLogic(); @@ -168,10 +166,11 @@ uint32_t VortexEngine::savefileSize() #include #include "Colors/Colorset.h" #include "Memory/Memory.h" +#include "Random/Random.h" void VortexEngine::compressionTest() { // always use same seed - randomSeed(0xdeadbeef); + Random rand(0xdeadbeef); ByteStream stream; Modes::clearModes(); for (uint32_t len = 1; len < 4096; ++len) { @@ -185,7 +184,7 @@ void VortexEngine::compressionTest() Mode tmpMode; tmpMode.setPattern((PatternID)(len % PATTERN_COUNT)); Colorset set; - set.randomizeColorTheory(8); + set.randomizeColorTheory(rand, 8); tmpMode.setColorset(&set); Modes::addMode(&tmpMode); modeStream.clear(); @@ -203,7 +202,7 @@ void VortexEngine::compressionTest() stream.decompress(); if (memcmp(stream.data(), modeStream.data(), modeStream.size()) != 0) { ERROR_LOGF("Buffers not equal: %u", modeStream.size()); - printf("\t"); + INFO_LOG("\t"); for (uint32_t i = 0; i < len; ++i) { printf("%02x ", modeStream.data()[i]); if (i > 0 && ((i + 1) % 32) == 0) { @@ -212,11 +211,12 @@ void VortexEngine::compressionTest() } return; } - if (modeStream.size() != complen) { + if (modeStream.size() != (size_t)complen) { DEBUG_LOGF("Success %u compressed to %u", modeStream.size(), complen); } free(buf); } + Modes::clearModes(); DEBUG_LOG("Success testing compression"); } #endif @@ -227,12 +227,14 @@ void VortexEngine::compressionTest() #include "Patterns/Pattern.h" #include "Patterns/PatternArgs.h" #include "Patterns/PatternBuilder.h" +#include "Random/Random.h" #include void VortexEngine::serializationTest() { Colorset bigSet; + Random rand; for (uint32_t i = 0; i < MAX_COLOR_SLOTS; ++i) { - bigSet.addColorByHue(i * 31); + bigSet.addColorWithValueStyle(rand, i * 31, 255, Colorset::VAL_STYLE_ALTERNATING, MAX_COLOR_SLOTS); } DEBUG_LOG("== Beginning Serialization Test =="); for (PatternID patternID = PATTERN_FIRST; patternID < PATTERN_COUNT; ++patternID) { @@ -254,12 +256,12 @@ void VortexEngine::serializationTest() uint8_t byte = 0; uint32_t count = 0; while (buffer.unserialize(&byte)) { - printf("%02x ", byte); + INFO_LOGF("%02x ", byte); if ((++count % 32) == 0) { - printf("\n"); + INFO_LOG("\n"); } } - printf("\n"); + INFO_LOG("\n"); return; } if (!tmpMode.equals(&tmpMode2)) { @@ -296,7 +298,7 @@ void VortexEngine::serializationTest() uint8_t byte = 0; uint32_t count = 0; while (buffer.unserialize(&byte)) { - printf("%02x ", byte); + INFO_LOGF("%02x ", byte); if ((++count % 32) == 0) { printf("\n"); } diff --git a/example_modes.json b/example_modes.json deleted file mode 100644 index 5f4e34c6e0..0000000000 --- a/example_modes.json +++ /dev/null @@ -1,1623 +0,0 @@ -{ - "Brightness": 255, - "NumModes": 15, - "Modes": [ - { - "flags": 1, - "Leds": [ - { - "PatternID": 44, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "SubPatterns": [ - { - "PatternID": 0, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 0, - "OffDuration": 1, - "GapDuration": 5, - "GroupSize": 3, - "SkipColors": 0, - "RepeatGroup": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 0, - "OffDuration": 1, - "GapDuration": 69, - "GroupSize": 3, - "SkipColors": 0, - "RepeatGroup": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 0, - "OffDuration": 1, - "GapDuration": 5, - "GroupSize": 3, - "SkipColors": 0, - "RepeatGroup": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 0, - "OffDuration": 1, - "GapDuration": 69, - "GroupSize": 3, - "SkipColors": 0, - "RepeatGroup": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 0, - "OffDuration": 1, - "GapDuration": 5, - "GroupSize": 3, - "SkipColors": 0, - "RepeatGroup": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 0, - "OffDuration": 1, - "GapDuration": 69, - "GroupSize": 3, - "SkipColors": 0, - "RepeatGroup": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 0, - "OffDuration": 1, - "GapDuration": 5, - "GroupSize": 3, - "SkipColors": 0, - "RepeatGroup": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 0, - "OffDuration": 1, - "GapDuration": 69, - "GroupSize": 3, - "SkipColors": 0, - "RepeatGroup": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 0, - "OffDuration": 1, - "GapDuration": 5, - "GroupSize": 3, - "SkipColors": 0, - "RepeatGroup": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 0, - "OffDuration": 1, - "GapDuration": 69, - "GroupSize": 3, - "SkipColors": 0, - "RepeatGroup": 0 - } - } - ] - } - } - ] - }, - { - "flags": 2, - "Leds": [ - { - "PatternID": 11, - "Colorset": { - "NumColors": 3, - "Colors": [ - { - "Red": 255, - "Green": 255, - "Blue": 255 - }, - { - "Red": 255, - "Green": 255, - "Blue": 255 - }, - { - "Red": 170, - "Green": 0, - "Blue": 0 - } - ] - }, - "Params": { - "OnDuration": 1, - "OffDuration": 0, - "GapDuration": 50 - } - } - ] - }, - { - "flags": 1, - "Leds": [ - { - "PatternID": 40, - "Colorset": { - "NumColors": 8, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "SubPatterns": [ - { - "PatternID": 0, - "Colorset": { - "NumColors": 8, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 5, - "OffDuration": 8, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 250, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 8, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 5, - "OffDuration": 8, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 250, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 129, - "Green": 255, - "Blue": 0 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 250, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 129, - "Green": 255, - "Blue": 0 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 250, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 8, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 5, - "OffDuration": 8, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 250, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 250, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 250, - "GapDuration": 0 - } - } - ] - } - } - ] - }, - { - "flags": 1, - "Leds": [ - { - "PatternID": 34, - "Colorset": { - "NumColors": 2, - "Colors": [ - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 80, - "Green": 0, - "Blue": 170 - } - ] - }, - "Params": { - "BlinkOnDuration": 2, - "BlinkOffDuration": 7, - "StepDuration": 100 - } - } - ] - }, - { - "flags": 1, - "Leds": [ - { - "PatternID": 37, - "Colorset": { - "NumColors": 3, - "Colors": [ - { - "Red": 0, - "Green": 255, - "Blue": 252 - }, - { - "Red": 255, - "Green": 84, - "Blue": 219 - }, - { - "Red": 0, - "Green": 23, - "Blue": 85 - } - ] - }, - "Params": { - "OnDuration1": 2, - "OffDuration1": 7, - "OnDuration2": 5, - "OffDuration2": 8, - "StepDuration": 100 - } - } - ] - }, - { - "flags": 1, - "Leds": [ - { - "PatternID": 23, - "Colorset": { - "NumColors": 4, - "Colors": [ - { - "Red": 33, - "Green": 255, - "Blue": 0 - }, - { - "Red": 120, - "Green": 0, - "Blue": 255 - }, - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 170, - "Green": 0, - "Blue": 0 - } - ] - }, - "Params": { - "OnDuration": 2, - "OffDuraiton": 13, - "StepDuration": 50 - } - } - ] - }, - { - "flags": 2, - "Leds": [ - { - "PatternID": 0, - "Colorset": { - "NumColors": 4, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 105 - }, - { - "Red": 0, - "Green": 165, - "Blue": 255 - }, - { - "Red": 235, - "Green": 255, - "Blue": 84 - }, - { - "Red": 0, - "Green": 165, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 5, - "OffDuration": 8, - "GapDuration": 0 - } - } - ] - }, - { - "flags": 1, - "Leds": [ - { - "PatternID": 35, - "Colorset": { - "NumColors": 3, - "Colors": [ - { - "Red": 85, - "Green": 36, - "Blue": 0 - }, - { - "Red": 0, - "Green": 191, - "Blue": 76 - }, - { - "Red": 46, - "Green": 0, - "Blue": 123 - } - ] - }, - "Params": { - "BlinkOnDuration": 2, - "BlinkOffDuration": 7, - "StepDuration": 100 - } - } - ] - }, - { - "flags": 2, - "Leds": [ - { - "PatternID": 4, - "Colorset": { - "NumColors": 8, - "Colors": [ - { - "Red": 85, - "Green": 0, - "Blue": 0 - }, - { - "Red": 170, - "Green": 128, - "Blue": 0 - }, - { - "Red": 129, - "Green": 255, - "Blue": 0 - }, - { - "Red": 0, - "Green": 85, - "Blue": 20 - }, - { - "Red": 0, - "Green": 85, - "Blue": 84 - }, - { - "Red": 0, - "Green": 23, - "Blue": 85 - }, - { - "Red": 80, - "Green": 0, - "Blue": 170 - }, - { - "Red": 85, - "Green": 0, - "Blue": 67 - } - ] - }, - "Params": { - "OnDuration": 1, - "OffDuration": 3, - "GapDuration": 0 - } - } - ] - }, - { - "flags": 1, - "Leds": [ - { - "PatternID": 45, - "Colorset": { - "NumColors": 4, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 201 - }, - { - "Red": 169, - "Green": 193, - "Blue": 255 - }, - { - "Red": 40, - "Green": 0, - "Blue": 85 - }, - { - "Red": 84, - "Green": 255, - "Blue": 253 - } - ] - }, - "Params": { - "OnDuration1": 5, - "OffDuration1": 8, - "OnDuration2": 3, - "OffDuration2": 35, - "StepSpeed": 800 - } - } - ] - }, - { - "flags": 1, - "Leds": [ - { - "PatternID": 32, - "Colorset": { - "NumColors": 8, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 23, - "Blue": 85 - }, - { - "Red": 0, - "Green": 23, - "Blue": 85 - }, - { - "Red": 0, - "Green": 23, - "Blue": 85 - }, - { - "Red": 0, - "Green": 23, - "Blue": 85 - }, - { - "Red": 0, - "Green": 23, - "Blue": 85 - }, - { - "Red": 0, - "Green": 23, - "Blue": 85 - }, - { - "Red": 0, - "Green": 23, - "Blue": 85 - } - ] - }, - "Params": { - "BlinkOnDuration": 2, - "BlinkOffDuration": 7, - "StepDuration": 125 - } - } - ] - }, - { - "flags": 2, - "Leds": [ - { - "PatternID": 11, - "Colorset": { - "NumColors": 5, - "Colors": [ - { - "Red": 80, - "Green": 0, - "Blue": 170 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 255, - "Green": 255, - "Blue": 255 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 80, - "Green": 0, - "Blue": 170 - } - ] - }, - "Params": { - "OnDuration": 1, - "OffDuration": 0, - "GapDuration": 50 - } - } - ] - }, - { - "flags": 1, - "Leds": [ - { - "PatternID": 32, - "Colorset": { - "NumColors": 3, - "Colors": [ - { - "Red": 0, - "Green": 255, - "Blue": 252 - }, - { - "Red": 216, - "Green": 0, - "Blue": 255 - }, - { - "Red": 255, - "Green": 149, - "Blue": 84 - } - ] - }, - "Params": { - "BlinkOnDuration": 2, - "BlinkOffDuration": 7, - "StepDuration": 125 - } - } - ] - }, - { - "flags": 1, - "Leds": [ - { - "PatternID": 19, - "Colorset": { - "NumColors": 6, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "SubPatterns": [ - { - "PatternID": 1, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 25, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 6, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 5, - "OffDuration": 8, - "GapDuration": 0 - } - }, - { - "PatternID": 1, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 25, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 6, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 5, - "OffDuration": 8, - "GapDuration": 0 - } - }, - { - "PatternID": 1, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 25, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 6, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 5, - "OffDuration": 8, - "GapDuration": 0 - } - }, - { - "PatternID": 1, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 25, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 6, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 5, - "OffDuration": 8, - "GapDuration": 0 - } - }, - { - "PatternID": 1, - "Colorset": { - "NumColors": 1, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 25, - "OffDuration": 25, - "GapDuration": 0 - } - }, - { - "PatternID": 0, - "Colorset": { - "NumColors": 6, - "Colors": [ - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - }, - { - "Red": 0, - "Green": 255, - "Blue": 60 - }, - { - "Red": 0, - "Green": 69, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 5, - "OffDuration": 8, - "GapDuration": 0 - } - } - ] - } - } - ] - }, - { - "flags": 2, - "Leds": [ - { - "PatternID": 14, - "Colorset": { - "NumColors": 3, - "Colors": [ - { - "Red": 255, - "Green": 0, - "Blue": 0 - }, - { - "Red": 0, - "Green": 255, - "Blue": 0 - }, - { - "Red": 0, - "Green": 0, - "Blue": 255 - } - ] - }, - "Params": { - "OnDuration": 2, - "OffDuration": 13, - "GapDuration": 0, - "BlendSpeed": 1 - } - } - ] - } - ] -} From 1989f57a0f59e3997522687ab8e7adf29792b2d3 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 27 Apr 2023 23:47:48 -0700 Subject: [PATCH 03/85] Added lockstep to vortexlib --- VortexEngine/VortexLib/VortexLib.cpp | 5 +++++ VortexEngine/VortexLib/VortexLib.h | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/VortexEngine/VortexLib/VortexLib.cpp b/VortexEngine/VortexLib/VortexLib.cpp index dc4628e77a..1b89edf3b1 100644 --- a/VortexEngine/VortexLib/VortexLib.cpp +++ b/VortexEngine/VortexLib/VortexLib.cpp @@ -71,6 +71,7 @@ bool Vortex::m_initialized = false; uint32_t Vortex::m_buttonsPressed = 0; std::string Vortex::m_commandLog; bool Vortex::m_commandLogEnabled = false; +bool Vortex::m_lockstepEnabled = false; #ifdef _MSC_VER #include @@ -280,6 +281,10 @@ bool Vortex::tick() // we don't call getchar() too many times and accidentally block uint32_t numInputs = 0; ioctl(STDIN_FILENO, FIONREAD, &numInputs); + if (m_lockstepEnabled && !numInputs) { + // don't tick till we have input + return true; + } // iterate the number of inputs on stdin and parse each letter // into a command for the engine, this will inject all of the commands // that are available into the engine but that doesn't necessarily diff --git a/VortexEngine/VortexLib/VortexLib.h b/VortexEngine/VortexLib/VortexLib.h index 8bdb623953..1771adde91 100644 --- a/VortexEngine/VortexLib/VortexLib.h +++ b/VortexEngine/VortexLib/VortexLib.h @@ -213,6 +213,10 @@ class Vortex static const std::string &getCommandLog() { return m_commandLog; } static void clearCommandLog() { m_commandLog.clear(); } + // enable/disable lockstep mode (one tick per input) + static bool enableLockstep(bool enable) { return m_lockstepEnabled = enable; } + static bool isLockstep() { return m_lockstepEnabled; } + private: // the last command to have been executed static char m_lastCommand; @@ -295,4 +299,6 @@ class Vortex static std::string m_commandLog; // whether to record commands static bool m_commandLogEnabled; + // whether to run in lockstep mode (one input per step) + static bool m_lockstepEnabled; }; From 622c2de934b98ac23b479e9d1aafe57ee927f20f Mon Sep 17 00:00:00 2001 From: Shane Aronson Date: Mon, 17 Apr 2023 21:42:01 -0700 Subject: [PATCH 04/85] Basic Pattern Dash Param --- .../src/Patterns/Single/BasicPattern.cpp | 56 ++++++++++++++++++- .../src/Patterns/Single/BasicPattern.h | 7 +++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 522da9d240..628f49c3b0 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -9,6 +9,7 @@ BasicPattern::BasicPattern(const PatternArgs &args) : m_onDuration(0), m_offDuration(0), m_gapDuration(0), + m_dashDuration(30), m_groupSize(0), m_reflectIndex(0), m_repeatGroup(0), @@ -18,7 +19,10 @@ BasicPattern::BasicPattern(const PatternArgs &args) : m_reflect(false), m_blinkTimer(), m_gapTimer(), - m_inGap(false) + m_inGap(false), + m_dashTimer(), + m_inDash(false), + m_dashTriggered(false) { m_patternID = PATTERN_BASIC; REGISTER_ARG(m_onDuration); @@ -41,10 +45,14 @@ void BasicPattern::init() m_inGap = false; m_reflect = false; + m_inDash = false; + m_dashTriggered = false; // don't start the gap timer till we're in a gap m_gapTimer.init(TIMER_1_ALARM, m_gapDuration); + m_dashTimer.init(TIMER_1_ALARM, m_dashDuration); + // start the blink timer now m_blinkTimer.init(TIMER_2_ALARMS | TIMER_START, m_onDuration, m_offDuration); @@ -53,6 +61,9 @@ void BasicPattern::init() } else { m_realGroupSize = m_groupSize; } + if (m_dashDuration && m_colorset.numColors() > 1) { + m_realGroupSize -= 1; + } m_groupCounter = 0; m_repeatCounter = m_repeatGroup; } @@ -68,6 +79,15 @@ void BasicPattern::play() return; } + // check to see if the ribbon timer triggered to end the ribbon + if (m_inDash) { + if (m_dashTimer.onEnd()) { + endDash(); + } + Leds::setIndex(m_ledPos, m_colorset.get(0)); + return; + } + // check the alarm to toggle the light AlarmID id = m_blinkTimer.alarm(); @@ -89,8 +109,11 @@ void BasicPattern::triggerGap() // next frame will be a gap m_gapTimer.restart(1); m_inGap = true; + } else { + triggerDash(); } m_groupCounter = 0; + } void BasicPattern::endGap() @@ -98,6 +121,10 @@ void BasicPattern::endGap() // next frame will not be a gap m_blinkTimer.restart(1); m_inGap = false; + if (!m_dashTriggered) { + triggerDash(); + } + m_dashTriggered = false; // Here we perform logic for repeating groups if (m_repeatCounter > 0) { // the repeat counter starts at group size and counts down @@ -118,16 +145,41 @@ void BasicPattern::endGap() // here is additional logic for when there is a refelct index set if (m_reflectIndex) { // this sets the colorset to the color after the central color in the previous reflection - m_colorset.skip(-(int32_t)((m_colorset.numColors() - m_reflectIndex) + 1)); + m_colorset.skip(-(int32_t)((m_colorset.numColors() - m_reflectIndex) + 1 + m_groupSize)); m_reflect = !m_reflect; return; } } } +void BasicPattern::triggerDash() +{ + if (m_dashDuration > 0) { + //next frame will be a ribbon + m_dashTimer.restart(1); + m_inDash = true; + } +} + +void BasicPattern::endDash() +{ + m_blinkTimer.restart(1); + m_inDash = false; + m_dashTriggered = true; + if (m_gapDuration) { + triggerGap(); + } + if (!m_groupSize) { + m_colorset.setCurIndex(0); + } +} + void BasicPattern::onBlinkOn() { // set the target led with the given color + if ( m_dashDuration && m_colorset.onEnd()) { + m_colorset.skip(); + } Leds::setIndex(m_ledPos, m_reflect ? m_colorset.getPrev() : m_colorset.getNext()); } diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.h b/VortexEngine/src/Patterns/Single/BasicPattern.h index ac9132f979..05cf353b1b 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.h +++ b/VortexEngine/src/Patterns/Single/BasicPattern.h @@ -23,6 +23,9 @@ class BasicPattern : public Pattern virtual void triggerGap(); virtual void endGap(); + virtual void triggerDash(); + virtual void endDash(); + // whether in the gap bool inGap() const { return m_inGap; } @@ -30,6 +33,7 @@ class BasicPattern : public Pattern uint8_t m_onDuration; uint8_t m_offDuration; uint8_t m_gapDuration; + uint8_t m_dashDuration; // the duration the light is on/off for uint8_t m_groupSize; uint8_t m_reflectIndex; @@ -48,7 +52,10 @@ class BasicPattern : public Pattern // the blink timer Timer m_blinkTimer; Timer m_gapTimer; + Timer m_dashTimer; bool m_inGap; + bool m_inDash; + bool m_dashTriggered; }; #endif From a46b10236273f9384b80589869f68edbcd59ae4d Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 10 May 2023 18:45:44 -0700 Subject: [PATCH 05/85] Fixed vortexlib and mode apis --- VortexEngine/Makefile | 2 +- VortexEngine/VortexLib/VortexLib.cpp | 11 ++++++++--- VortexEngine/src/Modes/Mode.h | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/VortexEngine/Makefile b/VortexEngine/Makefile index 295cbc2478..754c20e72e 100644 --- a/VortexEngine/Makefile +++ b/VortexEngine/Makefile @@ -17,7 +17,7 @@ MAKE=make RM=rm -rf RANLIB=ranlib -CFLAGS=-O2 -Wall +CFLAGS=-O2 -g -Wall # compiler defines DEFINES=\ diff --git a/VortexEngine/VortexLib/VortexLib.cpp b/VortexEngine/VortexLib/VortexLib.cpp index 1b89edf3b1..a894a6b72d 100644 --- a/VortexEngine/VortexLib/VortexLib.cpp +++ b/VortexEngine/VortexLib/VortexLib.cpp @@ -541,6 +541,7 @@ bool Vortex::setPattern(PatternID id, const PatternArgs *args, const Colorset *s if (!pMode->setPattern(id, LED_ANY, args, set)) { return false; } + pMode->init(); return !save || doSave(); } @@ -597,6 +598,7 @@ bool Vortex::setPatternAt(LedPos pos, PatternID id, if (!pMode->setPattern(id, pos, args, set)) { return false; } + pMode->init(); return !save || doSave(); } @@ -619,6 +621,7 @@ bool Vortex::setColorset(LedPos pos, const Colorset &set, bool save) if (!pMode->setColorset(set, pos)) { return false; } + pMode->init(); return !save || doSave(); } @@ -642,11 +645,13 @@ bool Vortex::setPatternArgs(LedPos pos, PatternArgs &args, bool save) if (!pMode) { return false; } - Pattern *pat = pMode->getPattern(pos); - if (!pat) { + // there's no setPatternArgs for an LedPos in the engine, it's not + // useful there and ends up causing more bloat than it's worth. + // So we just force an args change by setting the pattern again + Colorset set(RGB_RED, RGB_GREEN); + if (!pMode->setPattern(PATTERN_FIRST, pos, &args)) { return false; } - pat->setArgs(args); // re-initialize the mode after changing pattern args pMode->init(); // save the new params diff --git a/VortexEngine/src/Modes/Mode.h b/VortexEngine/src/Modes/Mode.h index eb07b9b360..4d6cd1ae66 100644 --- a/VortexEngine/src/Modes/Mode.h +++ b/VortexEngine/src/Modes/Mode.h @@ -89,7 +89,7 @@ class Mode // get the pattern ID of the given pattern PatternID getPatternID(LedPos pos = LED_ANY) const; - // change a single led pattern + // change the pattern on a mode (NOTE: you may need to call init() after!) bool setPattern(PatternID pat, LedPos pos = LED_ANY, const PatternArgs *args = nullptr, const Colorset *set = nullptr); bool setPatternMap(LedMap pos, PatternID pat, const PatternArgs *args = nullptr, const Colorset *set = nullptr); From 936304722e38846280209f8a2f83c67576d9ded5 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 11 May 2023 01:31:51 -0700 Subject: [PATCH 06/85] fixed basicpattern change --- .../src/Patterns/Single/BasicPattern.cpp | 56 +------------------ .../src/Patterns/Single/BasicPattern.h | 7 --- 2 files changed, 2 insertions(+), 61 deletions(-) diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 628f49c3b0..522da9d240 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -9,7 +9,6 @@ BasicPattern::BasicPattern(const PatternArgs &args) : m_onDuration(0), m_offDuration(0), m_gapDuration(0), - m_dashDuration(30), m_groupSize(0), m_reflectIndex(0), m_repeatGroup(0), @@ -19,10 +18,7 @@ BasicPattern::BasicPattern(const PatternArgs &args) : m_reflect(false), m_blinkTimer(), m_gapTimer(), - m_inGap(false), - m_dashTimer(), - m_inDash(false), - m_dashTriggered(false) + m_inGap(false) { m_patternID = PATTERN_BASIC; REGISTER_ARG(m_onDuration); @@ -45,14 +41,10 @@ void BasicPattern::init() m_inGap = false; m_reflect = false; - m_inDash = false; - m_dashTriggered = false; // don't start the gap timer till we're in a gap m_gapTimer.init(TIMER_1_ALARM, m_gapDuration); - m_dashTimer.init(TIMER_1_ALARM, m_dashDuration); - // start the blink timer now m_blinkTimer.init(TIMER_2_ALARMS | TIMER_START, m_onDuration, m_offDuration); @@ -61,9 +53,6 @@ void BasicPattern::init() } else { m_realGroupSize = m_groupSize; } - if (m_dashDuration && m_colorset.numColors() > 1) { - m_realGroupSize -= 1; - } m_groupCounter = 0; m_repeatCounter = m_repeatGroup; } @@ -79,15 +68,6 @@ void BasicPattern::play() return; } - // check to see if the ribbon timer triggered to end the ribbon - if (m_inDash) { - if (m_dashTimer.onEnd()) { - endDash(); - } - Leds::setIndex(m_ledPos, m_colorset.get(0)); - return; - } - // check the alarm to toggle the light AlarmID id = m_blinkTimer.alarm(); @@ -109,11 +89,8 @@ void BasicPattern::triggerGap() // next frame will be a gap m_gapTimer.restart(1); m_inGap = true; - } else { - triggerDash(); } m_groupCounter = 0; - } void BasicPattern::endGap() @@ -121,10 +98,6 @@ void BasicPattern::endGap() // next frame will not be a gap m_blinkTimer.restart(1); m_inGap = false; - if (!m_dashTriggered) { - triggerDash(); - } - m_dashTriggered = false; // Here we perform logic for repeating groups if (m_repeatCounter > 0) { // the repeat counter starts at group size and counts down @@ -145,41 +118,16 @@ void BasicPattern::endGap() // here is additional logic for when there is a refelct index set if (m_reflectIndex) { // this sets the colorset to the color after the central color in the previous reflection - m_colorset.skip(-(int32_t)((m_colorset.numColors() - m_reflectIndex) + 1 + m_groupSize)); + m_colorset.skip(-(int32_t)((m_colorset.numColors() - m_reflectIndex) + 1)); m_reflect = !m_reflect; return; } } } -void BasicPattern::triggerDash() -{ - if (m_dashDuration > 0) { - //next frame will be a ribbon - m_dashTimer.restart(1); - m_inDash = true; - } -} - -void BasicPattern::endDash() -{ - m_blinkTimer.restart(1); - m_inDash = false; - m_dashTriggered = true; - if (m_gapDuration) { - triggerGap(); - } - if (!m_groupSize) { - m_colorset.setCurIndex(0); - } -} - void BasicPattern::onBlinkOn() { // set the target led with the given color - if ( m_dashDuration && m_colorset.onEnd()) { - m_colorset.skip(); - } Leds::setIndex(m_ledPos, m_reflect ? m_colorset.getPrev() : m_colorset.getNext()); } diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.h b/VortexEngine/src/Patterns/Single/BasicPattern.h index 05cf353b1b..ac9132f979 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.h +++ b/VortexEngine/src/Patterns/Single/BasicPattern.h @@ -23,9 +23,6 @@ class BasicPattern : public Pattern virtual void triggerGap(); virtual void endGap(); - virtual void triggerDash(); - virtual void endDash(); - // whether in the gap bool inGap() const { return m_inGap; } @@ -33,7 +30,6 @@ class BasicPattern : public Pattern uint8_t m_onDuration; uint8_t m_offDuration; uint8_t m_gapDuration; - uint8_t m_dashDuration; // the duration the light is on/off for uint8_t m_groupSize; uint8_t m_reflectIndex; @@ -52,10 +48,7 @@ class BasicPattern : public Pattern // the blink timer Timer m_blinkTimer; Timer m_gapTimer; - Timer m_dashTimer; bool m_inGap; - bool m_inDash; - bool m_dashTriggered; }; #endif From 5f88d7da9b695fbfa8d5265908f2461cac8bed02 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 18 Apr 2023 00:14:31 -0700 Subject: [PATCH 07/85] BasicPattern refactor (shane) --- .../src/Patterns/Single/BasicPattern.cpp | 45 +++++++++++++++++-- .../src/Patterns/Single/BasicPattern.h | 13 +++--- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 522da9d240..162b334b59 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -9,16 +9,20 @@ BasicPattern::BasicPattern(const PatternArgs &args) : m_onDuration(0), m_offDuration(0), m_gapDuration(0), + m_dashDuration(0), m_groupSize(0), m_reflectIndex(0), m_repeatGroup(0), m_realGroupSize(0), m_groupCounter(0), m_repeatCounter(0), - m_reflect(false), m_blinkTimer(), m_gapTimer(), - m_inGap(false) + m_dashTimer(), + m_inGap(false), + m_inDash(false), + m_dashTriggered(false), + m_reflect(false) { m_patternID = PATTERN_BASIC; REGISTER_ARG(m_onDuration); @@ -41,10 +45,15 @@ void BasicPattern::init() m_inGap = false; m_reflect = false; + m_inDash = false; + m_dashTriggered = false; // don't start the gap timer till we're in a gap m_gapTimer.init(TIMER_1_ALARM, m_gapDuration); + // don't start the dash timer till we're in a dash + m_dashTimer.init(TIMER_1_ALARM, m_dashDuration); + // start the blink timer now m_blinkTimer.init(TIMER_2_ALARMS | TIMER_START, m_onDuration, m_offDuration); @@ -68,6 +77,15 @@ void BasicPattern::play() return; } + // check to see if the ribbon timer triggered to end the ribbon + if (m_inDash) { + if (m_dashTimer.onEnd()) { + endDash(); + } + Leds::setIndex(m_ledPos, m_colorset.get(0)); + return; + } + // check the alarm to toggle the light AlarmID id = m_blinkTimer.alarm(); @@ -98,6 +116,10 @@ void BasicPattern::endGap() // next frame will not be a gap m_blinkTimer.restart(1); m_inGap = false; + if (!m_dashTriggered) { + triggerDash(); + } + m_dashTriggered = false; // Here we perform logic for repeating groups if (m_repeatCounter > 0) { // the repeat counter starts at group size and counts down @@ -118,13 +140,30 @@ void BasicPattern::endGap() // here is additional logic for when there is a refelct index set if (m_reflectIndex) { // this sets the colorset to the color after the central color in the previous reflection - m_colorset.skip(-(int32_t)((m_colorset.numColors() - m_reflectIndex) + 1)); + m_colorset.skip(-(int32_t)((m_colorset.numColors() - m_reflectIndex) + 1 + m_groupSize)); m_reflect = !m_reflect; return; } } } +void BasicPattern::triggerDash() +{ + if (m_dashDuration > 0) { + //next frame will be a ribbon + m_dashTimer.restart(1); + m_inDash = true; + } +} + +void BasicPattern::endDash() +{ + m_blinkTimer.restart(1); + m_inDash = false; + m_dashTriggered = true; + triggerGap(); +} + void BasicPattern::onBlinkOn() { // set the target led with the given color diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.h b/VortexEngine/src/Patterns/Single/BasicPattern.h index ac9132f979..b5caf49192 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.h +++ b/VortexEngine/src/Patterns/Single/BasicPattern.h @@ -11,7 +11,6 @@ class BasicPattern : public Pattern virtual ~BasicPattern(); virtual void init() override; - virtual void play() override; protected: @@ -23,6 +22,9 @@ class BasicPattern : public Pattern virtual void triggerGap(); virtual void endGap(); + virtual void triggerDash(); + virtual void endDash(); + // whether in the gap bool inGap() const { return m_inGap; } @@ -30,7 +32,7 @@ class BasicPattern : public Pattern uint8_t m_onDuration; uint8_t m_offDuration; uint8_t m_gapDuration; - // the duration the light is on/off for + uint8_t m_dashDuration; uint8_t m_groupSize; uint8_t m_reflectIndex; uint8_t m_repeatGroup; @@ -42,13 +44,14 @@ class BasicPattern : public Pattern // the repeat counter uint8_t m_repeatCounter; - // the reflection bool - bool m_reflect; - // the blink timer Timer m_blinkTimer; Timer m_gapTimer; + Timer m_dashTimer; bool m_inGap; + bool m_inDash; + bool m_dashTriggered; + bool m_reflect; }; #endif From ccea8b7d1b736e0368c980b2c01e98a98777c8e1 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 11 May 2023 19:05:20 -0700 Subject: [PATCH 08/85] work on basicpattern refactor --- .../src/Patterns/Single/BasicPattern.cpp | 158 +++--------------- .../src/Patterns/Single/BasicPattern.h | 50 +++--- VortexEngine/src/VortexEngine.cpp | 4 + 3 files changed, 54 insertions(+), 158 deletions(-) diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 162b334b59..da3e47fe0b 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -10,27 +10,15 @@ BasicPattern::BasicPattern(const PatternArgs &args) : m_offDuration(0), m_gapDuration(0), m_dashDuration(0), - m_groupSize(0), - m_reflectIndex(0), - m_repeatGroup(0), - m_realGroupSize(0), - m_groupCounter(0), - m_repeatCounter(0), + m_state(STATE_BLINK_ON), m_blinkTimer(), - m_gapTimer(), - m_dashTimer(), - m_inGap(false), - m_inDash(false), - m_dashTriggered(false), - m_reflect(false) + m_altTimer() { m_patternID = PATTERN_BASIC; REGISTER_ARG(m_onDuration); REGISTER_ARG(m_offDuration); REGISTER_ARG(m_gapDuration); - REGISTER_ARG(m_groupSize); - REGISTER_ARG(m_reflectIndex); - REGISTER_ARG(m_repeatGroup); + REGISTER_ARG(m_dashDuration); setArgs(args); } @@ -43,131 +31,40 @@ void BasicPattern::init() // run base pattern init logic Pattern::init(); - m_inGap = false; - m_reflect = false; - m_inDash = false; - m_dashTriggered = false; - - // don't start the gap timer till we're in a gap - m_gapTimer.init(TIMER_1_ALARM, m_gapDuration); - - // don't start the dash timer till we're in a dash - m_dashTimer.init(TIMER_1_ALARM, m_dashDuration); - // start the blink timer now m_blinkTimer.init(TIMER_2_ALARMS | TIMER_START, m_onDuration, m_offDuration); - - if (!m_groupSize) { - m_realGroupSize = m_colorset.numColors(); - } else { - m_realGroupSize = m_groupSize; - } - m_groupCounter = 0; - m_repeatCounter = m_repeatGroup; } void BasicPattern::play() { - if (m_inGap) { - // check to see if the gap timer triggered to end the gap - if (m_gapTimer.onEnd()) { - endGap(); - } - Leds::clearIndex(m_ledPos); - return; - } - - // check to see if the ribbon timer triggered to end the ribbon - if (m_inDash) { - if (m_dashTimer.onEnd()) { - endDash(); + switch (m_state) { + case STATE_BLINK_ON: + if (!m_onDuration) { + m_state = STATE_BLINK_OFF; + return; } - Leds::setIndex(m_ledPos, m_colorset.get(0)); - return; - } - - // check the alarm to toggle the light - AlarmID id = m_blinkTimer.alarm(); - - if (id == 0) { - // when timer 0 starts it's time to blink on onBlinkOn(); - } else if (id == 1) { - // when timer 1 starts it's time to blink off - onBlinkOff(); - } else if (m_blinkTimer.curAlarm() == 1 && m_blinkTimer.onEnd() && m_colorset.onEnd() && !m_realGroupSize) { - // trigger the gap in the pattern - triggerGap(); - } -} - -void BasicPattern::triggerGap() -{ - if (m_gapDuration > 0) { - // next frame will be a gap - m_gapTimer.restart(1); - m_inGap = true; - } - m_groupCounter = 0; -} - -void BasicPattern::endGap() -{ - // next frame will not be a gap - m_blinkTimer.restart(1); - m_inGap = false; - if (!m_dashTriggered) { - triggerDash(); - } - m_dashTriggered = false; - // Here we perform logic for repeating groups - if (m_repeatCounter > 0) { - // the repeat counter starts at group size and counts down - // each time an entire group has been displayed - m_repeatCounter--; - // to "repeat" we simply move the colorset back one group size - if (!m_reflectIndex) { - m_colorset.skip(-(int32_t)m_realGroupSize); - } else { - m_colorset.skip(-1); - m_reflect = !m_reflect; + m_state = STATE_ON; + break; + case STATE_ON: + if (m_blinkTimer.alarm() != 0) { + m_state = STATE_BLINK_OFF; } - // nothing more to do + // just do nothing return; - } - if (!m_repeatCounter) { - m_repeatCounter = m_repeatGroup; - // here is additional logic for when there is a refelct index set - if (m_reflectIndex) { - // this sets the colorset to the color after the central color in the previous reflection - m_colorset.skip(-(int32_t)((m_colorset.numColors() - m_reflectIndex) + 1 + m_groupSize)); - m_reflect = !m_reflect; - return; + case STATE_BLINK_OFF: + if (m_colorset.onEnd()) { } + break; + case STATE_DASH: + break; } } -void BasicPattern::triggerDash() -{ - if (m_dashDuration > 0) { - //next frame will be a ribbon - m_dashTimer.restart(1); - m_inDash = true; - } -} - -void BasicPattern::endDash() -{ - m_blinkTimer.restart(1); - m_inDash = false; - m_dashTriggered = true; - triggerGap(); -} - void BasicPattern::onBlinkOn() { // set the target led with the given color - Leds::setIndex(m_ledPos, m_reflect ? m_colorset.getPrev() : m_colorset.getNext()); + Leds::setIndex(m_ledPos, m_colorset.getNext()); } void BasicPattern::onBlinkOff() @@ -176,19 +73,4 @@ void BasicPattern::onBlinkOff() // clear the target led if there is an off duration Leds::clearIndex(m_ledPos); } - - // count a blink in the group - m_groupCounter++; - - if (m_reflectIndex && m_reflectIndex == m_groupCounter) { - // switch direction of colors - m_reflect = !m_reflect; - } - - // check if the group has reached the intended size - if (m_groupCounter >= m_realGroupSize && !m_reflectIndex) { - triggerGap(); - } else if (m_groupCounter == (m_reflectIndex * 2) - 1) { - triggerGap(); - } } diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.h b/VortexEngine/src/Patterns/Single/BasicPattern.h index b5caf49192..3a1a0aabce 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.h +++ b/VortexEngine/src/Patterns/Single/BasicPattern.h @@ -26,32 +26,42 @@ class BasicPattern : public Pattern virtual void endDash(); // whether in the gap - bool inGap() const { return m_inGap; } + bool inGap() const { return m_state == STATE_GAP; } - // the duration the light is on/off for + // the parameters of the pattern uint8_t m_onDuration; uint8_t m_offDuration; uint8_t m_gapDuration; uint8_t m_dashDuration; - uint8_t m_groupSize; - uint8_t m_reflectIndex; - uint8_t m_repeatGroup; - - // the real group size based on num colors - uint8_t m_realGroupSize; - // the counter for groups - uint8_t m_groupCounter; - // the repeat counter - uint8_t m_repeatCounter; - - // the blink timer + + // the various different blinking states the pattern can be in + enum PatternState : uint8_t + { + // the pattern is blinking on the next color in the set + STATE_BLINK_ON, + // the pattern is displaying a color + STATE_ON, + // the pattern is blinking off + STATE_BLINK_OFF, + // the pattern is off + STATE_OFF, + // the pattern is starting a gap after a colorset + STATE_BEGIN_GAP, + // the pattern is off for an extended gap between colorsets + STATE_IN_GAP, + // the pattern is beginning a dash after a colorset or gap + STATE_BEGIN_DASH, + // the pattern is displaying a dash with the first color in the colorset + STATE_DASH, + }; + + // the state of the current pattern + PatternState m_state; + + // the blink timer used to measure blink timings Timer m_blinkTimer; - Timer m_gapTimer; - Timer m_dashTimer; - bool m_inGap; - bool m_inDash; - bool m_dashTriggered; - bool m_reflect; + // the secondary timer used to time gaps and dashes + Timer m_altTimer; }; #endif diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index e45af3d522..fd4a9e2d37 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -15,6 +15,8 @@ #include +#include "VortexLib.h" + bool VortexEngine::init() { // all of the global controllers @@ -55,6 +57,8 @@ bool VortexEngine::init() return false; } + Vortex::setPattern(PATTERN_FIRST); + #if COMPRESSION_TEST == 1 compressionTest(); #endif From 95f722c7b2b13e0c125d1a4267f1204c5153b2c1 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 11 May 2023 22:01:33 -0700 Subject: [PATCH 09/85] probably the best working state I have it in --- VortexEngine/src/Modes/Mode.cpp | 1 + .../src/Patterns/Single/BasicPattern.cpp | 86 +++++++++++++------ .../src/Patterns/Single/BasicPattern.h | 32 +++---- VortexEngine/src/VortexEngine.cpp | 5 ++ 4 files changed, 83 insertions(+), 41 deletions(-) diff --git a/VortexEngine/src/Modes/Mode.cpp b/VortexEngine/src/Modes/Mode.cpp index eeea7cfb13..3a6e1b85dc 100644 --- a/VortexEngine/src/Modes/Mode.cpp +++ b/VortexEngine/src/Modes/Mode.cpp @@ -510,6 +510,7 @@ bool Mode::setPattern(PatternID pat, LedPos pos, const PatternArgs *args, const if (!setPattern(pat, pos, args, &newSet)) { return false; } + break; } // actually break here return true; diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index da3e47fe0b..65e53a6959 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -11,8 +11,7 @@ BasicPattern::BasicPattern(const PatternArgs &args) : m_gapDuration(0), m_dashDuration(0), m_state(STATE_BLINK_ON), - m_blinkTimer(), - m_altTimer() + m_blinkTimer() { m_patternID = PATTERN_BASIC; REGISTER_ARG(m_onDuration); @@ -31,46 +30,81 @@ void BasicPattern::init() // run base pattern init logic Pattern::init(); - // start the blink timer now - m_blinkTimer.init(TIMER_2_ALARMS | TIMER_START, m_onDuration, m_offDuration); + // if a dash is present then always start with the dash because + // it consumes the first color in the colorset + if (m_dashDuration > 0) { + m_state = STATE_BEGIN_DASH; + } + // if there's no on duration or dash duration the led is just disabled + if (!m_onDuration && !m_dashDuration) { + m_state = STATE_DISABLED; + } +} + +void BasicPattern::nextState(uint8_t timing) +{ + m_blinkTimer.init(TIMER_1_ALARM | TIMER_START, timing); + m_state = (PatternState)(m_state + 1); } void BasicPattern::play() { +replay: + // its kinda evolving as i go switch (m_state) { - case STATE_BLINK_ON: - if (!m_onDuration) { - m_state = STATE_BLINK_OFF; - return; - } - onBlinkOn(); - m_state = STATE_ON; - break; - case STATE_ON: - if (m_blinkTimer.alarm() != 0) { - m_state = STATE_BLINK_OFF; - } - // just do nothing + case STATE_DISABLED: return; + case STATE_BLINK_ON: + if (m_onDuration > 0) { onBlinkOn(); nextState(m_onDuration); return; } + m_state = STATE_BLINK_OFF; case STATE_BLINK_OFF: - if (m_colorset.onEnd()) { - } - break; - case STATE_DASH: + if (m_offDuration > 0) { onBlinkOff(); nextState(m_offDuration); return; } + if (!m_colorset.onEnd()) { m_state = STATE_BLINK_ON; goto replay; } + m_state = STATE_BEGIN_GAP; + case STATE_BEGIN_GAP: + if (m_gapDuration > 0) { beginGap(); nextState(m_gapDuration); return; } + m_state = STATE_BEGIN_DASH; + case STATE_BEGIN_DASH: + if (m_dashDuration > 0) { beginDash(); nextState(m_dashDuration); return; } + m_state = STATE_BEGIN_GAP2; + case STATE_BEGIN_GAP2: + if (m_dashDuration > 0 && m_gapDuration > 0) { beginGap(); nextState(m_gapDuration); return; } + m_state = STATE_BLINK_ON; + goto replay; + default: break; } + + if (m_blinkTimer.alarm() == -1) { + // no alarm triggered? + return; + } + + if (m_state == STATE_IN_GAP2 || (m_state == STATE_OFF && m_colorset.onEnd())) { + m_state = STATE_BLINK_ON; + } else { + m_state = (PatternState)(m_state + 1); + } + // recurse with the new state change + goto replay; } void BasicPattern::onBlinkOn() { - // set the target led with the given color Leds::setIndex(m_ledPos, m_colorset.getNext()); } void BasicPattern::onBlinkOff() { - if (m_offDuration > 0) { - // clear the target led if there is an off duration - Leds::clearIndex(m_ledPos); - } + Leds::clearIndex(m_ledPos); +} + +void BasicPattern::beginGap() +{ + Leds::clearIndex(m_ledPos); +} + +void BasicPattern::beginDash() +{ + Leds::setIndex(m_ledPos, m_colorset.getNext()); } diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.h b/VortexEngine/src/Patterns/Single/BasicPattern.h index 3a1a0aabce..eae63555b4 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.h +++ b/VortexEngine/src/Patterns/Single/BasicPattern.h @@ -17,16 +17,14 @@ class BasicPattern : public Pattern // when blinking off virtual void onBlinkOn(); virtual void onBlinkOff(); + virtual void beginGap(); + virtual void beginDash(); - // trigger the gap - virtual void triggerGap(); - virtual void endGap(); + // iterate to next state + void nextState(uint8_t timing); - virtual void triggerDash(); - virtual void endDash(); - - // whether in the gap - bool inGap() const { return m_state == STATE_GAP; } + // begin a state if we are in the correct state + //bool beginState(); // the parameters of the pattern uint8_t m_onDuration; @@ -37,22 +35,28 @@ class BasicPattern : public Pattern // the various different blinking states the pattern can be in enum PatternState : uint8_t { + // the led is disabled (there is no on or dash) + STATE_DISABLED, + // the pattern is blinking on the next color in the set STATE_BLINK_ON, - // the pattern is displaying a color STATE_ON, + // the pattern is blinking off STATE_BLINK_OFF, - // the pattern is off STATE_OFF, + // the pattern is starting a gap after a colorset STATE_BEGIN_GAP, - // the pattern is off for an extended gap between colorsets STATE_IN_GAP, + // the pattern is beginning a dash after a colorset or gap STATE_BEGIN_DASH, - // the pattern is displaying a dash with the first color in the colorset - STATE_DASH, + STATE_IN_DASH, + + // the pattern is starting a gap after a dash + STATE_BEGIN_GAP2, + STATE_IN_GAP2, }; // the state of the current pattern @@ -60,8 +64,6 @@ class BasicPattern : public Pattern // the blink timer used to measure blink timings Timer m_blinkTimer; - // the secondary timer used to time gaps and dashes - Timer m_altTimer; }; #endif diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index fd4a9e2d37..3c1282ce1a 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -13,6 +13,9 @@ #include "Leds/Leds.h" #include "Log/Log.h" +#include "Patterns/PatternArgs.h" +#include "Leds/LedTypes.h" + #include #include "VortexLib.h" @@ -58,6 +61,8 @@ bool VortexEngine::init() } Vortex::setPattern(PATTERN_FIRST); + PatternArgs args = PatternArgs(0, 0, 0, 1); + Vortex::setPatternArgs(LED_ALL, args); #if COMPRESSION_TEST == 1 compressionTest(); From 71f266ede5a599c30ef30a43c282e3c13b9ce175 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 11 May 2023 23:47:53 -0700 Subject: [PATCH 10/85] this one working quite well --- VortexEngine/src/Patterns/Single/BasicPattern.cpp | 14 +++++++++++--- VortexEngine/src/VortexEngine.cpp | 5 ++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 65e53a6959..d614fcc120 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -4,6 +4,8 @@ #include "../../Colors/Colorset.h" #include "../../Leds/Leds.h" +#include + BasicPattern::BasicPattern(const PatternArgs &args) : Pattern(args), m_onDuration(0), @@ -38,7 +40,7 @@ void BasicPattern::init() // if there's no on duration or dash duration the led is just disabled if (!m_onDuration && !m_dashDuration) { m_state = STATE_DISABLED; - } + } } void BasicPattern::nextState(uint8_t timing) @@ -59,7 +61,7 @@ void BasicPattern::play() m_state = STATE_BLINK_OFF; case STATE_BLINK_OFF: if (m_offDuration > 0) { onBlinkOff(); nextState(m_offDuration); return; } - if (!m_colorset.onEnd()) { m_state = STATE_BLINK_ON; goto replay; } + if (!m_colorset.onEnd() && m_onDuration > 0) { m_state = STATE_BLINK_ON; goto replay; } m_state = STATE_BEGIN_GAP; case STATE_BEGIN_GAP: if (m_gapDuration > 0) { beginGap(); nextState(m_gapDuration); return; } @@ -80,7 +82,9 @@ void BasicPattern::play() return; } - if (m_state == STATE_IN_GAP2 || (m_state == STATE_OFF && m_colorset.onEnd())) { + if (m_state == STATE_IN_GAP2 || (m_state == STATE_OFF && !m_colorset.onEnd())) { + m_state = STATE_BLINK_ON; + } else if (m_state == STATE_OFF && (m_colorset.onEnd() || m_colorset.numColors() == 1)) { m_state = STATE_BLINK_ON; } else { m_state = (PatternState)(m_state + 1); @@ -91,20 +95,24 @@ void BasicPattern::play() void BasicPattern::onBlinkOn() { + printf("on "); Leds::setIndex(m_ledPos, m_colorset.getNext()); } void BasicPattern::onBlinkOff() { + printf("off "); Leds::clearIndex(m_ledPos); } void BasicPattern::beginGap() { + printf("gap "); Leds::clearIndex(m_ledPos); } void BasicPattern::beginDash() { + printf("dash"); Leds::setIndex(m_ledPos, m_colorset.getNext()); } diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index 3c1282ce1a..c24b9bb1b7 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -14,6 +14,7 @@ #include "Log/Log.h" #include "Patterns/PatternArgs.h" +#include "Colors/Colorset.h" #include "Leds/LedTypes.h" #include @@ -61,8 +62,10 @@ bool VortexEngine::init() } Vortex::setPattern(PATTERN_FIRST); - PatternArgs args = PatternArgs(0, 0, 0, 1); + PatternArgs args = PatternArgs(0,1,0,1); + Colorset set(RGB_RED, RGB_GREEN); Vortex::setPatternArgs(LED_ALL, args); + Vortex::setColorset(LED_ALL, set); #if COMPRESSION_TEST == 1 compressionTest(); From 2ea6c6bde9e13229e2e4a2e65b1378f258519b6e Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 12 May 2023 12:25:12 -0700 Subject: [PATCH 11/85] fixed basic pattern works now --- .../src/Patterns/Single/BasicPattern.cpp | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index d614fcc120..389aaccd02 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -4,7 +4,12 @@ #include "../../Colors/Colorset.h" #include "../../Leds/Leds.h" +// uncomment me to print debug labels on the pattern states +#define DEBUG_BASIC_PATTERN + +#ifdef DEBUG_BASIC_PATTERN #include +#endif BasicPattern::BasicPattern(const PatternArgs &args) : Pattern(args), @@ -78,14 +83,37 @@ void BasicPattern::play() } if (m_blinkTimer.alarm() == -1) { +#ifdef DEBUG_BASIC_PATTERN + switch (m_state) { + case STATE_ON: + printf("on "); + break; + case STATE_OFF: + printf("off "); + break; + case STATE_IN_GAP: + printf("gap1"); + break; + case STATE_IN_DASH: + printf("dash"); + break; + case STATE_IN_GAP2: + printf("gap2"); + break; + } +#endif // no alarm triggered? return; } if (m_state == STATE_IN_GAP2 || (m_state == STATE_OFF && !m_colorset.onEnd())) { - m_state = STATE_BLINK_ON; + if (!m_onDuration && !m_gapDuration) { + m_state = STATE_BEGIN_DASH; + } else { + m_state = STATE_BLINK_ON; + } } else if (m_state == STATE_OFF && (m_colorset.onEnd() || m_colorset.numColors() == 1)) { - m_state = STATE_BLINK_ON; + m_state = STATE_BEGIN_GAP; } else { m_state = (PatternState)(m_state + 1); } @@ -95,24 +123,36 @@ void BasicPattern::play() void BasicPattern::onBlinkOn() { +#ifdef DEBUG_BASIC_PATTERN printf("on "); +#endif Leds::setIndex(m_ledPos, m_colorset.getNext()); } void BasicPattern::onBlinkOff() { +#ifdef DEBUG_BASIC_PATTERN printf("off "); +#endif Leds::clearIndex(m_ledPos); } void BasicPattern::beginGap() { - printf("gap "); +#ifdef DEBUG_BASIC_PATTERN + if (m_state == STATE_BEGIN_GAP) { + printf("gap1"); + } else { + printf("gap2"); + } +#endif Leds::clearIndex(m_ledPos); } void BasicPattern::beginDash() { +#ifdef DEBUG_BASIC_PATTERN printf("dash"); +#endif Leds::setIndex(m_ledPos, m_colorset.getNext()); } From 1dfcaf22edfff4d893d858b2c79202b7426ad6f0 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 12 May 2023 12:27:32 -0700 Subject: [PATCH 12/85] fixed warning --- VortexEngine/src/Patterns/Single/BasicPattern.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 389aaccd02..0db088ae7a 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -100,6 +100,8 @@ void BasicPattern::play() case STATE_IN_GAP2: printf("gap2"); break; + default: + break; } #endif // no alarm triggered? From 9ad44a6d6219415a38ba041ab721d7c7630d3f8f Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 12 May 2023 13:40:06 -0700 Subject: [PATCH 13/85] fixes --- .../src/Patterns/Single/BasicPattern.cpp | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 0db088ae7a..2a5ab4beef 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -5,7 +5,7 @@ #include "../../Leds/Leds.h" // uncomment me to print debug labels on the pattern states -#define DEBUG_BASIC_PATTERN +//#define DEBUG_BASIC_PATTERN #ifdef DEBUG_BASIC_PATTERN #include @@ -56,7 +56,12 @@ void BasicPattern::nextState(uint8_t timing) void BasicPattern::play() { + + // Sometimes the pattern needs to cycle multiple states in a single frame so + // instead of using a loop or recursion I have just used a simple goto replay: + + // its kinda evolving as i go switch (m_state) { case STATE_DISABLED: @@ -65,8 +70,12 @@ void BasicPattern::play() if (m_onDuration > 0) { onBlinkOn(); nextState(m_onDuration); return; } m_state = STATE_BLINK_OFF; case STATE_BLINK_OFF: - if (m_offDuration > 0) { onBlinkOff(); nextState(m_offDuration); return; } - if (!m_colorset.onEnd() && m_onDuration > 0) { m_state = STATE_BLINK_ON; goto replay; } + // the whole 'should blink off' situation is tricky because we might need + // to go back to blinking on if or colorset isn't at the end yet + if (!m_colorset.onEnd() || (!m_gapDuration && !m_dashDuration)) { + if (m_offDuration > 0) { onBlinkOff(); nextState(m_offDuration); return; } + if (!m_colorset.onEnd() && m_onDuration > 0) { m_state = STATE_BLINK_ON; goto replay; } + } m_state = STATE_BEGIN_GAP; case STATE_BEGIN_GAP: if (m_gapDuration > 0) { beginGap(); nextState(m_gapDuration); return; } @@ -85,23 +94,12 @@ void BasicPattern::play() if (m_blinkTimer.alarm() == -1) { #ifdef DEBUG_BASIC_PATTERN switch (m_state) { - case STATE_ON: - printf("on "); - break; - case STATE_OFF: - printf("off "); - break; - case STATE_IN_GAP: - printf("gap1"); - break; - case STATE_IN_DASH: - printf("dash"); - break; - case STATE_IN_GAP2: - printf("gap2"); - break; - default: - break; + case STATE_ON: printf("on "); break; + case STATE_OFF: printf("off "); break; + case STATE_IN_GAP: printf("gap1"); break; + case STATE_IN_DASH: printf("dash"); break; + case STATE_IN_GAP2: printf("gap2"); break; + default: break; } #endif // no alarm triggered? @@ -142,11 +140,7 @@ void BasicPattern::onBlinkOff() void BasicPattern::beginGap() { #ifdef DEBUG_BASIC_PATTERN - if (m_state == STATE_BEGIN_GAP) { - printf("gap1"); - } else { - printf("gap2"); - } + printf("gap%d", (m_state == STATE_BEGIN_GAP) ? 1 : 2); #endif Leds::clearIndex(m_ledPos); } From 3e80cdd40a7ccb532c7aa0301990ecd2b9e2fdd0 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 12 May 2023 13:48:36 -0700 Subject: [PATCH 14/85] removed debug code --- VortexEngine/src/Modes/Mode.cpp | 1 - VortexEngine/src/VortexEngine.cpp | 6 ------ 2 files changed, 7 deletions(-) diff --git a/VortexEngine/src/Modes/Mode.cpp b/VortexEngine/src/Modes/Mode.cpp index 3a6e1b85dc..eeea7cfb13 100644 --- a/VortexEngine/src/Modes/Mode.cpp +++ b/VortexEngine/src/Modes/Mode.cpp @@ -510,7 +510,6 @@ bool Mode::setPattern(PatternID pat, LedPos pos, const PatternArgs *args, const if (!setPattern(pat, pos, args, &newSet)) { return false; } - break; } // actually break here return true; diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index c24b9bb1b7..d6abe09acd 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -61,12 +61,6 @@ bool VortexEngine::init() return false; } - Vortex::setPattern(PATTERN_FIRST); - PatternArgs args = PatternArgs(0,1,0,1); - Colorset set(RGB_RED, RGB_GREEN); - Vortex::setPatternArgs(LED_ALL, args); - Vortex::setColorset(LED_ALL, set); - #if COMPRESSION_TEST == 1 compressionTest(); #endif From dc4c21e23d84012e930ee939c92ca6d5694013ab Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 12 May 2023 15:44:09 -0700 Subject: [PATCH 15/85] Fixes for pattern and stuff --- VortexEngine/VortexEngine.vcxproj | 2 - VortexEngine/VortexEngine.vcxproj.filters | 6 -- VortexEngine/src/Modes/DefaultModes.cpp | 2 +- VortexEngine/src/Patterns/PatternBuilder.cpp | 15 ++--- VortexEngine/src/Patterns/Patterns.h | 1 - .../src/Patterns/Single/BasicPattern.cpp | 21 ++++-- .../src/Patterns/Single/BasicPattern.h | 6 +- .../src/Patterns/Single/DashDopsPattern.cpp | 66 ------------------- .../src/Patterns/Single/DashDopsPattern.h | 29 -------- 9 files changed, 25 insertions(+), 123 deletions(-) delete mode 100644 VortexEngine/src/Patterns/Single/DashDopsPattern.cpp delete mode 100644 VortexEngine/src/Patterns/Single/DashDopsPattern.h diff --git a/VortexEngine/VortexEngine.vcxproj b/VortexEngine/VortexEngine.vcxproj index a26c790fca..2f72e95b84 100644 --- a/VortexEngine/VortexEngine.vcxproj +++ b/VortexEngine/VortexEngine.vcxproj @@ -198,7 +198,6 @@ - @@ -272,7 +271,6 @@ - diff --git a/VortexEngine/VortexEngine.vcxproj.filters b/VortexEngine/VortexEngine.vcxproj.filters index 86bba18d4b..6894e7bd3a 100644 --- a/VortexEngine/VortexEngine.vcxproj.filters +++ b/VortexEngine/VortexEngine.vcxproj.filters @@ -279,9 +279,6 @@ Source Files\Patterns\Single - - Source Files\Patterns\Single - Source Files\Patterns\Single @@ -497,9 +494,6 @@ Header Files\Patterns\Single - - Header Files\Patterns\Single - Header Files\Patterns\Single diff --git a/VortexEngine/src/Modes/DefaultModes.cpp b/VortexEngine/src/Modes/DefaultModes.cpp index 8ed36d7c2c..55074f45f8 100644 --- a/VortexEngine/src/Modes/DefaultModes.cpp +++ b/VortexEngine/src/Modes/DefaultModes.cpp @@ -6,7 +6,7 @@ // the gloveset upon factory reset const default_mode_entry default_modes[] = { { - PATTERN_DOPS, 5, { + PATTERN_DASHCRUSH, 5, { RGB_RED, RGB_GREEN, RGB_BLUE, diff --git a/VortexEngine/src/Patterns/PatternBuilder.cpp b/VortexEngine/src/Patterns/PatternBuilder.cpp index 8abc88c2ab..4e470b8d3b 100644 --- a/VortexEngine/src/Patterns/PatternBuilder.cpp +++ b/VortexEngine/src/Patterns/PatternBuilder.cpp @@ -30,7 +30,6 @@ #include "Multi/MateriaPattern.h" #include "Single/TracerPattern.h" -#include "Single/DashDopsPattern.h" #include "Single/BasicPattern.h" #include "Single/BlendPattern.h" #include "Single/SolidPattern.h" @@ -139,16 +138,13 @@ PatternArgs PatternBuilder::getDefaultArgs(PatternID id) case PATTERN_ULTRADOPS2: return PatternArgs(ULTRADOPS_ON_DURATION, ULTRADOPS_OFF_DURATION, 25); case PATTERN_BLINKIE: return PatternArgs(3, 6, 60); case PATTERN_GHOSTCRUSH: return PatternArgs(4, 1, 55); - case PATTERN_BASIC: return PatternArgs(4, 4, 35, 2, 0, 2); - case PATTERN_BASIC2: return PatternArgs(DOPS_ON_DURATION, STROBE_OFF_DURATION, 10, 1, 0, 2); - case PATTERN_BRACKETS: return PatternArgs(4, 4, 20, 1, 2, 1); - case PATTERN_SANDWICH: return PatternArgs(4, 4, 20, 1, 3, 1); + case PATTERN_BASIC: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 35, 10); case PATTERN_BLEND: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 0, 0, 0, 0, 0, 1); case PATTERN_BLENDSTROBE: return PatternArgs(STROBE_ON_DURATION, 28, 0, 0, 0, 0, 0, 1); case PATTERN_COMPLEMENTARY_BLEND: return PatternArgs(2, 13, 0, 0, 0, 0, 0, 2); case PATTERN_COMPLEMENTARY_BLENDSTROBE: return PatternArgs(STROBE_ON_DURATION, 28, 0, 0, 0, 0, 0, 2); - case PATTERN_DASHDOPS: return PatternArgs(30, 2, 7); - case PATTERN_DASHCRUSH: return PatternArgs(30, 4, 1); + case PATTERN_DASHDOPS: return PatternArgs(2, 7, 7, 30); + case PATTERN_DASHCRUSH: return PatternArgs(4, 1, 10, 30); case PATTERN_TRACER: return PatternArgs(16, 3); case PATTERN_RIBBON: return PatternArgs(RIBBON_DURATION); case PATTERN_MINIRIBBON: return PatternArgs(3); @@ -224,17 +220,16 @@ Pattern *PatternBuilder::generate(PatternID id, const PatternArgs *userArgs) case PATTERN_BLINKIE: case PATTERN_GHOSTCRUSH: case PATTERN_BASIC: - case PATTERN_BASIC2: case PATTERN_BRACKETS: case PATTERN_SANDWICH: case PATTERN_RIBBON: + case PATTERN_DASHDOPS: + case PATTERN_DASHCRUSH: case PATTERN_MINIRIBBON: return new BasicPattern(args); case PATTERN_BLEND: case PATTERN_BLENDSTROBE: case PATTERN_COMPLEMENTARY_BLEND: case PATTERN_COMPLEMENTARY_BLENDSTROBE: return new BlendPattern(args); - case PATTERN_DASHDOPS: - case PATTERN_DASHCRUSH: return new DashDopsPattern(args); case PATTERN_TRACER: return new TracerPattern(args); case PATTERN_SOLID: return new SolidPattern(args); diff --git a/VortexEngine/src/Patterns/Patterns.h b/VortexEngine/src/Patterns/Patterns.h index 3e29fc8c82..2b11fb0d65 100644 --- a/VortexEngine/src/Patterns/Patterns.h +++ b/VortexEngine/src/Patterns/Patterns.h @@ -39,7 +39,6 @@ enum PatternID : uint8_t PATTERN_BLINKIE, PATTERN_GHOSTCRUSH, PATTERN_BASIC, - PATTERN_BASIC2, PATTERN_BRACKETS, PATTERN_SANDWICH, PATTERN_BLEND, diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 2a5ab4beef..704a0534f8 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -17,6 +17,8 @@ BasicPattern::BasicPattern(const PatternArgs &args) : m_offDuration(0), m_gapDuration(0), m_dashDuration(0), + m_groupSize(0), + m_groupCounter(0), m_state(STATE_BLINK_ON), m_blinkTimer() { @@ -25,6 +27,7 @@ BasicPattern::BasicPattern(const PatternArgs &args) : REGISTER_ARG(m_offDuration); REGISTER_ARG(m_gapDuration); REGISTER_ARG(m_dashDuration); + REGISTER_ARG(m_groupSize); setArgs(args); } @@ -43,9 +46,10 @@ void BasicPattern::init() m_state = STATE_BEGIN_DASH; } // if there's no on duration or dash duration the led is just disabled - if (!m_onDuration && !m_dashDuration) { + if ((!m_onDuration && !m_dashDuration) || !m_colorset.numColors()) { m_state = STATE_DISABLED; } + m_groupCounter = m_groupSize ? m_groupSize : m_colorset.numColors(); } void BasicPattern::nextState(uint8_t timing) @@ -67,17 +71,18 @@ void BasicPattern::play() case STATE_DISABLED: return; case STATE_BLINK_ON: - if (m_onDuration > 0) { onBlinkOn(); nextState(m_onDuration); return; } + if (m_onDuration > 0) { onBlinkOn(); --m_groupCounter; nextState(m_onDuration); return; } m_state = STATE_BLINK_OFF; case STATE_BLINK_OFF: // the whole 'should blink off' situation is tricky because we might need // to go back to blinking on if or colorset isn't at the end yet - if (!m_colorset.onEnd() || (!m_gapDuration && !m_dashDuration)) { + if (m_groupCounter > 0 || (!m_gapDuration && !m_dashDuration)) { if (m_offDuration > 0) { onBlinkOff(); nextState(m_offDuration); return; } - if (!m_colorset.onEnd() && m_onDuration > 0) { m_state = STATE_BLINK_ON; goto replay; } + if (m_groupCounter > 0 && m_onDuration > 0) { m_state = STATE_BLINK_ON; goto replay; } } m_state = STATE_BEGIN_GAP; case STATE_BEGIN_GAP: + m_groupCounter = m_groupSize ? m_groupSize : m_colorset.numColors(); if (m_gapDuration > 0) { beginGap(); nextState(m_gapDuration); return; } m_state = STATE_BEGIN_DASH; case STATE_BEGIN_DASH: @@ -106,6 +111,8 @@ void BasicPattern::play() return; } + // this just transitions the state into the next state, with some edge conditions for + // transitioning to different states under certain circumstances if (m_state == STATE_IN_GAP2 || (m_state == STATE_OFF && !m_colorset.onEnd())) { if (!m_onDuration && !m_gapDuration) { m_state = STATE_BEGIN_DASH; @@ -113,7 +120,11 @@ void BasicPattern::play() m_state = STATE_BLINK_ON; } } else if (m_state == STATE_OFF && (m_colorset.onEnd() || m_colorset.numColors() == 1)) { - m_state = STATE_BEGIN_GAP; + if (m_groupCounter > 0) { + m_state = STATE_BLINK_ON; + } else { + m_state = STATE_BEGIN_GAP; + } } else { m_state = (PatternState)(m_state + 1); } diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.h b/VortexEngine/src/Patterns/Single/BasicPattern.h index eae63555b4..809844cc8f 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.h +++ b/VortexEngine/src/Patterns/Single/BasicPattern.h @@ -23,14 +23,14 @@ class BasicPattern : public Pattern // iterate to next state void nextState(uint8_t timing); - // begin a state if we are in the correct state - //bool beginState(); - // the parameters of the pattern uint8_t m_onDuration; uint8_t m_offDuration; uint8_t m_gapDuration; uint8_t m_dashDuration; + uint8_t m_groupSize; + + uint8_t m_groupCounter; // the various different blinking states the pattern can be in enum PatternState : uint8_t diff --git a/VortexEngine/src/Patterns/Single/DashDopsPattern.cpp b/VortexEngine/src/Patterns/Single/DashDopsPattern.cpp deleted file mode 100644 index 826e3917e8..0000000000 --- a/VortexEngine/src/Patterns/Single/DashDopsPattern.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "DashDopsPattern.h" - -#include "../../Leds/Leds.h" - -DashDopsPattern::DashDopsPattern(const PatternArgs &args) : - SingleLedPattern(args), - m_dashDuration(0), - m_dotDuration(0), - m_offDuration(0), - m_blinkTimer(), - m_dotColor(0) -{ - m_patternID = PATTERN_DASHDOPS; - REGISTER_ARG(m_dashDuration); - REGISTER_ARG(m_dotDuration); - REGISTER_ARG(m_offDuration); - setArgs(args); -} - -DashDopsPattern::~DashDopsPattern() -{ -} - -void DashDopsPattern::init() -{ - // run base pattern init logic - SingleLedPattern::init(); - - // reset the blink timer entirely - m_blinkTimer.reset(); - - // add the alarms for on then off - m_blinkTimer.addAlarm(m_dashDuration); - m_blinkTimer.addAlarm(m_offDuration); - - if (m_colorset.numColors() > 1) { - for (uint8_t dots = 0; dots < m_colorset.numColors() - 1; ++dots) { - m_blinkTimer.addAlarm(m_dotDuration); - m_blinkTimer.addAlarm(m_offDuration); - } - } - - // start the blink timer from the current frame - m_blinkTimer.start(); -} - -// pure virtual must override the play function -void DashDopsPattern::play() -{ - AlarmID id = m_blinkTimer.alarm(); - if (id == 0) { - // draw the tracer background - Leds::setIndex(m_ledPos, m_colorset.get(0)); - } else if (id % 2 == 1) { - Leds::clearIndex(m_ledPos); - } else if (id % 2 == 0) { - // display dot, never display the tracer color which - // is at index 0 of the colorset - Leds::setIndex(m_ledPos, m_colorset.get(1 + m_dotColor)); - // prevent division by 0 - if (m_colorset.numColors() > 1) { - // increment tracer counter and wrap at 1 less than num colors - m_dotColor = (m_dotColor + 1) % (m_colorset.numColors() - 1); - } - } -} diff --git a/VortexEngine/src/Patterns/Single/DashDopsPattern.h b/VortexEngine/src/Patterns/Single/DashDopsPattern.h deleted file mode 100644 index 7da0b2289c..0000000000 --- a/VortexEngine/src/Patterns/Single/DashDopsPattern.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef DASHDOPS_PATTERN_H -#define DASHDOPS_PATTERN_H - -#include "SingleLedPattern.h" - -#include "../../Time/Timer.h" - -class DashDopsPattern : public SingleLedPattern -{ -public: - DashDopsPattern(const PatternArgs &args); - virtual ~DashDopsPattern(); - - virtual void init() override; - - virtual void play() override; - -private: - // the duration the light is on/off for - uint8_t m_dashDuration; - uint8_t m_dotDuration; - uint8_t m_offDuration; - // the timer for performing blinks - Timer m_blinkTimer; - // the counter for dot color - uint8_t m_dotColor; -}; - -#endif From 5be9e369f826525dc2729f93d421b2494e05dccc Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 12 May 2023 16:17:35 -0700 Subject: [PATCH 16/85] fixed issues and improved printing mechanism --- .../src/Patterns/Single/BasicPattern.cpp | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 704a0534f8..2fea83e983 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -5,10 +5,11 @@ #include "../../Leds/Leds.h" // uncomment me to print debug labels on the pattern states -//#define DEBUG_BASIC_PATTERN +#define DEBUG_BASIC_PATTERN #ifdef DEBUG_BASIC_PATTERN #include +uint64_t lastPrint = 0; #endif BasicPattern::BasicPattern(const PatternArgs &args) : @@ -49,7 +50,7 @@ void BasicPattern::init() if ((!m_onDuration && !m_dashDuration) || !m_colorset.numColors()) { m_state = STATE_DISABLED; } - m_groupCounter = m_groupSize ? m_groupSize : m_colorset.numColors(); + m_groupCounter = m_groupSize ? m_groupSize : (m_colorset.numColors() - (m_dashDuration != 0)); } void BasicPattern::nextState(uint8_t timing) @@ -82,7 +83,7 @@ void BasicPattern::play() } m_state = STATE_BEGIN_GAP; case STATE_BEGIN_GAP: - m_groupCounter = m_groupSize ? m_groupSize : m_colorset.numColors(); + m_groupCounter = m_groupSize ? m_groupSize : (m_colorset.numColors() - (m_dashDuration != 0)); if (m_gapDuration > 0) { beginGap(); nextState(m_gapDuration); return; } m_state = STATE_BEGIN_DASH; case STATE_BEGIN_DASH: @@ -98,14 +99,18 @@ void BasicPattern::play() if (m_blinkTimer.alarm() == -1) { #ifdef DEBUG_BASIC_PATTERN + if (lastPrint == Time::getCurtime()) { + return; + } switch (m_state) { case STATE_ON: printf("on "); break; case STATE_OFF: printf("off "); break; case STATE_IN_GAP: printf("gap1"); break; case STATE_IN_DASH: printf("dash"); break; case STATE_IN_GAP2: printf("gap2"); break; - default: break; + default: return; } + lastPrint = Time::getCurtime(); #endif // no alarm triggered? return; @@ -113,13 +118,13 @@ void BasicPattern::play() // this just transitions the state into the next state, with some edge conditions for // transitioning to different states under certain circumstances - if (m_state == STATE_IN_GAP2 || (m_state == STATE_OFF && !m_colorset.onEnd())) { + if (m_state == STATE_IN_GAP2 || (m_state == STATE_OFF && m_groupCounter > 0)) { if (!m_onDuration && !m_gapDuration) { m_state = STATE_BEGIN_DASH; } else { m_state = STATE_BLINK_ON; } - } else if (m_state == STATE_OFF && (m_colorset.onEnd() || m_colorset.numColors() == 1)) { + } else if (m_state == STATE_OFF && (!m_groupCounter || m_colorset.numColors() == 1)) { if (m_groupCounter > 0) { m_state = STATE_BLINK_ON; } else { @@ -135,7 +140,10 @@ void BasicPattern::play() void BasicPattern::onBlinkOn() { #ifdef DEBUG_BASIC_PATTERN - printf("on "); + if (lastPrint != Time::getCurtime()) { + printf("on "); + lastPrint = Time::getCurtime(); + } #endif Leds::setIndex(m_ledPos, m_colorset.getNext()); } @@ -143,7 +151,10 @@ void BasicPattern::onBlinkOn() void BasicPattern::onBlinkOff() { #ifdef DEBUG_BASIC_PATTERN - printf("off "); + if (lastPrint != Time::getCurtime()) { + printf("off "); + lastPrint = Time::getCurtime(); + } #endif Leds::clearIndex(m_ledPos); } @@ -151,7 +162,10 @@ void BasicPattern::onBlinkOff() void BasicPattern::beginGap() { #ifdef DEBUG_BASIC_PATTERN - printf("gap%d", (m_state == STATE_BEGIN_GAP) ? 1 : 2); + if (lastPrint != Time::getCurtime()) { + printf("gap%d", (m_state == STATE_BEGIN_GAP) ? 1 : 2); + lastPrint = Time::getCurtime(); + } #endif Leds::clearIndex(m_ledPos); } @@ -159,7 +173,10 @@ void BasicPattern::beginGap() void BasicPattern::beginDash() { #ifdef DEBUG_BASIC_PATTERN - printf("dash"); + if (lastPrint != Time::getCurtime()) { + printf("dash"); + lastPrint = Time::getCurtime(); + } #endif Leds::setIndex(m_ledPos, m_colorset.getNext()); } From c424c67176826e7d22ebbb8b2bc2d230fd434ecd Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 12 May 2023 16:49:51 -0700 Subject: [PATCH 17/85] more cleanup --- .../src/Patterns/Single/BasicPattern.cpp | 36 +++++++------------ 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 2fea83e983..750a893f8e 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -4,8 +4,10 @@ #include "../../Colors/Colorset.h" #include "../../Leds/Leds.h" -// uncomment me to print debug labels on the pattern states -#define DEBUG_BASIC_PATTERN +// uncomment me to print debug labels on the pattern states, this is useful if you +// are debugging a pattern strip from the command line and want to see what state +// the pattern is in each tick of the pattern +//#define DEBUG_BASIC_PATTERN #ifdef DEBUG_BASIC_PATTERN #include @@ -61,12 +63,10 @@ void BasicPattern::nextState(uint8_t timing) void BasicPattern::play() { - // Sometimes the pattern needs to cycle multiple states in a single frame so // instead of using a loop or recursion I have just used a simple goto replay: - // its kinda evolving as i go switch (m_state) { case STATE_DISABLED: @@ -99,16 +99,14 @@ void BasicPattern::play() if (m_blinkTimer.alarm() == -1) { #ifdef DEBUG_BASIC_PATTERN - if (lastPrint == Time::getCurtime()) { - return; - } + if (lastPrint == Time::getCurtime()) return; switch (m_state) { - case STATE_ON: printf("on "); break; - case STATE_OFF: printf("off "); break; - case STATE_IN_GAP: printf("gap1"); break; - case STATE_IN_DASH: printf("dash"); break; - case STATE_IN_GAP2: printf("gap2"); break; - default: return; + case STATE_ON: printf("on "); break; + case STATE_OFF: printf("off "); break; + case STATE_IN_GAP: printf("gap1"); break; + case STATE_IN_DASH: printf("dash"); break; + case STATE_IN_GAP2: printf("gap2"); break; + default: return; } lastPrint = Time::getCurtime(); #endif @@ -119,17 +117,9 @@ void BasicPattern::play() // this just transitions the state into the next state, with some edge conditions for // transitioning to different states under certain circumstances if (m_state == STATE_IN_GAP2 || (m_state == STATE_OFF && m_groupCounter > 0)) { - if (!m_onDuration && !m_gapDuration) { - m_state = STATE_BEGIN_DASH; - } else { - m_state = STATE_BLINK_ON; - } + m_state = (!m_onDuration && !m_gapDuration) ? STATE_BEGIN_DASH : STATE_BLINK_ON; } else if (m_state == STATE_OFF && (!m_groupCounter || m_colorset.numColors() == 1)) { - if (m_groupCounter > 0) { - m_state = STATE_BLINK_ON; - } else { - m_state = STATE_BEGIN_GAP; - } + m_state = (m_groupCounter > 0) ? STATE_BLINK_ON : STATE_BEGIN_GAP; } else { m_state = (PatternState)(m_state + 1); } From f9164e4221b2b0a15ed1d76c9cf36110d8542ffe Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 12 May 2023 16:55:05 -0700 Subject: [PATCH 18/85] fixes --- VortexEngine/src/Modes/DefaultModes.cpp | 2 +- VortexEngine/src/VortexEngine.cpp | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/VortexEngine/src/Modes/DefaultModes.cpp b/VortexEngine/src/Modes/DefaultModes.cpp index 55074f45f8..8ed36d7c2c 100644 --- a/VortexEngine/src/Modes/DefaultModes.cpp +++ b/VortexEngine/src/Modes/DefaultModes.cpp @@ -6,7 +6,7 @@ // the gloveset upon factory reset const default_mode_entry default_modes[] = { { - PATTERN_DASHCRUSH, 5, { + PATTERN_DOPS, 5, { RGB_RED, RGB_GREEN, RGB_BLUE, diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index d6abe09acd..e45af3d522 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -13,14 +13,8 @@ #include "Leds/Leds.h" #include "Log/Log.h" -#include "Patterns/PatternArgs.h" -#include "Colors/Colorset.h" -#include "Leds/LedTypes.h" - #include -#include "VortexLib.h" - bool VortexEngine::init() { // all of the global controllers From df5c7c8a129ae46a329e08468dc3d46c1c378a18 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 20 Jun 2023 13:24:29 -0700 Subject: [PATCH 19/85] All of the reintegrated changes from duo work --- VortexEngine/VortexEngine.vcxproj | 2 - VortexEngine/VortexEngine.vcxproj.filters | 6 - VortexEngine/VortexLib/VortexLib.cpp | 5 + VortexEngine/VortexLib/VortexLib.h | 3 + VortexEngine/src/Buttons/Button.cpp | 25 +++- VortexEngine/src/Buttons/Button.h | 16 ++- VortexEngine/src/Buttons/Buttons.cpp | 10 +- VortexEngine/src/Buttons/Buttons.h | 2 +- VortexEngine/src/Leds/LedTypes.h | 4 +- VortexEngine/src/Leds/Leds.cpp | 6 +- VortexEngine/src/Leds/Leds.h | 6 +- VortexEngine/src/Menus/Menu.cpp | 2 +- .../src/Menus/MenuList/PatternSelect.cpp | 2 + .../src/Menus/MenuList/Randomizer.cpp | 28 ++++- VortexEngine/src/Menus/MenuList/Randomizer.h | 5 + VortexEngine/src/Menus/Menus.cpp | 5 + VortexEngine/src/Menus/Menus.h | 1 + VortexEngine/src/Modes/Modes.cpp | 38 +++++- VortexEngine/src/Modes/Modes.h | 10 ++ VortexEngine/src/Patterns/PatternBuilder.cpp | 6 +- VortexEngine/src/Patterns/Patterns.h | 1 - .../src/Patterns/Single/BasicPattern.cpp | 114 ++++++++++-------- .../src/Patterns/Single/TracerPattern.cpp | 55 --------- .../src/Patterns/Single/TracerPattern.h | 28 ----- VortexEngine/src/Serial/BitStream.cpp | 7 +- VortexEngine/src/Serial/BitStream.h | 4 +- VortexEngine/src/Time/Timings.h | 5 +- VortexEngine/src/VortexConfig.h | 23 ++++ VortexEngine/src/VortexEngine.cpp | 7 +- VortexEngine/src/VortexEngine.h | 2 + 30 files changed, 243 insertions(+), 185 deletions(-) delete mode 100644 VortexEngine/src/Patterns/Single/TracerPattern.cpp delete mode 100644 VortexEngine/src/Patterns/Single/TracerPattern.h diff --git a/VortexEngine/VortexEngine.vcxproj b/VortexEngine/VortexEngine.vcxproj index 2f72e95b84..fdc59e3a98 100644 --- a/VortexEngine/VortexEngine.vcxproj +++ b/VortexEngine/VortexEngine.vcxproj @@ -200,7 +200,6 @@ - @@ -273,7 +272,6 @@ - diff --git a/VortexEngine/VortexEngine.vcxproj.filters b/VortexEngine/VortexEngine.vcxproj.filters index 6894e7bd3a..c9c556618d 100644 --- a/VortexEngine/VortexEngine.vcxproj.filters +++ b/VortexEngine/VortexEngine.vcxproj.filters @@ -285,9 +285,6 @@ Source Files\Patterns\Single - - Source Files\Patterns\Single - Source Files\Patterns\Single @@ -500,9 +497,6 @@ Header Files\Patterns\Single - - Header Files\Patterns\Single - Header Files\Patterns diff --git a/VortexEngine/VortexLib/VortexLib.cpp b/VortexEngine/VortexLib/VortexLib.cpp index 98ca6c0bb0..e84956e362 100644 --- a/VortexEngine/VortexLib/VortexLib.cpp +++ b/VortexEngine/VortexLib/VortexLib.cpp @@ -268,6 +268,11 @@ void Vortex::doCommand(char c) m_lastCommand = c; } +bool Vortex::isSleeping() +{ + return VortexEngine::isSleeping(); +} + bool Vortex::tick() { if (!m_initialized) { diff --git a/VortexEngine/VortexLib/VortexLib.h b/VortexEngine/VortexLib/VortexLib.h index 1771adde91..813f25e36c 100644 --- a/VortexEngine/VortexLib/VortexLib.h +++ b/VortexEngine/VortexLib/VortexLib.h @@ -208,6 +208,9 @@ class Vortex // per tick so multiple commands will be queued up static void doCommand(char c); + // whether the engine is asleep + static bool isSleeping(); + // enable, fetch and clear the internal command log static void enableCommandLog(bool enable) { m_commandLogEnabled = enable; } static const std::string &getCommandLog() { return m_commandLog; } diff --git a/VortexEngine/src/Buttons/Button.cpp b/VortexEngine/src/Buttons/Button.cpp index 6e2ae5606e..9ca1cf02a0 100644 --- a/VortexEngine/src/Buttons/Button.cpp +++ b/VortexEngine/src/Buttons/Button.cpp @@ -16,6 +16,8 @@ Button::Button() : m_releaseTime(0), m_holdDuration(0), m_releaseDuration(0), + m_consecutivePresses(0), + m_releaseCount(0), m_buttonState(false), m_newPress(false), m_newRelease(false), @@ -36,10 +38,12 @@ bool Button::init(int pin) m_releaseTime = 0; m_holdDuration = 0; m_releaseDuration = 0; - m_buttonState = false; + m_consecutivePresses = 0; + m_releaseCount = 0; + m_buttonState = check(); m_newPress = false; m_newRelease = false; - m_isPressed = false; + m_isPressed = m_buttonState; m_shortClick = false; m_longClick = false; @@ -48,14 +52,19 @@ bool Button::init(int pin) return true; } -void Button::check() +bool Button::check() +{ + return (digitalRead(m_pinNum) == 0); +} + +void Button::update() { // reset the new press/release members this tick m_newPress = false; m_newRelease = false; // read the new button state, 0 (LOW) means pressed - bool newButtonState = (digitalRead(m_pinNum) == 0); + bool newButtonState = check(); // did the button change (press/release occurred) if (newButtonState != m_buttonState) { @@ -86,9 +95,17 @@ void Button::check() // update the release duration as long as the button is released if (Time::getCurtime() >= m_releaseTime) { m_releaseDuration = (uint32_t)(Time::getCurtime() - m_releaseTime); + if (m_releaseDuration > CONSECUTIVE_WINDOW_TICKS) { + // if the release duration is greater than the threshold, reset the consecutive presses + m_consecutivePresses = 0; + } } } + if (m_newRelease) { + m_consecutivePresses++; + } + // whether a shortclick or long click just occurred m_shortClick = (m_newRelease && (m_holdDuration <= SHORT_CLICK_THRESHOLD_TICKS)); m_longClick = (m_newRelease && (m_holdDuration > SHORT_CLICK_THRESHOLD_TICKS)); diff --git a/VortexEngine/src/Buttons/Button.h b/VortexEngine/src/Buttons/Button.h index 0e506bf89f..39af44ce71 100644 --- a/VortexEngine/src/Buttons/Button.h +++ b/VortexEngine/src/Buttons/Button.h @@ -18,8 +18,10 @@ class Button // initialize a new button object with a pin number bool init(int pin); - // check the state of the button by querying the pin - void check(); + // directly poll the pin for whether it's pressed right now + bool check(); + // poll the button pin and update the state of the button object + void update(); // whether the button was pressed this tick bool onPress() const { return m_newPress; } @@ -43,6 +45,11 @@ class Button // how long the button is currently or was last released for (in ticks) uint32_t releaseDuration() const { return m_releaseDuration; } + // the number of consecutive presses + uint8_t consecutivePresses() const { return m_consecutivePresses; } + // the number of releases + uint8_t releaseCount() const { return m_releaseCount; } + private: // the pin number that is read int m_pinNum; @@ -60,6 +67,11 @@ class Button // the last release duration uint32_t m_releaseDuration; + // the number of repeated presses (automatically detects rapid presses) + uint8_t m_consecutivePresses; + // the number of times released, will overflow at 255 + uint8_t m_releaseCount; + // the active state of the button bool m_buttonState; diff --git a/VortexEngine/src/Buttons/Buttons.cpp b/VortexEngine/src/Buttons/Buttons.cpp index 568f501863..9a0f7dfb3d 100644 --- a/VortexEngine/src/Buttons/Buttons.cpp +++ b/VortexEngine/src/Buttons/Buttons.cpp @@ -32,18 +32,12 @@ void Buttons::cleanup() { } -void Buttons::check() +void Buttons::update() { -#ifndef VORTEX_LIB - // on real devices ignore button presses at startup - if (Time::getCurtime() <= IGNORE_BUTTON_TICKS) { - return; - } -#endif // would iterate all buttons and check them here // but there's only one button so for (uint32_t i = 0; i < NUM_BUTTONS; ++i) { - m_buttons[i].check(); + m_buttons[i].update(); } #ifdef VORTEX_LIB // read input from the vortex lib interface, for example Vortex::shortClick() diff --git a/VortexEngine/src/Buttons/Buttons.h b/VortexEngine/src/Buttons/Buttons.h index 3ed2709a33..cecb028f88 100644 --- a/VortexEngine/src/Buttons/Buttons.h +++ b/VortexEngine/src/Buttons/Buttons.h @@ -20,7 +20,7 @@ class Buttons static void cleanup(); // poll the buttons - static void check(); + static void update(); private: // feel free to add more I guess diff --git a/VortexEngine/src/Leds/LedTypes.h b/VortexEngine/src/Leds/LedTypes.h index 8b3fece26d..b5b4e07d22 100644 --- a/VortexEngine/src/Leds/LedTypes.h +++ b/VortexEngine/src/Leds/LedTypes.h @@ -140,8 +140,8 @@ inline LedPos mapGetNextLed(LedMap map, LedPos pos) #define MAP_INVERSE(map) ((~map) & MAP_LED_ALL) // macro for all evens and odds -#define MAP_PAIR_EVENS (((1 << LED_COUNT) - 1) & 0xAAAAAAAA) -#define MAP_PAIR_ODDS (((1 << LED_COUNT) - 1) & 0x55555555) +#define MAP_PAIR_EVENS (((1 << LED_COUNT) - 1) & 0x55555555) +#define MAP_PAIR_ODDS (((1 << LED_COUNT) - 1) & 0xAAAAAAAA) // Some preset bitmaps for pair groupings #define MAP_PAIR_ODD_EVENS (MAP_PAIR_EVEN(PAIR_0) | MAP_PAIR_EVEN(PAIR_2) | MAP_PAIR_EVEN(PAIR_4)) diff --git a/VortexEngine/src/Leds/Leds.cpp b/VortexEngine/src/Leds/Leds.cpp index d8901999d0..73ba371906 100644 --- a/VortexEngine/src/Leds/Leds.cpp +++ b/VortexEngine/src/Leds/Leds.cpp @@ -15,7 +15,7 @@ // array of led color values RGBColor Leds::m_ledColors[LED_COUNT] = { RGB_OFF }; // global brightness -uint32_t Leds::m_brightness = DEFAULT_BRIGHTNESS; +uint8_t Leds::m_brightness = DEFAULT_BRIGHTNESS; bool Leds::init() { @@ -223,12 +223,12 @@ void Leds::breathIndex(LedPos target, uint32_t hue, uint32_t variance, uint32_t void Leds::breathIndexSat(LedPos target, uint32_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val) { - setIndex(target, HSVColor(hue, 255 - (uint8_t)(sat + ((sin(variance * 0.0174533) + 1) * magnitude)), val)); + setIndex(target, HSVColor(hue, 255 - (uint8_t)(sat + 128 + ((sin(variance * 0.0174533) + 1) * magnitude)), val)); } void Leds::breathIndexVal(LedPos target, uint32_t hue, uint32_t variance, uint32_t magnitude, uint8_t sat, uint8_t val) { - setIndex(target, HSVColor(hue, sat, 255 - (uint8_t)(val + ((sin(variance * 0.0174533) + 1) * magnitude)))); + setIndex(target, HSVColor(hue, sat, 255 - (uint8_t)(val + 128 + ((sin(variance * 0.0174533) + 1) * magnitude)))); } void Leds::update() diff --git a/VortexEngine/src/Leds/Leds.h b/VortexEngine/src/Leds/Leds.h index d96c92760c..4ad252ab1f 100644 --- a/VortexEngine/src/Leds/Leds.h +++ b/VortexEngine/src/Leds/Leds.h @@ -97,8 +97,8 @@ class Leds static RGBColor getLed(LedPos pos) { return led(pos); } // global brightness - static uint32_t getBrightness() { return m_brightness; } - static void setBrightness(uint32_t brightness) { m_brightness = brightness; } + static uint8_t getBrightness() { return m_brightness; } + static void setBrightness(uint8_t brightness) { m_brightness = brightness; } // actually update the LEDs and show the changes static void update(); @@ -114,7 +114,7 @@ class Leds } // the global brightness - static uint32_t m_brightness; + static uint8_t m_brightness; // array of led color values static RGBColor m_ledColors[LED_COUNT]; diff --git a/VortexEngine/src/Menus/Menu.cpp b/VortexEngine/src/Menus/Menu.cpp index 34985cd821..3a382b0e07 100644 --- a/VortexEngine/src/Menus/Menu.cpp +++ b/VortexEngine/src/Menus/Menu.cpp @@ -105,7 +105,7 @@ void Menu::showSelect() if (g_pButton->isPressed() && g_pButton->holdDuration() > SHORT_CLICK_THRESHOLD_TICKS && g_pButton->holdDuration() < (SHORT_CLICK_THRESHOLD_TICKS + Time::msToTicks(250))) { - Leds::setAll(RGB_DIM_WHITE1); + Leds::setAll(RGB_DIM_WHITE2); } } diff --git a/VortexEngine/src/Menus/MenuList/PatternSelect.cpp b/VortexEngine/src/Menus/MenuList/PatternSelect.cpp index 485d8c28b1..487cb0c068 100644 --- a/VortexEngine/src/Menus/MenuList/PatternSelect.cpp +++ b/VortexEngine/src/Menus/MenuList/PatternSelect.cpp @@ -32,6 +32,8 @@ Menu::MenuAction PatternSelect::run() } // run the current mode m_pCurMode->play(); + // show selections + showSelect(); return MENU_CONTINUE; } diff --git a/VortexEngine/src/Menus/MenuList/Randomizer.cpp b/VortexEngine/src/Menus/MenuList/Randomizer.cpp index 88a127019d..f99230d1f4 100644 --- a/VortexEngine/src/Menus/MenuList/Randomizer.cpp +++ b/VortexEngine/src/Menus/MenuList/Randomizer.cpp @@ -13,8 +13,14 @@ #include "../../Leds/Leds.h" #include "../../Log/Log.h" +#ifdef VORTEX_LIB +#include +#endif + Randomizer::Randomizer(const RGBColor &col) : - Menu(col) + Menu(col), + m_lastRandomization(0), + m_autoMode(false) { } @@ -73,14 +79,28 @@ Menu::MenuAction Randomizer::run() m_pCurMode->init(); } + // if the user fast-clicks 3 times then toggle automode + if (g_pButton->onRelease() && g_pButton->consecutivePresses() == 3) { + m_autoMode = !m_autoMode; + // display a quick flash of either green or red to indicate whether auto mode is on or not + Leds::setAll((m_autoMode ? RGB_GREEN : RGB_RED)); + Leds::update(); + delay(250); + return MENU_CONTINUE; + } + + if (m_autoMode && (m_lastRandomization + AUTO_RANDOM_DELAY_TICKS < Time::getCurtime())) { + m_lastRandomization = Time::getCurtime(); + reRoll(); + } + // display the randomized mode if (m_pCurMode) { m_pCurMode->play(); } - if (g_pButton->isPressed() && g_pButton->holdDuration() > SHORT_CLICK_THRESHOLD_TICKS) { - Leds::setAll(RGB_DIM_WHITE2); - } + // show the selection + showSelect(); // return true to continue staying in randomizer menu return MENU_CONTINUE; diff --git a/VortexEngine/src/Menus/MenuList/Randomizer.h b/VortexEngine/src/Menus/MenuList/Randomizer.h index 6206d1c3ec..0a6f5f3da5 100644 --- a/VortexEngine/src/Menus/MenuList/Randomizer.h +++ b/VortexEngine/src/Menus/MenuList/Randomizer.h @@ -26,6 +26,11 @@ class Randomizer : public Menu Random m_singlesRandCtx[LED_COUNT]; Random m_multiRandCtx; + // the time of the last randomization + uint64_t m_lastRandomization; + // whether auto-randomize is enabled + bool m_autoMode; + // re-roll a new randomization with a given context on an led bool reRoll(LedPos pos); bool reRoll(); diff --git a/VortexEngine/src/Menus/Menus.cpp b/VortexEngine/src/Menus/Menus.cpp index 604babddd5..e5b3a8f4d9 100644 --- a/VortexEngine/src/Menus/Menus.cpp +++ b/VortexEngine/src/Menus/Menus.cpp @@ -217,6 +217,11 @@ bool Menus::checkOpen() return m_menuState != MENU_STATE_NOT_OPEN; } +bool Menus::checkInMenu() +{ + return m_menuState == MENU_STATE_IN_MENU; +} + Menu *Menus::curMenu() { return m_pCurMenu; diff --git a/VortexEngine/src/Menus/Menus.h b/VortexEngine/src/Menus/Menus.h index e179b5e95c..3874bf339f 100644 --- a/VortexEngine/src/Menus/Menus.h +++ b/VortexEngine/src/Menus/Menus.h @@ -47,6 +47,7 @@ class Menus // check if the menus are open static bool checkOpen(); + static bool checkInMenu(); static Menu *curMenu(); static MenuEntryID curMenuID(); diff --git a/VortexEngine/src/Modes/Modes.cpp b/VortexEngine/src/Modes/Modes.cpp index 5563e1ad98..2ceafa49fd 100644 --- a/VortexEngine/src/Modes/Modes.cpp +++ b/VortexEngine/src/Modes/Modes.cpp @@ -7,6 +7,7 @@ #include "../Serial/ByteStream.h" #include "../Time/TimeControl.h" +#include "../Time/Timings.h" #include "../Colors/Colorset.h" #include "../Storage/Storage.h" #include "../Buttons/Buttons.h" @@ -21,6 +22,8 @@ uint8_t Modes::m_numModes = 0; Modes::ModeLink *Modes::m_pCurModeLink = nullptr; // list of serialized version of bufers Modes::ModeLink *Modes::m_storedModes = nullptr; +// global flags for all modes +uint8_t Modes::m_globalFlags = 0; bool Modes::init() { @@ -71,6 +74,9 @@ bool Modes::saveToBuffer(ByteStream &modesBuffer) { // serialize the engine version into the modes buffer VortexEngine::serializeVersion(modesBuffer); + // NOTE: instead of global brightness the duo uses this to store the + // startup mode ID. The duo doesn't offer a global brightness option + modesBuffer.serialize(m_globalFlags); // serialize the global brightness modesBuffer.serialize((uint8_t)Leds::getBrightness()); // serialize all modes data into the modesBuffer @@ -102,6 +108,10 @@ bool Modes::loadFromBuffer(ByteStream &modesBuffer) ERROR_LOGF("Incompatible savefile version: %u.%u", major, minor); return false; } + // NOTE: instead of global brightness the duo uses this to store the + // startup mode ID. The duo doesn't offer a global brightness option + // unserialize the global brightness + modesBuffer.unserialize(&m_globalFlags); // unserialize the global brightness uint8_t brightness = 0; modesBuffer.unserialize(&brightness); @@ -109,7 +119,17 @@ bool Modes::loadFromBuffer(ByteStream &modesBuffer) Leds::setBrightness(brightness); } // now just unserialize the list of modes - return unserialize(modesBuffer); + if (!unserialize(modesBuffer)) { + return false; + } + // startupMode is 1-based offset that encodes both the index to start at and + // whether the system is enabled, hence why 0 cannot be used as an offset + uint8_t startupMode = (m_globalFlags & 0xF0) >> 4; + if (startupMode > 0) { + // set the current mode to the startup mode + setCurMode(startupMode - 1); + } + return true; } bool Modes::loadStorage() @@ -505,6 +525,22 @@ void Modes::clearModes() Leds::clearAll(); } +bool Modes::setLocked(bool locked, bool save) +{ + if (locked) { + m_globalFlags |= MODES_FLAG_LOCKED; + } else { + m_globalFlags &= ~MODES_FLAG_LOCKED; + } + DEBUG_LOGF("Toggled locked to %s", m_locked ? "on" : "off"); + return !save || saveStorage(); +} + +bool Modes::locked() +{ + return (m_globalFlags & MODES_FLAG_LOCKED) != 0; +} + #ifdef VORTEX_LIB #include "Patterns/PatternBuilder.h" // get the maximum size a mode can occupy diff --git a/VortexEngine/src/Modes/Modes.h b/VortexEngine/src/Modes/Modes.h index 3e5e0fa8fc..b13697f5d1 100644 --- a/VortexEngine/src/Modes/Modes.h +++ b/VortexEngine/src/Modes/Modes.h @@ -14,6 +14,9 @@ class PatternArgs; class Colorset; class Mode; +// the device is locked and needs to be unlocked to turn on +#define MODES_FLAG_LOCKED (1 << 0) + class Modes { // private unimplemented constructor @@ -84,6 +87,10 @@ class Modes // delete all modes in the list static void clearModes(); + // toggle the locked state + static bool setLocked(bool locked, bool save = true); + static bool locked(); + #if MODES_TEST == 1 static void test(); #endif @@ -162,6 +169,9 @@ class Modes // list of serialized version of bufers static ModeLink *m_storedModes; + + // global flags for all modes + static uint8_t m_globalFlags; }; #endif diff --git a/VortexEngine/src/Patterns/PatternBuilder.cpp b/VortexEngine/src/Patterns/PatternBuilder.cpp index ae1e81d7ad..9d72e489cf 100644 --- a/VortexEngine/src/Patterns/PatternBuilder.cpp +++ b/VortexEngine/src/Patterns/PatternBuilder.cpp @@ -29,7 +29,7 @@ #include "Multi/BackStrobePattern.h" #include "Multi/MateriaPattern.h" -#include "Single/TracerPattern.h" +#include "Single/SingleLedPattern.h" #include "Single/BasicPattern.h" #include "Single/BlendPattern.h" #include "Single/SolidPattern.h" @@ -145,7 +145,6 @@ PatternArgs PatternBuilder::getDefaultArgs(PatternID id) case PATTERN_COMPLEMENTARY_BLENDSTROBE: return PatternArgs(STROBE_ON_DURATION, 28, 0, 0, 0, 0, 2); case PATTERN_DASHDOPS: return PatternArgs(2, 7, 7, 30); case PATTERN_DASHCRUSH: return PatternArgs(4, 1, 10, 30); - case PATTERN_TRACER: return PatternArgs(16, 3); case PATTERN_RIBBON: return PatternArgs(RIBBON_DURATION); case PATTERN_MINIRIBBON: return PatternArgs(3); case PATTERN_SOLID: return PatternArgs(250); @@ -172,7 +171,7 @@ PatternArgs PatternBuilder::getDefaultArgs(PatternID id) case PATTERN_PULSISH: return PatternArgs(DOPISH_ON_DURATION, DOPISH_OFF_DURATION, STROBE_ON_DURATION, STROBE_OFF_DURATION, 250); case PATTERN_FILL: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 200); case PATTERN_BOUNCE: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 200, 10); - case PATTERN_SPLITSTROBIE: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 0, 16, 3, 10, PATTERN_DOPS, PATTERN_TRACER); + case PATTERN_SPLITSTROBIE: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 0, 16, 3, 10, PATTERN_DOPS, PATTERN_STROBIE); case PATTERN_BACKSTROBE: return PatternArgs(DOPS_ON_DURATION, DOPS_OFF_DURATION, 0, HYPERSTROBE_ON_DURATION, HYPERSTROBE_OFF_DURATION, 10, PATTERN_DOPS, PATTERN_HYPERSTROBE); case PATTERN_MATERIA: return PatternArgs(STROBE_ON_DURATION, STROBE_OFF_DURATION, 3, 35, 80); case PATTERN_NONE: break; @@ -230,7 +229,6 @@ Pattern *PatternBuilder::generate(PatternID id, const PatternArgs *userArgs) case PATTERN_BLENDSTROBE: case PATTERN_COMPLEMENTARY_BLEND: case PATTERN_COMPLEMENTARY_BLENDSTROBE: return new BlendPattern(args); - case PATTERN_TRACER: return new TracerPattern(args); case PATTERN_SOLID: return new SolidPattern(args); // ===================== diff --git a/VortexEngine/src/Patterns/Patterns.h b/VortexEngine/src/Patterns/Patterns.h index 2b11fb0d65..d564953ee6 100644 --- a/VortexEngine/src/Patterns/Patterns.h +++ b/VortexEngine/src/Patterns/Patterns.h @@ -47,7 +47,6 @@ enum PatternID : uint8_t PATTERN_COMPLEMENTARY_BLENDSTROBE, PATTERN_DASHDOPS, PATTERN_DASHCRUSH, - PATTERN_TRACER, PATTERN_RIBBON, PATTERN_MINIRIBBON, PATTERN_SOLID, diff --git a/VortexEngine/src/Patterns/Single/BasicPattern.cpp b/VortexEngine/src/Patterns/Single/BasicPattern.cpp index 2ae450d349..043e4b459f 100644 --- a/VortexEngine/src/Patterns/Single/BasicPattern.cpp +++ b/VortexEngine/src/Patterns/Single/BasicPattern.cpp @@ -11,7 +11,24 @@ #ifdef DEBUG_BASIC_PATTERN #include -uint64_t lastPrint = 0; +// print out the current state of the pattern +#define PRINT_STATE(state) printState(state) +static void printState(PatternState state) +{ + static uint64_t lastPrint = 0; + if (lastPrint == Time::getCurtime()) return; + switch (m_state) { + case STATE_ON: printf("on "); break; + case STATE_OFF: printf("off "); break; + case STATE_IN_GAP: printf("gap1"); break; + case STATE_IN_DASH: printf("dash"); break; + case STATE_IN_GAP2: printf("gap2"); break; + default: return; + } + lastPrint = Time::getCurtime(); +} +#else +#define PRINT_STATE(state) // do nothing #endif BasicPattern::BasicPattern(const PatternArgs &args) : @@ -72,25 +89,49 @@ void BasicPattern::play() case STATE_DISABLED: return; case STATE_BLINK_ON: - if (m_onDuration > 0) { onBlinkOn(); --m_groupCounter; nextState(m_onDuration); return; } + if (m_onDuration > 0) { + onBlinkOn(); + --m_groupCounter; + nextState(m_onDuration); + return; + } m_state = STATE_BLINK_OFF; case STATE_BLINK_OFF: // the whole 'should blink off' situation is tricky because we might need - // to go back to blinking on if or colorset isn't at the end yet + // to go back to blinking on if our colorset isn't at the end yet if (m_groupCounter > 0 || (!m_gapDuration && !m_dashDuration)) { - if (m_offDuration > 0) { onBlinkOff(); nextState(m_offDuration); return; } - if (m_groupCounter > 0 && m_onDuration > 0) { m_state = STATE_BLINK_ON; goto replay; } + if (m_offDuration > 0) { + onBlinkOff(); + nextState(m_offDuration); + return; + } + if (m_groupCounter > 0 && m_onDuration > 0) { + m_state = STATE_BLINK_ON; + goto replay; + } } m_state = STATE_BEGIN_GAP; case STATE_BEGIN_GAP: m_groupCounter = m_groupSize ? m_groupSize : (m_colorset.numColors() - (m_dashDuration != 0)); - if (m_gapDuration > 0) { beginGap(); nextState(m_gapDuration); return; } + if (m_gapDuration > 0) { + beginGap(); + nextState(m_gapDuration); + return; + } m_state = STATE_BEGIN_DASH; case STATE_BEGIN_DASH: - if (m_dashDuration > 0) { beginDash(); nextState(m_dashDuration); return; } + if (m_dashDuration > 0) { + beginDash(); + nextState(m_dashDuration); + return; + } m_state = STATE_BEGIN_GAP2; case STATE_BEGIN_GAP2: - if (m_dashDuration > 0 && m_gapDuration > 0) { beginGap(); nextState(m_gapDuration); return; } + if (m_dashDuration > 0 && m_gapDuration > 0) { + beginGap(); + nextState(m_gapDuration); + return; + } m_state = STATE_BLINK_ON; goto replay; default: @@ -98,19 +139,8 @@ void BasicPattern::play() } if (m_blinkTimer.alarm() == -1) { -#ifdef DEBUG_BASIC_PATTERN - if (lastPrint == Time::getCurtime()) return; - switch (m_state) { - case STATE_ON: printf("on "); break; - case STATE_OFF: printf("off "); break; - case STATE_IN_GAP: printf("gap1"); break; - case STATE_IN_DASH: printf("dash"); break; - case STATE_IN_GAP2: printf("gap2"); break; - default: return; - } - lastPrint = Time::getCurtime(); -#endif - // no alarm triggered? + // no alarm triggered just stay in current state, return and don't transition states + PRINT_STATE(m_state); return; } @@ -118,60 +148,42 @@ void BasicPattern::play() // transitioning to different states under certain circumstances. Honestly this is // a nightmare to read now and idk how to fix it if (m_state == STATE_IN_GAP2 || (m_state == STATE_OFF && m_groupCounter > 0)) { - if (m_onDuration) { - m_state = STATE_BLINK_ON; - } else { - m_state = m_dashDuration ? STATE_BEGIN_DASH : STATE_BEGIN_GAP; - } + // this is an edge condition for when in the second gap or off in the non-last off blink + // then the state actually needs to jump backwards rather than iterate + m_state = m_onDuration ? STATE_BLINK_ON : (m_dashDuration ? STATE_BEGIN_DASH : STATE_BEGIN_GAP); } else if (m_state == STATE_OFF && (!m_groupCounter || m_colorset.numColors() == 1)) { + // this is an edge condition when the state is off but this is the last off blink in the + // group or there's literally only one color in the group then if there is more blinks + // left in the group we need to cycle back to blink on instead of to the next state m_state = (m_groupCounter > 0) ? STATE_BLINK_ON : STATE_BEGIN_GAP; } else { + // this is the standard case, iterate to the next state m_state = (PatternState)(m_state + 1); } - // recurse with the new state change + // poor-mans recurse with the new state change (this transitions to a new state within the same tick) goto replay; } void BasicPattern::onBlinkOn() { -#ifdef DEBUG_BASIC_PATTERN - if (lastPrint != Time::getCurtime()) { - printf("on "); - lastPrint = Time::getCurtime(); - } -#endif + PRINT_STATE(STATE_ON); Leds::setIndex(m_ledPos, m_colorset.getNext()); } void BasicPattern::onBlinkOff() { -#ifdef DEBUG_BASIC_PATTERN - if (lastPrint != Time::getCurtime()) { - printf("off "); - lastPrint = Time::getCurtime(); - } -#endif + PRINT_STATE(STATE_OFF); Leds::clearIndex(m_ledPos); } void BasicPattern::beginGap() { -#ifdef DEBUG_BASIC_PATTERN - if (lastPrint != Time::getCurtime()) { - printf("gap%d", (m_state == STATE_BEGIN_GAP) ? 1 : 2); - lastPrint = Time::getCurtime(); - } -#endif + PRINT_STATE(STATE_IN_GAP); Leds::clearIndex(m_ledPos); } void BasicPattern::beginDash() { -#ifdef DEBUG_BASIC_PATTERN - if (lastPrint != Time::getCurtime()) { - printf("dash"); - lastPrint = Time::getCurtime(); - } -#endif + PRINT_STATE(STATE_IN_DASH); Leds::setIndex(m_ledPos, m_colorset.getNext()); } diff --git a/VortexEngine/src/Patterns/Single/TracerPattern.cpp b/VortexEngine/src/Patterns/Single/TracerPattern.cpp deleted file mode 100644 index 14841a6a00..0000000000 --- a/VortexEngine/src/Patterns/Single/TracerPattern.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "TracerPattern.h" - -#include "../../Leds/Leds.h" - -TracerPattern::TracerPattern(const PatternArgs &args) : - SingleLedPattern(args), - m_tracerDuration(0), - m_dotDuration(0), - m_blinkTimer(), - m_dotColor(0) -{ - m_patternID = PATTERN_TRACER; - REGISTER_ARG(m_tracerDuration); - REGISTER_ARG(m_dotDuration); - setArgs(args); -} - -TracerPattern::~TracerPattern() -{ -} - -void TracerPattern::init() -{ - // run base pattern init logic - SingleLedPattern::init(); - - // reset the blink timer entirely - m_blinkTimer.reset(); - - // add the alarms for on then off - m_blinkTimer.addAlarm(m_tracerDuration); - m_blinkTimer.addAlarm(m_dotDuration); - - // start the blink timer from the current frame - m_blinkTimer.start(); -} - -// pure virtual must override the play function -void TracerPattern::play() -{ - AlarmID id = m_blinkTimer.alarm(); - if (id == 0) { - // draw the tracer background - Leds::setIndex(m_ledPos, m_colorset.get(0)); - } else if (id == 1) { - // display dot, never display the tracer color which - // is at index 0 of the colorset - Leds::setIndex(m_ledPos, m_colorset.get(1 + m_dotColor)); - // prevent division by 0 - if (m_colorset.numColors() > 1) { - // increment tracer counter and wrap at 1 less than num colors - m_dotColor = (m_dotColor + 1) % (m_colorset.numColors() - 1); - } - } -} diff --git a/VortexEngine/src/Patterns/Single/TracerPattern.h b/VortexEngine/src/Patterns/Single/TracerPattern.h deleted file mode 100644 index b9ed96295b..0000000000 --- a/VortexEngine/src/Patterns/Single/TracerPattern.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef TRACER_PATTERN_H -#define TRACER_PATTERN_H - -#include "SingleLedPattern.h" - -#include "../../Time/Timer.h" - -class TracerPattern : public SingleLedPattern -{ -public: - TracerPattern(const PatternArgs &args); - virtual ~TracerPattern(); - - virtual void init() override; - - virtual void play() override; - -private: - // the duration the light is on/off for - uint8_t m_tracerDuration; - uint8_t m_dotDuration; - // the timer for performing blinks - Timer m_blinkTimer; - // the counter for dot color - uint8_t m_dotColor; -}; - -#endif diff --git a/VortexEngine/src/Serial/BitStream.cpp b/VortexEngine/src/Serial/BitStream.cpp index 85228ab925..bee9e72b46 100644 --- a/VortexEngine/src/Serial/BitStream.cpp +++ b/VortexEngine/src/Serial/BitStream.cpp @@ -31,22 +31,25 @@ BitStream::~BitStream() } } -void BitStream::init(uint8_t *buf, uint32_t size) +bool BitStream::init(uint8_t *buf, uint32_t size) { m_buf = buf; m_buf_size = size; resetPos(); + return true; } -void BitStream::init(uint32_t size) +bool BitStream::init(uint32_t size) { m_buf = (uint8_t *)vcalloc(1, size); if (!m_buf) { ERROR_OUT_OF_MEMORY(); + return false; } m_buf_size = size; m_allocated = true; resetPos(); + return true; } void BitStream::reset() diff --git a/VortexEngine/src/Serial/BitStream.h b/VortexEngine/src/Serial/BitStream.h index 43e6104ddd..5fad05bd99 100644 --- a/VortexEngine/src/Serial/BitStream.h +++ b/VortexEngine/src/Serial/BitStream.h @@ -13,9 +13,9 @@ class BitStream ~BitStream(); // init the stream with a buffer - void init(uint8_t *buf, uint32_t size); + bool init(uint8_t *buf, uint32_t size); // init the stream and allocate a buffer - void init(uint32_t size); + bool init(uint32_t size); // clear the target buffer to 0 and reset position void reset(); diff --git a/VortexEngine/src/Time/Timings.h b/VortexEngine/src/Time/Timings.h index ca324383b2..ce41844817 100644 --- a/VortexEngine/src/Time/Timings.h +++ b/VortexEngine/src/Time/Timings.h @@ -11,8 +11,9 @@ #define MENU_TRIGGER_THRESHOLD_TICKS Time::msToTicks(MENU_TRIGGER_TIME) #define SHORT_CLICK_THRESHOLD_TICKS Time::msToTicks(CLICK_THRESHOLD) -// how long after startup before the button starts responding -#define IGNORE_BUTTON_TICKS Time::msToTicks(IGNORE_BUTTON_TIME) +#define CONSECUTIVE_WINDOW_TICKS Time::msToTicks(CONSECUTIVE_WINDOW) + +#define AUTO_RANDOM_DELAY_TICKS Time::msToTicks(AUTO_RANDOM_DELAY) // Color delete threshold (in milliseconds) #define DELETE_THRESHOLD_TICKS Time::msToTicks(COL_DELETE_THRESHOLD) diff --git a/VortexEngine/src/VortexConfig.h b/VortexEngine/src/VortexConfig.h index c2f8da3505..7787fec33e 100644 --- a/VortexEngine/src/VortexConfig.h +++ b/VortexEngine/src/VortexConfig.h @@ -63,6 +63,25 @@ // after sleep or immediately on startup #define IGNORE_BUTTON_TIME 150 +// Rapid Press Window (in milliseconds) +// +// How long the user has after releasing to short click the button +// again and be counted as a 'consecutive press' +#define CONSECUTIVE_WINDOW 150 + +// Auto Randomization Delay (in milliseconds) +// +// How long the randomizer will wait before it triggers another randomization +// when it's been set to auto mode. +// +// Most electronic music often falls within a range of approximately 120-130 BPM +// To convert BPM to a measure of time in milliseconds (ms) we can do: +// Milliseconds per beat at 128 BPM = 1/128 * 60,000 = approximately 468.75 ms +// So, a beat at 128 BPM lasts about 468.75 ms, so 4 beats would be about 1875. +// +// This will make the randomizer wait approximately 4 edm beats between switches +#define AUTO_RANDOM_DELAY 1875 + // Color delete threshold (in milliseconds) // // How long you must hold down on a color in the color select menu to @@ -417,6 +436,10 @@ #undef FIXED_LED_COUNT #define FIXED_LED_COUNT 0 +// test framework needs more time to click idk +#undef CONSECUTIVE_WINDOW +#define CONSECUTIVE_WINDOW 300 + // force logging to 3 on linux build #ifndef _MSC_VER #undef LOGGING_LEVEL diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index 2a6e23fbf4..5ddfe4a82f 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -75,6 +75,7 @@ void VortexEngine::cleanup() // cleanup in reverse order // NOTE: the arduino doesn't actually cleanup, // but the test frameworks do +#ifdef VORTEX_LIB Modes::cleanup(); Menus::cleanup(); Buttons::cleanup(); @@ -84,6 +85,7 @@ void VortexEngine::cleanup() Storage::cleanup(); Time::cleanup(); SerialComs::cleanup(); +#endif } void VortexEngine::tick() @@ -101,9 +103,8 @@ void VortexEngine::tick() // tick the current time counter forward Time::tickClock(); - // don't poll the button till some cycles have passed, this prevents - // the wakeup from cycling to the next mode - Buttons::check(); + // poll the button(s) and update the button object states + Buttons::update(); // run the main logic for the engine runMainLogic(); diff --git a/VortexEngine/src/VortexEngine.h b/VortexEngine/src/VortexEngine.h index 743aa6de63..402c160be6 100644 --- a/VortexEngine/src/VortexEngine.h +++ b/VortexEngine/src/VortexEngine.h @@ -76,6 +76,8 @@ class VortexEngine static uint32_t totalStorageSpace(); // the size of the savefile static uint32_t savefileSize(); + // whether sleeping or not, some versions support sleep + static bool isSleeping() { return false; } #endif private: From 60097ac66bdfb2e5f37ff93d519fb1565b75825a Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 20 Jun 2023 18:58:48 -0700 Subject: [PATCH 20/85] minor fix --- VortexEngine/VortexLib/VortexLib.cpp | 2 +- VortexEngine/src/Time/Timings.h | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/VortexEngine/VortexLib/VortexLib.cpp b/VortexEngine/VortexLib/VortexLib.cpp index e84956e362..fbe7300b62 100644 --- a/VortexEngine/VortexLib/VortexLib.cpp +++ b/VortexEngine/VortexLib/VortexLib.cpp @@ -915,7 +915,7 @@ void Vortex::handleInputQueue(Button *buttons, uint32_t numButtons) // us immediately enter the menus. But we need to unset the pressed // button right after so we push a reset click event to reset the button pButton->m_pressTime = Time::getCurtime(); -#ifdef SLEEP_ENTER_THRESHOLD_TICKS +#if defined(SLEEP_ENTER_THRESHOLD_TICKS) && defined(SLEEP_ENTER_THRESHOLD_TICKS) && defined(SLEEP_WINDOW_THRESHOLD_TICKS) // microlight must hold longer (past sleep time) pButton->m_holdDuration = MENU_TRIGGER_THRESHOLD_TICKS + SLEEP_ENTER_THRESHOLD_TICKS + SLEEP_WINDOW_THRESHOLD_TICKS + 1; #else diff --git a/VortexEngine/src/Time/Timings.h b/VortexEngine/src/Time/Timings.h index 002caa7b2a..ce41844817 100644 --- a/VortexEngine/src/Time/Timings.h +++ b/VortexEngine/src/Time/Timings.h @@ -15,10 +15,6 @@ #define AUTO_RANDOM_DELAY_TICKS Time::msToTicks(AUTO_RANDOM_DELAY) -// the number of ticks to enter sleep mode -#define SLEEP_ENTER_THRESHOLD_TICKS Time::msToTicks(SLEEP_TRIGGER_TIME) -#define SLEEP_WINDOW_THRESHOLD_TICKS Time::msToTicks(SLEEP_WINDOW_TIME) - // Color delete threshold (in milliseconds) #define DELETE_THRESHOLD_TICKS Time::msToTicks(COL_DELETE_THRESHOLD) // Color delete cycle time (in milliseconds) From 245891b985594c0cf39961c06ecc728f5436b044 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 21 Jun 2023 15:11:27 -0700 Subject: [PATCH 21/85] Removed left over irconfig --- VortexEngine/src/Infrared/IRConfig.h | 40 ---------------------------- 1 file changed, 40 deletions(-) delete mode 100644 VortexEngine/src/Infrared/IRConfig.h diff --git a/VortexEngine/src/Infrared/IRConfig.h b/VortexEngine/src/Infrared/IRConfig.h deleted file mode 100644 index f638dadca3..0000000000 --- a/VortexEngine/src/Infrared/IRConfig.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef IR_CONFIG_H -#define IR_CONFIG_H - -// IR Enable -// -// Whether to enable the IR system as a whole -// -#define IR_ENABLE 1 - -// the size of IR blocks in bits -#define DEFAULT_IR_BLOCK_SIZE 32 -#define DEFAULT_IR_BLOCK_SPACING Time::msToTicks(300) - -// the max number of DWORDs that will transfer -#define MAX_DWORDS_TRANSFER 1024 -#define MAX_DATA_TRANSFER (MAX_DWORDS_TRANSFER * sizeof(uint32_t)) - -// the IR receiver buffer size in dwords -#define IR_RECV_BUF_SIZE MAX_DATA_TRANSFER - -#define IR_TIMING 562 -#define IR_TIMING_MIN ((uint32_t)(IR_TIMING * 0.75)) - -#define HEADER_MARK (IR_TIMING * 16) -#define HEADER_SPACE (IR_TIMING * 8) - -#define HEADER_MARK_MIN ((uint32_t)(HEADER_MARK * 0.75)) -#define HEADER_SPACE_MIN ((uint32_t)(HEADER_SPACE * 0.75)) - -#define HEADER_MARK_MAX ((uint32_t)(HEADER_MARK * 1.25)) -#define HEADER_SPACE_MAX ((uint32_t)(HEADER_SPACE * 1.25)) - -#define DIVIDER_SPACE HEADER_MARK -#define DIVIDER_SPACE_MIN HEADER_MARK_MIN -#define DIVIDER_SPACE_MAX HEADER_MARK_MAX - -#define IR_SEND_PWM_PIN 0 -#define RECEIVER_PIN 2 - -#endif From 0f24d0af154c8240a22fb05b6dd273495fbe9541 Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 24 Jun 2023 04:12:01 -0700 Subject: [PATCH 22/85] handle fix --- VortexEngine/src/Modes/DefaultModes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VortexEngine/src/Modes/DefaultModes.cpp b/VortexEngine/src/Modes/DefaultModes.cpp index dd8fea98ce..dfac601f4a 100644 --- a/VortexEngine/src/Modes/DefaultModes.cpp +++ b/VortexEngine/src/Modes/DefaultModes.cpp @@ -24,7 +24,7 @@ const default_mode_entry default_modes[] = { } }, { - PATTERN_BRACKETS, 6, { + PATTERN_DASHDOPS, 6, { RGB_OFF, 0x56D400, 0x5500AB, From 03fd1ec5f08f0b9479a2c7425775393b8ad759a5 Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 24 Jun 2023 18:39:11 -0700 Subject: [PATCH 23/85] fixes for handle to build --- VortexEngine/src/VortexEngine.cpp | 5 ----- VortexEngine/src/Wireless/VLConfig.h | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index 025225a53f..d8a5b9e080 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -40,10 +40,6 @@ bool VortexEngine::init() DEBUG_LOG("IRSender failed to initialize"); return false; } - if (!VLReceiver::init()) { - DEBUG_LOG("VLReceiver failed to initialize"); - return false; - } if (!VLSender::init()) { DEBUG_LOG("VLSender failed to initialize"); return false; @@ -91,7 +87,6 @@ void VortexEngine::cleanup() Buttons::cleanup(); Leds::cleanup(); VLSender::cleanup(); - VLReceiver::cleanup(); IRSender::cleanup(); IRReceiver::cleanup(); Storage::cleanup(); diff --git a/VortexEngine/src/Wireless/VLConfig.h b/VortexEngine/src/Wireless/VLConfig.h index 369e092d46..43b5041339 100644 --- a/VortexEngine/src/Wireless/VLConfig.h +++ b/VortexEngine/src/Wireless/VLConfig.h @@ -6,7 +6,7 @@ // Whether to enable the Visible Light system as a whole // #define VL_ENABLE_SENDER 1 -#define VL_ENABLE_RECEIVER 1 +#define VL_ENABLE_RECEIVER 0 // the size of IR blocks in bits #define VL_DEFAULT_BLOCK_SIZE 256 From 179ceff740bdf178820098d01be73841e5861bf3 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 7 Jul 2023 03:59:06 -0700 Subject: [PATCH 24/85] Fixed vortex arduino macros --- VortexEngine/src/Leds/Leds.cpp | 6 +++--- VortexEngine/src/Storage/Storage.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VortexEngine/src/Leds/Leds.cpp b/VortexEngine/src/Leds/Leds.cpp index 25b8e37809..54d00650ab 100644 --- a/VortexEngine/src/Leds/Leds.cpp +++ b/VortexEngine/src/Leds/Leds.cpp @@ -13,7 +13,7 @@ #include "../../VortexLib/VortexLib.h" #endif -#ifdef VORTEX_ARDUINO +#ifdef VORTEX_EMBEDDED #include #include @@ -35,7 +35,7 @@ uint8_t Leds::m_brightness = DEFAULT_BRIGHTNESS; bool Leds::init() { -#ifdef VORTEX_ARDUINO +#ifdef VORTEX_EMBEDDED // setup leds on data pin 4 FastLED.addLeds((CRGB *)m_ledColors, LED_COUNT); // get screwed fastled, don't throttle us! @@ -265,7 +265,7 @@ void Leds::holdIndex(LedPos target, uint16_t ms, RGBColor col) void Leds::update() { -#ifdef VORTEX_ARDUINO +#ifdef VORTEX_EMBEDDED FastLED.show(m_brightness); #endif #ifdef VORTEX_LIB diff --git a/VortexEngine/src/Storage/Storage.cpp b/VortexEngine/src/Storage/Storage.cpp index 3e0286e641..50756561a0 100644 --- a/VortexEngine/src/Storage/Storage.cpp +++ b/VortexEngine/src/Storage/Storage.cpp @@ -17,7 +17,7 @@ // only arduino needs const I guess? __attribute__((__aligned__(256))) -#ifndef VORTEX_ARDUINO +#ifndef VORTEX_EMBEDDED uint8_t _storagedata[(STORAGE_SIZE+255)/256*256] = { }; #else // only arduino needs const I guess? From c53a5fc248ed3be4e3144df3c82946b5b5684d8a Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 23 Jul 2023 05:17:30 -0700 Subject: [PATCH 25/85] minor fix for arduino build --- VortexEngine/src/Time/TimeControl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VortexEngine/src/Time/TimeControl.cpp b/VortexEngine/src/Time/TimeControl.cpp index 4c3a6b2fe4..6b42c9b674 100644 --- a/VortexEngine/src/Time/TimeControl.cpp +++ b/VortexEngine/src/Time/TimeControl.cpp @@ -191,7 +191,7 @@ uint32_t Time::microseconds() void Time::delayMicroseconds(uint32_t us) { -#ifdef _MSC_VER +#if defined(VORTEX_EMBEDDED) || defined(_MSC_VER) uint32_t newtime = microseconds() + us; while (microseconds() < newtime) { // busy loop From 92a9b01ec66987a09643fe03e7d85fba5d69f341 Mon Sep 17 00:00:00 2001 From: Unreal-Dan <72595612+Unreal-Dan@users.noreply.github.com> Date: Thu, 27 Jul 2023 22:07:05 -0700 Subject: [PATCH 26/85] Create handle.yml --- .github/workflows/handle.yml | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/handle.yml diff --git a/.github/workflows/handle.yml b/.github/workflows/handle.yml new file mode 100644 index 0000000000..ade3c6a559 --- /dev/null +++ b/.github/workflows/handle.yml @@ -0,0 +1,39 @@ +name: Handle Build + +on: + push: + branches: [ "handle" ] + pull_request: + branches: [ "handle" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout outer repository + uses: actions/checkout@v3 + with: + repository: "StoneOrbits/VortexEmulator" + path: VortexEmulator + + - name: Checkout current repository inside the outer repository + uses: actions/checkout@v3 + with: + path: VortexEmulator/VortexTestingFramework/VortexEngine + + - name: Install Dependencies + run: sudo apt-get install valgrind g++ make + + - name: Build + run: make + working-directory: VortexEmulator/VortexTestingFramework + + - name: Set execute permissions for test script + run: chmod +x ./runtests.sh + working-directory: VortexEmulator/VortexTestingFramework/tests + + - name: Run tests + run: ./runtests.sh --handle + working-directory: VortexEmulator/VortexTestingFramework/tests From 60e210bc6a4e2decc0b799e22125cec53ba3760c Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 27 Jul 2023 22:13:39 -0700 Subject: [PATCH 27/85] removed core build from orbit, and renamed build --- .github/workflows/core_build.yml | 39 ------------------- .../{handle.yml => handle_build.yml} | 0 2 files changed, 39 deletions(-) delete mode 100644 .github/workflows/core_build.yml rename .github/workflows/{handle.yml => handle_build.yml} (100%) diff --git a/.github/workflows/core_build.yml b/.github/workflows/core_build.yml deleted file mode 100644 index 623fd19206..0000000000 --- a/.github/workflows/core_build.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Core Build - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - name: Checkout outer repository - uses: actions/checkout@v3 - with: - repository: "StoneOrbits/VortexEmulator" - path: VortexEmulator - - - name: Checkout current repository inside the outer repository - uses: actions/checkout@v3 - with: - path: VortexEmulator/VortexTestingFramework/VortexEngine - - - name: Install Dependencies - run: sudo apt-get install valgrind g++ make - - - name: Build - run: make - working-directory: VortexEmulator/VortexTestingFramework - - - name: Set execute permissions for test script - run: chmod +x ./runtests.sh - working-directory: VortexEmulator/VortexTestingFramework/tests - - - name: Run tests - run: ./runtests.sh --core - working-directory: VortexEmulator/VortexTestingFramework/tests diff --git a/.github/workflows/handle.yml b/.github/workflows/handle_build.yml similarity index 100% rename from .github/workflows/handle.yml rename to .github/workflows/handle_build.yml From 8c6f6fc99b665b87b0a9447512dee207b7be9bd4 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 27 Jul 2023 23:13:04 -0700 Subject: [PATCH 28/85] re-added libs --- .../Adafruit_DotStar/Adafruit_DotStar.cpp | 615 +++++ libraries/Adafruit_DotStar/Adafruit_DotStar.h | 209 ++ libraries/Adafruit_DotStar/COPYING | 794 ++++++ libraries/Adafruit_DotStar/README.md | 3 + .../ItsyBitsyM4Onboard/ItsyBitsyM4Onboard.ino | 49 + .../examples/strandtest/strandtest.ino | 57 + libraries/Adafruit_DotStar/keywords.txt | 42 + libraries/Adafruit_DotStar/library.properties | 9 + libraries/FastLED-master/.gitignore | 2 + libraries/FastLED-master/FastLED.cpp | 272 ++ libraries/FastLED-master/FastLED.h | 581 ++++ libraries/FastLED-master/LICENSE | 20 + libraries/FastLED-master/PORTING.md | 29 + libraries/FastLED-master/README.md | 88 + libraries/FastLED-master/bitswap.cpp | 28 + libraries/FastLED-master/bitswap.h | 276 ++ libraries/FastLED-master/chipsets.h | 506 ++++ libraries/FastLED-master/color.h | 84 + libraries/FastLED-master/colorpalettes.cpp | 174 ++ libraries/FastLED-master/colorpalettes.h | 57 + libraries/FastLED-master/colorutils.cpp | 1198 +++++++++ libraries/FastLED-master/colorutils.h | 1706 ++++++++++++ libraries/FastLED-master/controller.h | 407 +++ libraries/FastLED-master/cpp_compat.h | 16 + libraries/FastLED-master/dmx.h | 65 + libraries/FastLED-master/docs/Doxyfile | 2331 +++++++++++++++++ libraries/FastLED-master/docs/mainpage.dox | 10 + .../examples/AnalogOutput/AnalogOutput.ino | 65 + .../FastLED-master/examples/Blink/Blink.ino | 54 + .../examples/ColorPalette/ColorPalette.ino | 188 ++ .../ColorTemperature/ColorTemperature.ino | 85 + .../FastLED-master/examples/Cylon/Cylon.ino | 53 + .../examples/DemoReel100/DemoReel100.ino | 126 + .../examples/Fire2012/Fire2012.ino | 105 + .../Fire2012WithPalette.ino | 164 ++ .../examples/FirstLight/FirstLight.ino | 78 + .../ArrayOfLedArrays/ArrayOfLedArrays.ino | 37 + .../MirroringSample/MirroringSample.ino | 44 + .../Multiple/MultiArrays/MultiArrays.ino | 52 + .../MultipleStripsInOneArray.ino | 34 + .../OctoWS2811Demo/OctoWS2811Demo.ino | 37 + .../ParallelOutputDemo/ParallelOutputDemo.ino | 47 + .../FastLED-master/examples/Noise/Noise.ino | 112 + .../NoisePlayground/NoisePlayground.ino | 73 + .../NoisePlusPalette/NoisePlusPalette.ino | 273 ++ .../examples/Pintest/Pintest.ino | 105 + .../PJRCSpectrumAnalyzer.ino | 136 + .../examples/RGBCalibrate/RGBCalibrate.ino | 70 + .../examples/RGBSetDemo/RGBSetDemo.ino | 22 + .../examples/SmartMatrix/SmartMatrix.ino | 121 + .../examples/XYMatrix/XYMatrix.ino | 196 ++ libraries/FastLED-master/extras/AppleII.s65 | 40 + .../FastLED-master/extras/FastLED6502.s65 | 633 +++++ .../FastLED-master/extras/RainbowDemo.bin.zip | Bin 0 -> 848 bytes .../FastLED-master/extras/RainbowDemo.s65 | 89 + libraries/FastLED-master/fastled_config.h | 56 + libraries/FastLED-master/fastled_delay.h | 133 + libraries/FastLED-master/fastled_progmem.h | 81 + libraries/FastLED-master/fastpin.h | 248 ++ libraries/FastLED-master/fastspi.h | 134 + libraries/FastLED-master/fastspi_bitbang.h | 362 +++ libraries/FastLED-master/fastspi_dma.h | 0 libraries/FastLED-master/fastspi_nop.h | 64 + libraries/FastLED-master/fastspi_ref.h | 95 + libraries/FastLED-master/fastspi_types.h | 43 + libraries/FastLED-master/hsv2rgb.cpp | 714 +++++ libraries/FastLED-master/hsv2rgb.h | 91 + libraries/FastLED-master/keywords.txt | 373 +++ libraries/FastLED-master/led_sysdefs.h | 48 + libraries/FastLED-master/lib8tion.cpp | 251 ++ libraries/FastLED-master/lib8tion.h | 1125 ++++++++ libraries/FastLED-master/lib8tion/math8.h | 523 ++++ libraries/FastLED-master/lib8tion/random8.h | 94 + libraries/FastLED-master/lib8tion/scale8.h | 712 +++++ libraries/FastLED-master/lib8tion/trig8.h | 259 ++ libraries/FastLED-master/library.json | 30 + libraries/FastLED-master/library.properties | 9 + libraries/FastLED-master/noise.cpp | 788 ++++++ libraries/FastLED-master/noise.h | 97 + libraries/FastLED-master/pixelset.h | 305 +++ libraries/FastLED-master/pixeltypes.h | 867 ++++++ libraries/FastLED-master/platforms.h | 35 + .../platforms/arm/common/m0clockless.h | 318 +++ .../platforms/arm/d21/clockless_arm_d21.h | 61 + .../platforms/arm/d21/fastled_arm_d21.h | 7 + .../platforms/arm/d21/fastpin_arm_d21.h | 164 ++ .../platforms/arm/d21/led_sysdefs_arm_d21.h | 26 + .../platforms/arm/k20/clockless_arm_k20.h | 124 + .../arm/k20/clockless_block_arm_k20.h | 330 +++ .../platforms/arm/k20/fastled_arm_k20.h | 13 + .../platforms/arm/k20/fastpin_arm_k20.h | 120 + .../platforms/arm/k20/fastspi_arm_k20.h | 466 ++++ .../platforms/arm/k20/led_sysdefs_arm_k20.h | 46 + .../platforms/arm/k20/octows2811_controller.h | 66 + .../platforms/arm/k20/smartmatrix_t3.h | 55 + .../arm/k20/ws2812serial_controller.h | 46 + .../platforms/arm/k66/clockless_arm_k66.h | 124 + .../arm/k66/clockless_block_arm_k66.h | 344 +++ .../platforms/arm/k66/fastled_arm_k66.h | 14 + .../platforms/arm/k66/fastpin_arm_k66.h | 128 + .../platforms/arm/k66/fastspi_arm_k66.h | 470 ++++ .../platforms/arm/k66/led_sysdefs_arm_k66.h | 46 + .../platforms/arm/kl26/clockless_arm_kl26.h | 65 + .../platforms/arm/kl26/fastled_arm_kl26.h | 10 + .../platforms/arm/kl26/fastpin_arm_kl26.h | 88 + .../platforms/arm/kl26/fastspi_arm_kl26.h | 252 ++ .../platforms/arm/kl26/led_sysdefs_arm_kl26.h | 47 + .../platforms/arm/nrf51/clockless_arm_nrf51.h | 83 + .../platforms/arm/nrf51/fastled_arm_nrf51.h | 9 + .../platforms/arm/nrf51/fastpin_arm_nrf51.h | 119 + .../platforms/arm/nrf51/fastspi_arm_nrf51.h | 150 ++ .../arm/nrf51/led_sysdefs_arm_nrf51.h | 46 + .../platforms/arm/sam/clockless_arm_sam.h | 120 + .../arm/sam/clockless_block_arm_sam.h | 184 ++ .../platforms/arm/sam/fastled_arm_sam.h | 10 + .../platforms/arm/sam/fastpin_arm_sam.h | 137 + .../platforms/arm/sam/fastspi_arm_sam.h | 163 ++ .../platforms/arm/sam/led_sysdefs_arm_sam.h | 39 + .../platforms/arm/stm32/clockless_arm_stm32.h | 126 + .../platforms/arm/stm32/fastled_arm_stm32.h | 9 + .../platforms/arm/stm32/fastpin_arm_stm32.h | 105 + .../arm/stm32/led_sysdefs_arm_stm32.h | 47 + .../platforms/avr/clockless_trinket.h | 464 ++++ .../platforms/avr/fastled_avr.h | 13 + .../platforms/avr/fastpin_avr.h | 341 +++ .../platforms/avr/fastspi_avr.h | 505 ++++ .../platforms/avr/led_sysdefs_avr.h | 64 + .../platforms/esp/32/clockless_block_esp32.h | 168 ++ .../platforms/esp/32/clockless_esp32.h | 125 + .../platforms/esp/32/fastled_esp32.h | 5 + .../platforms/esp/32/fastpin_esp32.h | 92 + .../platforms/esp/32/led_sysdefs_esp32.h | 33 + .../esp/8266/clockless_block_esp8266.h | 159 ++ .../platforms/esp/8266/clockless_esp8266.h | 117 + .../platforms/esp/8266/fastled_esp8266.h | 5 + .../platforms/esp/8266/fastpin_esp8266.h | 101 + .../platforms/esp/8266/led_sysdefs_esp8266.h | 39 + libraries/FastLED-master/power_mgt.cpp | 185 ++ libraries/FastLED-master/power_mgt.h | 88 + libraries/FastLED-master/preview_changes.txt | 19 + libraries/FastLED-master/release_notes.md | 169 ++ libraries/FastLED-master/wiring.cpp | 238 ++ .../examples/EmulateEEPROM/EmulateEEPROM.ino | 56 + .../FlashStoreAndRetrieve.ino | 38 + .../StoreNameAndSurname.ino | 81 + libraries/FlashStorage-master/library.json | 20 + .../FlashStorage-master/library.properties | 9 + libraries/FlashStorage-master/readme.md | 93 + .../FlashStorage-master/src/FlashAsEEPROM.cpp | 74 + .../FlashStorage-master/src/FlashAsEEPROM.h | 87 + .../FlashStorage-master/src/FlashStorage.cpp | 101 + .../FlashStorage-master/src/FlashStorage.h | 77 + libraries/readme.txt | 1 + 153 files changed, 28548 insertions(+) create mode 100644 libraries/Adafruit_DotStar/Adafruit_DotStar.cpp create mode 100644 libraries/Adafruit_DotStar/Adafruit_DotStar.h create mode 100644 libraries/Adafruit_DotStar/COPYING create mode 100644 libraries/Adafruit_DotStar/README.md create mode 100644 libraries/Adafruit_DotStar/examples/ItsyBitsyM4Onboard/ItsyBitsyM4Onboard.ino create mode 100644 libraries/Adafruit_DotStar/examples/strandtest/strandtest.ino create mode 100644 libraries/Adafruit_DotStar/keywords.txt create mode 100644 libraries/Adafruit_DotStar/library.properties create mode 100644 libraries/FastLED-master/.gitignore create mode 100644 libraries/FastLED-master/FastLED.cpp create mode 100644 libraries/FastLED-master/FastLED.h create mode 100644 libraries/FastLED-master/LICENSE create mode 100644 libraries/FastLED-master/PORTING.md create mode 100644 libraries/FastLED-master/README.md create mode 100644 libraries/FastLED-master/bitswap.cpp create mode 100644 libraries/FastLED-master/bitswap.h create mode 100644 libraries/FastLED-master/chipsets.h create mode 100644 libraries/FastLED-master/color.h create mode 100644 libraries/FastLED-master/colorpalettes.cpp create mode 100644 libraries/FastLED-master/colorpalettes.h create mode 100644 libraries/FastLED-master/colorutils.cpp create mode 100644 libraries/FastLED-master/colorutils.h create mode 100644 libraries/FastLED-master/controller.h create mode 100644 libraries/FastLED-master/cpp_compat.h create mode 100644 libraries/FastLED-master/dmx.h create mode 100644 libraries/FastLED-master/docs/Doxyfile create mode 100644 libraries/FastLED-master/docs/mainpage.dox create mode 100644 libraries/FastLED-master/examples/AnalogOutput/AnalogOutput.ino create mode 100644 libraries/FastLED-master/examples/Blink/Blink.ino create mode 100644 libraries/FastLED-master/examples/ColorPalette/ColorPalette.ino create mode 100644 libraries/FastLED-master/examples/ColorTemperature/ColorTemperature.ino create mode 100644 libraries/FastLED-master/examples/Cylon/Cylon.ino create mode 100644 libraries/FastLED-master/examples/DemoReel100/DemoReel100.ino create mode 100644 libraries/FastLED-master/examples/Fire2012/Fire2012.ino create mode 100644 libraries/FastLED-master/examples/Fire2012WithPalette/Fire2012WithPalette.ino create mode 100644 libraries/FastLED-master/examples/FirstLight/FirstLight.ino create mode 100644 libraries/FastLED-master/examples/Multiple/ArrayOfLedArrays/ArrayOfLedArrays.ino create mode 100644 libraries/FastLED-master/examples/Multiple/MirroringSample/MirroringSample.ino create mode 100644 libraries/FastLED-master/examples/Multiple/MultiArrays/MultiArrays.ino create mode 100644 libraries/FastLED-master/examples/Multiple/MultipleStripsInOneArray/MultipleStripsInOneArray.ino create mode 100644 libraries/FastLED-master/examples/Multiple/OctoWS2811Demo/OctoWS2811Demo.ino create mode 100644 libraries/FastLED-master/examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino create mode 100644 libraries/FastLED-master/examples/Noise/Noise.ino create mode 100644 libraries/FastLED-master/examples/NoisePlayground/NoisePlayground.ino create mode 100644 libraries/FastLED-master/examples/NoisePlusPalette/NoisePlusPalette.ino create mode 100644 libraries/FastLED-master/examples/Pintest/Pintest.ino create mode 100644 libraries/FastLED-master/examples/Ports/PJRCSpectrumAnalyzer/PJRCSpectrumAnalyzer.ino create mode 100644 libraries/FastLED-master/examples/RGBCalibrate/RGBCalibrate.ino create mode 100644 libraries/FastLED-master/examples/RGBSetDemo/RGBSetDemo.ino create mode 100644 libraries/FastLED-master/examples/SmartMatrix/SmartMatrix.ino create mode 100644 libraries/FastLED-master/examples/XYMatrix/XYMatrix.ino create mode 100644 libraries/FastLED-master/extras/AppleII.s65 create mode 100644 libraries/FastLED-master/extras/FastLED6502.s65 create mode 100644 libraries/FastLED-master/extras/RainbowDemo.bin.zip create mode 100644 libraries/FastLED-master/extras/RainbowDemo.s65 create mode 100644 libraries/FastLED-master/fastled_config.h create mode 100644 libraries/FastLED-master/fastled_delay.h create mode 100644 libraries/FastLED-master/fastled_progmem.h create mode 100644 libraries/FastLED-master/fastpin.h create mode 100644 libraries/FastLED-master/fastspi.h create mode 100644 libraries/FastLED-master/fastspi_bitbang.h create mode 100644 libraries/FastLED-master/fastspi_dma.h create mode 100644 libraries/FastLED-master/fastspi_nop.h create mode 100644 libraries/FastLED-master/fastspi_ref.h create mode 100644 libraries/FastLED-master/fastspi_types.h create mode 100644 libraries/FastLED-master/hsv2rgb.cpp create mode 100644 libraries/FastLED-master/hsv2rgb.h create mode 100644 libraries/FastLED-master/keywords.txt create mode 100644 libraries/FastLED-master/led_sysdefs.h create mode 100644 libraries/FastLED-master/lib8tion.cpp create mode 100644 libraries/FastLED-master/lib8tion.h create mode 100644 libraries/FastLED-master/lib8tion/math8.h create mode 100644 libraries/FastLED-master/lib8tion/random8.h create mode 100644 libraries/FastLED-master/lib8tion/scale8.h create mode 100644 libraries/FastLED-master/lib8tion/trig8.h create mode 100644 libraries/FastLED-master/library.json create mode 100644 libraries/FastLED-master/library.properties create mode 100644 libraries/FastLED-master/noise.cpp create mode 100644 libraries/FastLED-master/noise.h create mode 100644 libraries/FastLED-master/pixelset.h create mode 100644 libraries/FastLED-master/pixeltypes.h create mode 100644 libraries/FastLED-master/platforms.h create mode 100644 libraries/FastLED-master/platforms/arm/common/m0clockless.h create mode 100644 libraries/FastLED-master/platforms/arm/d21/clockless_arm_d21.h create mode 100644 libraries/FastLED-master/platforms/arm/d21/fastled_arm_d21.h create mode 100644 libraries/FastLED-master/platforms/arm/d21/fastpin_arm_d21.h create mode 100644 libraries/FastLED-master/platforms/arm/d21/led_sysdefs_arm_d21.h create mode 100644 libraries/FastLED-master/platforms/arm/k20/clockless_arm_k20.h create mode 100644 libraries/FastLED-master/platforms/arm/k20/clockless_block_arm_k20.h create mode 100644 libraries/FastLED-master/platforms/arm/k20/fastled_arm_k20.h create mode 100644 libraries/FastLED-master/platforms/arm/k20/fastpin_arm_k20.h create mode 100644 libraries/FastLED-master/platforms/arm/k20/fastspi_arm_k20.h create mode 100644 libraries/FastLED-master/platforms/arm/k20/led_sysdefs_arm_k20.h create mode 100644 libraries/FastLED-master/platforms/arm/k20/octows2811_controller.h create mode 100644 libraries/FastLED-master/platforms/arm/k20/smartmatrix_t3.h create mode 100644 libraries/FastLED-master/platforms/arm/k20/ws2812serial_controller.h create mode 100644 libraries/FastLED-master/platforms/arm/k66/clockless_arm_k66.h create mode 100644 libraries/FastLED-master/platforms/arm/k66/clockless_block_arm_k66.h create mode 100644 libraries/FastLED-master/platforms/arm/k66/fastled_arm_k66.h create mode 100644 libraries/FastLED-master/platforms/arm/k66/fastpin_arm_k66.h create mode 100644 libraries/FastLED-master/platforms/arm/k66/fastspi_arm_k66.h create mode 100644 libraries/FastLED-master/platforms/arm/k66/led_sysdefs_arm_k66.h create mode 100644 libraries/FastLED-master/platforms/arm/kl26/clockless_arm_kl26.h create mode 100644 libraries/FastLED-master/platforms/arm/kl26/fastled_arm_kl26.h create mode 100644 libraries/FastLED-master/platforms/arm/kl26/fastpin_arm_kl26.h create mode 100644 libraries/FastLED-master/platforms/arm/kl26/fastspi_arm_kl26.h create mode 100644 libraries/FastLED-master/platforms/arm/kl26/led_sysdefs_arm_kl26.h create mode 100644 libraries/FastLED-master/platforms/arm/nrf51/clockless_arm_nrf51.h create mode 100644 libraries/FastLED-master/platforms/arm/nrf51/fastled_arm_nrf51.h create mode 100644 libraries/FastLED-master/platforms/arm/nrf51/fastpin_arm_nrf51.h create mode 100644 libraries/FastLED-master/platforms/arm/nrf51/fastspi_arm_nrf51.h create mode 100644 libraries/FastLED-master/platforms/arm/nrf51/led_sysdefs_arm_nrf51.h create mode 100644 libraries/FastLED-master/platforms/arm/sam/clockless_arm_sam.h create mode 100644 libraries/FastLED-master/platforms/arm/sam/clockless_block_arm_sam.h create mode 100644 libraries/FastLED-master/platforms/arm/sam/fastled_arm_sam.h create mode 100644 libraries/FastLED-master/platforms/arm/sam/fastpin_arm_sam.h create mode 100644 libraries/FastLED-master/platforms/arm/sam/fastspi_arm_sam.h create mode 100644 libraries/FastLED-master/platforms/arm/sam/led_sysdefs_arm_sam.h create mode 100644 libraries/FastLED-master/platforms/arm/stm32/clockless_arm_stm32.h create mode 100644 libraries/FastLED-master/platforms/arm/stm32/fastled_arm_stm32.h create mode 100644 libraries/FastLED-master/platforms/arm/stm32/fastpin_arm_stm32.h create mode 100644 libraries/FastLED-master/platforms/arm/stm32/led_sysdefs_arm_stm32.h create mode 100644 libraries/FastLED-master/platforms/avr/clockless_trinket.h create mode 100644 libraries/FastLED-master/platforms/avr/fastled_avr.h create mode 100644 libraries/FastLED-master/platforms/avr/fastpin_avr.h create mode 100644 libraries/FastLED-master/platforms/avr/fastspi_avr.h create mode 100644 libraries/FastLED-master/platforms/avr/led_sysdefs_avr.h create mode 100644 libraries/FastLED-master/platforms/esp/32/clockless_block_esp32.h create mode 100644 libraries/FastLED-master/platforms/esp/32/clockless_esp32.h create mode 100644 libraries/FastLED-master/platforms/esp/32/fastled_esp32.h create mode 100644 libraries/FastLED-master/platforms/esp/32/fastpin_esp32.h create mode 100644 libraries/FastLED-master/platforms/esp/32/led_sysdefs_esp32.h create mode 100644 libraries/FastLED-master/platforms/esp/8266/clockless_block_esp8266.h create mode 100644 libraries/FastLED-master/platforms/esp/8266/clockless_esp8266.h create mode 100644 libraries/FastLED-master/platforms/esp/8266/fastled_esp8266.h create mode 100644 libraries/FastLED-master/platforms/esp/8266/fastpin_esp8266.h create mode 100644 libraries/FastLED-master/platforms/esp/8266/led_sysdefs_esp8266.h create mode 100644 libraries/FastLED-master/power_mgt.cpp create mode 100644 libraries/FastLED-master/power_mgt.h create mode 100644 libraries/FastLED-master/preview_changes.txt create mode 100644 libraries/FastLED-master/release_notes.md create mode 100644 libraries/FastLED-master/wiring.cpp create mode 100644 libraries/FlashStorage-master/examples/EmulateEEPROM/EmulateEEPROM.ino create mode 100644 libraries/FlashStorage-master/examples/FlashStoreAndRetrieve/FlashStoreAndRetrieve.ino create mode 100644 libraries/FlashStorage-master/examples/StoreNameAndSurname/StoreNameAndSurname.ino create mode 100644 libraries/FlashStorage-master/library.json create mode 100644 libraries/FlashStorage-master/library.properties create mode 100644 libraries/FlashStorage-master/readme.md create mode 100644 libraries/FlashStorage-master/src/FlashAsEEPROM.cpp create mode 100644 libraries/FlashStorage-master/src/FlashAsEEPROM.h create mode 100644 libraries/FlashStorage-master/src/FlashStorage.cpp create mode 100644 libraries/FlashStorage-master/src/FlashStorage.h create mode 100644 libraries/readme.txt diff --git a/libraries/Adafruit_DotStar/Adafruit_DotStar.cpp b/libraries/Adafruit_DotStar/Adafruit_DotStar.cpp new file mode 100644 index 0000000000..fb3751fe33 --- /dev/null +++ b/libraries/Adafruit_DotStar/Adafruit_DotStar.cpp @@ -0,0 +1,615 @@ +/*! + * @file Adafruit_DotStar.cpp + * + * @mainpage Arduino Library for driving Adafruit DotStar addressable LEDs + * and compatible devicess -- APA102, etc. + * + * @section intro_sec Introduction + * + * This is the documentation for Adafruit's DotStar library for the + * Arduino platform, allowing a broad range of microcontroller boards + * (most AVR boards, many ARM devices, ESP8266 and ESP32, among others) + * to control Adafruit DotStars and compatible devices -- APA102, etc. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing products + * from Adafruit! + * + * @section author Author + * + * Written by Limor Fried and Phil Burgess for Adafruit Industries with + * contributions from members of the open source community. + * + * @section license License + * + * This file is part of the Adafruit_DotStar library. + * + * Adafruit_DotStar is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * Adafruit_DotStar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with DotStar. If not, see . + * + */ + +#include "Adafruit_DotStar.h" +#if !defined(__AVR_ATtiny85__) + #include +#endif + +#define USE_HW_SPI 255 ///< Assigned to dataPin to indicate 'hard' SPI + +/*! + @brief DotStar constructor for hardware SPI. Must be connected to + MOSI, SCK pins. + @param n Number of DotStars in strand. + @param o Pixel type -- one of the DOTSTAR_* constants defined in + Adafruit_DotStar.h, for example DOTSTAR_BRG for DotStars + expecting color bytes expressed in blue, red, green order + per pixel. Default if unspecified is DOTSTAR_BRG. + @return Adafruit_DotStar object. Call the begin() function before use. +*/ +Adafruit_DotStar::Adafruit_DotStar(uint16_t n, uint8_t o) : + numLEDs(n), dataPin(USE_HW_SPI), brightness(0), pixels(NULL), + rOffset(o & 3), gOffset((o >> 2) & 3), bOffset((o >> 4) & 3) +{ + updateLength(n); +} + +/*! + @brief DotStar constructor for 'soft' (bitbang) SPI. Any two pins + can be used. + @param n Number of DotStars in strand. + @param data Arduino pin number for data out. + @param clock Arduino pin number for clock out. + @param o Pixel type -- one of the DOTSTAR_* constants defined in + Adafruit_DotStar.h, for example DOTSTAR_BRG for DotStars + expecting color bytes expressed in blue, red, green order + per pixel. Default if unspecified is DOTSTAR_BRG. + @return Adafruit_DotStar object. Call the begin() function before use. +*/ +Adafruit_DotStar::Adafruit_DotStar(uint16_t n, uint8_t data, uint8_t clock, + uint8_t o) : dataPin(data), clockPin(clock), brightness(0), pixels(NULL), + rOffset(o & 3), gOffset((o >> 2) & 3), bOffset((o >> 4) & 3) +{ + updateLength(n); +} + +/*! + @brief Deallocate Adafruit_DotStar object, set data and clock pins + back to INPUT. +*/ +Adafruit_DotStar::~Adafruit_DotStar(void) { + free(pixels); + if(dataPin == USE_HW_SPI) hw_spi_end(); + else sw_spi_end(); +} + +/*! + @brief Initialize Adafruit_DotStar object -- sets data and clock pins + to outputs and initializes hardware SPI if necessary. +*/ +void Adafruit_DotStar::begin(void) { + if(dataPin == USE_HW_SPI) hw_spi_init(); + else sw_spi_init(); +} + +// Pins may be reassigned post-begin(), so a sketch can store hardware +// config in flash, SD card, etc. rather than hardcoded. Also permits +// "recycling" LED ram across multiple strips: set pins to first strip, +// render & write all data, reassign pins to next strip, render & write, +// etc. They won't update simultaneously, but usually unnoticeable. + +/*! + @brief Switch over to hardware SPI. DotStars must be connected to + MOSI, SCK pins. Data in pixel buffer is unaffected and can + continue to be used. +*/ +void Adafruit_DotStar::updatePins(void) { + sw_spi_end(); + dataPin = USE_HW_SPI; + hw_spi_init(); +} + +/*! + @brief Switch over to 'soft' (bitbang) SPI. DotStars can be connected + to any two pins. Data in pixel buffer is unaffected and can + continue to be used. + @param data Arduino pin number for data out. + @param clock Arduino pin number for clock out. +*/ +void Adafruit_DotStar::updatePins(uint8_t data, uint8_t clock) { + hw_spi_end(); + dataPin = data; + clockPin = clock; + sw_spi_init(); +} + +/*! + @brief Change the length of a previously-declared Adafruit_DotStar + strip object. Old data is deallocated and new data is cleared. + Pin numbers and pixel format are unchanged. + @param n New length of strip, in pixels. + @note This function is deprecated, here only for old projects that + may still be calling it. New projects should instead use the + 'new' keyword. +*/ +void Adafruit_DotStar::updateLength(uint16_t n) { + free(pixels); + uint16_t bytes = (rOffset == gOffset) ? + n + ((n + 3) / 4) : // MONO: 10 bits/pixel, round up to next byte + n * 3; // COLOR: 3 bytes/pixel + if((pixels = (uint8_t *)malloc(bytes))) { + numLEDs = n; + clear(); + } else { + numLEDs = 0; + } +} + +// SPI STUFF --------------------------------------------------------------- + +/*! + @brief Initialize hardware SPI. + @note This library is written in pre-SPI-transactions style and needs + some rewriting to correctly share the SPI bus with other devices. +*/ +void Adafruit_DotStar::hw_spi_init(void) { // Initialize hardware SPI +#ifdef __AVR_ATtiny85__ + PORTB &= ~(_BV(PORTB1) | _BV(PORTB2)); // Outputs + DDRB |= _BV(PORTB1) | _BV(PORTB2); // DO (NOT MOSI) + SCK +#elif (SPI_INTERFACES_COUNT > 0) || !defined(SPI_INTERFACES_COUNT) + SPI.begin(); + // Hardware SPI clock speeds are chosen to run at roughly 1-8 MHz for most + // boards, providing a slower but more reliable experience by default. If + // you want faster LED updates, experiment with the clock speeds to find + // what works best with your particular setup. + #if defined(__AVR__) || defined(CORE_TEENSY) || defined(__ARDUINO_ARC__) || defined(__ARDUINO_X86__) + SPI.setClockDivider(SPI_CLOCK_DIV2); // 8 MHz (6 MHz on Pro Trinket 3V) + #else + #ifdef ESP8266 + SPI.setFrequency(8000000L); + #elif defined(PIC32) + // Use begin/end transaction to set SPI clock rate + SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + SPI.endTransaction(); + #else + SPI.setClockDivider((F_CPU + 4000000L) / 8000000L); // 8-ish MHz on Due + #endif + #endif + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); +#endif +} + +/*! + @brief Stop hardware SPI. +*/ +void Adafruit_DotStar::hw_spi_end(void) { +#ifdef __AVR_ATtiny85__ + DDRB &= ~(_BV(PORTB1) | _BV(PORTB2)); // Inputs +#elif (SPI_INTERFACES_COUNT > 0) || !defined(SPI_INTERFACES_COUNT) + SPI.end(); +#endif +} + +/*! + @brief Initialize 'soft' (bitbang) SPI. Data and clock pins are set + to outputs. +*/ +void Adafruit_DotStar::sw_spi_init(void) { + pinMode(dataPin , OUTPUT); + pinMode(clockPin, OUTPUT); +#ifdef __AVR__ + dataPort = portOutputRegister(digitalPinToPort(dataPin)); + clockPort = portOutputRegister(digitalPinToPort(clockPin)); + dataPinMask = digitalPinToBitMask(dataPin); + clockPinMask = digitalPinToBitMask(clockPin); + *dataPort &= ~dataPinMask; + *clockPort &= ~clockPinMask; +#else + digitalWrite(dataPin , LOW); + digitalWrite(clockPin, LOW); +#endif +} + +/*! + @brief Stop 'soft' (bitbang) SPI. Data and clock pins are set to inputs. +*/ +void Adafruit_DotStar::sw_spi_end() { + pinMode(dataPin , INPUT); + pinMode(clockPin, INPUT); +} + +#ifdef __AVR_ATtiny85__ + +// Teensy/Gemma-specific stuff for hardware-half-assisted SPI + +#define SPIBIT \ + USICR = ((1< 0) || !defined(SPI_INTERFACES_COUNT) + +// All other boards have full-featured hardware support for SPI + +#define spi_out(n) (void)SPI.transfer(n) ///< Call hardware SPI function +// Pipelining reads next byte while current byte is clocked out +#if (defined(__AVR__) && !defined(__AVR_ATtiny85__)) || defined(CORE_TEENSY) + #define SPI_PIPELINE +#endif + +#else // no hardware spi +#define spi_out(n) sw_spi_out(n) + +#endif + +/*! + @brief Soft (bitbang) SPI write. + @param n 8-bit value to transfer. +*/ +void Adafruit_DotStar::sw_spi_out(uint8_t n) { + for(uint8_t i=8; i--; n <<= 1) { +#ifdef __AVR__ + if(n & 0x80) *dataPort |= dataPinMask; + else *dataPort &= ~dataPinMask; + *clockPort |= clockPinMask; + *clockPort &= ~clockPinMask; +#else + if(n & 0x80) digitalWrite(dataPin, HIGH); + else digitalWrite(dataPin, LOW); + digitalWrite(clockPin, HIGH); +#if F_CPU >= 48000000 + __asm__ volatile( + "nop \n nop" + ); +#endif + digitalWrite(clockPin, LOW); +#if F_CPU >= 48000000 + __asm__ volatile( + "nop \n nop" + ); +#endif +#endif + } +} + +/* ISSUE DATA TO LED STRIP ------------------------------------------------- + + Although the LED driver has an additional per-pixel 5-bit brightness + setting, it is NOT used or supported here. On APA102, the normally + very fast PWM is gated through a much slower PWM (about 400 Hz), + rendering it useless for POV or other high-speed things that are + probably why one is using DotStars instead of NeoPixels in the first + place. I'm told that some APA102 clones use current control rather than + PWM for this, which would be much more worthwhile. Still, no support + here, no plans for it. If you really can't live without it, you can fork + the library and add it for your own use, but any pull requests for this + are unlikely be merged for the foreseeable future. +*/ + +/*! + @brief Transmit pixel data in RAM to DotStars. +*/ +void Adafruit_DotStar::show(void) { + + if(!pixels) return; + + uint8_t *ptr = pixels, i; // -> LED data + uint16_t n = numLEDs; // Counter + uint16_t b16 = (uint16_t)brightness; // Type-convert for fixed-point math + + if(dataPin == USE_HW_SPI) { + + // TO DO: modernize this for SPI transactions + +#ifdef SPI_PIPELINE + uint8_t next; + for(i=0; i<3; i++) spi_out(0x00); // First 3 start-frame bytes + SPDR = 0x00; // 4th is pipelined + do { // For each pixel... + while(!(SPSR & _BV(SPIF))); // Wait for prior byte out + SPDR = 0xFF; // Pixel start + for(i=0; i<3; i++) { // For R,G,B... + next = brightness ? (*ptr++ * b16) >> 8 : *ptr++; // Read, scale + while(!(SPSR & _BV(SPIF))); // Wait for prior byte out + SPDR = next; // Write scaled color + } + } while(--n); + while(!(SPSR & _BV(SPIF))); // Wait for last byte out +#else + for(i=0; i<4; i++) spi_out(0x00); // 4 byte start-frame marker + if(brightness) { // Scale pixel brightness on output + do { // For each pixel... + spi_out(0xFF); // Pixel start + for(i=0; i<3; i++) spi_out((*ptr++ * b16) >> 8); // Scale, write RGB + } while(--n); + } else { // Full brightness (no scaling) + do { // For each pixel... + spi_out(0xFF); // Pixel start + for(i=0; i<3; i++) spi_out(*ptr++); // Write R,G,B + } while(--n); + } +#endif + // Four end-frame bytes are seemingly indistinguishable from a white + // pixel, and empirical testing suggests it can be left out...but it's + // always a good idea to follow the datasheet, in case future hardware + // revisions are more strict (e.g. might mandate use of end-frame + // before start-frame marker). i.e. let's not remove this. But after + // testing a bit more the suggestion is to use at least (numLeds+1)/2 + // high values (1) or (numLeds+15)/16 full bytes as EndFrame. For details see also: + // https://cpldcpu.wordpress.com/2014/11/30/understanding-the-apa102-superled/ + for(i=0; i<((numLEDs + 15) / 16); i++) spi_out(0xFF); + + } else { // Soft (bitbang) SPI + + for(i=0; i<4; i++) sw_spi_out(0); // Start-frame marker + if(brightness) { // Scale pixel brightness on output + do { // For each pixel... + sw_spi_out(0xFF); // Pixel start + for(i=0; i<3; i++) sw_spi_out((*ptr++ * b16) >> 8); // Scale, write + } while(--n); + } else { // Full brightness (no scaling) + do { // For each pixel... + sw_spi_out(0xFF); // Pixel start + for(i=0; i<3; i++) sw_spi_out(*ptr++); // R,G,B + } while(--n); + } + for(i=0; i<((numLEDs + 15) / 16); i++) sw_spi_out(0xFF); // End-frame marker (see note above) + } +} + +/*! + @brief Fill the whole DotStar strip with 0 / black / off. +*/ +void Adafruit_DotStar::clear() { + memset(pixels, 0, (rOffset == gOffset) ? + numLEDs + ((numLEDs + 3) / 4) : // MONO: 10 bits/pixel + numLEDs * 3); // COLOR: 3 bytes/pixel +} + +/*! + @brief Set a pixel's color using separate red, green and blue components. + @param n Pixel index, starting from 0. + @param r Red brightness, 0 = minimum (off), 255 = maximum. + @param g Green brightness, 0 = minimum (off), 255 = maximum. + @param b Blue brightness, 0 = minimum (off), 255 = maximum. +*/ +void Adafruit_DotStar::setPixelColor( + uint16_t n, uint8_t r, uint8_t g, uint8_t b) { + if(n < numLEDs) { + uint8_t *p = &pixels[n * 3]; + p[rOffset] = r; + p[gOffset] = g; + p[bOffset] = b; + } +} + +/*! + @brief Set a pixel's color using a 32-bit 'packed' RGB value. + @param n Pixel index, starting from 0. + @param c 32-bit color value. Most significant byte is 0, second is + red, then green, and least significant byte is blue. + e.g. 0x00RRGGBB +*/ +void Adafruit_DotStar::setPixelColor(uint16_t n, uint32_t c) { + if(n < numLEDs) { + uint8_t *p = &pixels[n * 3]; + p[rOffset] = (uint8_t)(c >> 16); + p[gOffset] = (uint8_t)(c >> 8); + p[bOffset] = (uint8_t)c; + } +} + +/*! + @brief Fill all or part of the DotStar strip with a color. + @param c 32-bit color value. Most significant byte is 0, second + is red, then green, and least significant byte is blue. + e.g. 0x00RRGGBB. If all arguments are unspecified, this + will be 0 (off). + @param first Index of first pixel to fill, starting from 0. Must be + in-bounds, no clipping is performed. 0 if unspecified. + @param count Number of pixels to fill, as a positive value. Passing + 0 or leaving unspecified will fill to end of strip. +*/ +void Adafruit_DotStar::fill(uint32_t c, uint16_t first, uint16_t count) { + uint16_t i, end; + + if(first >= numLEDs) { + return; // If first LED is past end of strip, nothing to do + } + + // Calculate the index ONE AFTER the last pixel to fill + if(count == 0) { + // Fill to end of strip + end = numLEDs; + } else { + // Ensure that the loop won't go past the last pixel + end = first + count; + if(end > numLEDs) end = numLEDs; + } + + for(i = first; i < end; i++) { + this->setPixelColor(i, c); + } +} + +/*! + @brief Convert hue, saturation and value into a packed 32-bit RGB color + that can be passed to setPixelColor() or other RGB-compatible + functions. + @param hue An unsigned 16-bit value, 0 to 65535, representing one full + loop of the color wheel, which allows 16-bit hues to "roll + over" while still doing the expected thing (and allowing + more precision than the wheel() function that was common to + prior DotStar and NeoPixel examples). + @param sat Saturation, 8-bit value, 0 (min or pure grayscale) to 255 + (max or pure hue). Default of 255 if unspecified. + @param val Value (brightness), 8-bit value, 0 (min / black / off) to + 255 (max or full brightness). Default of 255 if unspecified. + @return Packed 32-bit RGB color. Result is linearly but not perceptually + correct, so you may want to pass the result through the gamma32() + function (or your own gamma-correction operation) else colors may + appear washed out. This is not done automatically by this + function because coders may desire a more refined gamma- + correction function than the simplified one-size-fits-all + operation of gamma32(). Diffusing the LEDs also really seems to + help when using low-saturation colors. +*/ +uint32_t Adafruit_DotStar::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) { + + uint8_t r, g, b; + + // Remap 0-65535 to 0-1529. Pure red is CENTERED on the 64K rollover; + // 0 is not the start of pure red, but the midpoint...a few values above + // zero and a few below 65536 all yield pure red (similarly, 32768 is the + // midpoint, not start, of pure cyan). The 8-bit RGB hexcone (256 values + // each for red, green, blue) really only allows for 1530 distinct hues + // (not 1536, more on that below), but the full unsigned 16-bit type was + // chosen for hue so that one's code can easily handle a contiguous color + // wheel by allowing hue to roll over in either direction. + hue = (hue * 1530L + 32768) / 65536; + // Because red is centered on the rollover point (the +32768 above, + // essentially a fixed-point +0.5), the above actually yields 0 to 1530, + // where 0 and 1530 would yield the same thing. Rather than apply a + // costly modulo operator, 1530 is handled as a special case below. + + // So you'd think that the color "hexcone" (the thing that ramps from + // pure red, to pure yellow, to pure green and so forth back to red, + // yielding six slices), and with each color component having 256 + // possible values (0-255), might have 1536 possible items (6*256), + // but in reality there's 1530. This is because the last element in + // each 256-element slice is equal to the first element of the next + // slice, and keeping those in there this would create small + // discontinuities in the color wheel. So the last element of each + // slice is dropped...we regard only elements 0-254, with item 255 + // being picked up as element 0 of the next slice. Like this: + // Red to not-quite-pure-yellow is: 255, 0, 0 to 255, 254, 0 + // Pure yellow to not-quite-pure-green is: 255, 255, 0 to 1, 255, 0 + // Pure green to not-quite-pure-cyan is: 0, 255, 0 to 0, 255, 254 + // and so forth. Hence, 1530 distinct hues (0 to 1529), and hence why + // the constants below are not the multiples of 256 you might expect. + + // Convert hue to R,G,B (nested ifs faster than divide+mod+switch): + if(hue < 510) { // Red to Green-1 + b = 0; + if(hue < 255) { // Red to Yellow-1 + r = 255; + g = hue; // g = 0 to 254 + } else { // Yellow to Green-1 + r = 510 - hue; // r = 255 to 1 + g = 255; + } + } else if(hue < 1020) { // Green to Blue-1 + r = 0; + if(hue < 765) { // Green to Cyan-1 + g = 255; + b = hue - 510; // b = 0 to 254 + } else { // Cyan to Blue-1 + g = 1020 - hue; // g = 255 to 1 + b = 255; + } + } else if(hue < 1530) { // Blue to Red-1 + g = 0; + if(hue < 1275) { // Blue to Magenta-1 + r = hue - 1020; // r = 0 to 254 + b = 255; + } else { // Magenta to Red-1 + r = 255; + b = 1530 - hue; // b = 255 to 1 + } + } else { // Last 0.5 Red (quicker than % operator) + r = 255; + g = b = 0; + } + + // Apply saturation and value to R,G,B, pack into 32-bit result: + uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255 + uint16_t s1 = 1 + sat; // 1 to 256; same reason + uint8_t s2 = 255 - sat; // 255 to 0 + return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) | + (((((g * s1) >> 8) + s2) * v1) & 0xff00) | + ( ((((b * s1) >> 8) + s2) * v1) >> 8); +} + +/*! + @brief Query the color of a previously-set pixel. + @param n Index of pixel to read (0 = first). + @return 'Packed' 32-bit RGB value. Most significant byte is 0, second is + is red, then green, and least significant byte is blue. +*/ +uint32_t Adafruit_DotStar::getPixelColor(uint16_t n) const { + if(n >= numLEDs) return 0; + uint8_t *p = &pixels[n * 3]; + return ((uint32_t)p[rOffset] << 16) | + ((uint32_t)p[gOffset] << 8) | + (uint32_t)p[bOffset]; +} + +/*! + @brief Adjust output brightness. Does not immediately affect what's + currently displayed on the LEDs. The next call to show() will + refresh the LEDs at this level. + @param b Brightness setting, 0=minimum (off), 255=brightest. + @note For various reasons I think brightness is better handled in + one's sketch, but it's here for parity with the NeoPixel + library. Good news is that brightness setting in this library + is 'non destructive' -- it's applied as color data is being + issued to the strip, not during setPixelColor(), and also + means that getPixelColor() returns the exact value originally + stored. +*/ +void Adafruit_DotStar::setBrightness(uint8_t b) { + // Stored brightness value is different than what's passed. This + // optimizes the actual scaling math later, allowing a fast 8x8-bit + // multiply and taking the MSB. 'brightness' is a uint8_t, adding 1 + // here may (intentionally) roll over...so 0 = max brightness (color + // values are interpreted literally; no scaling), 1 = min brightness + // (off), 255 = just below max brightness. + brightness = b + 1; +} + +/*! + @brief Retrieve the last-set brightness value for the strip. + @return Brightness value: 0 = minimum (off), 255 = maximum. +*/ +uint8_t Adafruit_DotStar::getBrightness(void) const { + return brightness - 1; // Reverse above operation +} + +/*! + @brief A gamma-correction function for 32-bit packed RGB colors. + Makes color transitions appear more perceptially correct. + @param x 32-bit packed RGB color. + @return Gamma-adjusted packed color, can then be passed in one of the + setPixelColor() functions. Like gamma8(), this uses a fixed + gamma correction exponent of 2.6, which seems reasonably okay + for average DotStars in average tasks. If you need finer + control you'll need to provide your own gamma-correction + function instead. +*/ +uint32_t Adafruit_DotStar::gamma32(uint32_t x) { + uint8_t *y = (uint8_t *)&x; + // All four bytes of a 32-bit value are filtered to avoid a bunch of + // shifting and masking that would be necessary for properly handling + // different endianisms (and each byte is a fairly trivial operation, + // so it might not even be wasting cycles vs a check and branch. + // In theory this might cause trouble *if* someone's storing information + // in the unused most significant byte of an RGB value, but this seems + // exceedingly rare and if it's encountered in reality they can mask + // values going in or coming out. + for(uint8_t i=0; i<4; i++) y[i] = gamma8(y[i]); + return x; // Packed 32-bit return +} diff --git a/libraries/Adafruit_DotStar/Adafruit_DotStar.h b/libraries/Adafruit_DotStar/Adafruit_DotStar.h new file mode 100644 index 0000000000..5c002f1eab --- /dev/null +++ b/libraries/Adafruit_DotStar/Adafruit_DotStar.h @@ -0,0 +1,209 @@ +/*! + * @file Adafruit_DotStar.h + * + * This file is part of the Adafruit_DotStar library. + * + * Adafruit_DotStar is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * Adafruit_DotStar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with DotStar. If not, see . + * + */ + +#ifndef _ADAFRUIT_DOT_STAR_H_ +#define _ADAFRUIT_DOT_STAR_H_ + +#if (ARDUINO >= 100) + #include +#else + #include + #include +#endif + +// Color-order flag for LED pixels (optional extra parameter to constructor): +// Bits 0,1 = R index (0-2), bits 2,3 = G index, bits 4,5 = B index +#define DOTSTAR_RGB (0 | (1 << 2) | (2 << 4)) ///< Transmit as R,G,B +#define DOTSTAR_RBG (0 | (2 << 2) | (1 << 4)) ///< Transmit as R,B,G +#define DOTSTAR_GRB (1 | (0 << 2) | (2 << 4)) ///< Transmit as G,R,B +#define DOTSTAR_GBR (2 | (0 << 2) | (1 << 4)) ///< Transmit as G,B,R +#define DOTSTAR_BRG (1 | (2 << 2) | (0 << 4)) ///< Transmit as B,R,G +#define DOTSTAR_BGR (2 | (1 << 2) | (0 << 4)) ///< Transmit as B,G,R +#define DOTSTAR_MONO 0 ///< Single-color strip WIP DO NOT USE, use RGB for now + +// These two tables are declared outside the Adafruit_DotStar class +// because some boards may require oldschool compilers that don't +// handle the C++11 constexpr keyword. + +/* A PROGMEM (flash mem) table containing 8-bit unsigned sine wave (0-255). + Copy & paste this snippet into a Python REPL to regenerate: +import math +for x in range(256): + print("{:3},".format(int((math.sin(x/128.0*math.pi)+1.0)*127.5+0.5))), + if x&15 == 15: print +*/ +static const uint8_t PROGMEM _DotStarSineTable[256] = { + 128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173, + 176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215, + 218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244, + 245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255, + 255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246, + 245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220, + 218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179, + 176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131, + 128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82, + 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, + 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, + 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, + 10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, + 37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, + 79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124}; + +/* Similar to above, but for an 8-bit gamma-correction table. + Copy & paste this snippet into a Python REPL to regenerate: +import math +gamma=2.6 +for x in range(256): + print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))), + if x&15 == 15: print +*/ +static const uint8_t PROGMEM _DotStarGammaTable[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, + 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, + 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, + 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, + 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, + 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, + 76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, + 97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120, + 122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148, + 150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180, + 182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215, + 218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255}; + +/*! + @brief Class that stores state and functions for interacting with + Adafruit DotStars and compatible devices. +*/ +class Adafruit_DotStar { + + public: + + Adafruit_DotStar(uint16_t n, uint8_t o=DOTSTAR_BRG); + Adafruit_DotStar(uint16_t n, uint8_t d, uint8_t c, uint8_t o=DOTSTAR_BRG); + ~Adafruit_DotStar(void); + + void begin(void); + void show(void); + void setPixelColor(uint16_t n, uint32_t c); + void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); + void fill(uint32_t c=0, uint16_t first=0, uint16_t count=0); + void setBrightness(uint8_t); + void clear(); + void updateLength(uint16_t n); + void updatePins(void); + void updatePins(uint8_t d, uint8_t c); + /*! + @brief Get a pointer directly to the DotStar data buffer in RAM. + Pixel data is stored in a device-native format (a la the + DOTSTAR_* constants) and is not translated here. Applications + that access this buffer will need to be aware of the specific + data format and handle colors appropriately. + @return Pointer to DotStar buffer (uint8_t* array). + @note This is for high-performance applications where calling + setPixelColor() on every single pixel would be too slow (e.g. + POV or light-painting projects). There is no bounds checking + on the array, creating tremendous potential for mayhem if one + writes past the ends of the buffer. Great power, great + responsibility and all that. + */ + uint8_t *getPixels(void) const { return pixels; }; + uint8_t getBrightness(void) const; + /*! + @brief Return the number of pixels in an Adafruit_DotStar strip object. + @return Pixel count (0 if not set). + */ + uint16_t numPixels(void) const { return numLEDs; }; + uint32_t getPixelColor(uint16_t n) const; + /*! + @brief An 8-bit integer sine wave function, not directly compatible + with standard trigonometric units like radians or degrees. + @param x Input angle, 0-255; 256 would loop back to zero, completing + the circle (equivalent to 360 degrees or 2 pi radians). + One can therefore use an unsigned 8-bit variable and simply + add or subtract, allowing it to overflow/underflow and it + still does the expected contiguous thing. + @return Sine result, 0 to 255, or -128 to +127 if type-converted to + a signed int8_t, but you'll most likely want unsigned as this + output is often used for pixel brightness in animation effects. + */ + static uint8_t sine8(uint8_t x) { + return pgm_read_byte(&_DotStarSineTable[x]); // 0-255 in, 0-255 out + } + /*! + @brief An 8-bit gamma-correction function for basic pixel brightness + adjustment. Makes color transitions appear more perceptially + correct. + @param x Input brightness, 0 (minimum or off/black) to 255 (maximum). + @return Gamma-adjusted brightness, can then be passed to one of the + setPixelColor() functions. This uses a fixed gamma correction + exponent of 2.6, which seems reasonably okay for average + DotStars in average tasks. If you need finer control you'll + need to provide your own gamma-correction function instead. + */ + static uint8_t gamma8(uint8_t x) { + return pgm_read_byte(&_DotStarGammaTable[x]); // 0-255 in, 0-255 out + } + /*! + @brief Convert separate red, green and blue values into a single + "packed" 32-bit RGB color. + @param r Red brightness, 0 to 255. + @param g Green brightness, 0 to 255. + @param b Blue brightness, 0 to 255. + @return 32-bit packed RGB value, which can then be assigned to a + variable for later use or passed to the setPixelColor() + function. Packed RGB format is predictable, regardless of + LED strand color order. + */ + static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) { + return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; + } + static uint32_t ColorHSV(uint16_t hue, uint8_t sat=255, uint8_t val=255); + static uint32_t gamma32(uint32_t x); + + private: + + uint16_t numLEDs; ///< Number of pixels + uint8_t dataPin; ///< If soft SPI, data pin # + uint8_t clockPin; ///< If soft SPI, clock pin # + uint8_t brightness; ///< Global brightness setting + uint8_t *pixels; ///< LED RGB values (3 bytes ea.) + uint8_t rOffset; ///< Index of red in 3-byte pixel + uint8_t gOffset; ///< Index of green byte + uint8_t bOffset; ///< Index of blue byte +#ifdef __AVR__ + uint8_t dataPinMask; ///< If soft SPI, data pin bitmask + uint8_t clockPinMask; ///< If soft SPI, clock pin bitmask + volatile uint8_t *dataPort; ///< If soft SPI, data PORT + volatile uint8_t *clockPort; ///< If soft SPI, clock PORT +#endif + void hw_spi_init(void); ///< Start hardware SPI + void hw_spi_end(void); ///< Stop hardware SPI + void sw_spi_init(void); ///< Start bitbang SPI + void sw_spi_out(uint8_t n); ///< Bitbang SPI write + void sw_spi_end(void); ///< Stop bitbang SPI +}; + +#endif // _ADAFRUIT_DOT_STAR_H_ diff --git a/libraries/Adafruit_DotStar/COPYING b/libraries/Adafruit_DotStar/COPYING new file mode 100644 index 0000000000..7dcf8e8ae4 --- /dev/null +++ b/libraries/Adafruit_DotStar/COPYING @@ -0,0 +1,794 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + + +LGPL ADDENDUM: + + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/libraries/Adafruit_DotStar/README.md b/libraries/Adafruit_DotStar/README.md new file mode 100644 index 0000000000..0b63698f49 --- /dev/null +++ b/libraries/Adafruit_DotStar/README.md @@ -0,0 +1,3 @@ +# Adafruit DotStar Library [![Build Status](https://travis-ci.com/adafruit/Adafruit_DotStar.svg?branch=master)](https://travis-ci.com/adafruit/Adafruit_DotStar) + +Arduino library for controlling two-wire-based LED pixels and strips such as Adafruit DotStar LEDs and other APA102-compatible devices. diff --git a/libraries/Adafruit_DotStar/examples/ItsyBitsyM4Onboard/ItsyBitsyM4Onboard.ino b/libraries/Adafruit_DotStar/examples/ItsyBitsyM4Onboard/ItsyBitsyM4Onboard.ino new file mode 100644 index 0000000000..d038957b1f --- /dev/null +++ b/libraries/Adafruit_DotStar/examples/ItsyBitsyM4Onboard/ItsyBitsyM4Onboard.ino @@ -0,0 +1,49 @@ +// An example demonstrating how to control the Adafruit Dot Star RGB LED +// included on board the ItsyBitsy M4 board. + +#include + +// There is only one pixel on the board +#define NUMPIXELS 1 + +//Use these pin definitions for the ItsyBitsy M4 +#define DATAPIN 8 +#define CLOCKPIN 6 + +Adafruit_DotStar strip(NUMPIXELS, DATAPIN, CLOCKPIN, DOTSTAR_BRG); + +void setup() { + strip.begin(); // Initialize pins for output + strip.setBrightness(80); + strip.show(); // Turn all LEDs off ASAP +} + +void loop() { + rainbow(10); // Flowing rainbow cycle along the whole strip +} + + + +// Rainbow cycle along whole strip. Pass delay time (in ms) between frames. +void rainbow(int wait) { + // Hue of first pixel runs 5 complete loops through the color wheel. + // Color wheel has a range of 65536 but it's OK if we roll over, so + // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time + // means we'll make 5*65536/256 = 1280 passes through this outer loop: + for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) { + for(int i=0; i +// Because conditional #includes don't work w/Arduino sketches... +#include // COMMENT OUT THIS LINE FOR GEMMA OR TRINKET +//#include // ENABLE THIS LINE FOR GEMMA OR TRINKET + +#define NUMPIXELS 30 // Number of LEDs in strip + +// Here's how to control the LEDs from any two pins: +#define DATAPIN 4 +#define CLOCKPIN 5 +Adafruit_DotStar strip(NUMPIXELS, DATAPIN, CLOCKPIN, DOTSTAR_BRG); +// The last parameter is optional -- this is the color data order of the +// DotStar strip, which has changed over time in different production runs. +// Your code just uses R,G,B colors, the library then reassigns as needed. +// Default is DOTSTAR_BRG, so change this if you have an earlier strip. + +// Hardware SPI is a little faster, but must be wired to specific pins +// (Arduino Uno = pin 11 for data, 13 for clock, other boards are different). +//Adafruit_DotStar strip(NUMPIXELS, DOTSTAR_BRG); + +void setup() { + +#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000L) + clock_prescale_set(clock_div_1); // Enable 16 MHz on Trinket +#endif + + strip.begin(); // Initialize pins for output + strip.show(); // Turn all LEDs off ASAP +} + +// Runs 10 LEDs at a time along strip, cycling through red, green and blue. +// This requires about 200 mA for all the 'on' pixels + 1 mA per 'off' pixel. + +int head = 0, tail = -10; // Index of first 'on' and 'off' pixels +uint32_t color = 0xFF0000; // 'On' color (starts red) + +void loop() { + + strip.setPixelColor(head, color); // 'On' pixel at head + strip.setPixelColor(tail, 0); // 'Off' pixel at tail + strip.show(); // Refresh strip + delay(20); // Pause 20 milliseconds (~50 FPS) + + if(++head >= NUMPIXELS) { // Increment head index. Off end of strip? + head = 0; // Yes, reset head index to start + if((color >>= 8) == 0) // Next color (R->G->B) ... past blue now? + color = 0xFF0000; // Yes, reset to red + } + if(++tail >= NUMPIXELS) tail = 0; // Increment, reset tail index +} diff --git a/libraries/Adafruit_DotStar/keywords.txt b/libraries/Adafruit_DotStar/keywords.txt new file mode 100644 index 0000000000..ac0001759f --- /dev/null +++ b/libraries/Adafruit_DotStar/keywords.txt @@ -0,0 +1,42 @@ +####################################### +# Syntax Coloring Map For Adafruit_DotStar +####################################### +# Class +####################################### + +Adafruit_DotStar KEYWORD1 + +####################################### +# Methods and Functions +####################################### + +begin KEYWORD2 +show KEYWORD2 +setPixelColor KEYWORD2 +fill KEYWORD2 +setBrightness KEYWORD2 +clear KEYWORD2 +updateLength KEYWORD2 +updatePins KEYWORD2 +getPixels KEYWORD2 +getBrightness KEYWORD2 +numPixels KEYWORD2 +getPixelColor KEYWORD2 +sine8 KEYWORD2 +gamma8 KEYWORD2 +Color KEYWORD2 +ColorHSV KEYWORD2 +gamma32 KEYWORD2 + +####################################### +# Constants +####################################### + +DOTSTAR_RGB LITERAL1 +DOTSTAR_RBG LITERAL1 +DOTSTAR_GRB LITERAL1 +DOTSTAR_GBR LITERAL1 +DOTSTAR_BRG LITERAL1 +DOTSTAR_BGR LITERAL1 +DOTSTAR_MONO LITERAL1 + diff --git a/libraries/Adafruit_DotStar/library.properties b/libraries/Adafruit_DotStar/library.properties new file mode 100644 index 0000000000..79756df0e1 --- /dev/null +++ b/libraries/Adafruit_DotStar/library.properties @@ -0,0 +1,9 @@ +name=Adafruit DotStar +version=1.1.3 +author=Adafruit +maintainer=Adafruit +sentence=Adafruit DotStar LED Library +paragraph=Adafruit DotStar LED Library +category=Display +url=https://github.com/adafruit/Adafruit_DotStar +architectures=* diff --git a/libraries/FastLED-master/.gitignore b/libraries/FastLED-master/.gitignore new file mode 100644 index 0000000000..60b7a717bd --- /dev/null +++ b/libraries/FastLED-master/.gitignore @@ -0,0 +1,2 @@ +html/ +*.gch diff --git a/libraries/FastLED-master/FastLED.cpp b/libraries/FastLED-master/FastLED.cpp new file mode 100644 index 0000000000..d3d389bac1 --- /dev/null +++ b/libraries/FastLED-master/FastLED.cpp @@ -0,0 +1,272 @@ +#define FASTLED_INTERNAL +#include "FastLED.h" + + +#if defined(__SAM3X8E__) +volatile uint32_t fuckit; +#endif + +FASTLED_NAMESPACE_BEGIN + +void *pSmartMatrix = NULL; + +CFastLED FastLED; + +CLEDController *CLEDController::m_pHead = NULL; +CLEDController *CLEDController::m_pTail = NULL; +static uint32_t lastshow = 0; + +uint32_t _frame_cnt=0; +uint32_t _retry_cnt=0; + +// uint32_t CRGB::Squant = ((uint32_t)((__TIME__[4]-'0') * 28))<<16 | ((__TIME__[6]-'0')*50)<<8 | ((__TIME__[7]-'0')*28); + +CFastLED::CFastLED() { + // clear out the array of led controllers + // m_nControllers = 0; + m_Scale = 255; + m_nFPS = 0; + m_pPowerFunc = NULL; + m_nPowerData = 0xFFFFFFFF; +} + +CLEDController &CFastLED::addLeds(CLEDController *pLed, + struct CRGB *data, + int nLedsOrOffset, int nLedsIfOffset) { + int nOffset = (nLedsIfOffset > 0) ? nLedsOrOffset : 0; + int nLeds = (nLedsIfOffset > 0) ? nLedsIfOffset : nLedsOrOffset; + + pLed->init(); + pLed->setLeds(data + nOffset, nLeds); + FastLED.setMaxRefreshRate(pLed->getMaxRefreshRate(),true); + return *pLed; +} + +void CFastLED::show(uint8_t scale) { + // guard against showing too rapidly + while(m_nMinMicros && ((micros()-lastshow) < m_nMinMicros)); + lastshow = micros(); + + // If we have a function for computing power, use it! + if(m_pPowerFunc) { + scale = (*m_pPowerFunc)(scale, m_nPowerData); + } + + CLEDController *pCur = CLEDController::head(); + while(pCur) { + uint8_t d = pCur->getDither(); + if(m_nFPS < 100) { pCur->setDither(0); } + pCur->showLeds(scale); + pCur->setDither(d); + pCur = pCur->next(); + } + countFPS(); +} + +int CFastLED::count() { + int x = 0; + CLEDController *pCur = CLEDController::head(); + while( pCur) { + x++; + pCur = pCur->next(); + } + return x; +} + +CLEDController & CFastLED::operator[](int x) { + CLEDController *pCur = CLEDController::head(); + while(x-- && pCur) { + pCur = pCur->next(); + } + if(pCur == NULL) { + return *(CLEDController::head()); + } else { + return *pCur; + } +} + +void CFastLED::showColor(const struct CRGB & color, uint8_t scale) { + while(m_nMinMicros && ((micros()-lastshow) < m_nMinMicros)); + lastshow = micros(); + + // If we have a function for computing power, use it! + if(m_pPowerFunc) { + scale = (*m_pPowerFunc)(scale, m_nPowerData); + } + + CLEDController *pCur = CLEDController::head(); + while(pCur) { + uint8_t d = pCur->getDither(); + if(m_nFPS < 100) { pCur->setDither(0); } + pCur->showColor(color, scale); + pCur->setDither(d); + pCur = pCur->next(); + } + countFPS(); +} + +void CFastLED::clear(bool writeData) { + if(writeData) { + showColor(CRGB(0,0,0), 0); + } + clearData(); +} + +void CFastLED::clearData() { + CLEDController *pCur = CLEDController::head(); + while(pCur) { + pCur->clearLedData(); + pCur = pCur->next(); + } +} + +void CFastLED::delay(unsigned long ms) { + unsigned long start = millis(); + do { +#ifndef FASTLED_ACCURATE_CLOCK + // make sure to allow at least one ms to pass to ensure the clock moves + // forward + ::delay(1); +#endif + show(); + yield(); + } + while((millis()-start) < ms); +} + +void CFastLED::setTemperature(const struct CRGB & temp) { + CLEDController *pCur = CLEDController::head(); + while(pCur) { + pCur->setTemperature(temp); + pCur = pCur->next(); + } +} + +void CFastLED::setCorrection(const struct CRGB & correction) { + CLEDController *pCur = CLEDController::head(); + while(pCur) { + pCur->setCorrection(correction); + pCur = pCur->next(); + } +} + +void CFastLED::setDither(uint8_t ditherMode) { + CLEDController *pCur = CLEDController::head(); + while(pCur) { + pCur->setDither(ditherMode); + pCur = pCur->next(); + } +} + +// +// template void transpose8(unsigned char A[8], unsigned char B[8]) { +// uint32_t x, y, t; +// +// // Load the array and pack it into x and y. +// y = *(unsigned int*)(A); +// x = *(unsigned int*)(A+4); +// +// // x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m]; +// // y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m]; +// + // // pre-transform x + // t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); + // t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); + // + // // pre-transform y + // t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); + // t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); + // + // // final transform + // t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); + // y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); + // x = t; +// +// B[7*n] = y; y >>= 8; +// B[6*n] = y; y >>= 8; +// B[5*n] = y; y >>= 8; +// B[4*n] = y; +// +// B[3*n] = x; x >>= 8; +// B[2*n] = x; x >>= 8; +// B[n] = x; x >>= 8; +// B[0] = x; +// // B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x>>0; +// // B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y>>0; +// } +// +// void transposeLines(Lines & out, Lines & in) { +// transpose8<1,2>(in.bytes, out.bytes); +// transpose8<1,2>(in.bytes + 8, out.bytes + 1); +// } + +extern int noise_min; +extern int noise_max; + +void CFastLED::countFPS(int nFrames) { + static int br = 0; + static uint32_t lastframe = 0; // millis(); + + if(br++ >= nFrames) { + uint32_t now = millis(); + now -= lastframe; + m_nFPS = (br * 1000) / now; + br = 0; + lastframe = millis(); + } +} + +void CFastLED::setMaxRefreshRate(uint16_t refresh, bool constrain) { + if(constrain) { + // if we're constraining, the new value of m_nMinMicros _must_ be higher than previously (because we're only + // allowed to slow things down if constraining) + if(refresh > 0) { + m_nMinMicros = ( (1000000/refresh) > m_nMinMicros) ? (1000000/refresh) : m_nMinMicros; + } + } else if(refresh > 0) { + m_nMinMicros = 1000000 / refresh; + } else { + m_nMinMicros = 0; + } +} + +extern "C" int atexit(void (* /*func*/ )()) { return 0; } + +#ifdef FASTLED_NEEDS_YIELD +extern "C" void yield(void) { } +#endif + +#ifdef NEED_CXX_BITS +namespace __cxxabiv1 +{ + #ifndef ESP8266 + extern "C" void __cxa_pure_virtual (void) {} + #endif + + /* guard variables */ + + /* The ABI requires a 64-bit type. */ + __extension__ typedef int __guard __attribute__((mode(__DI__))); + + extern "C" int __cxa_guard_acquire (__guard *) __attribute__((weak)); + extern "C" void __cxa_guard_release (__guard *) __attribute__((weak)); + extern "C" void __cxa_guard_abort (__guard *) __attribute__((weak)); + + extern "C" int __cxa_guard_acquire (__guard *g) + { + return !*(char *)(g); + } + + extern "C" void __cxa_guard_release (__guard *g) + { + *(char *)g = 1; + } + + extern "C" void __cxa_guard_abort (__guard *) + { + + } +} +#endif + +FASTLED_NAMESPACE_END diff --git a/libraries/FastLED-master/FastLED.h b/libraries/FastLED-master/FastLED.h new file mode 100644 index 0000000000..1ef543ff8e --- /dev/null +++ b/libraries/FastLED-master/FastLED.h @@ -0,0 +1,581 @@ +#ifndef __INC_FASTSPI_LED2_H +#define __INC_FASTSPI_LED2_H + +///@file FastLED.h +/// central include file for FastLED, defines the CFastLED class/object + +#define xstr(s) str(s) +#define str(s) #s + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) +#define FASTLED_HAS_PRAGMA_MESSAGE +#endif + +#define FASTLED_VERSION 3001006 +#ifndef FASTLED_INTERNAL +# ifdef FASTLED_HAS_PRAGMA_MESSAGE +// no please stop printing this I don't care +//# pragma message "FastLED version 3.001.006" +# else +# warning FastLED version 3.001.006 (Not really a warning, just telling you here.) +# endif +#endif + +#ifndef __PROG_TYPES_COMPAT__ +#define __PROG_TYPES_COMPAT__ +#endif + +#ifdef SmartMatrix_h +#include +#endif + +#ifdef DmxSimple_h +#include +#endif + +#ifdef DmxSerial_h +#include +#endif + +#include + +#include "cpp_compat.h" + +#include "fastled_config.h" +#include "led_sysdefs.h" + +// Utility functions +#include "fastled_delay.h" +#include "bitswap.h" + +#include "controller.h" +#include "fastpin.h" +#include "fastspi_types.h" +#include "./dmx.h" + +#include "platforms.h" +#include "fastled_progmem.h" + +#include "lib8tion.h" +#include "pixeltypes.h" +#include "hsv2rgb.h" +#include "colorutils.h" +#include "pixelset.h" +#include "colorpalettes.h" + +#include "noise.h" +#include "power_mgt.h" + +#include "fastspi.h" +#include "chipsets.h" + +FASTLED_NAMESPACE_BEGIN + +/// definitions for the spi chipset constants +enum ESPIChipsets { + LPD8806, + WS2801, + WS2803, + SM16716, + P9813, + APA102, + SK9822, + DOTSTAR +}; + +enum ESM { SMART_MATRIX }; +enum OWS2811 { OCTOWS2811,OCTOWS2811_400, OCTOWS2813}; +enum SWS2812 { WS2812SERIAL }; + +#ifdef HAS_PIXIE +template class PIXIE : public PixieController {}; +#endif + +#ifdef FASTLED_HAS_CLOCKLESS +template class NEOPIXEL : public WS2812Controller800Khz {}; +template class TM1829 : public TM1829Controller800Khz {}; +template class TM1812 : public TM1809Controller800Khz {}; +template class TM1809 : public TM1809Controller800Khz {}; +template class TM1804 : public TM1809Controller800Khz {}; +template class TM1803 : public TM1803Controller400Khz {}; +template class UCS1903 : public UCS1903Controller400Khz {}; +template class UCS1903B : public UCS1903BController800Khz {}; +template class UCS1904 : public UCS1904Controller800Khz {}; +template class UCS2903 : public UCS2903Controller {}; +template class WS2812 : public WS2812Controller800Khz {}; +template class WS2852 : public WS2812Controller800Khz {}; +template class WS2812B : public WS2812Controller800Khz {}; +template class SK6812 : public SK6812Controller {}; +template class SK6822 : public SK6822Controller {}; +template class APA106 : public SK6822Controller {}; +template class PL9823 : public PL9823Controller {}; +template class WS2811 : public WS2811Controller800Khz {}; +template class WS2813 : public WS2813Controller {}; +template class APA104 : public WS2811Controller800Khz {}; +template class WS2811_400 : public WS2811Controller400Khz {}; +template class GW6205 : public GW6205Controller800Khz {}; +template class GW6205_400 : public GW6205Controller400Khz {}; +template class LPD1886 : public LPD1886Controller1250Khz {}; +template class LPD1886_8BIT : public LPD1886Controller1250Khz_8bit {}; +#ifdef DmxSimple_h +template class DMXSIMPLE : public DMXSimpleController {}; +#endif +#ifdef DmxSerial_h +template class DMXSERIAL : public DMXSerialController {}; +#endif +#endif + +enum EBlockChipsets { +#ifdef PORTA_FIRST_PIN + WS2811_PORTA, + WS2813_PORTA, + WS2811_400_PORTA, + TM1803_PORTA, + UCS1903_PORTA, +#endif +#ifdef PORTB_FIRST_PIN + WS2811_PORTB, + WS2813_PORTB, + WS2811_400_PORTB, + TM1803_PORTB, + UCS1903_PORTB, +#endif +#ifdef PORTC_FIRST_PIN + WS2811_PORTC, + WS2813_PORTC, + WS2811_400_PORTC, + TM1803_PORTC, + UCS1903_PORTC, +#endif +#ifdef PORTD_FIRST_PIN + WS2811_PORTD, + WS2813_PORTD, + WS2811_400_PORTD, + TM1803_PORTD, + UCS1903_PORTD, +#endif +#ifdef HAS_PORTDC + WS2811_PORTDC, + WS2813_PORTDC, + WS2811_400_PORTDC, + TM1803_PORTDC, + UCS1903_PORTDC, +#endif +}; + +#if defined(LIB8_ATTINY) +#define NUM_CONTROLLERS 2 +#else +#define NUM_CONTROLLERS 8 +#endif + +typedef uint8_t (*power_func)(uint8_t scale, uint32_t data); + +/// High level controller interface for FastLED. This class manages controllers, global settings and trackings +/// such as brightness, and refresh rates, and provides access functions for driving led data to controllers +/// via the show/showColor/clear methods. +/// @nosubgrouping +class CFastLED { + // int m_nControllers; + uint8_t m_Scale; ///< The current global brightness scale setting + uint16_t m_nFPS; ///< Tracking for current FPS value + uint32_t m_nMinMicros; ///< minimum µs between frames, used for capping frame rates. + uint32_t m_nPowerData; ///< max power use parameter + power_func m_pPowerFunc; ///< function for overriding brightness when using FastLED.show(); + +public: + CFastLED(); + + + /// Add a CLEDController instance to the world. Exposed to the public to allow people to implement their own + /// CLEDController objects or instances. There are two ways to call this method (as well as the other addLeds) + /// variations. The first is with 3 arguments, in which case the arguments are the controller, a pointer to + /// led data, and the number of leds used by this controller. The second is with 4 arguments, in which case + /// the first two arguments are the same, the third argument is an offset into the CRGB data where this controller's + /// CRGB data begins, and the fourth argument is the number of leds for this controller object. + /// @param pLed - the led controller being added + /// @param data - base point to an array of CRGB data structures + /// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array + /// @param nLedsIfOffset - number of leds (4 argument version) + /// @returns a reference to the added controller + static CLEDController &addLeds(CLEDController *pLed, struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0); + + /// @name Adding SPI based controllers + //@{ + /// Add an SPI based CLEDController instance to the world. + /// There are two ways to call this method (as well as the other addLeds) + /// variations. The first is with 2 arguments, in which case the arguments are a pointer to + /// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case + /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's + /// CRGB data begins, and the third argument is the number of leds for this controller object. + /// + /// This method also takes a 1 to 5 template parameters for identifying the specific chipset, data and clock pins, + /// RGB ordering, and SPI data rate + /// @param data - base point to an array of CRGB data structures + /// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array + /// @param nLedsIfOffset - number of leds (4 argument version) + /// @tparam CHIPSET - the chipset type + /// @tparam DATA_PIN - the optional data pin for the leds (if omitted, will default to the first hardware SPI MOSI pin) + /// @tparam CLOCK_PIN - the optional clock pin for the leds (if omitted, will default to the first hardware SPI clock pin) + /// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in) + /// @tparam SPI_DATA_RATE - the data rate to drive the SPI clock at, defined using DATA_RATE_MHZ or DATA_RATE_KHZ macros + /// @returns a reference to the added controller + template CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + switch(CHIPSET) { + case LPD8806: { static LPD8806Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case WS2801: { static WS2801Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case WS2803: { static WS2803Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SM16716: { static SM16716Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case P9813: { static P9813Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case DOTSTAR: + case APA102: { static APA102Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SK9822: { static SK9822Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + } + } + + template static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + switch(CHIPSET) { + case LPD8806: { static LPD8806Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case WS2801: { static WS2801Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case WS2803: { static WS2803Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SM16716: { static SM16716Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case P9813: { static P9813Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case DOTSTAR: + case APA102: { static APA102Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SK9822: { static SK9822Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + } + } + + template static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + switch(CHIPSET) { + case LPD8806: { static LPD8806Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case WS2801: { static WS2801Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case WS2803: { static WS2803Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SM16716: { static SM16716Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case P9813: { static P9813Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case DOTSTAR: + case APA102: { static APA102Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SK9822: { static SK9822Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + } + } + +#ifdef SPI_DATA + template static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + return addLeds(data, nLedsOrOffset, nLedsIfOffset); + } + + template static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + return addLeds(data, nLedsOrOffset, nLedsIfOffset); + } + + template static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + return addLeds(data, nLedsOrOffset, nLedsIfOffset); + } + +#endif + //@} + +#ifdef FASTLED_HAS_CLOCKLESS + /// @name Adding 3-wire led controllers + //@{ + /// Add a clockless (aka 3wire, also DMX) based CLEDController instance to the world. + /// There are two ways to call this method (as well as the other addLeds) + /// variations. The first is with 2 arguments, in which case the arguments are a pointer to + /// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case + /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's + /// CRGB data begins, and the third argument is the number of leds for this controller object. + /// + /// This method also takes a 2 to 3 template parameters for identifying the specific chipset, data pin, and rgb ordering + /// RGB ordering, and SPI data rate + /// @param data - base point to an array of CRGB data structures + /// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array + /// @param nLedsIfOffset - number of leds (4 argument version) + /// @tparam CHIPSET - the chipset type (required) + /// @tparam DATA_PIN - the optional data pin for the leds (required) + /// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in) + /// @returns a reference to the added controller + template class CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER> + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + static CHIPSET c; + return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); + } + + template class CHIPSET, uint8_t DATA_PIN> + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + static CHIPSET c; + return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); + } + + template class CHIPSET, uint8_t DATA_PIN> + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + static CHIPSET c; + return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); + } + + #ifdef FASTSPI_USE_DMX_SIMPLE + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) + { + switch(CHIPSET) { + case DMX: { static DMXController controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); } + } + } + #endif + //@} +#endif + + /// @name Adding 3rd party library controllers + //@{ + /// Add a 3rd party library based CLEDController instance to the world. + /// There are two ways to call this method (as well as the other addLeds) + /// variations. The first is with 2 arguments, in which case the arguments are a pointer to + /// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case + /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's + /// CRGB data begins, and the third argument is the number of leds for this controller object. This class includes the SmartMatrix + /// and OctoWS2811 based controllers + /// + /// This method also takes a 1 to 2 template parameters for identifying the specific chipset and rgb ordering + /// RGB ordering, and SPI data rate + /// @param data - base point to an array of CRGB data structures + /// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array + /// @param nLedsIfOffset - number of leds (4 argument version) + /// @tparam CHIPSET - the chipset type (required) + /// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in) + /// @returns a reference to the added controller + template class CHIPSET, EOrder RGB_ORDER> + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + static CHIPSET c; + return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); + } + + template class CHIPSET> + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + static CHIPSET c; + return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); + } + +#ifdef USE_OCTOWS2811 + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) + { + switch(CHIPSET) { + case OCTOWS2811: { static COctoWS2811Controller controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); } + case OCTOWS2811_400: { static COctoWS2811Controller controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); } +#ifdef WS2813_800kHz + case OCTOWS2813: { static COctoWS2811Controller controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); } +#endif + } + } + + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) + { + return addLeds(data,nLedsOrOffset,nLedsIfOffset); + } + +#endif + +#ifdef USE_WS2812SERIAL + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) + { + static CWS2812SerialController controller; + return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); + } +#endif + +#ifdef SmartMatrix_h + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) + { + switch(CHIPSET) { + case SMART_MATRIX: { static CSmartMatrixController controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); } + } + } +#endif + //@} + + +#ifdef FASTLED_HAS_BLOCKLESS + + /// @name adding parallel output controllers + //@{ + /// Add a block based CLEDController instance to the world. + /// There are two ways to call this method (as well as the other addLeds) + /// variations. The first is with 2 arguments, in which case the arguments are a pointer to + /// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case + /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's + /// CRGB data begins, and the third argument is the number of leds for this controller object. + /// + /// This method also takes a 2 to 3 template parameters for identifying the specific chipset and rgb ordering + /// RGB ordering, and SPI data rate + /// @param data - base point to an array of CRGB data structures + /// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array + /// @param nLedsIfOffset - number of leds (4 argument version) + /// @tparam CHIPSET - the chipset/port type (required) + /// @tparam NUM_LANES - how many parallel lanes of output to write + /// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in) + /// @returns a reference to the added controller + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + switch(CHIPSET) { + #ifdef PORTA_FIRST_PIN + case WS2811_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2811_400_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2813_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case TM1803_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case UCS1903_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + #endif + #ifdef PORTB_FIRST_PIN + case WS2811_PORTB: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2811_400_PORTB: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2813_PORTB: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case TM1803_PORTB: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case UCS1903_PORTB: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + #endif + #ifdef PORTC_FIRST_PIN + case WS2811_PORTC: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2811_400_PORTC: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2813_PORTC: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case TM1803_PORTC: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case UCS1903_PORTC: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + #endif + #ifdef PORTD_FIRST_PIN + case WS2811_PORTD: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2811_400_PORTD: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2813_PORTD: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case TM1803_PORTD: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case UCS1903_PORTD: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + #endif + #ifdef HAS_PORTDC + case WS2811_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2811_400_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2813_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case TM1803_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case UCS1903_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + #endif + } + } + + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + return addLeds(data,nLedsOrOffset,nLedsIfOffset); + } + //@} +#endif + + /// Set the global brightness scaling + /// @param scale a 0-255 value for how much to scale all leds before writing them out + void setBrightness(uint8_t scale) { m_Scale = scale; } + + /// Get the current global brightness setting + /// @returns the current global brightness value + uint8_t getBrightness() { return m_Scale; } + + /// Set the maximum power to be used, given in volts and milliamps. + /// @param volts - how many volts the leds are being driven at (usually 5) + /// @param milliamps - the maximum milliamps of power draw you want + inline void setMaxPowerInVoltsAndMilliamps(uint8_t volts, uint32_t milliamps) { setMaxPowerInMilliWatts(volts * milliamps); } + + /// Set the maximum power to be used, given in milliwatts + /// @param milliwatts - the max power draw desired, in milliwatts + inline void setMaxPowerInMilliWatts(uint32_t milliwatts) { m_pPowerFunc = &calculate_max_brightness_for_power_mW; m_nPowerData = milliwatts; } + + /// Update all our controllers with the current led colors, using the passed in brightness + /// @param scale temporarily override the scale + void show(uint8_t scale); + + /// Update all our controllers with the current led colors + void show() { show(m_Scale); } + + /// clear the leds, wiping the local array of data, optionally black out the leds as well + /// @param writeData whether or not to write out to the leds as well + void clear(bool writeData = false); + + /// clear out the local data array + void clearData(); + + /// Set all leds on all controllers to the given color/scale + /// @param color what color to set the leds to + /// @param scale what brightness scale to show at + void showColor(const struct CRGB & color, uint8_t scale); + + /// Set all leds on all controllers to the given color + /// @param color what color to set the leds to + void showColor(const struct CRGB & color) { showColor(color, m_Scale); } + + /// Delay for the given number of milliseconds. Provided to allow the library to be used on platforms + /// that don't have a delay function (to allow code to be more portable). Note: this will call show + /// constantly to drive the dithering engine (and will call show at least once). + /// @param ms the number of milliseconds to pause for + void delay(unsigned long ms); + + /// Set a global color temperature. Sets the color temperature for all added led strips, overriding whatever + /// previous color temperature those controllers may have had + /// @param temp A CRGB structure describing the color temperature + void setTemperature(const struct CRGB & temp); + + /// Set a global color correction. Sets the color correction for all added led strips, + /// overriding whatever previous color correction those controllers may have had. + /// @param correction A CRGB structure describin the color correction. + void setCorrection(const struct CRGB & correction); + + /// Set the dithering mode. Sets the dithering mode for all added led strips, overriding + /// whatever previous dithering option those controllers may have had. + /// @param ditherMode - what type of dithering to use, either BINARY_DITHER or DISABLE_DITHER + void setDither(uint8_t ditherMode = BINARY_DITHER); + + /// Set the maximum refresh rate. This is global for all leds. Attempts to + /// call show faster than this rate will simply wait. Note that the refresh rate + /// defaults to the slowest refresh rate of all the leds added through addLeds. If + /// you wish to set/override this rate, be sure to call setMaxRefreshRate _after_ + /// adding all of your leds. + /// @param refresh - maximum refresh rate in hz + /// @param constrain - constrain refresh rate to the slowest speed yet set + void setMaxRefreshRate(uint16_t refresh, bool constrain=false); + + /// for debugging, will keep track of time between calls to countFPS, and every + /// nFrames calls, it will update an internal counter for the current FPS. + /// @todo make this a rolling counter + /// @param nFrames - how many frames to time for determining FPS + void countFPS(int nFrames=25); + + /// Get the number of frames/second being written out + /// @returns the most recently computed FPS value + uint16_t getFPS() { return m_nFPS; } + + /// Get how many controllers have been registered + /// @returns the number of controllers (strips) that have been added with addLeds + int count(); + + /// Get a reference to a registered controller + /// @returns a reference to the Nth controller + CLEDController & operator[](int x); + + /// Get the number of leds in the first controller + /// @returns the number of LEDs in the first controller + int size() { return (*this)[0].size(); } + + /// Get a pointer to led data for the first controller + /// @returns pointer to the CRGB buffer for the first controller + CRGB *leds() { return (*this)[0].leds(); } +}; + +#define FastSPI_LED FastLED +#define FastSPI_LED2 FastLED +#ifndef LEDS +#define LEDS FastLED +#endif + +extern CFastLED FastLED; + +// Warnings for undefined things +#ifndef HAS_HARDWARE_PIN_SUPPORT +#warning "No pin/port mappings found, pin access will be slightly slower. See fastpin.h for info." +#define NO_HARDWARE_PIN_SUPPORT +#endif + + +FASTLED_NAMESPACE_END + +#endif diff --git a/libraries/FastLED-master/LICENSE b/libraries/FastLED-master/LICENSE new file mode 100644 index 0000000000..ebe476330b --- /dev/null +++ b/libraries/FastLED-master/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 FastLED + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/libraries/FastLED-master/PORTING.md b/libraries/FastLED-master/PORTING.md new file mode 100644 index 0000000000..2b4ade2ed7 --- /dev/null +++ b/libraries/FastLED-master/PORTING.md @@ -0,0 +1,29 @@ +=New platform porting guide= + +== Setting up the basic files/folders == + +* Create platform directory (e.g. platforms/arm/kl26) +* Create configuration header led_sysdefs_arm_kl26.h: + * Define platform flags (like FASTLED_ARM/FASTLED_TEENSY) + * Define configuration parameters re: interrupts, or clock doubling + * Include extar system header files if needed +* Create main platform include, fastled_arm_kl26.h + * Include the various other header files as needed +* Modify led_sysdefs.h to conditionally include platform sysdefs header file +* Modify platforms.h to conditionally include platform fastled header + +== Porting fastpin.h == + +The heart of the FastLED library is the fast pin accesss. This is a templated class that provides 1-2 cycle pin access, bypassing digital write and other such things. As such, this will usually be the first bit of the library that you will want to port when moving to a new platform. Once you have FastPIN up and running then you can do some basic work like testing toggles or running bit-bang'd SPI output. + +There's two low level FastPin classes. There's the base FastPIN template class, and then there is FastPinBB which is for bit-banded access on those MCUs that support bitbanding. Note that the bitband class is optional and primarily useful in the implementation of other functionality internal to the platform. This file is also where you would do the pin to port/bit mapping defines. + +Explaining how the macros work and should be used is currently beyond the scope of this document. + +== Porting fastspi.h == + +This is where you define the low level interface to the hardware SPI system (including a writePixels method that does a bunch of housekeeping for writing led data). Use the fastspi_nop.h file as a reference for the methods that need to be implemented. There are ofteh other useful methods that can help with the internals of the SPI code, I recommend taking a look at how the various platforms implement their SPI classes. + +== Porting clockless.h == + +This is where you define the code for the clockless controllers. Across ARM platforms this will usually be fairly similar - though different arm platforms will have different clock sources that you can/should use. diff --git a/libraries/FastLED-master/README.md b/libraries/FastLED-master/README.md new file mode 100644 index 0000000000..ebf3bf0e8b --- /dev/null +++ b/libraries/FastLED-master/README.md @@ -0,0 +1,88 @@ +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/FastLED/public) + +IMPORTANT NOTE: For AVR based systems, avr-gcc 4.8.x is supported and tested. This means Arduino 1.6.5 and later. + + +FastLED 3.1 +=========== + +This is a library for easily & efficiently controlling a wide variety of LED chipsets, like the ones +sold by adafruit (Neopixel, DotStar, LPD8806), Sparkfun (WS2801), and aliexpress. In addition to writing to the +leds, this library also includes a number of functions for high-performing 8bit math for manipulating +your RGB values, as well as low level classes for abstracting out access to pins and SPI hardware, while +still keeping things as fast as possible. Tested with Arduino up to 1.6.5 from arduino.cc. + +Quick note for people installing from GitHub repo zips, rename the folder FastLED before copying it to your Arduino/libraries folder. Github likes putting -branchname into the name of the folder, which unfortunately, makes Arduino cranky! + +We have multiple goals with this library: + +* Quick start for new developers - hook up your leds and go, no need to think about specifics of the led chipsets being used +* Zero pain switching LED chipsets - you get some new leds that the library supports, just change the definition of LEDs you're using, et. voila! Your code is running with the new leds. +* High performance - with features like zero cost global brightness scaling, high performance 8-bit math for RGB manipulation, and some of the fastest bit-bang'd SPI support around, FastLED wants to keep as many CPU cycles available for your led patterns as possible + +## Getting help + +If you need help with using the library, please consider going to the google+ community first, which is at http://fastled.io/+ - there are hundreds of people in that group and many times you will get a quicker answer to your question there, as you will be likely to run into other people who have had the same issue. If you run into bugs with the library (compilation failures, the library doing the wrong thing), or if you'd like to request that we support a particular platform or LED chipset, then please open an issue at http://fastled.io/issues and we will try to figure out what is going wrong. + +## Simple example + +How quickly can you get up and running with the library? Here's a simple blink program: + + #include "FastLED.h" + #define NUM_LEDS 60 + CRGB leds[NUM_LEDS]; + void setup() { FastLED.addLeds(leds, NUM_LEDS); } + void loop() { + leds[0] = CRGB::White; FastLED.show(); delay(30); + leds[0] = CRGB::Black; FastLED.show(); delay(30); + } + +## Supported LED chipsets + +Here's a list of all the LED chipsets are supported. More details on the led chipsets are included *TODO: Link to wiki page* + +* Adafruit's DotStars - AKA the APA102 +* Adafruit's Neopixel - aka the WS2812B (also WS2811/WS2812/WS2813, also supported in lo-speed mode) - a 3 wire addressable led chipset +* TM1809/4 - 3 wire chipset, cheaply available on aliexpress.com +* TM1803 - 3 wire chipset, sold by radio shack +* UCS1903 - another 3 wire led chipset, cheap +* GW6205 - another 3 wire led chipset +* LPD8806 - SPI based chpiset, very high speed +* WS2801 - SPI based chipset, cheap and widely available +* SM16716 - SPI based chipset +* APA102 - SPI based chipset +* P9813 - aka Cool Neon's Total Control Lighting +* DMX - send rgb data out over DMX using arduino DMX libraries +* SmartMatrix panels - needs the SmartMatrix library - https://github.com/pixelmatix/SmartMatrix + + +LPD6803, HL1606, and "595"-style shift registers are no longer supported by the library. The older Version 1 of the library ("FastSPI_LED") has support for these, but is missing many of the advanced features of current versions and is no longer being maintained. + + +## Supported platforms + +Right now the library is supported on a variety of arduino compatable platforms. If it's ARM or AVR and uses the arduino software (or a modified version of it to build) then it is likely supported. Note that we have a long list of upcoming platforms to support, so if you don't see what you're looking for here, ask, it may be on the roadmap (or may already be supported). N.B. at the moment we are only supporting the stock compilers that ship with the arduino software. Support for upgraded compilers, as well as using AVR studio and skipping the arduino entirely, should be coming in a near future release. + +* Arduino & compatibles - straight up arduino devices, uno, duo, leonardo, mega, nano, etc... +* Arduino Yún +* Adafruit Trinket & Gemma - Trinket Pro may be supported, but haven't tested to confirm yet +* Teensy 2, Teensy++ 2, Teensy 3.0, Teensy 3.1/3.2, Teensy LC - arduino compataible from pjrc.com with some extra goodies (note the teensy 3, 3.1, and LC are ARM, not AVR!) +* Arduino Due and the digistump DigiX +* RFDuino +* SparkCore +* Arduino Zero +* ESP8266 using the arduino board definitions from http://arduino.esp8266.com/stable/package_esp8266com_index.json - please be sure to also read https://github.com/FastLED/FastLED/wiki/ESP8266-notes for information specific to the 8266. +* The wino board - http://wino-board.com + +What types of platforms are we thinking about supporting in the future? Here's a short list: ChipKit32, Maple, Beagleboard + +## What about that name? + +Wait, what happend to FastSPI_LED and FastSPI_LED2? The library was initially named FastSPI_LED because it was focused on very fast and efficient SPI access. However, since then, the library has expanded to support a number of LED chipsets that don't use SPI, as well as a number of math and utility functions for LED processing across the board. We decided that the name FastLED more accurately represents the totality of what the library provides, everything fast, for LEDs. + +## For more information + +Check out the official site http://fastled.io for links to documentation, issues, and news + + +*TODO* - get candy diff --git a/libraries/FastLED-master/bitswap.cpp b/libraries/FastLED-master/bitswap.cpp new file mode 100644 index 0000000000..67530c72ac --- /dev/null +++ b/libraries/FastLED-master/bitswap.cpp @@ -0,0 +1,28 @@ +#define FASTLED_INTERNAL +#include "FastLED.h" + +/// Simplified form of bits rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt - rotating +/// data into LSB for a faster write (the code using this data can happily walk the array backwards) +void transpose8x1_noinline(unsigned char *A, unsigned char *B) { + uint32_t x, y, t; + + // Load the array and pack it into x and y. + y = *(unsigned int*)(A); + x = *(unsigned int*)(A+4); + + // pre-transform x + t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); + t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); + + // pre-transform y + t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); + t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); + + // final transform + t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); + y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); + x = t; + + *((uint32_t*)B) = y; + *((uint32_t*)(B+4)) = x; +} diff --git a/libraries/FastLED-master/bitswap.h b/libraries/FastLED-master/bitswap.h new file mode 100644 index 0000000000..64fed49e3f --- /dev/null +++ b/libraries/FastLED-master/bitswap.h @@ -0,0 +1,276 @@ +#ifndef __INC_BITSWAP_H +#define __INC_BITSWAP_H + +#include "FastLED.h" + +FASTLED_NAMESPACE_BEGIN + +///@file bitswap.h +///Functions for rotating bits/bytes + +///@defgroup Bitswap Bit swapping/rotate +///Functions for doing a rotation of bits/bytes used by parallel output +///@{ +#if defined(FASTLED_ARM) || defined(FASTLED_ESP8266) +/// structure representing 8 bits of access +typedef union { + uint8_t raw; + struct { + uint32_t a0:1; + uint32_t a1:1; + uint32_t a2:1; + uint32_t a3:1; + uint32_t a4:1; + uint32_t a5:1; + uint32_t a6:1; + uint32_t a7:1; + }; +} just8bits; + +/// structure representing 32 bits of access +typedef struct { + uint32_t a0:1; + uint32_t a1:1; + uint32_t a2:1; + uint32_t a3:1; + uint32_t a4:1; + uint32_t a5:1; + uint32_t a6:1; + uint32_t a7:1; + uint32_t b0:1; + uint32_t b1:1; + uint32_t b2:1; + uint32_t b3:1; + uint32_t b4:1; + uint32_t b5:1; + uint32_t b6:1; + uint32_t b7:1; + uint32_t c0:1; + uint32_t c1:1; + uint32_t c2:1; + uint32_t c3:1; + uint32_t c4:1; + uint32_t c5:1; + uint32_t c6:1; + uint32_t c7:1; + uint32_t d0:1; + uint32_t d1:1; + uint32_t d2:1; + uint32_t d3:1; + uint32_t d4:1; + uint32_t d5:1; + uint32_t d6:1; + uint32_t d7:1; +} sub4; + +/// union containing a full 8 bytes to swap the bit orientation on +typedef union { + uint32_t word[2]; + uint8_t bytes[8]; + struct { + sub4 a; + sub4 b; + }; +} bitswap_type; + + +#define SWAPSA(X,N) out. X ## 0 = in.a.a ## N; \ + out. X ## 1 = in.a.b ## N; \ + out. X ## 2 = in.a.c ## N; \ + out. X ## 3 = in.a.d ## N; + +#define SWAPSB(X,N) out. X ## 0 = in.b.a ## N; \ + out. X ## 1 = in.b.b ## N; \ + out. X ## 2 = in.b.c ## N; \ + out. X ## 3 = in.b.d ## N; + +#define SWAPS(X,N) out. X ## 0 = in.a.a ## N; \ + out. X ## 1 = in.a.b ## N; \ + out. X ## 2 = in.a.c ## N; \ + out. X ## 3 = in.a.d ## N; \ + out. X ## 4 = in.b.a ## N; \ + out. X ## 5 = in.b.b ## N; \ + out. X ## 6 = in.b.c ## N; \ + out. X ## 7 = in.b.d ## N; + + +/// Do an 8byte by 8bit rotation +__attribute__((always_inline)) inline void swapbits8(bitswap_type in, bitswap_type & out) { + + // SWAPS(a.a,7); + // SWAPS(a.b,6); + // SWAPS(a.c,5); + // SWAPS(a.d,4); + // SWAPS(b.a,3); + // SWAPS(b.b,2); + // SWAPS(b.c,1); + // SWAPS(b.d,0); + + // SWAPSA(a.a,7); + // SWAPSA(a.b,6); + // SWAPSA(a.c,5); + // SWAPSA(a.d,4); + // + // SWAPSB(a.a,7); + // SWAPSB(a.b,6); + // SWAPSB(a.c,5); + // SWAPSB(a.d,4); + // + // SWAPSA(b.a,3); + // SWAPSA(b.b,2); + // SWAPSA(b.c,1); + // SWAPSA(b.d,0); + // // + // SWAPSB(b.a,3); + // SWAPSB(b.b,2); + // SWAPSB(b.c,1); + // SWAPSB(b.d,0); + + for(int i = 0; i < 8; i++) { + just8bits work; + work.a3 = in.word[0] >> 31; + work.a2 = in.word[0] >> 23; + work.a1 = in.word[0] >> 15; + work.a0 = in.word[0] >> 7; + in.word[0] <<= 1; + work.a7 = in.word[1] >> 31; + work.a6 = in.word[1] >> 23; + work.a5 = in.word[1] >> 15; + work.a4 = in.word[1] >> 7; + in.word[1] <<= 1; + out.bytes[i] = work.raw; + } +} + +/// Slow version of the 8 byte by 8 bit rotation +__attribute__((always_inline)) inline void slowswap(unsigned char *A, unsigned char *B) { + + for(int row = 0; row < 7; row++) { + uint8_t x = A[row]; + + uint8_t bit = (1<>= 1) { + if(x & mask) { + *p++ |= bit; + } else { + *p++ &= ~bit; + } + } + // B[7] |= (x & 0x01) << row; x >>= 1; + // B[6] |= (x & 0x01) << row; x >>= 1; + // B[5] |= (x & 0x01) << row; x >>= 1; + // B[4] |= (x & 0x01) << row; x >>= 1; + // B[3] |= (x & 0x01) << row; x >>= 1; + // B[2] |= (x & 0x01) << row; x >>= 1; + // B[1] |= (x & 0x01) << row; x >>= 1; + // B[0] |= (x & 0x01) << row; x >>= 1; + } +} + +void transpose8x1_noinline(unsigned char *A, unsigned char *B); + +/// Simplified form of bits rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt - rotating +/// data into LSB for a faster write (the code using this data can happily walk the array backwards) +__attribute__((always_inline)) inline void transpose8x1(unsigned char *A, unsigned char *B) { + uint32_t x, y, t; + + // Load the array and pack it into x and y. + y = *(unsigned int*)(A); + x = *(unsigned int*)(A+4); + + // pre-transform x + t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); + t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); + + // pre-transform y + t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); + t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); + + // final transform + t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); + y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); + x = t; + + *((uint32_t*)B) = y; + *((uint32_t*)(B+4)) = x; +} + +/// Simplified form of bits rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt +__attribute__((always_inline)) inline void transpose8x1_MSB(unsigned char *A, unsigned char *B) { + uint32_t x, y, t; + + // Load the array and pack it into x and y. + y = *(unsigned int*)(A); + x = *(unsigned int*)(A+4); + + // pre-transform x + t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); + t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); + + // pre-transform y + t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); + t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); + + // final transform + t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); + y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); + x = t; + + B[7] = y; y >>= 8; + B[6] = y; y >>= 8; + B[5] = y; y >>= 8; + B[4] = y; + + B[3] = x; x >>= 8; + B[2] = x; x >>= 8; + B[1] = x; x >>= 8; + B[0] = x; /* */ +} + +/// templated bit-rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt +template +__attribute__((always_inline)) inline void transpose8(unsigned char *A, unsigned char *B) { + uint32_t x, y, t; + + // Load the array and pack it into x and y. + if(m == 1) { + y = *(unsigned int*)(A); + x = *(unsigned int*)(A+4); + } else { + x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m]; + y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m]; + } + + // pre-transform x + t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); + t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); + + // pre-transform y + t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); + t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); + + // final transform + t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); + y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); + x = t; + + B[7*n] = y; y >>= 8; + B[6*n] = y; y >>= 8; + B[5*n] = y; y >>= 8; + B[4*n] = y; + + B[3*n] = x; x >>= 8; + B[2*n] = x; x >>= 8; + B[n] = x; x >>= 8; + B[0] = x; + // B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x>>0; + // B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y>>0; +} + +#endif + +FASTLED_NAMESPACE_END + +///@} +#endif diff --git a/libraries/FastLED-master/chipsets.h b/libraries/FastLED-master/chipsets.h new file mode 100644 index 0000000000..c41dbb986f --- /dev/null +++ b/libraries/FastLED-master/chipsets.h @@ -0,0 +1,506 @@ +#ifndef __INC_CHIPSETS_H +#define __INC_CHIPSETS_H + +#include "FastLED.h" +#include "pixeltypes.h" + +///@file chipsets.h +/// contains the bulk of the definitions for the various LED chipsets supported. + +FASTLED_NAMESPACE_BEGIN +///@defgroup chipsets +/// Implementations of CLEDController classes for various led chipsets. +/// +///@{ + +#if defined(ARDUINO) //&& defined(SoftwareSerial_h) + + +#if defined(SoftwareSerial_h) +#include "SoftwareSerial.h" + +#define HAS_PIXIE + +/// Adafruit Pixie controller class +/// @tparam DATAPIN the pin to write data out on +/// @tparam RGB_ORDER the RGB ordering for the led data +template +class PixieController : public CPixelLEDController { + SoftwareSerial Serial; + CMinWait<2000> mWait; +public: + PixieController() : Serial(-1, DATA_PIN) {} + +protected: + virtual void init() { + Serial.begin(115200); + mWait.mark(); + } + + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + while(pixels.has(1)) { + uint8_t r = pixels.loadAndScale0(); + Serial.write(r); + uint8_t g = pixels.loadAndScale1(); + Serial.write(g); + uint8_t b = pixels.loadAndScale2(); + Serial.write(b); + pixels.advanceData(); + pixels.stepDithering(); + } + mWait.mark(); + } + +}; + +// template +// class PixieController : public PixieBaseController { +// public: +// virtual void init() { +// STREAM.begin(115200); +// } +// }; +#endif +#endif + +///@name Clocked chipsets - nominally SPI based these chipsets have a data and a clock line. +///@{ +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// LPD8806 controller class - takes data/clock/select pin values (N.B. should take an SPI definition?) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// LPD8806 controller class. +/// @tparam DATA_PIN the data pin for these leds +/// @tparam CLOCK_PIN the clock pin for these leds +/// @tparam RGB_ORDER the RGB ordering for these leds +/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(12) +template +class LPD8806Controller : public CPixelLEDController { + typedef SPIOutput SPI; + + class LPD8806_ADJUST { + public: + // LPD8806 spec wants the high bit of every rgb data byte sent out to be set. + __attribute__((always_inline)) inline static uint8_t adjust(register uint8_t data) { return ((data>>1) | 0x80) + ((data && (data<254)) & 0x01); } + __attribute__((always_inline)) inline static void postBlock(int len) { + SPI::writeBytesValueRaw(0, ((len*3+63)>>6)); + } + + }; + + SPI mSPI; +public: + + LPD8806Controller() {} + virtual void init() { + mSPI.init(); + } + +protected: + + virtual void showPixels(PixelController & pixels) { + mSPI.template writePixels<0, LPD8806_ADJUST, RGB_ORDER>(pixels); + } +}; + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// WS2801 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// WS2801 controller class. +/// @tparam DATA_PIN the data pin for these leds +/// @tparam CLOCK_PIN the clock pin for these leds +/// @tparam RGB_ORDER the RGB ordering for these leds +/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(1) +template +class WS2801Controller : public CPixelLEDController { + typedef SPIOutput SPI; + SPI mSPI; + CMinWait<1000> mWaitDelay; +public: + WS2801Controller() {} + + virtual void init() { + mSPI.init(); + mWaitDelay.mark(); + } + +protected: + + virtual void showPixels(PixelController & pixels) { + mWaitDelay.wait(); + mSPI.template writePixels<0, DATA_NOP, RGB_ORDER>(pixels); + mWaitDelay.mark(); + } +}; + +template +class WS2803Controller : public WS2801Controller {}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// APA102 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// APA102 controller class. +/// @tparam DATA_PIN the data pin for these leds +/// @tparam CLOCK_PIN the clock pin for these leds +/// @tparam RGB_ORDER the RGB ordering for these leds +/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(12) +template +class APA102Controller : public CPixelLEDController { + typedef SPIOutput SPI; + SPI mSPI; + + void startBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); } + void endBoundary(int nLeds) { int nDWords = (nLeds/32); do { mSPI.writeByte(0xFF); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nDWords--); } + + inline void writeLed(uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) { + mSPI.writeByte(0xFF); mSPI.writeByte(b0); mSPI.writeByte(b1); mSPI.writeByte(b2); + } + +public: + APA102Controller() {} + + virtual void init() { + mSPI.init(); + } + +protected: + + virtual void showPixels(PixelController & pixels) { + mSPI.select(); + + startBoundary(); + while(pixels.has(1)) { +#ifdef FASTLED_SPI_BYTE_ONLY + mSPI.writeByte(0xFF); + mSPI.writeByte(pixels.loadAndScale0()); + mSPI.writeByte(pixels.loadAndScale1()); + mSPI.writeByte(pixels.loadAndScale2()); +#else + uint16_t b = 0xFF00 | (uint16_t)pixels.loadAndScale0(); + mSPI.writeWord(b); + uint16_t w = pixels.loadAndScale1() << 8; + w |= pixels.loadAndScale2(); + mSPI.writeWord(w); +#endif + pixels.stepDithering(); + pixels.advanceData(); + } + endBoundary(pixels.size()); + mSPI.waitFully(); + mSPI.release(); + } + +}; + +/// SK9822 controller class. +/// @tparam DATA_PIN the data pin for these leds +/// @tparam CLOCK_PIN the clock pin for these leds +/// @tparam RGB_ORDER the RGB ordering for these leds +/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(24) +template +class SK9822Controller : public CPixelLEDController { + typedef SPIOutput SPI; + SPI mSPI; + + void startBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); } + void endBoundary(int nLeds) { int nLongWords = (nLeds/32); do { mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nLongWords--); } + + inline void writeLed(uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) { + mSPI.writeByte(0xFF); mSPI.writeByte(b0); mSPI.writeByte(b1); mSPI.writeByte(b2); + } + +public: + SK9822Controller() {} + + virtual void init() { + mSPI.init(); + } + +protected: + + virtual void showPixels(PixelController & pixels) { + mSPI.select(); + + startBoundary(); + while(pixels.has(1)) { +#ifdef FASTLED_SPI_BYTE_ONLY + mSPI.writeByte(0xFF); + mSPI.writeByte(pixels.loadAndScale0()); + mSPI.writeByte(pixels.loadAndScale1()); + mSPI.writeByte(pixels.loadAndScale2()); +#else + uint16_t b = 0xFF00 | (uint16_t)pixels.loadAndScale0(); + mSPI.writeWord(b); + uint16_t w = pixels.loadAndScale1() << 8; + w |= pixels.loadAndScale2(); + mSPI.writeWord(w); +#endif + pixels.stepDithering(); + pixels.advanceData(); + } + + endBoundary(pixels.size()); + + mSPI.waitFully(); + mSPI.release(); + } + +}; + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// P9813 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// P9813 controller class. +/// @tparam DATA_PIN the data pin for these leds +/// @tparam CLOCK_PIN the clock pin for these leds +/// @tparam RGB_ORDER the RGB ordering for these leds +/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(10) +template +class P9813Controller : public CPixelLEDController { + typedef SPIOutput SPI; + SPI mSPI; + + void writeBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); } + + inline void writeLed(uint8_t r, uint8_t g, uint8_t b) __attribute__((always_inline)) { + register uint8_t top = 0xC0 | ((~b & 0xC0) >> 2) | ((~g & 0xC0) >> 4) | ((~r & 0xC0) >> 6); + mSPI.writeByte(top); mSPI.writeByte(b); mSPI.writeByte(g); mSPI.writeByte(r); + } + +public: + P9813Controller() {} + + virtual void init() { + mSPI.init(); + } + +protected: + + virtual void showPixels(PixelController & pixels) { + mSPI.select(); + + writeBoundary(); + while(pixels.has(1)) { + writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2()); + pixels.advanceData(); + pixels.stepDithering(); + } + writeBoundary(); + mSPI.waitFully(); + + mSPI.release(); + } + +}; + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// SM16716 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// SM16716 controller class. +/// @tparam DATA_PIN the data pin for these leds +/// @tparam CLOCK_PIN the clock pin for these leds +/// @tparam RGB_ORDER the RGB ordering for these leds +/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(16) +template +class SM16716Controller : public CPixelLEDController { + typedef SPIOutput SPI; + SPI mSPI; + + void writeHeader() { + // Write out 50 zeros to the spi line (6 blocks of 8 followed by two single bit writes) + mSPI.select(); + mSPI.writeBytesValueRaw(0, 6); + mSPI.waitFully(); + mSPI.template writeBit<0>(0); + mSPI.template writeBit<0>(0); + mSPI.release(); + } + +public: + SM16716Controller() {} + + virtual void init() { + mSPI.init(); + } + +protected: + + virtual void showPixels(PixelController & pixels) { + // Make sure the FLAG_START_BIT flag is set to ensure that an extra 1 bit is sent at the start + // of each triplet of bytes for rgb data + // writeHeader(); + mSPI.template writePixels( pixels ); + writeHeader(); + } + +}; +/// @} +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Clockless template instantiations - see clockless.h for how the timing values are used +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef FASTLED_HAS_CLOCKLESS +/// @name clockless controllers +/// Provides timing definitions for the variety of clockless controllers supplied by the library. +/// @{ + +// We want to force all avr's to use the Trinket controller when running at 8Mhz, because even the 328's at 8Mhz +// need the more tightly defined timeframes. +#if (F_CPU == 8000000 || F_CPU == 16000000 || F_CPU == 24000000) // || F_CPU == 48000000 || F_CPU == 96000000) // 125ns/clock +#define FMUL (F_CPU/8000000) +// LPD1886 +template +class LPD1886Controller1250Khz : public ClocklessController {}; + +// LPD1886 +template +class LPD1886Controller1250Khz_8bit : public ClocklessController {}; + +// WS2811@800khz 2 clocks, 5 clocks, 3 clocks +template +class WS2812Controller800Khz : public ClocklessController {}; + +template +class WS2811Controller800Khz : public ClocklessController {}; + +template //not tested +class WS2813Controller : public ClocklessController {}; + +template +class WS2811Controller400Khz : public ClocklessController {}; + +template +class SK6822Controller : public ClocklessController {}; + +template +class SK6812Controller : public ClocklessController {}; + +template +class UCS1903Controller400Khz : public ClocklessController {}; + +template +class UCS1903BController800Khz : public ClocklessController {}; + +template +class UCS1904Controller800Khz : public ClocklessController {}; + +template +class UCS2903Controller : public ClocklessController {}; + +template +class TM1809Controller800Khz : public ClocklessController {}; + +template +class TM1803Controller400Khz : public ClocklessController {}; + +template +class TM1829Controller800Khz : public ClocklessController {}; + +template +class GW6205Controller400Khz : public ClocklessController {}; + +template +class GW6205Controller800Khz : public ClocklessController {}; + +template +class PL9823Controller : public ClocklessController {}; + +#else +// GW6205@400khz - 800ns, 800ns, 800ns +template +class GW6205Controller400Khz : public ClocklessController {}; + +// GW6205@400khz - 400ns, 400ns, 400ns +template +class GW6205Controller800Khz : public ClocklessController {}; + +// UCS1903 - 500ns, 1500ns, 500ns +template +class UCS1903Controller400Khz : public ClocklessController {}; + +// UCS1903B - 400ns, 450ns, 450ns +template +class UCS1903BController800Khz : public ClocklessController {}; + +// UCS1904 - 400ns, 400ns, 450ns +template +class UCS1904Controller800Khz : public ClocklessController {}; + +// UCS2903 - 250ns, 750ns, 250ns +template +class UCS2903Controller : public ClocklessController {}; + +// TM1809 - 350ns, 350ns, 550ns +template +class TM1809Controller800Khz : public ClocklessController {}; + +// WS2811 - 320ns, 320ns, 640ns +template +class WS2811Controller800Khz : public ClocklessController {}; + +// WS2813 - 320ns, 320ns, 640ns +template +class WS2813Controller : public ClocklessController {}; + +// WS2812 - 250ns, 625ns, 375ns +template +class WS2812Controller800Khz : public ClocklessController {}; + +// WS2811@400khz - 800ns, 800ns, 900ns +template +class WS2811Controller400Khz : public ClocklessController {}; + +// 750NS, 750NS, 750NS +template +class TM1803Controller400Khz : public ClocklessController {}; + +template +class TM1829Controller800Khz : public ClocklessController {}; + +template +class TM1829Controller1600Khz : public ClocklessController {}; + +template +class LPD1886Controller1250Khz : public ClocklessController {}; + +template +class LPD1886Controller1250Khz_8bit : public ClocklessController {}; + + +template +class SK6822Controller : public ClocklessController {}; + +template +class SK6812Controller : public ClocklessController {}; + +template +class PL9823Controller : public ClocklessController {}; +#endif +///@} + +#endif +///@} +FASTLED_NAMESPACE_END + +#endif diff --git a/libraries/FastLED-master/color.h b/libraries/FastLED-master/color.h new file mode 100644 index 0000000000..1ed60b4d65 --- /dev/null +++ b/libraries/FastLED-master/color.h @@ -0,0 +1,84 @@ +#ifndef __INC_COLOR_H +#define __INC_COLOR_H + +#include "FastLED.h" + +FASTLED_NAMESPACE_BEGIN + +///@file color.h +/// contains definitions for color correction and temperature +///@defgroup ColorEnums Color correction/temperature +/// definitions for color correction and light temperatures +///@{ +typedef enum { + // Color correction starting points + + /// typical values for SMD5050 LEDs + ///@{ + TypicalSMD5050=0xFFB0F0 /* 255, 176, 240 */, + TypicalLEDStrip=0xFFB0F0 /* 255, 176, 240 */, + ///@} + + /// typical values for 8mm "pixels on a string" + /// also for many through-hole 'T' package LEDs + ///@{ + Typical8mmPixel=0xFFE08C /* 255, 224, 140 */, + TypicalPixelString=0xFFE08C /* 255, 224, 140 */, + ///@} + + /// uncorrected color + UncorrectedColor=0xFFFFFF + +} LEDColorCorrection; + + +typedef enum { + /// @name Black-body radiation light sources + /// Black-body radiation light sources emit a (relatively) continuous + /// spectrum, and can be described as having a Kelvin 'temperature' + ///@{ + /// 1900 Kelvin + Candle=0xFF9329 /* 1900 K, 255, 147, 41 */, + /// 2600 Kelvin + Tungsten40W=0xFFC58F /* 2600 K, 255, 197, 143 */, + /// 2850 Kelvin + Tungsten100W=0xFFD6AA /* 2850 K, 255, 214, 170 */, + /// 3200 Kelvin + Halogen=0xFFF1E0 /* 3200 K, 255, 241, 224 */, + /// 5200 Kelvin + CarbonArc=0xFFFAF4 /* 5200 K, 255, 250, 244 */, + /// 5400 Kelvin + HighNoonSun=0xFFFFFB /* 5400 K, 255, 255, 251 */, + /// 6000 Kelvin + DirectSunlight=0xFFFFFF /* 6000 K, 255, 255, 255 */, + /// 7000 Kelvin + OvercastSky=0xC9E2FF /* 7000 K, 201, 226, 255 */, + /// 20000 Kelvin + ClearBlueSky=0x409CFF /* 20000 K, 64, 156, 255 */, + ///@} + + /// @name Gaseous light sources + /// Gaseous light sources emit discrete spectral bands, and while we can + /// approximate their aggregate hue with RGB values, they don't actually + /// have a proper Kelvin temperature. + ///@{ + WarmFluorescent=0xFFF4E5 /* 0 K, 255, 244, 229 */, + StandardFluorescent=0xF4FFFA /* 0 K, 244, 255, 250 */, + CoolWhiteFluorescent=0xD4EBFF /* 0 K, 212, 235, 255 */, + FullSpectrumFluorescent=0xFFF4F2 /* 0 K, 255, 244, 242 */, + GrowLightFluorescent=0xFFEFF7 /* 0 K, 255, 239, 247 */, + BlackLightFluorescent=0xA700FF /* 0 K, 167, 0, 255 */, + MercuryVapor=0xD8F7FF /* 0 K, 216, 247, 255 */, + SodiumVapor=0xFFD1B2 /* 0 K, 255, 209, 178 */, + MetalHalide=0xF2FCFF /* 0 K, 242, 252, 255 */, + HighPressureSodium=0xFFB74C /* 0 K, 255, 183, 76 */, + ///@} + + /// Uncorrected temperature 0xFFFFFF + UncorrectedTemperature=0xFFFFFF +} ColorTemperature; + +FASTLED_NAMESPACE_END + +///@} +#endif diff --git a/libraries/FastLED-master/colorpalettes.cpp b/libraries/FastLED-master/colorpalettes.cpp new file mode 100644 index 0000000000..3c3a1f519b --- /dev/null +++ b/libraries/FastLED-master/colorpalettes.cpp @@ -0,0 +1,174 @@ +#ifndef __INC_COLORPALETTES_H +#define __INC_COLORPALETTES_H +#define FASTLED_INTERNAL +#include "FastLED.h" +#include "colorutils.h" +#include "colorpalettes.h" + +FASTLED_USING_NAMESPACE + + +// Preset color schemes, such as they are. + +// These schemes are all declared as "PROGMEM", meaning +// that they won't take up SRAM on AVR chips until used. +// Furthermore, the compiler won't even include these +// in your PROGMEM (flash) storage unless you specifically +// use each one, so you only 'pay for' those you actually use. + + +extern const TProgmemRGBPalette16 CloudColors_p FL_PROGMEM = +{ + CRGB::Blue, + CRGB::DarkBlue, + CRGB::DarkBlue, + CRGB::DarkBlue, + + CRGB::DarkBlue, + CRGB::DarkBlue, + CRGB::DarkBlue, + CRGB::DarkBlue, + + CRGB::Blue, + CRGB::DarkBlue, + CRGB::SkyBlue, + CRGB::SkyBlue, + + CRGB::LightBlue, + CRGB::White, + CRGB::LightBlue, + CRGB::SkyBlue +}; + +extern const TProgmemRGBPalette16 LavaColors_p FL_PROGMEM = +{ + CRGB::Black, + CRGB::Maroon, + CRGB::Black, + CRGB::Maroon, + + CRGB::DarkRed, + CRGB::Maroon, + CRGB::DarkRed, + + CRGB::DarkRed, + CRGB::DarkRed, + CRGB::Red, + CRGB::Orange, + + CRGB::White, + CRGB::Orange, + CRGB::Red, + CRGB::DarkRed +}; + + +extern const TProgmemRGBPalette16 OceanColors_p FL_PROGMEM = +{ + CRGB::MidnightBlue, + CRGB::DarkBlue, + CRGB::MidnightBlue, + CRGB::Navy, + + CRGB::DarkBlue, + CRGB::MediumBlue, + CRGB::SeaGreen, + CRGB::Teal, + + CRGB::CadetBlue, + CRGB::Blue, + CRGB::DarkCyan, + CRGB::CornflowerBlue, + + CRGB::Aquamarine, + CRGB::SeaGreen, + CRGB::Aqua, + CRGB::LightSkyBlue +}; + +extern const TProgmemRGBPalette16 ForestColors_p FL_PROGMEM = +{ + CRGB::DarkGreen, + CRGB::DarkGreen, + CRGB::DarkOliveGreen, + CRGB::DarkGreen, + + CRGB::Green, + CRGB::ForestGreen, + CRGB::OliveDrab, + CRGB::Green, + + CRGB::SeaGreen, + CRGB::MediumAquamarine, + CRGB::LimeGreen, + CRGB::YellowGreen, + + CRGB::LightGreen, + CRGB::LawnGreen, + CRGB::MediumAquamarine, + CRGB::ForestGreen +}; + +/// HSV Rainbow +extern const TProgmemRGBPalette16 RainbowColors_p FL_PROGMEM = +{ + 0xFF0000, 0xD52A00, 0xAB5500, 0xAB7F00, + 0xABAB00, 0x56D500, 0x00FF00, 0x00D52A, + 0x00AB55, 0x0056AA, 0x0000FF, 0x2A00D5, + 0x5500AB, 0x7F0081, 0xAB0055, 0xD5002B +}; + +/// HSV Rainbow colors with alternatating stripes of black +#define RainbowStripesColors_p RainbowStripeColors_p +extern const TProgmemRGBPalette16 RainbowStripeColors_p FL_PROGMEM = +{ + 0xFF0000, 0x000000, 0xAB5500, 0x000000, + 0xABAB00, 0x000000, 0x00FF00, 0x000000, + 0x00AB55, 0x000000, 0x0000FF, 0x000000, + 0x5500AB, 0x000000, 0xAB0055, 0x000000 +}; + +/// HSV color ramp: blue purple ping red orange yellow (and back) +/// Basically, everything but the greens, which tend to make +/// people's skin look unhealthy. This palette is good for +/// lighting at a club or party, where it'll be shining on people. +extern const TProgmemRGBPalette16 PartyColors_p FL_PROGMEM = +{ + 0x5500AB, 0x84007C, 0xB5004B, 0xE5001B, + 0xE81700, 0xB84700, 0xAB7700, 0xABAB00, + 0xAB5500, 0xDD2200, 0xF2000E, 0xC2003E, + 0x8F0071, 0x5F00A1, 0x2F00D0, 0x0007F9 +}; + +/// Approximate "black body radiation" palette, akin to +/// the FastLED 'HeatColor' function. +/// Recommend that you use values 0-240 rather than +/// the usual 0-255, as the last 15 colors will be +/// 'wrapping around' from the hot end to the cold end, +/// which looks wrong. +extern const TProgmemRGBPalette16 HeatColors_p FL_PROGMEM = +{ + 0x000000, + 0x330000, 0x660000, 0x990000, 0xCC0000, 0xFF0000, + 0xFF3300, 0xFF6600, 0xFF9900, 0xFFCC00, 0xFFFF00, + 0xFFFF33, 0xFFFF66, 0xFFFF99, 0xFFFFCC, 0xFFFFFF +}; + + +// Gradient palette "Rainbow_gp", +// provided for situations where you're going +// to use a number of other gradient palettes, AND +// you want a 'standard' FastLED rainbow as well. + +DEFINE_GRADIENT_PALETTE( Rainbow_gp ) { + 0, 255, 0, 0, // Red + 32, 171, 85, 0, // Orange + 64, 171,171, 0, // Yellow + 96, 0,255, 0, // Green + 128, 0,171, 85, // Aqua + 160, 0, 0,255, // Blue + 192, 85, 0,171, // Purple + 224, 171, 0, 85, // Pink + 255, 255, 0, 0};// and back to Red + +#endif diff --git a/libraries/FastLED-master/colorpalettes.h b/libraries/FastLED-master/colorpalettes.h new file mode 100644 index 0000000000..4458575e41 --- /dev/null +++ b/libraries/FastLED-master/colorpalettes.h @@ -0,0 +1,57 @@ +#ifndef __INC_COLORPALETTES_H +#define __INC_COLORPALETTES_H + +#include "FastLED.h" +#include "colorutils.h" + +///@file colorpalettes.h +/// contains definitions for the predefined color palettes supplied by FastLED. + +FASTLED_NAMESPACE_BEGIN + +///@defgroup Colorpalletes Pre-defined color palletes +/// These schemes are all declared as "PROGMEM", meaning +/// that they won't take up SRAM on AVR chips until used. +/// Furthermore, the compiler won't even include these +/// in your PROGMEM (flash) storage unless you specifically +/// use each one, so you only 'pay for' those you actually use. + +///@{ + +/// Cloudy color pallete +extern const TProgmemRGBPalette16 CloudColors_p FL_PROGMEM; +/// Lava colors +extern const TProgmemRGBPalette16 LavaColors_p FL_PROGMEM; +/// Ocean colors, blues and whites +extern const TProgmemRGBPalette16 OceanColors_p FL_PROGMEM; +/// Forest colors, greens +extern const TProgmemRGBPalette16 ForestColors_p FL_PROGMEM; + +/// HSV Rainbow +extern const TProgmemRGBPalette16 RainbowColors_p FL_PROGMEM; + +#define RainbowStripesColors_p RainbowStripeColors_p +/// HSV Rainbow colors with alternatating stripes of black +extern const TProgmemRGBPalette16 RainbowStripeColors_p FL_PROGMEM; + +/// HSV color ramp: blue purple ping red orange yellow (and back) +/// Basically, everything but the greens, which tend to make +/// people's skin look unhealthy. This palette is good for +/// lighting at a club or party, where it'll be shining on people. +extern const TProgmemRGBPalette16 PartyColors_p FL_PROGMEM; + +/// Approximate "black body radiation" palette, akin to +/// the FastLED 'HeatColor' function. +/// Recommend that you use values 0-240 rather than +/// the usual 0-255, as the last 15 colors will be +/// 'wrapping around' from the hot end to the cold end, +/// which looks wrong. +extern const TProgmemRGBPalette16 HeatColors_p FL_PROGMEM; + + +DECLARE_GRADIENT_PALETTE( Rainbow_gp); + +FASTLED_NAMESPACE_END + +///@} +#endif diff --git a/libraries/FastLED-master/colorutils.cpp b/libraries/FastLED-master/colorutils.cpp new file mode 100644 index 0000000000..74b8ce46e4 --- /dev/null +++ b/libraries/FastLED-master/colorutils.cpp @@ -0,0 +1,1198 @@ +#define FASTLED_INTERNAL +#define __PROG_TYPES_COMPAT__ + +#include +#include + +#include "FastLED.h" + +FASTLED_NAMESPACE_BEGIN + + + +void fill_solid( struct CRGB * leds, int numToFill, + const struct CRGB& color) +{ + for( int i = 0; i < numToFill; i++) { + leds[i] = color; + } +} + +void fill_solid( struct CHSV * targetArray, int numToFill, + const struct CHSV& hsvColor) +{ + for( int i = 0; i < numToFill; i++) { + targetArray[i] = hsvColor; + } +} + + +// void fill_solid( struct CRGB* targetArray, int numToFill, +// const struct CHSV& hsvColor) +// { +// fill_solid( targetArray, numToFill, (CRGB) hsvColor); +// } + +void fill_rainbow( struct CRGB * pFirstLED, int numToFill, + uint8_t initialhue, + uint8_t deltahue ) +{ + CHSV hsv; + hsv.hue = initialhue; + hsv.val = 255; + hsv.sat = 240; + for( int i = 0; i < numToFill; i++) { + pFirstLED[i] = hsv; + hsv.hue += deltahue; + } +} + +void fill_rainbow( struct CHSV * targetArray, int numToFill, + uint8_t initialhue, + uint8_t deltahue ) +{ + CHSV hsv; + hsv.hue = initialhue; + hsv.val = 255; + hsv.sat = 240; + for( int i = 0; i < numToFill; i++) { + targetArray[i] = hsv; + hsv.hue += deltahue; + } +} + + +void fill_gradient_RGB( CRGB* leds, + uint16_t startpos, CRGB startcolor, + uint16_t endpos, CRGB endcolor ) +{ + // if the points are in the wrong order, straighten them + if( endpos < startpos ) { + uint16_t t = endpos; + CRGB tc = endcolor; + endcolor = startcolor; + endpos = startpos; + startpos = t; + startcolor = tc; + } + + saccum87 rdistance87; + saccum87 gdistance87; + saccum87 bdistance87; + + rdistance87 = (endcolor.r - startcolor.r) << 7; + gdistance87 = (endcolor.g - startcolor.g) << 7; + bdistance87 = (endcolor.b - startcolor.b) << 7; + + uint16_t pixeldistance = endpos - startpos; + int16_t divisor = pixeldistance ? pixeldistance : 1; + + saccum87 rdelta87 = rdistance87 / divisor; + saccum87 gdelta87 = gdistance87 / divisor; + saccum87 bdelta87 = bdistance87 / divisor; + + rdelta87 *= 2; + gdelta87 *= 2; + bdelta87 *= 2; + + accum88 r88 = startcolor.r << 8; + accum88 g88 = startcolor.g << 8; + accum88 b88 = startcolor.b << 8; + for( uint16_t i = startpos; i <= endpos; i++) { + leds[i] = CRGB( r88 >> 8, g88 >> 8, b88 >> 8); + r88 += rdelta87; + g88 += gdelta87; + b88 += bdelta87; + } +} + +#if 0 +void fill_gradient( const CHSV& c1, const CHSV& c2) +{ + fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2); +} + +void fill_gradient( const CHSV& c1, const CHSV& c2, const CHSV& c3) +{ + fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3); +} + +void fill_gradient( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) +{ + fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3, c4); +} + +void fill_gradient_RGB( const CRGB& c1, const CRGB& c2) +{ + fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2); +} + +void fill_gradient_RGB( const CRGB& c1, const CRGB& c2, const CRGB& c3) +{ + fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3); +} + +void fill_gradient_RGB( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) +{ + fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3, c4); +} +#endif + + + + +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2) +{ + uint16_t last = numLeds - 1; + fill_gradient_RGB( leds, 0, c1, last, c2); +} + + +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3) +{ + uint16_t half = (numLeds / 2); + uint16_t last = numLeds - 1; + fill_gradient_RGB( leds, 0, c1, half, c2); + fill_gradient_RGB( leds, half, c2, last, c3); +} + +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) +{ + uint16_t onethird = (numLeds / 3); + uint16_t twothirds = ((numLeds * 2) / 3); + uint16_t last = numLeds - 1; + fill_gradient_RGB( leds, 0, c1, onethird, c2); + fill_gradient_RGB( leds, onethird, c2, twothirds, c3); + fill_gradient_RGB( leds, twothirds, c3, last, c4); +} + + + + +void nscale8_video( CRGB* leds, uint16_t num_leds, uint8_t scale) +{ + for( uint16_t i = 0; i < num_leds; i++) { + leds[i].nscale8_video( scale); + } +} + +void fade_video(CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8_video( leds, num_leds, 255 - fadeBy); +} + +void fadeLightBy(CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8_video( leds, num_leds, 255 - fadeBy); +} + + +void fadeToBlackBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8( leds, num_leds, 255 - fadeBy); +} + +void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8( leds, num_leds, 255 - fadeBy); +} + +void nscale8_raw( CRGB* leds, uint16_t num_leds, uint8_t scale) +{ + nscale8( leds, num_leds, scale); +} + +void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale) +{ + for( uint16_t i = 0; i < num_leds; i++) { + leds[i].nscale8( scale); + } +} + +void fadeUsingColor( CRGB* leds, uint16_t numLeds, const CRGB& colormask) +{ + uint8_t fr, fg, fb; + fr = colormask.r; + fg = colormask.g; + fb = colormask.b; + + for( uint16_t i = 0; i < numLeds; i++) { + leds[i].r = scale8_LEAVING_R1_DIRTY( leds[i].r, fr); + leds[i].g = scale8_LEAVING_R1_DIRTY( leds[i].g, fg); + leds[i].b = scale8 ( leds[i].b, fb); + } +} + + +CRGB& nblend( CRGB& existing, const CRGB& overlay, fract8 amountOfOverlay ) +{ + if( amountOfOverlay == 0) { + return existing; + } + + if( amountOfOverlay == 255) { + existing = overlay; + return existing; + } + +#if 0 + // Old blend method which unfortunately had some rounding errors + fract8 amountOfKeep = 255 - amountOfOverlay; + + existing.red = scale8_LEAVING_R1_DIRTY( existing.red, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.red, amountOfOverlay); + existing.green = scale8_LEAVING_R1_DIRTY( existing.green, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.green, amountOfOverlay); + existing.blue = scale8_LEAVING_R1_DIRTY( existing.blue, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.blue, amountOfOverlay); + + cleanup_R1(); +#else + // Corrected blend method, with no loss-of-precision rounding errors + existing.red = blend8( existing.red, overlay.red, amountOfOverlay); + existing.green = blend8( existing.green, overlay.green, amountOfOverlay); + existing.blue = blend8( existing.blue, overlay.blue, amountOfOverlay); +#endif + + return existing; +} + + + +void nblend( CRGB* existing, CRGB* overlay, uint16_t count, fract8 amountOfOverlay) +{ + for( uint16_t i = count; i; i--) { + nblend( *existing, *overlay, amountOfOverlay); + existing++; + overlay++; + } +} + +CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 ) +{ + CRGB nu(p1); + nblend( nu, p2, amountOfP2); + return nu; +} + +CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest, uint16_t count, fract8 amountOfsrc2 ) +{ + for( uint16_t i = 0; i < count; i++) { + dest[i] = blend(src1[i], src2[i], amountOfsrc2); + } + return dest; +} + + + +CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, TGradientDirectionCode directionCode) +{ + if( amountOfOverlay == 0) { + return existing; + } + + if( amountOfOverlay == 255) { + existing = overlay; + return existing; + } + + fract8 amountOfKeep = 255 - amountOfOverlay; + + uint8_t huedelta8 = overlay.hue - existing.hue; + + if( directionCode == SHORTEST_HUES ) { + directionCode = FORWARD_HUES; + if( huedelta8 > 127) { + directionCode = BACKWARD_HUES; + } + } + + if( directionCode == LONGEST_HUES ) { + directionCode = FORWARD_HUES; + if( huedelta8 < 128) { + directionCode = BACKWARD_HUES; + } + } + + if( directionCode == FORWARD_HUES) { + existing.hue = existing.hue + scale8( huedelta8, amountOfOverlay); + } + else /* directionCode == BACKWARD_HUES */ + { + huedelta8 = -huedelta8; + existing.hue = existing.hue - scale8( huedelta8, amountOfOverlay); + } + + existing.sat = scale8_LEAVING_R1_DIRTY( existing.sat, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.sat, amountOfOverlay); + existing.val = scale8_LEAVING_R1_DIRTY( existing.val, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.val, amountOfOverlay); + + cleanup_R1(); + + return existing; +} + + + +void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay, TGradientDirectionCode directionCode ) +{ + if(existing == overlay) return; + for( uint16_t i = count; i; i--) { + nblend( *existing, *overlay, amountOfOverlay, directionCode); + existing++; + overlay++; + } +} + +CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2, TGradientDirectionCode directionCode ) +{ + CHSV nu(p1); + nblend( nu, p2, amountOfP2, directionCode); + return nu; +} + +CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, uint16_t count, fract8 amountOfsrc2, TGradientDirectionCode directionCode ) +{ + for( uint16_t i = 0; i < count; i++) { + dest[i] = blend(src1[i], src2[i], amountOfsrc2, directionCode); + } + return dest; +} + + + +// Forward declaration of the function "XY" which must be provided by +// the application for use in two-dimensional filter functions. +uint16_t XY( uint8_t, uint8_t);// __attribute__ ((weak)); + + +// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors. +// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors. +// +// 0 = no spread at all +// 64 = moderate spreading +// 172 = maximum smooth, even spreading +// +// 173..255 = wider spreading, but increasing flicker +// +// Total light is NOT entirely conserved, so many repeated +// calls to 'blur' will also result in the light fading, +// eventually all the way to black; this is by design so that +// it can be used to (slowly) clear the LEDs to black. +void blur1d( CRGB* leds, uint16_t numLeds, fract8 blur_amount) +{ + uint8_t keep = 255 - blur_amount; + uint8_t seep = blur_amount >> 1; + CRGB carryover = CRGB::Black; + for( uint16_t i = 0; i < numLeds; i++) { + CRGB cur = leds[i]; + CRGB part = cur; + part.nscale8( seep); + cur.nscale8( keep); + cur += carryover; + if( i) leds[i-1] += part; + leds[i] = cur; + carryover = part; + } +} + +void blur2d( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount) +{ + blurRows(leds, width, height, blur_amount); + blurColumns(leds, width, height, blur_amount); +} + +// blurRows: perform a blur1d on every row of a rectangular matrix +void blurRows( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount) +{ + for( uint8_t row = 0; row < height; row++) { + CRGB* rowbase = leds + (row * width); + blur1d( rowbase, width, blur_amount); + } +} + +// blurColumns: perform a blur1d on each column of a rectangular matrix +void blurColumns(CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount) +{ + // blur columns + uint8_t keep = 255 - blur_amount; + uint8_t seep = blur_amount >> 1; + for( uint8_t col = 0; col < width; col++) { + CRGB carryover = CRGB::Black; + for( uint8_t i = 0; i < height; i++) { + CRGB cur = leds[XY(col,i)]; + CRGB part = cur; + part.nscale8( seep); + cur.nscale8( keep); + cur += carryover; + if( i) leds[XY(col,i-1)] += part; + leds[XY(col,i)] = cur; + carryover = part; + } + } +} + + + +// CRGB HeatColor( uint8_t temperature) +// +// Approximates a 'black body radiation' spectrum for +// a given 'heat' level. This is useful for animations of 'fire'. +// Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot). +// This is NOT a chromatically correct 'black body radiation' +// spectrum, but it's surprisingly close, and it's fast and small. +// +// On AVR/Arduino, this typically takes around 70 bytes of program memory, +// versus 768 bytes for a full 256-entry RGB lookup table. + +CRGB HeatColor( uint8_t temperature) +{ + CRGB heatcolor; + + // Scale 'heat' down from 0-255 to 0-191, + // which can then be easily divided into three + // equal 'thirds' of 64 units each. + uint8_t t192 = scale8_video( temperature, 191); + + // calculate a value that ramps up from + // zero to 255 in each 'third' of the scale. + uint8_t heatramp = t192 & 0x3F; // 0..63 + heatramp <<= 2; // scale up to 0..252 + + // now figure out which third of the spectrum we're in: + if( t192 & 0x80) { + // we're in the hottest third + heatcolor.r = 255; // full red + heatcolor.g = 255; // full green + heatcolor.b = heatramp; // ramp up blue + + } else if( t192 & 0x40 ) { + // we're in the middle third + heatcolor.r = 255; // full red + heatcolor.g = heatramp; // ramp up green + heatcolor.b = 0; // no blue + + } else { + // we're in the coolest third + heatcolor.r = heatramp; // ramp up red + heatcolor.g = 0; // no green + heatcolor.b = 0; // no blue + } + + return heatcolor; +} + + +// lsrX4: helper function to divide a number by 16, aka four LSR's. +// On avr-gcc, "u8 >> 4" generates a loop, which is big, and slow. +// merely forcing it to be four /=2's causes avr-gcc to emit +// a SWAP instruction followed by an AND 0x0F, which is faster, and smaller. +inline uint8_t lsrX4( uint8_t dividend) __attribute__((always_inline)); +inline uint8_t lsrX4( uint8_t dividend) +{ +#if defined(__AVR__) + dividend /= 2; + dividend /= 2; + dividend /= 2; + dividend /= 2; +#else + dividend >>= 4; +#endif + return dividend; +} + + +CRGB ColorFromPalette( const CRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); + uint8_t lo4 = index & 0x0F; + + // const CRGB* entry = &(pal[0]) + hi4; + // since hi4 is always 0..15, hi4 * sizeof(CRGB) can be a single-byte value, + // instead of the two byte 'int' that avr-gcc defaults to. + // So, we multiply hi4 X sizeof(CRGB), giving hi4XsizeofCRGB; + uint8_t hi4XsizeofCRGB = hi4 * sizeof(CRGB); + // We then add that to a base array pointer. + const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi4XsizeofCRGB); + + uint8_t blend = lo4 && (blendType != NOBLEND); + + uint8_t red1 = entry->red; + uint8_t green1 = entry->green; + uint8_t blue1 = entry->blue; + + + if( blend ) { + + if( hi4 == 15 ) { + entry = &(pal[0]); + } else { + entry++; + } + + uint8_t f2 = lo4 << 4; + uint8_t f1 = 255 - f2; + + // rgb1.nscale8(f1); + uint8_t red2 = entry->red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry->green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry->blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + } + + if( brightness != 255) { + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. + if( red1 ) { + red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + red1++; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + green1++; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + blue1++; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + +CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); + uint8_t lo4 = index & 0x0F; + + CRGB entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) + hi4 ); + + + uint8_t red1 = entry.red; + uint8_t green1 = entry.green; + uint8_t blue1 = entry.blue; + + uint8_t blend = lo4 && (blendType != NOBLEND); + + if( blend ) { + + if( hi4 == 15 ) { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) ); + } else { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[1]) + hi4 ); + } + + uint8_t f2 = lo4 << 4; + uint8_t f1 = 255 - f2; + + uint8_t red2 = entry.red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry.green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry.blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + } + + if( brightness != 255) { + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. + if( red1 ) { + red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + red1++; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + green1++; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + blue1++; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + + +CRGB ColorFromPalette( const CRGBPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + // const CRGB* entry = &(pal[0]) + hi5; + // since hi5 is always 0..31, hi4 * sizeof(CRGB) can be a single-byte value, + // instead of the two byte 'int' that avr-gcc defaults to. + // So, we multiply hi5 X sizeof(CRGB), giving hi5XsizeofCRGB; + uint8_t hi5XsizeofCRGB = hi5 * sizeof(CRGB); + // We then add that to a base array pointer. + const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi5XsizeofCRGB); + + uint8_t red1 = entry->red; + uint8_t green1 = entry->green; + uint8_t blue1 = entry->blue; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = &(pal[0]); + } else { + entry++; + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t red2 = entry->red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry->green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry->blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + + } + + if( brightness != 255) { + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. + if( red1 ) { + red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + red1++; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + green1++; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + blue1++; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + + +CRGB ColorFromPalette( const TProgmemRGBPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + CRGB entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) + hi5); + + uint8_t red1 = entry.red; + uint8_t green1 = entry.green; + uint8_t blue1 = entry.blue; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) ); + } else { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[1]) + hi5 ); + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t red2 = entry.red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry.green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry.blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + } + + if( brightness != 255) { + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. + if( red1 ) { + red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + red1++; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + green1++; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + blue1++; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + + + +CRGB ColorFromPalette( const CRGBPalette256& pal, uint8_t index, uint8_t brightness, TBlendType) +{ + const CRGB* entry = &(pal[0]) + index; + + uint8_t red = entry->red; + uint8_t green = entry->green; + uint8_t blue = entry->blue; + + if( brightness != 255) { + brightness++; // adjust for rounding + red = scale8_video_LEAVING_R1_DIRTY( red, brightness); + green = scale8_video_LEAVING_R1_DIRTY( green, brightness); + blue = scale8_video_LEAVING_R1_DIRTY( blue, brightness); + cleanup_R1(); + } + + return CRGB( red, green, blue); +} + + +CHSV ColorFromPalette( const struct CHSVPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); + uint8_t lo4 = index & 0x0F; + + // CRGB rgb1 = pal[ hi4]; + const CHSV* entry = &(pal[0]) + hi4; + + uint8_t hue1 = entry->hue; + uint8_t sat1 = entry->sat; + uint8_t val1 = entry->val; + + uint8_t blend = lo4 && (blendType != NOBLEND); + + if( blend ) { + + if( hi4 == 15 ) { + entry = &(pal[0]); + } else { + entry++; + } + + uint8_t f2 = lo4 << 4; + uint8_t f1 = 255 - f2; + + uint8_t hue2 = entry->hue; + uint8_t sat2 = entry->sat; + uint8_t val2 = entry->val; + + // Now some special casing for blending to or from + // either black or white. Black and white don't have + // proper 'hue' of their own, so when ramping from + // something else to/from black/white, we set the 'hue' + // of the black/white color to be the same as the hue + // of the other color, so that you get the expected + // brightness or saturation ramp, with hue staying + // constant: + + // If we are starting from white (sat=0) + // or black (val=0), adopt the target hue. + if( sat1 == 0 || val1 == 0) { + hue1 = hue2; + } + + // If we are ending at white (sat=0) + // or black (val=0), adopt the starting hue. + if( sat2 == 0 || val2 == 0) { + hue2 = hue1; + } + + + sat1 = scale8_LEAVING_R1_DIRTY( sat1, f1); + val1 = scale8_LEAVING_R1_DIRTY( val1, f1); + + sat2 = scale8_LEAVING_R1_DIRTY( sat2, f2); + val2 = scale8_LEAVING_R1_DIRTY( val2, f2); + + // cleanup_R1(); + + // These sums can't overflow, so no qadd8 needed. + sat1 += sat2; + val1 += val2; + + uint8_t deltaHue = (uint8_t)(hue2 - hue1); + if( deltaHue & 0x80 ) { + // go backwards + hue1 -= scale8( 255 - deltaHue, f2); + } else { + // go forwards + hue1 += scale8( deltaHue, f2); + } + + cleanup_R1(); + } + + if( brightness != 255) { + val1 = scale8_video( val1, brightness); + } + + return CHSV( hue1, sat1, val1); +} + + +CHSV ColorFromPalette( const struct CHSVPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + uint8_t hi5XsizeofCHSV = hi5 * sizeof(CHSV); + const CHSV* entry = (CHSV*)( (uint8_t*)(&(pal[0])) + hi5XsizeofCHSV); + + uint8_t hue1 = entry->hue; + uint8_t sat1 = entry->sat; + uint8_t val1 = entry->val; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = &(pal[0]); + } else { + entry++; + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t hue2 = entry->hue; + uint8_t sat2 = entry->sat; + uint8_t val2 = entry->val; + + // Now some special casing for blending to or from + // either black or white. Black and white don't have + // proper 'hue' of their own, so when ramping from + // something else to/from black/white, we set the 'hue' + // of the black/white color to be the same as the hue + // of the other color, so that you get the expected + // brightness or saturation ramp, with hue staying + // constant: + + // If we are starting from white (sat=0) + // or black (val=0), adopt the target hue. + if( sat1 == 0 || val1 == 0) { + hue1 = hue2; + } + + // If we are ending at white (sat=0) + // or black (val=0), adopt the starting hue. + if( sat2 == 0 || val2 == 0) { + hue2 = hue1; + } + + + sat1 = scale8_LEAVING_R1_DIRTY( sat1, f1); + val1 = scale8_LEAVING_R1_DIRTY( val1, f1); + + sat2 = scale8_LEAVING_R1_DIRTY( sat2, f2); + val2 = scale8_LEAVING_R1_DIRTY( val2, f2); + + // cleanup_R1(); + + // These sums can't overflow, so no qadd8 needed. + sat1 += sat2; + val1 += val2; + + uint8_t deltaHue = (uint8_t)(hue2 - hue1); + if( deltaHue & 0x80 ) { + // go backwards + hue1 -= scale8( 255 - deltaHue, f2); + } else { + // go forwards + hue1 += scale8( deltaHue, f2); + } + + cleanup_R1(); + } + + if( brightness != 255) { + val1 = scale8_video( val1, brightness); + } + + return CHSV( hue1, sat1, val1); +} + +CHSV ColorFromPalette( const struct CHSVPalette256& pal, uint8_t index, uint8_t brightness, TBlendType) +{ + CHSV hsv = *( &(pal[0]) + index ); + + if( brightness != 255) { + hsv.value = scale8_video( hsv.value, brightness); + } + + return hsv; +} + + +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette256& destpal256) +{ + for( int i = 0; i < 256; i++) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal16, i); + } +} + +void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& destpal256) +{ + for( int i = 0; i < 256; i++) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal16, i); + } +} + + +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette32& destpal32) +{ + for( uint8_t i = 0; i < 16; i++) { + uint8_t j = i * 2; + destpal32[j+0] = srcpal16[i]; + destpal32[j+1] = srcpal16[i]; + } +} + +void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette32& destpal32) +{ + for( uint8_t i = 0; i < 16; i++) { + uint8_t j = i * 2; + destpal32[j+0] = srcpal16[i]; + destpal32[j+1] = srcpal16[i]; + } +} + +void UpscalePalette(const struct CRGBPalette32& srcpal32, struct CRGBPalette256& destpal256) +{ + for( int i = 0; i < 256; i++) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal32, i); + } +} + +void UpscalePalette(const struct CHSVPalette32& srcpal32, struct CHSVPalette256& destpal256) +{ + for( int i = 0; i < 256; i++) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal32, i); + } +} + + + +#if 0 +// replaced by PartyColors_p +void SetupPartyColors(CRGBPalette16& pal) +{ + fill_gradient( pal, 0, CHSV( HUE_PURPLE,255,255), 7, CHSV(HUE_YELLOW - 18,255,255), FORWARD_HUES); + fill_gradient( pal, 8, CHSV( HUE_ORANGE,255,255), 15, CHSV(HUE_BLUE + 18,255,255), BACKWARD_HUES); +} +#endif + + +void nblendPaletteTowardPalette( CRGBPalette16& current, CRGBPalette16& target, uint8_t maxChanges) +{ + uint8_t* p1; + uint8_t* p2; + uint8_t changes = 0; + + p1 = (uint8_t*)current.entries; + p2 = (uint8_t*)target.entries; + + const uint8_t totalChannels = sizeof(CRGBPalette16); + for( uint8_t i = 0; i < totalChannels; i++) { + // if the values are equal, no changes are needed + if( p1[i] == p2[i] ) { continue; } + + // if the current value is less than the target, increase it by one + if( p1[i] < p2[i] ) { p1[i]++; changes++; } + + // if the current value is greater than the target, + // increase it by one (or two if it's still greater). + if( p1[i] > p2[i] ) { + p1[i]--; changes++; + if( p1[i] > p2[i] ) { p1[i]--; } + } + + // if we've hit the maximum number of changes, exit + if( changes >= maxChanges) { break; } + } +} + + +uint8_t applyGamma_video( uint8_t brightness, float gamma) +{ + float orig; + float adj; + orig = (float)(brightness) / (255.0); + adj = pow( orig, gamma) * (255.0); + uint8_t result = (uint8_t)(adj); + if( (brightness > 0) && (result == 0)) { + result = 1; // never gamma-adjust a positive number down to zero + } + return result; +} + +CRGB applyGamma_video( const CRGB& orig, float gamma) +{ + CRGB adj; + adj.r = applyGamma_video( orig.r, gamma); + adj.g = applyGamma_video( orig.g, gamma); + adj.b = applyGamma_video( orig.b, gamma); + return adj; +} + +CRGB applyGamma_video( const CRGB& orig, float gammaR, float gammaG, float gammaB) +{ + CRGB adj; + adj.r = applyGamma_video( orig.r, gammaR); + adj.g = applyGamma_video( orig.g, gammaG); + adj.b = applyGamma_video( orig.b, gammaB); + return adj; +} + +CRGB& napplyGamma_video( CRGB& rgb, float gamma) +{ + rgb = applyGamma_video( rgb, gamma); + return rgb; +} + +CRGB& napplyGamma_video( CRGB& rgb, float gammaR, float gammaG, float gammaB) +{ + rgb = applyGamma_video( rgb, gammaR, gammaG, gammaB); + return rgb; +} + +void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gamma) +{ + for( uint16_t i = 0; i < count; i++) { + rgbarray[i] = applyGamma_video( rgbarray[i], gamma); + } +} + +void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gammaR, float gammaG, float gammaB) +{ + for( uint16_t i = 0; i < count; i++) { + rgbarray[i] = applyGamma_video( rgbarray[i], gammaR, gammaG, gammaB); + } +} + + +FASTLED_NAMESPACE_END diff --git a/libraries/FastLED-master/colorutils.h b/libraries/FastLED-master/colorutils.h new file mode 100644 index 0000000000..4fcf394047 --- /dev/null +++ b/libraries/FastLED-master/colorutils.h @@ -0,0 +1,1706 @@ +#ifndef __INC_COLORUTILS_H +#define __INC_COLORUTILS_H + +///@file colorutils.h +/// functions for color fill, paletters, blending, and more + +#include "FastLED.h" +#include "pixeltypes.h" +#include "fastled_progmem.h" + +FASTLED_NAMESPACE_BEGIN +///@defgroup Colorutils Color utility functions +///A variety of functions for working with color, palletes, and leds +///@{ + +/// fill_solid - fill a range of LEDs with a solid color +/// Example: fill_solid( leds, NUM_LEDS, CRGB(50,0,200)); +void fill_solid( struct CRGB * leds, int numToFill, + const struct CRGB& color); + +/// fill_solid - fill a range of LEDs with a solid color +/// Example: fill_solid( leds, NUM_LEDS, CRGB(50,0,200)); +void fill_solid( struct CHSV* targetArray, int numToFill, + const struct CHSV& hsvColor); + + +/// fill_rainbow - fill a range of LEDs with a rainbow of colors, at +/// full saturation and full value (brightness) +void fill_rainbow( struct CRGB * pFirstLED, int numToFill, + uint8_t initialhue, + uint8_t deltahue = 5); + +/// fill_rainbow - fill a range of LEDs with a rainbow of colors, at +/// full saturation and full value (brightness) +void fill_rainbow( struct CHSV * targetArray, int numToFill, + uint8_t initialhue, + uint8_t deltahue = 5); + + +// fill_gradient - fill an array of colors with a smooth HSV gradient +// between two specified HSV colors. +// Since 'hue' is a value around a color wheel, +// there are always two ways to sweep from one hue +// to another. +// This function lets you specify which way you want +// the hue gradient to sweep around the color wheel: +// FORWARD_HUES: hue always goes clockwise +// BACKWARD_HUES: hue always goes counter-clockwise +// SHORTEST_HUES: hue goes whichever way is shortest +// LONGEST_HUES: hue goes whichever way is longest +// The default is SHORTEST_HUES, as this is nearly +// always what is wanted. +// +// fill_gradient can write the gradient colors EITHER +// (1) into an array of CRGBs (e.g., into leds[] array, or an RGB Palette) +// OR +// (2) into an array of CHSVs (e.g. an HSV Palette). +// +// In the case of writing into a CRGB array, the gradient is +// computed in HSV space, and then HSV values are converted to RGB +// as they're written into the RGB array. + +typedef enum { FORWARD_HUES, BACKWARD_HUES, SHORTEST_HUES, LONGEST_HUES } TGradientDirectionCode; + + + +#define saccum87 int16_t + +/// fill_gradient - fill an array of colors with a smooth HSV gradient +/// between two specified HSV colors. +/// Since 'hue' is a value around a color wheel, +/// there are always two ways to sweep from one hue +/// to another. +/// This function lets you specify which way you want +/// the hue gradient to sweep around the color wheel: +/// +/// FORWARD_HUES: hue always goes clockwise +/// BACKWARD_HUES: hue always goes counter-clockwise +/// SHORTEST_HUES: hue goes whichever way is shortest +/// LONGEST_HUES: hue goes whichever way is longest +/// +/// The default is SHORTEST_HUES, as this is nearly +/// always what is wanted. +/// +/// fill_gradient can write the gradient colors EITHER +/// (1) into an array of CRGBs (e.g., into leds[] array, or an RGB Palette) +/// OR +/// (2) into an array of CHSVs (e.g. an HSV Palette). +/// +/// In the case of writing into a CRGB array, the gradient is +/// computed in HSV space, and then HSV values are converted to RGB +/// as they're written into the RGB array. +template +void fill_gradient( T* targetArray, + uint16_t startpos, CHSV startcolor, + uint16_t endpos, CHSV endcolor, + TGradientDirectionCode directionCode = SHORTEST_HUES ) +{ + // if the points are in the wrong order, straighten them + if( endpos < startpos ) { + uint16_t t = endpos; + CHSV tc = endcolor; + endcolor = startcolor; + endpos = startpos; + startpos = t; + startcolor = tc; + } + + // If we're fading toward black (val=0) or white (sat=0), + // then set the endhue to the starthue. + // This lets us ramp smoothly to black or white, regardless + // of what 'hue' was set in the endcolor (since it doesn't matter) + if( endcolor.value == 0 || endcolor.saturation == 0) { + endcolor.hue = startcolor.hue; + } + + // Similarly, if we're fading in from black (val=0) or white (sat=0) + // then set the starthue to the endhue. + // This lets us ramp smoothly up from black or white, regardless + // of what 'hue' was set in the startcolor (since it doesn't matter) + if( startcolor.value == 0 || startcolor.saturation == 0) { + startcolor.hue = endcolor.hue; + } + + saccum87 huedistance87; + saccum87 satdistance87; + saccum87 valdistance87; + + satdistance87 = (endcolor.sat - startcolor.sat) << 7; + valdistance87 = (endcolor.val - startcolor.val) << 7; + + uint8_t huedelta8 = endcolor.hue - startcolor.hue; + + if( directionCode == SHORTEST_HUES ) { + directionCode = FORWARD_HUES; + if( huedelta8 > 127) { + directionCode = BACKWARD_HUES; + } + } + + if( directionCode == LONGEST_HUES ) { + directionCode = FORWARD_HUES; + if( huedelta8 < 128) { + directionCode = BACKWARD_HUES; + } + } + + if( directionCode == FORWARD_HUES) { + huedistance87 = huedelta8 << 7; + } + else /* directionCode == BACKWARD_HUES */ + { + huedistance87 = (uint8_t)(256 - huedelta8) << 7; + huedistance87 = -huedistance87; + } + + uint16_t pixeldistance = endpos - startpos; + int16_t divisor = pixeldistance ? pixeldistance : 1; + + saccum87 huedelta87 = huedistance87 / divisor; + saccum87 satdelta87 = satdistance87 / divisor; + saccum87 valdelta87 = valdistance87 / divisor; + + huedelta87 *= 2; + satdelta87 *= 2; + valdelta87 *= 2; + + accum88 hue88 = startcolor.hue << 8; + accum88 sat88 = startcolor.sat << 8; + accum88 val88 = startcolor.val << 8; + for( uint16_t i = startpos; i <= endpos; i++) { + targetArray[i] = CHSV( hue88 >> 8, sat88 >> 8, val88 >> 8); + hue88 += huedelta87; + sat88 += satdelta87; + val88 += valdelta87; + } +} + + +// Convenience functions to fill an array of colors with a +// two-color, three-color, or four-color gradient +template +void fill_gradient( T* targetArray, uint16_t numLeds, const CHSV& c1, const CHSV& c2, + TGradientDirectionCode directionCode = SHORTEST_HUES ) +{ + uint16_t last = numLeds - 1; + fill_gradient( targetArray, 0, c1, last, c2, directionCode); +} + +template +void fill_gradient( T* targetArray, uint16_t numLeds, + const CHSV& c1, const CHSV& c2, const CHSV& c3, + TGradientDirectionCode directionCode = SHORTEST_HUES ) +{ + uint16_t half = (numLeds / 2); + uint16_t last = numLeds - 1; + fill_gradient( targetArray, 0, c1, half, c2, directionCode); + fill_gradient( targetArray, half, c2, last, c3, directionCode); +} + +template +void fill_gradient( T* targetArray, uint16_t numLeds, + const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4, + TGradientDirectionCode directionCode = SHORTEST_HUES ) +{ + uint16_t onethird = (numLeds / 3); + uint16_t twothirds = ((numLeds * 2) / 3); + uint16_t last = numLeds - 1; + fill_gradient( targetArray, 0, c1, onethird, c2, directionCode); + fill_gradient( targetArray, onethird, c2, twothirds, c3, directionCode); + fill_gradient( targetArray, twothirds, c3, last, c4, directionCode); +} + +// convenience synonym +#define fill_gradient_HSV fill_gradient + + +// fill_gradient_RGB - fill a range of LEDs with a smooth RGB gradient +// between two specified RGB colors. +// Unlike HSV, there is no 'color wheel' in RGB space, +// and therefore there's only one 'direction' for the +// gradient to go, and no 'direction code' is needed. +void fill_gradient_RGB( CRGB* leds, + uint16_t startpos, CRGB startcolor, + uint16_t endpos, CRGB endcolor ); +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2); +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3); +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4); + + +// fadeLightBy and fade_video - reduce the brightness of an array +// of pixels all at once. Guaranteed +// to never fade all the way to black. +// (The two names are synonyms.) +void fadeLightBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); +void fade_video( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); + +// nscale8_video - scale down the brightness of an array of pixels +// all at once. Guaranteed to never scale a pixel +// all the way down to black, unless 'scale' is zero. +void nscale8_video( CRGB* leds, uint16_t num_leds, uint8_t scale); + +// fadeToBlackBy and fade_raw - reduce the brightness of an array +// of pixels all at once. These +// functions will eventually fade all +// the way to black. +// (The two names are synonyms.) +void fadeToBlackBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); +void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); + +// nscale8 - scale down the brightness of an array of pixels +// all at once. This function can scale pixels all the +// way down to black even if 'scale' is not zero. +void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale); + +// fadeUsingColor - scale down the brightness of an array of pixels, +// as though it were seen through a transparent +// filter with the specified color. +// For example, if the colormask is +// CRGB( 200, 100, 50) +// then the pixels' red will be faded to 200/256ths, +// their green to 100/256ths, and their blue to 50/256ths. +// This particular example give a 'hot fade' look, +// with white fading to yellow, then red, then black. +// You can also use colormasks like CRGB::Blue to +// zero out the red and green elements, leaving blue +// (largely) the same. +void fadeUsingColor( CRGB* leds, uint16_t numLeds, const CRGB& colormask); + + +// Pixel blending +// +// blend - computes a new color blended some fraction of the way +// between two other colors. +CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 ); + +CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2, + TGradientDirectionCode directionCode = SHORTEST_HUES ); + +// blend - computes a new color blended array of colors, each +// a given fraction of the way between corresponding +// elements of two source arrays of colors. +// Useful for blending palettes. +CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest, + uint16_t count, fract8 amountOfsrc2 ); + +CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, + uint16_t count, fract8 amountOfsrc2, + TGradientDirectionCode directionCode = SHORTEST_HUES ); + +// nblend - destructively modifies one color, blending +// in a given fraction of an overlay color +CRGB& nblend( CRGB& existing, const CRGB& overlay, fract8 amountOfOverlay ); + +CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, + TGradientDirectionCode directionCode = SHORTEST_HUES ); + +// nblend - destructively blends a given fraction of +// a new color array into an existing color array +void nblend( CRGB* existing, CRGB* overlay, uint16_t count, fract8 amountOfOverlay); + +void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay, + TGradientDirectionCode directionCode = SHORTEST_HUES); + + +// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors. +// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors. +// +// 0 = no spread at all +// 64 = moderate spreading +// 172 = maximum smooth, even spreading +// +// 173..255 = wider spreading, but increasing flicker +// +// Total light is NOT entirely conserved, so many repeated +// calls to 'blur' will also result in the light fading, +// eventually all the way to black; this is by design so that +// it can be used to (slowly) clear the LEDs to black. +void blur1d( CRGB* leds, uint16_t numLeds, fract8 blur_amount); +void blur2d( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); + +// blurRows: perform a blur1d on every row of a rectangular matrix +void blurRows( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); +// blurColumns: perform a blur1d on each column of a rectangular matrix +void blurColumns(CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); + + +// CRGB HeatColor( uint8_t temperature) +// +// Approximates a 'black body radiation' spectrum for +// a given 'heat' level. This is useful for animations of 'fire'. +// Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot). +// This is NOT a chromatically correct 'black body radiation' +// spectrum, but it's surprisingly close, and it's fast and small. +CRGB HeatColor( uint8_t temperature); + + +// Palettes +// +// RGB Palettes map an 8-bit value (0..255) to an RGB color. +// +// You can create any color palette you wish; a couple of starters +// are provided: Forest, Clouds, Lava, Ocean, Rainbow, and Rainbow Stripes. +// +// Palettes come in the traditional 256-entry variety, which take +// up 768 bytes of RAM, and lightweight 16-entry varieties. The 16-entry +// variety automatically interpolates between its entries to produce +// a full 256-element color map, but at a cost of only 48 bytes or RAM. +// +// Basic operation is like this: (example shows the 16-entry variety) +// 1. Declare your palette storage: +// CRGBPalette16 myPalette; +// +// 2. Fill myPalette with your own 16 colors, or with a preset color scheme. +// You can specify your 16 colors a variety of ways: +// CRGBPalette16 myPalette( +// CRGB::Black, +// CRGB::Black, +// CRGB::Red, +// CRGB::Yellow, +// CRGB::Green, +// CRGB::Blue, +// CRGB::Purple, +// CRGB::Black, +// +// 0x100000, +// 0x200000, +// 0x400000, +// 0x800000, +// +// CHSV( 30,255,255), +// CHSV( 50,255,255), +// CHSV( 70,255,255), +// CHSV( 90,255,255) +// ); +// +// Or you can initiaize your palette with a preset color scheme: +// myPalette = RainbowStripesColors_p; +// +// 3. Any time you want to set a pixel to a color from your palette, use +// "ColorFromPalette(...)" as shown: +// +// uint8_t index = /* any value 0..255 */; +// leds[i] = ColorFromPalette( myPalette, index); +// +// Even though your palette has only 16 explicily defined entries, you +// can use an 'index' from 0..255. The 16 explicit palette entries will +// be spread evenly across the 0..255 range, and the intermedate values +// will be RGB-interpolated between adjacent explicit entries. +// +// It's easier to use than it sounds. +// + +class CRGBPalette16; +class CRGBPalette32; +class CRGBPalette256; +class CHSVPalette16; +class CHSVPalette32; +class CHSVPalette256; +typedef uint32_t TProgmemRGBPalette16[16]; +typedef uint32_t TProgmemHSVPalette16[16]; +#define TProgmemPalette16 TProgmemRGBPalette16 +typedef uint32_t TProgmemRGBPalette32[32]; +typedef uint32_t TProgmemHSVPalette32[32]; +#define TProgmemPalette32 TProgmemRGBPalette32 + +typedef const uint8_t TProgmemRGBGradientPalette_byte ; +typedef const TProgmemRGBGradientPalette_byte *TProgmemRGBGradientPalette_bytes; +typedef TProgmemRGBGradientPalette_bytes TProgmemRGBGradientPalettePtr; +typedef union { + struct { + uint8_t index; + uint8_t r; + uint8_t g; + uint8_t b; + }; + uint32_t dword; + uint8_t bytes[4]; +} TRGBGradientPaletteEntryUnion; + +typedef uint8_t TDynamicRGBGradientPalette_byte ; +typedef const TDynamicRGBGradientPalette_byte *TDynamicRGBGradientPalette_bytes; +typedef TDynamicRGBGradientPalette_bytes TDynamicRGBGradientPalettePtr; + +// Convert a 16-entry palette to a 256-entry palette +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette256& destpal256); +void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& destpal256); + +// Convert a 16-entry palette to a 32-entry palette +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette32& destpal32); +void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette32& destpal32); + +// Convert a 32-entry palette to a 256-entry palette +void UpscalePalette(const struct CRGBPalette32& srcpal32, struct CRGBPalette256& destpal256); +void UpscalePalette(const struct CHSVPalette32& srcpal32, struct CHSVPalette256& destpal256); + + +class CHSVPalette16 { +public: + CHSV entries[16]; + CHSVPalette16() {}; + CHSVPalette16( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, + const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, + const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, + const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) + { + entries[0]=c00; entries[1]=c01; entries[2]=c02; entries[3]=c03; + entries[4]=c04; entries[5]=c05; entries[6]=c06; entries[7]=c07; + entries[8]=c08; entries[9]=c09; entries[10]=c10; entries[11]=c11; + entries[12]=c12; entries[13]=c13; entries[14]=c14; entries[15]=c15; + }; + + CHSVPalette16( const CHSVPalette16& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + CHSVPalette16& operator=( const CHSVPalette16& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + + CHSVPalette16( const TProgmemHSVPalette16& rhs) + { + for( uint8_t i = 0; i < 16; i++) { + CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); + entries[i].hue = xyz.red; + entries[i].sat = xyz.green; + entries[i].val = xyz.blue; + } + } + CHSVPalette16& operator=( const TProgmemHSVPalette16& rhs) + { + for( uint8_t i = 0; i < 16; i++) { + CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); + entries[i].hue = xyz.red; + entries[i].sat = xyz.green; + entries[i].val = xyz.blue; + } + return *this; + } + + inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + inline CHSV& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + inline const CHSV& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + operator CHSV*() + { + return &(entries[0]); + } + + bool operator==( const CHSVPalette16 rhs) + { + const uint8_t* p = (const uint8_t*)(&(this->entries[0])); + const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); + if( p == q) return true; + for( uint8_t i = 0; i < (sizeof( entries)); i++) { + if( *p != *q) return false; + p++; + q++; + } + return true; + } + bool operator!=( const CHSVPalette16 rhs) + { + return !( *this == rhs); + } + + CHSVPalette16( const CHSV& c1) + { + fill_solid( &(entries[0]), 16, c1); + } + CHSVPalette16( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 16, c1, c2); + } + CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3); + } + CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); + } + +}; + +class CHSVPalette256 { +public: + CHSV entries[256]; + CHSVPalette256() {}; + CHSVPalette256( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, + const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, + const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, + const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) + { + CHSVPalette16 p16(c00,c01,c02,c03,c04,c05,c06,c07, + c08,c09,c10,c11,c12,c13,c14,c15); + *this = p16; + }; + + CHSVPalette256( const CHSVPalette256& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + CHSVPalette256& operator=( const CHSVPalette256& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + + CHSVPalette256( const CHSVPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + } + CHSVPalette256& operator=( const CHSVPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + return *this; + } + + CHSVPalette256( const TProgmemRGBPalette16& rhs) + { + CHSVPalette16 p16(rhs); + *this = p16; + } + CHSVPalette256& operator=( const TProgmemRGBPalette16& rhs) + { + CHSVPalette16 p16(rhs); + *this = p16; + return *this; + } + + inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + inline CHSV& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + inline const CHSV& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + operator CHSV*() + { + return &(entries[0]); + } + + bool operator==( const CHSVPalette256 rhs) + { + const uint8_t* p = (const uint8_t*)(&(this->entries[0])); + const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); + if( p == q) return true; + for( uint16_t i = 0; i < (sizeof( entries)); i++) { + if( *p != *q) return false; + p++; + q++; + } + return true; + } + bool operator!=( const CHSVPalette256 rhs) + { + return !( *this == rhs); + } + + CHSVPalette256( const CHSV& c1) + { + fill_solid( &(entries[0]), 256, c1); + } + CHSVPalette256( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 256, c1, c2); + } + CHSVPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 256, c1, c2, c3); + } + CHSVPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 256, c1, c2, c3, c4); + } +}; + +class CRGBPalette16 { +public: + CRGB entries[16]; + CRGBPalette16() {}; + CRGBPalette16( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, + const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, + const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, + const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) + { + entries[0]=c00; entries[1]=c01; entries[2]=c02; entries[3]=c03; + entries[4]=c04; entries[5]=c05; entries[6]=c06; entries[7]=c07; + entries[8]=c08; entries[9]=c09; entries[10]=c10; entries[11]=c11; + entries[12]=c12; entries[13]=c13; entries[14]=c14; entries[15]=c15; + }; + + CRGBPalette16( const CRGBPalette16& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + CRGBPalette16( const CRGB rhs[16]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + } + CRGBPalette16& operator=( const CRGBPalette16& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + CRGBPalette16& operator=( const CRGB rhs[16]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + return *this; + } + + CRGBPalette16( const CHSVPalette16& rhs) + { + for( uint8_t i = 0; i < 16; i++) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + } + CRGBPalette16( const CHSV rhs[16]) + { + for( uint8_t i = 0; i < 16; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + } + CRGBPalette16& operator=( const CHSVPalette16& rhs) + { + for( uint8_t i = 0; i < 16; i++) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + CRGBPalette16& operator=( const CHSV rhs[16]) + { + for( uint8_t i = 0; i < 16; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + + CRGBPalette16( const TProgmemRGBPalette16& rhs) + { + for( uint8_t i = 0; i < 16; i++) { + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); + } + } + CRGBPalette16& operator=( const TProgmemRGBPalette16& rhs) + { + for( uint8_t i = 0; i < 16; i++) { + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); + } + return *this; + } + + bool operator==( const CRGBPalette16 rhs) + { + const uint8_t* p = (const uint8_t*)(&(this->entries[0])); + const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); + if( p == q) return true; + for( uint8_t i = 0; i < (sizeof( entries)); i++) { + if( *p != *q) return false; + p++; + q++; + } + return true; + } + bool operator!=( const CRGBPalette16 rhs) + { + return !( *this == rhs); + } + + inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + inline CRGB& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + inline const CRGB& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + operator CRGB*() + { + return &(entries[0]); + } + + CRGBPalette16( const CHSV& c1) + { + fill_solid( &(entries[0]), 16, c1); + } + CRGBPalette16( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 16, c1, c2); + } + CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3); + } + CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); + } + + CRGBPalette16( const CRGB& c1) + { + fill_solid( &(entries[0]), 16, c1); + } + CRGBPalette16( const CRGB& c1, const CRGB& c2) + { + fill_gradient_RGB( &(entries[0]), 16, c1, c2); + } + CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3) + { + fill_gradient_RGB( &(entries[0]), 16, c1, c2, c3); + } + CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) + { + fill_gradient_RGB( &(entries[0]), 16, c1, c2, c3, c4); + } + + + // Gradient palettes are loaded into CRGB16Palettes in such a way + // that, if possible, every color represented in the gradient palette + // is also represented in the CRGBPalette16. + // For example, consider a gradient palette that is all black except + // for a single, one-element-wide (1/256th!) spike of red in the middle: + // 0, 0,0,0 + // 124, 0,0,0 + // 125, 255,0,0 // one 1/256th-palette-wide red stripe + // 126, 0,0,0 + // 255, 0,0,0 + // A naive conversion of this 256-element palette to a 16-element palette + // might accidentally completely eliminate the red spike, rendering the + // palette completely black. + // However, the conversions provided here would attempt to include a + // the red stripe in the output, more-or-less as faithfully as possible. + // So in this case, the resulting CRGBPalette16 palette would have a red + // stripe in the middle which was 1/16th of a palette wide -- the + // narrowest possible in a CRGBPalette16. + // This means that the relative width of stripes in a CRGBPalette16 + // will be, by definition, different from the widths in the gradient + // palette. This code attempts to preserve "all the colors", rather than + // the exact stripe widths at the expense of dropping some colors. + CRGBPalette16( TProgmemRGBGradientPalette_bytes progpal ) + { + *this = progpal; + } + CRGBPalette16& operator=( TProgmemRGBGradientPalette_bytes progpal ) + { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint16_t count = 0; + do { + u.dword = FL_PGM_READ_DWORD_NEAR(progent + count); + count++;; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + progent++; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 16; + iend8 = indexend / 16; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + CRGBPalette16& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) + { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint16_t count = 0; + do { + u = *(ent + count); + count++;; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + + u = *ent; + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + ent++; + u = *ent; + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 16; + iend8 = indexend / 16; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + +}; + + + +class CHSVPalette32 { +public: + CHSV entries[32]; + CHSVPalette32() {}; + CHSVPalette32( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, + const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, + const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, + const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) + { + for( uint8_t i = 0; i < 2; i++) { + entries[0+i]=c00; entries[2+i]=c01; entries[4+i]=c02; entries[6+i]=c03; + entries[8+i]=c04; entries[10+i]=c05; entries[12+i]=c06; entries[14+i]=c07; + entries[16+i]=c08; entries[18+i]=c09; entries[20+i]=c10; entries[22+i]=c11; + entries[24+i]=c12; entries[26+i]=c13; entries[28+i]=c14; entries[30+i]=c15; + } + }; + + CHSVPalette32( const CHSVPalette32& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + CHSVPalette32& operator=( const CHSVPalette32& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + + CHSVPalette32( const TProgmemHSVPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); + entries[i].hue = xyz.red; + entries[i].sat = xyz.green; + entries[i].val = xyz.blue; + } + } + CHSVPalette32& operator=( const TProgmemHSVPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); + entries[i].hue = xyz.red; + entries[i].sat = xyz.green; + entries[i].val = xyz.blue; + } + return *this; + } + + inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + inline CHSV& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + inline const CHSV& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + operator CHSV*() + { + return &(entries[0]); + } + + bool operator==( const CHSVPalette32 rhs) + { + const uint8_t* p = (const uint8_t*)(&(this->entries[0])); + const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); + if( p == q) return true; + for( uint8_t i = 0; i < (sizeof( entries)); i++) { + if( *p != *q) return false; + p++; + q++; + } + return true; + } + bool operator!=( const CHSVPalette32 rhs) + { + return !( *this == rhs); + } + + CHSVPalette32( const CHSV& c1) + { + fill_solid( &(entries[0]), 32, c1); + } + CHSVPalette32( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 32, c1, c2); + } + CHSVPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3); + } + CHSVPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3, c4); + } + +}; + +class CRGBPalette32 { +public: + CRGB entries[32]; + CRGBPalette32() {}; + CRGBPalette32( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, + const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, + const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, + const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) + { + for( uint8_t i = 0; i < 2; i++) { + entries[0+i]=c00; entries[2+i]=c01; entries[4+i]=c02; entries[6+i]=c03; + entries[8+i]=c04; entries[10+i]=c05; entries[12+i]=c06; entries[14+i]=c07; + entries[16+i]=c08; entries[18+i]=c09; entries[20+i]=c10; entries[22+i]=c11; + entries[24+i]=c12; entries[26+i]=c13; entries[28+i]=c14; entries[30+i]=c15; + } + }; + + CRGBPalette32( const CRGBPalette32& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + CRGBPalette32( const CRGB rhs[32]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + } + CRGBPalette32& operator=( const CRGBPalette32& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + CRGBPalette32& operator=( const CRGB rhs[32]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + return *this; + } + + CRGBPalette32( const CHSVPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + } + CRGBPalette32( const CHSV rhs[32]) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + } + CRGBPalette32& operator=( const CHSVPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + CRGBPalette32& operator=( const CHSV rhs[32]) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + + CRGBPalette32( const TProgmemRGBPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); + } + } + CRGBPalette32& operator=( const TProgmemRGBPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); + } + return *this; + } + + bool operator==( const CRGBPalette32 rhs) + { + const uint8_t* p = (const uint8_t*)(&(this->entries[0])); + const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); + if( p == q) return true; + for( uint8_t i = 0; i < (sizeof( entries)); i++) { + if( *p != *q) return false; + p++; + q++; + } + return true; + } + bool operator!=( const CRGBPalette32 rhs) + { + return !( *this == rhs); + } + + inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + inline CRGB& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + inline const CRGB& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + operator CRGB*() + { + return &(entries[0]); + } + + CRGBPalette32( const CHSV& c1) + { + fill_solid( &(entries[0]), 32, c1); + } + CRGBPalette32( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 32, c1, c2); + } + CRGBPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3); + } + CRGBPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3, c4); + } + + CRGBPalette32( const CRGB& c1) + { + fill_solid( &(entries[0]), 32, c1); + } + CRGBPalette32( const CRGB& c1, const CRGB& c2) + { + fill_gradient_RGB( &(entries[0]), 32, c1, c2); + } + CRGBPalette32( const CRGB& c1, const CRGB& c2, const CRGB& c3) + { + fill_gradient_RGB( &(entries[0]), 32, c1, c2, c3); + } + CRGBPalette32( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) + { + fill_gradient_RGB( &(entries[0]), 32, c1, c2, c3, c4); + } + + + CRGBPalette32( const CRGBPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + } + CRGBPalette32& operator=( const CRGBPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + return *this; + } + + CRGBPalette32( const TProgmemRGBPalette16& rhs) + { + CRGBPalette16 p16(rhs); + *this = p16; + } + CRGBPalette32& operator=( const TProgmemRGBPalette16& rhs) + { + CRGBPalette16 p16(rhs); + *this = p16; + return *this; + } + + + // Gradient palettes are loaded into CRGB16Palettes in such a way + // that, if possible, every color represented in the gradient palette + // is also represented in the CRGBPalette32. + // For example, consider a gradient palette that is all black except + // for a single, one-element-wide (1/256th!) spike of red in the middle: + // 0, 0,0,0 + // 124, 0,0,0 + // 125, 255,0,0 // one 1/256th-palette-wide red stripe + // 126, 0,0,0 + // 255, 0,0,0 + // A naive conversion of this 256-element palette to a 16-element palette + // might accidentally completely eliminate the red spike, rendering the + // palette completely black. + // However, the conversions provided here would attempt to include a + // the red stripe in the output, more-or-less as faithfully as possible. + // So in this case, the resulting CRGBPalette32 palette would have a red + // stripe in the middle which was 1/16th of a palette wide -- the + // narrowest possible in a CRGBPalette32. + // This means that the relative width of stripes in a CRGBPalette32 + // will be, by definition, different from the widths in the gradient + // palette. This code attempts to preserve "all the colors", rather than + // the exact stripe widths at the expense of dropping some colors. + CRGBPalette32( TProgmemRGBGradientPalette_bytes progpal ) + { + *this = progpal; + } + CRGBPalette32& operator=( TProgmemRGBGradientPalette_bytes progpal ) + { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint16_t count = 0; + do { + u.dword = FL_PGM_READ_DWORD_NEAR(progent + count); + count++;; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + progent++; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 8; + iend8 = indexend / 8; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 31)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + CRGBPalette32& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) + { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint16_t count = 0; + do { + u = *(ent + count); + count++;; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + + u = *ent; + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + ent++; + u = *ent; + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 8; + iend8 = indexend / 8; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 31)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + +}; + + + +class CRGBPalette256 { +public: + CRGB entries[256]; + CRGBPalette256() {}; + CRGBPalette256( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, + const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, + const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, + const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) + { + CRGBPalette16 p16(c00,c01,c02,c03,c04,c05,c06,c07, + c08,c09,c10,c11,c12,c13,c14,c15); + *this = p16; + }; + + CRGBPalette256( const CRGBPalette256& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + CRGBPalette256( const CRGB rhs[256]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + } + CRGBPalette256& operator=( const CRGBPalette256& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + CRGBPalette256& operator=( const CRGB rhs[256]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + return *this; + } + + CRGBPalette256( const CHSVPalette256& rhs) + { + for( int i = 0; i < 256; i++) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + } + CRGBPalette256( const CHSV rhs[256]) + { + for( int i = 0; i < 256; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + } + CRGBPalette256& operator=( const CHSVPalette256& rhs) + { + for( int i = 0; i < 256; i++) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + CRGBPalette256& operator=( const CHSV rhs[256]) + { + for( int i = 0; i < 256; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + + CRGBPalette256( const CRGBPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + } + CRGBPalette256& operator=( const CRGBPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + return *this; + } + + CRGBPalette256( const TProgmemRGBPalette16& rhs) + { + CRGBPalette16 p16(rhs); + *this = p16; + } + CRGBPalette256& operator=( const TProgmemRGBPalette16& rhs) + { + CRGBPalette16 p16(rhs); + *this = p16; + return *this; + } + + bool operator==( const CRGBPalette256 rhs) + { + const uint8_t* p = (const uint8_t*)(&(this->entries[0])); + const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); + if( p == q) return true; + for( uint16_t i = 0; i < (sizeof( entries)); i++) { + if( *p != *q) return false; + p++; + q++; + } + return true; + } + bool operator!=( const CRGBPalette256 rhs) + { + return !( *this == rhs); + } + + inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + inline CRGB& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + inline const CRGB& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + operator CRGB*() + { + return &(entries[0]); + } + + CRGBPalette256( const CHSV& c1) + { + fill_solid( &(entries[0]), 256, c1); + } + CRGBPalette256( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 256, c1, c2); + } + CRGBPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 256, c1, c2, c3); + } + CRGBPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 256, c1, c2, c3, c4); + } + + CRGBPalette256( const CRGB& c1) + { + fill_solid( &(entries[0]), 256, c1); + } + CRGBPalette256( const CRGB& c1, const CRGB& c2) + { + fill_gradient_RGB( &(entries[0]), 256, c1, c2); + } + CRGBPalette256( const CRGB& c1, const CRGB& c2, const CRGB& c3) + { + fill_gradient_RGB( &(entries[0]), 256, c1, c2, c3); + } + CRGBPalette256( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) + { + fill_gradient_RGB( &(entries[0]), 256, c1, c2, c3, c4); + } + + CRGBPalette256( TProgmemRGBGradientPalette_bytes progpal ) + { + *this = progpal; + } + CRGBPalette256& operator=( TProgmemRGBGradientPalette_bytes progpal ) + { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + while( indexstart < 255) { + progent++; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + fill_gradient_RGB( &(entries[0]), indexstart, rgbstart, indexend, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + CRGBPalette256& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) + { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + u = *ent; + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + while( indexstart < 255) { + ent++; + u = *ent; + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + fill_gradient_RGB( &(entries[0]), indexstart, rgbstart, indexend, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } +}; + + + +typedef enum { NOBLEND=0, LINEARBLEND=1 } TBlendType; + +CRGB ColorFromPalette( const CRGBPalette16& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +CRGB ColorFromPalette( const CRGBPalette256& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=NOBLEND ); + +CHSV ColorFromPalette( const CHSVPalette16& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +CHSV ColorFromPalette( const CHSVPalette256& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=NOBLEND ); + +CRGB ColorFromPalette( const CRGBPalette32& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +CRGB ColorFromPalette( const TProgmemRGBPalette32& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +CHSV ColorFromPalette( const CHSVPalette32& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + + +// Fill a range of LEDs with a sequece of entryies from a palette +template +void fill_palette(CRGB* L, uint16_t N, uint8_t startIndex, uint8_t incIndex, + const PALETTE& pal, uint8_t brightness, TBlendType blendType) +{ + uint8_t colorIndex = startIndex; + for( uint16_t i = 0; i < N; i++) { + L[i] = ColorFromPalette( pal, colorIndex, brightness, blendType); + colorIndex += incIndex; + } +} + +template +void map_data_into_colors_through_palette( + uint8_t *dataArray, uint16_t dataCount, + CRGB* targetColorArray, + const PALETTE& pal, + uint8_t brightness=255, + uint8_t opacity=255, + TBlendType blendType=LINEARBLEND) +{ + for( uint16_t i = 0; i < dataCount; i++) { + uint8_t d = dataArray[i]; + CRGB rgb = ColorFromPalette( pal, d, brightness, blendType); + if( opacity == 255 ) { + targetColorArray[i] = rgb; + } else { + targetColorArray[i].nscale8( 256 - opacity); + rgb.nscale8_video( opacity); + targetColorArray[i] += rgb; + } + } +} + +// nblendPaletteTowardPalette: +// Alter one palette by making it slightly more like +// a 'target palette', used for palette cross-fades. +// +// It does this by comparing each of the R, G, and B channels +// of each entry in the current palette to the corresponding +// entry in the target palette and making small adjustments: +// If the Red channel is too low, it will be increased. +// If the Red channel is too high, it will be slightly reduced. +// ... and likewise for Green and Blue channels. +// +// Additionally, there are two significant visual improvements +// to this algorithm implemented here. First is this: +// When increasing a channel, it is stepped up by ONE. +// When decreasing a channel, it is stepped down by TWO. +// Due to the way the eye perceives light, and the way colors +// are represented in RGB, this produces a more uniform apparent +// brightness when cross-fading between most palette colors. +// +// The second visual tweak is limiting the number of changes +// that will be made to the palette at once. If all the palette +// entries are changed at once, it can give a muddled appearance. +// However, if only a few palette entries are changed at once, +// you get a visually smoother transition: in the middle of the +// cross-fade your current palette will actually contain some +// colors from the old palette, a few blended colors, and some +// colors from the new palette. +// The maximum number of possible palette changes per call +// is 48 (sixteen color entries time three channels each). +// The default 'maximim number of changes' here is 12, meaning +// that only approximately a quarter of the palette entries +// will be changed per call. +void nblendPaletteTowardPalette( CRGBPalette16& currentPalette, + CRGBPalette16& targetPalette, + uint8_t maxChanges=24); + + + + +// You can also define a static RGB palette very compactly in terms of a series +// of connected color gradients. +// For example, if you want the first 3/4ths of the palette to be a slow +// gradient ramping from black to red, and then the remaining 1/4 of the +// palette to be a quicker ramp to white, you specify just three points: the +// starting black point (at index 0), the red midpoint (at index 192), +// and the final white point (at index 255). It looks like this: +// +// index: 0 192 255 +// |----------r-r-r-rrrrrrrrRrRrRrRrRRRR-|-RRWRWWRWWW-| +// color: (0,0,0) (255,0,0) (255,255,255) +// +// Here's how you'd define that gradient palette: +// +// DEFINE_GRADIENT_PALETTE( black_to_red_to_white_p ) { +// 0, 0, 0, 0, /* at index 0, black(0,0,0) */ +// 192, 255, 0, 0, /* at index 192, red(255,0,0) */ +// 255, 255,255,255 /* at index 255, white(255,255,255) */ +// }; +// +// This format is designed for compact storage. The example palette here +// takes up just 12 bytes of PROGMEM (flash) storage, and zero bytes +// of SRAM when not currently in use. +// +// To use one of these gradient palettes, simply assign it into a +// CRGBPalette16 or a CRGBPalette256, like this: +// +// CRGBPalette16 pal = black_to_red_to_white_p; +// +// When the assignment is made, the gradients are expanded out into +// either 16 or 256 palette entries, depending on the kind of palette +// object they're assigned to. +// +// IMPORTANT NOTES & CAVEATS: +// +// - The last 'index' position MUST BE 255! Failure to end with +// index 255 will result in program hangs or crashes. +// +// - At this point, these gradient palette definitions MUST BE +// stored in PROGMEM on AVR-based Arduinos. If you use the +// DEFINE_GRADIENT_PALETTE macro, this is taken care of automatically. +// + +#define DEFINE_GRADIENT_PALETTE(X) \ + FL_ALIGN_PROGMEM \ + extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM = + +#define DECLARE_GRADIENT_PALETTE(X) \ + FL_ALIGN_PROGMEM \ + extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM + + +// Functions to apply gamma adjustments, either: +// - a single gamma adjustment to a single scalar value, +// - a single gamma adjustment to each channel of a CRGB color, or +// - different gamma adjustments for each channel of a CRFB color. +// +// Note that the gamma is specified as a traditional floating point value +// e.g., "2.5", and as such these functions should not be called in +// your innermost pixel loops, or in animations that are extremely +// low on program storage space. Nevertheless, if you need these +// functions, here they are. +// +// Furthermore, bear in mind that CRGB leds have only eight bits +// per channel of color resolution, and that very small, subtle shadings +// may not be visible. +uint8_t applyGamma_video( uint8_t brightness, float gamma); +CRGB applyGamma_video( const CRGB& orig, float gamma); +CRGB applyGamma_video( const CRGB& orig, float gammaR, float gammaG, float gammaB); +// The "n" versions below modify their arguments in-place. +CRGB& napplyGamma_video( CRGB& rgb, float gamma); +CRGB& napplyGamma_video( CRGB& rgb, float gammaR, float gammaG, float gammaB); +void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gamma); +void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gammaR, float gammaG, float gammaB); + + +FASTLED_NAMESPACE_END + +///@} +#endif diff --git a/libraries/FastLED-master/controller.h b/libraries/FastLED-master/controller.h new file mode 100644 index 0000000000..61b5a94af0 --- /dev/null +++ b/libraries/FastLED-master/controller.h @@ -0,0 +1,407 @@ +#ifndef __INC_CONTROLLER_H +#define __INC_CONTROLLER_H + +///@file controller.h +/// base definitions used by led controllers for writing out led data + +#include "FastLED.h" +#include "led_sysdefs.h" +#include "pixeltypes.h" +#include "color.h" +#include + +FASTLED_NAMESPACE_BEGIN + +#define RO(X) RGB_BYTE(RGB_ORDER, X) +#define RGB_BYTE(RO,X) (((RO)>>(3*(2-(X)))) & 0x3) + +#define RGB_BYTE0(RO) ((RO>>6) & 0x3) +#define RGB_BYTE1(RO) ((RO>>3) & 0x3) +#define RGB_BYTE2(RO) ((RO) & 0x3) + +// operator byte *(struct CRGB[] arr) { return (byte*)arr; } + +#define DISABLE_DITHER 0x00 +#define BINARY_DITHER 0x01 +typedef uint8_t EDitherMode; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// LED Controller interface definition +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Base definition for an LED controller. Pretty much the methods that every LED controller object will make available. +/// Note that the showARGB method is not impelemented for all controllers yet. Note also the methods for eventual checking +/// of background writing of data (I'm looking at you, teensy 3.0 DMA controller!). If you want to pass LED controllers around +/// to methods, make them references to this type, keeps your code saner. However, most people won't be seeing/using these objects +/// directly at all +class CLEDController { +protected: + friend class CFastLED; + CRGB *m_Data; + CLEDController *m_pNext; + CRGB m_ColorCorrection; + CRGB m_ColorTemperature; + EDitherMode m_DitherMode; + int m_nLeds; + static CLEDController *m_pHead; + static CLEDController *m_pTail; + + /// set all the leds on the controller to a given color + ///@param data the crgb color to set the leds to + ///@param nLeds the numner of leds to set to this color + ///@param scale the rgb scaling value for outputting color + virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) = 0; + + /// write the passed in rgb data out to the leds managed by this controller + ///@param data the rgb data to write out to the strip + ///@param nLeds the number of leds being written out + ///@param scale the rgb scaling to apply to each led before writing it out + virtual void show(const struct CRGB *data, int nLeds, CRGB scale) = 0; + +public: + /// create an led controller object, add it to the chain of controllers + CLEDController() : m_Data(NULL), m_ColorCorrection(UncorrectedColor), m_ColorTemperature(UncorrectedTemperature), m_DitherMode(BINARY_DITHER), m_nLeds(0) { + m_pNext = NULL; + if(m_pHead==NULL) { m_pHead = this; } + if(m_pTail != NULL) { m_pTail->m_pNext = this; } + m_pTail = this; + } + + ///initialize the LED controller + virtual void init() = 0; + + ///clear out/zero out the given number of leds. + virtual void clearLeds(int nLeds) { showColor(CRGB::Black, nLeds, CRGB::Black); } + + /// show function w/integer brightness, will scale for color correction and temperature + void show(const struct CRGB *data, int nLeds, uint8_t brightness) { + show(data, nLeds, getAdjustment(brightness)); + } + + /// show function w/integer brightness, will scale for color correction and temperature + void showColor(const struct CRGB &data, int nLeds, uint8_t brightness) { + showColor(data, nLeds, getAdjustment(brightness)); + } + + /// show function using the "attached to this controller" led data + void showLeds(uint8_t brightness=255) { + show(m_Data, m_nLeds, getAdjustment(brightness)); + } + + /// show the given color on the led strip + void showColor(const struct CRGB & data, uint8_t brightness=255) { + showColor(data, m_nLeds, getAdjustment(brightness)); + } + + /// get the first led controller in the chain of controllers + static CLEDController *head() { return m_pHead; } + /// get the next controller in the chain after this one. will return NULL at the end of the chain + CLEDController *next() { return m_pNext; } + + /// set the default array of leds to be used by this controller + CLEDController & setLeds(CRGB *data, int nLeds) { + m_Data = data; + m_nLeds = nLeds; + return *this; + } + + /// zero out the led data managed by this controller + void clearLedData() { + if(m_Data) { + memset8((void*)m_Data, 0, sizeof(struct CRGB) * m_nLeds); + } + } + + /// How many leds does this controller manage? + virtual int size() { return m_nLeds; } + + /// Pointer to the CRGB array for this controller + CRGB* leds() { return m_Data; } + + /// Reference to the n'th item in the controller + CRGB &operator[](int x) { return m_Data[x]; } + + /// set the dithering mode for this controller to use + inline CLEDController & setDither(uint8_t ditherMode = BINARY_DITHER) { m_DitherMode = ditherMode; return *this; } + /// get the dithering option currently set for this controller + inline uint8_t getDither() { return m_DitherMode; } + + /// the the color corrction to use for this controller, expressed as an rgb object + CLEDController & setCorrection(CRGB correction) { m_ColorCorrection = correction; return *this; } + /// set the color correction to use for this controller + CLEDController & setCorrection(LEDColorCorrection correction) { m_ColorCorrection = correction; return *this; } + /// get the correction value used by this controller + CRGB getCorrection() { return m_ColorCorrection; } + + /// set the color temperature, aka white point, for this controller + CLEDController & setTemperature(CRGB temperature) { m_ColorTemperature = temperature; return *this; } + /// set the color temperature, aka white point, for this controller + CLEDController & setTemperature(ColorTemperature temperature) { m_ColorTemperature = temperature; return *this; } + /// get the color temperature, aka whipe point, for this controller + CRGB getTemperature() { return m_ColorTemperature; } + + /// Get the combined brightness/color adjustment for this controller + CRGB getAdjustment(uint8_t scale) { + return computeAdjustment(scale, m_ColorCorrection, m_ColorTemperature); + } + + static CRGB computeAdjustment(uint8_t scale, const CRGB & colorCorrection, const CRGB & colorTemperature) { + #if defined(NO_CORRECTION) && (NO_CORRECTION==1) + return CRGB(scale,scale,scale); + #else + CRGB adj(0,0,0); + + if(scale > 0) { + for(uint8_t i = 0; i < 3; i++) { + uint8_t cc = colorCorrection.raw[i]; + uint8_t ct = colorTemperature.raw[i]; + if(cc > 0 && ct > 0) { + uint32_t work = (((uint32_t)cc)+1) * (((uint32_t)ct)+1) * scale; + work /= 0x10000L; + adj.raw[i] = work & 0xFF; + } + } + } + + return adj; + #endif + } + virtual uint16_t getMaxRefreshRate() const { return 0; } +}; + +// Pixel controller class. This is the class that we use to centralize pixel access in a block of data, including +// support for things like RGB reordering, scaling, dithering, skipping (for ARGB data), and eventually, we will +// centralize 8/12/16 conversions here as well. +template +struct PixelController { + const uint8_t *mData; + int mLen,mLenRemaining; + uint8_t d[3]; + uint8_t e[3]; + CRGB mScale; + int8_t mAdvance; + int mOffsets[LANES]; + + PixelController(const PixelController & other) { + d[0] = other.d[0]; + d[1] = other.d[1]; + d[2] = other.d[2]; + e[0] = other.e[0]; + e[1] = other.e[1]; + e[2] = other.e[2]; + mData = other.mData; + mScale = other.mScale; + mAdvance = other.mAdvance; + mLenRemaining = mLen = other.mLen; + for(int i = 0; i < LANES; i++) { mOffsets[i] = other.mOffsets[i]; } + + } + + void initOffsets(int len) { + int nOffset = 0; + for(int i = 0; i < LANES; i++) { + mOffsets[i] = nOffset; + if((1<1) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>2) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>4) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>8) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>16) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>32) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>64) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>128) ) +#define VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS + + // R is the digther signal 'counter'. + static uint8_t R = 0; + R++; + + // R is wrapped around at 2^ditherBits, + // so if ditherBits is 2, R will cycle through (0,1,2,3) + uint8_t ditherBits = VIRTUAL_BITS; + R &= (0x01 << ditherBits) - 1; + + // Q is the "unscaled dither signal" itself. + // It's initialized to the reversed bits of R. + // If 'ditherBits' is 2, Q here will cycle through (0,128,64,192) + uint8_t Q = 0; + + // Reverse bits in a byte + { + if(R & 0x01) { Q |= 0x80; } + if(R & 0x02) { Q |= 0x40; } + if(R & 0x04) { Q |= 0x20; } + if(R & 0x08) { Q |= 0x10; } + if(R & 0x10) { Q |= 0x08; } + if(R & 0x20) { Q |= 0x04; } + if(R & 0x40) { Q |= 0x02; } + if(R & 0x80) { Q |= 0x01; } + } + + // Now we adjust Q to fall in the center of each range, + // instead of at the start of the range. + // If ditherBits is 2, Q will be (0, 128, 64, 192) at first, + // and this adjustment makes it (31, 159, 95, 223). + if( ditherBits < 8) { + Q += 0x01 << (7 - ditherBits); + } + + // D and E form the "scaled dither signal" + // which is added to pixel values to affect the + // actual dithering. + + // Setup the initial D and E values + for(int i = 0; i < 3; i++) { + uint8_t s = mScale.raw[i]; + e[i] = s ? (256/s) + 1 : 0; + d[i] = scale8(Q, e[i]); +#if (FASTLED_SCALE8_FIXED == 1) + if(d[i]) (d[i]--); +#endif + if(e[i]) e[i]--; + } +#endif + } + + // Do we have n pixels left to process? + __attribute__((always_inline)) inline bool has(int n) { + return mLenRemaining >= n; + } + + // toggle dithering enable + void enable_dithering(EDitherMode dither) { + switch(dither) { + case BINARY_DITHER: init_binary_dithering(); break; + default: d[0]=d[1]=d[2]=e[0]=e[1]=e[2]=0; break; + } + } + + __attribute__((always_inline)) inline int size() { return mLen; } + + // get the amount to advance the pointer by + __attribute__((always_inline)) inline int advanceBy() { return mAdvance; } + + // advance the data pointer forward, adjust position counter + __attribute__((always_inline)) inline void advanceData() { mData += mAdvance; mLenRemaining--;} + + // step the dithering forward + __attribute__((always_inline)) inline void stepDithering() { + // IF UPDATING HERE, BE SURE TO UPDATE THE ASM VERSION IN + // clockless_trinket.h! + d[0] = e[0] - d[0]; + d[1] = e[1] - d[1]; + d[2] = e[2] - d[2]; + } + + // Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately + __attribute__((always_inline)) inline void preStepFirstByteDithering() { + d[RO(0)] = e[RO(0)] - d[RO(0)]; + } + + template __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc) { return pc.mData[RO(SLOT)]; } + template __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + RO(SLOT)]; } + + template __attribute__((always_inline)) inline static uint8_t dither(PixelController & pc, uint8_t b) { return b ? qadd8(b, pc.d[RO(SLOT)]) : 0; } + template __attribute__((always_inline)) inline static uint8_t dither(PixelController & , uint8_t b, uint8_t d) { return b ? qadd8(b,d) : 0; } + + template __attribute__((always_inline)) inline static uint8_t scale(PixelController & pc, uint8_t b) { return scale8(b, pc.mScale.raw[RO(SLOT)]); } + template __attribute__((always_inline)) inline static uint8_t scale(PixelController & , uint8_t b, uint8_t scale) { return scale8(b, scale); } + + // composite shortcut functions for loading, dithering, and scaling + template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc) { return scale(pc, pc.dither(pc, pc.loadByte(pc))); } + template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane) { return scale(pc, pc.dither(pc, pc.loadByte(pc, lane))); } + template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t d, uint8_t scale) { return scale8(pc.dither(pc, pc.loadByte(pc, lane), d), scale); } + template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t scale) { return scale8(pc.loadByte(pc, lane), scale); } + + template __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale(pc); } + template __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale(pc, lane); } + + template __attribute__((always_inline)) inline static uint8_t getd(PixelController & pc) { return pc.d[RO(SLOT)]; } + template __attribute__((always_inline)) inline static uint8_t getscale(PixelController & pc) { return pc.mScale.raw[RO(SLOT)]; } + + // Helper functions to get around gcc stupidities + __attribute__((always_inline)) inline uint8_t loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); } + __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); } + __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); } + __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane) { return advanceAndLoadAndScale<0>(*this, lane); } + __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane); } + + __attribute__((always_inline)) inline uint8_t loadAndScale0() { return loadAndScale<0>(*this); } + __attribute__((always_inline)) inline uint8_t loadAndScale1() { return loadAndScale<1>(*this); } + __attribute__((always_inline)) inline uint8_t loadAndScale2() { return loadAndScale<2>(*this); } + __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0() { return advanceAndLoadAndScale<0>(*this); } + __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0() { stepDithering(); return advanceAndLoadAndScale<0>(*this); } +}; + +template class CPixelLEDController : public CLEDController { +protected: + virtual void showPixels(PixelController & pixels) = 0; + + /// set all the leds on the controller to a given color + ///@param data the crgb color to set the leds to + ///@param nLeds the numner of leds to set to this color + ///@param scale the rgb scaling value for outputting color + virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) { + PixelController pixels(data, nLeds, scale, getDither()); + showPixels(pixels); + } + +/// write the passed in rgb data out to the leds managed by this controller +///@param data the rgb data to write out to the strip +///@param nLeds the number of leds being written out +///@param scale the rgb scaling to apply to each led before writing it out + virtual void show(const struct CRGB *data, int nLeds, CRGB scale) { + PixelController pixels(data, nLeds, scale, getDither()); + showPixels(pixels); + } + +public: + CPixelLEDController() : CLEDController() {} +}; + + +FASTLED_NAMESPACE_END + +#endif diff --git a/libraries/FastLED-master/cpp_compat.h b/libraries/FastLED-master/cpp_compat.h new file mode 100644 index 0000000000..ab5b773aa4 --- /dev/null +++ b/libraries/FastLED-master/cpp_compat.h @@ -0,0 +1,16 @@ +#ifndef __INC_CPP_COMPAT_H +#define __INC_CPP_COMPAT_H + +#include "FastLED.h" + +#if __cplusplus <= 199711L + +#define static_assert(expression, message) +#define constexpr const + +#else + +// things that we can turn on if we're in a C++11 environment +#endif + +#endif diff --git a/libraries/FastLED-master/dmx.h b/libraries/FastLED-master/dmx.h new file mode 100644 index 0000000000..245c7cfcce --- /dev/null +++ b/libraries/FastLED-master/dmx.h @@ -0,0 +1,65 @@ +#ifndef __INC_DMX_H +#define __INC_DMX_H + +#include "FastLED.h" + +#ifdef DmxSimple_h +#include +#define HAS_DMX_SIMPLE + +///@ingroup chipsets +///@{ +FASTLED_NAMESPACE_BEGIN + +// note - dmx simple must be included before FastSPI for this code to be enabled +template class DMXSimpleController : public CPixelLEDController { +public: + // initialize the LED controller + virtual void init() { DmxSimple.usePin(DATA_PIN); } + +protected: + virtual void showPixels(PixelController & pixels) { + int iChannel = 1; + while(pixels.has(1)) { + DmxSimple.write(iChannel++, pixels.loadAndScale0()); + DmxSimple.write(iChannel++, pixels.loadAndScale1()); + DmxSimple.write(iChannel++, pixels.loadAndScale2()); + pixels.advanceData(); + pixels.stepDithering(); + } + } +}; + +FASTLED_NAMESPACE_END + +#endif + +#ifdef DmxSerial_h +#include + +FASTLED_NAMESPACE_BEGIN + +template class DMXSerialController : public CPixelLEDController { +public: + // initialize the LED controller + virtual void init() { DMXSerial.init(DMXController); } + + virtual void showPixels(PixelController & pixels) { + int iChannel = 1; + while(pixels.has(1)) { + DMXSerial.write(iChannel++, pixels.loadAndScale0()); + DMXSerial.write(iChannel++, pixels.loadAndScale1()); + DMXSerial.write(iChannel++, pixels.loadAndScale2()); + pixels.advanceData(); + pixels.stepDithering(); + } + } +}; + +FASTLED_NAMESPACE_END +///@} + +#define HAS_DMX_SERIAL +#endif + +#endif diff --git a/libraries/FastLED-master/docs/Doxyfile b/libraries/FastLED-master/docs/Doxyfile new file mode 100644 index 0000000000..eb300236d6 --- /dev/null +++ b/libraries/FastLED-master/docs/Doxyfile @@ -0,0 +1,2331 @@ +# Doxyfile 1.8.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "FastLED" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = 3.1 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = YES + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = . lib8tion + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = M0-clocklessnotes.md TODO.md + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html/docs/3.1 + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra stylesheet files is of importance (e.g. the last +# stylesheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: . +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /