Skip to content
Max Litruv Boonzaayer edited this page Feb 15, 2026 · 3 revisions

Events

MeshRadio uses an event-based system for asynchronous notifications. Subscribe to events to receive messages, advertisements, and status updates.

Available Events

Connected

Fired when successfully connected to the device.

public event Action<SelfInfo>? Connected;

Example:

radio.Connected += (self) =>
{
    Console.WriteLine($"Connected to {self.Name}");
    Console.WriteLine($"Public Key: {self.PublicKeyPrefix}...");
};

Disconnected

Fired when disconnected from the device.

public event Action? Disconnected;

Example:

radio.Disconnected += () =>
{
    Console.WriteLine("Disconnected from radio");
};

ErrorOccurred

Fired when an error occurs during communication.

public event Action<string>? ErrorOccurred;

Example:

radio.ErrorOccurred += (error) =>
{
    Console.WriteLine($"Error: {error}");
    // Consider reconnecting or alerting the user
};

DirectMessageReceived

Fired when a direct message is received from a contact.

public event Action<DirectMessage>? DirectMessageReceived;

Example:

radio.DirectMessageReceived += (msg) =>
{
    Console.WriteLine($"From: {msg.SenderPrefixHex}");
    Console.WriteLine($"Text: {msg.Text}");
    Console.WriteLine($"Hops: {msg.PathLen}");
    
    if (msg.IsV3)
    {
        Console.WriteLine($"Sender: {msg.SenderName}");
        Console.WriteLine($"SNR: {msg.Snr} dB");
    }
};

ChannelMessageReceived

Fired when a message is received on a channel.

public event Action<ChannelMessage>? ChannelMessageReceived;

Example:

radio.ChannelMessageReceived += (msg) =>
{
    Console.WriteLine($"Channel {msg.ChannelIndex}: {msg.Text}");
    
    if (msg.IsV3 && !string.IsNullOrEmpty(msg.SenderName))
    {
        Console.WriteLine($"  From: {msg.SenderName}");
    }
};

AdvertReceived

Fired when an advertisement is received from the mesh.

public event Action<Advert>? AdvertReceived;

Example:

radio.AdvertReceived += (advert) =>
{
    Console.WriteLine($"Advert from {advert.Name}");
    Console.WriteLine($"  Key: {advert.PublicKeyHex[..12]}...");
    Console.WriteLine($"  Hops: {advert.PathLen}, SNR: {advert.Snr} dB");
    
    if (advert.Latitude != 0 || advert.Longitude != 0)
    {
        var lat = advert.Latitude / 1_000_000.0;
        var lon = advert.Longitude / 1_000_000.0;
        Console.WriteLine($"  Location: {lat:F4}, {lon:F4}");
    }
};

MessageWaiting

Fired when the device signals that a message is waiting to be fetched.

public event Action? MessageWaiting;

Example:

radio.MessageWaiting += async () =>
{
    // Fetch waiting messages
    DirectMessage? msg;
    while ((msg = await radio.GetNextMessageAsync()) != null)
    {
        Console.WriteLine($"Fetched: {msg.Text}");
    }
};

RawDataReceived

Fired when raw data is received (PAYLOAD_TYPE_RAW_CUSTOM).

public event Action<byte[]>? RawDataReceived;

Example:

radio.RawDataReceived += (data) =>
{
    Console.WriteLine($"Raw data: {BitConverter.ToString(data)}");
};

PacketReceived

Fired for every packet received from the device. Useful for debugging and logging.

public event Action<byte[]>? PacketReceived;

Example:

radio.PacketReceived += (packet) =>
{
    var code = packet[0];
    Console.WriteLine($"[RX] Code: 0x{code:X2}, Length: {packet.Length}");
};

PacketSent

Fired for every packet sent to the device. Useful for debugging and logging.

public event Action<byte[]>? PacketSent;

Example:

radio.PacketSent += (packet) =>
{
    var code = packet[0];
    Console.WriteLine($"[TX] Code: 0x{code:X2}, Length: {packet.Length}");
};

Complete Example

Here's a complete example subscribing to all events:

using MeshCS;

var radio = new MeshRadio("COM11");

// Lifecycle events
radio.Connected += (self) =>
{
    Console.WriteLine($"✓ Connected: {self.Name}");
};

radio.Disconnected += () =>
{
    Console.WriteLine("✗ Disconnected");
};

radio.ErrorOccurred += (error) =>
{
    Console.WriteLine($"⚠ Error: {error}");
};

// Message events
radio.DirectMessageReceived += (msg) =>
{
    var sender = msg.IsV3 && !string.IsNullOrEmpty(msg.SenderName) 
        ? msg.SenderName 
        : msg.SenderPrefixHex;
    Console.WriteLine($"[DM] {sender}: {msg.Text}");
};

radio.ChannelMessageReceived += (msg) =>
{
    var sender = msg.IsV3 && !string.IsNullOrEmpty(msg.SenderName)
        ? msg.SenderName
        : "Unknown";
    Console.WriteLine($"[CH{msg.ChannelIndex}] {sender}: {msg.Text}");
};

radio.AdvertReceived += (advert) =>
{
    Console.WriteLine($"[AD] {advert.Name} seen ({advert.PathLen} hops)");
};

radio.MessageWaiting += () =>
{
    Console.WriteLine("[!] Message waiting");
};

// Debug events (optional)
if (verbose)
{
    radio.PacketReceived += (pkt) =>
        Console.WriteLine($"[RX] 0x{pkt[0]:X2} ({pkt.Length} bytes)");
    
    radio.PacketSent += (pkt) =>
        Console.WriteLine($"[TX] 0x{pkt[0]:X2} ({pkt.Length} bytes)");
}

// Connect and run
await radio.ConnectAsync();
await Task.Delay(Timeout.Infinite);

Thread Safety

Events are fired from a background reader thread. If you need to update UI or access shared state, marshal to the appropriate thread:

// WPF example
radio.DirectMessageReceived += (msg) =>
{
    Dispatcher.Invoke(() =>
    {
        MessagesList.Add(msg);
    });
};

// Generic .NET
radio.DirectMessageReceived += (msg) =>
{
    lock (_messagesLock)
    {
        _messages.Add(msg);
    }
};

Unsubscribing

To unsubscribe from events:

void OnMessage(DirectMessage msg)
{
    Console.WriteLine(msg.Text);
}

// Subscribe
radio.DirectMessageReceived += OnMessage;

// Unsubscribe
radio.DirectMessageReceived -= OnMessage;

Clone this wiki locally