New driver for SCD4X series CO2 sensors

Submodule Sensirion's embedded-i2c-scd4x driver
Add i2c_hal.cpp to binds it to Pimoroni::I2C
Port (loosely) scd4x_i2c_example_usage.c to Pico
pull/174/head
Phil Howard 2021-06-28 14:39:06 +01:00
rodzic 9f6ddb86ae
commit 0eeada72d7
11 zmienionych plików z 374 dodań i 0 usunięć

3
.gitmodules vendored
Wyświetl plik

@ -11,3 +11,6 @@
path = drivers/bmp280/src
url = https://github.com/pimoroni/BMP280_driver
branch = patch-intf-ptr
[submodule "drivers/scd4x/src"]
path = drivers/scd4x/src
url = https://github.com/Sensirion/embedded-i2c-scd4x

Wyświetl plik

@ -23,4 +23,5 @@ add_subdirectory(button)
add_subdirectory(plasma)
add_subdirectory(rgbled)
add_subdirectory(icp10125)
add_subdirectory(scd4x)
add_subdirectory(hub75)

Wyświetl plik

@ -0,0 +1 @@
include(scd4x.cmake)

Wyświetl plik

@ -0,0 +1,122 @@
/*
* 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_i2c_hal.h"
#include "sensirion_common.h"
#include "sensirion_config.h"
#define SCD4X_I2C_ADDRESS 98
pimoroni::I2C *__i2c = nullptr;
/*
* INSTRUCTIONS
* ============
*
* Implement all functions where they are marked as IMPLEMENT.
* Follow the function specification in the comments.
*/
/**
* Select the current i2c bus by index.
* All following i2c operations will be directed at that bus.
*
* THE IMPLEMENTATION IS OPTIONAL ON SINGLE-BUS SETUPS (all sensors on the same
* bus)
*
* @param bus_idx Bus index to select
* @returns 0 on success, an error code otherwise
*/
int16_t sensirion_i2c_hal_select_bus(uint8_t bus_idx) {
/* TODO:IMPLEMENT or leave empty if all sensors are located on one single
* bus
*/
return NOT_IMPLEMENTED_ERROR;
}
/**
* Initialize all hard- and software components that are needed for the I2C
* communication.
*/
void sensirion_i2c_hal_init(pimoroni::I2C *i2c) {
__i2c = i2c;
}
/**
* Release all resources initialized by sensirion_i2c_hal_init().
*/
void sensirion_i2c_hal_free(void) {
/* TODO:IMPLEMENT or leave empty if no resources need to be freed */
}
/**
* Execute one read transaction on the I2C bus, reading a given number of bytes.
* If the device does not acknowledge the read command, an error shall be
* returned.
*
* @param address 7-bit I2C address to read from
* @param data pointer to the buffer where the data is to be stored
* @param count number of bytes to read from I2C and store in the buffer
* @returns 0 on success, error code otherwise
*/
int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) {
int result = __i2c->read_blocking(address, data, count, false);
return result >= 0 ? 0 : result;
}
/**
* Execute one write transaction on the I2C bus, sending a given number of
* bytes. The bytes in the supplied buffer must be sent to the given address. If
* the slave device does not acknowledge any of the bytes, an error shall be
* returned.
*
* @param address 7-bit I2C address to write to
* @param data pointer to the buffer containing the data to write
* @param count number of bytes to read from the buffer and send over I2C
* @returns 0 on success, error code otherwise
*/
int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data,
uint16_t count) {
int result = __i2c->write_blocking(address, data, count, false);
return result >= 0 ? 0 : result;
}
/**
* Sleep for a given number of microseconds. The function should delay the
* execution for at least the given time, but may also sleep longer.
*
* Despite the unit, a <10 millisecond precision is sufficient.
*
* @param useconds the sleep time in microseconds
*/
void sensirion_i2c_hal_sleep_usec(uint32_t useconds) {
sleep_us(useconds);
}

Wyświetl plik

@ -0,0 +1,15 @@
set(DRIVER_NAME scd4x)
add_library(${DRIVER_NAME} INTERFACE)
target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/src/scd4x_i2c.c
${CMAKE_CURRENT_LIST_DIR}/src/sensirion_common.c
${CMAKE_CURRENT_LIST_DIR}/src/sensirion_i2c.c
${CMAKE_CURRENT_LIST_DIR}/i2c_hal.cpp
${CMAKE_CURRENT_LIST_DIR}/scd4x.cpp
)
target_link_libraries(${DRIVER_NAME} INTERFACE pimoroni_i2c hardware_i2c)
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/src)

Wyświetl plik

Wyświetl plik

