Coder Social home page Coder Social logo

dvsu / sps30 Goto Github PK

View Code? Open in Web Editor NEW
18.0 1.0 8.0 21 KB

Python-based driver for Sensirion SPS30 particulate matter sensor. Tested on Raspberry Pi Zero/Zero W/3B+/4B.

License: MIT License

Python 100.00%
sensirion sps30 particulate-matter pm25 sensor python raspberry-pi

sps30's Introduction

Sensirion SPS30

Introduction

Python-based driver for Sensirion SPS30 particulate matter sensor. Tested on Raspberry Pi Zero/Zero W/3B+/4B.

Wiring

Sensor

                                 Pin 1   Pin 5
                                   |       |
                                   V       V
.------------------------------------------------.
|                                .-----------.   |
|                                | x x x x x |   |
|                                '-----------'   |
|     []          []          []          []     |
'------------------------------------------------'
Pin Description UART I2C
1 Supply voltage 5V VDD VDD
2 UART receiving pin/ I2C serial data input/ output RX SDA
3 UART transmitting pin/ I2C serial clock input TX SCL
4 Interface select (UART: floating (NC) /I2C: GND) NC GND
5 Ground GND GND

I2C Interface

  Sensor Pins                                 Raspberry Pi Pins
.-------.-----.                             .----------.---------.
| Pin 1 | VDD |-----------------------------|    5V    | Pin 2/4 |
| Pin 2 | SDA |-----------------------------| I2C1 SDA |  Pin 3  |
| Pin 3 | SCL |-----------------------------| I2C1 SCL |  Pin 5  |
| Pin 4 | GND |-----.                       |          |         |
| Pin 5 | GND |-----'-----------------------|   GND    | Pin 6/9 |
'-------'-----'                             '----------'---------'

Example

Default parameters of SPS30 class

Parameter Value Description
bus 1 I2C bus of Raspberry Pi
address 0x69 Default I2C address
import sys
import json
from time import sleep
from sps30 import SPS30


if __name__ == "__main__":
    pm_sensor = SPS30()
    print(f"Firmware version: {pm_sensor.firmware_version()}")
    print(f"Product type: {pm_sensor.product_type()}")
    print(f"Serial number: {pm_sensor.serial_number()}")
    print(f"Status register: {pm_sensor.read_status_register()}")
    print(
        f"Auto cleaning interval: {pm_sensor.read_auto_cleaning_interval()}s")
    print(f"Set auto cleaning interval: {pm_sensor.write_auto_cleaning_interval_days(2)}s")
    pm_sensor.start_measurement()

    while True:
        try:
            print(json.dumps(pm_sensor.get_measurement(), indent=2))
            sleep(2)

        except KeyboardInterrupt:
            print("Stopping measurement...")
            pm_sensor.stop_measurement()
            sys.exit()

Output data format

{
  "sensor_data": {
    "mass_density": {
      "pm1.0": 1.883,
      "pm2.5": 3.889,
      "pm4.0": 6.232,
      "pm10": 6.7
    },
    "particle_count": {
      "pm0.5": 1.302,
      "pm1.0": 4.595,
      "pm2.5": 7.326,
      "pm4.0": 7.864,
      "pm10": 7.967
    },
    "particle_size": 1.63,
    "mass_density_unit": "ug/m3",
    "particle_count_unit": "#/cm3",
    "particle_size_unit": "um"
  },
  "timestamp": 1630217804
}

Dependencies

None

sps30's People

Contributors

dvsu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

sps30's Issues

Issue with i2c setup

Hello

I am trying to use this cool library on a raspberrypi 4b with circuitpython. I have two other sensors connected on the i2c working ok. My circuitpython i2c interface is setup like so:

import json
import socket
import time

import adafruit_scd30
import adafruit_sht4x
import board

i2c = board.I2C() 
sht = adafruit_sht4x.SHT4x(i2c)
scd = adafruit_scd30.SCD30(i2c)

So adding the code for sps30 looks like this:

import json
import socket
import time

import adafruit_scd30
import adafruit_sht4x
import board

import sys  # this is not working - greyed out
from sps30 import SPS30

i2c = board.I2C() 
sht = adafruit_sht4x.SHT4x(i2c)
scd = adafruit_scd30.SCD30(i2c)

# sps dust sensor setup
if __name__ == "__main__":
    pm_sensor = SPS30(i2c)
    print(f"Firmware version: {pm_sensor.firmware_version()}")
    print(f"Product type: {pm_sensor.product_type()}")
    print(f"Serial number: {pm_sensor.serial_number()}")
    print(f"Status register: {pm_sensor.read_status_register()}")
    print(
        f"Auto cleaning interval: {pm_sensor.read_auto_cleaning_interval()}s")
    print(f"Set auto cleaning interval: {pm_sensor.write_auto_cleaning_interval_days(2)}s")
    pm_sensor.start_measurement()

