diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 9accb2998..174246f47 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -144,6 +144,17 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +### External Power Monitoring Stats - Per-channel voltage and current +**Usage:** `stats-extpower` + +**Serial Only:** Yes + +**Note:** Returns JSON with only enabled channels, e.g. `{"ch1_voltage_mv":12450,"ch1_current_ma":150,"ch2_voltage_mv":3800,"ch2_current_ma":85}` + +**Note:** Returns "No external power monitoring board detected" if no supported hardware is present + +--- + ## Logging ### Begin capture of rx log to node storage diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 096907494..e9337ea67 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1148,10 +1148,16 @@ void MyMesh::formatRadioStatsReply(char *reply) { } void MyMesh::formatPacketStatsReply(char *reply) { - StatsFormatHelper::formatPacketStats(reply, radio_driver, getNumSentFlood(), getNumSentDirect(), + StatsFormatHelper::formatPacketStats(reply, radio_driver, getNumSentFlood(), getNumSentDirect(), getNumRecvFlood(), getNumRecvDirect()); } +void MyMesh::formatExtPowerStatsReply(char *reply) { + if (!sensors.formatExtPowerStats(reply)) { + strcpy(reply, "{\"err\":\"No external power monitoring board detected\"}"); + } +} + void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) IdentityStore store(*_fs, ""); diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 7597c6c6f..c9d758591 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -214,6 +214,7 @@ public: void formatStatsReply(char *reply) override; void formatRadioStatsReply(char *reply) override; void formatPacketStatsReply(char *reply) override; + void formatExtPowerStatsReply(char *reply) override; void startRegionsLoad() override; bool saveRegions() override; void onDefaultRegionChanged(const RegionEntry* r) override; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index b78ad6ebd..ffe687657 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -473,6 +473,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re _callbacks->formatRadioStatsReply(reply); } else if (sender_timestamp == 0 && memcmp(command, "stats-core", 10) == 0 && (command[10] == 0 || command[10] == ' ')) { _callbacks->formatStatsReply(reply); + } else if (sender_timestamp == 0 && memcmp(command, "stats-extpower", 14) == 0 && (command[14] == 0 || command[14] == ' ')) { + _callbacks->formatExtPowerStatsReply(reply); } else { strcpy(reply, "Unknown command"); } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index b509c2b31..d5413d945 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -86,6 +86,9 @@ public: virtual void formatStatsReply(char *reply) = 0; virtual void formatRadioStatsReply(char *reply) = 0; virtual void formatPacketStatsReply(char *reply) = 0; + virtual void formatExtPowerStatsReply(char *reply) { + strcpy(reply, "{\"err\":\"No external power monitoring board detected\"}"); + }; virtual mesh::LocalIdentity& getSelfId() = 0; virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0; virtual void clearStats() = 0; diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 89a174c22..062be546f 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -23,6 +23,7 @@ public: virtual const char* getSettingValue(int i) const { return NULL; } virtual bool setSettingValue(const char* name, const char* value) { return false; } virtual LocationProvider* getLocationProvider() { return NULL; } + virtual bool formatExtPowerStats(char* reply) { return false; } // Helper functions to manage setting by keys (useful in many places ...) const char* getSettingByKey(const char* key) { diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 73842d9ee..60c0b1d0f 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -682,6 +682,35 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen } +bool EnvironmentSensorManager::formatExtPowerStats(char* reply) { +#if ENV_INCLUDE_INA3221 + if (!INA3221_initialized) return false; + char* dp = reply; + int remaining = 160; // max reply buffer length + *dp++ = '{'; + remaining--; + bool first = true; + for (int i = 0; i < TELEM_INA3221_NUM_CHANNELS; i++) { + if (INA3221.isChannelEnabled(i)) { + int ch = i + 1; + int voltage_mv = (int)(INA3221.getBusVoltage(i) * 1000); + int current_ma = (int)(INA3221.getCurrentAmps(i) * 1000); + if (!first) { *dp++ = ','; remaining--; } + int n = snprintf(dp, remaining, "\"ch%d_voltage_mv\":%d,\"ch%d_current_ma\":%d", ch, voltage_mv, ch, current_ma); + if (n >= remaining) break; + dp += n; + remaining -= n; + first = false; + } + } + *dp++ = '}'; + *dp = 0; + return true; +#else + return false; +#endif +} + int EnvironmentSensorManager::getNumSettings() const { int settings = 0; #if ENV_INCLUDE_GPS diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 29147c896..bfa728401 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -50,4 +50,5 @@ public: const char* getSettingName(int i) const override; const char* getSettingValue(int i) const override; bool setSettingValue(const char* name, const char* value) override; + bool formatExtPowerStats(char* reply) override; };