aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Perry <np@npry.dev>2024-10-23 22:57:18 -0400
committerNathan Perry <np@npry.dev>2024-10-23 22:57:18 -0400
commit9c07c2b85d908f699f8ba590b1d60232157eca90 (patch)
tree750796b85ad46e1aff8f55da2d1e9f081ee208d6
parentf2fc1133d5ddf507d92192310f3f3c71fb1c0ba7 (diff)
test_fw: read veml and bme
-rw-r--r--test_fw/.clang-format54
-rw-r--r--test_fw/.gitignore1
-rw-r--r--test_fw/include/i2c_scan.h37
-rw-r--r--test_fw/include/veml.h140
-rw-r--r--test_fw/platformio.ini9
-rw-r--r--test_fw/src/main.cpp103
-rw-r--r--test_fw/src/veml.cpp70
7 files changed, 414 insertions, 0 deletions
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 <Arduino.h>
+#include <Wire.h>
+
+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 <Wire.h>
+
+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 <Arduino.h>
+#include <Wire.h>
+
+#include <Adafruit_BME680.h>
+
+#include <hardware/pio.h>
+
+#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<uint8_t>(integration) << 6;
+ result |= static_cast<uint8_t>(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<float>(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<uint8_t>(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<uint8_t>(reg));
+ wire.endTransmission(false);
+ wire.requestFrom(ADDR, 2);
+ wire.endTransmission(true);
+
+ auto result = static_cast<uint16_t>(wire.read());
+ result |= wire.read() << 8;
+
+ return result;
+}