top of page
Search

How to Create a DIY Star Wars Lo-La59 Shoulder Buddy with ESP32 Home Assistant Integration

  • Writer: Logan Morris
    Logan Morris
  • May 28, 2024
  • 5 min read

Updated: Jun 2, 2024

In this article, we will look at how to create a Home Assistant ESP32 powered Lola-59 Shoulder Buddy.


Requirements

  1. Home Assistant up and running.

  2. ESPHome is setup and running.

  3. Seeed Studio XIAO ESP32S3


Parts Required



Preparing the Lo-La59 Droid 


Congratulations! You've purchased the Lo-La59 droid and are preparing to mod it for miles of fun and automation. A Dremel of any kind is heavily recommended as you will basically be removing all of the inside of the toy to make room for the SG90 servos, DFPlayer, Step-Up Converter/LiPo Battery Charger and the ESP32S3 Board.


  1. Remove the entire area underneath the wings as that is where the servos will go. You can watch the video here so you can get an idea of where they go.

  2. Once you've done that level out the inside of the droid with the Dremel so that you can get a small piece of flat plastic in there so the servos can be placed on top of it.

  3. The droid comes with a small PCB board inside of it. Remove it as you will not need it anymore. You can place the ESP32S3 in its place if you want or you can mount it somewhere else inside the droid.

  4. The wings have notches in it that allowed it to snap in place before. You are going to smooth those out with the Dremel so that wings can move freely up and down.

  5. Because of how the wing moves you cant attach the servo arm directly to it to move. Attach a tight coiled spring to the servo arm and the wing. The tension will cause the wing to move up and down

  6. The USB-C power input and power switch are placed underneath the droid, but feel free to put them where you feel like they should go.

  7. Where the battery slot was, I cut out all of that and placed a slim piece of plastic right over the slot and mounted the step up converter and DFPlayer there.

  8. The 8 Ohm speaker can either go where it originally was or you can mount it in the wing like I did.

  9. You may find it difficult to put everything together depending on where you place things so I used JST connectors to connect things even for power and ground.

  10. Note: Its going to be scary cutting into the toy, But don't be shy, anything don't on the inside wont be seen from the top and hot glue/gorilla glue will be your best friend.

Schematics for the Wiring



Coding the ESP32


Below is the code for the droid. You can find a video of the breakdown of the code here.

substitutions:
  name: esphome-web-812b34
  friendly_name: LOLA_S-SEED

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  name_add_mac_suffix: false
  platformio_options:
    board_build.flash_mode: dio
  project:
    name: esphome.web
    version: '1.0'
  on_boot:
     - priority: -800
       then:
         - delay: 5s
         - light.turn_on: 
             id: lola_light
         - dfplayer.set_volume: 20
esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  services:
  - service: dfplayer_next
    then:
      - dfplayer.play_next:
  - service: dfplayer_previous
    then:
      - dfplayer.play_previous:
  - service: dfplayer_play
    variables:
      file: int
    then:
      - dfplayer.play: !lambda 'return file;'
  - service: dfplayer_play_loop
    variables:
      file: int
      loop_: bool
    then:
      - dfplayer.play:
          file: !lambda 'return file;'
          loop: !lambda 'return loop_;'
  - service: dfplayer_play_folder
    variables:
      folder: int
      file: int
    then:
      - dfplayer.play_folder:
          folder: !lambda 'return folder;'
          file: !lambda 'return file;'

  - service: dfplayer_play_loop_folder
    variables:
      folder: int
    then:
      - dfplayer.play_folder:
          folder: !lambda 'return folder;'
          loop: true

  - service: dfplayer_set_device_tf
    then:
      - dfplayer.set_device: TF_CARD

  - service: dfplayer_set_device_usb
    then:
      - dfplayer.set_device: USB

  - service: dfplayer_set_volume
    variables:
      volume: int
    then:
      - dfplayer.set_volume: !lambda 'return volume;'
  - service: dfplayer_set_eq
    variables:
      preset: int
    then:
      - dfplayer.set_eq: !lambda 'return static_cast<dfplayer::EqPreset>(preset);'

  - service: dfplayer_sleep
    then:
      - dfplayer.sleep

  - service: dfplayer_reset
    then:
      - dfplayer.reset

  - service: dfplayer_start
    then:
      - dfplayer.start

  - service: dfplayer_pause
    then:
      - dfplayer.pause

  - service: dfplayer_stop
    then:
      - dfplayer.stop

  - service: dfplayer_random
    then:
      - dfplayer.random

  - service: dfplayer_volume_up
    then:
      - dfplayer.volume_up

  - service: dfplayer_volume_down
    then:
      - dfplayer.volume_down
uart:
  tx_pin: GPIO02
  rx_pin: GPIO01
  baud_rate: 9600

dfplayer:
  on_finished_playback:
    then:
      logger.log: 'Playback finished event'
#########################################################################################
#Global Integers
globals:
- id: randomDelay
  type: int
  restore_value: no
  initial_value: '0'
###############################################################################################
#Light
light:

  - platform: binary
    name: "Lola Bottom Lights"
    output: bottom_blue
    id: lola_light

#Outputs
output:
  - platform: ledc
    id: bottom_blue
    pin: GPIO05
  - platform: ledc
    id: pwm_output
    pin: GPIO04
    frequency: 50 Hz
  - platform: ledc
    id: pwm_output2
    pin: GPIO09
    frequency: 50 Hz
