Skip to content

Instantly share code, notes, and snippets.

@rmeissn
Created March 10, 2025 09:02
Show Gist options
  • Save rmeissn/a6bc1c91f65a47cb5e37d6e2fcfa8849 to your computer and use it in GitHub Desktop.
Save rmeissn/a6bc1c91f65a47cb5e37d6e2fcfa8849 to your computer and use it in GitHub Desktop.
Onju Voice with esphome 2025.2
substitutions:
name: "onju"
friendly_name: "Onju Voice PE"
project_version: "1.1.0"
device_description: "Onju Voice Satellite with ESPHome software and microWakeWord"
wakeup_sound_url: "http://192.168.0.202:8123/local/wakeup.flac" # New Notification #7 by UNIVERSFIELD https://freesound.org/people/UNIVERSFIELD/sounds/736267/
error_sound_url: "http://192.168.0.202:8123/local/error.flac" # Error #8 by UNIVERSFIELD https://freesound.org/people/UNIVERSFIELD/sounds/734442/
timer_finished_sound_url: "http://192.168.0.202:8123/local/timer_finished.flac" # New Notification #6 by UNIVERSFIELD https://freesound.org/people/UNIVERSFIELD/sounds/734445/
mute_sound_url: "http://192.168.0.202:8123/local/mute.flac" # https://github.com/esphome/home-assistant-voice-pe/blob/dev/sounds/jack_disconnected.flac
unmute_sound_url: "http://192.168.0.202:8123/local/unmute.flac" # https://github.com/esphome/home-assistant-voice-pe/blob/dev/sounds/jack_connected.flac
knock_sound_url: "http://192.168.0.202:8123/local/knock.flac" # https://freesound.org/people/UberBosser/sounds/421585/
click_sound_url: "http://192.168.0.202:8123/local/tongue-click.flac" # https://freesound.org/people/MichellePamelaLyons/sounds/135515/
# NOTE for sounds: all sound were converted to flac, mono, 48khz (match the speaker sample_rate!)
esphome:
name: "${name}"
friendly_name: "${friendly_name}"
comment: "${device_description}"
#name_add_mac_suffix: true
project:
name: tetele.onju_voice_satellite
version: "${project_version}"
min_version: 2025.2.0
platformio_options:
board_build.flash_mode: dio
board_build.arduino.memory_type: qio_opi
on_boot:
then:
- light.turn_on:
id: top_led
effect: slow_pulse
red: 100%
green: 60%
blue: 0%
- wait_until:
condition:
wifi.connected
- light.turn_on:
id: top_led
effect: pulse
red: 0%
green: 100%
blue: 0%
- wait_until:
condition:
api.connected
- light.turn_on:
id: top_led
effect: none
red: 0%
green: 100%
blue: 0%
- delay: 1s
- script.execute: reset_led
- media_player.volume_set:
id: onju_out
volume: !lambda "return id(volume_percent);"
- lambda: id(booted) = true;
dashboard_import: # not sure this is needed at all
package_import_url: github://tetele/onju-voice-satellite/esphome/onju-voice-microwakeword.yaml@main
esp32:
board: esp32-s3-devkitc-1
variant: esp32s3
flash_size: 16MB
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_ESP32S3_INSTRUCTION_CACHE_32KB: "y"
CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y"
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y"
CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y"
CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" # TLS1.3 support isn't enabled by default in IDF 5.1.5
psram:
mode: octal
speed: 80MHz
# Enable logging
logger:
#level: debug
#initial_level: debug
#logs:
# sensor: WARN
# Allow OTA updates
ota:
platform: esphome
password: "" # ADD YOUR PASSWORD HERE
# Allow provisioning Wi-Fi via serial
improv_serial:
wifi:
ssid: !secret wifi_ssid # ADD YOUR SSID HERE
password: !secret wifi_password # ADD YOUR PASSWORD HERE
#fast_connect: True # activate if you only got one access point (no mesh or similar)
enable_rrm: True
enable_btm: True
#power_save_mode: NONE
#domain: .local
ap:
ssid: "Onju"
password: !secret fallback_ap_password # ADD YOUR PASSWORD HERE
# In combination with the `ap` this allows the user
# to provision wifi credentials to the device via WiFi AP.
captive_portal:
api:
services:
- service: start_va
then:
voice_assistant.start
- service: start_va_continuous
then:
voice_assistant.start_continuous
- service: stop_va
then:
voice_assistant.stop
- service: notification_on
then:
- script.execute: turn_on_notification
- service: notification_clear
then:
- script.execute: clear_notification
globals:
- id: thresh_percent
type: float
initial_value: "0.03"
restore_value: false
- id: touch_calibration_values_left
type: uint32_t[5]
restore_value: false
- id: touch_calibration_values_center
type: uint32_t[5]
restore_value: false
- id: touch_calibration_values_right
type: uint32_t[5]
restore_value: false
- id: notification
type: bool
restore_value: false
- id: booted # new
type: bool
restore_value: false
- id: mic_off # new
type: bool
restore_value: false
- id: internal_flicker # new
type: bool
restore_value: false
- id: volume_change # new
type: bool
restore_value: false
- id: volume_percent
type: float
initial_value: "0.5"
restore_value: true
interval:
- interval: 1s
then:
- script.execute:
id: calibrate_touch
button: 0
- script.execute:
id: calibrate_touch
button: 1
- script.execute:
id: calibrate_touch
button: 2
i2s_audio:
#- id: i2s_in
# i2s_lrclk_pin:
# number: GPIO13 # WS / LRCLK
# allow_other_uses: true
# i2s_bclk_pin:
# number: GPIO18 # SCK / BCLK
# allow_other_uses: true
#- id: i2s_out
# i2s_lrclk_pin:
# number: GPIO13 # WS / LRCLK
# allow_other_uses: true
# i2s_bclk_pin:
# number: GPIO18 # SCK / BCLK
# allow_other_uses: true
- id: i2s_shared
i2s_lrclk_pin: GPIO13 # WS / LRCLK
i2s_bclk_pin: GPIO18 # SCK / BCLK
microphone:
- platform: nabu_microphone
id: nabu_mic
i2s_din_pin: GPIO17 # SDI
adc_type: external
use_apll: true
pdm: false
sample_rate: 16000 # mic supports 16kHz to 64kHz, captures approx. ~45Hz to ~15kHz -> 16kHz to 32kHz is sufficient, mww and va need 16kHz
bits_per_sample: 32bit # mic only supports 24 bits, TODO: test 16 or 24 bit
i2s_mode: primary
#i2s_audio_id: i2s_in
i2s_audio_id: i2s_shared
channel_0: # e.g. left
id: onju_microphone
amplify_shift: 3 # 0 to 8, higher is better, because it will produce louder signals, that might clip, 3 seems the highest possible value
channel_1: # e.g. right
id: mww_microphone
amplify_shift: 3 # 0 to 8, higher is better, because it will produce louder signals, that might clip, 3 seems the highest possible value
speaker:
# Hardware speaker output
- platform: i2s_audio
id: i2s_audio_speaker
#i2s_mode: primary
sample_rate: 48000 # DAC supports 8kHz to 96kHz, TODO: test 44.1kHz
bits_per_sample: 32bit # DAC supports 16/24/32 bit, TODO: set to 16 or 24?
use_apll: true
#i2s_audio_id: i2s_out
i2s_audio_id: i2s_shared
dac_type: external
i2s_dout_pin: GPIO12 # SDO / Din
channel: left
timeout: never
buffer_duration: 100ms
- platform: mixer
id: mixing_speaker
output_speaker: i2s_audio_speaker
num_channels: 1
#task_stack_in_psram: true
source_speakers:
- id: announcement_mixing_input
timeout: never
- id: media_mixing_input
timeout: never
# Vritual speakers to resample each pipelines' audio, if necessary, as the mixer speaker requires the same sample rate
- platform: resampler
id: announcement_resampling_speaker
output_speaker: announcement_mixing_input
sample_rate: 48000 # NOTE: must be same as for speaker
bits_per_sample: 16 # NOTE: there will never arrive 32 bit at the speaker itself
#task_stack_in_psram: true
- platform: resampler
id: media_resampling_speaker
output_speaker: media_mixing_input
sample_rate: 48000 # NOTE: must be same as for speaker
bits_per_sample: 16 # NOTE: there will never arrive 32 bit at the speaker itself
#task_stack_in_psram: true
media_player:
- platform: speaker
id: onju_out
name: Media Player
internal: False
volume_increment: 0.05
volume_min: 0.2
volume_max: 0.85
#task_stack_in_psram: true
announcement_pipeline:
speaker: announcement_resampling_speaker
format: FLAC # FLAC is the least processor intensive codec
num_channels: 1 # Stereo audio is unnecessary for announcements
sample_rate: 48000 # NOTE: must be same as for speaker
media_pipeline:
speaker: media_resampling_speaker
format: FLAC # FLAC is the least processor intensive codec
num_channels: 1 # Onju only got one speaker
sample_rate: 48000 # NOTE: must be same as for speaker
on_announcement:
- lambda: id(nabu_mic).stop();
- mixer_speaker.apply_ducking:
id: media_mixing_input
decibel_reduction: 20
duration: 0.0s # duck now
on_state:
then:
- lambda: |-
static float old_volume = -1;
float new_volume = id(onju_out).volume;
if(abs(new_volume-old_volume) > 0.0001) {
if(old_volume != -1) {
id(volume_change) = true;
id(show_volume)->execute();
}
}
old_volume = new_volume;
id(volume_percent) = old_volume;
- if: # reset ducking only of va is not active
condition:
and:
- not:
voice_assistant.is_running:
- not:
media_player.is_announcing:
then:
- mixer_speaker.apply_ducking:
id: media_mixing_input
decibel_reduction: 0 # stop ducking
duration: 1.0s # over 1s
on_play: # not called for announcements
- lambda: id(internal_flicker) = false; # needed to deactivate flicker on audio playback
- script.execute: reset_led # TODO: causes the volume_show to "fail" (not shown)
- lambda: id(nabu_mic).stop();
on_pause: # speaker is auto-restarted if something is paused, causing the mic to fail -> stop on wakeword detection
- lambda: id(internal_flicker) = true; # needed to activate flicker on pause
- script.execute: reset_led # TODO: causes the volume_show to "fail" (not shown)
- script.execute: stop_speaker_start_microphone
- script.wait: stop_speaker_start_microphone
on_idle: # also called after announcement finished, is triggered on volume change
- if: # TODO: should also be included at pause and play. Is there a better alternative?
condition:
not:
lambda: return id(volume_change);
then:
- lambda: id(internal_flicker) = true; # needed to activate flicker on idle
- script.execute: reset_led
- if: # stop destroying speaker if media_player plays music
condition:
- not:
media_player.is_playing
then:
- script.execute: stop_speaker_start_microphone
- script.wait: stop_speaker_start_microphone
files:
- id: wakeup
file: "${wakeup_sound_url}"
- id: error
file: "${error_sound_url}"
- id: mute
file: "${mute_sound_url}"
- id: unmute
file: "${unmute_sound_url}"
- id: knock
file: "${knock_sound_url}"
#- id: click # TODO: Adding this causes the ota partition to fail (too small)
# file: "${click_sound_url}"
- id: timer_finished
file: "${timer_finished_sound_url}"
external_components:
# https://github.com/esphome/esphome/pull/7802 might be interesting
- source:
type: git
url: https://github.com/formatBCE/home-assistant-voice-pe
ref: dev
components:
- micro_wake_word
- microphone
- nabu_microphone
- voice_assistant
refresh: 0s
#- source: # only needed if mic is on 48kHz
# type: git
# url: https://github.com/formatBCE/home-assistant-voice-pe
# ref: 48kHz_mic_support
# components:
# - nabu_microphone
# refresh: 0s
micro_wake_word: # requires 16khz input currently
id: mww
models:
- model: https://github.com/kahrendt/microWakeWord/releases/download/okay_nabu_20241226.3/okay_nabu.json
id: okay_nabu
#probability_cutoff: 0.8 # TODO tune cutoff to the onju
vad:
microphone: mww_microphone
on_wake_word_detected:
- if:
condition:
- switch.is_on: use_wake_word # ignore detection if switch is on
then:
- if: # media_player needs to be stopped to not trigger on_idle too often
condition:
media_player.is_paused
then:
- media_player.stop
- delay: 300ms
- media_player.speaker.play_on_device_media_file:
media_file: wakeup
announcement: true
- wait_until: media_player.is_announcing
- wait_until:
or:
- media_player.is_idle
- media_player.is_paused
- voice_assistant.start:
wake_word: !lambda return wake_word;
voice_assistant: # requires 16khz input currently (for vad?)
id: va
microphone: onju_microphone
media_player: onju_out
micro_wake_word: mww
use_wake_word: false
noise_suppression_level: 0 # this is done at the ha side and for recorded audio (activate debug mode to see settings), maybe buggy
auto_gain: 31 dbfs # this is done at the ha side and for recorded audio (activate debug mode to see settings), maybe buggy
volume_multiplier: 3 # this is done at the ha side and for recorded audio (activate debug mode to see settings), maybe buggy
on_start:
- mixer_speaker.apply_ducking:
id: media_mixing_input
decibel_reduction: 20 # Number of dB quieter; higher implies more quiet, 0 implies full volume
duration: 0.0s # The duration of the transition (default is no transition) -> duck now
on_listening:
- light.turn_on:
id: top_led
blue: 100%
red: 100%
green: 100%
brightness: 100%
effect: listening
on_stt_vad_end:
- light.turn_on:
id: top_led
blue: 100%
red: 0%
green: 20%
brightness: 70%
effect: processing
on_tts_end:
- light.turn_on:
id: top_led
blue: 0%
red: 20%
green: 100%
effect: speaking
on_end:
- wait_until:
not:
voice_assistant.is_running
- mixer_speaker.apply_ducking: # Stop ducking audio.
id: media_mixing_input
decibel_reduction: 0
duration: 1.0s # duck over 1s
- script.execute: reset_led
on_timer_started:
- light.turn_on:
id: top_led
effect: random_twinkle
on_timer_finished:
- media_player.speaker.play_on_device_media_file:
media_file: timer_finished
announcement: true
- light.turn_on:
id: top_led
blue: 0%
red: 100%
green: 80%
effect: slow_pulse
- delay: 5s
- script.execute: reset_led
on_client_connected:
- if:
condition:
and:
- switch.is_on: use_wake_word
- binary_sensor.is_off: mute_switch
then:
#- lambda: id(nabu_mic).start();
- micro_wake_word.start
on_client_disconnected:
- if:
condition:
and:
- switch.is_on: use_wake_word
- binary_sensor.is_off: mute_switch
then:
- voice_assistant.stop
- micro_wake_word.stop
#- lambda: id(nabu_mic).stop();
on_error:
- media_player.speaker.play_on_device_media_file:
media_file: error
announcement: true
- light.turn_on:
id: top_led
blue: 0%
red: 100%
green: 0%
effect: none
- delay: 1s
- script.execute: reset_speaker_microphone
- script.execute: reset_led
number:
- platform: template
name: "Touch threshold percentage"
id: touch_threshold_percentage
icon: mdi:gesture-tap
update_interval: never
entity_category: config
initial_value: 0.75
min_value: 0.25
max_value: 5
step: 0.05
optimistic: true
on_value:
then:
- lambda: !lambda |-
id(thresh_percent) = 0.01 * x;
esp32_touch:
setup_mode: false
sleep_duration: 2ms
measurement_duration: 800us
low_voltage_reference: 0.8V
high_voltage_reference: 2.4V
filter_mode: IIR_16
debounce_count: 2
noise_threshold: 0
jitter_step: 0
smooth_mode: IIR_2
denoise_grade: BIT8
denoise_cap_level: L0
button:
- platform: restart
id: restart_button
name: "Restart"
entity_category: config
disabled_by_default: true
icon: "mdi:restart"
binary_sensor:
- platform: template
id: conversation_mode
name: "Conversation Mode"
icon: mdi:forum
disabled_by_default: true
- platform: esp32_touch
id: volume_down
name: "VOL-"
icon: mdi:volume-minus
disabled_by_default: true
pin: GPIO4
threshold: 539000
on_press:
then:
- light.turn_on: left_led
- script.execute:
id: set_volume
volume: -0.05
- delay: 750ms
- while:
condition:
binary_sensor.is_on: volume_down
then:
- script.execute:
id: set_volume
volume: -0.05
- delay: 150ms
on_release:
then:
- light.turn_off: left_led
- platform: esp32_touch
id: volume_up
name: "VOL+"
icon: mdi:volume-plus
disabled_by_default: true
pin: GPIO2
threshold: 580000
on_press:
then:
- light.turn_on: right_led
- script.execute:
id: set_volume
volume: 0.05
- delay: 750ms
- while:
condition:
binary_sensor.is_on: volume_up
then:
- script.execute:
id: set_volume
volume: 0.05
- delay: 150ms
on_release:
then:
- light.turn_off: right_led
- platform: esp32_touch
id: action
pin: GPIO3
threshold: 751000
on_multi_click:
- timing: # double click
- ON for at most 0.5s
- OFF for at most 0.5s
- ON for at most 0.5s
- OFF for at least 0.25s
then:
- if:
condition:
media_player.is_playing
then:
- media_player.pause
- media_player.speaker.play_on_device_media_file:
media_file: knock
announcement: true
- wait_until: media_player.is_announcing
- wait_until: media_player.is_idle
- micro_wake_word.stop
- binary_sensor.template.publish:
id: conversation_mode
state: ON
- delay: 50ms
- voice_assistant.start_continuous # TODO causes the mic to stop for some reason after mode was ended by user, buggy!
- timing: # single click
- ON for at most 1s
- OFF for at least 0.5s
then:
- if:
condition:
media_player.is_announcing
then:
- media_player.stop:
announcement: true
- if:
condition:
voice_assistant.is_running
then:
- voice_assistant.stop
- binary_sensor.template.publish:
id: conversation_mode
state: OFF
else:
- if: # switch between pause/play
condition:
media_player.is_playing
then:
- media_player.pause:
else:
- if:
condition:
media_player.is_paused
then:
- media_player.play
else:
- if: # if not paused, activate va
condition:
and:
- not:
voice_assistant.is_running
- lambda: return id(booted);
- not:
binary_sensor.is_on: mute_switch
then:
- media_player.speaker.play_on_device_media_file:
media_file: wakeup
announcement: true
- wait_until: media_player.is_announcing
- wait_until: media_player.is_idle
- delay: 50ms
- voice_assistant.start
- timing: # long press, reset everything, still a bit buggy
- ON for 1s to 3s
- OFF for at least 0.25s
then:
- voice_assistant.stop
- micro_wake_word.stop
- media_player.stop
- lambda: id(mic_off) = true;
- media_player.speaker.play_on_device_media_file:
media_file: knock
announcement: true
- wait_until: media_player.is_announcing
- wait_until: media_player.is_idle
- media_player.speaker.play_on_device_media_file:
media_file: knock
announcement: true
- wait_until: media_player.is_announcing
- lambda: id(mic_off) = false;
- wait_until: media_player.is_idle
- binary_sensor.template.publish:
id: conversation_mode
state: OFF
- script.execute: reset_led
- script.wait: reset_led
- script.execute: reset_speaker_microphone
- script.wait: reset_speaker_microphone
#- lambda: id(nabu_mic).start();
- micro_wake_word.start
- platform: gpio
id: mute_switch
icon: mdi:microphone-message-off
pin:
number: GPIO38
mode: INPUT_PULLUP
name: "Muted (Hardware Switch)"
on_press:
- media_player.speaker.play_on_device_media_file:
media_file: mute
announcement: true
- wait_until: media_player.is_announcing
- wait_until: media_player.is_idle
- script.execute: turn_off_wake_word
on_release:
- media_player.speaker.play_on_device_media_file:
media_file: unmute
announcement: true
- wait_until: media_player.is_announcing
- wait_until: media_player.is_idle
- script.execute: turn_on_wake_word
light:
- platform: esp32_rmt_led_strip
id: leds
pin: GPIO11
chipset: SK6812
num_leds: 6
rgb_order: GRB
default_transition_length: 0s
gamma_correct: 2.8
- platform: partition
id: left_led
segments:
- id: leds
from: 0
to: 0
default_transition_length: 100ms
- platform: partition
id: top_led
segments:
- id: leds
from: 1
to: 4
default_transition_length: 100ms
effects:
- pulse:
name: pulse
transition_length: 250ms
update_interval: 250ms
- pulse:
name: slow_pulse
transition_length: 1s
update_interval: 2s
- addressable_lambda:
name: show_volume
update_interval: 50ms
lambda: |-
int int_volume = int(id(onju_out).volume * 100.0f * it.size());
int full_leds = int_volume / 100;
int last_brightness = int_volume % 100;
int i = 0;
for(; i < full_leds; i++) {
it[i] = Color::WHITE;
}
if(i < 4) {
it[i++] = Color(64, 64, 64).fade_to_white(last_brightness*256/100);
}
for(; i < it.size(); i++) {
it[i] = Color(64, 64, 64);
}
- addressable_twinkle:
name: listening_ww
twinkle_probability: 1%
- addressable_twinkle:
name: listening
twinkle_probability: 45%
- addressable_scan:
name: processing
move_interval: 80ms
- addressable_twinkle: # changed from default onju
name: speaking
twinkle_probability: 45%
- addressable_random_twinkle:
name: random_twinkle
twinkle_probability: 45%
- platform: partition
id: right_led
segments:
- id: leds
from: 5
to: 5
default_transition_length: 100ms
script:
- id: reset_led
then:
- if:
condition:
- lambda: return id(notification);
then:
- light.turn_on:
id: top_led
blue: 100%
red: 100%
green: 0%
brightness: 100%
effect: slow_pulse
else:
- if:
condition:
and:
- switch.is_on: use_wake_word
- switch.is_on: flicker_wake_word
- lambda: return id(internal_flicker);
- binary_sensor.is_off: mute_switch
then:
- if:
condition:
- binary_sensor.is_off: conversation_mode
then:
- light.turn_on:
id: top_led
blue: 100%
red: 0%
green: 100%
brightness: 60%
effect: listening_ww
else:
- light.turn_on:
id: top_led
blue: 0%
red: 100%
green: 100%
brightness: 60%
effect: listening_ww
else:
- light.turn_off: top_led
- id: turn_on_notification
then:
- lambda: id(notification) = true;
- script.execute: reset_led
- id: clear_notification
then:
- lambda: id(notification) = false;
- script.execute: reset_led
- id: reset_speaker_microphone # new
then:
- lambda: id(i2s_audio_speaker).stop();
- lambda: id(nabu_mic).stop();
- delay: 250ms
- lambda: id(nabu_mic).start();
- id: stop_speaker_start_microphone # new, tuned timings
then:
- if:
condition:
not:
- lambda: return id(mic_off);
then:
- delay: 125ms
- lambda: id(i2s_audio_speaker).stop();
- delay: 125ms
- lambda: id(nabu_mic).start();
- id: set_volume
mode: restart
parameters:
volume: float
then:
- media_player.volume_set:
id: onju_out
volume: !lambda return clamp(id(onju_out).volume+volume, 0.0f, 1.0f);
- id: show_volume
mode: restart
then:
- light.turn_on:
id: top_led
effect: show_volume
- delay: 1s
- lambda: id(volume_change) = false;
- script.execute: reset_led
- id: turn_on_wake_word
then:
- if:
condition:
and:
- binary_sensor.is_off: mute_switch
- switch.is_on: use_wake_word
then:
#- lambda: id(nabu_mic).start();
- micro_wake_word.start
- lambda: id(internal_flicker) = true;
- delay: 50ms
- script.execute: reset_led
else:
- logger.log:
tag: "turn_on_wake_word"
format: "Trying to start listening for wake word, but %s"
args:
[
'id(mute_switch).state ? "mute switch is on" : "use wake word toggle is off"',
]
level: "INFO"
- id: turn_off_wake_word
then:
- micro_wake_word.stop
- delay: 250ms
#- lambda: id(nabu_mic).stop();
- lambda: id(internal_flicker) = false;
- script.execute: reset_led
- id: calibrate_touch
parameters:
button: int
then:
- lambda: |-
static uint8_t thresh_indices[3] = {0, 0, 0};
static uint32_t sums[3] = {0, 0, 0};
static uint8_t qsizes[3] = {0, 0, 0};
static uint16_t consecutive_anomalies_per_button[3] = {0, 0, 0};
uint32_t newval;
uint32_t* calibration_values;
switch(button) {
case 0:
newval = id(volume_down).get_value();
calibration_values = id(touch_calibration_values_left);
break;
case 1:
newval = id(action).get_value();
calibration_values = id(touch_calibration_values_center);
break;
case 2:
newval = id(volume_up).get_value();
calibration_values = id(touch_calibration_values_right);
break;
default:
ESP_LOGE("touch_calibration", "Invalid button ID (%d)", button);
return;
}
if(newval == 0) return;
//ESP_LOGD("touch_calibration", "[%d] qsize %d, sum %d, thresh_index %d, consecutive_anomalies %d", button, qsizes[button], sums[button], thresh_indices[button], consecutive_anomalies_per_button[button]);
//ESP_LOGD("touch_calibration", "[%d] New value is %d", button, newval);
if(qsizes[button] == 5) {
float avg = float(sums[button])/float(qsizes[button]);
if((fabs(float(newval)-avg)/avg) > id(thresh_percent)) {
consecutive_anomalies_per_button[button]++;
//ESP_LOGD("touch_calibration", "[%d] %d anomalies detected.", button, consecutive_anomalies_per_button[button]);
if(consecutive_anomalies_per_button[button] < 10)
return;
}
}
//ESP_LOGD("touch_calibration", "[%d] Resetting consecutive anomalies counter.", button);
consecutive_anomalies_per_button[button] = 0;
if(qsizes[button] == 5) {
//ESP_LOGD("touch_calibration", "[%d] Queue full, removing %d.", button, id(touch_calibration_values)[thresh_indices[button]]);
sums[button] -= (uint32_t) *(calibration_values+thresh_indices[button]);// id(touch_calibration_values)[thresh_indices[button]];
qsizes[button]--;
}
*(calibration_values+thresh_indices[button]) = newval;
sums[button] += newval;
qsizes[button]++;
thresh_indices[button] = (thresh_indices[button] + 1) % 5;
//ESP_LOGD("touch_calibration", "[%d] Average value is %d", button, sums[button]/qsizes[button]);
uint32_t newthresh = uint32_t((sums[button]/qsizes[button]) * (1.0 + id(thresh_percent)));
//ESP_LOGD("touch_calibration", "[%d] Setting threshold %d", button, newthresh);
switch(button) {
case 0:
id(volume_down).set_threshold(newthresh);
break;
case 1:
id(action).set_threshold(newthresh);
break;
case 2:
id(volume_up).set_threshold(newthresh);
break;
default:
ESP_LOGE("touch_calibration", "Invalid button ID (%d)", button);
return;
}
switch:
- platform: template
name: Use Wake Word
id: use_wake_word # TODO: if reenabled from off, the mic crahses, buggy!
icon: mdi:microphone-message
entity_category: config
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
on_turn_on:
- script.execute: turn_on_wake_word
on_turn_off:
- script.execute: turn_off_wake_word
- platform: template
name: Wake Word Listening Light
id: flicker_wake_word
icon: mdi:microphone-settings
entity_category: config
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
on_turn_on:
- lambda: id(internal_flicker) = true;
- script.execute: reset_led
on_turn_off:
- lambda: id(internal_flicker) = false;
- script.execute: reset_led
- platform: gpio
id: dac_mute
icon: mdi:volume-off
restore_mode: ALWAYS_OFF
pin:
number: GPIO21
inverted: True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment