ESPhome Automation Particulate Matter Sensor update_interval

Hi I am upgrading my Ikea PM1006 Particulate Matter Sensor.

I connected it so i directly read the values. Following the manual it does a update every 20 seconds. The sensor also has a seperated FAN. I want to turn on this fan when the when it is updating the value. If i read correct, i should use an automation for this. But i can’t seem to figure out how to do this. Who can help. Below the components in my config which i would like to like to eachother.

switch:
  - platform: gpio
    pin: GPIO25
    name: "Fan"


uart:
  rx_pin: D2
  tx_pin: D1
  baud_rate: 9600

sensor:
  - platform: pm1006
    pm_2_5:
      name: "Particulate Matter 2.5µm Concentration"
    update_interval: 20s

Manual pm1006

I dont see anything about it having a fan in the manual. You’re a liar!!!
Lol I’m Jk. Is it a 2 wire fan or 3 wires? I was looking for it in the manual to see if its 5v or can use 3.3v, how much current it uses and are you sure its not PWM too? These are meant to be used with esp/microcontroller’s right? It should be ok then.

Is the goal to turn the fan on and cool it right before a reading or is it to cool it after a reading?

Do you need a logic level converter too? Looks like the signal Out is 4.5v.

thanks for your reply.

i use somewhat this drawing to power the fan (but without the resitor and diode)

the pm2.5 meter and the fan are separated and was contolled from the Ikea MCU. But i want to replace the mcu with an ESP 32 D1.


With the ikea MCU the fan only turned on when it was taking samples. I would like to do the same with the ESP32. Hope it makes sense.

PS: Both work on 5 volt

So, you dont have a current limiting resistor on the base of the transistor? Thats bad bad news, youll keep burning it out or worse, damage your esp board. The diode is something you need too.

thanks, i will add them. Is it simple to explain why ?

Also still looking for an way to turn on the fan when the pm2.5 is sampling evenry 20 seconds

Ok so, theres a few ways you could do this. This is how i would do it. First, set up Time. You can use HA as your time source.
https://esphome.io/components/time/homeassistant.html?highlight=time

Then just look at the examples they give you for how to use Time.
Screenshot from 2024-01-28 12-18-37

time:
  - platform: homeassistant
    id: time_source
    on_time:
      # Every 15 Seconds
      - seconds: /15
        minutes: 
        then:
          - switch.turn_on: Fan
          - delay: 5s
          - component.update: Particulate Matter 2.5µm Concentration
          - switch.turn_off: Fan

You’ll also want to remove the update_interval: 20s from the pm1006 sensor.

Yes, the transistor for example can only handle a very small amount of current. The current limiting resistor does just that, it only allows X amount of current so you don’t burn out the transistor.

The diode or in this case, the flyback diode is used to prevent sudden voltage spikes from inductive loads when they are turned off. When you turn that fan off, it’s still spinning and can cause a voltage spike that will damage electronics.

The “component_update” only works for some components so, if it gives you a compile error let me know and we can go another route.

1 Like

seeing how the data voltage is 4.5v you will need a logic shifter too. This just steps up/down voltage back and forth. 4.5v will come from the sensor and into the logic shifter which steps it down to a safe and usable 3.3v. UART data being sent to the sensor will go out the esp32 gpio at 3.3v, the logic shifter will bump it up to 5v and send it to the sensor.

https://www.amazon.com/WWZMDiB-Channels-Converter-Bi-Directional-3-3V-5V/dp/B0BD8JNXWB/ref=sr_1_1_sspa?crid=2K78FPY5TWE1S&keywords=logic+level+shifter&qid=1706463693&sprefix=logic+level%2Caps%2C125&sr=8-1-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9hdGY&psc=1

1 Like

Thanks so much!! for all your replies. they all make sense to me and totaly understand why i should add the resistor and diode. The diode i didn’t had laying around anymore and will order one. The resistor is in, and with your example for the code i saw that i already had something like that in place so only had to add the fan and component update in there and it is working! Cool, this topic can be closed very quickly. Many thanks!

My current ikea vindriktning ESPhome code (still in development):

