diff --git a/README.md b/README.md index 02a8d1264..0d2615759 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ _Davepl, 9/19/2021_ ## Table of Contents - [What NightDriverStrip is](#what-nightdriverstrip-is) +- [Project Overview](#project-overview) +- [Key Features](#key-features) - [Using the Web Installer](#using-the-web-installer) - [Introduction](#introduction) - [(Re)flashing your device with the Web Installer](#reflashing-your-device-with-the-web-installer) @@ -49,6 +51,24 @@ NightDriver can drive both WS2812B style strips and HUB75 style matrices. More recently, a web installer has been added to the project with which most of the NightDriver projects can be flashed on supported devices, using nothing but a web browser. Please refer to the next section if this is how you'd like to get started. +## Project Overview + +- It's an open-source project for controlling LED strips/matrices and doing cool effects on them +- Uses WiFi for remote control and data reception. +- Supports audio reactive effects. +- Includes a web server and telnet debug server. + +## Key Features + +- Multiple LED control methods (WS2812B, HUB75, etc.) +- WiFi connectivity for remote control and data +- Audio analysis for sound-reactive effects +- OTA (Over-The-Air) updates +- Debug console accessible via telnet and serial +- Display support (OLED, TFT, LCD) for status information +- NTP time synchronization so effects can span multiple ESP32s in sync +- Configurable via web interface that runs on the ESP32 + ## Using the Web Installer ### Introduction diff --git a/config/web_projects.json b/config/web_projects.json index 9e4b8e614..771f2d884 100644 --- a/config/web_projects.json +++ b/config/web_projects.json @@ -16,6 +16,10 @@ "chipfamily": "ESP32", "name": "ESP32", "projects": [ + { + "tag": "atomlight", + "name": "Atom Light" + }, { "tag": "demo", "name":"Demo" @@ -61,10 +65,6 @@ "chipfamily": "ESP32", "name": "Heltec Wifi Kit 32", "projects": [ - { - "tag": "atomlight", - "name": "Atom Light" - }, { "tag": "heltecdemo", "name":"Demo" @@ -144,6 +144,10 @@ "tag": "fanset", "name": "Fan Set" }, + { + "tag": "helmet", + "name": "Helmet" + }, { "tag": "insulators", "name": "Insulators" @@ -156,12 +160,31 @@ "tag": "spectrum", "name": "Spectrum" }, + { + "tag": "spirallamp", + "name": "Spiral Lamp" + }, { "tag": "xmastrees", "name": "X-mas Trees" } ] }, + { + "tag": "m5stick-c-plus2", + "chipfamily": "ESP32", + "name": "M5StickC Plus2", + "projects": [ + { + "tag": "pdpgrid", + "name": "PDP Grid" + }, + { + "tag": "spectrum2", + "name": "Spectrum" + } + ] + }, { "tag": "mesmerizer", "chipfamily": "ESP32", diff --git a/include/deviceconfig.h b/include/deviceconfig.h index b2f067eeb..7153945f5 100644 --- a/include/deviceconfig.h +++ b/include/deviceconfig.h @@ -86,7 +86,7 @@ #define NTP_SERVER_DEFAULT "0.pool.ntp.org" #define BRIGHTNESS_MIN uint8_t(10) #define BRIGHTNESS_MAX uint8_t(255) -#define POWER_LIMIT_MIN 2000 +#define POWER_LIMIT_MIN 1000 #define POWER_LIMIT_DEFAULT 4500 // DeviceConfig holds, persists and loads device-wide configuration settings. Effect-specific settings should diff --git a/include/effectmanager.h b/include/effectmanager.h index d04002b65..c81dfd0e1 100644 --- a/include/effectmanager.h +++ b/include/effectmanager.h @@ -68,7 +68,7 @@ class EffectManager : public IJSONSerializable bool _bPlayAll; bool _bShowVU = true; bool _clearTempEffectWhenExpired = false; - bool _newFrameAvailable = false; + std::atomic_bool _newFrameAvailable = false; int _effectSetVersion = 1; std::vector> _gfx; diff --git a/include/effects.h b/include/effects.h index da8ec847c..68479344d 100644 --- a/include/effects.h +++ b/include/effects.h @@ -145,6 +145,8 @@ #define EFFECT_MATRIX_SMWALKING_MACHINE 157 #define EFFECT_MATRIX_ANIMATEDGIF 158 #define EFFECT_MATRIX_STOCKS 159 +#define EFFECT_MATRIX_SILON 160 +#define EFFECT_MATRIX_PDPGRID 161 // Hexagon Effects #define EFFECT_HEXAGON_OUTER_RING 201 diff --git a/include/effects/matrix/PatternLife.h b/include/effects/matrix/PatternLife.h index 11a55bef6..0b97f8661 100644 --- a/include/effects/matrix/PatternLife.h +++ b/include/effects/matrix/PatternLife.h @@ -162,7 +162,7 @@ class PatternLife : public LEDStripEffect // // Example: Seed: 92465, Generations: 1626 - static constexpr long bakedInSeeds[] = + static constexpr std::array bakedInSeeds = { 130908, // 3253 1576, // 3125 @@ -185,7 +185,6 @@ class PatternLife : public LEDStripEffect 555109764, // 4470 }; - void randomFillWorld() { // Some fraction of the time we pick a pre-baked seed that we know lasts for a lot @@ -194,7 +193,7 @@ class PatternLife : public LEDStripEffect srand(millis()); if (random(0, 4) == 0) { - seed = bakedInSeeds[random(ARRAYSIZE(bakedInSeeds))]; + seed = bakedInSeeds[random(std::size(bakedInSeeds))]; debugV("Prebaked Seed: %lu", seed); } else diff --git a/include/effects/matrix/PatternSMGamma.h b/include/effects/matrix/PatternSMGamma.h index bee063a35..d0d59f46b 100644 --- a/include/effects/matrix/PatternSMGamma.h +++ b/include/effects/matrix/PatternSMGamma.h @@ -24,7 +24,8 @@ class PatternSMGamma : public LEDStripEffect void Draw() override { - static const uint8_t exp_gamma[256] = { + static constexpr std::array exp_gamma = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 7, 7, @@ -37,7 +38,8 @@ class PatternSMGamma : public LEDStripEffect 109, 111, 112, 114, 115, 117, 118, 120, 121, 123, 125, 126, 128, 130, 131, 133, 135, 136, 138, 140, 142, 143, 145, 147, 149, 151, 152, 154, 156, 158, 160, 162, 164, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 190, 192, 194, 196, 198, 200, 202, 204, 207, 209, 211, 213, 216, 218, 220, - 222, 225, 227, 229, 232, 234, 236, 239, 241, 244, 246, 249, 251, 253, 254, 255}; + 222, 225, 227, 229, 232, 234, 236, 239, 241, 244, 246, 249, 251, 253, 254, 255 + }; int a = millis() / 8; for (uint x = 0; x < MATRIX_WIDTH; x++) diff --git a/include/effects/matrix/PatternStocks.h b/include/effects/matrix/PatternStocks.h index 6e2db9bf8..5ac794117 100644 --- a/include/effects/matrix/PatternStocks.h +++ b/include/effects/matrix/PatternStocks.h @@ -58,7 +58,7 @@ using namespace std; using namespace std::chrono; using namespace std::chrono_literals; -#define STOCKS_UPDATE_INTERVAL_SECONDS 10s +#define STOCKS_UPDATE_INTERVAL_SECONDS 6s #define STOCKS_FETCH_INTERVAL_SECONDS 60s #define DEFAULT_STOCK_SERVER "davepl.com:8888" @@ -416,7 +416,8 @@ class PatternStocks : public LEDStripEffect auto changetext = String(data.close - data.open, 2); auto changelen = changetext.length(); - auto voltext = String(data.volume, 0); + constexpr auto formatVolumeLargerThan = 1000000; + auto voltext = formatSize(data.volume, formatVolumeLargerThan); auto vollen = voltext.length(); textSymbol = AnimatedText(data.symbol, CRGB::White, &Apple5x7, 0.50f, -MATRIX_WIDTH, 8, 0, 8); @@ -425,6 +426,7 @@ class PatternStocks : public LEDStripEffect textVolume = AnimatedText(voltext, CRGB::LightGrey, &Apple5x7, 1.0f, -MATRIX_WIDTH * 2, 22, MATRIX_WIDTH - vollen * textwidth, 22); } + // UpdateQuoteDisplay // // Updates the position of the text and draws it on the screen, then draws @@ -459,23 +461,15 @@ class PatternStocks : public LEDStripEffect if (n > 0) { - float max = 0.0f; - float min = numeric_limits::max(); - // We have the high and low data in the stock, but let's not trust it and calculate it ourselves + // If this works, Davepl wrote it. If not, Robert made me do it! - for (int i = 0; i < n; i++) - { - max = std::max(max, currentStock.points[i].val); - min = std::min(min, currentStock.points[i].val); - } - - // Now draw each vertical line segment + auto [minpoint, maxpoint] = std::minmax_element(currentStock.points.begin(), currentStock.points.end(), [](const StockPoint& a, const StockPoint& b) { return a.val < b.val; }); + float min = minpoint->val, max = maxpoint->val, range = max - min; - float range = max - min; if (range > 0.0f) { - float scale = h / range; + float scale = range > 0.0f ? h / range : 0.0f; float breakeven = currentStock.open; float breakevenY = y + h - (breakeven - min) * scale; @@ -511,7 +505,7 @@ class PatternStocks : public LEDStripEffect if (WiFi.isConnected()) { - if (system_clock::now() > nextFetch) + if (system_clock::now() >= nextFetch) { nextFetch = system_clock::now() + STOCKS_FETCH_INTERVAL_SECONDS; // Trigger the stock data reader. diff --git a/include/effects/matrix/PatternWeather.h b/include/effects/matrix/PatternWeather.h index f8fb41b22..3b32a3080 100644 --- a/include/effects/matrix/PatternWeather.h +++ b/include/effects/matrix/PatternWeather.h @@ -42,6 +42,7 @@ #include #include "systemcontainer.h" #include +#include #include #include #include @@ -92,9 +93,9 @@ extern const uint8_t thunderstorm_end[] asm("_binary_assets_bmp_thun extern const uint8_t thunderstorm_night_start[] asm("_binary_assets_bmp_thunderstormnight_jpg_start"); extern const uint8_t thunderstorm_night_end[] asm("_binary_assets_bmp_thunderstormnight_jpg_end"); -static const char * pszDaysOfWeek[] = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; +static constexpr auto pszDaysOfWeek = to_array( { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" } ); -static std::map, psram_allocator>> weatherIcons = +static std::map, psram_allocator>> weatherIcons = { { "01d", EmbeddedFile(clearsky_start, clearsky_end) }, { "02d", EmbeddedFile(fewclouds_start, fewclouds_end) }, diff --git a/include/effects/matrix/spectrumeffects.h b/include/effects/matrix/spectrumeffects.h index 22001622d..4a579daff 100644 --- a/include/effects/matrix/spectrumeffects.h +++ b/include/effects/matrix/spectrumeffects.h @@ -216,15 +216,16 @@ class SpectrumAnalyzerEffect : public LEDStripEffect, virtual public VUMeter uint8_t _numBars; uint8_t _colorOffset; - uint16_t _scrollSpeed; + uint16_t _colorScrollSpeed; uint8_t _fadeRate; + bool _bScrollBars; const CRGBPalette16 _palette; bool _ignoreGlobalColor; float _peak1DecayRate; float _peak2DecayRate; - bool _bShowVU; + int _offset = 0; virtual size_t DesiredFramesPerSecond() const override { @@ -241,7 +242,7 @@ class SpectrumAnalyzerEffect : public LEDStripEffect, virtual public VUMeter // Draws the bar graph rectangle for a bar and then the white line on top of it. Interpolates odd bars when you // have twice as many bars as bands. - void DrawBar(const uint8_t iBar, CRGB baseColor) + void DrawBar(const uint8_t iBar, CRGB baseColor, int offset = 0) { auto pGFXChannel = g(); int value, value2; @@ -293,9 +294,11 @@ class SpectrumAnalyzerEffect : public LEDStripEffect, virtual public VUMeter int yOffset = pGFXChannel->height() - value ; int yOffset2 = pGFXChannel->height() - value2 ; + offset %= MATRIX_WIDTH; + for (int y = yOffset2; y < pGFXChannel->height(); y++) for (int x = xOffset; x < xOffset + barWidth; x++) - g()->setPixel(x, y, baseColor); + g()->setPixel((x - offset + MATRIX_WIDTH) % MATRIX_WIDTH, y, baseColor); // We draw the highlight in white, but if its falling at a different rate than the bar itself, // it indicates a free-floating highlight, and those get faded out based on age @@ -307,6 +310,8 @@ class SpectrumAnalyzerEffect : public LEDStripEffect, virtual public VUMeter // If a decay rate has been defined and it's different than the rate at which the bar falls if (_peak1DecayRate >= 0.0f) { + xOffset = (xOffset - offset + MATRIX_WIDTH) % MATRIX_WIDTH; + if (_peak1DecayRate != _peak2DecayRate) { const int PeakFadeTime_ms = 1000; @@ -336,16 +341,18 @@ class SpectrumAnalyzerEffect : public LEDStripEffect, virtual public VUMeter uint16_t scrollSpeed = 0, uint8_t fadeRate = 0, float peak1DecayRate = 1.0, - float peak2DecayRate = 1.0) + float peak2DecayRate = 1.0, + bool bScrollBars = false) : LEDStripEffect(EFFECT_MATRIX_SPECTRUM_ANALYZER, pszFriendlyName), _numBars(cNumBars), _colorOffset(0), - _scrollSpeed(scrollSpeed), + _colorScrollSpeed(scrollSpeed), _fadeRate(fadeRate), _palette(palette), _ignoreGlobalColor(ignoreGlobalColor), _peak1DecayRate(peak1DecayRate), - _peak2DecayRate(peak2DecayRate) + _peak2DecayRate(peak2DecayRate), + _bScrollBars(bScrollBars) { } @@ -354,16 +361,18 @@ class SpectrumAnalyzerEffect : public LEDStripEffect, virtual public VUMeter const CRGB & baseColor = CRGB::Red, uint8_t fadeRate = 0, float peak1DecayRate = 1.0, - float peak2DecayRate = 1.0) + float peak2DecayRate = 1.0, + bool bScrollBars = false) : LEDStripEffect(EFFECT_MATRIX_SPECTRUM_ANALYZER, pszFriendlyName), _numBars(cNumBars), _colorOffset(0), - _scrollSpeed(0), + _colorScrollSpeed(0), _fadeRate(fadeRate), _palette(baseColor), _ignoreGlobalColor(true), _peak1DecayRate(peak1DecayRate), - _peak2DecayRate(peak2DecayRate) + _peak2DecayRate(peak2DecayRate), + _bScrollBars(bScrollBars) { } @@ -372,13 +381,13 @@ class SpectrumAnalyzerEffect : public LEDStripEffect, virtual public VUMeter : LEDStripEffect(jsonObject), _numBars(jsonObject["nmb"]), _colorOffset(0), - _scrollSpeed(jsonObject[PTY_SPEED]), + _colorScrollSpeed(jsonObject[PTY_SPEED]), _fadeRate(jsonObject["frt"]), _palette(jsonObject[PTY_PALETTE].as()), _ignoreGlobalColor(jsonObject[PTY_IGNOREGLOBALCOLOR]), _peak1DecayRate(jsonObject["pd1"]), - _peak2DecayRate(jsonObject["pd2"]) - + _peak2DecayRate(jsonObject["pd2"]), + _bScrollBars(jsonObject["scb"]) { } @@ -392,10 +401,11 @@ class SpectrumAnalyzerEffect : public LEDStripEffect, virtual public VUMeter jsonDoc[PTY_PALETTE] = _palette; jsonDoc[PTY_IGNOREGLOBALCOLOR] = _ignoreGlobalColor; jsonDoc["nmb"] = _numBars; - jsonDoc[PTY_SPEED] = _scrollSpeed; + jsonDoc[PTY_SPEED] = _colorScrollSpeed; jsonDoc["frt"] = _fadeRate; jsonDoc["pd1"] = _peak1DecayRate; jsonDoc["pd2"] = _peak2DecayRate; + jsonDoc["scb"] = _bScrollBars; assert(!jsonDoc.overflowed()); @@ -413,11 +423,14 @@ class SpectrumAnalyzerEffect : public LEDStripEffect, virtual public VUMeter virtual void Draw() override { + if (_bScrollBars) + _offset++; + auto pGFXChannel = _GFX[0]; - if (_scrollSpeed > 0) + if (_colorScrollSpeed > 0) { - EVERY_N_MILLISECONDS(_scrollSpeed) + EVERY_N_MILLISECONDS(_colorScrollSpeed) { _colorOffset+=2; } @@ -441,7 +454,7 @@ class SpectrumAnalyzerEffect : public LEDStripEffect, virtual public VUMeter { // We don't use the color offset when the palette is paused int q = ::map(i, 0, _numBars, 0, 240); - DrawBar(i, pGFXChannel->ColorFromCurrentPalette(q % 240, 255, _scrollSpeed > 0 ? LINEARBLEND : NOBLEND)); + DrawBar(i, pGFXChannel->ColorFromCurrentPalette(q % 240, 255, _colorScrollSpeed > 0 ? LINEARBLEND : NOBLEND), _offset); } else { @@ -453,7 +466,7 @@ class SpectrumAnalyzerEffect : public LEDStripEffect, virtual public VUMeter globalPalette = CRGBPalette16(deviceConfig.GlobalColor(), deviceConfig.SecondColor()); int q = ::map(i, 0, _numBars, 0, 255) + _colorOffset; - DrawBar(i, ColorFromPalette(globalPalette ? *globalPalette : _palette, (q) % 255, 255, _scrollSpeed > 0 ? LINEARBLEND : NOBLEND)); + DrawBar(i, ColorFromPalette(globalPalette ? *globalPalette : _palette, (q) % 255, 255, _colorScrollSpeed > 0 ? LINEARBLEND : NOBLEND), _offset); } } } diff --git a/include/effects/strip/bouncingballeffect.h b/include/effects/strip/bouncingballeffect.h index cfab631ed..134941451 100644 --- a/include/effects/strip/bouncingballeffect.h +++ b/include/effects/strip/bouncingballeffect.h @@ -36,7 +36,7 @@ // // Draws a set of N bouncing balls using a simple little kinematics formula. Clears the section first. -static const CRGB ballColors[] = +static constexpr auto ballColors = to_array( { CRGB::Green, CRGB::Red, @@ -45,8 +45,7 @@ static const CRGB ballColors[] = CRGB::Purple, CRGB::Yellow, CRGB::Indigo, - -}; +}); class BouncingBallEffect : public LEDStripEffect { @@ -60,15 +59,15 @@ class BouncingBallEffect : public LEDStripEffect const bool _bErase; - float Gravity = -9.81; - float StartHeight = 1; - float ImpactVelocityStart = sqrt(-2 * Gravity * StartHeight); + static constexpr float Gravity = -9.81f; + static constexpr float StartHeight = 1.0f; + static constexpr float ImpactVelocityStart = sqrt(-2.0f * Gravity * StartHeight); std::vector ClockTimeSinceLastBounce; std::vector TimeSinceLastBounce; - std::vector Height; - std::vector ImpactVelocity; - std::vector Dampening; + std::vector Height; + std::vector ImpactVelocity; + std::vector Dampening; std::vector Colors; public: @@ -134,7 +133,7 @@ class BouncingBallEffect : public LEDStripEffect ClockTimeSinceLastBounce[i] = g_Values.AppTime.FrameStartTime(); Dampening[i] = 1.0f - i / powf(_cBalls, 2); // Was 0.9 TimeSinceLastBounce[i] = 0; - Colors[i] = ballColors[i % ARRAYSIZE(ballColors)]; + Colors[i] = ballColors[i % std::size(ballColors)]; } return true; } @@ -174,9 +173,9 @@ class BouncingBallEffect : public LEDStripEffect } float position = Height[i] * (_cLength - 1) / StartHeight; - setPixelsOnAllChannels(position, _cBallSize, Colors[i % ARRAYSIZE(ballColors)]); + setPixelsOnAllChannels(position, _cBallSize, Colors[i % std::size(ballColors)]); if (_bMirrored) - setPixelsOnAllChannels(_cLength-1-position, _cBallSize, Colors[i % ARRAYSIZE(ballColors)], true); + setPixelsOnAllChannels(_cLength-1-position, _cBallSize, Colors[i % std::size(ballColors)], true); } } }; diff --git a/include/effects/strip/faneffects.h b/include/effects/strip/faneffects.h index e3722f677..b7def5b2c 100644 --- a/include/effects/strip/faneffects.h +++ b/include/effects/strip/faneffects.h @@ -949,7 +949,7 @@ class FireFanEffect : public LEDStripEffect CRGBPalette16 Palette; int LEDCount; // Number of LEDs total int CellsPerLED; - int Cooling; // Rate at which the pixels cool off + float Cooling; // Rate at which the pixels cool off int Sparks; // How many sparks will be attempted each frame int SparkHeight; // If created, max height for a spark int Sparking; // Probability of a spark each attempt @@ -976,7 +976,7 @@ class FireFanEffect : public LEDStripEffect FireFanEffect(CRGBPalette16 palette, int ledCount, int cellsPerLED = 1, - int cooling = 20, + float cooling = 20, int sparking = 100, int sparks = 3, int sparkHeight = 4, @@ -1067,7 +1067,7 @@ class FireFanEffect : public LEDStripEffect { for (int i = 0; i < CellCount(); i++) { - int coolingAmount = random(0, Cooling); + float coolingAmount = random_range(0.0f, 2.0f); abHeat[i] = ::max(0.0, abHeat[i] - coolingAmount * (2.0 - g_Analyzer._VURatio)); } } diff --git a/include/effects/strip/meteoreffect.h b/include/effects/strip/meteoreffect.h index d184c4d15..194708371 100644 --- a/include/effects/strip/meteoreffect.h +++ b/include/effects/strip/meteoreffect.h @@ -74,7 +74,7 @@ class MeteorChannel hueval = hueval + 48; hueval %= 256; hue[i] = hueval; - iPos[i] = meteorCount < 1 ? 0 : (pGFX->GetLEDCount() / (meteorCount - 1) * i); + iPos[i] = meteorCount <= 1 ? 0 : (pGFX->GetLEDCount() / (meteorCount - 1) * i); speed[i] = random_range(meteorSpeedMin, meteorSpeedMax); if (i % 1) speed[i] *= -1; diff --git a/include/effects/strip/misceffects.h b/include/effects/strip/misceffects.h index 0940c8a69..9ef737d73 100644 --- a/include/effects/strip/misceffects.h +++ b/include/effects/strip/misceffects.h @@ -393,15 +393,15 @@ class StatusEffect : public LEDStripEffect }; #if CLASSIC_GE_C9 -static const CRGB TwinkleColors[] = +static constexpr auto TwinkleColors = to_array( { CRGB(238, 51, 39), // Red CRGB(0, 172, 87), // Green CRGB(250, 164, 25), // Yellow CRGB(0, 131, 203) // Blue -}; +}); #else -static const CRGB TwinkleColors[] = +static constexpr auto TwinkleColors = to_array( { CRGB::Red, CRGB::Green, @@ -409,7 +409,7 @@ static const CRGB TwinkleColors[] = CRGB::Blue, CRGB::Purple, CRGB::Yellow -}; +}); #endif class TwinkleEffect : public LEDStripEffect @@ -491,7 +491,7 @@ class TwinkleEffect : public LEDStripEffect return; } assert(litPixels.end() == find(litPixels.begin(), litPixels.end(), iNew)); - setPixelOnAllChannels(iNew, TwinkleColors[random(0, ARRAYSIZE(TwinkleColors))]); + setPixelOnAllChannels(iNew, TwinkleColors[random(0, std::size(TwinkleColors))]); litPixels.push_front(iNew); } } @@ -503,6 +503,97 @@ class TwinkleEffect : public LEDStripEffect } }; +// SilonEffect +// +// A Battlestar Galactica inspired effect that moves red and green bars back and forth + +class SilonEffect : public LEDStripEffect +{ + public: + + SilonEffect() : LEDStripEffect(EFFECT_MATRIX_SILON, "SilonEffect") + { + } + + SilonEffect(const JsonObjectConst& jsonObject) + : LEDStripEffect(jsonObject) + { + } + + int _offset = 0; + int _direction = 1; + + virtual size_t DesiredFramesPerSecond() const + { + return 20; + } + + virtual void Draw() override + { + _offset += _direction; + if (_offset >= MATRIX_WIDTH) + { + _offset = MATRIX_WIDTH - 1; + _direction = -1; + } + if (_offset <= 0) + { + _offset = 0; + _direction = 1; + } + fadeAllChannelsToBlackBy(75); + + for (int y = 0; y < MATRIX_HEIGHT; y++) + { + setPixelOnAllChannels(_offset, y, CRGB::Red); + setPixelOnAllChannels(MATRIX_WIDTH - 1 - _offset, y, CRGB::Green); + } + } +}; + +// PDPGridEffect +// +// A Display for the front of the PDP-11/34 + +class PDPGridEffect : public LEDStripEffect +{ + public: + + PDPGridEffect() : LEDStripEffect(EFFECT_MATRIX_PDPGRID, "PDPGridEffect") + { + } + + PDPGridEffect(const JsonObjectConst& jsonObject) + : LEDStripEffect(jsonObject) + { + } + + int _offset = 0; + int _direction = 1; + + virtual size_t DesiredFramesPerSecond() const + { + return 20; + } + + virtual void Draw() override + { + fadeAllChannelsToBlackBy(255 * g_Values.AppTime.LastFrameTime()); + + EVERY_N_MILLISECONDS(200) + { + g()->MoveY(1); + for (int x = 0; x < MATRIX_WIDTH; x++) + { + if (random(0, 100) < 20) + setPixelOnAllChannels(x, MATRIX_HEIGHT-1, CRGB::Red); + else + setPixelOnAllChannels(x, MATRIX_HEIGHT-1, CRGB::Black); + } + } + } +}; + #if HEXAGON //////////////////////////////////////////////// // Hexagon Effects diff --git a/include/effects/strip/stareffect.h b/include/effects/strip/stareffect.h index cc0e4899c..588daf3b5 100644 --- a/include/effects/strip/stareffect.h +++ b/include/effects/strip/stareffect.h @@ -164,8 +164,8 @@ class MusicStar : public Star virtual float PreignitionTime() const { return 0.0f; } virtual float IgnitionTime() const { return 0.00f; } - virtual float HoldTime() const { return 2.00f; } - virtual float FadeTime() const { return 0.25f; } + virtual float HoldTime() const { return 0.00f; } + virtual float FadeTime() const { return 0.5f; } }; @@ -506,9 +506,9 @@ template class StarryNightEffect : public LEDStripEffect { double prob = _newStarProbability; - prob = (prob / 100) + (g_Analyzer._VURatio - 1.0) * _musicFactor * 4; + prob = (prob / 100) + (g_Analyzer._VURatio - 1.0) * _musicFactor; - constexpr auto kProbabilitySpan = 2.0; + constexpr auto kProbabilitySpan = 1.0; if (g_Analyzer._VU > 0) { @@ -550,13 +550,12 @@ template class StarryNightEffect : public LEDStripEffect fadeAllChannelsToBlackBy(55 * (2.0 - g_Analyzer._VURatioFade)); } - //Serial.printf("Stars: %d Deltatime: %lf\n", _allParticles.size(), g_AppTime.DeltaTime()); for(auto i = _allParticles.begin(); i != _allParticles.end(); i++) { i->UpdatePosition(); float fPos = i->_iPos; CRGB c = i->ObjectColor(); - g()->setPixelsF(fPos - i->_objectSize / 2.0, i->_objectSize, c, true); + setPixelsFOnAllChannels(fPos - i->_objectSize / 2.0, i->_objectSize, c, true); } } }; @@ -617,7 +616,7 @@ class TwinkleStarEffect : public LEDStripEffect // Rotate the buffer - //memmove(buffer, buffer + 1, ARRAYSIZE(buffer) * (Count - 1)); + //memmove(buffer, buffer + 1, std::size(buffer) * (Count - 1)); for (int i = 0; i < NUM_TWINKLES - 1; i++) buffer[i] = buffer[i + 1]; diff --git a/include/gfxbase.h b/include/gfxbase.h index f4cf1bbd9..ecc6078b9 100644 --- a/include/gfxbase.h +++ b/include/gfxbase.h @@ -110,10 +110,29 @@ class GFXBase : public Adafruit_GFX size_t _width; size_t _height; - static const uint8_t gamma5[]; - static const uint8_t gamma6[]; - - static const int _paletteCount = 10; + // 32 Entries in the 5-bit gamma table + static constexpr auto gamma5 = to_array + ({ + 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0b, + 0x0e, 0x11, 0x14, 0x18, 0x1d, 0x22, 0x28, 0x2e, + 0x36, 0x3d, 0x46, 0x4f, 0x59, 0x64, 0x6f, 0x7c, + 0x89, 0x97, 0xa6, 0xb6, 0xc7, 0xd9, 0xeb, 0xff + }); + + // 64 Entries in the 6-bit gamma table + static constexpr auto gamma6 = to_array + ({ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, + 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x10, 0x12, 0x13, + 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x20, 0x22, 0x25, + 0x27, 0x2a, 0x2d, 0x30, 0x33, 0x37, 0x3a, 0x3e, + 0x41, 0x45, 0x49, 0x4d, 0x52, 0x56, 0x5b, 0x5f, + 0x64, 0x69, 0x6e, 0x74, 0x79, 0x7f, 0x85, 0x8b, + 0x91, 0x97, 0x9d, 0xa4, 0xab, 0xb2, 0xb9, 0xc0, + 0xc7, 0xcf, 0xd6, 0xde, 0xe6, 0xee, 0xf7, 0xff + }); + + static constexpr int _paletteCount = 10; int _paletteIndex = -1; uint _lastSecond = 99; bool _palettePaused = false; @@ -127,8 +146,8 @@ class GFXBase : public Adafruit_GFX std::unique_ptr _ptrNoise; #endif - static const int _heatColorsPaletteIndex = 6; - static const int _randomPaletteIndex = 9; + static constexpr int _heatColorsPaletteIndex = 6; + static constexpr int _randomPaletteIndex = 9; public: // Many of the Aurora effects need direct access to these from external classes @@ -144,13 +163,13 @@ class GFXBase : public Adafruit_GFX } #if USE_NOISE - Noise &GetNoise() + Noise &GetNoise() const { return *_ptrNoise; } #endif - CRGBPalette16 &GetCurrentPalette() + const CRGBPalette16 &GetCurrentPalette() const { return _currentPalette; } @@ -268,6 +287,8 @@ class GFXBase : public Adafruit_GFX #if USE_HUB75 #define XY(x, y) ((y) * MATRIX_WIDTH + (x)) + #elif HELMET + #define XY(x, y) xy(x, MATRIX_HEIGHT - 1 - y) // Invert the Y axis for the helmet display #else #define XY(x, y) xy(x, y) #endif @@ -698,9 +719,9 @@ class GFXBase : public Adafruit_GFX Serial.println(","); Serial.println(F(" \"results\": [")); - String paletteNames[] = { + static constexpr auto paletteNames = to_array( + { "Rainbow", - // "RainbowStripe", "Ocean", "Cloud", "Forest", @@ -709,7 +730,8 @@ class GFXBase : public Adafruit_GFX "Heat", "Lava", "Ice", - "Random"}; + "Random" + }); for (int i = 0; i < _paletteCount; i++) { diff --git a/include/globals.h b/include/globals.h index 198cf6c15..9153daaaa 100644 --- a/include/globals.h +++ b/include/globals.h @@ -90,6 +90,7 @@ #include #include #include +#include #include #include #include @@ -153,7 +154,6 @@ // NAME_OF with first character cut off - addresses underscore-prefixed (member) variables #define ACTUAL_NAME_OF(x) ((#x) + 1) -#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) // Returns the number of elements in an array #define PERIOD_FROM_FREQ(f) (round(1000000 * (1.0 / f))) // Calculate period in microseconds (us) from frequency in Hz #define FREQ_FROM_PERIOD(p) (1.0 / p * 1000000) // Calculate frequency in Hz given the period in microseconds (us) @@ -162,18 +162,12 @@ // of the OLED/LCD is now controlled separately, but M5 is always equipped // with one (but it doesn't have to be used!). -#if M5STICKC -#include "M5StickC.h" -#undef min // They define a min() on us +#if M5STICKC || M5STICKCPLUS || M5STACKCORE2 || M5STICKCPLUS2 + #define USE_M5 1 #endif -#if M5STICKCPLUS -#include "M5StickCPlus.h" -#undef min // They define a min() on us -#endif - -#if M5STACKCORE2 -#include "M5Core2.h" +#if USE_M5 +#include "M5Unified.h" #undef min // They define a min() on us #endif @@ -276,7 +270,7 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define ENABLE_NTP 0 // Set the clock from the web #define ENABLE_OTA 0 // Accept over the air flash updates - #if M5STICKC || M5STICKCPLUS || M5STACKCORE2 + #if USE_M5 #define LED_PIN0 32 #elif LILYGOTDISPLAYS3 #define LED_PIN0 21 @@ -320,7 +314,7 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define ENABLE_AUDIO 1 // Listen for audio from the microphone and process it #define COLORDATA_SERVER_ENABLED 0 #define MIN_VU 20 - #define NOISE_CUTOFF 10 + #define NOISE_CUTOFF 1000 #if USE_PSRAM #define MAX_BUFFERS 500 @@ -330,7 +324,7 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define DEFAULT_EFFECT_INTERVAL (60*60*24*5) - #if M5STICKC || M5STICKCPLUS || M5STACKCORE2 + #if USE_M5 #define LED_PIN0 32 #elif LILYGOTDISPLAYS3 #define LED_PIN0 21 @@ -341,7 +335,7 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define TOGGLE_BUTTON_1 37 #define TOGGLE_BUTTON_2 39 - #if M5STICKC || M5STICKCPLUS || M5STACKCORE2 + #if USE_M5 #define LED_PIN0 32 #elif LILYGOTDISPLAYS3 #define LED_PIN0 21 @@ -382,7 +376,7 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #if M5STICKC #define LED_PIN0 33 - #elif M5STICKCPLUS || M5STACKCORE2 + #elif M5STICKCPLUS || M5STACKCORE2 || M5STICKCPLUS2 #define LED_PIN0 32 #else #define LED_PIN0 5 @@ -400,6 +394,55 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define TOGGLE_BUTTON_1 37 #define TOGGLE_BUTTON_2 39 +#elif PDPGRID + + // A matrix grid display for the front of the PDP-11 + + #ifndef PROJECT_NAME + #define PROJECT_NAME "PDPGrid" + #endif + + #define NUM_FANS 1 + #define NUM_RINGS 4 + #define FAN_SIZE (RING_SIZE_0 + RING_SIZE_1 + RING_SIZE_2 + RING_SIZE_3) + #define RING_SIZE_0 16 + #define RING_SIZE_1 12 + #define RING_SIZE_2 8 + #define RING_SIZE_3 1 + #define MATRIX_WIDTH 14 + #define MATRIX_HEIGHT 16 + #define NUM_LEDS (MATRIX_WIDTH*MATRIX_HEIGHT) + #define NUM_CHANNELS 1 + #define ENABLE_AUDIO 1 + + #define POWER_LIMIT_MW 1000 + + // Once you have a working project, selectively enable various additional features by setting + // them to 1 in the list below. This config assumes no audio (mic), or screen, etc. + + #define ENABLE_WIFI 1 // Connect to WiFi + #define INCOMING_WIFI_ENABLED 1 // Accepting incoming color data and commands + #define TIME_BEFORE_LOCAL 1 // How many seconds before the lamp times out and shows local contexnt + #define ENABLE_NTP 1 // Set the clock from the web + #define ENABLE_OTA 1 // Accept over the air flash updates + #define ENABLE_WEBSERVER 1 // Turn on the internal webserver + + #define LED_PIN0 32 + + #define MIN_VU 280 + #define NOISE_CUTOFF 1000 + #define NOISE_FLOOR 2000 + + // The webserver serves files that are baked into the device firmware. When running you should be able to + // see/select the list of effects by visiting the chip's IP in a browser. You can get the chip's IP by + // watching the serial output or checking your router for the DHCP given to a new device; often they're + // named "esp32-" followed by a seemingly random 6-digit hexadecimal number. + + #define DEFAULT_EFFECT_INTERVAL 0 + + #define TOGGLE_BUTTON_1 37 + #define TOGGLE_BUTTON_2 39 + #elif TREESET #ifndef PROJECT_NAME @@ -540,6 +583,8 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define COLOR_ORDER EOrder::RGB + #define MIN_VU 80 + #elif TTGO // Variant of Spectrum set up for a TTGO using a MAX4466 microphone on pin27 @@ -632,12 +677,13 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define WAIT_FOR_WIFI 0 // Hold in setup until we have WiFi - for strips without effects #define TIME_BEFORE_LOCAL 3 // How many seconds before the lamp times out and shows local content - #define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT) #define MAX_BUFFERS 30 // Times 4 channels, but they're only NUM_LEDS big #define NUM_CHANNELS 4 // One per spoke #define MATRIX_WIDTH 53 // Number of pixels wide (how many LEDs per channel) #define MATRIX_HEIGHT 1 // Number of pixels tall + #define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT) #define ENABLE_REMOTE 1 // IR Remote Control + #define IR_REMOTE_PIN 35 // Eric's is PIN 35 #define ENABLE_AUDIO 1 // Listen for audio from the microphone and process it #define USE_SCREEN 0 // Normally we use a tiny board inside the lamp with no screen #define FAN_SIZE NUM_LEDS // Allows us to use fan effects on the spokes @@ -646,8 +692,6 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define LED_FAN_OFFSET_BU 0 #define BONUS_PIXELS 0 - #define IR_REMOTE_PIN 15 // Eric's is PIN 35 - // Original Wiring: // Fine red = 3.3v // brown = gnd @@ -657,7 +701,7 @@ extern RemoteDebug Debug; // Let everyone in the project know about it // blue = IO12 // purple = IO4 - // Eric's Version Wiring is the same. Which is a complete coincidence but handy! + // Wiring is: #define LED_PIN0 5 #define LED_PIN1 16 @@ -666,6 +710,45 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define DEFAULT_EFFECT_INTERVAL (1000*60*5) +#elif SPIRALLAMP + + // This is the "Tiki Atomic Fire Lamp" project, which is an LED lamp with 4 arms of 53 LEDs each. + // Each arm is wired as a separate channel. + + #ifndef PROJECT_NAME + #define PROJECT_NAME "Spiral Light" + #endif + + #define ENABLE_WIFI 1 // Connect to WiFi + #define INCOMING_WIFI_ENABLED 1 // Accepting incoming color data and commands + #define WAIT_FOR_WIFI 0 // Hold in setup until we have WiFi - for strips without effects + #define TIME_BEFORE_LOCAL 3 // How many seconds before the lamp times out and shows local content + + #define MAX_BUFFERS 30 // Times 4 channels, but they're only NUM_LEDS big + #define NUM_CHANNELS 2 // One per spoke + #define MATRIX_WIDTH 172 // Number of pixels wide (how many LEDs per channel) + #define MATRIX_HEIGHT 1 // Number of pixels tall + #define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT) + #define ENABLE_REMOTE 1 // IR Remote Control + #define IR_REMOTE_PIN 26 + #define ENABLE_AUDIO 1 // Listen for audio from the microphone and process it + #define USE_SCREEN 1 // Normally we use a tiny board inside the lamp with no screen + #define FAN_SIZE NUM_LEDS // Allows us to use fan effects on the spokes + #define NUM_FANS 1 // Our fans are on channels, not in sequential order, so only one "fan" + #define NUM_RINGS 1 + #define LED_FAN_OFFSET_BU 0 + #define BONUS_PIXELS 0 + + // Wiring is: + + #define TOGGLE_BUTTON_1 39 + #define TOGGLE_BUTTON_2 37 + + #define LED_PIN0 32 + #define LED_PIN1 33 + + #define DEFAULT_EFFECT_INTERVAL (1000*60*5) + #elif UMBRELLA #ifndef PROJECT_NAME @@ -708,8 +791,8 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define PROJECT_NAME "Magic Mirror" #endif - #define ENABLE_WIFI 1 // Connect to WiFi - #define INCOMING_WIFI_ENABLED 1 // Accepting incoming color data and commands + #define ENABLE_WIFI 0 // Connect to WiFi + #define INCOMING_WIFI_ENABLED 0 // Accepting incoming color data and commands #define WAIT_FOR_WIFI 0 // Hold in setup until we have WiFi - for strips without effects #define TIME_BEFORE_LOCAL 1 // How many seconds before the lamp times out and shows local content @@ -863,14 +946,13 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define ENABLE_OTA 0 // Accept over the air flash updates #define ENABLE_REMOTE 1 // IR Remote Control #define ENABLE_AUDIO 1 // Listen for audio from the microphone and process it - #define COLORDATA_SERVER_ENABLED 0 #if USE_PSRAM #define INCOMING_WIFI_ENABLED 1 // Accepting incoming color data and commands #define COLORDATA_SERVER_ENABLED 1 #define MAX_BUFFERS 500 #else - #define INCOMING_WIFI_ENABLED 0 // Accepting incoming color data and commands + #define INCOMING_WIFI_ENABLED 0 // Do not accept incoming color data and commands #define COLORDATA_SERVER_ENABLED 0 #define MIN_BUFFERS 1 #define MAX_BUFFERS 1 @@ -903,14 +985,65 @@ extern RemoteDebug Debug; // Let everyone in the project know about it // The mic in the M5 is not quite the same as the Mesmerizer, so it gets a different minimum VU than default - #define MIN_VU 280 - #define NOISE_CUTOFF 1000 + #define MIN_VU 150 + #define NOISE_CUTOFF 100 #if !(ELECROW) #define TOGGLE_BUTTON_1 37 #define TOGGLE_BUTTON_2 39 #endif +#elif HELMET + + #ifndef PROJECT_NAME + #define PROJECT_NAME "Helmet" + #endif + + #define POWER_LIMIT_MW 1000 + + #define ENABLE_AUDIOSERIAL 0 // Report peaks at 2400baud on serial port for PETRock consumption + #define ENABLE_WIFI 1 // Connect to WiFi + #define WAIT_FOR_WIFI 0 // Hold in setup until we have WiFi - for strips without effects + #define TIME_BEFORE_LOCAL 2 // How many seconds before the lamp times out and shows local content + #define ENABLE_WEBSERVER 1 // Turn on the internal webserver + #define ENABLE_NTP 1 // Set the clock from the web + #define ENABLE_OTA 0 // Accept over the air flash updates + #define ENABLE_REMOTE 1 // IR Remote Control + #define ENABLE_AUDIO 1 // Listen for audio from the microphone and process it + + #if USE_PSRAM + #define INCOMING_WIFI_ENABLED 1 // Accepting incoming color data and commands + #define COLORDATA_SERVER_ENABLED 1 + #define MAX_BUFFERS 500 + #else + #define INCOMING_WIFI_ENABLED 0 // Do not accept incoming color data and commands + #define COLORDATA_SERVER_ENABLED 0 + #define MIN_BUFFERS 1 + #define MAX_BUFFERS 1 + #endif + + #define DEFAULT_EFFECT_INTERVAL 0 // Do not auto-advance unless the button is presssed + + #define LED_PIN0 26 + #define NUM_CHANNELS 1 + #define RING_SIZE_0 24 + #define BONUS_PIXELS 0 + #define MATRIX_WIDTH 32 + #define MATRIX_HEIGHT 8 + #define NUM_FANS MATRIX_WIDTH + #define FAN_SIZE MATRIX_HEIGHT + #define NUM_BANDS 16 + #define NUM_LEDS (MATRIX_WIDTH*MATRIX_HEIGHT) + #define LED_FAN_OFFSET_BU 6 + + // The mic in the M5 is not quite the same as the Mesmerizer, so it gets a different minimum VU than default + + #define MIN_VU 280 + #define NOISE_CUTOFF 1000 + + #define TOGGLE_BUTTON_1 39 + #define TOGGLE_BUTTON_2 37 + #elif FANSET // An M5 stick that controls the 10 RGB fans in my PC @@ -1156,16 +1289,16 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define NUM_BANDS 16 #endif #ifndef NOISE_FLOOR - #define NOISE_FLOOR (MAX_VU / 2) + #define NOISE_FLOOR 4000 #endif #ifndef NOISE_CUTOFF - #define NOISE_CUTOFF 2000 + #define NOISE_CUTOFF 1000 #endif #ifndef AUDIO_PEAK_REMOTE_TIMEOUT #define AUDIO_PEAK_REMOTE_TIMEOUT 1000.0f // How long after remote PeakData before local microphone is used again #endif #ifndef ENABLE_AUDIO_SMOOTHING - #define ENABLE_AUDIO_SMOOTHING 1 + #define ENABLE_AUDIO_SMOOTHING 0 #endif #ifndef BARBEAT_ENHANCE #define BARBEAT_ENHANCE 0.3 // How much the SpectrumAnalyzer "pulses" with the music @@ -1300,15 +1433,7 @@ extern RemoteDebug Debug; // Let everyone in the project know about it #define USE_OLED 1 // Enable the Heltec's monochrome OLED - #elif M5STICKCPLUS // screen definitions for m5stick-c-plus - - #define USE_M5DISPLAY 1 // enable the M5's LCD screen - - #elif M5STICKC // screen definitions for m5stick-c (or m5stick-c plus) - - #define USE_M5DISPLAY 1 // enable the M5's LCD screen - - #elif M5STACKCORE2 // screen definitions for m5stick-c (or m5stick-c plus) + #elif USE_M5 // screen definitions for m5stick-c-plus #define USE_M5DISPLAY 1 // enable the M5's LCD screen @@ -1401,7 +1526,10 @@ extern RemoteDebug Debug; // Let everyone in the project know about it extern DRAM_ATTR const int g_aRingSizeTable[]; -#define MICROS_PER_SECOND 1000000UL +#ifndef MICROS_PER_SECOND + #define MICROS_PER_SECOND 1000000 +#endif + #define MILLIS_PER_SECOND 1000 #define MICROS_PER_MILLI 1000 @@ -1413,6 +1541,10 @@ extern DRAM_ATTR const int g_aRingSizeTable[]; #define M5STICKCPLUS 0 #endif +#ifndef M5STICKCPLUS2 +#define M5STICKCPLUS2 0 +#endif + #ifndef M5STACKCORE2 #define M5STACKCORE2 0 #endif @@ -1435,10 +1567,7 @@ extern DRAM_ATTR const int g_aRingSizeTable[]; #define INPUT_PIN (36) #elif ELECROW #define INPUT_PIN (41) - #elif M5STACKCORE2 - #define INPUT_PIN (0) - #define IO_PIN (0) - #elif M5STICKC || M5STICKCPLUS + #elif USE_M5 #define INPUT_PIN (34) #define IO_PIN (0) #else @@ -1606,6 +1735,47 @@ inline bool SetSocketBlockingEnabled(int fd, bool blocking) return (fcntl(fd, F_SETFL, flags) == 0) ? true : false; } +// formatSize +// +// Returns a string with the size formatted in a human readable format. +// For example, 1024 becomes "1K", 1000*1000 becomes "1M", etc. +// It pains me not to use 1024, but such are the times we live in. + +inline String formatSize(size_t size, size_t threshold = 1000) +{ + // If the size is less than the threshold, we don't need to worry about precision because + // we'll be showing whole units + const int precision = size >= threshold ? 2 : 0; + + const char* suffixes[] = {"", "K", "M", "G", "T", "P", "E", "Z"}; + size_t suffixIndex = 0; + double sizeDouble = static_cast(size); + + while (sizeDouble >= threshold && suffixIndex < (sizeof(suffixes) / sizeof(suffixes[0])) - 1) + { + sizeDouble /= 1000; + ++suffixIndex; + } + + std::ostringstream oss; + oss << std::fixed << std::setprecision(precision) << sizeDouble << suffixes[suffixIndex]; + return String(oss.str().c_str()); +} + +// to_array +// +// Because the ESP32 compiler, as of this writing, doesn't have std::to_array, we provide our own (davepl). +// BUGBUG: Once we have compiler support we shoud use the C++20 versions + +template +constexpr std::array to_array(const T (&arr)[N]) { + std::array result{}; + for (std::size_t i = 0; i < N; ++i) { + result[i] = arr[i]; + } + return result; +} + // 16-bit (5:6:5) color definitions for common colors #define BLACK16 0x0000 diff --git a/include/ledstripeffect.h b/include/ledstripeffect.h index e551ee8f7..acbc1e74e 100644 --- a/include/ledstripeffect.h +++ b/include/ledstripeffect.h @@ -343,7 +343,7 @@ class LEDStripEffect : public IJSONSerializable CRGB::Indigo, CRGB::Violet }; - int randomColorIndex = random_range(0U, ARRAYSIZE(colors)); + int randomColorIndex = random_range(0U, std::size(colors)); return colors[randomColorIndex]; } @@ -411,6 +411,16 @@ class LEDStripEffect : public IJSONSerializable } } + // SetPixelsFOnAllChannels + // + // Smooth drawing on fractional pixels on all channels in the given color; if merge is specified, + + void setPixelsFOnAllChannels(float fPos, float count, CRGB c, bool bMerge = false) + { + for (auto& device : _GFX) + device->setPixelsF(fPos, count, c, bMerge); + } + // ClearFrameOnAllChannels // // Clears ALL the channels @@ -496,6 +506,11 @@ class LEDStripEffect : public IJSONSerializable device->setPixel(i, c); } + void setPixelOnAllChannels(int x, int y, CRGB c) + { + for (auto& device : _GFX) + device->setPixel(x, y, c); + } // setPixelsOnAllChannels // // Smooth drawing on fractional pixels on all channels in the given color; if merge is specified, diff --git a/include/screen.h b/include/screen.h index c6e5b2853..085157ceb 100644 --- a/include/screen.h +++ b/include/screen.h @@ -117,7 +117,7 @@ class Screen : public GFXBase // // Display code for the M5 based TFT displays on the M5 Stick, Stick C Plus, and Stack - #include + #include // M5Screen // @@ -125,16 +125,11 @@ class Screen : public GFXBase class M5Screen : public Screen { - private: - - M5Display m5display; - public: M5Screen(int w, int h) : Screen(w, h) { M5.Lcd.fillScreen(GREEN16); - M5.Lcd.setRotation(1); } virtual void drawPixel(int16_t x, int16_t y, uint16_t color) override diff --git a/include/socketserver.h b/include/socketserver.h index 2603e58b3..44ccda1e5 100644 --- a/include/socketserver.h +++ b/include/socketserver.h @@ -112,7 +112,7 @@ class SocketServer _server_fd(-1), _cbReceived(0) { - _abOutputBuffer.reset( psram_allocator().allocate(MAXIMUM_PACKET_SIZE+1) ); // +1 for uzlib one byte overreach bug + _abOutputBuffer.reset( psram_allocator().allocate(MAXIMUM_PACKET_SIZE+1) ); // +1 for uzlib one byte overreach bug memset(&_address, 0, sizeof(_address)); } @@ -175,6 +175,7 @@ class SocketServer void ResetReadBuffer() { _cbReceived = 0; + memset(_pBuffer.get(), 0, MAXIMUM_PACKET_SIZE); } // ReadUntilNBytesReceived @@ -204,7 +205,11 @@ class SocketServer // Read data from the socket until we have _bcNeeded bytes in the buffer - int cbRead = read(socket, (uint8_t *) _pBuffer.get() + _cbReceived, cbNeeded - _cbReceived); + int cbRead = 0; + do + { + cbRead = read(socket, (uint8_t *) _pBuffer.get() + _cbReceived, cbNeeded - _cbReceived); + } while (cbRead < 0 && errno == EINTR); // Restore the old state @@ -226,7 +231,7 @@ class SocketServer // Socket server main ProcessIncomingConnectionsLoop - accepts new connections and reads from them, dispatching // data packets into our buffer and closing the socket if anything goes weird. - int ProcessIncomingConnectionsLoop(); + bool ProcessIncomingConnectionsLoop(); // DecompressBuffer // diff --git a/include/soundanalyzer.h b/include/soundanalyzer.h index a8cd45dc5..2d61fdf72 100644 --- a/include/soundanalyzer.h +++ b/include/soundanalyzer.h @@ -60,7 +60,7 @@ struct AudioVariables float _VURatio = 1.0; // Current VU as a ratio to its recent min and max float _VURatioFade = 1.0; // Same as gVURatio but with a slow decay float _VU = 0.0; // Instantaneous read of VU value - float _PeakVU = MAX_VU; // How high our peak VU scale is in live mode + float _PeakVU = 0.0; // How high our peak VU scale is in live mode float _MinVU = 0.0; // How low our peak VU scale is in live mode unsigned long _cSamples = 0U; // Total number of samples successfully collected int _AudioFPS = 0; // Framerate of the audio sampler @@ -120,7 +120,8 @@ class PeakData { MESMERIZERMIC, PCREMOTE, - M5 + M5, + M5PLUS2 } MicrophoneType; PeakData() @@ -153,19 +154,26 @@ class PeakData { case MESMERIZERMIC: { - static const float Scalars16[16] = {0.4, .5, 0.75, 1.0, 0.6, 0.6, 0.8, 0.8, 1.2, 1.5, 3.0, 3.0, 3.0, 3.0, 3.5, 3.5}; // {0.08, 0.12, 0.3, 0.35, 0.35, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.4, 1.4, 1.0, 1.0, 1.0}; + static constexpr std::array Scalars16 = {0.4, .5, 0.75, 1.0, 0.6, 0.6, 0.8, 0.8, 1.2, 1.5, 3.0, 3.0, 3.0, 3.0, 3.5, 2.5}; // {0.08, 0.12, 0.3, 0.35, 0.35, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.4, 1.4, 1.0, 1.0, 1.0}; float result = (NUM_BANDS == 16) ? Scalars16[i] : map(i, 0, NUM_BANDS - 1, 1.0, 1.0); return result; } case PCREMOTE: { - static const float Scalars16[16] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + + static constexpr std::array Scalars16 = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + float result = (NUM_BANDS == 16) ? Scalars16[i] : map(i, 0, NUM_BANDS - 1, 1.0, 1.0); + return result; + } + case M5PLUS2: + { + static constexpr std::array Scalars16 = {0.3, .5, 0.8, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.7, 0.7, 0.7}; float result = (NUM_BANDS == 16) ? Scalars16[i] : map(i, 0, NUM_BANDS - 1, 1.0, 1.0); return result; } default: { - static const float Scalars16[16] = {0.4, .35, 0.6, 0.8, 1.2, 0.7, 1.2, 1.6, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 4.0, 5.0}; // {0.08, 0.12, 0.3, 0.35, 0.35, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.4, 1.4, 1.0, 1.0, 1.0}; + static constexpr std::array Scalars16 = {0.5, .5, 0.8, 1.0, 1.5, 1.2, 1.5, 1.6, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 5.0, 2.5}; float result = (NUM_BANDS == 16) ? Scalars16[i] : map(i, 0, NUM_BANDS - 1, 1.0, 1.0); return result; } @@ -193,17 +201,17 @@ class PeakData class SoundAnalyzer : public AudioVariables { - static const size_t MAX_SAMPLES = 256; + static constexpr size_t MAX_SAMPLES = 256; std::unique_ptr ptrSampleBuffer; // I'm old enough I can only hear up to about 12K, but feel free to adjust. Remember from // school that you need to sample at double the frequency you want to process, so 24000 is 12K - static const size_t SAMPLING_FREQUENCY = 20000; - static const size_t LOWEST_FREQ = 40; - static const size_t HIGHEST_FREQ = SAMPLING_FREQUENCY / 2; + static constexpr size_t SAMPLING_FREQUENCY = 20000; + static constexpr size_t LOWEST_FREQ = 40; + static constexpr size_t HIGHEST_FREQ = SAMPLING_FREQUENCY / 2; - static const size_t _sampling_period_us = PERIOD_FROM_FREQ(SAMPLING_FREQUENCY); + static constexpr size_t _sampling_period_us = PERIOD_FROM_FREQ(SAMPLING_FREQUENCY); int _cutOffsBand[NUM_BANDS]; // The upper frequency for each band float _oldVU; // Old VU value for damping @@ -211,7 +219,7 @@ class SoundAnalyzer : public AudioVariables float _oldMinVU; // Old min VU value for damping double * _vPeaks; // The peak value for each band - PeakData::MicrophoneType _MicMode = PeakData::M5; + PeakData::MicrophoneType _MicMode; // GetBandIndex // @@ -265,10 +273,9 @@ class SoundAnalyzer : public AudioVariables { arduinoFFT _FFT(_vReal, _vImaginary, MAX_SAMPLES, SAMPLING_FREQUENCY); _FFT.DCRemoval(); - _FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD); + _FFT.Windowing(FFT_WIN_TYP_BLACKMAN, FFT_FORWARD); _FFT.Compute(FFT_FORWARD); _FFT.ComplexToMagnitude(); - _FFT.MajorPeak(); } void FillBufferI2S() @@ -277,12 +284,13 @@ class SoundAnalyzer : public AudioVariables size_t bytesRead = 0; - #if M5STICKC || M5STICKCPLUS || M5STACKCORE2 || ELECROW - ESP_ERROR_CHECK(i2s_read(I2S_NUM_0, (void *)ptrSampleBuffer.get(), bytesExpected, &bytesRead, (100 / portTICK_RATE_MS))); + #if USE_M5 + if (M5.Mic.record((int16_t *)ptrSampleBuffer.get(), MAX_SAMPLES, SAMPLING_FREQUENCY, false)) + bytesRead = bytesExpected; #else - ESP_ERROR_CHECK(i2s_adc_enable(EXAMPLE_I2S_NUM)); - ESP_ERROR_CHECK(i2s_read(EXAMPLE_I2S_NUM, (void *) ptrSampleBuffer.get(), bytesExpected, &bytesRead, (100 / portTICK_RATE_MS))); - ESP_ERROR_CHECK(i2s_adc_disable(EXAMPLE_I2S_NUM)); + ESP_ERROR_CHECK(i2s_start(EXAMPLE_I2S_NUM)); + ESP_ERROR_CHECK(i2s_read(EXAMPLE_I2S_NUM, (void *) ptrSampleBuffer.get(), bytesExpected, &bytesRead, 100 / portTICK_RATE_MS)); + ESP_ERROR_CHECK(i2s_stop(EXAMPLE_I2S_NUM)); #endif if (bytesRead != bytesExpected) @@ -292,7 +300,9 @@ class SoundAnalyzer : public AudioVariables } for (int i = 0; i < MAX_SAMPLES; i++) + { _vReal[i] = ptrSampleBuffer[i]; + } } // UpdateVU @@ -349,33 +359,38 @@ class SoundAnalyzer : public AudioVariables double averageSum = 0.0f; - int hitCount[NUM_BANDS] = {0}; for (int i = 0; i < NUM_BANDS; i++) _vPeaks[i] = 0.0f; for (int i = 2; i < MAX_SAMPLES / 2; i++) { + #if USE_M5 + // The M5 Mic returns some large vales, so we normalize here + _vReal[i] = _vReal[i] / MAX_SAMPLES; + #endif + int freq = GetBucketFrequency(i-2); if (freq >= LOWEST_FREQ) { // Track the average and the peak value - averageSum += _vReal[i]; + double vVal = _vReal[i]; + averageSum += vVal; // If it's above the noise floor, figure out which band this belongs to and // if it's a new peak for that band, record that fact int iBand = GetBandIndex(freq); - _vPeaks[iBand] += _vReal[i]; - hitCount[iBand]++; + if (vVal > _vPeaks[iBand]) + _vPeaks[iBand] = _vReal[i]; } } + averageSum = averageSum / (MAX_SAMPLES / 2 - 2); // Noise gate - if the signal in this band is below a threshold we define, then we say there's no energy in this band for (int i = 0; i < NUM_BANDS; i++) { - _vPeaks[i] /= std::max(1, hitCount[i]); // max to avoid div by zero error _vPeaks[i] *= PeakData::GetBandScalar(_MicMode, i); if (_vPeaks[i] < NOISE_CUTOFF) _vPeaks[i] = 0.0f; @@ -406,15 +421,21 @@ class SoundAnalyzer : public AudioVariables // just triggering the bottom pixel, and real silence yielding darkness allBandsPeak = std::max((double)NOISE_FLOOR, allBandsPeak); - debugV("All Bands Peak: %f", allBandsPeak); + debugV("All Bands Peak: %lf", allBandsPeak); // Normalize all the bands relative to allBandsPeak for (int i = 0; i < NUM_BANDS; i++) _vPeaks[i] /= allBandsPeak; + if (_VURatio < 1.0) + + for (int i = 0; i < NUM_BANDS; i++) + _vPeaks[i] = max(0.0, _vPeaks[i]); + // We'll use the average as the gVU. I assume the average of the samples tracks sound pressure level, but don't really know... - float newval = averageSum / (MAX_SAMPLES / 2 - 2); + float newval = averageSum; + debugV("AverageSum : %f", averageSum); debugV("Newval : %f", newval); @@ -423,7 +444,7 @@ class SoundAnalyzer : public AudioVariables EVERY_N_MILLISECONDS(100) { auto peaks = GetPeakData(); - debugV("Audio Data -- Sum: %0.2f, _MinVU: %f0.2, _PeakVU: %f0.2, _VU: %f, Peak0: %f, Peak1: %f, Peak2: %f, Peak3: %f", averageSum, _MinVU, _PeakVU, _VU, peaks[0], peaks[1], peaks[2], peaks[3]); + debugV("Audio Data -- Sum: %0.2f, _MinVU: %0.2f, _PeakVU: %0.2f, _VU: %f, Peak0: %f, Peak1: %f, Peak2: %f, Peak3: %f", averageSum, _MinVU, _PeakVU, _VU, peaks[0], peaks[1], peaks[2], peaks[3]); } return PeakData(_vPeaks); @@ -437,8 +458,10 @@ class SoundAnalyzer : public AudioVariables { if (NUM_BANDS == 16) { - static const int cutOffs16Band[16] = - {200, 380, 580, 800, 980, 1200, 1360, 1584, 1996, 2412, 3162, 3781, 5312, 6310, 8400, (int)HIGHEST_FREQ}; + static constexpr std::array cutOffs16Band = + { + 200, 380, 580, 780, 980, 1200, 1600, 1800, 2000, 2412, 3162, 3781, 5312, 6310, 8400, (int)HIGHEST_FREQ + }; for (int i = 0; i < NUM_BANDS; i++) _cutOffsBand[i] = cutOffs16Band[i]; @@ -488,7 +511,7 @@ class SoundAnalyzer : public AudioVariables // BeatEnhance // - // Looks like pure voodoo, but it returns the multiplier by which to scale a vale to enhance it + // Looks like pure voodoo, but it returns the multiplier by which to scale a value to enhance it // by the current VURatioFade amount. The amt amount is the amount of your factor that should be // made up of the VURatioFade multiplier. So passing a 0.75 is a lot of beat enhancement, whereas // 0.25 is a little bit. @@ -505,61 +528,20 @@ class SoundAnalyzer : public AudioVariables debugV("Begin SamplerBufferInitI2S..."); - #if M5STACKCORE2 - - esp_err_t err = ESP_OK; - - i2s_driver_uninstall(Speak_I2S_NUMBER); // Uninstall the I2S driver. 卸载I2S驱动 - i2s_config_t i2s_config = - { - .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM), - .sample_rate = SAMPLING_FREQUENCY, // Set the I2S sampling rate. - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // Fixed 12-bit stereo MSB. - .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, // Set the channel format. - .communication_format = I2S_COMM_FORMAT_STAND_I2S, // Set the format of the communication. - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // Set the interrupt flag. - .dma_buf_count = 2, // DMA buffer count. - .dma_buf_len = 256, // DMA buffer length. - }; - - err += i2s_driver_install(Speak_I2S_NUMBER, &i2s_config, 0, NULL); - - i2s_pin_config_t tx_pin_config; - tx_pin_config.mck_io_num = I2S_PIN_NO_CHANGE; - tx_pin_config.bck_io_num = CONFIG_I2S_BCK_PIN; // Link the BCK to the CONFIG_I2S_BCK_PIN pin. - tx_pin_config.ws_io_num = CONFIG_I2S_LRCK_PIN; - tx_pin_config.data_out_num = CONFIG_I2S_DATA_PIN; - tx_pin_config.data_in_num = CONFIG_I2S_DATA_IN_PIN; - err += i2s_set_pin(Speak_I2S_NUMBER, &tx_pin_config); // Set the I2S pin number. - err += i2s_set_clk(Speak_I2S_NUMBER, SAMPLING_FREQUENCY, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO); // Set the clock and bitwidth used by I2S Rx and Tx. - - #elif M5STICKC || M5STICKCPLUS - - i2s_config_t i2s_config = - { - .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM), - .sample_rate = SAMPLING_FREQUENCY, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // is fixed at 12bit, stereo, MSB - .channel_format = I2S_CHANNEL_FMT_ALL_RIGHT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, // Set the format of the communication. - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 2, - .dma_buf_len = 256, - }; - - i2s_pin_config_t pin_config; - - pin_config.mck_io_num = I2S_PIN_NO_CHANGE; - pin_config.bck_io_num = I2S_PIN_NO_CHANGE; - pin_config.ws_io_num = IO_PIN; - pin_config.data_out_num = I2S_PIN_NO_CHANGE; - pin_config.data_in_num = INPUT_PIN; - - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_pin(I2S_NUM_0, &pin_config); - i2s_set_clk(I2S_NUM_0, SAMPLING_FREQUENCY, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO); - -#elif ELECROW + #if USE_M5 + + auto miccfg = M5.Mic.config(); + miccfg.over_sampling = 4; + miccfg.magnification = 1; + miccfg.dma_buf_count = 2; + miccfg.dma_buf_len = MAX_SAMPLES; + miccfg.sample_rate = SAMPLING_FREQUENCY; + miccfg.use_adc = false; + M5.Mic.config(miccfg); + + M5.Mic.begin(); + + #elif ELECROW const i2s_config_t i2s_config = { .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX), @@ -585,7 +567,7 @@ class SoundAnalyzer : public AudioVariables ESP_ERROR_CHECK( i2s_set_pin(I2S_NUM_0, &pin_config) ); ESP_ERROR_CHECK( i2s_start(I2S_NUM_0) ); -#elif TTGO || MESMERIZER || SPECTRUM_WROVER_KIT + #elif TTGO || MESMERIZER || SPECTRUM_WROVER_KIT i2s_config_t i2s_config; i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN); @@ -603,7 +585,7 @@ class SoundAnalyzer : public AudioVariables ESP_ERROR_CHECK(i2s_driver_install(EXAMPLE_I2S_NUM, &i2s_config, 0, NULL)); ESP_ERROR_CHECK(i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL)); -#else + #else i2s_config_t i2s_config; i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN); @@ -621,7 +603,7 @@ class SoundAnalyzer : public AudioVariables ESP_ERROR_CHECK(i2s_driver_install(EXAMPLE_I2S_NUM, &i2s_config, 0, NULL)); ESP_ERROR_CHECK(i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL)); -#endif + #endif debugV("SamplerBufferInitI2S Complete\n"); } @@ -703,7 +685,9 @@ class SoundAnalyzer : public AudioVariables { if (millis() - _msLastRemote > AUDIO_PEAK_REMOTE_TIMEOUT) { - #if M5STICKC || M5STICKCPLUS || M5STACKCORE2 + #if M5STICKCPLUS2 + _MicMode = PeakData::M5PLUS2; + #elif USE_M5 _MicMode = PeakData::M5; #else _MicMode = PeakData::MESMERIZERMIC; diff --git a/include/taskmgr.h b/include/taskmgr.h index 12c919da4..88867db66 100644 --- a/include/taskmgr.h +++ b/include/taskmgr.h @@ -86,14 +86,14 @@ class IdleTask int delta = millis() - _lastMeasurement; if (delta >= kMillisPerCalc) { - //Serial.printf("Core %u Spent %lu in delay during a window of %d for a ratio of %f\n", - // xPortGetCoreID(), counter, delta, (float)counter/delta); + // We've used up all the time, so calculate the idle ratio and reset the counter _idleRatio = ((float) counter / delta); _lastMeasurement = millis(); counter = 0; } else { + // Burn a little time and update the counter esp_task_wdt_reset(); delayMicroseconds(kMillisPerLoop*1000); counter += kMillisPerLoop; @@ -383,6 +383,7 @@ class NightDriverTaskManager : public TaskManager // Effect threads run with NET priority and on the NET core by default. It seems a sensible choice // because effect threads tend to pull things from the Internet that they want to show + TaskHandle_t StartEffectThread(EffectTaskFunction function, LEDStripEffect* pEffect, const char* name, UBaseType_t priority = NET_PRIORITY, BaseType_t core = NET_CORE) { // We use a raw pointer here just to cross the thread/task boundary. The EffectTaskEntry method diff --git a/include/types.h b/include/types.h index cba3542ea..1d9dad19e 100644 --- a/include/types.h +++ b/include/types.h @@ -241,11 +241,23 @@ inline void * PreferPSRAMAlloc(size_t s) if (psramInit()) { debugV("PSRAM Array Request for %u bytes\n", s); - return ps_malloc(s); + auto p = ps_malloc(s); + if (!p) + { + debugE("PSRAM Allocation failed for %u bytes\n", s); + throw std::bad_alloc(); + } + return p; } else { - return malloc(s); + auto p = malloc(s); + if (!p) + { + debugE("RAM Allocation failed for %u bytes\n", s); + throw std::bad_alloc(); + } + return p; } } diff --git a/platformio.ini b/platformio.ini index dd8a2bd18..639085f99 100644 --- a/platformio.ini +++ b/platformio.ini @@ -148,7 +148,7 @@ build_flags = -DUSE_SCREEN=1 -DM5STICKC=1 ${base.build_flags} lib_deps = ${base.lib_deps} - m5stack/M5StickC @ ^0.2.3 + https://github.com/m5stack/M5Unified [dev_m5stick-c-plus] extends = base @@ -159,7 +159,21 @@ build_flags = -DUSE_SCREEN=1 -DM5STICKCPLUS=1 ${base.build_flags} lib_deps = ${base.lib_deps} - m5stack/M5StickCPlus @ ^0.0.2 + https://github.com/m5stack/M5Unified + +[dev_m5stick-c-plus2] +extends = base +board = m5stick-c ; Requires the M5 Stick C Plus (note the Plus) +monitor_speed = 115200 +upload_speed = 2000000 +build_flags = -DUSE_SCREEN=1 + -DM5STICKCPLUS2=1 + ${base.build_flags} + ${psram_flags.build_flags} +lib_deps = ${base.lib_deps} + https://github.com/m5stack/M5Unified +board_build.partitions = config/partitions_custom_8M.csv +board_upload.flash_size = 8MB [dev_m5stack] extends = base @@ -171,7 +185,9 @@ build_flags = -DUSE_SCREEN=1 ${base.build_flags} ${psram_flags.build_flags} lib_deps = ${base.lib_deps} - https://github.com/m5stack/M5Core2.git + https://github.com/m5stack/M5Unified +board_build.partitions = config/partitions_custom_8M.csv +board_upload.flash_size = 8MB [dev_elecrow_mesmerizer] extends = dev_esp32-s3 @@ -496,6 +512,22 @@ build_flags = -DSPECTRUM=1 ${dev_m5stick-c-plus.build_flags} board_build.partitions = config/partitions_custom_noota.csv +[env:spectrum2] +extends = dev_m5stick-c-plus2 +build_flags = -DSPECTRUM=1 + -DSHOW_VU_METER=1 + ${remote_flags.build_flags} + ${dev_m5stick-c-plus2.build_flags} +board_build.partitions = config/partitions_custom_noota.csv + +[env:helmet] +extends = dev_m5stick-c-plus +build_flags = -DHELMET=1 + -DSHOW_VU_METER=1 + ${remote_flags.build_flags} + ${dev_m5stick-c-plus.build_flags} +board_build.partitions = config/partitions_custom_noota.csv + [env:spectrum_elecrow] extends = dev_elecrow_mesmerizer build_flags = -DSPECTRUM=1 @@ -520,7 +552,7 @@ build_flags = -DSPECTRUM=1 ${psram_flags.build_flags} ${remote_flags.build_flags} ${dev_m5stack.build_flags} -board_build.partitions = config/partitions_custom_noota.csv +board_build.partitions = config/partitions_custom_8M.csv ; Same as Spectrum but using a non-Plus M5 Stick (older version with smaller screen) [env:spectrumlite] @@ -575,6 +607,12 @@ build_flags = -DLANTERN=1 ${remote_flags.build_flags} ${dev_m5stick-c.build_flags} +[env:pdpgrid] +extends = dev_m5stick-c-plus2 +build_flags = -DPDPGRID=1 + ${remote_flags.build_flags} + ${dev_m5stick-c-plus2.build_flags} + [env:chieftain] extends = dev_tinypico build_flags = -DCHIEFTAIN=1 @@ -658,14 +696,22 @@ build_flags = -DINSULATORS=1 extends = dev_m5stick-c build_flags = -DMAGICMIRROR=1 ${dev_m5stick-c.build_flags} +board_build.partitions = config/partitions_custom_noota.csv [env:atomlight] -extends = dev_heltec_wifi +extends = dev_esp32 build_flags = -DATOMLIGHT=1 - ${dev_heltec_wifi.build_flags} + ${dev_esp32.build_flags} -UUSE_SCREEN ; Unset USE_SCREEN that is set in the device build flags. ; The ATOMLIGHT project really prefers not to use it. +[env:spirallamp] +extends = dev_m5stick-c-plus +build_flags = -DSPIRALLAMP=1 + ${remote_flags.build_flags} + ${dev_m5stick-c-plus.build_flags} +board_build.partitions = config/partitions_custom_noota.csv + [env:fanset] extends = dev_m5stick-c-plus build_flags = -DFANSET=1 diff --git a/samples/audioserver/audioserver.py b/samples/audioserver/audioserver.py index 47808e501..07a34b5fc 100755 --- a/samples/audioserver/audioserver.py +++ b/samples/audioserver/audioserver.py @@ -95,7 +95,7 @@ # Compute band values band_values = [] - for i in range(len(bands)-1): + for i in range(len(bands)-1): # BUGBUG RANGE stops one before band_start = np.searchsorted(freqs, bands[i]) band_stop = np.searchsorted(freqs, bands[i+1]) band_value = np.median(fft_data[band_start:band_stop]) diff --git a/samples/videoserver/VideoServer2.py b/samples/videoserver/VideoServer2.py index 1dcca604c..91c55dd42 100644 --- a/samples/videoserver/VideoServer2.py +++ b/samples/videoserver/VideoServer2.py @@ -28,9 +28,9 @@ MATRIX_WIDTH = 64 MATRIX_HEIGHT = 32 -FUTURE_DELAY = 5 -URL = "https://youtu.be/WPdCT0l6vGs" -ESP32_WIFI_ADDRESS = '192.168.8.127' +FUTURE_DELAY = 5 +URL = "https://youtu.be/dQw4w9WgXcQ" +ESP32_WIFI_ADDRESS = '192.168.8.87' PORT = 49152 WIFI_COMMAND_PIXELDATA64 = 3 @@ -102,7 +102,7 @@ def send_video_data(stream): sock.close() sock = None - time.sleep(1.0 / stream.fps / 2) + time.sleep(1.0 / stream.fps) # build_header # diff --git a/samples/videoserver/pcstats.py b/samples/videoserver/pcstats.py index 746b731ae..7b847a1f7 100644 --- a/samples/videoserver/pcstats.py +++ b/samples/videoserver/pcstats.py @@ -26,7 +26,7 @@ MATRIX_WIDTH = 64 MATRIX_HEIGHT = 32 FUTURE_DELAY = 3 -ESP32_WIFI_ADDRESS = '192.168.8.86' +ESP32_WIFI_ADDRESS = '192.168.8.235' PORT = 49152 WIFI_COMMAND_PIXELDATA64 = 3 FPS = 20 diff --git a/src/audio.cpp b/src/audio.cpp index 1dfcdc448..a8c87d44b 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -74,6 +74,8 @@ void IRAM_ATTR AudioSamplerTaskEntry(void *) // Instantaneous VURatio + assert(g_Analyzer._PeakVU >= g_Analyzer._MinVU); + g_Analyzer._VURatio = (g_Analyzer._PeakVU == g_Analyzer._MinVU) ? 0.0 : (g_Analyzer._VU - g_Analyzer._MinVU) / std::max(g_Analyzer._PeakVU - g_Analyzer._MinVU, (float) MIN_VU) * 2.0f; diff --git a/src/colordata.cpp b/src/colordata.cpp index 50b783a3e..9f118821f 100644 --- a/src/colordata.cpp +++ b/src/colordata.cpp @@ -87,31 +87,6 @@ DEFINE_GRADIENT_PALETTE(redorange_gp) }; const CRGBPalette16 redorange_pal = redorange_gp; - -// For LEDMatrixGFX::from16Bit color conversions -// -// These tables can't go in the .H file so we have this .CPP file for them instead - -const uint8_t GFXBase::gamma5[] = -{ - 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0b, - 0x0e, 0x11, 0x14, 0x18, 0x1d, 0x22, 0x28, 0x2e, - 0x36, 0x3d, 0x46, 0x4f, 0x59, 0x64, 0x6f, 0x7c, - 0x89, 0x97, 0xa6, 0xb6, 0xc7, 0xd9, 0xeb, 0xff -}; - -const uint8_t GFXBase::gamma6[] = -{ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, - 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x10, 0x12, 0x13, - 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x20, 0x22, 0x25, - 0x27, 0x2a, 0x2d, 0x30, 0x33, 0x37, 0x3a, 0x3e, - 0x41, 0x45, 0x49, 0x4d, 0x52, 0x56, 0x5b, 0x5f, - 0x64, 0x69, 0x6e, 0x74, 0x79, 0x7f, 0x85, 0x8b, - 0x91, 0x97, 0x9d, 0xa4, 0xab, 0xb2, 0xb9, 0xc0, - 0xc7, 0xcf, 0xd6, 0xde, 0xe6, 0xee, 0xf7, 0xff -}; - extern const TProgmemRGBPalette16 BlueHeatColors_p FL_PROGMEM = { 0x000000, diff --git a/src/effects.cpp b/src/effects.cpp index 9152b85a4..7fa4ef526 100644 --- a/src/effects.cpp +++ b/src/effects.cpp @@ -212,6 +212,10 @@ void LoadEffectFactories() ADD_EFFECT(EFFECT_STRIP_PALETTE, PaletteEffect, RainbowColors_p, 2.0f, 0.1, 0.0, 1.0, 0.0, LINEARBLEND, true, 1.0); ADD_EFFECT(EFFECT_STRIP_RAINBOW_FILL, RainbowFillEffect, 10, 32); + #elif PDPGRID + + ADD_EFFECT(EFFECT_MATRIX_PDPGRID, PDPGridEffect); + #elif LANTERN ADD_EFFECT(EFFECT_STRIP_FIRE, FireEffect, "Calm Fire", NUM_LEDS, 40, 5, 50, 3, 3, true, true); @@ -223,9 +227,9 @@ void LoadEffectFactories() #define EFFECT_SET_VERSION 6 // Bump version if default set changes in a meaningful way #endif - ADD_EFFECT(EFFECT_MATRIX_SPECTRUMBAR, SpectrumBarEffect, "Audiograph", 16, 4, 0); - ADD_EFFECT(EFFECT_MATRIX_SPECTRUM_ANALYZER, SpectrumAnalyzerEffect, "Spectrum", NUM_BANDS, spectrumBasicColors, false, 100, 0, 0.75, 0.75); - ADD_EFFECT(EFFECT_MATRIX_SPECTRUM_ANALYZER, SpectrumAnalyzerEffect, "AudioWave", MATRIX_WIDTH, CRGB(0,0,40), 0, 1.25, 1.25); + ADD_EFFECT(EFFECT_MATRIX_SPECTRUMBAR, SpectrumBarEffect, "Audiograph", 16, 4, 0); + ADD_EFFECT(EFFECT_MATRIX_SPECTRUM_ANALYZER, SpectrumAnalyzerEffect, "Spectrum", NUM_BANDS, spectrumAltColors, false, 0, 0, 1.6, 1.6); + ADD_EFFECT(EFFECT_MATRIX_SPECTRUM_ANALYZER, SpectrumAnalyzerEffect, "AudioWave", MATRIX_WIDTH, CRGB(0,0,40), 0, 1.25, 1.25, true); ADD_EFFECT(EFFECT_MATRIX_SMRADIAL_WAVE, PatternSMRadialWave); ADD_EFFECT(EFFECT_MATRIX_ANIMATEDGIF, PatternAnimatedGIF, "Fire Log", GIFIdentifier::Firelog); ADD_EFFECT(EFFECT_MATRIX_ANIMATEDGIF, PatternAnimatedGIF, "Pacman", GIFIdentifier::Pacman); @@ -236,7 +240,7 @@ void LoadEffectFactories() ADD_EFFECT(EFFECT_MATRIX_SMGAMMA, PatternSMGamma); ADD_EFFECT(EFFECT_MATRIX_ANIMATEDGIF, PatternAnimatedGIF, "Rings", GIFIdentifier::ThreeRings); ADD_EFFECT(EFFECT_MATRIX_ANIMATEDGIF, PatternAnimatedGIF, "Atomic", GIFIdentifier::Atomic); - ADD_EFFECT(EFFECT_MATRIX_ANIMATEDGIF, PatternAnimatedGIF, "Bananaman", GIFIdentifier::Banana, true, CRGB::DarkBlue); + ADD_EFFECT(EFFECT_MATRIX_ANIMATEDGIF, PatternAnimatedGIF, "Bananaman", GIFIdentifier::Banana, true, CRGB::DarkBlue); ADD_EFFECT(EFFECT_MATRIX_SMMETA_BALLS, PatternSMMetaBalls); ADD_EFFECT(EFFECT_MATRIX_SMSUPERNOVA, PatternSMSupernova); ADD_EFFECT(EFFECT_MATRIX_CUBE, PatternCube); @@ -248,7 +252,7 @@ void LoadEffectFactories() ADD_EFFECT(EFFECT_MATRIX_SPECTRUM_ANALYZER, SpectrumAnalyzerEffect, "Spectrum 2", 32, spectrumBasicColors, false, 100, 0, 0.75, 0.75); ADD_EFFECT(EFFECT_MATRIX_SPECTRUM_ANALYZER, SpectrumAnalyzerEffect, "Spectrum++", NUM_BANDS, spectrumBasicColors, false, 0, 40, -1.0, 2.0); ADD_EFFECT(EFFECT_MATRIX_WAVEFORM, WaveformEffect, "WaveIn", 8); - ADD_EFFECT(EFFECT_MATRIX_GHOST_WAVE, GhostWave, "WaveOut", 0, 0, true, 0); + ADD_EFFECT(EFFECT_MATRIX_GHOST_WAVE, GhostWave, "WaveOut", 0, 0, true, 0); ADD_STARRY_NIGHT_EFFECT(MusicStar, "Stars", RainbowColors_p, 1.0, 1, LINEARBLEND, 2.0, 0.5, 10.0); // Rainbow Music Star @@ -390,6 +394,11 @@ void LoadEffectFactories() ADD_EFFECT(EFFECT_STRIP_MOLTEN_GLASS_ON_VIOLET_BKGND, MoltenGlassOnVioletBkgnd, "MoltenGlass", RainbowColors_p); + #elif HELMET + + ADD_EFFECT(EFFECT_MATRIX_SILON, SilonEffect); + ADD_EFFECT(EFFECT_MATRIX_SPECTRUM_ANALYZER, SpectrumAnalyzerEffect, "Spectrum Standard", NUM_BANDS, spectrumAltColors, false, 0, 0, 0.5, 1.5); + #elif SPECTRUM ADD_EFFECT(EFFECT_MATRIX_SPECTRUM_ANALYZER, SpectrumAnalyzerEffect, "Spectrum Standard", NUM_BANDS, spectrumAltColors, false, 0, 0, 0.5, 1.5); @@ -438,6 +447,42 @@ void LoadEffectFactories() ADD_EFFECT(EFFECT_STRIP_METEOR, MeteorEffect, 1, 1, 5, .15, .25); ADD_EFFECT(EFFECT_STRIP_METEOR, MeteorEffect); // Rainbow palette + #elif SPIRALLAMP + + #ifndef EFFECT_SET_VERSION + #define EFFECT_SET_VERSION 2 // Bump version if default set changes in a meaningful way + #endif + + ADD_EFFECT(EFFECT_STRIP_FIRE_FAN, FireFanEffect, HeatColors_p, NUM_LEDS, 1, 1.6, 750, 2, 15, Sequential, true, false); + ADD_EFFECT(EFFECT_STRIP_FIRE_FAN, FireFanEffect, GreenHeatColors_p, NUM_LEDS, 1, 1.6, 750, 2, 15, Sequential, true, false); + ADD_EFFECT(EFFECT_STRIP_FIRE_FAN, FireFanEffect, BlueHeatColors_p, NUM_LEDS, 1, 1.6, 750, 2, 15, Sequential, true, false); + ADD_EFFECT(EFFECT_STRIP_FIRE_FAN, FireFanEffect, RainbowColors_p, NUM_LEDS, 1, 1.6, 750, 2, 15, Sequential, true, false); + ADD_EFFECT(EFFECT_STRIP_FIRE_FAN, FireFanEffect, HeatColors_p, NUM_LEDS, 1, 1.6, 750, 2, 15, Sequential, true, false, true); + + ADD_EFFECT(EFFECT_STRIP_RAINBOW_FILL, RainbowFillEffect, 120, 0); + ADD_EFFECT(EFFECT_STRIP_COLOR_CYCLE, ColorCycleEffect, Sequential); + ADD_EFFECT(EFFECT_STRIP_PALETTE, PaletteEffect, RainbowColors_p, 4, 0.1, 0.0, 1.0, 0.0); + + ADD_EFFECT(EFFECT_STRIP_BOUNCING_BALL, BouncingBallEffect, 3, true, true, 8); + ADD_STARRY_NIGHT_EFFECT(MusicStar, "Rainbow Twinkle Stars", RainbowColors_p, STARRYNIGHT_PROBABILITY, 1, LINEARBLEND, 0.0, 0.0, STARRYNIGHT_MUSICFACTOR); // Rainbow Twinkle + + ADD_STARRY_NIGHT_EFFECT(Star, "Rainbow Twinkle Stars", RainbowColors_p, STARRYNIGHT_PROBABILITY, 1, LINEARBLEND, 2.0, 0.0, STARRYNIGHT_MUSICFACTOR); // Rainbow Twinkle + + ADD_STARRY_NIGHT_EFFECT(Star, "Red Sparkle Stars", RedColors_p, STARRYNIGHT_PROBABILITY, 1, LINEARBLEND, 2.0, 0.0, STARRYNIGHT_MUSICFACTOR); // Blue Sparkle + ADD_STARRY_NIGHT_EFFECT(MusicStar, "Red Stars", RedColors_p, STARRYNIGHT_PROBABILITY, 1, LINEARBLEND, 2.0, 0.0, STARRYNIGHT_MUSICFACTOR); + ADD_STARRY_NIGHT_EFFECT(Star, "Blue Sparkle Stars", BlueColors_p, STARRYNIGHT_PROBABILITY, 1, LINEARBLEND, 2.0, 0.0, STARRYNIGHT_MUSICFACTOR); // Blue Sparkle + ADD_STARRY_NIGHT_EFFECT(MusicStar, "Blue Stars", BlueColors_p, STARRYNIGHT_PROBABILITY, 1, LINEARBLEND, 2.0, 0.0, STARRYNIGHT_MUSICFACTOR); + ADD_STARRY_NIGHT_EFFECT(Star, "Green Sparkle Stars", GreenColors_p, STARRYNIGHT_PROBABILITY, 1, LINEARBLEND, 2.0, 0.0, STARRYNIGHT_MUSICFACTOR); // Blue Sparkle + ADD_STARRY_NIGHT_EFFECT(MusicStar, "Green Stars", GreenColors_p, STARRYNIGHT_PROBABILITY, 1, LINEARBLEND, 2.0, 0.0, STARRYNIGHT_MUSICFACTOR); + + ADD_EFFECT(EFFECT_STRIP_METEOR, MeteorEffect, 4, 4, 10, 1.0, 1.0); + ADD_EFFECT(EFFECT_STRIP_METEOR, MeteorEffect, 2, 4, 10, 0.25, 0.25); + + ADD_EFFECT(EFFECT_STRIP_TWINKLE, TwinkleEffect, NUM_LEDS / 2, 20, 50); + ADD_EFFECT(EFFECT_STRIP_PALETTE, PaletteEffect, RainbowColors_p, .25, 1, 0, 1.0, 0.0, LINEARBLEND, true, 1.0); + ADD_EFFECT(EFFECT_STRIP_PALETTE, PaletteEffect, RainbowColors_p); + + #elif FANSET ADD_EFFECT(EFFECT_STRIP_RAINBOW_FILL, RainbowFillEffect, 24, 0); diff --git a/src/main.cpp b/src/main.cpp index 81d218098..5dc6f2c82 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -427,20 +427,15 @@ void setup() debugW("Creating LCD Screen"); g_ptrSystem->SetupDisplay(TFT_HEIGHT, TFT_WIDTH); - #elif M5STICKC || M5STICKCPLUS || M5STACKCORE2 + #elif USE_M5 - #if USE_M5DISPLAY - M5.begin(); - debugW("Creating M5 Screen"); - g_ptrSystem->SetupDisplay(TFT_HEIGHT, TFT_WIDTH); - #else - M5.begin(false); - #endif + M5.begin(); + M5.Display.startWrite(); + M5.Display.setRotation(1); + M5.Display.setTextDatum(top_center); + M5.Display.setTextColor(WHITE); - // Turn off the M5 vibration motor - #if M5STACKCORE2 - M5.Axp.SetLDOEnable(3, false); - #endif + g_ptrSystem->SetupDisplay(M5.Lcd.width(), M5.Lcd.height()); #elif ELECROW @@ -600,6 +595,6 @@ void loop() // Once an update is underway, we loop tightly on ArduinoOTA.handle. Otherwise, we delay a bit to share the CPU. if (!g_Values.UpdateStarted) - delay(10); + delay(1); } } diff --git a/src/network.cpp b/src/network.cpp index 27ef279be..a39efd355 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -380,9 +380,6 @@ bool ProcessIncomingData(std::unique_ptr & payloadData, size_t paylo debugV("payloadLength: %zu, command16: %d", payloadLength, command16); - // The very old original implementation used channel numbers, not a mask, and only channel 0 was supported at that time, so if - // we see a Channel 0 asked for, it must be very old, and we massage it into the mask for Channel0 instead - switch (command16) { // WIFI_COMMAND_PEAKDATA has a header plus NUM_BANDS floats that will be used to set the audio peaks @@ -417,13 +414,14 @@ bool ProcessIncomingData(std::unique_ptr & payloadData, size_t paylo uint64_t seconds = ULONGFromMemory(&payloadData[8]); uint64_t micros = ULONGFromMemory(&payloadData[16]); - debugV("ProcessIncomingData -- Channel: %u, Length: %u, Seconds: %llu, Micros: %llu ... ", channel16, length32, seconds, micros); + // The very old original implementation used channel numbers, not a mask, and only channel 0 was supported at that time, so if + // we see a Channel 0 asked for, it must be very old, and we massage it into the mask for Channel0 instead // Another option here would be to draw on all channels (0xff) instead of just one (0x01) if 0 is specified if (channel16 == 0) @@ -500,7 +498,7 @@ bool ReadWiFiConfig(String& WiFi_ssid, String& WiFi_password) { // Read the SSID and Password from the NVS partition name/value keypair set - auto len = ARRAYSIZE(szBuffer); + auto len = std::size(szBuffer); err = nvs_get_str(nvsROHandle, NAME_OF(WiFi_ssid), szBuffer, &len); if (ESP_OK != err) { @@ -510,7 +508,7 @@ bool ReadWiFiConfig(String& WiFi_ssid, String& WiFi_password) } WiFi_ssid = szBuffer; - len = ARRAYSIZE(szBuffer); + len = std::size(szBuffer); err = nvs_get_str(nvsROHandle, NAME_OF(WiFi_password), szBuffer, &len); if (ESP_OK != err) { @@ -602,12 +600,8 @@ bool WriteWiFiConfig(const String& WiFi_ssid, const String& WiFi_password) for (;;) // Call Debug.handle() 20 times a second { - EVERY_N_MILLIS(50) - { - Debug.handle(); - } - - delay(10); + Debug.handle(); + delay(MILLIS_PER_SECOND / 20); } } #endif diff --git a/src/remotecontrol.cpp b/src/remotecontrol.cpp index 03fc177f6..693928429 100644 --- a/src/remotecontrol.cpp +++ b/src/remotecontrol.cpp @@ -132,7 +132,7 @@ void RemoteControl::handle() effectManager.ShowVU( !effectManager.IsVUVisible() ); } - for (int i = 0; i < ARRAYSIZE(RemoteColorCodes); i++) + for (int i = 0; i < std::size(RemoteColorCodes); i++) { if (RemoteColorCodes[i].code == result) { diff --git a/src/screen.cpp b/src/screen.cpp index b07fa1e33..5a62e4798 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -158,7 +158,7 @@ void BasicInfoSummary(bool bRedraw) time(&t); struct tm *tmp = localtime(&t); char szTime[16]; - strftime(szTime, ARRAYSIZE(szTime), "%H:%M:%S", tmp); + strftime(szTime, std::size(szTime), "%H:%M:%S", tmp); display.setCursor(xMargin + 0, yMargin + lineHeight * 3); display.println(str_sprintf("CLCK:%s %04.3lf", @@ -255,11 +255,18 @@ void CurrentEffectSummary(bool bRedraw) static auto lastFullDraw = 0; static auto lastAudio = 0; static auto lastSerial = 0; + static auto lastScreen = millis(); + float screenFPS = 0; auto yh = 2; // Start at top of screen display.setTextSize(display.width() > 160 ? 2 : 1); const int topMargin = display.fontHeight() * 3 + 4; + screenFPS = (millis() - lastScreen) / 1000.0f; + if (screenFPS != 0) + screenFPS = 1.0f / screenFPS; + lastScreen = millis(); + if (lastFullDraw == 0 || millis() - lastFullDraw > 1000) { lastFullDraw = millis(); @@ -311,7 +318,7 @@ void CurrentEffectSummary(bool bRedraw) display.setTextColor(YELLOW16, backColor); display.setTextSize(1); yh = display.height() - display.fontHeight(); - String strOut = str_sprintf(" LED: %2d Aud: %2d Ser:%2d ", g_Values.FPS, g_Analyzer._AudioFPS, g_Analyzer._serialFPS); + String strOut = str_sprintf(" LED: %2d Aud: %2d Ser:%2d Scr: %02d", g_Values.FPS, g_Analyzer._AudioFPS, g_Analyzer._serialFPS, (int) screenFPS); auto w = display.textWidth(strOut); display.setCursor(display.width() / 2 - w / 2, yh); display.print(strOut); @@ -481,7 +488,7 @@ void IRAM_ATTR ScreenUpdateLoopEntry(void *) #if AMOLED_S3 lv_task_handler(); #endif - delay(2); + delay(1); } bRedraw = false; } diff --git a/src/socketserver.cpp b/src/socketserver.cpp index b2f80c49f..0d0e65d56 100644 --- a/src/socketserver.cpp +++ b/src/socketserver.cpp @@ -3,9 +3,9 @@ #if INCOMING_WIFI_ENABLED -int SocketServer::ProcessIncomingConnectionsLoop() +bool SocketServer::ProcessIncomingConnectionsLoop() { - if (0 == _server_fd) + if (0 >= _server_fd) { debugW("No _server_fd, returning."); return false; @@ -47,6 +47,21 @@ int SocketServer::ProcessIncomingConnectionsLoop() return false; } + if (_pBuffer == nullptr) + { + debugE("Buffer not allocated!"); + close(new_socket); + ResetReadBuffer(); + return false; + } + + // Ensure the new_socket is valid + if (new_socket < 0) { + debugE("Invalid socket!"); + ResetReadBuffer(); + return false; + } + do { bool bSendResponsePacket = false; @@ -110,7 +125,7 @@ int SocketServer::ProcessIncomingConnectionsLoop() bSendResponsePacket = true; } else - { + { // Read the rest of the data uint16_t command16 = WORDFromMemory(&_pBuffer.get()[0]); @@ -228,6 +243,9 @@ int SocketServer::ProcessIncomingConnectionsLoop() if (sizeof(response) != write(new_socket, &response, sizeof(response))) debugW("Unable to send response back to server."); } + + delay(100); + } while (true); close(new_socket);