From 598e0bc06143bceac8dd705f8e855c68df3808ce Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 18 Sep 2023 15:34:53 +0200 Subject: [PATCH 1/7] matrix: fix for a corner case (e.g. gapmaps) workaround for a corner case; if the reference pixels falls into a "gap" then gPC returns BLACK. Solutions is to reject BLACK. --- wled00/FX.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 31ff74aa..7cea200e 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5303,6 +5303,7 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. oldSpawnColor = SEGMENT.getPixelColorXY(SEGENV.aux0, SEGENV.aux1); // find color of previous spawns SEGENV.aux1 ++; // our sample pixel will be one row down the next time } + if ((oldSpawnColor == CRGB::Black) || (oldSpawnColor == trailColor)) oldSpawnColor = spawnColor; // reject "black", as it would mean that ALL pixels create trails // move pixels one row down. Falling codes keep color and add trail pixels; all others pixels are faded for (int row=rows-1; row>=0; row--) { From a79243135a6500e20ccddb4f9e27d1496b86a271 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 18 Sep 2023 15:56:50 +0200 Subject: [PATCH 2/7] Matrix effect speedup Typically, more than 50% of pixels are black. This optimization avoids to fade and rewrite already black pixels. --- wled00/FX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 7cea200e..74a705b4 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5314,7 +5314,7 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. if (row < rows-1) SEGMENT.setPixelColorXY(col, row+1, spawnColor); } else { // fade other pixels - SEGMENT.setPixelColorXY(col, row, pix.nscale8(fade)); + if (pix != CRGB::Black) SEGMENT.setPixelColorXY(col, row, pix.nscale8(fade)); // optimization: don't fade black pixels } } } From 89c33607bc3296748ff17f845a20682758ecde59 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 18 Sep 2023 22:05:53 +0200 Subject: [PATCH 3/7] SR_DEBUG shows stack high water marks some debug support for showing free stack space. --- usermods/audioreactive/audio_reactive.h | 32 ++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 0f50f57f..ffe6e8ad 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -449,9 +449,9 @@ void FFTcode(void * parameter) } #if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS) + // timing uint64_t start = esp_timer_get_time(); bool haveDoneFFT = false; // indicates if second measurement (FFT time) is valid - static uint64_t lastCycleStart = 0; static uint64_t lastLastTime = 0; if ((lastCycleStart > 0) && (lastCycleStart < start)) { // filter out overflows @@ -466,6 +466,14 @@ void FFTcode(void * parameter) if (audioSource) audioSource->getSamples(vReal, samplesFFT); #if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS) + // debug info in case that stack usage changes + static unsigned int minStackFree = UINT32_MAX; + unsigned int stackFree = uxTaskGetStackHighWaterMark(NULL); + if (minStackFree > stackFree) { + minStackFree = stackFree; + DEBUGSR_PRINTF("|| %-9s min free stack %d\n", pcTaskGetTaskName(NULL), minStackFree); //WLEDMM + } + // timing if (start < esp_timer_get_time()) { // filter out overflows uint64_t sampleTimeInMillis = (esp_timer_get_time() - start +5ULL) / 10ULL; // "+5" to ensure proper rounding sampleTime = (sampleTimeInMillis*3 + sampleTime*7)/10.0; // smooth @@ -715,6 +723,7 @@ void FFTcode(void * parameter) postProcessFFTResults((fabsf(volumeSmth)>0.25f)? true : false , NUM_GEQ_CHANNELS); // this function modifies fftCalc, fftAvg and fftResult #if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS) + // timing static uint64_t lastLastFFT = 0; if (haveDoneFFT && (start < esp_timer_get_time())) { // filter out overflows uint64_t fftTimeInMillis = ((esp_timer_get_time() - start) +5ULL) / 10ULL; // "+5" to ensure proper rounding @@ -1758,6 +1767,10 @@ class AudioReactive : public Usermod { DEBUGSR_PRINT(F("AR: init done, enabled = ")); DEBUGSR_PRINTLN(enabled ? F("true.") : F("false.")); USER_FLUSH(); + + #if defined(ARDUINO_ARCH_ESP32) && defined(SR_DEBUG) + DEBUGSR_PRINTF("|| %-9s min free stack %d\n", pcTaskGetTaskName(NULL), uxTaskGetStackHighWaterMark(NULL)); //WLEDMM + #endif } @@ -1788,6 +1801,10 @@ class AudioReactive : public Usermod { DEBUGSR_PRINTLN(udpSyncConnected ? F("AR connected(): UDP: connected to WIFI.") : F("AR connected(): UDP is disconnected (Wifi).")); } } + + #if defined(ARDUINO_ARCH_ESP32) && defined(SR_DEBUG) + DEBUGSR_PRINTF("|| %-9s min free stack %d\n", pcTaskGetTaskName(NULL), uxTaskGetStackHighWaterMark(NULL)); //WLEDMM + #endif } @@ -1844,6 +1861,15 @@ class AudioReactive : public Usermod { #ifdef ARDUINO_ARCH_ESP32 if (!audioSource->isInitialized()) disableSoundProcessing = true; // no audio source + #ifdef SR_DEBUG + // debug info in case that task stack usage changes + static unsigned int minLoopStackFree = UINT32_MAX; + unsigned int stackFree = uxTaskGetStackHighWaterMark(NULL); + if (minLoopStackFree > stackFree) { + minLoopStackFree = stackFree; + DEBUGSR_PRINTF("|| %-9s min free stack %d\n", pcTaskGetTaskName(NULL), minLoopStackFree); //WLEDMM + } + #endif // Only run the sampling code IF we're not in Receive mode or realtime mode if (!(audioSyncEnabled & 0x02) && !disableSoundProcessing) { @@ -2040,6 +2066,10 @@ class AudioReactive : public Usermod { micDataReal = 0.0f; // just to be sure if (enabled) disableSoundProcessing = false; updateIsRunning = init; + + #if defined(ARDUINO_ARCH_ESP32) && defined(SR_DEBUG) + DEBUGSR_PRINTF("|| %-9s min free stack %d\n", pcTaskGetTaskName(NULL), uxTaskGetStackHighWaterMark(NULL)); //WLEDMM + #endif } #else // reduced function for 8266 From b3d9621427e89208b7b797d67c4de3a4fe974c29 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 19 Sep 2023 15:27:41 +0200 Subject: [PATCH 4/7] fix wrong signature of SPH0654::initialize() * debug messages added to different initializers * SPH0654::initialize() was having a wrong signature: uint8 instead of int8. C++ can be a real bastard ;-) --- usermods/audioreactive/audio_source.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index 3c257d78..a0982381 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -206,6 +206,7 @@ class I2SSource : public AudioSource { } virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { + DEBUGSR_PRINTLN("I2SSource:: initialize()."); if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) { if (!pinManager.allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) || !pinManager.allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206 @@ -462,6 +463,7 @@ public: }; void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { + DEBUGSR_PRINTLN("ES7243:: initialize();"); // check that pins are valid if ((sdaPin < 0) || (sclPin < 0)) { ERRORSR_PRINTF("\nAR: invalid ES7243 I2C pins: SDA=%d, SCL=%d\n", sdaPin, sclPin); @@ -618,6 +620,7 @@ class ES8388Source : public I2SSource { }; void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { + DEBUGSR_PRINTLN("ES8388Source:: initialize();"); // BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here. // Workaround: Set I2C pins here, which will also set them globally. @@ -712,6 +715,7 @@ class I2SAdcSource : public I2SSource { AudioSourceType getType(void) {return(Type_I2SAdc);} void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { + DEBUGSR_PRINTLN("I2SAdcSource:: initialize()."); _myADCchannel = 0x0F; if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) { ERRORSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin); @@ -882,7 +886,8 @@ class SPH0654 : public I2SSource { I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {} - void initialize(uint8_t i2swsPin, uint8_t i2ssdPin, uint8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { + void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { + DEBUGSR_PRINTLN("SPH0654:: initialize();"); I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin); #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) // these registers are only existing in "classic" ESP32 From acc2d3aa10d0d38db43d90033bfc56ee0ca86bd7 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 19 Sep 2023 19:33:36 +0200 Subject: [PATCH 5/7] clean up I2C pin handling in audioreactive (ES7243+ES8388) * remove double Wire.begin() - Wire cannot be re-initialized; its already started by pinManager.joinWire() * Only use global I2C pins; make sure that audireactive I2C settings are aligned with global I2C pins * minor cleanup * remove I2C pins from AudioSource::initialize() Note to self: sdaPin, sclPin are just dummy values now, good for UI consistency, but unused otherwise. Could be removed. --- usermods/audioreactive/audio_reactive.h | 27 +++++- usermods/audioreactive/audio_source.h | 109 ++++++------------------ 2 files changed, 49 insertions(+), 87 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index ffe6e8ad..c4669869 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -1687,7 +1687,13 @@ class AudioReactive : public Usermod { //useInputFilter = 0; // in case you need to disable low-cut software filtering audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE); delay(100); - if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin); + // WLEDMM align global pins + if ((sdaPin >= 0) && (i2c_sda < 0)) i2c_sda = sdaPin; // copy usermod prefs into globals (if globals not defined) + if ((sclPin >= 0) && (i2c_scl < 0)) i2c_scl = sclPin; + if (i2c_sda >= 0) sdaPin = -1; // -1 = use global + if (i2c_scl >= 0) sclPin = -1; + + if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); break; case 3: DEBUGSR_PRINT(F("AR: SPH0645 Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT)); @@ -1719,11 +1725,21 @@ class AudioReactive : public Usermod { break; #endif case 6: - DEBUGSR_PRINTLN(F("AR: ES8388 Source")); + #ifdef use_es8388_mic + DEBUGSR_PRINTLN(F("AR: ES8388 Source (Mic)")); + #else + DEBUGSR_PRINTLN(F("AR: ES8388 Source (Line-In)")); + #endif audioSource = new ES8388Source(SAMPLE_RATE, BLOCK_SIZE, 1.0f); //useInputFilter = 0; // to disable low-cut software filtering and restore previous behaviour delay(100); - if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin); + // WLEDMM align global pins + if ((sdaPin >= 0) && (i2c_sda < 0)) i2c_sda = sdaPin; // copy usermod prefs into globals (if globals not defined) + if ((sclPin >= 0) && (i2c_scl < 0)) i2c_scl = sclPin; + if (i2c_sda >= 0) sdaPin = -1; // -1 = use global + if (i2c_scl >= 0) sclPin = -1; + + if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); break; #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) @@ -2366,6 +2382,11 @@ class AudioReactive : public Usermod { JsonObject dmic = top.createNestedObject(FPSTR(_digitalmic)); dmic[F("type")] = dmType; + // WLEDMM: align with globals I2C pins + if ((dmType == 2) || (dmType == 6)) { // only for ES7243 and ES8388 + if (i2c_sda >= 0) sdaPin = -1; // -1 = use global + if (i2c_scl >= 0) sclPin = -1; // -1 = use global + } JsonArray pinArray = dmic.createNestedArray("pin"); pinArray.add(i2ssdPin); pinArray.add(i2swsPin); diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index a0982381..8aa7c44d 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -126,7 +126,7 @@ class AudioSource { This function needs to take care of anything that needs to be done before samples can be obtained from the microphone. */ - virtual void initialize(int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) = 0; + virtual void initialize(int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) = 0; /* Deinitialize Release all resources and deactivate any functionality that is used @@ -205,7 +205,7 @@ class I2SSource : public AudioSource { }; } - virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { + virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE) { DEBUGSR_PRINTLN("I2SSource:: initialize()."); if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) { if (!pinManager.allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) || @@ -425,19 +425,14 @@ class ES7243 : public I2SSource { private: // I2C initialization functions for ES7243 void _es7243I2cBegin() { - bool i2c_initialized = Wire.begin(pin_ES7243_SDA, pin_ES7243_SCL, 100000U); - if (i2c_initialized == false) { - ERRORSR_PRINTLN(F("AR: ES7243 failed to initialize I2C bus driver.")); - } + Wire.setClock(100000); } void _es7243I2cWrite(uint8_t reg, uint8_t val) { -#ifndef ES7243_ADDR - Wire.beginTransmission(0x13); - #define ES7243_ADDR 0x13 // default address -#else + #ifndef ES7243_ADDR + #define ES7243_ADDR 0x13 // default address + #endif Wire.beginTransmission(ES7243_ADDR); -#endif Wire.write((uint8_t)reg); Wire.write((uint8_t)val); uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK @@ -462,54 +457,30 @@ public: _config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT; }; - void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { + void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { DEBUGSR_PRINTLN("ES7243:: initialize();"); - // check that pins are valid - if ((sdaPin < 0) || (sclPin < 0)) { - ERRORSR_PRINTF("\nAR: invalid ES7243 I2C pins: SDA=%d, SCL=%d\n", sdaPin, sclPin); - return; - } - if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined" + // if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too + // ERRORSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin); + // return; + // } + if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined" ERRORSR_PRINTF("\nAR: invalid ES7243 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl); return; } - if (!pinManager.joinWire()) { // WLEDMM specific: start I2C with globally defined pins + if (!pinManager.joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl); return; } - if ((i2sckPin < 0) || (mclkPin < 0)) { - ERRORSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin); - return; - } - - // Reserve SDA and SCL pins of the I2C interface - PinManagerPinType es7243Pins[2] = { { sdaPin, true }, { sclPin, true } }; - if (!pinManager.allocateMultiplePins(es7243Pins, 2, PinOwner::HW_I2C)) { - pinManager.deallocateMultiplePins(es7243Pins, 2, PinOwner::HW_I2C); - ERRORSR_PRINTF("\nAR: Failed to allocate ES7243 I2C pins: SDA=%d, SCL=%d\n", sdaPin, sclPin); - return; - } - - pin_ES7243_SDA = sdaPin; - pin_ES7243_SCL = sclPin; - // First route mclk, then configure ADC over I2C, then configure I2S _es7243InitAdc(); I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); } void deinitialize() { - // Release SDA and SCL pins of the I2C interface - PinManagerPinType es7243Pins[2] = { { pin_ES7243_SDA, true }, { pin_ES7243_SCL, true } }; - pinManager.deallocateMultiplePins(es7243Pins, 2, PinOwner::HW_I2C); I2SSource::deinitialize(); } - - private: - int8_t pin_ES7243_SDA; - int8_t pin_ES7243_SCL; }; /* ES8388 Sound Modude @@ -520,19 +491,14 @@ class ES8388Source : public I2SSource { private: // I2C initialization functions for ES8388 void _es8388I2cBegin() { - bool i2c_initialized = Wire.begin(pin_ES8388_SDA, pin_ES8388_SCL, 100000U); - if (i2c_initialized == false) { - ERRORSR_PRINTLN(F("AR: ES8388 failed to initialize I2C bus driver.")); - } + Wire.setClock(100000); } void _es8388I2cWrite(uint8_t reg, uint8_t val) { -#ifndef ES8388_ADDR - Wire.beginTransmission(0x10); - #define ES8388_ADDR 0x10 // default address -#else + #ifndef ES8388_ADDR + #define ES8388_ADDR 0x10 // default address + #endif Wire.beginTransmission(ES8388_ADDR); -#endif Wire.write((uint8_t)reg); Wire.write((uint8_t)val); uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK @@ -619,59 +585,34 @@ class ES8388Source : public I2SSource { _config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT; }; - void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { + void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { DEBUGSR_PRINTLN("ES8388Source:: initialize();"); + // if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too + // ERRORSR_PRINTF("\nAR: invalid I2S ES8388 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin); + // return; + // } // BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here. // Workaround: Set I2C pins here, which will also set them globally. // Bug also exists in ES7243. - - // check that pins are valid - if ((sdaPin < 0) || (sclPin < 0)) { - ERRORSR_PRINTF("\nAR: invalid ES8388 I2C pins: SDA=%d, SCL=%d\n", sdaPin, sclPin); - return; - } - if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined" ERRORSR_PRINTF("\nAR: invalid ES8388 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl); return; } - if (!pinManager.joinWire()) { // WLEDMM specific: start I2C with globally defined pins + if (!pinManager.joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl); return; } - if ((i2sckPin < 0) || (mclkPin < 0)) { - ERRORSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin); - return; - } - - // Reserve SDA and SCL pins of the I2C interface - PinManagerPinType es8388Pins[2] = { { sdaPin, true }, { sclPin, true } }; - if (!pinManager.allocateMultiplePins(es8388Pins, 2, PinOwner::HW_I2C)) { - pinManager.deallocateMultiplePins(es8388Pins, 2, PinOwner::HW_I2C); - ERRORSR_PRINTF("\nAR: Failed to allocate ES8388 I2C pins: SDA=%d, SCL=%d\n", sdaPin, sclPin); - return; - } - - pin_ES8388_SDA = sdaPin; - pin_ES8388_SCL = sclPin; - // First route mclk, then configure ADC over I2C, then configure I2S _es8388InitAdc(); I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); } void deinitialize() { - // Release SDA and SCL pins of the I2C interface - PinManagerPinType es8388Pins[2] = { { pin_ES8388_SDA, true }, { pin_ES8388_SCL, true } }; - pinManager.deallocateMultiplePins(es8388Pins, 2, PinOwner::HW_I2C); I2SSource::deinitialize(); } - private: - int8_t pin_ES8388_SDA; - int8_t pin_ES8388_SCL; }; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) @@ -714,7 +655,7 @@ class I2SAdcSource : public I2SSource { /* identify Audiosource type - I2S-ADC*/ AudioSourceType getType(void) {return(Type_I2SAdc);} - void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { + void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { DEBUGSR_PRINTLN("I2SAdcSource:: initialize()."); _myADCchannel = 0x0F; if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) { @@ -886,7 +827,7 @@ class SPH0654 : public I2SSource { I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {} - void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { + void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE) { DEBUGSR_PRINTLN("SPH0654:: initialize();"); I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin); #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) From e28d303d4c958967546a2e7a1985d663d02c7722 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 19 Sep 2023 22:54:08 +0200 Subject: [PATCH 6/7] adjust RTC usermod for "MM Style" --- usermods/RTC/usermod_rtc.h | 31 +++++++++++++++++++++++-------- wled00/usermods_list.cpp | 2 +- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/usermods/RTC/usermod_rtc.h b/usermods/RTC/usermod_rtc.h index 08b92ef1..a68bf821 100644 --- a/usermods/RTC/usermod_rtc.h +++ b/usermods/RTC/usermod_rtc.h @@ -8,21 +8,25 @@ #define RTC_DELTA 2 // only modify RTC time if delta exceeds this number of seconds -//Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL)) +//Connect DS1307 or DS3231 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL)) class RTCUsermod : public Usermod { private: unsigned long lastTime = 0; - bool disabled = false; + bool RTCfound = false; // WLEDMM to prevent errors after anabling the usermod (Wire.cpp:526] write(): NULL TX buffer pointer) public: + RTCUsermod(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM: this shouldn't be necessary (passthrough of constructor), maybe because Usermod is an abstract class + void setup() { + RTCfound = false; // WLEDMM PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; - if (pins[1].pin < 0 || pins[0].pin < 0) { disabled=true; return; } //WLEDMM bugfix - ensure that "final" GPIO are valid and no "-1" sneaks trough + if (pins[1].pin < 0 || pins[0].pin < 0) { enabled=false; return; } //WLEDMM bugfix - ensure that "final" GPIO are valid and no "-1" sneaks trough + if (!enabled) { DEBUG_PRINTLN(F("RTC usermod not enabled.")); return; } // WLEDMM join hardware I2C if (!pinManager.joinWire()) { // WLEDMM - this allocates global I2C pins, then starts Wire - if not started previously - disabled = true; + enabled = false; return; } @@ -40,20 +44,31 @@ class RTCUsermod : public Usermod { updateLocalTime(); USER_PRINTLN(F("Localtime updated from RTC.")); } else { - if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error + if (!RTC.chipPresent()) { + enabled = false; //don't waste time if H/W error + USER_PRINTLN(F("RTC board not present.")); + } } + if (enabled) RTCfound = true; // WLEDMM } void loop() { - if (strip.isUpdating()) return; - if (!disabled && toki.isTick()) { + // WLEDMM begin + if (!enabled) return; + if (!RTCfound) return; // WLEDMM + unsigned long currentTime = millis(); // get the current elapsed time + if (strip.isUpdating() && (currentTime - lastTime < 10000)) return; // WLEDMM: be nice + // WLEDMM end + + if (enabled && toki.isTick()) { // WLEDMM time_t t = toki.second(); + lastTime = millis(); // WLEDMM if (abs(t - RTC.get())> RTC_DELTA) { // WLEDMM only consider time diffs > 2 seconds if ( (toki.getTimeSource() == TOKI_TS_NTP) ||( (toki.getTimeSource() != TOKI_TS_NONE) && (toki.getTimeSource() != TOKI_TS_RTC) && (toki.getTimeSource() != TOKI_TS_BAD) && (toki.getTimeSource() != TOKI_TS_UDP_SEC) && (toki.getTimeSource() != TOKI_TS_UDP))) - { // WLEMM update RTC if we have a reliable time source + { // WLEDMM update RTC if we have a reliable time source RTC.set(t); //set RTC to NTP/UI-provided value - WLEDMM allow up to 3 sec deviation USER_PRINTLN(F("RTC updated using localtime.")); } else { diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 44b51670..986edf5f 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -290,7 +290,7 @@ void registerUsermods() #endif #ifdef USERMOD_RTC - usermods.add(new RTCUsermod()); + usermods.add(new RTCUsermod("RTC", true)); #endif #ifdef USERMOD_ELEKSTUBE_IPS From 7425b437dae84f4e3cbe7594689c0e35e06f93e1 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 20 Sep 2023 00:43:03 +0200 Subject: [PATCH 7/7] (WIP) adjust Battery usermod for "MM Style" seems to work, but still needs more testing. --- usermods/Battery/usermod_v2_Battery.h | 41 +++++++++++++++++++-------- wled00/usermods_list.cpp | 2 +- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/usermods/Battery/usermod_v2_Battery.h b/usermods/Battery/usermod_v2_Battery.h index 3d519724..cd518b9a 100644 --- a/usermods/Battery/usermod_v2_Battery.h +++ b/usermods/Battery/usermod_v2_Battery.h @@ -60,9 +60,9 @@ class UsermodBattery : public Usermod bool initializing = true; // strings to reduce flash memory usage (used more than twice) - static const char _name[]; + // static const char _name[]; static const char _readInterval[]; - static const char _enabled[]; + // static const char _enabled[]; static const char _threshold[]; static const char _preset[]; static const char _duration[]; @@ -117,6 +117,7 @@ class UsermodBattery : public Usermod float readVoltage() { #ifdef ARDUINO_ARCH_ESP32 + if ((batteryPin <0) || !pinManager.isPinAnalog(batteryPin)) return(-1.0f); // WLEDMM avoid reading from invalid pin // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default attentuation) and divide by 1000 to get from milivolts to volts and multiply by voltage multiplier and apply calibration value return (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration; #else @@ -127,6 +128,7 @@ class UsermodBattery : public Usermod public: //Functions called by WLED + UsermodBattery(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM: this shouldn't be necessary (passthrough of constructor), maybe because Usermod is an abstract class /* * setup() is called once at boot. WiFi is not yet connected at this point. @@ -134,10 +136,11 @@ class UsermodBattery : public Usermod */ void setup() { + if (!enabled) return; // WLEDMM #ifdef ARDUINO_ARCH_ESP32 bool success = false; DEBUG_PRINTLN(F("Allocating battery pin...")); - if (batteryPin >= 0 && digitalPinToAnalogChannel(batteryPin) >= 0) + if ((batteryPin >= 0) && (digitalPinToAnalogChannel(batteryPin) >= 0)) if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) { DEBUG_PRINTLN(F("Battery pin allocation succeeded.")); success = true; @@ -145,8 +148,9 @@ class UsermodBattery : public Usermod } if (!success) { - DEBUG_PRINTLN(F("Battery pin allocation failed.")); + USER_PRINTLN(F("Battery pin allocation failed.")); batteryPin = -1; // allocation failed + enabled = false; } else { pinMode(batteryPin, INPUT); } @@ -178,7 +182,13 @@ class UsermodBattery : public Usermod */ void loop() { - if(strip.isUpdating()) return; + // WLEDMM begin + if (!enabled) return; + if (batteryPin < 0) return; + unsigned long currentTime = millis(); // get the current elapsed time + if (strip.isUpdating() && (currentTime - lastTime < 30000)) return; // WLEDMM: be nice + lastTime = currentTime; + // WLEDMM end lowPowerIndicator(); @@ -252,6 +262,7 @@ class UsermodBattery : public Usermod JsonObject user = root["u"]; if (user.isNull()) user = root.createNestedObject("u"); + if (!enabled) return; // WLEDMM if (batteryPin < 0) { JsonArray infoVoltage = user.createNestedArray(F("Battery voltage")); infoVoltage.add(F("n/a")); @@ -360,6 +371,8 @@ class UsermodBattery : public Usermod void addToConfig(JsonObject& root) { JsonObject battery = root.createNestedObject(FPSTR(_name)); // usermodname + battery[F("enabled")] = enabled; // WLEDMM + #ifdef ARDUINO_ARCH_ESP32 battery[F("pin")] = batteryPin; #endif @@ -373,11 +386,11 @@ class UsermodBattery : public Usermod battery[FPSTR(_readInterval)] = readingInterval; JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section - ao[FPSTR(_enabled)] = autoOffEnabled; + ao[F("auto-off-enabled")] = autoOffEnabled; ao[FPSTR(_threshold)] = autoOffThreshold; JsonObject lp = battery.createNestedObject(F("indicator")); // low power section - lp[FPSTR(_enabled)] = lowPowerIndicatorEnabled; + lp[F("indicator-enabled")] = lowPowerIndicatorEnabled; lp[FPSTR(_preset)] = lowPowerIndicatorPreset; // dropdown trickery (String)lowPowerIndicatorPreset; lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold; lp[FPSTR(_duration)] = lowPowerIndicatorDuration; @@ -436,6 +449,10 @@ class UsermodBattery : public Usermod #endif JsonObject battery = root[FPSTR(_name)]; + + bool configComplete = !battery.isNull(); // WLEDMM + configComplete &= getJsonValue(battery[F("enabled")], enabled, true); // WLEDMM + if (battery.isNull()) { DEBUG_PRINT(FPSTR(_name)); @@ -455,11 +472,11 @@ class UsermodBattery : public Usermod setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval); JsonObject ao = battery[F("auto-off")]; - setAutoOffEnabled(ao[FPSTR(_enabled)] | autoOffEnabled); + setAutoOffEnabled(ao[F("auto-off-enabled")] | autoOffEnabled); // WLEDMM setAutoOffThreshold(ao[FPSTR(_threshold)] | autoOffThreshold); JsonObject lp = battery[F("indicator")]; - setLowPowerIndicatorEnabled(lp[FPSTR(_enabled)] | lowPowerIndicatorEnabled); + setLowPowerIndicatorEnabled(lp[F("indicator-enabled")] | lowPowerIndicatorEnabled); // WLEDMM setLowPowerIndicatorPreset(lp[FPSTR(_preset)] | lowPowerIndicatorPreset); // dropdown trickery (int)lp["preset"] setLowPowerIndicatorThreshold(lp[FPSTR(_threshold)] | lowPowerIndicatorThreshold); lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10; @@ -490,7 +507,7 @@ class UsermodBattery : public Usermod } #endif - return !battery[FPSTR(_readInterval)].isNull(); + return configComplete && (!battery[FPSTR(_readInterval)].isNull()); // WLEDMM } /* @@ -780,9 +797,9 @@ class UsermodBattery : public Usermod }; // strings to reduce flash memory usage (used more than twice) -const char UsermodBattery::_name[] PROGMEM = "Battery"; +// const char UsermodBattery::_name[] PROGMEM = "Battery"; const char UsermodBattery::_readInterval[] PROGMEM = "interval"; -const char UsermodBattery::_enabled[] PROGMEM = "enabled"; +//const char UsermodBattery::_enabled[] PROGMEM = "enabled"; const char UsermodBattery::_threshold[] PROGMEM = "threshold"; const char UsermodBattery::_preset[] PROGMEM = "preset"; const char UsermodBattery::_duration[] PROGMEM = "duration"; diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 986edf5f..1ccf1d03 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -222,7 +222,7 @@ void registerUsermods() */ //usermods.add(new MyExampleUsermod()); #ifdef USERMOD_BATTERY - usermods.add(new UsermodBattery()); + usermods.add(new UsermodBattery("Battery", true)); #endif #ifdef USERMOD_DALLASTEMPERATURE