From b0354871019fd7bf7da1fe85b3d7ffd65729a147 Mon Sep 17 00:00:00 2001 From: Normunds Gavars Date: Tue, 13 May 2025 23:52:49 +0300 Subject: [PATCH] 283 Add support of INA3221 to Promicro telemetry --- .gitignore | 2 + src/helpers/SensorManager.h | 6 ++ src/helpers/sensors/SensorSettingsManager.h | 72 +++++++++++++ variants/promicro/platformio.ini | 3 +- variants/promicro/target.cpp | 106 +++++++++++++++++++- variants/promicro/target.h | 28 +++++- 6 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 src/helpers/sensors/SensorSettingsManager.h diff --git a/.gitignore b/.gitignore index 7e7cc694..51449c2d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ .vscode/ipch out/ .direnv/ +.DS_Store +.vscode/settings.json diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index a5d53939..ae783c58 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -7,6 +7,12 @@ #define TELEM_CHANNEL_SELF 1 // LPP data channel for 'self' device +#define TELEM_INA3221_ADDRESS 0x40 // INA3221 3 channel current, voltage, power sensor I2C address +#define TELEM_INA3221_SHUNT_VALUE 0.100 // most variants will have a 0.1 ohm shunts +#define TELEM_INA3221_SETTING_CH1 "INA3221 Channel 1" +#define TELEM_INA3221_SETTING_CH2 "INA3221 Channel 2" +#define TELEM_INA3221_SETTING_CH3 "INA3221 Channel 3" + class SensorManager { public: double node_lat, node_lon; // modify these, if you want to affect Advert location diff --git a/src/helpers/sensors/SensorSettingsManager.h b/src/helpers/sensors/SensorSettingsManager.h new file mode 100644 index 00000000..e3561332 --- /dev/null +++ b/src/helpers/sensors/SensorSettingsManager.h @@ -0,0 +1,72 @@ +#include +#include + +class SensorSettingsManager { + private: + std::vector> settings; + + public: + + int getSettingCount() const { + return static_cast(settings.size()); + }; + + bool addSetting(const std::string& name, bool defaultValue = false){ + for (const auto& setting : settings) { + if (setting.first == name) { + return false; + } + } + settings.emplace_back(name, defaultValue); + return true; + }; + + bool removeSetting(const std::string& name) { + for (auto it = settings.begin(); it != settings.end(); ++it) { + if (it->first == name) { + settings.erase(it); + return true; + } + } + return false; + }; + + const char* getSettingValue(const std::string& name) const{ + for (const auto& setting : settings) { + if (setting.first == name) { + return setting.second ? "true" : "false"; + } + } + return NULL; + }; + + const char* getSettingValue(int index) const { + if (index >= 0 && index < getSettingCount()) { + return settings[index].second ? "true" : "false"; + } + return NULL; + }; + + bool setSettingValue(const std::string& name, const std::string& value) { + for (auto& setting : settings) { + if (setting.first == name) { + // Convert value to boolean + if (value == "1" || value == "true") { + setting.second = true; + } else { + setting.second = false; + } + return true; + } + } + return false; + } + + const char* getSettingName(int index) const { + if (index >= 0 && index < getSettingCount()){ + return settings[index].first.c_str(); + } + return NULL; + }; + +}; \ No newline at end of file diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index b2b7465c..7b370412 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -36,7 +36,8 @@ build_flags = ; -D MESH_DEBUG=1 lib_deps = ${Faketec.lib_deps} adafruit/RTClib @ ^2.1.3 - + robtillaart/INA3221 @ ^0.4.1 + [env:Faketec_room_server] extends = Faketec build_src_filter = ${Faketec.build_src_filter} diff --git a/variants/promicro/target.cpp b/variants/promicro/target.cpp index 212e3b25..a8a27b4e 100644 --- a/variants/promicro/target.cpp +++ b/variants/promicro/target.cpp @@ -10,7 +10,7 @@ WRAPPER_CLASS radio_driver(radio, board); VolatileRTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -SensorManager sensors; +PromicroSensorManager sensors; #ifndef LORA_CR #define LORA_CR 5 @@ -74,3 +74,107 @@ mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity } + +PromicroSensorManager::PromicroSensorManager(){ + INA_3221 = new INA3221(TELEM_INA3221_ADDRESS, &Wire); +} + +PromicroSensorManager::~PromicroSensorManager(){ + if (INA_3221) { + delete INA_3221; + INA_3221 = nullptr; + } +} + +bool PromicroSensorManager::begin() { + if (INA_3221->begin() ) { + Serial.print("Found INA3221 at address "); + Serial.print(INA_3221->getAddress()); + Serial.println(); + Serial.print(INA_3221->getDieID(), HEX); + Serial.print(INA_3221->getManufacturerID(), HEX); + Serial.print(INA_3221->getConfiguration(), HEX); + Serial.println(); + + for(int i = 0; i < 3; i++) { + INA_3221->setShuntR(i, TELEM_INA3221_SHUNT_VALUE); + } + // add INA3221 settings to settings manager + settingsManager.addSetting(TELEM_INA3221_SETTING_CH1, true); + settingsManager.addSetting(TELEM_INA3221_SETTING_CH2, true); + settingsManager.addSetting(TELEM_INA3221_SETTING_CH3, true); + INA3221initialized = true; + } + else { + INA3221initialized = false; + Serial.print("INA3221 was not found at I2C address "); + Serial.print(TELEM_INA3221_ADDRESS, HEX); + Serial.println(); + } + return true; +} + +bool PromicroSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { + // TODO: what is the correct permission here? + if (requester_permissions && TELEM_PERM_BASE) { + if (INA3221initialized) { + for(int i = 0; i < 3; i++) { + // add only enabled INA3221 channels to telemetry + if (settingsManager.getSettingValue(INA3221_CHANNEL_NAMES[i]) == "true") { + + // TODO: remove when telemetry support gets properly added + // Serial.print("CH"); + // Serial.print(i); + // Serial.print(" Voltage: "); + // Serial.print(INA_3221->getBusVoltage(i)); + // Serial.print("V Current: "); + // Serial.print(INA_3221->getCurrent(i)); + // Serial.print("A Power: "); + // Serial.print(INA_3221->getPower(i)); + // Serial.println(); + + telemetry.addVoltage(INA3221_CHANNELS[i], INA_3221->getBusVoltage(i)); + telemetry.addCurrent(INA3221_CHANNELS[i], INA_3221->getCurrent(i)); + telemetry.addPower(INA3221_CHANNELS[i], INA_3221->getPower(i)); + } + } + } + } + + return true; +} + +int PromicroSensorManager::getNumSettings() const { + return settingsManager.getSettingCount(); +} + +const char* PromicroSensorManager::getSettingName(int i) const { + return settingsManager.getSettingName(i); +} + +const char* PromicroSensorManager::getSettingValue(int i) const { + return settingsManager.getSettingValue(i); +} + +bool PromicroSensorManager::setSettingValue(const char* name, const char* value) { + if (settingsManager.setSettingValue(name, value)) { + onSettingsChanged(); + return true; + } + return false; +} + +void PromicroSensorManager::onSettingsChanged() { + if (INA3221initialized) { + for(int i = 0; i < 3; i++) { + int channelEnabled = INA_3221->getEnableChannel(i); + bool settingEnabled = settingsManager.getSettingValue(INA3221_CHANNEL_NAMES[i]) == "true"; + if (!settingEnabled && channelEnabled) { + INA_3221->disableChannel(i); + } + if (settingEnabled && !channelEnabled) { + INA_3221->enableChannel(i); + } + } + } +} \ No newline at end of file diff --git a/variants/promicro/target.h b/variants/promicro/target.h index b2c4f9d2..4cfe4b75 100644 --- a/variants/promicro/target.h +++ b/variants/promicro/target.h @@ -8,14 +8,40 @@ #include #include #include +#include +#include extern PromicroBoard board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern SensorManager sensors; + bool radio_init(); uint32_t radio_get_rng_seed(); void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); void radio_set_tx_power(uint8_t dbm); mesh::LocalIdentity radio_new_identity(); + +class PromicroSensorManager: public SensorManager { + INA3221 * INA_3221; + bool INA3221initialized = false; + SensorSettingsManager settingsManager; + + // INA3221 channels in telemetry + int INA3221_CHANNELS[3] = {TELEM_CHANNEL_SELF + 1, TELEM_CHANNEL_SELF + 2, TELEM_CHANNEL_SELF+ 3}; + char * INA3221_CHANNEL_NAMES[3] = { TELEM_INA3221_SETTING_CH1, TELEM_INA3221_SETTING_CH2, TELEM_INA3221_SETTING_CH3}; + void onSettingsChanged(); + +public: + PromicroSensorManager(); + ~PromicroSensorManager(); + bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; + int getNumSettings() const override; + const char* getSettingName(int i) const override; + const char* getSettingValue(int i) const override; + bool setSettingValue(const char* name, const char* value) override; +}; + + +extern PromicroSensorManager sensors; \ No newline at end of file