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