Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[submodule "boards/components/BME280/BME280_SensorAPI"]
path = boards/components/BME280/BME280_SensorAPI
url = https://github.com/boschsensortec/BME280_SensorAPI.git
[submodule "boards/components/LSM6DSO/lsm6dso-pid"]
path = boards/components/LSM6DSO/lsm6dso-pid
url = https://github.com/STMicroelectronics/lsm6dso-pid.git
[submodule "boards/components/ring_buffer/ultra-low-latency-ring-buffer"]
path = boards/components/ring_buffer/ultra-low-latency-ring-buffer
url = https://github.com/cale-cmd/ultra-low-latency-ring-buffer.git
9 changes: 8 additions & 1 deletion boards/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.22)

find_package(Git QUIET)
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_SUBMODULE_RESULT)

set(CMAKE_CXX_STANDARD 23)

set(IDF_TOOLCHAIN clang)

Expand Down
1 change: 1 addition & 0 deletions boards/components/BME280/BME280_SensorAPI
Submodule BME280_SensorAPI added at c90d41
2 changes: 2 additions & 0 deletions boards/components/BME280/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS "BME280_SensorAPI/bme280.c"
INCLUDE_DIRS ".")
2 changes: 2 additions & 0 deletions boards/components/LSM6DSO/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS "lsm6dso-pid/lsm6dso_reg.c"
INCLUDE_DIRS ".")
1 change: 1 addition & 0 deletions boards/components/LSM6DSO/lsm6dso-pid
Submodule lsm6dso-pid added at f3720e
1 change: 1 addition & 0 deletions boards/components/ring_buffer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
idf_component_register(INCLUDE_DIRS ".")
1 change: 1 addition & 0 deletions boards/components/ring_buffer/ring_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "ultra-low-latency-ring-buffer/src/ring_buffer.cpp"
3 changes: 2 additions & 1 deletion boards/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
file(GLOB_RECURSE MAIN_SOURCES "*.cpp")

idf_component_register(SRCS ${MAIN_SOURCES}
INCLUDE_DIRS ".")
INCLUDE_DIRS "."
REQUIRES driver esp_timer BME280 LSM6DSO ring_buffer)
23 changes: 23 additions & 0 deletions boards/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
menu "Rocket board configuration"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"

config I2C_SDA_GPIO
int "I2C SDA GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 7

config I2C_SCL_GPIO
int "I2C SCL GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 6

config BME280_I2C_ADDR
hex "BME280 I2C address"
range 0x00 0xFF
default 0x76

config LSM6DSO_I2C_ADDR
hex "LSM6DSO I2C address"
range 0x00 0xFF
default 0x6B
endmenu
131 changes: 131 additions & 0 deletions boards/main/bme280.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#include "bme280.h"

#include <unistd.h>

#include <ctime>
#include <expected>
#include <functional>
#include <utility>
#include <variant>

#include "BME280_SensorAPI/bme280.h"
#include "common.h"
#include "esp_log.h"
#include "i2c.h"
#include "sdkconfig.h"
#include "storage.h"

