1
+ /* !
2
+ * @file drvSen6x.h
3
+ *
4
+ * Device driver for the SEN66 Particulate Matter, Temperature, Humidity, VOC,
5
+ * NOX, and CO2 sensor.
6
+ *
7
+ * Adafruit invests time and resources providing this open source code,
8
+ * please support Adafruit and open-source hardware by purchasing
9
+ * products from Adafruit!
10
+ *
11
+ * Copyright (c) Tyeth Gundry 2022 for Adafruit Industries.
12
+ * Modified (c) by Martin Ebner 2024 https://github.com/MartinEbnerSensirion
13
+ *
14
+ * MIT license, all text here must be included in any redistribution.
15
+ *
16
+ */
17
+
18
+ #ifndef DRV_SEN6X_H
19
+ #define DRV_SEN6X_H
20
+
21
+ #include " drvBase.h"
22
+ #include < SensirionI2cSen66.h>
23
+ #include < Wire.h>
24
+
25
+ /* *************************************************************************/
26
+ /* !
27
+ @brief Class that provides a driver interface for the SEN6X sensor.
28
+ */
29
+ /* *************************************************************************/
30
+ class drvSen6x : public drvBase {
31
+
32
+ const float OVERFLOW_SEN6X = (0xFFFF / 10 ); // maxes out at u_int16 / 10
33
+
34
+ public:
35
+ /* ******************************************************************************/
36
+ /* !
37
+ @brief Constructor for a SEN6X sensor.
38
+ @param i2c
39
+ The I2C interface.
40
+ @param sensorAddress
41
+ 7-bit device address.
42
+ @param mux_channel
43
+ The I2C multiplexer channel.
44
+ @param driver_name
45
+ The name of the driver.
46
+ */
47
+ /* ******************************************************************************/
48
+ drvSen6x (TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel,
49
+ const char *driver_name)
50
+ : drvBase(i2c, sensorAddress, mux_channel, driver_name) {
51
+ _massConcentrationPm1p0 = NAN;
52
+ _massConcentrationPm2p5 = NAN;
53
+ _massConcentrationPm4p0 = NAN;
54
+ _massConcentrationPm10p0 = NAN;
55
+ _ambientHumidity = NAN;
56
+ _ambientTemperature = NAN;
57
+ _vocIndex = NAN;
58
+ _noxIndex = NAN;
59
+ _co2 = 0uL;
60
+ }
61
+
62
+ /* ******************************************************************************/
63
+ /* !
64
+ @brief Initializes the SEN6X sensor and begins I2C.
65
+ @returns True if initialized successfully, False otherwise.
66
+ */
67
+ /* ******************************************************************************/
68
+ bool begin () {
69
+ _sen = new SensirionI2cSen66 ();
70
+ _sen->begin (*_i2c, (uint8_t )_address);
71
+ u_int16_t error_stop = _sen->deviceReset ();
72
+ if (error_stop != 0 ) {
73
+ return false ;
74
+ }
75
+ // Wait 1 second for sensors to start recording + 100ms for reset command
76
+ delay (1100 );
77
+ u_int16_t error_start = _sen->startContinuousMeasurement ();
78
+ if (error_start != 0 ) {
79
+ return false ;
80
+ }
81
+
82
+ return true ;
83
+ }
84
+
85
+ /* ******************************************************************************/
86
+ /* !
87
+ @brief Checks if sensor was read within last 1s, or is the first read.
88
+ @returns True if the sensor was recently read, False otherwise.
89
+ */
90
+ bool HasBeenReadInLastSecond () {
91
+ return _lastRead != 0 && millis () - _lastRead < 1000 ;
92
+ }
93
+
94
+ /* ******************************************************************************/
95
+ /* !
96
+ @brief Checks if the sensor is ready to be read
97
+ @returns True if the sensor is ready, False otherwise.
98
+ */
99
+ /* ******************************************************************************/
100
+ bool IsSensorReady () {
101
+ bool isDataReady = false ;
102
+ uint8_t padding = 0x0 ;
103
+ for (int i = 0 ; i < 2 ; i++) {
104
+ uint16_t error = _sen->getDataReady (padding, isDataReady);
105
+ if (error == 0 && isDataReady) {
106
+ return true ;
107
+ }
108
+ delay (100 );
109
+ }
110
+ return false ;
111
+ }
112
+
113
+ /* ******************************************************************************/
114
+ /* !
115
+ @brief Reads the sensor.
116
+ @returns True if the sensor was read successfully, False otherwise.
117
+ */
118
+ /* ******************************************************************************/
119
+ bool ReadSensorData () {
120
+ // dont read sensor more than once per second
121
+ if (HasBeenReadInLastSecond ()) {
122
+ return true ;
123
+ }
124
+
125
+ if (!IsSensorReady ()) {
126
+ return false ;
127
+ }
128
+
129
+ uint16_t error = _sen->readMeasuredValues (
130
+ _massConcentrationPm1p0, _massConcentrationPm2p5,
131
+ _massConcentrationPm4p0, _massConcentrationPm10p0, _ambientHumidity,
132
+ _ambientTemperature, _vocIndex, _noxIndex, _co2);
133
+ if (error != 0 ) {
134
+ return false ;
135
+ }
136
+ _lastRead = millis ();
137
+ return true ;
138
+ }
139
+
140
+ /* ******************************************************************************/
141
+ /* !
142
+ @brief Gets the SEN6X's current temperature.
143
+ @param tempEvent
144
+ Pointer to an Adafruit_Sensor event.
145
+ @returns True if the temperature was obtained successfully, False
146
+ otherwise.
147
+ */
148
+ /* ******************************************************************************/
149
+ bool getEventAmbientTemp (sensors_event_t *tempEvent) {
150
+ if (!ReadSensorData () || _ambientTemperature == NAN) {
151
+ return false ;
152
+ }
153
+ tempEvent->temperature = _ambientTemperature;
154
+ return true ;
155
+ }
156
+
157
+ /* ******************************************************************************/
158
+ /* !
159
+ @brief Gets the SEN6X's current relative humidity reading.
160
+ @param humidEvent
161
+ Pointer to an Adafruit_Sensor event.
162
+ @returns True if the humidity was obtained successfully, False
163
+ otherwise.
164
+ */
165
+ /* ******************************************************************************/
166
+ bool getEventRelativeHumidity (sensors_event_t *humidEvent) {
167
+ if (!ReadSensorData () || _ambientHumidity == NAN) {
168
+ return false ;
169
+ }
170
+ humidEvent->relative_humidity = _ambientHumidity;
171
+ return true ;
172
+ }
173
+
174
+ /* ******************************************************************************/
175
+ /* !
176
+ @brief Gets the SEN6X's current NOX reading.
177
+ Note: If this value is unknown, which is true for SEN54,
178
+ NAN is returned. During the first 10..11 seconds after
179
+ power-on or device reset, this value will be NAN as well.
180
+ @param noxIndexEvent
181
+ Adafruit Sensor event for NOx Index (0-500, 1 is normal)
182
+ @returns True if the sensor value was obtained successfully, False
183
+ otherwise.
184
+ */
185
+ /* ******************************************************************************/
186
+ bool getEventNOxIndex (sensors_event_t *noxIndexEvent) {
187
+ if (!ReadSensorData () || _noxIndex == NAN) {
188
+ return false ;
189
+ }
190
+ noxIndexEvent->nox_index = _noxIndex;
191
+ return true ;
192
+ }
193
+
194
+ /* ******************************************************************************/
195
+ /* !
196
+ @brief Gets the SEN6X's current VOC reading.
197
+ @param vocIndexEvent
198
+ Adafruit Sensor event for VOC Index (1-500, 100 is normal)
199
+ @returns True if the sensor value was obtained successfully, False
200
+ otherwise.
201
+ */
202
+ /* ******************************************************************************/
203
+ bool getEventVOCIndex (sensors_event_t *vocIndexEvent) {
204
+ if (!ReadSensorData () || _vocIndex == NAN) {
205
+ return false ;
206
+ }
207
+ vocIndexEvent->voc_index = _vocIndex;
208
+ return true ;
209
+ }
210
+
211
+ /* ******************************************************************************/
212
+ /* !
213
+ @brief Gets the SEN6X sensor's PM1.0 STD reading.
214
+ @param pm10StdEvent
215
+ Adafruit Sensor event for PM1.0
216
+ @returns True if the sensor value was obtained successfully, False
217
+ otherwise.
218
+ */
219
+ /* ******************************************************************************/
220
+ bool getEventPM10_STD (sensors_event_t *pm10StdEvent) {
221
+ if (!ReadSensorData () || _massConcentrationPm1p0 == NAN ||
222
+ _massConcentrationPm1p0 == OVERFLOW_SEN6X) {
223
+ return false ;
224
+ }
225
+ pm10StdEvent->pm10_std = _massConcentrationPm1p0;
226
+ return true ;
227
+ }
228
+
229
+ /* ******************************************************************************/
230
+ /* !
231
+ @brief Gets the SEN6X sensor's PM2.5 STD reading.
232
+ @param pm25StdEvent
233
+ Adafruit Sensor event for PM2.5
234
+ @returns True if the sensor value was obtained successfully, False
235
+ otherwise.
236
+ */
237
+ /* ******************************************************************************/
238
+ bool getEventPM25_STD (sensors_event_t *pm25StdEvent) {
239
+ if (!ReadSensorData () || _massConcentrationPm2p5 == NAN ||
240
+ _massConcentrationPm2p5 == OVERFLOW_SEN6X) {
241
+ return false ;
242
+ }
243
+ pm25StdEvent->pm25_std = _massConcentrationPm2p5;
244
+ return true ;
245
+ }
246
+
247
+ /* ******************************************************************************/
248
+ /* !
249
+ @brief Gets the SEN6X sensor's PM4.0 STD reading.
250
+ @param pm40StdEvent
251
+ Adafruit Sensor event for PM4.0
252
+ @returns True if the sensor value was obtained successfully, False
253
+ otherwise.
254
+ */
255
+ /* ******************************************************************************/
256
+ bool getEventPM40_STD (sensors_event_t *pm40StdEvent) {
257
+ if (!ReadSensorData () || _massConcentrationPm4p0 == NAN ||
258
+ _massConcentrationPm4p0 == OVERFLOW_SEN6X) {
259
+ return false ;
260
+ }
261
+ pm40StdEvent->data [0 ] = _massConcentrationPm4p0;
262
+ return true ;
263
+ }
264
+
265
+ /* ******************************************************************************/
266
+ /* !
267
+ @brief Gets the SEN6X sensor's PM10.0 STD reading.
268
+ @param pm100StdEvent
269
+ Adafruit Sensor event for PM10.0
270
+ @returns True if the sensor value was obtained successfully, False
271
+ otherwise.
272
+ */
273
+ /* ******************************************************************************/
274
+ bool getEventPM100_STD (sensors_event_t *pm100StdEvent) {
275
+ if (!ReadSensorData () || _massConcentrationPm10p0 == NAN ||
276
+ _massConcentrationPm10p0 == OVERFLOW_SEN6X) {
277
+ return false ;
278
+ }
279
+ pm100StdEvent->pm100_std = _massConcentrationPm10p0;
280
+ return true ;
281
+ }
282
+
283
+ /* ******************************************************************************/
284
+ /* !
285
+ @brief Gets the SEN6X sensor's CO2 reading.
286
+ @param co2Event
287
+ Adafruit Sensor event for CO2
288
+ @returns True if the sensor value was obtained successfully, False
289
+ otherwise.
290
+ */
291
+ /* ******************************************************************************/
292
+ bool getEventCO2 (sensors_event_t *co2Event) {
293
+ if (!ReadSensorData () || _co2 == 0xFFFF ) {
294
+ return false ;
295
+ }
296
+ co2Event->CO2 = _co2;
297
+ return true ;
298
+ }
299
+
300
+ protected:
301
+ SensirionI2cSen66 *_sen = nullptr ; // /< SEN6X driver object
302
+ float _massConcentrationPm1p0; // /< PM1.0 mass concentration
303
+ float _massConcentrationPm2p5; // /< PM2.5 mass concentration
304
+ float _massConcentrationPm4p0; // /< PM4.0 mass concentration
305
+ float _massConcentrationPm10p0; // /< PM10.0 mass concentration
306
+ float _ambientHumidity; // /< Ambient humidity
307
+ float _ambientTemperature; // /< Ambient temperature
308
+ float _vocIndex; // /< VOC index
309
+ float _noxIndex; // /< NOx index
310
+ uint16_t _co2; // /< CO2 value
311
+ ulong _lastRead; // /< Last time the sensor was read
312
+ };
313
+
314
+ #endif // DRV_SEN6X_H
0 commit comments