# CLAUDE.md - MeshCore SAR Technical Reference This document provides technical details for AI assistants (like Claude) working with this codebase. ## Project Overview **Type**: Flutter Mobile Application **Purpose**: Search and Rescue (SAR) operations with MeshCore mesh network devices **Architecture**: Provider-based state management with BLE communication **Target Platforms**: iOS 13+, Android 5.0+ (API 21+) ## Project Structure ``` lib/ ├── models/ # Data models │ ├── contact.dart # Contact with telemetry │ ├── contact_telemetry.dart # GPS, battery, temperature │ ├── message.dart # Messages and SAR markers │ ├── sar_marker.dart # SAR tactical markers │ ├── device_info.dart # BLE device connection state │ └── map_layer.dart # Map tile layer definitions ├── services/ # Business logic services │ ├── meshcore_ble_service.dart # BLE communication │ ├── meshcore_constants.dart # Protocol constants │ ├── buffer_reader.dart # Binary protocol reader │ ├── buffer_writer.dart # Binary protocol writer │ ├── cayenne_lpp_parser.dart # Telemetry decoder │ └── tile_cache_service.dart # Offline map tiles ├── providers/ # State management │ ├── connection_provider.dart # BLE connection state │ ├── contacts_provider.dart # Contact list management │ ├── messages_provider.dart # Message history + SAR markers │ ├── map_provider.dart # Map navigation state │ └── app_provider.dart # Coordinator provider ├── screens/ # UI screens │ ├── home_screen.dart # Main screen with tabs │ ├── messages_tab.dart # Message list view │ ├── contacts_tab.dart # Contact list view │ └── map_tab.dart # Interactive map view ├── widgets/ # Reusable UI components │ └── map_markers.dart # Custom map marker widgets ├── utils/ # Utilities │ └── sar_message_parser.dart # Parse S::lat,lon format └── main.dart # App entry point ``` ## Key Technologies ### Core Dependencies - **flutter_blue_plus** (^2.0.0): BLE communication - **flutter_map** (^8.2.2): Interactive mapping with OpenStreetMap - **flutter_map_tile_caching** (^10.1.1): Offline map tile storage - **provider** (^6.1.0): State management - **latlong2** (^0.9.0): GPS coordinate handling - **geolocator** (^14.0.2): Precise GPS location tracking - **permission_handler** (^12.0.1): Runtime permissions ### MeshCore Protocol The app implements the MeshCore BLE protocol based on https://github.com/meshcore-dev/meshcore.js **BLE Service**: `6E400001-B5A3-F393-E0A9-E50E24DCCA9E` **RX Characteristic** (write): `6E400002-B5A3-F393-E0A9-E50E24DCCA9E` **TX Characteristic** (notify): `6E400003-B5A3-F393-E0A9-E50E24DCCA9E` #### Command Codes (Client → Device) - `4`: Get contacts list - `2`: Send text message - `39`: Request telemetry #### Response Codes (Device → Client) - `3`: Contact information - `7`: Message received - `0x8B` (139): Telemetry response (Cayenne LPP format) #### Binary Protocol Format All protocol messages use little-endian byte order. **Get Contacts Request**: ``` [0x04] - Command code ``` **Contact Response**: ``` [0x03] - Response code [32 bytes] - Public key [1 byte] - Contact type (0=none, 1=chat, 2=repeater, 3=room) [64 bytes] - Advertised name (null-terminated string) [4 bytes] - Latitude (int32, divide by 10000 for degrees) [4 bytes] - Longitude (int32, divide by 10000 for degrees) ``` **Message Received**: ``` [0x07] - Response code [1 byte] - Message type (0=contact, 1=channel) [4 bytes] - Sender public key prefix [4 bytes] - Recipient public key prefix [2 bytes] - Text length (uint16) [N bytes] - UTF-8 text ``` **Send Text Message**: ``` [0x02] - Command code [32 bytes] - Recipient public key [2 bytes] - Text length (uint16) [N bytes] - UTF-8 text ``` **Request Telemetry**: ``` [0x27] (39) - Command code [32 bytes] - Contact public key ``` **Telemetry Response**: ``` [0x8B] (139) - Response code [4 bytes] - Contact public key prefix [N bytes] - Cayenne LPP payload ``` ### Cayenne LPP Format Telemetry data uses Cayenne Low Power Payload format: ``` [Channel] [Type] [Data...] ``` **Supported Types**: - `136` (0x88): GPS Location - 4 bytes: Latitude (int32, divide by 10000) - 4 bytes: Longitude (int32, divide by 10000) - 4 bytes: Altitude (int32, divide by 100) - `103` (0x67): Temperature Sensor - 2 bytes: Temperature (int16, divide by 10 for °C) - `2` (0x02): Analog Input (used for battery voltage) - 2 bytes: Value (uint16, divide by 100 for volts) ### SAR Message Format Special tactical markers embedded in messages: ``` S::, ``` **Recognized Emojis**: - `🧑` or `👤`: Found Person - `🔥`: Fire Location - `🏕️` or `⛺`: Staging Area **Examples**: - `S:🧑:46.0569,14.5058` - Person found at coordinates - `S:🔥:46.0570,14.5060` - Fire detected - `S:🏕️:46.0571,14.5062` - Base camp location **Parsing Rules**: - Must start with `S:` - Single emoji character after first colon - Comma-separated lat,lon after second colon - Coordinates can be negative (e.g., `-12.3456`) - No spaces allowed in format ## State Management Architecture ### Provider Hierarchy ``` MultiProvider ├── ConnectionProvider # BLE connection state ├── ContactsProvider # Contact list ├── MessagesProvider # Messages + SAR markers ├── MapProvider # Map navigation └── AppProvider # Coordinator (uses all above) ``` ### Event Flow ``` BLE Device → MeshCoreBleService → ConnectionProvider → AppProvider ↓ ContactsProvider MessagesProvider ↓ UI ``` **Example: Receiving a Message** 1. BLE device sends message via TX characteristic 2. `MeshCoreBleService._onDataReceived()` parses binary data 3. Calls `onMessageReceived` callback 4. `ConnectionProvider` receives message 5. `AppProvider` enhances message (check for SAR format) 6. `MessagesProvider.addMessage()` stores message 7. UI rebuilds via `Consumer` ### Contact Types ```dart enum ContactType { none(0), // Unknown/invalid chat(1), // Team member (shown on map) repeater(2), // Network repeater node room(3), // Communication channel/room } ``` **Map Display Rules**: - Only `ContactType.chat` contacts with valid GPS are shown on map - Repeaters and rooms are listed in Contacts tab but not mapped ## Map Implementation ### Tile Layers Three tile sources are supported via `MapLayer` enum: 1. **OpenStreetMap** (default) - URL: `https://tile.openstreetmap.org/{z}/{x}/{y}.png` - Max zoom: 19 - Best for street-level navigation 2. **OpenTopoMap** - URL: `https://a.tile.opentopomap.org/{z}/{x}/{y}.png` - Max zoom: 17 - Shows topographic features, elevation contours 3. **ESRI World Imagery** - URL: `https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}` - Max zoom: 19 - Satellite imagery ### Offline Tile Caching Uses `flutter_map_tile_caching` with ObjectBox backend: ```dart // Initialize cache await FMTCObjectBoxBackend().initialise(); final store = FMTCStore('meshcore_sar_tiles'); await store.manage.create(); // Download region final region = RectangleRegion(bounds); await store.download.startForeground(region: region); ``` **Cache Behavior**: - `CacheBehavior.cacheFirst`: Use cached tiles if available - 30-day validity period - Automatic background updates when online ### Map Markers **Team Member Markers** (Blue): - CircleAvatar with person icon - Battery percentage badge at top - Name label at bottom - Tap to show details dialog **SAR Event Markers** (Color-coded): - Found Person: Green with 🧑 - Fire: Red with 🔥 - Staging Area: Orange with 🏕️ - Time ago label at top - Type label at bottom - Tap to show details dialog ### Map Navigation **Navigation from Messages Tab**: 1. User taps SAR marker message 2. `MapProvider.navigateToLocation()` called 3. Target location and zoom stored in provider 4. Tab switches to Map 5. `MapTab._handleMapNavigation()` moves map 6. `MapProvider.clearNavigation()` resets state **Zoom State Preservation**: - Current zoom stored in `MapProvider` - Maintained across tab switches - Updated on user zoom gestures ### User Location Tracking The app tracks the user's precise GPS location in real-time: **Permission Setup** (iOS Info.plist): ```xml NSLocationWhenInUseUsageDescription MeshCore SAR needs location access to display team members and SAR markers on the map NSLocationTemporaryPreciseUsageDescription MeshCore SAR needs precise location for accurate positioning in SAR operations NSLocationDefaultAccuracyReduced ``` **Implementation** (lib/screens/map_tab.dart): ```dart Position? _currentPosition; bool _trackingLocation = false; // Request permission and start tracking final position = await Geolocator.getCurrentPosition( locationSettings: const LocationSettings( accuracy: LocationAccuracy.best, distanceFilter: 0, ), ); // Listen to continuous position updates Geolocator.getPositionStream( locationSettings: const LocationSettings( accuracy: LocationAccuracy.best, distanceFilter: 10, // Update every 10 meters ), ).listen((Position position) { setState(() => _currentPosition = position); if (_trackingLocation) { // Auto-center map on user location _mapController.move( LatLng(position.latitude, position.longitude), _mapController.camera.zoom ); } }); ``` **User Location Marker**: - Blue pulsing circle showing current position - Navigation icon indicating heading - Tap location button to center map on user - Tap again to enable tracking mode (map follows user movement) ### Map Legend **Collapsible Legend** (lib/screens/map_tab.dart): - Shows counts of team members and SAR markers - Click to collapse to compact view - Click again to expand - Positioned in top-right corner ```dart bool _showLegend = true; GestureDetector( onTap: () => setState(() => _showLegend = !_showLegend), child: _showLegend ? _MapLegend(/* full legend with all counts */) : Card( child: Column([ Text('Legend'), Icon(Icons.expand_more), ]), ), ) ``` ### Detailed Compass Dialog **Location Display** (lib/screens/map_tab.dart): - Ultra-compact location format display with tap-to-toggle formats - Tap location text to switch between DD and DMS formats - No labels - just the coordinates for maximum space efficiency - Two formats available: - DD (Decimal Degrees): 5 decimal places (e.g., "46.05690, 14.50580") - DMS (Degrees, Minutes, Seconds): Traditional format (e.g., "46°03'24.84"N, 14°30'20.88"E") - Monospace font for coordinate values **Dialog Controls**: - Tap anywhere outside interactive elements to close dialog - Tap location coordinates to toggle format - No close button - cleaner, more compact interface ```dart class _LocationFormatToggle extends StatefulWidget { bool _showDMS = false; @override Widget build(BuildContext context) { final displayText = _showDMS ? 'DMS format with newline' : 'DD format single line'; return GestureDetector( onTap: () => setState(() => _showDMS = !_showDMS), behavior: HitTestBehavior.opaque, child: Container(/* compact display */), ); } } ``` ## Building and Development ### Development Commands ```bash # Install dependencies flutter pub get # Run in debug mode flutter run # Run with specific device flutter run -d # Hot reload (during debug) # Press 'r' in terminal # Hot restart (during debug) # Press 'R' in terminal # Analyze code flutter analyze # Run tests flutter test # Format code dart format lib/ # Clean build flutter clean ``` ### iOS Build ```bash # Open Xcode workspace open ios/Runner.xcworkspace # Build from command line flutter build ios --release # Create IPA (requires signing) flutter build ipa ``` **Key iOS Files**: - `ios/Runner/Info.plist`: Permissions and app configuration - `ios/Podfile`: CocoaPods dependencies - `ios/Runner.xcodeproj`: Xcode project ### Android Build ```bash # Debug APK flutter build apk --debug # Release APK flutter build apk --release # App Bundle (for Play Store) flutter build appbundle --release # Split APKs by ABI flutter build apk --split-per-abi ``` **Key Android Files**: - `android/app/src/main/AndroidManifest.xml`: Permissions and app configuration - `android/app/build.gradle`: App-level build configuration - `android/build.gradle`: Project-level build configuration ## Common Development Tasks ### Adding a New BLE Command 1. **Add command code** to `lib/services/meshcore_constants.dart`: ```dart static const int cmdYourCommand = 42; ``` 2. **Create command method** in `lib/services/meshcore_ble_service.dart`: ```dart Future yourCommand() async { final writer = BufferWriter(); writer.writeByte(MeshCoreConstants.cmdYourCommand); await _sendCommand(writer.toBytes()); } ``` 3. **Handle response** in `_onDataReceived()`: ```dart case MeshCoreConstants.respYourResponse: // Parse response data onYourCallback?.call(data); break; ``` ### Adding a New SAR Marker Type 1. **Update enum** in `lib/models/sar_marker.dart`: ```dart enum SarMarkerType { // existing types... yourType('🆕', 'Your Type'); } ``` 2. **Add to parser** in `lib/utils/sar_message_parser.dart`: ```dart case '🆕': return SarMarkerType.yourType; ``` 3. **Add color** in `lib/widgets/map_markers.dart`: ```dart case SarMarkerType.yourType: return Colors.purple; ``` 4. **Update providers** in `lib/providers/messages_provider.dart`: ```dart List get yourTypeMarkers => sarMarkers.where((m) => m.type == SarMarkerType.yourType).toList(); ``` ### Adding a New Map Layer 1. **Add to model** in `lib/models/map_layer.dart`: ```dart static const yourLayer = MapLayer( type: MapLayerType.yourLayer, name: 'Your Layer', urlTemplate: 'https://your-tile-server/{z}/{x}/{y}.png', attribution: '© Your Attribution', maxZoom: 19, ); ``` 2. **Add to list**: ```dart static const List allLayers = [ openStreetMap, openTopoMap, esriWorldImagery, yourLayer, // Add here ]; ``` 3. Layer automatically appears in layer selector UI ## Testing ### Unit Tests ```bash # Run all tests flutter test # Run specific test file flutter test test/widget_test.dart # Run with coverage flutter test --coverage ``` ### Integration Tests ```bash # Run integration tests flutter drive --target=test_driver/app.dart ``` ### Manual Testing Checklist **BLE Connection**: - [ ] Scan discovers MeshCore devices - [ ] Connection successful - [ ] Device info displayed in status bar - [ ] Disconnect works properly **Contacts**: - [ ] Contacts load after connection - [ ] Contacts grouped by type - [ ] Telemetry request works - [ ] Battery/GPS displayed correctly **Messages**: - [ ] Messages received and displayed - [ ] SAR markers highlighted - [ ] Tap SAR marker navigates to map - [ ] Message timestamps correct **Map**: - [ ] Map loads and displays tiles - [ ] Team member markers appear - [ ] SAR markers appear with correct colors - [ ] Layer switching works - [ ] Zoom/pan gestures work - [ ] Marker tap shows details - [ ] Offline tiles load ## Troubleshooting ### BLE Issues **"Bluetooth adapter not available"**: - Check device Bluetooth is on - Verify permissions granted - iOS: Check Info.plist has usage descriptions - Android: Check AndroidManifest.xml has permissions **"Connection failed"**: - Device must support BLE - Check service UUID matches - Verify device is in range (<10m typically) - Try scanning again ### Runtime Issues **MissingPluginException for geolocator or other plugins**: Example error: ``` MissingPluginException(No implementation found for method isLocationServiceEnabled on channel flutter.baseflow.com/geolocator_apple) ``` This occurs when native plugin implementations aren't properly installed. Common after adding new dependencies. **Solution**: ```bash # For iOS cd ios pod install cd .. # Clean and rebuild flutter clean flutter pub get flutter run ``` **If still failing on iOS**: ```bash cd ios rm Podfile.lock rm -rf Pods/ pod install cd .. flutter clean flutter pub get ``` ### Build Issues **iOS Pod Install Fails**: ```bash cd ios rm Podfile.lock rm -rf Pods/ pod install --repo-update cd .. ``` **CocoaPods ObjectBox Version Conflict**: This error occurs when flutter_map_tile_caching updates its ObjectBox dependency but the cached Podfile.lock has an older version: ``` [!] CocoaPods could not find compatible versions for pod "ObjectBox": In snapshot (Podfile.lock): ObjectBox (= 1.9.2) In Podfile: objectbox_flutter_libs depends on ObjectBox (= 4.4.1) ``` **Solution**: ```bash # Navigate to iOS directory cd ios # Remove cached dependency lock file rm Podfile.lock # Remove all installed pods rm -rf Pods/ # Update CocoaPods repository (this may take a few minutes) pod repo update # Reinstall all pods with updated versions pod install # Return to project root cd .. # Clean Flutter build cache flutter clean # Reinstall Flutter dependencies flutter pub get # Run the app flutter run ``` **Alternative solution** (if the above doesn't work): ```bash cd ios rm Podfile.lock rm -rf Pods/ pod deintegrate pod cache clean --all pod setup pod install cd .. flutter clean flutter pub get ``` **Note**: The `pod repo update` command can take 5-10 minutes as it downloads the entire CocoaPods specifications repository. This is normal. **Android Gradle Timeout**: ```gradle // android/gradle.properties org.gradle.daemon=true org.gradle.parallel=true org.gradle.jvmargs=-Xmx4096m ``` **Flutter Version Conflicts**: ```bash flutter channel stable flutter upgrade flutter pub upgrade ``` ## Performance Optimization ### BLE Communication - Buffer incoming data to handle partial packets - Throttle telemetry requests (max 1 per second per contact) - Use `notifyListeners()` sparingly in providers ### Map Performance - Limit visible markers (cluster if >100 markers) - Use `repaint boundary` for marker widgets - Implement marker virtualization for large datasets ### Memory Management - Dispose controllers in `dispose()` methods - Clear message history after 1000 messages - Implement tile cache size limits ## Security Considerations - **BLE**: No authentication in current protocol - add encryption for production - **Permissions**: Request minimum required permissions - **Data**: No sensitive data should be logged - **Network**: Use HTTPS for all tile sources ## Future Enhancements Potential features to add: 1. **Message Sending**: UI to compose and send messages 2. **Route Recording**: Track team member paths over time 3. **Geofencing**: Alerts when team members enter/exit areas 4. **Voice Notes**: Attach audio to SAR markers 5. **Team Chat**: Real-time chat between team members 6. **Mission Plans**: Pre-loaded search patterns 7. **Statistics**: Coverage analysis, search time tracking ## References - [Flutter Documentation](https://docs.flutter.dev/) - [flutter_blue_plus API](https://pub.dev/documentation/flutter_blue_plus/) - [flutter_map Documentation](https://docs.fleaflet.dev/) - [MeshCore Protocol](https://github.com/meshcore-dev/meshcore.js) - [Cayenne LPP Specification](https://developers.mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload) - [Provider Package](https://pub.dev/packages/provider) ## Contact For questions or contributions, please refer to the project repository or contact the development team.