From b0354871019fd7bf7da1fe85b3d7ffd65729a147 Mon Sep 17 00:00:00 2001 From: Normunds Gavars Date: Tue, 13 May 2025 23:52:49 +0300 Subject: [PATCH 1/4] 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 From c69657a13b7e0b1e993c44d2d812c9afb3e619b0 Mon Sep 17 00:00:00 2001 From: Normunds Gavars Date: Wed, 14 May 2025 13:27:57 +0300 Subject: [PATCH 2/4] 283 remove settingsManager and avoid the String class --- src/helpers/SensorManager.h | 2 +- src/helpers/sensors/SensorSettingsManager.h | 72 ----------------- variants/promicro/target.cpp | 86 +++++++++------------ variants/promicro/target.h | 17 ++-- 4 files changed, 47 insertions(+), 130 deletions(-) delete mode 100644 src/helpers/sensors/SensorSettingsManager.h diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index ae783c58..36181923 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -7,7 +7,7 @@ #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_ADDRESS 0x42 // 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" diff --git a/src/helpers/sensors/SensorSettingsManager.h b/src/helpers/sensors/SensorSettingsManager.h deleted file mode 100644 index e3561332..00000000 --- a/src/helpers/sensors/SensorSettingsManager.h +++ /dev/null @@ -1,72 +0,0 @@ -#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/target.cpp b/variants/promicro/target.cpp index a8a27b4e..835a9be0 100644 --- a/variants/promicro/target.cpp +++ b/variants/promicro/target.cpp @@ -75,34 +75,21 @@ mesh::LocalIdentity radio_new_identity() { 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; - } -} +INA3221 INA_3221(TELEM_INA3221_ADDRESS, &Wire); bool PromicroSensorManager::begin() { - if (INA_3221->begin() ) { + if (INA_3221.begin() ) { Serial.print("Found INA3221 at address "); - Serial.print(INA_3221->getAddress()); + 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.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); + 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 { @@ -120,22 +107,21 @@ bool PromicroSensorManager::querySensors(uint8_t requester_permissions, CayenneL if (INA3221initialized) { for(int i = 0; i < 3; i++) { // add only enabled INA3221 channels to telemetry - if (settingsManager.getSettingValue(INA3221_CHANNEL_NAMES[i]) == "true") { - + if (INA3221_CHANNEL_ENABLED[i]) { // 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(INA_3221.getBusVoltage(i)); // Serial.print("V Current: "); - // Serial.print(INA_3221->getCurrent(i)); + // Serial.print(INA_3221.getCurrent(i)); // Serial.print("A Power: "); - // Serial.print(INA_3221->getPower(i)); + // 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)); + 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)); } } } @@ -145,36 +131,40 @@ bool PromicroSensorManager::querySensors(uint8_t requester_permissions, CayenneL } int PromicroSensorManager::getNumSettings() const { - return settingsManager.getSettingCount(); + return NUM_SENSOR_SETTINGS; } const char* PromicroSensorManager::getSettingName(int i) const { - return settingsManager.getSettingName(i); + if (i >= 0 && i < NUM_SENSOR_SETTINGS) { + return INA3221_CHANNEL_NAMES[i]; + } + return NULL; } const char* PromicroSensorManager::getSettingValue(int i) const { - return settingsManager.getSettingValue(i); + if (i >= 0 && i < NUM_SENSOR_SETTINGS) { + return INA3221_CHANNEL_ENABLED[i] ? "1" : "0"; + } + return NULL; } 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); + for (int i = 0; i < NUM_SENSOR_SETTINGS; i++) { + if (strcmp(name, INA3221_CHANNEL_NAMES[i]) == 0) { + int channelEnabled = INA_3221.getEnableChannel(i); + if (strcmp(value, "1") == 0) { + INA3221_CHANNEL_ENABLED[i] = true; + if (!channelEnabled) { + INA_3221.enableChannel(i); + } + } else { + INA3221_CHANNEL_ENABLED[i] = false; + if (channelEnabled) { + INA_3221.disableChannel(i); + } } + return true; } } + return false; } \ No newline at end of file diff --git a/variants/promicro/target.h b/variants/promicro/target.h index 4cfe4b75..225afd4d 100644 --- a/variants/promicro/target.h +++ b/variants/promicro/target.h @@ -8,9 +8,10 @@ #include #include #include -#include #include +#define NUM_SENSOR_SETTINGS 3 + extern PromicroBoard board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; @@ -22,19 +23,17 @@ 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(); - + int INA3221_CHANNELS[NUM_SENSOR_SETTINGS] = {TELEM_CHANNEL_SELF + 1, TELEM_CHANNEL_SELF + 2, TELEM_CHANNEL_SELF+ 3}; + char * INA3221_CHANNEL_NAMES[NUM_SENSOR_SETTINGS] = { TELEM_INA3221_SETTING_CH1, TELEM_INA3221_SETTING_CH2, TELEM_INA3221_SETTING_CH3}; + bool INA3221_CHANNEL_ENABLED[NUM_SENSOR_SETTINGS] = {true, true, true}; + public: - PromicroSensorManager(); - ~PromicroSensorManager(); + PromicroSensorManager(){}; bool begin() override; bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; int getNumSettings() const override; From 8b3d60abe763d4549906393e8feac1295890fb94 Mon Sep 17 00:00:00 2001 From: Normunds Gavars Date: Wed, 14 May 2025 13:55:45 +0300 Subject: [PATCH 3/4] 283 add new permision for access to environment sensors --- src/helpers/SensorManager.h | 5 +++-- variants/promicro/target.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 36181923..8d3d4aef 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -2,8 +2,9 @@ #include -#define TELEM_PERM_BASE 0x01 // 'base' permission includes battery -#define TELEM_PERM_LOCATION 0x02 +#define TELEM_PERM_BASE 0x01 // 'base' permission includes battery +#define TELEM_PERM_LOCATION 0x02 +#define TELEM_PERM_ENVIRONMENT 0x04 // permission to access environment sensors #define TELEM_CHANNEL_SELF 1 // LPP data channel for 'self' device diff --git a/variants/promicro/target.cpp b/variants/promicro/target.cpp index 835a9be0..49566342 100644 --- a/variants/promicro/target.cpp +++ b/variants/promicro/target.cpp @@ -103,7 +103,7 @@ bool PromicroSensorManager::begin() { bool PromicroSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { // TODO: what is the correct permission here? - if (requester_permissions && TELEM_PERM_BASE) { + if (requester_permissions && TELEM_PERM_ENVIRONMENT) { if (INA3221initialized) { for(int i = 0; i < 3; i++) { // add only enabled INA3221 channels to telemetry From 74c1ff3d6dae7f22fc9f12325cc48c2342a61452 Mon Sep 17 00:00:00 2001 From: Normunds Gavars Date: Wed, 14 May 2025 13:58:52 +0300 Subject: [PATCH 4/4] 283 minor cleanup --- variants/promicro/target.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/variants/promicro/target.cpp b/variants/promicro/target.cpp index 49566342..304bb54a 100644 --- a/variants/promicro/target.cpp +++ b/variants/promicro/target.cpp @@ -102,23 +102,11 @@ bool PromicroSensorManager::begin() { } bool PromicroSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { - // TODO: what is the correct permission here? if (requester_permissions && TELEM_PERM_ENVIRONMENT) { if (INA3221initialized) { for(int i = 0; i < 3; i++) { // add only enabled INA3221 channels to telemetry if (INA3221_CHANNEL_ENABLED[i]) { - // 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));