Skip to content

Commit 2c6713d

Browse files
committed
Add SEN6x
1 parent 943b717 commit 2c6713d

File tree

4 files changed

+346
-0
lines changed

4 files changed

+346
-0
lines changed

platformio.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ lib_deps =
8080
https://github.com/pstolarz/OneWireNg.git
8181
https://github.com/Sensirion/arduino-sht.git
8282
https://github.com/Sensirion/arduino-i2c-sen5x.git
83+
https://github.com/Sensirion/arduino-i2c-sen66.git
8384
https://github.com/adafruit/WiFiNINA.git
8485
https://github.com/Starmbi/hp_BH1750.git
8586
https://github.com/adafruit/RTClib.git

src/components/i2c/controller.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,36 @@ static const std::map<std::string, FnCreateI2CDriver> I2cFactory = {
303303
const char *driver_name) -> drvBase * {
304304
return new drvSen5x(i2c, addr, mux_channel, driver_name);
305305
}},
306+
{"sen60",
307+
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
308+
const char *driver_name) -> drvBase * {
309+
return new drvSen6x(i2c, addr, mux_channel, driver_name);
310+
}},
311+
{"sen63c",
312+
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
313+
const char *driver_name) -> drvBase * {
314+
return new drvSen6x(i2c, addr, mux_channel, driver_name);
315+
}},
316+
{"sen65",
317+
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
318+
const char *driver_name) -> drvBase * {
319+
return new drvSen6x(i2c, addr, mux_channel, driver_name);
320+
}},
321+
{"sen66",
322+
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
323+
const char *driver_name) -> drvBase * {
324+
return new drvSen6x(i2c, addr, mux_channel, driver_name);
325+
}},
326+
{"sen68",
327+
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
328+
const char *driver_name) -> drvBase * {
329+
return new drvSen6x(i2c, addr, mux_channel, driver_name);
330+
}},
331+
{"sen6x",
332+
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
333+
const char *driver_name) -> drvBase * {
334+
return new drvSen6x(i2c, addr, mux_channel, driver_name);
335+
}},
306336
{"shtc3",
307337
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
308338
const char *driver_name) -> drvBase * {

src/components/i2c/controller.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include "drivers/drvScd30.h"
5555
#include "drivers/drvScd4x.h"
5656
#include "drivers/drvSen5x.h"
57+
#include "drivers/drvSen6x.h"
5758
#include "drivers/drvSgp30.h"
5859
#include "drivers/drvSgp40.h"
5960
#include "drivers/drvSht3x.h"

src/components/i2c/drivers/drvSen6x.h

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
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

Comments
 (0)