initial commit

This commit is contained in:
root
2026-06-28 14:27:20 -04:00
commit ae0f1f559e
115 changed files with 30411 additions and 0 deletions
+246
View File
@@ -0,0 +1,246 @@
# ============================================================
# Attic Climate Sensor — ESP32-C3 SuperMini (Battery-Powered)
# Board: ESP32-C3 SuperMini (RISC-V, single-core, 160MHz)
# Sensor: AHT20 + BMP280 combo (I2C)
# Battery: 18650 Li-Ion
# Schedule: Wake every 6 hours, report to HA, deep sleep
# OTA: Hold GPIO3 LOW (jumper to GND) to prevent deep sleep
# ============================================================
#
# WIRING:
# AHT20/BMP280 Combo Board:
# SDA → GPIO4
# SCL → GPIO5
# VCC → 3.3V
# GND → GND
#
# Battery Voltage Monitor (voltage divider):
# BAT+ → 100kΩ → GPIO0 (ADC) → 100kΩ → GND
#
# OTA Stay-Awake Jumper:
# GPIO3 → 2-pin header → GND (bridge to stay awake)
#
# NOTES:
# - GPIO8 = onboard blue LED (active LOW / inverted)
# - GPIO9 = BOOT button (avoid for general use)
# - GPIO2, GPIO10 = strapping pins (avoid)
# - All GPIO are 3.3V logic only
# ============================================================
substitutions:
device_name: "attic-climate-sensor"
friendly_name: "Attic Climate Sensor"
# Deep sleep duration: 6 hours = 21600 seconds
sleep_duration: "21600s"
# Max awake time before forced sleep (safety net)
run_duration: "60s"
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
on_boot:
priority: -10
then:
- wait_until:
condition:
api.connected:
timeout: 45s
# Give sensors time to stabilize and publish
- delay: 5s
- if:
condition:
binary_sensor.is_on: ota_stay_awake
then:
- logger.log: "OTA jumper detected — staying awake for maintenance"
- deep_sleep.prevent: deep_sleep_control
- light.turn_on: onboard_led
else:
- logger.log: "Publishing complete — entering deep sleep"
- deep_sleep.enter: deep_sleep_control
esp32:
board: esp32-c3-devkitm-1
variant: esp32c3
framework:
type: arduino
# ── Logging ────────────────────────────────────────────────
logger:
level: DEBUG
# The C3 SuperMini uses native USB — hardware UART is on GPIO20/21
# For production, reduce to save power:
# level: WARN
# ── Network ────────────────────────────────────────────────
wifi:
ssid: !secret wifi_iot_ssid
password: !secret wifi_password
# Static IP dramatically reduces WiFi connect time (~3-5s saved per wake)
manual_ip:
static_ip: !secret attic_sensor_ip
gateway: !secret gateway
subnet: !secret subnet
dns1: !secret dns1
fast_connect: true
power_save_mode: none
ap:
ssid: "${device_name}-fallback"
password: !secret wifi_password
captive_portal:
# ── API & OTA ──────────────────────────────────────────────
api:
encryption:
key: !secret api_encryption_key
ota:
- platform: esphome
password: !secret ota_password
# ── I2C Bus ────────────────────────────────────────────────
# AHT20: 0x38 | BMP280: 0x77 (or 0x76 depending on board)
# Using GPIO4 (SDA) and GPIO5 (SCL) — safe general-purpose pins
i2c:
sda: GPIO4
scl: GPIO5
scan: true
# ── Deep Sleep ─────────────────────────────────────────────
deep_sleep:
id: deep_sleep_control
run_duration: ${run_duration}
sleep_duration: ${sleep_duration}
# ── Status LED ─────────────────────────────────────────────
# Onboard blue LED on GPIO8 (active LOW / inverted)
output:
- platform: gpio
pin:
number: GPIO8
inverted: true
id: blue_led_output
light:
- platform: binary
name: "${friendly_name} Status LED"
output: blue_led_output
id: onboard_led
entity_category: diagnostic
# ── Binary Sensors ─────────────────────────────────────────
binary_sensor:
# OTA Stay-Awake Jumper
# Wire a 2-pin header from GPIO3 → GND
# Bridge jumper = stay awake for OTA | Remove = normal deep sleep
- platform: gpio
pin:
number: GPIO3
mode:
input: true
pullup: true
inverted: true # LOW (jumper bridged) = ON = stay awake
name: "${friendly_name} OTA Stay Awake"
id: ota_stay_awake
entity_category: diagnostic
on_press:
- logger.log: "OTA jumper enabled — preventing deep sleep"
- deep_sleep.prevent: deep_sleep_control
- light.turn_on: onboard_led
on_release:
- logger.log: "OTA jumper removed — allowing deep sleep"
- deep_sleep.allow: deep_sleep_control
- light.turn_off: onboard_led
# ── Sensors ────────────────────────────────────────────────
sensor:
# ── AHT20: Temperature & Humidity ──
- platform: aht10
variant: AHT20
temperature:
name: "${friendly_name} Temperature"
unit_of_measurement: "°F"
accuracy_decimals: 1
filters:
- lambda: return x * 9.0f / 5.0f + 32.0f;
- sliding_window_moving_average:
window_size: 3
send_every: 1
humidity:
name: "${friendly_name} Humidity"
accuracy_decimals: 1
filters:
- sliding_window_moving_average:
window_size: 3
send_every: 1
update_interval: 5s
# ── BMP280: Barometric Pressure (+ backup temp) ──
- platform: bmp280_i2c
address: 0x77 # Change to 0x76 if your board uses that
temperature:
name: "${friendly_name} BMP280 Temperature"
unit_of_measurement: "°F"
accuracy_decimals: 1
filters:
- lambda: return x * 9.0f / 5.0f + 32.0f;
disabled_by_default: true
pressure:
name: "${friendly_name} Barometric Pressure"
accuracy_decimals: 1
unit_of_measurement: "inHg"
filters:
- multiply: 0.0295299831
update_interval: 5s
# ── Battery Voltage via ADC ──
# Voltage divider: 100kΩ + 100kΩ from BAT+ to GND, midpoint → GPIO0
# GPIO0 is ADC1_CH0 on the C3 — safe for ADC use
# 2:1 divider ratio — multiply by 2 to get actual battery voltage
- platform: adc
pin: GPIO0
name: "${friendly_name} Battery Voltage"
id: battery_voltage
accuracy_decimals: 2
update_interval: 5s
attenuation: 11db
filters:
- multiply: 2.0
# ── Battery Percentage (estimated) ──
# 18650 range: ~3.0V (empty) to ~4.2V (full)
- platform: template
name: "${friendly_name} Battery Percentage"
unit_of_measurement: "%"
device_class: battery
accuracy_decimals: 0
update_interval: 5s
lambda: |-
float voltage = id(battery_voltage).state;
if (voltage >= 4.2) return 100.0;
if (voltage <= 3.0) return 0.0;
return (voltage - 3.0) / (4.2 - 3.0) * 100.0;
# ── WiFi Signal ──
- platform: wifi_signal
name: "${friendly_name} WiFi Signal"
update_interval: 10s
entity_category: diagnostic
# ── Text Sensors ───────────────────────────────────────────
text_sensor:
- platform: wifi_info
ip_address:
name: "${friendly_name} IP Address"
entity_category: diagnostic
# ── Switches ───────────────────────────────────────────────
switch:
# Manual deep sleep trigger from HA (for testing)
- platform: template
name: "${friendly_name} Force Sleep"
icon: "mdi:sleep"
entity_category: config
turn_on_action:
- deep_sleep.enter: deep_sleep_control