Skip to content

Commit 80bd08f

Browse files
drivers/sps30: add saul integration
1 parent 6ce6f0c commit 80bd08f

File tree

5 files changed

+283
-1
lines changed

5 files changed

+283
-1
lines changed

drivers/include/sps30.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
/**
1010
* @defgroup drivers_sps30 Sensirion SPS30 Particulate Matter Sensor
1111
* @ingroup drivers_sensors
12+
* @ingroup drivers_saul
1213
*
1314
* About
1415
* =====

drivers/sps30/include/sps30_params.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "board.h"
2121
#include "sps30.h"
22+
#include "saul_reg.h"
2223

2324
#ifdef __cplusplus
2425
extern "C" {
@@ -31,7 +32,6 @@ extern "C" {
3132
#ifndef SPS30_PARAM_I2C_DEV
3233
#define SPS30_PARAM_I2C_DEV (I2C_DEV(0))
3334
#endif
34-
3535
#ifndef SPS30_PARAMS
3636
#define SPS30_PARAMS { .i2c_dev = SPS30_PARAM_I2C_DEV }
3737
#endif
@@ -53,6 +53,19 @@ static const sps30_params_t sps30_params[] =
5353
*/
5454
#define SPS30_NUM ARRAY_SIZE(sps30_params)
5555

56+
/**
57+
* @brief Additional meta information to keep in the SAUL registry
58+
*/
59+
static const saul_reg_info_t sps30_saul_info[] =
60+
{
61+
SPS30_SAUL_INFO
62+
};
63+
64+
/**
65+
* @brief Number of saul info structs
66+
*/
67+
#define SPS30_INFO_NUM ARRAY_SIZE(sps30_saul_info)
68+
5669
#ifdef __cplusplus
5770
}
5871
#endif

