9.2 KiB
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:
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:
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:
- ✅ PUSH_CODE_ADVERT (0x80) received → just informational
- ✅ PUSH_CODE_NEW_ADVERT (0x8A) received → contact automatically added to app
- ✅ 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:
- ✅ PUSH_CODE_ADVERT (0x80) received → informational
- ❌ PUSH_CODE_NEW_ADVERT (0x8A) NOT sent
- 📞 App must call
CMD_GET_CONTACTSto 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:
// 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);
// 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:
// 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:
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:
- Check device settings via SelfInfo
- Change to automatic mode, OR
- Call
CMD_GET_CONTACTSafter receiving adverts
"Contacts don't appear on the map"
Possible causes:
- Contact type is not
ContactType.chat(only chat contacts show on map) - Contact has no GPS coordinates (lat/lon = 0)
- Contact hasn't advertised recently
Debug:
// 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:
// 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
- Use automatic mode for SAR operations - Team members will appear as they join
- Sync contacts on first connect - Always call
getContacts()after connecting - Handle both modes gracefully - Check
manual_add_contactssetting - Monitor advert activity - Use
onAdvertReceivedcallback to show network activity - 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()implementationlib/services/meshcore_ble_service.dart:924-_handleNewAdvert()implementationlib/providers/connection_provider.dart:164-onAdvertReceivedcallback setuplib/providers/app_provider.dart:44- Contact sync setuplib/models/contact.dart- Contact data model
Further Reading
- MeshCore Protocol Documentation - Full protocol specification
- Contact Management - Contact provider implementation