diff --git a/platformio.ini b/platformio.ini index a9563b8e..10822e36 100644 --- a/platformio.ini +++ b/platformio.ini @@ -819,7 +819,7 @@ build_flags = ${common.build_flags_esp8266} ; RAM: [====== ] 59.3% (used 48608 bytes from 81920 bytes) ; Flash: [======== ] 77.0% (used 804176 bytes from 1044464 bytes) -[env:esp8266_4MB_max] ;WLEDMM: WIP +[env:esp8266_4MB_max] extends = env:d1_mini upload_speed = 460800 ;115200 board_build.f_cpu = 160000000L ;; we want 160Mhz (default = 80Mhz) @@ -829,26 +829,56 @@ build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_HUESYNC + -D WLED_DISABLE_LOXONE ; -D USERMOD_AUDIOREACTIVE ; -D USERMOD_CUSTOMEFFECTS ; to be done ; -UWLED_USE_MY_CONFIG -D USERMOD_PIRSWITCH -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?) -D USERMOD_MULTI_RELAY - ; -D USE_ALT_DISPLAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI - ; -D USERMOD_FOUR_LINE_DISPLAY + -D USE_ALT_DISPLAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI + -D USERMOD_FOUR_LINE_DISPLAY -D USERMOD_MPU6050_IMU ; gyro/accelero for USERMOD_GAMES (ONLY WORKS IF USERMOD_FOUR_LINE_DISPLAY NOT INCLUDED - I2C SHARING BUG) -D USERMOD_GAMES ; WLEDMM usermod - -D WLED_DEBUG + ; -D WLED_DEBUG ; monitor_filters = esp8266_exception_decoder lib_deps = ${esp8266.lib_deps} OneWire@~2.3.5 ; used for USERMOD_FOUR_LINE_DISPLAY and USERMOD_DALLASTEMPERATURE - ; olikraus/U8g2 @ ^2.28.8 ; used for USERMOD_FOUR_LINE_DISPLAY - ; olikraus/U8g2@ ^2.34.5 ; used for USERMOD_FOUR_LINE_DISPLAY -> need newer version with bugfixes for arduino-esp32 v2.0.4 (Wire inititialization) + olikraus/U8g2 @ ^2.28.8 ; used for USERMOD_FOUR_LINE_DISPLAY ElectronicCats/MPU6050 @ 0.6.0 ; used for USERMOD_MPU6050_IMU ; RAM: [====== ] 61.5% (used 50344 bytes from 81920 bytes) ; Flash: [======== ] 81.8% (used 854444 bytes from 1044464 bytes) +; Blaz env (for reference purposes) +[env:d1_mini_temp] +extends = env:d1_mini +board_build.filesystem = littlefs +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 + -D WLED_DISABLE_ALEXA + -D WLED_DISABLE_BLYNK + -D WLED_DISABLE_HUESYNC + -D WLED_DISABLE_LOXONE + -D WLED_DISABLE_AUDIO ;WLEDMM not used anywhere + -D WLED_ENABLE_SIMPLE_UI + -D USERMOD_FOUR_LINE_DISPLAY + -D USE_ALT_DISPlAY + -D USERMOD_DALLASTEMPERATURE + -D TEMPERATURE_PIN=13 # (D7) + -D LEDPIN=2 # (D4) + -D RLYPIN=12 # (D6) + -D BTNPIN=0 # (D3) + -D IRPIN=14 # (D5) + -D USERMOD_MULTI_RELAY + -D MULTI_RELAY_MAX_RELAYS=2 + -D USERMOD_PIRSWITCH + -D PIR_SENSOR_PIN=16 + -D PIR_SENSOR_OFF_SEC=60 + -UWLED_USE_MY_CONFIG +lib_deps = ${esp8266.lib_deps} + paulstoffregen/OneWire@~2.3.7 ;WLEDMM Softhack, we need this as well (instead of 2.3.5)? + olikraus/U8g2 # @~2.33.15 + Wire ; WLEDMM needed? + [env:esp8266pro_16MB_min] extends = env:d1_mini board = d1_mini_pro ;; "D1 mini pro": ESP8266EX, 160MHz, 80KB RAM, 16MB Flash @@ -1083,6 +1113,7 @@ build_flags = ${common.build_flags} ${esp32.build_flagsV4} ${esp32c3.build_flags ; -DARDUINO_USB_CDC_ON_BOOT=1 ;; enable CDC USB -> needed for debugging over serial USB -DARDUINO_USB_CDC_ON_BOOT=0 ;; disable CDC USB -D SERVERNAME='"WLED-C3"' + ;-D WLEDMM_WIFI_POWERON_HACK ;; use this _only_ if your device is not able to make a WiFI connection! ;-D WLED_DISABLE_INFRARED ;; save flash space ;-D WLED_DISABLE_ALEXA ;; save flash space -D LEDPIN=8 ;; onboard neopixel 5x5 Matrix. Attach your own LEDs to GPIO 20 @@ -1116,6 +1147,8 @@ build_flags = ${esp32_4MB_max_base.build_flags} -D HW_PIN_SCL=22 -D HW_PIN_SDA=21 -D HW_PIN_CLOCKSPI=-1 -D HW_PIN_MOSISPI=-1 -D HW_PIN_MISOSPI=-1 ; WLEDMM: is now also default but just to show we didn't agree on wemos pins for spi yet -D ENCODER_DT_PIN=35 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=39 ;WLEDMM spec by @SERG74: use 35 and 39 instead of 18 and 19 (conflicts) + -D PIR_SENSOR_PIN=-1 + -D PWM_PIN=-1 ; -D WLED_USE_MY_CONFIG [wemos_shield_esp32_4MB_all_base] @@ -1223,6 +1256,7 @@ build_flags = ${esp32_4MB_max_base.build_flags} -D SR_DMTYPE=1 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14 -D SR_SQUELCH=5 -D SR_GAIN=30 -D SR_FREQ_PROF=5 ; ICS-43434 specific ; -D MCLK_PIN=0 + -D SR_ENABLE_DEFAULT ;; enable at first start - no need to manually set "enable", then reboot ; -D WLED_USE_MY_CONFIG ; -D WLED_DISABLE_LOXONE ; -D WLED_DISABLE_ALEXA diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h index 2928c75f..9aede3a8 100644 --- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h +++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h @@ -4,11 +4,7 @@ #ifndef PIR_SENSOR_PIN // compatible with QuinLED-Dig-Uno - #ifdef ARDUINO_ARCH_ESP32 - #define PIR_SENSOR_PIN 23 // Q4 - #else //ESP8266 boards - #define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini) - #endif + #define PIR_SENSOR_PIN -1 //WLEDMM not default 23 // Q4 for esp32 or otherwise 13 // Q4 (D7 on D1 mini) #endif #ifndef PIR_SENSOR_OFF_SEC diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index 1c3cf1bc..88719786 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -29,6 +29,7 @@ class UsermodTemperature : public Usermod { bool degC = true; // using parasite power on the sensor bool parasite = false; + int8_t parasitePin = -1; // how often do we read from sensor? unsigned long readingInterval = USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL; // set last reading as "40 sec before boot", so first reading is taken after 20 sec @@ -53,6 +54,7 @@ class UsermodTemperature : public Usermod { static const char _enabled[]; static const char _readInterval[]; static const char _parasite[]; + static const char _parasitePin[]; //Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013 float readDallas() { @@ -94,12 +96,14 @@ class UsermodTemperature : public Usermod { DEBUG_PRINTLN(F("Requesting temperature.")); oneWire->reset(); oneWire->skip(); // skip ROM - oneWire->write(0x44,parasite); // request new temperature reading (TODO: parasite would need special handling) + oneWire->write(0x44,parasite); // request new temperature reading + if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, HIGH); // has to happen within 10us (open MOSFET) lastTemperaturesRequest = millis(); waitingForConversion = true; } void readTemperature() { + if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET) temperature = readDallas(); lastMeasurement = millis(); waitingForConversion = false; @@ -175,6 +179,12 @@ class UsermodTemperature : public Usermod { delay(25); // try to find sensor } } + if (parasite && pinManager.allocatePin(parasitePin, true, PinOwner::UM_Temperature)) { + pinMode(parasitePin, OUTPUT); + digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET) + } else { + parasitePin = -1; + } } else { if (temperaturePin >= 0) { USER_PRINTLN(F("Temperature pin allocation failed.")); @@ -311,10 +321,6 @@ class UsermodTemperature : public Usermod { // if (!initDone) return; // prevent crash on boot applyPreset() //} - void appendConfigData() { - oappend(SET_F("addHB('Temperature');")); - } - /** * addToConfig() (called from set.cpp) stores persistent properties to cfg.json */ @@ -326,6 +332,7 @@ class UsermodTemperature : public Usermod { top["degC"] = degC; // usermodparam top[FPSTR(_readInterval)] = readingInterval / 1000; top[FPSTR(_parasite)] = parasite; + top[FPSTR(_parasitePin)] = parasitePin; DEBUG_PRINTLN(F("Temperature config saved.")); } @@ -351,6 +358,7 @@ class UsermodTemperature : public Usermod { readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000; readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms parasite = top[FPSTR(_parasite)] | parasite; + parasitePin = top[FPSTR(_parasitePin)] | parasitePin; if (!initDone) { // first run: reading from cfg.json @@ -365,12 +373,22 @@ class UsermodTemperature : public Usermod { delete oneWire; pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature); temperaturePin = newTemperaturePin; + pinManager.deallocatePin(parasitePin, PinOwner::UM_Temperature); // initialise setup(); } } // use "return !top["newestParameter"].isNull();" when updating Usermod with new features - return !top[FPSTR(_parasite)].isNull(); + return !top[FPSTR(_parasitePin)].isNull(); + } + + void appendConfigData() + { + oappend(SET_F("addHB('Temperature');")); // WLEDMM + oappend(SET_F("addInfo('Temperature:parasite-pwr")); + oappend(SET_F("',1,'(if no Vcc connected)');")); // 0 is field type, 1 is actual field + oappend(SET_F("addInfo('Temperature:parasite-pwr-pin")); + oappend(SET_F("',1,'(for external MOSFET)');")); // 0 is field type, 1 is actual field } uint16_t getId() @@ -384,3 +402,4 @@ const char UsermodTemperature::_name[] PROGMEM = "Temperature"; const char UsermodTemperature::_enabled[] PROGMEM = "enabled"; const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s"; const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr"; +const char UsermodTemperature::_parasitePin[] PROGMEM = "parasite-pwr-pin"; diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 0bba5ffa..182e8d7a 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -225,17 +225,20 @@ static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequen static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0};// Our calculated freq. channel result table to be used by effects #if defined(WLED_DEBUG) || defined(SR_DEBUG) || defined(SR_STATS) -static uint64_t fftTime = 0; -static uint64_t sampleTime = 0; +static float fftTaskCycle = 0; // avg cycle time for FFT task +static float fftTime = 0; // avg time for single FFT +static float sampleTime = 0; // avg (blocked) time for reading I2S samples #endif // FFT Task variables (filtering and post-processing) +static float lastFftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // backup of last FFT channels (before postprocessing) static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON) #ifdef SR_DEBUG static float fftResultMax[NUM_GEQ_CHANNELS] = {0.0f}; // A table used for testing to determine how our post-processing is working. #endif +#if !defined(CONFIG_IDF_TARGET_ESP32C3) // audio source parameters and constant constexpr SRate_t SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz is a standard rate. Physical sample time -> 23ms //constexpr SRate_t SAMPLE_RATE = 16000; // 16kHz - use if FFTtask takes more than 20ms. Physical sample time -> 32ms @@ -245,6 +248,16 @@ constexpr SRate_t SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz //#define FFT_MIN_CYCLE 30 // Use with 16Khz sampling //#define FFT_MIN_CYCLE 23 // minimum time before FFT task is repeated. Use with 20Khz sampling //#define FFT_MIN_CYCLE 46 // minimum time before FFT task is repeated. Use with 10Khz sampling +#else +// slightly lower the sampling rate for -C3, to improve stability +//constexpr SRate_t SAMPLE_RATE = 20480; // 20Khz; Physical sample time -> 25ms +//#define FFT_MIN_CYCLE 23 // minimum time before FFT task is repeated. +constexpr SRate_t SAMPLE_RATE = 18000; // 18Khz; Physical sample time -> 28ms +#define FFT_MIN_CYCLE 25 // minimum time before FFT task is repeated. +// try 16Khz in case your device still lags and responds too slowly. +//constexpr SRate_t SAMPLE_RATE = 16000; // 16Khz -> Physical sample time -> 32ms +//#define FFT_MIN_CYCLE 30 // minimum time before FFT task is repeated. +#endif // FFT Constants constexpr uint16_t samplesFFT = 512; // Samples in an FFT batch - This value MUST ALWAYS be a power of 2 @@ -308,6 +321,11 @@ static float fftAddAvg(int from, int to) { } #endif +#if defined(CONFIG_IDF_TARGET_ESP32C3) +constexpr bool skipSecondFFT = true; +#else +constexpr bool skipSecondFFT = false; +#endif // // FFT main task // @@ -317,6 +335,8 @@ void FFTcode(void * parameter) // see https://www.freertos.org/vtaskdelayuntil.html const TickType_t xFrequency = FFT_MIN_CYCLE * portTICK_PERIOD_MS; + const TickType_t xFrequencyDouble = FFT_MIN_CYCLE * portTICK_PERIOD_MS * 2; + static bool isFirstRun = false; TickType_t xLastWakeTime = xTaskGetTickCount(); for(;;) { @@ -325,6 +345,7 @@ void FFTcode(void * parameter) // Don't run FFT computing code if we're in Receive mode or in realtime mode if (disableSoundProcessing || (audioSyncEnabled & 0x02)) { + isFirstRun = false; vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers continue; } @@ -332,6 +353,15 @@ void FFTcode(void * parameter) #if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS) 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 + uint64_t taskTimeInMillis = ((start - lastCycleStart) +5ULL) / 10ULL; // "+5" to ensure proper rounding + fftTaskCycle = (((taskTimeInMillis + lastLastTime)/2) *4 + fftTaskCycle*6)/10.0; // smart smooth + lastLastTime = taskTimeInMillis; + } + lastCycleStart = start; #endif // get a fresh batch of samples from I2S @@ -340,12 +370,13 @@ void FFTcode(void * parameter) #if defined(WLED_DEBUG) || defined(SR_DEBUG)|| defined(SR_STATS) 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; // smooth + sampleTime = (sampleTimeInMillis*3 + sampleTime*7)/10.0; // smooth } start = esp_timer_get_time(); // start measuring FFT time #endif xLastWakeTime = xTaskGetTickCount(); // update "last unblocked time" for vTaskDelay + isFirstRun = !isFirstRun; // toggle throtte #ifdef MIC_LOGGER float datMin = 0.0f; @@ -399,38 +430,41 @@ void FFTcode(void * parameter) // run FFT (takes 3-5ms on ESP32) //if (fabsf(sampleAvg) > 0.25f) { // noise gate open if (fabsf(volumeSmth) > 0.25f) { // noise gate open + if ((skipSecondFFT == false) || (isFirstRun == true)) { + // run FFT (takes 2-3ms on ESP32, ~12ms on ESP32-S2, ~30ms on -C3) + #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT + FFT.dcRemoval(); // remove DC offset + #if !defined(FFT_PREFER_EXACT_PEAKS) + FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy + #else + FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection + #endif + FFT.compute( FFTDirection::Forward ); // Compute FFT + FFT.complexToMagnitude(); // Compute magnitudes + #else + FFT.DCRemoval(); // let FFT lib remove DC component, so we don't need to care about this in getSamples() - // run FFT (takes 3-5ms on ESP32, ~12ms on ESP32-S2) -#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT - FFT.dcRemoval(); // remove DC offset - #if !defined(FFT_PREFER_EXACT_PEAKS) - FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy - #else - FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection - #endif - FFT.compute( FFTDirection::Forward ); // Compute FFT - FFT.complexToMagnitude(); // Compute magnitudes -#else - FFT.DCRemoval(); // let FFT lib remove DC component, so we don't need to care about this in getSamples() + //FFT.Windowing( FFT_WIN_TYP_HAMMING, FFT_FORWARD ); // Weigh data - standard Hamming window + //FFT.Windowing( FFT_WIN_TYP_BLACKMAN, FFT_FORWARD ); // Blackman window - better side freq rejection + #if !defined(FFT_PREFER_EXACT_PEAKS) + FFT.Windowing( FFT_WIN_TYP_FLT_TOP, FFT_FORWARD ); // Flat Top Window - better amplitude accuracy + #else + FFT.Windowing( FFT_WIN_TYP_BLACKMAN_HARRIS, FFT_FORWARD );// Blackman-Harris - excellent sideband rejection + #endif + FFT.Compute( FFT_FORWARD ); // Compute FFT + FFT.ComplexToMagnitude(); // Compute magnitudes + #endif - //FFT.Windowing( FFT_WIN_TYP_HAMMING, FFT_FORWARD ); // Weigh data - standard Hamming window - //FFT.Windowing( FFT_WIN_TYP_BLACKMAN, FFT_FORWARD ); // Blackman window - better side freq rejection - #if !defined(FFT_PREFER_EXACT_PEAKS) - FFT.Windowing( FFT_WIN_TYP_FLT_TOP, FFT_FORWARD ); // Flat Top Window - better amplitude accuracy - #else - FFT.Windowing( FFT_WIN_TYP_BLACKMAN_HARRIS, FFT_FORWARD );// Blackman-Harris - excellent sideband rejection - #endif - FFT.Compute( FFT_FORWARD ); // Compute FFT - FFT.ComplexToMagnitude(); // Compute magnitudes -#endif - -#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT - FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant -#else - FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant -#endif - FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects + #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT + FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant + #else + FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant + #endif + FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects + } else { // skip second run --> clear fft results, keep peaks + memset(vReal, 0, sizeof(vReal)); + } #if defined(WLED_DEBUG) || defined(SR_DEBUG) || defined(SR_STATS) haveDoneFFT = true; #endif @@ -441,14 +475,16 @@ void FFTcode(void * parameter) FFT_Magnitude = 0.001; } - for (int i = 0; i < samplesFFT; i++) { - float t = fabsf(vReal[i]); // just to be sure - values in fft bins should be positive any way - vReal[i] = t / 16.0f; // Reduce magnitude. Want end result to be scaled linear and ~4096 max. - } // for() + if ((skipSecondFFT == false) || (isFirstRun == true)) { - // mapping of FFT result bins to frequency channels - //if (fabsf(sampleAvg) > 0.25f) { // noise gate open - if (fabsf(volumeSmth) > 0.25f) { // noise gate open + for (int i = 0; i < samplesFFT; i++) { + float t = fabsf(vReal[i]); // just to be sure - values in fft bins should be positive any way + vReal[i] = t / 16.0f; // Reduce magnitude. Want end result to be scaled linear and ~4096 max. + } // for() + + // mapping of FFT result bins to frequency channels + //if (fabsf(sampleAvg) > 0.25f) { // noise gate open + if (fabsf(volumeSmth) > 0.25f) { // noise gate open #if 0 /* This FFT post processing is a DIY endeavour. What we really need is someone with sound engineering expertise to do a great job here AND most importantly, that the animations look GREAT as a result. * @@ -506,24 +542,34 @@ void FFTcode(void * parameter) fftCalc[13] = fftAddAvg(86,104); // 18 3704 - 4479 high mid fftCalc[14] = fftAddAvg(104,165) * 0.88f; // 61 4479 - 7106 high mid + high -- with slight damping #endif - } else { // noise gate closed - just decay old values - for (int i=0; i < NUM_GEQ_CHANNELS; i++) { - fftCalc[i] *= 0.85f; // decay to zero - if (fftCalc[i] < 4.0f) fftCalc[i] = 0.0f; + } else { // noise gate closed - just decay old values + isFirstRun = false; + for (int i=0; i < NUM_GEQ_CHANNELS; i++) { + fftCalc[i] *= 0.85f; // decay to zero + if (fftCalc[i] < 4.0f) fftCalc[i] = 0.0f; + } } + + memcpy(lastFftCalc, fftCalc, sizeof(lastFftCalc)); // make a backup of last "good" channels + + } else { // if second run skipped + memcpy(fftCalc, lastFftCalc, sizeof(fftCalc)); // restore last "good" channels } // post-processing of frequency channels (pink noise adjustment, AGC, smooting, scaling) if (pinkIndex > MAX_PINK) pinkIndex = MAX_PINK; //postProcessFFTResults((fabsf(sampleAvg) > 0.25f)? true : false , NUM_GEQ_CHANNELS); - postProcessFFTResults((fabsf(volumeSmth)>0.25f)? true : false , NUM_GEQ_CHANNELS); + 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) + 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 - fftTime = (fftTimeInMillis*3 + fftTime*7)/10; // smooth + fftTime = (((fftTimeInMillis + lastLastFFT)/2) *3 + fftTime*7)/10.0; // smart smooth + lastLastFFT = fftTimeInMillis; } #endif + // run peak detection autoResetPeak(); detectSamplePeak(); @@ -531,8 +577,13 @@ void FFTcode(void * parameter) #if !defined(I2S_GRAB_ADC1_COMPLETELY) if ((audioSource == nullptr) || (audioSource->getType() != AudioSource::Type_I2SAdc)) // the "delay trick" does not help for analog ADC #endif - vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers - + { + if ((skipSecondFFT == false) || (fabsf(volumeSmth) < 0.25f)) { + vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers + } else if (isFirstRun == true) { + vTaskDelayUntil( &xLastWakeTime, xFrequencyDouble); // release CPU after performing FFT in "skip second run" mode + } + } } // for(;;)ever } // FFTcode() task end @@ -769,7 +820,11 @@ class AudioReactive : public Usermod { }; // set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer) + #ifdef SR_ENABLE_DEFAULT + bool enabled = true; // WLEDMM + #else bool enabled = false; + #endif bool initDone = false; // variables for UDP sound sync @@ -1774,12 +1829,16 @@ class AudioReactive : public Usermod { } #if defined(WLED_DEBUG) || defined(SR_DEBUG) || defined(SR_STATS) + infoArr = user.createNestedArray(F("I2S cycle time")); + infoArr.add(roundf(fftTaskCycle)/100.0f); + infoArr.add(" ms"); + infoArr = user.createNestedArray(F("Sampling time")); - infoArr.add(float(sampleTime)/100.0f); + infoArr.add(roundf(sampleTime)/100.0f); infoArr.add(" ms"); infoArr = user.createNestedArray(F("FFT time")); - infoArr.add(float(fftTime)/100.0f); + infoArr.add(roundf(fftTime)/100.0f); if ((fftTime/100) >= FFT_MIN_CYCLE) // FFT time over budget -> I2S buffer will overflow infoArr.add("! ms"); else if ((fftTime/80 + sampleTime/80) >= FFT_MIN_CYCLE) // FFT time >75% of budget -> risk of instability @@ -1787,8 +1846,9 @@ class AudioReactive : public Usermod { else infoArr.add(" ms"); - DEBUGSR_PRINTF("AR Sampling time: %5.2f ms\n", float(sampleTime)/100.0f); - DEBUGSR_PRINTF("AR FFT time : %5.2f ms\n", float(fftTime)/100.0f); + DEBUGSR_PRINTF("AR I2S cycle time: %5.2f ms\n", roundf(fftTaskCycle)/100.0f); + DEBUGSR_PRINTF("AR Sampling time : %5.2f ms\n", roundf(sampleTime)/100.0f); + DEBUGSR_PRINTF("AR FFT time : %5.2f ms\n", roundf(fftTime)/100.0f); #endif } } diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index 62ded98d..276711de 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -23,7 +23,7 @@ // see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/chip-series-comparison.html#related-documents // and https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#overview-of-all-modes -#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265) +#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265) // there are two things in these MCUs that could lead to problems with audio processing: // * no floating point hardware (FPU) support - FFT uses float calculations. If done in software, a strong slow-down can be expected (between 8x and 20x) // * single core, so FFT task might slow down other things like LED updates diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 2a952981..9906b3de 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5892,15 +5892,21 @@ uint16_t mode_2Dscrollingtext(void) { SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color } -} + } for (int i = 0; i < numberOfLetters; i++) { if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen - SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0)); + uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0); + uint32_t col2 = BLACK; + if (SEGMENT.check1 && SEGMENT.palette == 0) { + col1 = SEGCOLOR(0); + col2 = SEGCOLOR(2); + } + SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2); } return FRAMETIME; } -static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,,Overlay;!,!;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0"; +static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0"; //////////////////////////// @@ -7138,6 +7144,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. band = constrain(band, 0, 15); uint16_t colorIndex = band * 17; uint16_t barHeight = map(fftResult[band], 0, 255, 0, rows); // do not subtract -1 from rows here + if (barHeight > rows) barHeight = rows; // WLEDMM map() can "overshoot" due to rounding errors if (barHeight > previousBarHeight[x]) previousBarHeight[x] = barHeight; //drive the peak up uint32_t ledColor = BLACK; @@ -7148,7 +7155,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); SEGMENT.setPixelColorXY(x, rows-1 - y, ledColor); } - if (previousBarHeight[x] > 0) + if ((previousBarHeight[x] > 0) && (previousBarHeight[x] < rows)) // WLEDMM avoid "overshooting" into other segments SEGMENT.setPixelColorXY(x, rows - previousBarHeight[x], (SEGCOLOR(2) != BLACK) ? SEGCOLOR(2) : ledColor); if (rippleTime && previousBarHeight[x]>0) previousBarHeight[x]--; //delay/ripple effect diff --git a/wled00/FX.h b/wled00/FX.h index 9ae94700..97019624 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -602,8 +602,8 @@ typedef struct Segment { void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, uint32_t fillColor = 0); void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.b,0)); } // automatic inline - void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color); - void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0); + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline void wu_pixel(uint32_t x, uint32_t y, CRGB c); void blur1d(fract8 blur_amount); // blur all rows in 1 dimension void blur2d(fract8 blur_amount) { blur(blur_amount); } diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index a6aecda3..07116d0b 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -531,13 +531,16 @@ void Segment::drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, // draws a raster font character on canvas // only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM -void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color) { +void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) { if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported chr -= 32; // align with font table entries const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); const int font = w*h; + CRGB col = CRGB(color); + CRGBPalette16 grad = CRGBPalette16(col, col2 ? CRGB(col2) : col); + //if (w<5 || w>6 || h!=8) return; for (int i = 0; i= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen - addPixelColorXY(x0, y0, color); + addPixelColorXY(x0, y0, col); } } } diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 4f92080e..c76ec8d4 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -9,6 +9,10 @@ var d=document,laprev=55,maxB=1,maxV=0,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32 var customStarts=false,startsDirty=[],maxCOOverrides=5; var loc = false, locip; + d.um_p = []; + d.rsvd = []; + d.ro_gpio = []; + d.max_gpio = 39; function H(){window.open("https://mm.kno.wled.ge/features/settings/#led-settings");} function B(){window.open("/settings","_self");} function gId(n){return d.getElementById(n);} @@ -24,10 +28,6 @@ // success event scE.addEventListener("load", () => { //console.log("File loaded"); - d.um_p = []; - d.rsvd = []; - d.ro_pins = []; - d.max_gpio = 39; GetV();checkSi();setABL(); if (d.um_p[0]==-1) d.um_p.shift(); }); @@ -67,7 +67,7 @@ for (k=0;ke==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(p)} can't be used.`);LCs[i].value="";LCs[i].focus();return false;} - else if (!(nm == "IR" || nm=="BT") && d.ro_pins.some((e)=>e==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(d.ro_gpio)} are input only.`);LCs[i].value="";LCs[i].focus();return false;} + else if (!(nm == "IR" || nm=="BT") && d.ro_gpio.some((e)=>e==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(d.ro_gpio)} are input only.`);LCs[i].value="";LCs[i].focus();return false;} for (j=i+1; j`; - for (var j=-1; j<=d.max_gpio; j++) { // all possible pins + for (var j=-1; j<=50; j++) { // all possible pins WLEDMM: hardcoded to 50 as d.max_gpio is not calculated yet here (done in appendGPIOinfo) let foundPin = -1; for (var i=0; i max_gpio function pinDropdownsPost() { - // console.log('pinDropdownsPost', d.max_gpio, d.ro_pins, d.ro_gpio, d.rsvd); + // console.log('pinDropdownsPost', d.max_gpio, d.ro_gpio, d.rsvd); var elements = gId("form_s").elements; for (var i = 0, select; select = elements[i++];) { @@ -244,14 +244,15 @@ // console.log("pinDropdownsPost option", c, c.value, d.ro_gpio.includes(c.value)); for (let j=0; j= max_gpio + //remove pins > max_gpio if (c.value > d.max_gpio) { select.removeChild(c); i--; //decrease i by one because the index has been adjusted } //https://www.javascripttutorial.net/javascript-dom/javascript-add-remove-options/ //https://www.javascripttutorial.net/javascript-dom/javascript-remove-items-from-a-select-conditionally/ - if (c.text.length <= 2) c.text += " 🟢"; + if (c.text.length <= 4) c.text += " 🟢"; //2 digit number space and ⍼/⎌. If no reserved/read only/other um, then pin can be freely used (green) + for (let jj=0; jj= 0) return true; + #endif + return false; // fall-through case + } + + // returns true if gpio supports analogRead + bool PinManagerClass::isPinAnalog(int gpio) { + #if !defined(ARDUINO_ARCH_ESP32) + if (gpio == A0) return true; // for 8266 + #else // for ESP32 variants + if (digitalPinToAnalogChannel(gpio) >= 0) return true; + #endif + return false; // fall-through case + } + + // returns true if gpio supports analogRead, and it belongs to ADC unit 1 + bool PinManagerClass::isPinADC1(int gpio) { + if ((gpio < 0) || !isPinAnalog(gpio)) return false; + + #if !defined(ARDUINO_ARCH_ESP32) + if (gpio == A0) return true; // for 8266 + #else // for ESP32 variants + #ifdef SOC_ADC_CHANNEL_NUM + if (digitalPinToAnalogChannel(gpio) < SOC_ADC_CHANNEL_NUM(0)) return true; // ADC1 on ESP32-S3, ESP32-S2, ESP32-C3 + #else + if (digitalPinToAnalogChannel(gpio) < 8) return true; // ADC1 on classic ESP32 + #endif + #endif + return false; // fall-through case + } + + // returns true if gpio supports analogRead, and it belongs to ADC unit 2 + bool PinManagerClass::isPinADC2(int gpio) { + if ((gpio < 0) || !isPinAnalog(gpio)) return false; // catch errors + + #if !defined(ARDUINO_ARCH_ESP32) + return false; // for 8266 - no ADC2 + #else // for ESP32 variants + if (isPinADC1(gpio) == false) return true; // analog but not ADC1 --> must be ADC2 + #endif + return false; // fall-through case + } + + // returns GPIO number for ADC unit x, channel y. 255 = no such pin + // see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html#gpio-summary + uint8_t PinManagerClass::getADCPin(AdcIdentifier adcUnit, uint8_t adcPort) + { + #if !defined(ARDUINO_ARCH_ESP32) + if ((adcUnit == ADC1) && (adcPort == 0)) return A0; // for 8266 + else return(PM_NO_PIN); + + #else // for ESP32 variants + if ((adcUnit != ADC1) && (adcUnit != ADC2)) return(PM_NO_PIN); // catch errors + + #if defined(SOC_ADC_MAX_CHANNEL_NUM) // for ESP32-S3, ESP32-S2, ESP32-C3 + int8_t analogChannel = (adcUnit == ADC1) ? adcPort : (SOC_ADC_MAX_CHANNEL_NUM + adcPort); + if (adcPort >= SOC_ADC_MAX_CHANNEL_NUM) analogChannel = 255; + #else // for classic ESP32 + int8_t analogChannel = (adcUnit == ADC1) ? adcPort : (10 + adcPort); + if (adcPort >= 10) analogChannel = 255; + #endif + + //int analogPin = analogChannelToDigitalPin(analogChannel); + int analogPin = analogInputToDigitalPin(analogChannel); + if (analogPin >= 0) return(analogPin); + else return(PM_NO_PIN); + #endif + + return(PM_NO_PIN); // fall-through case + } + + // WLEDMM end + + /* see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/gpio.html * The ESP32-S3 chip features 45 physical GPIO pins (GPIO0 ~ GPIO21 and GPIO26 ~ GPIO48). Each pin can be used as a general-purpose I/O * Strapping pins: GPIO0, GPIO3, GPIO45 and GPIO46 are strapping pins. For more infomation, please refer to ESP32-S3 datasheet. diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index 508d9cac..21029320 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -67,7 +67,7 @@ static_assert(0u == static_cast(PinOwner::None), "PinOwner::None must b class PinManagerClass { private: #ifdef ESP8266 - #define WLED_NUM_PINS 17 + #define WLED_NUM_PINS 18 // WLEDMM include A0 = gpio17 uint8_t pinAlloc[3] = {0x00, 0x00, 0x00}; //24bit, 1 bit per pin, we use first 17bits PinOwner ownerTag[WLED_NUM_PINS] = { PinOwner::None }; PinOwner ownerConflict[WLED_NUM_PINS] = { PinOwner::None }; // WLEDMM: record pin alloc conflicts @@ -124,10 +124,22 @@ class PinManagerClass { bool isPinOk(byte gpio, bool output = true); PinOwner getPinOwner(byte gpio); + + // WLEDMM begin String getOwnerText(PinOwner tag); // WLEDMM - return PIN owner tag as text String getPinOwnerText(int gpio); // WLEDMM - return PIN owner as text String getPinSpecialText(int gpio); // WLEDMM - return PIN special comments (if any) String getPinConflicts(int gpio); // WLEDMM - return PIN alloc conflicts (if any) + + bool isPinTouch(int gpio); // true if gpio supports touch functions + bool isPinAnalog(int gpio); // true if gpio supports analogRead + bool isPinADC1(int gpio); // true if gpio supports analogRead, and it belongs to ADC unit 1 + bool isPinADC2(int gpio); // true if gpio supports analogRead, and it belongs to ADC unit 2 + #define PM_NO_PIN 255 + typedef enum { ADC_none = 0, ADC1 = 1, ADC2 = 2 } AdcIdentifier; + uint8_t getADCPin(AdcIdentifier adcUnit, uint8_t adcPort); // get GPIO number for ADC unit x, channel y. 255 = no such pin + // WLEDMM end + #ifdef ARDUINO_ARCH_ESP32 byte allocateLedc(byte channels); void deallocateLedc(byte pos, byte channels); diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 23427146..8eef3ba2 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -575,6 +575,27 @@ void WLED::setup() USER_FLUSH(); // avoid lost lines (Serial buffer overflow) } } + +#if 0 // for testing + USER_PRINTLN(F("\n")); + USER_PRINTF("ADC1-0 = %d, ADC1-3 = %d, ADC1-7 = %d, ADC2-0 = %d, ADC2-1 = %d, ADC2-8 = %d, ADC2-10 = %d\n", + pinManager.getADCPin(PinManagerClass::ADC1, 0), pinManager.getADCPin(PinManagerClass::ADC1, 3), pinManager.getADCPin(PinManagerClass::ADC1, 7), + pinManager.getADCPin(PinManagerClass::ADC2, 0), pinManager.getADCPin(PinManagerClass::ADC2, 1), pinManager.getADCPin(PinManagerClass::ADC2, 8), + pinManager.getADCPin(PinManagerClass::ADC2, 10) + ); + USER_PRINTLN(); + for(int p=0; p<12; p++) { + if(pinManager.getADCPin(PinManagerClass::ADC1, p) < 255) + USER_PRINTF("ADC1-%d = %d, ", p, pinManager.getADCPin(PinManagerClass::ADC1, p)); + } + USER_PRINTLN(); + for(int p=0; p<12; p++) { + if(pinManager.getADCPin(PinManagerClass::ADC2, p) < 255) + USER_PRINTF("ADC2-%d = %d, ", p, pinManager.getADCPin(PinManagerClass::ADC2, p)); + } + USER_PRINTLN(F("\n")); +#endif + USER_PRINTLN(F("WLED initialization done.\n")); delay(50); // repeat Ada prompt @@ -784,8 +805,10 @@ void WLED::initConnection() WiFi.begin(clientSSID, clientPass); #ifdef ARDUINO_ARCH_ESP32 - // WLEDMM - if your board has issues connecting to WiFi, try uncommenting this - // WiFi.setTxPower(WIFI_POWER_5dBm); // required for ESP32-C3FH4-RGB +#ifdef WLEDMM_WIFI_POWERON_HACK + // WLEDMM - if your board has issues connecting to WiFi, try this + WiFi.setTxPower(WIFI_POWER_5dBm); // required for ESP32-C3FH4-RGB +#endif WiFi.setSleep(!noWifiSleep); WiFi.setHostname(hostname); #else diff --git a/wled00/wled.h b/wled00/wled.h index 405af084..34e41c65 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2301170 +#define VERSION 2301202 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG diff --git a/wled00/xml.cpp b/wled00/xml.cpp index b0674cf6..84f70bbc 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -273,6 +273,14 @@ void appendGPIOinfo() { oappendi(16); #endif oappend(SET_F(";")); + + char dt_pins[30]; + #if defined(ESP8266) && !defined(ARDUINO_ESP8266_ESP01) + sprintf(dt_pins, "d.dt_pins=[%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d];", D0, D1, D2, D3, D4, D5, D6, D7, D8, hardwareRX, hardwareTX); + #else + sprintf(dt_pins, "d.dt_pins=[%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d];", 99, 99, 99, 99, 99, 99, 99, 99, 99, hardwareRX, hardwareTX); + #endif + oappend(dt_pins); } //get values for settings form in javascript @@ -748,7 +756,7 @@ void getSettingsJS(AsyncWebServerRequest* request, byte subPage, char* dest) //W if (usermod) usermod->appendConfigData(); } - // oappend(SET_F("console.log('getSettingsJS fix ro pins', d.max_gpio, d.ro_pins, d.ro_gpio);")); + // oappend(SET_F("console.log('getSettingsJS fix ro pins', d.max_gpio, d.ro_gpio);")); oappend(SET_F("pinDropdownsPost();")); }