This throws an error, because I guess its referencing a different I2C setup in your files?

Traceback (most recent call last):
  File "/opt/piMonitor_env/airmonitor.py", line 111, in <module>
    pm_sensor = SPS30(i2c)
  File "/opt/piMonitor_env/sps30.py", line 50, in __init__
    self.i2c = I2C(bus, address)
  File "/opt/piMonitor_env/i2c/i2c.py", line 11, in __init__
    self.fr = io.open("/dev/i2c-"+str(bus), "rb", buffering=0)
FileNotFoundError: [Errno 2] No such file or directory: '/dev/i2c-<busio.I2C object at 0x7f86cf22b0>'

Any ideas how to use circuitpythons i2c setup?

Extension : write_auto_cleaning_interval_days() function

In sps30.py :
def write_auto_cleaning_interval_days(self, days: int) -> int:
seconds = days * 86400 # 1day = 86400sec
interval = []
interval.append((seconds & 0xff000000) >> 24)
interval.append((seconds & 0x00ff0000) >> 16)
interval.append((seconds & 0x0000ff00) >> 8)
interval.append(seconds & 0x000000ff)
data = CMD_AUTO_CLEANING_INTERVAL
data.extend([interval[0], interval[1]])
data.append(self.crc_calc(data[2:4]))
data.extend([interval[2], interval[3]])
data.append(self.crc_calc(data[5:7]))
self.i2c.write(data)
sleep(0.05)
return self.read_auto_cleaning_interval()

Usage (example.py):
...
pm_sensor = SPS30(3) #Im using i3c_bus3
...
print(f"Set auto cleaning interval: {pm_sensor.write_auto_cleaning_interval_days(2)}s")

I'm using your code for the SPS30 which is really cool but I have a problem.

I'm hoping you can help me with an error running example.py. I get this error:

Serial number: CCC69E8DCEAD8B5F
Status register: {'speed_status': 'ok', 'laser_status': 'ok', 'fan_status': 'ok'}
Traceback (most recent call last):
File "/home/pi/Source/SPS30/example.py", line 13, in
print(f"Auto cleaning interval: {pm_sensor.read_auto_cleaning_interval()}s")
File "/home/pi/Source/SPS30/sps30.py", line 167, in read_auto_cleaning_interval
data = self.i2c.read(NBYTES_AUTO_CLEANING_INTERVAL)
File "/home/pi/Source/SPS30/i2c/i2c.py", line 21, in read
return list(self.fr.read(nbytes))
OSError: [Errno 121] Remote I/O error

If I remove/comment the line print(f"Auto cleaning interval: {pm_sensor.read_auto_cleaning_interval()}s")line I get readings as expected:

Do you have any thoughts on what might be happening?

Here is the code I'm using which is the example.py file included in the download:

import sys
import json
from time import sleep
from sps30 import SPS30

if name == "main":
pm_sensor = SPS30()
print(f"Firmware version: {pm_sensor.firmware_version()}")
print(f"Product type: {pm_sensor.product_type()}")
print(f"Serial number: {pm_sensor.serial_number()}")
print(f"Status register: {pm_sensor.read_status_register()}")
#print(f"Auto cleaning interval: {pm_sensor.read_auto_cleaning_interval()}s")
pm_sensor.start_measurement()

while True:
    try:
        print(json.dumps(pm_sensor.get_measurement(), indent=2))
        sleep(2)

    except KeyboardInterrupt:
        print("Stopping measurement...")
        pm_sensor.stop_measurement()
        sys.exit()

Thanks advance,
Walt

ieee754 conversion

Hi.

I think there is a problem with your ieee754 conversion function. I observed that particulate size smaller than 1 um is still displayed as 1+ um sized particle. We are talking about the case when the exponent is < 0. In that case the resulting number should be divided with the power of 2, of the absolute value of the exponent. I hope it is clear what i mean to say, and am i right. :) I modified the sps30.py (ieee754_number_conversion function) in the way seen below:

exp = 0 if exp < 0 else exp ------ would be

     divider = 0
     if (exp < 0):
         divider = abs(exp)
         exp = 0 

return round((((-1)**(sign) * real) + dec), 3) ------ would be

     if (divider == 0):
         return (round((((-1)**(sign) * real) + dec), 3))
     else:
         return ((round((((-1)**(sign) * real) + dec), 3) / pow(2, divider)))

Probably there is a more elegant way to do this, but it is working.
Regards,
Laszlo

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.