feat: finalize documentation and validation (Task 7)

- Rewrite project CLAUDE.md to match actual C/Unity implementation and modules
- Add docs/hardware_connection_diagram.md (ESP32-C6 <-> Ballu AC wiring)
- Add docs/home_assistant_integration.md (HA usage examples)
- Update Readme.md doc index and fix unclosed code fence
- Validate docs against implementation (build + all tests pass)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 19:31:38 +03:00
parent b3e09d991d
commit ff22483f8b
5 changed files with 355 additions and 48 deletions

127
Claude.md
View File

@@ -3,36 +3,61 @@
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Common Development Commands ## Common Development Commands
1. **Build**: `make build` - Compiles firmware for ESP32-C6 The project is plain C compiled with `gcc` and tested with the vendored Unity
2. **Upload**: `make upload` - Flashes firmware to device (requires USB connection) framework (see `Makefile`). There is no ESP-IDF dependency for the host build/test
3. **Test**: `make test` - Runs all unit/integration tests using Vitest flow; the firmware entry point in `src/main.c` is excluded from test binaries via
4. **Single Test**: `make test TEST_NAME="test_module_name"` - Runs specific test the `UNIT_TEST` define.
5. **Debug**: `make debug` - Starts debug mode with serial console
1. **Build (host)**: `make build` - Compiles all `src/*.c` into `build/app`
2. **Run all tests**: `make test` - Builds and runs every Unity test suite
3. **Single Test**: `make test-<module>` - e.g. `make test-app-controller`,
`make test-midea-protocol`, `make test-uart`, `make test-zigbee-zcl`,
`make test-integration-layer`, `make test-status-monitor`
4. **Clean**: `make clean` - Removes `build/` and compiled test binaries
5. **Help**: `make help` - Lists all available targets
> Note: `make upload`/`make debug` (flashing to real ESP32-C6 hardware) are
> hardware steps performed with the ESP-IDF toolchain and are not part of the
> host test flow.
## Code Architecture ## Code Architecture
The system implements a layered architecture for AC controller control: The system implements a layered architecture, top to bottom. Each layer is a
`src/<name>.c` + `<name>.h` pair with a matching `test/<name>_test.c` suite:
1. **Zigbee Interface Module** 1. **Application Controller** (`app_controller.c/h`, entry: `main.c`)
- Receives and decodes Zigbee messages from Home Assistant or similar - Owns every subsystem and wires the two end-to-end data flows.
- Translates commands to UART format - Command path: HA → Zigbee → Integration → UART → AC.
- Key files: `zigbee-handler.c`, `zigbee-decoder.h` - Feedback path: AC → UART → Status → Integration → Zigbee → HA.
- Provides reset/recovery, health check, and the firmware service loop.
2. **Command Processing Core** 2. **Integration Layer** (`integration_layer.c/h`)
- Implements MideaUART protocol (based on cloned MideaUART repository) - Bridges Zigbee ZCL and MideaUART: maps `system_mode` ↔ MideaUART modes,
- Parses UART commands and converts to AC control signals converts temperatures, enforces 50ms command spacing / rate limiting.
- Key files: `uart-controller.c`, `midea-parser.h`
3. **Hardware Abstraction Layer** 3. **Zigbee ZCL Cluster** (`zigbee_zcl.c/h`)
- Manages ESP32-C6 GPIO and UART operations - ZCL Thermostat cluster server: `local_temperature`, `system_mode`, and
- Handles precise timing for AC communication protocol weekly schedule command handling.
- Key files: `hardware-abstraction.c`
4. **Status Monitor** (`status_monitor.c/h`)
- Periodic AC status polling, ZCL attribute mapping, fault detection
(`error_code` / `alarm_mask`), and the communication watchdog.
5. **MideaUART Protocol** (`midea_protocol.c/h`)
- `midea_control_t` / `midea_status_t` structures and encode/decode of AC
frames; timing helpers (50ms command spacing).
6. **UART Driver** (`uart_driver.c/h`)
- Hardware abstraction for ESP32-C6 UART: init at 9600 baud 8N1,
send/receive with timeout handling.
## Development Notes ## Development Notes
- Protocol implementation must strictly follow MideaUART specifications - Protocol implementation must strictly follow MideaUART specifications.
- UART communication requires 50ms baud rate timing - UART runs at 9600 baud 8N1; MideaUART commands are spaced at least 50ms apart
- Testing should focus on edge cases for time-sensitive operations (`command_spacing_ms`, default 50 in `app_controller`/`integration_layer`).
- Dependencies managed via Makefile - do not modify directly - Testing should focus on edge cases for time-sensitive operations.
- MideaUART implementation: https://github.com/dudanov/MideaUART - Tests link every `src/*.c` together, so exactly one `main()` may exist per test
binary — the firmware `main()` in `src/main.c` is guarded by `#ifndef UNIT_TEST`.
- MideaUART reference implementation: https://github.com/dudanov/MideaUART
## Zigbee ZCL HVAC Integration (Thermostat Cluster) ## Zigbee ZCL HVAC Integration (Thermostat Cluster)
@@ -49,14 +74,19 @@ The system implements a layered architecture for AC controller control:
- `EZB_ZCL_HVAC_SYSTEM_TYPE_CONFIGURATION_COOLING_SYSTEM_STAGE = 0x03` - `EZB_ZCL_HVAC_SYSTEM_TYPE_CONFIGURATION_COOLING_SYSTEM_STAGE = 0x03`
### 3. Temperature Control ### 3. Temperature Control
- Temperature range: -1°C to 32767°C (0.01°C resolution) - ZCL `local_temperature` / target temperature are in 0.01°C units (int16_t).
- Convert to MideaUART format: divide by 100 (e.g., 2500 → 25.0°C) - MideaUART `target_temp` is also stored in 0.01°C units (`* 100`), so the
- Command structure: integration layer passes the value through (see
`integration_layer_convert_zcl_temperature`).
- The MideaUART setter takes Celsius as a float:
`midea_control_set_temperature(&control, 25.0f)` for a 25°C target.
- Command structure (actual C API):
```c ```c
control.mode = MODE_COOL; // Zigbee mode: 0x03 (Cooling) midea_control_t control;
control.targetTemp = 25.0f; // 25°C target midea_control_init(&control);
control.modeChange = true; midea_control_set_mode(&control, MODE_COOL); // MideaUART cool mode
ac.control(control); midea_control_set_temperature(&control, 25.0f);// 25°C target
integration_layer_send_midea_command(&layer, &control);
``` ```
### 4. Schedule Management ### 4. Schedule Management
@@ -68,14 +98,37 @@ The system implements a layered architecture for AC controller control:
- Track `ACErrorCode` for system fault detection - Track `ACErrorCode` for system fault detection
- Implement status callbacks for UI updates - Implement status callbacks for UI updates
### 6. Mapping Example ### 6. Mode Mapping (ZCL ↔ MideaUART)
ZCL `system_mode` and MideaUART `midea_mode_t` use different numeric values; the
integration layer translates between them
(`integration_layer_map_zcl_to_midea_mode` / `integration_layer_map_midea_to_zcl_mode`):
| ZCL system_mode | MideaUART midea_mode_t |
| :-- | :-- |
| 0 Off | MODE_OFF (0) |
| 1 Auto | MODE_AUTO (3) |
| 3 Cool | MODE_COOL (1) |
| 4 Heat | MODE_HEAT (2) |
`midea_mode_t` additionally defines MODE_DRY (4), MODE_FAN (5), MODE_SLEEP (6),
MODE_TURBO (7). See `src/midea_protocol.h` for the authoritative enum.
### 7. Mapping Example (feedback path)
```c ```c
// When Zigbee reports mode = 0x03 (Cooling) and target temp = 2500 (25.00°C) // An AC status frame decoded to midea_status_t is mapped to ZCL attributes
Control control; // and pushed into the cluster for Home Assistant to read:
control.mode = MODE_COOL; zcl_thermostat_attrs_t attrs;
control.targetTemp = 25.0f; // Convert from 0.01°C units status_monitor_map_to_zcl(&status, &attrs);
control.modeChange = true; zigbee_zcl_set_local_temperature(attrs.local_temperature);
ac.control(control); zigbee_zcl_set_system_mode(attrs.system_mode);
``` ```
This documentation covers both the core Midea UART protocol for device communication and the Zigbee ZCL thermostat cluster integration for smart home control. This documentation covers both the core Midea UART protocol for device communication and the Zigbee ZCL thermostat cluster integration for smart home control.
## Additional Documentation
- `docs/protocol.md` — MideaUART protocol details
- `docs/zcl_hvac.md` — Zigbee ZCL Thermostat cluster details
- `docs/hardware_connection_diagram.md` — ESP32-C6 ↔ Ballu AC wiring diagram
- `docs/home_assistant_integration.md` — Home Assistant usage examples
- `docs/Hardware integration guide.md` — ESP32-C6 hardware integration (RU)
- `docs/Build system details.md` — Build system and dependencies

