tetele / onju-voice-satellite

An ESPHome config for the Onju Voice which makes it a Home Assistant voice satellite
MIT License
63 stars 7 forks source link

Purple - 4 lights #23

Closed nigels0 closed 1 month ago

nigels0 commented 1 month ago

I assembled one of these glorious devices, the assembly seemed OK, it’s showing up in HA as a voice device and the logs look normal - though isn’t working. It is WiFi connected and starts up OK.

What do the four purple lights mean? Where am I going wrong!

thanks for a great project!

tetele commented 1 month ago

The 4 purple "blinking" lights (not really binking, but rather with random intensity) mean it's listening for wake word.

Could you please ellaborate a bit on "it's not working"? What have you tried? What are you expecting it to do?

nigels0 commented 1 month ago

Well it isn't blinking - just a solid four sets of purple lights.

On powering on, it goes white, then flashes green for a while then just the four purple lights. I've connected it to HA (it seems OK) -

I'm trying to set this up to use OpenWakeWord (the voice parts otherwise work fine from accessing via Assist on my phone. Pressing near the four purple lights doesn't do anything, though volume up/down is working fine.

here is the log: < INFO ESPHome 2024.3.2 INFO Reading configuration /config/esphome/onju-1.yaml... INFO Starting log output from 192.168.10.212 using esphome API INFO Successfully connected to onju-voice1 @ 192.168.10.212 in 0.006s INFO Successful handshake with onju-voice1 @ 192.168.10.212 in 0.077s [16:52:28][I][app:102]: ESPHome version 2024.3.2 compiled on Apr 8 2024, 16:44:37

[16:52:28][C][wifi:408]: Local MAC: 80:65:99:C6:44:70 [16:52:28][C][wifi:413]: SSID: 'WaysIoT'[redacted] [16:52:28][C][wifi:416]: IP Address: 192.168.10.212 [16:52:28][C][wifi:420]: BSSID: 26:5A:4C:2C:EE:D5[redacted]

[16:52:28][C][wifi:423]: Signal strength: -62 dB ▂▄▆█ [16:52:28][C][wifi:427]: Channel: 6 [16:52:28][C][wifi:428]: Subnet: 255.255.255.0 [16:52:28][C][wifi:429]: Gateway: 192.168.10.1 [16:52:28][C][wifi:430]: DNS1: 1.1.1.1 [16:52:28][C][wifi:431]: DNS2: 9.9.9.9

[16:52:28][C][logger:167]: Level: DEBUG [16:52:28][C][logger:169]: Log Baud Rate: 115200 [16:52:28][C][logger:170]: Hardware UART: USB_CDC [16:52:28][C][template.number:050]: Template Number 'Touch threshold percentage' [16:52:28][C][template.number:051]: Optimistic: YES [16:52:28][C][template.number:052]: Update Interval: never [16:52:28][C][esp32_rmt_led_strip:175]: ESP32 RMT LED Strip: [16:52:28][C][esp32_rmt_led_strip:176]: Pin: 11 [16:52:28][C][esp32_rmt_led_strip:177]: Channel: 0 [16:52:28][C][esp32_rmt_led_strip:202]: RGB Order: GRB [16:52:28][C][esp32_rmt_led_strip:203]: Max refresh rate: 0 [16:52:28][C][esp32_rmt_led_strip:204]: Number of LEDs: 6 [16:52:28][C][gpio.binary_sensor:015]: GPIO Binary Sensor 'Disable wake word' [16:52:28][C][gpio.binary_sensor:016]: Pin: GPIO38

[16:52:28][C][light:105]: Default Transition Length: 0.0s [16:52:28][C][light:106]: Gamma Correct: 2.80

[16:52:28][C][light:105]: Default Transition Length: 1.0s [16:52:28][C][light:106]: Gamma Correct: 2.80

[16:52:28][C][light:105]: Default Transition Length: 1.0s [16:52:28][C][light:106]: Gamma Correct: 2.80

[16:52:28][C][light:105]: Default Transition Length: 1.0s [16:52:28][C][light:106]: Gamma Correct: 2.80 [16:52:28][C][template.switch:068]: Template Switch 'Use Wake Word' [16:52:28][C][template.switch:091]: Restore Mode: restore defaults to ON [16:52:28][C][template.switch:057]: Optimistic: YES [16:52:28][C][esp32_touch:073]: Config for ESP32 Touch Hub: [16:52:28][C][esp32_touch:074]: Meas cycle: 0.80ms [16:52:28][C][esp32_touch:075]: Sleep cycle: 2.00ms [16:52:28][C][esp32_touch:095]: Low Voltage Reference: 0.8V [16:52:28][C][esp32_touch:115]: High Voltage Reference: 2.4V [16:52:28][C][esp32_touch:135]: Voltage Attenuation: 0V [16:52:28][C][esp32_touch:169]: Filter mode: IIR_16 [16:52:28][C][esp32_touch:170]: Debounce count: 2 [16:52:28][C][esp32_touch:171]: Noise threshold coefficient: 0 [16:52:28][C][esp32_touch:172]: Jitter filter step size: 0 [16:52:28][C][esp32_touch:191]: Smooth level: IIR_2 [16:52:28][C][esp32_touch:213]: Denoise grade: BIT8 [16:52:28][C][esp32_touch:245]: Denoise capacitance level: L0 [16:52:28][C][esp32_touch:260]: Touch Pad 'volume_down' [16:52:28][C][esp32_touch:261]: Pad: T4 [16:52:28][C][esp32_touch:262]: Threshold: 483444 [16:52:28][C][status:034]: Status Binary Sensor 'api_connection' [16:52:28][C][status:034]: Device Class: 'connectivity' [16:52:28][C][captive_portal:088]: Captive Portal:

