initial commit
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user