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 +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 . 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!