diff --git a/platformio.ini b/platformio.ini index 08d21665f..41f1ca764 100644 --- a/platformio.ini +++ b/platformio.ini @@ -160,4 +160,5 @@ lib_deps = https://github.com/KodinLanewave/INA3221@1.0.1 mprograms/QMC5883LCompass@1.2.3 dfrobot/DFRobot_RTU@1.0.3 - https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d \ No newline at end of file + https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d + robtillaart/INA226@0.6.0 diff --git a/src/Power.cpp b/src/Power.cpp index a354b74e2..ae0908ec6 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -72,8 +72,9 @@ static const uint8_t ext_chrg_detect_value = EXT_CHRG_DETECT_VALUE; #endif #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) -INA260Sensor ina260Sensor; INA219Sensor ina219Sensor; +INA226Sensor ina226Sensor; +INA260Sensor ina260Sensor; INA3221Sensor ina3221Sensor; #endif @@ -413,7 +414,20 @@ class AnalogBatteryLevel : public HasBatteryLevel #ifdef EXT_CHRG_DETECT return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value; #else +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && \ + !defined(DISABLE_INA_CHARGING_DETECTION) + if (hasINA()) { + // get current flow from INA sensor - negative value means power flowing into the battery + // default assuming BATTERY+ <--> INA_VIN+ <--> SHUNT RESISTOR <--> INA_VIN- <--> LOAD + LOG_DEBUG("Using INA on I2C addr 0x%x for charging detection", config.power.device_battery_ina_address); +#if defined(INA_CHARGING_DETECTION_INVERT) + return getINACurrent() > 0; +#else + return getINACurrent() < 0; +#endif + } return isBatteryConnect() && isVbusIn(); +#endif #endif } @@ -450,6 +464,9 @@ class AnalogBatteryLevel : public HasBatteryLevel { if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) { return ina219Sensor.getBusVoltageMv(); + } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == + config.power.device_battery_ina_address) { + return ina226Sensor.getBusVoltageMv(); } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == config.power.device_battery_ina_address) { return ina260Sensor.getBusVoltageMv(); @@ -460,6 +477,20 @@ class AnalogBatteryLevel : public HasBatteryLevel return 0; } + int16_t getINACurrent() + { + if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) { + return ina219Sensor.getCurrentMa(); + } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == + config.power.device_battery_ina_address) { + return ina226Sensor.getCurrentMa(); + } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first == + config.power.device_battery_ina_address) { + return ina3221Sensor.getCurrentMa(); + } + return 0; + } + bool hasINA() { if (!config.power.device_battery_ina_address) { @@ -469,6 +500,10 @@ class AnalogBatteryLevel : public HasBatteryLevel if (!ina219Sensor.isInitialized()) return ina219Sensor.runOnce() > 0; return ina219Sensor.isRunning(); + } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == + config.power.device_battery_ina_address) { + if (!ina226Sensor.isInitialized()) + return ina226Sensor.runOnce() > 0; } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == config.power.device_battery_ina_address) { if (!ina260Sensor.isInitialized()) @@ -1154,4 +1189,4 @@ bool Power::lipoInit() { return false; } -#endif \ No newline at end of file +#endif diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index 367643849..10133fca5 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -56,6 +56,8 @@ int32_t PowerTelemetryModule::runOnce() // therefore, we should only enable the sensor loop if measurement is also enabled if (ina219Sensor.hasSensor() && !ina219Sensor.isInitialized()) result = ina219Sensor.runOnce(); + if (ina226Sensor.hasSensor() && !ina226Sensor.isInitialized()) + result = ina226Sensor.runOnce(); if (ina260Sensor.hasSensor() && !ina260Sensor.isInitialized()) result = ina260Sensor.runOnce(); if (ina3221Sensor.hasSensor() && !ina3221Sensor.isInitialized()) @@ -170,6 +172,8 @@ bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m) #if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) if (ina219Sensor.hasSensor()) valid = ina219Sensor.getMetrics(m); + if (ina226Sensor.hasSensor()) + valid = ina226Sensor.getMetrics(m); if (ina260Sensor.hasSensor()) valid = ina260Sensor.getMetrics(m); if (ina3221Sensor.hasSensor()) @@ -253,4 +257,4 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) return false; } -#endif +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/CurrentSensor.h b/src/modules/Telemetry/Sensor/CurrentSensor.h new file mode 100644 index 000000000..9827a9aa4 --- /dev/null +++ b/src/modules/Telemetry/Sensor/CurrentSensor.h @@ -0,0 +1,13 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#pragma once + +class CurrentSensor +{ + public: + virtual int16_t getCurrentMa() = 0; +}; + +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/INA219Sensor.cpp b/src/modules/Telemetry/Sensor/INA219Sensor.cpp index de69163b4..ea47e265d 100644 --- a/src/modules/Telemetry/Sensor/INA219Sensor.cpp +++ b/src/modules/Telemetry/Sensor/INA219Sensor.cpp @@ -45,4 +45,9 @@ uint16_t INA219Sensor::getBusVoltageMv() return lround(ina219.getBusVoltage_V() * 1000); } +int16_t INA219Sensor::getCurrentMa() +{ + return lround(ina219.getCurrent_mA()); +} + #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/INA219Sensor.h b/src/modules/Telemetry/Sensor/INA219Sensor.h index 9dded067b..9b6a2fcca 100644 --- a/src/modules/Telemetry/Sensor/INA219Sensor.h +++ b/src/modules/Telemetry/Sensor/INA219Sensor.h @@ -3,11 +3,12 @@ #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR #include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "CurrentSensor.h" #include "TelemetrySensor.h" #include "VoltageSensor.h" #include -class INA219Sensor : public TelemetrySensor, VoltageSensor +class INA219Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor { private: Adafruit_INA219 ina219; @@ -20,6 +21,7 @@ class INA219Sensor : public TelemetrySensor, VoltageSensor virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual uint16_t getBusVoltageMv() override; + virtual int16_t getCurrentMa() override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/INA226Sensor.cpp b/src/modules/Telemetry/Sensor/INA226Sensor.cpp new file mode 100644 index 000000000..1ee7cd92e --- /dev/null +++ b/src/modules/Telemetry/Sensor/INA226Sensor.cpp @@ -0,0 +1,58 @@ +#include "configuration.h" + +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "INA226.h" +#include "INA226Sensor.h" +#include "TelemetrySensor.h" + +INA226Sensor::INA226Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_INA226, "INA226") {} + +int32_t INA226Sensor::runOnce() +{ + LOG_INFO("Init sensor: %s", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + + begin(nodeTelemetrySensorsMap[sensorType].second, nodeTelemetrySensorsMap[sensorType].first); + + if (!status) { + status = ina226.begin(); + } + return initI2CSensor(); +} + +void INA226Sensor::setup() {} + +void INA226Sensor::begin(TwoWire *wire, uint8_t addr) +{ + _wire = wire; + _addr = addr; + ina226 = INA226(_addr, _wire); + _wire->begin(); +} + +bool INA226Sensor::getMetrics(meshtastic_Telemetry *measurement) +{ + measurement->variant.environment_metrics.has_voltage = true; + measurement->variant.environment_metrics.has_current = true; + + // mV conversion to V + measurement->variant.environment_metrics.voltage = ina226.getBusVoltage() / 1000; + measurement->variant.environment_metrics.current = ina226.getCurrent_mA(); + return true; +} + +uint16_t INA226Sensor::getBusVoltageMv() +{ + return lround(ina226.getBusVoltage()); +} + +int16_t INA226Sensor::getCurrentMa() +{ + return lround(ina226.getCurrent_mA()); +} + +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/INA226Sensor.h b/src/modules/Telemetry/Sensor/INA226Sensor.h new file mode 100644 index 000000000..2f71c5b86 --- /dev/null +++ b/src/modules/Telemetry/Sensor/INA226Sensor.h @@ -0,0 +1,30 @@ +#include "configuration.h" + +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "CurrentSensor.h" +#include "TelemetrySensor.h" +#include "VoltageSensor.h" +#include + +class INA226Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor +{ + private: + uint8_t _addr = INA_ADDR; + TwoWire *_wire = &Wire; + INA226 ina226 = INA226(_addr, _wire); + + protected: + virtual void setup() override; + void begin(TwoWire *wire = &Wire, uint8_t addr = INA_ADDR); + + public: + INA226Sensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual uint16_t getBusVoltageMv() override; + virtual int16_t getCurrentMa() override; +}; + +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp index ed09856e2..7ac11dfde 100644 --- a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp +++ b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp @@ -102,4 +102,9 @@ uint16_t INA3221Sensor::getBusVoltageMv() return lround(ina3221.getVoltage(BAT_CH) * 1000); } +int16_t INA3221Sensor::getCurrentMa() +{ + return lround(ina3221.getCurrent(BAT_CH)); +} + #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.h b/src/modules/Telemetry/Sensor/INA3221Sensor.h index d5121aab6..8eeda3e02 100644 --- a/src/modules/Telemetry/Sensor/INA3221Sensor.h +++ b/src/modules/Telemetry/Sensor/INA3221Sensor.h @@ -3,11 +3,12 @@ #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR #include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "CurrentSensor.h" #include "TelemetrySensor.h" #include "VoltageSensor.h" #include -class INA3221Sensor : public TelemetrySensor, VoltageSensor +class INA3221Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor { private: INA3221 ina3221 = INA3221(INA3221_ADDR42_SDA); @@ -35,6 +36,7 @@ class INA3221Sensor : public TelemetrySensor, VoltageSensor int32_t runOnce() override; bool getMetrics(meshtastic_Telemetry *measurement) override; virtual uint16_t getBusVoltageMv() override; + virtual int16_t getCurrentMa() override; }; struct _INA3221Measurement { diff --git a/src/power.h b/src/power.h index 63335104b..ab55fc7e1 100644 --- a/src/power.h +++ b/src/power.h @@ -42,10 +42,12 @@ extern RTC_NOINIT_ATTR uint64_t RTC_reg_b; #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) #include "modules/Telemetry/Sensor/INA219Sensor.h" +#include "modules/Telemetry/Sensor/INA226Sensor.h" #include "modules/Telemetry/Sensor/INA260Sensor.h" #include "modules/Telemetry/Sensor/INA3221Sensor.h" -extern INA260Sensor ina260Sensor; extern INA219Sensor ina219Sensor; +extern INA226Sensor ina226Sensor; +extern INA260Sensor ina260Sensor; extern INA3221Sensor ina3221Sensor; #endif @@ -99,4 +101,4 @@ class Power : private concurrency::OSThread #endif }; -extern Power *power; \ No newline at end of file +extern Power *power;