It compiles!
Cleaned (and possibly broken) AudioSource Added: - usermod notification about update - strip.getMinShowDelay() - pin manager updates Changed: - data exchange
This commit is contained in:
parent
dd584e929f
commit
562a206508
@ -106,7 +106,6 @@ static uint16_t micDataSm; // Smoothed mic data, as it's a
|
|||||||
static float micDataReal = 0.0f; // future support - this one has the full 24bit MicIn data - lowest 8bit after decimal point
|
static float micDataReal = 0.0f; // future support - this one has the full 24bit MicIn data - lowest 8bit after decimal point
|
||||||
static byte soundAgc = 0; // default Automagic gain control
|
static byte soundAgc = 0; // default Automagic gain control
|
||||||
static float multAgc = 1.0f; // sample * multAgc = sampleAgc. Our multiplier
|
static float multAgc = 1.0f; // sample * multAgc = sampleAgc. Our multiplier
|
||||||
static uint16_t noiseFloor = 100; // default squelch value for FFT reactive routines
|
|
||||||
|
|
||||||
static double FFT_MajorPeak = 0;
|
static double FFT_MajorPeak = 0;
|
||||||
static double FFT_Magnitude = 0;
|
static double FFT_Magnitude = 0;
|
||||||
@ -127,13 +126,14 @@ static float fftResultMax[16]; // A table used for test
|
|||||||
static float fftAvg[16];
|
static float fftAvg[16];
|
||||||
|
|
||||||
// Table of linearNoise results to be multiplied by soundSquelch in order to reduce squelch across fftResult bins.
|
// Table of linearNoise results to be multiplied by soundSquelch in order to reduce squelch across fftResult bins.
|
||||||
static uint16_t linearNoise[16] = { 34, 28, 26, 25, 20, 12, 9, 6, 4, 4, 3, 2, 2, 2, 2, 2 };
|
static uint8_t linearNoise[16] = { 34, 28, 26, 25, 20, 12, 9, 6, 4, 4, 3, 2, 2, 2, 2, 2 };
|
||||||
|
|
||||||
// Table of multiplication factors so that we can even out the frequency response.
|
// Table of multiplication factors so that we can even out the frequency response.
|
||||||
static float fftResultPink[16] = { 1.70f, 1.71f, 1.73f, 1.78f, 1.68f, 1.56f, 1.55f, 1.63f, 1.79f, 1.62f, 1.80f, 2.06f, 2.47f, 3.35f, 6.83f, 9.55f };
|
static float fftResultPink[16] = { 1.70f, 1.71f, 1.73f, 1.78f, 1.68f, 1.56f, 1.55f, 1.63f, 1.79f, 1.62f, 1.80f, 2.06f, 2.47f, 3.35f, 6.83f, 9.55f };
|
||||||
|
|
||||||
// Create FFT object
|
// Create FFT object
|
||||||
static arduinoFFT FFT = arduinoFFT(vReal, vImag, samples, SAMPLE_RATE);
|
static arduinoFFT FFT = arduinoFFT(vReal, vImag, samples, SAMPLE_RATE);
|
||||||
|
static TaskHandle_t FFT_Task;
|
||||||
|
|
||||||
float fftAdd(int from, int to) {
|
float fftAdd(int from, int to) {
|
||||||
float result = 0.0f;
|
float result = 0.0f;
|
||||||
@ -156,15 +156,13 @@ void FFTcode(void * parameter) {
|
|||||||
// taskYIELD(), yield(), vTaskDelay() and esp_task_wdt_feed() didn't seem to work.
|
// taskYIELD(), yield(), vTaskDelay() and esp_task_wdt_feed() didn't seem to work.
|
||||||
|
|
||||||
// Only run the FFT computing code if we're not in Receive mode
|
// Only run the FFT computing code if we're not in Receive mode
|
||||||
if (audioSyncEnabled & 0x02)
|
if (audioSyncEnabled & 0x02) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
audioSource->getSamples(vReal, samplesFFT);
|
audioSource->getSamples(vReal, samplesFFT);
|
||||||
|
|
||||||
// old code - Last sample in vReal is our current mic sample
|
// old code - Last sample in vReal is our current mic sample
|
||||||
//micDataSm = (uint16_t)vReal[samples - 1]; // will do a this a bit later
|
//micDataSm = (uint16_t)vReal[samples - 1]; // will do a this a bit later
|
||||||
|
//micDataSm = ((micData * 3) + micData)/4;
|
||||||
// micDataSm = ((micData * 3) + micData)/4;
|
|
||||||
|
|
||||||
const int halfSamplesFFT = samplesFFT / 2; // samplesFFT divided by 2
|
const int halfSamplesFFT = samplesFFT / 2; // samplesFFT divided by 2
|
||||||
double maxSample1 = 0.0; // max sample from first half of FFT batch
|
double maxSample1 = 0.0; // max sample from first half of FFT batch
|
||||||
@ -356,6 +354,21 @@ class AudioReactive : public Usermod {
|
|||||||
#else
|
#else
|
||||||
int8_t i2sckPin = I2S_CKPIN;
|
int8_t i2sckPin = I2S_CKPIN;
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef ES7243_SDAPIN
|
||||||
|
int8_t sdaPin = 18;
|
||||||
|
#else
|
||||||
|
int8_t sdaPin = ES7243_SDAPIN;
|
||||||
|
#endif
|
||||||
|
#ifndef ES7243_SDAPIN
|
||||||
|
int8_t sclPin = 23;
|
||||||
|
#else
|
||||||
|
int8_t sclPin = ES7243_SCLPIN;
|
||||||
|
#endif
|
||||||
|
#ifndef MCLK_PIN
|
||||||
|
int8_t mclkPin = 0;
|
||||||
|
#else
|
||||||
|
int8_t mclkPin = MLCK_PIN;
|
||||||
|
#endif
|
||||||
|
|
||||||
#define UDP_SYNC_HEADER "00001"
|
#define UDP_SYNC_HEADER "00001"
|
||||||
struct audioSyncPacket {
|
struct audioSyncPacket {
|
||||||
@ -389,8 +402,8 @@ class AudioReactive : public Usermod {
|
|||||||
float sampleAdj; // Gain adjusted sample value
|
float sampleAdj; // Gain adjusted sample value
|
||||||
float sampleAgc = 0.0f; // Our AGC sample
|
float sampleAgc = 0.0f; // Our AGC sample
|
||||||
int16_t rawSampleAgc = 0; // Our AGC sample - raw
|
int16_t rawSampleAgc = 0; // Our AGC sample - raw
|
||||||
long timeOfPeak = 0;
|
uint32_t timeOfPeak = 0;
|
||||||
long lastTime = 0;
|
uint32_t lastTime = 0;
|
||||||
float micLev = 0.0f; // Used to convert returned value to have '0' as minimum. A leveller
|
float micLev = 0.0f; // Used to convert returned value to have '0' as minimum. A leveller
|
||||||
float sampleAvg = 0.0f; // Smoothed Average
|
float sampleAvg = 0.0f; // Smoothed Average
|
||||||
float beat = 0.0f; // beat Detection
|
float beat = 0.0f; // beat Detection
|
||||||
@ -680,36 +693,6 @@ class AudioReactive : public Usermod {
|
|||||||
} // getSample()
|
} // getSample()
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A simple averaging multiplier to automatically adjust sound sensitivity.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* A simple, but hairy, averaging multiplier to automatically adjust sound sensitivity.
|
|
||||||
* not sure if not sure "sample" or "sampleAvg" is the correct input signal for AGC
|
|
||||||
*/
|
|
||||||
void agcAvg() {
|
|
||||||
|
|
||||||
float lastMultAgc = multAgc;
|
|
||||||
float tmpAgc;
|
|
||||||
if(fabs(sampleAvg) < 2.0f) {
|
|
||||||
tmpAgc = sampleAvg; // signal below squelch -> deliver silence
|
|
||||||
multAgc = multAgc * 0.95f; // slightly decrease gain multiplier
|
|
||||||
} else {
|
|
||||||
multAgc = (sampleAvg < 1) ? targetAgc : targetAgc / sampleAvg; // Make the multiplier so that sampleAvg * multiplier = setpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
if (multAgc < 0.5f) multAgc = 0.5f; // signal higher than 2*setpoint -> don't reduce it further
|
|
||||||
multAgc = (lastMultAgc*127.0f +multAgc) / 128.0f; //apply some filtering to gain multiplier -> smoother transitions
|
|
||||||
tmpAgc = (float)sample * multAgc; // apply gain to signal
|
|
||||||
if (tmpAgc <= (soundSquelch*1.2f)) tmpAgc = sample; // check against squelch threshold - increased by 20% to avoid artefacts (ripples)
|
|
||||||
|
|
||||||
if (tmpAgc > 255) tmpAgc = 255;
|
|
||||||
sampleAgc = tmpAgc; // ONLY update sampleAgc ONCE because it's used elsewhere asynchronously!!!!
|
|
||||||
userVar0 = sampleAvg * 4;
|
|
||||||
if (userVar0 > 255) userVar0 = 255;
|
|
||||||
} // agcAvg()
|
|
||||||
|
|
||||||
|
|
||||||
void transmitAudioData() {
|
void transmitAudioData() {
|
||||||
if (!udpSyncConnected) return;
|
if (!udpSyncConnected) return;
|
||||||
|
|
||||||
@ -752,22 +735,25 @@ class AudioReactive : public Usermod {
|
|||||||
// usermod exchangeable data
|
// usermod exchangeable data
|
||||||
// we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers
|
// we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers
|
||||||
um_data = new um_data_t;
|
um_data = new um_data_t;
|
||||||
um_data->ub8_size = 2;
|
um_data->u_size = 8;
|
||||||
um_data->ub8_data = new (*uint8_t)[um_data->ub8_size];
|
um_data->u_type = new um_types_t[um_data->u_size];
|
||||||
um_data->ub8_data[0] = &maxVol;
|
um_data->u_data = new void*[um_data->u_size];
|
||||||
um_data->ub8_data[1] = fftResult;
|
um_data->u_data[0] = &maxVol;
|
||||||
um_data->ui32_size = 1;
|
um_data->u_type[0] = UMT_BYTE;
|
||||||
um_data->ui32_data = new (*int32_t)[um_data->ui32_size];
|
um_data->u_data[1] = fftResult;
|
||||||
um_data->ui32_data[0] = &sample;
|
um_data->u_type[1] = UMT_BYTE_ARR;
|
||||||
um_data->uf4_size = 3;
|
um_data->u_data[2] = &sample;
|
||||||
um_data->uf4_data = new (*float)[um_data->uf4_size];
|
um_data->u_type[2] = UMT_INT16;
|
||||||
um_data->uf4_data[0] = fftAvg;
|
um_data->u_data[3] = fftAvg;
|
||||||
um_data->uf4_data[1] = fftCalc;
|
um_data->u_type[3] = UMT_FLOAT_ARR;
|
||||||
um_data->uf4_data[2] = fftBin;
|
um_data->u_data[4] = fftCalc;
|
||||||
um_data->uf8_size = 2;
|
um_data->u_type[4] = UMT_FLOAT_ARR;
|
||||||
um_data->uf8_data = new (*double)[um_data->uf8_size];
|
um_data->u_data[5] = fftBin;
|
||||||
um_data->uf8_data[0] = &FFT_MajorPeak;
|
um_data->u_type[5] = UMT_FLOAT_ARR;
|
||||||
um_data->uf8_data[1] = &FFT_Magnitude;
|
um_data->u_data[6] = &FFT_MajorPeak;
|
||||||
|
um_data->u_type[6] = UMT_DOUBLE;
|
||||||
|
um_data->u_data[7] = &FFT_Magnitude;
|
||||||
|
um_data->u_type[7] = UMT_DOUBLE;
|
||||||
//...
|
//...
|
||||||
// these are values used by effects in soundreactive fork
|
// these are values used by effects in soundreactive fork
|
||||||
//uint8_t *fftResult = um_data->;
|
//uint8_t *fftResult = um_data->;
|
||||||
@ -800,22 +786,32 @@ class AudioReactive : public Usermod {
|
|||||||
case 1:
|
case 1:
|
||||||
DEBUGSR_PRINTLN("AS: Generic I2S Microphone.");
|
DEBUGSR_PRINTLN("AS: Generic I2S Microphone.");
|
||||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
||||||
|
delay(100);
|
||||||
|
audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
DEBUGSR_PRINTLN("AS: ES7243 Microphone.");
|
DEBUGSR_PRINTLN("AS: ES7243 Microphone.");
|
||||||
audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
||||||
|
delay(100);
|
||||||
|
audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
DEBUGSR_PRINTLN("AS: SPH0645 Microphone");
|
DEBUGSR_PRINTLN("AS: SPH0645 Microphone");
|
||||||
audioSource = new SPH0654(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
audioSource = new SPH0654(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
||||||
|
delay(100);
|
||||||
|
audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
DEBUGSR_PRINTLN("AS: Generic I2S Microphone with Master Clock");
|
DEBUGSR_PRINTLN("AS: Generic I2S Microphone with Master Clock");
|
||||||
audioSource = new I2SSourceWithMasterClock(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
audioSource = new I2SSourceWithMasterClock(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
||||||
|
delay(100);
|
||||||
|
audioSource->initialize(mclkPin, i2swsPin, i2ssdPin, i2sckPin);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
DEBUGSR_PRINTLN("AS: I2S PDM Microphone");
|
DEBUGSR_PRINTLN("AS: I2S PDM Microphone");
|
||||||
audioSource = new I2SPdmSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
audioSource = new I2SPdmSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0xFFFFFFFF);
|
||||||
|
delay(100);
|
||||||
|
audioSource->initialize(i2swsPin, i2ssdPin);
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
default:
|
default:
|
||||||
@ -823,18 +819,17 @@ class AudioReactive : public Usermod {
|
|||||||
// we don't do the down-shift by 16bit any more
|
// we don't do the down-shift by 16bit any more
|
||||||
//audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE, -4, 0x0FFF); // request upscaling to 16bit - still produces too much noise
|
//audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE, -4, 0x0FFF); // request upscaling to 16bit - still produces too much noise
|
||||||
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0x0FFF); // keep at 12bit - less noise
|
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE, 0, 0x0FFF); // keep at 12bit - less noise
|
||||||
|
delay(100);
|
||||||
|
audioSource->initialize(audioPin);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(100);
|
|
||||||
|
|
||||||
audioSource->initialize();
|
|
||||||
delay(250);
|
delay(250);
|
||||||
|
|
||||||
//pinMode(LED_BUILTIN, OUTPUT);
|
|
||||||
|
|
||||||
//sampling_period_us = round(1000000*(1.0/SAMPLE_RATE));
|
//sampling_period_us = round(1000000*(1.0/SAMPLE_RATE));
|
||||||
|
|
||||||
|
onUpdateBegin(false); // create FFT task
|
||||||
|
/*
|
||||||
// Define the FFT Task and lock it to core 0
|
// Define the FFT Task and lock it to core 0
|
||||||
xTaskCreatePinnedToCore(
|
xTaskCreatePinnedToCore(
|
||||||
FFTcode, // Function to implement the task
|
FFTcode, // Function to implement the task
|
||||||
@ -844,6 +839,7 @@ class AudioReactive : public Usermod {
|
|||||||
1, // Priority of the task
|
1, // Priority of the task
|
||||||
&FFT_Task, // Task handle
|
&FFT_Task, // Task handle
|
||||||
0); // Core where the task should run
|
0); // Core where the task should run
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1008,11 +1004,27 @@ class AudioReactive : public Usermod {
|
|||||||
|
|
||||||
bool getUMData(um_data_t **data) {
|
bool getUMData(um_data_t **data) {
|
||||||
if (!data) return false; // no pointer provided by caller -> exit
|
if (!data) return false; // no pointer provided by caller -> exit
|
||||||
*data = &um_data;
|
*data = um_data;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void onUpdateBegin(bool init) {
|
||||||
|
if (init) vTaskDelete(FFT_Task); // update is about to begin, remove task to prevent crash
|
||||||
|
else { // update has failed or create task requested
|
||||||
|
// Define the FFT Task and lock it to core 0
|
||||||
|
xTaskCreatePinnedToCore(
|
||||||
|
FFTcode, // Function to implement the task
|
||||||
|
"FFT", // Name of the task
|
||||||
|
5000, // Stack size in words
|
||||||
|
NULL, // Task input parameter
|
||||||
|
1, // Priority of the task
|
||||||
|
&FFT_Task, // Task handle
|
||||||
|
0); // Core where the task should run
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
||||||
@ -1098,7 +1110,7 @@ class AudioReactive : public Usermod {
|
|||||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||||
|
|
||||||
JsonObject amic = top.createNestedObject(FPSTR(_analogmic));
|
JsonObject amic = top.createNestedObject(FPSTR(_analogmic));
|
||||||
top["pin"] = audioPin;
|
amic["pin"] = audioPin;
|
||||||
|
|
||||||
JsonObject dmic = top.createNestedObject(FPSTR(_digitalmic));
|
JsonObject dmic = top.createNestedObject(FPSTR(_digitalmic));
|
||||||
dmic[F("type")] = dmType;
|
dmic[F("type")] = dmType;
|
||||||
|
@ -20,29 +20,6 @@
|
|||||||
#define I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
#define I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MCLK_PIN
|
|
||||||
int mclkPin = 0;
|
|
||||||
#else
|
|
||||||
int mclkPin = MLCK_PIN;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ES7243_ADDR
|
|
||||||
int addr_ES7243 = 0x13;
|
|
||||||
#else
|
|
||||||
int addr_ES7243 = ES7243_ADDR;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ES7243_SDAPIN
|
|
||||||
int pin_ES7243_SDA = 18;
|
|
||||||
#else
|
|
||||||
int pin_ES7243_SDA = ES7243_SDAPIN;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ES7243_SDAPIN
|
|
||||||
int pin_ES7243_SCL = 23;
|
|
||||||
#else
|
|
||||||
int pin_ES7243_SCL = ES7243_SCLPIN;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Interface class
|
/* Interface class
|
||||||
AudioSource serves as base class for all microphone types
|
AudioSource serves as base class for all microphone types
|
||||||
@ -50,7 +27,7 @@
|
|||||||
which simplifies the caller code
|
which simplifies the caller code
|
||||||
*/
|
*/
|
||||||
class AudioSource {
|
class AudioSource {
|
||||||
public:
|
public:
|
||||||
/* All public methods are virtual, so they can be overridden
|
/* All public methods are virtual, so they can be overridden
|
||||||
Everything but the destructor is also removed, to make sure each mic
|
Everything but the destructor is also removed, to make sure each mic
|
||||||
Implementation provides its version of this function
|
Implementation provides its version of this function
|
||||||
@ -61,7 +38,7 @@ public:
|
|||||||
This function needs to take care of anything that needs to be done
|
This function needs to take care of anything that needs to be done
|
||||||
before samples can be obtained from the microphone.
|
before samples can be obtained from the microphone.
|
||||||
*/
|
*/
|
||||||
virtual void initialize() = 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, int8_t = I2S_PIN_NO_CHANGE) {};
|
||||||
|
|
||||||
/* Deinitialize
|
/* Deinitialize
|
||||||
Release all resources and deactivate any functionality that is used
|
Release all resources and deactivate any functionality that is used
|
||||||
@ -76,148 +53,146 @@ public:
|
|||||||
virtual void getSamples(double *buffer, uint16_t num_samples) = 0;
|
virtual void getSamples(double *buffer, uint16_t num_samples) = 0;
|
||||||
|
|
||||||
/* Get an up-to-date sample without DC offset */
|
/* Get an up-to-date sample without DC offset */
|
||||||
virtual int getSampleWithoutDCOffset() = 0;
|
virtual int getSampleWithoutDCOffset() { return _sampleNoDCOffset; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Private constructor, to make sure it is not callable except from derived classes
|
// Private constructor, to make sure it is not callable except from derived classes
|
||||||
AudioSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) : _sampleRate(sampleRate), _blockSize(blockSize), _sampleNoDCOffset(0), _dcOffset(0.0f), _shift(lshift), _mask(mask), _initialized(false) {};
|
AudioSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||||
|
_sampleRate(sampleRate),
|
||||||
|
_blockSize(blockSize),
|
||||||
|
_sampleNoDCOffset(0),
|
||||||
|
_dcOffset(0.0f),
|
||||||
|
_shift(lshift),
|
||||||
|
_mask(mask),
|
||||||
|
_initialized(false)
|
||||||
|
{};
|
||||||
|
|
||||||
int _sampleRate; /* Microphone sampling rate */
|
int _sampleRate; // Microphone sampling rate
|
||||||
int _blockSize; /* I2S block size */
|
int _blockSize; // I2S block size
|
||||||
volatile int _sampleNoDCOffset; /* Up-to-date sample without DCOffset */
|
volatile int _sampleNoDCOffset; // Up-to-date sample without DCOffset
|
||||||
float _dcOffset; /* Rolling average DC offset */
|
float _dcOffset; // Rolling average DC offset
|
||||||
int16_t _shift; /* Shift obtained samples to the right (positive) or left(negative) by this amount */
|
int16_t _shift; // Shift obtained samples to the right (positive) or left(negative) by this amount
|
||||||
uint32_t _mask; /* Bitmask for sample data after shifting. Bitmask 0X0FFF means that we need to convert 12bit ADC samples from unsigned to signed*/
|
uint32_t _mask; // Bitmask for sample data after shifting. Bitmask 0X0FFF means that we need to convert 12bit ADC samples from unsigned to signed
|
||||||
bool _initialized; /* Gets set to true if initialization is successful */
|
bool _initialized; // Gets set to true if initialization is successful
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Basic I2S microphone source
|
/* Basic I2S microphone source
|
||||||
All functions are marked virtual, so derived classes can replace them
|
All functions are marked virtual, so derived classes can replace them
|
||||||
*/
|
*/
|
||||||
class I2SSource : public AudioSource {
|
class I2SSource : public AudioSource {
|
||||||
public:
|
public:
|
||||||
I2SSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
I2SSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||||
AudioSource(sampleRate, blockSize, lshift, mask) {
|
AudioSource(sampleRate, blockSize, lshift, mask) {
|
||||||
_config = {
|
_config = {
|
||||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
|
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
|
||||||
.sample_rate = _sampleRate,
|
.sample_rate = _sampleRate,
|
||||||
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
|
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
|
||||||
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||||
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
|
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
|
||||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||||
.dma_buf_count = 8,
|
.dma_buf_count = 8,
|
||||||
.dma_buf_len = _blockSize
|
.dma_buf_len = _blockSize
|
||||||
};
|
};
|
||||||
|
}
|
||||||
_pinConfig = {
|
|
||||||
.bck_io_num = i2sckPin,
|
|
||||||
.ws_io_num = i2swsPin,
|
|
||||||
.data_out_num = I2S_PIN_NO_CHANGE,
|
|
||||||
.data_in_num = i2ssdPin
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
virtual void initialize() {
|
|
||||||
|
|
||||||
|
void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE) {
|
||||||
|
if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) {
|
||||||
if (!pinManager.allocatePin(i2swsPin, true, PinOwner::DigitalMic) ||
|
if (!pinManager.allocatePin(i2swsPin, true, PinOwner::DigitalMic) ||
|
||||||
!pinManager.allocatePin(i2ssdPin, true, PinOwner::DigitalMic)) {
|
!pinManager.allocatePin(i2ssdPin, true, PinOwner::DigitalMic)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// i2ssckPin needs special treatment, since it might be unused on PDM mics
|
// i2ssckPin needs special treatment, since it might be unused on PDM mics
|
||||||
if (i2sckPin != -1) {
|
if (i2sckPin != I2S_PIN_NO_CHANGE) {
|
||||||
if (!pinManager.allocatePin(i2sckPin, true, PinOwner::DigitalMic))
|
if (!pinManager.allocatePin(i2sckPin, true, PinOwner::DigitalMic)) return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
|
_pinConfig = {
|
||||||
if (err != ESP_OK) {
|
.bck_io_num = i2sckPin,
|
||||||
DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
|
.ws_io_num = i2swsPin,
|
||||||
return;
|
.data_out_num = I2S_PIN_NO_CHANGE,
|
||||||
}
|
.data_in_num = i2ssdPin
|
||||||
|
};
|
||||||
|
|
||||||
err = i2s_set_pin(I2S_NUM_0, &_pinConfig);
|
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
DEBUGSR_PRINTF("Failed to set i2s pin config: %d\n", err);
|
DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_initialized = true;
|
err = i2s_set_pin(I2S_NUM_0, &_pinConfig);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
DEBUGSR_PRINTF("Failed to set i2s pin config: %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void deinitialize() {
|
void deinitialize() {
|
||||||
_initialized = false;
|
_initialized = false;
|
||||||
esp_err_t err = i2s_driver_uninstall(I2S_NUM_0);
|
esp_err_t err = i2s_driver_uninstall(I2S_NUM_0);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
|
DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pinManager.deallocatePin(i2swsPin, PinOwner::DigitalMic);
|
if (_pinConfig.ws_io_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.ws_io_num, PinOwner::DigitalMic);
|
||||||
pinManager.deallocatePin(i2ssdPin, PinOwner::DigitalMic);
|
if (_pinConfig.data_in_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.data_in_num, PinOwner::DigitalMic);
|
||||||
// i2ssckPin needs special treatment, since it might be unused on PDM mics
|
if (_pinConfig.bck_io_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.bck_io_num, PinOwner::DigitalMic);
|
||||||
if (i2sckPin != -1) {
|
|
||||||
pinManager.deallocatePin(i2sckPin, PinOwner::DigitalMic);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void getSamples(double *buffer, uint16_t num_samples) {
|
void getSamples(double *buffer, uint16_t num_samples) {
|
||||||
if(_initialized) {
|
if (_initialized) {
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
size_t bytes_read = 0; /* Counter variable to check if we actually got enough data */
|
size_t bytes_read = 0; /* Counter variable to check if we actually got enough data */
|
||||||
I2S_datatype newSamples[num_samples]; /* Intermediary sample storage */
|
I2S_datatype newSamples[num_samples]; /* Intermediary sample storage */
|
||||||
|
|
||||||
// Reset dc offset
|
// Reset dc offset
|
||||||
_dcOffset = 0.0f;
|
_dcOffset = 0.0f;
|
||||||
|
|
||||||
err = i2s_read(I2S_NUM_0, (void *)newSamples, sizeof(newSamples), &bytes_read, portMAX_DELAY);
|
err = i2s_read(I2S_NUM_0, (void *)newSamples, sizeof(newSamples), &bytes_read, portMAX_DELAY);
|
||||||
if ((err != ESP_OK)){
|
if (err != ESP_OK) {
|
||||||
DEBUGSR_PRINTF("Failed to get samples: %d\n", err);
|
DEBUGSR_PRINTF("Failed to get samples: %d\n", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For correct operation, we need to read exactly sizeof(samples) bytes from i2s
|
// For correct operation, we need to read exactly sizeof(samples) bytes from i2s
|
||||||
if(bytes_read != sizeof(newSamples)) {
|
if (bytes_read != sizeof(newSamples)) {
|
||||||
DEBUGSR_PRINTF("Failed to get enough samples: wanted: %d read: %d\n", sizeof(newSamples), bytes_read);
|
DEBUGSR_PRINTF("Failed to get enough samples: wanted: %d read: %d\n", sizeof(newSamples), bytes_read);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store samples in sample buffer and update DC offset
|
// Store samples in sample buffer and update DC offset
|
||||||
for (int i = 0; i < num_samples; i++) {
|
for (int i = 0; i < num_samples; i++) {
|
||||||
// pre-shift samples down to 16bit
|
// pre-shift samples down to 16bit
|
||||||
#ifdef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
#ifdef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||||
if (_shift != 0)
|
if (_shift != 0)
|
||||||
newSamples[i] >>= 16;
|
newSamples[i] >>= 16;
|
||||||
#endif
|
#endif
|
||||||
double currSample = 0.0;
|
double currSample = 0.0;
|
||||||
if(_shift > 0)
|
if(_shift > 0)
|
||||||
currSample = (double) (newSamples[i] >> _shift);
|
currSample = (double) (newSamples[i] >> _shift);
|
||||||
else {
|
else {
|
||||||
if(_shift < 0)
|
if(_shift < 0)
|
||||||
currSample = (double) (newSamples[i] << (- _shift)); // need to "pump up" 12bit ADC to full 16bit as delivered by other digital mics
|
currSample = (double) (newSamples[i] << (- _shift)); // need to "pump up" 12bit ADC to full 16bit as delivered by other digital mics
|
||||||
else
|
else
|
||||||
#ifdef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
#ifdef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||||
currSample = (double) newSamples[i] / 65536.0; // _shift == 0 -> use the chance to keep lower 16bits
|
currSample = (double) newSamples[i] / 65536.0; // _shift == 0 -> use the chance to keep lower 16bits
|
||||||
#else
|
#else
|
||||||
currSample = (double) newSamples[i];
|
currSample = (double) newSamples[i];
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
buffer[i] = currSample;
|
buffer[i] = currSample;
|
||||||
_dcOffset = ((_dcOffset * 31) + currSample) / 32;
|
_dcOffset = ((_dcOffset * 31) + currSample) / 32;
|
||||||
}
|
|
||||||
|
|
||||||
// Update no-DC sample
|
|
||||||
_sampleNoDCOffset = buffer[num_samples - 1] - _dcOffset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update no-DC sample
|
||||||
|
_sampleNoDCOffset = buffer[num_samples - 1] - _dcOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int getSampleWithoutDCOffset() {
|
protected:
|
||||||
return _sampleNoDCOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
i2s_config_t _config;
|
i2s_config_t _config;
|
||||||
i2s_pin_config_t _pinConfig;
|
i2s_pin_config_t _pinConfig;
|
||||||
};
|
};
|
||||||
@ -227,97 +202,111 @@ protected:
|
|||||||
routing via the provided API, so we have to do it by hand
|
routing via the provided API, so we have to do it by hand
|
||||||
*/
|
*/
|
||||||
class I2SSourceWithMasterClock : public I2SSource {
|
class I2SSourceWithMasterClock : public I2SSource {
|
||||||
public:
|
public:
|
||||||
I2SSourceWithMasterClock(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
I2SSourceWithMasterClock(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||||
I2SSource(sampleRate, blockSize, lshift, mask) {
|
I2SSource(sampleRate, blockSize, lshift, mask) {
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual void initialize() {
|
virtual void initialize(int8_t mclkPin, int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE) {
|
||||||
// Reserve the master clock pin
|
// Reserve the master clock pin
|
||||||
if(!pinManager.allocatePin(mclkPin, true, PinOwner::DigitalMic)) {
|
if(!pinManager.allocatePin(mclkPin, true, PinOwner::DigitalMic)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_routeMclk();
|
_mclkPin = mclkPin;
|
||||||
I2SSource::initialize();
|
_routeMclk(mclkPin);
|
||||||
|
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void deinitialize() {
|
virtual void deinitialize() {
|
||||||
// Release the master clock pin
|
// Release the master clock pin
|
||||||
pinManager.deallocatePin(mclkPin, PinOwner::DigitalMic);
|
pinManager.deallocatePin(_mclkPin, PinOwner::DigitalMic);
|
||||||
I2SSource::deinitialize();
|
I2SSource::deinitialize();
|
||||||
}
|
}
|
||||||
protected:
|
|
||||||
void _routeMclk() {
|
protected:
|
||||||
/* Enable the mclk routing depending on the selected mclk pin
|
void _routeMclk(int8_t mclkPin) {
|
||||||
Only I2S_NUM_0 is supported
|
/* Enable the mclk routing depending on the selected mclk pin
|
||||||
*/
|
Only I2S_NUM_0 is supported
|
||||||
if (mclkPin == GPIO_NUM_0) {
|
*/
|
||||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
|
if (mclkPin == GPIO_NUM_0) {
|
||||||
WRITE_PERI_REG(PIN_CTRL,0xFFF0);
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
|
||||||
} else if (mclkPin == GPIO_NUM_1) {
|
WRITE_PERI_REG(PIN_CTRL,0xFFF0);
|
||||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3);
|
} else if (mclkPin == GPIO_NUM_1) {
|
||||||
WRITE_PERI_REG(PIN_CTRL, 0xF0F0);
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3);
|
||||||
} else {
|
WRITE_PERI_REG(PIN_CTRL, 0xF0F0);
|
||||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2);
|
} else {
|
||||||
WRITE_PERI_REG(PIN_CTRL, 0xFF00);
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2);
|
||||||
}
|
WRITE_PERI_REG(PIN_CTRL, 0xFF00);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int8_t _mclkPin;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ES7243 Microphone
|
/* ES7243 Microphone
|
||||||
This is an I2S microphone that requires ininitialization over
|
This is an I2S microphone that requires ininitialization over
|
||||||
I2C before I2S data can be received
|
I2C before I2S data can be received
|
||||||
*/
|
*/
|
||||||
class ES7243 : public I2SSourceWithMasterClock {
|
class ES7243 : public I2SSource {
|
||||||
|
private:
|
||||||
private:
|
|
||||||
// I2C initialization functions for ES7243
|
// I2C initialization functions for ES7243
|
||||||
void _es7243I2cBegin() {
|
void _es7243I2cBegin() {
|
||||||
Wire.begin(pin_ES7243_SDA, pin_ES7243_SCL, 100000U);
|
Wire.begin(pin_ES7243_SDA, pin_ES7243_SCL, 100000U);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _es7243I2cWrite(uint8_t reg, uint8_t val) {
|
void _es7243I2cWrite(uint8_t reg, uint8_t val) {
|
||||||
Wire.beginTransmission(addr_ES7243);
|
#ifndef ES7243_ADDR
|
||||||
Wire.write((uint8_t)reg);
|
Wire.beginTransmission(0x13);
|
||||||
Wire.write((uint8_t)val);
|
#else
|
||||||
Wire.endTransmission();
|
Wire.beginTransmission(ES7243_ADDR);
|
||||||
|
#endif
|
||||||
|
Wire.write((uint8_t)reg);
|
||||||
|
Wire.write((uint8_t)val);
|
||||||
|
Wire.endTransmission();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _es7243InitAdc() {
|
void _es7243InitAdc() {
|
||||||
_es7243I2cBegin();
|
_es7243I2cBegin();
|
||||||
_es7243I2cWrite(0x00, 0x01);
|
_es7243I2cWrite(0x00, 0x01);
|
||||||
_es7243I2cWrite(0x06, 0x00);
|
_es7243I2cWrite(0x06, 0x00);
|
||||||
_es7243I2cWrite(0x05, 0x1B);
|
_es7243I2cWrite(0x05, 0x1B);
|
||||||
_es7243I2cWrite(0x01, 0x00); // 0x00 for 24 bit to match INMP441 - not sure if this needs adjustment to get 16bit samples from I2S
|
_es7243I2cWrite(0x01, 0x00); // 0x00 for 24 bit to match INMP441 - not sure if this needs adjustment to get 16bit samples from I2S
|
||||||
_es7243I2cWrite(0x08, 0x43);
|
_es7243I2cWrite(0x08, 0x43);
|
||||||
_es7243I2cWrite(0x05, 0x13);
|
_es7243I2cWrite(0x05, 0x13);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ES7243(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
ES7243(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||||
I2SSourceWithMasterClock(sampleRate, blockSize, lshift, mask) {
|
I2SSource(sampleRate, blockSize, lshift, mask) {
|
||||||
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
|
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
|
||||||
};
|
};
|
||||||
void initialize() {
|
|
||||||
// Reserve SDA and SCL pins of the I2C interface
|
|
||||||
if (!pinManager.allocatePin(pin_ES7243_SDA, true, PinOwner::DigitalMic) ||
|
|
||||||
!pinManager.allocatePin(pin_ES7243_SCL, true, PinOwner::DigitalMic)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// First route mclk, then configure ADC over I2C, then configure I2S
|
void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE) {
|
||||||
_es7243InitAdc();
|
// Reserve SDA and SCL pins of the I2C interface
|
||||||
I2SSourceWithMasterClock::initialize();
|
if (!pinManager.allocatePin(sdaPin, true, PinOwner::DigitalMic) ||
|
||||||
|
!pinManager.allocatePin(sclPin, true, PinOwner::DigitalMic)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_ES7243_SDA = sdaPin;
|
||||||
|
pin_ES7243_SCL = sclPin;
|
||||||
|
|
||||||
|
// First route mclk, then configure ADC over I2C, then configure I2S
|
||||||
|
_es7243InitAdc();
|
||||||
|
I2SSource::initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void deinitialize() {
|
void deinitialize() {
|
||||||
// Release SDA and SCL pins of the I2C interface
|
// Release SDA and SCL pins of the I2C interface
|
||||||
pinManager.deallocatePin(pin_ES7243_SDA, PinOwner::DigitalMic);
|
pinManager.deallocatePin(pin_ES7243_SDA, PinOwner::DigitalMic);
|
||||||
pinManager.deallocatePin(pin_ES7243_SCL, PinOwner::DigitalMic);
|
pinManager.deallocatePin(pin_ES7243_SCL, PinOwner::DigitalMic);
|
||||||
I2SSourceWithMasterClock::deinitialize();
|
I2SSource::deinitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int8_t pin_ES7243_SDA;
|
||||||
|
int8_t pin_ES7243_SCL;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ADC over I2S Microphone
|
/* ADC over I2S Microphone
|
||||||
@ -326,110 +315,113 @@ public:
|
|||||||
without the need of manual timing of the samples
|
without the need of manual timing of the samples
|
||||||
*/
|
*/
|
||||||
class I2SAdcSource : public I2SSource {
|
class I2SAdcSource : public I2SSource {
|
||||||
public:
|
public:
|
||||||
I2SAdcSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
I2SAdcSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||||
I2SSource(sampleRate, blockSize, lshift, mask){
|
I2SSource(sampleRate, blockSize, lshift, mask) {
|
||||||
_config = {
|
_config = {
|
||||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
|
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
|
||||||
.sample_rate = _sampleRate,
|
.sample_rate = _sampleRate,
|
||||||
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
|
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
|
||||||
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||||
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
|
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
|
||||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
|
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
|
||||||
.dma_buf_count = 8,
|
.dma_buf_count = 8,
|
||||||
.dma_buf_len = _blockSize
|
.dma_buf_len = _blockSize
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize() {
|
void initialize(int8_t audioPin) {
|
||||||
|
if(!pinManager.allocatePin(audioPin, false, PinOwner::AnalogMic)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_audioPin = audioPin;
|
||||||
|
|
||||||
if(!pinManager.allocatePin(audioPin, false, PinOwner::AnalogMic)) {
|
// Determine Analog channel. Only Channels on ADC1 are supported
|
||||||
return;
|
int8_t channel = digitalPinToAnalogChannel(_audioPin);
|
||||||
}
|
if (channel > 9) {
|
||||||
// Determine Analog channel. Only Channels on ADC1 are supported
|
DEBUGSR_PRINTF("Incompatible GPIO used for audio in: %d\n", _audioPin);
|
||||||
int8_t channel = digitalPinToAnalogChannel(audioPin);
|
return;
|
||||||
if (channel > 9) {
|
} else {
|
||||||
DEBUGSR_PRINTF("Incompatible GPIO used for audio in: %d\n", audioPin);
|
adc_gpio_init(ADC_UNIT_1, adc_channel_t(channel));
|
||||||
return;
|
}
|
||||||
} else {
|
|
||||||
adc_gpio_init(ADC_UNIT_1, adc_channel_t(channel));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Install Driver
|
// Install Driver
|
||||||
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
|
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
|
DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable I2S mode of ADC
|
// Enable I2S mode of ADC
|
||||||
err = i2s_set_adc_mode(ADC_UNIT_1, adc1_channel_t(channel));
|
err = i2s_set_adc_mode(ADC_UNIT_1, adc1_channel_t(channel));
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
DEBUGSR_PRINTF("Failed to set i2s adc mode: %d\n", err);
|
DEBUGSR_PRINTF("Failed to set i2s adc mode: %d\n", err);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
// according to docs from espressif, the ADC needs to be started explicitly
|
// according to docs from espressif, the ADC needs to be started explicitly
|
||||||
// fingers crossed
|
// fingers crossed
|
||||||
err = i2s_adc_enable(I2S_NUM_0);
|
err = i2s_adc_enable(I2S_NUM_0);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err);
|
DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err);
|
||||||
//return;
|
//return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void getSamples(double *buffer, uint16_t num_samples) {
|
void getSamples(double *buffer, uint16_t num_samples) {
|
||||||
|
/* Enable ADC. This has to be enabled and disabled directly before and
|
||||||
/* Enable ADC. This has to be enabled and disabled directly before and
|
* after sampling, otherwise Wifi dies
|
||||||
after sampling, otherwise Wifi dies
|
*/
|
||||||
*/
|
if (_initialized) {
|
||||||
if (_initialized) {
|
|
||||||
#if !defined(ARDUINO_ARCH_ESP32)
|
#if !defined(ARDUINO_ARCH_ESP32)
|
||||||
// old code - works for me without enable/disable, at least on ESP32.
|
// old code - works for me without enable/disable, at least on ESP32.
|
||||||
esp_err_t err = i2s_adc_enable(I2S_NUM_0);
|
esp_err_t err = i2s_adc_enable(I2S_NUM_0);
|
||||||
//esp_err_t err = i2s_start(I2S_NUM_0);
|
//esp_err_t err = i2s_start(I2S_NUM_0);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err);
|
DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
#endif
|
|
||||||
I2SSource::getSamples(buffer, num_samples);
|
|
||||||
|
|
||||||
#if !defined(ARDUINO_ARCH_ESP32)
|
|
||||||
// old code - works for me without enable/disable, at least on ESP32.
|
|
||||||
err = i2s_adc_disable(I2S_NUM_0);
|
|
||||||
//err = i2s_stop(I2S_NUM_0);
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
I2SSource::getSamples(buffer, num_samples);
|
||||||
|
|
||||||
|
#if !defined(ARDUINO_ARCH_ESP32)
|
||||||
|
// old code - works for me without enable/disable, at least on ESP32.
|
||||||
|
err = i2s_adc_disable(I2S_NUM_0);
|
||||||
|
//err = i2s_stop(I2S_NUM_0);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void deinitialize() {
|
void deinitialize() {
|
||||||
pinManager.deallocatePin(audioPin, PinOwner::AnalogMic);
|
pinManager.deallocatePin(_audioPin, PinOwner::AnalogMic);
|
||||||
_initialized = false;
|
_initialized = false;
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
// according to docs from espressif, the ADC needs to be stopped explicitly
|
// according to docs from espressif, the ADC needs to be stopped explicitly
|
||||||
// fingers crossed
|
// fingers crossed
|
||||||
err = i2s_adc_disable(I2S_NUM_0);
|
err = i2s_adc_disable(I2S_NUM_0);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err);
|
DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err);
|
||||||
//return;
|
//return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
err = i2s_driver_uninstall(I2S_NUM_0);
|
err = i2s_driver_uninstall(I2S_NUM_0);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
|
DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int8_t _audioPin;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* SPH0645 Microphone
|
/* SPH0645 Microphone
|
||||||
@ -437,15 +429,14 @@ public:
|
|||||||
special consideration.
|
special consideration.
|
||||||
*/
|
*/
|
||||||
class SPH0654 : public I2SSource {
|
class SPH0654 : public I2SSource {
|
||||||
|
public:
|
||||||
public:
|
|
||||||
SPH0654(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
SPH0654(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||||
I2SSource(sampleRate, blockSize, lshift, mask){}
|
I2SSource(sampleRate, blockSize, lshift, mask){}
|
||||||
|
|
||||||
void initialize() {
|
void initialize(uint8_t i2swsPin, uint8_t i2ssdPin, uint8_t i2sckPin) {
|
||||||
I2SSource::initialize();
|
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||||
REG_SET_BIT(I2S_TIMING_REG(I2S_NUM_0), BIT(9));
|
REG_SET_BIT(I2S_TIMING_REG(I2S_NUM_0), BIT(9));
|
||||||
REG_SET_BIT(I2S_CONF_REG(I2S_NUM_0), I2S_RX_MSB_SHIFT);
|
REG_SET_BIT(I2S_CONF_REG(I2S_NUM_0), I2S_RX_MSB_SHIFT);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -454,20 +445,14 @@ public:
|
|||||||
data line, to make it simpler to debug, use the WS pin as CLK and SD
|
data line, to make it simpler to debug, use the WS pin as CLK and SD
|
||||||
pin as DATA
|
pin as DATA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class I2SPdmSource : public I2SSource {
|
class I2SPdmSource : public I2SSource {
|
||||||
|
public:
|
||||||
public:
|
|
||||||
I2SPdmSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
I2SPdmSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||||
I2SSource(sampleRate, blockSize, lshift, mask) {
|
I2SSource(sampleRate, blockSize, lshift, mask) {
|
||||||
|
_config.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); // Change mode to pdm
|
||||||
|
}
|
||||||
|
|
||||||
_config.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); // Change mode to pdm
|
void initialize(uint8_t i2swsPin, uint8_t i2ssdPin) {
|
||||||
|
I2SSource::initialize(i2swsPin, i2ssdPin, I2S_PIN_NO_CHANGE);
|
||||||
_pinConfig = {
|
|
||||||
.bck_io_num = I2S_PIN_NO_CHANGE, // bck is unused in PDM mics
|
|
||||||
.ws_io_num = i2swsPin, // clk pin for PDM mic
|
|
||||||
.data_out_num = I2S_PIN_NO_CHANGE,
|
|
||||||
.data_in_num = i2ssdPin
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -5569,7 +5569,7 @@ uint16_t WS2812FX::mode_2DAkemi(void) {
|
|||||||
|
|
||||||
um_data_t *um_data;
|
um_data_t *um_data;
|
||||||
if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||||
fftResult = um_data->ub8_data[1];
|
fftResult = (uint8_t*)um_data->u_data[1];
|
||||||
base = fftResult[0]/255.0f;
|
base = fftResult[0]/255.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -752,6 +752,8 @@ class WS2812FX {
|
|||||||
getLengthPhysical(void),
|
getLengthPhysical(void),
|
||||||
getFps();
|
getFps();
|
||||||
|
|
||||||
|
inline uint16_t getMinShowDelay() { return MIN_SHOW_DELAY; }
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
now,
|
now,
|
||||||
timebase,
|
timebase,
|
||||||
|
@ -216,54 +216,38 @@ int getSignalQuality(int rssi);
|
|||||||
void WiFiEvent(WiFiEvent_t event);
|
void WiFiEvent(WiFiEvent_t event);
|
||||||
|
|
||||||
//um_manager.cpp
|
//um_manager.cpp
|
||||||
|
typedef enum UM_Data_Types {
|
||||||
|
UMT_BYTE = 0,
|
||||||
|
UMT_UINT16,
|
||||||
|
UMT_INT16,
|
||||||
|
UMT_UINT32,
|
||||||
|
UMT_INT32,
|
||||||
|
UMT_FLOAT,
|
||||||
|
UMT_DOUBLE,
|
||||||
|
UMT_BYTE_ARR,
|
||||||
|
UMT_UINT16_ARR,
|
||||||
|
UMT_INT16_ARR,
|
||||||
|
UMT_UINT32_ARR,
|
||||||
|
UMT_INT32_ARR,
|
||||||
|
UMT_FLOAT_ARR,
|
||||||
|
UMT_DOUBLE_ARR
|
||||||
|
} um_types_t;
|
||||||
typedef struct UM_Exchange_Data {
|
typedef struct UM_Exchange_Data {
|
||||||
// should just use: size_t arr_size, void **arr_ptr, byte *ptr_type
|
// should just use: size_t arr_size, void **arr_ptr, byte *ptr_type
|
||||||
size_t ub8_size; // size of ub8_data
|
size_t u_size; // size of u_data array
|
||||||
uint8_t **ub8_data; // array of pointers to bytes (pointer can point to an array of bytes, depends on the usermod)
|
um_types_t *u_type; // array of data types
|
||||||
size_t uw16_size; // size of uw16_data
|
void **u_data; // array of pointers to data
|
||||||
uint16_t **uw16_data; // array of pointers to unsigned words
|
|
||||||
size_t uw32_size; // size of uw32_data
|
|
||||||
uint32_t **uw32_data; // array of pointers to unsigned long words
|
|
||||||
size_t ui32_size; // size of uw32_data
|
|
||||||
int32_t **ui32_data; // array of pointers to long words
|
|
||||||
size_t uf4_size; // size of ubf4_data
|
|
||||||
float **uf4_data; // array of pointers to floats
|
|
||||||
size_t uf8_size; // size of ubf4_data
|
|
||||||
double **uf8_data; // array of pointers to doubles
|
|
||||||
/*
|
|
||||||
uint8_t ub1, ub2, ub3, ub4; // 4 byte values
|
|
||||||
uint16_t ui1, ui2, *aui1, *aui2, *aui3; // 2 word values and 3 pointers to word arrays/values
|
|
||||||
int16_t ui3, ui4, *aui4, *aui5, *aui6; // 2 signed word values and 3 pointers to signed word arrays/values
|
|
||||||
uint32_t ul1, ul2; // 2 long word values
|
|
||||||
float uf1, uf2, uf3, *auf1, *auf2; // 3 float values and 2 pointers to float arrays/values
|
|
||||||
*/
|
|
||||||
UM_Exchange_Data() {
|
UM_Exchange_Data() {
|
||||||
ub8_size = 0;
|
u_size = 0;
|
||||||
uw16_size = 0;
|
u_type = nullptr;
|
||||||
uw32_size = 0;
|
u_data = nullptr;
|
||||||
ui32_size = 0;
|
|
||||||
uf4_size = 0;
|
|
||||||
uf8_size = 0;
|
|
||||||
/*
|
|
||||||
ub1 = ub2 = ub3 = ub4 = 0;
|
|
||||||
ui1 = ui2 = ui3 = ui4 = 0;
|
|
||||||
ul1 = ul2 = 0;
|
|
||||||
uf1 = uf2 = uf3 = 0.0f;
|
|
||||||
aui1 = aui2 = aui3 = nullptr;
|
|
||||||
aui4 = aui5 = aui6 = nullptr;
|
|
||||||
auf1 = auf2 = nullptr;
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
~UM_Exchange_Data() {
|
~UM_Exchange_Data() {
|
||||||
if (ub8_size && ub8_data ) delete[] ub8_data;
|
if (u_type) delete[] u_type;
|
||||||
if (uw16_size && uw16_data) delete[] uw16_data;
|
if (u_data) delete[] u_data;
|
||||||
if (uw32_size && uw32_data) delete[] uw32_data;
|
|
||||||
if (ui32_size && ui32_data) delete[] ui32_data;
|
|
||||||
if (uf4_size && uf4_data ) delete[] uf4_data;
|
|
||||||
if (uf8_size && uf8_data ) delete[] uf8_data;
|
|
||||||
}
|
}
|
||||||
} um_data_t;
|
} um_data_t;
|
||||||
const unsigned int um_data_size = sizeof(um_data_t); // about 64 bytes
|
const unsigned int um_data_size = sizeof(um_data_t); // 12 bytes
|
||||||
|
|
||||||
class Usermod {
|
class Usermod {
|
||||||
protected:
|
protected:
|
||||||
@ -284,6 +268,7 @@ class Usermod {
|
|||||||
virtual bool readFromConfig(JsonObject& obj) { return true; } // Note as of 2021-06 readFromConfig() now needs to return a bool, see usermod_v2_example.h
|
virtual bool readFromConfig(JsonObject& obj) { return true; } // Note as of 2021-06 readFromConfig() now needs to return a bool, see usermod_v2_example.h
|
||||||
virtual void onMqttConnect(bool sessionPresent) {}
|
virtual void onMqttConnect(bool sessionPresent) {}
|
||||||
virtual bool onMqttMessage(char* topic, char* payload) { return false; }
|
virtual bool onMqttMessage(char* topic, char* payload) { return false; }
|
||||||
|
virtual void onUpdateBegin(bool) {}
|
||||||
virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;}
|
virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -306,6 +291,7 @@ class UsermodManager {
|
|||||||
bool readFromConfig(JsonObject& obj);
|
bool readFromConfig(JsonObject& obj);
|
||||||
void onMqttConnect(bool sessionPresent);
|
void onMqttConnect(bool sessionPresent);
|
||||||
bool onMqttMessage(char* topic, char* payload);
|
bool onMqttMessage(char* topic, char* payload);
|
||||||
|
void onUpdateBegin(bool);
|
||||||
bool add(Usermod* um);
|
bool add(Usermod* um);
|
||||||
Usermod* lookup(uint16_t mod_id);
|
Usermod* lookup(uint16_t mod_id);
|
||||||
byte getModCount();
|
byte getModCount();
|
||||||
|
@ -34,6 +34,8 @@ enum struct PinOwner : uint8_t {
|
|||||||
DebugOut = 0x89, // 'Dbg' == debug output always IO1
|
DebugOut = 0x89, // 'Dbg' == debug output always IO1
|
||||||
DMX = 0x8A, // 'DMX' == hard-coded to IO2
|
DMX = 0x8A, // 'DMX' == hard-coded to IO2
|
||||||
HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32)
|
HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32)
|
||||||
|
AnalogMic = 0x8C, // WLEDSR
|
||||||
|
DigitalMic = 0x8D, // WLEDSR
|
||||||
// Use UserMod IDs from const.h here
|
// Use UserMod IDs from const.h here
|
||||||
UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01
|
UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01
|
||||||
UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h"
|
UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h"
|
||||||
@ -53,7 +55,8 @@ enum struct PinOwner : uint8_t {
|
|||||||
// #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h
|
// #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h
|
||||||
// #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager
|
// #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager
|
||||||
UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h"
|
UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h"
|
||||||
UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA // 0x17 // Usermod "quinled-an-penta.h"
|
UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h"
|
||||||
|
UM_Audioreactive = USERMOD_ID_AUDIOREACTIVE // 0x1E // Usermod: "audio_reactive.h"
|
||||||
};
|
};
|
||||||
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
|
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ bool UsermodManager::onMqttMessage(char* topic, char* payload) {
|
|||||||
for (byte i = 0; i < numMods; i++) if (ums[i]->onMqttMessage(topic, payload)) return true;
|
for (byte i = 0; i < numMods; i++) if (ums[i]->onMqttMessage(topic, payload)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
void UsermodManager::onUpdateBegin(bool init) { for (byte i = 0; i < numMods; i++) ums[i]->onUpdateBegin(init); } // notify usermods that update is to begin
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enables usermods to lookup another Usermod.
|
* Enables usermods to lookup another Usermod.
|
||||||
|
@ -284,6 +284,7 @@ void initServer()
|
|||||||
if(!index){
|
if(!index){
|
||||||
DEBUG_PRINTLN(F("OTA Update Start"));
|
DEBUG_PRINTLN(F("OTA Update Start"));
|
||||||
WLED::instance().disableWatchdog();
|
WLED::instance().disableWatchdog();
|
||||||
|
usermods.onUpdateBegin(true); // notify usermods that update is about to begin (some may require task de-init)
|
||||||
lastEditTime = millis(); // make sure PIN does not lock during update
|
lastEditTime = millis(); // make sure PIN does not lock during update
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
Update.runAsync(true);
|
Update.runAsync(true);
|
||||||
@ -297,6 +298,7 @@ void initServer()
|
|||||||
} else {
|
} else {
|
||||||
DEBUG_PRINTLN(F("Update Failed"));
|
DEBUG_PRINTLN(F("Update Failed"));
|
||||||
WLED::instance().enableWatchdog();
|
WLED::instance().enableWatchdog();
|
||||||
|
usermods.onUpdateBegin(false); // notify usermods that update has failed (some may require task init)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user