The Sensirion SPS30 is a laser-scattering particulate matter sensor that can turn an ESP32 into a useful local PM1, PM2.5 and PM10 monitor for Home Assistant. Unlike a VOC sensor, it measures airborne particles. Unlike a CO₂ sensor, it cannot tell you whether ventilation is sufficient for people in a room. Those are different measurements and are useful together.
This guide is a dedicated SPS30 implementation rather than another general air-quality station. It covers the sensor’s unusual 5 V power / I²C voltage combination, connection to an ESP32, a complete ESPHome configuration, mass versus particle-number readings, the internal fan, automatic cleaning, an air-purifier automation and practical fault-finding.
SPS30 at a Glance
| Specification | SPS30 |
|---|---|
| What it detects | Optical particle scattering; particulate matter, not VOC or CO₂ |
| Mass channels | PM1, PM2.5, PM4 and PM10 in µg/m³ |
| Particle-number channels | Number concentrations for nominal 0.5, 1, 2.5, 4 and 10 µm cut-offs in particles/cm³ |
| Additional output | Typical particle size in µm |
| Power | 4.5–5.5 V; nominal 5 V |
| Supply budget | Approximately 55 mA typical while measuring; allow at least 80 mA startup and sensible margin |
| Interface | I²C or UART at sensor level; current ESPHome component uses I²C |
| I²C address | 0x69 |
| Interface select | SEL connected to ground for I²C |
| Physical size | Approximately 41 × 41 × 12 mm |
| Maintenance | Internal fan and programmable automatic fan cleaning |
Sensirion lists the SPS30 as a long-life PM sensor designed for air-quality and HVAC applications. Its accuracy figures apply to defined test conditions. Treat its Home Assistant readings as an indoor trend and control input, not as a substitute for a regulatory monitoring station or a workplace safety instrument.
What PM1, PM2.5 and PM10 Mean
PM2.5 means particles with an aerodynamic size up to roughly 2.5 µm under the relevant particulate-matter convention. The SPS30 estimates mass concentration optically and reports it in µg/m³. PM1, PM2.5 and PM10 are cumulative size fractions, not three separate piles of particles to add together.
PM1 = mass concentration up to 1 µm
PM2.5 = mass concentration up to 2.5 µm
PM4 = mass concentration up to 4 µm
PM10 = mass concentration up to 10 µm
For example, PM2.5 of 18 µg/m³ and PM10 of 26 µg/m³ does not mean the total is 44 µg/m³. The PM10 reading already includes the smaller particles counted in the PM2.5 fraction. These are estimates based on optical response, so aerosol type and humidity can affect the result.
Particle Number Is Not Particle Mass
The SPS30 also reports particle number concentration in particles/cm³. These values answer a different question: approximately how many particles occupy a volume of air in particular size fractions. Do not label a particle-number output as µg/m³, and do not combine number and mass readings on the same axis as if they were interchangeable.
For a first Home Assistant dashboard, keep PM2.5 and PM10 as the primary visible entities, make PM1 available for comparison, and mark the number-concentration channels as diagnostic or advanced. You can add all of them later without rewiring.
Five-Pin Connector and Correct Wiring
The SPS30 uses a five-pin, 1.5 mm-pitch JST-style connector. On Sensirion’s standard cable the pins are identified as follows; verify the connector orientation and any third-party breakout silkscreen before applying power.
| SPS30 pin | Function | Connection for ESP32 build |
|---|---|---|
| 1 — VDD | 5 V sensor supply | Regulated 5 V, NOT the ESP32 3V3 pin |
| 2 — SDA | I²C data | ESP32 GPIO21 via a 3.3 V-safe I²C interface |
| 3 — SCL | I²C clock | ESP32 GPIO22 via a 3.3 V-safe I²C interface |
| 4 — SEL | Interface selection | Ground for I²C; connect before powering sensor |
| 5 — GND | Common ground | ESP32 GND and supply return |
SPS30 VDD (pin 1) → regulated 5 V
SPS30 GND (pin 5) → ESP32 GND
SPS30 SEL (pin 4) → GND
SPS30 SDA (pin 2) → I²C level shifter → ESP32 GPIO21
SPS30 SCL (pin 3) → I²C level shifter → ESP32 GPIO22
Do not power the bare SPS30 from 3.3 V. Its specified supply is 4.5–5.5 V, with fan-start current higher than the average measurement current. A weak source can make the laser/fan subsystem reset or produce intermittent readings while the ESP32 itself remains online.
The 5 V I²C Pull-Up Trap
Sensirion’s reference SPS30 I²C circuit shows SDA and SCL pulled up through resistors to the sensor’s 5 V supply. An ESP32 GPIO is not 5 V tolerant, so copying that pull-up arrangement directly onto GPIO21/22 is unsafe. The SPS30’s supply voltage and the ESP32’s allowable GPIO voltage are two separate design constraints.
The conservative solution is a bidirectional I²C level shifter: sensor-side pull-ups to 5 V, ESP32-side pull-ups to 3.3 V, all grounds common. Some commercial SPS30 carrier boards already provide level shifting and safe 3.3 V logic connections; check the specific board schematic instead of adding a second shifter blindly.
Avoid connecting a 5 V pull-up in parallel with a 3.3 V pull-up and assuming the lower rail “wins”. The bus high voltage can rise above the ESP32 supply. If you have an unknown adapter, measure idle SDA/SCL voltage on the microcontroller side before connecting it to the ESP32.
I²C Address and Bus Speed
The SPS30’s I²C address is 0x69. ESPHome can scan the bus at startup; the device should appear at that address with SEL grounded. A sensor that appears on a generic I²C scanner but produces no particulate data may still have a power, fan, warm-up or command-communication problem.
i2c:
sda: GPIO21
scl: GPIO22
frequency: 100kHz
scan: true
Use a short cable between the sensor and controller wherever practical. Sensirion recommends the SPS30 UART interface for longer sensor cables, but current ESPHome SPS30 support is I²C-only; do not copy a UART wiring diagram and expect platform: sps30 to read it. For long cable runs, move the ESP32 closer to the sensor or use a different firmware/interface arrangement.
Minimal ESPHome Configuration
ESPHome provides a native sps30 sensor platform. The minimal example below publishes the three mass channels most useful for a household particulate dashboard.
i2c:
sda: GPIO21
scl: GPIO22
frequency: 100kHz
scan: true
sensor:
- platform: sps30
id: sps30_sensor
address: 0x69
pm_1_0:
name: "Room PM1"
pm_2_5:
name: "Room PM2.5"
id: pm25_room
pm_10_0:
name: "Room PM10"
update_interval: 30s
Allow the sensor to start its measurement process before judging the first reading. The SPS30 has a physical fan and measurement warm-up; it is not a passive instant-read I²C temperature probe. If it reports no values immediately after boot, check the ESPHome log before power-cycling it repeatedly.
Complete ESP32 + SPS30 ESPHome YAML
This standalone example uses a classic ESP32 DevKit and a safely level-shifted I²C connection. Supply the encryption key through your ESPHome secrets configuration and flash the node once by USB.
esphome:
name: workshop-pm-monitor
friendly_name: Workshop PM Monitor
esp32:
board: esp32dev
framework:
type: esp-idf
logger:
api:
encryption:
key: !secret api_encryption_key
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
i2c:
sda: GPIO21
scl: GPIO22
frequency: 100kHz
scan: true
sensor:
- platform: sps30
id: workshop_sps30
address: 0x69
update_interval: 30s
pm_1_0:
name: "Workshop PM1"
pm_2_5:
name: "Workshop PM2.5"
id: workshop_pm25
pm_4_0:
name: "Workshop PM4"
pm_10_0:
name: "Workshop PM10"
pmc_0_5:
name: "Workshop Particles 0.5"
entity_category: diagnostic
pmc_1_0:
name: "Workshop Particles 1"
entity_category: diagnostic
pmc_2_5:
name: "Workshop Particles 2.5"
entity_category: diagnostic
pmc_4_0:
name: "Workshop Particles 4"
entity_category: diagnostic
pmc_10_0:
name: "Workshop Particles 10"
entity_category: diagnostic
pm_size:
name: "Workshop Typical Particle Size"
entity_category: diagnostic
button:
- platform: template
name: "SPS30 Fan Clean"
on_press:
- sps30.start_fan_autoclean: workshop_sps30
Do not paste this into an existing configuration that already declares sensor:, i2c: or button: at the top level without merging those sections. Use an ESP32-S3/C3/C6 pinout appropriate to your actual board rather than copying GPIO21/22 from the original ESP32.
What the ESPHome Output Keys Mean
| ESPHome key | What Home Assistant receives |
|---|---|
pm_1_0 | PM1 mass concentration, µg/m³ |
pm_2_5 | PM2.5 mass concentration, µg/m³ |
pm_4_0 | PM4 mass concentration, µg/m³ |
pm_10_0 | PM10 mass concentration, µg/m³ |
pmc_0_5 to pmc_10_0 | Particle-number concentration channels, particles/cm³ |
pm_size | Typical particle size, µm |
ESPHome provides units for these entities; keep them unchanged. The pmc_... channels represent the sensor’s defined number-concentration size cuts, not individual microscopy counts of every particle. For everyday automation, PM2.5 concentration is usually easier to interpret than the raw number channels.
Continuous Sampling Versus ESPHome update_interval
The update_interval controls how often ESPHome reads/publishes the SPS30 measurement, not whether the internal fan stops between Home Assistant updates. Setting update_interval: 60s does not make a permanently running SPS30 into a battery-friendly sensor.
For a fixed mains-powered air-quality node, 10–60 seconds is a sensible interval depending on how responsive the dashboard should feel. For a slowly changing historical chart, an interval of 30 seconds with modest filtering is usually enough. Avoid heavy averaging if you want to see short cooking or workshop events.
Using Idle Mode to Reduce Fan Runtime
Current ESPHome also supports an SPS30 idle_interval. With this setting, ESPHome can stop measurement, keep the sensor idle for the configured interval, wake it, allow about 30 seconds of warm-up and obtain a reading. That changes the measurement schedule in a way an ordinary update_interval does not.
sensor:
- platform: sps30
id: sps30_sensor
pm_2_5:
name: "PM2.5"
update_interval: 30s
idle_interval: 5min
Use the idle option only if intermittent sampling matches the application. For a sensor that should catch short dust events or immediately drive an air purifier, continuous operation gives a more useful time series. For a trend-only device with modest energy constraints, test idle mode and compare the power draw and latency of your particular build.
Automatic Fan Cleaning and Manual Cleaning
The SPS30 can briefly accelerate its internal fan to reduce deposited dust. ESPHome documents a nominal automatic cleaning interval of 168 hours of uninterrupted operation, or one week. A fan-cleaning cycle runs for roughly 10 seconds. Repeatedly switching the sensor fully off can reset the counter, so do not assume a power-cycled SPS30 always performs the automatic clean on schedule.
The full YAML above includes a Home Assistant button that requests a manual cleaning cycle. You can run that after commissioning, or schedule periodic cleaning if your deployment would otherwise never reach a full week of uninterrupted operation. Do not use the button to try to clear a physically blocked inlet or a sensor contaminated by water.
button:
- platform: template
name: "SPS30 Fan Clean"
on_press:
- sps30.start_fan_autoclean: workshop_sps30
Cleaning is not a recalibration procedure, and an apparent reduction in PM immediately afterward is not proof that the ambient air improved. Look at the sustained trend after the sensor resumes its normal measurement state.
Mounting and Airflow
The SPS30 needs a clear inlet and outlet. A tiny sealed project box is unsuitable: the sensor would repeatedly sample the same trapped air instead of the room. Put the sensor where room air can enter and leave freely, without directly exposing it to a purifier exhaust stream or a jet from a ventilation duct.
- Mount it away from the ESP32 regulator and other heat sources.
- Do not block its inlet or outlet with foam, tape, wires or enclosure walls.
- Protect it from liquid water, condensing steam and high dust loading.
- Use a vented enclosure with accessible openings for future inspection.
- Avoid direct sunlight and locations beside a kettle or humidifier.
- Keep the sensor away from the immediate outlet of an air purifier if you want a representative room measurement.
Humid air and droplets can change the apparent optical scattering, so fog, shower steam or an ultrasonic humidifier may create a large “particle” spike even when there is no corresponding increase in combustion-derived fine dust. Use placement and context before treating every spike as pollution.
How to Interpret Home Assistant Readings
A PM2.5 rise during frying, candle use or dusty work is often more useful as a relative local event than as a standalone declaration that the room is safe or unsafe. Look at the baseline before the activity, the peak during the activity and the decay when you ventilate or run a purifier.
Mass concentration and particle-number concentration may respond differently to an aerosol because a small number of relatively large particles can contribute a lot of mass, while a cloud of ultrafine particles may greatly increase the count. The SPS30’s nominal sizing channels cannot identify chemical composition, allergens or pathogens.
A Simple PM2.5 Air-Purifier Automation
The following Home Assistant example is deliberately an illustrative user-adjustable threshold, not a medical or regulatory air-quality limit. Replace the example entity names with those from your ESPHome integration and adapt the thresholds to your ventilation setup.
alias: Workshop purifier from PM2.5
triggers:
- trigger: numeric_state
entity_id: sensor.workshop_pm25
above: 25
for: "00:02:00"
id: high_pm
- trigger: numeric_state
entity_id: sensor.workshop_pm25
below: 10
for: "00:10:00"
id: low_pm
actions:
- choose:
- conditions: "{{ trigger.id == 'high_pm' }}"
sequence:
- action: switch.turn_on
target:
entity_id: switch.workshop_air_purifier
- conditions: "{{ trigger.id == 'low_pm' }}"
sequence:
- action: switch.turn_off
target:
entity_id: switch.workshop_air_purifier
mode: restart
Separate ON/OFF thresholds provide hysteresis and help stop a purifier from repeatedly cycling near a single cutoff. Home Assistant numeric-state triggers fire when a threshold is crossed; they do not continuously re-run just because the reading remains high. If persistent operation through restarts matters, also add a startup/reconciliation automation that checks the current reading and sensor availability.
When the sensor goes unavailable or reports an implausible stale value, do not treat missing PM data as proof of clean air. Decide explicitly whether the purifier should continue running, be controlled by a manual schedule, or notify the user. The right fallback depends on what the purifier is being used for.
SPS30 vs PMS5003 vs SEN55
| Sensor | Distinctive feature | Typical ESPHome path |
|---|---|---|
| Sensirion SPS30 | Dedicated multi-channel PM sensor; reports mass and number concentration, typical size; built-in fan cleaning | I²C sps30 |
| Plantower PMS5003 | Widely used low-cost PM1/PM2.5/PM10 unit | UART pmsx003 |
| Sensirion SEN55 | Adds temperature, humidity and VOC/NOx indices to PM channels | I²C sen5x |
If you already have a reliable PMS5003, there is no need to replace it just to get a PM2.5 graph in Home Assistant. Choose SPS30 when its outputs, mechanical design or fan-cleaning workflow are useful. Choose SEN55 when one physical module must also provide gas indices and climate context, while remembering that those indices are not gas concentrations.
For background, read our existing PMS5003 ESP32 + ESPHome guide and Home Assistant CO₂/VOC/PM2.5 monitor. The SPS30 article concentrates on the separate fan-driven particulate sensor and its hardware constraints.
Troubleshooting: I²C Scan Finds No 0x69
- Check that SEL (pin 4) is grounded before or when the SPS30 is powered.
- Verify a regulated 5 V supply at pin 1 while the fan starts.
- Check shared ground, SDA/SCL order and the connector orientation.
- Inspect the level shifter and both sets of pull-ups; do not let ESP32-side lines rise to 5 V.
- Start with short wires and 100 kHz I²C; eliminate other bus devices during diagnosis.
- If the unit was previously configured in UART mode, power-cycle it after asserting SEL low.
Troubleshooting: 0x69 Appears but Values Are Missing
An I²C address scan proves that some bus-level communication is possible; it does not prove that the fan started or that particulate data is ready. Inspect the ESPHome serial log for measurement-initialisation, firmware-version or data-read errors. Check that the 5 V rail does not sag during the SPS30 fan-start transient and that the level shifter is appropriate for bidirectional I²C.
If values appear after startup and then disappear after enabling idle mode, check the configured idle/warm-up sequence rather than adding aggressive Home Assistant polling. If the values look persistently wrong, compare the device with a known-good sensor in the same location and avoid blocking the air path.
Troubleshooting: Readings Spike When the Humidifier Runs
A laser particle sensor responds to light scattered by airborne material, including water droplets. An ultrasonic humidifier can create a large cloud of droplets that the instrument counts as particles; high humidity can also change particle optical behaviour. Move the SPS30 away from the mist path, allow the room to stabilise and compare the PM trend before concluding that an electrical fault exists.
Troubleshooting: ESP32 Reboots When the Fan Starts
The ESP32 and the SPS30 have separate transient current demands. Do not power the SPS30 from a low-current 3.3 V pin, and do not rely on a long thin USB lead to supply everything without voltage drop. Use a regulated 5 V rail with adequate headroom and inspect uptime and supply voltage. The failure pattern “SPS30 fan starts → Wi-Fi disappears → uptime resets” indicates a power issue before it indicates a router issue.
Can an SPS30 Be Battery Powered?
Technically yes, but it is not a tiny deep-sleep humidity sensor. The fan, laser and 5 V converter consume meaningful energy, and the warm-up/cleaning behaviour must be included in the power budget. A permanently running SPS30 is normally much more convenient on mains-derived low-voltage power; intermittent measurement or idle mode trades responsiveness for reduced duty cycle.
If you duty-cycle the complete supply, remember the automatic fan-cleaning time counter and allow sufficient settling time after every restart. For a battery-operated project that must capture short cooking or smoke events, continuous sensing may be incompatible with your desired runtime.
Quick Checklist Before Closing the Enclosure
- Verify the exact SPS30 connector orientation and pin 1.
- Power the sensor from a stable regulated 5 V source.
- Keep ESP32 SDA/SCL at safe 3.3 V logic levels using verified breakout circuitry or level shifting.
- Ground SEL to select I²C and confirm address 0x69 in ESPHome logs.
- Check PM1, PM2.5 and PM10 units are µg/m³, not particles/cm³.
- Test the fan-clean button and confirm the air inlet/outlet remain unobstructed.
- Confirm the node recovers after a power cut and that Home Assistant handles sensor-unavailable states.
- Observe a clean-air baseline and a controlled everyday event before enabling automatic fan control.
Official Documentation and Related Guides
- ESPHome SPS30 component — exact YAML output keys, I²C limitation, idle interval and fan-cleaning action.
- Sensirion SPS30 product and specifications — PM measurement outputs, supply and manufacturer information.
- Sensirion embedded I²C SPS30 driver documentation — pin names, 5 V supply and 0x69 address.
- ESP32 PMS5003 with Home Assistant — UART-based particulate alternative.
- Home Assistant air-quality monitor — combining particulate, VOC and genuine CO₂ measurements.