From 04daeeb6812776805a00ff0390bc17142243b739 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 11:27:57 +0200 Subject: [PATCH 01/23] Convert C++ code to C (keeping .cpp extension temporarily) --- .gitignore | 1 + Helios/Button.cpp | 627 +++++++++------- Helios/Button.h | 180 ++--- Helios/Colorset.cpp | 392 +++++----- Helios/Colorset.h | 251 ++++--- Helios/Colortypes.cpp | 383 +++++----- Helios/Colortypes.h | 129 ++-- Helios/Helios.cpp | 1541 ++++++++++++++++++--------------------- Helios/Helios.h | 142 +--- Helios/HeliosConfig.h | 4 +- Helios/Led.cpp | 337 +++++---- Helios/Led.h | 87 +-- Helios/Pattern.cpp | 431 ++++++----- Helios/Pattern.h | 183 ++--- Helios/Patterns.cpp | 397 +++++----- Helios/Patterns.h | 46 +- Helios/Random.cpp | 46 +- Helios/Random.h | 32 +- Helios/Storage.cpp | 217 ++++-- Helios/Storage.h | 66 +- Helios/TimeControl.cpp | 380 +++++----- Helios/TimeControl.h | 60 +- Helios/Timer.cpp | 63 +- Helios/Timer.h | 40 +- HeliosEmbedded/Makefile | 62 +- HeliosEmbedded/main.cpp | 14 +- 26 files changed, 3030 insertions(+), 3081 deletions(-) diff --git a/.gitignore b/.gitignore index 522a6a86..3dea8476 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ !*.pattern !*.h !*.cpp +!*.c !*.py !*.atsln !*.componentinfo.xml diff --git a/Helios/Button.cpp b/Helios/Button.cpp index b58ed095..3ea1f9f2 100644 --- a/Helios/Button.cpp +++ b/Helios/Button.cpp @@ -1,278 +1,349 @@ -#include "Button.h" -#include "TimeControl.h" - -#ifdef HELIOS_EMBEDDED -#include -#include -#ifdef HELIOS_ARDUINO -#include -#endif -#define BUTTON_PIN 3 -#define BUTTON_PORT 2 -#endif - -#include "Helios.h" - -// static members of Button -uint32_t Button::m_pressTime = 0; -uint32_t Button::m_releaseTime = 0; -uint32_t Button::m_holdDuration = 0; -uint32_t Button::m_releaseDuration = 0; -uint8_t Button::m_releaseCount = 0; -bool Button::m_buttonState = false; -bool Button::m_newPress = false; -bool Button::m_newRelease = false; -bool Button::m_isPressed = false; -bool Button::m_shortClick = false; -bool Button::m_longClick = false; -bool Button::m_holdClick = false; - -#ifdef HELIOS_CLI -// an input queue for the button, each tick one even is processed -// out of this queue and used to produce input -std::queue Button::m_inputQueue; -// the virtual pin state -bool Button::m_pinState = false; -// whether the button is waiting to wake the device -bool Button::m_enableWake = false; -#endif - -// initialize a new button object with a pin number -bool Button::init() -{ - m_pressTime = 0; - m_releaseTime = 0; - m_holdDuration = 0; - m_releaseDuration = 0; - m_newPress = false; - m_newRelease = false; - m_shortClick = false; - m_longClick = false; - m_holdClick = false; - m_buttonState = check(); - m_releaseCount = !m_buttonState; - m_isPressed = m_buttonState; -#ifdef HELIOS_CLI - m_pinState = false; - m_enableWake = false; -#endif -#ifdef HELIOS_EMBEDDED -#ifdef HELIOS_ARDUINO - pinMode(3, INPUT); -#else - // turn off wake - PCMSK &= ~(1 << PCINT3); - GIMSK &= ~(1 << PCIE); -#endif -#endif - return true; -} - -// enable wake on press -void Button::enableWake() -{ -#ifdef HELIOS_EMBEDDED - // Configure INT0 to trigger on falling edge - PCMSK |= (1 << PCINT3); - GIMSK |= (1 << PCIE); - sei(); -#else // HELIOS_CLI - m_enableWake = false; -#endif -} - -#ifdef HELIOS_EMBEDDED -ISR(PCINT0_vect) { - PCMSK &= ~(1 << PCINT3); - GIMSK &= ~(1 << PCIE); - Helios::wakeup(); -} -#endif - -// directly poll the pin for whether it's pressed right now -bool Button::check() -{ -#ifdef HELIOS_EMBEDDED -#ifdef HELIOS_ARDUINO - return digitalRead(3) == HIGH; -#else - return (PINB & (1 << 3)) != 0; -#endif -#elif defined(HELIOS_CLI) - // then just return the pin state as-is, the input event may have - // adjusted this value - return m_pinState; -#endif -} - -// detect if the button is being held for a long hold (past long click) -bool Button::holdPressing() -{ - uint16_t holDur = (uint16_t)(Button::holdDuration()); - if (holDur > HOLD_CLICK_START && holDur <= HOLD_CLICK_END && Button::isPressed()) { - return true; - } - return false; -} - -// poll the button pin and update the state of the button object -void Button::update() -{ -#ifdef HELIOS_CLI - // process any pre-input events in the queue - bool processed_pre = processPreInput(); -#endif - - bool newButtonState = check(); - m_newPress = false; - m_newRelease = false; - if (newButtonState != m_buttonState) { - m_buttonState = newButtonState; - m_isPressed = m_buttonState; - if (m_isPressed) { - m_pressTime = Time::getCurtime(); - m_newPress = true; - } else { - m_releaseTime = Time::getCurtime(); - m_newRelease = true; - m_releaseCount++; - } - } - if (m_isPressed) { - m_holdDuration = (Time::getCurtime() >= m_pressTime) ? (uint32_t)(Time::getCurtime() - m_pressTime) : 0; - } else { - m_releaseDuration = (Time::getCurtime() >= m_releaseTime) ? (uint32_t)(Time::getCurtime() - m_releaseTime) : 0; - } - m_shortClick = (m_newRelease && (m_holdDuration <= SHORT_CLICK_THRESHOLD)); - m_longClick = (m_newRelease && (m_holdDuration > SHORT_CLICK_THRESHOLD) && (m_holdDuration < HOLD_CLICK_START)); - m_holdClick = (m_newRelease && (m_holdDuration >= HOLD_CLICK_START) && (m_holdDuration <= HOLD_CLICK_END)); - -#ifdef HELIOS_CLI - // if there was no pre-input event this tick, process a post input event - // to ensure there is only one event per tick processed - if (!processed_pre) { - processPostInput(); - } - - if (m_enableWake) { - if (m_isPressed || m_shortClick || m_longClick) { - Helios::wakeup(); - } - } -#endif -} - -#ifdef HELIOS_CLI -bool Button::processPreInput() -{ - if (!m_inputQueue.size()) { - return false; - } - char command = m_inputQueue.front(); - switch (command) { - case 'p': // press - Button::doPress(); - break; - case 'r': // release - Button::doRelease(); - break; - case 't': // toggle - Button::doToggle(); - break; - case 'q': // quit - Helios::terminate(); - break; - case 'w': // wait - // wait is pre input I guess - break; - default: - // return here! do not pop the queue - // do not process post input events - return false; - } - // now pop whatever pre-input command was processed - m_inputQueue.pop(); - return true; -} - -bool Button::processPostInput() -{ - if (!m_inputQueue.size()) { - // probably processed the pre-input event already - return false; - } - // process input queue from the command line - char command = m_inputQueue.front(); - switch (command) { - case 'c': // click button - Button::doShortClick(); - break; - case 'l': // long click button - Button::doLongClick(); - break; - default: - // should never happen - return false; - } - m_inputQueue.pop(); - return true; -} - -void Button::doShortClick() -{ - m_newRelease = true; - m_shortClick = true; - m_pressTime = Time::getCurtime(); - m_holdDuration = SHORT_CLICK_THRESHOLD - 1; - m_releaseCount++; -} - -void Button::doLongClick() -{ - m_newRelease = true; - m_longClick = true; - m_pressTime = Time::getCurtime(); - m_holdDuration = SHORT_CLICK_THRESHOLD + 1; - m_releaseCount++; -} - -void Button::doHoldClick() -{ - m_newRelease = true; - m_holdClick = true; - m_pressTime = Time::getCurtime(); - m_holdDuration = HOLD_CLICK_START + 1; - m_releaseCount++; -} - -// this will actually press down the button, it's your responsibility to wait -// for the appropriate number of ticks and then release the button -void Button::doPress() -{ - m_pinState = true; -} - -void Button::doRelease() -{ - m_pinState = false; -} - -void Button::doToggle() -{ - m_pinState = !m_pinState; -} - -// queue up an input event for the button -void Button::queueInput(char input) -{ - m_inputQueue.push(input); -} - -uint32_t Button::inputQueueSize() -{ - return m_inputQueue.size(); -} -#endif - -// global button -Button button; +#include "Button.h" +#include "TimeControl.h" +#include "HeliosConfig.h" + +#ifdef HELIOS_EMBEDDED +#include +#include +#ifdef HELIOS_ARDUINO +#include +#endif +#define BUTTON_PIN 3 +#define BUTTON_PORT 2 +#endif + +/* Forward declaration */ +void helios_wakeup(void); +void helios_terminate(void); + +#ifdef HELIOS_CLI +/* Forward declarations for CLI functions */ +static uint8_t button_process_pre_input(void); +static uint8_t button_process_post_input(void); +#endif + +/* static members of Button */ +static uint32_t m_pressTime = 0; +static uint32_t m_releaseTime = 0; +static uint32_t m_holdDuration = 0; +static uint32_t m_releaseDuration = 0; +static uint8_t m_releaseCount = 0; +static uint8_t m_buttonState = 0; +static uint8_t m_newPress = 0; +static uint8_t m_newRelease = 0; +static uint8_t m_isPressed = 0; +static uint8_t m_shortClick = 0; +static uint8_t m_longClick = 0; +static uint8_t m_holdClick = 0; + +#ifdef HELIOS_CLI +/* Note: For embedded builds, we exclude std::queue and CLI-only features. + * The CLI input queue functionality is omitted for embedded targets. */ +static uint8_t m_pinState = 0; +static uint8_t m_enableWake = 0; +/* Simple input queue for CLI - using a fixed-size circular buffer */ +#define INPUT_QUEUE_SIZE 64 +static char m_inputQueue[INPUT_QUEUE_SIZE]; +static uint32_t m_queueHead = 0; +static uint32_t m_queueTail = 0; +#endif + +uint8_t button_init(void) +{ + m_pressTime = 0; + m_releaseTime = 0; + m_holdDuration = 0; + m_releaseDuration = 0; + m_newPress = 0; + m_newRelease = 0; + m_shortClick = 0; + m_longClick = 0; + m_holdClick = 0; + m_buttonState = button_check(); + m_releaseCount = !m_buttonState; + m_isPressed = m_buttonState; +#ifdef HELIOS_CLI + m_pinState = 0; + m_enableWake = 0; + m_queueHead = 0; + m_queueTail = 0; +#endif +#ifdef HELIOS_EMBEDDED +#ifdef HELIOS_ARDUINO + pinMode(3, INPUT); +#else + /* turn off wake */ + PCMSK &= ~(1 << PCINT3); + GIMSK &= ~(1 << PCIE); +#endif +#endif + return 1; +} + +void button_enable_wake(void) +{ +#ifdef HELIOS_EMBEDDED + /* Configure INT0 to trigger on falling edge */ + PCMSK |= (1 << PCINT3); + GIMSK |= (1 << PCIE); + sei(); +#else /* HELIOS_CLI */ + m_enableWake = 0; +#endif +} + +#ifdef HELIOS_EMBEDDED +ISR(PCINT0_vect) { + PCMSK &= ~(1 << PCINT3); + GIMSK &= ~(1 << PCIE); + helios_wakeup(); +} +#endif + +uint8_t button_check(void) +{ +#ifdef HELIOS_EMBEDDED +#ifdef HELIOS_ARDUINO + return digitalRead(3) == HIGH; +#else + return (PINB & (1 << 3)) != 0; +#endif +#elif defined(HELIOS_CLI) + /* then just return the pin state as-is, the input event may have + * adjusted this value */ + return m_pinState; +#else + return 0; +#endif +} + +uint8_t button_hold_pressing(void) +{ + uint16_t holDur = (uint16_t)(button_hold_duration()); + if (holDur > HOLD_CLICK_START && holDur <= HOLD_CLICK_END && button_is_pressed()) { + return 1; + } + return 0; +} + +void button_update(void) +{ +#ifdef HELIOS_CLI + /* process any pre-input events in the queue */ + uint8_t processed_pre = button_process_pre_input(); +#endif + + uint8_t newButtonState = button_check(); + m_newPress = 0; + m_newRelease = 0; + if (newButtonState != m_buttonState) { + m_buttonState = newButtonState; + m_isPressed = m_buttonState; + if (m_isPressed) { + m_pressTime = time_get_current_time(); + m_newPress = 1; + } else { + m_releaseTime = time_get_current_time(); + m_newRelease = 1; + m_releaseCount++; + } + } + if (m_isPressed) { + m_holdDuration = (time_get_current_time() >= m_pressTime) ? (uint32_t)(time_get_current_time() - m_pressTime) : 0; + } else { + m_releaseDuration = (time_get_current_time() >= m_releaseTime) ? (uint32_t)(time_get_current_time() - m_releaseTime) : 0; + } + m_shortClick = (m_newRelease && (m_holdDuration <= SHORT_CLICK_THRESHOLD)); + m_longClick = (m_newRelease && (m_holdDuration > SHORT_CLICK_THRESHOLD) && (m_holdDuration < HOLD_CLICK_START)); + m_holdClick = (m_newRelease && (m_holdDuration >= HOLD_CLICK_START) && (m_holdDuration <= HOLD_CLICK_END)); + +#ifdef HELIOS_CLI + /* if there was no pre-input event this tick, process a post input event + * to ensure there is only one event per tick processed */ + if (!processed_pre) { + button_process_post_input(); + } + + if (m_enableWake) { + if (m_isPressed || m_shortClick || m_longClick) { + helios_wakeup(); + } + } +#endif +} + +uint8_t button_on_press(void) +{ + return m_newPress; +} + +uint8_t button_on_release(void) +{ + return m_newRelease; +} + +uint8_t button_is_pressed(void) +{ + return m_isPressed; +} + +uint8_t button_on_short_click(void) +{ + return m_shortClick; +} + +uint8_t button_on_long_click(void) +{ + return m_longClick; +} + +uint8_t button_on_hold_click(void) +{ + return m_holdClick; +} + +uint32_t button_press_time(void) +{ + return m_pressTime; +} + +uint32_t button_release_time(void) +{ + return m_releaseTime; +} + +uint32_t button_hold_duration(void) +{ + return m_holdDuration; +} + +uint32_t button_release_duration(void) +{ + return m_releaseDuration; +} + +uint8_t button_release_count(void) +{ + return m_releaseCount; +} + +#ifdef HELIOS_CLI +static uint8_t button_process_pre_input(void) +{ + if (m_queueHead == m_queueTail) { + return 0; + } + char command = m_inputQueue[m_queueHead]; + switch (command) { + case 'p': /* press */ + button_do_press(); + break; + case 'r': /* release */ + button_do_release(); + break; + case 't': /* toggle */ + button_do_toggle(); + break; + case 'q': /* quit */ + helios_terminate(); + break; + case 'w': /* wait */ + /* wait is pre input I guess */ + break; + default: + /* return here! do not pop the queue + * do not process post input events */ + return 0; + } + /* now pop whatever pre-input command was processed */ + m_queueHead = (m_queueHead + 1) % INPUT_QUEUE_SIZE; + return 1; +} + +static uint8_t button_process_post_input(void) +{ + if (m_queueHead == m_queueTail) { + /* probably processed the pre-input event already */ + return 0; + } + /* process input queue from the command line */ + char command = m_inputQueue[m_queueHead]; + switch (command) { + case 'c': /* click button */ + button_do_short_click(); + break; + case 'l': /* long click button */ + button_do_long_click(); + break; + default: + /* should never happen */ + return 0; + } + m_queueHead = (m_queueHead + 1) % INPUT_QUEUE_SIZE; + return 1; +} + +void button_do_short_click(void) +{ + m_newRelease = 1; + m_shortClick = 1; + m_pressTime = time_get_current_time(); + m_holdDuration = SHORT_CLICK_THRESHOLD - 1; + m_releaseCount++; +} + +void button_do_long_click(void) +{ + m_newRelease = 1; + m_longClick = 1; + m_pressTime = time_get_current_time(); + m_holdDuration = SHORT_CLICK_THRESHOLD + 1; + m_releaseCount++; +} + +void button_do_hold_click(void) +{ + m_newRelease = 1; + m_holdClick = 1; + m_pressTime = time_get_current_time(); + m_holdDuration = HOLD_CLICK_START + 1; + m_releaseCount++; +} + +/* this will actually press down the button, it's your responsibility to wait + * for the appropriate number of ticks and then release the button */ +void button_do_press(void) +{ + m_pinState = 1; +} + +void button_do_release(void) +{ + m_pinState = 0; +} + +void button_do_toggle(void) +{ + m_pinState = !m_pinState; +} + +/* queue up an input event for the button */ +void button_queue_input(char input) +{ + uint32_t nextTail = (m_queueTail + 1) % INPUT_QUEUE_SIZE; + if (nextTail != m_queueHead) { + m_inputQueue[m_queueTail] = input; + m_queueTail = nextTail; + } +} + +uint32_t button_input_queue_size(void) +{ + if (m_queueTail >= m_queueHead) { + return m_queueTail - m_queueHead; + } else { + return INPUT_QUEUE_SIZE - m_queueHead + m_queueTail; + } +} +#endif + diff --git a/Helios/Button.h b/Helios/Button.h index 33d26ecf..aa278563 100644 --- a/Helios/Button.h +++ b/Helios/Button.h @@ -1,119 +1,77 @@ +#ifndef BUTTON_H +#define BUTTON_H + #include -#ifdef HELIOS_CLI -#include -#endif +/* Initialize button */ +uint8_t button_init(void); -class Button -{ -public: - // initialize a new button object with a pin number - static bool init(); - // directly poll the pin for whether it's pressed right now - static bool check(); - // poll the button pin and update the state of the button object - static void update(); - - // whether the button was pressed this tick - static bool onPress() { return m_newPress; } - // whether the button was released this tick - static bool onRelease() { return m_newRelease; } - // whether the button is currently pressed - static bool isPressed() { return m_isPressed; } - - // whether the button was shortclicked this tick - static bool onShortClick() { return m_shortClick; } - // whether the button was long clicked this tick - static bool onLongClick() { return m_longClick; } - // whether the button was hold clicked this tick - static bool onHoldClick() { return m_holdClick; } - - // detect if the button is being held past long click - static bool holdPressing(); - - // when the button was last pressed - static uint32_t pressTime() { return m_pressTime; } - // when the button was last released - static uint32_t releaseTime() { return m_releaseTime; } - - // how long the button is currently or was last held down (in ticks) - static uint32_t holdDuration() { return m_holdDuration; } - // how long the button is currently or was last released for (in ticks) - static uint32_t releaseDuration() { return m_releaseDuration; } - - // the number of releases - static uint8_t releaseCount() { return m_releaseCount; } - - // enable wake on press - static void enableWake(); +/* Directly poll the pin for whether it's pressed right now */ +uint8_t button_check(void); -#ifdef HELIOS_CLI - // these will 'inject' a short/long click without actually touching the - // button state, it's important that code uses 'onShortClick' or - // 'onLongClick' to capture this injected input event. Code that uses - // for example: 'button.holdDuration() >= threshold && button.onRelease()' - // will never trigger because the injected input event doesn't actually - // press the button or change the button state it just sets the 'shortClick' - // or 'longClick' values accordingly - static void doShortClick(); - static void doLongClick(); - static void doHoldClick(); - - // this will actually press down the button, it's your responsibility to wait - // for the appropriate number of ticks and then release the button - static void doPress(); - static void doRelease(); - static void doToggle(); - - // queue up an input event for the button - static void queueInput(char input); - static uint32_t inputQueueSize(); -#endif +/* Poll the button pin and update the state of the button object */ +void button_update(void); + +/* Whether the button was pressed this tick */ +uint8_t button_on_press(void); + +/* Whether the button was released this tick */ +uint8_t button_on_release(void); + +/* Whether the button is currently pressed */ +uint8_t button_is_pressed(void); + +/* Whether the button was shortclicked this tick */ +uint8_t button_on_short_click(void); + +/* Whether the button was long clicked this tick */ +uint8_t button_on_long_click(void); -private: - // ======================================== - // state data that is populated each check - - // the timestamp of when the button was pressed - static uint32_t m_pressTime; - // the timestamp of when the button was released - static uint32_t m_releaseTime; - - // the last hold duration - static uint32_t m_holdDuration; - // the last release duration - static uint32_t m_releaseDuration; - - // the number of times released, will overflow at 255 - static uint8_t m_releaseCount; - - // the active state of the button - static bool m_buttonState; - - // whether pressed this tick - static bool m_newPress; - // whether released this tick - static bool m_newRelease; - // whether currently pressed - static bool m_isPressed; - // whether a short click occurred - static bool m_shortClick; - // whether a long click occurred - static bool m_longClick; - // whether a long hold occurred - static bool m_holdClick; +/* Whether the button was hold clicked this tick */ +uint8_t button_on_hold_click(void); + +/* Detect if the button is being held past long click */ +uint8_t button_hold_pressing(void); + +/* When the button was last pressed */ +uint32_t button_press_time(void); + +/* When the button was last released */ +uint32_t button_release_time(void); + +/* How long the button is currently or was last held down (in ticks) */ +uint32_t button_hold_duration(void); + +/* How long the button is currently or was last released for (in ticks) */ +uint32_t button_release_duration(void); + +/* The number of releases */ +uint8_t button_release_count(void); + +/* Enable wake on press */ +void button_enable_wake(void); #ifdef HELIOS_CLI - // process pre or post input events from the queue - static bool processPreInput(); - static bool processPostInput(); - - // an input queue for the button, each tick one even is processed - // out of this queue and used to produce input - static std::queue m_inputQueue; - // the virtual pin state that is polled instead of a digital pin - static bool m_pinState; - // whether the button is waiting to wake the device - static bool m_enableWake; +/* These will 'inject' a short/long click without actually touching the + * button state, it's important that code uses 'button_on_short_click' or + * 'button_on_long_click' to capture this injected input event. Code that uses + * for example: 'button_hold_duration() >= threshold && button_on_release()' + * will never trigger because the injected input event doesn't actually + * press the button or change the button state it just sets the 'shortClick' + * or 'longClick' values accordingly */ +void button_do_short_click(void); +void button_do_long_click(void); +void button_do_hold_click(void); + +/* This will actually press down the button, it's your responsibility to wait + * for the appropriate number of ticks and then release the button */ +void button_do_press(void); +void button_do_release(void); +void button_do_toggle(void); + +/* Queue up an input event for the button */ +void button_queue_input(char input); +uint32_t button_input_queue_size(void); +#endif + #endif -}; diff --git a/Helios/Colorset.cpp b/Helios/Colorset.cpp index c3d1d6a9..e3779af9 100644 --- a/Helios/Colorset.cpp +++ b/Helios/Colorset.cpp @@ -4,208 +4,185 @@ #include -// when no color is selected in the colorset the index is this -// then when you call getNext() for the first time it returns -// the 0th color in the colorset and after the index will be 0 +/* when no color is selected in the colorset the index is this + * then when you call colorset_get_next() for the first time it returns + * the 0th color in the colorset and after the index will be 0 */ #define INDEX_INVALID 255 -Colorset::Colorset() : - m_palette(), - m_numColors(0), - m_curIndex(INDEX_INVALID) +void colorset_init(colorset_t *set) { - init(); + memset((void *)set->m_palette, 0, sizeof(set->m_palette)); + set->m_numColors = 0; + set->m_curIndex = INDEX_INVALID; } -Colorset::Colorset(RGBColor c1, RGBColor c2, RGBColor c3, RGBColor c4, - RGBColor c5, RGBColor c6, RGBColor c7, RGBColor c8) : - Colorset() +void colorset_init_multi(colorset_t *set, rgb_color_t c1, rgb_color_t c2, rgb_color_t c3, + rgb_color_t c4, rgb_color_t c5, rgb_color_t c6, rgb_color_t c7, rgb_color_t c8) { - init(c1, c2, c3, c4, c5, c6, c7, c8); + colorset_init(set); + /* would be nice if we could do this another way */ + if (!rgb_empty(&c1)) colorset_add_color(set, c1); + if (!rgb_empty(&c2)) colorset_add_color(set, c2); + if (!rgb_empty(&c3)) colorset_add_color(set, c3); + if (!rgb_empty(&c4)) colorset_add_color(set, c4); + if (!rgb_empty(&c5)) colorset_add_color(set, c5); + if (!rgb_empty(&c6)) colorset_add_color(set, c6); + if (!rgb_empty(&c7)) colorset_add_color(set, c7); + if (!rgb_empty(&c8)) colorset_add_color(set, c8); } -Colorset::Colorset(uint8_t numCols, const uint32_t *cols) : - Colorset() +void colorset_init_array(colorset_t *set, uint8_t numCols, const uint32_t *cols) { + colorset_init(set); if (numCols > NUM_COLOR_SLOTS) { numCols = NUM_COLOR_SLOTS; } - for (uint8_t i = 0; i < numCols; ++i) { - addColor(RGBColor(cols[i])); + uint8_t i; + for (i = 0; i < numCols; ++i) { + rgb_color_t col; + rgb_init_from_raw(&col, cols[i]); + colorset_add_color(set, col); } } -Colorset::Colorset(const Colorset &other) : - Colorset() +void colorset_copy(colorset_t *dest, const colorset_t *src) { - // invoke = operator - *this = other; + memcpy(dest->m_palette, src->m_palette, sizeof(dest->m_palette)); + dest->m_numColors = src->m_numColors; + dest->m_curIndex = src->m_curIndex; } -Colorset::~Colorset() +uint8_t colorset_equals(const colorset_t *a, const colorset_t *b) { - clear(); + /* only compare the palettes for equality */ + return (a->m_numColors == b->m_numColors) && + (memcmp(a->m_palette, b->m_palette, a->m_numColors * sizeof(rgb_color_t)) == 0); } -bool Colorset::operator==(const Colorset &other) const +void colorset_clear(colorset_t *set) { - // only compare the palettes for equality - return (m_numColors == other.m_numColors) && - (memcmp(m_palette, other.m_palette, m_numColors * sizeof(RGBColor)) == 0); + memset((void *)set->m_palette, 0, sizeof(set->m_palette)); + set->m_numColors = 0; + colorset_reset_index(set); } -bool Colorset::operator!=(const Colorset &other) const -{ - return !operator==(other); -} - -void Colorset::init(RGBColor c1, RGBColor c2, RGBColor c3, RGBColor c4, - RGBColor c5, RGBColor c6, RGBColor c7, RGBColor c8) -{ - // clear any existing colors - clear(); - // would be nice if we could do this another way - if (!c1.empty()) addColor(c1); - if (!c2.empty()) addColor(c2); - if (!c3.empty()) addColor(c3); - if (!c4.empty()) addColor(c4); - if (!c5.empty()) addColor(c5); - if (!c6.empty()) addColor(c6); - if (!c7.empty()) addColor(c7); - if (!c8.empty()) addColor(c8); -} - -void Colorset::clear() -{ - memset((void *)m_palette, 0, sizeof(m_palette)); - m_numColors = 0; - resetIndex(); -} - -bool Colorset::equals(const Colorset &set) const -{ - return operator==(set); -} - -bool Colorset::equals(const Colorset *set) const -{ - if (!set) { - return false; - } - return operator==(*set); -} - -// crc the colorset -uint32_t Colorset::crc32() const +uint32_t colorset_crc32(const colorset_t *set) { uint32_t hash = 5381; - for (uint8_t i = 0; i < m_numColors; ++i) { - hash = ((hash << 5) + hash) + m_palette[i].raw(); + uint8_t i; + for (i = 0; i < set->m_numColors; ++i) { + hash = ((hash << 5) + hash) + rgb_raw(&set->m_palette[i]); } return hash; } -RGBColor Colorset::operator[](int index) const +rgb_color_t colorset_get_at_index(const colorset_t *set, int index) { - return get(index); + return colorset_get(set, index); } -// add a single color -bool Colorset::addColor(RGBColor col) +uint8_t colorset_add_color(colorset_t *set, rgb_color_t col) { - if (m_numColors >= NUM_COLOR_SLOTS) { - return false; + if (set->m_numColors >= NUM_COLOR_SLOTS) { + return 0; } - // insert new color and increment number of colors - m_palette[m_numColors] = col; - m_numColors++; - return true; + /* insert new color and increment number of colors */ + set->m_palette[set->m_numColors] = col; + set->m_numColors++; + return 1; } -bool Colorset::addColorHSV(uint8_t hue, uint8_t sat, uint8_t val) +uint8_t colorset_add_color_hsv(colorset_t *set, uint8_t hue, uint8_t sat, uint8_t val) { - return addColor(HSVColor(hue, sat, val)); + hsv_color_t hsv; + rgb_color_t rgb; + hsv_init3(&hsv, hue, sat, val); + rgb_init_from_hsv(&rgb, &hsv); + return colorset_add_color(set, rgb); } -void Colorset::addColorWithValueStyle(Random &ctx, uint8_t hue, uint8_t sat, ValueStyle valStyle, uint8_t numColors, uint8_t colorPos) +void colorset_add_color_with_value_style(colorset_t *set, random_t *ctx, uint8_t hue, uint8_t sat, + enum colorset_value_style valStyle, uint8_t numColors, uint8_t colorPos) { if (numColors == 1) { - addColorHSV(hue, sat, ctx.next8(16, 255)); + colorset_add_color_hsv(set, hue, sat, random_next8(ctx, 16, 255)); return; } switch (valStyle) { default: case VAL_STYLE_RANDOM: - addColorHSV(hue, sat, 85 * ctx.next8(1, 4)); + colorset_add_color_hsv(set, hue, sat, 85 * random_next8(ctx, 1, 4)); break; case VAL_STYLE_LOW_FIRST_COLOR: - if (m_numColors == 0) { - addColorHSV(hue, sat, ctx.next8(0, 86)); + if (set->m_numColors == 0) { + colorset_add_color_hsv(set, hue, sat, random_next8(ctx, 0, 86)); } else { - addColorHSV(hue, sat, 85 * ctx.next8(1, 4)); + colorset_add_color_hsv(set, hue, sat, 85 * random_next8(ctx, 1, 4)); } break; case VAL_STYLE_HIGH_FIRST_COLOR: - if (m_numColors == 0) { - addColorHSV(hue, sat, 255); + if (set->m_numColors == 0) { + colorset_add_color_hsv(set, hue, sat, 255); } else { - addColorHSV(hue, sat, ctx.next8(0, 86)); + colorset_add_color_hsv(set, hue, sat, random_next8(ctx, 0, 86)); } break; case VAL_STYLE_ALTERNATING: - if (m_numColors % 2 == 0) { - addColorHSV(hue, sat, 255); + if (set->m_numColors % 2 == 0) { + colorset_add_color_hsv(set, hue, sat, 255); } else { - addColorHSV(hue, sat, 85); + colorset_add_color_hsv(set, hue, sat, 85); } break; case VAL_STYLE_ASCENDING: - addColorHSV(hue, sat, (colorPos + 1) * (255 / numColors)); + colorset_add_color_hsv(set, hue, sat, (colorPos + 1) * (255 / numColors)); break; case VAL_STYLE_DESCENDING: - addColorHSV(hue, sat, 255 - (colorPos * (255 / numColors))); + colorset_add_color_hsv(set, hue, sat, 255 - (colorPos * (255 / numColors))); break; case VAL_STYLE_CONSTANT: - addColorHSV(hue, sat, 255); + colorset_add_color_hsv(set, hue, sat, 255); } } -void Colorset::removeColor(uint8_t index) +void colorset_remove_color(colorset_t *set, uint8_t index) { - if (index >= m_numColors) { + if (index >= set->m_numColors) { return; } - for (uint8_t i = index; i < (m_numColors - 1); ++i) { - m_palette[i] = m_palette[i + 1]; + uint8_t i; + for (i = index; i < (set->m_numColors - 1); ++i) { + set->m_palette[i] = set->m_palette[i + 1]; } - m_palette[--m_numColors].clear(); + rgb_clear(&set->m_palette[--set->m_numColors]); } -void Colorset::randomizeColors(Random &ctx, uint8_t numColors, ColorMode mode) +void colorset_randomize_colors(colorset_t *set, random_t *ctx, uint8_t numColors, enum colorset_color_mode mode) { - // if they specify randomly pick the color mode then roll it + /* if they specify randomly pick the color mode then roll it */ if (mode >= COLOR_MODE_RANDOMLY_PICK) { - mode = (ColorMode)(ctx.next8() % COLOR_MODE_COUNT); + mode = (enum colorset_color_mode)(random_next8(ctx, 0, 255) % COLOR_MODE_COUNT); } - clear(); + colorset_clear(set); if (!numColors) { - numColors = ctx.next8(mode == COLOR_MODE_MONOCHROMATIC ? 2 : 1, 9); + numColors = random_next8(ctx, mode == COLOR_MODE_MONOCHROMATIC ? 2 : 1, 9); } - uint8_t randomizedHue = ctx.next8(); + uint8_t randomizedHue = random_next8(ctx, 0, 255); uint8_t colorGap = 0; if (mode == COLOR_MODE_COLOR_THEORY && numColors > 1) { - colorGap = ctx.next8(16, 256 / (numColors - 1)); + colorGap = random_next8(ctx, 16, 256 / (numColors - 1)); } - ValueStyle valStyle = (ValueStyle)ctx.next8(0, VAL_STYLE_COUNT); - // the doubleStyle decides if some colors are added to the set twice + enum colorset_value_style valStyle = (enum colorset_value_style)random_next8(ctx, 0, VAL_STYLE_COUNT); + /* the doubleStyle decides if some colors are added to the set twice */ uint8_t doubleStyle = 0; if (numColors <= 7) { - doubleStyle = (ctx.next8(0, 1)); + doubleStyle = random_next8(ctx, 0, 1); } if (numColors <= 4) { - doubleStyle = (ctx.next8(0, 2)); + doubleStyle = random_next8(ctx, 0, 2); } - for (uint8_t i = 0; i < numColors; i++) { + uint8_t i; + for (i = 0; i < numColors; i++) { uint8_t hueToUse; uint8_t valueToUse = 255; if (mode == COLOR_MODE_COLOR_THEORY) { @@ -213,153 +190,176 @@ void Colorset::randomizeColors(Random &ctx, uint8_t numColors, ColorMode mode) } else if (mode == COLOR_MODE_MONOCHROMATIC) { hueToUse = randomizedHue; valueToUse = 255 - (i * (256 / numColors)); - } else { // EVENLY_SPACED + } else { /* EVENLY_SPACED */ hueToUse = (randomizedHue + (256 / numColors) * i); } - addColorWithValueStyle(ctx, hueToUse, valueToUse, valStyle, numColors, i); - // double all colors or only first color + colorset_add_color_with_value_style(set, ctx, hueToUse, valueToUse, valStyle, numColors, i); + /* double all colors or only first color */ if (doubleStyle == 2 || (doubleStyle == 1 && !i)) { - addColorWithValueStyle(ctx, hueToUse, valueToUse, valStyle, numColors, i); + colorset_add_color_with_value_style(set, ctx, hueToUse, valueToUse, valStyle, numColors, i); } } } -void Colorset::adjustBrightness(uint8_t fadeby) +void colorset_adjust_brightness(colorset_t *set, uint8_t fadeby) { - for (uint8_t i = 0; i < m_numColors; ++i) { - m_palette[i].adjustBrightness(fadeby); + uint8_t i; + for (i = 0; i < set->m_numColors; ++i) { + rgb_adjust_brightness(&set->m_palette[i], fadeby); } } -// get a color from the colorset -RGBColor Colorset::get(uint8_t index) const +rgb_color_t colorset_get(const colorset_t *set, uint8_t index) { - if (index >= m_numColors) { - return RGBColor(0, 0, 0); + rgb_color_t result; + if (index >= set->m_numColors) { + rgb_init3(&result, 0, 0, 0); + return result; } - return m_palette[index]; + return set->m_palette[index]; } -// set an rgb color in a slot, or add a new color if you specify -// a slot higher than the number of colors in the colorset -void Colorset::set(uint8_t index, RGBColor col) +void colorset_set(colorset_t *set, uint8_t index, rgb_color_t col) { - // special case for 'setting' a color at the edge of the palette, - // ie adding a new color when you set an index higher than the max - if (index >= m_numColors) { - if (!addColor(col)) { - //ERROR_LOGF("Failed to add new color at index %u", index); + /* special case for 'setting' a color at the edge of the palette, + * ie adding a new color when you set an index higher than the max */ + if (index >= set->m_numColors) { + if (!colorset_add_color(set, col)) { + /* ERROR_LOGF("Failed to add new color at index %u", index); */ } return; } - m_palette[index] = col; + set->m_palette[index] = col; } -// skip some amount of colors -void Colorset::skip(int32_t amount) +void colorset_skip(colorset_t *set, int32_t amount) { - if (!m_numColors) { + if (!set->m_numColors) { return; } - // if the colorset hasn't started yet - if (m_curIndex == INDEX_INVALID) { - m_curIndex = 0; + /* if the colorset hasn't started yet */ + if (set->m_curIndex == INDEX_INVALID) { + set->m_curIndex = 0; } - // first modulate the amount to skip to be within +/- the number of colors - amount %= (int32_t)m_numColors; + /* first modulate the amount to skip to be within +/- the number of colors */ + amount %= (int32_t)set->m_numColors; - // max = 3 - // m_curIndex = 2 - // amount = -10 - m_curIndex = ((int32_t)m_curIndex + (int32_t)amount) % (int32_t)m_numColors; - if (m_curIndex > m_numColors) { // must have wrapped - // simply wrap it back - m_curIndex += m_numColors; + /* max = 3 + * m_curIndex = 2 + * amount = -10 */ + set->m_curIndex = ((int32_t)set->m_curIndex + (int32_t)amount) % (int32_t)set->m_numColors; + if (set->m_curIndex > set->m_numColors) { /* must have wrapped */ + /* simply wrap it back */ + set->m_curIndex += set->m_numColors; } } -RGBColor Colorset::cur() +rgb_color_t colorset_cur(const colorset_t *set) { - if (m_curIndex >= m_numColors) { - return RGBColor(0, 0, 0); + rgb_color_t result; + if (set->m_curIndex >= set->m_numColors) { + rgb_init3(&result, 0, 0, 0); + return result; } - return m_palette[m_curIndex]; + return set->m_palette[set->m_curIndex]; } -void Colorset::setCurIndex(uint8_t index) +void colorset_set_cur_index(colorset_t *set, uint8_t index) { - if (!m_numColors) { + if (!set->m_numColors) { return; } - if (index > (m_numColors - 1)) { + if (index > (set->m_numColors - 1)) { return; } - m_curIndex = index; + set->m_curIndex = index; } -void Colorset::resetIndex() +void colorset_reset_index(colorset_t *set) { - m_curIndex = INDEX_INVALID; + set->m_curIndex = INDEX_INVALID; } -RGBColor Colorset::getPrev() +uint8_t colorset_cur_index(const colorset_t *set) { - if (!m_numColors) { - return RGB_OFF; + return set->m_curIndex; +} + +rgb_color_t colorset_get_prev(colorset_t *set) +{ + rgb_color_t result; + if (!set->m_numColors) { + rgb_init_from_raw(&result, RGB_OFF); + return result; } - // handle wrapping at 0 - if (m_curIndex == 0 || m_curIndex == INDEX_INVALID) { - m_curIndex = numColors() - 1; + /* handle wrapping at 0 */ + if (set->m_curIndex == 0 || set->m_curIndex == INDEX_INVALID) { + set->m_curIndex = colorset_num_colors(set) - 1; } else { - m_curIndex--; + set->m_curIndex--; } - // return the color - return m_palette[m_curIndex]; + /* return the color */ + return set->m_palette[set->m_curIndex]; } -RGBColor Colorset::getNext() +rgb_color_t colorset_get_next(colorset_t *set) { - if (!m_numColors) { - return RGB_OFF; + rgb_color_t result; + if (!set->m_numColors) { + rgb_init_from_raw(&result, RGB_OFF); + return result; } - // iterate current index, let it wrap at max uint8 - m_curIndex++; - // then modulate the result within max colors - m_curIndex %= numColors(); - // return the color - return m_palette[m_curIndex]; + /* iterate current index, let it wrap at max uint8 */ + set->m_curIndex++; + /* then modulate the result within max colors */ + set->m_curIndex %= colorset_num_colors(set); + /* return the color */ + return set->m_palette[set->m_curIndex]; } -// peek at the next color but don't iterate -RGBColor Colorset::peek(int32_t offset) const +rgb_color_t colorset_peek(const colorset_t *set, int32_t offset) { - if (!m_numColors) { - return RGB_OFF; + rgb_color_t result; + if (!set->m_numColors) { + rgb_init_from_raw(&result, RGB_OFF); + return result; } uint8_t nextIndex = 0; - // get index of the next color + /* get index of the next color */ if (offset >= 0) { - nextIndex = (m_curIndex + offset) % numColors(); + nextIndex = (set->m_curIndex + offset) % colorset_num_colors(set); } else { - if (offset < -1 * (int32_t)(numColors())) { - return RGB_OFF; + if (offset < -1 * (int32_t)(colorset_num_colors(set))) { + rgb_init_from_raw(&result, RGB_OFF); + return result; } - nextIndex = ((m_curIndex + numColors()) + (int)offset) % numColors(); + nextIndex = ((set->m_curIndex + colorset_num_colors(set)) + (int)offset) % colorset_num_colors(set); } - // return the color - return m_palette[nextIndex]; + /* return the color */ + return set->m_palette[nextIndex]; } -bool Colorset::onStart() const +rgb_color_t colorset_peek_next(const colorset_t *set) { - return (m_curIndex == 0); + return colorset_peek(set, 1); } -bool Colorset::onEnd() const +uint8_t colorset_num_colors(const colorset_t *set) { - if (!m_numColors) { - return false; + return set->m_numColors; +} + +uint8_t colorset_on_start(const colorset_t *set) +{ + return (set->m_curIndex == 0); +} + +uint8_t colorset_on_end(const colorset_t *set) +{ + if (!set->m_numColors) { + return 0; } - return (m_curIndex == m_numColors - 1); + return (set->m_curIndex == set->m_numColors - 1); } + diff --git a/Helios/Colorset.h b/Helios/Colorset.h index 4c6aeafe..b8c2408a 100644 --- a/Helios/Colorset.h +++ b/Helios/Colorset.h @@ -5,139 +5,132 @@ #include "HeliosConfig.h" -class Random; +/* Forward declaration */ +typedef struct random_t random_t; +typedef struct colorset_t colorset_t; -class Colorset +enum colorset_value_style { -public: - // empty colorset - Colorset(); - // constructor for 1-8 color slots - Colorset(RGBColor c1, RGBColor c2 = RGB_OFF, RGBColor c3 = RGB_OFF, - RGBColor c4 = RGB_OFF, RGBColor c5 = RGB_OFF, RGBColor c6 = RGB_OFF, - RGBColor c7 = RGB_OFF, RGBColor c8 = RGB_OFF); - Colorset(uint8_t numCols, const uint32_t *cols); - ~Colorset(); - - // copy and assignment operators - Colorset(const Colorset &other); - - // equality operators - bool operator==(const Colorset &other) const; - bool operator!=(const Colorset &other) const; - - // initialize the colorset - void init(RGBColor c1 = RGB_OFF, RGBColor c2 = RGB_OFF, RGBColor c3 = RGB_OFF, - RGBColor c4 = RGB_OFF, RGBColor c5 = RGB_OFF, RGBColor c6 = RGB_OFF, - RGBColor c7 = RGB_OFF, RGBColor c8 = RGB_OFF); - - // clear the colorset - void clear(); - - // pointer comparison - bool equals(const Colorset &set) const; - bool equals(const Colorset *set) const; - - // crc the colorset - uint32_t crc32() const; - - // index operator to access color index - RGBColor operator[](int index) const; - - enum ValueStyle : uint8_t - { - // Random values - VAL_STYLE_RANDOM = 0, - // First color low value, the rest are random - VAL_STYLE_LOW_FIRST_COLOR, - // First color high value, the rest are low - VAL_STYLE_HIGH_FIRST_COLOR, - // Alternat between high and low value - VAL_STYLE_ALTERNATING, - // Ascending values from low to high - VAL_STYLE_ASCENDING, - // Descending values from high to low - VAL_STYLE_DESCENDING, - // Constant value - VAL_STYLE_CONSTANT, - // Total number of value styles - VAL_STYLE_COUNT - }; - - // 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, - ValueStyle valStyle, uint8_t numColors, uint8_t colorPos); - void removeColor(uint8_t index); - - // various modes of randomization types to use with randomizeColors - enum ColorMode { - // randomize with color theory - COLOR_MODE_COLOR_THEORY, - // randomize a nonochromatic set - COLOR_MODE_MONOCHROMATIC, - // randomize an evenly spaced hue set - COLOR_MODE_EVENLY_SPACED, - - // total different randomize modes above - COLOR_MODE_COUNT, - - // EXTRA OPTION: randomly pick one of the other 3 options - COLOR_MODE_RANDOMLY_PICK = COLOR_MODE_COUNT, - }; - // function to randomize the colors with various different modes of randomization - void randomizeColors(Random &ctx, uint8_t numColors, ColorMode color_mode); - - // fade all of the colors in the set - void adjustBrightness(uint8_t fadeby); - - // get a color from the colorset - RGBColor get(uint8_t index = 0) const; - - // set an rgb color in a slot, or add a new color if you specify - // a slot higher than the number of colors in the colorset - void set(uint8_t index, RGBColor col); - - // skip some amount of colors - void skip(int32_t amount = 1); - - // get current color in cycle - RGBColor cur(); - - // set the current index of the colorset - void setCurIndex(uint8_t index); - void resetIndex(); - - // the current index - uint8_t curIndex() const { return m_curIndex; } - - // get the prev color in cycle - RGBColor getPrev(); - - // get the next color in cycle - RGBColor getNext(); - - // peek at the color indexes from current but don't iterate - RGBColor peek(int32_t offset) const; - - // better wording for peek 1 ahead - RGBColor peekNext() const { return peek(1); } - - // the number of colors in the palette - uint8_t numColors() const { return m_numColors; } - - // whether the colorset is currently on the first color or last color - bool onStart() const; - bool onEnd() const; -private: - // palette of colors - RGBColor m_palette[NUM_COLOR_SLOTS]; - // the actual number of colors in the set + /* Random values */ + VAL_STYLE_RANDOM = 0, + /* First color low value, the rest are random */ + VAL_STYLE_LOW_FIRST_COLOR, + /* First color high value, the rest are low */ + VAL_STYLE_HIGH_FIRST_COLOR, + /* Alternate between high and low value */ + VAL_STYLE_ALTERNATING, + /* Ascending values from low to high */ + VAL_STYLE_ASCENDING, + /* Descending values from high to low */ + VAL_STYLE_DESCENDING, + /* Constant value */ + VAL_STYLE_CONSTANT, + /* Total number of value styles */ + VAL_STYLE_COUNT +}; + +enum colorset_color_mode +{ + /* randomize with color theory */ + COLOR_MODE_COLOR_THEORY, + /* randomize a monochromatic set */ + COLOR_MODE_MONOCHROMATIC, + /* randomize an evenly spaced hue set */ + COLOR_MODE_EVENLY_SPACED, + + /* total different randomize modes above */ + COLOR_MODE_COUNT, + + /* EXTRA OPTION: randomly pick one of the other 3 options */ + COLOR_MODE_RANDOMLY_PICK = COLOR_MODE_COUNT, +}; + +struct colorset_t +{ + /* palette of colors */ + rgb_color_t m_palette[NUM_COLOR_SLOTS]; + /* the actual number of colors in the set */ uint8_t m_numColors; - // the current index, starts at UINT8_MAX so that - // the very first call to getNext will iterate to 0 + /* the current index, starts at 255 so that + * the very first call to colorset_getNext will iterate to 0 */ uint8_t m_curIndex; }; +/* Empty colorset */ +void colorset_init(colorset_t *set); + +/* Initialize with up to 8 colors */ +void colorset_init_multi(colorset_t *set, rgb_color_t c1, rgb_color_t c2, rgb_color_t c3, + rgb_color_t c4, rgb_color_t c5, rgb_color_t c6, rgb_color_t c7, rgb_color_t c8); + +/* Initialize from array of colors */ +void colorset_init_array(colorset_t *set, uint8_t numCols, const uint32_t *cols); + +/* Copy colorset */ +void colorset_copy(colorset_t *dest, const colorset_t *src); + +/* Equality operators */ +uint8_t colorset_equals(const colorset_t *a, const colorset_t *b); + +/* Clear the colorset */ +void colorset_clear(colorset_t *set); + +/* CRC the colorset */ +uint32_t colorset_crc32(const colorset_t *set); + +/* Index operator to access color index */ +rgb_color_t colorset_get_at_index(const colorset_t *set, int index); + +/* Add a single color */ +uint8_t colorset_add_color(colorset_t *set, rgb_color_t col); +uint8_t colorset_add_color_hsv(colorset_t *set, uint8_t hue, uint8_t sat, uint8_t val); +void colorset_add_color_with_value_style(colorset_t *set, random_t *ctx, uint8_t hue, uint8_t sat, + enum colorset_value_style valStyle, uint8_t numColors, uint8_t colorPos); +void colorset_remove_color(colorset_t *set, uint8_t index); + +/* Function to randomize the colors with various different modes of randomization */ +void colorset_randomize_colors(colorset_t *set, random_t *ctx, uint8_t numColors, enum colorset_color_mode color_mode); + +/* Fade all of the colors in the set */ +void colorset_adjust_brightness(colorset_t *set, uint8_t fadeby); + +/* Get a color from the colorset */ +rgb_color_t colorset_get(const colorset_t *set, uint8_t index); + +/* Set an rgb color in a slot, or add a new color if you specify + * a slot higher than the number of colors in the colorset */ +void colorset_set(colorset_t *set, uint8_t index, rgb_color_t col); + +/* Skip some amount of colors */ +void colorset_skip(colorset_t *set, int32_t amount); + +/* Get current color in cycle */ +rgb_color_t colorset_cur(const colorset_t *set); + +/* Set the current index of the colorset */ +void colorset_set_cur_index(colorset_t *set, uint8_t index); +void colorset_reset_index(colorset_t *set); + +/* The current index */ +uint8_t colorset_cur_index(const colorset_t *set); + +/* Get the prev color in cycle */ +rgb_color_t colorset_get_prev(colorset_t *set); + +/* Get the next color in cycle */ +rgb_color_t colorset_get_next(colorset_t *set); + +/* Peek at the color indexes from current but don't iterate */ +rgb_color_t colorset_peek(const colorset_t *set, int32_t offset); + +/* Better wording for peek 1 ahead */ +rgb_color_t colorset_peek_next(const colorset_t *set); + +/* The number of colors in the palette */ +uint8_t colorset_num_colors(const colorset_t *set); + +/* Whether the colorset is currently on the first color or last color */ +uint8_t colorset_on_start(const colorset_t *set); +uint8_t colorset_on_end(const colorset_t *set); + #endif diff --git a/Helios/Colortypes.cpp b/Helios/Colortypes.cpp index 8039c446..be745d7f 100644 --- a/Helios/Colortypes.cpp +++ b/Helios/Colortypes.cpp @@ -1,236 +1,256 @@ #include "Colortypes.h" #if ALTERNATIVE_HSV_RGB == 1 -// global hsv to rgb algorithm selector -hsv_to_rgb_algorithm g_hsv_rgb_alg = HSV_TO_RGB_GENERIC; +/* global hsv to rgb algorithm selector */ +enum hsv_to_rgb_algorithm g_hsv_rgb_alg = HSV_TO_RGB_GENERIC; #endif -HSVColor::HSVColor() : - hue(0), - sat(0), - val(0) +/* ========== HSVColor functions ========== */ + +void hsv_init(hsv_color_t *hsv) +{ + hsv->hue = 0; + hsv->sat = 0; + hsv->val = 0; +} + +void hsv_init3(hsv_color_t *hsv, uint8_t hue, uint8_t sat, uint8_t val) { + hsv->hue = hue; + hsv->sat = sat; + hsv->val = val; } -HSVColor::HSVColor(uint8_t hue, uint8_t sat, uint8_t val) : - hue(hue), sat(sat), val(val) +void hsv_init_from_raw(hsv_color_t *hsv, uint32_t dwVal) { + hsv_init(hsv); + hsv_assign_from_raw(hsv, dwVal); } -HSVColor::HSVColor(uint32_t dwVal) : - HSVColor() +void hsv_init_from_rgb(hsv_color_t *hsv, const rgb_color_t *rgb) { - *this = dwVal; + hsv_assign_from_rgb(hsv, rgb); } -// assignment from uint32_t -HSVColor &HSVColor::operator=(const uint32_t &rhs) +void hsv_copy(hsv_color_t *dest, const hsv_color_t *src) { - hue = ((rhs >> 16) & 0xFF); - sat = ((rhs >> 8) & 0xFF); - val = (rhs & 0xFF); - return *this; + dest->hue = src->hue; + dest->sat = src->sat; + dest->val = src->val; } -// construction/assignment from RGB -HSVColor::HSVColor(const RGBColor &rhs) +void hsv_assign_from_raw(hsv_color_t *hsv, uint32_t rhs) { - *this = rhs; + hsv->hue = ((rhs >> 16) & 0xFF); + hsv->sat = ((rhs >> 8) & 0xFF); + hsv->val = (rhs & 0xFF); } -HSVColor &HSVColor::operator=(const RGBColor &rhs) +void hsv_assign_from_rgb(hsv_color_t *hsv, const rgb_color_t *rhs) { - // always use generic - *this = rgb_to_hsv_generic(rhs); - return *this; + /* always use generic */ + hsv_color_t temp = rgb_to_hsv_generic(rhs); + hsv_copy(hsv, &temp); } -bool HSVColor::operator==(const HSVColor &other) const +uint8_t hsv_equals(const hsv_color_t *a, const hsv_color_t *b) { - return (other.raw() == raw()); + return (hsv_raw(b) == hsv_raw(a)); } -bool HSVColor::operator!=(const HSVColor &other) const +uint8_t hsv_empty(const hsv_color_t *hsv) { - return (other.raw() != raw()); + return !hsv->hue && !hsv->sat && !hsv->val; } -bool HSVColor::empty() const +void hsv_clear(hsv_color_t *hsv) { - return !hue && !sat && !val; + hsv->hue = 0; + hsv->sat = 0; + hsv->val = 0; } -void HSVColor::clear() +uint32_t hsv_raw(const hsv_color_t *hsv) { - hue = 0; - sat = 0; - val = 0; + return ((uint32_t)hsv->hue << 16) | ((uint32_t)hsv->sat << 8) | (uint32_t)hsv->val; } -// ========== -// RGBColor +/* ========== RGBColor functions ========== */ -RGBColor::RGBColor() : - red(0), - green(0), - blue(0) +void rgb_init(rgb_color_t *rgb) { + rgb->red = 0; + rgb->green = 0; + rgb->blue = 0; } -RGBColor::RGBColor(uint8_t red, uint8_t green, uint8_t blue) : - red(red), green(green), blue(blue) +void rgb_init3(rgb_color_t *rgb, uint8_t red, uint8_t green, uint8_t blue) { + rgb->red = red; + rgb->green = green; + rgb->blue = blue; } -RGBColor::RGBColor(uint32_t dwVal) : - RGBColor() +void rgb_init_from_raw(rgb_color_t *rgb, uint32_t dwVal) { - *this = dwVal; + rgb_init(rgb); + rgb_assign_from_raw(rgb, dwVal); } -// assignment from uint32_t -RGBColor &RGBColor::operator=(const uint32_t &rhs) +void rgb_init_from_hsv(rgb_color_t *rgb, const hsv_color_t *hsv) { - red = ((rhs >> 16) & 0xFF); - green = ((rhs >> 8) & 0xFF); - blue = (rhs & 0xFF); - return *this; + rgb_assign_from_hsv(rgb, hsv); } -RGBColor::RGBColor(const HSVColor &rhs) +void rgb_copy(rgb_color_t *dest, const rgb_color_t *src) { - *this = rhs; + dest->red = src->red; + dest->green = src->green; + dest->blue = src->blue; } -RGBColor &RGBColor::operator=(const HSVColor &rhs) +void rgb_assign_from_raw(rgb_color_t *rgb, uint32_t rhs) +{ + rgb->red = ((rhs >> 16) & 0xFF); + rgb->green = ((rhs >> 8) & 0xFF); + rgb->blue = (rhs & 0xFF); +} + +void rgb_assign_from_hsv(rgb_color_t *rgb, const hsv_color_t *rhs) { #if ALTERNATIVE_HSV_RGB == 1 + rgb_color_t temp; switch (g_hsv_rgb_alg) { case HSV_TO_RGB_RAINBOW: - *this = hsv_to_rgb_rainbow(rhs); + temp = hsv_to_rgb_rainbow(rhs); break; case HSV_TO_RGB_GENERIC: - *this = hsv_to_rgb_generic(rhs); + temp = hsv_to_rgb_generic(rhs); break; } + rgb_copy(rgb, &temp); #else - *this = hsv_to_rgb_generic(rhs); + rgb_color_t temp = hsv_to_rgb_generic(rhs); + rgb_copy(rgb, &temp); #endif - return *this; } -bool RGBColor::operator==(const RGBColor &other) const +uint8_t rgb_equals(const rgb_color_t *a, const rgb_color_t *b) { - return (other.raw() == raw()); + return (rgb_raw(b) == rgb_raw(a)); } -bool RGBColor::operator!=(const RGBColor &other) const +uint8_t rgb_empty(const rgb_color_t *rgb) { - return (other.raw() != raw()); + return !rgb->red && !rgb->green && !rgb->blue; } -bool RGBColor::empty() const +void rgb_clear(rgb_color_t *rgb) { - return !red && !green && !blue; + rgb->red = 0; + rgb->green = 0; + rgb->blue = 0; } -void RGBColor::clear() +/* scale down the brightness of a color by some fade amount */ +void rgb_adjust_brightness(rgb_color_t *rgb, uint8_t fadeBy) { - red = 0; - green = 0; - blue = 0; + rgb->red = (((int)rgb->red) * (int)(256 - fadeBy)) >> 8; + rgb->green = (((int)rgb->green) * (int)(256 - fadeBy)) >> 8; + rgb->blue = (((int)rgb->blue) * (int)(256 - fadeBy)) >> 8; } -// scale down the brightness of a color by some fade amount -RGBColor RGBColor::adjustBrightness(uint8_t fadeBy) +uint32_t rgb_raw(const rgb_color_t *rgb) { - red = (((int)red) * (int)(256 - fadeBy)) >> 8; - green = (((int)green) * (int)(256 - fadeBy)) >> 8; - blue = (((int)blue) * (int)(256 - fadeBy)) >> 8; - return *this; + return ((uint32_t)rgb->red << 16) | ((uint32_t)rgb->green << 8) | (uint32_t)rgb->blue; } #ifdef HELIOS_CLI -// Adjust brightness to ensure visibility on screens, without floating-point arithmetic and without using max function -RGBColor RGBColor::bringUpBrightness(uint8_t min_brightness) { - // Adjust each color component using FSCALE8 and ensure it doesn't drop below MIN_BRIGHTNESS - HSVColor col = *this; +/* Adjust brightness to ensure visibility on screens, without floating-point arithmetic */ +void rgb_bring_up_brightness(rgb_color_t *rgb, uint8_t min_brightness) +{ + hsv_color_t col; + hsv_init_from_rgb(&col, rgb); if (col.val == 0) { - return col; + rgb_assign_from_hsv(rgb, &col); + return; } if (col.val < min_brightness) { col.val = min_brightness; } - return col; + rgb_assign_from_hsv(rgb, &col); } -// scale a uint8 by a float value, don't use this on embedded! + +/* scale a uint8 by a float value, don't use this on embedded! */ #define FSCALE8(x, scale) (uint8_t)(((float)x * scale) > 255 ? 255 : ((float)x * scale)) -// return a scaled up the brightness version of the current color -RGBColor RGBColor::scaleBrightness(float scale) + +/* return a scaled up the brightness version of the current color */ +void rgb_scale_brightness(rgb_color_t *rgb, float scale) { - // scale each color up by the given amount and return a new color - return RGBColor(FSCALE8(red, scale), FSCALE8(green, scale), FSCALE8(blue, scale)); + rgb->red = FSCALE8(rgb->red, scale); + rgb->green = FSCALE8(rgb->green, scale); + rgb->blue = FSCALE8(rgb->blue, scale); } #endif -// ======================================================== -// Below are various functions for converting hsv <-> rgb + +/* ======================================================== + * Below are various functions for converting hsv <-> rgb */ #if ALTERNATIVE_HSV_RGB == 1 #define SCALE8(i, scale) (((uint16_t)i * (uint16_t)(scale)) >> 8) #define FIXFRAC8(N,D) (((N)*256)/(D)) -// Stolen from FastLED hsv to rgb full rainbox where all colours -// are given equal weight, this makes for-example yellow larger -// best to use this function as it is the legacy choice -RGBColor hsv_to_rgb_rainbow(const HSVColor &rhs) +/* Stolen from FastLED hsv to rgb full rainbow where all colours + * are given equal weight, this makes for-example yellow larger + * best to use this function as it is the legacy choice */ +rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) { - RGBColor col; - // Yellow has a higher inherent brightness than - // any other color; 'pure' yellow is perceived to - // be 93% as bright as white. In order to make - // yellow appear the correct relative brightness, - // it has to be rendered brighter than all other - // colors. - // Level Y1 is a moderate boost, the default. - // Level Y2 is a strong boost. + rgb_color_t col; + /* Yellow has a higher inherent brightness than + * any other color; 'pure' yellow is perceived to + * be 93% as bright as white. In order to make + * yellow appear the correct relative brightness, + * it has to be rendered brighter than all other + * colors. + * Level Y1 is a moderate boost, the default. + * Level Y2 is a strong boost. */ const uint8_t Y1 = 1; const uint8_t Y2 = 0; - // G2: Whether to divide all greens by two. - // Depends GREATLY on your particular LEDs + /* G2: Whether to divide all greens by two. + * Depends GREATLY on your particular LEDs */ const uint8_t G2 = 0; - // Gscale: what to scale green down by. - // Depends GREATLY on your particular LEDs + /* Gscale: what to scale green down by. + * Depends GREATLY on your particular LEDs */ const uint8_t Gscale = 185; + uint8_t hue = rhs->hue; + uint8_t sat = rhs->sat; + uint8_t val = rhs->val; - uint8_t hue = rhs.hue; - uint8_t sat = rhs.sat; - uint8_t val = rhs.val; - - uint8_t offset = hue & 0x1F; // 0..31 + uint8_t offset = hue & 0x1F; /* 0..31 */ - // offset8 = offset * 8 + /* offset8 = offset * 8 */ uint8_t offset8 = offset; offset8 <<= 3; - uint8_t third = SCALE8(offset8, (256 / 3)); // max = 85 + uint8_t third = SCALE8(offset8, (256 / 3)); /* max = 85 */ uint8_t r, g, b; if (!(hue & 0x80)) { - // 0XX + /* 0XX */ if (!(hue & 0x40)) { - // 00X - //section 0-1 + /* 00X */ + /* section 0-1 */ if (!(hue & 0x20)) { - // 000 - //case 0: // R -> O + /* 000 */ + /* case 0: R -> O */ r = 255 - third; g = third; b = 0; } else { - // 001 - //case 1: // O -> Y + /* 001 */ + /* case 1: O -> Y */ if (Y1) { r = 171; g = 85 + third; @@ -238,21 +258,21 @@ RGBColor hsv_to_rgb_rainbow(const HSVColor &rhs) } if (Y2) { r = 170 + third; - //uint8_t twothirds = (third << 1); - uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); // max=170 + /* uint8_t twothirds = (third << 1); */ + uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); /* max=170 */ g = 85 + twothirds; b = 0; } } } else { - //01X - // section 2-3 + /* 01X */ + /* section 2-3 */ if (!(hue & 0x20)) { - // 010 - //case 2: // Y -> G + /* 010 */ + /* case 2: Y -> G */ if (Y1) { - //uint8_t twothirds = (third << 1); - uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); // max=170 + /* uint8_t twothirds = (third << 1); */ + uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); /* max=170 */ r = 171 - twothirds; g = 170 + third; b = 0; @@ -263,43 +283,43 @@ RGBColor hsv_to_rgb_rainbow(const HSVColor &rhs) b = 0; } } else { - // 011 - // case 3: // G -> A + /* 011 */ + /* case 3: G -> A */ r = 0; g = 255 - third; b = third; } } } else { - // section 4-7 - // 1XX + /* section 4-7 */ + /* 1XX */ if (!(hue & 0x40)) { - // 10X + /* 10X */ if (!(hue & 0x20)) { - // 100 - //case 4: // A -> B + /* 100 */ + /* case 4: A -> B */ r = 0; - //uint8_t twothirds = (third << 1); - uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); // max=170 - g = 171 - twothirds; //170? + /* uint8_t twothirds = (third << 1); */ + uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); /* max=170 */ + g = 171 - twothirds; /* 170? */ b = 85 + twothirds; } else { - // 101 - //case 5: // B -> P + /* 101 */ + /* case 5: B -> P */ r = third; g = 0; b = 255 - third; } } else { if (!(hue & 0x20)) { - // 110 - //case 6: // P -- K + /* 110 */ + /* case 6: P -- K */ r = 85 + third; g = 0; b = 171 - third; } else { - // 111 - //case 7: // K -> R + /* 111 */ + /* case 7: K -> R */ r = 170 + third; g = 0; b = 85 - third; @@ -307,13 +327,13 @@ RGBColor hsv_to_rgb_rainbow(const HSVColor &rhs) } } - // This is one of the good places to scale the green down, - // although the client can scale green down as well. + /* This is one of the good places to scale the green down, + * although the client can scale green down as well. */ if (G2) g = g >> 1; if (Gscale) g = SCALE8(g, Gscale); - // Scale down colors if we're desaturated at all - // and add the brightness_floor to r, g, and b. + /* Scale down colors if we're desaturated at all + * and add the brightness_floor to r, g, and b. */ if (sat != 255) { if (sat == 0) { r = 255; b = 255; g = 255; @@ -332,23 +352,23 @@ RGBColor hsv_to_rgb_rainbow(const HSVColor &rhs) } } - // Now scale everything down if we're at value < 255. + /* Now scale everything down if we're at value < 255. */ if (val != 255) { val = SCALE8(val, val); if (val == 0) { r = 0; g = 0; b = 0; } else { - // nSCALE8x3_video( r, g, b, val); + /* nSCALE8x3_video( r, g, b, val); */ if (r) r = SCALE8(r, val) + 1; if (g) g = SCALE8(g, val) + 1; if (b) b = SCALE8(b, val) + 1; } } - // Here we have the old AVR "missing std X+n" problem again - // It turns out that fixing it winds up costing more than - // not fixing it. - // To paraphrase Dr Bronner, profile! profile! profile! + /* Here we have the old AVR "missing std X+n" problem again + * It turns out that fixing it winds up costing more than + * not fixing it. + * To paraphrase Dr Bronner, profile! profile! profile! */ col.red = r; col.green = g; col.blue = b; @@ -356,57 +376,57 @@ RGBColor hsv_to_rgb_rainbow(const HSVColor &rhs) } #endif -// generic hsv to rgb conversion nothing special -RGBColor hsv_to_rgb_generic(const HSVColor &rhs) +/* generic hsv to rgb conversion nothing special */ +rgb_color_t hsv_to_rgb_generic(const hsv_color_t *rhs) { unsigned char region, remainder, p, q, t; - RGBColor col; + rgb_color_t col; - if (rhs.sat == 0) { - col.red = rhs.val; - col.green = rhs.val; - col.blue = rhs.val; + if (rhs->sat == 0) { + col.red = rhs->val; + col.green = rhs->val; + col.blue = rhs->val; return col; } - region = rhs.hue / 43; - remainder = ((rhs.hue - (region * 43)) * 6); + region = rhs->hue / 43; + remainder = ((rhs->hue - (region * 43)) * 6); - // extraneous casts to uint16_t are to prevent overflow - p = (uint8_t)(((uint16_t)(rhs.val) * (255 - rhs.sat)) >> 8); - q = (uint8_t)(((uint16_t)(rhs.val) * (255 - (((uint16_t)(rhs.sat) * remainder) >> 8))) >> 8); - t = (uint8_t)(((uint16_t)(rhs.val) * (255 - (((uint16_t)(rhs.sat) * (255 - remainder)) >> 8))) >> 8); + /* extraneous casts to uint16_t are to prevent overflow */ + p = (uint8_t)(((uint16_t)(rhs->val) * (255 - rhs->sat)) >> 8); + q = (uint8_t)(((uint16_t)(rhs->val) * (255 - (((uint16_t)(rhs->sat) * remainder) >> 8))) >> 8); + t = (uint8_t)(((uint16_t)(rhs->val) * (255 - (((uint16_t)(rhs->sat) * (255 - remainder)) >> 8))) >> 8); switch (region) { case 0: - col.red = rhs.val; col.green = t; col.blue = p; + col.red = rhs->val; col.green = t; col.blue = p; break; case 1: - col.red = q; col.green = rhs.val; col.blue = p; + col.red = q; col.green = rhs->val; col.blue = p; break; case 2: - col.red = p; col.green = rhs.val; col.blue = t; + col.red = p; col.green = rhs->val; col.blue = t; break; case 3: - col.red = p; col.green = q; col.blue = rhs.val; + col.red = p; col.green = q; col.blue = rhs->val; break; case 4: - col.red = t; col.green = p; col.blue = rhs.val; + col.red = t; col.green = p; col.blue = rhs->val; break; default: - col.red = rhs.val; col.green = p; col.blue = q; + col.red = rhs->val; col.green = p; col.blue = q; break; } return col; } -// Convert rgb to hsv with generic fast method -HSVColor rgb_to_hsv_generic(const RGBColor &rhs) +/* Convert rgb to hsv with generic fast method */ +hsv_color_t rgb_to_hsv_generic(const rgb_color_t *rhs) { unsigned char rgbMin, rgbMax; - rgbMin = rhs.red < rhs.green ? (rhs.red < rhs.blue ? rhs.red : rhs.blue) : (rhs.green < rhs.blue ? rhs.green : rhs.blue); - rgbMax = rhs.red > rhs.green ? (rhs.red > rhs.blue ? rhs.red : rhs.blue) : (rhs.green > rhs.blue ? rhs.green : rhs.blue); - HSVColor hsv; + rgbMin = rhs->red < rhs->green ? (rhs->red < rhs->blue ? rhs->red : rhs->blue) : (rhs->green < rhs->blue ? rhs->green : rhs->blue); + rgbMax = rhs->red > rhs->green ? (rhs->red > rhs->blue ? rhs->red : rhs->blue) : (rhs->green > rhs->blue ? rhs->green : rhs->blue); + hsv_color_t hsv; hsv.val = rgbMax; if (hsv.val == 0) { @@ -421,12 +441,13 @@ HSVColor rgb_to_hsv_generic(const RGBColor &rhs) return hsv; } - if (rgbMax == rhs.red) { - hsv.hue = 0 + 43 * (rhs.green - rhs.blue) / (rgbMax - rgbMin); - } else if (rgbMax == rhs.green) { - hsv.hue = 85 + 43 * (rhs.blue - rhs.red) / (rgbMax - rgbMin); + if (rgbMax == rhs->red) { + hsv.hue = 0 + 43 * (rhs->green - rhs->blue) / (rgbMax - rgbMin); + } else if (rgbMax == rhs->green) { + hsv.hue = 85 + 43 * (rhs->blue - rhs->red) / (rgbMax - rgbMin); } else { - hsv.hue = 171 + 43 * (rhs.red - rhs.green) / (rgbMax - rgbMin); + hsv.hue = 171 + 43 * (rhs->red - rhs->green) / (rgbMax - rgbMin); } return hsv; } + diff --git a/Helios/Colortypes.h b/Helios/Colortypes.h index 2a579de7..a10d58ee 100644 --- a/Helios/Colortypes.h +++ b/Helios/Colortypes.h @@ -7,102 +7,79 @@ #include "ColorConstants.h" #if ALTERNATIVE_HSV_RGB == 1 -enum hsv_to_rgb_algorithm : uint8_t +enum hsv_to_rgb_algorithm { HSV_TO_RGB_GENERIC, HSV_TO_RGB_RAINBOW }; -// global hsv to rgb algorithm selector, switch this to control -// all hsv to rgb conversions -extern hsv_to_rgb_algorithm g_hsv_rgb_alg; +/* global hsv to rgb algorithm selector, switch this to control + * all hsv to rgb conversions */ +extern enum hsv_to_rgb_algorithm g_hsv_rgb_alg; #endif -class ByteStream; -class RGBColor; +/* Forward declarations */ +typedef struct hsv_color_t hsv_color_t; +typedef struct rgb_color_t rgb_color_t; -class HSVColor +struct hsv_color_t { -public: - HSVColor(); - HSVColor(uint8_t hue, uint8_t sat, uint8_t val); - - // assignment from uint32_t - HSVColor(uint32_t dwVal); - HSVColor &operator=(const uint32_t &rhs); - - // construction/assignment from RGB - HSVColor(const RGBColor &rhs); - HSVColor &operator=(const RGBColor &rhs); - - // equality operators - bool operator==(const HSVColor &other) const; - bool operator!=(const HSVColor &other) const; - - bool empty() const; - void clear(); - - uint32_t raw() const { return ((uint32_t)hue << 16) | ((uint32_t)sat << 8) | (uint32_t)val; } - - // public members uint8_t hue; uint8_t sat; uint8_t val; }; -class RGBColor +struct rgb_color_t { -public: - RGBColor(); - RGBColor(uint8_t red, uint8_t green, uint8_t blue); - - // assignment from uint32_t - RGBColor(uint32_t dwVal); - RGBColor &operator=(const uint32_t &rhs); - - RGBColor(const HSVColor &rhs); - RGBColor &operator=(const HSVColor &rhs); - - // equality operators - bool operator==(const RGBColor &other) const; - bool operator!=(const RGBColor &other) const; - - bool empty() const; - void clear(); - - // scale down the brightness of a color by some fade amount - RGBColor adjustBrightness(uint8_t fadeBy); - -#ifdef HELIOS_CLI - // return a scale brightness version of the current color, this uses - // a float value to scale the r/g/b values of the color - // ex: 0.0 = black - // 0.5 = half as bright - // 1.0 = no change - // 1.5 = 50% brighter - // 2.0 = twice as bright - // 255.0 = white - RGBColor scaleBrightness(float scale); - // bring up the brightness of a color to a minimum level - RGBColor bringUpBrightness(uint8_t min_brightness); -#endif - - uint32_t raw() const { return ((uint32_t)red << 16) | ((uint32_t)green << 8) | (uint32_t)blue; } - - // public members uint8_t red; uint8_t green; uint8_t blue; }; -// Stolen from FastLED hsv to rgb full rainbox where all colours -// are given equal weight, this makes for-example yellow larger -// best to use this function as it is the legacy choice -RGBColor hsv_to_rgb_rainbow(const HSVColor &rhs); -// generic hsv to rgb conversion nothing special -RGBColor hsv_to_rgb_generic(const HSVColor &rhs); +/* HSVColor functions */ +void hsv_init(hsv_color_t *hsv); +void hsv_init3(hsv_color_t *hsv, uint8_t hue, uint8_t sat, uint8_t val); +void hsv_init_from_raw(hsv_color_t *hsv, uint32_t dwVal); +void hsv_init_from_rgb(hsv_color_t *hsv, const rgb_color_t *rgb); +void hsv_copy(hsv_color_t *dest, const hsv_color_t *src); +void hsv_assign_from_raw(hsv_color_t *hsv, uint32_t rhs); +void hsv_assign_from_rgb(hsv_color_t *hsv, const rgb_color_t *rhs); +uint8_t hsv_equals(const hsv_color_t *a, const hsv_color_t *b); +uint8_t hsv_empty(const hsv_color_t *hsv); +void hsv_clear(hsv_color_t *hsv); +uint32_t hsv_raw(const hsv_color_t *hsv); + +/* RGBColor functions */ +void rgb_init(rgb_color_t *rgb); +void rgb_init3(rgb_color_t *rgb, uint8_t red, uint8_t green, uint8_t blue); +void rgb_init_from_raw(rgb_color_t *rgb, uint32_t dwVal); +void rgb_init_from_hsv(rgb_color_t *rgb, const hsv_color_t *hsv); +void rgb_copy(rgb_color_t *dest, const rgb_color_t *src); +void rgb_assign_from_raw(rgb_color_t *rgb, uint32_t rhs); +void rgb_assign_from_hsv(rgb_color_t *rgb, const hsv_color_t *rhs); +uint8_t rgb_equals(const rgb_color_t *a, const rgb_color_t *b); +uint8_t rgb_empty(const rgb_color_t *rgb); +void rgb_clear(rgb_color_t *rgb); +void rgb_adjust_brightness(rgb_color_t *rgb, uint8_t fadeBy); +uint32_t rgb_raw(const rgb_color_t *rgb); + +#ifdef HELIOS_CLI +/* Return a scaled brightness version of the current color + * ex: 0.0 = black, 0.5 = half brightness, 1.0 = no change, + * 1.5 = 50% brighter, 2.0 = twice as bright, 255.0 = white */ +void rgb_scale_brightness(rgb_color_t *rgb, float scale); +/* Bring up the brightness of a color to a minimum level */ +void rgb_bring_up_brightness(rgb_color_t *rgb, uint8_t min_brightness); +#endif -// Convert rgb to hsv with generic fast method -HSVColor rgb_to_hsv_generic(const RGBColor &rhs); +/* Conversion functions */ +/* Stolen from FastLED hsv to rgb full rainbow where all colours + * are given equal weight, this makes for-example yellow larger + * best to use this function as it is the legacy choice */ +rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs); +/* Generic hsv to rgb conversion nothing special */ +rgb_color_t hsv_to_rgb_generic(const hsv_color_t *rhs); +/* Convert rgb to hsv with generic fast method */ +hsv_color_t rgb_to_hsv_generic(const rgb_color_t *rhs); #endif diff --git a/Helios/Helios.cpp b/Helios/Helios.cpp index 835f945b..6949709c 100644 --- a/Helios/Helios.cpp +++ b/Helios/Helios.cpp @@ -1,847 +1,694 @@ -#include - -#include "Helios.h" - -#include "ColorConstants.h" -#include "TimeControl.h" -#include "Storage.h" -#include "Pattern.h" -#include "Random.h" -#include "Button.h" -#include "Led.h" - -#ifdef HELIOS_EMBEDDED -#include -#include -#include -#endif - -#ifdef HELIOS_CLI -#include -#endif - -#include - -// some internal macros that shouldn't change -// The number of menus in hue/sat/val selection -#define NUM_MENUS_HUE_SAT_VAL 4 -// the number of menus in quadrant selection -#define NUM_MENUS_QUADRANT 7 - -Helios::State Helios::cur_state; -Helios::Flags Helios::global_flags; -uint8_t Helios::menu_selection; -uint8_t Helios::cur_mode; -uint8_t Helios::selected_slot; -uint8_t Helios::selected_base_quad; -uint8_t Helios::selected_hue; -uint8_t Helios::selected_sat; -uint8_t Helios::selected_val; -Pattern Helios::pat; -bool Helios::keepgoing; - -#ifdef HELIOS_CLI -bool Helios::sleeping; -#endif - -volatile char helios_version[] = HELIOS_VERSION_STR; - -bool Helios::init() -{ - // first initialize all the components of helios - if (!init_components()) { - return false; - } - // then initialize the hardware for embedded helios -#ifdef HELIOS_EMBEDDED - // Set PB0, PB1, PB4 as output - DDRB |= (1 << DDB0) | (1 << DDB1) | (1 << DDB4); - // Timer0 Configuration for PWM - TCCR0A = (1 << WGM01) | (1 << WGM00) | (1 << COM0A1) | (1 << COM0B1); - // No prescaler - TCCR0B = (1 << CS00); - // Timer1 for PWM on PB4, Fast PWM, Non-inverting, No prescaler - TCCR1 = (1 << PWM1A) | (1 << COM1A1) | (1 << CS10); - // Enable PWM on OC1B - GTCCR = (1 << PWM1B) | (1 << COM1B1); - // Enable Timer0 overflow interrupt - TIMSK |= (1 << TOIE0); - // Enable interrupts - sei(); -#endif - return true; -} - -bool Helios::init_components() -{ - // initialize various components of Helios - if (!Time::init()) { - return false; - } - if (!Led::init()) { - return false; - } - if (!Storage::init()) { - return false; - } - if (!Button::init()) { - return false; - } - // initialize global variables - cur_state = STATE_MODES; - menu_selection = 0; - cur_mode = 0; - selected_slot = 0; - selected_base_quad = 0; - keepgoing = true; -#ifdef HELIOS_CLI - sleeping = false; -#endif - // load global flags, and brightness from storage, this - // includes for example conjure mode and the mode index - // of the conjure mode if it is enabled - load_global_flags(); - // finally load whatever current mode index is selected - // this might be mode 0, or for example a separate index - // if conjure mode is enabled - load_cur_mode(); - return true; -} - -void Helios::tick() -{ - // sample the button and re-calculate all button globals - // the button globals should not change anywhere else - Button::update(); - - // handle the current state of the system, ie whatever state - // we're in we check for the appropriate input events for that - // state by checking button globals, then run the appropriate logic - handle_state(); - - // Update the Leds once per frame - Led::update(); - - // finally tick the clock forward and then sleep till the entire - // tick duration has been consumed - Time::tickClock(); -} - -void Helios::enter_sleep() -{ -#ifdef HELIOS_EMBEDDED - // clear the led colors - Led::clear(); - // Set all pins to input - DDRB = 0x00; - // Disable pull-ups on all pins - PORTB = 0x00; - // Enable wake on interrupt for the button - Button::enableWake(); - // Set sleep mode to POWER DOWN mode - set_sleep_mode(SLEEP_MODE_PWR_DOWN); - // enter sleep - sleep_mode(); - // ... interrupt will make us wake here - - // Set PB0, PB1, PB4 as output - DDRB |= (1 << DDB0) | (1 << DDB1) | (1 << DDB4); - // wakeup here, re-init - init_components(); -#else - cur_state = STATE_SLEEP; - // enable the sleep bool - sleeping = true; -#endif -} - -void Helios::wakeup() -{ -#ifdef HELIOS_EMBEDDED - // nothing needed here, this interrupt firing will make the mainthread resume -#else - // if the button was held down then they are entering off-menus - // but if we re-initialize the button it will clear this state - bool pressed = Button::isPressed(); - // re-initialize some stuff - Time::init(); - Button::init(); - // so just re-press it - if (pressed) { - Button::doPress(); - } - cur_state = STATE_MODES; - // turn off the sleeping flag that only CLI has - sleeping = false; -#endif -} - -void Helios::load_next_mode() -{ - // increment current mode and wrap around - cur_mode = (uint8_t)(cur_mode + 1) % NUM_MODE_SLOTS; - // now load current mode again - load_cur_mode(); -} - -void Helios::load_cur_mode() -{ - // read pattern from storage at cur mode index - if (!Storage::read_pattern(cur_mode, pat)) { - // and just initialize default if it cannot be read - Patterns::make_default(cur_mode, pat); - // try to write it out because storage was corrupt - Storage::write_pattern(cur_mode, pat); - } - // then re-initialize the pattern - pat.init(); -} - -void Helios::save_cur_mode() -{ - Storage::write_pattern(cur_mode, pat); -} - -void Helios::load_global_flags() -{ - // read the global flags from index 0 config - global_flags = (Flags)Storage::read_global_flags(); - if (has_flags(FLAG_CONJURE)) { - // if conjure is enabled then load the current mode index from storage - cur_mode = Storage::read_current_mode(); - } - // read the global brightness from index 2 config - uint8_t saved_brightness = Storage::read_brightness(); - // Check if flags are valid (FLAGS_INVALID is inverse mask of valid flags) - // and brightness is set in storage - bool is_valid = !has_any_flags(FLAGS_INVALID) && saved_brightness > 0; - if (is_valid) { - Led::setBrightness(saved_brightness); - } - - if (!is_valid) { - // if the brightness was 0 and the flags are invalid then the storage was likely - // uninitialized or corrupt so write out the defaults - factory_reset(); - } -} - -void Helios::save_global_flags() -{ - Storage::write_global_flags(global_flags); - Storage::write_current_mode(cur_mode); -} - -void Helios::set_mode_index(uint8_t mode_index) -{ - cur_mode = (uint8_t)mode_index % NUM_MODE_SLOTS; - // now load current mode again - load_cur_mode(); -} - -void Helios::handle_state() -{ - // check for the force sleep button hold regardless of which state we're in - if (Button::holdDuration() > FORCE_SLEEP_TIME) { - // when released the device will just sleep - if (Button::onRelease()) { - enter_sleep(); - // ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! - return; - } - // but as long as it's held past the sleep time it just turns off the led - if (Button::isPressed()) { - Led::clear(); - return; - } - } - // otherwise just handle the state like normal - switch (cur_state) { - case STATE_MODES: - handle_state_modes(); - break; - case STATE_COLOR_SELECT_SLOT: - case STATE_COLOR_SELECT_QUADRANT: - case STATE_COLOR_SELECT_HUE: - case STATE_COLOR_SELECT_SAT: - case STATE_COLOR_SELECT_VAL: - handle_state_col_select(); - break; - case STATE_PATTERN_SELECT: - handle_state_pat_select(); - break; - case STATE_TOGGLE_CONJURE: - handle_state_toggle_flag(FLAG_CONJURE); - break; - case STATE_TOGGLE_LOCK: - handle_state_toggle_flag(FLAG_LOCKED); - break; - case STATE_SET_DEFAULTS: - handle_state_set_defaults(); - break; - case STATE_SET_GLOBAL_BRIGHTNESS: - handle_state_set_global_brightness(); - break; - case STATE_SHIFT_MODE: - handle_state_shift_mode(); - break; - case STATE_RANDOMIZE: - handle_state_randomize(); - break; -#ifdef HELIOS_CLI - case STATE_SLEEP: - // simulate sleep in helios CLI - if (Button::onPress() || Button::onShortClick() || Button::onLongClick()) { - wakeup(); - } - break; -#endif - } -} - -void Helios::handle_state_modes() -{ - // whether they have released the button since turning on - bool hasReleased = (Button::releaseCount() > 0); - - if (Button::releaseCount() > 1 && Button::onShortClick()) { - if (has_flags(FLAG_CONJURE)) { - enter_sleep(); - } else { - load_next_mode(); - } - return; - } - - // check for lock and go back to sleep - if (has_flags(FLAG_LOCKED) && hasReleased && !Button::onRelease()) { - enter_sleep(); - // ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! - return; - } - - if (!has_flags(FLAG_LOCKED) && hasReleased) { - // just play the current mode - pat.play(); - } - // check how long the button is held - uint32_t holdDur = Button::holdDuration(); - // calculate a magnitude which corresponds to how many times past the MENU_HOLD_TIME - // the user has held the button, so 0 means haven't held fully past one yet, etc - uint8_t magnitude = (uint8_t)(holdDur / MENU_HOLD_TIME); - // whether the user has held the button longer than a short click - bool heldPast = (holdDur > SHORT_CLICK_THRESHOLD); - - // flash red briefly when locked and short clicked - if (has_flags(FLAG_LOCKED) && !heldPast) { - Led::set(RGB_RED_BRI_LOW); - } - // if the button is held for at least 1 second - if (Button::isPressed() && heldPast) { - // if the button has been released before then show the on menu - if (hasReleased) { - switch (magnitude) { - default: - case 0: Led::clear(); break; // Turn off - case 1: Led::set(0, 0x3c, 0x31); break; // Color Selection - case 2: Led::set(0x3c, 0, 0x0e); break; // Pattern Selection - case 3: Led::set(0x3c, 0x1c, 0); break; // Conjure Mode - case 4: Led::set(0x3c, 0x3c, 0x3c); break; // Shift Mode - case 5: Led::set(HSVColor(Time::getCurtime(), 255, 100)); break; // Randomizer - } - } else { - if (has_flags(FLAG_LOCKED)) { - switch (magnitude) { - default: - case 0: Led::clear(); break; - case TIME_TILL_GLOW_LOCK_UNLOCK: Led::set(0x3c, 0, 0); break; // Exit - } - } else { - switch (magnitude) { - default: - case 0: Led::clear(); break; // nothing - case 1: Led::set(0x3c, 0, 0); break; // Enter Glow Lock - case 2: Led::set(0, 0x3c, 0); break; // Global Brightness - case 3: Led::set(0, 0, 0x3c); break; // Master Reset - } - } - } - } - // if this isn't a release tick there's nothing more to do - if (Button::onRelease()) { - // Resets the menu selection before entering new state - menu_selection = 0; - if (heldPast && Button::releaseCount() == 1) { - handle_off_menu(magnitude, heldPast); - return; - } - // otherwise if we have released it then we are in the 'on' menu - handle_on_menu(magnitude, heldPast); - } -} - -void Helios::handle_off_menu(uint8_t mag, bool past) -{ - // if still locked then handle the unlocking menu which is just if mag == 5 - if (has_flags(FLAG_LOCKED)) { - switch (mag) { - case TIME_TILL_GLOW_LOCK_UNLOCK: // red lock - cur_state = STATE_TOGGLE_LOCK; - break; - default: - // just go back to sleep in hold-past off menu - enter_sleep(); - // ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! - } - // in this case we return either way, since we're locked - return; - } - - // otherwise if not locked handle the off menu - switch (mag) { - case 1: // red lock - cur_state = STATE_TOGGLE_LOCK; - Led::clear(); - return; // RETURN HERE - case 2: // green global brightness - cur_state = STATE_SET_GLOBAL_BRIGHTNESS; - return; // RETURN HERE - case 3: // blue reset defaults - cur_state = STATE_SET_DEFAULTS; - return; //RETURN HERE - default: - // just go back to sleep in hold-past off menu - enter_sleep(); - // ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! - return; - } -} - -void Helios::handle_on_menu(uint8_t mag, bool past) -{ - switch (mag) { - case 0: // off - // but only if we held for more than a short click - if (past) { - enter_sleep(); - // ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! - return; - } - break; - case 1: // color select - cur_state = STATE_COLOR_SELECT_SLOT; - // reset the menu selection - menu_selection = 0; -#if ALTERNATIVE_HSV_RGB == 1 - // use the nice hue to rgb rainbow - g_hsv_rgb_alg = HSV_TO_RGB_RAINBOW; -#endif - break; - case 2: // pat select - cur_state = STATE_PATTERN_SELECT; - // reset the menu selection - menu_selection = 0; - break; - case 3: // conjure mode - cur_state = STATE_TOGGLE_CONJURE; - Led::clear(); - break; - case 4: // shift mode down - cur_state = STATE_SHIFT_MODE; - break; - case 5: // randomizer - cur_state = STATE_RANDOMIZE; - break; - default: // hold past - break; - } -} - -void Helios::handle_state_col_select() -{ - ColorSelectOption slot_option = OPTION_NONE; - switch (cur_state) { - case STATE_COLOR_SELECT_SLOT: - // pick the target colorset slot - handle_state_col_select_slot(slot_option); - break; - case STATE_COLOR_SELECT_QUADRANT: - // pick the hue quadrant - handle_state_col_select_quadrant(); - break; - case STATE_COLOR_SELECT_HUE: - case STATE_COLOR_SELECT_SAT: - case STATE_COLOR_SELECT_VAL: - default: - // pick the hue sat or val - handle_state_col_select_hue_sat_val(); - break; - } - // get the current color - RGBColor cur = Led::get(); - cur.red /= 2; - cur.green /= 2; - cur.blue /= 2; - // this is a stupid override for when we're exiting color select - // show a white selection instead - if (slot_option != OPTION_NONE) { - cur = RGB_WHITE_BRI_LOW; - } - // show selection in all of these menus - show_selection(cur); -} - -void Helios::handle_state_col_select_slot(ColorSelectOption &out_option) -{ - Colorset &set = pat.colorset(); - uint8_t num_cols = set.numColors(); - - if (Button::onShortClick()) { - // the number of menus in slot selection = all colors + exit - uint8_t num_menus = num_cols + 1; - // except if the number of colors is less than total color slots - if (num_cols < NUM_COLOR_SLOTS) { - // then we have another menu: add color - num_menus++; - } - menu_selection = (menu_selection + 1) % num_menus; - } - - bool long_click = Button::onLongClick(); - - // Reset the color selection variables, these are the hue/sat/val that have been selected - // in the following menus, this is a weird place to reset these but it ends up being the only - // place where it can be written once and still handle all the possible cases it needs to run - selected_sat = 255; - selected_val = 255; - - if (num_cols < NUM_COLOR_SLOTS && menu_selection == num_cols) { - // add color - out_option = SELECTED_ADD; - Led::strobe(100, 100, RGB_WHITE_BRI_LOW, RGB_OFF); - if (long_click) { - selected_slot = menu_selection; - } - } else if (menu_selection == num_cols + 1 || (num_cols == NUM_COLOR_SLOTS && menu_selection == num_cols)) { - // exit - out_option = SELECTED_EXIT; - Led::strobe(60, 40, RGB_RED_BRI_LOW, RGB_OFF); - if (long_click) { -#if ALTERNATIVE_HSV_RGB == 1 - // restore hsv to rgb algorithm type, done color selection - g_hsv_rgb_alg = HSV_TO_RGB_GENERIC; -#endif - save_cur_mode(); - cur_state = STATE_MODES; - return; - } - } else { - out_option = SELECTED_SLOT; - selected_slot = menu_selection; - // render current selection - RGBColor col = set.get(selected_slot); - if (col.empty()) { - Led::strobe(1, 30, RGB_OFF, RGB_WHITE_BRI_LOW); - } else { - Led::strobe(3, 30, RGB_OFF, col); - } - if (Button::holdPressing()) { - // flash red - Led::strobe(150, 150, RGB_RED_BRI_LOW, col); - } - if (Button::onHoldClick()){ - set.removeColor(selected_slot); - return; - } - } - if (long_click) { - cur_state = (State)(cur_state + 1); - // reset the menu selection - menu_selection = 0; - } -} - -struct ColorsMenuData { - uint8_t hues[4]; -}; -// array of hues for selection -static const ColorsMenuData color_menu_data[4] = { - // hue0 hue1 hue2 hue3 - // ================================================================================== - { HUE_RED, HUE_CORAL_ORANGE, HUE_ORANGE, HUE_YELLOW }, - { HUE_LIME_GREEN, HUE_GREEN, HUE_SEAFOAM, HUE_TURQUOISE }, - { HUE_ICE_BLUE, HUE_LIGHT_BLUE, HUE_BLUE, HUE_ROYAL_BLUE }, - { HUE_PURPLE, HUE_PINK, HUE_HOT_PINK, HUE_MAGENTA }, -}; - -void Helios::handle_state_col_select_quadrant() -{ - if (Button::onShortClick()) { - menu_selection = (menu_selection + 1) % NUM_MENUS_QUADRANT; - } - - uint8_t hue_quad = (menu_selection - 2) % 4; - if (menu_selection > 5) { - menu_selection = 0; - } - - if (Button::onLongClick()) { - // select hue/sat/val - switch (menu_selection) { - case 0: // selected blank - // add blank to set - pat.colorset().set(selected_slot, RGB_OFF); - // Return to the slot you were editing - menu_selection = selected_slot; - // go to slot selection - 1 because we will increment outside here - cur_state = STATE_COLOR_SELECT_SLOT; - // RETURN HERE - return; - case 1: // selected white - // adds white, skip hue/sat to brightness - selected_sat = 0; - menu_selection = 0; - cur_state = STATE_COLOR_SELECT_VAL; - // RETURN HERE - return; - default: // 2-5 - selected_base_quad = hue_quad; - break; - } - } - - // default col1/col2 to off and white for the first two options - RGBColor col1 = RGB_OFF; - RGBColor col2; - uint16_t on_dur, off_dur; - - switch (menu_selection) { - case 0: // Blank Option - col2 = RGB_WHITE_BRI_LOW; - on_dur = 1; - off_dur = 30; - break; - case 1: // White Option - col2 = RGB_WHITE; - on_dur = 9; - off_dur = 0; - break; - default: // Color options - col1 = HSVColor(color_menu_data[hue_quad].hues[0], 255, 255); - col2 = HSVColor(color_menu_data[hue_quad].hues[2], 255, 255); - on_dur = 500; - off_dur = 500; - break; - } - Led::strobe(on_dur, off_dur, col1, col2); - // show a white flash for the first two menus - if (menu_selection <= 1) { - show_selection(RGB_WHITE_BRI_LOW); - } else { - // dim the color for the quad menus - RGBColor cur = Led::get(); - cur.red /= 2; - cur.green /= 2; - cur.blue /= 2; - show_selection(RGB_WHITE_BRI_LOW); - } - if (Button::onLongClick()) { - cur_state = (State)(cur_state + 1); - // reset the menu selection - menu_selection = 0; - } -} - -void Helios::handle_state_col_select_hue_sat_val() -{ - // handle iterating to the next option - if (Button::onShortClick()) { - menu_selection = (menu_selection + 1) % NUM_MENUS_HUE_SAT_VAL; - } - // in the sat/val selection a longclick is next and hold is save but in - // the final val selection a longclick is save and there's no next - bool gotoNextMenu = Button::onLongClick(); - bool saveAndFinish = Button::onHoldClick(); - switch (cur_state) { - default: - case STATE_COLOR_SELECT_HUE: - selected_hue = color_menu_data[selected_base_quad].hues[menu_selection]; - break; - case STATE_COLOR_SELECT_SAT: - static const uint8_t saturation_values[4] = {HSV_SAT_HIGH, HSV_SAT_MEDIUM, HSV_SAT_LOW, HSV_SAT_LOWEST}; - selected_sat = saturation_values[menu_selection]; - break; - case STATE_COLOR_SELECT_VAL: - static const uint8_t hsv_values[4] = {HSV_VAL_HIGH, HSV_VAL_MEDIUM, HSV_VAL_LOW, HSV_VAL_LOWEST}; - selected_val = hsv_values[menu_selection]; - // longclick becomes save and there is no next - saveAndFinish = gotoNextMenu; - break; - } - // render current selection - Led::set(HSVColor(selected_hue, selected_sat, selected_val)); - // show the long selection flash - if (Button::holdPressing()) { - Led::strobe(150, 150, RGB_CORAL_ORANGE_SAT_LOWEST, Led::get()); - } - // check to see if we are holding to save and skip - if (saveAndFinish) { - cur_state = STATE_COLOR_SELECT_SLOT; - pat.updateColor(selected_slot, HSVColor(selected_hue, selected_sat, selected_val)); - save_cur_mode(); - // Return to the slot you were editing - menu_selection = selected_slot; - return; - } - if (gotoNextMenu) { - cur_state = (State)(cur_state + 1); - // reset the menu selection - menu_selection = 0; - } -} - -void Helios::handle_state_pat_select() -{ - if (Button::onLongClick()) { - save_cur_mode(); - cur_state = STATE_MODES; - } - if (Button::onShortClick()) { - Patterns::make_pattern((PatternID)menu_selection, pat); - menu_selection = (menu_selection + 1) % PATTERN_COUNT; - pat.init(); - } - pat.play(); - show_selection(RGB_MAGENTA_BRI_LOW); -} - -void Helios::handle_state_toggle_flag(Flags flag) -{ - // toggle the conjure flag - toggle_flags(flag); - // write out the new global flags and the current mode - save_global_flags(); - // switch back to modes - cur_state = STATE_MODES; -} - -void Helios::handle_state_set_defaults() -{ - if (Button::onShortClick()) { - menu_selection = !menu_selection; - } - // show low white for exit or red for select - if (menu_selection) { - Led::strobe(80, 20, RGB_RED_BRI_LOW, RGB_OFF); - } else { - Led::strobe(20, 10, RGB_WHITE_BRI_LOWEST, RGB_OFF); - } - // when the user long clicks a selection - if (Button::onLongClick()) { - // if the user actually selected 'yes' - if (menu_selection == 1) { - factory_reset(); - } - cur_state = STATE_MODES; - } - show_selection(RGB_WHITE_BRI_LOW); -} - -void Helios::factory_reset() -{ - for (uint8_t i = 0; i < NUM_MODE_SLOTS; ++i) { - Patterns::make_default(i, pat); - Storage::write_pattern(i, pat); - } - // Reset global brightness to default - Led::setBrightness(DEFAULT_BRIGHTNESS); - Storage::write_brightness(DEFAULT_BRIGHTNESS); - // reset global flags - global_flags = FLAG_NONE; - cur_mode = 0; - // save global flags - save_global_flags(); - // re-load current mode - load_cur_mode(); -} - -void Helios::handle_state_set_global_brightness() -{ - if (Button::onShortClick()) { - menu_selection = (menu_selection + 1) % NUM_BRIGHTNESS_OPTIONS; - } - // show different levels of green for each selection - uint8_t col = 0; - uint8_t brightness = 0; - switch (menu_selection) { - case 0: - col = 0xFF; - brightness = BRIGHTNESS_HIGH; - break; - case 1: - col = 0x78; - brightness = BRIGHTNESS_MEDIUM; - break; - case 2: - col = 0x3c; - brightness = BRIGHTNESS_LOW; - break; - case 3: - col = 0x28; - brightness = BRIGHTNESS_LOWEST; - break; - } - Led::set(0, col, 0); - // when the user long clicks a selection - if (Button::onLongClick()) { - // set the brightness based on the selection - Led::setBrightness(brightness); - Storage::write_brightness(brightness); - cur_state = STATE_MODES; - } - show_selection(RGB_WHITE_BRI_LOW); -} - -void Helios::handle_state_shift_mode() -{ - uint8_t new_mode = (cur_mode > 0) ? (uint8_t)(cur_mode - 1) : (uint8_t)(NUM_MODE_SLOTS - 1); - // copy the storage from the new position into our current position - Storage::copy_slot(new_mode, cur_mode); - // point at the new position - cur_mode = new_mode; - // write out the current mode to the newly updated position - save_cur_mode(); - cur_state = STATE_MODES; -} - -void Helios::handle_state_randomize() -{ - if (Button::onShortClick()) { - Colorset &cur_set = pat.colorset(); - Random ctx(pat.crc32()); - uint8_t randVal = ctx.next8(); - cur_set.randomizeColors(ctx, (randVal + 1) % NUM_COLOR_SLOTS, Colorset::COLOR_MODE_RANDOMLY_PICK); - Patterns::make_pattern((PatternID)(randVal % PATTERN_COUNT), pat); - pat.init(); - } - if (Button::onLongClick()) { - save_cur_mode(); - cur_state = STATE_MODES; - } - pat.play(); - show_selection(RGB_WHITE_BRI_LOW); -} - -void Helios::show_selection(RGBColor color) -{ - // only show selection while pressing the button - if (!Button::isPressed()) { - return; - } - uint16_t holdDur = (uint16_t)Button::holdDuration(); - // if the hold duration is outside the flashing range do nothing - if (holdDur < SHORT_CLICK_THRESHOLD || holdDur >= HOLD_CLICK_START) { - return; - } - Led::set(color); -} +#include + +#include "Helios.h" + +#include "ColorConstants.h" +#include "TimeControl.h" +#include "Storage.h" +#include "Pattern.h" +#include "Patterns.h" +#include "Random.h" +#include "Button.h" +#include "Led.h" + +#ifdef HELIOS_EMBEDDED +#include +#include +#include +#endif + +#ifdef HELIOS_CLI +#include +#endif + +#include + +/* some internal macros that shouldn't change */ +/* The number of menus in hue/sat/val selection */ +#define NUM_COLORS_PER_GROUP 4 +/* the number of color groups in the color selection menu */ +#define NUM_COLOR_GROUPS 4 +/* the number of menus in group selection */ +#define NUM_MENUS_GROUP 8 + +/* Forward declarations for internal functions */ +static uint8_t helios_init_components(void); +static void helios_handle_state(void); +static void helios_handle_state_modes(void); +static void helios_handle_off_menu(uint8_t mag, uint8_t past); +static void helios_handle_on_menu(uint8_t mag, uint8_t past); +static void helios_handle_state_color_selection(void); +static void helios_handle_state_color_group_selection(void); +static void helios_handle_state_color_variant_selection(void); +static void helios_handle_state_pat_select(void); +static void helios_handle_state_toggle_flag(enum helios_flags flag); +static void helios_handle_state_set_defaults(void); +static void helios_show_selection(rgb_color_t color); +static void helios_factory_reset(void); + +/* the slot selection returns this info for internal menu logic */ +enum helios_color_select_option { + OPTION_NONE = 0, + + SELECTED_ADD, + SELECTED_EXIT, + SELECTED_SLOT +}; + +enum helios_state { + STATE_MODES, + STATE_COLOR_GROUP_SELECTION, + STATE_COLOR_VARIANT_SELECTION, + STATE_PATTERN_SELECT, + STATE_TOGGLE_LOCK, + STATE_SET_DEFAULTS, +#ifdef HELIOS_CLI + STATE_SLEEP, +#endif +}; + +/* static members */ +static enum helios_state cur_state; +static enum helios_flags global_flags; +static uint8_t menu_selection; +static uint8_t cur_mode; +static uint8_t selected_base_group; +static uint8_t num_colors_selected; /* Track number of colors selected in current session */ +static pattern_t pat; +static uint8_t keepgoing; +static uint32_t last_mode_switch_time; +static colorset_t new_colorset; + +#ifdef HELIOS_CLI +static uint8_t sleeping; /* Only used in CLI mode */ +#endif + +volatile char helios_version[] = HELIOS_VERSION_STR; + +uint8_t helios_init(void) +{ + /* first initialize all the components of helios */ + if (!helios_init_components()) { + return 0; + } + /* then initialize the hardware for embedded helios */ +#ifdef HELIOS_EMBEDDED + /* Set PB0, PB1, PB4 as output */ + DDRB |= (1 << DDB0) | (1 << DDB1) | (1 << DDB4); + /* Timer0 Configuration for PWM */ + TCCR0A = (1 << WGM01) | (1 << WGM00) | (1 << COM0A1) | (1 << COM0B1); + /* No prescaler */ + TCCR0B = (1 << CS00); + /* Timer1 for PWM on PB4, Fast PWM, Non-inverting, No prescaler */ + TCCR1 = (1 << PWM1A) | (1 << COM1A1) | (1 << CS10); + /* Enable PWM on OC1B */ + GTCCR = (1 << PWM1B) | (1 << COM1B1); + /* Enable Timer0 overflow interrupt */ + TIMSK |= (1 << TOIE0); + /* Enable interrupts */ + sei(); +#endif + return 1; +} + +static uint8_t helios_init_components(void) +{ + /* initialize various components of Helios */ + if (!time_init()) { + return 0; + } + if (!led_init()) { + return 0; + } + if (!storage_init()) { + return 0; + } + if (!button_init()) { + return 0; + } + /* initialize global variables */ + cur_state = STATE_MODES; + menu_selection = 0; + cur_mode = 0; + num_colors_selected = 0; + selected_base_group = 0; + keepgoing = 1; + last_mode_switch_time = 0; +#ifdef HELIOS_CLI + sleeping = 0; +#endif + helios_load_global_flags(); + helios_load_cur_mode(); + return 1; +} + +void helios_tick(void) +{ + /* sample the button and re-calculate all button globals + * the button globals should not change anywhere else */ + button_update(); + + /* handle the current state of the system, ie whatever state + * we're in we check for the appropriate input events for that + * state by checking button globals, then run the appropriate logic */ + helios_handle_state(); + + /* Update the Leds once per frame */ + led_update(); + + /* finally tick the clock forward and then sleep till the entire + * tick duration has been consumed */ + time_tick_clock(); +} + +void helios_enter_sleep(void) +{ +#ifdef HELIOS_EMBEDDED + /* clear the led colors */ + led_clear(); + /* Set all pins to input */ + DDRB = 0x00; + /* Disable pull-ups on all pins */ + PORTB = 0x00; + /* Enable wake on interrupt for the button */ + button_enable_wake(); + /* Set sleep mode to POWER DOWN mode */ + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + /* enter sleep */ + sleep_mode(); + /* ... interrupt will make us wake here */ + + /* Set PB0, PB1, PB4 as output */ + DDRB |= (1 << DDB0) | (1 << DDB1) | (1 << DDB4); + /* wakeup here, re-init */ + helios_init_components(); +#else + cur_state = STATE_SLEEP; + /* enable the sleep bool */ + sleeping = 1; +#endif +} + +void helios_wakeup(void) +{ +#ifdef HELIOS_EMBEDDED + /* nothing needed here, this interrupt firing will make the mainthread resume */ +#else + /* if the button was held down then they are entering off-menus + * but if we re-initialize the button it will clear this state */ + uint8_t pressed = button_is_pressed(); + /* re-initialize some stuff */ + time_init(); + button_init(); + /* so just re-press it */ + if (pressed) { + button_do_press(); + } + cur_state = STATE_MODES; + /* turn off the sleeping flag that only CLI has */ + sleeping = 0; +#endif +} + +void helios_load_next_mode(void) +{ + /* increment current mode and wrap around */ + cur_mode = (uint8_t)(cur_mode + 1) % NUM_MODE_SLOTS; + /* now load current mode again */ + helios_load_cur_mode(); +} + +void helios_load_cur_mode(void) +{ + /* read pattern from storage at cur mode index */ + if (!storage_read_pattern(cur_mode, &pat)) { + /* and just initialize default if it cannot be read */ + patterns_make_default(cur_mode, &pat); + /* try to write it out because storage was corrupt */ + storage_write_pattern(cur_mode, &pat); + } + /* then re-initialize the pattern */ + pattern_init_state(&pat); + /* Update the last mode switch time when loading a mode */ + last_mode_switch_time = time_get_current_time(); +} + +void helios_save_cur_mode(void) +{ + storage_write_pattern(cur_mode, &pat); +} + +void helios_load_global_flags(void) +{ + /* read the global flags from index 0 config */ + global_flags = (enum helios_flags)storage_read_global_flags(); + cur_mode = storage_read_current_mode(); +} + +void helios_save_global_flags(void) +{ + storage_write_global_flags(global_flags); + storage_write_current_mode(cur_mode); +} + +void helios_set_mode_index(uint8_t mode_index) +{ + cur_mode = (uint8_t)mode_index % NUM_MODE_SLOTS; + /* now load current mode again */ + helios_load_cur_mode(); +} + +uint8_t helios_keep_going(void) +{ + return keepgoing; +} + +void helios_terminate(void) +{ + keepgoing = 0; +} + +void helios_set_flag(enum helios_flags flag) +{ + global_flags = (enum helios_flags)(global_flags | flag); +} + +uint8_t helios_has_flag(enum helios_flags flag) +{ + return (global_flags & flag) == flag; +} + +void helios_clear_flag(enum helios_flags flag) +{ + global_flags = (enum helios_flags)(global_flags & ~flag); +} + +void helios_toggle_flag(enum helios_flags flag) +{ + global_flags = (enum helios_flags)(global_flags ^ flag); +} + +#ifdef HELIOS_CLI +uint8_t helios_is_asleep(void) +{ + return sleeping; +} + +pattern_t *helios_cur_pattern(void) +{ + return &pat; +} +#endif + +static void helios_handle_state(void) +{ + /* check for the force sleep button hold regardless of which state we're in */ + if (button_hold_duration() > FORCE_SLEEP_TIME) { + /* when released the device will just sleep */ + if (button_on_release()) { + helios_enter_sleep(); + /* ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! */ + return; + } + /* but as long as it's held past the sleep time it just turns off the led */ + if (button_is_pressed()) { + led_clear(); + return; + } + } + /* otherwise just handle the state like normal */ + switch (cur_state) { + case STATE_MODES: + helios_handle_state_modes(); + break; + case STATE_COLOR_GROUP_SELECTION: + case STATE_COLOR_VARIANT_SELECTION: + helios_handle_state_color_selection(); + break; + case STATE_PATTERN_SELECT: + helios_handle_state_pat_select(); + break; + case STATE_TOGGLE_LOCK: + helios_handle_state_toggle_flag(FLAG_LOCKED); + break; + case STATE_SET_DEFAULTS: + helios_handle_state_set_defaults(); + break; +#ifdef HELIOS_CLI + case STATE_SLEEP: + /* simulate sleep in helios CLI */ + if (button_on_press() || button_on_short_click() || button_on_long_click()) { + helios_wakeup(); + } + break; +#endif + } +} + +static void helios_handle_state_modes(void) +{ + /* whether they have released the button since turning on */ + uint8_t hasReleased = (button_release_count() > 0); + + if (button_release_count() > 1 && button_on_short_click()) { + helios_enter_sleep(); + return; + } + + /* check for lock and go back to sleep */ + if (helios_has_flag(FLAG_LOCKED) && hasReleased && !button_on_release()) { + helios_enter_sleep(); + /* ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! */ + return; + } + + if (!helios_has_flag(FLAG_LOCKED) && hasReleased) { + /* just play the current mode */ + pattern_play(&pat); + } + /* check how long the button is held */ + uint32_t holdDur = button_hold_duration(); + /* calculate a magnitude which corresponds to how many times past the MENU_HOLD_TIME + * the user has held the button, so 0 means haven't held fully past one yet, etc */ + uint8_t magnitude = (uint8_t)(holdDur / MENU_HOLD_TIME); + /* whether the user has held the button longer than a short click */ + uint8_t heldPast = (holdDur > SHORT_CLICK_THRESHOLD); + + /* flash red briefly when locked and short clicked */ + if (helios_has_flag(FLAG_LOCKED) && holdDur < SHORT_CLICK_THRESHOLD) { + rgb_color_t red; + rgb_init_from_raw(&red, RGB_RED_BRI_LOW); + led_set_rgb(&red); + } + /* if the button is held for at least 1 second */ + if (button_is_pressed() && heldPast) { + rgb_color_t color; + /* if the button has been released before then show the on menu */ + if (hasReleased) { + switch (magnitude) { + default: + case 0: led_clear(); break; /* Turn off */ + case 1: rgb_init_from_raw(&color, RGB_TURQUOISE_BRI_LOW); led_set_rgb(&color); break; /* Color Selection */ + case 2: rgb_init_from_raw(&color, RGB_MAGENTA_BRI_LOW); led_set_rgb(&color); break; /* Pattern Selection */ + } + } else { + if (helios_has_flag(FLAG_LOCKED)) { + switch (magnitude) { + default: + case 0: led_clear(); break; + case TIME_TILL_GLOW_LOCK_UNLOCK: rgb_init_from_raw(&color, RGB_RED_BRI_LOW); led_set_rgb(&color); break; /* Exit */ + } + } else { + switch (magnitude) { + default: + case 0: led_clear(); break; /* nothing */ + case 1: rgb_init_from_raw(&color, RGB_RED_BRI_LOW); led_set_rgb(&color); break; /* Enter Glow Lock */ + case 2: rgb_init_from_raw(&color, RGB_BLUE_BRI_LOW); led_set_rgb(&color); break; /* Master Reset */ + } + } + } + } + /* if this isn't a release tick there's nothing more to do */ + if (button_on_release()) { + /* Resets the menu selection before entering new state */ + menu_selection = 0; + if (heldPast && button_release_count() == 1) { + helios_handle_off_menu(magnitude, heldPast); + return; + } + /* otherwise if we have released it then we are in the 'on' menu */ + helios_handle_on_menu(magnitude, heldPast); + } +} + +static void helios_handle_off_menu(uint8_t mag, uint8_t past) +{ + (void)past; /* unused */ + /* if still locked then handle the unlocking menu which is just if mag == 5 */ + if (helios_has_flag(FLAG_LOCKED)) { + switch (mag) { + case TIME_TILL_GLOW_LOCK_UNLOCK: /* red lock */ + cur_state = STATE_TOGGLE_LOCK; + break; + default: + /* just go back to sleep in hold-past off menu */ + helios_enter_sleep(); + /* ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! */ + } + /* in this case we return either way, since we're locked */ + return; + } + + /* otherwise if not locked handle the off menu */ + switch (mag) { + case 1: /* red lock */ + cur_state = STATE_TOGGLE_LOCK; + led_clear(); + return; /* RETURN HERE */ + case 2: /* blue reset defaults */ + cur_state = STATE_SET_DEFAULTS; + return; /* RETURN HERE */ + default: + /* just go back to sleep in hold-past off menu */ + helios_enter_sleep(); + /* ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! */ + return; + } +} + +static void helios_handle_on_menu(uint8_t mag, uint8_t past) +{ + switch (mag) { + case 0: /* off */ + /* but only if we held for more than a short click */ + if (past) { + helios_enter_sleep(); + /* ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! */ + return; + } + break; + case 1: /* color select */ + cur_state = STATE_COLOR_GROUP_SELECTION; + /* reset the menu selection and colors selected */ + menu_selection = 0; + num_colors_selected = 0; + /* Store original colorset before clearing */ + new_colorset = *pattern_colorset_ptr(&pat); + /* Clear existing colors in pattern */ + colorset_clear(&new_colorset); +#if ALTERNATIVE_HSV_RGB == 1 + /* use the nice hue to rgb rainbow */ + g_hsv_rgb_alg = HSV_TO_RGB_RAINBOW; +#endif + break; + case 2: /* pat select */ + cur_state = STATE_PATTERN_SELECT; + /* reset the menu selection */ + menu_selection = 0; + break; + default: /* hold past */ + break; + } +} + +struct colors_menu_data { + uint32_t colors[4]; +}; + +/* array of colors for selection */ +static const struct colors_menu_data color_menu_data[NUM_COLOR_GROUPS] = { + /* color0 color1 color2 color3 */ + /* =================================================================== */ + { {RGB_RED, RGB_CORAL_ORANGE, RGB_ORANGE, RGB_YELLOW} }, + { {RGB_LIME_GREEN, RGB_GREEN, RGB_SEAFOAM, RGB_TURQUOISE} }, + { {RGB_ICE_BLUE, RGB_LIGHT_BLUE, RGB_BLUE, RGB_ROYAL_BLUE} }, + { {RGB_PURPLE, RGB_PINK, RGB_HOT_PINK, RGB_MAGENTA} }, +}; + +static void helios_handle_state_color_selection(void) +{ + switch (cur_state) { + case STATE_COLOR_GROUP_SELECTION: + /* pick the hue group */ + helios_handle_state_color_group_selection(); + break; + case STATE_COLOR_VARIANT_SELECTION: + /* pick the hue */ + helios_handle_state_color_variant_selection(); + break; + default: + break; + } + /* get the current color */ + rgb_color_t cur = led_get(); + cur.red /= 2; + cur.green /= 2; + cur.blue /= 2; + /* show selection in all of these menus */ + helios_show_selection(cur); +} + +static void helios_handle_state_color_group_selection(void) +{ + rgb_color_t color; + + if (button_on_short_click()) { + menu_selection = (menu_selection + 1) % NUM_COLOR_GROUPS; + } + + /* Display a sample color from the selected group */ + rgb_init_from_raw(&color, color_menu_data[menu_selection].colors[0]); + led_set_rgb(&color); + + if (button_on_long_click()) { + selected_base_group = menu_selection; + cur_state = STATE_COLOR_VARIANT_SELECTION; + menu_selection = 0; + } +} + +static void helios_handle_state_color_variant_selection(void) +{ + rgb_color_t color; + + if (button_on_short_click()) { + /* If we've selected max colors, next click exits */ + if (num_colors_selected >= NUM_COLOR_SLOTS) { + /* Apply the newly built colorset */ + pattern_set_colorset(&pat, &new_colorset); + /* Save and return to normal mode */ + helios_save_cur_mode(); + cur_state = STATE_MODES; + menu_selection = 0; +#if ALTERNATIVE_HSV_RGB == 1 + g_hsv_rgb_alg = HSV_TO_RGB_GENERIC; +#endif + return; + } + + /* Cycle through colors in the group */ + menu_selection = (menu_selection + 1) % NUM_COLORS_PER_GROUP; + } + + /* Display the currently selected color */ + rgb_init_from_raw(&color, color_menu_data[selected_base_group].colors[menu_selection]); + led_set_rgb(&color); + + if (button_on_long_click()) { + /* Add the selected color to the colorset */ + rgb_init_from_raw(&color, color_menu_data[selected_base_group].colors[menu_selection]); + if (colorset_add_color(&new_colorset, color)) { + num_colors_selected++; + } + + /* If we've selected max colors, exit */ + if (num_colors_selected >= NUM_COLOR_SLOTS) { + /* Apply the newly built colorset */ + pattern_set_colorset(&pat, &new_colorset); + /* Save and return to normal mode */ + helios_save_cur_mode(); + cur_state = STATE_MODES; + menu_selection = 0; +#if ALTERNATIVE_HSV_RGB == 1 + g_hsv_rgb_alg = HSV_TO_RGB_GENERIC; +#endif + return; + } + + /* Otherwise go back to group selection for next color */ + cur_state = STATE_COLOR_GROUP_SELECTION; + menu_selection = 0; + } +} + +static void helios_handle_state_pat_select(void) +{ + rgb_color_t color; + + if (button_on_short_click()) { + menu_selection = (menu_selection + 1) % PATTERN_COUNT; + } + + /* show the menu selection */ + switch (menu_selection) { + case 0: rgb_init_from_raw(&color, RGB_RED); break; + case 1: rgb_init_from_raw(&color, RGB_GREEN); break; + case 2: rgb_init_from_raw(&color, RGB_BLUE); break; + case 3: rgb_init_from_raw(&color, RGB_YELLOW); break; + case 4: rgb_init_from_raw(&color, RGB_MAGENTA); break; + default: rgb_init_from_raw(&color, RGB_WHITE); break; + } + led_set_rgb(&color); + + if (button_on_long_click()) { + /* make the selected pattern */ + patterns_make_pattern((enum pattern_id)(PATTERN_FIRST + menu_selection), &pat); + /* reset the pattern to revert to on/off state */ + pattern_init_state(&pat); + /* save and return to normal mode */ + helios_save_cur_mode(); + cur_state = STATE_MODES; + menu_selection = 0; + } +} + +static void helios_handle_state_toggle_flag(enum helios_flags flag) +{ + rgb_color_t color; + + /* wait until button release then toggle the flag */ + if (button_on_release()) { + helios_toggle_flag(flag); + helios_save_global_flags(); + /* show feedback based on new state */ + if (helios_has_flag(flag)) { + rgb_init_from_raw(&color, RGB_GREEN); + } else { + rgb_init_from_raw(&color, RGB_RED); + } + led_hold(&color); + cur_state = STATE_MODES; + } +} + +static void helios_handle_state_set_defaults(void) +{ + rgb_color_t color; + + /* wait until button release then factory reset */ + if (button_on_release()) { + helios_factory_reset(); + /* show feedback */ + rgb_init_from_raw(&color, RGB_BLUE); + led_hold(&color); + cur_state = STATE_MODES; + } +} + +static void helios_show_selection(rgb_color_t color) +{ + uint32_t time_since_click = time_get_current_time(); + if (button_press_time() > 0) { + time_since_click = time_get_current_time() - button_press_time(); + } + /* flash the selection color briefly after clicking */ + if (time_since_click < 150) { + led_set_rgb(&color); + } +} + +static void helios_factory_reset(void) +{ + uint8_t slot; + /* write default patterns to all slots */ + for (slot = 0; slot < NUM_MODE_SLOTS; ++slot) { + patterns_make_default(slot, &pat); + storage_write_pattern(slot, &pat); + } + /* clear all flags */ + global_flags = FLAG_NONE; + helios_save_global_flags(); + /* reload current mode */ + helios_load_cur_mode(); +} + diff --git a/Helios/Helios.h b/Helios/Helios.h index eef2d972..57140eee 100644 --- a/Helios/Helios.h +++ b/Helios/Helios.h @@ -1,128 +1,46 @@ +#ifndef HELIOS_H +#define HELIOS_H + #include #include "HeliosConfig.h" #include "Colorset.h" #include "Pattern.h" -class Helios -{ -public: - static bool init(); - static void tick(); - - static void enter_sleep(); - static void wakeup(); - - static bool keep_going() { return keepgoing; } - static void terminate() { keepgoing = false; } - - static void load_next_mode(); - static void load_cur_mode(); - static void save_cur_mode(); - static void load_global_flags(); - static void save_global_flags(); - static void set_mode_index(uint8_t mode_index); - -#ifdef HELIOS_CLI - static bool is_asleep() { return sleeping; } - static Pattern &cur_pattern() { return pat; } -#endif - - enum Flags : uint8_t { - // No flags are set - FLAG_NONE = 0, +/* Forward declaration */ +typedef struct pattern_t pattern_t; +typedef struct colorset_t colorset_t; - // The device is locked and must be unlocked to turn on - FLAG_LOCKED = (1 << 0), - // Conjure mode is enabled, one click will toggle power - FLAG_CONJURE = (1 << 1), - // Autoplay is enabled, modes will automatically cycle - FLAG_AUTOPLAY = (1 << 2), - // Add new flags here, max 8 flags +uint8_t helios_init(void); +void helios_tick(void); - // ============================================== - // Auto increment to count the number of flags - INTERNAL_FLAGS_END, - // Calculate mask for invalid Flags based on the - // inverse of all flags listed above here - FLAGS_INVALID = (uint8_t)(~((1 << (INTERNAL_FLAGS_END - 1)) - 1)) - }; +void helios_enter_sleep(void); +void helios_wakeup(void); - // get/set global flags - static void set_flags(Flags flag) { global_flags = (Flags)(global_flags | flag); } - static bool has_flags(Flags flag) { return (global_flags & flag) == flag; } - static bool has_any_flags(Flags flag) { return (global_flags & flag) != FLAG_NONE; } - static void clear_flags(Flags flag) { global_flags = (Flags)(global_flags & ~flag); } - static void toggle_flags(Flags flag) { global_flags = (Flags)(global_flags ^ flag); } +uint8_t helios_keep_going(void); +void helios_terminate(void); -private: - // initialize the various components of helios - static bool init_components(); +void helios_load_next_mode(void); +void helios_load_cur_mode(void); +void helios_save_cur_mode(void); +void helios_load_global_flags(void); +void helios_save_global_flags(void); +void helios_set_mode_index(uint8_t mode_index); - static void handle_state(); - static void handle_state_modes(); - - // the slot selection returns this info for internal menu logic - enum ColorSelectOption { - OPTION_NONE = 0, - - SELECTED_ADD, - SELECTED_EXIT, - SELECTED_SLOT - }; - - static void handle_off_menu(uint8_t mag, bool past); - static void handle_on_menu(uint8_t mag, bool past); - static void handle_state_col_select(); - static void handle_state_col_select_slot(ColorSelectOption &out_option); - static void handle_state_col_select_quadrant(); - static void handle_state_col_select_hue_sat_val(); - static void handle_state_pat_select(); - static void handle_state_toggle_flag(Flags flag); - static void handle_state_set_defaults(); - static void handle_state_set_global_brightness(); - static void handle_state_shift_mode(); - static void handle_state_randomize(); - static void show_selection(RGBColor color); - static void factory_reset(); - - enum State : uint8_t { - STATE_MODES, - STATE_COLOR_SELECT_SLOT, - STATE_COLOR_SELECT_QUADRANT, - STATE_COLOR_SELECT_HUE, - STATE_COLOR_SELECT_SAT, - STATE_COLOR_SELECT_VAL, - STATE_PATTERN_SELECT, - STATE_TOGGLE_CONJURE, - STATE_TOGGLE_LOCK, - STATE_SET_DEFAULTS, - STATE_SET_GLOBAL_BRIGHTNESS, - STATE_SHIFT_MODE, - STATE_RANDOMIZE, #ifdef HELIOS_CLI - STATE_SLEEP, +uint8_t helios_is_asleep(void); +pattern_t *helios_cur_pattern(void); #endif - }; - // the current state of the system - static State cur_state; - // global flags for the entire system - static Flags global_flags; - static uint8_t menu_selection; - static uint8_t cur_mode; - // the quadrant that was selected in color select - static uint8_t selected_slot; - static uint8_t selected_base_quad; - static uint8_t selected_hue; - static uint8_t selected_sat; - static uint8_t selected_val; - static PatternArgs default_args[6]; - static Colorset default_colorsets[6]; - static Pattern pat; - static bool keepgoing; +enum helios_flags { + FLAG_NONE = 0, + FLAG_LOCKED = (1 << 0), +}; + +/* get/set global flags */ +void helios_set_flag(enum helios_flags flag); +uint8_t helios_has_flag(enum helios_flags flag); +void helios_clear_flag(enum helios_flags flag); +void helios_toggle_flag(enum helios_flags flag); -#ifdef HELIOS_CLI - static bool sleeping; #endif -}; diff --git a/Helios/HeliosConfig.h b/Helios/HeliosConfig.h index cda27691..b7c575df 100644 --- a/Helios/HeliosConfig.h +++ b/Helios/HeliosConfig.h @@ -160,13 +160,13 @@ // Colorset Size // // the colorset is just an array of colors but it also has a num colors val -#define COLORSET_SIZE ((sizeof(RGBColor) * NUM_COLOR_SLOTS) + 1) +#define COLORSET_SIZE ((sizeof(rgb_color_t) * NUM_COLOR_SLOTS) + 1) // Pattern Args Size // // There is currently 6 args for a pattern: on, off, gap, dash, group, blend // Each takes up 1 byte currently -#define PAT_ARGS_SIZE (sizeof(PatternArgs)) +#define PAT_ARGS_SIZE (sizeof(pattern_args_t)) // Pattern Size // diff --git a/Helios/Led.cpp b/Helios/Led.cpp index 13052f1e..06ec5841 100644 --- a/Helios/Led.cpp +++ b/Helios/Led.cpp @@ -1,147 +1,190 @@ -#include - -#include "Led.h" - -#include "TimeControl.h" - -#include "HeliosConfig.h" -#include "Helios.h" - -#ifdef HELIOS_EMBEDDED -#ifdef HELIOS_ARDUINO -#include -#else -#include -#include -#include -#endif -#define PWM_PIN_R PB0 // Red channel (pin 5) -#define PWM_PIN_G PB1 // Green channel (pin 6) -#define PWM_PIN_B PB4 // Blue channel (pin 3) -#endif - -#define SCALE8(i, scale) (((uint16_t)i * (uint16_t)(scale)) >> 8) - -// array of led color values -RGBColor Led::m_ledColor = RGB_OFF; -RGBColor Led::m_realColor = RGB_OFF; -// global brightness -uint8_t Led::m_brightness = DEFAULT_BRIGHTNESS; - -bool Led::init() -{ - // clear the led colors - m_ledColor = RGB_OFF; - m_realColor = RGB_OFF; -#ifdef HELIOS_EMBEDDED -#ifdef HELIOS_ARDUINO - pinMode(0, OUTPUT); - pinMode(1, OUTPUT); - pinMode(4, OUTPUT); -#else - // pin ctrl done in Helios::init -#endif -#endif - return true; -} - -void Led::cleanup() -{ -} - -void Led::set(RGBColor col) -{ - m_ledColor = col; - m_realColor.red = SCALE8(m_ledColor.red, m_brightness); - m_realColor.green = SCALE8(m_ledColor.green, m_brightness); - m_realColor.blue = SCALE8(m_ledColor.blue, m_brightness); -} - -void Led::set(uint8_t r, uint8_t g, uint8_t b) -{ - set(RGBColor(r, g, b)); -} - -void Led::adjustBrightness(uint8_t fadeBy) -{ - m_ledColor.adjustBrightness(fadeBy); -} - -void Led::strobe(uint16_t on_time, uint16_t off_time, RGBColor off_col, RGBColor on_col) -{ - set(((Time::getCurtime() % (on_time + off_time)) > on_time) ? off_col : on_col); -} - -void Led::breath(uint8_t hue, uint32_t duration, uint8_t magnitude, uint8_t sat, uint8_t val) -{ - if (!duration) { - // don't divide by 0 - return; - } - // Determine the phase in the cycle - uint32_t phase = Time::getCurtime() % (2 * duration); - // Calculate hue shift - int32_t hueShift; - if (phase < duration) { - // Ascending phase - from hue to hue + magnitude - hueShift = (phase * magnitude) / duration; - } else { - // Descending phase - from hue + magnitude to hue - hueShift = ((2 * duration - phase) * magnitude) / duration; - } - // Apply hue shift - ensure hue stays within valid range - uint8_t shiftedHue = hue + hueShift; - // Apply the hsv color as a strobing hue shift - strobe(2, 13, RGB_OFF, HSVColor(shiftedHue, sat, val)); -} - -void Led::hold(RGBColor col) -{ - set(col); - update(); - Time::delayMilliseconds(250); -} - -void Led::setPWM(uint8_t pwmPin, uint8_t pwmValue, volatile uint8_t &controlRegister, - uint8_t controlBit, volatile uint8_t &compareRegister) -{ -#ifdef HELIOS_EMBEDDED - if (pwmValue == 0) { - // digitalWrite(pin, LOW) - controlRegister &= ~controlBit; // Disable PWM - PORTB &= ~(1 << pwmPin); // Set the pin low - } else if (pwmValue == 255) { - // digitalWrite(pin, HIGH) - controlRegister &= ~controlBit; // Disable PWM - PORTB |= (1 << pwmPin); // Set the pin high - } else { - // analogWrite(pin, value) - controlRegister |= controlBit; // Enable PWM - compareRegister = pwmValue; // Set PWM duty cycle - } -#endif -} - -void Led::update() -{ -#ifdef HELIOS_EMBEDDED - // write out the rgb values to analog pins -#ifdef HELIOS_ARDUINO - analogWrite(PWM_PIN_R, m_realColor.red); - analogWrite(PWM_PIN_G, m_realColor.green); - analogWrite(PWM_PIN_B, m_realColor.blue); -#else - // backup SREG and turn off interrupts - uint8_t oldSREG = SREG; - cli(); - - // set the PWM for R/G/B output - setPWM(PWM_PIN_R, m_realColor.red, TCCR0A, (1 << COM0A1), OCR0A); - setPWM(PWM_PIN_G, m_realColor.green, TCCR0A, (1 << COM0B1), OCR0B); - setPWM(PWM_PIN_B, m_realColor.blue, GTCCR, (1 << COM1B1), OCR1B); - - // turn interrupts back on - SREG = oldSREG; -#endif -#endif -} +#include + +#include "Led.h" + +#include "TimeControl.h" + +#include "HeliosConfig.h" + +#ifdef HELIOS_EMBEDDED +#ifdef HELIOS_ARDUINO +#include +#else +#include +#include +#include +#endif +#define PWM_PIN_R PB0 /* Red channel (pin 5) */ +#define PWM_PIN_G PB1 /* Green channel (pin 6) */ +#define PWM_PIN_B PB4 /* Blue channel (pin 3) */ +#endif + +#define SCALE8(i, scale) (((uint16_t)i * (uint16_t)(scale)) >> 8) + +/* Forward declaration */ +static void led_set_pwm(uint8_t pwmPin, uint8_t pwmValue, volatile uint8_t *controlRegister, + uint8_t controlBit, volatile uint8_t *compareRegister); + +/* array of led color values */ +static rgb_color_t m_ledColor; +static rgb_color_t m_realColor; +/* global brightness */ +static uint8_t m_brightness = DEFAULT_BRIGHTNESS; + +uint8_t led_init(void) +{ + /* clear the led colors */ + rgb_init_from_raw(&m_ledColor, RGB_OFF); + rgb_init_from_raw(&m_realColor, RGB_OFF); +#ifdef HELIOS_EMBEDDED +#ifdef HELIOS_ARDUINO + pinMode(0, OUTPUT); + pinMode(1, OUTPUT); + pinMode(4, OUTPUT); +#else + /* pin ctrl done in helios_init */ +#endif +#endif + return 1; +} + +void led_cleanup(void) +{ +} + +void led_set_rgb(const rgb_color_t *col) +{ + rgb_copy(&m_ledColor, col); + m_realColor.red = SCALE8(m_ledColor.red, m_brightness); + m_realColor.green = SCALE8(m_ledColor.green, m_brightness); + m_realColor.blue = SCALE8(m_ledColor.blue, m_brightness); +} + +void led_set_rgb3(uint8_t r, uint8_t g, uint8_t b) +{ + rgb_color_t col; + rgb_init3(&col, r, g, b); + led_set_rgb(&col); +} + +void led_clear(void) +{ + rgb_color_t off; + rgb_init_from_raw(&off, RGB_OFF); + led_set_rgb(&off); +} + +void led_adjust_brightness(uint8_t fadeBy) +{ + rgb_adjust_brightness(&m_ledColor, fadeBy); +} + +void led_strobe(uint16_t on_time, uint16_t off_time, const rgb_color_t *off_col, const rgb_color_t *on_col) +{ + if ((time_get_current_time() % (on_time + off_time)) > on_time) { + led_set_rgb(off_col); + } else { + led_set_rgb(on_col); + } +} + +void led_breath(uint8_t hue, uint32_t duration, uint8_t magnitude, uint8_t sat, uint8_t val) +{ + if (!duration) { + /* don't divide by 0 */ + return; + } + /* Determine the phase in the cycle */ + uint32_t phase = time_get_current_time() % (2 * duration); + /* Calculate hue shift */ + int32_t hueShift; + if (phase < duration) { + /* Ascending phase - from hue to hue + magnitude */ + hueShift = (phase * magnitude) / duration; + } else { + /* Descending phase - from hue + magnitude to hue */ + hueShift = ((2 * duration - phase) * magnitude) / duration; + } + /* Apply hue shift - ensure hue stays within valid range */ + uint8_t shiftedHue = hue + hueShift; + /* Apply the hsv color as a strobing hue shift */ + hsv_color_t hsv; + rgb_color_t off, on; + hsv_init3(&hsv, shiftedHue, sat, val); + rgb_init_from_raw(&off, RGB_OFF); + rgb_init_from_hsv(&on, &hsv); + led_strobe(2, 13, &off, &on); +} + +void led_hold(const rgb_color_t *col) +{ + led_set_rgb(col); + led_update(); + time_delay_milliseconds(250); +} + +static void led_set_pwm(uint8_t pwmPin, uint8_t pwmValue, volatile uint8_t *controlRegister, + uint8_t controlBit, volatile uint8_t *compareRegister) +{ +#ifdef HELIOS_EMBEDDED + if (pwmValue == 0) { + /* digitalWrite(pin, LOW) */ + *controlRegister &= ~controlBit; /* Disable PWM */ + PORTB &= ~(1 << pwmPin); /* Set the pin low */ + } else if (pwmValue == 255) { + /* digitalWrite(pin, HIGH) */ + *controlRegister &= ~controlBit; /* Disable PWM */ + PORTB |= (1 << pwmPin); /* Set the pin high */ + } else { + /* analogWrite(pin, value) */ + *controlRegister |= controlBit; /* Enable PWM */ + *compareRegister = pwmValue; /* Set PWM duty cycle */ + } +#else + (void)pwmPin; + (void)pwmValue; + (void)controlRegister; + (void)controlBit; + (void)compareRegister; +#endif +} + +rgb_color_t led_get(void) +{ + return m_ledColor; +} + +uint8_t led_get_brightness(void) +{ + return m_brightness; +} + +void led_set_brightness(uint8_t brightness) +{ + m_brightness = brightness; +} + +void led_update(void) +{ +#ifdef HELIOS_EMBEDDED + /* write out the rgb values to analog pins */ +#ifdef HELIOS_ARDUINO + analogWrite(PWM_PIN_R, m_realColor.red); + analogWrite(PWM_PIN_G, m_realColor.green); + analogWrite(PWM_PIN_B, m_realColor.blue); +#else + /* backup SREG and turn off interrupts */ + uint8_t oldSREG = SREG; + cli(); + + /* set the PWM for R/G/B output */ + led_set_pwm(PWM_PIN_R, m_realColor.red, &TCCR0A, (1 << COM0A1), &OCR0A); + led_set_pwm(PWM_PIN_G, m_realColor.green, &TCCR0A, (1 << COM0B1), &OCR0B); + led_set_pwm(PWM_PIN_B, m_realColor.blue, >CCR, (1 << COM1B1), &OCR1B); + + /* turn interrupts back on */ + SREG = oldSREG; +#endif +#endif +} + diff --git a/Helios/Led.h b/Helios/Led.h index 34c5faad..e0f1bff8 100644 --- a/Helios/Led.h +++ b/Helios/Led.h @@ -5,57 +5,40 @@ #include "Colortypes.h" -class Led -{ - // private unimplemented constructor - Led(); - -public: - // opting for static class here because there should only ever be one - // Led control object and I don't like singletons - static bool init(); - static void cleanup(); - - // control individual LED, these are appropriate to use in internal pattern logic - static void set(RGBColor col); - static void set(uint8_t r, uint8_t g, uint8_t b); - - // Turn off individual LEDs, these are appropriate to use in internal pattern logic - static void clear() { set(RGB_OFF); } - - // Dim individual LEDs, these are appropriate to use in internal pattern logic - static void adjustBrightness(uint8_t fadeBy); - - // strobe between two colors with a simple on/off timing - static void strobe(uint16_t on_time, uint16_t off_time, RGBColor col1, RGBColor col2); - - // breath the hue on an index - // warning: these use hsv to rgb in realtime! - static void breath(uint8_t hue, uint32_t duration = 1000, uint8_t magnitude = 60, - uint8_t sat = 255, uint8_t val = 255); - - // a very specialized api to hold all leds on a color for 250ms - static void hold(RGBColor col); - - // get the RGBColor of an Led index - static RGBColor get() { return m_ledColor; } - - // global 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(); - -private: - static void setPWM(uint8_t pwmPin, uint8_t pwmValue, volatile uint8_t &controlRegister, - uint8_t controlBit, volatile uint8_t &compareRegister); - - // the global brightness - static uint8_t m_brightness; - // led color - static RGBColor m_ledColor; - static RGBColor m_realColor; -}; +/* opting for static functions here because there should only ever be one + * Led control object and I don't like singletons */ + +uint8_t led_init(void); +void led_cleanup(void); + +/* control individual LED, these are appropriate to use in internal pattern logic */ +void led_set_rgb(const rgb_color_t *col); +void led_set_rgb3(uint8_t r, uint8_t g, uint8_t b); + +/* Turn off individual LEDs, these are appropriate to use in internal pattern logic */ +void led_clear(void); + +/* Dim individual LEDs, these are appropriate to use in internal pattern logic */ +void led_adjust_brightness(uint8_t fadeBy); + +/* strobe between two colors with a simple on/off timing */ +void led_strobe(uint16_t on_time, uint16_t off_time, const rgb_color_t *col1, const rgb_color_t *col2); + +/* breath the hue on an index + * warning: these use hsv to rgb in realtime! */ +void led_breath(uint8_t hue, uint32_t duration, uint8_t magnitude, uint8_t sat, uint8_t val); + +/* a very specialized api to hold all leds on a color for 250ms */ +void led_hold(const rgb_color_t *col); + +/* get the RGBColor of an Led index */ +rgb_color_t led_get(void); + +/* global brightness */ +uint8_t led_get_brightness(void); +void led_set_brightness(uint8_t brightness); + +/* actually update the LEDs and show the changes */ +void led_update(void); #endif diff --git a/Helios/Pattern.cpp b/Helios/Pattern.cpp index 19c4497e..8dfd4a5d 100644 --- a/Helios/Pattern.cpp +++ b/Helios/Pattern.cpp @@ -1,282 +1,375 @@ #include "Pattern.h" -//#include "../Patterns/PatternBuilder.h" #include "TimeControl.h" #include "Colorset.h" #include "HeliosConfig.h" #include "Led.h" -#include // for memcpy +#include /* for memcpy */ -// 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 +/* Forward declarations for internal functions */ +static void pattern_on_blink_on(pattern_t *pat); +static void pattern_on_blink_off(pattern_t *pat); +static void pattern_begin_gap(pattern_t *pat); +static void pattern_begin_dash(pattern_t *pat); +static void pattern_next_state(pattern_t *pat, uint8_t timing); +static void pattern_blend_blink_on(pattern_t *pat); +static void pattern_interpolate(uint8_t *current, const uint8_t next, uint8_t blend_speed); +static void pattern_tick_fade(pattern_t *pat); -#ifdef DEBUG_BASIC_PATTERN -#include "../../Time/TimeControl.h" -#include -// print out the current state of the pattern -#define PRINT_STATE(state) printState(state) -static void printState(PatternState state) +/* ================================== + * Pattern Args Functions */ + +void pattern_args_init(pattern_args_t *args, uint8_t on, uint8_t off, uint8_t gap, + uint8_t dash, uint8_t group, uint8_t blend, uint8_t fade) { - 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(); + args->on_dur = on; + args->off_dur = off; + args->gap_dur = gap; + args->dash_dur = dash; + args->group_size = group; + args->blend_speed = blend; + args->fade_dur = fade; } -#else -#define PRINT_STATE(state) // do nothing -#endif - -Pattern::Pattern(uint8_t onDur, uint8_t offDur, uint8_t gap, - uint8_t dash, uint8_t group, uint8_t blend) : - m_args(onDur, offDur, gap, dash, group, blend), - m_patternFlags(0), - m_colorset(), - m_groupCounter(0), - m_state(STATE_BLINK_ON), - m_blinkTimer(), - m_cur(), - m_next() + +/* ================================== + * Pattern Functions */ + +void pattern_init(pattern_t *pat, uint8_t onDur, uint8_t offDur, uint8_t gap, + uint8_t dash, uint8_t group, uint8_t blend, uint8_t fade) { + pattern_args_init(&pat->m_args, onDur, offDur, gap, dash, group, blend, fade); + pat->m_patternFlags = 0; + colorset_init(&pat->m_colorset); + pat->m_groupCounter = 0; + pat->m_state = STATE_BLINK_ON; + timer_init_default(&pat->m_blinkTimer); + rgb_init(&pat->m_cur); + rgb_init(&pat->m_next); + pat->m_fadeValue = 0; + pat->m_fadeStartTime = 0; } -Pattern::Pattern(const PatternArgs &args) : - Pattern(args.on_dur, args.off_dur, args.gap_dur, - args.dash_dur, args.group_size, args.blend_speed) +void pattern_init_with_args(pattern_t *pat, const pattern_args_t *args) { + pattern_init(pat, args->on_dur, args->off_dur, args->gap_dur, + args->dash_dur, args->group_size, args->blend_speed, args->fade_dur); } -Pattern::~Pattern() +void pattern_init_state(pattern_t *pat) { + colorset_reset_index(&pat->m_colorset); + + /* Reset the fade start time to the current time */ + pat->m_fadeStartTime = time_get_current_time(); + + /* the default state to begin with */ + pat->m_state = STATE_BLINK_ON; + /* if a dash is present then always start with the dash because + * it consumes the first color in the colorset */ + if (pat->m_args.dash_dur > 0) { + pat->m_state = STATE_BEGIN_DASH; + } + /* if there's no on duration or dash duration the led is just disabled */ + if ((!pat->m_args.on_dur && !pat->m_args.dash_dur) || !colorset_num_colors(&pat->m_colorset)) { + pat->m_state = STATE_DISABLED; + } + pat->m_groupCounter = pat->m_args.group_size ? pat->m_args.group_size : (colorset_num_colors(&pat->m_colorset) - (pat->m_args.dash_dur != 0)); + + if (pat->m_args.blend_speed > 0) { + /* convert current/next colors to HSV but only if we are doing a blend */ + pat->m_cur = colorset_get_next(&pat->m_colorset); + pat->m_next = colorset_get_next(&pat->m_colorset); + } else if (pat->m_args.fade_dur) { + /* if there is a fade dur and no blend need to iterate colorset */ + colorset_get_next(&pat->m_colorset); + } + + /* Initialize the fluctuating fade value */ + pat->m_fadeValue = 0; } -void Pattern::init() +static void pattern_tick_fade(pattern_t *pat) { - m_colorset.resetIndex(); - - // the default state to begin with - m_state = STATE_BLINK_ON; - // if a dash is present then always start with the dash because - // it consumes the first color in the colorset - if (m_args.dash_dur > 0) { - m_state = STATE_BEGIN_DASH; + uint32_t now = time_get_current_time(); + /* Calculate relative time since pattern was initialized */ + uint32_t relativeTime = now - pat->m_fadeStartTime; + uint32_t duration = pat->m_args.fade_dur * 10; + + /* only tick forward every fade_dur ticks */ + if (!relativeTime || (relativeTime % duration) != 0) { + return; } - // if there's no on duration or dash duration the led is just disabled - if ((!m_args.on_dur && !m_args.dash_dur) || !m_colorset.numColors()) { - m_state = STATE_DISABLED; + + /* count the number of steps based on relative time */ + uint32_t steps = relativeTime / duration; + uint32_t range = pat->m_args.off_dur; + + /* make sure the range is non-zero */ + if (range == 0) { + pat->m_fadeValue = 0; + return; } - m_groupCounter = m_args.group_size ? m_args.group_size : (m_colorset.numColors() - (m_args.dash_dur != 0)); - if (m_args.blend_speed > 0) { - // convert current/next colors to HSV but only if we are doing a blend - m_cur = m_colorset.getNext(); - m_next = m_colorset.getNext(); + uint32_t double_range = range * 2; + uint32_t step = steps % double_range; + + /* Triangle wave: up from 0 to range, then down to 0 */ + pat->m_fadeValue = (step < range) ? step : (double_range - step - 1); + + /* iterate color when at lowest point */ + if (step == 0) { + colorset_get_next(&pat->m_colorset); } } -void Pattern::play() +void pattern_play(pattern_t *pat) { - // 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 + /* tick forward the fade logic each tick */ + if (pattern_is_fade(pat)) { + pattern_tick_fade(pat); + } + + /* 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) { + /* its kinda evolving as i go */ + switch (pat->m_state) { case STATE_DISABLED: return; case STATE_BLINK_ON: - if (m_args.on_dur > 0) { - onBlinkOn(); - --m_groupCounter; - nextState(m_args.on_dur); + if (pat->m_args.on_dur > 0) { + pattern_on_blink_on(pat); + --pat->m_groupCounter; + /* When in ON state, use current fading on-time */ + pattern_next_state(pat, pat->m_args.on_dur + pat->m_fadeValue); return; } - m_state = STATE_BLINK_OFF; + pat->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 our colorset isn't at the end yet - if (m_groupCounter > 0 || (!m_args.gap_dur && !m_args.dash_dur)) { - if (m_args.off_dur > 0) { - onBlinkOff(); - nextState(m_args.off_dur); + /* the whole 'should blink off' situation is tricky because we might need + * to go back to blinking on if our colorset isn't at the end yet */ + if (pat->m_groupCounter > 0 || (!pat->m_args.gap_dur && !pat->m_args.dash_dur)) { + if (pat->m_args.off_dur > 0) { + pattern_on_blink_off(pat); + pattern_next_state(pat, pat->m_args.off_dur - pat->m_fadeValue); return; } - if (m_groupCounter > 0 && m_args.on_dur > 0) { - m_state = STATE_BLINK_ON; + if (pat->m_groupCounter > 0 && pat->m_args.on_dur > 0) { + pat->m_state = STATE_BLINK_ON; goto replay; } } - m_state = STATE_BEGIN_GAP; + pat->m_state = STATE_BEGIN_GAP; case STATE_BEGIN_GAP: - m_groupCounter = m_args.group_size ? m_args.group_size : (m_colorset.numColors() - (m_args.dash_dur != 0)); - if (m_args.gap_dur > 0) { - beginGap(); - nextState(m_args.gap_dur); + pat->m_groupCounter = pat->m_args.group_size ? pat->m_args.group_size : (colorset_num_colors(&pat->m_colorset) - (pat->m_args.dash_dur != 0)); + if (pat->m_args.gap_dur > 0) { + pattern_begin_gap(pat); + pattern_next_state(pat, pat->m_args.gap_dur); return; } - m_state = STATE_BEGIN_DASH; + pat->m_state = STATE_BEGIN_DASH; case STATE_BEGIN_DASH: - if (m_args.dash_dur > 0) { - beginDash(); - nextState(m_args.dash_dur); + if (pat->m_args.dash_dur > 0) { + pattern_begin_dash(pat); + pattern_next_state(pat, pat->m_args.dash_dur); return; } - m_state = STATE_BEGIN_GAP2; + pat->m_state = STATE_BEGIN_GAP2; case STATE_BEGIN_GAP2: - if (m_args.dash_dur > 0 && m_args.gap_dur > 0) { - beginGap(); - nextState(m_args.gap_dur); + if (pat->m_args.dash_dur > 0 && pat->m_args.gap_dur > 0) { + pattern_begin_gap(pat); + pattern_next_state(pat, pat->m_args.gap_dur); return; } - m_state = STATE_BLINK_ON; + pat->m_state = STATE_BLINK_ON; goto replay; default: break; } - if (!m_blinkTimer.alarm()) { - // no alarm triggered just stay in current state, return and don't transition states - PRINT_STATE(m_state); + if (!timer_alarm(&pat->m_blinkTimer)) { + /* no alarm triggered just stay in current state, return and don't transition states */ return; } - // this just transitions the state into the next state, with some edge conditions for - // 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)) { - // 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_args.on_dur ? STATE_BLINK_ON : (m_args.dash_dur ? 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; + /* this just transitions the state into the next state, with some edge conditions for + * transitioning to different states under certain circumstances. Honestly this is + * a nightmare to read now and idk how to fix it */ + if (pat->m_state == STATE_IN_GAP2 || (pat->m_state == STATE_OFF && pat->m_groupCounter > 0)) { + /* 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 */ + pat->m_state = pat->m_args.on_dur ? STATE_BLINK_ON : (pat->m_args.dash_dur ? STATE_BEGIN_DASH : STATE_BEGIN_GAP); + } else if (pat->m_state == STATE_OFF && (!pat->m_groupCounter || colorset_num_colors(&pat->m_colorset) == 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 */ + pat->m_state = (pat->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); + /* this is the standard case, iterate to the next state */ + pat->m_state = (enum pattern_state)(pat->m_state + 1); } - // poor-mans recurse with the new state change (this transitions to a new state within the same tick) + /* poor-mans recurse with the new state change (this transitions to a new state within the same tick) */ goto replay; } -// set args -void Pattern::setArgs(const PatternArgs &args) +void pattern_set_args(pattern_t *pat, const pattern_args_t *args) +{ + memcpy(&pat->m_args, args, sizeof(pattern_args_t)); +} + +pattern_args_t pattern_get_args(const pattern_t *pat) { - memcpy(&m_args, &args, sizeof(PatternArgs)); + return pat->m_args; } -void Pattern::onBlinkOn() +pattern_args_t *pattern_args_ptr(pattern_t *pat) { - PRINT_STATE(STATE_ON); - if (isBlend()) { - blendBlinkOn(); + return &pat->m_args; +} + +static void pattern_on_blink_on(pattern_t *pat) +{ + if (pattern_is_blend(pat)) { + pattern_blend_blink_on(pat); + return; + } + + /* Check if this is a fading duration pattern */ + if (pattern_is_fade(pat)) { + rgb_color_t cur_col = colorset_cur(&pat->m_colorset); + led_set_rgb(&cur_col); return; } - Led::set(m_colorset.getNext()); + + rgb_color_t next_col = colorset_get_next(&pat->m_colorset); + led_set_rgb(&next_col); +} + +static void pattern_on_blink_off(pattern_t *pat) +{ + (void)pat; /* unused */ + led_clear(); +} + +static void pattern_begin_gap(pattern_t *pat) +{ + (void)pat; /* unused */ + led_clear(); } -void Pattern::onBlinkOff() +static void pattern_begin_dash(pattern_t *pat) { - PRINT_STATE(STATE_OFF); - Led::clear(); + rgb_color_t next_col = colorset_get_next(&pat->m_colorset); + led_set_rgb(&next_col); } -void Pattern::beginGap() +static void pattern_next_state(pattern_t *pat, uint8_t timing) { - PRINT_STATE(STATE_IN_GAP); - Led::clear(); + timer_init(&pat->m_blinkTimer, timing); + pat->m_state = (enum pattern_state)(pat->m_state + 1); } -void Pattern::beginDash() +colorset_t pattern_get_colorset(const pattern_t *pat) { - PRINT_STATE(STATE_IN_DASH); - Led::set(m_colorset.getNext()); + return pat->m_colorset; } -void Pattern::nextState(uint8_t timing) +colorset_t *pattern_colorset_ptr(pattern_t *pat) { - m_blinkTimer.init(timing); - m_state = (PatternState)(m_state + 1); + return &pat->m_colorset; } -// change the colorset -void Pattern::setColorset(const Colorset &set) +void pattern_set_colorset(pattern_t *pat, const colorset_t *set) { - m_colorset = set; - init(); + colorset_copy(&pat->m_colorset, set); + pattern_init_state(pat); } -void Pattern::clearColorset() +void pattern_clear_colorset(pattern_t *pat) { - m_colorset.clear(); + colorset_clear(&pat->m_colorset); } -bool Pattern::equals(const Pattern *other) +uint8_t pattern_equals(const pattern_t *pat, const pattern_t *other) { if (!other) { - return false; + return 0; } - // compare the colorset - if (!m_colorset.equals(&other->m_colorset)) { - return false; + /* compare the colorset */ + if (!colorset_equals(&pat->m_colorset, &other->m_colorset)) { + return 0; } - // compare the args of each pattern for equality - if (memcmp(&m_args, &other->m_args, sizeof(PatternArgs)) == 0) { - return false; + /* compare the args of each pattern for equality */ + if (memcmp(&pat->m_args, &other->m_args, sizeof(pattern_args_t)) != 0) { + return 0; } - // if those match then it's effectively the same - // pattern even if anything else is different - return true; + /* if those match then it's effectively the same + * pattern even if anything else is different */ + return 1; } -void Pattern::updateColor(uint8_t index, const RGBColor &col) +void pattern_update_color(pattern_t *pat, uint8_t index, const rgb_color_t *col) { - m_colorset.set(index, col); - init(); + colorset_set(&pat->m_colorset, index, *col); + pattern_init_state(pat); } -uint32_t Pattern::crc32() const +uint32_t pattern_crc32(const pattern_t *pat) { uint32_t hash = 5381; - for (uint8_t i = 0; i < PATTERN_SIZE; ++i) { - hash = ((hash << 5) + hash) + ((uint8_t *)this)[i]; + uint8_t i; + for (i = 0; i < PATTERN_SIZE; ++i) { + hash = ((hash << 5) + hash) + ((uint8_t *)pat)[i]; } return hash; } -void Pattern::blendBlinkOn() +uint32_t pattern_get_flags(const pattern_t *pat) +{ + return pat->m_patternFlags; +} + +uint8_t pattern_has_flags(const pattern_t *pat, uint32_t flags) +{ + return (pat->m_patternFlags & flags) != 0; +} + +uint8_t pattern_is_blend(const pattern_t *pat) +{ + return pat->m_args.blend_speed > 0; +} + +uint8_t pattern_is_fade(const pattern_t *pat) +{ + return pat->m_args.fade_dur > 0; +} + +static void pattern_blend_blink_on(pattern_t *pat) { - // if we reached the next color, then cycle the colorset - // like normal and begin playing the next color - if (m_cur == m_next) { - m_next = m_colorset.getNext(); + /* if we reached the next color, then cycle the colorset + * like normal and begin playing the next color */ + if (rgb_equals(&pat->m_cur, &pat->m_next)) { + pat->m_next = colorset_get_next(&pat->m_colorset); } - // interpolate to the next color - interpolate(m_cur.red, m_next.red); - interpolate(m_cur.green, m_next.green); - interpolate(m_cur.blue, m_next.blue); - // set the color - Led::set(m_cur); + /* interpolate to the next color */ + pattern_interpolate(&pat->m_cur.red, pat->m_next.red, pat->m_args.blend_speed); + pattern_interpolate(&pat->m_cur.green, pat->m_next.green, pat->m_args.blend_speed); + pattern_interpolate(&pat->m_cur.blue, pat->m_next.blue, pat->m_args.blend_speed); + /* set the color */ + led_set_rgb(&pat->m_cur); } -void Pattern::interpolate(uint8_t ¤t, const uint8_t next) +static void pattern_interpolate(uint8_t *current, const uint8_t next, uint8_t blend_speed) { - if (current < next) { - uint8_t step = (next - current) > m_args.blend_speed ? m_args.blend_speed : (next - current); - current += step; - } else if (current > next) { - uint8_t step = (current - next) > m_args.blend_speed ? m_args.blend_speed : (current - next); - current -= step; + if (*current < next) { + uint8_t step = (next - *current) > blend_speed ? blend_speed : (next - *current); + *current += step; + } else if (*current > next) { + uint8_t step = (*current - next) > blend_speed ? blend_speed : (*current - next); + *current -= step; } } + diff --git a/Helios/Pattern.h b/Helios/Pattern.h index fb78176f..248a6bad 100644 --- a/Helios/Pattern.h +++ b/Helios/Pattern.h @@ -6,129 +6,132 @@ #include "Timer.h" #include "Patterns.h" -// for specifying things like default args -struct PatternArgs { - PatternArgs(uint8_t on = 0, uint8_t off = 0, uint8_t gap = 0, uint8_t dash = 0, uint8_t group = 0, uint8_t blend = 0) : - on_dur(on), off_dur(off), gap_dur(gap), dash_dur(dash), group_size(group), blend_speed(blend) {} +/* Forward declarations */ +typedef struct pattern_args_t pattern_args_t; +typedef struct pattern_t pattern_t; + +/* for specifying things like default args */ +struct pattern_args_t { uint8_t on_dur; uint8_t off_dur; uint8_t gap_dur; uint8_t dash_dur; uint8_t group_size; uint8_t blend_speed; + uint8_t fade_dur; }; -class Pattern +/* Initialize pattern args with all parameters */ +void pattern_args_init(pattern_args_t *args, uint8_t on, uint8_t off, uint8_t gap, + uint8_t dash, uint8_t group, uint8_t blend, uint8_t fade); + +/* The various different blinking states the pattern can be in */ +enum pattern_state { -public: - // try to not set on duration to 0 - Pattern(uint8_t onDur = 1, uint8_t offDur = 0, uint8_t gap = 0, - uint8_t dash = 0, uint8_t group = 0, uint8_t blend = 0); - Pattern(const PatternArgs &args); - ~Pattern(); + /* 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, + STATE_ON, - // init the pattern to initial state - void init(); + /* the pattern is blinking off */ + STATE_BLINK_OFF, + STATE_OFF, - // play the pattern - void play(); + /* the pattern is starting a gap after a colorset */ + STATE_BEGIN_GAP, + STATE_IN_GAP, - // set/get args - void setArgs(const PatternArgs &args); - const PatternArgs getArgs() const { return m_args; } - PatternArgs getArgs() { return m_args; } - PatternArgs &args() { return m_args; } + /* the pattern is beginning a dash after a colorset or gap */ + STATE_BEGIN_DASH, + STATE_IN_DASH, - // change the colorset - const Colorset getColorset() const { return m_colorset; } - Colorset getColorset() { return m_colorset; } - Colorset &colorset() { return m_colorset; } - void setColorset(const Colorset &set); - void clearColorset(); + /* the pattern is starting a gap after a dash */ + STATE_BEGIN_GAP2, + STATE_IN_GAP2, +}; + +struct pattern_t +{ + /* ================================== + * Pattern Parameters */ + pattern_args_t m_args; - // comparison to other pattern - bool equals(const Pattern *other); + /* ================================== + * Pattern Members */ - // set a color in the colorset and re-initialize - void updateColor(uint8_t index, const RGBColor &col); + /* any flags the pattern has */ + uint8_t m_patternFlags; + /* a copy of the colorset that this pattern is initialized with */ + colorset_t m_colorset; - // calculate crc of the colorset + pattern - uint32_t crc32() const; + /* ================================== + * Blink Members */ + uint8_t m_groupCounter; - // get the pattern flags - uint32_t getFlags() const { return m_patternFlags; } - bool hasFlags(uint32_t flags) const { return (m_patternFlags & flags) != 0; } + /* the state of the current pattern */ + enum pattern_state m_state; - // whether blend speed is non 0 - bool isBlend() const { return m_args.blend_speed > 0; } + /* the blink timer used to measure blink timings */ + timer_t m_blinkTimer; -protected: - // ================================== - // Pattern Parameters - PatternArgs m_args; + /* ================================== + * Blend Members */ - // ================================== - // Pattern Members + /* current color and target blend color */ + rgb_color_t m_cur; + rgb_color_t m_next; - // any flags the pattern has - uint8_t m_patternFlags; - // a copy of the colorset that this pattern is initialized with - Colorset m_colorset; + /* ================================== + * Fade Members */ - // ================================== - // Blink Members - uint8_t m_groupCounter; + /* shifting value to represent current fade */ + uint8_t m_fadeValue; - // apis for blink - void onBlinkOn(); - void onBlinkOff(); - void beginGap(); - void beginDash(); - void nextState(uint8_t timing); + /* Add a member variable to store when the pattern was last initialized */ + uint32_t m_fadeStartTime; +}; - // 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, +/* try to not set on duration to 0 */ +void pattern_init(pattern_t *pat, uint8_t onDur, uint8_t offDur, uint8_t gap, + uint8_t dash, uint8_t group, uint8_t blend, uint8_t fade); +void pattern_init_with_args(pattern_t *pat, const pattern_args_t *args); - // the pattern is blinking on the next color in the set - STATE_BLINK_ON, - STATE_ON, +/* init the pattern to initial state */ +void pattern_init_state(pattern_t *pat); - // the pattern is blinking off - STATE_BLINK_OFF, - STATE_OFF, +/* play the pattern */ +void pattern_play(pattern_t *pat); - // the pattern is starting a gap after a colorset - STATE_BEGIN_GAP, - STATE_IN_GAP, +/* set/get args */ +void pattern_set_args(pattern_t *pat, const pattern_args_t *args); +pattern_args_t pattern_get_args(const pattern_t *pat); +pattern_args_t *pattern_args_ptr(pattern_t *pat); - // the pattern is beginning a dash after a colorset or gap - STATE_BEGIN_DASH, - STATE_IN_DASH, +/* change the colorset */ +colorset_t pattern_get_colorset(const pattern_t *pat); +colorset_t *pattern_colorset_ptr(pattern_t *pat); +void pattern_set_colorset(pattern_t *pat, const colorset_t *set); +void pattern_clear_colorset(pattern_t *pat); - // the pattern is starting a gap after a dash - STATE_BEGIN_GAP2, - STATE_IN_GAP2, - }; +/* comparison to other pattern */ +uint8_t pattern_equals(const pattern_t *pat, const pattern_t *other); - // the state of the current pattern - PatternState m_state; +/* set a color in the colorset and re-initialize */ +void pattern_update_color(pattern_t *pat, uint8_t index, const rgb_color_t *col); - // the blink timer used to measure blink timings - Timer m_blinkTimer; +/* calculate crc of the colorset + pattern */ +uint32_t pattern_crc32(const pattern_t *pat); - // ================================== - // Blend Members +/* get the pattern flags */ +uint32_t pattern_get_flags(const pattern_t *pat); +uint8_t pattern_has_flags(const pattern_t *pat, uint32_t flags); - // current color and target blend color - RGBColor m_cur; - RGBColor m_next; +/* whether blend speed is non 0 */ +uint8_t pattern_is_blend(const pattern_t *pat); - // apis for blend - void blendBlinkOn(); - void interpolate(uint8_t ¤t, const uint8_t next); -}; +/* whether fade speed is non 0 */ +uint8_t pattern_is_fade(const pattern_t *pat); #endif diff --git a/Helios/Patterns.cpp b/Helios/Patterns.cpp index c6fa555f..044d2000 100644 --- a/Helios/Patterns.cpp +++ b/Helios/Patterns.cpp @@ -1,196 +1,201 @@ -#include "Patterns.h" - -#include "Storage.h" -#include "Pattern.h" - -// define arrays of colors, you can reuse these if you have multiple -// modes that use the same colorset -- these demonstrate the max amount -// of colors in each set but you can absolutely list a lesser amount -static const uint32_t color_codes0[] = {RGB_RED, RGB_ORANGE, RGB_YELLOW, RGB_TURQUOISE, RGB_BLUE, RGB_PINK}; -static const uint32_t color_codes1[] = {RGB_RED, RGB_CORAL_ORANGE_SAT_MEDIUM, RGB_ORANGE, RGB_YELLOW_SAT_LOW}; -static const uint32_t color_codes2[] = {RGB_PURPLE_BRI_LOWEST, RGB_MAGENTA, RGB_HOT_PINK_SAT_MEDIUM, RGB_PINK_SAT_LOWEST}; -static const uint32_t color_codes3[] = {RGB_WHITE, RGB_BLUE_BRI_LOWEST, RGB_BLUE_BRI_LOWEST, RGB_BLUE_BRI_LOWEST, RGB_BLUE_BRI_LOWEST, RGB_BLUE_BRI_LOWEST}; -static const uint32_t color_codes4[] = {RGB_MAGENTA_BRI_LOWEST, RGB_ROYAL_BLUE_BRI_LOW, RGB_TURQUOISE, RGB_ROYAL_BLUE_BRI_LOW, RGB_MAGENTA_BRI_LOWEST, RGB_OFF}; -static const uint32_t color_codes5[] = {RGB_RED, RGB_HOT_PINK, RGB_ROYAL_BLUE, RGB_BLUE, RGB_GREEN, RGB_YELLOW}; - -// Define Colorset configurations for each slot -struct default_colorset { - uint8_t num_cols; - const uint32_t *cols; -}; - -// the array of colorset entries, make sure the number on the left reflects -// the number of colors in the array on the right -static const default_colorset default_colorsets[] = { - { 6, color_codes0 }, // 0 Lightside - { 4, color_codes1 }, // 1 Sauna - { 4, color_codes2 }, // 2 Butterfly - { 6, color_codes3 }, // 3 Freezer Burn - { 6, color_codes4 }, // 4 Ice Blade - { 6, color_codes5 }, // 5 Rainbow Glitter -}; - -void Patterns::make_default(uint8_t index, Pattern &pat) -{ - if (index >= NUM_MODE_SLOTS) { - return; - } - PatternArgs args; - switch (index) { - case 0: // Lightside - args.on_dur = 2; - args.gap_dur = 40; - break; - case 1: // Sauna - args.on_dur = 1; - args.off_dur = 9; - break; - case 2: // Butterfly - args.on_dur = 1; - args.off_dur = 9; - args.gap_dur = 6; - args.dash_dur = 15; - break; - case 3: // Freezer Burn - args.on_dur = 1; - args.off_dur = 9; - args.dash_dur = 5; - break; - case 4: // Ice Blade - args.on_dur = 3; - args.off_dur = 1; - args.gap_dur = 30; - break; - case 5: // Rainbow Glitter - args.on_dur = 1; - args.off_dur = 50; - break; - } - // assign default args - pat.setArgs(args); - // build the set out of the defaults - Colorset set(default_colorsets[index].num_cols, default_colorsets[index].cols); - // assign default colorset - pat.setColorset(set); -} - -void Patterns::make_pattern(PatternID id, Pattern &pat) -{ - PatternArgs args; - switch (id) - { - default: - - case PATTERN_RIBBON: - args.on_dur = 9; // 10 for flashing pattern circles - break; - - case PATTERN_ULTRA_DOPS: - args.on_dur = 1; - args.off_dur = 3; - break; - - case PATTERN_DOPS: - args.on_dur = 1; - args.off_dur = 9; - break; - - case PATTERN_STROBE: - args.on_dur = 5; - args.off_dur = 8; // 10 for flashing pattern circles - break; - - case PATTERN_HYPNOSTROBE: - args.on_dur = 14; - args.off_dur = 10; - break; - - case PATTERN_STROBIE: - args.on_dur = 3; - args.off_dur = 23; // 21 for flashing pattern circles - break; - - case PATTERN_RAZOR: - args.on_dur = 3; - args.off_dur = 1; - args.gap_dur = 30; // 29 for flashing pattern circles - break; - - case PATTERN_FLARE: - args.on_dur = 2; - args.off_dur = 30; // 28 for flashing pattern circles - break; - - case PATTERN_BURST: - args.on_dur = 3; - args.off_dur = 40; // 37 for flashing pattern circles - break; - - case PATTERN_GLOW: - args.on_dur = 2; - args.gap_dur = 40; // 39 for flashing pattern circles - break; - - case PATTERN_FLICKER: - args.on_dur = 1; - args.off_dur = 50; // 44 for flashing pattern circles - break; - - case PATTERN_FLASH: - args.on_dur = 10; - args.off_dur = 250; // 120 for flashing pattern circles - break; - - case PATTERN_MORPH: - args.on_dur = 9; - args.blend_speed = 5; // 14 for flashing pattern circles - break; - - case PATTERN_MORPH_STROBE: - args.on_dur = 5; - args.off_dur = 8; - args.blend_speed = 10; // 19 for flashing pattern circles - break; - - case PATTERN_MORPH_STROBIE: - args.on_dur = 3; - args.off_dur = 23; - args.blend_speed = 10; // 35 for flashing pattern circles - break; - - case PATTERN_MORPH_GLOW: - args.on_dur = 1; - args.off_dur = 3; - args.gap_dur = 40; // 36 for flashing pattern circles - args.blend_speed = 30; - break; - - case PATTERN_DASH_DOPS: - args.on_dur = 1; - args.off_dur = 9; - args.gap_dur = 6; - args.dash_dur = 15; // 17 for flashing pattern circles - break; - - case PATTERN_DASH_DOT: - args.on_dur = 2; - args.off_dur = 3; - args.dash_dur = 24; // 22 for flashing pattern circles - break; - - case PATTERN_WAVE_PARTICLE: - args.on_dur = 1; - args.off_dur = 9; - args.dash_dur = 5; // 10 for flashing pattern circles - break; - - case PATTERN_LIGHTSPEED: - args.on_dur = 2; - args.off_dur = 3; - args.dash_dur = 24; // 23 for flashing pattern circles - args.blend_speed = 10; - break; - } - - pat.setArgs(args); -} +#include "Patterns.h" + +#include "Storage.h" +#include "Pattern.h" +#include "ColorConstants.h" + +/* define arrays of colors, you can reuse these if you have multiple + * modes that use the same colorset -- these demonstrate the max amount + * of colors in each set but you can absolutely list a lesser amount */ +static const uint32_t color_codes0[] = {RGB_RED, RGB_ORANGE, RGB_YELLOW, RGB_TURQUOISE, RGB_BLUE, RGB_PINK}; +static const uint32_t color_codes1[] = {RGB_RED, RGB_CORAL_ORANGE_SAT_MEDIUM, RGB_ORANGE, RGB_YELLOW_SAT_LOW}; +static const uint32_t color_codes2[] = {RGB_PURPLE_BRI_LOWEST, RGB_MAGENTA, RGB_HOT_PINK_SAT_MEDIUM, RGB_PINK_SAT_LOWEST}; +static const uint32_t color_codes3[] = {RGB_WHITE, RGB_BLUE_BRI_LOWEST, RGB_BLUE_BRI_LOWEST, RGB_BLUE_BRI_LOWEST, RGB_BLUE_BRI_LOWEST, RGB_BLUE_BRI_LOWEST}; +static const uint32_t color_codes4[] = {RGB_MAGENTA_BRI_LOWEST, RGB_ROYAL_BLUE_BRI_LOW, RGB_TURQUOISE, RGB_ROYAL_BLUE_BRI_LOW, RGB_MAGENTA_BRI_LOWEST, RGB_OFF}; +static const uint32_t color_codes5[] = {RGB_RED, RGB_HOT_PINK, RGB_ROYAL_BLUE, RGB_BLUE, RGB_GREEN, RGB_YELLOW}; + +/* Define Colorset configurations for each slot */ +struct default_colorset_t { + uint8_t num_cols; + const uint32_t *cols; +}; + +/* the array of colorset entries, make sure the number on the left reflects + * the number of colors in the array on the right */ +static const struct default_colorset_t default_colorsets[] = { + { 6, color_codes0 }, /* 0 Lightside */ + { 4, color_codes1 }, /* 1 Sauna */ + { 4, color_codes2 }, /* 2 Butterfly */ + { 6, color_codes3 }, /* 3 Freezer Burn */ + { 6, color_codes4 }, /* 4 Ice Blade */ + { 6, color_codes5 }, /* 5 Rainbow Glitter */ +}; + +void patterns_make_default(uint8_t index, pattern_t *pat) +{ + if (index >= NUM_MODE_SLOTS) { + return; + } + pattern_args_t args; + pattern_args_init(&args, 0, 0, 0, 0, 0, 0, 0); + switch (index) { + case 0: /* Lightside */ + args.on_dur = 2; + args.gap_dur = 40; + break; + case 1: /* Sauna */ + args.on_dur = 1; + args.off_dur = 9; + break; + case 2: /* Butterfly */ + args.on_dur = 1; + args.off_dur = 9; + args.gap_dur = 6; + args.dash_dur = 15; + break; + case 3: /* Freezer Burn */ + args.on_dur = 1; + args.off_dur = 9; + args.dash_dur = 5; + break; + case 4: /* Ice Blade */ + args.on_dur = 3; + args.off_dur = 1; + args.gap_dur = 30; + break; + case 5: /* Rainbow Glitter */ + args.on_dur = 1; + args.off_dur = 50; + break; + } + /* assign default args */ + pattern_set_args(pat, &args); + /* build the set out of the defaults */ + colorset_t set; + colorset_init_array(&set, default_colorsets[index].num_cols, default_colorsets[index].cols); + /* assign default colorset */ + pattern_set_colorset(pat, &set); +} + +void patterns_make_pattern(enum pattern_id id, pattern_t *pat) +{ + pattern_args_t args; + pattern_args_init(&args, 0, 0, 0, 0, 0, 0, 0); + switch (id) + { + default: + + case PATTERN_RIBBON: + args.on_dur = 9; /* 10 for flashing pattern circles */ + break; + + case PATTERN_ULTRA_DOPS: + args.on_dur = 1; + args.off_dur = 3; + break; + + case PATTERN_DOPS: + args.on_dur = 1; + args.off_dur = 9; + break; + + case PATTERN_STROBE: + args.on_dur = 5; + args.off_dur = 8; /* 10 for flashing pattern circles */ + break; + + case PATTERN_HYPNOSTROBE: + args.on_dur = 14; + args.off_dur = 10; + break; + + case PATTERN_STROBIE: + args.on_dur = 3; + args.off_dur = 23; /* 21 for flashing pattern circles */ + break; + + case PATTERN_RAZOR: + args.on_dur = 3; + args.off_dur = 1; + args.gap_dur = 30; /* 29 for flashing pattern circles */ + break; + + case PATTERN_FLARE: + args.on_dur = 2; + args.off_dur = 30; /* 28 for flashing pattern circles */ + break; + + case PATTERN_BURST: + args.on_dur = 3; + args.off_dur = 40; /* 37 for flashing pattern circles */ + break; + + case PATTERN_GLOW: + args.on_dur = 2; + args.gap_dur = 40; /* 39 for flashing pattern circles */ + break; + + case PATTERN_FLICKER: + args.on_dur = 1; + args.off_dur = 50; /* 44 for flashing pattern circles */ + break; + + case PATTERN_FLASH: + args.on_dur = 10; + args.off_dur = 250; /* 120 for flashing pattern circles */ + break; + + case PATTERN_MORPH: + args.on_dur = 9; + args.blend_speed = 5; /* 14 for flashing pattern circles */ + break; + + case PATTERN_MORPH_STROBE: + args.on_dur = 5; + args.off_dur = 8; + args.blend_speed = 10; /* 19 for flashing pattern circles */ + break; + + case PATTERN_MORPH_STROBIE: + args.on_dur = 3; + args.off_dur = 23; + args.blend_speed = 10; /* 35 for flashing pattern circles */ + break; + + case PATTERN_MORPH_GLOW: + args.on_dur = 1; + args.off_dur = 3; + args.gap_dur = 40; /* 36 for flashing pattern circles */ + args.blend_speed = 30; + break; + + case PATTERN_DASH_DOPS: + args.on_dur = 1; + args.off_dur = 9; + args.gap_dur = 6; + args.dash_dur = 15; /* 17 for flashing pattern circles */ + break; + + case PATTERN_DASH_DOT: + args.on_dur = 2; + args.off_dur = 3; + args.dash_dur = 24; /* 22 for flashing pattern circles */ + break; + + case PATTERN_WAVE_PARTICLE: + args.on_dur = 1; + args.off_dur = 9; + args.dash_dur = 5; /* 10 for flashing pattern circles */ + break; + + case PATTERN_LIGHTSPEED: + args.on_dur = 2; + args.off_dur = 3; + args.dash_dur = 24; /* 23 for flashing pattern circles */ + args.blend_speed = 10; + break; + } + + pattern_set_args(pat, &args); +} + diff --git a/Helios/Patterns.h b/Helios/Patterns.h index a653a330..bc2d87f0 100644 --- a/Helios/Patterns.h +++ b/Helios/Patterns.h @@ -3,21 +3,24 @@ #include -// List of patterns that can be built, both single and multi-led patterns are found in this list. -// Within both single and multi LED pattern lists there are 'core' patterns which are associated -// with a class, and there are 'shell' patterns which are simply wrapperns around another pattern -// with different parameters passed to the constructor. There is no way to know which patterns -// are 'core' patterns, except by looking at PatternBuilder::generate to see which classes exist -enum PatternID : int8_t { - // no pattern at all, use this sparingly and default to - // PATTERN_FIRST when possible - PATTERN_NONE = (PatternID)-1, - - // first pattern of all +/* Forward declaration */ +typedef struct pattern_t pattern_t; + +/* List of patterns that can be built, both single and multi-led patterns are found in this list. + * Within both single and multi LED pattern lists there are 'core' patterns which are associated + * with a class, and there are 'shell' patterns which are simply wrappers around another pattern + * with different parameters passed to the constructor. There is no way to know which patterns + * are 'core' patterns, except by looking at PatternBuilder::generate to see which classes exist */ +enum pattern_id { + /* no pattern at all, use this sparingly and default to + * PATTERN_FIRST when possible */ + PATTERN_NONE = -1, + + /* first pattern of all */ PATTERN_FIRST = 0, - // ===================================== + /* ===================================== */ - // Strobe + /* Strobe */ PATTERN_RIBBON = PATTERN_FIRST, PATTERN_ULTRA_DOPS, PATTERN_DOPS, @@ -30,28 +33,25 @@ enum PatternID : int8_t { PATTERN_GLOW, PATTERN_FLICKER, PATTERN_FLASH, - // Morph + /* Morph */ PATTERN_MORPH, PATTERN_MORPH_STROBE, PATTERN_MORPH_STROBIE, PATTERN_MORPH_GLOW, - // Dash + /* Dash */ PATTERN_DASH_DOPS, PATTERN_DASH_DOT, PATTERN_WAVE_PARTICLE, PATTERN_LIGHTSPEED, - // Meta pattern constants + /* Meta pattern constants */ INTERNAL_PATTERNS_END, PATTERN_LAST = (INTERNAL_PATTERNS_END - 1), - PATTERN_COUNT = (PATTERN_LAST - PATTERN_FIRST) + 1, // total number of patterns + PATTERN_COUNT = (PATTERN_LAST - PATTERN_FIRST) + 1 /* total number of patterns */ }; -class Pattern; +/* Pattern creation functions */ +void patterns_make_default(uint8_t index, pattern_t *pat); +void patterns_make_pattern(enum pattern_id id, pattern_t *pat); -class Patterns { - public: - static void make_default(uint8_t index, Pattern &pat); - static void make_pattern(PatternID id, Pattern &pat); -}; #endif diff --git a/Helios/Random.cpp b/Helios/Random.cpp index fb10f201..8950ba08 100644 --- a/Helios/Random.cpp +++ b/Helios/Random.cpp @@ -1,45 +1,43 @@ #include "Random.h" -Random::Random() : - m_seed(0) +void random_init(random_t *rng) { + rng->m_seed = 0; } -Random::Random(uint32_t newseed) : - Random() +void random_init_seed(random_t *rng, uint32_t newseed) { - seed(newseed); + random_init(rng); + random_seed(rng, newseed); } -Random::~Random() -{ -} - -void Random::seed(uint32_t newseed) +void random_seed(random_t *rng, uint32_t newseed) { if (!newseed) { - m_seed = 42; + rng->m_seed = 42; + } else { + rng->m_seed = newseed; } - m_seed = newseed; } -uint16_t Random::next16(uint16_t minValue, uint16_t maxValue) +uint16_t random_next16(random_t *rng, uint16_t minValue, uint16_t maxValue) { - // walk the LCG forward to the next step - m_seed = (m_seed * 1103515245 + 12345) & 0x7FFFFFFF; + /* walk the LCG forward to the next step */ + rng->m_seed = (rng->m_seed * 1103515245 + 12345) & 0x7FFFFFFF; uint32_t range = maxValue - minValue; if (range != 0xFFFFFFFF) { - // shift the seed 16 bits to the right because the lower 16 bits - // of this LCG are apparently not uniform whatsoever, where as the - // upper 16 bits appear to be quite uniform as per tests. We don't - // really need 32bit random values so we offer max 16bits of entropy - return ((m_seed >> 16) % (range + 1)) + minValue; + /* shift the seed 16 bits to the right because the lower 16 bits + * of this LCG are apparently not uniform whatsoever, where as the + * upper 16 bits appear to be quite uniform as per tests. We don't + * really need 32bit random values so we offer max 16bits of entropy */ + return ((rng->m_seed >> 16) % (range + 1)) + minValue; } - return (m_seed >> 16); + return (rng->m_seed >> 16); } -uint8_t Random::next8(uint8_t minValue, uint8_t maxValue) +uint8_t random_next8(random_t *rng, uint8_t minValue, uint8_t maxValue) { - uint32_t result = next16(minValue, maxValue); - return static_cast(result); + uint32_t result = random_next16(rng, minValue, maxValue); + return (uint8_t)result; } + diff --git a/Helios/Random.h b/Helios/Random.h index 1f556f42..f4fcbca6 100644 --- a/Helios/Random.h +++ b/Helios/Random.h @@ -1,20 +1,28 @@ -#pragma once +#ifndef RANDOM_H +#define RANDOM_H #include -class Random +typedef struct random_t random_t; + +struct random_t { -public: - Random(); - Random(uint32_t newseed); - ~Random(); + uint32_t m_seed; +}; - void seed(uint32_t newseed); +/* Initialize a random struct with default seed */ +void random_init(random_t *rng); - uint8_t next8(uint8_t minValue = 0, uint8_t maxValue = 0xFF); - uint16_t next16(uint16_t minValue = 0, uint16_t maxValue = 0xFFFF); +/* Initialize a random struct with a specific seed */ +void random_init_seed(random_t *rng, uint32_t newseed); -private: - uint32_t m_seed; -}; +/* Set the seed for the random number generator */ +void random_seed(random_t *rng, uint32_t newseed); + +/* Generate next random 8-bit value within range [minValue, maxValue] */ +uint8_t random_next8(random_t *rng, uint8_t minValue, uint8_t maxValue); + +/* Generate next random 16-bit value within range [minValue, maxValue] */ +uint16_t random_next16(random_t *rng, uint16_t minValue, uint16_t maxValue); +#endif diff --git a/Helios/Storage.cpp b/Helios/Storage.cpp index af401f99..5413c26a 100644 --- a/Helios/Storage.cpp +++ b/Helios/Storage.cpp @@ -15,127 +15,175 @@ #include #endif +/* Forward declarations for internal functions */ +static uint8_t storage_crc_pos(uint8_t pos); +static uint8_t storage_read_crc(uint8_t pos); +static uint8_t storage_check_crc(uint8_t pos); +static void storage_write_crc(uint8_t pos); +static void storage_write_byte(uint8_t address, uint8_t data); +static uint8_t storage_read_byte(uint8_t address); + +#ifdef HELIOS_EMBEDDED +static inline uint8_t storage_internal_read(uint8_t address); +static inline void storage_internal_write(uint8_t address, uint8_t data); +#endif + #ifdef HELIOS_CLI -// whether storage is enabled, default enabled -bool Storage::m_enableStorage = true; +/* whether storage is enabled, default enabled */ +static uint8_t m_enableStorage = 1; #endif -bool Storage::init() +uint8_t storage_init(void) { #ifdef HELIOS_CLI if (!m_enableStorage) { - return true; + return 1; } - // if the storage filename doesn't exist then create it + /* if the storage filename doesn't exist then create it */ if (access(STORAGE_FILENAME, O_RDWR) != 0 && errno == ENOENT) { - // The file doesn't exist, so try creating it + /* The file doesn't exist, so try creating it */ FILE *f = fopen(STORAGE_FILENAME, "w+b"); if (!f) { perror("Error creating storage file for write"); - return false; + return 0; } - // fill the storage with 0s - for (uint32_t i = 0; i < STORAGE_SIZE; ++i){ + /* fill the storage with 0s */ + uint32_t i; + for (i = 0; i < STORAGE_SIZE; ++i){ uint8_t b = 0x0; fwrite(&b, 1, sizeof(uint8_t), f); } fclose(f); } #endif - return true; + return 1; } -bool Storage::read_pattern(uint8_t slot, Pattern &pat) +uint8_t storage_read_pattern(uint8_t slot, pattern_t *pat) { uint8_t pos = slot * SLOT_SIZE; - if (!check_crc(pos)) { - return false; + if (!storage_check_crc(pos)) { + return 0; } - for (uint8_t i = 0; i < PATTERN_SIZE; ++i) { - ((uint8_t *)&pat)[i] = read_byte(pos + i); + uint8_t i; + for (i = 0; i < PATTERN_SIZE; ++i) { + ((uint8_t *)pat)[i] = storage_read_byte(pos + i); } - return true; + return 1; } -void Storage::write_pattern(uint8_t slot, const Pattern &pat) +void storage_write_pattern(uint8_t slot, const pattern_t *pat) { uint8_t pos = slot * SLOT_SIZE; - for (uint8_t i = 0; i < PATTERN_SIZE; ++i) { - uint8_t val = ((uint8_t *)&pat)[i]; + uint8_t i; + for (i = 0; i < PATTERN_SIZE; ++i) { + uint8_t val = ((uint8_t *)pat)[i]; uint8_t target = pos + i; - write_byte(target, val); + storage_write_byte(target, val); } - write_crc(pos); + storage_write_crc(pos); } -void Storage::copy_slot(uint8_t srcSlot, uint8_t dstSlot) +void storage_copy_slot(uint8_t srcSlot, uint8_t dstSlot) { uint8_t src = srcSlot * SLOT_SIZE; uint8_t dst = dstSlot * SLOT_SIZE; - for (uint8_t i = 0; i < SLOT_SIZE; ++i) { - write_byte(dst + i, read_byte(src + i)); + uint8_t i; + for (i = 0; i < SLOT_SIZE; ++i) { + storage_write_byte(dst + i, storage_read_byte(src + i)); } } -uint8_t Storage::read_config(uint8_t index) +uint8_t storage_read_config(uint8_t index) +{ + return storage_read_byte(CONFIG_START_INDEX - index); +} + +void storage_write_config(uint8_t index, uint8_t val) +{ + storage_write_byte(CONFIG_START_INDEX - index, val); +} + +uint8_t storage_read_global_flags(void) +{ + return storage_read_config(STORAGE_GLOBAL_FLAG_INDEX); +} + +void storage_write_global_flags(uint8_t global_flags) +{ + storage_write_config(STORAGE_GLOBAL_FLAG_INDEX, global_flags); +} + +uint8_t storage_read_current_mode(void) +{ + return storage_read_config(STORAGE_CURRENT_MODE_INDEX); +} + +void storage_write_current_mode(uint8_t current_mode) +{ + storage_write_config(STORAGE_CURRENT_MODE_INDEX, current_mode); +} + +uint8_t storage_read_brightness(void) { - return read_byte(CONFIG_START_INDEX - index); + return storage_read_config(STORAGE_BRIGHTNESS_INDEX); } -void Storage::write_config(uint8_t index, uint8_t val) +void storage_write_brightness(uint8_t brightness) { - write_byte(CONFIG_START_INDEX - index, val); + storage_write_config(STORAGE_BRIGHTNESS_INDEX, brightness); } -uint8_t Storage::crc8(uint8_t pos, uint8_t size) +uint8_t storage_crc8(uint8_t pos, uint8_t size) { - uint8_t hash = 33; // A non-zero initial value - for (uint8_t i = 0; i < size; ++i) { - hash = ((hash << 5) + hash) + read_byte(pos); + uint8_t hash = 33; /* A non-zero initial value */ + uint8_t i; + for (i = 0; i < size; ++i) { + hash = ((hash << 5) + hash) + storage_read_byte(pos); } return hash; } -uint8_t Storage::crc_pos(uint8_t pos) +static uint8_t storage_crc_pos(uint8_t pos) { - // crc the entire slot except last byte - return crc8(pos, PATTERN_SIZE); + /* crc the entire slot except last byte */ + return storage_crc8(pos, PATTERN_SIZE); } -uint8_t Storage::read_crc(uint8_t pos) +static uint8_t storage_read_crc(uint8_t pos) { - // read the last byte of the slot - return read_byte(pos + PATTERN_SIZE); + /* read the last byte of the slot */ + return storage_read_byte(pos + PATTERN_SIZE); } -bool Storage::check_crc(uint8_t pos) +static uint8_t storage_check_crc(uint8_t pos) { - // compare the last byte to the calculated crc - return (read_crc(pos) == crc_pos(pos)); + /* compare the last byte to the calculated crc */ + return (storage_read_crc(pos) == storage_crc_pos(pos)); } -void Storage::write_crc(uint8_t pos) +static void storage_write_crc(uint8_t pos) { - // compare the last byte to the calculated crc - write_byte(pos + PATTERN_SIZE, crc_pos(pos)); + /* compare the last byte to the calculated crc */ + storage_write_byte(pos + PATTERN_SIZE, storage_crc_pos(pos)); } -void Storage::write_byte(uint8_t address, uint8_t data) +static void storage_write_byte(uint8_t address, uint8_t data) { #ifdef HELIOS_EMBEDDED - // reads out the byte of the eeprom first to see if it's different - // before writing out the byte -- this is faster than always writing - if (read_byte(address) == data) { + /* reads out the byte of the eeprom first to see if it's different + * before writing out the byte -- this is faster than always writing */ + if (storage_read_byte(address) == data) { return; } - internal_write(address, data); - // double check that shit - if (read_byte(address) != data) { - // do it again because eeprom is stupid - internal_write(address, data); - // god forbid it doesn't write again + storage_internal_write(address, data); + /* double check that shit */ + if (storage_read_byte(address) != data) { + /* do it again because eeprom is stupid */ + storage_internal_write(address, data); + /* god forbid it doesn't write again */ } -#else // HELIOS_CLI +#else /* HELIOS_CLI */ if (!m_enableStorage) { return; } @@ -144,29 +192,30 @@ void Storage::write_byte(uint8_t address, uint8_t data) perror("Error opening storage file"); return; } - // Seek to the specified address + /* Seek to the specified address */ if (fseek(f, address, SEEK_SET) != 0) { perror("Error opening storage file for write"); fclose(f); return; } if (!fwrite((const void *)&data, sizeof(uint8_t), 1, f)) { + fclose(f); return; } - fclose(f); // Close the file + fclose(f); /* Close the file */ #endif } -uint8_t Storage::read_byte(uint8_t address) +static uint8_t storage_read_byte(uint8_t address) { #ifdef HELIOS_EMBEDDED - // do a three way read because the attiny85 eeprom basically doesn't work - uint8_t b1 = internal_read(address); - uint8_t b2 = internal_read(address); + /* do a three way read because the attiny85 eeprom basically doesn't work */ + uint8_t b1 = storage_internal_read(address); + uint8_t b2 = storage_internal_read(address); if (b1 == b2) { return b2; } - uint8_t b3 = internal_read(address); + uint8_t b3 = storage_internal_read(address); if (b3 == b1) { return b1; } @@ -182,55 +231,63 @@ uint8_t Storage::read_byte(uint8_t address) if (access(STORAGE_FILENAME, O_RDONLY) != 0) { return val; } - FILE *f = fopen(STORAGE_FILENAME, "rb"); // Open file for reading in binary mode + FILE *f = fopen(STORAGE_FILENAME, "rb"); /* Open file for reading in binary mode */ if (!f) { - // this error is ok, just means no storage - //perror("Error opening file for read"); + /* this error is ok, just means no storage */ + /* perror("Error opening file for read"); */ return val; } - // Seek to the specified address + /* Seek to the specified address */ if (fseek(f, address, SEEK_SET) != 0) { - // error + /* error */ perror("Failed to seek"); fclose(f); return val; } - // Read a byte of data + /* Read a byte of data */ if (!fread(&val, sizeof(uint8_t), 1, f)) { perror("Failed to read byte"); } - fclose(f); // Close the file + fclose(f); /* Close the file */ return val; #endif } #ifdef HELIOS_EMBEDDED -inline void Storage::internal_write(uint8_t address, uint8_t data) +static inline void storage_internal_write(uint8_t address, uint8_t data) { while (EECR & (1< #include "HeliosConfig.h" -// the index of the first config byte, the config bytes start at the end -// then work their way backwards (so 'config index 0' is the last byte) +/* the index of the first config byte, the config bytes start at the end + * then work their way backwards (so 'config index 0' is the last byte) */ #define CONFIG_START_INDEX (STORAGE_SIZE - 2) -// the crc of the config bytes is the very last byte in storage -// TODO: implement the global config CRC again it got removed at some point +/* the crc of the config bytes is the very last byte in storage + * TODO: implement the global config CRC again it got removed at some point */ #define CONFIG_CRC_INDEX (STORAGE_SIZE - 1) -// Storage Config Indexes relative to the CONFIG_START_INDEX +/* Storage Config Indexes relative to the CONFIG_START_INDEX */ #define STORAGE_GLOBAL_FLAG_INDEX 0 #define STORAGE_CURRENT_MODE_INDEX 1 #define STORAGE_BRIGHTNESS_INDEX 2 -class Pattern; +/* Forward declaration */ +typedef struct pattern_t pattern_t; -class Storage -{ -public: +uint8_t storage_init(void); - static bool init(); +uint8_t storage_read_pattern(uint8_t slot, pattern_t *pat); +void storage_write_pattern(uint8_t slot, const pattern_t *pat); - static bool read_pattern(uint8_t slot, Pattern &pat); - static void write_pattern(uint8_t slot, const Pattern &pat); +void storage_copy_slot(uint8_t srcSlot, uint8_t dstSlot); - static void copy_slot(uint8_t srcSlot, uint8_t dstSlot); +uint8_t storage_read_config(uint8_t index); +void storage_write_config(uint8_t index, uint8_t val); - static uint8_t read_config(uint8_t index); - static void write_config(uint8_t index, uint8_t val); +uint8_t storage_read_global_flags(void); +void storage_write_global_flags(uint8_t global_flags); - static uint8_t read_global_flags() { return read_config(STORAGE_GLOBAL_FLAG_INDEX); } - static void write_global_flags(uint8_t global_flags) { write_config(STORAGE_GLOBAL_FLAG_INDEX, global_flags); } +uint8_t storage_read_current_mode(void); +void storage_write_current_mode(uint8_t current_mode); - static uint8_t read_current_mode() { return read_config(STORAGE_CURRENT_MODE_INDEX); } - static void write_current_mode(uint8_t current_mode) { write_config(STORAGE_CURRENT_MODE_INDEX, current_mode); } +uint8_t storage_read_brightness(void); +void storage_write_brightness(uint8_t brightness); - static uint8_t read_brightness() { return read_config(STORAGE_BRIGHTNESS_INDEX); } - static void write_brightness(uint8_t brightness) { write_config(STORAGE_BRIGHTNESS_INDEX, brightness); } - - static uint8_t crc8(uint8_t pos, uint8_t size); - -#ifdef HELIOS_CLI - // toggle storage on/off - static void enableStorage(bool enabled) { m_enableStorage = enabled; } -#endif -private: - static uint8_t crc_pos(uint8_t pos); - static uint8_t read_crc(uint8_t pos); - static bool check_crc(uint8_t pos); - static void write_crc(uint8_t pos); - - static void write_byte(uint8_t address, uint8_t data); - static uint8_t read_byte(uint8_t address); - -#ifdef HELIOS_EMBEDDED - static inline uint8_t internal_read(uint8_t address); - static inline void internal_write(uint8_t address, uint8_t data); -#endif +uint8_t storage_crc8(uint8_t pos, uint8_t size); #ifdef HELIOS_CLI - // whether storage is enabled - static bool m_enableStorage; +/* toggle storage on/off */ +void storage_enable_storage(uint8_t enabled); #endif -}; #endif diff --git a/Helios/TimeControl.cpp b/Helios/TimeControl.cpp index 904be2a3..cc80e655 100644 --- a/Helios/TimeControl.cpp +++ b/Helios/TimeControl.cpp @@ -1,183 +1,197 @@ -#include "TimeControl.h" - -#include - -#include "Timings.h" - -#include "Led.h" - -#ifdef HELIOS_EMBEDDED -#include -#include -#ifdef HELIOS_ARDUINO -#include -#endif -#endif - -#ifdef HELIOS_CLI -#include -#include -uint64_t start = 0; -// convert seconds and nanoseconds to microseconds -#define SEC_TO_US(sec) ((sec)*1000000) -#define NS_TO_US(ns) ((ns)/1000) -#endif - -// static members -uint32_t Time::m_curTick = 0; -// the last frame timestamp -uint32_t Time::m_prevTime = 0; - -#ifdef HELIOS_CLI -// whether timestep is enabled, default enabled -bool Time::m_enableTimestep = true; -#endif - -bool Time::init() -{ - m_prevTime = microseconds(); - m_curTick = 0; - return true; -} - -void Time::cleanup() -{ -} - -void Time::tickClock() -{ - // tick clock forward - m_curTick++; - -#ifdef HELIOS_CLI - if (!m_enableTimestep) { - return; - } -#endif - - // the rest of this only runs inside vortexlib because on the duo the tick runs in the - // tcb timer callback instead of in a busy loop constantly checking microseconds() - // perform timestep - uint32_t elapsed_us; - uint32_t us; - do { - us = microseconds(); - // detect rollover of microsecond counter - if (us < m_prevTime) { - // calculate wrapped around difference - elapsed_us = (uint32_t)((UINT32_MAX - m_prevTime) + us); - } else { - // otherwise calculate regular difference - elapsed_us = (uint32_t)(us - m_prevTime); - } - // if building anywhere except visual studio then we can run alternate sleep code - // because in visual studio + windows it's better to just spin and check the high - // resolution clock instead of trying to sleep for microseconds. - // 1000us per ms, divided by tickrate gives - // the number of microseconds per tick - } while (elapsed_us < (1000000 / TICKRATE)); - - // store current time - m_prevTime = microseconds(); -} - -#ifdef HELIOS_EMBEDDED -volatile uint32_t timer0_overflow_count = 0; -ISR(TIMER0_OVF_vect) { - timer0_overflow_count++; // Increment on each overflow -} -#endif - -uint32_t Time::microseconds() -{ -#ifdef HELIOS_CLI - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - uint64_t us = SEC_TO_US((uint64_t)ts.tv_sec) + NS_TO_US((uint64_t)ts.tv_nsec); - return (unsigned long)us; -#else -#ifdef HELIOS_ARDUINO - return micros(); -#else - // The only reason that micros() is actually necessary is if Helios::tick() - // cannot be called in a 1Khz ISR. If Helios::tick() cannot be reliably called - // by an interrupt then Time::tickClock() must perform manual timestep via micros(). - // If Helios::tick() is called by an interrupt then you don't need this function and - // should always just rely on the current tick to perform operations - uint8_t oldSREG = SREG; - cli(); - // multiply by 8 early to avoid floating point math or division - uint32_t micros = (timer0_overflow_count * (256 * 8)) + (TCNT0 * 8); - SREG = oldSREG; - // then shift right to counteract the multiplication by 8 - return micros >> 6; -#endif -#endif -} - -#ifdef HELIOS_EMBEDDED -__attribute__((noinline)) -#endif -void -Time::delayMicroseconds(uint32_t us) -{ -#ifdef HELIOS_EMBEDDED -#if F_CPU >= 16000000L - // For the ATtiny85 running at 16MHz - - // The loop takes 3 cycles per iteration - us *= 2; // 0.5us per iteration - - // Subtract the overhead of the function call and loop setup - // Assuming approximately 5 cycles overhead - us -= 5; // Simplified subtraction - - // Assembly loop for delay - __asm__ __volatile__( - "1: sbiw %0, 1" - "\n\t" // 2 cycles - "nop" - "\n\t" // 1 cycle - "brne 1b" : "=w"(us) : "0"(us) // 2 cycles - ); - -#elif F_CPU >= 8000000L - // For the ATtiny85 running at 8MHz - - // The loop takes 4 cycles per iteration - us <<= 1; // 1us per iteration - - // Subtract the overhead of the function call and loop setup - // Assuming approximately 6 cycles overhead - us -= 6; // Simplified subtraction - - // Assembly loop for delay - __asm__ __volatile__( - "1: sbiw %0, 1" - "\n\t" // 2 cycles - "rjmp .+0" - "\n\t" // 2 cycles - "brne 1b" : "=w"(us) : "0"(us) // 2 cycles - ); -#endif - -#else - uint32_t newtime = microseconds() + us; - while (microseconds() < newtime) - { - // busy loop - } -#endif -} - -void Time::delayMilliseconds(uint32_t ms) -{ -#ifdef HELIOS_CLI - usleep(ms * 1000); -#else - // not very accurate - for (uint16_t i = 0; i < ms; ++i) { - delayMicroseconds(1000); - } -#endif -} +#include "TimeControl.h" + +#include + +#include "Timings.h" + +#include "Led.h" + +#ifdef HELIOS_EMBEDDED +#include +#include +#ifdef HELIOS_ARDUINO +#include +#endif +#endif + +#ifdef HELIOS_CLI +#include +#include +static uint64_t start = 0; +/* convert seconds and nanoseconds to microseconds */ +#define SEC_TO_US(sec) ((sec)*1000000) +#define NS_TO_US(ns) ((ns)/1000) +#endif + +/* static members */ +static uint32_t m_curTick = 0; +/* the last frame timestamp */ +static uint32_t m_prevTime = 0; + +#ifdef HELIOS_CLI +/* whether timestep is enabled, default enabled */ +static uint8_t m_enableTimestep = 1; +#endif + +uint8_t time_init(void) +{ + m_prevTime = time_microseconds(); + m_curTick = 0; + return 1; +} + +void time_cleanup(void) +{ +} + +void time_tick_clock(void) +{ + /* tick clock forward */ + m_curTick++; + +#ifdef HELIOS_CLI + if (!m_enableTimestep) { + return; + } +#endif + + /* the rest of this only runs inside vortexlib because on the duo the tick runs in the + * tcb timer callback instead of in a busy loop constantly checking microseconds() + * perform timestep */ + uint32_t elapsed_us; + uint32_t us; + do { + us = time_microseconds(); + /* detect rollover of microsecond counter */ + if (us < m_prevTime) { + /* calculate wrapped around difference */ + elapsed_us = (uint32_t)((UINT32_MAX - m_prevTime) + us); + } else { + /* otherwise calculate regular difference */ + elapsed_us = (uint32_t)(us - m_prevTime); + } + /* if building anywhere except visual studio then we can run alternate sleep code + * because in visual studio + windows it's better to just spin and check the high + * resolution clock instead of trying to sleep for microseconds. + * 1000us per ms, divided by tickrate gives + * the number of microseconds per tick */ + } while (elapsed_us < (1000000 / TICKRATE)); + + /* store current time */ + m_prevTime = time_microseconds(); +} + +uint32_t time_get_current_time(void) +{ + return m_curTick; +} + +#ifdef HELIOS_EMBEDDED +volatile uint32_t timer0_overflow_count = 0; +ISR(TIMER0_OVF_vect) { + timer0_overflow_count++; /* Increment on each overflow */ +} +#endif + +uint32_t time_microseconds(void) +{ +#ifdef HELIOS_CLI + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t us = SEC_TO_US((uint64_t)ts.tv_sec) + NS_TO_US((uint64_t)ts.tv_nsec); + return (unsigned long)us; +#else +#ifdef HELIOS_ARDUINO + return micros(); +#else + /* The only reason that micros() is actually necessary is if Helios::tick() + * cannot be called in a 1Khz ISR. If Helios::tick() cannot be reliably called + * by an interrupt then Time::tickClock() must perform manual timestep via micros(). + * If Helios::tick() is called by an interrupt then you don't need this function and + * should always just rely on the current tick to perform operations */ + uint8_t oldSREG = SREG; + cli(); + /* multiply by 8 early to avoid floating point math or division */ + uint32_t micros = (timer0_overflow_count * (256 * 8)) + (TCNT0 * 8); + SREG = oldSREG; + /* then shift right to counteract the multiplication by 8 */ + return micros >> 6; +#endif +#endif +} + +#ifdef HELIOS_EMBEDDED +__attribute__((noinline)) +#endif +void +time_delay_microseconds(uint32_t us) +{ +#ifdef HELIOS_EMBEDDED +#if F_CPU >= 16000000L + /* For the ATtiny85 running at 16MHz */ + + /* The loop takes 3 cycles per iteration */ + us *= 2; /* 0.5us per iteration */ + + /* Subtract the overhead of the function call and loop setup + * Assuming approximately 5 cycles overhead */ + us -= 5; /* Simplified subtraction */ + + /* Assembly loop for delay */ + __asm__ __volatile__( + "1: sbiw %0, 1" + "\n\t" /* 2 cycles */ + "nop" + "\n\t" /* 1 cycle */ + "brne 1b" : "=w"(us) : "0"(us) /* 2 cycles */ + ); + +#elif F_CPU >= 8000000L + /* For the ATtiny85 running at 8MHz */ + + /* The loop takes 4 cycles per iteration */ + us <<= 1; /* 1us per iteration */ + + /* Subtract the overhead of the function call and loop setup + * Assuming approximately 6 cycles overhead */ + us -= 6; /* Simplified subtraction */ + + /* Assembly loop for delay */ + __asm__ __volatile__( + "1: sbiw %0, 1" + "\n\t" /* 2 cycles */ + "rjmp .+0" + "\n\t" /* 2 cycles */ + "brne 1b" : "=w"(us) : "0"(us) /* 2 cycles */ + ); +#endif + +#else + uint32_t newtime = time_microseconds() + us; + while (time_microseconds() < newtime) + { + /* busy loop */ + } +#endif +} + +void time_delay_milliseconds(uint32_t ms) +{ +#ifdef HELIOS_CLI + usleep(ms * 1000); +#else + /* not very accurate */ + uint16_t i; + for (i = 0; i < ms; ++i) { + time_delay_microseconds(1000); + } +#endif +} + +#ifdef HELIOS_CLI +void time_enable_timestep(uint8_t enabled) +{ + m_enableTimestep = enabled; +} +#endif + diff --git a/Helios/TimeControl.h b/Helios/TimeControl.h index b5f316d8..b65eb8aa 100644 --- a/Helios/TimeControl.h +++ b/Helios/TimeControl.h @@ -5,55 +5,35 @@ #include "HeliosConfig.h" -// macros to convert milliseconds and seconds to measures of ticks +/* macros to convert milliseconds and seconds to measures of ticks */ #define MS_TO_TICKS(ms) (uint32_t)(((uint32_t)(ms) * TICKRATE) / 1000) #define SEC_TO_TICKS(s) (uint32_t)((uint32_t)(s) * TICKRATE) -class Time -{ - // private unimplemented constructor - Time(); +/* Initialize time system */ +uint8_t time_init(void); +void time_cleanup(void); -public: - // opting for static class here because there should only ever be one - // Settings control object and I don't like singletons - static bool init(); - static void cleanup(); +/* Tick the clock forward to millis() */ +void time_tick_clock(void); - // tick the clock forward to millis() - static void tickClock(); +/* Get the current tick, offset by any active simulation (simulation only exists in vortexlib) + * Exposing this as inline or macro seems to save on space a non negligible amount, it is used a lot + * and exposing in the header probably allows the compiler to optimize away repetitive calls */ +uint32_t time_get_current_time(void); - // get the current tick, offset by any active simulation (simulation only exists in vortexlib) - // Exposing this in the header seems to save on space a non negligible amount, it is used a lot - // and exposing in the header probably allows the compiler to optimize away repititive calls - static uint32_t getCurtime() { return m_curTick; } +/* Current microseconds since startup, only use this for things like measuring rapid data transfer timings. + * If you just need to perform regular time checks for a pattern or some logic then use time_get_current_time() and measure + * time in ticks, use the SEC_TO_TICKS() or MS_TO_TICKS() macros to convert timings to measures of ticks for + * purpose of comparing against time_get_current_time() */ +uint32_t time_microseconds(void); - // Current microseconds since startup, only use this for things like measuring rapid data transfer timings. - // If you just need to perform regular time checks for a pattern or some logic then use getCurtime() and measure - // time in ticks, use the SEC_TO_TICKS() or MS_TO_TICKS() macros to convert timings to measures of ticks for - // purpose of comparing against getCurtime() - static uint32_t microseconds(); - - // delay for some number of microseconds or milliseconds, these are bad - static void delayMicroseconds(uint32_t us); - static void delayMilliseconds(uint32_t ms); - -#ifdef HELIOS_CLI - // toggle timestep on/off - static void enableTimestep(bool enabled) { m_enableTimestep = enabled; } -#endif - -private: - // global tick counter - static uint32_t m_curTick; - // the last frame timestamp - static uint32_t m_prevTime; +/* Delay for some number of microseconds or milliseconds, these are bad */ +void time_delay_microseconds(uint32_t us); +void time_delay_milliseconds(uint32_t ms); #ifdef HELIOS_CLI - // whether timestep is enabled - static bool m_enableTimestep; +/* Toggle timestep on/off */ +void time_enable_timestep(uint8_t enabled); #endif -}; #endif - diff --git a/Helios/Timer.cpp b/Helios/Timer.cpp index 48dd685b..fd3aeb0a 100644 --- a/Helios/Timer.cpp +++ b/Helios/Timer.cpp @@ -4,56 +4,53 @@ #include "TimeControl.h" -Timer::Timer() : - m_alarm(0), - m_startTime(0) +void timer_init_default(timer_t *timer) { + timer->m_alarm = 0; + timer->m_startTime = 0; } -Timer::~Timer() +void timer_init(timer_t *timer, uint8_t alarm) { + timer_reset(timer); + timer->m_alarm = alarm; + timer_start(timer, 0); } -void Timer::init(uint8_t alarm) +void timer_start(timer_t *timer, uint32_t offset) { - reset(); - m_alarm = alarm; - start(); + /* reset the start time */ + timer->m_startTime = time_get_current_time() + offset; } -void Timer::start(uint32_t offset) +void timer_reset(timer_t *timer) { - // reset the start time - m_startTime = Time::getCurtime() + offset; + timer->m_alarm = 0; + timer->m_startTime = 0; } -void Timer::reset() +uint8_t timer_alarm(timer_t *timer) { - m_alarm = 0; - m_startTime = 0; -} - -bool Timer::alarm() -{ - if (!m_alarm) { - return false; + if (!timer->m_alarm) { + return 0; } - uint32_t now = Time::getCurtime(); - // time since start (forward or backwards) - int32_t timeDiff = (int32_t)(int64_t)(now - m_startTime); + uint32_t now = time_get_current_time(); + /* time since start (forward or backwards) */ + int32_t timeDiff = (int32_t)(int64_t)(now - timer->m_startTime); if (timeDiff < 0) { - return false; + return 0; } - // if no time passed it's first alarm that is starting + /* if no time passed it's first alarm that is starting */ if (timeDiff == 0) { - return true; + return 1; } - // if the current alarm duration is not a multiple of the current tick - if (m_alarm && (timeDiff % m_alarm) != 0) { - // then the alarm was not hit - return false; + /* if the current alarm duration is not a multiple of the current tick */ + if (timer->m_alarm && (timeDiff % timer->m_alarm) != 0) { + /* then the alarm was not hit */ + return 0; } - // update the start time of the timer - m_startTime = now; - return true; + /* update the start time of the timer */ + timer->m_startTime = now; + return 1; } + diff --git a/Helios/Timer.h b/Helios/Timer.h index 92a19a52..46b8394e 100644 --- a/Helios/Timer.h +++ b/Helios/Timer.h @@ -3,28 +3,30 @@ #include -class Timer +typedef struct timer_t timer_t; + +struct timer_t { -public: - Timer(); - ~Timer(); - - // init a timer with a number of alarms and optionally start it - void init(uint8_t alarm); - - // start the timer but don't change current alarm, this shifts - // the timer startTime but does not reset it's alarm state - void start(uint32_t offset = 0); - // delete all alarms from the timer and reset - void reset(); - // Will return the true if the timer hit - bool alarm(); - -private: - // the alarm + /* the alarm */ uint32_t m_alarm; - // start time in microseconds + /* start time in microseconds */ uint32_t m_startTime; }; +/* Initialize a timer struct to default values */ +void timer_init_default(timer_t *timer); + +/* Init a timer with a number of alarms and optionally start it */ +void timer_init(timer_t *timer, uint8_t alarm); + +/* Start the timer but don't change current alarm, this shifts + * the timer startTime but does not reset it's alarm state */ +void timer_start(timer_t *timer, uint32_t offset); + +/* Delete all alarms from the timer and reset */ +void timer_reset(timer_t *timer); + +/* Will return true if the timer hit */ +uint8_t timer_alarm(timer_t *timer); + #endif diff --git a/HeliosEmbedded/Makefile b/HeliosEmbedded/Makefile index 02dee584..c89c5586 100644 --- a/HeliosEmbedded/Makefile +++ b/HeliosEmbedded/Makefile @@ -2,7 +2,7 @@ ### CONFIGURATION ### ##################### -.PHONY: all upload set_fuses set_default_fuses set_16mhz_fuses set_8mhz_fuses set_1mhz_fuses get_fuses extract_hex upload_hex extract_eeprom upload_eeprom clean compute_version extract_version helios_release +.PHONY: all upload set_fuses set_default_fuses set_16mhz_fuses set_8mhz_fuses set_1mhz_fuses get_fuses extract_hex upload_hex extract_eeprom upload_eeprom clean compute_version extract_version aeos_release ifneq ($(OS),Windows_NT) OS = $(shell uname -s) @@ -46,8 +46,8 @@ endif ### TOOLCHAIN SETUP ### ####################### -CC = ${BINDIR}avr-g++ -LD = ${BINDIR}avr-g++ +CC = ${BINDIR}avr-gcc +LD = ${BINDIR}avr-gcc OBJCOPY = ${BINDIR}avr-objcopy -v AR = ${BINDIR}avr-gcc-ar SIZE = ${BINDIR}avr-size @@ -117,15 +117,13 @@ CFLAGS = -g \ -Wall \ -flto \ -mrelax \ - -std=gnu++17 \ + -std=c11 \ -fshort-enums \ -fpack-struct \ - -fno-exceptions \ -fdata-sections \ -funsigned-char \ -ffunction-sections\ -funsigned-bitfields \ - -fno-threadsafe-statics \ -mcall-prologues \ -D__AVR_ATtiny85__ \ -mmcu=$(AVRDUDE_CHIP) \ @@ -162,15 +160,15 @@ CFLAGS+=$(INCLUDES) # Source files ifeq ($(OS),Windows_NT) # Windows SRCS = \ - $(shell find ../Helios -maxdepth 1 -type f -name '\*.cpp') main.cpp + $(shell find ../Helios -maxdepth 1 -type f -name '\*.c') main.c else # linux SRCS = \ - $(shell find ../Helios -maxdepth 1 -type f -name \*.cpp) main.cpp + $(shell find ../Helios -maxdepth 1 -type f -name \*.c) main.c endif -OBJS = $(SRCS:.cpp=.o) +OBJS = $(SRCS:.c=.o) -DFILES = $(SRCS:.cpp=.d) +DFILES = $(SRCS:.c=.d) ####################### ### BUILD TARGETS ##### @@ -198,7 +196,7 @@ $(TARGET).elf: compute_version $(OBJS) %.o: %.S $(CC) $(ASMFLAGS) -c $< -o $@ -%.o: %.cpp +%.o: %.c $(CC) $(CFLAGS) -c $< -o $@ upload: set_fuses $(TARGET).hex @@ -208,32 +206,32 @@ upload: set_fuses $(TARGET).hex ### GITHUB RELEASE #### ####################### -helios_release: +aeos_release: @if [ -z "$(VERSION)" ]; then \ echo "Error: VERSION parameter is required"; \ - echo "Usage: make helios_release VERSION=1.2.3"; \ + echo "Usage: make aeos_release VERSION=1.2.3"; \ exit 1; \ fi - @echo "Creating and pushing $(VERSION) tag..." - @if git rev-parse "$(VERSION)" >/dev/null 2>&1; then \ - echo "Warning: Tag $(VERSION) already exists locally"; \ + @echo "Creating and pushing aeos-$(VERSION) tag..." + @if git rev-parse "aeos-$(VERSION)" >/dev/null 2>&1; then \ + echo "Warning: Tag aeos-$(VERSION) already exists locally"; \ echo "Checking if it exists on remote..."; \ - if git ls-remote --tags origin | grep -q "refs/tags/$(VERSION)$$"; then \ - echo "Tag $(VERSION) already exists on remote. Skipping tag creation."; \ + if git ls-remote --tags origin | grep -q "aeos-$(VERSION)"; then \ + echo "Tag aeos-$(VERSION) already exists on remote. Skipping tag creation."; \ else \ echo "Pushing existing local tag to remote..."; \ - git push origin $(VERSION); \ + git push origin aeos-$(VERSION); \ fi \ else \ - echo "Creating new tag $(VERSION)..."; \ - git tag $(VERSION); \ - git push origin $(VERSION); \ - echo "✓ Tag $(VERSION) created and pushed"; \ + echo "Creating new tag aeos-$(VERSION)..."; \ + git tag aeos-$(VERSION); \ + git push origin aeos-$(VERSION); \ + echo "✓ Tag aeos-$(VERSION) created and pushed"; \ fi - @echo "Triggering Helios Release workflow..." - gh workflow run "Helios Release" --ref master - @echo "✓ Helios Release workflow triggered successfully!" - @echo "Monitor progress with: gh run list --workflow=\"release.yml\" --limit=1" + @echo "Triggering Aeos Release workflow..." + gh workflow run "Aeos Release" --ref aeos + @echo "✓ Aeos Release workflow triggered successfully!" + @echo "Monitor progress with: gh run list --workflow=\"aeos_release.yml\" --limit=1" ####################### ### LINUX SETUP ####### @@ -323,10 +321,10 @@ upload_eeprom_fingers: eeprom_fingers.eep ################### upload_hex: - $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:helios_firmware.hex:i + $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:aeos.hex:i -extract_hex: helios_firmware.hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:r:helios_firmware.hex:i +extract_hex: aeos.hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:r:aeos.hex:i ##################### ####### CLEAN ####### @@ -336,7 +334,7 @@ clean: rm -f $(OBJS) $(TARGET).elf $(TARGET).hex $(DFILES) $(TARGET).bin $(TARGET).eep $(TARGET).lst $(TARGET).map compute_version: - $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$$' | sort -V | tail -n1)) + $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list | sort -V | tail -n1)) $(eval HELIOS_VERSION_MAJOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f1)) $(eval HELIOS_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f2)) $(eval LAST_HELIOS_BUILD_NUMBER ?= $(shell echo $(LATEST_TAG) | cut -d. -f3)) @@ -355,4 +353,4 @@ extract_version: @rm $(TEMP_HEX) # include dependency files to ensure partial rebuilds work correctly --include $(DFILES) \ No newline at end of file +-include $(DFILES) diff --git a/HeliosEmbedded/main.cpp b/HeliosEmbedded/main.cpp index 391fdf1b..d537ca01 100644 --- a/HeliosEmbedded/main.cpp +++ b/HeliosEmbedded/main.cpp @@ -4,14 +4,18 @@ #include #if !defined(HELIOS_CLI) && !defined(HELIOS_ARDUINO) -// this is the main thread for non-arduino embedded builds +/* this is the main thread for non-arduino embedded builds */ int main(int argc, char *argv[]) { - Helios::init(); - // the main thread just initializes Helios then continuously calls tick - while (Helios::keep_going()) { - Helios::tick(); + (void)argc; /* unused */ + (void)argv; /* unused */ + + helios_init(); + /* the main thread just initializes Helios then continuously calls tick */ + while (helios_keep_going()) { + helios_tick(); } return 0; } #endif + From 1b12a3c8060d3fa9ab9f91225ded58b526d75dcf Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 11:28:10 +0200 Subject: [PATCH 02/23] Rename .cpp files to .c files --- Helios/{Button.cpp => Button.c} | 0 Helios/{Colorset.cpp => Colorset.c} | 0 Helios/{Colortypes.cpp => Colortypes.c} | 0 Helios/{Helios.cpp => Helios.c} | 0 Helios/{Led.cpp => Led.c} | 0 Helios/{Pattern.cpp => Pattern.c} | 0 Helios/{Patterns.cpp => Patterns.c} | 0 Helios/{Random.cpp => Random.c} | 0 Helios/{Storage.cpp => Storage.c} | 0 Helios/{TimeControl.cpp => TimeControl.c} | 0 Helios/{Timer.cpp => Timer.c} | 0 HeliosEmbedded/{main.cpp => main.c} | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename Helios/{Button.cpp => Button.c} (100%) rename Helios/{Colorset.cpp => Colorset.c} (100%) rename Helios/{Colortypes.cpp => Colortypes.c} (100%) rename Helios/{Helios.cpp => Helios.c} (100%) rename Helios/{Led.cpp => Led.c} (100%) rename Helios/{Pattern.cpp => Pattern.c} (100%) rename Helios/{Patterns.cpp => Patterns.c} (100%) rename Helios/{Random.cpp => Random.c} (100%) rename Helios/{Storage.cpp => Storage.c} (100%) rename Helios/{TimeControl.cpp => TimeControl.c} (100%) rename Helios/{Timer.cpp => Timer.c} (100%) rename HeliosEmbedded/{main.cpp => main.c} (100%) diff --git a/Helios/Button.cpp b/Helios/Button.c similarity index 100% rename from Helios/Button.cpp rename to Helios/Button.c diff --git a/Helios/Colorset.cpp b/Helios/Colorset.c similarity index 100% rename from Helios/Colorset.cpp rename to Helios/Colorset.c diff --git a/Helios/Colortypes.cpp b/Helios/Colortypes.c similarity index 100% rename from Helios/Colortypes.cpp rename to Helios/Colortypes.c diff --git a/Helios/Helios.cpp b/Helios/Helios.c similarity index 100% rename from Helios/Helios.cpp rename to Helios/Helios.c diff --git a/Helios/Led.cpp b/Helios/Led.c similarity index 100% rename from Helios/Led.cpp rename to Helios/Led.c diff --git a/Helios/Pattern.cpp b/Helios/Pattern.c similarity index 100% rename from Helios/Pattern.cpp rename to Helios/Pattern.c diff --git a/Helios/Patterns.cpp b/Helios/Patterns.c similarity index 100% rename from Helios/Patterns.cpp rename to Helios/Patterns.c diff --git a/Helios/Random.cpp b/Helios/Random.c similarity index 100% rename from Helios/Random.cpp rename to Helios/Random.c diff --git a/Helios/Storage.cpp b/Helios/Storage.c similarity index 100% rename from Helios/Storage.cpp rename to Helios/Storage.c diff --git a/Helios/TimeControl.cpp b/Helios/TimeControl.c similarity index 100% rename from Helios/TimeControl.cpp rename to Helios/TimeControl.c diff --git a/Helios/Timer.cpp b/Helios/Timer.c similarity index 100% rename from Helios/Timer.cpp rename to Helios/Timer.c diff --git a/HeliosEmbedded/main.cpp b/HeliosEmbedded/main.c similarity index 100% rename from HeliosEmbedded/main.cpp rename to HeliosEmbedded/main.c From 851a8c79b9e6f2bb9bfb838d2f4d941c9b15cc96 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Thu, 16 Oct 2025 22:49:37 +0200 Subject: [PATCH 03/23] Fix timer_t conflict and add extern C guards for CLI compatibility --- Helios/Button.h | 8 ++++ Helios/Colorset.h | 8 ++++ Helios/Colortypes.h | 8 ++++ Helios/Helios.h | 8 ++++ Helios/Led.h | 8 ++++ Helios/Pattern.h | 10 ++++- Helios/Random.h | 8 ++++ Helios/Storage.h | 8 ++++ Helios/TimeControl.h | 8 ++++ Helios/Timer.c | 10 ++--- Helios/Timer.h | 22 +++++++--- HeliosCLI/Makefile | 42 +++++++++++------- HeliosCLI/cli_main.cpp | 97 +++++++++++++++++++++++++----------------- 13 files changed, 177 insertions(+), 68 deletions(-) diff --git a/Helios/Button.h b/Helios/Button.h index aa278563..28bc2f7a 100644 --- a/Helios/Button.h +++ b/Helios/Button.h @@ -1,6 +1,10 @@ #ifndef BUTTON_H #define BUTTON_H +#ifdef __cplusplus +extern "C" { +#endif + #include /* Initialize button */ @@ -74,4 +78,8 @@ void button_queue_input(char input); uint32_t button_input_queue_size(void); #endif +#ifdef __cplusplus +} +#endif + #endif diff --git a/Helios/Colorset.h b/Helios/Colorset.h index b8c2408a..1e194158 100644 --- a/Helios/Colorset.h +++ b/Helios/Colorset.h @@ -1,6 +1,10 @@ #ifndef COLORSET_H #define COLORSET_H +#ifdef __cplusplus +extern "C" { +#endif + #include "Colortypes.h" #include "HeliosConfig.h" @@ -133,4 +137,8 @@ uint8_t colorset_num_colors(const colorset_t *set); uint8_t colorset_on_start(const colorset_t *set); uint8_t colorset_on_end(const colorset_t *set); +#ifdef __cplusplus +} +#endif + #endif diff --git a/Helios/Colortypes.h b/Helios/Colortypes.h index a10d58ee..f7b5d7f3 100644 --- a/Helios/Colortypes.h +++ b/Helios/Colortypes.h @@ -6,6 +6,10 @@ #include "HeliosConfig.h" #include "ColorConstants.h" +#ifdef __cplusplus +extern "C" { +#endif + #if ALTERNATIVE_HSV_RGB == 1 enum hsv_to_rgb_algorithm { @@ -82,4 +86,8 @@ rgb_color_t hsv_to_rgb_generic(const hsv_color_t *rhs); /* Convert rgb to hsv with generic fast method */ hsv_color_t rgb_to_hsv_generic(const rgb_color_t *rhs); +#ifdef __cplusplus +} +#endif + #endif diff --git a/Helios/Helios.h b/Helios/Helios.h index 57140eee..3af4dbb7 100644 --- a/Helios/Helios.h +++ b/Helios/Helios.h @@ -1,6 +1,10 @@ #ifndef HELIOS_H #define HELIOS_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include "HeliosConfig.h" @@ -43,4 +47,8 @@ uint8_t helios_has_flag(enum helios_flags flag); void helios_clear_flag(enum helios_flags flag); void helios_toggle_flag(enum helios_flags flag); +#ifdef __cplusplus +} +#endif + #endif diff --git a/Helios/Led.h b/Helios/Led.h index e0f1bff8..cae2ca2c 100644 --- a/Helios/Led.h +++ b/Helios/Led.h @@ -1,6 +1,10 @@ #ifndef LED_CONTROL_H #define LED_CONTROL_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include "Colortypes.h" @@ -41,4 +45,8 @@ void led_set_brightness(uint8_t brightness); /* actually update the LEDs and show the changes */ void led_update(void); +#ifdef __cplusplus +} +#endif + #endif diff --git a/Helios/Pattern.h b/Helios/Pattern.h index 248a6bad..68b3c8fa 100644 --- a/Helios/Pattern.h +++ b/Helios/Pattern.h @@ -1,6 +1,10 @@ #ifndef PATTERN_H #define PATTERN_H +#ifdef __cplusplus +extern "C" { +#endif + #include "Colorset.h" #include "Timer.h" @@ -74,7 +78,7 @@ struct pattern_t enum pattern_state m_state; /* the blink timer used to measure blink timings */ - timer_t m_blinkTimer; + helios_timer_t m_blinkTimer; /* ================================== * Blend Members */ @@ -134,4 +138,8 @@ uint8_t pattern_is_blend(const pattern_t *pat); /* whether fade speed is non 0 */ uint8_t pattern_is_fade(const pattern_t *pat); +#ifdef __cplusplus +} +#endif + #endif diff --git a/Helios/Random.h b/Helios/Random.h index f4fcbca6..b5bee10f 100644 --- a/Helios/Random.h +++ b/Helios/Random.h @@ -1,6 +1,10 @@ #ifndef RANDOM_H #define RANDOM_H +#ifdef __cplusplus +extern "C" { +#endif + #include typedef struct random_t random_t; @@ -25,4 +29,8 @@ uint8_t random_next8(random_t *rng, uint8_t minValue, uint8_t maxValue); /* Generate next random 16-bit value within range [minValue, maxValue] */ uint16_t random_next16(random_t *rng, uint16_t minValue, uint16_t maxValue); +#ifdef __cplusplus +} +#endif + #endif diff --git a/Helios/Storage.h b/Helios/Storage.h index fc41e44e..79a1daa4 100644 --- a/Helios/Storage.h +++ b/Helios/Storage.h @@ -1,6 +1,10 @@ #ifndef STORAGE_H #define STORAGE_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include "HeliosConfig.h" @@ -45,4 +49,8 @@ uint8_t storage_crc8(uint8_t pos, uint8_t size); void storage_enable_storage(uint8_t enabled); #endif +#ifdef __cplusplus +} +#endif + #endif diff --git a/Helios/TimeControl.h b/Helios/TimeControl.h index b65eb8aa..85fd4aac 100644 --- a/Helios/TimeControl.h +++ b/Helios/TimeControl.h @@ -1,6 +1,10 @@ #ifndef TIME_CONTROL_H #define TIME_CONTROL_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include "HeliosConfig.h" @@ -36,4 +40,8 @@ void time_delay_milliseconds(uint32_t ms); void time_enable_timestep(uint8_t enabled); #endif +#ifdef __cplusplus +} +#endif + #endif diff --git a/Helios/Timer.c b/Helios/Timer.c index fd3aeb0a..e18be8ae 100644 --- a/Helios/Timer.c +++ b/Helios/Timer.c @@ -4,32 +4,32 @@ #include "TimeControl.h" -void timer_init_default(timer_t *timer) +void timer_init_default(helios_timer_t *timer) { timer->m_alarm = 0; timer->m_startTime = 0; } -void timer_init(timer_t *timer, uint8_t alarm) +void timer_init(helios_timer_t *timer, uint8_t alarm) { timer_reset(timer); timer->m_alarm = alarm; timer_start(timer, 0); } -void timer_start(timer_t *timer, uint32_t offset) +void timer_start(helios_timer_t *timer, uint32_t offset) { /* reset the start time */ timer->m_startTime = time_get_current_time() + offset; } -void timer_reset(timer_t *timer) +void timer_reset(helios_timer_t *timer) { timer->m_alarm = 0; timer->m_startTime = 0; } -uint8_t timer_alarm(timer_t *timer) +uint8_t timer_alarm(helios_timer_t *timer) { if (!timer->m_alarm) { return 0; diff --git a/Helios/Timer.h b/Helios/Timer.h index 46b8394e..40997d07 100644 --- a/Helios/Timer.h +++ b/Helios/Timer.h @@ -3,9 +3,13 @@ #include -typedef struct timer_t timer_t; +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct helios_timer_t helios_timer_t; -struct timer_t +struct helios_timer_t { /* the alarm */ uint32_t m_alarm; @@ -14,19 +18,23 @@ struct timer_t }; /* Initialize a timer struct to default values */ -void timer_init_default(timer_t *timer); +void timer_init_default(helios_timer_t *timer); /* Init a timer with a number of alarms and optionally start it */ -void timer_init(timer_t *timer, uint8_t alarm); +void timer_init(helios_timer_t *timer, uint8_t alarm); /* Start the timer but don't change current alarm, this shifts * the timer startTime but does not reset it's alarm state */ -void timer_start(timer_t *timer, uint32_t offset); +void timer_start(helios_timer_t *timer, uint32_t offset); /* Delete all alarms from the timer and reset */ -void timer_reset(timer_t *timer); +void timer_reset(helios_timer_t *timer); /* Will return true if the timer hit */ -uint8_t timer_alarm(timer_t *timer); +uint8_t timer_alarm(helios_timer_t *timer); + +#ifdef __cplusplus +} +#endif #endif diff --git a/HeliosCLI/Makefile b/HeliosCLI/Makefile index 9f5b6a82..c9178d1d 100644 --- a/HeliosCLI/Makefile +++ b/HeliosCLI/Makefile @@ -5,14 +5,16 @@ .PHONY: all tests clean pngs bmps clean_storage # compiler tool definitions -CC=g++ +CXX=g++ +CC=gcc AR=ar cru MAKE=make RM=rm -rf RANLIB=ranlib -CFLAGS=-O2 -g -Wall -std=c++11 +CXXFLAGS=-O2 -g -Wall -std=c++11 +CFLAGS=-O2 -g -Wall -std=c11 # compiler defines DEFINES=\ @@ -29,9 +31,11 @@ INCLUDES=\ # only set them if they're not empty to prevent unnecessary whitespace ifneq ($(DEFINES),) CFLAGS+=$(DEFINES) + CXXFLAGS+=$(DEFINES) endif ifneq ($(INCLUDES),) CFLAGS+=$(INCLUDES) + CXXFLAGS+=$(INCLUDES) endif # local NONSTANDARD libraries to link with @@ -52,20 +56,24 @@ LIBS=\ # source files # local source files first, other sources after ifeq ($(OS),Windows_NT) - SRC = $(shell find ../Helios -type f -name \\*.cpp) \ - $(shell find . -type f -name \\*.cpp) + C_SRC = $(shell find ../Helios -type f -name \\*.c) + CPP_SRC = $(shell find . -type f -name \\*.cpp -not -path "./venv/*") else - SRC = $(shell find ../Helios -type f -name '*.cpp') \ - $(shell find . -type f -name '*.cpp') + C_SRC = $(shell find ../Helios -type f -name '*.c') + CPP_SRC = $(shell find . -type f -name '*.cpp' -not -path "./venv/*") endif -# object files are source files with .c replaced with .o -OBJS=\ - $(SRC:.cpp=.o) \ +SRC = $(C_SRC) $(CPP_SRC) -# dependency files are source files with .c replaced with .d -DFILES=\ - $(SRC:.cpp=.d) \ +# object files are source files with .c/.cpp replaced with .o +C_OBJS = $(C_SRC:.c=.o) +CPP_OBJS = $(CPP_SRC:.cpp=.o) +OBJS = $(C_OBJS) $(CPP_OBJS) + +# dependency files +C_DFILES = $(C_SRC:.c=.d) +CPP_DFILES = $(CPP_SRC:.cpp=.d) +DFILES = $(C_DFILES) $(CPP_DFILES) # target dependencies # this includes any script generated c/h files, @@ -89,10 +97,14 @@ tests: $(TESTS) # target for vortex lib helios: compute_version $(DEPS) - $(CC) $(CFLAGS) $(DEPS) -o $@ $(LLIBS) + $(CXX) $(CXXFLAGS) $(DEPS) -o $@ $(LLIBS) -# catch-all make target to generate .o and .d files +# catch-all make target to generate .o and .d files from .cpp %.o: %.cpp + $(CXX) $(CXXFLAGS) -MMD -c $< -o $@ + +# catch-all make target to generate .o and .d files from .c +%.o: %.c $(CC) $(CFLAGS) -MMD -c $< -o $@ # catch-all for static libraries in the form of: @@ -110,7 +122,7 @@ clean: @$(RM) $(DFILES) $(OBJS) $(TARGETS) $(TESTS) compute_version: - $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$$' | sort -V | tail -n1)) + $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list | sort -V | tail -n1)) $(eval HELIOS_VERSION_MAJOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f1)) $(eval HELIOS_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f2)) $(eval LAST_HELIOS_BUILD_NUMBER ?= $(shell echo $(LATEST_TAG) | cut -d. -f3)) diff --git a/HeliosCLI/cli_main.cpp b/HeliosCLI/cli_main.cpp index 8552fb85..22d98734 100644 --- a/HeliosCLI/cli_main.cpp +++ b/HeliosCLI/cli_main.cpp @@ -22,6 +22,8 @@ #include "Colortypes.h" #include "Button.h" #include "Led.h" +#include "Patterns.h" +#include "Pattern.h" #include "color_map.h" /* @@ -53,7 +55,7 @@ bool timestep = true; bool eeprom = false; std::string eeprom_file; bool generate_bmp = false; -std::vector colorBuffer; +std::vector colorBuffer; uint32_t num_cycles = 0; float brightness_scale = 1.0f; uint8_t minumum_brightness = 75; @@ -71,7 +73,7 @@ static bool read_inputs(); static void show(); static void restore_terminal(); static void set_terminal_nonblocking(); -static bool writeBMP(const std::string& filename, const std::vector& colors); +static bool writeBMP(const std::string& filename, const std::vector& colors); static void print_usage(const char* program_name); static bool parse_eep_file(const std::string& filename, std::vector& memory); static bool parse_csv_hex(const std::string& filename, std::vector& memory); @@ -91,29 +93,30 @@ int main(int argc, char *argv[]) return 0; } // toggle timestep in the engine based on the cli input - Time::enableTimestep(timestep); + time_enable_timestep(timestep); // toggle storage in the engine based on cli input - Storage::enableStorage(storage); + storage_enable_storage(storage); // run the engine initialization - Helios::init(); + helios_init(); // set the initial mode index - Helios::set_mode_index(initial_mode_index); + helios_set_mode_index(initial_mode_index); // Set the initial pattern based on user arguments if (initial_pattern_str.length() > 0) { // convert the string arg to integer, then treat it as a PatternID - PatternID id = (PatternID)strtoul(initial_pattern_str.c_str(), NULL, 10); + pattern_id id = (pattern_id)strtoul(initial_pattern_str.c_str(), NULL, 10); // pass the current pattern to make_pattern to update it's internals - Patterns::make_pattern(id, Helios::cur_pattern()); + pattern_t* pat = helios_cur_pattern(); + patterns_make_pattern(id, pat); // re-initialize the current pattern - Helios::cur_pattern().init(); + pattern_init_state(pat); } // set initial pattern args based on user arguments if (initial_pattern_args_str.length() > 0) { // parse the list of args into an array of ints std::vector vals; std::istringstream ss(initial_pattern_args_str); - // push 6 args into the array - while (vals.size() < 6) { + // push 7 args into the array (on_dur, off_dur, gap_dur, dash_dur, group_size, blend_speed, fade_dur) + while (vals.size() < 7) { std::string arg; uint32_t val = 0; // try to parse out a number @@ -124,28 +127,40 @@ int main(int argc, char *argv[]) vals.push_back(val); } // construct pattern args from the array of values - PatternArgs args(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]); + pattern_args_t args; + pattern_args_init(&args, vals[0], vals[1], vals[2], vals[3], vals[4], vals[5], vals[6]); // set the args of the current pattern - Helios::cur_pattern().setArgs(args); + pattern_t* pat = helios_cur_pattern(); + pattern_set_args(pat, &args); } // Set the initial colorset based on user arguments if (initial_colorset_str.length() > 0) { std::stringstream ss(initial_colorset_str); std::string color; - Colorset set; + colorset_t set; + colorset_init(&set); while (getline(ss, color, ',')) { // iterate letters and lowercase them std::transform(color.begin(), color.end(), color.begin(), [](unsigned char c){ return tolower(c); }); + rgb_color_t rgb; if (color_map.count(color) > 0) { - set.addColor(color_map[color]); + uint32_t color_val = color_map[color]; + rgb.red = (color_val >> 16) & 0xFF; + rgb.green = (color_val >> 8) & 0xFF; + rgb.blue = color_val & 0xFF; } else { - set.addColor(strtoul(color.c_str(), nullptr, 16)); + uint32_t color_val = strtoul(color.c_str(), nullptr, 16); + rgb.red = (color_val >> 16) & 0xFF; + rgb.green = (color_val >> 8) & 0xFF; + rgb.blue = color_val & 0xFF; } + colorset_add_color(&set, rgb); } // update the colorset of the current pattern - Helios::cur_pattern().setColorset(set); + pattern_t* pat = helios_cur_pattern(); + pattern_set_colorset(pat, &set); // re-initialize the current pattern - Helios::cur_pattern().init(); + pattern_init_state(pat); } // just generate eeprom? if (eeprom) { @@ -155,19 +170,19 @@ int main(int argc, char *argv[]) // so that we can detect when one full cycle of the pattern has passed uint32_t cycle_count = 0; uint8_t last_index = 0; - while (Helios::keep_going()) { + while (helios_keep_going()) { // check for any inputs and read the next one read_inputs(); // if lockstep is enabled, only run logic if the // input queue isn't actually empty - if (lockstep && !Button::inputQueueSize()) { + if (lockstep && !button_input_queue_size()) { // just keep waiting for an input continue; } // run the main loop - Helios::tick(); + helios_tick(); // don't render anything if asleep, but technically it's still running... - if (Helios::is_asleep()) { + if (helios_is_asleep()) { continue; } // watch for a full cycle if it was requested by the command line @@ -175,7 +190,8 @@ int main(int argc, char *argv[]) // grab the current index of the colorset, which might be the same for // several tick in a row, so we must check whether it just changed this // tick by comparing it to the index we saved last tick - uint8_t cur_index = Helios::cur_pattern().colorset().curIndex(); + pattern_t* pat = helios_cur_pattern(); + uint8_t cur_index = pat->m_colorset.m_curIndex; if (cur_index == 0 && last_index != 0) { // only if the current index is 0 (start of colorset) and the last index was // not 0 then the colorset *just* started iterating through it's colors, so @@ -184,7 +200,7 @@ int main(int argc, char *argv[]) } // then if we run more than the chosen number of cycles just quit if (cycle_count >= num_cycles) { - Helios::terminate(); + helios_terminate(); break; } last_index = cur_index; @@ -389,7 +405,7 @@ static bool read_inputs() } for (uint32_t i = 0; i < repeatAmount; ++i) { // otherwise just queue up the command - Button::queueInput(command); + button_queue_input(command); } } return true; @@ -402,8 +418,9 @@ static void show() if (generate_bmp) { // still need to generate the BMP by recoring all the output colors // even if they have chosen the -q for quiet option - RGBColor currentColor = {Led::get().red, Led::get().green, Led::get().blue}; - RGBColor scaledColor = currentColor.scaleBrightness(brightness_scale); + rgb_color_t currentColor = led_get(); + rgb_color_t scaledColor = currentColor; + rgb_scale_brightness(&scaledColor, brightness_scale); colorBuffer.push_back(scaledColor); } return; @@ -414,8 +431,9 @@ static void show() out += "\r"; } // Get the current color and scale its brightness up - RGBColor currentColor = {Led::get().red, Led::get().green, Led::get().blue}; - RGBColor scaledColor = currentColor.scaleBrightness(brightness_scale); + rgb_color_t currentColor = led_get(); + rgb_color_t scaledColor = currentColor; + rgb_scale_brightness(&scaledColor, brightness_scale); if (output_type == OUTPUT_TYPE_COLOR) { out += "\x1B[0m["; // opening | out += "\x1B[48;2;"; // colorcode start @@ -473,7 +491,7 @@ static void set_terminal_nonblocking() atexit(restore_terminal); } -bool writeBMP(const std::string& filename, const std::vector& colors) +bool writeBMP(const std::string& filename, const std::vector& colors) { if (colors.empty()) { std::cerr << "Invalid image dimensions or empty color array." << std::endl; @@ -525,7 +543,7 @@ bool writeBMP(const std::string& filename, const std::vector& colors) // write out data for (int32_t y = height - 1; y >= 0; --y) { for (int32_t x = 0; x < width; ++x) { - const RGBColor& color = colors[y * width + x]; + const rgb_color_t& color = colors[y * width + x]; // BGR format const unsigned char pixel[3] = { color.blue, color.green, color.red }; file.write((const char *)pixel, 3); @@ -717,33 +735,32 @@ static void dump_eeprom(const std::string& filename) for (size_t slot = 0; slot < NUM_MODE_SLOTS; ++slot) { size_t pos = slot * SLOT_SIZE; - Pattern pat; - memcpy((void*)&pat, &memory[pos], sizeof(Pattern)); + pattern_t pat; + memcpy((void*)&pat, &memory[pos], sizeof(pattern_t)); printf("Slot %zu:\n", slot); printf(" Colorset: "); - for (size_t i = 0; i < pat.getColorset().numColors(); ++i) { - RGBColor color = pat.getColorset()[i]; + for (size_t i = 0; i < pat.m_colorset.m_numColors; ++i) { + rgb_color_t color = colorset_get(&pat.m_colorset, i); char hexCode[8]; snprintf(hexCode, sizeof(hexCode), "#%02X%02X%02X", color.red, color.green, color.blue); printf("\033[48;2;%d;%d;%dm \033[0m (%s) ", color.red, color.green, color.blue, hexCode); } printf("\n"); - PatternArgs args = pat.getArgs(); + pattern_args_t args = pat.m_args; printf(" Args: on_dur=%d, off_dur=%d, gap_dur=%d, dash_dur=%d, group_size=%d, blend_speed=%d\n", args.on_dur, args.off_dur, args.gap_dur, args.dash_dur, args.group_size, args.blend_speed); - printf(" Flags: %02X\n", pat.getFlags()); + printf(" Flags: %02X\n", pat.m_patternFlags); } uint8_t flags = (uint8_t)memory[CONFIG_START_INDEX - STORAGE_GLOBAL_FLAG_INDEX]; - bool locked = (flags & Helios::FLAG_LOCKED) != 0; - bool conjure = (flags & Helios::FLAG_CONJURE) != 0; + bool locked = (flags & FLAG_LOCKED) != 0; uint8_t modeIdx = (uint8_t)memory[CONFIG_START_INDEX - STORAGE_CURRENT_MODE_INDEX]; uint8_t brightness = (uint8_t)memory[CONFIG_START_INDEX - STORAGE_BRIGHTNESS_INDEX]; printf("Brightness: %u\n", brightness); printf("Mode Index: %u\n", modeIdx); - printf("Flags: 0x%02X (locked=%u conjure=%u)\n", flags, locked, conjure); + printf("Flags: 0x%02X (locked=%u)\n", flags, locked); } From ba79b2ef5d75cf2cf44747b51f750440105411a1 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Thu, 16 Oct 2025 22:54:31 +0200 Subject: [PATCH 04/23] Fix POSIX function declarations for Linux compatibility --- Helios/TimeControl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Helios/TimeControl.c b/Helios/TimeControl.c index cc80e655..9b3572dd 100644 --- a/Helios/TimeControl.c +++ b/Helios/TimeControl.c @@ -1,3 +1,8 @@ +/* Enable POSIX features for clock_gettime, usleep, etc. */ +#ifdef HELIOS_CLI +#define _POSIX_C_SOURCE 200112L +#endif + #include "TimeControl.h" #include @@ -17,7 +22,6 @@ #ifdef HELIOS_CLI #include #include -static uint64_t start = 0; /* convert seconds and nanoseconds to microseconds */ #define SEC_TO_US(sec) ((sec)*1000000) #define NS_TO_US(ns) ((ns)/1000) From aa96f654a9bdd992f747d239ffd2487ddd631f7f Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 09:59:03 +0200 Subject: [PATCH 05/23] Refactor Helios state management and color selection logic; update global flags handling and improve readability with consistent naming conventions. --- Helios/Helios.c | 1030 ++++++++++++++++++++++++++++++----------------- Helios/Helios.h | 42 +- 2 files changed, 696 insertions(+), 376 deletions(-) diff --git a/Helios/Helios.c b/Helios/Helios.c index 6949709c..3bb009d4 100644 --- a/Helios/Helios.c +++ b/Helios/Helios.c @@ -23,89 +23,77 @@ #include -/* some internal macros that shouldn't change */ -/* The number of menus in hue/sat/val selection */ -#define NUM_COLORS_PER_GROUP 4 -/* the number of color groups in the color selection menu */ -#define NUM_COLOR_GROUPS 4 -/* the number of menus in group selection */ -#define NUM_MENUS_GROUP 8 - -/* Forward declarations for internal functions */ -static uint8_t helios_init_components(void); -static void helios_handle_state(void); -static void helios_handle_state_modes(void); -static void helios_handle_off_menu(uint8_t mag, uint8_t past); -static void helios_handle_on_menu(uint8_t mag, uint8_t past); -static void helios_handle_state_color_selection(void); -static void helios_handle_state_color_group_selection(void); -static void helios_handle_state_color_variant_selection(void); -static void helios_handle_state_pat_select(void); -static void helios_handle_state_toggle_flag(enum helios_flags flag); -static void helios_handle_state_set_defaults(void); -static void helios_show_selection(rgb_color_t color); -static void helios_factory_reset(void); +/* Internal macros */ +#define NUM_MENUS_HUE_SAT_VAL 4 +#define NUM_MENUS_QUADRANT 7 -/* the slot selection returns this info for internal menu logic */ -enum helios_color_select_option { +/* Color select options for internal menu logic */ +enum color_select_option { OPTION_NONE = 0, - SELECTED_ADD, SELECTED_EXIT, SELECTED_SLOT }; -enum helios_state { - STATE_MODES, - STATE_COLOR_GROUP_SELECTION, - STATE_COLOR_VARIANT_SELECTION, - STATE_PATTERN_SELECT, - STATE_TOGGLE_LOCK, - STATE_SET_DEFAULTS, -#ifdef HELIOS_CLI - STATE_SLEEP, -#endif -}; - -/* static members */ -static enum helios_state cur_state; -static enum helios_flags global_flags; -static uint8_t menu_selection; -static uint8_t cur_mode; -static uint8_t selected_base_group; -static uint8_t num_colors_selected; /* Track number of colors selected in current session */ -static pattern_t pat; -static uint8_t keepgoing; -static uint32_t last_mode_switch_time; -static colorset_t new_colorset; +/* Global state variables */ +static enum helios_state g_cur_state; +static enum helios_flags g_global_flags; +static uint8_t g_menu_selection; +static uint8_t g_cur_mode; +static uint8_t g_selected_slot; +static uint8_t g_selected_base_quad; +static uint8_t g_selected_hue; +static uint8_t g_selected_sat; +static uint8_t g_selected_val; +static pattern_t g_pat; +static uint8_t g_keepgoing; #ifdef HELIOS_CLI -static uint8_t sleeping; /* Only used in CLI mode */ +static uint8_t g_sleeping; #endif volatile char helios_version[] = HELIOS_VERSION_STR; +/* Forward declarations for internal helper functions */ +static uint8_t helios_init_components(void); +static void helios_handle_state(void); +static void helios_handle_state_modes(void); +static void helios_handle_off_menu(uint8_t mag, uint8_t past); +static void helios_handle_on_menu(uint8_t mag, uint8_t past); +static void helios_handle_state_col_select(void); +static void helios_handle_state_col_select_slot(enum color_select_option *out_option); +static void helios_handle_state_col_select_quadrant(void); +static void helios_handle_state_col_select_hue_sat_val(void); +static void helios_handle_state_pat_select(void); +static void helios_handle_state_toggle_flag(enum helios_flags flag); +static void helios_handle_state_set_defaults(void); +static void helios_factory_reset(void); +static void helios_handle_state_set_global_brightness(void); +static void helios_handle_state_shift_mode(void); +static void helios_handle_state_randomize(void); +static void helios_show_selection(rgb_color_t color); + uint8_t helios_init(void) { - /* first initialize all the components of helios */ + // first initialize all the components of helios if (!helios_init_components()) { return 0; } - /* then initialize the hardware for embedded helios */ + // then initialize the hardware for embedded helios #ifdef HELIOS_EMBEDDED - /* Set PB0, PB1, PB4 as output */ + // Set PB0, PB1, PB4 as output DDRB |= (1 << DDB0) | (1 << DDB1) | (1 << DDB4); - /* Timer0 Configuration for PWM */ + // Timer0 Configuration for PWM TCCR0A = (1 << WGM01) | (1 << WGM00) | (1 << COM0A1) | (1 << COM0B1); - /* No prescaler */ + // No prescaler TCCR0B = (1 << CS00); - /* Timer1 for PWM on PB4, Fast PWM, Non-inverting, No prescaler */ + // Timer1 for PWM on PB4, Fast PWM, Non-inverting, No prescaler TCCR1 = (1 << PWM1A) | (1 << COM1A1) | (1 << CS10); - /* Enable PWM on OC1B */ + // Enable PWM on OC1B GTCCR = (1 << PWM1B) | (1 << COM1B1); - /* Enable Timer0 overflow interrupt */ + // Enable Timer0 overflow interrupt TIMSK |= (1 << TOIE0); - /* Enable interrupts */ + // Enable interrupts sei(); #endif return 1; @@ -113,7 +101,7 @@ uint8_t helios_init(void) static uint8_t helios_init_components(void) { - /* initialize various components of Helios */ + // initialize various components of Helios if (!time_init()) { return 0; } @@ -126,217 +114,210 @@ static uint8_t helios_init_components(void) if (!button_init()) { return 0; } - /* initialize global variables */ - cur_state = STATE_MODES; - menu_selection = 0; - cur_mode = 0; - num_colors_selected = 0; - selected_base_group = 0; - keepgoing = 1; - last_mode_switch_time = 0; + // initialize global variables + g_cur_state = STATE_MODES; + g_menu_selection = 0; + g_cur_mode = 0; + g_selected_slot = 0; + g_selected_base_quad = 0; + g_keepgoing = 1; #ifdef HELIOS_CLI - sleeping = 0; + g_sleeping = 0; #endif + // load global flags, and brightness from storage, this + // includes for example conjure mode and the mode index + // of the conjure mode if it is enabled helios_load_global_flags(); + // finally load whatever current mode index is selected + // this might be mode 0, or for example a separate index + // if conjure mode is enabled helios_load_cur_mode(); return 1; } void helios_tick(void) { - /* sample the button and re-calculate all button globals - * the button globals should not change anywhere else */ + // sample the button and re-calculate all button globals + // the button globals should not change anywhere else button_update(); - /* handle the current state of the system, ie whatever state - * we're in we check for the appropriate input events for that - * state by checking button globals, then run the appropriate logic */ + // handle the current state of the system, ie whatever state + // we're in we check for the appropriate input events for that + // state by checking button globals, then run the appropriate logic helios_handle_state(); - /* Update the Leds once per frame */ + // Update the Leds once per frame led_update(); - /* finally tick the clock forward and then sleep till the entire - * tick duration has been consumed */ + // finally tick the clock forward and then sleep till the entire + // tick duration has been consumed time_tick_clock(); } void helios_enter_sleep(void) { #ifdef HELIOS_EMBEDDED - /* clear the led colors */ + // clear the led colors led_clear(); - /* Set all pins to input */ + // Set all pins to input DDRB = 0x00; - /* Disable pull-ups on all pins */ + // Disable pull-ups on all pins PORTB = 0x00; - /* Enable wake on interrupt for the button */ + // Enable wake on interrupt for the button button_enable_wake(); - /* Set sleep mode to POWER DOWN mode */ + // Set sleep mode to POWER DOWN mode set_sleep_mode(SLEEP_MODE_PWR_DOWN); - /* enter sleep */ + // enter sleep sleep_mode(); - /* ... interrupt will make us wake here */ + // ... interrupt will make us wake here - /* Set PB0, PB1, PB4 as output */ + // Set PB0, PB1, PB4 as output DDRB |= (1 << DDB0) | (1 << DDB1) | (1 << DDB4); - /* wakeup here, re-init */ + // wakeup here, re-init helios_init_components(); #else - cur_state = STATE_SLEEP; - /* enable the sleep bool */ - sleeping = 1; + g_cur_state = STATE_SLEEP; + // enable the sleep uint8_t + g_sleeping = 1; #endif } void helios_wakeup(void) { #ifdef HELIOS_EMBEDDED - /* nothing needed here, this interrupt firing will make the mainthread resume */ + // nothing needed here, this interrupt firing will make the mainthread resume #else - /* if the button was held down then they are entering off-menus - * but if we re-initialize the button it will clear this state */ + // if the button was held down then they are entering off-menus + // but if we re-initialize the button it will clear this state uint8_t pressed = button_is_pressed(); - /* re-initialize some stuff */ + // re-initialize some stuff time_init(); button_init(); - /* so just re-press it */ + // so just re-press it if (pressed) { button_do_press(); } - cur_state = STATE_MODES; - /* turn off the sleeping flag that only CLI has */ - sleeping = 0; + g_cur_state = STATE_MODES; + // turn off the sleeping flag that only CLI has + g_sleeping = 0; #endif } void helios_load_next_mode(void) { - /* increment current mode and wrap around */ - cur_mode = (uint8_t)(cur_mode + 1) % NUM_MODE_SLOTS; - /* now load current mode again */ + // increment current mode and wrap around + g_cur_mode = (uint8_t)(g_cur_mode + 1) % NUM_MODE_SLOTS; + // now load current mode again helios_load_cur_mode(); } void helios_load_cur_mode(void) { - /* read pattern from storage at cur mode index */ - if (!storage_read_pattern(cur_mode, &pat)) { - /* and just initialize default if it cannot be read */ - patterns_make_default(cur_mode, &pat); - /* try to write it out because storage was corrupt */ - storage_write_pattern(cur_mode, &pat); + // read pattern from storage at cur mode index + if (!storage_read_pattern(g_cur_mode, &g_pat)) { + // and just initialize default if it cannot be read + patterns_make_default(g_cur_mode, &g_pat); + // try to write it out because storage was corrupt + storage_write_pattern(g_cur_mode, &g_pat); } - /* then re-initialize the pattern */ - pattern_init_state(&pat); - /* Update the last mode switch time when loading a mode */ - last_mode_switch_time = time_get_current_time(); + // then re-initialize the pattern + pattern_init_state(&g_pat); } void helios_save_cur_mode(void) { - storage_write_pattern(cur_mode, &pat); + storage_write_pattern(g_cur_mode, &g_pat); } void helios_load_global_flags(void) { - /* read the global flags from index 0 config */ - global_flags = (enum helios_flags)storage_read_global_flags(); - cur_mode = storage_read_current_mode(); + // read the global flags from index 0 config + g_global_flags = (enum helios_flags)storage_read_global_flags(); + if (helios_has_flags(FLAG_CONJURE)) { + // if conjure is enabled then load the current mode index from storage + g_cur_mode = storage_read_current_mode(); + } + // read the global brightness from index 2 config + uint8_t saved_brightness = storage_read_brightness(); + // Check if flags are valid (FLAGS_INVALID is inverse mask of valid flags) + // and brightness is set in storage + uint8_t is_valid = !helios_has_any_flags(FLAGS_INVALID) && saved_brightness > 0; + if (is_valid) { + led_set_brightness(saved_brightness); + } + + if (!is_valid) { + // if the brightness was 0 and the flags are invalid then the storage was likely + // uninitialized or corrupt so write out the defaults + helios_factory_reset(); + } } void helios_save_global_flags(void) { - storage_write_global_flags(global_flags); - storage_write_current_mode(cur_mode); + storage_write_global_flags(g_global_flags); + storage_write_current_mode(g_cur_mode); } void helios_set_mode_index(uint8_t mode_index) { - cur_mode = (uint8_t)mode_index % NUM_MODE_SLOTS; - /* now load current mode again */ + g_cur_mode = (uint8_t)mode_index % NUM_MODE_SLOTS; + // now load current mode again helios_load_cur_mode(); } -uint8_t helios_keep_going(void) -{ - return keepgoing; -} - -void helios_terminate(void) -{ - keepgoing = 0; -} - -void helios_set_flag(enum helios_flags flag) -{ - global_flags = (enum helios_flags)(global_flags | flag); -} - -uint8_t helios_has_flag(enum helios_flags flag) -{ - return (global_flags & flag) == flag; -} - -void helios_clear_flag(enum helios_flags flag) -{ - global_flags = (enum helios_flags)(global_flags & ~flag); -} - -void helios_toggle_flag(enum helios_flags flag) -{ - global_flags = (enum helios_flags)(global_flags ^ flag); -} - -#ifdef HELIOS_CLI -uint8_t helios_is_asleep(void) -{ - return sleeping; -} - -pattern_t *helios_cur_pattern(void) -{ - return &pat; -} -#endif - static void helios_handle_state(void) { - /* check for the force sleep button hold regardless of which state we're in */ + // check for the force sleep button hold regardless of which state we're in if (button_hold_duration() > FORCE_SLEEP_TIME) { - /* when released the device will just sleep */ + // when released the device will just sleep if (button_on_release()) { helios_enter_sleep(); - /* ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! */ + // ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! return; } - /* but as long as it's held past the sleep time it just turns off the led */ + // but as long as it's held past the sleep time it just turns off the led if (button_is_pressed()) { led_clear(); return; } } - /* otherwise just handle the state like normal */ - switch (cur_state) { + // otherwise just handle the state like normal + switch (g_cur_state) { case STATE_MODES: helios_handle_state_modes(); break; - case STATE_COLOR_GROUP_SELECTION: - case STATE_COLOR_VARIANT_SELECTION: - helios_handle_state_color_selection(); + case STATE_COLOR_SELECT_SLOT: + case STATE_COLOR_SELECT_QUADRANT: + case STATE_COLOR_SELECT_HUE: + case STATE_COLOR_SELECT_SAT: + case STATE_COLOR_SELECT_VAL: + helios_handle_state_col_select(); break; case STATE_PATTERN_SELECT: helios_handle_state_pat_select(); break; + case STATE_TOGGLE_CONJURE: + helios_handle_state_toggle_flag(FLAG_CONJURE); + break; case STATE_TOGGLE_LOCK: helios_handle_state_toggle_flag(FLAG_LOCKED); break; case STATE_SET_DEFAULTS: helios_handle_state_set_defaults(); break; + case STATE_SET_GLOBAL_BRIGHTNESS: + helios_handle_state_set_global_brightness(); + break; + case STATE_SHIFT_MODE: + helios_handle_state_shift_mode(); + break; + case STATE_RANDOMIZE: + helios_handle_state_randomize(); + break; #ifdef HELIOS_CLI case STATE_SLEEP: - /* simulate sleep in helios CLI */ + // simulate sleep in helios CLI if (button_on_press() || button_on_short_click() || button_on_long_click()) { helios_wakeup(); } @@ -347,111 +328,126 @@ static void helios_handle_state(void) static void helios_handle_state_modes(void) { - /* whether they have released the button since turning on */ + // whether they have released the button since turning on uint8_t hasReleased = (button_release_count() > 0); if (button_release_count() > 1 && button_on_short_click()) { - helios_enter_sleep(); + if (helios_has_flags(FLAG_CONJURE)) { + helios_enter_sleep(); + } else { + helios_load_next_mode(); + } return; } - /* check for lock and go back to sleep */ - if (helios_has_flag(FLAG_LOCKED) && hasReleased && !button_on_release()) { + // check for lock and go back to sleep + if (helios_has_flags(FLAG_LOCKED) && hasReleased && !button_on_release()) { helios_enter_sleep(); - /* ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! */ + // ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! return; } - if (!helios_has_flag(FLAG_LOCKED) && hasReleased) { - /* just play the current mode */ - pattern_play(&pat); + if (!helios_has_flags(FLAG_LOCKED) && hasReleased) { + // just play the current mode + pattern_play(&g_pat); } - /* check how long the button is held */ + // check how long the button is held uint32_t holdDur = button_hold_duration(); - /* calculate a magnitude which corresponds to how many times past the MENU_HOLD_TIME - * the user has held the button, so 0 means haven't held fully past one yet, etc */ + // calculate a magnitude which corresponds to how many times past the MENU_HOLD_TIME + // the user has held the button, so 0 means haven't held fully past one yet, etc uint8_t magnitude = (uint8_t)(holdDur / MENU_HOLD_TIME); - /* whether the user has held the button longer than a short click */ + // whether the user has held the button longer than a short click uint8_t heldPast = (holdDur > SHORT_CLICK_THRESHOLD); - /* flash red briefly when locked and short clicked */ - if (helios_has_flag(FLAG_LOCKED) && holdDur < SHORT_CLICK_THRESHOLD) { - rgb_color_t red; - rgb_init_from_raw(&red, RGB_RED_BRI_LOW); - led_set_rgb(&red); + // flash red briefly when locked and short clicked + if (helios_has_flags(FLAG_LOCKED) && !heldPast) { + rgb_color_t temp; + rgb_init_from_raw(&temp, RGB_RED_BRI_LOW); + led_set_rgb(&temp); } - /* if the button is held for at least 1 second */ + // if the button is held for at least 1 second if (button_is_pressed() && heldPast) { - rgb_color_t color; - /* if the button has been released before then show the on menu */ + // if the button has been released before then show the on menu if (hasReleased) { switch (magnitude) { default: - case 0: led_clear(); break; /* Turn off */ - case 1: rgb_init_from_raw(&color, RGB_TURQUOISE_BRI_LOW); led_set_rgb(&color); break; /* Color Selection */ - case 2: rgb_init_from_raw(&color, RGB_MAGENTA_BRI_LOW); led_set_rgb(&color); break; /* Pattern Selection */ + case 0: led_clear(); break; // Turn off + case 1: led_set_rgb3(0, 0x3c, 0x31); break; // Color Selection + case 2: led_set_rgb3(0x3c, 0, 0x0e); break; // Pattern Selection + case 3: led_set_rgb3(0x3c, 0x1c, 0); break; // Conjure Mode + case 4: led_set_rgb3(0x3c, 0x3c, 0x3c); break; // Shift Mode + case 5: { // Randomizer + hsv_color_t hsv_temp; + rgb_color_t rgb_temp; + hsv_init3(&hsv_temp, (uint8_t)time_get_current_time(), 255, 100); + rgb_init_from_hsv(&rgb_temp, &hsv_temp); + led_set_rgb(&rgb_temp); + } break; } } else { - if (helios_has_flag(FLAG_LOCKED)) { + if (helios_has_flags(FLAG_LOCKED)) { switch (magnitude) { default: case 0: led_clear(); break; - case TIME_TILL_GLOW_LOCK_UNLOCK: rgb_init_from_raw(&color, RGB_RED_BRI_LOW); led_set_rgb(&color); break; /* Exit */ + case TIME_TILL_GLOW_LOCK_UNLOCK: led_set_rgb3(0x3c, 0, 0); break; // Exit } } else { switch (magnitude) { default: - case 0: led_clear(); break; /* nothing */ - case 1: rgb_init_from_raw(&color, RGB_RED_BRI_LOW); led_set_rgb(&color); break; /* Enter Glow Lock */ - case 2: rgb_init_from_raw(&color, RGB_BLUE_BRI_LOW); led_set_rgb(&color); break; /* Master Reset */ + case 0: led_clear(); break; // nothing + case 1: led_set_rgb3(0x3c, 0, 0); break; // Enter Glow Lock + case 2: led_set_rgb3(0, 0x3c, 0); break; // Global Brightness + case 3: led_set_rgb3(0, 0, 0x3c); break; // Master Reset } } } } - /* if this isn't a release tick there's nothing more to do */ + // if this isn't a release tick there's nothing more to do if (button_on_release()) { - /* Resets the menu selection before entering new state */ - menu_selection = 0; + // Resets the menu selection before entering new state + g_menu_selection = 0; if (heldPast && button_release_count() == 1) { helios_handle_off_menu(magnitude, heldPast); return; } - /* otherwise if we have released it then we are in the 'on' menu */ + // otherwise if we have released it then we are in the 'on' menu helios_handle_on_menu(magnitude, heldPast); } } static void helios_handle_off_menu(uint8_t mag, uint8_t past) { - (void)past; /* unused */ - /* if still locked then handle the unlocking menu which is just if mag == 5 */ - if (helios_has_flag(FLAG_LOCKED)) { + // if still locked then handle the unlocking menu which is just if mag == 5 + if (helios_has_flags(FLAG_LOCKED)) { switch (mag) { - case TIME_TILL_GLOW_LOCK_UNLOCK: /* red lock */ - cur_state = STATE_TOGGLE_LOCK; + case TIME_TILL_GLOW_LOCK_UNLOCK: // red lock + g_cur_state = STATE_TOGGLE_LOCK; break; default: - /* just go back to sleep in hold-past off menu */ + // just go back to sleep in hold-past off menu helios_enter_sleep(); - /* ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! */ + // ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! } - /* in this case we return either way, since we're locked */ + // in this case we return either way, since we're locked return; } - /* otherwise if not locked handle the off menu */ + // otherwise if not locked handle the off menu switch (mag) { - case 1: /* red lock */ - cur_state = STATE_TOGGLE_LOCK; + case 1: // red lock + g_cur_state = STATE_TOGGLE_LOCK; led_clear(); - return; /* RETURN HERE */ - case 2: /* blue reset defaults */ - cur_state = STATE_SET_DEFAULTS; - return; /* RETURN HERE */ + return; // RETURN HERE + case 2: // green global brightness + g_cur_state = STATE_SET_GLOBAL_BRIGHTNESS; + return; // RETURN HERE + case 3: // blue reset defaults + g_cur_state = STATE_SET_DEFAULTS; + return; //RETURN HERE default: - /* just go back to sleep in hold-past off menu */ + // just go back to sleep in hold-past off menu helios_enter_sleep(); - /* ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! */ + // ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! return; } } @@ -459,236 +455,526 @@ static void helios_handle_off_menu(uint8_t mag, uint8_t past) static void helios_handle_on_menu(uint8_t mag, uint8_t past) { switch (mag) { - case 0: /* off */ - /* but only if we held for more than a short click */ + case 0: // off + // but only if we held for more than a short click if (past) { helios_enter_sleep(); - /* ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! */ + // ALWAYS RETURN AFTER SLEEP! WE WILL WAKE HERE! return; } break; - case 1: /* color select */ - cur_state = STATE_COLOR_GROUP_SELECTION; - /* reset the menu selection and colors selected */ - menu_selection = 0; - num_colors_selected = 0; - /* Store original colorset before clearing */ - new_colorset = *pattern_colorset_ptr(&pat); - /* Clear existing colors in pattern */ - colorset_clear(&new_colorset); + case 1: // color select + g_cur_state = STATE_COLOR_SELECT_SLOT; + // reset the menu selection + g_menu_selection = 0; #if ALTERNATIVE_HSV_RGB == 1 - /* use the nice hue to rgb rainbow */ + // use the nice hue to rgb rainbow g_hsv_rgb_alg = HSV_TO_RGB_RAINBOW; #endif break; - case 2: /* pat select */ - cur_state = STATE_PATTERN_SELECT; - /* reset the menu selection */ - menu_selection = 0; + case 2: // g_pat select + g_cur_state = STATE_PATTERN_SELECT; + // reset the menu selection + g_menu_selection = 0; + break; + case 3: // conjure mode + g_cur_state = STATE_TOGGLE_CONJURE; + led_clear(); + break; + case 4: // shift mode down + g_cur_state = STATE_SHIFT_MODE; break; - default: /* hold past */ + case 5: // randomizer + g_cur_state = STATE_RANDOMIZE; + break; + default: // hold past break; } } -struct colors_menu_data { - uint32_t colors[4]; -}; - -/* array of colors for selection */ -static const struct colors_menu_data color_menu_data[NUM_COLOR_GROUPS] = { - /* color0 color1 color2 color3 */ - /* =================================================================== */ - { {RGB_RED, RGB_CORAL_ORANGE, RGB_ORANGE, RGB_YELLOW} }, - { {RGB_LIME_GREEN, RGB_GREEN, RGB_SEAFOAM, RGB_TURQUOISE} }, - { {RGB_ICE_BLUE, RGB_LIGHT_BLUE, RGB_BLUE, RGB_ROYAL_BLUE} }, - { {RGB_PURPLE, RGB_PINK, RGB_HOT_PINK, RGB_MAGENTA} }, -}; - -static void helios_handle_state_color_selection(void) +static void helios_handle_state_col_select(void) { - switch (cur_state) { - case STATE_COLOR_GROUP_SELECTION: - /* pick the hue group */ - helios_handle_state_color_group_selection(); + enum color_select_option slot_option = OPTION_NONE; + switch (g_cur_state) { + case STATE_COLOR_SELECT_SLOT: + // pick the target colorset slot + helios_handle_state_col_select_slot(&slot_option); break; - case STATE_COLOR_VARIANT_SELECTION: - /* pick the hue */ - helios_handle_state_color_variant_selection(); + case STATE_COLOR_SELECT_QUADRANT: + // pick the hue quadrant + helios_handle_state_col_select_quadrant(); break; + case STATE_COLOR_SELECT_HUE: + case STATE_COLOR_SELECT_SAT: + case STATE_COLOR_SELECT_VAL: default: + // pick the hue sat or val + helios_handle_state_col_select_hue_sat_val(); break; } - /* get the current color */ + // get the current color rgb_color_t cur = led_get(); cur.red /= 2; cur.green /= 2; cur.blue /= 2; - /* show selection in all of these menus */ + // this is a stupid override for when we're exiting color select + // show a white selection instead + if (slot_option != OPTION_NONE) { + rgb_init_from_raw(&cur, RGB_WHITE_BRI_LOW); + } + // show selection in all of these menus helios_show_selection(cur); } -static void helios_handle_state_color_group_selection(void) +static void helios_handle_state_col_select_slot(enum color_select_option *out_option) { - rgb_color_t color; + colorset_t *set = &g_pat.m_colorset; + uint8_t num_cols = colorset_num_colors(set); if (button_on_short_click()) { - menu_selection = (menu_selection + 1) % NUM_COLOR_GROUPS; - } - - /* Display a sample color from the selected group */ - rgb_init_from_raw(&color, color_menu_data[menu_selection].colors[0]); - led_set_rgb(&color); - - if (button_on_long_click()) { - selected_base_group = menu_selection; - cur_state = STATE_COLOR_VARIANT_SELECTION; - menu_selection = 0; + // the number of menus in slot selection = all colors + exit + uint8_t num_menus = num_cols + 1; + // except if the number of colors is less than total color slots + if (num_cols < NUM_COLOR_SLOTS) { + // then we have another menu: add color + num_menus++; + } + g_menu_selection = (g_menu_selection + 1) % num_menus; } -} - -static void helios_handle_state_color_variant_selection(void) -{ - rgb_color_t color; - if (button_on_short_click()) { - /* If we've selected max colors, next click exits */ - if (num_colors_selected >= NUM_COLOR_SLOTS) { - /* Apply the newly built colorset */ - pattern_set_colorset(&pat, &new_colorset); - /* Save and return to normal mode */ - helios_save_cur_mode(); - cur_state = STATE_MODES; - menu_selection = 0; + uint8_t long_click = button_on_long_click(); + + // Reset the color selection variables, these are the hue/sat/val that have been selected + // in the following menus, this is a weird place to reset these but it ends up being the only + // place where it can be written once and still handle all the possible cases it needs to run + g_selected_sat = 255; + g_selected_val = 255; + + if (num_cols < NUM_COLOR_SLOTS && g_menu_selection == num_cols) { + // add color + *out_option = SELECTED_ADD; + rgb_color_t temp_col1, temp_col2; + rgb_init_from_raw(&temp_col1, RGB_WHITE_BRI_LOW); + rgb_init_from_raw(&temp_col2, RGB_OFF); + led_strobe(100, 100, &temp_col1, &temp_col2); + if (long_click) { + g_selected_slot = g_menu_selection; + } + } else if (g_menu_selection == num_cols + 1 || (num_cols == NUM_COLOR_SLOTS && g_menu_selection == num_cols)) { + // exit + *out_option = SELECTED_EXIT; + rgb_color_t temp_col1, temp_col2; + rgb_init_from_raw(&temp_col1, RGB_RED_BRI_LOW); + rgb_init_from_raw(&temp_col2, RGB_OFF); + led_strobe(60, 40, &temp_col1, &temp_col2); + if (long_click) { #if ALTERNATIVE_HSV_RGB == 1 + // restore hsv to rgb algorithm type, done color selection g_hsv_rgb_alg = HSV_TO_RGB_GENERIC; #endif + helios_save_cur_mode(); + g_cur_state = STATE_MODES; + return; + } + } else { + *out_option = SELECTED_SLOT; + g_selected_slot = g_menu_selection; + // render current selection + rgb_color_t col = colorset_get(set, g_selected_slot); + rgb_color_t empty_col; + rgb_init_from_raw(&empty_col, RGB_OFF); + if (rgb_equals(&col, &empty_col)) { + rgb_color_t temp_col1, temp_col2; + rgb_init_from_raw(&temp_col1, RGB_OFF); + rgb_init_from_raw(&temp_col2, RGB_WHITE_BRI_LOW); + led_strobe(1, 30, &temp_col1, &temp_col2); + } else { + rgb_color_t temp_col1; + rgb_init_from_raw(&temp_col1, RGB_OFF); + led_strobe(3, 30, &temp_col1, &col); + } + if (button_hold_pressing()) { + // flash red + rgb_color_t temp_col1; + rgb_init_from_raw(&temp_col1, RGB_RED_BRI_LOW); + led_strobe(150, 150, &temp_col1, &col); + } + if (button_on_hold_click()){ + colorset_remove_color(set, g_selected_slot); return; } + } + if (long_click) { + g_cur_state = (enum helios_state)(g_cur_state + 1); + // reset the menu selection + g_menu_selection = 0; + } +} - /* Cycle through colors in the group */ - menu_selection = (menu_selection + 1) % NUM_COLORS_PER_GROUP; +struct colors_menu_data { + uint8_t hues[4]; +}; +// array of hues for selection +static const struct colors_menu_data color_menu_data[4] = { + // hue0 hue1 hue2 hue3 + // ================================================================================== + { { HUE_RED, HUE_CORAL_ORANGE, HUE_ORANGE, HUE_YELLOW } }, + { { HUE_LIME_GREEN, HUE_GREEN, HUE_SEAFOAM, HUE_TURQUOISE } }, + { { HUE_ICE_BLUE, HUE_LIGHT_BLUE, HUE_BLUE, HUE_ROYAL_BLUE } }, + { { HUE_PURPLE, HUE_PINK, HUE_HOT_PINK, HUE_MAGENTA } }, +}; + +static void helios_handle_state_col_select_quadrant(void) +{ + if (button_on_short_click()) { + g_menu_selection = (g_menu_selection + 1) % NUM_MENUS_QUADRANT; } - /* Display the currently selected color */ - rgb_init_from_raw(&color, color_menu_data[selected_base_group].colors[menu_selection]); - led_set_rgb(&color); + uint8_t hue_quad = (g_menu_selection - 2) % 4; + if (g_menu_selection > 5) { + g_menu_selection = 0; + } if (button_on_long_click()) { - /* Add the selected color to the colorset */ - rgb_init_from_raw(&color, color_menu_data[selected_base_group].colors[menu_selection]); - if (colorset_add_color(&new_colorset, color)) { - num_colors_selected++; + // select hue/sat/val + switch (g_menu_selection) { + case 0: { // selected blank + // add blank to set + rgb_color_t blank_col; + rgb_init_from_raw(&blank_col, RGB_OFF); + colorset_set(&g_pat.m_colorset, g_selected_slot, blank_col); + // Return to the slot you were editing + g_menu_selection = g_selected_slot; + // go to slot selection - 1 because we will increment outside here + g_cur_state = STATE_COLOR_SELECT_SLOT; + // RETURN HERE + return; + } + case 1: // selected white + // adds white, skip hue/sat to brightness + g_selected_sat = 0; + g_menu_selection = 0; + g_cur_state = STATE_COLOR_SELECT_VAL; + // RETURN HERE + return; + default: // 2-5 + g_selected_base_quad = hue_quad; + break; } + } - /* If we've selected max colors, exit */ - if (num_colors_selected >= NUM_COLOR_SLOTS) { - /* Apply the newly built colorset */ - pattern_set_colorset(&pat, &new_colorset); - /* Save and return to normal mode */ - helios_save_cur_mode(); - cur_state = STATE_MODES; - menu_selection = 0; -#if ALTERNATIVE_HSV_RGB == 1 - g_hsv_rgb_alg = HSV_TO_RGB_GENERIC; -#endif - return; - } + // default col1/col2 to off and white for the first two options + rgb_color_t col1, col2; + uint16_t on_dur, off_dur; + rgb_init_from_raw(&col1, RGB_OFF); - /* Otherwise go back to group selection for next color */ - cur_state = STATE_COLOR_GROUP_SELECTION; - menu_selection = 0; + switch (g_menu_selection) { + case 0: // Blank Option + rgb_init_from_raw(&col2, RGB_WHITE_BRI_LOW); + on_dur = 1; + off_dur = 30; + break; + case 1: // White Option + rgb_init_from_raw(&col2, RGB_WHITE); + on_dur = 9; + off_dur = 0; + break; + default: { // Color options + hsv_color_t temp_hsv1, temp_hsv2; + hsv_init3(&temp_hsv1, color_menu_data[hue_quad].hues[0], 255, 255); + rgb_init_from_hsv(&col1, &temp_hsv1); + hsv_init3(&temp_hsv2, color_menu_data[hue_quad].hues[2], 255, 255); + rgb_init_from_hsv(&col2, &temp_hsv2); + on_dur = 500; + off_dur = 500; + } break; + } + led_strobe(on_dur, off_dur, &col1, &col2); + // show a white flash for the first two menus + if (g_menu_selection <= 1) { + rgb_color_t temp; + rgb_init_from_raw(&temp, RGB_WHITE_BRI_LOW); + helios_show_selection(temp); + } else { + // dim the color for the quad menus + rgb_color_t cur = led_get(); + cur.red /= 2; + cur.green /= 2; + cur.blue /= 2; + rgb_color_t temp; + rgb_init_from_raw(&temp, RGB_WHITE_BRI_LOW); + helios_show_selection(temp); + } + if (button_on_long_click()) { + g_cur_state = (enum helios_state)(g_cur_state + 1); + // reset the menu selection + g_menu_selection = 0; } } -static void helios_handle_state_pat_select(void) +static void helios_handle_state_col_select_hue_sat_val(void) { - rgb_color_t color; - + // handle iterating to the next option if (button_on_short_click()) { - menu_selection = (menu_selection + 1) % PATTERN_COUNT; + g_menu_selection = (g_menu_selection + 1) % NUM_MENUS_HUE_SAT_VAL; } - - /* show the menu selection */ - switch (menu_selection) { - case 0: rgb_init_from_raw(&color, RGB_RED); break; - case 1: rgb_init_from_raw(&color, RGB_GREEN); break; - case 2: rgb_init_from_raw(&color, RGB_BLUE); break; - case 3: rgb_init_from_raw(&color, RGB_YELLOW); break; - case 4: rgb_init_from_raw(&color, RGB_MAGENTA); break; - default: rgb_init_from_raw(&color, RGB_WHITE); break; + // in the sat/val selection a longclick is next and hold is save but in + // the final val selection a longclick is save and there's no next + uint8_t gotoNextMenu = button_on_long_click(); + uint8_t saveAndFinish = button_on_hold_click(); + switch (g_cur_state) { + default: + case STATE_COLOR_SELECT_HUE: + g_selected_hue = color_menu_data[g_selected_base_quad].hues[g_menu_selection]; + break; + case STATE_COLOR_SELECT_SAT: { + static const uint8_t saturation_values[4] = {HSV_SAT_HIGH, HSV_SAT_MEDIUM, HSV_SAT_LOW, HSV_SAT_LOWEST}; + g_selected_sat = saturation_values[g_menu_selection]; + } break; + case STATE_COLOR_SELECT_VAL: { + static const uint8_t hsv_values[4] = {HSV_VAL_HIGH, HSV_VAL_MEDIUM, HSV_VAL_LOW, HSV_VAL_LOWEST}; + g_selected_val = hsv_values[g_menu_selection]; + // longclick becomes save and there is no next + saveAndFinish = gotoNextMenu; + } break; } - led_set_rgb(&color); + // render current selection + hsv_color_t hsv_sel; + hsv_init3(&hsv_sel, g_selected_hue, g_selected_sat, g_selected_val); + rgb_color_t rgb_sel; + rgb_init_from_hsv(&rgb_sel, &hsv_sel); + led_set_rgb(&rgb_sel); + // show the long selection flash + if (button_hold_pressing()) { + rgb_color_t temp_col = led_get(); + rgb_color_t temp_col1; + rgb_init_from_raw(&temp_col1, RGB_CORAL_ORANGE_SAT_LOWEST); + led_strobe(150, 150, &temp_col1, &temp_col); + } + // check to see if we are holding to save and skip + if (saveAndFinish) { + g_cur_state = STATE_COLOR_SELECT_SLOT; + hsv_color_t hsv_final; + hsv_init3(&hsv_final, g_selected_hue, g_selected_sat, g_selected_val); + rgb_color_t rgb_final; + rgb_init_from_hsv(&rgb_final, &hsv_final); + pattern_update_color(&g_pat, g_selected_slot, &rgb_final); + helios_save_cur_mode(); + // Return to the slot you were editing + g_menu_selection = g_selected_slot; + return; + } + if (gotoNextMenu) { + g_cur_state = (enum helios_state)(g_cur_state + 1); + // reset the menu selection + g_menu_selection = 0; + } +} +static void helios_handle_state_pat_select(void) +{ if (button_on_long_click()) { - /* make the selected pattern */ - patterns_make_pattern((enum pattern_id)(PATTERN_FIRST + menu_selection), &pat); - /* reset the pattern to revert to on/off state */ - pattern_init_state(&pat); - /* save and return to normal mode */ helios_save_cur_mode(); - cur_state = STATE_MODES; - menu_selection = 0; + g_cur_state = STATE_MODES; } + if (button_on_short_click()) { + patterns_make_pattern((enum pattern_id)g_menu_selection, &g_pat); + g_menu_selection = (g_menu_selection + 1) % PATTERN_COUNT; + pattern_init_state(&g_pat); + } + pattern_play(&g_pat); + rgb_color_t temp; + rgb_init_from_raw(&temp, RGB_MAGENTA_BRI_LOW); + helios_show_selection(temp); } static void helios_handle_state_toggle_flag(enum helios_flags flag) { - rgb_color_t color; + // toggle the conjure flag + helios_toggle_flags(flag); + // write out the new global flags and the current mode + helios_save_global_flags(); + // switch back to modes + g_cur_state = STATE_MODES; +} - /* wait until button release then toggle the flag */ - if (button_on_release()) { - helios_toggle_flag(flag); - helios_save_global_flags(); - /* show feedback based on new state */ - if (helios_has_flag(flag)) { - rgb_init_from_raw(&color, RGB_GREEN); - } else { - rgb_init_from_raw(&color, RGB_RED); +static void helios_handle_state_set_defaults(void) +{ + if (button_on_short_click()) { + g_menu_selection = !g_menu_selection; + } + // show low white for exit or red for select + rgb_color_t temp_col1, temp_col2; + if (g_menu_selection) { + rgb_init_from_raw(&temp_col1, RGB_RED_BRI_LOW); + rgb_init_from_raw(&temp_col2, RGB_OFF); + led_strobe(80, 20, &temp_col1, &temp_col2); + } else { + rgb_init_from_raw(&temp_col1, RGB_WHITE_BRI_LOWEST); + rgb_init_from_raw(&temp_col2, RGB_OFF); + led_strobe(20, 10, &temp_col1, &temp_col2); + } + // when the user long clicks a selection + if (button_on_long_click()) { + // if the user actually selected 'yes' + if (g_menu_selection == 1) { + helios_factory_reset(); } - led_hold(&color); - cur_state = STATE_MODES; + g_cur_state = STATE_MODES; } + rgb_color_t temp; + rgb_init_from_raw(&temp, RGB_WHITE_BRI_LOW); + helios_show_selection(temp); } -static void helios_handle_state_set_defaults(void) +static void helios_factory_reset(void) { - rgb_color_t color; + for (uint8_t i = 0; i < NUM_MODE_SLOTS; ++i) { + patterns_make_default(i, &g_pat); + storage_write_pattern(i, &g_pat); + } + // Reset global brightness to default + led_set_brightness(DEFAULT_BRIGHTNESS); + storage_write_brightness(DEFAULT_BRIGHTNESS); + // reset global flags + g_global_flags = FLAG_NONE; + g_cur_mode = 0; + // save global flags + helios_save_global_flags(); + // re-load current mode + helios_load_cur_mode(); +} - /* wait until button release then factory reset */ - if (button_on_release()) { - helios_factory_reset(); - /* show feedback */ - rgb_init_from_raw(&color, RGB_BLUE); - led_hold(&color); - cur_state = STATE_MODES; +static void helios_handle_state_set_global_brightness(void) +{ + if (button_on_short_click()) { + g_menu_selection = (g_menu_selection + 1) % NUM_BRIGHTNESS_OPTIONS; } + // show different levels of green for each selection + uint8_t col = 0; + uint8_t brightness = 0; + switch (g_menu_selection) { + case 0: + col = 0xFF; + brightness = BRIGHTNESS_HIGH; + break; + case 1: + col = 0x78; + brightness = BRIGHTNESS_MEDIUM; + break; + case 2: + col = 0x3c; + brightness = BRIGHTNESS_LOW; + break; + case 3: + col = 0x28; + brightness = BRIGHTNESS_LOWEST; + break; + } + led_set_rgb3(0, col, 0); + // when the user long clicks a selection + if (button_on_long_click()) { + // set the brightness based on the selection + led_set_brightness(brightness); + storage_write_brightness(brightness); + g_cur_state = STATE_MODES; + } + rgb_color_t temp; + rgb_init_from_raw(&temp, RGB_WHITE_BRI_LOW); + helios_show_selection(temp); } -static void helios_show_selection(rgb_color_t color) +static void helios_handle_state_shift_mode(void) { - uint32_t time_since_click = time_get_current_time(); - if (button_press_time() > 0) { - time_since_click = time_get_current_time() - button_press_time(); + uint8_t new_mode = (g_cur_mode > 0) ? (uint8_t)(g_cur_mode - 1) : (uint8_t)(NUM_MODE_SLOTS - 1); + // copy the storage from the new position into our current position + storage_copy_slot(new_mode, g_cur_mode); + // point at the new position + g_cur_mode = new_mode; + // write out the current mode to the newly updated position + helios_save_cur_mode(); + g_cur_state = STATE_MODES; +} + +static void helios_handle_state_randomize(void) +{ + if (button_on_short_click()) { + colorset_t *cur_set = &g_pat.m_colorset; + random_t ctx; + random_init_seed(&ctx, pattern_crc32(&g_pat)); + uint8_t randVal = random_next8(&ctx, 0, 255); + colorset_randomize_colors(cur_set, &ctx, (randVal + 1) % NUM_COLOR_SLOTS, COLOR_MODE_RANDOMLY_PICK); + patterns_make_pattern((enum pattern_id)(randVal % PATTERN_COUNT), &g_pat); + pattern_init_state(&g_pat); } - /* flash the selection color briefly after clicking */ - if (time_since_click < 150) { - led_set_rgb(&color); + if (button_on_long_click()) { + helios_save_cur_mode(); + g_cur_state = STATE_MODES; } + pattern_play(&g_pat); + rgb_color_t temp; + rgb_init_from_raw(&temp, RGB_WHITE_BRI_LOW); + helios_show_selection(temp); } -static void helios_factory_reset(void) +static void helios_show_selection(rgb_color_t color) { - uint8_t slot; - /* write default patterns to all slots */ - for (slot = 0; slot < NUM_MODE_SLOTS; ++slot) { - patterns_make_default(slot, &pat); - storage_write_pattern(slot, &pat); + // only show selection while pressing the button + if (!button_is_pressed()) { + return; } - /* clear all flags */ - global_flags = FLAG_NONE; - helios_save_global_flags(); - /* reload current mode */ - helios_load_cur_mode(); + uint16_t holdDur = (uint16_t)button_hold_duration(); + // if the hold duration is outside the flashing range do nothing + if (holdDur < SHORT_CLICK_THRESHOLD || holdDur >= HOLD_CLICK_START) { + return; + } + led_set_rgb(&color); +} + +/* Flag manipulation functions */ +void helios_set_flags(enum helios_flags flag) +{ + g_global_flags = (enum helios_flags)(g_global_flags | flag); +} + +uint8_t helios_has_flags(enum helios_flags flag) +{ + return (g_global_flags & flag) == flag; } +uint8_t helios_has_any_flags(enum helios_flags flag) +{ + return (g_global_flags & flag) != 0; +} + +void helios_clear_flags(enum helios_flags flag) +{ + g_global_flags = (enum helios_flags)(g_global_flags & ~flag); +} + +void helios_toggle_flags(enum helios_flags flag) +{ + g_global_flags = (enum helios_flags)(g_global_flags ^ flag); +} + +uint8_t helios_keep_going(void) +{ + return g_keepgoing; +} + +void helios_terminate(void) +{ + g_keepgoing = 0; +} + +#ifdef HELIOS_CLI +uint8_t helios_is_asleep(void) +{ + return g_sleeping; +} + +pattern_t *helios_cur_pattern(void) +{ + return &g_pat; +} +#endif diff --git a/Helios/Helios.h b/Helios/Helios.h index 3af4dbb7..14c95b04 100644 --- a/Helios/Helios.h +++ b/Helios/Helios.h @@ -36,16 +36,50 @@ uint8_t helios_is_asleep(void); pattern_t *helios_cur_pattern(void); #endif +enum helios_state { + STATE_MODES, + STATE_COLOR_SELECT_SLOT, + STATE_COLOR_SELECT_QUADRANT, + STATE_COLOR_SELECT_HUE, + STATE_COLOR_SELECT_SAT, + STATE_COLOR_SELECT_VAL, + STATE_PATTERN_SELECT, + STATE_TOGGLE_CONJURE, + STATE_TOGGLE_LOCK, + STATE_SET_DEFAULTS, + STATE_SET_GLOBAL_BRIGHTNESS, + STATE_SHIFT_MODE, + STATE_RANDOMIZE, +#ifdef HELIOS_CLI + STATE_SLEEP, +#endif +}; + enum helios_flags { + /* No flags are set */ FLAG_NONE = 0, + /* The device is locked and must be unlocked to turn on */ FLAG_LOCKED = (1 << 0), + /* Conjure mode is enabled, one click will toggle power */ + FLAG_CONJURE = (1 << 1), + /* Autoplay is enabled, modes will automatically cycle */ + FLAG_AUTOPLAY = (1 << 2), + /* Add new flags here, max 8 flags */ + + /* ============================================== */ + /* Auto increment to count the number of flags */ + INTERNAL_FLAGS_END, + /* Calculate mask for invalid Flags based on the + * inverse of all flags listed above here */ + FLAGS_INVALID = (uint8_t)(~((1 << (INTERNAL_FLAGS_END - 1)) - 1)) }; /* get/set global flags */ -void helios_set_flag(enum helios_flags flag); -uint8_t helios_has_flag(enum helios_flags flag); -void helios_clear_flag(enum helios_flags flag); -void helios_toggle_flag(enum helios_flags flag); +void helios_set_flags(enum helios_flags flag); +uint8_t helios_has_flags(enum helios_flags flag); +uint8_t helios_has_any_flags(enum helios_flags flag); +void helios_clear_flags(enum helios_flags flag); +void helios_toggle_flags(enum helios_flags flag); #ifdef __cplusplus } From 495100a60ed66ee876fb0f08e7ac334be12d71d6 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 10:33:42 +0200 Subject: [PATCH 06/23] Increase CLI input queue size to 4096 for handling longer test sequences and enable wake on button press in sleep mode. --- Helios/Button.c | 5 +++-- Helios/Helios.c | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Helios/Button.c b/Helios/Button.c index 3ea1f9f2..e08587ac 100644 --- a/Helios/Button.c +++ b/Helios/Button.c @@ -42,7 +42,8 @@ static uint8_t m_holdClick = 0; static uint8_t m_pinState = 0; static uint8_t m_enableWake = 0; /* Simple input queue for CLI - using a fixed-size circular buffer */ -#define INPUT_QUEUE_SIZE 64 +/* Larger queue size for CLI to handle long test sequences */ +#define INPUT_QUEUE_SIZE 4096 static char m_inputQueue[INPUT_QUEUE_SIZE]; static uint32_t m_queueHead = 0; static uint32_t m_queueTail = 0; @@ -88,7 +89,7 @@ void button_enable_wake(void) GIMSK |= (1 << PCIE); sei(); #else /* HELIOS_CLI */ - m_enableWake = 0; + m_enableWake = 1; #endif } diff --git a/Helios/Helios.c b/Helios/Helios.c index 3bb009d4..f5062f82 100644 --- a/Helios/Helios.c +++ b/Helios/Helios.c @@ -179,6 +179,8 @@ void helios_enter_sleep(void) g_cur_state = STATE_SLEEP; // enable the sleep uint8_t g_sleeping = 1; + // Enable wake on button press/click for CLI + button_enable_wake(); #endif } From 148195e257561684faa2ea71b83e266b220ec7de Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 11:20:11 +0200 Subject: [PATCH 07/23] Increase CLI input queue size to 8192 to accommodate longer test sequences. --- Helios/Button.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Helios/Button.c b/Helios/Button.c index e08587ac..00b27245 100644 --- a/Helios/Button.c +++ b/Helios/Button.c @@ -43,7 +43,7 @@ static uint8_t m_pinState = 0; static uint8_t m_enableWake = 0; /* Simple input queue for CLI - using a fixed-size circular buffer */ /* Larger queue size for CLI to handle long test sequences */ -#define INPUT_QUEUE_SIZE 4096 +#define INPUT_QUEUE_SIZE 8192 static char m_inputQueue[INPUT_QUEUE_SIZE]; static uint32_t m_queueHead = 0; static uint32_t m_queueTail = 0; From befc15dae685eb32d404fc6d75e4571ca553477e Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 11:46:52 +0200 Subject: [PATCH 08/23] Added Claude.md file --- CLAUDE.md | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..3b603ad4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,240 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +Helios Engine is an embedded LED control system designed for the ATTiny85 microcontroller. The codebase is split into three main components: +- **Helios/**: Core engine code (C) shared between embedded and CLI +- **HeliosEmbedded/**: ATTiny85 firmware implementation +- **HeliosCLI/**: Command-line tool for simulation and testing (C++) +- **HeliosLib/**: Static library build of Helios core + +## Build Commands + +### Building the CLI Tool +```bash +cd HeliosCLI +make # Build the CLI executable +make clean # Clean build artifacts +make bmps # Generate BMP pattern visualizations +make pngs # Generate PNG pattern visualizations +make svgs # Generate SVG pattern visualizations +make clean_storage # Delete Helios.storage file +``` + +### Building the Embedded Firmware +```bash +cd HeliosEmbedded +make # Build firmware for ATTiny85 +make upload # Compile and upload to ATTiny85 (sets fuses automatically) +make clean # Clean build artifacts +``` + +### Building the Library +```bash +cd HeliosLib +make # Build static library +make wasm # Build WebAssembly version +make clean # Clean build artifacts +``` + +### Clock Speed Configuration +The ATTiny85 firmware supports 8MHz and 16MHz (1MHz not supported). Edit `HeliosEmbedded/Makefile` and set: +```makefile +CPU_SPEED = 8000000L # For 8MHz +CPU_SPEED = 16000000L # For 16MHz +``` + +### Fuse Settings +```bash +cd HeliosEmbedded +make set_8mhz_fuses # Set fuses for 8MHz +make set_16mhz_fuses # Set fuses for 16MHz +make get_fuses # Read current fuse settings +``` + +## Testing + +### Running All Tests +```bash +cd tests +./runtests.sh # Run all tests +./runtests.sh -v # Verbose mode +./runtests.sh -t=5 # Run specific test number +./runtests.sh -n # Skip rebuild of helios executable +./runtests.sh -f # Run with Valgrind for memory leak detection +``` + +### Test File Structure +Tests use `.test` files with this format: +``` +Input= +Brief= +Args= +-------------------------------------------------------------------------------- + +``` + +### CLI Input Commands +- `c` - Short click +- `l` - Long click +- `p` - Press and hold +- `r` - Release button +- `t` - Toggle press state +- `w` - Wait one tick (1ms at TICKRATE=1000) +- `q` - Quit simulation +- `[number]` - Repeat next command N times (e.g., `300w` = wait 300 ticks) + +### Creating Tests +```bash +cd tests +./create_test.sh # Interactive test creation +./record_test.sh # Record a single test +./recordtests.sh # Record multiple tests +``` + +## Architecture + +### Core State Machine +Helios operates as a state machine defined in `Helios/Helios.h`: +- `STATE_MODES` - Normal mode operation +- `STATE_COLOR_SELECT_*` - Color selection menus (slot, quadrant, hue, sat, val) +- `STATE_PATTERN_SELECT` - Pattern selection menu +- `STATE_TOGGLE_CONJURE` - Toggle conjure mode (one-click on/off) +- `STATE_TOGGLE_LOCK` - Toggle light lock +- `STATE_SET_DEFAULTS` - Factory reset +- `STATE_SET_GLOBAL_BRIGHTNESS` - Brightness adjustment +- `STATE_SHIFT_MODE` - Mode slot shifting +- `STATE_RANDOMIZE` - Randomize colors/patterns + +### Global Flags +Defined in `Helios/Helios.h`: +- `FLAG_LOCKED` - Device is locked, must unlock to turn on +- `FLAG_CONJURE` - Conjure mode enabled (single click toggles on/off) +- `FLAG_AUTOPLAY` - Timer mode, automatically cycles modes + +### Key Components + +**Pattern System** (`Helios/Pattern.h`, `Helios/Pattern.c`): +- Manages LED timing: on_dur, off_dur, gap_dur, dash_dur +- Supports blinking, blending (morphing), and complex dash patterns +- Works with colorsets to produce visual effects + +**Colorset System** (`Helios/Colorset.h`, `Helios/Colorset.c`): +- Manages up to 6 colors per pattern (NUM_COLOR_SLOTS) +- Supports RGB and HSV color representations +- Pre-defined colors in `Helios/ColorConstants.h` + +**Button Handling** (`Helios/Button.h`, `Helios/Button.c`): +- Detects short clicks (<400ms), long clicks, and holds (>1000ms) +- `SHORT_CLICK_THRESHOLD`, `MENU_HOLD_TIME`, `FORCE_SLEEP_TIME` in `HeliosConfig.h` + +**Storage** (`Helios/Storage.h`, `Helios/Storage.c`): +- Persists 6 mode slots (NUM_MODE_SLOTS) and global flags +- EEPROM on ATTiny85 (256 bytes used of 512 available) +- CLI simulates storage with `Helios.storage` file + +**LED Control** (`Helios/Led.h`, `Helios/Led.c`): +- Platform-specific RGB LED output +- Embedded: Direct ATTiny85 pin control +- CLI: Terminal color output or hex values + +### Configuration Constants +All in `Helios/HeliosConfig.h`: +- `NUM_COLOR_SLOTS = 6` - Colors per pattern +- `NUM_MODE_SLOTS = 6` - Number of mode presets +- `TICKRATE = 1000` - Engine ticks per second +- `SHORT_CLICK_THRESHOLD = 400` - Short vs long click timing (ms) +- `MENU_HOLD_TIME = 1000` - Button hold time to open menus (ms) +- `FORCE_SLEEP_TIME = 7000` - Hold duration to force sleep (ms) +- `STORAGE_SIZE = 256` - EEPROM storage size (bytes) + +### Version System +Version is automatically computed from git tags in the format `MAJOR.MINOR.BUILD`: +- Major/Minor from latest git tag +- Build number = last tag's build + commits since tag +- Defined at compile time via Makefile `compute_version` target + +## Platform-Specific Code + +**Embedded** (`HeliosEmbedded/main.c`): +- Initializes ATTiny85 hardware (LED pins, button, sleep modes) +- Main loop calls `helios_tick()` at TICKRATE +- Uses AVR sleep modes for power efficiency + +**CLI** (`HeliosCLI/cli_main.cpp`): +- Simulates hardware with terminal I/O +- Reads input commands from stdin +- Outputs LED colors as hex or ANSI terminal colors +- Can generate BMP images of pattern sequences + +## Common Development Workflows + +### Adding a New Pattern +1. Define timing parameters in `Helios/Patterns.c` (on_dur, off_dur, gap_dur, etc.) +2. Add pattern to default pattern list +3. Rebuild CLI: `cd HeliosCLI && make` +4. Test pattern: `./helios -P -C "red,blue"` +5. Create test in `tests/` for the new pattern +6. Generate visualization: `make bmps pngs` + +### Modifying State Machine Behavior +1. State logic is in `Helios/Helios.c` +2. Each state has a handler function (e.g., `handle_state_modes()`) +3. Button events trigger state transitions +4. Test state transitions thoroughly with CLI before flashing to hardware + +### Debugging with CLI +```bash +cd HeliosCLI +# Interactive mode with color output +./helios -c -i + +# Test specific sequence +./helios -x <<< "300wcw300wcp1500wr300wq" + +# Test with specific pattern and colors +./helios -P 1 -C "red,green,blue" -x <<< "500wq" +``` + +### Flashing Firmware +1. Connect ISP programmer to ATTiny85 (see README.md wiring) +2. Configure `HeliosEmbedded/Makefile` AVRDUDE settings for your programmer +3. Build and upload: `cd HeliosEmbedded && make upload` +4. First upload sets fuses automatically based on CPU_SPEED + +## Important Notes + +### Memory Constraints +ATTiny85 has only 8KB flash and 512 bytes SRAM. Code must be: +- Highly optimized (use `-Os`, `-flto`, `-ffunction-sections`) +- Avoid dynamic allocation +- Minimize string constants +- Use `uint8_t` and bitfields aggressively + +### Timing Requirements +- Engine runs at TICKRATE (1000 ticks/sec = 1ms per tick) +- Pattern timings are in milliseconds/ticks +- Button timing constants must align with user expectations +- Sleep mode reduces power but adds wake latency + +### Storage Format +- Each mode slot = 28 bytes (colorset + pattern args + CRC) +- Global flags = 1 byte +- Storage format version tied to HELIOS_VERSION_MAJOR +- Changing storage format requires major version bump + +### Cross-Platform Compatibility +- Core Helios code is pure C for maximum portability +- Use `#ifdef HELIOS_CLI` for CLI-specific code +- Use `#ifdef HELIOS_EMBEDDED` for embedded-specific code +- Platform abstraction in LED and Button implementations + +## CI/CD + +GitHub Actions workflow (`.github/workflows/build.yml`): +- Builds on every push and PR to master branch +- Compiles both CLI and embedded firmware +- Runs full test suite +- Validates build on Linux From afa36297f459a5a1097cbff2478d5f5f4c4037f3 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 12:20:10 +0200 Subject: [PATCH 09/23] Refactor pattern argument handling by removing fade duration; update related functions and initialization to streamline pattern configuration. --- Helios/Pattern.c | 77 ++++-------------------------------------- Helios/Pattern.h | 17 ++-------- Helios/Patterns.c | 4 +-- HeliosCLI/cli_main.cpp | 6 ++-- 4 files changed, 13 insertions(+), 91 deletions(-) diff --git a/Helios/Pattern.c b/Helios/Pattern.c index 8dfd4a5d..2378b48c 100644 --- a/Helios/Pattern.c +++ b/Helios/Pattern.c @@ -16,13 +16,12 @@ static void pattern_begin_dash(pattern_t *pat); static void pattern_next_state(pattern_t *pat, uint8_t timing); static void pattern_blend_blink_on(pattern_t *pat); static void pattern_interpolate(uint8_t *current, const uint8_t next, uint8_t blend_speed); -static void pattern_tick_fade(pattern_t *pat); /* ================================== * Pattern Args Functions */ void pattern_args_init(pattern_args_t *args, uint8_t on, uint8_t off, uint8_t gap, - uint8_t dash, uint8_t group, uint8_t blend, uint8_t fade) + uint8_t dash, uint8_t group, uint8_t blend) { args->on_dur = on; args->off_dur = off; @@ -30,16 +29,15 @@ void pattern_args_init(pattern_args_t *args, uint8_t on, uint8_t off, uint8_t ga args->dash_dur = dash; args->group_size = group; args->blend_speed = blend; - args->fade_dur = fade; } /* ================================== * Pattern Functions */ void pattern_init(pattern_t *pat, uint8_t onDur, uint8_t offDur, uint8_t gap, - uint8_t dash, uint8_t group, uint8_t blend, uint8_t fade) + uint8_t dash, uint8_t group, uint8_t blend) { - pattern_args_init(&pat->m_args, onDur, offDur, gap, dash, group, blend, fade); + pattern_args_init(&pat->m_args, onDur, offDur, gap, dash, group, blend); pat->m_patternFlags = 0; colorset_init(&pat->m_colorset); pat->m_groupCounter = 0; @@ -47,23 +45,18 @@ void pattern_init(pattern_t *pat, uint8_t onDur, uint8_t offDur, uint8_t gap, timer_init_default(&pat->m_blinkTimer); rgb_init(&pat->m_cur); rgb_init(&pat->m_next); - pat->m_fadeValue = 0; - pat->m_fadeStartTime = 0; } void pattern_init_with_args(pattern_t *pat, const pattern_args_t *args) { pattern_init(pat, args->on_dur, args->off_dur, args->gap_dur, - args->dash_dur, args->group_size, args->blend_speed, args->fade_dur); + args->dash_dur, args->group_size, args->blend_speed); } void pattern_init_state(pattern_t *pat) { colorset_reset_index(&pat->m_colorset); - /* Reset the fade start time to the current time */ - pat->m_fadeStartTime = time_get_current_time(); - /* the default state to begin with */ pat->m_state = STATE_BLINK_ON; /* if a dash is present then always start with the dash because @@ -81,56 +74,11 @@ void pattern_init_state(pattern_t *pat) /* convert current/next colors to HSV but only if we are doing a blend */ pat->m_cur = colorset_get_next(&pat->m_colorset); pat->m_next = colorset_get_next(&pat->m_colorset); - } else if (pat->m_args.fade_dur) { - /* if there is a fade dur and no blend need to iterate colorset */ - colorset_get_next(&pat->m_colorset); - } - - /* Initialize the fluctuating fade value */ - pat->m_fadeValue = 0; -} - -static void pattern_tick_fade(pattern_t *pat) -{ - uint32_t now = time_get_current_time(); - /* Calculate relative time since pattern was initialized */ - uint32_t relativeTime = now - pat->m_fadeStartTime; - uint32_t duration = pat->m_args.fade_dur * 10; - - /* only tick forward every fade_dur ticks */ - if (!relativeTime || (relativeTime % duration) != 0) { - return; - } - - /* count the number of steps based on relative time */ - uint32_t steps = relativeTime / duration; - uint32_t range = pat->m_args.off_dur; - - /* make sure the range is non-zero */ - if (range == 0) { - pat->m_fadeValue = 0; - return; - } - - uint32_t double_range = range * 2; - uint32_t step = steps % double_range; - - /* Triangle wave: up from 0 to range, then down to 0 */ - pat->m_fadeValue = (step < range) ? step : (double_range - step - 1); - - /* iterate color when at lowest point */ - if (step == 0) { - colorset_get_next(&pat->m_colorset); } } void pattern_play(pattern_t *pat) { - /* tick forward the fade logic each tick */ - if (pattern_is_fade(pat)) { - pattern_tick_fade(pat); - } - /* 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: @@ -143,8 +91,7 @@ void pattern_play(pattern_t *pat) if (pat->m_args.on_dur > 0) { pattern_on_blink_on(pat); --pat->m_groupCounter; - /* When in ON state, use current fading on-time */ - pattern_next_state(pat, pat->m_args.on_dur + pat->m_fadeValue); + pattern_next_state(pat, pat->m_args.on_dur); return; } pat->m_state = STATE_BLINK_OFF; @@ -154,7 +101,7 @@ void pattern_play(pattern_t *pat) if (pat->m_groupCounter > 0 || (!pat->m_args.gap_dur && !pat->m_args.dash_dur)) { if (pat->m_args.off_dur > 0) { pattern_on_blink_off(pat); - pattern_next_state(pat, pat->m_args.off_dur - pat->m_fadeValue); + pattern_next_state(pat, pat->m_args.off_dur); return; } if (pat->m_groupCounter > 0 && pat->m_args.on_dur > 0) { @@ -237,13 +184,6 @@ static void pattern_on_blink_on(pattern_t *pat) return; } - /* Check if this is a fading duration pattern */ - if (pattern_is_fade(pat)) { - rgb_color_t cur_col = colorset_cur(&pat->m_colorset); - led_set_rgb(&cur_col); - return; - } - rgb_color_t next_col = colorset_get_next(&pat->m_colorset); led_set_rgb(&next_col); } @@ -342,11 +282,6 @@ uint8_t pattern_is_blend(const pattern_t *pat) return pat->m_args.blend_speed > 0; } -uint8_t pattern_is_fade(const pattern_t *pat) -{ - return pat->m_args.fade_dur > 0; -} - static void pattern_blend_blink_on(pattern_t *pat) { /* if we reached the next color, then cycle the colorset diff --git a/Helios/Pattern.h b/Helios/Pattern.h index 68b3c8fa..5399f504 100644 --- a/Helios/Pattern.h +++ b/Helios/Pattern.h @@ -22,12 +22,11 @@ struct pattern_args_t { uint8_t dash_dur; uint8_t group_size; uint8_t blend_speed; - uint8_t fade_dur; }; /* Initialize pattern args with all parameters */ void pattern_args_init(pattern_args_t *args, uint8_t on, uint8_t off, uint8_t gap, - uint8_t dash, uint8_t group, uint8_t blend, uint8_t fade); + uint8_t dash, uint8_t group, uint8_t blend); /* The various different blinking states the pattern can be in */ enum pattern_state @@ -86,20 +85,11 @@ struct pattern_t /* current color and target blend color */ rgb_color_t m_cur; rgb_color_t m_next; - - /* ================================== - * Fade Members */ - - /* shifting value to represent current fade */ - uint8_t m_fadeValue; - - /* Add a member variable to store when the pattern was last initialized */ - uint32_t m_fadeStartTime; }; /* try to not set on duration to 0 */ void pattern_init(pattern_t *pat, uint8_t onDur, uint8_t offDur, uint8_t gap, - uint8_t dash, uint8_t group, uint8_t blend, uint8_t fade); + uint8_t dash, uint8_t group, uint8_t blend); void pattern_init_with_args(pattern_t *pat, const pattern_args_t *args); /* init the pattern to initial state */ @@ -135,9 +125,6 @@ uint8_t pattern_has_flags(const pattern_t *pat, uint32_t flags); /* whether blend speed is non 0 */ uint8_t pattern_is_blend(const pattern_t *pat); -/* whether fade speed is non 0 */ -uint8_t pattern_is_fade(const pattern_t *pat); - #ifdef __cplusplus } #endif diff --git a/Helios/Patterns.c b/Helios/Patterns.c index 044d2000..7a7d6dfa 100644 --- a/Helios/Patterns.c +++ b/Helios/Patterns.c @@ -37,7 +37,7 @@ void patterns_make_default(uint8_t index, pattern_t *pat) return; } pattern_args_t args; - pattern_args_init(&args, 0, 0, 0, 0, 0, 0, 0); + pattern_args_init(&args, 0, 0, 0, 0, 0, 0); switch (index) { case 0: /* Lightside */ args.on_dur = 2; @@ -80,7 +80,7 @@ void patterns_make_default(uint8_t index, pattern_t *pat) void patterns_make_pattern(enum pattern_id id, pattern_t *pat) { pattern_args_t args; - pattern_args_init(&args, 0, 0, 0, 0, 0, 0, 0); + pattern_args_init(&args, 0, 0, 0, 0, 0, 0); switch (id) { default: diff --git a/HeliosCLI/cli_main.cpp b/HeliosCLI/cli_main.cpp index 22d98734..d57db80d 100644 --- a/HeliosCLI/cli_main.cpp +++ b/HeliosCLI/cli_main.cpp @@ -115,8 +115,8 @@ int main(int argc, char *argv[]) // parse the list of args into an array of ints std::vector vals; std::istringstream ss(initial_pattern_args_str); - // push 7 args into the array (on_dur, off_dur, gap_dur, dash_dur, group_size, blend_speed, fade_dur) - while (vals.size() < 7) { + // push 6 args into the array (on_dur, off_dur, gap_dur, dash_dur, group_size, blend_speed) + while (vals.size() < 6) { std::string arg; uint32_t val = 0; // try to parse out a number @@ -128,7 +128,7 @@ int main(int argc, char *argv[]) } // construct pattern args from the array of values pattern_args_t args; - pattern_args_init(&args, vals[0], vals[1], vals[2], vals[3], vals[4], vals[5], vals[6]); + pattern_args_init(&args, vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]); // set the args of the current pattern pattern_t* pat = helios_cur_pattern(); pattern_set_args(pat, &args); From 9ab0b0af6f3a59b9154f1ffee434a27fb3d64346 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 12:21:33 +0200 Subject: [PATCH 10/23] Update HeliosConfig.h to correct storage size calculations for patterns, adjusting values for slots and total bytes. --- Helios/HeliosConfig.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Helios/HeliosConfig.h b/Helios/HeliosConfig.h index b7c575df..454a0729 100644 --- a/Helios/HeliosConfig.h +++ b/Helios/HeliosConfig.h @@ -180,12 +180,10 @@ // Some math to calculate storage sizes: // 3 * 6 = 18 for the colorset -// 1 + 7 + 1 + 1 = 10 for the rest -// = 28 bytes total for a pattern including CRC -// -> 8 slots = 8 * 28 = 224 -// = 31 bytes left -// -> 9 slots = 9 * 28 = 252 -// = 3 bytes left +// 1 + 6 + 1 + 1 = 9 for the rest +// = 27 bytes total for a pattern including CRC +// -> 9 slots = 9 * 27 = 243 +// = 12 bytes left // forbidden constant: // #define HELIOS_ARDUINO 1 From 76f832aa744e117506228065cbb3d3ad6b9c047a Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 16:03:11 +0200 Subject: [PATCH 11/23] Refactor helios_handle_off_menu to replace break with return for improved state management in the off menu handling. --- Helios/Helios.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Helios/Helios.c b/Helios/Helios.c index f5062f82..c9d18db3 100644 --- a/Helios/Helios.c +++ b/Helios/Helios.c @@ -424,7 +424,7 @@ static void helios_handle_off_menu(uint8_t mag, uint8_t past) switch (mag) { case TIME_TILL_GLOW_LOCK_UNLOCK: // red lock g_cur_state = STATE_TOGGLE_LOCK; - break; + return; default: // just go back to sleep in hold-past off menu helios_enter_sleep(); From ca182416842fab61a3e4c27cb4bd5281248aafcd Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 16:57:08 +0200 Subject: [PATCH 12/23] Add new Helios components: Button, Colorset, Colortypes, Helios, Led, Pattern, Random, Storage, TimeControl, Timer, and CLI Makefile updates This commit introduces several new source files for the Helios project, implementing core functionalities such as button handling, color management, pattern definitions, and random number generation. Additionally, the Makefile for both CLI and Embedded versions has been updated to ensure proper compilation of the new C++ source files, enhancing the overall structure and maintainability of the project. --- Helios/{Button.c => Button.cpp} | 6 ++++++ Helios/{Colorset.c => Colorset.cpp} | 0 Helios/{Colortypes.c => Colortypes.cpp} | 0 Helios/{Helios.c => Helios.cpp} | 0 Helios/{Led.c => Led.cpp} | 0 Helios/{Pattern.c => Pattern.cpp} | 0 Helios/{Patterns.c => Patterns.cpp} | 0 Helios/Patterns.h | 8 ++++++++ Helios/{Random.c => Random.cpp} | 0 Helios/{Storage.c => Storage.cpp} | 0 Helios/{TimeControl.c => TimeControl.cpp} | 0 Helios/{Timer.c => Timer.cpp} | 0 HeliosCLI/Makefile | 22 ++++++---------------- HeliosEmbedded/Makefile | 11 +++++++---- 14 files changed, 27 insertions(+), 20 deletions(-) rename Helios/{Button.c => Button.cpp} (99%) rename Helios/{Colorset.c => Colorset.cpp} (100%) rename Helios/{Colortypes.c => Colortypes.cpp} (100%) rename Helios/{Helios.c => Helios.cpp} (100%) rename Helios/{Led.c => Led.cpp} (100%) rename Helios/{Pattern.c => Pattern.cpp} (100%) rename Helios/{Patterns.c => Patterns.cpp} (100%) rename Helios/{Random.c => Random.cpp} (100%) rename Helios/{Storage.c => Storage.cpp} (100%) rename Helios/{TimeControl.c => TimeControl.cpp} (100%) rename Helios/{Timer.c => Timer.cpp} (100%) diff --git a/Helios/Button.c b/Helios/Button.cpp similarity index 99% rename from Helios/Button.c rename to Helios/Button.cpp index 00b27245..38e0c040 100644 --- a/Helios/Button.c +++ b/Helios/Button.cpp @@ -13,8 +13,14 @@ #endif /* Forward declaration */ +#ifdef __cplusplus +extern "C" { +#endif void helios_wakeup(void); void helios_terminate(void); +#ifdef __cplusplus +} +#endif #ifdef HELIOS_CLI /* Forward declarations for CLI functions */ diff --git a/Helios/Colorset.c b/Helios/Colorset.cpp similarity index 100% rename from Helios/Colorset.c rename to Helios/Colorset.cpp diff --git a/Helios/Colortypes.c b/Helios/Colortypes.cpp similarity index 100% rename from Helios/Colortypes.c rename to Helios/Colortypes.cpp diff --git a/Helios/Helios.c b/Helios/Helios.cpp similarity index 100% rename from Helios/Helios.c rename to Helios/Helios.cpp diff --git a/Helios/Led.c b/Helios/Led.cpp similarity index 100% rename from Helios/Led.c rename to Helios/Led.cpp diff --git a/Helios/Pattern.c b/Helios/Pattern.cpp similarity index 100% rename from Helios/Pattern.c rename to Helios/Pattern.cpp diff --git a/Helios/Patterns.c b/Helios/Patterns.cpp similarity index 100% rename from Helios/Patterns.c rename to Helios/Patterns.cpp diff --git a/Helios/Patterns.h b/Helios/Patterns.h index bc2d87f0..b8e6a1a6 100644 --- a/Helios/Patterns.h +++ b/Helios/Patterns.h @@ -1,6 +1,10 @@ #ifndef PATTERNS_H #define PATTERNS_H +#ifdef __cplusplus +extern "C" { +#endif + #include /* Forward declaration */ @@ -54,4 +58,8 @@ enum pattern_id { void patterns_make_default(uint8_t index, pattern_t *pat); void patterns_make_pattern(enum pattern_id id, pattern_t *pat); +#ifdef __cplusplus +} +#endif + #endif diff --git a/Helios/Random.c b/Helios/Random.cpp similarity index 100% rename from Helios/Random.c rename to Helios/Random.cpp diff --git a/Helios/Storage.c b/Helios/Storage.cpp similarity index 100% rename from Helios/Storage.c rename to Helios/Storage.cpp diff --git a/Helios/TimeControl.c b/Helios/TimeControl.cpp similarity index 100% rename from Helios/TimeControl.c rename to Helios/TimeControl.cpp diff --git a/Helios/Timer.c b/Helios/Timer.cpp similarity index 100% rename from Helios/Timer.c rename to Helios/Timer.cpp diff --git a/HeliosCLI/Makefile b/HeliosCLI/Makefile index c9178d1d..7210e298 100644 --- a/HeliosCLI/Makefile +++ b/HeliosCLI/Makefile @@ -56,24 +56,18 @@ LIBS=\ # source files # local source files first, other sources after ifeq ($(OS),Windows_NT) - C_SRC = $(shell find ../Helios -type f -name \\*.c) - CPP_SRC = $(shell find . -type f -name \\*.cpp -not -path "./venv/*") + CPP_SRC = $(shell find ../Helios -type f -name \\*.cpp) $(shell find . -type f -name \\*.cpp -not -path "./venv/*") else - C_SRC = $(shell find ../Helios -type f -name '*.c') - CPP_SRC = $(shell find . -type f -name '*.cpp' -not -path "./venv/*") + CPP_SRC = $(shell find ../Helios -type f -name '*.cpp') $(shell find . -type f -name '*.cpp' -not -path "./venv/*") endif -SRC = $(C_SRC) $(CPP_SRC) +SRC = $(CPP_SRC) -# object files are source files with .c/.cpp replaced with .o -C_OBJS = $(C_SRC:.c=.o) -CPP_OBJS = $(CPP_SRC:.cpp=.o) -OBJS = $(C_OBJS) $(CPP_OBJS) +# object files are source files with .cpp replaced with .o +OBJS = $(CPP_SRC:.cpp=.o) # dependency files -C_DFILES = $(C_SRC:.c=.d) -CPP_DFILES = $(CPP_SRC:.cpp=.d) -DFILES = $(C_DFILES) $(CPP_DFILES) +DFILES = $(CPP_SRC:.cpp=.d) # target dependencies # this includes any script generated c/h files, @@ -103,10 +97,6 @@ helios: compute_version $(DEPS) %.o: %.cpp $(CXX) $(CXXFLAGS) -MMD -c $< -o $@ -# catch-all make target to generate .o and .d files from .c -%.o: %.c - $(CC) $(CFLAGS) -MMD -c $< -o $@ - # catch-all for static libraries in the form of: # / # this expects that the makefile in has a diff --git a/HeliosEmbedded/Makefile b/HeliosEmbedded/Makefile index c89c5586..c7629808 100644 --- a/HeliosEmbedded/Makefile +++ b/HeliosEmbedded/Makefile @@ -160,15 +160,15 @@ CFLAGS+=$(INCLUDES) # Source files ifeq ($(OS),Windows_NT) # Windows SRCS = \ - $(shell find ../Helios -maxdepth 1 -type f -name '\*.c') main.c + $(shell find ../Helios -maxdepth 1 -type f -name '\*.cpp') main.c else # linux SRCS = \ - $(shell find ../Helios -maxdepth 1 -type f -name \*.c) main.c + $(shell find ../Helios -maxdepth 1 -type f -name \*.cpp) main.c endif -OBJS = $(SRCS:.c=.o) +OBJS = $(SRCS:.cpp=.o) $(filter %.c,$(SRCS):.c=.o) -DFILES = $(SRCS:.c=.d) +DFILES = $(SRCS:.cpp=.d) $(filter %.c,$(SRCS):.c=.d) ####################### ### BUILD TARGETS ##### @@ -196,6 +196,9 @@ $(TARGET).elf: compute_version $(OBJS) %.o: %.S $(CC) $(ASMFLAGS) -c $< -o $@ +%.o: %.cpp + $(CC) $(CFLAGS) -c $< -o $@ + %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ From e000bf03f954ea6f5c5e8c898d979ba040613610 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 17:01:59 +0200 Subject: [PATCH 13/23] Refactor Makefile for HeliosCLI and HeliosEmbedded: update compiler settings, remove main.c, and enhance version tagging process. The changes include switching from gcc to g++ for CFLAGS, consolidating source file handling, and renaming the release target for consistency. --- HeliosCLI/Makefile | 34 +++++++++--------- HeliosEmbedded/Makefile | 55 ++++++++++++++--------------- HeliosEmbedded/{main.c => main.cpp} | 0 3 files changed, 42 insertions(+), 47 deletions(-) rename HeliosEmbedded/{main.c => main.cpp} (100%) diff --git a/HeliosCLI/Makefile b/HeliosCLI/Makefile index 7210e298..9f5b6a82 100644 --- a/HeliosCLI/Makefile +++ b/HeliosCLI/Makefile @@ -5,16 +5,14 @@ .PHONY: all tests clean pngs bmps clean_storage # compiler tool definitions -CXX=g++ -CC=gcc +CC=g++ AR=ar cru MAKE=make RM=rm -rf RANLIB=ranlib -CXXFLAGS=-O2 -g -Wall -std=c++11 -CFLAGS=-O2 -g -Wall -std=c11 +CFLAGS=-O2 -g -Wall -std=c++11 # compiler defines DEFINES=\ @@ -31,11 +29,9 @@ INCLUDES=\ # only set them if they're not empty to prevent unnecessary whitespace ifneq ($(DEFINES),) CFLAGS+=$(DEFINES) - CXXFLAGS+=$(DEFINES) endif ifneq ($(INCLUDES),) CFLAGS+=$(INCLUDES) - CXXFLAGS+=$(INCLUDES) endif # local NONSTANDARD libraries to link with @@ -56,18 +52,20 @@ LIBS=\ # source files # local source files first, other sources after ifeq ($(OS),Windows_NT) - CPP_SRC = $(shell find ../Helios -type f -name \\*.cpp) $(shell find . -type f -name \\*.cpp -not -path "./venv/*") + SRC = $(shell find ../Helios -type f -name \\*.cpp) \ + $(shell find . -type f -name \\*.cpp) else - CPP_SRC = $(shell find ../Helios -type f -name '*.cpp') $(shell find . -type f -name '*.cpp' -not -path "./venv/*") + SRC = $(shell find ../Helios -type f -name '*.cpp') \ + $(shell find . -type f -name '*.cpp') endif -SRC = $(CPP_SRC) +# object files are source files with .c replaced with .o +OBJS=\ + $(SRC:.cpp=.o) \ -# object files are source files with .cpp replaced with .o -OBJS = $(CPP_SRC:.cpp=.o) - -# dependency files -DFILES = $(CPP_SRC:.cpp=.d) +# dependency files are source files with .c replaced with .d +DFILES=\ + $(SRC:.cpp=.d) \ # target dependencies # this includes any script generated c/h files, @@ -91,11 +89,11 @@ tests: $(TESTS) # target for vortex lib helios: compute_version $(DEPS) - $(CXX) $(CXXFLAGS) $(DEPS) -o $@ $(LLIBS) + $(CC) $(CFLAGS) $(DEPS) -o $@ $(LLIBS) -# catch-all make target to generate .o and .d files from .cpp +# catch-all make target to generate .o and .d files %.o: %.cpp - $(CXX) $(CXXFLAGS) -MMD -c $< -o $@ + $(CC) $(CFLAGS) -MMD -c $< -o $@ # catch-all for static libraries in the form of: # / @@ -112,7 +110,7 @@ clean: @$(RM) $(DFILES) $(OBJS) $(TARGETS) $(TESTS) compute_version: - $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list | sort -V | tail -n1)) + $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$$' | sort -V | tail -n1)) $(eval HELIOS_VERSION_MAJOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f1)) $(eval HELIOS_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f2)) $(eval LAST_HELIOS_BUILD_NUMBER ?= $(shell echo $(LATEST_TAG) | cut -d. -f3)) diff --git a/HeliosEmbedded/Makefile b/HeliosEmbedded/Makefile index c7629808..e8e39594 100644 --- a/HeliosEmbedded/Makefile +++ b/HeliosEmbedded/Makefile @@ -2,7 +2,7 @@ ### CONFIGURATION ### ##################### -.PHONY: all upload set_fuses set_default_fuses set_16mhz_fuses set_8mhz_fuses set_1mhz_fuses get_fuses extract_hex upload_hex extract_eeprom upload_eeprom clean compute_version extract_version aeos_release +.PHONY: all upload set_fuses set_default_fuses set_16mhz_fuses set_8mhz_fuses set_1mhz_fuses get_fuses extract_hex upload_hex extract_eeprom upload_eeprom clean compute_version extract_version helios_release ifneq ($(OS),Windows_NT) OS = $(shell uname -s) @@ -160,15 +160,15 @@ CFLAGS+=$(INCLUDES) # Source files ifeq ($(OS),Windows_NT) # Windows SRCS = \ - $(shell find ../Helios -maxdepth 1 -type f -name '\*.cpp') main.c + $(shell find ../Helios -maxdepth 1 -type f -name '\*.cpp') main.cpp else # linux SRCS = \ - $(shell find ../Helios -maxdepth 1 -type f -name \*.cpp) main.c + $(shell find ../Helios -maxdepth 1 -type f -name \*.cpp) main.cpp endif -OBJS = $(SRCS:.cpp=.o) $(filter %.c,$(SRCS):.c=.o) +OBJS = $(SRCS:.cpp=.o) -DFILES = $(SRCS:.cpp=.d) $(filter %.c,$(SRCS):.c=.d) +DFILES = $(SRCS:.cpp=.d) ####################### ### BUILD TARGETS ##### @@ -199,9 +199,6 @@ $(TARGET).elf: compute_version $(OBJS) %.o: %.cpp $(CC) $(CFLAGS) -c $< -o $@ -%.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ - upload: set_fuses $(TARGET).hex $(AVRDUDE) $(AVRDUDE_FLAGS) -Uflash:w:$(TARGET).hex:i @@ -209,32 +206,32 @@ upload: set_fuses $(TARGET).hex ### GITHUB RELEASE #### ####################### -aeos_release: +helios_release: @if [ -z "$(VERSION)" ]; then \ echo "Error: VERSION parameter is required"; \ - echo "Usage: make aeos_release VERSION=1.2.3"; \ + echo "Usage: make helios_release VERSION=1.2.3"; \ exit 1; \ fi - @echo "Creating and pushing aeos-$(VERSION) tag..." - @if git rev-parse "aeos-$(VERSION)" >/dev/null 2>&1; then \ - echo "Warning: Tag aeos-$(VERSION) already exists locally"; \ + @echo "Creating and pushing $(VERSION) tag..." + @if git rev-parse "$(VERSION)" >/dev/null 2>&1; then \ + echo "Warning: Tag $(VERSION) already exists locally"; \ echo "Checking if it exists on remote..."; \ - if git ls-remote --tags origin | grep -q "aeos-$(VERSION)"; then \ - echo "Tag aeos-$(VERSION) already exists on remote. Skipping tag creation."; \ + if git ls-remote --tags origin | grep -q "refs/tags/$(VERSION)$$"; then \ + echo "Tag $(VERSION) already exists on remote. Skipping tag creation."; \ else \ echo "Pushing existing local tag to remote..."; \ - git push origin aeos-$(VERSION); \ + git push origin $(VERSION); \ fi \ else \ - echo "Creating new tag aeos-$(VERSION)..."; \ - git tag aeos-$(VERSION); \ - git push origin aeos-$(VERSION); \ - echo "✓ Tag aeos-$(VERSION) created and pushed"; \ + echo "Creating new tag $(VERSION)..."; \ + git tag $(VERSION); \ + git push origin $(VERSION); \ + echo "✓ Tag $(VERSION) created and pushed"; \ fi - @echo "Triggering Aeos Release workflow..." - gh workflow run "Aeos Release" --ref aeos - @echo "✓ Aeos Release workflow triggered successfully!" - @echo "Monitor progress with: gh run list --workflow=\"aeos_release.yml\" --limit=1" + @echo "Triggering Helios Release workflow..." + gh workflow run "Helios Release" --ref master + @echo "✓ Helios Release workflow triggered successfully!" + @echo "Monitor progress with: gh run list --workflow=\"release.yml\" --limit=1" ####################### ### LINUX SETUP ####### @@ -324,10 +321,10 @@ upload_eeprom_fingers: eeprom_fingers.eep ################### upload_hex: - $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:aeos.hex:i + $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:helios_firmware.hex:i -extract_hex: aeos.hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:r:aeos.hex:i +extract_hex: helios_firmware.hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:r:helios_firmware.hex:i ##################### ####### CLEAN ####### @@ -337,7 +334,7 @@ clean: rm -f $(OBJS) $(TARGET).elf $(TARGET).hex $(DFILES) $(TARGET).bin $(TARGET).eep $(TARGET).lst $(TARGET).map compute_version: - $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list | sort -V | tail -n1)) + $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$$' | sort -V | tail -n1)) $(eval HELIOS_VERSION_MAJOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f1)) $(eval HELIOS_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f2)) $(eval LAST_HELIOS_BUILD_NUMBER ?= $(shell echo $(LATEST_TAG) | cut -d. -f3)) @@ -356,4 +353,4 @@ extract_version: @rm $(TEMP_HEX) # include dependency files to ensure partial rebuilds work correctly --include $(DFILES) +-include $(DFILES) \ No newline at end of file diff --git a/HeliosEmbedded/main.c b/HeliosEmbedded/main.cpp similarity index 100% rename from HeliosEmbedded/main.c rename to HeliosEmbedded/main.cpp From 6fc0d17cd49d755040cd7e5781169ab4d6564df1 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 17:05:17 +0200 Subject: [PATCH 14/23] Remove C11 standard flag from Makefile for HeliosEmbedded to streamline compiler settings. --- HeliosEmbedded/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/HeliosEmbedded/Makefile b/HeliosEmbedded/Makefile index e8e39594..3c94474b 100644 --- a/HeliosEmbedded/Makefile +++ b/HeliosEmbedded/Makefile @@ -117,7 +117,6 @@ CFLAGS = -g \ -Wall \ -flto \ -mrelax \ - -std=c11 \ -fshort-enums \ -fpack-struct \ -fdata-sections \ From 7f8ccf0a58657af8d35114710df60f0a0303feae Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 18:07:07 +0200 Subject: [PATCH 15/23] Update .gitignore to include additional file patterns for tests and configuration files, while removing the exclusion for C source files. This enhances the project's build process by ensuring relevant files are tracked and managed correctly. --- .gitignore | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 3dea8476..80409b73 100644 --- a/.gitignore +++ b/.gitignore @@ -9,12 +9,11 @@ !*.pattern !*.h !*.cpp -!*.c !*.py !*.atsln !*.componentinfo.xml !*.cppproj - +!*.github/workflows/** # Ignore object files, dependency files, and other build artifacts *.o *.d @@ -38,3 +37,7 @@ !**/Makefile + +!*.test + +!*.conf From 27ad9d7288551bdf7a1b13e278c61b06d0c432dc Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 18:52:11 +0200 Subject: [PATCH 16/23] Update Makefile for HeliosCLI to exclude virtual environment files from source file search, improving build accuracy and preventing unnecessary file inclusion. --- HeliosCLI/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HeliosCLI/Makefile b/HeliosCLI/Makefile index 9f5b6a82..2b4a68f7 100644 --- a/HeliosCLI/Makefile +++ b/HeliosCLI/Makefile @@ -53,10 +53,10 @@ LIBS=\ # local source files first, other sources after ifeq ($(OS),Windows_NT) SRC = $(shell find ../Helios -type f -name \\*.cpp) \ - $(shell find . -type f -name \\*.cpp) + $(shell find . -type f -name \\*.cpp -not -path "./venv/*") else SRC = $(shell find ../Helios -type f -name '*.cpp') \ - $(shell find . -type f -name '*.cpp') + $(shell find . -type f -name '*.cpp' -not -path "./venv/*") endif # object files are source files with .c replaced with .o From 5a575d83ca98237c9a4351c3a498eb4d0fae9c07 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Fri, 17 Oct 2025 19:01:01 +0200 Subject: [PATCH 17/23] Remove FLAG_AUTOPLAY from Helios state machine and update related documentation and EEPROM dump output to reflect the change. --- CLAUDE.md | 1 - Helios/Helios.h | 2 -- HeliosCLI/cli_main.cpp | 3 ++- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3b603ad4..23049577 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -112,7 +112,6 @@ Helios operates as a state machine defined in `Helios/Helios.h`: Defined in `Helios/Helios.h`: - `FLAG_LOCKED` - Device is locked, must unlock to turn on - `FLAG_CONJURE` - Conjure mode enabled (single click toggles on/off) -- `FLAG_AUTOPLAY` - Timer mode, automatically cycles modes ### Key Components diff --git a/Helios/Helios.h b/Helios/Helios.h index 14c95b04..9d1b839b 100644 --- a/Helios/Helios.h +++ b/Helios/Helios.h @@ -62,8 +62,6 @@ enum helios_flags { FLAG_LOCKED = (1 << 0), /* Conjure mode is enabled, one click will toggle power */ FLAG_CONJURE = (1 << 1), - /* Autoplay is enabled, modes will automatically cycle */ - FLAG_AUTOPLAY = (1 << 2), /* Add new flags here, max 8 flags */ /* ============================================== */ diff --git a/HeliosCLI/cli_main.cpp b/HeliosCLI/cli_main.cpp index d57db80d..e42211ee 100644 --- a/HeliosCLI/cli_main.cpp +++ b/HeliosCLI/cli_main.cpp @@ -756,11 +756,12 @@ static void dump_eeprom(const std::string& filename) uint8_t flags = (uint8_t)memory[CONFIG_START_INDEX - STORAGE_GLOBAL_FLAG_INDEX]; bool locked = (flags & FLAG_LOCKED) != 0; + bool conjure = (flags & FLAG_CONJURE) != 0; uint8_t modeIdx = (uint8_t)memory[CONFIG_START_INDEX - STORAGE_CURRENT_MODE_INDEX]; uint8_t brightness = (uint8_t)memory[CONFIG_START_INDEX - STORAGE_BRIGHTNESS_INDEX]; printf("Brightness: %u\n", brightness); printf("Mode Index: %u\n", modeIdx); - printf("Flags: 0x%02X (locked=%u)\n", flags, locked); + printf("Flags: 0x%02X (locked=%u, conjure=%u)\n", flags, locked, conjure); } From 8cb7a6b5ee42adc20a8074714584bf42b0f8e260 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Sat, 18 Oct 2025 10:38:03 +0200 Subject: [PATCH 18/23] Revert comment style changes back to C++ style comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- Helios/Button.cpp | 28 +-- Helios/Button.h | 34 ++-- Helios/Colorset.cpp | 34 ++-- Helios/Colorset.h | 76 ++++---- Helios/Colortypes.cpp | 82 ++++----- Helios/Colortypes.h | 14 +- Helios/Helios.cpp | 10 +- Helios/Helios.h | 16 +- Helios/HeliosConfig.h | 386 ++++++++++++++++++++--------------------- Helios/Led.cpp | 38 ++-- Helios/Led.h | 16 +- Helios/Pattern.cpp | 24 +-- Helios/Pattern.h | 50 +++--- Helios/Patterns.cpp | 8 +- Helios/Patterns.h | 16 +- Helios/Random.cpp | 2 +- Helios/Random.h | 10 +- Helios/Storage.cpp | 56 +++--- Helios/Storage.h | 6 +- Helios/TimeControl.cpp | 40 ++--- Helios/TimeControl.h | 10 +- Helios/Timer.cpp | 12 +- Helios/Timer.h | 12 +- 23 files changed, 490 insertions(+), 490 deletions(-) diff --git a/Helios/Button.cpp b/Helios/Button.cpp index 38e0c040..06ef0171 100644 --- a/Helios/Button.cpp +++ b/Helios/Button.cpp @@ -12,7 +12,7 @@ #define BUTTON_PORT 2 #endif -/* Forward declaration */ +// Forward declaration #ifdef __cplusplus extern "C" { #endif @@ -23,12 +23,12 @@ void helios_terminate(void); #endif #ifdef HELIOS_CLI -/* Forward declarations for CLI functions */ +// Forward declarations for CLI functions static uint8_t button_process_pre_input(void); static uint8_t button_process_post_input(void); #endif -/* static members of Button */ +// static members of Button static uint32_t m_pressTime = 0; static uint32_t m_releaseTime = 0; static uint32_t m_holdDuration = 0; @@ -47,8 +47,8 @@ static uint8_t m_holdClick = 0; * The CLI input queue functionality is omitted for embedded targets. */ static uint8_t m_pinState = 0; static uint8_t m_enableWake = 0; -/* Simple input queue for CLI - using a fixed-size circular buffer */ -/* Larger queue size for CLI to handle long test sequences */ +// Simple input queue for CLI - using a fixed-size circular buffer +// Larger queue size for CLI to handle long test sequences #define INPUT_QUEUE_SIZE 8192 static char m_inputQueue[INPUT_QUEUE_SIZE]; static uint32_t m_queueHead = 0; @@ -79,7 +79,7 @@ uint8_t button_init(void) #ifdef HELIOS_ARDUINO pinMode(3, INPUT); #else - /* turn off wake */ + // turn off wake PCMSK &= ~(1 << PCINT3); GIMSK &= ~(1 << PCIE); #endif @@ -90,7 +90,7 @@ uint8_t button_init(void) void button_enable_wake(void) { #ifdef HELIOS_EMBEDDED - /* Configure INT0 to trigger on falling edge */ + // Configure INT0 to trigger on falling edge PCMSK |= (1 << PCINT3); GIMSK |= (1 << PCIE); sei(); @@ -136,7 +136,7 @@ uint8_t button_hold_pressing(void) void button_update(void) { #ifdef HELIOS_CLI - /* process any pre-input events in the queue */ + // process any pre-input events in the queue uint8_t processed_pre = button_process_pre_input(); #endif @@ -255,14 +255,14 @@ static uint8_t button_process_pre_input(void) helios_terminate(); break; case 'w': /* wait */ - /* wait is pre input I guess */ + // wait is pre input I guess break; default: /* return here! do not pop the queue * do not process post input events */ return 0; } - /* now pop whatever pre-input command was processed */ + // now pop whatever pre-input command was processed m_queueHead = (m_queueHead + 1) % INPUT_QUEUE_SIZE; return 1; } @@ -270,10 +270,10 @@ static uint8_t button_process_pre_input(void) static uint8_t button_process_post_input(void) { if (m_queueHead == m_queueTail) { - /* probably processed the pre-input event already */ + // probably processed the pre-input event already return 0; } - /* process input queue from the command line */ + // process input queue from the command line char command = m_inputQueue[m_queueHead]; switch (command) { case 'c': /* click button */ @@ -283,7 +283,7 @@ static uint8_t button_process_post_input(void) button_do_long_click(); break; default: - /* should never happen */ + // should never happen return 0; } m_queueHead = (m_queueHead + 1) % INPUT_QUEUE_SIZE; @@ -334,7 +334,7 @@ void button_do_toggle(void) m_pinState = !m_pinState; } -/* queue up an input event for the button */ +// queue up an input event for the button void button_queue_input(char input) { uint32_t nextTail = (m_queueTail + 1) % INPUT_QUEUE_SIZE; diff --git a/Helios/Button.h b/Helios/Button.h index 28bc2f7a..1cd534a4 100644 --- a/Helios/Button.h +++ b/Helios/Button.h @@ -7,52 +7,52 @@ extern "C" { #include -/* Initialize button */ +// Initialize button uint8_t button_init(void); -/* Directly poll the pin for whether it's pressed right now */ +// Directly poll the pin for whether it's pressed right now uint8_t button_check(void); -/* Poll the button pin and update the state of the button object */ +// Poll the button pin and update the state of the button object void button_update(void); -/* Whether the button was pressed this tick */ +// Whether the button was pressed this tick uint8_t button_on_press(void); -/* Whether the button was released this tick */ +// Whether the button was released this tick uint8_t button_on_release(void); -/* Whether the button is currently pressed */ +// Whether the button is currently pressed uint8_t button_is_pressed(void); -/* Whether the button was shortclicked this tick */ +// Whether the button was shortclicked this tick uint8_t button_on_short_click(void); -/* Whether the button was long clicked this tick */ +// Whether the button was long clicked this tick uint8_t button_on_long_click(void); -/* Whether the button was hold clicked this tick */ +// Whether the button was hold clicked this tick uint8_t button_on_hold_click(void); -/* Detect if the button is being held past long click */ +// Detect if the button is being held past long click uint8_t button_hold_pressing(void); -/* When the button was last pressed */ +// When the button was last pressed uint32_t button_press_time(void); -/* When the button was last released */ +// When the button was last released uint32_t button_release_time(void); -/* How long the button is currently or was last held down (in ticks) */ +// How long the button is currently or was last held down (in ticks) uint32_t button_hold_duration(void); -/* How long the button is currently or was last released for (in ticks) */ +// How long the button is currently or was last released for (in ticks) uint32_t button_release_duration(void); -/* The number of releases */ +// The number of releases uint8_t button_release_count(void); -/* Enable wake on press */ +// Enable wake on press void button_enable_wake(void); #ifdef HELIOS_CLI @@ -73,7 +73,7 @@ void button_do_press(void); void button_do_release(void); void button_do_toggle(void); -/* Queue up an input event for the button */ +// Queue up an input event for the button void button_queue_input(char input); uint32_t button_input_queue_size(void); #endif diff --git a/Helios/Colorset.cpp b/Helios/Colorset.cpp index e3779af9..758e7195 100644 --- a/Helios/Colorset.cpp +++ b/Helios/Colorset.cpp @@ -20,7 +20,7 @@ void colorset_init_multi(colorset_t *set, rgb_color_t c1, rgb_color_t c2, rgb_co rgb_color_t c4, rgb_color_t c5, rgb_color_t c6, rgb_color_t c7, rgb_color_t c8) { colorset_init(set); - /* would be nice if we could do this another way */ + // would be nice if we could do this another way if (!rgb_empty(&c1)) colorset_add_color(set, c1); if (!rgb_empty(&c2)) colorset_add_color(set, c2); if (!rgb_empty(&c3)) colorset_add_color(set, c3); @@ -54,7 +54,7 @@ void colorset_copy(colorset_t *dest, const colorset_t *src) uint8_t colorset_equals(const colorset_t *a, const colorset_t *b) { - /* only compare the palettes for equality */ + // only compare the palettes for equality return (a->m_numColors == b->m_numColors) && (memcmp(a->m_palette, b->m_palette, a->m_numColors * sizeof(rgb_color_t)) == 0); } @@ -86,7 +86,7 @@ uint8_t colorset_add_color(colorset_t *set, rgb_color_t col) if (set->m_numColors >= NUM_COLOR_SLOTS) { return 0; } - /* insert new color and increment number of colors */ + // insert new color and increment number of colors set->m_palette[set->m_numColors] = col; set->m_numColors++; return 1; @@ -159,7 +159,7 @@ void colorset_remove_color(colorset_t *set, uint8_t index) void colorset_randomize_colors(colorset_t *set, random_t *ctx, uint8_t numColors, enum colorset_color_mode mode) { - /* if they specify randomly pick the color mode then roll it */ + // if they specify randomly pick the color mode then roll it if (mode >= COLOR_MODE_RANDOMLY_PICK) { mode = (enum colorset_color_mode)(random_next8(ctx, 0, 255) % COLOR_MODE_COUNT); } @@ -173,7 +173,7 @@ void colorset_randomize_colors(colorset_t *set, random_t *ctx, uint8_t numColors colorGap = random_next8(ctx, 16, 256 / (numColors - 1)); } enum colorset_value_style valStyle = (enum colorset_value_style)random_next8(ctx, 0, VAL_STYLE_COUNT); - /* the doubleStyle decides if some colors are added to the set twice */ + // the doubleStyle decides if some colors are added to the set twice uint8_t doubleStyle = 0; if (numColors <= 7) { doubleStyle = random_next8(ctx, 0, 1); @@ -194,7 +194,7 @@ void colorset_randomize_colors(colorset_t *set, random_t *ctx, uint8_t numColors hueToUse = (randomizedHue + (256 / numColors) * i); } colorset_add_color_with_value_style(set, ctx, hueToUse, valueToUse, valStyle, numColors, i); - /* double all colors or only first color */ + // double all colors or only first color if (doubleStyle == 2 || (doubleStyle == 1 && !i)) { colorset_add_color_with_value_style(set, ctx, hueToUse, valueToUse, valStyle, numColors, i); } @@ -225,7 +225,7 @@ void colorset_set(colorset_t *set, uint8_t index, rgb_color_t col) * ie adding a new color when you set an index higher than the max */ if (index >= set->m_numColors) { if (!colorset_add_color(set, col)) { - /* ERROR_LOGF("Failed to add new color at index %u", index); */ + // ERROR_LOGF("Failed to add new color at index %u", index); } return; } @@ -237,12 +237,12 @@ void colorset_skip(colorset_t *set, int32_t amount) if (!set->m_numColors) { return; } - /* if the colorset hasn't started yet */ + // if the colorset hasn't started yet if (set->m_curIndex == INDEX_INVALID) { set->m_curIndex = 0; } - /* first modulate the amount to skip to be within +/- the number of colors */ + // first modulate the amount to skip to be within +/- the number of colors amount %= (int32_t)set->m_numColors; /* max = 3 @@ -250,7 +250,7 @@ void colorset_skip(colorset_t *set, int32_t amount) * amount = -10 */ set->m_curIndex = ((int32_t)set->m_curIndex + (int32_t)amount) % (int32_t)set->m_numColors; if (set->m_curIndex > set->m_numColors) { /* must have wrapped */ - /* simply wrap it back */ + // simply wrap it back set->m_curIndex += set->m_numColors; } } @@ -293,13 +293,13 @@ rgb_color_t colorset_get_prev(colorset_t *set) rgb_init_from_raw(&result, RGB_OFF); return result; } - /* handle wrapping at 0 */ + // handle wrapping at 0 if (set->m_curIndex == 0 || set->m_curIndex == INDEX_INVALID) { set->m_curIndex = colorset_num_colors(set) - 1; } else { set->m_curIndex--; } - /* return the color */ + // return the color return set->m_palette[set->m_curIndex]; } @@ -310,11 +310,11 @@ rgb_color_t colorset_get_next(colorset_t *set) rgb_init_from_raw(&result, RGB_OFF); return result; } - /* iterate current index, let it wrap at max uint8 */ + // iterate current index, let it wrap at max uint8 set->m_curIndex++; - /* then modulate the result within max colors */ + // then modulate the result within max colors set->m_curIndex %= colorset_num_colors(set); - /* return the color */ + // return the color return set->m_palette[set->m_curIndex]; } @@ -326,7 +326,7 @@ rgb_color_t colorset_peek(const colorset_t *set, int32_t offset) return result; } uint8_t nextIndex = 0; - /* get index of the next color */ + // get index of the next color if (offset >= 0) { nextIndex = (set->m_curIndex + offset) % colorset_num_colors(set); } else { @@ -336,7 +336,7 @@ rgb_color_t colorset_peek(const colorset_t *set, int32_t offset) } nextIndex = ((set->m_curIndex + colorset_num_colors(set)) + (int)offset) % colorset_num_colors(set); } - /* return the color */ + // return the color return set->m_palette[nextIndex]; } diff --git a/Helios/Colorset.h b/Helios/Colorset.h index 1e194158..eccc28a3 100644 --- a/Helios/Colorset.h +++ b/Helios/Colorset.h @@ -9,131 +9,131 @@ extern "C" { #include "HeliosConfig.h" -/* Forward declaration */ +// Forward declaration typedef struct random_t random_t; typedef struct colorset_t colorset_t; enum colorset_value_style { - /* Random values */ + // Random values VAL_STYLE_RANDOM = 0, - /* First color low value, the rest are random */ + // First color low value, the rest are random VAL_STYLE_LOW_FIRST_COLOR, - /* First color high value, the rest are low */ + // First color high value, the rest are low VAL_STYLE_HIGH_FIRST_COLOR, - /* Alternate between high and low value */ + // Alternate between high and low value VAL_STYLE_ALTERNATING, - /* Ascending values from low to high */ + // Ascending values from low to high VAL_STYLE_ASCENDING, - /* Descending values from high to low */ + // Descending values from high to low VAL_STYLE_DESCENDING, - /* Constant value */ + // Constant value VAL_STYLE_CONSTANT, - /* Total number of value styles */ + // Total number of value styles VAL_STYLE_COUNT }; enum colorset_color_mode { - /* randomize with color theory */ + // randomize with color theory COLOR_MODE_COLOR_THEORY, - /* randomize a monochromatic set */ + // randomize a monochromatic set COLOR_MODE_MONOCHROMATIC, - /* randomize an evenly spaced hue set */ + // randomize an evenly spaced hue set COLOR_MODE_EVENLY_SPACED, - /* total different randomize modes above */ + // total different randomize modes above COLOR_MODE_COUNT, - /* EXTRA OPTION: randomly pick one of the other 3 options */ + // EXTRA OPTION: randomly pick one of the other 3 options COLOR_MODE_RANDOMLY_PICK = COLOR_MODE_COUNT, }; struct colorset_t { - /* palette of colors */ + // palette of colors rgb_color_t m_palette[NUM_COLOR_SLOTS]; - /* the actual number of colors in the set */ + // the actual number of colors in the set uint8_t m_numColors; /* the current index, starts at 255 so that * the very first call to colorset_getNext will iterate to 0 */ uint8_t m_curIndex; }; -/* Empty colorset */ +// Empty colorset void colorset_init(colorset_t *set); -/* Initialize with up to 8 colors */ +// Initialize with up to 8 colors void colorset_init_multi(colorset_t *set, rgb_color_t c1, rgb_color_t c2, rgb_color_t c3, rgb_color_t c4, rgb_color_t c5, rgb_color_t c6, rgb_color_t c7, rgb_color_t c8); -/* Initialize from array of colors */ +// Initialize from array of colors void colorset_init_array(colorset_t *set, uint8_t numCols, const uint32_t *cols); -/* Copy colorset */ +// Copy colorset void colorset_copy(colorset_t *dest, const colorset_t *src); -/* Equality operators */ +// Equality operators uint8_t colorset_equals(const colorset_t *a, const colorset_t *b); -/* Clear the colorset */ +// Clear the colorset void colorset_clear(colorset_t *set); -/* CRC the colorset */ +// CRC the colorset uint32_t colorset_crc32(const colorset_t *set); -/* Index operator to access color index */ +// Index operator to access color index rgb_color_t colorset_get_at_index(const colorset_t *set, int index); -/* Add a single color */ +// Add a single color uint8_t colorset_add_color(colorset_t *set, rgb_color_t col); uint8_t colorset_add_color_hsv(colorset_t *set, uint8_t hue, uint8_t sat, uint8_t val); void colorset_add_color_with_value_style(colorset_t *set, random_t *ctx, uint8_t hue, uint8_t sat, enum colorset_value_style valStyle, uint8_t numColors, uint8_t colorPos); void colorset_remove_color(colorset_t *set, uint8_t index); -/* Function to randomize the colors with various different modes of randomization */ +// Function to randomize the colors with various different modes of randomization void colorset_randomize_colors(colorset_t *set, random_t *ctx, uint8_t numColors, enum colorset_color_mode color_mode); -/* Fade all of the colors in the set */ +// Fade all of the colors in the set void colorset_adjust_brightness(colorset_t *set, uint8_t fadeby); -/* Get a color from the colorset */ +// Get a color from the colorset rgb_color_t colorset_get(const colorset_t *set, uint8_t index); /* Set an rgb color in a slot, or add a new color if you specify * a slot higher than the number of colors in the colorset */ void colorset_set(colorset_t *set, uint8_t index, rgb_color_t col); -/* Skip some amount of colors */ +// Skip some amount of colors void colorset_skip(colorset_t *set, int32_t amount); -/* Get current color in cycle */ +// Get current color in cycle rgb_color_t colorset_cur(const colorset_t *set); -/* Set the current index of the colorset */ +// Set the current index of the colorset void colorset_set_cur_index(colorset_t *set, uint8_t index); void colorset_reset_index(colorset_t *set); -/* The current index */ +// The current index uint8_t colorset_cur_index(const colorset_t *set); -/* Get the prev color in cycle */ +// Get the prev color in cycle rgb_color_t colorset_get_prev(colorset_t *set); -/* Get the next color in cycle */ +// Get the next color in cycle rgb_color_t colorset_get_next(colorset_t *set); -/* Peek at the color indexes from current but don't iterate */ +// Peek at the color indexes from current but don't iterate rgb_color_t colorset_peek(const colorset_t *set, int32_t offset); -/* Better wording for peek 1 ahead */ +// Better wording for peek 1 ahead rgb_color_t colorset_peek_next(const colorset_t *set); -/* The number of colors in the palette */ +// The number of colors in the palette uint8_t colorset_num_colors(const colorset_t *set); -/* Whether the colorset is currently on the first color or last color */ +// Whether the colorset is currently on the first color or last color uint8_t colorset_on_start(const colorset_t *set); uint8_t colorset_on_end(const colorset_t *set); diff --git a/Helios/Colortypes.cpp b/Helios/Colortypes.cpp index be745d7f..813f0dc5 100644 --- a/Helios/Colortypes.cpp +++ b/Helios/Colortypes.cpp @@ -1,11 +1,11 @@ #include "Colortypes.h" #if ALTERNATIVE_HSV_RGB == 1 -/* global hsv to rgb algorithm selector */ +// global hsv to rgb algorithm selector enum hsv_to_rgb_algorithm g_hsv_rgb_alg = HSV_TO_RGB_GENERIC; #endif -/* ========== HSVColor functions ========== */ +// ========== HSVColor functions ========== void hsv_init(hsv_color_t *hsv) { @@ -48,7 +48,7 @@ void hsv_assign_from_raw(hsv_color_t *hsv, uint32_t rhs) void hsv_assign_from_rgb(hsv_color_t *hsv, const rgb_color_t *rhs) { - /* always use generic */ + // always use generic hsv_color_t temp = rgb_to_hsv_generic(rhs); hsv_copy(hsv, &temp); } @@ -75,7 +75,7 @@ uint32_t hsv_raw(const hsv_color_t *hsv) return ((uint32_t)hsv->hue << 16) | ((uint32_t)hsv->sat << 8) | (uint32_t)hsv->val; } -/* ========== RGBColor functions ========== */ +// ========== RGBColor functions ========== void rgb_init(rgb_color_t *rgb) { @@ -152,7 +152,7 @@ void rgb_clear(rgb_color_t *rgb) rgb->blue = 0; } -/* scale down the brightness of a color by some fade amount */ +// scale down the brightness of a color by some fade amount void rgb_adjust_brightness(rgb_color_t *rgb, uint8_t fadeBy) { rgb->red = (((int)rgb->red) * (int)(256 - fadeBy)) >> 8; @@ -166,7 +166,7 @@ uint32_t rgb_raw(const rgb_color_t *rgb) } #ifdef HELIOS_CLI -/* Adjust brightness to ensure visibility on screens, without floating-point arithmetic */ +// Adjust brightness to ensure visibility on screens, without floating-point arithmetic void rgb_bring_up_brightness(rgb_color_t *rgb, uint8_t min_brightness) { hsv_color_t col; @@ -181,10 +181,10 @@ void rgb_bring_up_brightness(rgb_color_t *rgb, uint8_t min_brightness) rgb_assign_from_hsv(rgb, &col); } -/* scale a uint8 by a float value, don't use this on embedded! */ +// scale a uint8 by a float value, don't use this on embedded! #define FSCALE8(x, scale) (uint8_t)(((float)x * scale) > 255 ? 255 : ((float)x * scale)) -/* return a scaled up the brightness version of the current color */ +// return a scaled up the brightness version of the current color void rgb_scale_brightness(rgb_color_t *rgb, float scale) { rgb->red = FSCALE8(rgb->red, scale); @@ -231,26 +231,26 @@ rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) uint8_t offset = hue & 0x1F; /* 0..31 */ - /* offset8 = offset * 8 */ + // offset8 = offset * 8 uint8_t offset8 = offset; offset8 <<= 3; uint8_t third = SCALE8(offset8, (256 / 3)); /* max = 85 */ uint8_t r, g, b; if (!(hue & 0x80)) { - /* 0XX */ + // 0XX if (!(hue & 0x40)) { - /* 00X */ - /* section 0-1 */ + // 00X + // section 0-1 if (!(hue & 0x20)) { - /* 000 */ - /* case 0: R -> O */ + // 000 + // case 0: R -> O r = 255 - third; g = third; b = 0; } else { - /* 001 */ - /* case 1: O -> Y */ + // 001 + // case 1: O -> Y if (Y1) { r = 171; g = 85 + third; @@ -258,20 +258,20 @@ rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) } if (Y2) { r = 170 + third; - /* uint8_t twothirds = (third << 1); */ + // uint8_t twothirds = (third << 1); uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); /* max=170 */ g = 85 + twothirds; b = 0; } } } else { - /* 01X */ - /* section 2-3 */ + // 01X + // section 2-3 if (!(hue & 0x20)) { - /* 010 */ - /* case 2: Y -> G */ + // 010 + // case 2: Y -> G if (Y1) { - /* uint8_t twothirds = (third << 1); */ + // uint8_t twothirds = (third << 1); uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); /* max=170 */ r = 171 - twothirds; g = 170 + third; @@ -283,43 +283,43 @@ rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) b = 0; } } else { - /* 011 */ - /* case 3: G -> A */ + // 011 + // case 3: G -> A r = 0; g = 255 - third; b = third; } } } else { - /* section 4-7 */ - /* 1XX */ + // section 4-7 + // 1XX if (!(hue & 0x40)) { - /* 10X */ + // 10X if (!(hue & 0x20)) { - /* 100 */ - /* case 4: A -> B */ + // 100 + // case 4: A -> B r = 0; - /* uint8_t twothirds = (third << 1); */ + // uint8_t twothirds = (third << 1); uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); /* max=170 */ g = 171 - twothirds; /* 170? */ b = 85 + twothirds; } else { - /* 101 */ - /* case 5: B -> P */ + // 101 + // case 5: B -> P r = third; g = 0; b = 255 - third; } } else { if (!(hue & 0x20)) { - /* 110 */ - /* case 6: P -- K */ + // 110 + // case 6: P -- K r = 85 + third; g = 0; b = 171 - third; } else { - /* 111 */ - /* case 7: K -> R */ + // 111 + // case 7: K -> R r = 170 + third; g = 0; b = 85 - third; @@ -352,13 +352,13 @@ rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) } } - /* Now scale everything down if we're at value < 255. */ + // Now scale everything down if we're at value < 255. if (val != 255) { val = SCALE8(val, val); if (val == 0) { r = 0; g = 0; b = 0; } else { - /* nSCALE8x3_video( r, g, b, val); */ + // nSCALE8x3_video( r, g, b, val); if (r) r = SCALE8(r, val) + 1; if (g) g = SCALE8(g, val) + 1; if (b) b = SCALE8(b, val) + 1; @@ -376,7 +376,7 @@ rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) } #endif -/* generic hsv to rgb conversion nothing special */ +// generic hsv to rgb conversion nothing special rgb_color_t hsv_to_rgb_generic(const hsv_color_t *rhs) { unsigned char region, remainder, p, q, t; @@ -392,7 +392,7 @@ rgb_color_t hsv_to_rgb_generic(const hsv_color_t *rhs) region = rhs->hue / 43; remainder = ((rhs->hue - (region * 43)) * 6); - /* extraneous casts to uint16_t are to prevent overflow */ + // extraneous casts to uint16_t are to prevent overflow p = (uint8_t)(((uint16_t)(rhs->val) * (255 - rhs->sat)) >> 8); q = (uint8_t)(((uint16_t)(rhs->val) * (255 - (((uint16_t)(rhs->sat) * remainder) >> 8))) >> 8); t = (uint8_t)(((uint16_t)(rhs->val) * (255 - (((uint16_t)(rhs->sat) * (255 - remainder)) >> 8))) >> 8); @@ -420,7 +420,7 @@ rgb_color_t hsv_to_rgb_generic(const hsv_color_t *rhs) return col; } -/* Convert rgb to hsv with generic fast method */ +// Convert rgb to hsv with generic fast method hsv_color_t rgb_to_hsv_generic(const rgb_color_t *rhs) { unsigned char rgbMin, rgbMax; diff --git a/Helios/Colortypes.h b/Helios/Colortypes.h index f7b5d7f3..d32040eb 100644 --- a/Helios/Colortypes.h +++ b/Helios/Colortypes.h @@ -22,7 +22,7 @@ enum hsv_to_rgb_algorithm extern enum hsv_to_rgb_algorithm g_hsv_rgb_alg; #endif -/* Forward declarations */ +// Forward declarations typedef struct hsv_color_t hsv_color_t; typedef struct rgb_color_t rgb_color_t; @@ -40,7 +40,7 @@ struct rgb_color_t uint8_t blue; }; -/* HSVColor functions */ +// HSVColor functions void hsv_init(hsv_color_t *hsv); void hsv_init3(hsv_color_t *hsv, uint8_t hue, uint8_t sat, uint8_t val); void hsv_init_from_raw(hsv_color_t *hsv, uint32_t dwVal); @@ -53,7 +53,7 @@ uint8_t hsv_empty(const hsv_color_t *hsv); void hsv_clear(hsv_color_t *hsv); uint32_t hsv_raw(const hsv_color_t *hsv); -/* RGBColor functions */ +// RGBColor functions void rgb_init(rgb_color_t *rgb); void rgb_init3(rgb_color_t *rgb, uint8_t red, uint8_t green, uint8_t blue); void rgb_init_from_raw(rgb_color_t *rgb, uint32_t dwVal); @@ -72,18 +72,18 @@ uint32_t rgb_raw(const rgb_color_t *rgb); * ex: 0.0 = black, 0.5 = half brightness, 1.0 = no change, * 1.5 = 50% brighter, 2.0 = twice as bright, 255.0 = white */ void rgb_scale_brightness(rgb_color_t *rgb, float scale); -/* Bring up the brightness of a color to a minimum level */ +// Bring up the brightness of a color to a minimum level void rgb_bring_up_brightness(rgb_color_t *rgb, uint8_t min_brightness); #endif -/* Conversion functions */ +// Conversion functions /* Stolen from FastLED hsv to rgb full rainbow where all colours * are given equal weight, this makes for-example yellow larger * best to use this function as it is the legacy choice */ rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs); -/* Generic hsv to rgb conversion nothing special */ +// Generic hsv to rgb conversion nothing special rgb_color_t hsv_to_rgb_generic(const hsv_color_t *rhs); -/* Convert rgb to hsv with generic fast method */ +// Convert rgb to hsv with generic fast method hsv_color_t rgb_to_hsv_generic(const rgb_color_t *rhs); #ifdef __cplusplus diff --git a/Helios/Helios.cpp b/Helios/Helios.cpp index c9d18db3..de97d3ea 100644 --- a/Helios/Helios.cpp +++ b/Helios/Helios.cpp @@ -23,11 +23,11 @@ #include -/* Internal macros */ +// Internal macros #define NUM_MENUS_HUE_SAT_VAL 4 #define NUM_MENUS_QUADRANT 7 -/* Color select options for internal menu logic */ +// Color select options for internal menu logic enum color_select_option { OPTION_NONE = 0, SELECTED_ADD, @@ -35,7 +35,7 @@ enum color_select_option { SELECTED_SLOT }; -/* Global state variables */ +// Global state variables static enum helios_state g_cur_state; static enum helios_flags g_global_flags; static uint8_t g_menu_selection; @@ -54,7 +54,7 @@ static uint8_t g_sleeping; volatile char helios_version[] = HELIOS_VERSION_STR; -/* Forward declarations for internal helper functions */ +// Forward declarations for internal helper functions static uint8_t helios_init_components(void); static void helios_handle_state(void); static void helios_handle_state_modes(void); @@ -933,7 +933,7 @@ static void helios_show_selection(rgb_color_t color) led_set_rgb(&color); } -/* Flag manipulation functions */ +// Flag manipulation functions void helios_set_flags(enum helios_flags flag) { g_global_flags = (enum helios_flags)(g_global_flags | flag); diff --git a/Helios/Helios.h b/Helios/Helios.h index 9d1b839b..c8bf71c4 100644 --- a/Helios/Helios.h +++ b/Helios/Helios.h @@ -11,7 +11,7 @@ extern "C" { #include "Colorset.h" #include "Pattern.h" -/* Forward declaration */ +// Forward declaration typedef struct pattern_t pattern_t; typedef struct colorset_t colorset_t; @@ -56,23 +56,23 @@ enum helios_state { }; enum helios_flags { - /* No flags are set */ + // No flags are set FLAG_NONE = 0, - /* The device is locked and must be unlocked to turn on */ + // The device is locked and must be unlocked to turn on FLAG_LOCKED = (1 << 0), - /* Conjure mode is enabled, one click will toggle power */ + // Conjure mode is enabled, one click will toggle power FLAG_CONJURE = (1 << 1), - /* Add new flags here, max 8 flags */ + // Add new flags here, max 8 flags - /* ============================================== */ - /* Auto increment to count the number of flags */ + // ============================================== + // Auto increment to count the number of flags INTERNAL_FLAGS_END, /* Calculate mask for invalid Flags based on the * inverse of all flags listed above here */ FLAGS_INVALID = (uint8_t)(~((1 << (INTERNAL_FLAGS_END - 1)) - 1)) }; -/* get/set global flags */ +// get/set global flags void helios_set_flags(enum helios_flags flag); uint8_t helios_has_flags(enum helios_flags flag); uint8_t helios_has_any_flags(enum helios_flags flag); diff --git a/Helios/HeliosConfig.h b/Helios/HeliosConfig.h index 454a0729..24b3dd0f 100644 --- a/Helios/HeliosConfig.h +++ b/Helios/HeliosConfig.h @@ -1,193 +1,193 @@ -#ifndef HELIOS_CONFIG_H -#define HELIOS_CONFIG_H - -// Version Configurations -// -// The engine major version indicates the state of the save file, -// if changes to the save format occur then the major version -// must increment so that the savefiles will not be loaded -#ifndef HELIOS_VERSION_MAJOR -#define HELIOS_VERSION_MAJOR 0 -#endif - -// A minor version simply indicates a bugfix or minor change that -// will not affect the save files produced by the engine. This means -// a savefile produced by 1.1 should be loadable by an engine on 1.2 -// and vice versa, but an engine on 2.0 cannot share savefiles with -// either of the engines on version 1.1 or 1.2 -#ifndef HELIOS_VERSION_MINOR -#define HELIOS_VERSION_MINOR 0 -#endif - -// The build or patch number based on the major.minor version, this is -// set by the build system using the number of commits since last version -#ifndef HELIOS_BUILD_NUMBER -#define HELIOS_BUILD_NUMBER 0 -#endif - -// Produces a number like 1.3.0 -#ifndef HELIOS_VERSION_NUMBER -#define HELIOS_VERSION_NUMBER HELIOS_VERSION_MAJOR.HELIOS_VERSION_MINOR.HELIOS_BUILD_NUMBER -#endif - - -// Helios Version String -// -// This is the string literal equivalent of HELIOS_VERSION_NUMBER above -#define ADD_QUOTES(str) #str -#define EXPAND_AND_QUOTE(str) ADD_QUOTES(str) -#define HELIOS_VERSION_STR EXPAND_AND_QUOTE(HELIOS_VERSION_NUMBER) - -// Short Click Threshold -// -// The length of time in milliseconds for a click to -// be considered either a short or long click -#define SHORT_CLICK_THRESHOLD 400 - -// Selection Flash Duration -// -// How long the led flashes when selecting something -#define TIME_TILL_LONG_CLICK_FLASH 1000 - -// Unlock Glow Lock Duration -// -// How long the hold the button to unlock chip -#define TIME_TILL_GLOW_LOCK_UNLOCK 2 - -// Hold Click Start Threshold -// -// The minimum length a hold click can be -#define HOLD_CLICK_START (SHORT_CLICK_THRESHOLD + TIME_TILL_LONG_CLICK_FLASH) - -// Hold Click End Threshold -// -// The maximum length a long click can be -#define HOLD_CLICK_END (HOLD_CLICK_START + TIME_TILL_LONG_CLICK_FLASH) - -// Max Color Slots -// -// The number of slots in a colorset -#define NUM_COLOR_SLOTS 6 - -// Mode Slots -// -// The number of modes on the device -#define NUM_MODE_SLOTS 6 - -// Number of Global Brightness Options -// -// The number of global brightness options -#define NUM_BRIGHTNESS_OPTIONS 4 - -// Default Brightness -// -// The default brightness of the led -#define DEFAULT_BRIGHTNESS 255 - -// Global Brightness Options -// -// There are three brightness options, high, medium, and low -#define BRIGHTNESS_HIGH 255 -#define BRIGHTNESS_MEDIUM 170 -#define BRIGHTNESS_LOW 85 -#define BRIGHTNESS_LOWEST 30 - -// Tickrate -// -// The number of engine ticks per second -#define TICKRATE 1000 - -// Menu Hold Time -// -// How long the button must be held for the menus to open and cycle -// note this is a measure of ticks, but if the tickrate is 1000 then -// it is a measure of milliseconds -#define MENU_HOLD_TIME 1000 - -// Force Sleep Time -// -// The duration in ms/ticks to hold the button to force the chip to -// sleep at any location in the menus -#define FORCE_SLEEP_TIME 7000 - -// Delete Color Time -// -// How long to hold button on a color to start the delete color flash -// begins and starts flashes. Also how long the cycling flash is for the -// delete color selection, ie how long the user has to release to delete -// the color before it cycles back -#define DELETE_COLOR_TIME 1500 - -// Alternative HSV to RGB -// -// This enabled the alternative HSV to RGB algorithm to be used in the -// color selection menu and provide a slightly different range of colors -#define ALTERNATIVE_HSV_RGB 0 - - -// Pre-defined saturation values -#define HSV_SAT_HIGH 255 -#define HSV_SAT_MEDIUM 220 -#define HSV_SAT_LOW 180 -#define HSV_SAT_LOWEST 120 - -// Pre-defined brightness values -#define HSV_VAL_HIGH 255 -#define HSV_VAL_MEDIUM 120 -#define HSV_VAL_LOW 60 -#define HSV_VAL_LOWEST 10 - -// ============================================================================ -// Storage Constants -// -// These are various storage sizes of data and some math to help -// calculate sizes or space requirements, note these will produce -// compiler errors unless you include the respective headers - - -// Storage Name -// -// This is mainly used by the CLI tool as a filename for simulated eeprom -#define STORAGE_FILENAME "Helios.storage" - -// Storage Size -// -// The total size of storage where modes and global settings are saved. -// The EEPROM on attiny85 is 512 bytes, but due to limitations on flash -// only the lower half of the eeprom is being used -#define STORAGE_SIZE 256 - -// Colorset Size -// -// the colorset is just an array of colors but it also has a num colors val -#define COLORSET_SIZE ((sizeof(rgb_color_t) * NUM_COLOR_SLOTS) + 1) - -// Pattern Args Size -// -// There is currently 6 args for a pattern: on, off, gap, dash, group, blend -// Each takes up 1 byte currently -#define PAT_ARGS_SIZE (sizeof(pattern_args_t)) - -// Pattern Size -// -// The actual pattern storage size is the size of the colorset + params + 1 pat flags -#define PATTERN_SIZE (COLORSET_SIZE + PAT_ARGS_SIZE + 1) - -// Slot Size -// -// the slot stores the pattern + 1 byte CRC -#define SLOT_SIZE (PATTERN_SIZE + 1) - -// Some math to calculate storage sizes: -// 3 * 6 = 18 for the colorset -// 1 + 6 + 1 + 1 = 9 for the rest -// = 27 bytes total for a pattern including CRC -// -> 9 slots = 9 * 27 = 243 -// = 12 bytes left - -// forbidden constant: -// #define HELIOS_ARDUINO 1 - - - -#endif +#ifndef HELIOS_CONFIG_H +#define HELIOS_CONFIG_H + +// Version Configurations +// +// The engine major version indicates the state of the save file, +// if changes to the save format occur then the major version +// must increment so that the savefiles will not be loaded +#ifndef HELIOS_VERSION_MAJOR +#define HELIOS_VERSION_MAJOR 0 +#endif + +// A minor version simply indicates a bugfix or minor change that +// will not affect the save files produced by the engine. This means +// a savefile produced by 1.1 should be loadable by an engine on 1.2 +// and vice versa, but an engine on 2.0 cannot share savefiles with +// either of the engines on version 1.1 or 1.2 +#ifndef HELIOS_VERSION_MINOR +#define HELIOS_VERSION_MINOR 0 +#endif + +// The build or patch number based on the major.minor version, this is +// set by the build system using the number of commits since last version +#ifndef HELIOS_BUILD_NUMBER +#define HELIOS_BUILD_NUMBER 0 +#endif + +// Produces a number like 1.3.0 +#ifndef HELIOS_VERSION_NUMBER +#define HELIOS_VERSION_NUMBER HELIOS_VERSION_MAJOR.HELIOS_VERSION_MINOR.HELIOS_BUILD_NUMBER +#endif + + +// Helios Version String +// +// This is the string literal equivalent of HELIOS_VERSION_NUMBER above +#define ADD_QUOTES(str) #str +#define EXPAND_AND_QUOTE(str) ADD_QUOTES(str) +#define HELIOS_VERSION_STR EXPAND_AND_QUOTE(HELIOS_VERSION_NUMBER) + +// Short Click Threshold +// +// The length of time in milliseconds for a click to +// be considered either a short or long click +#define SHORT_CLICK_THRESHOLD 400 + +// Selection Flash Duration +// +// How long the led flashes when selecting something +#define TIME_TILL_LONG_CLICK_FLASH 1000 + +// Unlock Glow Lock Duration +// +// How long the hold the button to unlock chip +#define TIME_TILL_GLOW_LOCK_UNLOCK 2 + +// Hold Click Start Threshold +// +// The minimum length a hold click can be +#define HOLD_CLICK_START (SHORT_CLICK_THRESHOLD + TIME_TILL_LONG_CLICK_FLASH) + +// Hold Click End Threshold +// +// The maximum length a long click can be +#define HOLD_CLICK_END (HOLD_CLICK_START + TIME_TILL_LONG_CLICK_FLASH) + +// Max Color Slots +// +// The number of slots in a colorset +#define NUM_COLOR_SLOTS 6 + +// Mode Slots +// +// The number of modes on the device +#define NUM_MODE_SLOTS 6 + +// Number of Global Brightness Options +// +// The number of global brightness options +#define NUM_BRIGHTNESS_OPTIONS 4 + +// Default Brightness +// +// The default brightness of the led +#define DEFAULT_BRIGHTNESS 255 + +// Global Brightness Options +// +// There are three brightness options, high, medium, and low +#define BRIGHTNESS_HIGH 255 +#define BRIGHTNESS_MEDIUM 170 +#define BRIGHTNESS_LOW 85 +#define BRIGHTNESS_LOWEST 30 + +// Tickrate +// +// The number of engine ticks per second +#define TICKRATE 1000 + +// Menu Hold Time +// +// How long the button must be held for the menus to open and cycle +// note this is a measure of ticks, but if the tickrate is 1000 then +// it is a measure of milliseconds +#define MENU_HOLD_TIME 1000 + +// Force Sleep Time +// +// The duration in ms/ticks to hold the button to force the chip to +// sleep at any location in the menus +#define FORCE_SLEEP_TIME 7000 + +// Delete Color Time +// +// How long to hold button on a color to start the delete color flash +// begins and starts flashes. Also how long the cycling flash is for the +// delete color selection, ie how long the user has to release to delete +// the color before it cycles back +#define DELETE_COLOR_TIME 1500 + +// Alternative HSV to RGB +// +// This enabled the alternative HSV to RGB algorithm to be used in the +// color selection menu and provide a slightly different range of colors +#define ALTERNATIVE_HSV_RGB 0 + + +// Pre-defined saturation values +#define HSV_SAT_HIGH 255 +#define HSV_SAT_MEDIUM 220 +#define HSV_SAT_LOW 180 +#define HSV_SAT_LOWEST 120 + +// Pre-defined brightness values +#define HSV_VAL_HIGH 255 +#define HSV_VAL_MEDIUM 120 +#define HSV_VAL_LOW 60 +#define HSV_VAL_LOWEST 10 + +// ============================================================================ +// Storage Constants +// +// These are various storage sizes of data and some math to help +// calculate sizes or space requirements, note these will produce +// compiler errors unless you include the respective headers + + +// Storage Name +// +// This is mainly used by the CLI tool as a filename for simulated eeprom +#define STORAGE_FILENAME "Helios.storage" + +// Storage Size +// +// The total size of storage where modes and global settings are saved. +// The EEPROM on attiny85 is 512 bytes, but due to limitations on flash +// only the lower half of the eeprom is being used +#define STORAGE_SIZE 256 + +// Colorset Size +// +// the colorset is just an array of colors but it also has a num colors val +#define COLORSET_SIZE ((sizeof(rgb_color_t) * NUM_COLOR_SLOTS) + 1) + +// Pattern Args Size +// +// There is currently 6 args for a pattern: on, off, gap, dash, group, blend +// Each takes up 1 byte currently +#define PAT_ARGS_SIZE (sizeof(pattern_args_t)) + +// Pattern Size +// +// The actual pattern storage size is the size of the colorset + params + 1 pat flags +#define PATTERN_SIZE (COLORSET_SIZE + PAT_ARGS_SIZE + 1) + +// Slot Size +// +// the slot stores the pattern + 1 byte CRC +#define SLOT_SIZE (PATTERN_SIZE + 1) + +// Some math to calculate storage sizes: +// 3 * 6 = 18 for the colorset +// 1 + 6 + 1 + 1 = 9 for the rest +// = 27 bytes total for a pattern including CRC +// -> 9 slots = 9 * 27 = 243 +// = 12 bytes left + +// forbidden constant: +// #define HELIOS_ARDUINO 1 + + + +#endif diff --git a/Helios/Led.cpp b/Helios/Led.cpp index 06ec5841..89e6cccd 100644 --- a/Helios/Led.cpp +++ b/Helios/Led.cpp @@ -21,19 +21,19 @@ #define SCALE8(i, scale) (((uint16_t)i * (uint16_t)(scale)) >> 8) -/* Forward declaration */ +// Forward declaration static void led_set_pwm(uint8_t pwmPin, uint8_t pwmValue, volatile uint8_t *controlRegister, uint8_t controlBit, volatile uint8_t *compareRegister); -/* array of led color values */ +// array of led color values static rgb_color_t m_ledColor; static rgb_color_t m_realColor; -/* global brightness */ +// global brightness static uint8_t m_brightness = DEFAULT_BRIGHTNESS; uint8_t led_init(void) { - /* clear the led colors */ + // clear the led colors rgb_init_from_raw(&m_ledColor, RGB_OFF); rgb_init_from_raw(&m_realColor, RGB_OFF); #ifdef HELIOS_EMBEDDED @@ -42,7 +42,7 @@ uint8_t led_init(void) pinMode(1, OUTPUT); pinMode(4, OUTPUT); #else - /* pin ctrl done in helios_init */ + // pin ctrl done in helios_init #endif #endif return 1; @@ -91,23 +91,23 @@ void led_strobe(uint16_t on_time, uint16_t off_time, const rgb_color_t *off_col, void led_breath(uint8_t hue, uint32_t duration, uint8_t magnitude, uint8_t sat, uint8_t val) { if (!duration) { - /* don't divide by 0 */ + // don't divide by 0 return; } - /* Determine the phase in the cycle */ + // Determine the phase in the cycle uint32_t phase = time_get_current_time() % (2 * duration); - /* Calculate hue shift */ + // Calculate hue shift int32_t hueShift; if (phase < duration) { - /* Ascending phase - from hue to hue + magnitude */ + // Ascending phase - from hue to hue + magnitude hueShift = (phase * magnitude) / duration; } else { - /* Descending phase - from hue + magnitude to hue */ + // Descending phase - from hue + magnitude to hue hueShift = ((2 * duration - phase) * magnitude) / duration; } - /* Apply hue shift - ensure hue stays within valid range */ + // Apply hue shift - ensure hue stays within valid range uint8_t shiftedHue = hue + hueShift; - /* Apply the hsv color as a strobing hue shift */ + // Apply the hsv color as a strobing hue shift hsv_color_t hsv; rgb_color_t off, on; hsv_init3(&hsv, shiftedHue, sat, val); @@ -128,15 +128,15 @@ static void led_set_pwm(uint8_t pwmPin, uint8_t pwmValue, volatile uint8_t *cont { #ifdef HELIOS_EMBEDDED if (pwmValue == 0) { - /* digitalWrite(pin, LOW) */ + // digitalWrite(pin, LOW) *controlRegister &= ~controlBit; /* Disable PWM */ PORTB &= ~(1 << pwmPin); /* Set the pin low */ } else if (pwmValue == 255) { - /* digitalWrite(pin, HIGH) */ + // digitalWrite(pin, HIGH) *controlRegister &= ~controlBit; /* Disable PWM */ PORTB |= (1 << pwmPin); /* Set the pin high */ } else { - /* analogWrite(pin, value) */ + // analogWrite(pin, value) *controlRegister |= controlBit; /* Enable PWM */ *compareRegister = pwmValue; /* Set PWM duty cycle */ } @@ -167,22 +167,22 @@ void led_set_brightness(uint8_t brightness) void led_update(void) { #ifdef HELIOS_EMBEDDED - /* write out the rgb values to analog pins */ + // write out the rgb values to analog pins #ifdef HELIOS_ARDUINO analogWrite(PWM_PIN_R, m_realColor.red); analogWrite(PWM_PIN_G, m_realColor.green); analogWrite(PWM_PIN_B, m_realColor.blue); #else - /* backup SREG and turn off interrupts */ + // backup SREG and turn off interrupts uint8_t oldSREG = SREG; cli(); - /* set the PWM for R/G/B output */ + // set the PWM for R/G/B output led_set_pwm(PWM_PIN_R, m_realColor.red, &TCCR0A, (1 << COM0A1), &OCR0A); led_set_pwm(PWM_PIN_G, m_realColor.green, &TCCR0A, (1 << COM0B1), &OCR0B); led_set_pwm(PWM_PIN_B, m_realColor.blue, >CCR, (1 << COM1B1), &OCR1B); - /* turn interrupts back on */ + // turn interrupts back on SREG = oldSREG; #endif #endif diff --git a/Helios/Led.h b/Helios/Led.h index cae2ca2c..fe64fe3d 100644 --- a/Helios/Led.h +++ b/Helios/Led.h @@ -15,34 +15,34 @@ extern "C" { uint8_t led_init(void); void led_cleanup(void); -/* control individual LED, these are appropriate to use in internal pattern logic */ +// control individual LED, these are appropriate to use in internal pattern logic void led_set_rgb(const rgb_color_t *col); void led_set_rgb3(uint8_t r, uint8_t g, uint8_t b); -/* Turn off individual LEDs, these are appropriate to use in internal pattern logic */ +// Turn off individual LEDs, these are appropriate to use in internal pattern logic void led_clear(void); -/* Dim individual LEDs, these are appropriate to use in internal pattern logic */ +// Dim individual LEDs, these are appropriate to use in internal pattern logic void led_adjust_brightness(uint8_t fadeBy); -/* strobe between two colors with a simple on/off timing */ +// strobe between two colors with a simple on/off timing void led_strobe(uint16_t on_time, uint16_t off_time, const rgb_color_t *col1, const rgb_color_t *col2); /* breath the hue on an index * warning: these use hsv to rgb in realtime! */ void led_breath(uint8_t hue, uint32_t duration, uint8_t magnitude, uint8_t sat, uint8_t val); -/* a very specialized api to hold all leds on a color for 250ms */ +// a very specialized api to hold all leds on a color for 250ms void led_hold(const rgb_color_t *col); -/* get the RGBColor of an Led index */ +// get the RGBColor of an Led index rgb_color_t led_get(void); -/* global brightness */ +// global brightness uint8_t led_get_brightness(void); void led_set_brightness(uint8_t brightness); -/* actually update the LEDs and show the changes */ +// actually update the LEDs and show the changes void led_update(void); #ifdef __cplusplus diff --git a/Helios/Pattern.cpp b/Helios/Pattern.cpp index 2378b48c..4a253bac 100644 --- a/Helios/Pattern.cpp +++ b/Helios/Pattern.cpp @@ -8,7 +8,7 @@ #include /* for memcpy */ -/* Forward declarations for internal functions */ +// Forward declarations for internal functions static void pattern_on_blink_on(pattern_t *pat); static void pattern_on_blink_off(pattern_t *pat); static void pattern_begin_gap(pattern_t *pat); @@ -57,21 +57,21 @@ void pattern_init_state(pattern_t *pat) { colorset_reset_index(&pat->m_colorset); - /* the default state to begin with */ + // the default state to begin with pat->m_state = STATE_BLINK_ON; /* if a dash is present then always start with the dash because * it consumes the first color in the colorset */ if (pat->m_args.dash_dur > 0) { pat->m_state = STATE_BEGIN_DASH; } - /* if there's no on duration or dash duration the led is just disabled */ + // if there's no on duration or dash duration the led is just disabled if ((!pat->m_args.on_dur && !pat->m_args.dash_dur) || !colorset_num_colors(&pat->m_colorset)) { pat->m_state = STATE_DISABLED; } pat->m_groupCounter = pat->m_args.group_size ? pat->m_args.group_size : (colorset_num_colors(&pat->m_colorset) - (pat->m_args.dash_dur != 0)); if (pat->m_args.blend_speed > 0) { - /* convert current/next colors to HSV but only if we are doing a blend */ + // convert current/next colors to HSV but only if we are doing a blend pat->m_cur = colorset_get_next(&pat->m_colorset); pat->m_next = colorset_get_next(&pat->m_colorset); } @@ -83,7 +83,7 @@ void pattern_play(pattern_t *pat) * instead of using a loop or recursion I have just used a simple goto */ replay: - /* its kinda evolving as i go */ + // its kinda evolving as i go switch (pat->m_state) { case STATE_DISABLED: return; @@ -138,7 +138,7 @@ void pattern_play(pattern_t *pat) } if (!timer_alarm(&pat->m_blinkTimer)) { - /* no alarm triggered just stay in current state, return and don't transition states */ + // no alarm triggered just stay in current state, return and don't transition states return; } @@ -155,10 +155,10 @@ void pattern_play(pattern_t *pat) * left in the group we need to cycle back to blink on instead of to the next state */ pat->m_state = (pat->m_groupCounter > 0) ? STATE_BLINK_ON : STATE_BEGIN_GAP; } else { - /* this is the standard case, iterate to the next state */ + // this is the standard case, iterate to the next state pat->m_state = (enum pattern_state)(pat->m_state + 1); } - /* poor-mans recurse with the new state change (this transitions to a new state within the same tick) */ + // poor-mans recurse with the new state change (this transitions to a new state within the same tick) goto replay; } @@ -238,11 +238,11 @@ uint8_t pattern_equals(const pattern_t *pat, const pattern_t *other) if (!other) { return 0; } - /* compare the colorset */ + // compare the colorset if (!colorset_equals(&pat->m_colorset, &other->m_colorset)) { return 0; } - /* compare the args of each pattern for equality */ + // compare the args of each pattern for equality if (memcmp(&pat->m_args, &other->m_args, sizeof(pattern_args_t)) != 0) { return 0; } @@ -289,11 +289,11 @@ static void pattern_blend_blink_on(pattern_t *pat) if (rgb_equals(&pat->m_cur, &pat->m_next)) { pat->m_next = colorset_get_next(&pat->m_colorset); } - /* interpolate to the next color */ + // interpolate to the next color pattern_interpolate(&pat->m_cur.red, pat->m_next.red, pat->m_args.blend_speed); pattern_interpolate(&pat->m_cur.green, pat->m_next.green, pat->m_args.blend_speed); pattern_interpolate(&pat->m_cur.blue, pat->m_next.blue, pat->m_args.blend_speed); - /* set the color */ + // set the color led_set_rgb(&pat->m_cur); } diff --git a/Helios/Pattern.h b/Helios/Pattern.h index 5399f504..18572b53 100644 --- a/Helios/Pattern.h +++ b/Helios/Pattern.h @@ -10,11 +10,11 @@ extern "C" { #include "Timer.h" #include "Patterns.h" -/* Forward declarations */ +// Forward declarations typedef struct pattern_args_t pattern_args_t; typedef struct pattern_t pattern_t; -/* for specifying things like default args */ +// for specifying things like default args struct pattern_args_t { uint8_t on_dur; uint8_t off_dur; @@ -24,33 +24,33 @@ struct pattern_args_t { uint8_t blend_speed; }; -/* Initialize pattern args with all parameters */ +// Initialize pattern args with all parameters void pattern_args_init(pattern_args_t *args, uint8_t on, uint8_t off, uint8_t gap, uint8_t dash, uint8_t group, uint8_t blend); -/* The various different blinking states the pattern can be in */ +// The various different blinking states the pattern can be in enum pattern_state { - /* the led is disabled (there is no on or dash) */ + // the led is disabled (there is no on or dash) STATE_DISABLED, - /* the pattern is blinking on the next color in the set */ + // the pattern is blinking on the next color in the set STATE_BLINK_ON, STATE_ON, - /* the pattern is blinking off */ + // the pattern is blinking off STATE_BLINK_OFF, STATE_OFF, - /* the pattern is starting a gap after a colorset */ + // the pattern is starting a gap after a colorset STATE_BEGIN_GAP, STATE_IN_GAP, - /* the pattern is beginning a dash after a colorset or gap */ + // the pattern is beginning a dash after a colorset or gap STATE_BEGIN_DASH, STATE_IN_DASH, - /* the pattern is starting a gap after a dash */ + // the pattern is starting a gap after a dash STATE_BEGIN_GAP2, STATE_IN_GAP2, }; @@ -64,65 +64,65 @@ struct pattern_t /* ================================== * Pattern Members */ - /* any flags the pattern has */ + // any flags the pattern has uint8_t m_patternFlags; - /* a copy of the colorset that this pattern is initialized with */ + // a copy of the colorset that this pattern is initialized with colorset_t m_colorset; /* ================================== * Blink Members */ uint8_t m_groupCounter; - /* the state of the current pattern */ + // the state of the current pattern enum pattern_state m_state; - /* the blink timer used to measure blink timings */ + // the blink timer used to measure blink timings helios_timer_t m_blinkTimer; /* ================================== * Blend Members */ - /* current color and target blend color */ + // current color and target blend color rgb_color_t m_cur; rgb_color_t m_next; }; -/* try to not set on duration to 0 */ +// try to not set on duration to 0 void pattern_init(pattern_t *pat, uint8_t onDur, uint8_t offDur, uint8_t gap, uint8_t dash, uint8_t group, uint8_t blend); void pattern_init_with_args(pattern_t *pat, const pattern_args_t *args); -/* init the pattern to initial state */ +// init the pattern to initial state void pattern_init_state(pattern_t *pat); -/* play the pattern */ +// play the pattern void pattern_play(pattern_t *pat); -/* set/get args */ +// set/get args void pattern_set_args(pattern_t *pat, const pattern_args_t *args); pattern_args_t pattern_get_args(const pattern_t *pat); pattern_args_t *pattern_args_ptr(pattern_t *pat); -/* change the colorset */ +// change the colorset colorset_t pattern_get_colorset(const pattern_t *pat); colorset_t *pattern_colorset_ptr(pattern_t *pat); void pattern_set_colorset(pattern_t *pat, const colorset_t *set); void pattern_clear_colorset(pattern_t *pat); -/* comparison to other pattern */ +// comparison to other pattern uint8_t pattern_equals(const pattern_t *pat, const pattern_t *other); -/* set a color in the colorset and re-initialize */ +// set a color in the colorset and re-initialize void pattern_update_color(pattern_t *pat, uint8_t index, const rgb_color_t *col); -/* calculate crc of the colorset + pattern */ +// calculate crc of the colorset + pattern uint32_t pattern_crc32(const pattern_t *pat); -/* get the pattern flags */ +// get the pattern flags uint32_t pattern_get_flags(const pattern_t *pat); uint8_t pattern_has_flags(const pattern_t *pat, uint32_t flags); -/* whether blend speed is non 0 */ +// whether blend speed is non 0 uint8_t pattern_is_blend(const pattern_t *pat); #ifdef __cplusplus diff --git a/Helios/Patterns.cpp b/Helios/Patterns.cpp index 7a7d6dfa..73c1fb8b 100644 --- a/Helios/Patterns.cpp +++ b/Helios/Patterns.cpp @@ -14,7 +14,7 @@ static const uint32_t color_codes3[] = {RGB_WHITE, RGB_BLUE_BRI_LOWEST, RGB_BLUE static const uint32_t color_codes4[] = {RGB_MAGENTA_BRI_LOWEST, RGB_ROYAL_BLUE_BRI_LOW, RGB_TURQUOISE, RGB_ROYAL_BLUE_BRI_LOW, RGB_MAGENTA_BRI_LOWEST, RGB_OFF}; static const uint32_t color_codes5[] = {RGB_RED, RGB_HOT_PINK, RGB_ROYAL_BLUE, RGB_BLUE, RGB_GREEN, RGB_YELLOW}; -/* Define Colorset configurations for each slot */ +// Define Colorset configurations for each slot struct default_colorset_t { uint8_t num_cols; const uint32_t *cols; @@ -68,12 +68,12 @@ void patterns_make_default(uint8_t index, pattern_t *pat) args.off_dur = 50; break; } - /* assign default args */ + // assign default args pattern_set_args(pat, &args); - /* build the set out of the defaults */ + // build the set out of the defaults colorset_t set; colorset_init_array(&set, default_colorsets[index].num_cols, default_colorsets[index].cols); - /* assign default colorset */ + // assign default colorset pattern_set_colorset(pat, &set); } diff --git a/Helios/Patterns.h b/Helios/Patterns.h index b8e6a1a6..a73b158c 100644 --- a/Helios/Patterns.h +++ b/Helios/Patterns.h @@ -7,7 +7,7 @@ extern "C" { #include -/* Forward declaration */ +// Forward declaration typedef struct pattern_t pattern_t; /* List of patterns that can be built, both single and multi-led patterns are found in this list. @@ -20,11 +20,11 @@ enum pattern_id { * PATTERN_FIRST when possible */ PATTERN_NONE = -1, - /* first pattern of all */ + // first pattern of all PATTERN_FIRST = 0, - /* ===================================== */ + // ===================================== - /* Strobe */ + // Strobe PATTERN_RIBBON = PATTERN_FIRST, PATTERN_ULTRA_DOPS, PATTERN_DOPS, @@ -37,24 +37,24 @@ enum pattern_id { PATTERN_GLOW, PATTERN_FLICKER, PATTERN_FLASH, - /* Morph */ + // Morph PATTERN_MORPH, PATTERN_MORPH_STROBE, PATTERN_MORPH_STROBIE, PATTERN_MORPH_GLOW, - /* Dash */ + // Dash PATTERN_DASH_DOPS, PATTERN_DASH_DOT, PATTERN_WAVE_PARTICLE, PATTERN_LIGHTSPEED, - /* Meta pattern constants */ + // Meta pattern constants INTERNAL_PATTERNS_END, PATTERN_LAST = (INTERNAL_PATTERNS_END - 1), PATTERN_COUNT = (PATTERN_LAST - PATTERN_FIRST) + 1 /* total number of patterns */ }; -/* Pattern creation functions */ +// Pattern creation functions void patterns_make_default(uint8_t index, pattern_t *pat); void patterns_make_pattern(enum pattern_id id, pattern_t *pat); diff --git a/Helios/Random.cpp b/Helios/Random.cpp index 8950ba08..db855d7a 100644 --- a/Helios/Random.cpp +++ b/Helios/Random.cpp @@ -22,7 +22,7 @@ void random_seed(random_t *rng, uint32_t newseed) uint16_t random_next16(random_t *rng, uint16_t minValue, uint16_t maxValue) { - /* walk the LCG forward to the next step */ + // walk the LCG forward to the next step rng->m_seed = (rng->m_seed * 1103515245 + 12345) & 0x7FFFFFFF; uint32_t range = maxValue - minValue; if (range != 0xFFFFFFFF) { diff --git a/Helios/Random.h b/Helios/Random.h index b5bee10f..bef01d0c 100644 --- a/Helios/Random.h +++ b/Helios/Random.h @@ -14,19 +14,19 @@ struct random_t uint32_t m_seed; }; -/* Initialize a random struct with default seed */ +// Initialize a random struct with default seed void random_init(random_t *rng); -/* Initialize a random struct with a specific seed */ +// Initialize a random struct with a specific seed void random_init_seed(random_t *rng, uint32_t newseed); -/* Set the seed for the random number generator */ +// Set the seed for the random number generator void random_seed(random_t *rng, uint32_t newseed); -/* Generate next random 8-bit value within range [minValue, maxValue] */ +// Generate next random 8-bit value within range [minValue, maxValue] uint8_t random_next8(random_t *rng, uint8_t minValue, uint8_t maxValue); -/* Generate next random 16-bit value within range [minValue, maxValue] */ +// Generate next random 16-bit value within range [minValue, maxValue] uint16_t random_next16(random_t *rng, uint16_t minValue, uint16_t maxValue); #ifdef __cplusplus diff --git a/Helios/Storage.cpp b/Helios/Storage.cpp index 5413c26a..6df4d5cd 100644 --- a/Helios/Storage.cpp +++ b/Helios/Storage.cpp @@ -15,7 +15,7 @@ #include #endif -/* Forward declarations for internal functions */ +// Forward declarations for internal functions static uint8_t storage_crc_pos(uint8_t pos); static uint8_t storage_read_crc(uint8_t pos); static uint8_t storage_check_crc(uint8_t pos); @@ -29,7 +29,7 @@ static inline void storage_internal_write(uint8_t address, uint8_t data); #endif #ifdef HELIOS_CLI -/* whether storage is enabled, default enabled */ +// whether storage is enabled, default enabled static uint8_t m_enableStorage = 1; #endif @@ -39,15 +39,15 @@ uint8_t storage_init(void) if (!m_enableStorage) { return 1; } - /* if the storage filename doesn't exist then create it */ + // if the storage filename doesn't exist then create it if (access(STORAGE_FILENAME, O_RDWR) != 0 && errno == ENOENT) { - /* The file doesn't exist, so try creating it */ + // The file doesn't exist, so try creating it FILE *f = fopen(STORAGE_FILENAME, "w+b"); if (!f) { perror("Error creating storage file for write"); return 0; } - /* fill the storage with 0s */ + // fill the storage with 0s uint32_t i; for (i = 0; i < STORAGE_SIZE; ++i){ uint8_t b = 0x0; @@ -146,25 +146,25 @@ uint8_t storage_crc8(uint8_t pos, uint8_t size) static uint8_t storage_crc_pos(uint8_t pos) { - /* crc the entire slot except last byte */ + // crc the entire slot except last byte return storage_crc8(pos, PATTERN_SIZE); } static uint8_t storage_read_crc(uint8_t pos) { - /* read the last byte of the slot */ + // read the last byte of the slot return storage_read_byte(pos + PATTERN_SIZE); } static uint8_t storage_check_crc(uint8_t pos) { - /* compare the last byte to the calculated crc */ + // compare the last byte to the calculated crc return (storage_read_crc(pos) == storage_crc_pos(pos)); } static void storage_write_crc(uint8_t pos) { - /* compare the last byte to the calculated crc */ + // compare the last byte to the calculated crc storage_write_byte(pos + PATTERN_SIZE, storage_crc_pos(pos)); } @@ -177,11 +177,11 @@ static void storage_write_byte(uint8_t address, uint8_t data) return; } storage_internal_write(address, data); - /* double check that shit */ + // double check that shit if (storage_read_byte(address) != data) { - /* do it again because eeprom is stupid */ + // do it again because eeprom is stupid storage_internal_write(address, data); - /* god forbid it doesn't write again */ + // god forbid it doesn't write again } #else /* HELIOS_CLI */ if (!m_enableStorage) { @@ -192,7 +192,7 @@ static void storage_write_byte(uint8_t address, uint8_t data) perror("Error opening storage file"); return; } - /* Seek to the specified address */ + // Seek to the specified address if (fseek(f, address, SEEK_SET) != 0) { perror("Error opening storage file for write"); fclose(f); @@ -209,7 +209,7 @@ static void storage_write_byte(uint8_t address, uint8_t data) static uint8_t storage_read_byte(uint8_t address) { #ifdef HELIOS_EMBEDDED - /* do a three way read because the attiny85 eeprom basically doesn't work */ + // do a three way read because the attiny85 eeprom basically doesn't work uint8_t b1 = storage_internal_read(address); uint8_t b2 = storage_internal_read(address); if (b1 == b2) { @@ -233,18 +233,18 @@ static uint8_t storage_read_byte(uint8_t address) } FILE *f = fopen(STORAGE_FILENAME, "rb"); /* Open file for reading in binary mode */ if (!f) { - /* this error is ok, just means no storage */ - /* perror("Error opening file for read"); */ + // this error is ok, just means no storage + // perror("Error opening file for read"); return val; } - /* Seek to the specified address */ + // Seek to the specified address if (fseek(f, address, SEEK_SET) != 0) { - /* error */ + // error perror("Failed to seek"); fclose(f); return val; } - /* Read a byte of data */ + // Read a byte of data if (!fread(&val, sizeof(uint8_t), 1, f)) { perror("Failed to read byte"); } @@ -257,29 +257,29 @@ static uint8_t storage_read_byte(uint8_t address) static inline void storage_internal_write(uint8_t address, uint8_t data) { while (EECR & (1< #include -/* convert seconds and nanoseconds to microseconds */ +// convert seconds and nanoseconds to microseconds #define SEC_TO_US(sec) ((sec)*1000000) #define NS_TO_US(ns) ((ns)/1000) #endif -/* static members */ +// static members static uint32_t m_curTick = 0; -/* the last frame timestamp */ +// the last frame timestamp static uint32_t m_prevTime = 0; #ifdef HELIOS_CLI -/* whether timestep is enabled, default enabled */ +// whether timestep is enabled, default enabled static uint8_t m_enableTimestep = 1; #endif @@ -50,7 +50,7 @@ void time_cleanup(void) void time_tick_clock(void) { - /* tick clock forward */ + // tick clock forward m_curTick++; #ifdef HELIOS_CLI @@ -66,12 +66,12 @@ void time_tick_clock(void) uint32_t us; do { us = time_microseconds(); - /* detect rollover of microsecond counter */ + // detect rollover of microsecond counter if (us < m_prevTime) { - /* calculate wrapped around difference */ + // calculate wrapped around difference elapsed_us = (uint32_t)((UINT32_MAX - m_prevTime) + us); } else { - /* otherwise calculate regular difference */ + // otherwise calculate regular difference elapsed_us = (uint32_t)(us - m_prevTime); } /* if building anywhere except visual studio then we can run alternate sleep code @@ -81,7 +81,7 @@ void time_tick_clock(void) * the number of microseconds per tick */ } while (elapsed_us < (1000000 / TICKRATE)); - /* store current time */ + // store current time m_prevTime = time_microseconds(); } @@ -115,10 +115,10 @@ uint32_t time_microseconds(void) * should always just rely on the current tick to perform operations */ uint8_t oldSREG = SREG; cli(); - /* multiply by 8 early to avoid floating point math or division */ + // multiply by 8 early to avoid floating point math or division uint32_t micros = (timer0_overflow_count * (256 * 8)) + (TCNT0 * 8); SREG = oldSREG; - /* then shift right to counteract the multiplication by 8 */ + // then shift right to counteract the multiplication by 8 return micros >> 6; #endif #endif @@ -132,16 +132,16 @@ time_delay_microseconds(uint32_t us) { #ifdef HELIOS_EMBEDDED #if F_CPU >= 16000000L - /* For the ATtiny85 running at 16MHz */ + // For the ATtiny85 running at 16MHz - /* The loop takes 3 cycles per iteration */ + // The loop takes 3 cycles per iteration us *= 2; /* 0.5us per iteration */ /* Subtract the overhead of the function call and loop setup * Assuming approximately 5 cycles overhead */ us -= 5; /* Simplified subtraction */ - /* Assembly loop for delay */ + // Assembly loop for delay __asm__ __volatile__( "1: sbiw %0, 1" "\n\t" /* 2 cycles */ @@ -151,16 +151,16 @@ time_delay_microseconds(uint32_t us) ); #elif F_CPU >= 8000000L - /* For the ATtiny85 running at 8MHz */ + // For the ATtiny85 running at 8MHz - /* The loop takes 4 cycles per iteration */ + // The loop takes 4 cycles per iteration us <<= 1; /* 1us per iteration */ /* Subtract the overhead of the function call and loop setup * Assuming approximately 6 cycles overhead */ us -= 6; /* Simplified subtraction */ - /* Assembly loop for delay */ + // Assembly loop for delay __asm__ __volatile__( "1: sbiw %0, 1" "\n\t" /* 2 cycles */ @@ -174,7 +174,7 @@ time_delay_microseconds(uint32_t us) uint32_t newtime = time_microseconds() + us; while (time_microseconds() < newtime) { - /* busy loop */ + // busy loop } #endif } @@ -184,7 +184,7 @@ void time_delay_milliseconds(uint32_t ms) #ifdef HELIOS_CLI usleep(ms * 1000); #else - /* not very accurate */ + // not very accurate uint16_t i; for (i = 0; i < ms; ++i) { time_delay_microseconds(1000); diff --git a/Helios/TimeControl.h b/Helios/TimeControl.h index 85fd4aac..954bf639 100644 --- a/Helios/TimeControl.h +++ b/Helios/TimeControl.h @@ -9,15 +9,15 @@ extern "C" { #include "HeliosConfig.h" -/* macros to convert milliseconds and seconds to measures of ticks */ +// macros to convert milliseconds and seconds to measures of ticks #define MS_TO_TICKS(ms) (uint32_t)(((uint32_t)(ms) * TICKRATE) / 1000) #define SEC_TO_TICKS(s) (uint32_t)((uint32_t)(s) * TICKRATE) -/* Initialize time system */ +// Initialize time system uint8_t time_init(void); void time_cleanup(void); -/* Tick the clock forward to millis() */ +// Tick the clock forward to millis() void time_tick_clock(void); /* Get the current tick, offset by any active simulation (simulation only exists in vortexlib) @@ -31,12 +31,12 @@ uint32_t time_get_current_time(void); * purpose of comparing against time_get_current_time() */ uint32_t time_microseconds(void); -/* Delay for some number of microseconds or milliseconds, these are bad */ +// Delay for some number of microseconds or milliseconds, these are bad void time_delay_microseconds(uint32_t us); void time_delay_milliseconds(uint32_t ms); #ifdef HELIOS_CLI -/* Toggle timestep on/off */ +// Toggle timestep on/off void time_enable_timestep(uint8_t enabled); #endif diff --git a/Helios/Timer.cpp b/Helios/Timer.cpp index e18be8ae..1f623387 100644 --- a/Helios/Timer.cpp +++ b/Helios/Timer.cpp @@ -19,7 +19,7 @@ void timer_init(helios_timer_t *timer, uint8_t alarm) void timer_start(helios_timer_t *timer, uint32_t offset) { - /* reset the start time */ + // reset the start time timer->m_startTime = time_get_current_time() + offset; } @@ -35,21 +35,21 @@ uint8_t timer_alarm(helios_timer_t *timer) return 0; } uint32_t now = time_get_current_time(); - /* time since start (forward or backwards) */ + // time since start (forward or backwards) int32_t timeDiff = (int32_t)(int64_t)(now - timer->m_startTime); if (timeDiff < 0) { return 0; } - /* if no time passed it's first alarm that is starting */ + // if no time passed it's first alarm that is starting if (timeDiff == 0) { return 1; } - /* if the current alarm duration is not a multiple of the current tick */ + // if the current alarm duration is not a multiple of the current tick if (timer->m_alarm && (timeDiff % timer->m_alarm) != 0) { - /* then the alarm was not hit */ + // then the alarm was not hit return 0; } - /* update the start time of the timer */ + // update the start time of the timer timer->m_startTime = now; return 1; } diff --git a/Helios/Timer.h b/Helios/Timer.h index 40997d07..a84d08ce 100644 --- a/Helios/Timer.h +++ b/Helios/Timer.h @@ -11,26 +11,26 @@ typedef struct helios_timer_t helios_timer_t; struct helios_timer_t { - /* the alarm */ + // the alarm uint32_t m_alarm; - /* start time in microseconds */ + // start time in microseconds uint32_t m_startTime; }; -/* Initialize a timer struct to default values */ +// Initialize a timer struct to default values void timer_init_default(helios_timer_t *timer); -/* Init a timer with a number of alarms and optionally start it */ +// Init a timer with a number of alarms and optionally start it void timer_init(helios_timer_t *timer, uint8_t alarm); /* Start the timer but don't change current alarm, this shifts * the timer startTime but does not reset it's alarm state */ void timer_start(helios_timer_t *timer, uint32_t offset); -/* Delete all alarms from the timer and reset */ +// Delete all alarms from the timer and reset void timer_reset(helios_timer_t *timer); -/* Will return true if the timer hit */ +// Will return true if the timer hit uint8_t timer_alarm(helios_timer_t *timer); #ifdef __cplusplus From db48e42a2fb9b856865b1b1455230fd930b1418f Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Sat, 18 Oct 2025 10:53:18 +0200 Subject: [PATCH 19/23] Convert remaining C-style comments to C++ style comments including multi-line comments --- Helios/Button.cpp | 36 +++++++++++----------- Helios/Button.h | 18 +++++------ Helios/Colorset.cpp | 20 ++++++------- Helios/Colorset.h | 8 ++--- Helios/Colortypes.cpp | 62 +++++++++++++++++++------------------- Helios/Colortypes.h | 16 +++++----- Helios/Helios.h | 4 +-- Helios/Led.cpp | 18 +++++------ Helios/Led.h | 8 ++--- Helios/Pattern.cpp | 50 +++++++++++++++---------------- Helios/Pattern.h | 16 +++++----- Helios/Patterns.cpp | 68 +++++++++++++++++++++--------------------- Helios/Patterns.h | 16 +++++----- Helios/Random.cpp | 8 ++--- Helios/Storage.cpp | 14 ++++----- Helios/Storage.h | 8 ++--- Helios/TimeControl.cpp | 56 +++++++++++++++++----------------- Helios/TimeControl.h | 14 ++++----- Helios/Timer.h | 4 +-- 19 files changed, 222 insertions(+), 222 deletions(-) diff --git a/Helios/Button.cpp b/Helios/Button.cpp index 06ef0171..8eefdc94 100644 --- a/Helios/Button.cpp +++ b/Helios/Button.cpp @@ -43,8 +43,8 @@ static uint8_t m_longClick = 0; static uint8_t m_holdClick = 0; #ifdef HELIOS_CLI -/* Note: For embedded builds, we exclude std::queue and CLI-only features. - * The CLI input queue functionality is omitted for embedded targets. */ +// Note: For embedded builds, we exclude std::queue and CLI-only features. +// The CLI input queue functionality is omitted for embedded targets. static uint8_t m_pinState = 0; static uint8_t m_enableWake = 0; // Simple input queue for CLI - using a fixed-size circular buffer @@ -94,7 +94,7 @@ void button_enable_wake(void) PCMSK |= (1 << PCINT3); GIMSK |= (1 << PCIE); sei(); -#else /* HELIOS_CLI */ +#else // HELIOS_CLI m_enableWake = 1; #endif } @@ -116,8 +116,8 @@ uint8_t button_check(void) return (PINB & (1 << 3)) != 0; #endif #elif defined(HELIOS_CLI) - /* then just return the pin state as-is, the input event may have - * adjusted this value */ + // then just return the pin state as-is, the input event may have + // adjusted this value return m_pinState; #else return 0; @@ -165,8 +165,8 @@ void button_update(void) m_holdClick = (m_newRelease && (m_holdDuration >= HOLD_CLICK_START) && (m_holdDuration <= HOLD_CLICK_END)); #ifdef HELIOS_CLI - /* if there was no pre-input event this tick, process a post input event - * to ensure there is only one event per tick processed */ + // if there was no pre-input event this tick, process a post input event + // to ensure there is only one event per tick processed if (!processed_pre) { button_process_post_input(); } @@ -242,24 +242,24 @@ static uint8_t button_process_pre_input(void) } char command = m_inputQueue[m_queueHead]; switch (command) { - case 'p': /* press */ + case 'p': // press button_do_press(); break; - case 'r': /* release */ + case 'r': // release button_do_release(); break; - case 't': /* toggle */ + case 't': // toggle button_do_toggle(); break; - case 'q': /* quit */ + case 'q': // quit helios_terminate(); break; - case 'w': /* wait */ + case 'w': // wait // wait is pre input I guess break; default: - /* return here! do not pop the queue - * do not process post input events */ + // return here! do not pop the queue + // do not process post input events return 0; } // now pop whatever pre-input command was processed @@ -276,10 +276,10 @@ static uint8_t button_process_post_input(void) // process input queue from the command line char command = m_inputQueue[m_queueHead]; switch (command) { - case 'c': /* click button */ + case 'c': // click button button_do_short_click(); break; - case 'l': /* long click button */ + case 'l': // long click button button_do_long_click(); break; default: @@ -317,8 +317,8 @@ void button_do_hold_click(void) m_releaseCount++; } -/* this will actually press down the button, it's your responsibility to wait - * for the appropriate number of ticks and then release the button */ +// this will actually press down the button, it's your responsibility to wait +// for the appropriate number of ticks and then release the button void button_do_press(void) { m_pinState = 1; diff --git a/Helios/Button.h b/Helios/Button.h index 1cd534a4..638cb820 100644 --- a/Helios/Button.h +++ b/Helios/Button.h @@ -56,19 +56,19 @@ uint8_t button_release_count(void); void button_enable_wake(void); #ifdef HELIOS_CLI -/* These will 'inject' a short/long click without actually touching the - * button state, it's important that code uses 'button_on_short_click' or - * 'button_on_long_click' to capture this injected input event. Code that uses - * for example: 'button_hold_duration() >= threshold && button_on_release()' - * will never trigger because the injected input event doesn't actually - * press the button or change the button state it just sets the 'shortClick' - * or 'longClick' values accordingly */ +// These will 'inject' a short/long click without actually touching the +// button state, it's important that code uses 'button_on_short_click' or +// 'button_on_long_click' to capture this injected input event. Code that uses +// for example: 'button_hold_duration() >= threshold && button_on_release()' +// will never trigger because the injected input event doesn't actually +// press the button or change the button state it just sets the 'shortClick' +// or 'longClick' values accordingly void button_do_short_click(void); void button_do_long_click(void); void button_do_hold_click(void); -/* This will actually press down the button, it's your responsibility to wait - * for the appropriate number of ticks and then release the button */ +// This will actually press down the button, it's your responsibility to wait +// for the appropriate number of ticks and then release the button void button_do_press(void); void button_do_release(void); void button_do_toggle(void); diff --git a/Helios/Colorset.cpp b/Helios/Colorset.cpp index 758e7195..b23fbb69 100644 --- a/Helios/Colorset.cpp +++ b/Helios/Colorset.cpp @@ -4,9 +4,9 @@ #include -/* when no color is selected in the colorset the index is this - * then when you call colorset_get_next() for the first time it returns - * the 0th color in the colorset and after the index will be 0 */ +// when no color is selected in the colorset the index is this +// then when you call colorset_get_next() for the first time it returns +// the 0th color in the colorset and after the index will be 0 #define INDEX_INVALID 255 void colorset_init(colorset_t *set) @@ -190,7 +190,7 @@ void colorset_randomize_colors(colorset_t *set, random_t *ctx, uint8_t numColors } else if (mode == COLOR_MODE_MONOCHROMATIC) { hueToUse = randomizedHue; valueToUse = 255 - (i * (256 / numColors)); - } else { /* EVENLY_SPACED */ + } else { // EVENLY_SPACED hueToUse = (randomizedHue + (256 / numColors) * i); } colorset_add_color_with_value_style(set, ctx, hueToUse, valueToUse, valStyle, numColors, i); @@ -221,8 +221,8 @@ rgb_color_t colorset_get(const colorset_t *set, uint8_t index) void colorset_set(colorset_t *set, uint8_t index, rgb_color_t col) { - /* special case for 'setting' a color at the edge of the palette, - * ie adding a new color when you set an index higher than the max */ + // special case for 'setting' a color at the edge of the palette, + // ie adding a new color when you set an index higher than the max if (index >= set->m_numColors) { if (!colorset_add_color(set, col)) { // ERROR_LOGF("Failed to add new color at index %u", index); @@ -245,11 +245,11 @@ void colorset_skip(colorset_t *set, int32_t amount) // first modulate the amount to skip to be within +/- the number of colors amount %= (int32_t)set->m_numColors; - /* max = 3 - * m_curIndex = 2 - * amount = -10 */ + // max = 3 + // m_curIndex = 2 + // amount = -10 set->m_curIndex = ((int32_t)set->m_curIndex + (int32_t)amount) % (int32_t)set->m_numColors; - if (set->m_curIndex > set->m_numColors) { /* must have wrapped */ + if (set->m_curIndex > set->m_numColors) { // must have wrapped // simply wrap it back set->m_curIndex += set->m_numColors; } diff --git a/Helios/Colorset.h b/Helios/Colorset.h index eccc28a3..47642a5b 100644 --- a/Helios/Colorset.h +++ b/Helios/Colorset.h @@ -55,8 +55,8 @@ struct colorset_t rgb_color_t m_palette[NUM_COLOR_SLOTS]; // the actual number of colors in the set uint8_t m_numColors; - /* the current index, starts at 255 so that - * the very first call to colorset_getNext will iterate to 0 */ + // the current index, starts at 255 so that + // the very first call to colorset_getNext will iterate to 0 uint8_t m_curIndex; }; @@ -101,8 +101,8 @@ void colorset_adjust_brightness(colorset_t *set, uint8_t fadeby); // Get a color from the colorset rgb_color_t colorset_get(const colorset_t *set, uint8_t index); -/* Set an rgb color in a slot, or add a new color if you specify - * a slot higher than the number of colors in the colorset */ +// Set an rgb color in a slot, or add a new color if you specify +// a slot higher than the number of colors in the colorset void colorset_set(colorset_t *set, uint8_t index, rgb_color_t col); // Skip some amount of colors diff --git a/Helios/Colortypes.cpp b/Helios/Colortypes.cpp index 813f0dc5..7e8adf11 100644 --- a/Helios/Colortypes.cpp +++ b/Helios/Colortypes.cpp @@ -193,49 +193,49 @@ void rgb_scale_brightness(rgb_color_t *rgb, float scale) } #endif -/* ======================================================== - * Below are various functions for converting hsv <-> rgb */ +// ======================================================== +// Below are various functions for converting hsv <-> rgb #if ALTERNATIVE_HSV_RGB == 1 #define SCALE8(i, scale) (((uint16_t)i * (uint16_t)(scale)) >> 8) #define FIXFRAC8(N,D) (((N)*256)/(D)) -/* Stolen from FastLED hsv to rgb full rainbow where all colours - * are given equal weight, this makes for-example yellow larger - * best to use this function as it is the legacy choice */ +// Stolen from FastLED hsv to rgb full rainbow where all colours +// are given equal weight, this makes for-example yellow larger +// best to use this function as it is the legacy choice rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) { rgb_color_t col; - /* Yellow has a higher inherent brightness than - * any other color; 'pure' yellow is perceived to - * be 93% as bright as white. In order to make - * yellow appear the correct relative brightness, - * it has to be rendered brighter than all other - * colors. - * Level Y1 is a moderate boost, the default. - * Level Y2 is a strong boost. */ + // Yellow has a higher inherent brightness than + // any other color; 'pure' yellow is perceived to + // be 93% as bright as white. In order to make + // yellow appear the correct relative brightness, + // it has to be rendered brighter than all other + // colors. + // Level Y1 is a moderate boost, the default. + // Level Y2 is a strong boost. const uint8_t Y1 = 1; const uint8_t Y2 = 0; - /* G2: Whether to divide all greens by two. - * Depends GREATLY on your particular LEDs */ + // G2: Whether to divide all greens by two. + // Depends GREATLY on your particular LEDs const uint8_t G2 = 0; - /* Gscale: what to scale green down by. - * Depends GREATLY on your particular LEDs */ + // Gscale: what to scale green down by. + // Depends GREATLY on your particular LEDs const uint8_t Gscale = 185; uint8_t hue = rhs->hue; uint8_t sat = rhs->sat; uint8_t val = rhs->val; - uint8_t offset = hue & 0x1F; /* 0..31 */ + uint8_t offset = hue & 0x1F; // 0..31 // offset8 = offset * 8 uint8_t offset8 = offset; offset8 <<= 3; - uint8_t third = SCALE8(offset8, (256 / 3)); /* max = 85 */ + uint8_t third = SCALE8(offset8, (256 / 3)); // max = 85 uint8_t r, g, b; if (!(hue & 0x80)) { // 0XX @@ -259,7 +259,7 @@ rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) if (Y2) { r = 170 + third; // uint8_t twothirds = (third << 1); - uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); /* max=170 */ + uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); // max=170 g = 85 + twothirds; b = 0; } @@ -272,7 +272,7 @@ rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) // case 2: Y -> G if (Y1) { // uint8_t twothirds = (third << 1); - uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); /* max=170 */ + uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); // max=170 r = 171 - twothirds; g = 170 + third; b = 0; @@ -300,8 +300,8 @@ rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) // case 4: A -> B r = 0; // uint8_t twothirds = (third << 1); - uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); /* max=170 */ - g = 171 - twothirds; /* 170? */ + uint8_t twothirds = SCALE8(offset8, ((256 * 2) / 3)); // max=170 + g = 171 - twothirds; // 170? b = 85 + twothirds; } else { // 101 @@ -327,13 +327,13 @@ rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) } } - /* This is one of the good places to scale the green down, - * although the client can scale green down as well. */ + // This is one of the good places to scale the green down, + // although the client can scale green down as well. if (G2) g = g >> 1; if (Gscale) g = SCALE8(g, Gscale); - /* Scale down colors if we're desaturated at all - * and add the brightness_floor to r, g, and b. */ + // Scale down colors if we're desaturated at all + // and add the brightness_floor to r, g, and b. if (sat != 255) { if (sat == 0) { r = 255; b = 255; g = 255; @@ -365,10 +365,10 @@ rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs) } } - /* Here we have the old AVR "missing std X+n" problem again - * It turns out that fixing it winds up costing more than - * not fixing it. - * To paraphrase Dr Bronner, profile! profile! profile! */ + // Here we have the old AVR "missing std X+n" problem again + // It turns out that fixing it winds up costing more than + // not fixing it. + // To paraphrase Dr Bronner, profile! profile! profile! col.red = r; col.green = g; col.blue = b; diff --git a/Helios/Colortypes.h b/Helios/Colortypes.h index d32040eb..b1f0616b 100644 --- a/Helios/Colortypes.h +++ b/Helios/Colortypes.h @@ -17,8 +17,8 @@ enum hsv_to_rgb_algorithm HSV_TO_RGB_RAINBOW }; -/* global hsv to rgb algorithm selector, switch this to control - * all hsv to rgb conversions */ +// global hsv to rgb algorithm selector, switch this to control +// all hsv to rgb conversions extern enum hsv_to_rgb_algorithm g_hsv_rgb_alg; #endif @@ -68,18 +68,18 @@ void rgb_adjust_brightness(rgb_color_t *rgb, uint8_t fadeBy); uint32_t rgb_raw(const rgb_color_t *rgb); #ifdef HELIOS_CLI -/* Return a scaled brightness version of the current color - * ex: 0.0 = black, 0.5 = half brightness, 1.0 = no change, - * 1.5 = 50% brighter, 2.0 = twice as bright, 255.0 = white */ +// Return a scaled brightness version of the current color +// ex: 0.0 = black, 0.5 = half brightness, 1.0 = no change, +// 1.5 = 50% brighter, 2.0 = twice as bright, 255.0 = white void rgb_scale_brightness(rgb_color_t *rgb, float scale); // Bring up the brightness of a color to a minimum level void rgb_bring_up_brightness(rgb_color_t *rgb, uint8_t min_brightness); #endif // Conversion functions -/* Stolen from FastLED hsv to rgb full rainbow where all colours - * are given equal weight, this makes for-example yellow larger - * best to use this function as it is the legacy choice */ +// Stolen from FastLED hsv to rgb full rainbow where all colours +// are given equal weight, this makes for-example yellow larger +// best to use this function as it is the legacy choice rgb_color_t hsv_to_rgb_rainbow(const hsv_color_t *rhs); // Generic hsv to rgb conversion nothing special rgb_color_t hsv_to_rgb_generic(const hsv_color_t *rhs); diff --git a/Helios/Helios.h b/Helios/Helios.h index c8bf71c4..1373149e 100644 --- a/Helios/Helios.h +++ b/Helios/Helios.h @@ -67,8 +67,8 @@ enum helios_flags { // ============================================== // Auto increment to count the number of flags INTERNAL_FLAGS_END, - /* Calculate mask for invalid Flags based on the - * inverse of all flags listed above here */ + // Calculate mask for invalid Flags based on the + // inverse of all flags listed above here FLAGS_INVALID = (uint8_t)(~((1 << (INTERNAL_FLAGS_END - 1)) - 1)) }; diff --git a/Helios/Led.cpp b/Helios/Led.cpp index 89e6cccd..9bce4e01 100644 --- a/Helios/Led.cpp +++ b/Helios/Led.cpp @@ -14,9 +14,9 @@ #include #include #endif -#define PWM_PIN_R PB0 /* Red channel (pin 5) */ -#define PWM_PIN_G PB1 /* Green channel (pin 6) */ -#define PWM_PIN_B PB4 /* Blue channel (pin 3) */ +#define PWM_PIN_R PB0 // Red channel (pin 5) +#define PWM_PIN_G PB1 // Green channel (pin 6) +#define PWM_PIN_B PB4 // Blue channel (pin 3) #endif #define SCALE8(i, scale) (((uint16_t)i * (uint16_t)(scale)) >> 8) @@ -129,16 +129,16 @@ static void led_set_pwm(uint8_t pwmPin, uint8_t pwmValue, volatile uint8_t *cont #ifdef HELIOS_EMBEDDED if (pwmValue == 0) { // digitalWrite(pin, LOW) - *controlRegister &= ~controlBit; /* Disable PWM */ - PORTB &= ~(1 << pwmPin); /* Set the pin low */ + *controlRegister &= ~controlBit; // Disable PWM + PORTB &= ~(1 << pwmPin); // Set the pin low } else if (pwmValue == 255) { // digitalWrite(pin, HIGH) - *controlRegister &= ~controlBit; /* Disable PWM */ - PORTB |= (1 << pwmPin); /* Set the pin high */ + *controlRegister &= ~controlBit; // Disable PWM + PORTB |= (1 << pwmPin); // Set the pin high } else { // analogWrite(pin, value) - *controlRegister |= controlBit; /* Enable PWM */ - *compareRegister = pwmValue; /* Set PWM duty cycle */ + *controlRegister |= controlBit; // Enable PWM + *compareRegister = pwmValue; // Set PWM duty cycle } #else (void)pwmPin; diff --git a/Helios/Led.h b/Helios/Led.h index fe64fe3d..a132b493 100644 --- a/Helios/Led.h +++ b/Helios/Led.h @@ -9,8 +9,8 @@ extern "C" { #include "Colortypes.h" -/* opting for static functions here because there should only ever be one - * Led control object and I don't like singletons */ +// opting for static functions here because there should only ever be one +// Led control object and I don't like singletons uint8_t led_init(void); void led_cleanup(void); @@ -28,8 +28,8 @@ void led_adjust_brightness(uint8_t fadeBy); // strobe between two colors with a simple on/off timing void led_strobe(uint16_t on_time, uint16_t off_time, const rgb_color_t *col1, const rgb_color_t *col2); -/* breath the hue on an index - * warning: these use hsv to rgb in realtime! */ +// breath the hue on an index +// warning: these use hsv to rgb in realtime! void led_breath(uint8_t hue, uint32_t duration, uint8_t magnitude, uint8_t sat, uint8_t val); // a very specialized api to hold all leds on a color for 250ms diff --git a/Helios/Pattern.cpp b/Helios/Pattern.cpp index 4a253bac..19a2fd3e 100644 --- a/Helios/Pattern.cpp +++ b/Helios/Pattern.cpp @@ -6,7 +6,7 @@ #include "HeliosConfig.h" #include "Led.h" -#include /* for memcpy */ +#include // for memcpy // Forward declarations for internal functions static void pattern_on_blink_on(pattern_t *pat); @@ -17,8 +17,8 @@ static void pattern_next_state(pattern_t *pat, uint8_t timing); static void pattern_blend_blink_on(pattern_t *pat); static void pattern_interpolate(uint8_t *current, const uint8_t next, uint8_t blend_speed); -/* ================================== - * Pattern Args Functions */ +// ================================== +// Pattern Args Functions void pattern_args_init(pattern_args_t *args, uint8_t on, uint8_t off, uint8_t gap, uint8_t dash, uint8_t group, uint8_t blend) @@ -31,8 +31,8 @@ void pattern_args_init(pattern_args_t *args, uint8_t on, uint8_t off, uint8_t ga args->blend_speed = blend; } -/* ================================== - * Pattern Functions */ +// ================================== +// Pattern Functions void pattern_init(pattern_t *pat, uint8_t onDur, uint8_t offDur, uint8_t gap, uint8_t dash, uint8_t group, uint8_t blend) @@ -59,8 +59,8 @@ void pattern_init_state(pattern_t *pat) // the default state to begin with pat->m_state = STATE_BLINK_ON; - /* if a dash is present then always start with the dash because - * it consumes the first color in the colorset */ + // if a dash is present then always start with the dash because + // it consumes the first color in the colorset if (pat->m_args.dash_dur > 0) { pat->m_state = STATE_BEGIN_DASH; } @@ -79,8 +79,8 @@ void pattern_init_state(pattern_t *pat) void pattern_play(pattern_t *pat) { - /* 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 */ + // 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 @@ -96,8 +96,8 @@ void pattern_play(pattern_t *pat) } pat->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 our colorset isn't at the end yet */ + // the whole 'should blink off' situation is tricky because we might need + // to go back to blinking on if our colorset isn't at the end yet if (pat->m_groupCounter > 0 || (!pat->m_args.gap_dur && !pat->m_args.dash_dur)) { if (pat->m_args.off_dur > 0) { pattern_on_blink_off(pat); @@ -142,17 +142,17 @@ void pattern_play(pattern_t *pat) return; } - /* this just transitions the state into the next state, with some edge conditions for - * transitioning to different states under certain circumstances. Honestly this is - * a nightmare to read now and idk how to fix it */ + // this just transitions the state into the next state, with some edge conditions for + // transitioning to different states under certain circumstances. Honestly this is + // a nightmare to read now and idk how to fix it if (pat->m_state == STATE_IN_GAP2 || (pat->m_state == STATE_OFF && pat->m_groupCounter > 0)) { - /* 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 */ + // 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 pat->m_state = pat->m_args.on_dur ? STATE_BLINK_ON : (pat->m_args.dash_dur ? STATE_BEGIN_DASH : STATE_BEGIN_GAP); } else if (pat->m_state == STATE_OFF && (!pat->m_groupCounter || colorset_num_colors(&pat->m_colorset) == 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 */ + // 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 pat->m_state = (pat->m_groupCounter > 0) ? STATE_BLINK_ON : STATE_BEGIN_GAP; } else { // this is the standard case, iterate to the next state @@ -190,13 +190,13 @@ static void pattern_on_blink_on(pattern_t *pat) static void pattern_on_blink_off(pattern_t *pat) { - (void)pat; /* unused */ + (void)pat; // unused led_clear(); } static void pattern_begin_gap(pattern_t *pat) { - (void)pat; /* unused */ + (void)pat; // unused led_clear(); } @@ -246,8 +246,8 @@ uint8_t pattern_equals(const pattern_t *pat, const pattern_t *other) if (memcmp(&pat->m_args, &other->m_args, sizeof(pattern_args_t)) != 0) { return 0; } - /* if those match then it's effectively the same - * pattern even if anything else is different */ + // if those match then it's effectively the same + // pattern even if anything else is different return 1; } @@ -284,8 +284,8 @@ uint8_t pattern_is_blend(const pattern_t *pat) static void pattern_blend_blink_on(pattern_t *pat) { - /* if we reached the next color, then cycle the colorset - * like normal and begin playing the next color */ + // if we reached the next color, then cycle the colorset + // like normal and begin playing the next color if (rgb_equals(&pat->m_cur, &pat->m_next)) { pat->m_next = colorset_get_next(&pat->m_colorset); } diff --git a/Helios/Pattern.h b/Helios/Pattern.h index 18572b53..666bfa6d 100644 --- a/Helios/Pattern.h +++ b/Helios/Pattern.h @@ -57,20 +57,20 @@ enum pattern_state struct pattern_t { - /* ================================== - * Pattern Parameters */ + // ================================== + // Pattern Parameters pattern_args_t m_args; - /* ================================== - * Pattern Members */ + // ================================== + // Pattern Members // any flags the pattern has uint8_t m_patternFlags; // a copy of the colorset that this pattern is initialized with colorset_t m_colorset; - /* ================================== - * Blink Members */ + // ================================== + // Blink Members uint8_t m_groupCounter; // the state of the current pattern @@ -79,8 +79,8 @@ struct pattern_t // the blink timer used to measure blink timings helios_timer_t m_blinkTimer; - /* ================================== - * Blend Members */ + // ================================== + // Blend Members // current color and target blend color rgb_color_t m_cur; diff --git a/Helios/Patterns.cpp b/Helios/Patterns.cpp index 73c1fb8b..5ee0cc91 100644 --- a/Helios/Patterns.cpp +++ b/Helios/Patterns.cpp @@ -4,9 +4,9 @@ #include "Pattern.h" #include "ColorConstants.h" -/* define arrays of colors, you can reuse these if you have multiple - * modes that use the same colorset -- these demonstrate the max amount - * of colors in each set but you can absolutely list a lesser amount */ +// define arrays of colors, you can reuse these if you have multiple +// modes that use the same colorset -- these demonstrate the max amount +// of colors in each set but you can absolutely list a lesser amount static const uint32_t color_codes0[] = {RGB_RED, RGB_ORANGE, RGB_YELLOW, RGB_TURQUOISE, RGB_BLUE, RGB_PINK}; static const uint32_t color_codes1[] = {RGB_RED, RGB_CORAL_ORANGE_SAT_MEDIUM, RGB_ORANGE, RGB_YELLOW_SAT_LOW}; static const uint32_t color_codes2[] = {RGB_PURPLE_BRI_LOWEST, RGB_MAGENTA, RGB_HOT_PINK_SAT_MEDIUM, RGB_PINK_SAT_LOWEST}; @@ -20,15 +20,15 @@ struct default_colorset_t { const uint32_t *cols; }; -/* the array of colorset entries, make sure the number on the left reflects - * the number of colors in the array on the right */ +// the array of colorset entries, make sure the number on the left reflects +// the number of colors in the array on the right static const struct default_colorset_t default_colorsets[] = { - { 6, color_codes0 }, /* 0 Lightside */ - { 4, color_codes1 }, /* 1 Sauna */ - { 4, color_codes2 }, /* 2 Butterfly */ - { 6, color_codes3 }, /* 3 Freezer Burn */ - { 6, color_codes4 }, /* 4 Ice Blade */ - { 6, color_codes5 }, /* 5 Rainbow Glitter */ + { 6, color_codes0 }, // 0 Lightside + { 4, color_codes1 }, // 1 Sauna + { 4, color_codes2 }, // 2 Butterfly + { 6, color_codes3 }, // 3 Freezer Burn + { 6, color_codes4 }, // 4 Ice Blade + { 6, color_codes5 }, // 5 Rainbow Glitter }; void patterns_make_default(uint8_t index, pattern_t *pat) @@ -39,31 +39,31 @@ void patterns_make_default(uint8_t index, pattern_t *pat) pattern_args_t args; pattern_args_init(&args, 0, 0, 0, 0, 0, 0); switch (index) { - case 0: /* Lightside */ + case 0: // Lightside args.on_dur = 2; args.gap_dur = 40; break; - case 1: /* Sauna */ + case 1: // Sauna args.on_dur = 1; args.off_dur = 9; break; - case 2: /* Butterfly */ + case 2: // Butterfly args.on_dur = 1; args.off_dur = 9; args.gap_dur = 6; args.dash_dur = 15; break; - case 3: /* Freezer Burn */ + case 3: // Freezer Burn args.on_dur = 1; args.off_dur = 9; args.dash_dur = 5; break; - case 4: /* Ice Blade */ + case 4: // Ice Blade args.on_dur = 3; args.off_dur = 1; args.gap_dur = 30; break; - case 5: /* Rainbow Glitter */ + case 5: // Rainbow Glitter args.on_dur = 1; args.off_dur = 50; break; @@ -86,7 +86,7 @@ void patterns_make_pattern(enum pattern_id id, pattern_t *pat) default: case PATTERN_RIBBON: - args.on_dur = 9; /* 10 for flashing pattern circles */ + args.on_dur = 9; // 10 for flashing pattern circles break; case PATTERN_ULTRA_DOPS: @@ -101,7 +101,7 @@ void patterns_make_pattern(enum pattern_id id, pattern_t *pat) case PATTERN_STROBE: args.on_dur = 5; - args.off_dur = 8; /* 10 for flashing pattern circles */ + args.off_dur = 8; // 10 for flashing pattern circles break; case PATTERN_HYPNOSTROBE: @@ -111,61 +111,61 @@ void patterns_make_pattern(enum pattern_id id, pattern_t *pat) case PATTERN_STROBIE: args.on_dur = 3; - args.off_dur = 23; /* 21 for flashing pattern circles */ + args.off_dur = 23; // 21 for flashing pattern circles break; case PATTERN_RAZOR: args.on_dur = 3; args.off_dur = 1; - args.gap_dur = 30; /* 29 for flashing pattern circles */ + args.gap_dur = 30; // 29 for flashing pattern circles break; case PATTERN_FLARE: args.on_dur = 2; - args.off_dur = 30; /* 28 for flashing pattern circles */ + args.off_dur = 30; // 28 for flashing pattern circles break; case PATTERN_BURST: args.on_dur = 3; - args.off_dur = 40; /* 37 for flashing pattern circles */ + args.off_dur = 40; // 37 for flashing pattern circles break; case PATTERN_GLOW: args.on_dur = 2; - args.gap_dur = 40; /* 39 for flashing pattern circles */ + args.gap_dur = 40; // 39 for flashing pattern circles break; case PATTERN_FLICKER: args.on_dur = 1; - args.off_dur = 50; /* 44 for flashing pattern circles */ + args.off_dur = 50; // 44 for flashing pattern circles break; case PATTERN_FLASH: args.on_dur = 10; - args.off_dur = 250; /* 120 for flashing pattern circles */ + args.off_dur = 250; // 120 for flashing pattern circles break; case PATTERN_MORPH: args.on_dur = 9; - args.blend_speed = 5; /* 14 for flashing pattern circles */ + args.blend_speed = 5; // 14 for flashing pattern circles break; case PATTERN_MORPH_STROBE: args.on_dur = 5; args.off_dur = 8; - args.blend_speed = 10; /* 19 for flashing pattern circles */ + args.blend_speed = 10; // 19 for flashing pattern circles break; case PATTERN_MORPH_STROBIE: args.on_dur = 3; args.off_dur = 23; - args.blend_speed = 10; /* 35 for flashing pattern circles */ + args.blend_speed = 10; // 35 for flashing pattern circles break; case PATTERN_MORPH_GLOW: args.on_dur = 1; args.off_dur = 3; - args.gap_dur = 40; /* 36 for flashing pattern circles */ + args.gap_dur = 40; // 36 for flashing pattern circles args.blend_speed = 30; break; @@ -173,25 +173,25 @@ void patterns_make_pattern(enum pattern_id id, pattern_t *pat) args.on_dur = 1; args.off_dur = 9; args.gap_dur = 6; - args.dash_dur = 15; /* 17 for flashing pattern circles */ + args.dash_dur = 15; // 17 for flashing pattern circles break; case PATTERN_DASH_DOT: args.on_dur = 2; args.off_dur = 3; - args.dash_dur = 24; /* 22 for flashing pattern circles */ + args.dash_dur = 24; // 22 for flashing pattern circles break; case PATTERN_WAVE_PARTICLE: args.on_dur = 1; args.off_dur = 9; - args.dash_dur = 5; /* 10 for flashing pattern circles */ + args.dash_dur = 5; // 10 for flashing pattern circles break; case PATTERN_LIGHTSPEED: args.on_dur = 2; args.off_dur = 3; - args.dash_dur = 24; /* 23 for flashing pattern circles */ + args.dash_dur = 24; // 23 for flashing pattern circles args.blend_speed = 10; break; } diff --git a/Helios/Patterns.h b/Helios/Patterns.h index a73b158c..8667a5a7 100644 --- a/Helios/Patterns.h +++ b/Helios/Patterns.h @@ -10,14 +10,14 @@ extern "C" { // Forward declaration typedef struct pattern_t pattern_t; -/* List of patterns that can be built, both single and multi-led patterns are found in this list. - * Within both single and multi LED pattern lists there are 'core' patterns which are associated - * with a class, and there are 'shell' patterns which are simply wrappers around another pattern - * with different parameters passed to the constructor. There is no way to know which patterns - * are 'core' patterns, except by looking at PatternBuilder::generate to see which classes exist */ +// List of patterns that can be built, both single and multi-led patterns are found in this list. +// Within both single and multi LED pattern lists there are 'core' patterns which are associated +// with a class, and there are 'shell' patterns which are simply wrappers around another pattern +// with different parameters passed to the constructor. There is no way to know which patterns +// are 'core' patterns, except by looking at PatternBuilder::generate to see which classes exist enum pattern_id { - /* no pattern at all, use this sparingly and default to - * PATTERN_FIRST when possible */ + // no pattern at all, use this sparingly and default to + // PATTERN_FIRST when possible PATTERN_NONE = -1, // first pattern of all @@ -51,7 +51,7 @@ enum pattern_id { // Meta pattern constants INTERNAL_PATTERNS_END, PATTERN_LAST = (INTERNAL_PATTERNS_END - 1), - PATTERN_COUNT = (PATTERN_LAST - PATTERN_FIRST) + 1 /* total number of patterns */ + PATTERN_COUNT = (PATTERN_LAST - PATTERN_FIRST) + 1 // total number of patterns }; // Pattern creation functions diff --git a/Helios/Random.cpp b/Helios/Random.cpp index db855d7a..a4c90905 100644 --- a/Helios/Random.cpp +++ b/Helios/Random.cpp @@ -26,10 +26,10 @@ uint16_t random_next16(random_t *rng, uint16_t minValue, uint16_t maxValue) rng->m_seed = (rng->m_seed * 1103515245 + 12345) & 0x7FFFFFFF; uint32_t range = maxValue - minValue; if (range != 0xFFFFFFFF) { - /* shift the seed 16 bits to the right because the lower 16 bits - * of this LCG are apparently not uniform whatsoever, where as the - * upper 16 bits appear to be quite uniform as per tests. We don't - * really need 32bit random values so we offer max 16bits of entropy */ + // shift the seed 16 bits to the right because the lower 16 bits + // of this LCG are apparently not uniform whatsoever, where as the + // upper 16 bits appear to be quite uniform as per tests. We don't + // really need 32bit random values so we offer max 16bits of entropy return ((rng->m_seed >> 16) % (range + 1)) + minValue; } return (rng->m_seed >> 16); diff --git a/Helios/Storage.cpp b/Helios/Storage.cpp index 6df4d5cd..c3e0d636 100644 --- a/Helios/Storage.cpp +++ b/Helios/Storage.cpp @@ -136,7 +136,7 @@ void storage_write_brightness(uint8_t brightness) uint8_t storage_crc8(uint8_t pos, uint8_t size) { - uint8_t hash = 33; /* A non-zero initial value */ + uint8_t hash = 33; // A non-zero initial value uint8_t i; for (i = 0; i < size; ++i) { hash = ((hash << 5) + hash) + storage_read_byte(pos); @@ -171,8 +171,8 @@ static void storage_write_crc(uint8_t pos) static void storage_write_byte(uint8_t address, uint8_t data) { #ifdef HELIOS_EMBEDDED - /* reads out the byte of the eeprom first to see if it's different - * before writing out the byte -- this is faster than always writing */ + // reads out the byte of the eeprom first to see if it's different + // before writing out the byte -- this is faster than always writing if (storage_read_byte(address) == data) { return; } @@ -183,7 +183,7 @@ static void storage_write_byte(uint8_t address, uint8_t data) storage_internal_write(address, data); // god forbid it doesn't write again } -#else /* HELIOS_CLI */ +#else // HELIOS_CLI if (!m_enableStorage) { return; } @@ -202,7 +202,7 @@ static void storage_write_byte(uint8_t address, uint8_t data) fclose(f); return; } - fclose(f); /* Close the file */ + fclose(f); // Close the file #endif } @@ -231,7 +231,7 @@ static uint8_t storage_read_byte(uint8_t address) if (access(STORAGE_FILENAME, O_RDONLY) != 0) { return val; } - FILE *f = fopen(STORAGE_FILENAME, "rb"); /* Open file for reading in binary mode */ + FILE *f = fopen(STORAGE_FILENAME, "rb"); // Open file for reading in binary mode if (!f) { // this error is ok, just means no storage // perror("Error opening file for read"); @@ -248,7 +248,7 @@ static uint8_t storage_read_byte(uint8_t address) if (!fread(&val, sizeof(uint8_t), 1, f)) { perror("Failed to read byte"); } - fclose(f); /* Close the file */ + fclose(f); // Close the file return val; #endif } diff --git a/Helios/Storage.h b/Helios/Storage.h index 85dc1a6c..55eaa9b1 100644 --- a/Helios/Storage.h +++ b/Helios/Storage.h @@ -8,11 +8,11 @@ extern "C" { #include #include "HeliosConfig.h" -/* the index of the first config byte, the config bytes start at the end - * then work their way backwards (so 'config index 0' is the last byte) */ +// the index of the first config byte, the config bytes start at the end +// then work their way backwards (so 'config index 0' is the last byte) #define CONFIG_START_INDEX (STORAGE_SIZE - 2) -/* the crc of the config bytes is the very last byte in storage - * TODO: implement the global config CRC again it got removed at some point */ +// the crc of the config bytes is the very last byte in storage +// TODO: implement the global config CRC again it got removed at some point #define CONFIG_CRC_INDEX (STORAGE_SIZE - 1) // Storage Config Indexes relative to the CONFIG_START_INDEX diff --git a/Helios/TimeControl.cpp b/Helios/TimeControl.cpp index d3d0b6b6..ff33a894 100644 --- a/Helios/TimeControl.cpp +++ b/Helios/TimeControl.cpp @@ -59,9 +59,9 @@ void time_tick_clock(void) } #endif - /* the rest of this only runs inside vortexlib because on the duo the tick runs in the - * tcb timer callback instead of in a busy loop constantly checking microseconds() - * perform timestep */ + // the rest of this only runs inside vortexlib because on the duo the tick runs in the + // tcb timer callback instead of in a busy loop constantly checking microseconds() + // perform timestep uint32_t elapsed_us; uint32_t us; do { @@ -74,11 +74,11 @@ void time_tick_clock(void) // otherwise calculate regular difference elapsed_us = (uint32_t)(us - m_prevTime); } - /* if building anywhere except visual studio then we can run alternate sleep code - * because in visual studio + windows it's better to just spin and check the high - * resolution clock instead of trying to sleep for microseconds. - * 1000us per ms, divided by tickrate gives - * the number of microseconds per tick */ + // if building anywhere except visual studio then we can run alternate sleep code + // because in visual studio + windows it's better to just spin and check the high + // resolution clock instead of trying to sleep for microseconds. + // 1000us per ms, divided by tickrate gives + // the number of microseconds per tick } while (elapsed_us < (1000000 / TICKRATE)); // store current time @@ -93,7 +93,7 @@ uint32_t time_get_current_time(void) #ifdef HELIOS_EMBEDDED volatile uint32_t timer0_overflow_count = 0; ISR(TIMER0_OVF_vect) { - timer0_overflow_count++; /* Increment on each overflow */ + timer0_overflow_count++; // Increment on each overflow } #endif @@ -108,11 +108,11 @@ uint32_t time_microseconds(void) #ifdef HELIOS_ARDUINO return micros(); #else - /* The only reason that micros() is actually necessary is if Helios::tick() - * cannot be called in a 1Khz ISR. If Helios::tick() cannot be reliably called - * by an interrupt then Time::tickClock() must perform manual timestep via micros(). - * If Helios::tick() is called by an interrupt then you don't need this function and - * should always just rely on the current tick to perform operations */ + // The only reason that micros() is actually necessary is if Helios::tick() + // cannot be called in a 1Khz ISR. If Helios::tick() cannot be reliably called + // by an interrupt then Time::tickClock() must perform manual timestep via micros(). + // If Helios::tick() is called by an interrupt then you don't need this function and + // should always just rely on the current tick to perform operations uint8_t oldSREG = SREG; cli(); // multiply by 8 early to avoid floating point math or division @@ -135,38 +135,38 @@ time_delay_microseconds(uint32_t us) // For the ATtiny85 running at 16MHz // The loop takes 3 cycles per iteration - us *= 2; /* 0.5us per iteration */ + us *= 2; // 0.5us per iteration - /* Subtract the overhead of the function call and loop setup - * Assuming approximately 5 cycles overhead */ - us -= 5; /* Simplified subtraction */ + // Subtract the overhead of the function call and loop setup + // Assuming approximately 5 cycles overhead + us -= 5; // Simplified subtraction // Assembly loop for delay __asm__ __volatile__( "1: sbiw %0, 1" - "\n\t" /* 2 cycles */ + "\n\t" // 2 cycles "nop" - "\n\t" /* 1 cycle */ - "brne 1b" : "=w"(us) : "0"(us) /* 2 cycles */ + "\n\t" // 1 cycle + "brne 1b" : "=w"(us) : "0"(us) // 2 cycles ); #elif F_CPU >= 8000000L // For the ATtiny85 running at 8MHz // The loop takes 4 cycles per iteration - us <<= 1; /* 1us per iteration */ + us <<= 1; // 1us per iteration - /* Subtract the overhead of the function call and loop setup - * Assuming approximately 6 cycles overhead */ - us -= 6; /* Simplified subtraction */ + // Subtract the overhead of the function call and loop setup + // Assuming approximately 6 cycles overhead + us -= 6; // Simplified subtraction // Assembly loop for delay __asm__ __volatile__( "1: sbiw %0, 1" - "\n\t" /* 2 cycles */ + "\n\t" // 2 cycles "rjmp .+0" - "\n\t" /* 2 cycles */ - "brne 1b" : "=w"(us) : "0"(us) /* 2 cycles */ + "\n\t" // 2 cycles + "brne 1b" : "=w"(us) : "0"(us) // 2 cycles ); #endif diff --git a/Helios/TimeControl.h b/Helios/TimeControl.h index 954bf639..587bddb8 100644 --- a/Helios/TimeControl.h +++ b/Helios/TimeControl.h @@ -20,15 +20,15 @@ void time_cleanup(void); // Tick the clock forward to millis() void time_tick_clock(void); -/* Get the current tick, offset by any active simulation (simulation only exists in vortexlib) - * Exposing this as inline or macro seems to save on space a non negligible amount, it is used a lot - * and exposing in the header probably allows the compiler to optimize away repetitive calls */ +// Get the current tick, offset by any active simulation (simulation only exists in vortexlib) +// Exposing this as inline or macro seems to save on space a non negligible amount, it is used a lot +// and exposing in the header probably allows the compiler to optimize away repetitive calls uint32_t time_get_current_time(void); -/* Current microseconds since startup, only use this for things like measuring rapid data transfer timings. - * If you just need to perform regular time checks for a pattern or some logic then use time_get_current_time() and measure - * time in ticks, use the SEC_TO_TICKS() or MS_TO_TICKS() macros to convert timings to measures of ticks for - * purpose of comparing against time_get_current_time() */ +// Current microseconds since startup, only use this for things like measuring rapid data transfer timings. +// If you just need to perform regular time checks for a pattern or some logic then use time_get_current_time() and measure +// time in ticks, use the SEC_TO_TICKS() or MS_TO_TICKS() macros to convert timings to measures of ticks for +// purpose of comparing against time_get_current_time() uint32_t time_microseconds(void); // Delay for some number of microseconds or milliseconds, these are bad diff --git a/Helios/Timer.h b/Helios/Timer.h index a84d08ce..f3c2e1f0 100644 --- a/Helios/Timer.h +++ b/Helios/Timer.h @@ -23,8 +23,8 @@ void timer_init_default(helios_timer_t *timer); // Init a timer with a number of alarms and optionally start it void timer_init(helios_timer_t *timer, uint8_t alarm); -/* Start the timer but don't change current alarm, this shifts - * the timer startTime but does not reset it's alarm state */ +// Start the timer but don't change current alarm, this shifts +// the timer startTime but does not reset it's alarm state void timer_start(helios_timer_t *timer, uint32_t offset); // Delete all alarms from the timer and reset From 2af7d9e84f14b94ec4b3551a2397e4e1d56c7ed0 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Sat, 18 Oct 2025 11:12:07 +0200 Subject: [PATCH 20/23] Update comments in main.cpp to use C++ style comments for consistency across the HeliosEmbedded project. --- HeliosEmbedded/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/HeliosEmbedded/main.cpp b/HeliosEmbedded/main.cpp index d537ca01..19412afd 100644 --- a/HeliosEmbedded/main.cpp +++ b/HeliosEmbedded/main.cpp @@ -4,14 +4,14 @@ #include #if !defined(HELIOS_CLI) && !defined(HELIOS_ARDUINO) -/* this is the main thread for non-arduino embedded builds */ +// this is the main thread for non-arduino embedded builds int main(int argc, char *argv[]) { - (void)argc; /* unused */ - (void)argv; /* unused */ + (void)argc; + (void)argv; helios_init(); - /* the main thread just initializes Helios then continuously calls tick */ + // the main thread just initializes Helios then continuously calls tick while (helios_keep_going()) { helios_tick(); } From 2fb496ccde83cbcf8255d23a3693453e1057ad1c Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Sat, 18 Oct 2025 11:24:10 +0200 Subject: [PATCH 21/23] Add comments to Button.cpp for clarity on button state checking and long press detection functions --- Helios/Button.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Helios/Button.cpp b/Helios/Button.cpp index 8eefdc94..66e5903d 100644 --- a/Helios/Button.cpp +++ b/Helios/Button.cpp @@ -107,6 +107,7 @@ ISR(PCINT0_vect) { } #endif +// directly poll the pin for whether it's pressed right now uint8_t button_check(void) { #ifdef HELIOS_EMBEDDED @@ -124,6 +125,7 @@ uint8_t button_check(void) #endif } +// detect if the button is being held for a long hold (past long click) uint8_t button_hold_pressing(void) { uint16_t holDur = (uint16_t)(button_hold_duration()); @@ -133,6 +135,7 @@ uint8_t button_hold_pressing(void) return 0; } +// poll the button pin and update the state of the button object void button_update(void) { #ifdef HELIOS_CLI From 4401ac7c346ed2dd01beb7c8b4211d054823fdea Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Sat, 18 Oct 2025 11:27:46 +0200 Subject: [PATCH 22/23] Update comments in Button.cpp to clarify input queue functionality and adjust queue size for improved performance --- Helios/Button.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Helios/Button.cpp b/Helios/Button.cpp index 66e5903d..20dd5bde 100644 --- a/Helios/Button.cpp +++ b/Helios/Button.cpp @@ -43,18 +43,17 @@ static uint8_t m_longClick = 0; static uint8_t m_holdClick = 0; #ifdef HELIOS_CLI -// Note: For embedded builds, we exclude std::queue and CLI-only features. -// The CLI input queue functionality is omitted for embedded targets. static uint8_t m_pinState = 0; static uint8_t m_enableWake = 0; -// Simple input queue for CLI - using a fixed-size circular buffer -// Larger queue size for CLI to handle long test sequences -#define INPUT_QUEUE_SIZE 8192 +// an input queue for the button, each tick one even is processed +// out of this queue and used to produce input +#define INPUT_QUEUE_SIZE 4096 static char m_inputQueue[INPUT_QUEUE_SIZE]; static uint32_t m_queueHead = 0; static uint32_t m_queueTail = 0; #endif +// initialize a new button object with a pin number uint8_t button_init(void) { m_pressTime = 0; @@ -87,6 +86,7 @@ uint8_t button_init(void) return 1; } +// enable wake on press void button_enable_wake(void) { #ifdef HELIOS_EMBEDDED From fe98d936f8d89fe20d8e53a79536ab9a92fbefd8 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Sat, 18 Oct 2025 11:36:29 +0200 Subject: [PATCH 23/23] Add missing comment before colorset_get function --- Helios/Colorset.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Helios/Colorset.cpp b/Helios/Colorset.cpp index b23fbb69..64f764f9 100644 --- a/Helios/Colorset.cpp +++ b/Helios/Colorset.cpp @@ -209,6 +209,7 @@ void colorset_adjust_brightness(colorset_t *set, uint8_t fadeby) } } +// get a color from the colorset rgb_color_t colorset_get(const colorset_t *set, uint8_t index) { rgb_color_t result;