diff --git a/CMakeLists.txt b/CMakeLists.txt
index a0cca714048..89acdb6ccb5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -82,6 +82,9 @@ set(LIBRARY_SRCS
   libraries/BluetoothSerial/src/BTScanResultsSet.cpp
   libraries/DNSServer/src/DNSServer.cpp
   libraries/EEPROM/src/EEPROM.cpp
+  libraries/ESP_I2S/src/ESP_I2S.cpp
+  libraries/ESP_SR/src/ESP_SR.cpp
+  libraries/ESP_SR/src/esp32-hal-sr.c
   libraries/ESPmDNS/src/ESPmDNS.cpp
   libraries/Ethernet/src/ETH.cpp
   libraries/FFat/src/FFat.cpp
@@ -91,7 +94,6 @@ set(LIBRARY_SRCS
   libraries/HTTPUpdate/src/HTTPUpdate.cpp
   libraries/LittleFS/src/LittleFS.cpp
   libraries/Insights/src/Insights.cpp
-  libraries/I2S/src/I2S.cpp
   libraries/NetBIOS/src/NetBIOS.cpp
   libraries/Preferences/src/Preferences.cpp
   libraries/RainMaker/src/RMaker.cpp
diff --git a/libraries/ESP_I2S/keywords.txt b/libraries/ESP_I2S/keywords.txt
new file mode 100644
index 00000000000..f30b7ad1d9a
--- /dev/null
+++ b/libraries/ESP_I2S/keywords.txt
@@ -0,0 +1,21 @@
+#######################################
+# Syntax Coloring Map For ESP_I2S
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+ESP_I2S	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+onEvent	KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
+SR_EVENT_WAKEWORD	LITERAL1
diff --git a/libraries/ESP_I2S/library.properties b/libraries/ESP_I2S/library.properties
new file mode 100755
index 00000000000..d4e3e409071
--- /dev/null
+++ b/libraries/ESP_I2S/library.properties
@@ -0,0 +1,9 @@
+name=ESP_I2S
+version=1.0.0
+author=me-no-dev
+maintainer=me-no-dev
+sentence=Library for ESP I2S communication
+paragraph=Supports ESP32 Arduino platforms.
+category=Sound
+url=https://github.com/espressif/arduino-esp32/
+architectures=esp32
\ No newline at end of file
diff --git a/libraries/ESP_I2S/src/ESP_I2S.cpp b/libraries/ESP_I2S/src/ESP_I2S.cpp
new file mode 100644
index 00000000000..852f0cd6caa
--- /dev/null
+++ b/libraries/ESP_I2S/src/ESP_I2S.cpp
@@ -0,0 +1,446 @@
+#include "ESP_I2S.h"
+#include "wav_header.h"
+#include "mp3dec.h"
+
+#define I2S_READ_CHUNK_SIZE 1920
+
+#define I2S_STD_DEFAULT_CFG() { \
+    .id = I2S_NUM_AUTO,         \
+    .role = I2S_ROLE_MASTER,    \
+    .dma_desc_num = 6,          \
+    .dma_frame_num = 240,       \
+    .auto_clear = true,         \
+}
+
+#define I2S_STD_CHAN_CFG(_sample_rate, _data_bit_width, _slot_mode)                  \
+    {                                                                                \
+        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate),                         \
+        .slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(_data_bit_width, _slot_mode), \
+        .gpio_cfg = {                                                                \
+            .mclk = (gpio_num_t)_mclk,                                               \
+            .bclk = (gpio_num_t)_bclk,                                               \
+            .ws   = (gpio_num_t)_ws,                                                 \
+            .dout = (gpio_num_t)_dout,                                               \
+            .din  = (gpio_num_t)_din,                                                \
+            .invert_flags = {                                                        \
+                .mclk_inv = _mclk_inv,                                               \
+                .bclk_inv = _bclk_inv,                                               \
+                .ws_inv = _ws_inv,                                                   \
+            },                                                                       \
+        },                                                                           \
+    }
+
+#define I2S_ERROR_CHECK_RETURN(x,r) do { last_error = (x); if(unlikely(last_error != ESP_OK)){ log_e("ERROR: %s", esp_err_to_name(last_error)); return (r); } } while(0)
+#define I2S_ERROR_CHECK_RETURN_FALSE(x) I2S_ERROR_CHECK_RETURN(x,false)
+
+// Default read, no resmpling and temp buffer necessary
+static esp_err_t i2s_channel_read_default(i2s_chan_handle_t handle, char * tmp_buf, void * dst, size_t len, size_t *bytes_read, uint32_t timeout_ms){
+    return i2s_channel_read(handle, (char *)dst, len, bytes_read, timeout_ms);
+}
+
+// Resample the 32bit SPH0645 microphone data into 16bit. SPH0645 is actually 18 bit, but this trick helps save some space
+static esp_err_t i2s_channel_read_32_to_16(i2s_chan_handle_t handle, char * read_buff, void * dst, size_t len, size_t *bytes_read, uint32_t timeout_ms){
+  size_t out_len = 0;
+  size_t read_buff_len = len * 2;
+  if(read_buff == NULL){
+    log_e("Temp buffer is NULL!");
+    return ESP_FAIL;
+  }
+  esp_err_t err = i2s_channel_read(handle, read_buff, read_buff_len, &out_len, timeout_ms);
+  if (err != ESP_OK){
+    *bytes_read = 0;
+    return err;
+  }
+  out_len /= 4;
+  uint16_t * ds = (uint16_t*)dst;
+  uint32_t * src = (uint32_t*)read_buff;
+  for (size_t i = 0; i < out_len; i++){
+    ds[i] = src[i] >> 16;
+  }
+  *bytes_read = out_len * 2;
+  return ESP_OK;
+}
+
+// Resample the 16bit stereo microphone data into 16bit mono.
+static esp_err_t i2s_channel_read_16_stereo_to_mono(i2s_chan_handle_t handle, char * read_buff, void * dst, size_t len, size_t *bytes_read, uint32_t timeout_ms){
+  size_t out_len = 0;
+  size_t read_buff_len = len * 2;
+  if(read_buff == NULL){
+    log_e("Temp buffer is NULL!");
+    return ESP_FAIL;
+  }
+  esp_err_t err = i2s_channel_read(handle, read_buff, read_buff_len, &out_len, timeout_ms);
+  if (err != ESP_OK){
+    *bytes_read = 0;
+    return err;
+  }
+  out_len /= 2;
+  uint16_t * ds = (uint16_t*)dst;
+  uint16_t * src = (uint16_t*)read_buff;
+  for (size_t i = 0; i < out_len; i+=2){
+    *ds++ = src[i];
+  }
+  *bytes_read = out_len;
+  return ESP_OK;
+}
+
+I2SClass::I2SClass(){
+    last_error = ESP_OK;
+
+    tx_chan = NULL;
+    tx_sample_rate = 0;
+    tx_data_bit_width = I2S_DATA_BIT_WIDTH_16BIT;
+    tx_slot_mode = I2S_SLOT_MODE_STEREO;
+
+    rx_fn = i2s_channel_read_default;
+    rx_transform = I2S_RX_TRANSFORM_NONE;
+    rx_transform_buf = NULL;
+    rx_transform_buf_len = 0;
+
+    rx_chan = NULL;
+    rx_sample_rate = 0;
+    rx_data_bit_width = I2S_DATA_BIT_WIDTH_16BIT;
+    rx_slot_mode = I2S_SLOT_MODE_STEREO;
+
+    _mclk = -1;
+    _bclk = -1;
+    _ws = -1;
+    _dout = -1;
+    _din = -1;
+    _mclk_inv = false;
+    _bclk_inv = false;
+    _ws_inv = false;
+}
+
+I2SClass::~I2SClass(){
+  end();
+}
+
+void I2SClass::setPins(int8_t bclk, int8_t ws, int8_t dout, int8_t din, int8_t mclk){
+    _mclk = mclk;
+    _bclk = bclk;
+    _ws = ws;
+    _dout = dout;
+    _din = din;
+}
+
+void I2SClass::setInverted(bool bclk, bool ws, bool mclk){
+    _mclk_inv = mclk;
+    _bclk_inv = bclk;
+    _ws_inv = ws;
+}
+
+bool I2SClass::begin(uint32_t rate, i2s_data_bit_width_t bits_cfg, i2s_slot_mode_t ch){
+    /* Setup I2S peripheral */
+    i2s_chan_config_t chan_cfg = I2S_STD_DEFAULT_CFG();
+    chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer
+
+    if(_dout >= 0 && _din >= 0){
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan));
+    } else if(_dout >= 0){
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
+    } else if(_din >= 0){
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_new_channel(&chan_cfg, NULL, &rx_chan));
+    }
+
+    i2s_std_config_t i2s_config = I2S_STD_CHAN_CFG(rate, bits_cfg, ch);
+    /* Setup I2S channels */
+    if (tx_chan != NULL) {
+        tx_sample_rate = rate;
+        tx_data_bit_width = bits_cfg;
+        tx_slot_mode = ch;
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_init_std_mode(tx_chan, &i2s_config));
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_enable(tx_chan));
+    }
+    if (rx_chan != NULL) {
+        rx_sample_rate = rate;
+        rx_data_bit_width = bits_cfg;
+        rx_slot_mode = ch;
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_init_std_mode(rx_chan, &i2s_config));
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_enable(rx_chan));
+    }
+    return true;
+}
+
+bool I2SClass::end(){
+    if (tx_chan != NULL) {
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_disable(tx_chan));
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_del_channel(tx_chan));
+        tx_chan = NULL;
+    }
+    if (rx_chan != NULL) {
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_disable(rx_chan));
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_del_channel(rx_chan));
+        rx_chan = NULL;
+    }
+    if(rx_transform_buf != NULL){
+        free(rx_transform_buf);
+        rx_transform_buf = NULL;
+        rx_transform_buf_len = 0;
+    }
+    return true;
+}
+
+bool I2SClass::configureTX(uint32_t rate, i2s_data_bit_width_t bits_cfg, i2s_slot_mode_t ch){
+    /* Setup I2S channels */
+    if (tx_chan != NULL) {
+        if(tx_sample_rate == rate && tx_data_bit_width == bits_cfg && tx_slot_mode == ch){
+            return true;
+        }
+        i2s_std_config_t i2s_config = I2S_STD_CHAN_CFG(rate, bits_cfg, ch);
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_disable(tx_chan));
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_reconfig_std_clock(tx_chan, &i2s_config.clk_cfg));
+        tx_sample_rate = rate;
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_reconfig_std_slot(tx_chan, &i2s_config.slot_cfg));
+        tx_data_bit_width = bits_cfg;
+        tx_slot_mode = ch;
+        I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_enable(tx_chan));
+        return true;
+    }
+    return false;
+}
+
+bool I2SClass::configureRX(uint32_t rate, i2s_data_bit_width_t bits_cfg, i2s_slot_mode_t ch, i2s_rx_transform_t transform){
+    /* Setup I2S channels */
+    if (rx_chan != NULL) {
+        if(rx_sample_rate != rate || rx_data_bit_width != bits_cfg || rx_slot_mode != ch){
+            i2s_std_config_t i2s_config = I2S_STD_CHAN_CFG(rate, bits_cfg, ch);
+            I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_disable(rx_chan));
+            I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_reconfig_std_clock(rx_chan, &i2s_config.clk_cfg));
+            rx_sample_rate = rate;
+            I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_reconfig_std_slot(rx_chan, &i2s_config.slot_cfg));
+            rx_data_bit_width = bits_cfg;
+            rx_slot_mode = ch;
+            I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_enable(rx_chan));
+            return transformRX(transform);
+        }
+        if(rx_transform != transform){
+            return transformRX(transform);
+        }
+        return true;
+    }
+    return false;
+}
+
+size_t I2SClass::readBytes(char *buffer, size_t size){
+    size_t bytes_read = 0;
+    size_t total_size = 0;
+    last_error = ESP_FAIL;
+    if(rx_chan == NULL){
+        return total_size;
+    }
+    while (total_size < size) {
+        bytes_read = size - total_size;
+        if(rx_transform_buf != NULL && bytes_read > I2S_READ_CHUNK_SIZE){ bytes_read = I2S_READ_CHUNK_SIZE; }
+        I2S_ERROR_CHECK_RETURN(rx_fn(rx_chan, rx_transform_buf, (char*)(buffer + total_size), bytes_read, &bytes_read, _timeout), 0);
+        total_size += bytes_read;
+    }
+    return total_size;
+}
+
+size_t I2SClass::write(uint8_t *buffer, size_t size){
+    size_t written = 0;
+    size_t bytes_sent = 0;
+    last_error = ESP_FAIL;
+    if(tx_chan == NULL){
+        return written;
+    }
+    while(written < size){
+        bytes_sent = size - written;
+        esp_err_t err = i2s_channel_write(tx_chan, (char*)(buffer + written), bytes_sent, &bytes_sent, _timeout);
+        setWriteError(err);
+        I2S_ERROR_CHECK_RETURN(err, written);
+        written += bytes_sent;
+    }
+    return written;
+}
+
+i2s_chan_handle_t I2SClass::txChan(){ return tx_chan; }
+uint32_t I2SClass::txSampleRate(){ return tx_sample_rate; }
+i2s_data_bit_width_t I2SClass::txDataWidth(){ return tx_data_bit_width; }
+i2s_slot_mode_t I2SClass::txSlotMode(){ return tx_slot_mode; }
+
+i2s_chan_handle_t I2SClass::rxChan(){ return rx_chan; }
+uint32_t I2SClass::rxSampleRate(){ return rx_sample_rate; }
+i2s_data_bit_width_t I2SClass::rxDataWidth(){ return rx_data_bit_width; }
+i2s_slot_mode_t I2SClass::rxSlotMode(){ return rx_slot_mode; }
+
+int I2SClass::lastError(){
+    return (int)last_error;
+}
+
+int I2SClass::available(){
+    if(rx_chan == NULL){
+        return -1;
+    }
+    return I2S_READ_CHUNK_SIZE;// / (rx_data_bit_width/8);
+};
+
+int I2SClass::peek(){
+    return -1;
+};
+
+int I2SClass::read(){
+    int out = 0;
+    if(readBytes((char *)&out, rx_data_bit_width/8) == (rx_data_bit_width/8)){
+        return out;
+    }
+    return -1;
+};
+
+size_t I2SClass::write(uint8_t d){
+    return write(&d, 1);
+}
+
+bool I2SClass::transformRX(i2s_rx_transform_t transform){
+    switch(transform){
+        case I2S_RX_TRANSFORM_NONE:
+            allocTranformRX(0);
+            rx_fn = i2s_channel_read_default;
+            break;
+
+        case I2S_RX_TRANSFORM_32_TO_16:
+            if(rx_data_bit_width != I2S_DATA_BIT_WIDTH_32BIT){
+                log_e("Wrong data width. Should be 32bit");
+                return false;
+            }
+            if(!allocTranformRX(I2S_READ_CHUNK_SIZE * 2)){
+                return false;
+            }
+            rx_fn = i2s_channel_read_32_to_16;
+            rx_data_bit_width = I2S_DATA_BIT_WIDTH_16BIT;
+            break;
+
+        case I2S_RX_TRANSFORM_16_STEREO_TO_MONO:
+            if(rx_slot_mode != I2S_SLOT_MODE_STEREO){
+                log_e("Wrong slot mode. Should be Stereo");
+                return false;
+            }
+            if(!allocTranformRX(I2S_READ_CHUNK_SIZE * 2)){
+                return false;
+            }
+            rx_fn = i2s_channel_read_16_stereo_to_mono;
+            rx_slot_mode = I2S_SLOT_MODE_MONO;
+            break;
+            
+        default:
+            log_e("Unknown RX Transform %d", transform);
+            return false;
+    }
+    rx_transform = transform;
+    return true;
+}
+
+bool I2SClass::allocTranformRX(size_t buf_len){
+    char* buf = NULL;
+    if(buf_len == 0){
+        if(rx_transform_buf != NULL){
+            free(rx_transform_buf);
+            rx_transform_buf = NULL;
+            rx_transform_buf_len = 0;
+        }
+        return true;
+    }
+    if(rx_transform_buf == NULL || rx_transform_buf_len != buf_len){
+        buf = (char*)malloc(buf_len);
+        if(buf == NULL){
+            log_e("malloc %u failed!", buf_len);
+            return false;
+        }
+        if(rx_transform_buf != NULL){
+            free(rx_transform_buf);
+        }
+        rx_transform_buf = buf;
+        rx_transform_buf_len = buf_len;
+    }
+    return true;
+}
+
+const int WAVE_HEADER_SIZE = PCM_WAV_HEADER_SIZE;
+
+//Record PCM WAV with current RX settings
+uint8_t * I2SClass::recordWAV(size_t rec_seconds, size_t * out_size){
+  uint32_t sample_rate = rxSampleRate();
+  uint16_t sample_width = (uint16_t)rxDataWidth();
+  uint16_t num_channels = (uint16_t)rxSlotMode();
+  size_t rec_size = rec_seconds * ((sample_rate * (sample_width / 8)) * num_channels);
+  const pcm_wav_header_t wav_header = PCM_WAV_HEADER_DEFAULT(rec_size, sample_width, sample_rate, num_channels);
+  *out_size = 0;
+
+  log_d("Record WAV: rate:%lu, bits:%u, channels:%u, size:%lu", sample_rate, sample_width, num_channels, rec_size);
+
+  uint8_t * wav_buf = (uint8_t*)malloc(rec_size + WAVE_HEADER_SIZE);
+  if(wav_buf == NULL){
+    log_e("Failed to allocate WAV buffer with size %u", rec_size + WAVE_HEADER_SIZE);
+    return NULL;
+  }
+  memcpy(wav_buf, &wav_header, WAVE_HEADER_SIZE);
+  size_t wav_size = readBytes((char*)(wav_buf + WAVE_HEADER_SIZE), rec_size);
+  if(wav_size < rec_size){
+    log_e("Recorded %u bytes from %u", wav_size, rec_size);
+  } else if(lastError()){
+    log_e("Read Failed! %d", lastError());
+  } else {
+    *out_size = rec_size + WAVE_HEADER_SIZE;
+    return wav_buf;
+  }
+  free(wav_buf);
+  return NULL;
+}
+
+void I2SClass::playWAV(uint8_t * data, size_t len){
+  pcm_wav_header_t * header = (pcm_wav_header_t*)data;
+  if(header->fmt_chunk.audio_format != 1){
+    log_e("Audio format is not PCM!");
+    return;
+  }
+  wav_data_chunk_t * data_chunk = &header->data_chunk;
+  size_t data_offset = 0;
+  while(memcmp(data_chunk->subchunk_id, "data", 4) != 0){
+    log_d("Skip chunk: %c%c%c%c, len: %lu", data_chunk->subchunk_id[0], data_chunk->subchunk_id[1], data_chunk->subchunk_id[2], data_chunk->subchunk_id[3], data_chunk->subchunk_size + 8);
+    data_offset += data_chunk->subchunk_size + 8;
+    data_chunk = (wav_data_chunk_t *)(data + WAVE_HEADER_SIZE + data_offset - 8);
+  }
+  log_d("Play WAV: rate:%lu, bits:%d, channels:%d, size:%lu", header->fmt_chunk.sample_rate, header->fmt_chunk.bits_per_sample, header->fmt_chunk.num_of_channels, data_chunk->subchunk_size);
+  configureTX(header->fmt_chunk.sample_rate, (i2s_data_bit_width_t)header->fmt_chunk.bits_per_sample, (i2s_slot_mode_t)header->fmt_chunk.num_of_channels);
+  write(data + WAVE_HEADER_SIZE + data_offset, data_chunk->subchunk_size);
+}
+
+bool I2SClass::playMP3(uint8_t *src, size_t src_len)
+{
+    int16_t outBuf[MAX_NCHAN * MAX_NGRAN * MAX_NSAMP];
+    uint8_t *readPtr=NULL;
+    int bytesAvailable=0, err=0, offset=0;
+    MP3FrameInfo frameInfo;
+    HMP3Decoder decoder=NULL;
+
+    bytesAvailable = src_len;
+    readPtr = src;
+
+    decoder = MP3InitDecoder();
+    if (decoder == NULL){
+        log_e("Could not allocate decoder");
+        return false;
+    }
+
+    do {
+        offset = MP3FindSyncWord(readPtr, bytesAvailable);
+        if (offset < 0) {
+            break;
+        }
+        readPtr += offset;
+        bytesAvailable -= offset;
+        err = MP3Decode(decoder, &readPtr, &bytesAvailable, outBuf, 0);
+        if (err) {
+            log_e("Decode ERROR: %d", err);
+            MP3FreeDecoder(decoder);
+            return false;
+        } else {
+            MP3GetLastFrameInfo(decoder, &frameInfo);
+            configureTX(frameInfo.samprate, (i2s_data_bit_width_t)frameInfo.bitsPerSample, (i2s_slot_mode_t)frameInfo.nChans);
+            write((uint8_t*)outBuf, (size_t)((frameInfo.bitsPerSample / 8) * frameInfo.outputSamps));
+        }
+    } while (true);
+    MP3FreeDecoder(decoder);
+    return true;
+}
diff --git a/libraries/ESP_I2S/src/ESP_I2S.h b/libraries/ESP_I2S/src/ESP_I2S.h
new file mode 100644
index 00000000000..975a4f037c0
--- /dev/null
+++ b/libraries/ESP_I2S/src/ESP_I2S.h
@@ -0,0 +1,79 @@
+#pragma once
+#include "Arduino.h"
+#include "esp_err.h"
+#include "driver/i2s_std.h"
+
+typedef esp_err_t (*i2s_channel_read_fn)(i2s_chan_handle_t handle, char * tmp_buf, void *dest, size_t size, size_t *bytes_read, uint32_t timeout_ms);
+
+typedef enum {
+    I2S_RX_TRANSFORM_NONE,
+    I2S_RX_TRANSFORM_32_TO_16,
+    I2S_RX_TRANSFORM_16_STEREO_TO_MONO,
+    I2S_RX_TRANSFORM_MAX
+} i2s_rx_transform_t;
+
+class I2SClass: public Stream {
+  public:
+    I2SClass();
+    ~I2SClass();
+
+    void setPins(int8_t bclk, int8_t ws, int8_t dout, int8_t din=-1, int8_t mclk=-1);
+    void setInverted(bool bclk, bool ws, bool mclk=false);
+
+    bool begin(uint32_t rate, i2s_data_bit_width_t bits_cfg, i2s_slot_mode_t ch);
+    bool configureTX(uint32_t rate, i2s_data_bit_width_t bits_cfg, i2s_slot_mode_t ch);
+    bool configureRX(uint32_t rate, i2s_data_bit_width_t bits_cfg, i2s_slot_mode_t ch, i2s_rx_transform_t transform=I2S_RX_TRANSFORM_NONE);
+    bool end();
+
+    size_t readBytes(char *buffer, size_t size);
+    size_t write(uint8_t *buffer, size_t size);
+
+    i2s_chan_handle_t txChan();
+    uint32_t txSampleRate();
+    i2s_data_bit_width_t txDataWidth();
+    i2s_slot_mode_t txSlotMode();
+
+    i2s_chan_handle_t rxChan();
+    uint32_t rxSampleRate();
+    i2s_data_bit_width_t rxDataWidth();
+    i2s_slot_mode_t rxSlotMode();
+
+    int lastError();
+
+    int available();
+    int peek();
+    int read();
+    size_t write(uint8_t d);
+
+    // Record short PCM WAV to memory with current RX settings. Returns buffer that must be freed by the user.
+    uint8_t * recordWAV(size_t rec_seconds, size_t * out_size);
+    // Play short PCM WAV from memory
+    void playWAV(uint8_t * data, size_t len);
+    // Play short MP3 from memory
+    bool playMP3(uint8_t *src, size_t src_len);
+
+
+  private:
+    esp_err_t last_error;
+
+    i2s_chan_handle_t tx_chan;
+    uint32_t tx_sample_rate;
+    i2s_data_bit_width_t tx_data_bit_width;
+    i2s_slot_mode_t tx_slot_mode;
+
+    i2s_channel_read_fn rx_fn;
+    i2s_rx_transform_t rx_transform;
+    char * rx_transform_buf;
+    size_t rx_transform_buf_len;
+
+    i2s_chan_handle_t rx_chan;
+    uint32_t rx_sample_rate;
+    i2s_data_bit_width_t rx_data_bit_width;
+    i2s_slot_mode_t rx_slot_mode;
+
+    int8_t _mclk, _bclk, _ws, _dout, _din;
+    bool _mclk_inv, _bclk_inv, _ws_inv;
+
+    bool allocTranformRX(size_t buf_len);
+    bool transformRX(i2s_rx_transform_t transform);
+};
diff --git a/libraries/ESP_I2S/src/wav_header.h b/libraries/ESP_I2S/src/wav_header.h
new file mode 100644
index 00000000000..3969cac4043
--- /dev/null
+++ b/libraries/ESP_I2S/src/wav_header.h
@@ -0,0 +1,91 @@
+#pragma once
+#include <stdint.h>
+
+/**
+ * @brief Header structure for WAV file with only one data chunk
+ *
+ * @note See this for reference: http://soundfile.sapp.org/doc/WaveFormat/
+ *
+ * @note Assignment to variables in this struct directly is only possible for little endian architectures
+ *       (including Xtensa & RISC-V)
+ */
+
+typedef struct {
+    char chunk_id[4];         /*!< Contains the letters "RIFF" in ASCII form */
+    uint32_t chunk_size;      /*!< This is the size of the rest of the chunk following this number */
+    char chunk_format[4];     /*!< Contains the letters "WAVE" */
+} __attribute__((packed)) wav_descriptor_chunk_t; /*!< Canonical WAVE format starts with the RIFF header */
+
+typedef struct {
+    char subchunk_id[4];      /*!< Contains the letters "fmt " */
+    uint32_t subchunk_size;   /*!< PCM = 16, This is the size of the rest of the Subchunk which follows this number */
+    uint16_t audio_format;    /*!< PCM = 1, values other than 1 indicate some form of compression */
+    uint16_t num_of_channels; /*!< Mono = 1, Stereo = 2, etc. */
+    uint32_t sample_rate;     /*!< 8000, 44100, etc. */
+    uint32_t byte_rate;       /*!< ==SampleRate * NumChannels * BitsPerSample s/ 8 */
+    uint16_t block_align;     /*!< ==NumChannels * BitsPerSample / 8 */
+    uint16_t bits_per_sample; /*!< 8 bits = 8, 16 bits = 16, etc. */
+} __attribute__((packed)) pcm_wav_fmt_chunk_t; /*!< The "fmt " subchunk describes the sound data's format */
+
+typedef struct {
+    char subchunk_id[4];      /*!< Contains the letters "fmt " */
+    uint32_t subchunk_size;   /*!< ALAW/MULAW = 18, This is the size of the rest of the Subchunk which follows this number */
+    uint16_t audio_format;    /*!< ALAW = 6, MULAW = 7, values other than 1 indicate some form of compression */
+    uint16_t num_of_channels; /*!< ALAW/MULAW = 1, Mono = 1, Stereo = 2, etc. */
+    uint32_t sample_rate;     /*!< ALAW/MULAW = 8000, 8000, 44100, etc. */
+    uint32_t byte_rate;       /*!< ALAW/MULAW = 8000, ==SampleRate * NumChannels * BitsPerSample s/ 8 */
+    uint16_t block_align;     /*!< ALAW/MULAW = 1, ==NumChannels * BitsPerSample / 8 */
+    uint16_t bits_per_sample; /*!< ALAW/MULAW = 8, 8 bits = 8, 16 bits = 16, etc. */
+    uint16_t ext_size;        /*!< ALAW/MULAW = 0, Size of the extension (0 or 22) */
+} __attribute__((packed)) non_pcm_wav_fmt_chunk_t; /*!< The "fmt " subchunk describes the sound data's format */
+
+typedef struct {
+    char subchunk_id[4];      /*!< Contains the letters "data" */
+    uint32_t subchunk_size;   /*!< ==NumSamples * NumChannels * BitsPerSample / 8 */
+} __attribute__((packed)) wav_data_chunk_t; /*!< The "data" subchunk contains the size of the data and the actual sound */
+
+typedef struct {
+    wav_descriptor_chunk_t descriptor_chunk;  /*!< Canonical WAVE format starts with the RIFF header */
+    pcm_wav_fmt_chunk_t fmt_chunk;            /*!< The "fmt " subchunk describes the sound data's format */
+    wav_data_chunk_t data_chunk;              /*!< The "data" subchunk contains the size of the data and the actual sound */
+} __attribute__((packed)) pcm_wav_header_t;
+
+typedef struct {
+    wav_descriptor_chunk_t descriptor_chunk;  /*!< Canonical WAVE format starts with the RIFF header */
+    non_pcm_wav_fmt_chunk_t fmt_chunk;        /*!< The "fmt " subchunk describes the sound data's format */
+    wav_data_chunk_t data_chunk;              /*!< The "data" subchunk contains the size of the data and the actual sound */
+} __attribute__((packed)) non_pcm_wav_header_t;
+
+#define WAVE_FORMAT_PCM         1 // PCM
+#define WAVE_FORMAT_IEEE_FLOAT  3 // IEEE float
+#define WAVE_FORMAT_ALAW        6 // 8-bit ITU-T G.711 A-law
+#define WAVE_FORMAT_MULAW       7 // 8-bit ITU-T G.711 ยต-law
+
+#define PCM_WAV_HEADER_SIZE     44
+#define NON_PCM_WAV_HEADER_SIZE 46
+
+/**
+ * @brief Default header for PCM format WAV files
+ *
+ */
+#define PCM_WAV_HEADER_DEFAULT(wav_sample_size, wav_sample_bits, wav_sample_rate, wav_channel_num) { \
+    .descriptor_chunk = { \
+        .chunk_id = {'R', 'I', 'F', 'F'}, \
+        .chunk_size = (wav_sample_size) + sizeof(pcm_wav_header_t) - 8, \
+        .chunk_format = {'W', 'A', 'V', 'E'} \
+    }, \
+    .fmt_chunk = { \
+        .subchunk_id = {'f', 'm', 't', ' '}, \
+        .subchunk_size = 16, /* 16 for PCM */ \
+        .audio_format = WAVE_FORMAT_PCM, /* 1 for PCM */ \
+        .num_of_channels = (uint16_t)(wav_channel_num), \
+        .sample_rate = (wav_sample_rate), \
+        .byte_rate = (wav_sample_bits) * (wav_sample_rate) * (wav_channel_num) / 8, \
+        .block_align = (uint16_t)((wav_sample_bits) * (wav_channel_num) / 8), \
+        .bits_per_sample = (uint16_t)(wav_sample_bits)\
+    }, \
+    .data_chunk = { \
+        .subchunk_id = {'d', 'a', 't', 'a'}, \
+        .subchunk_size = (wav_sample_size) \
+    } \
+}
diff --git a/libraries/I2S/examples/ADCPlotter/.skip.esp32c3 b/libraries/ESP_SR/examples/Basic/.skip.esp32
similarity index 100%
rename from libraries/I2S/examples/ADCPlotter/.skip.esp32c3
rename to libraries/ESP_SR/examples/Basic/.skip.esp32
diff --git a/libraries/I2S/examples/FullDuplex/.skip.esp32c3 b/libraries/ESP_SR/examples/Basic/.skip.esp32c3
similarity index 100%
rename from libraries/I2S/examples/FullDuplex/.skip.esp32c3
rename to libraries/ESP_SR/examples/Basic/.skip.esp32c3
diff --git a/libraries/I2S/examples/ADCPlotter/.skip.esp32c6 b/libraries/ESP_SR/examples/Basic/.skip.esp32c6
similarity index 100%
rename from libraries/I2S/examples/ADCPlotter/.skip.esp32c6
rename to libraries/ESP_SR/examples/Basic/.skip.esp32c6
diff --git a/libraries/I2S/examples/ADCPlotter/.skip.esp32h2 b/libraries/ESP_SR/examples/Basic/.skip.esp32h2
similarity index 100%
rename from libraries/I2S/examples/ADCPlotter/.skip.esp32h2
rename to libraries/ESP_SR/examples/Basic/.skip.esp32h2
diff --git a/libraries/I2S/examples/FullDuplex/.skip.esp32c6 b/libraries/ESP_SR/examples/Basic/.skip.esp32s2
similarity index 100%
rename from libraries/I2S/examples/FullDuplex/.skip.esp32c6
rename to libraries/ESP_SR/examples/Basic/.skip.esp32s2
diff --git a/libraries/ESP_SR/examples/Basic/Basic.ino b/libraries/ESP_SR/examples/Basic/Basic.ino
new file mode 100644
index 00000000000..f1258d0c607
--- /dev/null
+++ b/libraries/ESP_SR/examples/Basic/Basic.ino
@@ -0,0 +1,92 @@
+
+#include "ESP_I2S.h"
+#include "ESP_SR.h"
+
+#define I2S_PIN_BCK 17
+#define I2S_PIN_WS  47
+#define I2S_PIN_DIN 16
+
+#define LIGHT_PIN   40
+#define FAN_PIN     41
+
+I2SClass i2s;
+
+// Generated using the following command:
+// python3 tools/gen_sr_commands.py "Turn on the light,Switch on the light;Turn off the light,Switch off the light,Go dark;Start fan;Stop fan"
+enum {
+  SR_CMD_TURN_ON_THE_LIGHT,
+  SR_CMD_TURN_OFF_THE_LIGHT,
+  SR_CMD_START_FAN,
+  SR_CMD_STOP_FAN,
+};
+static const sr_cmd_t sr_commands[] = {
+  { 0, "Turn on the light", "TkN nN jc LiT"},
+  { 0, "Switch on the light", "SWgp nN jc LiT"},
+  { 1, "Turn off the light", "TkN eF jc LiT"},
+  { 1, "Switch off the light", "SWgp eF jc LiT"},
+  { 1, "Go dark", "Gb DnRK"},
+  { 2, "Start fan", "STnRT FaN"},
+  { 3, "Stop fan", "STnP FaN"},
+};
+
+void onSrEvent(sr_event_t event, int command_id, int phrase_id){
+  switch(event){
+    case SR_EVENT_WAKEWORD:
+      Serial.println("WakeWord Detected!");
+      break;
+    case SR_EVENT_WAKEWORD_CHANNEL:
+      Serial.printf("WakeWord Channel %d Verified!\n", command_id);
+      ESP_SR.setMode(SR_MODE_COMMAND); // Switch to Command detection
+      break;
+    case SR_EVENT_TIMEOUT:
+      Serial.println("Timeout Detected!");
+      ESP_SR.setMode(SR_MODE_WAKEWORD); // Switch back to WakeWord detection
+      break;
+    case SR_EVENT_COMMAND:
+      Serial.printf("Command %d Detected! %s\n", command_id, sr_commands[phrase_id].str);
+      switch(command_id){
+	      case SR_CMD_TURN_ON_THE_LIGHT:
+	      	digitalWrite(LIGHT_PIN, HIGH);
+	      	break;
+	      case SR_CMD_TURN_OFF_THE_LIGHT:
+	      	digitalWrite(LIGHT_PIN, LOW);
+	      	break;
+	      case SR_CMD_START_FAN:
+	      	digitalWrite(FAN_PIN, HIGH);
+	      	break;
+	      case SR_CMD_STOP_FAN:
+	      	digitalWrite(FAN_PIN, LOW);
+	      	break;
+	      default:
+	      	Serial.println("Unknown Command!");
+	      	break;
+      }
+      ESP_SR.setMode(SR_MODE_COMMAND); // Allow for more commands to be given, before timeout
+      // ESP_SR.setMode(SR_MODE_WAKEWORD); // Switch back to WakeWord detection
+      break;
+    default:
+      Serial.println("Unknown Event!");
+      break;
+  }
+}
+
+void setup(){
+  Serial.begin(115200);
+
+  pinMode(LIGHT_PIN, OUTPUT);
+  digitalWrite(LIGHT_PIN, LOW);
+  pinMode(FAN_PIN, OUTPUT);
+  digitalWrite(FAN_PIN, LOW);
+
+  i2s.setPins(I2S_PIN_BCK, I2S_PIN_WS, -1, I2S_PIN_DIN);
+  i2s.setTimeout(1000);
+  i2s.begin(16000, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO);
+
+
+  ESP_SR.onEvent(onSrEvent);
+  ESP_SR.begin(i2s, sr_commands, sizeof(sr_commands) / sizeof(sr_cmd_t), SR_CHANNELS_STEREO, SR_MODE_WAKEWORD);
+}
+
+void loop(){
+
+}
diff --git a/libraries/ESP_SR/keywords.txt b/libraries/ESP_SR/keywords.txt
new file mode 100644
index 00000000000..30a3925187c
--- /dev/null
+++ b/libraries/ESP_SR/keywords.txt
@@ -0,0 +1,40 @@
+#######################################
+# Syntax Coloring Map For ESP_SR
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+ESP_SR	KEYWORD1
+ESP_SR_Class	KEYWORD1
+sr_cmd_t	KEYWORD1
+sr_event_t	KEYWORD1
+sr_mode_t	KEYWORD1
+sr_channels_t	KEYWORD1
+sr_cb	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+onEvent	KEYWORD2
+setMode	KEYWORD2
+pause	KEYWORD2
+resume	KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
+SR_EVENT_WAKEWORD	LITERAL1
+SR_EVENT_WAKEWORD_CHANNEL	LITERAL1
+SR_EVENT_COMMAND	LITERAL1
+SR_EVENT_TIMEOUT	LITERAL1
+SR_MODE_OFF	LITERAL1
+SR_MODE_WAKEWORD	LITERAL1
+SR_MODE_COMMAND	LITERAL1
+SR_MODE_MAX	LITERAL1
+SR_CHANNELS_MONO	LITERAL1
+SR_CHANNELS_STEREO	LITERAL1
+SR_CHANNELS_MAX	LITERAL1
diff --git a/libraries/ESP_SR/library.properties b/libraries/ESP_SR/library.properties
new file mode 100755
index 00000000000..518cc915fcf
--- /dev/null
+++ b/libraries/ESP_SR/library.properties
@@ -0,0 +1,9 @@
+name=ESP_SR
+version=1.0.0
+author=me-no-dev
+maintainer=me-no-dev
+sentence=Library for ESP Sound Recognition
+paragraph=Supports ESP32 Arduino platforms.
+category=Sound
+url=https://github.com/espressif/arduino-esp32/
+architectures=esp32
\ No newline at end of file
diff --git a/libraries/ESP_SR/src/ESP_SR.cpp b/libraries/ESP_SR/src/ESP_SR.cpp
new file mode 100644
index 00000000000..f0135f1cfa5
--- /dev/null
+++ b/libraries/ESP_SR/src/ESP_SR.cpp
@@ -0,0 +1,72 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "sdkconfig.h"
+#if CONFIG_IDF_TARGET_ESP32S3
+#include "ESP_SR.h"
+
+static esp_err_t on_sr_fill(void * arg, void * out, size_t len, size_t *bytes_read, uint32_t timeout_ms){
+    return ((ESP_SR_Class*)arg)->_fill(out, len, bytes_read, timeout_ms);
+}
+
+static void on_sr_event(void * arg, sr_event_t event, int command_id, int phrase_id){
+    ((ESP_SR_Class*)arg)->_sr_event(event, command_id, phrase_id);
+}
+
+ESP_SR_Class::ESP_SR_Class()
+    : cb(NULL)
+    , i2s(NULL)
+{
+
+}
+
+ESP_SR_Class::~ESP_SR_Class(){
+    end();
+}
+
+void ESP_SR_Class::onEvent(sr_cb event_cb){
+    cb = event_cb;
+}
+
+bool ESP_SR_Class::begin(I2SClass & _i2s, const sr_cmd_t * sr_commands, size_t sr_commands_len, sr_channels_t rx_chan, sr_mode_t mode){
+    i2s = &_i2s;
+    esp_err_t err = sr_start(on_sr_fill, this, rx_chan, mode, sr_commands, sr_commands_len, on_sr_event, this);
+    return (err == ESP_OK);
+}
+
+bool ESP_SR_Class::end(void){
+    return sr_stop() == ESP_OK;
+}
+
+bool ESP_SR_Class::setMode(sr_mode_t mode){
+    return sr_set_mode(mode) == ESP_OK;
+}
+
+bool ESP_SR_Class::pause(void){
+    return sr_pause() == ESP_OK;
+}
+
+bool ESP_SR_Class::resume(void){
+    return sr_resume() == ESP_OK;
+}
+
+void ESP_SR_Class::_sr_event(sr_event_t event, int command_id, int phrase_id){
+    if(cb){
+        cb(event, command_id, phrase_id);
+    }
+}
+
+esp_err_t ESP_SR_Class::_fill(void * out, size_t len, size_t *bytes_read, uint32_t timeout_ms){
+    if(i2s == NULL){
+        return ESP_FAIL;
+    }
+    i2s->setTimeout(timeout_ms);
+    *bytes_read = i2s->readBytes((char *)out, len);
+    return (esp_err_t)i2s->lastError();
+}
+
+ESP_SR_Class ESP_SR;
+
+#endif // CONFIG_IDF_TARGET_ESP32S3
diff --git a/libraries/ESP_SR/src/ESP_SR.h b/libraries/ESP_SR/src/ESP_SR.h
new file mode 100644
index 00000000000..210919ee29d
--- /dev/null
+++ b/libraries/ESP_SR/src/ESP_SR.h
@@ -0,0 +1,38 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#pragma once
+#include "sdkconfig.h"
+#if CONFIG_IDF_TARGET_ESP32S3
+
+#include "ESP_I2S.h"
+#include "esp32-hal-sr.h"
+
+typedef void (*sr_cb)(sr_event_t event, int command_id, int phrase_id);
+
+class ESP_SR_Class {
+  private:
+    sr_cb cb;
+    I2SClass * i2s;
+  public:
+    ESP_SR_Class();
+    ~ESP_SR_Class();
+
+    void onEvent(sr_cb cb);
+    bool begin(I2SClass & i2s, const sr_cmd_t * sr_commands, size_t sr_commands_len, sr_channels_t rx_chan=SR_CHANNELS_STEREO, sr_mode_t mode=SR_MODE_WAKEWORD);
+    bool end(void);
+    bool setMode(sr_mode_t mode);
+    bool pause(void);
+    bool resume(void);
+
+    void _sr_event(sr_event_t event, int command_id, int phrase_id);
+    esp_err_t _fill(void * out, size_t len, size_t *bytes_read, uint32_t timeout_ms);
+};
+
+extern ESP_SR_Class ESP_SR;
+
+
+#endif // CONFIG_IDF_TARGET_ESP32S3
diff --git a/libraries/ESP_SR/src/esp32-hal-sr.c b/libraries/ESP_SR/src/esp32-hal-sr.c
new file mode 100644
index 00000000000..1c53bd7b58c
--- /dev/null
+++ b/libraries/ESP_SR/src/esp32-hal-sr.c
@@ -0,0 +1,445 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "sdkconfig.h"
+#if CONFIG_IDF_TARGET_ESP32S3
+
+#if !defined(ARDUINO_PARTITION_esp_sr_32) && !defined(ARDUINO_PARTITION_esp_sr_16) && !defined(ARDUINO_PARTITION_esp_sr_8)
+#warning Compatible partition must be selected for ESP_SR to work
+#endif
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/queue.h"
+#include "freertos/event_groups.h"
+#include "freertos/task.h"
+#include "esp_task_wdt.h"
+#include "esp_check.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_mn_speech_commands.h"
+#include "esp_process_sdkconfig.h"
+#include "esp_afe_sr_models.h"
+#include "esp_mn_models.h"
+#include "esp_wn_iface.h"
+#include "esp_wn_models.h"
+#include "esp_afe_sr_iface.h"
+#include "esp_mn_iface.h"
+#include "model_path.h"
+
+#include "driver/i2s_common.h"
+#include "esp32-hal-sr.h"
+#include "esp32-hal-log.h"
+
+#undef ESP_GOTO_ON_FALSE
+#define ESP_GOTO_ON_FALSE(a, err_code, goto_tag, format, ...) do { \
+        if (unlikely(!(a))) {                                      \
+            log_e(format, ##__VA_ARGS__);                          \
+            ret = err_code;                                        \
+            goto goto_tag;                                         \
+        }                                                          \
+    } while (0)
+
+#undef ESP_RETURN_ON_FALSE
+#define ESP_RETURN_ON_FALSE(a, err_code, format, ...) do { \
+        if (unlikely(!(a))) {                              \
+            log_e(format, ##__VA_ARGS__);                  \
+            return err_code;                               \
+        }                                                  \
+    } while(0)
+
+#define NEED_DELETE BIT0
+#define FEED_DELETED BIT1
+#define DETECT_DELETED BIT2
+#define PAUSE_FEED BIT3
+#define PAUSE_DETECT BIT4
+#define RESUME_FEED BIT5
+#define RESUME_DETECT BIT6
+
+typedef struct {
+    wakenet_state_t wakenet_mode;
+    esp_mn_state_t state;
+    int command_id;
+    int phrase_id;
+} sr_result_t;
+
+typedef struct {
+    model_iface_data_t *model_data;
+    const esp_mn_iface_t *multinet;
+    const esp_afe_sr_iface_t *afe_handle;
+    esp_afe_sr_data_t *afe_data;
+    int16_t *afe_in_buffer;
+    sr_mode_t mode;
+    uint8_t i2s_rx_chan_num;
+    sr_event_cb user_cb;
+    void * user_cb_arg;
+    sr_fill_cb fill_cb;
+    void * fill_cb_arg;
+    TaskHandle_t feed_task;
+    TaskHandle_t detect_task;
+    TaskHandle_t handle_task;
+    QueueHandle_t result_que;
+    EventGroupHandle_t event_group;
+} sr_data_t;
+
+static int SR_CHANNEL_NUM = 3;
+
+static srmodel_list_t *models = NULL;
+static sr_data_t *g_sr_data = NULL;
+
+esp_err_t sr_set_mode(sr_mode_t mode);
+
+void sr_handler_task(void *pvParam)
+{
+    while (true) {
+        sr_result_t result;
+        if(xQueueReceive(g_sr_data->result_que, &result, portMAX_DELAY) != pdTRUE){
+            continue;
+        }
+
+        if (WAKENET_DETECTED == result.wakenet_mode) {
+            if(g_sr_data->user_cb){
+                g_sr_data->user_cb(g_sr_data->user_cb_arg, SR_EVENT_WAKEWORD, -1, -1);
+            }
+            continue;
+        }
+
+        if (WAKENET_CHANNEL_VERIFIED == result.wakenet_mode) {
+            if(g_sr_data->user_cb){
+                g_sr_data->user_cb(g_sr_data->user_cb_arg, SR_EVENT_WAKEWORD_CHANNEL, result.command_id, -1);
+            }
+            continue;
+        }
+
+        if (ESP_MN_STATE_DETECTED == result.state) {
+            if(g_sr_data->user_cb){
+                g_sr_data->user_cb(g_sr_data->user_cb_arg, SR_EVENT_COMMAND, result.command_id, result.phrase_id);
+            }
+            continue;
+        }
+
+        if (ESP_MN_STATE_TIMEOUT == result.state) {
+            if(g_sr_data->user_cb){
+                g_sr_data->user_cb(g_sr_data->user_cb_arg, SR_EVENT_TIMEOUT, -1, -1);
+            }
+            continue;
+        }
+    }
+    vTaskDelete(NULL);
+}
+
+static void audio_feed_task(void *arg)
+{
+    size_t bytes_read = 0;
+    int audio_chunksize = g_sr_data->afe_handle->get_feed_chunksize(g_sr_data->afe_data);
+    log_i("audio_chunksize=%d, feed_channel=%d", audio_chunksize, SR_CHANNEL_NUM);
+
+    /* Allocate audio buffer and check for result */
+    int16_t *audio_buffer = heap_caps_malloc(audio_chunksize * sizeof(int16_t) * SR_CHANNEL_NUM, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
+    if (NULL == audio_buffer) {
+        esp_system_abort("No mem for audio buffer");
+    }
+    g_sr_data->afe_in_buffer = audio_buffer;
+
+    while (true) {
+        EventBits_t bits = xEventGroupGetBits(g_sr_data->event_group);
+        if (NEED_DELETE & bits) {
+            xEventGroupSetBits(g_sr_data->event_group, FEED_DELETED);
+            break;
+        }
+        if (PAUSE_FEED & bits) {
+            xEventGroupWaitBits(g_sr_data->event_group, PAUSE_FEED | RESUME_FEED, 1, 1, portMAX_DELAY);
+        }
+
+        /* Read audio data from I2S bus */
+        //ToDo: handle error
+        if(g_sr_data->fill_cb == NULL){
+            vTaskDelay(100);
+            continue;
+        }
+        esp_err_t err = g_sr_data->fill_cb(g_sr_data->fill_cb_arg, (char *)audio_buffer, audio_chunksize * g_sr_data->i2s_rx_chan_num * sizeof(int16_t), &bytes_read, portMAX_DELAY);
+        if(err != ESP_OK){
+            vTaskDelay(100);
+            continue;
+        }
+
+        /* Channel Adjust */
+        if(g_sr_data->i2s_rx_chan_num == 1){
+            for (int  i = audio_chunksize - 1; i >= 0; i--) {
+                audio_buffer[i * SR_CHANNEL_NUM + 2] = 0;
+                audio_buffer[i * SR_CHANNEL_NUM + 1] = 0;
+                audio_buffer[i * SR_CHANNEL_NUM + 0] = audio_buffer[i];
+            }
+        } else if(g_sr_data->i2s_rx_chan_num == 2){
+            for (int  i = audio_chunksize - 1; i >= 0; i--) {
+                audio_buffer[i * SR_CHANNEL_NUM + 2] = 0;
+                audio_buffer[i * SR_CHANNEL_NUM + 1] = audio_buffer[i * 2 + 1];
+                audio_buffer[i * SR_CHANNEL_NUM + 0] = audio_buffer[i * 2 + 0];
+            }
+        } else {
+            vTaskDelay(100);
+            continue;
+        }
+
+        /* Feed samples of an audio stream to the AFE_SR */
+        g_sr_data->afe_handle->feed(g_sr_data->afe_data, audio_buffer);
+    }
+    vTaskDelete(NULL);
+}
+
+static void audio_detect_task(void *arg)
+{
+    int afe_chunksize = g_sr_data->afe_handle->get_fetch_chunksize(g_sr_data->afe_data);
+    int mu_chunksize = g_sr_data->multinet->get_samp_chunksize(g_sr_data->model_data);
+    assert(mu_chunksize == afe_chunksize);
+    log_i("------------detect start------------");
+
+    while (true) {
+        EventBits_t bits = xEventGroupGetBits(g_sr_data->event_group);
+        if (NEED_DELETE & bits) {
+            xEventGroupSetBits(g_sr_data->event_group, DETECT_DELETED);
+            break;
+        }
+        if (PAUSE_DETECT & bits) {
+            xEventGroupWaitBits(g_sr_data->event_group, PAUSE_DETECT | RESUME_DETECT, 1, 1, portMAX_DELAY);
+        }
+
+        afe_fetch_result_t* res = g_sr_data->afe_handle->fetch(g_sr_data->afe_data);
+        if (!res || res->ret_value == ESP_FAIL) {
+            continue;
+        }
+
+        if(g_sr_data->mode == SR_MODE_WAKEWORD){
+            if (res->wakeup_state == WAKENET_DETECTED) {
+                log_d("wakeword detected");
+                sr_result_t result = {
+                    .wakenet_mode = WAKENET_DETECTED,
+                    .state = ESP_MN_STATE_DETECTING,
+                    .command_id = 0,
+                    .phrase_id = 0,
+                };
+                xQueueSend(g_sr_data->result_que, &result, 0);
+            }
+            else if (res->wakeup_state == WAKENET_CHANNEL_VERIFIED) {
+                sr_set_mode(SR_MODE_OFF);
+                log_d("AFE_FETCH_CHANNEL_VERIFIED, channel index: %d", res->trigger_channel_id);
+                sr_result_t result = {
+                    .wakenet_mode = WAKENET_CHANNEL_VERIFIED,
+                    .state = ESP_MN_STATE_DETECTING,
+                    .command_id = res->trigger_channel_id,
+                    .phrase_id = 0,
+                };
+                xQueueSend(g_sr_data->result_que, &result, 0);
+            }
+        }
+
+        if (g_sr_data->mode == SR_MODE_COMMAND) {
+
+            esp_mn_state_t mn_state = ESP_MN_STATE_DETECTING;
+            mn_state = g_sr_data->multinet->detect(g_sr_data->model_data, res->data);
+
+            if (ESP_MN_STATE_DETECTING == mn_state) {
+                continue;
+            }
+
+            if (ESP_MN_STATE_TIMEOUT == mn_state) {
+                sr_set_mode(SR_MODE_OFF);
+                log_d("Time out");
+                sr_result_t result = {
+                    .wakenet_mode = WAKENET_NO_DETECT,
+                    .state = mn_state,
+                    .command_id = 0,
+                    .phrase_id = 0,
+                };
+                xQueueSend(g_sr_data->result_que, &result, 0);
+                continue;
+            }
+
+            if (ESP_MN_STATE_DETECTED == mn_state) {
+                sr_set_mode(SR_MODE_OFF);
+                esp_mn_results_t *mn_result = g_sr_data->multinet->get_results(g_sr_data->model_data);
+                for (int i = 0; i < mn_result->num; i++) {
+                    log_d("TOP %d, command_id: %d, phrase_id: %d, prob: %f",
+                        i + 1, mn_result->command_id[i], mn_result->phrase_id[i], mn_result->prob[i]);
+                }
+
+                int sr_command_id = mn_result->command_id[0];
+                int sr_phrase_id = mn_result->phrase_id[0];
+                log_d("Deteted command : %d, phrase: %d", sr_command_id, sr_phrase_id);
+                sr_result_t result = {
+                    .wakenet_mode = WAKENET_NO_DETECT,
+                    .state = mn_state,
+                    .command_id = sr_command_id,
+                    .phrase_id = sr_phrase_id,
+                };
+                xQueueSend(g_sr_data->result_que, &result, 0);
+                continue;
+            }
+            log_e("Exception unhandled");
+        }
+    }
+    vTaskDelete(NULL);
+}
+
+esp_err_t sr_set_mode(sr_mode_t mode)
+{
+    ESP_RETURN_ON_FALSE(NULL != g_sr_data, ESP_ERR_INVALID_STATE, "SR is not running");
+    switch(mode){
+        case SR_MODE_OFF:
+            if(g_sr_data->mode == SR_MODE_WAKEWORD){
+                g_sr_data->afe_handle->disable_wakenet(g_sr_data->afe_data);
+            }
+            break;
+        case SR_MODE_WAKEWORD:
+            if(g_sr_data->mode != SR_MODE_WAKEWORD){
+                g_sr_data->afe_handle->enable_wakenet(g_sr_data->afe_data);
+            }
+            break;
+        case SR_MODE_COMMAND:
+            if(g_sr_data->mode == SR_MODE_WAKEWORD){
+                g_sr_data->afe_handle->disable_wakenet(g_sr_data->afe_data);
+            }
+            break;
+        default:
+            return ESP_FAIL;
+    }
+    g_sr_data->mode = mode;
+    return ESP_OK;
+}
+
+esp_err_t sr_start(sr_fill_cb fill_cb, void * fill_cb_arg, sr_channels_t rx_chan, sr_mode_t mode, const sr_cmd_t sr_commands[], size_t cmd_number, sr_event_cb cb, void * cb_arg)
+{
+    esp_err_t ret = ESP_OK;
+    ESP_RETURN_ON_FALSE(NULL == g_sr_data, ESP_ERR_INVALID_STATE, "SR already running");
+
+    g_sr_data = heap_caps_calloc(1, sizeof(sr_data_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
+    ESP_RETURN_ON_FALSE(NULL != g_sr_data, ESP_ERR_NO_MEM, "Failed create sr data");
+
+    g_sr_data->result_que = xQueueCreate(3, sizeof(sr_result_t));
+    ESP_GOTO_ON_FALSE(NULL != g_sr_data->result_que, ESP_ERR_NO_MEM, err, "Failed create result queue");
+
+    g_sr_data->event_group = xEventGroupCreate();
+    ESP_GOTO_ON_FALSE(NULL != g_sr_data->event_group, ESP_ERR_NO_MEM, err, "Failed create event_group");
+
+    BaseType_t ret_val;
+    g_sr_data->user_cb = cb;
+    g_sr_data->user_cb_arg = cb_arg;
+    g_sr_data->fill_cb = fill_cb;
+    g_sr_data->fill_cb_arg = fill_cb_arg;
+    g_sr_data->i2s_rx_chan_num = rx_chan + 1;
+    g_sr_data->mode = mode;
+
+    // Init Model
+    log_d("init model");
+    models = esp_srmodel_init("model");
+
+    // Load WakeWord Detection
+    g_sr_data->afe_handle = (esp_afe_sr_iface_t *)&ESP_AFE_SR_HANDLE;
+    afe_config_t afe_config = AFE_CONFIG_DEFAULT();
+    afe_config.wakenet_model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, "hiesp");
+    afe_config.aec_init = false;
+    log_d("load wakenet '%s'", afe_config.wakenet_model_name);
+    g_sr_data->afe_data = g_sr_data->afe_handle->create_from_config(&afe_config);
+
+    // Load Custom Command Detection
+    char *mn_name = esp_srmodel_filter(models, ESP_MN_PREFIX, ESP_MN_ENGLISH);
+    log_d("load multinet '%s'", mn_name);
+    g_sr_data->multinet = esp_mn_handle_from_name(mn_name);
+    log_d("load model_data '%s'", mn_name);
+    g_sr_data->model_data = g_sr_data->multinet->create(mn_name, 5760);
+
+    
+    // Add commands
+    esp_mn_commands_alloc((esp_mn_iface_t *)g_sr_data->multinet, (model_iface_data_t *)g_sr_data->model_data);
+    log_i("add %d commands", cmd_number);
+    for (size_t i = 0; i < cmd_number; i++) {
+        esp_mn_commands_add(sr_commands[i].command_id, (char *)(sr_commands[i].phoneme));
+        log_i("  cmd[%d] phrase[%d]:'%s'", sr_commands[i].command_id, i, sr_commands[i].str);
+    }
+    
+    // Load commands
+    esp_mn_error_t *err_id = esp_mn_commands_update();
+    if(err_id){
+        for (int i = 0; i < err_id->num; i++) {
+            log_e("err cmd id:%d", err_id->phrases[i]->command_id);
+        }
+    }
+
+    //Start tasks
+    log_d("start tasks");
+    ret_val = xTaskCreatePinnedToCore(&audio_feed_task, "SR Feed Task", 4 * 1024, NULL, 5, &g_sr_data->feed_task, 0);
+    ESP_GOTO_ON_FALSE(pdPASS == ret_val, ESP_FAIL, err,  "Failed create audio feed task");
+    vTaskDelay(10);
+    ret_val = xTaskCreatePinnedToCore(&audio_detect_task, "SR Detect Task", 8 * 1024, NULL, 5, &g_sr_data->detect_task, 1);
+    ESP_GOTO_ON_FALSE(pdPASS == ret_val, ESP_FAIL, err,  "Failed create audio detect task");
+    ret_val = xTaskCreatePinnedToCore(&sr_handler_task, "SR Handler Task", 6 * 1024, NULL, configMAX_PRIORITIES - 1, &g_sr_data->handle_task, 1);
+    //ret_val = xTaskCreatePinnedToCore(&sr_handler_task, "SR Handler Task", 6 * 1024, NULL, configMAX_PRIORITIES - 1, &g_sr_data->handle_task, 0);
+    ESP_GOTO_ON_FALSE(pdPASS == ret_val, ESP_FAIL, err,  "Failed create audio handler task");
+
+    return ESP_OK;
+err:
+    sr_stop();
+    return ret;
+}
+
+esp_err_t sr_stop(void)
+{
+    ESP_RETURN_ON_FALSE(NULL != g_sr_data, ESP_ERR_INVALID_STATE, "SR is not running");
+
+    /**
+     * Waiting for all task stoped
+     * TODO: A task creation failure cannot be handled correctly now
+     * */
+    vTaskDelete(g_sr_data->handle_task);
+    xEventGroupSetBits(g_sr_data->event_group, NEED_DELETE);
+    xEventGroupWaitBits(g_sr_data->event_group, NEED_DELETE | FEED_DELETED | DETECT_DELETED, 1, 1, portMAX_DELAY);
+
+    if (g_sr_data->result_que) {
+        vQueueDelete(g_sr_data->result_que);
+        g_sr_data->result_que = NULL;
+    }
+
+    if (g_sr_data->event_group) {
+        vEventGroupDelete(g_sr_data->event_group);
+        g_sr_data->event_group = NULL;
+    }
+
+    if (g_sr_data->model_data) {
+        g_sr_data->multinet->destroy(g_sr_data->model_data);
+    }
+
+    if (g_sr_data->afe_data) {
+        g_sr_data->afe_handle->destroy(g_sr_data->afe_data);
+    }
+
+    if (g_sr_data->afe_in_buffer) {
+        heap_caps_free(g_sr_data->afe_in_buffer);
+    }
+
+    heap_caps_free(g_sr_data);
+    g_sr_data = NULL;
+    return ESP_OK;
+}
+
+esp_err_t sr_pause(void)
+{
+    ESP_RETURN_ON_FALSE(NULL != g_sr_data, ESP_ERR_INVALID_STATE, "SR is not running");
+    xEventGroupSetBits(g_sr_data->event_group, PAUSE_FEED | PAUSE_DETECT);
+    return ESP_OK;
+}
+
+esp_err_t sr_resume(void)
+{
+    ESP_RETURN_ON_FALSE(NULL != g_sr_data, ESP_ERR_INVALID_STATE, "SR is not running");
+    xEventGroupSetBits(g_sr_data->event_group, RESUME_FEED | RESUME_DETECT);
+    return ESP_OK;
+}
+
+#endif // CONFIG_IDF_TARGET_ESP32S3
diff --git a/libraries/ESP_SR/src/esp32-hal-sr.h b/libraries/ESP_SR/src/esp32-hal-sr.h
new file mode 100644
index 00000000000..3b637f44e2e
--- /dev/null
+++ b/libraries/ESP_SR/src/esp32-hal-sr.h
@@ -0,0 +1,76 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#pragma once
+#include "sdkconfig.h"
+#if CONFIG_IDF_TARGET_ESP32S3
+
+#include "driver/i2s_types.h"
+#include "esp_err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SR_CMD_STR_LEN_MAX 64
+#define SR_CMD_PHONEME_LEN_MAX 64
+
+typedef struct sr_cmd_t {
+    int command_id;
+    char str[SR_CMD_STR_LEN_MAX];
+    char phoneme[SR_CMD_PHONEME_LEN_MAX];
+} sr_cmd_t;
+
+typedef enum {
+    SR_EVENT_WAKEWORD,//WakeWord Detected
+    SR_EVENT_WAKEWORD_CHANNEL,//WakeWord Channel Verified
+    SR_EVENT_COMMAND,//Command Detected
+    SR_EVENT_TIMEOUT,//Command Timeout
+    SR_EVENT_MAX
+} sr_event_t;
+
+typedef enum {
+    SR_MODE_OFF,//Detection Off
+    SR_MODE_WAKEWORD,//WakeWord Detection
+    SR_MODE_COMMAND,//Command Detection
+    SR_MODE_MAX
+} sr_mode_t;
+
+typedef enum {
+    SR_CHANNELS_MONO,
+    SR_CHANNELS_STEREO,
+    SR_CHANNELS_MAX
+} sr_channels_t;
+
+typedef void (*sr_event_cb)(void * arg, sr_event_t event, int command_id, int phrase_id);
+typedef esp_err_t (*sr_fill_cb)(void * arg, void * out, size_t len, size_t *bytes_read, uint32_t timeout_ms);
+
+esp_err_t sr_start(sr_fill_cb fill_cb, void * fill_cb_arg, sr_channels_t rx_chan, sr_mode_t mode, const sr_cmd_t * sr_commands, size_t cmd_number, sr_event_cb cb, void * cb_arg);
+esp_err_t sr_stop(void);
+esp_err_t sr_pause(void);
+esp_err_t sr_resume(void);
+esp_err_t sr_set_mode(sr_mode_t mode);
+
+// static const sr_cmd_t sr_commands[] = {
+//     {0, "Turn On the Light", "TkN nN jc LiT"},
+//     {0, "Switch On the Light", "SWgp nN jc LiT"},
+//     {1, "Switch Off the Light", "SWgp eF jc LiT"},
+//     {1, "Turn Off the Light", "TkN eF jc LiT"},
+//     {2, "Turn Red", "TkN RfD"},
+//     {3, "Turn Green", "TkN GRmN"},
+//     {4, "Turn Blue", "TkN BLo"},
+//     {5, "Customize Color", "KcSTcMiZ KcLk"},
+//     {6, "Sing a song", "Sgl c Sel"},
+//     {7, "Play Music", "PLd MYoZgK"},
+//     {8, "Next Song", "NfKST Sel"},
+//     {9, "Pause Playing", "PeZ PLdgl"},
+// };
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // CONFIG_IDF_TARGET_ESP32S3
diff --git a/libraries/ESP_SR/tools/gen_sr_commands.py b/libraries/ESP_SR/tools/gen_sr_commands.py
new file mode 100644
index 00000000000..f3629b71bcb
--- /dev/null
+++ b/libraries/ESP_SR/tools/gen_sr_commands.py
@@ -0,0 +1,73 @@
+# pip3 install g2p_en
+from g2p_en import G2p
+import argparse
+
+# python3 gen_sr_commands.py "Turn on the light,Switch on the light;Turn off the light,Switch off the light,Go dark;Start fan;Stop fan;Volume down,Turn down;Mute sound;Next song;Pause playback"
+# enum {
+#   SR_CMD_TURN_ON_THE_LIGHT,
+#   SR_CMD_TURN_OFF_THE_LIGHT,
+#   SR_CMD_START_FAN,
+#   SR_CMD_STOP_FAN,
+#   SR_CMD_VOLUME_DOWN,
+#   SR_CMD_MUTE_SOUND,
+#   SR_CMD_NEXT_SONG,
+#   SR_CMD_PAUSE_PLAYBACK,
+# };
+# static const sr_cmd_t sr_commands[] = {
+#   { 0, "Turn on the light", "TkN nN jc LiT"},
+#   { 0, "Switch on the light", "SWgp nN jc LiT"},
+#   { 1, "Turn off the light", "TkN eF jc LiT"},
+#   { 1, "Switch off the light", "SWgp eF jc LiT"},
+#   { 1, "Go dark", "Gb DnRK"},
+#   { 2, "Start fan", "STnRT FaN"},
+#   { 3, "Stop fan", "STnP FaN"},
+#   { 4, "Volume down", "VnLYoM DtN"},
+#   { 4, "Turn down", "TkN DtN"},
+#   { 5, "Mute sound", "MYoT StND"},
+#   { 6, "Next song", "NfKST Sel"},
+#   { 7, "Pause playback", "PeZ PLdBaK"},
+# };
+
+def english_g2p(text):
+    g2p = G2p()
+    out = "static const sr_cmd_t sr_commands[] = {\n"
+    enum = "enum {\n"
+    alphabet={"AE1": "a", "N": "N", " ": " ", "OW1": "b", "V": "V", "AH0": "c", "L": "L", "F": "F", "EY1": "d", "S": "S", "B": "B", "R": "R", "AO1": "e", "D": "D", "AH1": "c", "EH1": "f", "OW0": "b", "IH0": "g", "G": "G", "HH": "h", "K": "K", "IH1": "g", "W": "W", "AY1": "i", "T": "T", "M": "M", "Z": "Z", "DH": "j", "ER0": "k", "P": "P", "NG": "l", "IY1": "m", "AA1": "n", "Y": "Y", "UW1": "o", "IY0": "m", "EH2": "f", "CH": "p", "AE0": "a", "JH": "q", "ZH": "r", "AA2": "n", "SH": "s", "AW1": "t", "OY1": "u", "AW2": "t", "IH2": "g", "AE2": "a", "EY2": "d", "ER1": "k", "TH": "v", "UH1": "w", "UW2": "o", "OW2": "b", "AY2": "i", "UW0": "o", "AH2": "c", "EH0": "f", "AW0": "t", "AO2": "e", "AO0": "e", "UH0": "w", "UH2": "w", "AA0": "n", "AY0": "i", "IY2": "m", "EY0": "d", "ER2": "k", "OY2": "u", "OY0": "u"}
+
+    cmd_id = 0
+    phrase_id = 0
+    text_list = text.split(";")
+    for item in text_list:
+        item = item.split(",")
+        phrase_id = 0
+        for phrase in item:
+            labels = g2p(phrase)
+            phoneme = ""
+            for char in labels:
+                if char not in alphabet:
+                    print("skip %s, not found in alphabet")
+                    continue
+                else:
+                    phoneme += alphabet[char]
+            out += "  { "+str(cmd_id)+", \""+phrase+"\", \""+phoneme+"\"},\n"
+            if phrase_id == 0:
+                enum += "  SR_CMD_"+phrase.upper().replace(" ", "_")+",\n"
+            phrase_id += 1
+        cmd_id += 1
+    out += "};"
+    enum += "};"
+    # print(text)
+    print(enum)
+    print(out)
+    
+    return out
+
+if __name__ == "__main__":
+
+    parser = argparse.ArgumentParser(prog="English Speech Commands G2P")
+    parser.add_argument("text", type=str,  default=None, help="input text")
+    args = parser.parse_args()
+    
+    if args.text is not None:
+        english_g2p(args.text)
+
diff --git a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino
deleted file mode 100644
index 5f3bd93ca9d..00000000000
--- a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- This example is only for ESP devices.
-
- This example demonstrates usage of integrated Digital to Analog Converter (DAC)
- You can display sound wave from audio device, or just measure voltage.
-
- To display audio prepare circuit found in following link or drafted as ASCII art
- https://forum.arduino.cc/index.php?topic=567581.0
- (!) Note that unlike in the link we are connecting the supply line to 3.3V (not 5V)
- because ADC can measure only up to around 3V. Anything above 3V will be very inaccurate.
-
-                      ^ +3.3V
-                      |
-                      _
-                     | |10k
-                     |_|
-                      |            | |10uF
-   GPIO34-------------*------------| |----------- line in
-(Default ADC pin)     |           +| |      
-                      |
-                      _
-                     | |10k
-                     |_|
-                      |
-                      |
-                      V GND
-
-Connect hot wire of your audio to Line in and GNd wire of audio cable to common ground (GND)
-
-Second option to measure voltage on trimmer / potentiometer has following connection
-                      ^ +3.3V
-                      |
-                      _
-                     | |
-   GPIO34----------->| |
-(Default ADC pin)    |_|
-                      |
-                      |
-                      _
-                     | | optional resistor
-                     |_|
-                      |
-                      |
-                      V GND
- Optional resistor will decrease read value.
-
- Steps to run:
- 1. Select target board:
-   Tools -> Board -> ESP32 Arduino -> your board
- 2. Upload sketch
-   Press upload button (arrow in top left corner)
-   When you see in console line like this: "Connecting........_____.....__"
-     On your board press and hold Boot button and press EN button shortly. Now you can release both buttons.
-     You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line.
-     If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable.
- 3. Open plotter
-   Tools -> Serial Plotter
-     Enjoy
-
-Created by Tomas Pilny
-on 17th June 2021
-*/
-
-#include <I2S.h>
-
-void setup() {
-  // Open serial communications and wait for port to open:
-  // A baud rate of 115200 is used instead of 9600 for a faster data rate
-  // on non-native USB ports
-  Serial.begin(115200);
-  while (!Serial) {
-    ; // wait for serial port to connect. Needed for native USB port only
-  }
-
-  // start I2S at 8 kHz with 32-bits per sample
-  if (!I2S.begin(ADC_DAC_MODE, 8000, 16)) {
-    Serial.println("Failed to initialize I2S!");
-    while (1); // do nothing
-  }
-}
-
-void loop() {
-  // read a sample
-  int sample = I2S.read();
-  Serial.println(sample);
-}
diff --git a/libraries/I2S/examples/FullDuplex/.skip.esp32h2 b/libraries/I2S/examples/FullDuplex/.skip.esp32h2
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/libraries/I2S/examples/FullDuplex/FullDuplex.ino b/libraries/I2S/examples/FullDuplex/FullDuplex.ino
deleted file mode 100644
index 9b3625fbd60..00000000000
--- a/libraries/I2S/examples/FullDuplex/FullDuplex.ino
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- This example is only for ESP
- This example demonstrates simultaneous usage of microphone and speaker using single I2S module.
- The application transfers data from input to output
-
- Circuit:
- * ESP32
-   * GND connected GND
-   * VIN connected 5V
-   * SCK 5
-   * FS 25
-   * DIN 35
-   * DOUT 26
- * I2S microphone
- * I2S decoder + headphones / speaker
-
- created 8 October 2021
- by Tomas Pilny
- */
-
-#include <I2S.h>
-const long sampleRate = 16000;
-const int bitsPerSample = 32;
-uint8_t *buffer;
-
-void setup() {
-  Serial.begin(115200);
-  //I2S.setAllPins(5, 25, 35, 26); // you can change default pins; order of pins = (CLK, WS, IN, OUT)
-  if(!I2S.setDuplex()){
-    Serial.println("ERROR - could not set duplex");
-    while(true){
-      vTaskDelay(10); // Cannot continue
-    }
-  }
-  if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, bitsPerSample)) {
-    Serial.println("Failed to initialize I2S!");
-    while(true){
-      vTaskDelay(10); // Cannot continue
-    }
-  }
-  buffer = (uint8_t*) malloc(I2S.getBufferSize() * (bitsPerSample / 8));
-  if(buffer == NULL){
-    Serial.println("Failed to allocate buffer!");
-    while(true){
-      vTaskDelay(10); // Cannot continue
-    }
-  }
-  Serial.println("Setup done");
-}
-
-void loop() {
-  //I2S.write(I2S.read()); // primitive implementation sample-by-sample
-
-  // Buffer based implementation
-  I2S.read(buffer, I2S.getBufferSize() * (bitsPerSample / 8));
-  I2S.write(buffer, I2S.getBufferSize() * (bitsPerSample / 8));
-
-  //optimistic_yield(1000); // yield if last yield occurred before <parameter> CPU clock cycles ago
-}
diff --git a/libraries/I2S/examples/InputSerialPlotter/.skip.esp32c3 b/libraries/I2S/examples/InputSerialPlotter/.skip.esp32c3
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/libraries/I2S/examples/InputSerialPlotter/.skip.esp32c6 b/libraries/I2S/examples/InputSerialPlotter/.skip.esp32c6
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/libraries/I2S/examples/InputSerialPlotter/.skip.esp32h2 b/libraries/I2S/examples/InputSerialPlotter/.skip.esp32h2
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino
deleted file mode 100644
index db9f7d0d4f8..00000000000
--- a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- This example reads audio data from an Invensense's ICS43432 I2S microphone
- breakout board, and prints out the samples to the Serial console. The
- Serial Plotter built into the Arduino IDE can be used to plot the audio
- data (Tools -> Serial Plotter)
-
- Circuit:
- * Arduino/Genuino Zero, MKR family and Nano 33 IoT
- * ICS43432:
-   * GND connected GND
-   * 3.3V connected to 3.3V (Zero, Nano, ESP32), VCC (MKR)
-   * WS connected to pin 0 (Zero) or 3 (MKR), A2 (Nano) or 25 (ESP32)
-   * CLK connected to pin 1 (Zero) or 2 (MKR), A3 (Nano) or 5 (ESP32)
-   * SD connected to pin 9 (Zero) or A6 (MKR), 4 (Nano) or 26 (ESP32)
- created 17 November 2016
- by Sandeep Mistry
- */
-
-#include <I2S.h>
-
-void setup() {
-  // Open serial communications and wait for port to open:
-  // A baud rate of 115200 is used instead of 9600 for a faster data rate
-  // on non-native USB ports
-  Serial.begin(115200);
-  while (!Serial) {
-    ; // wait for serial port to connect. Needed for native USB port only
-  }
-
-  // start I2S at 8 kHz with 32-bits per sample
-  if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 32)) {
-    Serial.println("Failed to initialize I2S!");
-    while (1); // do nothing
-  }
-}
-
-void loop() {
-  // read a sample
-  int sample = I2S.read();
-
-  if (sample && sample != -1 && sample != 1) {
-    Serial.println(sample);
-  }
-}
diff --git a/libraries/I2S/examples/SimpleTone/.skip.esp32c3 b/libraries/I2S/examples/SimpleTone/.skip.esp32c3
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/libraries/I2S/examples/SimpleTone/.skip.esp32c6 b/libraries/I2S/examples/SimpleTone/.skip.esp32c6
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/libraries/I2S/examples/SimpleTone/.skip.esp32h2 b/libraries/I2S/examples/SimpleTone/.skip.esp32h2
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino
deleted file mode 100644
index 49cae77238f..00000000000
--- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- This example generates a square wave based tone at a specified frequency
- and sample rate. Then outputs the data using the I2S interface to a
- MAX08357 I2S Amp Breakout board.
-
- I2S Circuit:
- * Arduino/Genuino Zero, MKR family and Nano 33 IoT
- * MAX08357:
-   * GND connected GND
-   * VIN connected 5V
-   * LRC connected to pin 0 (Zero) or 3 (MKR), A2 (Nano) or 25 (ESP32)
-   * BCLK connected to pin 1 (Zero) or 2 (MKR), A3 (Nano) or 5 (ESP32)
-   * DIN connected to pin 9 (Zero) or A6 (MKR), 4 (Nano) or 26 (ESP32)
-
- DAC Circuit:
- * ESP32 or ESP32-S2
- * Audio amplifier
-   - Note:
-     - ESP32 has DAC on GPIO pins 25 and 26.
-     - ESP32-S2 has DAC on GPIO pins 17 and 18.
-  - Connect speaker(s) or headphones.
-
- created 17 November 2016
- by Sandeep Mistry
- For ESP extended
- Tomas Pilny
- 2nd September 2021
- */
-
-#include <I2S.h>
-const int frequency = 440; // frequency of square wave in Hz
-const int amplitude = 500; // amplitude of square wave
-const int sampleRate = 8000; // sample rate in Hz
-const int bps = 16;
-
-const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave
-
-int32_t sample = amplitude; // current sample value
-int count = 0;
-
-i2s_mode_t mode = I2S_PHILIPS_MODE; // I2S decoder is needed
-// i2s_mode_t mode = ADC_DAC_MODE; // Audio amplifier is needed
-
-// Mono channel input
-// This is ESP specific implementation -
-//   samples will be automatically copied to both channels inside I2S driver
-//   If you want to have true mono output use I2S_PHILIPS_MODE and interlay
-//   second channel with 0-value samples.
-//   The order of channels is RIGH followed by LEFT
-//i2s_mode_t mode = I2S_RIGHT_JUSTIFIED_MODE; // I2S decoder is needed
-
-void setup() {
-  Serial.begin(115200);
-  Serial.println("I2S simple tone");
-
-  // start I2S at the sample rate with 16-bits per sample
-  if (!I2S.begin(mode, sampleRate, bps)) {
-    Serial.println("Failed to initialize I2S!");
-    while (1); // do nothing
-  }
-}
-
-void loop() {
-    if (count % halfWavelength == 0 ) {
-      // invert the sample every half wavelength count multiple to generate square wave
-      sample = -1 * sample;
-    }
-
-    if(mode == I2S_PHILIPS_MODE || mode == ADC_DAC_MODE){ // write the same sample twice, once for Right and once for Left channel
-      I2S.write(sample); // Right channel
-      I2S.write(sample); // Left channel
-    }else if(mode == I2S_RIGHT_JUSTIFIED_MODE || mode == I2S_LEFT_JUSTIFIED_MODE){
-      // write the same only once - it will be automatically copied to the other channel
-      I2S.write(sample);
-    }
-
-    // increment the counter for the next sample
-    count++;
-}
diff --git a/libraries/I2S/keywords.txt b/libraries/I2S/keywords.txt
deleted file mode 100644
index ad1b8028d42..00000000000
--- a/libraries/I2S/keywords.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-#######################################
-# Syntax Coloring Map I2S
-#######################################
-
-#######################################
-# Datatypes (KEYWORD1)
-#######################################
-
-I2S	KEYWORD1
-
-#######################################
-# Methods and Functions (KEYWORD2)
-#######################################
-I2SClass	KEYWORD2
-begin	KEYWORD2
-end	KEYWORD2
-
-onReceive	KEYWORD2
-onTransmit	KEYWORD2
-
-setSckPin	KEYWORD2
-setFsPin	KEYWORD2
-setDataInPin	KEYWORD2
-setDataOutPin	KEYWORD2
-setAllPins	KEYWORD2
-
-getSckPin	KEYWORD2
-getFsPin	KEYWORD2
-getDataPin	KEYWORD2
-getDataInPin	KEYWORD2
-getDataOutPin	KEYWORD2
-
-setDuplex	KEYWORD2
-setSimplex	KEYWORD2
-isDuplex	KEYWORD2
-
-setBufferSize	KEYWORD2
-getBufferSize	KEYWORD2
-
-write	KEYWORD2
-availableForWrite	KEYWORD2
-
-read	KEYWORD2
-available	KEYWORD2
-
-gpioToAdcUnit	KEYWORD2
-gpioToAdcChannel	KEYWORD2
-
-#######################################
-# Constants (LITERAL1)
-#######################################
-I2S_PHILIPS_MODE	LITERAL1
-I2S_RIGHT_JUSTIFIED_MODE	LITERAL1
-I2S_LEFT_JUSTIFIED_MODE	LITERAL1
-I2S_ADC_DAC	LITERAL1
-I2S_PDM	LITERAL1
-
-PIN_I2S_SCK	LITERAL1
-PIN_I2S_FS	LITERAL1
-PIN_I2S_SD	LITERAL1
-PIN_I2S_SD_OUT	LITERAL1
diff --git a/libraries/I2S/library.properties b/libraries/I2S/library.properties
deleted file mode 100644
index bb77e306158..00000000000
--- a/libraries/I2S/library.properties
+++ /dev/null
@@ -1,9 +0,0 @@
-name=I2S
-version=1.0
-author=Tomas Pilny
-maintainer=Tomas Pilny <tomas.pilny@espressif.com>
-sentence=Enables the communication with devices that use the Inter-IC Sound (I2S) Bus. Specific implementation for ESP.
-paragraph=
-category=Communication
-url=http://www.arduino.cc/en/Reference/I2S
-architectures=esp32
diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp
deleted file mode 100644
index 7414067eb87..00000000000
--- a/libraries/I2S/src/I2S.cpp
+++ /dev/null
@@ -1,1222 +0,0 @@
-/*
-  Copyright (c) 2016 Arduino LLC.  All right reserved.
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-  See the GNU Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include <Arduino.h>
-#include <wiring_private.h>
-#include "I2S.h"
-#include "freertos/semphr.h"
-
-#define _I2S_EVENT_QUEUE_LENGTH 16
-#define _I2S_DMA_BUFFER_COUNT 2 // BUFFER COUNT must be between 2 and 128
-#define I2S_INTERFACES_COUNT SOC_I2S_NUM
-
-#ifndef I2S_DEVICE
-  #define I2S_DEVICE 0
-#endif
-
-#ifndef I2S_CLOCK_GENERATOR
-  #define I2S_CLOCK_GENERATOR 0 // does nothing for ESP
-#endif
-
-I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) :
-  _deviceIndex(deviceIndex),
-  _sdPin(sdPin),             // shared data pin
-  _inSdPin(PIN_I2S_SD_IN),   // input data pin
-  _outSdPin(PIN_I2S_SD),     // output data pin
-  _sckPin(sckPin),           // clock pin
-  _fsPin(fsPin),             // frame (word) select pin
-
-  _state(I2S_STATE_IDLE),
-  _bitsPerSample(0),
-  _sampleRate(0),
-  _mode(I2S_PHILIPS_MODE),
-
-  _buffer_byte_size(0),
-
-  _driverInstalled(false),
-  _initialized(false),
-  _callbackTaskHandle(NULL),
-  _i2sEventQueue(NULL),
-  _i2s_general_mutex(NULL),
-  _input_ring_buffer(NULL),
-  _output_ring_buffer(NULL),
-  _i2s_dma_buffer_size(128), // Number of frames in each DMA buffer. Frame size = number of channels * Bytes per sample; Must be between 8 and 1024
-  _driveClock(true),
-  _peek_buff(0),
-  _peek_buff_valid(false),
-  _nesting_counter(0),
-
-  _onTransmit(NULL),
-  _onReceive(NULL)
-{
-  _i2s_general_mutex = xSemaphoreCreateMutex();
-  if(_i2s_general_mutex == NULL){
-    log_e("I2S could not create internal mutex!");
-  }
-}
-
-int I2SClass::_createCallbackTask(){
-  int stack_size = 20000;
-  if(_callbackTaskHandle != NULL){
-    log_e("Callback task already exists!");
-    return 0; // ERR
-  }
-
-  xTaskCreate(
-    onDmaTransferComplete,   // Function to implement the task
-    "onDmaTransferComplete", // Name of the task
-    stack_size,              // Stack size in words
-    NULL,                    // Task input parameter
-    2,                       // Priority of the task
-    &_callbackTaskHandle     // Task handle.
-    );
-  if(_callbackTaskHandle == NULL){
-    log_e("Could not create callback task");
-    return 0; // ERR
-  }
-  return 1; // OK
-}
-
-int I2SClass::_installDriver(){
-  if(_driverInstalled){
-    log_e("I2S driver is already installed");
-    return 0; // ERR
-  }
-
-  esp_i2s::i2s_mode_t i2s_mode = (esp_i2s::i2s_mode_t)(esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX);
-
-  if(_driveClock){
-    i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_MASTER);
-  }else{
-    i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE);
-  }
-
-  if(_mode == ADC_DAC_MODE){
-    #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC)
-      if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode
-        log_e("ERROR invalid bps for ADC/DAC. Allowed only 16, requested %d", _bitsPerSample);
-        return 0; // ERR
-      }
-      i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN);
-    #else
-      log_e("This chip does not support ADC / DAC mode");
-      return 0; // ERR
-    #endif
-  }else if(_mode == I2S_PHILIPS_MODE ||
-           _mode == I2S_RIGHT_JUSTIFIED_MODE ||
-           _mode == I2S_LEFT_JUSTIFIED_MODE){ // End of ADC/DAC mode; start of Normal Philips mode
-    if(_bitsPerSample != 8 && _bitsPerSample != 16 && _bitsPerSample != 24 &&  _bitsPerSample != 32){
-        log_e("Invalid bits per sample for normal mode (requested %d)\nAllowed bps = 8 | 16 | 24 | 32", _bitsPerSample);
-      return 0; // ERR
-    }
-    if(_bitsPerSample == 24){
-      log_w("Original Arduino library does not support 24 bits per sample.\nKeep that in mind if you should switch back to Arduino");
-    }
-  }else if(_mode == PDM_STEREO_MODE || _mode == PDM_MONO_MODE){ // end of Normal Philips mode; start of PDM mode
-    #if (SOC_I2S_SUPPORTS_PDM_TX && SOC_I2S_SUPPORTS_PDM_RX)
-      i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_PDM);
-    #else
-      log_e("This chip does not support PDM");
-      return 0; // ERR
-    #endif
-  } // Mode
-  esp_i2s::i2s_config_t i2s_config = {
-    .mode = i2s_mode,
-    .sample_rate = _sampleRate,
-    .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample,
-    .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT,
-    .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S),
-    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
-    .dma_buf_count = _I2S_DMA_BUFFER_COUNT,
-    .dma_buf_len = _i2s_dma_buffer_size,
-    .use_apll = false,
-    #warning The following values are new and need to be checked
-    .tx_desc_auto_clear = true,
-    .fixed_mclk = 0,
-    .mclk_multiple = esp_i2s::I2S_MCLK_MULTIPLE_128,
-    .bits_per_chan = esp_i2s::I2S_BITS_PER_CHAN_DEFAULT
-#if SOC_I2S_SUPPORTS_TDM
-    ,.chan_mask = esp_i2s::I2S_CHANNEL_STEREO,
-    .total_chan = 2,
-    .left_align = false,
-    .big_edin = false,
-    .bit_order_msb = false,
-    .skip_msk = false
-#endif // SOC_I2S_SUPPORTS_TDM
-  };
-
-  if(_driveClock == false){
-    i2s_config.use_apll = true;
-    i2s_config.fixed_mclk = 512*_sampleRate;
-  }
-
-  // Install and start i2s driver
-  while(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){
-    // increase buffer size
-    if(2*_i2s_dma_buffer_size <= 1024){
-      log_w("WARNING i2s driver install failed.\nTrying to increase I2S DMA buffer size from %d to %d\n", _i2s_dma_buffer_size, 2*_i2s_dma_buffer_size);
-      setBufferSize(2*_i2s_dma_buffer_size);
-    }else if(_i2s_dma_buffer_size < 1024){
-      log_w("WARNING i2s driver install failed.\nTrying to decrease I2S DMA buffer size from %d to 1024\n", _i2s_dma_buffer_size);
-      setBufferSize(1024);
-    }else{ // install failed with max buffer size
-      log_e("ERROR i2s driver install failed");
-      return 0; // ERR
-    }
-  } //try installing with increasing size
-
-  if(_mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE || _mode == PDM_MONO_MODE){ // mono/single channel
-    // Set the clock for MONO. Stereo is not supported yet.
-    if(ESP_OK != esp_i2s::i2s_set_clk((esp_i2s::i2s_port_t) _deviceIndex, _sampleRate, (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, esp_i2s::I2S_CHANNEL_MONO)){
-      log_e("Setting the I2S Clock has failed!\n");
-      return 0; // ERR
-    }
-  } // mono channel mode
-
-#if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC)
-  if(_mode == ADC_DAC_MODE){
-    esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN);
-    esp_i2s::adc_unit_t adc_unit;
-    if(!_gpioToAdcUnit((gpio_num_t)_inSdPin, &adc_unit)){
-      log_e("pin to adc unit conversion failed");
-      return 0; // ERR
-    }
-    esp_i2s::adc_channel_t adc_channel;
-    if(!_gpioToAdcChannel((gpio_num_t)_inSdPin, &adc_channel)){
-      log_e("pin to adc channel conversion failed");
-      return 0; // ERR
-    }
-    if(ESP_OK != esp_i2s::i2s_set_adc_mode(adc_unit, (esp_i2s::adc1_channel_t)adc_channel)){
-      log_e("i2s_set_adc_mode failed");
-      return 0; // ERR
-    }
-    if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){
-      log_e("i2s_set_pin failed");
-      return 0; // ERR
-    }
-
-    if(adc_unit == esp_i2s::ADC_UNIT_1){
-      esp_i2s::adc1_config_width(esp_i2s::ADC_WIDTH_BIT_12);
-      esp_i2s::adc1_config_channel_atten((esp_i2s::adc1_channel_t)adc_channel, esp_i2s::ADC_ATTEN_DB_11);
-    }else if(adc_unit == esp_i2s::ADC_UNIT_2){
-      esp_i2s::adc2_config_channel_atten((esp_i2s::adc2_channel_t)adc_channel, esp_i2s::ADC_ATTEN_DB_11);
-    }
-
-    esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex);
-    _driverInstalled = true;
-  }else // End of ADC/DAC mode
-#endif // SOC_I2S_SUPPORTS_ADC_DAC
-  if(_mode == I2S_PHILIPS_MODE || _mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE || _mode == PDM_STEREO_MODE || _mode == PDM_MONO_MODE){ // if I2S mode
-    _driverInstalled = true; // IDF I2S driver must be installed before calling _applyPinSetting
-    if(!_applyPinSetting()){
-      log_e("could not apply pin setting during driver install");
-      _uninstallDriver();
-      return 0; // ERR
-    }
-  } // if I2S _mode
-  return 1; // OK
-}
-
-// Init in MASTER mode: the SCK and FS pins are driven as outputs using the sample rate
-int I2SClass::begin(int mode, int sampleRate, int bitsPerSample){
-  _take_if_not_holding();
-  // master mode (driving clock and frame select pins - output)
-  int ret = begin(mode, sampleRate, bitsPerSample, true);
-  _give_if_top_call();
-  return ret;
-}
-
-// Init in SLAVE mode: the SCK and FS pins are inputs, other side controls sample rate
-int I2SClass::begin(int mode, int bitsPerSample){
-  _take_if_not_holding();
-  // slave mode (not driving clock and frame select pin - input)
-  int ret = begin(mode, 96000, bitsPerSample, false);
-  _give_if_top_call();
-  return ret;
-}
-
-
-// Core function
-int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock){
-  _take_if_not_holding();
-  if(_initialized){
-    log_e("ERROR: Object already initialized! Call I2S.end() to disable");
-    _give_if_top_call();
-    return 0; // ERR
-  }
-  _driveClock = driveClock;
-  _mode = mode;
-  _sampleRate = (uint32_t)sampleRate;
-  _bitsPerSample = bitsPerSample;
-
-  // There is work in progress on this library.
-  if(_bitsPerSample == 16 && _sampleRate > 16000 && driveClock){
-    log_w("This sample rate is not officially supported - audio might be noisy.\nTry using sample rate below or equal to 16000");
-  }
-  if(_bitsPerSample != 16){
-    log_w("This bit-per-sample is not officially supported - audio quality might suffer.\nTry using 16bps, with sample rate below or equal 16000");
-  }
-  if(_mode != I2S_PHILIPS_MODE){
-    log_w("This mode is not officially supported - audio quality might suffer.\nAt the moment the only supported mode is I2S_PHILIPS_MODE");
-  }
-
-  if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) {
-    log_e("Error: unexpected _state (%d)", _state);
-    _give_if_top_call();
-    return 0; // ERR
-  }
-
-  switch (mode) {
-    case I2S_PHILIPS_MODE:
-    case I2S_RIGHT_JUSTIFIED_MODE:
-    case I2S_LEFT_JUSTIFIED_MODE:
-
-    #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC)
-      case ADC_DAC_MODE:
-    #endif
-
-    case PDM_STEREO_MODE:
-    case PDM_MONO_MODE:
-      break;
-
-    default: // invalid mode
-      log_e("ERROR: unknown mode");
-      _give_if_top_call();
-      return 0; // ERR
-  }
-
-  if(!_installDriver()){
-    log_e("ERROR: failed to install driver");
-    end();
-    _give_if_top_call();
-    return 0; // ERR
-  }
-
-  _buffer_byte_size = _i2s_dma_buffer_size * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT * 2;
-  _input_ring_buffer  = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF);
-  _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF);
-  if(_input_ring_buffer == NULL || _output_ring_buffer == NULL){
-    log_e("ERROR: could not create one or both internal buffers. Requested size = %d\n", _buffer_byte_size);
-    _give_if_top_call();
-    return 0; // ERR
-  }
-
-  if(!_createCallbackTask()){
-    log_e("ERROR: failed to create callback task");
-    end();
-    _give_if_top_call();
-    return 0; // ERR
-  }
-  _initialized = true;
-  _give_if_top_call();
-  return 1; // OK
-}
-
-int I2SClass::_applyPinSetting(){
-  if(_driverInstalled){
-    esp_i2s::i2s_pin_config_t pin_config = {
-      .mck_io_num = I2S_PIN_NO_CHANGE,
-      .bck_io_num = _sckPin,
-      .ws_io_num = _fsPin,
-      .data_out_num = I2S_PIN_NO_CHANGE,
-      .data_in_num = I2S_PIN_NO_CHANGE
-    };
-    if (_state == I2S_STATE_DUPLEX){ // duplex
-      pin_config.data_out_num = _outSdPin;
-      pin_config.data_in_num = _inSdPin;
-    }else{ // simplex
-      if(_state == I2S_STATE_RECEIVER){
-        pin_config.data_out_num = I2S_PIN_NO_CHANGE;
-        pin_config.data_in_num = _sdPin;
-      }else if(_state == I2S_STATE_TRANSMITTER){
-        pin_config.data_out_num = _sdPin;
-        pin_config.data_in_num = I2S_PIN_NO_CHANGE;
-      }else{
-        pin_config.data_out_num = I2S_PIN_NO_CHANGE;
-        pin_config.data_in_num = _sdPin;
-      }
-    }
-    if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){
-      log_e("i2s_set_pin failed; attempted settings: SCK=%d; FS=%d; DIN=%d; DOUT=%d", pin_config.bck_io_num, pin_config.ws_io_num, pin_config.data_in_num, pin_config.data_out_num);
-      return 0; // ERR
-    }else{
-      return 1; // OK
-    }
-  } // if(_driverInstalled)
-  return 1; // OK
-}
-
-void I2SClass::_setSckPin(int sckPin){
-  _take_if_not_holding();
-  if(sckPin >= 0){
-    _sckPin = sckPin;
-  }else{
-    _sckPin = PIN_I2S_SCK;
-  }
-  _give_if_top_call();
-}
-
-int I2SClass::setSckPin(int sckPin){
-  _take_if_not_holding();
-  _setSckPin(sckPin);
-  int ret = _applyPinSetting();
-  _applyPinSetting();
-  _give_if_top_call();
-  return ret;
-}
-
-void I2SClass::_setFsPin(int fsPin){
-  if(fsPin >= 0){
-    _fsPin = fsPin;
-  }else{
-    _fsPin = PIN_I2S_FS;
-  }
-}
-
-int I2SClass::setFsPin(int fsPin){
-  _take_if_not_holding();
-  _setFsPin(fsPin);
-  int ret = _applyPinSetting();
-  _give_if_top_call();
-  return ret;
-}
-
-// shared data pin for simplex
-void I2SClass::_setDataPin(int sdPin){
-  if(sdPin >= 0){
-    _sdPin = sdPin;
-  }else{
-    _sdPin = PIN_I2S_SD;
-  }
-}
-
-// shared data pin for simplex
-int I2SClass::setDataPin(int sdPin){
-  _take_if_not_holding();
-  _setDataPin(sdPin);
-  int ret = _applyPinSetting();
-  _give_if_top_call();
-  return ret;
-}
-
-void I2SClass::_setDataInPin(int inSdPin){
-  if(inSdPin >= 0){
-    _inSdPin = inSdPin;
-  }else{
-    _inSdPin = PIN_I2S_SD_IN;
-  }
-}
-
-int I2SClass::setDataInPin(int inSdPin){
-  _take_if_not_holding();
-  _setDataInPin(inSdPin);
-  int ret = _applyPinSetting();
-  _give_if_top_call();
-  return ret;
-}
-
-void I2SClass::_setDataOutPin(int outSdPin){
-  if(outSdPin >= 0){
-    _outSdPin = outSdPin;
-  }else{
-    _outSdPin = PIN_I2S_SD;
-  }
-}
-
-int I2SClass::setDataOutPin(int outSdPin){
-  _take_if_not_holding();
-  _setDataOutPin(outSdPin);
-  int ret = _applyPinSetting();
-  _give_if_top_call();
-  return ret;
-}
-
-int I2SClass::setAllPins(){
-  _take_if_not_holding();
-  int ret = setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SD_IN);
-  _give_if_top_call();
-  return ret;
-}
-
-int I2SClass::setAllPins(int sckPin, int fsPin, int sdPin, int outSdPin, int inSdPin){
-  _take_if_not_holding();
-  _setSckPin(sckPin);
-  _setFsPin(fsPin);
-  _setDataPin(sdPin);
-  _setDataOutPin(outSdPin);
-  _setDataInPin(inSdPin);
-  int ret = _applyPinSetting();
-  _give_if_top_call();
-  return ret;
-}
-
-int I2SClass::setDuplex(){
-  _take_if_not_holding();
-  _state = I2S_STATE_DUPLEX;
-  _give_if_top_call();
-  return 1;
-}
-
-int I2SClass::setSimplex(){
-  _take_if_not_holding();
-  _state = I2S_STATE_IDLE;
-  _give_if_top_call();
-  return 1;
-}
-
-int I2SClass::isDuplex(){
-  _take_if_not_holding();
-  int ret = (int)(_state == I2S_STATE_DUPLEX);
-  _give_if_top_call();
-  return ret;
-}
-
-int I2SClass::getSckPin(){
-  _take_if_not_holding();
-  int ret = _sckPin;
-  _give_if_top_call();
-  return ret;
-}
-
-int I2SClass::getFsPin(){
-  _take_if_not_holding();
-  int ret = _fsPin;
-  _give_if_top_call();
-  return ret;
-}
-
-int I2SClass::getDataPin(){
-  _take_if_not_holding();
-  int ret = _sdPin;
-  _give_if_top_call();
-  return ret;
-}
-
-int I2SClass::getDataInPin(){
-  _take_if_not_holding();
-  int ret = _inSdPin;
-  _give_if_top_call();
-  return ret;
-}
-
-int I2SClass::getDataOutPin(){
-  _take_if_not_holding();
-  int ret = _outSdPin;
-  _give_if_top_call();
-  return ret;
-}
-
-void I2SClass::_uninstallDriver(){
-  if(_driverInstalled){
-    #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC)
-      if(_mode == ADC_DAC_MODE){
-        esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex);
-      }
-    #endif
-    esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex);
-
-    if(_state != I2S_STATE_DUPLEX){
-      _state = I2S_STATE_IDLE;
-    }
-    _driverInstalled = false;
-  } // if(_driverInstalled)
-}
-
-void I2SClass::end(){
-  _take_if_not_holding();
-  if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){
-    if(_callbackTaskHandle){
-      vTaskDelete(_callbackTaskHandle);
-      _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task
-    }
-    _uninstallDriver();
-    _onTransmit = NULL;
-    _onReceive  = NULL;
-    if(_input_ring_buffer != NULL){
-      vRingbufferDelete(_input_ring_buffer);
-      _input_ring_buffer = NULL;
-    }
-    if(_output_ring_buffer != NULL){
-      vRingbufferDelete(_output_ring_buffer);
-      _output_ring_buffer = NULL;
-    }
-    _initialized = false;
-  }else{
-    log_w("WARNING: ending I2SClass from callback task not permitted, but attempted!");
-  }
-  _give_if_top_call();
-}
-
-// Bytes available to read
-int I2SClass::available(){
-  _take_if_not_holding();
-  int ret = 0;
-  if(_input_ring_buffer != NULL){
-    ret = _buffer_byte_size - (int)xRingbufferGetCurFreeSize(_input_ring_buffer);
-  }
-  _give_if_top_call();
-  return ret;
-}
-
-union i2s_sample_t {
-  uint8_t b8;
-  int16_t b16;
-  int32_t b32;
-};
-
-int I2SClass::read(){
-  _take_if_not_holding();
-  i2s_sample_t sample;
-  sample.b32 = 0;
-  if(_initialized){
-    read(&sample, _bitsPerSample / 8);
-
-    if (_bitsPerSample == 32) {
-      _give_if_top_call();
-      return sample.b32;
-    } else if (_bitsPerSample == 16) {
-      _give_if_top_call();
-      return sample.b16;
-    } else if (_bitsPerSample == 8) {
-      _give_if_top_call();
-      return sample.b8;
-    } else {
-      _give_if_top_call();
-      return 0; // sample value
-    }
-  } // if(_initialized)
-  _give_if_top_call();
-  return 0; // sample value
-}
-
-int I2SClass::read(void* buffer, size_t size){
-  _take_if_not_holding();
-  size_t requested_size = size;
-  if(_initialized){
-    if(!_enableReceiver()){
-      _give_if_top_call();
-      return 0; // There was an error switching to receiver
-    } // _enableReceiver succeeded ?
-
-    size_t item_size = 0;
-    void *tmp_buffer;
-    if(_input_ring_buffer != NULL){
-      if(_peek_buff_valid){
-        memcpy(buffer, &_peek_buff, _bitsPerSample/8);
-        _peek_buff_valid = false;
-        requested_size -= _bitsPerSample/8;
-      }
-      tmp_buffer = xRingbufferReceiveUpTo(_input_ring_buffer, &item_size, pdMS_TO_TICKS(1000), requested_size);
-      if(tmp_buffer != NULL){
-        memcpy(buffer, tmp_buffer, item_size);
-        #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC)
-          if(_mode == ADC_DAC_MODE){
-            for(size_t i = 0; i < item_size / 2; ++i){
-              ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF;
-            }
-          } // ADC/DAC mode
-        #endif
-        vRingbufferReturnItem(_input_ring_buffer, tmp_buffer);
-        _give_if_top_call();
-        return item_size;
-      }else{
-        log_w("input buffer is empty - timed out");
-        _give_if_top_call();
-        return 0; // 0 Bytes read / ERR
-      } // tmp buffer not NULL ?
-    } // ring buffer not NULL ?
-  } // if(_initialized)
-  _give_if_top_call();
-  return 0; // 0 Bytes read / ERR
-}
-
-size_t I2SClass::write(uint8_t data){
-  _take_if_not_holding();
-  size_t ret = 0;
-  if(_initialized){
-    ret = write_blocking((int32_t*)&data, 1);
-  }
-  _give_if_top_call();
-  return ret;
-}
-
-size_t I2SClass::write(int32_t sample){
-  _take_if_not_holding();
-  size_t ret = 0;
-  if(_initialized){
-    ret = write_blocking(&sample, _bitsPerSample/8);
-  }
-  _give_if_top_call();
-  return ret;
-}
-
-size_t I2SClass::write(const uint8_t *buffer, size_t size){
-  _take_if_not_holding();
-  size_t ret = 0;
-  if(_initialized){
-    ret = write((const void*)buffer, size);
-  }
-  _give_if_top_call();
-  return ret;
-}
-
-size_t I2SClass::write(const void *buffer, size_t size){
-  _take_if_not_holding();
-  size_t ret = 0;
-  if(_initialized){
-    //size_t ret = write_blocking(buffer, size);
-    ret = write_nonblocking(buffer, size);
-  } // if(_initialized)
-  _give_if_top_call();
-  return ret;
-}
-
-// blocking version of write
-// This version of write will wait indefinitely to write requested samples
-// into output buffer
-size_t I2SClass::write_blocking(const void *buffer, size_t size){
-  _take_if_not_holding();
-  if(_initialized){
-    if(!_enableTransmitter()){
-      _give_if_top_call();
-      return 0; // There was an error switching to transmitter
-    } // _enableTransmitter succeeded ?
-
-    if(_output_ring_buffer != NULL){
-      int ret = xRingbufferSend(_output_ring_buffer, buffer, size, portMAX_DELAY);
-      if(pdTRUE == ret){
-        _give_if_top_call();
-        return size;
-      }else{
-        log_e("xRingbufferSend() with infinite wait returned with error");
-        _give_if_top_call();
-        return 0;
-      } // ring buffer send ok ?
-    } // ring buffer not NULL ?
-  } // if(_initialized)
-  return 0;
-  log_w("I2S not initialized");
-  _give_if_top_call();
-  return 0;
-}
-
-// non-blocking version of write
-// In case there is not enough space in buffer to write requested size
-// this function will try to flush the buffer and write requested data with 0 time-out
-size_t I2SClass::write_nonblocking(const void *buffer, size_t size){
-  _take_if_not_holding();
-  if(_initialized){
-    if(_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){
-      if(!_enableTransmitter()){
-        _give_if_top_call();
-        return 0; // There was an error switching to transmitter
-      }
-    }
-    if(availableForWrite() < size){
-      flush();
-    }
-    if(_output_ring_buffer != NULL){
-      if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 0)){
-        _give_if_top_call();
-        return size;
-      }else{
-        log_w("I2S could not write all data into ring buffer!");
-        _give_if_top_call();
-        return 0;
-      }
-    }
-  } // if(_initialized)
-  return 0;
-  _give_if_top_call(); // this should not be needed
-}
-
-/*
-  Read 1 sample from internal buffer and return it.
-  Repeated peeks will return the same sample until read is called.
-*/
-int I2SClass::peek(){
-  _take_if_not_holding();
-  int ret = 0;
-  if(_initialized && _input_ring_buffer != NULL && !_peek_buff_valid){
-    size_t item_size = 0;
-    void *item = NULL;
-
-    item = xRingbufferReceiveUpTo(_input_ring_buffer, &item_size, 0, _bitsPerSample/8); // fetch 1 sample
-    if (item != NULL && item_size == _bitsPerSample/8){
-      _peek_buff = *((int*)item);
-      vRingbufferReturnItem(_input_ring_buffer, item);
-      _peek_buff_valid = true;
-    }
-
-  } // if(_initialized)
-  if(_peek_buff_valid){
-    ret = _peek_buff;
-  }
-  _give_if_top_call();
-  return ret;
-}
-
-void I2SClass::flush(){
-  _take_if_not_holding();
-  if(_initialized){
-    const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8)*2;
-    size_t item_size = 0;
-    void *item = NULL;
-    if(_output_ring_buffer != NULL){
-      item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, 0, single_dma_buf);
-      if (item != NULL){
-        _fix_and_write(item, item_size);
-        vRingbufferReturnItem(_output_ring_buffer, item);
-      }
-    }
-  } // if(_initialized)
-  _give_if_top_call();
-}
-
-// Bytes available to write
-int I2SClass::availableForWrite(){
-  _take_if_not_holding();
-  int ret = 0;
-  if(_initialized){
-    if(_output_ring_buffer != NULL){
-      ret = (int)xRingbufferGetCurFreeSize(_output_ring_buffer);
-    }
-  } // if(_initialized)
-  _give_if_top_call();
-  return ret;
-}
-
-void I2SClass::onTransmit(void(*function)(void)){
-  _take_if_not_holding();
-  _onTransmit = function;
-  _give_if_top_call();
-}
-
-void I2SClass::onReceive(void(*function)(void)){
-  _take_if_not_holding();
-  _onReceive = function;
-  _give_if_top_call();
-}
-
-int I2SClass::setBufferSize(int bufferSize){
-  _take_if_not_holding();
-  int ret = 0;
-  if(bufferSize >= 8 && bufferSize <= 1024){
-    _i2s_dma_buffer_size = bufferSize;
-  }else{
-    log_e("setBufferSize: wrong input! Buffer size must be between 8 and 1024. Requested %d", bufferSize);
-    _give_if_top_call();
-    return 0; // ERR
-  } // check requested buffer size
-
-  if(_initialized){
-    _uninstallDriver();
-    ret = _installDriver();
-    _give_if_top_call();
-    return ret;
-  }else{ // check requested buffer size
-    _give_if_top_call();
-    return 1; // It's ok to change buffer size for uninitialized driver - new size will be used on begin()
-  } // if(_initialized)
-  _give_if_top_call();
-  return 0; // ERR
-}
-
-int I2SClass::getBufferSize(){
-  _take_if_not_holding();
-  int ret = _i2s_dma_buffer_size;
-  _give_if_top_call();
-  return ret;
-}
-
-int I2SClass::_enableTransmitter(){
-  if(_state != I2S_STATE_DUPLEX && _state != I2S_STATE_TRANSMITTER){
-    _state = I2S_STATE_TRANSMITTER;
-    return _applyPinSetting();
-  }
-  return 1; // Ok
-}
-
-int I2SClass::_enableReceiver(){
-  if(_state != I2S_STATE_DUPLEX && _state != I2S_STATE_RECEIVER){
-    _state = I2S_STATE_RECEIVER;
-    return _applyPinSetting();
-  }
-  return 1; // Ok
-}
-
-void I2SClass::_tx_done_routine(uint8_t* prev_item){
-  static bool prev_item_valid = false;
-  const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8)*2; // *2 for stereo - it has double number of samples for 2 channels
-  static size_t item_size = 0;
-  static size_t prev_item_size = 0;
-  static void *item = NULL;
-  static int prev_item_offset = 0;
-  static size_t bytes_written = 0;
-
-  if(prev_item_valid){ // use item from previous round
-    _fix_and_write(prev_item+prev_item_offset, prev_item_size, &bytes_written);
-    if(prev_item_size == bytes_written){
-      prev_item_valid = false;
-    } // write size check
-    prev_item_offset = bytes_written;
-    prev_item_size -= bytes_written;
-  } // prev_item_valid
-
-  if(_output_ring_buffer != NULL && (_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf)){ // fill up the I2S DMA buffer
-    bytes_written = 0;
-    item_size = 0;
-    if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= _i2s_dma_buffer_size*(_bitsPerSample/8)){ // don't read from almost empty buffer
-      item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(0), single_dma_buf);
-      if (item != NULL){
-        _fix_and_write(item, item_size, &bytes_written);
-        if(item_size != bytes_written){ // save item that was not written correctly for later
-          memcpy(prev_item, (void*)&((uint8_t*)item)[bytes_written], item_size-bytes_written);
-          prev_item_size = item_size - bytes_written;
-          prev_item_offset = 0;
-          prev_item_valid = true;
-        } // save item that was not written correctly for later
-        vRingbufferReturnItem(_output_ring_buffer, item);
-      } // Check received item
-    } // don't read from almost empty buffer
-  } // fill up the I2S DMA buffer
-  if(_onTransmit){
-    _onTransmit();
-  } // user callback
-}
-
-void I2SClass::_rx_done_routine(){
-  size_t bytes_read = 0;
-  const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8);
-
-  if(_input_ring_buffer != NULL){
-    uint8_t *_inputBuffer = (uint8_t*)malloc(_i2s_dma_buffer_size*4);
-    size_t avail = xRingbufferGetCurFreeSize(_input_ring_buffer);
-    if(avail > 0){
-      esp_err_t ret = esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0);
-      if(ret != ESP_OK){
-        log_w("i2s_read returned with error %d", ret);
-      }
-      _post_read_data_fix(_inputBuffer, &bytes_read);
-    }
-
-    if(bytes_read > 0){ // when read more than 0, then send to ring buffer
-      if(pdTRUE != xRingbufferSend(_input_ring_buffer, _inputBuffer, bytes_read, 0)){
-        log_w("I2S failed to send item from DMA to internal buffer\n");
-      } // xRingbufferSendComplete
-    } // if(bytes_read > 0)
-    free(_inputBuffer);
-    if (_onReceive && avail < _buffer_byte_size){ // when user callback is registered && and there is some data in ring buffer to read
-      _onReceive();
-    } // user callback
-  }
-}
-
-void I2SClass::_onTransferComplete(){
-  uint8_t prev_item[_i2s_dma_buffer_size*4];
-  esp_i2s::i2s_event_t i2s_event;
-
-  while(true){
-    xQueueReceive(_i2sEventQueue, &i2s_event, portMAX_DELAY);
-    if(i2s_event.type == esp_i2s::I2S_EVENT_TX_DONE){
-      _tx_done_routine(prev_item);
-    }else if(i2s_event.type == esp_i2s::I2S_EVENT_RX_DONE){
-      _rx_done_routine();
-    } // RX Done
-  } // infinite loop
-}
-
-void I2SClass::onDmaTransferComplete(void*){
-  I2S._onTransferComplete();
-}
-
-void I2SClass::_take_if_not_holding(){
-  TaskHandle_t mutex_holder = xSemaphoreGetMutexHolder(_i2s_general_mutex);
-  if(mutex_holder != NULL && mutex_holder == xTaskGetCurrentTaskHandle()){
-    ++_nesting_counter;
-    return; // we are already holding this mutex - no need to take it
-  }
-
-  // we are not holding the mutex - wait for it and take it
-  if(xSemaphoreTake(_i2s_general_mutex, portMAX_DELAY) != pdTRUE ){
-    log_e("I2S internal mutex take returned with error");
-  }
-  //_give_if_top_call(); // call after this function
-}
-
-void I2SClass::_give_if_top_call(){
-  if(_nesting_counter){
-    --_nesting_counter;
-  }else{
-    if(xSemaphoreGive(_i2s_general_mutex) != pdTRUE){
-      log_e("I2S internal mutex give error");
-    }
-  }
-}
-
-
-// Fixes data in-situ received from esp i2s driver. After fixing they reflect what was on the bus.
-// input - bytes as received from i2s_read - this serves as input and output buffer
-// size - number of bytes (this may be changed during operation)
-void I2SClass::_post_read_data_fix(void *input, size_t *size){
-  ulong dst_ptr = 0;
-  switch(_bitsPerSample){
-    case 8:
-      for(int i = 0; i < *size; i+=4){
-        ((uint8_t*)input)[dst_ptr++] = ((uint8_t*)input)[i+3];
-        ((uint8_t*)input)[dst_ptr++] = ((uint8_t*)input)[i+1];
-      }
-      *size /= 2;
-    break;
-    case 16:
-      uint16_t tmp;
-      for(int i = 0; i < *size/2; i+=2){
-        tmp = ((uint16_t*)input)[i];
-        ((uint16_t*)input)[dst_ptr++] = ((uint16_t*)input)[i+1];
-        ((uint16_t*)input)[dst_ptr++] = tmp;
-      }
-      break;
-      default: ; // Do nothing
-  } // switch
-}
-
-// Prepares data and writes them to IDF i2s driver.
-// This counters possible bug in ESP IDF I2S driver
-// output - bytes to be sent
-// size - number of bytes in original buffer
-// bytes_written - number of bytes used from original buffer
-// actual_bytes_written - number of bytes written by i2s_write after fix
-void I2SClass::_fix_and_write(void *output, size_t size, size_t *bytes_written, size_t *actual_bytes_written){
-  ulong src_ptr = 0;
-  uint8_t* buff = NULL;
-  size_t buff_size = size;
-  switch(_bitsPerSample){
-    case 8:
-      buff_size = size *2;
-      buff = (uint8_t*)calloc(buff_size, sizeof(uint8_t));
-      if(buff == NULL){
-        log_e("callock error");
-        if(bytes_written != NULL){ *bytes_written = 0; }
-        return;
-      }
-      for(int i = 0; i < buff_size ; i+=4){
-        ((uint8_t*)buff)[i+3] = (uint16_t)((uint8_t*)output)[src_ptr++];
-        ((uint8_t*)buff)[i+1] = (uint16_t)((uint8_t*)output)[src_ptr++];
-      }
-    break;
-    case 16:
-      buff = (uint8_t*)malloc(buff_size);
-      if(buff == NULL){
-        log_e("malloc error");
-        if(bytes_written != NULL){ *bytes_written = 0; }
-        return;
-      }
-      for(int i = 0; i < size/2; i += 2 ){
-        ((uint16_t*)buff)[i]   = ((uint16_t*)output)[i+1]; // [1] <- [0]
-        ((uint16_t*)buff)[i+1] = ((uint16_t*)output)[i]; // [0] <- [1]
-      }
-    break;
-    case 24:
-      buff = (uint8_t*)output;
-      break;
-    case 32:
-      buff = (uint8_t*)output;
-      break;
-    default: ; // Do nothing
-  } // switch
-
-  size_t _bytes_written;
-  esp_err_t ret = esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buff, buff_size, &_bytes_written, 0); // fixed
-  if(ret != ESP_OK){
-    log_e("Error: writing data to i2s - function returned with err code %d", ret);
-  }
-  if(ret == ESP_OK && buff_size != _bytes_written){
-    log_w("Warning: writing data to i2s - written %d B instead of requested %d B", _bytes_written, buff_size);
-  }
-  // free if the buffer was actually allocated
-  if(_bitsPerSample == 8 || _bitsPerSample == 16){
-    free(buff);
-  }
-  if(bytes_written != NULL){
-    *bytes_written = _bitsPerSample == 8 ? _bytes_written/2 : _bytes_written;
-  }
-  if(actual_bytes_written != NULL){
-    *actual_bytes_written = _bytes_written;
-  }
-}
-
-
-#if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC)
-int I2SClass::_gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){
-  switch(gpio_num){
-#if CONFIG_IDF_TARGET_ESP32
-    // ADC 1
-    case GPIO_NUM_36:
-    case GPIO_NUM_37:
-    case GPIO_NUM_38:
-    case GPIO_NUM_39:
-    case GPIO_NUM_32:
-    case GPIO_NUM_33:
-    case GPIO_NUM_34:
-    case GPIO_NUM_35:
-      *adc_unit = esp_i2s::ADC_UNIT_1;
-      return 1; // OK
-
-    // ADC 2
-    case GPIO_NUM_0:
-      log_w("GPIO 0 for ADC should not be used for dev boards due to external auto program circuits.");
-    case GPIO_NUM_4:
-    case GPIO_NUM_2:
-    case GPIO_NUM_15:
-    case GPIO_NUM_13:
-    case GPIO_NUM_12:
-    case GPIO_NUM_14:
-    case GPIO_NUM_27:
-    case GPIO_NUM_25:
-    case GPIO_NUM_26:
-      *adc_unit = esp_i2s::ADC_UNIT_2;
-      return 1; // OK
-#endif
-
-#if (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3)
-    case GPIO_NUM_1:
-    case GPIO_NUM_2:
-    case GPIO_NUM_3:
-    case GPIO_NUM_4:
-    case GPIO_NUM_5:
-    case GPIO_NUM_6:
-    case GPIO_NUM_7:
-    case GPIO_NUM_8:
-    case GPIO_NUM_9:
-    case GPIO_NUM_10:
-      *adc_unit = esp_i2s::ADC_UNIT_1;
-      return 1; // OK
-#endif
-
-#if CONFIG_IDF_TARGET_ESP32S2
-    case GPIO_NUM_11:
-    case GPIO_NUM_12:
-    case GPIO_NUM_13:
-    case GPIO_NUM_14:
-    case GPIO_NUM_15:
-    case GPIO_NUM_16:
-    case GPIO_NUM_17:
-    case GPIO_NUM_18:
-    case GPIO_NUM_19:
-    case GPIO_NUM_20:
-      *adc_unit = esp_i2s::ADC_UNIT_2;
-      return 1; // OK
-#endif
-
-#if (CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2)
-    case GPIO_NUM_0:
-    case GPIO_NUM_1:
-    case GPIO_NUM_2:
-    case GPIO_NUM_3:
-    case GPIO_NUM_4:
-      *adc_unit = esp_i2s::ADC_UNIT_1;
-      return 1; // OK
-    case GPIO_NUM_5:
-      *adc_unit = esp_i2s::ADC_UNIT_2;
-      return 1; // OK
-#endif
-    default:
-      log_e("GPIO %d not usable for ADC!", gpio_num);
-      log_i("Please refer to documentation https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html");
-      return 0; // ERR
-  }
-}
-
-int I2SClass::_gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel){
- switch(gpio_num){
-#if CONFIG_IDF_TARGET_ESP32
-    // ADC 1
-    case GPIO_NUM_36: *adc_channel =  esp_i2s::ADC_CHANNEL_0; return 1; // OK
-    case GPIO_NUM_37: *adc_channel =  esp_i2s::ADC_CHANNEL_1; return 1; // OK
-    case GPIO_NUM_38: *adc_channel =  esp_i2s::ADC_CHANNEL_2; return 1; // OK
-    case GPIO_NUM_39: *adc_channel =  esp_i2s::ADC_CHANNEL_3; return 1; // OK
-    case GPIO_NUM_32: *adc_channel =  esp_i2s::ADC_CHANNEL_4; return 1; // OK
-    case GPIO_NUM_33: *adc_channel =  esp_i2s::ADC_CHANNEL_5; return 1; // OK
-    case GPIO_NUM_34: *adc_channel =  esp_i2s::ADC_CHANNEL_6; return 1; // OK
-    case GPIO_NUM_35: *adc_channel =  esp_i2s::ADC_CHANNEL_7; return 1; // OK
-
-    // ADC 2
-    case GPIO_NUM_0:
-      log_w("GPIO 0 for ADC should not be used for dev boards due to external auto program circuits.");
-      *adc_channel =  esp_i2s::ADC_CHANNEL_1; return 1; // OK
-    case GPIO_NUM_4:  *adc_channel =  esp_i2s::ADC_CHANNEL_0; return 1; // OK
-    case GPIO_NUM_2:  *adc_channel =  esp_i2s::ADC_CHANNEL_2; return 1; // OK
-    case GPIO_NUM_15: *adc_channel =  esp_i2s::ADC_CHANNEL_3; return 1; // OK
-    case GPIO_NUM_13: *adc_channel =  esp_i2s::ADC_CHANNEL_4; return 1; // OK
-    case GPIO_NUM_12: *adc_channel =  esp_i2s::ADC_CHANNEL_5; return 1; // OK
-    case GPIO_NUM_14: *adc_channel =  esp_i2s::ADC_CHANNEL_6; return 1; // OK
-    case GPIO_NUM_27: *adc_channel =  esp_i2s::ADC_CHANNEL_7; return 1; // OK
-    case GPIO_NUM_25: *adc_channel =  esp_i2s::ADC_CHANNEL_8; return 1; // OK
-    case GPIO_NUM_26: *adc_channel =  esp_i2s::ADC_CHANNEL_9; return 1; // OK
-#endif
-
-#if (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3)
-    case GPIO_NUM_1: *adc_channel =  esp_i2s::ADC_CHANNEL_0; return 1; // OK
-    case GPIO_NUM_2: *adc_channel =  esp_i2s::ADC_CHANNEL_1; return 1; // OK
-    case GPIO_NUM_3: *adc_channel =  esp_i2s::ADC_CHANNEL_2; return 1; // OK
-    case GPIO_NUM_4: *adc_channel =  esp_i2s::ADC_CHANNEL_3; return 1; // OK
-    case GPIO_NUM_5: *adc_channel =  esp_i2s::ADC_CHANNEL_4; return 1; // OK
-    case GPIO_NUM_6: *adc_channel =  esp_i2s::ADC_CHANNEL_5; return 1; // OK
-    case GPIO_NUM_7: *adc_channel =  esp_i2s::ADC_CHANNEL_6; return 1; // OK
-    case GPIO_NUM_8: *adc_channel =  esp_i2s::ADC_CHANNEL_7; return 1; // OK
-    case GPIO_NUM_9: *adc_channel =  esp_i2s::ADC_CHANNEL_8; return 1; // OK
-    case GPIO_NUM_10: *adc_channel =  esp_i2s::ADC_CHANNEL_9; return 1; // OK
-#endif
-
-#if CONFIG_IDF_TARGET_ESP32S2
-    case GPIO_NUM_11: *adc_channel =  esp_i2s::ADC_CHANNEL_0; return 1; // OK
-    case GPIO_NUM_12: *adc_channel =  esp_i2s::ADC_CHANNEL_1; return 1; // OK
-    case GPIO_NUM_13: *adc_channel =  esp_i2s::ADC_CHANNEL_2; return 1; // OK
-    case GPIO_NUM_14: *adc_channel =  esp_i2s::ADC_CHANNEL_3; return 1; // OK
-    case GPIO_NUM_15: *adc_channel =  esp_i2s::ADC_CHANNEL_4; return 1; // OK
-    case GPIO_NUM_16: *adc_channel =  esp_i2s::ADC_CHANNEL_5; return 1; // OK
-    case GPIO_NUM_17: *adc_channel =  esp_i2s::ADC_CHANNEL_6; return 1; // OK
-    case GPIO_NUM_18: *adc_channel =  esp_i2s::ADC_CHANNEL_7; return 1; // OK
-    case GPIO_NUM_19: *adc_channel =  esp_i2s::ADC_CHANNEL_8; return 1; // OK
-    case GPIO_NUM_20: *adc_channel =  esp_i2s::ADC_CHANNEL_9; return 1; // OK
-#endif
-
-#if (CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2)
-    case GPIO_NUM_0: *adc_channel =  esp_i2s::ADC_CHANNEL_0; return 1; // OK
-    case GPIO_NUM_1: *adc_channel =  esp_i2s::ADC_CHANNEL_1; return 1; // OK
-    case GPIO_NUM_2: *adc_channel =  esp_i2s::ADC_CHANNEL_2; return 1; // OK
-    case GPIO_NUM_3: *adc_channel =  esp_i2s::ADC_CHANNEL_3; return 1; // OK
-    case GPIO_NUM_4: *adc_channel =  esp_i2s::ADC_CHANNEL_4; return 1; // OK
-    case GPIO_NUM_5: *adc_channel =  esp_i2s::ADC_CHANNEL_0; return 1; // OK
-#endif
-    default:
-      log_e("GPIO %d not usable for ADC!", gpio_num);
-      log_i("Please refer to documentation https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html");
-      return 0; // ERR
-  }
-}
-#endif // SOC_I2S_SUPPORTS_ADC_DAC
-
-#if I2S_INTERFACES_COUNT > 0
-  I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex
-#endif
-
-#if I2S_INTERFACES_COUNT > 1
-  // TODO set default pins for second module
-  //I2SClass I2S1(I2S_DEVICE+1, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex
-#endif
diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h
deleted file mode 100644
index 623fa8917b4..00000000000
--- a/libraries/I2S/src/I2S.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
-  Copyright (c) 2016 Arduino LLC.  All right reserved.
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-  See the GNU Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef _I2S_H_INCLUDED
-#define _I2S_H_INCLUDED
-
-#include <Arduino.h>
-#include "freertos/ringbuf.h"
-
-namespace esp_i2s {
-  #include "driver/i2s.h" // ESP specific i2s driver
-}
-
-// Default pins
-#ifndef PIN_I2S_SCK
-  #define PIN_I2S_SCK 14
-#endif
-
-#ifndef PIN_I2S_FS
-  #if CONFIG_IDF_TARGET_ESP32S2
-    #define PIN_I2S_FS 27
-  #else
-    #define PIN_I2S_FS 25
-  #endif
-#endif
-
-#ifndef PIN_I2S_SD
-  #define PIN_I2S_SD 26
-#endif
-
-#ifndef PIN_I2S_SD_OUT
-  #define PIN_I2S_SD_OUT 26
-#endif
-
-#ifndef PIN_I2S_SD_IN
-  #define PIN_I2S_SD_IN 35 // Pin 35 is only input!
-#endif
-
-typedef enum {
-  I2S_PHILIPS_MODE,
-  I2S_RIGHT_JUSTIFIED_MODE,
-  I2S_LEFT_JUSTIFIED_MODE,
-  ADC_DAC_MODE,
-  PDM_STEREO_MODE,
-  PDM_MONO_MODE
-} i2s_mode_t;
-
-class I2SClass : public Stream
-{
-public:
-  // The device index and pins must map to the "COM" pads in Table 6-1 of the datasheet
-  I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin);
-
-  // Init in MASTER mode: the SCK and FS pins are driven as outputs using the sample rate
-  int begin(int mode, int sampleRate, int bitsPerSample);
-
-  // Init in SLAVE mode: the SCK and FS pins are inputs, other side controls sample rate
-  int begin(int mode, int bitsPerSample);
-
-  // change pin setup and mode (default is Half Duplex)
-  // Can be called only on initialized object (after begin)
-  int setSckPin(int sckPin);
-  int setFsPin(int fsPin);
-  int setDataPin(int sdPin); // shared data pin for simplex
-  int setDataOutPin(int outSdPin);
-  int setDataInPin(int inSdPin);
-
-  int setAllPins();
-  int setAllPins(int sckPin, int fsPin, int sdPin, int outSdPin, int inSdPin);
-
-  int getSckPin();
-  int getFsPin();
-  int getDataPin();
-  int getDataOutPin();
-  int getDataInPin();
-
-  int setDuplex();
-  int setSimplex();
-  int isDuplex();
-
-  void end();
-
-  // from Stream
-  virtual int available();
-  virtual int read();
-  virtual int peek();
-  virtual void flush();
-
-  // from Print
-  virtual size_t write(uint8_t);
-  virtual size_t write(const uint8_t *buffer, size_t size);
-
-  virtual int availableForWrite();
-
-  int read(void* buffer, size_t size);
-
-  //size_t write(int);
-  size_t write(int32_t);
-  size_t write(const void *buffer, size_t size);
-  size_t write_blocking(const void *buffer, size_t size);
-  size_t write_nonblocking(const void *buffer, size_t size);
-
-  void onTransmit(void(*)(void));
-  void onReceive(void(*)(void));
-
-  int setBufferSize(int bufferSize);
-  int getBufferSize();
-private:
-  #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC)
-    int _gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit);
-    int _gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel);
-  #endif
-  int begin(int mode, int sampleRate, int bitsPerSample, bool driveClock);
-
-  int _enableTransmitter();
-  int _enableReceiver();
-  void _onTransferComplete();
-
-  int _createCallbackTask();
-
-  static void onDmaTransferComplete(void*);
-  int _installDriver();
-  void _uninstallDriver();
-  void _setSckPin(int sckPin);
-  void _setFsPin(int fsPin);
-  void _setDataPin(int sdPin);
-  void _setDataOutPin(int outSdPin);
-  void _setDataInPin(int inSdPin);
-  int  _applyPinSetting();
-
-private:
-  typedef enum {
-    I2S_STATE_IDLE,
-    I2S_STATE_TRANSMITTER,
-    I2S_STATE_RECEIVER,
-    I2S_STATE_DUPLEX
-  } i2s_state_t;
-
-  int _deviceIndex;
-  int _sdPin;
-  int _inSdPin;
-  int _outSdPin;
-  int _sckPin;
-  int _fsPin;
-
-  i2s_state_t _state;
-  int _bitsPerSample;
-  uint32_t _sampleRate;
-  int _mode;
-
-  uint16_t _buffer_byte_size;
-
-  bool _driverInstalled; // Is IDF I2S driver installed?
-  bool _initialized; // Is everything initialized (callback task, I2S driver, ring buffers)?
-  TaskHandle_t _callbackTaskHandle;
-  QueueHandle_t _i2sEventQueue;
-  SemaphoreHandle_t _i2s_general_mutex;
-  RingbufHandle_t _input_ring_buffer;
-  RingbufHandle_t _output_ring_buffer;
-  int _i2s_dma_buffer_size;
-  bool _driveClock;
-  uint32_t _peek_buff;
-  bool _peek_buff_valid;
-
-  void _tx_done_routine(uint8_t* prev_item);
-  void _rx_done_routine();
-
-  uint16_t _nesting_counter;
-  void _take_if_not_holding();
-  void _give_if_top_call();
-  void _post_read_data_fix(void *input, size_t *size);
-  void _fix_and_write(void *output, size_t size, size_t *bytes_written = NULL, size_t *actual_bytes_written = NULL);
-
-  void (*_onTransmit)(void);
-  void (*_onReceive)(void);
-};
-
-extern I2SClass I2S;
-
-#endif
diff --git a/variants/lilygo_t_display/pins_arduino.h.txt b/variants/lilygo_t_display/pins_arduino.h
similarity index 98%
rename from variants/lilygo_t_display/pins_arduino.h.txt
rename to variants/lilygo_t_display/pins_arduino.h
index e911f09b86c..87edf8ccd32 100644
--- a/variants/lilygo_t_display/pins_arduino.h.txt
+++ b/variants/lilygo_t_display/pins_arduino.h
@@ -64,4 +64,4 @@ static const uint8_t VBAT = 34;
 static const uint8_t RIGHT_BUTTON = 35;
 static const uint8_t LEFT_BUTTON = 0;
 
-#endif /* Pins_Arduino_h */
\ No newline at end of file
+#endif /* Pins_Arduino_h */