Files
meshcore-sar_android/CLAUDE.md
Janez T 021ce21cbe Refactor SAR marker handling and add template management
- Updated CompassSarList and DetailedCompassDialog to use marker.displayName instead of marker.type.displayName.
- Enhanced DrawingLayer and DrawingMarkersLayer to support simple mode for drawing visibility and interaction.
- Added toggle switches in DrawingToolbar for showing/hiding received drawings and SAR markers.
- Modified MapMarkers to utilize custom emojis and display names for markers.
- Introduced RecipientSelectorSheet for selecting message recipients with search functionality.
- Refactored SarUpdateSheet to use SAR templates instead of marker types, allowing for emoji and name customization.
- Created SarTemplateEditDialog for adding and editing SAR templates with color selection and preview.
2025-10-21 23:44:49 +02:00

18 KiB

CLAUDE.md - MeshCore SAR Technical Reference

AI assistant guide for the MeshCore SAR Flutter application.

Table of Contents

  1. Critical Development Rules
  2. Quick Reference
  3. Project Structure
  4. Protocol Reference
  5. Architecture
  6. Common Tasks
  7. Build & Troubleshooting

Critical Development Rules

Flutter Process Management

NEVER run or kill Flutter processes:

  • DO NOT execute flutter run command
  • DO NOT kill Flutter processes (pkill flutter, killall flutter)
  • User manages Flutter development server - only make code changes
  • Hot reload happens automatically when files are saved

Key Architecture Constraints

  • Echo Detection: Uses DJB2-style hash (NO crypto dependency needed)
  • Channel Messages: NO ACKs (fire-and-forget flood routing)
  • Direct Messages: Automatic ACKs via PUSH_CODE_SEND_CONFIRMED
  • SAR Markers: MUST be sent to rooms, NOT public channel

Quick Reference

Project: Flutter Mobile App (iOS 13+, Android API 21+) Architecture: Provider pattern + BLE communication Protocol: MeshCore BLE Companion Radio (Little Endian) Repository: https://github.com/meshcore-dev/meshcore.js

BLE UUIDs

Service: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
RX (write): 6E400002-B5A3-F393-E0A9-E50E24DCCA9E
TX (notify): 6E400003-B5A3-F393-E0A9-E50E24DCCA9E

Key Dependencies

flutter_blue_plus: ^2.0.0  # BLE communication
flutter_map: ^8.2.2         # Mapping
provider: ^6.1.0            # State management
geolocator: ^14.0.2         # GPS tracking

Project Structure

lib/
├── l10n/                   # Internationalization (en, hr, sl)
│   ├── app_localizations.dart  # Generated (DO NOT EDIT)
│   └── app_*.arb              # Translation files
├── models/                 # Data models
│   ├── contact.dart, message.dart, sar_marker.dart
│   ├── map_drawing.dart       # Drawing shapes
│   └── sent_message_tracker.dart  # Echo detection
├── services/               # Business logic
│   ├── meshcore_ble_service.dart  # BLE coordinator
│   ├── protocol/              # Frame parsing & building
│   ├── ble/                   # Connection, commands, responses
│   ├── location_tracking_service.dart  # GPS + broadcast
│   ├── map_marker_service.dart    # Marker generation
│   └── validation_service.dart    # Form validation
├── providers/              # State management
│   ├── connection_provider.dart   # BLE state
│   ├── contacts_provider.dart     # Contact list
│   ├── messages_provider.dart     # Messages + SAR
│   ├── map_provider.dart          # Map navigation
│   ├── drawing_provider.dart      # Map drawings
│   └── app_provider.dart          # Coordinator
├── screens/                # UI screens
│   └── (home, messages, contacts, map, settings, etc.)
├── widgets/                # Reusable components
│   ├── map_markers.dart
│   └── map/                   # Map-specific widgets
└── utils/                  # Utilities
    ├── sar_message_parser.dart
    └── drawing_message_parser.dart

Protocol Reference

Message Formats

SAR Marker Format

