# ============================================================================= # GENERIC ESP32-S3 VOIP - Dual I2S bus, no hardware codec # ============================================================================= # Generic SIP phone profile. It keeps the AEC-only audio pipeline small and # exposes standard SIP signaling plus RTP media through voip_stack. # # ESP-to-ESP calls use the unified phonebook from HA. # # Audio pipeline: # MEMS mic -> RX I2S bus -> esp_audio_stack (48kHz) -> esp_ae_rate_cvt -> esp_aec -> VoIP TX # VoIP RX (16kHz) -> resampler -> esp_audio_stack -> TX I2S bus -> class-D amp # # HARDWARE (adapt GPIOs to YOUR board): # - Board: Freenove ESP32-S3 WROOM N8R8, or equivalent S3 with octal PSRAM # - Microphone: Any I2S MEMS mic on DIN pin # - Speaker: Any I2S class-D amp on DOUT pin # - Mic and speaker use separate I2S controllers. ESP32-S3 is master on both. # - LED: SK6812/WS2812 RGB (optional) # # GPIO PINOUT (Freenove ESP32-S3 WROOM N8R8 example): # RX I2S: BCLK=GPIO9, LRCLK=GPIO10, DIN=GPIO11 (mic) # TX I2S: BCLK=GPIO12, LRCLK=GPIO13, DOUT=GPIO14 (speaker) # LED: DATA=GPIO48 # # ============================================================================= # ============================================================================= # PROJECT SETUP # ============================================================================= substitutions: # Hardware identity stays stable across YAML updates so HA entities and # ESPHome build cache are reused. name: generic-s3-dual-bus friendly_name: Generic S3 Dual Bus ext_components_source: "github://n-IA-hane/esphome-intercom@main" voip_stack_components_source: "github://n-IA-hane/esphome-voip-stack@main" audio_stack_components_source: "github://n-IA-hane/esphome-audio-stack@main" # --------------------------------------------------------------------------- # Pinout: microphone I2S bus # --------------------------------------------------------------------------- rx_i2s_bclk_pin: GPIO9 rx_i2s_lrclk_pin: GPIO10 rx_i2s_din_pin: GPIO11 # --------------------------------------------------------------------------- # Pinout: speaker I2S bus # --------------------------------------------------------------------------- tx_i2s_bclk_pin: GPIO12 tx_i2s_lrclk_pin: GPIO13 tx_i2s_dout_pin: GPIO14 # --------------------------------------------------------------------------- # Pinout: speaker amplifier and optional status LED # --------------------------------------------------------------------------- speaker_enable_pin: GPIO18 status_led_pin: GPIO48 status_led_chipset: WS2812 status_led_rgb_order: GRB packages: core: github://n-IA-hane/esphome-intercom/packages/voip_only.yaml@main voip_entities_full_duplex: github://n-IA-hane/esphome-intercom/packages/voip/entities_full_duplex.yaml@main call_settings: github://n-IA-hane/esphome-intercom/packages/voip/call_settings.yaml@main ha_api: github://n-IA-hane/esphome-intercom/packages/voip/ha_api.yaml@main phonebook_subscribe: github://n-IA-hane/esphome-intercom/packages/voip/phonebook_subscribe.yaml@main voip_transport: github://n-IA-hane/esphome-intercom/packages/voip/transport.yaml@main speaker_mute: github://n-IA-hane/esphome-intercom/packages/voip/speaker_mute.yaml@main s3_base: github://n-IA-hane/esphome-intercom/packages/platform/esp32s3_base.yaml@main native_diagnostics: github://n-IA-hane/esphome-intercom/packages/diagnostics/native.yaml@main status_led: github://n-IA-hane/esphome-intercom/packages/led/status_ws2812_single.yaml@main audio_stack_controls: github://n-IA-hane/esphome-intercom/packages/esp_audio_stack/controls.yaml@main audio_stack_aec_controls: github://n-IA-hane/esphome-intercom/packages/esp_audio_stack/aec_controls.yaml@main speaker_power: github://n-IA-hane/esphome-intercom/packages/esp_audio_stack/speaker_power_output.yaml@main # ============================================================================= # BOARD AND MEMORY # ============================================================================= # Device identity exposed to ESPHome, HA and the shared VoIP phonebook. esphome: name: ${name} friendly_name: ${friendly_name} platformio_options: board_build.flash_mode: dio # ESP32-S3 target and ESP-IDF tuning for the no-codec dual-bus reference. esp32: board: freenove_esp32_s3_wroom variant: esp32s3 framework: type: esp-idf advanced: compiler_optimization: PERF sdkconfig_options: # Allow mbedtls to allocate its working buffers in PSRAM. CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y" # Route ESP-IDF stdout (and ESPHome logger when hardware_uart=USB_SERIAL_JTAG) # to the built-in USB-Serial-JTAG peripheral. Required on boards where the # USB-C connector is wired to the S3's native USB-Serial-JTAG (e.g. Freenove, # bare ESP32-S3-DevKitC-1) rather than to an external USB-UART chip. CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG: "y" CONFIG_ESP_CONSOLE_SECONDARY_NONE: "y" # PSRAM gives VoIP/audio buffers and TLS working memory room on generic boards. psram: mode: octal speed: 80MHz # Persist user settings without excessive flash writes. preferences: flash_write_interval: 5min # ============================================================================= # EXTERNAL COMPONENTS # ============================================================================= # Project components for the no-codec AEC VoIP path. external_components: - source: ${voip_stack_components_source} components: [voip_stack] - source: ${audio_stack_components_source} components: [esp_audio_stack, esp_aec] # ============================================================================= # CONNECTIVITY # ============================================================================= # Normal Wi-Fi client mode. Power save is disabled for low-latency SIP/RTP and # reliable HA API updates. wifi: ssid: !secret wifi_ssid password: !secret wifi_password power_save_mode: NONE # OTA update endpoint used by ESPHome Dashboard and `esphome upload`. ota: - platform: esphome # USB Serial/JTAG logger. VoIP domains stay visible at INFO; noisy entity # domains stay WARN for normal field use. logger: hardware_uart: USB_SERIAL_JTAG # DEBUG keeps SIP signaling + AEC/AFE/i2s lifecycle visible. # Mute chatty ESPHome built-ins below; project components stay at DEBUG. level: INFO logs: voip_stack: INFO voip_stack.fsm: INFO voip_stack.tcp: INFO voip_stack.udp: INFO voip_stack.audio: INFO voip_stack.settings: INFO sensor: WARN text_sensor: WARN binary_sensor: WARN switch: WARN number: WARN button: WARN api: WARN api.connection: WARN component: WARN # ============================================================================= # OUTPUTS AND LIGHTS # ============================================================================= # Optional amplifier enable pin for dual-bus class-D boards. output: - platform: gpio id: speaker_enable pin: ${speaker_enable_pin} # ============================================================================= # AUDIO PIPELINE # ============================================================================= # ============================================================================= # VoIP signaling: voip_stack owns SIP INVITE/answer/hangup. # The YAML below only wires audio devices, LED feedback and local buttons. # ============================================================================= esp_audio_stack: id: audio_stack rx_bus: i2s_num: 0 i2s_lrclk_pin: ${rx_i2s_lrclk_pin} i2s_bclk_pin: ${rx_i2s_bclk_pin} i2s_din_pin: ${rx_i2s_din_pin} tx_bus: i2s_num: 1 i2s_lrclk_pin: ${tx_i2s_lrclk_pin} i2s_bclk_pin: ${tx_i2s_bclk_pin} i2s_dout_pin: ${tx_i2s_dout_pin} sample_rate: 48000 output_sample_rate: 16000 bits_per_sample: 32 slot_bit_width: 32 correct_dc_offset: true mic_channel: right rx_slot_mode: stereo processor_id: aec_processor # VoIP-only no-codec reference: keep the lighter previous-frame path. # The full-experience Freenove profile uses ring_buffer for VA/media AEC # tuning, but this preset only needs the hardware pin/channel fixes above. aec_reference: previous_frame buffers_in_psram: true audio_task_stack_in_psram: true esp_aec: id: aec_processor sample_rate: 16000 filter_length: 8 mode: fd_high_perf microphone: - platform: esp_audio_stack id: mic_main esp_audio_stack_id: audio_stack speaker: - platform: esp_audio_stack id: hw_speaker esp_audio_stack_id: audio_stack sample_rate: 48000 bits_per_sample: 16 timeout: 1s - platform: resampler id: voip_speaker_input output_speaker: hw_speaker bits_per_sample: 16 # ============================================================================= # MEDIA, ASSIST AND VOIP # ============================================================================= # SIP: the HA phonebook subscription provides SIP contacts in the unified # roster. voip_stack.start auto-binds signaling and RTP to the selected # endpoint. voip_stack: id: phone buffers_in_psram: true audio: # Primary/preferred RTP profile: 48 kHz/10 ms toward the speaker, and a # matching 10 ms TX reframe of the 16 kHz mic for peers that negotiate # ptime 10. 32 kHz/16 ms remains an alternate advertised format. tx_formats: - sample_rate: 16000 pcm_format: s16le channels: 1 frame_ms: 10 rx: sample_rate: 48000 pcm_format: s16le channels: 1 frame_ms: 10 # Additional accepted RX formats for direct ESP-to-ESP calls. These are valid # only because speaker: voip_speaker_input points at the resampler; do # not advertise additional formats when wiring voip_stack directly to a # fixed-rate mixer input. rx_formats: - sample_rate: 32000 pcm_format: s16le channels: 1 frame_ms: 16 - sample_rate: 16000 pcm_format: s16le channels: 1 frame_ms: 10 - sample_rate: 16000 pcm_format: s16le channels: 1 frame_ms: 16 - sample_rate: 16000 pcm_format: s16le channels: 1 frame_ms: 32 microphone: mic_main speaker: voip_speaker_input dc_offset_removal: false ringing_timeout: 30s delete_contact_missing_from: updates_number: 1 # ============================================================================= # RUNTIME LOGIC AND ENTITIES # ============================================================================= switch: select: - platform: template name: AEC Mode id: aec_mode_select icon: mdi:tune-vertical # Keep runtime switching within the full-duplex AEC engine. options: - "fd_low_cost" - "fd_high_perf" initial_option: "fd_high_perf" optimistic: false restore_value: true set_action: - if: condition: not: voip_stack.is_idle: then: - logger.log: level: WARN format: "Cannot switch AEC mode during active VoIP call" else: - logger.log: format: "Switching AEC mode to %s" args: ['x.c_str()'] - esp_audio_stack.stop: audio_stack - esp_aec.set_mode: id: aec_processor mode: !lambda 'return x;' - esp_audio_stack.start: audio_stack # Reflect actual live mode (covers rejection and silent hardware mode). - lambda: |- id(aec_mode_select).publish_state(id(aec_processor).get_mode_name());