diff --git a/ESPixelStick/src/ConstNames.cpp b/ESPixelStick/src/ConstNames.cpp index 54df96e4d..f20d3f9e5 100644 --- a/ESPixelStick/src/ConstNames.cpp +++ b/ESPixelStick/src/ConstNames.cpp @@ -38,6 +38,7 @@ const CN_PROGMEM char CN_baudrate [] = "baudrate"; const CN_PROGMEM char CN_blanktime [] = "blanktime"; const CN_PROGMEM char CN_bridge [] = "bridge"; const CN_PROGMEM char CN_brightness [] = "brightness"; +const CN_PROGMEM char CN_brightnessEnd [] = "brightnessEnd"; const CN_PROGMEM char CN_cfgver [] = "cfgver"; const CN_PROGMEM char CN_channels [] = "channels"; const CN_PROGMEM char CN_clean [] = "clean"; @@ -116,6 +117,7 @@ const CN_PROGMEM char CN_input_config [] = "input_config"; const CN_PROGMEM char CN_last_clientIP [] = "last_clientIP"; const CN_PROGMEM char CN_lwt [] = "lwt"; const CN_PROGMEM char CN_mac [] = "mac"; +const CN_PROGMEM char CN_MarqueeGroups [] = "MarqueeGroups"; const CN_PROGMEM char CN_mdc_pin [] = "mdc_pin"; const CN_PROGMEM char CN_mdio_pin [] = "mdio_pin"; const CN_PROGMEM char CN_Max [] = "Max"; diff --git a/ESPixelStick/src/ConstNames.hpp b/ESPixelStick/src/ConstNames.hpp index 6905d0abe..c6fc7cc0d 100644 --- a/ESPixelStick/src/ConstNames.hpp +++ b/ESPixelStick/src/ConstNames.hpp @@ -47,6 +47,7 @@ extern const CN_PROGMEM char CN_baudrate[]; extern const CN_PROGMEM char CN_blanktime[]; extern const CN_PROGMEM char CN_bridge[]; extern const CN_PROGMEM char CN_brightness[]; +extern const CN_PROGMEM char CN_brightnessEnd[]; extern const CN_PROGMEM char CN_cfgver[]; extern const CN_PROGMEM char CN_channels[]; extern const CN_PROGMEM char CN_clean[]; @@ -125,6 +126,7 @@ extern const CN_PROGMEM char CN_input_config[]; extern const CN_PROGMEM char CN_last_clientIP[]; extern const CN_PROGMEM char CN_lwt[]; extern const CN_PROGMEM char CN_mac[]; +extern const CN_PROGMEM char CN_MarqueeGroups[]; extern const CN_PROGMEM char CN_mdc_pin[]; extern const CN_PROGMEM char CN_mdio_pin[]; extern const CN_PROGMEM char CN_Max[]; diff --git a/ESPixelStick/src/ESPixelStick.h b/ESPixelStick/src/ESPixelStick.h index ccb1d378f..067834eeb 100644 --- a/ESPixelStick/src/ESPixelStick.h +++ b/ESPixelStick/src/ESPixelStick.h @@ -33,6 +33,7 @@ #endif #define ARDUINOJSON_USE_LONG_LONG 1 +#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 15 #include #include @@ -40,6 +41,7 @@ #include "memdebug.h" #include "ConstNames.hpp" #include "GPIO_Defs.hpp" +#include "FastTimer.hpp" #define REBOOT_DELAY 100 ///< Delay for rebooting once reboot flag is set #define LOG_PORT Serial ///< Serial port for console logging diff --git a/ESPixelStick/src/FastTimer.cpp b/ESPixelStick/src/FastTimer.cpp new file mode 100644 index 000000000..efbac248d --- /dev/null +++ b/ESPixelStick/src/FastTimer.cpp @@ -0,0 +1,74 @@ +/* +* FastTimer.cpp - Output Management class +* +* Project: ESPixelStick - An ESP8266 / ESP32 and E1.31 based pixel driver +* Copyright (c) 2021, 2022 Shelby Merrick +* http://www.forkineye.com +* +* This program is provided free for you to use in any way that you wish, +* subject to the laws and regulations where you are using it. Due diligence +* is strongly suggested before using this code. Please give credit where due. +* +* The Author makes no warranty of any kind, express or implied, with regard +* to this program or the documentation contained in this document. The +* Author shall not be liable in any event for incidental or consequential +* damages in connection with, or arising out of, the furnishing, performance +* or use of these programs. +* +*/ + +#include "FastTimer.hpp" + +//----------------------------------------------------------------------------- +///< Start up the driver and put it into a safe mode +FastTimer::FastTimer () +{ + CancelTimer(); +} // FastTimer + +//----------------------------------------------------------------------------- +///< deallocate any resources and put the output channels into a safe state +FastTimer::~FastTimer () +{ + // DEBUG_START; + + // DEBUG_END; + +} // ~FastTimer + +//----------------------------------------------------------------------------- +///< Start the module +void FastTimer::StartTimer (uint32_t DurationMS) +{ + // DEBUG_START; + + uint64_t now = uint64_t(millis()); + + EndTimeMS = now + uint64_t(DurationMS); + offsetMS = uint64_t((EndTimeMS > uint32_t(-1)) ? uint32_t(-1) : uint32_t(0)); + + // DEBUG_END; +} // StartTimer + +//----------------------------------------------------------------------------- +bool FastTimer::IsExpired () +{ + return ((uint64_t(millis ()) + uint64_t(offsetMS)) >= EndTimeMS); + +} // IsExpired + +//----------------------------------------------------------------------------- +void FastTimer::CancelTimer() +{ + EndTimeMS = millis(); + offsetMS = 0; + +} // CancelTimer + +//----------------------------------------------------------------------------- +uint32_t FastTimer::GetTimeRemaining() +{ + + return (IsExpired()) ? 0 : uint32_t(EndTimeMS - (uint64_t(millis()) + uint64_t(offsetMS))); + +} // GetTimeRemaining diff --git a/ESPixelStick/src/FastTimer.hpp b/ESPixelStick/src/FastTimer.hpp new file mode 100644 index 000000000..7f108acbe --- /dev/null +++ b/ESPixelStick/src/FastTimer.hpp @@ -0,0 +1,41 @@ +#pragma once +/* +* FastTimer.hpp +* +* Project: ESPixelStick - An ESP8266 / ESP32 and E1.31 based pixel driver +* Copyright (c) 2021, 2022 Shelby Merrick +* http://www.forkineye.com +* +* This program is provided free for you to use in any way that you wish, +* subject to the laws and regulations where you are using it. Due diligence +* is strongly suggested before using this code. Please give credit where due. +* +* The Author makes no warranty of any kind, express or implied, with regard +* to this program or the documentation contained in this document. The +* Author shall not be liable in any event for incidental or consequential +* damages in connection with, or arising out of, the furnishing, performance +* or use of these programs. +* +*/ + +#include "ESPixelStick.h" + +class FastTimer +{ +public: + FastTimer (); + virtual ~FastTimer (); + + void StartTimer (uint32_t durationMS); + bool IsExpired(); + void CancelTimer(); + uint32_t GetTimeRemaining(); + +private: + + uint64_t EndTimeMS = 0; + uint32_t offsetMS = 0; + +protected: + +}; // FastTimer diff --git a/ESPixelStick/src/input/InputEffectEngine.cpp b/ESPixelStick/src/input/InputEffectEngine.cpp index 3efdabe1b..d4a59c35a 100644 --- a/ESPixelStick/src/input/InputEffectEngine.cpp +++ b/ESPixelStick/src/input/InputEffectEngine.cpp @@ -40,7 +40,8 @@ static const c_InputEffectEngine::EffectDescriptor_t ListOfEffects[] = { "Lightning", &c_InputEffectEngine::effectLightning, "t_lightning", 1, 0, 0, 0, "T7" }, { "Breathe", &c_InputEffectEngine::effectBreathe, "t_breathe", 1, 0, 0, 0, "T8" }, { "Random", &c_InputEffectEngine::effectRandom, "t_random", 0, 0, 0, 0, "T9" }, - { "Transition", &c_InputEffectEngine::effectTransition, "t_Transition", 0, 0, 0, 0, "T10" } + { "Transition", &c_InputEffectEngine::effectTransition, "t_Transition", 0, 0, 0, 0, "T10" }, + { "Marquee", &c_InputEffectEngine::effectMarquee, "t_Marquee", 0, 0, 0, 0, "T11" } }; static std::vector TransitionColorTable = @@ -69,6 +70,12 @@ static std::vector TransitionColorTable = {100, 100, 55}, }; +static std::vector MarqueueGroupTable = +{ + {5, {255, 0, 0}, 100, 100}, + {5, {255, 255, 255}, 100, 0}, +}; // MarqueueGroupTable + //----------------------------------------------------------------------------- c_InputEffectEngine::c_InputEffectEngine (c_InputMgr::e_InputChannelIds NewInputChannelId, c_InputMgr::e_InputType NewChannelType, @@ -142,6 +149,16 @@ void c_InputEffectEngine::GetConfig (JsonObject& jsonConfig) jsonConfig[CN_EffectBrightness] = uint32_t(EffectBrightness * 100.0); jsonConfig[CN_EffectWhiteChannel] = EffectWhiteChannel; jsonConfig[CN_EffectColor] = HexColor; + jsonConfig[CN_pixel_count] = effectMarqueePixelAdvanceCount; + + jsonConfig["FlashEnable"] = FlashInfo.Enable; + jsonConfig["FlashMinInt"] = FlashInfo.MinIntensity; + jsonConfig["FlashMaxInt"] = FlashInfo.MaxIntensity; + jsonConfig["FlashMinDelay"] = FlashInfo.MinDelayMS; + jsonConfig["FlashMaxDelay"] = FlashInfo.MaxDelayMS; + jsonConfig["FlashMinDur"] = FlashInfo.MinDurationMS; + jsonConfig["FlashMaxDur"] = FlashInfo.MaxDurationMS; + // DEBUG_V (""); JsonArray EffectsArray = jsonConfig.createNestedArray (CN_effects); @@ -164,6 +181,19 @@ void c_InputEffectEngine::GetConfig (JsonObject& jsonConfig) currentJsonEntry["b"] = currentTransition.b; } + JsonArray MarqueeGroupArray = jsonConfig.createNestedArray (CN_MarqueeGroups); + for(auto CurrentMarqueeGroup : MarqueueGroupTable) + { + JsonObject currentJsonEntry = MarqueeGroupArray.createNestedObject (); + JsonObject currentJsonEntryColor = currentJsonEntry.createNestedObject (CN_color); + currentJsonEntryColor["r"] = CurrentMarqueeGroup.Color.r; + currentJsonEntryColor["g"] = CurrentMarqueeGroup.Color.g; + currentJsonEntryColor["b"] = CurrentMarqueeGroup.Color.b; + currentJsonEntry[CN_brightness] = CurrentMarqueeGroup.StartingIntensity; + currentJsonEntry[CN_pixel_count] = CurrentMarqueeGroup.NumPixelsInGroup; + currentJsonEntry[CN_brightnessEnd] = CurrentMarqueeGroup.EndingIntensity; + } + // DEBUG_END; } // GetConfig @@ -248,6 +278,53 @@ void c_InputEffectEngine::NextEffect () // DEBUG_END; } // NextEffect +//----------------------------------------------------------------------------- +void c_InputEffectEngine::PollFlash () +{ + do // once + { + if(!FlashInfo.Enable) + { + // not doing random flashing + break; + } + + if(!FlashInfo.delaytimer.IsExpired()) + { + // not time to flash yet + break; + } + + // is the flash done? + if(FlashInfo.durationtimer.IsExpired()) + { + // set up the next flash + uint32_t NextDelay = random(FlashInfo.MinDelayMS, FlashInfo.MaxDelayMS); + uint32_t NextDuration = random(FlashInfo.MinDurationMS, FlashInfo.MaxDurationMS); + + FlashInfo.delaytimer.StartTimer(NextDelay); + FlashInfo.durationtimer.StartTimer(NextDelay + NextDuration); + + // force the effect to overwrite the buffer + EffectDelayTimer.CancelTimer(); + // DEBUG_V(String(" now: ") + String(now)); + // DEBUG_V(String(" NextDelay: ") + String(NextDelay)); + // DEBUG_V(String("NextDuration: ") + String(NextDuration)); + + // dont overwrite the buffer + break; + } + + uint8_t intensity = uint8_t(map(random( FlashInfo.MinIntensity, FlashInfo.MaxIntensity),0,100,0,255)); + CRGB color; + color.r = intensity; + color.g = intensity; + color.b = intensity; + setAll(color); + } while(false); + +} // PollFlash + //----------------------------------------------------------------------------- void c_InputEffectEngine::Process () { @@ -270,13 +347,13 @@ void c_InputEffectEngine::Process () } // DEBUG_V ("Pixel Count OK"); - if (millis () < (EffectLastRun + EffectWait)) + if(!EffectDelayTimer.IsExpired()) { break; } // DEBUG_V ("Update output"); - EffectLastRun = millis (); + EffectDelayTimer.StartTimer(EffectWait); uint32_t wait = (this->*ActiveEffect->func)(); EffectWait = max ((int)wait, MIN_EFFECT_DELAY); EffectCounter++; @@ -284,6 +361,8 @@ void c_InputEffectEngine::Process () } while (false); + PollFlash(); + // DEBUG_END; } // process @@ -327,6 +406,15 @@ bool c_InputEffectEngine::SetConfig (ArduinoJson::JsonObject& jsonConfig) setFromJSON (effectName, jsonConfig, CN_currenteffect); setFromJSON (effectColor, jsonConfig, CN_EffectColor); // DEBUG_V (String ("effectColor: ") + effectColor); + setFromJSON (effectMarqueePixelAdvanceCount, jsonConfig, CN_pixel_count); + + setFromJSON (FlashInfo.Enable, jsonConfig, "FlashEnable"); + setFromJSON (FlashInfo.MinIntensity, jsonConfig, "FlashMinInt"); + setFromJSON (FlashInfo.MaxIntensity, jsonConfig, "FlashMaxInt"); + setFromJSON (FlashInfo.MinDelayMS, jsonConfig, "FlashMinDelay"); + setFromJSON (FlashInfo.MaxDelayMS, jsonConfig, "FlashMaxDelay"); + setFromJSON (FlashInfo.MinDurationMS, jsonConfig, "FlashMinDur"); + setFromJSON (FlashInfo.MaxDurationMS, jsonConfig, "FlashMaxDur"); if(jsonConfig.containsKey(CN_transitions)) { @@ -348,6 +436,31 @@ bool c_InputEffectEngine::SetConfig (ArduinoJson::JsonObject& jsonConfig) } } + if(jsonConfig.containsKey(CN_MarqueeGroups)) + { + MarqueueGroupTable.clear(); + + JsonArray MarqueeGroupArray = jsonConfig[CN_MarqueeGroups]; + for (auto currentMarqueeGroup : MarqueeGroupArray) + { + MarqueeGroup NewGroup; + // DEBUG_V (""); + JsonObject GroupColor = currentMarqueeGroup[CN_color]; + setFromJSON (NewGroup.Color.r, GroupColor, "r"); + setFromJSON (NewGroup.Color.g, GroupColor, "g"); + setFromJSON (NewGroup.Color.b, GroupColor, "b"); + // DEBUG_V (String("NewGroup.Color.r: ") + String(NewGroup.Color.r)); + // DEBUG_V (String("NewGroup.Color.g: ") + String(NewGroup.Color.g)); + // DEBUG_V (String("NewGroup.Color.b: ") + String(NewGroup.Color.b)); + + setFromJSON (NewGroup.NumPixelsInGroup, currentMarqueeGroup, CN_pixel_count); + setFromJSON (NewGroup.StartingIntensity, currentMarqueeGroup, CN_brightness); + setFromJSON (NewGroup.EndingIntensity, currentMarqueeGroup, CN_brightnessEnd); + + MarqueueGroupTable.push_back(NewGroup); + } + } + EffectBrightness /= 100.0; SetBufferInfo (InputDataBufferSize); @@ -455,7 +568,7 @@ void c_InputEffectEngine::setEffect (const String & effectName) { // DEBUG_V ("Starting Effect"); ActiveEffect = &ListOfEffects[EffectIndex]; - EffectLastRun = millis (); + EffectDelayTimer.StartTimer(EffectDelay); EffectWait = MIN_EFFECT_DELAY; EffectCounter = 0; EffectStep = 0; @@ -819,6 +932,99 @@ uint16_t c_InputEffectEngine::effectTransition () // return 1; } // effectTransition +//----------------------------------------------------------------------------- +uint16_t c_InputEffectEngine::effectMarquee () +{ + // DEBUG_START; + /* + Chase groups of pixels + Each group specifies a color and a number of pixels in the group + seperate number of pixels to advance for each iteration + + + Iterate backwards through the array of pixels. + Output data for each entry in the array of groups + Advance the next output pixel forward + wait + */ + + // DEBUG_V(String("MarqueeTargetColorId: ") + String(MarqueeTargetColorId)); + + uint32_t CurrentMarqueePixelLocation = effectMarqueePixelLocation; + uint32_t NumPixelsToProcess = PixelCount; + do + { + // iterate through the groups until we have processed all of the pixels. + for(auto CurrentGroup : MarqueueGroupTable) + { + uint32_t groupPixelCount = CurrentGroup.NumPixelsInGroup; + double CurrentBrightness = (EffectReverse) ? CurrentGroup.EndingIntensity : CurrentGroup.StartingIntensity; + double BrightnessInterval = (double(CurrentGroup.StartingIntensity) - double(CurrentGroup.EndingIntensity))/double(groupPixelCount); + + // now adjust for 100% = 1 + CurrentBrightness /= 100; + BrightnessInterval /= 100; + + // for each pixel in the group + for(; (0 != groupPixelCount) && (NumPixelsToProcess); --groupPixelCount, --NumPixelsToProcess) + { + CRGB color = CurrentGroup.Color; + color.r = uint8_t(double(color.r) * CurrentBrightness); + color.g = uint8_t(double(color.g) * CurrentBrightness); + color.b = uint8_t(double(color.b) * CurrentBrightness); + + // output the current value + outputEffectColor (CurrentMarqueePixelLocation, color); + + // advance to the next pixel + if(EffectReverse) + { + // set the next brightness + CurrentBrightness += BrightnessInterval; + + ++CurrentMarqueePixelLocation; + if(PixelCount <= CurrentMarqueePixelLocation) + { + // wrap bottom of the buffer + CurrentMarqueePixelLocation = 0; + } + } + else // forward + { + // set the next brightness + CurrentBrightness -= BrightnessInterval; + + if(0 == CurrentMarqueePixelLocation) + { + // wrap one past the top of the buffer + CurrentMarqueePixelLocation = PixelCount; + } + --CurrentMarqueePixelLocation; + } + } + + // did we stop due to pixel exhaustion + if(0 == NumPixelsToProcess) + { + break; + } + } + + } while (NumPixelsToProcess); + + // advance to the next starting location + effectMarqueePixelLocation += effectMarqueePixelAdvanceCount; + if(effectMarqueePixelLocation >= PixelCount) + { + // wrap around + effectMarqueePixelLocation-= PixelCount; + } + + // DEBUG_END; + return (EffectDelay / 10); +// return 1; +} // effectTransition + //----------------------------------------------------------------------------- void c_InputEffectEngine::CalculateTransitionStepValue(double tc, double cc, double & step) { @@ -1085,7 +1291,7 @@ uint16_t c_InputEffectEngine::effectBreathe () c_InputEffectEngine::dCHSV c_InputEffectEngine::rgb2hsv (CRGB in_int) { dCHSV out; - dCRGB in = { in_int.r / 255.0d, in_int.g / 255.0d, in_int.b / 255.0d }; + dCRGB in = { double(in_int.r) / double(255.0), double(in_int.g) / double(255.0), double(in_int.b) / double(255.0) }; double min, max, delta; min = in.r < in.g ? in.r : in.g; diff --git a/ESPixelStick/src/input/InputEffectEngine.hpp b/ESPixelStick/src/input/InputEffectEngine.hpp index ea99980b7..5d1a4beab 100644 --- a/ESPixelStick/src/input/InputEffectEngine.hpp +++ b/ESPixelStick/src/input/InputEffectEngine.hpp @@ -86,6 +86,14 @@ class c_InputEffectEngine : public c_InputCommon CRGB color; } MQTTConfiguration_s; + struct MarqueeGroup + { + uint32_t NumPixelsInGroup; + CRGB Color; + uint8_t StartingIntensity; + uint8_t EndingIntensity; + }; + // functions to be provided by the derived class void Begin (); ///< set up the operating environment based on the current config (or defaults) bool SetConfig (JsonObject& jsonConfig); ///< Set a new config in the driver @@ -111,6 +119,7 @@ class c_InputEffectEngine : public c_InputCommon uint16_t effectNull (); uint16_t effectRandom (); uint16_t effectTransition (); + uint16_t effectMarquee (); private: @@ -124,8 +133,9 @@ class c_InputEffectEngine : public c_InputCommon using timeType = decltype(millis()); + uint32_t EffectWait = 32; /* How long to wait for the effect to run again */ - timeType EffectLastRun = 0; /* When did the effect last run ? in millis() */ + uint32_t EffectCounter = 0; /* Counter for the number of calls to the active effect */ uint16_t EffectSpeed = 6; /* Externally controlled effect speed 1..10 */ uint16_t EffectDelay = DEFAULT_EFFECT_DELAY; /* Internal representation of speed */ @@ -136,11 +146,15 @@ class c_InputEffectEngine : public c_InputCommon float EffectBrightness = 1.0; /* Externally controlled effect brightness [0, 255] */ CRGB EffectColor = { 183, 0, 255 }; /* Externally controlled effect color */ + uint32_t effectMarqueePixelAdvanceCount = 1; + uint32_t effectMarqueePixelLocation = 0; + uint32_t EffectStep = 0; /* Shared mutable effect step counter */ uint32_t PixelCount = 0; /* Number of RGB leds (not channels) */ uint32_t MirroredPixelCount = 0; /* Number of RGB leds (not channels) */ uint8_t ChannelsPerPixel = 3; uint32_t PixelOffset = 0; + FastTimer EffectDelayTimer; void setPixel(uint16_t idx, CRGB color); void GetPixel (uint16_t pixelId, CRGB & out); @@ -158,6 +172,7 @@ class c_InputEffectEngine : public c_InputCommon void setBrightness (float brightness); void setSpeed (uint16_t speed); void setDelay (uint16_t delay); + void PollFlash(); void clearAll (); @@ -171,4 +186,17 @@ class c_InputEffectEngine : public c_InputCommon bool ColorHasReachedTarget (double tc, double cc, double step); void ConditionalIncrementColor(double tc, double & cc, double step); void CalculateTransitionStepValue(double tc, double cc, double & step); + + struct FlashInfo_t + { + bool Enable = false; + uint32_t MinIntensity = 100; + uint32_t MaxIntensity = 100; + uint32_t MinDelayMS = 100; + uint32_t MaxDelayMS = 5000; + uint32_t MinDurationMS = 25; + uint32_t MaxDurationMS = 50; + FastTimer delaytimer; + FastTimer durationtimer; + } FlashInfo; }; diff --git a/ESPixelStick/src/input/InputFPPRemotePlayEffect.hpp b/ESPixelStick/src/input/InputFPPRemotePlayEffect.hpp index 91eee98aa..b7404b692 100644 --- a/ESPixelStick/src/input/InputFPPRemotePlayEffect.hpp +++ b/ESPixelStick/src/input/InputFPPRemotePlayEffect.hpp @@ -47,8 +47,8 @@ class c_InputFPPRemotePlayEffect : public c_InputFPPRemotePlayItem fsm_PlayEffect_state_PlayingEffect fsm_PlayEffect_state_PlayingEffect_imp; fsm_PlayEffect_state* pCurrentFsmState = nullptr; - time_t PlayEffectEndTime = 0; - + FastTimer PlayEffectTimer; + c_InputEffectEngine EffectsEngine; }; // c_InputFPPRemotePlayEffect diff --git a/ESPixelStick/src/input/InputFPPRemotePlayEffectFsm.cpp b/ESPixelStick/src/input/InputFPPRemotePlayEffectFsm.cpp index f38e96b6a..bc2d42a88 100644 --- a/ESPixelStick/src/input/InputFPPRemotePlayEffectFsm.cpp +++ b/ESPixelStick/src/input/InputFPPRemotePlayEffectFsm.cpp @@ -51,7 +51,7 @@ void fsm_PlayEffect_state_Idle::Start (String & ConfigString, float ) // DEBUG_START; // DEBUG_V (String ("ConfigString: '") + ConfigString + "'"); - p_InputFPPRemotePlayEffect->PlayEffectEndTime = millis () + (1000 * p_InputFPPRemotePlayEffect->PlayDurationSec); + p_InputFPPRemotePlayEffect->PlayEffectTimer.StartTimer(1000 * p_InputFPPRemotePlayEffect->PlayDurationSec); // tell the effect engine what it is supposed to be doing DynamicJsonDocument EffectConfig (512); @@ -124,7 +124,7 @@ void fsm_PlayEffect_state_PlayingEffect::Poll () p_InputFPPRemotePlayEffect->EffectsEngine.SetBufferInfo (OutputMgr.GetBufferUsedSize()); p_InputFPPRemotePlayEffect->EffectsEngine.Process (); - if (p_InputFPPRemotePlayEffect->PlayEffectEndTime <= millis ()) + if (p_InputFPPRemotePlayEffect->PlayEffectTimer.IsExpired()) { // DEBUG_V (""); Stop (); @@ -192,12 +192,7 @@ void fsm_PlayEffect_state_PlayingEffect::GetStatus (JsonObject& jsonStatus) { // DEBUG_START; - time_t now = millis (); - time_t SecondsRemaining = (p_InputFPPRemotePlayEffect->PlayEffectEndTime - now) / 1000; - if (now > p_InputFPPRemotePlayEffect->PlayEffectEndTime) - { - SecondsRemaining = 0; - } + time_t SecondsRemaining = (p_InputFPPRemotePlayEffect->PlayEffectTimer.GetTimeRemaining()) / 1000; char buf[12]; ESP_ERROR_CHECK(saferSecondsToFormattedMinutesAndSecondsString(buf, (uint32_t)SecondsRemaining)); diff --git a/ESPixelStick/src/input/InputFPPRemotePlayList.cpp b/ESPixelStick/src/input/InputFPPRemotePlayList.cpp index 0239f71c5..fe70bb794 100644 --- a/ESPixelStick/src/input/InputFPPRemotePlayList.cpp +++ b/ESPixelStick/src/input/InputFPPRemotePlayList.cpp @@ -118,7 +118,7 @@ bool c_InputFPPRemotePlayList::ProcessPlayListEntry () // DEBUG_V (""); uint32_t FrameId = 0; uint32_t PlayCount = 1; - PauseEndTime = millis () + 10000; + PauseDelayTimer.StartTimer(10000); // Get the playlist file String FileData; @@ -126,7 +126,7 @@ bool c_InputFPPRemotePlayList::ProcessPlayListEntry () { logcon (String (F ("Could not read Playlist file: '")) + PlayItemName + "'"); fsm_PlayList_state_Paused_imp.Init (this); - pCurrentFsmState->Start (PlayItemName, PauseEndTime, 1); + pCurrentFsmState->Start (PlayItemName, PauseDelayTimer.GetTimeRemaining() / 1000, 1); break; } // DEBUG_V (""); @@ -141,7 +141,7 @@ bool c_InputFPPRemotePlayList::ProcessPlayListEntry () logcon (CfgFileMessagePrefix + String (F ("Deserialzation Error. Error code = ")) + error.c_str ()); logcon (String (F ("++++")) + FileData + String (F ("----"))); fsm_PlayList_state_Paused_imp.Init (this); - pCurrentFsmState->Start (PlayItemName, PauseEndTime, PlayCount); + pCurrentFsmState->Start (PlayItemName, PauseDelayTimer.GetTimeRemaining() / 1000, PlayCount); break; } @@ -165,9 +165,9 @@ bool c_InputFPPRemotePlayList::ProcessPlayListEntry () { // DEBUG_V ("Entry is empty. Do a Pause"); - PauseEndTime = millis () + 1000; + PauseDelayTimer.StartTimer(1000); fsm_PlayList_state_Paused_imp.Init (this); - pCurrentFsmState->Start (PlayItemName, PauseEndTime, PlayCount); + pCurrentFsmState->Start (PlayItemName, PauseDelayTimer.GetTimeRemaining() / 1000, PlayCount); break; } @@ -209,7 +209,7 @@ bool c_InputFPPRemotePlayList::ProcessPlayListEntry () uint32_t PlayListEntryDuration = 0; setFromJSON (PlayListEntryDuration, JsonPlayListArrayEntry, CN_duration); // DEBUG_V (String ("PlayListEntryDuration: '") + String (PlayListEntryDuration) + "'"); - PauseEndTime = (PlayListEntryDuration * 1000) + millis (); + PauseDelayTimer.StartTimer(PlayListEntryDuration * 1000); // DEBUG_V (String (" PauseEndTime: '") + String (PauseEndTime) + "'"); fsm_PlayList_state_Paused_imp.Init (this); @@ -219,7 +219,7 @@ bool c_InputFPPRemotePlayList::ProcessPlayListEntry () else { logcon (String (F ("Unsupported Play List Entry type: '")) + PlayListEntryType + "'"); - PauseEndTime = millis () + 10000; + PauseDelayTimer.StartTimer(10000); fsm_PlayList_state_Paused_imp.Init (this); pCurrentFsmState->Start (PlayListEntryName, FrameId, PlayCount); break; diff --git a/ESPixelStick/src/input/InputFPPRemotePlayList.hpp b/ESPixelStick/src/input/InputFPPRemotePlayList.hpp index 276b660c7..b260efb7d 100644 --- a/ESPixelStick/src/input/InputFPPRemotePlayList.hpp +++ b/ESPixelStick/src/input/InputFPPRemotePlayList.hpp @@ -77,7 +77,7 @@ class c_InputFPPRemotePlayList : public c_InputFPPRemotePlayItem // ensures this for all practical purposes. static_assert( (((time_t)1) / 2) == 0 ); // Verify time_t is an integer type (alternative: float) - time_t PauseEndTime = 0; + FastTimer PauseDelayTimer; uint32_t PlayListRepeatCount = 1; bool ProcessPlayListEntry (); diff --git a/ESPixelStick/src/input/InputFPPRemotePlayListFsm.cpp b/ESPixelStick/src/input/InputFPPRemotePlayListFsm.cpp index 7cc883989..91ffedc2e 100644 --- a/ESPixelStick/src/input/InputFPPRemotePlayListFsm.cpp +++ b/ESPixelStick/src/input/InputFPPRemotePlayListFsm.cpp @@ -309,7 +309,7 @@ void fsm_PlayList_state_Paused::Poll () { // DEBUG_START; - if (pInputFPPRemotePlayList->PauseEndTime <= millis ()) + if (pInputFPPRemotePlayList->PauseDelayTimer.IsExpired()) { Stop(); } @@ -359,13 +359,7 @@ void fsm_PlayList_state_Paused::GetStatus (JsonObject& jsonStatus) JsonObject PauseStatus = jsonStatus.createNestedObject (CN_Paused); - time_t now = millis (); - - time_t SecondsRemaining = (pInputFPPRemotePlayList->PauseEndTime - now) / 1000u; - if (now > pInputFPPRemotePlayList->PauseEndTime) - { - SecondsRemaining = 0; - } + time_t SecondsRemaining = pInputFPPRemotePlayList->PauseDelayTimer.GetTimeRemaining() / 1000u; char buf[12]; // BUGBUG -- casting time_t to integer types is not portable code (can be real ... e.g., float) diff --git a/ESPixelStick/src/input/InputMgr.hpp b/ESPixelStick/src/input/InputMgr.hpp index 86cfe578c..e67765973 100644 --- a/ESPixelStick/src/input/InputMgr.hpp +++ b/ESPixelStick/src/input/InputMgr.hpp @@ -60,8 +60,8 @@ class c_InputMgr void DeleteConfig () { FileMgr.DeleteConfigFile (ConfigFileName); } bool GetNetworkState () { return IsConnected; } void GetDriverName (String & Name) { Name = "InputMgr"; } - void RestartBlankTimer (c_InputMgr::e_InputChannelIds Selector) { BlankEndTime[int(Selector)] = (millis () / 1000) + config.BlankDelay; } - bool BlankTimerHasExpired (c_InputMgr::e_InputChannelIds Selector) { return !(BlankEndTime[int(Selector)] > (millis () / 1000)); } + void RestartBlankTimer (c_InputMgr::e_InputChannelIds Selector) { BlankEndTime[int(Selector)].StartTimer(config.BlankDelay * 1000); } + bool BlankTimerHasExpired (c_InputMgr::e_InputChannelIds Selector) { return (BlankEndTime[int(Selector)].IsExpired()); } enum e_InputType { @@ -91,7 +91,7 @@ class c_InputMgr }; DriverInfo_t InputChannelDrivers[InputChannelId_End]; ///< pointer(s) to the current active Input driver - uint32_t InputDataBufferSize = 0; + uint32_t InputDataBufferSize = 0; bool HasBeenInitialized = false; c_ExternalInput ExternalInput; bool EffectEngineIsConfiguredToRun[InputChannelId_End]; @@ -111,7 +111,7 @@ class c_InputMgr String ConfigFileName; bool rebootNeeded = false; - time_t BlankEndTime[InputChannelId_End]; + FastTimer BlankEndTime[InputChannelId_End]; #define IM_JSON_SIZE (5 * 1024) diff --git a/ESPixelStick/src/input/externalInput.cpp b/ESPixelStick/src/input/externalInput.cpp index ba079b0e3..93db3d99e 100644 --- a/ESPixelStick/src/input/externalInput.cpp +++ b/ESPixelStick/src/input/externalInput.cpp @@ -270,7 +270,7 @@ void fsm_ExternalInput_on_wait_short_state::Init(c_ExternalInput& pExternalInput { // DEBUG_START; - pExternalInput.m_iInputHoldTimeMS = millis() + INPUT_SHORT_VALUE_MS; + pExternalInput.m_InputHoldTimer.StartTimer(INPUT_SHORT_VALUE_MS); pExternalInput.m_CurrentFsmState = fsm_ExternalInput_on_wait_short_state_imp; // DEBUG_V ("Entring Wait Short State"); @@ -289,7 +289,7 @@ void fsm_ExternalInput_on_wait_short_state::Poll(c_ExternalInput& pExternalInput { // DEBUG_V(""); // decrement the counter - if (millis() >= pExternalInput.m_iInputHoldTimeMS) + if (pExternalInput.m_InputHoldTimer.IsExpired()) { // we really are on fsm_ExternalInput_on_wait_long_state_imp.Init(pExternalInput); @@ -309,7 +309,7 @@ void fsm_ExternalInput_on_wait_short_state::Poll(c_ExternalInput& pExternalInput // Input is always on void fsm_ExternalInput_on_wait_long_state::Init (c_ExternalInput& pExternalInput) { - pExternalInput.m_iInputHoldTimeMS = millis () + INPUT_LONG_VALUE_MS; + pExternalInput.m_InputHoldTimer.StartTimer(INPUT_LONG_VALUE_MS); pExternalInput.m_CurrentFsmState = fsm_ExternalInput_on_wait_long_state_imp; // DEBUG_V ("Entring Wait Long State"); @@ -329,7 +329,7 @@ void fsm_ExternalInput_on_wait_long_state::Poll (c_ExternalInput& pExternalInput { // DEBUG_V(""); // decrement the counter - if (millis () >= pExternalInput.m_iInputHoldTimeMS) + if (pExternalInput.m_InputHoldTimer.IsExpired()) { // we really are on fsm_ExternalInput_wait_for_off_state_imp.Init (pExternalInput); diff --git a/ESPixelStick/src/input/externalInput.h b/ESPixelStick/src/input/externalInput.h index 20b27c575..c4d90407c 100644 --- a/ESPixelStick/src/input/externalInput.h +++ b/ESPixelStick/src/input/externalInput.h @@ -54,7 +54,7 @@ class c_ExternalInput final time_t m_ExpirationTime = 0; bool m_bIsEnabled = false; uint32_t m_iInputDebounceCount = 0; - uint32_t m_iInputHoldTimeMS = 0; + FastTimer m_InputHoldTimer; bool m_bHadLongPush = false; bool m_bHadShortPush = false; fsm_ExternalInput_state& m_CurrentFsmState; // initialized in constructor diff --git a/ESPixelStick/src/network/EthernetDriver.cpp b/ESPixelStick/src/network/EthernetDriver.cpp index 5f9a881bc..abe392ca2 100644 --- a/ESPixelStick/src/network/EthernetDriver.cpp +++ b/ESPixelStick/src/network/EthernetDriver.cpp @@ -95,7 +95,7 @@ void c_EthernetDriver::Begin () WiFi.onEvent ([this](WiFiEvent_t event, arduino_event_info_t info) {this->onEventHandler (event, info); }); // set up the poll interval - NextPollTime = millis () + PollInterval; + NextPollTimer.StartTimer(PollInterval); // DEBUG_END; @@ -279,10 +279,10 @@ void c_EthernetDriver::Poll () { // DEBUG_START; - if (millis () > NextPollTime) + if (NextPollTimer.IsExpired()) { // DEBUG_V ("Polling"); - NextPollTime += PollInterval; + NextPollTimer.StartTimer(PollInterval); pCurrentFsmState->Poll (); } @@ -451,7 +451,7 @@ void fsm_Eth_state_Boot::Init () pEthernetDriver->SetFsmState (this); // pEthernetDriver->AnnounceState (); - pEthernetDriver->SetFsmStartTime (millis ()); + pEthernetDriver->GetFsmTimer().StartTimer(10000); // DEBUG_V(String("pEthernetDriver: 0x") + String(uint32_t(pEthernetDriver), HEX)); @@ -465,11 +465,9 @@ void fsm_Eth_state_Boot::Poll () { // DEBUG_START; - uint32_t CurrentTimeMS = millis (); - // DEBUG_V(String("pEthernetDriver: 0x") + String(uint32_t(pEthernetDriver), HEX)); - if (CurrentTimeMS - pEthernetDriver->GetFsmStartTime() > (10000)) + if (pEthernetDriver->GetFsmTimer().IsExpired()) { // DEBUG_V("Start trying to connect"); fsm_Eth_state_PoweringUp_imp.Init(); @@ -486,7 +484,7 @@ void fsm_Eth_state_PoweringUp::Init () pEthernetDriver->SetFsmState (this); pEthernetDriver->AnnounceState (); - pEthernetDriver->SetFsmStartTime (millis ()); + pEthernetDriver->GetFsmTimer().StartTimer (pEthernetDriver->GetPowerPinActiveDelayMs()); pEthernetDriver->InitPowerPin (); @@ -499,8 +497,7 @@ void fsm_Eth_state_PoweringUp::Poll () { // DEBUG_START; - uint32_t CurrentTimeMS = millis (); - if (CurrentTimeMS - pEthernetDriver->GetFsmStartTime () > (pEthernetDriver->GetPowerPinActiveDelayMs())) + if (pEthernetDriver->GetFsmTimer ().IsExpired()) { // Start trying to connect to based on input config fsm_Eth_state_ConnectingToEth_imp.Init (); @@ -520,7 +517,7 @@ void fsm_Eth_state_ConnectingToEth::Init () pEthernetDriver->SetFsmState (this); pEthernetDriver->AnnounceState (); - pEthernetDriver->SetFsmStartTime (millis ()); + pEthernetDriver->GetFsmTimer().StartTimer (5000); // DEBUG_END; @@ -571,7 +568,7 @@ void fsm_Eth_state_WaitForIP::Init () pEthernetDriver->SetFsmState (this); pEthernetDriver->AnnounceState (); - pEthernetDriver->SetFsmStartTime (millis ()); + pEthernetDriver->GetFsmTimer().StartTimer (5000); // DEBUG_END; diff --git a/ESPixelStick/src/network/EthernetDriver.hpp b/ESPixelStick/src/network/EthernetDriver.hpp index 281e50f1a..dd6e35264 100644 --- a/ESPixelStick/src/network/EthernetDriver.hpp +++ b/ESPixelStick/src/network/EthernetDriver.hpp @@ -69,8 +69,7 @@ class c_EthernetDriver bool IsConnected (); inline void SetFsmState (fsm_Eth_state * NewState) { pCurrentFsmState = NewState; } void AnnounceState (); - inline void SetFsmStartTime (uint32_t NewStartTime) { FsmTimerEthStartTime = NewStartTime; } - inline uint32_t GetFsmStartTime (void) { return FsmTimerEthStartTime; } + FastTimer & GetFsmTimer () { return FsmTimer; } void NetworkStateChanged (bool NetworkState); void StartEth (); void InitPowerPin (); @@ -82,7 +81,7 @@ class c_EthernetDriver void onEventHandler (const WiFiEvent_t event, const WiFiEventInfo_t info); - uint32_t NextPollTime = 0; + FastTimer NextPollTimer; uint32_t PollInterval = 1000; bool HasBeenPreviouslyConfigured = false; @@ -107,7 +106,7 @@ class c_EthernetDriver friend class fsm_Eth_state_DeviceInitFailed; friend class fsm_Eth_state; fsm_Eth_state * pCurrentFsmState = nullptr; - uint32_t FsmTimerEthStartTime = 0; + FastTimer FsmTimer; }; // c_EthernetDriver diff --git a/ESPixelStick/src/network/WiFiDriver.cpp b/ESPixelStick/src/network/WiFiDriver.cpp index fea3422e8..1077ac3ab 100644 --- a/ESPixelStick/src/network/WiFiDriver.cpp +++ b/ESPixelStick/src/network/WiFiDriver.cpp @@ -180,7 +180,7 @@ void c_WiFiDriver::Begin () #endif // set up the poll interval - NextPollTime = millis () + PollInterval; + NextPoll.StartTimer(PollInterval); // Main loop should start polling for us // pCurrentFsmState->Poll (); @@ -414,10 +414,10 @@ void c_WiFiDriver::Poll () { // DEBUG_START; - if (millis () > NextPollTime) + if (NextPoll.IsExpired()) { // DEBUG_V ("Start Poll"); - NextPollTime += PollInterval; + NextPoll.StartTimer(PollInterval); // displayFsmState (); pCurrentFsmState->Poll (); // displayFsmState (); @@ -641,11 +641,10 @@ void fsm_WiFi_state_ConnectingUsingConfig::Poll () /// DEBUG_START; // wait for the connection to complete via the callback function - uint32_t CurrentTimeMS = millis (); if (WiFi.status () != WL_CONNECTED) { - if (CurrentTimeMS - pWiFiDriver->GetFsmStartTime() > (1000 * pWiFiDriver->Get_sta_timeout())) + if (pWiFiDriver->GetFsmTimer().IsExpired()) { /// DEBUG_V (String ("this: ") + String (uint32_t (this), HEX)); logcon (F ("WiFi Failed to connect using Configured Credentials")); @@ -674,7 +673,7 @@ void fsm_WiFi_state_ConnectingUsingConfig::Init () { pWiFiDriver->SetFsmState (this); pWiFiDriver->AnnounceState (); - pWiFiDriver->SetFsmStartTime (millis ()); + pWiFiDriver->GetFsmTimer().StartTimer(1000 * pWiFiDriver->Get_sta_timeout()); pWiFiDriver->connectWifi (CurrentSsid, CurrentPassphrase); } @@ -704,13 +703,11 @@ void fsm_WiFi_state_ConnectingUsingDefaults::Poll () /// DEBUG_START; // wait for the connection to complete via the callback function - uint32_t CurrentTimeMS = millis (); - if (WiFi.status () != WL_CONNECTED) { - if (CurrentTimeMS - pWiFiDriver->GetFsmStartTime () > (1000 * pWiFiDriver->Get_sta_timeout ())) + if (pWiFiDriver->GetFsmTimer().IsExpired()) { - /// DEBUG_V (String ("this: ") + String (uint32_t (this), HEX)); + // DEBUG_V (String ("this: ") + String (uint32_t (this), HEX)); logcon (F ("WiFi Failed to connect using default Credentials")); fsm_WiFi_state_ConnectingAsAP_imp.Init (); } @@ -728,7 +725,7 @@ void fsm_WiFi_state_ConnectingUsingDefaults::Init () // DEBUG_V (String ("this: ") + String (uint32_t (this), HEX)); pWiFiDriver->SetFsmState (this); pWiFiDriver->AnnounceState (); - pWiFiDriver->SetFsmStartTime (millis ()); + pWiFiDriver->GetFsmTimer().StartTimer(1000 * pWiFiDriver->Get_sta_timeout ()); pWiFiDriver->connectWifi (default_ssid, default_passphrase); // pWiFiDriver->displayFsmState (); @@ -762,7 +759,7 @@ void fsm_WiFi_state_ConnectingAsAP::Poll () } else { - if (millis () - pWiFiDriver->GetFsmStartTime () > (1000 * pWiFiDriver->Get_ap_timeout ())) + if (pWiFiDriver->GetFsmTimer().IsExpired()) { if( false == pWiFiDriver->Get_ap_StayInApMode()) { @@ -783,6 +780,7 @@ void fsm_WiFi_state_ConnectingAsAP::Init () pWiFiDriver->SetFsmState (this); pWiFiDriver->AnnounceState (); + pWiFiDriver->GetFsmTimer ().StartTimer(1000 * pWiFiDriver->Get_ap_timeout ()); if (true == pWiFiDriver->Get_ap_fallbackIsEnabled() || pWiFiDriver->Get_ap_StayInApMode()) { diff --git a/ESPixelStick/src/network/WiFiDriver.hpp b/ESPixelStick/src/network/WiFiDriver.hpp index 1633ec0b5..653813366 100644 --- a/ESPixelStick/src/network/WiFiDriver.hpp +++ b/ESPixelStick/src/network/WiFiDriver.hpp @@ -69,8 +69,7 @@ class c_WiFiDriver void SetFsmState (fsm_WiFi_state* NewState); void AnnounceState (); - void SetFsmStartTime (uint32_t NewStartTime) { FsmTimerWiFiStartTime = NewStartTime; } - uint32_t GetFsmStartTime (void) { return FsmTimerWiFiStartTime; } + FastTimer &GetFsmTimer (void) { return FsmTimer; } bool IsWiFiConnected () { return ReportedIsWiFiConnected; } void SetIsWiFiConnected (bool value) { ReportedIsWiFiConnected = value; } void GetDriverName (String & Name) { Name = CN_WiFiDrv; } @@ -98,7 +97,7 @@ class c_WiFiDriver // config_t *config = nullptr; // Current configuration IPAddress CurrentIpAddress = IPAddress (0, 0, 0, 0); IPAddress CurrentSubnetMask = IPAddress (0, 0, 0, 0); - uint32_t NextPollTime = 0; + FastTimer NextPoll; uint32_t PollInterval = 1000; bool ReportedIsWiFiConnected = false; @@ -143,7 +142,7 @@ class c_WiFiDriver friend class fsm_WiFi_state_Disabled; friend class fsm_WiFi_state; fsm_WiFi_state * pCurrentFsmState = nullptr; - uint32_t FsmTimerWiFiStartTime = 0; + FastTimer FsmTimer; }; // c_WiFiDriver diff --git a/html/effects.html b/html/effects.html index a815fe7d7..bf4799592 100644 --- a/html/effects.html +++ b/html/effects.html @@ -47,21 +47,91 @@ -
-
- +
+ +
+ + + + + + + + + + + + + + '; + '; + '; + '; + '; + '; + +
EnableMin Intensity %Max Intensity %Min Delay (ms)Max Delay (ms)Min Duration (ms)Max Duration (ms)
+
+ + + + +
+ + Triggered Effect +
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
diff --git a/html/index.html b/html/index.html index 4fbcb2d2a..12e882f08 100644 --- a/html/index.html +++ b/html/index.html @@ -371,7 +371,7 @@
+ pattern="^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$" title="Hostname for this device.">
@@ -413,30 +413,30 @@ -
- +
+
-
-
- +
+
-
-
+
-
@@ -481,7 +481,7 @@
@@ -490,7 +490,7 @@
@@ -499,7 +499,7 @@
@@ -661,7 +661,7 @@ --> - +
diff --git a/html/script.js b/html/script.js index 47e8ee099..c142cb49c 100644 --- a/html/script.js +++ b/html/script.js @@ -40,7 +40,7 @@ wsConnect(); // jQuery doc ready $(function () { // Menu navigation for single page layout - $('ul.navbar-nav li a').click(function () { + $('ul.navbar-nav li a').on("click", (function () { // Highlight proper navbar item $('.nav li').removeClass('active'); $(this).parent().addClass('active'); @@ -94,21 +94,20 @@ $(function () { // _("status").innerHTML = "Upload Aborted"; } }); - }); + })); // DHCP field toggles - $('#wifi #dhcp').change(function () { + $('#wifi #dhcp').on("change", (function () { if ($(this).is(':checked')) { - $('.dhcp').removeClass('hidden'); - $('.dhcp').addClass('hidden'); + $('.wifiDhcp').addClass('hidden'); } else { - $('.dhcp').removeClass('hidden'); + $('.wifiDhcp').removeClass('hidden'); } $('#btn_network').prop("disabled", ValidateConfigFields($("#network #wifi input"))); - }); + })); - $('#eth #dhcp').change(function () { + $('#eth #dhcp').on("change", (function () { if ($(this).is(':checked')) { $('.ethdhcp').addClass('hidden'); } @@ -116,7 +115,7 @@ $(function () { $('.ethdhcp').removeClass('hidden'); } $('#btn_network').prop("disabled", ValidateConfigFields($("#network #wifi input"))); - }); + })); $('#network').on("input", (function () { $('#btn_network').prop("disabled", ValidateConfigFields($("#network #wifi input"))); @@ -126,24 +125,24 @@ $(function () { $('#DeviceConfigSave').prop("disabled", ValidateConfigFields($('#config input'))); })); - $('#DeviceConfigSave').click(function () { + $('#DeviceConfigSave').on("click", (function () { submitDeviceConfig(); - }); + })); - $('#btn_network').click(function () { + $('#btn_network').on("click", (function () { submitNetworkConfig(); - }); + })); - $('#viewStyle').change(function () { + $('#viewStyle').on("change", (function () { clearStream(); - }); + })); $('#v_columns').on('input', function () { clearStream(); }); //TODO: This should pull a configuration from the stick and not the web interface as web data could be invalid - $('#backupconfig').click(function () { + $('#backupconfig').on("click", (function () { ExtractNetworkConfigFromHtmlPage(); ExtractChannelConfigFromHtmlPage(Input_Config.channels, "input"); ExtractChannelConfigFromHtmlPage(Output_Config.channels, "output"); @@ -155,9 +154,9 @@ $(function () { let blob = new Blob([TotalConfig], { type: "text/json;charset=utf-8" }); let FileName = System_Config.device.id.replace(".", "-").replace(" ", "-").replace(",", "-") + "-" + AdminInfo.flashchipid; saveAs(blob, FileName + ".json"); // Filesaver.js - }); + })); - $('#restoreconfig').change(function () { + $('#restoreconfig').on("change", (function () { if (this.files.length !== 0) { const reader = new FileReader(); reader.onload = function fileReadCompleted() { @@ -166,16 +165,16 @@ $(function () { }; reader.readAsText(this.files[0]); } - }); + })); - $('#adminReboot').click(function () { + $('#adminReboot').on("click", (function () { reboot(); - }); + })); - $('#AdvancedOptions').change(function () { + $('#AdvancedOptions').on("change", (function () { UpdateAdvancedOptionsMode(); UpdateChannelCounts(); - }); + })); let finalUrl = "http://" + target + "/upload"; // console.log(finalUrl); @@ -241,13 +240,13 @@ $(function () { $("#filemanagementupload").addClass("dropzone"); - $('#FileDeleteButton').click(function () { + $('#FileDeleteButton').on("click", (function () { RequestFileDeletion(); - }); + })); /* - $('#FileUploadButton').click(function () { + $('#FileUploadButton').on("click", (function () { RequestFileUpload(); - }); + })); */ // Autoload tab based on URL hash let hash = window.location.hash; @@ -554,9 +553,16 @@ function ProcessModeConfigurationDataEffects(channelConfig) { $(jqSelector).append(''); }); - // set the current selector value $(jqSelector).val(channelConfig.currenteffect); + SetEffectVisibility(); + // set the current selector value + $(jqSelector).on("change", function() + { + SetEffectVisibility(); + }); + + // set up the transitions table $('#TransitionColorTable tbody').removeData(); channelConfig.transitions.forEach(element => { @@ -571,14 +577,61 @@ function ProcessModeConfigurationDataEffects(channelConfig) { RenumberTransitionTable(); $('#AddTransitionBtn').unbind(); - $('#AddTransitionBtn').click(function () { + $('#AddTransitionBtn').on("click", (function () { // console.info("Add a transition"); transitionAddRow('#000000'); RenumberTransitionTable(); + })); + + // set up the marqueue groups table + $('#MarqueeGroupTable tbody').removeData(); + + channelConfig.MarqueeGroups.forEach(element => { + // console.info("Element.r = " + element.r); + // console.info("Element.g = " + element.g); + // console.info("Element.b = " + element.b); + MarqueeGroupAddRow(element); }); + RenumberMarqueeGroupTable(); + + $('#AddMarqueeGroupBtn').unbind(); + $('#AddMarqueeGroupBtn').on("click", (function () { + // console.info("Add a MarqueeGroup button pressed"); + let newMarqueeGroup = {}; + newMarqueeGroup.brightness = 50; + newMarqueeGroup.brightnessEnd = 50; + newMarqueeGroup.pixel_count = 5; + newMarqueeGroup.color = {}; + newMarqueeGroup.color.r = 128; + newMarqueeGroup.color.g = 128; + newMarqueeGroup.color.b = 128; + MarqueeGroupAddRow(newMarqueeGroup); + RenumberMarqueeGroupTable(); + })); + } // ProcessModeConfigurationDataEffects +function SetEffectVisibility() +{ + let jqSelector = "#currenteffect"; + if($(jqSelector).val()==="Marquee") + { + $("#MarqueeConfig").removeClass("hidden"); + $("#TransitionsConfig").addClass("hidden"); + } + else if($(jqSelector).val()==="Transition") + { + $("#MarqueeConfig").addClass("hidden"); + $("#TransitionsConfig").removeClass("hidden"); + } + else + { + $("#MarqueeConfig").addClass("hidden"); + $("#TransitionsConfig").addClass("hidden"); + } +} + function UUID() { var uuid = (function () { var i, @@ -621,7 +674,7 @@ function transitionAddRow(CurrentColor) { let TransitionDeletePattern = ''; let rowPattern = ' ' + TransitionIdPattern + TransitionColorPattern + TransitionDeletePattern + ' '; $('#TransitionColorTable tbody tr:last').after(rowPattern); - $('#transitionDelete_' + CurrentRowId).click(function () { transitionDeleteRow($(this)); }); + $('#transitionDelete_' + CurrentRowId).on("click", (function () { transitionDeleteRow($(this)); })); } } // transitionAddRow @@ -664,6 +717,84 @@ function RenumberTransitionTable() { } // RenumberTransitionTable +function MarqueeGroupAddRow(CurrentConfig) { + + // console.info("MarqueeGroupAddRow::MarqueeGroupTable length " + $('#MarqueeGroupTable tbody tr').length); + // 1 header row + 5 group rows + if (6 > $('#MarqueeGroupTable tbody tr').length) { + let CurrentRowId = 'UUID_' + UUID().toString().toUpperCase(); + // console.info("CurrentColor " + CurrentColor); + + let CurrentGroupColor = "#" + ((CurrentConfig.color.r * 256 * 256) + (CurrentConfig.color.g * 256) + CurrentConfig.color.b).toString(16); + + while (-1 !== CurrentRowId.indexOf("-")) + { + CurrentRowId = CurrentRowId.replace("-", "_"); + } + + while (CurrentGroupColor.length < 7) { CurrentGroupColor = CurrentGroupColor.replace("#", "#0"); } + + let MarqueeGroupIdPattern = '' + (CurrentRowId) + ''; + let MarqueeGroupIntensityPattern = ''; + let MarqueeGroupIntensityEndPattern = ''; + let MarqueeGroupCountPattern = ''; + let MarqueeGroupColorPattern = ''; + let MarqueeGroupDeletePattern = ''; + +// var rowPattern = '' + StartPattern + EndPattern + StartValuePattern + EndValuePattern + OutputPattern + ''; + let rowPattern = '' + + MarqueeGroupIdPattern + + MarqueeGroupIntensityPattern + + MarqueeGroupIntensityEndPattern + + MarqueeGroupCountPattern + + MarqueeGroupColorPattern + + MarqueeGroupDeletePattern + + ' '; + $('#MarqueeGroupTable tbody tr:last').after(rowPattern); + $('#MarqueeGroupDelete_' + CurrentRowId).on("click", function () { MarqueeGroupDeleteRow($(this)); }); + + $('#MarqueeGroupIntensity_' + (CurrentRowId)).val(CurrentConfig.brightness); + $('#MarqueeGroupIntensityEnd_' + (CurrentRowId)).val(CurrentConfig.brightnessEnd); + $('#MarqueeGroupCount_' + (CurrentRowId)).val(CurrentConfig.pixel_count); + $('#MarqueeGroupColor_' + (CurrentRowId)).val(CurrentGroupColor); + } +} // MarqueeGroupAddRow + +function MarqueeGroupDeleteRow(button) { + // console.info("MarqueeGroupDeleteRow::MarqueeGroupTable length " + $('#MarqueeGroupTable tbody tr').length); + + let RowId = $(button).attr("RowId"); + // console.info("Got Click for CurrentRowId: " + RowId); + // console.info("Length: " + $('#MarqueeGroupTable tbody tr').length); + // 3 = hdr+2 rows + if (3 < $('#MarqueeGroupTable tbody tr').length) { + $('#MarqueeGroupRow_' + RowId).remove(); + RenumberMarqueeGroupTable(); + } + +} // MarqueeGroupDeleteRow + +function RenumberMarqueeGroupTable() { + // renumber the table + // console.info("RenumberMarqueeGroupTable::MarqueeGroupTable length " + $('#MarqueeGroupTable tbody tr').length); + + $('#MarqueeGroupTable tbody tr').each(elementId => { + if (0 !== elementId) { + // console.info(elementId); + $('#MarqueeGroupTable tbody tr:eq(' + elementId + ') td:eq(0)').html((elementId).toString()); + } + }); + + // 1 hdr + 5 rows + if (6 > $('#MarqueeGroupTable tbody tr').length) { + $('#AddMarqueeGroupBtn').show(); + } + else { + $('#AddMarqueeGroupBtn').hide(); + } + +} // RenumberMarqueeGroupTable + function ProcessModeConfigurationDataRelay(RelayConfig) { // console.log("relaychannelconfigurationtable.rows.length = " + $('#relaychannelconfigurationtable tr').length); @@ -1156,6 +1287,7 @@ function ExtractChannelConfigFromHtmlPage(JsonConfig, SectionName) { // the auto export adds the add color button to the structure. Remove it. delete ChannelConfig["AddTransitionBtn"]; + delete ChannelConfig["AddMarqueeGroupBtn"]; // build a new transitions array const transitions = []; @@ -1194,6 +1326,55 @@ function ExtractChannelConfigFromHtmlPage(JsonConfig, SectionName) { }); ChannelConfig.transitions = transitions; + + // build a new MarqueeGroup array + const MarqueeGroups = []; + MarqueeGroups.length = $('#MarqueeGroupTable tbody tr').length - 1; + elementId = 0; // row counter into the json array + + delete ChannelConfig["MarqueeGroups"]; + + $('#MarqueeGroupTable tbody tr').each(function () { + let CurRow = $(this)[0]; + let RowId = $(CurRow).attr("RowId"); + + if (undefined !== RowId) { + // console.info("RowId = " + RowId); + let DeleteButtonName = 'MarqueeGroupDelete_' + RowId; + let ColorElementName = 'MarqueeGroupColor_' + RowId; + // console.info("DeleteButtonName = " + DeleteButtonName); + // console.info("elementName = " + elementName); + + // the auto export adds the delete and table data to the structure. Remove it. + delete ChannelConfig[DeleteButtonName]; + delete ChannelConfig['MarqueeGroupIntensity_' + (RowId)]; + delete ChannelConfig['MarqueeGroupIntensityEnd_' + (RowId)]; + delete ChannelConfig['MarqueeGroupCount_' + (RowId)]; + delete ChannelConfig['MarqueeGroupColor_' + (RowId)]; + + let HexValue = $('#' + ColorElementName).val(); + + // console.info("HexValue = " + HexValue); + // console.info("r = " + hexToRgb(HexValue).r); + // console.info("g = " + hexToRgb(HexValue).g); + // console.info("b = " + hexToRgb(HexValue).b); + + MarqueeGroups[elementId] = {}; + let MarqueeGroup = MarqueeGroups[elementId]; + + MarqueeGroup.brightness = $('#MarqueeGroupIntensity_' + (RowId)).val(); + MarqueeGroup.brightnessEnd = $('#MarqueeGroupIntensityEnd_' + (RowId)).val(); + MarqueeGroup.pixel_count = $('#MarqueeGroupCount_' + (RowId)).val(); + + MarqueeGroup.color = {}; + MarqueeGroup.color.r = hexToRgb(HexValue).r; + MarqueeGroup.color.g = hexToRgb(HexValue).g; + MarqueeGroup.color.b = hexToRgb(HexValue).b; + elementId++; + } + }); + + ChannelConfig.MarqueeGroups = MarqueeGroups; } else { ExtractConfigFromHtmlPages(elementids, modeControlName, ChannelConfig); @@ -1296,7 +1477,7 @@ function wsConnect() { target = document.location.host; } - // target = "192.168.10.175"; + // target = "192.168.10.240"; // target = "192.168.10.101"; // Open a new web socket and set the binary type @@ -1884,7 +2065,7 @@ function reboot() { } // Reset config -$('#confirm-reset .btn-ok').click(function () { +$('#confirm-reset .btn-ok').on("click", (function () { showReboot(); wsEnqueue('X7'); -}); +}));