ESP32 smart głośnik v1.5

Powstała pierwotna wersja głośnika z asystentem o której wspominałem tutaj: Smart głośnik dla każdego ESP32 i zapowiedziałem wersję poprawioną V2.0 gdzie miało się odbyć wszystko w obudowie retro z mocniejszymi głośnikami.
Prawdą jest powiedzenie że: “Potrzeba matką wynalazków” i tak powstała wersja V1.5 która obok funkcji porannego radia posiada funkcję nocnej lampki :smiley: +update kodu
Bazą projektu stał się głośnik Esperanza EP131 który posiadał już w sobie diody RGB (niestety dawały zbyt małe światło i zostały wymienione na ring 24 led).

Chcąc zbudować podobny smart głośnik potrzebujesz:

  • esp32 devkitc V4
  • moduł MAX98357A
  • mikrofon INMP441 MEMS I2S
  • konwenter poziomów logicznych (zdania mogą być podzielone czy jest wymagany)
  • wspomniany głośnik Esperanza EP131
  • tact switch 12x12
  • led ring 24 RGB
  • buzzer
  • gniazdo USB + kabelek
  • obudowę w której trzeba wywiercić otwór na mikrofon i gniazdo zasilania
    obudowa.zip (63,4 KB)

Budowa głośnika:

Głównym problemem poprzedniego projektu było rozpoczęcie nadsłuchu po ponownym uruchomieniu oraz uśpienie mikrofonu. Wprowadziłem do kodu skrypt który eliminuje ten problem.

YAML do projektu:

esphome:
  name: "esp-speaker"
  friendly_name: "esp-speaker"

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "XXXXXXXX"

ota:
  password: "XXXXXXXXXXXX"

wifi:
  ssid: "XXXXXXXX"
  password: "XXXXXXXXXXx"
  fast_connect: on
  on_connect:
    - script.execute: start
    - delay: 5s
    - switch.turn_on: green
    - script.execute: restarter
    - delay: 1s
    - switch.turn_off: green

web_server:
   port: 80


captive_portal:

light:
  - platform: fastled_clockless
    default_transition_length: 0.5s
    id: ring
    chipset: WS2811
    pin: GPIO23
    num_leds: 24
    rgb_order: GRB
    name: "FastLED WS2811 Light"

binary_sensor:
  - platform: gpio
    pin: 
      number: 18
      mode: INPUT_PULLUP
      inverted: false
    name: Button
    on_press:
      then:
       if:
         condition:
           media_player.is_playing: player
         then:
           - media_player.stop: player
         else:
            - switch.toggle: Lampka
  

output:
  - platform: ledc
    pin: GPIO19
    id: rtttl_out

rtttl:
  output: rtttl_out
  id: my_rtttl

i2s_audio:
  - id: i2s_mic
    i2s_lrclk_pin: GPIO22  #WS
    i2s_bclk_pin: GPIO21   #SCK
  - id: i2s_out
    i2s_lrclk_pin: GPIO32 #LRCLK
    i2s_bclk_pin: GPIO27  #BLCK

microphone:
  - platform: i2s_audio
    id: mikrofon
    adc_type: external 
    channel: right
    bits_per_sample: 32bit
    i2s_audio_id: i2s_mic 
    i2s_din_pin: GPIO4   #DIN,SDIN,SD,SDATA
    pdm: false
        
voice_assistant:
  id: va
  microphone: mikrofon
  use_wake_word: true
  #noise_suppression_level: 1
  auto_gain: 31dBFS
  on_listening:
    - rtttl.play: 'two_short:d=4,o=5,b=100:16e6,16e6'
    - switch.turn_on: green
    - delay: 3s
    - switch.turn_off: green

    
