| // Copyright 2024 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| #pragma once |
| |
| #include "modules/pubsub/pubsub_events.h" |
| #include "pw_metric/metric.h" |
| #include "pw_result/result.h" |
| #include "pw_status/status.h" |
| #include "pw_sync/interrupt_spin_lock.h" |
| #include "pw_sync/lock_annotations.h" |
| #include "pw_sync/thread_notification.h" |
| |
| namespace sense { |
| |
| class AirSensor { |
| public: |
| // Default starting values representing decent air quality. |
| static constexpr float kDefaultTemperature = 20.f; |
| static constexpr float kDefaultPressure = 100.f; |
| static constexpr float kDefaultHumidity = 40.f; |
| static constexpr float kDefaultGasResistance = 50000.f; |
| |
| /// Threshold presets for convenience. |
| /// |
| /// The AirSensor is not connected to any output directly, and thus the use of |
| /// colors as names for the various thresholds is strictly speaking only to |
| /// provide an intuitive idea of the range from very bad (kRed) to very good |
| /// (kBlue) air quality. Note that the thresholds for raising and silencing |
| /// alarms can be set to any 10 bit values. This enum is strictly for |
| /// convenience. |
| enum class Score : uint16_t { |
| kRed = 0, |
| kOrange = 128, |
| kYellow = 256, |
| kLightGreen = 384, |
| kGreen = 512, |
| kBlueGreen = 640, |
| kCyan = 768, |
| kLightBlue = 896, |
| kBlue = 1023, |
| }; |
| |
| static constexpr uint16_t kMaxScore = static_cast<uint16_t>(Score::kBlue); |
| static constexpr uint16_t kAverageScore = static_cast<uint16_t>(Score::kCyan); |
| |
| /// Get the RGB values corresponding to an air quality score. |
| static LedValue GetLedValue(uint16_t score); |
| static LedValue GetLedValue(Score score) { |
| return GetLedValue(static_cast<uint16_t>(score)); |
| } |
| |
| virtual ~AirSensor() = default; |
| |
| /// Returns the most recent temperature reading. |
| float temperature() const PW_LOCKS_EXCLUDED(lock_); |
| |
| /// Returns the most recent barometric pressure reading. |
| float pressure() const PW_LOCKS_EXCLUDED(lock_); |
| |
| /// Returns the most recent relative humidity reading. |
| float humidity() const PW_LOCKS_EXCLUDED(lock_); |
| |
| /// Returns the most recent gas resistance reading. |
| float gas_resistance() const PW_LOCKS_EXCLUDED(lock_); |
| |
| /// Returns a 10-bit air quality score from 0 (terrible) to 1023 (excellent). |
| uint16_t score() const PW_LOCKS_EXCLUDED(lock_); |
| |
| /// Sets up the sensor. |
| pw::Status Init() { return DoInit(); } |
| |
| /// Requests an air measurement. |
| /// |
| /// When the measurement is complete, ``Update`` will be called and the |
| /// given notification will be released. |
| pw::Status Measure(pw::sync::ThreadNotification& notification) |
| PW_LOCKS_EXCLUDED(lock_) { |
| return DoMeasure(notification); |
| } |
| |
| /// Like `Measure`, but runs synchronously and returns the same score as |
| /// `GetScore`. |
| pw::Result<uint16_t> MeasureSync() PW_LOCKS_EXCLUDED(lock_); |
| |
| /// Writes the metrics to logs. |
| void LogMetrics() { metrics_.Dump(); } |
| |
| protected: |
| AirSensor() = default; |
| |
| /// Records the results of an air measurement. |
| void Update(float temperature, |
| float pressure, |
| float humidity, |
| float gas_resistance) PW_LOCKS_EXCLUDED(lock_); |
| |
| private: |
| /// @copydoc `AirSensor::Init`. |
| /// |
| /// By default, does nothing. |
| virtual pw::Status DoInit() { return pw::OkStatus(); } |
| |
| /// @copydoc `AirSensor::Measure`. |
| virtual pw::Status DoMeasure(pw::sync::ThreadNotification& notification) |
| PW_LOCKS_EXCLUDED(lock_) = 0; |
| |
| mutable pw::sync::InterruptSpinLock lock_; |
| |
| // Thread safety: metric values should be atomic. |
| // |
| // Currently, they are not due to a bug, so they are guarded by |
| // `lock_`. Unfortunately it isn't possible to use a PW_GUARDED_BY annotation |
| // on them. |
| PW_METRIC_GROUP(metrics_, "air sensor"); |
| |
| // Directly read values. |
| PW_METRIC(metrics_, temperature_, "ambient temperature", kDefaultTemperature); |
| PW_METRIC(metrics_, pressure_, "barometric pressure", kDefaultPressure); |
| PW_METRIC(metrics_, humidity_, "relative humidity", kDefaultHumidity); |
| PW_METRIC(metrics_, gas_resistance_, "gas resistance", kDefaultGasResistance); |
| |
| // Derived values. |
| PW_METRIC(metrics_, count_, "number of measurements", 0u); |
| PW_METRIC(metrics_, quality_, "current air quality", 0.f); |
| PW_METRIC(metrics_, average_, "average air quality", 0.f); |
| PW_METRIC(metrics_, sum_of_squares_, "aggregate air quality variance", 0.f); |
| PW_METRIC(metrics_, score_, "air quality score", kAverageScore); |
| }; |
| |
| } // namespace sense |