switch:
  - platform: gpio
    pin: GPIO25
    name: "Fan"
    id: Fan
light:
  - platform: partition
    name: "Partition Light 1"
    segments:
      - id: light1
        from: 0
        to: 1
  - platform: partition
    name: "Partition Light 2"
    segments:
      - id: light1
        from: 2
        to: 3
  - platform: partition
    name: "Partition Light 3"
    segments:
      - id: light1
        from: 4
        to: 5
  - platform: partition
    name: "Partition Light 4"
    segments:
      - id: light1
        from: 6
        to: 7

  - platform: neopixelbus
    id: light1
    type: GRB
    variant: WS2811
    pin: GPIO27
    num_leds: 8
    name: "Status Light"
    effects:
    - random:
    - pulse:
    - strobe:
    - flicker:
    - addressable_rainbow:
    - addressable_color_wipe:
    - addressable_scan:
    - addressable_twinkle:
    - addressable_random_twinkle:
    - addressable_fireworks:

font:
  - file: 'arial.ttf'
    id: font1
    size: 16
uart:
  - id: uart_1
    rx_pin: GPIO17
    tx_pin: GPIO16
    baud_rate: 256000
  - id: uart_2
    rx_pin: GPIO03
    tx_pin: GPIO01
    baud_rate: 9600

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true
  id: bus_a


ld2410:
  uart_id: uart_1

binary_sensor:
  - platform: ld2410
    has_target:
      name: Presence
    has_moving_target:
      name: Moving Target
      id: moving
    has_still_target:
      name: Still Target
    out_pin_presence_status:
      name: out pin presence status
  - platform: homeassistant
    entity_id: switch.ikeafan
    id: enable_automation

sensor:
  - platform: pm1006
    uart_id: uart_2
    id: pm25
    pm_2_5:
      name: "Particulate Matter 2.5µm Concentration"
      on_value:
        - if:
            condition:
              - binary_sensor.is_off: enable_automation
            then:
              - homeassistant.service:
                  service: number.set_value
                  data:
                    entity_id: $ap_id
                    value: !lambda |-
                      auto range = (x - $pms_low) / ($pms_high - $pms_low) * $af_speeds;
                      auto value = (x < $pms_low) ? 0 : (range > $af_speeds) ? $af_speeds : range;
                      return ceil(value);
            else:
              # If the "$auto_enable" binary sensor is on, set the fan speed to 0
              - homeassistant.service:
                  service: number.set_value
                  data:
                    entity_id: $ap_id
                    value: '0'

  - platform: bme280
    temperature:
      name: "BME280 Temperature"
      oversampling: 16x
      id: temperature
    pressure:
      name: "BME280 Pressure"
    humidity:
      name: "BME280 Humidity"
      oversampling: 16x
      id: humidity
    address: 0x76
    update_interval: 60s

  - platform: ccs811
    eco2:
      name: "Bedroom eCO2 Value"
    tvoc:
      name: "Bedroom Total Volatile Organic Compound"
    address: 0x5A
    update_interval: 60s
    baseline: 0x4481
    temperature: temperature
    humidity: humidity
    
  - platform: ld2410
    light:
      name: light
    moving_distance:
      name : Moving Distance
      id: distance
    still_distance:
      name: Still Distance
    moving_energy:
      name: Move Energy
    still_energy:
      name: Still Energy
    detection_distance:
      name: Detection Distance

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    id: my_display
    address: 0x3C
    update_interval: 1s
    pages:
      - id: page1    
        lambda: |-
          it.print(0, 0, id(font1), "Human Sensor");
          it.printf(0, 20, id(font1), "Afstand: %.1f cm", id(distance).state);
          it.printf(0, 40, id(font1), "Beweging: %s", id(moving).state ? "Ja" : "Nee");
      - id: page2    
        lambda: |-
          it.print(0, 0, id(font1), "Temp/Hum");
          it.printf(0, 20, id(font1), "Temp: %.1f°", id(distance).state);
          it.printf(0, 40, id(font1), "Hum: %.1f %%", id(distance).state);

