From 6dfb7a4b6941bc828069258724dc24988e5f7266 Mon Sep 17 00:00:00 2001 From: zjs81 Date: Sat, 14 Mar 2026 18:41:21 -0700 Subject: [PATCH] fix: auto-add flag parsing, contact cache restore, and USB reconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix operator precedence bug in _handleAutoAddConfig where `flags & flag != 0` was parsed as `flags & (flag != 0)`, always checking bit 0 instead of the correct flag bit - Populate _contacts from cache in loadContactCache() so contacts persist across app restarts - Toggle DTR low→high on USB connect to force device to see a fresh connection - Add 10ms inter-frame delay for USB sends to prevent missed responses - Deassert DTR before closing USB port on disconnect/dispose Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/connector/meshcore_connector.dart | 17 ++++++++++----- lib/connector/meshcore_connector_usb.dart | 2 ++ lib/services/usb_serial_service_native.dart | 23 +++++++++++++++++++++ lib/services/usb_serial_service_web.dart | 11 ++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 8f93f33..1af3c0b 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -708,6 +708,9 @@ class MeshCoreConnector extends ChangeNotifier { _knownContactKeys ..clear() ..addAll(cached.map((c) => c.publicKeyHex)); + _contacts + ..clear() + ..addAll(cached); for (final contact in cached) { _ensureContactSmazSettingLoaded(contact.publicKeyHex); } @@ -1540,6 +1543,10 @@ class MeshCoreConnector extends ChangeNotifier { if (_activeTransport == MeshCoreTransportType.usb) { await _usbManager.write(data); + // Brief pause so the device firmware can process each frame before the + // next arrives. Without this, rapid-fire frames over USB can cause the + // device to miss responses (especially on reconnect). + await Future.delayed(const Duration(milliseconds: 10)); } else if (_activeTransport == MeshCoreTransportType.tcp) { await _tcpConnector.write(data); } else { @@ -4837,11 +4844,11 @@ class MeshCoreConnector extends ChangeNotifier { try { reader.skipBytes(1); // Skip the response code byte final flags = reader.readByte(); - _autoAddUsers = flags & autoAddChatFlag != 0; - _autoAddRepeaters = flags & autoAddRepeaterFlag != 0; - _autoAddRoomServers = flags & autoAddRoomServerFlag != 0; - _autoAddSensors = flags & autoAddSensorFlag != 0; - _overwriteOldest = flags & autoAddOverwriteOldestFlag != 0; + _autoAddUsers = (flags & autoAddChatFlag) != 0; + _autoAddRepeaters = (flags & autoAddRepeaterFlag) != 0; + _autoAddRoomServers = (flags & autoAddRoomServerFlag) != 0; + _autoAddSensors = (flags & autoAddSensorFlag) != 0; + _overwriteOldest = (flags & autoAddOverwriteOldestFlag) != 0; } catch (e) { appLogger.error('Failed to parse auto-add config: $e', tag: 'Connector'); } diff --git a/lib/connector/meshcore_connector_usb.dart b/lib/connector/meshcore_connector_usb.dart index 74e7355..56718bc 100644 --- a/lib/connector/meshcore_connector_usb.dart +++ b/lib/connector/meshcore_connector_usb.dart @@ -64,6 +64,8 @@ class MeshCoreUsbManager { Future write(Uint8List data) => _service.write(data); + Future writeRaw(Uint8List data) => _service.writeRaw(data); + // --- Label management --- void updateConnectedLabel(String selfName) { _service.updateConnectedLabel(selfName); diff --git a/lib/services/usb_serial_service_native.dart b/lib/services/usb_serial_service_native.dart index fca3d19..c1d3946 100644 --- a/lib/services/usb_serial_service_native.dart +++ b/lib/services/usb_serial_service_native.dart @@ -189,6 +189,10 @@ class UsbSerialService { serial.setStopBits1(); serial.setFlowControlNone(); serial.setRTS(false); + // Toggle DTR low→high so the device sees a fresh connection even + // if the previous disconnect didn't cleanly signal DTR drop. + serial.setDTR(false); + await Future.delayed(const Duration(milliseconds: 50)); serial.setDTR(true); _serial = serial; // Update the normalized port name to whichever candidate succeeded. @@ -249,6 +253,23 @@ class UsbSerialService { _status = UsbSerialStatus.connected; } + Future writeRaw(Uint8List data) async { + if (!isConnected) { + throw StateError('USB serial port is not open'); + } + if (_useAndroidUsbHost) { + try { + await _androidMethodChannel.invokeMethod('write', { + 'data': data, + }); + } on PlatformException catch (error) { + throw StateError(error.message ?? error.code); + } + } else { + _serial!.write(data); + } + } + Future write(Uint8List data) async { if (!isConnected) { throw StateError('USB serial port is not open'); @@ -300,6 +321,7 @@ class UsbSerialService { _serial = null; try { if (serial?.isOpen() == FlOpenStatus.open) { + serial?.setDTR(false); serial?.closePort(); } } catch (_) { @@ -350,6 +372,7 @@ class UsbSerialService { final serial = _serial; try { if (serial?.isOpen() == FlOpenStatus.open) { + serial?.setDTR(false); serial?.closePort(); // synchronous C call — kills the SerialThread } } catch (_) {} diff --git a/lib/services/usb_serial_service_web.dart b/lib/services/usb_serial_service_web.dart index 4c83d7d..5261308 100644 --- a/lib/services/usb_serial_service_web.dart +++ b/lib/services/usb_serial_service_web.dart @@ -127,6 +127,17 @@ class UsbSerialService { } } + Future writeRaw(Uint8List data) async { + if (!isConnected || _writer == null) { + throw StateError('USB serial port is not open'); + } + final promise = _writer!.callMethod>( + 'write'.toJS, + data.toJS, + ); + await promise.toDart; + } + Future write(Uint8List data) async { if (!isConnected || _writer == null) { throw StateError('USB serial port is not open');