S:<emoji>:<latitude>,<longitude>:<optional_message>

Emojis:
  🧑 or 👤  → Found Person
  🔥        → Fire Location
  🏕️ or ⛺  → Staging Area

Examples:
  S:🧑:37.7749,-122.4194
  S:🔥:40.7128,-74.0060:Large wildfire spreading rapidly

Map Drawing Format

D:<json>

Line:      D:{"t":0,"c":0,"p":[lat1,lon1,lat2,lon2]}
Rectangle: D:{"t":1,"c":1,"b":[topLat,topLon,botLat,botLon]}

Fields:
  t = type (0=line, 1=rectangle)
  c = color index (0-7: red,blue,green,yellow,orange,purple,pink,cyan)
  p = points array (flat)
  b = bounds array (rectangles only)

Cayenne LPP Format

[Channel][Type][Data...]

Types:
  0x88 (136) → GPS: lat/lon/alt (int32/10000, int32/10000, int32/100)
  0x67 (103) → Temperature (int16/10 for °C)
  0x02 (2)   → Analog Input (uint16/100 for volts/battery)

Command Quick Reference

Code Command Response Description
1 CMD_APP_START RESP_CODE_SELF_INFO (5) First command after connection
2 CMD_SEND_TXT_MSG RESP_CODE_SENT (6) Send DM to contact (with ACK)
3 CMD_SEND_CHANNEL_TXT_MSG RESP_CODE_SENT (6) Broadcast to channel (NO ACK)
4 CMD_GET_CONTACTS RESP_CODE_CONTACTS_START (2) Sync contact list
10 CMD_SYNC_NEXT_MESSAGE RESP_CODE_CONTACT_MSG_RECV (7) Pull next message from queue
22 CMD_DEVICE_QUERY RESP_CODE_DEVICE_INFO (13) Get device firmware/hardware info
26 CMD_SEND_LOGIN PUSH_CODE_LOGIN_SUCCESS (0x85) Login to room server

Push Notifications (Async)

Code Name Purpose
0x80 PUSH_CODE_ADVERT New advertisement received
0x82 PUSH_CODE_SEND_CONFIRMED Message ACK received (DMs only)
0x83 PUSH_CODE_MSG_WAITING New message in queue → sync it
0x88 PUSH_CODE_LOG_RX_DATA Raw packet capture (always-on diagnostic)
0x85 PUSH_CODE_LOGIN_SUCCESS Room login successful
0x86 PUSH_CODE_LOGIN_FAIL Room login failed

Constants

Contact Types (ADV_TYPE)

0 = ADV_TYPE_NONE      # Unknown/invalid
1 = ADV_TYPE_CHAT      # Team member (shown on map)
2 = ADV_TYPE_REPEATER  # Network repeater
3 = ADV_TYPE_ROOM      # Communication room/server

Message Types (TXT_TYPE)

0 = TXT_TYPE_PLAIN         # Plain text
1 = TXT_TYPE_CLI_DATA      # CLI command
2 = TXT_TYPE_SIGNED_PLAIN  # Text + 4-byte pubkey signature

Error Codes (ERR_CODE)

1 = ERR_CODE_UNSUPPORTED_CMD
2 = ERR_CODE_NOT_FOUND
3 = ERR_CODE_TABLE_FULL
4 = ERR_CODE_BAD_STATE
5 = ERR_CODE_FILE_IO_ERROR
6 = ERR_CODE_ILLEGAL_ARG

Architecture

Provider Hierarchy

MultiProvider
├── ConnectionProvider    # BLE connection state
├── ContactsProvider      # Contact list management
├── MessagesProvider      # Messages + SAR markers
├── MapProvider          # Map navigation state
├── DrawingProvider      # Map drawing state
└── AppProvider          # Coordinator (wires everything)

Event Flow

BLE Device → MeshCoreBleService → ConnectionProvider → AppProvider
                                         ↓
                                  ContactsProvider
                                  MessagesProvider
                                  DrawingProvider
                                         ↓
                                        UI

