Files
meshcore-sar_android/ADVERT_SYSTEM.md

304 lines
9.2 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MeshCore Advertisement System
This document explains how the MeshCore mesh network advertisement system works and how your app receives and processes contact updates.
## Overview
The MeshCore mesh network uses a broadcast advertisement system where nodes periodically announce their presence, location, and metadata to the network. Your Flutter app receives these advertisements and automatically updates the contact list.
## Advertisement Flow
### 1. Node Broadcasts Advertisement
When a node in the mesh network wants to announce its presence:
- The node broadcasts an advertisement packet over LoRa
- Advertisement contains: public key, name, location, type, etc.
- Advertisements are typically sent every few minutes or when data changes
### 2. Companion Radio Receives Advertisement
Your BLE-connected companion radio listens to the mesh network and receives these advertisements:
```
[Mesh Network] ───(LoRa)──→ [Companion Radio]
```
### 3. Companion Radio Notifies App
The companion radio forwards advertisement notifications to your app via BLE:
#### Step 3a: PUSH_CODE_ADVERT (0x80)
First, you receive a simple notification that an advert was received:
```dart
flutter: 📥 [RX] Received: ADVERT (0x80)
flutter: Data size: 33 bytes
flutter: Payload: 32 bytes
flutter: Handling Advert push
flutter: [Advert] Parsing advert push notification...
flutter: 📡 ADVERT RECEIVED FROM NODE:
flutter: Public key prefix (6 bytes): a5:9c:36:02:c0:d7
flutter: Public key (full 32 bytes): a5:9c:36:02:c0:d7:e4:c3:...
flutter: This indicates the node is broadcasting its presence
flutter: The companion radio will automatically update contact info
flutter: Expected follow-up:
flutter: - If manual_add_contacts=0: PUSH_CODE_NEW_ADVERT with full details
flutter: - If manual_add_contacts=1: Call CMD_GET_CONTACTS to sync
```
**Protocol Format:**
```
[0x80] - PUSH_CODE_ADVERT
[32 bytes] - Public key of advertising node
```
#### Step 3b: PUSH_CODE_NEW_ADVERT (0x8A) - Automatic Contact Update
If your device has `manual_add_contacts=0` (automatic mode), the companion radio automatically sends the full contact details:
```dart
flutter: 📥 [RX] Received: NEW_ADVERT (0x8A)
flutter: Data size: 145 bytes
flutter: Payload: 144 bytes
flutter: Handling NewAdvert push
flutter: [NewAdvert] Parsing new advertisement...
flutter: Public key prefix: a5:9c:36:02:c0:d7
flutter: Type byte: 1 Type: ContactType.chat
flutter: Advertised name: "SAR Team Alpha"
flutter: Latitude: 46.056900°
flutter: Longitude: 14.505800°
flutter: [NewAdvert] Parsed successfully - new contact advertised on network
```
**Protocol Format:**
```
[0x8A] - PUSH_CODE_NEW_ADVERT
[32 bytes] - Public key
[1 byte] - Type (ADV_TYPE_*)
[1 byte] - Flags
[1 byte] - Out path length
[64 bytes] - Out path
[32 bytes] - Advertised name (null-terminated)
[4 bytes] - Last advert timestamp (uint32)
[4 bytes] - Latitude * 1E6 (int32)
[4 bytes] - Longitude * 1E6 (int32)
[4 bytes] - Last modified timestamp (uint32)
```
The app automatically adds/updates this contact via the `onContactReceived` callback!
## Manual vs Automatic Contact Management
Your companion radio has a setting called `manual_add_contacts`:
### Automatic Mode (manual_add_contacts=0) - RECOMMENDED
**Behavior:**
1. ✅ PUSH_CODE_ADVERT (0x80) received → just informational
2. ✅ PUSH_CODE_NEW_ADVERT (0x8A) received → **contact automatically added to app**
3. ✅ No action needed from app
**Advantages:**
- Contacts appear instantly when they advertise
- No need to manually sync
- Perfect for SAR operations where team members join dynamically
### Manual Mode (manual_add_contacts=1)
**Behavior:**
1. ✅ PUSH_CODE_ADVERT (0x80) received → informational
2. ❌ PUSH_CODE_NEW_ADVERT (0x8A) NOT sent
3. 📞 App must call `CMD_GET_CONTACTS` to sync
**When to use:**
- When you want control over which contacts are added
- When bandwidth is very limited
- When you have a static team roster
## Implementation in Your App
### Current Implementation
The app is already fully configured to handle advertisements automatically:
```dart
// In MeshCoreBleService (_handleNewAdvert)
final contact = Contact(
publicKey: publicKey,
type: type,
flags: flags,
outPathLen: outPathLen,
outPath: outPath,
advName: advName,
lastAdvert: lastAdvert,
advLat: advLat,
advLon: advLon,
lastMod: lastMod,
);
// This callback automatically updates the contact list
onContactReceived?.call(contact);
```
```dart
// In AppProvider (_setupCallbacks)
connectionProvider.onContactReceived = (contact) {
// Automatically add or update contact in the list
contactsProvider.addOrUpdateContact(contact);
};
```
### Event Flow
```
[Mesh Node Advertises]
[Companion Radio Receives via LoRa]
[PUSH_CODE_ADVERT (0x80) sent to app]
↓ (if manual_add_contacts=0)
[PUSH_CODE_NEW_ADVERT (0x8A) sent to app]
[onContactReceived callback fired]
[contactsProvider.addOrUpdateContact(contact)]
[UI automatically updates via notifyListeners()]
```
## Checking Your Device Settings
To see if your device is in automatic or manual mode:
```dart
// Check the manualAddContacts field from SelfInfo
final manualMode = connectionProvider.deviceInfo.manualAddContacts;
if (manualMode == false) {
print('✅ Automatic mode: Contacts will appear automatically');
} else {
print('⚠️ Manual mode: You need to call getContacts() after adverts');
}
```
You can change this setting:
```dart
await connectionProvider.setOtherParams(
manualAddContacts: 0, // 0 = automatic, 1 = manual
telemetryModes: currentTelemetryModes,
advertLocationPolicy: currentLocationPolicy,
);
```
## Troubleshooting
### "I receive ADVERT (0x80) but no NEW_ADVERT (0x8A)"
**Cause:** Your device is in manual mode (`manual_add_contacts=1`)
**Solution:**
1. Check device settings via SelfInfo
2. Change to automatic mode, OR
3. Call `CMD_GET_CONTACTS` after receiving adverts
### "Contacts don't appear on the map"
**Possible causes:**
1. Contact type is not `ContactType.chat` (only chat contacts show on map)
2. Contact has no GPS coordinates (lat/lon = 0)
3. Contact hasn't advertised recently
**Debug:**
```dart
// Check contact properties
print('Contact: ${contact.advName}');
print('Type: ${contact.type}'); // Should be ContactType.chat
print('Lat: ${contact.latitude}'); // Should not be null
print('Lon: ${contact.longitude}'); // Should not be null
```
### "Room contact not found for login"
**Cause:** Room hasn't advertised yet or wasn't synced
**Solution:**
```dart
// Force sync contacts first
await connectionProvider.getContacts();
// Small delay to ensure contacts are loaded
await Future.delayed(const Duration(milliseconds: 500));
// Then try login
await connectionProvider.loginToRoom(
roomPublicKey: roomContact.publicKey,
password: 'your_password',
);
```
## Best Practices
1. **Use automatic mode for SAR operations** - Team members will appear as they join
2. **Sync contacts on first connect** - Always call `getContacts()` after connecting
3. **Handle both modes gracefully** - Check `manual_add_contacts` setting
4. **Monitor advert activity** - Use `onAdvertReceived` callback to show network activity
5. **Cache contacts locally** - Don't rely solely on live advertisements
## Protocol Summary
| Push Code | Name | When Sent | Contains | Action Required |
|-----------|------|-----------|----------|-----------------|
| 0x80 | PUSH_CODE_ADVERT | When any node advertises | Just public key | None (informational) |
| 0x8A | PUSH_CODE_NEW_ADVERT | After ADVERT, if manual_add_contacts=0 | Full contact details | None (auto-added) |
## Example Logs
### Successful Automatic Flow
```
📥 [RX] Received: ADVERT (0x80)
📡 ADVERT RECEIVED FROM NODE:
Public key prefix (6 bytes): a5:9c:36:02:c0:d7
📥 [Provider] Advert received from node
Note: Waiting for PUSH_CODE_NEW_ADVERT (0x8A) with full contact details
📥 [RX] Received: NEW_ADVERT (0x8A)
[NewAdvert] Parsing new advertisement...
Advertised name: "SAR Team Alpha"
Latitude: 46.056900°
Longitude: 14.505800°
✅ [NewAdvert] Parsed successfully
✅ [Provider] Contact added: SAR Team Alpha
```
### Manual Mode Flow
```
📥 [RX] Received: ADVERT (0x80)
📡 ADVERT RECEIVED FROM NODE:
Public key prefix (6 bytes): a5:9c:36:02:c0:d7
📥 [Provider] Advert received from node
Note: manual_add_contacts=1, you need to call CMD_GET_CONTACTS
📤 [TX] Sending command: GET_CONTACTS (0x04)
📥 [RX] Received: CONTACTS_START (0x02)
📥 [RX] Received: CONTACT (0x03)
[Contact] Parsing contact...
Advertised name: "SAR Team Alpha"
✅ [Provider] Contact added: SAR Team Alpha
```
## Related Files
- `lib/services/meshcore_ble_service.dart:876` - `_handleAdvert()` implementation
- `lib/services/meshcore_ble_service.dart:924` - `_handleNewAdvert()` implementation
- `lib/providers/connection_provider.dart:164` - `onAdvertReceived` callback setup
- `lib/providers/app_provider.dart:44` - Contact sync setup
- `lib/models/contact.dart` - Contact data model
## Further Reading
- [MeshCore Protocol Documentation](CLAUDE.md) - Full protocol specification
- [Contact Management](lib/providers/contacts_provider.dart) - Contact provider implementation