Sensirion Sps30 Experiments
Sensirion SPS30 Experiments
For some days an air purifier is doing its work here, leading to significant improvements but only if running in manual mode, the automatic mode seems lacking, at least according to my nose.
Because this is not very scientific, I decided to connect another sensor to my pc and see if the values somewhere else in the room differ from what the purifier measures.
A possible reason: to measure airquality, you need to transport enough air to the sensor, but what happens if the automatic mode drops fan speeds so low that it’s not enough to produce accurate reads?
It may never measure high enough particle values to spin up the fan again.
Idea
Using a sensor further away from the purifier should lead to better results.
I decided to try out the sensirion sps30 since I still got one lying around.
Challenge
The sensor I have at hand is connected to the usb evaluation kit, so I should be able to poll those values.
Sensirion provides a GitHub repository for usage with the uart interface.
Turns out I found no easy way to do this, partly because I lack knowledge in handling C/C++1
Experiments
The only language I can handle okayish is python, so lets see how far we can get here.
# create and activate python venv
mkdir -p sensirion
cd sensirion
python3 -m venv .venv
./.venv/bin/activate
# we need the pyserial module so lets install it right away
./.venv/bin/python3 -m pip install pyserial
Let’s try some basic stuff to see if we can interface with the usb device.
If you get access denied, you should find out what group has access to the usb device:
stat /dev/ttyUSB0
File: /dev/ttyUSB0
Size: 0 Blocks: 0 IO Block: 4096 character special file
Device: 0,6 Inode: 1032 Links: 1 Device type: 188,0
Access: (0660/crw-rw----) Uid: ( 0/ root) Gid: ( 986/ uucp)
Access: 2025-06-09 10:27:32.294929519 +0200
Modify: 2025-06-09 10:27:32.294929519 +0200
Change: 2025-06-09 10:27:32.294929519 +0200
Birth: 2025-06-09 10:27:32.191891998 +0200
I’m on arch, so it’s uucp. This differs depending on the flavor you prefer.
sudo usermod -aG uucp <username>
Remember to logout&login and check the output of groups if you still get access denied.
Sometimes a reboot is needed.
Let’s see on what port the evaluation kit is connected.
lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
[...]
ls -al /dev/ttyUSB*
crw-rw---- 188,0 root 9 Jun 11:54 /dev/ttyUSB0
The only available Port is ttyUSB0 so we can go with that one.
import serial
ser = serial.Serial('/dev/ttyUSB0') # open serial port
print(ser.name) # check which port was really used
ser.write(b'hello') # write a string
ser.close() # close port
Should print something like this:
/dev/ttyUSB0
This means we are basically able to handle connections to the port.
Retreiving Data
The easy way now is to use an existing solution2.
I decided to just copy the sps30.py, comment out the import for paho.mqtt.publish and give it a try.
Looks like this:
./.venv/bin/python3 sps30.py
['/dev/ttyUSB0']
Starting
Wait: 0
Wait: 7
Wait: 0
Wait: 25
Wait: 0
Wait: 47
created: Jun2025
SPS_<serial>,06/09/25 11:40:51,0.87,0.90,0.90,0.90,6.00,6.94,6.96,6.96,6.96,0.50
Wait: 0
Wait: 47
SPS_<serial>,06/09/25 11:42:13,0.77,0.80,0.80,0.80,5.32,6.15,6.17,6.17,6.17,0.39
Wait: 0
Wait: 49
SPS_<serial>,06/09/25 11:43:35,1.89,1.95,1.95,1.95,13.06,15.11,15.15,15.15,15.15,0.43
[...]
What does it mean? A hint is inside the mqtt part of the code:
header = ['sensor', 'time', 'PM1', 'PM25', 'PM4', 'PM10', 'b0305',
'b031', 'b0325', 'b034', 'b0310', 'tsize']
A line like SPS_<serial>,06/09/25 11:40:51,0.87,0.90,0.90,0.90,6.00,6.94,6.96,6.96,6.96,0.50
gets translated to:
sensor: SPS_<serial>
time: 06/09/25 11:40:51
PM1: 0.87 # Mass Concentration PM1.0 [μg/m³]
PM25: 0.90 # Mass Concentration PM2.5 [μg/m³]
PM5: 0.90 # Mass Concentration PM4.0 [μg/m³] //Note: the sps30 is measuring PM4.0 not 5.0
PM10: 0.90 # Mass Concentration PM10 [μg/m³]
B0305: 6.00 # Number Concentration PM0.5 [#/cm³]
B031: 6.94 # Number Concentration PM1.0 [#/cm³]
B0325: 6.96 # Number Concentration PM2.5 [#/cm³]
B034: 6.96 # Number Concentration PM4.0 [#/cm³]
B0310: 6.96 # Number Concentration PM10 [#/cm³]
tsize: 0.50 # Typical Particle Size8 [μm]
To understand what we see, the datasheet3 helps along while also looking into the python code.
While someone was cooking, both the air purifier and the sps30 noticed the spike in particles.
After the purifier spun down again the line keeps at the minimum while the distant sensor is still able to pick up on elevated values.
Let’s keep this running for some more hours and see if this proves our assumption.

