I'm trying to make Azure IOT Hub behave (more or less) like an MQTT broker.
I want to publish a message on devices/{deviceid}/messages/events/ and then receive it on devices/{deviceid}/messages/devicebound/#.
The library I'm using is M2MQTT and so far, I can connect to the IOT hub and publish a message to the events topic. Using DeviceExplorer I can see the message arriving on the hub.
For some reason the message is not relayed to the devicebound topic and the MqttClient_MqttMsgPublishReceived is never triggered. The event does get triggered when using DeviceExplorer and sending a message from there.
So... I think I'm missing something either in code or in the configuration of the IOT Hub.
Does anybody know what I'm doing wrong?
This is my code:
using Microsoft.Azure.Devices.Common.Security;
using Newtonsoft.Json;
using System;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Windows.Forms;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
namespace DemoM2MqttAzure
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private const string IOTHubURI = "TESTIOTHUB.azure-devices.net";
private const string IOTDeviceID = "IOTDevice";
private string IOTDeviceUsername = string.Format("{0}/{1}/?api-version=2018-06-30", IOTHubURI, IOTDeviceID);
private const string IOTDevicePrimaryKey = "xxx";
private string IOTSubscribeDeviceTopic = string.Format("devices/{0}/messages/devicebound/#", IOTDeviceID);
private string IOTPublishDeviceTopic = string.Format("devices/{0}/messages/events/", IOTDeviceID);
//M2MQTT client.
MqttClient mqttClient;
private void buttonStartMQTT_Click(object sender, EventArgs e)
{
mqttClient = new MqttClient(IOTHubURI, 8883, true, MqttSslProtocols.TLSv1_0, UserCertificateSelectionCallback, null);
mqttClient.ProtocolVersion = MqttProtocolVersion.Version_3_1_1;
mqttClient.MqttMsgPublishReceived += MqttClient_MqttMsgPublishReceived;
mqttClient.MqttMsgPublished += MqttClient_MqttMsgPublished;
mqttClient.MqttMsgSubscribed += MqttClient_MqttMsgSubscribed;
string IOTDevicePassword = CreateSharedAccessSignature();
mqttClient.Connect(IOTDeviceID, IOTDeviceUsername, IOTDevicePassword);
if(mqttClient.IsConnected)
{
MessageBox.Show("Connected!");
}
else
{
MessageBox.Show("Connection failed.");
}
}
private void MqttClient_MqttMsgSubscribed(object sender, MqttMsgSubscribedEventArgs e)
{
byte[] qosLevels = e.GrantedQoSLevels;
ushort msgId = e.MessageId;
MessageBox.Show("Event subscribed");
}
private string CreateSharedAccessSignature()
{
string target = string.Format("{0}/devices/{1}", IOTHubURI, IOTDeviceID);
return new SharedAccessSignatureBuilder
{
Key = IOTDevicePrimaryKey,
Target = target,
KeyName = null,
TimeToLive = TimeSpan.FromDays(360)
}.ToSignature();
}
private void buttonSubscribe_Click(object sender, EventArgs e)
{
if(mqttClient.IsConnected)
{
mqttClient.Subscribe(new string[] { IOTSubscribeDeviceTopic }, new byte[] { MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE });
}
else
{
MessageBox.Show("Not connected.");
}
}
private void buttonPublish_Click(object sender, EventArgs e)
{
if (mqttClient.IsConnected)
{
var telemetryDataPoint = new
{
deviceId = IOTDeviceID,
value = 123.456,
text = "abc"
};
var json = JsonConvert.SerializeObject(telemetryDataPoint);
ushort result = mqttClient.Publish(IOTPublishDeviceTopic, Encoding.UTF8.GetBytes(json), MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE, true);
}
else
{
MessageBox.Show("Not connected.");
}
}
private void MqttClient_MqttMsgPublished(object sender, MqttMsgPublishedEventArgs e)
{
if (e.IsPublished)
{
MessageBox.Show("Event message published.");
}
else
{
MessageBox.Show("Message was not published...");
}
}
private void MqttClient_MqttMsgPublishReceived(object sender, uPLibrary.Networking.M2Mqtt.Messages.MqttMsgPublishEventArgs e)
{
MessageBox.Show("Event message publish received.");
}
private bool UserCertificateSelectionCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if(mqttClient.IsConnected)
{
mqttClient.Disconnect();
}
}
}
}
So what you really want to do is this:
Device A --> sends message --> IoT Hub receives it --> sends the message on to Device B --> Device B receives it
You can build this fairly easily, however, not only with IoT Hub.
I would propose using a simple Azure Function in the middle:
Device A sends messages to IoT Hub.
The Azure Function receives all D2C (device-2-cloud) message from the IoT Hub
The Function uses the Service SDK of the IoT Hub to send C2D (cloud-2-device) messages to Device B through the IoT Hub
Device B listens for messages on ../devicebound and will receive the message there.
I think MqttClient.MqttMsgPublishReceived will probably be triggerred when one client receive the message you published. Have MqttClient_MqttMsgSubscribed ever been triggered? Would you like to have a try?
Refer to: MQTT Publish, Subscribe & Unsubscribe - MQTT Essentials: Part 4
Related
I have laravel chat application working through laravel broadcasting on laravel-echo-server. On front end I subscribe to channels and listen events using laravel-echo npm package, but how can I subscribe to channels in desktop app written on c# ?
I expect to have something like this written on c#:
Echo.private('SomeChannel')
.listen('SomeEvent', (response) => {});
install https://github.com/doghappy/socket.io-client-csharp package
using Newtonsoft.Json;
using SocketIOClient.WebSocketClient;
using SocketIOClient;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
namespace c_sharp_demo
{
class Program
{
static async Task Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
var uri = new Uri("http://localhost:6001");
var socket = new SocketIO(uri, new SocketIOOptions
{
Query = new Dictionary<string, string>
{
{"token", "io" }
}
});
socket.OnConnected += Socket_OnConnected;
socket.OnPing += Socket_OnPing;
socket.OnPong += Socket_OnPong;
socket.OnDisconnected += Socket_OnDisconnected;
socket.OnReconnecting += Socket_OnReconnecting;
try
{
await socket.ConnectAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
socket.On("event-name", response =>
{
Console.WriteLine($"server: {response}");
});
Console.ReadLine();
}
private static async void Socket_OnConnected(object sender, EventArgs e)
{
Console.WriteLine("Socket_OnConnected");
var socket = sender as SocketIO;
await socket.EmitAsync("subscribe", new
{
channel = "channelName",
auth = ""
});
}
private static void Socket_OnPing(object sender, EventArgs e)
{
Console.WriteLine("Ping");
}
private static void Socket_OnPong(object sender, TimeSpan e)
{
Console.WriteLine("Pong: " + e.TotalMilliseconds);
}
private static void Socket_OnDisconnected(object sender, string e)
{
Console.WriteLine("disconnect: " + e);
}
private static void Socket_OnReconnecting(object sender, int e)
{
Console.WriteLine($"Reconnecting: attempt = {e}");
}
}
}
You can use pusher/pusher-websocket-dotnet. I have an equivalent solution here: https://stackoverflow.com/a/71869451/10875215
I have created my own MSMQ wrapper class like this:
public class MsgQueue
{
private MessageQueue messageQueue;
public delegate void ReadMessageDelegate(string message);
public event ReadMessageDelegate NewMessageAvailable;
public MsgQueue(string queueName)
{
var queuePath = #".\Private$\" + queueName;
if (!MessageQueue.Exists(queuePath)) MessageQueue.Create(queuePath);
messageQueue = new MessageQueue(queuePath);
messageQueue.Label = queueName;
messageQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string)});
messageQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(MsgReceivedHandler);
messageQueue.BeginReceive();
}
private void MsgReceivedHandler(object sender, ReceiveCompletedEventArgs e)
{
try
{
MessageQueue mq = (MessageQueue)sender;
var message = mq.EndReceive(e.AsyncResult);
NewMessageAvailable(message.Body.ToString());
mq.BeginReceive();
}
catch (MessageQueueException)
{
// Handle sources of MessageQueueException.
}
return;
}
public void SendMessage(string message)
{
messageQueue.Send(message);
}
}
I tested it on two separate WinForms applications.
First app sends a text message when button is clicked:
private void btn_Click(object sender, EventArgs e)
{
var queue = new MsgQueue.MsgQueue("GBMqueue");
queue.SendMessage("some message text");
}
Second app is listening for any incoming messages and then tries to process it:
// declaration
private MsgQueue queue;
// preparation of the queue
private void Form1_Load(object sender, EventArgs e)
{
queue = new MsgQueue.MsgQueue("GBMqueue");
queue.NewMessageAvailable += Queue_NewMessageAvailable;
}
// Handler for the incoming messages
private void Queue_NewMessageAvailable(string message)
{
// Hits here very rarely!!!
}
The problem is that I can send the message from App1 several times, but the Queue_NewMessageAvailable handler catches only one random message, not the first one - just one of those which were sent.
No exception is thrown, it just does not catch the incoming messages.
What am I doing wrong here?
I think the first App should not listen for new messages. It's possible that it takes away the messages for the second App. It should only send messages.
When you split the functionality it should work.
I am trying to send information from my windows phone to the computer. I read some where that the usb cable is treated like a Ethernet cable. I created a server and a client(Phone is client) to try to send information. The program sends a message every time it presses enter.
Client Side
public sealed partial class MainPage : Page
{
private bool Connected;
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached.
/// This parameter is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// TODO: Prepare page for display here.
// TODO: If your application contains multiple pages, ensure that you are
// handling the hardware Back button by registering for the
// Windows.Phone.UI.Input.HardwareButtons.BackPressed event.
// If you are using the NavigationHelper provided by some templates,
// this event is handled for you.
}
private DatagramSocket dataGramSocket = new DatagramSocket();
private DataWriter socketWriter;
private bool messageSent;
private static string port = "138";
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
dataGramSocket.MessageReceived += dataGramSocket_MessageReceived;
StartListener();
}
void dataGramSocket_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
}
private async void StartListener()
{
string IpHostname = "127.0.0.1";
var IpAdresses = NetworkInformation.GetHostNames();
for (int i = 0; i < IpAdresses.Count; i++)
{
if (IpAdresses[i].IPInformation != null)
{
if (IpAdresses[i].IPInformation.NetworkAdapter.IanaInterfaceType == 6 && IpAdresses[i].DisplayName != null)
{
IpHostname = IpAdresses[i].DisplayName;
break;
}
else if (IpAdresses[i].IPInformation.NetworkAdapter.IanaInterfaceType == 71 && IpAdresses[i].DisplayName != null)
{
IpHostname = IpAdresses[i].DisplayName;
break;
}
}
}
HostName host = new HostName(IpHostname);
//EndpointPair endpoint = new EndpointPair(localHostName,)
await dataGramSocket.BindServiceNameAsync(port);
await dataGramSocket.ConnectAsync(host, port);
socketWriter = new DataWriter(dataGramSocket.OutputStream);
Connected = true;
}
private async void SendPacket()
{
await socketWriter.StoreAsync();
messageSent = true;
}
private void TextBox1_KeyDown(object sender, KeyRoutedEventArgs e)
{
if(e.Key == Windows.System.VirtualKey.Enter)
{
SendMessage(textBox1.Text);
}
}
private void SendMessage(string message)
{
socketWriter.WriteString(message);
SendPacket();
textBox1.Text = "";
}
}
}
Server Side
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private static int port = 138;
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Task.Factory.StartNew(() =>
{
var dataStream = new MemoryStream(1024);
var udpClient = new UdpClient(port);
while (true)
{
if (udpClient.Available > 0)
{
udpClient.BeginReceive(ar =>
{
var clientEndPoint = new IPEndPoint(IPAddress.Any, port);
var bytesReceived = udpClient.EndReceive(ar, ref clientEndPoint);
dataStream.Write(bytesReceived, 0, bytesReceived.Length);
if (bytesReceived.Length > 0)
{
UpdateUI(Encoding.UTF8.GetString(bytesReceived));
UpdateUI(Encoding.ASCII.GetString(bytesReceived));
}
}, null);
}
Thread.Sleep(1);
}
}, _cancellationTokenSource.Token);
}
private void UpdateUI(string message)
{
this.Invoke(new MethodInvoker(delegate
{
this.textBox1.Text += message + Environment.NewLine;
}));
}
private void button1_Click_1(object sender, EventArgs e)
{
byte[] message = Encoding.UTF8.GetBytes(textBox2.Text);
using (var udpClient = new UdpClient())
{
udpClient.Send(message, message.Length, new IPEndPoint(IPAddress.Loopback, port));
}
textBox2.Clear();
}
}
}
The information isn't connecting somewhere in the middle. On both sides nothing breaks. When I test the form by sending information to the loopback it works. I don't know why the information is not reaching the other side. Can you please tell me if I am doing this write and it is suppose to be done this way or can you tell me if this is not possible so I can stop working on it.
I saw a similar question and I answered there, Communicate to PC over USB.
It requires editing the registry, the server code running on the device and I am not sure if the code will run outside of a development environment (I only need this to speed up my workflow and debugging - so its not an issue).
Not possible.
TCP/IP over USB worked OK in Windows Phone 7. In Windows Phone 8 however they removed the functionality.
Seek for other alternatives.
You could use TCP/IP over WiFi, or use BT, or write data to "Documents" and read with MTP COM API, or write data to isolated storage and read it using isetool.exe (this one only works for dev.unlocked devices).
I'm currently working on a project and one of the featured devices is a Windows Tablet. To "connect" it to other devices (like some Raspberry Pi) in the project environment UDP is used to send messages. The Windows Tablet is intended to be some controlling device with soem touch functionality. Therefore I'm writing an App (and the intention of the App is not to put it into the Windows Store). The UDP part in this work is quite painful because I had to do much research since I started with no experience in App programming. More painful than the programming is, that I practically finished the work only to start over again because the App didn't receive UDP anymore.
Here's my code (I removed elements not relevant to the actual problem). I apologize for the bad coding....
App.xaml.cs:
sealed partial class App : Application
{
NetworkInterface ni = new NetworkInterface();
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
ni.MessageReceived += OnMessageReceived;
ni.Connect(new HostName("127.0.0.1"), "5556");
}
private void OnMessageReceived(object sender, MessageReceivedEventArgs e)
{
Debug.WriteLine("Processing");
Debug.WriteLine(e.Message.Data);
}
public static new App Current
{
get { return Application.Current as App; }
}
private DatagramSocket _socket;
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Update_Timer();
}
DispatcherTimer timer = new DispatcherTimer();
private void Update_Timer()
{
timer.Start();
timer.Interval = new TimeSpan(0,0,0,0,500);
timer.Tick += alive;
}
private void alive(object sender, object e)
{
if (start == 0) {
Debug.WriteLine("App-Startup");
ni.SendMessage("Startup...");
start++;
}
else
{
Debug.WriteLine("App-Alive");
ni.SendMessage("alive");
start++;
}
}
}
This part of code is to send and receive Messages in the backgrond in the whole App.
And a NetworkInterface class:
class NetworkInterface
{
private DatagramSocket _socket;
public bool IsConnected { get; set; }
public NetworkInterface()
{
IsConnected = false;
_socket = new DatagramSocket();
_socket.MessageReceived += OnSocketMessageReceived;
}
public async void Connect(HostName remoteHostName, string remoteServiceNameOrPort)
{
if (IsConnected != true)
{
await _socket.BindServiceNameAsync("5321");
await _socket.ConnectAsync(remoteHostName, remoteServiceNameOrPort);
}
IsConnected = true;
}
public void alive(object sender, object e)
{
Debug.WriteLine("alive");
}
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
private void OnSocketMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
var reader = args.GetDataReader();
var count = reader.UnconsumedBufferLength;
var data = reader.ReadString(count);
Debug.WriteLine(args);
if (MessageReceived != null)
{
var ea = new MessageReceivedEventArgs();
ea.Message = new Message() { Data = data };
ea.RemoteHostName = args.RemoteAddress;
ea.RemotePort = args.RemotePort;
MessageReceived(this, ea);
}
}
DataWriter _writer = null;
public async void SendMessage(string message)
{
if (_writer == null)
{
var stream = _socket.OutputStream;
_writer = new DataWriter(stream);
}
_writer.WriteString(message);
await _writer.StoreAsync();
}
}
The main problems are:
If I dont send something before receiving, I won't be able top get an message.
If I send before I have random Faults at this line:
var reader = args.GetDataReader();
If nothing fails, I'm not able to receive messages from a local Python script (which works) but I can send messages from a local program which the App receives.
Does anyone know how I can fix these problems?
Thanks in advance!
In my C # application Chat Client I don`t not know how to do a function to receive messages from the Chat Server in C # and presents them in a TextView Chat Client
I'm programming in Visual Studio in C # using Mono for Android.
The question is... How I do a function for i can receive messages from Chat Server in PC and Chat Client in Android device? When i use Chat Client in windows 7 works good but when i make for a android device i can`t receive mensages from Server Chat to Chat Client.
I think the problem as in public void RecievedMessage and private void UpdateTextbox.
Sorry, my bad english.
Thanks your atention.
The Source Code, Chat Client:
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Widget.EditText;
using Android.Widget.Button;
using Android.Widget.TextView;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
namespace ChatClient_Android
{
[Activity(Label = "ChatClient_Android", MainLauncher = true, Icon = "#drawable/icon")]
public class MainChat : Activity
{
public delegate void OnRecievedMessage(string message);
public MainChat form;
const int WM_VSCROLL = 0x115;
const int SB_BOTTOM = 7;
string msgrecebida;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
Button enviar = FindViewById<Button>(Resource.Id.btenviar);
Button ligar = FindViewById<Button>(Resource.Id.btligar);
TextView text1 = FindViewById<TextView>(Resource.Id.text1);
EditText text2 = FindViewById<EditText>(Resource.Id.text2);
//Conexão com o servidor
ligar.Click += delegate
{
Connect();
ligar.Enabled = false;
text1.Text = "Connected";
};
enviar.Click += delegate
{
if (text2.Text != "")
{
if (text2.Text.ToLower().StartsWith("/"))
{
text2.Text = "";
}
else
{
SendMessage("MSG :" + text2.Text);
text2.Text = "";
}
}
};
}
private void Invoke(OnRecievedMessage onRecievedMessage, string message)
{
throw new NotImplementedException();
}
public bool InvokeRequired { get; set; }
public void RecievedMessage(string message)
{
if (InvokeRequired)
{
this.Invoke(new OnRecievedMessage(RecievedMessage), message);
}
else
{
UpdateTextbox(message);
}
}
private void UpdateTextbox(string text)
{
msgrecebida += "\r\n";
msgrecebida += text;
}
//Interligações Classes MainChat & Connection
public void Disconnected(string reason)
{
form.Disconnected(reason);
}
//TCP Connection
public StreamWriter Outgoing;
private StreamReader Incoming;
private TcpClient Connection;
private Thread Messages;
private IPAddress IP;
//public string host;
//public string nick;
//MainChat m_ParentForm;
bool isConnected;
//Função Conectar
public void Connect()
{
try
{
IP = IPAddress.Parse("10.0.2.2");
Connection = new TcpClient();
Connection.Connect(IP, 1986);
Outgoing = new StreamWriter(Connection.GetStream());
Outgoing.WriteLine("EcoDuty");
Outgoing.Flush();
//m_ParentForm.Vis(true);
Messages = new Thread(new ThreadStart(this.Communication));
Messages.Start();
}
catch (Exception e) { Disconnected(e.Message); }
}
private void Communication()
{
Incoming = new StreamReader(Connection.GetStream());
string check = Incoming.ReadLine();
if (check[0] == '1')
{
//m_ParentForm.Vis(true);
isConnected = true;
}
else
{
string Reason = "Disconnected: ";
Reason += check.Substring(2, check.Length - 2);
Disconnected(Reason);
return;
}
while (isConnected == true)
{
try
{
ServerMessage(Incoming.ReadLine());
}
catch (Exception e)
{
if (isConnected == true)
{
Disconnected("Connection to server lost");
Console.WriteLine("Connection Lost: " + e.ToString());
}
break;
}
}
}
private void ServerMessage(string message)
{
try
{
RecievedMessage(message);
}
catch { }
}
public void CloseConnection()
{
isConnected = false;
Incoming.Close();
Outgoing.Close();
Connection.Close();
Messages.Abort();
}
public void SendMessage(string message)
{
Outgoing.WriteLine(message);
Outgoing.Flush();
}
}
}
It looks to me like (one) problem is that you are trying to access text1 as if it is a field or property when it is a local variable declared in onCreate. You either need to push text1 up a level in scope, or grab a new reference to it in RecievedMessage. This is not a problem with Android, specifically, but is a difference with how the code is set up with Mono for Android.
Typically, when working with Winforms in Windows, you'll be working in a partial class with all your controls defined in the designer.cs file as fields. But in your Mono code, you are binding to TextViews instead, and in your OnCreate instead of at instance scope. When you copy/pasted your code, you likely didn't account for that difference.
As an aside, I highly doubt that the user32.dll function you are importing is going to do anything useful on Android. Unless the Mono folks are doing something magical, the win32 API will not be available on Android.