By Stephan Wijman

Introduction

While looking for some ESPHome project I could use myself I found one where they made a sensor to monitor the occupancy of the bed. I thought this is a helpful implementation to assist with my Home Automation.
At the moment I use my motion detector in the bedroom to know if somebody is there (to turn on the light) but also as a trigger for bedtime, where the alarm is set and all remaining lights are turned off. Unfortunately this isn't fully reliable since if somebody late at night does go to the bedroom, my automation thinks somebody is going to bed and turns the alarm on and all the lights off.
To make my automation better I thought to also create a bed occupancy sensor, but have made some slight modifications on the ones I have seen online, to fit better with my design.
Below are the materials I used (for a double bed, so 2 sensors) and how I implemented it into ESPHome and Home Assistant. Please feel free to use what ever bit is useful.

Material used

For this little project I have used the following materials:

esphome-bed-occupancy-sensor-01

I use the M5Stamp Pico since it fits the requirements (2x ADC input) and the wifi signal is able to reach my network. You can use other ESP32/ESP8266 if that fits your project better, for example an ESP with external wifi antenna for better signal.

Initial programming of the M5Stamp-Pico

To get the M5Stamp-Pico talking to ESPHome we have to create a new device. I created with board type pico32 and called it 'bed-occupancy-master' (since this goes in the master bedroom, and if required I can then create additional ones for the other rooms). I then put in the following code as part of the esphome section:

  on_boot:
    - priority: 800.0 # after power up and hardware initialization 
      then:
        - light.turn_on:
            id: builtin_led
            brightness: 100%
            red: 100%
            green: 0%
            blue: 0%
    - priority: 300.0 # after sensor setup, before WIFI initialization 
      then:
        - light.turn_on:
            id: builtin_led
            brightness: 100%
            red: 100%
            green: 100%
            blue: 0%
    - priority: 225.0 # after WIFI initialization, no connection
      then:
        - light.turn_on:
            id: builtin_led
            brightness: 100%
            red: 0%
            green: 0%
            blue: 100%
        - delay: '00:00:05'    
    - priority: -100.0 # everything is initialized, system is online - if condition waits for connection
      then:
        if:
          condition:
            wifi.connected:
          then:
            - light.turn_on: # indication of wifi connection confirmation
                id: builtin_led
                brightness: 100%
                red: 0%
                green: 100%
                blue: 0%
            - delay: '00:00:05'
            - light.turn_off: # turns off light waiting on automation to start
                id: builtin_led

This piece of code will use the interal RGB LED to show the status as follows:

  • RED - Hardware initialization
  • YELLOW - Most sensors are set up
  • BLUE - Connecting to wifi
  • GREEN (5s) - Connected to wifi

The LED will be green for 5 seconds before turning off.

Then at the bottom of the script (after captive_portal:) add the following code to create the required sensor entities:

light:
  # Create internal LED object for status
  - platform: neopixelbus
    type: GRB
    variant: SK6812
    pin: 27
    num_leds: 1
    id: "builtin_led"
    name: "Built-in LED"
    internal: true
    method:
      type: esp32_rmt
      channel: 0

binary_sensor:
  # Binary sensor for status of device
  - platform: status
    name: "Bed occupancy Master Status"
  # Binary sensor for left side
  - platform: analog_threshold
    name: "Bed occupancy Master - Left"
    sensor_id: bed_occupancy_master_left_value
    threshold: 0.4
  # Binary sensor for right side
  - platform: analog_threshold
    name: "Bed occupancy Master - Right"
    sensor_id: bed_occupancy_master_right_value
    threshold: 0.4

sensor:
  # ADC sensor for left TFP sensor
  - platform: adc
    pin: 32
    attenuation: 0db
    name: "Bed occupancy Master - Left Value"
    id: bed_occupancy_master_left_value
    update_interval: 60s
    internal: true # Default not exposed to front-end
    raw: true
    filters:
      - multiply: 0.00026862 # 1.1/4095, for attenuation 0db
  # ADC sensor for right TFP sensor
  - platform: adc
    pin: 33
    attenuation: 0db
    name: "Bed occupancy Master - Right Value"
    id: bed_occupancy_master_right_value
    update_interval: 60s
    internal: true # Default not exposed to front-end
    raw: true
    filters:
      - multiply: 0.00026862 # 1.1/4095, for attenuation 0db

NOTE: The 2 ADC sensors are not exposed to the front-end by default. If this is required for testing/calibration purposed change the internal value to false.

Here is the full YAML config I am using (Sensitive info marked with [REMOVED]):

