From 9c07c2b85d908f699f8ba590b1d60232157eca90 Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Wed, 23 Oct 2024 22:57:18 -0400 Subject: test_fw: read veml and bme --- test_fw/.clang-format | 54 +++++++++++++++++ test_fw/.gitignore | 1 + test_fw/include/i2c_scan.h | 37 ++++++++++++ test_fw/include/veml.h | 140 +++++++++++++++++++++++++++++++++++++++++++++ test_fw/platformio.ini | 9 +++ test_fw/src/main.cpp | 103 +++++++++++++++++++++++++++++++++ test_fw/src/veml.cpp | 70 +++++++++++++++++++++++ 7 files changed, 414 insertions(+) create mode 100644 test_fw/.clang-format create mode 100644 test_fw/.gitignore create mode 100644 test_fw/include/i2c_scan.h create mode 100644 test_fw/include/veml.h create mode 100644 test_fw/platformio.ini create mode 100644 test_fw/src/main.cpp create mode 100644 test_fw/src/veml.cpp diff --git a/test_fw/.clang-format b/test_fw/.clang-format new file mode 100644 index 0000000..823d9ba --- /dev/null +++ b/test_fw/.clang-format @@ -0,0 +1,54 @@ +# Generated from CLion C/C++ Code Style settings +--- +Language: Cpp +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignOperands: false +AlignTrailingComments: false +AlwaysBreakTemplateDeclarations: Yes +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: false + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: true + BeforeWhile: true + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBraces: Custom +BreakConstructorInitializers: AfterColon +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 100 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +IncludeCategories: + - Regex: '^<.*' + Priority: 1 + - Regex: '^".*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseBlocks: true +IndentWidth: 4 +InsertNewlineAtEOF: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +TabWidth: 4 +... diff --git a/test_fw/.gitignore b/test_fw/.gitignore new file mode 100644 index 0000000..03f4a3c --- /dev/null +++ b/test_fw/.gitignore @@ -0,0 +1 @@ +.pio diff --git a/test_fw/include/i2c_scan.h b/test_fw/include/i2c_scan.h new file mode 100644 index 0000000..1fc81c1 --- /dev/null +++ b/test_fw/include/i2c_scan.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +namespace ocularium { + inline void scan(TwoWire& wire, bool post_delay = true) + { + for(auto address = 1; address < 127; address++) + { + wire.beginTransmission(address); + const uint8_t error = wire.endTransmission(); + + if (error == 0) + { + Serial.print("I2C device found at address 0x"); + if (address<16) + Serial.print("0"); + Serial.print(address,HEX); + Serial.println(); + } + else if (error==4) + { + Serial.print("Unknown error at address 0x"); + if (address<16) + Serial.print("0"); + Serial.println(address,HEX); + } + } + + if (post_delay) + { + // Let the bus settle before doing anything else. + delay(10); + } + } +} \ No newline at end of file diff --git a/test_fw/include/veml.h b/test_fw/include/veml.h new file mode 100644 index 0000000..89282c8 --- /dev/null +++ b/test_fw/include/veml.h @@ -0,0 +1,140 @@ +#pragma once + +#include + +namespace ocularium +{ + namespace veml + { + enum class Reg: uint8_t { + CONFIG = 0x0, + THRS_HIGH = 0x1, + THRS_LOW = 0x2, + POWER = 0x3, + LUX = 0x4, + WHITE = 0x5, + INT_STATUS = 0x6, + CHIPID = 0x7, + }; + + enum class AmbientLightGain: uint8_t + { + Unity = 0b00, + TwoX = 0b01, + Quarter = 0b11, + Eighth = 0b10, + }; + + enum class IntegrationTime: uint8_t + { + Ms25 = 0b1100, + Ms50 = 0b1000, + Ms100 = 0b0000, + Ms200 = 0b0001, + Ms400 = 0b0010, + Ms800 = 0b0011, + }; + + enum class PowerMode: uint8_t + { + Mode1 = 0b00, + Mode2 = 0b01, + Mode3 = 0b10, + Mode4 = 0b11, + }; + + struct Config + { + bool shutdown = false; + bool interrupt = false; + AmbientLightGain gain = AmbientLightGain::Eighth; + IntegrationTime integration = IntegrationTime::Ms100; + + uint16_t to_reg() const; + + int32_t gain_factor() const + { + int32_t gain = 0; + + switch (this->gain) + { + case AmbientLightGain::TwoX: + gain = 1; + break; + + case AmbientLightGain::Unity: + gain = 2; + break; + + case AmbientLightGain::Eighth: + gain = 8; + break; + + case AmbientLightGain::Quarter: + gain = 16; + break; + + default: + return -1; + } + + int32_t integ = 0; + + switch (integration) + { + case IntegrationTime::Ms800: + integ = 1; + break; + + case IntegrationTime::Ms400: + integ = 2; + break; + + case IntegrationTime::Ms200: + integ = 4; + break; + + case IntegrationTime::Ms100: + integ = 8; + break; + + case IntegrationTime::Ms50: + integ = 16; + break; + + case IntegrationTime::Ms25: + integ = 32; + break; + + default: + return -1; + } + + return integ * gain; + } + }; + + struct VEML { + static constexpr uint8_t ADDR = 0x10; + static constexpr uint16_t CHIPID = 0xc481; + + explicit VEML(TwoWire& wire) : wire(wire) {} + + bool init(const Config& config = Config{}); + + uint16_t chipid() const; + float lux() const; + + void set_config(const Config& config); + + private: + TwoWire& wire; + Config config = Config{}; + + void write_reg(Reg reg, uint16_t val) const; + uint16_t read_reg(Reg reg) const; + }; + } + + using veml::VEML; +} \ No newline at end of file diff --git a/test_fw/platformio.ini b/test_fw/platformio.ini new file mode 100644 index 0000000..661a561 --- /dev/null +++ b/test_fw/platformio.ini @@ -0,0 +1,9 @@ +[env:pico] +platform = raspberrypi +board = pico +framework = arduino +lib_deps = + adafruit/Adafruit BME680 Library@^2.0.5 + etlcpp/Embedded Template Library@^20.39.4 +build_flags = + -std=c++2a diff --git a/test_fw/src/main.cpp b/test_fw/src/main.cpp new file mode 100644 index 0000000..c6d3834 --- /dev/null +++ b/test_fw/src/main.cpp @@ -0,0 +1,103 @@ +#include +#include + +#include + +#include + +#include "i2c_scan.h" +#include "veml.h" + +constexpr auto SDA = 6; +constexpr auto SCL = 23; + +constexpr auto BME_ADDR = 0x76; + +static TwoWire wire(SDA, SCL); + +static Adafruit_BME680 bme(&wire); +static ocularium::VEML veml(wire); + +struct init_check +{ + const String name; + bool (*f)(); + bool succeeded; +}; + +static init_check STARTUP_CHECKS[] = { + { + .name = String("bme680"), + .f = [] { return bme.begin(BME_ADDR); }, + .succeeded = false, + }, + { + .name = String("veml7700"), + .f = []{ return veml.init(ocularium::veml::Config { + .gain = ocularium::veml::AmbientLightGain::Quarter, + }); }, + .succeeded = false, + } +}; + +void startup_checks() +{ + auto success = true; + + do + { + success = true; + + for (auto & check : STARTUP_CHECKS) + { + if (check.succeeded) + continue; + + Serial.print("checking " + check.name + "... "); + + check.succeeded = check.f(); + success = success && check.succeeded; + + if (check.succeeded) Serial.println("ok"); + else Serial.println("bad"); + } + + if (!success) delay(10); + } while (!success); +} + +void setup() { + Serial.begin(115200); + delay(2000); + + Serial.println("boot"); + wire.begin(); + + ocularium::scan(wire); + startup_checks(); + + Serial.println("sensors initialized"); +} + +void loop() { + static uint32_t bme_target_millis = 0; + + if (millis() >= bme_target_millis) + { + if (bme_target_millis != 0 && !bme.endReading()) + { + Serial.println("bme read error!"); + } + + bme_target_millis = bme.beginReading(); + } + + Serial.print("lux: " + String(veml.lux())); + Serial.print(", temp: " + String(bme.temperature)); + Serial.print(", hum: " + String(bme.humidity)); + Serial.print(", pres: " + String(bme.pressure)); + Serial.print(", gas: " + String(bme.gas_resistance)); + Serial.println(); + + delay(100); +} \ No newline at end of file diff --git a/test_fw/src/veml.cpp b/test_fw/src/veml.cpp new file mode 100644 index 0000000..020a380 --- /dev/null +++ b/test_fw/src/veml.cpp @@ -0,0 +1,70 @@ +#include "veml.h" + +constexpr float BASE_LUX_PER_LSB = 0.0042f; + +using namespace ocularium::veml; + + +uint16_t Config::to_reg() const +{ + uint16_t result = 0; + + result |= shutdown ? 1 : 0; + result |= interrupt ? 1 : 0; + result |= static_cast(integration) << 6; + result |= static_cast(gain) << 11; + + return result; +} + +bool VEML::init(const Config& config) { + if (chipid() != CHIPID) return false; + + set_config(config); + write_reg(Reg::POWER, 0); + + return true; +} + +uint16_t VEML::chipid() const +{ + return read_reg(Reg::CHIPID); +} + +float VEML::lux() const +{ + const auto val = read_reg(Reg::LUX) * config.gain_factor(); + + return static_cast(val) * BASE_LUX_PER_LSB; +} + +void VEML::set_config(const Config& config) +{ + this->config = config; + write_reg(Reg::CONFIG, config.to_reg()); +} + +void VEML::write_reg(const Reg reg, const uint16_t val) const +{ + wire.beginTransmission(ADDR); + wire.write(static_cast(reg)); + wire.write(val & 0xff); + wire.write(val >> 8); + wire.endTransmission(); +} + +uint16_t VEML::read_reg(const Reg reg) const +{ + wire.flush(); + + wire.beginTransmission(ADDR); + wire.write(static_cast(reg)); + wire.endTransmission(false); + wire.requestFrom(ADDR, 2); + wire.endTransmission(true); + + auto result = static_cast(wire.read()); + result |= wire.read() << 8; + + return result; +} -- cgit v1.3.1