In this article you will learn how to integrate the XENSIV™ Sensor Shield into Home Assistant using the open-source ESPHome framework, to make your own custom "sensor station". We will use the Raspberry Pi Pico W as our edge device managing the XENSIV™ Sensor Shield.
The XENSIV™ Sensor ShieldFirst off, some information about the hardware we are going to be working with:
The XENSIV™ Sensor Shield is a collection of environment sensors on an Arduino UNO Shield form-factor board, making it compatible with many microcontroller kits.
It contains the following components:
- XENSIV™ PAS CO₂ sensor (PASCO2V15)
- XENSIV™ digital barometric pressure sensor with built-in temperature sensor (DPS368)
- XENSIV™ 60GHz Radar sensor (BGT60LTR11AIP)
- Digital humidity and temperature sensor (SHT35)
- Six-axis IMU (BMI270)
- SPI-based TFT display (ST7735)
- *Three-axis magnetometer sensor (BMM350)
- *2x XENSIV™ MEMS digital microphones (IM72D128)
- *OPTIGA™ Trust M security controller (SLS32AIA010MK)
(* we won't use these components in this guide)
You will find all necessary technical documents about the XENSIV™ Sensor Shield like the datasheet on the product page if you want more in-depth information.
Preparing the hardwareTo run the ESPHome firmware we need a compatible microcontroller. We chose the supported RP2040 platform which can be found on the Raspberry Pi Pico W. To connect both boards, a fitting RPi Pico to Uno FlexyPin Adapter by arturo182 will help us to utilize the benefit of the Arduino shield form-factor layout of the XENSIV™ Sensor Shield.
First we have to solder on the socket headers on the adapter board, as well as solder pin headers to the Pico W in case it didn't come with them. Now we can easily attach the XENSIV™ Sensor Shield to the Pico W by stacking them.
Make sure to set the interrupt switch configuration SW2 such that switch 5 is ON, as seen in the following image. This enables the interrupt signal from the CO₂ sensor.
For this demonstration we power the system via the Micro USB port on the Raspberry Pi Pico, since we won't draw too much current. If you want to use power hungry sensors like external radars, it's recommended to add an external power source as the Micro USB port may not deliver sufficient current. Refer to the official documentation for more information.
Window Mounting (optional)
We want to go further and attach the device to the window using suction cups so we can also detect whether the window is opened or closed. To attach suction cups to the adapter board we 3D-print an adapter plate. The step file is provided in the resources. The assembly is pretty simple:
Home Assistant (HA) is a very popular, self-hosted, free and open-source smart home platform. For this tutorial we assume you already have HA set up. If you need instructions on how to install Home Assistant on a Raspberry Pi (our recommendation), please refer to the official installation guide.
Adding ESPHomeTo add these components to Home Assistant we need the open-source ESPHome integration. We will refer you to this page to add the integration. Click on the "Add Integration to my Home Assistant" button to automatically get transferred to your Home Assistant instance.
Flashing ESPHome firmwareIn the next step, we need to connect the Pico W to the computer with a USB Micro cable once to flash the ESPHome firmware. Later, any updates can be flashed wirelessly over the air (OTA updates).
To do that, you need to go to https://web.esphome.io/?pico= and click on "First-time setup". This should prompt a list of instructions to get the firmware onto the Pico W. Follow these instructions until your device is successfully connected to the same network as your Home Assistant device (you will need to enter the network password).
Once that is done, the new device should be discovered by the ESPHome Add-On in the HA panel. "TAKE CONTROL" and update the new device.
This section is where you can enjoy your freedom by making your own YAML configurations, customizing what the edge device should be doing. To understand the format better, you should read the official documentation.
This is what the initial configuration may look like:
esphome:
name: esphome-web-13ddd5
friendly_name: ESPHome Web 13ddd5
min_version: 2025.11.0
name_add_mac_suffix: false
rp2040:
board: rpipicow
# Enable logging
logger:
level: DEBUG
# Enable Home Assistant API
api:
# Allow Over-The-Air updates
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Your custom config goes here:This is just boilerplate code and should remain unchanged to avoid problems, unless you know what you are doing. The YAML configuration works with components. There are many components for a lot of sensors integrated into ESPHome. A full documentation for all the components can be found here.
External Dependencies
Since some XENSIV™ features haven't been officially added to the ESPHome repository yet, we need to link to our external GitHub repository. This is how we include the XENSIV™ PAS CO₂ sensor, the XENSIV™ DPS368 pressure sensor as well as the Six-axis IMU:
external_components:
- source: github://michal-gora/esphome@xensiv_sensor_shield
components: [ xensiv_pas_co2_base, xensiv_pas_co2_i2c, xensiv_dps3xx_base, xensiv_dps3xx_i2c, bmi270 ]Alternatively, you could download our fork at GitHub and run the ESPHome utility locally on your computer. For the sake of simplicity we decided to show the instructions with the external component.
Communication Interfaces
To use the sensors on the XENSIV™ Sensor Shield we need to configure the communication interfaces. This includes setting up the i2c component to communicate with sensors via I²C and the spi component for SPI devices.
The I²C interface needs to use pins GPIO20 and GPIO21 for SDA and SCL, respectively:
i2c:
sda: GPIO20
scl: GPIO21We configure the SPI interface to use GPIO18, GPIO19 and GPIO16 for CLK, MOSI and MISO:
spi:
clk_pin: GPIO18
mosi_pin: GPIO19
miso_pin: GPIO16XENSIV™ PAS CO₂ Sensor
Next, we add the CO₂ sensor. The Sensor Shield uses pin GPIO09 for the interrupt signal of the PAS CO₂ while the rest of the communication runs via I²C. In addition to the sole PAS CO₂ sensor module, the XENSIV™ Sensor Shield uses pin GPIO13 to control the 5V operation power delivery to the sensor. Therefore, we need to configure the pin as a pull-up by setting it to HIGH. We added an additional setting power_pin for that. Here is an example of this configuration:
sensor:
- platform: xensiv_pas_co2_i2c
id: pasco2
co2:
name: "CO2"
id: co2_sensor
interrupt_pin: GPIO09
sensor_rate: 1min
operation_mode: continuous
pressure_compensation_source: pressure_sensor
power_pin: GPIO13 # Enable PAS CO2 5V Power (GPIO13 HIGH)XENSIV™ DPS368 Pressure Sensor
We can add the DPS368 sensor as a second sensor. The default I²C address is 0x77, however, since the SDO pin on this board is pulled down, we have to adjust to the address 0x76.
The pressure data from this sensor can be used to improve accuracy of the XENSIV™ PAS CO₂ sensor. To take advantage of this, we can reference the pressure sensor in the CO₂ sensor with the pressure_compensation_source parameter by passing the id of the pressure sensor. Altogether we get this configuration:
sensor:
# ... previous sensors ...
- platform: xensiv_dps3xx_i2c
pressure:
id: dps3xx_pressure
name: "DPS3XX Pressure"
temperature:
name: "DPS3XX Temperature"
operation_mode: continuous
update_interval: 10s
address: 0x76XENSIV™ 60GHz RadarSensor
For motion detection we can use the radar sensor. We read Target Detection (TDET) on pin GPIO06 and use the reading from Phase Detection (PDET) on pin GPIO07 to determine the direction of the detected target. PDET is set to HIGH for approaching targets and otherwise LOW. TDET is set to LOW if a target was detected.
Note: The LEDs on the board show the inverted values of TDET and PDET. We also invert the parametersin the YAML configuration for intuition.
binary_sensor:
# ... previous sensors ...
# Radar TDET
- platform: gpio
pin:
number: GPIO06 # TDET pin
mode: INPUT
inverted: true
name: "Radar TDET"
id: radar_sensor_tdet
# Radar PDET
- platform: gpio
pin:
number: GPIO07 # PDET pin
mode: INPUT
inverted: true
name: "Radar PDET"
id: radar_sensor_pdetHumidity Sensor
Next up is the environment sensor to measure humidity and temperature. We use the sht3xd sensor component for it:
sensor:
# ... previous sensors ...
- platform: sht3xd
temperature:
name: "SHT3X-D Temperature"
id: sht_temperature_sensor
humidity:
name: "SHT3X-D Humidity"
id: humidity_sensor
update_interval: 10sIMU Sensor
To get the IMU data from the sensor, we use our custom updated component for the bmi270, since the sensor is not backwards compatible with the bmi170 component, which already exists in ESPHome.
sensor:
# ... previous sensors ...
- platform: bmi270
address: 0x69
accel_x:
name: "BMI270 Accel X"
id: accel_x
accel_y:
name: "BMI270 Accel Y"
id: accel_y
accel_z:
name: "BMI270 Accel Z"
id: accel_z
gyro_x:
name: "BMI270 Gyro X"
gyro_y:
name: "BMI270 Gyro Y"
gyro_z:
name: "BMI270 Gyro Z"
update_interval: 5s
temperature:
name: "BMI270 Temperature"The acceleration values are presented in m/s² and the gyroscope values are in the unit of °/s. To get the orientation of the device, we have to consider, that the earth's gravitation accelerates objects with 9.81 m/s² towards the center of the planet. With this information we could for example determine the sensors downward direction. We will make an example in a later section of this blog.
TFT Display
Finally we can display our sensor values on the 80x160 TFT display. We provide the component configuration for a landscape oriented use of the display as seen in the images.
Make sure to use the same configuration as we do to get the best results:
# TFT Display
display:
- platform: st7735
model: "INITR_MINI160X80"
reset_pin: GPIO04
cs_pin: GPIO17
dc_pin: GPIO26
rotation: 90
device_width: 80
device_height: 160
col_start: 26 # Adjust to hide noisy columns on the edges
row_start: 1 # Adjust to hide the noisy row on the edge
update_interval: 0s
invert_colors: True
use_bgr: True
lambda: |-
// Your C code for displaying dataRefer to the documentation to learn how to print to the screen using the display component. However, if you simply want to follow our design, this is the code we used:
lambda: |-
double co2ppm = id(co2_sensor).state;
double humidity = id(humidity_sensor).state;
double pressure = id(pressure_sensor).state;
double temperature = (id(dps_temperature_sensor).state + id(sht_temperature_sensor).state) / 2.0;
auto background_color = Color(0, 0, 0);
auto co2_color = Color(255, 128, 128);
auto humidity_color = Color(255, 128, 255);
auto pressure_color = Color(128, 255, 255);
auto temperature_color = Color(255, 255, 0);
auto tdet_color = Color(255, 0, 0);
auto pdet_color = Color(0, 255, 0);
// Background
it.fill(background_color);
// Sensor values
if(id(co2_sensor).has_state()) it.printf(80, 2, id(my_font), co2_color, TextAlign::TOP_RIGHT, "%.1f ", co2ppm);
if(id(humidity_sensor).has_state()) it.printf(80, 22, id(my_font), humidity_color, TextAlign::TOP_RIGHT, "%.1f ", humidity);
if(id(pressure_sensor).has_state()) it.printf(80, 42, id(my_font), pressure_color, TextAlign::TOP_RIGHT, "%.1f ", pressure);
if(id(dps_temperature_sensor).has_state() && id(sht_temperature_sensor).has_state()) it.printf(80, 62, id(my_font), temperature_color, TextAlign::TOP_RIGHT, "%.1f ", temperature);
if(id(radar_sensor_tdet).has_state() && id(radar_sensor_tdet).state){
auto motion_color = id(radar_sensor_pdet).has_state() && id(radar_sensor_pdet).state ? pdet_color : tdet_color;
it.printf(125, 28, id(emoji_font), motion_color, TextAlign::TOP_LEFT, "👋");
}
// Sensor units
it.printf(80, 2, id(my_font), co2_color, TextAlign::TOP_LEFT, "ppm");
it.printf(80, 22, id(my_font), humidity_color, TextAlign::TOP_LEFT, "%%");
it.printf(80, 42, id(my_font), pressure_color, TextAlign::TOP_LEFT, "hPa");
it.printf(80, 62, id(my_font), temperature_color, TextAlign::TOP_LEFT, "°C");Feel free to modify parameters to your liking, like for example the update_interval or set the operation_mode to single_shot mode in the xensiv_pas_co2_i2c sensor component, if you want to trigger measurements manually.
For example, to trigger a CO₂ measurement, we can add a button component, which triggers the measure_now function of the component. Add this to the YAML configuration:
button:
- platform: template
name: "Measure CO2 Now"
on_press:
- lambda: |-
id(pasco2).measure_now();Other examples include doing something creative with the sensor data, like evaluating the IMU data to detect if the window is opened (we refer to European tilted windows here) if we attach the device to the window.
To create an "opened-window" detector we can check if the acceleration in the z‑axis passes a threshold. The right threshold should be between 0 and 9.81 and should be adjusted for each window to get the right sensitivity.
binary_sensor:
#... previous sensors ...
# Window open/closed detection using accelerometer Z-axis
- platform: template
name: "Window Open"
id: window_open
device_class: window
lambda: |-
float threshold = -0.5;
float z = id(accel_z).state;
// Window closed: z ≈ 0.0 (gravity), Window open: z drops when tilted backwards
return z < threshold; // Adjust threshold based on testingVisualizationAfter we are satisfied with our YAML configuration, we can save it and update our Pi Pico. This time we can do it wirelessly with an over-the-air (OTA) update. Click on INSTALL and choose Wirelessly. The new configuration should compile and be uploaded to the device automatically.
Note: Remember that both the Home Assistant instance and the edge device need to be connected to the same network.
There are multiple ways to display the sensor data in Home Assistant. Since this is a question of customization, we won't get into detail here. But if you are new to Home Assistant here are useful instructions on how to get your sensor data onto your dashboard. We simply added the sensors as an "entities card" to the Overview tab. Here is our result:
We set up ESPHome in Home Assistant, deployed the firmware on our Raspberry Pi Pico and assembled the XENSIV™ Sensor Shield. We also looked at the YAML configurations for the various sensors on the board.
Thanks for reading! If you had fun feel free to like, comment and share the projects you create with this guide!






_OFHpVQ1StL.jpg)







Comments