Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ jobs:
wifi_ssid: "CiDummyNet"
wifi_password: "ci-dummy-password"
ota_password: "ci-dummy-ota-password"
mqtt_host: "192.0.2.1"
mqtt_user: "ci-dummy-user"
mqtt_password: "ci-dummy-password"
EOF

- name: Validate ESPHome config
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
wifi_ssid: "ReleaseDummyNet"
wifi_password: "release-dummy-password"
ota_password: "release-dummy-ota-password"
mqtt_host: "192.0.2.1"
mqtt_user: "release-dummy-user"
mqtt_password: "release-dummy-password"
EOF

- name: Build ESPHome firmware
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ Release notes for each tagged version are extracted from this file by the

## [Unreleased]

### Changed
- Home Assistant transport switched from the native ESPHome **`api:` to
`mqtt:`** in `fancontrol.yaml`. The encrypted native-API block is now
commented out and the MQTT block (broker auto-discovery, `fancontrol`
topic prefix, online/offline birth & will messages) is enabled. Set
`mqtt_host` (bare IP, **no port** — the port is the separate `port:` key),
`mqtt_user`, and `mqtt_password` in `secrets.yaml`. Revert by swapping the
two blocks back. Verified on hardware: device connects to the broker and
publishes temperature, humidity, fan speed, and diagnostics.
- Fan PWM output moved from **GPIO 25 → GPIO 16** in `fancontrol.yaml` to
match the assembled hardware. GPIO 16 is free on the plain LOLIN D32
(WROOM-32, no PSRAM); only the D32 *Pro* (WROVER) reserves 16/17 for PSRAM.
Pin map updated in `CLAUDE.md`, `PROJECT_PLAN.md`, and
`docs/wiring_diagram.md`.

### Fixed
- `.github/dependabot.yml`: dropped the `pip` ecosystem. v0.2.0 removed
`platformio.ini` and there are no Python manifests left, so Dependabot's
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ HA integration) is declared there.
- **Board:** LOLIN D32 (ESP32-WROOM-32)
- **Sensor:** DHT22 with onboard 10 kΩ pull-up
- **Fan:** Ruck EM 125L EC 02 — PWM input 5–10 V, 1–5 kHz (datasheet)
- **PWM path:** ESP32 GPIO 25 → TXS0108E / 74HCT125 level shifter → MT3608 boost → 9 V PWM line
- **PWM path:** ESP32 GPIO 16 → TXS0108E / 74HCT125 level shifter → MT3608 boost → 9 V PWM line

Pin map, locked in `fancontrol.yaml`:

| GPIO | Role | Notes |
|---|---|---|
| 4 | DHT22 data | module has its own pull-up; firmware treats pin as plain input |
| 25 | Fan PWM (LEDC ch0) | default 1 kHz, 10-bit auto |
| 16 | Fan PWM (LEDC ch0) | default 1 kHz, 10-bit auto |
| 5 | Status LED | onboard LOLIN D32 LED, active LOW |
| 0 | Boot button | reserved for factory reset long-press (not yet wired in YAML) |

