In a proper smart home it should not be required to touch light switches, adjust the heating or think after a long home office day how much time you actually spent at your desk.
Today we'll have a look on how to solve this with the Infineon Radar Shield2Go by seamlessly integrating it in our smart home setup with MicroPython on a PSoC6 microcontroller board.
Why Radar?Well, of course I tried an infrared-based solution, because there are plenty out there and they are affordable. It is just not the right use case for it - infrared motion sensors need a certain motion threshold to trigger - and since I don't move a lot when I work at my desk, my light regularily turns off while I work. A typical infrared sensor pattern of me working at my desk (with yellow for detected motion) looks like this:
Currently, I solved this by bridging the gaps and delaying the light turn off action - but that's not really a sustainable solution and as a geek I want to see my light turning off immediately when I leave my office.
Different than the BGT60UTR11AIP radar sensor (used in another radar project), the Infineon BGT60LTR11AiP which we will use is not a real presence detector. It's a doppler radar without frequency modulation. But it has a huge advantage compared to the other models: it can be red out with only 2 GPIOs, stating target detection and direction. So there's no complicated radar data processing required!
Will this be enough to solve the desk presence problem? I did some testing with the sensor and when it's on full sensitivity it was nearly impossible for me to sit still enough to not trigger the sensor. So my prediction is yes!
Enough about the background, let's dive into the project.
Hardware ConnectionLet's start with some basic hardware connections to get this out of the way. I am using the BGT60LTR11AiP Shield2Go, which has all important pins broken out and two on-board potis to adjust threshold and hold-time of the internal target detector steplessly.
Next to the radar Shield2Go I use the PSoC6 Prototyping Kit. Why this one? Cause it's enabled for MicroPython!
I have a huge Home Assistant setup with >1000 entities running on a Raspberry Pi 4. Leading you through this whole setup would be too much for this article. However, you'll need to have a certain minimum Home Assistant setup running in order to get the MQTT Autodiscovery feature we'll be using working.
Here some useful links to get everything working:
Home Assistant installation: https://www.home-assistant.io/installation/
MQTT integration: https://www.home-assistant.io/integrations/mqtt/
That's it! If you have Home Assistant, a MQTT broker and the MQTT integration running, you're ready to continue.
MicroPythonNow, let's get MicroPython running on the PSoC6 board, this is also a prerequisite for this project. Fortunately, this can be done in less than 5 minutes with this tutorial.
Some more Prerequisites: Libraries!Thanks to the great community out there, we don't need to write our MQTT library for MicroPython from scratch and can use umqtt.simple, which can be installed directly from the Thonny IDE:
Now the interesting part - programming our custom smart home device in MicroPython! With seamless integration into Home Assistant (and therewith HomeKit, Google Home, Matter, etc.)!
Connecting to WiFi and MQTTThis is a quite default thing to do and not really scope of this article to cover it in detail, so please check the linked repository for the full code which covers connection to WiFi and MQTT also.
Mastering the AutodiscoveryI love this feature of Home Assistant - the complete configuration of our smart home device happens on the client, and it will automatically appear as new device with the configured entities in Home Assistant - a maintenance dream.
For this we just need to send the right config in json format via MQTT to Home Assistant. You can find all details on how to set up the configuration here: https://www.home-assistant.io/integrations/mqtt/#mqtt-discovery
It took me quite a while to get all the configuration right, so let's save us some time:
We start with the device properties. At first, I create a unique ID for my client device, so I can differentiate then later when I add multiple motion sensors (and I will for sure):
import ubinascii
# Generate unique client ID
client_id = ubinascii.hexlify(machine.unique_id()).decode()
# Short ID to two characters for readability
cid = client_id[-2:]
node_id = f'motion_sensor_{cid}'
Now we bring all information into the right format for the Home Assistant device discovery:
discovery_device = {
"name":f"Motion Sensor {cid}",
"identifiers": node_id,
"manufacturer": 'Infineon',
"model": 'PSoC6',
"sw_version": '0.1.0'
}
Once sent to the discovery topic, this will create a device in Home Assistant which is called Motion Sensor <id>. We need this in order to create our entities, which need to be assigned to a device. One device can have multiple entities. For an air quality sensor these could be for example temperature, humidity, CO2, etc. We create two entities: one for motion detection (target detection = td) and one for direction detection (phase detection = pd):
discovery_prefix = 'homeassistant'
discovery_entities = {
'td':{
'discovery_topic':f'{discovery_prefix}/binary_sensor/{node_id}',
'conf': {
"name":"Target Detected",
"unique_id":"td",
"state_topic":f'{discovery_prefix}/binary_sensor/{node_id}/state/td',
"device": discovery_device,
"device_class":"motion"
}
},
'pd':{
'discovery_topic':f'{discovery_prefix}/binary_sensor/{node_id}',
'conf': {
"name":"Target Approaching",
"unique_id":"pd",
"state_topic":f'{discovery_prefix}/binary_sensor/{node_id}/state/pd',
"device": discovery_device,
"availability_topic":f'{discovery_prefix}/binary_sensor/{node_id}/avl/pd',
"icon":"mdi:ray-end-arrow"
}
}
}
You can see, we use different configuration fields depending on the entities. Both entities will be added as binary sensors, but for TD, the motion detector, we set a device class. For PD, alias "target approaching" there is no predefined device class available, so we define a custom icon instead. PD can only be determined when there is a target detected, so we also give it an availability topic which we can use to let Home Assistant know, if the entity is even available. This adds a third state unavailable to the "binary" sensor.
Fortunately, the Home Assistant docs also use a motion sensor for their example, so you can find some more explanation here.
Now, let's MQTT-publish this configuration to the Home Assistant server:
import json
# Publish discovery message for Home Assistant
for entity in discovery_entities.values():
client.publish(f'{entity["discovery_topic"]}/{entity["conf"]["unique_id"]}/config', json.dumps(entity['conf']))
Now, look at your Home Assistant Settings -> Entities & Devices -> Devices and look for the Motion Sensor, it should be there already.
Yours will now still show both entities in grey. We gave Home Assistant the information that these entities exist but no information on their state. This is what we will do now.
Sensor IntegrationLike mentioned above, the BGT60LTR11AiP radar sensor is super easy to integrate. We just need to read out two GPIOs and send the respective state updates to Home Assistant. I wrote two small classes for this. One defines a generic binary sensor, based on a GPIO:
class BinarySensor:
def __init__(self, entity, pin, invert=False, pull=None):
self._entity = entity
self._pin = machine.Pin(pin, machine.Pin.IN, pull)
self._invert = invert
self._isActive = False
self._isAvailable = False
def read(self):
return (self._pin.value() == 1) != self._invert
def event(self):
if self.read() and not self._isActive:
self._isActive = True
return 'ON'
if not self.read() and self._isActive:
self._isActive = False
return 'OFF'
return False
def available(self, available):
if available and not self._isAvailable:
self._isAvailable = True
available = 'online'
elif not available and self._isAvailable:
self._isAvailable = False
available = 'offline'
else:
return
client.publish(discovery_entities[self._entity]['conf']['availability_topic'], available)
def update(self):
e = self.event()
if e: client.publish(discovery_entities[self._entity]['conf']['state_topic'], e)
The second class uses the first one to create a radar sensor interface out of two binary sensors. We need this class because the state of PD depends on TD, so there is a relation between the two binary sensors. This is reflected here:
class RadarSensor:
def __init__(self, pinTD, pinPD):
self.targetDet = BinarySensor('td', 'P6_5', invert=True, pull=machine.Pin.PULL_DOWN)
self.phaseDet = BinarySensor('pd', 'P6_4', invert=False, pull=machine.Pin.PULL_DOWN)
def update(self):
self.targetDet.update()
if self.targetDet.read():
self.phaseDet.available(True)
self.phaseDet.update()
else:
self.phaseDet.available(False)
Now, in the code we only need to use these prewritten classes. For initialization:
radarSensor = RadarSensor('P9_0', 'P6_5')
And then in a while(1) loop (together with whatever else you'd like to handle):
radarSensor.update()
One more thing: Mosquitto >= 2.0I use Mosquitto 2.x as MQTT broker in Home Assistant. Since version 2.0 the client has to regularly ping the broker to not get disconnected. When I last checked I still had to do this manually, that's why I added this to my code.
Before the while(1) loop:
import time
### Ping interval for Mosquitto >= 2.0
ping_interval = 60
last_ping = 0
And in the loop:
### Server ping (required for Mosquitto >= 2.0!)
now = time.time()
if (now - last_ping) > ping_interval:
client.ping()
last_ping = now
A lot of code fragments now, but you don't have to copy/paste this together manually - you can find the whole project on GitHub.
CosmeticsOf course a nice smart home sensor needs a housing, so I created some basic printable parts which you can find in the attachments of this article.
I also tried a closed version of the housing since radar can shoot through plastic. In principle it works too, but with increased sensitivity I got some interference with the on-board LEDs. So far I didn't want to desolder them, so I decided for this open housing for now.
SummaryNow, was this worth the effort? Looking at the results, I would say yes! While the infrared motion sensor would have turned off the light more than 10 times in 45 minutes, the radar sensor kept recognizing me:
I am quite happy with the result and will surely add more of these motion sensors to my smart home.
Now, one last Home Assistant trick I would like to share with you: I mentioned in the beginning of this article that I keep forgetting about the exact time I spend at my desk. But this can be important, especially if someone pays you for it. You can use the device we just built to track your desk time.
Make sure you have the history integration installed in Home Assistant and create a custom sensor in the configuration.yaml like this:
sensor:
- platform: history_stats
name: Office Time Today
entity_id: binary_sensor.motion_sensor_<id>_target_detected
state: "on"
type: time
start: "{{ now().replace(hour=0, minute=0, second=0) }}"
end: "{{ now() }}"
Restart Home Assistant and you will find an entity which helps you tracking your daily working time:
Let me know in the comments how you are using motion and presence sensors in your smart home!
Comments