Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Voice Assistant-Add support for the espressif esp32-korvo-v1.1 #2430

Open
rarroyo6 opened this issue Oct 15, 2023 · 107 comments
Open

Voice Assistant-Add support for the espressif esp32-korvo-v1.1 #2430

rarroyo6 opened this issue Oct 15, 2023 · 107 comments

Comments

@rarroyo6
Copy link

Describe the problem you have/What new integration you would like

Add support for the espressif esp32-korvo-v1.1, see documentation here: https://github.com/espressif/esp-skainet/blob/master/docs/en/hw-reference/esp32/user-guide-esp32-korvo-v1.1.md
Please describe your use case for this integration and alternatives you've tried:

This will provide another option for a voice assistant with all the needed features built-in.
Additional context

This board is available, relatively inexpensive, and has a microphone array, leds, and speaker output.

@trip5
Copy link

trip5 commented Oct 17, 2023

Searching, got me intrigued... The Korvo appears to be It looks like it may be using the same codebase as the what Espressif calls ESP-BOX (ie the ESP32-S3-BOX)... which puts it under the scope of #2239 - doesn't it?

@huishizhao
Copy link

hope someone could study how to add it into ESPHOME. it's it include a ADC es7210 and es8311CODEC and LED control chip.

@rarroyo6
Copy link
Author

Got one on order, will start playing with it as soon as I receive it.

@joey-90
Copy link

joey-90 commented Oct 20, 2023

I managed to get an ESP32-s3-Korvo-1 wooing with the setup posted https://github.com/joey-90/ESP32-S3-Korvo-1---Voice-Assistant/blob/main/voiceassistant.yaml

I can't get wake word detection working at the moment though

@asve99
Copy link

asve99 commented Oct 24, 2023

I managed to get an ESP32-s3-Korvo-1 wooing with the setup posted https://github.com/joey-90/ESP32-S3-Korvo-1---Voice-Assistant/blob/main/voiceassistant.yaml

I can't get wake word detection working at the moment though

Thanks @joey-90 for this. I copied the contents of the yaml from your repo and tried to compile/load for my board but I get these msgs when compiling. The firmware loads into the board but none of the i2s components load/work. Have you seen these warnings/errors when compiling? I am running ESPHome 2023.10.3.

src/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp: In member function 'void esphome::i2s_audio::I2SAudioMicrophone::start_()':
src/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp:62:3: warning: missing initializer for member 'i2s_driver_config_t::chan_mask' [-Wmissing-field-initializers]
   };
   ^
src/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp:62:3: warning: missing initializer for member 'i2s_driver_config_t::total_chan' [-Wmissing-field-initializers]
src/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp:62:3: warning: missing initializer for member 'i2s_driver_config_t::left_align' [-Wmissing-field-initializers]
src/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp:62:3: warning: missing initializer for member 'i2s_driver_config_t::big_edin' [-Wmissing-field-initializers]
src/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp:62:3: warning: missing initializer for member 'i2s_driver_config_t::bit_order_msb' [-Wmissing-field-initializers]
src/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp:62:3: warning: missing initializer for member 'i2s_driver_config_t::skip_msk' [-Wmissing-field-initializers]
Compiling .pioenvs/esp32-voice-3/src/esphome/components/light/automation.o
src/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp: In static member function 'static void esphome::i2s_audio::I2SAudioSpeaker::player_task(void*)':
src/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp:56:3: warning: missing initializer for member 'i2s_driver_config_t::chan_mask' [-Wmissing-field-initializers]
   };
   ^
src/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp:56:3: warning: missing initializer for member 'i2s_driver_config_t::total_chan' [-Wmissing-field-initializers]
src/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp:56:3: warning: missing initializer for member 'i2s_driver_config_t::left_align' [-Wmissing-field-initializers]
src/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp:56:3: warning: missing initializer for member 'i2s_driver_config_t::big_edin' [-Wmissing-field-initializers]
src/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp:56:3: warning: missing initializer for member 'i2s_driver_config_t::bit_order_msb' [-Wmissing-field-initializers]
src/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp:56:3: warning: missing initializer for member 'i2s_driver_config_t::skip_msk' [-Wmissing-field-initializers]

@netweaver1970
Copy link

I have the same compilation warnings and the same (non VAD working) behaviour with my Korvo-2. I used the S3-box yaml. Same warnings during compilation, looking good during the device esphome startup log (as far as I know) but also no wakeword detection. As per Jessie it should work very similar to the S3-BOX though ... https://discord.com/channels/429907082951524364/1163574334472863815/1166079003950588006

@joey-90
Copy link

joey-90 commented Oct 24, 2023

I've updated the code posted in the repository as I have now got wake word detection working.

In team of compile errors, I did get some initially but was mostly caused by fat fingering repository names and not having the correct pin layout. I think the Korvo 2 and the non S3 Korvo have different Pin outs.

The key to getting it working mostly for me was using the esp32-s3-devkitc-1 board variant.

Have you got the code for this posted anywhere?

@asve99
Copy link

asve99 commented Oct 24, 2023

I've updated the code posted in the repository as I have now got wake word detection working.

In team of compile errors, I did get some initially but was mostly caused by fat fingering repository names and not having the correct pin layout. I think the Korvo 2 and the non S3 Korvo have different Pin outs.

The key to getting it working mostly for me was using the esp32-s3-devkitc-1 board variant.

Have you got the code for this posted anywhere?

Thanks, I'll dbl chk my board version, gpio pins.

@asve99
Copy link

asve99 commented Oct 25, 2023

I managed to get this working...
As it turns out my esp32-korvo-v1.1 is not an esp32-s3, so I needed to change the board and the gpio pinouts.
My board is this one: https://dl.espressif.com/dl/schematics/ESP32-KORVO_V1.1_schematics.pdf
I am not 100% which board this should be in the esphome yaml, but I used the following:

esp32:
  board: esp32dev
  framework:
    type: esp-idf
    version: recommended
    sdkconfig_options:
      CONFIG_ESP32_DEFAULT_CPU_FREQ_240: "y"

Heres' the gpio pinouts I used:

i2c:
  sda: GPIO19 #GPIO1
  scl: GPIO32 #GPIO2
  scan: true
  frequency: 400kHz

output:
  - platform: gpio
    id: pa_ctrl
    pin: GPIO12 #GPIO38

i2s_audio:
  - id: codec
    i2s_lrclk_pin: GPIO22 #GPIO41 #ws
    i2s_bclk_pin: GPIO25 #GPIO40 #clk
    i2s_mclk_pin: GPIO0 #GPIO42
  - id: mic_adc
    i2s_lrclk_pin: GPIO26 #GPIO9 #ws
    i2s_bclk_pin: GPIO27 #GPIO10 #clk
    i2s_mclk_pin: GPIO0 #GPIO20

speaker:
  - platform: i2s_audio
    id: external_speaker
    dac_type: external
    i2s_audio_id: codec
    i2s_dout_pin: GPIO13 #GPIO39
    mode: mono

microphone:
  - platform: i2s_audio
    id: external_mic
    adc_type: external
    i2s_audio_id: mic_adc
    i2s_din_pin: GPIO36 #GPIO11
    pdm: false

light:
  - platform: esp32_rmt_led_strip
    id: led_ring
    name: "${friendly_name} Light"
    pin: GPIO33 #GPIO19

sensor:
  - id: button_adc
    platform: adc
    internal: true
    pin: 39 #8

The microphone, wakeword, LED all work fine.
I currently dont have an external speaker to test that.

A big thank you to @joey-90 for the yaml.

@trip5
Copy link

trip5 commented Oct 25, 2023

A-ha. Is it the WROOM-32-based module here? https://www.aliexpress.com/item/1005002803964499.html

For about $30, it's a steal if this can actually act as a voice assistant.

@asve99
Copy link

asve99 commented Oct 25, 2023

A-ha. Is it the WROOM-32-based module here? https://www.aliexpress.com/item/1005002803964499.html

For about $30, it's a steal if this can actually act as a voice assistant.

Yes, that's the one I am using.

@rarroyo6
Copy link
Author

That's the same board I ordered. Thanks to you guys for the work, I'll try it out as soon as I receive it.
I'm thinking of 3D printing a small cylindrical case for it, with the board and microphones on top, and the speaker on the bottom.
I'll post it after I get it designed.

@pyrodex
Copy link

pyrodex commented Oct 28, 2023

I managed to get this working...

As it turns out my esp32-korvo-v1.1 is not an esp32-s3, so I needed to change the board and the gpio pinouts.

My board is this one: https://dl.espressif.com/dl/schematics/ESP32-KORVO_V1.1_schematics.pdf

I am not 100% which board this should be in the esphome yaml, but I used the following:


esp32:

  board: esp32dev

  framework:

    type: esp-idf

    version: recommended

    sdkconfig_options:

      CONFIG_ESP32_DEFAULT_CPU_FREQ_240: "y"

Heres' the gpio pinouts I used:


i2c:

  sda: GPIO19 #GPIO1

  scl: GPIO32 #GPIO2

  scan: true

  frequency: 400kHz



output:

  - platform: gpio

    id: pa_ctrl

    pin: GPIO12 #GPIO38



i2s_audio:

  - id: codec

    i2s_lrclk_pin: GPIO22 #GPIO41 #ws

    i2s_bclk_pin: GPIO25 #GPIO40 #clk

    i2s_mclk_pin: GPIO0 #GPIO42

  - id: mic_adc

    i2s_lrclk_pin: GPIO26 #GPIO9 #ws

    i2s_bclk_pin: GPIO27 #GPIO10 #clk

    i2s_mclk_pin: GPIO0 #GPIO20