drivers/sps30/sps30_saul.c

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* Copyright (C) 2020 HAW Hamburg
3+
*
4+
* This file is subject to the terms and conditions of the GNU Lesser
5+
* General Public License v2.1. See the file LICENSE in the top level
6+
* directory for more details.
7+
*/
8+
9+
/**
10+
* @ingroup drivers_sps30
11+
* @brief SAUL adaption for Sensirion SPS30 sensor
12+
* @author Michel Rottleuthner <[email protected]>
13+
* @file
14+
*/
15+
16+
#include <errno.h>
17+
#include <stdio.h>
18+
#include <string.h>
19+
20+
#include "phydat.h"
21+
#include "saul.h"
22+
#include "sps30.h"
23+
#include "sps30_params.h"
24+
25+
/**
26+
* @brief Mapping of sensor values to logical SAUL instances
27+
*/
28+
#define SPS30_SAUL_VAL_IDX_MC_PM_1_2P5_4 (0)
29+
#define SPS30_SAUL_VAL_IDX_MC_PM_10 (1)
30+
#define SPS30_SAUL_VAL_IDX_NC_PM_0P5_1_2P5 (2)
31+
#define SPS30_SAUL_VAL_IDX_NC_PM_4_10 (3)
32+
#define SPS30_SAUL_VAL_IDX_PS (4)
33+
34+
/**
35+
* @brief Number of logical saul devices per physical sensor
36+
*/
37+
#define SPS30_SAUL_DEV_NUM (SPS30_SAUL_VAL_IDX_PS + 1)
38+
39+
extern sps30_t sps30_devs[SPS30_NUM];
40+
41+
/* contains a temporary copy of all sensor readings to allow returning all
42+
* values as one consistent group over multiple read calls */
43+
static sps30_data_t _readings[SPS30_NUM];
44+
static bool _valid[SPS30_NUM][SPS30_SAUL_DEV_NUM] = { {false} };
45+
46+
static unsigned _dev2index (const sps30_t *dev)
47+
{
48+
for (unsigned i = 0; i < SPS30_NUM; i++) {
49+
if (dev == &sps30_devs[i]) {
50+
return i;
51+
}
52+
}
53+
return SPS30_NUM;
54+
}
55+
56+
static void _float_fit(float *src, phydat_t *data, size_t dim, uint32_t mul)
57+
{
58+
int32_t i32[dim];
59+
60+
for (unsigned i = 0; i < dim; i++) {
61+
i32[i] = src[i] * mul;
62+
}
63+
64+
data->scale = 0;
65+
phydat_fit(data, &i32[0], dim);
66+
}
67+
68+
static int read(const void *dev, phydat_t *data, unsigned int val_idx)
69+
{
70+
/* find the device index */
71+
unsigned dev_idx = _dev2index((sps30_t*)dev);
72+
73+
if (dev_idx == SPS30_NUM) {
74+
return -ECANCELED;
75+
}
76+
77+
/* if data isn't valid form the last reading anymore, read again */
78+
if (!_valid[dev_idx][val_idx]) {
79+
int error_state = SPS30_OK;
80+
while (!sps30_data_ready(&sps30_devs[dev_idx], &error_state)) {
81+
if (error_state != SPS30_OK) {
82+
return -ECANCELED;
83+
}
84+
}
85+
if (!(sps30_read_measurement(&sps30_devs[dev_idx],
86+
&_readings[dev_idx]) == SPS30_OK)) {
87+
/* failure on read may corrupt _readings -> mark invalid */
88+
for (unsigned i = 0; i < SPS30_SAUL_DEV_NUM; i++) {
89+
_valid[dev_idx][i] = false;
90+
}
91+
return -ECANCELED;
92+
}
93+
for (unsigned i = 0; i < SPS30_SAUL_DEV_NUM; i++) {
94+
_valid[dev_idx][i] = true;
95+
}
96+
}
97+
98+
/* mark read values as invalid */
99+
_valid[dev_idx][val_idx] = false;
100+
101+
switch (val_idx) {
102+
case SPS30_SAUL_VAL_IDX_MC_PM_1_2P5_4:
103+
_float_fit(&_readings[dev_idx].mc_pm1, data, 3, 1000);
104+
data->unit = UNIT_GPM3;
105+
data->scale -= 9; /* fitted [ng/m^3] but unit is [g/m^3] */
106+
return 3;
107+
case SPS30_SAUL_VAL_IDX_MC_PM_10:
108+
_float_fit(&_readings[dev_idx].mc_pm10, data, 1, 1000);
109+
data->unit = UNIT_GPM3;
110+
data->scale = -9; /* fitted [ng/m^3] but unit is [g/m^3] */
111+
return 1;
112+
case SPS30_SAUL_VAL_IDX_NC_PM_0P5_1_2P5:
113+
_float_fit(&_readings[dev_idx].nc_pm0_5, data, 3, 1000);
114+
data->unit = UNIT_CPM3;
115+
data->scale = 3; /* fitted [#/dm^3] but unit is [#/m^3] */
116+
return 3;
117+
case SPS30_SAUL_VAL_IDX_NC_PM_4_10:
118+
_float_fit(&_readings[dev_idx].nc_pm4, data, 2, 1000);
119+
data->unit = UNIT_CPM3;
120+
data->scale = 3; /* fitted [#/dm^3] but unit is [#/m^3] */
121+
return 2;
122+
case SPS30_SAUL_VAL_IDX_PS:
123+
_float_fit(&_readings[dev_idx].ps, data, 1, 1000);
124+
data->unit = UNIT_M;
125+
data->scale -= 9; /* fitted [nm] but unit is [m] */
126+
return 1;
127+
}
128+
129+
return -ECANCELED;
130+
}
131+
132+
static int read_mc_pm_1_2p5_4(const void *dev, phydat_t *data)
133+
{
134+
return read(dev, data, SPS30_SAUL_VAL_IDX_MC_PM_1_2P5_4);
135+
}
136+
137+
static int read_mc_pm_10(const void *dev, phydat_t *data)
138+
{
139+
return read(dev, data, SPS30_SAUL_VAL_IDX_MC_PM_10);
140+
}
141+
142+
static int read_nc_pm_0p5_1_2p5(const void *dev, phydat_t *data)
143+
{
144+
return read(dev, data, SPS30_SAUL_VAL_IDX_NC_PM_0P5_1_2P5);
145+
}
146+
147+
static int read_nc_pm_4_10(const void *dev, phydat_t *data)
148+
{
149+
return read(dev, data, SPS30_SAUL_VAL_IDX_NC_PM_4_10);
150+
}
151+
152+
static int read_ps(const void *dev, phydat_t *data)
153+
{
154+
return read(dev, data, SPS30_SAUL_VAL_IDX_PS);
155+
}
156+
157+
const saul_driver_t sps30_saul_driver_mc_pm_1_2p5_4 = {
158+
.read = read_mc_pm_1_2p5_4,
159+
.write = saul_notsup,
160+
.type = SAUL_SENSE_PM
161+
};
162+
163+
const saul_driver_t sps30_saul_driver_mc_pm_10 = {
164+
.read = read_mc_pm_10,
165+
.write = saul_notsup,
166+
.type = SAUL_SENSE_PM
167+
};
168+
169+
const saul_driver_t sps30_saul_driver_nc_pm_0p5_1_2p5 = {
170+
.read = read_nc_pm_0p5_1_2p5,
171+
.write = saul_notsup,
172+
.type = SAUL_SENSE_COUNT
173+
};
174+
175+
const saul_driver_t sps30_saul_driver_nc_pm_4_10 = {
176+
.read = read_nc_pm_4_10,
177+
.write = saul_notsup,
178+
.type = SAUL_SENSE_COUNT
179+
};
180+
181+
const saul_driver_t sps30_saul_driver_ps = {
182+
.read = read_ps,
183+
.write = saul_notsup,
184+
.type = SAUL_SENSE_SIZE
185+
};

