diff --git a/.github/workflows/build_latest.yaml b/.github/workflows/build_latest.yaml index 7d3fbed..64ad9ba 100644 --- a/.github/workflows/build_latest.yaml +++ b/.github/workflows/build_latest.yaml @@ -16,7 +16,7 @@ jobs: with: files: | config/satellite1.factory.yaml - esphome-version: 2024.11.2 + esphome-version: 2024.12.2 release-summary: develop-branch release-url: release-version: diff --git a/.github/workflows/build_release.yaml b/.github/workflows/build_release.yaml index b85cf99..7e980c8 100644 --- a/.github/workflows/build_release.yaml +++ b/.github/workflows/build_release.yaml @@ -89,7 +89,7 @@ jobs: with: files: | config/satellite1.factory.yaml - esphome-version: 2024.11.2 + esphome-version: 2024.12.2 release-version: ${{ needs.prepare.outputs.next_tag }} push-tag: diff --git a/config/common/buttons.yaml b/config/common/buttons.yaml index 1acd21f..6485f74 100644 --- a/config/common/buttons.yaml +++ b/config/common/buttons.yaml @@ -32,15 +32,6 @@ event: binary_sensor: - - platform: gpio - id: line_out_sensor - pin: - pcm5122: - pin: 4 - inverted: false - name: "line-out jack" - icon: "mdi:audio-input-stereo-minijack" - - platform: gpio id: btn_up pin: diff --git a/config/common/components.external.yaml b/config/common/components.external.yaml new file mode 100644 index 0000000..3b9adfb --- /dev/null +++ b/config/common/components.external.yaml @@ -0,0 +1,9 @@ +substitutions: + ext_comp_repo_ref: develop + +external_components: + - source: + type: git + url: https://github.com/FutureProofHomes/Satellite1-ESPHome + ref: ${ext_comp_repo_ref} + components: [ i2s_audio, satellite1, memory_flasher, tas2780, pcm5122, fusb302b ] diff --git a/config/common/core_board.yaml b/config/common/core_board.yaml index 031aeba..c548fa8 100644 --- a/config/common/core_board.yaml +++ b/config/common/core_board.yaml @@ -22,10 +22,19 @@ esp32: CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY: "n" CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1: "y" + CONFIG_LWIP_TCP_MSS: "1460" + CONFIG_LWIP_TCP_WND_DEFAULT: "35040" CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP: "y" + + CONFIG_LWIP_TCPIP_RECVMBOX_SIZE: "32" + CONFIG_LWIP_TCP_RECVMBOX_SIZE: "26" + CONFIG_LWIP_UDP_RECVMBOX_SIZE: "6" + CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM: "16" CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM: "512" + CONFIG_ESP32_WIFI_RX_BA_WIN: "32" # = min(2*STATIC_RX_BUFFER_NUM, DYNAMIC_RX_BUFFER_NUM) + # BT CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y" CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y" diff --git a/config/common/debug.yaml b/config/common/debug.yaml index e90a6c6..e7aca20 100644 --- a/config/common/debug.yaml +++ b/config/common/debug.yaml @@ -1,13 +1,14 @@ - -external_components: - - source: - type: local - path: ../esphome/components - components: [ version ] - debug: update_interval: 5s +# # OPTIONAL Persist logs to a an MQTT broker so you can go back in time and inspect them. Read more here: https://esphome.io/components/mqtt.html +# mqtt: +# broker: homeassistant.local +# port: 1883 +# username: +# password: +# # topic_prefix: + text_sensor: - platform: debug device: diff --git a/config/common/developer.yaml b/config/common/developer.yaml new file mode 100644 index 0000000..42b4104 --- /dev/null +++ b/config/common/developer.yaml @@ -0,0 +1,71 @@ +# This is a developer configuration that enables the UDP stream component and adds switches to toggle between the ASR and Comm microphones. +# You can read more about how to use this configuration here: https://github.com/FutureProofHomes/Satellite1-ESPHome/tree/develop/tests/mic_streaming + +external_components: + - source: + type: git + url: https://github.com/FutureProofHomes/Satellite1-ESPHome + ref: develop + components: [ udp_stream ] + + +# Separate UDP Stream configurations for each microphone +udp_stream: + id: udp_streamer + microphone: asr_mic # Default microphone to prevent validation errors + + +# Switches to toggle between ASR and Comm microphones +switch: + - platform: template + name: "UDP Stream ASR Mic" + entity_category: "diagnostic" + id: use_asr_mic + icon: mdi:microphone + restore_mode: RESTORE_DEFAULT_OFF + optimistic: true + on_turn_on: + - logger.log: "Switching to ASR Mic for UDP stream" + - lambda: |- + if (id(udp_streamer).is_running()) { + id(udp_streamer).request_stop(); // Stop current stream + delay(500); // Wait for stream to stop + } + id(udp_streamer).set_microphone(id(asr_mic)); // Set ASR mic + id(udp_streamer).request_start(true); // Start stream with ASR mic + - switch.turn_off: use_comm_mic # Turn off the other switch + + on_turn_off: + - logger.log: "Turning off ASR Mic UDP stream" + - lambda: |- + if (id(udp_streamer).is_running()) { + id(udp_streamer).request_stop(); // Stop the stream when ASR mic is turned off + } + - platform: template + name: "UDP Stream Comm Mic" + entity_category: "diagnostic" + id: use_comm_mic + icon: mdi:microphone + restore_mode: RESTORE_DEFAULT_OFF + optimistic: true + on_turn_on: + - logger.log: "Switching to Comm Mic for UDP stream" + - micro_wake_word.stop: + - delay: 500ms + - lambda: |- + if (id(udp_streamer).is_running()) { + id(udp_streamer).request_stop(); // Stop current stream + delay(500); // Wait for stream to stop + } + id(udp_streamer).set_microphone(id(comm_mic)); // Set Comm mic + id(udp_streamer).request_start(true); // Start stream with Comm mic + - switch.turn_off: use_asr_mic # Turn off the other switch + + on_turn_off: + - logger.log: "Turning off Comm Mic UDP stream" + - lambda: |- + if (id(udp_streamer).is_running()) { + id(udp_streamer).request_stop(); // Stop the stream when Comm mic is turned off + } + - delay: 500ms + - micro_wake_word.start: \ No newline at end of file diff --git a/config/common/led_ring.yaml b/config/common/led_ring.yaml index 65b190c..a8454e2 100644 --- a/config/common/led_ring.yaml +++ b/config/common/led_ring.yaml @@ -5,6 +5,16 @@ globals: type: int restore_value: no initial_value: '0' + # Global variable tracking if the jack has been plugged touched. + - id: jack_plugged_recently + type: bool + restore_value: no + initial_value: 'false' + # Global variable tracking if the jack has been unplugged touched. + - id: jack_unplugged_recently + type: bool + restore_value: no + initial_value: 'false' light: # Hardware LED ring. Not used because remapping needed @@ -520,6 +530,10 @@ script: id(control_leds_volume_buttons_touched).execute(); } else if (id(btn_action).state) { id(control_leds_action_button_touched).execute(); + } else if (id(jack_plugged_recently)) { + id(control_leds_jack_plugged_recently).execute(); + } else if (id(jack_unplugged_recently)) { + id(control_leds_jack_unplugged_recently).execute(); } else if (id(warning)) { id(control_leds_warning).execute(); } else if (id(timer_ringing).state) { diff --git a/config/common/media_player.yaml b/config/common/media_player.yaml index 00c034c..fab45d8 100644 --- a/config/common/media_player.yaml +++ b/config/common/media_player.yaml @@ -1,9 +1,81 @@ audio_dac: - platform: pcm5122 + id: line_out_dac address: 0x4D - platform: tas2780 + id: speaker_dac address: 0x3F + - platform: satellite1 + id: dac_proxy + speaker_dac: speaker_dac + line_out_dac: line_out_dac + + on_speaker_activated: + # if speaker was used previously but is not available try line-out + - lambda: | + if( id(line_out_sensor).state && id(pd_fusb302b).contract_voltage < 9 ){ + id(dac_proxy).activate_line_out(); + } + + on_line_out_activated: + # if line_out was used previously but is not available try speaker + - lambda: | + if( !id(line_out_sensor).state && id(pd_fusb302b).contract_voltage >= 9 ){ + id(dac_proxy).activate_speaker(); + } + + on_state_change: + - media_player.volume_set: + volume: !lambda return id(dac_proxy).volume(); + - lambda: | + if( id(dac_proxy).is_muted() ){ + id(nabu_media_player)->make_call() + .set_command(MEDIA_PLAYER_COMMAND_MUTE) + .perform(); + } + + +binary_sensor: + - platform: gpio + id: line_out_sensor + pin: + pcm5122: + pin: 4 + inverted: false + name: "line-out jack" + icon: "mdi:audio-input-stereo-minijack" + filters: + - delayed_on: 200ms + - delayed_off: 200ms + + on_press: + - dac_proxy.activate_line_out: + - lambda: id(jack_plugged_recently) = true; + - script.execute: control_leds + - delay: 200ms + - script.execute: + id: play_sound + priority: false + sound_file: !lambda return id(jack_connected_sound); + - delay: 800ms + - lambda: id(jack_plugged_recently) = false; + - script.execute: control_leds + + + on_release: + - dac_proxy.activate_speaker: + - lambda: id(jack_unplugged_recently) = true; + - script.execute: control_leds + - delay: 200ms + - script.execute: + id: play_sound + priority: false + sound_file: !lambda return id(jack_disconnected_sound); + - delay: 800ms + - lambda: id(jack_unplugged_recently) = false; + - script.execute: control_leds + fusb302b: id: pd_fusb302b @@ -44,9 +116,10 @@ media_player: sample_rate: 48000 internal: false speaker: + audio_dac: dac_proxy volume_increment: 0.05 - volume_min: 0.4 - volume_max: 0.85 + volume_min: 0.1 + volume_max: 1. on_mute: - script.execute: control_leds on_unmute: @@ -92,7 +165,10 @@ media_player: file: https://github.com/esphome/home-assistant-voice-pe/raw/dev/sounds/timer_finished.flac - id: wake_word_triggered_sound file: https://github.com/esphome/home-assistant-voice-pe/raw/dev/sounds/wake_word_triggered.flac - + - id: jack_connected_sound + file: https://github.com/esphome/home-assistant-voice-pe/raw/dev/sounds/jack_connected.flac + - id: jack_disconnected_sound + file: https://github.com/esphome/home-assistant-voice-pe/raw/dev/sounds/jack_disconnected.flac script: diff --git a/config/common/mmwave_ld2410.yaml b/config/common/mmwave_ld2410.yaml index 3ac77f3..1b3f833 100644 --- a/config/common/mmwave_ld2410.yaml +++ b/config/common/mmwave_ld2410.yaml @@ -7,14 +7,156 @@ uart: parity: NONE stop_bits: 1 +button: + - platform: ld2410 + factory_reset: + name: "LD2410 factory reset" + restart: + name: "LD2410 sensor restart" + query_params: + name: LD2410 update sensors + +select: + - platform: ld2410 + distance_resolution: + name: distance resolution + light_function: + name: light function + disabled_by_default: true + +sensor: + - platform: ld2410 + moving_distance: + name : Moving Distance + still_distance: + name: Still Distance + moving_energy: + disabled_by_default: true + name: Moving Energy + still_energy: + disabled_by_default: true + name: Still Energy + detection_distance: + disabled_by_default: true + name: Detection Distance + light: + name: illuminance level + disabled_by_default: true + g0: + move_energy: + name: g0 moving energy + still_energy: + name: g0 still energy + g1: + move_energy: + name: g1 moving energy + still_energy: + name: g1 still energy + g2: + move_energy: + name: g2 moving energy + still_energy: + name: g2 still energy + g3: + move_energy: + name: g3 moving energy + still_energy: + name: g3 still energy + g4: + move_energy: + name: g4 moving energy + still_energy: + name: g4 still energy + g5: + move_energy: + name: g5 moving energy + still_energy: + name: g5 still energy + g6: + move_energy: + name: g6 moving energy + still_energy: + name: g6 still energy + g7: + move_energy: + name: g7 moving energy + still_energy: + name: g7 still energy + g8: + move_energy: + name: g8 moving energy + still_energy: + name: g8 still energy + binary_sensor: - platform: ld2410 has_target: - name: Room Presence - id: room_presence + name: Presence has_moving_target: name: Moving Target has_still_target: name: Still Target - out_pin_presence_status: - name: out pin presence status \ No newline at end of file + +number: + - platform: ld2410 + timeout: + name: timeout + max_move_distance_gate: + name: max moving distance gate + max_still_distance_gate: + name: max still distance gate + light_threshold: + name: light threshold + disabled_by_default: true + g0: + move_threshold: + name: g0 moving threshold + still_threshold: + name: g0 still threshold + g1: + move_threshold: + name: g1 moving threshold + still_threshold: + name: g1 still threshold + g2: + move_threshold: + name: g2 moving threshold + still_threshold: + name: g2 still threshold + g3: + move_threshold: + name: g3 moving threshold + still_threshold: + name: g3 still threshold + g4: + move_threshold: + name: g4 moving threshold + still_threshold: + name: g4 still threshold + g5: + move_threshold: + name: g5 moving threshold + still_threshold: + name: g5 still threshold + g6: + move_threshold: + name: g6 moving threshold + still_threshold: + name: g6 still threshold + g7: + move_threshold: + name: g7 moving threshold + still_threshold: + name: g7 still threshold + g8: + move_threshold: + name: g8 moving threshold + still_threshold: + name: g8 still threshold + +switch: + - platform: ld2410 + engineering_mode: + name: "engineering mode" + bluetooth: + name: "control bluetooth" \ No newline at end of file diff --git a/config/common/mmwave_ld2450.yaml b/config/common/mmwave_ld2450.yaml new file mode 100644 index 0000000..f1e4be3 --- /dev/null +++ b/config/common/mmwave_ld2450.yaml @@ -0,0 +1,196 @@ +# =========================================================================================== +# # The below configuration is a PR for the LD2450 (V2.02.23090617) that is waiting to be merged with ESPHome. +# # NOTE: This sensor only works when running V2.02.23090617 of the LD2450 firmware. +# # https://deploy-preview-3327--esphome.netlify.app/components/sensor/ld2450.html?highlight=ld2450 +# =========================================================================================== + +external_components: + - source: + type: git + url: https://github.com/hareeshmu/esphome + ref: ld2450 + components: [ ld2450 ] + + +uart: + id: uart_ld2450 + tx_pin: GPIO43 + rx_pin: GPIO44 + baud_rate: 256000 + parity: NONE + stop_bits: 1 + + +ld2450: + id: ld2450_radar + uart_id: uart_ld2450 + throttle: 1000ms + + +binary_sensor: + - platform: ld2450 + ld2450_id: ld2450_radar + has_target: + name: Presence + has_moving_target: + name: Moving Target + has_still_target: + name: Still Target + + +number: + - platform: ld2450 + ld2450_id: ld2450_radar + presence_timeout: + name: "Timeout" + zone_1: + x1: + name: Zone-1 X1 + y1: + name: Zone-1 Y1 + x2: + name: Zone-1 X2 + y2: + name: Zone-1 Y2 + zone_2: + x1: + name: Zone-2 X1 + y1: + name: Zone-2 Y1 + x2: + name: Zone-2 X2 + y2: + name: Zone-2 Y2 + zone_3: + x1: + name: Zone-3 X1 + y1: + name: Zone-3 Y1 + x2: + name: Zone-3 X2 + y2: + name: Zone-3 Y2 + + +switch: + - platform: ld2450 + ld2450_id: ld2450_radar + bluetooth: + name: "Bluetooth" + multi_target: + name: Multi Target Tracking" + + +select: + - platform: ld2450 + ld2450_id: ld2450_radar + baud_rate: + name: "Baud rate" + zone_type: + name: "Zone Type" + + +button: + - platform: ld2450 + ld2450_id: ld2450_radar + factory_reset: + name: "LD2450 Factory Reset" + entity_category: "config" + restart: + name: "LD2450 Restart" + entity_category: "config" + + +text_sensor: + - platform: ld2450 + ld2450_id: ld2450_radar + version: + name: "LD2450 Firmware" + mac_address: + name: "LD2450 BT MAC" + target_1: + direction: + name: "Target-1 Direction" + target_2: + direction: + name: "Target-2 Direction" + target_3: + direction: + name: "Target-3 Direction" + + +sensor: + - platform: ld2450 + ld2450_id: ld2450_radar + target_count: + name: Presence Target Count + - platform: ld2450 + ld2450_id: ld2450_radar + still_target_count: + name: Still Target Count + - platform: ld2450 + ld2450_id: ld2450_radar + moving_target_count: + name: Moving Target Count + - platform: ld2450 + ld2450_id: ld2450_radar + target_1: + x: + name: Target-1 X + y: + name: Target-1 Y + speed: + name: Target-1 Speed + angle: + name: Target-1 Angle + distance: + name: Target-1 Distance + resolution: + name: Target-1 Resolution + target_2: + x: + name: Target-2 X + y: + name: Target-2 Y + speed: + name: Target-2 Speed + angle: + name: Target-2 Angle + distance: + name: Target-2 Distance + resolution: + name: Target-2 Resolution + target_3: + x: + name: Target-3 X + y: + name: Target-3 Y + speed: + name: Target-3 Speed + angle: + name: Target-3 Angle + distance: + name: Target-3 Distance + resolution: + name: Target-3 Resolution + zone_1: + target_count: + name: Zone-1 All Target Count + still_target_count: + name: Zone-1 Still Target Count + moving_target_count: + name: Zone-1 Moving Target Count + zone_2: + target_count: + name: Zone-2 All Target Count + still_target_count: + name: Zone-2 Still Target Count + moving_target_count: + name: Zone-2 Moving Target Count + zone_3: + target_count: + name: Zone-3 All Target Count + still_target_count: + name: Zone-3 Still Target Count + moving_target_count: + name: Zone-3 Moving Target Count \ No newline at end of file diff --git a/config/common/voice_assistant.yaml b/config/common/voice_assistant.yaml index 0befdb8..d834ae4 100644 --- a/config/common/voice_assistant.yaml +++ b/config/common/voice_assistant.yaml @@ -114,7 +114,9 @@ voice_assistant: on_error: - if: condition: - lambda: return !id(init_in_progress); + and: + - lambda: return !id(init_in_progress); + - lambda: return code != "duplicate_wake_up_detected"; then: - lambda: id(voice_assistant_phase) = ${voice_assist_error_phase_id}; - script.execute: control_leds diff --git a/config/satellite1.base.yaml b/config/satellite1.base.yaml index 8220a80..fe4de5c 100644 --- a/config/satellite1.base.yaml +++ b/config/satellite1.base.yaml @@ -12,6 +12,7 @@ substitutions: project_name: Satellite1 component_name: Core + esp32_fw_version: dev xmos_fw_version: "v1.0.1" built_for_core_hw_version: "v1.0.0-beta.1" built_for_hat_hw_version: "v1.0.0-beta.1" @@ -39,7 +40,11 @@ esphome: name: ${node_name} name_add_mac_suffix: true friendly_name: ${friendly_name} - min_version: 2024.11.2 + min_version: 2024.12.2 + + project: + name: ${company_name}.${project_name} + version: ${esp32_fw_version} on_boot: - priority: 375 @@ -63,7 +68,7 @@ external_components: - source: type: git url: https://github.com/FutureProofHomes/home-assistant-voice-pe - ref: 2024.12.3-fph.0 + ref: 2024.12.10-fph.1 components: [ microphone, voice_assistant, audio_dac, media_player, nabu, micro_wake_word ] ota: @@ -72,6 +77,7 @@ ota: dashboard_import: package_import_url: github://futureproofhomes/satellite1-esphome/config/satellite1.yaml@develop + import_full_config: true packages: @@ -85,7 +91,11 @@ packages: timer: !include common/timer.yaml led_ring: !include common/led_ring.yaml - + ## OPTIONAL COMPONENTS + # mmwave_ld2410: !include common/mmwave_ld2410.yaml + # mmwave_ld2450: !include common/mmwave_ld2450.yaml + # debug: !include common/debug.yaml + # developer: !include common/developer.yaml http_request: diff --git a/config/satellite1.factory.yaml b/config/satellite1.factory.yaml index f43bf3f..c9d1dfe 100644 --- a/config/satellite1.factory.yaml +++ b/config/satellite1.factory.yaml @@ -96,9 +96,11 @@ switch: on_turn_on: - logger.log: "OTA updates set to use Beta firmware" - lambda: id(update_http_request).set_source_url("https://raw.githubusercontent.com/FutureProofHomes/Documentation/refs/heads/main/manifest-beta.json"); + - component.update: update_http_request on_turn_off: - logger.log: "OTA updates set to use Production firmware" - lambda: id(update_http_request).set_source_url("https://raw.githubusercontent.com/FutureProofHomes/Documentation/refs/heads/main/manifest.json"); + - component.update: update_http_request external_components: diff --git a/config/satellite1.yaml b/config/satellite1.yaml index f79ea2a..47f0ae1 100644 --- a/config/satellite1.yaml +++ b/config/satellite1.yaml @@ -1,30 +1,53 @@ substitutions: - company_name: FutureProofHomes - project_name: Satellite1 + name: satellite1 + friendly_name: Satellite1 + xmos_fw_version: "v1.0.1" - esp32_fw_version: dev - -packages: - fph-satellite1: !include satellite1.base.yaml - - #OPTIONAL COMPONENTS - # mmwave_ld2410: !include common/mmwave_ld2410.yaml - # debug: !include common/debug.yaml + # OPTIONALLY, set the log level to debug, info, warn, error + log_level: debug esphome: - project: - name: ${company_name}.${project_name} - version: ${esp32_fw_version} + name: ${name} + name_add_mac_suffix: true + friendly_name: ${friendly_name} +packages: + FutureProofHomes.Satellite1: + url: https://github.com/futureproofhomes/satellite1-esphome + ref: develop + refresh: 1s + files: + # Main config files, don't remove + - config/satellite1.base.yaml + - config/common/components.external.yaml + + ## OPTIONALLY, uncomment if you have the smaller LD2410 mmWave sensor connected to your HAT. + #- config/common/mmwave_ld2410.yaml + + ## OPTIONALLY, uncomment if you have the larger LD2450 mmWave sensor connected to your HAT. + #- config/common/mmwave_ld2450.yaml -external_components: - - source: - type: git - url: https://github.com/FutureProofHomes/Satellite1-ESPHome - ref: develop - components: [ i2s_audio, satellite1, memory_flasher, tas2780, pcm5122, fusb302b ] - + ## OPTIONALLY, uncomment if want extra memory, wifi and xmos control of the device. + #- config/common/debug.yaml + logger: - deassert_rts_dtr: true - hardware_uart : USB_SERIAL_JTAG - level: debug + level: ${log_level} + +## OPTIONALLY, enable transport encryption for the API layer by uncommenting the following lines +## and replacing "REPLACE_BY_32_BIT_RANDOM_KEY" with a 32-character random key. +## For more information, refer to the ESPHome documentation: +## https://esphome.io/components/api.html +## Note: The documentation also provides a tool to generate a random key + +#api: +# encryption: +# key: REPLACE_BY_32_BIT_RANDOM_KEY + +## OPTIONALLY, override the default Wi-Fi credentials provisioned on the Satellite1 device +## during setup by using your Wi-Fi credentials stored in the `secrets.yaml` file of the +## ESPHome dashboard. Uncomment the below lines to enable this feature, allowing you +## to manage Wi-Fi credentials centrally for all your devices. + +#wifi: +# ssid: !secret wifi_ssid +# password: !secret wifi_password \ No newline at end of file diff --git a/esphome/components/fusb302b/fusb302b.cpp b/esphome/components/fusb302b/fusb302b.cpp index 8512db7..cb26ed6 100644 --- a/esphome/components/fusb302b/fusb302b.cpp +++ b/esphome/components/fusb302b/fusb302b.cpp @@ -55,8 +55,10 @@ static void msg_reader_task(void *params){ PDEventInfo event_info; PDMsg &msg = event_info.msg; - + #if FUSB_DEBUG_PRINT printf("MSG READER TASK STARTED \n"); + #endif + fusb302b->enable_auto_crc(); fusb302b->fusb_reset_(); @@ -68,13 +70,16 @@ static void msg_reader_task(void *params){ while( !(regs.status1 & FUSB_STATUS1_RX_EMPTY) ){ if( fusb302b->read_message_(msg) ){ xQueueSend(pd_message_queue, &event_info, 0); - } else { + } +#if FUSB_DEBUG_PRINT + else { printf("Reading failed\n"); } +#endif fusb302b->read_status_register(FUSB_STATUS1, regs.status1); } } - +#if FUSB_DEBUG_PRINT if ( regs.interrupta & FUSB_INTERRUPTA_I_HARDRST){ event_info.event = PD_EVENT_HARD_RESET; printf(">>>FUSB_STATUS0A_HARDRST<<<\n"); @@ -86,7 +91,8 @@ static void msg_reader_task(void *params){ { event_info.event = PD_EVENT_SENDING_MSG_FAILED; printf("Message did not get acknowledged.\n"); - } + } +#endif } fusb302b->disable_auto_crc(); @@ -99,6 +105,23 @@ static void trigger_task(void *params){ pd_message_queue = xQueueCreate( 5, sizeof(PDEventInfo)); + gpio_num_t irq_gpio_pin = static_cast(fusb302b->irq_pin_); + + gpio_config_t io_conf; + io_conf.pin_bit_mask = (1ULL << irq_gpio_pin); + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.intr_type = GPIO_INTR_NEGEDGE ; + + gpio_config(&io_conf); + + // Install ISR service and attach the ISR handler + gpio_set_intr_type((gpio_num_t) irq_gpio_pin , GPIO_INTR_NEGEDGE ); + gpio_install_isr_service(0); + gpio_isr_handler_add( irq_gpio_pin, fusb302b_isr_handler, NULL); + + // Create the task that will wait for notifications xTaskCreatePinnedToCore( msg_reader_task, "fusb3202b_read_task", 4096, fusb302b, configMAX_PRIORITIES/2, &xReaderTaskHandle, 1 ); PDEventInfo event_info; @@ -106,7 +129,7 @@ static void trigger_task(void *params){ while(true){ if( xQueueReceive(pd_message_queue, &event_info, portMAX_DELAY) == pdTRUE ){ //delay needed for getting fusb302b ready for receiving i2c again, is this the right place though? - vTaskDelay(pdMS_TO_TICKS(1)); + //vTaskDelay(pdMS_TO_TICKS(1)); void taskENTER_CRITICAL( void ); PDMsg &msg = event_info.msg; //printf( "PD-Received new message with id: %d (%d, %d) [%u].\n", msg.id, msg.type, msg.num_of_obj, millis()); @@ -246,13 +269,7 @@ bool FUSB302B::read_status( fusb_status &status ){ void FUSB302B::check_status_(){ - static uint32_t last_check = millis(); - if( millis() - last_check < 1000 ){ - return; - } - last_check = millis(); - - + switch( this->state_){ case FUSB302_STATE_UNATTACHED: { @@ -270,6 +287,7 @@ void FUSB302B::check_status_(){ xSemaphoreGive(this->i2c_lock_); if( !connected ){ + this->state_ = FUSB302_STATE_FAILED; return; } @@ -278,24 +296,8 @@ void FUSB302B::check_status_(){ ESP_LOGD(TAG, "Statup delay reached!"); this->startup_delay_ = 0; - gpio_num_t irq_gpio_pin = static_cast(this->irq_pin_); - - gpio_config_t io_conf; - io_conf.pin_bit_mask = (1ULL << irq_gpio_pin); - io_conf.mode = GPIO_MODE_INPUT; - io_conf.pull_up_en = GPIO_PULLUP_ENABLE; - io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; - io_conf.intr_type = GPIO_INTR_NEGEDGE ; - - gpio_config(&io_conf); - - // Install ISR service and attach the ISR handler - gpio_set_intr_type((gpio_num_t) irq_gpio_pin , GPIO_INTR_NEGEDGE ); - gpio_install_isr_service(0); - gpio_isr_handler_add( irq_gpio_pin, fusb302b_isr_handler, NULL); - // Create the task that will wait for notifications - xTaskCreatePinnedToCore(trigger_task, "fusb3202b_task", 4096, this , configMAX_PRIORITIES, &xProcessTaskHandle, 0); + xTaskCreatePinnedToCore(trigger_task, "fusb3202b_task", 4096, this , 18 , &xProcessTaskHandle, 1); delay(1); } else { this->enable_auto_crc(); @@ -328,7 +330,7 @@ void FUSB302B::check_status_(){ } get_src_cap_retry_count_++; get_src_cap_time_stamp_ = millis(); - if( get_src_cap_retry_count_ < 4){ + if( get_src_cap_retry_count_ < 2){ /* clear interrupts */ this->read_status(); this->send_message_(PDMsg( pd_control_msg_type::PD_CNTRL_GET_SOURCE_CAP)); @@ -347,6 +349,8 @@ void FUSB302B::check_status_(){ } } break; + case FUSB302_STATE_FAILED: + break; } } @@ -496,9 +500,11 @@ bool FUSB302B::send_message_(const PDMsg &msg){ int err = this->write_register( FUSB_FIFOS, buf, pbuf - buf); + #if FUSB_DEBUG_PRINT if( err != i2c::ERROR_OK ){ printf("Sending Message (%d) failed err: %d.\n", (int) msg.type, err ); } + #endif // else { // printf("Sent Message (%d) id: %d. [%d] \n", (int) msg.type, msg.id, millis() ); // } diff --git a/esphome/components/fusb302b/fusb302b.h b/esphome/components/fusb302b/fusb302b.h index c289783..6d15e02 100644 --- a/esphome/components/fusb302b/fusb302b.h +++ b/esphome/components/fusb302b/fusb302b.h @@ -13,7 +13,8 @@ namespace power_delivery { enum FUSB302_state_t { FUSB302_STATE_UNATTACHED = 0, - FUSB302_STATE_ATTACHED + FUSB302_STATE_ATTACHED, + FUSB302_STATE_FAILED }; typedef union { @@ -43,7 +44,7 @@ class FUSB302B : public PowerDelivery, public Component, protected i2c::I2CDevic bool read_status(){ fusb_status regs; return read_status(regs); } bool read_status(fusb_status &status); - bool read_status_register( uint8_t register, uint8_t &value); + bool read_status_register( uint8_t reg, uint8_t &value); void set_irq_pin(int irq_pin){this->irq_pin_ = irq_pin;} @@ -67,7 +68,7 @@ class FUSB302B : public PowerDelivery, public Component, protected i2c::I2CDevic uint32_t startup_delay_{0}; - +int irq_pin_{0}; protected: void publish_() override { @@ -78,7 +79,7 @@ class FUSB302B : public PowerDelivery, public Component, protected i2c::I2CDevic SemaphoreHandle_t i2c_lock_; - int irq_pin_{0}; + }; } diff --git a/esphome/components/fusb302b/pd.cpp b/esphome/components/fusb302b/pd.cpp index 0cf4571..90282dd 100644 --- a/esphome/components/fusb302b/pd.cpp +++ b/esphome/components/fusb302b/pd.cpp @@ -137,7 +137,7 @@ void PowerDelivery::set_ams(bool ams){ } bool PowerDelivery::check_ams(){ - if( millis() - this->active_ams_timer_ > 2000 ){ + if( this->active_ams_ && (millis() - this->active_ams_timer_ > 2000) ){ this->active_ams_ = false; } return this->active_ams_; diff --git a/esphome/components/i2s_audio/i2s_audio.cpp b/esphome/components/i2s_audio/i2s_audio.cpp index 6a84b71..538822f 100644 --- a/esphome/components/i2s_audio/i2s_audio.cpp +++ b/esphome/components/i2s_audio/i2s_audio.cpp @@ -52,7 +52,7 @@ bool I2SAudioComponent::claim_access_(uint8_t access){ } this->unlock(); return success; - } +} bool I2SAudioComponent::release_access_(uint8_t access){ this->lock(); @@ -166,8 +166,8 @@ i2s_driver_config_t I2SSettings::get_i2s_cfg() const { .channel_format = this->channel_fmt_, .communication_format = I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 6, - .dma_buf_len = 512, + .dma_buf_count = 4, + .dma_buf_len = 480, .use_apll = false, .tx_desc_auto_clear = true, .fixed_mclk = I2S_PIN_NO_CHANGE, diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index fa052c8..8fed96b 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -13,13 +13,13 @@ namespace esphome { namespace i2s_audio { -static const uint8_t DMA_BUFFER_DURATION_MS = 15; +static const uint8_t DMA_BUFFER_DURATION_MS = 10; static const size_t DMA_BUFFERS_COUNT = 4; -static const size_t TASK_DELAY_MS = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT / 2; +static const size_t TASK_DELAY_MS = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT ; static const size_t TASK_STACK_SIZE = 4096; -static const ssize_t TASK_PRIORITY = 23; +static const ssize_t TASK_PRIORITY = 17; static const size_t I2S_EVENT_QUEUE_COUNT = DMA_BUFFERS_COUNT + 1; @@ -239,7 +239,7 @@ void I2SAudioSpeaker::speaker_task(void *params) { const ssize_t bytes_per_sample = audio_stream_info.get_bytes_per_sample(); const uint8_t number_of_channels = audio_stream_info.channels; - const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * this_speaker->sample_rate_ / 1000 * + const size_t dma_buffers_size = 3 * DMA_BUFFER_DURATION_MS * this_speaker->sample_rate_ / 1000 * bytes_per_sample * number_of_channels; const size_t ring_buffer_size = this_speaker->buffer_duration_ms_ * this_speaker->sample_rate_ / 1000 * bytes_per_sample * number_of_channels; @@ -285,7 +285,7 @@ void I2SAudioSpeaker::speaker_task(void *params) { size_t bytes_to_read = dma_buffers_size; size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, bytes_to_read, pdMS_TO_TICKS(TASK_DELAY_MS)); - + if (bytes_read > 0) { size_t bytes_written = 0; diff --git a/esphome/components/pcm5122/pcm5122.cpp b/esphome/components/pcm5122/pcm5122.cpp index eea88b3..edb38b1 100644 --- a/esphome/components/pcm5122/pcm5122.cpp +++ b/esphome/components/pcm5122/pcm5122.cpp @@ -46,6 +46,8 @@ void PCM5122::setup(){ pll_ref &= ~(7 << 4); pll_ref |= (1 << 4); this->reg(0x0D) = pll_ref; + + this->set_mute_on(); } void PCM5122::dump_config(){ @@ -76,7 +78,6 @@ float PCM5122::volume() { } bool PCM5122::write_mute_() { - ESP_LOGD(TAG, "DEBUG TEST is_muted_ is %d", is_muted()); uint8_t mute_byte = this->is_muted() ? 0x11 : 0x00; if (!this->write_byte(PCM5122_REG00_PAGE_SELECT, 0x00) || !this->write_byte(0x03, mute_byte)) { @@ -87,13 +88,10 @@ bool PCM5122::write_mute_() { } bool PCM5122::write_volume_() { - const uint8_t dvc_min_byte = 0x00; // check for pcm5122 - const uint8_t dvc_max_byte = 0xFF; - - // uint8_t volume_byte = - // dvc_min_byte + (this->volume_ * (dvc_max_byte - dvc_min_byte)); - // volume_byte = clamp(volume_byte, dvc_min_byte, dvc_max_byte); - uint8_t volume_byte = 0xFF - this->volume_ * 0xFF; + const uint8_t dvc_min_byte = 0x44; // 0x00: 24 dB ; 0x30: 0dB + const uint8_t dvc_max_byte = 0x99; // 0xFF: mute ; 0x94: -50dB + + const uint8_t volume_byte = dvc_min_byte + ((1. - this->volume_) * (dvc_max_byte - dvc_min_byte) + 0.5); ESP_LOGD(TAG, "Setting volume to 0x%.2x", volume_byte & 0xFF); diff --git a/esphome/components/pcm5122/pcm_gpio.cpp b/esphome/components/pcm5122/pcm_gpio.cpp index d2687ad..2e4b5d8 100644 --- a/esphome/components/pcm5122/pcm_gpio.cpp +++ b/esphome/components/pcm5122/pcm_gpio.cpp @@ -36,9 +36,11 @@ void PCMGPIOPin::digital_write(bool value) { } bool PCMGPIOPin::digital_read() { - uint8_t reg_val = this->parent_->reg(0x77).get(); - return !!(reg_val & (1 << (this->pin_ - 1))) != this->inverted_; - + optional read = this->parent_->read_byte(0x77); + if( read.has_value() ){ + this->value_ = !!(read.value() & (1 << (this->pin_ - 1))) != this->inverted_; + } + return this->value_; } } // namespace pcm5122 diff --git a/esphome/components/pcm5122/pcm_gpio.h b/esphome/components/pcm5122/pcm_gpio.h index 14696a4..5d0ac2c 100644 --- a/esphome/components/pcm5122/pcm_gpio.h +++ b/esphome/components/pcm5122/pcm_gpio.h @@ -23,6 +23,7 @@ class PCMGPIOPin : public GPIOPin, public Parented { uint8_t pin_; bool inverted_; gpio::Flags flags_; + bool value_{false}; }; } // namespace pcm5122 diff --git a/esphome/components/satellite1/audio_dac/__init__.py b/esphome/components/satellite1/audio_dac/__init__.py index e69de29..3761520 100644 --- a/esphome/components/satellite1/audio_dac/__init__.py +++ b/esphome/components/satellite1/audio_dac/__init__.py @@ -0,0 +1,100 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.components.audio_dac import AudioDac, audio_dac_ns + +from esphome.const import CONF_ID, CONF_TRIGGER_ID + +from ..satellite1 import ( + Satellite1, + Satellite1SPIService, + namespace as sat1_ns, + CONF_SATELLITE1 +) + + +CODEOWNERS = ["@gnumpi"] +DEPENDENCIES = ["satellite1"] + +DACProxy = sat1_ns.class_("DACProxy", AudioDac, Satellite1SPIService, cg.Component) + +ActivateAction = sat1_ns.class_( + "ActivateAction", automation.Action, cg.Parented.template(DACProxy) +) + + +ActivateLineOutAction = sat1_ns.class_( + "ActivateLineOutAction", automation.Action, cg.Parented.template(DACProxy) +) + +ActivateSpeakerAction = sat1_ns.class_( + "ActivateSpeakerAction", automation.Action, cg.Parented.template(DACProxy) +) + +StateTrigger = sat1_ns.class_("StateTrigger", automation.Trigger) +SpeakerActivatedTrigger = sat1_ns.class_("SpeakerActivatedTrigger", automation.Trigger.template()) +LineOutActivatedTrigger = sat1_ns.class_("LineOutActivatedTrigger", automation.Trigger.template()) + + + +CONF_SPEAKER_DAC = "speaker_dac" +CONF_LINE_OUT_DAC = "line_out_dac" + +CONF_ON_STATE_CHANGE = "on_state_change" +CONF_ON_SPEAKER_ACTIVATED = "on_speaker_activated" +CONF_ON_LINE_OUT_ACTIVATED = "on_line_out_activated" + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(DACProxy), + cv.GenerateID(CONF_SATELLITE1): cv.use_id(Satellite1), + cv.Optional(CONF_SPEAKER_DAC) : cv.use_id(AudioDac), + cv.Optional(CONF_LINE_OUT_DAC) : cv.use_id(AudioDac), + + cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), + }), + cv.Optional(CONF_ON_SPEAKER_ACTIVATED): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SpeakerActivatedTrigger), + }), + cv.Optional(CONF_ON_LINE_OUT_ACTIVATED): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LineOutActivatedTrigger), + }), + } + ) + .extend(cv.COMPONENT_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await cg.register_parented(var, config[CONF_SATELLITE1]) + if CONF_SPEAKER_DAC in config : + speaker_dac = await cg.get_variable(config[CONF_SPEAKER_DAC]) + cg.add( var.set_speaker_dac(speaker_dac) ) + if CONF_LINE_OUT_DAC in config : + lineout_dac = await cg.get_variable(config[CONF_LINE_OUT_DAC]) + cg.add( var.set_lineout_dac(lineout_dac) ) + + for conf in config.get(CONF_ON_STATE_CHANGE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_SPEAKER_ACTIVATED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_LINE_OUT_ACTIVATED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + + +DAC_ACTION_SCHEMA = automation.maybe_simple_id({cv.GenerateID(): cv.use_id(DACProxy)}) + +@automation.register_action("dac_proxy.activate", ActivateAction, DAC_ACTION_SCHEMA) +@automation.register_action("dac_proxy.activate_line_out", ActivateLineOutAction, DAC_ACTION_SCHEMA) +@automation.register_action("dac_proxy.activate_speaker", ActivateSpeakerAction, DAC_ACTION_SCHEMA) +async def tas2780_action(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var diff --git a/esphome/components/satellite1/audio_dac/automation.h b/esphome/components/satellite1/audio_dac/automation.h new file mode 100644 index 0000000..b0d5897 --- /dev/null +++ b/esphome/components/satellite1/audio_dac/automation.h @@ -0,0 +1,56 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "dac_proxy.h" + +namespace esphome { + +namespace satellite1 { + +template< typename... Ts> +class ActivateAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->activate(); } +}; + + + +template< typename... Ts> +class ActivateLineOutAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->activate_line_out(); } +}; + +template< typename... Ts> +class ActivateSpeakerAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->activate_speaker(); } +}; + + +class StateTrigger : public Trigger<> { + public: + explicit StateTrigger(DACProxy *proxy) { + proxy->add_on_state_callback([this]() { this->trigger(); }); + } +}; + + + +template class DACProxyStateTrigger : public Trigger<> { + public: + explicit DACProxyStateTrigger(DACProxy *proxy) { + proxy->add_on_state_callback([this, proxy]() { + if (proxy->active_dac == output_dac) + this->trigger(); + }); + } +}; + +using SpeakerActivatedTrigger = DACProxyStateTrigger; +using LineOutActivatedTrigger = DACProxyStateTrigger; + + + +} +} \ No newline at end of file diff --git a/esphome/components/satellite1/audio_dac/dac_proxy.cpp b/esphome/components/satellite1/audio_dac/dac_proxy.cpp new file mode 100644 index 0000000..31c0691 --- /dev/null +++ b/esphome/components/satellite1/audio_dac/dac_proxy.cpp @@ -0,0 +1,188 @@ +#include "dac_proxy.h" + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace satellite1 { + +static const char *const TAG = "dac_proxy"; + + +void DACProxy::setup(){ + ESP_LOGD(TAG, "Setting up DACProxy..."); + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); + + if (this->pref_.load(&this->restore_state_)) { + ESP_LOGD(TAG, "Read preferences from flash"); + if( this->pcm5122_ ){ + this->pcm5122_->set_volume( this->restore_state_.line_out_volume); + if( this->restore_state_.line_out_is_muted ){ + this->pcm5122_->set_mute_on(); + } + } + if( this->tas2780_ ){ + this->tas2780_->set_volume( this->restore_state_.speaker_volume); + if( this->restore_state_.speaker_is_muted ){ + this->tas2780_->set_mute_on(); + } + } + this->active_dac = (DacOutput) this->restore_state_.dac_output; + this->activate(); + } + else { + ESP_LOGW(TAG, "Preferences not found, using default settings"); + this->active_dac = SPEAKER; + this->restore_state_.dac_output = SPEAKER; + this->restore_state_.speaker_volume = .5; + this->restore_state_.speaker_is_muted = false; + this->restore_state_.line_out_volume = .5; + this->restore_state_.line_out_is_muted = false; + } + this->defer([this]() { this->state_callback_.call(); }); +} + +void DACProxy::dump_config(){ + if( this->tas2780_){ + esph_log_config(TAG, "SPEAKER-DAC, volume: %4.2f, muted: %s %s", + this->tas2780_->volume(), + this->tas2780_->is_muted() ? "true" : "false", + this->active_dac == SPEAKER ? "(active)" : "" + ); + } + if( this->pcm5122_){ + esph_log_config(TAG, "LINE-OUT-DAC, volume: %4.2f, muted: %s %s", + this->pcm5122_->volume(), + this->pcm5122_->is_muted() ? "true" : "false", + this->active_dac == LINE_OUT ? "(active)" : "" + ); + } +} + +void DACProxy::save_volume_restore_state_(){ + this->restore_state_.dac_output = this->active_dac; + if( this->active_dac == LINE_OUT && this->pcm5122_ ){ + this->restore_state_.line_out_volume = this->pcm5122_->volume(); + } + if( this->active_dac == SPEAKER && this->tas2780_ ){ + this->restore_state_.speaker_volume = this->tas2780_->volume(); + } + this->pref_.save(&this->restore_state_); +} + +void DACProxy::activate_line_out(){ + if ( this->pcm5122_ == nullptr ){ + return; + } + ESP_LOGD(TAG, "Activate Line-Out DAC."); + this->active_dac = LINE_OUT; + this->send_selected_dac_(); + if( this->tas2780_ ){ + this->tas2780_->set_mute_on(); + } + if( !this->restore_state_.line_out_is_muted ){ + this->set_mute_off(); + } + + this->defer([this]() { this->state_callback_.call(); }); + this->save_volume_restore_state_(); +} + +void DACProxy::activate_speaker(){ + if ( this->tas2780_ == nullptr ){ + return; + } + ESP_LOGD(TAG, "Activate Speaker DAC."); + this->active_dac = SPEAKER; + this->send_selected_dac_(); + if( this->pcm5122_ ){ + this->pcm5122_->set_mute_on(); + } + if( !this->restore_state_.speaker_is_muted ){ + this->set_mute_off(); + } + this->defer([this]() { this->state_callback_.call(); }); + this->save_volume_restore_state_(); +} + +void DACProxy::activate(){ + if( this->active_dac == SPEAKER ){ + if( !this->restore_state_.speaker_is_muted){ + this->set_mute_off(); + } + } else { + if( !this->restore_state_.line_out_is_muted){ + this->set_mute_off(); + } + } +} + + +bool DACProxy::set_mute_off(){ + ESP_LOGD(TAG, "set_mute_off: for %s", this->active_dac == LINE_OUT? "Line-Out" : "Speaker"); + bool ret = false; + if( this->active_dac == LINE_OUT && this->pcm5122_ ){ + ret = this->pcm5122_->set_mute_off(); + this->restore_state_.line_out_is_muted = false; + } + if( this->active_dac == SPEAKER && this->tas2780_ ){ + ret = this->tas2780_->set_mute_off(); + this->restore_state_.speaker_is_muted = false; + } + this->save_volume_restore_state_(); + return ret; +} + +bool DACProxy::set_mute_on(){ + ESP_LOGD(TAG, "set_mute_on: for %s", this->active_dac == LINE_OUT? "Line-Out" : "Speaker"); + bool ret = false; + if( this->active_dac == LINE_OUT && this->pcm5122_ ){ + ret = this->pcm5122_->set_mute_on(); + this->restore_state_.line_out_is_muted = true; + } + if( this->active_dac == SPEAKER && this->tas2780_ ){ + ret = this->tas2780_->set_mute_on(); + this->restore_state_.speaker_is_muted = true; + } + this->save_volume_restore_state_(); + return ret; +} + +bool DACProxy::set_volume(float volume){ + bool ret = false; + if( this->active_dac == LINE_OUT && this->pcm5122_ ){ + ret = this->pcm5122_->set_volume(volume); + } else if( this->active_dac == SPEAKER && this->tas2780_ ){ + ret = this->tas2780_->set_volume(volume); + } + this->save_volume_restore_state_(); + return ret; +} + +bool DACProxy::is_muted(){ + if( this->active_dac == LINE_OUT && this->pcm5122_ ){ + return this->pcm5122_->is_muted(); + } + if( this->active_dac == SPEAKER && this->tas2780_ ){ + return this->tas2780_->is_muted(); + } + return false; +} + +float DACProxy::volume(){ + if( this->active_dac == LINE_OUT && this->pcm5122_ ){ + return this->pcm5122_->volume(); + } + if( this->active_dac == SPEAKER && this->tas2780_ ){ + return this->tas2780_->volume(); + } + return 0.; +} + +void DACProxy::add_on_state_callback(std::function &&callback) { + this->state_callback_.add(std::move(callback)); +} + + +} // namespace satellite1 +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/satellite1/audio_dac/dac_proxy.h b/esphome/components/satellite1/audio_dac/dac_proxy.h new file mode 100644 index 0000000..4ebcb43 --- /dev/null +++ b/esphome/components/satellite1/audio_dac/dac_proxy.h @@ -0,0 +1,74 @@ +#pragma once + +#include "esphome/components/audio_dac/audio_dac.h" +#include "esphome/core/component.h" +#include "esphome/components/satellite1/satellite1.h" +#include "esphome/core/preferences.h" + +namespace esphome { +namespace satellite1 { + + +static const uint8_t AUDIO_SERVICER_CMD_SET_DAC = 0x00; + +enum DacOutput : uint8_t { + SPEAKER = 0, + LINE_OUT, +}; + + + +struct DACProxyRestoreState { + uint8_t dac_output; + float speaker_volume; + bool speaker_is_muted; + float line_out_volume; + bool line_out_is_muted; +}; + + +class DACProxy : public audio_dac::AudioDac, + public Component, + public Satellite1SPIService, + public EntityBase { + + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + + bool set_mute_off() override; + bool set_mute_on() override; + bool set_volume(float volume) override; + + bool is_muted() override; + float volume() override; + + void set_lineout_dac(audio_dac::AudioDac* pcm5122){ this->pcm5122_ = pcm5122; } + void set_speaker_dac(audio_dac::AudioDac* tas2780){ this->tas2780_ = tas2780; } + void add_on_state_callback(std::function &&callback); + + void activate(); + void activate_line_out(); + void activate_speaker(); + + DacOutput active_dac{SPEAKER}; +protected: + ESPPreferenceObject pref_; + DACProxyRestoreState restore_state_; + void save_volume_restore_state_(); + + void send_selected_dac_(){} + + audio_dac::AudioDac* pcm5122_{nullptr}; + audio_dac::AudioDac* tas2780_{nullptr}; + + CallbackManager state_callback_{}; +}; + + + + + +} +} \ No newline at end of file diff --git a/esphome/components/satellite1/microphone/sat1_microphone.cpp b/esphome/components/satellite1/microphone/sat1_microphone.cpp index 44f8b73..1bc9f4f 100644 --- a/esphome/components/satellite1/microphone/sat1_microphone.cpp +++ b/esphome/components/satellite1/microphone/sat1_microphone.cpp @@ -16,16 +16,16 @@ namespace esphome { namespace nabu_microphone { -static const size_t RING_BUFFER_LENGTH = 64; // Measured in milliseconds +static const size_t RING_BUFFER_LENGTH = 60; // Measured in milliseconds static const size_t QUEUE_LENGTH = 10; static const size_t NUMBER_OF_CHANNELS = 2; -static const size_t DMA_BUFFER_SIZE = 512; -static const size_t DMA_BUFFERS_COUNT = 6; +static const size_t DMA_BUFFER_SIZE = 480; //10 ms chunks +static const size_t DMA_BUFFERS_COUNT = 4; static const size_t FRAMES_IN_ALL_DMA_BUFFERS = DMA_BUFFER_SIZE * DMA_BUFFERS_COUNT; static const size_t SAMPLES_IN_ALL_DMA_BUFFERS = FRAMES_IN_ALL_DMA_BUFFERS * NUMBER_OF_CHANNELS; -static const size_t TASK_DELAY_MS = 10; +static const size_t TASK_DELAY_MS = 15; // TODO: // - Determine optimal buffer sizes (dma included) @@ -164,7 +164,7 @@ void NabuMicrophone::read_task_(void *params) { // Note, if we have 16 bit samples incoming, this requires modification ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); - int32_t *buffer = allocator.allocate(SAMPLES_IN_ALL_DMA_BUFFERS * 3); + int32_t *buffer = allocator.allocate(SAMPLES_IN_ALL_DMA_BUFFERS); std::vector> channel_0_samples; std::vector> channel_1_samples; @@ -211,7 +211,7 @@ void NabuMicrophone::read_task_(void *params) { size_t bytes_read; esp_err_t err = - i2s_read(this_microphone->parent_->get_port(), buffer, SAMPLES_IN_ALL_DMA_BUFFERS * sizeof(int32_t) * 3, + i2s_read(this_microphone->parent_->get_port(), buffer, DMA_BUFFER_SIZE * sizeof(int32_t) * 4, &bytes_read, pdMS_TO_TICKS(TASK_DELAY_MS)); if (err != ESP_OK) { event.type = TaskEventType::WARNING; @@ -292,7 +292,7 @@ void NabuMicrophone::start() { return; if (this->read_task_handle_ == nullptr) { - xTaskCreate(NabuMicrophone::read_task_, "microphone_task", 3584, (void *) this, 23, &this->read_task_handle_); + xTaskCreate(NabuMicrophone::read_task_, "microphone_task", 3584, (void *) this, 17, &this->read_task_handle_); } // TODO: Should we overwrite? If stop and start are called in quick succession, what behavior do we want diff --git a/esphome/components/tas2780/tas2780.cpp b/esphome/components/tas2780/tas2780.cpp index af2d281..35fb04c 100644 --- a/esphome/components/tas2780/tas2780.cpp +++ b/esphome/components/tas2780/tas2780.cpp @@ -11,6 +11,7 @@ static const char *const TAG = "tas2780"; static const uint8_t TAS2780_PAGE_SELECT = 0x00; // Page Select +/* PAGE 0*/ static const uint8_t TAS2780_MODE_CTRL = 0x02; static const uint8_t TAS2780_MODE_CTRL_BOP_SRC__PVDD_UVLO = 0x80; static const uint8_t TAS2780_MODE_CTRL_MODE_MASK = 0x07; @@ -18,6 +19,29 @@ static const uint8_t TAS2780_MODE_CTRL_MODE__ACTIVE = 0x00; static const uint8_t TAS2780_MODE_CTRL_MODE__ACTIVE_MUTED = 0x01; static const uint8_t TAS2780_MODE_CTRL_MODE__SFTW_SHTDWN = 0x02; +static const uint8_t TAS2780_CHNL_0 = 0x03; +static const uint8_t TAS2780_CHNL_0_CDS_MODE_SHIFT = 6; +static const uint8_t TAS2780_CHNL_0_CDS_MODE_MASK = (0x03 << TAS2780_CHNL_0_CDS_MODE_SHIFT); +static const uint8_t TAS2780_CHNL_0_AMP_LEVEL_SHIFT = 1; +static const uint8_t TAS2780_CHNL_0_AMP_LEVEL_MASK = (0x1F) << TAS2780_CHNL_0_AMP_LEVEL_SHIFT; + +static const uint8_t TAS2780_DC_BLK0 = 0x04; +static const uint8_t TAS2780_DC_BLK0_VBAT1S_MODE_SHIFT = 7; + +static const uint8_t TAS2780_TDM_CFG2 = 0x0a; +static const uint8_t TAS2780_TDM_CFG2_RX_SCFG_SHIFT = 4; +static const uint8_t TAS2780_TDM_CFG2_RX_SCFG_MASK = (3 << TAS2780_TDM_CFG2_RX_SCFG_SHIFT); +static const uint8_t TAS2780_TDM_CFG2_RX_SCFG__STEREO_DWN_MIX = (3 << TAS2780_TDM_CFG2_RX_SCFG_SHIFT); +static const uint8_t TAS2780_TDM_CFG2_RX_WLEN_SHIFT = 2; +static const uint8_t TAS2780_TDM_CFG2_RX_WLEN_MASK = (3 << TAS2780_TDM_CFG2_RX_WLEN_SHIFT); +static const uint8_t TAS2780_TDM_CFG2_RX_WLEN__16BIT = (0 << TAS2780_TDM_CFG2_RX_WLEN_SHIFT); +static const uint8_t TAS2780_TDM_CFG2_RX_WLEN__24BIT = (2 << TAS2780_TDM_CFG2_RX_WLEN_SHIFT); +static const uint8_t TAS2780_TDM_CFG2_RX_WLEN__32BIT = (3 << TAS2780_TDM_CFG2_RX_WLEN_SHIFT); +static const uint8_t TAS2780_TDM_CFG2_RX_SLEN_MASK = (3 << 0); +static const uint8_t TAS2780_TDM_CFG2_RX_SLEN__32BIT = 2; + +static const uint8_t TAS2780_DVC = 0x1a; + static const uint8_t TAS2780_INT_MASK0 = 0x3b; static const uint8_t TAS2780_INT_MASK1 = 0x3c; static const uint8_t TAS2780_INT_MASK4 = 0x3d; @@ -26,6 +50,17 @@ static const uint8_t TAS2780_INT_MASK3 = 0x41; +/* PAGE 1*/ +static const uint8_t TAS2780_INT_LDO = 0x36; + +static const uint8_t POWER_MODES[4][2] = { + {2, 0}, // PWR_MODE0: CDS_MODE=10, VBAT1S_MODE=0 + {0, 0}, // PWR_MODE1: CDS_MODE=00, VBAT1S_MODE=0 + {3, 1}, // PWR_MODE2: CDS_MODE=11, VBAT1S_MODE=1 + {1, 0}, // PWR_MODE3: CDS_MODE=01, VBAT1S_MODE=0 +}; + + void TAS2780::setup(){ // select page 0 this->reg(TAS2780_PAGE_SELECT) = 0x00; @@ -53,7 +88,12 @@ void TAS2780::setup(){ this->reg(0x0e) = 0x44; this->reg(0x0f) = 0x40; - this->reg(0x0a) = (2 << 4) | (3 << 2) | 2 ; + this->reg(TAS2780_TDM_CFG2) = ( + TAS2780_TDM_CFG2_RX_SCFG__STEREO_DWN_MIX | + TAS2780_TDM_CFG2_RX_WLEN__32BIT | + TAS2780_TDM_CFG2_RX_SLEN__32BIT + ); + this->reg(TAS2780_PAGE_SELECT) = 0x01; this->reg(0x17) = 0xc0; @@ -137,7 +177,12 @@ void TAS2780::reset(){ this->reg(TAS2780_PAGE_SELECT) = 0x00; this->reg(0x0e) = 0x44; this->reg(0x0f) = 0x40; - this->reg(0x0a) = (2 << 4) | (3 << 2) | 2 ; + + this->reg(TAS2780_TDM_CFG2) = ( + TAS2780_TDM_CFG2_RX_SCFG__STEREO_DWN_MIX | + TAS2780_TDM_CFG2_RX_WLEN__32BIT | + TAS2780_TDM_CFG2_RX_SLEN__32BIT + ); this->reg(TAS2780_PAGE_SELECT) = 0x01; this->reg(0x17) = 0xc0; @@ -210,10 +255,36 @@ float TAS2780::volume() { } bool TAS2780::write_mute_() { + if( this->is_muted_ ){ + this->reg(TAS2780_DVC) = 0xc9; + } else { + this->write_volume_(); + } return true; } bool TAS2780::write_volume_() { + /* + V_{AMP} = INPUT + A_{DVC} + A_{AMP} + + V_{AMP} is the amplifier output voltage in dBV () + INPUT: digital input amplitude as a number of dB with respect to 0 dBFS + A_{DVC}: is the digital volume control setting as a number of dB (default 0 dB) + A_{AMP}: the amplifier output level setting as a number of dBV + + DVC_LVL[7:0] : 0dB to -100dB [0x00, 0xc8] c8 = 200 + AMP_LEVEL[4:0] : @48ksps 11dBV - 21dBV [0x00, 0x14] + */ + float attenuation = (1. - this->volume_) * 90.f; + uint8_t dvc = clamp(attenuation, 0, 0xc8); + this->reg(TAS2780_DVC) = dvc; + + uint8_t amp_level = 8; // 7: 15dBV + uint8_t reg_val = this->reg(TAS2780_CHNL_0).get(); + reg_val &= ~TAS2780_CHNL_0_AMP_LEVEL_MASK; + reg_val |= amp_level << TAS2780_CHNL_0_AMP_LEVEL_SHIFT; + this->reg(TAS2780_CHNL_0) = reg_val; + return true; } diff --git a/requirements.txt b/requirements.txt index 0bc0be7..8e302e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -esphome==2024.11.2 +esphome==2024.12.2 setuptools \ No newline at end of file