Pimoroni PIM629 Enviro Urban with ESPHome

I tried to use MQTT approach with a broker, but since Pimoroni Enviro Urban is based on RPI zero W and ESPHome supports all sensors, I wrote a dedicated configuration. The most problematic part was enabling the PM sensor dc/dc converter early enough.


esphome:
  name: pimoroni-ext
  friendly_name: pimoroni_ext
  on_boot:
    - priority: 1000 #make sure PM sensor wakes up before the init
      then:
        - lambda: |-
            pinMode(2, OUTPUT); //keep the board power on
            digitalWrite(2, HIGH);
            delay(100);
            pinMode(11, OUTPUT); //enable Boost converter
            digitalWrite(11, HIGH);
            delay(1000); //wait until DC/DC wakes up
            pinMode(10, OUTPUT); 
            digitalWrite(10, HIGH); //enable sensor
            delay(100);
            pinMode(9, OUTPUT); //reset sensor
            digitalWrite(9, LOW);
            delay(100);
            digitalWrite(9, HIGH); //un-reset sensor
            delay(1000); //wait until PM sensor wakes up
        - delay: 2s
        

        

rp2040:
  board: rpipicow
  framework:
    # Required until https://github.com/platformio/platform-raspberrypi/pull/36 is merged
    platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxx"

ota:
  password: "xxxx"

wifi:
  ssid: "xxxx"
  password: "xxxx."

  # Enable fallback hotspot in case wifi connection fails
  ap:
    ssid: "Pimoroni-Ext Fallback Hotspot"
    password: "xxxxx"

i2c:
  - id: bus_a   # RTC + QWST + BME280
    sda: 4
    scl: 5
    scan: true
  - id: bus_b   #pm sensor
    sda: 14
    scl: 15
    scan: true
 
sensor:
  - platform: pmsa003i
    setup_priority: -100
    i2c_id: bus_b
    pm_1_0:
      name: "PM1.0"
    pm_2_5:
      name: "PM2.5"
    pm_10_0:
      name: "PM10.0"
    pmc_0_3:
      name: "PMC <0.3µm"
    pmc_0_5:
      name: "PMC <0.5µm"
    pmc_1_0:
      name: "PMC <1µm"
    pmc_2_5:
      name: "PMC <2.5µm"
    pmc_5_0:
      name: "PMC <5µm"
    pmc_10_0:
      name: "PMC <10µm"

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

  - platform: adc
    id: adc_mic_reading
    pin: 26
  - platform: ct_clamp
    sensor: adc_mic_reading
    name: "Sound level from MEMS mic"
    sample_duration: 10s
    update_interval: 60s


1 Like

Thanks for posting this! I was having trouble setting up the MQTT method, and this worked fine. However, the values being returned seem to be incorrect. The PM1.0, PM2.5, and PM10.0 values seem to hardly even be above zero. The original firmware didn’t expose the set of 6 PMC values, but most of those seem to be nonzero at least, though they might need some adjustment too.

In looking at the original firmware code, it appears that a calculation was being done on the values before returning them for PM1, 2.5, and 10:

Specifically, it’s this line (in the current version at least), where the multiplier is set to 1 for these values:

return ((particulate_data[measure * 2] << 8) | particulate_data[measure * 2 + 1]) * multiplier

Have you made any updates to this esphome file? I haven’t yet attempted to try to apply similar calculations and see if the values returned are any better. My enviro urban still works fine with the original firmware, but this is a much nicer solution for integrating into HomeAssistant, and I was hoping to use it.

Here’s what I get back from the device when using your setup:

The humidity, pressure, and temperature are on par with the original firmware. The temperature is way off though, which is apparently a known problem when running it from USB power. I’ve got another way to get the temperature so I don’t mind ignoring it here. (In theory it should be more accurate if running off battery since the board heats up when using USB, but I want it to be always on and not deal with swapping out batteries.) The PM values are almost always 0. The larger PMC values seem to be incorrect at 0 as well, but since I’ve never seen what they report before using this, I’m not sure if they are incorrect or the device is just not reliable for them. The MEMS sensor seems to work appropriately here too.

Thanks again for posting this. It’s a great approach and a great start, but either my device is having issues or the values need some tweaking after they are returned.

