# Dave Williams, DitroniX 2019-2026 (ditronix.net) # WREN | ESP32-C5 Recliner Linear Motor Controller | IoT Home Automation # Example Code, to demonstrate and test the WREN C5 # Remember! # - Set the BOARD to Use ESP32C5 Dev Module (or similar). # - You can also set the BAUD rate up to 921600 to speed up flashing. # - The SDK does NOT need external power to flash. It will take Power from the USB 5V. # - The Serial Monitor is configured for BAUD 115200 # The purpose of this test code is to cycle through the various main functions of the board as part of bring up testing. # This test code is OPEN SOURCE and formatted for easier viewing. Although is is not intended for real world use, it may be freely used, or modified as needed. # It is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # For board configuration, see: # * github.com/DitroniX/WREN-Recliner-Linear-Motor-Controller-IoT-Home-Automation/wiki/Arduino-IDE # * github.com/DitroniX/WREN-Recliner-Linear-Motor-Controller-IoT-Home-Automation/wiki/PlatformIO-ESP32%E2%80%90C5 # * github.com/DitroniX/WREN-Recliner-Linear-Motor-Controller-IoT-Home-Automation/wiki/Home-Assistant-and-ESPHome # * github.com/DitroniX/Home-Assistant-Dev # Further information, details and examples can be found on our website or github.com/DitroniX # * ditronix.net # * github.com/DitroniX # * github.com/DitroniX/WREN-Recliner-Linear-Motor-Controller-IoT-Home-Automation # * github.com/DitroniX/WREN-Recliner-Linear-Motor-Controller-IoT-Home-Automation/wiki # * github.com/DitroniX/WREN-Recliner-Linear-Motor-Controller-IoT-Home-Automation/wiki/FAQ # * hackster.io/DitroniX/ # * hackster.io/DitroniX/wren-esp32-c5-recliner-linear-motor-controller-iot-rs485-e2b397 # This YAML code has been created to show the features and functionality of WREN C5. # This code has expanded to become an all encompassing ESPHome applicaition and is a fully working baseline. esphome: name: wren-c5 friendly_name: WREN C5 on_boot: priority: 800 then: - lambda: |- uint8_t cfg[2] = {0x03,0x00}; id(bus_a).write(0x21,cfg,2); uint8_t pol[2] = {0x02,0x00}; id(bus_a).write(0x21,pol,2); uint8_t out[2] = {0x01,0x00}; id(bus_a).write(0x21,out,2); ESP_LOGI( "tca", "TCA6408A initialized" ); - delay: 500ms - if: condition: binary_sensor.is_on: sflag then: - logger.log: "SFLAG ACTIVE AFTER BOOT" - script.execute: fault_handler esp32: board: esp32-c5-devkitc-1 variant: esp32c5 framework: type: esp-idf ################################## ESPHOME ################################## # ======================== ESPHome - Enter Below your Key/Pasawords =========================== api: encryption: key: !secret wren_c5__encryption_key ota: - platform: esphome password: !secret wren_c5__ota_password wifi: ssid: !secret wifi_ssid password: !secret wifi_password ap: ssid: WREN C5 Fallback Hotspot password: !secret wren_c5__ap_password captive_portal: logger: ################################## SUBSTITUTIONS ################################## substitutions: # ======================== About =========================== author: "DitroniX - Dave Williams" firmware_version: "1.00.02" release_date: "2026-06-21" board_name: "WREN C5" board_mcu: "ESP32-C5" board_rev: "1.2606.104" # ======================== Config =========================== board_location: "Lounge" voltage_low: 3.00 voltage_high: 48.0 ################################## SETUP ################################## # ========================== GPIO ============================= i2c: id: bus_a sda: GPIO2 scl: GPIO3 scan: true globals: - id: fault_active type: bool restore_value: no initial_value: "false" - id: tca_state type: uint8_t restore_value: no initial_value: "0" ################################## SWITCH ################################## switch: - platform: gpio id: rs485_enable pin: GPIO23 restore_mode: ALWAYS_ON output: - platform: gpio pin: GPIO27 id: blue_led ################################## LIGHT ################################## light: - platform: binary id: heartbeat output: blue_led ################################## TEXT SENSOR ################################## text_sensor: # ===================================================== # WREN C5 FIRMWARE METADATA # ===================================================== - platform: template name: "Author" entity_category: diagnostic lambda: |- return {"${author}"}; - platform: template name: "Firmware Version" entity_category: diagnostic lambda: |- return {"${firmware_version}"}; - platform: template name: "Release Date" entity_category: diagnostic lambda: |- return {"${release_date}"}; - platform: template name: "Board Name" entity_category: diagnostic lambda: |- return {"${board_name}"}; - platform: template name: "Board MCU" entity_category: diagnostic lambda: |- return {"${board_mcu}"}; - platform: template name: "Board Build" entity_category: diagnostic lambda: |- return {"${board_rev}"}; - platform: template name: "Board Location" entity_category: diagnostic icon: mdi:information update_interval: 60s lambda: |- return {"${board_location}"}; - platform: template name: "Product" icon: mdi:information entity_category: diagnostic update_interval: 60s lambda: |- return {"Professional Energy Monitor"}; - platform: template name: "Hardware" entity_category: diagnostic icon: mdi:information update_interval: 60s lambda: |- return {"ESP32-C5 Dual ATM90E32"}; - platform: template name: "WiFi Signal Quality" id: wifi_signal_quality entity_category: diagnostic update_interval: 30s lambda: |- if (!id(wifi_signal_db).has_state()) { return {"No Signal"}; } int rssi = abs((int)id(wifi_signal_db).state); if (rssi <= 30) { return {"Signal Very Strong"}; } else if (rssi <= 50) { return {"Signal Excellent"}; } else if (rssi <= 60) { return {"Signal Healthy"}; } else if (rssi <= 70) { return {"Signal Very Good"}; } else if (rssi <= 80) { return {"Signal Good"}; } else if (rssi <= 90) { return {"Signal Poor - Try Moving Position"}; } else if (rssi <= 100) { return {"Signal Very Low - Move Position"}; } else { return {"No Signal"}; } # ===================================================== # VOLTAGE STATUS AND LOG # ===================================================== - platform: template name: "Supply Status" id: supply_status icon: mdi:power-plug entity_category: diagnostic update_interval: 1s lambda: |- float v = id(supply_voltage).state; constexpr float VOLTAGE_LOW = ${voltage_low}; constexpr float VOLTAGE_HIGH = ${voltage_high}; // Developer. Only enable for PCB 1.2606.104 and above, as this value is now taken from +VBUS rather than P24V_DVR // if (v >= VOLTAGE_LOW && v <= 5.5f) { // ESP_LOGI( // "POWER", // "USB Power Detected - Supply Voltage %.2fV", // v // ); // return {"USB Powered"}; // } if (v >= 5.5f && v <= 10.0f) { ESP_LOGI( "POWER", "Voltage Too Low %.2fV", v ); return {"Power Fail"}; } if (v >= 10.0f && v <= 20.0f) { return {"Low Voltage"}; } if (v >= 20.0f && v <= 30.0f) { return {"Recliner"}; } if (v >= 30.0f && v <= VOLTAGE_HIGH) { char buffer[20]; snprintf(buffer, sizeof(buffer), "%.1fV", v); return {buffer}; } if (v > 48.0f) { ESP_LOGI( "POWER", "Over Voltage %.2fV", v ); return {"Over Voltage"}; } return {"Unknown"}; ############################## BINARY SENSOR ############################### binary_sensor: - platform: gpio id: in_1a name: "Motor 1 Extend" pin: number: GPIO8 mode: input: true pulldown: true - platform: gpio id: in_1b name: "Motor 1 Retract" pin: number: GPIO9 mode: input: true pulldown: true - platform: gpio id: in_2a name: "Motor 2 Extend" pin: number: GPIO14 mode: input: true pulldown: true - platform: gpio id: in_2b name: "Motor 2 Retract" pin: number: GPIO13 mode: input: true pulldown: true - platform: gpio id: sflag name: "Motor Driver Fault" pin: number: GPIO10 mode: input: true pulldown: true on_press: then: - logger.log: "SFLAG INTERRUPT" - script.execute: fault_handler - platform: gpio id: en1 name: "EN1 Feedback" pin: number: GPIO5 mode: input: true - platform: gpio id: en2 name: "EN2 Feedback" pin: number: GPIO6 mode: input: true ################################## SENSOR ################################## sensor: - platform: adc id: supply_voltage pin: GPIO4 name: "Supply Voltage" attenuation: auto update_interval: 30s entity_category: diagnostic filters: - multiply: 44.4444 - platform: tmp102 name: "Board Temperature" address: 0x4A entity_category: diagnostic update_interval: 30s - platform: wifi_signal name: "WiFi RSSI" update_interval: 60s - platform: uptime name: "Uptime" # ===================================================== # OTHER SENSORS # ===================================================== - platform: wifi_signal name: "WiFi Signal" id: wifi_signal_db unit_of_measurement: "dBm" device_class: signal_strength state_class: measurement entity_category: diagnostic update_interval: 30s ################################## SCRIPT ################################## script: - id: fault_handler mode: single then: - lambda: |- if(id(fault_active)) return; id(fault_active)=true; ESP_LOGE( "FAULT - IMMEDIATE FAULT SHUTDOWN", "MOTOR STOP - TCA OUTPUT LOW. POWER CYCLE TO RE-ENABLE WREN" "HAVE A GREAT DAY!" ); uint8_t off[2]; off[0]=0x01; off[1]=0x00; id(bus_a).write(0x21, off, 2); id(bus_a).write(0x21, off, 2); id(bus_a).write(0x21, off, 2); - repeat: count: 20 then: - lambda: |- uint8_t led[2]; led[0]=0x01; // TCA BIT7 ON led[1]=0x80; id(bus_a).write( 0x21, led, 2 ); - delay: 1000ms - lambda: |- uint8_t led[2]; led[0]=0x01; // ALL OFF led[1]=0x00; id(bus_a).write( 0x21, led, 2 ); - delay: 100ms # - lambda: |- # ESP_LOGE( # "FAULT", # "RESTARTING" # ); # esp_restart(); ################################## INTERVAL ################################## interval: # heartbeat - interval: 1s then: - light.turn_on: heartbeat - delay: 750ms - light.turn_off: heartbeat # motor supervisor - interval: 10ms then: - lambda: |- // // HARD SFLAG CHECK // if(id(sflag).state) { ESP_LOGE( "FAULT", "SFLAG HIGH" ); id(fault_handler).execute(); return; } if(id(sflag).state) { id(fault_handler).execute(); return; } // // LATCHED FAULT // if(id(fault_active)) { uint8_t off[2]={0x01,0x00}; id(bus_a).write( 0x21, off, 2 ); return; } // // VOLTAGE CHECK // constexpr float VOLTAGE_LOW = ${voltage_low}; constexpr float VOLTAGE_HIGH = ${voltage_high}; float v = id(supply_voltage).state; if (v < VOLTAGE_LOW || v > VOLTAGE_HIGH) { ESP_LOGE("FAULT", "Voltage %.2fV out of range", v); id(fault_handler).execute(); return; } uint8_t output=0; bool m1a=id(in_1a).state; bool m1b=id(in_1b).state; bool m2a=id(in_2a).state; bool m2b=id(in_2b).state; bool active = m1a || m1b || m2a || m2b; // // BOTH ENABLES TOGETHER // if(active) { output |= (1<<0); // EN1 output |= (1<<3); // EN2 } // MOTOR 1 if(m1a) output |= (1<<1); if(m1b) output |= (1<<2); // MOTOR 2 if(m2a) output |= (1<<4); if(m2b) output |= (1<<5); uint8_t buf[2]; buf[0]=0x01; buf[1]=output; id(bus_a).write( 0x21, buf, 2 ); // ESP_LOGI( // "motor", // "OUT=0x%02X", // output // );