pschatzmann / arduino-audio-tools

Arduino Audio Tools (a powerful Audio library not only for Arduino)
GNU General Public License v3.0
1.43k stars 222 forks source link

I2SCodecStream can not use I2C to set volume after i2s.begin was initiated #1664

Open uutzinger opened 1 month ago

uutzinger commented 1 month ago

Problem Description

I was trying to set microphone input gain on ES8388. I noticed that I am having I2C communications errors at the end of the setup:

I2SCodecStream                i2s_stream(audio_board);             // i2s coded
i2s_stream.begin(i2s_config); // this should apply I2C and I2S configuration

I identified the issue in I2SCodecStream. Its the code after i2s.begin() when you attempt to read the volume and setVolume.

  bool begin1() {
   ...
    is_active = i2s.begin(cfg);

    // if setvolume was called before begin
    if (is_active && volume() >= 0.0f) {
      setVolume(volume());
    } 
...

The issue is that it appears when i2s is running you no longer have access to i2c bus:

[D] I2SESP32V1.h : 255 - startChannels - started
Debug:   es8388_get_voice_volume
Debug:   i2c_bus_read_bytes: addr=16 reglen=1 datalen=1 - reg=46
Error:   ->p_wire->endTransmission: 2
Error:   ->p_wire->requestFrom 1->0
...

I thought perhaps there is issue with delay between i2c write/read and I added a delay to the i2c_read and write functions in AudioDriver but that did not resolve the problem.

I can set volume before I start the I2SCodecSteam but the issue is that the begin function of the I2SCodecStream re-initializes the pins, and the board and does not retain the previously applied microphone or headphone volume.

I have not yet tested older ESP32 board versions to see whether this is a "new" bug in the Espressif Arduino libraries.

I would appreciate your guidance on how to go about this: 1) Setting pins and the board is redundant in the I2SCodecStream. Is there an alternative I2S stream setup or shall I try to modify your codec stream code so that if the board is already initialized it does not do it again? I took ino example from AudioDriver. 2) From your previous experience with I2S audio boards, do you think this is Espressif Arduino software issue or do you think its possible that ES8388 needs to have its I2S communication stopped in order to listen to I2C?

Device Description

ESP32-S3 Adafruit feather Custom ES8388 as well as PCB Artist ES8388

Sketch

/**
 * @brief We define a ES8388 board and configure it
 * with the help of the AudioTools I2SCodecStream
 * @author Urs Utzinger
 */

#include "AudioTools.h" // install https://github.com/pschatzmann/arduino-audio-tools
#include "AudioLibs/I2SCodecStream.h"

// I2C
#define SDAPIN             SDA // I2C Data,  Adafruit ESP32 S3 3, Sparkfun Thing Plus C 23
#define SCLPIN             SCL // I2C Clock, Adafruit ESP32 S3 4, Sparkfun Thing Plus C 22
#define I2CSPEED        400000 // Clock Rate
#define ES8388ADDR        0x10 // Address of ES8388 I2C port

// I2S, your configuration for the ES8388 board
#define MCLKPIN             A4 // Master Clock
#define BCLKPIN            SCK // Bit Clock
#define WSPIN               A5 // Word select
#define DOPIN             MISO // This is connected to DI on ES8388 (MISO)
#define DIPIN             MOSI // This is connected to DO on ES8388 (MOSI)

AudioInfo                     audio_info(44200, 2, 16);                // sampling rate, # channels, bit depth
DriverPins                    my_pins;                                 // board pins
AudioBoard                    audio_board(AudioDriverES8388, my_pins); // audio board
I2SCodecStream                i2s_stream(audio_board);             // i2s coded
CsvOutput<int16_t>            serial_stream(Serial); // serial output
TwoWire                       myWire = TwoWire(0);                     // universal I2C interface

void setup() {
  Serial.begin(500000);
  while(!Serial){delay(50);} // Wait for Serial to be ready
  delay(5000);

  AudioLogger::instance().begin(Serial, AudioLogger::Debug); // Debug, Info, Warning, Error
  LOGLEVEL_AUDIODRIVER = AudioDriverDebug;

  Serial.println("Setup starting...");

  Serial.println("I2C pin ...");
  my_pins.addI2C(PinFunction::CODEC, SCLPIN, SDAPIN, ES8388ADDR, I2CSPEED, myWire);
  Serial.println("I2S pin ...");
  my_pins.addI2S(PinFunction::CODEC, MCLKPIN, BCLKPIN, WSPIN, DIPIN, DOPIN);

  Serial.println("Pins begin ..."); 
  my_pins.begin();

  Serial.println("Board begin ..."); 
  CodecConfig cfg;
  cfg.input_device  = ADC_INPUT_LINE1;  
  cfg.output_device = DAC_OUTPUT_ALL;  
  cfg.i2s.bits      = BIT_LENGTH_16BITS;
  cfg.i2s.rate      = RATE_44K; 
  cfg.i2s.channels  = CHANNELS2;
  cfg.i2s.fmt       = I2S_NORMAL;
  cfg.i2s.mode      = MODE_SLAVE;  
  audio_board.begin(cfg);

  Serial.println("Set Volume ...");
  audio_board.setInputVolume(77); // 0 to 100
  audio_board.setVolume(100); // 0 to 100

  Serial.println("I2S begin ...");
  auto i2s_config = i2s_stream.defaultConfig(RXTX_MODE);
  i2s_config.copyFrom(audio_info);
  i2s_stream.begin(i2s_config); // this should apply I2C and I2S configuration

  Serial.println("Setup completed ...");
}

void loop() { 
}

Other Steps to Reproduce

You move

  Serial.println("Set Volume ...");
  audio_board.setInputVolume(77); // 0 to 100
  audio_board.setVolume(100); // 0 to 100

After i2s_stream.begin

and they cause errors.

i2s_stream.begin(i2s_config); // this should apply I2C and I2S configuration Does not complete without errors.

What is your development environment

Arduino IDE

I have checked existing issues, discussions and online documentation

pschatzmann commented 1 month ago

I only tried with AudioBoards that have a regular ESP32 and there, I never had any issue in using I2S and I2C concurrently.

My first gut feeling would be, that you have some pin conflicts. Did you double check the log for the logged GPIO numbers ?

Did you try to reduce the I2C speed ? Not sure if it makes a difference, but use CodecConfig cfg = i2s_stream.defaultConfig(TX_MODE); // or RXTX_MODE

uutzinger commented 1 month ago

Thanks Phil, I tried 100kHz clock and older version of Espressif Arduino. That did not solve the problem. I ordered Espressif Lyrat to compare. I also ordered shielded wire. I will try shorter wires a little later and I think its time to hookup an oscilloscope.... Thank you for your support.

uutzinger commented 4 weeks ago

I looked at the schematics of the Lyrat board from Espressif. It has a low pass filter on the i2c clock and data lines and a comment that its needed to avoid i2c errors. I think that will solve the problem.

pschatzmann commented 4 weeks ago

If I remember right, the documentation of the module says that the module does not provide any pull up resistors on the I2C pins, so they need to be added externally to make things work.

They're typically 4.7K or 10K ohm, but should be in the range of 2K to 10K. (The internal pullup of the ESP32 of 45k might be insufficient even for 100kHz)