mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
feat: USB serial companion support (Android)
- Add MeshCoreSerialService to meshcore_client (0x3C/0x3E framing) - Add usb_serial package for Android USB OTG - Add UsbSerialTransport wrapper for Android - Add USB tab to connection dialog (scan, select, connect) - Add ConnectionMode.usb + connectSerial/disconnectSerial - Web Serial stub (not yet implemented) - Fix message limits: DM 156→150, Channel 127→160 (firmware MAX_TEXT_LEN=160)
This commit is contained in:
@@ -16,6 +16,9 @@ enum ConnectionMode {
|
|||||||
|
|
||||||
/// Direct TCP/WiFi connection to MeshCore device (port 5000)
|
/// Direct TCP/WiFi connection to MeshCore device (port 5000)
|
||||||
tcp,
|
tcp,
|
||||||
|
|
||||||
|
/// USB serial connection (Android OTG or Web Serial API)
|
||||||
|
usb,
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ConnectionModeExtension on ConnectionMode {
|
extension ConnectionModeExtension on ConnectionMode {
|
||||||
@@ -25,6 +28,8 @@ extension ConnectionModeExtension on ConnectionMode {
|
|||||||
return 'Direct (BLE)';
|
return 'Direct (BLE)';
|
||||||
case ConnectionMode.tcp:
|
case ConnectionMode.tcp:
|
||||||
return 'Direct (WiFi)';
|
return 'Direct (WiFi)';
|
||||||
|
case ConnectionMode.usb:
|
||||||
|
return 'Direct (USB)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +39,8 @@ extension ConnectionModeExtension on ConnectionMode {
|
|||||||
return 'Direct BLE connection to MeshCore device';
|
return 'Direct BLE connection to MeshCore device';
|
||||||
case ConnectionMode.tcp:
|
case ConnectionMode.tcp:
|
||||||
return 'Direct WiFi/TCP connection to MeshCore device';
|
return 'Direct WiFi/TCP connection to MeshCore device';
|
||||||
|
case ConnectionMode.usb:
|
||||||
|
return 'USB serial connection to MeshCore device';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,15 +72,21 @@ class ConnectionProvider with ChangeNotifier {
|
|||||||
static const int _controlTypeNodeDiscoverReq = 0x80;
|
static const int _controlTypeNodeDiscoverReq = 0x80;
|
||||||
final MeshCoreBleService _bleService = MeshCoreBleService();
|
final MeshCoreBleService _bleService = MeshCoreBleService();
|
||||||
MeshCoreTcpService? _tcpService;
|
MeshCoreTcpService? _tcpService;
|
||||||
|
MeshCoreSerialService? _serialService;
|
||||||
|
|
||||||
/// Expose BLE service for background location tracking
|
/// Expose BLE service for background location tracking
|
||||||
MeshCoreBleService get bleService => _bleService;
|
MeshCoreBleService get bleService => _bleService;
|
||||||
|
|
||||||
/// Active service — BLE or TCP depending on current mode
|
/// Active service — BLE, TCP, or USB depending on current mode
|
||||||
MeshCoreServiceBase get _activeService =>
|
MeshCoreServiceBase get _activeService {
|
||||||
(_connectionMode == ConnectionMode.tcp && _tcpService != null)
|
if (_connectionMode == ConnectionMode.tcp && _tcpService != null) {
|
||||||
? _tcpService!
|
return _tcpService!;
|
||||||
: _bleService;
|
}
|
||||||
|
if (_connectionMode == ConnectionMode.usb && _serialService != null) {
|
||||||
|
return _serialService!;
|
||||||
|
}
|
||||||
|
return _bleService;
|
||||||
|
}
|
||||||
|
|
||||||
/// Current connection mode
|
/// Current connection mode
|
||||||
ConnectionMode _connectionMode = ConnectionMode.ble;
|
ConnectionMode _connectionMode = ConnectionMode.ble;
|
||||||
@@ -710,6 +716,56 @@ class ConnectionProvider with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Connect via USB serial using a pre-configured [MeshCoreSerialService].
|
||||||
|
///
|
||||||
|
/// The caller is responsible for opening the serial port and wiring
|
||||||
|
/// [service.writeRaw] + [service.feedRawBytes] before calling this.
|
||||||
|
/// After this call succeeds, [service.markConnected()] has already run.
|
||||||
|
Future<bool> connectSerial(MeshCoreSerialService service) async {
|
||||||
|
debugPrint('🔌 [Provider] connectSerial()');
|
||||||
|
|
||||||
|
_deviceInfo = _deviceInfo.copyWith(
|
||||||
|
deviceId: 'usb',
|
||||||
|
deviceName: 'USB Companion',
|
||||||
|
connectionState: ConnectionState.connecting,
|
||||||
|
);
|
||||||
|
_error = null;
|
||||||
|
_supportsAutoaddConfig = null;
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
_serialService?.dispose();
|
||||||
|
_serialService = service;
|
||||||
|
_wireServiceCallbacks(_serialService!);
|
||||||
|
_connectionMode = ConnectionMode.usb;
|
||||||
|
|
||||||
|
// markConnected() should already have been called by the transport.
|
||||||
|
// If it hasn't, the service won't be connected yet.
|
||||||
|
if (!service.isConnected) {
|
||||||
|
_deviceInfo = _deviceInfo.copyWith(
|
||||||
|
connectionState: ConnectionState.error,
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disconnect from USB serial device.
|
||||||
|
Future<void> disconnectSerial() async {
|
||||||
|
_serialService?.markDisconnected();
|
||||||
|
_serialService?.dispose();
|
||||||
|
_serialService = null;
|
||||||
|
_connectionMode = ConnectionMode.ble;
|
||||||
|
_supportsAutoaddConfig = null;
|
||||||
|
_resetSyncState();
|
||||||
|
_deviceInfo = DeviceInfo(connectionState: ConnectionState.disconnected);
|
||||||
|
_roomLoginManager.clearRoomLoginStates();
|
||||||
|
_pingTracker.clearAll();
|
||||||
|
_pendingSendOperations.clear();
|
||||||
|
_messageDeliveryTracker.clearTracking();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
/// Disconnect from device
|
/// Disconnect from device
|
||||||
Future<void> disconnect() async {
|
Future<void> disconnect() async {
|
||||||
_deviceInfo = _deviceInfo.copyWith(
|
_deviceInfo = _deviceInfo.copyWith(
|
||||||
@@ -721,6 +777,10 @@ class ConnectionProvider with ChangeNotifier {
|
|||||||
await disconnectTcp();
|
await disconnectTcp();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (_connectionMode == ConnectionMode.usb) {
|
||||||
|
await disconnectSerial();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await _bleService.disconnect();
|
await _bleService.disconnect();
|
||||||
|
|
||||||
|
|||||||
101
lib/services/serial/usb_serial_transport.dart
Normal file
101
lib/services/serial/usb_serial_transport.dart
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:usb_serial/usb_serial.dart';
|
||||||
|
import 'package:meshcore_client/meshcore_client.dart';
|
||||||
|
|
||||||
|
/// Android USB OTG transport for MeshCore serial connection.
|
||||||
|
///
|
||||||
|
/// Wraps the `usb_serial` package to provide raw byte I/O to
|
||||||
|
/// [MeshCoreSerialService].
|
||||||
|
class UsbSerialTransport {
|
||||||
|
static const int _baudRate = 115200;
|
||||||
|
|
||||||
|
UsbPort? _port;
|
||||||
|
StreamSubscription? _inputSubscription;
|
||||||
|
final MeshCoreSerialService _service;
|
||||||
|
|
||||||
|
UsbSerialTransport(this._service);
|
||||||
|
|
||||||
|
MeshCoreSerialService get service => _service;
|
||||||
|
|
||||||
|
bool get isConnected => _port != null;
|
||||||
|
|
||||||
|
/// List available USB serial devices.
|
||||||
|
static Future<List<UsbDevice>> listDevices() async {
|
||||||
|
return UsbSerial.listDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connect to a USB serial device.
|
||||||
|
Future<bool> connect(UsbDevice device) async {
|
||||||
|
try {
|
||||||
|
_port = await device.create();
|
||||||
|
if (_port == null) {
|
||||||
|
debugPrint('❌ [USB] Failed to create port for ${device.productName}');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final opened = await _port!.open();
|
||||||
|
if (!opened) {
|
||||||
|
debugPrint('❌ [USB] Failed to open port');
|
||||||
|
_port = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _port!.setDTR(true);
|
||||||
|
await _port!.setRTS(true);
|
||||||
|
await _port!.setPortParameters(
|
||||||
|
_baudRate,
|
||||||
|
UsbPort.DATABITS_8,
|
||||||
|
UsbPort.STOPBITS_1,
|
||||||
|
UsbPort.PARITY_NONE,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wire write callback
|
||||||
|
_service.writeRaw = _write;
|
||||||
|
|
||||||
|
// Listen for incoming data
|
||||||
|
_inputSubscription = _port!.inputStream?.listen(
|
||||||
|
(data) => _service.feedRawBytes(data),
|
||||||
|
onError: (error) {
|
||||||
|
debugPrint('❌ [USB] Read error: $error');
|
||||||
|
disconnect();
|
||||||
|
},
|
||||||
|
onDone: () {
|
||||||
|
debugPrint('⚠️ [USB] Port closed');
|
||||||
|
disconnect();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
debugPrint(
|
||||||
|
'✅ [USB] Connected to ${device.productName} at $_baudRate baud',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initialize MeshCore session
|
||||||
|
return _service.markConnected();
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('❌ [USB] Connection failed: $e');
|
||||||
|
await disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disconnect from the USB device.
|
||||||
|
Future<void> disconnect() async {
|
||||||
|
_service.markDisconnected();
|
||||||
|
_service.writeRaw = null;
|
||||||
|
await _inputSubscription?.cancel();
|
||||||
|
_inputSubscription = null;
|
||||||
|
await _port?.close();
|
||||||
|
_port = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _write(Uint8List data) async {
|
||||||
|
if (_port == null) throw Exception('USB not connected');
|
||||||
|
await _port!.write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
22
lib/services/serial/web_serial_transport.dart
Normal file
22
lib/services/serial/web_serial_transport.dart
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:meshcore_client/meshcore_client.dart';
|
||||||
|
|
||||||
|
// Web Serial API is only available on web platform.
|
||||||
|
// This file provides the interface; the actual JS interop is conditional.
|
||||||
|
|
||||||
|
/// Web Serial transport stub for non-web platforms.
|
||||||
|
/// On web, use [WebSerialTransportImpl] from web_serial_transport_web.dart.
|
||||||
|
class WebSerialTransport {
|
||||||
|
final MeshCoreSerialService service;
|
||||||
|
|
||||||
|
WebSerialTransport(this.service);
|
||||||
|
|
||||||
|
bool get isConnected => false;
|
||||||
|
bool get isSupported => false;
|
||||||
|
|
||||||
|
Future<bool> requestAndConnect() async => false;
|
||||||
|
Future<void> disconnect() async {}
|
||||||
|
void dispose() {}
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:meshcore_client/meshcore_client.dart' hide Contact;
|
||||||
|
import 'package:usb_serial/usb_serial.dart';
|
||||||
import '../providers/connection_provider.dart';
|
import '../providers/connection_provider.dart';
|
||||||
import '../providers/app_provider.dart';
|
import '../providers/app_provider.dart';
|
||||||
import '../services/network_scanner_service.dart';
|
import '../services/network_scanner_service.dart';
|
||||||
@@ -55,7 +58,7 @@ class _ConnectionDialogState extends State<ConnectionDialog>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_tabController = TabController(length: 2, vsync: this);
|
_tabController = TabController(length: 3, vsync: this);
|
||||||
_connectionProvider = Provider.of<ConnectionProvider>(
|
_connectionProvider = Provider.of<ConnectionProvider>(
|
||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
@@ -178,8 +181,9 @@ class _ConnectionDialogState extends State<ConnectionDialog>
|
|||||||
TabBar(
|
TabBar(
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
tabs: const [
|
tabs: const [
|
||||||
Tab(text: 'BLE Devices', icon: Icon(Icons.bluetooth)),
|
Tab(text: 'BLE', icon: Icon(Icons.bluetooth)),
|
||||||
Tab(text: 'Network Servers', icon: Icon(Icons.wifi)),
|
Tab(text: 'Network', icon: Icon(Icons.wifi)),
|
||||||
|
Tab(text: 'USB', icon: Icon(Icons.usb)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -196,6 +200,9 @@ class _ConnectionDialogState extends State<ConnectionDialog>
|
|||||||
|
|
||||||
// Network Servers Tab
|
// Network Servers Tab
|
||||||
_buildNetworkServersTab(),
|
_buildNetworkServersTab(),
|
||||||
|
|
||||||
|
// USB Serial Tab
|
||||||
|
_buildUsbTab(connectionProvider),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -630,4 +637,203 @@ class _ConnectionDialogState extends State<ConnectionDialog>
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildUsbTab(ConnectionProvider connectionProvider) {
|
||||||
|
return _UsbDeviceList(
|
||||||
|
onConnected: () {
|
||||||
|
if (mounted) Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _UsbDeviceList extends StatefulWidget {
|
||||||
|
final VoidCallback onConnected;
|
||||||
|
|
||||||
|
const _UsbDeviceList({required this.onConnected});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_UsbDeviceList> createState() => _UsbDeviceListState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _UsbDeviceListState extends State<_UsbDeviceList> {
|
||||||
|
List<UsbDevice> _devices = [];
|
||||||
|
bool _isScanning = false;
|
||||||
|
bool _isConnecting = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||||
|
_scanDevices();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _scanDevices() async {
|
||||||
|
setState(() => _isScanning = true);
|
||||||
|
try {
|
||||||
|
final devices = await UsbSerial.listDevices();
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_devices = devices;
|
||||||
|
_isScanning = false;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_devices = [];
|
||||||
|
_isScanning = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _connectToDevice(UsbDevice device) async {
|
||||||
|
setState(() => _isConnecting = true);
|
||||||
|
try {
|
||||||
|
final connectionProvider = context.read<ConnectionProvider>();
|
||||||
|
final service = MeshCoreSerialService(appName: 'MeshCore SAR');
|
||||||
|
|
||||||
|
final port = await device.create();
|
||||||
|
if (port == null) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => _isConnecting = false);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Failed to create USB port')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final opened = await port.open();
|
||||||
|
if (!opened) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => _isConnecting = false);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Failed to open USB port')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await port.setDTR(true);
|
||||||
|
await port.setRTS(true);
|
||||||
|
await port.setPortParameters(
|
||||||
|
115200,
|
||||||
|
UsbPort.DATABITS_8,
|
||||||
|
UsbPort.STOPBITS_1,
|
||||||
|
UsbPort.PARITY_NONE,
|
||||||
|
);
|
||||||
|
|
||||||
|
service.writeRaw = (data) async {
|
||||||
|
await port.write(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
port.inputStream?.listen(
|
||||||
|
(data) => service.feedRawBytes(data),
|
||||||
|
onError: (_) {
|
||||||
|
service.markDisconnected();
|
||||||
|
port.close();
|
||||||
|
},
|
||||||
|
onDone: () {
|
||||||
|
service.markDisconnected();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final sessionOk = await service.markConnected();
|
||||||
|
if (!sessionOk) {
|
||||||
|
await port.close();
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => _isConnecting = false);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('USB session initialization failed')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final success = await connectionProvider.connectSerial(service);
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
widget.onConnected();
|
||||||
|
} else {
|
||||||
|
await port.close();
|
||||||
|
setState(() => _isConnecting = false);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Failed to connect via USB')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _isConnecting = false);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('USB error: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (defaultTargetPlatform != TargetPlatform.android) {
|
||||||
|
return Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: Text(
|
||||||
|
kIsWeb
|
||||||
|
? 'Web Serial is not yet supported.\nUse BLE or Network instead.'
|
||||||
|
: 'USB serial is available on Android only.\nConnect via OTG cable.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isScanning) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: OutlinedButton.icon(
|
||||||
|
onPressed: _isConnecting ? null : _scanDevices,
|
||||||
|
icon: const Icon(Icons.refresh),
|
||||||
|
label: const Text('Scan USB devices'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_devices.isEmpty)
|
||||||
|
const Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'No USB serial devices found.\nConnect a MeshCore device via OTG cable.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: _devices.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final device = _devices[index];
|
||||||
|
return ListTile(
|
||||||
|
leading: const Icon(Icons.usb),
|
||||||
|
title: Text(device.productName ?? 'USB Device'),
|
||||||
|
subtitle: Text(device.manufacturerName ?? ''),
|
||||||
|
trailing: _isConnecting
|
||||||
|
? const SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
)
|
||||||
|
: const Icon(Icons.chevron_right),
|
||||||
|
onTap: _isConnecting ? null : () => _connectToDevice(device),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
pubspec.lock
10
pubspec.lock
@@ -788,7 +788,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: main
|
ref: main
|
||||||
resolved-ref: b36f726c3e29fd2dd992abffa2b575d4058d721f
|
resolved-ref: abff892fac0dd0e8004bfa1f415df74b44b8eb52
|
||||||
url: "https://github.com/dz0ny/meshcore_client.git"
|
url: "https://github.com/dz0ny/meshcore_client.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.1.0"
|
version: "0.1.0"
|
||||||
@@ -1405,6 +1405,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.5"
|
version: "3.1.5"
|
||||||
|
usb_serial:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: usb_serial
|
||||||
|
sha256: a605a600e34e7f28d4e80851ca3999ef747e42e406138887b8a88b8c382a8b07
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.2"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ dependencies:
|
|||||||
|
|
||||||
# Network Service Discovery (Bonjour/mDNS)
|
# Network Service Discovery (Bonjour/mDNS)
|
||||||
nsd: ^4.0.3
|
nsd: ^4.0.3
|
||||||
|
usb_serial: ^0.5.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user