This project has been the culmination of my focus as-of-late to deliver the fast responding, reliable motion lighting. Turning on and off lights is fun, except when there is lag, or a sensor misses movement. Building upon recent experience with ESPHome, I have refine the requirements to the following;
- consistently rapid response time from detection to turn_on
- sub 5 second turn_off time
- the ability capture near-still movement
- the ability to tune detection parameters
- the ability to function in the absence of HA
- reduced functionality accepted
Fulfilling all these requirements has been a journey, which has lead me to build this monstrosity. The requested maintenance window for a build picture was denied by āthe bossā.
A few words on parts selection. This used to be a sole mmWave sensor used in conjuction with a Philips Hue PIR and that worked really well. 99% of the time. But the first requirement for fast response time was missed once or twice a day. Some device was caught sleeping and you could enter the room before the lights turned on. Leading me down the path of consolidating sensors and reducing dependencies.
I could never tell the true culprit of this infrequent slowness. Be it the ZHA network, wifi, HA or light controllers sleeping before being turned on. So I chose to remove the performance dependencies that I could, ZHA, HA & partially wifi by selecting a POE ESP32 for the sensor. So while the lights were still wifi, halving any potential wifi latency seemed like a good step.
Letās talk about the sensors
* mmWave
The mmWave chosen was a known value for me, having used it for a few months in similar presence-based projects. The ability to capture micro-movement with sub-second hold times is a huge benefit. With the expected constraint of undesired detection due to any physical object moving. Hence the PIR combination.
* PIR
Adding a PIR sensor module was an unknown for me. And like most of my projects, I start cheap to see what works, what doesnāt, and what really matters. So when an HC-SR501 PIR landed at the doorstep, I quickly learned about electromagnetic interference and just poor detection qualities. After sorting out the EM issue by disabling wifi on the ESP32-POE, adding a choke and filter capacitor, the HC-SR501 was still an unreliable choice. The espresso machine never moves, but the heat from it would trigger a detection. This lead me to actually reading PIR datasheets and ohāboy are there a lot of options. I landed on a Panasonic Slight Motion u6A for a number of key reasons;
- the 6uA seems to have the best balance of power draw and circuit [startup] stability time.
- the slight motion speed detection threshold of 0.5M/s is desired based on experience from other radar projects and tracking people movement.
- a smaller [target] object size helps trigger a detection sooner when appearing around a corner (head size vs body size target).
After two days of practical experience with the Panasonic I can confidently say that previous false positives (due to non-moving heat sources) are gone and the datasheets standby/mask times are conservative. I can see ~1 sec detection/redetection times which almost borders on flapping.
* Flapping & Detection
Since we are working with sensor modules with extremely quick trigger/hold/retrigger times it is quite important to include cooldown/debounce time into the light-trigger logic. For that reason I included the delay_off
parameter in the binary_sensor configurations.
Letās talk about the lights
The lights in question are running either Tasmota or MongooseOS (ShellyRGBW). I have considered migrating any and all to Tasmota or ESPHome but I am unconvinced the firmware is a culprit in any slow response. I say this from the observation that they all turn_on within milliseconds of each other. Therefore it stands to reason that any slowness in response is not firmware related. I have taken precautions non-the-less. These include;
- disabling any power-save/ECO modes
- configuring <500ms turn_on transition times
- Tasmota
speed 1
is the fastest available at 500MS - Shellyās at 166ms because I can.
- Tasmota
ESPHome Configuration
Letās go back to our requirements and map out specific configurations targeted at solving each need;
consistently rapid response time from detection to turn_on
This requirement is met by having the sensor send http calls in order to turn_on the lights, even though HA has an automation to do so. No other parameters are sent to the lights from the sensor. The key here is that a light will begin to turn on sooner from a direct API call from the sensor than vs HA. HA retains all the customization options since there lights have numerous scenes/etc. Out of this I have gained at least a whole two tenths of a second. It could be more, but I only actually measured after the fact (no baseline was taken when sensor was wifi). If you watch Formula 1, you will know that that is a lot
15:59:27.401 MQT: stat/StoveTasmota/RESULT = {"POWER":"ON"} <-- ESPHome
15:59:27.404 MQT: stat/StoveTasmota/POWER = ON
15:59:27.623 MQT: stat/StoveTasmota/RESULT = {"Fade":"ON"} <-- HA automation
15:59:27.638 MQT: stat/StoveTasmota/RESULT = {"Speed2":1}
15:59:27.651 MQT: stat/StoveTasmota/RESULT = {"POWER":"ON","Dimmer":100,"Color":"000000CC33","HSBColor":"0,87,0","White":100,"CT":222,"Channel":[0,0,0,80,20]}
sub 5 second turn_off time
This easily beats the Hue 8 second minimum as well as the Ecolink 5 second test mode. Plus the Ecolink is sloooow to trigger in comparison.
the ability capture near-still movement
This is achieve by a single mmWave sensor module. In the first build thread it was asked if two could be used in parallel. I was initially suspicious that they would interfere with each other as channel selection is not possible. Yet it seems they donāt. Assuming pointed in different directions as I have done.
The constraints
The first is obviously having to run a cable. But that is also true for the Aqara FP1 and it is yet to be determined its resilience to non-human micro-movements.
The second is heat. The -ISO version of the Olimex gets warm. I would be curious if the non-isolated version gets less so.
Whereās the yaml!
esphome:
name: poe-mmwave-pir
includes:
- uart_read_line_sensor.h
on_boot:
then:
# wifi interferes with HC-SR501 PIR generating false positives
- lambda: WiFi.mode(WIFI_OFF);
esp32:
board: esp32-poe-iso
framework:
type: arduino
web_server:
port: 80
version: 2
include_internal: true
ota: false
auth:
username: admin
password: !secret web_server_password
substitutions:
device_name: poe-mmwave-pir
stove: !secret stove_ip
kitchen: !secret kitchen_ip
kitchen2ch1: !secret kitchen2ch1_ip
kitchen2ch2: !secret kitchen2ch2_ip
http_request:
useragent: esphome/$device_name
timeout: 2s
# Enable logging
logger:
# Enable Home Assistant API
api:
reboot_timeout: 6h
services:
# Service to send a command directly to the display. Useful for testing
- service: send_command
variables:
cmd: string
then:
- uart.write: !lambda
char buf[128];
sprintf(buf, "%s\n", cmd.c_str());
std::string s = buf;
return std::vector<unsigned char>( s.begin(), s.end() );
mdns:
disabled: true
ota:
password: !secret another_one
ethernet:
type: LAN8720
mdc_pin: GPIO23
mdio_pin: GPIO18
clk_mode: GPIO17_OUT
phy_addr: 0
power_pin: GPIO12
manual_ip:
static_ip: !secret another_one
gateway: !secret another_one
subnet: 255.255.255.192
dns1: !secret another_one
dns2: !secret another_one
uart:
id: uart_bus
rx_pin: GPIO36
tx_pin: GPIO4
baud_rate: 115200
switch:
- platform: template
name: kitchen_lights
id: kitchen_lights
optimistic: true
internal: true
device_class: switch
turn_on_action:
then:
- http_request.get:
url: http://${stove}/cm?cmnd=POWER%20ON
- http_request.get:
url: http://${kitchen}/color/0?turn=on
- http_request.get:
url: http://${kitchen2ch1}/white/0?turn=on
- http_request.get:
url: http://${kitchen2ch2}/white/1?turn=on
turn_off_action:
then:
- http_request.get:
url: http://${kitchen2ch1}/white/0?turn=off
- http_request.get:
url: http://${kitchen2ch2}/white/1?turn=off
- http_request.get:
url: http://${kitchen}/color/0?turn=off
- http_request.get:
url: http://${stove}/cm?cmnd=POWER%20OFF
binary_sensor:
- platform: gpio
name: mmwave_detection_lower
id: mmwave_detection_lower
internal: true
pin:
number: GPIO16
mode: INPUT_PULLDOWN
filters:
- delayed_off: 2s
- platform: gpio
name: mmwave_detection_upper
id: mmwave_detection_upper
internal: true
pin:
number: GPIO15
mode: INPUT_PULLDOWN
filters:
- delayed_off: 2s
- platform: gpio
name: pir_detection
id: pir_detection
internal: true
pin:
number: GPIO14
mode: INPUT_PULLDOWN
filters:
- delayed_off: 2s
- platform: template
name: mmwave_pir_combined
id: mmwave_pir_combined
device_class: motion
filters:
- delayed_off: 2s
lambda: |-
if ( ( id(mmwave_detection_lower).state or id(mmwave_detection_upper).state ) and id(pir_detection).state) {
return true;
}
else if (id(mmwave_detection_lower).state == 0 and id(mmwave_detection_upper).state == 0 and id(pir_detection).state == 0) {
return false;
}
else {
return id(mmwave_pir_combined).state;
}
on_state:
- if:
condition:
- binary_sensor.is_on: mmwave_pir_combined
- binary_sensor.is_off: motion_light_override
- binary_sensor.is_off: kitchenswitch
then:
- switch.turn_on: kitchen_lights
- if:
condition:
- binary_sensor.is_off: mmwave_pir_combined
then:
- switch.template.publish:
id: kitchen_lights
state: OFF
- if:
condition:
- not:
api.connected
- binary_sensor.is_off: mmwave_pir_combined
then:
- switch.turn_off: kitchen_lights
- platform: homeassistant
id: kitchenswitch
entity_id: switch.kitchenswitch
- platform: homeassistant
id: motion_light_override
entity_id: input_boolean.motion_light_override
text_sensor:
- platform: custom
lambda: |-
auto my_custom_sensor = new UartReadLineSensor(id(uart_bus));
App.register_component(my_custom_sensor);
return {my_custom_sensor};
text_sensors:
id: "uart_readline"
internal: true
button:
- platform: restart
name: Restart $device_name
# #Notes
# sensorStop
# # range almost 1m
# detRangeCfg -1 0 9 # kitchen lower 9
# # 5 sec disappear
# outputLatency -1 0 200
# #save
# saveCfg 0x45670123 0xCDEF89AB 0x956128C6 0xDF54AC89
# #start
# sensorStart
[edit] - update lambda
My mmWave Projects
Tracking Radar
mmWave Presence Detection
Low-Latency DFRobot+PIR
mmWave Wars: one sensor (module) to rule them all