Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scd40 driver libhal 2.0 #108

Merged
merged 41 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9bdda38
scd40.hpp and scd40.cpp transfered
Nov 11, 2023
563d818
transfer scd40.cpp
MichaelYKersey Nov 11, 2023
84381b4
transfer scd40.hpp
MichaelYKersey Nov 11, 2023
3abde90
fix no return for read of scd40
MichaelYKersey Nov 11, 2023
f826011
finished scd40 file transfer
Nov 11, 2023
a54c388
git scd40.cpp indentaiton
MichaelYKersey Nov 11, 2023
7dd31ab
fix scd40 namespace
MichaelYKersey Nov 11, 2023
6b795ce
add member variables for scd40
MichaelYKersey Nov 11, 2023
0506bc2
fix member variable names for scd40
MichaelYKersey Nov 11, 2023
031a924
create starter demo
MichaelYKersey Nov 12, 2023
a2c75f7
scd40 demo
MichaelYKersey Nov 12, 2023
75b7bd0
get_co2 method
MichaelYKersey Nov 12, 2023
3da5489
get_RH method
MichaelYKersey Nov 12, 2023
7f820a7
get_RH and get_temp
Nov 12, 2023
72fe910
Revert "get_co2 method"
MichaelYKersey Nov 12, 2023
fde9a57
get_temp and get_RH
Nov 12, 2023
09f67c9
Merge branch 'scd40-driver-libhal-2.0' of github.com:SJSURoboticsTeam…
MichaelYKersey Nov 12, 2023
9bc5c49
scd40 demo
Nov 18, 2023
76bfda0
print statments added to scd40_demo.cpp
Nov 18, 2023
260d290
:rotatiing_light: fixed errors and added buffer reading
MichaelYKersey Nov 19, 2023
2f10cd0
:recycle: have get functions call the get buffer functions
MichaelYKersey Nov 19, 2023
102f8d4
correct where get_RH_buffer() reads in the buffer
MichaelYKersey Dec 4, 2023
cc42592
fix demo print and change to table
MichaelYKersey Dec 4, 2023
775a6c2
fix conan libhal version
MichaelYKersey Dec 4, 2023
4107ad0
remove call to removed method
MichaelYKersey Dec 9, 2023
b7bf426
read returns a struct for data
MichaelYKersey Dec 17, 2023
7d32f70
remove individual data read methods in scd40.hpp
MichaelYKersey Dec 17, 2023
3c06eea
Rename scd40 Adresses
MichaelYKersey Dec 30, 2023
9dba9f6
Reimplement scd40 stop
MichaelYKersey Dec 30, 2023
737eef4
Add scd40 Settting Addresses
MichaelYKersey Dec 30, 2023
ffa698e
Fixed scd40_demo to use struct
Dec 30, 2023
68e8bbb
Add crc fuction in scd40.hpp
MichaelYKersey Dec 30, 2023
32ef530
Fixed scd40_demo to use struct
Dec 30, 2023
312d262
Adapt CRC code from data sheet
MichaelYKersey Jan 6, 2024
ff6590d
Finished basic setting functions have not tested set_settings
Jan 7, 2024
8f55fe7
Seprate scd40 crc method into generate and validate
MichaelYKersey Jan 13, 2024
f36f904
Chages finally committed
Jan 13, 2024
30eddd2
merge conflict somewhat resolved
Jan 13, 2024
7494a44
Merge branch 'libhal-2.0-port-science' into scd40-driver-libhal-2.0
MichaelYKersey Jan 13, 2024
846f0e7
Gave option to set some of the settings
Jan 13, 2024
d9f7fa4
data type of settings and read_data channged from double to float
Jan 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
Kirthika-Ashokkumar marked this conversation as resolved.
Show resolved Hide resolved
if(setting.set_temp != 0){
int temp = int(((setting.set_temp*(1<<16)) / 175)) ;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int temp = int(((setting.set_temp*(1<<16)) / 175)) ;
int temp = int(((setting.set_temp*(1<<16)) / 175)) ;

Give these magic numbers names. 175 isn't some power of two so it must mean something. Fine the term in the datasheet and make a constexpr variables for it. Something like:

// Found on page X in datasheet name "<url to datasheet>"
constexpr std::uint32_t temperature_scalar = 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;
kammce marked this conversation as resolved.
Show resolved Hide resolved
}
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
enum addresses {// deal with hal::byte later
enum class addresses { // deal with hal::byte later

Great work using an enum for all of the addresses. Makes it easier to understand whats what. But its favorable to use an enum class as they get promoted to a type. You can static_cast<> them back to whatever integral repersation you need provided that representation does not truncate the enum value. You can also use hal::value() in <libhal-util/enum.hpp>

Copy link
Contributor Author

@MichaelYKersey MichaelYKersey Jan 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I talked to Corey and Gene to understand what you were saying. While on the topic they recommended using constants (likely in a separate file) for this use case. What are your thoughts on switching the values in the enum to static constants?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static constants are great! Sometimes the constructs of an enum work fine too just depends on what you are doing.

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;
Kirthika-Ashokkumar marked this conversation as resolved.
Show resolved Hide resolved
};

struct settings
{
double set_temp = 4;
double set_alt = 0;
double set_pressure = -1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do these magic numbers mean? Maybe make some constants or add some comments?

};

struct scd40_settings
{
double temp_offset;
double 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( struct settings setting);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These APIs should be documented in order to understand how they should be used.

};

} // namespace science