27
27
28
28
"""
29
29
30
+ try :
31
+ from busio import I2C
32
+ from typing_extensions import NoReturn
33
+ except ImportError :
34
+ pass
35
+
30
36
import time
31
37
import struct
32
38
from micropython import const
44
50
_AGS02MA_CRC8_POLYNOMIAL = const (0x31 )
45
51
46
52
47
- def _generate_crc (data ) :
53
+ def _generate_crc (data : int ) -> int :
48
54
"""8-bit CRC algorithm for checking data
49
55
50
56
:param bytearray data: The data to generate a CRC for
@@ -64,13 +70,45 @@ def _generate_crc(data):
64
70
65
71
66
72
class AGS02MA :
67
- """Driver for the AGS02MA air quality sensor
73
+ """Driver for the AGS02MA air quality sensor.
74
+
75
+ .. warning::
76
+ I2C communication rate cannot be higher than 30KHZ
68
77
69
78
:param ~busio.I2C i2c_bus: The I2C bus the AGS02MA is connected to.
70
79
:param int address: The I2C device address. Defaults to :const:`0x1A`
80
+
81
+ :raises RunTimeError: When the sensor could not be found
82
+
83
+
84
+ **Quickstart: Importing and using the device**
85
+
86
+ Here is an example of using the :class:`AGS02MA` class.
87
+ First you will need to import the libraries to use the sensor
88
+
89
+ .. code-block:: python
90
+
91
+ import board
92
+ import busio
93
+ from adafruit_ags02ma import AGS02MA
94
+
95
+ Once this is done you can define your `busio.I2C` object and define your sensor object
96
+
97
+ .. code-block:: python
98
+
99
+ i2c = busio.I2C(board.SCL, board.SDA, frequency=20_000)
100
+ ags = AGS02MA(i2c, address=0x1A)
101
+
102
+ Now you have access to the :attr:`gas_resistance` and :attr:`TVOC` attributes
103
+
104
+ .. code-block:: python
105
+
106
+ res = ags.gas_resistance
107
+ tvoc = ags.TVOC
108
+
71
109
"""
72
110
73
- def __init__ (self , i2c_bus , address = AGS02MA_I2CADDR_DEFAULT ):
111
+ def __init__ (self , i2c_bus : I2C , address : int = AGS02MA_I2CADDR_DEFAULT ) -> None :
74
112
self .i2c_device = i2c_device .I2CDevice (i2c_bus , address )
75
113
76
114
self ._buf = bytearray (5 )
@@ -80,30 +118,40 @@ def __init__(self, i2c_bus, address=AGS02MA_I2CADDR_DEFAULT):
80
118
except RuntimeError as exc : # a CRC error or something!
81
119
raise RuntimeError ("Failed to find AGS02MA - check your wiring!" ) from exc
82
120
83
- def firmware_version (self ):
121
+ def firmware_version (self ) -> int :
84
122
"""Return 24-bit value which contains the firmware version"""
85
123
return self ._read_reg (_AGS02MA_VERSION_REG , 30 )
86
124
87
125
@property
88
- def gas_resistance (self ):
126
+ def gas_resistance (self ) -> float :
89
127
"""The resistance of the MEMS gas sensor"""
90
128
return self ._read_reg (_AGS02MA_GASRES_REG , 1500 ) * 100 # in 0.1Kohm
91
129
92
130
@property
93
- def TVOC (self ): # pylint: disable=invalid-name
94
- """The calculated Total Volatile Organic Compound measurement, in ppb"""
131
+ def TVOC (self ) -> int : # pylint: disable=invalid-name
132
+ """The calculated Total Volatile Organic Compound measurement, in ppb
133
+
134
+ :raises RunTimeError: When the sensor still preheating
135
+ """
95
136
val = self ._read_reg (_AGS02MA_TVOCSTAT_REG , 1500 )
96
137
status = val >> 24
97
- # print(hex(status))
138
+
98
139
if status & 0x1 :
99
140
raise RuntimeError ("Sensor still preheating" )
100
141
return val & 0xFFFFFF
101
142
102
- def set_address (self , new_addr ) :
143
+ def set_address (self , new_addr : int ) -> NoReturn :
103
144
"""Set the address for the I2C interface, from 0x0 to 0x7F
145
+ The sensor supports modifying the I2C address. After sending
146
+ this command, the new address will take effect immediately,
147
+ New address will remain after powering the sensor off.
104
148
105
- :param int new_addr: THe new address
149
+ :param int new_addr: The new address
150
+
151
+ :raises: ValueError: When selected address is not in the range 0x00-0x7F
106
152
"""
153
+ if new_addr not in range (1 , 128 ):
154
+ raise ValueError ("Selected address must be between 0x00 and 0x7F" )
107
155
108
156
_buf = bytearray (
109
157
[
@@ -119,21 +167,24 @@ def set_address(self, new_addr):
119
167
with self .i2c_device as i2c :
120
168
i2c .write (_buf )
121
169
122
- def _read_reg (self , addr , delayms ) :
170
+ def _read_reg (self , addr : int , delayms : int ) -> int :
123
171
"""Read a register
124
172
125
173
:param int addr: The address to read
126
174
:param int delayms: The delay between writes and reads, in milliseconds
175
+
176
+ :raises RunTimeError: When CRC check have failed.
177
+
127
178
"""
128
179
129
180
with self .i2c_device as i2c :
130
181
self ._addr [0 ] = addr
131
182
i2c .write (self ._addr )
132
183
time .sleep (delayms / 1000 )
133
184
i2c .readinto (self ._buf )
134
- # print([hex(x) for x in self._buf])
185
+
135
186
if _generate_crc (self ._buf ) != 0 :
136
187
raise RuntimeError ("CRC check failed" )
137
188
val , crc = struct .unpack (">IB" , self ._buf ) # pylint: disable=unused-variable
138
- # print(hex(val), hex(crc))
189
+
139
190
return val
0 commit comments