158 lines
4.4 KiB
C
158 lines
4.4 KiB
C
/*
|
|
* File: scd4x.h
|
|
* Description: Sensirion SCD4x sensor communication library
|
|
* Author: David Zaitlik
|
|
* Date: 2022-06-08
|
|
*
|
|
*
|
|
* Copyright (c) 2024 Veles Labs s.r.o.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include "scd4x.h"
|
|
|
|
/*
|
|
* Functions to be implemented by user
|
|
*/
|
|
|
|
/* I2C */
|
|
int8_t scd4x_i2c_transmit(uint8_t address, uint8_t *buffer, int len) __attribute__((weak));
|
|
int8_t scd4x_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 scd4x_disable_interrupts(void) __attribute__((weak));
|
|
int8_t scd4x_enable_interrupts(void) __attribute__((weak));
|
|
/* delay function */
|
|
void delay_ms(int delay_ms) __attribute__((weak));
|
|
|
|
/*
|
|
* Public functions
|
|
*/
|
|
|
|
int8_t scd4x_send_cmd(scd4x_cmd_t cmd)
|
|
{
|
|
uint8_t buffer[32];
|
|
int result;
|
|
|
|
// start measurement
|
|
buffer[0] = cmd >> 8;
|
|
buffer[1] = cmd & 0x00ff;
|
|
scd4x_disable_interrupts();
|
|
result = scd4x_i2c_transmit(SCD4X_I2C_ADDRESS<<1, buffer, 2);
|
|
scd4x_enable_interrupts();
|
|
if (result != 0) {
|
|
return SCD4X_ERROR;
|
|
}
|
|
|
|
/* Sensirion sensors return NACK after last byte (so NACK at the end is ok) */
|
|
return SCD4X_OK;
|
|
}
|
|
|
|
int8_t scd4x_get_serial(uint8_t serial[6])
|
|
{
|
|
uint8_t buffer[16];
|
|
int result;
|
|
|
|
scd4x_send_cmd(SCD4X_GET_SERIAL_NUMBER);
|
|
scd4x_disable_interrupts();
|
|
result = scd4x_i2c_receive(SCD4X_I2C_ADDRESS << 1, buffer, 9);
|
|
scd4x_enable_interrupts();
|
|
if (result != 0) {
|
|
return SCD4X_ERROR;
|
|
}
|
|
return SCD4X_OK;
|
|
}
|
|
|
|
int8_t scd4x_read_data(uint8_t *buffer, int len)
|
|
{
|
|
return SCD4X_OK;
|
|
}
|
|
|
|
int8_t scd4x_start_periodic_measurement( void )
|
|
{
|
|
return scd4x_send_cmd(SCD4X_START_PERIODIC_MEASUREMENT);
|
|
}
|
|
|
|
int8_t scd4x_stop_periodic_measurement( void )
|
|
{
|
|
return scd4x_send_cmd(SCD4X_STOP_PERIODIC_MEASUREMENT);
|
|
}
|
|
|
|
int8_t scd4x_perform_factory_reset( void )
|
|
{
|
|
return scd4x_send_cmd(SCD4X_PERFORM_FACTORY_RESET);
|
|
}
|
|
|
|
int8_t scd4x_read_measurement(uint16_t *co2, int16_t *temperature, uint16_t *relative_humidity)
|
|
{
|
|
uint8_t buffer[32];
|
|
int result;
|
|
|
|
/* start measurement */
|
|
buffer[0] = SCD4X_READ_MEASUREMENT >> 8;
|
|
buffer[1] = SCD4X_READ_MEASUREMENT & 0x00ff;
|
|
/* disable interrupts to prevent MODBUS/I2C conflict */
|
|
scd4x_disable_interrupts();
|
|
result = scd4x_i2c_transmit(SCD4X_I2C_ADDRESS<<1, buffer, 2);
|
|
scd4x_enable_interrupts();
|
|
if (result != 0) {
|
|
return SCD4X_ERROR;
|
|
}
|
|
delay_ms(1);
|
|
/* read out */
|
|
scd4x_disable_interrupts();
|
|
result = scd4x_i2c_receive(SCD4X_I2C_ADDRESS<<1, buffer, 9);
|
|
scd4x_enable_interrupts();
|
|
if (result != 0)
|
|
{
|
|
return SCD4X_ERROR;
|
|
}
|
|
/* Convert to T and RH; taken directly from pseudocode in SHT4x datasheet, page 3 */
|
|
uint32_t co2_ticks = (buffer[0] << 8) + buffer[1];
|
|
uint8_t co2_crc = buffer[2];
|
|
uint32_t t_ticks = (buffer[3] << 8) + buffer[4];
|
|
uint8_t t_crc = buffer[5];
|
|
uint32_t rh_ticks = (buffer[6] << 8) + buffer[7];
|
|
uint8_t rh_crc = buffer[8];
|
|
/* check CRC-8 checksum */
|
|
uint8_t crc_correct = sensirion_crc8_calculate(buffer, 2) == co2_crc;
|
|
crc_correct &= sensirion_crc8_calculate(buffer + 3, 2) == t_crc;
|
|
crc_correct &= sensirion_crc8_calculate(buffer + 6, 2) == rh_crc;
|
|
if (!crc_correct) {
|
|
return SCD4X_CRC8_ERROR;
|
|
}
|
|
/* copy to output variables */
|
|
int t_degC = -450 + 10 * 175 * t_ticks / 65535;
|
|
int rh_pRH = 100 * rh_ticks / 65535;
|
|
if (rh_pRH > 100) {
|
|
rh_pRH = 100;
|
|
}
|
|
if (rh_pRH < 0) {
|
|
rh_pRH = 0;
|
|
}
|
|
*co2 = co2_ticks;
|
|
*temperature = t_degC;
|
|
*relative_humidity = rh_pRH;
|
|
|
|
return SCD4X_OK;
|
|
}
|