diff --git a/examples/sevensegment/NeoSegmentBus/NeoSegmentBus.ino b/examples/sevensegment/NeoSegmentBus/NeoSegmentBus.ino new file mode 100644 index 0000000..e5c3718 --- /dev/null +++ b/examples/sevensegment/NeoSegmentBus/NeoSegmentBus.ino @@ -0,0 +1,36 @@ +// NeoSegmentBus +// This example will demonstrate using the NeoSegmentBus which provides support for a +// seven segment LED digit driven by three WS2811; connected in series with other digits +// +// See https://shop.idlehandsdev.com/products/addressable-7-segment-display for a hardware example +// +// This example will print the string "3.14" and then rotate it through the available digits +// + +#include + +const uint16_t DigitCount = 4; // Max Digits, not segments, not pixels +const uint8_t BusPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +#define brightness 128 + +NeoPixelSegmentBus strip(DigitCount, BusPin); + +void setup() +{ + strip.Begin(); + strip.Show(); // clears all digits by default + + delay(500); + strip.SetString(0, "3.14", brightness); + strip.Show(); +} + +void loop() +{ + delay(2000); + + strip.RotateLeft(1); + strip.Show(); +} + diff --git a/examples/sevensegment/NeoSegmentFade/NeoSegmentFade.ino b/examples/sevensegment/NeoSegmentFade/NeoSegmentFade.ino new file mode 100644 index 0000000..587f3cd --- /dev/null +++ b/examples/sevensegment/NeoSegmentFade/NeoSegmentFade.ino @@ -0,0 +1,144 @@ +// NeoSegmentBus +// This example will demonstrate using the NeoSegmentBus which provides support for a +// seven segment LED digit driven by three WS2811; connected in series with other digits +// +// See https://shop.idlehandsdev.com/products/addressable-7-segment-display for a hardware example +// +// This example will print current seconds since start of the Arduino +// with a digit animating a circling path for each second +// + +#include +#include + +const uint16_t DigitCount = 5; // Max Digits, not segments, not pixels +const uint8_t BusPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 +const uint16_t CycleDigit = 0; +const uint16_t SecondsDigit = 1; + +#define brightness 128 + +NeoPixelSegmentBus strip(DigitCount, BusPin); + +enum Animation +{ + Animation_Cycle, // animation for the cycle indicator + Animation_Fade, // animation for fade of seconds + Animation_COUNT +}; + +NeoPixelAnimator animations(Animation_COUNT); + +void CycleAnimation(const AnimationParam& param) +{ + // calculate which segment should be on using the animation progress + uint8_t bitfield = 1 << (uint8_t)(param.progress * LedSegment_F); + // instant a digit with that segment on + SevenSegDigit digit(bitfield, brightness); + // apply it to the strip + strip.SetPixelColor(CycleDigit, digit); +} + +// for the animation of fading the new number in, we use +// two digit DIBs (Device Independant Bitmaps) of SevenSegDigit to blend with; +// each sized one less than the strip due to the first is a used for the cycle +// animation. +typedef NeoDib SevenSegDib; + +SevenSegDib StartingDigits(DigitCount - 1); +SevenSegDib EndingDigits(DigitCount - 1); + +// shader class that will do the "string" blending +// +class DigitBlendShader +{ +public: + // this shader always renders and doesn't track a dirty state + bool IsDirty() const + { + return true; + } + + void ResetDirty() + { + } + + SevenSegDigit Apply(uint16_t indexDigit, SevenSegDigit digit) + { + // since we call EndingDigits.Render below, the digit argument is + // from the EndingDigits so no need to call GetPixelColor to get it + // create a digit that is a blend between the last seconds + // value and the next seconds value using the BlendAmount + SevenSegDigit blendDigit = SevenSegDigit::LinearBlend( + StartingDigits.GetPixelColor(indexDigit), + digit, + BlendAmount); + + return blendDigit; + } + + float BlendAmount; +}; + +// the instance of our shader class +DigitBlendShader blendShader; + +void FadeAnimation(const AnimationParam& param) +{ + // set the shader property BlendAmount to the animation progress + blendShader.BlendAmount = param.progress; + // apply it to the strip at the SecondsDigit location + EndingDigits.Render(strip, + blendShader, + SecondsDigit); +} + +uint32_t lastSeconds; + +void setup() +{ + lastSeconds = millis() / 1000; + + strip.Begin(); + strip.Show(); + + // init animation Dibs as cleared + StartingDigits.ClearTo(0); + EndingDigits.ClearTo(0); +} + +void loop() +{ + uint32_t seconds = millis() / 1000; + + // when the seconds change, start animations for the update + // + if (seconds != lastSeconds) + { + // copy last animation ending digits as starting digits + StartingDigits = EndingDigits; + + // format and display new value in ending digits dib + String display(seconds); + SevenSegDigit::SetString(EndingDigits, + 0, + display.c_str(), + brightness); + + // start the seconds fade animation + animations.StartAnimation(Animation_Fade, 1000, FadeAnimation); + + // start the cycle animation for the next second + animations.StartAnimation(Animation_Cycle, 1000, CycleAnimation); + + lastSeconds = seconds; + } + + if (animations.IsAnimating()) + { + // the normal loop just needs these two to run the active animations + animations.UpdateAnimations(); + strip.Show(); + } +} + diff --git a/src/NeoPixelAnimator.h b/src/NeoPixelAnimator.h index c49d9ec..46fcad1 100644 --- a/src/NeoPixelAnimator.h +++ b/src/NeoPixelAnimator.h @@ -153,7 +153,6 @@ private: _duration = duration; _remaining = duration; _fnCallback = animUpdate; - } void StopAnimation() diff --git a/src/NeoPixelBus.h b/src/NeoPixelBus.h index 15ac120..c7833b6 100644 --- a/src/NeoPixelBus.h +++ b/src/NeoPixelBus.h @@ -43,10 +43,12 @@ License along with NeoPixel. If not, see #include "internal/HsbColor.h" #include "internal/HtmlColor.h" #include "internal/RgbwColor.h" +#include "internal/SegmentDigit.h" #include "internal/NeoColorFeatures.h" #include "internal/DotStarColorFeatures.h" #include "internal/Lpd8806ColorFeatures.h" +#include "internal/NeoSegmentFeatures.h" #include "internal/Layouts.h" #include "internal/NeoTopology.h" diff --git a/src/NeoPixelSegmentBus.h b/src/NeoPixelSegmentBus.h new file mode 100644 index 0000000..ebe16ce --- /dev/null +++ b/src/NeoPixelSegmentBus.h @@ -0,0 +1,67 @@ +/*------------------------------------------------------------------------- +NeoPixelBus library wrapper template class that provides enhanced methods +for writing to segment based strips + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#include "NeoPixelBus.h" + +template class NeoPixelSegmentBus : + public NeoPixelBus +{ +public: + NeoPixelSegmentBus(uint16_t countPixels, uint8_t pin) : + NeoPixelBus(countPixels, pin) + { + } + + NeoPixelSegmentBus(uint16_t countPixels) : + NeoPixelBus(countPixels) + { + } + + void SetString(uint16_t indexDigit, + const char* str, + uint8_t brightness, + uint8_t defaultBrightness = 0) + { + T_COLOR_FEATURE::ColorObject::SetString(*this, + indexDigit, + str, + brightness, + defaultBrightness); + } + + void SetString(uint16_t indexDigit, + const String& str, + uint8_t brightness, + uint8_t defaultBrightness = 0) + { + SetString(indexDigit, str.c_str(), brightness, defaultBrightness); + } +}; + + diff --git a/src/internal/NeoColorFeatures.h b/src/internal/NeoColorFeatures.h index 9922363..ab6c6b0 100644 --- a/src/internal/NeoColorFeatures.h +++ b/src/internal/NeoColorFeatures.h @@ -45,9 +45,10 @@ public: uint8_t* pEnd = pPixelDest + (count * PixelSize); while (pPixelDest < pEnd) { - *pPixelDest++ = pPixelSrc[0]; - *pPixelDest++ = pPixelSrc[1]; - *pPixelDest++ = pPixelSrc[2]; + for (uint8_t iElement = 0; iElement < PixelSize; iElement++) + { + *pPixelDest++ = pPixelSrc[iElement]; + } } } @@ -57,8 +58,6 @@ public: while (pPixelDest < pEnd) { *pPixelDest++ = *pPixelSrc++; - *pPixelDest++ = *pPixelSrc++; - *pPixelDest++ = *pPixelSrc++; } } @@ -69,8 +68,6 @@ public: while (pPixelDest < pEnd) { *pPixelDest++ = pgm_read_byte(pSrc++); - *pPixelDest++ = pgm_read_byte(pSrc++); - *pPixelDest++ = pgm_read_byte(pSrc++); } } @@ -81,8 +78,6 @@ public: while (pDestBack > pPixelDest) { *--pDestBack = *--pSrcBack; - *--pDestBack = *--pSrcBack; - *--pDestBack = *--pSrcBack; } } diff --git a/src/internal/NeoDib.h b/src/internal/NeoDib.h index 2ec93eb..9ede9b1 100644 --- a/src/internal/NeoDib.h +++ b/src/internal/NeoDib.h @@ -94,6 +94,25 @@ public: free((uint8_t*)_pixels); } + NeoDib& operator=(const NeoDib& other) + { + // check for self-assignment + if (&other == this) + { + return *this; + } + + uint16_t copyCount = other.PixelCount() < PixelCount() ? other.PixelCount() : PixelCount(); + + for (uint16_t pixel = 0; pixel < copyCount; pixel++) + { + _pixels[pixel] = other.Pixels()[pixel]; + } + + Dirty(); + return *this; + } + T_COLOR_OBJECT* Pixels() const { return _pixels; @@ -144,8 +163,8 @@ public: Dirty(); }; - template void Render(NeoBufferContext destBuffer, - T_SHADER& shader) + template + void Render(NeoBufferContext destBuffer, T_SHADER& shader, uint16_t destIndexPixel = 0) { if (IsDirty() || shader.IsDirty()) { @@ -159,7 +178,7 @@ public: for (uint16_t indexPixel = 0; indexPixel < countPixels; indexPixel++) { T_COLOR_OBJECT color = shader.Apply(indexPixel, _pixels[indexPixel]); - T_COLOR_FEATURE::applyPixelColor(destBuffer.Pixels, indexPixel, color); + T_COLOR_FEATURE::applyPixelColor(destBuffer.Pixels, destIndexPixel + indexPixel, color); } shader.ResetDirty(); diff --git a/src/internal/NeoSegmentFeatures.h b/src/internal/NeoSegmentFeatures.h new file mode 100644 index 0000000..9e5ea54 --- /dev/null +++ b/src/internal/NeoSegmentFeatures.h @@ -0,0 +1,130 @@ +/*------------------------------------------------------------------------- +NeoSegmentFeatures provides feature classes to describe seven segment display +elements for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class Neo9Elements +{ +public: + static const size_t PixelSize = 9; // three 3 element + + static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + static const uint8_t* getPixelAddress(const uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + + static void replicatePixel(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + while (pPixelDest < pEnd) + { + for (uint8_t iElement = 0; iElement < PixelSize; iElement++) + { + *pPixelDest++ = pPixelSrc[iElement]; + } + } + } + + static void movePixelsInc(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + while (pPixelDest < pEnd) + { + *pPixelDest++ = *pPixelSrc++; + } + } + + static void movePixelsInc_P(uint8_t* pPixelDest, PGM_VOID_P pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + const uint8_t* pSrc = (const uint8_t*)pPixelSrc; + while (pPixelDest < pEnd) + { + *pPixelDest++ = pgm_read_byte(pSrc++); + } + } + + static void movePixelsDec(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pDestBack = pPixelDest + (count * PixelSize); + const uint8_t* pSrcBack = pPixelSrc + (count * PixelSize); + while (pDestBack > pPixelDest) + { + *--pDestBack = *--pSrcBack; + } + } + + typedef SevenSegDigit ColorObject; +}; + +// Abcdefg byte order +class NeoAbcdefgSegmentFeature : public Neo9Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + uint8_t commonSize = (PixelSize < color.SegmentCount) ? PixelSize : color.SegmentCount; + for (uint8_t iSegment = 0; iSegment < commonSize; iSegment++) + { + *p++ = color.Segment[iSegment]; + } + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + uint8_t commonSize = (PixelSize < color.SegmentCount) ? PixelSize : color.SegmentCount; + + for (uint8_t iSegment = 0; iSegment < commonSize; iSegment++) + { + color.Segment[iSegment] = *p++; + } + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + uint8_t commonSize = (PixelSize < color.SegmentCount) ? PixelSize : color.SegmentCount; + + for (uint8_t iSegment = 0; iSegment < commonSize; iSegment++) + { + color.Segment[iSegment] = pgm_read_byte(p++); + } + + return color; + } + +}; + +typedef NeoAbcdefgSegmentFeature SevenSegmentFeature; // Abcdefg order is default \ No newline at end of file diff --git a/src/internal/SegmentDigit.cpp b/src/internal/SegmentDigit.cpp new file mode 100644 index 0000000..b456afa --- /dev/null +++ b/src/internal/SegmentDigit.cpp @@ -0,0 +1,153 @@ +/*------------------------------------------------------------------------- +SegmentDigit provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include "SegmentDigit.h" + +// +// https://en.wikichip.org/wiki/seven-segment_display/representing_letters +// +const uint8_t SevenSegDigit::DecodeNumbers[] = { + // 0 1 2 3 4 5 6 7 8 9 + 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F }; + +const uint8_t SevenSegDigit::DecodeAlphaCaps[] = { + // A B C D E F G + 0x77, 0x00, 0x39, 0x00, 0x79, 0x71, 0x3D, + // H I J K L M N + 0x76, 0x30, 0x1E, 0x00, 0x38, 0x00, 0x00, + // O P Q R S + 0x3F, 0x73, 0x00, 0x00, 0x6D, + // T U V W X Y Z + 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +const uint8_t SevenSegDigit::DecodeAlpha[] = { + // a b c d e f g + 0x00, 0x7C, 0x58, 0x5E, 0x00, 0x00, 0x00, + // h i j k l m n + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, + // o p q r s + 0x5C, 0x00, 0x67, 0x50, 0x00, + // t u v w x y z + 0x78, 0x1C, 0x00, 0x00, 0x00, 0x6E, 0x00 }; + +const uint8_t SevenSegDigit::DecodeSpecial[] = { + // , - . / + 0x80, 0x40, 0x80, 0x40 }; + +void SevenSegDigit::init(uint8_t bitmask, uint8_t brightness, uint8_t defaultBrightness) +{ + for (uint8_t iSegment = 0; iSegment < SegmentCount; iSegment++) + { + Segment[iSegment] = (bitmask & 0x01) ? brightness : defaultBrightness; + bitmask >>= 1; + } +} + +SevenSegDigit::SevenSegDigit(uint8_t bitmask, uint8_t brightness, uint8_t defaultBrightness) +{ + init(bitmask, brightness, defaultBrightness); +}; + +SevenSegDigit::SevenSegDigit(char letter, uint8_t brightness, uint8_t defaultBrightness) +{ + if (letter >= '0' && letter <= '9') + { + init(DecodeNumbers[letter - '0'], brightness, defaultBrightness); + } + else if (letter >= 'a' && letter <= 'z') + { + init(DecodeAlpha[letter - 'a'], brightness, defaultBrightness); + } + else if (letter >= 'A' && letter <= 'Z') + { + init(DecodeAlphaCaps[letter - 'A'], brightness, defaultBrightness); + } + else if (letter >= ',' && letter <= '/') + { + init(DecodeSpecial[letter - ','], brightness, defaultBrightness); + } + else + { + memset(Segment, defaultBrightness, sizeof(Segment)); + } +}; + +uint8_t SevenSegDigit::CalculateBrightness() const +{ + uint16_t sum = 0; + + for (uint8_t iSegment = 0; iSegment < SegmentCount; iSegment++) + { + sum += Segment[iSegment]; + } + + return (uint8_t)(sum / SegmentCount); +} + +void SevenSegDigit::Darken(uint8_t delta) +{ + for (uint8_t iSegment = 0; iSegment < SegmentCount; iSegment++) + { + uint8_t element = Segment[iSegment]; + if (element > delta) + { + element -= delta; + } + else + { + element = 0; + } + Segment[iSegment] = element; + } +} + +void SevenSegDigit::Lighten(uint8_t delta) +{ + for (uint8_t iSegment = 0; iSegment < SegmentCount; iSegment++) + { + uint8_t element = Segment[iSegment]; + if (element < 255 - delta) + { + element += delta; + } + else + { + element = 255; + } + Segment[iSegment] = element; + } +} + +SevenSegDigit SevenSegDigit::LinearBlend(const SevenSegDigit& left, const SevenSegDigit& right, float progress) +{ + SevenSegDigit result; + + for (uint8_t iSegment = 0; iSegment < SegmentCount; iSegment++) + { + result.Segment[iSegment] = left.Segment[iSegment] + ((right.Segment[iSegment] - left.Segment[iSegment]) * progress); + } + return result; +} diff --git a/src/internal/SegmentDigit.h b/src/internal/SegmentDigit.h new file mode 100644 index 0000000..826044c --- /dev/null +++ b/src/internal/SegmentDigit.h @@ -0,0 +1,202 @@ +/*------------------------------------------------------------------------- +SegmentDigit provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +#include + +enum LedSegment +{ + LedSegment_A, + LedSegment_B, + LedSegment_C, + LedSegment_D, + LedSegment_E, + LedSegment_F, + LedSegment_G, + LedSegment_Decimal, + LedSegment_COUNT +}; + +// ------------------------------------------------------------------------ +// SevenSegDigit represents a color object that is represented by the segments +// of a 7 segment LED display digit. It contains helpful routines to manipulate +// and set the elements. +// +// The order represents the physical LED location starting at A, through to G, then +// ending at the decimal point +// "abcdefg." +// ------------------------------------------------------------------------ +struct SevenSegDigit +{ + // ------------------------------------------------------------------------ + // Construct a SevenSegDigit using + // the default brightness to apply to all segments + // ------------------------------------------------------------------------ + SevenSegDigit(uint8_t defaultBrightness) + { + memset(Segment, defaultBrightness, sizeof(Segment)); + } + + // ------------------------------------------------------------------------ + // Construct a SevenSegDigit using + // a bitmask for the segment (bit order is ".gfedcba") + // the brightness to apply to them, (0-255) + // the default brightness to apply to those not set in the bitmask (0-255) + // ------------------------------------------------------------------------ + SevenSegDigit(uint8_t bitmask, uint8_t brightness, uint8_t defaultBrightness = 0); + + // ------------------------------------------------------------------------ + // Construct a SevenSegDigit using + // a char that will get mapped to the segments, + // the brightness to apply to them, (0-255) + // the default brightness to apply to those not set in the bitmask (0-255) + // ------------------------------------------------------------------------ + SevenSegDigit(char letter, uint8_t brightness, uint8_t defaultBrightness = 0); + + // ------------------------------------------------------------------------ + // Construct a SevenSegDigit that will have its values set in latter operations + // CAUTION: The members are not initialized and may not be consistent + // ------------------------------------------------------------------------ + SevenSegDigit() + { + }; + + // ------------------------------------------------------------------------ + // Comparison operators + // ------------------------------------------------------------------------ + bool operator==(const SevenSegDigit& other) const + { + for (uint8_t iSegment = 0; iSegment < SegmentCount; iSegment++) + { + if (Segment[iSegment] != other.Segment[iSegment]) + { + return false; + } + } + return true; + }; + + bool operator!=(const SevenSegDigit& other) const + { + return !(*this == other); + }; + + // ------------------------------------------------------------------------ + // CalculateBrightness will calculate the overall brightness + // NOTE: This is a simple linear brightness + // ------------------------------------------------------------------------ + uint8_t CalculateBrightness() const; + + // ------------------------------------------------------------------------ + // Darken will adjust the color by the given delta toward black + // NOTE: This is a simple linear change + // delta - (0-255) the amount to dim the segment + // ------------------------------------------------------------------------ + void Darken(uint8_t delta); + + // ------------------------------------------------------------------------ + // Lighten will adjust the color by the given delta toward white + // NOTE: This is a simple linear change + // delta - (0-255) the amount to lighten the segment + // ------------------------------------------------------------------------ + void Lighten(uint8_t delta); + + // ------------------------------------------------------------------------ + // LinearBlend between two colors by the amount defined by progress variable + // left - the segment to start the blend at + // right - the segment to end the blend at + // progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right + // and a value between will blend the brightness of each element + // weighted linearly between them + // ------------------------------------------------------------------------ + static SevenSegDigit LinearBlend(const SevenSegDigit& left, const SevenSegDigit& right, float progress); + + template + static void SetString(T_SET_TARGET& target, uint16_t indexDigit, const char* str, uint8_t brightness, uint8_t defaultBrightness = 0) + { + if (str == nullptr) + { + return; + } + + const char* pFirst = str; + const char* pIter = str; + + // digits are right to left + // so find the end + while (*pIter != '\0') + { + pIter++; + } + pIter--; + + + while (pIter >= pFirst) + { + bool decimal = false; + char value = *pIter; + + // check if merging a decimal is required + if (pIter > pFirst && (*pIter == '.' || *pIter == ',')) + { + // merge a decimal as long as they aren't the same + if (*(pIter - 1) != *pIter) + { + decimal = true; + pIter--; + value = *pIter; // use the next char + } + } + + SevenSegDigit digit(value, brightness, defaultBrightness); + if (decimal) + { + digit.Segment[LedSegment_Decimal] = brightness; + } + target.SetPixelColor(indexDigit, digit); + indexDigit++; + } + } + + // ------------------------------------------------------------------------ + // segment members (0-255) where each represents the segment location + // and the value defines the brightnes (0) is off and (255) is full brightness + // ------------------------------------------------------------------------ + static const uint8_t SegmentCount = 9; + uint8_t Segment[SegmentCount]; + + + // segment decode maps from ascii relative first char in map to a bitmask of segments + // + static const uint8_t DecodeNumbers[10]; // 0-9 + static const uint8_t DecodeAlphaCaps[26]; // A-Z + static const uint8_t DecodeAlpha[26]; // a-z + static const uint8_t DecodeSpecial[4]; // , - . / + +protected: + void init(uint8_t bitmask, uint8_t brightness, uint8_t defaultBrightness); +}; +