Edit esp-astroclockstepper1.yaml

This commit is contained in:
ESPHome Device Builder
2026-06-28 16:39:19 +12:00
parent 85ee5e96d0
commit 7636b7fd12
+286 -76
View File
@@ -6,6 +6,7 @@
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-astroclockstepper.yaml
#:########################################################################################:#
# VERSIONS:
# V1.2 2026-06-22 Added DHCP IPv4 address flash sequence on onboard LED after WiFi connects
# V1.1 2026-06-22 Added onboard D1 Mini LED flash on each motor step
# V1.0 2026-06-22 Initial Version
#:########################################################################################:#
@@ -48,19 +49,20 @@
# - Manual movement only applies while a physical button or web switch is held/on.
# - Position counter wraps back to zero after one full rotation.
# - Onboard D1 Mini LED flashes briefly every time a motor step is issued.
# - After WiFi connects, the onboard LED flashes the DHCP IPv4 address.
# - IPv4 flash sequence is decimal digit based.
# - A digit of 0 is represented as 10 flashes.
#:########################################################################################:#
# OFFLINE NOTES:
# a) Home Assistant OFFLINE, but Network and MQTT ONLINE
# - Device remains controllable via local web portal and MQTT commands
# a) Home Assistant OFFLINE, but Network ONLINE
# - Device remains controllable via local web portal
# - HA entities unavailable until HA returns
# b) MQTT OFFLINE, but WiFi/Network and HA API ONLINE
# - Device remains controllable from Home Assistant via API
# - Local web portal remains available
# c) Entire WiFi/Network OFFLINE
# - No HA API, MQTT, or web control available
# b) WiFi/Network OFFLINE
# - No HA API or web control available
# - Physical buttons continue to work
# - Normal motor timing continues from device uptime
# d) Device power loss / reboot
# - Fallback AP / captive portal should become available from network_common_dhcp.yaml
# c) Device power loss / reboot
# - V1 does not restore exact astronomical position after reboot
# - Re-align manually using the web or physical button controls
#:########################################################################################:#
@@ -79,23 +81,11 @@ substitutions:
# Project Naming
project_name: "zorruno.Astronomical Clock Stepper"
project_version: "v1.1"
project_version: "v1.2"
# Passwords & Secrets (Unfortunately, you cannot use substitutions inside secret names)
api_key: !secret esp-api_key
ota_pass: !secret esp-ota_pass
static_ip_address: !secret esp-astroclockstepper1_ip
# If we are changing IP addresses, you must update the current IP address here, otherwise it remains
# Don't forget to switch it back when changed.
current_ip_address: ${static_ip_address}
# MQTT Topics
#mqtt_command_main_topic: !secret mqtt_command_main_topic
#mqtt_status_main_topic: !secret mqtt_status_main_topic
#mqtt_device_name: "astro-clock-stepper1"
#mqtt_command_topic: "${mqtt_command_main_topic}/${mqtt_device_name}"
#mqtt_status_topic: "${mqtt_status_main_topic}/${mqtt_device_name}"
# Device Settings
log_level: "INFO"
@@ -122,6 +112,19 @@ substitutions:
onboard_step_led_pin: GPIO2
step_led_flash_ms: "25"
# DHCP IP Address LED Flash Settings
ip_flash_enabled: "true"
ip_flash_repeat_count: "5"
ip_flash_zero_digit_blinks: "10"
ip_flash_loop_interval: "25ms"
# Suggested human-countable timing
ip_flash_on_ms: "200"
ip_flash_between_flash_gap_ms: "450"
ip_flash_digit_gap_ms: "1500"
ip_flash_octet_gap_ms: "3500"
ip_flash_repeat_gap_ms: "8000"
# Stepper Output Pins
stepper_in1_pin: GPIO14
stepper_in2_pin: GPIO12
@@ -146,6 +149,7 @@ substitutions:
physical_slow_reverse_name: "Physical Slow Reverse"
zero_position_name: "Zero Position Counter"
replay_ip_flash_name: "Replay IP Address Flash"
#:########################################################################################:#
@@ -153,15 +157,12 @@ substitutions:
# https://esphome.io/components/packages.html
#:########################################################################################:#
packages:
#### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode ####
#### WIFI, Network (DHCP/IPV6 etc), Fallback AP, Safemode ####
common_wifi: !include
#file: common/network_common.yaml
file: common/network_common_dhcp.yaml
vars:
local_device_name: "${device_name}"
local_static_ip_address: "${static_ip_address}"
local_ota_pass: "${ota_pass}"
local_current_ip_address: "${current_ip_address}"
#### HOME ASSISTANT API (choose encryption or no encryption options) ####
common_api: !include
@@ -213,6 +214,14 @@ esphome:
args:
- '"${rotation_days}"'
# Wait for WiFi/DHCP, then flash the assigned IPv4 address.
# This does not stop the main stepper scheduler from running.
- wait_until:
condition:
wifi.connected:
- delay: 3s
- script.execute: start_ip_flash_script
#:########################################################################################:#
# ESP PLATFORM AND FRAMEWORK:
@@ -240,7 +249,7 @@ logger:
#:########################################################################################:#
# GLOBALS:
# Internal position, timing, mode, and held-button state
# Internal position, timing, mode, held-button state, and IP flash state
# https://esphome.io/components/globals.html
#:########################################################################################:#
globals:
@@ -336,6 +345,62 @@ globals:
restore_value: no
initial_value: "false"
# IP Address Flash State
- id: ip_flash_active
type: bool
restore_value: no
initial_value: "false"
- id: ip_flash_led_is_on
type: bool
restore_value: no
initial_value: "false"
- id: ip_flash_next_action_ms
type: uint32_t
restore_value: no
initial_value: "0"
- id: ip_flash_repeats_completed
type: int
restore_value: no
initial_value: "0"
- id: ip_flash_octet_index
type: int
restore_value: no
initial_value: "0"
- id: ip_flash_digit_index
type: int
restore_value: no
initial_value: "0"
- id: ip_flash_blinks_completed
type: int
restore_value: no
initial_value: "0"
- id: ip_flash_octet_0
type: int
restore_value: no
initial_value: "0"
- id: ip_flash_octet_1
type: int
restore_value: no
initial_value: "0"
- id: ip_flash_octet_2
type: int
restore_value: no
initial_value: "0"
- id: ip_flash_octet_3
type: int
restore_value: no
initial_value: "0"
#:########################################################################################:#
# OUTPUT:
@@ -367,6 +432,63 @@ output:
inverted: true
#:########################################################################################:#
# SCRIPT:
# Utility scripts
# https://esphome.io/components/script.html
#:########################################################################################:#
script:
- id: start_ip_flash_script
mode: restart
then:
- lambda: |-
const bool IP_FLASH_ENABLED = ${ip_flash_enabled};
if (!IP_FLASH_ENABLED) {
ESP_LOGI("ip_flash", "IP flash is disabled.");
return;
}
if (WiFi.status() != WL_CONNECTED) {
ESP_LOGW("ip_flash", "WiFi is not connected. Cannot flash IP address.");
return;
}
IPAddress ip = WiFi.localIP();
if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) {
ESP_LOGW("ip_flash", "Invalid IPv4 address 0.0.0.0. Cannot flash IP address.");
return;
}
id(ip_flash_octet_0) = ip[0];
id(ip_flash_octet_1) = ip[1];
id(ip_flash_octet_2) = ip[2];
id(ip_flash_octet_3) = ip[3];
id(ip_flash_repeats_completed) = 0;
id(ip_flash_octet_index) = 0;
id(ip_flash_digit_index) = 0;
id(ip_flash_blinks_completed) = 0;
id(ip_flash_led_is_on) = false;
id(ip_flash_next_action_ms) = millis();
id(ip_flash_active) = true;
// Keep the IP flash sequence visually clean.
id(step_led_is_on) = false;
id(onboard_step_led).turn_off();
ESP_LOGI(
"ip_flash",
"Flashing IPv4 address %u.%u.%u.%u on onboard LED. Repeats: %d",
ip[0],
ip[1],
ip[2],
ip[3],
${ip_flash_repeat_count}
);
#:########################################################################################:#
# SWITCH:
# Web held-button controls
@@ -553,6 +675,13 @@ button:
level: INFO
format: "Position counter zeroed."
- platform: template
name: "${replay_ip_flash_name}"
id: replay_ip_flash_button
icon: mdi:ip-network
on_press:
- script.execute: start_ip_flash_script
#:########################################################################################:#
# SENSOR:
@@ -647,7 +776,7 @@ text_sensor:
#:########################################################################################:#
# INTERVAL:
# Stepper scheduler
# Stepper scheduler and IP flash scheduler
# https://esphome.io/components/interval.html
#:########################################################################################:#
interval:
@@ -678,9 +807,11 @@ interval:
};
auto flash_step_led = [&](uint32_t now_ms) {
id(onboard_step_led).turn_on();
id(step_led_started_ms) = now_ms;
id(step_led_is_on) = true;
if (!id(ip_flash_active)) {
id(onboard_step_led).turn_on();
id(step_led_started_ms) = now_ms;
id(step_led_is_on) = true;
}
};
auto set_coils_for_phase = [&](int phase) {
@@ -811,6 +942,7 @@ interval:
}
if (
!id(ip_flash_active) &&
id(step_led_is_on) &&
((uint32_t) (now_ms - id(step_led_started_ms)) >= STEP_LED_FLASH_MS)
) {
@@ -818,50 +950,128 @@ interval:
id(step_led_is_on) = false;
}
- interval: ${ip_flash_loop_interval}
then:
- lambda: |-
if (!id(ip_flash_active)) {
return;
}
#:########################################################################################:#
# MQTT:
# Device-specific MQTT command triggers, in addition to common MQTT config
# https://esphome.io/components/mqtt.html
#:########################################################################################:#
#mqtt:
# on_message:
# - topic: "${mqtt_command_topic}/fast_forward/set"
# payload: "ON"
# then:
# - switch.turn_on: web_fast_forward_switch
#
# - topic: "${mqtt_command_topic}/fast_forward/set"
# payload: "OFF"
# then:
# - switch.turn_off: web_fast_forward_switch
#
# - topic: "${mqtt_command_topic}/slow_forward/set"
# payload: "ON"
# then:
# - switch.turn_on: web_slow_forward_switch
#
# - topic: "${mqtt_command_topic}/slow_forward/set"
# payload: "OFF"
# then:
# - switch.turn_off: web_slow_forward_switch
#
# - topic: "${mqtt_command_topic}/fast_reverse/set"
# payload: "ON"
# then:
# - switch.turn_on: web_fast_reverse_switch
#
# - topic: "${mqtt_command_topic}/fast_reverse/set"
# payload: "OFF"
# then:
# - switch.turn_off: web_fast_reverse_switch
#
# - topic: "${mqtt_command_topic}/slow_reverse/set"
# payload: "ON"
# then:
# - switch.turn_on: web_slow_reverse_switch
#
# - topic: "${mqtt_command_topic}/slow_reverse/set"
# payload: "OFF"
# then:
# - switch.turn_off: web_slow_reverse_switch
const int IP_FLASH_REPEAT_COUNT = ${ip_flash_repeat_count};
const int IP_FLASH_ZERO_DIGIT_BLINKS = ${ip_flash_zero_digit_blinks};
const uint32_t IP_FLASH_ON_MS = ${ip_flash_on_ms};
const uint32_t IP_FLASH_BETWEEN_FLASH_GAP_MS = ${ip_flash_between_flash_gap_ms};
const uint32_t IP_FLASH_DIGIT_GAP_MS = ${ip_flash_digit_gap_ms};
const uint32_t IP_FLASH_OCTET_GAP_MS = ${ip_flash_octet_gap_ms};
const uint32_t IP_FLASH_REPEAT_GAP_MS = ${ip_flash_repeat_gap_ms};
const uint32_t now_ms = millis();
if ((int32_t) (now_ms - id(ip_flash_next_action_ms)) < 0) {
return;
}
auto get_octet = [&](int index) -> int {
switch (index) {
case 0:
return id(ip_flash_octet_0);
case 1:
return id(ip_flash_octet_1);
case 2:
return id(ip_flash_octet_2);
case 3:
default:
return id(ip_flash_octet_3);
}
};
auto get_digit_count = [&](int value) -> int {
if (value >= 100) {
return 3;
}
if (value >= 10) {
return 2;
}
return 1;
};
auto get_digit = [&](int value, int digit_index) -> int {
const int digit_count = get_digit_count(value);
if (digit_count == 3) {
if (digit_index == 0) {
return value / 100;
}
if (digit_index == 1) {
return (value / 10) % 10;
}
return value % 10;
}
if (digit_count == 2) {
if (digit_index == 0) {
return value / 10;
}
return value % 10;
}
return value;
};
const int current_octet = get_octet(id(ip_flash_octet_index));
const int current_digit_count = get_digit_count(current_octet);
const int current_digit = get_digit(current_octet, id(ip_flash_digit_index));
const int target_blinks =
current_digit == 0 ? IP_FLASH_ZERO_DIGIT_BLINKS : current_digit;
if (id(ip_flash_led_is_on)) {
id(onboard_step_led).turn_off();
id(ip_flash_led_is_on) = false;
id(ip_flash_blinks_completed)++;
if (id(ip_flash_blinks_completed) < target_blinks) {
id(ip_flash_next_action_ms) = now_ms + IP_FLASH_BETWEEN_FLASH_GAP_MS;
return;
}
id(ip_flash_blinks_completed) = 0;
id(ip_flash_digit_index)++;
if (id(ip_flash_digit_index) < current_digit_count) {
id(ip_flash_next_action_ms) = now_ms + IP_FLASH_DIGIT_GAP_MS;
return;
}
id(ip_flash_digit_index) = 0;
id(ip_flash_octet_index)++;
if (id(ip_flash_octet_index) < 4) {
id(ip_flash_next_action_ms) = now_ms + IP_FLASH_OCTET_GAP_MS;
return;
}
id(ip_flash_octet_index) = 0;
id(ip_flash_repeats_completed)++;
if (id(ip_flash_repeats_completed) >= IP_FLASH_REPEAT_COUNT) {
id(ip_flash_active) = false;
id(ip_flash_led_is_on) = false;
id(onboard_step_led).turn_off();
ESP_LOGI("ip_flash", "Finished flashing IPv4 address.");
return;
}
id(ip_flash_next_action_ms) = now_ms + IP_FLASH_REPEAT_GAP_MS;
return;
}
id(onboard_step_led).turn_on();
id(ip_flash_led_is_on) = true;
id(ip_flash_next_action_ms) = now_ms + IP_FLASH_ON_MS