speaker:

  - platform: i2s_audio

    id: external_speaker

    dac_type: external

    i2s_audio_id: codec

    i2s_dout_pin: GPIO13 #GPIO39

    mode: mono



microphone:

  - platform: i2s_audio

    id: external_mic

    adc_type: external

    i2s_audio_id: mic_adc

    i2s_din_pin: GPIO36 #GPIO11

    pdm: false



light:

  - platform: esp32_rmt_led_strip

    id: led_ring

    name: "${friendly_name} Light"

    pin: GPIO33 #GPIO19



sensor:

  - id: button_adc

    platform: adc

    internal: true

    pin: 39 #8



The microphone, wakeword, LED all work fine.

I currently dont have an external speaker to test that.

A big thank you to @joey-90 for the yaml.

Can you post somewhere the full YAML? I just ordered two of these, one for my test bench and one to create a nice setup once I can get a cool 3D printed case.

@asve99
Copy link

asve99 commented Nov 2, 2023

The microphone, wakeword, LED all work fine.
I currently dont have an external speaker to test that.
A big thank you to @joey-90 for the yaml.

Can you post somewhere the full YAML? I just ordered two of these, one for my test bench and one to create a nice setup once I can get a cool 3D printed case.

This is the full yaml I used. Please note I didn't manage to successfully test the speaker output. I am not sure if it was the speaker I was using or the yaml cfg.

substitutions:
  friendly_name: esp32-voice-3

esphome:
  name: esp32-voice-3
  platformio_options:
    board_build.flash_mode: dio
  on_boot:
    - priority: -100
      then:
        - wait_until: api.connected
        - delay: 1s
        - if:
            condition:
              switch.is_on: use_wake_word
            then:
              - voice_assistant.start_continuous:

esp32:
  board: esp-wrover-kit
  framework:
    #type: esp-idf
    type: arduino
    version: recommended

external_components:
  - source: github://rpatel3001/esphome@es8311
    components: [ es8311 ]
  - source: github://rpatel3001/esphome@es7210
    components: [ es7210 ]
  - source: github://pr#5230
    components:
      - esp_adf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: <REDACTED>

ota:
  password: <REDACTED>

wifi:
  ssid: <REDACTED>
  password: <REDACTED>
  use_address: <REDACTED>

i2c:
  sda: GPIO19 #GPIO1
  scl: GPIO32 #GPIO2
  scan: true
  frequency: 400kHz

es8311:
  address: 0x18

es7210:
  address: 0x40

output:
  - platform: gpio
    id: pa_ctrl
    pin: GPIO12 #GPIO38

i2s_audio:
  - id: codec
    i2s_lrclk_pin: GPIO22 #GPIO41 #ws
    i2s_bclk_pin: GPIO25 #GPIO40 #clk
    i2s_mclk_pin: GPIO0 #GPIO42
  - id: mic_adc
    i2s_lrclk_pin: GPIO26 #GPIO9 #ws
    i2s_bclk_pin: GPIO27 #GPIO10 #clk
    i2s_mclk_pin: GPIO0 #GPIO20

speaker:
  - platform: i2s_audio
    id: external_speaker
    dac_type: external
    i2s_audio_id: codec
    i2s_dout_pin: GPIO13 #GPIO39
    mode: mono

microphone:
  - platform: i2s_audio
    id: external_mic
    adc_type: external
    i2s_audio_id: mic_adc
    i2s_din_pin: GPIO36 #GPIO11
    pdm: false

voice_assistant:
  id: voice_asst
  microphone: external_mic
  speaker: external_speaker
  noise_suppression_level: 2
  auto_gain: 15dBFS
  volume_multiplier: 0.5
  use_wake_word: false
  on_listening:
    - light.turn_on:
        id: led_ring
        blue: 100%
        red: 0%
        green: 0%
        brightness: 100%
        effect: wakeword
  on_tts_start:
    - light.turn_on:
        id: led_ring
        blue: 0%
        red: 0%
        green: 100%
        brightness: 50%
        effect: pulse
  on_end:
    - delay: 100ms
    - wait_until:
        not:
          speaker.is_playing:
    - script.execute: reset_led
  on_error:
    - light.turn_on:
        id: led_ring
        blue: 0%
        red: 100%
        green: 0%
        brightness: 100%
        effect: none
    - delay: 1s
    - script.execute: reset_led
    - script.wait: reset_led
    - lambda: |-
        if (code == "wake-provider-missing" || code == "wake-engine-missing") {
          id(use_wake_word).turn_off();
        }

script:
  - id: reset_led
    then:
      - if:
          condition:
            switch.is_on: use_wake_word
          then:
            - light.turn_on:
                id: led_ring
                blue: 30%
                red: 0%
                green: 0%
                brightness: 25%
                effect: none
          else:
            - light.turn_off: led_ring

switch:
  - platform: template
    name: Use wake word
    id: use_wake_word
    optimistic: true
    restore_mode: RESTORE_DEFAULT_ON
    entity_category: config
    on_turn_on:
      - lambda: id(voice_asst).set_use_wake_word(true);
      - if:
          condition:
            not:
              - voice_assistant.is_running
          then:
            - voice_assistant.start_continuous
      - script.execute: reset_led
    on_turn_off:
      - voice_assistant.stop
      - script.execute: reset_led

light:
  - platform: esp32_rmt_led_strip
    id: led_ring
    name: "${friendly_name} Light"
    pin: GPIO33 #GPIO19
    num_leds: 12
    rmt_channel: 0
    rgb_order: GRB
    chipset: ws2812
    default_transition_length: 0s
    effects:
      - pulse:
          name: "Pulse"
          transition_length: 0.5s
          update_interval: 0.5s
      - addressable_twinkle:
          name: "Working"
          twinkle_probability: 5%
          progress_interval: 4ms
      - addressable_color_wipe:
          name: "Wakeword"
          colors:
            - red: 0%
              green: 50%
              blue: 0%
              num_leds: 12
          add_led_interval: 40ms
          reverse: false

binary_sensor:
  - platform: template
    name: "${friendly_name} Volume Up"
    id: btn_volume_up
  - platform: template
    name: "${friendly_name} Volume Down"
    id: btn_volume_down
  - platform: template
    name: "${friendly_name} Set"
    id: btn_set
  - platform: template
    name: "${friendly_name} Play"
    id: btn_play
  - platform: template
    name: "${friendly_name} Mode"
    id: btn_mode
  - platform: template
    name: "${friendly_name} Record"
    id: btn_record
    on_press:
      - output.turn_on: pa_ctrl
      - voice_assistant.start:
      - light.turn_on:
          id: led_ring
          brightness: 100%
          effect: "Wakeword"
    on_release:
      - voice_assistant.stop:
      - output.turn_off: pa_ctrl
      - light.turn_off:
          id: led_ring

