diff --git a/extras/multiple_versions.md b/extras/multiple_versions.md new file mode 100644 index 0000000..198f124 --- /dev/null +++ b/extras/multiple_versions.md @@ -0,0 +1,99 @@ +# Using Multiple ESP32 Board Versions in Arduino IDE (Windows) + +Tested with: **Arduino IDE 2.3.5**\ +Target: Use multiple versions of **Espressif's ESP32 Arduino core** side-by-side in the Arduino IDE. + +--- + +## ⚒️ Step-by-step Instructions + +### 1. 📂 Prepare Your Folder Structure + +Create a `hardware` folder inside your Arduino sketchbook: + +```text +C:\Users\%USERNAME%\Documents\Arduino\hardware +``` + +Inside that, create a subfolder for your desired version: + +```text +C:\Users\%USERNAME%\Documents\Arduino\hardware\espressif_2.0.17 +``` + +Clone the ESP32 Arduino core into the `esp32` subfolder: + +```bash +cd %USERPROFILE%\Documents\Arduino\hardware\espressif_2.0.17 + +git clone https://github.com/espressif/arduino-esp32.git esp32 + +cd esp32 + +git checkout tags/2.0.17 +``` + +This will download version 2.0.17 into the path: + +```text +...\Arduino\hardware\espressif_2.0.17\esp32 +``` + +### 2. 🔄 Restart Arduino IDE + +After restarting the Arduino IDE, you will now see both board versions: + +- The default one from Boards Manager +- Your custom version from the Sketchbook + +Look for entries like: + +- `ESP32 Dev Module` +- `ESP32 Dev Module (in Sketchbook) (espressif_2.0.17)` + +![grafik](https://github.com/user-attachments/assets/5fa68133-cdfc-461a-acec-9a76942a4a9d) + + +--- + +### 3. ⚠️ Compatibility with Arduino IDE < 2.x + +Older IDE versions may **not display version labels** in the board selection menu. + +To make your custom version distinguishable: + +1. Edit this file: + + ```text + ...\Arduino\hardware\espressif_2.0.17\esp32\platform.txt + ``` + +2. Add or modify the `name` field at the top: + + ```ini + name=ESP32 Arduino (in Sketchbook) (espressif_2.0.17) + ``` + +This ensures proper identification in older IDEs. + +--- + +## ✅ Benefits + +- Use different versions of ESP32 platform for different projects +- Test compatibility with older/newer SDKs +- Avoid breaking changes from updates + +--- + +## 💻 Optional: One-liner Script for Setup + +```bash +cd %USERPROFILE%\Documents\Arduino\hardware + +mkdir espressif_2.0.17 && cd espressif_2.0.17 + +git clone https://github.com/espressif/arduino-esp32.git esp32 + +cd esp32 && git checkout tags/2.0.17 +``` diff --git a/src/audio_module.h b/src/audio_module.h index 02d9b32..f4932a1 100644 --- a/src/audio_module.h +++ b/src/audio_module.h @@ -197,6 +197,12 @@ void Audio_Setup(void) #endif #endif +#if defined(DUAL_CODEC_ENABLED) && defined(ES8388_ENABLED) + ES8388_SelectCodec(1); + ES8388_Setup(); + ES8388_SetIn2OoutVOL(0, 0); + ES8388_SelectCodec(0); +#endif #ifdef WM8978_ENABLED WM8978_Setup(); diff --git a/src/es8388.h b/src/es8388.h index a1bbd3b..e4f8ca4 100644 --- a/src/es8388.h +++ b/src/es8388.h @@ -57,7 +57,9 @@ void ES8388_SetOUT1VOL(float vol); void ES8388_SetOUT1VOL(uint8_t unused, float vol); void ES8388_SetOUT2VOL(float vol); void ES8388_SetOUT2VOL(uint8_t unused, float vol); - +#ifdef DUAL_CODEC_ENABLED +void ES8388_SelectCodec(bool select); +#endif #endif // ES8388_ENABLED #endif // #ifdef ML_SYNTH_INLINE_DECLARATION @@ -75,6 +77,7 @@ void ES8388_SetOUT2VOL(uint8_t unused, float vol); /* ES8388 address */ //#define ES8388_ADDR 0x20 /*!< 0x22:CE=1;0x20:CE=0*/ #define ES8388_ADDR 0x10 /*!< 0x22:CE=1;0x20:CE=0*/ +#define ES8388_ADDR_SECONDARY 0x11 /*!< 0x22:CE=1;0x20:CE=0*/ /* ES8388 register */ @@ -139,15 +142,35 @@ void ES8388_SetOUT2VOL(uint8_t unused, float vol); #define ES8388_DACCONTROL29 0x33 #define ES8388_DACCONTROL30 0x34 +bool selected_codec_id = 0; +uint16_t codecAddress = ES8388_ADDR; + +void ES8388_SelectCodec(bool select) +{ + if (selected_codec_id != select) + { + if (select) + { + codecAddress = ES8388_ADDR_SECONDARY; + selected_codec_id = 1; + } + else + { + codecAddress = ES8388_ADDR; + selected_codec_id = 0; + } + } + return; +} uint8_t ES8388_ReadReg(uint8_t reg) { - Wire.beginTransmission(ES8388_ADDR); + Wire.beginTransmission(codecAddress); Wire.write(reg); Wire.endTransmission(false); uint8_t val = 0u; - if (1 == Wire.requestFrom(uint16_t(ES8388_ADDR), uint8_t(1), true)) + if (1 == Wire.requestFrom(uint16_t(codecAddress), uint8_t(1), true)) { val = Wire.read(); } @@ -157,7 +180,7 @@ uint8_t ES8388_ReadReg(uint8_t reg) bool ES8388_WriteReg(uint8_t reg, uint8_t val) { - Wire.beginTransmission(ES8388_ADDR); + Wire.beginTransmission(codecAddress); Wire.write(reg); Wire.write(val); return 0 == Wire.endTransmission(true); diff --git a/src/i2s_interface.h b/src/i2s_interface.h index e5f19eb..a17285e 100644 --- a/src/i2s_interface.h +++ b/src/i2s_interface.h @@ -52,8 +52,12 @@ void setup_i2s(); bool i2s_write_stereo_samples_i16(const int16_t *fl_sample, const int16_t *fr_sample, const int buffLen); bool i2s_write_stereo_samples_buff(const float *fl_sample, const float *fr_sample, const int buffLen); +bool i2s_write_stereo_samples_buff_dual(const float *fl_sample, const float *fr_sample, const int buffLen, uint8_t codec_num); +bool i2s_write_stereo_samples_i16_dual(const int16_t *fl_sample, const int16_t *fr_sample, const int buffLen, uint8_t codec_num); void i2s_read_stereo_samples_buff(float *fl_sample, float *fr_sample, const int buffLen); void i2s_read_stereo_samples_buff(int16_t *fl_sample, int16_t *fr_sample, const int buffLen); +void i2s_read_stereo_samples_buff_dual(float *fl_sample, float *fr_sample, const int buffLen, uint8_t codec_num); +void i2s_read_stereo_samples_i16_dual(int16_t *fl_sample, int16_t *fr_sample, const int buffLen, uint8_t codec_num); #endif /* ML_SYNTH_INLINE_DECLARATION */ @@ -139,6 +143,10 @@ union sampleTUNT const i2s_port_t i2s_port_number = I2S_NUM_0; +#ifdef DUAL_CODEC_ENABLED +const i2s_port_t i2s_port_number_secondary = I2S_NUM_1; +#endif + /* * please refer to https://www.hackster.io/janost/audio-hacking-on-the-esp8266-fa9464#toc-a-simple-909-drum-synth-0 * for the following implementation @@ -510,6 +518,70 @@ i2s_config_t i2s_configuration = }; #endif +#ifdef DUAL_CODEC_ENABLED +/* Secondary I2S configuration for dual codec setup */ +i2s_config_t i2s_configuration_secondary = +{ + .mode = (i2s_mode_t)(I2S_MODE_SLAVE | I2S_MODE_TX | I2S_MODE_RX), + .sample_rate = SAMPLE_RATE * I2S_OVERSAMPLE, +#ifdef SAMPLE_SIZE_32BIT + .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, +#endif +#ifdef SAMPLE_SIZE_24BIT + .bits_per_sample = I2S_BITS_PER_SAMPLE_24BIT, +#endif +#ifdef SAMPLE_SIZE_16BIT + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, +#endif + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, +#ifdef ARDUINO_RUNNING_CORE +#ifdef MAX_98357A_ENABLED + .communication_format = I2S_COMM_FORMAT_STAND_PCM_LONG, +#else + .communication_format = I2S_COMM_FORMAT_STAND_I2S, +#endif +#else + .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), +#endif + .intr_alloc_flags = 0, + .dma_buf_count = 2, + .dma_buf_len = SAMPLE_BUFFER_SIZE, +#ifdef I2S_USE_APLL + .use_apll = true, +#else + .use_apll = false, +#endif + +#ifdef ARDUINO_RUNNING_CORE + .tx_desc_auto_clear = true, + .fixed_mclk = 0, +#ifdef I2S_MCLK_MULTIPLE_DEFAULT + .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, +#else + .mclk_multiple = I2S_MCLK_MULTIPLE_256, +#endif +#ifdef SAMPLE_SIZE_16BIT + .bits_per_chan = I2S_BITS_PER_CHAN_16BIT, +#endif +#ifdef SAMPLE_SIZE_24BIT + .bits_per_chan = I2S_BITS_PER_CHAN_24BIT, +#endif +#ifdef SAMPLE_SIZE_32BIT + .bits_per_chan = I2S_BITS_PER_CHAN_32BIT, +#endif +#endif + +#if SOC_I2S_SUPPORTS_TDM + .chan_mask = I2S_CHANNEL_STEREO, + .total_chan = 0, + .left_align = 0, + .big_edin = 0, + .bit_order_msb = 0, + .skip_msk = 0, +#endif +}; +#endif + #ifdef I2S_NODAC #ifdef ESP8266 @@ -535,6 +607,40 @@ i2s_pin_config_t pins = }; #endif /* (defined I2S_BCLK_PIN) && (defined I2S_WCLK_PIN) && (defined I2S_DOUT_PIN) */ +#ifdef DUAL_CODEC_ENABLED +#if (defined I2S_DOUT_PIN_SECONDARY) || (defined I2S_DIN_PIN_SECONDARY) +/* Secondary I2S pin configuration for dual codec setup */ +i2s_pin_config_t pins_secondary = +{ + #ifdef I2S_MCLK_PIN_SECONDARY + .mck_io_num = I2S_MCLK_PIN_SECONDARY, + #else + .mck_io_num = I2S_PIN_NO_CHANGE, + #endif + #ifdef I2S_BCLK_PIN_SECONDARY + .bck_io_num = I2S_BCLK_PIN_SECONDARY, + #else + .bck_io_num = I2S_PIN_NO_CHANGE, + #endif + #ifdef I2S_WCLK_PIN_SECONDARY + .ws_io_num = I2S_WCLK_PIN_SECONDARY, + #else + .ws_io_num = I2S_PIN_NO_CHANGE, + #endif + #ifdef I2S_DOUT_PIN_SECONDARY + .data_out_num = I2S_DOUT_PIN_SECONDARY, + #else + .data_out_num = I2S_PIN_NO_CHANGE, + #endif + #ifdef I2S_DIN_PIN_SECONDARY + .data_in_num = I2S_DIN_PIN_SECONDARY, + #else + .data_in_num = I2S_PIN_NO_CHANGE, + #endif +}; +#endif /* (defined I2S_BCLK_PIN_SECONDARY) && (defined I2S_WCLK_PIN_SECONDARY) && (defined I2S_DOUT_PIN_SECONDARY) */ +#endif /* DUAL_CODEC_ENABLED */ + #endif #if (defined I2S_BCLK_PIN) && (defined I2S_WCLK_PIN) && (defined I2S_DOUT_PIN) @@ -569,7 +675,49 @@ void setup_i2s() Serial.printf(" MCLK: %d\n", pins.mck_io_num); #endif i2s_set_sample_rates(i2s_port_number, SAMPLE_RATE); + #ifdef DUAL_CODEC_ENABLED +#if (defined I2S_BCLK_PIN_SECONDARY) || (defined I2S_WCLK_PIN_SECONDARY) || (defined I2S_DOUT_PIN_SECONDARY) || (defined I2S_DIN_PIN_SECONDARY) + /* Setup secondary codec */ + i2s_driver_install(i2s_port_number_secondary, &i2s_configuration_secondary, 0, NULL); + Serial.printf("Secondary i2s_configuration:\n"); + Serial.printf("\ttx_desc_auto_clear: %d\n", i2s_configuration_secondary.tx_desc_auto_clear); + Serial.printf("\tfixed_mclk: %d\n", i2s_configuration_secondary.fixed_mclk); + Serial.printf("\tmclk_multiple: %d\n", i2s_configuration_secondary.mclk_multiple); + Serial.printf("\tbits_per_chan: %d\n", i2s_configuration_secondary.bits_per_chan); + + i2s_set_pin(i2s_port_number_secondary, &pins_secondary); + + Serial.printf("Secondary I2S_NUM_%d configured using following pins:\n", i2s_port_number_secondary); + Serial.printf(" BCLK,BCK: %d\n", pins_secondary.bck_io_num); + Serial.printf(" WCLK,LCK: %d\n", pins_secondary.ws_io_num); + Serial.printf(" DOUT: %d\n", pins_secondary.data_out_num); + Serial.printf(" DIN: %d\n", pins_secondary.data_in_num); + Serial.printf(" MCLK: %d\n", pins_secondary.mck_io_num); + + i2s_set_sample_rates(i2s_port_number_secondary, SAMPLE_RATE); + + Serial.printf("Secondary I2S configured using following pins:\n"); + Serial.printf(" BCLK,BCK: %d\n", pins_secondary.bck_io_num); + Serial.printf(" WCLK,LCK: %d\n", pins_secondary.ws_io_num); + Serial.printf(" DOUT: %d\n", pins_secondary.data_out_num); + Serial.printf(" DIN: %d\n", pins_secondary.data_in_num); + Serial.printf(" MCLK: %d\n", pins_secondary.mck_io_num); + + #endif /* (defined I2S_BCLK_PIN_SECONDARY) && (defined I2S_WCLK_PIN_SECONDARY) && (defined I2S_DOUT_PIN_SECONDARY) */ + #include "esp_rom_gpio.h" + #include "soc/i2s_periph.h" + /* Connnect both BCLK signals */ + esp_rom_gpio_connect_out_signal(I2S_BCLK_PIN_SECONDARY, i2s_periph_signal[0].m_tx_bck_sig, 0, 0); + esp_rom_gpio_connect_in_signal(I2S_BCLK_PIN_SECONDARY, i2s_periph_signal[1].s_rx_bck_sig, 0); + /* Connect both WS signals */ + esp_rom_gpio_connect_out_signal(I2S_WCLK_PIN_SECONDARY, i2s_periph_signal[0].m_tx_ws_sig, 0, 0); + esp_rom_gpio_connect_in_signal(I2S_WCLK_PIN_SECONDARY, i2s_periph_signal[1].s_rx_ws_sig, 0); + i2s_start(i2s_port_number); + i2s_start(i2s_port_number_secondary); + #else i2s_start(i2s_port_number); + #endif /* DUAL_CODEC_ENABLED */ + #ifdef ES8388_ENABLED #ifdef PIN_CTRL REG_WRITE(PIN_CTRL, 0xFFFFFFF0); @@ -606,6 +754,116 @@ void setup_i2s() } #endif /* (defined I2S_BCLK_PIN) && (defined I2S_WCLK_PIN) && (defined I2S_DOUT_PIN) */ +#ifdef DUAL_CODEC_ENABLED +#ifdef SAMPLE_BUFFER_SIZE +/* Dual codec write function - allows writing to specific codec */ +bool i2s_write_stereo_samples_buff_dual(const float *fl_sample, const float *fr_sample, const int buffLen, uint8_t codec_num) +{ + static union sampleTUNT sampleDataU[SAMPLE_BUFFER_SIZE]; + i2s_port_t target_port = (codec_num == 0) ? i2s_port_number : i2s_port_number_secondary; + + for (int n = 0; n < buffLen; n++) + { + /* Using RIGHT_LEFT format for both codecs */ + sampleDataU[n].ch[0] = (SAMPLE_DATA_TYPE)(fr_sample[n] * MULTIPLIER_CONST); + sampleDataU[n].ch[1] = (SAMPLE_DATA_TYPE)(fl_sample[n] * MULTIPLIER_CONST); + } + + static size_t bytes_written = 0; + +#ifdef CYCLE_MODULE_ENABLED + calcCycleCountPre(); +#endif + i2s_write(target_port, (const char *)&sampleDataU[0].sample, 2 * BYTES_PER_SAMPLE * buffLen, &bytes_written, portMAX_DELAY); +#ifdef CYCLE_MODULE_ENABLED + calcCycleCount(); +#endif + + if (bytes_written > 0) + { + return true; + } + else + { + return false; + } +} + +/* Dual codec write function for int16 - allows writing to specific codec */ +bool i2s_write_stereo_samples_i16_dual(const int16_t *fl_sample, const int16_t *fr_sample, const int buffLen, uint8_t codec_num) +{ + static union sampleTUNT sampleDataU[SAMPLE_BUFFER_SIZE]; + i2s_port_t target_port = (codec_num == 0) ? i2s_port_number : i2s_port_number_secondary; + + for (int n = 0; n < buffLen; n++) + { + /* Using RIGHT_LEFT format for both codecs */ + sampleDataU[n].ch[0] = fr_sample[n]; + sampleDataU[n].ch[1] = fl_sample[n]; + } + + static size_t bytes_written = 0; + +#ifdef CYCLE_MODULE_ENABLED + calcCycleCountPre(); +#endif + i2s_write(target_port, (const char *)&sampleDataU[0].sample, 2 * BYTES_PER_SAMPLE * buffLen, &bytes_written, portMAX_DELAY); +#ifdef CYCLE_MODULE_ENABLED + calcCycleCount(); +#endif + + if (bytes_written > 0) + { + return true; + } + else + { + return false; + } +} + +/* Dual codec read function - allows reading from specific codec */ +void i2s_read_stereo_samples_buff_dual(float *fl_sample, float *fr_sample, const int buffLen, uint8_t codec_num) +{ + i2s_port_t target_port = (codec_num == 0) ? i2s_port_number : i2s_port_number_secondary; + +#ifdef I2S_DIN_PIN + static size_t bytes_read = 0; + static union sampleTUNT sampleData[SAMPLE_BUFFER_SIZE]; + + i2s_read(target_port, (char *)&sampleData[0].sample, 2 * BYTES_PER_SAMPLE * buffLen, &bytes_read, 0); + + for (int n = 0; n < buffLen; n++) + { + /* Using RIGHT_LEFT format */ + fr_sample[n] = ((float)sampleData[n].ch[0] * (1.0f / MULTIPLIER_CONST)); + fl_sample[n] = ((float)sampleData[n].ch[1] * (1.0f / MULTIPLIER_CONST)); + } +#endif +} + +/* Dual codec read function for int16 - allows reading from specific codec */ +void i2s_read_stereo_samples_i16_dual(int16_t *fl_sample, int16_t *fr_sample, const int buffLen, uint8_t codec_num) +{ + i2s_port_t target_port = (codec_num == 0) ? i2s_port_number : i2s_port_number_secondary; + +#ifdef I2S_DIN_PIN + static size_t bytes_read = 0; + static union sampleTUNT sampleData[SAMPLE_BUFFER_SIZE]; + + i2s_read(target_port, (char *)&sampleData[0].sample, 2 * BYTES_PER_SAMPLE * buffLen, &bytes_read, 0); + + for (int n = 0; n < buffLen; n++) + { + /* Using RIGHT_LEFT format */ + fr_sample[n] = sampleData[n].ch[0]; + fl_sample[n] = sampleData[n].ch[1]; + } +#endif +} +#endif /* SAMPLE_BUFFER_SIZE */ +#endif /* DUAL_CODEC_ENABLED */ + #endif /* ESP32 */