3 #define _TWR_LP8_MODBUS_DEVICE_ADDRESS 0xfe
4 #define _TWR_LP8_MODBUS_WRITE 0x41
5 #define _TWR_LP8_MODBUS_READ 0x44
6 #define _TWR_LP8_INITIAL_MEASUREMENT 0x10
7 #define _TWR_LP8_SEQUENTIAL_MEASUREMENT 0x20
8 #define _TWR_LP8_RX_ERROR_STATUS0 (3 + 0xa7 - 0x80)
9 #define _TWR_LP8_RX_ERROR_STATUS1 (3 + 0xa6 - 0x80)
10 #define _TWR_LP8_RX_CONC (3 + 0x9a - 0x80)
12 #define _TWR_LP8_CALIBRATION_TIMEOUT (8 * 24 * 60 * 60 * 1000)
14 static void _twr_lp8_task_interval(
void *param);
16 static void _twr_lp8_task_measure(
void *param);
18 static uint16_t _twr_lp8_calculate_crc16(uint8_t *buffer, uint8_t length);
22 memset(
self, 0,
sizeof(*
self));
24 self->_driver = driver;
26 self->_pressure = 10124;
28 self->_tick_calibration =
twr_tick_get() + _TWR_LP8_CALIBRATION_TIMEOUT;
33 self->_driver->init();
38 self->_event_handler = event_handler;
39 self->_event_param = event_param;
44 self->_update_interval = interval;
60 if (self->_state == TWR_LP8_STATE_READY)
62 self->_state = TWR_LP8_STATE_PRECHARGE;
68 else if (self->_state == TWR_LP8_STATE_INITIALIZE)
85 *ppm = (float) self->_concentration;
92 *error =
self->_error;
99 self->_calibration = calibration;
101 self->_tick_calibration = 0;
106 static void _twr_lp8_task_interval(
void *param)
115 static void _twr_lp8_error(
twr_lp8_t *
self, twr_lp8_error_t error)
117 self->_state = TWR_LP8_STATE_ERROR;
118 self->_error = error;
122 static void _twr_lp8_task_measure(
void *param)
128 switch (self->_state)
130 case TWR_LP8_STATE_ERROR:
132 self->_valid =
false;
134 self->_first_measurement_done =
false;
136 self->_driver->uart_enable(
false);
137 self->_driver->device_enable(
false);
138 self->_driver->charge_enable(
false);
140 if (self->_event_handler != NULL)
145 self->_state = TWR_LP8_STATE_INITIALIZE;
151 case TWR_LP8_STATE_INITIALIZE:
153 if (!self->_driver->charge_enable(
true))
155 _twr_lp8_error(
self, TWR_LP8_ERROR_INITIALIZE);
159 self->_state = TWR_LP8_STATE_CHARGE;
165 case TWR_LP8_STATE_READY:
169 case TWR_LP8_STATE_PRECHARGE:
171 if (!self->_driver->charge_enable(
true))
173 _twr_lp8_error(
self, TWR_LP8_ERROR_PRECHARGE);
177 self->_state = TWR_LP8_STATE_CHARGE;
183 case TWR_LP8_STATE_CHARGE:
185 if (!self->_driver->charge_enable(
false))
187 _twr_lp8_error(
self, TWR_LP8_ERROR_CHARGE_CHARGE_ENABLE);
191 if (!self->_driver->device_enable(
true))
193 _twr_lp8_error(
self, TWR_LP8_ERROR_CHARGE_DEVICE_ENABLE);
197 self->_state = TWR_LP8_STATE_BOOT;
205 case TWR_LP8_STATE_BOOT:
207 int signal_rdy_value;
211 if (!self->_driver->read_signal_rdy(&signal_rdy_value))
213 _twr_lp8_error(
self, TWR_LP8_ERROR_BOOT_SIGNAL_READY);
217 if (signal_rdy_value != 0)
221 _twr_lp8_error(
self, TWR_LP8_ERROR_BOOT_TIMEOUT);
232 if (!self->_first_measurement_done)
234 self->_tx_buffer[0] = _TWR_LP8_MODBUS_DEVICE_ADDRESS;
235 self->_tx_buffer[1] = _TWR_LP8_MODBUS_WRITE;
236 self->_tx_buffer[2] = 0x00;
237 self->_tx_buffer[3] = 0x80;
238 self->_tx_buffer[4] = 0x01;
239 self->_tx_buffer[5] = _TWR_LP8_INITIAL_MEASUREMENT;
240 self->_tx_buffer[6] = 0x28;
241 self->_tx_buffer[7] = 0x7e;
249 self->_tx_buffer[0] = _TWR_LP8_MODBUS_DEVICE_ADDRESS;
250 self->_tx_buffer[1] = _TWR_LP8_MODBUS_WRITE;
251 self->_tx_buffer[2] = 0x00;
252 self->_tx_buffer[3] = 0x80;
253 self->_tx_buffer[4] = 0x1a;
255 self->_calibration_run =
self->_tick_calibration <
twr_tick_get();
257 if (self->_calibration_run)
259 self->_tx_buffer[5] =
self->_calibration;
263 self->_tx_buffer[5] = _TWR_LP8_SEQUENTIAL_MEASUREMENT;
266 memcpy(&self->_tx_buffer[6], self->_sensor_state, 23);
268 self->_tx_buffer[29] =
self->_pressure >> 8;
269 self->_tx_buffer[30] =
self->_pressure;
271 crc16 = _twr_lp8_calculate_crc16(self->_tx_buffer, 31);
273 self->_tx_buffer[31] = crc16;
274 self->_tx_buffer[32] = crc16 >> 8;
279 if (!self->_driver->uart_enable(
true))
281 _twr_lp8_error(
self, TWR_LP8_ERROR_BOOT_UART_ENABLE);
286 if (self->_driver->uart_write(self->_tx_buffer, length) != length)
288 _twr_lp8_error(
self, TWR_LP8_ERROR_BOOT_UART_WRITE);
293 self->_rx_buffer_length = 0;
295 self->_state = TWR_LP8_STATE_BOOT_READ;
303 case TWR_LP8_STATE_BOOT_READ:
305 self->_rx_buffer_length +=
self->_driver->uart_read(self->_rx_buffer + self->_rx_buffer_length, 4 - self->_rx_buffer_length);
307 if (self->_rx_buffer_length == 4)
309 if (!self->_driver->uart_enable(
false))
311 _twr_lp8_error(
self, TWR_LP8_ERROR_BOOT_READ_UART_ENABLE);
316 if (self->_rx_buffer[0] != _TWR_LP8_MODBUS_DEVICE_ADDRESS)
318 _twr_lp8_error(
self, TWR_LP8_ERROR_BOOT_READ_DEVICE_ADDRESS);
323 if (self->_rx_buffer[1] != self->_tx_buffer[1])
325 _twr_lp8_error(
self, TWR_LP8_ERROR_BOOT_READ_COMMAND);
330 if (_twr_lp8_calculate_crc16(self->_rx_buffer, 4) != 0)
332 _twr_lp8_error(
self, TWR_LP8_ERROR_BOOT_READ_CRC);
337 self->_state = TWR_LP8_STATE_MEASURE;
339 self->_tick_timeout =
twr_tick_get() + (
self->_calibration_run ? 1000 : 250);
348 _twr_lp8_error(
self, TWR_LP8_ERROR_BOOT_READ_TIMEOUT);
357 case TWR_LP8_STATE_MEASURE:
359 int signal_rdy_value;
361 if (!self->_driver->read_signal_rdy(&signal_rdy_value))
363 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_SIGNAL_RDY);
368 if (signal_rdy_value == 0)
372 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_SIGNAL_RDY_TIMEOUT);
384 self->_tx_buffer[0] = _TWR_LP8_MODBUS_DEVICE_ADDRESS;
385 self->_tx_buffer[1] = _TWR_LP8_MODBUS_READ;
386 self->_tx_buffer[2] = 0x00;
387 self->_tx_buffer[3] = 0x80;
388 self->_tx_buffer[4] = 0x2c;
390 uint16_t crc16 = _twr_lp8_calculate_crc16(self->_tx_buffer, 5);
392 self->_tx_buffer[5] = crc16;
393 self->_tx_buffer[6] = crc16 >> 8;
395 if (!self->_driver->uart_enable(
true))
397 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_UART_ENABLE);
402 if (self->_driver->uart_write(self->_tx_buffer, 7) != 7)
404 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_UART_WRITE);
409 self->_rx_buffer_length = 0;
411 self->_state = TWR_LP8_STATE_MEASURE_READ;
419 case TWR_LP8_STATE_MEASURE_READ:
421 self->_rx_buffer_length +=
self->_driver->uart_read(self->_rx_buffer + self->_rx_buffer_length, 49 - self->_rx_buffer_length);
423 if (self->_rx_buffer_length == 49)
425 if (!self->_driver->uart_enable(
false))
427 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_READ_UART_ENABLE);
432 if (!self->_driver->device_enable(
false))
434 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_READ_DEVICE_ENABLE);
439 if (self->_rx_buffer[0] != _TWR_LP8_MODBUS_DEVICE_ADDRESS)
441 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_READ_DEVICE_ADDRESS);
446 if (self->_rx_buffer[1] != self->_tx_buffer[1])
448 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_READ_COMMAND);
453 if (_twr_lp8_calculate_crc16(self->_rx_buffer, 49) != 0)
455 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_READ_CRC);
460 if ((self->_rx_buffer[_TWR_LP8_RX_ERROR_STATUS0] & 0xdd) != 0)
463 if (self->_calibration_run)
465 if ((self->_rx_buffer[_TWR_LP8_RX_ERROR_STATUS0] == 8) &&
469 self->_state = TWR_LP8_STATE_CHARGE;
478 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_READ_CALIBRATION_RUN);
484 if ((self->_rx_buffer[_TWR_LP8_RX_ERROR_STATUS1] & 0xf7) != 0)
486 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_READ_STATUS1);
491 if (self->_calibration_run)
495 self->_tick_calibration =
twr_tick_get() + _TWR_LP8_CALIBRATION_TIMEOUT;
498 memcpy(self->_sensor_state, &self->_rx_buffer[4], 23);
500 self->_first_measurement_done =
true;
502 self->_concentration = (int16_t) self->_rx_buffer[_TWR_LP8_RX_CONC] << 8;
503 self->_concentration |= (int16_t)
self->_rx_buffer[_TWR_LP8_RX_CONC + 1];
505 self->_valid = (
self->_concentration >= 0) && (self->_concentration <= 10000);
507 self->_state = TWR_LP8_STATE_READY;
509 if (self->_event_handler != NULL)
520 _twr_lp8_error(
self, TWR_LP8_ERROR_MEASURE_READ_TIMEOUT);
537 static uint16_t _twr_lp8_calculate_crc16(uint8_t *buffer, uint8_t length)
541 for (crc16 = 0xffff; length != 0; length--, buffer++)
545 for (
int i = 0; i < 8; i++)
547 if ((crc16 & 1) != 0)
void twr_lp8_set_event_handler(twr_lp8_t *self, void(*event_handler)(twr_lp8_event_t, void *), void *event_param)
Set callback function.
bool twr_lp8_measure(twr_lp8_t *self)
Start measurement manually.
void twr_lp8_calibration(twr_lp8_t *self, twr_lp8_calibration_t calibration)
Request sensor calibration.
void twr_lp8_set_update_interval(twr_lp8_t *self, twr_tick_t interval)
Set measurement interval.
void twr_lp8_init(twr_lp8_t *self, const twr_lp8_driver_t *driver)
Initialize LP8.
bool twr_lp8_get_concentration_ppm(twr_lp8_t *self, float *ppm)
Get CO2 concentration in ppm (parts per million)
twr_lp8_event_t
Callback events.
bool twr_lp8_get_error(twr_lp8_t *self, twr_lp8_error_t *error)
Get last error code.
twr_lp8_calibration_t
Calibration.
struct twr_lp8_t twr_lp8_t
LP8 instance.
@ TWR_LP8_EVENT_UPDATE
Update event.
@ TWR_LP8_EVENT_ERROR
Error event.
@ TWR_LP8_CALIBRATION_ABC
ABC (based on filtered data)
@ TWR_LP8_CALIBRATION_ATWR_RF
ABC (based on filtered data) + reset filters.
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_current_now(void)
Schedule current 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.
#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.