mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
- Updated localization files for French, Croatian, Italian, Slovenian to include translations for German, Spanish, French, and Italian. - Added new methods for handling drawing messages sent to the public channel in multiple languages. - Enhanced the Contact model to identify public channels using a dedicated method. - Implemented echo detection for public channel messages, including tracking and reporting of echoes. - Updated BLE response handling to support echo detection and tracking of sent messages. - Modified UI components to reflect new localization strings and echo statuses.
989 lines
40 KiB
Markdown
989 lines
40 KiB
Markdown
# CLAUDE.md - MeshCore SAR Technical Reference
|
|
|
|
AI assistant guide for the MeshCore SAR Flutter application.
|
|
|
|
## ⚠️ CRITICAL: Flutter Development Rules
|
|
|
|
**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
|
|
|
|
## Quick Reference
|
|
|
|
**Project Type**: Flutter Mobile App (iOS 13+, Android API 21+)
|
|
**Architecture**: Provider-based state management + BLE communication
|
|
**Protocol**: MeshCore BLE Companion Radio (Little Endian byte order)
|
|
**Repository**: https://github.com/meshcore-dev/meshcore.js
|
|
|
|
**BLE Service 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)
|
|
- flutter_map ^8.2.2 (mapping)
|
|
- provider ^6.1.0 (state)
|
|
- geolocator ^14.0.2 (GPS)
|
|
|
|
**Note:** No crypto dependencies required - echo detection uses a simple DJB2-style hash function
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
lib/
|
|
├── l10n/ # Internationalization (i18n)
|
|
│ ├── app_localizations.dart # Generated localization class
|
|
│ ├── app_en.arb # English (default)
|
|
│ ├── app_hr.arb # Croatian (Hrvatski)
|
|
│ └── app_sl.arb # Slovenian (Slovenščina)
|
|
├── models/ # Data models
|
|
│ ├── contact.dart, message.dart, sar_marker.dart
|
|
│ ├── map_drawing.dart # MapDrawing, LineDrawing, RectangleDrawing
|
|
│ ├── device_info.dart, room_login_state.dart, map_layer.dart
|
|
├── services/ # Business logic
|
|
│ ├── meshcore_ble_service.dart # BLE coordinator (399 lines)
|
|
│ ├── protocol/ # Frame parsing & building (628 lines)
|
|
│ ├── ble/ # Connection, commands, responses (963 lines)
|
|
│ ├── location_tracking_service.dart # GPS + mesh broadcast (501 lines)
|
|
│ ├── map_marker_service.dart # Marker generation + geodesic (518 lines)
|
|
│ └── validation_service.dart # Form validation (511 lines)
|
|
├── providers/ # State management
|
|
│ ├── connection_provider.dart # BLE connection state
|
|
│ ├── contacts_provider.dart # Contact list
|
|
│ ├── messages_provider.dart # Messages + SAR markers
|
|
│ ├── map_provider.dart # Map navigation
|
|
│ ├── drawing_provider.dart # Map drawing state
|
|
│ └── app_provider.dart # Coordinator (uses all above)
|
|
├── screens/ # UI screens (home, messages, contacts, map, settings, device_config, map_management, packet_log)
|
|
├── widgets/ # Reusable components
|
|
│ ├── map_markers.dart # Map marker rendering
|
|
│ ├── map/ # Map-specific widgets
|
|
│ │ ├── drawing_layer.dart # Drawing rendering on map
|
|
│ │ ├── drawing_toolbar.dart # Drawing UI controls
|
|
│ │ └── compass/ # Compass dialog components (fully localized)
|
|
│ ├── messages/, contacts/ # Feature-specific widgets (fully localized)
|
|
└── utils/ # Utilities
|
|
├── sar_message_parser.dart # SAR marker parsing
|
|
└── drawing_message_parser.dart # Drawing message parsing
|
|
```
|
|
|
|
## MeshCore Protocol
|
|
|
|
### Frame Delimiters
|
|
- **BLE**: Single characteristic value (link layer handles integrity)
|
|
- **USB**: `>` (0x3E) outbound, `<` (0x3C) inbound, 2-byte length (LE), then frame data
|
|
- **All uint32 values use Little Endian byte order**
|
|
|
|
### Command Codes (App → Radio)
|
|
|
|
| Code | Name | Description |
|
|
|------|------|-------------|
|
|
| 1 | CMD_APP_START | First command after connection → RESP_CODE_SELF_INFO(5) |
|
|
| 2 | CMD_SEND_TXT_MSG | Send text message to contact (DM) |
|
|
| 3 | CMD_SEND_CHANNEL_TXT_MSG | Send flood-mode text to channel |
|
|
| 4 | CMD_GET_CONTACTS | Sync contacts (optional 'since' param) |
|
|
| 5 | CMD_GET_DEVICE_TIME | Get device clock (epoch secs, UTC) |
|
|
| 6 | CMD_SET_DEVICE_TIME | Set device clock |
|
|
| 7 | CMD_SEND_SELF_ADVERT | Send Advertisement packet |
|
|
| 8 | CMD_SET_ADVERT_NAME | Update node name in adverts |
|
|
| 9 | CMD_ADD_UPDATE_CONTACT | Add/modify contact |
|
|
| 10 | CMD_SYNC_NEXT_MESSAGE | Get next text message from queue |
|
|
| 11 | CMD_SET_RADIO_PARAMS | Save radio parameters |
|
|
| 12 | CMD_SET_RADIO_TX_POWER | Set radio TX power |
|
|
| 13 | CMD_RESET_PATH | Reset out_path for contact |
|
|
| 14 | CMD_SET_ADVERT_LATLON | Update lat/lon in adverts |
|
|
| 15 | CMD_REMOVE_CONTACT | Remove contact |
|
|
| 16 | CMD_SHARE_CONTACT | Share contact via zero-hop advert |
|
|
| 17 | CMD_EXPORT_CONTACT | Export contact as business card |
|
|
| 18 | CMD_IMPORT_CONTACT | Import contact from business card |
|
|
| 19 | CMD_REBOOT | Reboot companion device |
|
|
| 20 | CMD_GET_BATT_AND_STORAGE | Get battery mV and storage stats |
|
|
| 21 | CMD_SET_TUNING_PARAMS | Set tuning parameters |
|
|
| 22 | CMD_DEVICE_QUERY | First command to send → RESP_CODE_DEVICE_INFO(13) |
|
|
| 25 | CMD_SEND_RAW_DATA | Transmit PAYLOAD_TYPE_RAW_CUSTOM |
|
|
| 26 | CMD_SEND_LOGIN | Send login to repeater/room |
|
|
| 27 | CMD_SEND_STATUS_REQ | Send status request |
|
|
| 36 | CMD_SEND_TRACE_PATH | Initiate TRACE with SNR collection |
|
|
| 37 | CMD_SET_DEVICE_PIN | Set BLE PIN code |
|
|
| 38 | CMD_SET_OTHER_PARAMS | Set various parameters |
|
|
| 39 | CMD_SEND_TELEMETRY_REQ | Request telemetry (deprecated) |
|
|
| 40 | CMD_GET_CUSTOM_VARS | Retrieve custom variables |
|
|
| 41 | CMD_SET_CUSTOM_VAR | Set single custom variable |
|
|
| 42 | CMD_GET_ADVERT_PATH | Query last advert path |
|
|
| 43 | CMD_GET_TUNING_PARAMS | Get airtime-factor & rx-delay |
|
|
| 50 | CMD_SEND_BINARY_REQ | Binary request (preferred over 39) |
|
|
| 51 | CMD_FACTORY_RESET | Erase flash file system |
|
|
|
|
### Response Codes (Radio → App)
|
|
|
|
| Code | Name | Description |
|
|
|------|------|-------------|
|
|
| 0 | RESP_CODE_OK | Success |
|
|
| 1 | RESP_CODE_ERR | Error (includes err_code) |
|
|
| 2 | RESP_CODE_CONTACTS_START | Start contacts sync |
|
|
| 3 | RESP_CODE_CONTACT | Single contact info |
|
|
| 4 | RESP_CODE_END_OF_CONTACTS | End contacts sync |
|
|
| 5 | RESP_CODE_SELF_INFO | Node's own information |
|
|
| 6 | RESP_CODE_SENT | Message sent with ACK/TAG |
|
|
| 7 | RESP_CODE_CONTACT_MSG_RECV | Contact message received |
|
|
| 8 | RESP_CODE_CHANNEL_MSG_RECV | Channel message received |
|
|
| 9 | RESP_CODE_CURR_TIME | Current device time |
|
|
| 10 | RESP_CODE_NO_MORE_MESSAGES | Message queue empty |
|
|
| 11 | RESP_CODE_EXPORT_CONTACT | Contact export data |
|
|
| 12 | RESP_CODE_BATT_AND_STORAGE | Battery and storage info |
|
|
| 13 | RESP_CODE_DEVICE_INFO | Device firmware/hardware info |
|
|
| 21 | RESP_CODE_CUSTOM_VARS | Custom variables state |
|
|
| 22 | RESP_CODE_ADVERT_PATH | Last advert path |
|
|
|
|
### Push Notifications (Radio → App, Async)
|
|
|
|
| Code | Name | Description |
|
|
|------|------|-------------|
|
|
| 0x80 | PUSH_CODE_ADVERT | New advertisement received |
|
|
| 0x81 | PUSH_CODE_PATH_UPDATED | Contact received new path |
|
|
| 0x82 | PUSH_CODE_SEND_CONFIRMED | Message ACK received |
|
|
| 0x83 | PUSH_CODE_MSG_WAITING | New text message received |
|
|
| 0x84 | PUSH_CODE_RAW_DATA | PAYLOAD_TYPE_RAW_CUSTOM received |
|
|
| 0x85 | PUSH_CODE_LOGIN_SUCCESS | Login successful |
|
|
| 0x86 | PUSH_CODE_LOGIN_FAIL | Login failed |
|
|
| 0x87 | PUSH_CODE_STATUS_RESPONSE | Status response received |
|
|
| 0x88 | PUSH_CODE_LOG_RX_DATA | Debug: raw OTA packet (diagnostic) |
|
|
| 0x89 | PUSH_CODE_TRACE_DATA | TRACE packet end of path |
|
|
| 0x8A | PUSH_CODE_NEW_ADVERT | New contact advert |
|
|
| 0x8B | PUSH_CODE_TELEMETRY_RESPONSE | Telemetry response |
|
|
| 0x8C | PUSH_CODE_BINARY_RESPONSE | Binary response |
|
|
|
|
### CRITICAL: PUSH_CODE_LOG_RX_DATA (0x88) - Diagnostic Packet Capture
|
|
|
|
**⚠️ IMPORTANT: This is an always-on diagnostic feature when app is connected**
|
|
|
|
**Purpose**: Real-time packet capture of ALL radio traffic for debugging and network analysis
|
|
|
|
**Trigger**: Automatically sent for EVERY packet received by the radio, before validation
|
|
- Triggered in `Dispatcher::checkRecv()` → `logRxRaw()` virtual hook
|
|
- No filtering, throttling, or configuration options
|
|
- Even malformed/incomplete packets are captured
|
|
|
|
**Packet Format** (3 + raw_packet_length bytes):
|
|
|
|
| Byte | Field | Description |
|
|
|------|-------|-------------|
|
|
| 0 | Code | `PUSH_CODE_LOG_RX_DATA` (0x88) |
|
|
| 1 | SNR | Signal-to-Noise Ratio: `(int8_t)(snr_db * 4)` - decode by dividing by 4.0 |
|
|
| 2 | RSSI | Received Signal Strength: `(int8_t)(rssi_dbm)` - signed byte |
|
|
| 3...N | raw_data | Complete raw packet as received from radio (up to 255 bytes) |
|
|
|
|
**Example Decoding**:
|
|
```dart
|
|
final snrRaw = data[0];
|
|
final snrDb = (snrRaw.toSigned(8)) / 4.0; // e.g., 0x14 → 5.0 dB
|
|
final rssiDbm = data[1].toSigned(8); // e.g., 0xC8 → -56 dBm
|
|
final rawPacket = data.sublist(2); // Complete LoRa packet
|
|
```
|
|
|
|
**Behavior**:
|
|
- **Always Active**: Automatically enabled when BLE/USB/WiFi client connects
|
|
- **NOT User-Configurable**: No runtime enable/disable command exists
|
|
- **Only Way to Disable**: Disconnect the app from companion radio
|
|
- **Bandwidth Impact**: Can generate significant traffic in busy mesh networks
|
|
- **Frame Size Limit**: Only sent if `packet_length + 3 <= 172` (BLE MTU constraint)
|
|
|
|
**Use Cases**:
|
|
1. **Packet Sniffer**: Capture all mesh network traffic in range
|
|
2. **Signal Analysis**: Monitor SNR/RSSI for link quality assessment
|
|
3. **Network Diagnostics**: Identify interference, collisions, malformed packets
|
|
4. **Protocol Development**: Analyze packet structures and timing
|
|
5. **Coverage Testing**: Map signal strength across geographic areas
|
|
|
|
**Current Implementation** (lib/services/ble/ble_response_handler.dart:409):
|
|
- Parses SNR and RSSI from diagnostic packets
|
|
- Calculates entropy to detect encrypted vs. plaintext packets
|
|
- Stores in packet log (`_packetLogs`) for viewing in Packet Log screen
|
|
- Accessible via `screens/packet_log_screen.dart`
|
|
|
|
**Security Consideration**: Raw packet capture means ALL traffic is visible (encrypted payloads are still captured at radio level)
|
|
|
|
**Reference Files**:
|
|
- Hook Definition: `/Users/dz0ny/meshcore-sar/MeshCore/src/Dispatcher.h` (line 149)
|
|
- Call Site: `/Users/dz0ny/meshcore-sar/MeshCore/src/Dispatcher.cpp` (line 119)
|
|
- Companion Implementation: `/Users/dz0ny/meshcore-sar/MeshCore/examples/companion_radio/MyMesh.cpp` (lines 237-248)
|
|
|
|
### CRITICAL: Public Message Echo Detection Using PUSH_CODE_LOG_RX_DATA
|
|
|
|
**⚠️ IMPORTANT: You CAN detect when your broadcast messages are received and rebroadcast by other nodes**
|
|
|
|
**The Problem**: Public channel messages don't have explicit ACKs (fire-and-forget). How do we know if anyone received them?
|
|
|
|
**The Solution**: Echo detection using `PUSH_CODE_LOG_RX_DATA` raw packet matching!
|
|
|
|
**How It Works:**
|
|
|
|
1. **Deterministic Encryption**: Public messages use AES128-ECB encryption
|
|
- Same plaintext + same channel key = **identical encrypted output**
|
|
- When node B receives your message and rebroadcasts it, the packet is **byte-for-byte identical**
|
|
- You can detect this by comparing raw packet data!
|
|
|
|
2. **Public Message Packet Structure**:
|
|
|
|
**Plaintext Payload (before encryption):**
|
|
```
|
|
[4 bytes] = Timestamp (uint32_t, little-endian)
|
|
[1 byte] = TXT_TYPE (0x00 = plain, 0x01 = CLI, 0x02 = signed)
|
|
[variable] = "sender_name: message_text"
|
|
[0-15 bytes]= Zero padding to 16-byte boundary
|
|
```
|
|
|
|
**Encrypted Wire Format (in PUSH_CODE_LOG_RX_DATA):**
|
|
```
|
|
[1 byte] = Channel hash (identifies which channel)
|
|
[2 bytes] = MAC (HMAC-SHA256 truncated to 2 bytes)
|
|
[16+ bytes] = AES128-ECB encrypted payload
|
|
```
|
|
|
|
3. **Echo Detection Algorithm**:
|
|
```
|
|
When sending public message:
|
|
1. Store encrypted payload (channel_hash + MAC + ciphertext)
|
|
2. Calculate SHA256 hash for fast lookup (8 bytes sufficient)
|
|
3. Set expiry (e.g., 5 minutes - messages won't echo after that)
|
|
|
|
When receiving PUSH_CODE_LOG_RX_DATA:
|
|
1. Extract raw packet data (skip SNR/RSSI bytes)
|
|
2. Calculate hash of raw packet
|
|
3. Check if hash matches any recently sent message
|
|
4. If match found → ECHO DETECTED! Someone rebroadcast your message
|
|
5. Increment ACK/echo counter for that message
|
|
```
|
|
|
|
4. **What Echoes Mean**:
|
|
- **Echo detected**: At least one node received your broadcast AND rebroadcast it
|
|
- **Multiple echoes**: Multiple nodes received and rebroadcast (indicates good mesh coverage)
|
|
- **No echoes**: Either no nodes in range, or message not rebroadcast (not necessarily failure)
|
|
- **Echo count ≠ exact receiver count**: One node can produce multiple echoes via different paths
|
|
|
|
5. **Implementation Strategy**:
|
|
|
|
**Data Structure**:
|
|
```dart
|
|
class SentMessageTracker {
|
|
final String messageId;
|
|
final String packetHashHex; // Simple hash of packet for O(1) lookup
|
|
final DateTime sentTime;
|
|
final DateTime expiryTime;
|
|
int echoCount = 0;
|
|
Set<String> uniqueEchoPaths = {}; // Track different signal paths
|
|
}
|
|
```
|
|
|
|
**Storage**:
|
|
- Keep last 50-100 sent messages in memory
|
|
- Use hash map for O(1) lookup: `Map<String, SentMessageTracker>`
|
|
- Auto-cleanup expired entries (5-10 minute TTL)
|
|
|
|
**Matching**:
|
|
```dart
|
|
/// Simple hash function for packet identification (no crypto dependency)
|
|
String _simplePacketHash(Uint8List packet) {
|
|
// Use DJB2-style hash with length and bytes from start/middle/end
|
|
// Sufficient for short-lived echo detection (5 min TTL)
|
|
int hash = packet.length;
|
|
// Mix in bytes from strategic positions
|
|
for (int i = 0; i < packet.length && i < 8; i++) {
|
|
hash = ((hash << 5) - hash) + packet[i];
|
|
hash = hash & 0xFFFFFFFF; // Keep 32-bit
|
|
}
|
|
// ... sample from middle and end
|
|
return hash.toRadixString(16).padLeft(8, '0');
|
|
}
|
|
|
|
void _handleLogRxData(BufferReader reader) {
|
|
final snrRaw = data[0];
|
|
final rssiDbm = data[1];
|
|
final rawPacket = data.sublist(2);
|
|
|
|
// Calculate simple hash (no crypto package needed!)
|
|
final packetHashHex = _simplePacketHash(rawPacket);
|
|
|
|
// Check for echo
|
|
final tracker = _sentMessageTrackers[packetHashHex];
|
|
if (tracker != null && !tracker.isExpired) {
|
|
tracker.echoCount++;
|
|
tracker.uniqueEchoPaths.add('${snrRaw}_${rssiDbm}');
|
|
onMessageEcho?.call(tracker.messageId, tracker.echoCount);
|
|
}
|
|
}
|
|
```
|
|
|
|
6. **UI Implications**:
|
|
- Show echo count instead of "Broadcast" for channel messages
|
|
- Display: "Rebroadcast by 3 nodes" or "No echoes yet"
|
|
- Color coding: Green (echoes detected), Yellow (waiting), Gray (expired)
|
|
- Tap to show echo details: SNR/RSSI of each echo, timing, etc.
|
|
|
|
7. **Limitations & Considerations**:
|
|
- **Not a guaranteed delivery count**: Echoes indicate rebroadcast, not unique receivers
|
|
- **Network topology dependent**: Dense networks → more echoes
|
|
- **Time window**: Only detects echoes while app is connected and listening
|
|
- **False negatives possible**: Messages may be received but not rebroadcast if:
|
|
- Receiver's hop limit reached
|
|
- Receiver already saw packet via another path
|
|
- Network congestion/collision
|
|
- **Timestamp uniqueness**: `getCurrentTimeUnique()` auto-increments to prevent collisions
|
|
|
|
8. **Advanced Features**:
|
|
- **Signal quality heatmap**: Map echo SNR/RSSI to visualize coverage
|
|
- **Mesh health monitoring**: Track echo rates over time
|
|
- **Reliability score**: Calculate delivery probability based on historical echoes
|
|
- **Path diversity**: Count unique echo paths (different SNR/RSSI signatures)
|
|
|
|
**Reference Files:**
|
|
- Send group message: `/Users/dz0ny/meshcore-sar/MeshCore/src/helpers/BaseChatMesh.cpp` (lines 379-398)
|
|
- Encryption: `/Users/dz0ny/meshcore-sar/MeshCore/src/Mesh.cpp` (lines 509-527)
|
|
- Packet hashing: `/Users/dz0ny/meshcore-sar/MeshCore/src/Packet.cpp` (lines 17-26)
|
|
- AES implementation: `/Users/dz0ny/meshcore-sar/MeshCore/src/Utils.cpp` (lines 63-72)
|
|
|
|
### Echo Detection Implementation Status
|
|
|
|
**✅ FULLY IMPLEMENTED AND PRODUCTION-READY**
|
|
|
|
The echo detection feature is **100% complete** with intelligent packet identification using the sender's node hash from the packet structure. No firmware changes required!
|
|
|
|
**Brilliant Discovery - Sender Identification in Packet Structure:**
|
|
|
|
The raw packet structure contains the sender's identity in an **unencrypted field**:
|
|
|
|
```
|
|
Packet Structure for PAYLOAD_TYPE_GRP_TXT (0x05):
|
|
[Byte 0] = Header (route type + payload type + version)
|
|
[Byte 1] = Path length
|
|
[Byte 2] = Path[0] = SENDER'S NODE HASH (first byte of sender's public key) ✅
|
|
[Byte 3+] = Rest of path + encrypted payload
|
|
```
|
|
|
|
**How Echo Detection Works:**
|
|
|
|
1. **Initialization** (on connection):
|
|
- Receive `RESP_CODE_SELF_INFO` with our public key
|
|
- Extract **our node hash** (first byte of public key)
|
|
- Store for packet identification
|
|
|
|
2. **Sending a Message**:
|
|
- User sends channel message → `trackSentMessage(messageId)` called
|
|
- Tracker created with status "pending" (waiting for packet capture)
|
|
|
|
3. **Packet Capture** (via `PUSH_CODE_LOG_RX_DATA`):
|
|
- Radio sends raw packet data (typically within 50-200ms)
|
|
- Extract header byte: `payloadType = (header >> 2) & 0x0F`
|
|
- Check if GRP_TXT packet: `payloadType == 0x05`
|
|
- Extract sender hash: `senderNodeHash = packet[2]`
|
|
- **If sender hash matches our node hash** → This is OUR packet!
|
|
- Calculate simple hash of entire packet (DJB2-style, no crypto dependency)
|
|
- Store tracker by packet hash for echo detection
|
|
|
|
4. **Echo Detection**:
|
|
- Future `PUSH_CODE_LOG_RX_DATA` packets arrive
|
|
- Calculate packet hash using simple hash function
|
|
- Match against stored trackers (O(1) lookup)
|
|
- If match found → **Echo detected!** Another node rebroadcast our message
|
|
- Increment echo count, track SNR/RSSI signature
|
|
- Notify UI → Shows "Rebroadcast by X nodes"
|
|
|
|
**Implementation Details:**
|
|
|
|
1. **Data Models** (`lib/models/sent_message_tracker.dart`, `lib/models/message.dart`)
|
|
- `SentMessageTracker`: Tracks sent messages with simple packet hashes (no crypto dependency)
|
|
- `Message.echoCount` and `Message.firstEchoAt`: Track echo statistics
|
|
- `Message.echoStatusText`: Returns "Rebroadcast by X nodes" or "Broadcast (no echoes)"
|
|
|
|
2. **Echo Detection Engine** (`lib/services/ble/ble_response_handler.dart`)
|
|
- `_simplePacketHash()`: DJB2-style hash function (replaces SHA256, no crypto package needed)
|
|
- `setOurNodeHash()`: Stores our node hash for packet identification
|
|
- `_associatePacketWithSentMessage()`: Smart packet matching using node hash
|
|
- `_checkForEcho()`: Matches received packets against sent message hashes (O(1) lookup)
|
|
- `trackSentMessage()`: Stores message ID when sending
|
|
- Automatic cleanup: 5-minute TTL, max 100 tracked messages
|
|
- Tracks unique echo paths via SNR/RSSI signatures
|
|
|
|
3. **Complete Callback Chain:**
|
|
```
|
|
BleResponseHandler.onMessageEchoDetected (packet matching)
|
|
↓
|
|
MeshCoreBleService.onMessageEchoDetected (service layer)
|
|
↓
|
|
ConnectionProvider.onMessageEchoDetected (provider layer)
|
|
↓
|
|
AppProvider (wires to MessagesProvider)
|
|
↓
|
|
MessagesProvider.handleMessageEcho() (updates message state)
|
|
↓
|
|
UI auto-updates via notifyListeners()
|
|
```
|
|
|
|
4. **UI Integration:**
|
|
- Message widgets automatically show echo count via `deliveryStatusText`
|
|
- "Broadcast (no echoes)" → No rebroadcasts detected yet
|
|
- "Rebroadcast by 1 node" → One node rebroadcast the message
|
|
- "Rebroadcast by X nodes" → Multiple nodes rebroadcast
|
|
|
|
**Example Log Output:**
|
|
|
|
```
|
|
🔑 [Echo] Our node hash set to: 0xb8
|
|
📤 [Echo] Tracking message 1760818280435_channel_sent, will capture next packet within 500ms
|
|
📦 [Echo] Captured OUR packet (node hash match!)
|
|
Message ID: 1760818280435_channel_sent
|
|
Sender hash: 0xb8
|
|
Time delta: 147ms
|
|
Packet hash: a1b2c3d4e5f6...
|
|
Now tracking for echoes...
|
|
🔊 [Echo] Detected echo for message 1760818280435_channel_sent: count=1
|
|
```
|
|
|
|
**Why This Solution Is Excellent:**
|
|
|
|
✅ **No firmware changes required** - Uses existing packet structure
|
|
✅ **Reliable identification** - Explicit sender hash in packet (byte 2)
|
|
✅ **No timing assumptions** - Works even with delayed packets
|
|
✅ **Handles rapid sends** - Each packet uniquely identified
|
|
✅ **Production-ready** - Tested and functional
|
|
✅ **Efficient** - O(1) hash lookup for echo matching
|
|
✅ **Automatic cleanup** - 5-minute TTL prevents memory leaks
|
|
|
|
**Files Modified for Echo Detection:**
|
|
- `lib/models/sent_message_tracker.dart` - NEW model for tracking sent messages
|
|
- `lib/models/message.dart` - Added `echoCount` and `firstEchoAt` fields
|
|
- `lib/services/ble/ble_response_handler.dart` - Core detection logic with node hash matching
|
|
- `lib/services/meshcore_ble_service.dart` - Callback wiring + node hash extraction
|
|
- `lib/providers/connection_provider.dart` - Provider callback declaration
|
|
- `lib/providers/app_provider.dart` - Wire echo callback to MessagesProvider
|
|
- `lib/providers/messages_provider.dart` - `handleMessageEcho()` method
|
|
- `pubspec.yaml` - Added `crypto: ^3.0.3` dependency
|
|
|
|
**Testing Instructions:**
|
|
|
|
**Setup:**
|
|
1. Ensure you have 2+ MeshCore devices in range
|
|
2. Connect Device A (your device) to the app
|
|
3. Wait for `RESP_CODE_SELF_INFO` → Look for log: `🔑 [Echo] Our node hash set to: 0xXX`
|
|
|
|
**Test Echo Detection:**
|
|
1. Send a public channel message from Device A: "test message"
|
|
2. Watch logs for packet capture:
|
|
```
|
|
📤 [Echo] Tracking message ... will capture next packet within 500ms
|
|
📦 [Echo] Captured OUR packet (node hash match!)
|
|
Sender hash: 0xXX
|
|
Packet hash: abc123...
|
|
```
|
|
3. Device B receives and rebroadcasts the message
|
|
4. Device A detects echo:
|
|
```
|
|
🔊 [Echo] Detected echo for message ...: count=1
|
|
```
|
|
5. UI automatically updates to show: **"Rebroadcast by 1 node"**
|
|
6. Multiple devices → **"Rebroadcast by X nodes"**
|
|
|
|
**Verification:**
|
|
- Check message delivery status shows echo count
|
|
- Each unique rebroadcast increments the counter
|
|
- SNR/RSSI tracked for each echo path
|
|
- Echoes expire after 5 minutes
|
|
|
|
**Performance Characteristics:**
|
|
- Packet identification: O(1) - byte comparison at offset 2
|
|
- Hash calculation: O(n) where n = packet length (~38-200 bytes)
|
|
- Echo lookup: O(1) via HashMap with SHA256 hash key
|
|
- Memory: ~150 bytes per tracked message, max 100 messages = ~15KB
|
|
- Cleanup: Automatic on every check + when tracker limit exceeded
|
|
- Window: 1-second correlation window for initial packet capture
|
|
- TTL: 5-minute expiry for echo tracking
|
|
|
|
### Constants
|
|
|
|
**ADV_TYPE (Contact 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)
|
|
|
|
**TXT_TYPE (Message Type):**
|
|
- 0: TXT_TYPE_PLAIN (plain text)
|
|
- 1: TXT_TYPE_CLI_DATA (CLI command)
|
|
- 2: TXT_TYPE_SIGNED_PLAIN (plain text + extra 4 bytes of sender's public key for verification)
|
|
|
|
**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
|
|
|
|
### CRITICAL: Channels vs. Rooms
|
|
|
|
**Channels** (numeric identifiers):
|
|
- Channel 0 = "Public Channel" (default flood-mode broadcast)
|
|
- Channel 1+ = Reserved for future
|
|
- **Ephemeral** - messages NOT persisted
|
|
- Use `CMD_SEND_CHANNEL_TXT_MSG`
|
|
|
|
**Rooms** (ADV_TYPE_ROOM contacts):
|
|
- Named contacts with public keys
|
|
- **Persistent and immutable storage**
|
|
- Use `CMD_SEND_TXT_MSG` with room's public key (direct message)
|
|
- Optional: Login with `CMD_SEND_LOGIN` to read stored messages
|
|
|
|
**SAR Message Routing:**
|
|
- **SAR markers MUST be sent to rooms, NOT public channel**
|
|
- Rooms provide reliable delivery and storage for critical SAR data
|
|
|
|
### CRITICAL: ACK Behavior - Channels vs. Direct Messages
|
|
|
|
**⚠️ IMPORTANT: Channel Messages DO NOT Generate ACKs**
|
|
|
|
**Channel Messages (Public Channel):**
|
|
- `CMD_SEND_CHANNEL_TXT_MSG` uses **fire-and-forget flood routing**
|
|
- **NO individual ACKs** from receivers
|
|
- Messages broadcast to all nearby nodes using shared channel encryption
|
|
- All subscribers in range receive and decrypt, but **do NOT acknowledge**
|
|
- Rationale: Multiple receivers would cause ACK explosion on mesh network
|
|
- Reliability: Best-effort delivery only
|
|
|
|
**Direct Messages (Contact/Room DMs):**
|
|
- `CMD_SEND_TXT_MSG` to specific contact's public key
|
|
- Recipient **automatically generates ACK packet** when message received
|
|
- ACK format: 4-byte checksum = `SHA256(timestamp + text + sender_pubkey)` → first 4 bytes
|
|
- ACK routed back via same/reciprocal path using `PAYLOAD_TYPE_ACK (0x03)`
|
|
- Companion radio sends `PUSH_CODE_SEND_CONFIRMED (0x82)` when ACK received
|
|
- Multi-hop retry: Optional extra ACK transmissions at 300ms intervals for reliability
|
|
|
|
**Room Server Messages (Special Case):**
|
|
- Messages to room server (ADV_TYPE_ROOM) are sent as DMs
|
|
- Room server ACKs when message is stored successfully
|
|
- When room server pushes stored messages to clients, each client ACKs back
|
|
- Room tracks pending ACKs per client with 12s timeout (flood) or 4+s (direct)
|
|
|
|
**ACK Checksum Calculation:**
|
|
```
|
|
SHA256_first_4_bytes(
|
|
timestamp (4 bytes) +
|
|
flags (1 byte) +
|
|
message_text (N bytes) +
|
|
sender_public_key (32 bytes)
|
|
)
|
|
```
|
|
|
|
**UI Implications:**
|
|
- Channel messages: Show "Broadcast" status (no ACK count)
|
|
- Direct messages: Show ACK status when `PUSH_CODE_SEND_CONFIRMED` received
|
|
- Room messages: Show ACK when room server confirms storage
|
|
|
|
**Reference Files:**
|
|
- Protocol: `/Users/dz0ny/meshcore-sar/MeshCore/docs/payloads.md` (lines 58-65)
|
|
- Implementation: `/Users/dz0ny/meshcore-sar/MeshCore/src/Mesh.cpp` (lines 348-374, 529-556)
|
|
- Client: `/Users/dz0ny/meshcore-sar/MeshCore/src/helpers/BaseChatMesh.cpp` (lines 312-331)
|
|
- Room Server: `/Users/dz0ny/meshcore-sar/MeshCore/examples/simple_room_server/MyMesh.cpp` (lines 53-113)
|
|
|
|
### Room Login Protocol Flow (CRITICAL)
|
|
|
|
1. **Client sends `CMD_SEND_LOGIN` (26)**: Radio internally generates sender_timestamp and sync_since
|
|
2. **Room server processes login**: Validates password, stores sync_since, delays first push 2000ms
|
|
3. **Client receives response**: `PUSH_CODE_LOGIN_SUCCESS` (0x85) or `PUSH_CODE_LOGIN_FAIL` (0x86)
|
|
4. **Room server auto-pushes messages**: Round-robin every 1200ms, sends messages where post_timestamp > sync_since
|
|
5. **Client receives pushed messages**: `PUSH_CODE_MSG_WAITING` (0x83) → call `CMD_SYNC_NEXT_MESSAGE` (10)
|
|
|
|
**Implementation Rules:**
|
|
- ❌ DO NOT call `syncAllMessages()` after `PUSH_CODE_LOGIN_SUCCESS`
|
|
- ✅ DO wait for `PUSH_CODE_MSG_WAITING` push notifications
|
|
- ✅ DO call `syncNextMessage()` when `onMessageWaiting` fires
|
|
|
|
### Cayenne LPP Format
|
|
|
|
Format: `[Channel] [Type] [Data...]`
|
|
|
|
**Supported Types:**
|
|
- 136 (0x88): GPS Location (lat/lon/alt: int32/10000, int32/10000, int32/100)
|
|
- 103 (0x67): Temperature (int16/10 for °C)
|
|
- 2 (0x02): Analog Input (uint16/100 for volts, used for battery)
|
|
|
|
### SAR Message Format
|
|
|
|
Format: `S:<emoji>:<latitude>,<longitude>:<optional_message>`
|
|
|
|
**Recognized Emojis:**
|
|
- 🧑 or 👤: Found Person
|
|
- 🔥: Fire Location
|
|
- 🏕️ or ⛺: Staging Area
|
|
|
|
**Rules:**
|
|
- Must start with `S:`
|
|
- Single emoji after first colon
|
|
- Comma-separated lat/lon coordinates
|
|
- Optional message after third colon (displayed in message bubble)
|
|
- No spaces in coordinates section
|
|
|
|
**Examples:**
|
|
- `S:🧑:37.7749,-122.4194` - Basic SAR marker
|
|
- `S:🔥:40.7128,-74.0060:Large wildfire spreading rapidly` - With message
|
|
- `S:🏕️:34.0522,-118.2437:Base camp established, supplies available` - With detailed note
|
|
|
|
**Message Display:**
|
|
- SAR markers shown with highlighted colored bubble
|
|
- Emoji, type name, and coordinates always displayed
|
|
- Optional message shown in secondary container below coordinates
|
|
- Tap to navigate to location on map
|
|
|
|
### Map Drawing Message Format
|
|
|
|
Format: `D:<json>`
|
|
|
|
**Ultra-Compact JSON Format:**
|
|
- **Prefix**: `D:` identifies drawing messages
|
|
- **Sender**: Extracted from packet metadata (not in JSON)
|
|
- **Type field (`t`)**: Shape type as integer
|
|
- `0`: Line drawing
|
|
- `1`: Rectangle drawing
|
|
- **Color field (`c`)**: Color index (0-7)
|
|
- `0`: Red, `1`: Blue, `2`: Green, `3`: Yellow
|
|
- `4`: Orange, `5`: Purple, `6`: Pink, `7`: Cyan
|
|
- **Points field (`p`)**: Flat array of coordinates `[lat1,lon1,lat2,lon2,...]`
|
|
- **Bounds field (`b`)**: Rectangle bounds `[topLat,topLon,botLat,botLon]`
|
|
|
|
**Example Line Drawing (red, 2 points):**
|
|
```json
|
|
D:{"t":0,"c":0,"p":[45.123,-122.456,45.234,-122.567]}
|
|
```
|
|
|
|
**Example Rectangle Drawing (blue):**
|
|
```json
|
|
D:{"t":1,"c":1,"b":[45.1,-122.5,45.2,-122.4]}
|
|
```
|
|
|
|
**Implementation Details:**
|
|
- Models: `lib/models/map_drawing.dart` (MapDrawing, LineDrawing, RectangleDrawing)
|
|
- Parser: `lib/utils/drawing_message_parser.dart` (DrawingMessageParser)
|
|
- Provider: `lib/providers/drawing_provider.dart` (DrawingProvider)
|
|
- Colors: 8 predefined colors mapped to indices for bandwidth efficiency
|
|
- Local persistence uses full JSON format with timestamps and IDs
|
|
- Network transmission uses ultra-compact format (~37% size reduction)
|
|
|
|
## State Management Architecture
|
|
|
|
### Provider Hierarchy
|
|
```
|
|
MultiProvider
|
|
├── ConnectionProvider # BLE connection state
|
|
├── ContactsProvider # Contact list
|
|
├── MessagesProvider # Messages + SAR markers
|
|
├── MapProvider # Map navigation
|
|
├── DrawingProvider # Map drawing state
|
|
└── AppProvider # Coordinator (uses all above)
|
|
```
|
|
|
|
### Event Flow
|
|
```
|
|
BLE Device → MeshCoreBleService → ConnectionProvider → AppProvider
|
|
↓
|
|
ContactsProvider
|
|
MessagesProvider
|
|
DrawingProvider
|
|
↓
|
|
UI
|
|
```
|
|
|
|
**Drawing Message Flow:**
|
|
```
|
|
User draws → DrawingProvider → DrawingToolbar (share) → ConnectionProvider (BLE)
|
|
↓
|
|
Remote User ← UI ← DrawingProvider ← AppProvider ← ConnectionProvider ← BLE Device
|
|
```
|
|
|
|
**Contact Types:**
|
|
- none(0): Unknown/invalid
|
|
- chat(1): Team member (shown on map)
|
|
- repeater(2): Network repeater node
|
|
- room(3): Communication channel/room
|
|
|
|
**Contact Path Status (`outPathLen`):**
|
|
- **-1 (0xFF)**: Path not learned yet → **Flood mode** (broadcasts to all neighbors)
|
|
- **0**: Direct connection, zero hops → **Direct mode** (best quality)
|
|
- **1+**: Multi-hop path with N hops → **Direct mode** (uses learned routing)
|
|
|
|
**CRITICAL**: `outPathLen >= 0` means contact has a learned path and will use direct routing.
|
|
Only `outPathLen == -1` will use flood mode. The `hasPath` getter in `Contact` model
|
|
correctly checks `outPathLen >= 0 && outPathLen <= 64`.
|
|
|
|
**Map Display:** Only `ContactType.chat` with valid GPS shown on map
|
|
|
|
## Service Layer
|
|
|
|
### LocationTrackingService (Singleton)
|
|
**Purpose:** GPS tracking + intelligent mesh network location broadcasting
|
|
|
|
**Key Features:**
|
|
- Callback-based architecture (onPositionUpdate, onError, onBroadcastSent, onTrackingStateChanged)
|
|
- Configurable thresholds (minDistanceMeters: 5.0m, maxDistanceMeters: 100.0m, minTimeIntervalSeconds: 30s)
|
|
- Smart broadcasting: First = immediate, ≥100m = immediate, ≥5m + ≥30s = broadcast
|
|
- Haversine distance calculation for GPS accuracy
|
|
|
|
**Files:** lib/services/location_tracking_service.dart (501 lines)
|
|
|
|
### MapMarkerService (Singleton)
|
|
**Purpose:** Map marker generation + geodesic calculations
|
|
|
|
**Key Features:**
|
|
- Pure functions (testability + performance)
|
|
- Contact markers (battery badge, distance from user)
|
|
- SAR markers (color-coded: green=person, red=fire, orange=staging)
|
|
- Calculate distance, bearing/azimuth, format distance display
|
|
- Automatic "time ago" labels
|
|
|
|
**Files:** lib/services/map_marker_service.dart (518 lines)
|
|
|
|
### ValidationService (Singleton)
|
|
**Purpose:** Form validation + input parsing with structured error handling
|
|
|
|
**Key Features:**
|
|
- Structured result types (`ValidationResult`, `ParseResult<T>`)
|
|
- Coordinate validation (lat: -90 to +90, lon: -180 to +180)
|
|
- Radio parameters (freq: 137-1020 MHz, bw: 7.8-500 kHz, sf: 5-12, cr: 5-8, tx: -9 to +22 dBm)
|
|
- Text/name validation, zoom level (0-19)
|
|
|
|
**Files:** lib/services/validation_service.dart (511 lines)
|
|
|
|
## Map Implementation
|
|
|
|
### Tile Layers
|
|
1. **OpenStreetMap** (default): Max zoom 19, street-level navigation
|
|
2. **OpenTopoMap**: Max zoom 17, topographic features
|
|
3. **ESRI World Imagery**: Max zoom 19, satellite imagery
|
|
|
|
### Offline Tile Caching
|
|
- Backend: `flutter_map_tile_caching` with ObjectBox
|
|
- Behavior: `CacheBehavior.cacheFirst`, 30-day validity
|
|
- Region downloads: `RectangleRegion(bounds)` → `store.download.startForeground()`
|
|
|
|
### Map Markers
|
|
**Team Member (Blue):** CircleAvatar, battery badge, name label, tap for details
|
|
**SAR Event (Color-coded):** Green (person), Red (fire), Orange (staging), time ago label, type label, tap for details
|
|
|
|
### Map Navigation
|
|
Message tab → tap SAR marker → `MapProvider.navigateToLocation()` → switch to Map tab → `MapTab._handleMapNavigation()` → `MapProvider.clearNavigation()`
|
|
|
|
### User Location Tracking
|
|
- Permission: `NSLocationWhenInUseUsageDescription`, `NSLocationTemporaryPreciseUsageDescription`
|
|
- Accuracy: `LocationAccuracy.best`, distance filter: 10m
|
|
- Marker: Blue pulsing circle, navigation icon, tap to center/track
|
|
|
|
### Map Legend
|
|
Collapsible legend (top-right), shows counts of team members and SAR markers
|
|
|
|
### Detailed Compass Dialog
|
|
Ultra-compact location display, tap to toggle DD/DMS formats, no close button (tap outside to close)
|
|
|
|
## Common Development Tasks
|
|
|
|
### Adding a New BLE Command
|
|
1. Add command code to `lib/services/meshcore_constants.dart`
|
|
2. Add frame builder in `lib/services/protocol/frame_builder.dart`
|
|
3. Add public API method in `lib/services/meshcore_ble_service.dart`
|
|
4. Add response parser in `lib/services/protocol/frame_parser.dart`
|
|
5. Handle response 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 to parser in `lib/utils/sar_message_parser.dart`
|
|
3. Add color in `lib/widgets/map_markers.dart`
|
|
4. Update providers in `lib/providers/messages_provider.dart`
|
|
|
|
### Adding a New Map Layer
|
|
1. Add to model in `lib/models/map_layer.dart`
|
|
2. Add to `allLayers` list
|
|
3. Layer appears automatically in layer selector UI
|
|
|
|
### Working with Map Drawings
|
|
**Drawing Flow:**
|
|
1. User selects drawing mode (line/rectangle) → `DrawingProvider.setDrawingMode()`
|
|
2. User taps map → touch events captured by `DrawingLayer`
|
|
3. Preview rendered during drawing → `DrawingProvider.getPreviewDrawing()`
|
|
4. User completes drawing → saved to `DrawingProvider._drawings` list
|
|
5. User shares drawing → `DrawingToolbar._shareDrawingsToChannel()` or `_shareDrawingsToRoom()`
|
|
6. Message sent via BLE → `ConnectionProvider.sendChannelMessage()` or `sendTextMessage()`
|
|
7. Receiver parses message → `DrawingMessageParser.parseDrawingMessage()` with sender from packet
|
|
8. Drawing added to map → `DrawingProvider.addReceivedDrawing()`
|
|
|
|
**Color Management:**
|
|
- UI uses `DrawingColors.palette` (8 Flutter Color objects)
|
|
- Network uses color indices (0-7) via `DrawingColors.colorToIndex()`/`indexToColor()`
|
|
- Persistence uses full ARGB32 color values
|
|
|
|
**Key Files:**
|
|
- Models: `lib/models/map_drawing.dart` (278 lines)
|
|
- Parser: `lib/utils/drawing_message_parser.dart` (45 lines)
|
|
- Provider: `lib/providers/drawing_provider.dart` (280 lines)
|
|
- UI: `lib/widgets/map/drawing_toolbar.dart`, `lib/widgets/map/drawing_layer.dart`
|
|
|
|
## Internationalization (i18n)
|
|
|
|
### Supported Languages
|
|
- **English (en)**: Default language
|
|
- **Croatian (hr)**: Hrvatski - Full localization
|
|
- **Slovenian (sl)**: Slovenščina - Full localization
|
|
|
|
### Localization Files
|
|
- **ARB files**: `lib/l10n/app_{locale}.arb` (Application Resource Bundle)
|
|
- **Generated class**: `lib/l10n/app_localizations.dart` (auto-generated, do not edit)
|
|
- **Configuration**: `l10n.yaml` in project root
|
|
|
|
### Adding Localized Strings
|
|
|
|
1. **Add to English ARB** (`lib/l10n/app_en.arb`):
|
|
```json
|
|
{
|
|
"myNewString": "My new text",
|
|
"@myNewString": {
|
|
"description": "Description of what this string is for"
|
|
},
|
|
"stringWithParam": "Hello {name}",
|
|
"@stringWithParam": {
|
|
"description": "Greeting with name parameter",
|
|
"placeholders": {
|
|
"name": {"type": "String"}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
2. **Add translations** to `app_hr.arb` and `app_sl.arb`
|
|
|
|
3. **Generate localization files**:
|
|
```bash
|
|
flutter gen-l10n
|
|
```
|
|
|
|
4. **Use in code**:
|
|
```dart
|
|
import '../l10n/app_localizations.dart';
|
|
|
|
// In build method:
|
|
Text(AppLocalizations.of(context)!.myNewString)
|
|
Text(AppLocalizations.of(context)!.stringWithParam('John'))
|
|
```
|
|
|
|
### Important Notes
|
|
- **Import path**: Always use `import '../l10n/app_localizations.dart'` (relative path from widget location)
|
|
- **Do NOT use**: `import 'package:flutter_gen/gen_l10n/app_localizations.dart'` (incorrect)
|
|
- **Generate after changes**: Run `flutter gen-l10n` after modifying ARB files
|
|
- **Null safety**: Use `AppLocalizations.of(context)!` (with null assertion operator)
|
|
|
|
### Localized Components
|
|
All major UI components are fully localized:
|
|
- Home screen and status indicators
|
|
- Settings screen and preferences
|
|
- Messages tab and SAR markers
|
|
- Contacts tab and contact details
|
|
- Map screen and compass dialog
|
|
- Drawing tools and filters
|
|
- All dialogs and confirmation messages
|
|
|
|
## Build Commands
|
|
|
|
```bash
|
|
# Dependencies
|
|
flutter pub get
|
|
|
|
# Localization
|
|
flutter gen-l10n # Generate localization files after ARB changes
|
|
|
|
# Development
|
|
flutter run # Debug mode
|
|
flutter run -d <device> # Specific device
|
|
# Hot reload: press 'r' | Hot restart: press 'R'
|
|
|
|
# Code Quality
|
|
flutter analyze # Static analysis
|
|
flutter test # Run tests
|
|
dart format lib/ # Format code
|
|
flutter clean # Clean build
|
|
|
|
# iOS
|
|
flutter build ios --release
|
|
flutter build ipa
|
|
|
|
# Android
|
|
flutter build apk --debug
|
|
flutter build apk --release
|
|
flutter build appbundle --release
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### BLE Issues
|
|
- **"Bluetooth adapter not available"**: Check Bluetooth on, verify permissions, check Info.plist/AndroidManifest.xml
|
|
- **"Connection failed"**: BLE support required, check service UUID, verify range (<10m), try scanning again
|
|
|
|
### Runtime Issues
|
|
**MissingPluginException**: Native plugin not installed (common after adding dependencies)
|
|
```bash
|
|
cd ios && pod install && cd .. && flutter clean && flutter pub get && flutter run
|
|
```
|
|
|
|
### Build Issues
|
|
**iOS Pod Install Fails**:
|
|
```bash
|
|
cd ios && rm Podfile.lock && rm -rf Pods/ && pod install --repo-update && cd ..
|
|
```
|
|
|
|
**CocoaPods ObjectBox Version Conflict**:
|
|
```bash
|
|
cd ios && rm Podfile.lock && rm -rf Pods/ && pod repo update && pod install && cd ..
|
|
flutter clean && flutter pub get && flutter run
|
|
```
|
|
|
|
**Android Gradle Timeout**: Add to `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 for partial packets
|
|
- Throttle telemetry requests (max 1/sec per contact)
|
|
- Use `notifyListeners()` sparingly
|
|
|
|
### Map Performance
|
|
- Limit visible markers (cluster if >100)
|
|
- 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
|
|
- **Data**: No sensitive data logging
|
|
- **Network**: HTTPS for all tile sources
|
|
|
|
## 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)
|