Contact Path Status

outPathLen indicates routing mode:

  • -1 (0xFF): Path unknown → Flood mode (broadcast to all neighbors)
  • 0: Direct connection → Direct mode (zero hops, best quality)
  • 1-64: Multi-hop path → Direct mode (uses learned routing)

Map Display: Only ContactType.chat contacts with valid GPS coordinates shown.

Channels vs. Rooms

Channels (numeric identifiers):

  • Channel 0 = "Public Channel" (default broadcast)
  • Ephemeral - messages NOT persisted
  • Uses CMD_SEND_CHANNEL_TXT_MSG (3)
  • NO ACKs - fire-and-forget flood routing
  • Pre-configured with secret: 8b3387e9c5cdea6ac9e5edbaa115cd72 (hex)

Rooms (ADV_TYPE_ROOM contacts):

  • Named contacts with public keys
  • Persistent storage on room server
  • Uses CMD_SEND_TXT_MSG (2) with room's public key
  • Automatic ACKs when messages stored
  • Login via CMD_SEND_LOGIN (26) to receive stored messages

SAR Routing: SAR markers MUST go to rooms for reliable delivery.

Room Login Protocol

1. Send CMD_SEND_LOGIN (26)
   ↓
2. Radio generates sender_timestamp & sync_since
   ↓
3. Room validates password, stores sync_since
   ↓
4. Receive PUSH_CODE_LOGIN_SUCCESS (0x85) or PUSH_CODE_LOGIN_FAIL (0x86)
   ↓
5. Room auto-pushes messages (1200ms intervals)
   ↓
6. Receive PUSH_CODE_MSG_WAITING (0x83) → call CMD_SYNC_NEXT_MESSAGE (10)

CRITICAL: Do NOT call syncAllMessages() after login. Wait for push notifications.


Echo Detection Feature

Overview

Status: Fully implemented and production-ready Purpose: Detect when broadcast messages are rebroadcast by mesh nodes

How It Works

  1. Packet Identification (via PUSH_CODE_LOG_RX_DATA 0x88):

    Packet Structure (PAYLOAD_TYPE_GRP_TXT 0x05):
    [Byte 0] = Header (route + payload type + version)
    [Byte 1] = Path length
    [Byte 2] = Sender's node hash (first byte of public key) ✅
    [Byte 3+] = Rest of path + encrypted payload
    
  2. On Connection:

    • Receive RESP_CODE_SELF_INFO with our public key
    • Extract our node hash (byte 0 of public key)
    • Store for packet matching
  3. Sending a Message:

    • Call trackSentMessage(messageId) when user sends channel message
    • Status = "pending" (waiting for packet capture)
  4. Packet Capture (50-200ms later):

    • Radio sends PUSH_CODE_LOG_RX_DATA with raw packet
    • Extract sender hash from packet[2]
    • If sender hash == our node hash → This is our packet!
    • Calculate DJB2-style hash of entire packet (no crypto dependency)
    • Store tracker by packet hash for echo detection
  5. Echo Detection:

    • Future PUSH_CODE_LOG_RX_DATA packets arrive
    • Calculate packet hash and lookup in tracker map (O(1))
    • If match found → Echo detected! Increment counter
    • Track SNR/RSSI signature for path diversity
    • UI auto-updates to show "Rebroadcast by X nodes"

Implementation Details

Files:

  • lib/models/sent_message_tracker.dart - Tracker model
  • lib/models/message.dart - echoCount, firstEchoAt fields
  • lib/services/ble/ble_response_handler.dart - Detection engine
  • lib/services/meshcore_ble_service.dart - Callback wiring
  • lib/providers/connection_provider.dart - Provider callback
  • lib/providers/messages_provider.dart - handleMessageEcho()

Performance:

  • Packet ID: O(1) - byte comparison at offset 2
  • Hash calc: O(n) where n = packet length (~38-200 bytes)
  • Echo lookup: O(1) via HashMap
  • Memory: ~150 bytes/message, max 100 messages = ~15KB
  • TTL: 5-minute expiry, auto-cleanup

