--- /dev/null
+# Based on https://github.com/joshr120/PD-Stepper/blob/main/Software/ESPHome/PD-Stepper-Blinds-Advanced.yaml
+# (GPLv3)
+
+
+esp32:
+ board: esp32-s3-devkitc-1
+ framework:
+ type: esp-idf
+
+esphome:
+ name: ${name}
+ platformio_options:
+ board_build.flash_mode: dio
+ upload_speed: 921600
+ on_boot:
+ - tmc2209.configure:
+ microsteps: 2 # ${microsteps}
+ interpolation: true
+ tcool_threshold: 400
+ - tmc2209.stallguard:
+ threshold: 45 #set lower if stall gaurd triggering too often
+ - tmc2209.currents:
+ ihold: 0
+ tpowerdown: 0
+ iholddelay: 0
+ # irun: 16
+ run_current: 800m #Set your desired current here (800m = 800mA, 1 = 1A etc.)
+ standstill_mode: freewheeling
+
+ # set PD voltage # 5V 9V 12V 15V 20V
+ # CFG1 # 1 0 0 0 0
+ # CFG2 # - 0 0 1 1
+ # CFG3 # - 0 1 1 0
+ - lambda: |-
+ int voltage = 15; // 5, 9, 12, 15 or 20.
+ bool cfg1 = false, cfg2 = false, cfg3 = false;
+ if (voltage == 5) {
+ cfg1 = true;
+ } else if (voltage == 9) {
+ // all off;
+ } else if (voltage == 12) {
+ cfg3 = true;
+ } else if (voltage == 15) {
+ cfg2 = cfg3 = true;
+ } else if (voltage == 20) {
+ cfg2 = true;
+ } else ESP_LOGE("USB-PD", "Invalid voltage %dV selected", voltage);
+ id(CFG1_pin).set_state(cfg1);
+ id(CFG2_pin).set_state(cfg2);
+ id(CFG3_pin).set_state(cfg3);
+
+
+ - delay: 0.5s
+ - lambda: id(sensored_home_pos) = id(encoder)->get_state(); # Set the home position to power on position
+ # - button.press: home
+
+packages:
+ base: !include base.yaml
+
+wifi:
+ power_save_mode: none
+ networks:
+ - ssid: !secret wifi_ssid
+ password: !secret wifi_pw
+ bssid: !secret lantiq_bssid
+ priority: 1
+
+logger:
+ level: INFO
+
+sun:
+ latitude: !secret latitude
+ longitude: !secret longitude
+
+ on_sunrise:
+ - then:
+ - cover.open: pd_blinds
+ on_sunset:
+ - then:
+ - cover.close: pd_blinds
+
+external_components:
+ - source: github://slimcdk/esphome-custom-components
+ components: [tmc2209_hub, tmc2209, stepper]
+
+mqtt:
+ on_connect:
+ then:
+ - light.turn_on: led2
+ on_disconnect:
+ then:
+ - light.turn_off: led2
+
+i2c:
+ sda: 8
+ scl: 9
+ scan: true
+
+uart:
+ tx_pin: 17
+ rx_pin: 18
+ baud_rate: 712000
+
+output:
+ - platform: ledc
+ pin: 12
+ id: led1_output
+
+ - platform: ledc
+ pin: 10
+ id: led2_output
+
+ - platform: gpio
+ pin: GPIO38
+ id: CFG1_pin
+ - platform: gpio
+ pin: GPIO48
+ id: CFG2_pin
+ - platform: gpio
+ pin: GPIO47
+ id: CFG3_pin
+
+light:
+ - platform: monochromatic
+ output: led1_output
+ id: led1
+ name: LED 1
+ - platform: status_led
+ output: led2_output
+ id: led2
+ name: Status LED
+
+globals:
+ - id: encoder_tracking_
+ type: int32_t[2]
+ restore_value: no
+
+ - id: encoder_offset_
+ type: int32_t
+ restore_value: no
+
+ - id: sensored_home_pos
+ type: int32_t
+ restore_value: no
+ initial_value: "0"
+
+stepper:
+ - platform: tmc2209
+ id: motor
+ max_speed: 600 steps/s
+ acceleration: 400 steps/s^2
+ deceleration: 400 steps/s^2
+ rsense: 100 mOhm
+ vsense: False
+ enn_pin: 21
+ diag_pin: 16
+ index_pin: 11
+ # step_pin: 5
+ # dir_pin: 6
+ on_stall:
+ - logger.log:
+ format: "Motor stalled!"
+ level: INFO
+ - stepper.stop: motor #un-comment this to disable on stall
+ - lambda: id(sensored_home_pos) = id(encoder)->get_state(); #un-comment this to not reset home on stall
+ - cover.template.publish:
+ id: pd_blinds
+ state: OPEN
+ - light.turn_on:
+ id: led1
+ transition_length: 0s
+ - delay: 250ms
+ - light.turn_off:
+ id: led1
+ transition_length: 1s
+
+button:
+ - platform: restart
+ name: Restart
+
+ - platform: template
+ name: Stop
+ on_press:
+ - stepper.stop: motor
+
+binary_sensor:
+ - platform: gpio
+ name: Power Good
+ pin:
+ number: 15
+ mode: INPUT
+ inverted: true
+ device_class: power
+ filters:
+ - delayed_on: 10ms
+
+ - platform: gpio
+ name: Button 1
+ pin:
+ number: 35
+ mode: INPUT
+ inverted: true
+ id: btn1
+ filters:
+ - delayed_on: 10ms
+ on_press:
+ - cover.close: pd_blinds
+
+ - platform: gpio
+ name: Button 2
+ id: btn2
+ pin:
+ number: 36
+ mode: INPUT
+ inverted: true
+ filters:
+ - delayed_on: 10ms
+ on_press:
+ - tmc2209.disable: motor
+
+ - platform: gpio
+ name: Button 3
+ id: btn3
+ pin:
+ number: 37
+ mode: INPUT
+ inverted: true
+ filters:
+ - delayed_on: 10ms
+ on_press:
+ - cover.open: pd_blinds
+
+as5600:
+ slow_filter: 16x
+
+sensor:
+ - platform: as5600
+ id: encoder_status
+ update_interval: 60s
+ status:
+ name: Encoder status
+
+ - platform: as5600
+ name: Encoder
+ id: encoder
+ internal: true
+ update_interval: 0ms
+ filters:
+ - delta: 2 # throttle the high polling rate to only act on value changes
+ # compute absolute position from angle value
+ - lambda: |
+ const uint16_t curr = x;
+ int16_t delta = curr - id(encoder_tracking_)[0];
+ if (delta > 2048)
+ delta -= 4096;
+ else if (delta < -2047)
+ delta += 4096;
+ ESP_LOGD("angle", "Old %d new %d delta %d now %d home %d", id(encoder_tracking_)[0], curr, delta,
+ id(encoder_tracking_)[1] + delta, id(sensored_home_pos) );
+ id(encoder_tracking_)[0] = curr;
+ id(encoder_tracking_)[1] -= delta;
+ if (id(encoder_tracking_)[1] < id(sensored_home_pos)) {
+ ESP_LOGI("home", "Adjusted to %d", id(encoder_tracking_)[1]);
+ id(sensored_home_pos) = id(encoder_tracking_)[1];
+ }
+ return id(encoder_tracking_)[1];
+ accuracy_decimals: 0
+ state_class: measurement
+
+ - platform: adc
+ pin: 4
+ name: VBUS Voltage
+ update_interval: 10s
+ attenuation: 12dB
+ filters:
+ - multiply: 8.47742
+
+cover:
+ - platform: template
+ id: pd_blinds
+ name: PD Stepper Blinds
+ has_position: true
+ assumed_state: false
+ # internal: true
+ lambda: |-
+ int percent = 100.0 * (id(encoder)->get_state()-id(sensored_home_pos)) / ${encoder_closed_pos};
+ return (100.0 - percent) / 100.0;
+ stop_action:
+ - tmc2209.disable: motor
+ open_action:
+ - lambda: |-
+ int32_t wantpos = id(sensored_home_pos);
+ int32_t posdelta = wantpos - id(encoder)->get_state();
+ int32_t stepdelta = ((posdelta * 200 * ${microsteps}) + 2048) / 4096;
+ ESP_LOGI("cover", "From %f to open (%f to %d) is delta %d, %d steps. Step %d -> %d",
+ id(pd_blinds)->position, id(encoder)->get_state(), wantpos, posdelta, stepdelta,
+ id(motor)->current_position, id(motor)->current_position + stepdelta);
+ id(motor).set_target(id(motor)->current_position + stepdelta);
+ close_action:
+ - lambda: |-
+ int32_t wantpos = id(sensored_home_pos) + ${encoder_closed_pos};
+ int32_t posdelta = wantpos - id(encoder)->get_state();
+ int32_t stepdelta = ((posdelta * 200 * ${microsteps}) + 2048) / 4096;
+ ESP_LOGI("cover", "From %f to closed (%f to %d) is delta %d, %d steps. Step %d -> %d",
+ id(pd_blinds)->position, id(encoder)->get_state(), wantpos, posdelta, stepdelta,
+ id(motor)->current_position, id(motor)->current_position + stepdelta);
+ id(motor).set_target(id(motor)->current_position + stepdelta);
+ position_action:
+ - lambda: |-
+ int32_t wantpos = id(sensored_home_pos) + (1.0 - pos) * ${encoder_closed_pos};
+ int32_t posdelta = wantpos - id(encoder)->get_state();
+ int32_t stepdelta = ((posdelta * 200 * ${microsteps}) + 2048) / 4096;
+ ESP_LOGI("cover", "From %f to %f (%f to %d) is delta %d, %d steps. Step %d -> %d",
+ id(pd_blinds)->position, pos, id(encoder)->get_state(), wantpos, posdelta, stepdelta,
+ id(motor)->current_position, id(motor)->current_position + stepdelta);
+ id(motor).set_target(id(motor)->current_position + stepdelta);