I have a solar node, where on cloudy days the battery is not charging enough. By default, this node sends its sensor data to home assistant and then sleeps for 5 minutes. When the battery voltage drops below 3.7V, the sleep time will linearly increase with the drop in voltage. At 3.0V the sleep time is 1 hour, and even more when the battery drops lower.
I am reading the battery voltage with an INA219 (which gives current and power consumption as well), but you can use any method to read the battery voltage. For example, using a voltage divider with resistors and then reading the voltage on one of the analog pins of the ESP.
In home assistant, I created a binary sensor âdeep_sleep_activatedâ. By default the node is not using deep sleep, this prevents the node going to sleep when the sensor data is not yet read from home assistant. In my case, this works better than the other way around (a âprevent_deep_sleepâ sensor, like most examples show). I am using the esphome API, not MQTT.
When the node is not in deep sleep, it will âdo stuffâ every minute and check the deep sleep time, and if it needs to be activated. Once deep sleep is activated the default 5-minute sleep interval will start (if the battery is fully charged).
Below is the code I created. It is not plug&play. I copied the relevant parts. Hopefully, it is useful for you, and if you have suggestions for improvement add them to the comments.
In the graph below the voltage (hour average) of the battery. The arrow shows the point where the increased sleep time is activated. The day before, the sleep time was always 5 minutes. You see the voltage drop gets less significant over time. (In my case my sensor is still consuming power when the esp in deep sleep)
esphome:
name: 'Solarnode'
# set CPU speed to 80Mhz (minimum needed for wifi) to save energy
platformio_options:
board_build.f_cpu: 80000000L
on_boot:
# wait until everything is initialized
priority: -100.00
then:
- script.execute: do_stuff_and_sleep_if_sleepy
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# level: VERBOSE
# Enable Home Assistant API
api:
encryption:
key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ota:
password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# connect to the first wifi router found to save some energy
fast_connect: true
# Set a static IP address to save some energy
manual_ip:
static_ip: x.x.x.x
gateway: x.x.x.x
subnet: x.x.x.x
use_address: "x.x.x.x"
# for INA219
i2c:
sda: GPIO21
scl: GPIO22
id: i2c_bus_a
time:
- platform: homeassistant
id: local_time
globals:
- id: dynamic_sleep_duration
type: int
restore_value: yes
# default sleep time
initial_value: '300'
deep_sleep:
# default sleep time
sleep_duration: 5min
id: deep_sleep_1
# if not in sleep, get data every minute
interval:
- interval: 1min
then:
- script.execute: do_stuff_and_sleep_if_sleepy
binary_sensor:
- platform: status
name: "Solarnode Status"
- platform: homeassistant
id: deep_sleep_activated
entity_id: input_boolean.deep_sleep_activated
sensor:
- platform: template
name: "Solarnode Sleep Time"
id: solarnode_sleep_time
unit_of_measurement: "s"
icon: mdi:sleep
accuracy_decimals: 0
lambda: |-
return id(dynamic_sleep_duration);
- platform: ina219
address: 0x40
shunt_resistance: 0.1 ohm
bus_voltage:
name: "Solarnode Battery Bus Voltage"
id: solarnode_battery_voltage
on_value:
then:
- script.execute: calculate_sleep_time
max_voltage: 26.0V
max_current: 3.2A
update_interval: 15s
# other sensors
script:
- id: do_stuff_and_sleep_if_sleepy
then:
# Do stuff here to get your sensor data.
# For example a delay of 30s to allow esphome to send data to home assistant
- delay: 30s
- if:
condition:
- binary_sensor.is_on: deep_sleep_activated
then:
- logger.log:
format: "Entering deep sleep for %d seconds..."
args: [ 'id(dynamic_sleep_duration)' ]
- lambda: |-
id(deep_sleep_1).set_sleep_duration(id(dynamic_sleep_duration)*1000);
- deep_sleep.enter: deep_sleep_1
- id: calculate_sleep_time
then:
- lambda: |-
float VMAX = 3.7;
float VMIN = 3.0;
double MINIMUM_SLEEPTIME = 300;
double SLEEPTIME_VMAX = 300;
double SLEEPTIME_VMIN = 3600;
float battery_voltage = id(solarnode_battery_voltage).state;
id(dynamic_sleep_duration) = max(MINIMUM_SLEEPTIME, ceil(SLEEPTIME_VMIN - (SLEEPTIME_VMIN-SLEEPTIME_VMAX)*(battery_voltage-VMIN)/(VMAX-VMIN)));
id(solarnode_sleep_time).publish_state(id(dynamic_sleep_duration));
ESP_LOGD("main", "Sleeptime adjusted to: %d seconds (%.1f volt)", id(dynamic_sleep_duration), battery_voltage);