sgp40/sgp40.c
2022-06-17 10:30:37 +02:00

205 lines
4.8 KiB
C

/*
* 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 = sensirion_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);
}