media_player:
  - platform: i2s_audio
    id: player
    name: ESP Media Player
    dac_type: external
    i2s_audio_id: i2s_out
    mode: mono
    i2s_dout_pin: GPIO2 #DIN,SDIN,SD,SDATA
    on_play:
      - logger.log: "playing"
      - switch.turn_off: use_wake_word
    on_pause:
      - logger.log: "paused"
      - switch.turn_on: use_wake_word
    on_idle:
      - logger.log: "idle"
      - switch.turn_on: use_wake_word

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(va).set_use_wake_word(true);
      - if:
          condition:
            not:
              - voice_assistant.is_running
          then:
            - voice_assistant.start_continuous
    on_turn_off:
      - voice_assistant.stop
      - lambda: id(va).set_use_wake_word(false);

  - platform: template
    name: Lampka
    id: Lampka
    optimistic: true
    on_turn_on:
      - lambda: id(ring).turn_on().set_rgb(0.99, 1.00, 0.66).perform();    
    on_turn_off:
      - lambda: id(ring).turn_off().perform(); 

  - platform: template
    name: Green
    id: green
    optimistic: true
    on_turn_on:
      - lambda: id(ring).turn_on().set_rgb(0.11, 1.00, 0.08).perform();    
    on_turn_off:
      - lambda: id(ring).turn_off().perform();  

  - platform: template
    name: Blue
    id: blue
    optimistic: true
    on_turn_on:
      - lambda: id(ring).turn_on().set_rgb(0.05, 0.45, 1.00).perform();    
    on_turn_off:
      - lambda: id(ring).turn_off().perform();   

  - platform: template
    name: Red
    id: red
    optimistic: true
    on_turn_on:
      - lambda: id(ring).turn_on().set_rgb(1.00, 0.30, 0.03).perform();    
    on_turn_off:
      - lambda: id(ring).turn_off().perform();  


interval:
  - interval: 300s
    then:
     if:
      condition:
          media_player.is_playing: player
      then:
         - logger.log: "restarter stopped"
      else:
          - script.execute: restarter

script:
  - id: start
    then:
    - wait_until:
       wifi.connected:
    - logger.log: "polaczono"
    - media_player.volume_set: !lambda "return 0.5;"
    - switch.turn_on: blue
    - delay: 3s
    - switch.turn_off: blue

  - id: restarter
    then:
     - switch.turn_off: use_wake_word     
     - delay: 1s
     - switch.turn_on: use_wake_word
     - delay: 1s
     - switch.turn_off: use_wake_word     
     - delay: 1s
     - switch.turn_on: use_wake_word

Od nagrania filmu pokazowego został też poprawiona funkcja przycisku który stopuje radio lub wyłącza lampkę.

Na zdjęciu widać gdzie znajduje się przycisk dzięki czemu włączenie/ wyłączenie lampki nocnej odbywa się przez delikatne dotknięcie głośnika.

Podsumowanie:
Projekt działa u mnie od kilkunastu dni bez większych problemów i myślę że zostanie już w moim salonie na dobre. Wprowadziłem kilka poprawek w stosunku do starszej wersji która zaczynała mnie irytować swoimi niedociągnięciami. Podczas testowania zauważyłem też że najlepszą jakość rozpoznawania dźwięku jest wtedy gdy cały mikrofon MEMS jest odsłonięty dlatego w swojej obudowie wyciągnąłem go na zewnątrz i zasłoniłem wydrukowaną kratką która nie każdemu może przypaść do gustu :sweat_smile:. Dzięki temu wake word działa mi do 4m. Znaczna część kodu została uproszczona bo wprowadzanie dodatkowych stanów do voice assistant takich jak on_stt_end , on_end czy on_error wprowadzało niepożądane działanie głośnika. To samo powodowało wgranie biblioteki neopixel więc zdecydowałem się fastled.

Życzę wszystkim dobrej zabawy oraz samych udanych projektów!

7 polubień

Pokaż więcej zdjęć :slightly_smiling_face:
Czuję że będziesz miał następców a to ułatwi im życie. :slightly_smiling_face:





Miałem jeszcze zdjęcia z rozbiórki głośnika ale po drodze wychodziły nowe przeróbki i pomysły więc nie mają sensu :wink:

3 polubienia

Co powinienem doinstalowac do HA żeby działalo to tak jak u Ciebie.

Musisz mieć asystenta od nabu casy (czyli płatna opcja) i zainstalowanego wake worda z addonow. Płatny asystent nie jest konieczny ale ten akurat działa najlepiej i najszybciej