From 1190bc18d3203f7505684d9b3176aab65db94b8f Mon Sep 17 00:00:00 2001 From: Filip Dobrovolny Date: Sat, 22 Apr 2023 13:31:14 +0200 Subject: [PATCH] First version --- README.md | 129 ++++++++++++++++++++++++++++++++++++++++- setup.py | 32 ++++++++++ src/mh_zxx/__init__.py | 5 ++ src/mh_zxx/base.py | 124 +++++++++++++++++++++++++++++++++++++++ src/mh_zxx/version.py | 1 + 5 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 setup.py create mode 100644 src/mh_zxx/__init__.py create mode 100644 src/mh_zxx/base.py create mode 100644 src/mh_zxx/version.py diff --git a/README.md b/README.md index c6989cc..cf489d0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,127 @@ -# mh-z16 -Lightweight python library for interactions with MH-Z16 Intelligent Infrared Gas Module CO2 +# mh-zxx +Lightweight python library for interactions with MH-Z16, MH-Z19 and similar Intelligent Infrared Gas Module CO2 + +# MH-ZXX CO2 Sensor API + +* [CO2Sensor](#mh_zxx.base.CO2Sensor) + * [\_\_init\_\_](#mh_zxx.base.CO2Sensor.__init__) + * [read\_co2](#mh_zxx.base.CO2Sensor.read_co2) + * [read\_temperature](#mh_zxx.base.CO2Sensor.read_temperature) + * [read\_status](#mh_zxx.base.CO2Sensor.read_status) + * [read](#mh_zxx.base.CO2Sensor.read) + * [zero\_calibration](#mh_zxx.base.CO2Sensor.zero_calibration) + * [span\_calibration](#mh_zxx.base.CO2Sensor.span_calibration) + * [set\_auto\_calibration](#mh_zxx.base.CO2Sensor.set_auto_calibration) + * [close](#mh_zxx.base.CO2Sensor.close) + + + +## CO2Sensor + +```python +class CO2Sensor() +``` + +MH-Z16 and MH-Z19 CO2 sensor. + + + +#### \_\_init\_\_ + +```python +def __init__(port: str, baudrate: int = 9600, timeout: float = 0.2) +``` + +Setup CO2 sensor + +**Arguments**: + +- `port`: The serial port name, for example `/dev/ttyUSB0` (Linux), +`/dev/tty.usbserial` (OS X) or `COM4` (Windows). +- `baudrate`: Baudrate to use. +- `timeout`: Read timeout after each command. + + + +#### read\_co2 + +```python +def read_co2() -> int +``` + +Read CO2 concentration. + + + +#### read\_temperature + +```python +def read_temperature() -> float +``` + +Read temperature. + + + +#### read\_status + +```python +def read_status() -> int +``` + +Read status. + + + +#### read + +```python +def read() -> Tuple[int, float, int, int] +``` + +Read all values. + +**Returns**: + +CO2 concentration, temperature, status, co2 auto calibration point + + + +#### zero\_calibration + +```python +def zero_calibration() +``` + +Zero calibration. + + + +#### span\_calibration + +```python +def span_calibration(span: int) +``` + +Span calibration. + + + +#### set\_auto\_calibration + +```python +def set_auto_calibration(state: bool) +``` + +Set auto calibration point. + + + +#### close + +```python +def close() +``` + +Close serial port. + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..dfec6be --- /dev/null +++ b/setup.py @@ -0,0 +1,32 @@ +"""Install packages as defined in this file into the Python environment.""" +from setuptools import setup, find_namespace_packages + + +# The version of this tool is based on the following steps: +# https://packaging.python.org/guides/single-sourcing-package-version/ +VERSION = {} + +with open("./src/mh_zxx/version.py") as fp: + # pylint: disable=W0122 + exec(fp.read(), VERSION) + +setup( + name="mh_zxx", + author="Filip Dobrovolny", + author_email="dobrovolny.filip@gmail.com", + url="https://github.com/fdobrovolny/mh-zxx", + description="Python library for interaction with MH-Z16, MH-Z19 and similar.", + version=VERSION.get("__version__", "0.0.0"), + package_dir={"": "src"}, + packages=find_namespace_packages(where="src", exclude=["tests"]), + install_requires=[ + "setuptools>=45.0", + "pyserial>=3.5", + ], + classifiers=[ + "Development Status :: 1 - Planning", + "Programming Language :: Python :: 3.0", + "Topic :: Utilities", + "Topic :: System :: Hardware", + ], +) diff --git a/src/mh_zxx/__init__.py b/src/mh_zxx/__init__.py new file mode 100644 index 0000000..c357ba2 --- /dev/null +++ b/src/mh_zxx/__init__.py @@ -0,0 +1,5 @@ +from mh_z16.base import CO2Sensor +from mh_z16.version import __version__ + + +__all__ = ["CO2Sensor", "__version__"] diff --git a/src/mh_zxx/base.py b/src/mh_zxx/base.py new file mode 100644 index 0000000..eeae9d7 --- /dev/null +++ b/src/mh_zxx/base.py @@ -0,0 +1,124 @@ +import logging +from typing import List, Tuple + +from serial import Serial + + +logger = logging.getLogger(__name__) + + +class CO2Sensor: + """ + MH-Z16 and MH-Z19 CO2 sensor. + """ + + serial: Serial + + def __init__( + self, + port: str, + baudrate: int = 9600, + timeout: float = 0.2, + ): + """ + Setup CO2 sensor + + :param port: The serial port name, for example `/dev/ttyUSB0` (Linux), + `/dev/tty.usbserial` (OS X) or `COM4` (Windows). + :param baudrate: Baudrate to use. + :param timeout: Read timeout after each command. + """ + self.serial = Serial(port=port, baudrate=baudrate, timeout=timeout) + self.serial.open() + + READ_CO2 = [0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00] + ZERO_CALIBRATION = [0xff, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00] + SPAN_CALIBRATION = [0xff, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00] + AUTO_CALIBRATION = [0xff, 0x01, 0x79, 0xa0, 0x00, 0x00, 0x00, 0x00] + AUTO_CALIBRATION_OFF = [0xff, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00] + + @staticmethod + def _checksum(data: bytes | List[int]) -> int: + """ + Calculate _checksum of the command. + """ + return 0xff - (sum(data) % 256) + 1 + + def _write_command(self, command: List[int]): + """ + Write command to the sensor. + """ + command.append(self._checksum(command)) + self.serial.write(command) + + def _read_response(self) -> bytes: + """ + Read response from the sensor. + """ + return self.serial.read(9) + + def read_co2(self) -> int: + """ + Read CO2 concentration. + """ + return self.read()[0] + + def read_temperature(self) -> float: + """ + Read temperature. + """ + return self.read()[1] + + def read_status(self) -> int: + """ + Read status. + """ + return self.read()[2] + + def read(self) -> Tuple[int, float, int, int]: + """ + Read all values. + + :note: last value is auto calibration point, see + :link: https://revspace.nl/MH-Z19#Command_set + + :return: CO2 concentration, temperature, status, co2 auto calibration point + """ + self._write_command(self.READ_CO2) + response = self._read_response() + if response[0] != 0xff or response[1] != 0x86: + raise ValueError("Invalid response") + elif response[8] != self._checksum(response[:8]): + raise ValueError("Invalid checksum") + + return response[2] * 256 + response[3], response[4] - 40, response[5], response[6] + + def zero_calibration(self): + """ + Zero calibration. + """ + self._write_command(self.ZERO_CALIBRATION) + + def span_calibration(self, span: int): + """ + Span calibration. + """ + command = self.SPAN_CALIBRATION.copy() + command[3] = span // 256 + command[4] = span % 256 + self._write_command(command) + + def set_auto_calibration(self, state: bool): + """ + Set auto calibration point. + """ + if state: + self._write_command(self.AUTO_CALIBRATION) + else: + self._write_command(self.AUTO_CALIBRATION_OFF) + + def close(self): + """ + Close serial port. + """ + self.serial.close() diff --git a/src/mh_zxx/version.py b/src/mh_zxx/version.py new file mode 100644 index 0000000..f102a9c --- /dev/null +++ b/src/mh_zxx/version.py @@ -0,0 +1 @@ +__version__ = "0.0.1"