From c9023ae20527fcbe123f32b1567e2035338ef0bb Mon Sep 17 00:00:00 2001 From: Nick Dunklee Date: Tue, 23 Jun 2026 14:36:43 -0600 Subject: [PATCH] feat: cli to query sensor running on the node Adds one command: sensors - Can access via CLI on repeater, sensor, room server - lists sensor, channel, and i2c address - format example: ch2: BME680+BSEC @ 0x76 - Channel numbering mirrors the order querySensors() assigns at runtime. This is one part of a 2-part addition per some feedback on the initial sensor change deployment, so people can query/scan their installed sensors. Accessible via USB CLI and LoRa MeshCore app CLI. Tested on: - RAK19003 repeater via LoRa in official MC app - RAK19007 repeater via LoRa in official MC app - RAK19001 sensor via USB - RAK3401 1W repeater via LoRa in official MC app --- src/helpers/CommonCLI.cpp | 2 ++ src/helpers/SensorManager.h | 1 + .../sensors/EnvironmentSensorManager.cpp | 28 ++++++++++++++++++- .../sensors/EnvironmentSensorManager.h | 5 +++- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index dce1c5d894..02b1c52a26 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -312,6 +312,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re sprintf(reply, "%s (Build: %s)", _callbacks->getFirmwareVer(), _callbacks->getBuildDate()); } else if (memcmp(command, "board", 5) == 0) { sprintf(reply, "%s", _board->getManufacturerName()); + } else if (memcmp(command, "sensors", 7) == 0) { + _sensors->formatActiveSensorsReply(reply); } else if (memcmp(command, "sensor get ", 11) == 0) { const char* key = command + 11; const char* val = _sensors->getSettingByKey(key); diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 89a174c228..e572d47062 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -23,6 +23,7 @@ class SensorManager { 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 void formatActiveSensorsReply(char* reply) { strcpy(reply, "no sensors"); } // 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 73842d9eeb..99717316d9 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -651,7 +651,7 @@ bool EnvironmentSensorManager::begin() { } MESH_DEBUG_PRINTLN("Found %s at address: %02X", def.name, def.address); for (uint8_t sub = 0; sub < n && _active_sensor_count < MAX_ACTIVE_SENSORS; sub++) { - _active_sensors[_active_sensor_count++] = { def.query, sub }; + _active_sensors[_active_sensor_count++] = { def.query, sub, def.name, def.address }; } } @@ -681,6 +681,32 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen return true; } +// ============================================================ +// formatActiveSensorsReply() — lists each detected sensor with +// its assigned telemetry channel and I2C address, for the +// "sensors" CLI command. Channel numbering mirrors the order +// querySensors() assigns at runtime. +// ============================================================ + +void EnvironmentSensorManager::formatActiveSensorsReply(char* reply) { + if (_active_sensor_count == 0) { + strcpy(reply, "no sensors detected"); + return; + } + + char* dp = reply; + int i; + for (i = 0; i < _active_sensor_count && (dp - reply < 140); i++) { + uint8_t channel = TELEM_CHANNEL_SELF + 1 + i; + sprintf(dp, "ch%d: %s @ 0x%02X\n", channel, _active_sensors[i].name, _active_sensors[i].address); + dp = strchr(dp, 0); + } + if (i < _active_sensor_count) { + sprintf(dp, "... +%d more", _active_sensor_count - i); + } else { + *(dp - 1) = 0; // remove trailing newline + } +} int EnvironmentSensorManager::getNumSettings() const { int settings = 0; diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 29147c8967..1b3d73f556 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -12,7 +12,9 @@ class EnvironmentSensorManager : public SensorManager { // Sub-channel is 0 for all single-output sensors. struct ActiveSensor { void (*query)(uint8_t channel, uint8_t sub_channel, CayenneLPP& telemetry); - uint8_t sub_channel; + uint8_t sub_channel; + const char* name; // points into SENSOR_TABLE's static string, not owned + uint8_t address; }; ActiveSensor _active_sensors[MAX_ACTIVE_SENSORS]; @@ -50,4 +52,5 @@ class EnvironmentSensorManager : public SensorManager { const char* getSettingName(int i) const override; const char* getSettingValue(int i) const override; bool setSettingValue(const char* name, const char* value) override; + void formatActiveSensorsReply(char* reply) override; };