interval:
  - interval: 15s
    then:        
      - display.page.show_next: my_display
      - component.update: my_display
      - switch.turn_on: Fan
      - delay: 5s
      - component.update: pm25
      - switch.turn_off: Fan
1 Like

Nice! This sensor has me curious. Can you let me know how it is and if it’s worth buying one for myself?

Sorry, saw i did not replied yet. The project is still in the make. Sensors are working but are not accurate, so that needs some work. When ready will post an update. As of now it takes a lot of time and not sure it is worth it. It is a fun project and learning a lot on how to advanced use esphome

Hello all.
Did you manage in the end to request the pm25 values using the code above? For me this does not work. At line - component.update: pm25, the compiler gives the error: ID ‘pm25’ of type sensor::Sensor doesn’t inherit from PollingComponent. Please double check your ID is pointing to the correct value.
Interval code:

interval:
  - interval: 25s
    then:        
      - switch.turn_on: fanPin
      - delay: 5s
      - component.update: pm25
      - switch.turn_off: fanPin

Sensor code:

uart:
  rx_pin: 16
  tx_pin: 17
  baud_rate: 9600
  id: uart_2

sensor:
  - platform: pm1006
    pm_2_5:
      name: "PM 2.5µm"
      id: pm25
      accuracy_decimals: 1
      on_value:
        then:
        - if:
            condition:
              lambda: 'return x < 30;'
            then:
              - light.addressable_set:
                  id: status
                  range_from: 0
                  range_to: 0
                  red: 0.0
                  green: 1.0
                  blue: 0.0
                  color_brightness:  !lambda |-
                    return id(light_sensor).state ? 1.0 : 0.5;
            else:
            - if:
                condition:
                  lambda: 'return x > 90;'
                then:
                - light.addressable_set:
                    id: status
                    range_from: 0
                    range_to: 0
                    red: 1.0
                    green: 0.0
                    blue: 0.0
                    color_brightness:  !lambda |-
                      return id(light_sensor).state ? 1.0 : 0.5;
                else:
                - light.addressable_set:
                    id: status
                    range_from: 0
                    range_to: 0
                    red: 1.0
                    green: 0.72
                    blue: 0.0
                    color_brightness:  !lambda |-
                      return id(light_sensor).state ? 1.0 : 0.5;
    uart_id: uart_2

Hi,

Yes got it to work. But some sensor still need some tuning/tweaking to get the correct results.

Currently working code below. There are a lot of sensors added. So pick out why only applies to your config

# Enable logging
logger:
  logs:
    component: ERROR
  # baud_rate: 0

bluetooth_proxy:

esp32_improv:
  authorizer: none

switch:
  - platform: gpio
    pin: GPIO25
    name: "Fan"
    id: Fan

light:
  - platform: partition
    name: "Partition Light 1"
    segments:
      - id: light1
        from: 0
        to: 1
  - platform: partition
    name: "Partition Light 2"
    segments:
      - id: light1
        from: 2
        to: 3
  - platform: partition
    name: "Partition Light 3"
    segments:
      - id: light1
        from: 4
        to: 5
  - platform: partition
    name: "Partition Light 4"
    segments:
      - id: light1
        from: 6
        to: 7

  - platform: neopixelbus
    id: light1
    type: GRB
    variant: WS2811
    pin: GPIO27
    num_leds: 8
    name: "Status Light"
    effects:
    - random:
    - pulse:
    - strobe:
    - flicker:
    - addressable_rainbow:
    - addressable_color_wipe:
    - addressable_scan:
    - addressable_twinkle:
    - addressable_random_twinkle:
    - addressable_fireworks:

font:
  - file: 'arial.ttf'
    id: font1
    size: 14
  - file: 'arialbd.ttf'
    id: font2
    size: 16

    
## Serial Port for the IKEA Sensor
uart:
  - id: uart_1
    rx_pin: GPIO17
    tx_pin: GPIO16
    baud_rate: 256000
  - id: uart_2
    rx_pin: GPIO03
    tx_pin: GPIO01
    baud_rate: 9600