namespace bme280 {

constexpr inline const char *TAG = "BME280";

// Indicates that an error has occurred during initialization and all subsequent
// operations will fail.
bool g_init_error = false;
// Sensor driver context
struct bme280_dev g_sensor;

// I/O functions passed to bme280 driver
BME280_INTF_RET_TYPE platform_read(uint8_t reg_addr, uint8_t *reg_data,
uint32_t length, void *intf_ptr) {
return i2c::read(CONFIG_BME280_I2C_ADDR, reg_addr, reg_data, length)
.transform([](Success) -> BME280_INTF_RET_TYPE { return 0; })
.value_or(-1);
}

BME280_INTF_RET_TYPE platform_write(uint8_t reg_addr, const uint8_t *reg_data,
uint32_t length, void *intf_ptr) {
return i2c::write(CONFIG_BME280_I2C_ADDR, reg_addr, reg_data, length)
.transform([](Success) -> BME280_INTF_RET_TYPE { return 0; })
.value_or(-1);
}

void bme280_delay_us(uint32_t period, void *intf_ptr) { usleep(period); }

auto init() -> std::expected<Success, Error> {
g_sensor.intf = BME280_I2C_INTF;
g_sensor.intf_ptr = nullptr; // Not used
g_sensor.read = platform_read;
g_sensor.write = platform_write;
g_sensor.delay_us = bme280_delay_us;

int8_t res = bme280_init(&g_sensor);
if (res != BME280_OK) {
ESP_LOGE(TAG, "sensor not found. %d", res);
g_init_error = true;
return std::unexpected(Error::BME280_INIT_FAILED);
}

struct bme280_settings settings;

// Filter is of no use to us, because we need to know values at the moment,
// even if they reach extremes and last only a short while
settings.filter = BME280_FILTER_COEFF_OFF;

// Only the pressure is oversampled to provide higher accuracy
settings.osr_h = BME280_OVERSAMPLING_1X;
settings.osr_p = BME280_OVERSAMPLING_16X;
settings.osr_t = BME280_OVERSAMPLING_1X;

// Measure continuously
settings.standby_time = BME280_STANDBY_TIME_0_5_MS;

res = bme280_set_sensor_settings(BME280_SEL_ALL_SETTINGS, &settings,
&g_sensor);
if (res != BME280_OK) {
ESP_LOGE(TAG, "Set settings failed");
g_init_error = true;
return std::unexpected(Error::BME280_INIT_FAILED);
}

res = bme280_set_sensor_mode(BME280_POWERMODE_NORMAL, &g_sensor);
if (res != BME280_OK) {
ESP_LOGE(TAG, "Set mode failed");
g_init_error = true;
return std::unexpected(Error::BME280_INIT_FAILED);
}

uint32_t period = 0;
bme280_cal_meas_delay(&period, &settings);
ESP_LOGD(TAG, "Measurement time [ms]: %f", period / 1000.0f);

return Success{};
}

static auto readData() -> std::expected<Data, Error> {
Data data{};
if (g_init_error) {
return std::unexpected(Error::BME280_INIT_FAILED);
}

struct bme280_data raw_data{};
if (bme280_get_sensor_data(BME280_PRESS, &raw_data, &g_sensor) != 0) {
ESP_LOGE(TAG, "Read failed. Reinitializing...");
init();
g_init_error = false;
return std::unexpected(Error::BME280_READ_FAILED);
}
data.air_pressure = static_cast<float>(
raw_data.pressure); // We don't need double precision
if (bme280_get_sensor_data(BME280_HUM, &raw_data, &g_sensor) != 0) {
return std::unexpected(Error::BME280_READ_FAILED);
}
data.humidity = static_cast<float>(raw_data.humidity);
if (bme280_get_sensor_data(BME280_TEMP, &raw_data, &g_sensor) != 0) {
return std::unexpected(Error::BME280_READ_FAILED);
}
data.temperature = static_cast<float>(raw_data.temperature);

return data;
}

auto readAndProcessData() -> std::expected<Success, Error> {
auto ret = readData();

return ret.and_then([](Data const &data) -> std::expected<Success, Error> {
storage::postBarometerData(data);
return Success{};
});
}

} // namespace bme280
18 changes: 18 additions & 0 deletions boards/main/bme280.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <expected>

#include "common.h"

namespace bme280 {

struct Data {
float air_pressure;
float humidity;
float temperature;
};

auto init() -> std::expected<Success, Error>;
auto readAndProcessData() -> std::expected<Success, Error>;

} // namespace bme280
22 changes: 22 additions & 0 deletions boards/main/common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include <cstdint>
#include <expected>

struct Success {};

enum class Error : uint8_t {
FAILURE,

I2C_INIT_FAILED = 0x10,
I2C_WRITE_FAILED,
I2C_READ_FAILED,

BME280_INIT_FAILED = 0x20,
BME280_READ_FAILED,

LSM6DSO_INIT_FAILED = 0x30,
LSM6DSO_READ_FAILED,

STORAGE_INIT_FAILED = 0x40,
};
101 changes: 101 additions & 0 deletions boards/main/i2c.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "i2c.h"

