From 3c22de49c7b3c896b05f8a3e523da3dced804143 Mon Sep 17 00:00:00 2001 From: lukas Date: Tue, 1 Feb 2022 00:51:31 +0100 Subject: [PATCH 1/2] add a writing animation of words --- src/Clock.cpp | 155 ++++++++++++++++++++++++++++++++++--------- src/Clock.h | 14 +++- src/LoadAnimator.cpp | 5 +- src/LoadAnimator.h | 2 + src/Word.cpp | 10 +++ src/Word.h | 19 ++++++ 6 files changed, 172 insertions(+), 33 deletions(-) create mode 100644 src/Word.cpp create mode 100644 src/Word.h diff --git a/src/Clock.cpp b/src/Clock.cpp index 9ef75c9..6ede7d2 100644 --- a/src/Clock.cpp +++ b/src/Clock.cpp @@ -30,13 +30,21 @@ void Clock::turnOn() { } void Clock::paintAlwaysOnLeds() { - printWord(types::es, Adafruit_NeoPixel::Color(255, 255, 0)); - printWord(types::ist, Adafruit_NeoPixel::Color(255, 0, 255)); + addque.push_back(Word(types::es, 0xFFFF00)); + addque.push_back(Word(types::ist, 0xFF00FF)); } -void Clock::printWord(const std::vector& word, uint32_t color) { - for (const uint8_t i : word) { - strip.setPixelColor(i, color); +void Clock::printWord(const std::vector& word, uint32_t color, bool highlightfirst) { + uint r = (color >> 16) & 0xFF; + uint g = (color >> 8) & 0xFF; + uint b = (color)&0xFF; + + for (const uint8_t& i : word) { + strip.setPixelColor( + i, + !highlightfirst || (&i == &word.back()) + ? color + : (((r > 200 ? r - 200 : r) << 16) | ((g > 200 ? g - 200 : g) << 8) | (b > 200 ? b - 200 : b))); } } @@ -50,8 +58,13 @@ void Clock::refreshTime() { return; } - animator.stopLoadAnimation(); - strip.clear(); + if (animator.animationActive()) { + animator.stopLoadAnimation(); + strip.clear(); + + // only add on first time iteration + paintAlwaysOnLeds(); + } tm* loctime = localtime(&now); @@ -65,18 +78,17 @@ void Clock::refreshTime() { strip.setBrightness(DAYBRIGHTNESS); } - paintAlwaysOnLeds(); setTime(hour, minute, this->twentyAfterSyntax, this->clockAlwaysOn); strip.show(); Serial.printf("Time now: %uh %uM\n", hour, minute); } -void Clock::setTime(uint8_t hour, uint8_t minute, bool twentyAfterSyntax, bool alwaysClockWord) { +void Clock::setTime(uint8_t hour, uint8_t minute, bool twas, bool alwaysClockWord) { const uint8_t minuteselector = minute / 5; // if minuteselector >= 4 +1 to hour - if (twentyAfterSyntax ? minuteselector >= 5 : minuteselector >= 4) + if (twas ? minuteselector >= 5 : minuteselector >= 4) hour++; // convert to 12h format @@ -98,24 +110,39 @@ void Clock::setTime(uint8_t hour, uint8_t minute, bool twentyAfterSyntax, bool a : hour == 0 || hour == 12 ? types::zwoelf : hourWord; - printWord(hourWord, Adafruit_NeoPixel::Color(0, 255, 255)); + if (oldhourWord != hourWord) { + addque.push_back(Word(hourWord, 0x00FFFF)); + delque.push_back(Word(oldhourWord, 0x00FFFF)); + + oldhourWord = hourWord; + } + // printWord(hourWord, Adafruit_NeoPixel::Color(0, 255, 255)); // print the minute words and the corresponding after/before half words - printMinutes(minuteselector, twentyAfterSyntax); + printMinutes(minuteselector, twas); // uhr - if (minuteselector == 0 || alwaysClockWord) - printWord(types::uhr, Adafruit_NeoPixel::Color(255, 0, 0)); + bool uhractive = minuteselector == 0 || alwaysClockWord; + + if (oldUhrActive != uhractive) { + const Word wrd = Word(types::uhr, 0xFF0000); + (uhractive ? addque : delque).push_back(wrd); + oldUhrActive = uhractive; + } + + performWordTransition(); } -void Clock::printMinutes(uint8_t minuteSelector, bool twentyAfterSyntax) { +void Clock::printMinutes(uint8_t minuteSelector, bool twas) { // fuenf / zehn / viertl word - std::vector minuteWord; + std::vector minuteWord, vornachWord; + bool halfActive = false; + if (minuteSelector == 1 || minuteSelector == 5 || minuteSelector == 7 || minuteSelector == 11) { minuteWord = types::fuenf; } else if (minuteSelector == 2 || minuteSelector == 4 || minuteSelector == 8 || minuteSelector == 10) { // check if we use the twenty after syntax - if ((minuteSelector == 4 || minuteSelector == 8) && twentyAfterSyntax) + if ((minuteSelector == 4 || minuteSelector == 8) && twas) minuteWord = types::zwanzig; else minuteWord = types::zehn; @@ -126,34 +153,45 @@ void Clock::printMinutes(uint8_t minuteSelector, bool twentyAfterSyntax) { else if (minuteSelector == 6) minuteWord = types::halb; - printWord(minuteWord, Adafruit_NeoPixel::Color(0, 255, 0)); + if (oldminuteWord != minuteWord) { + addque.push_back(Word(minuteWord, 0x00FF00)); + delque.push_back(Word(oldminuteWord, 0x00FF00)); + oldminuteWord = minuteWord; + } // vor / nach - std::vector vornachWord; if (minuteSelector == 1 || minuteSelector == 2 || minuteSelector == 3 || - ((minuteSelector == 8) && - !twentyAfterSyntax) || // check if twentyafter syntax is enabled - if not display the the after + ((minuteSelector == 8) && !twas) || // check if twentyafter syntax is enabled - if not display the the after minuteSelector == 7 || - ((minuteSelector == 4) && - twentyAfterSyntax)) // check if twentyafter syntax is enabled - if yes display the the after + ((minuteSelector == 4) && twas)) // check if twentyafter syntax is enabled - if yes display the the after vornachWord = types::nach; else if (minuteSelector == 5 || - ((minuteSelector == 4) && !twentyAfterSyntax) // check if twentyafter enabled if not -- display vor + ((minuteSelector == 4) && !twas) // check if twentyafter enabled if not -- display vor || minuteSelector == 10 || minuteSelector == 11 || - ((minuteSelector == 8) && twentyAfterSyntax)) // check if twentyafter enabled if yess -- display vor + ((minuteSelector == 8) && twas)) // check if twentyafter enabled if yess -- display vor vornachWord = types::vor; - printWord(vornachWord, Adafruit_NeoPixel::Color(255, 255, 255)); + + if (oldvornachWord != vornachWord) { + addque.push_back(Word(vornachWord, 0xFFFFFF)); + delque.push_back(Word(oldvornachWord, 0xFFFFFF)); + oldvornachWord = vornachWord; + } // halb if (minuteSelector >= 4 && minuteSelector <= 8) { // only 3 times if twentyafter syntax is on - if (!twentyAfterSyntax || (minuteSelector >= 5 && minuteSelector <= 7)) - printWord(types::halb, Adafruit_NeoPixel::Color(255, 0, 0)); + if (!twas || (minuteSelector >= 5 && minuteSelector <= 7)) + halfActive = true; + } + + if (oldhalfActive != halfActive) { + (halfActive ? addque : delque).push_back(Word(types::halb, 0xFF0000)); + oldhalfActive = halfActive; } } -void Clock::useTwentyAfterSyntax(bool twentyAFterSyntax) { - this->twentyAfterSyntax = twentyAFterSyntax; +void Clock::useTwentyAfterSyntax(bool twas) { + this->twentyAfterSyntax = twas; } void Clock::useAlwaysOnUhrSyntax(bool alwaysOnUhr) { @@ -164,3 +202,60 @@ void Clock::update() { if (refreshTicker.active()) this->refreshTime(); } + +int it = 0; +void Clock::performWordTransition() { + if (addque.empty() && delque.empty()) + return; + + int maxdels = 0; + for (const Word& el : delque) { + if (el.wrd.size() > maxdels) { + maxdels = el.wrd.size(); + } + } + + int maxadds = 0; + for (const Word& el : addque) { + if (el.wrd.size() > maxadds) { + maxadds = el.wrd.size(); + } + } + + // const uint iterationcount = maxdels + maxadds; + + if (!transitionTicker.active()) { + transitionTicker.attach_ms(150, [this, maxdels, maxadds]() { + if (it <= maxdels) { + for (Word word : delque) { + if (word.wrd.size() < it) + continue; + + this->printWord(word.wrd, 0x0, false); + this->printWord({word.wrd.begin(), word.wrd.end() - it}, word.color, false); + } + + this->strip.show(); + } else if (it - maxdels <= maxadds) { + // we are modding the endword + int idx = it - maxdels; + for (Word word : addque) { + if (word.wrd.size() < idx) + continue; + + this->printWord(word.wrd, 0x0, false); + this->printWord({word.wrd.begin(), word.wrd.begin() + idx}, word.color, idx < word.wrd.size()); + } + this->strip.show(); + } else { + addque.clear(); + delque.clear(); + + transitionTicker.detach(); + it = 0; + } + + it++; + }); + } +} diff --git a/src/Clock.h b/src/Clock.h index 032bbcf..99e12e2 100644 --- a/src/Clock.h +++ b/src/Clock.h @@ -8,8 +8,8 @@ #include "Arduino.h" #include #include "Adafruit_NeoPixel.h" -#include "Arduino.h" #include "LoadAnimator.h" +#include "Word.h" // define the strip length of the wordclock #define NUMPIXELS 108 @@ -24,6 +24,13 @@ class Clock { Adafruit_NeoPixel strip{}; LoadAnimator animator; Ticker refreshTicker; + Ticker transitionTicker; + + std::vector delque; + std::vector addque; + + std::vector oldminuteWord, oldvornachWord, oldhourWord; + bool oldhalfActive, oldUhrActive; bool twentyAfterSyntax; bool clockAlwaysOn; @@ -33,12 +40,15 @@ class Clock { */ void paintAlwaysOnLeds(); + std::vector minuteBefore; + void performWordTransition(); + /** * paint a specific word * @param word word pointer to print * @param color the color in which to print it */ - void printWord(const std::vector& word, uint32_t color); + void printWord(const std::vector& word, uint32_t color, bool highlightfirst); /** * logic to print the correct minute word and its corresponding wods diff --git a/src/LoadAnimator.cpp b/src/LoadAnimator.cpp index 956e145..28f53ba 100644 --- a/src/LoadAnimator.cpp +++ b/src/LoadAnimator.cpp @@ -43,4 +43,7 @@ void LoadAnimator::animationStep() { nrooo = nroo; nroo = nro; nro = nr; -} \ No newline at end of file +} +bool LoadAnimator::animationActive() { + return timer.active(); +} diff --git a/src/LoadAnimator.h b/src/LoadAnimator.h index 1414dee..5d3af0f 100644 --- a/src/LoadAnimator.h +++ b/src/LoadAnimator.h @@ -32,6 +32,8 @@ class LoadAnimator { * stop the animation */ void stopLoadAnimation(); + + bool animationActive(); }; #endif // LEDSTRIPINTERFACE_LOADANIMATOR_H diff --git a/src/Word.cpp b/src/Word.cpp new file mode 100644 index 0000000..38ed286 --- /dev/null +++ b/src/Word.cpp @@ -0,0 +1,10 @@ +// +// Created by lukas on 31.01.22. +// + +#include "Word.h" + +#include +Word::Word(std::vector wrd, uint32_t color) : wrd(std::move(wrd)), color(color) { + +} diff --git a/src/Word.h b/src/Word.h new file mode 100644 index 0000000..88f6ccb --- /dev/null +++ b/src/Word.h @@ -0,0 +1,19 @@ +// +// Created by lukas on 31.01.22. +// + +#ifndef LEDSTRIPINTERFACE_WORD_H +#define LEDSTRIPINTERFACE_WORD_H + +#include +#include "Arduino.h" + +class Word { + public: + std::vector wrd; + uint32_t color; + + Word(std::vector wrd, uint32_t color); +}; + +#endif // LEDSTRIPINTERFACE_WORD_H From 4b7553246d88b089b541fa74f08186403962de6c Mon Sep 17 00:00:00 2001 From: lukas Date: Tue, 1 Feb 2022 14:46:43 +0100 Subject: [PATCH 2/2] fix half transition fix static words not displayed after transition mode change --- src/Clock.cpp | 14 +++++++------- src/Clock.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Clock.cpp b/src/Clock.cpp index 6ede7d2..7d0f2a8 100644 --- a/src/Clock.cpp +++ b/src/Clock.cpp @@ -72,10 +72,13 @@ void Clock::refreshTime() { const uint8_t minute = loctime->tm_min; // enable night brighntess at 21' and disable it at 6' - if (hour >= 22 || hour <= 6) { - strip.setBrightness(NIGHTBRIGHTNESS); - } else { - strip.setBrightness(DAYBRIGHTNESS); + bool daybrightness = !(hour >= 22 || hour <= 6); + + if (oldDayBrightness != daybrightness) { + strip.setBrightness(daybrightness ? DAYBRIGHTNESS : NIGHTBRIGHTNESS); + paintAlwaysOnLeds(); + + oldDayBrightness = daybrightness; } setTime(hour, minute, this->twentyAfterSyntax, this->clockAlwaysOn); @@ -116,7 +119,6 @@ void Clock::setTime(uint8_t hour, uint8_t minute, bool twas, bool alwaysClockWor oldhourWord = hourWord; } - // printWord(hourWord, Adafruit_NeoPixel::Color(0, 255, 255)); // print the minute words and the corresponding after/before half words printMinutes(minuteselector, twas); @@ -150,8 +152,6 @@ void Clock::printMinutes(uint8_t minuteSelector, bool twas) { minuteWord = types::viertel; else if (minuteSelector == 9) minuteWord = types::dreiviertel; - else if (minuteSelector == 6) - minuteWord = types::halb; if (oldminuteWord != minuteWord) { addque.push_back(Word(minuteWord, 0x00FF00)); diff --git a/src/Clock.h b/src/Clock.h index 99e12e2..3c2f37d 100644 --- a/src/Clock.h +++ b/src/Clock.h @@ -30,7 +30,7 @@ class Clock { std::vector addque; std::vector oldminuteWord, oldvornachWord, oldhourWord; - bool oldhalfActive, oldUhrActive; + bool oldhalfActive, oldUhrActive, oldDayBrightness; bool twentyAfterSyntax; bool clockAlwaysOn;