Craftiarenko
Published © GPL3+

Voice assistant with a mechanical display

A voice assistant that has additional sensors and a mechanical display.

IntermediateFull instructions providedOver 1 day843
Voice assistant with a mechanical display

Things used in this project

Hardware components

Wemos LOLIN32 v1.0.0
×1
INMP441
×1
MAX98357
×1
BME680
×1
HDC1080
×1
APDS-9960
×1
Speaker 2W 8ohm 70x30
×1
Hall Effect Sensor A3144
only for stepper motor version
×1
USB Type C Connector Board
×1
WS2812B LED module
×4
Level Shifter Board
SparkFun Level Shifter Board
×1
28BYJ-48 + ULN2003 driver
×1
Resistor 0805 100K
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Hex screw M3x15
×10
Hex screw M3x5
×6
Hex screw M2x5
×5
Magnet 3x1 mm
only for stepper motor version
×1
Aluminium rivet
×10

Software apps and online services

Home Assistant
Home Assistant

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
Solder Wire, Lead Free
Solder Wire, Lead Free
Soldering iron (generic)
Soldering iron (generic)
Multitool, Screwdriver
Multitool, Screwdriver
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Custom parts and enclosures

STL files

zip archive with all STL files of the project

Schematics

ESP32 circuit

Schematic diagram of the assistant

Code

assistant.yaml

YAML
esphome assistant configuration
esphome:
  name: esp-assistant
  friendly_name: esp-assistant

esp32:
  board: lolin32
  framework:
    type: arduino

# Enable logging
logger:
  # level: DEBUG
  baud_rate: 0

# Enable Home Assistant API
api:
  encryption:
    key: "your-encryption-key"

ota:
  password: "your-password"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: none
  output_power: 20dB

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "esp-assistant Fallback Hotspot"
    password: "your-ap-password"

captive_portal:

button:
  - platform: restart
    name: "Assistant Restart"

i2c:
  sda: 19
  scl: 17
  scan: False
  id: bus_a
  frequency: 400kHz

bme680_bsec:
  address: 0x77
  sample_rate: ulp

apds9960:
  id: adps9960_sensor
  address: 0x39
  update_interval: 60s

text_sensor:
  - platform: bme680_bsec
    iaq_accuracy:
      name: "BME680 IAQ Accuracy"

  - platform: template
    name: "BME680 IAQ Classification"
    icon: "mdi:checkbox-marked-circle-outline"
    lambda: |-
      if ( int(id(iaq).state) <= 50) {
        return {"Excellent"};
      }
      else if (int(id(iaq).state) >= 51 && int(id(iaq).state) <= 100) {
        return {"Good"};
      }
      else if (int(id(iaq).state) >= 101 && int(id(iaq).state) <= 150) {
        return {"Lightly polluted"};
      }
      else if (int(id(iaq).state) >= 151 && int(id(iaq).state) <= 200) {
        return {"Moderately polluted"};
      }
      else if (int(id(iaq).state) >= 201 && int(id(iaq).state) <= 250) {
        return {"Heavily polluted"};
      }
      else if (int(id(iaq).state) >= 251 && int(id(iaq).state) <= 350) {
        return {"Severely polluted"};
      }
      else if (int(id(iaq).state) >= 351) {
        return {"Extremely polluted"};
      }
      else {
        return {"error"};
      }

binary_sensor:
  - platform: apds9960
    direction: UP
    id: apds9960_up

  - platform: apds9960
    direction: DOWN
    id: apds9960_down

  - platform: apds9960
    direction: LEFT
    id: apds9960_left

  - platform: apds9960
    direction: RIGHT
    id: apds9960_right
    on_press:
      then:
        - script.execute:
            id: step_backlash
            position: 682
        - script.wait: step_backlash
        - voice_assistant.start:

  - platform: gpio
    pin:
      number: 23
      inverted: true
      mode:
        input: true
        pullup: true
    id: position_sensor
    name: Zero Position Sensor
    on_press:
      - stepper.report_position:
          id: picture_stepper
          position: 0

