Skip to content
This repository has been archived by the owner on Apr 19, 2024. It is now read-only.

Commit

Permalink
Initial scd30 driver
Browse files Browse the repository at this point in the history
  • Loading branch information
abrauchli committed Jul 19, 2018
1 parent edbab4f commit f57d29c
Show file tree
Hide file tree
Showing 7 changed files with 688 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.o
/release
/scd30/scd_git_version.c
/scd30/scd30_example_usage_hw_i2c
/scd30/scd30_example_usage_sw_i2c
42 changes: 42 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
drivers=scd30
clean_drivers=$(foreach d, $(drivers), clean_$(d))
release_drivers=$(foreach d, $(drivers), release/$(d))

.PHONY: FORCE all $(release_drivers) $(clean_drivers)

all: $(drivers)

$(drivers): scd30/scd_git_version.c FORCE
cd $@ && $(MAKE) $(MFLAGS)

scd30/scd_git_version.c: FORCE
git describe --always --dirty | \
awk 'BEGIN \
{print "/* THIS FILE IS AUTOGENERATED */"} \
{print "#include \"scd_git_version.h\""} \
{print "const char * SCD_DRV_VERSION_STR = \"" $$0"\";"} \
END {}' > $@

$(release_drivers): scd30/scd_git_version.c
export rel=$@ && \
export driver=$${rel#release/} && \
export tag="$$(git describe --always --dirty)" && \
export pkgname="$${driver}-$${tag}" && \
export pkgdir="release/$${pkgname}" && \
rm -rf "$${pkgdir}" && mkdir -p "$${pkgdir}" && \
cp -r embedded-common/* "$${pkgdir}" && \
cp -r $${driver}/* "$${pkgdir}" && \
perl -pi -e 's/^sensirion_common_dir :=.*$$/sensirion_common_dir := ./' "$${pkgdir}/Makefile" && \
cd "$${pkgdir}" && $(MAKE) $(MFLAGS) && $(MAKE) clean $(MFLAGS) && cd - && \
cd release && zip -r "$${pkgname}.zip" "$${pkgname}" && cd - && \
ln -sf $${pkgname} $@

release: clean $(release_drivers)

$(clean_drivers):
export rel=$@ && \
export driver=$${rel#clean_} && \
cd $${driver} && $(MAKE) clean $(MFLAGS) && cd -

clean: $(clean_drivers)
rm -rf release scd30/scd_git_version.c
42 changes: 42 additions & 0 deletions scd30/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
sensirion_common_dir := ../embedded-common
scd_common_dir := .
sw_i2c_dir := ${sensirion_common_dir}/sw_i2c
hw_i2c_dir := ${sensirion_common_dir}/hw_i2c

CFLAGS := -Os -Wall -Werror -I. -I${sensirion_common_dir}

sensirion_common_objects := sensirion_common.o
scd_common_objects := scd_git_version.o
scd30_binaries := scd30_example_usage_sw_i2c scd30_example_usage_hw_i2c
scd_binaries += ${scd30_binaries}

sw_objects := sensirion_sw_i2c.o sensirion_sw_i2c_implementation.o
hw_objects := sensirion_hw_i2c_implementation.o
all_objects := ${sensirion_common_objects} ${scd_common_objects} ${hw_objects} ${sw_objects} scd30.o

.PHONY: all

all: ${scd_binaries}

scd_git_version.o: ${scd_common_dir}/scd_git_version.c
$(CC) $(CFLAGS) -c -o $@ $^
sensirion_common.o: ${sensirion_common_dir}/sensirion_common.c
$(CC) $(CFLAGS) -c -o $@ $^
sensirion_sw_i2c_implementation.o: ${sw_i2c_dir}/sample-implementations/linux_user_space/sensirion_sw_i2c_implementation.c
$(CC) -I${sw_i2c_dir} $(CFLAGS) -c -o $@ $^
sensirion_hw_i2c_implementation.o: ${hw_i2c_dir}/sensirion_hw_i2c_implementation.c
$(CC) $(CFLAGS) -c -o $@ $^

sensirion_sw_i2c.o: ${sw_i2c_dir}/sensirion_sw_i2c.c
$(CC) -I${sw_i2c_dir} $(CFLAGS) -c -o $@ $^

scd30.o: ${sensirion_common_dir}/sensirion_arch_config.h ${sensirion_common_dir}/sensirion_i2c.h ${scd_common_dir}/scd_git_version.c scd30.h scd30.c

scd30_example_usage_sw_i2c: ${sensirion_common_objects} ${scd_common_objects} ${sw_objects} scd30.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS) scd30_example_usage.c

scd30_example_usage_hw_i2c: ${sensirion_common_objects} ${scd_common_objects} ${hw_objects} scd30.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS) scd30_example_usage.c

clean:
$(RM) ${all_objects} ${scd_binaries}
291 changes: 291 additions & 0 deletions scd30/scd30.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
/*
* Copyright (c) 2018, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "sensirion_arch_config.h"
#include "sensirion_i2c.h"
#include "sensirion_common.h"
#include "scd_git_version.h"
#include "scd30.h"


#ifdef SCD_ADDRESS
static const u8 SCD_I2C_ADDRESS = SCD_ADDRESS;
#else
static const u8 SCD_I2C_ADDRESS = 0x61;
#endif

#define SCD_CMD_START_PERIODIC_MEASUREMENT 0x0010
#define SCD_CMD_STOP_PERIODIC_MEASUREMENT 0x0104
#define SCD_CMD_READ_MEASUREMENT 0x0300
#define SCD_CMD_SET_MEASUREMENT_INTERVAL 0x4600
#define SCD_CMD_GET_DATA_READY 0x0202
#define SCD_CMD_SET_TEMPERATURE_OFFSET 0x5403
#define SCD_CMD_SET_ALTITUDE 0x5102

#define SCD_WORD_LEN 2
#define SCD_COMMAND_LEN 2
#define SCD_MAX_BUFFER_WORDS 24


/**
* scd_i2c_read_words() - read data words from sensor
* @data: Allocated buffer to store the read data.
* The buffer may also have been modified on STATUS_FAIL return.
* @data_words: Number of data words to read (without CRC bytes)
*
* Return: STATUS_OK on success, an error code otherwise
*/
static s16 scd_i2c_read_words(u16 *data, u16 data_words) {
s16 ret;
u16 i, j;
u16 size = data_words * (SCD_WORD_LEN + CRC8_LEN);
u16 word_buf[SCD_MAX_BUFFER_WORDS];
u8 * const buf8 = (u8 *)word_buf;

ret = sensirion_i2c_read(SCD_I2C_ADDRESS, buf8, size);
if (ret != STATUS_OK)
return ret;

/* check the CRC for each word */
for (i = 0, j = 0;
i < size;
i += SCD_WORD_LEN + CRC8_LEN, j += SCD_WORD_LEN) {

if (sensirion_common_check_crc(&buf8[i], SCD_WORD_LEN,
buf8[i + SCD_WORD_LEN]) == STATUS_FAIL) {
return STATUS_FAIL;
}
((u8 *)data)[j] = buf8[i];
((u8 *)data)[j + 1] = buf8[i + 1];
}

return STATUS_OK;
}


/**
* scd_fill_cmd_send_buf() - create the i2c send buffer for a command and a set
* of argument words.
* The output buffer interleaves argument words with
* their checksums.
* @buf: The generated buffer to send over i2c. Then buffer length must
* be at least SCD_COMMAND_LEN + num_args * (SCD_WORD_LEN +
* CRC8_LEN).
* @cmd: The i2c command to send. It already includes a checksum.
* @args: The arguments to the command. Can be NULL if none.
* @num_args: The number of word arguments in args.
*/
static void scd_fill_cmd_send_buf(u8 *buf, u16 cmd, const u16 *args,
u8 num_args) {
u16 word;
u8 crc;
u8 i;
u8 idx = 0;

buf[idx++] = (u8)((cmd & 0xFF00) >> 8);
buf[idx++] = (u8)((cmd & 0x00FF) >> 0);

for (i = 0; i < num_args; ++i) {
word = be16_to_cpu(args[i]);
crc = sensirion_common_generate_crc((u8 *)&word, SCD_WORD_LEN);

buf[idx++] = (u8)((word & 0x00FF) >> 0);
buf[idx++] = (u8)((word & 0xFF00) >> 8);
buf[idx++] = crc;
}
}


/**
* scd_i2c_write() - writes to the sensor
* @command: Sensor command
*
* Return: STATUS_OK on success, an error code otherwise
*/
static s16 scd_i2c_write(u16 command) {
u8 buf[SCD_COMMAND_LEN];

scd_fill_cmd_send_buf(buf, command, NULL, 0);
return sensirion_i2c_write(SCD_I2C_ADDRESS, buf, SCD_COMMAND_LEN);
}


/**
* scd_i2c_read_words_from_cmd() - reads data words from the sensor after a
* command has been issued
* @cmd: Command
* @data_words: Allocated buffer to store the read data
* @num_words: Data words to read (without CRC bytes)
*
* Return: STATUS_OK on success, an error code otherwise
*/
static s16 scd_i2c_read_words_from_cmd(u16 cmd, u16 *data_words,
u16 num_words) {

s16 ret = scd_i2c_write(cmd);

if (ret != STATUS_OK)
return ret;

return scd_i2c_read_words(data_words, num_words);
}


s16 scd_start_periodic_measurement(u16 ambient_pressure_mbar) {
const u16 BUF_SIZE = SCD_COMMAND_LEN + SCD_WORD_LEN + CRC8_LEN;
u8 buf[BUF_SIZE];

if (ambient_pressure_mbar && (ambient_pressure_mbar < 700 ||
ambient_pressure_mbar > 1400)) {
/* out of allowable range */
return STATUS_FAIL;
}

scd_fill_cmd_send_buf(buf, SCD_CMD_START_PERIODIC_MEASUREMENT,
&ambient_pressure_mbar,
sizeof(ambient_pressure_mbar) / SCD_WORD_LEN);

return sensirion_i2c_write(SCD_I2C_ADDRESS, buf, BUF_SIZE);
}


s16 scd_stop_periodic_measurement() {
return scd_i2c_write(SCD_CMD_STOP_PERIODIC_MEASUREMENT);
}


s16 scd_read_measurement(f32 *co2_ppm, f32 *temperature, f32 *humidity) {
s16 ret;
u16 word_buf[6]; /* 2 words for each co2, temperature, humidity */
union {
u32 raw;
f32 float32;
} tmp;

ret = scd_i2c_read_words_from_cmd(SCD_CMD_READ_MEASUREMENT, word_buf,
sizeof(word_buf) / SCD_WORD_LEN);
if (ret != STATUS_OK)
return ret;

tmp.raw = (((u32)be16_to_cpu(word_buf[0])) << 16) |
((u32)be16_to_cpu(word_buf[1]));
*co2_ppm = tmp.float32;

tmp.raw = (((u32)be16_to_cpu(word_buf[2])) << 16) |
((u32)be16_to_cpu(word_buf[3]));
*temperature = tmp.float32;

tmp.raw = (((u32)be16_to_cpu(word_buf[4])) << 16) |
((u32)be16_to_cpu(word_buf[5]));
*humidity = tmp.float32;

return STATUS_OK;
}


s16 scd_set_measurement_interval(u16 interval_sec) {
const u16 BUF_SIZE = SCD_COMMAND_LEN + SCD_WORD_LEN + CRC8_LEN;
u8 buf[BUF_SIZE];

if (interval_sec < 2 || interval_sec > 1800) {
/* out of allowable range */
return STATUS_FAIL;
}

scd_fill_cmd_send_buf(buf, SCD_CMD_SET_MEASUREMENT_INTERVAL, &interval_sec,
sizeof(interval_sec) / SCD_WORD_LEN);

return sensirion_i2c_write(SCD_I2C_ADDRESS, buf, BUF_SIZE);
}


s16 scd_get_data_ready(u16 *data_ready) {
return scd_i2c_read_words_from_cmd(SCD_CMD_GET_DATA_READY, data_ready,
sizeof(*data_ready) / SCD_WORD_LEN);
}


s16 scd_set_temperature_offset(u16 temperature_offset) {
const u16 BUF_SIZE = SCD_COMMAND_LEN + SCD_WORD_LEN + CRC8_LEN;
u8 buf[BUF_SIZE];

scd_fill_cmd_send_buf(buf, SCD_CMD_SET_TEMPERATURE_OFFSET,
&temperature_offset,
sizeof(temperature_offset) / SCD_WORD_LEN);

return sensirion_i2c_write(SCD_I2C_ADDRESS, buf, BUF_SIZE);
}


s16 scd_set_altitude(u16 altitude) {
const u16 BUF_SIZE = SCD_COMMAND_LEN + SCD_WORD_LEN + CRC8_LEN;
u8 buf[BUF_SIZE];

scd_fill_cmd_send_buf(buf, SCD_CMD_SET_ALTITUDE, &altitude,
sizeof(altitude) / SCD_WORD_LEN);

return sensirion_i2c_write(SCD_I2C_ADDRESS, buf, BUF_SIZE);
}


/**
* scd_get_driver_version() - Return the driver version
* Return: Driver version string
*/
const char *scd_get_driver_version()
{
return SCD_DRV_VERSION_STR;
}


/**
* scd_get_configured_address() - returns the configured I2C address
*
* Return: u8 I2C address
*/
u8 scd_get_configured_address() {
return SCD_I2C_ADDRESS;
}


/**
* scd_probe() - check if sensor is available
*
* Return: STATUS_OK on success, an error code otherwise
*/
s16 scd_probe() {
u16 data_ready;

/* Initialize I2C */
sensirion_i2c_init();

/* try to read data-ready state */
return scd_get_data_ready(&data_ready);
}

0 comments on commit f57d29c

Please sign in to comment.