diff --git a/adafruit_pm25.py b/adafruit_pm25/__init__.py similarity index 65% rename from adafruit_pm25.py rename to adafruit_pm25/__init__.py index 7ee24a4..1a9ca45 100644 --- a/adafruit_pm25.py +++ b/adafruit_pm25/__init__.py @@ -43,11 +43,7 @@ """ -# imports -import time import struct -from adafruit_bus_device.i2c_device import I2CDevice -from digitalio import Direction __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PM25.git" @@ -117,72 +113,3 @@ def read(self): ) = frame return self.aqi_reading - - -class PM25_I2C(PM25): - """ - A driver for the PM2.5 Air quality sensor over I2C - """ - - def __init__(self, i2c_bus, reset_pin=None, address=0x12): - if reset_pin: - # Reset device - reset_pin.direction = Direction.OUTPUT - reset_pin.value = False - time.sleep(0.01) - reset_pin.value = True - # it takes at least a second to start up - time.sleep(1) - - for _ in range(5): # try a few times, it can be sluggish - try: - self.i2c_device = I2CDevice(i2c_bus, address) - break - except ValueError: - time.sleep(1) - continue - else: - raise RuntimeError("Unable to find PM2.5 device") - super().__init__() - - def _read_into_buffer(self): - with self.i2c_device as i2c: - try: - i2c.readinto(self._buffer) - except OSError: - raise RuntimeError("Unable to read from PM2.5 over I2C") - - -class PM25_UART(PM25): - """ - A driver for the PM2.5 Air quality sensor over UART - """ - - def __init__(self, uart, reset_pin=None): - if reset_pin: - # Reset device - reset_pin.direction = Direction.OUTPUT - reset_pin.value = False - time.sleep(0.01) - reset_pin.value = True - # it takes at least a second to start up - time.sleep(1) - - self._uart = uart - super().__init__() - - def _read_into_buffer(self): - while True: - b = self._uart.read(1) - if not b: - raise RuntimeError("Unable to read from PM2.5 (no start of frame)") - if b[0] == 0x42: - break - self._buffer[0] = b[0] # first byte and start of frame - - remain = self._uart.read(31) - if not remain or len(remain) != 31: - raise RuntimeError("Unable to read from PM2.5 (incomplete frame)") - for i in range(31): - self._buffer[i + 1] = remain[i] - # print([hex(i) for i in self._buffer]) diff --git a/adafruit_pm25/i2c.py b/adafruit_pm25/i2c.py new file mode 100644 index 0000000..d4b191b --- /dev/null +++ b/adafruit_pm25/i2c.py @@ -0,0 +1,84 @@ +# The MIT License (MIT) +# +# Copyright (c) 2020 ladyada for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_pm25.i2c` +================================================================================ + +I2C module for CircuitPython library for PM2.5 Air Quality Sensors + + +* Author(s): ladyada + +Implementation Notes +-------------------- + +**Hardware:** + +Works with most (any?) Plantower I2C interfaced PM2.5 sensor. + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases +* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice + +""" + +# imports +import time +from digitalio import Direction +from adafruit_bus_device.i2c_device import I2CDevice +from . import PM25 + + +class PM25_I2C(PM25): + """ + A module for using the PM2.5 Air quality sensor over I2C + """ + + def __init__(self, i2c_bus, reset_pin=None, address=0x12): + if reset_pin: + # Reset device + reset_pin.direction = Direction.OUTPUT + reset_pin.value = False + time.sleep(0.01) + reset_pin.value = True + # it takes at least a second to start up + time.sleep(1) + + for _ in range(5): # try a few times, it can be sluggish + try: + self.i2c_device = I2CDevice(i2c_bus, address) + break + except ValueError: + time.sleep(1) + continue + else: + raise RuntimeError("Unable to find PM2.5 device") + super().__init__() + + def _read_into_buffer(self): + with self.i2c_device as i2c: + try: + i2c.readinto(self._buffer) + except OSError as err: + raise RuntimeError("Unable to read from PM2.5 over I2C") from err diff --git a/adafruit_pm25/uart.py b/adafruit_pm25/uart.py new file mode 100644 index 0000000..1b91e05 --- /dev/null +++ b/adafruit_pm25/uart.py @@ -0,0 +1,82 @@ +# The MIT License (MIT) +# +# Copyright (c) 2020 ladyada for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_pm25.uart` +================================================================================ + +UART module for CircuitPython library for PM2.5 Air Quality Sensors + + +* Author(s): ladyada + +Implementation Notes +-------------------- + +**Hardware:** + +Works with most (any?) Plantower UART or I2C interfaced PM2.5 sensor. + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +""" + +import time +from digitalio import Direction +from . import PM25 + + +class PM25_UART(PM25): + """ + A driver for the PM2.5 Air quality sensor over UART + """ + + def __init__(self, uart, reset_pin=None): + if reset_pin: + # Reset device + reset_pin.direction = Direction.OUTPUT + reset_pin.value = False + time.sleep(0.01) + reset_pin.value = True + # it takes at least a second to start up + time.sleep(1) + + self._uart = uart + super().__init__() + + def _read_into_buffer(self): + while True: + b = self._uart.read(1) + if not b: + raise RuntimeError("Unable to read from PM2.5 (no start of frame)") + if b[0] == 0x42: + break + self._buffer[0] = b[0] # first byte and start of frame + + remain = self._uart.read(31) + if not remain or len(remain) != 31: + raise RuntimeError("Unable to read from PM2.5 (incomplete frame)") + for i in range(31): + self._buffer[i + 1] = remain[i] + # print([hex(i) for i in self._buffer]) diff --git a/docs/api.rst b/docs/api.rst index 21a29ed..e38e9c0 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -6,3 +6,9 @@ .. automodule:: adafruit_pm25 :members: + +.. automodule:: adafruit_pm25.i2c + :members: + +.. automodule:: adafruit_pm25.uart + :members: diff --git a/examples/pm25_simpletest.py b/examples/pm25_simpletest.py index 7faf202..bb53d27 100644 --- a/examples/pm25_simpletest.py +++ b/examples/pm25_simpletest.py @@ -7,7 +7,8 @@ import board import busio from digitalio import DigitalInOut, Direction, Pull -import adafruit_pm25 +from adafruit_pm25.i2c import PM25_I2C + reset_pin = None # If you have a GPIO, its not a bad idea to connect it to the RESET pin @@ -33,12 +34,13 @@ # uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=0.25) # Connect to a PM2.5 sensor over UART -# pm25 = adafruit_pm25.PM25_UART(uart, reset_pin) +# from adafruit_pm25.uart import PM25_UART +# pm25 = PM25_UART(uart, reset_pin) # Create library object, use 'slow' 100KHz frequency! i2c = busio.I2C(board.SCL, board.SDA, frequency=100000) # Connect to a PM2.5 sensor over I2C -pm25 = adafruit_pm25.PM25_I2C(i2c, reset_pin) +pm25 = PM25_I2C(i2c, reset_pin) print("Found PM2.5 sensor, reading data...") diff --git a/setup.py b/setup.py index 776c3ea..0d8f285 100644 --- a/setup.py +++ b/setup.py @@ -49,5 +49,5 @@ # simple. Or you can use find_packages(). # TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, # CHANGE `py_modules=['...']` TO `packages=['...']` - py_modules=["adafruit_pm25"], + packages=["adafruit_pm25"], )