############################################################################################
#Servo Code
servo:
  - id: right_wing
    output: pwm_output
  - id: left_wing
    output: pwm_output2

number:
  - platform: template
    id: servo_value
    name: Right Wing
    min_value: -100
    initial_value: 0
    max_value: 100
    step: 1
    optimistic: true
    set_action:
      then:
        - servo.write:
            id: right_wing
            level: !lambda 'return x / 100.0;'

  - platform: template
    id: servo_value1
    name: Left Wing
    min_value: -100
    initial_value: 0
    max_value: 100
    step: 1
    optimistic: true
    set_action:
      then:
        - servo.write:
            id: left_wing
            level: !lambda 'return x / 100.0;' 
##############################################################################################
#Switch (Button) 


binary_sensor:
  - platform: gpio
    pin: 
      number: 6
      mode:
        input: true
        pullup: true
    name: Lola Button"
    id: lola_button
    filters:
      - invert: 
    on_click:
    - min_length: 50ms
      max_length: 350ms
      then:
        - script.execute: wings_animation
    - min_length: 3s
      max_length: 5s
      then:
        - script.stop: wings_animation
        - delay: 3s
        - script.execute: wings_closed
##############################################################################################
#Scripts#
script:
  - id: wing_shuttering 
    then:
      - dfplayer.play: 5
      - delay: .004s      
      - lambda: id(right_wing).write(-.55);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.52);
      - delay: .01s
      - lambda: id(right_wing).write(-.26);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.39);
      - delay: .01s
      - delay: .004s      
      - lambda: id(right_wing).write(-.55);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.52);
      - delay: .01s
      - lambda: id(right_wing).write(-.26);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.39);
      - delay: .01s
      - delay: .004s      
      - lambda: id(right_wing).write(-.55);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.52);
      - delay: .01s
      - lambda: id(right_wing).write(-.26);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.39);
      - delay: .01s
      - delay: .004s      
      - lambda: id(right_wing).write(-.55);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.52);
      - delay: .01s
      - lambda: id(right_wing).write(-.26);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.39);
      - delay: .01s
      - delay: .004s      
      - lambda: id(right_wing).write(-.55);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.52);
      - delay: .01s
      - lambda: id(right_wing).write(-.26);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.39);
      - delay: .01s
      - delay: .004s      
      - lambda: id(right_wing).write(-.55);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.52);
      - delay: .01s
      - lambda: id(right_wing).write(-.26);   #.48%
      - delay: .004s
      - lambda: id(left_wing).write(.39);
      - delay: .01s
      - dfplayer.play: 17
  - id: wings_open
    then:
      - lambda: id(right_wing).write(.27);
      - lambda: id(left_wing).write(.13);
  - id: wings_closed
    then:
      - lambda: id(right_wing).write(-1.0);
      - lambda: id(left_wing).write(1.0);
  - id: wings_animation
    mode: queued
    then:
      - light.turn_on: lola_light
      - lambda: id(right_wing).write(-1.0);  #MIN-R
      - lambda: id(left_wing).write(.13);   #MAX-L
      - delay: 2s
      - lambda: id(left_wing).write(1.0);   #MIN-L
      - lambda: id(right_wing).write(.27);   #MAX-R
      - delay: 2s
      - lambda: id(right_wing).write(-1.0);  #MIN-R
      - lambda: id(left_wing).write(.13);   #MAX-L
      - delay: 2s
      - script.execute: wing_shuttering
      - lambda: id(left_wing).write(1.0);   #MIN-L
      - lambda: id(right_wing).write(.27);   #MAX-R
      - delay: 2s
      - lambda: id(right_wing).write(-1.0);  #MIN-R
      - lambda: id(left_wing).write(.13);   #MAX-L
      - delay: 2s
      - lambda: id(left_wing).write(1.0);   #MIN-L
      - lambda: id(right_wing).write(.27);   #MAX-R
      - delay: 3s
      - dfplayer.play: 18 
      - delay: 3s 
      - script.execute: wing_shuttering
      - delay: 3s
      - script.execute: wings_open
      - delay: .5s
      - script.execute: wings_closed
      - delay: .5s
      - script.execute: wings_open
      - delay: .5s
      - script.execute: wings_closed
      - delay: .5s
      - dfplayer.play: 5 
      - script.execute: wing_shuttering
      - delay: .5s
      - script.execute: wings_animation

    
   
     
# Allow Over-The-Air updates
ota:

# Allow provisioning Wi-Fi via serial
improv_serial:

wifi:
  # Set up a wifi access point
  ap:
    ssid: "Logan ESP"
    password: ""

# In combination with the `ap` this allows the user
# to provision wifi credentials to the device via WiFi AP.
captive_portal:

dashboard_import:
  package_import_url: github://esphome/firmware/esphome-web/esp32s3.yaml@v2
  import_full_config: true

# Sets up Bluetooth LE (Only on ESP32) to allow the user
# to provision wifi credentials to the device.
esp32_improv:
  authorizer: none

# To have a "next url" for improv serial
web_server:

Congratulations you are done! Rey thanks you!



 
 
 

Comentarios


  • Facebook
  • Twitter
  • Instagram

You Can Build Too

Contact

Ask me anything

Thanks for submitting!

bottom of page