3 #define _TWR_SPS30_DELAY_RUN 100
4 #define _TWR_SPS30_DELAY_INITIALIZE 1000
5 #define _TWR_SPS30_DELAY_READ 30
6 #define _TWR_SPS30_DELAY_MEASUREMENT 250
8 #define TWR_SPS30_NUM_WORDS(x) (sizeof(x) / 2)
10 #define be16_to_cpu(s) (((uint16_t)(s) << 8) | (0xff & ((uint16_t)(s)) >> 8))
11 #define be32_to_cpu(s) (((uint32_t)be16_to_cpu(s) << 16) | (0xffff & (be16_to_cpu((s) >> 16))))
13 static void _twr_sps30_task_interval(
void *param);
15 static void _twr_sps30_task_measure(
void *param);
17 static uint8_t _twr_sps30_calculate_crc(uint8_t *buffer,
size_t length);
18 static bool _twr_sps30_convert_to_words(uint8_t *buffer,
size_t buffer_length, uint16_t *data,
size_t data_length);
22 memset(
self, 0,
sizeof(*
self));
24 self->_i2c_channel = i2c_channel;
25 self->_i2c_address = i2c_address;
30 self->_state = TWR_SPS30_STATE_INITIALIZE;
37 self->_event_handler = event_handler;
38 self->_event_param = event_param;
43 self->_startup_time = startup_time;
48 self->_update_interval = interval;
62 if (self->_state == TWR_SPS30_STATE_READY)
64 self->_state = TWR_SPS30_STATE_START_MEASUREMENT;
77 if (!self->_measurement_valid)
82 mass_concentration->mc_1p0 =
self->_mass_concentration.mc_1p0;
83 mass_concentration->mc_2p5 =
self->_mass_concentration.mc_2p5;
84 mass_concentration->mc_4p0 =
self->_mass_concentration.mc_4p0;
85 mass_concentration->mc_10p0 =
self->_mass_concentration.mc_10p0;
92 if (!self->_measurement_valid)
97 number_concentration->nc_0p5 =
self->_number_concentration.nc_0p5;
98 number_concentration->nc_1p0 =
self->_number_concentration.nc_1p0;
99 number_concentration->nc_2p5 =
self->_number_concentration.nc_2p5;
100 number_concentration->nc_4p0 =
self->_number_concentration.nc_4p0;
101 number_concentration->nc_10p0 =
self->_number_concentration.nc_10p0;
108 if (!self->_measurement_valid)
113 *typical_particle_size =
self->_typical_particle_size;
118 static void _twr_sps30_task_interval(
void *param)
127 static void _twr_sps30_task_measure(
void *param)
133 switch (self->_state)
135 case TWR_SPS30_STATE_ERROR:
137 self->_measurement_valid =
false;
139 if (self->_event_handler != NULL)
144 self->_state = TWR_SPS30_STATE_INITIALIZE;
148 case TWR_SPS30_STATE_READY:
152 case TWR_SPS30_STATE_INITIALIZE:
154 self->_state = TWR_SPS30_STATE_GET_SERIAL_NUMBER;
158 case TWR_SPS30_STATE_GET_SERIAL_NUMBER:
160 self->_state = TWR_SPS30_STATE_ERROR;
162 static const uint8_t buffer[] = { 0xd0, 0x33 };
167 transfer.
buffer = (uint8_t *) buffer;
168 transfer.
length =
sizeof(buffer);
175 self->_state = TWR_SPS30_STATE_READ_SERIAL_NUMBER;
181 case TWR_SPS30_STATE_READ_SERIAL_NUMBER:
183 self->_state = TWR_SPS30_STATE_ERROR;
188 uint16_t __enforce_alignment;
195 transfer.
length =
sizeof(buffer);
202 if (!_twr_sps30_convert_to_words(buffer,
sizeof(buffer),
203 (uint16_t *) data.serial, TWR_SPS30_NUM_WORDS(data.serial)))
208 self->_state = TWR_SPS30_STATE_READY;
212 case TWR_SPS30_STATE_START_MEASUREMENT:
214 self->_state = TWR_SPS30_STATE_ERROR;
222 buffer[4] = _twr_sps30_calculate_crc(&buffer[2], 2);
228 transfer.
length =
sizeof(buffer);
235 self->_state = TWR_SPS30_STATE_SET_DATAREADY_FLAG;
239 case TWR_SPS30_STATE_SET_DATAREADY_FLAG:
241 self->_state = TWR_SPS30_STATE_ERROR;
243 static const uint8_t buffer[] = { 0x02, 0x02 };
248 transfer.
buffer = (uint8_t *) buffer;
249 transfer.
length =
sizeof(buffer);
256 self->_state = TWR_SPS30_STATE_READ_DATAREADY_FLAG;
262 case TWR_SPS30_STATE_READ_DATAREADY_FLAG:
264 self->_state = TWR_SPS30_STATE_ERROR;
272 transfer.
length =
sizeof(buffer);
279 if (_twr_sps30_calculate_crc(&buffer[0], 2) != buffer[2])
284 if (buffer[1] == 0x01)
286 self->_state = TWR_SPS30_STATE_GET_MEASUREMENT_DATA;
291 self->_state = TWR_SPS30_STATE_READ_DATAREADY_FLAG;
297 case TWR_SPS30_STATE_GET_MEASUREMENT_DATA:
299 self->_state = TWR_SPS30_STATE_ERROR;
301 static const uint8_t buffer[] = { 0x03, 0x00 };
306 transfer.
buffer = (uint8_t *) buffer;
307 transfer.
length =
sizeof(buffer);
314 self->_state = TWR_SPS30_STATE_READ_MEASUREMENT_DATA;
320 case TWR_SPS30_STATE_READ_MEASUREMENT_DATA:
322 self->_state = TWR_SPS30_STATE_ERROR;
326 uint16_t uint16_t[2];
334 transfer.
buffer = (uint8_t *) buffer;
335 transfer.
length =
sizeof(buffer);
342 if (!_twr_sps30_convert_to_words(buffer,
sizeof(buffer), data->uint16_t, TWR_SPS30_NUM_WORDS(data)))
347 val.u = be32_to_cpu(data[0].u);
348 self->_mass_concentration.mc_1p0 = val.f;
349 val.u = be32_to_cpu(data[1].u);
350 self->_mass_concentration.mc_2p5 = val.f;
351 val.u = be32_to_cpu(data[2].u);
352 self->_mass_concentration.mc_4p0 = val.f;
353 val.u = be32_to_cpu(data[3].u);
354 self->_mass_concentration.mc_10p0 = val.f;
355 val.u = be32_to_cpu(data[4].u);
356 self->_number_concentration.nc_0p5 = val.f;
357 val.u = be32_to_cpu(data[5].u);
358 self->_number_concentration.nc_1p0 = val.f;
359 val.u = be32_to_cpu(data[6].u);
360 self->_number_concentration.nc_2p5 = val.f;
361 val.u = be32_to_cpu(data[7].u);
362 self->_number_concentration.nc_4p0 = val.f;
363 val.u = be32_to_cpu(data[8].u);
364 self->_number_concentration.nc_10p0 = val.f;
365 val.u = be32_to_cpu(data[9].u);
366 self->_typical_particle_size = val.f;
368 self->_measurement_valid =
true;
370 if ((
twr_tick_get() - self->_start_time) > self->_startup_time)
372 if (self->_event_handler != NULL)
377 self->_state = TWR_SPS30_STATE_STOP_MEASUREMENT;
381 self->_state = TWR_SPS30_STATE_SET_DATAREADY_FLAG;
386 case TWR_SPS30_STATE_STOP_MEASUREMENT:
388 self->_state = TWR_SPS30_STATE_ERROR;
390 static const uint8_t buffer[] = { 0x01, 0x04 };
395 transfer.
buffer = (uint8_t *) buffer;
396 transfer.
length =
sizeof(buffer);
403 self->_state = TWR_SPS30_STATE_READY;
409 self->_state = TWR_SPS30_STATE_ERROR;
417 static uint8_t _twr_sps30_calculate_crc(uint8_t *buffer,
size_t length)
421 for (
size_t i = 0; i < length; i++)
425 for (
int j = 0; j < 8; j++)
427 if ((crc & 0x80) != 0)
429 crc = (crc << 1) ^ 0x31;
441 static bool _twr_sps30_convert_to_words(uint8_t *buffer,
size_t buffer_length, uint16_t *data,
size_t data_length)
443 uint8_t *data8 = (uint8_t *) data;
446 if (buffer_length != (data_length * 3))
451 for (i = 0, j = 0; i < buffer_length; i += 3)
453 if (_twr_sps30_calculate_crc(&buffer[i], 2) != buffer[i + 2])
458 data8[j++] = buffer[i];
459 data8[j++] = buffer[i + 1];
void twr_i2c_init(twr_i2c_channel_t channel, twr_i2c_speed_t speed)
Initialize I2C channel.
bool twr_i2c_read(twr_i2c_channel_t channel, const twr_i2c_transfer_t *transfer)
Read from I2C channel.
bool twr_i2c_write(twr_i2c_channel_t channel, const twr_i2c_transfer_t *transfer)
Write to I2C channel.
twr_i2c_channel_t
I2C channels.
@ TWR_I2C_SPEED_100_KHZ
I2C communication speed is 100 kHz.
void twr_scheduler_plan_current_from_now(twr_tick_t tick)
Schedule current task to tick relative from now.
void twr_scheduler_plan_current_relative(twr_tick_t tick)
Schedule current task to tick relative from current spin.
void twr_scheduler_plan_absolute(twr_scheduler_task_id_t task_id, twr_tick_t tick)
Schedule specified task to absolute tick.
void twr_scheduler_plan_now(twr_scheduler_task_id_t task_id)
Schedule specified task for immediate execution.
void twr_scheduler_plan_relative(twr_scheduler_task_id_t task_id, twr_tick_t tick)
Schedule specified task to tick relative from current spin.
twr_scheduler_task_id_t twr_scheduler_register(void(*task)(void *), void *param, twr_tick_t tick)
Register task in scheduler.
twr_sps30_event_t
Callback events.
void twr_sps30_init(twr_sps30_t *self, twr_i2c_channel_t i2c_channel, uint8_t i2c_address)
Initialize SPS30.
struct twr_sps30_t twr_sps30_t
SPS30 instance.
bool twr_sps30_get_mass_concentration(twr_sps30_t *self, twr_sps30_mass_concentration_t *mass_concentration)
Get measured mass concentration in μg/m3.
void twr_sps30_set_startup_time(twr_sps30_t *self, twr_tick_t startup_time)
Set startup time (how long the fan blows air before the measurement)
void twr_sps30_set_update_interval(twr_sps30_t *self, twr_tick_t interval)
Set measurement interval.
bool twr_sps30_measure(twr_sps30_t *self)
Start measurement manually.
void twr_sps30_set_event_handler(twr_sps30_t *self, void(*event_handler)(twr_sps30_t *, twr_sps30_event_t, void *), void *event_param)
Set callback function.
bool twr_sps30_get_typical_particle_size(twr_sps30_t *self, float *typical_particle_size)
Get measured typical particle size in μm.
bool twr_sps30_get_number_concentration(twr_sps30_t *self, twr_sps30_number_concentration_t *number_concentration)
Get measured number concentration in #/cm3.
@ TWR_SPS30_EVENT_UPDATE
Update event.
@ TWR_SPS30_EVENT_ERROR
Error event.
#define TWR_TICK_INFINITY
Maximum timestamp value.
twr_tick_t twr_tick_get(void)
Get absolute timestamp since start of program.
uint64_t twr_tick_t
Timestamp data type.
void * buffer
Pointer to buffer which is being written or read.
uint8_t device_address
7-bit I2C device address
size_t length
Length of buffer which is being written or read.