esphome:
  name: "bed-occupancy-master"
  friendly_name: "Bed Occupancy (Master Bedroom)"
  on_boot:
    - priority: 800.0 # after power up and hardware initialization 
      then:
        - light.turn_on:
            id: bed_occupancy_master_builtin_led
            brightness: 100%
            red: 100%
            green: 0%
            blue: 0%
    - priority: 300.0 # after sensor setup, before WIFI initialization 
      then:
        - light.turn_on:
            id: bed_occupancy_master_builtin_led
            brightness: 100%
            red: 100%
            green: 100%
            blue: 0%
    - priority: 225.0 # after WIFI initialization, no connection
      then:
        - light.turn_on:
            id: bed_occupancy_master_builtin_led
            brightness: 100%
            red: 0%
            green: 0%
            blue: 100%
        - delay: '00:00:05'    
    - priority: -100.0 # everything is initialized, system is online - if condition waits for connection
      then:
        if:
          condition:
            wifi.connected:
          then:
            - light.turn_on: # indication of wifi connection confirmation
                id: bed_occupancy_master_builtin_led
                brightness: 100%
                red: 0%
                green: 100%
                blue: 0%
            - delay: '00:00:05'
            - light.turn_off: # turns off light waiting on automation to start
                id: bed_occupancy_master_builtin_led

esp32:
  board: pico32
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "[REMOVED]"

ota:
  password: "[REMOVED]"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: true

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Bed Occupancy Fallback Hotspot"
    password: "[REMOVED]"

captive_portal:

light:
  # Note: the built-in LED doesn't seem to behave correctly, and color control is flaky...
  - platform: neopixelbus
    type: GRB
    variant: SK6812
    pin: 27
    num_leds: 1
    id: "bed_occupancy_master_builtin_led"
    name: "Bed occupancy Master Built-in LED"
    internal: true # Default not exposed to front-end
    method:
      type: esp32_rmt
      channel: 0

binary_sensor:
  # Sensor status entity
  - platform: status
    name: "Bed occupancy Master Status"
  - platform: analog_threshold
  # Sensor left side entity
    name: "Bed occupancy Master - Left"
    sensor_id: bed_occupancy_master_left_value
    threshold: 0.3
  # Sensor right side entity
  - platform: analog_threshold
    name: "Bed occupancy Master - Right"
    sensor_id: bed_occupancy_master_right_value
    threshold: 0.3

sensor:
  - platform: adc
    pin: 32
    attenuation: 0db
    name: "Bed occupancy Master - Right Value"
    id: bed_occupancy_master_right_value
    update_interval: 60s
    internal: false # Default not exposed to front-end
    raw: true
    filters:
      - multiply: 0.00026862 # 1.1/4095, for attenuation 0db
  - platform: adc
    pin: 33
    attenuation: 0db
    name: "Bed occupancy Master - Left Value"
    id: bed_occupancy_master_left_value
    update_interval: 60s
    internal: false # Default not exposed to front-end
    raw: true
    filters:
      - multiply: 0.00026862 # 1.1/4095, for attenuation 0db

Once the device has been created, download the bin image file to upload to the M5Stamp-Pico over USB.

Connect the M5Stamp-Pico to the ESP32 programmer. Then use an Web Serial API enabled browser (Edge or Chrome) and use the https://web.esphome.io dashboard to upload the bin file to the M5Stamp-Pico.

Once the M5Stamp-Pico connects to ESPHome it will create the following entities on Home Assistant:
esphome-bed-occupancy-sensor-02

Test setup

As you can see in the picture below, the pins on the TFP sensor are delicate, so I already soldered the cable to it, and used heat-shrink tubing to avoid damaging it.

Now I can create the setup on a normal breadboard to see if it works as expected. I use the following design:
esphome-bed-occupancy-sensor-05

Pin 19 (3.3V) goes to both the 2 TFP sensors, the other end is connected to pin 12 (GPIO32) and Pin 13 (GPIO33) and both 5.1K Ohm resistors. The resistors are then connected to the ground.
Pin 8 (5V) and pin 9 (GND) are connected to the power from the USB cable.

Below is how my breadboard setup looks like for testing 1 TFP sensor. Since the 2nd TFP sensor is identical, but different pin, I don't need to test both at the same time.
esphome-bed-occupancy-sensor-06

Once I have confirmed this is working as expected I installed it under the mattrass on one of the beams, I can test the values out. Initially the value is 0.0V, and once I sit on the bed it went up to 0.6V. So for now I use 0.4V as my threshold. I will now try it out overnight to see how it works.

The sensor seems to work as expected, and the binary-sensor was on for the whole night. Below is the graph of the ADC value overnight.
esphome-bed-occupancy-sensor-07

As you can see it isn't consistant with knowing I am in bed, since the threshold was 0.4 and it went below that several times. I will therefore lower the threshold to 0.2.
esphome-bed-occupancy-sensor-08

Now that I am happy with the setup, I can transfer it to the breadboard PCB and solder everything in place.

Final build

Now that I am happy with the setup, I can create the final build on the breadboard PCB, and put it in the box for protection.

As it is properly installed it is time to create automations for when we go to bed, during the night if we need to go to the bathroom, and when we get up in the morning.

I hope you like how I create my little project, and if you have any feedback or comment then please let me know below.

Stephan Wijman • 33 Articles

View Articles