I posted this originally on lemmy.world, but someone suggested you might appreciate it here. Turns out as a new user I can’t post the whole thing here, so here’s a link to the original post .
tl;dr it’s a solar and battery powered ESP32 connected to a pair of VCNL4010 proximity sensors inside my mailbox, which lets me know when there’s mail to collect via ESPHome and Home Assistant.
2 Likes
Spiro
May 19, 2024, 3:18pm
2
You should post the code here using </> .
Sure, here it is.
substitutions:
devicename: "smart-mailbox"
sub_ota: "1"
sub_sun: "2"
upd_prox1: "1"
upd_prox2: "2"
upd_adc: "4"
esphome:
name: $devicename
platform: ESP32
board: esp32dev
on_boot:
- priority: -300
then:
- script.execute: check_stuff
wifi:
ssid: !secret wifi_ext_ssid
password: !secret wifi_password
power_save_mode: LIGHT
fast_connect: true
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Smart Mailbox Fallback Hotspot"
password: !secret ota_smart_mailbox
# captive_portal:
# Enable logging
logger:
level: DEBUG
# baud_rate: 0
# level: NONE
# Enable API
# api:
# Enable MQTT
mqtt:
id: mqtt_cli
broker: !secret mqtt_broker
username: !secret mqtt_username
password: !secret mqtt_password
birth_message:
will_message:
ota:
safe_mode: True
password: !secret ota_smart_mailbox
on_end:
then:
- lambda: |-
id(ota_mode) = 0;
- mqtt.publish:
topic: "$devicename/ota"
payload: "OFF"
retain: true
- lambda: |-
id(ota_ready).publish_state(false);
globals:
- id: updates
type: int
restore_value: no
initial_value: '0'
- id: subscriptions
type: int
restore_value: no
initial_value: '0'
- id: ota_mode
type: int
restore_value: no
initial_value: '0'
- id: sun_status
type: std::string
restore_value: no
initial_value: '"unknown"'
- id: naptime
type: int
restore_value: no
initial_value: '300'
deep_sleep:
id: sleepy
i2c:
- id: bus_a
sda: 21
scl: 22
- id: bus_b
sda: 25
scl: 26
external_components:
source: github://dixonte/esphome-vcnl4010
components: [vcnl4010_i2c_sensor]
script:
- id: check_stuff
then:
# Announce device is online
- mqtt.publish:
topic: "$devicename/status"
payload: "online"
retain: true
# Get status from Home Assistant
- lambda: |-
id(ota_ready).publish_state(false);
id(mqtt_cli).subscribe("$devicename/ota", [=](const std::string &topic, const std::string &payload) {
ESP_LOGD("mqtt", "Received OTA status: %s", payload.c_str());
auto previous_ota = id(ota_mode);
id(ota_mode) = (payload.compare("ON") == 0);
id(subscriptions) |= $sub_ota;
if (!id(ota_mode) && previous_ota) {
ESP_LOGD("mailbox", "OTA mode aborted, going into immediate microsleep...");
id(ota_ready).publish_state(false);
id(sleepy).set_sleep_duration(1000);
id(sleepy).begin_sleep(true);
}
});
id(mqtt_cli).subscribe("sun/status", [=](const std::string &topic, const std::string &payload) {
ESP_LOGD("mqtt", "Received sun status: %s", payload.c_str());
id(sun_status) = payload;
id(subscriptions) |= $sub_sun;
});
# Wait for status from Home Assistant
- wait_until:
lambda: |-
return (id(subscriptions) == ($sub_ota | $sub_sun));
# How long should we sleep after updating?
- lambda: |-
if (id(sun_status) == "above_horizon") {
id(naptime) = 60; //600;
} else if (id(sun_status) == "below_horizon") {
id(naptime) = 3600;
} else {
id(naptime) = 60;
}
# Update sensors
- lambda: |-
ESP_LOGD("mailbox", "Updating sensors");
id(sensor1).update();
id(sensor2).update();
id(lastUpdate).update();
id(bat_voltage_raw).set_update_interval(1300);
id(bat_voltage_raw).start_poller();
# Wait for updates
- wait_until:
lambda: |-
return (id(updates) == ($upd_prox1 | $upd_prox2 | $upd_adc));
# Do we have mail?
- lambda: |-
bool gotMail;
if ((id(prox1).state > 30000) || (id(prox2).state > 30000)) {
gotMail = true;
} else {
gotMail = false;
}
ESP_LOGD("mailbox", "Publishing got mail state: %d", gotMail);
id(got_mail).publish_state(gotMail);
- logger.log:
format: "Sleeping for %d seconds | OTA: %d"
args: [ 'id(naptime)', 'id(ota_mode)' ]
# Shut down indefinitely if battery low
- if:
condition:
lambda: 'return (id(bat_voltage).state < 3.1);'
then:
- mqtt.publish:
topic: "$devicename/status"
payload: "offline"
retain: true
- deep_sleep.enter:
id: sleepy
sleep_duration: 3650d
# Deep sleep to save battery, if not OTA mode
- if:
condition:
lambda: 'return id(ota_mode) == 0;'
then:
- deep_sleep.enter:
id: sleepy
sleep_duration: !lambda |-
return id(naptime) * 1000;
else:
- lambda: |-
ESP_LOGD("mqtt", "OTA ready.");
id(ota_ready).publish_state(true);
button:
- platform: template
name: "Manual update"
on_press:
- logger.log: Checking mail on demand
- component.update: sensor1
- component.update: sensor2
sensor:
- platform: vcnl4010_i2c_sensor
id: sensor1
i2c_id: bus_a
update_interval: never
proximity:
name: Proximity sensor 1
id: prox1
on_value:
lambda: |-
id(updates) |= $upd_prox1;
# ambient:
# name: Light sensor 1
- platform: vcnl4010_i2c_sensor
id: sensor2
i2c_id: bus_b
update_interval: never
proximity:
name: Proximity sensor 2
id: prox2
on_value:
lambda: |-
id(updates) |= $upd_prox2;
# ambient:
# name: Light sensor 2
- platform: adc
pin: 33
id: "bat_voltage_raw"
name: "Battery voltage (raw)"
accuracy_decimals: 3
update_interval: never
attenuation: auto
# filters:
# - sliding_window_moving_average:
# window_size: 30
# send_every: 5
# send_first_at: 1
filters:
- median:
window_size: 10
send_every: 10
send_first_at: 10
on_value:
lambda: |-
id(bat_voltage_raw).stop_poller();
id(bat_voltage).update();
id(bat_percentage).update();
id(updates) |= $upd_adc;
- platform: template
id: bat_voltage
name: "Battery voltage"
unit_of_measurement: 'V'
icon: "mdi:battery"
accuracy_decimals: 3
update_interval: never
lambda: |-
return ((360.0f * id(bat_voltage_raw).state) + (1000.0f * id(bat_voltage_raw).state)) / 1000.0f;
- platform: template
id: bat_percentage
name: "Battery level"
unit_of_measurement: '%'
icon: "mdi:battery-check"
update_interval: never
lambda: |-
return id(bat_voltage).state;
filters:
- calibrate_linear:
- 3.1 -> 0
- 4.2 -> 100
- platform: template
id: lastUpdate
name: "Last update"
icon: "mdi:timer-sand-complete"
state_class: "measurement"
accuracy_decimals: 1
update_interval: never
lambda: |-
return millis();
binary_sensor:
- platform: template
id: got_mail
name: Got mail?
icon: mdi:mailbox
- platform: template
id: ota_ready
name: OTA ready?
icon: mdi:download
Cool project! working on smartifying my mailbox as well and will leverage some of your code. Thanks!
Curious what you did for powering the sensors? Did you power off a pin of the esp (or a pin with a mosfet) so they aren’t sipping power while the esp sleeps? Out just powering those all the time? I’ve also seen some designs using an nfet and pfet on the adc so the adc resistor bridge isn’t sipping power – will probably implement that as well.
I may not be able to do the solar on mine so increasing battery life is one of my goals.
Sorry to say, I didn’t think to do anything like that. That might explain why it loses power over time faster than I’d expect, though. If I ever do a revision I’ll give that a try.