/* SENSOR MODULE Copyright (C) 2016-2019 by Xose PĂ©rez Copyright (C) 2020 by Maxim Prokhorov */ #pragma once #include "espurna.h" //-------------------------------------------------------------------------------- namespace sensor { namespace type { enum Type : unsigned char { Base = 0, Emon = 1 << 0, Analog = 1 << 1 }; } // namespace type enum class Unit : int { Min_, None, Celcius, Farenheit, Kelvin, Percentage, Hectopascal, Ampere, Volt, Voltampere, Kilovoltampere, VoltampereReactive, KilovoltampereReactive, Watt, Kilowatt, WattSecond, Joule = WattSecond, KilowattHour, PartsPerMillion, Ohm, MicrogrammPerCubicMeter, // The concentration of an air pollutant MilligrammPerCubicMeter, // Lux, UltravioletIndex, // "measurement of the strength of sunburn-producing ultraviolet (UV) radiation at a particular place and time" // (XXX: Not a unit. Distinguish from None and specify decimals) CountsPerMinute, // Unit of local dose rate (Geiger counting) MicrosievertPerHour, // 2nd unit of local dose rate (Geiger counting) Meter, Hertz, Ph, Max_ }; // Base units are 32 bit since we are the fastest with them. struct Ws { Ws(); Ws(uint32_t); uint32_t value; }; struct Wh { Wh(); Wh(Ws); Wh(uint32_t); uint32_t value; }; struct KWh { KWh(); KWh(Ws); KWh(Wh); KWh(uint32_t); uint32_t value; }; struct Energy { constexpr static uint32_t KwhMultiplier = 3600000ul; constexpr static uint32_t KwhLimit = ((1ul << 31ul) / KwhMultiplier); Energy() = default; // TODO: while we accept ws >= the kwh conversion limit, // should this be dealt with on the unit level? Energy(double); Energy(KWh, Ws); Energy(KWh); Energy(Wh); Energy(Ws); // Sets internal counters to zero void reset(); // Check whether we have *any* energy recorded. Can be zero: // - on cold boot // - on overflow // - when we call `reset()` operator bool(); // Generic conversion as-is double asDouble(); // Convert back to input unit, with overflow mechanics when kwh values goes over 32 bit Ws asWs(); // Generic sensors output energy in joules / watt-second Energy& operator +=(Ws); Energy operator +(Ws); // But sometimes we want to accept asDouble() value back Energy& operator =(double); // We are storing a kind-of integral and fractional parts // Using watt-second to avoid loosing precision, we don't expect these to be accessed directly KWh kwh; Ws ws; }; struct Value { double get(); double last; double reported; unsigned char decimals; }; } using MagnitudeReadHandler = void(*)(const String&, unsigned char, double, const char*); void sensorSetMagnitudeRead(MagnitudeReadHandler handler); void sensorSetMagnitudeReport(MagnitudeReadHandler handler); String magnitudeUnits(unsigned char index); String magnitudeDescription(unsigned char index); unsigned char magnitudeType(unsigned char index); unsigned char magnitudeIndex(unsigned char index); String magnitudeTopicIndex(unsigned char index); unsigned char magnitudeCount(); sensor::Value magnitudeValue(unsigned char index); void magnitudeFormat(const sensor::Value& value, char* output, size_t size); // XXX: without param name it is kind of vague what exactly unsigned char is // consider adding stronger param type e.g. enum class String magnitudeTopic(unsigned char type); String magnitudeName(unsigned char type); String sensorError(unsigned char error); void sensorWebSocketMagnitudes(JsonObject& root, const String& prefix); unsigned char sensorCount(); void sensorSetup(); void sensorLoop();