/* * SGP40.c * * Created on: Jan 9, 2022 * Author: david */ #include "sgp40.h" /* * Functions to be implemented by user */ /* I2C */ int8_t sgp40_i2c_transmit(uint8_t address, uint8_t *buffer, int len) __attribute__((weak)); int8_t sgp40_i2c_receive(uint8_t address, uint8_t *buffer, int len) __attribute__((weak)); /* CRC */ uint8_t sensirion_crc8_calculate(const uint8_t *data, uint16_t count) __attribute__((weak)); /* Interrupts */ int8_t sgp40_disable_interrupts(void) __attribute__((weak)); int8_t sgp40_enable_interrupts(void) __attribute__((weak)); /* delay function */ void delay_ms(int delay_ms) __attribute__((weak)); /* * Public functions */ int8_t sgp40_send_cmd(sgp40_cmd_t cmd) { uint8_t buffer[32]; int result; // start measurement buffer[0] = cmd >> 8; buffer[1] = cmd & 0x00ff; sgp40_disable_interrupts(); result = sgp40_i2c_transmit(SGP40_I2C_ADDRESS<<1, buffer, 2); sgp40_enable_interrupts(); if (result != 0) { return SGP40_ERROR; } /* Sensirion sensors return NACK after last byte (so NACK at the end is ok) */ return SGP40_OK; } int8_t sgp40_measure_raw_signal(uint16_t * voc_ticks) { uint8_t buffer[32]; int result; /* Start measurement */ buffer[0] = 0x26; buffer[1] = 0x0F; buffer[2] = 0x80; buffer[3] = 0x00; buffer[4] = 0xA2; buffer[5] = 0x66; buffer[6] = 0x66; buffer[7] = 0x93; /* Returns NACK if CRC is wrong */ sgp40_disable_interrupts(); result = sgp40_i2c_transmit(SGP40_I2C_ADDRESS<<1, buffer, 8); sgp40_enable_interrupts(); if (result != 0) { return SGP40_ERROR; } delay_ms(SGP40_MAX_MEAS_DURATION_MS); // 30ms sgp40_disable_interrupts(); result = sgp40_i2c_receive(SGP40_I2C_ADDRESS<<1, buffer, 3); sgp40_enable_interrupts(); if (result != 0) { return SGP40_ERROR; } *voc_ticks = (buffer[0] << 8) + buffer[1]; uint8_t voc_ticks_crc = buffer[2]; uint8_t crc_correct = sensirion_crc8_calculate(buffer, 2) == voc_ticks_crc; if (!crc_correct) { return SGP40_CRC8_ERROR; } return SGP40_OK; } int8_t sgp40_measure_raw_signal_compensated(uint16_t * voc_ticks, uint16_t relative_humidity, int16_t temperature) { uint8_t buffer[32]; int result; uint16_t rh_ticks = (uint16_t)((uint32_t)relative_humidity * 65535 / 100); uint16_t t_ticks = (uint16_t)(((uint32_t)temperature/10 + 45) * 65535 / 175); buffer[0] = SGP40_MEASURE_RAW_SIGNAL >> 8; buffer[1] = SGP40_MEASURE_RAW_SIGNAL & 0x00ff; buffer[2] = (uint8_t)(rh_ticks >> 8); buffer[3] = (uint8_t)rh_ticks; buffer[4] = sensirion_crc8_calculate(buffer+2, 2); buffer[5] = (uint8_t)(t_ticks >> 8); buffer[6] = (uint8_t)t_ticks; buffer[7] = sensirion_crc8_calculate(buffer+5, 2); /* Returns NACK if CRC is wrong */ sgp40_disable_interrupts(); result = sgp40_i2c_transmit(SGP40_I2C_ADDRESS<<1, buffer, 8); sgp40_enable_interrupts(); if (result != 0) { return SGP40_ERROR; } delay_ms(SGP40_MAX_MEAS_DURATION_MS); // 30ms sgp40_disable_interrupts(); result = sgp40_i2c_receive(SGP40_I2C_ADDRESS<<1, buffer, 3); sgp40_enable_interrupts(); if (result != 0) { return SGP40_ERROR; } *voc_ticks = (buffer[0] << 8) + buffer[1]; uint8_t voc_ticks_crc = buffer[2]; uint8_t crc_correct = sensirion_crc8_calculate(buffer, 2) == voc_ticks_crc; if (!crc_correct) { return SGP40_CRC8_ERROR; } return SGP40_OK; } int8_t SGP40_execute_self_test ( uint8_t * test_result) { uint8_t buffer[16]; int8_t result; result = sgp40_send_cmd(SGP40_EXECUTE_SELF_TEST); if (result != 0) { return SGP40_ERROR; } delay_ms(350); sgp40_disable_interrupts(); result = sgp40_i2c_receive(SGP40_I2C_ADDRESS << 1, buffer, 3); sgp40_enable_interrupts(); if (result != 0) { return SGP40_ERROR; } test_result = buffer[0]; uint8_t test_result_crc = buffer[2]; uint8_t crc_correct = crc8_calculate(buffer, 2) == test_result_crc; if (!crc_correct) { return SGP40_CRC8_ERROR; } return SGP40_OK; } int8_t SGP40_get_serial_number(uint8_t serial[6]) { uint8_t buffer[16]; sgp40_disable_interrupts(); sgp40_send_cmd(SGP40_GET_SERIAL_NUMBER); sgp40_enable_interrupts(); delay_ms(5); sgp40_disable_interrupts(); sgp40_i2c_receive(SGP40_I2C_ADDRESS << 1, buffer, 9); sgp40_enable_interrupts(); serial[0] = buffer[0]; serial[1] = buffer[1]; uint8_t crc_ser01 = buffer[3]; serial[2] = buffer[4]; serial[3] = buffer[5]; uint8_t crc_ser23 = buffer[6]; serial[4] = buffer[7]; serial[5] = buffer[8]; uint8_t crc_ser45 = buffer[9]; uint8_t crc_correct = sensirion_crc8_calculate(buffer, 2) == crc_ser01; crc_correct &= sensirion_crc8_calculate(buffer + 3, 2) == crc_ser23; crc_correct &= sensirion_crc8_calculate(buffer + 6, 2) == crc_ser45; if (!crc_correct) { return SGP40_CRC8_ERROR; } return SGP40_OK; } int8_t SGP40_turn_heater_off(void) { return sgp40_send_cmd(SGP40_TURN_HEATER_OFF); } int8_t SGP40_soft_reset(void) { return sgp40_send_cmd(SGP40_SOFT_RESET); }