View File

@@ -36,10 +36,22 @@ test/
app_controller_test.c - End-to-end integration tests (full command cycles) app_controller_test.c - End-to-end integration tests (full command cycles)
docs/ docs/
protocol.md - MideaUART protocol details protocol.md - MideaUART protocol details
zcl_hvac.md - Zigbee ZCL Thermostat cluster details zcl_hvac.md - Zigbee ZCL Thermostat cluster details
Hardware integration guide.md - Hardware connection information hardware_connection_diagram.md - ESP32-C6 <-> Ballu AC wiring diagram
Build system details.md - Build system and dependencies home_assistant_integration.md - Home Assistant usage examples
Hardware integration guide.md - ESP32-C6 hardware integration (RU)
Build system details.md - Build system and dependencies
```
## Documentation
- [MideaUART protocol](docs/protocol.md)
- [Zigbee ZCL Thermostat cluster](docs/zcl_hvac.md)
- [Hardware connection diagram](docs/hardware_connection_diagram.md)
- [Home Assistant integration guide](docs/home_assistant_integration.md)
- [ESP32-C6 hardware integration guide](docs/Hardware%20integration%20guide.md)
- [Build system details](docs/Build%20system%20details.md)
## Getting Started ## Getting Started

View File

@@ -0,0 +1,98 @@
# Hardware Connection Diagram: ESP32-C6 ↔ Ballu AC
This document describes the physical wiring between the ESP32-C6 controller and
the Ballu air conditioner's MideaUART port, and the logical signal path through
the firmware.
> Pin numbers below match the firmware defaults in `src/app_controller.c`
> (`apply_default_uart_config`): **TX = GPIO16**, **RX = GPIO17**, 9600 baud, 8N1.
> These are configurable via `app_controller_config_t.uart_config`.
## 1. Signal Overview
```
+---------------------+ UART 9600 8N1 +---------------------+
| | (50 ms command spacing) | |
| Home Assistant | | Ballu AC unit |
| (Zigbee coord.) | | (Midea UART bus) |
| | | |
+----------+----------+ +----------+----------+
| |
Zigbee 2.4 GHz (802.15.4) 3.3V TTL UART
| |
+----------v---------------------------------------------------v----------+
| ESP32-C6 controller |
| |
| [Zigbee radio] --> zigbee_zcl --> integration_layer --> midea_protocol |
| | | |
| v v |
| status_monitor <--> uart_driver |
| | |
| GPIO16 (TX) --------+ |
| GPIO17 (RX) --------+ |
+-------------------------------------------------------------------------+
```
## 2. Physical Wiring (ESP32-C6 ↔ AC MideaUART header)
The Ballu/Midea indoor unit exposes a 3-wire (or 4-wire) TTL UART header. The AC
side already provides regulated power on some models; **verify the voltage on the
AC connector before wiring** — the ESP32-C6 GPIO are strictly 3.3V tolerant.
| ESP32-C6 pin | Direction | AC MideaUART pin | Notes |
| :-- | :--: | :-- | :-- |
| GPIO16 (TX) | ESP → AC | RX | Controller transmits commands to the AC |
| GPIO17 (RX) | AC → ESP | TX | Controller receives status frames from the AC |
| GND | common | GND | **Mandatory** common ground reference |
| 3V3 | power | +5V/+12V | Do **not** connect AC +5V/+12V directly to 3V3; use a suitable regulator if powering the ESP32-C6 from the AC |
```mermaid
graph LR
subgraph AC [Ballu AC - Midea UART header]
ARX[RX]
ATX[TX]
AGND[GND]
APWR[+5V / +12V]
end
subgraph ESP [ESP32-C6 controller]
ETX[GPIO16 TX]
ERX[GPIO17 RX]
EGND[GND]
E3V3[3V3]
end
ETX -->|3.3V TTL| ARX
ATX -->|3.3V TTL| ERX
EGND --- AGND
APWR -.->|via 3.3V regulator only| E3V3
```
## 3. Wiring Rules
- **Cross TX/RX**: controller TX (GPIO16) → AC RX; AC TX → controller RX (GPIO17).
- **Common ground is mandatory** — floating grounds cause framing errors.
- **3.3V logic only.** If the AC UART header uses 5V logic, insert a logic-level
shifter between the boards.
- **Power isolation.** Never feed the AC's raw supply rail into the ESP32-C6 3V3
pin; step it down with a regulator (or power the ESP32-C6 over USB during
development).
- **Strapping pins.** GPIO16/GPIO17 are the ESP32-C6 default UART0 pins used for
boot logging/flashing. When wiring the AC to these pins, disconnect the AC
during flashing, or remap the AC UART to spare GPIO via
`uart_config_t.tx_pin` / `rx_pin` to keep the boot console free.
## 4. Firmware Signal Path
- **Command path (HA → AC):**
`zigbee_zcl` receives a ZCL Thermostat command → `integration_layer` maps
`system_mode`/temperature to a `midea_control_t``midea_protocol` encodes the
frame → `uart_driver` transmits it on GPIO16 with ≥50 ms spacing.
- **Feedback path (AC → HA):**
`uart_driver` reads a frame on GPIO17 → `midea_protocol` decodes it to
`midea_status_t``status_monitor` runs fault detection + watchdog and maps to
`zcl_thermostat_attrs_t``zigbee_zcl` publishes `local_temperature` and
`system_mode` for Home Assistant.
See `docs/Hardware integration guide.md` for detailed ESP32-C6 pinout, strapping
pin cautions, and PCB layout guidance.

View File

@@ -0,0 +1,144 @@
# Home Assistant Integration Guide
This controller presents the Ballu AC to Home Assistant as a standard Zigbee
**ZCL Thermostat** device. Home Assistant talks to it through a Zigbee
coordinator running either ZHA or Zigbee2MQTT — no custom integration is needed
beyond the device pairing.
## 1. What the Device Exposes
The firmware implements a ZCL Thermostat cluster server
(`src/zigbee_zcl.c`) with these attributes:
| ZCL attribute | Type | Meaning |
| :-- | :-- | :-- |
| `local_temperature` | int16, 0.01°C | Current indoor temperature read from the AC |
| `system_mode` | uint8 | Operating mode (see mapping below) |
`system_mode` values (as exposed on the Zigbee side):
| Value | Mode |
| :-- | :-- |
| 0 | Off |
| 1 | Auto |
| 3 | Cool |
| 4 | Heat |
Internally these map to MideaUART modes via the integration layer
(`integration_layer_map_zcl_to_midea_mode`); MideaUART additionally supports
Dry, Fan, Sleep, and Turbo (`src/midea_protocol.h`).
## 2. Pairing
1. Put the Zigbee coordinator into permit-join mode (ZHA: *Add device*;
Zigbee2MQTT: *Permit join*).
2. Power on the ESP32-C6 controller; it joins as a Zigbee end device / router.
3. The device appears with a Thermostat entity exposing current temperature and
a mode selector.
## 3. Home Assistant Climate Entity
Once paired, ZHA/Zigbee2MQTT automatically create a `climate.*` entity backed by
the Thermostat cluster. Example resulting entity state:
```yaml
climate.ballu_ac:
current_temperature: 24.5 # from local_temperature (2450 * 0.01°C)
hvac_mode: cool # from system_mode = 3
temperature: 25.0 # target setpoint
```
### Setting mode and temperature (Lovelace / service call)
```yaml
# Turn on cooling at 25°C
service: climate.set_temperature
target:
entity_id: climate.ballu_ac
data:
hvac_mode: cool
temperature: 25
```
```yaml
# Switch to heating
service: climate.set_hvac_mode
target:
entity_id: climate.ballu_ac
data:
hvac_mode: heat
```
```yaml
# Turn the unit off
service: climate.set_hvac_mode
target:
entity_id: climate.ballu_ac
data:
hvac_mode: "off"
```
## 4. Example Automations
Cool the room when it gets warm:
```yaml
automation:
- alias: "Cool bedroom when hot"
trigger:
- platform: numeric_state
entity_id: climate.ballu_ac
attribute: current_temperature
above: 27
action:
- service: climate.set_temperature
target:
entity_id: climate.ballu_ac
data:
hvac_mode: cool
temperature: 24
```
Turn the AC off when everyone leaves:
```yaml
automation:
- alias: "AC off when away"
trigger:
- platform: state
entity_id: group.family
to: "not_home"
action:
- service: climate.set_hvac_mode
target:
entity_id: climate.ballu_ac
data:
hvac_mode: "off"
```
## 5. Zigbee2MQTT Notes
If using Zigbee2MQTT, the device publishes to
`zigbee2mqtt/<friendly_name>` with a JSON payload containing
`local_temperature` and `system_mode`. Publish a command like:
```json
{ "system_mode": "cool", "occupied_heating_setpoint": 2500 }
```
Setpoints in raw ZCL are in 0.01°C units (2500 = 25.0°C), matching the firmware's
temperature representation.
## 6. Status Reporting & Faults
- The controller polls the AC on a configurable interval (default 5000 ms,
`status_monitor_config_t.poll_interval_ms`) and updates `local_temperature` /
`system_mode` accordingly.
- If the AC reports a non-zero `error_code` or `alarm_mask`, the status monitor
latches a fault (`status_monitor_has_fault`) and the app controller reports
unhealthy (`app_controller_is_healthy`).
- A communication watchdog triggers automatic reset/recovery
(`app_controller_recover_if_needed`) if polling fails repeatedly.
See `docs/zcl_hvac.md` for the full ZCL Thermostat cluster reference and
`docs/hardware_connection_diagram.md` for wiring.

View File

@@ -80,12 +80,12 @@ Implementation of ESP32-C6 based AC controller that bridges Zigbee (Home Assista
- [x] Update Readme.md - [x] Update Readme.md
### Task 7: Documentation and Validation ### Task 7: Documentation and Validation
- [ ] Update CLAUDE.md with implementation details - [x] Update CLAUDE.md with implementation details
- [ ] Create hardware connection diagram in docs/ - [x] Create hardware connection diagram in docs/
- [ ] Add usage examples for Home Assistant integration - [x] Add usage examples for Home Assistant integration
- [ ] Validate all documentation against implementation - [x] Validate all documentation against implementation
- [ ] Final review and cleanup - [x] Final review and cleanup
- [ ] Update Readme.md - [x] Update Readme.md
## Technical Details ## Technical Details