From 0c0883480d122f1e713bcbf55554bc606e302002 Mon Sep 17 00:00:00 2001 From: Vladimir Zagainov Date: Sat, 11 Jul 2026 19:20:23 +0300 Subject: [PATCH] feat: implement status monitoring, fault detection, and comms watchdog Co-Authored-By: Claude Opus 4.8 --- Makefile | 12 +- Readme.md | 21 +- ...lu-ac-esp32c6-controller-implementation.md | 14 +- src/status_monitor.c | 259 ++++++++++++++ src/status_monitor.h | 77 ++++ test/integration_layer_test | Bin 57336 -> 75496 bytes test/status_monitor_test | Bin 0 -> 76464 bytes test/status_monitor_test.c | 333 ++++++++++++++++++ test/uart_driver_test | Bin 57472 -> 75440 bytes test/zigbee_zcl_test | Bin 57504 -> 58960 bytes unity | 1 + 11 files changed, 707 insertions(+), 10 deletions(-) create mode 100644 src/status_monitor.c create mode 100644 src/status_monitor.h create mode 100755 test/status_monitor_test create mode 100644 test/status_monitor_test.c create mode 160000 unity diff --git a/Makefile b/Makefile index 26f2c20..f357b72 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Makefile for Ballu AC ESP32-C6 Controller -.PHONY: all build test clean test-uart test-midea-protocol test-zigbee-zcl test-integration-layer help +.PHONY: all build test clean test-uart test-midea-protocol test-zigbee-zcl test-integration-layer test-status-monitor help # Directories SRC_DIR := src @@ -31,7 +31,7 @@ $(BUILD_DIR)/app: $(SRC_FILES) $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) # Build and run tests -test: test-uart test-midea-protocol test-zigbee-zcl test-integration-layer +test: test-uart test-midea-protocol test-zigbee-zcl test-integration-layer test-status-monitor @echo "All tests passed!" # Build individual test executables @@ -51,6 +51,10 @@ $(TEST_DIR)/integration_layer_test: $(TEST_DIR)/integration_layer_test.c $(SRC_F @mkdir -p $(BUILD_DIR) $(CC) $(CFLAGS) $< $(SRC_FILES) $(UNITY_SRC) -o $@ $(LDFLAGS) +$(TEST_DIR)/status_monitor_test: $(TEST_DIR)/status_monitor_test.c $(SRC_FILES) $(UNITY_SRC) + @mkdir -p $(BUILD_DIR) + $(CC) $(CFLAGS) $< $(SRC_FILES) $(UNITY_SRC) -o $@ $(LDFLAGS) + # Run individual tests test-uart: $(TEST_DIR)/uart_driver_test @echo "Running UART driver tests..." @@ -68,6 +72,10 @@ test-integration-layer: $(TEST_DIR)/integration_layer_test @echo "Running integration layer tests..." @./$(TEST_DIR)/integration_layer_test +test-status-monitor: $(TEST_DIR)/status_monitor_test + @echo "Running status monitor tests..." + @./$(TEST_DIR)/status_monitor_test + # Clean build artifacts clean: rm -rf $(BUILD_DIR) diff --git a/Readme.md b/Readme.md index b1fab5e..57f59f9 100644 --- a/Readme.md +++ b/Readme.md @@ -11,7 +11,7 @@ This project implements a bridge between Zigbee (Home Assistant) and UART-based - UART communication at 9600 baud 8N1 for MideaUART protocol - Zigbee ZCL Thermostat cluster implementation - Bidirectional communication between Zigbee and UART layers -- Status monitoring and error handling +- Status monitoring with periodic AC polling, fault detection, and a communication watchdog - Configurable timing controls (50ms command spacing) ## Directory Structure @@ -22,6 +22,7 @@ src/ midea_protocol.c/h - MideaUART protocol encoding/decoding zigbee_zcl.c/h - Zigbee ZCL Thermostat cluster integration_layer.c/h- Integration between Zigbee and UART layers + status_monitor.c/h - AC status polling, fault detection, and comms watchdog main.c - Application entry point test/ @@ -29,6 +30,7 @@ test/ midea_protocol_test.c - Unit tests for MideaUART protocol zigbee_zcl_test.c - Unit tests for Zigbee ZCL integration_layer_test.c - Unit tests for integration layer + status_monitor_test.c - Unit tests for status monitoring docs/ protocol.md - MideaUART protocol details @@ -62,6 +64,23 @@ make upload make test ``` +## Status Monitoring + +The `status_monitor` module (see `src/status_monitor.c/h`) provides: + +- Periodic AC status polling: sends a MideaUART status-request frame over the UART + driver, receives the response, and decodes it into a `midea_status_t`. +- ZCL mapping: decoded status is mapped to ZCL Thermostat attributes + (`local_temperature`, `system_mode`) for Home Assistant reporting. +- Fault detection: any non-zero `error_code` or `alarm_mask` from the AC raises a + fault flag (`status_monitor_has_fault`) and records the fault code. +- Communication watchdog: tracks the time of the last successful poll and trips a + timeout when the poll interval (plus per-attempt timeout) is exceeded or when + `max_retries` consecutive attempts fail. + +Configuration (`status_monitor_config_t`) exposes `poll_interval_ms` (default +5000ms), `timeout_ms` (default 1000ms), and `max_retries` (default 3). + ## Implementation Progress See `docs/plans/2026-07-05-ballu-ac-esp32c6-controller-implementation.md` for detailed implementation plan and progress tracking. diff --git a/docs/plans/2026-07-05-ballu-ac-esp32c6-controller-implementation.md b/docs/plans/2026-07-05-ballu-ac-esp32c6-controller-implementation.md index 8206386..c4f2c0c 100644 --- a/docs/plans/2026-07-05-ballu-ac-esp32c6-controller-implementation.md +++ b/docs/plans/2026-07-05-ballu-ac-esp32c6-controller-implementation.md @@ -62,13 +62,13 @@ Implementation of ESP32-C6 based AC controller that bridges Zigbee (Home Assista - [x] Update Readme.md ### Task 5: Status Monitoring and Feedback -- [ ] Implement AC status polling via UART (temperature, mode, etc.) -- [ ] Map MideaUART status to ZCL attributes for reporting -- [ ] Implement error handling and fault detection -- [ ] Add watchdog for communication timeouts -- [ ] Write unit tests for status monitoring and error paths -- [ ] Run tests - must pass before next task -- [ ] Update Readme.md +- [x] Implement AC status polling via UART (temperature, mode, etc.) +- [x] Map MideaUART status to ZCL attributes for reporting +- [x] Implement error handling and fault detection +- [x] Add watchdog for communication timeouts +- [x] Write unit tests for status monitoring and error paths +- [x] Run tests - must pass before next task +- [x] Update Readme.md ### Task 6: End-to-End Integration and Testing - [ ] Integrate all layers: Zigbee ←→ Integration ←→ UART diff --git a/src/status_monitor.c b/src/status_monitor.c new file mode 100644 index 0000000..9808960 --- /dev/null +++ b/src/status_monitor.c @@ -0,0 +1,259 @@ +#include "status_monitor.h" +#include "uart_driver.h" +#include "midea_protocol.h" +#include + +// Default configuration values +#define DEFAULT_POLL_INTERVAL_MS 5000 +#define DEFAULT_TIMEOUT_MS 1000 +#define DEFAULT_MAX_RETRIES 3 + +// Maximum expected response frame size +#define STATUS_RESPONSE_MAX 32 + +bool status_monitor_init(status_monitor_t *monitor, const status_monitor_config_t *config) { + if (!monitor) { + return false; + } + + memset(monitor, 0, sizeof(status_monitor_t)); + + monitor->initialized = true; + monitor->last_poll_success = false; + monitor->last_poll_time = 0; + monitor->last_attempt_time = 0; + monitor->retry_count = 0; + monitor->error_count = 0; + monitor->comm_timeout = false; + monitor->fault_detected = false; + monitor->fault_code = 0; + + // Apply configuration with fallback to defaults + if (config) { + monitor->config.poll_interval_ms = + config->poll_interval_ms ? config->poll_interval_ms : DEFAULT_POLL_INTERVAL_MS; + monitor->config.timeout_ms = + config->timeout_ms ? config->timeout_ms : DEFAULT_TIMEOUT_MS; + monitor->config.max_retries = + config->max_retries ? config->max_retries : DEFAULT_MAX_RETRIES; + } else { + monitor->config.poll_interval_ms = DEFAULT_POLL_INTERVAL_MS; + monitor->config.timeout_ms = DEFAULT_TIMEOUT_MS; + monitor->config.max_retries = DEFAULT_MAX_RETRIES; + } + + return true; +} + +void status_monitor_deinit(status_monitor_t *monitor) { + if (monitor) { + memset(monitor, 0, sizeof(status_monitor_t)); + monitor->initialized = false; + } +} + +size_t status_monitor_build_request(uint8_t *buffer, size_t buffer_size) { + if (!buffer || buffer_size < 5) { + return 0; + } + + // MideaUART status-request frame: + // [Header 0xAA 0x55][Length 1][Command 0x07 = query][Checksum] + size_t offset = 0; + buffer[offset++] = 0xAA; + buffer[offset++] = 0x55; + buffer[offset++] = 1; // length of command payload + buffer[offset++] = 0x07; // query/status-request command + + // Checksum: XOR of length + command bytes + uint8_t checksum = 0; + for (size_t i = 2; i < offset; i++) { + checksum ^= buffer[i]; + } + buffer[offset++] = checksum; + + return offset; +} + +// Internal: apply a freshly decoded status to monitor state. +static void status_monitor_apply_status(status_monitor_t *monitor, + const midea_status_t *status, + uint32_t current_time) { + monitor->last_poll_success = true; + monitor->last_poll_time = current_time; + monitor->retry_count = 0; + monitor->comm_timeout = false; + + memcpy(&monitor->last_status, status, sizeof(midea_status_t)); + + // Fault detection: any error code or alarm bit indicates a fault. + if (status->error_code != 0 || status->alarm_mask != 0) { + monitor->fault_detected = true; + monitor->fault_code = + (uint16_t)(status->error_code) | status->alarm_mask; + } else { + monitor->fault_detected = false; + monitor->fault_code = 0; + } + + // Keep ZCL attributes in sync for reporting. + status_monitor_map_to_zcl(status, &monitor->last_zcl_attrs); +} + +bool status_monitor_process_response(status_monitor_t *monitor, + const uint8_t *buffer, size_t length, + uint32_t current_time, midea_status_t *status) { + if (!monitor || !monitor->initialized || !status) { + return false; + } + + monitor->last_attempt_time = current_time; + + midea_status_t decoded; + memset(&decoded, 0, sizeof(decoded)); + + if (!buffer || length == 0 || !midea_protocol_decode(buffer, length, &decoded)) { + // Decode failed -> treat as a communication error. + status_monitor_handle_error(monitor); + return false; + } + + status_monitor_apply_status(monitor, &decoded, current_time); + memcpy(status, &decoded, sizeof(midea_status_t)); + return true; +} + +bool status_monitor_poll(status_monitor_t *monitor, uart_driver_t *uart, + uint32_t current_time, midea_status_t *status) { + if (!monitor || !monitor->initialized || !uart || !status) { + return false; + } + + monitor->last_attempt_time = current_time; + + // Build and send a status-request frame. + uint8_t request[8]; + size_t request_len = status_monitor_build_request(request, sizeof(request)); + if (request_len == 0 || + !uart_driver_send(uart, request, request_len, monitor->config.timeout_ms)) { + status_monitor_handle_error(monitor); + return false; + } + + // Receive the response. + uint8_t response[STATUS_RESPONSE_MAX]; + int received = uart_driver_receive(uart, response, sizeof(response), + monitor->config.timeout_ms); + if (received <= 0) { + // No data / error -> communication failure. + status_monitor_handle_error(monitor); + return false; + } + + return status_monitor_process_response(monitor, response, (size_t)received, + current_time, status); +} + +void status_monitor_map_to_zcl(const midea_status_t *status, zcl_thermostat_attrs_t *zcl_attrs) { + if (!status || !zcl_attrs) { + return; + } + + // Map MideaUART status to ZCL Thermostat attributes + zcl_attrs->local_temperature = status->indoor_temp; + + // Map power state and mode to ZCL system_mode + if (status->power_state == 0x00) { + // AC is OFF + zcl_attrs->system_mode = 0x00; // Off + } else { + // AC is ON, map the mode + switch (status->mode) { + case MODE_COOL: + zcl_attrs->system_mode = 0x03; // Cooling + break; + case MODE_HEAT: + zcl_attrs->system_mode = 0x04; // Heating + break; + case MODE_AUTO: + zcl_attrs->system_mode = 0x01; // Auto + break; + case MODE_DRY: + zcl_attrs->system_mode = 0x08; // Dry + break; + case MODE_FAN: + zcl_attrs->system_mode = 0x00; // Off (treat fan only as off for HVAC) + break; + default: + zcl_attrs->system_mode = 0x00; // Default to Off + break; + } + } + + // Set control sequence (simplified) + zcl_attrs->control_sequence = 0x00; // Not used in basic implementation +} + +bool status_monitor_check_timeout(const status_monitor_t *monitor, uint32_t current_time) { + if (!monitor || !monitor->initialized) { + return true; // Treat uninitialized monitor as timed out + } + + // Never successfully polled -> considered timed out. + if (!monitor->last_poll_success) { + return true; + } + + // Watchdog window: one poll interval plus the per-attempt timeout allowance. + uint32_t window = monitor->config.poll_interval_ms + monitor->config.timeout_ms; + + // Guard against clock going backwards. + if (current_time < monitor->last_poll_time) { + return false; + } + + return (current_time - monitor->last_poll_time) > window; +} + +void status_monitor_handle_error(status_monitor_t *monitor) { + if (!monitor) { + return; + } + + monitor->last_poll_success = false; + monitor->error_count++; + + if (monitor->retry_count < 0xFF) { + monitor->retry_count++; + } + + // Trip the watchdog once retries are exhausted. + if (monitor->retry_count >= monitor->config.max_retries) { + monitor->comm_timeout = true; + } +} + +bool status_monitor_has_fault(const status_monitor_t *monitor) { + if (!monitor) { + return false; + } + return monitor->fault_detected; +} + +bool status_monitor_get_last_status(const status_monitor_t *monitor, midea_status_t *status) { + if (!monitor || !status) { + return false; + } + + memcpy(status, &monitor->last_status, sizeof(midea_status_t)); + return true; +} + +bool status_monitor_get_last_zcl_attrs(const status_monitor_t *monitor, zcl_thermostat_attrs_t *zcl_attrs) { + if (!monitor || !zcl_attrs) { + return false; + } + + memcpy(zcl_attrs, &monitor->last_zcl_attrs, sizeof(zcl_thermostat_attrs_t)); + return true; +} diff --git a/src/status_monitor.h b/src/status_monitor.h new file mode 100644 index 0000000..2970d82 --- /dev/null +++ b/src/status_monitor.h @@ -0,0 +1,77 @@ +#ifndef STATUS_MONITOR_H +#define STATUS_MONITOR_H + +#include +#include +#include "midea_protocol.h" +#include "zigbee_zcl.h" +#include "uart_driver.h" + +// Status monitoring configuration +typedef struct { + uint32_t poll_interval_ms; // How often to poll for status (default: 5000ms) + uint32_t timeout_ms; // Communication timeout per attempt (default: 1000ms) + uint8_t max_retries; // Maximum retry attempts before comm failure (default: 3) +} status_monitor_config_t; + +// Status monitoring state +typedef struct { + bool initialized; + bool last_poll_success; + uint32_t last_poll_time; // Timestamp (ms) of last SUCCESSFUL poll + uint32_t last_attempt_time; // Timestamp (ms) of last poll attempt + uint8_t retry_count; // Consecutive failed attempts + uint32_t error_count; // Total accumulated communication errors + bool comm_timeout; // Watchdog tripped (retries exhausted / no response) + bool fault_detected; // AC reported an error_code or alarm_mask + uint16_t fault_code; // Last fault value (error_code | alarm_mask bits) + status_monitor_config_t config; + midea_status_t last_status; + zcl_thermostat_attrs_t last_zcl_attrs; +} status_monitor_t; + +// Initialize status monitor. If config is NULL, defaults are applied. +bool status_monitor_init(status_monitor_t *monitor, const status_monitor_config_t *config); + +// Deinitialize status monitor +void status_monitor_deinit(status_monitor_t *monitor); + +// Poll AC status via UART: sends a status request, receives and decodes the +// response, then updates internal state (fault detection, watchdog). +// current_time is the current monotonic time in milliseconds. +// Returns true on a successful poll, false on communication/decode failure. +bool status_monitor_poll(status_monitor_t *monitor, uart_driver_t *uart, + uint32_t current_time, midea_status_t *status); + +// Process a raw UART response buffer (decode + fault detection + state update). +// Exposed separately so it can be exercised without live hardware. +// Returns true if the buffer decoded into a valid status. +bool status_monitor_process_response(status_monitor_t *monitor, + const uint8_t *buffer, size_t length, + uint32_t current_time, midea_status_t *status); + +// Map MideaUART status to ZCL attributes +void status_monitor_map_to_zcl(const midea_status_t *status, zcl_thermostat_attrs_t *zcl_attrs); + +// Watchdog: returns true if the time since the last successful poll exceeds the +// allowed window (poll_interval_ms + timeout_ms), or if the monitor has never +// polled successfully. +bool status_monitor_check_timeout(const status_monitor_t *monitor, uint32_t current_time); + +// Handle a communication error: increments retry/error counters and trips the +// watchdog once max_retries is reached. +void status_monitor_handle_error(status_monitor_t *monitor); + +// Returns true if the AC currently reports a fault. +bool status_monitor_has_fault(const status_monitor_t *monitor); + +// Get last known status. Returns false on bad args. +bool status_monitor_get_last_status(const status_monitor_t *monitor, midea_status_t *status); + +// Get last known ZCL attributes. Returns false on bad args. +bool status_monitor_get_last_zcl_attrs(const status_monitor_t *monitor, zcl_thermostat_attrs_t *zcl_attrs); + +// Build a MideaUART status-request frame into buffer. Returns bytes written (0 on error). +size_t status_monitor_build_request(uint8_t *buffer, size_t buffer_size); + +#endif // STATUS_MONITOR_H diff --git a/test/integration_layer_test b/test/integration_layer_test index 89ac21ffc5565cab633ddecdd2560a5ff761dc86..2dbe7c43816ec15d32507843c1a8959e74d9bdc4 100755 GIT binary patch literal 75496 zcmeHwdwf*Ywf{ad2~2o~m!Le76qNufT0s*ZWiWt25isDRRT+k4NFsSLnOGuP2cs=j zyq8g0ZMBjBZ3ESoDpu0MHHy`=wZ8(jN-M1i&^iIV#mcpb1^Ioy`!RE7G83@9{rvv; zP58{rS$nU&_F8MNz4qE`pL6)lCm(z|KnP3V&w2!~PhHiQ%d1K}VM%A^j72j%1@q@ERHNIf=m<9|`PVAHg@K&_`62C0d_11g z>Zp{26VuBBuKa6N?KLF&;DR6X*Hdq?^zvF2e@k=&!sI8}U#y&VEK5xux4kTSZI_d! z9?#O+sI?h6qxj^CrZLJ^WamqpO{A7`R#sM5nF6IZ1CfrRmuYL*8@=d!pF3yX^|NNq zQKN}*iT3N{D$18mDXFh00nESA_>hkm*RTKH zy;om(?V1-Zdh3FWZ++qOD9gr2$#f2!&^hv2>F%SnNvKou;R1iTkdJg)xmkszW6s*!{S^^gcV^~p6u)E zLabYZkINFF>6U288Y1#rou0S&IaOfoJ-I4v%SopdDkDyZ2nnn8>UL-FYVU?u`hPhsxUW*vC9f9&&&J2)ypHg@Xj;fh5AO3O;=@TjIJcbKEkcdM#NIV| ztBaa`BHb&6OUnsE4Xi{maSuauwHx;+$ZI7if(6y;{BJD z$?5LyxIUO4gv{enPI+IIJYKWq6!fcjbb!Zx;L)M%ZlJr{Wx2b-SC_OyueO8!+71uF z4k6D2R$@DB>KEL1bhH}#T-c_|x+;`mx$6(ticsg3qTZF^&JV8=`R_s2Lg+G#v1kX) z9O&Y6?qE>?etr0S@2J?j1%23c(JX|tqp;FTtW9wa3<&OfL-$RZkD&dQ29J_M%jL2% zj$-~SqJIAj9iCiu9dzN)x}fd#fiCnMjMqhz)Vl2857u@t7*T5#TTX$u__-nH_@S(ojfdV{xrX#H-MU02EHra-Wxc^>5vm+33))j z|wvrtK26 zX&)xK$D_>=-~ukf7`FU50WRq5qihAheHXYv;5ItP?f63iTsLrwf%iM$2`gB%HvtaX z?`t0^x*LG^EAk1v*Aw8i0&f#=ULk)2&}YH89lLCJozO!g=1ka`A@#8vxGw@1dTlw> z3*PwQ!F|sX599FrI6RJtp;rLBO~lJ`xBMm^Z&(zs7 zyo9+T*W?}3LW^Mc0&BU*90mU-j^$i@S!m;kVA04(`8le*@v_h>C?8_VFCL@HM_nE| zi1PlXd`XrnUw?VXaaOP>-IO28QRSI=q0gcG)H%A}?DJLmt9hYoP~L6IGbgF?Kz?W` z%0D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 z�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 eX<&zQKygJT1 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 YRv8OH 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 + +void setUp(void) {} +void tearDown(void) {} + +// Build a valid MideaUART status response frame for testing. +// data[0..7] follow the decoder layout: +// d0: (mode & 0x0F) | (power << 4) +// d1,d2: indoor_temp (LE) +// d3,d4: target_temp (LE) +// d5: (fan & 0x0F) | (error << 4) +// d6: alarm low, d7: alarm high +static size_t build_status_frame(uint8_t *buf, uint8_t mode, uint8_t power, + int16_t indoor, int16_t target, + uint8_t fan, uint8_t error, uint16_t alarm) { + size_t o = 0; + buf[o++] = 0xAA; + buf[o++] = 0x55; + buf[o++] = 8; // length + buf[o++] = 0x07; // status command + buf[o++] = (mode & 0x0F) | ((power & 0x01) << 4); + buf[o++] = indoor & 0xFF; + buf[o++] = (indoor >> 8) & 0xFF; + buf[o++] = target & 0xFF; + buf[o++] = (target >> 8) & 0xFF; + buf[o++] = (fan & 0x0F) | ((error & 0x0F) << 4); + buf[o++] = alarm & 0xFF; + buf[o++] = (alarm >> 8) & 0xFF; + uint8_t chk = 0; + for (size_t i = 2; i < o; i++) chk ^= buf[i]; + buf[o++] = chk; + return o; +} + +void test_status_monitor_init_defaults(void) { + status_monitor_t m; + TEST_ASSERT_TRUE(status_monitor_init(&m, NULL)); + TEST_ASSERT_TRUE(m.initialized); + TEST_ASSERT_EQUAL_UINT32(5000, m.config.poll_interval_ms); + TEST_ASSERT_EQUAL_UINT32(1000, m.config.timeout_ms); + TEST_ASSERT_EQUAL_UINT8(3, m.config.max_retries); + TEST_ASSERT_FALSE(m.last_poll_success); + TEST_ASSERT_FALSE(m.fault_detected); +} + +void test_status_monitor_init_custom_config(void) { + status_monitor_config_t cfg = { .poll_interval_ms = 2000, .timeout_ms = 500, .max_retries = 5 }; + status_monitor_t m; + TEST_ASSERT_TRUE(status_monitor_init(&m, &cfg)); + TEST_ASSERT_EQUAL_UINT32(2000, m.config.poll_interval_ms); + TEST_ASSERT_EQUAL_UINT32(500, m.config.timeout_ms); + TEST_ASSERT_EQUAL_UINT8(5, m.config.max_retries); +} + +void test_status_monitor_init_null(void) { + TEST_ASSERT_FALSE(status_monitor_init(NULL, NULL)); +} + +void test_build_request_frame(void) { + uint8_t req[8]; + size_t len = status_monitor_build_request(req, sizeof(req)); + TEST_ASSERT_EQUAL_UINT(5, len); + TEST_ASSERT_EQUAL_UINT8(0xAA, req[0]); + TEST_ASSERT_EQUAL_UINT8(0x55, req[1]); + TEST_ASSERT_EQUAL_UINT8(0x07, req[3]); + // checksum = len ^ cmd = 1 ^ 0x07 + TEST_ASSERT_EQUAL_UINT8((uint8_t)(1 ^ 0x07), req[4]); +} + +void test_build_request_buffer_too_small(void) { + uint8_t req[3]; + TEST_ASSERT_EQUAL_UINT(0, status_monitor_build_request(req, sizeof(req))); + TEST_ASSERT_EQUAL_UINT(0, status_monitor_build_request(NULL, 8)); +} + +void test_process_response_success(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); + + uint8_t frame[16]; + size_t len = build_status_frame(frame, MODE_COOL, 1, 2300, 2400, 2, 0, 0); + + midea_status_t status; + TEST_ASSERT_TRUE(status_monitor_process_response(&m, frame, len, 1000, &status)); + TEST_ASSERT_EQUAL_UINT8(MODE_COOL, status.mode); + TEST_ASSERT_EQUAL_INT16(2300, status.indoor_temp); + TEST_ASSERT_EQUAL_INT16(2400, status.target_temp); + TEST_ASSERT_TRUE(m.last_poll_success); + TEST_ASSERT_EQUAL_UINT32(1000, m.last_poll_time); + TEST_ASSERT_EQUAL_UINT8(0, m.retry_count); + TEST_ASSERT_FALSE(m.fault_detected); +} + +void test_process_response_decode_failure(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); + + // Bad header -> decode fails -> counts as error. + uint8_t bad[12] = { 0x00, 0x00, 8, 0x07, 0, 0, 0, 0, 0, 0, 0, 0 }; + midea_status_t status; + TEST_ASSERT_FALSE(status_monitor_process_response(&m, bad, sizeof(bad), 500, &status)); + TEST_ASSERT_FALSE(m.last_poll_success); + TEST_ASSERT_EQUAL_UINT8(1, m.retry_count); + TEST_ASSERT_EQUAL_UINT32(1, m.error_count); +} + +void test_process_response_null_buffer(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); + midea_status_t status; + TEST_ASSERT_FALSE(status_monitor_process_response(&m, NULL, 0, 100, &status)); + TEST_ASSERT_EQUAL_UINT8(1, m.retry_count); +} + +void test_fault_detection_error_code(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); + + uint8_t frame[16]; + size_t len = build_status_frame(frame, MODE_COOL, 1, 2300, 2400, 2, 3, 0); + midea_status_t status; + TEST_ASSERT_TRUE(status_monitor_process_response(&m, frame, len, 1000, &status)); + TEST_ASSERT_EQUAL_UINT8(3, status.error_code); + TEST_ASSERT_TRUE(status_monitor_has_fault(&m)); + TEST_ASSERT_TRUE(m.fault_code != 0); +} + +void test_fault_detection_alarm_mask(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); + + uint8_t frame[16]; + size_t len = build_status_frame(frame, MODE_HEAT, 1, 2000, 2100, 1, 0, 0x0004); + midea_status_t status; + TEST_ASSERT_TRUE(status_monitor_process_response(&m, frame, len, 1000, &status)); + TEST_ASSERT_EQUAL_UINT16(0x0004, status.alarm_mask); + TEST_ASSERT_TRUE(status_monitor_has_fault(&m)); +} + +void test_fault_cleared_on_good_status(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); + + uint8_t frame[16]; + // First a faulty status. + size_t len = build_status_frame(frame, MODE_COOL, 1, 2300, 2400, 2, 5, 0); + midea_status_t status; + status_monitor_process_response(&m, frame, len, 1000, &status); + TEST_ASSERT_TRUE(status_monitor_has_fault(&m)); + + // Then a clean status clears the fault. + len = build_status_frame(frame, MODE_COOL, 1, 2300, 2400, 2, 0, 0); + status_monitor_process_response(&m, frame, len, 2000, &status); + TEST_ASSERT_FALSE(status_monitor_has_fault(&m)); + TEST_ASSERT_EQUAL_UINT16(0, m.fault_code); +} + +void test_handle_error_trips_watchdog(void) { + status_monitor_config_t cfg = { .poll_interval_ms = 5000, .timeout_ms = 1000, .max_retries = 3 }; + status_monitor_t m; + status_monitor_init(&m, &cfg); + + status_monitor_handle_error(&m); + TEST_ASSERT_EQUAL_UINT8(1, m.retry_count); + TEST_ASSERT_FALSE(m.comm_timeout); + + status_monitor_handle_error(&m); + TEST_ASSERT_EQUAL_UINT8(2, m.retry_count); + TEST_ASSERT_FALSE(m.comm_timeout); + + status_monitor_handle_error(&m); + TEST_ASSERT_EQUAL_UINT8(3, m.retry_count); + TEST_ASSERT_TRUE(m.comm_timeout); + TEST_ASSERT_EQUAL_UINT32(3, m.error_count); +} + +void test_check_timeout_never_polled(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); + TEST_ASSERT_TRUE(status_monitor_check_timeout(&m, 0)); + TEST_ASSERT_TRUE(status_monitor_check_timeout(&m, 100000)); +} + +void test_check_timeout_within_window(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); // window = 5000 + 1000 = 6000 + + uint8_t frame[16]; + size_t len = build_status_frame(frame, MODE_COOL, 1, 2300, 2400, 2, 0, 0); + midea_status_t status; + status_monitor_process_response(&m, frame, len, 1000, &status); + + // 1000 -> 5000 : within 6000ms window + TEST_ASSERT_FALSE(status_monitor_check_timeout(&m, 5000)); +} + +void test_check_timeout_exceeded(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); // window = 6000 + + uint8_t frame[16]; + size_t len = build_status_frame(frame, MODE_COOL, 1, 2300, 2400, 2, 0, 0); + midea_status_t status; + status_monitor_process_response(&m, frame, len, 1000, &status); + + // 1000 -> 8000 = 7000ms elapsed > 6000ms window + TEST_ASSERT_TRUE(status_monitor_check_timeout(&m, 8000)); +} + +void test_check_timeout_uninitialized(void) { + status_monitor_t m; + memset(&m, 0, sizeof(m)); + TEST_ASSERT_TRUE(status_monitor_check_timeout(&m, 100)); + TEST_ASSERT_TRUE(status_monitor_check_timeout(NULL, 100)); +} + +void test_poll_uart_no_data(void) { + // The mock UART receive returns 0 bytes -> poll should fail and count error. + status_monitor_t m; + status_monitor_init(&m, NULL); + + uart_config_t ucfg = { .tx_pin = 1, .rx_pin = 2, .baud_rate = 9600, + .data_bits = 8, .parity = 0, .stop_bits = 1 }; + uart_driver_t uart; + TEST_ASSERT_TRUE(uart_driver_init(&uart, &ucfg)); + + midea_status_t status; + TEST_ASSERT_FALSE(status_monitor_poll(&m, &uart, 1000, &status)); + TEST_ASSERT_FALSE(m.last_poll_success); + TEST_ASSERT_EQUAL_UINT8(1, m.retry_count); + TEST_ASSERT_EQUAL_UINT32(1000, m.last_attempt_time); +} + +void test_poll_null_args(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); + midea_status_t status; + TEST_ASSERT_FALSE(status_monitor_poll(&m, NULL, 0, &status)); + TEST_ASSERT_FALSE(status_monitor_poll(NULL, NULL, 0, &status)); +} + +void test_map_to_zcl_modes(void) { + midea_status_t s; + zcl_thermostat_attrs_t z; + memset(&s, 0, sizeof(s)); + + // Power off -> Off regardless of mode + s.power_state = 0; s.mode = MODE_COOL; s.indoor_temp = 2500; + status_monitor_map_to_zcl(&s, &z); + TEST_ASSERT_EQUAL_UINT8(0x00, z.system_mode); + TEST_ASSERT_EQUAL_INT16(2500, z.local_temperature); + + s.power_state = 1; + s.mode = MODE_COOL; status_monitor_map_to_zcl(&s, &z); + TEST_ASSERT_EQUAL_UINT8(0x03, z.system_mode); + s.mode = MODE_HEAT; status_monitor_map_to_zcl(&s, &z); + TEST_ASSERT_EQUAL_UINT8(0x04, z.system_mode); + s.mode = MODE_AUTO; status_monitor_map_to_zcl(&s, &z); + TEST_ASSERT_EQUAL_UINT8(0x01, z.system_mode); + s.mode = MODE_DRY; status_monitor_map_to_zcl(&s, &z); + TEST_ASSERT_EQUAL_UINT8(0x08, z.system_mode); +} + +void test_get_last_status_and_zcl(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); + + uint8_t frame[16]; + size_t len = build_status_frame(frame, MODE_HEAT, 1, 2100, 2200, 3, 0, 0); + midea_status_t status; + status_monitor_process_response(&m, frame, len, 1000, &status); + + midea_status_t got; + TEST_ASSERT_TRUE(status_monitor_get_last_status(&m, &got)); + TEST_ASSERT_EQUAL_UINT8(MODE_HEAT, got.mode); + TEST_ASSERT_EQUAL_INT16(2100, got.indoor_temp); + + zcl_thermostat_attrs_t z; + TEST_ASSERT_TRUE(status_monitor_get_last_zcl_attrs(&m, &z)); + TEST_ASSERT_EQUAL_UINT8(0x04, z.system_mode); + TEST_ASSERT_EQUAL_INT16(2100, z.local_temperature); + + TEST_ASSERT_FALSE(status_monitor_get_last_status(&m, NULL)); + TEST_ASSERT_FALSE(status_monitor_get_last_status(NULL, &got)); +} + +void test_successful_poll_resets_retry(void) { + status_monitor_t m; + status_monitor_init(&m, NULL); + + // Accumulate errors + status_monitor_handle_error(&m); + status_monitor_handle_error(&m); + TEST_ASSERT_EQUAL_UINT8(2, m.retry_count); + + // A good response resets retry_count and clears comm_timeout + uint8_t frame[16]; + size_t len = build_status_frame(frame, MODE_COOL, 1, 2300, 2400, 2, 0, 0); + midea_status_t status; + TEST_ASSERT_TRUE(status_monitor_process_response(&m, frame, len, 3000, &status)); + TEST_ASSERT_EQUAL_UINT8(0, m.retry_count); + TEST_ASSERT_FALSE(m.comm_timeout); +} + +int main(void) { + UNITY_BEGIN(); + RUN_TEST(test_status_monitor_init_defaults); + RUN_TEST(test_status_monitor_init_custom_config); + RUN_TEST(test_status_monitor_init_null); + RUN_TEST(test_build_request_frame); + RUN_TEST(test_build_request_buffer_too_small); + RUN_TEST(test_process_response_success); + RUN_TEST(test_process_response_decode_failure); + RUN_TEST(test_process_response_null_buffer); + RUN_TEST(test_fault_detection_error_code); + RUN_TEST(test_fault_detection_alarm_mask); + RUN_TEST(test_fault_cleared_on_good_status); + RUN_TEST(test_handle_error_trips_watchdog); + RUN_TEST(test_check_timeout_never_polled); + RUN_TEST(test_check_timeout_within_window); + RUN_TEST(test_check_timeout_exceeded); + RUN_TEST(test_check_timeout_uninitialized); + RUN_TEST(test_poll_uart_no_data); + RUN_TEST(test_poll_null_args); + RUN_TEST(test_map_to_zcl_modes); + RUN_TEST(test_get_last_status_and_zcl); + RUN_TEST(test_successful_poll_resets_retry); + return UNITY_END(); +} diff --git a/test/uart_driver_test b/test/uart_driver_test index 56dc5e5b806c68fffb7c9dc19a1b72c6adb95ed9..2e90a538c4d25feba1eaa07226d46cfcd4ccd028 100755 GIT binary patch delta 16736 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 RZ*|>OFlC{6QZ)wge*q-9NM8T| delta 12131 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 UdnTcA&GYAc^wz*>JMo78|9V7uYybcN diff --git a/test/zigbee_zcl_test b/test/zigbee_zcl_test index 3906661e1a2e09f933553c3b5c28f0d02178df1f..811fe61379fc06f5691f7734a2535244a194987f 100755 GIT binary patch delta 15368 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 C@n1Ip delta 10659 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 vv5OaL4dn%k&Zji5du!_rUBPnK