mirror of
https://github.com/pelgraine/Meck.git
synced 2026-03-28 17:42:44 +01:00
Added telemetry print to repeater admin including battery and temperature status
This commit is contained in:
@@ -51,4 +51,5 @@ public:
|
||||
// Repeater admin callbacks (from MyMesh)
|
||||
virtual void onAdminLoginResult(bool success, uint8_t permissions, uint32_t server_time) {}
|
||||
virtual void onAdminCliResponse(const char* from_name, const char* text) {}
|
||||
virtual void onAdminTelemetryResult(const uint8_t* data, uint8_t len) {}
|
||||
};
|
||||
@@ -706,6 +706,29 @@ bool MyMesh::uiSendCliCommand(uint32_t contact_idx, const char* command) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MyMesh::uiSendTelemetryRequest(uint32_t contact_idx) {
|
||||
ContactInfo contact;
|
||||
if (!getContactByIdx(contact_idx, contact)) return false;
|
||||
|
||||
ContactInfo* recipient = lookupContactByPubKey(contact.id.pub_key, PUB_KEY_SIZE);
|
||||
if (!recipient) return false;
|
||||
|
||||
uint32_t tag, est_timeout;
|
||||
int result = sendRequest(*recipient, REQ_TYPE_GET_TELEMETRY_DATA, tag, est_timeout);
|
||||
if (result == MSG_SEND_FAILED) {
|
||||
MESH_DEBUG_PRINTLN("UI: Telemetry request send failed to %s", recipient->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
clearPendingReqs();
|
||||
pending_telemetry = tag;
|
||||
|
||||
MESH_DEBUG_PRINTLN("UI: Telemetry request sent to %s (%s), timeout=%dms",
|
||||
recipient->name, result == MSG_SEND_SENT_FLOOD ? "flood" : "direct",
|
||||
est_timeout);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data,
|
||||
uint8_t len, uint8_t *reply) {
|
||||
if (data[0] == REQ_TYPE_GET_TELEMETRY_DATA) {
|
||||
@@ -816,6 +839,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
|
||||
_serial->writeFrame(out_frame, i);
|
||||
} else if (len > 4 && tag == pending_telemetry) { // check for matching response tag
|
||||
pending_telemetry = 0;
|
||||
MESH_DEBUG_PRINTLN("Telemetry response received from %s, len=%d", contact.name, len);
|
||||
|
||||
int i = 0;
|
||||
out_frame[i++] = PUSH_CODE_TELEMETRY_RESPONSE;
|
||||
@@ -825,6 +849,11 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
|
||||
memcpy(&out_frame[i], &data[4], len - 4);
|
||||
i += (len - 4);
|
||||
_serial->writeFrame(out_frame, i);
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
// Route telemetry data to UI (LPP buffer after the 4-byte tag)
|
||||
if (_ui) _ui->onAdminTelemetryResult(&data[4], len - 4);
|
||||
#endif
|
||||
} else if (len > 4 && tag == pending_req) { // check for matching response tag
|
||||
pending_req = 0;
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ public:
|
||||
// Repeater admin - UI-initiated operations
|
||||
bool uiLoginToRepeater(uint32_t contact_idx, const char* password);
|
||||
bool uiSendCliCommand(uint32_t contact_idx, const char* command);
|
||||
bool uiSendTelemetryRequest(uint32_t contact_idx);
|
||||
int getAdminContactIdx() const { return _admin_contact_idx; }
|
||||
|
||||
|
||||
|
||||
@@ -208,6 +208,13 @@ private:
|
||||
PwdCacheEntry _pwdCache[PWD_CACHE_SIZE];
|
||||
int _pwdCacheCount;
|
||||
|
||||
// Remote telemetry (from LPP response)
|
||||
float _telemVoltage; // Battery voltage in volts
|
||||
float _telemTempC; // Temperature in celsius
|
||||
bool _telemHasVoltage;
|
||||
bool _telemHasTemp;
|
||||
bool _telemRequested; // Telemetry request already sent
|
||||
|
||||
const char* getCachedPassword(int contactIdx) {
|
||||
for (int i = 0; i < _pwdCacheCount; i++) {
|
||||
if (_pwdCache[i].contactIdx == contactIdx) return _pwdCache[i].password;
|
||||
@@ -236,6 +243,49 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// --- LPP telemetry parser ---
|
||||
// Walks a CayenneLPP buffer extracting voltage and temperature.
|
||||
// Format per entry: [channel 1B][type 1B][data varies]
|
||||
void parseLPPTelemetry(const uint8_t* data, uint8_t len) {
|
||||
const uint8_t TELEM_TYPE_VOLTAGE = 0x74; // 2 bytes, uint16 / 100 = V
|
||||
const uint8_t TELEM_TYPE_TEMPERATURE = 0x67; // 2 bytes, int16 / 10 = C
|
||||
|
||||
int pos = 0;
|
||||
while (pos + 2 < len) {
|
||||
// uint8_t channel = data[pos]; // not needed
|
||||
uint8_t type = data[pos + 1];
|
||||
pos += 2; // skip channel + type
|
||||
|
||||
// Determine data size for this type
|
||||
int dataSize = 0;
|
||||
switch (type) {
|
||||
case TELEM_TYPE_VOLTAGE: dataSize = 2; break;
|
||||
case TELEM_TYPE_TEMPERATURE: dataSize = 2; break;
|
||||
case 0x88: dataSize = 9; break; // GPS
|
||||
case 0x75: dataSize = 2; break; // Current
|
||||
case 0x68: dataSize = 1; break; // Humidity
|
||||
case 0x73: dataSize = 2; break; // Pressure
|
||||
case 0x76: dataSize = 2; break; // Altitude
|
||||
case 0x80: dataSize = 2; break; // Power
|
||||
default: return; // Unknown type, stop parsing
|
||||
}
|
||||
|
||||
if (pos + dataSize > len) break;
|
||||
|
||||
if (type == TELEM_TYPE_VOLTAGE) {
|
||||
uint16_t raw = ((uint16_t)data[pos] << 8) | data[pos + 1];
|
||||
_telemVoltage = raw / 100.0f;
|
||||
_telemHasVoltage = true;
|
||||
} else if (type == TELEM_TYPE_TEMPERATURE) {
|
||||
int16_t raw = ((int16_t)data[pos] << 8) | data[pos + 1];
|
||||
_telemTempC = raw / 10.0f;
|
||||
_telemHasTemp = true;
|
||||
}
|
||||
|
||||
pos += dataSize;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
static void formatTime(char* buf, size_t bufLen, uint32_t epoch) {
|
||||
@@ -378,7 +428,9 @@ public:
|
||||
_catSel(0), _cmdSel(0), _scrollOffset(0),
|
||||
_paramLen(0), _pendingCmd(nullptr),
|
||||
_responseLen(0), _responseScroll(0), _responseTotalLines(0),
|
||||
_cmdSentAt(0), _waitingForLogin(false), _pwdCacheCount(0) {
|
||||
_cmdSentAt(0), _waitingForLogin(false), _pwdCacheCount(0),
|
||||
_telemVoltage(0), _telemTempC(0),
|
||||
_telemHasVoltage(false), _telemHasTemp(false), _telemRequested(false) {
|
||||
_password[0] = '\0';
|
||||
_repeaterName[0] = '\0';
|
||||
_response[0] = '\0';
|
||||
@@ -405,6 +457,9 @@ public:
|
||||
_pendingCmd = nullptr;
|
||||
_paramLen = 0;
|
||||
_paramBuf[0] = '\0';
|
||||
_telemHasVoltage = false;
|
||||
_telemHasTemp = false;
|
||||
_telemRequested = false;
|
||||
|
||||
const char* cached = getCachedPassword(contactIdx);
|
||||
if (cached) {
|
||||
@@ -427,6 +482,14 @@ public:
|
||||
_serverTime = server_time;
|
||||
_state = STATE_CATEGORY_MENU;
|
||||
cachePassword(_contactIdx, _password);
|
||||
|
||||
// Auto-request telemetry (battery & temperature) after login
|
||||
if (!_telemRequested) {
|
||||
_telemRequested = true;
|
||||
bool sent = the_mesh.uiSendTelemetryRequest(_contactIdx);
|
||||
Serial.printf("[Admin] Telemetry request %s for contact idx %d\n",
|
||||
sent ? "sent" : "FAILED", _contactIdx);
|
||||
}
|
||||
} else {
|
||||
snprintf(_response, sizeof(_response), "Login failed.\nCheck password.");
|
||||
_responseLen = strlen(_response);
|
||||
@@ -456,6 +519,15 @@ public:
|
||||
_state = STATE_RESPONSE_VIEW;
|
||||
}
|
||||
|
||||
void onTelemetryResult(const uint8_t* data, uint8_t len) {
|
||||
Serial.printf("[Admin] Telemetry response received, %d bytes:", len);
|
||||
for (int i = 0; i < len && i < 32; i++) Serial.printf(" %02X", data[i]);
|
||||
Serial.println();
|
||||
parseLPPTelemetry(data, len);
|
||||
Serial.printf("[Admin] Parsed: hasVoltage=%d (%.2fV) hasTemp=%d (%.1fC)\n",
|
||||
_telemHasVoltage, _telemVoltage, _telemHasTemp, _telemTempC);
|
||||
}
|
||||
|
||||
void poll() override {
|
||||
if ((_state == STATE_LOGGING_IN || _state == STATE_COMMAND_PENDING) &&
|
||||
_cmdSentAt > 0 && (millis() - _cmdSentAt) > ADMIN_TIMEOUT_MS) {
|
||||
@@ -469,6 +541,7 @@ public:
|
||||
_responseLen = strlen(_response);
|
||||
_state = STATE_ERROR;
|
||||
}
|
||||
_task->forceRefresh(); // Immediate redraw on state change
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,7 +631,7 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
if (_state == STATE_LOGGING_IN || _state == STATE_COMMAND_PENDING) return 1000;
|
||||
if (_state == STATE_LOGGING_IN || _state == STATE_COMMAND_PENDING) return 30000; // static text; poll()/callbacks force refresh on state change
|
||||
if (_state == STATE_PASSWORD_ENTRY && _lastCharAt > 0 && (millis() - _lastCharAt) < 800) {
|
||||
return _lastCharAt + 800 - millis() + 50;
|
||||
}
|
||||
@@ -682,6 +755,28 @@ private:
|
||||
y += lineHeight + 2;
|
||||
}
|
||||
|
||||
// Remote telemetry info line (battery & temperature)
|
||||
if (_telemHasVoltage || _telemHasTemp) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.setCursor(0, y);
|
||||
char telem[48];
|
||||
int tpos = 0;
|
||||
if (_telemHasVoltage) {
|
||||
tpos += snprintf(telem + tpos, sizeof(telem) - tpos, "Batt:%.2fV", _telemVoltage);
|
||||
}
|
||||
if (_telemHasTemp) {
|
||||
if (tpos > 0) tpos += snprintf(telem + tpos, sizeof(telem) - tpos, " ");
|
||||
tpos += snprintf(telem + tpos, sizeof(telem) - tpos, "Temp:%.1fC", _telemTempC);
|
||||
}
|
||||
display.print(telem);
|
||||
y += lineHeight + 2;
|
||||
} else if (_telemRequested) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.setCursor(0, y);
|
||||
display.print("Telemetry: requesting...");
|
||||
y += lineHeight + 2;
|
||||
}
|
||||
|
||||
// Render categories
|
||||
for (int i = 0; i < CAT_COUNT && y + lineHeight <= display.height() - 16; i++) {
|
||||
bool isSystem = (i == CAT_REBOOT_OTA);
|
||||
|
||||
@@ -1550,6 +1550,14 @@ void UITask::onAdminCliResponse(const char* from_name, const char* text) {
|
||||
}
|
||||
}
|
||||
|
||||
void UITask::onAdminTelemetryResult(const uint8_t* data, uint8_t len) {
|
||||
Serial.printf("[UITask] onAdminTelemetryResult: %d bytes, onAdmin=%d\n", len, isOnRepeaterAdmin());
|
||||
if (repeater_admin && isOnRepeaterAdmin()) {
|
||||
((RepeaterAdminScreen*)repeater_admin)->onTelemetryResult(data, len);
|
||||
_next_refresh = 100; // trigger re-render
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MECK_AUDIO_VARIANT
|
||||
bool UITask::isAudioPlayingInBackground() const {
|
||||
if (!audiobook_screen) return false;
|
||||
|
||||
@@ -158,6 +158,7 @@ public:
|
||||
// Repeater admin callbacks
|
||||
void onAdminLoginResult(bool success, uint8_t permissions, uint32_t server_time) override;
|
||||
void onAdminCliResponse(const char* from_name, const char* text) override;
|
||||
void onAdminTelemetryResult(const uint8_t* data, uint8_t len) override;
|
||||
|
||||
// Get current screen for checking state
|
||||
UIScreen* getCurrentScreen() const { return curr; }
|
||||
|
||||
Reference in New Issue
Block a user