/ firmware / src / sensors / barometric_pressure.cpp
barometric_pressure.cpp
  1  #include "barometric_pressure.h"
  2  #include "registry.h"
  3  #include <i2c.h>
  4  
  5  #include <Arduino.h>
  6  #include <Adafruit_LPS2X.h>
  7  
  8  namespace {
  9  
 10  constexpr uint16_t LPS25_INIT_SETTLE_MS = 200;
 11  
 12  Adafruit_LPS25 lps;
 13  bool available = false;
 14  uint8_t detected_bus = 0;
 15  
 16  }
 17  
 18  bool sensors::barometric_pressure::initialize() {
 19    available = false;
 20  
 21    hardware::i2c::DiscoveredDevice dev = {};
 22    if (!hardware::i2c::findDevice(0x5D, &dev)) {
 23      if (!hardware::i2c::findDevice(0x5C, &dev)) {
 24        return false;
 25      }
 26    }
 27  
 28    TwoWire *wire = (dev.bus == 0) ? &Wire : &Wire1;
 29    if (!lps.begin_I2C(dev.address, wire)) {
 30      return false;
 31    }
 32  
 33    lps.setDataRate(LPS25_RATE_25_HZ);
 34    delay(LPS25_INIT_SETTLE_MS);
 35  
 36    sensors_event_t discard_p, discard_t;
 37    lps.getEvent(&discard_p, &discard_t);
 38  
 39    detected_bus = dev.bus;
 40    available = true;
 41    Serial.printf("[pressure] LPS25 detected on bus %d at 0x%02X\n", dev.bus, dev.address);
 42  
 43    sensors::registry::add({
 44        .kind = SensorKind::BarometricPressure,
 45        .name = "Barometric Pressure",
 46        .isAvailable = sensors::barometric_pressure::isAvailable,
 47        .instanceCount = []() -> uint8_t { return 1; },
 48        .poll = [](uint8_t, void *out, size_t cap) -> bool {
 49            if (cap < sizeof(BarometricPressureSensorData)) return false;
 50            return sensors::barometric_pressure::access(
 51                static_cast<BarometricPressureSensorData *>(out));
 52        },
 53        .data_size = sizeof(BarometricPressureSensorData),
 54    });
 55    return true;
 56  }
 57  
 58  bool sensors::barometric_pressure::access(BarometricPressureSensorData *data) {
 59    if (!data) return false;
 60    if (!available) {
 61      data->ok = false;
 62      data->model = "none";
 63      return false;
 64    }
 65  
 66    sensors_event_t pressure_event, temp_event;
 67    if (!lps.getEvent(&pressure_event, &temp_event)) {
 68      data->ok = false;
 69      data->model = "LPS25";
 70      return false;
 71    }
 72  
 73    data->pressure_hpa = pressure_event.pressure;
 74    data->temperature_celsius = temp_event.temperature;
 75    data->model = "LPS25";
 76    data->ok = true;
 77    return true;
 78  }
 79  
 80  bool sensors::barometric_pressure::isAvailable() {
 81    return available;
 82  }
 83  
 84  #ifdef PIO_UNIT_TESTING
 85  
 86  #include <testing/utils.h>
 87  
 88  namespace sensors::barometric_pressure { void test(void); }
 89  
 90  static void test_pressure_init(void) {
 91    WHEN("the barometric pressure module is initialized");
 92    hardware::i2c::initialize();
 93    if (!sensors::barometric_pressure::initialize()) {
 94      TEST_IGNORE_MESSAGE("no LPS25 sensor connected");
 95      return;
 96    }
 97    TEST_ASSERT_TRUE_MESSAGE(sensors::barometric_pressure::isAvailable(),
 98      "device: LPS25 not available after initialization");
 99  }
100  
101  static void test_pressure_read(void) {
102    GIVEN("the LPS25 sensor is available");
103    WHEN("a reading is taken");
104    if (!sensors::barometric_pressure::isAvailable()) {
105      TEST_IGNORE_MESSAGE("no LPS25 sensor available");
106      return;
107    }
108    delay(500);
109    BarometricPressureSensorData data = {};
110    bool ok = sensors::barometric_pressure::access(&data);
111    if (!ok) {
112      TEST_IGNORE_MESSAGE("reading not ready yet");
113      return;
114    }
115    char msg[64];
116    snprintf(msg, sizeof(msg), "LPS25: %.2f hPa, %.2f C", data.pressure_hpa, data.temperature_celsius);
117    TEST_MESSAGE(msg);
118    TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(data.pressure_hpa,
119      "device: pressure reading is NaN or Inf");
120    TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(data.temperature_celsius,
121      "device: temperature reading is NaN or Inf");
122    TEST_ASSERT_GREATER_THAN_FLOAT_MESSAGE(900.0f, data.pressure_hpa,
123      "device: pressure below 900 hPa — sensor may be faulty");
124    TEST_ASSERT_LESS_THAN_FLOAT_MESSAGE(1100.0f, data.pressure_hpa,
125      "device: pressure above 1100 hPa — sensor may be faulty");
126  }
127  
128  void sensors::barometric_pressure::test(void) {
129    MODULE("Barometric Pressure");
130    RUN_TEST(test_pressure_init);
131    RUN_TEST(test_pressure_read);
132  }
133  
134  #endif