Job Mos

Motion activated smart home display

2 August 2026
SSD1306 oled display showing sensor values.

I recently ordered an ESP32 kit with a few sensors including a 1" SSD1306 OLED screen and a PIR (infrared) motion sensor. The extra ESP32 is useful when wanting to experiment but not break any existing projects.

PIR motion sensor module (left) and SSD1306 oled screen (right).

The OLED uses the I2C protocol for communication. The motion sensor uses a regular output pin that's LOW in idle and HIGH when motion is detected. The combination of these two components allow me to display any information (such as CO2, temperature, humidity, illuminance) on a screen that's activated by nearby motion. I only bought a kit because it had exactly the components I wanted but then for a cheaper price with a bunch of extras.

When buying the kit, I decided to buy a modern USB C powered soldering iron. Specifically, the FNIRSI HS-02A. It's a 100W iron that can comfortably be powered by a 65W laptop power supply. It has a little screen and a few buttons that let you adjust temperature and it has an auto-sleep mode that triggers when you lay the iron down. The truly mindboggling thing (to me) is the speed at which it heats up. Literally a few seconds and it's up to 100 °C. That's unimaginable compared to what I was used to; cheap generic soldering stations.

Going back to the project, the goal is twofold:

  1. Viewing sensor values without a phone.
  2. Fully self contained and portable (no need for WiFi)so I can take it anywhere.

I first tried connecting the ESP32 to the motion sensor and screen using a breadboard to make sure everything was in working order and most importantly to prove my idea would work.

Demonstration of the motion activated display, temporarily wired on a breaboard. The display flickering is a video artifact and isn't visible to the human eye.

As I have some minor regrets soldering my (previously) only ESP32 directly to perfboard, I wanted to experiment with header pin standoffs that allow removing and placing components without permanently soldering them to a board. The second reason for using standoffs is that the motion sensor doesn't fit directly on the perfboard because of its bulky bottom mounted components.

3-Pin and 4-pin female header standoffs.

The wiring is a bit of a hot mess, but it works. I'm glad I initially chose a relatively large perfboard as it's getting pretty crowded. I have a few more sensors I'd like to mount in the future. It's going to be tight.

Back side of the perfboard with some questionable wiring.

I decided to connect screen to its own I2C pins as I was afraid to break the existing sensor's connections. I wired up both components to 3.3 V initially but discovered the motion sensor produces unreliable results at 3.3 V. Therefore, I hooked them both up to 5 V instead.

Front side of the perfboard, showing all components.

Now for the software. This time around, I used the Home Assistant ESPHome Device Builder app which worked like a charm. You simply connect the ESP32 to the machine running Home Assistant. In my case, this is my server. When running Proxmox, give the Home Assistant VM access to the USB port. Then, open the builder app, select the ESP32 device type (I chose 'generic'), name it and you'll be directed to the configuration file editor. From here, create your desired configuration (see below) and click Upload.

# I2C pin definitions 
i2c:
  - id: bus_a
    sda: 21
    scl: 22
  - id: bus_b
    sda: 17
    scl: 16
   
# Motion sensor
binary_sensor:
  - platform: gpio
    pin:
      number: GPIO19
      mode: INPUT
    id: motion_sensor
    name: "Motion"
    device_class: motion
    on_press:
      - lambda: |-
          id(oled_display).turn_on();
    on_release:
      - delay: 30s
      - lambda: |-
          id(oled_display).turn_off();

# CO2, temperature, humidity and light sensors
sensor:
  - platform: scd30
    co2:
      name: "CO2"
      id: co2
      accuracy_decimals: 1
    temperature:
      name: "Temperature"
      id: temperature
      accuracy_decimals: 2
    humidity:
      name: "Humidity"
      id: humidity
      accuracy_decimals: 1
    address: 0x61
    i2c_id: bus_a
    update_interval: 30s
  - platform: bh1750
    name: "Illuminance"
    id: illuminance
    address: 0x23
    i2c_id: bus_a
    update_interval: 60s

# Display
font:
  - file: "gfonts://Roboto"
    id: my_font
    size: 12

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    id: oled_display
    address: 0x3C
    i2c_id: bus_b
    update_interval: 30s
    lambda: |-
      it.printf(64, 0, id(my_font), TextAlign::TOP_CENTER, "Sensor Values");
      it.line(0, 14, 128, 14);
      it.printf(0, 20, id(my_font), "CO2: %.0f ppm", id(co2).state);
      it.printf(0, 34, id(my_font), "Temp: %.1f C", id(temperature).state);
      it.printf(0, 48, id(my_font), "Hum: %.1f %%  Lux: %.0f", id(humidity).state, id(illuminance).state);

This works great except for when the motion sensor is too close to your WiFi router. My server is positioned right below a WiFi router, and placing the motion sensor right next to these devices causes erratic and unreliable motion sensor behaviour. Moving the sensor board a few meters away solves this issue.

The motion sensor's sensitivity can be tweaked using the dials on the side of the module using a Phillips screw driver. I adjust the sensitivity on mine all the way to low, which is equivalent to a sensitivity up to 3 meters instead of 7 meters.

Dials on the PIR motion sensor. Sensitivity on the left and time on the right.