sensor:
  - id: button_adc
    platform: adc
    internal: true
    pin: 39 #8
    attenuation: 11db
    update_interval: 15ms
    filters:
      - median:
          window_size: 5
          send_every: 5
          send_first_at: 1
      - delta: 0.1
    on_value_range:
      - below: 0.55
        then:
          - binary_sensor.template.publish:
              id: btn_volume_up
              state: ON
      - above: 0.65
        below: 0.92
        then:
          - binary_sensor.template.publish:
              id: btn_volume_down
              state: ON
      - above: 1.02
        below: 1.33
        then:
          - binary_sensor.template.publish:
              id: btn_set
              state: ON
      - above: 1.43
        below: 1.77
        then:
          - binary_sensor.template.publish:
              id: btn_play
              state: ON
      - above: 1.87
        below: 2.15
        then:
          - binary_sensor.template.publish:
              id: btn_mode
              state: ON
      - above: 2.25
        below: 2.56
        then:
          - binary_sensor.template.publish:
              id: btn_record
              state: ON
      - above: 2.8
        then:
          - binary_sensor.template.publish:
              id: btn_volume_up
              state: OFF
          - binary_sensor.template.publish:
              id: btn_volume_down
              state: OFF
          - binary_sensor.template.publish:
              id: btn_set
              state: OFF
          - binary_sensor.template.publish:
              id: btn_play
              state: OFF
          - binary_sensor.template.publish:
              id: btn_mode
              state: OFF
          - binary_sensor.template.publish:
              id: btn_record
              state: OFF
    ```

@tobsch
Copy link

tobsch commented Nov 3, 2023

Great input folks! Just received my device and tested it out.
Problem with @asve99's example: I receive a lot of errors telling me that the speaker buffer is full:

[21:25:52][W][voice_assistant:283]: Receive buffer full.
[21:25:52][W][voice_assistant:293]: Speaker buffer full.

I have a classic wired apple headphone connected to the jack.
Any ideas?

@trip5
Copy link

trip5 commented Nov 3, 2023

Has anyone tried putting an enclosure on this yet? I'm also curious about the dimensions. The only spec I found said it's 88mm wide but no mention of the height - especially since it appears to be 2 boards strapped together... pretty hard to find 2 prefabricated plastic enclosures... Is 3D printing the best option?

@rarroyo6
Copy link
Author

rarroyo6 commented Nov 3, 2023

Just received my board today. If I get a chance this weekend to set it up, I'll work on designing a case that I have mind.
3D printing is the best option for this. I'll post my design when I finish it.

@rarroyo6
Copy link
Author

rarroyo6 commented Nov 5, 2023

Just tried to program the board. When I connect the USB, it does not get recognized as a serial port, when I connect a FTDI board to the pads on the PCB, it does not recognize the type of ESP chip.
Did you guys have to do anything special to program it, or could it be that I got a defective board?

@asve99
Copy link

asve99 commented Nov 5, 2023

Just tried to program the board. When I connect the USB, it does not get recognized as a serial port, when I connect a FTDI board to the pads on the PCB, it does not recognize the type of ESP chip. Did you guys have to do anything special to program it, or could it be that I got a defective board?

Initially I thought I had the same issue with my board (not detected), but the issue was that I needed to connect power to the board (via the usb power connector) and connect your PC via the UART usb port on the board.

@rarroyo6
Copy link
Author

rarroyo6 commented Nov 6, 2023

Never mind, thanks for the help.
Apparently the USB port on my computer had locked-up or something.
I restarted my computer and it recognized it.

@abmantis
Copy link

abmantis commented Nov 9, 2023

@joey-90 thanks for your config! It works great except the speaker. Did you got that to work? On mine there is no sound (I'm using the headphone jack)

@rarroyo6
Copy link
Author

I couldn't get the speaker to work either. I verified the I/O pins against the schematic, and they seem to match correctly.
The following is what I get from the logs:

[21:42:04][D][voice_assistant:502]: Event Type: 8
[21:42:04][D][voice_assistant:572]: Response URL: "http://192.168.1.31:8123/api/tts_proxy/093290dcda7b2989879ced0ee701d7978ed0c34a_en-us_bcc7c1a994_tts.piper.raw"
[21:42:04][D][voice_assistant:395]: State changed from AWAITING_RESPONSE to STREAMING_RESPONSE
[21:42:04][D][voice_assistant:401]: Desired state set to STREAMING_RESPONSE
[21:42:04][D][i2s_audio.speaker:161]: Starting I2S Audio Speaker
[21:42:04][D][voice_assistant:502]: Event Type: 2
[21:42:04][D][voice_assistant:584]: Assist Pipeline ended
[21:42:04][D][i2s_audio.speaker:164]: Started I2S Audio Speaker
[21:42:04][D][voice_assistant:502]: Event Type: 98
[21:42:06][W][voice_assistant:293]: Speaker buffer full.
[21:42:06][W][voice_assistant:293]: Speaker buffer full.
[21:42:06][W][voice_assistant:293]: Speaker buffer full.
...
[21:42:07][W][voice_assistant:293]: Speaker buffer full.
[21:42:07][W][voice_assistant:293]: Speaker buffer full.
[21:42:07][D][voice_assistant:502]: Event Type: 99
[21:42:07][D][voice_assistant:395]: State changed from STREAMING_RESPONSE to RESPONSE_FINISHED
[21:42:07][D][voice_assistant:401]: Desired state set to IDLE
[21:42:07][D][voice_assistant:395]: State changed from RESPONSE_FINISHED to IDLE

@joey-90
Copy link

joey-90 commented Nov 11, 2023

Apologies for the delay in getting back to everyone, its been a busy week or two with work etc.

I also can't get the speaker output to work, the pin out is definitely correct. The same pin config is used in the demo firmware from Espressif. I get similar error messages to what you are seeing. I've got some updated yaml config to upload where I've been playing with some of the values for the Voice assistant etc.

@rarroyo6
Copy link
Author

Great! thank you, I would like to get this working.
I'm also trying a couple of things on my end.

@pascalmtts
Copy link

I received mine today and can't get Assistant to work. When pressing the record button and releasing it nothing happens and I also don't receive any events in Home Asssitant. The Debug Page of my Voice Assistant says: There where no events in this run.

Besides of that I can see the event of the button press and the LED state change in Home Assistant from the device.

I used the same config as @asve99

Anyone with an idea why this is not working for me?

@pyrodex
Copy link

pyrodex commented Nov 13, 2023

I received mine today and can't get Assistant to work. When pressing the record button and releasing it nothing happens and I also don't receive any events in Home Asssitant. The Debug Page of my Voice Assistant says: There where no events in this run.

Besides of that I can see the event of the button press and the LED state change in Home Assistant from the device.

I used the same config as @asve99

Anyone with an idea why this is not working for me?

The buttons aren't tied to any functions at the moment, you have to leverage a wake word from my personal experience.

@abmantis
Copy link

@pascalmtts see @joey-90 example on how to use the buttons. In his example voice is also working fine, but no sound output yet.

I am currently trying to add the Korvo1 board to ESP-ADF, which in turn should make it easy to get it into ESPHome.

@pascalmtts
Copy link

pascalmtts commented Nov 13, 2023

The button record is tied to a function in the template above:

  - platform: template
    name: "${friendly_name} Record"
    id: btn_record
    on_press:
      - output.turn_on: pa_ctrl
      - voice_assistant.start:
      - light.turn_on:
          id: led_ring
          brightness: 100%
          effect: "Wakeword"
    on_release:
      - voice_assistant.stop:
      - output.turn_off: pa_ctrl
      - light.turn_off:
          id: led_ring

As I did not get the wake word to work I tried using the record button, as it should start and stop the voice assistant.

I am currently trying to add the Korvo1 board to ESP-ADF, which in turn should make it easy to get it into ESPHome.

That's great! I don't know if you also have the Korvo1.1 on the list because I think these are 2 different boards. The Korvo1.1 is much cheaper then ESP32-S3-Korvo-1

Edit:

Here is a log output from ESPHome when pressing, holding and then releasing the rec button:

[15:51:54][D][binary_sensor:036]: 'ESP32 Korvo Record': Sending state ON
[15:51:54][D][voice_assistant:395]: State changed from IDLE to START_PIPELINE
[15:51:54][D][voice_assistant:401]: Desired state set to START_MICROPHONE
[15:51:54][D][light:036]: 'ESP32 Korvo Light' Setting:
[15:51:54][D][light:047]:   State: ON
[15:51:54][D][light:051]:   Brightness: 100%
[15:51:54][D][light:109]:   Effect: 'Wakeword'
[15:51:54][W][component:214]: Component adc.sensor took a long time for an operation (0.05 s).
[15:51:54][W][component:215]: Components should block for at most 20-30ms.
[15:51:54][D][voice_assistant:124]: microphone not running
[15:51:54][D][voice_assistant:206]: Requesting start...
[15:51:54][D][voice_assistant:395]: State changed from START_PIPELINE to STARTING_PIPELINE
[15:51:54][D][voice_assistant:124]: microphone not running
[15:51:54][D][voice_assistant:124]: microphone not running
[15:51:54][D][voice_assistant:124]: microphone not running
[15:51:54][D][voice_assistant:416]: Client started, streaming microphone
[15:51:54][D][voice_assistant:395]: State changed from STARTING_PIPELINE to START_MICROPHONE
[15:51:54][D][voice_assistant:401]: Desired state set to STREAMING_MICROPHONE
[15:51:54][D][voice_assistant:159]: Starting Microphone
[15:51:54][D][voice_assistant:395]: State changed from START_MICROPHONE to STARTING_MICROPHONE
[15:51:54][D][voice_assistant:395]: State changed from STARTING_MICROPHONE to STREAMING_MICROPHONE
[15:51:57][D][sensor:094]: 'button_adc': Sending state 3.13900 V with 2 decimals of accuracy
[15:51:57][D][binary_sensor:036]: 'ESP32 Korvo Volume Up': Sending state OFF
[15:51:57][D][binary_sensor:036]: 'ESP32 Korvo Volume Down': Sending state OFF
[15:51:57][D][binary_sensor:036]: 'ESP32 Korvo Set': Sending state OFF
[15:51:57][D][binary_sensor:036]: 'ESP32 Korvo Play': Sending state OFF
[15:51:57][D][binary_sensor:036]: 'ESP32 Korvo Mode': Sending state OFF
[15:51:57][D][binary_sensor:036]: 'ESP32 Korvo Record': Sending state OFF
[15:51:57][D][voice_assistant:495]: Signaling stop...
[15:51:57][D][voice_assistant:395]: State changed from STREAMING_MICROPHONE to STOP_MICROPHONE
[15:51:57][D][voice_assistant:401]: Desired state set to IDLE
[15:51:57][D][light:036]: 'ESP32 Korvo Light' Setting:
[15:51:57][D][light:047]:   State: OFF
[15:51:57][D][light:109]:   Effect: 'None'
[15:51:57][W][component:214]: Component adc.sensor took a long time for an operation (0.09 s).
[15:51:57][W][component:215]: Components should block for at most 20-30ms.
[15:51:57][D][voice_assistant:395]: State changed from STOP_MICROPHONE to STOPPING_MICROPHONE
[15:51:57][D][voice_assistant:395]: State changed from STOPPING_MICROPHONE to IDLE

@pascalmtts
Copy link

Alright I got it working. Problem was in my Assist configuration - after selecting Piper as TTS it is working now. Audio is also not working in my case, but I think we will find a solution for this in the future

@joey-90
Copy link

joey-90 commented Nov 17, 2023

I've uploaded new YAML config having made some tweaks.

These are:

  • Night light switch - enables a dim setting at night etc.
  • Adjusted Voice assistant sensitivity - these are for a noisy room so YMMV
  • Added a start up lighting routine.

Still no success with the sound output, but there is something to try. Will post over the weekend when I've had a chance to play.

Things to add:

  • Detect WiFi connection lost and set indicator light
  • Detect Home Assistant API status and set indicator lights
  • Graft in changes from recent changes made by Jesserockz in Atom firmwares
  • Look at using the SD card slot for local files

Please comment if there any suggestions and feel free to add pull requests if you see any errors or can add better code.

@janstadt
Copy link

substitutions:
  friendly_name: esp32-voice-3

esphome:
  name: esp32-voice-3
  platformio_options:
    board_build.flash_mode: dio
  on_boot:
    - priority: -100
      then:
        - wait_until: api.connected
        - delay: 1s
        - if:
            condition:
              switch.is_on: use_wake_word
            then:
              - voice_assistant.start_continuous:

esp32:
  board: esp-wrover-kit
  framework:
    #type: esp-idf
    type: arduino
    version: recommended

external_components:
  - source: github://rpatel3001/esphome@es8311
    components: [ es8311 ]
  - source: github://rpatel3001/esphome@es7210
    components: [ es7210 ]
  - source: github://pr#5230
    components:
      - esp_adf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: <REDACTED>

ota:
  password: <REDACTED>

wifi:
  ssid: <REDACTED>
  password: <REDACTED>
  use_address: <REDACTED>

i2c:
  sda: GPIO19 #GPIO1
  scl: GPIO32 #GPIO2
  scan: true
  frequency: 400kHz

es8311:
  address: 0x18

es7210:
  address: 0x40

output:
  - platform: gpio
    id: pa_ctrl
    pin: GPIO12 #GPIO38

i2s_audio:
  - id: codec
    i2s_lrclk_pin: GPIO22 #GPIO41 #ws
    i2s_bclk_pin: GPIO25 #GPIO40 #clk
    i2s_mclk_pin: GPIO0 #GPIO42
  - id: mic_adc
    i2s_lrclk_pin: GPIO26 #GPIO9 #ws
    i2s_bclk_pin: GPIO27 #GPIO10 #clk
    i2s_mclk_pin: GPIO0 #GPIO20

speaker:
  - platform: i2s_audio
    id: external_speaker
    dac_type: external
    i2s_audio_id: codec
    i2s_dout_pin: GPIO13 #GPIO39
    mode: mono

microphone:
  - platform: i2s_audio
    id: external_mic
    adc_type: external
    i2s_audio_id: mic_adc
    i2s_din_pin: GPIO36 #GPIO11
    pdm: false

voice_assistant:
  id: voice_asst
  microphone: external_mic
  speaker: external_speaker
  noise_suppression_level: 2
  auto_gain: 15dBFS
  volume_multiplier: 0.5
  use_wake_word: false
  on_listening:
    - light.turn_on:
        id: led_ring
        blue: 100%
        red: 0%
        green: 0%
        brightness: 100%
        effect: wakeword
  on_tts_start:
    - light.turn_on:
        id: led_ring
        blue: 0%
        red: 0%
        green: 100%
        brightness: 50%
        effect: pulse
  on_end:
    - delay: 100ms
    - wait_until:
        not:
          speaker.is_playing:
    - script.execute: reset_led
  on_error:
    - light.turn_on:
        id: led_ring
        blue: 0%
        red: 100%
        green: 0%
        brightness: 100%
        effect: none
    - delay: 1s
    - script.execute: reset_led
    - script.wait: reset_led
    - lambda: |-
        if (code == "wake-provider-missing" || code == "wake-engine-missing") {
          id(use_wake_word).turn_off();
        }

script:
  - id: reset_led
    then:
      - if:
          condition:
            switch.is_on: use_wake_word
          then:
            - light.turn_on:
                id: led_ring
                blue: 30%
                red: 0%
                green: 0%
                brightness: 25%
                effect: none
          else:
            - light.turn_off: led_ring

switch:
  - platform: template
    name: Use wake word
    id: use_wake_word
    optimistic: true
    restore_mode: RESTORE_DEFAULT_ON
    entity_category: config
    on_turn_on:
      - lambda: id(voice_asst).set_use_wake_word(true);
      - if:
          condition:
            not:
              - voice_assistant.is_running
          then:
            - voice_assistant.start_continuous
      - script.execute: reset_led
    on_turn_off:
      - voice_assistant.stop
      - script.execute: reset_led

light:
  - platform: esp32_rmt_led_strip
    id: led_ring
    name: "${friendly_name} Light"
    pin: GPIO33 #GPIO19
    num_leds: 12
    rmt_channel: 0
    rgb_order: GRB
    chipset: ws2812
    default_transition_length: 0s
    effects:
      - pulse:

@pbanj have you made any additional updates/fixes to your yaml for the korvo 1.1?

@TomG736
Copy link

TomG736 commented Apr 18, 2024

@TomG736 Bummer. Are you using microWakeWord or piper via HA? Really trying to get that microWakeWord setup working properly but havent have much success.

I'm using piper via HA

@janstadt
Copy link

@TomG736 Bummer. Are you using microWakeWord or piper via HA? Really trying to get that microWakeWord setup working properly but havent have much success.

I'm using piper via HA

Would you mind sharing your yaml or are you using the one posted up top by @pbanj ?

@TomG736
Copy link

TomG736 commented Apr 18, 2024

@TomG736 Bummer. Are you using microWakeWord or piper via HA? Really trying to get that microWakeWord setup working properly but havent have much success.

I'm using piper via HA

Would you mind sharing your yaml or are you using the one posted up top by @pbanj ?

I'm pretty much using the one you posted here but with minor changes where i was trying to swap to tapping the button to talk instead of holding it.

Did you ever fix the reset issue where you have to press the rst button after turning it on?

@janstadt
Copy link

Nah i havent figured that out yet and ended up seeing later on that micro wake word is supported so i started focusing on that. I can circle back and see whats going on there though. I wish there were smarter people out there than me that could just get this stuff working so we dont have to fumble through all this stuff. lol.

@janstadt
Copy link

So, i've changed back to piper from micro wake word using the same config i posted earlier and my device is non responsive to wake words. I wonder if all of the futzing with the micro wake word messed up some partitions or something on the device or something?

@pbanj
Copy link

pbanj commented May 1, 2024

substitutions:
  friendly_name: esp32-voice-3

esphome:
  name: esp32-voice-3
  platformio_options:
    board_build.flash_mode: dio
  on_boot:
    - priority: -100
      then:
        - wait_until: api.connected
        - delay: 1s
        - if:
            condition:
              switch.is_on: use_wake_word
            then:
              - voice_assistant.start_continuous:

esp32:
  board: esp-wrover-kit
  framework:
    #type: esp-idf
    type: arduino
    version: recommended

external_components:
  - source: github://rpatel3001/esphome@es8311
    components: [ es8311 ]
  - source: github://rpatel3001/esphome@es7210
    components: [ es7210 ]
  - source: github://pr#5230
    components:
      - esp_adf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: <REDACTED>

ota:
  password: <REDACTED>

wifi:
  ssid: <REDACTED>
  password: <REDACTED>
  use_address: <REDACTED>

i2c:
  sda: GPIO19 #GPIO1
  scl: GPIO32 #GPIO2
  scan: true
  frequency: 400kHz

es8311:
  address: 0x18

es7210:
  address: 0x40

output:
  - platform: gpio
    id: pa_ctrl
    pin: GPIO12 #GPIO38

i2s_audio:
  - id: codec
    i2s_lrclk_pin: GPIO22 #GPIO41 #ws
    i2s_bclk_pin: GPIO25 #GPIO40 #clk
    i2s_mclk_pin: GPIO0 #GPIO42
  - id: mic_adc
    i2s_lrclk_pin: GPIO26 #GPIO9 #ws
    i2s_bclk_pin: GPIO27 #GPIO10 #clk
    i2s_mclk_pin: GPIO0 #GPIO20

speaker:
  - platform: i2s_audio
    id: external_speaker
    dac_type: external
    i2s_audio_id: codec
    i2s_dout_pin: GPIO13 #GPIO39
    mode: mono

microphone:
  - platform: i2s_audio
    id: external_mic
    adc_type: external
    i2s_audio_id: mic_adc
    i2s_din_pin: GPIO36 #GPIO11
    pdm: false

voice_assistant:
  id: voice_asst
  microphone: external_mic
  speaker: external_speaker
  noise_suppression_level: 2
  auto_gain: 15dBFS
  volume_multiplier: 0.5
  use_wake_word: false
  on_listening:
    - light.turn_on:
        id: led_ring
        blue: 100%
        red: 0%
        green: 0%
        brightness: 100%
        effect: wakeword
  on_tts_start:
    - light.turn_on:
        id: led_ring
        blue: 0%
        red: 0%
        green: 100%
        brightness: 50%
        effect: pulse
  on_end:
    - delay: 100ms
    - wait_until:
        not:
          speaker.is_playing:
    - script.execute: reset_led
  on_error:
    - light.turn_on:
        id: led_ring
        blue: 0%
        red: 100%
        green: 0%
        brightness: 100%
        effect: none
    - delay: 1s
    - script.execute: reset_led
    - script.wait: reset_led
    - lambda: |-
        if (code == "wake-provider-missing" || code == "wake-engine-missing") {
          id(use_wake_word).turn_off();
        }

script:
  - id: reset_led
    then:
      - if:
          condition:
            switch.is_on: use_wake_word
          then:
            - light.turn_on:
                id: led_ring
                blue: 30%
                red: 0%
                green: 0%
                brightness: 25%
                effect: none
          else:
            - light.turn_off: led_ring

switch:
  - platform: template
    name: Use wake word
    id: use_wake_word
    optimistic: true
    restore_mode: RESTORE_DEFAULT_ON
    entity_category: config
    on_turn_on:
      - lambda: id(voice_asst).set_use_wake_word(true);
      - if:
          condition:
            not:
              - voice_assistant.is_running
          then:
            - voice_assistant.start_continuous
      - script.execute: reset_led
    on_turn_off:
      - voice_assistant.stop
      - script.execute: reset_led

light:
  - platform: esp32_rmt_led_strip
    id: led_ring
    name: "${friendly_name} Light"
    pin: GPIO33 #GPIO19
    num_leds: 12
    rmt_channel: 0
    rgb_order: GRB
    chipset: ws2812
    default_transition_length: 0s
    effects:
      - pulse:

@pbanj have you made any additional updates/fixes to your yaml for the korvo 1.1?

no i ended up in the hospital for a bit. but im going to start messing with it again

@janstadt
Copy link

janstadt commented May 1, 2024

sorry to hear that @pbanj. Keep me posted! I need to get these devices hardened before my wife loses her mind without a voice assistant. lol.

@janstadt
Copy link

janstadt commented May 2, 2024

@sajov thanks. I tried that setup already and while it worked alright, i had better luck with the previous config and i think its a bit more flexible for microWakeWord.

@sajov
Copy link

sajov commented May 2, 2024

@janstadt good to know. I have followed and tested the setup here, today I tried the other one. I’m just looking for a stable setup and using a google nest mini. Both solutions stop work over some time. I have to spend more time in testing and understanding

@janstadt
Copy link

janstadt commented May 6, 2024

So i disabled the service call to my homeassistant media player and now its not hanging and goes back to idle. I'll need to look at why its doing that now. One thing after another. lol

@GinAndBacon
Copy link

GinAndBacon commented May 27, 2024

So, i've changed back to piper from micro wake word using the same config i posted earlier and my device is non responsive to wake words. I wonder if all of the futzing with the micro wake word messed up some partitions or something on the device or something?

I know this is an older post but I think you are confusing Piper with openeakeword. Openeakeword can't listen for the trigger word on ESP32, the code was never optimized. With ESP32 and Openeakeword the HA server has to constantly listen for the trigger word and uses a lot of resources, especially with multiple assistants. I think 4 ESP32 assistants using Microwakeword can crash a raspberry pi running HA because of this. Microwakeword actually has the ESP32 device listen for the trigger word so no constant streaming needed. Piper is always used in a voice pipeline. Either locally or through Nabu Casa cloud. A raspberry pi constantly having to steam and check every few milliseconds to see if the trigger word was spoken uses a lot of CPU or RAM. This can easily be confirmed by using Microwakeword on an ESP32 and looking at the Openeakeword add on where it shows resources used by the add on.

Openeakeword can work the same on a Wyoming satellite but that requires something like a pi zero and a respeaker hat as Openeakeword is optimized for ARM and you actually have to install Wyoming and piper onto the pi before connecting to HA. Honestly I'm thinking of going that route. All the benefits of microwakeword without the current headaches. I've got a spare Pi4 and respeaker hat. I'm going to set that up today. Hoping for more stable performance. I'll report back to give my results. I have a feeling this is the best solution until they get microwakeword working more stable. I'm also going to build just and ESP32 We with PDM microphone to compare. That is the entire reason microwakeword was created, HA wanting to leverage ESPHome for obvious reasons.

Detailed information.om.setup and how it can listen for wake words with no constant streaming
https://youtu.be/eTKgc0YDCwE?si=MEXuauwBqZvZEtrV

Great video on setting up HA with Open AI, downsides are cloud based and costs although I just set my limit to 1 dollar so it just stops working after I reach my limit and only costs me 1 dollar. It also shows how to use the same integration for an LLM but you need a PC running Linux with a decent GPU for halfway decent response times.

https://youtu.be/pAKqKTkx5X4?si=phSJxqVK9_-cUR-b

Interview with creator of HA and how Nvidia has reached out to them to build a specific LLM field HA. Downside is it will require something like a Nvidia Jetson nano.

https://youtu.be/hMlkzt-2qgk?si=jzA77uZFsM0inDn0

Project
https://forums.developer.nvidia.com/t/jetson-ai-lab-home-assistant-integration/288225

@ALX-TH
Copy link

ALX-TH commented Jun 9, 2024

there is no working (mic+speacker) solution for that boad ?

@sajov
Copy link

sajov commented Jun 9, 2024

there is no working (mic+speacker) solution for that boad ?

I used this versions above and it’s working including speaker

@ALX-TH
Copy link

ALX-TH commented Jun 9, 2024

@dwitgen
Copy link

dwitgen commented Jun 9, 2024

So I was able to get the speaker to work using a modified jesserockz/esp-adf to enable and disable the PA in the start and stop functions when streaming the audio. I am not sure how good it works I just know I finally got clear audio from the korvo 1 with the wroover chip. I am completely new to this so how to provide the changes to the group is unknown to me and also there is some other changes that would need to happen in order to refence the gpio the proper way instead of directly in the code. I have to head to work so I will try and at least provide what I changed and maybe someone smarter than me can get it incorporated into a pr or something maybe tomorrow.

@ALX-TH
Copy link

ALX-TH commented Jun 9, 2024

@dwitgen can u pls share modified esp-adf ?

@dwitgen
Copy link

dwitgen commented Jun 9, 2024

@dwitgen can u pls share modified esp-adf ?

Unfortunately I work aftetnoons, I will try to post something tomorrow. I had given up on the korvo 1 non S3 version and was only using it for testing and learning. I actually was just trying to control the pa inside esp-adf and low and behold I got audio. I was always able to get static but never any audio just like everyone else.

@dwitgen
Copy link

dwitgen commented Jun 10, 2024

@dwitgen can u pls share modified esp-adf ?

Ok so I did some more testing and what I did in esp-adf had nothing to do with it working. I can only assume there was a change somewhere down the line with jesserockz/esp-adf. Sorry for any confusion but I went back to using the pr#5230.

@pascalmtts
Copy link

Is audio working for you through the 3.5mm port or with the dedicated speaker port?

@dwitgen
Copy link

dwitgen commented Jun 11, 2024

Is audio working for you through the 3.5mm port or with the dedicated speaker port?

Both, enabling the PA inside the esp-adf speaker removes the popping from the speaker but it is still there in the 3.5mm headphone but that does not go through the PA so the pa is not what is causing the popping. I tried to attach a video but the smallest I was able to get was 16MB and 10MB is the max. So I am completely new to this and github so I am trying to figure how to share config. However I can say that what appears to make it work is the changes in the esp-adf from jesserockz. So you can use that directly but I believe you need to have a custom audio board config or at least that is how I am using it. So below is what I am using to setup the board, esp-adf speaker and microphone. I will leave the custom board component open for now but I am completely new to this and github so I am in complete testing mode trying to figure things out. If someone wants to grab that board config and add it to a more stable repository or see about getting added directly to the esp_adf boards. Note under esp_adf I am refencing board: esp32s3korvo1 and this has to be there, but it is using the custom audio board. I am so new to this I cannot even figure out how to properly paste but hopefully this will help out. Most everything else in the config is the same as others have used so not going to repeat that. Also I did try micro wake word and it did compile but used up almost all of the flash and gave space errors. I could switch it to use home assistant and it worked but never got it to work on the device. What I have tried on the esp32-s3-korvo-1 for micro wake word did work but horribly. Again, no guarantees on the custom board it is just me testing.

esp32:
board: esp-wrover-kit
variant: esp32
framework:
type: esp-idf
version: recommended
sdkconfig_options:
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: "y"
CONFIG_ESP32S3_DATA_CACHE_64KB: "y"
CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y"
CONFIG_AUDIO_BOARD_CUSTOM: "y"
components:
- name: esp32_korvo1_board
source: github://dwitgen/esphome_test_audio_boards@main
refresh: 0s
psram:
mode: octal
speed: 80MHz

external_components:

  • source: github://pr#5230
    components:
    - esp_adf

esp_adf:
board: esp32s3korvo1

speaker:

  • platform: esp_adf
    id: external_speaker

microphone:

  • platform: esp_adf
    id: external_mic

@GinAndBacon
Copy link

I know people have had issues getting the speakers working and both the 3.5mm jack and speaker outputs can't be used at the same time on both the V1.1 and -1 versions. I think that is the main issue. See the post above . One of the pins is either high or low when the 3.5mm jack is plugged in. I have the Korvo-1 and it has the "popping" issue when beginning a reply. I have given up on trying to figure that one out. The V1.1 and -1 are almost identical outside the S3 and I think slightly different DSP chip or codec. The top mic/led array is the same for both so only difference is the bottom, which has the same layout.

#2430 (comment)

@ALX-TH
Copy link

ALX-TH commented Jun 13, 2024

for me not works anyway )not speacker not 3.5 =)

@dwitgen
Copy link

dwitgen commented Jun 15, 2024

The board is still goofy and I am still learning. So what I have works on the esp32-korvo-1.1 but I am not sure if what I am doing is the correct way. Thie config has working audio out both the speaker and 3.5mm and also working volume buttons on the device and in HA. There is a volume sensor but that is so-so. I tried to make a custom sensor but could not get it to work so I made a generic sensor then passed the data to another sensor. I had to do it this way because the only way I could get the volume into the sensor was with the get_sensor_by_key and the only way to get the key was to find the specific named sensor. Again probably not the correct way and its reporting is slow. Any way this config works but with the esp_adf I modified and again probably not the correct way. And hopefully I can paste my yaml so it is formatted correctly. And yes most of this yaml is borrowed. So issues I have seen, have to reset after install, lights act wonky (I think there is some clock issues), the popping is still in the headphone but not in the speaker, only because the PA is being turned off and on with audio so the pop I believe happens before the PA is turned up and after it is turned down. These issues are not seen in the esp32-s3-korvo-1.5 (other than the headphone pop on initial audio stream and stop).

---
substitutions:
  name: homeassist03
  friendly_name: HomeAssist Speaker 3
  voice_assist_idle_phase_id: "1"
  voice_assist_listening_phase_id: "2"
  voice_assist_thinking_phase_id: "3"
  voice_assist_replying_phase_id: "4"
  voice_assist_not_ready_phase_id: "10"
  voice_assist_error_phase_id: "11"
  voice_assist_muted_phase_id: "12"
  

esphome:
  name: "${name}"
  friendly_name: "${friendly_name}"
  min_version: 2024.5.5
  platformio_options:
    board_build.flash_mode: dio
  on_boot:
    - priority: -100
      then:
        - light.turn_on:
            id: led_ring
            blue: 0%
            red: 100%
            green: 0%
            effect: Fast Pulse
        - delay: 1s
        - wait_until:
            condition:
              wifi.connected:
        - light.turn_on:
            id: led_ring
            blue: 0%
            red: 100%
            green: 50%
            effect: Slow Pulse
        - wait_until: 
            condition:
              api.connected
        - lambda: id(init_in_progress) = false;
        - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
        - script.execute: reset_led

esp32:
  board: esp-wrover-kit
  framework:
    type: esp-idf
    version: recommended
    sdkconfig_options:
      CONFIG_IDF_TARGET_ESP32: y
      CONFIG_ESPTOOLPY_FLASHMODE_QIO: y
      CONFIG_ESPTOOLPY_FLASHFREQ_80M: y
      CONFIG_ESPTOOLPY_FLASHSIZE_16MB: y
      CONFIG_ESP32S3_DATA_CACHE_64KB: y
      CONFIG_ESP32S3_DATA_CACHE_LINE_64B: y
      CONFIG_PARTITION_TABLE_CUSTOM: y
      CONFIG_PARTITION_TABLE_CUSTOM_FILENAME: "default_16MB.csv" 
      CONFIG_PARTITION_TABLE_FILENAME: "default_16MB.csv" 
      CONFIG_PARTITION_TABLE_OFFSET: "0x8000"
      CONFIG_ESP32_DEFAULT_CPU_FREQ_240: y
      CONFIG_ESP32_SPIRAM_SUPPORT: y
      CONFIG_SPIRAM_SPEED_80M: y
      CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT: y
      CONFIG_I2S_ENABLE_DEBUG_LOG: y
      CONFIG_AUDIO_BOARD_CUSTOM: y
      
    components:
      - name: esp32_korvo1_board #esp32_s3_korvo1_board for the s3 variant and really you should be able to name this anything
        source: github://dwitgen/korvo_1@main #s3_korvo_1 for the s3 variant
        refresh: 0s

external_components:
  - source: github://dwitgen/esphome@main #pr#5230
    components: [esp_adf]
    refresh: 0s
 
# Enable logging
logger:

ota:
  password: !secret haspk03_ota_pwd

# Enable Home Assistant API
api:
  encryption:
    key: !secret haspk03_api_key
  services:
    - service: volume_up
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }

    - service: volume_down
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }
text_sensor:
  - platform: wifi_info
    ip_address:
      name: "${friendly_name} IP Address"

time:
  platform: homeassistant
  id: homeassistant_time

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  use_address: !secret haspk03_ip
 
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Homeassist-Smartspeaker-03"
    password: !secret haspk03_ap_pwd

captive_portal:

output:
  - platform: gpio
    id: pa_ctrl
    pin:
      number: GPIO12
      ignore_strapping_warning: true

esp_adf:
  board: esp32korvo1 #esp32s3korvo1 for s3 variant


speaker:
  - platform: esp_adf
    id: external_speaker
    
microphone:
  - platform: esp_adf
    id: external_mic

voice_assistant:
  id: voice_asst
  microphone: external_mic
  speaker: external_speaker
  noise_suppression_level: 1
  auto_gain: 31dBFS
  volume_multiplier: 6.0
  vad_threshold: 1
  use_wake_word: false
  on_listening:
    - lambda: id(voice_assistant_phase) = ${voice_assist_listening_phase_id};
    - script.execute: reset_led
  on_stt_vad_end:
    - lambda: id(voice_assistant_phase) = ${voice_assist_thinking_phase_id};
    - script.execute: reset_led
  on_tts_start:
    - light.turn_on:
        id: led_ring
        blue: 0%
        red: 0%
        green: 100%
        brightness: 50%
        effect: pulse
  on_stt_end: 
    - homeassistant.service:
        service: media_player.play_media
        data:
          entity_id: media_player.ke_ting
          media_content_id: !lambda return x;
          media_content_type: music
          announce: "true"

  on_tts_stream_start:
    - delay: 100ms
    - lambda: id(voice_assistant_phase) = ${voice_assist_replying_phase_id};
    - script.execute: reset_led
  on_end:
    - wait_until:
        not:
          speaker.is_playing:
    - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
    - script.execute: reset_led
    - if:
        condition:
          and:
            - switch.is_off: mute
        then:
          - wait_until:
              not:
                voice_assistant.is_running:
         
  on_error:
    - if:
        condition:
          lambda: return !id(init_in_progress);
        then:
          - lambda: id(voice_assistant_phase) = ${voice_assist_error_phase_id};
          - script.execute: reset_led
          - delay: 2s
          - if:
              condition:
                switch.is_off: mute
              then:
                - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
              else:
                - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
          - script.execute: reset_led
    
  on_client_connected:
    - if:
        condition:
          switch.is_off: mute
        then:
          - lambda: id(voice_asst).set_use_wake_word(true);
          - voice_assistant.start_continuous:
          - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
        else:
          - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
    - lambda: id(init_in_progress) = false;
    - script.execute: reset_led

  on_client_disconnected:
    - lambda: id(voice_asst).set_use_wake_word(false);
    - voice_assistant.stop:
    - lambda: id(voice_assistant_phase) = ${voice_assist_not_ready_phase_id};
    - script.execute: reset_led

script:
  - id: reset_led
    then:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_listening_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 0%
                      green: 0%
                      brightness: 100%
                      effect: wakeword
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_thinking_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 100%
                      green: 0%
                      brightness: 100%
                      effect: Working
                  - delay: 100ms
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_replying_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 0%
                      green: 0%
                      brightness: 100%
                      effect: Working
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_idle_phase_id};
                then:
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 0%
                      green: 0%
                      brightness: 40%
                      effect: none
                  - delay: 200ms
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_not_ready_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 40%
                      red: 100%
                      green: 0%
                      effect: Slow Pulse
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_error_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 0%
                      red: 100%
                      green: 0%
                      brightness: 100%
                      effect: none
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_muted_phase_id};
                then:                     
                  - light.turn_off: led_ring
          else:
            - light.turn_on:
                id: led_ring
                blue: 0%
                red: 100%
                green: 0%
                effect: Fast Pulse

switch:
  - platform: template
    name: Mute
    id: mute
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF
    entity_category: config
    on_turn_off:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
            - if:
                condition:
                  not:
                    - voice_assistant.is_running
                then:
                  - lambda: id(voice_asst).set_use_wake_word(true);
                  - voice_assistant.start_continuous
            - script.execute: reset_led
    on_turn_on:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - lambda: id(voice_asst).set_use_wake_word(false);
            - voice_assistant.stop
            - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
            - script.execute: reset_led
  - platform: restart
    name: "${name} Restart"

light:
  - platform: esp32_rmt_led_strip
    id: led_ring
    name: "${friendly_name} Light"
    pin: GPIO33
    num_leds: 12
    rmt_channel: 0
    rgb_order: GRB
    chipset: ws2812
    default_transition_length: 0s
    effects:
      - pulse:
          name: "Pulse"
          transition_length: 0.5s
          update_interval: 0.5s
      - addressable_twinkle:
          name: "Working"
          twinkle_probability: 5%
          progress_interval: 4ms
      - addressable_color_wipe:
          name: "Wakeword"
          colors:
            - red: 28%
              green: 100%
              blue: 90%
              num_leds: 12
          add_led_interval: 40ms
          reverse: false
      - addressable_color_wipe:
          name: "Connecting"
          colors:
            - red: 60%
              green: 60%
              blue: 60%
              num_leds: 12
            - red: 60%
              green: 60%
              blue: 0%
              num_leds: 12
          add_led_interval: 100ms
          reverse: true
      - addressable_color_wipe:
          name: "Thinking"
          colors:
            - red: 1%
              green: 90%
              blue: 99%
              num_leds: 2
            - red: 13%
              green: 17%
              blue: 87%
              num_leds: 6
          add_led_interval: 75ms
          reverse: true
      - pulse:
          name: "Slow Pulse"
          transition_length: 0.5s
          update_interval: 1s
          min_brightness: 0%
          max_brightness: 100%
      - pulse:
          name: "Fast Pulse"
          transition_length: 50ms
          update_interval: 100ms
          min_brightness: 50%
          max_brightness: 100%

globals:
  - id: init_in_progress
    type: bool
    restore_value: false
    initial_value: "true"
  - id: voice_assistant_phase
    type: int
    restore_value: false
    initial_value: ${voice_assist_not_ready_phase_id}
  - id: speaker_volume
    type: int
    restore_value: yes
    initial_value: '50'  # Initial volume level (0-100)

button:
  - platform: template
    name: "${friendly_name} Volume Up"
    id: btn_volume_up
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }

  - platform: template
    name: "${friendly_name} Volume Down"
    id: btn_volume_down
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }

binary_sensor:
  - platform: template
    name: "${friendly_name} Volume Up"
    id: btn_vol_up
    publish_initial_state : True
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }
  - platform: template
    name: "${friendly_name} Volume Down"
    id: btn_vol_down
    publish_initial_state : True
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }
  - platform: template
    name: "${friendly_name} Set"
    id: btn_set
    publish_initial_state : True
  - platform: template
    name: "${friendly_name} Play"
    id: btn_play
    publish_initial_state : True
  - platform: template
    name: "${friendly_name} Mode"
    id: btn_mode
    publish_initial_state : True
  - platform: template
    name: "${friendly_name} Record"
    id: btn_record
    publish_initial_state : True
    on_press:
      - voice_assistant.start:
      - light.turn_on:
          id: led_ring
          blue: 0%
          red: 0%
          green: 100%
          brightness: 100%
          effect: "Wakeword"

# Status connection
  - platform: status
    name: "${friendly_name} Status"
sensor:
  - id: button_adc
    platform: adc
    internal: true
    pin: 39
    attenuation: auto
    update_interval: 15ms
    filters:
      - median:
          window_size: 5
          send_every: 5
          send_first_at: 1
      - delta: 0.1
    on_value_range:
      - below: 0.55
        then:
          - binary_sensor.template.publish:
              id: btn_vol_up
              state: ON
      - above: 0.65
        below: 0.92
        then:
          - binary_sensor.template.publish:
              id: btn_vol_down
              state: ON
      - above: 1.02
        below: 1.33
        then:
          - binary_sensor.template.publish:
              id: btn_set
              state: ON
      - above: 1.43
        below: 1.77
        then:
          - binary_sensor.template.publish:
              id: btn_play
              state: ON
      - above: 1.87
        below: 2.15
        then:
          - binary_sensor.template.publish:
              id: btn_mode
              state: ON
      - above: 2.25
        below: 2.56
        then:
          - binary_sensor.template.publish:
              id: btn_record
              state: ON
      - above: 2.8
        then:
          - binary_sensor.template.publish:
              id: btn_vol_up
              state: OFF
          - binary_sensor.template.publish:
              id: btn_vol_down
              state: OFF
          - binary_sensor.template.publish:
              id: btn_set
              state: OFF
          - binary_sensor.template.publish:
              id: btn_play
              state: OFF
          - binary_sensor.template.publish:
              id: btn_mode
              state: OFF
          - binary_sensor.template.publish:
              id: btn_record
              state: OFF

# Wifi signal
  - platform: wifi_signal
    name: "${friendly_name} WiFi Signal"
    update_interval: 60s
 
 # Generic volume sensor was used so it can be used in the esp_adf_speaker to get volume into HA
  - platform: template
    id: generic_volume_sensor 
    internal: true  

  - platform: template
    name: "${friendly_name} Volume"
    accuracy_decimals: 0
    id: scaled_volume_sensor
    lambda: |-
      // Scale the volume from 0-100 to 0-10
      return id(generic_volume_sensor).state / 10.0;

@ALX-TH
Copy link

ALX-TH commented Jun 20, 2024

@dwitgen thank u very much. With your config I have everything worked like a charm!

@pascalmtts
Copy link

The board is still goofy and I am still learning. So what I have works on the esp32-korvo-1.1 but I am not sure if what I am doing is the correct way. Thie config has working audio out both the speaker and 3.5mm and also working volume buttons on the device and in HA. There is a volume sensor but that is so-so. I tried to make a custom sensor but could not get it to work so I made a generic sensor then passed the data to another sensor. I had to do it this way because the only way I could get the volume into the sensor was with the get_sensor_by_key and the only way to get the key was to find the specific named sensor. Again probably not the correct way and its reporting is slow. Any way this config works but with the esp_adf I modified and again probably not the correct way. And hopefully I can paste my yaml so it is formatted correctly. And yes most of this yaml is borrowed. So issues I have seen, have to reset after install, lights act wonky (I think there is some clock issues), the popping is still in the headphone but not in the speaker, only because the PA is being turned off and on with audio so the pop I believe happens before the PA is turned up and after it is turned down. These issues are not seen in the esp32-s3-korvo-1.5 (other than the headphone pop on initial audio stream and stop).

---
substitutions:
  name: homeassist03
  friendly_name: HomeAssist Speaker 3
  voice_assist_idle_phase_id: "1"
  voice_assist_listening_phase_id: "2"
  voice_assist_thinking_phase_id: "3"
  voice_assist_replying_phase_id: "4"
  voice_assist_not_ready_phase_id: "10"
  voice_assist_error_phase_id: "11"
  voice_assist_muted_phase_id: "12"
  

esphome:
  name: "${name}"
  friendly_name: "${friendly_name}"
  min_version: 2024.5.5
  platformio_options:
    board_build.flash_mode: dio
  on_boot:
    - priority: -100
      then:
        - light.turn_on:
            id: led_ring
            blue: 0%
            red: 100%
            green: 0%
            effect: Fast Pulse
        - delay: 1s
        - wait_until:
            condition:
              wifi.connected:
        - light.turn_on:
            id: led_ring
            blue: 0%
            red: 100%
            green: 50%
            effect: Slow Pulse
        - wait_until: 
            condition:
              api.connected
        - lambda: id(init_in_progress) = false;
        - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
        - script.execute: reset_led

esp32:
  board: esp-wrover-kit
  framework:
    type: esp-idf
    version: recommended
    sdkconfig_options:
      CONFIG_IDF_TARGET_ESP32: y
      CONFIG_ESPTOOLPY_FLASHMODE_QIO: y
      CONFIG_ESPTOOLPY_FLASHFREQ_80M: y
      CONFIG_ESPTOOLPY_FLASHSIZE_16MB: y
      CONFIG_ESP32S3_DATA_CACHE_64KB: y
      CONFIG_ESP32S3_DATA_CACHE_LINE_64B: y
      CONFIG_PARTITION_TABLE_CUSTOM: y
      CONFIG_PARTITION_TABLE_CUSTOM_FILENAME: "default_16MB.csv" 
      CONFIG_PARTITION_TABLE_FILENAME: "default_16MB.csv" 
      CONFIG_PARTITION_TABLE_OFFSET: "0x8000"
      CONFIG_ESP32_DEFAULT_CPU_FREQ_240: y
      CONFIG_ESP32_SPIRAM_SUPPORT: y
      CONFIG_SPIRAM_SPEED_80M: y
      CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT: y
      CONFIG_I2S_ENABLE_DEBUG_LOG: y
      CONFIG_AUDIO_BOARD_CUSTOM: y
      
    components:
      - name: esp32_korvo1_board #esp32_s3_korvo1_board for the s3 variant and really you should be able to name this anything
        source: github://dwitgen/korvo_1@main #s3_korvo_1 for the s3 variant
        refresh: 0s

external_components:
  - source: github://dwitgen/esphome@main #pr#5230
    components: [esp_adf]
    refresh: 0s
 
# Enable logging
logger:

ota:
  password: !secret haspk03_ota_pwd

# Enable Home Assistant API
api:
  encryption:
    key: !secret haspk03_api_key
  services:
    - service: volume_up
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }

    - service: volume_down
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }
text_sensor:
  - platform: wifi_info
    ip_address:
      name: "${friendly_name} IP Address"

time:
  platform: homeassistant
  id: homeassistant_time

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  use_address: !secret haspk03_ip
 
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Homeassist-Smartspeaker-03"
    password: !secret haspk03_ap_pwd

captive_portal:

output:
  - platform: gpio
    id: pa_ctrl
    pin:
      number: GPIO12
      ignore_strapping_warning: true

esp_adf:
  board: esp32korvo1 #esp32s3korvo1 for s3 variant


speaker:
  - platform: esp_adf
    id: external_speaker
    
microphone:
  - platform: esp_adf
    id: external_mic

voice_assistant:
  id: voice_asst
  microphone: external_mic
  speaker: external_speaker
  noise_suppression_level: 1
  auto_gain: 31dBFS
  volume_multiplier: 6.0
  vad_threshold: 1
  use_wake_word: false
  on_listening:
    - lambda: id(voice_assistant_phase) = ${voice_assist_listening_phase_id};
    - script.execute: reset_led
  on_stt_vad_end:
    - lambda: id(voice_assistant_phase) = ${voice_assist_thinking_phase_id};
    - script.execute: reset_led
  on_tts_start:
    - light.turn_on:
        id: led_ring
        blue: 0%
        red: 0%
        green: 100%
        brightness: 50%
        effect: pulse
  on_stt_end: 
    - homeassistant.service:
        service: media_player.play_media
        data:
          entity_id: media_player.ke_ting
          media_content_id: !lambda return x;
          media_content_type: music
          announce: "true"

  on_tts_stream_start:
    - delay: 100ms
    - lambda: id(voice_assistant_phase) = ${voice_assist_replying_phase_id};
    - script.execute: reset_led
  on_end:
    - wait_until:
        not:
          speaker.is_playing:
    - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
    - script.execute: reset_led
    - if:
        condition:
          and:
            - switch.is_off: mute
        then:
          - wait_until:
              not:
                voice_assistant.is_running:
         
  on_error:
    - if:
        condition:
          lambda: return !id(init_in_progress);
        then:
          - lambda: id(voice_assistant_phase) = ${voice_assist_error_phase_id};
          - script.execute: reset_led
          - delay: 2s
          - if:
              condition:
                switch.is_off: mute
              then:
                - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
              else:
                - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
          - script.execute: reset_led
    
  on_client_connected:
    - if:
        condition:
          switch.is_off: mute
        then:
          - lambda: id(voice_asst).set_use_wake_word(true);
          - voice_assistant.start_continuous:
          - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
        else:
          - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
    - lambda: id(init_in_progress) = false;
    - script.execute: reset_led

  on_client_disconnected:
    - lambda: id(voice_asst).set_use_wake_word(false);
    - voice_assistant.stop:
    - lambda: id(voice_assistant_phase) = ${voice_assist_not_ready_phase_id};
    - script.execute: reset_led

script:
  - id: reset_led
    then:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_listening_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 0%
                      green: 0%
                      brightness: 100%
                      effect: wakeword
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_thinking_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 100%
                      green: 0%
                      brightness: 100%
                      effect: Working
                  - delay: 100ms
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_replying_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 0%
                      green: 0%
                      brightness: 100%
                      effect: Working
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_idle_phase_id};
                then:
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 0%
                      green: 0%
                      brightness: 40%
                      effect: none
                  - delay: 200ms
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_not_ready_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 40%
                      red: 100%
                      green: 0%
                      effect: Slow Pulse
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_error_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 0%
                      red: 100%
                      green: 0%
                      brightness: 100%
                      effect: none
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_muted_phase_id};
                then:                     
                  - light.turn_off: led_ring
          else:
            - light.turn_on:
                id: led_ring
                blue: 0%
                red: 100%
                green: 0%
                effect: Fast Pulse

switch:
  - platform: template
    name: Mute
    id: mute
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF
    entity_category: config
    on_turn_off:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
            - if:
                condition:
                  not:
                    - voice_assistant.is_running
                then:
                  - lambda: id(voice_asst).set_use_wake_word(true);
                  - voice_assistant.start_continuous
            - script.execute: reset_led
    on_turn_on:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - lambda: id(voice_asst).set_use_wake_word(false);
            - voice_assistant.stop
            - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
            - script.execute: reset_led
  - platform: restart
    name: "${name} Restart"

light:
  - platform: esp32_rmt_led_strip
    id: led_ring
    name: "${friendly_name} Light"
    pin: GPIO33
    num_leds: 12
    rmt_channel: 0
    rgb_order: GRB
    chipset: ws2812
    default_transition_length: 0s
    effects:
      - pulse:
          name: "Pulse"
          transition_length: 0.5s
          update_interval: 0.5s
      - addressable_twinkle:
          name: "Working"
          twinkle_probability: 5%
          progress_interval: 4ms
      - addressable_color_wipe:
          name: "Wakeword"
          colors:
            - red: 28%
              green: 100%
              blue: 90%
              num_leds: 12
          add_led_interval: 40ms
          reverse: false
      - addressable_color_wipe:
          name: "Connecting"
          colors:
            - red: 60%
              green: 60%
              blue: 60%
              num_leds: 12
            - red: 60%
              green: 60%
              blue: 0%
              num_leds: 12
          add_led_interval: 100ms
          reverse: true
      - addressable_color_wipe:
          name: "Thinking"
          colors:
            - red: 1%
              green: 90%
              blue: 99%
              num_leds: 2
            - red: 13%
              green: 17%
              blue: 87%
              num_leds: 6
          add_led_interval: 75ms
          reverse: true
      - pulse:
          name: "Slow Pulse"
          transition_length: 0.5s
          update_interval: 1s
          min_brightness: 0%
          max_brightness: 100%
      - pulse:
          name: "Fast Pulse"
          transition_length: 50ms
          update_interval: 100ms
          min_brightness: 50%
          max_brightness: 100%

globals:
  - id: init_in_progress
    type: bool
    restore_value: false
    initial_value: "true"
  - id: voice_assistant_phase
    type: int
    restore_value: false
    initial_value: ${voice_assist_not_ready_phase_id}
  - id: speaker_volume
    type: int
    restore_value: yes
    initial_value: '50'  # Initial volume level (0-100)

button:
  - platform: template
    name: "${friendly_name} Volume Up"
    id: btn_volume_up
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }

  - platform: template
    name: "${friendly_name} Volume Down"
    id: btn_volume_down
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }

binary_sensor:
  - platform: template
    name: "${friendly_name} Volume Up"
    id: btn_vol_up
    publish_initial_state : True
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }
  - platform: template
    name: "${friendly_name} Volume Down"
    id: btn_vol_down
    publish_initial_state : True
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }
  - platform: template
    name: "${friendly_name} Set"
    id: btn_set
    publish_initial_state : True
  - platform: template
    name: "${friendly_name} Play"
    id: btn_play
    publish_initial_state : True
  - platform: template
    name: "${friendly_name} Mode"
    id: btn_mode
    publish_initial_state : True
  - platform: template
    name: "${friendly_name} Record"
    id: btn_record
    publish_initial_state : True
    on_press:
      - voice_assistant.start:
      - light.turn_on:
          id: led_ring
          blue: 0%
          red: 0%
          green: 100%
          brightness: 100%
          effect: "Wakeword"

# Status connection
  - platform: status
    name: "${friendly_name} Status"
sensor:
  - id: button_adc
    platform: adc
    internal: true
    pin: 39
    attenuation: auto
    update_interval: 15ms
    filters:
      - median:
          window_size: 5
          send_every: 5
          send_first_at: 1
      - delta: 0.1
    on_value_range:
      - below: 0.55
        then:
          - binary_sensor.template.publish:
              id: btn_vol_up
              state: ON
      - above: 0.65
        below: 0.92
        then:
          - binary_sensor.template.publish:
              id: btn_vol_down
              state: ON
      - above: 1.02
        below: 1.33
        then:
          - binary_sensor.template.publish:
              id: btn_set
              state: ON
      - above: 1.43
        below: 1.77
        then:
          - binary_sensor.template.publish:
              id: btn_play
              state: ON
      - above: 1.87
        below: 2.15
        then:
          - binary_sensor.template.publish:
              id: btn_mode
              state: ON
      - above: 2.25
        below: 2.56
        then:
          - binary_sensor.template.publish:
              id: btn_record
              state: ON
      - above: 2.8
        then:
          - binary_sensor.template.publish:
              id: btn_vol_up
              state: OFF
          - binary_sensor.template.publish:
              id: btn_vol_down
              state: OFF
          - binary_sensor.template.publish:
              id: btn_set
              state: OFF
          - binary_sensor.template.publish:
              id: btn_play
              state: OFF
          - binary_sensor.template.publish:
              id: btn_mode
              state: OFF
          - binary_sensor.template.publish:
              id: btn_record
              state: OFF

# Wifi signal
  - platform: wifi_signal
    name: "${friendly_name} WiFi Signal"
    update_interval: 60s
 
 # Generic volume sensor was used so it can be used in the esp_adf_speaker to get volume into HA
  - platform: template
    id: generic_volume_sensor 
    internal: true  

  - platform: template
    name: "${friendly_name} Volume"
    accuracy_decimals: 0
    id: scaled_volume_sensor
    lambda: |-
      // Scale the volume from 0-100 to 0-10
      return id(generic_volume_sensor).state / 10.0;

Thanks for your config! It does work but sometimes randomly hangs up. Also LEDs are a bit unstable. But besides that everything does work now for me.
Not going to replace Google or Alexa soon, as it is still missing some important functions like timers, alarm and also cancelling running responses by saying the wake word again, but that's all things that have to come from Home Assistant I guess.

@GinAndBacon
Copy link

GinAndBacon commented Jun 26, 2024

Thanks for your config! It does work but sometimes randomly hangs up. Also LEDs are a bit unstable. But besides that everything does work now for me. Not going to replace Google or Alexa soon, as it is still missing some important functions like timers, alarm and also cancelling running responses by saying the wake word again, but that's all things that have to come from Home Assistant I guess.

I created my own timers using the timer helper and custom automations. Only issue I had is getting the time remaining via voicce. Probably because the actual timer is an attribute I believe. I had to create a sensor that passes that value but you have to pause/unpause. When that is done the sensor catches the value. May be an easier way but it works for me.

The main issue for me with the Korvo-1 is it works great but if the TV is on or other background noise is going, it's very hit or miss. I'm pretty sure Amazon and Google still uses the cloud for their super efficient background noise detector. If it runs completely on device then neither will never give up that secret sauce. Read that Amazon was going to start charging for Alexa but not confirmed by them. Just the rumor mill.

Regarding the install, that may be because the buttons get all wonky and go into an unknown state. At the same time my korvo-1 doesn't have that issue, only the popping issue you described which is annoying. I think it may because one pin is high/low if anything is plugged into the jack. high for 3.5mm and low for speakers (or vice versa). Think that is why nobody I am aware of has gotten the speaker output through JST to work. I am still wondering why they added the korvo-2 and not the korvo-1 as an actual board in esphome. While it does have speaker and mics, it seems more geared towards the added LCD screen and something else, think a camera.

One odd thing is if I use the below now, it instantly gives me a message saying the board is not supported by ESP_ADF almost instantly in esphome which is odd because I would imagine it does since they are both espresiff. I have it set to esp32s3box, along with esphome board at the top on my korovo-1 and point it to a github component url for all the pin stuff. What is odd is I used to have it set to the below and it worked. I just starting using esp32s3box because it just works just fine on the -1 and I got sick of tinkering with the esphome.
esp_adf:
board: esp32korvo1

image

@GinAndBacon
Copy link

Just wanted to add I started messing around with some stuff and if you put "publish_initial_state=true" in your binary_sensor template buttons, they all come up as OFF after a flashing/updating via esphome instead of unknown which requires doing a factory reset to get them in the right state.. Small but just thought I would add it.

  • platform: template
    name: "${friendly_name} Volume Up"
    id: btn_volume_up
    publish_initial_state : true

@GinAndBacon
Copy link

GinAndBacon commented Jul 5, 2024

Last post but found this on YouTube to output audio to a different speaker as I got sick of the popping noise from the 3.5mm.jack. You do have to go to devices, then ESPHome (top option) then click configure and allow the Korvo to make home assistant service calls. Zero issues outside a few milliseconds delay for audio output but barely noticable.

  on_tts_stream_start:
    - lambda: id(voice_assistant_phase) = ${voice_assist_replying_phase_id};
    - light.turn_on:
        id: led_ring
        blue: 0%
        red: 0%
        green: 100%
        brightness: 100%        
        effect: working
    - script.execute: reset_led 
  on_tts_end:
     - homeassistant.service:
         service: media_player.play_media
         data:
           entity_id: media_player.vlc_telnet  
           media_content_id: !lambda 'return x;'
           media_content_type: music
           announce: "true"
  on_tts_stream_end:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests