Meshtastic Flutter

A comprehensive Flutter package for communicating with Meshtastic devices over Bluetooth Low Energy (BLE).

Features

  • Complete BLE Integration: Connect to Meshtastic devices using the official Meshtastic Bluetooth service
  • Full Protocol Support: Implements the complete Meshtastic protocol including configuration, messaging, and telemetry
  • Node Management: Track and manage all nodes in the mesh network
  • Real-time Updates: Stream-based API for real-time packet and node updates
  • Type-safe: Fully typed API with comprehensive wrapper classes
  • Cross-platform: Works on both Android and iOS

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  meshtastic_flutter: ^0.0.1

Complete the setup for permission_handler plugin as given here

Permissions

Android

Add these permissions to your android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

iOS

Add these to your ios/Runner/Info.plist:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app needs Bluetooth to connect to Meshtastic devices</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app needs Bluetooth to connect to Meshtastic devices</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location for Bluetooth scanning</string>

Quick Start

import 'package:meshtastic_flutter/meshtastic_flutter.dart';

void main() async {
  // Create and initialize the client
  final client = MeshtasticClient();
  await client.initialize();

  // Listen for connection changes
  client.connectionStream.listen((status) {
    print('Connection: ${status.state}');
  });

  // Listen for incoming messages
  client.packetStream.listen((packet) {
    if (packet.isTextMessage) {
      print('Message from ${packet.from.toRadixString(16)}: ${packet.textMessage}');
    }
  });

  // Scan and connect to a device
  await for (final device in client.scanForDevices()) {
    await client.connectToDevice(device);
    break; // Connect to first device found
  }

  // Send a message
  await client.sendTextMessage('Hello Mesh!');

  // Send position
  await client.sendPosition(37.7749, -122.4194);
}

API Documentation

MeshtasticClient

The main client class for interacting with Meshtastic devices.

Methods

  • initialize() - Initialize the client and request permissions
  • scanForDevices() - Stream of nearby Meshtastic devices
  • connectToDevice(BluetoothDevice) - Connect to a specific device
  • disconnect() - Disconnect from current device
  • sendTextMessage(String, {int? destinationId, int channel}) - Send text message
  • sendPosition(double lat, double lon, {int? altitude}) - Send position update

Properties

  • connectionStream - Stream of connection status changes
  • packetStream - Stream of incoming mesh packets
  • nodeStream - Stream of node information updates
  • nodes - Map of all known nodes
  • myNodeInfo - Information about the local node
  • config - Device configuration
  • isConnected - Whether connected to a device
  • isConfigured - Whether initial configuration is complete

MeshPacketWrapper

Wrapper class for MeshPacket with convenience methods.

Properties

  • from / to - Sender and destination node IDs
  • channel - Channel number
  • isTextMessage / isTelemetry / isPosition - Packet type checks
  • textMessage - Text content (if text message)
  • isEncrypted / isDecoded - Encryption status
  • packetTypeDescription - Human-readable packet type

NodeInfoWrapper

Wrapper class for NodeInfo with enhanced functionality.

Properties

  • displayName - Best available name for the node
  • longName / shortName - User names
  • latitude / longitude / altitude - Position data
  • batteryLevel / voltage - Power information
  • lastHeard - Last contact timestamp
  • isOnline - Whether node is currently reachable
  • statusDescription - Summary status string

ConnectionStatus

Information about the current connection state.

Properties

  • state - Current connection state (disconnected, connecting, configuring, connected, error)
  • deviceAddress / deviceName - Connected device information
  • errorMessage - Error description (if any)
  • timestamp - When status changed

Usage Examples

Basic Messaging

final client = MeshtasticClient();
await client.initialize();

// Connect to first device found
await for (final device in client.scanForDevices()) {
  await client.connectToDevice(device);
  break;
}

// Send broadcast message
await client.sendTextMessage('Hello everyone!');

// Send direct message to specific node
await client.sendTextMessage('Private message', destinationId: 0x12345678);

// Send on specific channel
await client.sendTextMessage('Channel message', channel: 1);

Node Monitoring

// Listen for node updates
client.nodeStream.listen((node) {
  print('Node ${node.displayName}:');
  print('  Battery: ${node.batteryLevel}%');
  print('  Last heard: ${node.lastHeard}');
  print('  Position: ${node.latitude}, ${node.longitude}');
  print('  Distance: ${myNode.distanceFrom(node)}m');
});

// Get all nodes
for (final node in client.nodes.values) {
  print('${node.displayName} - ${node.statusDescription}');
}

Configuration Access

final config = client.config;
if (config != null) {
  print('Device role: ${config.deviceRole}');
  print('LoRa region: ${config.region}');
  print('Channels: ${config.channels.length}');
  print('Primary channel: ${config.primaryChannel?.settings.name}');
}

Error Handling

try {
  await client.connectToDevice(device);
} on BluetoothException catch (e) {
  print('Bluetooth error: $e');
} on ConnectionException catch (e) {
  print('Connection error: $e');
} on PermissionException catch (e) {
  print('Permission error: $e');
} catch (e) {
  print('Unexpected error: $e');
}

Protocol Implementation

This package implements the complete Meshtastic Bluetooth protocol:

BLE Service Details

  • Service UUID: 6ba1b218-15a8-461f-9fa8-5dcae273eafd
  • ToRadio: f75c76d2-129e-4dad-a1dd-7866124401e7 (write commands/packets)
  • FromRadio: 2c55e69e-4993-11ed-b878-0242ac120002 (read responses)
  • FromNum: ed9da18c-a800-4f66-a670-aa7547e34453 (notifications for new data)

Configuration Flow

  1. Connect to device and discover services
  2. Set MTU to 512 bytes
  3. Enable notifications on FromNum characteristic
  4. Send wantConfigId to start configuration download
  5. Read configuration data until configCompleteId received
  6. Continue reading for ongoing mesh packets

Supported Message Types

  • Text messages (TEXT_MESSAGE_APP)
  • Position updates (POSITION_APP)
  • Node information (NODEINFO_APP)
  • Telemetry data (TELEMETRY_APP)
  • Administrative commands (ADMIN_APP)
  • Routing information (ROUTING_APP)

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.

License

This project is licensed under the GPL-3.0 License - see the LICENSE file for details.

Libraries

generated/admin.pb
generated/admin.pbenum
generated/admin.pbjson
generated/apponly.pb
generated/apponly.pbenum
generated/apponly.pbjson
generated/atak.pb
generated/atak.pbenum
generated/atak.pbjson
generated/cannedmessages.pb
generated/cannedmessages.pbenum
generated/cannedmessages.pbjson
generated/channel.pb
generated/channel.pbenum
generated/channel.pbjson
generated/clientonly.pb
generated/clientonly.pbenum
generated/clientonly.pbjson
generated/config.pb
generated/config.pbenum
generated/config.pbjson
generated/connection_status.pb
generated/connection_status.pbenum
generated/connection_status.pbjson
generated/device_ui.pb
generated/device_ui.pbenum
generated/device_ui.pbjson
generated/deviceonly.pb
generated/deviceonly.pbenum
generated/deviceonly.pbjson
generated/interdevice.pb
generated/interdevice.pbenum
generated/interdevice.pbjson
generated/localonly.pb
generated/localonly.pbenum
generated/localonly.pbjson
generated/mesh.pb
generated/mesh.pbenum
generated/mesh.pbjson
generated/module_config.pb
generated/module_config.pbenum
generated/module_config.pbjson
generated/mqtt.pb
generated/mqtt.pbenum
generated/mqtt.pbjson
generated/paxcount.pb
generated/paxcount.pbenum
generated/paxcount.pbjson
generated/portnums.pb
generated/portnums.pbenum
generated/portnums.pbjson
generated/powermon.pb
generated/powermon.pbenum
generated/powermon.pbjson
generated/remote_hardware.pb
generated/remote_hardware.pbenum
generated/remote_hardware.pbjson
generated/rtttl.pb
generated/rtttl.pbenum
generated/rtttl.pbjson
generated/storeforward.pb
generated/storeforward.pbenum
generated/storeforward.pbjson
generated/telemetry.pb
generated/telemetry.pbenum
generated/telemetry.pbjson
generated/xmodem.pb
generated/xmodem.pbenum
generated/xmodem.pbjson
meshtastic_flutter