Limitations:

  • Echo count ≠ exact receiver count (one node can echo multiple times)
  • Only detects echoes while app connected
  • Network topology dependent (dense networks → more echoes)

Common Tasks

Adding a New BLE Command

  1. Add code to lib/services/meshcore_constants.dart
  2. Build frame in lib/services/protocol/frame_builder.dart
  3. Add API method in lib/services/meshcore_ble_service.dart
  4. Parse response in lib/services/protocol/frame_parser.dart
  5. Handle in lib/services/ble/ble_response_handler.dart
  6. Add callback in lib/services/meshcore_ble_service.dart

Adding a New SAR Marker Type

  1. Update enum in lib/models/sar_marker.dart
  2. Add parser logic in lib/utils/sar_message_parser.dart
  3. Add color mapping in lib/widgets/map_markers.dart
  4. Update handling in lib/providers/messages_provider.dart

Adding Localized Strings

  1. Add to lib/l10n/app_en.arb:

    {
      "myNewString": "My new text",
      "@myNewString": {
        "description": "What this string is for"
      }
    }
    
  2. Add translations to app_hr.arb and app_sl.arb

  3. Generate: flutter gen-l10n

  4. Use in code:

    import '../l10n/app_localizations.dart';
    
    Text(AppLocalizations.of(context)!.myNewString)
    

IMPORTANT: Always use relative import path '../l10n/app_localizations.dart'

Importing/Exporting Map Tiles

The app supports importing and exporting cached map tiles using the flutter_map_tile_caching library's archive format (.fmtc files).

Export Workflow:

  1. Navigate to Map Management screen
  2. Tap "Export Tiles to File"
  3. Archive is created in temporary directory with gzip compression
  4. System share sheet appears (iOS/Android)
  5. Choose where to save: Files app, email, cloud storage, etc.
  6. File named: meshcore_tiles_<timestamp>.fmtc

Import Workflow:

  1. Navigate to Map Management screen
  2. Tap "Import Tiles from File"
  3. Select .fmtc archive file
  4. Tiles are merged with existing cache
  5. Cache statistics refreshed automatically

API Usage:

// Export current cache to file
final tileCount = await tileCacheService.exportStore(
  '/path/to/export.fmtc',
);

// Import tiles from archive (with merge strategy)
final result = await tileCacheService.importStore(
  '/path/to/import.fmtc',
  storeNames: null, // null = import all stores
  strategy: ImportConflictStrategy.merge, // default: merge
);

// Preview stores in archive before importing
final stores = await tileCacheService.listArchiveStores(
  '/path/to/archive.fmtc',
);

Use Cases:

  • Backup: Export tiles before clearing cache or reinstalling app
  • Sharing: Pre-download maps once, distribute to team devices
  • Disaster Recovery: Restore offline maps after device reset
  • Bandwidth Saving: Reduce cellular data usage by sharing cached tiles

Technical Details:

  • Archive format: .fmtc (FMTC native format)
  • Compression: gzip (built-in by FMTC)
  • Import strategy: Merge (tiles combined with existing cache)
  • Export location: Temporary directory → system share sheet
  • Import source: User-selected via file_picker package
  • Conflict handling: Automatic merge of tile stores
  • Cross-platform: Works on Android and iOS using native share mechanisms

File: lib/services/tile_cache_service.dart - Export/import methods UI: lib/screens/map_management_screen.dart - Import/export card

Map Drawing Workflow

User selects mode → DrawingProvider.setDrawingMode()
       ↓
User taps map → DrawingLayer captures touches
       ↓
Preview rendered → DrawingProvider.getPreviewDrawing()
       ↓
User completes → Saved to DrawingProvider._drawings
       ↓
User shares → DrawingToolbar._shareDrawingsToChannel/Room()
       ↓
BLE send → ConnectionProvider.sendChannelMessage/sendTextMessage()
       ↓
Receiver parses → DrawingMessageParser.parseDrawingMessage()
       ↓
