Skip to content

Commit

Permalink
Merge pull request #108 from SJSURoboticsTeam/scd40-driver-libhal-2.0
Browse files Browse the repository at this point in the history
Scd40 driver libhal-science-2.0
  • Loading branch information
Viha123 authored Jan 23, 2024
2 parents cdd5259 + d9f7fa4 commit a47c064
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 1 deletion.
19 changes: 19 additions & 0 deletions code/science/implementations/scd40.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <libhal-util/i2c.hpp>
#include <libhal-util/serial.hpp>
#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(){}
4 changes: 3 additions & 1 deletion science/demos/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
51 changes: 51 additions & 0 deletions science/demos/applications/scd40_demo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <libhal-armcortex/dwt_counter.hpp>
#include <libhal-armcortex/startup.hpp>
#include <libhal-armcortex/system_control.hpp>
#include <libhal-util/serial.hpp>
#include <libhal-util/steady_clock.hpp>
#include <libhal/units.hpp>

#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
129 changes: 129 additions & 0 deletions science/platform-implementations/scd40.cpp
Original file line number Diff line number Diff line change
@@ -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> 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<hal::byte, 2> 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::scd40_read_data> scd40_nm::read() {
std::array<hal::byte, 2> read_address = {read_measurement_first_half, read_measurement_second_half };
std::array<hal::byte, 9> 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<hal::byte, 2> 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::scd40_settings> scd40_nm::get_settings() {
std::array<hal::byte, 2> read_address_temp = {get_temperature_offset_first_half, get_temperature_offset_second_half };
std::array<hal::byte, 2> read_address_alt = { get_sensor_altitude_first_half, get_sensor_altitude_second_half };
std::array<hal::byte, 6> 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<hal::byte, 4> 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<hal::byte, 4> 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<hal::byte, 4> 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<hal::byte, 2> 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<hal::byte, 3> 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;
}
72 changes: 72 additions & 0 deletions science/platform-implementations/scd40.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#pragma once
#include <libhal-util/i2c.hpp>
#include <libhal-util/serial.hpp>
#include <libhal-util/steady_clock.hpp>
#include <libhal/units.hpp>


//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<hal::byte, 2> data);
bool validate_crc(std::array<hal::byte, 3> 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<scd40> create(hal::i2c& p_i2c,hal::steady_clock& p_clock);
hal::status start();
hal::result<scd40_read_data> read();
hal::status stop();
hal::result<scd40_settings> get_settings();
hal::status set_settings( settings setting);
};

} // namespace science

0 comments on commit a47c064

Please sign in to comment.