Thank you very much, without your job I could not make it work.
thanks again.
the consumption is quite high, about 100mA continuously, do you think is it possible to send it to sleep for 1 minute and then wake it up?
the mic is always 0, do you have the same?

thank you again

I add my code for reference


esphome:
  name: enviro-eh
  friendly_name: enviro-EH
  on_boot:
  - priority: 1000 #make sure PM sensor wakes up before the init
    then:
      - lambda: |-
          pinMode(2, OUTPUT); //keep the board power on
          digitalWrite(2, HIGH);
          delay(100);
          pinMode(11, OUTPUT); //enable Boost converter
          digitalWrite(11, HIGH);
          delay(1000); //wait until DC/DC wakes up
          pinMode(10, OUTPUT); 
          digitalWrite(10, HIGH); //enable sensor
          delay(100);
          pinMode(9, OUTPUT); //reset sensor
          digitalWrite(9, LOW);
          delay(100);
          digitalWrite(9, HIGH); //un-reset sensor
          delay(1000); //wait until PM sensor wakes up
      - delay: 2s

rp2040:
  board: rpipicow
  framework:
    # Required until https://github.com/platformio/platform-raspberrypi/pull/36 is merged
    platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: ""

ota:
  password: ""

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

  # Enable fallback hotspot in case wifi connection fails
  ap:
    ssid: "Enviro-Eh Fallback Hotspot"
    password: ""

    
i2c:
  - id: bus_a   # RTC + QWST + BME280
    sda: 4
    scl: 5
    scan: true
  - id: bus_b   #pm sensor
    sda: 14
    scl: 15
    scan: true
 
sensor:
  - platform: pmsa003i
    setup_priority: -100
    i2c_id: bus_b
    pm_1_0:
      name: "PM1.0"
    pm_2_5:
      name: "PM2.5"
    pm_10_0:
      name: "PM10.0"
    pmc_0_3:
      name: "PMC <0.3µm"
    pmc_0_5:
      name: "PMC <0.5µm"
    pmc_1_0:
      name: "PMC <1µm"
    pmc_2_5:
      name: "PMC <2.5µm"
    pmc_5_0:
      name: "PMC <5µm"
    pmc_10_0:
      name: "PMC <10µm"

  - platform: bme280_i2c
    i2c_id: bus_a 
    temperature:
      name: "BME280 Temperature"
      id: bme280_temperature
    pressure:
      name: "BME280 Pressure"
      id: bme280_pressure
    humidity:
      name: "BME280 Relative Humidity"
      id: bme280_humidity
    address: 0x77
    update_interval: 60s
  - platform: template
    name: "Altitude"
    lambda: |-
      const float STANDARD_SEA_LEVEL_PRESSURE = 1013.25; //in hPa, see note
      return ((id(bme280_temperature).state + 273.15) / 0.0065) *
        (powf((STANDARD_SEA_LEVEL_PRESSURE / id(bme280_pressure).state), 0.190234) - 1); // in meter
    update_interval: 60s
    icon: 'mdi:signal'
    unit_of_measurement: 'm'
  - platform: absolute_humidity
    name: "Absolute Humidity"
    temperature: bme280_temperature
    humidity: bme280_humidity
  - platform: template
    name: "Dew Point"
    lambda: |-
      return (243.5*(log(id(bme280_humidity).state/100)+((17.67*id(bme280_temperature).state)/
      (243.5+id(bme280_temperature).state)))/(17.67-log(id(bme280_humidity).state/100)-
      ((17.67*id(bme280_temperature).state)/(243.5+id(bme280_temperature).state))));
    unit_of_measurement: °C
    icon: 'mdi:thermometer-alert'
  - platform: template
    name: "Equivalent sea level pressure"
    lambda: |-
      const float STANDARD_ALTITUDE = 0.6; // in meters, see note
      return id(bme280_pressure).state / powf(1 - ((0.0065 * STANDARD_ALTITUDE) /
        (id(bme280_temperature).state + (0.0065 * STANDARD_ALTITUDE) + 273.15)), 5.257); // in hPa
    update_interval: 60s
    unit_of_measurement: 'hPa'

  - platform: adc
    id: adc_mic_reading
    pin: 26
  - platform: ct_clamp
    sensor: adc_mic_reading
    name: "Sound level from MEMS mic"
    sample_duration: 10s
    update_interval: 60s