## I²C Port - For Temp/Humidity/Pressure & CO²/VOC Sensors
i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true
  id: bus_a


ld2410:
  uart_id: uart_1

binary_sensor:
  - platform: ld2410
    has_target:
      name: Presence
    has_moving_target:
      name: Moving
      id: moving
    has_still_target:
      name: Still
    out_pin_presence_status:
      name: out pin presence status
  - platform: template
    name: "closeby"
    id: closeby
    lambda: |-
      if (id(distance).state < 70) {
        // Garage Door is open.
        
        return true;
      } else {
        // Garage Door is closed.
        return false;
      }
    on_press:
      then:
        - display.page.show: page1

sensor:
  - platform: pm1006
    id: pm25
    uart_id: uart_2
    pm_2_5:
      name: "PM 2.5"
    update_interval: 15s

    # update_interval: 10s
  - platform: bh1750
    name: "Illuminance"
    address: 0x23
    update_interval: 10s

  - platform: bme280_i2c
    temperature:
      name: "Temperature"
      id: temperature
      filters:
      - calibrate_linear:
         method: least_squares
         datapoints:
          # Map 0.0 (from sensor) to 1.0 (true value)
          - 0.0 -> 1.0
          - 18.1 -> 19.0
          - 24.4 -> 20.0
    pressure:
      name: "Pressure"
    humidity:
      name: "Humidity"
      id: humidity
      # filters:
      # - calibrate_linear:
      #    method: least_squares
      #    datapoints:
      #     # Map 0.0 (from sensor) to 1.0 (true value)
      #     - 0.0 -> 1.0
      #     - 33.1 -> 50.0
      #     - 60.0 -> 53.0
    address: 0x76
    update_interval: 20s

  - platform: ccs811
    eco2:
      name: "eCO2"
      id: eco2
    tvoc:
      name: "VOC"
      id: voc
    address: 0x5A
    update_interval: 20s
    baseline: 0x4481
    temperature: temperature
    humidity: humidity
    
  - platform: ld2410
    light:
      name: light
    moving_distance:
      name : Distance Moving
      id: distance
    still_distance:
      name: Distance Still
    moving_energy:
      name: Energy Move
    still_energy:
      name: Energy Still
    detection_distance:
      name:  Detection

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    id: my_display
    address: 0x3C
    update_interval: 1s
    pages:
      - id: page1
        lambda: |-
          it.print(0, 0, id(font2), "Radar");
          it.printf(0, 20, id(font1), "Afstand: %.1f cm", id(distance).state);
          it.printf(0, 40, id(font1), "Beweging: %s", id(moving).state ? "Ja" : "Nee");
      - id: page2
        lambda: |-
          it.print(0, 0, id(font2), "Temp/Hum");
          it.printf(0, 20, id(font1), "Temp: %.1f°", id(temperature).state);
          it.printf(0, 40, id(font1), "Hum: %.1f %%", id(humidity).state);
      - id: page3
        lambda: |-
          it.print(0, 0, id(font2), "Air");
          it.printf(0, 20, id(font1), "eCO2: %.1f ppm", id(eco2).state);
          it.printf(0, 40, id(font1), "VOC: %.1f ppb", id(voc).state);
      - id: page4
        lambda: |-
          it.print(0, 0, id(font2), "Air");
          it.printf(0, 20, id(font1), "%.0f ug/m2", id(pm25));
      - id: page5
        lambda: |-
          it.fill(COLOR_OFF);
interval:
  - interval: 30s
    then:        
      # - display.page.show_next: my_display
      # - component.update: my_display
      - switch.turn_on: Fan
      - delay: 12s
      - component.update: pm25
      - switch.turn_off: Fan
  - interval: 3s
    then:
    - if:
        condition:
          lambda: 'return id(distance).state < 70;'
        then:
          - if:
              condition:
                display.is_displaying_page: page4
              then:
                display.page.show: page1
              else:
                - display.page.show_next: my_display
                - component.update: my_display
        else:
          - display.page.show: page5

I think this is realy the maximum that will fit in to it :stuck_out_tongue: