Edit esp-astroclockstepper1.yaml
This commit is contained in:
@@ -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) {
|
||||
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
|
||||
Reference in New Issue
Block a user