Display → DrawingProvider.addReceivedDrawing()

Build & Troubleshooting

Build Commands

# Dependencies
flutter pub get
flutter gen-l10n          # After ARB file changes

# Development
flutter run               # Debug mode (user controls this)
# Hot reload: save file  |  Hot restart: press 'R' in terminal

# Code Quality
flutter analyze
flutter test
dart format lib/
flutter clean

# Release
flutter build ios --release
flutter build ipa
flutter build apk --release
flutter build appbundle --release

Common Issues

BLE Connection Failed:

  • Check Bluetooth enabled and permissions granted
  • Verify device in range (<10m)
  • Check service UUID matches: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E

MissingPluginException (after adding dependencies):

cd ios && pod install && cd ..
flutter clean
flutter pub get
flutter run

iOS Pod Install Fails:

cd ios
rm Podfile.lock
rm -rf Pods/
pod install --repo-update
cd ..

Android Gradle Timeout - Add to android/gradle.properties:

org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.jvmargs=-Xmx4096m

Performance Tips

BLE:

  • Buffer partial packets
  • Throttle telemetry (max 1/sec per contact)
  • Use notifyListeners() sparingly

Map:

  • Cluster markers if >100 visible
  • Use RepaintBoundary for marker widgets
  • Implement marker virtualization for large datasets

Memory:

  • Dispose controllers in dispose() methods
  • Limit message history to 1000 messages
  • Set tile cache size limits

Services Reference

LocationTrackingService (Singleton)

Purpose: GPS tracking + intelligent mesh location broadcasting

Callbacks: onPositionUpdate, onError, onBroadcastSent, onTrackingStateChanged

Thresholds:

  • Min distance: 5.0m
  • Max distance: 100.0m
  • Min time: 30s

Logic:

  • First update → broadcast immediately
  • ≥100m moved → broadcast immediately
  • ≥5m + ≥30s → broadcast

File: lib/services/location_tracking_service.dart (501 lines)

MapMarkerService (Singleton)

Purpose: Map marker generation + geodesic calculations

Features:

  • Pure functions (testable)
  • Contact markers with battery badge, distance
  • SAR markers (color-coded by type)
  • Distance/bearing calculations (Haversine)
  • "Time ago" formatting

File: lib/services/map_marker_service.dart (518 lines)

ValidationService (Singleton)

Purpose: Form validation + input parsing

Returns: Structured ValidationResult<T> or ParseResult<T>

Validates:

  • Coordinates (lat: -90 to +90, lon: -180 to +180)
  • Radio params (freq: 137-1020 MHz, bw: 7.8-500 kHz, sf: 5-12, cr: 5-8, tx: -9 to +22 dBm)
  • Text/names, zoom levels (0-19)

File: lib/services/validation_service.dart (511 lines)


Map Implementation

Tile Layers

  1. OpenStreetMap (default) - Max zoom 19
  2. OpenTopoMap - Max zoom 17, topographic
  3. ESRI World Imagery - Max zoom 19, satellite

Offline Caching

  • Backend: flutter_map_tile_caching + ObjectBox
  • Behavior: CacheBehavior.cacheFirst, 30-day validity
  • Downloads: RectangleRegion(bounds).download.startForeground()

Marker Types

Team Member: Blue circle, battery badge, name, distance, tap for details SAR Event: Color-coded (green=person, red=fire, orange=staging), time ago, tap for details

Navigation Flow

Messages tab → tap SAR marker
    ↓
MapProvider.navigateToLocation()
    ↓
Switch to Map tab
    ↓
MapTab._handleMapNavigation() → animate to location
    ↓
MapProvider.clearNavigation()

References


Security Considerations

  • BLE: No authentication in current protocol - consider encryption for production
  • Permissions: Request minimum required permissions only
  • Logging: No sensitive data in logs
  • Network: HTTPS for all tile sources
  • Raw Packets: PUSH_CODE_LOG_RX_DATA exposes all radio traffic (diagnostic feature)