Expand Down
2 changes: 1 addition & 1 deletion PROJECT_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Everything runtime-visible lives in a single file: [`fancontrol.yaml`](fancontro
## 2. Features

### F1 — Ventilation core
- F1.1 PWM output on GPIO 25. Default 1 kHz (Ruck EM 125L EC 02 datasheet:
- F1.1 PWM output on GPIO 16. Default 1 kHz (Ruck EM 125L EC 02 datasheet:
PWM 5–10 V, 1–5 kHz), runtime-tunable via HA / web UI `number` component
(clamped 1000–5000 Hz, 100 Hz step).
- F1.2 Temperature-driven fan speed using a 5-point piecewise-linear curve
Expand Down
6 changes: 3 additions & 3 deletions docs/wiring_diagram.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
| ESP32 GPIO | Role | Direction | Notes |
|---|---|---|---|
| **4** | `PIN_DHT22` | Input (pulled up on module) | Avoids ADC2 (conflicts with WiFi). |
| **25** | `PIN_FAN_PWM` | LEDC PWM output | Channel `FAN_LEDC_CHANNEL = 0`, default 1 kHz, 10-bit resolution. Drives the level-shifter LV side. |
| **16** | `PIN_FAN_PWM` | LEDC PWM output | Channel `FAN_LEDC_CHANNEL = 0`, default 1 kHz, 10-bit resolution. Drives the level-shifter LV side. |
| **5** | `PIN_STATUS_LED` | Output (active LOW) | Onboard LED. Reserved — not yet driven by firmware. |
| **0** | `PIN_FACTORY_RESET` | Input (boot button) | Hold at boot to trigger factory reset (to be wired into `storage::factoryReset()` as a future hook — currently only web UI calls it). |
| 3V3, GND, VIN (5 V) | Power rails | — | VIN accepts 5 V USB. 3V3 from the onboard regulator feeds the DHT22 module. |

## Rationale

- **GPIO 4 for DHT22**: input-capable, outside the ADC2 block, not a strapping pin. Widely used for DHT sensors in ESP32 examples.
- **GPIO 25 for fan PWM**: LEDC-capable, not a strapping pin, no ADC2 conflict. Next to the DAC pins but we're using LEDC (digital PWM) not DAC.
- **GPIO 16 for fan PWM**: LEDC-capable, not a strapping pin, not an ADC pin. Free on the plain LOLIN D32 (ESP32-WROOM-32, no PSRAM). Note: GPIO 16/17 are reserved for PSRAM on the LOLIN D32 *Pro* (WROVER) — not applicable to this board.
- **Avoid GPIO 12**: strapping pin (MTDI); pulled HIGH during boot can brick flash in some boards.
- **Avoid GPIO 2 / 15**: boot-time strapping; fine for outputs after boot but safer to leave alone.

Expand All @@ -35,7 +35,7 @@
│ LOLIN D32 │
│ │
│ GPIO 4 ──── DHT22 DAT (module has built-in 10 kΩ pull-up to VCC)
│ GPIO 25 ──── LevelShift A1 (LV) ── LevelShift B1 (HV) ── Fan PWM+
│ GPIO 16 ──── LevelShift A1 (LV) ── LevelShift B1 (HV) ── Fan PWM+
│ 3V3 ──── DHT22 VCC, LevelShift LV
│ GND ──── DHT22 GND, LevelShift GND (both sides), Fan GND,
│ MT3608 input GND, 5 V PSU GND
Expand Down
49 changes: 25 additions & 24 deletions fancontrol.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@ logger:
level: INFO
baud_rate: 115200

# Prefer ESPHome native API for Home Assistant (encrypted, fast). MQTT
# left optional below for users who want to keep the v0.1.x topic shape.
api:
encryption:
key: !secret api_encryption_key
# Native API disabled for this deployment — Home Assistant integration is
# via MQTT only (see the mqtt: block below). Re-enable by uncommenting and
# commenting out mqtt: to go back to the encrypted native-API transport.
# api:
# encryption:
# key: !secret api_encryption_key

ota:
- platform: esphome
Expand All @@ -68,24 +69,24 @@ captive_portal:
mdns:
# Advertises the device as fancontrol.local on the LAN.

# Optional MQTT. Uncomment + fill secrets.yaml to publish to an external
# broker in addition to (or instead of) the native API.
# mqtt:
# broker: !secret mqtt_host
# port: 1883
# username: !secret mqtt_user
# password: !secret mqtt_password
# topic_prefix: fancontrol
# discovery: true
# discovery_retain: true
# birth_message:
# topic: fancontrol/status
# payload: online
# retain: true
# will_message:
# topic: fancontrol/status
# payload: offline
# retain: true
# MQTT transport — Home Assistant integration via broker auto-discovery.
# Fill mqtt_host / mqtt_user / mqtt_password in secrets.yaml.
mqtt:
broker: !secret mqtt_host
port: 1883
username: !secret mqtt_user
password: !secret mqtt_password
topic_prefix: fancontrol
discovery: true
discovery_retain: true
birth_message:
topic: fancontrol/status
payload: online
retain: true
will_message:
topic: fancontrol/status
payload: offline
retain: true

web_server:
port: 80
Expand Down Expand Up @@ -252,7 +253,7 @@ number:

output:
- platform: ledc
pin: GPIO25
pin: GPIO16
id: fan_pwm_output
frequency: 1000Hz
channel: 0
Expand Down