b5b3999fe4
Added more substitutions and travel time in preperation for the next phase. Trying to keep the controls generic as the WREN is more than a recliner controller and is being used in other area such as solar panel and vent controls.
753 lines
18 KiB
YAML
753 lines
18 KiB
YAML
# 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.03"
|
|
release_date: "2026-06-22"
|
|
board_name: "WREN C5"
|
|
board_mcu: "ESP32-C5"
|
|
board_rev: "1.2606.104"
|
|
|
|
# ======================== Config ===========================
|
|
|
|
board_Initial_location: "Lounge"
|
|
|
|
voltage_low: 3.00
|
|
voltage_high: 48.0
|
|
|
|
travel_time_min: "1"
|
|
travel_time_max: "60"
|
|
travel_time_step: "1"
|
|
|
|
open_travel_time_default: "12"
|
|
preset_position_time_default: "6"
|
|
close_travel_time_default: "11"
|
|
|
|
################################## 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
|
|
|
|
################################## NUMBER ######################################
|
|
number:
|
|
- platform: template
|
|
id: open_travel_time
|
|
name: "Open Travel Time"
|
|
entity_category: config
|
|
icon: mdi:arrow-expand-horizontal
|
|
optimistic: true
|
|
restore_value: true
|
|
min_value: ${travel_time_min}
|
|
max_value: ${travel_time_max}
|
|
step: ${travel_time_step}
|
|
unit_of_measurement: "s"
|
|
initial_value: ${open_travel_time_default}
|
|
|
|
- platform: template
|
|
id: preset_position_time
|
|
name: "Preset Position Time"
|
|
entity_category: config
|
|
icon: mdi:seat-recline-extra
|
|
optimistic: true
|
|
restore_value: true
|
|
min_value: ${travel_time_min}
|
|
max_value: ${travel_time_max}
|
|
step: ${travel_time_step}
|
|
unit_of_measurement: "s"
|
|
initial_value: ${preset_position_time_default}
|
|
set_action:
|
|
- lambda: |-
|
|
id(preset_position_time).publish_state(x);
|
|
id(preset_position_time_diag).publish_state(x);
|
|
|
|
- platform: template
|
|
id: close_travel_time
|
|
name: "Close Travel Time"
|
|
entity_category: config
|
|
icon: mdi:arrow-collapse-horizontal
|
|
optimistic: true
|
|
restore_value: true
|
|
min_value: ${travel_time_min}
|
|
max_value: ${travel_time_max}
|
|
step: ${travel_time_step}
|
|
unit_of_measurement: "s"
|
|
initial_value: ${close_travel_time_default}
|
|
|
|
################################## BUTTON ######################################
|
|
button:
|
|
- platform: template
|
|
name: "Reset Travel Times"
|
|
id: reset_travel_times
|
|
entity_category: config
|
|
icon: mdi:restore
|
|
on_press:
|
|
- lambda: |-
|
|
id(open_travel_time).publish_state(${open_travel_time_default});
|
|
id(preset_position_time).publish_state(${preset_position_time_default});
|
|
id(close_travel_time).publish_state(${close_travel_time_default});
|
|
|
|
ESP_LOGI(
|
|
"CONFIG",
|
|
"Travel times restored to defaults."
|
|
);
|
|
|
|
################################## TEXT ######################################
|
|
text:
|
|
- platform: template
|
|
id: board_location
|
|
name: "Board Location"
|
|
mode: TEXT
|
|
max_length: 32
|
|
restore_value: true
|
|
optimistic: true
|
|
entity_category: config
|
|
icon: mdi:map-marker
|
|
|
|
initial_value: "${board_Initial_location}"
|
|
|
|
set_action:
|
|
- lambda: |-
|
|
id(board_location).publish_state(x);
|
|
|
|
################################## TEXT SENSOR ##################################
|
|
|
|
text_sensor:
|
|
- platform: template
|
|
id: board_location_diagnostic
|
|
name: "Board Location"
|
|
entity_category: diagnostic
|
|
icon: mdi:map-marker
|
|
|
|
- platform: version
|
|
id: esphome_version
|
|
name: "ESPHome Version"
|
|
entity_category: diagnostic
|
|
icon: mdi:tag
|
|
hide_timestamp: true
|
|
hide_hash: true
|
|
|
|
- platform: template
|
|
name: "Author"
|
|
entity_category: diagnostic
|
|
icon: mdi:account
|
|
lambda: |-
|
|
return {"${author}"};
|
|
|
|
- platform: template
|
|
name: "Firmware Version"
|
|
entity_category: diagnostic
|
|
icon: mdi:application-cog
|
|
lambda: |-
|
|
return {"${firmware_version}"};
|
|
|
|
- platform: template
|
|
name: "Release Date"
|
|
entity_category: diagnostic
|
|
icon: mdi:calendar
|
|
lambda: |-
|
|
return {"${release_date}"};
|
|
|
|
- platform: template
|
|
name: "Board Name"
|
|
entity_category: diagnostic
|
|
icon: mdi:developer-board
|
|
lambda: |-
|
|
return {"${board_name}"};
|
|
|
|
- platform: template
|
|
name: "Board MCU"
|
|
entity_category: diagnostic
|
|
icon: mdi:cpu-64-bit
|
|
lambda: |-
|
|
return {"${board_mcu}"};
|
|
|
|
- platform: template
|
|
name: "Board Revision"
|
|
entity_category: diagnostic
|
|
icon: mdi:memory
|
|
lambda: |-
|
|
return {"${board_rev}"};
|
|
|
|
- platform: template
|
|
name: "Product"
|
|
entity_category: diagnostic
|
|
icon: mdi:package-variant-closed
|
|
lambda: |-
|
|
return {"Professional Energy Monitor"};
|
|
|
|
- platform: template
|
|
name: "Hardware"
|
|
entity_category: diagnostic
|
|
icon: mdi:chip
|
|
lambda: |-
|
|
return {"ESP32-C5 Dual ATM90E32"};
|
|
|
|
- platform: template
|
|
id: wifi_signal_quality
|
|
name: "WiFi Signal Quality"
|
|
entity_category: diagnostic
|
|
icon: mdi:wifi
|
|
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 {"Very Strong"};
|
|
} else if (rssi <= 50) {
|
|
return {"Excellent"};
|
|
} else if (rssi <= 60) {
|
|
return {"Healthy"};
|
|
} else if (rssi <= 70) {
|
|
return {"Very Good"};
|
|
} else if (rssi <= 80) {
|
|
return {"Good"};
|
|
} else if (rssi <= 90) {
|
|
return {"Poor - Reposition AP"};
|
|
} else if (rssi <= 100) {
|
|
return {"Very Low - Move AP"};
|
|
}
|
|
|
|
return {"No Signal"};
|
|
|
|
- platform: template
|
|
name: "Travel Configuration"
|
|
entity_category: diagnostic
|
|
icon: mdi:clipboard-check
|
|
|
|
lambda: |-
|
|
float open = id(open_travel_time).state;
|
|
float preset = id(preset_position_time).state;
|
|
float close = id(close_travel_time).state;
|
|
|
|
if (preset > open)
|
|
return {"Invalid - Preset exceeds Open"};
|
|
|
|
if (preset < close)
|
|
return {"Invalid - Preset below Close"};
|
|
|
|
return {"Valid"};
|
|
|
|
# =====================================================
|
|
# 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: template
|
|
id: open_travel_time_diag
|
|
name: " Open Time"
|
|
entity_category: diagnostic
|
|
icon: mdi:arrow-expand-horizontal
|
|
unit_of_measurement: "s"
|
|
accuracy_decimals: 0
|
|
lambda: |-
|
|
return id(open_travel_time).state;
|
|
|
|
- platform: template
|
|
id: preset_position_time_diag
|
|
name: "Travel Preset Time"
|
|
entity_category: diagnostic
|
|
icon: mdi:seat-recline-extra
|
|
unit_of_measurement: "s"
|
|
accuracy_decimals: 0
|
|
lambda: |-
|
|
return id(preset_position_time).state;
|
|
|
|
- platform: template
|
|
id: close_travel_time_diag
|
|
name: "Travel Close Time"
|
|
entity_category: diagnostic
|
|
icon: mdi:arrow-collapse-horizontal
|
|
unit_of_measurement: "s"
|
|
accuracy_decimals: 0
|
|
lambda: |-
|
|
return id(close_travel_time).state;
|
|
|
|
- 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
|
|
// );
|