I’ve finished my testing (probably) and I have reliable results (also probably).
Note that error count resets to 0 when the ESP boots, and increments for each connection that fails. If error count stays at 0 the entire time, then WIFI, MQTT and API all connected. If error count returns a 1, then one of the 3 failed at time out, and if it returns a 2, then 2 of the 3 failed at time out.
Results
MQTT consistently connects 100ms-300ms behind WIFI.
However, API takes significantly longer and sits around the 57s mark.
It’s also possible that my test isn’t very good and I’m not giving API a fair chance.
I hope that one day I will be able to remove MQTT, but for now, that’s what I’m going to use for deep sleep applications.
Test Settings
- Timeout: 90s
- Sleep Time: 10s
Versions
- Home Assistant Core 2022.7.5
- Home Asssistant OS 8.2
- ESPHome 2022.1.3
Notes for further testing
I’m curious to see if changing sleep time has an effect on connection time. When I had timeout set to 30s, half the time API would connect at around 18s, and the other half of the time, API would fail to connect and this test would time out.
In the test with a 90s time out, the error count stays at 0. During the earlier tests with the 30s time out, API often failed to connect and error count would change to 1.
A connection time of 0ms is not possible if WIFI connects at roughly 4s. If MQTT or API have a connection time of 0ms, they timed out. I just couldn’t figure out how to publish an empty data point and break up the line in the plot.
My code
substitutions:
# Device Properties
devicename: dev-connection-time
friendly_name: Dev-Connection-Time
device_topic: ConnectionTime
device_sleephelper_topic: admin/esphome_disable_deep_sleep
# Logger Properties
log_tag: yaml
# Sleep Properties
sleep_time: 10s #1min
#run_time: 1min #run_duration has been replaced by a script.
globals:
# WIFI connection time and state
- id: wifi_last_state
type: bool
restore_value: no
initial_value: 'false'
- id: wifi_time
type: unsigned long
restore_value: no
initial_value: '0'
# MQTT connection time and state
- id: mqtt_last_state
type: bool
restore_value: no
initial_value: 'false'
- id: mqtt_time
type: unsigned long
restore_value: no
initial_value: '0'
# API connection time and state
- id: api_last_state
type: bool
restore_value: no
initial_value: 'false'
- id: api_time
type: unsigned long
restore_value: no
initial_value: '0'
# Error counter
- id: timeout_count_value
type: int
restore_value: no
initial_value: '0'
# Time out in ms
- id: timeout
type: int
restore_value: no
initial_value: '90000'
# reason for shutting down.
- id: shutdown_reason
type: std::string
restore_value: no
initial_value: '"run_duration expired"'
esphome:
name: $devicename
on_boot:
then:
- logger.log:
format: "Booted"
level: ERROR
on_loop:
- if:
condition:
# If timeout hasn't been reached.
- lambda: "return millis() < id(timeout);"
then:
# Check and log the time each connection is made.
- lambda: |-
unsigned long t = millis();
if (id(wifi_client).is_connected() && !id(wifi_last_state)) {
id(wifi_time) = t;
id(wifi_last_state) = true;
ESP_LOGE("${log_tag}", "WIFI connection time of %lu ms recorded", t);
}
if (id(mqtt_client).is_connected() && !id(mqtt_last_state)) {
id(mqtt_time) = t;
id(mqtt_last_state) = true;
ESP_LOGE("${log_tag}", "MQTT connection time of %lu ms recorded", t);
}
if (id(api_client).is_connected() && !id(api_last_state)) {
id(api_time) = t;
id(api_last_state) = true;
ESP_LOGE("${log_tag}", "API connection time of %lu ms recorded", t);
}
# If all connections are successful before timing out, publish data and enter deep sleep.
- lambda: |-
if (id(wifi_last_state) && id(mqtt_last_state) && id(api_last_state)) {
id(shutdown_reason) = "All Connections Successful";
id(start_deep_sleep).execute();
}
else:
# If the timeout has been reached, increment the error counter by 1 for each failed connection.
- lambda: |-
if (id(wifi_time) == 0) {
id(timeout_count_value)++;
ESP_LOGE("${log_tag}", "WIFI failed to connect");
}
if (id(mqtt_time) == 0) {
id(timeout_count_value)++;
ESP_LOGE("${log_tag}", "MQTT failed to connect");
}
if (id(api_time) == 0) {
id(timeout_count_value)++;
ESP_LOGE("${log_tag}", "API failed to connect");
}
id(shutdown_reason) = "Timeout";
# Publish data and enter deep sleep.
- script.execute: start_deep_sleep
on_shutdown:
- lambda: |-
ESP_LOGE("${log_tag}", "Entering Deep Sleep at %lu ms, Reason: %s", millis(), id(shutdown_reason).c_str());
esp8266:
board: d1_mini
# Enable logging
logger:
level: INFO #WARN
# Publish logs to MQTT
on_message:
level: WARN
then:
- mqtt.publish:
topic: $device_topic/logger
payload: !lambda |-
return "Level: " + to_string(level) + " ||| Tag: " + tag + " ||| Message: " + message;
# Enable Over-The-Air flashing
ota:
#safe_mode: True
password: !secret ota_password
on_end:
- logger.log:
format: "OTA Update"
level: ERROR
deep_sleep:
id: deep_sleep_control
sleep_duration: $sleep_time
#run_duration: $run_time
wifi:
id: wifi_client
power_save_mode: LIGHT #There are other options. Just checking if this helps with self heating.
fast_connect: true # Skip wifi scan to save time.
ssid: !secret wifi_ssid
password: !secret wifi_password
# Set a static IP address.
manual_ip:
static_ip: 192.168.86.201
gateway: 192.168.86.1
subnet: 255.255.255.0
# If changing the name or the IP address of the device, this provides the old address so that ESPHome knows which device to overwrite.
use_address: "192.168.86.201"
# Enable Home Assistant API
api:
id: api_client
password: !secret api_password
# Enable MQTT
mqtt:
id: mqtt_client
broker: !secret mqtt_broker
port: !secret mqtt_port
username: !secret mqtt_user
password: !secret mqtt_password
discovery: true
discovery_retain: true
# Set empty birth and will messages to avoid showing unavailable during sleep
birth_message:
will_message:
topic_prefix: $device_topic
# It is not ok to enter deep sleep as soon as this device realises the HA helper for OTA mode is set to False.
# I really need to get deep_sleep.allow to work so that it can replace deep_sleep.enter in the on_message: 'off' section.
# Disable for now, cry for help later.
# on_message:
# - topic: $device_sleephelper_topic
# qos: 0 #1
# payload: 'on'
# then:
# - logger.log:
# format: 'MQTT Admin: disable deep sleep'
# level: ERROR
# - deep_sleep.prevent: deep_sleep_control
# - topic: $device_sleephelper_topic
# qos: 0 #1
# payload: 'off'
# then:
# - logger.log:
# format: 'MQTT Admin: entering deep sleep'
# level: ERROR
# - deep_sleep.enter: deep_sleep_control
sensor:
# WIFI Connection Time
- platform: template
name: "$friendly_name WIFI Connection Time"
id: wifi_connection_time
update_interval: never
unit_of_measurement: ms
# MQTT Connection Time
- platform: template
name: "$friendly_name MQTT Connection Time"
id: mqtt_connection_time
update_interval: never
unit_of_measurement: ms
# API Connection Time
- platform: template
name: "$friendly_name API Connection Time"
id: api_connection_time
update_interval: never
unit_of_measurement: ms
# Time Out Error Count
- platform: template
name: "$friendly_name Time Out Error Count"
id: timeout_count
update_interval: never
unit_of_measurement: timeouts
script:
# Script to publish data and enter deep sleep.
- id: start_deep_sleep
then:
- lambda: |-
id(wifi_connection_time).publish_state(id(wifi_time));
id(mqtt_connection_time).publish_state(id(mqtt_time));
id(api_connection_time).publish_state(id(api_time));
id(timeout_count).publish_state(id(timeout_count_value));
delay(3000);
id(deep_sleep_control).begin_sleep();