#include <cstring>
#include <expected>

#include "common.h"
#include "driver/i2c.h"
#include "esp_log.h"

namespace i2c {

constexpr inline const char *TAG = "I2C";

// The project uses only one I2C bus
constexpr inline i2c_port_t BUS_NUMBER = I2C_NUM_0;

constexpr inline int FREQUENCY = 400000;

constexpr inline uint32_t TIMEOUT = 50;

auto init() -> std::expected<Success, Error> {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = CONFIG_I2C_SDA_GPIO,
.scl_io_num = CONFIG_I2C_SCL_GPIO,
.sda_pullup_en = true,
.scl_pullup_en = true,
.master =
{
.clk_speed = FREQUENCY,
},
.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL,
};

if (i2c_param_config(BUS_NUMBER, &conf) != ESP_OK) {
ESP_LOGE(TAG, "Config error");
return std::unexpected(Error::I2C_INIT_FAILED);
}

if (i2c_driver_install(BUS_NUMBER, conf.mode, 0, 0, 0) != ESP_OK) {
ESP_LOGE(TAG, "Init error");
return std::unexpected(Error::I2C_INIT_FAILED);
}

return Success{};
}

auto read(uint8_t addr, uint8_t reg, uint8_t *buffer, uint16_t size)
-> std::expected<Success, Error> {
if (i2c_master_write_read_device(BUS_NUMBER, addr, &reg, 1, buffer, size,
TIMEOUT / portTICK_PERIOD_MS) != ESP_OK) {
ESP_LOGE(TAG, "Failed to read");
return std::unexpected(Error::I2C_READ_FAILED);
}

return Success{};
}

static auto performWriteTransaction(uint8_t addr, uint8_t reg,
const uint8_t *buffer, uint16_t size,
i2c_cmd_handle_t command)
-> std::expected<Success, Error> {
if (i2c_master_start(command) != ESP_OK) {
return std::unexpected(Error::I2C_WRITE_FAILED);
}
if (i2c_master_write_byte(command, addr << 1U, true) != ESP_OK) {
return std::unexpected(Error::I2C_WRITE_FAILED);
}
if (i2c_master_write_byte(command, reg, true) != ESP_OK) {
return std::unexpected(Error::I2C_WRITE_FAILED);
}
if (i2c_master_write(command, buffer, size, true) != ESP_OK) {
return std::unexpected(Error::I2C_WRITE_FAILED);
}
if (i2c_master_stop(command) != ESP_OK) {
return std::unexpected(Error::I2C_WRITE_FAILED);
}
if (i2c_master_cmd_begin(BUS_NUMBER, command, TIMEOUT) != ESP_OK) {
return std::unexpected(Error::I2C_WRITE_FAILED);
}
return Success{};
}

auto write(uint8_t addr, uint8_t reg, const uint8_t *buffer, uint16_t size)
-> std::expected<Success, Error> {
uint8_t command_buffer[I2C_LINK_RECOMMENDED_SIZE(2)] = {0};
i2c_cmd_handle_t command = i2c_cmd_link_create_static(
command_buffer, I2C_LINK_RECOMMENDED_SIZE(2));

auto res = performWriteTransaction(addr, reg, buffer, size, command);

i2c_cmd_link_delete_static(command);

return res.or_else([](Error const &error) -> std::expected<Success, Error> {
ESP_LOGE(TAG, "Failed to write");

return std::unexpected(error);
});
}

} // namespace i2c
15 changes: 15 additions & 0 deletions boards/main/i2c.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <cstdint>

#include "common.h"

namespace i2c {

auto init() -> std::expected<Success, Error>;
auto read(uint8_t addr, uint8_t reg, uint8_t *buffer, uint16_t size)
-> std::expected<Success, Error>;
auto write(uint8_t addr, uint8_t reg, const uint8_t *buffer, uint16_t size)
-> std::expected<Success, Error>;

} // namespace i2c
Loading