Add a saved override to keep USB packet logging continuous

This commit is contained in:
mikecarper committed 2026-10-27 18:47:05 -07:00
1 parent 1f2fcfc449
commit 05679aa741
18 files changed
+382 -39

No files matched your search

@@ -150,6 +150,9 @@ MESH_COMPANION_HELP_BEGIN
#if MESH_USB_LOGGING_AVAILABLE
" get/set usb.logging <on|off|stream> [reboot]\r\n"
" get/set usb.debug <on|off>\r\n"
#if !defined(MESH_DUAL_CDC_LOGGING)
" get/set usb.logging.always <on|off>\r\n"
#endif
#if MESH_USB_CONSOLE_COOPERATIVE
" get usb.watchdog.last\r\n"
" get/set usb.watchdog <off|on|auto> (saved; default auto)\r\n"
+11 -3
View File
@@ -1314,6 +1314,8 @@ bool DataStore::loadPrefsInt(const char *filename,
if (success && file.read(static_cast<uint8_t*>(destination), field.length)
!= field.length) success = false;
}
loaded_prefs.usb_logging_always = success
&& pad[0] == CompanionNodePrefs::USB_LOGGING_ALWAYS_WIRE_ON;
#else
auto readField = [&file, &success](void* dest, size_t size) -> bool {
if (!success
@@ -1332,6 +1334,8 @@ bool DataStore::loadPrefsInt(const char *filename,
readField(&loaded_prefs.airtime_factor, sizeof(float)); // 0
readField(loaded_prefs.node_name, sizeof(loaded_prefs.node_name)); // 4
readField(pad, 4); // 36
loaded_prefs.usb_logging_always = success
&& pad[0] == CompanionNodePrefs::USB_LOGGING_ALWAYS_WIRE_ON;
readField(&loaded_lat, sizeof(loaded_lat)); // 40
readField(&loaded_lon, sizeof(loaded_lon)); // 48
readField(&loaded_prefs.freq, sizeof(loaded_prefs.freq)); // 56
@@ -1542,7 +1546,8 @@ bool DataStore::savePrefs(const CompanionNodePrefs& _prefs, double node_lat, dou
#endif
#if defined(STM32_PLATFORM) && defined(__GNUC__)
using namespace mesh::companion_prefs;
const uint8_t pad[4] = {};
const uint8_t pad[4] = {uint8_t(_prefs.usb_logging_always == 1
? CompanionNodePrefs::USB_LOGGING_ALWAYS_WIRE_ON : 0), 0, 0, 0};
size_t used = 0;
for (const PrefField& field : PREF_FIELDS) {
if (field.length > PREFS_PREFIX_SIZE - used) {
@@ -1562,7 +1567,8 @@ bool DataStore::savePrefs(const CompanionNodePrefs& _prefs, double node_lat, dou
// nRF52 retains streaming writes and its existing small stack footprint.
// The table is the same validated wire order used by the scalar emitter.
using namespace mesh::companion_prefs;
const uint8_t pad[4] = {};
const uint8_t pad[4] = {uint8_t(_prefs.usb_logging_always == 1
? CompanionNodePrefs::USB_LOGGING_ALWAYS_WIRE_ON : 0), 0, 0, 0};
bool success = true;
for (const PrefField& field : PREF_FIELDS) {
if (!success) break;
@@ -1595,6 +1601,8 @@ bool DataStore::savePrefs(const CompanionNodePrefs& _prefs, double node_lat, dou
#endif
uint8_t pad[8];
memset(pad, 0, sizeof(pad));
pad[0] = _prefs.usb_logging_always == 1
? CompanionNodePrefs::USB_LOGGING_ALWAYS_WIRE_ON : 0;
bool success = writeField((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0
success = success && writeField((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4
@@ -1615,7 +1623,7 @@ bool DataStore::savePrefs(const CompanionNodePrefs& _prefs, double node_lat, dou
success = success && writeField((uint8_t *)&_prefs.advert_loc_policy, sizeof(_prefs.advert_loc_policy)); // 76
success = success && writeField((uint8_t *)&_prefs.multi_acks, sizeof(_prefs.multi_acks)); // 77
success = success && writeField((uint8_t *)&_prefs.path_hash_mode, sizeof(_prefs.path_hash_mode)); // 78
success = success && writeField(pad, 1); // 79
success = success && writeField(pad + 1, 1); // 79
success = success && writeField((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
success = success && writeField((uint8_t *)&_prefs.buzzer_quiet, sizeof(_prefs.buzzer_quiet)); // 84
success = success && writeField((uint8_t *)&_prefs.gps_enabled, sizeof(_prefs.gps_enabled)); // 85
+26
View File
@@ -3360,6 +3360,7 @@ void MyMesh::begin(bool has_display, bool radio_available,
_prefs.wifi_enabled = constrain(_prefs.wifi_enabled, 0, 1);
_prefs.usb_logging_enabled = constrain(_prefs.usb_logging_enabled, 0, 2);
_prefs.usb_debug_enabled = _prefs.usb_debug_enabled == 1 ? 1 : 0;
_prefs.usb_logging_always = _prefs.usb_logging_always == 1 ? 1 : 0;
_prefs.cad_enabled = constrain(_prefs.cad_enabled, 0, 1);
if (_prefs.cad_scan_timeout_ms != 0
&& (_prefs.cad_scan_timeout_ms < mesh::CAD_SCAN_MIN_TIMEOUT_MS
@@ -3383,6 +3384,9 @@ void MyMesh::begin(bool has_display, bool radio_available,
const bool usb_logging_enabled = _prefs.usb_logging_enabled != 0;
mesh::configureUsbLoggingPacketStream(_prefs.usb_logging_enabled == 2);
mesh::setUsbDebugEnabled(_prefs.usb_debug_enabled != 0);
#if !defined(MESH_DUAL_CDC_LOGGING)
mesh::setUsbPacketLoggingAlways(_prefs.usb_logging_always != 0);
#endif
applyUsbLoggingState(usb_logging_enabled);
if (!mesh::saveUsbLoggingBootPreference(usb_logging_enabled)) {
MESH_DEBUG_PRINTLN("Unable to save next-boot USB logging interface state");
@@ -4311,6 +4315,28 @@ bool MyMesh::handleLocalControlCommand(const char* command, char* reply,
}
#endif
#if MESH_USB_LOGGING_AVAILABLE && !defined(MESH_DUAL_CDC_LOGGING)
if (strcmp(command, "get usb.logging.always") == 0) {
snprintf(reply, reply_size, "usb.logging.always %s",
mesh::isUsbPacketLoggingAlways() ? "on" : "off");
return true;
}
if (strncmp(command, "set usb.logging.always", 22) == 0
&& (command[22] == 0 || command[22] == ' ' || command[22] == '\t')) {
const char* value = command + 22;
while (*value == ' ' || *value == '\t') value++;
if (strcmp(value, "on") != 0 && strcmp(value, "off") != 0) {
mesh::cli::formatError(reply, reply_size, "use set usb.logging.always on|off");
} else if (!savePreference(_prefs.usb_logging_always,
uint8_t(strcmp(value, "on") == 0))) {
mesh::cli::formatError(reply, reply_size, "USB logging policy could not be saved");
} else {
snprintf(reply, reply_size, "OK - usb.logging.always %s (saved)",
_prefs.usb_logging_always ? "on" : "off");
}
return true;
}
#endif
#if MESH_USB_LOGGING_AVAILABLE
if (strcmp(command, "get usb.debug") == 0) {
snprintf(reply, reply_size, "usb.debug %s%s",
+8 -2
View File
@@ -610,17 +610,23 @@ public:
bool getChannel(int channel_idx, ChannelDetails& channel);
bool setChannel(int channel_idx, const ChannelDetails& channel);
bool savePrefs() {
const uint8_t previous_usb_debug = _prefs.usb_debug_enabled;
const uint8_t previous_tail_flags[] = {
_prefs.usb_debug_enabled, _prefs.usb_logging_always};
_prefs.usb_debug_enabled = _prefs.usb_debug_enabled == 1 ? 1 : 0;
_prefs.usb_logging_always = _prefs.usb_logging_always == 1 ? 1 : 0;
const bool saved =
_store->savePrefs(_prefs, sensors.node_lat, sensors.node_lon);
if (saved) {
_prefs.clearDirty();
#if MESH_USB_LOGGING_AVAILABLE
mesh::setUsbDebugEnabled(_prefs.usb_debug_enabled != 0);
#if !defined(MESH_DUAL_CDC_LOGGING)
mesh::setUsbPacketLoggingAlways(_prefs.usb_logging_always != 0);
#endif
#endif
} else {
_prefs.usb_debug_enabled = previous_usb_debug;
_prefs.usb_debug_enabled = previous_tail_flags[0];
_prefs.usb_logging_always = previous_tail_flags[1];
}
return saved;
}
+33 -11
View File
@@ -138,6 +138,9 @@ public:
#endif
uint8_t tx_media = mesh::RADIO_TX_AUTO;
uint8_t tx_ssh = mesh::RADIO_TX_AUTO;
// Reserved wire byte 36; older writers stored zero here.
uint8_t usb_logging_always = 0;
static constexpr uint8_t USB_LOGGING_ALWAYS_WIRE_ON = 0xA1;
private:
class RadioPrefs : public CommonRadioPrefs {
@@ -168,6 +171,9 @@ private:
def("rxps_level", _parent->rx_ps_level);
def("rxps_preamble", _parent->rx_ps_preamble);
def("usb_debug", _parent->usb_debug_enabled);
#if !defined(MESH_DUAL_CDC_LOGGING)
def("usb_log_always", _parent->usb_logging_always);
#endif
#ifdef TBEAM_1W
def("fan", _parent->fan_mode, sizeof(_parent->fan_mode));
def("fan_lo", _parent->fan_lo);
@@ -183,25 +189,41 @@ private:
}
bool getByKey(const char* key, char* value, size_t max_len) override {
if (key != nullptr && strcmp(key, "usb_debug") == 0) {
uint8_t* flag = &_parent->usb_debug_enabled;
bool handled = key != nullptr && strcmp(key, "usb_debug") == 0;
#if !defined(MESH_DUAL_CDC_LOGGING)
if (key != nullptr && strcmp(key, "usb_log_always") == 0) {
flag = &_parent->usb_logging_always;
handled = true;
}
#endif
if (handled) {
if (value == nullptr || max_len == 0) return false;
if (max_len < 2) {
value[0] = 0;
return false;
}
value[0] = _parent->usb_debug_enabled == 1 ? '1' : '0';
value[0] = *flag == 1 ? '1' : '0';
value[1] = 0;
return true;
}
return CommonRadioPrefs::getByKey(key, value, max_len);
}
bool setByKey(const char* key, const char* value) override {
if (key != nullptr && strcmp(key, "usb_debug") == 0) {
uint8_t* flag = &_parent->usb_debug_enabled;
bool handled = key != nullptr && strcmp(key, "usb_debug") == 0;
#if !defined(MESH_DUAL_CDC_LOGGING)
if (key != nullptr && strcmp(key, "usb_log_always") == 0) {
flag = &_parent->usb_logging_always;
handled = true;
}
#endif
if (handled) {
if (value == nullptr
|| (strcmp(value, "0") != 0 && strcmp(value, "1") != 0)) {
return false;
}
_parent->usb_debug_enabled = value[0] == '1' ? 1 : 0;
*flag = value[0] == '1' ? 1 : 0;
markDirty();
return true;
}
@@ -289,23 +311,23 @@ public:
// both live adapters untouched and a scratch snapshot's adapters local.
bool copyPersistedValuesFrom(const CompanionNodePrefs& source) {
if (&source == this) return true;
const auto& last_value = tx_ssh;
static_assert(sizeof(tx_ssh) == sizeof(uint8_t),
"The last persisted traffic policy is one byte");
const auto& last_value = usb_logging_always;
static_assert(sizeof(usb_logging_always) == sizeof(uint8_t),
"The last persisted preference is one byte");
const uintptr_t first = reinterpret_cast<uintptr_t>(&airtime_factor);
const uintptr_t last = reinterpret_cast<uintptr_t>(&last_value);
if (last < first
|| last - first > sizeof(*this) - sizeof(tx_ssh)) {
|| last - first > sizeof(*this) - sizeof(usb_logging_always)) {
return false;
}
const size_t size = static_cast<size_t>(last - first)
+ sizeof(tx_ssh);
+ sizeof(usb_logging_always);
const auto bounded = [size](const CompanionNodePrefs& values) -> bool {
const uintptr_t object = reinterpret_cast<uintptr_t>(&values);
const uintptr_t begin =
reinterpret_cast<uintptr_t>(&values.airtime_factor);
const uintptr_t end = reinterpret_cast<uintptr_t>(
&values.tx_ssh
&values.usb_logging_always
);
const uintptr_t radio_begin = reinterpret_cast<uintptr_t>(&values.radio);
const uintptr_t custom_begin = reinterpret_cast<uintptr_t>(&values.custom);
@@ -314,7 +336,7 @@ public:
return begin >= object && begin - object <= sizeof(values)
&& size <= sizeof(values) - static_cast<size_t>(begin - object)
&& end >= begin
&& end - begin == size - sizeof(values.tx_ssh)
&& end - begin == size - sizeof(values.usb_logging_always)
&& radio_begin >= begin && size <= radio_begin - begin
&& custom_begin >= begin && size <= custom_begin - begin;
};
@@ -136,7 +136,8 @@ static const void* prefValue(const PrefField& field,
const int8_t* timezone = nullptr) {
if (field.offset == PrefsLat) return &lat;
if (field.offset == PrefsLon) return &lon;
if (field.offset == PrefsPad) return pad;
// Byte 36 carries the always-log marker; the separate byte 79 stays zero.
if (field.offset == PrefsPad) return pad + (field.length == 1);
const void* value = reinterpret_cast<const uint8_t*>(&values) + field.offset;
return timezone && value == &values.tz_offset_legacy ? timezone : value;
}