sensor:
  - platform: hdc1080
    temperature:
      name: "HDC1080 Temperature"
    humidity:
      name: "HDC1080 Humidity"
    address: 0x40
    update_interval: 60s
    id: hdc1080_sensor

  - platform: bme680_bsec
    temperature:
      name: "BME680 Temperature"
    pressure:
      name: "BME680 Pressure"
    humidity:
      name: "BME680 Humidity"
    gas_resistance:
      name: "BME680 Gas Resistance"
    iaq:
      name: "BME680 IAQ"
      id: iaq
    co2_equivalent:
      name: "BME680 CO2 Equivalent"
    breath_voc_equivalent:
      name: "BME680 Breath VOC Equivalent"

light:
  - platform: neopixelbus
    type: GRB
    variant: WS2812
    pin: 22
    num_leds: 4
    name: "RGB Light"
    id: ws2812_led
    effects:
      - pulse:
          transition_length: 250ms
          update_interval: 250ms

i2s_audio:  
  - i2s_lrclk_pin: 13
    i2s_bclk_pin: 14
    id: audio_bus

media_player:
  - platform: i2s_audio
    dac_type: external
    id: max98357_speaker
    i2s_audio_id: audio_bus
    i2s_dout_pin: 27
    mode: mono
    name: ESPHome I2S Media Player

microphone:
  - platform: i2s_audio
    i2s_audio_id: audio_bus
    id: inmp441_mic
    adc_type: external
    i2s_din_pin: 26
    pdm: false

voice_assistant:
  id: va
  microphone: inmp441_mic
  # media_player: max98357_speaker

  on_start:
    - media_player.stop:
    - light.turn_on:
        id: ws2812_led
        blue: 100%
        red: 0%
        green: 0%
        effect: none

  on_tts_start:
    - script.execute:
        id: step_backlash
        position: 1300
    - light.turn_on:
        id: ws2812_led
        blue: 50%
        red: 50%
        green: 50%
        effect: none

  on_tts_end:
    - wait_until:
        not: 
          script.is_running: step_backlash
    - media_player.play_media: !lambda return x;
    - light.turn_on:
        id: ws2812_led
        blue: 0%
        red: 0%
        green: 100%
        effect: pulse

  on_end:
    - delay: 1s
    - wait_until:
        not: 
          script.is_running: step_backlash
    - delay: 1s
    - wait_until:
        not:
          media_player.is_playing: max98357_speaker
    - light.turn_off: ws2812_led
    - script.execute:
        id: step_backlash
        position: 0

  on_error:
    - light.turn_on:
        id: ws2812_led
        blue: 0%
        red: 100%
        green: 0%
        effect: none
    - delay: 1s
    - light.turn_off: ws2812_led
    - script.execute:
        id: step_backlash
        position: 0

script:
  - id: step_backlash
    parameters:
      position: int
    then:
      - stepper.set_target:
          id: picture_stepper
          target: !lambda |- 
            if(position > id(picture_stepper).current_position) return position + 200;
            else return position - 200;
      - wait_until:
          condition:
            lambda: |-
              return id(picture_stepper).current_position == id(picture_stepper).target_position;
      - stepper.set_target:
          id: picture_stepper
          target: !lambda return position;
      - wait_until:
          condition:
            lambda: |-
              return id(picture_stepper).current_position == id(picture_stepper).target_position;

switch:
  - platform: template
    id: talks_switch
    name: "Talk"
    optimistic: True
    turn_on_action:
      - script.execute:
          id: step_backlash
          position: 682
      - script.wait: step_backlash
      - voice_assistant.start:
    turn_off_action:
      - voice_assistant.stop:

number:
  - platform: template
    id: desired_position
    name: "Desired Position"
    max_value: 2
    min_value: 0
    optimistic: True
    step: 1
    on_value:
      then:
        - script.execute:
            id: step_backlash
            position: !lambda 'return x*682;'

stepper:
  - platform: uln2003
    id: picture_stepper
    pin_a: 33
    pin_b: 32
    pin_c: 21
    pin_d: 5
    max_speed: 400 steps/s
    sleep_when_done: True

Credits

Craftiarenko

Craftiarenko

5 projects • 10 followers
Making simple things difficult :)

Comments