Merge USB session safety and preserve GPS preference compatibility

This commit is contained in:
mikecarper committed 2026-10-03 16:12:32 -07:00
commit 24fcbf3247
62 files changed
+2479 -240

No files matched your search

+24 -18
View File
@@ -1002,6 +1002,7 @@ bool DataStore::loadPrefsInt(const char *filename,
// Files written before this tail existed always started Bluetooth at boot.
// Do not inherit a runtime off value when loading one of those images.
loaded_prefs.bluetooth_enabled = 1;
loaded_prefs.gps_sync_interval_hours = 0;
// Older images have no independent debug preference. Never inherit a
// runtime-on value when loading one of those images.
loaded_prefs.usb_debug_enabled = 0;
@@ -1012,7 +1013,7 @@ bool DataStore::loadPrefsInt(const char *filename,
// actually emitted by those releases, but reject a truncated field/group
// or an unknown tail before any value reaches the live preferences.
static const uint32_t MIN_PREFS_SIZE = 84;
static const uint32_t KNOWN_PREFS_SIZES[] = {
static const uint16_t KNOWN_PREFS_SIZES[] = {
84, 85, 90, 91, 92, 93, 140, 141, 142, 143, 144, 155,
156, 157, 158, 159,
159 + sizeof(loaded_prefs.bluetooth_name),
@@ -1061,11 +1062,18 @@ bool DataStore::loadPrefsInt(const char *filename,
232,
#endif
#ifdef TBEAM_1W
240, // Independent USB debug preference, after the existing tail
241, // GPS time-sync cadence in hours, appended after Bluetooth enable
#elif defined(RP2040_PLATFORM) && defined(ENABLE_WIFI_INTERFACE)
330,
331,
#else
233,
234,
#endif
#ifdef TBEAM_1W
242, // Independent USB debug preference, after the published GPS tail
#elif defined(RP2040_PLATFORM) && defined(ENABLE_WIFI_INTERFACE)
332,
#else
235,
#endif
};
const uint32_t prefs_size = file.size();
@@ -1210,6 +1218,8 @@ bool DataStore::loadPrefsInt(const char *filename,
sizeof(loaded_prefs.one_key_dm_enabled));
readOptionalField(&loaded_prefs.bluetooth_enabled,
sizeof(loaded_prefs.bluetooth_enabled));
readOptionalField(&loaded_prefs.gps_sync_interval_hours,
sizeof(loaded_prefs.gps_sync_interval_hours));
readOptionalField(&loaded_prefs.usb_debug_enabled,
sizeof(loaded_prefs.usb_debug_enabled));
@@ -1364,20 +1374,16 @@ bool DataStore::savePrefs(const CompanionNodePrefs& _prefs, double node_lat, dou
success = success && file.write((uint8_t *)_prefs.wifi_pwd,
sizeof(_prefs.wifi_pwd)) == sizeof(_prefs.wifi_pwd);
#endif
success = success && file.write((uint8_t *)&_prefs.flood_retry_attempts,
sizeof(_prefs.flood_retry_attempts)) == sizeof(_prefs.flood_retry_attempts);
success = success && file.write((uint8_t *)&_prefs.flood_retry_max_path,
sizeof(_prefs.flood_retry_max_path)) == sizeof(_prefs.flood_retry_max_path);
success = success && file.write((uint8_t *)&_prefs.flood_retry_group_max_path,
sizeof(_prefs.flood_retry_group_max_path)) == sizeof(_prefs.flood_retry_group_max_path);
success = success && file.write((uint8_t *)&_prefs.flood_retry_advert_enabled,
sizeof(_prefs.flood_retry_advert_enabled)) == sizeof(_prefs.flood_retry_advert_enabled);
success = success && file.write((uint8_t *)&_prefs.one_key_dm_enabled,
sizeof(_prefs.one_key_dm_enabled)) == sizeof(_prefs.one_key_dm_enabled);
success = success && file.write((uint8_t *)&_prefs.bluetooth_enabled,
sizeof(_prefs.bluetooth_enabled)) == sizeof(_prefs.bluetooth_enabled);
success = success && file.write((uint8_t *)&_prefs.usb_debug_enabled,
sizeof(_prefs.usb_debug_enabled)) == sizeof(_prefs.usb_debug_enabled);
// Pack only the explicit persisted tail, never the class's padded layout.
// memcpy retains the GPS field's established native byte representation.
uint8_t tail[9] = {
_prefs.flood_retry_attempts, _prefs.flood_retry_max_path,
_prefs.flood_retry_group_max_path, _prefs.flood_retry_advert_enabled,
_prefs.one_key_dm_enabled, _prefs.bluetooth_enabled, 0, 0,
_prefs.usb_debug_enabled};
memcpy(tail + 6, &_prefs.gps_sync_interval_hours,
sizeof(_prefs.gps_sync_interval_hours));
success = success && file.write(tail, sizeof(tail)) == sizeof(tail);
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) || defined(ESP32_PLATFORM) || defined(RP2040_PLATFORM)
success = file.commit(success);
+40 -1
View File
@@ -74,6 +74,7 @@
#endif
#include <helpers/CLICommandUtils.h>
#include <helpers/GpsPowerPolicy.h>
#if COMPANION_FEATURE_TEXT_TERMINAL
#include <helpers/TracePathHelpers.h>
#endif
@@ -2308,6 +2309,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
_prefs.buzzer_quiet = DEFAULT_BUZZER_QUIET ? 1 : 0;
_prefs.gps_enabled = 0; // GPS disabled by default
_prefs.gps_interval = 0; // Use the default 1-second fix-processing interval
_prefs.gps_sync_interval_hours = 0; // Retain the board's default time-sync cadence
_prefs.autoadd_config = DEFAULT_AUTOADD_CONFIG;
_prefs.path_hash_mode = DEFAULT_PATH_HASH_MODE;
#ifdef RADIO_FEM_TXGAIN
@@ -2606,6 +2608,8 @@ void MyMesh::begin(bool has_display, bool radio_available,
_prefs.vibe_quiet = constrain(_prefs.vibe_quiet, 0, 1);
_prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1
_prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours
_prefs.gps_sync_interval_hours = constrain(_prefs.gps_sync_interval_hours,
0, mesh::gps::MAX_SYNC_INTERVAL_HOURS);
_prefs.autoadd_config &= AUTO_ADD_OVERWRITE_OLDEST | AUTO_ADD_CHAT | AUTO_ADD_REPEATER | AUTO_ADD_ROOM_SERVER | AUTO_ADD_SENSOR;
_prefs.path_hash_mode = constrain(_prefs.path_hash_mode, 0, 2);
_prefs.radio_fem_rxgain_override = constrain(_prefs.radio_fem_rxgain_override, 0, 1);
@@ -3223,6 +3227,37 @@ bool MyMesh::handleLocalControlCommand(const char* command, char* reply,
}
#if ENV_INCLUDE_GPS == 1
if (strcmp(command, "get gps.sync.interval") == 0) {
auto* provider = sensors.getLocationProvider();
if (provider == nullptr) {
snprintf(reply, reply_size, "Error: GPS unavailable");
} else {
const uint16_t hours = provider->getTimeSyncIntervalHours();
if (hours == 0) snprintf(reply, reply_size, "> default (board GPS sync policy)");
else snprintf(reply, reply_size, "> %u hours", (unsigned)hours);
}
return true;
}
const char* gps_sync_value = nullptr;
if (strncmp(command, "set gps.sync.interval", 21) == 0
&& (command[21] == 0 || command[21] == ' ' || command[21] == '\t')) {
gps_sync_value = command + 21;
while (*gps_sync_value == ' ' || *gps_sync_value == '\t') ++gps_sync_value;
}
if (gps_sync_value) {
uint16_t hours = 0;
if (!mesh::gps::parseSyncIntervalHours(gps_sync_value, hours)) {
snprintf(reply, reply_size, "Error: GPS sync interval must be 1..336 hours");
} else if (sensors.getLocationProvider() == nullptr) {
snprintf(reply, reply_size, "Error: GPS unavailable");
} else if (!savePreference(_prefs.gps_sync_interval_hours, hours)) {
snprintf(reply, reply_size, "Error: GPS sync interval could not be saved");
} else {
sensors.applyGpsTimeSyncInterval(hours);
snprintf(reply, reply_size, "OK - GPS sync interval %u hours (saved)", (unsigned)hours);
}
return true;
}
if (strcmp(command, "get gps") == 0) {
const char* gps = sensors.getSettingByKey("gps");
snprintf(reply, reply_size, gps ? "> %s" : "Error: GPS unavailable",
@@ -5210,6 +5245,8 @@ void MyMesh::handleCmdFrame(size_t len) {
} else {
text[tlen] = 0; // ensure null
BaseSerialInterface* reply_route = _serial->captureReplyRoute();
beginUsbLoggingReplyBarrier(reply_route);
reply_buf[0] = 0;
bool handled = false;
#if defined(MESHCORE_EXTRAFS_HIL)
@@ -5225,7 +5262,9 @@ void MyMesh::handleCmdFrame(size_t len) {
out_frame[0] = RESP_CODE_CLI_REPLY;
int rlen = strlen(reply_buf);
memcpy(&out_frame[1], reply_buf, rlen);
_serial->writeFrame(out_frame, 1 + rlen);
const bool reply_queued = _serial->writeFrameToRoute(
reply_route, out_frame, 1 + rlen) == static_cast<size_t>(1 + rlen);
endUsbLoggingReplyBarrier(reply_queued);
}
} else if (cmd_frame[0] == CMD_SEND_TXT_MSG && len >= 14) {
int i = 1;
+5
View File
@@ -303,6 +303,10 @@ public:
bool handleLocalControlCommand(const char* command, char* reply,
size_t reply_size);
void applyUsbLoggingState(bool enabled);
// A framed USB logging change cannot abandon its requester before the
// acknowledgement has entered the transport. Other transports stay live.
void beginUsbLoggingReplyBarrier(BaseSerialInterface* route);
void endUsbLoggingReplyBarrier(bool reply_queued);
bool handleTxRoutingCommand(const char* command, char* reply, size_t reply_size);
int getRecentlyHeard(AdvertPath dest[], int max_num);
@@ -449,6 +453,7 @@ public:
char interval_str[12]; // Max: 24 hours = 86400 seconds (5 digits + null)
sprintf(interval_str, "%u", _prefs.gps_interval);
sensors.setSettingValue("gps_interval", interval_str);
sensors.applyGpsTimeSyncInterval(_prefs.gps_sync_interval_hours);
}
#endif
+3 -1
View File
@@ -110,8 +110,10 @@ public:
uint8_t one_key_dm_enabled = 0;
// Appended after all existing fields so older images keep Bluetooth enabled.
uint8_t bluetooth_enabled = 1;
// Published tail: zero retains the board's legacy GPS sync cadence.
uint16_t gps_sync_interval_hours = 0;
// Debug verbosity is independent of the packet/output master switch. Keep
// this after every prior persisted field so legacy images default to quiet.
// this after the published GPS cadence so legacy images default to quiet.
uint8_t usb_debug_enabled = 0;
private:
+70
View File
@@ -628,6 +628,12 @@ static bool usb_host_session_connected = false;
static bool usb_logging_terminal_mode = false;
static bool usb_logging_network_parked = false;
static bool usb_protocol_initialized = false;
#if MESH_USB_LOGGING_AVAILABLE
static bool usb_logging_reply_hold = false;
static bool usb_logging_reply_staged = false;
static bool usb_logging_reply_pending = false;
static bool usb_logging_reply_state = false;
#endif
static mesh::UsbBinaryStartupProbe usb_binary_startup_probe;
static mesh::UsbAsciiSessionDefault usb_ascii_session_default;
#if COMPANION_FEATURE_USB_MOTA_SOURCE
@@ -741,6 +747,10 @@ static bool hasObservableActiveUsbTerminalClient() {
}
static void cancelUsbSerialOperations() {
#if MESH_USB_LOGGING_AVAILABLE
// A cancelled reply must not enable diagnostics for the next USB host.
usb_logging_reply_pending = false;
#endif
// Contact enumeration uses the manager's pinned streaming route; delayed
// single replies capture their own route inside MyMesh. Cancel only USB's
// ownership so a simultaneous BLE/WiFi operation keeps running.
@@ -1236,6 +1246,16 @@ static void serviceUsbLoggingOwnership(bool logging_enabled) {
}
void MyMesh::applyUsbLoggingState(bool enabled) {
#if MESH_USB_LOGGING_AVAILABLE
if (usb_logging_reply_hold) {
usb_logging_reply_staged = true;
usb_logging_reply_state = enabled;
return;
}
// A newer explicit setting (including one from BLE/WiFi) wins over an
// earlier USB command whose acknowledgement is still backpressured.
usb_logging_reply_pending = false;
#endif
// Park the framed transport before opening the diagnostic gate, including
// TCP/browser commands which execute outside the mesh dispatcher.
if (enabled && usb_protocol_initialized) serviceUsbLoggingOwnership(true);
@@ -1243,6 +1263,52 @@ void MyMesh::applyUsbLoggingState(bool enabled) {
if (usb_protocol_initialized) serviceUsbLoggingOwnership(enabled);
}
void MyMesh::beginUsbLoggingReplyBarrier(BaseSerialInterface* route) {
#if MESH_USB_LOGGING_AVAILABLE
usb_logging_reply_hold = usb_protocol_initialized
&& route == &usb_serial_interface
&& !mesh::hasDedicatedUsbLoggingPort()
&& !usb_serial_interface.isPassthroughMode();
usb_logging_reply_staged = false;
#else
(void)route;
#endif
}
void MyMesh::endUsbLoggingReplyBarrier(bool reply_queued) {
#if MESH_USB_LOGGING_AVAILABLE
usb_logging_reply_hold = false;
if (usb_logging_reply_staged) {
// No response fallback: a full queue leaves logging unchanged for this
// boot. Its saved preference can still take effect at the next boot.
usb_logging_reply_pending = reply_queued;
usb_logging_reply_staged = false;
}
#else
(void)reply_queued;
#endif
}
static void serviceUsbLoggingReplyBarrier() {
#if MESH_USB_LOGGING_AVAILABLE
if (!usb_logging_reply_pending) return;
if (!usb_serial_interface.isConnected()
|| usb_serial_interface.isPassthroughMode()) {
usb_logging_reply_pending = false;
return;
}
// One nonblocking transport pass. Never wait on an unread USB host or
// include RX parser work in this check: a partial next command cannot hold
// the already queued acknowledgement. Once the complete frame is admitted,
// the ordered native FIFO sends it before any new terminal/log bytes.
usb_serial_interface.loop();
if (usb_serial_interface.isWriteBusy()) return;
const bool enabled = usb_logging_reply_state;
usb_logging_reply_pending = false;
the_mesh.applyUsbLoggingState(enabled);
#endif
}
static void expireUsbBinaryStartupProbeBeforeDispatch() {
const uint32_t now = millis();
if (!usb_binary_startup_probe.hasTimedOut(now)) return;
@@ -1263,6 +1329,9 @@ static void expireUsbBinaryStartupProbeBeforeDispatch() {
void MyMesh::applyUsbLoggingState(bool enabled) {
mesh::setUsbLoggingEnabled(enabled);
}
void MyMesh::beginUsbLoggingReplyBarrier(BaseSerialInterface*) {}
void MyMesh::endUsbLoggingReplyBarrier(bool) {}
#endif
#if defined(ENABLE_USB_INTERFACE) && (COMPANION_FEATURE_NETWORK_TERMINAL || defined(WITH_WEBCONFIG))
@@ -3403,6 +3472,7 @@ void loop() {
#if defined(ENABLE_USB_INTERFACE)
serviceUsbTerminalHostSessionReset();
serviceUsbAsciiSessionDefault();
serviceUsbLoggingReplyBarrier();
serviceUsbLoggingOwnership(mesh::isUsbLoggingEnabled()); // Before framed dispatch.
#endif
// Identify nRF52 CDC 1 when a terminal opens it. Doing this on the connection
+64 -61
View File
@@ -5154,6 +5154,21 @@ void MyMesh::cancelPendingSerialOutput() {
void MyMesh::servicePendingSerialOutput() {
Stream& console = mesh::usbConsolePort();
const auto write_pending = [&]() {
const int available = console.availableForWrite();
if (available <= 0 || serial_log_pending_size == 0) return;
// A low-memory HWCDC setup can have a 256/512-byte ring. Never require a
// whole 640-byte stored line to fit that ring: retain and retry its suffix.
const size_t attempt = serial_log_pending_size < static_cast<size_t>(available)
? serial_log_pending_size : static_cast<size_t>(available);
size_t written = console.write(
reinterpret_cast<const uint8_t*>(serial_log_pending), attempt);
if (written > attempt) written = attempt;
serial_log_pending_size -= written;
if (written > 0 && serial_log_pending_size > 0) {
memmove(serial_log_pending, serial_log_pending + written, serial_log_pending_size);
}
};
if (serial_acl_next >= 0) {
if (serial_log_pending_size == 0) {
@@ -5181,63 +5196,58 @@ void MyMesh::servicePendingSerialOutput() {
}
}
}
if (console.availableForWrite() < static_cast<int>(serial_log_pending_size)) return;
size_t written = console.write(
reinterpret_cast<const uint8_t*>(serial_log_pending), serial_log_pending_size);
if (written > serial_log_pending_size) written = serial_log_pending_size;
serial_log_pending_size -= written;
if (written > 0 && serial_log_pending_size > 0) {
memmove(serial_log_pending, serial_log_pending + written, serial_log_pending_size);
}
write_pending();
return;
}
if (serial_recent_next >= 0) {
char record[64];
int length = 0;
const SimpleMeshTables::RecentRepeaterInfo* next_info = nullptr;
int next_index = -1;
if (serial_recent_header) {
length = snprintf(record, sizeof(record), "Recent repeaters (%d):\n",
serial_recent_count);
} else if (serial_recent_count == 0) {
length = snprintf(record, sizeof(record), "-none-\r\n");
} else if (serial_recent_next < serial_recent_count) {
const auto* tables = static_cast<const SimpleMeshTables*>(getTables());
const auto* info = tables ? tables->getNextRecentRepeaterBySortKey(
serial_recent_has_cursor ? &serial_recent_cursor : nullptr,
serial_recent_cursor_index, next_index) : nullptr;
if (info == nullptr) {
if (serial_log_pending_size == 0) {
char record[64];
int length = 0;
int next_index = -1;
if (serial_recent_header) {
length = snprintf(record, sizeof(record), "Recent repeaters (%d):\n",
serial_recent_count);
} else if (serial_recent_count == 0) {
length = snprintf(record, sizeof(record), "-none-\r\n");
} else if (serial_recent_next < serial_recent_count) {
const auto* tables = static_cast<const SimpleMeshTables*>(getTables());
const auto* info = tables ? tables->getNextRecentRepeaterBySortKey(
serial_recent_has_cursor ? &serial_recent_cursor : nullptr,
serial_recent_cursor_index, next_index) : nullptr;
if (info == nullptr) {
serial_recent_next = -1;
return;
}
char prefix[MAX_ROUTE_HASH_BYTES * 2 + 1];
char snr[12];
mesh::Utils::toHex(prefix, info->prefix, info->prefix_len);
formatLocalSnrX4(snr, sizeof(snr), info->snr_x4);
length = snprintf(record, sizeof(record), "%s,%s%s\n", prefix,
snr[0] == '-' ? "" : " ", snr);
// Snapshot the row before a short write. Its next pass must retry only
// the retained suffix, even if the live recent-repeater table changes.
serial_recent_cursor = *info;
serial_recent_cursor_index = next_index;
serial_recent_has_cursor = true;
} else {
serial_recent_next = -1;
return;
}
next_info = info;
char prefix[MAX_ROUTE_HASH_BYTES * 2 + 1];
char snr[12];
mesh::Utils::toHex(prefix, info->prefix, info->prefix_len);
formatLocalSnrX4(snr, sizeof(snr), info->snr_x4);
length = snprintf(record, sizeof(record), "%s,%s%s\n", prefix,
snr[0] == '-' ? "" : " ", snr);
} else {
serial_recent_next = -1;
return;
// One bounded row per pass. HWCDC can return a short write even after a
// capacity preflight because another diagnostic task shares its TX ring.
if (length <= 0 || static_cast<size_t>(length) >= sizeof(record)) {
serial_recent_next = -1;
return;
}
memcpy(serial_log_pending, record, length);
serial_log_pending_size = static_cast<size_t>(length);
}
// One complete row per pass, admitted atomically only when it fits.
if (length <= 0 || static_cast<size_t>(length) >= sizeof(record)) {
serial_recent_next = -1;
return;
}
if (console.availableForWrite() < length
|| console.write(reinterpret_cast<const uint8_t*>(record), length)
!= static_cast<size_t>(length)) return;
write_pending();
if (serial_log_pending_size != 0) return;
if (serial_recent_header) {
serial_recent_header = false;
} else {
if (next_info != nullptr) {
serial_recent_cursor = *next_info;
serial_recent_cursor_index = next_index;
serial_recent_has_cursor = true;
}
if (serial_recent_count == 0 || ++serial_recent_next >= serial_recent_count) {
serial_recent_next = -1;
}
@@ -5247,11 +5257,13 @@ void MyMesh::servicePendingSerialOutput() {
if (!serial_log_active) {
// CommonCLI's synchronous EOF is suppressed until the queued dump ends.
static const char eof[] = " -> EOF\r\n";
if (serial_log_eof_pending
&& console.availableForWrite() >= static_cast<int>(sizeof(eof) - 1)
&& console.write(reinterpret_cast<const uint8_t*>(eof), sizeof(eof) - 1)
== sizeof(eof) - 1) {
serial_log_eof_pending = false;
if (serial_log_eof_pending) {
if (serial_log_pending_size == 0) {
memcpy(serial_log_pending, eof, sizeof(eof) - 1);
serial_log_pending_size = sizeof(eof) - 1;
}
write_pending();
if (serial_log_pending_size == 0) serial_log_eof_pending = false;
}
return;
}
@@ -5303,16 +5315,7 @@ void MyMesh::servicePendingSerialOutput() {
serial_log_pending[serial_log_pending_size++] = '\n';
}
}
if (serial_log_pending_size > 0
&& console.availableForWrite() >= static_cast<int>(serial_log_pending_size)) {
size_t written = console.write(
reinterpret_cast<const uint8_t*>(serial_log_pending), serial_log_pending_size);
if (written > serial_log_pending_size) written = serial_log_pending_size;
serial_log_pending_size -= written;
if (written > 0 && serial_log_pending_size > 0) {
memmove(serial_log_pending, serial_log_pending + written, serial_log_pending_size);
}
}
write_pending();
if (serial_log_remaining == 0 && serial_log_pending_size == 0) {
serial_log_dump.close();
serial_log_active = false;
+1
View File
@@ -980,6 +980,7 @@ protected:
char interval_str[12];
sprintf(interval_str, "%u", _prefs.gps_interval);
sensors.setSettingValue("gps_interval", interval_str);
sensors.applyGpsTimeSyncInterval(_prefs.gps_sync_interval_hours);
}
#endif
+23 -23
View File
@@ -1712,6 +1712,20 @@ void MyMesh::cancelPendingSerialOutput() {
void MyMesh::servicePendingSerialOutput() {
Stream& console = mesh::usbConsolePort();
const auto write_pending = [&]() {
const int available = console.availableForWrite();
if (available <= 0 || serial_log_pending_size == 0) return;
// Retain a larger stored line across low-memory 256/512-byte HWCDC rings.
const size_t attempt = serial_log_pending_size < static_cast<size_t>(available)
? serial_log_pending_size : static_cast<size_t>(available);
size_t written = console.write(
reinterpret_cast<const uint8_t*>(serial_log_pending), attempt);
if (written > attempt) written = attempt;
serial_log_pending_size -= written;
if (written > 0 && serial_log_pending_size > 0) {
memmove(serial_log_pending, serial_log_pending + written, serial_log_pending_size);
}
};
if (serial_acl_next >= 0) {
if (serial_log_pending_size == 0) {
if (serial_acl_header) {
@@ -1738,24 +1752,19 @@ void MyMesh::servicePendingSerialOutput() {
}
}
}
if (console.availableForWrite() < static_cast<int>(serial_log_pending_size)) return;
size_t written = console.write(
reinterpret_cast<const uint8_t*>(serial_log_pending), serial_log_pending_size);
if (written > serial_log_pending_size) written = serial_log_pending_size;
serial_log_pending_size -= written;
if (written > 0 && serial_log_pending_size > 0) {
memmove(serial_log_pending, serial_log_pending + written, serial_log_pending_size);
}
write_pending();
return;
}
if (!serial_log_active) {
// CommonCLI's synchronous EOF is suppressed until the queued dump ends.
static const char eof[] = " -> EOF\r\n";
if (serial_log_eof_pending
&& console.availableForWrite() >= static_cast<int>(sizeof(eof) - 1)
&& console.write(reinterpret_cast<const uint8_t*>(eof), sizeof(eof) - 1)
== sizeof(eof) - 1) {
serial_log_eof_pending = false;
if (serial_log_eof_pending) {
if (serial_log_pending_size == 0) {
memcpy(serial_log_pending, eof, sizeof(eof) - 1);
serial_log_pending_size = sizeof(eof) - 1;
}
write_pending();
if (serial_log_pending_size == 0) serial_log_eof_pending = false;
}
return;
}
@@ -1807,16 +1816,7 @@ void MyMesh::servicePendingSerialOutput() {
serial_log_pending[serial_log_pending_size++] = '\n';
}
}
if (serial_log_pending_size > 0
&& console.availableForWrite() >= static_cast<int>(serial_log_pending_size)) {
size_t written = console.write(
reinterpret_cast<const uint8_t*>(serial_log_pending), serial_log_pending_size);
if (written > serial_log_pending_size) written = serial_log_pending_size;
serial_log_pending_size -= written;
if (written > 0 && serial_log_pending_size > 0) {
memmove(serial_log_pending, serial_log_pending + written, serial_log_pending_size);
}
}
write_pending();
if (serial_log_remaining == 0 && serial_log_pending_size == 0) {
serial_log_dump.close();
serial_log_active = false;
+1
View File
@@ -412,6 +412,7 @@ protected:
char interval_str[12];
sprintf(interval_str, "%u", _prefs.gps_interval);
sensors.setSettingValue("gps_interval", interval_str);
sensors.applyGpsTimeSyncInterval(_prefs.gps_sync_interval_hours);
}
#endif
+1
View File
@@ -257,6 +257,7 @@ private:
char interval_str[12];
sprintf(interval_str, "%u", _prefs.gps_interval);
sensors.setSettingValue("gps_interval", interval_str);
sensors.applyGpsTimeSyncInterval(_prefs.gps_sync_interval_hours);
}
#endif
};