[16:52:28][C][mdns:116]: Hostname: onju-voice1 [16:52:28][C][ota:096]: Over-The-Air Updates: [16:52:28][C][ota:097]: Address: onju-voice1.local:3232 [16:52:28][C][ota:103]: OTA version: 2. [16:52:28][C][api:139]: API Server: [16:52:28][C][api:140]: Address: onju-voice1.local:6053 [16:52:28][C][api:142]: Using noise encryption: YES

[16:52:28][C][audio:225]: External DAC channels: 1 [16:52:28][C][audio:226]: I2S DOUT Pin: 12 [16:52:28][C][audio:227]: Mute Pin: GPIO21

tetele commented 1 month ago

So even when you press the top, the volume tabs, anything at all, the logs don't register anything?

Make sure the physical switch is not set to Mute. Try toggling the "Use wake word" virtual switch in HA.

Although, the main issue that all your lights are solid purple. I don't think there's any case in which the config makes all top LEDs turn solid purple. Could you paste the entire config you used, too, please?

nigels0 commented 1 month ago

**definitely purple!

Sorry - I wasn't clear - yes the buttons work - volume down gives:**


[15:05:11][D][binary_sensor:036]: 'volume_down': Sending state ON
[15:05:11][D][light:036]: 'left_led' Setting:
[15:05:11][D][light:047]:   State: ON
[15:05:11][D][light:085]:   Transition length: 1.0s
[15:05:11][D][media_player:059]: 'Onju Voice 1' - Setting
[15:05:11][D][media_player:069]:   Volume: 0.85
[15:05:11][D][light:036]: 'top_led' Setting:
[15:05:11][D][light:109]:   Effect: 'show_volume'
[15:05:11][D][binary_sensor:036]: 'volume_down': Sending state OFF
[15:05:11][D][light:036]: 'left_led' Setting:
[15:05:11][D][light:047]:   State: OFF
[15:05:11][D][light:085]:   Transition length: 1.0s
[15:05:12][D][light:036]: 'top_led' Setting:
[15:05:12][D][light:051]:   Brightness: 100%
[15:05:12][D][light:059]:   Red: 100%, Green: 0%, Blue: 100%
[15:05:12][D][light:085]:   Transition length: 1.0s
[15:05:12][D][light:091]:   Effect: 'None'

The lights go briefly white to show the volume level - then back to four purples!

Here's my config file:


captive_portal:

substitutions:
  name: "onju-voice1"
  friendly_name: "Onju Voice 1"

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  name_add_mac_suffix: false
  min_version: 2023.10.1
  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

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino

logger:
api:
  encryption:
   key: "GBQg79HCLfP6e0JWW/rHLRkpLhctZkt2O/75quNEqO8="
  services:
    - service: start_va
      then:
        - voice_assistant.start
    - service: stop_va
      then:
        - voice_assistant.stop

ota:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Onju-1 Fallback Hotspot"
    password: "IKEtRK2suWwy"

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

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:
  - i2s_lrclk_pin: GPIO13
    i2s_bclk_pin: GPIO18

media_player:
  - platform: i2s_audio
    name: None
    id: onju_out
    dac_type: external
    i2s_dout_pin: GPIO12
    mode: mono
    mute_pin:
      number: GPIO21
      inverted: True

######
# speaker:
#   - platform: i2s_audio
#     id: onju_out
#     dac_type: external
#     i2s_dout_pin: GPIO12
#     mode: stereo
######

microphone:
  - platform: i2s_audio
    id: onju_microphone
    i2s_din_pin: GPIO17
    adc_type: external
    pdm: false

voice_assistant:
  id: va
  microphone: onju_microphone
  media_player: onju_out
######
  # speaker: onju_out
