diff --git a/code/science/implementations/scd40.cpp b/code/science/implementations/scd40.cpp new file mode 100644 index 0000000..2a8cd90 --- /dev/null +++ b/code/science/implementations/scd40.cpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include "scd40.hpp" + +scd40::create(hal::i2c& p_i2c, hal::steady_clock& clock){ + scd40 scd40 = scd40(p_i2c, clock); + HAL_CHECK(scd40.start()) + +} + +scd40::start(){} + +scd40::get_CO2{} + +scd40::get_RH(){} + +scd40::get_temp(){} \ No newline at end of file diff --git a/science/demos/CMakeLists.txt b/science/demos/CMakeLists.txt index 0a2307f..5262622 100644 --- a/science/demos/CMakeLists.txt +++ b/science/demos/CMakeLists.txt @@ -20,9 +20,11 @@ libhal_build_demos( DEMOS co2_demo pressure_sensor_demo + scd40_demo color_sensor_demo - + SOURCES + ../platform-implementations/scd40.cpp ../platform-implementations/pressure_sensor_bme680.cpp ../platform-implementations/color_sensor_opt4048.cpp diff --git a/science/demos/applications/scd40_demo.cpp b/science/demos/applications/scd40_demo.cpp new file mode 100644 index 0000000..f1d05bb --- /dev/null +++ b/science/demos/applications/scd40_demo.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include + +#include "../../platform-implementations/scd40.hpp" +#include "../hardware_map.hpp" + +using namespace hal::literals; +using namespace std::chrono_literals; +namespace sjsu::science { + +hal::status application(application_framework& p_framework) +{ + // configure drivers + auto& i2c2 = *p_framework.i2c; + auto& clock = *p_framework.steady_clock; + auto& terminal = *p_framework.terminal; + + auto scd40_sensor = HAL_CHECK(scd40::create(i2c2, clock)); + + while(true){ + //get settings test + // scd40_sensor.stop(); + // auto get = HAL_CHECK(scd40_sensor.get_settings()); + // auto temp = get.temp_offset; + // auto alt = get.altitude; + // hal::print<64>(terminal, "%-5.2f\t%-5.2f\n", temp, alt); + + // periodic readings are only updated every 5000ms (temperature) + hal::delay(clock, 5000ms); + auto rd = HAL_CHECK(scd40_sensor.read()); + auto co2_levels = rd.co2; + auto temp = rd.temp; + auto RH_levels = rd.rh; + + + // hal::print<64>(terminal, "%-5.2f\t%-5.2f\t%-5.2f\n", co2_levels, temp, RH_levels); + // hal::delay(clock, 500ms); + + hal::print<64>(terminal, "CO2 Levels: %f\n", co2_levels); + hal::print<64>(terminal, "Temperature %f\n", temp); + hal::print<64>(terminal, "RH Levels: %f\n", RH_levels); + + } + + return hal::success(); +} +} // namespace sjsu::science \ No newline at end of file diff --git a/science/platform-implementations/scd40.cpp b/science/platform-implementations/scd40.cpp new file mode 100644 index 0000000..9d1628c --- /dev/null +++ b/science/platform-implementations/scd40.cpp @@ -0,0 +1,129 @@ +// #pragma once + +#include "scd40.hpp" + +using namespace std::chrono_literals; +using scd40_nm = sjsu::science::scd40; + + +scd40_nm::scd40(hal::i2c& p_i2c, hal::steady_clock& p_clock) : m_i2c(p_i2c), m_clock(p_clock) {} + +hal::result scd40_nm::create(hal::i2c& p_i2c, hal::steady_clock& clock){ + scd40 scd40(p_i2c, clock); + HAL_CHECK(scd40.start()); + return scd40; + +} + +hal::status scd40_nm::start(){ + std::array start_address = { start_periodic_measurement_first_half, start_periodic_measurement_second_half }; + HAL_CHECK(hal::write(m_i2c, addresses::device_address, start_address, hal::never_timeout())); + return hal::success(); +} + +hal::result scd40_nm::read() { + std::array read_address = {read_measurement_first_half, read_measurement_second_half }; + std::array buffer; + + HAL_CHECK(hal::write(m_i2c,addresses::device_address, read_address)); + hal::delay(m_clock, 1ms); + HAL_CHECK(hal::read(m_i2c, addresses::device_address, buffer, hal::never_timeout())); + + scd40_nm::scd40_read_data rd; + rd.co2 = buffer[0] << 8 | buffer[1]; + rd.temp = (-45 + 175.0*(buffer[3] << 8 | buffer[4])/ (1 << 16)); + rd.rh = 100.0 * (buffer[6] << 8 | buffer[7]) / (1 << 16); + + return rd; +} + +hal::status scd40_nm::stop() { + std::array stop_address = { stop_periodic_measurement_first_half, stop_periodic_measurement_second_half }; + HAL_CHECK(hal::write(m_i2c, addresses::device_address, stop_address, hal::never_timeout())); + return hal::success(); +} + +hal::result scd40_nm::get_settings() { + std::array read_address_temp = {get_temperature_offset_first_half, get_temperature_offset_second_half }; + std::array read_address_alt = { get_sensor_altitude_first_half, get_sensor_altitude_second_half }; + std::array buffer; + + HAL_CHECK(hal::write(m_i2c,addresses::device_address, read_address_temp)); + hal::delay(m_clock, 1ms); + HAL_CHECK(hal::read(m_i2c, addresses::device_address, buffer, hal::never_timeout())); + + scd40_nm::scd40_settings get; + + get.temp_offset = 175 *(buffer[0] << 8 | buffer[1])/(1 << 16); + + HAL_CHECK(hal::write(m_i2c,addresses::device_address, read_address_alt)); + hal::delay(m_clock, 1ms); + HAL_CHECK(hal::read(m_i2c, addresses::device_address, buffer, hal::never_timeout())); + get.altitude = buffer[0]<<8 | buffer[1]; + + return get; +} + +hal::status scd40_nm::set_settings( struct settings setting) { + if(setting.set_temp != 0){ + int temp = int(((setting.set_temp*(1<<16)) / 175)) ; + hal::byte temp_first_half = (temp) >> 8; + hal::byte temp_second_half = temp & 0xff; + std::array set_temp_address = {set_temperature_offset_first_half, set_temperature_offset_second_half, temp_first_half, temp_second_half}; + HAL_CHECK(hal::write(m_i2c,addresses::device_address,set_temp_address)); + hal::delay(m_clock, 1ms); + } + if(setting.set_pressure != -1){ + int pressure = int(setting.set_pressure/100); + hal::byte pressure_first_half = (pressure) >> 8; + hal::byte pressure_second_half = pressure & 0xff; + std::array set_pressure_address = {set_ambient_pressure_first_half, set_ambient_pressure_second_half, pressure_first_half, pressure_second_half}; + HAL_CHECK(hal::write(m_i2c,addresses::device_address,set_pressure_address)); + hal::delay(m_clock, 1ms); + } else if(setting.set_alt != 0){ + int alt = int(setting.set_alt); + hal::byte alt_first_half = (alt) >> 8; + hal::byte alt_second_half = alt & 0xff; + std::array set_alt_address = {set_sensor_altitude_first_half, set_sensor_altitude_second_half, alt_first_half, alt_second_half}; + HAL_CHECK(hal::write(m_i2c,addresses::device_address,set_alt_address)); + hal::delay(m_clock, 1ms); + } + + return hal::success(); +} + +hal::byte scd40_nm::generate_crc(std::array data){ + uint16_t current_byte; + uint8_t crc_bit; + uint8_t crc = 0xFF; + uint8_t CRC8_POLYNOMIAL = 0x31; + + for (current_byte = 0; current_byte < 2 ; ++current_byte) { + crc ^= (data[current_byte]); + for (crc_bit = 8; crc_bit > 0; --crc_bit) { + if (crc & 0x80) + crc = (crc << 1) ^ CRC8_POLYNOMIAL; + else + crc = (crc << 1); + } + } + return crc; +} + +bool scd40_nm::validate_crc(std::array data) { + uint16_t current_byte; + uint8_t crc_bit; + uint8_t crc = 0xFF; + uint8_t CRC8_POLYNOMIAL = 0x31; + + for (current_byte = 0; current_byte < 3 ; ++current_byte) { + crc ^= (data[current_byte]); + for (crc_bit = 8; crc_bit > 0; --crc_bit) { + if (crc & 0x80) + crc = (crc << 1) ^ CRC8_POLYNOMIAL; + else + crc = (crc << 1); + } + } + return crc == 0; +} \ No newline at end of file diff --git a/science/platform-implementations/scd40.hpp b/science/platform-implementations/scd40.hpp new file mode 100644 index 0000000..5bb7360 --- /dev/null +++ b/science/platform-implementations/scd40.hpp @@ -0,0 +1,72 @@ +#pragma once +#include +#include +#include +#include + + +//TODO: maybe later add CRC and config for altitude, ambient pressure, temperature offset (or look at setting persistent values) + +namespace sjsu::science { +class scd40 +{ +private: + + scd40(hal::i2c& p_i2c, hal::steady_clock& p_clock); + hal::i2c& m_i2c; + hal::steady_clock& m_clock; + hal::byte generate_crc(std::array data); + bool validate_crc(std::array data); + + + //TODO: revise output type + enum addresses {// deal with hal::byte later + device_address = 0x62, + start_periodic_measurement_first_half = 0x21, + start_periodic_measurement_second_half = 0xb1, + read_measurement_first_half = 0xec, + read_measurement_second_half = 0x05, + stop_periodic_measurement_first_half = 0x3f, + stop_periodic_measurement_second_half = 0x86, + set_temperature_offset_first_half = 0x24, + set_temperature_offset_second_half = 0x1d, + get_temperature_offset_first_half = 0x23, + get_temperature_offset_second_half = 0x18, + set_sensor_altitude_first_half = 0x24, + set_sensor_altitude_second_half = 0x27, + get_sensor_altitude_first_half = 0x23, + get_sensor_altitude_second_half = 0x22, + set_ambient_pressure_first_half = 0xe0, + set_ambient_pressure_second_half = 0x00 + }; + +public: + struct scd40_read_data + { + double co2; + double temp; + double rh; + }; + + struct settings + { + float set_temp = 4; + float set_alt = 0; + float set_pressure = -1; + }; + + struct scd40_settings + { + float temp_offset; + float altitude; + }; + + static hal::result create(hal::i2c& p_i2c,hal::steady_clock& p_clock); + hal::status start(); + hal::result read(); + hal::status stop(); + hal::result get_settings(); + hal::status set_settings( settings setting); +}; + +} // namespace science \ No newline at end of file