@ -0,0 +1,113 @@
/*
* 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.
*/
#ifndef SENSIRION_I2C_HAL_H
#define SENSIRION_I2C_HAL_H
#include "sensirion_config.h"
#include "common/pimoroni_i2c.hpp"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* Select the current i2c bus by index.
* All following i2c operations will be directed at that bus.
*
* THE IMPLEMENTATION IS OPTIONAL ON SINGLE-BUS SETUPS (all sensors on the same
* bus)
*
* @param bus_idx Bus index to select
* @returns 0 on success, an error code otherwise
*/
int16_t sensirion_i2c_hal_select_bus(uint8_t bus_idx);
/**
* Initialize all hard- and software components that are needed for the I2C
* communication.
*/
void sensirion_i2c_hal_init(pimoroni::I2C *_i2c);
/**
* Release all resources initialized by sensirion_i2c_hal_init().
*/
void sensirion_i2c_hal_free(void);
/**
* Execute one read transaction on the I2C bus, reading a given number of bytes.
* If the device does not acknowledge the read command, an error shall be
* returned.
*
* @param address 7-bit I2C address to read from
* @param data pointer to the buffer where the data is to be stored
* @param count number of bytes to read from I2C and store in the buffer
* @returns 0 on success, error code otherwise
*/
int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count);
/**
* Execute one write transaction on the I2C bus, sending a given number of
* bytes. The bytes in the supplied buffer must be sent to the given address. If
* the slave device does not acknowledge any of the bytes, an error shall be
* returned.
*
* @param address 7-bit I2C address to write to
* @param data pointer to the buffer containing the data to write
* @param count number of bytes to read from the buffer and send over I2C
* @returns 0 on success, error code otherwise
*/
int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data,
uint16_t count);
/**
* Sleep for a given number of microseconds. The function should delay the
* execution approximately, but no less than, the given time.
*
* When using hardware i2c:
* Despite the unit, a <10 millisecond precision is sufficient.
*
* When using software i2c:
* The precision needed depends on the desired i2c frequency, i.e. should be
* exact to about half a clock cycle (defined in
* `SENSIRION_I2C_CLOCK_PERIOD_USEC` in `sensirion_sw_i2c_gpio.h`).
*
* Example with 400kHz requires a precision of 1 / (2 * 400kHz) == 1.25usec.
*
* @param useconds the sleep time in microseconds
*/
void sensirion_i2c_hal_sleep_usec(uint32_t useconds);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SENSIRION_I2C_HAL_H */

@ -0,0 +1 @@
Subproject commit bcbb2190324d9d39ae38619dd4a8931a0b8cf049

Wyświetl plik

@ -20,6 +20,7 @@ add_subdirectory(breakout_bme280)
add_subdirectory(breakout_as7262)
add_subdirectory(breakout_bh1745)
add_subdirectory(breakout_icp10125)
add_subdirectory(breakout_scd41)
add_subdirectory(pico_display)
add_subdirectory(pico_display_2)

Wyświetl plik

@ -0,0 +1,16 @@
set(OUTPUT_NAME scd41_demo)
add_executable(
${OUTPUT_NAME}
scd41_demo.cpp
)
# enable usb output, disable uart output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_enable_stdio_uart(${OUTPUT_NAME} 1)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib scd4x)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,101 @@
/*
* Copyright (c) 2021, 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 <stdio.h> // printf
#include "scd4x_i2c.h"
#include "sensirion_common.h"
#include "sensirion_i2c_hal.h"
#include "pico/stdlib.h"
#include "common/pimoroni_i2c.hpp"
using namespace pimoroni;
I2C i2c(BOARD::BREAKOUT_GARDEN);
/**
* TO USE CONSOLE OUTPUT (PRINTF) IF NOT PRESENT ON YOUR PLATFORM
*/
//#define printf(...)
int main(void) {
stdio_init_all();
int16_t error = 0;
sensirion_i2c_hal_init(&i2c);
// Clean up potential SCD40 states
scd4x_wake_up();
scd4x_stop_periodic_measurement();
scd4x_reinit();
uint16_t serial_0;
uint16_t serial_1;
uint16_t serial_2;
error = scd4x_get_serial_number(&serial_0, &serial_1, &serial_2);
if (error) {
printf("Error executing scd4x_get_serial_number(): %i\n", error);
} else {
printf("serial: 0x%04x%04x%04x\n", serial_0, serial_1, serial_2);
}
// Start Measurement
error = scd4x_start_periodic_measurement();
if (error) {
printf("Error executing scd4x_start_periodic_measurement(): %i\n",
error);
}
printf("Waiting for first measurement... (5 sec)\n");
for (;;) {
// Read Measurement
sensirion_i2c_hal_sleep_usec(5000000);
uint16_t co2;
int32_t temperature;
int32_t humidity;
error = scd4x_read_measurement(&co2, &temperature, &humidity);
if (error) {
printf("Error executing scd4x_read_measurement(): %i\n", error);
} else if (co2 == 0) {
printf("Invalid sample detected, skipping.\n");
} else {
printf("CO2: %u\n", co2);
printf("Temperature: %ld m°C\n", temperature);
printf("Humidity: %ld mRH\n", humidity);
}
}
return 0;
}