######
  use_wake_word: true
  on_start:
    - light.turn_on:
        id: top_led
        blue: 100%
        red: 0%
        green: 0%
        effect: none
  on_listening:
    - light.turn_on:
        id: top_led
        blue: 100%
        red: 0%
        green: 0%
        brightness: 100%
        effect: pulse
  on_tts_end:
    - media_player.play_media: !lambda return x;
    - light.turn_on:
        id: top_led
        blue: 0%
        red: 20%
        green: 100%
        effect: pulse
  on_end:
    - delay: 100ms
    - wait_until:
        not:
          media_player.is_playing: onju_out
    - script.execute: reset_led
  on_client_connected:
    - if:
        condition:
          and:
            - switch.is_on: use_wake_word
            - binary_sensor.is_off: mute_switch
        then:
          - voice_assistant.start_continuous:
  on_client_disconnected:
    - if:
        condition:
          and:
            - switch.is_on: use_wake_word
            - binary_sensor.is_off: mute_switch
        then:
          - voice_assistant.stop:
  on_error:
    - light.turn_on:
        id: top_led
        blue: 0%
        red: 100%
        green: 0%
    - delay: 1s
    - script.execute: reset_led

number:
  - platform: template
    name: "Touch threshold percentage"
    id: touch_threshold_percentage
    update_interval: never
    entity_category: config
    initial_value: 1.25
    min_value: -1
    max_value: 5
    step: 0.25
    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

binary_sensor:
  - platform: esp32_touch
    id: volume_down
    pin: GPIO4
    threshold: 539000 # 533156-551132
    on_press: 
      then:
        - light.turn_on: left_led
        - script.execute:
            id: set_volume
            volume: -0.05
        - delay: 1s
        - 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
    pin: GPIO2
    threshold: 580000 # 575735-593064
    on_press: 
      then:
        - light.turn_on: right_led
        - script.execute:
            id: set_volume
            volume: 0.05
        - delay: 1s
        - 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 # 745618-767100
    on_click:
      - if:
          condition:
            or:
              - switch.is_off: use_wake_word
              - binary_sensor.is_on: mute_switch
          then:
            - if:
                condition: voice_assistant.is_running
                then:
                  - voice_assistant.stop:
                  - script.execute: reset_led
                else:
                  - voice_assistant.start:
          else:
            - voice_assistant.stop
            - delay: 1s
            - script.execute: reset_led
            - script.wait: reset_led
            - voice_assistant.start_continuous:

  - platform: gpio
    id: mute_switch
    pin:
      number: GPIO38
      mode: INPUT_PULLUP
    name: Disable wake word
    on_press:
      - script.execute: turn_on_wake_word
    on_release:
      - script.execute: turn_off_wake_word

  - platform: status
    id: api_connection
    filters:
      - delayed_on: 1s
    on_press:
      - if:
          condition:
            and:
              - switch.is_on: use_wake_word
              - binary_sensor.is_off: mute_switch
          then:
            - voice_assistant.start_continuous:
    on_release:
      - if:
          condition:
            and:
              - switch.is_on: use_wake_word
              - binary_sensor.is_off: mute_switch
          then:
            - voice_assistant.stop:

light:
  - platform: esp32_rmt_led_strip
    id: leds
    pin: GPIO11
    chipset: SK6812
    num_leds: 6
    rgb_order: grb
    rmt_channel: 0
    default_transition_length: 0s
    gamma_correct: 2.8
  - platform: partition
    id: left_led
    segments:
      - id: leds
        from: 0
        to: 0
  - platform: partition
    id: top_led
    segments:
      - id: leds
        from: 1
        to: 4
    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(0,0,0).fade_to_white(last_brightness*256/100);
            }
            for(; i < it.size(); i++) {
              it[i] = Color::BLACK;
            }
  - platform: partition
    id: right_led
    segments:
      - id: leds
        from: 5
        to: 5

script:
  - id: reset_led
    then:
      - if:
          condition:
            and:
              - switch.is_on: use_wake_word
              - binary_sensor.is_off: mute_switch
          then:
            - light.turn_on:
                id: top_led
                blue: 100%
                red: 100%
                green: 0%
                brightness: 100%
                effect: none
          else:
            - light.turn_off: top_led

  - 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);
      - light.turn_on:
          id: top_led
          effect: show_volume
      - delay: 1s
      - 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(va).set_use_wake_word(true);
            - if:
                condition:
                  not:
                    - voice_assistant.is_running
                then:
                  - voice_assistant.start_continuous
            - script.execute: reset_led

  - id: turn_off_wake_word
    then:
      - voice_assistant.stop
      - lambda: id(va).set_use_wake_word(false);
      - script.execute: reset_led

  - id: calibrate_touch
    parameters:
      button: int
    then:
      - lambda: |-
          static byte thresh_indices[3] = {0, 0, 0};
          static uint32_t sums[3] = {0, 0, 0};
          static byte qsizes[3] = {0, 0, 0};
          static int 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
    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
tetele commented 1 month ago

Please format the code properly.

nigels0 commented 1 month ago

Sorry. Done.

tetele commented 1 month ago

Ah, now I get it! I suggest you either update to the latest version of my config or request support from the guy from where you got your config :)

Unlike Mark, I actually keep it up to date, because I understand what's there since I've written it myself.

nigels0 commented 1 month ago

yes, that fixed it thanks.