diff --git a/lib/libesp32/berry/default/be_modtab.c b/lib/libesp32/berry/default/be_modtab.c index 7655236a3..7cb46c2e9 100644 --- a/lib/libesp32/berry/default/be_modtab.c +++ b/lib/libesp32/berry/default/be_modtab.c @@ -215,6 +215,7 @@ be_extern_native_class(AudioGeneratorWAV); be_extern_native_class(AudioGeneratorMP3); be_extern_native_class(AudioFileSourceFS); be_extern_native_class(AudioOpusDecoder); +be_extern_native_class(AudioInputI2S); be_extern_native_class(md5); be_extern_native_class(udp); be_extern_native_class(webclient); @@ -304,6 +305,7 @@ BERRY_LOCAL bclass_array be_class_table = { &be_native_class(AudioFileSourceFS), #endif // USE_UFILESYS &be_native_class(AudioOpusDecoder), + &be_native_class(AudioInputI2S), #endif // USE_I2S_AUDIO_BERRY #if defined(USE_BERRY_INT64) || defined(USE_MATTER_DEVICE) &be_native_class(int64), diff --git a/lib/libesp32/berry_tasmota/src/be_i2s_audio_lib.c b/lib/libesp32/berry_tasmota/src/be_i2s_audio_lib.c index 1f3c74a26..081f56868 100644 --- a/lib/libesp32/berry_tasmota/src/be_i2s_audio_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_i2s_audio_lib.c @@ -86,6 +86,17 @@ BE_FUNC_CTYPE_DECLARE(be_audio_output_consume_silence, "i", "."); extern int i2s_output_i2s_set_lsb_justified(void* out, bbool lsbJustified); BE_FUNC_CTYPE_DECLARE(i2s_output_i2s_set_lsb_justified, "b", ".b"); +// ---------------------------------------------------------------------- +// Audio Input I2S + +// AudioInputI2S.init() -> instance +extern void* be_audio_input_i2s_init(void); +BE_FUNC_CTYPE_DECLARE(be_audio_input_i2s_init, "+.p", ""); + +// AudioInputI2S.deinit()-> void +extern void* be_audio_input_i2s_deinit(void* instance); +BE_FUNC_CTYPE_DECLARE(be_audio_input_i2s_deinit, "", "."); + #include "be_fixed_be_class_AudioOutputI2S.h" #include "be_fixed_be_class_AudioGenerator.h" @@ -93,6 +104,7 @@ BE_FUNC_CTYPE_DECLARE(i2s_output_i2s_set_lsb_justified, "b", ".b"); #include "be_fixed_be_class_AudioGeneratorMP3.h" #include "be_fixed_be_class_AudioFileSource.h" #include "be_fixed_be_class_AudioFileSourceFS.h" +#include "be_fixed_be_class_AudioInputI2S.h" /* @const_object_info_begin @@ -146,6 +158,12 @@ class be_class_AudioFileSourceFS (scope: global, name: AudioFileSourceFS, super: deinit, func(i2s_file_source_fs_deinit) } +class be_class_AudioInputI2S (scope: global, name: AudioInputI2S, strings: weak) { + .p, var + init, ctype_func(be_audio_input_i2s_init) + deinit, ctype_func(be_audio_input_i2s_deinit) +} + @const_object_info_end */ #endif // USE_I2S_AUDIO_BERRY diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s__lib_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s__lib_idf51.ino index 9910287fd..3102b3d92 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s__lib_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s__lib_idf51.ino @@ -42,34 +42,55 @@ * Driver Settings in memory \*********************************************************************************************/ +// I2S communication mode +enum : int8_t { + I2S_MODE_STD = 0, // I2S mode standard + I2S_MODE_PDM = 1, // I2S mode PDM + I2S_MODE_TDM = 2, // I2S mode TDM + I2S_MODE_DAC = 3, // Using internal DAC - only available on ESP32 +}; + +// I2S slot mask (left, right, both) +enum : int8_t { + I2S_SLOT_LEFT = 1, // left + I2S_SLOT_RIGHT = 2, // right + I2S_SLOT_BOTH = 3, // both +}; + +// I2S slot configuration +enum : int8_t { + I2S_SLOT_MSB = 0, // MSB + I2S_SLOT_PCM = 1, // PCM + I2S_SLOT_PHILIPS = 2, // Philips +}; + typedef struct{ struct{ uint8_t version = 0; // B00 // runtime options, will be saved but ignored on setting read - bool duplex = 0; // B01 - depends on GPIO setting and SOC caps, DIN and DOUT on same port in GPIO means -> try to use duplex if possible - bool tx = 0; // B02 - depends on GPIO setting - bool rx = 0; // B03 - depends on GPIO setting - uint32_t spare01; // B04-07 + bool duplex = 0; // B01 - depends on GPIO setting and SOC caps, DIN and DOUT on same port in GPIO means -> try to use duplex if possible + bool tx = 0; // B02 - depends on GPIO setting + bool rx = 0; // B03 - depends on GPIO setting + uint32_t spare01; // B04-07 } sys; struct { - uint8_t mode = 0; // B00 STD = 0, PDM = 1, TDM = 2 - bool apll = 1; // B01 - will be ignored on unsupported SOC's - bool channels = 0; // B02 - 1 = mono, 2 = stereo - uint8_t codec = 0; // B03 - S3 box only, unused for now - uint8_t slot_config = 0; // B04 - slot configuration MSB = 0, PCM = 1, PHILIPS = 2 - uint8_t volume = 10; // B05 - bool mclk_inv = 0; // B06 - invert mclk - bool bclk_inv = 0; // B07 - invert bclk - bool ws_inv = 0; // B08 - invert ws - uint8_t spare[7]; // B09-0F + uint8_t mode = I2S_MODE_STD; // B00 - I2S mode standard, PDM, TDM, DAC + bool apll = 1; // B01 - will be ignored on unsupported SOC's + uint8_t channels = 2; // B02 - 1 = mono, 2 = stereo + uint8_t codec = 0; // B03 - S3 box only, unused for now + uint8_t slot_config = I2S_SLOT_MSB;// B04 - slot configuration MSB = 0, PCM = 1, PHILIPS = 2 + uint8_t volume = 10; // B05 + bool mclk_inv = 0; // B06 - invert mclk + bool bclk_inv = 0; // B07 - invert bclk + bool ws_inv = 0; // B08 - invert ws + uint8_t spare[7]; // B09-0F } tx; struct { - uint32_t sample_rate = 32000; // B00-03 + uint32_t sample_rate = 16000; // B00-03 uint8_t gain = 30; // B04 - uint8_t mode = 0; // B05 - STD = 0, PDM = 1, TDM = 2 - - uint8_t slot_mask = 1; // B06 - left = 1 /right = 2 /both = 3 + uint8_t mode = I2S_MODE_STD; // B05 - I2S mode standard, PDM, TDM, DAC + uint8_t slot_mask = I2S_SLOT_LEFT;// B06 - slot mask uint8_t slot_mode = 0; // B07 - mono/stereo - 1 is added for both uint8_t codec = 0; // B08 - unused for now uint8_t mp3_encoder = 1; // B09 - will be ignored without PS-RAM @@ -106,10 +127,7 @@ struct AUDIO_I2S_t { TaskHandle_t mp3_task_handle; TaskHandle_t mic_task_handle; - uint32_t mic_size; - uint8_t *mic_buff; char mic_path[32]; - File fwp; uint8_t mic_stop; int8_t mic_error; bool use_stream = false; @@ -132,8 +150,6 @@ struct AUDIO_I2S_t { } audio_i2s; -enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 }; - /*********************************************************************************************\ * Class for outputting sound as endpoint for ESP8266Audio library \*********************************************************************************************/ @@ -172,7 +188,7 @@ public: } virtual bool SetRate(int hz) { - AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: SetRate: %i"), hz); + AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: SetRate: %i was %i on=%i"), hz, this->hertz, _i2s_on); if (hz == (int) this->hertz) { return true; } this->hertz = hz; if (_i2s_on) { @@ -229,7 +245,7 @@ protected: bool _i2s_on = false; // is I2S audio active // local copy of useful settings for audio // TX - uint8_t _tx_mode = EXTERNAL_I2S; // EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 + uint8_t _tx_mode = I2S_MODE_STD; // EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 bool _tx_enabled = false; // true = enabled, false = disabled uint8_t _tx_channels = 2; // true = mono, false = stereo i2s_chan_handle_t _tx_chan; // I2S channel handle, automatically computed @@ -255,10 +271,10 @@ void TasmotaAudioOutputI2S::loadSettings(void) { _i2s_on = false; bps = I2S_DATA_BIT_WIDTH_16BIT; _tx_channels = audio_i2s.Settings->tx.channels; - if (_tx_channels == 0) { _tx_channels = 1; } // if zero channel default to mono + if (_tx_channels == 0) { _tx_channels = 2; } // if zero channel default to stereo if (_tx_channels > 2) { _tx_channels = 2; } // if > 2 channels default to stereo channels = (_tx_channels == 1) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO; - _tx_mode = EXTERNAL_I2S; + _tx_mode = I2S_MODE_STD; _tx_enabled = false; _gpio_mclk = (gpio_num_t) Pin(GPIO_I2S_MCLK); @@ -292,6 +308,7 @@ bool TasmotaAudioOutputI2S::stop() { if (audio_i2s.Settings->sys.duplex == 0 && audio_i2s.Settings->sys.rx == 1) { i2s_del_channel(_tx_chan); _i2s_on = false; + AddLog(LOG_LEVEL_INFO, "I2S: stop: I2S channel disabled"); } _tx_enabled = false; return true; @@ -322,7 +339,7 @@ int32_t TasmotaAudioOutputI2S::consumeSamples(int16_t *samples, size_t count) { right = (((int16_t)(right & 0xff)) - 128) << 8; } - if (_tx_mode == INTERNAL_DAC) { + if (_tx_mode == I2S_MODE_DAC) { left = Amplify(left) + 0x8000; right = Amplify(right) + 0x8000; } else { @@ -377,14 +394,14 @@ bool TasmotaAudioOutputI2S::startI2SChannel(void) { }; // change configuration if we are using PCM or PHILIPS - if (audio_i2s.Settings->tx.slot_config == 1) { // PCM + if (audio_i2s.Settings->tx.slot_config == I2S_SLOT_PCM) { // PCM tx_std_cfg.slot_cfg = I2S_STD_PCM_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t)bps, (i2s_slot_mode_t)channels); - } else if (audio_i2s.Settings->tx.slot_config == 2) { // PHILIPS + } else if (audio_i2s.Settings->tx.slot_config == I2S_SLOT_PHILIPS) { // PHILIPS tx_std_cfg.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t)bps, (i2s_slot_mode_t)channels); } _i2s_on = (i2s_channel_init_std_mode(_tx_chan, &tx_std_cfg) == 0); - AddLog(LOG_LEVEL_DEBUG, "I2S: TX channel with %i bit width on %i channels initialized ok=%i", bps, channels, _i2s_on); + AddLog(LOG_LEVEL_DEBUG, "I2S: TX channel with %i bits width on %i channels initialized i2s_on=%i", bps, channels, _i2s_on); if (audio_i2s.Settings->sys.duplex == 1) { i2s_channel_init_std_mode(audio_i2s.rx_handle, &tx_std_cfg); @@ -403,7 +420,7 @@ int TasmotaAudioOutputI2S::updateClockConfig(void) { #endif int result = i2s_channel_reconfig_std_clock(_tx_chan, &clk_cfg ); if (_tx_enabled) { i2s_channel_enable(_tx_chan); } - AddLog(LOG_LEVEL_DEBUG, "I2S: Updating clock config, result=%i", result); + AddLog(LOG_LEVEL_DEBUG, "I2S: Updating clock config"); return result; } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino index 9529b0a75..72f5f3e13 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino @@ -100,77 +100,92 @@ void (* const I2SAudio_Command[])(void) PROGMEM = { * microphone related functions \*********************************************************************************************/ -uint32_t I2sMicInit(uint8_t enable) { +uint32_t I2sMicInit(void) { esp_err_t err = ESP_OK; - i2s_slot_mode_t slot_mode = (audio_i2s.Settings->rx.slot_mode == 0) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO; - gpio_num_t _CLK; + gpio_num_t clk_gpio; - if(audio_i2s.Settings->sys.duplex == 1 && audio_i2s.rx_handle != nullptr){ + i2s_slot_mode_t slot_mode = (audio_i2s.Settings->rx.slot_mode == 0) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO; + AddLog(LOG_LEVEL_DEBUG, "I2S: mic init rx_channels=%i", slot_mode); + + if (audio_i2s.Settings->sys.duplex == 1 && audio_i2s.rx_handle){ return 0; // no need to en- or disable when in full duplex mode and already initialized } - if(audio_i2s.rx_handle == nullptr){ + if (audio_i2s.rx_handle == nullptr){ i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER); + err = i2s_new_channel(&chan_cfg, NULL, &audio_i2s.rx_handle); + AddLog(LOG_LEVEL_DEBUG, "I2S: mic init i2s_new_channel err=%i", err); switch (audio_i2s.Settings->rx.mode){ - case 1: - { - _CLK = (gpio_num_t)Pin(GPIO_I2S_WS,1); //legacy setting for Core2, might be wrong - if(_CLK == -1){ - _CLK = (gpio_num_t)Pin(GPIO_I2S_WS); //fallback to other port, might be wrong + case I2S_MODE_PDM: + { + clk_gpio = (gpio_num_t)Pin(GPIO_I2S_WS,1); //legacy setting for Core2, might be wrong + if (clk_gpio == -1){ + clk_gpio = (gpio_num_t)Pin(GPIO_I2S_WS); //fallback to other port, might be wrong } i2s_pdm_rx_config_t pdm_rx_cfg = { - .clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(audio_i2s.Settings->rx.sample_rate), - /* The default mono slot is the left slot (whose 'select pin' of the PDM microphone is pulled down) */ - .slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, slot_mode), - .gpio_cfg = { - .clk = _CLK, + .clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(audio_i2s.Settings->rx.sample_rate), + /* The default mono slot is the left slot (whose 'select pin' of the PDM microphone is pulled down) */ + .slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, slot_mode), + .gpio_cfg = { + .clk = clk_gpio, .din = (gpio_num_t)Pin(GPIO_I2S_DIN), .invert_flags = { - .clk_inv = false, + .clk_inv = false, }, - }, - }; - pdm_rx_cfg.slot_cfg.slot_mask = (i2s_pdm_slot_mask_t)audio_i2s.Settings->rx.slot_mask; - err = i2s_channel_init_pdm_rx_mode(audio_i2s.rx_handle, &pdm_rx_cfg);} - AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel in PDM mode, CLK: %i, DIN: %i, 16 bit width, %i channel(s), err code: %u"),_CLK, Pin(GPIO_I2S_DIN), slot_mode, err); + }, + }; + pdm_rx_cfg.slot_cfg.slot_mask = (i2s_pdm_slot_mask_t)audio_i2s.Settings->rx.slot_mask; + err = i2s_channel_init_pdm_rx_mode(audio_i2s.rx_handle, &pdm_rx_cfg); + AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel in PDM mode, CLK: %i, DIN: %i, 16 bit width, %i channel(s), err code: %u"),clk_gpio, Pin(GPIO_I2S_DIN), slot_mode, err); + } break; - default: // same as 0 - { + case I2S_MODE_STD: + { i2s_std_config_t rx_std_cfg = { - .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(audio_i2s.Settings->rx.sample_rate), - .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, slot_mode), - .gpio_cfg = { + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(audio_i2s.Settings->rx.sample_rate), + .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, slot_mode), + .gpio_cfg = { .mclk = (gpio_num_t)Pin(GPIO_I2S_MCLK), .bclk = (gpio_num_t)Pin(GPIO_I2S_BCLK), .ws = (gpio_num_t)Pin(GPIO_I2S_WS), .dout = I2S_GPIO_UNUSED, .din = (gpio_num_t)Pin(GPIO_I2S_DIN), .invert_flags = { - .mclk_inv = false, - .bclk_inv = false, - .ws_inv = false, - }, + .mclk_inv = false, + .bclk_inv = false, + .ws_inv = false, }, + }, }; rx_std_cfg.slot_cfg.slot_mask = (i2s_std_slot_mask_t)audio_i2s.Settings->rx.slot_mask; - i2s_channel_init_std_mode(audio_i2s.rx_handle, &rx_std_cfg);} + i2s_channel_init_std_mode(audio_i2s.rx_handle, &rx_std_cfg); AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel in standard mode with 16 bit width on %i channel(s) initialized"),slot_mode); - break; + } + break; + default: + AddLog(LOG_LEVEL_INFO, "I2S: invalid rx mode=%i", audio_i2s.Settings->rx.mode); } - } - if(enable == 0){ - int _err = i2s_channel_disable(audio_i2s.rx_handle); + + err = i2s_channel_enable(audio_i2s.rx_handle); + AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel enable: %i"),err); + return err; +} + + +void I2sMicDeinit(void) { + esp_err_t err = ESP_OK; + gpio_num_t clk_gpio; + + if (!audio_i2s.rx_handle) { return; } + + if (!audio_i2s.Settings->sys.duplex) { // if duplex mode, there is no mic channel - TODO check this + int err = i2s_channel_disable(audio_i2s.rx_handle); i2s_del_channel(audio_i2s.rx_handle); audio_i2s.rx_handle = nullptr; - AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel disable: %i"),_err); + AddLog(LOG_LEVEL_DEBUG, "I2S: RX channel disable: %i", err); } - else{ - err = i2s_channel_enable(audio_i2s.rx_handle); - AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel enable: %i"),err); - } - return err; } // micro to mp3 file or stream @@ -295,7 +310,7 @@ exit: audio_i2s.client.stop(); } - I2sMicInit(0); + I2sMicDeinit(); audio_i2s.mic_stop = 0; audio_i2s.mic_error = error; AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %d"), error); @@ -319,7 +334,7 @@ int32_t I2sRecordShine(char *path) { if (audio_i2s.use_stream) { stack = 8000; } - I2sMicInit(1); + I2sMicInit(); err = xTaskCreatePinnedToCore(I2sMicTask, "MIC", stack, NULL, 3, &audio_i2s.mic_task_handle, 1); @@ -342,40 +357,47 @@ void I2SAudioPower(bool power) { callBerryEventDispatcher(PSTR("audio"), PSTR("power"), power, nullptr, 0); } -void I2SSettingsLoad(bool erase) { - +// +// I2SSettingsLoad(erase:bool) +// +// Load settings from file system. +// File is `/.drvset042` +void I2SSettingsLoad(const char * config_filename, bool erase) { + // allocate memory for settings audio_i2s.Settings = new tI2SSettings(); + if (!audio_i2s.Settings) { + AddLog(LOG_LEVEL_ERROR, "I2S: ERROR memory allocation failed"); + return; + } + + if (!config_filename) { return; } // if no filename, use defaults #ifndef USE_UFILESYS - AddLog(LOG_LEVEL_INFO, PSTR("CFG: I2S use defaults as file system not enabled")); + AddLog(LOG_LEVEL_INFO, "I2S: use defaults as file system not enabled"); #else - char filename[20]; - // Use for drivers: - snprintf_P(filename, sizeof(filename), TASM_FILE_DRIVER, XDRV_42); if (erase) { - TfsDeleteFile(filename); // Use defaults + TfsDeleteFile(config_filename); // Use defaults } - else if (TfsLoadFile(filename, (uint8_t*)audio_i2s.Settings, sizeof(tI2SSettings))) { - AddLog(LOG_LEVEL_INFO, PSTR("CFG: I2S loaded from file")); + else if (TfsLoadFile(config_filename, (uint8_t*)audio_i2s.Settings, sizeof(tI2SSettings))) { + AddLog(LOG_LEVEL_INFO, "I2S: config loaded from file '%s'", config_filename); } else { // File system not ready: No flash space reserved for file system - AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: I2S use defaults as file system not ready or file not found")); - I2SSettingsSave(); + AddLog(LOG_LEVEL_DEBUG, "I2S: use defaults as file system not ready or file not found"); + I2SSettingsSave(config_filename); } #endif // USE_UFILESYS } -void I2SSettingsSave(void) { +void I2SSettingsSave(const char * config_filename) { #ifdef USE_UFILESYS - char filename[20]; - // Use for drivers: - snprintf_P(filename, sizeof(filename), TASM_FILE_DRIVER, XDRV_42); - if (TfsSaveFile(filename, (const uint8_t*)audio_i2s.Settings, sizeof(tI2SSettings))) { - AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: I2S saved to file")); + if (!config_filename) { return; } // if no filename, use defaults + + if (TfsSaveFile(config_filename, (const uint8_t*)audio_i2s.Settings, sizeof(tI2SSettings))) { + AddLog(LOG_LEVEL_DEBUG, "I2S: config saved to file '%s'", config_filename); } else { // File system not ready: No flash space reserved for file system - AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: ERROR I2S file system not ready or unable to save file")); + AddLog(LOG_LEVEL_DEBUG, "I2S: ERROR file system not ready or unable to save file"); } #endif // USE_UFILESYS } @@ -384,11 +406,17 @@ void I2SSettingsSave(void) { * Driver init \*********************************************************************************************/ +// +// I2sCheckCfg +// +// Multiple checks void I2sCheckCfg(void){ - bool useDuplexMode = ((Pin(GPIO_I2S_DIN) != -1) && (Pin(GPIO_I2S_DOUT) != -1)); // din and dout must be configured on port 0 for full duplex + // din and dout must be configured on port 0 for full duplex + bool useDuplexMode = ((Pin(GPIO_I2S_DIN) != -1) && (Pin(GPIO_I2S_DOUT) != -1)); // AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: DIN %i , DOUT %i"),Pin(GPIO_I2S_DIN),Pin(GPIO_I2S_DOUT) ); - if(useDuplexMode){ - if(audio_i2s.Settings->rx.mode == 1 || audio_i2s.Settings->tx.mode == 1 ){ + + if (useDuplexMode){ + if (audio_i2s.Settings->rx.mode == I2S_MODE_PDM || audio_i2s.Settings->tx.mode == I2S_MODE_PDM ){ useDuplexMode = false; AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: PDM forbids full duplex mode")); } @@ -397,37 +425,48 @@ void I2sCheckCfg(void){ AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: will try to use full duplex mode")); } } - if(Pin(GPIO_I2S_DIN) != -1 || Pin(GPIO_I2S_DIN,1) != -1){ // micro could be port 0 or 1 + if (Pin(GPIO_I2S_DIN) != -1 || Pin(GPIO_I2S_DIN, 1) != -1){ // micro could be port 0 or 1 audio_i2s.Settings->sys.rx = 1; - AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: I2S RX config = mode: %i, channels: %i, gain: %i, sample rate: %i"), audio_i2s.Settings->rx.mode, (uint8_t)(audio_i2s.Settings->rx.slot_mode + 1), audio_i2s.Settings->rx.gain, audio_i2s.Settings->rx.sample_rate); + AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX config = mode: %i, channels: %i, gain: %i, sample rate: %i"), audio_i2s.Settings->rx.mode, (uint8_t)(audio_i2s.Settings->rx.slot_mode + 1), audio_i2s.Settings->rx.gain, audio_i2s.Settings->rx.sample_rate); } else{ audio_i2s.Settings->sys.rx = 0; audio_i2s.Settings->rx.mp3_encoder = 0; // do not allocate buffer } - if(Pin(GPIO_I2S_DOUT) != -1){ // output is only supported on port 0 + if (Pin(GPIO_I2S_DOUT) != -1){ // output is only supported on port 0 audio_i2s.Settings->sys.tx = 1; } else{ audio_i2s.Settings->sys.tx = 0; } - AddLog(LOG_LEVEL_INFO, PSTR("I2S: init pins bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), Pin(GPIO_I2S_BCLK) , Pin(GPIO_I2S_WS), Pin(GPIO_I2S_DOUT), Pin(GPIO_I2S_MCLK), Pin(GPIO_I2S_DIN)); + AddLog(LOG_LEVEL_INFO, PSTR("I2S: init pins bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d [tx=%i, rx=%i, duplex=%i]"), + Pin(GPIO_I2S_BCLK) , Pin(GPIO_I2S_WS), Pin(GPIO_I2S_DOUT), Pin(GPIO_I2S_MCLK), Pin(GPIO_I2S_DIN), + audio_i2s.Settings->sys.tx, audio_i2s.Settings->sys.tx, audio_i2s.Settings->sys.duplex); } -bool I2sPinInit(void) { - int result = 0; - +// +// I2sInit +// +// Initialize I2S driver for input and output +void I2sInit(void) { + // we need at least one pin configured if(Pin(GPIO_I2S_DIN) + Pin(GPIO_I2S_DIN,1) + Pin(GPIO_I2S_DOUT) + Pin(GPIO_I2S_DOUT,1) == -4){ - return false; + AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: no pin configured")); + return; } - I2SSettingsLoad(false); + I2SSettingsLoad("/.drvset042", false); // load configuration (no-erase) + if (!audio_i2s.Settings) { return; } // fatal error, could not allocate memory for configuration + + // check configuration is valid I2sCheckCfg(); - if(audio_i2s.Settings->sys.rx == 1 && audio_i2s.Settings->sys.duplex == 0){ - result += I2sMicInit(0); + // TODO + int result = 0; + if (audio_i2s.Settings->sys.rx == 1 && audio_i2s.Settings->sys.duplex == 0){ + I2sMicDeinit(); } if(audio_i2s.Settings->sys.tx == 1){ @@ -436,16 +475,6 @@ bool I2sPinInit(void) { result += err; } - return (result == 0); -} - -void I2sInit(void) { - - if (I2sPinInit() == false) { - AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: no valid pin config")); - return; - } - if(audio_i2s.out != nullptr){ audio_i2s.out->SetGain(((float)audio_i2s.Settings->tx.volume / 100.0) * 4.0); audio_i2s.out->begin(); @@ -691,12 +720,12 @@ bool Xdrv42(uint32_t function) { bool result = false; switch (function) { - case FUNC_COMMAND: - result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command); - break; case FUNC_INIT: I2sInit(); break; + case FUNC_COMMAND: + result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command); + break; case FUNC_WEB_ADD_MAIN_BUTTON: //MP3ShowStream(); break; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_audio.ino index 36e26c18e..3e25ac097 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_audio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_audio.ino @@ -308,6 +308,19 @@ extern "C" { be_return_nil(vm); } #endif // USE_UFILESYS + + + // ---------------------------------------------------------------------- + // AudioInputI2S.init() -> instance + void* be_audio_input_i2s_init(void) { + return audio_i2s.out; // return the singleton of TasmotaAudioOutputI2S which is already initialized + } + + // AudioInputI2S.deinit()-> void + void be_audio_input_i2s_deinit(TasmotaAudioOutputI2S * out) { + out->stop(); + } + } #endif // USE_I2S_AUDIO_BERRY