sys/auto_init/auto_init.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,10 @@ void auto_init(void)
563563
extern void auto_init_si70xx(void);
564564
auto_init_si70xx();
565565
#endif
566+
#ifdef MODULE_SPS30
567+
extern void auto_init_sps30(void);
568+
auto_init_sps30();
569+
#endif
566570
#ifdef MODULE_TCS37727
567571
extern void auto_init_tcs37727(void);
568572
auto_init_tcs37727();
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (C) 2020 HAW Hamburg
3+
*
4+
* This file is subject to the terms and conditions of the GNU Lesser
5+
* General Public License v2.1. See the file LICENSE in the top level
6+
* directory for more details.
7+
*/
8+
9+
/**
10+
* @ingroup sys_auto_init_saul
11+
* @brief Auto initialization of Sensirion SPS30 device driver
12+
* @author Michel Rottleuthner <[email protected]>
13+
* @file
14+
*/
15+
16+
#ifdef MODULE_SPS30
17+
18+
#include "assert.h"
19+
#include "log.h"
20+
#include "saul_reg.h"
21+
#include "sps30.h"
22+
#include "sps30_params.h"
23+
24+
/**
25+
* @brief Number of logical saul devices per physical sensor
26+
*/
27+
#define SPS30_SAUL_DEV_NUM (5)
28+
29+
/**
30+
* @brief Allocation of memory for device descriptors
31+
*/
32+
sps30_t sps30_devs[SPS30_NUM];
33+
34+
/**
35+
* @brief Memory for the SAUL registry entries
36+
*/
37+
static saul_reg_t saul_entries[SPS30_NUM * SPS30_SAUL_DEV_NUM];
38+
39+
/**
40+
* @name Reference the driver structs.
41+
* @{
42+
*/
43+
extern const saul_driver_t sps30_saul_driver_mc_pm_1_2p5_4;
44+
extern const saul_driver_t sps30_saul_driver_mc_pm_10;
45+
extern const saul_driver_t sps30_saul_driver_nc_pm_0p5_1_2p5;
46+
extern const saul_driver_t sps30_saul_driver_nc_pm_4_10;
47+
extern const saul_driver_t sps30_saul_driver_ps;
48+
/** @} */
49+
50+
void auto_init_sps30(void)
51+
{
52+
assert(SPS30_INFO_NUM == SPS30_NUM);
53+
54+
for (unsigned i = 0; i < SPS30_NUM; i++) {
55+
LOG_DEBUG("[auto_init_saul] initializing sps30 #%u\n", i);
56+
57+
if (sps30_init(&sps30_devs[i],
58+
&sps30_params[i]) != SPS30_OK) {
59+
LOG_ERROR("[auto_init_saul] error initializing sps30 #%u\n", i);
60+
continue;
61+
}
62+
63+
saul_entries[i * 5].driver = &sps30_saul_driver_mc_pm_1_2p5_4;
64+
saul_entries[i * 5 + 1].driver = &sps30_saul_driver_mc_pm_10;
65+
saul_entries[i * 5 + 2].driver = &sps30_saul_driver_nc_pm_0p5_1_2p5;
66+
saul_entries[i * 5 + 3].driver = &sps30_saul_driver_nc_pm_4_10;
67+
saul_entries[i * 5 + 4].driver = &sps30_saul_driver_ps;
68+
69+
/* the physical device is the same for all logical SAUL instances */
70+
for (unsigned x = 0; x < SPS30_SAUL_DEV_NUM; x++) {
71+
saul_entries[i * 5 + x].dev = &(sps30_devs[i]);
72+
saul_entries[i * 5 + x].name = sps30_saul_info[i].name;
73+
saul_reg_add(&saul_entries[i * 5 + x]);
74+
}
75+
}
76+
}
77+
#else
78+
typedef int dont_be_pedantic;
79+
#endif /* MODULE_SPS30 */

0 commit comments

Comments
 (0)